source: branches/virtualcpu4/UAssembler.pas

Last change on this file was 185, checked in by chronos, 5 years ago
  • Added: Assembler labels reference address calculation.
  • Fixed: Displaying address/data hex numbers in opcode and instruction.
File size: 8.6 KB
Line 
1unit UAssembler;
2
3{$mode delphi}
4
5interface
6
7uses
8 Classes, SysUtils, fgl, UCpu, UOpcode, UInstructionWriter;
9
10type
11 TErrorEvent = procedure (Text: string) of object;
12
13 { TParser }
14
15 TParser = class
16 private
17 FOnError: TErrorEvent;
18 procedure Error(Text: string);
19 public
20 Text: string;
21 procedure Expect(Text: string);
22 function IsOperator(C: Char): Boolean;
23 function IsWhiteSpace(C: Char): Boolean;
24 function ReadNext: string;
25 function EndOfText: Boolean;
26 property OnError: TErrorEvent read FOnError write FOnError;
27 end;
28
29 TLabelRef = class
30 Address: QWord;
31 BitWidth: TBitWidth;
32 Relative: Boolean;
33 end;
34
35 { TLabel }
36
37 TLabel = class
38 Name: string;
39 Address: QWord;
40 Refs: TFPGList<TLabelRef>;
41 constructor Create;
42 destructor Destroy; override;
43 end;
44
45 { TLabels }
46
47 TLabels = class(TFPGObjecTList<TLabel>)
48 function SearchByName(Name: string): TLabel;
49 end;
50
51 { TAssembler }
52
53 TAssembler = class
54 private
55 FOnError: TErrorEvent;
56 OpcodeDefs: TOpcodeDefs;
57 InstructionIP: QWord;
58 procedure Error(Text: string);
59 procedure ParseParam(Param: TOpcodeParam);
60 procedure WriteRefAddr(Name: string; Relative: Boolean = False);
61 procedure WriteRefData(Name: string);
62 procedure UpdateLabelRef;
63 procedure ParseInstruction;
64 public
65 Parser: TParser;
66 Labels: TLabels;
67 Source: TStringList;
68 InstructionWriter: TInstructionWriter;
69 procedure Compile;
70 constructor Create;
71 destructor Destroy; override;
72 property OnError: TErrorEvent read FOnError write FOnError;
73 end;
74
75
76implementation
77
78{ TLabels }
79
80function TLabels.SearchByName(Name: string): TLabel;
81var
82 I: Integer;
83begin
84 I := 0;
85 while (I < Count) and (Items[I].Name <> Name) do Inc(I);
86 if I < Count then Result := Items[I]
87 else Result := nil;
88end;
89
90{ TLabel }
91
92constructor TLabel.Create;
93begin
94 Refs := TFPGList<TLabelRef>.Create;
95end;
96
97destructor TLabel.Destroy;
98begin
99 Refs.Free;
100 inherited Destroy;
101end;
102
103{ TParser }
104
105procedure TParser.Error(Text: string);
106begin
107 if Assigned(FOnError) then FOnError(Text);
108end;
109
110procedure TParser.Expect(Text: string);
111var
112 Next: string;
113begin
114 Next := ReadNext;
115 if Next <> Text then
116 Error('Expected ' + Text + ' but ' + Next + ' found');
117end;
118
119function TParser.IsOperator(C: Char): Boolean;
120begin
121 Result := (C = ',') or (C = ';');
122end;
123
124function TParser.IsWhiteSpace(C: Char): Boolean;
125begin
126 Result := (C = ' ') or (C = #8);
127end;
128
129function TParser.ReadNext: string;
130var
131 P: Integer;
132begin
133 Text := Trim(Text);
134 P := 1;
135 if (Length(Text) > 0) and IsOperator(Text[P]) then begin
136 Result := Text[P];
137 Delete(Text, 1, 1);
138 end else begin
139 while (P <= Length(Text)) and not IsWhiteSpace(Text[P]) and not IsOperator(Text[P]) do Inc(P);
140 Result := Copy(Text, 1, P - 1);
141 Delete(Text, 1, P - 1);
142 end;
143end;
144
145function TParser.EndOfText: Boolean;
146begin
147 Result := Text = '';
148end;
149
150{ TAssembler }
151
152procedure TAssembler.Error(Text: string);
153begin
154 if Assigned(FOnError) then FOnError(Text);
155end;
156
157procedure TAssembler.ParseParam(Param: TOpcodeParam);
158var
159 Reg: TRegIndex;
160 Addr: Int64;
161 Next: string;
162begin
163 if Param = prReg then begin
164 Next := Parser.ReadNext;
165 if (Length(Next) > 1) and (Next[1] = 'R') then
166 Reg := StrToInt(Copy(Next, 2, Length(Next)))
167 else Error('Expected register name but found ' + Next);
168 InstructionWriter.Write8(Reg);
169 end else
170 if Param = prData then begin
171 Next := Parser.ReadNext;
172 if TryStrToInt64(Next, Addr) then
173 InstructionWriter.WriteData(Addr)
174 else WriteRefData(Next);
175 end else
176 if Param = prAddr then begin
177 Next := Parser.ReadNext;
178 if TryStrToInt64(Next, Addr) then
179 InstructionWriter.WriteAddress(Addr)
180 else WriteRefAddr(Next);
181 end else
182 if Param = prAddrRel then begin
183 Next := Parser.ReadNext;
184 if TryStrToInt64(Next, Addr) then
185 InstructionWriter.WriteAddress(InstructionWriter.IP + Addr)
186 else WriteRefAddr(Next, True);
187 end;
188end;
189
190procedure TAssembler.WriteRefAddr(Name: string; Relative: Boolean = False);
191var
192 L: TLabel;
193 NewRef: TLabelRef;
194begin
195 L := Labels.SearchByName(Name);
196 if Assigned(L) then begin
197 if Relative then
198 InstructionWriter.WriteAddressSigned(InstructionWriter.GetRelativeAddr(
199 InstructionWriter.AddrSize, InstructionIP, L.Address))
200 else InstructionWriter.WriteAddress(L.Address);
201 end else begin
202 L := TLabel.Create;
203 L.Name := Name;
204 NewRef := TLabelRef.Create;
205 NewRef.Address := InstructionWriter.IP;
206 NewRef.BitWidth := InstructionWriter.AddrSize;
207 if Relative then NewRef.Relative := True;
208 L.Refs.Add(NewRef);
209 Labels.Add(L);
210 InstructionWriter.WriteAddress(0);
211 end;
212end;
213
214procedure TAssembler.WriteRefData(Name: string);
215var
216 L: TLabel;
217 NewRef: TLabelRef;
218begin
219 L := Labels.SearchByName(Name);
220 if Assigned(L) then begin
221 InstructionWriter.WriteData(L.Address);
222 end else begin
223 L := TLabel.Create;
224 L.Name := Name;
225 NewRef := TLabelRef.Create;
226 NewRef.Address := InstructionWriter.IP;
227 NewRef.BitWidth := InstructionWriter.DataSize;
228 L.Refs.Add(NewRef);
229 Labels.Add(L);
230 InstructionWriter.WriteData(0);
231 end;
232end;
233
234procedure TAssembler.UpdateLabelRef;
235var
236 I: Integer;
237 R: Integer;
238begin
239 for I := 0 to Labels.Count - 1 do
240 with TLabel(Labels[I]) do begin
241 for R := 0 to Refs.Count - 1 do
242 begin
243 InstructionWriter.IP := Refs[R].Address;
244 InstructionWriter.AddrSize := Refs[R].BitWidth;
245 if Refs[R].Relative then InstructionWriter.WriteAddressSigned(InstructionWriter.GetRelativeAddr(
246 InstructionWriter.AddrSize, InstructionWriter.IP - 1, Address))
247 else InstructionWriter.WriteAddress(Address);
248 end;
249 end;
250end;
251
252procedure TAssembler.ParseInstruction;
253var
254 Next: string;
255 LabelName: string;
256 NewLabel: TLabel;
257 OpcodeDef: TOpcodeDef;
258begin
259 Next := Parser.ReadNext;
260 if Next = '' then Exit;
261 if (Length(Next) > 0) and (Next[Length(Next)] = ':') then begin
262 LabelName := Copy(Next, 1, Length(Next) - 1);
263 NewLabel := Labels.SearchByName(LabelName);
264 if not Assigned(NewLabel) then begin
265 NewLabel := TLabel.Create;
266 NewLabel.Name := LabelName;
267 Labels.Add(NewLabel);
268 end;
269 NewLabel.Address := InstructionWriter.IP;
270 Next := Parser.ReadNext;
271 end;
272 if Next = '' then Exit;
273 OpcodeDef := OpcodeDefs.SearchByName(Next);
274 if Assigned(OpcodeDef) then begin
275 if OpcodeDef.Prefix then InstructionWriter.Prefix := True;
276 if OpcodeDef.Opcode = opDataPrefix16 then InstructionWriter.DataSize := bw8
277 else if OpcodeDef.Opcode = opDataPrefix16 then InstructionWriter.DataSize := bw16
278 else if OpcodeDef.Opcode = opDataPrefix32 then InstructionWriter.DataSize := bw32
279 else if OpcodeDef.Opcode = opDataPrefix64 then InstructionWriter.DataSize := bw64
280 else if OpcodeDef.Opcode = opAddrPrefix8 then InstructionWriter.AddrSize := bw8
281 else if OpcodeDef.Opcode = opAddrPrefix16 then InstructionWriter.AddrSize := bw16
282 else if OpcodeDef.Opcode = opAddrPrefix32 then InstructionWriter.AddrSize := bw32
283 else if OpcodeDef.Opcode = opAddrPrefix64 then InstructionWriter.AddrSize := bw64;
284 InstructionIP := InstructionWriter.IP;
285 InstructionWriter.Write8(Byte(OpcodeDef.Opcode));
286 ParseParam(OpcodeDef.Param1);
287 if OpcodeDef.Param2 <> prNone then begin
288 Parser.Expect(',');
289 ParseParam(OpcodeDef.Param2);
290 if OpcodeDef.Param3 <> prNone then begin
291 Parser.Expect(',');
292 ParseParam(OpcodeDef.Param3);
293 end;
294 end;
295 if not OpcodeDef.Prefix then begin
296 InstructionWriter.DataSize := InstructionWriter.DataSizeBase;
297 InstructionWriter.AddrSize := InstructionWriter.AddrSizeBase;
298 end;
299 end else
300 if Next = 'STRING' then begin
301 Next := Parser.ReadNext;
302 if (Length(Next) >= 2) and (Next[1] = '''') and (Next[Length(Next)] = '''') then
303 InstructionWriter.WriteString(Copy(Next, 2, Length(Next) - 2));
304 end else
305 Error('Unknown instruction ' + Next);
306end;
307
308procedure TAssembler.Compile;
309var
310 I: Integer;
311 Next: string;
312begin
313 InstructionWriter.Init;
314 Labels.Clear;
315 for I := 0 to Source.Count - 1 do begin
316 Parser.Text := Source[I];
317 ParseInstruction;
318 repeat
319 Next := Parser.ReadNext;
320 if (Next = '') or (Next <> ';') then Break;
321 ParseInstruction;
322 until False;
323 end;
324 UpdateLabelRef;
325end;
326
327constructor TAssembler.Create;
328begin
329 OpcodeDefs := TOpcodeDefs.Create;
330 Source := TStringList.Create;
331 Labels := TLabels.Create;
332 Parser := TParser.Create;
333 Parser.OnError := Error;
334 InstructionWriter := TInstructionWriter.Create;
335end;
336
337destructor TAssembler.Destroy;
338begin
339 FreeAndNil(InstructionWriter);
340 FreeAndNil(Parser);
341 FreeAndNil(Labels);
342 FreeAndNil(Source);
343 FreeAndNil(OpcodeDefs);
344 inherited Destroy;
345end;
346
347
348
349end.
350
Note: See TracBrowser for help on using the repository browser.