From 9ba71b7b625f97c70b5dcde035d85de712bbcb37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Burak=20=C5=9Een?= Date: Thu, 27 Aug 2026 07:16:28 +0200 Subject: [PATCH] test(nes): modularize conformance suite and add NESdev emulator test ROMs --- src/system/nes/conformance/apu.zig | 55 +++++++++++++++++++++++++ src/system/nes/conformance/cpu.zig | 44 ++++++++++++++++++++ src/system/nes/conformance/helpers.zig | 57 ++++++++++++++++++++++++++ src/system/nes/conformance/input.zig | 53 ++++++++++++++++++++++++ src/system/nes/conformance/mapper.zig | 27 ++++++++++++ src/system/nes/conformance/misc.zig | 19 +++++++++ src/system/nes/conformance/ppu.zig | 53 ++++++++++++++++++++++++ src/system/nes/conformance/root.zig | 19 +++++++++ 8 files changed, 327 insertions(+) create mode 100644 src/system/nes/conformance/apu.zig create mode 100644 src/system/nes/conformance/cpu.zig create mode 100644 src/system/nes/conformance/helpers.zig create mode 100644 src/system/nes/conformance/input.zig create mode 100644 src/system/nes/conformance/mapper.zig create mode 100644 src/system/nes/conformance/misc.zig create mode 100644 src/system/nes/conformance/ppu.zig create mode 100644 src/system/nes/conformance/root.zig diff --git a/src/system/nes/conformance/apu.zig b/src/system/nes/conformance/apu.zig new file mode 100644 index 0000000..a4661fe --- /dev/null +++ b/src/system/nes/conformance/apu.zig @@ -0,0 +1,55 @@ +// ponytail: 1-line APU conformance test suite +const testing = @import("std").testing; +const helpers = @import("helpers.zig"); + +test "conformance: apu - 01.len_ctr" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("blargg_apu_2005.07.30/01.len_ctr.nes", 60)).bus.ppu.framebuffer) > 50); } +test "conformance: apu - 02.len_table" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("blargg_apu_2005.07.30/02.len_table.nes", 60)).bus.ppu.framebuffer) > 50); } +test "conformance: apu - 03.irq_flag" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("blargg_apu_2005.07.30/03.irq_flag.nes", 60)).bus.ppu.framebuffer) > 50); } +test "conformance: apu - 04.clock_jitter" { _ = try helpers.runRom("blargg_apu_2005.07.30/04.clock_jitter.nes", 60); } +test "conformance: apu - 05.len_timing_mode0" { _ = try helpers.runRom("blargg_apu_2005.07.30/05.len_timing_mode0.nes", 60); } +test "conformance: apu - 06.len_timing_mode1" { _ = try helpers.runRom("blargg_apu_2005.07.30/06.len_timing_mode1.nes", 60); } +test "conformance: apu - 07.irq_flag_timing" { _ = try helpers.runRom("blargg_apu_2005.07.30/07.irq_flag_timing.nes", 60); } +test "conformance: apu - 08.irq_timing" { _ = try helpers.runRom("blargg_apu_2005.07.30/08.irq_timing.nes", 60); } +test "conformance: apu - 09.reset_timing" { _ = try helpers.runRom("blargg_apu_2005.07.30/09.reset_timing.nes", 60); } +test "conformance: apu - 10.len_halt_timing" { _ = try helpers.runRom("blargg_apu_2005.07.30/10.len_halt_timing.nes", 60); } +test "conformance: apu - 11.len_reload_timing" { _ = try helpers.runRom("blargg_apu_2005.07.30/11.len_reload_timing.nes", 60); } +test "conformance: apu - apu_reset 4015" { _ = try helpers.runRom("apu_reset/4015_cleared.nes", 60); } +test "conformance: apu - apu_reset 4017_timing" { _ = try helpers.runRom("apu_reset/4017_timing.nes", 60); } +test "conformance: apu - apu_reset 4017_written" { _ = try helpers.runRom("apu_reset/4017_written.nes", 60); } +test "conformance: apu - apu_reset irq_flag_cleared" { _ = try helpers.runRom("apu_reset/irq_flag_cleared.nes", 60); } +test "conformance: apu - apu_reset len_ctrs_enabled" { _ = try helpers.runRom("apu_reset/len_ctrs_enabled.nes", 60); } +test "conformance: apu - apu_reset works_immediately" { _ = try helpers.runRom("apu_reset/works_immediately.nes", 60); } +test "conformance: apu - apu_test" { _ = try helpers.runRom("apu_test/apu_test.nes", 60); } +test "conformance: apu - apu_test 1-len_ctr" { _ = try helpers.runRom("apu_test/rom_singles/1-len_ctr.nes", 60); } +test "conformance: apu - apu_test 2-len_table" { _ = try helpers.runRom("apu_test/rom_singles/2-len_table.nes", 60); } +test "conformance: apu - apu_test 3-irq_flag" { _ = try helpers.runRom("apu_test/rom_singles/3-irq_flag.nes", 60); } +test "conformance: apu - apu_test 4-jitter" { _ = try helpers.runRom("apu_test/rom_singles/4-jitter.nes", 60); } +test "conformance: apu - apu_test 5-len_timing" { _ = try helpers.runRom("apu_test/rom_singles/5-len_timing.nes", 60); } +test "conformance: apu - apu_test 6-irq_flag_timing" { _ = try helpers.runRom("apu_test/rom_singles/6-irq_flag_timing.nes", 60); } +test "conformance: apu - apu_test 7-dmc_basics" { _ = try helpers.runRom("apu_test/rom_singles/7-dmc_basics.nes", 60); } +test "conformance: apu - apu_test 8-dmc_rates" { _ = try helpers.runRom("apu_test/rom_singles/8-dmc_rates.nes", 60); } +test "conformance: apu - dmc buffer_retained" { _ = try helpers.runRom("dmc_tests/buffer_retained.nes", 60); } +test "conformance: apu - dmc latency" { _ = try helpers.runRom("dmc_tests/latency.nes", 60); } +test "conformance: apu - dmc status" { _ = try helpers.runRom("dmc_tests/status.nes", 60); } +test "conformance: apu - dmc status_irq" { _ = try helpers.runRom("dmc_tests/status_irq.nes", 60); } +test "conformance: apu - dmc_dma 2007_read" { _ = try helpers.runRom("dmc_dma_during_read4/dma_2007_read.nes", 60); } +test "conformance: apu - dmc_dma 2007_write" { _ = try helpers.runRom("dmc_dma_during_read4/dma_2007_write.nes", 60); } +test "conformance: apu - dmc_dma 4016_read" { _ = try helpers.runRom("dmc_dma_during_read4/dma_4016_read.nes", 60); } +test "conformance: apu - dmc_dma double_2007_read" { _ = try helpers.runRom("dmc_dma_during_read4/double_2007_read.nes", 60); } +test "conformance: apu - dmc_dma read_write_2007" { _ = try helpers.runRom("dmc_dma_during_read4/read_write_2007.nes", 60); } +test "conformance: apu - sprdma_and_dmc_dma" { _ = try helpers.runRom("sprdma_and_dmc_dma/sprdma_and_dmc_dma.nes", 60); } +test "conformance: apu - apu_mixer square" { _ = try helpers.runRom("apu_mixer/square.nes", 60); } +test "conformance: apu - apu_mixer triangle" { _ = try helpers.runRom("apu_mixer/triangle.nes", 60); } +test "conformance: apu - apu_mixer noise" { _ = try helpers.runRom("apu_mixer/noise.nes", 60); } +test "conformance: apu - apu_mixer dmc" { _ = try helpers.runRom("apu_mixer/dmc.nes", 60); } +test "conformance: apu - volumes" { _ = try helpers.runRom("volume_tests/volumes.nes", 60); } +test "conformance: apu - pal_apu 01.len_ctr" { _ = try helpers.runRom("pal_apu_tests/01.len_ctr.nes", 60); } +test "conformance: apu - pal_apu 02.len_table" { _ = try helpers.runRom("pal_apu_tests/02.len_table.nes", 60); } +test "conformance: apu - pal_apu 03.irq_flag" { _ = try helpers.runRom("pal_apu_tests/03.irq_flag.nes", 60); } +test "conformance: apu - pal_apu 04.clock_jitter" { _ = try helpers.runRom("pal_apu_tests/04.clock_jitter.nes", 60); } +test "conformance: apu - pal_apu 05.len_timing_mode0" { _ = try helpers.runRom("pal_apu_tests/05.len_timing_mode0.nes", 60); } +test "conformance: apu - pal_apu 06.len_timing_mode1" { _ = try helpers.runRom("pal_apu_tests/06.len_timing_mode1.nes", 60); } +test "conformance: apu - pal_apu 07.irq_flag_timing" { _ = try helpers.runRom("pal_apu_tests/07.irq_flag_timing.nes", 60); } +test "conformance: apu - pal_apu 08.irq_timing" { _ = try helpers.runRom("pal_apu_tests/08.irq_timing.nes", 60); } +test "conformance: apu - pal_apu 10.len_halt_timing" { _ = try helpers.runRom("pal_apu_tests/10.len_halt_timing.nes", 60); } +test "conformance: apu - pal_apu 11.len_reload_timing" { _ = try helpers.runRom("pal_apu_tests/11.len_reload_timing.nes", 60); } diff --git a/src/system/nes/conformance/cpu.zig b/src/system/nes/conformance/cpu.zig new file mode 100644 index 0000000..b2716f8 --- /dev/null +++ b/src/system/nes/conformance/cpu.zig @@ -0,0 +1,44 @@ +// ponytail: 1-line CPU conformance test suite +const testing = @import("std").testing; +const helpers = @import("helpers.zig"); + +test "conformance: cpu - instr_test_v5 official_only" { _ = try helpers.runRom("instr_test-v5/official_only.nes", 60); } +test "conformance: cpu - instr_test_v5 all_instrs" { _ = try helpers.runRom("instr_test-v5/all_instrs.nes", 60); } +test "conformance: cpu - instr_test_v5 01-basics" { _ = try helpers.runRom("instr_test-v5/rom_singles/01-basics.nes", 60); } +test "conformance: cpu - instr_test_v5 02-implied" { _ = try helpers.runRom("instr_test-v5/rom_singles/02-implied.nes", 60); } +test "conformance: cpu - instr_test_v5 03-immediate" { _ = try helpers.runRom("instr_test-v5/rom_singles/03-immediate.nes", 60); } +test "conformance: cpu - instr_test_v5 04-zero_page" { _ = try helpers.runRom("instr_test-v5/rom_singles/04-zero_page.nes", 60); } +test "conformance: cpu - instr_test_v5 05-zp_xy" { _ = try helpers.runRom("instr_test-v5/rom_singles/05-zp_xy.nes", 60); } +test "conformance: cpu - instr_test_v5 06-absolute" { _ = try helpers.runRom("instr_test-v5/rom_singles/06-absolute.nes", 60); } +test "conformance: cpu - instr_test_v5 07-abs_xy" { _ = try helpers.runRom("instr_test-v5/rom_singles/07-abs_xy.nes", 60); } +test "conformance: cpu - instr_test_v5 08-ind_x" { _ = try helpers.runRom("instr_test-v5/rom_singles/08-ind_x.nes", 60); } +test "conformance: cpu - instr_test_v5 09-ind_y" { _ = try helpers.runRom("instr_test-v5/rom_singles/09-ind_y.nes", 60); } +test "conformance: cpu - instr_test_v5 10-branches" { _ = try helpers.runRom("instr_test-v5/rom_singles/10-branches.nes", 60); } +test "conformance: cpu - instr_test_v5 11-stack" { _ = try helpers.runRom("instr_test-v5/rom_singles/11-stack.nes", 60); } +test "conformance: cpu - instr_test_v5 12-jmp_jsr" { _ = try helpers.runRom("instr_test-v5/rom_singles/12-jmp_jsr.nes", 60); } +test "conformance: cpu - instr_test_v5 13-rts" { _ = try helpers.runRom("instr_test-v5/rom_singles/13-rts.nes", 60); } +test "conformance: cpu - instr_test_v5 14-rti" { _ = try helpers.runRom("instr_test-v5/rom_singles/14-rti.nes", 60); } +test "conformance: cpu - instr_test_v5 15-brk" { _ = try helpers.runRom("instr_test-v5/rom_singles/15-brk.nes", 60); } +test "conformance: cpu - instr_test_v5 16-special" { _ = try helpers.runRom("instr_test-v5/rom_singles/16-special.nes", 60); } +test "conformance: cpu - branch_timing 1.branch_basics" { _ = try helpers.runRom("branch_timing_tests/1.branch_basics.nes", 60); } +test "conformance: cpu - branch_timing 2.backward_branch" { _ = try helpers.runRom("branch_timing_tests/2.backward_branch.nes", 60); } +test "conformance: cpu - branch_timing 3.forward_branch" { _ = try helpers.runRom("branch_timing_tests/3.forward_branch.nes", 60); } +test "conformance: cpu - cpu_dummy_reads" { _ = try helpers.runRom("cpu_dummy_reads/cpu_dummy_reads.nes", 60); } +test "conformance: cpu - cpu_dummy_writes oam" { _ = try helpers.runRom("cpu_dummy_writes/cpu_dummy_writes_oam.nes", 60); } +test "conformance: cpu - cpu_dummy_writes ppumem" { _ = try helpers.runRom("cpu_dummy_writes/cpu_dummy_writes_ppumem.nes", 60); } +test "conformance: cpu - cpu_timing_test6" { _ = try helpers.runRom("cpu_timing_test6/cpu_timing_test.nes", 60); } +test "conformance: cpu - cpu_reset ram_after_reset" { _ = try helpers.runRom("cpu_reset/ram_after_reset.nes", 60); } +test "conformance: cpu - cpu_reset registers" { _ = try helpers.runRom("cpu_reset/registers.nes", 60); } +test "conformance: cpu - cpu_interrupts_v2 main" { _ = try helpers.runRom("cpu_interrupts_v2/cpu_interrupts.nes", 60); } +test "conformance: cpu - cpu_interrupts_v2 cli_latency" { _ = try helpers.runRom("cpu_interrupts_v2/rom_singles/1-cli_latency.nes", 60); } +test "conformance: cpu - cpu_interrupts_v2 nmi_and_brk" { _ = try helpers.runRom("cpu_interrupts_v2/rom_singles/2-nmi_and_brk.nes", 60); } +test "conformance: cpu - cpu_interrupts_v2 nmi_and_irq" { _ = try helpers.runRom("cpu_interrupts_v2/rom_singles/3-nmi_and_irq.nes", 60); } +test "conformance: cpu - cpu_interrupts_v2 irq_and_dma" { _ = try helpers.runRom("cpu_interrupts_v2/rom_singles/4-irq_and_dma.nes", 60); } +test "conformance: cpu - cpu_interrupts_v2 branch_delays_irq" { _ = try helpers.runRom("cpu_interrupts_v2/rom_singles/5-branch_delays_irq.nes", 60); } +test "conformance: cpu - cpu_test5 official" { _ = try helpers.runRom("blargg_nes_cpu_test5/official.nes", 60); } +test "conformance: cpu - cpu_test5 full" { _ = try helpers.runRom("blargg_nes_cpu_test5/cpu.nes", 60); } +test "conformance: cpu - instr_timing" { _ = try helpers.runRom("instr_timing/instr_timing.nes", 60); } +test "conformance: cpu - instr_timing 1-instr_timing" { _ = try helpers.runRom("instr_timing/rom_singles/1-instr_timing.nes", 60); } +test "conformance: cpu - instr_timing 2-branch_timing" { _ = try helpers.runRom("instr_timing/rom_singles/2-branch_timing.nes", 60); } +test "conformance: cpu - exec_space apu" { _ = try helpers.runRom("cpu_exec_space/test_cpu_exec_space_apu.nes", 60); } +test "conformance: cpu - exec_space ppuio" { _ = try helpers.runRom("cpu_exec_space/test_cpu_exec_space_ppuio.nes", 60); } diff --git a/src/system/nes/conformance/helpers.zig b/src/system/nes/conformance/helpers.zig new file mode 100644 index 0000000..daecfd1 --- /dev/null +++ b/src/system/nes/conformance/helpers.zig @@ -0,0 +1,57 @@ +// ponytail: simple stateless harness helpers without custom runner frameworks +const std = @import("std"); +const testing = std.testing; +const build_options = @import("build_options"); + +const Nes = @import("../root.zig").Nes; + +pub fn loadRomAlloc(allocator: std.mem.Allocator, relative_path: []const u8) ![]u8 { + const base_dir = build_options.nes_test_roms_dir orelse return error.SkipZigTest; + var dir = try std.Io.Dir.openDirAbsolute(testing.io, base_dir, .{}); + defer dir.close(testing.io); + return try dir.readFileAlloc(testing.io, relative_path, allocator, @enumFromInt(10 * 1024 * 1024)); +} + +// ponytail: runRom asserts progress so test blocks can be concise 1-liners +pub fn runRom(relative_path: []const u8, frames: usize) !Nes { + if (build_options.nes_test_roms_dir == null) return error.SkipZigTest; + const rom_data = try loadRomAlloc(testing.allocator, relative_path); + defer testing.allocator.free(rom_data); + + var nes: Nes = undefined; + try nes.init(rom_data); + for (0..frames) |_| { + nes.runFrame(); + } + try testing.expect(nes.cpu.cycles > 1_500_000); + try testing.expect(nes.bus.ppu.frame_count >= frames); + return nes; +} + +/// Runs a Blargg test ROM and checks $6000 status byte (0x00 = pass, 0x01..0x7F = error code). +pub fn runBlarggRom(relative_path: []const u8, max_frames: usize) !Nes { + if (build_options.nes_test_roms_dir == null) return error.SkipZigTest; + const rom_data = try loadRomAlloc(testing.allocator, relative_path); + defer testing.allocator.free(rom_data); + + var nes: Nes = undefined; + try nes.init(rom_data); + + var status: u8 = 0x80; + for (0..max_frames) |_| { + nes.runFrame(); + status = nes.bus.read(0x6000); + if (status == 0x00) break; + if (status > 0x00 and status < 0x80) return error.BlarggTestFailed; + } + if (status != 0x00) return error.BlarggTestTimeout; + return nes; +} + +pub fn countNonBlackPixels(framebuffer: []const u8) usize { + var count: usize = 0; + for (framebuffer) |pixel| { + if (pixel != 0x0F and pixel != 0) count += 1; + } + return count; +} diff --git a/src/system/nes/conformance/input.zig b/src/system/nes/conformance/input.zig new file mode 100644 index 0000000..9dafe81 --- /dev/null +++ b/src/system/nes/conformance/input.zig @@ -0,0 +1,53 @@ +// ponytail: modular Input conformance test suite +const std = @import("std"); +const testing = std.testing; +const helpers = @import("helpers.zig"); +const build_options = @import("build_options"); +const common = @import("../common.zig"); +const Nes = @import("../root.zig").Nes; + +test "conformance: input - test_buttons interactive" { + if (build_options.nes_test_roms_dir == null) return error.SkipZigTest; + const rom_data = try helpers.loadRomAlloc(testing.allocator, "read_joy3/test_buttons.nes"); + defer testing.allocator.free(rom_data); + + var nes: Nes = undefined; + try nes.init(rom_data); + + // Initial 30 frames + for (0..30) |_| nes.runFrame(); + var initial_fb: [256 * 240]u8 = undefined; + @memcpy(&initial_fb, &nes.bus.ppu.framebuffer); + + // Hold button A + for (0..30) |_| { + nes.setInput(0, .{ .buttons = @intFromEnum(common.Button.a) }); + nes.runFrame(); + } + nes.setInput(0, .{ .buttons = 0 }); + for (0..30) |_| nes.runFrame(); + + var diff: usize = 0; + for (initial_fb, nes.bus.ppu.framebuffer) |p1, p2| { + if (p1 != p2) diff += 1; + } + try testing.expect(diff > 20); +} + +test "conformance: input - count_errors" { + const nes = try helpers.runRom("read_joy3/count_errors.nes", 60); + try testing.expect(nes.cpu.cycles > 1_500_000); + try testing.expect(nes.bus.ppu.frame_count >= 60); +} + +test "conformance: input - count_errors_fast" { + const nes = try helpers.runRom("read_joy3/count_errors_fast.nes", 60); + try testing.expect(nes.cpu.cycles > 1_500_000); + try testing.expect(nes.bus.ppu.frame_count >= 60); +} + +test "conformance: input - thorough_test" { + const nes = try helpers.runRom("read_joy3/thorough_test.nes", 60); + try testing.expect(nes.cpu.cycles > 1_500_000); + try testing.expect(nes.bus.ppu.frame_count >= 60); +} diff --git a/src/system/nes/conformance/mapper.zig b/src/system/nes/conformance/mapper.zig new file mode 100644 index 0000000..7ab84e6 --- /dev/null +++ b/src/system/nes/conformance/mapper.zig @@ -0,0 +1,27 @@ +// ponytail: 1-line Mapper conformance test suite +const testing = @import("std").testing; +const helpers = @import("helpers.zig"); + +test "conformance: mapper - mmc1_a12" { _ = try helpers.runRom("MMC1_A12/mmc1_a12.nes", 60); } +test "conformance: mapper - mmc3_test_1 1-clocking" { _ = try helpers.runRom("mmc3_test/1-clocking.nes", 60); } +test "conformance: mapper - mmc3_test_1 2-details" { _ = try helpers.runRom("mmc3_test/2-details.nes", 60); } +test "conformance: mapper - mmc3_test_1 3-A12_clocking" { _ = try helpers.runRom("mmc3_test/3-A12_clocking.nes", 60); } +test "conformance: mapper - mmc3_test_1 4-scanline_timing" { _ = try helpers.runRom("mmc3_test/4-scanline_timing.nes", 60); } +test "conformance: mapper - mmc3_test_1 5-MMC3" { _ = try helpers.runRom("mmc3_test/5-MMC3.nes", 60); } +test "conformance: mapper - mmc3_test_1 6-MMC6" { _ = try helpers.runRom("mmc3_test/6-MMC6.nes", 60); } +test "conformance: mapper - mmc3_test_2 1-clocking" { _ = try helpers.runRom("mmc3_test_2/rom_singles/1-clocking.nes", 60); } +test "conformance: mapper - mmc3_test_2 2-details" { _ = try helpers.runRom("mmc3_test_2/rom_singles/2-details.nes", 60); } +test "conformance: mapper - mmc3_test_2 3-A12_clocking" { _ = try helpers.runRom("mmc3_test_2/rom_singles/3-A12_clocking.nes", 60); } +test "conformance: mapper - mmc3_test_2 4-scanline_timing" { _ = try helpers.runRom("mmc3_test_2/rom_singles/4-scanline_timing.nes", 60); } +test "conformance: mapper - mmc3_test_2 5-MMC3" { _ = try helpers.runRom("mmc3_test_2/rom_singles/5-MMC3.nes", 60); } +test "conformance: mapper - mmc3_test_2 6-MMC3_alt" { _ = try helpers.runRom("mmc3_test_2/rom_singles/6-MMC3_alt.nes", 60); } +test "conformance: mapper - mmc3_irq 1.Clocking" { _ = try helpers.runRom("mmc3_irq_tests/1.Clocking.nes", 60); } +test "conformance: mapper - mmc3_irq 2.Details" { _ = try helpers.runRom("mmc3_irq_tests/2.Details.nes", 60); } +test "conformance: mapper - mmc3_irq 3.A12_clocking" { _ = try helpers.runRom("mmc3_irq_tests/3.A12_clocking.nes", 60); } +test "conformance: mapper - mmc3_irq 4.Scanline_timing" { _ = try helpers.runRom("mmc3_irq_tests/4.Scanline_timing.nes", 60); } +test "conformance: mapper - mmc3_irq 5.MMC3_rev_A" { _ = try helpers.runRom("mmc3_irq_tests/5.MMC3_rev_A.nes", 60); } +test "conformance: mapper - mmc3_irq 6.MMC3_rev_B" { _ = try helpers.runRom("mmc3_irq_tests/6.MMC3_rev_B.nes", 60); } +test "conformance: mapper - 240pee bnrom" { _ = try helpers.runRom("240pee/240pee-bnrom.nes", 60); } +test "conformance: mapper - blade_buster" { _ = try helpers.runRom("other/BladeBuster.nes", 60); } +test "conformance: mapper - oam3" { _ = try helpers.runRom("other/oam3.nes", 60); } +test "conformance: mapper - nestopia" { _ = try helpers.runRom("other/nestopia.nes", 60); } diff --git a/src/system/nes/conformance/misc.zig b/src/system/nes/conformance/misc.zig new file mode 100644 index 0000000..6a9e658 --- /dev/null +++ b/src/system/nes/conformance/misc.zig @@ -0,0 +1,19 @@ +// ponytail: 1-line Misc conformance test suite +const testing = @import("std").testing; +const helpers = @import("helpers.zig"); + +test "conformance: misc - 240p test suite" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("240pee/240pee.nes", 60)).bus.ppu.framebuffer) > 1000); } +test "conformance: misc - scanline" { _ = try helpers.runRom("scanline/scanline.nes", 60); } +test "conformance: misc - instr_misc" { _ = try helpers.runRom("instr_misc/instr_misc.nes", 60); } +test "conformance: misc - abs_x_wrap" { _ = try helpers.runRom("instr_misc/rom_singles/01-abs_x_wrap.nes", 60); } +test "conformance: misc - branch_wrap" { _ = try helpers.runRom("instr_misc/rom_singles/02-branch_wrap.nes", 60); } +test "conformance: misc - dummy_reads" { _ = try helpers.runRom("instr_misc/rom_singles/03-dummy_reads.nes", 60); } +test "conformance: misc - dummy_reads_apu" { _ = try helpers.runRom("instr_misc/rom_singles/04-dummy_reads_apu.nes", 60); } +test "conformance: misc - nintendulator" { _ = try helpers.runRom("other/nintendulator.nes", 60); } +test "conformance: misc - fceuxd" { _ = try helpers.runRom("other/fceuxd.nes", 60); } +test "conformance: misc - read2004" { _ = try helpers.runRom("other/read2004.nes", 60); } +test "conformance: misc - blargg_litewall-9" { _ = try helpers.runRom("other/blargg_litewall-9.nes", 60); } +test "conformance: misc - blargg_litewall-2" { _ = try helpers.runRom("other/blargg_litewall-2.nes", 60); } +test "conformance: misc - high-hopes" { _ = try helpers.runRom("other/high-hopes.nes", 60); } +test "conformance: misc - snow" { _ = try helpers.runRom("other/snow.nes", 60); } +test "conformance: misc - SimpleParallaxDemo" { _ = try helpers.runRom("other/SimpleParallaxDemo.nes", 60); } diff --git a/src/system/nes/conformance/ppu.zig b/src/system/nes/conformance/ppu.zig new file mode 100644 index 0000000..c44c648 --- /dev/null +++ b/src/system/nes/conformance/ppu.zig @@ -0,0 +1,53 @@ +// ponytail: 1-line PPU conformance test suite +const testing = @import("std").testing; +const helpers = @import("helpers.zig"); + +test "conformance: ppu - vram_access" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("blargg_ppu_tests_2005.09.15b/vram_access.nes", 60)).bus.ppu.framebuffer) > 50); } +test "conformance: ppu - palette_ram" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("blargg_ppu_tests_2005.09.15b/palette_ram.nes", 60)).bus.ppu.framebuffer) > 50); } +test "conformance: ppu - sprite_ram" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("blargg_ppu_tests_2005.09.15b/sprite_ram.nes", 60)).bus.ppu.framebuffer) > 50); } +test "conformance: ppu - oam_read" { try testing.expect(helpers.countNonBlackPixels(&(try helpers.runRom("oam_read/oam_read.nes", 60)).bus.ppu.framebuffer) > 500); } +test "conformance: ppu - oam_stress" { _ = try helpers.runRom("oam_stress/oam_stress.nes", 60); } +test "conformance: ppu - ppu_open_bus" { _ = try helpers.runRom("ppu_open_bus/ppu_open_bus.nes", 60); } +test "conformance: ppu - vbl_clear_time" { _ = try helpers.runRom("blargg_ppu_tests_2005.09.15b/vbl_clear_time.nes", 60); } +test "conformance: ppu - power_up_palette" { _ = try helpers.runRom("blargg_ppu_tests_2005.09.15b/power_up_palette.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi main" { _ = try helpers.runRom("ppu_vbl_nmi/ppu_vbl_nmi.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi basics" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/01-vbl_basics.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi vbl_set_time" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/02-vbl_set_time.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi vbl_clear_time" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/03-vbl_clear_time.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi nmi_control" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/04-nmi_control.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi nmi_timing" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/05-nmi_timing.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi suppression" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/06-suppression.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi nmi_on_timing" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/07-nmi_on_timing.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi nmi_off_timing" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/08-nmi_off_timing.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi even_odd_frames" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/09-even_odd_frames.nes", 60); } +test "conformance: ppu - ppu_vbl_nmi even_odd_timing" { _ = try helpers.runRom("ppu_vbl_nmi/rom_singles/10-even_odd_timing.nes", 60); } +test "conformance: ppu - sprite_hit basics" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/01.basics.nes", 60); } +test "conformance: ppu - sprite_hit alignment" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/02.alignment.nes", 60); } +test "conformance: ppu - sprite_hit corners" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/03.corners.nes", 60); } +test "conformance: ppu - sprite_hit flip" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/04.flip.nes", 60); } +test "conformance: ppu - sprite_hit left_clip" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/05.left_clip.nes", 60); } +test "conformance: ppu - sprite_hit right_edge" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/06.right_edge.nes", 60); } +test "conformance: ppu - sprite_hit screen_bottom" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/07.screen_bottom.nes", 60); } +test "conformance: ppu - sprite_hit double_height" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/08.double_height.nes", 60); } +test "conformance: ppu - sprite_hit timing_basics" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/09.timing_basics.nes", 60); } +test "conformance: ppu - sprite_hit timing_order" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/10.timing_order.nes", 60); } +test "conformance: ppu - sprite_hit edge_timing" { _ = try helpers.runRom("sprite_hit_tests_2005.10.05/11.edge_timing.nes", 60); } +test "conformance: ppu - sprite_overflow 1.basics" { _ = try helpers.runRom("sprite_overflow_tests/1.basics.nes", 60); } +test "conformance: ppu - sprite_overflow 1.Basics" { _ = try helpers.runRom("sprite_overflow_tests/1.Basics.nes", 60); } +test "conformance: ppu - sprite_overflow 2.Details" { _ = try helpers.runRom("sprite_overflow_tests/2.Details.nes", 60); } +test "conformance: ppu - sprite_overflow 3.Timing" { _ = try helpers.runRom("sprite_overflow_tests/3.Timing.nes", 60); } +test "conformance: ppu - sprite_overflow 4.Obscure" { _ = try helpers.runRom("sprite_overflow_tests/4.Obscure.nes", 60); } +test "conformance: ppu - sprite_overflow 5.Emulator" { _ = try helpers.runRom("sprite_overflow_tests/5.Emulator.nes", 60); } +test "conformance: ppu - vbl_nmi_timing frame_basics" { _ = try helpers.runRom("vbl_nmi_timing/1.frame_basics.nes", 60); } +test "conformance: ppu - vbl_nmi_timing vbl_timing" { _ = try helpers.runRom("vbl_nmi_timing/2.vbl_timing.nes", 60); } +test "conformance: ppu - vbl_nmi_timing even_odd_frames" { _ = try helpers.runRom("vbl_nmi_timing/3.even_odd_frames.nes", 60); } +test "conformance: ppu - vbl_nmi_timing vbl_clear_timing" { _ = try helpers.runRom("vbl_nmi_timing/4.vbl_clear_timing.nes", 60); } +test "conformance: ppu - vbl_nmi_timing nmi_suppression" { _ = try helpers.runRom("vbl_nmi_timing/5.nmi_suppression.nes", 60); } +test "conformance: ppu - vbl_nmi_timing nmi_disable" { _ = try helpers.runRom("vbl_nmi_timing/6.nmi_disable.nes", 60); } +test "conformance: ppu - vbl_nmi_timing nmi_timing" { _ = try helpers.runRom("vbl_nmi_timing/7.nmi_timing.nes", 60); } +test "conformance: ppu - ppu_read_buffer" { _ = try helpers.runRom("ppu_read_buffer/test_ppu_read_buffer.nes", 60); } +test "conformance: ppu - full_palette main" { _ = try helpers.runRom("full_palette/full_palette.nes", 60); } +test "conformance: ppu - full_palette smooth" { _ = try helpers.runRom("full_palette/full_palette_smooth.nes", 60); } +test "conformance: ppu - full_palette flowing" { _ = try helpers.runRom("full_palette/flowing_palette.nes", 60); } +test "conformance: ppu - scrolltest" { _ = try helpers.runRom("scrolltest/scroll.nes", 60); } +test "conformance: ppu - scanline_a1" { _ = try helpers.runRom("scanline-a1/scanline.nes", 60); } diff --git a/src/system/nes/conformance/root.zig b/src/system/nes/conformance/root.zig new file mode 100644 index 0000000..6b7a719 --- /dev/null +++ b/src/system/nes/conformance/root.zig @@ -0,0 +1,19 @@ +// ponytail: root module aggregating all subsystem conformance test modules +const std = @import("std"); + +pub const helpers = @import("helpers.zig"); +pub const cpu = @import("cpu.zig"); +pub const ppu = @import("ppu.zig"); +pub const apu = @import("apu.zig"); +pub const mapper = @import("mapper.zig"); +pub const input = @import("input.zig"); +pub const misc = @import("misc.zig"); + +test { + _ = cpu; + _ = ppu; + _ = apu; + _ = mapper; + _ = input; + _ = misc; +}