cpu: finish ALU impl #1

merged
opened by pluie.me targeting main from pluie/jj-tpnmozqyuktv
Changed files
+768 -149
src
+256 -144
src/Cpu.zig
···
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)
-
0x05 => if (self.zp(pins)) self.ora(pins, v), // ORA zp
-
0x06 => if (self.zp(pins)) self.asl(pins, .mem), // ASL zp
-
0x09 => if (self.imm(pins)) self.ora(pins, v), // ORA #
-
0x0a => if (self.imm(pins)) self.asl(pins, .acc), // ASL A
-
0x0d => if (self.abs(pins)) self.ora(pins, v), // ORA abs
-
0x0e => if (self.abs(pins)) self.asl(pins, .mem), // ASL abs
-
-
0x10 => if (self.imm(pins)) self.branch(pins, !self.status.negative), // BPL
-
0x11 => if (self.zpIndY(pins)) self.ora(pins, v), // ORA (zp),Y
-
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
+
0x01 => if (self.zpXInd(pins)) |_| self.ora(pins, v), // ORA (zp,X)
+
0x05 => if (self.zp(pins)) |_| self.ora(pins, v), // ORA zp
+
0x06 => if (self.zp(pins)) |_| self.asl(pins, .mem), // ASL zp
+
0x09 => if (self.imm(pins)) |_| self.ora(pins, v), // ORA #
+
0x0a => if (self.imm(pins)) |_| self.asl(pins, .acc), // ASL A
+
0x0d => if (self.abs(pins)) |_| self.ora(pins, v), // ORA abs
+
0x0e => if (self.abs(pins)) |_| self.asl(pins, .mem), // ASL abs
+
+
0x10 => if (self.imm(pins)) |_| self.branch(pins, !self.status.negative), // BPL
+
0x11 => if (self.zpIndY(pins)) |_| self.ora(pins, v), // ORA (zp),Y
+
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
-
0x19 => if (self.absOff(pins, self.y)) self.ora(pins, v), // ORA abs,Y
-
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
-
-
0x21 => if (self.zpXInd(pins)) self._and(pins, v), // AND (zp,X)
-
0x25 => if (self.zp(pins)) self._and(pins, v), // AND zp
-
0x26 => if (self.zp(pins)) self.rol(pins, .mem), // ROL zp
-
0x29 => if (self.imm(pins)) self._and(pins, v), // AND #
-
0x2a => if (self.imm(pins)) self.rol(pins, .acc), // ROL A
-
0x2d => if (self.abs(pins)) self._and(pins, v), // AND abs
-
0x2e => if (self.abs(pins)) self.rol(pins, .mem), // ROL abs
-
-
0x30 => if (self.imm(pins)) self.branch(pins, self.status.negative), // BMI
-
0x31 => if (self.zpIndY(pins)) self._and(pins, v), // AND (zp),Y
-
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
+
0x19 => if (self.absOff(pins, self.y)) |_| self.ora(pins, v), // ORA abs,Y
+
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
+
+
0x21 => if (self.zpXInd(pins)) |_| self._and(pins, v), // AND (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
+
0x29 => if (self.imm(pins)) |_| self._and(pins, v), // AND #
+
0x2a => if (self.imm(pins)) |_| self.rol(pins, .acc), // ROL A
+
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
+
+
0x30 => if (self.imm(pins)) |_| self.branch(pins, self.status.negative), // BMI
+
0x31 => if (self.zpIndY(pins)) |_| self._and(pins, v), // AND (zp),Y
+
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
-
0x39 => if (self.absOff(pins, self.y)) self._and(pins, v), // AND abs,Y
-
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
+
0x39 => if (self.absOff(pins, self.y)) |_| self._and(pins, v), // AND abs,Y
+
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
0x40 => self.rti(pins), // RTI
-
0x41 => if (self.zpXInd(pins)) self.eor(pins, v), // EOR (zp,X)
-
0x45 => if (self.zp(pins)) self.eor(pins, v), // EOR zp
-
0x46 => if (self.zp(pins)) self.lsr(pins, .mem), // LSR zp
-
0x49 => if (self.imm(pins)) self.eor(pins, v), // EOR #
-
0x4a => if (self.imm(pins)) self.lsr(pins, .acc), // LSR A
-
0x4d => if (self.abs(pins)) self.eor(pins, v), // EOR abs
-
0x4e => if (self.abs(pins)) self.lsr(pins, .mem), // LSR abs
-
-
0x50 => if (self.imm(pins)) self.branch(pins, !self.status.overflow), // BVC
-
0x51 => if (self.zpIndY(pins)) self.eor(pins, v), // EOR (zp),Y
-
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
+
0x41 => if (self.zpXInd(pins)) |_| self.eor(pins, v), // EOR (zp,X)
+
0x45 => if (self.zp(pins)) |_| self.eor(pins, v), // EOR zp
+
0x46 => if (self.zp(pins)) |_| self.lsr(pins, .mem), // LSR zp
+
0x49 => if (self.imm(pins)) |_| self.eor(pins, v), // EOR #
+
0x4a => if (self.imm(pins)) |_| self.lsr(pins, .acc), // LSR A
+
0x4d => if (self.abs(pins)) |_| self.eor(pins, v), // EOR abs
+
0x4e => if (self.abs(pins)) |_| self.lsr(pins, .mem), // LSR abs
+
+
0x50 => if (self.imm(pins)) |_| self.branch(pins, !self.status.overflow), // BVC
+
0x51 => if (self.zpIndY(pins)) |_| self.eor(pins, v), // EOR (zp),Y
+
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
-
0x59 => if (self.absOff(pins, self.y)) self.eor(pins, v), // EOR abs,Y
-
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
-
-
// 0x61 => if (self.zpXInd(pins)) self.adc(pins, v), // ADC (zp,X)
-
// 0x65 => if (self.zp(pins)) self.adc(pins, v), // ADC zp
-
0x66 => if (self.zp(pins)) self.ror(pins, .mem), // ROR zp
-
// 0x69 => if (self.imm(pins)) self.adc(pins, v), // ADC #
-
0x6a => if (self.imm(pins)) self.ror(pins, .acc), // ROR A
-
// 0x6d => if (self.abs(pins)) self.adc(pins, v), // ADC abs
-
0x6e => if (self.abs(pins)) self.ror(pins, .mem), // ROR abs
-
-
0x70 => if (self.imm(pins)) self.branch(pins, self.status.overflow), // BVS
-
// 0x71 => if (self.zpIndY(pins)) self.adc(pins, v), // ADC (zp),Y
-
// 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
+
0x59 => if (self.absOff(pins, self.y)) |_| self.eor(pins, v), // EOR abs,Y
+
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
+
+
0x61 => if (self.zpXInd(pins)) |_| self.adc(pins, v), // ADC (zp,X)
+
0x65 => if (self.zp(pins)) |_| self.adc(pins, v), // ADC zp
+
0x66 => if (self.zp(pins)) |_| self.ror(pins, .mem), // ROR zp
+
0x69 => if (self.imm(pins)) |_| self.adc(pins, v), // ADC #
+
0x6a => if (self.imm(pins)) |_| self.ror(pins, .acc), // ROR A
+
0x6d => if (self.abs(pins)) |_| self.adc(pins, v), // ADC abs
+
0x6e => if (self.abs(pins)) |_| self.ror(pins, .mem), // ROR abs
+
+
0x70 => if (self.imm(pins)) |_| self.branch(pins, self.status.overflow), // BVS
+
0x71 => if (self.zpIndY(pins)) |_| self.adc(pins, v), // ADC (zp),Y
+
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
-
// 0x79 => if (self.absOff(pins, self.y)) self.adc(pins, v), // ADC abs,Y
-
// 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
-
-
0x81 => if (self.zpXInd(pins)) self.st(pins, self.y), // STA (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.dec(pins, &self.y), // DEY
-
0x8a => if (self.imm(pins)) self.ld(pins, &self.a, self.x), // TXA
-
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
-
-
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
-
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
-
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
-
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
-
-
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 #
-
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
-
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
-
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
-
-
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
-
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
+
0x79 => if (self.absOff(pins, self.y)) |_| self.adc(pins, v), // ADC abs,Y
+
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
+
+
0x81 => if (self.zpXInd(pins)) |_| self.st(pins, self.y), // STA (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
+
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
+
+
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
+
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
+
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
+
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
+
+
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 #
+
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
+
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
+
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
+
+
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
+
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
-
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
-
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
-
-
0xd0 => if (self.imm(pins)) self.branch(pins, !self.status.zero), // BNE
+
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
+
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
+
+
0xc0 => if (self.imm(pins)) |_| self.cmp(pins, self.y), // CPY #
+
0xc1 => if (self.zpXInd(pins)) |_| self.cmp(pins, self.a), // CMP (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
+
0xc9 => if (self.imm(pins)) |_| self.cmp(pins, self.a), // CMP #
+
0xca => if (self.imm(pins)) |_| self.dexy(pins, &self.x), // DEY
+
0xcc => if (self.abs(pins)) |_| self.cmp(pins, self.y), // CPY abs
+
0xcd => if (self.abs(pins)) |_| self.cmp(pins, self.a), // CMP abs
+
0xce => if (self.abs(pins)) |c| self.dec(pins, c), // DEC abs
+
+
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
+
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
-
-
0xea => if (self.imm(pins)) self.fetch(pins), // NOP
-
-
0xf0 => if (self.imm(pins)) self.branch(pins, self.status.zero), // BEQ
+
0xd9 => if (self.absOff(pins, self.y)) |_| self.cmp(pins, self.a), // CMP abs,Y
+
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
+
+
0xe0 => if (self.imm(pins)) |_| self.cmp(pins, self.x), // CPX #
+
0xe1 => if (self.zpXInd(pins)) |_| self.sbc(pins, v), // SBC (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
+
0xe9 => if (self.imm(pins)) |_| self.sbc(pins, v), // SBC #
+
0xea => if (self.imm(pins)) |_| self.fetch(pins), // NOP
+
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
+
+
0xf0 => if (self.imm(pins)) |_| self.branch(pins, self.status.zero), // BEQ
+
0xf1 => if (self.zpIndY(pins)) |_| self.sbc(pins, v), // SBC (zp),Y
+
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
+
0xf9 => if (self.absOff(pins, self.y)) |_| self.sbc(pins, v), // SBC abs,Y
+
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
-
else => log.err("UNHANDLED OP={X}", .{self.opcode}),
+
else => {
+
log.err("UNHANDLED OP={X}", .{self.opcode});
+
self.fetch(pins);
+
},
}
}
-
inline fn imm(self: *Cpu, pins: *zesty.Pins) bool {
+
inline fn imm(self: *Cpu, pins: *zesty.Pins) ?u3 {
switch (self.cycle) {
0 => {
self.pc +%= 1;
pins.cpu_addr = self.pc;
},
-
else => return true,
+
else => return self.cycle - 1,
}
-
return false;
+
return null;
}
-
inline fn zp(self: *Cpu, pins: *zesty.Pins) bool {
+
inline fn zp(self: *Cpu, pins: *zesty.Pins) ?u3 {
switch (self.cycle) {
0 => {
self.pc +%= 1;
···
self.hilo = .{ .lo = pins.cpu_data };
pins.cpu_addr = self.hilo.addr();
},
-
else => return true,
+
else => return self.cycle - 2,
}
-
return false;
+
return null;
}
-
inline fn zpOff(self: *Cpu, pins: *zesty.Pins, v: u8) bool {
+
inline fn zpOff(self: *Cpu, pins: *zesty.Pins, v: u8) ?u3 {
switch (self.cycle) {
0 => {
self.pc +%= 1;
···
self.hilo.lo +%= v;
pins.cpu_addr = self.hilo.addr();
},
-
else => return true,
+
else => return self.cycle - 3,
}
-
return false;
+
return null;
}
-
inline fn abs(self: *Cpu, pins: *zesty.Pins) bool {
+
inline fn abs(self: *Cpu, pins: *zesty.Pins) ?u3 {
switch (self.cycle) {
0 => {
self.pc +%= 1;
···
self.hilo.hi = pins.cpu_data;
pins.cpu_addr = self.hilo.addr();
},
-
else => return true,
+
else => return self.cycle - 3,
}
-
return false;
+
return null;
}
-
inline fn absOff(self: *Cpu, pins: *zesty.Pins, v: u8) bool {
+
inline fn absOff(self: *Cpu, pins: *zesty.Pins, v: u8) ?u3 {
switch (self.cycle) {
0 => {
self.pc +%= 1;
···
3 => {
pins.cpu_addr = self.hilo.addr();
},
-
else => return true,
+
else => return self.cycle - 4,
}
-
return false;
+
return null;
}
-
inline fn zpXInd(self: *Cpu, pins: *zesty.Pins) bool {
+
inline fn zpXInd(self: *Cpu, pins: *zesty.Pins) ?u3 {
switch (self.cycle) {
0 => {
self.pc +%= 1;
···
self.hilo.hi = pins.cpu_data;
pins.cpu_addr = self.hilo.addr();
},
-
else => return true,
+
else => return self.cycle - 5,
}
-
return false;
+
return null;
}
-
inline fn zpIndY(self: *Cpu, pins: *zesty.Pins) bool {
+
inline fn zpIndY(self: *Cpu, pins: *zesty.Pins) ?u3 {
switch (self.cycle) {
0 => {
self.pc +%= 1;
···
4 => {
pins.cpu_addr = self.hilo.addr();
},
-
else => return true,
+
else => return self.cycle - 5,
}
-
return false;
+
return null;
}
inline fn fetch(self: *Cpu, pins: *zesty.Pins) void {
···
self.setNZ(v);
self.fetch(pins);
}
-
inline fn dec(self: *Cpu, pins: *zesty.Pins, v: *u8) void {
+
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.fetch(pins);
+
}
+
inline fn sbc(self: *Cpu, pins: *zesty.Pins, v: u8) void {
+
self.adc(pins, ~v);
+
}
+
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);
+
self.fetch(pins);
+
}
+
inline fn inxy(self: *Cpu, pins: *zesty.Pins, v: *u8) void {
+
v.* +%= 1;
+
self.setNZ(v.*);
+
self.fetch(pins);
+
}
+
+
inline fn dexy(self: *Cpu, pins: *zesty.Pins, v: *u8) void {
v.* -%= 1;
self.setNZ(v.*);
self.fetch(pins);
}
+
inline fn inc(self: *Cpu, pins: *zesty.Pins, c: u3) void {
+
switch (c) {
+
0 => {
+
// Dummy write? (Not sure why the processor does this)
+
self.hilo = .{ .lo = pins.cpu_data };
+
pins.cpu_rw = .write;
+
},
+
1 => {
+
self.hilo.lo +%= 1;
+
self.setNZ(self.hilo.lo);
+
pins.cpu_data = self.hilo.lo;
+
pins.cpu_rw = .write;
+
},
+
else => self.fetch(pins),
+
}
+
}
+
inline fn dec(self: *Cpu, pins: *zesty.Pins, c: u3) void {
+
switch (c) {
+
0 => {
+
// Dummy write? (Not sure why the processor does this)
+
self.hilo = .{ .lo = pins.cpu_data };
+
pins.cpu_rw = .write;
+
},
+
1 => {
+
self.hilo.lo -%= 1;
+
self.setNZ(self.hilo.lo);
+
pins.cpu_data = self.hilo.lo;
+
pins.cpu_rw = .write;
+
},
+
else => self.fetch(pins),
+
}
+
}
+
inline fn bit(self: *Cpu, pins: *zesty.Pins) void {
+
const status: Status = .from(pins.cpu_data);
+
self.status.negative = status.negative;
+
self.status.overflow = status.overflow;
+
self.status.zero = (self.a & pins.cpu_data) == 0;
+
self.fetch(pins);
+
}
//------------------------------------------------------
// Opcodes: Control flow
···
pins.cpu_data,
true,
);
-
pins.cpu_addr = self.pc;
+
pins.cpu_addr = pc;
if (!page_crossed) self.fetchAt(pins, pc);
},
else => self.fetchAt(pins, self.hilo.addr()),
···
inline fn offsetWithPageFaultBehavior(
self: *Addr,
v: u8,
-
signed: bool,
+
comptime signed: bool,
) struct { u16, bool } {
-
self.lo, const page_crossed = if (!signed and v < 0x80)
-
@addWithOverflow(self.lo, v)
-
else
+
const is_negative = signed and v >= 0x80;
+
self.lo, const page_crossed = if (is_negative)
// v should NEVER be lower than 0x80, but saturate down to 0 to be safe
-
@subWithOverflow(self.lo, v -| 0x80);
+
@subWithOverflow(self.lo, v - 0x80)
+
else
+
@addWithOverflow(self.lo, v);
// If we haven't crossed the page boundary, skip over the next cycle.
-
defer self.hi +%= page_crossed;
+
defer {
+
if (is_negative)
+
self.hi -%= page_crossed
+
else
+
self.hi +%= page_crossed;
+
}
return .{ self.addr(), page_crossed > 0 };
}
};
+279
src/tests/cpu/alu.zig
···
try std.testing.expect(!z.cpu.status.zero);
try std.testing.expect(z.cpu.status.carry);
}
+
+
test "ADC" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\LDA #%10000101
+
\\SEC
+
\\ADC #%10000101
+
),
+
},
+
},
+
});
+
cpu.run(&z); // Setup A register
+
cpu.run(&z); // Set carry
+
cpu.run(&z); // Perform addition
+
+
try std.testing.expectEqual(0x8005, 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);
+
try std.testing.expect(z.cpu.status.carry);
+
try std.testing.expect(z.cpu.status.overflow);
+
}
+
+
test "SBC" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\LDA #%10000101
+
\\SEC
+
\\SBC #%01111010
+
),
+
},
+
},
+
});
+
cpu.run(&z); // Setup A register
+
cpu.run(&z); // Set carry
+
cpu.run(&z); // Perform addition
+
+
try std.testing.expectEqual(0x8005, 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);
+
try std.testing.expect(z.cpu.status.carry);
+
try std.testing.expect(z.cpu.status.overflow);
+
}
+
+
test "DEC" {
+
var z = cpu.testZes(.{
+
.ram = &.{
+
.{ 0x42, &.{0x69} },
+
},
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\DEC <$42
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8002, z.cpu.pc);
+
try std.testing.expectEqual(0x68, z.ram[0x42]);
+
try std.testing.expect(!z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
}
+
+
test "DEX" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\DEX
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8002, 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);
+
}
+
+
test "DEY" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\DEY
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8002, 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);
+
}
+
+
test "INC" {
+
var z = cpu.testZes(.{
+
.ram = &.{
+
.{ 0x42, &.{0x69} },
+
},
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\INC <$42
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8002, z.cpu.pc);
+
try std.testing.expectEqual(0x6a, z.ram[0x42]);
+
try std.testing.expect(!z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
}
+
+
test "INX" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\INX
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8002, 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);
+
}
+
+
test "INY" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\INY
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8002, 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);
+
}
+
+
test "CMP" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\LDA #$dd
+
\\CMP #$42
+
\\CMP #$dd
+
\\CMP #$ee
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8004, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
try std.testing.expect(z.cpu.status.carry);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8006, z.cpu.pc);
+
try std.testing.expect(!z.cpu.status.negative);
+
try std.testing.expect(z.cpu.status.zero);
+
try std.testing.expect(z.cpu.status.carry);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8008, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
try std.testing.expect(!z.cpu.status.carry);
+
}
+
+
test "CPX" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\LDX #$dd
+
\\CPX #$42
+
\\CPX #$dd
+
\\CPX #$ee
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8004, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
try std.testing.expect(z.cpu.status.carry);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8006, z.cpu.pc);
+
try std.testing.expect(!z.cpu.status.negative);
+
try std.testing.expect(z.cpu.status.zero);
+
try std.testing.expect(z.cpu.status.carry);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8008, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
try std.testing.expect(!z.cpu.status.carry);
+
}
+
test "CPY" {
+
var z = cpu.testZes(.{
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\LDY #$dd
+
\\CPY #$42
+
\\CPY #$dd
+
\\CPY #$ee
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8004, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
try std.testing.expect(z.cpu.status.carry);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8006, z.cpu.pc);
+
try std.testing.expect(!z.cpu.status.negative);
+
try std.testing.expect(z.cpu.status.zero);
+
try std.testing.expect(z.cpu.status.carry);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8008, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
try std.testing.expect(!z.cpu.status.zero);
+
try std.testing.expect(!z.cpu.status.carry);
+
}
+
test "BIT" {
+
var z = cpu.testZes(.{
+
.ram = &.{
+
.{ 0x42, &.{0b11000000} },
+
},
+
.rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\LDA #%00000101
+
\\BIT <$42
+
),
+
},
+
},
+
});
+
cpu.run(&z);
+
+
cpu.run(&z);
+
try std.testing.expectEqual(0x8004, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
try std.testing.expect(z.cpu.status.overflow);
+
try std.testing.expect(z.cpu.status.zero);
+
}
+142 -3
src/tests/cpu/assembler.zig
···
pub fn assembleComptime(comptime in: []const u8) []const u8 {
// Heuristically defined
-
@setEvalBranchQuota(in.len * 100);
+
@setEvalBranchQuota(in.len * 500);
return comptime v: {
var count_w: std.Io.Writer.Discarding = .init(&.{});
-
assemble(in, &count_w.writer) catch |err| std.debug.panic("error: {}", .{err});
-
+
assemble(in, &count_w.writer) catch |err| @compileError(std.fmt.comptimePrint("error: {}", .{err}));
var buf: [count_w.fullCount()]u8 = undefined;
var w: std.Io.Writer = .fixed(&buf);
assemble(in, &w) catch unreachable;
···
ora,
@"and",
eor,
+
adc,
+
sbc,
asl,
lsr,
rol,
ror,
+
inc,
+
dec,
+
inx,
+
dex,
+
iny,
+
dey,
+
cmp,
+
cpx,
+
cpy,
+
bit,
clc,
sec,
···
cld,
sed,
+
bpl,
+
bmi,
+
bvc,
+
bvs,
+
bcc,
+
bcs,
+
bne,
+
beq,
+
+
nop,
+
fn encode(op: Opcode, opr: Operand) ?u8 {
return switch (op) {
.brk => switch (opr) {
···
.indirect_indexed => 0x51,
else => null,
},
+
.adc => switch (opr) {
+
.zeropage => 0x65,
+
.zeropage_x => 0x75,
+
.absolute => 0x6d,
+
.absolute_x => 0x7d,
+
.absolute_y => 0x79,
+
.immediate => 0x69,
+
.indexed_indirect => 0x61,
+
.indirect_indexed => 0x71,
+
else => null,
+
},
+
.sbc => switch (opr) {
+
.zeropage => 0xe5,
+
.zeropage_x => 0xf5,
+
.absolute => 0xed,
+
.absolute_x => 0xfd,
+
.absolute_y => 0xf9,
+
.immediate => 0xe9,
+
.indexed_indirect => 0xe1,
+
.indirect_indexed => 0xf1,
+
else => null,
+
},
.asl => switch (opr) {
.zeropage => 0x06,
.zeropage_x => 0x16,
···
.implied => 0x6a,
else => null,
},
+
.inc => switch (opr) {
+
.zeropage => 0xe6,
+
.zeropage_x => 0xf6,
+
.absolute => 0xee,
+
.absolute_x => 0xfe,
+
else => null,
+
},
+
.dec => switch (opr) {
+
.zeropage => 0xc6,
+
.zeropage_x => 0xd6,
+
.absolute => 0xce,
+
.absolute_x => 0xde,
+
else => null,
+
},
+
.inx => switch (opr) {
+
.implied => 0xe8,
+
else => null,
+
},
+
.dex => switch (opr) {
+
.implied => 0xca,
+
else => null,
+
},
+
.iny => switch (opr) {
+
.implied => 0xc8,
+
else => null,
+
},
+
.dey => switch (opr) {
+
.implied => 0x88,
+
else => null,
+
},
+
.cmp => switch (opr) {
+
.zeropage => 0xc5,
+
.zeropage_x => 0xd5,
+
.absolute => 0xcd,
+
.absolute_x => 0xdd,
+
.absolute_y => 0xd9,
+
.immediate => 0xc9,
+
.indexed_indirect => 0xc1,
+
.indirect_indexed => 0xd1,
+
else => null,
+
},
+
.cpx => switch (opr) {
+
.immediate => 0xe0,
+
.zeropage => 0xe4,
+
.absolute => 0xec,
+
else => null,
+
},
+
.cpy => switch (opr) {
+
.immediate => 0xc0,
+
.zeropage => 0xc4,
+
.absolute => 0xcc,
+
else => null,
+
},
+
.bit => switch (opr) {
+
.zeropage => 0x24,
+
.absolute => 0x2c,
+
else => null,
+
},
.clc => switch (opr) {
.implied => 0x18,
else => null,
···
.implied => 0xf8,
else => null,
},
+
+
.bpl => switch (opr) {
+
.immediate => 0x10,
+
else => null,
+
},
+
.bmi => switch (opr) {
+
.immediate => 0x30,
+
else => null,
+
},
+
.bvc => switch (opr) {
+
.immediate => 0x50,
+
else => null,
+
},
+
.bvs => switch (opr) {
+
.immediate => 0x70,
+
else => null,
+
},
+
.bcc => switch (opr) {
+
.immediate => 0x90,
+
else => null,
+
},
+
.bcs => switch (opr) {
+
.immediate => 0xb0,
+
else => null,
+
},
+
.bne => switch (opr) {
+
.immediate => 0xd0,
+
else => null,
+
},
+
.beq => switch (opr) {
+
.immediate => 0xf0,
+
else => null,
+
},
+
.nop => switch (opr) {
+
.implied => 0xea,
+
else => null,
+
},
};
}
};
+91 -2
src/tests/cpu/control_flow.zig
···
try std.testing.expectEqual(0x69, z.pins.cpu_data);
}
-
test "BRK and RTI" {
+
test "BRK" {
var z = cpu.testZes(.{
// Set a recognizable IRQ offset
.irq = 0x8964,
.rom = &.{
-
.{ 0x8000, cpu.assemble("brk") }, // BRK
+
.{ 0x8000, cpu.assemble("brk") },
},
});
···
try std.testing.expectEqual(0x00, z.ram[0x1fc]);
try std.testing.expectEqual(status, z.ram[0x1fb]);
}
+
+
test "set and clear" {
+
// TODO: Test CLV as well
+
var z = cpu.testZes(.{ .rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\sec
+
\\cli
+
\\sed
+
\\clc
+
\\sei
+
\\cld
+
),
+
},
+
} });
+
+
// Initial state
+
try std.testing.expect(!z.cpu.status.carry);
+
try std.testing.expect(z.cpu.status.irq_disabled);
+
try std.testing.expect(!z.cpu.status.decimal);
+
+
cpu.run(&z); // SEC
+
try std.testing.expectEqual(0x8001, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.carry);
+
+
cpu.run(&z); // CLI
+
try std.testing.expectEqual(0x8002, z.cpu.pc);
+
try std.testing.expect(!z.cpu.status.irq_disabled);
+
+
cpu.run(&z); // SED
+
try std.testing.expectEqual(0x8003, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.decimal);
+
+
cpu.run(&z); // CLC
+
try std.testing.expectEqual(0x8004, z.cpu.pc);
+
try std.testing.expect(!z.cpu.status.carry);
+
+
cpu.run(&z); // SEI
+
try std.testing.expectEqual(0x8005, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.irq_disabled);
+
+
cpu.run(&z); // CLD
+
try std.testing.expectEqual(0x8006, z.cpu.pc);
+
try std.testing.expect(!z.cpu.status.decimal);
+
}
+
+
test "branches" {
+
// TODO: Test overflow as well
+
var z = cpu.testZes(.{ .rom = &.{
+
.{
+
0x8000, cpu.assemble(
+
\\sec
+
\\bcs #5
+
\\
+
\\lda #$80
+
\\bmi #5
+
\\
+
\\lda #0
+
\\beq #$87
+
\\nop
+
),
+
},
+
} });
+
+
cpu.run(&z); // SEC
+
try std.testing.expectEqual(0x8001, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.carry);
+
cpu.run(&z); // BCS
+
try std.testing.expectEqual(0x8007, z.cpu.pc);
+
// LDA #80 should be skipped here.
+
try std.testing.expect(!z.cpu.status.negative);
+
+
cpu.run(&z); // LDA #0
+
try std.testing.expectEqual(0x8009, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.zero);
+
cpu.run(&z); // BEQ (jump backwards)
+
try std.testing.expectEqual(0x8003, z.cpu.pc);
+
+
cpu.run(&z); // LDA #$80
+
try std.testing.expectEqual(0xa9, z.cpu.opcode);
+
try std.testing.expectEqual(0x8005, z.cpu.pc);
+
try std.testing.expect(z.cpu.status.negative);
+
+
// TODO: investigate why the CPU run helper doesn't stop after branching
+
z.stepCpu(); // BMI
+
z.stepCpu(); // BMI
+
z.stepCpu(); // BMI
+
try std.testing.expectEqual(0x800c, z.cpu.pc);
+
}