| `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 |