source: branches/CpuSingleSize/Cpu.pas

Last change on this file was 238, checked in by chronos, 10 months ago
  • Modified: Removed U prefix from unit names.
  • Fixed: Memory leaks.
File size: 14.3 KB
Line 
1unit Cpu;
2
3interface
4
5uses
6 Classes, SysUtils;
7
8type
9 TInstruction = (inNop, inHalt, inSet, inCopy, inInc, inDec, inLoad, inStore,
10 inAdd, inSub, inIn, inOut, inJump, inJumpZero, inJumpNotZero, inPush, inPop,
11 inCall, inRet, inAnd, inOr, inXor, inShl, inShr, inMul, inDiv, inMod,
12 inJumpRel, inLoadIndex, inStoreIndex, inLoadCpu, inStoreCpu, inEi, inDi);
13
14 TInteger = Integer;
15
16 TCpuThread = class;
17 TOutputEvent = procedure (Device, Port: TInteger; Value: TInteger) of object;
18 TInputEvent = function (Device, Port: TInteger): TInteger of object;
19
20 { TCpuStatus }
21
22 TCpuStatus = packed record
23 InterruptEnabled: Boolean;
24 UserMode: Boolean;
25 UserModeWasActive: Boolean;
26 function GetInteger: TInteger;
27 procedure SetInteger(Value: TInteger);
28 end;
29
30 TIntegerArray = array of TInteger;
31
32 TMemoryBlock = record
33 Base: TIntegerArray;
34 Size: TInteger;
35 end;
36
37 TMemoryBlockIndex = (mbCode, mbData, mbStack, mbInterrupt);
38 TInstructionHandler = procedure of object;
39 TSystemInterrupt = (siReset, siUserIn, siUserOut, siUserInvalidMemory,
40 siPrivilegedInstruction);
41 TCpuRegister = (crIP, crSP, crStatusRegister, crUserMemoryBase, crUserMemorySize);
42
43 { TCpu }
44
45 TCpu = class
46 private
47 FBaseMemory: TMemoryBlock;
48 FUserMemory: TMemoryBlock;
49 FMemory: TMemoryBlock;
50 FRunning: Boolean;
51 FThread: TCpuThread;
52 FOnInput: TInputEvent;
53 FOnOutput: TOutputEvent;
54 FNextInterupt: TInteger;
55 FHalted: Boolean;
56 FTicks: Integer;
57 FInstructionHandlers: array[TInstruction] of TInstructionHandler;
58 function GetMemory: TIntegerArray;
59 procedure InstructionNop;
60 procedure InstructionHalt;
61 procedure InstructionSet;
62 procedure InstructionCopy;
63 procedure InstructionLoad;
64 procedure InstructionStore;
65 procedure InstructionInc;
66 procedure InstructionDec;
67 procedure InstructionAdd;
68 procedure InstructionSub;
69 procedure InstructionIn;
70 procedure InstructionOut;
71 procedure InstructionJump;
72 procedure InstructionJumpRel;
73 procedure InstructionJumpZero;
74 procedure InstructionJumpNotZero;
75 procedure InstructionPush;
76 procedure InstructionPop;
77 procedure InstructionCall;
78 procedure InstructionRet;
79 procedure InstructionReti;
80 procedure InstructionAnd;
81 procedure InstructionOr;
82 procedure InstructionXor;
83 procedure InstructionShl;
84 procedure InstructionShr;
85 procedure InstructionMod;
86 procedure InstructionDiv;
87 procedure InstructionMul;
88 procedure InstructionLoadIndex;
89 procedure InstructionStoreIndex;
90 procedure InstructionLoadCpu;
91 procedure InstructionStoreCpu;
92 procedure InstructionEi;
93 procedure InstructionDi;
94 procedure SetMemory(AValue: TIntegerArray);
95 procedure SetRunning(AValue: Boolean);
96 procedure CheckInterreupts;
97 procedure Push(Value: TInteger);
98 function Pop: TInteger;
99 procedure StoreContext;
100 procedure LoadContext;
101 procedure InitInstructions;
102 procedure ActivateUserMode;
103 procedure DeactivateUserMode;
104 public
105 InstructionPointer: TInteger; // Instruction Pointer
106 StackPointer: TInteger; // Stack Pointer
107 StatusRegister: TCpuStatus; // Status Register
108 R: array of TInteger;
109 function ReadNext: TInteger;
110 procedure WriteNext(Value: TInteger);
111 procedure Step;
112 procedure Start;
113 procedure Stop;
114 procedure Reset;
115 procedure Interrupt(Index: TSystemInterrupt); overload;
116 procedure Interrupt(Index: TInteger); overload;
117 constructor Create;
118 destructor Destroy; override;
119 property Ticks: Integer read FTicks;
120 property Memory: TIntegerArray read GetMemory write SetMemory;
121 property OnInput: TInputEvent read FOnInput write FOnInput;
122 property OnOutput: TOutputEvent read FOnOutput write FOnOutput;
123 property Running: Boolean read FRunning write SetRunning;
124 property Halted: Boolean read FHalted;
125 end;
126
127 { TCpuThread }
128
129 TCpuThread = class(TThread)
130 Cpu: TCpu;
131 procedure Execute; override;
132 end;
133
134
135implementation
136
137{ TCpuStatus }
138
139function TCpuStatus.GetInteger: TInteger;
140begin
141
142end;
143
144procedure TCpuStatus.SetInteger(Value: TInteger);
145begin
146
147end;
148
149{ TCpuThread }
150
151procedure TCpuThread.Execute;
152begin
153 repeat
154 if not Cpu.Halted then Cpu.Step
155 else Sleep(1);
156 Cpu.CheckInterreupts;
157 until Terminated;
158end;
159
160{ TCpu }
161
162procedure TCpu.SetRunning(AValue: Boolean);
163begin
164 if FRunning = AValue then Exit;
165 if AValue then Start
166 else Stop;
167end;
168
169procedure TCpu.InstructionNop;
170begin
171 // No operation
172end;
173
174function TCpu.GetMemory: TIntegerArray;
175begin
176 Result := FBaseMemory.Base;
177end;
178
179procedure TCpu.InstructionHalt;
180begin
181 FHalted := True;
182end;
183
184procedure TCpu.InstructionSet;
185begin
186 R[ReadNext] := ReadNext;
187end;
188
189procedure TCpu.InstructionCopy;
190begin
191 R[ReadNext] := R[ReadNext];
192end;
193
194procedure TCpu.InstructionLoad;
195begin
196 R[ReadNext] := FMemory.Base[R[ReadNext]];
197end;
198
199procedure TCpu.InstructionStore;
200begin
201 FMemory.Base[R[ReadNext]] := R[ReadNext];
202end;
203
204procedure TCpu.InstructionInc;
205begin
206 Inc(R[ReadNext]);
207end;
208
209procedure TCpu.InstructionDec;
210begin
211 Dec(R[ReadNext]);
212end;
213
214procedure TCpu.InstructionAdd;
215var
216 Index: TInteger;
217begin
218 Index := ReadNext;
219 R[Index] := R[Index] + R[ReadNext];
220end;
221
222procedure TCpu.InstructionSub;
223var
224 Index: TInteger;
225begin
226 Index := ReadNext;
227 R[Index] := R[Index] - R[ReadNext];
228end;
229
230procedure TCpu.InstructionIn;
231var
232 Index: TInteger;
233 Device: TInteger;
234 Port: TInteger;
235begin
236 Index := ReadNext;
237 Device := R[ReadNext];
238 Port := R[ReadNext];
239 if StatusRegister.UserMode then begin
240 Interrupt(siUserIn);
241 end else begin
242 if Assigned(FOnInput) then R[Index] := FOnInput(Device, Port);
243 end;
244end;
245
246procedure TCpu.InstructionOut;
247var
248 Device: TInteger;
249 Port: TInteger;
250 Index: TInteger;
251begin
252 Device := R[ReadNext];
253 Port := R[ReadNext];
254 Index := ReadNext;
255 if StatusRegister.UserMode then begin
256 Interrupt(siUserOut);
257 end else begin
258 if Assigned(FOnOutput) then FOnOutput(Device, Port, R[Index]);
259 end;
260end;
261
262procedure TCpu.InstructionJump;
263begin
264 InstructionPointer := ReadNext;
265end;
266
267procedure TCpu.InstructionJumpRel;
268begin
269 InstructionPointer := InstructionPointer + ReadNext;
270end;
271
272procedure TCpu.InstructionJumpZero;
273var
274 Index: TInteger;
275 Address: TInteger;
276begin
277 Index := ReadNext;
278 Address := ReadNext;
279 if R[Index] = 0 then InstructionPointer := Address;
280end;
281
282procedure TCpu.InstructionJumpNotZero;
283var
284 Index: TInteger;
285 Address: TInteger;
286begin
287 Index := ReadNext;
288 Address := ReadNext;
289 if R[Index] <> 0 then InstructionPointer := Address;
290end;
291
292procedure TCpu.InstructionPush;
293begin
294 Push(R[ReadNext]);
295end;
296
297procedure TCpu.InstructionPop;
298begin
299 R[ReadNext] := Pop;
300end;
301
302procedure TCpu.InstructionCall;
303var
304 Address: TInteger;
305begin
306 Address := ReadNext;
307 Push(InstructionPointer);
308 InstructionPointer := Address;
309end;
310
311procedure TCpu.InstructionRet;
312begin
313 InstructionPointer := Pop;
314end;
315
316procedure TCpu.InstructionReti;
317begin
318 if StatusRegister.UserMode then begin
319 Interrupt(siPrivilegedInstruction);
320 Exit;
321 end;
322 if StatusRegister.UserModeWasActive then ActivateUserMode;
323 StatusRegister.InterruptEnabled := True;
324 InstructionPointer := Pop;
325end;
326
327procedure TCpu.InstructionAnd;
328var
329 Index: TInteger;
330begin
331 Index := ReadNext;
332 R[Index] := R[Index] and R[ReadNext];
333end;
334
335procedure TCpu.InstructionOr;
336var
337 Index: TInteger;
338begin
339 Index := ReadNext;
340 R[Index] := R[Index] or R[ReadNext];
341end;
342
343procedure TCpu.InstructionXor;
344var
345 Index: TInteger;
346begin
347 Index := ReadNext;
348 R[Index] := R[Index] xor R[ReadNext];
349end;
350
351procedure TCpu.InstructionShl;
352var
353 Index: TInteger;
354begin
355 Index := ReadNext;
356 R[Index] := R[Index] shl R[ReadNext];
357end;
358
359procedure TCpu.InstructionShr;
360var
361 Index: TInteger;
362begin
363 Index := ReadNext;
364 R[Index] := R[Index] shr R[ReadNext];
365end;
366
367procedure TCpu.InstructionMod;
368var
369 Index: TInteger;
370begin
371 Index := ReadNext;
372 R[Index] := R[Index] mod R[ReadNext];
373end;
374
375procedure TCpu.InstructionDiv;
376var
377 Index: TInteger;
378begin
379 Index := ReadNext;
380 R[Index] := R[Index] div R[ReadNext];
381end;
382
383procedure TCpu.InstructionMul;
384var
385 Index: TInteger;
386begin
387 Index := ReadNext;
388 R[Index] := R[Index] * R[ReadNext];
389end;
390
391procedure TCpu.InstructionLoadIndex;
392begin
393 R[ReadNext] := FMemory.Base[R[ReadNext] + ReadNext];
394end;
395
396procedure TCpu.InstructionStoreIndex;
397begin
398 FMemory.Base[R[ReadNext] + ReadNext] := R[ReadNext];
399end;
400
401procedure TCpu.InstructionLoadCpu;
402var
403 Index: TInteger;
404 I: TInteger;
405begin
406 R[ReadNext] := FMemory.Base[R[ReadNext]];
407 Index := ReadNext;
408 I := ReadNext;
409 case TCpuRegister(I) of
410 crIP: R[Index] := InstructionPointer;
411 crSP: R[Index] := StackPointer;
412 crStatusRegister: R[Index] := StatusRegister.GetInteger;
413 crUserMemoryBase: R[Index] := Pointer(FUserMemory.Base) - Pointer(FBaseMemory.Base);
414 crUserMemorySize: R[Index] := FUserMemory.Size;
415 end;
416end;
417
418procedure TCpu.InstructionStoreCpu;
419var
420 Index: TInteger;
421 I: TInteger;
422begin
423 if StatusRegister.UserMode then begin
424 Interrupt(siPrivilegedInstruction);
425 Exit;
426 end;
427 R[ReadNext] := FMemory.Base[R[ReadNext]];
428 Index := ReadNext;
429 I := ReadNext;
430 case TCpuRegister(I) of
431 crIP: InstructionPointer := R[Index];
432 crSP: StackPointer := R[Index];
433 crStatusRegister: StatusRegister.SetInteger(R[Index]);
434 crUserMemoryBase: FUserMemory.Base := Pointer(FBaseMemory.Base) + R[Index];
435 crUserMemorySize: FUserMemory.Size := R[Index];
436 end;
437end;
438
439procedure TCpu.InstructionEi;
440begin
441 if StatusRegister.UserMode then begin
442 Interrupt(siPrivilegedInstruction);
443 Exit;
444 end;
445 StatusRegister.InterruptEnabled := True;
446end;
447
448procedure TCpu.InstructionDi;
449begin
450 if StatusRegister.UserMode then begin
451 Interrupt(siPrivilegedInstruction);
452 Exit;
453 end;
454 StatusRegister.InterruptEnabled := False;
455end;
456
457procedure TCpu.SetMemory(AValue: TIntegerArray);
458begin
459 if (FBaseMemory.Base = AValue) and (FBaseMemory.Size = Length(AValue)) then Exit;
460 FBaseMemory.Base := AValue;
461 FBaseMemory.Size := Length(AValue);
462end;
463
464procedure TCpu.CheckInterreupts;
465begin
466 if StatusRegister.InterruptEnabled and (FNextInterupt <> -1) then begin
467 Push(InstructionPointer);
468 if StatusRegister.UserMode then DeactivateUserMode;
469 InstructionPointer := FMemory.Base[FNextInterupt];
470 StatusRegister.InterruptEnabled := False;
471 FNextInterupt := -1;
472 FHalted := False;
473 end;
474end;
475
476procedure TCpu.Push(Value: TInteger);
477begin
478 Dec(StackPointer);
479 FMemory.Base[StackPointer] := Value;
480end;
481
482function TCpu.Pop: TInteger;
483begin
484 Result := FMemory.Base[StackPointer];
485 Inc(StackPointer);
486end;
487
488procedure TCpu.StoreContext;
489var
490 I: Integer;
491begin
492 for I := 0 to Length(R) - 1 do
493 Push(R[I]);
494end;
495
496procedure TCpu.LoadContext;
497var
498 I: Integer;
499begin
500 for I := Length(R) - 1 downto 0 do
501 R[I] := Pop;
502end;
503
504procedure TCpu.InitInstructions;
505begin
506 FInstructionHandlers[inNop] := InstructionNop;
507 FInstructionHandlers[inHalt] := InstructionHalt;
508 FInstructionHandlers[inSet] := InstructionSet;
509 FInstructionHandlers[inCopy] := InstructionCopy;
510 FInstructionHandlers[inLoad] := InstructionLoad;
511 FInstructionHandlers[inStore] := InstructionStore;
512 FInstructionHandlers[inInc] := InstructionInc;
513 FInstructionHandlers[inDec] := InstructionDec;
514 FInstructionHandlers[inAdd] := InstructionAdd;
515 FInstructionHandlers[inSub] := InstructionSub;
516 FInstructionHandlers[inIn] := InstructionIn;
517 FInstructionHandlers[inOut] := InstructionOut;
518 FInstructionHandlers[inJump] := InstructionJump;
519 FInstructionHandlers[inJumpRel] := InstructionJumpRel;
520 FInstructionHandlers[inJumpZero] := InstructionJumpZero;
521 FInstructionHandlers[inJumpNotZero] := InstructionJumpNotZero;
522 FInstructionHandlers[inPush] := InstructionPush;
523 FInstructionHandlers[inPop] := InstructionPop;
524 FInstructionHandlers[inCall] := InstructionCall;
525 FInstructionHandlers[inRet] := InstructionRet;
526 FInstructionHandlers[inAnd] := InstructionAnd;
527 FInstructionHandlers[inOr] := InstructionOr;
528 FInstructionHandlers[inXor] := InstructionXor;
529 FInstructionHandlers[inShl] := InstructionShl;
530 FInstructionHandlers[inShr] := InstructionShr;
531 FInstructionHandlers[inMul] := InstructionMul;
532 FInstructionHandlers[inDiv] := InstructionDiv;
533 FInstructionHandlers[inMod] := InstructionMod;
534 FInstructionHandlers[inLoadIndex] := InstructionLoadIndex;
535 FInstructionHandlers[inStoreIndex] := InstructionStoreIndex;
536 FInstructionHandlers[inLoadCpu] := InstructionLoadCpu;
537 FInstructionHandlers[inStoreCpu] := InstructionStoreCpu;
538 FInstructionHandlers[inEi] := InstructionEi;
539 FInstructionHandlers[inDi] := InstructionDi;
540end;
541
542procedure TCpu.ActivateUserMode;
543begin
544 StatusRegister.UserMode := True;
545 FMemory := FUserMemory;
546end;
547
548procedure TCpu.DeactivateUserMode;
549begin
550 FMemory := FBaseMemory;
551 StatusRegister.UserModeWasActive := StatusRegister.UserMode;
552 StatusRegister.UserMode := False;
553end;
554
555function TCpu.ReadNext: TInteger;
556begin
557 if InstructionPointer >= FMemory.Size then InstructionPointer := 0;
558 Result := FMemory.Base[InstructionPointer];
559 Inc(InstructionPointer);
560end;
561
562procedure TCpu.WriteNext(Value: TInteger);
563begin
564 if InstructionPointer >= FMemory.Size then InstructionPointer := 0;
565 FMemory.Base[InstructionPointer] := Value;
566 Inc(InstructionPointer);
567end;
568
569procedure TCpu.Step;
570var
571 Instruction: TInstruction;
572begin
573 Instruction := TInstruction(ReadNext);
574 if Assigned(FInstructionHandlers[Instruction]) then
575 FInstructionHandlers[Instruction]
576 else raise Exception.Create('Missing handler for instruction ' + IntToStr(Integer(Instruction)));
577 Inc(FTicks);
578end;
579
580procedure TCpu.Start;
581begin
582 if not Running then begin
583 Reset;
584 FThread := TCpuThread.Create(True);
585 FThread.Cpu := Self;
586 FThread.Start;
587 FRunning := True;
588 end;
589end;
590
591procedure TCpu.Stop;
592begin
593 if Running then begin
594 FHalted := True;
595 FThread.Terminate;
596 FThread.WaitFor;
597 FreeAndNil(FThread);
598 FRunning := False;
599 end;
600end;
601
602procedure TCpu.Reset;
603var
604 I: Integer;
605begin
606 FMemory := FBaseMemory;
607 FNextInterupt := -1;
608 FHalted := False;
609 FTicks := 0;
610 InstructionPointer := FMemory.Base[Integer(siReset)];
611 StackPointer := FMemory.Size;
612 for I := 0 to Length(R) - 1 do
613 R[I] := 0;
614end;
615
616procedure TCpu.Interrupt(Index: TSystemInterrupt);
617begin
618 Interrupt(TInteger(Index));
619end;
620
621procedure TCpu.Interrupt(Index: TInteger);
622begin
623 FNextInterupt := Index;
624end;
625
626constructor TCpu.Create;
627begin
628 SetLength(R, 16);
629 Memory := nil;
630 InitInstructions;
631end;
632
633destructor TCpu.Destroy;
634begin
635 Stop;
636 inherited;
637end;
638
639end.
640
Note: See TracBrowser for help on using the repository browser.