diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 74e119c..02332cd 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -34,6 +34,22 @@ async def handle_memory(dut): if we: memory[addr] = int(dut.DO.value) +async def check_instruction_sequence(dut, instruction_sequence): + for expected_output in instruction_sequence: + await RisingEdge(dut.clk) + + if expected_output: + expected_addr, expected_we, expected_do = expected_output + dut_addr = int(dut.AB.value) + dut_we = bool(dut.WE.value) + dut_do = int(dut.DO.value) + + assert dut_addr == expected_addr + assert dut_we == expected_we + + if dut_we: + assert dut_do == expected_do + @cocotb.test async def test_reset(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) @@ -62,17 +78,7 @@ async def test_reset(dut): (0x12345679, False, int(dut.regfile.value)), # Read second byte ] - for expected_output in expected_cpu_outputs: - await RisingEdge(dut.clk) - - expected_addr, expected_we, expected_do = expected_output - dut_addr = int(dut.AB.value) - dut_we = bool(dut.WE.value) - dut_do = int(dut.DO.value) - - assert dut_addr == expected_addr - assert dut_we == expected_we - assert dut_do == expected_do + await check_instruction_sequence(dut, expected_cpu_outputs) @cocotb.test async def test_absolute(dut): @@ -120,20 +126,8 @@ async def test_absolute(dut): (0x50515253, True, 0x55), # Write to absolute address ] - for expected_output in expected_cpu_outputs: - await RisingEdge(dut.clk) + await check_instruction_sequence(dut, expected_cpu_outputs) - if expected_output: - expected_addr, expected_we, expected_do = expected_output - dut_addr = int(dut.AB.value) - dut_we = bool(dut.WE.value) - dut_do = int(dut.DO.value) - - assert dut_addr == expected_addr - assert dut_we == expected_we - - if dut_we: - assert dut_do == expected_do @cocotb.test async def test_absolute_x(dut): @@ -191,20 +185,8 @@ async def test_absolute_x(dut): (0x01020306, True, 0xaa), # Write to address ] - for expected_output in expected_cpu_outputs: - await RisingEdge(dut.clk) + await check_instruction_sequence(dut, expected_cpu_outputs) - if expected_output: - expected_addr, expected_we, expected_do = expected_output - dut_addr = int(dut.AB.value) - dut_we = bool(dut.WE.value) - dut_do = int(dut.DO.value) - - assert dut_addr == expected_addr - assert dut_we == expected_we - - if dut_we: - assert dut_do == expected_do @cocotb.test async def test_absolute_y(dut): @@ -262,17 +244,53 @@ async def test_absolute_y(dut): (0x01020306, True, 0xaa), # Write to address ] - for expected_output in expected_cpu_outputs: + await check_instruction_sequence(dut, expected_cpu_outputs) + + +@cocotb.test +async def test_absolute_x_indirect(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + # ldx #1 + # jmp ($deadbeef,x) + write_bytes(0x200, [0xa2, 0x01]) + write_bytes(0x202, [0x7c, 0xef, 0xbe, 0xad, 0xde]) + write_byte(0xbeefb055, 0xcb) + + write_dword(0xdeadbeef + 1, 0xbeefb055) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): await RisingEdge(dut.clk) + dut.reset.value = 0 - if expected_output: - expected_addr, expected_we, expected_do = expected_output - dut_addr = int(dut.AB.value) - dut_we = bool(dut.WE.value) - dut_do = int(dut.DO.value) + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00000200, False, None), # ldx #1 + (0x00000201, False, None), # Immediate + (0x00000202, False, None), # jmp ($deadbeef,x) + (0x00000203, False, None), # addr 0 + (0x00000204, False, None), # addr 1 + (0x00000205, False, None), # addr 2 + (0x00000206, False, None), # addr 3 + (0xdeadbef0, False, None), # addr 0 + (0xdeadbef1, False, None), # addr 0 + (0xdeadbef2, False, None), # addr 0 + (0xdeadbef3, False, None), # addr 0 + (0xbeefb055, False, None), # + ] - assert dut_addr == expected_addr - assert dut_we == expected_we - - if dut_we: - assert dut_do == expected_do \ No newline at end of file + await check_instruction_sequence(dut, expected_cpu_outputs) diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index e05ced5..a1a890c 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -208,69 +208,71 @@ parameter */ parameter - ABS0 = 6'd0, // ABS - fetch LSB - ABS1 = 6'd1, // ABS - fetch MSB - ABSX0 = 6'd2, // ABS, X - fetch LSB and send to ALU (+X) - ABSX1 = 6'd3, // ABS, X - fetch MSB and send to ALU (+Carry) - ABSX2 = 6'd4, // ABS, X - Wait for ALU (only if needed) - BRA0 = 6'd5, // Branch - fetch offset and send to ALU (+PC[7:0]) - BRA1 = 6'd6, // Branch - fetch opcode, and send PC[15:8] to ALU - BRA2 = 6'd7, // Branch - fetch opcode (if page boundary crossed) - BRK0 = 6'd8, // BRK/IRQ - push PCH, send S to ALU (-1) - BRK1 = 6'd9, // BRK/IRQ - push PCL, send S to ALU (-1) - BRK2 = 6'd10, // BRK/IRQ - push P, send S to ALU (-1) - BRK3 = 6'd11, // BRK/IRQ - write S, and fetch @ fffe - DECODE = 6'd12, // IR is valid, decode instruction, and write prev reg - FETCH = 6'd13, // fetch next opcode, and perform prev ALU op - INDX0 = 6'd14, // (ZP,X) - fetch ZP address, and send to ALU (+X) - INDX1 = 6'd15, // (ZP,X) - fetch LSB at ZP+X, calculate ZP+X+1 - INDX2 = 6'd16, // (ZP,X) - fetch MSB at ZP+X+1 - INDX3 = 6'd17, // (ZP,X) - fetch data - INDY0 = 6'd18, // (ZP),Y - fetch ZP address, and send ZP to ALU (+1) - INDY1 = 6'd19, // (ZP),Y - fetch at ZP+1, and send LSB to ALU (+Y) - INDY2 = 6'd20, // (ZP),Y - fetch data, and send MSB to ALU (+Carry) - INDY3 = 6'd21, // (ZP),Y) - fetch data (if page boundary crossed) - JMP0 = 6'd22, // JMP - fetch PCL and hold - JMP1 = 6'd23, // JMP - fetch PCH - JMPI0 = 6'd24, // JMP IND - fetch LSB and send to ALU for delay (+0) - JMPI1 = 6'd25, // JMP IND - fetch MSB, proceed with JMP0 state - JSR0 = 6'd26, // JSR - push PCH, save LSB, send S to ALU (-1) - JSR1 = 6'd27, // JSR - push PCL, send S to ALU (-1) - JSR2 = 6'd28, // JSR - write S - JSR3 = 6'd29, // JSR - fetch MSB - PULL0 = 6'd30, // PLP/PLA/PLX/PLY - save next op in IRHOLD, send S to ALU (+1) - PULL1 = 6'd31, // PLP/PLA/PLX/PLY - fetch data from stack, write S - PULL2 = 6'd32, // PLP/PLA/PLX/PLY - prefetch op, but don't increment PC - PUSH0 = 6'd33, // PHP/PHA/PHX/PHY - send A to ALU (+0) - PUSH1 = 6'd34, // PHP/PHA/PHX/PHY - write A/P, send S to ALU (-1) - READ = 6'd35, // Read memory for read/modify/write (INC, DEC, shift) - REG = 6'd36, // Read register for reg-reg transfers - RTI0 = 6'd37, // RTI - send S to ALU (+1) - RTI1 = 6'd38, // RTI - read P from stack - RTI2 = 6'd39, // RTI - read PCL from stack - RTI3 = 6'd40, // RTI - read PCH from stack - RTI4 = 6'd41, // RTI - read PCH from stack - RTS0 = 6'd42, // RTS - send S to ALU (+1) - RTS1 = 6'd43, // RTS - read PCL from stack - RTS2 = 6'd44, // RTS - write PCL to ALU, read PCH - RTS3 = 6'd45, // RTS - load PC and increment - WRITE = 6'd46, // Write memory for read/modify/write - ZP0 = 6'd47, // Z-page - fetch ZP address - ZPX0 = 6'd48, // ZP, X - fetch ZP, and send to ALU (+X) - ZPX1 = 6'd49, // ZP, X - load from memory - IND0 = 6'd50, // (ZP) - fetch ZP address, and send to ALU (+0) - JMPIX0 = 6'd51, // JMP (,X)- fetch LSB and send to ALU (+X) - JMPIX1 = 6'd52, // JMP (,X)- fetch MSB and send to ALU (+Carry) - JMPIX2 = 6'd53, // JMP (,X)- Wait for ALU (only if needed) - WAI = 6'd54, // WAI - Wait for interrupt, then go to decode - BRK4 = 6'd55, // TODO - BRK5 = 6'd56, // TODO - JMP2 = 6'd57, // TODO - JMP3 = 6'd58, // TODO - ABS2 = 6'd59, // TODO - ABS3 = 6'd60, // TODO - ABSX3 = 6'd61, // TODO - ABSX4 = 6'd62; // TODO + ABS0 = 7'd0, // ABS - fetch LSB + ABS1 = 7'd1, // ABS - fetch MSB + ABSX0 = 7'd2, // ABS, X - fetch LSB and send to ALU (+X) + ABSX1 = 7'd3, // ABS, X - fetch MSB and send to ALU (+Carry) + ABSX2 = 7'd4, // ABS, X - Wait for ALU (only if needed) + BRA0 = 7'd5, // Branch - fetch offset and send to ALU (+PC[7:0]) + BRA1 = 7'd6, // Branch - fetch opcode, and send PC[15:8] to ALU + BRA2 = 7'd7, // Branch - fetch opcode (if page boundary crossed) + BRK0 = 7'd8, // BRK/IRQ - push PCH, send S to ALU (-1) + BRK1 = 7'd9, // BRK/IRQ - push PCL, send S to ALU (-1) + BRK2 = 7'd10, // BRK/IRQ - push P, send S to ALU (-1) + BRK3 = 7'd11, // BRK/IRQ - write S, and fetch @ fffe + DECODE = 7'd12, // IR is valid, decode instruction, and write prev reg + FETCH = 7'd13, // fetch next opcode, and perform prev ALU op + INDX0 = 7'd14, // (ZP,X) - fetch ZP address, and send to ALU (+X) + INDX1 = 7'd15, // (ZP,X) - fetch LSB at ZP+X, calculate ZP+X+1 + INDX2 = 7'd16, // (ZP,X) - fetch MSB at ZP+X+1 + INDX3 = 7'd17, // (ZP,X) - fetch data + INDY0 = 7'd18, // (ZP),Y - fetch ZP address, and send ZP to ALU (+1) + INDY1 = 7'd19, // (ZP),Y - fetch at ZP+1, and send LSB to ALU (+Y) + INDY2 = 7'd20, // (ZP),Y - fetch data, and send MSB to ALU (+Carry) + INDY3 = 7'd21, // (ZP),Y) - fetch data (if page boundary crossed) + JMP0 = 7'd22, // JMP - fetch PCL and hold + JMP1 = 7'd23, // JMP - fetch PCH + JMPI0 = 7'd24, // JMP IND - fetch LSB and send to ALU for delay (+0) + JMPI1 = 7'd25, // JMP IND - fetch MSB, proceed with JMP0 state + JSR0 = 7'd26, // JSR - push PCH, save LSB, send S to ALU (-1) + JSR1 = 7'd27, // JSR - push PCL, send S to ALU (-1) + JSR2 = 7'd28, // JSR - write S + JSR3 = 7'd29, // JSR - fetch MSB + PULL0 = 7'd30, // PLP/PLA/PLX/PLY - save next op in IRHOLD, send S to ALU (+1) + PULL1 = 7'd31, // PLP/PLA/PLX/PLY - fetch data from stack, write S + PULL2 = 7'd32, // PLP/PLA/PLX/PLY - prefetch op, but don't increment PC + PUSH0 = 7'd33, // PHP/PHA/PHX/PHY - send A to ALU (+0) + PUSH1 = 7'd34, // PHP/PHA/PHX/PHY - write A/P, send S to ALU (-1) + READ = 7'd35, // Read memory for read/modify/write (INC, DEC, shift) + REG = 7'd36, // Read register for reg-reg transfers + RTI0 = 7'd37, // RTI - send S to ALU (+1) + RTI1 = 7'd38, // RTI - read P from stack + RTI2 = 7'd39, // RTI - read PCL from stack + RTI3 = 7'd40, // RTI - read PCH from stack + RTI4 = 7'd41, // RTI - read PCH from stack + RTS0 = 7'd42, // RTS - send S to ALU (+1) + RTS1 = 7'd43, // RTS - read PCL from stack + RTS2 = 7'd44, // RTS - write PCL to ALU, read PCH + RTS3 = 7'd45, // RTS - load PC and increment + WRITE = 7'd46, // Write memory for read/modify/write + ZP0 = 7'd47, // Z-page - fetch ZP address + ZPX0 = 7'd48, // ZP, X - fetch ZP, and send to ALU (+X) + ZPX1 = 7'd49, // ZP, X - load from memory + IND0 = 7'd50, // (ZP) - fetch ZP address, and send to ALU (+0) + JMPIX0 = 7'd51, // JMP (,X)- fetch LSB and send to ALU (+X) + JMPIX1 = 7'd52, // JMP (,X)- fetch MSB and send to ALU (+Carry) + JMPIX2 = 7'd53, // JMP (,X)- Wait for ALU (only if needed) + WAI = 7'd54, // WAI - Wait for interrupt, then go to decode + BRK4 = 7'd55, // TODO + BRK5 = 7'd56, // TODO + JMP2 = 7'd57, // TODO + JMP3 = 7'd58, // TODO + ABS2 = 7'd59, // TODO + ABS3 = 7'd60, // TODO + ABSX3 = 7'd61, // TODO + ABSX4 = 7'd62, // TODO + JMPIX3 = 7'd63, // TODO + JMPIX4 = 7'd64; // TODO `ifdef SIM @@ -343,6 +345,8 @@ always @* JMPIX0: statename = "JMPIX0"; JMPIX1: statename = "JMPIX1"; JMPIX2: statename = "JMPIX2"; + JMPIX3: statename = "JMPIX3"; + JMPIX4: statename = "JMPIX4"; WAI: statename = "WAI"; endcase @@ -368,14 +372,14 @@ always @* JMP3, JMPI1, - JMPIX1, + JMPIX3, JSR3, RTS3, RTI4: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1}; BRA1: PC_temp = { ABR[15:8], ADD }; - JMPIX2, + JMPIX4, BRA2: PC_temp = { ADD, PC[7:0] }; // TODO BRK4: PC_temp = res ? 32'hFFFFFFF4 : @@ -398,7 +402,9 @@ always @* ABS1, ABS2, JMPIX0, + JMPIX1, JMPIX2, + JMPIX4, ABSX0, ABSX1, ABSX2, @@ -414,7 +420,7 @@ always @* RTI4, RTS3: PC_inc = 1; - JMPIX1: PC_inc = ~CO; // Don't increment PC if we are going to go through JMPIX2 + JMPIX3: PC_inc = ~CO; // Don't increment PC if we are going to go through JMPIX4 BRA1: PC_inc = CO ^~ backwards; @@ -438,7 +444,7 @@ parameter always @* case( state ) - JMPIX1, + JMPIX3, ABSX3, INDX3, INDY2, @@ -449,7 +455,7 @@ always @* BRA2, INDY3, - JMPIX2, + JMPIX4, ABSX4: AB = { ADD, ABR[23:0] }; // TODO BRA1: AB = { ABR[15:8], ADD }; // TODO @@ -1061,8 +1067,10 @@ always @(posedge clk or posedge reset) ABSX4 : state <= write_back ? READ : FETCH; JMPIX0 : state <= JMPIX1; - JMPIX1 : state <= CO ? JMPIX2 : JMP0; - JMPIX2 : state <= JMP0; + JMPIX1 : state <= JMPIX2; + JMPIX2 : state <= JMPIX3; + JMPIX3 : state <= CO ? JMPIX4 : JMP0; + JMPIX4 : state <= JMP0; IND0 : state <= INDX1;