cpu: all undocumented opcodes #4

merged
opened by pluie.me targeting main from pluie/jj-tpnmozqyuktv
Changed files
+549 -147
src
+493 -139
src/Cpu.zig
···
// 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 => {
···
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 {
···
}
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);
···
//------------------------------------------------------
// 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 {
···
},
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;
},
···
},
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;
},
···
}
//------------------------------------------------------
-
// 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,
···
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,
+1
src/tests/cpu.zig
···
_ = @import("cpu/alu.zig");
_ = @import("cpu/control_flow.zig");
_ = @import("cpu/stack.zig");
+
_ = @import("cpu/undocumented.zig");
}
//------------------------------------------------------
+8 -8
src/tests/cpu/alu.zig
···
} });
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);
···
} });
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);
···
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);
···
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);
···
},
});
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);
···
},
});
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);
···
},
});
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);
···
},
});
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);
+47
src/tests/cpu/undocumented.zig
···
+
//! 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();
+
}