source: branches/virt simple/UMachine.pas

Last change on this file was 138, checked in by chronos, 6 years ago
  • Modified: Preparation for support for multiple memory types.
File size: 13.7 KB
Line 
1unit UMachine;
2
3{$mode delphi}{$H+}
4
5interface
6
7uses
8 Classes, SysUtils, fgl, UBigInt;
9
10type
11 TOpcode = (opNoOperation, opHalt, opLoad, opIncrement, opDecrement, opPush,
12 opPop, opCall, opReturn, opJumpAbsolute, opJumpRelative, opAdd, opSubtract,
13 opMultiply, opDivide, opModulo, opExchange, opInput, opOutput, opSkip,
14 opSystemCall);
15 TSpecialRegister = (srIP, srSP);
16
17const
18 OpcodeString: array[TOpcode] of string = ('NOP', 'HALT', 'LD',
19 'INC', 'DEC', 'PUSH', 'POP', 'CALL', 'RET', 'JP', 'JR', 'ADD', 'SUB', 'MUL',
20 'DIV', 'MOD', 'EX', 'IN', 'OUT', 'SKIP', 'SYS');
21 SpecialRegisterString: array[TSpecialRegister] of string = ('IP', 'SP');
22
23type
24 TParamType = (ptNone, ptConst, ptRegister, ptSpecialRegister);
25 TCondition = (cdZero, cdNotZero, cdNegative, cdPositive);
26 TMemoryType = (mtNone, mtData);
27
28 TValue = Integer;
29 //TValue = TBigInt;
30 TAddress = Integer;
31
32 { TParam }
33
34 TParam = class
35 ParamType: TParamType;
36 MemoryType: TMemoryType;
37 Value: TValue;
38 function GetString: string;
39 procedure SetParam(ParamType: TParamType; MemoryType: TMemoryType; Value: TValue);
40 end;
41
42 { TInstruction }
43
44 TInstruction = class
45 Opcode: TOpcode;
46 Params: array[0..1] of TParam;
47 constructor Create;
48 destructor Destroy; override;
49 function GetString: string;
50 end;
51
52 TInstructions = class(TFPGObjectList<TInstruction>)
53 procedure AddInst(Opcode: TOpcode); overload;
54 procedure AddInst(Opcode: TOpcode;
55 ParamType1: TParamType; Indirect1: TMemoryType; Value1: TValue); overload;
56 procedure AddInst(Opcode: TOpcode;
57 ParamType1: TParamType; Indirect1: TMemoryType; Value1: TValue;
58 ParamType2: TParamType; Indirect2: TMemoryType; Value2: TValue); overload;
59 end;
60
61 TPortOutEvent = procedure (Sender: TObject; Port: Integer; Value: TValue) of object;
62 TPortInEvent = function (Sender: TObject; Port: Integer): TValue of object;
63
64 TInstructionHandler = procedure of object;
65 TSystemCallEvent = procedure(Index: TValue) of object;
66
67 TMachineThread = class;
68
69 { TMachine }
70
71 TMachine = class
72 private
73 FOnPortIn: TPortInEvent;
74 FOnPortOut: TPortOutEvent;
75 FOnSystemCall: TSystemCallEvent;
76 FRunning: Boolean;
77 InstructionHandlers: array[TOpcode] of TInstructionHandler;
78 Thread: TMachineThread;
79 function GetValue(Param: TParam): TValue;
80 procedure InstructionAdd;
81 procedure InstructionCall;
82 procedure InstructionDecrement;
83 procedure InstructionDivide;
84 procedure InstructionExchange;
85 procedure InstructionHalt;
86 procedure InstructionIncrement;
87 procedure InstructionInput;
88 procedure InstructionJumpAbsolute;
89 procedure InstructionJumpRelative;
90 procedure InstructionLoad;
91 procedure InstructionModulo;
92 procedure InstructionMultiply;
93 procedure InstructionNoOperation;
94 procedure InstructionOutput;
95 procedure InstructionPop;
96 procedure InstructionPush;
97 procedure InstructionReturn;
98 procedure InstructionSkip;
99 procedure InstructionSubtract;
100 procedure InstructionSystemCall;
101 procedure SetValue(Param: TParam; Value: TValue);
102 public
103 Instructions: TInstructions;
104 Registers: TFPGList<TValue>;
105 Memory: TFPGList<TValue>;
106 IP: TAddress;
107 SP: TAddress;
108 Terminated: Boolean;
109 procedure Init(MemorySize, RegisterCount: Integer);
110 procedure Run;
111 procedure Start;
112 procedure Stop;
113 constructor Create;
114 destructor Destroy; override;
115 property Running: Boolean read FRunning;
116 property OnPortOut: TPortOutEvent read FOnPortOut write FOnPortOut;
117 property OnPortIn: TPortInEvent read FOnPortIn write FOnPortIn;
118 property OnSystemCall: TSystemCallEvent read FOnSystemCall write FOnSystemCall;
119 end;
120
121 { TMachineThread }
122
123 TMachineThread = class(TThread)
124 Machine: TMachine;
125 procedure Execute; override;
126 end;
127
128
129implementation
130
131{ TMachineThread }
132
133procedure TMachineThread.Execute;
134begin
135 Machine.Run;
136end;
137
138
139{ TParam }
140
141function TParam.GetString: string;
142begin
143 if ParamType = ptConst then Result := IntToHex(Value, 8)
144 else if ParamType = ptRegister then Result := 'R' + IntToStr(Value)
145 else if ParamType = ptSpecialRegister then begin
146 if Value < Length(SpecialRegisterString) then
147 Result := SpecialRegisterString[TSpecialRegister(Value)]
148 else Result := 'S' + IntToStr(Value);
149 end;
150 if MemoryType = mtData then Result := '[' + Result + ']';
151end;
152
153procedure TParam.SetParam(ParamType: TParamType; MemoryType: TMemoryType;
154 Value: TValue);
155begin
156 Self.ParamType := ParamType;
157 Self.MemoryType := MemoryType;
158 Self.Value := Value;
159end;
160
161{ TInstruction }
162
163constructor TInstruction.Create;
164begin
165 Params[0] := TParam.Create;
166 Params[1] := TParam.Create;
167end;
168
169destructor TInstruction.Destroy;
170begin
171 FreeAndNil(Params[0]);
172 FreeAndNil(Params[1]);
173 inherited;
174end;
175
176function TInstruction.GetString: string;
177begin
178 Result := OpcodeString[Opcode];
179 if Params[0].ParamType <> ptNone then begin
180 Result := Result + ' ' + Params[0].GetString;
181 if Params[1].ParamType <> ptNone then begin
182 Result := Result + ', ' + Params[1].GetString;
183 end;
184 end;
185end;
186
187{ TInstructions }
188
189procedure TInstructions.AddInst(Opcode: TOpcode);
190var
191 NewInst: TInstruction;
192begin
193 NewInst := TInstruction.Create;
194 NewInst.Opcode := Opcode;
195 NewInst.Params[0].SetParam(ptNone, mtNone, 0);
196 NewInst.Params[1].SetParam(ptNone, mtNone, 0);
197 Add(NewInst);
198end;
199
200procedure TInstructions.AddInst(Opcode: TOpcode; ParamType1: TParamType;
201 Indirect1: TMemoryType; Value1: TValue);
202var
203 NewInst: TInstruction;
204begin
205 NewInst := TInstruction.Create;
206 NewInst.Opcode := Opcode;
207 NewInst.Params[0].SetParam(ParamType1, Indirect1, Value1);
208 NewInst.Params[1].SetParam(ptNone, mtNone, 0);
209 Add(NewInst);
210end;
211
212procedure TInstructions.AddInst(Opcode: TOpcode;
213 ParamType1: TParamType; Indirect1: TMemoryType; Value1: TValue;
214 ParamType2: TParamType; Indirect2: TMemoryType; Value2: TValue);
215var
216 NewInst: TInstruction;
217begin
218 NewInst := TInstruction.Create;
219 NewInst.Opcode := Opcode;
220 NewInst.Params[0].SetParam(ParamType1, Indirect1, Value1);
221 NewInst.Params[1].SetParam(ParamType2, Indirect2, Value2);
222 Add(NewInst);
223end;
224
225
226{ TMachine }
227
228procedure TMachine.Init(MemorySize, RegisterCount: Integer);
229begin
230 Instructions.Count := 0;
231 Registers.Count := RegisterCount;
232 Memory.Count := MemorySize;
233 IP := 0;
234 SP := Memory.Count;
235end;
236
237procedure TMachine.InstructionNoOperation;
238begin
239 // Do nothing
240end;
241
242procedure TMachine.InstructionHalt;
243begin
244 Terminated := True;
245end;
246
247procedure TMachine.InstructionIncrement;
248begin
249 with TInstruction(Instructions[IP]) do
250 SetValue(Params[0], GetValue(Params[0]) + 1);
251end;
252
253procedure TMachine.InstructionDecrement;
254begin
255 with TInstruction(Instructions[IP]) do
256 SetValue(Params[0], GetValue(Params[0]) - 1);
257end;
258
259procedure TMachine.InstructionAdd;
260begin
261 with TInstruction(Instructions[IP]) do
262 SetValue(Params[0], GetValue(Params[0]) + GetValue(Params[1]));
263end;
264
265procedure TMachine.InstructionSubtract;
266begin
267 with TInstruction(Instructions[IP]) do
268 SetValue(Params[0], GetValue(Params[0]) + GetValue(Params[1]));
269end;
270
271procedure TMachine.InstructionSystemCall;
272begin
273 if Assigned(FOnSystemCall) then
274 with TInstruction(Instructions[IP]) do
275 FOnSystemCall(GetValue(Params[0]));
276end;
277
278procedure TMachine.InstructionMultiply;
279begin
280 with TInstruction(Instructions[IP]) do
281 SetValue(Params[0], GetValue(Params[0]) * GetValue(Params[1]));
282end;
283
284procedure TMachine.InstructionDivide;
285begin
286 with TInstruction(Instructions[IP]) do
287 SetValue(Params[0], GetValue(Params[0]) div GetValue(Params[1]));
288end;
289
290procedure TMachine.InstructionModulo;
291begin
292 with TInstruction(Instructions[IP]) do
293 SetValue(Params[0], GetValue(Params[0]) mod GetValue(Params[1]));
294end;
295
296procedure TMachine.InstructionLoad;
297begin
298 with TInstruction(Instructions[IP]) do
299 SetValue(Params[0], GetValue(Params[1]));
300end;
301
302procedure TMachine.InstructionPush;
303begin
304 with TInstruction(Instructions[IP]) do begin
305 SP := SP - 1;
306 Memory[SP] := GetValue(Params[0]);
307 end;
308end;
309
310procedure TMachine.InstructionPop;
311begin
312 with TInstruction(Instructions[IP]) do begin
313 SetValue(Params[0], Memory[SP]);
314 SP := SP + 1;
315 end;
316end;
317
318procedure TMachine.InstructionJumpAbsolute;
319begin
320 with TInstruction(Instructions[IP]) do
321 IP := GetValue(Params[0]);
322end;
323
324procedure TMachine.InstructionJumpRelative;
325begin
326 with TInstruction(Instructions[IP]) do
327 IP := IP + GetValue(Params[0]);
328end;
329
330procedure TMachine.InstructionInput;
331begin
332 with TInstruction(Instructions[IP]) do
333 if Assigned(FOnPortIn) then SetValue(Params[0], FOnPortIn(Self, GetValue(Params[1])))
334 else SetValue(Params[0], 0);
335end;
336
337procedure TMachine.InstructionOutput;
338begin
339 with TInstruction(Instructions[IP]) do
340 if Assigned(FOnPortOut) then FOnPortOut(Self, GetValue(Params[0]), GetValue(Params[1]));
341end;
342
343procedure TMachine.InstructionCall;
344begin
345 with TInstruction(Instructions[IP]) do begin
346 Dec(SP);
347 Memory[SP] := IP + 1;
348 IP := GetValue(Params[0]);
349 end;
350end;
351
352procedure TMachine.InstructionReturn;
353begin
354 with TInstruction(Instructions[IP]) do begin
355 IP := Memory[SP];
356 Inc(SP);
357 end;
358end;
359
360procedure TMachine.InstructionExchange;
361var
362 Temp: TValue;
363begin
364 with TInstruction(Instructions[IP]) do begin
365 Temp := GetValue(Params[0]);
366 SetValue(Params[0], GetValue(Params[1]));
367 SetValue(Params[1], Temp);
368 end;
369end;
370
371procedure TMachine.InstructionSkip;
372var
373 Condition: TCondition;
374begin
375 with TInstruction(Instructions[IP]) do begin
376 Condition := TCondition(Integer(GetValue(Params[0])));
377 case Condition of
378 cdZero: if GetValue(Params[1]) <> 0 then Inc(IP);
379 cdNotZero: if GetValue(Params[1]) = 0 then Inc(IP);
380 cdPositive: if GetValue(Params[1]) <= 0 then Inc(IP);
381 cdNegative: if GetValue(Params[1]) >= 0 then Inc(IP);
382 else raise Exception.Create('Unsupported condition type');
383 end;
384 end;
385end;
386
387procedure TMachine.Run;
388begin
389 while not Terminated do begin
390 if IP < Instructions.Count then begin
391 with TInstruction(Instructions[IP]) do
392 if Assigned(InstructionHandlers[Opcode]) then
393 InstructionHandlers[Opcode]
394 else raise Exception.Create('Unsupported instruction opcode ' + OpcodeString[Opcode]);
395 end else raise Exception.Create('Reached end of program memory');
396 Inc(IP);
397 end;
398end;
399
400procedure TMachine.Start;
401begin
402 if not Running then begin
403 Terminated := False;
404 Thread := TMachineThread.Create(True);
405 Thread.Machine := Self;
406 Thread.Start;
407 FRunning := True;
408 end;
409end;
410
411procedure TMachine.Stop;
412begin
413 if Running then begin
414 Terminated := True;
415 Thread.Terminate;
416 Thread.WaitFor;
417 FreeAndNil(Thread);
418 FRunning := False;
419 end;
420end;
421
422constructor TMachine.Create;
423begin
424 Registers := TFPGList<TValue>.Create;
425 Memory := TFPGList<TValue>.Create;
426 Instructions := TInstructions.Create;
427
428 InstructionHandlers[opNoOperation] := InstructionNoOperation;
429 InstructionHandlers[opHalt] := InstructionHalt;
430 InstructionHandlers[opIncrement] := InstructionIncrement;
431 InstructionHandlers[opDecrement] := InstructionDecrement;
432 InstructionHandlers[opAdd] := InstructionAdd;
433 InstructionHandlers[opSubtract] := InstructionSubtract;
434 InstructionHandlers[opMultiply] := InstructionMultiply;
435 InstructionHandlers[opDivide] := InstructionDivide;
436 InstructionHandlers[opModulo] := InstructionModulo;
437 InstructionHandlers[opLoad] := InstructionLoad;
438 InstructionHandlers[opPush] := InstructionPush;
439 InstructionHandlers[opPop] := InstructionPop;
440 InstructionHandlers[opJumpAbsolute] := InstructionJumpAbsolute;
441 InstructionHandlers[opJumpRelative] := InstructionJumpRelative;
442 InstructionHandlers[opOutput] := InstructionOutput;
443 InstructionHandlers[opInput] := InstructionInput;
444 InstructionHandlers[opCall] := InstructionCall;
445 InstructionHandlers[opReturn] := InstructionReturn;
446 InstructionHandlers[opExchange] := InstructionExchange;
447 InstructionHandlers[opSkip] := InstructionSkip;
448 InstructionHandlers[opSystemCall] := InstructionSystemCall;
449end;
450
451destructor TMachine.Destroy;
452begin
453 FreeAndNil(Instructions);
454 FreeAndNil(Memory);
455 FreeAndNil(Registers);
456 inherited Destroy;
457end;
458
459function TMachine.GetValue(Param: TParam): TValue;
460begin
461 if Param.ParamType = ptRegister then Result := Registers[Param.Value]
462 else if Param.ParamType = ptConst then Result := Param.Value
463 else if Param.ParamType = ptSpecialRegister then begin
464 if Param.Value = Integer(srIP) then Result := IP
465 else if Param.Value = Integer(srSP) then Result := SP
466 else raise Exception.Create('Unsupported special register ' + IntToStr(Integer(Param.Value)));
467 end else if Param.ParamType = ptNone then raise Exception.Create('Read from not set parameter')
468 else raise Exception.Create('Unsupported parameter type ' + IntToStr(Integer(Param.ParamType)));
469 if Param.MemoryType = mtData then Result := Memory[Result];
470end;
471
472procedure TMachine.SetValue(Param: TParam; Value: TValue);
473begin
474 if Param.ParamType = ptRegister then begin
475 if Param.MemoryType = mtData then Memory[Registers[Param.Value]] := Value
476 else Registers[Param.Value] := Value;
477 end else
478 if Param.ParamType = ptConst then begin
479 if Param.MemoryType = mtData then Memory[Param.Value] := Value
480 else raise Exception.Create('Cannot assign to constant parameter');
481 end else
482 if Param.ParamType = ptSpecialRegister then begin
483 if Param.Value = Integer(srIP) then begin
484 if Param.MemoryType = mtData then Memory[IP] := Value
485 else IP := Value;
486 end else if Param.Value = Integer(srSP) then begin
487 if Param.MemoryType = mtData then Memory[SP] := Value
488 else SP := Value;
489 end else raise Exception.Create('Unsupported special register ' + IntToStr(Integer(Param.Value)));
490 end else
491 if Param.ParamType = ptNone then begin
492 raise Exception.Create('Write to not set parameter')
493 end else
494 raise Exception.Create('Unsupported parameter type ' + IntToStr(Integer(Param.ParamType)));
495end;
496
497end.
498
Note: See TracBrowser for help on using the repository browser.