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