From 9476c6a0dd3bec6bf7d521cdd2c3467bcd3fb929 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 26 Apr 2026 08:53:59 -0700 Subject: [PATCH 01/17] Add 32 bit BRK --- sim/verilog6502_32bit.yaml | 9 ++++ sim/verilog6502_32bit_test.py | 20 +++++++++ src/cpu_65c02.v | 77 +++++++++++++++++++++-------------- 3 files changed, 75 insertions(+), 31 deletions(-) create mode 100644 sim/verilog6502_32bit.yaml create mode 100644 sim/verilog6502_32bit_test.py diff --git a/sim/verilog6502_32bit.yaml b/sim/verilog6502_32bit.yaml new file mode 100644 index 0000000..3553d1f --- /dev/null +++ b/sim/verilog6502_32bit.yaml @@ -0,0 +1,9 @@ +tests: + - name: "cpu_65c02" + toplevel: "cpu_65c02" + modules: + - "verilog6502_32bit_test" + sources: "sources.list" + waves: True + defines: + SIM: "hi" \ No newline at end of file diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py new file mode 100644 index 0000000..08d111d --- /dev/null +++ b/sim/verilog6502_32bit_test.py @@ -0,0 +1,20 @@ +import cocotb +from cocotb.handle import Immediate + +from cocotb.clock import Clock +from cocotb.triggers import Timer, RisingEdge + +CLK_PERIOD = 5 + +@cocotb.test +async def test_absolute(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + await Timer(1, "us") \ No newline at end of file diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index 1086546..c140cf9 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -52,7 +52,7 @@ module cpu_65c02( clk, reset, AB, DI, DO, WE, IRQ, NMI, RDY, RDY_O, SYNC ); input clk; // CPU clock input reset; // reset signal -output reg [15:0] AB; // address bus +output reg [31:0] AB; // address bus input [7:0] DI; // data in, read bus output [7:0] DO; // data out, write bus output WE; // write enable @@ -66,9 +66,8 @@ output reg SYNC; // AB is first cycle of the intruction * internal signals */ -reg [15:0] PC; // Program Counter -reg [7:0] ABL; // Address Bus Register LSB -reg [7:0] ABH; // Address Bus Register MSB +reg [31:0] PC; // Program Counter +reg [31:0] ABR; // Address Bus Register wire [7:0] ADD; // Adder Hold Register (registered in ALU) reg [7:0] DIHOLD; // Hold for Data In @@ -103,8 +102,6 @@ wire [7:0] AO; // ALU output after BCD adjustment reg WE; // Write Enable reg CI; // Carry In wire CO; // Carry Out -wire [7:0] PCH = PC[15:8]; -wire [7:0] PCL = PC[7:0]; reg NMI_edge = 0; // captured NMI edge @@ -142,7 +139,7 @@ reg [5:0] state; */ reg PC_inc; // Increment PC -reg [15:0] PC_temp; // intermediate value of PC +reg [31:0] PC_temp; // intermediate value of PC reg [1:0] src_reg; // source register index reg [1:0] dst_reg; // destination register index @@ -263,7 +260,9 @@ parameter 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 + WAI = 6'd54, // WAI - Wait for interrupt, then go to decode + BRK4 = 6'd55, // TODO + BRK5 = 6'd56; // TODO `ifdef SIM @@ -318,6 +317,8 @@ always @* BRK1: statename = "BRK1"; BRK2: statename = "BRK2"; BRK3: statename = "BRK3"; + BRK4: statename = "BRK4"; + BRK5: statename = "BRK5"; BRA0: statename = "BRA0"; BRA1: statename = "BRA1"; BRA2: statename = "BRA2"; @@ -346,7 +347,7 @@ always @* always @* case( state ) DECODE: if( (~I & IRQ) | NMI_edge ) - PC_temp = { ABH, ABL }; + PC_temp = ABR; else PC_temp = PC; @@ -358,13 +359,13 @@ always @* RTS3, RTI4: PC_temp = { DIMUX, ADD }; - BRA1: PC_temp = { ABH, ADD }; + BRA1: PC_temp = { ABR[15:8], ADD }; JMPIX2, - BRA2: PC_temp = { ADD, PCL }; + BRA2: PC_temp = { ADD, PC[7:0] }; // TODO - BRK2: PC_temp = res ? 16'hfffc : - NMI_edge ? 16'hfffa : 16'hfffe; + BRK2: PC_temp = res ? 32'hFFFFFFF4 : + NMI_edge ? 32'hFFFFFFF8 : 32'hFFFFFFFC; default: PC_temp = PC; endcase @@ -386,7 +387,7 @@ always @* FETCH, BRA0, BRA2, - BRK3, + BRK5, JMPI1, JMP1, RTI4, @@ -428,15 +429,15 @@ always @* BRA2, INDY3, JMPIX2, - ABSX2: AB = { ADD, ABL }; + ABSX2: AB = { ADD, ABR[7:0] }; // TODO - BRA1: AB = { ABH, ADD }; + BRA1: AB = { ABR[15:8], ADD }; // TODO JSR0, PUSH1, RTS0, RTI0, - BRK0: AB = { STACKPAGE, regfile }; + BRK0: AB = { 16'h0, STACKPAGE, regfile }; BRK1, JSR1, @@ -446,7 +447,9 @@ always @* RTI1, RTI2, RTI3, - BRK2: AB = { STACKPAGE, ADD }; + BRK2, + BRK3, + BRK4: AB = { 16'h0, STACKPAGE, ADD }; INDY1, INDX1, @@ -458,7 +461,7 @@ always @* REG, READ, - WRITE: AB = { ABH, ABL }; + WRITE: AB = ABR; default: AB = PC; endcase @@ -472,8 +475,7 @@ always @(posedge clk) if( state != PUSH0 && state != PUSH1 && RDY && state != PULL0 && state != PULL1 && state != PULL2 ) begin - ABL <= AB[7:0]; - ABH <= AB[15:8]; + ABR <= AB; end /* @@ -484,14 +486,20 @@ always @* WRITE: DO = ADD; JSR0, - BRK0: DO = PCH; + BRK0: DO = PC[31:24]; JSR1, - BRK1: DO = PCL; + BRK1: DO = PC[23:16]; + + JSR2, + BRK2: DO = PC[15:8]; + + JSR3, + BRK3: DO = PC[7:0]; PUSH1: DO = php ? P : ADD; - BRK2: DO = (IRQ | NMI_edge) ? (P & 8'b1110_1111) : P; + BRK4: DO = (IRQ | NMI_edge) ? (P & 8'b1110_1111) : P; default: DO = store_zero ? 8'b0 : regfile; endcase @@ -505,8 +513,13 @@ always @* BRK0, // writing to stack or memory BRK1, BRK2, + BRK2, + BRK3, + BRK4, JSR0, JSR1, + JSR2, + JSR3, PUSH1, WRITE: WE = 1; @@ -535,7 +548,7 @@ always @* PULL1, RTS2, RTI3, - BRK3, + BRK5, JSR0, JSR2 : write_register = 1; @@ -621,7 +634,7 @@ always @* DECODE : regsel = dst_reg; BRK0, - BRK3, + BRK5, JSR0, JSR2, PULL0, @@ -730,7 +743,7 @@ always @* BRA0, READ: AI = DIMUX; - BRA1: AI = ABH; // don't use PCH in case we're + BRA1: AI = ABR[15:8]; // don't use PCH in case we're TODO FETCH: AI = load_only ? 8'b0 : regfile; @@ -767,7 +780,7 @@ always @* READ: BI = txb_ins ? (trb_ins ? ~regfile : regfile) : 8'h00; - BRA0: BI = PCL; + BRA0: BI = PC[7:0]; // TODO DECODE, ABS1: BI = 8'hxx; @@ -880,7 +893,7 @@ always @(posedge clk) */ always @(posedge clk) - if( state == BRK3 ) + if( state == BRK5 ) I <= 1; else if( state == RTI2 ) I <= DIMUX[2]; @@ -1069,7 +1082,9 @@ always @(posedge clk or posedge reset) BRK0 : state <= BRK1; BRK1 : state <= BRK2; BRK2 : state <= BRK3; - BRK3 : state <= JMP0; + BRK3 : state <= BRK4; + BRK4 : state <= BRK5; + BRK5 : state <= JMP0; WAI : state <= ( (~I & IRQ) | NMI_edge ) ? DECODE : WAI; @@ -1415,7 +1430,7 @@ always @(posedge clk) NMI_1 <= NMI; always @(posedge clk ) - if( NMI_edge && state == BRK3 ) + if( NMI_edge && state == BRK5 ) NMI_edge <= 0; else if( NMI & ~NMI_1 ) NMI_edge <= 1; -- 2.47.3 From 019b84f41d6ea775194eeeb250fbd0eb185c3779 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 26 Apr 2026 19:28:39 -0700 Subject: [PATCH 02/17] Get reset sequence to work --- sim/verilog6502_32bit_test.py | 50 ++++++++++++++++++++++++++++++++++- src/cpu_65c02.v | 35 ++++++++++++++++++------ 2 files changed, 76 insertions(+), 9 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 08d111d..ff55f5d 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -4,11 +4,35 @@ from cocotb.handle import Immediate from cocotb.clock import Clock from cocotb.triggers import Timer, RisingEdge +from collections import defaultdict + CLK_PERIOD = 5 +memory = defaultdict(int) + +def write_dword(addr: int, data: int): + memory[addr + 0] = (data >> 0) & 0xff + memory[addr + 1] = (data >> 8) & 0xff + memory[addr + 2] = (data >> 16) & 0xff + memory[addr + 3] = (data >> 24) & 0xff + +async def handle_memory(dut): + while True: + await RisingEdge(dut.clk) + addr = int(dut.AB.value) + we = bool(dut.WE.value) + + dut.DI.value = memory[addr] + + if we: + memory[addr] = int(dut.DO.value) + @cocotb.test async def test_absolute(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x12345678) dut.RDY.value = Immediate(1) @@ -17,4 +41,28 @@ async def test_absolute(dut): await RisingEdge(dut.clk) dut.reset.value = 0 - await Timer(1, "us") \ No newline at end of file + expected_cpu_outputs = [ + (0x00000100, True, (int(dut.PC.value) >> 24) & 0xff), # High addr + (0x000001ff, True, (int(dut.PC.value) >> 16) & 0xff), # Mid high addr + (0x000001fe, True, (int(dut.PC.value) >> 8) & 0xff), # Mid low addr + (0x000001fd, True, (int(dut.PC.value) >> 0) & 0xff), # Low addr + (0x000001fc, True, int(dut.P.value)), # Status + (0xfffffff4, False, int(dut.regfile.value)), # read vector byte 0 + (0xfffffff5, False, int(dut.regfile.value)), # read vector byte 1 + (0xfffffff6, False, int(dut.regfile.value)), # read vector byte 2 + (0xfffffff7, False, int(dut.regfile.value)), # read vector byte 3 + (0x12345678, False, int(dut.regfile.value)), # Load PC with vector + (0x12345678, False, int(dut.regfile.value)), # Read first instruction + ] + + 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 diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index c140cf9..c8da170 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -69,6 +69,8 @@ output reg SYNC; // AB is first cycle of the intruction reg [31:0] PC; // Program Counter reg [31:0] ABR; // Address Bus Register wire [7:0] ADD; // Adder Hold Register (registered in ALU) +reg [7:0] alu_sr_0; // ALU output shift register 0 +reg [7:0] alu_sr_1; // ALU output shift register 1 reg [7:0] DIHOLD; // Hold for Data In reg DIHOLD_valid; // @@ -262,7 +264,9 @@ parameter 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 + BRK5 = 6'd56, // TODO + JMP2 = 6'd57, // TODO + JMP3 = 6'd58; // TODO `ifdef SIM @@ -324,6 +328,8 @@ always @* BRA2: statename = "BRA2"; JMP0: statename = "JMP0"; JMP1: statename = "JMP1"; + JMP2: statename = "JMP2"; + JMP3: statename = "JMP3"; JMPI0: statename = "JMPI0"; JMPI1: statename = "JMPI1"; JMPIX0: statename = "JMPIX0"; @@ -352,19 +358,19 @@ always @* PC_temp = PC; - JMP1, + JMP3, JMPI1, JMPIX1, JSR3, RTS3, - RTI4: PC_temp = { DIMUX, ADD }; + RTI4: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1}; BRA1: PC_temp = { ABR[15:8], ADD }; JMPIX2, BRA2: PC_temp = { ADD, PC[7:0] }; // TODO - BRK2: PC_temp = res ? 32'hFFFFFFF4 : + BRK4: PC_temp = res ? 32'hFFFFFFF4 : NMI_edge ? 32'hFFFFFFF8 : 32'hFFFFFFFC; default: PC_temp = PC; @@ -389,7 +395,9 @@ always @* BRA2, BRK5, JMPI1, + JMP0, JMP1, + JMP2, RTI4, RTS3: PC_inc = 1; @@ -421,10 +429,10 @@ always @* ABSX1, INDX3, INDY2, - JMP1, + JMP3, JMPI1, RTI4, - ABS1: AB = { DIMUX, ADD }; + ABS1: AB = { DIMUX, ADD, alu_sr_0, alu_sr_1}; BRA2, INDY3, @@ -667,6 +675,11 @@ ALU ALU( .clk(clk), .HC(HC), .RDY(RDY) ); +always @(posedge clk) begin + alu_sr_0 <= ADD; + alu_sr_1 <= alu_sr_0; +end + /* * Select current ALU operation */ @@ -687,6 +700,8 @@ always @* BRK0, BRK1, BRK2, + BRK3, + BRK4, JSR0, JSR1: alu_op = OP_SUB; @@ -723,6 +738,8 @@ always @* RTI2, BRK1, BRK2, + BRK3, + BRK4, INDX1: AI = ADD; REG, @@ -1074,7 +1091,9 @@ always @(posedge clk or posedge reset) BRA2 : state <= DECODE; JMP0 : state <= JMP1; - JMP1 : state <= DECODE; + JMP1 : state <= JMP2; + JMP2 : state <= JMP3; + JMP3 : state <= DECODE; JMPI0 : state <= JMPI1; JMPI1 : state <= JMP0; @@ -1106,7 +1125,7 @@ always @(posedge clk or posedge reset) PUSH1, PULL2, RTI4, - JMP1, + JMP3, BRA2 : SYNC <= 1'b1; default: SYNC <= 1'b0; endcase -- 2.47.3 From 747438a9b678417f56eb94c90a31c456f70056b5 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 26 Apr 2026 20:34:11 -0700 Subject: [PATCH 03/17] Add absolute addressing --- sim/verilog6502_32bit_test.py | 72 ++++++++++++++++++++++++++++++++++- src/cpu_65c02.v | 25 ++++++++---- 2 files changed, 87 insertions(+), 10 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index ff55f5d..ace9288 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -16,6 +16,13 @@ def write_dword(addr: int, data: int): memory[addr + 2] = (data >> 16) & 0xff memory[addr + 3] = (data >> 24) & 0xff +def write_byte(addr: int, data: int): + memory[addr] = data & 0xff + +def write_bytes(addr: int, data: bytes| list[int]): + for i, val in enumerate(data): + memory[addr + i] = int(val) + async def handle_memory(dut): while True: await RisingEdge(dut.clk) @@ -28,7 +35,7 @@ async def handle_memory(dut): memory[addr] = int(dut.DO.value) @cocotb.test -async def test_absolute(dut): +async def test_reset(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) @@ -51,8 +58,8 @@ async def test_absolute(dut): (0xfffffff5, False, int(dut.regfile.value)), # read vector byte 1 (0xfffffff6, False, int(dut.regfile.value)), # read vector byte 2 (0xfffffff7, False, int(dut.regfile.value)), # read vector byte 3 - (0x12345678, False, int(dut.regfile.value)), # Load PC with vector (0x12345678, False, int(dut.regfile.value)), # Read first instruction + (0x12345679, False, int(dut.regfile.value)), # Read second byte ] for expected_output in expected_cpu_outputs: @@ -66,3 +73,64 @@ async def test_absolute(dut): assert dut_addr == expected_addr assert dut_we == expected_we assert dut_do == expected_do + +@cocotb.test +async def test_absolute(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + # lda #$abcd1234 + # wai + write_bytes(0x200, [0xad, 0x34, 0x12, 0xcd, 0xab]) + write_bytes(0x205, [0x8d, 0x53, 0x52, 0x51, 0x50]) + write_byte(0x20a, 0xcb) + write_byte(0xabcd1234, 0x55) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00000200, False, None), # Read first instruction + (0x00000201, False, None), # Read address byte 0 + (0x00000202, False, None), # Read address byte 1 + (0x00000203, False, None), # Read address byte 2 + (0x00000204, False, None), # Read address byte 3 + (0xabcd1234, False, None), # Read from absolute address + (0x00000205, False, None), # Read second instruction + (0x00000206, False, None), # Read address byte 0 + (0x00000207, False, None), # Read address byte 1 + (0x00000208, False, None), # Read address byte 2 + (0x00000209, False, None), # Read address byte 3 + (0x50515253, True, 0x55), # Write to absolute address + + ] + + for expected_output in expected_cpu_outputs: + 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 diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index c8da170..21cfa3b 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -266,7 +266,9 @@ parameter BRK4 = 6'd55, // TODO BRK5 = 6'd56, // TODO JMP2 = 6'd57, // TODO - JMP3 = 6'd58; // TODO + JMP3 = 6'd58, // TODO + ABS2 = 6'd59, // TODO + ABS3 = 6'd60; // TODO `ifdef SIM @@ -284,6 +286,8 @@ always @* ZPX1: statename = "ZPX1"; ABS0: statename = "ABS0"; ABS1: statename = "ABS1"; + ABS2: statename = "ABS2"; + ABS3: statename = "ABS3"; ABSX0: statename = "ABSX0"; ABSX1: statename = "ABSX1"; ABSX2: statename = "ABSX2"; @@ -387,6 +391,8 @@ always @* PC_inc = 1; ABS0, + ABS1, + ABS2, JMPIX0, JMPIX2, ABSX0, @@ -398,6 +404,7 @@ always @* JMP0, JMP1, JMP2, + JMP3, RTI4, RTS3: PC_inc = 1; @@ -432,7 +439,7 @@ always @* JMP3, JMPI1, RTI4, - ABS1: AB = { DIMUX, ADD, alu_sr_0, alu_sr_1}; + ABS3: AB = { DIMUX, ADD, alu_sr_0, alu_sr_1}; BRA2, INDY3, @@ -534,7 +541,7 @@ always @* INDX3, // only if doing a STA, STX or STY INDY3, ABSX2, - ABS1, + ABS3, ZPX1, ZP0: WE = store; @@ -694,7 +701,7 @@ always @* REG : alu_op = op; DECODE, - ABS1: alu_op = 1'bx; + ABS3: alu_op = 1'bx; PUSH1, BRK0, @@ -765,7 +772,7 @@ always @* FETCH: AI = load_only ? 8'b0 : regfile; DECODE, - ABS1: AI = 8'hxx; // don't care + ABS3: AI = 8'hxx; // don't care default: AI = 0; endcase @@ -800,7 +807,7 @@ always @* BRA0: BI = PC[7:0]; // TODO DECODE, - ABS1: BI = 8'hxx; + ABS3: BI = 8'hxx; default: BI = DIMUX; endcase @@ -817,7 +824,7 @@ always @* ABSX1: CI = CO; DECODE, - ABS1: CI = 1'bx; + ABS3: CI = 1'bx; READ, REG: CI = rotate ? C : @@ -1035,7 +1042,9 @@ always @(posedge clk or posedge reset) ZPX1 : state <= write_back ? READ : FETCH; ABS0 : state <= ABS1; - ABS1 : state <= write_back ? READ : FETCH; + ABS1 : state <= ABS2; + ABS2 : state <= ABS3; + ABS3 : state <= write_back ? READ : FETCH; ABSX0 : state <= ABSX1; ABSX1 : state <= (CO | store | write_back) ? ABSX2 : FETCH; -- 2.47.3 From cb6cac12451b7a673680625ea2f77e9d4895305f Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 26 Apr 2026 21:03:53 -0700 Subject: [PATCH 04/17] add absolute,x --- sim/verilog6502_32bit_test.py | 75 ++++++++++++++++++++++++++++++++++- src/cpu_65c02.v | 24 +++++++---- 2 files changed, 90 insertions(+), 9 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index ace9288..da4740e 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -81,7 +81,8 @@ async def test_absolute(dut): write_dword(0xfffffff4, 0x200) - # lda #$abcd1234 + # lda $abcd1234 + # sta $50515253 # wai write_bytes(0x200, [0xad, 0x34, 0x12, 0xcd, 0xab]) write_bytes(0x205, [0x8d, 0x53, 0x52, 0x51, 0x50]) @@ -117,7 +118,77 @@ async def test_absolute(dut): (0x00000208, False, None), # Read address byte 2 (0x00000209, False, None), # Read address byte 3 (0x50515253, True, 0x55), # Write to absolute address - + ] + + for expected_output in expected_cpu_outputs: + 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_absolute_x(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + # ldx #1 + # lda $abcd1234,x + # inx + # sta $01020304,x + # wai + write_bytes(0x200, [0xa2, 0x01]) + write_bytes(0x202, [0xbd, 0x34, 0x12, 0xcd, 0xab]) + write_bytes(0x207, [0xe8]) + write_bytes(0x208, [0x9d, 0x04, 0x03, 0x02, 0x01]) + + write_byte(0x20d, 0xcb) + write_byte(0xabcd1235, 0xaa) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + 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), # ldx $abcd1234,x + (0x00000203, False, None), # addr 0 + (0x00000204, False, None), # addr 1 + (0x00000205, False, None), # addr 2 + (0x00000206, False, None), # addr 3 + (0xabcd1235, False, None), # Read from address + (0x00000207, False, None), # inx + (0x00000208, False, None), # sta $01020304,x + (0x00000208, False, None), # store reg + (0x00000209, False, None), # addr 0 + (0x0000020a, False, None), # addr 1 + (0x0000020b, False, None), # addr 2 + (0x0000020c, False, None), # addr 3 + (0x01020306, False, None), # Write to address + (0x01020306, True, 0xaa), # Write to address ] for expected_output in expected_cpu_outputs: diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index 21cfa3b..e05ced5 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -268,7 +268,9 @@ parameter JMP2 = 6'd57, // TODO JMP3 = 6'd58, // TODO ABS2 = 6'd59, // TODO - ABS3 = 6'd60; // TODO + ABS3 = 6'd60, // TODO + ABSX3 = 6'd61, // TODO + ABSX4 = 6'd62; // TODO `ifdef SIM @@ -291,6 +293,8 @@ always @* ABSX0: statename = "ABSX0"; ABSX1: statename = "ABSX1"; ABSX2: statename = "ABSX2"; + ABSX3: statename = "ABSX3"; + ABSX4: statename = "ABSX4"; IND0: statename = "IND0"; INDX0: statename = "INDX0"; INDX1: statename = "INDX1"; @@ -396,6 +400,8 @@ always @* JMPIX0, JMPIX2, ABSX0, + ABSX1, + ABSX2, FETCH, BRA0, BRA2, @@ -433,7 +439,7 @@ parameter always @* case( state ) JMPIX1, - ABSX1, + ABSX3, INDX3, INDY2, JMP3, @@ -444,7 +450,7 @@ always @* BRA2, INDY3, JMPIX2, - ABSX2: AB = { ADD, ABR[7:0] }; // TODO + ABSX4: AB = { ADD, ABR[23:0] }; // TODO BRA1: AB = { ABR[15:8], ADD }; // TODO @@ -540,7 +546,7 @@ always @* INDX3, // only if doing a STA, STX or STY INDY3, - ABSX2, + ABSX4, ABS3, ZPX1, ZP0: WE = store; @@ -821,7 +827,9 @@ always @* INDY2, BRA1, JMPIX1, - ABSX1: CI = CO; + ABSX1, + ABSX2, + ABSX3: CI = CO; DECODE, ABS3: CI = 1'bx; @@ -1047,8 +1055,10 @@ always @(posedge clk or posedge reset) ABS3 : state <= write_back ? READ : FETCH; ABSX0 : state <= ABSX1; - ABSX1 : state <= (CO | store | write_back) ? ABSX2 : FETCH; - ABSX2 : state <= write_back ? READ : FETCH; + ABSX1 : state <= ABSX2; + ABSX2 : state <= ABSX3; + ABSX3 : state <= (CO | store | write_back) ? ABSX4 : FETCH; + ABSX4 : state <= write_back ? READ : FETCH; JMPIX0 : state <= JMPIX1; JMPIX1 : state <= CO ? JMPIX2 : JMP0; -- 2.47.3 From 7164a8172f63ece5728cd34f5849c2cb111a9ea2 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 26 Apr 2026 21:08:21 -0700 Subject: [PATCH 05/17] Add test for abs,y --- sim/verilog6502_32bit_test.py | 71 +++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index da4740e..74e119c 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -205,3 +205,74 @@ async def test_absolute_x(dut): if dut_we: assert dut_do == expected_do + +@cocotb.test +async def test_absolute_y(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + # ldy #1 + # lda $abcd1234,y + # iny + # sta $01020304,y + # wai + write_bytes(0x200, [0xa0, 0x01]) + write_bytes(0x202, [0xb9, 0x34, 0x12, 0xcd, 0xab]) + write_bytes(0x207, [0xc8]) + write_bytes(0x208, [0x99, 0x04, 0x03, 0x02, 0x01]) + + write_byte(0x20d, 0xcb) + write_byte(0xabcd1235, 0xaa) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + 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), # ldx $abcd1234,x + (0x00000203, False, None), # addr 0 + (0x00000204, False, None), # addr 1 + (0x00000205, False, None), # addr 2 + (0x00000206, False, None), # addr 3 + (0xabcd1235, False, None), # Read from address + (0x00000207, False, None), # inx + (0x00000208, False, None), # sta $01020304,x + (0x00000208, False, None), # store reg + (0x00000209, False, None), # addr 0 + (0x0000020a, False, None), # addr 1 + (0x0000020b, False, None), # addr 2 + (0x0000020c, False, None), # addr 3 + (0x01020306, False, None), # Write to address + (0x01020306, True, 0xaa), # Write to address + ] + + for expected_output in expected_cpu_outputs: + 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 \ No newline at end of file -- 2.47.3 From dc339cb725af758c7bc9838d4920e9d921d31a55 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 26 Apr 2026 21:57:08 -0700 Subject: [PATCH 06/17] Add absolute indexed indirect --- sim/verilog6502_32bit_test.py | 114 +++++++++++++++----------- src/cpu_65c02.v | 148 ++++++++++++++++++---------------- 2 files changed, 144 insertions(+), 118 deletions(-) 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; -- 2.47.3 From b31d7490b2d318d742cdd20a44003655cc565613 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Sun, 26 Apr 2026 22:13:42 -0700 Subject: [PATCH 07/17] Add indirect jump --- sim/verilog6502_32bit_test.py | 53 ++++++++++++++++++++++++++++++++--- src/cpu_65c02.v | 19 +++++++++---- 2 files changed, 63 insertions(+), 9 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 02332cd..575c7df 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -287,10 +287,55 @@ async def test_absolute_x_indirect(dut): (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), # + (0xdeadbef1, False, None), # addr 1 + (0xdeadbef2, False, None), # addr 2 + (0xdeadbef3, False, None), # addr 3 + (0xbeefb055, False, None), # target + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + + +@cocotb.test +async def test_indirect(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + # jmp ($deadbeef) + write_bytes(0x200, [0x6c, 0xef, 0xbe, 0xad, 0xde]) + write_byte(0xbeefb055, 0xcb) + + write_dword(0xdeadbeef, 0xbeefb055) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00000200, False, None), # jmp ($deadbeef) + (0x00000201, False, None), # addr 0 + (0x00000202, False, None), # addr 1 + (0x00000203, False, None), # addr 2 + (0x00000204, False, None), # addr 3 + (0xdeadbeef, False, None), # addr 0 + (0xdeadbef0, False, None), # addr 1 + (0xdeadbef1, False, None), # addr 2 + (0xdeadbef2, False, None), # addr 3 + (0xbeefb055, False, None), # target ] await check_instruction_sequence(dut, expected_cpu_outputs) diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index a1a890c..de8b6bd 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -134,7 +134,7 @@ wire [7:0] P = { N, V, 2'b11, D, I, Z, C }; * instruction decoder/sequencer */ -reg [5:0] state; +reg [6:0] state; /* * control signals @@ -272,7 +272,9 @@ parameter ABSX3 = 7'd61, // TODO ABSX4 = 7'd62, // TODO JMPIX3 = 7'd63, // TODO - JMPIX4 = 7'd64; // TODO + JMPIX4 = 7'd64, // TODO + JMPI2 = 7'd65, // TODO + JMPI3 = 7'd66; // TODO `ifdef SIM @@ -342,6 +344,8 @@ always @* JMP3: statename = "JMP3"; JMPI0: statename = "JMPI0"; JMPI1: statename = "JMPI1"; + JMPI2: statename = "JMPI2"; + JMPI3: statename = "JMPI3"; JMPIX0: statename = "JMPIX0"; JMPIX1: statename = "JMPIX1"; JMPIX2: statename = "JMPIX2"; @@ -371,7 +375,7 @@ always @* JMP3, - JMPI1, + JMPI3, JMPIX3, JSR3, RTS3, @@ -412,7 +416,10 @@ always @* BRA0, BRA2, BRK5, + JMPI0, JMPI1, + JMPI2, + JMPI3, JMP0, JMP1, JMP2, @@ -449,7 +456,7 @@ always @* INDX3, INDY2, JMP3, - JMPI1, + JMPI3, RTI4, ABS3: AB = { DIMUX, ADD, alu_sr_0, alu_sr_1}; @@ -1123,7 +1130,9 @@ always @(posedge clk or posedge reset) JMP3 : state <= DECODE; JMPI0 : state <= JMPI1; - JMPI1 : state <= JMP0; + JMPI1 : state <= JMPI2; + JMPI2 : state <= JMPI3; + JMPI3 : state <= JMP0; BRK0 : state <= BRK1; BRK1 : state <= BRK2; -- 2.47.3 From dfe27d4ec783cbc8e3f03a18853f24042a6a0c89 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 27 Apr 2026 21:59:30 -0700 Subject: [PATCH 08/17] Add indexed indirect --- sim/verilog6502_32bit_test.py | 64 +++++++++++++++++++++++++++++++++++ src/cpu_65c02.v | 47 ++++++++++++++++++------- 2 files changed, 99 insertions(+), 12 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 575c7df..21d084d 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -339,3 +339,67 @@ async def test_indirect(dut): ] await check_instruction_sequence(dut, expected_cpu_outputs) + +@cocotb.test +async def test_indexed_indirect(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + # ldy #1 + # lda ($04),y + # iny + # inc + # sta ($04),y + write_bytes(0x200, [0xa0, 0x02]) + write_bytes(0x202, [0xb1, 0x04]) + write_bytes(0x204, [0xc8]) + write_bytes(0x205, [0x1a]) + write_bytes(0x206, [0x91, 0x04]) + write_byte(0x208, 0xcb) + + write_dword(0x04, 0xabcdbeef) + write_dword(0xabcdbeef+2, 0xaa) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00000200, False, None), # ldy #0 + (0x00000201, False, None), # Immediate + (0x00000202, False, None), # lda ($04),y + (0x00000203, False, None), # ZP index + (0x00000004, False, None), # zp addr 0 + (0x00000005, False, None), # zp addr 1 + (0x00000006, False, None), # zp addr 2 + (0x00000007, False, None), # zp addr 3 + (0xabcdbef1, False, None), # fetch data + (0x00000204, False, None), # iny + (0x00000205, False, None), # iny + (0x00000205, False, None), # inc + (0x00000206, False, None), # inc + (0x00000206, False, None), # sta ($04),y + (0x00000207, False, None), # ZP index + (0x00000004, False, None), # zp addr 0 + (0x00000005, False, None), # zp addr 1 + (0x00000006, False, None), # zp addr 2 + (0x00000007, False, None), # zp addr 3 + (0xabcdbef2, False, None), # store takes extra cycle + (0xabcdbef2, True, 0xAB), # write data + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) \ No newline at end of file diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index de8b6bd..76e8eef 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -67,6 +67,7 @@ output reg SYNC; // AB is first cycle of the intruction */ reg [31:0] PC; // Program Counter +reg abr_inc; // should increase address bus register reg [31:0] ABR; // Address Bus Register wire [7:0] ADD; // Adder Hold Register (registered in ALU) reg [7:0] alu_sr_0; // ALU output shift register 0 @@ -226,10 +227,10 @@ parameter 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) + INDY0 = 7'd18, // (ZP),Y - fetch ZP address + INDY1 = 7'd19, // (ZP),Y - fetch at ZP+1, and send byte 0 to ALU (+Y) + INDY2 = 7'd20, // (ZP),Y - fetch at ZP+2, and send byte 1 to ALU (+Carry) + INDY5 = 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) @@ -274,7 +275,9 @@ parameter JMPIX3 = 7'd63, // TODO JMPIX4 = 7'd64, // TODO JMPI2 = 7'd65, // TODO - JMPI3 = 7'd66; // TODO + JMPI3 = 7'd66, // TODO + INDY3 = 7'd67, // (ZP),Y - fetch at ZP+3, and send byte 2 to ALU (+Carry) + INDY4 = 7'd68; // (ZP),Y - fetch data, and send byte 3 to ALU (+Carry) `ifdef SIM @@ -308,6 +311,8 @@ always @* INDY1: statename = "INDY1"; INDY2: statename = "INDY2"; INDY3: statename = "INDY3"; + INDY4: statename = "INDY4"; + INDY5: statename = "INDY5"; READ: statename = "READ"; WRITE: statename = "WRITE"; FETCH: statename = "FETCH"; @@ -454,14 +459,12 @@ always @* JMPIX3, ABSX3, INDX3, - INDY2, JMP3, JMPI3, RTI4, ABS3: AB = { DIMUX, ADD, alu_sr_0, alu_sr_1}; BRA2, - INDY3, JMPIX4, ABSX4: AB = { ADD, ABR[23:0] }; // TODO @@ -485,7 +488,6 @@ always @* BRK3, BRK4: AB = { 16'h0, STACKPAGE, ADD }; - INDY1, INDX1, ZPX1, INDX2: AB = { ZEROPAGE, ADD }; @@ -495,8 +497,14 @@ always @* REG, READ, + INDY1, + INDY2, + INDY3, WRITE: AB = ABR; + INDY4: AB = {DIMUX, ADD, alu_sr_0, alu_sr_1}; + INDY5: AB = {ADD, ABR[23:0]}; + default: AB = PC; endcase @@ -509,9 +517,22 @@ always @(posedge clk) if( state != PUSH0 && state != PUSH1 && RDY && state != PULL0 && state != PULL1 && state != PULL2 ) begin - ABR <= AB; + if (abr_inc) begin + ABR <= AB + 1; + end else begin + ABR <= AB; + end end +always @* begin + case( state ) + INDY0, + INDY1, + INDY2: abr_inc = 1; + default: abr_inc = 0; + endcase +end + /* * Data Out MUX */ @@ -558,7 +579,7 @@ always @* WRITE: WE = 1; INDX3, // only if doing a STA, STX or STY - INDY3, + INDY5, ABSX4, ABS3, ZPX1, @@ -1088,8 +1109,10 @@ always @(posedge clk or posedge reset) INDY0 : state <= INDY1; INDY1 : state <= INDY2; - INDY2 : state <= (CO | store) ? INDY3 : FETCH; - INDY3 : state <= FETCH; + INDY2 : state <= INDY3; + INDY3 : state <= INDY4; + INDY4 : state <= (CO | store) ? INDY5 : FETCH; + INDY5 : state <= FETCH; READ : state <= WRITE; WRITE : state <= FETCH; -- 2.47.3 From 2a9af9e9dcfcde47be772f659a0a8b91d71ee282 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 27 Apr 2026 23:00:44 -0700 Subject: [PATCH 09/17] Add indirect indexed --- sim/verilog6502_32bit_test.py | 72 ++++++++++++++++++++++++++++++++++- src/cpu_65c02.v | 36 +++++++++++------- 2 files changed, 93 insertions(+), 15 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 21d084d..375acca 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -401,5 +401,73 @@ async def test_indexed_indirect(dut): (0xabcdbef2, False, None), # store takes extra cycle (0xabcdbef2, True, 0xAB), # write data ] - - await check_instruction_sequence(dut, expected_cpu_outputs) \ No newline at end of file + + await check_instruction_sequence(dut, expected_cpu_outputs) + +@cocotb.test +async def test_indirect_indexed(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + + # ldx #4 + # lda ($04,x) + # inc + # ldx #8 + # sta ($04,x) + write_bytes(0x200, [0xa2, 0x04]) + write_bytes(0x202, [0xa1, 0x04]) + write_bytes(0x204, [0x1a]) + write_bytes(0x205, [0xa2, 0x08]) + write_bytes(0x207, [0x81, 0x04]) + write_byte(0x209, 0xcb) + + write_dword(0x08, 0xfeedf00d) + write_dword(0x0c, 0xf00d600d) + + write_byte(0xfeedf00d, 0x69) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00000200, False, None), # ldx #4 + (0x00000201, False, None), # Immediate + (0x00000202, False, None), # lda ($04,x) + (0x00000203, False, None), # ZP index + (0x00000004, False, None), # Compute ZP index + (0x00000008, False, None), # zp addr 0 + (0x00000009, False, None), # zp addr 1 + (0x0000000a, False, None), # zp addr 2 + (0x0000000b, False, None), # zp addr 3 + (0xfeedf00d, False, None), # fetch data + (0x00000204, False, None), # iny + (0x00000205, False, None), # iny + (0x00000205, False, None), # inc + (0x00000206, False, None), # inc + (0x00000207, False, None), # sta ($04),y + (0x00000208, False, None), # ZP index + (0x00000004, False, None), # Compute ZP index + (0x0000000c, False, None), # zp addr 0 + (0x0000000d, False, None), # zp addr 1 + (0x0000000e, False, None), # zp addr 2 + (0x0000000f, False, None), # zp addr 3 + (0xf00d600d, True, 0x6a), # write data + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index 76e8eef..91e64a5 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -226,7 +226,7 @@ parameter 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 + INDX5 = 7'd17, // (ZP,X) - fetch data INDY0 = 7'd18, // (ZP),Y - fetch ZP address INDY1 = 7'd19, // (ZP),Y - fetch at ZP+1, and send byte 0 to ALU (+Y) INDY2 = 7'd20, // (ZP),Y - fetch at ZP+2, and send byte 1 to ALU (+Carry) @@ -277,7 +277,10 @@ parameter JMPI2 = 7'd65, // TODO JMPI3 = 7'd66, // TODO INDY3 = 7'd67, // (ZP),Y - fetch at ZP+3, and send byte 2 to ALU (+Carry) - INDY4 = 7'd68; // (ZP),Y - fetch data, and send byte 3 to ALU (+Carry) + INDY4 = 7'd68, // (ZP),Y - fetch data, and send byte 3 to ALU (+Carry) + INDX3 = 7'd69, // (ZP,X) - fetch addr 2 at ZP+X+2 + INDX4 = 7'd70; // (ZP,X) - fetch addr 3 at ZP+X+3 + `ifdef SIM @@ -307,6 +310,8 @@ always @* INDX1: statename = "INDX1"; INDX2: statename = "INDX2"; INDX3: statename = "INDX3"; + INDX4: statename = "INDX4"; + INDX5: statename = "INDX5"; INDY0: statename = "INDY0"; INDY1: statename = "INDY1"; INDY2: statename = "INDY2"; @@ -458,7 +463,7 @@ always @* case( state ) JMPIX3, ABSX3, - INDX3, + INDX5, JMP3, JMPI3, RTI4, @@ -489,10 +494,10 @@ always @* BRK4: AB = { 16'h0, STACKPAGE, ADD }; INDX1, - ZPX1, - INDX2: AB = { ZEROPAGE, ADD }; + ZPX1: AB = { ZEROPAGE, ADD }; ZP0, + INDX0, INDY0: AB = { ZEROPAGE, DIMUX }; REG, @@ -500,6 +505,9 @@ always @* INDY1, INDY2, INDY3, + INDX2, + INDX3, + INDX4, WRITE: AB = ABR; INDY4: AB = {DIMUX, ADD, alu_sr_0, alu_sr_1}; @@ -528,7 +536,10 @@ always @* begin case( state ) INDY0, INDY1, - INDY2: abr_inc = 1; + INDY2, + INDX1, + INDX2, + INDX3: abr_inc = 1; default: abr_inc = 0; endcase end @@ -578,7 +589,7 @@ always @* PUSH1, WRITE: WE = 1; - INDX3, // only if doing a STA, STX or STY + INDX5, // only if doing a STA, STX or STY INDY5, ABSX4, ABS3, @@ -786,8 +797,7 @@ always @* BRK1, BRK2, BRK3, - BRK4, - INDX1: AI = ADD; + BRK4: AI = ADD; REG, ZPX0, @@ -829,7 +839,6 @@ always @* RTI0, RTI1, RTI2, - INDX1, REG, JSR0, JSR1, @@ -882,8 +891,7 @@ always @* RTI2, RTS0, RTS1, - INDY0, - INDX1: CI = 1; + INDY0: CI = 1; default: CI = 0; endcase @@ -1105,7 +1113,9 @@ always @(posedge clk or posedge reset) INDX0 : state <= INDX1; INDX1 : state <= INDX2; INDX2 : state <= INDX3; - INDX3 : state <= FETCH; + INDX3 : state <= INDX4; + INDX4 : state <= INDX5; + INDX5 : state <= FETCH; INDY0 : state <= INDY1; INDY1 : state <= INDY2; -- 2.47.3 From 2338d4c720089ae13d74467275b2f879faf02426 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Tue, 28 Apr 2026 22:17:42 -0700 Subject: [PATCH 10/17] Add some more tests --- sim/verilog6502_32bit_test.py | 81 ++++++++++++++++++++++++++++++++++- 1 file changed, 80 insertions(+), 1 deletion(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 375acca..86cb441 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -2,10 +2,13 @@ import cocotb from cocotb.handle import Immediate from cocotb.clock import Clock -from cocotb.triggers import Timer, RisingEdge +from cocotb.triggers import Timer, RisingEdge, FallingEdge from collections import defaultdict +import struct +import random + CLK_PERIOD = 5 memory = defaultdict(int) @@ -471,3 +474,79 @@ async def test_indirect_indexed(dut): ] await check_instruction_sequence(dut, expected_cpu_outputs) + +@cocotb.test +async def test_adc(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + + def zp_indirect(): + value = random.randint(0,255) + address = random.randint(0x300, 0xffffffff) + + address_le = struct.pack(" Date: Thu, 30 Apr 2026 21:27:59 -0700 Subject: [PATCH 11/17] Add jsr and rts --- sim/verilog6502_32bit_test.py | 64 +++++++++++++++++++ src/cpu_65c02.v | 117 ++++++++++++++++++++++++++++------ 2 files changed, 160 insertions(+), 21 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 86cb441..fd9ad00 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -476,6 +476,70 @@ async def test_indirect_indexed(dut): await check_instruction_sequence(dut, expected_cpu_outputs) @cocotb.test +async def test_jsr(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + + # @0x200 + # ldx #$0 + # txs + # jsr $12345678 + # wai + # + # @0x1234 + # rts + write_bytes(0x200, [0xa2, 0xff, 0x9a, 0x20, 0x78, 0x56, 0x34, 0x12, 0xcb]) + write_bytes(0x12345678, [0x60]) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00000200, False, None), # ldx #$00 + (0x00000201, False, None), # Immediate + (0x00000202, False, None), # txs + (0x00000203, False, None), # second cycle of txs + (0x00000203, False, None), # jsr $12345678 + (0x00000204, False, None), # first byte of address + (0x000001ff, True, 0x00), # 24-31 + (0x000001fe, True, 0x00), # 16-23 + (0x000001fd, True, 0x02), # 8-15 + (0x000001fc, True, 0x05), # 7-0 + (0x00000205, False, None), # second byte of address + (0x00000206, False, None), # third byte of address + (0x00000207, False, None), # fourth byte of address + (0x00000208, False, None), # receive last byte of address + (0x12345678, False, None), # rts + (0x12345679, False, None), # rts + (0x000001fb, False, None), # current stack while we add 1 to it + (0x000001fc, False, None), # 7-0 + (0x000001fd, False, None), # 15-8 + (0x000001fe, False, None), # 23-16 + (0x000001ff, False, None), # 31-24 + (0x1234567c, False, None), # Updating PC before jump + (0x00000208, False, None), # WAI + (0x00000209, False, None), # second wai + (0x0000020a, False, None), # third wai + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + +# @cocotb.test async def test_adc(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index 91e64a5..cdb2eb0 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -141,12 +141,15 @@ reg [6:0] state; * control signals */ -reg PC_inc; // Increment PC +reg [1:0] PC_inc; // Increment PC reg [31:0] PC_temp; // intermediate value of PC reg [1:0] src_reg; // source register index reg [1:0] dst_reg; // destination register index +reg sr_sel; // choose to load shift register from dimux or from alu +reg alu_sr_enable; // choose to shift or not + reg index_y; // if set, then Y is index reg rather than X reg load_reg; // loading a register (A, X, Y, S) in this instruction reg inc; // increment @@ -201,6 +204,10 @@ parameter OP_ROL = 4'b1011, OP_A = 4'b1111; +parameter + SR_ALU = 1'b0, + SR_DI = 1'b1; + /* * Microcode state machine. Basically, every addressing mode has its own * path through the state machine. Additional information, such as the @@ -238,7 +245,7 @@ parameter 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 + JSR4 = 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 @@ -254,7 +261,7 @@ parameter 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 + RTS5 = 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) @@ -279,7 +286,13 @@ parameter INDY3 = 7'd67, // (ZP),Y - fetch at ZP+3, and send byte 2 to ALU (+Carry) INDY4 = 7'd68, // (ZP),Y - fetch data, and send byte 3 to ALU (+Carry) INDX3 = 7'd69, // (ZP,X) - fetch addr 2 at ZP+X+2 - INDX4 = 7'd70; // (ZP,X) - fetch addr 3 at ZP+X+3 + INDX4 = 7'd70, // (ZP,X) - fetch addr 3 at ZP+X+3 + JSR3 = 7'd71, + JSR5 = 7'd73, + JSR6 = 7'd74, + JSR7 = 7'd75, + RTS3 = 7'd76, + RTS4 = 7'd77; `ifdef SIM @@ -330,6 +343,10 @@ always @* JSR1: statename = "JSR1"; JSR2: statename = "JSR2"; JSR3: statename = "JSR3"; + JSR4: statename = "JSR4"; + JSR5: statename = "JSR5"; + JSR6: statename = "JSR6"; + JSR7: statename = "JSR7"; RTI0: statename = "RTI0"; RTI1: statename = "RTI1"; RTI2: statename = "RTI2"; @@ -339,6 +356,8 @@ always @* RTS1: statename = "RTS1"; RTS2: statename = "RTS2"; RTS3: statename = "RTS3"; + RTS4: statename = "RTS4"; + RTS5: statename = "RTS5"; BRK0: statename = "BRK0"; BRK1: statename = "BRK1"; BRK2: statename = "BRK2"; @@ -387,9 +406,11 @@ always @* JMP3, JMPI3, JMPIX3, - JSR3, - RTS3, - RTI4: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1}; + JSR7: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1}; + + RTS5, + RTI4: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1} + 2; + BRA1: PC_temp = { ABR[15:8], ADD }; @@ -407,10 +428,11 @@ always @* */ always @* case( state ) - DECODE: if( (~I & IRQ) | NMI_edge ) + DECODE: if( (~I & IRQ) | NMI_edge ) begin PC_inc = 0; - else + end else begin PC_inc = 1; + end ABS0, ABS1, @@ -434,12 +456,17 @@ always @* JMP1, JMP2, JMP3, + JSR4, + JSR5, + JSR6, RTI4, - RTS3: PC_inc = 1; + RTS3, + RTS4, + RTS5: PC_inc = 1; - JMPIX3: PC_inc = ~CO; // Don't increment PC if we are going to go through JMPIX4 + JMPIX3: PC_inc = {1'b0, ~CO}; // Don't increment PC if we are going to go through JMPIX4 - BRA1: PC_inc = CO ^~ backwards; + BRA1: PC_inc = {1'b0, CO ^~ backwards}; default: PC_inc = 0; endcase @@ -483,9 +510,13 @@ always @* BRK1, JSR1, + JSR2, + JSR3, PULL1, RTS1, RTS2, + RTS3, + RTS4, RTI1, RTI2, RTI3, @@ -612,11 +643,11 @@ always @* DECODE: write_register = load_reg & ~plp; PULL1, - RTS2, + RTS4, RTI3, BRK5, JSR0, - JSR2 : write_register = 1; + JSR4 : write_register = 1; default: write_register = 0; endcase @@ -703,13 +734,15 @@ always @* BRK5, JSR0, JSR2, + JSR3, + JSR4, PULL0, PULL1, PUSH1, RTI0, RTI3, RTS0, - RTS2 : regsel = SEL_S; + RTS4 : regsel = SEL_S; default: regsel = src_reg; endcase @@ -734,8 +767,30 @@ ALU ALU( .clk(clk), .RDY(RDY) ); always @(posedge clk) begin - alu_sr_0 <= ADD; - alu_sr_1 <= alu_sr_0; + if (alu_sr_enable) begin + if (sr_sel == SR_ALU) begin + alu_sr_0 <= ADD; + end else begin + alu_sr_0 <= DIMUX; + end + alu_sr_1 <= alu_sr_0; + end +end + +always @* begin + case ( state ) + RTS2, + RTS3: sr_sel = SR_DI; + + default: sr_sel = SR_ALU; + endcase +end + +always @*begin + case ( state) + RTS4: alu_sr_enable = 0; + default: alu_sr_enable = 1; + endcase end /* @@ -761,7 +816,9 @@ always @* BRK3, BRK4, JSR0, - JSR1: alu_op = OP_SUB; + JSR1, + JSR2, + JSR3: alu_op = OP_SUB; default: alu_op = OP_ADD; endcase @@ -791,7 +848,11 @@ always @(posedge clk) always @* case( state ) JSR1, + JSR2, + JSR3, RTS1, + RTS2, + RTS3, RTI1, RTI2, BRK1, @@ -806,8 +867,10 @@ always @* ABSX0, RTI0, RTS0, + RTS1, + RTS2, JSR0, - JSR2, + JSR4, BRK0, PULL0, INDY1, @@ -836,6 +899,8 @@ always @* case( state ) BRA1, RTS1, + RTS2, + RTS3, RTI0, RTI1, RTI2, @@ -843,6 +908,8 @@ always @* JSR0, JSR1, JSR2, + JSR3, + JSR4, BRK0, BRK1, BRK2, @@ -891,6 +958,8 @@ always @* RTI2, RTS0, RTS1, + RTS2, + RTS3, INDY0: CI = 1; default: CI = 0; @@ -1140,7 +1209,11 @@ always @(posedge clk or posedge reset) JSR0 : state <= JSR1; JSR1 : state <= JSR2; JSR2 : state <= JSR3; - JSR3 : state <= FETCH; + JSR3 : state <= JSR4; + JSR4 : state <= JSR5; + JSR5 : state <= JSR6; + JSR6 : state <= JSR7; + JSR7 : state <= FETCH; RTI0 : state <= RTI1; RTI1 : state <= RTI2; @@ -1151,7 +1224,9 @@ always @(posedge clk or posedge reset) RTS0 : state <= RTS1; RTS1 : state <= RTS2; RTS2 : state <= RTS3; - RTS3 : state <= FETCH; + RTS3 : state <= RTS4; + RTS4 : state <= RTS5; + RTS5 : state <= FETCH; BRA0 : state <= cond_true ? BRA1 : DECODE; BRA1 : state <= (CO ^ backwards) ? BRA2 : DECODE; -- 2.47.3 From d51994338598cb46356736a565cb45e0191df8bd Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Thu, 30 Apr 2026 22:24:31 -0700 Subject: [PATCH 12/17] Add RTI --- sim/verilog6502_32bit_test.py | 68 ++++++++++++++++++++++++++++++++++- src/cpu_65c02.v | 45 ++++++++++++++++------- 2 files changed, 99 insertions(+), 14 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index fd9ad00..66859bf 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -539,7 +539,73 @@ async def test_jsr(dut): await check_instruction_sequence(dut, expected_cpu_outputs) -# @cocotb.test +@cocotb.test +async def test_rti(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + write_dword(0xfffffffc, 0x300) + + + # @0x200 + # ldx #$ff + # txs + # brk + # wai + # @0x300 + # rti + + write_bytes(0x200, [0xa2, 0xff, 0x9a, 0x00, 0x00, 0xcb]) # BRK is technically a 2 byte instruction + write_bytes(0x300, [0x40]) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00000200, False, None), # ldx #$ff + (0x00000201, False, None), # immediate + (0x00000202, False, None), # txs + (0x00000203, False, None), # txs + (0x00000203, False, None), # brk + (0x00000204, False, None), # brk + (0x000001ff, True, 0x00), # brk 31-24 + (0x000001fe, True, 0x00), # brk 13-16 + (0x000001fd, True, 0x02), # brk 15-08 + (0x000001fc, True, 0x05), # brk 07-00 + (0x000001fb, True, 0xb4), # brk flags + (0xfffffffc, False, None), # vector + (0xfffffffd, False, None), # vector + (0xfffffffe, False, None), # vector + (0xffffffff, False, None), # vector + (0x00000300, False, None), # rti + (0x00000301, False, None), # rti + (0x000001fa, False, None), # rti + (0x000001fb, False, None), # rti + (0x000001fc, False, None), # rti + (0x000001fd, False, None), # rti + (0x000001fe, False, None), # rti + (0x000001ff, False, None), # rti + (0x00000205, False, None), # wai + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + + +@cocotb.test async def test_adc(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index cdb2eb0..e371654 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -257,7 +257,7 @@ parameter 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 + RTI6 = 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 @@ -292,7 +292,9 @@ parameter JSR6 = 7'd74, JSR7 = 7'd75, RTS3 = 7'd76, - RTS4 = 7'd77; + RTS4 = 7'd77, + RTI4 = 7'd78, + RTI5 = 7'd79; `ifdef SIM @@ -352,6 +354,8 @@ always @* RTI2: statename = "RTI2"; RTI3: statename = "RTI3"; RTI4: statename = "RTI4"; + RTI5: statename = "RTI5"; + RTI6: statename = "RTI6"; RTS0: statename = "RTS0"; RTS1: statename = "RTS1"; RTS2: statename = "RTS2"; @@ -406,10 +410,10 @@ always @* JMP3, JMPI3, JMPIX3, - JSR7: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1}; + JSR7, + RTI6: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1}; - RTS5, - RTI4: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1} + 2; + RTS5: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1} + 2; BRA1: PC_temp = { ABR[15:8], ADD }; @@ -459,7 +463,7 @@ always @* JSR4, JSR5, JSR6, - RTI4, + RTI6, RTS3, RTS4, RTS5: PC_inc = 1; @@ -493,7 +497,7 @@ always @* INDX5, JMP3, JMPI3, - RTI4, + RTI6, ABS3: AB = { DIMUX, ADD, alu_sr_0, alu_sr_1}; BRA2, @@ -520,6 +524,8 @@ always @* RTI1, RTI2, RTI3, + RTI4, + RTI5, BRK2, BRK3, BRK4: AB = { 16'h0, STACKPAGE, ADD }; @@ -644,7 +650,7 @@ always @* PULL1, RTS4, - RTI3, + RTI5, BRK5, JSR0, JSR4 : write_register = 1; @@ -740,7 +746,7 @@ always @* PULL1, PUSH1, RTI0, - RTI3, + RTI5, RTS0, RTS4 : regsel = SEL_S; @@ -780,7 +786,9 @@ end always @* begin case ( state ) RTS2, - RTS3: sr_sel = SR_DI; + RTS3, + RTI3, + RTI4: sr_sel = SR_DI; default: sr_sel = SR_ALU; endcase @@ -788,7 +796,8 @@ end always @*begin case ( state) - RTS4: alu_sr_enable = 0; + RTS4, + RTI5: alu_sr_enable = 0; default: alu_sr_enable = 1; endcase end @@ -855,6 +864,8 @@ always @* RTS3, RTI1, RTI2, + RTI3, + RTI4, BRK1, BRK2, BRK3, @@ -904,6 +915,8 @@ always @* RTI0, RTI1, RTI2, + RTI3, + RTI4, REG, JSR0, JSR1, @@ -913,6 +926,8 @@ always @* BRK0, BRK1, BRK2, + BRK3, + BRK4, PUSH0, PUSH1, PULL0, @@ -956,6 +971,8 @@ always @* RTI0, RTI1, RTI2, + RTI3, + RTI4, RTS0, RTS1, RTS2, @@ -1219,7 +1236,9 @@ always @(posedge clk or posedge reset) RTI1 : state <= RTI2; RTI2 : state <= RTI3; RTI3 : state <= RTI4; - RTI4 : state <= DECODE; + RTI4 : state <= RTI5; + RTI5 : state <= RTI6; + RTI6 : state <= DECODE; RTS0 : state <= RTS1; RTS1 : state <= RTS2; @@ -1268,7 +1287,7 @@ always @(posedge clk or posedge reset) REG, PUSH1, PULL2, - RTI4, + RTI6, JMP3, BRA2 : SYNC <= 1'b1; default: SYNC <= 1'b0; -- 2.47.3 From 79ce91669b6fdc8b18eb6f3e7dbb2644831da4dc Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Thu, 30 Apr 2026 22:59:21 -0700 Subject: [PATCH 13/17] Add IRQ test --- sim/verilog6502_32bit_test.py | 43 ++++++++++++++++++++++++++++++++++- src/cpu_65c02.v | 2 ++ 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 66859bf..ee06ad9 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -534,7 +534,6 @@ async def test_jsr(dut): (0x1234567c, False, None), # Updating PC before jump (0x00000208, False, None), # WAI (0x00000209, False, None), # second wai - (0x0000020a, False, None), # third wai ] await check_instruction_sequence(dut, expected_cpu_outputs) @@ -604,6 +603,48 @@ async def test_rti(dut): await check_instruction_sequence(dut, expected_cpu_outputs) +@cocotb.test +async def test_irq(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0x200) + write_dword(0xfffffffc, 0x300) + + # @0x200 + # cli + # wai + # wai + # 0x300 + # rti + + write_bytes(0x200, [0x58, 0xcb, 0xcb]) + write_bytes(0x300, [0x40]) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + await FallingEdge(dut.RDY_O) + + await RisingEdge(dut.clk) + await RisingEdge(dut.clk) + + dut.IRQ.value = 1 + + while True: + await RisingEdge(dut.clk) + if int(dut.state.value) == 0x08: + break + + dut.IRQ.value = 0 + + await Timer(300, "ns") + + assert int(dut.RDY_O.value) == 0 @cocotb.test async def test_adc(dut): diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index e371654..7816a93 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -434,6 +434,8 @@ always @* case( state ) DECODE: if( (~I & IRQ) | NMI_edge ) begin PC_inc = 0; + end else if (IR == 8'b1100_1011) begin + PC_inc = 0; end else begin PC_inc = 1; end -- 2.47.3 From 3c8df897699e613330c52c43909792770c46df7d Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Mon, 4 May 2026 23:00:32 -0700 Subject: [PATCH 14/17] Add branches --- sim/verilog6502_32bit_test.py | 83 +++++++++++++++++++++++++++++++++++ src/cpu_65c02.v | 60 ++++++++++++++++++------- 2 files changed, 128 insertions(+), 15 deletions(-) diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index ee06ad9..95f73c7 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -646,6 +646,89 @@ async def test_irq(dut): assert int(dut.RDY_O.value) == 0 +@cocotb.test +async def test_bra_always(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0xfffffd) + + # L1: bra L1 0x80 0x03 + # nop + # nop + # L2: bra L2 + + write_bytes(0xfffffd, [0x80, 0x3, 0x00, 0x00, 0x00, 0x80, 0xfe]) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + count = 0 + + async def count_address(): + nonlocal count + while True: + await RisingEdge(dut.clk) + if int(dut.AB.value) == 0x01000004: + count+=1 + + cocotb.start_soon(count_address()) + + await Timer(1, "us") + + assert count > 0 + +@cocotb.test +async def test_bra_never(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff4, 0xfffffd) + + # lda #$00 + # bne + # wai + + write_bytes(0xfffffd, [0xa9, 0x00, 0xd0, 0x7f, 0xd0, 0x80, 0xd0, 0xfe, 0xcb]) + + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + expected_cpu_outputs = [ + None, # ignore reset sequence + None, + None, + None, + None, + None, + None, + None, + None, + (0x00fffffd, False, None), + (0x00fffffe, False, None), + (0x00ffffff, False, None), + (0x01000000, False, None), + (0x01000001, False, None), + (0x01000002, False, None), + (0x01000003, False, None), + (0x01000004, False, None), + (0x01000005, False, None), + (0x01000006, False, None), + (0x01000006, False, None), + (0x01000006, False, None), + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + @cocotb.test async def test_adc(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index 7816a93..b9886d3 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -72,6 +72,7 @@ reg [31:0] ABR; // Address Bus Register wire [7:0] ADD; // Adder Hold Register (registered in ALU) reg [7:0] alu_sr_0; // ALU output shift register 0 reg [7:0] alu_sr_1; // ALU output shift register 1 +reg [7:0] alu_sr_2; // ALU output shift register 2 reg [7:0] DIHOLD; // Hold for Data In reg DIHOLD_valid; // @@ -294,7 +295,10 @@ parameter RTS3 = 7'd76, RTS4 = 7'd77, RTI4 = 7'd78, - RTI5 = 7'd79; + RTI5 = 7'd79, + BRA3 = 7'd80, + BRA4 = 7'd81, + BRA5 = 7'd82; `ifdef SIM @@ -371,6 +375,9 @@ always @* BRA0: statename = "BRA0"; BRA1: statename = "BRA1"; BRA2: statename = "BRA2"; + BRA3: statename = "BRA3"; + BRA4: statename = "BRA4"; + BRA5: statename = "BRA5"; JMP0: statename = "JMP0"; JMP1: statename = "JMP1"; JMP2: statename = "JMP2"; @@ -415,11 +422,12 @@ always @* RTS5: PC_temp = { DIMUX, ADD, alu_sr_0, alu_sr_1} + 2; - - BRA1: PC_temp = { ABR[15:8], ADD }; + BRA1: PC_temp = AB; + BRA2: PC_temp = AB; + BRA3: PC_temp = AB; + BRA4: PC_temp = AB; JMPIX4, - BRA2: PC_temp = { ADD, PC[7:0] }; // TODO BRK4: PC_temp = res ? 32'hFFFFFFF4 : NMI_edge ? 32'hFFFFFFF8 : 32'hFFFFFFFC; @@ -452,7 +460,11 @@ always @* ABSX2, FETCH, BRA0, + BRA1, BRA2, + BRA3, + BRA4, + BRA5, BRK5, JMPI0, JMPI1, @@ -502,11 +514,13 @@ always @* RTI6, ABS3: AB = { DIMUX, ADD, alu_sr_0, alu_sr_1}; - BRA2, JMPIX4, ABSX4: AB = { ADD, ABR[23:0] }; // TODO - BRA1: AB = { ABR[15:8], ADD }; // TODO + BRA1: AB = { ABR[31:8], ADD }; + BRA2: AB = { ABR[31:16], ADD, alu_sr_0 }; + BRA3: AB = { ABR[31:24], ADD, alu_sr_0, alu_sr_1 }; + BRA4: AB = { ADD, alu_sr_0, alu_sr_1, alu_sr_2 }; JSR0, PUSH1, @@ -782,6 +796,7 @@ always @(posedge clk) begin alu_sr_0 <= DIMUX; end alu_sr_1 <= alu_sr_0; + alu_sr_2 <= alu_sr_1; end end @@ -812,7 +827,10 @@ always @* case( state ) READ: alu_op = op; - BRA1: alu_op = backwards ? OP_SUB : OP_ADD; + BRA1, + BRA2, + BRA3, + BRA4: alu_op = OP_ADD; FETCH, REG : alu_op = op; @@ -849,7 +867,7 @@ always @* */ always @(posedge clk) - if( RDY ) + if( RDY && state == BRA0) backwards <= DIMUX[7]; /* @@ -893,7 +911,10 @@ always @* BRA0, READ: AI = DIMUX; - BRA1: AI = ABR[15:8]; // don't use PCH in case we're TODO + BRA1, + BRA2, + BRA3, + BRA4: AI = backwards ? 8'hff : 0; FETCH: AI = load_only ? 8'b0 : regfile; @@ -910,7 +931,6 @@ always @* always @* case( state ) - BRA1, RTS1, RTS2, RTS3, @@ -937,7 +957,10 @@ always @* READ: BI = txb_ins ? (trb_ins ? ~regfile : regfile) : 8'h00; - BRA0: BI = PC[7:0]; // TODO + BRA0: BI = ABR[7:0]; + BRA1: BI = ABR[15:8]; + BRA2: BI = ABR[23:16]; + BRA3: BI = ABR[31:24]; DECODE, ABS3: BI = 8'hxx; @@ -953,6 +976,8 @@ always @* case( state ) INDY2, BRA1, + BRA2, + BRA3, JMPIX1, ABSX1, ABSX2, @@ -1250,8 +1275,11 @@ always @(posedge clk or posedge reset) RTS5 : state <= FETCH; BRA0 : state <= cond_true ? BRA1 : DECODE; - BRA1 : state <= (CO ^ backwards) ? BRA2 : DECODE; - BRA2 : state <= DECODE; + BRA1 : state <= (CO ^ backwards) ? BRA2 : BRA5; + BRA2 : state <= (CO ^ backwards) ? BRA3 : BRA5; + BRA3 : state <= (CO ^ backwards) ? BRA4 : BRA5; + BRA4 : state <= BRA5; + BRA5 : state <= DECODE; JMP0 : state <= JMP1; JMP1 : state <= JMP2; @@ -1284,14 +1312,16 @@ always @(posedge clk or posedge reset) else if( RDY ) case( state ) BRA0 : SYNC <= !cond_true; BRA1 : SYNC <= !(CO ^ backwards); - BRA2, + BRA2 : SYNC <= !(CO ^ backwards); + BRA3 : SYNC <= !(CO ^ backwards); + BRA4 : SYNC <= !(CO ^ backwards); FETCH, REG, PUSH1, PULL2, RTI6, JMP3, - BRA2 : SYNC <= 1'b1; + BRA5 : SYNC <= 1'b1; default: SYNC <= 1'b0; endcase -- 2.47.3 From c4d91a1994e2eec907905794ef5549ed761621fc Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Fri, 8 May 2026 09:54:14 -0700 Subject: [PATCH 15/17] Add cc65 code --- .gitignore | 5 ++- init_env.sh | 4 +- sim/asm_source/Makefile | 29 +++++++++++++ sim/asm_source/jsr_test.s | 11 +++++ sim/asm_source/lda_test.s | 68 +++++++++++++++++++++++++++++++ sim/asm_source/memory.cfg | 13 ++++++ sim/asm_source/vectors.s | 7 ++++ sim/verilog6502_32bit_asm.yaml | 9 ++++ sim/verilog6502_32bit_asm_test.py | 62 ++++++++++++++++++++++++++++ sim/verilog6502_32bit_test.py | 36 ++++++++-------- src/cpu_65c02.v | 4 +- 11 files changed, 226 insertions(+), 22 deletions(-) create mode 100644 sim/asm_source/Makefile create mode 100644 sim/asm_source/jsr_test.s create mode 100644 sim/asm_source/lda_test.s create mode 100644 sim/asm_source/memory.cfg create mode 100644 sim/asm_source/vectors.s create mode 100644 sim/verilog6502_32bit_asm.yaml create mode 100644 sim/verilog6502_32bit_asm_test.py diff --git a/.gitignore b/.gitignore index 0eb1c57..1cfdb72 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,5 @@ sim_build -__pycache__ \ No newline at end of file +__pycache__ + +*.o +*.lst \ No newline at end of file diff --git a/init_env.sh b/init_env.sh index 820e49f..d868ec8 100644 --- a/init_env.sh +++ b/init_env.sh @@ -7,4 +7,6 @@ pip install -r requirements.txt export PYTHON3=$(which python) -module load verilator \ No newline at end of file +module load verilator + +export CC65_BIN="$REPO_TOP/../cc65/bin/" \ No newline at end of file diff --git a/sim/asm_source/Makefile b/sim/asm_source/Makefile new file mode 100644 index 0000000..3facd4c --- /dev/null +++ b/sim/asm_source/Makefile @@ -0,0 +1,29 @@ + + +# work in progress +.SUFFIXES: + + +PROGRAMS=jsr_test lda_test + +SRCS=$(wildcard *.s) +OBJS=$(SRCS:.s=.o) + +CA65=$(CC65_BIN)/ca65 +LD65=$(CC65_BIN)/ld65 + +CA_ARGS=--cpu 65c032 + +all: $(PROGRAMS) + +$(PROGRAMS): $(PROGRAM=$(.TARGET)) + +%.o: %.s + $(CA65) $(CA_ARGS) $^ -o $@ -l $@.lst + +$(PROGRAMS): $(OBJS) + $(LD65) -o $@ -C memory.cfg vectors.o $@.o + +clean: + rm -rf $(PROGRAMS) $(OBJS) + rm -rf *.o *.lst \ No newline at end of file diff --git a/sim/asm_source/jsr_test.s b/sim/asm_source/jsr_test.s new file mode 100644 index 0000000..98d4ca1 --- /dev/null +++ b/sim/asm_source/jsr_test.s @@ -0,0 +1,11 @@ +.export vec_reset, vec_irq, vec_nmi + +.segment "CODE" + +vec_nmi: +vec_reset: +vec_irq: + + +jsr_test: + bra jsr_test \ No newline at end of file diff --git a/sim/asm_source/lda_test.s b/sim/asm_source/lda_test.s new file mode 100644 index 0000000..ebd994e --- /dev/null +++ b/sim/asm_source/lda_test.s @@ -0,0 +1,68 @@ +.export vec_reset, vec_irq, vec_nmi + +.ZEROPAGE + +zp0: .res 1 +zp1: .res 4 +zp2: .res 8 +zp3: .res 4 + +.CODE + +data1: .byte 1 +data2: .byte 2 +data3: .byte 3 +data4: .byte 4 +data5: .byte 5 +data6: .res 4 + .byte 6 +data7: .res 2 + .byte 7 + +vec_nmi: +vec_reset: +vec_irq: + + +prepare_test: + lda data1 + sta zp0 + + lda #.LOBYTE(data2) + sta zp1 + lda #.HIBYTE(data2) + sta zp1+1 + lda #.BANKBYTE(data2) + sta zp1+2 + lda #.TOPBYTE(data2) + sta zp1+3 + + lda #.LOBYTE(data4) + sta zp2+4 + lda #.HIBYTE(data4) + sta zp2+5 + lda #.BANKBYTE(data4) + sta zp2+6 + lda #.TOPBYTE(data4) + sta zp2+7 + + lda #.LOBYTE(data5-2) + sta zp3 + lda #.HIBYTE(data5-2) + sta zp3+1 + lda #.BANKBYTE(data5-2) + sta zp3+2 + lda #.TOPBYTE(data5-2) + sta zp3+3 + +lda_test: + lda zp0 ; data 1 + lda (zp1) ; data 2 + lda data3 ; data 3 + ldx #$4 + ldy #$2 + lda (zp2,x) ; data 4 + lda (zp3),y ; data 5 + lda data6,x ; data 6 + lda data7,y ; data 7 + wai \ No newline at end of file diff --git a/sim/asm_source/memory.cfg b/sim/asm_source/memory.cfg new file mode 100644 index 0000000..3f4d098 --- /dev/null +++ b/sim/asm_source/memory.cfg @@ -0,0 +1,13 @@ +MEMORY { + ZP: start = $0, size = $100; + RAM: start = $fffff000, size = $1000, file=%O; +} + +SEGMENTS { + ZEROPAGE: load = ZP, type = zp; + CODE: load = RAM, type = ro; + RODATA: load = RAM, type = ro; + DATA: load = RAM, type = rw; + BSS: load = RAM, type = bss, define = yes; + VECTORS: load = RAM, type = ro, start = $FFFFFFF4; +} \ No newline at end of file diff --git a/sim/asm_source/vectors.s b/sim/asm_source/vectors.s new file mode 100644 index 0000000..cee7ebd --- /dev/null +++ b/sim/asm_source/vectors.s @@ -0,0 +1,7 @@ +.import vec_reset, vec_irq, vec_nmi + +.segment "VECTORS": dword + +.addr vec_nmi +.addr vec_reset +.addr vec_irq \ No newline at end of file diff --git a/sim/verilog6502_32bit_asm.yaml b/sim/verilog6502_32bit_asm.yaml new file mode 100644 index 0000000..c4aec09 --- /dev/null +++ b/sim/verilog6502_32bit_asm.yaml @@ -0,0 +1,9 @@ +tests: + - name: "cpu_65c02" + toplevel: "cpu_65c02" + modules: + - "verilog6502_32bit_asm_test" + sources: "sources.list" + waves: True + defines: + SIM: "hi" \ No newline at end of file diff --git a/sim/verilog6502_32bit_asm_test.py b/sim/verilog6502_32bit_asm_test.py new file mode 100644 index 0000000..28bfaf7 --- /dev/null +++ b/sim/verilog6502_32bit_asm_test.py @@ -0,0 +1,62 @@ +import cocotb +from cocotb.handle import Immediate + +from cocotb.clock import Clock +from cocotb.triggers import Timer, RisingEdge, FallingEdge + +from collections import defaultdict + +import struct +import random + +import os + +CLK_PERIOD = 5 + +memory = defaultdict(int) + +def write_dword(addr: int, data: int): + memory[addr + 0] = (data >> 0) & 0xff + memory[addr + 1] = (data >> 8) & 0xff + memory[addr + 2] = (data >> 16) & 0xff + memory[addr + 3] = (data >> 24) & 0xff + +def write_byte(addr: int, data: int): + memory[addr] = data & 0xff + +def write_bytes(addr: int, data: bytes| list[int]): + for i, val in enumerate(data): + memory[addr + i] = int(val) + +async def handle_memory(dut): + while True: + await RisingEdge(dut.clk) + addr = int(dut.AB.value) + we = bool(dut.WE.value) + + dut.DI.value = memory[addr] + + if we: + memory[addr] = int(dut.DO.value) + +@cocotb.test +async def test_lda(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + path = os.path.dirname(os.path.abspath(__file__)) + + base_addr = 0xfffff000 + + with open(f"{path}/asm_source/lda_test", "rb") as file: + for i, val in enumerate(file.read()): + write_byte(base_addr+i, val) + + dut.RDY.value = Immediate(1) + + dut.reset.value = Immediate(1) + for _ in range(10): + await RisingEdge(dut.clk) + dut.reset.value = 0 + + await FallingEdge(dut.RDY_O) \ No newline at end of file diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py index 95f73c7..f43331b 100644 --- a/sim/verilog6502_32bit_test.py +++ b/sim/verilog6502_32bit_test.py @@ -58,7 +58,7 @@ async def test_reset(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x12345678) + write_dword(0xfffffff8, 0x12345678) dut.RDY.value = Immediate(1) @@ -73,10 +73,10 @@ async def test_reset(dut): (0x000001fe, True, (int(dut.PC.value) >> 8) & 0xff), # Mid low addr (0x000001fd, True, (int(dut.PC.value) >> 0) & 0xff), # Low addr (0x000001fc, True, int(dut.P.value)), # Status - (0xfffffff4, False, int(dut.regfile.value)), # read vector byte 0 - (0xfffffff5, False, int(dut.regfile.value)), # read vector byte 1 - (0xfffffff6, False, int(dut.regfile.value)), # read vector byte 2 - (0xfffffff7, False, int(dut.regfile.value)), # read vector byte 3 + (0xfffffff8, False, int(dut.regfile.value)), # read vector byte 0 + (0xfffffff9, False, int(dut.regfile.value)), # read vector byte 1 + (0xfffffffa, False, int(dut.regfile.value)), # read vector byte 2 + (0xfffffffb, False, int(dut.regfile.value)), # read vector byte 3 (0x12345678, False, int(dut.regfile.value)), # Read first instruction (0x12345679, False, int(dut.regfile.value)), # Read second byte ] @@ -88,7 +88,7 @@ async def test_absolute(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) # lda $abcd1234 # sta $50515253 @@ -137,7 +137,7 @@ async def test_absolute_x(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) # ldx #1 # lda $abcd1234,x @@ -196,7 +196,7 @@ async def test_absolute_y(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) # ldy #1 # lda $abcd1234,y @@ -255,7 +255,7 @@ 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) + write_dword(0xfffffff8, 0x200) # ldx #1 # jmp ($deadbeef,x) @@ -304,7 +304,7 @@ async def test_indirect(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) # jmp ($deadbeef) write_bytes(0x200, [0x6c, 0xef, 0xbe, 0xad, 0xde]) @@ -348,7 +348,7 @@ async def test_indexed_indirect(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) # ldy #1 # lda ($04),y @@ -412,7 +412,7 @@ async def test_indirect_indexed(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) # ldx #4 @@ -480,7 +480,7 @@ async def test_jsr(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) # @0x200 # ldx #$0 @@ -543,7 +543,7 @@ async def test_rti(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) write_dword(0xfffffffc, 0x300) @@ -608,7 +608,7 @@ async def test_irq(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) write_dword(0xfffffffc, 0x300) # @0x200 @@ -651,7 +651,7 @@ async def test_bra_always(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0xfffffd) + write_dword(0xfffffff8, 0xfffffd) # L1: bra L1 0x80 0x03 # nop @@ -687,7 +687,7 @@ async def test_bra_never(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0xfffffd) + write_dword(0xfffffff8, 0xfffffd) # lda #$00 # bne @@ -734,7 +734,7 @@ async def test_adc(dut): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) - write_dword(0xfffffff4, 0x200) + write_dword(0xfffffff8, 0x200) def zp_indirect(): diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index b9886d3..61fa6f5 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -429,8 +429,8 @@ always @* JMPIX4, - BRK4: PC_temp = res ? 32'hFFFFFFF4 : - NMI_edge ? 32'hFFFFFFF8 : 32'hFFFFFFFC; + BRK4: PC_temp = res ? 32'hFFFFFFF8 : + NMI_edge ? 32'hFFFFFFFC : 32'hFFFFFFFC; default: PC_temp = PC; endcase -- 2.47.3 From 6c47ce12a5c27638f76e0cec6abe852cb5ea101e Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Fri, 8 May 2026 22:07:13 -0700 Subject: [PATCH 16/17] Update assembly tests --- sim/asm_source/jsr_test.s | 23 +++++++++++++- sim/asm_source/lda_test.s | 53 +++++++++++++++++++++++++++++++ sim/verilog6502_32bit_asm_test.py | 19 ++++++++--- 3 files changed, 89 insertions(+), 6 deletions(-) diff --git a/sim/asm_source/jsr_test.s b/sim/asm_source/jsr_test.s index 98d4ca1..ceb5b92 100644 --- a/sim/asm_source/jsr_test.s +++ b/sim/asm_source/jsr_test.s @@ -1,5 +1,9 @@ .export vec_reset, vec_irq, vec_nmi +.ZEROPAGE + +result: .res 1 + .segment "CODE" vec_nmi: @@ -8,4 +12,21 @@ vec_irq: jsr_test: - bra jsr_test \ No newline at end of file + lda #$ff + sta result + ldx #$ff + txs + jsr function_1 + lda #$01 + sta result + wai + +function_2: + pha + pla + rts + +function_1: + jsr function_2 + rts + diff --git a/sim/asm_source/lda_test.s b/sim/asm_source/lda_test.s index ebd994e..94faf1d 100644 --- a/sim/asm_source/lda_test.s +++ b/sim/asm_source/lda_test.s @@ -2,11 +2,15 @@ .ZEROPAGE +result: .res 1 + zp0: .res 1 zp1: .res 4 zp2: .res 8 zp3: .res 4 +good_count: .res 1 + .CODE data1: .byte 1 @@ -55,14 +59,63 @@ prepare_test: lda #.TOPBYTE(data5-2) sta zp3+3 + stz good_count + lda_test: +@test1: lda zp0 ; data 1 + cmp #$1 + bne @test2 + inc good_count + +@test2: lda (zp1) ; data 2 + cmp #$2 + bne @test3 + inc good_count + +@test3: lda data3 ; data 3 + cmp #$3 + bne @test4 + inc good_count + +@test4: ldx #$4 ldy #$2 lda (zp2,x) ; data 4 + cmp #$4 + bne @test5 + inc good_count + +@test5: lda (zp3),y ; data 5 + cmp #$5 + bne @test6 + inc good_count + +@test6: lda data6,x ; data 6 + cmp #$6 + bne @test7 + inc good_count + +@test7: lda data7,y ; data 7 + cmp #$7 + bne @done + inc good_count + +@done: + lda good_count + cmp #$7 + bne @fail + + lda #$1 + sta result + wai + +@fail: + lda #$ff + sta result wai \ No newline at end of file diff --git a/sim/verilog6502_32bit_asm_test.py b/sim/verilog6502_32bit_asm_test.py index 28bfaf7..f47a83d 100644 --- a/sim/verilog6502_32bit_asm_test.py +++ b/sim/verilog6502_32bit_asm_test.py @@ -2,7 +2,7 @@ import cocotb from cocotb.handle import Immediate from cocotb.clock import Clock -from cocotb.triggers import Timer, RisingEdge, FallingEdge +from cocotb.triggers import Timer, RisingEdge, FallingEdge, with_timeout from collections import defaultdict @@ -39,8 +39,7 @@ async def handle_memory(dut): if we: memory[addr] = int(dut.DO.value) -@cocotb.test -async def test_lda(dut): +async def do_asm_test(dut, filename): cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) cocotb.start_soon(handle_memory(dut)) @@ -48,7 +47,7 @@ async def test_lda(dut): base_addr = 0xfffff000 - with open(f"{path}/asm_source/lda_test", "rb") as file: + with open(f"{path}/asm_source/{filename}", "rb") as file: for i, val in enumerate(file.read()): write_byte(base_addr+i, val) @@ -59,4 +58,14 @@ async def test_lda(dut): await RisingEdge(dut.clk) dut.reset.value = 0 - await FallingEdge(dut.RDY_O) \ No newline at end of file + await with_timeout(FallingEdge(dut.RDY_O), 10, "us") + + assert memory[0] == 1 + +@cocotb.test +async def test_lda(dut): + await do_asm_test(dut, "lda_test") + +@cocotb.test +async def test_jsr(dut): + await do_asm_test(dut, "jsr_test") \ No newline at end of file -- 2.47.3 From 7827bcceeb01c30f331b75b9377cdc1e075acb93 Mon Sep 17 00:00:00 2001 From: Byron Lathi Date: Fri, 8 May 2026 23:02:39 -0700 Subject: [PATCH 17/17] Update wrapper to work with 32 bit addresses --- sim/verilog6502_wrapper_test.py | 3 +- src/cpu_65c02.v | 17 ++-- src/regs/verilog6502_io_regs.rdl | 39 +++++--- src/regs/verilog6502_io_regs.sv | 132 +++++++++++++++------------- src/regs/verilog6502_io_regs_pkg.sv | 43 +++++---- src/verilog6502_addr_decoder.sv | 16 ++-- src/verilog6502_apb_adapter.sv | 18 ++-- src/verilog6502_external_memory.sv | 6 +- src/verilog6502_internal_memory.sv | 2 +- src/verilog6502_wrapper.sv | 22 ++--- 10 files changed, 164 insertions(+), 134 deletions(-) diff --git a/sim/verilog6502_wrapper_test.py b/sim/verilog6502_wrapper_test.py index f24348e..91f4500 100644 --- a/sim/verilog6502_wrapper_test.py +++ b/sim/verilog6502_wrapper_test.py @@ -33,7 +33,8 @@ async def test_sanity(dut): await RisingEdge(dut.clk) # await s_axi.write(0x200, [0x58, 0xa9, 0x00, 0x1a, 0xcb, 0x4c, 0x03, 0x02]) - await s_axi.write(0x200, [0xAD, 0x00, 0xE0, 0xAD, 0x01, 0xE0, 0xAD, 0x02, 0xE0, 0xAD, 0x03, 0xE0, 0xAD, 0x04, 0xE0, 0xCB]) + # await s_axi.write(0x200, [0xAD, 0x00, 0xE0, 0xAD, 0x01, 0xE0, 0xAD, 0x02, 0xE0, 0xAD, 0x03, 0xE0, 0xAD, 0x04, 0xE0, 0xCB]) + await s_axi.write(0x200, [0x80, 0xfe]) cocotb.start_soon(s_axi.read(0x200, 8)) diff --git a/src/cpu_65c02.v b/src/cpu_65c02.v index 61fa6f5..2ca5ced 100644 --- a/src/cpu_65c02.v +++ b/src/cpu_65c02.v @@ -789,15 +789,16 @@ ALU ALU( .clk(clk), .RDY(RDY) ); always @(posedge clk) begin - if (alu_sr_enable) begin - if (sr_sel == SR_ALU) begin - alu_sr_0 <= ADD; - end else begin - alu_sr_0 <= DIMUX; + if( RDY ) + if (alu_sr_enable) begin + if (sr_sel == SR_ALU) begin + alu_sr_0 <= ADD; + end else begin + alu_sr_0 <= DIMUX; + end + alu_sr_1 <= alu_sr_0; + alu_sr_2 <= alu_sr_1; end - alu_sr_1 <= alu_sr_0; - alu_sr_2 <= alu_sr_1; - end end always @* begin diff --git a/src/regs/verilog6502_io_regs.rdl b/src/regs/verilog6502_io_regs.rdl index 7aeefef..59000b3 100644 --- a/src/regs/verilog6502_io_regs.rdl +++ b/src/regs/verilog6502_io_regs.rdl @@ -12,21 +12,28 @@ addrmap verilog6502_io_regs { hw = r; sw = rw; } reset[0:0] = 0x1; + + field { + name = "rdy"; + desc = ""; + hw = r; + sw = rw; + } rdy[1:1] = 0x0; } core_ctrl @ 0x0; reg { - name = "AXI Base Address"; + name = "Core Status"; desc = ""; - + field { - name = "val"; + name = "rdy_o"; desc = ""; hw = r; - sw = rw; - } val[31:0] = 0x0; - - } axi_base_address @ 0x10; + sw = r; + } rdy_o[0:0] = 0x0; + + } core_status @ 0x4; reg { name = "nmi"; @@ -36,11 +43,11 @@ addrmap verilog6502_io_regs { desc = ""; hw = r; sw = rw; - } nmi[31:16] = 0x200; - } nmi @ 0xff8; + } nmi[31:0] = 0x200; + } nmi @ 0xff4; reg { - name = "reset_brq"; + name = "reset"; desc = ""; field { @@ -48,13 +55,17 @@ addrmap verilog6502_io_regs { desc = ""; hw = r; sw = rw; - } reset[15:0] = 0x200; - + } reset[31:0] = 0x200; + } rst @ 0xff8; + + reg { + name = "brk"; + field { name = "brq"; desc = ""; hw = r; sw = rw; - } brk[31:16] = 0x200; - } reset_brq @ 0xffc; + } brk[31:0] = 0x200; + } brk @ 0xffc; }; \ No newline at end of file diff --git a/src/regs/verilog6502_io_regs.sv b/src/regs/verilog6502_io_regs.sv index 147881d..9ef75ef 100644 --- a/src/regs/verilog6502_io_regs.sv +++ b/src/regs/verilog6502_io_regs.sv @@ -87,9 +87,10 @@ module verilog6502_io_regs ( //-------------------------------------------------------------------------- typedef struct { logic core_ctrl; - logic axi_base_address; + logic core_status; logic nmi; - logic reset_brq; + logic rst; + logic brk; } decoded_reg_strb_t; decoded_reg_strb_t decoded_reg_strb; logic decoded_err; @@ -105,9 +106,10 @@ module verilog6502_io_regs ( is_valid_addr = '1; // No valid address check is_valid_rw = '1; // No valid RW check decoded_reg_strb.core_ctrl = cpuif_req_masked & (cpuif_addr == 12'h0); - decoded_reg_strb.axi_base_address = cpuif_req_masked & (cpuif_addr == 12'h10); - decoded_reg_strb.nmi = cpuif_req_masked & (cpuif_addr == 12'hff8); - decoded_reg_strb.reset_brq = cpuif_req_masked & (cpuif_addr == 12'hffc); + decoded_reg_strb.core_status = cpuif_req_masked & (cpuif_addr == 12'h4) & !cpuif_req_is_wr; + decoded_reg_strb.nmi = cpuif_req_masked & (cpuif_addr == 12'hff4); + decoded_reg_strb.rst = cpuif_req_masked & (cpuif_addr == 12'hff8); + decoded_reg_strb.brk = cpuif_req_masked & (cpuif_addr == 12'hffc); decoded_err = '0; end @@ -127,29 +129,29 @@ module verilog6502_io_regs ( logic next; logic load_next; } reset; + struct { + logic next; + logic load_next; + } rdy; } core_ctrl; struct { struct { logic [31:0] next; logic load_next; - } val; - } axi_base_address; - struct { - struct { - logic [15:0] next; - logic load_next; } nmi; } nmi; struct { struct { - logic [15:0] next; + logic [31:0] next; logic load_next; } reset; + } rst; + struct { struct { - logic [15:0] next; + logic [31:0] next; logic load_next; } brk; - } reset_brq; + } brk; } field_combo_t; field_combo_t field_combo; @@ -158,25 +160,25 @@ module verilog6502_io_regs ( struct { logic value; } reset; + struct { + logic value; + } rdy; } core_ctrl; struct { struct { logic [31:0] value; - } val; - } axi_base_address; - struct { - struct { - logic [15:0] value; } nmi; } nmi; struct { struct { - logic [15:0] value; + logic [31:0] value; } reset; + } rst; + struct { struct { - logic [15:0] value; + logic [31:0] value; } brk; - } reset_brq; + } brk; } field_storage_t; field_storage_t field_storage; @@ -203,37 +205,38 @@ module verilog6502_io_regs ( end end assign hwif_out.core_ctrl.reset.value = field_storage.core_ctrl.reset.value; - // Field: verilog6502_io_regs.axi_base_address.val + // Field: verilog6502_io_regs.core_ctrl.rdy always_comb begin - automatic logic [31:0] next_c; + automatic logic [0:0] next_c; automatic logic load_next_c; - next_c = field_storage.axi_base_address.val.value; + next_c = field_storage.core_ctrl.rdy.value; load_next_c = '0; - if(decoded_reg_strb.axi_base_address && decoded_req_is_wr) begin // SW write - next_c = (field_storage.axi_base_address.val.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); + if(decoded_reg_strb.core_ctrl && decoded_req_is_wr) begin // SW write + next_c = (field_storage.core_ctrl.rdy.value & ~decoded_wr_biten[1:1]) | (decoded_wr_data[1:1] & decoded_wr_biten[1:1]); load_next_c = '1; end - field_combo.axi_base_address.val.next = next_c; - field_combo.axi_base_address.val.load_next = load_next_c; + field_combo.core_ctrl.rdy.next = next_c; + field_combo.core_ctrl.rdy.load_next = load_next_c; end always_ff @(posedge clk) begin if(rst) begin - field_storage.axi_base_address.val.value <= 32'h0; + field_storage.core_ctrl.rdy.value <= 1'h0; end else begin - if(field_combo.axi_base_address.val.load_next) begin - field_storage.axi_base_address.val.value <= field_combo.axi_base_address.val.next; + if(field_combo.core_ctrl.rdy.load_next) begin + field_storage.core_ctrl.rdy.value <= field_combo.core_ctrl.rdy.next; end end end - assign hwif_out.axi_base_address.val.value = field_storage.axi_base_address.val.value; + assign hwif_out.core_ctrl.rdy.value = field_storage.core_ctrl.rdy.value; + assign hwif_out.core_status.rdy_o.value = 1'h0; // Field: verilog6502_io_regs.nmi.nmi always_comb begin - automatic logic [15:0] next_c; + automatic logic [31:0] next_c; automatic logic load_next_c; next_c = field_storage.nmi.nmi.value; load_next_c = '0; if(decoded_reg_strb.nmi && decoded_req_is_wr) begin // SW write - next_c = (field_storage.nmi.nmi.value & ~decoded_wr_biten[31:16]) | (decoded_wr_data[31:16] & decoded_wr_biten[31:16]); + next_c = (field_storage.nmi.nmi.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); load_next_c = '1; end field_combo.nmi.nmi.next = next_c; @@ -241,7 +244,7 @@ module verilog6502_io_regs ( end always_ff @(posedge clk) begin if(rst) begin - field_storage.nmi.nmi.value <= 16'h200; + field_storage.nmi.nmi.value <= 32'h200; end else begin if(field_combo.nmi.nmi.load_next) begin field_storage.nmi.nmi.value <= field_combo.nmi.nmi.next; @@ -249,52 +252,52 @@ module verilog6502_io_regs ( end end assign hwif_out.nmi.nmi.value = field_storage.nmi.nmi.value; - // Field: verilog6502_io_regs.reset_brq.reset + // Field: verilog6502_io_regs.rst.reset always_comb begin - automatic logic [15:0] next_c; + automatic logic [31:0] next_c; automatic logic load_next_c; - next_c = field_storage.reset_brq.reset.value; + next_c = field_storage.rst.reset.value; load_next_c = '0; - if(decoded_reg_strb.reset_brq && decoded_req_is_wr) begin // SW write - next_c = (field_storage.reset_brq.reset.value & ~decoded_wr_biten[15:0]) | (decoded_wr_data[15:0] & decoded_wr_biten[15:0]); + if(decoded_reg_strb.rst && decoded_req_is_wr) begin // SW write + next_c = (field_storage.rst.reset.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); load_next_c = '1; end - field_combo.reset_brq.reset.next = next_c; - field_combo.reset_brq.reset.load_next = load_next_c; + field_combo.rst.reset.next = next_c; + field_combo.rst.reset.load_next = load_next_c; end always_ff @(posedge clk) begin if(rst) begin - field_storage.reset_brq.reset.value <= 16'h200; + field_storage.rst.reset.value <= 32'h200; end else begin - if(field_combo.reset_brq.reset.load_next) begin - field_storage.reset_brq.reset.value <= field_combo.reset_brq.reset.next; + if(field_combo.rst.reset.load_next) begin + field_storage.rst.reset.value <= field_combo.rst.reset.next; end end end - assign hwif_out.reset_brq.reset.value = field_storage.reset_brq.reset.value; - // Field: verilog6502_io_regs.reset_brq.brk + assign hwif_out.rst.reset.value = field_storage.rst.reset.value; + // Field: verilog6502_io_regs.brk.brk always_comb begin - automatic logic [15:0] next_c; + automatic logic [31:0] next_c; automatic logic load_next_c; - next_c = field_storage.reset_brq.brk.value; + next_c = field_storage.brk.brk.value; load_next_c = '0; - if(decoded_reg_strb.reset_brq && decoded_req_is_wr) begin // SW write - next_c = (field_storage.reset_brq.brk.value & ~decoded_wr_biten[31:16]) | (decoded_wr_data[31:16] & decoded_wr_biten[31:16]); + if(decoded_reg_strb.brk && decoded_req_is_wr) begin // SW write + next_c = (field_storage.brk.brk.value & ~decoded_wr_biten[31:0]) | (decoded_wr_data[31:0] & decoded_wr_biten[31:0]); load_next_c = '1; end - field_combo.reset_brq.brk.next = next_c; - field_combo.reset_brq.brk.load_next = load_next_c; + field_combo.brk.brk.next = next_c; + field_combo.brk.brk.load_next = load_next_c; end always_ff @(posedge clk) begin if(rst) begin - field_storage.reset_brq.brk.value <= 16'h200; + field_storage.brk.brk.value <= 32'h200; end else begin - if(field_combo.reset_brq.brk.load_next) begin - field_storage.reset_brq.brk.value <= field_combo.reset_brq.brk.next; + if(field_combo.brk.brk.load_next) begin + field_storage.brk.brk.value <= field_combo.brk.brk.next; end end end - assign hwif_out.reset_brq.brk.value = field_storage.reset_brq.brk.value; + assign hwif_out.brk.brk.value = field_storage.brk.brk.value; //-------------------------------------------------------------------------- // Write response @@ -318,16 +321,19 @@ module verilog6502_io_regs ( readback_data_var = '0; if(rd_mux_addr == 12'h0) begin readback_data_var[0] = field_storage.core_ctrl.reset.value; + readback_data_var[1] = field_storage.core_ctrl.rdy.value; end - if(rd_mux_addr == 12'h10) begin - readback_data_var[31:0] = field_storage.axi_base_address.val.value; + if(rd_mux_addr == 12'h4) begin + readback_data_var[0] = 1'h0; + end + if(rd_mux_addr == 12'hff4) begin + readback_data_var[31:0] = field_storage.nmi.nmi.value; end if(rd_mux_addr == 12'hff8) begin - readback_data_var[31:16] = field_storage.nmi.nmi.value; + readback_data_var[31:0] = field_storage.rst.reset.value; end if(rd_mux_addr == 12'hffc) begin - readback_data_var[15:0] = field_storage.reset_brq.reset.value; - readback_data_var[31:16] = field_storage.reset_brq.brk.value; + readback_data_var[31:0] = field_storage.brk.brk.value; end readback_data = readback_data_var; readback_done = decoded_req & ~decoded_req_is_wr; diff --git a/src/regs/verilog6502_io_regs_pkg.sv b/src/regs/verilog6502_io_regs_pkg.sv index d533c72..75de506 100644 --- a/src/regs/verilog6502_io_regs_pkg.sv +++ b/src/regs/verilog6502_io_regs_pkg.sv @@ -11,20 +11,25 @@ package verilog6502_io_regs_pkg; logic value; } verilog6502_io_regs__core_ctrl__reset__out_t; + typedef struct { + logic value; + } verilog6502_io_regs__core_ctrl__rdy__out_t; + typedef struct { verilog6502_io_regs__core_ctrl__reset__out_t reset; + verilog6502_io_regs__core_ctrl__rdy__out_t rdy; } verilog6502_io_regs__core_ctrl__out_t; + typedef struct { + logic value; + } verilog6502_io_regs__core_status__rdy_o__out_t; + + typedef struct { + verilog6502_io_regs__core_status__rdy_o__out_t rdy_o; + } verilog6502_io_regs__core_status__out_t; + typedef struct { logic [31:0] value; - } verilog6502_io_regs__axi_base_address__val__out_t; - - typedef struct { - verilog6502_io_regs__axi_base_address__val__out_t val; - } verilog6502_io_regs__axi_base_address__out_t; - - typedef struct { - logic [15:0] value; } verilog6502_io_regs__nmi__nmi__out_t; typedef struct { @@ -32,22 +37,26 @@ package verilog6502_io_regs_pkg; } verilog6502_io_regs__nmi__out_t; typedef struct { - logic [15:0] value; - } verilog6502_io_regs__reset_brq__reset__out_t; + logic [31:0] value; + } verilog6502_io_regs__rst__reset__out_t; typedef struct { - logic [15:0] value; - } verilog6502_io_regs__reset_brq__brk__out_t; + verilog6502_io_regs__rst__reset__out_t reset; + } verilog6502_io_regs__rst__out_t; typedef struct { - verilog6502_io_regs__reset_brq__reset__out_t reset; - verilog6502_io_regs__reset_brq__brk__out_t brk; - } verilog6502_io_regs__reset_brq__out_t; + logic [31:0] value; + } verilog6502_io_regs__brk__brk__out_t; + + typedef struct { + verilog6502_io_regs__brk__brk__out_t brk; + } verilog6502_io_regs__brk__out_t; typedef struct { verilog6502_io_regs__core_ctrl__out_t core_ctrl; - verilog6502_io_regs__axi_base_address__out_t axi_base_address; + verilog6502_io_regs__core_status__out_t core_status; verilog6502_io_regs__nmi__out_t nmi; - verilog6502_io_regs__reset_brq__out_t reset_brq; + verilog6502_io_regs__rst__out_t rst; + verilog6502_io_regs__brk__out_t brk; } verilog6502_io_regs__out_t; endpackage diff --git a/src/verilog6502_addr_decoder.sv b/src/verilog6502_addr_decoder.sv index 84cbaa8..b2ad026 100644 --- a/src/verilog6502_addr_decoder.sv +++ b/src/verilog6502_addr_decoder.sv @@ -2,7 +2,7 @@ module verilog6502_addr_decoder( input i_clk, input i_rst, - input logic [15:0] i_cpu_addr, + input logic [31:0] i_cpu_addr, input logic [7:0] i_cpu_data, output logic [7:0] o_cpu_data, input logic i_cpu_we, @@ -17,14 +17,14 @@ module verilog6502_addr_decoder( output logic o_mem_we, input logic i_mem_rdy, - output logic [15:0] o_external_addr, + output logic [31:0] o_external_addr, output logic [7:0] o_external_data, input logic [7:0] i_external_data, output logic o_external_rd, output logic o_external_we, input logic i_external_rdy, - output logic [15:0] o_io_addr, + output logic [11:0] o_io_addr, output logic [7:0] o_io_data, input logic [7:0] i_io_data, output logic o_io_rd, @@ -88,20 +88,20 @@ always_comb begin endcase if (o_cpu_rdy) begin - if (i_cpu_addr < 16'hE000) begin - o_mem_addr = i_cpu_addr; + if (i_cpu_addr < 32'hFFFF) begin + o_mem_addr = i_cpu_addr[15:0]; o_mem_data = i_cpu_data; o_mem_we = i_cpu_we & o_cpu_rdy; o_mem_rd = ~i_cpu_we & o_cpu_rdy; prev_addr_next = MEM; - end else if (i_cpu_addr < 16'hF000) begin - o_external_addr = {4'b0, i_cpu_addr[11:0]}; + end else if (i_cpu_addr < 32'hFFFFEFFF) begin + o_external_addr = i_cpu_addr; o_external_data = i_cpu_data; o_external_we = i_cpu_we & o_cpu_rdy; o_external_rd = ~i_cpu_we & o_cpu_rdy; prev_addr_next = EXT; end else begin - o_io_addr = {4'b0, i_cpu_addr[11:0]}; + o_io_addr = i_cpu_addr[11:0]; o_io_data = i_cpu_data; o_io_we = i_cpu_we & o_cpu_rdy; o_io_rd = ~i_cpu_we & o_cpu_rdy; diff --git a/src/verilog6502_apb_adapter.sv b/src/verilog6502_apb_adapter.sv index 6d1bb36..7a699f3 100644 --- a/src/verilog6502_apb_adapter.sv +++ b/src/verilog6502_apb_adapter.sv @@ -1,8 +1,10 @@ -module verilog6502_apb_adapter( +module verilog6502_apb_adapter #( + parameter ADDR_WIDTH = 32 +)( input i_clk, input i_rst, - input logic [15:0] i_addr, + input logic [ADDR_WIDTH-1:0] i_addr, input logic [7:0] i_data, output logic [7:0] o_data, input logic i_rd, @@ -12,10 +14,12 @@ module verilog6502_apb_adapter( taxi_apb_if.mst m_apb ); +localparam APB_ADDR_WIDTH = m_apb.ADDR_W; + enum logic {IDLE, ENABLE} state, state_next; -logic [15:0] latched_addr, latched_addr_next; -logic [15:0] second_addr, second_addr_next; +logic [ADDR_WIDTH-1:0] latched_addr, latched_addr_next; +logic [ADDR_WIDTH-1:0] second_addr, second_addr_next; logic second_we, second_rd, second_we_next, second_rd_next; logic [7:0] latched_data, latched_data_next; logic [7:0] second_data, second_data_next; @@ -51,7 +55,7 @@ always_comb begin IDLE: begin if (i_rd | i_we) begin m_apb.pprot = '0; - m_apb.paddr = {16'b0, i_addr} & 32'hfffc; // 32 bit address + m_apb.paddr = APB_ADDR_WIDTH'({i_addr[ADDR_WIDTH-1:2], 2'b0}); m_apb.psel = '1; m_apb.pwrite = i_we; m_apb.pstrb = 4'h1 << i_addr[1:0]; // shift based on lower 2 bits @@ -65,7 +69,7 @@ always_comb begin latched_pwrite_next = i_we; end else if (second_rd | second_we) begin m_apb.pprot = '0; - m_apb.paddr = {16'b0, second_addr} & 32'hfffc; // 32 bit address + m_apb.paddr = APB_ADDR_WIDTH'({second_addr[ADDR_WIDTH-1:2], 2'b0}); m_apb.psel = '1; m_apb.pwrite = second_we; m_apb.pstrb = 4'h1 << second_addr[1:0]; // shift based on lower 2 bits @@ -97,7 +101,7 @@ always_comb begin second_data_next = i_data; m_apb.pprot = '0; - m_apb.paddr = {16'b0, latched_addr} & 32'hfffc; // 32 bit address + m_apb.paddr = APB_ADDR_WIDTH'({latched_addr[ADDR_WIDTH-1:2], 2'b0}); m_apb.psel = '1; m_apb.pwrite = latched_pwrite; m_apb.pstrb = 4'h1 << latched_addr[1:0]; // shift based on lower 2 bits diff --git a/src/verilog6502_external_memory.sv b/src/verilog6502_external_memory.sv index 778e9cb..74243d8 100644 --- a/src/verilog6502_external_memory.sv +++ b/src/verilog6502_external_memory.sv @@ -2,15 +2,13 @@ module verilog6502_external_memory ( input i_clk, input i_rst, - input logic [15:0] i_addr, + input logic [31:0] i_addr, input logic [7:0] i_data, output logic [7:0] o_data, input logic i_rd, input logic i_we, output logic o_rdy, - input logic [31:0] i_axi_base_addr, - taxi_axil_if.wr_mst m_axil_wr, taxi_axil_if.rd_mst m_axil_rd @@ -33,7 +31,7 @@ verilog6502_apb_adapter u_internal_apb_adapter ( .m_apb (internal_apb) ); -assign addr_shift_apb.paddr = {i_axi_base_addr[31:12], {internal_apb.paddr[11:0]}}; +assign addr_shift_apb.paddr = internal_apb.paddr; assign addr_shift_apb.pprot = internal_apb.pprot; assign addr_shift_apb.psel = internal_apb.psel; assign addr_shift_apb.penable = internal_apb.penable; diff --git a/src/verilog6502_internal_memory.sv b/src/verilog6502_internal_memory.sv index 12b0399..9dc1106 100644 --- a/src/verilog6502_internal_memory.sv +++ b/src/verilog6502_internal_memory.sv @@ -73,7 +73,7 @@ taxi_axi_ram_if_rdwr #( ); -logic [7:0] mem [4][14*1024]; +logic [7:0] mem [4][16*1024]; enum logic {CPU, EXT} sel, sel_next; diff --git a/src/verilog6502_wrapper.sv b/src/verilog6502_wrapper.sv index a4ff43f..43b6a2c 100644 --- a/src/verilog6502_wrapper.sv +++ b/src/verilog6502_wrapper.sv @@ -1,11 +1,11 @@ // Wrapper around verilog-6502 // memory map: -// 0x0000-0x00FF Zero Page (Hard coded) -// 0x0100-0x01FF Stack (Hard coded) -// 0x0200-0xCFFF Internal Memory -// 0xE000-0xEFFF External AXI -// 0xF000-0xFFFF Processor IO +// 0x00000000-0x000000FF Zero Page (Hard coded) +// 0x00000100-0x000001FF Stack (Hard coded) +// 0x00000200-0x0000FFFF Internal Memory +// 0x00010000-0xFFFFEFFF External AXI +// 0xFFFFF000-0xFFFFFFFF Processor IO module verilog6502_wrapper( input clk, @@ -59,7 +59,7 @@ taxi_apb_interconnect #( logic cpu_clk; logic cpu_reset; -logic [15:0] cpu_addr; +logic [31:0] cpu_addr; logic [7:0] cpu_data_in; logic [7:0] cpu_data_out; @@ -83,14 +83,14 @@ logic mem_rd; logic mem_we; logic mem_rdy; -logic [15:0] ext_addr; +logic [31:0] ext_addr; logic [7:0] ext_data_in; logic [7:0] ext_data_out; logic ext_rd; logic ext_we; logic ext_rdy; -logic [15:0] io_addr; +logic [11:0] io_addr; logic [7:0] io_data_in; logic [7:0] io_data_out; logic io_rd; @@ -173,13 +173,13 @@ verilog6502_external_memory u_external_memory ( .i_we (ext_we), .o_rdy (ext_rdy), - .i_axi_base_addr (hwif_out.axi_base_address.val.value), - .m_axil_rd (m_axil_rd), .m_axil_wr (m_axil_wr) ); -verilog6502_apb_adapter u_io_apb_adapter( +verilog6502_apb_adapter #( + .ADDR_WIDTH(12) +) u_io_apb_adapter( .i_clk (cpu_clk), .i_rst (rst), -- 2.47.3