blob: e272690041e304a6b7099d8a09ed7796e5034b76 [file] [log] [blame]
`timescale 1ns / 1ps
`default_nettype wire
`include "common.v"
//////////////////////////////////////////////////////////////////////////////////
// Company:
// Engineer: Wenting Zhang
//
// Module Name: control
// Project Name: VerilogBoy
// Description:
// The control unit of Game Boy CPU.
// Dependencies:
//
// Additional Comments:
//
//////////////////////////////////////////////////////////////////////////////////
module control(
input clk,
input rst,
input [7:0] opcode_early,
/* verilator lint_off UNUSED */
input [7:0] imm,
/* verilator lint_on UNUSED */
input [7:0] cb,
input [2:0] m_cycle_early,
input [1:0] ct_state,
input f_z,
input f_c,
output reg [1:0] alu_src_a,
output reg [2:0] alu_src_b,
output reg alu_src_xchg,
output reg [1:0] alu_op_prefix,
output reg [1:0] alu_op_src,
output reg alu_op_signed,
output reg [1:0] alu_dst,
output reg [1:0] pc_src,
output reg pc_we,
output reg pc_b_sel,
output reg pc_jr,
output reg pc_revert,
output reg [2:0] rf_wr_sel,
output reg [2:0] rf_rd_sel,
output reg [1:0] rf_rdw_sel,
output reg temp_redir,
output reg opcode_redir,
output reg [1:0] bus_op,
output reg [1:0] db_src,
output reg [1:0] ab_src,
output reg [1:0] ct_op,
output reg flags_we,
output reg [1:0] flags_pattern,
output reg high_mask,
output int_master_en,
input int_dispatch,
output reg int_ack,
output reg next,
output reg stop,
output reg halt,
input wake,
output reg fault
);
// Comb signal generated by control logic
reg ime_clear;
reg ime_set;
reg ime_delay_set;
// FF
reg ime_delay_set_ff;
reg ime;
assign int_master_en = ime;
wire [7:0] opcode = opcode_early;
wire [2:0] m_cycle = m_cycle_early;
always @(posedge clk)
if (ct_state == 2'd2)
ime_delay_set_ff <= ime_delay_set;
always @(posedge clk) begin
if (rst)
ime <= 1'b0;
else if (ime_clear)
ime <= 1'b0;
else if (ime_set)
ime <= 1'b1;
else if (ime_delay_set_ff)
ime <= 1'b1;
end
reg halt_last;
reg stop_last;
reg fault_last;
always @(posedge clk) begin
if (rst) begin
halt_last <= 1'b0;
stop_last <= 1'b0;
fault_last <= 1'b0;
end
else begin
halt_last <= halt;
stop_last <= stop;
fault_last <= fault;
end
end
reg wake_by_int;
always @(posedge clk) begin
if (rst)
wake_by_int <= 1'b0;
else begin
if (int_dispatch && (m_cycle == 0)) begin
wake_by_int <= wake;
end
end
end
// Combinational control signal
reg [1:0] comb_alu_src_a;
reg [2:0] comb_alu_src_b;
reg comb_alu_src_xchg;
reg [1:0] comb_alu_op_prefix;
reg [1:0] comb_alu_op_src;
reg comb_alu_op_signed;
reg [1:0] comb_alu_dst;
reg [1:0] comb_pc_src;
reg comb_pc_we;
reg comb_pc_b_sel;
reg comb_pc_jr;
reg comb_pc_revert;
reg [2:0] comb_rf_wr_sel;
reg [2:0] comb_rf_rd_sel;
reg [1:0] comb_rf_rdw_sel;
reg comb_temp_redir;
reg comb_opcode_redir;
reg [1:0] comb_bus_op;
reg [1:0] comb_db_src;
reg [1:0] comb_ab_src;
reg [1:0] comb_ct_op;
reg comb_flags_we;
reg [1:0] comb_flags_pattern;
reg comb_high_mask;
reg comb_int_ack;
reg comb_next;
reg comb_stop;
reg comb_halt;
reg comb_fault;
// All these nonsense will be replaced by a vector decoding ROM...
// in the future
always @(*) begin
// Set default output
// ACC = ACC + 0
comb_alu_src_a = `ALU_SRC_A_ACC;
comb_alu_src_b = `ALU_SRC_B_ZERO;
comb_alu_op_prefix = `ALU_OP_PREFIX_NORMAL;
comb_alu_op_src = `ALU_OP_SRC_ADD_FTOR;
comb_alu_dst = `ALU_DST_ACC;
comb_pc_we = 0;
comb_rf_wr_sel = `RF_SEL_B; // Doesn't matter
comb_rf_rd_sel = `RF_SEL_B; // Doesn't matter
comb_bus_op = `BUS_OP_IF; // Fetch comb_next instruction
comb_db_src = `DB_SRC_DB; // Should != ACC
comb_ab_src = `AB_SRC_PC; // Output PC
comb_ct_op = `CT_OP_PC_INC; // PC = PC + 1
comb_flags_we = 0;
comb_next = 0;
comb_alu_src_xchg = 0;
comb_rf_rdw_sel = 2'b10; // Select HL
comb_pc_src = 2'b00;
comb_pc_b_sel = m_cycle[0];
comb_pc_jr = 1'b0;
comb_pc_revert = 1'b0;
comb_stop = 1'b0;
comb_halt = 1'b0;
comb_fault = 1'b0;
comb_high_mask = 1'b0;
comb_alu_op_signed = 1'b0;
comb_temp_redir = 1'b0;
comb_opcode_redir = 1'b0;
ime_set = 1'b0;
ime_delay_set = 1'b0;
ime_clear = 1'b0;
comb_int_ack = 1'b0;
comb_flags_pattern = 2'b00;
// Though the idea behind the original GB is that when in comb_halt or comb_stop
// mode, the clock can be comb_stopped, thus lower the power consumption and
// save the battery. On FPGA, this is hard to achieve since clocking in
// FPGA works very differently than on ASIC. So here, when comb_halted, CPU
// would executing NOP in place as if it was comb_halted.
if (halt_last || stop_last || fault_last) begin
if (wake) begin
comb_halt = 1'b0;
comb_stop = 1'b0;
// Fault could not be waked up
end
else begin
// Keep sleeping
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_halt = halt_last;
comb_stop = stop_last;
end
// Fault cannot be waken up
comb_fault = fault_last;
end
if (int_dispatch) begin
// Interrupt dispatch process
case (m_cycle)
0: begin
// Revert PC
comb_pc_revert = 1'b1;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_SP_DEC;
comb_next = 1'b1;
end
1: begin
// Save PCh
comb_alu_src_a = `ALU_SRC_A_PC;
comb_alu_dst = `ALU_DST_DB;
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_ct_op = `CT_OP_SP_DEC;
comb_next = 1'b1;
end
2: begin
// Save PCl
comb_alu_src_a = `ALU_SRC_A_PC;
comb_alu_dst = `ALU_DST_DB;
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_ct_op = `CT_OP_IDLE;
comb_pc_we = 1;
comb_next = 1'b1;
end
3: begin
// Delay
if (wake_by_int) begin
ime_clear = 1'b1;
comb_int_ack = 1'b1;
end
else begin
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1'b1;
end
end
4: begin
// Normal instruction fetch process
ime_clear = 1'b1;
comb_int_ack = 1'b1;
end
endcase
end
//else begin
// If waken up
if (!comb_halt && !comb_stop && !comb_fault && !int_dispatch) begin
if (opcode == 8'h00) begin // NOP
// Default behavior is enough
end
else if (opcode == 8'h10) begin // STOP
comb_stop = 1;
end
else if (opcode == 8'h76) begin // HALT
comb_halt = 1;
end
else if (opcode == 8'hF3) begin // DI
ime_clear = 1'b1;
end
else if (opcode == 8'hFB) begin // EI
// EI here need to be delayed for 1 clock?
ime_delay_set = 1'b1;
end
// 16-bit IMM to register LD instructions
else if ((opcode[7:6] == 2'b00) && (opcode[3:0] == 4'b0001)) begin
comb_alu_src_a = `ALU_SRC_A_DB; // Load from databus
comb_alu_dst = `ALU_DST_REG; // Load to register
comb_db_src = `DB_SRC_DB; // DB destination to databus buffer
if ((m_cycle == 0) || (m_cycle == 1)) begin
comb_rf_wr_sel = {opcode[5:4], 1'b1}; // Register no based on opcode
comb_bus_op = `BUS_OP_READ; // Read from databus
comb_next = 1;
end
else begin
comb_rf_wr_sel = {opcode[5:4], 1'b0};
comb_next = 0;
end
end
// LD (nn), SP
else if (opcode == 8'h08) begin
if ((m_cycle == 0) || (m_cycle == 1)) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_PC;
comb_ct_op = `CT_OP_PC_INC;
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_ab_src = `AB_SRC_TEMP;
comb_db_src = `DB_SRC_REG;
comb_rf_rd_sel = `RF_SEL_SP_L;
comb_bus_op = `BUS_OP_WRITE;
comb_ct_op = `CT_OP_SP_INC;
comb_temp_redir = 1'b1;
comb_next = 1'b1;
end
else if (m_cycle == 3) begin
comb_ab_src = `AB_SRC_TEMP;
comb_db_src = `DB_SRC_REG;
comb_rf_rd_sel = `RF_SEL_SP_H;
comb_bus_op = `BUS_OP_WRITE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1'b1;
end
else begin
// Default behaviour is enough.
end
end
// 8 bit reg-to-reg, mem-to-reg, or reg-to-mem LD instructions
else if (opcode[7:6] == 2'b01) begin
if (opcode[2:0] == 3'b110)
comb_alu_src_a = `ALU_SRC_A_DB; // Src A from data bus
else if (opcode[2:0] == 3'b111)
comb_alu_src_a = `ALU_SRC_A_ACC; // Src A from accumulator
else
comb_alu_src_a = `ALU_SRC_A_REG; // Src A from register file
if (opcode[5:3] == 3'b110)
comb_alu_dst = `ALU_DST_DB; // Destination is (HL)
else if (opcode[5:3] == 3'b111)
comb_alu_dst = `ALU_DST_ACC; // Destination is A
else
comb_alu_dst = `ALU_DST_REG; // Destination is register
comb_rf_wr_sel = opcode[5:3];
comb_rf_rd_sel = opcode[2:0];
if (opcode[5:3] == 3'b110) begin // Register to Memory
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_WRITE;
comb_db_src = `DB_SRC_ALU;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
else if (opcode[2:0] == 3'b110) begin // Memory to Register
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
end
// 8 bit imm-to-reg, imm-to-mem LD instructions
else if ((opcode[7:6] == 2'b00) && (opcode[2:0] == 3'b110)) begin
comb_alu_src_a = `ALU_SRC_A_DB;
if (opcode[5:3] == 3'b110) begin // imm to mem
comb_alu_dst = `ALU_DST_DB;
comb_rf_rd_sel = `RF_SEL_HL;
end
else if (opcode[5:3] == 3'b111) begin
comb_alu_dst = `ALU_DST_ACC;
end
else begin
comb_alu_dst = `ALU_DST_REG;
comb_rf_wr_sel = opcode[5:3];
end
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_next = 1;
end
else if (m_cycle == 1) begin
if (opcode[5:3] == 3'b110) begin
comb_bus_op = `BUS_OP_WRITE;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
end
// LD (BC)/(DE), A
else if ((opcode == 8'h02) || (opcode == 8'h12)) begin
comb_alu_dst = `ALU_DST_DB;
if (opcode == 8'h02)
comb_rf_rdw_sel = 2'b00; // Select BC
else
comb_rf_rdw_sel = 2'b01; // Select DE
if (m_cycle == 0) begin
comb_next = 1;
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
end
end
// LD (HL+)/(HL-), A
else if ((opcode == 8'h22) || (opcode == 8'h32)) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_dst = `ALU_DST_REG;
if (opcode == 8'h22)
comb_alu_op_src = `ALU_OP_SRC_ADD_FTOR;
else
comb_alu_op_src = `ALU_OP_SRC_SUB_ATOF;
if (m_cycle == 0) begin
// A being written to the memory, calculate L +/- 1
comb_alu_src_b = `ALU_SRC_B_ONE;
comb_rf_rd_sel = `RF_SEL_L;
comb_rf_wr_sel = `RF_SEL_L;
comb_bus_op = `BUS_OP_WRITE;
comb_db_src = `DB_SRC_ACC;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else begin
// calculate H +/- carry
comb_alu_src_b = `ALU_SRC_B_CARRY;
comb_rf_rd_sel = `RF_SEL_H;
comb_rf_wr_sel = `RF_SEL_H;
end
end
// LD A, (BC)/(DE)
else if ((opcode == 8'h0A) || (opcode == 8'h1A)) begin
comb_alu_src_a = `ALU_SRC_A_DB;
if (opcode == 8'h0A) begin
comb_rf_rdw_sel = 2'b00; // Select BC
end
else begin
comb_rf_rdw_sel = 2'b01; // Select DE
end
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// LD A, (HL+)/(HL-)
else if ((opcode == 8'h2A) || (opcode == 8'h3A)) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_src_b = `ALU_SRC_B_ONE;
if (opcode == 8'h2A)
comb_alu_op_src = `ALU_OP_SRC_ADD_FTOR;
else
comb_alu_op_src = `ALU_OP_SRC_SUB_ATOF;
comb_alu_dst = `ALU_DST_REG;
if (m_cycle == 0) begin
comb_alu_src_b = `ALU_SRC_B_ONE;
comb_rf_rd_sel = `RF_SEL_L;
comb_rf_wr_sel = `RF_SEL_L;
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_ACC;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else begin
comb_alu_src_b = `ALU_SRC_B_CARRY;
comb_rf_rd_sel = `RF_SEL_H;
comb_rf_wr_sel = `RF_SEL_H;
end
end
// 16-bit INC/DEC
else if ((opcode[7:6] == 2'b00) && (opcode[2:0] == 3'b011)) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_dst = `ALU_DST_REG;
if (opcode[3] == 1) begin
comb_alu_op_src = `ALU_OP_SRC_SUB_ATOF;
end
if (m_cycle == 0) begin
comb_alu_src_b = `ALU_SRC_B_ONE;
comb_rf_rd_sel = {opcode[5:4], 1'b1};
comb_rf_wr_sel = {opcode[5:4], 1'b1};
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else begin
comb_alu_src_b = `ALU_SRC_B_CARRY;
comb_rf_rd_sel = {opcode[5:4], 1'b0};
comb_rf_wr_sel = {opcode[5:4], 1'b0};
end
end
// 8-bit INC/DEC
else if ((opcode[7:6] == 2'b00) && (opcode[2:1] == 2'b10)) begin
comb_alu_src_b = `ALU_SRC_B_ONE;
comb_flags_pattern = `FLAGS_ZNHx;
comb_flags_we = 1'b1;
// INC or DEC
if (opcode[0])
comb_alu_op_src = `ALU_OP_SRC_SUB_ATOF;
else
comb_alu_op_src = `ALU_OP_SRC_ADD_FTOR;
if (opcode[5:3] == 3'b110) begin
// INC/DEC (HL)
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_DB;
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_REG;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else begin
// End cycle
comb_flags_we = 0;
end
end
else if (opcode[5:3] == 3'b111) begin
// INC/DEC A
comb_alu_src_a = `ALU_SRC_A_ACC;
comb_alu_dst = `ALU_DST_ACC;
end
else begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_dst = `ALU_DST_REG;
comb_rf_rd_sel = opcode[5:3];
comb_rf_wr_sel = opcode[5:3];
end
end
// ADD HL, r16
else if ((opcode[7:6] == 2'b00) && (opcode[3:0] == 4'b1001)) begin
comb_alu_dst = `ALU_DST_REG;
comb_flags_we = 1'b1;
comb_flags_pattern = `FLAGS_x0HC;
if (m_cycle == 0) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_src_b = `ALU_SRC_B_L;
comb_rf_wr_sel = `RF_SEL_L;
comb_rf_rd_sel = {opcode[5:4], 1'b1};
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_src_b = `ALU_SRC_B_H;
comb_rf_wr_sel = `RF_SEL_H;
comb_rf_rd_sel = {opcode[5:4], 1'b0};
comb_alu_op_signed = 1'b1;
end
end
// 8 bit reg-to-reg, mem-to-reg ALU operation
else if (opcode[7:6] == 2'b10) begin
comb_alu_src_b = `ALU_SRC_B_ACC;
comb_alu_op_src = `ALU_OP_SRC_INSTR_5TO3;
comb_rf_rd_sel = opcode[2:0];
comb_flags_we = 1'b1;
if ((opcode[5:4] == 2'b01) || (opcode[5:3] == 3'b111)) begin
// Sub or CP
comb_alu_src_xchg = 1'b1;
end
if (opcode[2:0] == 3'b110) begin // Source from HL
comb_alu_src_a = `ALU_SRC_A_DB;
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_ab_src = `AB_SRC_REG;
// Do not writeback in the first cycle
comb_alu_dst = `ALU_DST_DB;
comb_flags_we = 1'b0;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
else if (opcode[2:0] == 3'b111) begin // Source from A
comb_alu_src_a = `ALU_SRC_A_ACC;
end
else begin
comb_alu_src_a = `ALU_SRC_A_REG;
end
end
// 8 bit imm-to-reg ALU operation
else if ((opcode[7:6] == 2'b11) && (opcode[2:0] == 3'b110)) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_next = 1;
end
else begin
if ((opcode[5:4] == 2'b01) || (opcode[5:3] == 3'b111)) begin
// Sub or CP
comb_alu_src_xchg = 1'b1;
end
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_src_b = `ALU_SRC_B_ACC;
comb_alu_op_src = `ALU_OP_SRC_INSTR_5TO3;
comb_flags_we = 1'b1;
end
end
// 16-bit PUSH
else if ((opcode[7:6] == 2'b11) && (opcode[3:0] == 4'b0101)) begin
if (opcode[5:4] == 2'b11) begin
// AF
comb_alu_op_prefix = `ALU_OP_PREFIX_SPECIAL;
comb_db_src = `DB_SRC_ACC;
end
else begin
comb_db_src = `DB_SRC_DB;
end
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_dst = `ALU_DST_DB;
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_IDLE;
comb_ab_src = `AB_SRC_SP;
comb_ct_op = `CT_OP_SP_DEC;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_SP;
comb_ct_op = `CT_OP_SP_DEC;
comb_rf_rd_sel = {opcode[5:4], 1'b0};
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_SP;
comb_ct_op = `CT_OP_IDLE;
comb_rf_rd_sel = {opcode[5:4], 1'b1};
if (opcode[5:4] == 2'b11) begin
comb_db_src = `DB_SRC_ALU;
end
comb_next = 1;
end
end
// 16-bit POP
else if ((opcode[7:6] == 2'b11) && (opcode[3:0] == 4'b0001)) begin
if ((m_cycle == 1) || (m_cycle == 2)) begin
comb_alu_src_a = `ALU_SRC_A_DB;
if (opcode[5:4] == 2'b11) begin
comb_alu_dst = `ALU_DST_ACC;
end
else begin
comb_alu_dst = `ALU_DST_REG;
end
end
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_SP;
comb_ct_op = `CT_OP_SP_INC;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_SP;
comb_ct_op = `CT_OP_SP_INC;
comb_rf_wr_sel = {opcode[5:4], 1'b1};
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_rf_wr_sel = {opcode[5:4], 1'b0};
if (opcode[5:4] == 2'b11) begin
// Copy from memory to flags
comb_alu_op_prefix = `ALU_OP_PREFIX_SPECIAL;
comb_alu_op_src = `ALU_OP_SRC_SUB_ATOF;
comb_alu_src_b = `ALU_SRC_B_ACC;
comb_flags_we = 1'b1;
end
end
end
// LD (C), A
else if (opcode == 8'he2) begin
comb_rf_rdw_sel = 2'b00; // Select BC
comb_high_mask = 1'b1; // Select C only
comb_alu_src_a = `ALU_SRC_A_ACC;
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_WRITE;
comb_db_src = `DB_SRC_ACC;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// LD A, (C)
else if (opcode == 8'hf2) begin
comb_rf_rdw_sel = 2'b00; // Select BC
comb_high_mask = 1'b1; // Select C only
comb_alu_src_a = `ALU_SRC_A_DB;
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1'b1;
end
end
// ADD SP, r8
else if (opcode == 8'he8) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_src_b = `ALU_SRC_B_IMM;
comb_alu_dst = `ALU_DST_REG;
comb_rf_rd_sel = `RF_SEL_SP_L;
comb_rf_wr_sel = `RF_SEL_SP_L;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_flags_pattern = `FLAGS_00HC;
comb_flags_we = 1'b1;
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_src_b = `ALU_SRC_B_IMM;
comb_alu_dst = `ALU_DST_REG;
comb_alu_op_signed = 1'b1;
comb_rf_rd_sel = `RF_SEL_SP_H;
comb_rf_wr_sel = `RF_SEL_SP_H;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// LD HL, SP+r8
else if (opcode == 8'hf8) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_src_b = `ALU_SRC_B_IMM;
comb_alu_dst = `ALU_DST_REG;
comb_rf_rd_sel = `RF_SEL_SP_L;
comb_rf_wr_sel = `RF_SEL_L;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_flags_pattern = `FLAGS_00HC;
comb_flags_we = 1'b1;
comb_next = 1;
end
else begin
comb_alu_op_signed = 1'b1;
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_src_b = `ALU_SRC_B_IMM;
comb_alu_dst = `ALU_DST_REG;
comb_rf_rd_sel = `RF_SEL_SP_H;
comb_rf_wr_sel = `RF_SEL_H;
end
end
// LD SP, HL
else if (opcode == 8'hf9) begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_dst = `ALU_DST_REG;
if (m_cycle == 0) begin
comb_rf_wr_sel = `RF_SEL_SP_H;
comb_rf_rd_sel = `RF_SEL_H;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else begin
comb_rf_wr_sel = `RF_SEL_SP_L;
comb_rf_rd_sel = `RF_SEL_L;
end
end
// LDH (a8), A
else if (opcode == 8'hE0) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_alu_src_a = `ALU_SRC_A_ACC;
comb_alu_dst = `ALU_DST_DB;
comb_bus_op = `BUS_OP_WRITE;
comb_db_src = `DB_SRC_ACC;
comb_ab_src = `AB_SRC_TEMP;
comb_ct_op = `CT_OP_IDLE;
comb_high_mask = 1;
comb_next = 1;
end
end
// LDH A, (a8)
else if (opcode == 8'hF0) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_bus_op = `BUS_OP_READ;
comb_ab_src = `AB_SRC_TEMP;
comb_ct_op = `CT_OP_IDLE;
comb_high_mask = 1;
comb_next = 1;
end
else begin
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_ACC;
end
end
// LD (a16), A
else if (opcode == 8'hEA) begin
if ((m_cycle == 0) || (m_cycle == 1)) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_alu_src_a = `ALU_SRC_A_ACC;
comb_alu_dst = `ALU_DST_DB;
comb_bus_op = `BUS_OP_WRITE;
comb_db_src = `DB_SRC_ACC;
comb_ab_src = `AB_SRC_TEMP;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// LDH A, (a16)
else if (opcode == 8'hFA) begin
if ((m_cycle == 0) || (m_cycle == 1)) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_bus_op = `BUS_OP_READ;
comb_ab_src = `AB_SRC_TEMP;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else begin
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_ACC;
end
end
// JP HL
else if (opcode == 8'hE9) begin
comb_rf_rd_sel = `RF_SEL_H;
comb_ab_src = `AB_SRC_REG;
comb_pc_we = 1;
end
// JP CC, a16
else if ((opcode == 8'hC3) || (opcode == 8'hC2) || (opcode == 8'hD2)
|| (opcode == 8'hCA) || (opcode == 8'hDA)) begin
if ((m_cycle == 0) || (m_cycle == 1)) begin
// Read 16 bit imm
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 2) begin
if (((opcode == 8'hC2) && (!f_z)) || // JP NZ
((opcode == 8'hD2) && (!f_c)) || // JP NC
((opcode == 8'hC3)) || // JP
((opcode == 8'hCA) && (f_z)) || // JP Z
((opcode == 8'hDA) && (f_c))) begin // JP C
// Branch taken
comb_pc_src = `PC_SRC_TEMP;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_pc_we = 1;
comb_next = 1;
end
// Branch not taken
end
end
// CALL CC, a16
else if ((opcode == 8'hCD) || (opcode == 8'hCC) || (opcode == 8'hDC)
|| (opcode == 8'hC4) || (opcode == 8'hD4)) begin
if ((m_cycle == 0) || (m_cycle == 1)) begin
// Read 16 bit imm
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 2) begin
if (((opcode == 8'hC4) && (!f_z)) || // CALL NZ
((opcode == 8'hD4) && (!f_c)) || // CALL NC
((opcode == 8'hCD)) || // CALL
((opcode == 8'hCC) && (f_z)) || // CALL Z
((opcode == 8'hDC) && (f_c))) begin // CALL C
// Call taken
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_SP_DEC;
comb_next = 1;
end
end
else if (m_cycle == 3) begin
comb_alu_src_a = `ALU_SRC_A_PC;
comb_alu_dst = `ALU_DST_DB;
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_ct_op = `CT_OP_SP_DEC;
comb_next = 1;
end
else if (m_cycle == 4) begin
comb_alu_src_a = `ALU_SRC_A_PC;
comb_alu_dst = `ALU_DST_DB;
comb_pc_src = `PC_SRC_TEMP;
comb_pc_we = 1;
comb_bus_op = `BUS_OP_WRITE;
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// JR CC, imm8
else if ((opcode == 8'h20) || (opcode == 8'h30) || (opcode == 8'h18)
|| (opcode == 8'h28) || (opcode == 8'h38)) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 1) begin
if (((opcode == 8'h20) && (!f_z)) || // JR NZ
((opcode == 8'h30) && (!f_c)) || // JR NC
((opcode == 8'h18)) || // JR
((opcode == 8'h28) && (f_z)) || // JR Z
((opcode == 8'h38) && (f_c))) begin // JR C
comb_bus_op = `BUS_OP_IDLE;
comb_pc_jr = 1;
comb_next = 1;
end
end
end
// RET, RETI
else if ((opcode == 8'hC9) || (opcode == 8'hD9)) begin
if (m_cycle == 0) begin
if (opcode == 8'hD9) begin
ime_set = 1;
end
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_bus_op = `BUS_OP_READ;
comb_ct_op = `CT_OP_SP_INC;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_bus_op = `BUS_OP_READ;
comb_ct_op = `CT_OP_SP_INC;
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_PC;
comb_pc_b_sel = 0;
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_PC;
comb_pc_b_sel = 1;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// RET CC
else if ((opcode[7:5] == 3'b110) && (opcode[2:0] == 3'b000)) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
else if (m_cycle == 1) begin
if (((opcode == 8'hC0) && (!f_z)) || // RET NZ
((opcode == 8'hD0) && (!f_c)) || // RET NC
((opcode == 8'hC8) && (f_z)) || // RET Z
((opcode == 8'hD8) && (f_c))) begin // RET C
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_bus_op = `BUS_OP_READ;
comb_ct_op = `CT_OP_SP_INC;
comb_next = 1;
end
end
else if (m_cycle == 2) begin
comb_ab_src = `AB_SRC_SP;
comb_db_src = `DB_SRC_DB;
comb_bus_op = `BUS_OP_READ;
comb_ct_op = `CT_OP_SP_INC;
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_PC;
comb_pc_b_sel = 0;
comb_next = 1;
end
else if (m_cycle == 3) begin
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_PC;
comb_pc_b_sel = 1;
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// RST
else if ((opcode[7:6] == 2'b11) && (opcode[2:0] == 3'b111)) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_IDLE;
comb_ct_op = `CT_OP_SP_DEC;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_alu_src_a = `ALU_SRC_A_PC;
comb_alu_dst = `ALU_DST_DB;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_SP;
comb_bus_op = `BUS_OP_WRITE;
comb_ct_op = `CT_OP_SP_DEC;
comb_next = 1;
end
else if (m_cycle == 2) begin
comb_alu_src_a = `ALU_SRC_A_PC;
comb_alu_dst = `ALU_DST_DB;
comb_db_src = `DB_SRC_DB;
comb_ab_src = `AB_SRC_SP;
comb_pc_src = `PC_SRC_RST;
comb_pc_we = 1;
comb_bus_op = `BUS_OP_WRITE;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
end
// RLCA, RRCA, RLA, RRA
else if ((opcode[7:5] == 3'b000) && (opcode[2:0] == 3'b111)) begin
comb_alu_src_b = `ALU_SRC_B_ACC;
comb_alu_op_prefix = `ALU_OP_PREFIX_SHIFT_ROTATE;
comb_alu_op_src = `ALU_OP_SRC_INSTR_5TO3;
comb_flags_pattern = `FLAGS_00HC;
comb_flags_we = 1;
end
// DAA, CPL, SCF, CCF
else if ((opcode[7:5] == 3'b001) && (opcode[2:0] == 3'b111)) begin
comb_alu_src_b = `ALU_SRC_B_ACC;
comb_alu_op_prefix = `ALU_OP_PREFIX_SPECIAL;
comb_alu_op_src = `ALU_OP_SRC_INSTR_5TO3;
comb_flags_we = 1;
end
// CB prefix
else if (opcode == 8'hCB) begin
if (m_cycle == 0) begin
comb_bus_op = `BUS_OP_READ;
comb_db_src = `DB_SRC_DB;
comb_next = 1;
end
else if (m_cycle == 1) begin
comb_opcode_redir = 1'b1;
if (cb[2:0] == 3'b110) begin
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_DB;
comb_ct_op = `CT_OP_IDLE;
comb_ab_src = `AB_SRC_REG;
comb_bus_op = `BUS_OP_READ;
comb_flags_we = 0;
comb_next = 1;
end
else if (cb[2:0] == 3'b111) begin
comb_alu_src_a = `ALU_SRC_A_ACC;
comb_alu_dst = `ALU_DST_ACC;
comb_flags_we = !cb[7];
end
else begin
comb_alu_src_a = `ALU_SRC_A_REG;
comb_alu_dst = `ALU_DST_REG;
comb_rf_rd_sel = cb[2:0];
comb_rf_wr_sel = cb[2:0];
comb_flags_we = !cb[7];
end
if (cb[7:6] == 2'b00) begin
comb_alu_op_prefix = `ALU_OP_PREFIX_SHIFT_ROTATE;
comb_alu_op_src = `ALU_OP_SRC_INSTR_5TO3;
end
else begin
comb_alu_op_prefix = `ALU_OP_PREFIX_CB;
comb_alu_op_src = `ALU_OP_SRC_INSTR_7TO6;
end
if (cb[7:6] == 2'b01) begin
// Only affects flags
comb_alu_dst = `ALU_DST_DB;
end
end
else if (m_cycle == 2) begin
comb_opcode_redir = 1'b1;
comb_alu_src_a = `ALU_SRC_A_DB;
comb_alu_dst = `ALU_DST_DB;
if (cb[7:6] == 2'b00) begin
comb_alu_op_prefix = `ALU_OP_PREFIX_SHIFT_ROTATE;
comb_alu_op_src = `ALU_OP_SRC_INSTR_5TO3;
end
else begin
comb_alu_op_prefix = `ALU_OP_PREFIX_CB;
comb_alu_op_src = `ALU_OP_SRC_INSTR_7TO6;
end
if (cb[7:6] != 2'b01) begin
// Write-back cycle required.
comb_bus_op = `BUS_OP_WRITE;
comb_db_src = `DB_SRC_ALU;
comb_ab_src = `AB_SRC_REG;
comb_ct_op = `CT_OP_IDLE;
comb_next = 1;
end
comb_flags_we = !cb[7];
end
end
end
end
always @(posedge clk) begin
if ((ct_state == 2'd3) || (rst == 1'b1)) begin
alu_src_a <= comb_alu_src_a;
alu_src_b <= comb_alu_src_b;
alu_src_xchg <= comb_alu_src_xchg;
alu_op_prefix <= comb_alu_op_prefix;
alu_op_src <= comb_alu_op_src;
alu_op_signed <= comb_alu_op_signed;
alu_dst <= comb_alu_dst;
pc_src <= comb_pc_src;
pc_we <= comb_pc_we;
pc_b_sel <= comb_pc_b_sel;
pc_jr <= comb_pc_jr;
pc_revert <= comb_pc_revert;
rf_wr_sel <= comb_rf_wr_sel;
rf_rd_sel <= comb_rf_rd_sel;
rf_rdw_sel <= comb_rf_rdw_sel;
temp_redir <= comb_temp_redir;
opcode_redir <= comb_opcode_redir;
bus_op <= comb_bus_op;
db_src <= comb_db_src;
ab_src <= comb_ab_src;
ct_op <= comb_ct_op;
flags_we <= comb_flags_we;
flags_pattern <= comb_flags_pattern;
high_mask <= comb_high_mask;
int_ack <= comb_int_ack;
next <= comb_next;
stop <= comb_stop;
halt <= comb_halt;
fault <= comb_fault;
end
end
endmodule