Skip to content

Commit d4864a2

Browse files
committed
Add tests and test helpers
Signed-off-by: Joseph Mattiello <[email protected]>
1 parent 8a25bd2 commit d4864a2

13 files changed

Lines changed: 2564 additions & 97 deletions

.gitignore

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,8 @@ chd_dump.c
3232
test/roms/private/*
3333
!test/roms/private/.gitkeep
3434
!test/roms/private/README.md
35+
/test/test_dsp_instructions
36+
/test/test_gpu_instructions
37+
/test/test_hle_bios
38+
/test/test_irq
39+
/test/test_m68k_instructions

Makefile

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -616,7 +616,52 @@ endif
616616
clean:
617617
rm -f $(TARGET) $(OBJECTS)
618618

619-
.PHONY: clean
619+
# --- Unit tests ---
620+
# Build tests against the dylib via dlsym.
621+
# Run: make test (builds core + tests, then runs all test suites)
622+
623+
TEST_CC ?= $(CC)
624+
TEST_CFLAGS = -O0 -g -Wno-incompatible-pointer-types
625+
TEST_LDFLAGS = -ldl
626+
TEST_BINS = test/test_gpu_instructions test/test_dsp_instructions test/test_m68k_instructions test/test_irq test/test_hle_bios test/test_blitter_simd
627+
628+
test/test_gpu_instructions: test/test_gpu_instructions.c test/test_framework.h $(TARGET)
629+
$(TEST_CC) $(TEST_CFLAGS) -o $@ $< $(TEST_LDFLAGS)
630+
631+
test/test_dsp_instructions: test/test_dsp_instructions.c test/test_framework.h $(TARGET)
632+
$(TEST_CC) $(TEST_CFLAGS) -o $@ $< $(TEST_LDFLAGS)
633+
634+
test/test_m68k_instructions: test/test_m68k_instructions.c test/test_framework.h $(TARGET)
635+
$(TEST_CC) $(TEST_CFLAGS) -o $@ $< $(TEST_LDFLAGS)
636+
637+
test/test_irq: test/test_irq.c test/test_framework.h $(TARGET)
638+
$(TEST_CC) $(TEST_CFLAGS) -o $@ $< $(TEST_LDFLAGS)
639+
640+
test/test_hle_bios: test/test_hle_bios.c test/test_framework.h $(TARGET)
641+
$(TEST_CC) $(TEST_CFLAGS) -o $@ $< $(TEST_LDFLAGS)
642+
643+
test/test_blitter_simd: test/test_blitter_simd.c src/blitter_simd.h $(TARGET)
644+
$(TEST_CC) -O2 -o $@ test/test_blitter_simd.c src/blitter_simd_neon.c
645+
646+
test-build: $(TEST_BINS)
647+
648+
test: test-build
649+
@echo ""; echo "=== Running unit tests ===" ; echo ""
650+
@fail=0; \
651+
for t in test/test_gpu_instructions test/test_dsp_instructions test/test_m68k_instructions test/test_irq test/test_hle_bios test/test_blitter_simd; do \
652+
if [ -x "$$t" ]; then \
653+
DYLD_LIBRARY_PATH=. LD_LIBRARY_PATH=. "$$t" > /tmp/vj_test_out.txt 2>&1; \
654+
rc=$$?; \
655+
grep -E 'PASS|FAIL|SKIP|===|---|Results:' /tmp/vj_test_out.txt; \
656+
if [ $$rc -ne 0 ]; then fail=1; fi; \
657+
fi; \
658+
done; \
659+
exit $$fail
660+
661+
clean-test:
662+
rm -f $(TEST_BINS) $(addsuffix .dSYM,$(TEST_BINS))
663+
664+
.PHONY: clean test test-build clean-test
620665
endif
621666

622667
print-%:

docs/test-infrastructure.md

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,49 @@ Tests libretro SRAM interface for save game handling.
6666
./test/sram_test.sh ./virtualjaguar_libretro.dylib
6767
```
6868

69+
## Unit Test Suites (make test)
70+
71+
Six test suites run via `make test`, covering CPU emulation, interrupt handling,
72+
HLE BIOS, and blitter SIMD correctness.
73+
74+
### test_gpu_instructions.c — GPU RISC ISA (51 tests)
75+
Arithmetic, logic, shift/rotate, compare, move/MOVEI, STORE/LOAD, saturation,
76+
register bank switching. Tests run GPU programs in GPU RAM via dlsym.
77+
78+
### test_dsp_instructions.c — DSP RISC ISA (28 tests)
79+
Same ISA as GPU with DSP-specific differences: signed saturation (sat16s/sat32s
80+
instead of SAT8/SAT16), 8KB RAM at $F1B000.
81+
82+
### test_m68k_instructions.c — Motorola 68000 (39 tests)
83+
MOVEQ, ADD/SUB/NEG/CLR, MULU/MULS/DIVU, SWAP, EXT, AND/OR/EOR/NOT,
84+
LSL/LSR/ASR/ROL/ROR, CMP/TST, memory addressing modes (direct, pre-dec,
85+
post-inc), LEA, ADDA/SUBA, BTST/BSET/BCLR.
86+
87+
### test_irq.c — Interrupt Handling (18 tests)
88+
TOM IRQ enable/disable/latch/pending, JERRY IRQ enable, GPU IRQ assert/clear/IMASK,
89+
TOM video mode register, JERRY timer prescaler, BUTCH interrupt control.
90+
91+
### test_hle_bios.c — HLE CD BIOS (14 tests)
92+
Jump table, CD_poll A1=0 convention, CD_wait_response, ISR setup handlers,
93+
TOC format, no-op entry safety, GPU auth magic, RAM byte order.
94+
95+
### test_blitter_simd.c — Blitter SIMD (40,067 tests)
96+
Exhaustive bit-exact comparison of LFU, DCOMP, ZCOMP, byte_merge against
97+
reference implementations.
98+
99+
### Build & Run
100+
```bash
101+
make -j4 DEBUG=1 # Build core
102+
make test # Build & run all test suites
103+
make test-build # Build tests only
104+
make clean-test # Remove test binaries
105+
```
106+
107+
### Framework (test_framework.h)
108+
Minimal single-header test framework with dlsym-based core loading.
109+
Provides GPU/DSP instruction encoding helpers, assert macros, and
110+
function pointers to all hardware subsystem functions.
111+
69112
## CI Integration
70113

71114
GitHub Actions workflow (`.github/workflows/regression-test.yml`) runs
@@ -80,9 +123,14 @@ test/
80123
regression_test.sh # Screenshot regression suite
81124
sram_test.sh # SRAM interface test
82125
test_cd_boot.c # CD boot diagnostics (C)
83-
test_blitter_simd.c # SIMD blitter test (C)
126+
test_framework.h # Unit test framework header
127+
test_gpu_instructions.c # GPU RISC ISA tests (51)
128+
test_dsp_instructions.c # DSP RISC ISA tests (28)
129+
test_m68k_instructions.c # 68K CPU tests (39)
130+
test_irq.c # Interrupt handling tests (18)
131+
test_hle_bios.c # HLE CD BIOS tests (14)
132+
test_blitter_simd.c # SIMD blitter tests (40067)
84133
baselines/ # Reference PNG screenshots
85134
roms/ # Test ROMs (private/ is git-ignored)
86135
tools/ # Test ROM generators, SRAM test harness
87-
cd_trace_*.log # Debug logs from CD boot tests
88136
```

libretro.c

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -382,6 +382,19 @@ static void check_variables(void)
382382
vjs.cdBiosType = CDBIOS_RETAIL;
383383
}
384384

385+
var.key = "virtualjaguar_cd_boot_mode";
386+
var.value = NULL;
387+
388+
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
389+
{
390+
if (strcmp(var.value, "hle") == 0)
391+
vjs.cdBootMode = CDBOOT_HLE;
392+
else if (strcmp(var.value, "bios") == 0)
393+
vjs.cdBootMode = CDBOOT_BIOS;
394+
else
395+
vjs.cdBootMode = CDBOOT_AUTO;
396+
}
397+
385398
var.key = "virtualjaguar_alt_inputs";
386399
var.value = NULL;
387400
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
@@ -1108,17 +1121,24 @@ bool retro_load_game(const struct retro_game_info *info)
11081121
strncpy(cd_image_path, info->path, sizeof(cd_image_path) - 1);
11091122
cd_image_path[sizeof(cd_image_path) - 1] = '\0';
11101123

1111-
/* For CD mode, force BIOS on -- CD games require the BIOS */
11121124
vjs.useJaguarBIOS = true;
11131125
vjs.useCDBIOS = true;
11141126

