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..ceb5b92 --- /dev/null +++ b/sim/asm_source/jsr_test.s @@ -0,0 +1,32 @@ +.export vec_reset, vec_irq, vec_nmi + +.ZEROPAGE + +result: .res 1 + +.segment "CODE" + +vec_nmi: +vec_reset: +vec_irq: + + +jsr_test: + 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 new file mode 100644 index 0000000..94faf1d --- /dev/null +++ b/sim/asm_source/lda_test.s @@ -0,0 +1,121 @@ +.export vec_reset, vec_irq, vec_nmi + +.ZEROPAGE + +result: .res 1 + +zp0: .res 1 +zp1: .res 4 +zp2: .res 8 +zp3: .res 4 + +good_count: .res 1 + +.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 + + 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/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.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_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..f47a83d --- /dev/null +++ b/sim/verilog6502_32bit_asm_test.py @@ -0,0 +1,71 @@ +import cocotb +from cocotb.handle import Immediate + +from cocotb.clock import Clock +from cocotb.triggers import Timer, RisingEdge, FallingEdge, with_timeout + +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) + +async def do_asm_test(dut, filename): + 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/{filename}", "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 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 diff --git a/sim/verilog6502_32bit_test.py b/sim/verilog6502_32bit_test.py new file mode 100644 index 0000000..f43331b --- /dev/null +++ b/sim/verilog6502_32bit_test.py @@ -0,0 +1,806 @@ +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 + +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) + +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()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff8, 0x12345678) + + 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 = [ + (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 + (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 + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + +@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(0xfffffff8, 0x200) + + # lda $abcd1234 + # sta $50515253 + # 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 + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + + +@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(0xfffffff8, 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 + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + + +@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(0xfffffff8, 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 + ] + + 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(0xfffffff8, 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 + + 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 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(0xfffffff8, 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) + +@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(0xfffffff8, 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) + +@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(0xfffffff8, 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) + +@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(0xfffffff8, 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 + ] + + await check_instruction_sequence(dut, expected_cpu_outputs) + +@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(0xfffffff8, 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_irq(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff8, 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_bra_always(dut): + cocotb.start_soon(Clock(dut.clk, CLK_PERIOD, unit="ns").start()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff8, 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(0xfffffff8, 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()) + cocotb.start_soon(handle_memory(dut)) + + write_dword(0xfffffff8, 0x200) + + + def zp_indirect(): + value = random.randint(0,255) + address = random.randint(0x300, 0xffffffff) + + address_le = struct.pack("