/* This is Z65C02 (full), by John Kortink (to contact me, visit www.zeridajh.org). Z65C02 is a 65C02 core that drastically reduces instruction cycle counts, mainly by exploiting simultaneous memory reads (on copies of main memory). You may only copy this source if it remains completely unchanged (including these comments). You may only change this source to serve your own private experiments. If you want to use any part of this source, changed or unchanged, in some distributed, bigger thing, you must contact me for permission to do so. Note that this source comes straight from my 'soft' Acorn 6502 Second Processor implementation(s). It contains a few tweaks geared towards that environment. This core runs at up to 200 MHz on a high end Altera Stratix V (roughly equivalent to a real 65C02 running at 400 MHz). The 'full' version offers the best performance, but requires the most memory. The 'small' and 'tiny' increase cycle counts, but require less memory. Some notes about less obvious interface parts (see module z65c02 below) : - All memory blocks (see variables starting 'peek') have to be connected. It is recommended to use dual ported memory to combine blocks. - All memory blocks are written to simultaneously (see additional variables starting 'write'). - The 'unique' interface allows memory mapped hardware access (only by LDA/X/Y, STA/X/Y/Z and BIT, with ABS+0 addressing). Asynchronously flag via test_confirm that test_address points to memory mapped hardware. - The 'protect' interface allows code and vector memory to be (partially) write protected. Asynchronously flag via code_confirm that code_address points to memory that should not be written to (also involving program_counter, if required). In all cases, peruse the logic below if you need more details. History ------- 14-Jan-2022 : First public release (of the 17-Oct-2020 version). 22-Feb-2022 : Second release. Fixed SBC V flag bug. Corrected NOP 'sizes'. */ `default_nettype none `define INDEX_FC 3'h0 `define INDEX_FZ 3'h1 `define INDEX_FI 3'h2 `define INDEX_FD 3'h3 `define INDEX_FV 3'h6 `define INDEX_FN 3'h7 `define C_FLAG status_flags[`INDEX_FC] `define Z_FLAG status_flags[`INDEX_FZ] `define I_FLAG status_flags[`INDEX_FI] `define D_FLAG status_flags[`INDEX_FD] `define V_FLAG status_flags[`INDEX_FV] `define N_FLAG status_flags[`INDEX_FN] `define INDEX_RA 2'h0 `define INDEX_RX 2'h1 `define INDEX_RY 2'h2 `define INDEX_RZ 2'h3 `define A_REGISTER register_set[`INDEX_RA] `define X_REGISTER register_set[`INDEX_RX] `define Y_REGISTER register_set[`INDEX_RY] `define Z_REGISTER register_set[`INDEX_RZ] `define S_REGISTER stack_pointer `define P_REGISTER { `N_FLAG, `V_FLAG, 1'bx, 1'bx, `D_FLAG, `I_FLAG, `Z_FLAG, `C_FLAG } `define P_WITH_B_0 { `N_FLAG, `V_FLAG, 1'bx, 1'b0, `D_FLAG, `I_FLAG, `Z_FLAG, `C_FLAG } `define P_WITH_B_1 { `N_FLAG, `V_FLAG, 1'bx, 1'b1, `D_FLAG, `I_FLAG, `Z_FLAG, `C_FLAG } `define P_NZCVID_OF(byte) { byte[`INDEX_FN], byte[`INDEX_FZ], byte[`INDEX_FC], byte[`INDEX_FV], byte[`INDEX_FI], byte[`INDEX_FD] } `define EXCEPTION_NMI 2'b01 `define EXCEPTION_RST 2'b10 `define EXCEPTION_IRQ 2'b11 `define OPERAND_IMM 4'h0 `define OPERAND_AB0 4'h1 `define OPERAND_ABX 4'h2 `define OPERAND_ABY 4'h3 `define OPERAND_ZP0 4'h4 `define OPERAND_ZPX 4'h5 `define OPERAND_ZPY 4'h6 `define OPERAND_IN0 4'h7 `define OPERAND_INX 4'h8 `define OPERAND_INY 4'h9 `define OPERAND_ST1 4'hA `define OPERAND_UNQ 4'hB `define NOW_FIRST 3'h0 `define NOW_AWAIT 3'h1 `define NOW_DELAY 3'h2 `define NOW_FINAL 3'h3 `define NOW_JSR_PUSHH 3'h4 `define NOW_EXC_PUSHH 3'h5 `define NOW_EXC_PUSHL 3'h6 `define NEXT_ADC_DONE { 3'b100, `NOW_FIRST } `define NEXT_LDA_DONE { 3'b100, `NOW_FIRST } `define NEXT_STA_DONE { 3'b000, `NOW_FIRST } `define NEXT_BCC_DONE { 3'b000, `NOW_FIRST } `define NEXT_CLC_DONE { 3'b100, `NOW_FIRST } `define NEXT_CPX_DONE { 3'b100, `NOW_FIRST } `define NEXT_SIA_DONE { 3'b100, `NOW_FIRST } `define NEXT_TAX_DONE { 3'b100, `NOW_FIRST } `define NEXT_BIT_DONE { 3'b100, `NOW_FIRST } `define NEXT_DEX_DONE { 3'b100, `NOW_FIRST } `define NEXT_OTHER_DONE { 3'b000, `NOW_FIRST } `define NEXT_ADC_WAIT_1 { 3'b000, `NOW_AWAIT } `define NEXT_LDA_WAIT_1 { 3'b000, `NOW_AWAIT } `define NEXT_LDA_UNIQUE { 3'b001, `NOW_AWAIT } `define NEXT_STA_UNIQUE { 3'b001, `NOW_AWAIT } `define NEXT_BIT_UNIQUE { 3'b001, `NOW_AWAIT } `define NEXT_BCC_RESOLVE { 3'b000, `NOW_DELAY } `define NEXT_JMP_RESOLVE { 3'b000, `NOW_DELAY } `define NEXT_RTI_RESOLVE { 3'b100, `NOW_DELAY } `define NEXT_RTS_RESOLVE { 3'b000, `NOW_DELAY } `define NEXT_JSR_PUSHH { 3'b010, `NOW_JSR_PUSHH } `define NEXT_EXC_PUSHH { 3'b110, `NOW_EXC_PUSHH } `define NEXT_EXC_PUSHL { 3'b010, `NOW_EXC_PUSHL } `define NEXT_SIM_WRITE { 3'b110, `NOW_FINAL } `define NEXT_STA_WRITE { 3'b010, `NOW_FINAL } `define NEXT_TRB_WRITE { 3'b110, `NOW_FINAL } `define NEXT_PHA_WRITE { 3'b010, `NOW_FINAL } `define NEXT_PLA_STACK { 3'b100, `NOW_FINAL } `define NEXT_TXS_STACK { 3'b000, `NOW_FINAL } `define NEXT_PLP_STACK { 3'b100, `NOW_FINAL } `define NEXT_JSR_PUSHL { 3'b010, `NOW_FINAL } `define NEXT_EXC_PUSHP { 3'b010, `NOW_FINAL } `define NEXT_ANY_DELAYED { 3'b000, `NOW_FINAL } `define GROUP_ADC 8'b0xxx_xx01, 8'b0xx1_0010, 8'b11xx_xx01, 8'b11x1_0010 /* ADC AND CMP EOR ORA SBC */ `define GROUP_SIM 8'b0xxx_x110, 8'b11xx_x110 /* ASL DEC INC LSR ROL ROR (not A) */ `define GROUP_LDA 8'b1010_0000, 8'b101x_xx01, 8'b101x_0010, 8'b101x_x1x0 /* LDA LDX LDY */ `define GROUP_STA 8'b011x_0100, 8'b100x_0001, 8'b100x_x10x, 8'b100x_x110, 8'b1001_0010, 8'b1001_1001 /* STA STX STY STZ */ `define GROUP_BCC 8'bxxx1_0000, 8'b1000_0000 /* BCC BCS BEQ BMI BNE BPL BRA BVC BVS */ `define GROUP_CLC 8'b0xx1_1000, 8'b1011_1000, 8'b11x1_1000 /* CLC CLD CLI CLV SEC SED SEI */ `define GROUP_CPX 8'b11x0_0x00, 8'b11x0_1100 /* CPX CPY */ `define GROUP_SIA 8'b00xx_1010, 8'b01x0_1010 /* ASL DEC INC LSR ROL ROR (A) */ `define GROUP_TAX 8'b1000_1010, 8'b1001_1000, 8'b1010_10x0, 8'b1011_1010 /* TAX TAY TSX TXA TYA */ `define GROUP_BIT 8'b001x_x100, 8'b1000_1001 /* BIT */ `define GROUP_TRB 8'b000x_x100 /* TRB TSB */ `define GROUP_DEX 8'b1000_1000, 8'b1100_10x0, 8'b1110_1000 /* DEX DEY INX INY */ `define GROUP_PHA 8'b0x00_1000, 8'bx101_1010 /* PHA PHP PHX PHY */ `define GROUP_PLA 8'b0110_1000, 8'bx111_1010 /* PLA PLX PLY */ `define GROUP_JMP 8'b0100_1100, 8'b011x_1100 /* JMP */ `define GROUP_RTI 8'b0100_0000 /* RTI */ `define GROUP_RTS 8'b0110_0000 /* RTS */ `define GROUP_TXS 8'b1001_1010 /* TXS */ `define GROUP_PLP 8'b0010_1000 /* PLP */ `define GROUP_JSR 8'b0010_0000 /* JSR */ `define GROUP_BRK 8'b0000_0000 /* BRK */ `define INST_BRK_IMP 8'b0000_0000 `define INST_LDX_IMM 8'b1010_0010 `define INST_LDX_ZP0 8'b1010_0110 `define INST_LDX_AB0 8'b1010_1110 `define INST_LDX_ZPY 8'b1011_0110 `define INST_LDX_ABY 8'b1011_1110 `define INST_TAX_IMP 8'b1010_1010 `define INST_TSX_IMP 8'b1011_1010 `define INST_DEX_IMP 8'b1100_1010 `define INST_INX_IMP 8'b1110_1000 `define INST_PLX_IMP 8'b1111_1010 `define INST_LDY_IMM 8'b1010_0000 `define INST_LDY_ZP0 8'b1010_0100 `define INST_LDY_AB0 8'b1010_1100 `define INST_LDY_ZPY 8'b1011_0100 `define INST_LDY_ABY 8'b1011_1100 `define INST_TAY_IMP 8'b1010_1000 `define INST_DEY_IMP 8'b1000_1000 `define INST_INY_IMP 8'b1100_1000 `define INST_PLY_IMP 8'b0111_1010 module BCD_ADD ( a_in, data_in, carry_in, fixup_in, sbc_else_adc, data_out, carry_out, fixup_out ); input [3:0] a_in; input [3:0] data_in; input carry_in; input fixup_in; input sbc_else_adc; output [3:0] data_out; output carry_out; output fixup_out; wire [3:0] adder_data; wire [4:0] adder_result; wire adder_carry; wire digit_not_bcd; reg [3:0] digit_add_data; reg digit_add_carry; wire [4:0] digit_result; assign adder_data = sbc_else_adc ? ~data_in : data_in; assign adder_result = a_in + adder_data + carry_in; assign adder_carry = adder_result[4]; assign digit_not_bcd = (adder_result[3:0] > 4'd9); always @(*) casex ({ sbc_else_adc, adder_carry, digit_not_bcd }) 3'b000 : { digit_add_data, digit_add_carry } = { 4'h0, 1'b0 }; 3'b0xx : { digit_add_data, digit_add_carry } = { 4'h6, 1'b0 }; 3'b10x : { digit_add_data, digit_add_carry } = { 4'h9, fixup_in }; 3'b11x : { digit_add_data, digit_add_carry } = { 4'hF, fixup_in }; endcase assign digit_result = adder_result[3:0] + digit_add_data + digit_add_carry; assign data_out = digit_result[3:0]; assign carry_out = adder_carry | (~sbc_else_adc & digit_not_bcd); assign fixup_out = digit_result[4]; endmodule module ALU_ADC ( a_in, data_in, carry_in, bcd_else_bin, operation, data_out, flag_nzcv ); input [7:0] a_in; input [7:0] data_in; input carry_in; input bcd_else_bin; input [2:0] operation; output reg [7:0] data_out; output [3:0] flag_nzcv; wire [7:0] b_adder_data; wire [8:0] b_adder_result; wire b_adder_overflow; wire [8:0] d_adder_result; wire d_nibble_carry; wire d_nibble_fixup; wire d_adder_overflow; reg carry_out; reg overflow_out; assign b_adder_data = operation[2] ? ~data_in : data_in; assign b_adder_result = a_in + b_adder_data + (operation[0] ? carry_in : 1'b1); assign b_adder_overflow = (a_in[7] ^ b_adder_result[7]) & (b_adder_data[7] ^ b_adder_result[7]); BCD_ADD nibble_0 ( .a_in(a_in[3:0]), .data_in(data_in[3:0]), .carry_in(carry_in), .fixup_in(1'b1), .sbc_else_adc(operation[2]), .data_out(d_adder_result[3:0]), .carry_out(d_nibble_carry), .fixup_out(d_nibble_fixup) ); BCD_ADD nibble_1 ( .a_in(a_in[7:4]), .data_in(data_in[7:4]), .carry_in(d_nibble_carry), .fixup_in(d_nibble_fixup), .sbc_else_adc(operation[2]), .data_out(d_adder_result[7:4]), .carry_out(d_adder_result[8]), .fixup_out() ); assign d_adder_overflow = (a_in[7] ^ d_adder_result[7]) & (data_in[7] ^ d_adder_result[7]); always @(*) casex ({ operation, bcd_else_bin }) 4'b000x : begin carry_out = 1'bx ; overflow_out = 1'bx ; data_out = a_in | data_in ; end // ORA 4'b001x : begin carry_out = 1'bx ; overflow_out = 1'bx ; data_out = a_in & data_in ; end // AND 4'b010x : begin carry_out = 1'bx ; overflow_out = 1'bx ; data_out = a_in ^ data_in ; end // EOR 4'b0110 : begin carry_out = b_adder_result[8]; overflow_out = b_adder_overflow; data_out = b_adder_result[7:0]; end // ADC (bin) 4'b0111 : begin carry_out = d_adder_result[8]; overflow_out = d_adder_overflow; data_out = d_adder_result[7:0]; end // ADC (bcd) 4'b110x : begin carry_out = b_adder_result[8]; overflow_out = 1'bx ; data_out = b_adder_result[7:0]; end // CMP 4'b1110 : begin carry_out = b_adder_result[8]; overflow_out = b_adder_overflow; data_out = b_adder_result[7:0]; end // SBC (bin) 4'b1111 : begin carry_out = d_adder_result[8]; overflow_out = d_adder_overflow; data_out = d_adder_result[7:0]; end // SBC (bcd) default : begin carry_out = 1'bx ; overflow_out = 1'bx ; data_out = 8'bxxxxxxxx ; end // --- endcase assign flag_nzcv = { data_out[7], data_out == 8'h00, carry_out, overflow_out }; endmodule module ALU_SIM ( data_in, carry_in, operation, data_out, flag_nzcv ); input [7:0] data_in; input carry_in; input [2:0] operation; output reg [7:0] data_out; output [3:0] flag_nzcv; reg carry_out; always @(*) case (operation) 3'b000 : begin { carry_out, data_out } = { data_in , 1'b0 }; end // ASL 3'b001 : begin { carry_out, data_out } = { data_in , carry_in }; end // ROL 3'b010 : begin { data_out, carry_out } = { 1'b0 , data_in }; end // LSR 3'b011 : begin { data_out, carry_out } = { carry_in, data_in }; end // ROR 3'b110 : begin carry_out = 1'bx; data_out = data_in - 1; end // DEC 3'b111 : begin carry_out = 1'bx; data_out = data_in + 1; end // INC default : begin carry_out = 1'bx; data_out = 8'bxxxxxxxx; end // --- endcase assign flag_nzcv = { data_out[7], data_out == 8'h00, carry_out, 1'bx }; endmodule module ALU_DNZ ( data_in, flag_nzcv ); input [7:0] data_in; output [3:0] flag_nzcv; assign flag_nzcv = { data_in[7], data_in == 8'h00, 1'bx, 1'bx }; endmodule module ALU_CPX ( xy_in, data_in, flag_nzcv ); input [7:0] xy_in; input [7:0] data_in; output [3:0] flag_nzcv; wire [8:0] cmp_result; assign cmp_result = xy_in + ~data_in + 1'b1; assign flag_nzcv = { cmp_result[7], cmp_result[7:0] == 8'h00, ~cmp_result[8], 1'bx }; endmodule module ALU_SIA ( a_in, carry_in, operation, data_out, flag_nzcv ); input [7:0] a_in; input carry_in; input [2:0] operation; output reg [7:0] data_out; output [3:0] flag_nzcv; reg carry_out; always @(*) case (operation) 3'b000 : begin { carry_out, data_out } = { a_in , 1'b0 }; end // ASL 3'b010 : begin { carry_out, data_out } = { a_in , carry_in }; end // ROL 3'b100 : begin { data_out, carry_out } = { 1'b0 , a_in }; end // LSR 3'b110 : begin { data_out, carry_out } = { carry_in, a_in }; end // ROR 3'b011 : begin carry_out = 1'bx; data_out = a_in - 1 ; end // DEC 3'b001 : begin carry_out = 1'bx; data_out = a_in + 1 ; end // INC default : begin carry_out = 1'bx; data_out = 8'bxxxxxxxx; end // --- endcase assign flag_nzcv = { data_out[7], data_out == 8'h00, carry_out, 1'bx }; endmodule module ALU_BIT ( a_in, data_in, flag_nzcv ); input [7:0] a_in; input [7:0] data_in; output [3:0] flag_nzcv; assign flag_nzcv = { data_in[7], (a_in & data_in) == 8'h00, 1'bx, data_in[6] }; endmodule module ALU_TRB ( a_in, data_in, operation, data_out, flag_nzcv ); input [7:0] a_in; input [7:0] data_in; input operation; output [7:0] data_out; output [3:0] flag_nzcv; assign data_out = operation ? (~a_in & data_in) /* TRB */ : (a_in | data_in) /* TSB */; assign flag_nzcv = { 1'bx, ((a_in & data_in) == 8'h00), 1'bx, 1'bx }; endmodule module ALU_DEX ( xy_in, operation, data_out, flag_nzcv ); input [7:0] xy_in; input operation; output [7:0] data_out; output [3:0] flag_nzcv; assign data_out = operation ? (xy_in + 1) /* INC */ : (xy_in - 1) /* DEC */; assign flag_nzcv = { data_out[7], data_out == 8'h00, 1'bx, 1'bx }; endmodule module z65c02 ( cpu_clock, cpu_rst, cpu_nmi, cpu_irq, ram_clock, peek_16x24_address_1, peek_16x24_enable_1, peek_16x24_write_1, peek_16x24_data_1, peek_16x16_address_1, peek_16x16_enable_1, peek_16x16_write_1, peek_16x16_data_1, peek_16x16_address_2, peek_16x16_enable_2, peek_16x16_write_2, peek_16x16_data_2, peek_16x8_address_1, peek_16x8_enable_1, peek_16x8_write_1, peek_16x8_data_1, peek_16x8_address_2, peek_16x8_enable_2, peek_16x8_write_2, peek_16x8_data_2, peek_16x8_address_3, peek_16x8_enable_3, peek_16x8_write_3, peek_16x8_data_3, peek_16x8_address_4, peek_16x8_enable_4, peek_16x8_write_4, peek_16x8_data_4, peek_8x24_address_1, peek_8x24_enable_1, peek_8x24_write_1, peek_8x24_data_1, peek_8x16_address_1, peek_8x16_enable_1, peek_8x16_write_1, peek_8x16_data_1, peek_8x16_address_2, peek_8x16_enable_2, peek_8x16_write_2, peek_8x16_data_2, peek_8x8_address_1, peek_8x8_enable_1, peek_8x8_write_1, peek_8x8_data_1, peek_3x16_address_1, peek_3x16_enable_1, peek_3x16_write_1, peek_3x16_data_1, write_16x8_address, write_16x8_data, unique_test_address, unique_test_confirm, unique_select, unique_read_not_write, unique_address, unique_read_data, unique_write_data, protect_program_counter, protect_code_address, protect_code_confirm, debug_out ); input cpu_clock; input cpu_rst; input cpu_nmi; input cpu_irq; output ram_clock; output [15:0] peek_16x24_address_1; output peek_16x24_enable_1; output peek_16x24_write_1; input [23:0] peek_16x24_data_1; output [15:0] peek_16x16_address_1; output peek_16x16_enable_1; output peek_16x16_write_1; input [15:0] peek_16x16_data_1; output [15:0] peek_16x16_address_2; output peek_16x16_enable_2; output peek_16x16_write_2; input [15:0] peek_16x16_data_2; output [15:0] peek_16x8_address_1; output peek_16x8_enable_1; output peek_16x8_write_1; input [ 7:0] peek_16x8_data_1; output [15:0] peek_16x8_address_2; output peek_16x8_enable_2; output peek_16x8_write_2; input [ 7:0] peek_16x8_data_2; output [15:0] peek_16x8_address_3; output peek_16x8_enable_3; output peek_16x8_write_3; input [ 7:0] peek_16x8_data_3; output [15:0] peek_16x8_address_4; output peek_16x8_enable_4; output peek_16x8_write_4; input [ 7:0] peek_16x8_data_4; output [ 7:0] peek_8x24_address_1; output peek_8x24_enable_1; output peek_8x24_write_1; input [23:0] peek_8x24_data_1; output [ 7:0] peek_8x16_address_1; output peek_8x16_enable_1; output peek_8x16_write_1; input [15:0] peek_8x16_data_1; output [ 7:0] peek_8x16_address_2; output peek_8x16_enable_2; output peek_8x16_write_2; input [15:0] peek_8x16_data_2; output [ 7:0] peek_8x8_address_1; output peek_8x8_enable_1; output peek_8x8_write_1; input [ 7:0] peek_8x8_data_1; output [ 2:0] peek_3x16_address_1; output peek_3x16_enable_1; output peek_3x16_write_1; input [15:0] peek_3x16_data_1; output [15:0] write_16x8_address; output [ 7:0] write_16x8_data; output [15:0] unique_test_address; input unique_test_confirm; output unique_select; output unique_read_not_write; output [15:0] unique_address; input [ 7:0] unique_read_data; output [ 7:0] unique_write_data; output [15:0] protect_program_counter; output [15:0] protect_code_address; input protect_code_confirm; output [99:0] debug_out; // // 65C02 // assign ram_clock = cpu_clock; // // Exceptions // reg [1:0] nmi_detect; reg active_rst; reg active_nmi; reg active_irq; reg exception_active; reg [1:0] exception_source; always @(posedge cpu_clock) begin nmi_detect <= { nmi_detect[0], cpu_nmi }; active_rst <= cpu_rst; if (nmi_detect == 2'b01) active_nmi <= 1'b1; else if (now_state == `NOW_EXC_PUSHH) active_nmi <= 1'b0; active_irq <= ~`I_FLAG & cpu_irq; exception_active <= active_rst | active_nmi | active_irq; if (active_rst) exception_source <= `EXCEPTION_RST; else if (active_nmi) exception_source <= `EXCEPTION_NMI; else if (active_irq) exception_source <= `EXCEPTION_IRQ; end // // Code read // wire code_write_enable; wire [23:0] code_read_data; assign protect_program_counter = program_counter; assign protect_code_address = data_write_address; assign code_write_enable = data_write_enable & ~protect_code_confirm; assign peek_16x24_address_1 = next_is_first ? next_program_counter : program_counter; assign peek_16x24_enable_1 = 1'b1; assign peek_16x24_write_1 = code_write_enable; assign code_read_data = peek_16x24_data_1; // // Data peek // wire zpg_read_enable; wire abs_read_enable; wire ind_read_enable; wire stk_read_enable; wire vec_read_enable; wire [15:0] abs_0_read_data; wire [15:0] abs_x_read_data; wire [ 7:0] abs_y_read_data; wire [15:0] zpg_0_read_data; wire [15:0] zpg_x_read_data; wire [ 7:0] zpg_y_read_data; wire [ 7:0] ind_0_read_data; wire [ 7:0] ind_x_read_data; wire [ 7:0] ind_y_read_data; wire [23:0] stk_1_read_data; wire [15:0] vector_read_data; wire [15:0] operand_address[11:0]; wire [ 7:0] operand_data[11:0]; assign zpg_read_enable = next_is_first; assign abs_read_enable = next_is_first; assign ind_read_enable = now_is_first; assign stk_read_enable = next_is_first; assign vec_read_enable = 1'b1; assign operand_address[`OPERAND_AB0] = instruction_arg_abs; assign operand_address[`OPERAND_ABX] = instruction_arg_abs + `X_REGISTER; assign operand_address[`OPERAND_ABY] = instruction_arg_abs + `Y_REGISTER; assign operand_address[`OPERAND_ZP0] = { 8'h00, instruction_arg_zpg }; assign operand_address[`OPERAND_ZPX] = { 8'h00, instruction_arg_zpg + `X_REGISTER }; assign operand_address[`OPERAND_ZPY] = { 8'h00, instruction_arg_zpg + `Y_REGISTER }; assign operand_address[`OPERAND_IN0] = zpg_0_read_data[15:0]; assign operand_address[`OPERAND_INX] = zpg_x_read_data[15:0]; assign operand_address[`OPERAND_INY] = zpg_0_read_data[15:0] + `Y_REGISTER; assign operand_address[`OPERAND_ST1] = { 8'h00, `S_REGISTER + 1 }; assign operand_data[`OPERAND_IMM] = instruction_arg_imm; assign operand_data[`OPERAND_AB0] = abs_0_read_data[7:0]; assign operand_data[`OPERAND_ABX] = abs_x_read_data[7:0]; assign operand_data[`OPERAND_ABY] = abs_y_read_data[7:0]; assign operand_data[`OPERAND_ZP0] = zpg_0_read_data[7:0]; assign operand_data[`OPERAND_ZPX] = zpg_x_read_data[7:0]; assign operand_data[`OPERAND_ZPY] = zpg_y_read_data[7:0]; assign operand_data[`OPERAND_IN0] = ind_0_read_data[7:0]; assign operand_data[`OPERAND_INX] = ind_x_read_data[7:0]; assign operand_data[`OPERAND_INY] = ind_y_read_data[7:0]; assign operand_data[`OPERAND_ST1] = stk_1_read_data[7:0]; assign operand_data[`OPERAND_UNQ] = unique_read_data; // ABS assign peek_16x16_address_1 = incoming_arg_abs; assign peek_16x16_enable_1 = abs_read_enable; assign peek_16x16_write_1 = data_write_enable; assign abs_0_read_data = peek_16x16_data_1; // ABS,X assign peek_16x16_address_2 = incoming_arg_abs + early_x_register; assign peek_16x16_enable_2 = abs_read_enable; assign peek_16x16_write_2 = data_write_enable; assign abs_x_read_data = peek_16x16_data_2; // ABS,Y assign peek_16x8_address_1 = incoming_arg_abs + early_y_register; assign peek_16x8_enable_1 = abs_read_enable; assign peek_16x8_write_1 = data_write_enable; assign abs_y_read_data = peek_16x8_data_1; // ZPG assign peek_8x16_address_1 = incoming_arg_zpg; assign peek_8x16_enable_1 = zpg_read_enable; assign peek_8x16_write_1 = data_write_enable & (data_write_address[15:8] == 8'h00); assign zpg_0_read_data = peek_8x16_data_1; // ZPG,X assign peek_8x16_address_2 = incoming_arg_zpg + early_x_register; assign peek_8x16_enable_2 = zpg_read_enable; assign peek_8x16_write_2 = data_write_enable & (data_write_address[15:8] == 8'h00); assign zpg_x_read_data = peek_8x16_data_2; // ZPG,Y assign peek_8x8_address_1 = incoming_arg_zpg + early_y_register; assign peek_8x8_enable_1 = zpg_read_enable; assign peek_8x8_write_1 = data_write_enable & (data_write_address[15:8] == 8'h00); assign zpg_y_read_data = peek_8x8_data_1; // (IND) assign peek_16x8_address_2 = zpg_0_read_data[15:0]; assign peek_16x8_enable_2 = ind_read_enable; assign peek_16x8_write_2 = data_write_enable; assign ind_0_read_data = peek_16x8_data_2; // (IND,X) assign peek_16x8_address_3 = zpg_x_read_data[15:0]; assign peek_16x8_enable_3 = ind_read_enable; assign peek_16x8_write_3 = data_write_enable; assign ind_x_read_data = peek_16x8_data_3; // (IND),Y assign peek_16x8_address_4 = zpg_0_read_data[15:0] + `Y_REGISTER; assign peek_16x8_enable_4 = ind_read_enable; assign peek_16x8_write_4 = data_write_enable; assign ind_y_read_data = peek_16x8_data_4; // STK,1 assign peek_8x24_address_1 = `S_REGISTER + 1; assign peek_8x24_enable_1 = stk_read_enable; assign peek_8x24_write_1 = data_write_enable & (data_write_address[15:8] == 8'h01); assign stk_1_read_data = peek_8x24_data_1; // Vector assign peek_3x16_address_1 = { exception_active ? exception_source : `EXCEPTION_IRQ, 1'b0 }; assign peek_3x16_enable_1 = vec_read_enable; assign peek_3x16_write_1 = code_write_enable & (data_write_address[15:3] == 13'b1111111111111); assign vector_read_data = peek_3x16_data_1; // // Data write // reg [15:0] data_write_address; wire data_write_enable; reg [ 7:0] data_write_data; assign write_16x8_address = data_write_address; assign write_16x8_data = data_write_data; // // Unique read / write // wire abs_0_is_unique; reg unique_select; reg unique_read_not_write; reg [15:0] unique_address; reg [ 7:0] unique_write_data; assign unique_test_address = operand_address[`OPERAND_AB0]; assign abs_0_is_unique = unique_test_confirm; always @(posedge cpu_clock) if (next_is_unique) begin unique_select <= 1'b1; unique_read_not_write <= instruction_opcode[5]; unique_address <= operand_address[`OPERAND_AB0]; unique_write_data <= register_set[sta_register]; end else unique_select <= 1'b0; // // ADC group // reg [3:0] adc_timing; reg [7:0] adc_data_in; reg [2:0] adc_mask_axy; reg [3:0] adc_mask_nzcv; wire [7:0] adc_data_out; wire [3:0] adc_flag_nzcv; always @(*) case (instruction_opcode[4:2]) 3'b000 : { adc_timing, adc_data_in } = { 4'b1000, operand_data[`OPERAND_INX] }; 3'b001 : { adc_timing, adc_data_in } = { 4'b0010, operand_data[`OPERAND_ZP0] }; 3'b010 : { adc_timing, adc_data_in } = { 4'b0001, operand_data[`OPERAND_IMM] }; 3'b011 : { adc_timing, adc_data_in } = { 4'b0100, operand_data[`OPERAND_AB0] }; 3'b100 : { adc_timing, adc_data_in } = { 4'b1000, instruction_opcode[0] ? operand_data[`OPERAND_INY] : operand_data[`OPERAND_IN0] }; 3'b101 : { adc_timing, adc_data_in } = { 4'b0010, operand_data[`OPERAND_ZPX] }; 3'b110 : { adc_timing, adc_data_in } = { 4'b0100, operand_data[`OPERAND_ABY] }; 3'b111 : { adc_timing, adc_data_in } = { 4'b0100, operand_data[`OPERAND_ABX] }; endcase always @(*) case (instruction_opcode[7:5]) 3'b000 : { adc_mask_axy, adc_mask_nzcv } = { 3'b100, 4'b1100 }; 3'b001 : { adc_mask_axy, adc_mask_nzcv } = { 3'b100, 4'b1100 }; 3'b010 : { adc_mask_axy, adc_mask_nzcv } = { 3'b100, 4'b1100 }; 3'b011 : { adc_mask_axy, adc_mask_nzcv } = { 3'b100, 4'b1111 }; 3'b110 : { adc_mask_axy, adc_mask_nzcv } = { 3'b000, 4'b1110 }; 3'b111 : { adc_mask_axy, adc_mask_nzcv } = { 3'b100, 4'b1111 }; default : { adc_mask_axy, adc_mask_nzcv } = { 3'bxxx, 4'bxxxx }; endcase ALU_ADC adc_alu ( .a_in(`A_REGISTER), .data_in(adc_data_in), .carry_in(`C_FLAG), .bcd_else_bin(`D_FLAG), .operation(instruction_opcode[7:5]), .data_out(adc_data_out), .flag_nzcv(adc_flag_nzcv) ); // // SIM group // reg [ 3:0] sim_timing; reg [15:0] sim_target; reg [ 7:0] sim_data_in; reg [ 3:0] sim_mask_nzcv; wire [ 7:0] sim_data_out; wire [ 3:0] sim_flag_nzcv; always @(*) case (instruction_opcode[4:3]) 2'b00 : { sim_timing, sim_target, sim_data_in } = { 4'b0010, operand_address[`OPERAND_ZP0], operand_data[`OPERAND_ZP0] }; 2'b01 : { sim_timing, sim_target, sim_data_in } = { 4'b0100, operand_address[`OPERAND_AB0], operand_data[`OPERAND_AB0] }; 2'b10 : { sim_timing, sim_target, sim_data_in } = { 4'b0010, operand_address[`OPERAND_ZPX], operand_data[`OPERAND_ZPX] }; 2'b11 : { sim_timing, sim_target, sim_data_in } = { 4'b0100, operand_address[`OPERAND_ABX], operand_data[`OPERAND_ABX] }; endcase always @(*) case (instruction_opcode[7:5]) 3'b000 : sim_mask_nzcv = 4'b1110; 3'b001 : sim_mask_nzcv = 4'b1110; 3'b010 : sim_mask_nzcv = 4'b1110; 3'b011 : sim_mask_nzcv = 4'b1110; 3'b110 : sim_mask_nzcv = 4'b1100; 3'b111 : sim_mask_nzcv = 4'b1100; default : sim_mask_nzcv = 4'bxxxx; endcase ALU_SIM sim_alu ( .data_in(sim_data_in), .carry_in(`C_FLAG), .operation(instruction_opcode[7:5]), .data_out(sim_data_out), .flag_nzcv(sim_flag_nzcv) ); // // LDA group // reg lda_unique; reg [3:0] lda_timing; reg [7:0] lda_source; reg [2:0] lda_mask_axy; wire [3:0] lda_flag_nzcv; always @(*) casex (instruction_opcode) 8'bxxx0_0000 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0001, 3'b001, operand_data[`OPERAND_IMM] }; 8'bxxx0_0001 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b1000, 3'b100, operand_data[`OPERAND_INX] }; 8'bxxx0_001x : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0001, 3'b010, operand_data[`OPERAND_IMM] }; 8'bxxx0_0100 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0010, 3'b001, operand_data[`OPERAND_ZP0] }; 8'bxxx0_0101 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0010, 3'b100, operand_data[`OPERAND_ZP0] }; 8'bxxx0_011x : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0010, 3'b010, operand_data[`OPERAND_ZP0] }; 8'bxxx0_10xx : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0001, 3'b100, operand_data[`OPERAND_IMM] }; 8'bxxx0_1100 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b1, 4'b0100, 3'b001, unique_select ? operand_data[`OPERAND_UNQ] : operand_data[`OPERAND_AB0] }; 8'bxxx0_1101 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b1, 4'b0100, 3'b100, unique_select ? operand_data[`OPERAND_UNQ] : operand_data[`OPERAND_AB0] }; 8'bxxx0_111x : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b1, 4'b0100, 3'b010, unique_select ? operand_data[`OPERAND_UNQ] : operand_data[`OPERAND_AB0] }; 8'bxxx1_000x : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b1000, 3'b100, operand_data[`OPERAND_INY] }; 8'bxxx1_001x : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b1000, 3'b100, operand_data[`OPERAND_IN0] }; 8'bxxx1_0100 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0010, 3'b001, operand_data[`OPERAND_ZPX] }; 8'bxxx1_0101 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0010, 3'b100, operand_data[`OPERAND_ZPX] }; 8'bxxx1_011x : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0010, 3'b010, operand_data[`OPERAND_ZPY] }; 8'bxxx1_10xx : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0100, 3'b100, operand_data[`OPERAND_ABY] }; 8'bxxx1_1100 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0100, 3'b001, operand_data[`OPERAND_ABX] }; 8'bxxx1_1101 : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0100, 3'b100, operand_data[`OPERAND_ABX] }; 8'bxxx1_111x : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'b0, 4'b0100, 3'b010, operand_data[`OPERAND_ABY] }; default : { lda_unique, lda_timing, lda_mask_axy, lda_source } = { 1'bx, 4'bxxxx, 3'bxxx, 8'bxxxxxxxx }; endcase ALU_DNZ lda_alu ( .data_in(lda_source), .flag_nzcv(lda_flag_nzcv) ); // // STA group // reg sta_unique; reg [ 3:0] sta_timing; reg [ 1:0] sta_register; reg [15:0] sta_target; always @(*) casex (instruction_opcode) 8'b0xx0_xxxx : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RZ, operand_address[`OPERAND_ZP0] }; 8'b0xx1_xxxx : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RZ, operand_address[`OPERAND_ZPX] }; 8'b1xx0_00xx : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b1000, `INDEX_RA, operand_address[`OPERAND_INX] }; 8'b1xx0_0100 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RY, operand_address[`OPERAND_ZP0] }; 8'b1xx0_0101 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RA, operand_address[`OPERAND_ZP0] }; 8'b1xx0_011x : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RX, operand_address[`OPERAND_ZP0] }; 8'b1xx0_1x00 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b1, 4'b0100, `INDEX_RY, operand_address[`OPERAND_AB0] }; 8'b1xx0_1x01 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b1, 4'b0100, `INDEX_RA, operand_address[`OPERAND_AB0] }; 8'b1xx0_1x1x : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b1, 4'b0100, `INDEX_RX, operand_address[`OPERAND_AB0] }; 8'b1xx1_000x : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b1000, `INDEX_RA, operand_address[`OPERAND_INY] }; 8'b1xx1_001x : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b1000, `INDEX_RA, operand_address[`OPERAND_IN0] }; 8'b1xx1_0100 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RY, operand_address[`OPERAND_ZPX] }; 8'b1xx1_0101 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RA, operand_address[`OPERAND_ZPX] }; 8'b1xx1_011x : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0010, `INDEX_RX, operand_address[`OPERAND_ZPY] }; 8'b1xx1_10xx : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0100, `INDEX_RA, operand_address[`OPERAND_ABY] }; 8'b1xx1_1100 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b1, 4'b0100, `INDEX_RZ, operand_address[`OPERAND_AB0] }; 8'b1xx1_1101 : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0100, `INDEX_RA, operand_address[`OPERAND_ABX] }; 8'b1xx1_111x : { sta_unique, sta_timing, sta_register, sta_target } = { 1'b0, 4'b0100, `INDEX_RZ, operand_address[`OPERAND_ABX] }; default : { sta_unique, sta_timing, sta_register, sta_target } = { 1'bx, 4'bxxxx, 2'bxx , 16'bxxxxxxxxxxxxxxxx }; endcase // // BCC group // reg branch_condition; always @(*) case (instruction_opcode[7:5]) 3'b000 : branch_condition = ~`N_FLAG; 3'b001 : branch_condition = `N_FLAG; 3'b010 : branch_condition = ~`V_FLAG; 3'b011 : branch_condition = `V_FLAG; 3'b100 : branch_condition = instruction_opcode[4] ? ~`C_FLAG : 1'b1; 3'b101 : branch_condition = `C_FLAG; 3'b110 : branch_condition = ~`Z_FLAG; 3'b111 : branch_condition = `Z_FLAG; endcase // // CLC group // reg [3:0] clc_mask_cvid; reg [3:0] clc_flag_cvid; always @(*) casex (instruction_opcode) 8'b000x_xxxx : { clc_mask_cvid, clc_flag_cvid } = { 4'b1000, 4'b0xxx }; 8'b001x_xxxx : { clc_mask_cvid, clc_flag_cvid } = { 4'b1000, 4'b1xxx }; 8'b010x_xxxx : { clc_mask_cvid, clc_flag_cvid } = { 4'b0010, 4'bxx0x }; 8'b011x_xxxx : { clc_mask_cvid, clc_flag_cvid } = { 4'b0010, 4'bxx1x }; 8'b10xx_xxxx : { clc_mask_cvid, clc_flag_cvid } = { 4'b0100, 4'bx0xx }; 8'b110x_xxxx : { clc_mask_cvid, clc_flag_cvid } = { 4'b0001, 4'bxxx0 }; 8'b111x_xxxx : { clc_mask_cvid, clc_flag_cvid } = { 4'b0001, 4'bxxx1 }; default : { clc_mask_cvid, clc_flag_cvid } = { 4'bxxxx, 4'bxxxx }; endcase // // CPX group // reg [3:0] cpx_timing; reg [7:0] cpx_data_in; wire [3:0] cpx_flag_nzcv; always @(*) case (instruction_opcode[3:2]) 2'b00 : { cpx_timing, cpx_data_in } = { 4'b0001, operand_data[`OPERAND_IMM] }; 2'b01 : { cpx_timing, cpx_data_in } = { 4'b0010, operand_data[`OPERAND_ZP0] }; 2'b11 : { cpx_timing, cpx_data_in } = { 4'b0100, operand_data[`OPERAND_AB0] }; default : { cpx_timing, cpx_data_in } = { 4'bxxxx, 8'bxxxxxxxx }; endcase ALU_CPX cpx_alu ( .xy_in(instruction_opcode[5] ? `X_REGISTER : `Y_REGISTER), .data_in(cpx_data_in), .flag_nzcv(cpx_flag_nzcv) ); // // SIA group // reg [3:0] sia_mask_nzcv; wire [7:0] sia_data_out; wire [3:0] sia_flag_nzcv; always @(*) case (instruction_opcode[6:4]) 3'b000 : sia_mask_nzcv = 4'b1110; 3'b010 : sia_mask_nzcv = 4'b1110; 3'b100 : sia_mask_nzcv = 4'b1110; 3'b110 : sia_mask_nzcv = 4'b1110; 3'b011 : sia_mask_nzcv = 4'b1100; 3'b001 : sia_mask_nzcv = 4'b1100; default : sia_mask_nzcv = 4'bxxxx; endcase ALU_SIA sia_alu ( .a_in(`A_REGISTER), .carry_in(`C_FLAG), .operation(instruction_opcode[6:4]), .data_out(sia_data_out), .flag_nzcv(sia_flag_nzcv) ); // // TAX group // reg [7:0] tax_source; reg [2:0] tax_mask_axy; wire [3:0] tax_flag_nzcv; always @(*) casex (instruction_opcode) 8'bxx00_xxxx : { tax_source, tax_mask_axy } = { `X_REGISTER, 3'b100 }; 8'bxx01_xxxx : { tax_source, tax_mask_axy } = { `Y_REGISTER, 3'b100 }; 8'bxx10_xx0x : { tax_source, tax_mask_axy } = { `A_REGISTER, 3'b001 }; 8'bxx10_xx1x : { tax_source, tax_mask_axy } = { `A_REGISTER, 3'b010 }; 8'bxx11_xxxx : { tax_source, tax_mask_axy } = { `S_REGISTER, 3'b010 }; default : { tax_source, tax_mask_axy } = { 8'bxxxxxxxx, 3'bxxx }; endcase ALU_DNZ tax_alu ( .data_in(tax_source), .flag_nzcv(tax_flag_nzcv) ); // // BIT group // reg bit_unique; reg [3:0] bit_timing; reg [7:0] bit_source; wire [3:0] bit_mask_nzcv; wire [3:0] bit_flag_nzcv; always @(*) casex ({ instruction_opcode[4:3], instruction_opcode[0] }) 3'b00x : { bit_unique, bit_timing, bit_source } = { 1'b0, 4'b0010, operand_data[`OPERAND_ZP0] }; 3'b011 : { bit_unique, bit_timing, bit_source } = { 1'b0, 4'b0001, operand_data[`OPERAND_IMM] }; 3'b010 : { bit_unique, bit_timing, bit_source } = { 1'b1, 4'b0100, unique_select ? operand_data[`OPERAND_UNQ] : operand_data[`OPERAND_AB0] }; 3'b10x : { bit_unique, bit_timing, bit_source } = { 1'b0, 4'b0010, operand_data[`OPERAND_ZPX] }; 3'b11x : { bit_unique, bit_timing, bit_source } = { 1'b0, 4'b0100, operand_data[`OPERAND_ABX] }; endcase assign bit_mask_nzcv = instruction_opcode[0] ? 4'b0100 : 4'b1101; ALU_BIT bit_alu ( .a_in(`A_REGISTER), .data_in(bit_source), .flag_nzcv(bit_flag_nzcv) ); // // TRB group // wire [ 3:0] trb_timing; wire [ 7:0] trb_source; wire [15:0] trb_target; wire [ 7:0] trb_data_out; wire [ 3:0] trb_flag_nzcv; assign trb_timing = instruction_opcode[3] ? 4'b0100 : 4'b0010; assign trb_source = instruction_opcode[3] ? operand_data[`OPERAND_AB0] : operand_data[`OPERAND_ZP0]; assign trb_target = instruction_opcode[3] ? operand_address[`OPERAND_AB0] : operand_address[`OPERAND_ZP0]; ALU_TRB trb_alu ( .a_in(`A_REGISTER), .data_in(trb_source), .operation(instruction_opcode[4]), .data_out(trb_data_out), .flag_nzcv(trb_flag_nzcv) ); // // DEX group // reg dex_operation; reg [2:0] dex_mask_axy; wire [7:0] dex_data_out; wire [3:0] dex_flag_nzcv; always @(*) casex (instruction_opcode) 8'bx0xx_xxxx : { dex_operation, dex_mask_axy } = { 1'b0, 3'b001 }; 8'bx10x_xx0x : { dex_operation, dex_mask_axy } = { 1'b1, 3'b001 }; 8'bx10x_xx1x : { dex_operation, dex_mask_axy } = { 1'b0, 3'b010 }; 8'bx11x_xxxx : { dex_operation, dex_mask_axy } = { 1'b1, 3'b010 }; default : { dex_operation, dex_mask_axy } = { 1'bx, 3'bxxx }; endcase ALU_DEX dex_alu ( .xy_in(dex_mask_axy[1] ? `X_REGISTER : `Y_REGISTER), .operation(dex_operation), .data_out(dex_data_out), .flag_nzcv(dex_flag_nzcv) ); // // PHA group // reg [7:0] pha_source; always @(*) casex (instruction_opcode) 8'b00xx_xxxx : pha_source = `P_REGISTER; 8'b01x0_xxxx : pha_source = `A_REGISTER; 8'b01x1_xxxx : pha_source = `Y_REGISTER; 8'b1xxx_xxxx : pha_source = `X_REGISTER; default : pha_source = 3'bxxx ; endcase // // PLA group // wire [7:0] pla_source; reg [2:0] pla_mask_axy; wire [3:0] pla_flag_nzcv; assign pla_source = operand_data[`OPERAND_ST1]; always @(*) casex (instruction_opcode) 8'b0xx0_xxxx : pla_mask_axy = 3'b100; 8'b0xx1_xxxx : pla_mask_axy = 3'b001; 8'b1xxx_xxxx : pla_mask_axy = 3'b010; default : pla_mask_axy = 3'bxxx; endcase ALU_DNZ pla_alu ( .data_in(pla_source), .flag_nzcv(pla_flag_nzcv) ); // // JMP group // reg [3:0] jmp_timing; always @(*) casex (instruction_opcode) 8'bxx0x_xxxx : jmp_timing = 4'b0001; 8'bxx10_xxxx : jmp_timing = 4'b0100; 8'bxx11_xxxx : jmp_timing = 4'b0100; default : jmp_timing = 4'bxxxx; endcase // // RTI group // wire [5:0] rti_nzcvid; assign rti_nzcvid = `P_NZCVID_OF(operand_data[`OPERAND_ST1]); // // PLP group // wire [5:0] plp_nzcvid; assign plp_nzcvid = `P_NZCVID_OF(operand_data[`OPERAND_ST1]); // // Early PC // wire [15:0] next_program_counter; assign next_program_counter = program_counter + instruction_length; // // Early X // reg [2:0] early_x_select; reg [7:0] early_x_register; reg [7:0] early_x_fast_data; always @(negedge cpu_clock) case (instruction_opcode) `INST_LDX_IMM : early_x_fast_data <= operand_data[`OPERAND_IMM]; `INST_TAX_IMP : early_x_fast_data <= `A_REGISTER; `INST_TSX_IMP : early_x_fast_data <= `S_REGISTER; `INST_DEX_IMP : early_x_fast_data <= `X_REGISTER - 1; `INST_INX_IMP : early_x_fast_data <= `X_REGISTER + 1; `INST_PLX_IMP : early_x_fast_data <= `X_REGISTER; default : early_x_fast_data <= `X_REGISTER; endcase always @(negedge cpu_clock) case (instruction_opcode) `INST_LDX_IMM : early_x_select <= 3'h0; `INST_LDX_ZP0 : early_x_select <= 3'h1; `INST_LDX_AB0 : early_x_select <= unique_select ? 3'h2 : 3'h3; `INST_LDX_ZPY : early_x_select <= 3'h4; `INST_LDX_ABY : early_x_select <= 3'h5; `INST_TAX_IMP : early_x_select <= 3'h0; `INST_TSX_IMP : early_x_select <= 3'h0; `INST_DEX_IMP : early_x_select <= 3'h0; `INST_INX_IMP : early_x_select <= 3'h0; `INST_PLX_IMP : early_x_select <= 3'h0; default : early_x_select <= 3'h0; endcase always @(*) case (early_x_select) 3'h1 : early_x_register = operand_data[`OPERAND_ZP0]; 3'h2 : early_x_register = operand_data[`OPERAND_UNQ]; 3'h3 : early_x_register = operand_data[`OPERAND_AB0]; 3'h4 : early_x_register = operand_data[`OPERAND_ZPY]; 3'h5 : early_x_register = operand_data[`OPERAND_ABY]; default : early_x_register = early_x_fast_data; endcase // // Early Y // reg [2:0] early_y_select; reg [7:0] early_y_register; reg [7:0] early_y_fast_data; always @(negedge cpu_clock) case (instruction_opcode) `INST_LDY_IMM : early_y_fast_data <= operand_data[`OPERAND_IMM]; `INST_TAY_IMP : early_y_fast_data <= `A_REGISTER; `INST_DEY_IMP : early_y_fast_data <= `Y_REGISTER - 1; `INST_INY_IMP : early_y_fast_data <= `Y_REGISTER + 1; `INST_PLY_IMP : early_y_fast_data <= `Y_REGISTER; default : early_y_fast_data <= `Y_REGISTER; endcase always @(negedge cpu_clock) case (instruction_opcode) `INST_LDY_IMM : early_y_select <= 3'h0; `INST_LDY_ZP0 : early_y_select <= 3'h1; `INST_LDY_AB0 : early_y_select <= unique_select ? 3'h2 : 3'h3; `INST_LDY_ZPY : early_y_select <= 3'h4; `INST_LDY_ABY : early_y_select <= 3'h5; `INST_TAY_IMP : early_y_select <= 3'h0; `INST_DEY_IMP : early_y_select <= 3'h0; `INST_INY_IMP : early_y_select <= 3'h0; `INST_PLY_IMP : early_y_select <= 3'h0; default : early_y_select <= 3'h0; endcase always @(*) case (early_y_select) 3'h1 : early_y_register = operand_data[`OPERAND_ZP0]; 3'h2 : early_y_register = operand_data[`OPERAND_UNQ]; 3'h3 : early_y_register = operand_data[`OPERAND_AB0]; 3'h4 : early_y_register = operand_data[`OPERAND_ZPX]; 3'h5 : early_y_register = operand_data[`OPERAND_ABX]; default : early_y_register = early_y_fast_data; endcase // // All instructions // reg [1:0] instruction_length; always @(*) casex (incoming_opcode) 8'b0x00_0000, 8'b0110_0000, 8'bxxxx_0x11, 8'bxxxx_1000, 8'bxxxx_101x, 8'bxxxx_1111 : instruction_length = 2'd1; 8'b0xx1_0000, 8'b1xxx_0000, 8'bxxxx_0001, 8'bxxxx_0x10, 8'bxxxx_010x, 8'bxxx0_1001 : instruction_length = 2'd2; 8'b0010_0000, 8'bxxx1_1001, 8'bxxxx_110x, 8'bxxxx_1110 : instruction_length = 2'd3; endcase // // Main core // reg [ 7:0] register_set[3:0]; reg [ 7:0] status_flags; reg [ 7:0] stack_pointer; reg [15:0] program_counter; reg [15:0] this_program_counter; wire [15:0] this_program_counter_plus_2; wire [ 7:0] incoming_opcode; wire [15:0] incoming_arg_abs; wire [ 7:0] incoming_arg_zpg; reg [23:0] current_instruction; reg instruction_excepted; wire [ 7:0] instruction_opcode; wire [15:0] instruction_arg_abs; wire [ 7:0] instruction_arg_zpg; wire [ 7:0] instruction_arg_imm; wire [ 7:0] instruction_arg_rel; reg [ 2:0] now_state; reg [ 5:0] next_state; reg now_is_first; reg now_is_commit; reg next_is_first; reg next_is_write; reg next_is_unique; reg [ 8:0] write_axy_nzcvid; reg [29:0] data_axy_nzcvid; reg [ 7:0] push_p_register; assign this_program_counter_plus_2 = this_program_counter + 2; assign incoming_opcode = code_read_data[7:0]; assign incoming_arg_abs = code_read_data[23:8]; assign incoming_arg_zpg = code_read_data[15:8]; assign instruction_opcode = current_instruction[7:0]; assign instruction_arg_abs = current_instruction[23:8]; assign instruction_arg_zpg = current_instruction[15:8]; assign instruction_arg_imm = current_instruction[15:8]; assign instruction_arg_rel = current_instruction[15:8]; always @(*) case (now_state) `NOW_FIRST : casex (instruction_opcode) `GROUP_ADC : next_state = adc_timing[3] ? `NEXT_ADC_WAIT_1 : `NEXT_ADC_DONE; `GROUP_SIM : next_state = `NEXT_SIM_WRITE; `GROUP_LDA : if (lda_unique & abs_0_is_unique) next_state = `NEXT_LDA_UNIQUE; else next_state = lda_timing[3] ? `NEXT_LDA_WAIT_1 : `NEXT_LDA_DONE; `GROUP_STA : if (sta_unique & abs_0_is_unique) next_state = `NEXT_STA_UNIQUE; else next_state = `NEXT_STA_WRITE; `GROUP_BCC : next_state = branch_condition ? `NEXT_BCC_RESOLVE : `NEXT_BCC_DONE; `GROUP_CLC : next_state = `NEXT_CLC_DONE; `GROUP_CPX : next_state = `NEXT_CPX_DONE; `GROUP_SIA : next_state = `NEXT_SIA_DONE; `GROUP_TAX : next_state = `NEXT_TAX_DONE; `GROUP_BIT : if (bit_unique & abs_0_is_unique) next_state = `NEXT_BIT_UNIQUE; else next_state = `NEXT_BIT_DONE; `GROUP_TRB : next_state = `NEXT_TRB_WRITE; `GROUP_DEX : next_state = `NEXT_DEX_DONE; `GROUP_PHA : next_state = `NEXT_PHA_WRITE; `GROUP_PLA : next_state = `NEXT_PLA_STACK; `GROUP_JMP : next_state = `NEXT_JMP_RESOLVE; `GROUP_RTI : next_state = `NEXT_RTI_RESOLVE; `GROUP_RTS : next_state = `NEXT_RTS_RESOLVE; `GROUP_TXS : next_state = `NEXT_TXS_STACK; `GROUP_PLP : next_state = `NEXT_PLP_STACK; `GROUP_JSR : next_state = `NEXT_JSR_PUSHH; `GROUP_BRK : next_state = `NEXT_EXC_PUSHH; default : next_state = `NEXT_OTHER_DONE; endcase `NOW_AWAIT : casex (instruction_opcode) `GROUP_ADC : next_state = `NEXT_ADC_DONE; `GROUP_LDA : next_state = `NEXT_LDA_DONE; `GROUP_STA : next_state = `NEXT_STA_DONE; `GROUP_BIT : next_state = `NEXT_BIT_DONE; default : next_state = `NEXT_OTHER_DONE; endcase `NOW_DELAY : next_state = `NEXT_ANY_DELAYED; `NOW_JSR_PUSHH : next_state = `NEXT_JSR_PUSHL; `NOW_EXC_PUSHH : next_state = `NEXT_EXC_PUSHL; `NOW_EXC_PUSHL : next_state = `NEXT_EXC_PUSHP; default : next_state = `NEXT_OTHER_DONE; endcase always @(negedge cpu_clock) begin now_is_first <= (now_state == `NOW_FIRST); now_is_commit <= next_state[5]; next_is_first <= (next_state[2:0] == `NOW_FIRST); next_is_write <= next_state[4]; next_is_unique <= next_state[3]; end always @(posedge cpu_clock or posedge active_rst) if (active_rst) now_state <= `NOW_FIRST; else now_state <= next_state[2:0]; always @(posedge cpu_clock) if (next_is_first) begin this_program_counter <= program_counter; program_counter <= next_program_counter; end else case (now_state) `NOW_FIRST : casex (instruction_opcode) `GROUP_BCC : if (branch_condition) program_counter <= program_counter + { {8{instruction_arg_rel[7]}}, instruction_arg_rel }; `GROUP_JMP : casex (instruction_opcode) 8'bxx0x_xxxx : program_counter <= instruction_arg_abs; 8'bxx10_xxxx : program_counter <= abs_0_read_data[15:0]; 8'bxx11_xxxx : program_counter <= abs_x_read_data[15:0]; endcase `GROUP_RTI : program_counter <= stk_1_read_data[23:8]; `GROUP_RTS : program_counter <= stk_1_read_data[15:0] + 1; `GROUP_JSR : program_counter <= instruction_arg_abs; `GROUP_BRK : begin program_counter <= vector_read_data[15:0]; push_p_register <= instruction_excepted ? `P_WITH_B_0 : `P_WITH_B_1; end endcase endcase always @(posedge cpu_clock or posedge active_rst) if (active_rst) begin instruction_excepted <= 1'b1; current_instruction[7:0] <= `INST_BRK_IMP; end else if (next_is_first) begin `Z_REGISTER <= 8'h00; instruction_excepted <= exception_active; current_instruction <= { code_read_data[23:8], exception_active ? `INST_BRK_IMP : code_read_data[7:0] }; end assign data_write_enable = next_is_write; always @(*) case (now_state) `NOW_FIRST, `NOW_AWAIT : casex (instruction_opcode) `GROUP_SIM : data_write_address = sim_target; `GROUP_STA : data_write_address = sta_target; `GROUP_TRB : data_write_address = trb_target; `GROUP_PHA : data_write_address = { 8'h01, `S_REGISTER }; `GROUP_JSR : data_write_address = { 8'h01, `S_REGISTER }; `GROUP_BRK : data_write_address = { 8'h01, `S_REGISTER }; default : data_write_address = 16'bxxxxxxxxxxxxxxxx; endcase `NOW_JSR_PUSHH : data_write_address = { 8'h01, `S_REGISTER }; `NOW_EXC_PUSHH : data_write_address = { 8'h01, `S_REGISTER }; `NOW_EXC_PUSHL : data_write_address = { 8'h01, `S_REGISTER }; default : data_write_address = 16'bxxxxxxxxxxxxxxxx; endcase always @(*) case (now_state) `NOW_FIRST, `NOW_AWAIT : casex (instruction_opcode) `GROUP_SIM : data_write_data = sim_data_out; `GROUP_STA : data_write_data = register_set[sta_register]; `GROUP_TRB : data_write_data = trb_data_out; `GROUP_PHA : data_write_data = pha_source; `GROUP_JSR : data_write_data = this_program_counter_plus_2[15:8]; `GROUP_BRK : data_write_data = instruction_excepted ? this_program_counter[15:8] : this_program_counter_plus_2[15:8]; default : data_write_data = 8'bxxxxxxxx; endcase `NOW_JSR_PUSHH : data_write_data = this_program_counter_plus_2[7:0]; `NOW_EXC_PUSHH : data_write_data = instruction_excepted ? this_program_counter[7:0] : this_program_counter_plus_2[7:0]; `NOW_EXC_PUSHL : data_write_data = push_p_register; default : data_write_data = 8'bxxxxxxxx; endcase always @(posedge cpu_clock) case (now_state) `NOW_FIRST : casex (instruction_opcode) `GROUP_PHA : `S_REGISTER <= `S_REGISTER - 1; `GROUP_PLA : `S_REGISTER <= `S_REGISTER + 1; `GROUP_RTI : `S_REGISTER <= `S_REGISTER + 3; `GROUP_RTS : `S_REGISTER <= `S_REGISTER + 2; `GROUP_TXS : `S_REGISTER <= `X_REGISTER; `GROUP_PLP : `S_REGISTER <= `S_REGISTER + 1; `GROUP_JSR : `S_REGISTER <= `S_REGISTER - 1; `GROUP_BRK : `S_REGISTER <= `S_REGISTER - 1; endcase `NOW_JSR_PUSHH : `S_REGISTER <= `S_REGISTER - 1; `NOW_EXC_PUSHH : `S_REGISTER <= `S_REGISTER - 1; `NOW_EXC_PUSHL : `S_REGISTER <= `S_REGISTER - 1; endcase always @(negedge cpu_clock) casex (instruction_opcode) `GROUP_ADC : write_axy_nzcvid <= { adc_mask_axy, adc_mask_nzcv, 2'b00 }; `GROUP_SIM : write_axy_nzcvid <= { 3'b000 , sim_mask_nzcv, 2'b00 }; `GROUP_LDA : write_axy_nzcvid <= { lda_mask_axy, 4'b1100 , 2'b00 }; `GROUP_CLC : write_axy_nzcvid <= { 3'b000 , 2'b00 , clc_mask_cvid }; `GROUP_CPX : write_axy_nzcvid <= { 3'b000 , 4'b1110 , 2'b00 }; `GROUP_SIA : write_axy_nzcvid <= { 3'b100 , sia_mask_nzcv, 2'b00 }; `GROUP_TAX : write_axy_nzcvid <= { tax_mask_axy, 4'b1100 , 2'b00 }; `GROUP_BIT : write_axy_nzcvid <= { 3'b000 , bit_mask_nzcv, 2'b00 }; `GROUP_TRB : write_axy_nzcvid <= { 3'b000 , 4'b0100 , 2'b00 }; `GROUP_DEX : write_axy_nzcvid <= { dex_mask_axy, 4'b1100 , 2'b00 }; `GROUP_PLA : write_axy_nzcvid <= { pla_mask_axy, 4'b1100 , 2'b00 }; `GROUP_RTI : write_axy_nzcvid <= { 3'b000 , 4'b1111 , 2'b11 }; `GROUP_PLP : write_axy_nzcvid <= { 3'b000 , 4'b1111 , 2'b11 }; `GROUP_BRK : write_axy_nzcvid <= { 3'b000 , 4'b0000 , 2'b11 }; default : write_axy_nzcvid <= { 3'b000 , 4'b0000 , 2'b00 }; endcase always @(*) casex (instruction_opcode) `GROUP_ADC : data_axy_nzcvid = { adc_data_out, 8'bxxxxxxxx , 8'bxxxxxxxx , adc_flag_nzcv, 2'bxx }; `GROUP_SIM : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , sim_flag_nzcv, 2'bxx }; `GROUP_LDA : data_axy_nzcvid = { lda_source , lda_source , lda_source , lda_flag_nzcv, 2'bxx }; `GROUP_CLC : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , 2'bxx , clc_flag_cvid }; `GROUP_CPX : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , cpx_flag_nzcv, 2'bxx }; `GROUP_SIA : data_axy_nzcvid = { sia_data_out, 8'bxxxxxxxx , 8'bxxxxxxxx , sia_flag_nzcv, 2'bxx }; `GROUP_TAX : data_axy_nzcvid = { tax_source , tax_source , tax_source , tax_flag_nzcv, 2'bxx }; `GROUP_BIT : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , bit_flag_nzcv, 2'bxx }; `GROUP_TRB : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , trb_flag_nzcv, 2'bxx }; `GROUP_DEX : data_axy_nzcvid = { 8'bxxxxxxxx , dex_data_out, dex_data_out, dex_flag_nzcv, 2'bxx }; `GROUP_PLA : data_axy_nzcvid = { pla_source , pla_source , pla_source , pla_flag_nzcv, 2'bxx }; `GROUP_RTI : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , rti_nzcvid }; `GROUP_PLP : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , plp_nzcvid }; `GROUP_BRK : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , 4'bxxxx , 2'b10 }; default : data_axy_nzcvid = { 8'bxxxxxxxx , 8'bxxxxxxxx , 8'bxxxxxxxx , 4'bxxxx , 2'bxx }; endcase always @(posedge cpu_clock) if (now_is_commit) begin if (write_axy_nzcvid[8]) `A_REGISTER <= data_axy_nzcvid[29:22]; if (write_axy_nzcvid[7]) `X_REGISTER <= data_axy_nzcvid[21:14]; if (write_axy_nzcvid[6]) `Y_REGISTER <= data_axy_nzcvid[13:6]; if (write_axy_nzcvid[5]) `N_FLAG <= data_axy_nzcvid[5]; if (write_axy_nzcvid[4]) `Z_FLAG <= data_axy_nzcvid[4]; if (write_axy_nzcvid[3]) `C_FLAG <= data_axy_nzcvid[3]; if (write_axy_nzcvid[2]) `V_FLAG <= data_axy_nzcvid[2]; if (write_axy_nzcvid[1]) `I_FLAG <= data_axy_nzcvid[1]; if (write_axy_nzcvid[0]) `D_FLAG <= data_axy_nzcvid[0]; end endmodule