source: trunk/CpuZ80.pas

Last change on this file was 2, checked in by chronos, 14 months ago
  • Added: Initial development version.
File size: 8.3 KB
Line 
1unit CpuZ80;
2
3interface
4
5uses
6 Classes, SysUtils, Memory;
7
8type
9 TReadEvent = function (Address: Word): Byte of object;
10 TWriteEvent = procedure (Address: Word; Data: Byte) of object;
11
12 TInstruction = (inNop = 0, inLdBcNn = $1, inLdBcIndirectA = $2,
13 inIncBc = $3, inIncB = $4, inDecB = $5, inLdBN = $6, inRlca = $7,
14 inLdDeNn = $11, inJrNzD = $20, inLdHlNn = $21,
15 inIncHl = $23, inLdHlIndirectNn = $2a, inDecHl = $2b, inJrNcD = $30,
16 inLdSpNn = $31, inLdNnIndirectA = $32, inLdHlIndirectN = $36, inLdAN = $3e,
17 inLdCHlIndirect = $4e,
18 inLdHlIndirectD = $72, inLdHlIndirectE = $73, inHalt = $76, inLdAC = $79,
19 inLdAHlIndirect = $7e,
20 inXorA = $af, inCpD = $ba, inCpE = $bb,
21 inJpNn = $c3, inPushBc = $c5, inRst00 = $c7, inRet = $c9, inJpZNn = $ca, inPrefixCb = $cb,
22 inCallNn = $cd, inRst08 = $cf,
23 inOutNA = $d3, inPushDe = $d5, inRst10 = $d7, inPrefixDd = $dd,
24 inRst18 = $df, inRst20 = $e0, inPushHl = $e5, inExDeHl = $eb,
25 inPrefixEd = $ed, inRst28 = $ef, inDi = $f3,
26 inRst30 = $f7, inEi = $fb, inPrefixFd = $fd, inCpN = $fe,
27 inRst38 = $ff, inSbcHlDe = $ed52, inIm1 = $ed56);
28
29 TCpuZ80 = class;
30
31 { TCpuThread }
32
33 TCpuThread = class(TThread)
34 Cpu: TCpuZ80;
35 procedure Execute; override;
36 end;
37
38 TRegBC = record
39 case Byte of
40 0: (B, C: Byte);
41 1: (Value: Word);
42 end;
43
44 TRegDE = record
45 case Byte of
46 0: (D, E: Byte);
47 1: (Value: Word);
48 end;
49
50 TRegHL = record
51 case Byte of
52 0: (H, L: Byte);
53 1: (Value: Word);
54 end;
55
56 TDebugMode = (dmNone, dmStepIn, dmStepOut, dmStepOver, dmStopAddress);
57
58 { TCpuZ80 }
59
60 TCpuZ80 = class
61 private
62 FOnInput: TReadEvent;
63 FOnOutput: TWriteEvent;
64 FOnRead: TReadEvent;
65 FOnWrite: TWriteEvent;
66 FRunning: Boolean;
67 FThread: TCpuThread;
68 Instruction: TInstruction;
69 procedure SetRunning(AValue: Boolean);
70 function DoRead(Address: Word): Byte;
71 procedure DoWrite(Address: Word; Data: Byte);
72 function DoInput(Address: Word): Byte;
73 procedure DoOutput(Address: Word; Data: Byte);
74 function ReadByte: Byte;
75 function ReadWord: Word;
76 procedure PushWord(Data: Word); inline;
77 function PopWord: Word; inline;
78 procedure Call(Address: Word); inline;
79 procedure Cp(Data: Byte); inline;
80 procedure Jr(Condition: Boolean); inline;
81 procedure Jp(Condition: Boolean); inline;
82 public
83 A: Byte;
84 PC: Word;
85 SP: Word;
86 BC: TRegBC;
87 DE: TRegDE;
88 HL: TRegHL;
89 Carry: Boolean;
90 Zero: Boolean;
91 Memory: TMemory;
92 Ticks: Cardinal;
93 InterruptEnabled: Boolean;
94 InterruptMode: Byte;
95 DebugMode: TDebugMode;
96 DebugStopAddress: Word;
97 procedure Step;
98 procedure Reset;
99 constructor Create;
100 destructor Destroy; override;
101 property Running: Boolean read FRunning write SetRunning;
102 property OnRead: TReadEvent read FOnRead write FOnRead;
103 property OnWrite: TWriteEvent read FOnWrite write FOnWrite;
104 property OnInput: TReadEvent read FOnInput write FOnInput;
105 property OnOutput: TWriteEvent read FOnOutput write FOnOutput;
106 end;
107
108implementation
109
110{ TCpuThread }
111
112procedure TCpuThread.Execute;
113begin
114 while not Terminated do begin
115 Cpu.Step;
116 if Cpu.DebugMode <> dmNone then begin
117 if Cpu.DebugMode = dmStepIn then Terminate;
118 if (Cpu.DebugMode = dmStopAddress) and (Cpu.DebugStopAddress = Cpu.PC) then
119 Terminate;
120 Cpu.DebugMode := dmNone;
121 end;
122 end;
123 Cpu.FRunning := False;
124end;
125
126{ TCpuZ80 }
127
128procedure TCpuZ80.SetRunning(AValue: Boolean);
129begin
130 if FRunning = AValue then Exit;
131 if FRunning then begin
132 FThread.Terminate;
133 FThread.WaitFor;
134 FreeAndNil(FThread);
135 end;
136 FRunning := AValue;
137 if FRunning then begin
138 if Assigned(FThread) then FThread.Free;
139 FThread := TCpuThread.Create(True);
140 FThread.FreeOnTerminate := False;
141 FThread.Cpu := Self;
142 FThread.Start;
143 end;
144end;
145
146function TCpuZ80.DoRead(Address: Word): Byte;
147begin
148 if Assigned(FOnRead) then Result := FOnRead(Address);
149end;
150
151procedure TCpuZ80.DoWrite(Address: Word; Data: Byte);
152begin
153 if Assigned(FOnRead) then FOnWrite(Address, Data);
154end;
155
156function TCpuZ80.DoInput(Address: Word): Byte;
157begin
158 if Assigned(FOnInput) then Result := FOnInput(Address);
159end;
160
161procedure TCpuZ80.DoOutput(Address: Word; Data: Byte);
162begin
163 if Assigned(FOnOutput) then FOnOutput(Address, Data);
164end;
165
166function TCpuZ80.ReadByte: Byte;
167begin
168 Result := DoRead(PC);
169 PC := (PC + SizeOf(Byte)) mod $10000;
170end;
171
172function TCpuZ80.ReadWord: Word;
173begin
174 Result := DoRead(PC) or (DoRead(PC + 1) shl 8);
175 PC := (PC + SizeOf(Word)) mod $10000;
176end;
177
178procedure TCpuZ80.PushWord(Data: Word);
179begin
180 SP := (SP + $10000 - SizeOf(Word)) mod $10000;
181 DoWrite(SP, Data and $ff);
182 DoWrite(SP + 1, Data shr 8);
183end;
184
185function TCpuZ80.PopWord: Word;
186begin
187 Result := DoRead(SP) or (DoRead(SP + 1) shl 8);
188 SP := (SP + SizeOf(Word)) mod $10000;
189end;
190
191procedure TCpuZ80.Call(Address: Word);
192begin
193 PushWord(PC);
194 PC := Address;
195end;
196
197procedure TCpuZ80.Cp(Data: Byte);
198var
199 TempByte: Byte;
200begin
201 Carry := Data > A;
202 TempByte := Byte(ShortInt(A) - ShortInt(Data));
203 Zero := TempByte = 0;
204end;
205
206procedure TCpuZ80.Jr(Condition: Boolean);
207var
208 Temp: Byte;
209begin
210 Temp := ReadByte;
211 if Condition then
212 PC := PC + ShortInt(Temp);
213end;
214
215procedure TCpuZ80.Jp(Condition: Boolean);
216var
217 Temp: Word;
218begin
219 Temp := ReadWord;
220 if Condition then PC := Temp;
221end;
222
223procedure TCpuZ80.Step;
224var
225 Opcode: Byte;
226 TempWord: Word;
227begin
228 Opcode := ReadByte;
229 if Opcode = $cb then begin
230 Opcode := ReadByte;
231 Instruction := TInstruction($cb00 or Opcode)
232 end
233 else if Opcode = $dd then begin
234 Opcode := ReadByte;
235 Instruction := TInstruction($dd00 or Opcode)
236 end
237 else if Opcode = $ed then begin
238 Opcode := ReadByte;
239 Instruction := TInstruction($ed00 or Opcode)
240 end
241 else if Opcode = $fd then begin
242 Opcode := ReadByte;
243 Instruction := TInstruction($fd00 or Opcode)
244 end
245 else Instruction := TInstruction(Opcode);
246 case Instruction of
247 inNop: ;
248 inHalt: ;
249 inLdHlNn: HL.Value := ReadWord;
250 inLdBcNn: BC.Value := ReadWord;
251 inLdDeNn: DE.Value := ReadWord;
252 inLdSpNn: SP := ReadWord;
253 inLdAN: A := ReadByte;
254 inLdBN: BC.B := ReadByte;
255 inLdAC: A := BC.C;
256 inLdHlIndirectN: DoWrite(HL.Value, ReadByte);
257 inLdNnIndirectA: DoWrite(ReadWord, A);
258 inLdHlIndirectD: DoWrite(HL.Value, DE.D);
259 inLdHlIndirectE: DoWrite(HL.Value, DE.E);
260 inLdCHlIndirect: BC.C := DoRead(HL.Value);
261 inLdAHlIndirect: A := DoRead(HL.Value);
262 inLdBcIndirectA: DoWrite(BC.Value, A);
263 inLdHlIndirectNn: begin
264 TempWord := ReadWord;
265 DoWrite(HL.Value, TempWord and $ff);
266 DoWrite(HL.Value + 1, TempWord shr 8);
267 end;
268 inJpNn: PC := ReadWord;
269 inJpZNn: Jp(not Zero);
270 inCpN: Cp(ReadByte);
271 inCpD: Cp(DE.D);
272 inCpE: Cp(DE.E);
273 inCallNn: begin
274 TempWord := ReadWord;
275 if DebugMode = dmStepOver then begin
276 DebugStopAddress := PC;
277 DebugMode := dmStopAddress;
278 end;
279 Call(TempWord);
280 end;
281 inRet: begin
282 PC := PopWord;
283 if DebugMode = dmStepOut then begin
284 FThread.Terminate;
285 DebugMode := dmNone;
286 end;
287 end;
288 inRst00: Call($00);
289 inRst08: Call($08);
290 inRst10: Call($10);
291 inRst18: Call($18);
292 inRst20: Call($20);
293 inRst28: Call($28);
294 inRst30: Call($30);
295 inRst38: Call($08);
296 inDi: InterruptEnabled := False;
297 inEi: InterruptEnabled := True;
298 inIm1: InterruptMode := 1;
299 inOutNA: DoOutput(ReadByte, A);
300 inXorA: A := A xor A;
301 inPushBc: PushWord(BC.Value);
302 inPushDe: PushWord(DE.Value);
303 inPushHl: PushWord(HL.Value);
304 inSbcHlDe: HL.Value := HL.Value - DE.Value;
305 inExDeHl: begin
306 TempWord := DE.Value;
307 DE.Value := HL.Value;
308 HL.Value := TempWord;
309 end;
310 inDecHl: Dec(HL.Value);
311 inIncHl: Inc(HL.Value);
312 inIncBc: Inc(BC.Value);
313 inIncB: Inc(BC.B);
314 inDecB: Dec(BC.B);
315 inJrNzD: Jr(not Zero);
316 inJrNcD: Jr(not Carry);
317 else raise Exception.Create('Unsupported instruction ' + IntToHex(Word(Instruction), 4));
318 end;
319 Ticks := Cardinal(Ticks + 1);
320end;
321
322procedure TCpuZ80.Reset;
323begin
324 A := 0;
325 PC := 0;
326 BC.Value := 0;
327 DE.Value := 0;
328 HL.Value := 0;
329 SP := 0;
330 Carry := False;
331 Zero := False;
332 InterruptEnabled := True;
333 InterruptMode := 0;
334end;
335
336constructor TCpuZ80.Create;
337begin
338 Reset;
339end;
340
341destructor TCpuZ80.Destroy;
342begin
343 Running := False;
344 inherited;
345end;
346
347end.
348
Note: See TracBrowser for help on using the repository browser.