diff --git a/Cargo.toml b/Cargo.toml index f423fc3..f89063d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,4 +4,5 @@ version = "0.1.0" edition = "2021" [dependencies] +bitflags = "2.6.0" lazy_static = "1.5.0" diff --git a/src/cpu.rs b/src/cpu.rs index 01ca427..0fcddbb 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -1,6 +1,38 @@ use crate::opcodes; use std::collections::HashMap; +bitflags! { + #[derive(Clone)] + /// # Status Register (P) http://wiki.nesdev.com/w/index.php/Status_flags + /// + /// 7 6 5 4 3 2 1 0 + /// N V _ B D I Z C + /// | | | | | | +--- Carry Flag + /// | | | | | +----- Zero Flag + /// | | | | +------- Interrupt Disable + /// | | | +--------- Decimal Mode (not used on NES) + /// | | +----------- Break Command + /// | +--------------- Overflow Flag + /// +----------------- Negative Flag + /// + pub struct CpuFlags: u8 { + const CARRY = 0b00000001; + const ZERO = 0b00000010; + const INTERRUPT_DISABLE = 0b00000100; + const DECIMAL_MODE = 0b00001000; + const BREAK = 0b00010000; + const BREAK2 = 0b00100000; + const OVERFLOW = 0b01000000; + const NEGATIV = 0b10000000; + } +} + +// stack at page 2: 0x0100 - 0x01ff +const STACK: u16 = 0x0100; + +// 0x01ff is for first function call +const STACK_RESET: u8 = 0xFD; + /// The `CPU` struct represents the central processing unit of the NES emulator. /// It contains the following fields: /// * `register_a`: The accumulator register, used for arithmetic operations. @@ -11,8 +43,9 @@ pub struct CPU { pub register_a: u8, pub register_x: u8, pub register_y: u8, - pub status: u8, + pub status: CpuFlags, pub program_counter: u16, + pub stack_pointer: u8, memory: [u8; 0xFFFF], } @@ -72,12 +105,18 @@ impl CPU { register_a: 0, register_x: 0, register_y: 0, - status: 0, + status: CpuFlags::from_bits_truncate(0b0010_0100), program_counter: 0, + stack_pointer: STACK_RESET, memory: [0; 0xFFFF], } } + fn set_register_a(&mut self, value: u8) { + self.register_a = value; + self.update_zero_and_negative_flags(self.register_a); + } + pub fn load_and_run(&mut self, program: Vec) { self.load(program); self.reset(); @@ -100,6 +139,7 @@ impl CPU { let opcode = opcodes .get(&code) .unwrap_or_else(|| panic!("opcode {:x} is not supported", code)); + match code { 0xa9 | 0xa5 | 0xb5 | 0xad | 0xbd | 0xb9 | 0xa1 | 0xb1 => { self.lda(&opcode.mode); @@ -113,6 +153,274 @@ impl CPU { 0xe8 => self.inx(), 0x00 => return, + /* CLD */ 0xd8 => self.status.remove(CpuFlags::DECIMAL_MODE), + + /* CLI */ 0x58 => self.status.remove(CpuFlags::INTERRUPT_DISABLE), + + /* CLV */ 0xb8 => self.status.remove(CpuFlags::OVERFLOW), + + /* CLC */ 0x18 => self.clear_carry_flag(), + + /* SEC */ 0x38 => self.set_carry_flag(), + + /* SEI */ 0x78 => self.status.insert(CpuFlags::INTERRUPT_DISABLE), + + /* SED */ 0xf8 => self.status.insert(CpuFlags::DECIMAL_MODE), + + /* PHA */ 0x48 => self.stack_push(self.register_a), + + /* PLA */ + 0x68 => { + self.pla(); + } + + /* PHP */ + 0x08 => { + self.php(); + } + + /* PLP */ + 0x28 => { + self.plp(); + } + + /* ADC */ + 0x69 | 0x65 | 0x75 | 0x6d | 0x7d | 0x79 | 0x61 | 0x71 => { + self.adc(&opcode.mode); + } + + /* SBC */ + 0xe9 | 0xe5 | 0xf5 | 0xed | 0xfd | 0xf9 | 0xe1 | 0xf1 => { + self.sbc(&opcode.mode); + } + + /* AND */ + 0x29 | 0x25 | 0x35 | 0x2d | 0x3d | 0x39 | 0x21 | 0x31 => { + self.and(&opcode.mode); + } + + /* EOR */ + 0x49 | 0x45 | 0x55 | 0x4d | 0x5d | 0x59 | 0x41 | 0x51 => { + self.eor(&opcode.mode); + } + + /* ORA */ + 0x09 | 0x05 | 0x15 | 0x0d | 0x1d | 0x19 | 0x01 | 0x11 => { + self.ora(&opcode.mode); + } + + /* LSR */ 0x4a => self.lsr_accumulator(), + + /* LSR */ + 0x46 | 0x56 | 0x4e | 0x5e => { + self.lsr(&opcode.mode); + } + + /*ASL*/ 0x0a => self.asl_accumulator(), + + /* ASL */ + 0x06 | 0x16 | 0x0e | 0x1e => { + self.asl(&opcode.mode); + } + + /*ROL*/ 0x2a => self.rol_accumulator(), + + /* ROL */ + 0x26 | 0x36 | 0x2e | 0x3e => { + self.rol(&opcode.mode); + } + + /* ROR */ 0x6a => self.ror_accumulator(), + + /* ROR */ + 0x66 | 0x76 | 0x6e | 0x7e => { + self.ror(&opcode.mode); + } + + /* INC */ + 0xe6 | 0xf6 | 0xee | 0xfe => { + self.inc(&opcode.mode); + } + + /* INY */ + 0xc8 => self.iny(), + + /* DEC */ + 0xc6 | 0xd6 | 0xce | 0xde => { + self.dec(&opcode.mode); + } + + /* DEX */ + 0xca => { + self.dex(); + } + + /* DEY */ + 0x88 => { + self.dey(); + } + + /* CMP */ + 0xc9 | 0xc5 | 0xd5 | 0xcd | 0xdd | 0xd9 | 0xc1 | 0xd1 => { + self.compare(&opcode.mode, self.register_a); + } + + /* CPY */ + 0xc0 | 0xc4 | 0xcc => { + self.compare(&opcode.mode, self.register_y); + } + + /* CPX */ + 0xe0 | 0xe4 | 0xec => self.compare(&opcode.mode, self.register_x), + + /* JMP Absolute */ + 0x4c => { + let mem_address = self.mem_read_u16(self.program_counter); + self.program_counter = mem_address; + } + + /* JMP Indirect */ + 0x6c => { + let mem_address = self.mem_read_u16(self.program_counter); + // let indirect_ref = self.mem_read_u16(mem_address); + //6502 bug mode with with page boundary: + // if address $3000 contains $40, $30FF contains $80, and $3100 contains $50, + // the result of JMP ($30FF) will be a transfer of control to $4080 rather than $5080 as you intended + // i.e. the 6502 took the low byte of the address from $30FF and the high byte from $3000 + + let indirect_ref = if mem_address & 0x00FF == 0x00FF { + let lo = self.mem_read(mem_address); + let hi = self.mem_read(mem_address & 0xFF00); + (hi as u16) << 8 | (lo as u16) + } else { + self.mem_read_u16(mem_address) + }; + + self.program_counter = indirect_ref; + } + + /* JSR */ + 0x20 => { + self.stack_push_u16(self.program_counter + 2 - 1); + let target_address = self.mem_read_u16(self.program_counter); + self.program_counter = target_address + } + + /* RTS */ + 0x60 => { + self.program_counter = self.stack_pop_u16() + 1; + } + + /* RTI */ + 0x40 => { + self.status = CpuFlags::from_bits_truncate(self.stack_pop()); + self.status.remove(CpuFlags::BREAK); + self.status.insert(CpuFlags::BREAK2); + + self.program_counter = self.stack_pop_u16(); + } + + /* BNE */ + 0xd0 => { + self.branch(!self.status.contains(CpuFlags::ZERO)); + } + + /* BVS */ + 0x70 => { + self.branch(self.status.contains(CpuFlags::OVERFLOW)); + } + + /* BVC */ + 0x50 => { + self.branch(!self.status.contains(CpuFlags::OVERFLOW)); + } + + /* BPL */ + 0x10 => { + self.branch(!self.status.contains(CpuFlags::NEGATIV)); + } + + /* BMI */ + 0x30 => { + self.branch(self.status.contains(CpuFlags::NEGATIV)); + } + + /* BEQ */ + 0xf0 => { + self.branch(self.status.contains(CpuFlags::ZERO)); + } + + /* BCS */ + 0xb0 => { + self.branch(self.status.contains(CpuFlags::CARRY)); + } + + /* BCC */ + 0x90 => { + self.branch(!self.status.contains(CpuFlags::CARRY)); + } + + /* BIT */ + 0x24 | 0x2c => { + self.bit(&opcode.mode); + } + + /* STX */ + 0x86 | 0x96 | 0x8e => { + let addr = self.get_operand_address(&opcode.mode); + self.mem_write(addr, self.register_x); + } + + /* STY */ + 0x84 | 0x94 | 0x8c => { + let addr = self.get_operand_address(&opcode.mode); + self.mem_write(addr, self.register_y); + } + + /* LDX */ + 0xa2 | 0xa6 | 0xb6 | 0xae | 0xbe => { + self.ldx(&opcode.mode); + } + + /* LDY */ + 0xa0 | 0xa4 | 0xb4 | 0xac | 0xbc => { + self.ldy(&opcode.mode); + } + + /* NOP */ + 0xea => { + //do nothing + } + + /* TAY */ + 0xa8 => { + self.register_y = self.register_a; + self.update_zero_and_negative_flags(self.register_y); + } + + /* TSX */ + 0xba => { + self.register_x = self.stack_pointer; + self.update_zero_and_negative_flags(self.register_x); + } + + /* TXA */ + 0x8a => { + self.register_a = self.register_x; + self.update_zero_and_negative_flags(self.register_a); + } + + /* TXS */ + 0x9a => { + self.stack_pointer = self.register_x; + } + + /* TYA */ + 0x98 => { + self.register_a = self.register_y; + self.update_zero_and_negative_flags(self.register_a); + } + _ => todo!(), } @@ -126,33 +434,41 @@ impl CPU { self.register_a = 0; self.register_x = 0; self.register_y = 0; - self.status = 0; + self.stack_pointer = STACK_RESET; + self.status = CpuFlags::from_bits_truncate(0b0010_0100); self.program_counter = self.mem_read_u16(0xFFFC); } fn get_operand_address(&self, mode: &AddressingMode) -> u16 { match mode { AddressingMode::Immediate => self.program_counter, + AddressingMode::ZeroPage => self.mem_read(self.program_counter) as u16, + AddressingMode::Absolute => self.mem_read_u16(self.program_counter), + AddressingMode::ZeroPage_X => { let pos = self.mem_read(self.program_counter); let addr = pos.wrapping_add(self.register_x); addr as u16 } + AddressingMode::ZeroPage_Y => { let pos = self.mem_read(self.program_counter); let addr = pos.wrapping_add(self.register_y); addr as u16 } + AddressingMode::Absolute_X => { let base = self.mem_read_u16(self.program_counter); base.wrapping_add(self.register_x as u16) } + AddressingMode::Absolute_Y => { let base = self.mem_read_u16(self.program_counter); base.wrapping_add(self.register_y as u16) } + AddressingMode::Indirect_X => { let base = self.mem_read(self.program_counter); @@ -161,6 +477,7 @@ impl CPU { let hi = self.mem_read(ptr.wrapping_add(1) as u16); (hi as u16) << 8 | (lo as u16) } + AddressingMode::Indirect_Y => { let base = self.mem_read(self.program_counter); @@ -169,6 +486,7 @@ impl CPU { let deref_base = (hi as u16) << 8 | (lo as u16); deref_base.wrapping_add(self.register_y as u16) } + AddressingMode::NoneAddressing => { panic!("mode {:?} is not supported", mode); } @@ -182,8 +500,23 @@ impl CPU { let value = self.mem_read(addr); - self.register_a = value; - self.update_zero_and_negative_flags(self.register_a); + self.set_register_a(value); + } + + /// LDX + fn ldx(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let value = self.mem_read(addr); + self.register_x = value; + self.update_zero_and_negative_flags(self.register_x); + } + + /// LDY + fn ldy(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let value = self.mem_read(addr); + self.register_y = value; + self.update_zero_and_negative_flags(self.register_y); } /// STA - Store Accumulator @@ -193,6 +526,27 @@ impl CPU { self.mem_write(addr, self.register_a); } + /// AND - Logical AND + fn and(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let value = self.mem_read(addr); + self.set_register_a(value & self.register_a); + } + + /// EOR - Exclusive OR + fn eor(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let value = self.mem_read(addr); + self.set_register_a(value ^ self.register_a); + } + + /// ORA - Logical Inclusive OR + fn ora(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let value = self.mem_read(addr); + self.set_register_a(value | self.register_a); + } + /// TAX - Transfer Accumulator to X /// Copies the current contents of the accumulator into the X register and sets the zero and negative flags as appropriate. fn tax(&mut self) { @@ -207,17 +561,302 @@ impl CPU { self.update_zero_and_negative_flags(self.register_x); } + /// INY - Increment Y Register + fn iny(&mut self) { + self.register_y = self.register_y.wrapping_add(1); + self.update_zero_and_negative_flags(self.register_y); + } + + fn set_carry_flag(&mut self) { + self.status.insert(CpuFlags::CARRY); + } + + fn clear_carry_flag(&mut self) { + self.status.remove(CpuFlags::CARRY); + } + + fn add_to_register_a(&mut self, data: u8) { + let sum = self.register_a as u16 + + data as u16 + + (if self.status.contains(CpuFlags::CARRY) { + 1 + } else { + 0 + }) as u16; + + let carry = sum > 0xff; + + if carry { + self.status.insert(CpuFlags::CARRY); + } else { + self.status.remove(CpuFlags::CARRY); + } + + let result = sum as u8; + + if (data ^ result) & (result ^ self.register_a) & 0x80 != 0 { + self.status.insert(CpuFlags::OVERFLOW); + } else { + self.status.remove(CpuFlags::OVERFLOW) + } + + self.set_register_a(result); + } + + /// SBC + fn sbc(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let data = self.mem_read(addr); + self.add_to_register_a(((data as i8).wrapping_neg().wrapping_sub(1)) as u8); + } + + /// ADC - Add with Carry + fn adc(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let value = self.mem_read(addr); + self.add_to_register_a(value); + } + + fn stack_pop(&mut self) -> u8 { + self.stack_pointer = self.stack_pointer.wrapping_add(1); + self.mem_read((STACK) + self.stack_pointer as u16) + } + + fn stack_push(&mut self, data: u8) { + self.mem_write((STACK) + self.stack_pointer as u16, data); + self.stack_pointer = self.stack_pointer.wrapping_sub(1) + } + + fn stack_push_u16(&mut self, data: u16) { + let hi = (data >> 8) as u8; + let lo = (data & 0xff) as u8; + self.stack_push(hi); + self.stack_push(lo); + } + + fn stack_pop_u16(&mut self) -> u16 { + let lo = self.stack_pop() as u16; + let hi = self.stack_pop() as u16; + + hi << 8 | lo + } + + fn asl_accumulator(&mut self) { + let mut data = self.register_a; + if data >> 7 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data <<= 1; + self.set_register_a(data) + } + + fn asl(&mut self, mode: &AddressingMode) -> u8 { + let addr = self.get_operand_address(mode); + let mut data = self.mem_read(addr); + if data >> 7 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data <<= 1; + self.mem_write(addr, data); + self.update_zero_and_negative_flags(data); + data + } + + fn lsr_accumulator(&mut self) { + let mut data = self.register_a; + if data & 1 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data >>= 1; + self.set_register_a(data) + } + + fn lsr(&mut self, mode: &AddressingMode) -> u8 { + let addr = self.get_operand_address(mode); + let mut data = self.mem_read(addr); + if data & 1 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data >>= 1; + self.mem_write(addr, data); + self.update_zero_and_negative_flags(data); + data + } + + fn rol(&mut self, mode: &AddressingMode) -> u8 { + let addr = self.get_operand_address(mode); + let mut data = self.mem_read(addr); + let old_carry = self.status.contains(CpuFlags::CARRY); + + if data >> 7 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data <<= 1; + if old_carry { + data |= 1; + } + self.mem_write(addr, data); + self.update_zero_and_negative_flags(data); + data + } + + fn rol_accumulator(&mut self) { + let mut data = self.register_a; + let old_carry = self.status.contains(CpuFlags::CARRY); + + if data >> 7 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data <<= 1; + if old_carry { + data |= 1; + } + self.set_register_a(data); + } + + fn ror(&mut self, mode: &AddressingMode) -> u8 { + let addr = self.get_operand_address(mode); + let mut data = self.mem_read(addr); + let old_carry = self.status.contains(CpuFlags::CARRY); + + if data & 1 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data >>= 1; + if old_carry { + data |= 0b10000000; + } + self.mem_write(addr, data); + self.update_zero_and_negative_flags(data); + data + } + + fn ror_accumulator(&mut self) { + let mut data = self.register_a; + let old_carry = self.status.contains(CpuFlags::CARRY); + + if data & 1 == 1 { + self.set_carry_flag(); + } else { + self.clear_carry_flag(); + } + data >>= 1; + if old_carry { + data |= 0b10000000; + } + self.set_register_a(data); + } + + fn inc(&mut self, mode: &AddressingMode) -> u8 { + let addr = self.get_operand_address(mode); + let mut data = self.mem_read(addr); + data = data.wrapping_add(1); + self.mem_write(addr, data); + self.update_zero_and_negative_flags(data); + data + } + + fn dey(&mut self) { + self.register_y = self.register_y.wrapping_sub(1); + self.update_zero_and_negative_flags(self.register_y); + } + + fn dex(&mut self) { + self.register_x = self.register_x.wrapping_sub(1); + self.update_zero_and_negative_flags(self.register_x); + } + + fn dec(&mut self, mode: &AddressingMode) -> u8 { + let addr = self.get_operand_address(mode); + let mut data = self.mem_read(addr); + data = data.wrapping_sub(1); + self.mem_write(addr, data); + self.update_zero_and_negative_flags(data); + data + } + + fn pla(&mut self) { + let data = self.stack_pop(); + self.set_register_a(data); + } + + fn plp(&mut self) { + self.status = CpuFlags::from_bits_truncate(self.stack_pop()); + self.status.remove(CpuFlags::BREAK); + self.status.insert(CpuFlags::BREAK2); + } + + fn php(&mut self) { + //http://wiki.nesdev.com/w/index.php/CPU_status_flag_behavior + let mut flags = self.status.clone(); + flags.insert(CpuFlags::BREAK); + flags.insert(CpuFlags::BREAK2); + self.stack_push(flags.bits()); + } + + fn bit(&mut self, mode: &AddressingMode) { + let addr = self.get_operand_address(mode); + let data = self.mem_read(addr); + let and = self.register_a & data; + if and == 0 { + self.status.insert(CpuFlags::ZERO); + } else { + self.status.remove(CpuFlags::ZERO); + } + + self.status.set(CpuFlags::NEGATIV, data & 0b10000000 > 0); + self.status.set(CpuFlags::OVERFLOW, data & 0b01000000 > 0); + } + + fn compare(&mut self, mode: &AddressingMode, compare_with: u8) { + let addr = self.get_operand_address(mode); + let data = self.mem_read(addr); + if data <= compare_with { + self.status.insert(CpuFlags::CARRY); + } else { + self.status.remove(CpuFlags::CARRY); + } + + self.update_zero_and_negative_flags(compare_with.wrapping_sub(data)); + } + + fn branch(&mut self, condition: bool) { + if condition { + let jump: i8 = self.mem_read(self.program_counter) as i8; + let jump_addr = self + .program_counter + .wrapping_add(1) + .wrapping_add(jump as u16); + + self.program_counter = jump_addr; + } + } + fn update_zero_and_negative_flags(&mut self, result: u8) { if result == 0 { - self.status |= 0b0000_0010; + self.status.insert(CpuFlags::ZERO); } else { - self.status &= 0b1111_1101; + self.status.remove(CpuFlags::ZERO); } if result & 0b1000_0000 != 0 { - self.status |= 0b1000_0000; + self.status.insert(CpuFlags::NEGATIV); } else { - self.status &= 0b0111_1111; + self.status.remove(CpuFlags::NEGATIV); } } } @@ -229,26 +868,19 @@ mod test { #[test] fn test_0xa9_lda_immediate_load_data() { let mut cpu = CPU::new(); - cpu.reset(); cpu.load_and_run(vec![0xa9, 0x05, 0x00]); assert_eq!(cpu.register_a, 5); - assert_eq!(cpu.status & 0b0000_0010, 0); - assert_eq!(cpu.status & 0b1000_0000, 0); - } - - #[test] - fn test_0xa9_lda_zero_flag() { - let mut cpu = CPU::new(); - cpu.reset(); - cpu.load_and_run(vec![0xa9, 0x00, 0x00]); - assert_eq!(cpu.status & 0b0000_0010, 0b10); + assert!(cpu.status.bits() & 0b0000_0010 == 0b00); + assert!(cpu.status.bits() & 0b1000_0000 == 0); } #[test] fn test_0xaa_tax_move_a_to_x() { let mut cpu = CPU::new(); + cpu.load(vec![0xaa, 0x00]); cpu.reset(); - cpu.load_and_run(vec![0xa9, 0x0A, 0xaa, 0x00]); + cpu.register_a = 10; + cpu.run(); assert_eq!(cpu.register_x, 10) } @@ -256,7 +888,6 @@ mod test { #[test] fn test_5_ops_working_together() { let mut cpu = CPU::new(); - cpu.reset(); cpu.load_and_run(vec![0xa9, 0xc0, 0xaa, 0xe8, 0x00]); assert_eq!(cpu.register_x, 0xc1) @@ -265,8 +896,10 @@ mod test { #[test] fn test_inx_overflow() { let mut cpu = CPU::new(); + cpu.load(vec![0xe8, 0xe8, 0x00]); cpu.reset(); - cpu.load_and_run(vec![0xa9, 0xff, 0xaa, 0xe8, 0xe8, 0x00]); + cpu.register_x = 0xff; + cpu.run(); assert_eq!(cpu.register_x, 1) } @@ -274,8 +907,8 @@ mod test { #[test] fn test_lda_from_memory() { let mut cpu = CPU::new(); - cpu.reset(); cpu.mem_write(0x10, 0x55); + cpu.load_and_run(vec![0xa5, 0x10, 0x00]); assert_eq!(cpu.register_a, 0x55); diff --git a/src/main.rs b/src/main.rs index 8bdbbf6..5c37f2d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -4,6 +4,9 @@ pub mod opcodes; #[macro_use] extern crate lazy_static; +#[macro_use] +extern crate bitflags; + fn main() { println!("Hello, world!"); } diff --git a/src/opcodes.rs b/src/opcodes.rs index 7deae37..b167549 100644 --- a/src/opcodes.rs +++ b/src/opcodes.rs @@ -23,19 +23,231 @@ impl OpCode { lazy_static! { pub static ref CPU_OPS_CODES: Vec = vec![ + + // ADC - Add with Carry + OpCode::new(0x69, "ADC", 2, 2, AddressingMode::Immediate), + OpCode::new(0x65, "ADC", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0x75, "ADC", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0x6D, "ADC", 3, 4, AddressingMode::Absolute), + OpCode::new(0x7D, "ADC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + OpCode::new(0x79, "ADC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + OpCode::new(0x61, "ADC", 2, 6, AddressingMode::Indirect_X), + OpCode::new(0x71, "ADC", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + + // AND + OpCode::new(0x29, "AND", 2, 2, AddressingMode::Immediate), + OpCode::new(0x25, "AND", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0x35, "AND", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0x2D, "AND", 3, 4, AddressingMode::Absolute), + OpCode::new(0x3D, "AND", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + OpCode::new(0x39, "AND", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + OpCode::new(0x21, "AND", 2, 6, AddressingMode::Indirect_X), + OpCode::new(0x31, "AND", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + + // ASL + OpCode::new(0x0A, "ASL", 1, 2, AddressingMode::NoneAddressing), + OpCode::new(0x06, "ASL", 2, 5, AddressingMode::ZeroPage), + OpCode::new(0x16, "ASL", 2, 6, AddressingMode::ZeroPage_X), + OpCode::new(0x0E, "ASL", 3, 6, AddressingMode::Absolute), + OpCode::new(0x1E, "ASL", 3, 7, AddressingMode::Absolute_X), + + // BCC + OpCode::new(0x90, "BCC", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + + // BCS + OpCode::new(0xB0, "BCS", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + + // BEQ + OpCode::new(0xF0, "BEQ", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + + // BIT + OpCode::new(0x24, "BIT", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0x2C, "BIT", 3, 4, AddressingMode::NoneAddressing), + + // BMI + OpCode::new(0x30, "BMI", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + + // BNE + OpCode::new(0xD0, "BNE", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + + // BPL + OpCode::new(0x10, "BPL", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + + // BRK OpCode::new(0x00, "BRK", 1, 7, AddressingMode::NoneAddressing), - OpCode::new(0xaa, "TAX", 1, 2, AddressingMode::NoneAddressing), - OpCode::new(0xe8, "INX", 1, 2, AddressingMode::NoneAddressing), - OpCode::new(0xa9, "LDA", 2, 2, AddressingMode::Immediate), - OpCode::new(0xa5, "LDA", 2, 3, AddressingMode::ZeroPage), - OpCode::new(0xb5, "LDA", 2, 4, AddressingMode::ZeroPage_X), - OpCode::new(0xad, "LDA", 3, 4, AddressingMode::Absolute), - OpCode::new(0xbd, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), - OpCode::new(0xb9, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), - OpCode::new(0xa1, "LDA", 2, 6, AddressingMode::Indirect_X), - OpCode::new(0xb1, "LDA", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + // BVC + OpCode::new(0x50, "BVC", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + // BVS + OpCode::new(0x70, "BVS", 2, 2/*+1 if branch succeeds, +2 if to a new page*/, AddressingMode::NoneAddressing), + + // CLC + OpCode::new(0x18, "CLC", 1, 2, AddressingMode::NoneAddressing), + + // CLD + OpCode::new(0xD8, "CLD", 1, 2, AddressingMode::NoneAddressing), + + // CLI + OpCode::new(0x58, "CLI", 1, 2, AddressingMode::NoneAddressing), + + // CLV + OpCode::new(0xB8, "CLV", 1, 2, AddressingMode::NoneAddressing), + + // CMP + OpCode::new(0xC9, "CMP", 2, 2, AddressingMode::Immediate), + OpCode::new(0xC5, "CMP", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0xD5, "CMP", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0xCD, "CMP", 3, 4, AddressingMode::Absolute), + OpCode::new(0xDD, "CMP", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + OpCode::new(0xD9, "CMP", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + OpCode::new(0xC1, "CMP", 2, 6, AddressingMode::Indirect_X), + OpCode::new(0xD1, "CMP", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + + // CPX + OpCode::new(0xE0, "CPX", 2, 2, AddressingMode::Immediate), + OpCode::new(0xE4, "CPX", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0xEC, "CPX", 3, 4, AddressingMode::Absolute), + + // CPY + OpCode::new(0xC0, "CPY", 2, 2, AddressingMode::Immediate), + OpCode::new(0xC4, "CPY", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0xCC, "CPY", 3, 4, AddressingMode::Absolute), + + // DEC + OpCode::new(0xC6, "DEC", 2, 5, AddressingMode::ZeroPage), + OpCode::new(0xD6, "DEC", 2, 6, AddressingMode::ZeroPage_X), + OpCode::new(0xCE, "DEC", 3, 6, AddressingMode::Absolute), + OpCode::new(0xDE, "DEC", 3, 7, AddressingMode::Absolute_X), + + // DEX + OpCode::new(0xCA, "DEX", 1, 2, AddressingMode::NoneAddressing), + + // EOR + OpCode::new(0x49, "EOR", 2, 2, AddressingMode::Immediate), + OpCode::new(0x45, "EOR", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0x55, "EOR", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0x4D, "EOR", 3, 4, AddressingMode::Absolute), + OpCode::new(0x5D, "EOR", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + OpCode::new(0x59, "EOR", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + OpCode::new(0x41, "EOR", 2, 6, AddressingMode::Indirect_X), + OpCode::new(0x51, "EOR", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + + // INC + OpCode::new(0xE6, "INC", 2, 5, AddressingMode::ZeroPage), + OpCode::new(0xF6, "INC", 2, 6, AddressingMode::ZeroPage_X), + OpCode::new(0xEE, "INC", 3, 6, AddressingMode::Absolute), + OpCode::new(0xFE, "INC", 3, 7, AddressingMode::Absolute_X), + + // INX + OpCode::new(0xE8, "INX", 1, 2, AddressingMode::NoneAddressing), + + // INY + OpCode::new(0xC8, "INY", 1, 2, AddressingMode::NoneAddressing), + + // JMP + OpCode::new(0x4C, "JMP", 3, 3, AddressingMode::Absolute), + OpCode::new(0x6C, "JMP", 3, 5, AddressingMode::NoneAddressing), + + // JSR + OpCode::new(0x20, "JSR", 3, 6, AddressingMode::Absolute), + + // LDA + OpCode::new(0xA9, "LDA", 2, 2, AddressingMode::Immediate), + OpCode::new(0xA5, "LDA", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0xB5, "LDA", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0xAD, "LDA", 3, 4, AddressingMode::Absolute), + OpCode::new(0xBD, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + OpCode::new(0xB9, "LDA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + OpCode::new(0xA1, "LDA", 2, 6, AddressingMode::Indirect_X), + OpCode::new(0xB1, "LDA", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + + // LDX + OpCode::new(0xA2, "LDX", 2, 2, AddressingMode::Immediate), + OpCode::new(0xA6, "LDX", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0xB6, "LDX", 2, 4, AddressingMode::ZeroPage_Y), + OpCode::new(0xAE, "LDX", 3, 4, AddressingMode::Absolute), + OpCode::new(0xBE, "LDX", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + + // LDY + OpCode::new(0xA0, "LDY", 2, 2, AddressingMode::Immediate), + OpCode::new(0xA4, "LDY", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0xB4, "LDY", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0xAC, "LDY", 3, 4, AddressingMode::Absolute), + OpCode::new(0xBC, "LDY", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + + // LSR + OpCode::new(0x4A, "LSR", 1, 2, AddressingMode::NoneAddressing), + OpCode::new(0x46, "LSR", 2, 5, AddressingMode::ZeroPage), + OpCode::new(0x56, "LSR", 2, 6, AddressingMode::ZeroPage_X), + OpCode::new(0x4E, "LSR", 3, 6, AddressingMode::Absolute), + OpCode::new(0x5E, "LSR", 3, 7, AddressingMode::Absolute_X), + + // NOP + OpCode::new(0xEA, "NOP", 1, 2, AddressingMode::NoneAddressing), + + // ORA + OpCode::new(0x09, "ORA", 2, 2, AddressingMode::Immediate), + OpCode::new(0x05, "ORA", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0x15, "ORA", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0x0D, "ORA", 3, 4, AddressingMode::Absolute), + OpCode::new(0x1D, "ORA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + OpCode::new(0x19, "ORA", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + OpCode::new(0x01, "ORA", 2, 6, AddressingMode::Indirect_X), + OpCode::new(0x11, "ORA", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + + // PHA + OpCode::new(0x48, "PHA", 1, 3, AddressingMode::NoneAddressing), + + // PHP + OpCode::new(0x08, "PHP", 1, 3, AddressingMode::NoneAddressing), + + // PLA + OpCode::new(0x68, "PLA", 1, 4, AddressingMode::NoneAddressing), + + // PLP + OpCode::new(0x28, "PLP", 1, 4, AddressingMode::NoneAddressing), + + // ROL + OpCode::new(0x2A, "ROL", 1, 2, AddressingMode::NoneAddressing), + OpCode::new(0x26, "ROL", 2, 5, AddressingMode::ZeroPage), + OpCode::new(0x36, "ROL", 2, 6, AddressingMode::ZeroPage_X), + OpCode::new(0x2E, "ROL", 3, 6, AddressingMode::Absolute), + OpCode::new(0x3E, "ROL", 3, 7, AddressingMode::Absolute_X), + + // ROR + OpCode::new(0x6A, "ROR", 1, 2, AddressingMode::NoneAddressing), + OpCode::new(0x66, "ROR", 2, 5, AddressingMode::ZeroPage), + OpCode::new(0x76, "ROR", 2, 6, AddressingMode::ZeroPage_X), + OpCode::new(0x6E, "ROR", 3, 6, AddressingMode::Absolute), + OpCode::new(0x7E, "ROR", 3, 7, AddressingMode::Absolute_X), + + // RTI + OpCode::new(0x40, "RTI", 1, 6, AddressingMode::NoneAddressing), + + // RTS + OpCode::new(0x60, "RTS", 1, 6, AddressingMode::NoneAddressing), + + // SBC + OpCode::new(0xE9, "SBC", 2, 2, AddressingMode::Immediate), + OpCode::new(0xE5, "SBC", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0xF5, "SBC", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0xED, "SBC", 3, 4, AddressingMode::Absolute), + OpCode::new(0xFD, "SBC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_X), + OpCode::new(0xF9, "SBC", 3, 4/*+1 if page crossed*/, AddressingMode::Absolute_Y), + OpCode::new(0xE1, "SBC", 2, 6, AddressingMode::Indirect_X), + OpCode::new(0xF1, "SBC", 2, 5/*+1 if page crossed*/, AddressingMode::Indirect_Y), + + // SEC + OpCode::new(0x38, "SEC", 1, 2, AddressingMode::NoneAddressing), + + // SED + OpCode::new(0xF8, "SED", 1, 2, AddressingMode::NoneAddressing), + + // SEI + OpCode::new(0x78, "SEI", 1, 2, AddressingMode::NoneAddressing), + + // STA OpCode::new(0x85, "STA", 2, 3, AddressingMode::ZeroPage), OpCode::new(0x95, "STA", 2, 4, AddressingMode::ZeroPage_X), OpCode::new(0x8d, "STA", 3, 4, AddressingMode::Absolute), @@ -44,6 +256,34 @@ lazy_static! { OpCode::new(0x81, "STA", 2, 6, AddressingMode::Indirect_X), OpCode::new(0x91, "STA", 2, 6, AddressingMode::Indirect_Y), + // STX + OpCode::new(0x86, "STX", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0x96, "STX", 2, 4, AddressingMode::ZeroPage_Y), + OpCode::new(0x8E, "STX", 3, 4, AddressingMode::Absolute), + + // STY + OpCode::new(0x84, "STY", 2, 3, AddressingMode::ZeroPage), + OpCode::new(0x94, "STY", 2, 4, AddressingMode::ZeroPage_X), + OpCode::new(0x8C, "STY", 3, 4, AddressingMode::Absolute), + + // TAX + OpCode::new(0xAA, "TAX", 1, 2, AddressingMode::NoneAddressing), + + // TAY + OpCode::new(0xA8, "TAY", 1, 2, AddressingMode::NoneAddressing), + + // TSX + OpCode::new(0xBA, "TSX", 1, 2, AddressingMode::NoneAddressing), + + // TXA + OpCode::new(0x8A, "TXA", 1, 2, AddressingMode::NoneAddressing), + + // TXS + OpCode::new(0x9A, "TXS", 1, 2, AddressingMode::NoneAddressing), + + // TYA + OpCode::new(0x98, "TYA", 1, 2, AddressingMode::NoneAddressing), + ];