source: branches/ByteArray/Assembler.pas

Last change on this file was 59, checked in by chronos, 6 months ago
  • Fixed: Assembler and disassembler to work correctly with supported instructions.
File size: 14.7 KB
Line 
1unit Assembler;
2
3interface
4
5uses
6 Classes, SysUtils, Instructions, Cpu, Generics.Collections, BigInt,
7 Memory, Message, Parser;
8
9type
10 { TLabelRef }
11
12 TLabelRef = record
13 LabelName: string;
14 RefPos: Integer;
15 TextPos: TPoint;
16 class function Create(LabelName: string; RefPos: Integer; TextPos: TPoint): TLabelRef; static;
17 end;
18
19 { TAssembler }
20
21 TAssembler = class
22 private
23 FOnError: TErrorEvent;
24 Parser: TParser;
25 InstDataWidth: TBigIntSize;
26 InstAddressWidth: TBigIntSize;
27 function ParseVar: Boolean;
28 function ParseDb: Boolean;
29 function ParseOrg: Boolean;
30 function ParseInstruction: Boolean;
31 function ParseInstructionParameter(ParamType: TParamType; Memory: TMemory): Boolean;
32 function ParseLabel: Boolean;
33 procedure UpdateLabelRefs;
34 function ParseNumParam(out Number: TBigInt): Boolean;
35 function ParseReg(out RegIndex: TRegIndex): Boolean;
36 function ParseDataWidth(out Size: TBigIntSize): Boolean;
37 function ParseAddressWidth(out Size: TBigIntSize): Boolean;
38 public
39 InstructionSet: TInstructionInfos;
40 Memory: TMemory;
41 Labels: TDictionary<string, TBigInt>;
42 LabelRefs: TList<TLabelRef>;
43 Variables: TDictionary<string, TBigInt>;
44 Messages: TMessages;
45 DataWidth: TBigIntSize;
46 AddressWidth: TBigIntSize;
47 procedure Error(Text: string; Pos: TPoint);
48 procedure Compile(Source: string);
49 procedure LoadFromFile(FileName: string);
50 function ParseStr(var Text: string; Separator: string): string;
51 constructor Create;
52 destructor Destroy; override;
53 property OnError: TErrorEvent read FOnError write FOnError;
54 end;
55
56
57implementation
58
59resourcestring
60 SCompilationFinished = 'Compilation finished.';
61 SExpectedNumber = 'Expected number.';
62 SExpectedRegisterName = 'Expected register name.';
63 SExpectedNumericIndex = 'Expected numeric register index.';
64 SExpectedVariableName = 'Expected variable name.';
65 SExpectedVariableValue = 'Expected variable value.';
66 SUnsupportedParameterType = 'Unsupported parameter type';
67 SDuplicateLabel = 'Duplicate label %s.';
68 SLabelReferencedButNotDefined = 'Label %s referenced but not defined.';
69 SDuplicateVariableName = 'Duplicate variable name %s';
70 SExpectedParameter = 'Expected parameter';
71 SExpectedParameterType = 'Expected parameter type [%s]';
72 SExpectedNumberButFound = 'Expected number but %s found.';
73
74{ TLabelRef }
75
76class function TLabelRef.Create(LabelName: string; RefPos: Integer; TextPos: TPoint): TLabelRef;
77begin
78 Result.LabelName := LabelName;
79 Result.RefPos := RefPos;
80 Result.TextPos := TextPos;
81end;
82
83{ TAssembler }
84
85procedure TAssembler.UpdateLabelRefs;
86var
87 I: Integer;
88 Addr: TBigInt;
89begin
90 for I := 0 to LabelRefs.Count - 1 do begin
91 if Labels.TryGetValue(LabelRefs[I].LabelName, Addr) then
92 Memory.Write(LabelRefs[I].RefPos, 1, Addr)
93 else Error(Format(SLabelReferencedButNotDefined, [LabelRefs[I].LabelName]),
94 LabelRefs[I].TextPos);
95 end;
96end;
97
98function TAssembler.ParseNumParam(out Number: TBigInt): Boolean;
99var
100 Token: TToken;
101 LastPos: TParserPos;
102begin
103 LastPos := Parser.Pos;
104 Result := False;
105 Token := Parser.ReadNext;
106 if Token.Kind = tkNumber then begin
107 if TryStrToBigInt(Token.Value, Number) then begin
108 Result := True;
109 end;
110 end else
111 if Token.Kind = tkIdentifier then begin;
112 if Variables.TryGetValue(Token.Value, Number) then begin
113 Result := True;
114 end else
115 if Labels.TryGetValue(Token.Value, Number) then begin
116 Result := True;
117 end else begin
118 LabelRefs.Add(TLabelRef.Create(Token.Value, Memory.Position + 1, Parser.Pos.Pos));
119 Number := 0;
120 Result := True;
121 end;
122 end;
123 if not Result then Parser.Pos := LastPos;
124end;
125
126function TAssembler.ParseReg(out RegIndex: TRegIndex): Boolean;
127var
128 LastPos: TParserPos;
129 Token: TToken;
130 Index: Integer;
131begin
132 Result := False;
133 LastPos := Parser.Pos;
134 Token := Parser.ReadNext;
135 if (Length(Token.Value) >= 2) and (Token.Value[1] = 'R') then begin
136 if TryStrToInt(Copy(Token.Value, 2, MaxInt), Index) then begin
137 RegIndex := Index;
138 Result := True;
139 end;
140 end;
141 if not Result then Parser.Pos := LastPos;
142end;
143
144function TAssembler.ParseDataWidth(out Size: TBigIntSize): Boolean;
145var
146 LastPos: TParserPos;
147 Token: TToken;
148 Index: Integer;
149begin
150 Result := False;
151 LastPos := Parser.Pos;
152 Token := Parser.ReadNext;
153 if (Length(Token.Value) >= 2) and (Token.Value[1] = 'D') then begin
154 if TryStrToInt(Copy(Token.Value, 2, MaxInt), Index) then begin
155 Size := Index;
156 Result := True;
157 end;
158 end;
159 if not Result then Parser.Pos := LastPos;
160end;
161
162function TAssembler.ParseAddressWidth(out Size: TBigIntSize): Boolean;
163var
164 LastPos: TParserPos;
165 Token: TToken;
166 Index: Integer;
167begin
168 Result := False;
169 LastPos := Parser.Pos;
170 Token := Parser.ReadNext;
171 if (Length(Token.Value) >= 2) and (Token.Value[1] = 'A') then begin
172 if TryStrToInt(Copy(Token.Value, 2, MaxInt), Index) then begin
173 Size := Index;
174 Result := True;
175 end;
176 end;
177 if not Result then Parser.Pos := LastPos;
178end;
179
180procedure TAssembler.Error(Text: string; Pos: TPoint);
181begin
182 Messages.AddMessage(Text, Pos);
183 if Assigned(FOnError) then
184 FOnError(Text, Pos);
185end;
186
187procedure TAssembler.Compile(Source: string);
188begin
189 Messages.Clear;
190 Memory.Size := 1000;
191 Memory.Position := 0;
192 Labels.Clear;
193 LabelRefs.Clear;
194 Variables.Clear;
195 Parser.Reset;
196 Parser.Source := Source;
197 while not Parser.CheckNextKind(tkEof) do begin
198 ParseLabel;
199 if ParseVar then begin
200 end else
201 if ParseDb then begin
202 end else
203 if ParseOrg then begin
204 end else
205 if ParseInstruction then begin
206 end;
207 if Parser.CheckNextKind(tkEof) then begin
208 end else Parser.Expect(tkEol);
209 end;
210 Parser.Expect(tkEof);
211 UpdateLabelRefs;
212 Error(SCompilationFinished, Point(0, 0));
213 Memory.Size := Memory.Position;
214end;
215
216function TAssembler.ParseVar: Boolean;
217var
218 TokenName: TToken;
219 TokenValue: TToken;
220 Number: TBigInt;
221begin
222 Result := False;
223 if Parser.CheckNextAndRead(tkIdentifier, 'VAR') then begin
224 Result := True;
225 while True do begin
226 TokenName := Parser.ReadNext;
227 if TokenName.Kind = tkIdentifier then begin
228 TokenValue := Parser.ReadNext;
229 if TokenValue.Kind = tkNumber then begin
230 if not Labels.ContainsKey(TokenName.Value) and not Variables.ContainsKey(TokenName.Value) then begin
231 if TryStrToBigInt(TokenValue.Value, Number) then
232 Variables.Add(TokenName.Value, Number)
233 else Error(SExpectedNumber, TokenValue.Pos);
234 end else Error(Format(SDuplicateVariableName, [TokenName.Value]), TokenName.Pos);
235 end else Error(SExpectedVariableValue, TokenValue.Pos);
236 end else Error(SExpectedVariableName, TokenName.Pos);
237 if Parser.CheckNextAndRead(tkSpecialSymbol, ',') then begin
238 Continue;
239 end;
240 Break;
241 end;
242 end;
243end;
244
245function TAssembler.ParseDb: Boolean;
246var
247 Token: TToken;
248 Number: TBigInt;
249begin
250 Result := False;
251 if Parser.CheckNextAndRead(tkIdentifier, 'DB') then begin
252 Result := True;
253 while True do begin
254 if ParseNumParam(Number) then begin
255 Memory.WritePos(1, Number);
256 end else begin
257 Token := Parser.ReadNext;
258 if Token.Kind = tkString then begin
259 Memory.WriteStringPos(Token.Value);
260 end else
261 if Token.Kind = tkEof then
262 else if Token.Kind = tkEol then
263 else raise Exception.Create(SExpectedParameter);
264 end;
265 if Parser.CheckNextAndRead(tkSpecialSymbol, ',') then begin
266 Continue;
267 end;
268 Break;
269 end;
270 end;
271end;
272
273function TAssembler.ParseOrg: Boolean;
274var
275 Token: TToken;
276begin
277 Result := False;
278 if Parser.CheckNextAndRead(tkIdentifier, 'ORG') then begin
279 Result := True;
280 Token := Parser.ReadNext;
281 if Token.Kind = tkNumber then begin
282 Memory.Position := StrToInt(Token.Value);
283 end else Error(Format(SExpectedNumberButFound, [Token.Value]), Token.Pos);
284 end;
285end;
286
287function TAssembler.ParseInstruction: Boolean;
288var
289 InstructionInfos: TInstructionInfos;
290 I: Integer;
291 Token: TToken;
292 LastPos: TParserPos;
293 J: Integer;
294 AllowedParams: set of TParamType;
295 ParamType: TParamType;
296 ParamOk: Boolean;
297 LastMessagesCount: Integer;
298 Text: string;
299 EmptyParam: Boolean;
300 InstructionMemory: TMemory;
301 ParamMemory: TMemory;
302begin
303 Result := False;
304 LastPos := Parser.Pos;
305 Token := Parser.ReadNext;
306 InstructionInfos := InstructionSet.SearchByNameMultiple(Token.Value);
307 InstDataWidth := DataWidth;
308 InstAddressWidth := AddressWidth;
309 InstructionMemory := TMemory.Create;
310 InstructionMemory.Grow := True;
311 ParamMemory := TMemory.Create;
312 ParamMemory.Grow := True;
313 J := 0;
314 if InstructionInfos.Count > 0 then
315 repeat
316 EmptyParam := False;
317
318 if J > 0 then
319 Parser.Expect(tkSpecialSymbol, ',');
320
321 // Get allowed parameters
322 AllowedParams := [];
323 for I := 0 to InstructionInfos.Count - 1 do begin
324 if Length(InstructionInfos[I].Params) > J then
325 AllowedParams := AllowedParams + [InstructionInfos[I].Params[J]]
326 else EmptyParam := True;
327 end;
328
329 if AllowedParams <> [] then begin
330 ParamOk := False;
331 for ParamType := Low(TParamType) to High(TParamType) do begin
332 if ParamType in AllowedParams then begin
333 LastMessagesCount := Messages.Count;
334 ParamMemory.Clear;
335 if ParseInstructionParameter(ParamType, ParamMemory) and
336 (LastMessagesCount = Messages.Count) then begin
337 ParamOk := True;
338 InstructionMemory.WriteMemoryPos(ParamMemory);
339 Break;
340 end else Messages.Count := LastMessagesCount;
341 end;
342 end;
343 if not ParamOk then begin
344 if EmptyParam then begin
345 ParamType := ptNone;
346 end else begin
347 Text := '';
348 for ParamType := Low(TParamType) to High(TParamType) do begin
349 if ParamType in AllowedParams then begin
350 if Text <> '' then Text := Text + ' or ';
351 Text := Text + ParamTypeString[ParamType]
352 end;
353 end;
354
355 Error(Format(SExpectedParameterType, [Text]), Parser.Pos.Pos);
356 Break;
357 end;
358 end;
359 end;
360
361 // Remove not matching instruction infos
362 for I := InstructionInfos.Count - 1 downto 0 do
363 if (Length(InstructionInfos[I].Params) > J) and
364 (InstructionInfos[I].Params[J] <> ParamType) then InstructionInfos.Delete(I);
365
366 if (InstructionInfos.Count = 1) and (J >= (Length(InstructionInfos[0].Params) - 1)) then begin
367 Memory.WritePos(1, Byte(InstructionInfos[0].Instruction));
368 Memory.WriteMemoryPos(InstructionMemory);
369 Result := True;
370 Break;
371 end;
372
373 Inc(J);
374 until False;
375
376 if not Result then Parser.Pos := LastPos;
377 FreeAndNil(ParamMemory);
378 FreeAndNil(InstructionMemory);
379end;
380
381function TAssembler.ParseInstructionParameter(ParamType: TParamType; Memory: TMemory): Boolean;
382var
383 LastPos: TParserPos;
384 I: Integer;
385 RegIndex: TRegIndex;
386 Number: TBigInt;
387 Token: TToken;
388begin
389 LastPos := Parser.Pos;
390 Result := True;
391 case ParamType of
392 ptData: begin
393 if ParseNumParam(Number) then Memory.WritePos(InstDataWidth, Number)
394 else begin
395 Error(SExpectedNumber, Parser.Pos.Pos);
396 Result := False;
397 end;
398 end;
399 ptAddress: begin
400 if ParseNumParam(Number) then Memory.WritePos(InstAddressWidth, Number)
401 else begin
402 Error(SExpectedNumber, Parser.Pos.Pos);
403 Result := False;
404 end;
405 end;
406 ptReg: begin
407 if ParseReg(RegIndex) then Memory.WritePos(1, Byte(RegIndex))
408 else begin
409 Error(SExpectedRegisterName, Parser.Pos.Pos);
410 Result := False;
411 end;
412 end;
413 ptRegIndirect: begin
414 if not Parser.Expect(tkSpecialSymbol, '(') then Result := False;
415 if ParseReg(RegIndex) then Memory.WritePos(1, Byte(RegIndex))
416 else begin
417 Error(SExpectedRegisterName, Parser.Pos.Pos);
418 Result := False;
419 end;
420 if not Parser.Expect(tkSpecialSymbol, ')') then Result := False;;
421 end;
422 ptRegIndirectIndex: begin
423 if not Parser.Expect(tkSpecialSymbol, '(') then Result := False;
424 if ParseReg(RegIndex) then begin
425 Memory.WritePos(1, Byte(RegIndex));
426 if not Parser.Expect(tkSpecialSymbol, '+') then Result := False;
427 if ParseNumParam(Number) then Memory.WritePos(InstAddressWidth, Number)
428 else begin
429 Error(SExpectedNumericIndex, Parser.Pos.Pos);
430 Result := False;
431 end;
432 end else begin
433 Error(SExpectedRegisterName, Parser.Pos.Pos);
434 Result := False;
435 end;
436 if not Parser.Expect(tkSpecialSymbol, ')') then Result := False;
437 end;
438 ptDataWidth: begin
439 if ParseDataWidth(InstDataWidth) then begin
440 Memory.WritePos(1, InstDataWidth)
441 end else begin
442 Error(SExpectedRegisterName, Parser.Pos.Pos);
443 Result := False;
444 end;
445 end;
446 ptAddressWidth: begin
447 if ParseAddressWidth(InstAddressWidth) then begin
448 Memory.WritePos(1, InstAddressWidth)
449 end else begin
450 Error(SExpectedRegisterName, Parser.Pos.Pos);
451 Result := False;
452 end;
453 end;
454 else begin
455 Result := False;
456 Error(SUnsupportedParameterType, Parser.Pos.Pos);
457 end;
458 end;
459 if not Result then Parser.Pos := LastPos;
460end;
461
462function TAssembler.ParseLabel: Boolean;
463var
464 LastPos: TParserPos;
465 Token: TToken;
466 Addr: TBigInt;
467begin
468 Result := False;
469 LastPos := Parser.Pos;
470 Token := Parser.ReadNext;
471 if Parser.CheckNextAndRead(tkSpecialSymbol, ':') then begin
472 Result := True;
473 if not Labels.TryGetValue(Token.Value, Addr) then begin
474 Labels.Add(Token.Value, Memory.Position);
475 end else Error(Format(SDuplicateLabel, [Token.Value]), Token.Pos);
476 end;
477 if not Result then Parser.Pos := LastPos;
478end;
479
480procedure TAssembler.LoadFromFile(FileName: string);
481var
482 Lines: TStringList;
483begin
484 Lines := TStringList.Create;
485 try
486 Lines.LoadFromFile(FileName);
487 Compile(Lines.Text);
488 finally
489 Lines.Free;
490 end;
491end;
492
493function TAssembler.ParseStr(var Text: string; Separator: string): string;
494var
495 P: Integer;
496begin
497 P := Pos(Separator, Text);
498 if P > 0 then begin
499 Result := Trim(Copy(Text, 1, P - 1));
500 Text := Trim(Copy(Text, P + 1, MaxInt));
501 end else begin
502 Result := Trim(Text);
503 Text := '';
504 end;
505end;
506
507constructor TAssembler.Create;
508begin
509 Parser := TParser.Create;
510 Parser.OnError := Error;
511 Messages := TMessages.Create;
512 Memory := TMemory.Create;
513 InstructionSet := TInstructionInfos.Create;
514 InstructionSet.Init;
515 Labels := TDictionary<string, TBigInt>.Create;
516 LabelRefs := TList<TLabelRef>.Create;
517 Variables := TDictionary<string, TBigInt>.Create;
518end;
519
520destructor TAssembler.Destroy;
521begin
522 FreeAndNil(Variables);
523 FreeAndNil(Labels);
524 FreeAndNil(LabelRefs);
525 FreeAndNil(InstructionSet);
526 FreeAndNil(Memory);
527 FreeAndNil(Messages);
528 FreeAndNil(Parser);
529 inherited;
530end;
531
532end.
533
Note: See TracBrowser for help on using the repository browser.