From 5c6989154f6247a5cc5ff860df8ec2330fffd239 Mon Sep 17 00:00:00 2001 From: Leah Amelia Chen Date: Sat, 13 Sep 2025 00:41:26 +0200 Subject: [PATCH] cpu: all undocumented opcodes Change-Id: mwvpmvlzkqypumzkuqnsovtylltttqkw --- src/Cpu.zig | 632 +++++++++++++++++++++++++-------- src/tests/cpu.zig | 1 + src/tests/cpu/alu.zig | 16 +- src/tests/cpu/undocumented.zig | 47 +++ 4 files changed, 549 insertions(+), 147 deletions(-) create mode 100644 src/tests/cpu/undocumented.zig diff --git a/src/Cpu.zig b/src/Cpu.zig index 6112c13..013cac6 100644 --- a/src/Cpu.zig +++ b/src/Cpu.zig @@ -106,183 +106,293 @@ pub fn tick(self: *Cpu, pins: *zesty.Pins, last_pins: zesty.Pins) void { // log.err("BOBER={x}", .{self.opcode}); + @setEvalBranchQuota(2000); const v = pins.cpu_data; switch (self.opcode) { 0x00 => self.brk(pins), // BRK 0x01 => if (self.zpXInd(pins)) |_| self.ora(pins, v), // ORA (zp,X) + 0x02 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x03 => if (self.zpXInd(pins)) |c| self.slo(pins, c), // *SLO (zp,X) + 0x04 => if (self.zp(pins)) |_| self.fetch(pins), // *NOP zp 0x05 => if (self.zp(pins)) |_| self.ora(pins, v), // ORA zp - 0x06 => if (self.zp(pins)) |_| self.asl(pins, .mem), // ASL zp + 0x06 => if (self.zp(pins)) |c| self.asl(pins, c), // ASL zp + 0x07 => if (self.zp(pins)) |c| self.slo(pins, c), // *SLO zp 0x08 => self.php(pins), // PHP 0x09 => if (self.imm(pins)) |_| self.ora(pins, v), // ORA # - 0x0a => if (self.imm(pins)) |_| self.asl(pins, .acc), // ASL A + 0x0a => if (self.imp(pins)) |_| self.asla(pins), // ASL A + 0x0b => if (self.imm(pins)) |_| self.anc(pins), // *ANC # + 0x0c => if (self.abs(pins)) |_| self.fetch(pins), // *NOP abs 0x0d => if (self.abs(pins)) |_| self.ora(pins, v), // ORA abs - 0x0e => if (self.abs(pins)) |_| self.asl(pins, .mem), // ASL abs + 0x0e => if (self.abs(pins)) |c| self.asl(pins, c), // ASL abs + 0x0f => if (self.abs(pins)) |c| self.slo(pins, c), // *SLO abs - 0x10 => if (self.imm(pins)) |_| self.branch(pins, !self.status.negative), // BPL + 0x10 => if (self.imm(pins)) |_| self.branch(pins, !self.status.negative), // BPL # 0x11 => if (self.zpIndY(pins)) |_| self.ora(pins, v), // ORA (zp),Y + 0x12 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x13 => if (self.zpIndY(pins)) |c| self.slo(pins, c), // *SLO (zp),Y + 0x14 => if (self.zpOff(pins, self.x)) |_| self.fetch(pins), // *NOP zp,X 0x15 => if (self.zpOff(pins, self.x)) |_| self.ora(pins, v), // ORA zp,X - 0x16 => if (self.zpOff(pins, self.x)) |_| self.asl(pins, .mem), // ASL zp,X - 0x18 => self.set(pins, .carry, false), // CLC + 0x16 => if (self.zpOff(pins, self.x)) |c| self.asl(pins, c), // ASL zp,X + 0x17 => if (self.zpOff(pins, self.x)) |c| self.slo(pins, c), // *SLO zp,X + 0x18 => if (self.imp(pins)) |_| self.set(pins, .carry, false), // CLC 0x19 => if (self.absOff(pins, self.y)) |_| self.ora(pins, v), // ORA abs,Y + 0x1a => if (self.imp(pins)) |_| self.fetch(pins), // *NOP + 0x1b => if (self.absOff(pins, self.y)) |c| self.slo(pins, c), // *SLO abs,Y + 0x1c => if (self.absOff(pins, self.x)) |_| self.fetch(pins), // *NOP abs,X 0x1d => if (self.absOff(pins, self.x)) |_| self.ora(pins, v), // ORA abs,X - 0x1e => if (self.absOff(pins, self.x)) |_| self.asl(pins, .mem), // ASL abs,X + 0x1e => if (self.absOff(pins, self.x)) |c| self.asl(pins, c), // ASL abs,X + 0x1f => if (self.absOff(pins, self.x)) |c| self.slo(pins, c), // *SLO abs,X 0x20 => self.jsr(pins), // JSR abs 0x21 => if (self.zpXInd(pins)) |_| self._and(pins, v), // AND (zp,X) + 0x22 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x23 => if (self.zpXInd(pins)) |c| self.rla(pins, c), // *RLA (zp,X) 0x24 => if (self.zp(pins)) |_| self.bit(pins), // BIT zp 0x25 => if (self.zp(pins)) |_| self._and(pins, v), // AND zp - 0x26 => if (self.zp(pins)) |_| self.rol(pins, .mem), // ROL zp + 0x26 => if (self.zp(pins)) |c| self.rol(pins, c), // ROL zp + 0x27 => if (self.zp(pins)) |c| self.rla(pins, c), // *RLA zp 0x28 => self.plp(pins), // PLP 0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND # - 0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A + 0x2a => if (self.imp(pins)) |_| self.rola(pins), // ROL A + 0x2b => if (self.imm(pins)) |_| self.anc(pins), // *ANC # 0x2c => if (self.abs(pins)) |_| self.bit(pins), // BIT abs 0x2d => if (self.abs(pins)) |_| self._and(pins, v), // AND abs - 0x2e => if (self.abs(pins)) |_| self.rol(pins, .mem), // ROL abs + 0x2e => if (self.abs(pins)) |c| self.rol(pins, c), // ROL abs + 0x2f => if (self.abs(pins)) |c| self.rla(pins, c), // *RLA abs - 0x30 => if (self.imm(pins)) |_| self.branch(pins, self.status.negative), // BMI + 0x30 => if (self.imm(pins)) |_| self.branch(pins, self.status.negative), // BMI # 0x31 => if (self.zpIndY(pins)) |_| self._and(pins, v), // AND (zp),Y + 0x32 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x33 => if (self.zpIndY(pins)) |c| self.rla(pins, c), // *RLA (zp),Y + 0x34 => if (self.zpOff(pins, self.x)) |_| self.fetch(pins), // *NOP zp,X 0x35 => if (self.zpOff(pins, self.x)) |_| self._and(pins, v), // AND zp,X - 0x36 => if (self.zpOff(pins, self.x)) |_| self.rol(pins, .mem), // ROL zp,X - 0x38 => self.set(pins, .carry, true), // SEC + 0x36 => if (self.zpOff(pins, self.x)) |c| self.rol(pins, c), // ROL zp,X + 0x37 => if (self.zpOff(pins, self.x)) |c| self.rla(pins, c), // *RLA zp,X + 0x38 => if (self.imp(pins)) |_| self.set(pins, .carry, true), // SEC 0x39 => if (self.absOff(pins, self.y)) |_| self._and(pins, v), // AND abs,Y + 0x3a => if (self.imp(pins)) |_| self.fetch(pins), // *NOP + 0x3b => if (self.absOff(pins, self.y)) |c| self.rla(pins, c), // *RLA abs,Y + 0x3c => if (self.absOff(pins, self.x)) |_| self.fetch(pins), // *NOP abs,X 0x3d => if (self.absOff(pins, self.x)) |_| self._and(pins, v), // AND abs,X - 0x3e => if (self.absOff(pins, self.x)) |_| self.rol(pins, .mem), // ROL abs,X + 0x3e => if (self.absOff(pins, self.x)) |c| self.rol(pins, c), // ROL abs,X + 0x3f => if (self.absOff(pins, self.x)) |c| self.rla(pins, c), // *RLA abs,X 0x40 => self.rti(pins), // RTI 0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X) + 0x42 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x43 => if (self.zpXInd(pins)) |c| self.sre(pins, c), // *SRE (zp,X) + 0x44 => if (self.zp(pins)) |_| self.fetch(pins), // *NOP zp 0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp - 0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp + 0x46 => if (self.zp(pins)) |c| self.lsr(pins, c), // LSR zp + 0x47 => if (self.zp(pins)) |c| self.sre(pins, c), // *SRE zp 0x48 => self.pha(pins), // PHA 0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR # - 0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A + 0x4a => if (self.imp(pins)) |_| self.lsra(pins), // LSR A + 0x4b => if (self.imm(pins)) |_| self.alr(pins), // *ALR # 0x4c => self.jmp(pins), // JMP abs 0x4d => if (self.abs(pins)) |_| self.eor(pins, v), // EOR abs - 0x4e => if (self.abs(pins)) |_| self.lsr(pins, .mem), // LSR abs + 0x4e => if (self.abs(pins)) |c| self.lsr(pins, c), // LSR abs + 0x4f => if (self.abs(pins)) |c| self.sre(pins, c), // *SRE abs - 0x50 => if (self.imm(pins)) |_| self.branch(pins, !self.status.overflow), // BVC + 0x50 => if (self.imp(pins)) |_| self.branch(pins, !self.status.overflow), // BVC # 0x51 => if (self.zpIndY(pins)) |_| self.eor(pins, v), // EOR (zp),Y + 0x52 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x53 => if (self.zpIndY(pins)) |c| self.sre(pins, c), // *SRE (zp),Y + 0x54 => if (self.zpOff(pins, self.x)) |_| self.fetch(pins), // *NOP zp,X 0x55 => if (self.zpOff(pins, self.x)) |_| self.eor(pins, v), // EOR zp,X - 0x56 => if (self.zpOff(pins, self.x)) |_| self.lsr(pins, .mem), // LSR zp,X - 0x58 => self.set(pins, .irq_disabled, false), // CLI + 0x56 => if (self.zpOff(pins, self.x)) |c| self.lsr(pins, c), // LSR zp,X + 0x57 => if (self.zpOff(pins, self.x)) |c| self.sre(pins, c), // *SRE zp,X + 0x58 => if (self.imp(pins)) |_| self.set(pins, .irq_disabled, false), // CLI 0x59 => if (self.absOff(pins, self.y)) |_| self.eor(pins, v), // EOR abs,Y + 0x5a => if (self.imp(pins)) |_| self.fetch(pins), // *NOP + 0x5b => if (self.absOff(pins, self.y)) |c| self.sre(pins, c), // *SRE abs,Y + 0x5c => if (self.absOff(pins, self.x)) |_| self.fetch(pins), // *NOP abs,X 0x5d => if (self.absOff(pins, self.x)) |_| self.eor(pins, v), // EOR abs,X - 0x5e => if (self.absOff(pins, self.x)) |_| self.lsr(pins, .mem), // LSR abs,X + 0x5e => if (self.absOff(pins, self.x)) |c| self.lsr(pins, c), // LSR abs,X + 0x5f => if (self.absOff(pins, self.x)) |c| self.sre(pins, c), // *SRE abs,X 0x60 => self.rts(pins), // RTS 0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X) + 0x62 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x63 => if (self.zpXInd(pins)) |c| self.rra(pins, c), // *RRA (zp,X) + 0x64 => if (self.zp(pins)) |_| self.fetch(pins), // *NOP zp 0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp - 0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp + 0x66 => if (self.zp(pins)) |c| self.ror(pins, c), // ROR zp + 0x67 => if (self.zp(pins)) |c| self.rra(pins, c), // *RRA zp 0x68 => self.pla(pins), // PLA 0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC # - 0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A + 0x6a => if (self.imp(pins)) |_| self.rora(pins), // ROR A + 0x6b => if (self.imm(pins)) |_| self.arr(pins), // *ARR # 0x6c => self.jmpInd(pins), // JMP (ind) 0x6d => if (self.abs(pins)) |_| self.adc(pins, v), // ADC abs - 0x6e => if (self.abs(pins)) |_| self.ror(pins, .mem), // ROR abs + 0x6e => if (self.abs(pins)) |c| self.ror(pins, c), // ROR abs + 0x6f => if (self.abs(pins)) |c| self.rra(pins, c), // *RRA abs - 0x70 => if (self.imm(pins)) |_| self.branch(pins, self.status.overflow), // BVS + 0x70 => if (self.imm(pins)) |_| self.branch(pins, self.status.overflow), // BVS # 0x71 => if (self.zpIndY(pins)) |_| self.adc(pins, v), // ADC (zp),Y + 0x72 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x73 => if (self.zpIndY(pins)) |c| self.rra(pins, c), // *RRA (zp),Y + 0x74 => if (self.zpOff(pins, self.x)) |_| self.fetch(pins), // *NOP zp,X 0x75 => if (self.zpOff(pins, self.x)) |_| self.adc(pins, v), // ADC zp,X - 0x76 => if (self.zpOff(pins, self.x)) |_| self.ror(pins, .mem), // ROR zp,X - 0x78 => self.set(pins, .irq_disabled, true), // SEI + 0x76 => if (self.zpOff(pins, self.x)) |c| self.ror(pins, c), // ROR zp,X + 0x77 => if (self.zpOff(pins, self.x)) |c| self.rra(pins, c), // *RRA zp,X + 0x78 => if (self.imp(pins)) |_| self.set(pins, .irq_disabled, true), // SEI 0x79 => if (self.absOff(pins, self.y)) |_| self.adc(pins, v), // ADC abs,Y + 0x7a => if (self.imp(pins)) |_| self.fetch(pins), // *NOP + 0x7b => if (self.absOff(pins, self.y)) |c| self.rra(pins, c), // *RRA abs,Y + 0x7c => if (self.absOff(pins, self.x)) |_| self.fetch(pins), // *NOP abs,X 0x7d => if (self.absOff(pins, self.x)) |_| self.adc(pins, v), // ADC abs,X - 0x7e => if (self.absOff(pins, self.x)) |_| self.ror(pins, .mem), // ROR abs,X + 0x7e => if (self.absOff(pins, self.x)) |c| self.ror(pins, c), // ROR abs,X + 0x7f => if (self.absOff(pins, self.x)) |c| self.rra(pins, c), // *RRA abs,X + 0x80 => if (self.imm(pins)) |_| self.fetch(pins), // *NOP imm 0x81 => if (self.zpXInd(pins)) |_| self.st(pins, self.y), // STA (zp,X) + 0x82 => if (self.imm(pins)) |_| self.fetch(pins), // *NOP imm + 0x83 => if (self.zpXInd(pins)) |c| self.sax(pins, c), // *SAX (zp,X) 0x84 => if (self.zp(pins)) |_| self.st(pins, self.y), // STY zp 0x85 => if (self.zp(pins)) |_| self.st(pins, self.a), // STA zp 0x86 => if (self.zp(pins)) |_| self.st(pins, self.x), // STX zp - 0x88 => if (self.imm(pins)) |_| self.dexy(pins, &self.y), // DEY - 0x8a => if (self.imm(pins)) |_| self.ld(pins, &self.a, self.x), // TXA + 0x87 => if (self.zp(pins)) |c| self.sax(pins, c), // *SAX zp + 0x88 => if (self.imp(pins)) |_| self.dexy(pins, &self.y), // DEY + 0x89 => if (self.imm(pins)) |_| self.fetch(pins), // *NOP imm + 0x8a => if (self.imp(pins)) |_| self.ld(pins, &self.a, self.x), // TXA + 0x8b => if (self.imm(pins)) |_| self.ane(pins), // *ANE # 0x8c => if (self.abs(pins)) |_| self.st(pins, self.y), // STY abs 0x8d => if (self.abs(pins)) |_| self.st(pins, self.a), // STA abs 0x8e => if (self.abs(pins)) |_| self.st(pins, self.x), // STX abs + 0x8f => if (self.abs(pins)) |c| self.sax(pins, c), // *SAX abs - 0x90 => if (self.imm(pins)) |_| self.branch(pins, !self.status.carry), // BCC + 0x90 => if (self.imm(pins)) |_| self.branch(pins, !self.status.carry), // BCC # 0x91 => if (self.zpIndY(pins)) |_| self.ld(pins, &self.a, v), // STA (zp),Y + 0x92 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0x93 => if (self.zpIndY(pins)) |c| self.a11(pins, self.a & self.x, c), // *SHA (zp,Y) 0x94 => if (self.zpOff(pins, self.x)) |_| self.st(pins, self.y), // STY zp,X 0x95 => if (self.zpOff(pins, self.x)) |_| self.st(pins, self.a), // STA zp,X 0x96 => if (self.zpOff(pins, self.y)) |_| self.st(pins, self.x), // STX zp,Y + 0x97 => if (self.zpOff(pins, self.y)) |c| self.sax(pins, c), // *SAX zp,Y 0x99 => if (self.absOff(pins, self.y)) |_| self.st(pins, self.a), // STA abs,Y - 0x9a => if (self.imm(pins)) |_| self.ld(pins, &self.sp, self.x), // TXS - 0x9c => if (self.absOff(pins, self.x)) |_| self.st(pins, self.y), // STY abs,X + 0x9a => if (self.imp(pins)) |_| self.ld(pins, &self.sp, self.x), // TXS + 0x9b => if (self.absOff(pins, self.y)) |c| self.tas(pins, c), // *TAS abs,Y + 0x9c => if (self.absOff(pins, self.x)) |c| self.a11(pins, self.y, c), // *SHY abs,X 0x9d => if (self.absOff(pins, self.x)) |_| self.st(pins, self.a), // STA abs,X - 0x9e => if (self.absOff(pins, self.y)) |_| self.st(pins, self.x), // STX abs,Y + 0x9e => if (self.absOff(pins, self.y)) |c| self.a11(pins, self.x, c), // *SHX abs,Y + 0x9f => if (self.absOff(pins, self.y)) |c| self.a11(pins, self.a & self.x, c), // *SHA abs,Y 0xa0 => if (self.imm(pins)) |_| self.ld(pins, &self.y, v), // LDY # 0xa1 => if (self.zpXInd(pins)) |_| self.ld(pins, &self.y, v), // LDA (zp,X) 0xa2 => if (self.imm(pins)) |_| self.ld(pins, &self.x, v), // LDX # + 0xa3 => if (self.zpXInd(pins)) |c| self.dcp(pins, c), // *DCP (zp,X) 0xa4 => if (self.zp(pins)) |_| self.ld(pins, &self.y, v), // LDY zp 0xa5 => if (self.zp(pins)) |_| self.ld(pins, &self.a, v), // LDA zp 0xa6 => if (self.zp(pins)) |_| self.ld(pins, &self.x, v), // LDX zp - 0xa8 => if (self.imm(pins)) |_| self.ld(pins, &self.y, self.a), // TAY + 0xa7 => if (self.zp(pins)) |c| self.dcp(pins, c), // *DCP zp + 0xa8 => if (self.imp(pins)) |_| self.ld(pins, &self.y, self.a), // TAY 0xa9 => if (self.imm(pins)) |_| self.ld(pins, &self.a, v), // LDA # - 0xaa => if (self.imm(pins)) |_| self.ld(pins, &self.x, self.a), // TAX + 0xaa => if (self.imp(pins)) |_| self.ld(pins, &self.x, self.a), // TAX + 0xab => if (self.imm(pins)) |_| self.lxa(pins), // *LXA # 0xac => if (self.abs(pins)) |_| self.ld(pins, &self.y, v), // LDY abs 0xad => if (self.abs(pins)) |_| self.ld(pins, &self.a, v), // LDA abs 0xae => if (self.abs(pins)) |_| self.ld(pins, &self.x, v), // LDX abs + 0xaf => if (self.abs(pins)) |c| self.dcp(pins, c), // *DCP abs - 0xb0 => if (self.imm(pins)) |_| self.branch(pins, self.status.carry), // BCS + 0xb0 => if (self.imm(pins)) |_| self.branch(pins, self.status.carry), // BCS # 0xb1 => if (self.zpIndY(pins)) |_| self.ld(pins, &self.a, v), // LDA (zp),Y + 0xb2 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0xb3 => if (self.zpIndY(pins)) |c| self.dcp(pins, c), // *DCP (zp),Y 0xb4 => if (self.zpOff(pins, self.x)) |_| self.ld(pins, &self.y, v), // LDY zp,X 0xb5 => if (self.zpOff(pins, self.x)) |_| self.ld(pins, &self.a, v), // LDA zp,X 0xb6 => if (self.zpOff(pins, self.y)) |_| self.ld(pins, &self.x, v), // LDX zp,Y - 0xb8 => self.set(pins, .overflow, true), // SEV + 0xb7 => if (self.zpOff(pins, self.y)) |c| self.dcp(pins, c), // *DCP zp,Y + 0xb8 => if (self.imp(pins)) |_| self.set(pins, .overflow, true), // SEV 0xb9 => if (self.absOff(pins, self.y)) |_| self.ld(pins, &self.a, v), // LDA abs,Y - 0xba => if (self.imm(pins)) |_| self.ld(pins, &self.x, self.sp), // TSX + 0xba => if (self.imp(pins)) |_| self.ld(pins, &self.x, self.sp), // TSX + 0xbb => if (self.absOff(pins, self.y)) |_| self.las(pins), // *LAS abs,Y 0xbc => if (self.absOff(pins, self.x)) |_| self.ld(pins, &self.y, v), // LDY abs,X 0xbd => if (self.absOff(pins, self.x)) |_| self.ld(pins, &self.a, v), // LDA abs,X 0xbe => if (self.absOff(pins, self.y)) |_| self.ld(pins, &self.x, v), // LDX abs,Y + 0xbf => if (self.absOff(pins, self.y)) |c| self.dcp(pins, c), // *DCP abs,Y 0xc0 => if (self.imm(pins)) |_| self.cmp(pins, self.y), // CPY # 0xc1 => if (self.zpXInd(pins)) |_| self.cmp(pins, self.a), // CMP (zp,X) + 0xc2 => if (self.imm(pins)) |_| self.fetch(pins), // *NOP imm + 0xc3 => if (self.zpXInd(pins)) |c| self.dcp(pins, c), // *DCP (zp,X) 0xc4 => if (self.zp(pins)) |_| self.cmp(pins, self.y), // CPY zp 0xc5 => if (self.zp(pins)) |_| self.cmp(pins, self.a), // CMP zp 0xc6 => if (self.zp(pins)) |c| self.dec(pins, c), // DEC zp - 0xc8 => if (self.imm(pins)) |_| self.inxy(pins, &self.y), // INY + 0xc7 => if (self.zp(pins)) |c| self.dcp(pins, c), // *DCP zp + 0xc8 => if (self.imp(pins)) |_| self.inxy(pins, &self.y), // INY 0xc9 => if (self.imm(pins)) |_| self.cmp(pins, self.a), // CMP # - 0xca => if (self.imm(pins)) |_| self.dexy(pins, &self.x), // DEY + 0xca => if (self.imp(pins)) |_| self.dexy(pins, &self.x), // DEX 0xcc => if (self.abs(pins)) |_| self.cmp(pins, self.y), // CPY abs + 0xcb => if (self.imm(pins)) |_| self.las(pins), // *SBX # 0xcd => if (self.abs(pins)) |_| self.cmp(pins, self.a), // CMP abs 0xce => if (self.abs(pins)) |c| self.dec(pins, c), // DEC abs + 0xcf => if (self.abs(pins)) |c| self.dcp(pins, c), // *DCP abs - 0xd0 => if (self.imm(pins)) |_| self.branch(pins, !self.status.zero), // BNE + 0xd0 => if (self.imm(pins)) |_| self.branch(pins, !self.status.zero), // BNE # 0xd1 => if (self.zpIndY(pins)) |_| self.cmp(pins, self.a), // CMP (zp),Y + 0xd2 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0xd3 => if (self.zpIndY(pins)) |c| self.dcp(pins, c), // *DCP (zp),Y + 0xd4 => if (self.zpOff(pins, self.x)) |_| self.fetch(pins), // *NOP zp,X 0xd5 => if (self.zpOff(pins, self.x)) |_| self.cmp(pins, self.a), // CMP zp,X 0xd6 => if (self.zpOff(pins, self.x)) |c| self.dec(pins, c), // DEC zp,X - 0xd8 => self.set(pins, .decimal, false), // CLD + 0xd7 => if (self.zpOff(pins, self.x)) |c| self.dcp(pins, c), // *DCP zp,X + 0xd8 => if (self.imp(pins)) |_| self.set(pins, .decimal, false), // CLD 0xd9 => if (self.absOff(pins, self.y)) |_| self.cmp(pins, self.a), // CMP abs,Y + 0xda => if (self.imp(pins)) |_| self.fetch(pins), // *NOP + 0xdb => if (self.absOff(pins, self.y)) |c| self.dcp(pins, c), // *DCP abs,Y + 0xdc => if (self.absOff(pins, self.x)) |_| self.fetch(pins), // *NOP abs,X 0xdd => if (self.absOff(pins, self.x)) |_| self.cmp(pins, self.a), // CMP abs,X 0xde => if (self.absOff(pins, self.x)) |c| self.dec(pins, c), // DEC abs,X + 0xdf => if (self.absOff(pins, self.x)) |c| self.dcp(pins, c), // *DCP abs,X 0xe0 => if (self.imm(pins)) |_| self.cmp(pins, self.x), // CPX # 0xe1 => if (self.zpXInd(pins)) |_| self.sbc(pins, v), // SBC (zp,X) + 0xe2 => if (self.imm(pins)) |_| self.fetch(pins), // *NOP imm + 0xe3 => if (self.zpXInd(pins)) |c| self.isc(pins, c), // *ISC (zp,X) 0xe4 => if (self.zp(pins)) |_| self.cmp(pins, self.x), // CPX zp 0xe5 => if (self.zp(pins)) |_| self.sbc(pins, v), // SBC zp 0xe6 => if (self.zp(pins)) |c| self.inc(pins, c), // INC zp - 0xe8 => if (self.imm(pins)) |_| self.inxy(pins, &self.x), // INX + 0xe7 => if (self.zp(pins)) |c| self.isc(pins, c), // *ISC zp + 0xe8 => if (self.imp(pins)) |_| self.inxy(pins, &self.x), // INX 0xe9 => if (self.imm(pins)) |_| self.sbc(pins, v), // SBC # - 0xea => if (self.imm(pins)) |_| self.fetch(pins), // NOP + 0xea => if (self.imp(pins)) |_| self.fetch(pins), // NOP + 0xeb => if (self.imm(pins)) |_| self.sbc(pins, v), // *SBC # 0xec => if (self.abs(pins)) |_| self.cmp(pins, self.x), // CPX abs 0xed => if (self.abs(pins)) |_| self.sbc(pins, v), // SBC abs 0xee => if (self.abs(pins)) |c| self.inc(pins, c), // INC abs + 0xef => if (self.abs(pins)) |c| self.isc(pins, c), // *ISC abs - 0xf0 => if (self.imm(pins)) |_| self.branch(pins, self.status.zero), // BEQ + 0xf0 => if (self.imm(pins)) |_| self.branch(pins, self.status.zero), // BEQ # 0xf1 => if (self.zpIndY(pins)) |_| self.sbc(pins, v), // SBC (zp),Y + 0xf2 => if (self.imp(pins)) |_| self.hlt(pins), // *HLT + 0xf3 => if (self.zpIndY(pins)) |c| self.isc(pins, c), // *ISC (zp),Y + 0xf4 => if (self.zpOff(pins, self.x)) |_| self.fetch(pins), // *NOP zp,X 0xf5 => if (self.zpOff(pins, self.x)) |_| self.sbc(pins, v), // SBC zp,X 0xf6 => if (self.zpOff(pins, self.x)) |c| self.inc(pins, c), // INC zp,X - 0xf8 => self.set(pins, .decimal, true), // SED + 0xf7 => if (self.zpOff(pins, self.x)) |c| self.isc(pins, c), // *ISC zp,X + 0xf8 => if (self.imp(pins)) |_| self.set(pins, .decimal, true), // SED 0xf9 => if (self.absOff(pins, self.y)) |_| self.sbc(pins, v), // SBC abs,Y + 0xfa => if (self.imp(pins)) |_| self.fetch(pins), // *NOP + 0xfb => if (self.absOff(pins, self.y)) |c| self.isc(pins, c), // *ISC abs,Y + 0xfc => if (self.absOff(pins, self.x)) |_| self.fetch(pins), // *NOP abs,X 0xfd => if (self.absOff(pins, self.x)) |_| self.sbc(pins, v), // SBC abs,X 0xfe => if (self.absOff(pins, self.x)) |c| self.inc(pins, c), // INC abs,X + 0xff => if (self.absOff(pins, self.x)) |c| self.isc(pins, c), // *ISC abs,X - else => { - log.err("UNHANDLED OP={X}", .{self.opcode}); - self.fetch(pins); - }, + else => unreachable, } } +inline fn imp(self: *Cpu, pins: *zesty.Pins) ?u3 { + switch (self.cycle) { + 0 => { + pins.cpu_addr = self.pc; + }, + else => return self.cycle - 1, + } + return null; +} inline fn imm(self: *Cpu, pins: *zesty.Pins) ?u3 { switch (self.cycle) { 0 => { @@ -433,7 +543,7 @@ inline fn fetchAt(self: *Cpu, pins: *zesty.Pins, pc: u16) void { inline fn ld(self: *Cpu, pins: *zesty.Pins, to: *u8, from: u8) void { to.* = from; - self.setNZ(to.*); + self.status.setNZ(to.*); self.fetch(pins); } inline fn st(self: *Cpu, pins: *zesty.Pins, from: u8) void { @@ -486,7 +596,7 @@ inline fn php(self: *Cpu, pins: *zesty.Pins) void { } inline fn pla(self: *Cpu, pins: *zesty.Pins) void { self.a = self.pull(pins) orelse return; - self.setNZ(self.a); + self.status.setNZ(self.a); } inline fn plp(self: *Cpu, pins: *zesty.Pins) void { const status: Status = .from(self.pull(pins) orelse return); @@ -504,126 +614,139 @@ inline fn plp(self: *Cpu, pins: *zesty.Pins) void { //------------------------------------------------------ // Opcodes: Arithmetic -const Dst = enum { acc, mem }; +inline fn _adc(a: u8, b: u8, status: *Status) u8 { + var result, var carry = @addWithOverflow(a, @intFromBool(status.carry)); + // TODO: implement optional support for decimal mode + result, carry = @addWithOverflow(result, b); + + status.carry = carry > 0; + status.setNZ(result); + // Overflow bit is set if both inputs have the same sign, + // and the output has a different sign + status.overflow = ~(a ^ b) & (a ^ result) & 0x80 > 0; + return result; +} +inline fn _sbc(a: u8, b: u8, status: *Status) u8 { + return _adc(a, ~b, status); +} +inline fn _cmp(a: u8, b: u8, status: *Status) u8 { + // a CMP is basically a SBC in disguise + const result, const carry = @addWithOverflow(a, ~b +% 1); + status.carry = carry > 0; + status.setNZ(result); + return result; +} + +inline fn _asl(a: u8, comptime rotate: bool, status: *Status) u8 { + var v, const carry = @shlWithOverflow(a, 1); + if (rotate) v |= @intFromBool(status.carry); + status.carry = carry > 0; + status.setNZ(v); + return v; +} +inline fn _lsr(a: u8, comptime rotate: bool, status: *Status) u8 { + // There's no @shrWithOverflow :( + var v = a >> 1; + if (rotate and status.carry) v |= 0x80; + status.carry = a & 1 > 0; + status.setNZ(v); + return v; +} + inline fn ora(self: *Cpu, pins: *zesty.Pins, v: u8) void { self.a |= v; - self.setNZ(self.a); + self.status.setNZ(self.a); self.fetch(pins); } inline fn _and(self: *Cpu, pins: *zesty.Pins, v: u8) void { self.a &= v; - self.setNZ(self.a); + self.status.setNZ(self.a); self.fetch(pins); } inline fn eor(self: *Cpu, pins: *zesty.Pins, v: u8) void { self.a ^= v; - self.setNZ(self.a); + self.status.setNZ(self.a); self.fetch(pins); } -inline fn asl(self: *Cpu, pins: *zesty.Pins, comptime dst: Dst) void { - const v, self.status.carry = switch (dst) { - .acc => v: { - self.a, const carry = @shlWithOverflow(self.a, 1); - break :v .{ self.a, carry > 0 }; - }, - .mem => v: { - pins.cpu_data, const carry = @shlWithOverflow(pins.cpu_data, 1); +inline fn asl(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + pins.cpu_data = _asl(pins.cpu_data, false, &self.status); pins.cpu_rw = .write; - break :v .{ pins.cpu_data, carry > 0 }; }, - }; - self.setNZ(v); - self.fetch(pins); + else => self.fetch(pins), + } } -inline fn lsr(self: *Cpu, pins: *zesty.Pins, comptime dst: Dst) void { - // There's no @shrWithOverflow :( - const v, self.status.carry = switch (dst) { - .acc => v: { - const carry = self.a & 0b1; - self.a >>= 1; - break :v .{ self.a, carry > 0 }; - }, - .mem => v: { - const carry = pins.cpu_data & 0b1; - pins.cpu_data >>= 1; +inline fn rol(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + pins.cpu_data = _asl(pins.cpu_data, true, &self.status); pins.cpu_rw = .write; - break :v .{ pins.cpu_data, carry > 0 }; }, - }; - - self.setNZ(v); - self.fetch(pins); + else => self.fetch(pins), + } } -inline fn rol(self: *Cpu, pins: *zesty.Pins, comptime dst: Dst) void { - const v, self.status.carry = switch (dst) { - .acc => v: { - self.a, const carry = @shlWithOverflow(self.a, 1); - self.a |= @intFromBool(self.status.carry); - break :v .{ self.a, carry > 0 }; - }, - .mem => v: { - pins.cpu_data, const carry = @shlWithOverflow(pins.cpu_data, 1); - pins.cpu_data |= @intFromBool(self.status.carry); +inline fn lsr(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + pins.cpu_data = _lsr(pins.cpu_data, false, &self.status); + self.status.setNZ(pins.cpu_data); pins.cpu_rw = .write; - break :v .{ pins.cpu_data, carry > 0 }; }, - }; - - self.setNZ(v); - self.fetch(pins); + else => self.fetch(pins), + } } -inline fn ror(self: *Cpu, pins: *zesty.Pins, comptime dst: Dst) void { - const v, self.status.carry = switch (dst) { - .acc => v: { - const carry = self.a & 1; - self.a >>= 1; - if (self.status.carry) self.a |= 0b1000_0000; - break :v .{ self.a, carry > 0 }; - }, - .mem => v: { - const carry = pins.cpu_data & 1; - pins.cpu_data >>= 1; - if (self.status.carry) pins.cpu_data |= 0b1000_0000; +inline fn ror(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + pins.cpu_data = _lsr(pins.cpu_data, true, &self.status); + self.status.setNZ(pins.cpu_data); pins.cpu_rw = .write; - break :v .{ pins.cpu_data, carry > 0 }; }, - }; - - self.setNZ(v); + else => self.fetch(pins), + } +} +inline fn asla(self: *Cpu, pins: *zesty.Pins) void { + self.a = _asl(self.a, false, &self.status); + self.status.setNZ(self.a); + self.fetch(pins); +} +inline fn rola(self: *Cpu, pins: *zesty.Pins) void { + self.a = _asl(self.a, true, &self.status); + self.status.setNZ(self.a); + self.fetch(pins); +} +inline fn lsra(self: *Cpu, pins: *zesty.Pins) void { + self.a = _lsr(self.a, false, &self.status); + self.status.setNZ(self.a); + self.fetch(pins); +} +inline fn rora(self: *Cpu, pins: *zesty.Pins) void { + self.a = _lsr(self.a, true, &self.status); + self.status.setNZ(self.a); self.fetch(pins); } inline fn adc(self: *Cpu, pins: *zesty.Pins, v: u8) void { - var result, var carry = @addWithOverflow(self.a, @intFromBool(self.status.carry)); - // TODO: implement optional support for decimal mode - result, carry = @addWithOverflow(result, v); - - self.status.carry = carry > 0; - self.setNZ(result); - // Overflow bit is set if both inputs have the same sign, - // and the output has a different sign - self.status.overflow = ~(self.a ^ v) & (self.a ^ result) & 0x80 > 0; - self.a = result; + self.a = _adc(self.a, v, &self.status); self.fetch(pins); } inline fn sbc(self: *Cpu, pins: *zesty.Pins, v: u8) void { - self.adc(pins, ~v); + self.a = _sbc(self.a, v, &self.status); + self.fetch(pins); } inline fn cmp(self: *Cpu, pins: *zesty.Pins, v: u8) void { - // a CMP is basically a SBC in disguise - const result, const carry = @addWithOverflow(v, ~pins.cpu_data +% 1); - self.status.carry = carry > 0; - self.setNZ(result); + _ = _cmp(v, pins.cpu_data, &self.status); self.fetch(pins); } inline fn inxy(self: *Cpu, pins: *zesty.Pins, v: *u8) void { v.* +%= 1; - self.setNZ(v.*); + self.status.setNZ(v.*); self.fetch(pins); } inline fn dexy(self: *Cpu, pins: *zesty.Pins, v: *u8) void { v.* -%= 1; - self.setNZ(v.*); + self.status.setNZ(v.*); self.fetch(pins); } inline fn inc(self: *Cpu, pins: *zesty.Pins, c: u3) void { @@ -635,7 +758,7 @@ inline fn inc(self: *Cpu, pins: *zesty.Pins, c: u3) void { }, 1 => { self.hilo.lo +%= 1; - self.setNZ(self.hilo.lo); + self.status.setNZ(self.hilo.lo); pins.cpu_data = self.hilo.lo; pins.cpu_rw = .write; }, @@ -651,7 +774,7 @@ inline fn dec(self: *Cpu, pins: *zesty.Pins, c: u3) void { }, 1 => { self.hilo.lo -%= 1; - self.setNZ(self.hilo.lo); + self.status.setNZ(self.hilo.lo); pins.cpu_data = self.hilo.lo; pins.cpu_rw = .write; }, @@ -938,13 +1061,239 @@ inline fn rts(self: *Cpu, pins: *zesty.Pins) void { } //------------------------------------------------------ -// Helpers +// Opcodes: Undocumented + +/// This seems like the most common "magic" value for ANE and LXA. +/// I genuinely hope nobody depends on this. +/// You shouldn't, anyway, since on real hardware +/// this apparently changes depending on temperature?? +const undocumented_magic: u8 = 0xee; + +/// HLT (halt) +inline fn hlt(self: *Cpu, pins: *zesty.Pins) void { + // Stuck + pins.cpu_addr = 0xffff; + pins.cpu_data = 0xff; + self.cycle -= 1; +} +/// ALR (AND + LSR) +inline fn alr(self: *Cpu, pins: *zesty.Pins) void { + self.a &= pins.cpu_data; + self.a = _lsr(self.a, false, &self.status); + self.fetch(pins); +} +/// ANC (AND, set C) +inline fn anc(self: *Cpu, pins: *zesty.Pins) void { + self.a &= pins.cpu_data; + self.status.carry = self.a >= 0x80; + self.status.setNZ(self.a); + self.fetch(pins); +} +/// ANE/XAA, aka The One Unstable Opcode +/// See https://www.nesdev.org/wiki/Visual6502wiki/6502_Opcode_8B_(XAA,_ANE) +inline fn ane(self: *Cpu, pins: *zesty.Pins) void { + self.a = (self.a | undocumented_magic) & self.x & pins.cpu_data; + self.status.setNZ(self.a); + self.fetch(pins); +} +/// ARR (AND + ROR) +inline fn arr(self: *Cpu, pins: *zesty.Pins) void { + // The behavior of these flags is cursed AF. + // Kudos to https://github.com/floooh/chips for expressing + // this in emulator-friendly form + self.a >>= 1; + if (self.status.carry) self.a |= 0x80; + self.status.setNZ(self.a); + + if (self.a & 0x40 > 0) { + self.status.overflow = true; + self.status.carry = true; + } + if (self.a & 0x20 > 0) { + self.status.overflow = !self.status.overflow; + } + self.fetch(pins); +} +/// DCP (DEC + CMP) +inline fn dcp(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + // Dummy write + self.hilo = .{ .lo = pins.cpu_data }; + pins.cpu_rw = .write; + }, + 1 => { + self.hilo.lo -%= 1; + pins.cpu_data = self.hilo.lo; + pins.cpu_rw = .write; + _ = _cmp(self.a, pins.cpu_data, &self.status); + }, + else => self.fetch(pins), + } +} +/// ISC (INC + SBC) +inline fn isc(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + // Dummy write + self.hilo = .{ .lo = pins.cpu_data }; + pins.cpu_rw = .write; + }, + 1 => { + self.hilo.lo +%= 1; + pins.cpu_data = self.hilo.lo; + pins.cpu_rw = .write; + self.a = _sbc(self.a, self.hilo.lo, &self.status); + }, + else => self.fetch(pins), + } +} +/// LAS (LDA + TSX) +inline fn las(self: *Cpu, pins: *zesty.Pins) void { + self.sp &= pins.cpu_data; + self.a = self.sp; + self.x = self.sp; + self.status.setNZ(self.a); + self.fetch(pins); +} +/// LAX (LDA + LDX) +inline fn lax(self: *Cpu, pins: *zesty.Pins) void { + self.a = pins.cpu_data; + self.x = pins.cpu_data; + self.status.setNZ(self.a); + self.fetch(pins); +} +/// LXA (LAX immediate) +/// Highly magical. See ANE +inline fn lxa(self: *Cpu, pins: *zesty.Pins) void { + self.a = (self.a | undocumented_magic) & pins.cpu_data; + self.x = self.a; + self.status.setNZ(self.a); + self.fetch(pins); +} +/// RLA (ROL + AND) +inline fn rla(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + // Dummy write + self.hilo = .{ .lo = pins.cpu_data }; + pins.cpu_rw = .write; + }, + 1 => { + pins.cpu_data = self.hilo.lo; + + pins.cpu_data, const carry = @shlWithOverflow(pins.cpu_data, 1); + pins.cpu_data |= @intFromBool(self.status.carry); + pins.cpu_rw = .write; + + self.a &= self.hilo.lo; + self.status.setNZ(self.a); + self.status.carry = carry > 0; + }, + else => self.fetch(pins), + } +} +/// RRA (ROR + AND) +inline fn rra(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + // Dummy write + self.hilo = .{ .lo = pins.cpu_data }; + pins.cpu_rw = .write; + }, + 1 => { + pins.cpu_data = self.hilo.lo; + + self.status.carry = pins.cpu_data & 1 > 0; + pins.cpu_data >>= 1; + if (self.status.carry) pins.cpu_data |= 0b1000_0000; + pins.cpu_rw = .write; + + self.a &= pins.cpu_data; + self.status.setNZ(self.a); + }, + else => self.fetch(pins), + } +} +/// SAX +inline fn sax(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + pins.cpu_data = self.a & self.x; + pins.cpu_rw = .write; + }, + else => self.fetch(pins), + } +} +/// SBX (CMP, DEX) +inline fn sbx(self: *Cpu, pins: *zesty.Pins) void { + self.x = _cmp(self.x & self.a, pins.cpu_data, &self.status); + self.fetch(pins); +} +/// SHA, SHX, SHY (A11) +/// Even though the high byte may be dropped on real 6502 CPUs, +/// here I choose to add them unconditionally. +inline fn a11(self: *Cpu, pins: *zesty.Pins, v: u8, c: u3) void { + switch (c) { + 0 => { + const pc: Addr = .from(self.pc); + pins.cpu_data = v & (pc.hi +% 1); + pins.cpu_rw = .write; + }, + else => self.fetch(pins), + } +} +/// SLO (ASL + ORA) +inline fn slo(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + self.hilo.lo = pins.cpu_data; + pins.cpu_rw = .write; + }, + 1 => { + pins.cpu_data = _asl(self.hilo.lo, false, &self.status); + self.a |= pins.cpu_data; + self.status.setNZ(self.a); + pins.cpu_rw = .write; + }, + else => self.fetch(pins), + } +} +/// SRE (LSR + EOR) +inline fn sre(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + self.hilo.lo = pins.cpu_data; + pins.cpu_rw = .write; + }, + 1 => { + pins.cpu_data = _lsr(self.hilo.lo, false, &self.status); + self.a ^= pins.cpu_data; + self.status.setNZ(self.a); + pins.cpu_rw = .write; + }, + else => self.fetch(pins), + } +} +/// TAS +/// Unstable. See A11 +inline fn tas(self: *Cpu, pins: *zesty.Pins, c: u3) void { + switch (c) { + 0 => { + const pc: Addr = .from(self.pc); + const v = self.a & self.x; -inline fn setNZ(self: *Cpu, v: u8) void { - self.status.zero = v == 0; - self.status.negative = v >= 0x80; + self.sp = v; + pins.cpu_data = v & (pc.hi +% 1); + pins.cpu_rw = .write; + }, + else => self.fetch(pins), + } } +//------------------------------------------------------ +// Helpers + pub const Status = packed struct(u8) { carry: bool = false, zero: bool = false, @@ -966,6 +1315,11 @@ pub const Status = packed struct(u8) { return @bitCast(v); } + inline fn setNZ(self: *Status, v: u8) void { + self.zero = v == 0; + self.negative = v >= 0x80; + } + pub fn format( self: Status, writer: *std.Io.Writer, diff --git a/src/tests/cpu.zig b/src/tests/cpu.zig index 72d4618..94b7d6c 100644 --- a/src/tests/cpu.zig +++ b/src/tests/cpu.zig @@ -10,6 +10,7 @@ test { _ = @import("cpu/alu.zig"); _ = @import("cpu/control_flow.zig"); _ = @import("cpu/stack.zig"); + _ = @import("cpu/undocumented.zig"); } //------------------------------------------------------ diff --git a/src/tests/cpu/alu.zig b/src/tests/cpu/alu.zig index aa20cf6..5a92e3b 100644 --- a/src/tests/cpu/alu.zig +++ b/src/tests/cpu/alu.zig @@ -64,7 +64,7 @@ test "ASL" { } }); cpu.run(&z); // Setup A register cpu.run(&z); - try std.testing.expectEqual(0x8004, z.cpu.pc); + try std.testing.expectEqual(0x8003, z.cpu.pc); try std.testing.expectEqual(0b00001010, z.cpu.a); try std.testing.expect(!z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); @@ -81,7 +81,7 @@ test "LSR" { } }); cpu.run(&z); // Setup A register cpu.run(&z); - try std.testing.expectEqual(0x8004, z.cpu.pc); + try std.testing.expectEqual(0x8003, z.cpu.pc); try std.testing.expectEqual(0b01000010, z.cpu.a); try std.testing.expect(!z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); @@ -103,7 +103,7 @@ test "ROL" { cpu.run(&z); // Set carry cpu.run(&z); - try std.testing.expectEqual(0x8005, z.cpu.pc); + try std.testing.expectEqual(0x8004, z.cpu.pc); try std.testing.expectEqual(0b00001011, z.cpu.a); try std.testing.expect(!z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); @@ -125,7 +125,7 @@ test "ROR" { cpu.run(&z); // Set carry cpu.run(&z); - try std.testing.expectEqual(0x8005, z.cpu.pc); + try std.testing.expectEqual(0x8004, z.cpu.pc); try std.testing.expectEqual(0b11000010, z.cpu.a); try std.testing.expect(z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); @@ -211,7 +211,7 @@ test "DEX" { }, }); cpu.run(&z); - try std.testing.expectEqual(0x8002, z.cpu.pc); + try std.testing.expectEqual(0x8001, z.cpu.pc); try std.testing.expectEqual(0b11111111, z.cpu.x); try std.testing.expect(z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); @@ -228,7 +228,7 @@ test "DEY" { }, }); cpu.run(&z); - try std.testing.expectEqual(0x8002, z.cpu.pc); + try std.testing.expectEqual(0x8001, z.cpu.pc); try std.testing.expectEqual(0b11111111, z.cpu.y); try std.testing.expect(z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); @@ -265,7 +265,7 @@ test "INX" { }, }); cpu.run(&z); - try std.testing.expectEqual(0x8002, z.cpu.pc); + try std.testing.expectEqual(0x8001, z.cpu.pc); try std.testing.expectEqual(0b00000001, z.cpu.x); try std.testing.expect(!z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); @@ -282,7 +282,7 @@ test "INY" { }, }); cpu.run(&z); - try std.testing.expectEqual(0x8002, z.cpu.pc); + try std.testing.expectEqual(0x8001, z.cpu.pc); try std.testing.expectEqual(0b00000001, z.cpu.y); try std.testing.expect(!z.cpu.status.negative); try std.testing.expect(!z.cpu.status.zero); diff --git a/src/tests/cpu/undocumented.zig b/src/tests/cpu/undocumented.zig new file mode 100644 index 0000000..4ecb230 --- /dev/null +++ b/src/tests/cpu/undocumented.zig @@ -0,0 +1,47 @@ +//! Tests about undocumented opcodes. +const std = @import("std"); +const cpu = @import("../cpu.zig"); + +test "HLT" { + // These opcodes all kill the CPU. + const ops = [_]u8{ + 0x02, 0x12, 0x22, 0x32, 0x42, 0x52, // + 0x62, 0x72, 0x92, 0xb2, 0xd2, 0xf2, + }; + + for (ops) |op| { + var z = cpu.testZes(.{ + .init_cpu = false, + .rom = &.{ + .{ 0x8000, &.{op} }, + }, + }); + while (!z.cpu.sync) z.stepCpu(); // Boot cycle + + z.stepCpu(); // First cycle is fine + + for (0..3) |_| { + z.stepCpu(); // Stuck! + try std.testing.expectEqual(0xffff, z.pins.cpu_addr); + } + } +} + +test "NOPs" { + var z = cpu.testZes(.{ + .init_cpu = false, + .rom = &.{ + .{ + 0x8000, &.{ + 0x1a, 0x3a, 0x5a, 0x7a, 0xda, 0xfa, // implied + 0x80, 0x82, 0x89, 0xc2, 0xe2, // immediate + 0x04, 0x44, 0x64, // zp + 0x14, 0x34, 0x54, 0x74, 0xd4, 0xf4, // zp,X + 0x0c, // abs + 0x1c, 0x3c, 0x5c, 0x7c, 0xdc, 0xfc, // abs,X + }, + }, + }, + }); + while (!z.cpu.status.brk) z.stepCpu(); +} -- 2.43.0