source: branches/UltimatOS/UAssembler.pas

Last change on this file was 32, checked in by chronos, 23 months ago
  • Added: Interrupt handling.
File size: 14.5 KB
Line 
1unit UAssembler;
2
3interface
4
5uses
6 Classes, SysUtils, Generics.Collections, UMemory, UCpu;
7
8type
9 TInstructionParam = (ipNone, ipRegIndex, ipAddress, ipData, ipIndex,
10 ipRegIndexRel, ipAddressRel);
11
12 TInstructionDef = class
13 Code: TInstruction;
14 Name: string;
15 Param1: TInstructionParam;
16 Param2: TInstructionParam;
17 end;
18
19 { TInstructionDefs }
20
21 TInstructionDefs = class(TObjectList<TInstructionDef>)
22 function AddNew(Code: TInstruction; Name: string; Param1: TInstructionParam = ipNone;
23 Param2: TInstructionParam = ipNone): TInstructionDef;
24 function SearchByName(Name: string): TInstructionDef;
25 end;
26
27 { TLabel }
28
29 TLabel = class
30 Name: string;
31 Address: TAddress;
32 ForwardRefs: array of TAddress;
33 procedure AddForwardRef(Address: TAddress);
34 end;
35
36 { TLabels }
37
38 TLabels = class(TObjectList<TLabel>)
39 function AddNew(Name: string; Address: TAddress): TLabel;
40 function SearchByName(Name: string): TLabel;
41 end;
42
43 TConstant = class
44 Name: string;
45 Value: Integer;
46 end;
47
48 { TConstants }
49
50 TConstants = class(TObjectList<TConstant>)
51 function AddNew(Name: string; Value: Integer): TConstant;
52 function SearchByName(Name: string): TConstant;
53 end;
54
55 { TAssembler }
56
57 TAssembler = class
58 private
59 InstructionDefs: TInstructionDefs;
60 Labels: TLabels;
61 Constants: TConstants;
62 LineNumber: Integer;
63 function ParseText(var Text: string; Separator: Char): string;
64 function ParseConst(Text: string; out Value: Integer): Boolean;
65 function ParseLabel(Text: string; out Value: Integer): Boolean;
66 function ParseNumber(Text: string; out Value: Integer): Boolean;
67 procedure WriteParam(Param: TInstructionParam; var Text: string);
68 procedure Error(Text: string);
69 public
70 Memory: TMemory;
71 IP: Integer;
72 procedure Parse(Lines: TStrings);
73 procedure ParseLine(Text: string);
74 procedure WriteInstruction(Instruction: TInstruction);
75 procedure WriteAddress(Address: TAddress);
76 procedure WriteData(Data: TData);
77 procedure WriteIndex(Index: Byte);
78 procedure WriteReg(Index: Byte);
79 procedure WriteByte(Value: Byte);
80 procedure WriteWord(Value: Word);
81 procedure WriteCardinal(Value: Cardinal);
82 constructor Create;
83 destructor Destroy; override;
84 end;
85
86implementation
87
88{ TLabel }
89
90procedure TLabel.AddForwardRef(Address: TAddress);
91begin
92 SetLength(ForwardRefs, Length(ForwardRefs) + 1);
93 ForwardRefs[Length(ForwardRefs) - 1] := Address;
94end;
95
96{ TConstants }
97
98function TConstants.AddNew(Name: string; Value: Integer): TConstant;
99begin
100 Result := TConstant.Create;
101 Result.Name := Name;
102 Result.Value := Value;
103 Add(Result);
104end;
105
106function TConstants.SearchByName(Name: string): TConstant;
107var
108 I: Integer;
109begin
110 I := 0;
111 while (I < Count) and (Items[I].Name <> Name) do Inc(I);
112 if I < Count then Result := Items[I]
113 else Result := nil;
114end;
115
116{ TLabels }
117
118function TLabels.AddNew(Name: string; Address: TAddress): TLabel;
119begin
120 Result := TLabel.Create;
121 Result.Name := Name;
122 Result.Address := Address;
123 Add(Result);
124end;
125
126function TLabels.SearchByName(Name: string): TLabel;
127var
128 I: Integer;
129begin
130 I := 0;
131 while (I < Count) and (Items[I].Name <> Name) do Inc(I);
132 if I < Count then Result := Items[I]
133 else Result := nil;
134end;
135
136{ TInstructionDefs }
137
138function TInstructionDefs.AddNew(Code: TInstruction; Name: string;
139 Param1: TInstructionParam; Param2: TInstructionParam): TInstructionDef;
140begin
141 Result := TInstructionDef.Create;
142 Result.Code := Code;
143 Result.Name := Name;
144 Result.Param1 := Param1;
145 Result.Param2 := Param2;
146 Add(Result);
147end;
148
149function TInstructionDefs.SearchByName(Name: string): TInstructionDef;
150var
151 I: Integer;
152begin
153 I := 0;
154 while (I < Count) and (Items[I].Name <> Name) do Inc(I);
155 if I < Count then Result := Items[I]
156 else Result := nil;
157end;
158
159
160{ TAssembler }
161
162function TAssembler.ParseText(var Text: string; Separator: Char
163 ): string;
164var
165 I: Integer;
166begin
167 I := Pos(Separator, Text);
168 if I > 0 then begin
169 Result := Copy(Text, 1, I - 1);
170 Delete(Text, 1, I);
171 end else begin
172 Result := Text;
173 Text := '';
174 end;
175end;
176
177function TAssembler.ParseConst(Text: string; out Value: Integer): Boolean;
178var
179 FoundConstant: TConstant;
180begin
181 FoundConstant := Constants.SearchByName(UpperCase(Text));
182 if Assigned(FoundConstant) then begin
183 Result := True;
184 Value := FoundConstant.Value;
185 end;
186 Result := False;
187end;
188
189function TAssembler.ParseLabel(Text: string; out Value: Integer): Boolean;
190var
191 FoundLabel: TLabel;
192begin
193 Result := True;
194 FoundLabel := Labels.SearchByName(UpperCase(Text));
195 if Assigned(FoundLabel) then begin
196 // Existing label
197 if FoundLabel.Address = -1 then begin
198 FoundLabel.AddForwardRef(IP);
199 Value := 0;
200 end else
201 Value := FoundLabel.Address;
202 end else begin
203 // Forward label reference
204 with Labels.AddNew(UpperCase(Text), -1) do begin
205 AddForwardRef(IP);
206 end;
207 Value := 0;
208 end;
209end;
210
211function TAssembler.ParseNumber(Text: string; out Value: Integer): Boolean;
212begin
213 if TryStrToInt(Text, Value) then begin
214 Result := True;
215 end else
216 Result := False;
217end;
218
219procedure TAssembler.WriteParam(Param: TInstructionParam; var Text: string);
220var
221 Address: string;
222 FoundLabel: TLabel;
223 FoundConstant: TConstant;
224 Value: Integer;
225begin
226 case Param of
227 ipAddress: begin
228 Address := Trim(ParseText(Text, ','));
229 if TryStrToInt(Address, Value) then begin
230 WriteAddress(Value);
231 end else begin
232 FoundConstant := Constants.SearchByName(UpperCase(Address));
233 if Assigned(FoundConstant) then begin
234 WriteAddress(FoundConstant.Value);
235 Exit;
236 end;
237 ParseLabel(Address, Value);
238 WriteAddress(Value);
239 end;
240 end;
241 ipAddressRel: begin
242 Address := Trim(ParseText(Text, ','));
243 if Address.StartsWith('(') and Address.EndsWith(')') then begin
244 Address := Copy(Address, 2, Length(Address) - 2);
245 if TryStrToInt(Address, Value) then begin
246 WriteAddress(Value);
247 end else begin
248 FoundConstant := Constants.SearchByName(UpperCase(Address));
249 if Assigned(FoundConstant) then begin
250 WriteAddress(FoundConstant.Value);
251 Exit;
252 end;
253 ParseLabel(Address, Value);
254 WriteAddress(Value);
255 end;
256 end else Error('Expected indirect address ' + Address);
257 end;
258 ipRegIndex: begin
259 Address := Trim(ParseText(Text, ','));
260 if Address.StartsWith('R') then begin
261 WriteReg(StrToInt(Copy(Address, 2, MaxInt)));
262 end else Error('Expected register ' + Address);
263 end;
264 ipRegIndexRel: begin
265 Address := Trim(ParseText(Text, ','));
266 if Address.StartsWith('(') and Address.EndsWith(')') then begin
267 Address := Copy(Address, 2, Length(Address) - 2);
268 if Address.StartsWith('R') then begin
269 WriteReg(StrToInt(Copy(Address, 2, MaxInt)));
270 end else Error('Expected register ' + Address);
271 end else Error('Expected indirect address ' + Address);
272 end;
273 ipData: begin
274 Address := Trim(ParseText(Text, ','));
275 if TryStrToInt(Address, Value) then begin
276 WriteData(Value);
277 end else begin
278 FoundConstant := Constants.SearchByName(UpperCase(Address));
279 if Assigned(FoundConstant) then begin
280 WriteData(FoundConstant.Value);
281 Exit;
282 end;
283 ParseLabel(Address, Value);
284 WriteData(Value);
285 end;
286 end;
287 ipIndex: begin
288 Address := Trim(ParseText(Text, ','));
289 if TryStrToInt(Address, Value) then begin
290 WriteIndex(Value);
291 end else begin
292 FoundConstant := Constants.SearchByName(UpperCase(Address));
293 if Assigned(FoundConstant) then begin
294 WriteIndex(FoundConstant.Value);
295 end else Error('Unknown constant ' + Address);
296 end;
297 end;
298 end;
299end;
300
301procedure TAssembler.Error(Text: string);
302begin
303 raise Exception.Create(IntToStr(LineNumber) + ': ' + Text);
304end;
305
306procedure TAssembler.Parse(Lines: TStrings);
307var
308 I: Integer;
309begin
310 for I := 0 to Lines.Count - 1 do begin
311 LineNumber := I + 1;
312 ParseLine(Lines[I]);
313 end;
314 for I := 0 to Labels.Count - 1 do begin
315 if Length(Labels[I].ForwardRefs) > 0 then
316 Error('Forward reference not satisfied ' + Labels[I].Name);
317 end;
318end;
319
320procedure TAssembler.ParseLine(Text: string);
321var
322 FoundLabel: TLabel;
323 FoundConstant: TConstant;
324 LabelName: string;
325 InstructionName: string;
326 InstructionDef: TInstructionDef;
327 I: Integer;
328 ConstName: string;
329 ConstValue: string;
330 Lines: TStringList;
331 Param: string;
332 Num: Integer;
333begin
334 // Remove comments
335 I := Pos(';', Text);
336 if I > 0 then begin
337 Text := Trim(Copy(Text, 1, I - 1));
338 end;
339
340 // Process labels
341 if Pos(':', Text) > 0 then begin
342 LabelName := Trim(ParseText(Text, ':'));
343 FoundLabel := Labels.SearchByName(UpperCase(LabelName));
344 if not Assigned(FoundLabel) then begin
345 Labels.AddNew(UpperCase(LabelName), IP);
346 end else begin
347 if Length(FoundLabel.ForwardRefs) > 0 then begin
348 FoundLabel.Address := IP;
349 // Update forward references
350 for I := 0 to Length(FoundLabel.ForwardRefs) - 1 do
351 PAddress(@Memory.Data[FoundLabel.ForwardRefs[I]])^ := IP;
352 SetLength(FoundLabel.ForwardRefs, 0);
353 end else
354 Error('Duplicate label ' + LabelName);
355 end;
356 end;
357 Text := Trim(Text);
358 InstructionName := Trim(ParseText(Text, ' '));
359 if InstructionName.StartsWith('.') then begin
360 InstructionName := Copy(InstructionName, 2, MaxInt);
361 if InstructionName = 'const' then begin
362 ConstName := Trim(ParseText(Text, ' '));
363 ConstValue := Trim(Text);
364 FoundConstant := Constants.SearchByName(UpperCase(ConstName));
365 if Assigned(FoundConstant) then begin
366 FoundConstant.Value := StrToInt(ConstValue);
367 end else begin
368 Constants.AddNew(UpperCase(ConstName), StrToInt(ConstValue));
369 end;
370 end else
371 if InstructionName = 'include' then begin
372 if FileExists(Text) then begin
373 Lines := TStringList.Create;
374 try
375 Lines.LoadFromFile(Text);
376 Parse(Lines);
377 finally
378 Lines.Free;
379 end;
380 end else Error('File ' + Text + ' doesn''t exist');
381 end else
382 if InstructionName = 'db' then begin
383 while Text <> '' do begin
384 Param := ParseText(Text, ',');
385 if Param.StartsWith('"') and Param.EndsWith('"') then begin
386 Param := Copy(Param, 2, Length(Param) - 2);
387 for I := 1 to Length(Param) do
388 WriteByte(Ord(Param[I]));
389 end else WriteByte(StrToInt(Param));
390 end;
391 end else
392 if InstructionName = 'dw' then begin
393 while Text <> '' do begin
394 Param := ParseText(Text, ',');
395 if Param.StartsWith('"') and Param.EndsWith('"') then begin
396 Param := Copy(Param, 2, Length(Param) - 2);
397 for I := 1 to Length(Param) do
398 WriteWord(Ord(Param[I]));
399 end else WriteWord(StrToInt(Param));
400 end;
401 end else
402 if InstructionName = 'dd' then begin
403 while Text <> '' do begin
404 Param := Trim(ParseText(Text, ','));
405 if Param.StartsWith('"') and Param.EndsWith('"') then begin
406 Param := Copy(Param, 2, Length(Param) - 2);
407 for I := 1 to Length(Param) do
408 WriteCardinal(Ord(Param[I]));
409 end else begin
410 if ParseNumber(Param, Num) then
411 WriteCardinal(Num)
412 else if ParseConst(Param, Num) then
413 WriteCardinal(Num)
414 else if ParseLabel(Param, Num) then
415 WriteCardinal(Num);
416 end;
417 end;
418 end else
419 if InstructionName = 'org' then begin
420 IP := StrToInt(Text);
421 end else Error('Unsupported directive name ' + InstructionName);
422 end else begin
423 InstructionDef := InstructionDefs.SearchByName(UpperCase(InstructionName));
424 if Assigned(InstructionDef) then begin
425 WriteInstruction(InstructionDef.Code);
426 WriteParam(InstructionDef.Param1, Text);
427 WriteParam(InstructionDef.Param2, Text);
428 end else begin
429 if InstructionName <> '' then
430 Error('Unknown instruction ' + InstructionName);
431 end;
432 end;
433end;
434
435procedure TAssembler.WriteInstruction(Instruction: TInstruction);
436begin
437 Memory.Data[IP] := Byte(Instruction);
438 Inc(IP);
439end;
440
441procedure TAssembler.WriteAddress(Address: TAddress);
442begin
443 PAddress(@Memory.Data[IP])^ := Address;
444 Inc(IP, SizeOf(TAddress));
445end;
446
447procedure TAssembler.WriteData(Data: TData);
448begin
449 PData(@Memory.Data[IP])^ := Data;
450 Inc(IP, SizeOf(TData));
451end;
452
453procedure TAssembler.WriteIndex(Index: Byte);
454begin
455 Memory.Data[IP] := Index;
456 Inc(IP);
457end;
458
459procedure TAssembler.WriteReg(Index: Byte);
460begin
461 Memory.Data[IP] := Index;
462 Inc(IP);
463end;
464
465procedure TAssembler.WriteByte(Value: Byte);
466begin
467 Memory.Data[IP] := Value;
468 Inc(IP);
469end;
470
471procedure TAssembler.WriteWord(Value: Word);
472begin
473 PWord(@Memory.Data[IP])^ := Value;
474 Inc(IP, SizeOf(Word));
475end;
476
477procedure TAssembler.WriteCardinal(Value: Cardinal);
478begin
479 PCardinal(@Memory.Data[IP])^ := Value;
480 Inc(IP, SizeOf(Cardinal));
481end;
482
483constructor TAssembler.Create;
484begin
485 Labels := TLabels.Create;
486 Constants := TConstants.Create;
487 InstructionDefs := TInstructionDefs.Create;
488 with InstructionDefs do begin
489 AddNew(inNop, 'NOP');
490 AddNew(inHalt, 'HALT');
491 AddNew(inSet, 'SET', ipRegIndex, ipData);
492 AddNew(inInput, 'IN', ipRegIndex, ipAddressRel);
493 AddNew(inOutput, 'OUT', ipAddressRel, ipRegIndex);
494 AddNew(inInc, 'INC', ipRegIndex);
495 AddNew(inDec, 'DEC', ipRegIndex);
496 AddNew(inAdd, 'ADD', ipRegIndex, ipRegIndex);
497 AddNew(inSub, 'SUB', ipRegIndex, ipRegIndex);
498 AddNew(inJp, 'JP', ipAddress);
499 AddNew(inJpz, 'JPZ', ipRegIndex, ipAddress);
500 AddNew(inJpnz, 'JPNZ', ipRegIndex, ipAddress);
501 AddNew(inCall, 'CALL', ipAddress);
502 AddNew(inRet, 'RET');
503 AddNew(inPush, 'PUSH', ipRegIndex);
504 AddNew(inPop, 'POP', ipRegIndex);
505 AddNew(inCopy, 'COPY', ipRegIndex, ipRegIndex);
506 AddNew(inShl, 'SHL', ipRegIndex, ipIndex);
507 AddNew(inShr, 'SHR', ipRegIndex, ipIndex);
508 AddNew(inLoad, 'LD', ipRegIndex, ipRegIndexRel);
509 AddNew(inLoadi, 'LDI', ipRegIndex, ipAddressRel);
510 AddNew(inStore, 'ST', ipRegIndexRel, ipRegIndex);
511 AddNew(inMul, 'MUL', ipRegIndex, ipRegIndex);
512 AddNew(inAnd, 'AND', ipRegIndex, ipRegIndex);
513 AddNew(inAndi, 'ANDI', ipRegIndex, ipData);
514 AddNew(inOr, 'OR', ipRegIndex, ipRegIndex);
515 AddNew(inXor, 'XOR', ipRegIndex, ipRegIndex);
516 AddNew(inInt, 'INT', ipIndex);
517 AddNew(inReti, 'RETI');
518 AddNew(inEnableInt, 'EI');
519 AddNew(inDisableInt, 'DI');
520 end;
521end;
522
523destructor TAssembler.Destroy;
524begin
525 FreeAndNil(InstructionDefs);
526 FreeAndNil(Constants);
527 FreeAndNil(Labels);
528 inherited;
529end;
530
531end.
532
Note: See TracBrowser for help on using the repository browser.