]> Joshua Wise's Git repositories - fpgaboy.git/blame - GBZ80Core.v
LD{D,I} A,(HL) and LD{D,I} (HL),A
[fpgaboy.git] / GBZ80Core.v
CommitLineData
2f55f809
JW
1`define REG_A 0
2`define REG_B 1
3`define REG_C 2
4`define REG_D 3
5`define REG_E 4
6`define REG_F 5
7`define REG_H 6
8`define REG_L 7
9`define REG_SPH 8
10`define REG_SPL 9
11`define REG_PCH 10
12`define REG_PCL 11
13
14`define FLAG_Z 8'b10000000
15`define FLAG_N 8'b01000000
16`define FLAG_H 8'b00100000
17`define FLAG_C 8'b00010000
18
19`define STATE_FETCH 2'h0
20`define STATE_DECODE 2'h1
21`define STATE_EXECUTE 2'h2
22`define STATE_WRITEBACK 2'h3
23
24`define INSN_LD_reg_imm8 8'b00xxx110
b85870e0
JW
25`define INSN_HALT 8'b01110110
26`define INSN_LD_HL_reg 8'b01110xxx
27`define INSN_LD_reg_HL 8'b01xxx110
28`define INSN_LD_reg_reg 8'b01xxxxxx
634ce02c
JW
29`define INSN_LD_reg_imm16 8'b00xx0001
30`define INSN_LD_SP_HL 8'b11111001
97649fed 31`define INSN_PUSH_reg 8'b11xx0101
00e30b4d
JW
32`define INSN_POP_reg 8'b11xx0001
33`define INSN_LDH_AC 8'b111x0010 // Either LDH A,(C) or LDH (C),A
fa136d63
JW
34`define INSN_LDx_AHL 8'b001xx010 // LDD/LDI A,(HL) / (HL),A
35
b85870e0
JW
36`define INSN_reg_A 3'b111
37`define INSN_reg_B 3'b000
38`define INSN_reg_C 3'b001
39`define INSN_reg_D 3'b010
40`define INSN_reg_E 3'b011
41`define INSN_reg_H 3'b100
42`define INSN_reg_L 3'b101
43`define INSN_reg_dHL 3'b110
634ce02c
JW
44`define INSN_reg16_BC 2'b00
45`define INSN_reg16_DE 2'b01
46`define INSN_reg16_HL 2'b10
47`define INSN_reg16_SP 2'b11
97649fed
JW
48`define INSN_stack_AF 2'b11
49`define INSN_stack_BC 2'b00
50`define INSN_stack_DE 2'b01
51`define INSN_stack_HL 2'b10
2f55f809
JW
52module GBZ80Core(
53 input clk,
54 output reg [15:0] busaddress, /* BUS_* is latched on STATE_FETCH. */
55 inout [7:0] busdata,
56 output reg buswr, output reg busrd);
57
58 reg [1:0] state = 0; /* State within this bus cycle (see STATE_*). */
59 reg [2:0] cycle = 0; /* Cycle for instructions. */
60
61 reg [7:0] registers[11:0];
62
63 reg [15:0] address; /* Address for the next bus operation. */
64
65 reg [7:0] opcode; /* Opcode from the current machine cycle. */
66
67 reg [7:0] rdata, wdata; /* Read data from this bus cycle, or write data for the next. */
68 reg rd = 1, wr = 0, newcycle = 1;
69
b85870e0
JW
70 reg [7:0] tmp; /* Generic temporary reg. */
71
2f55f809
JW
72 reg [7:0] buswdata;
73 assign busdata = buswr ? buswdata : 8'bzzzzzzzz;
74
75 initial begin
241c995c
JW
76 registers[ 0] <= 0;
77 registers[ 1] <= 0;
78 registers[ 2] <= 0;
79 registers[ 3] <= 0;
80 registers[ 4] <= 0;
81 registers[ 5] <= 0;
82 registers[ 6] <= 0;
83 registers[ 7] <= 0;
84 registers[ 8] <= 0;
85 registers[ 9] <= 0;
86 registers[10] <= 0;
87 registers[11] <= 0;
2f55f809
JW
88 end
89
90 always @(posedge clk)
91 case (state)
92 `STATE_FETCH: begin
93 if (wr)
94 buswdata <= wdata;
95 if (newcycle)
96 busaddress <= {registers[`REG_PCH], registers[`REG_PCL]};
97 else
98 busaddress <= address;
99 buswr <= wr;
100 busrd <= rd;
101 state <= `STATE_DECODE;
102 end
103 `STATE_DECODE: begin
104 if (newcycle) begin
105 opcode <= busdata;
106 rdata <= busdata;
b85870e0 107 newcycle <= 0;
2f55f809
JW
108 cycle <= 0;
109 end else
110 if (rd) rdata <= busdata;
111 buswr <= 0;
112 busrd <= 0;
97649fed
JW
113 wr <= 0;
114 rd <= 0;
115 address <= 16'bxxxxxxxxxxxxxxxx; // Make it obvious if something of type has happened.
116 wdata <= 8'bxxxxxxxx;
2f55f809
JW
117 state <= `STATE_EXECUTE;
118 end
119 `STATE_EXECUTE: begin
120`define EXEC_INC_PC \
121 {registers[`REG_PCH], registers[`REG_PCL]} <= {registers[`REG_PCH], registers[`REG_PCL]} + 1
122`define EXEC_NEXTADDR_PCINC \
123 address <= {registers[`REG_PCH], registers[`REG_PCL]} + 1
124`define EXEC_NEWCYCLE \
125 newcycle <= 1; rd <= 1; wr <= 0
126 casex (opcode)
127 `INSN_LD_reg_imm8: begin
128 case (cycle)
129 0: begin
130 `EXEC_INC_PC;
131 `EXEC_NEXTADDR_PCINC;
2f55f809
JW
132 rd <= 1;
133 end
134 1: begin
135 `EXEC_INC_PC;
b85870e0 136 if (opcode[5:3] == `INSN_reg_dHL) begin
2f55f809
JW
137 address <= {registers[`REG_H], registers[`REG_L]};
138 wdata <= rdata;
139 rd <= 0;
140 wr <= 1;
141 end else begin
142 `EXEC_NEWCYCLE;
143 end
144 end
145 2: begin
146 `EXEC_NEWCYCLE;
147 end
148 endcase
149 end
b85870e0 150 `INSN_HALT: begin
f2e04715
JW
151 `EXEC_NEWCYCLE;
152 /* XXX Interrupts needed for HALT. */
b85870e0
JW
153 end
154 `INSN_LD_HL_reg: begin
f2e04715
JW
155 case (cycle)
156 0: begin
157 case (opcode[2:0])
158 `INSN_reg_A: begin wdata <= registers[`REG_A]; end
159 `INSN_reg_B: begin wdata <= registers[`REG_B]; end
160 `INSN_reg_C: begin wdata <= registers[`REG_C]; end
161 `INSN_reg_D: begin wdata <= registers[`REG_D]; end
162 `INSN_reg_E: begin wdata <= registers[`REG_E]; end
163 `INSN_reg_H: begin wdata <= registers[`REG_H]; end
164 `INSN_reg_L: begin wdata <= registers[`REG_L]; end
165 endcase
166 address <= {registers[`REG_H], registers[`REG_L]};
167 wr <= 1; rd <= 0;
168 end
169 1: begin
170 `EXEC_INC_PC;
171 `EXEC_NEWCYCLE;
172 end
173 endcase
b85870e0
JW
174 end
175 `INSN_LD_reg_HL: begin
f2e04715
JW
176 case(cycle)
177 0: begin
178 address <= {registers[`REG_H], registers[`REG_L]};
97649fed 179 rd <= 1;
f2e04715
JW
180 end
181 1: begin
182 tmp <= rdata;
183 `EXEC_INC_PC;
184 `EXEC_NEWCYCLE;
185 end
186 endcase
b85870e0
JW
187 end
188 `INSN_LD_reg_reg: begin
189 `EXEC_INC_PC;
190 `EXEC_NEWCYCLE;
191 case (opcode[2:0])
192 `INSN_reg_A: begin tmp <= registers[`REG_A]; end
193 `INSN_reg_B: begin tmp <= registers[`REG_B]; end
194 `INSN_reg_C: begin tmp <= registers[`REG_C]; end
195 `INSN_reg_D: begin tmp <= registers[`REG_D]; end
196 `INSN_reg_E: begin tmp <= registers[`REG_E]; end
197 `INSN_reg_H: begin tmp <= registers[`REG_H]; end
198 `INSN_reg_L: begin tmp <= registers[`REG_L]; end
199 endcase
200 end
634ce02c
JW
201 `INSN_LD_reg_imm16: begin
202 `EXEC_INC_PC;
203 case (cycle)
204 0: begin
205 `EXEC_NEXTADDR_PCINC;
206 rd <= 1;
207 end
208 1: begin
209 `EXEC_NEXTADDR_PCINC;
210 rd <= 1;
211 end
212 2: begin `EXEC_NEWCYCLE; end
213 endcase
214 end
215 `INSN_LD_SP_HL: begin
216 case (cycle)
217 0: begin
634ce02c
JW
218 tmp <= registers[`REG_H];
219 end
220 1: begin
221 `EXEC_NEWCYCLE;
222 `EXEC_INC_PC;
223 tmp <= registers[`REG_L];
224 end
225 endcase
226 end
97649fed
JW
227 `INSN_PUSH_reg: begin /* PUSH is 16 cycles! */
228 case (cycle)
229 0: begin
230 wr <= 1;
231 address <= {registers[`REG_SPH],registers[`REG_SPL]}-1;
232 case (opcode[5:4])
233 `INSN_stack_AF: wdata <= registers[`REG_A];
234 `INSN_stack_BC: wdata <= registers[`REG_B];
235 `INSN_stack_DE: wdata <= registers[`REG_D];
236 `INSN_stack_HL: wdata <= registers[`REG_H];
237 endcase
238 end
239 1: begin
240 wr <= 1;
241 address <= {registers[`REG_SPH],registers[`REG_SPL]}-1;
242 case (opcode[5:4])
243 `INSN_stack_AF: wdata <= registers[`REG_F];
244 `INSN_stack_BC: wdata <= registers[`REG_C];
245 `INSN_stack_DE: wdata <= registers[`REG_E];
246 `INSN_stack_HL: wdata <= registers[`REG_L];
247 endcase
248 end
249 2: begin /* TWIDDLE OUR FUCKING THUMBS! */ end
250 3: begin
251 `EXEC_NEWCYCLE;
252 `EXEC_INC_PC;
253 end
254 endcase
255 end
256 `INSN_POP_reg: begin /* POP is 12 cycles! */
257 case (cycle)
258 0: begin
259 rd <= 1;
260 address <= {registers[`REG_SPH],registers[`REG_SPL]};
261 end
262 1: begin
263 rd <= 1;
264 address <= {registers[`REG_SPH],registers[`REG_SPL]};
265 end
266 2: begin
267 `EXEC_NEWCYCLE;
268 `EXEC_INC_PC;
269 end
270 endcase
271 end
00e30b4d
JW
272 `INSN_LDH_AC: begin
273 case (cycle)
274 0: begin
275 address <= {8'hFF,registers[`REG_C]};
276 if (opcode[4]) begin // LD A,(C)
277 rd <= 1;
278 end else begin
279 wr <= 1;
fa136d63 280 wdata <= registers[`REG_A];
00e30b4d
JW
281 end
282 end
283 1: begin
284 `EXEC_NEWCYCLE;
285 `EXEC_INC_PC;
286 end
287 endcase
288 end
fa136d63
JW
289 `INSN_LDx_AHL: begin
290 case (cycle)
291 0: begin
292 address <= {registers[`REG_H],registers[`REG_L]};
293 if (opcode[3]) begin // LDx A, (HL)
294 rd <= 1;
295 end else begin
296 wr <= 1;
297 wdata <= registers[`REG_A];
298 end
299 end
300 1: begin
301 `EXEC_NEWCYCLE;
302 `EXEC_INC_PC;
303 end
304 endcase
305 end
634ce02c
JW
306 default:
307 $stop;
2f55f809
JW
308 endcase
309 state <= `STATE_WRITEBACK;
310 end
311 `STATE_WRITEBACK: begin
312 casex (opcode)
634ce02c
JW
313 `INSN_LD_reg_imm8:
314 case (cycle)
315 0: cycle <= 1;
316 1: case (opcode[5:3])
317 `INSN_reg_A: begin registers[`REG_A] <= rdata; cycle <= 0; end
318 `INSN_reg_B: begin registers[`REG_B] <= rdata; cycle <= 0; end
319 `INSN_reg_C: begin registers[`REG_C] <= rdata; cycle <= 0; end
320 `INSN_reg_D: begin registers[`REG_D] <= rdata; cycle <= 0; end
321 `INSN_reg_E: begin registers[`REG_E] <= rdata; cycle <= 0; end
322 `INSN_reg_H: begin registers[`REG_H] <= rdata; cycle <= 0; end
323 `INSN_reg_L: begin registers[`REG_L] <= rdata; cycle <= 0; end
324 `INSN_reg_dHL: cycle <= 2;
b85870e0 325 endcase
634ce02c
JW
326 2: cycle <= 0;
327 endcase
328 `INSN_HALT: begin
329 /* Nothing needs happen here. */
330 /* XXX Interrupts needed for HALT. */
331 end
332 `INSN_LD_HL_reg: begin
333 case (cycle)
334 0: cycle <= 1;
335 1: cycle <= 0;
336 endcase
337 end
338 `INSN_LD_reg_HL: begin
339 case (cycle)
340 0: cycle <= 1;
341 1: begin
342 case (opcode[5:3])
343 `INSN_reg_A: begin registers[`REG_A] <= tmp; end
344 `INSN_reg_B: begin registers[`REG_B] <= tmp; end
345 `INSN_reg_C: begin registers[`REG_C] <= tmp; end
346 `INSN_reg_D: begin registers[`REG_D] <= tmp; end
347 `INSN_reg_E: begin registers[`REG_E] <= tmp; end
348 `INSN_reg_H: begin registers[`REG_H] <= tmp; end
349 `INSN_reg_L: begin registers[`REG_L] <= tmp; end
350 endcase
351 cycle <= 0;
352 end
353 endcase
354 end
355 `INSN_LD_reg_reg: begin
356 case (opcode[5:3])
357 `INSN_reg_A: begin registers[`REG_A] <= tmp; end
358 `INSN_reg_B: begin registers[`REG_B] <= tmp; end
359 `INSN_reg_C: begin registers[`REG_C] <= tmp; end
360 `INSN_reg_D: begin registers[`REG_D] <= tmp; end
361 `INSN_reg_E: begin registers[`REG_E] <= tmp; end
362 `INSN_reg_H: begin registers[`REG_H] <= tmp; end
363 `INSN_reg_L: begin registers[`REG_L] <= tmp; end
364 endcase
365 end
366 `INSN_LD_reg_imm16: begin
367 case (cycle)
368 0: cycle <= 1;
369 1: begin
370 case (opcode[5:4])
371 `INSN_reg16_BC: registers[`REG_C] <= rdata;
372 `INSN_reg16_DE: registers[`REG_E] <= rdata;
373 `INSN_reg16_HL: registers[`REG_L] <= rdata;
374 `INSN_reg16_SP: registers[`REG_SPL] <= rdata;
375 endcase
376 cycle <= 2;
377 end
378 2: begin
379 case (opcode[5:4])
380 `INSN_reg16_BC: registers[`REG_B] <= rdata;
381 `INSN_reg16_DE: registers[`REG_D] <= rdata;
382 `INSN_reg16_HL: registers[`REG_H] <= rdata;
383 `INSN_reg16_SP: registers[`REG_SPH] <= rdata;
384 endcase
385 cycle <= 0;
386 end
387 endcase
388 end
389 `INSN_LD_SP_HL: begin
390 case (cycle)
391 0: begin
392 cycle <= 1;
393 registers[`REG_SPH] <= tmp;
394 end
395 1: begin
396 cycle <= 0;
397 registers[`REG_SPL] <= tmp;
398 end
399 endcase
400 end
97649fed
JW
401 `INSN_PUSH_reg: begin /* PUSH is 16 cycles! */
402 case (cycle)
403 0: begin
241c995c 404 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
405 {registers[`REG_SPH],registers[`REG_SPL]} - 1;
406 cycle <= 1;
407 end
408 1: begin
241c995c 409 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
410 {registers[`REG_SPH],registers[`REG_SPL]} - 1;
411 cycle <= 2;
412 end
413 2: cycle <= 3;
414 3: cycle <= 0;
415 endcase
416 end
417 `INSN_POP_reg: begin /* POP is 12 cycles! */
418 case (cycle)
419 0: begin
420 cycle <= 1;
241c995c 421 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
422 {registers[`REG_SPH],registers[`REG_SPL]} + 1;
423 end
424 1: begin
425 case (opcode[5:4])
426 `INSN_stack_AF: registers[`REG_F] <= rdata;
427 `INSN_stack_BC: registers[`REG_C] <= rdata;
428 `INSN_stack_DE: registers[`REG_E] <= rdata;
429 `INSN_stack_HL: registers[`REG_L] <= rdata;
430 endcase
241c995c 431 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
432 {registers[`REG_SPH],registers[`REG_SPL]} + 1;
433 cycle <= 2;
434 end
435 2: begin
436 case (opcode[5:4])
437 `INSN_stack_AF: registers[`REG_A] <= rdata;
438 `INSN_stack_BC: registers[`REG_B] <= rdata;
439 `INSN_stack_DE: registers[`REG_D] <= rdata;
440 `INSN_stack_HL: registers[`REG_H] <= rdata;
441 endcase
442 cycle <= 0;
443 end
444 endcase
00e30b4d
JW
445 end
446 `INSN_LDH_AC: begin
447 case (cycle)
448 0: cycle <= 1;
449 1: begin
450 cycle <= 0;
451 if (opcode[4])
452 registers[`REG_A] <= rdata;
453 end
454 endcase
455 end
fa136d63
JW
456 `INSN_LDx_AHL: begin
457 case (cycle)
458 0: cycle <= 1;
459 1: begin
460 cycle <= 0;
461 if (opcode[3])
462 registers[`REG_A] <= rdata;
463 {registers[`REG_H],registers[`REG_L]} <=
464 opcode[4] ? // if set, LDD, else LDI
465 ({registers[`REG_H],registers[`REG_L]} - 1) :
466 ({registers[`REG_H],registers[`REG_L]} + 1);
467 end
468 endcase
469 end
2f55f809
JW
470 endcase
471 state <= `STATE_FETCH;
472 end
473 endcase
474endmodule
475
476`timescale 1ns / 1ps
477module TestBench();
478 reg clk = 0;
479 wire [15:0] addr;
480 wire [7:0] data;
481 wire wr, rd;
482 reg [7:0] rom [2047:0];
483
484 initial $readmemh("rom.hex", rom);
485 always #10 clk <= ~clk;
486 GBZ80Core core(
487 .clk(clk),
488 .busaddress(addr),
489 .busdata(data),
490 .buswr(wr),
491 .busrd(rd));
492 assign data = rd ? rom[addr] : 8'bzzzzzzzz;
493endmodule
This page took 0.063681 seconds and 4 git commands to generate.