all 151 offical opcodes implemented

This commit is contained in:
2026-08-09 11:48:37 -05:00
parent 16b51620a6
commit a54d3ccc09
3 changed files with 4580 additions and 39 deletions
+245 -9
View File
@@ -98,7 +98,7 @@ impl Cpu {
let cycles = match opcode {
// Jumps & subroutines
0x4C => self.jmp_abs(bus), 0x20 => self.jsr_abs(bus), 0x60 => self.rts_imp(bus),
0x40 => self.rti_imp(bus), 0x00 => self.brk_imp(bus),
0x40 => self.rti_imp(bus), 0x00 => self.brk_imp(bus), 0x6C => self.jmp_ind(bus),
// Stack
0x48 => self.pha_imp(bus), 0x68 => self.pla_imp(bus), 0x08 => self.php_imp(bus),
0x28 => self.plp_imp(bus),
@@ -154,6 +154,19 @@ impl Cpu {
// Decrements
0xC6 => self.dec_zp(bus), 0xD6 => self.dec_zpx(bus), 0xCE => self.dec_abs(bus),
0xDE => self.dec_abx(bus), 0xCA => self.dex_imp(bus), 0x88 => self.dey_imp(bus),
// Shifts
0x0A => self.asl_acc(bus), 0x06 => self.asl_zp(bus), 0x16 => self.asl_zpx(bus),
0x0E => self.asl_abs(bus), 0x1E => self.asl_abx(bus),
0x4A => self.lsr_acc(bus), 0x46 => self.lsr_zp(bus), 0x56 => self.lsr_zpx(bus),
0x4E => self.lsr_abs(bus), 0x5E => self.lsr_abx(bus),
0x2A => self.rol_acc(bus), 0x26 => self.rol_zp(bus), 0x36 => self.rol_zpx(bus),
0x2E => self.rol_abs(bus), 0x3E => self.rol_abx(bus),
0x6A => self.ror_acc(bus), 0x66 => self.ror_zp(bus), 0x76 => self.ror_zpx(bus),
0x6E => self.ror_abs(bus), 0x7E => self.ror_abx(bus),
// Transfers
0xAA => self.tax_imp(bus), 0xA8 => self.tay_imp(bus), 0x8A => self.txa_imp(bus),
0x98 => self.tya_imp(bus), 0xBA => self.tsx_imp(bus), 0x9A => self.txs_imp(bus),
_ => panic!("illegal/unkown opcode {opcode:#04X} at {:#06X}", self.pc.wrapping_sub(1)),
};
self.cycles += cycles as u64;
@@ -194,6 +207,13 @@ impl Cpu {
0xE6 => 2, /* INC zp */ 0xF6 => 2, /* INC zpx */ 0xEE => 3, /* INC abs */ 0xFE => 3, /* INC abx */
0xE8 => 1, /* INX imp */ 0xC8 => 1, /* INY imp */ 0xC6 => 2, /* DEC zp */ 0xD6 => 2, /* DEC zpx */
0xCE => 3, /* DEC abs */ 0xDE => 3, /* DEC abx */ 0xCA => 1, /* DEX imp */ 0x88 => 1, /* DEY imp */
0x0A => 1, /* ASL A */ 0x06 => 2, /* ASL zp */ 0x16 => 2, /* ASL zpx */ 0x0E => 3, /* ASL abs */
0x1E => 3, /* ASL abx */ 0x4A => 1, /* LSR A */ 0x46 => 2, /* LSR zp */ 0x56 => 2, /* LSR zpx */
0x4E => 3, /* LSR abs */ 0x5E => 3, /* LSR abx */ 0x2A => 1, /* ROL A */ 0x26 => 2, /* ROL zp */
0x36 => 2, /* ROL zpx */ 0x2E => 3, /* ROL abs */ 0x3E => 3, /* ROL abx */ 0x6A => 1, /* ROR A */
0x66 => 2, /* ROR zp */ 0x76 => 2, /* ROR zpx */ 0x6E => 3, /* ROR abs */ 0x7E => 3, /* ROR abx */
0xAA => 1, /* TAX */ 0xA8 => 1, /* TAY */ 0x8A => 1, /* TXA */ 0x98 => 1, /* TYA */
0xBA => 1, /* TSX */ 0x9A => 1, /* TXS */ 0x6C => 3, /* JMP ind */
_ => 1, // fallback, would show the invalid/unknown opcode only not its parameters
}
}
@@ -309,9 +329,9 @@ impl Cpu {
}
// Shared cores for STx instructions
fn sta(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
fn sta(&mut self, bus: &mut impl Bus, (address, _extra): (u16, u8), base: u8) -> u8 {
self.write(bus, address, self.a);
base + extra
base
}
fn stx(&mut self, bus: &mut impl Bus, (address, extra): (u16, u8), base: u8) -> u8 {
@@ -430,6 +450,42 @@ impl Cpu {
base // NO + extra — RMW cycles are fixed
}
// Shared shifting cores
// ASL: arithmetic shift left
fn shift_asl(&mut self, value: u8) -> u8 {
if value & 0x80 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = value << 1;
self.set_zp_flags(result);
result
}
// LSR: arithmetic shift right
fn shift_lsr(&mut self, value: u8) -> u8 {
if value & 0x01 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = value >> 1;
self.set_zp_flags(result);
result
}
// ROL: rotate left through carry
fn shift_rol(&mut self, value: u8) -> u8 {
let carry_in = if self.flag(FLAG_CARRY) { 1u8 } else { 0 };
if value & 0x80 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = (value << 1) | carry_in;
self.set_zp_flags(result);
result
}
// ROR: rotate right through carry
fn shift_ror(&mut self, value: u8) -> u8 {
let carry_in = if self.flag(FLAG_CARRY) { 1u8 } else { 0 };
if value & 0x01 != 0 { self.set_flag(FLAG_CARRY); } else { self.clear_flag(FLAG_CARRY); }
let result = (value >> 1) | (carry_in << 7);
self.set_zp_flags(result);
result
}
// NES 6502 opcode -> functions
fn jmp_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4C, 3 bytes, 3 cycles
self.pc = self.fetch16(bus);
@@ -630,14 +686,14 @@ impl Cpu {
self.sta(bus, (address, extra), 4)
}
fn sta_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x9D, 3 bytes, 4 cycles
fn sta_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x9D, 3 bytes, 4 + 1 cycles
let (address, extra) = self.address_abx(bus);
self.sta(bus, (address, extra), 4)
self.sta(bus, (address, extra), 5)
}
fn sta_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x99, 3 bytes, 4 cycles
fn sta_aby(&mut self, bus: &mut impl Bus) -> u8 { // 0x99, 3 bytes, 4 + 1 cycles
let (address, extra) = self.address_aby(bus);
self.sta(bus, (address, extra), 4)
self.sta(bus, (address, extra), 5)
}
fn sta_izx(&mut self, bus: &mut impl Bus) -> u8 { // 0x81, 2 bytes, 6 cycles
@@ -645,9 +701,9 @@ impl Cpu {
self.sta(bus, (address, extra), 6)
}
fn sta_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x91, 2 bytes, 5 cycles
fn sta_izy(&mut self, bus: &mut impl Bus) -> u8 { // 0x91, 2 bytes, 6 cycles
let (address, extra) = self.address_izy(bus);
self.sta(bus, (address, extra), 5)
self.sta(bus, (address, extra), 6)
}
// STX variants
@@ -1061,6 +1117,186 @@ impl Cpu {
2
}
// ASL variants
fn asl_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x0A, 1 byte, 2 cycles
self.a = self.shift_asl(self.a);
2
}
fn asl_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x06, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
5
}
fn asl_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x16, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
6
}
fn asl_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x0E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
6
}
fn asl_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x1E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_asl(self.read(bus, address));
self.write(bus, address, result);
7
}
// LSR variants
fn lsr_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x4A, 1 byte, 2 cycles
self.a = self.shift_lsr(self.a);
2
}
fn lsr_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x46, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
5
}
fn lsr_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x56, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
6
}
fn lsr_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x4E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
6
}
fn lsr_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x5E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_lsr(self.read(bus, address));
self.write(bus, address, result);
7
}
// ROL variants
fn rol_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x2A, 1 byte, 2 cycles
self.a = self.shift_rol(self.a);
2
}
fn rol_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x26, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
5
}
fn rol_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x36, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
6
}
fn rol_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x2E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
6
}
fn rol_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x3E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_rol(self.read(bus, address));
self.write(bus, address, result);
7
}
// ROR variants
fn ror_acc(&mut self, _bus: &mut impl Bus) -> u8 { // 0x6A, 1 byte, 2 cycles
self.a = self.shift_ror(self.a);
2
}
fn ror_zp(&mut self, bus: &mut impl Bus) -> u8 { // 0x66, 2 bytes, 5 cycles
let (address, _extra) = self.address_zp(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
5
}
fn ror_zpx(&mut self, bus: &mut impl Bus) -> u8 { // 0x76, 2 bytes, 6 cycles
let (address, _extra) = self.address_zpx(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
6
}
fn ror_abs(&mut self, bus: &mut impl Bus) -> u8 { // 0x6E, 3 bytes, 6 cycles
let (address, _extra) = self.address_abs(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
6
}
fn ror_abx(&mut self, bus: &mut impl Bus) -> u8 { // 0x7E, 3 bytes, 7 cycles
let (address, _extra) = self.address_abx(bus);
let result = self.shift_ror(self.read(bus, address));
self.write(bus, address, result);
7
}
// Transfers (all 1 byte, 2 cycles) TAX/TXx family
fn tax_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xAA: A -> X
self.x = self.a;
self.set_zp_flags(self.x);
2
}
fn tay_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xA8: A -> Y
self.y = self.a;
self.set_zp_flags(self.y);
2
}
fn txa_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x8A: X -> A
self.a = self.x;
self.set_zp_flags(self.a);
2
}
fn tya_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x98: Y -> A
self.a = self.y;
self.set_zp_flags(self.a);
2
}
fn tsx_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0xBA: SP -> X
self.x = self.sp;
self.set_zp_flags(self.x);
2
}
fn txs_imp(&mut self, _bus: &mut impl Bus) -> u8 { // 0x9A: X -> SP, NO flags
self.sp = self.x;
2
}
// JMP indirect
fn jmp_ind(&mut self, bus: &mut impl Bus) -> u8 { // 0x6C, 3 bytes, 5 cycles
let (addr, _) = self.address_ind(bus);
self.pc = addr;
5
}
// 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 bcc_rel(&mut self, bus: &mut impl Bus) -> u8 { self.branch(bus, !self.flag(FLAG_CARRY)) } // 0x90