Zesty - a pin-accurate, cycle-accurate NES emulator written in Zig

cpu: add stack operations #2

merged opened by pluie.me targeting main from pluie/jj-tpnmozqyuktv
Labels

None yet.

Participants 1
AT URI
at://did:plc:e4f33w5yt2m54tq6vsagpwiu/sh.tangled.repo.pull/3lyo7elked222
+200 -2
Diff #1
+69 -2
src/Cpu.zig
··· 112 0x01 => if (self.zpXInd(pins)) |_| self.ora(pins, v), // ORA (zp,X) 113 0x05 => if (self.zp(pins)) |_| self.ora(pins, v), // ORA zp 114 0x06 => if (self.zp(pins)) |_| self.asl(pins, .mem), // ASL zp 115 0x09 => if (self.imm(pins)) |_| self.ora(pins, v), // ORA # 116 0x0a => if (self.imm(pins)) |_| self.asl(pins, .acc), // ASL A 117 0x0d => if (self.abs(pins)) |_| self.ora(pins, v), // ORA abs ··· 131 0x24 => if (self.zp(pins)) |_| self.bit(pins), // BIT zp 132 0x25 => if (self.zp(pins)) |_| self._and(pins, v), // AND zp 133 0x26 => if (self.zp(pins)) |_| self.rol(pins, .mem), // ROL zp 134 0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND # 135 0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A 136 0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs ··· 150 0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X) 151 0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp 152 0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp 153 0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR # 154 0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A 155 0x4c => self.jmp(pins), // JMP abs ··· 169 0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X) 170 0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp 171 0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp 172 0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC # 173 0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A 174 0x6c => self.jmpInd(pins), // JMP (ind) ··· 425 } 426 427 //------------------------------------------------------ 428 - // Opcodes: Load/Store & Arithmetic 429 - const Dst = enum { acc, mem }; 430 431 inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void { 432 to.* = from; ··· 438 pins.cpu_rw = .write; 439 self.fetch(pins); 440 } 441 inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void { 442 self.a |= v; 443 self.setNZ(self.a);
··· 112 0x01 => if (self.zpXInd(pins)) |_| self.ora(pins, v), // ORA (zp,X) 113 0x05 => if (self.zp(pins)) |_| self.ora(pins, v), // ORA zp 114 0x06 => if (self.zp(pins)) |_| self.asl(pins, .mem), // ASL zp 115 + 0x08 => self.php(pins), // PHP 116 0x09 => if (self.imm(pins)) |_| self.ora(pins, v), // ORA # 117 0x0a => if (self.imm(pins)) |_| self.asl(pins, .acc), // ASL A 118 0x0d => if (self.abs(pins)) |_| self.ora(pins, v), // ORA abs ··· 132 0x24 => if (self.zp(pins)) |_| self.bit(pins), // BIT zp 133 0x25 => if (self.zp(pins)) |_| self._and(pins, v), // AND zp 134 0x26 => if (self.zp(pins)) |_| self.rol(pins, .mem), // ROL zp 135 + 0x28 => self.plp(pins), // PLP 136 0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND # 137 0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A 138 0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs ··· 152 0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X) 153 0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp 154 0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp 155 + 0x48 => self.pha(pins), // PHA 156 0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR # 157 0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A 158 0x4c => self.jmp(pins), // JMP abs ··· 172 0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X) 173 0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp 174 0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp 175 + 0x68 => self.pla(pins), // PLA 176 0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC # 177 0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A 178 0x6c => self.jmpInd(pins), // JMP (ind) ··· 429 } 430 431 //------------------------------------------------------ 432 + // Opcodes: Load/Store 433 434 inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void { 435 to.* = from; ··· 441 pins.cpu_rw = .write; 442 self.fetch(pins); 443 } 444 + inline fn push(self: *Cpu, pins: *zesty.Pins, v: u8) void { 445 + switch (self.cycle) { 446 + // Dummy read 447 + 0 => pins.cpu_addr = self.pc, 448 + 1 => { 449 + self.hilo = .stack(self.sp); 450 + pins.cpu_addr = self.hilo.addr(); 451 + pins.cpu_data = v; 452 + pins.cpu_rw = .write; 453 + self.sp -%= 1; 454 + }, 455 + else => self.fetch(pins), 456 + } 457 + } 458 + inline fn pull(self: *Cpu, pins: *zesty.Pins) ?u8 { 459 + switch (self.cycle) { 460 + // Dummy read 461 + 0 => pins.cpu_addr = self.pc, 462 + 1 => { 463 + self.hilo = .stack(self.sp); 464 + pins.cpu_addr = self.hilo.addr(); 465 + self.sp +%= 1; 466 + }, 467 + 2 => { 468 + self.hilo = .stack(self.sp); 469 + pins.cpu_addr = self.hilo.addr(); 470 + }, 471 + else => { 472 + self.fetch(pins); 473 + return pins.cpu_data; 474 + }, 475 + } 476 + return null; 477 + } 478 + inline fn pha(self: *Cpu, pins: *zesty.Pins) void { 479 + self.push(pins, self.a); 480 + } 481 + inline fn php(self: *Cpu, pins: *zesty.Pins) void { 482 + var status = self.status; 483 + // BRK is always set to true here 484 + status.brk = true; 485 + self.push(pins, status.toByte()); 486 + } 487 + inline fn pla(self: *Cpu, pins: *zesty.Pins) void { 488 + self.a = self.pull(pins) orelse return; 489 + self.setNZ(self.a); 490 + } 491 + inline fn plp(self: *Cpu, pins: *zesty.Pins) void { 492 + const status: Status = .from(self.pull(pins) orelse return); 493 + self.status = .{ 494 + .negative = status.negative, 495 + .overflow = status.overflow, 496 + .brk = self.status.brk, // Do not inherit BRK. 497 + .decimal = status.decimal, 498 + .irq_disabled = status.irq_disabled, 499 + .zero = status.zero, 500 + .carry = status.carry, 501 + }; 502 + } 503 + 504 + //------------------------------------------------------ 505 + // Opcodes: Arithmetic 506 + 507 + const Dst = enum { acc, mem }; 508 inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void { 509 self.a |= v; 510 self.setNZ(self.a);
+1
src/tests/cpu.zig
··· 9 _ = @import("cpu/addressing.zig"); 10 _ = @import("cpu/alu.zig"); 11 _ = @import("cpu/control_flow.zig"); 12 } 13 14 //------------------------------------------------------
··· 9 _ = @import("cpu/addressing.zig"); 10 _ = @import("cpu/alu.zig"); 11 _ = @import("cpu/control_flow.zig"); 12 + _ = @import("cpu/stack.zig"); 13 } 14 15 //------------------------------------------------------
+34
src/tests/cpu/assembler.zig
··· 73 stx, 74 sty, 75 76 ora, 77 @"and", 78 eor, ··· 382 .implied => 0xea, 383 else => null, 384 }, 385 }; 386 } 387 };
··· 73 stx, 74 sty, 75 76 + php, 77 + plp, 78 + pha, 79 + pla, 80 + 81 ora, 82 @"and", 83 eor, ··· 387 .implied => 0xea, 388 else => null, 389 }, 390 + .pha => switch (opr) { 391 + .implied => 0x48, 392 + else => null, 393 + }, 394 + .pla => switch (opr) { 395 + .implied => 0x68, 396 + else => null, 397 + }, 398 + .php => switch (opr) { 399 + .implied => 0x08, 400 + else => null, 401 + }, 402 + .plp => switch (opr) { 403 + .implied => 0x28, 404 + else => null, 405 + }, 406 + .jmp => switch (opr) { 407 + .absolute => 0x4c, 408 + .indirect => 0x6c, 409 + else => null, 410 + }, 411 + .jsr => switch (opr) { 412 + .absolute => 0x20, 413 + else => null, 414 + }, 415 + .rts => switch (opr) { 416 + .implied => 0x60, 417 + else => null, 418 + }, 419 }; 420 } 421 };
+96
src/tests/cpu/stack.zig
···
··· 1 + //! Stack-related tests. 2 + const std = @import("std"); 3 + const cpu = @import("../cpu.zig"); 4 + 5 + test "PHA/PLA" { 6 + var z = cpu.testZes(.{ .rom = &.{ 7 + .{ 8 + 0x8000, cpu.assemble( 9 + \\LDA #$89 10 + \\PHA 11 + \\LDA #0 12 + \\PLA 13 + ), 14 + }, 15 + } }); 16 + cpu.run(&z); // Setup A register 17 + 18 + try std.testing.expectEqual(0xfd, z.cpu.sp); 19 + cpu.run(&z); // PHA 20 + try std.testing.expectEqual(0xfc, z.cpu.sp); 21 + try std.testing.expectEqual(0x8003, z.cpu.pc); 22 + try std.testing.expectEqual(0x89, z.ram[0x1fd]); 23 + try std.testing.expect(z.cpu.status.negative); 24 + try std.testing.expect(!z.cpu.status.zero); 25 + 26 + cpu.run(&z); // Reset A register 27 + try std.testing.expectEqual(0x8005, z.cpu.pc); 28 + try std.testing.expectEqual(0x00, z.cpu.a); 29 + try std.testing.expect(!z.cpu.status.negative); 30 + try std.testing.expect(z.cpu.status.zero); 31 + 32 + cpu.run(&z); // PLA 33 + try std.testing.expectEqual(0x8006, z.cpu.pc); 34 + try std.testing.expectEqual(0x89, z.cpu.a); 35 + try std.testing.expectEqual(0xfd, z.cpu.sp); 36 + try std.testing.expect(z.cpu.status.negative); 37 + try std.testing.expect(!z.cpu.status.zero); 38 + } 39 + 40 + test "PHP/PLP" { 41 + var z = cpu.testZes(.{ .rom = &.{ 42 + .{ 43 + 0x8000, cpu.assemble( 44 + \\CLI 45 + \\SED 46 + \\LDA #0 47 + \\PHP 48 + \\ 49 + \\SEI 50 + \\CLD 51 + \\LDA #$ff 52 + \\PLP 53 + ), 54 + }, 55 + } }); 56 + 57 + // Prepare flags 58 + cpu.run(&z); // Clear I 59 + cpu.run(&z); // Set D 60 + cpu.run(&z); // Set Z 61 + try std.testing.expect(!z.cpu.status.negative); 62 + try std.testing.expect(z.cpu.status.zero); 63 + try std.testing.expect(z.cpu.status.decimal); 64 + try std.testing.expect(!z.cpu.status.irq_disabled); 65 + 66 + try std.testing.expectEqual(0xfd, z.cpu.sp); 67 + cpu.run(&z); // PHP 68 + try std.testing.expectEqual(0xfc, z.cpu.sp); 69 + 70 + try std.testing.expectEqual(0x8005, z.cpu.pc); 71 + try std.testing.expectEqual(0b00111010, z.ram[0x1fd]); 72 + 73 + // Reset flags 74 + cpu.run(&z); // Set I 75 + cpu.run(&z); // Clear D 76 + cpu.run(&z); // Clear Z; Set N 77 + try std.testing.expect(z.cpu.status.negative); 78 + try std.testing.expect(!z.cpu.status.zero); 79 + try std.testing.expect(!z.cpu.status.decimal); 80 + try std.testing.expect(z.cpu.status.irq_disabled); 81 + 82 + try std.testing.expectEqual(0xfc, z.cpu.sp); 83 + cpu.run(&z); // PLP 84 + try std.testing.expectEqual(0xfd, z.cpu.sp); 85 + 86 + try std.testing.expectEqual(0x800a, z.cpu.pc); 87 + 88 + // Check if flags are restored 89 + try std.testing.expect(!z.cpu.status.negative); 90 + try std.testing.expect(z.cpu.status.zero); 91 + try std.testing.expect(z.cpu.status.decimal); 92 + try std.testing.expect(!z.cpu.status.irq_disabled); 93 + 94 + // BRK should not be restored. 95 + try std.testing.expect(!z.cpu.status.brk); 96 + }

History

2 rounds 0 comments
sign up or login to add to the discussion
1 commit
expand
cpu: add stack operations
expand 0 comments
pull request successfully merged
pluie.me submitted #0
1 commit
expand
cpu: add stack operations
expand 0 comments