source: branches/virtcpu varint/UAssembler.pas

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