NOP, and bug fixes
[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 34`define INSN_LDx_AHL 8'b001xx010 // LDD/LDI A,(HL) / (HL),A
94522011 35`define INSN_ALU8 8'b10xxxxxx // 10 xxx yyy
d3938806 36`define INSN_NOP 8'b00000000
fa136d63 37
b85870e0
JW
38`define INSN_reg_A 3'b111
39`define INSN_reg_B 3'b000
40`define INSN_reg_C 3'b001
41`define INSN_reg_D 3'b010
42`define INSN_reg_E 3'b011
43`define INSN_reg_H 3'b100
44`define INSN_reg_L 3'b101
45`define INSN_reg_dHL 3'b110
634ce02c
JW
46`define INSN_reg16_BC 2'b00
47`define INSN_reg16_DE 2'b01
48`define INSN_reg16_HL 2'b10
49`define INSN_reg16_SP 2'b11
97649fed
JW
50`define INSN_stack_AF 2'b11
51`define INSN_stack_BC 2'b00
52`define INSN_stack_DE 2'b01
53`define INSN_stack_HL 2'b10
94522011
JW
54`define INSN_alu_ADD 3'b000
55`define INSN_alu_ADC 3'b001
56`define INSN_alu_SUB 3'b010
57`define INSN_alu_SBC 3'b011
58`define INSN_alu_AND 3'b100
59`define INSN_alu_XOR 3'b101
60`define INSN_alu_OR 3'b110
61`define INSN_alu_CP 3'b111 // Oh lawd, is dat some CP?
62
2f55f809
JW
63module GBZ80Core(
64 input clk,
65 output reg [15:0] busaddress, /* BUS_* is latched on STATE_FETCH. */
66 inout [7:0] busdata,
67 output reg buswr, output reg busrd);
68
69 reg [1:0] state = 0; /* State within this bus cycle (see STATE_*). */
70 reg [2:0] cycle = 0; /* Cycle for instructions. */
71
72 reg [7:0] registers[11:0];
73
74 reg [15:0] address; /* Address for the next bus operation. */
75
76 reg [7:0] opcode; /* Opcode from the current machine cycle. */
77
78 reg [7:0] rdata, wdata; /* Read data from this bus cycle, or write data for the next. */
79 reg rd = 1, wr = 0, newcycle = 1;
80
b85870e0
JW
81 reg [7:0] tmp; /* Generic temporary reg. */
82
2f55f809
JW
83 reg [7:0] buswdata;
84 assign busdata = buswr ? buswdata : 8'bzzzzzzzz;
85
86 initial begin
241c995c
JW
87 registers[ 0] <= 0;
88 registers[ 1] <= 0;
89 registers[ 2] <= 0;
90 registers[ 3] <= 0;
91 registers[ 4] <= 0;
92 registers[ 5] <= 0;
93 registers[ 6] <= 0;
94 registers[ 7] <= 0;
95 registers[ 8] <= 0;
96 registers[ 9] <= 0;
97 registers[10] <= 0;
98 registers[11] <= 0;
2f55f809
JW
99 end
100
101 always @(posedge clk)
102 case (state)
103 `STATE_FETCH: begin
104 if (wr)
105 buswdata <= wdata;
106 if (newcycle)
107 busaddress <= {registers[`REG_PCH], registers[`REG_PCL]};
108 else
109 busaddress <= address;
110 buswr <= wr;
111 busrd <= rd;
112 state <= `STATE_DECODE;
113 end
114 `STATE_DECODE: begin
115 if (newcycle) begin
116 opcode <= busdata;
117 rdata <= busdata;
b85870e0 118 newcycle <= 0;
2f55f809
JW
119 cycle <= 0;
120 end else
121 if (rd) rdata <= busdata;
122 buswr <= 0;
123 busrd <= 0;
97649fed
JW
124 wr <= 0;
125 rd <= 0;
126 address <= 16'bxxxxxxxxxxxxxxxx; // Make it obvious if something of type has happened.
127 wdata <= 8'bxxxxxxxx;
2f55f809
JW
128 state <= `STATE_EXECUTE;
129 end
130 `STATE_EXECUTE: begin
131`define EXEC_INC_PC \
132 {registers[`REG_PCH], registers[`REG_PCL]} <= {registers[`REG_PCH], registers[`REG_PCL]} + 1
133`define EXEC_NEXTADDR_PCINC \
134 address <= {registers[`REG_PCH], registers[`REG_PCL]} + 1
135`define EXEC_NEWCYCLE \
136 newcycle <= 1; rd <= 1; wr <= 0
137 casex (opcode)
138 `INSN_LD_reg_imm8: begin
139 case (cycle)
140 0: begin
141 `EXEC_INC_PC;
142 `EXEC_NEXTADDR_PCINC;
2f55f809
JW
143 rd <= 1;
144 end
145 1: begin
146 `EXEC_INC_PC;
b85870e0 147 if (opcode[5:3] == `INSN_reg_dHL) begin
2f55f809
JW
148 address <= {registers[`REG_H], registers[`REG_L]};
149 wdata <= rdata;
150 rd <= 0;
151 wr <= 1;
152 end else begin
153 `EXEC_NEWCYCLE;
154 end
155 end
156 2: begin
157 `EXEC_NEWCYCLE;
158 end
159 endcase
160 end
b85870e0 161 `INSN_HALT: begin
f2e04715
JW
162 `EXEC_NEWCYCLE;
163 /* XXX Interrupts needed for HALT. */
b85870e0
JW
164 end
165 `INSN_LD_HL_reg: begin
f2e04715
JW
166 case (cycle)
167 0: begin
168 case (opcode[2:0])
169 `INSN_reg_A: begin wdata <= registers[`REG_A]; end
170 `INSN_reg_B: begin wdata <= registers[`REG_B]; end
171 `INSN_reg_C: begin wdata <= registers[`REG_C]; end
172 `INSN_reg_D: begin wdata <= registers[`REG_D]; end
173 `INSN_reg_E: begin wdata <= registers[`REG_E]; end
174 `INSN_reg_H: begin wdata <= registers[`REG_H]; end
175 `INSN_reg_L: begin wdata <= registers[`REG_L]; end
176 endcase
177 address <= {registers[`REG_H], registers[`REG_L]};
178 wr <= 1; rd <= 0;
179 end
180 1: begin
181 `EXEC_INC_PC;
182 `EXEC_NEWCYCLE;
183 end
184 endcase
b85870e0
JW
185 end
186 `INSN_LD_reg_HL: begin
f2e04715
JW
187 case(cycle)
188 0: begin
189 address <= {registers[`REG_H], registers[`REG_L]};
97649fed 190 rd <= 1;
f2e04715
JW
191 end
192 1: begin
193 tmp <= rdata;
194 `EXEC_INC_PC;
195 `EXEC_NEWCYCLE;
196 end
197 endcase
b85870e0
JW
198 end
199 `INSN_LD_reg_reg: begin
200 `EXEC_INC_PC;
201 `EXEC_NEWCYCLE;
202 case (opcode[2:0])
203 `INSN_reg_A: begin tmp <= registers[`REG_A]; end
204 `INSN_reg_B: begin tmp <= registers[`REG_B]; end
205 `INSN_reg_C: begin tmp <= registers[`REG_C]; end
206 `INSN_reg_D: begin tmp <= registers[`REG_D]; end
207 `INSN_reg_E: begin tmp <= registers[`REG_E]; end
208 `INSN_reg_H: begin tmp <= registers[`REG_H]; end
209 `INSN_reg_L: begin tmp <= registers[`REG_L]; end
210 endcase
211 end
634ce02c
JW
212 `INSN_LD_reg_imm16: begin
213 `EXEC_INC_PC;
214 case (cycle)
215 0: begin
216 `EXEC_NEXTADDR_PCINC;
217 rd <= 1;
218 end
219 1: begin
220 `EXEC_NEXTADDR_PCINC;
221 rd <= 1;
222 end
223 2: begin `EXEC_NEWCYCLE; end
224 endcase
225 end
226 `INSN_LD_SP_HL: begin
227 case (cycle)
228 0: begin
634ce02c
JW
229 tmp <= registers[`REG_H];
230 end
231 1: begin
232 `EXEC_NEWCYCLE;
233 `EXEC_INC_PC;
234 tmp <= registers[`REG_L];
235 end
236 endcase
237 end
97649fed
JW
238 `INSN_PUSH_reg: begin /* PUSH is 16 cycles! */
239 case (cycle)
240 0: begin
241 wr <= 1;
242 address <= {registers[`REG_SPH],registers[`REG_SPL]}-1;
243 case (opcode[5:4])
244 `INSN_stack_AF: wdata <= registers[`REG_A];
245 `INSN_stack_BC: wdata <= registers[`REG_B];
246 `INSN_stack_DE: wdata <= registers[`REG_D];
247 `INSN_stack_HL: wdata <= registers[`REG_H];
248 endcase
249 end
250 1: begin
251 wr <= 1;
252 address <= {registers[`REG_SPH],registers[`REG_SPL]}-1;
253 case (opcode[5:4])
254 `INSN_stack_AF: wdata <= registers[`REG_F];
255 `INSN_stack_BC: wdata <= registers[`REG_C];
256 `INSN_stack_DE: wdata <= registers[`REG_E];
257 `INSN_stack_HL: wdata <= registers[`REG_L];
258 endcase
259 end
260 2: begin /* TWIDDLE OUR FUCKING THUMBS! */ end
261 3: begin
262 `EXEC_NEWCYCLE;
263 `EXEC_INC_PC;
264 end
265 endcase
266 end
267 `INSN_POP_reg: begin /* POP is 12 cycles! */
268 case (cycle)
269 0: begin
270 rd <= 1;
271 address <= {registers[`REG_SPH],registers[`REG_SPL]};
272 end
273 1: begin
274 rd <= 1;
275 address <= {registers[`REG_SPH],registers[`REG_SPL]};
276 end
277 2: begin
278 `EXEC_NEWCYCLE;
279 `EXEC_INC_PC;
280 end
281 endcase
282 end
00e30b4d
JW
283 `INSN_LDH_AC: begin
284 case (cycle)
285 0: begin
286 address <= {8'hFF,registers[`REG_C]};
287 if (opcode[4]) begin // LD A,(C)
288 rd <= 1;
289 end else begin
290 wr <= 1;
fa136d63 291 wdata <= registers[`REG_A];
00e30b4d
JW
292 end
293 end
294 1: begin
295 `EXEC_NEWCYCLE;
296 `EXEC_INC_PC;
297 end
298 endcase
299 end
fa136d63
JW
300 `INSN_LDx_AHL: begin
301 case (cycle)
302 0: begin
303 address <= {registers[`REG_H],registers[`REG_L]};
304 if (opcode[3]) begin // LDx A, (HL)
305 rd <= 1;
306 end else begin
307 wr <= 1;
308 wdata <= registers[`REG_A];
309 end
310 end
311 1: begin
312 `EXEC_NEWCYCLE;
313 `EXEC_INC_PC;
314 end
315 endcase
316 end
94522011
JW
317 `INSN_ALU8: begin
318 if ((opcode[2:0] == `INSN_reg_dHL) && (cycle == 0)) begin
319 // fffffffff fuck your shit, read from (HL) :(
320 rd <= 1;
321 address <= {registers[`REG_H], registers[`REG_L]};
322 end else begin
323 `EXEC_NEWCYCLE;
324 `EXEC_INC_PC;
325 case (opcode[2:0])
326 `INSN_reg_A: begin tmp <= registers[`REG_A]; end
327 `INSN_reg_B: begin tmp <= registers[`REG_B]; end
328 `INSN_reg_C: begin tmp <= registers[`REG_C]; end
329 `INSN_reg_D: begin tmp <= registers[`REG_D]; end
330 `INSN_reg_E: begin tmp <= registers[`REG_E]; end
331 `INSN_reg_H: begin tmp <= registers[`REG_H]; end
332 `INSN_reg_L: begin tmp <= registers[`REG_L]; end
333 `INSN_reg_dHL: begin tmp <= rdata; end
334 endcase
335 end
336 end
d3938806
JW
337 `INSN_NOP: begin
338 `EXEC_NEWCYCLE;
339 `EXEC_INC_PC;
340 end
634ce02c
JW
341 default:
342 $stop;
2f55f809
JW
343 endcase
344 state <= `STATE_WRITEBACK;
345 end
346 `STATE_WRITEBACK: begin
347 casex (opcode)
634ce02c
JW
348 `INSN_LD_reg_imm8:
349 case (cycle)
350 0: cycle <= 1;
351 1: case (opcode[5:3])
352 `INSN_reg_A: begin registers[`REG_A] <= rdata; cycle <= 0; end
353 `INSN_reg_B: begin registers[`REG_B] <= rdata; cycle <= 0; end
354 `INSN_reg_C: begin registers[`REG_C] <= rdata; cycle <= 0; end
355 `INSN_reg_D: begin registers[`REG_D] <= rdata; cycle <= 0; end
356 `INSN_reg_E: begin registers[`REG_E] <= rdata; cycle <= 0; end
357 `INSN_reg_H: begin registers[`REG_H] <= rdata; cycle <= 0; end
358 `INSN_reg_L: begin registers[`REG_L] <= rdata; cycle <= 0; end
359 `INSN_reg_dHL: cycle <= 2;
b85870e0 360 endcase
634ce02c
JW
361 2: cycle <= 0;
362 endcase
363 `INSN_HALT: begin
364 /* Nothing needs happen here. */
365 /* XXX Interrupts needed for HALT. */
366 end
367 `INSN_LD_HL_reg: begin
368 case (cycle)
369 0: cycle <= 1;
370 1: cycle <= 0;
371 endcase
372 end
373 `INSN_LD_reg_HL: begin
374 case (cycle)
375 0: cycle <= 1;
376 1: begin
377 case (opcode[5:3])
378 `INSN_reg_A: begin registers[`REG_A] <= tmp; end
379 `INSN_reg_B: begin registers[`REG_B] <= tmp; end
380 `INSN_reg_C: begin registers[`REG_C] <= tmp; end
381 `INSN_reg_D: begin registers[`REG_D] <= tmp; end
382 `INSN_reg_E: begin registers[`REG_E] <= tmp; end
383 `INSN_reg_H: begin registers[`REG_H] <= tmp; end
384 `INSN_reg_L: begin registers[`REG_L] <= tmp; end
385 endcase
386 cycle <= 0;
387 end
388 endcase
389 end
390 `INSN_LD_reg_reg: begin
391 case (opcode[5:3])
392 `INSN_reg_A: begin registers[`REG_A] <= tmp; end
393 `INSN_reg_B: begin registers[`REG_B] <= tmp; end
394 `INSN_reg_C: begin registers[`REG_C] <= tmp; end
395 `INSN_reg_D: begin registers[`REG_D] <= tmp; end
396 `INSN_reg_E: begin registers[`REG_E] <= tmp; end
397 `INSN_reg_H: begin registers[`REG_H] <= tmp; end
398 `INSN_reg_L: begin registers[`REG_L] <= tmp; end
399 endcase
400 end
401 `INSN_LD_reg_imm16: begin
402 case (cycle)
403 0: cycle <= 1;
404 1: begin
405 case (opcode[5:4])
406 `INSN_reg16_BC: registers[`REG_C] <= rdata;
407 `INSN_reg16_DE: registers[`REG_E] <= rdata;
408 `INSN_reg16_HL: registers[`REG_L] <= rdata;
409 `INSN_reg16_SP: registers[`REG_SPL] <= rdata;
410 endcase
411 cycle <= 2;
412 end
413 2: begin
414 case (opcode[5:4])
415 `INSN_reg16_BC: registers[`REG_B] <= rdata;
416 `INSN_reg16_DE: registers[`REG_D] <= rdata;
417 `INSN_reg16_HL: registers[`REG_H] <= rdata;
418 `INSN_reg16_SP: registers[`REG_SPH] <= rdata;
419 endcase
420 cycle <= 0;
421 end
422 endcase
423 end
424 `INSN_LD_SP_HL: begin
425 case (cycle)
426 0: begin
427 cycle <= 1;
428 registers[`REG_SPH] <= tmp;
429 end
430 1: begin
431 cycle <= 0;
432 registers[`REG_SPL] <= tmp;
433 end
434 endcase
435 end
97649fed
JW
436 `INSN_PUSH_reg: begin /* PUSH is 16 cycles! */
437 case (cycle)
438 0: begin
241c995c 439 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
440 {registers[`REG_SPH],registers[`REG_SPL]} - 1;
441 cycle <= 1;
442 end
443 1: begin
241c995c 444 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
445 {registers[`REG_SPH],registers[`REG_SPL]} - 1;
446 cycle <= 2;
447 end
448 2: cycle <= 3;
449 3: cycle <= 0;
450 endcase
451 end
452 `INSN_POP_reg: begin /* POP is 12 cycles! */
453 case (cycle)
454 0: begin
455 cycle <= 1;
241c995c 456 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
457 {registers[`REG_SPH],registers[`REG_SPL]} + 1;
458 end
459 1: begin
460 case (opcode[5:4])
461 `INSN_stack_AF: registers[`REG_F] <= rdata;
462 `INSN_stack_BC: registers[`REG_C] <= rdata;
463 `INSN_stack_DE: registers[`REG_E] <= rdata;
464 `INSN_stack_HL: registers[`REG_L] <= rdata;
465 endcase
241c995c 466 {registers[`REG_SPH],registers[`REG_SPL]} <=
97649fed
JW
467 {registers[`REG_SPH],registers[`REG_SPL]} + 1;
468 cycle <= 2;
469 end
470 2: begin
471 case (opcode[5:4])
472 `INSN_stack_AF: registers[`REG_A] <= rdata;
473 `INSN_stack_BC: registers[`REG_B] <= rdata;
474 `INSN_stack_DE: registers[`REG_D] <= rdata;
475 `INSN_stack_HL: registers[`REG_H] <= rdata;
476 endcase
477 cycle <= 0;
478 end
479 endcase
00e30b4d
JW
480 end
481 `INSN_LDH_AC: begin
482 case (cycle)
483 0: cycle <= 1;
484 1: begin
485 cycle <= 0;
486 if (opcode[4])
487 registers[`REG_A] <= rdata;
488 end
489 endcase
490 end
fa136d63
JW
491 `INSN_LDx_AHL: begin
492 case (cycle)
493 0: cycle <= 1;
494 1: begin
495 cycle <= 0;
496 if (opcode[3])
497 registers[`REG_A] <= rdata;
498 {registers[`REG_H],registers[`REG_L]} <=
499 opcode[4] ? // if set, LDD, else LDI
500 ({registers[`REG_H],registers[`REG_L]} - 1) :
501 ({registers[`REG_H],registers[`REG_L]} + 1);
502 end
503 endcase
504 end
94522011
JW
505 `INSN_ALU8: begin
506 if ((opcode[2:0] == `INSN_reg_dHL) && (cycle == 0)) begin
507 /* Sit on our asses. */
508 cycle <= 1;
509 end else begin /* Actually do the computation! */
510 case (opcode[5:3])
511 `INSN_alu_ADD: begin
512 registers[`REG_A] <=
513 registers[`REG_A] + tmp;
514 registers[`REG_F] <=
515 { /* Z */ ((registers[`REG_A] + tmp) == 0) ? 1'b1 : 1'b0,
d3938806 516 /* N */ 1'b0,
94522011
JW
517 /* H */ (({1'b0,registers[`REG_A][3:0]} + {1'b0,tmp[3:0]}) >> 4 == 1) ? 1'b1 : 1'b0,
518 /* C */ (({1'b0,registers[`REG_A]} + {1'b0,tmp}) >> 8 == 1) ? 1'b1 : 1'b0,
519 registers[`REG_F][3:0]
520 };
521 end
6dbce0b5
JW
522 `INSN_alu_ADC: begin
523 registers[`REG_A] <=
524 registers[`REG_A] + tmp + {7'b0,registers[`REG_F][4]};
525 registers[`REG_F] <=
526 { /* Z */ ((registers[`REG_A] + tmp + {7'b0,registers[`REG_F][4]}) == 0) ? 1'b1 : 1'b0,
d3938806 527 /* N */ 1'b0,
6dbce0b5
JW
528 /* H */ (({1'b0,registers[`REG_A][3:0]} + {1'b0,tmp[3:0]} + {4'b0,registers[`REG_F][4]}) >> 4 == 1) ? 1'b1 : 1'b0,
529 /* C */ (({1'b0,registers[`REG_A]} + {1'b0,tmp} + {8'b0,registers[`REG_F][4]}) >> 8 == 1) ? 1'b1 : 1'b0,
530 registers[`REG_F][3:0]
531 };
532 end
533 `INSN_alu_AND: begin
534 registers[`REG_A] <=
535 registers[`REG_A] & tmp;
536 registers[`REG_F] <=
537 { /* Z */ ((registers[`REG_A] & tmp) == 0) ? 1'b1 : 1'b0,
d3938806 538 3'b010,
6dbce0b5
JW
539 registers[`REG_F][3:0]
540 };
541 end
542 `INSN_alu_OR: begin
543 registers[`REG_A] <=
544 registers[`REG_A] | tmp;
545 registers[`REG_F] <=
546 { /* Z */ ((registers[`REG_A] | tmp) == 0) ? 1'b1 : 1'b0,
d3938806 547 3'b000,
6dbce0b5
JW
548 registers[`REG_F][3:0]
549 };
550 end
551 `INSN_alu_XOR: begin
552 registers[`REG_A] <=
553 registers[`REG_A] ^ tmp;
554 registers[`REG_F] <=
555 { /* Z */ ((registers[`REG_A] ^ tmp) == 0) ? 1'b1 : 1'b0,
d3938806 556 3'b000,
6dbce0b5
JW
557 registers[`REG_F][3:0]
558 };
559 end
94522011
JW
560 default:
561 $stop;
562 endcase
563 end
564 end
d3938806 565 `INSN_NOP: begin /* NOP! */ end
2f55f809
JW
566 endcase
567 state <= `STATE_FETCH;
568 end
569 endcase
570endmodule
571
572`timescale 1ns / 1ps
573module TestBench();
574 reg clk = 0;
575 wire [15:0] addr;
576 wire [7:0] data;
577 wire wr, rd;
578 reg [7:0] rom [2047:0];
579
580 initial $readmemh("rom.hex", rom);
581 always #10 clk <= ~clk;
582 GBZ80Core core(
583 .clk(clk),
584 .busaddress(addr),
585 .busdata(data),
586 .buswr(wr),
587 .busrd(rd));
588 assign data = rd ? rom[addr] : 8'bzzzzzzzz;
589endmodule
This page took 0.0736 seconds and 4 git commands to generate.