+`define REG_A 0
+`define REG_B 1
+`define REG_C 2
+`define REG_D 3
+`define REG_E 4
+`define REG_F 5
+`define REG_H 6
+`define REG_L 7
+`define REG_SPH 8
+`define REG_SPL 9
+`define REG_PCH 10
+`define REG_PCL 11
+
+`define FLAG_Z 8'b10000000
+`define FLAG_N 8'b01000000
+`define FLAG_H 8'b00100000
+`define FLAG_C 8'b00010000
+
+`define STATE_FETCH 2'h0
+`define STATE_DECODE 2'h1
+`define STATE_EXECUTE 2'h2
+`define STATE_WRITEBACK 2'h3
+
+`define INSN_LD_reg_imm8 8'b00xxx110
+`define INSN_imm8_reg_A 3'b111
+`define INSN_imm8_reg_B 3'b000
+`define INSN_imm8_reg_C 3'b001
+`define INSN_imm8_reg_D 3'b010
+`define INSN_imm8_reg_E 3'b011
+`define INSN_imm8_reg_H 3'b100
+`define INSN_imm8_reg_L 3'b101
+`define INSN_imm8_reg_dHL 3'b110
+
+module GBZ80Core(
+ input clk,
+ output reg [15:0] busaddress, /* BUS_* is latched on STATE_FETCH. */
+ inout [7:0] busdata,
+ output reg buswr, output reg busrd);
+
+ reg [1:0] state = 0; /* State within this bus cycle (see STATE_*). */
+ reg [2:0] cycle = 0; /* Cycle for instructions. */
+
+ reg [7:0] registers[11:0];
+
+ reg [15:0] address; /* Address for the next bus operation. */
+
+ reg [7:0] opcode; /* Opcode from the current machine cycle. */
+
+ reg [7:0] rdata, wdata; /* Read data from this bus cycle, or write data for the next. */
+ reg rd = 1, wr = 0, newcycle = 1;
+
+ reg [7:0] buswdata;
+ assign busdata = buswr ? buswdata : 8'bzzzzzzzz;
+
+ initial begin
+ registers[ 0] = 0;
+ registers[ 1] = 0;
+ registers[ 2] = 0;
+ registers[ 3] = 0;
+ registers[ 4] = 0;
+ registers[ 5] = 0;
+ registers[ 6] = 0;
+ registers[ 7] = 0;
+ registers[ 8] = 0;
+ registers[ 9] = 0;
+ registers[10] = 0;
+ registers[11] = 0;
+ end
+
+ always @(posedge clk)
+ case (state)
+ `STATE_FETCH: begin
+ if (wr)
+ buswdata <= wdata;
+ if (newcycle)
+ busaddress <= {registers[`REG_PCH], registers[`REG_PCL]};
+ else
+ busaddress <= address;
+ buswr <= wr;
+ busrd <= rd;
+ state <= `STATE_DECODE;
+ end
+ `STATE_DECODE: begin
+ if (newcycle) begin
+ opcode <= busdata;
+ rdata <= busdata;
+ cycle <= 0;
+ end else
+ if (rd) rdata <= busdata;
+ buswr <= 0;
+ busrd <= 0;
+ state <= `STATE_EXECUTE;
+ end
+ `STATE_EXECUTE: begin
+`define EXEC_INC_PC \
+ {registers[`REG_PCH], registers[`REG_PCL]} <= {registers[`REG_PCH], registers[`REG_PCL]} + 1
+`define EXEC_NEXTADDR_PCINC \
+ address <= {registers[`REG_PCH], registers[`REG_PCL]} + 1
+`define EXEC_NEWCYCLE \
+ newcycle <= 1; rd <= 1; wr <= 0
+ casex (opcode)
+ `INSN_LD_reg_imm8: begin
+ case (cycle)
+ 0: begin
+ `EXEC_INC_PC;
+ `EXEC_NEXTADDR_PCINC;
+ newcycle <= 0;
+ rd <= 1;
+ end
+ 1: begin
+ `EXEC_INC_PC;
+ if (opcode[5:3] == `INSN_imm8_reg_dHL) begin
+ address <= {registers[`REG_H], registers[`REG_L]};
+ wdata <= rdata;
+ rd <= 0;
+ wr <= 1;
+ end else begin
+ `EXEC_NEWCYCLE;
+ end
+ end
+ 2: begin
+ `EXEC_NEWCYCLE;
+ end
+ endcase
+ end
+ endcase
+ state <= `STATE_WRITEBACK;
+ end
+ `STATE_WRITEBACK: begin
+ casex (opcode)
+ `INSN_LD_reg_imm8:
+ case (cycle)
+ 0: cycle <= 1;
+ 1: case (opcode[5:3])
+ `INSN_imm8_reg_A: begin registers[`REG_A] <= rdata; cycle <= 0; end
+ `INSN_imm8_reg_B: begin registers[`REG_B] <= rdata; cycle <= 0; end
+ `INSN_imm8_reg_C: begin registers[`REG_C] <= rdata; cycle <= 0; end
+ `INSN_imm8_reg_D: begin registers[`REG_D] <= rdata; cycle <= 0; end
+ `INSN_imm8_reg_E: begin registers[`REG_E] <= rdata; cycle <= 0; end
+ `INSN_imm8_reg_H: begin registers[`REG_H] <= rdata; cycle <= 0; end
+ `INSN_imm8_reg_L: begin registers[`REG_L] <= rdata; cycle <= 0; end
+ `INSN_imm8_reg_dHL: cycle <= 2;
+ endcase
+ 2: cycle <= 0;
+ endcase
+ endcase
+ state <= `STATE_FETCH;
+ end
+ endcase
+endmodule
+
+`timescale 1ns / 1ps
+module TestBench();
+ reg clk = 0;
+ wire [15:0] addr;
+ wire [7:0] data;
+ wire wr, rd;
+ reg [7:0] rom [2047:0];
+
+ initial $readmemh("rom.hex", rom);
+ always #10 clk <= ~clk;
+ GBZ80Core core(
+ .clk(clk),
+ .busaddress(addr),
+ .busdata(data),
+ .buswr(wr),
+ .busrd(rd));
+ assign data = rd ? rom[addr] : 8'bzzzzzzzz;
+endmodule