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
+187 -2
Diff #0
+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 ··· 130 0x24 => if (self.zp(pins)) |_| self.bit(pins), // BIT zp 131 0x25 => if (self.zp(pins)) |_| self._and(pins, v), // AND zp 132 0x26 => if (self.zp(pins)) |_| self.rol(pins, .mem), // ROL zp 133 0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND # 134 0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A 135 0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs ··· 149 0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X) 150 0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp 151 0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp 152 0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR # 153 0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A 154 0x4d => if (self.abs(pins)) |_| self.eor(pins, v), // EOR abs ··· 166 0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X) 167 0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp 168 0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp 169 0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC # 170 0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A 171 0x6d => if (self.abs(pins)) |_| self.adc(pins, v), // ADC abs ··· 421 } 422 423 //------------------------------------------------------ 424 - // Opcodes: Load/Store & Arithmetic 425 - const Dst = enum { acc, mem }; 426 427 inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void { 428 to.* = from; ··· 434 pins.cpu_rw = .write; 435 self.fetch(pins); 436 } 437 inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void { 438 self.a |= v; 439 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 ··· 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 + 0x28 => self.plp(pins), // PLP 135 0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND # 136 0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A 137 0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs ··· 151 0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X) 152 0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp 153 0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp 154 + 0x48 => self.pha(pins), // PHA 155 0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR # 156 0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A 157 0x4d => if (self.abs(pins)) |_| self.eor(pins, v), // EOR 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 + 0x68 => self.pla(pins), // PLA 173 0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC # 174 0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A 175 0x6d => if (self.abs(pins)) |_| self.adc(pins, v), // ADC abs ··· 425 } 426 427 //------------------------------------------------------ 428 + // Opcodes: Load/Store 429 430 inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void { 431 to.* = from; ··· 437 pins.cpu_rw = .write; 438 self.fetch(pins); 439 } 440 + inline fn push(self: *Cpu, pins: *zesty.Pins, v: u8) void { 441 + switch (self.cycle) { 442 + // Dummy read 443 + 0 => pins.cpu_addr = self.pc, 444 + 1 => { 445 + self.hilo = .stack(self.sp); 446 + pins.cpu_addr = @bitCast(self.hilo); 447 + pins.cpu_data = v; 448 + pins.cpu_rw = .write; 449 + self.sp -%= 1; 450 + }, 451 + else => self.fetch(pins), 452 + } 453 + } 454 + inline fn pull(self: *Cpu, pins: *zesty.Pins) ?u8 { 455 + switch (self.cycle) { 456 + // Dummy read 457 + 0 => pins.cpu_addr = self.pc, 458 + 1 => { 459 + self.hilo = .stack(self.sp); 460 + pins.cpu_addr = @bitCast(self.hilo); 461 + self.sp +%= 1; 462 + }, 463 + 2 => { 464 + self.hilo = .stack(self.sp); 465 + pins.cpu_addr = @bitCast(self.hilo); 466 + }, 467 + else => { 468 + self.fetch(pins); 469 + return pins.cpu_data; 470 + }, 471 + } 472 + return null; 473 + } 474 + inline fn pha(self: *Cpu, pins: *zesty.Pins) void { 475 + self.push(pins, self.a); 476 + } 477 + inline fn php(self: *Cpu, pins: *zesty.Pins) void { 478 + var status = self.status; 479 + // BRK is always set to true here 480 + status.brk = true; 481 + self.push(pins, status.toByte()); 482 + } 483 + inline fn pla(self: *Cpu, pins: *zesty.Pins) void { 484 + self.a = self.pull(pins) orelse return; 485 + self.setNZ(self.a); 486 + } 487 + inline fn plp(self: *Cpu, pins: *zesty.Pins) void { 488 + const status: Status = .from(self.pull(pins) orelse return); 489 + self.status = .{ 490 + .negative = status.negative, 491 + .overflow = status.overflow, 492 + .brk = self.status.brk, // Do not inherit BRK. 493 + .decimal = status.decimal, 494 + .irq_disabled = status.irq_disabled, 495 + .zero = status.zero, 496 + .carry = status.carry, 497 + }; 498 + } 499 + 500 + //------------------------------------------------------ 501 + // Opcodes: Arithmetic 502 + 503 + const Dst = enum { acc, mem }; 504 inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void { 505 self.a |= v; 506 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 //------------------------------------------------------
+21
src/tests/cpu/assembler.zig
··· 73 stx, 74 sty, 75 76 ora, 77 @"and", 78 eor, ··· 379 .implied => 0xea, 380 else => null, 381 }, 382 }; 383 } 384 };
··· 73 stx, 74 sty, 75 76 + php, 77 + plp, 78 + pha, 79 + pla, 80 + 81 ora, 82 @"and", 83 eor, ··· 384 .implied => 0xea, 385 else => null, 386 }, 387 + .pha => switch (opr) { 388 + .implied => 0x48, 389 + else => null, 390 + }, 391 + .pla => switch (opr) { 392 + .implied => 0x68, 393 + else => null, 394 + }, 395 + .php => switch (opr) { 396 + .implied => 0x08, 397 + else => null, 398 + }, 399 + .plp => switch (opr) { 400 + .implied => 0x28, 401 + else => null, 402 + }, 403 }; 404 } 405 };
+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.runDebug(&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