1115-
/* Try to load an external CD BIOS from the system directory.
1116-
* If no external BIOS is found, we'll use HLE (High-Level
1117-
* Emulation) to boot the CD game directly. */
11181127
cd_bios_loaded_externally = false;
1119-
if (!load_external_cd_bios())
1128+
1129+
if (vjs.cdBootMode == CDBOOT_HLE)
1130+
{
1131+
fprintf(stderr, "[CD] Boot mode: HLE (skipping BIOS search)\n");
1132+
}
1133+
else
11201134
{
1121-
fprintf(stderr, "[CD] No external BIOS found — will use HLE boot path\n");
1135+
if (!load_external_cd_bios())
1136+
{
1137+
if (vjs.cdBootMode == CDBOOT_BIOS)
1138+
fprintf(stderr, "[CD] WARNING: Boot mode is BIOS but no external BIOS found\n");
1139+
else
1140+
fprintf(stderr, "[CD] No external BIOS found — will use HLE boot path\n");
1141+
}
11221142
}
11231143
}
11241144

libretro_core_options.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,21 @@ struct retro_core_option_v2_definition option_defs_us[] = {
161161
},
162162
"retail"
163163
},
164+
{
165+
"virtualjaguar_cd_boot_mode",
166+
"CD Boot Mode (Restart)",
167+
NULL,
168+
"How to boot Jaguar CD games. Auto uses the real BIOS if found, otherwise HLE. HLE always uses high-level emulation (no BIOS ROM needed). BIOS requires an external BIOS ROM file.",
169+
NULL,
170+
NULL,
171+
{
172+
{ "auto", "Auto" },
173+
{ "hle", "HLE (No BIOS Required)" },
174+
{ "bios", "BIOS (Required)" },
175+
{ NULL, NULL },
176+
},
177+
"auto"
178+
},
164179
{
165180
"virtualjaguar_alt_inputs",
166181
"Enable Core Options Remapping",

src/settings.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ struct VJSettings
3232
bool useFastBlitter;
3333
bool useCDBIOS;
3434
uint32_t cdBiosType;
35+
uint32_t cdBootMode;
3536

3637
// Paths
3738

@@ -48,6 +49,10 @@ enum { BT_K_SERIES, BT_M_SERIES, BT_STUBULATOR_1, BT_STUBULATOR_2 };
4849

4950
enum { CDBIOS_RETAIL, CDBIOS_DEV };
5051

52+
// CD boot modes
53+
54+
enum { CDBOOT_AUTO, CDBOOT_HLE, CDBOOT_BIOS };
55+
5156
// Exported variables
5257

5358
extern struct VJSettings vjs;

test/headless.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ def parse_args() -> argparse.Namespace:
5050
p.add_argument("--frames", type=int, default=600, help="Frames to run (default: 600)")
5151
p.add_argument("--cd-bios", choices=["retail", "dev"], default="retail",
5252
help="CD BIOS variant (default: retail)")
53+
p.add_argument("--cd-boot-mode", choices=["auto", "hle", "bios"], default="auto",
54+
help="CD boot mode: auto, hle, or bios (default: auto)")
5355
p.add_argument("--core", type=Path, default=None, help="Override core path")
5456
p.add_argument("--system-dir", type=Path, default=REPO_ROOT / "test" / "roms" / "private",
5557
help="Directory containing BIOS files")
@@ -112,6 +114,7 @@ def language(self): return None
112114
"virtualjaguar_bios": "enabled",
113115
"virtualjaguar_usefastblitter": "enabled",
114116
"virtualjaguar_cd_bios_type": args.cd_bios,
117+
"virtualjaguar_cd_boot_mode": args.cd_boot_mode,
115118
}
116119

117120
paths = FixedPathDriver(args.system_dir, args.save_dir, core)

0 commit comments

Comments
 (0)