source: branches/simple/Assembler.pas

Last change on this file was 42, checked in by chronos, 9 months ago
  • Modified: Improved simple virtual machine.
File size: 11.7 KB
Line 
1unit Assembler;
2
3interface
4
5uses
6 Classes, SysUtils, Instructions, Cpu, Generics.Collections,
7 Memory, Message, Parser, Channel;
8
9type
10 { TLabelRef }
11
12 TLabelRef = record
13 LabelName: string;
14 RefPos: QWord;
15 TextPos: TPoint;
16 BitWidth: TBitWidth;
17 class function Create(LabelName: string; RefPos: Integer): TLabelRef; static;
18 end;
19
20 { TAssembler }
21
22 TAssembler = class
23 private
24 FOnError: TErrorEvent;
25 Parser: TParser;
26 function ParseVar: Boolean;
27 function ParseDb: Boolean;
28 function ParseOrg: Boolean;
29 function ParseInstruction: Boolean;
30 function ParseLabel: Boolean;
31 procedure UpdateLabelRefs;
32 procedure ParseNumParam(Token: TToken; DataWidth: TBitWidth);
33 public
34 AddressWidth: TBitWidth;
35 InstructionSet: TInstructionSet;
36 Memory: TMemory64;
37 Labels: TDictionary<string, QWord>;
38 LabelRefs: TList<TLabelRef>;
39 Variables: TDictionary<string, QWord>;
40 Messages: TMessages;
41 procedure Error(Text: string; Pos: TPoint);
42 procedure Compile(Source: string);
43 procedure LoadFromFile(FileName: string);
44 function ParseStr(var Text: string; Separator: string): string;
45 constructor Create;
46 destructor Destroy; override;
47 property OnError: TErrorEvent read FOnError write FOnError;
48 end;
49
50
51implementation
52
53{ TLabelRef }
54
55class function TLabelRef.Create(LabelName: string; RefPos: Integer): TLabelRef;
56begin
57 Result.LabelName := LabelName;
58 Result.RefPos := RefPos;
59end;
60
61{ TAssembler }
62
63procedure TAssembler.UpdateLabelRefs;
64var
65 I: Integer;
66 Addr: QWord;
67begin
68 for I := 0 to LabelRefs.Count - 1 do begin
69 if Labels.TryGetValue(LabelRefs[I].LabelName, Addr) then begin
70 case AddressWidth of
71 bw8: Memory.Write8(LabelRefs[I].RefPos, Addr);
72 bw16: Memory.Write16(LabelRefs[I].RefPos, Addr);
73 bw32: Memory.Write32(LabelRefs[I].RefPos, Addr);
74 bw64: Memory.Write64(LabelRefs[I].RefPos, Addr);
75 end;
76 end else Error('Label ' + LabelRefs[I].LabelName + ' referenced but not defined.', LabelRefs[I].TextPos);
77 end;
78end;
79
80procedure TAssembler.ParseNumParam(Token: TToken; DataWidth: TBitWidth);
81var
82 Value: Integer;
83 Value8: Byte;
84 Value16: Word;
85 Value32: DWord;
86 Value64: QWord;
87begin
88 if Token.Kind = tkNumber then begin
89 case DataWidth of
90 bw8: if TryStrToInt(Token.Value, Value) then Memory.WritePos8(Value);
91 bw16: if TryStrToInt(Token.Value, Value) then Memory.WritePos16(Value);
92 bw32: if TryStrToDWord(Token.Value, Value32) then Memory.WritePos32(Value32);
93 bw64: if TryStrToQWord(Token.Value, Value64) then Memory.WritePos64(Value64);
94 end;
95 end else
96 if Token.Kind = tkIdentifier then begin;
97 if Variables.TryGetValue(Token.Value, Value64) then begin
98 case DataWidth of
99 bw8: Memory.WritePos8(Value64);
100 bw16: Memory.WritePos16(Value64);
101 bw32: Memory.WritePos32(Value64);
102 bw64: Memory.WritePos64(Value64);
103 end;
104 end else
105 if Labels.TryGetValue(Token.Value, Value64) then begin
106 case DataWidth of
107 bw8: Memory.WritePos8(Value64);
108 bw16: Memory.WritePos16(Value64);
109 bw32: Memory.WritePos32(Value64);
110 bw64: Memory.WritePos64(Value64);
111 end;
112 end else begin
113 LabelRefs.Add(TLabelRef.Create(Token.Value, Memory.Position));
114 case DataWidth of
115 bw8: Memory.WritePos8(0);
116 bw16: Memory.WritePos16(0);
117 bw32: Memory.WritePos32(0);
118 bw64: Memory.WritePos64(0);
119 end;
120 end;
121 end else Error('Unexpected token ' + Token.Value + '.', Token.Pos);
122end;
123
124procedure TAssembler.Error(Text: string; Pos: TPoint);
125begin
126 Messages.AddMessage(Text, Pos);
127 if Assigned(FOnError) then
128 FOnError(Text, Pos);
129end;
130
131procedure TAssembler.Compile(Source: string);
132begin
133 Messages.Clear;
134 Memory.Size := 0;
135 Labels.Clear;
136 LabelRefs.Clear;
137 Parser.Reset;
138 Parser.Source := Source;
139 while not Parser.CheckNextKind(tkEof) do begin
140 ParseLabel;
141 if ParseVar then begin
142 end else
143 if ParseDb then begin
144 end else
145 if ParseOrg then begin
146 end else
147 if ParseInstruction then begin
148 end;
149 if Parser.CheckNextKind(tkEof) then begin
150 end else Parser.Expect(tkEol);
151 end;
152 Parser.Expect(tkEof);
153 UpdateLabelRefs;
154 Error('Compilation finished.', Point(0, 0));
155end;
156
157function TAssembler.ParseVar: Boolean;
158var
159 TokenName: TToken;
160 TokenValue: TToken;
161 Number: QWord;
162begin
163 Result := False;
164 if Parser.CheckNextAndRead(tkIdentifier, 'VAR') then begin
165 Result := True;
166 while True do begin
167 TokenName := Parser.ReadNext;
168 if TokenName.Kind = tkIdentifier then begin
169 TokenValue := Parser.ReadNext;
170 if TokenValue.Kind = tkNumber then begin
171 if not Labels.ContainsKey(TokenName.Value) and not Variables.ContainsKey(TokenName.Value) then begin
172 if TryStrToQWord(TokenValue.Value, Number) then
173 Variables.Add(TokenName.Value, Number)
174 else Error('Expected number', TokenValue.Pos);
175 end else Error('Duplicate variable name ' + TokenName.Value, TokenName.Pos);
176 end else Error('Expected variable value.', TokenValue.Pos);
177 end else Error('Expected variable name.', TokenName.Pos);
178 if Parser.CheckNextAndRead(tkSpecialSymbol, ',') then begin
179 Continue;
180 end;
181 Break;
182 end;
183 end;
184end;
185
186function TAssembler.ParseDb: Boolean;
187var
188 Token: TToken;
189begin
190 Result := False;
191 if Parser.CheckNextAndRead(tkIdentifier, 'DB') then begin
192 Result := True;
193 while True do begin
194 Token := Parser.ReadNext;
195 if Token.Kind = tkString then begin
196 //Memory.WriteString(Token.Value);
197 end else
198 ParseNumParam(Token, bw8);
199 if Parser.CheckNextAndRead(tkSpecialSymbol, ',') then begin
200 Continue;
201 end;
202 Break;
203 end;
204 end;
205end;
206
207function TAssembler.ParseOrg: Boolean;
208var
209 Token: TToken;
210begin
211 Result := False;
212 if Parser.CheckNextAndRead(tkIdentifier, 'ORG') then begin
213 Result := True;
214 Token := Parser.ReadNext;
215 if Token.Kind = tkNumber then begin
216 Memory.Position := StrToInt(Token.Value);
217 end else Error('Expected number but ' + Token.Value + ' found.', Token.Pos);
218 end;
219end;
220
221function TAssembler.ParseInstruction: Boolean;
222var
223 InstructionInfo: TInstructionInfo;
224 I: Integer;
225 Token: TToken;
226 LastPos: TParserPos;
227 Number: Integer;
228begin
229 Result := False;
230 LastPos := Parser.Pos;
231 Token := Parser.ReadNext;
232 InstructionInfo := InstructionSet.SearchName(Token.Value);
233 if Assigned(InstructionInfo) then begin
234 Result := True;
235 Memory.WritePos8(Integer(InstructionInfo.Instruction));
236 for I := 0 to Length(InstructionInfo.Params) - 1 do begin
237 if I > 0 then
238 Parser.Expect(tkSpecialSymbol, ',');
239 if InstructionInfo.Params[I] = ptNumber then begin
240 Token := Parser.ReadNext;
241 ParseNumParam(Token, AddressWidth);
242 end else
243 if InstructionInfo.Params[I] = ptNumber8 then begin
244 Token := Parser.ReadNext;
245 ParseNumParam(Token, bw8);
246 end else
247 if InstructionInfo.Params[I] = ptNumber16 then begin
248 Token := Parser.ReadNext;
249 ParseNumParam(Token, bw16);
250 end else
251 if InstructionInfo.Params[I] = ptNumber32 then begin
252 Token := Parser.ReadNext;
253 ParseNumParam(Token, bw32);
254 end else
255 if InstructionInfo.Params[I] = ptNumber64 then begin
256 Token := Parser.ReadNext;
257 ParseNumParam(Token, bw64);
258 end else
259 if InstructionInfo.Params[I] = ptReg then begin
260 Token := Parser.ReadNext;
261 if (Token.Value <> '') and (Token.Value[1] = 'R') then begin
262 Token.Value := Copy(Token.Value, 2, MaxInt);
263 if TryStrToInt(Token.Value, Number) then
264 Memory.WritePos8(Number)
265 else Error('Expected numeric register index error', Token.Pos);
266 end else Error('Expected register name starting with R character.', Token.Pos);
267 end else
268 if InstructionInfo.Params[I] = ptRegIndirect then begin
269 Parser.Expect(tkSpecialSymbol, '(');
270 Token := Parser.ReadNext;
271 if (Token.Value <> '') and (Token.Value[1] = 'R') then begin
272 Token.Value := Copy(Token.Value, 2, MaxInt);
273 if TryStrToInt(Token.Value, Number) then
274 Memory.WritePos8(Number)
275 else Error('Expected numeric register index error', Token.Pos);
276 end else Error('Expected register name starting with R character.', Token.Pos);
277 Parser.Expect(tkSpecialSymbol, ')');
278 end else
279 if InstructionInfo.Params[I] = ptRegIndirectIndex then begin
280 Parser.Expect(tkSpecialSymbol, '(');
281 Token := Parser.ReadNext;
282 if (Token.Value <> '') and (Token.Value[1] = 'R') then begin
283 Token.Value := Copy(Token.Value, 2, MaxInt);
284 if TryStrToInt(Token.Value, Number) then begin
285 Memory.WritePos8(Number);
286 Parser.Expect(tkSpecialSymbol, '+');
287 Token := Parser.ReadNext;
288 ParseNumParam(Token, bw8);
289 end else Error('Expected numeric register index error', Token.Pos);
290 end else Error('Expected register name starting with R character.', Token.Pos);
291 Parser.Expect(tkSpecialSymbol, ')');
292 end else
293 if InstructionInfo.Params[I] = ptRegIndirectGroup then begin
294 Parser.Expect(tkSpecialSymbol, '(');
295 Token := Parser.ReadNext;
296 if (Token.Value <> '') and (Token.Value[1] = 'R') then begin
297 Token.Value := Copy(Token.Value, 2, MaxInt);
298 if TryStrToInt(Token.Value, Number) then begin
299 Memory.WritePos8(Number);
300 Parser.Expect(tkSpecialSymbol, ':');
301 Token := Parser.ReadNext;
302 if (Token.Value <> '') and (Token.Value[1] = 'R') then begin
303 Token.Value := Copy(Token.Value, 2, MaxInt);
304 if TryStrToInt(Token.Value, Number) then begin
305 Memory.WritePos8(Number);
306 end else Error('Expected numeric register index error', Token.Pos);
307 end else Error('Expected register name starting with R character.', Token.Pos);
308 end else Error('Expected numeric register index error', Token.Pos);
309 end else Error('Expected register name starting with R character.', Token.Pos);
310 Parser.Expect(tkSpecialSymbol, ')');
311 end else
312 end;
313 end;
314 if not Result then Parser.Pos := LastPos;
315end;
316
317function TAssembler.ParseLabel: Boolean;
318var
319 LastPos: TParserPos;
320 Token: TToken;
321 Addr: QWord;
322begin
323 Result := False;
324 LastPos := Parser.Pos;
325 Token := Parser.ReadNext;
326 if Parser.CheckNextAndRead(tkSpecialSymbol, ':') then begin
327 Result := True;
328 if not Labels.TryGetValue(Token.Value, Addr) then begin
329 Labels.Add(Token.Value, Memory.Position);
330 end else Error('Duplicate label ' + Token.Value + '.', Token.Pos);
331 end;
332 if not Result then Parser.Pos := LastPos;
333end;
334
335procedure TAssembler.LoadFromFile(FileName: string);
336var
337 Lines: TStringList;
338begin
339 Lines := TStringList.Create;
340 try
341 Lines.LoadFromFile(FileName);
342 Compile(Lines.Text);
343 finally
344 Lines.Free;
345 end;
346end;
347
348function TAssembler.ParseStr(var Text: string; Separator: string): string;
349var
350 P: Integer;
351begin
352 P := Pos(Separator, Text);
353 if P > 0 then begin
354 Result := Trim(Copy(Text, 1, P - 1));
355 Text := Trim(Copy(Text, P + 1, MaxInt));
356 end else begin
357 Result := Trim(Text);
358 Text := '';
359 end;
360end;
361
362constructor TAssembler.Create;
363begin
364 Parser := TParser.Create;
365 Parser.OnError := Error;
366 Messages := TMessages.Create;
367 Memory := TMemory64.Create;
368 InstructionSet := TInstructionSet.Create;
369 Labels := TDictionary<string, QWord>.Create;
370 LabelRefs := TList<TLabelRef>.Create;
371 Variables := TDictionary<string, QWord>.Create;
372end;
373
374destructor TAssembler.Destroy;
375begin
376 FreeAndNil(Variables);
377 FreeAndNil(Labels);
378 FreeAndNil(LabelRefs);
379 FreeAndNil(InstructionSet);
380 FreeAndNil(Memory);
381 FreeAndNil(Messages);
382 FreeAndNil(Parser);
383 inherited;
384end;
385
386end.
387
Note: See TracBrowser for help on using the repository browser.