implemented SBC, ADC and BIT family of instruction, .logs match now 73 instructions on nestest
This commit is contained in:
@@ -27,6 +27,7 @@
|
|||||||
- Trace now matches the nestest reference log through line 38 (C782 BIT, CYC:98), field-for-field (PC/A/X/Y/P/SP/CYC verified by label-comparison script). Next panic is on 0x24 (BIT zp) at line 38, top of Batch 6
|
- Trace now matches the nestest reference log through line 38 (C782 BIT, CYC:98), field-for-field (PC/A/X/Y/P/SP/CYC verified by label-comparison script). Next panic is on 0x24 (BIT zp) at line 38, top of Batch 6
|
||||||
- Batch 3 done (loads): LDA (8 modes), LDX (5 modes), LDY (5 modes) via three shared cores (lda/ldx/ldy) + thin wrappers. Caught two real bugs in review: all 4 LDY wrappers called self.ldx (would write to X instead of Y), and 6 of 8 LDX/LDY wrappers had wrong base cycles (3 instead of 4). Both fixed
|
- Batch 3 done (loads): LDA (8 modes), LDX (5 modes), LDY (5 modes) via three shared cores (lda/ldx/ldy) + thin wrappers. Caught two real bugs in review: all 4 LDY wrappers called self.ldx (would write to X instead of Y), and 6 of 8 LDX/LDY wrappers had wrong base cycles (3 instead of 4). Both fixed
|
||||||
- Batch 4 done (stores): STA (7 modes), STX (3 modes), STY (3 modes) via three shared cores (sta/stx/sty) + thin wrappers. No flag changes. Refactored stx_zp to use the shared stx core for consistency. Fixed a comment typo (sta_abx was labeled 0x95 instead of 0x9D)
|
- Batch 4 done (stores): STA (7 modes), STX (3 modes), STY (3 modes) via three shared cores (sta/stx/sty) + thin wrappers. No flag changes. Refactored stx_zp to use the shared stx core for consistency. Fixed a comment typo (sta_abx was labeled 0x95 instead of 0x9D)
|
||||||
|
- Batch 5 done (compares): CMP (8 modes), CPX (3 modes), CPY (3 modes) via three shared cores (cmp/cpx/cpy) + wrappers + immediate variants. Sets C (reg >= mem), Z, N; no store. Uses wrapping_sub for the borrow math. 70 of 151 official opcodes done. No diff progress expected until the log reaches these opcodes
|
||||||
- trace bytes are length-aware via opcode_len (fixed-width byte field, columns align for awk diff)
|
- trace bytes are length-aware via opcode_len (fixed-width byte field, columns align for awk diff)
|
||||||
|
|
||||||
## Next
|
## Next
|
||||||
@@ -39,8 +40,8 @@ CPU implementation batches (by family, ordered by nestest log frequency):
|
|||||||
- Batch 2 - DONE (branches, see Done section)
|
- Batch 2 - DONE (branches, see Done section)
|
||||||
- Batch 3 - DONE (loads, see Done section)
|
- Batch 3 - DONE (loads, see Done section)
|
||||||
- Batch 4 - DONE (stores, see Done section)
|
- Batch 4 - DONE (stores, see Done section)
|
||||||
- Batch 5 - Compares: CMP 8 modes, CPX 3, CPY 3 (set C/Z/N, no store). Next up. Note: keeping family order on purpose, no diff progress until the log reaches these opcodes
|
- Batch 5 - DONE (compares, see Done section)
|
||||||
- Batch 6 - Arithmetic: ADC 8 modes, SBC 8 modes (N/V/C/Z, decimal flag inert), BIT 2 modes
|
- Batch 6 - Arithmetic: ADC 8 modes, SBC 8 modes (N/V/C/Z, decimal flag inert), BIT 2 modes. Next up
|
||||||
- Batch 7 - Logic: AND, ORA, EOR (8 modes each)
|
- Batch 7 - Logic: AND, ORA, EOR (8 modes each)
|
||||||
- Batch 8 - Inc/Dec: INC/DEC 4 modes each (RMW), INX, INY, DEX, DEY
|
- Batch 8 - Inc/Dec: INC/DEC 4 modes each (RMW), INX, INY, DEX, DEY
|
||||||
- Batch 9 - Shifts: ASL, LSR, ROL, ROR (accumulator + 4 memory modes each, RMW)
|
- Batch 9 - Shifts: ASL, LSR, ROL, ROR (accumulator + 4 memory modes each, RMW)
|
||||||
|
|||||||
@@ -35,4 +35,39 @@ C776 4C 7D C7 A:00 X:00 Y:00 P:26 SP:FB CYC:88
|
|||||||
C77D EA A:00 X:00 Y:00 P:26 SP:FB CYC:91
|
C77D EA A:00 X:00 Y:00 P:26 SP:FB CYC:91
|
||||||
C77E A9 FF A:00 X:00 Y:00 P:26 SP:FB CYC:93
|
C77E A9 FF A:00 X:00 Y:00 P:26 SP:FB CYC:93
|
||||||
C780 85 01 A:FF X:00 Y:00 P:A4 SP:FB CYC:95
|
C780 85 01 A:FF X:00 Y:00 P:A4 SP:FB CYC:95
|
||||||
C782 24 A:FF X:00 Y:00 P:A4 SP:FB CYC:98
|
C782 24 01 A:FF X:00 Y:00 P:A4 SP:FB CYC:98
|
||||||
|
C784 70 04 A:FF X:00 Y:00 P:E4 SP:FB CYC:101
|
||||||
|
C78A EA A:FF X:00 Y:00 P:E4 SP:FB CYC:104
|
||||||
|
C78B 24 01 A:FF X:00 Y:00 P:E4 SP:FB CYC:106
|
||||||
|
C78D 50 03 A:FF X:00 Y:00 P:E4 SP:FB CYC:109
|
||||||
|
C78F 4C 96 C7 A:FF X:00 Y:00 P:E4 SP:FB CYC:111
|
||||||
|
C796 EA A:FF X:00 Y:00 P:E4 SP:FB CYC:114
|
||||||
|
C797 A9 00 A:FF X:00 Y:00 P:E4 SP:FB CYC:116
|
||||||
|
C799 85 01 A:00 X:00 Y:00 P:66 SP:FB CYC:118
|
||||||
|
C79B 24 01 A:00 X:00 Y:00 P:66 SP:FB CYC:121
|
||||||
|
C79D 50 04 A:00 X:00 Y:00 P:26 SP:FB CYC:124
|
||||||
|
C7A3 EA A:00 X:00 Y:00 P:26 SP:FB CYC:127
|
||||||
|
C7A4 24 01 A:00 X:00 Y:00 P:26 SP:FB CYC:129
|
||||||
|
C7A6 70 03 A:00 X:00 Y:00 P:26 SP:FB CYC:132
|
||||||
|
C7A8 4C AF C7 A:00 X:00 Y:00 P:26 SP:FB CYC:134
|
||||||
|
C7AF EA A:00 X:00 Y:00 P:26 SP:FB CYC:137
|
||||||
|
C7B0 A9 00 A:00 X:00 Y:00 P:26 SP:FB CYC:139
|
||||||
|
C7B2 10 04 A:00 X:00 Y:00 P:26 SP:FB CYC:141
|
||||||
|
C7B8 EA A:00 X:00 Y:00 P:26 SP:FB CYC:144
|
||||||
|
C7B9 A9 80 A:00 X:00 Y:00 P:26 SP:FB CYC:146
|
||||||
|
C7BB 10 03 A:80 X:00 Y:00 P:A4 SP:FB CYC:148
|
||||||
|
C7BD 4C D9 C7 A:80 X:00 Y:00 P:A4 SP:FB CYC:150
|
||||||
|
C7D9 EA A:80 X:00 Y:00 P:A4 SP:FB CYC:153
|
||||||
|
C7DA 60 A:80 X:00 Y:00 P:A4 SP:FB CYC:155
|
||||||
|
C600 20 DB C7 A:80 X:00 Y:00 P:A4 SP:FD CYC:161
|
||||||
|
C7DB EA A:80 X:00 Y:00 P:A4 SP:FB CYC:167
|
||||||
|
C7DC A9 FF A:80 X:00 Y:00 P:A4 SP:FB CYC:169
|
||||||
|
C7DE 85 01 A:FF X:00 Y:00 P:A4 SP:FB CYC:171
|
||||||
|
C7E0 24 01 A:FF X:00 Y:00 P:A4 SP:FB CYC:174
|
||||||
|
C7E2 A9 00 A:FF X:00 Y:00 P:E4 SP:FB CYC:177
|
||||||
|
C7E4 38 A:00 X:00 Y:00 P:66 SP:FB CYC:179
|
||||||
|
C7E5 78 A:00 X:00 Y:00 P:67 SP:FB CYC:181
|
||||||
|
C7E6 F8 A:00 X:00 Y:00 P:67 SP:FB CYC:183
|
||||||
|
C7E7 08 A:00 X:00 Y:00 P:6F SP:FB CYC:185
|
||||||
|
C7E8 68 A:00 X:00 Y:00 P:6F SP:FA CYC:188
|
||||||
|
C7E9 29 A:7F X:00 Y:00 P:6D SP:FB CYC:192
|
||||||
|
|||||||
+170
@@ -166,6 +166,24 @@ impl Cpu {
|
|||||||
0xC0 => self.cpy_imm(bus),
|
0xC0 => self.cpy_imm(bus),
|
||||||
0xC4 => self.cpy_zp(bus),
|
0xC4 => self.cpy_zp(bus),
|
||||||
0xCC => self.cpy_abs(bus),
|
0xCC => self.cpy_abs(bus),
|
||||||
|
0x69 => self.adc_imm(bus),
|
||||||
|
0x65 => self.adc_zp(bus),
|
||||||
|
0x75 => self.adc_zpx(bus),
|
||||||
|
0x6D => self.adc_abs(bus),
|
||||||
|
0x7D => self.adc_abx(bus),
|
||||||
|
0x79 => self.adc_aby(bus),
|
||||||
|
0x61 => self.adc_izx(bus),
|
||||||
|
0x71 => self.adc_izy(bus),
|
||||||
|
0xE9 => self.sbc_imm(bus),
|
||||||
|
0xE5 => self.sbc_zp(bus),
|
||||||
|
0xF5 => self.sbc_zpx(bus),
|
||||||
|
0xED => self.sbc_abs(bus),
|
||||||
|
0xFD => self.sbc_abx(bus),
|
||||||
|
0xF9 => self.sbc_aby(bus),
|
||||||
|
0xE1 => self.sbc_izx(bus),
|
||||||
|
0xF1 => self.sbc_izy(bus),
|
||||||
|
0x24 => self.bit_zp(bus),
|
||||||
|
0x2C => self.bit_abs(bus),
|
||||||
_ => panic!("illegal/unkown opcode {opcode:#04X} at {:#06X}", self.pc.wrapping_sub(1)),
|
_ => panic!("illegal/unkown opcode {opcode:#04X} at {:#06X}", self.pc.wrapping_sub(1)),
|
||||||
};
|
};
|
||||||
self.cycles += cycles as u64;
|
self.cycles += cycles as u64;
|
||||||
@@ -324,6 +342,46 @@ impl Cpu {
|
|||||||
base + extra
|
base + extra
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// shared add/sbc cores (based on adding the complement)
|
||||||
|
fn add_with_carry(&mut self, value: u8) {
|
||||||
|
let carry = if self.flag(FLAG_CARRY) { 1u16 } else { 0 };
|
||||||
|
let sum = self.a as u16 + value as u16 + carry;
|
||||||
|
let result = sum as u8;
|
||||||
|
if sum > 0xFF { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
|
||||||
|
|
||||||
|
if (self.a ^ result) & (value ^ result) & 0x80 != 0 {
|
||||||
|
self.set_flag(FLAG_OVERFLOW);
|
||||||
|
} else {
|
||||||
|
self.clear_flag(FLAG_OVERFLOW);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_zp_flags(result);
|
||||||
|
self.a = result;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adc(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
|
||||||
|
let value = self.read(bus, address);
|
||||||
|
self.add_with_carry(value); // add as-is
|
||||||
|
base + extra
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
|
||||||
|
let value = self.read(bus, address);
|
||||||
|
self.add_with_carry(!value); // add the complement => subtract
|
||||||
|
base + extra
|
||||||
|
}
|
||||||
|
// BIT core
|
||||||
|
fn bit(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
|
||||||
|
let value = self.read(bus, address);
|
||||||
|
self.clear_flag(FLAG_ZERO);
|
||||||
|
if self.a & value == 0 { self.set_flag(FLAG_ZERO); }
|
||||||
|
self.clear_flag(FLAG_NEGATIVE);
|
||||||
|
if value & 0x80 != 0 { self.set_flag(FLAG_NEGATIVE); }
|
||||||
|
self.clear_flag(FLAG_OVERFLOW);
|
||||||
|
if value & 0x40 != 0 { self.set_flag(FLAG_OVERFLOW); }
|
||||||
|
base + extra
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// NES 6502 opcode -> functions
|
// NES 6502 opcode -> functions
|
||||||
fn jmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4C, 3 bytes, 3 cycles
|
fn jmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4C, 3 bytes, 3 cycles
|
||||||
@@ -663,7 +721,101 @@ impl Cpu {
|
|||||||
2
|
2
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ADC variants
|
||||||
|
fn adc_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x65, 2 bytes, 3 cycles
|
||||||
|
let (address, extra) = self.address_zp(bus);
|
||||||
|
self.adc(bus, (address, extra), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adc_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x75, 2 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_zpx(bus);
|
||||||
|
self.adc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adc_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x6D, 3 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_abs(bus);
|
||||||
|
self.adc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adc_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x7D, 3 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_abx(bus);
|
||||||
|
self.adc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adc_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x79, 3 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_aby(bus);
|
||||||
|
self.adc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adc_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0x61, 2 bytes, 6 cycles
|
||||||
|
let (address, extra) = self.address_izx(bus);
|
||||||
|
self.adc(bus, (address, extra), 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn adc_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x71, 2 bytes, 5 cycles
|
||||||
|
let (address, extra) = self.address_izy(bus);
|
||||||
|
self.adc(bus, (address, extra), 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SBC variants
|
||||||
|
fn sbc_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0xE5, 2 bytes, 3 cycles
|
||||||
|
let (address, extra) = self.address_zp(bus);
|
||||||
|
self.sbc(bus, (address, extra), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0xF5, 2 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_zpx(bus);
|
||||||
|
self.sbc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0xED, 3 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_abs(bus);
|
||||||
|
self.sbc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0xFD, 3 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_abx(bus);
|
||||||
|
self.sbc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0xF9, 3 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_aby(bus);
|
||||||
|
self.sbc(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0xE1, 2 bytes, 6 cycles
|
||||||
|
let (address, extra) = self.address_izx(bus);
|
||||||
|
self.sbc(bus, (address, extra), 6)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0xF1, 2 bytes, 5 cycles
|
||||||
|
let (address, extra) = self.address_izy(bus);
|
||||||
|
self.sbc(bus, (address, extra), 5)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BIT variants
|
||||||
|
fn bit_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x24, 2 bytes, 3 cycles
|
||||||
|
let (address, extra) = self.address_zp(bus);
|
||||||
|
self.bit(bus, (address, extra), 3)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn bit_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x2C, 3 bytes, 4 cycles
|
||||||
|
let (address, extra) = self.address_abs(bus);
|
||||||
|
self.bit(bus, (address, extra), 4)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ADC/SBC immediate variants
|
||||||
|
fn adc_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0x69, 2 bytes, 2 cycles
|
||||||
|
let value = self.fetch(bus);
|
||||||
|
self.add_with_carry(value);
|
||||||
|
2
|
||||||
|
}
|
||||||
|
|
||||||
|
fn sbc_imm(&mut self, bus: &mut impl Bus) -> u8 { // 0xE9, 2 bytes, 2 cycles
|
||||||
|
let value = self.fetch(bus);
|
||||||
|
self.add_with_carry(!value);
|
||||||
|
2
|
||||||
|
}
|
||||||
|
|
||||||
// all branching alternatives here all 1 bytes
|
// all branching alternatives here all 1 bytes
|
||||||
fn bcs_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_CARRY)) } // 0xB0
|
fn bcs_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, self.flag(FLAG_CARRY)) } // 0xB0
|
||||||
@@ -748,6 +900,24 @@ impl Cpu {
|
|||||||
0xC0 => 2, // CPY imm
|
0xC0 => 2, // CPY imm
|
||||||
0xC4 => 2, // CPY zp
|
0xC4 => 2, // CPY zp
|
||||||
0xCC => 3, // CPY abs
|
0xCC => 3, // CPY abs
|
||||||
|
0x69 => 2, // ADC imm
|
||||||
|
0x65 => 2, // ADC zp
|
||||||
|
0x75 => 2, // ADC zpx
|
||||||
|
0x6D => 3, // ADC abs
|
||||||
|
0x7D => 3, // ADC abx
|
||||||
|
0x79 => 3, // ADC aby
|
||||||
|
0x61 => 2, // ADC izx
|
||||||
|
0x71 => 2, // ADC izy
|
||||||
|
0xE9 => 2, // SBC imm
|
||||||
|
0xE5 => 2, // SBC zp
|
||||||
|
0xF5 => 2, // SBC zpx
|
||||||
|
0xED => 3, // SBC abs
|
||||||
|
0xFD => 3, // SBC abx
|
||||||
|
0xF9 => 3, // SBC aby
|
||||||
|
0xE1 => 2, // SBC izx
|
||||||
|
0xF1 => 2, // SBC izy
|
||||||
|
0x24 => 2, // BIT zp
|
||||||
|
0x2C => 3, // BIT abs
|
||||||
_ => 1, // fallback, would show the invalid/unknown opcode only not its parameters
|
_ => 1, // fallback, would show the invalid/unknown opcode only not its parameters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user