Zesty - a pin-accurate, cycle-accurate NES emulator written in Zig
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254//! Interrupt-related (IRQ/NMI/BRK/RESET) tests.const std = @import("std");const cpu = @import("../cpu.zig");
test "reset" { var z = cpu.testZes(.{ .init_cpu = false, .rom = &.{ .{ 0x8000, &.{0x69} }, }, }); while (!z.cpu.sync) z.stepCpu();
// Correctly read the first byte of ROM try std.testing.expectEqual(0x69, z.pins.cpu_data);}
test "BRK" { var z = cpu.testZes(.{ // Set a recognizable IRQ offset .irq = 0x8964, .rom = &.{ .{ 0x8000, cpu.assemble("brk") }, }, });
// Setup some initial setup state. z.cpu.status.carry = true; z.cpu.status.irq_disabled = true; z.cpu.status.negative = true;
var status_copy = z.cpu.status; // Since this is a BRK, the B flag will be set status_copy.brk = true;
while (!z.cpu.status.brk) z.stepCpu();
// Store PC hi try std.testing.expectEqual(0x01fd, z.pins.cpu_addr);
// Store PC lo z.stepCpu(); try std.testing.expectEqual(0x01fc, z.pins.cpu_addr);
// Store processor status z.stepCpu(); const status: u8 = @bitCast(status_copy); try std.testing.expectEqual(0x01fb, z.pins.cpu_addr);
// Fetch PC lo z.stepCpu(); try std.testing.expectEqual(0xfffe, z.pins.cpu_addr); try std.testing.expectEqual(0x64, z.pins.cpu_data);
// Fetch PC hi z.stepCpu(); try std.testing.expectEqual(0xffff, z.pins.cpu_addr); try std.testing.expectEqual(0x89, z.pins.cpu_data);
// PC should be reset correctly z.stepCpu(); try std.testing.expectEqual(0x8964, z.cpu.pc); try std.testing.expectEqual(0x8964, z.pins.cpu_addr);
// Old PC should remain in stack try std.testing.expectEqual(0x80, z.ram[0x1fd]); try std.testing.expectEqual(0x00, z.ram[0x1fc]); try std.testing.expectEqual(status, z.ram[0x1fb]);}
test "set and clear" { // TODO: Test CLV as well var z = cpu.testZes(.{ .rom = &.{ .{ 0x8000, cpu.assemble( \\sec \\cli \\sed \\clc \\sei \\cld ), }, } });
// Initial state try std.testing.expect(!z.cpu.status.carry); try std.testing.expect(z.cpu.status.irq_disabled); try std.testing.expect(!z.cpu.status.decimal);
cpu.run(&z); // SEC try std.testing.expectEqual(0x8001, z.cpu.pc); try std.testing.expect(z.cpu.status.carry);
cpu.run(&z); // CLI try std.testing.expectEqual(0x8002, z.cpu.pc); try std.testing.expect(!z.cpu.status.irq_disabled);
cpu.run(&z); // SED try std.testing.expectEqual(0x8003, z.cpu.pc); try std.testing.expect(z.cpu.status.decimal);
cpu.run(&z); // CLC try std.testing.expectEqual(0x8004, z.cpu.pc); try std.testing.expect(!z.cpu.status.carry);
cpu.run(&z); // SEI try std.testing.expectEqual(0x8005, z.cpu.pc); try std.testing.expect(z.cpu.status.irq_disabled);
cpu.run(&z); // CLD try std.testing.expectEqual(0x8006, z.cpu.pc); try std.testing.expect(!z.cpu.status.decimal);}
test "branches" { var z = cpu.testZes(.{ .rom = &.{ .{ 0x8000, cpu.assemble( \\sec \\bcs #5 \\ \\lda #%11000000 \\bmi #5 \\ \\lda #0 \\beq #$87 \\ \\bit $8004 \\bvs #2 \\brk \\nop ), }, } });
cpu.run(&z); // SEC try std.testing.expectEqual(0x8001, z.cpu.pc); try std.testing.expect(z.cpu.status.carry); cpu.run(&z); // BCS try std.testing.expectEqual(0x8007, z.cpu.pc); // LDA #80 should be skipped here. try std.testing.expect(!z.cpu.status.negative);
cpu.run(&z); // LDA #0 try std.testing.expectEqual(0x8009, z.cpu.pc); try std.testing.expect(z.cpu.status.zero); cpu.run(&z); // BEQ (jump backwards) try std.testing.expectEqual(0x8003, z.cpu.pc);
cpu.run(&z); // LDA #%11000000 try std.testing.expectEqual(0xa9, z.cpu.opcode); try std.testing.expectEqual(0x8005, z.cpu.pc); try std.testing.expect(z.cpu.status.negative);
// TODO: investigate why the CPU run helper doesn't stop after branching z.stepCpu(); // BMI z.stepCpu(); // BMI z.stepCpu(); // BMI try std.testing.expectEqual(0x800c, z.cpu.pc);
cpu.run(&z); // BIT try std.testing.expectEqual(0x800e, z.cpu.pc); try std.testing.expect(z.cpu.status.overflow);
z.stepCpu(); // BVS z.stepCpu(); // BVS z.stepCpu(); // BVS try std.testing.expectEqual(0x8011, z.cpu.pc);
// Never hit BRK try std.testing.expect(!z.cpu.status.brk);}
test "subroutines" { var z = cpu.testZes(.{ .rom = &.{ .{ 0x8000, cpu.assemble( \\jsr $8007 \\cmp $8008 \\brk \\lda #$80 \\rts ), }, } });
try std.testing.expectEqual(0xfd, z.cpu.sp); cpu.run(&z); // JSR try std.testing.expectEqual(0x8007, z.cpu.pc); try std.testing.expectEqual(0xfb, z.cpu.sp); try std.testing.expectEqual(0x01, z.ram[0x1fc]); try std.testing.expectEqual(0x80, z.ram[0x1fd]);
cpu.run(&z); // LDA try std.testing.expectEqual(0x8009, z.cpu.pc); try std.testing.expectEqual(0x80, z.cpu.a); try std.testing.expect(z.cpu.status.negative);
// TODO: figure out why the run harness runs two commands at once cpu.run(&z); // RTS, CMP try std.testing.expectEqual(0x8006, z.cpu.pc); try std.testing.expectEqual(0xfd, z.cpu.sp); try std.testing.expect(z.cpu.status.zero); // Should be equal
// Never hit BRK try std.testing.expect(!z.cpu.status.brk);}
test "JMP" { var z = cpu.testZes(.{ .rom = &.{ .{ 0x8000, cpu.assemble( \\jmp $8004 \\brk \\lda $8000 ), }, } });
cpu.run(&z); // JMP try std.testing.expectEqual(0x8004, z.cpu.pc);
cpu.run(&z); // LDA try std.testing.expectEqual(0x8007, z.cpu.pc); try std.testing.expectEqual(0x4c, z.cpu.a);
// Never hit BRK try std.testing.expect(!z.cpu.status.brk);}
test "JMP indirect" { var z = cpu.testZes(.{ .rom = &.{ .{ 0x8000, cpu.assemble( \\jmp ($aabb) \\brk \\lda $8000 ), }, .{ 0xaabb, &.{ 0x04, 0x80 } }, } });
cpu.run(&z); // JMP try std.testing.expectEqual(0x8004, z.cpu.pc);
cpu.run(&z); // LDA try std.testing.expectEqual(0x8007, z.cpu.pc); try std.testing.expectEqual(0x6c, z.cpu.a);
// Never hit BRK try std.testing.expect(!z.cpu.status.brk);}