From 542272fdac3db5da34fc92c6be0baa2b17f49cc6 Mon Sep 17 00:00:00 2001 From: mooinglemur Date: Sun, 10 Mar 2024 01:20:05 -0700 Subject: [PATCH] [CI/CD] the first unit tests --- .github/workflows/build.yml | 10 ++ Makefile | 3 + test/unit/00-kernal/ff6e-jsrfar.py | 95 ++++++++++ test/unit/00-kernal/ff6e-jsrfar.s | 275 +++++++++++++++++++++++++++++ test/unit/testutil.py | 39 ++++ 5 files changed, 422 insertions(+) create mode 100644 test/unit/00-kernal/ff6e-jsrfar.py create mode 100644 test/unit/00-kernal/ff6e-jsrfar.s create mode 100644 test/unit/testutil.py diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index a499f80c..b30d7373 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -31,6 +31,16 @@ jobs: if: startsWith(github.ref, 'refs/tags/r') run: | RELEASE_VERSION=$(echo "$GITHUB_REF_NAME" | grep -oP '[0-9]+$') make + - name: Build Test Emulator + run: | + git clone https://github.com/X16Community/x16-emulator.git + cd x16-emulator + git checkout 65c816 + make all + - name: Run Unit Tests + run: | + export PATH="$(pwd)/x16-emulator:$PATH" + make test - name: Archive Build Result run: | mkdir artifact diff --git a/Makefile b/Makefile index d2d930d8..9364a71e 100644 --- a/Makefile +++ b/Makefile @@ -327,6 +327,9 @@ all: $(BUILD_DIR)/rom.bin $(ROM_LABELS) $(ROM_LST) $(BUILD_DIR)/rom.bin: $(BANK_BINS) cat $(BANK_BINS) > $@ +test: FORCE $(BUILD_DIR)/rom.bin + for f in test/unit/*/*.py; do PYTHONPATH="test/unit" python3 -B $${f}; done + x16edit_update: @rm -rf x16edittmp git clone https://github.com/stefan-b-jakobsson/x16-edit.git x16edittmp diff --git a/test/unit/00-kernal/ff6e-jsrfar.py b/test/unit/00-kernal/ff6e-jsrfar.py new file mode 100644 index 00000000..a9d7628c --- /dev/null +++ b/test/unit/00-kernal/ff6e-jsrfar.py @@ -0,0 +1,95 @@ +#!/usr/bin/env python3 + +import os +import testutil + +# Determine the script directory +script_dir = os.path.dirname(__file__) + +# Assemble the full path to the output directory +output_dir = os.path.join(script_dir, "../../../build/x16/test") + +# Create the output directory if it doesn't yet exist +if not os.path.exists(output_dir): + os.mkdir(output_dir) + +test_source = os.path.join(script_dir, "ff6e-jsrfar.s") +test_prg = os.path.join(output_dir, "ff6e-jsrfar.prg") + +testutil.assemble(test_source, test_prg) +res = testutil.run_tests(test_prg, ['-c816']) + +TEST1_A = res[0] + +TEST2_R = res[1] + (res[2] << 8) + (res[3] << 16) + +TEST3_A = res[4] +TEST3_X = res[5] +TEST3_Y = res[6] + +TEST4_A = res[7] +TEST4_X = res[8] +TEST4_Y = res[9] + +TEST5_A = res[10] +TEST5_X = res[11] + (res[12] << 8) +TEST5_Y = res[13] + (res[14] << 8) + +TEST6_A = res[15] + (res[16] << 8) +TEST6_X = res[17] + (res[18] << 8) +TEST6_Y = res[19] + (res[20] << 8) + +TEST7_A = res[21] + (res[22] << 8) +TEST7_X = res[23] + (res[24] << 8) +TEST7_Y = res[25] + (res[26] << 8) + +TEST8_A = res[27] + (res[28] << 8) +TEST8_X = res[29] +TEST8_Y = res[30] + +TEST9_A = res[31] + +print(f"ff6e-jsrfar (e) TEST1: ROM, ym_get_chip_type, expected 0 or 2, got: {TEST1_A}") + +if not (TEST1_A == 0 or TEST1_A == 2): + raise RuntimeError("TEST1 failed") + +print(f"ff6e-jsrfar (e) TEST2: ROM, rdtim, expected 60, got: {TEST2_R}") + +if not (TEST2_R == 60): + raise RuntimeError("TEST2 failed") + +print(f"ff6e-jsrfar (e) TEST3: BRAM, incr regs, expected 70, 165, 2, got: {TEST3_A}, {TEST3_X}, {TEST3_Y}") + +if not (TEST3_A == 70 and TEST3_X == 165 and TEST3_Y == 2): + raise RuntimeError("TEST3 failed") + +print(f"ff6e-jsrfar (m=1,x=1) TEST4: BRAM, incr regs, expected 50, 150, 250, got: {TEST4_A}, {TEST4_X}, {TEST4_Y}") + +if not (TEST4_A == 50 and TEST4_X == 150 and TEST4_Y == 250): + raise RuntimeError("TEST4 failed") + +print(f"ff6e-jsrfar (m=1,x=0) TEST5: BRAM, incr regs, expected 121, 1338, 42070, got: {TEST5_A}, {TEST5_X}, {TEST5_Y}") + +if not (TEST5_A == 121 and TEST5_X == 1338 and TEST5_Y == 42070): + raise RuntimeError("TEST5 failed") + +print(f"ff6e-jsrfar (m=0,x=0) TEST6: BRAM, incr regs, expected 12346, 54322, 65433, got: {TEST6_A}, {TEST6_X}, {TEST6_Y}") + +if not (TEST6_A == 12346 and TEST6_X == 54322 and TEST6_Y == 65433): + raise RuntimeError("TEST6 failed") + +print(f"ff6e-jsrfar (m=0,x=0) TEST7: ROM, extapi16 #0, expected 23432, got: {TEST7_A}") + +if not (TEST7_A == 23432): + raise RuntimeError("TEST7 failed") + +print(f"ff6e-jsrfar (m=0,x=1) TEST8: ROM, extapi16 #0, expected 260, got: {TEST8_A}") + +if not (TEST8_A == 260): + raise RuntimeError("TEST8 failed") + +print(f"ff6e-jsrfar (e) TEST9: RAM -> BRAM, fixed return, expected 255, got: {TEST9_A}") + +if not (TEST9_A == 255): + raise RuntimeError("TEST9 failed") diff --git a/test/unit/00-kernal/ff6e-jsrfar.s b/test/unit/00-kernal/ff6e-jsrfar.s new file mode 100644 index 00000000..ea893dd0 --- /dev/null +++ b/test/unit/00-kernal/ff6e-jsrfar.s @@ -0,0 +1,275 @@ +.macpack longbranch +.include "ascii_charmap.inc" + +.segment "ZEROPAGE" +ptr: + .res 2 + +.segment "STARTUP" + jmp start +.segment "INIT" +.segment "ONCE" +.segment "CODE" + +extapi16 = $fea8 +jsrfar = $ff6e +rdtim = $ffde +udtim = $ffea + +test_harness = $fffd + +KERNAL_BANK = $00 +BASIC_BANK = $04 +AUDIO_BANK = $0a + +ym_get_chip_type = $c0a5 + +ram_bank = $00 +rom_bank = $01 + +TEST_RESULTS = $400 + +start: + stz rom_bank + ; udtim 60 times + ldx #60 +: jsr udtim + dex + bne :- + + ; ----------------------------------------------------------------------- + ; TEST 1 + ; standard jsrfar from KERNAL bank to AUDIO bank + ; ----------------------------------------------------------------------- + + lda #$ff ; canary value + + jsr jsrfar + .word ym_get_chip_type + .byte AUDIO_BANK + + sta TEST_RESULTS ; expected to be 0 since we'll run with no audio device + + ; ----------------------------------------------------------------------- + ; TEST 2 + ; standard jsrfar from BASIC bank to KERNAL bank + ; ----------------------------------------------------------------------- + lda #BASIC_BANK + sta rom_bank + + jsr jsrfar + .word rdtim + .byte KERNAL_BANK + + sta TEST_RESULTS+1 + stx TEST_RESULTS+2 + sty TEST_RESULTS+3 + + jsr write_code_to_ram_banks + + ; ----------------------------------------------------------------------- + ; TEST 9 + ; standard jsrfar from KERNAL bank, LOW RAM to BRAM + ; other tests will continue there + ; ----------------------------------------------------------------------- + stz rom_bank + + jsr jsrfar + .word code_bank_1_run + .byte 1 + + sta TEST_RESULTS+31 + + ; ----------------------------------------------------------------------- + ; finish test, jump to test harness + ; ----------------------------------------------------------------------- + jmp test_harness + +code_bank_1_load = * +.org $A000 +.proc code_bank_1_run + ; ----------------------------------------------------------------------- + ; TEST 3 + ; standard jsrfar from KERNAL bank, BRAM to BRAM + ; ----------------------------------------------------------------------- + + lda #69 + ldx #<420 ; 164/$a4 + ldy #>420 ; 1/$01 + + jsr jsrfar + .word code_bank_2_run + .byte 2 + + sta TEST_RESULTS+4 + stx TEST_RESULTS+5 + sty TEST_RESULTS+6 + + +.pushcpu +.setcpu "65816" + lda rom_bank + pha + + clc + xce + + ; ----------------------------------------------------------------------- + ; TEST 4 + ; native jsrfar (m8 x8) from KERNAL bank, BRAM to BRAM + ; ----------------------------------------------------------------------- + .a8 + .i8 + + lda #49 + ldx #149 + ldy #249 + + jsr jsrfar + .word code_bank_2_run + .byte 2 + + sta TEST_RESULTS+7 + stx TEST_RESULTS+8 + sty TEST_RESULTS+9 + + ; ----------------------------------------------------------------------- + ; TEST 5 + ; native jsrfar (m8 x16) from KERNAL bank, BRAM to BRAM + ; ----------------------------------------------------------------------- + rep #$10 + + .a8 + .i16 + + lda #120 + ldx #1337 + ldy #42069 + + jsr jsrfar + .word code_bank_2_run + .byte 2 + + sta TEST_RESULTS+10 + stx TEST_RESULTS+11 ; and 12 + sty TEST_RESULTS+13 ; and 14 + + ; ----------------------------------------------------------------------- + ; TEST 6 + ; native jsrfar (m16 x16) from KERNAL bank, BRAM to BRAM + ; ----------------------------------------------------------------------- + rep #$20 + + .a16 + + lda #12345 + ldx #54321 + ldy #65432 + + jsr jsrfar + .word code_bank_2_run + .byte 2 + + sta TEST_RESULTS+15 ; and 16 + stx TEST_RESULTS+17 ; and 18 + sty TEST_RESULTS+19 ; and 20 + + ; ----------------------------------------------------------------------- + ; TEST 7 + ; native jsrfar (m16 x16) from BASIC bank, BRAM to KERNAL bank + ; ----------------------------------------------------------------------- + sep #$20 + .a8 + lda #4 + sta rom_bank + rep #$20 + .a16 + + lda #0 ; X+Y -> A + ldx #12321 + ldy #11111 + + jsr jsrfar + .word extapi16 + .byte 0 + + sta TEST_RESULTS+21 ; and 22 + stx TEST_RESULTS+23 ; and 24 + sty TEST_RESULTS+25 ; and 26 + + ; ----------------------------------------------------------------------- + ; TEST 8 + ; native jsrfar (m16 x8) from BASIC bank, BRAM to KERNAL bank + ; ----------------------------------------------------------------------- + sep #$10 + .i8 + + lda #0 ; X+Y -> A + ldx #129 + ldy #131 + + jsr jsrfar + .word extapi16 + .byte 0 + + sta TEST_RESULTS+27 ; and 28 + stx TEST_RESULTS+29 + sty TEST_RESULTS+30 + + sec + xce + + .a8 + .i8 + + pla + sta rom_bank + + lda #$ff + + rts + +.endproc +.reloc +end_code_bank_1_load = * + +.popcpu + +code_bank_2_load = * +.org $A000 +.proc code_bank_2_run + inc + inx + iny + + rts +.endproc +.reloc +end_code_bank_2_load = * + + +.proc write_code_to_ram_banks + lda #1 + sta ram_bank + +.assert end_code_bank_2_load-code_bank_2_load < 256, error, "ram bank 1 code is too large" + + ldx #<(end_code_bank_1_load-code_bank_1_load) +: lda code_bank_1_load-1,x + sta code_bank_1_run-1,x + dex + bne :- + + lda #2 + sta ram_bank + +.assert end_code_bank_2_load-code_bank_2_load < 256, error, "ram bank 2 code is too large" + + ldx #<(end_code_bank_2_load-code_bank_2_load) +: lda code_bank_2_load-1,x + sta code_bank_2_run-1,x + dex + bne :- + + rts +.endproc diff --git a/test/unit/testutil.py b/test/unit/testutil.py new file mode 100644 index 00000000..e3b678fe --- /dev/null +++ b/test/unit/testutil.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +import subprocess + +def assemble(source, prg): + # Assemble the test into the output dir + result = subprocess.run(['cl65', '-t', 'cx16', source, '-o', prg]) + result.check_returncode() + + +def run_tests(prg, emuargs=[]): + with subprocess.Popen(['x16emu','-rom','build/x16/rom.bin','-testbench','-prg', prg] + emuargs, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE) as emu: + + rdy = '' + while not rdy == 'RDY': + rdy = emu.stdout.readline().decode().rstrip() + + emu.stdin.write("RUN 080d\n".encode()) + emu.stdin.flush() + + rdy = '' + while not rdy == 'RDY': + rdy = emu.stdout.readline().decode().rstrip() + + result = [] + + for i in range(0x400,0x500): + emu.stdin.write(f"RQM {i:04x}\n".encode()) + emu.stdin.flush() + ans = emu.stdout.readline().decode().rstrip() + result.append(int(ans, 16)) + + return result + +if __name__ == "__main__": + print(f"{__file__} is not meant to be run directly.") +