From b94fde2979007c068498e23bf2912b274d268d1a Mon Sep 17 00:00:00 2001 From: august Date: Fri, 2 Feb 2024 13:44:15 -0500 Subject: [PATCH] Added cycle logic and instruction functions --- src/main.rs | 1445 +++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 1241 insertions(+), 204 deletions(-) diff --git a/src/main.rs b/src/main.rs index fd43716..044b7b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,6 +1,6 @@ #[allow(warnings)] use core::panic; -use std::collections::HashMap; +use std::{collections::HashMap, thread::sleep, time::Duration, u16}; type Byte = u8; type Word = u16; @@ -84,22 +84,43 @@ impl Mem { } fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> { - let mut contains = false; for area in self.areas.iter_mut() { if area.contains(address) { - contains = true; println!("Writing to area {label}", label = area.label); let translated_address = area.translate_address(address); area.data[translated_address as usize] = data; + return Ok(()); }; } - println!("{:?}", contains); - match contains { - true => return Ok(()), - false => Err(MemoryError::Unmapped), - } + Err(MemoryError::Unmapped) } } +#[derive(Debug)] +enum ExecutionError { + InvalidInstruction, + InterruptsDisabled, + StackOverflow, + IncompatibleAddrMode, + MemoryError(MemoryError), +} + +impl From for ExecutionError { + fn from(error: MemoryError) -> Self { + ExecutionError::MemoryError(error) + } +} + +#[derive(Clone, Copy)] +enum StatusFlag { + Negative = 0b1000_0000, + Overflow = 0b0100_0000, + Brk = 0b0011_0000, + BrkIrq = 0b0010_0000, + Decimal = 0b0000_1000, + IrqDisable = 0b0000_0100, + Zero = 0b000_0010, + Carry = 0b0000_0001, +} struct Cpu { a: Byte, // Accumulator Register @@ -111,15 +132,8 @@ struct Cpu { irq: bool, nmi: bool, memory: Mem, -} - -#[derive(Debug)] -enum ExecutionError { - InvalidInstruction, - InterruptsDisabled, - NoInterrupt, - StackOverflow, - MemoryError(MemoryError), + pending_cycles: usize, + cycle_count: usize, } impl Cpu { @@ -134,30 +148,22 @@ impl Cpu { irq: false, nmi: false, memory, + pending_cycles: 0, + cycle_count: 0, } } fn reset(&mut self) -> Result<(), ExecutionError> { - match self.read_word(0xFFFC) { - Ok(word) => match self.read(word) { - Ok(_byte) => Ok(self.pc = word), - Err(error) => Err(ExecutionError::MemoryError(error)), - }, - Err(error) => Err(ExecutionError::MemoryError(error)), - } + let reset_vector_pointer = self.read_word(0xFFFC)?; + self.pending_cycles = 8; + Ok(self.pc = reset_vector_pointer) } fn read(&self, address: Word) -> Result { self.memory.read(address) } fn read_word(&self, address: Word) -> Result { - let low_byte = self.read(address); - let high_byte = self.read(address + 0x1); - match high_byte { - Err(error) => Err(error), - Ok(high) => match low_byte { - Err(error) => Err(error), - Ok(low) => Ok((high as u16) << 8 | (low as u16)), - }, - } + let low_byte = self.read(address)?; + let high_byte = self.read(address + 0x1)?; + Ok((high_byte as u16) << 8 | (low_byte as u16)) } fn stack_addr(&self) -> Word { @@ -167,62 +173,149 @@ impl Cpu { fn push_stack(&mut self, data: Byte) -> Result<(), ExecutionError> { self.s -= 0x1; - match self.write(self.stack_addr(), data) { - Ok(()) => Ok(()), - Err(error) => Err(ExecutionError::MemoryError(error)), + Ok(self.write(self.stack_addr(), data)?) + } + + fn push_stack_word(&mut self, address: Word) -> Result<(), ExecutionError> { + self.s -= 0x1; + self.write(self.stack_addr(), address.to_le_bytes()[1])?; // Upper byte first + self.s -= 0x1; + self.write(self.stack_addr(), address.to_le_bytes()[0])?; // Lower byte second + Ok(()) + } + + fn pop_stack(&mut self) -> Result { + let byte = self.read(self.stack_addr())?; + self.s += 0x1; + Ok(byte) + } + + fn pop_stack_word(&mut self) -> Result { + let low_byte = self.pop_stack()?; + let high_byte = self.pop_stack()?; + let word = ((high_byte as Word) << 8) + low_byte as u16; + Ok(word) + } + + fn set_flag(&mut self, flag: StatusFlag, value: bool) { + self.p &= !(flag as Byte); + if value { + self.p |= flag as Byte } } - fn pop_stack(&self) -> Result { - match self.read(self.stack_addr()) { - Ok(byte) => Ok(byte), - Err(error) => Err(ExecutionError::MemoryError(error)), - } + fn get_flag(&self, flag: StatusFlag) -> bool { + (self.p & flag as Byte) > 0 + } + fn is_negative(&self, value: Byte) -> bool { + value & 0b1000_0000 == 0b1000_0000 } fn write(&mut self, address: Word, data: Byte) -> Result<(), MemoryError> { self.memory.write(address, data) } - fn execute(&self, opcode: Byte) -> Result<(), ExecutionError> { - let instruction: Instruction = get_instruction(opcode); - match instruction { - Instruction::Valid(valid_instruction) => Ok(valid_instruction.opcode.call()), - Instruction::Invalid => Err(ExecutionError::InvalidInstruction), + fn execute(&mut self) { + self.cycle(); + while self.pending_cycles != 0 { + self.cycle(); } } - fn handle_interrupt(&self) -> Result<(), ExecutionError> { - match self.p & 0b0000_0100 { - // Check that interrupts aren't disabled - 0b0000_0100 => Err(ExecutionError::InterruptsDisabled), - 0b0000_0000 => { - if self.irq { - let irq_vector = 0xFFFC; - match self.read_word(irq_vector) { - Err(error) => Err(ExecutionError::MemoryError(error)), - Ok(address) => { - let isr_address = address; - match self.read(isr_address) { - Ok(opcode) => self.execute(opcode), - Err(error) => Err(ExecutionError::MemoryError(error)), - } - } - } - } else { - Ok(()) - } - } - _ => Ok(()), - } + fn wait_for_interrupt(&self) { + unimplemented!() } - fn tick(&mut self) -> Result<(), ExecutionError> { - // `cycle` is a reserved word :/ - self.pc += 1; - match self.read(self.pc) { - Ok(instruction) => self.execute(instruction), - Err(error) => Err(ExecutionError::MemoryError(error)), + + //fn handle_interrupt(&mut self) -> Result<(), ExecutionError> { + // match self.get_flag(StatusFlag::IrqDisable) { + // // Check that interrupts aren't disabled + // true => Err(ExecutionError::InterruptsDisabled), + // false => { + // if self.irq { + // let irq_vector = 0xFFFC; + // match self.read_word(irq_vector) { + // Err(error) => Err(ExecutionError::MemoryError(error)), + // Ok(address) => { + // let isr_address = address; + // match self.read(isr_address) { + // Ok(opcode) => self.execute(opcode), + // Err(error) => Err(ExecutionError::MemoryError(error)), + // } + // } + // } + // } else { + // Ok(()) + // } + // } + // } + //} + fn stop(&mut self) { + unimplemented!() + } + fn cycle(&mut self) { + sleep(Duration::from_nanos(500)); + if self.pending_cycles == 0 { + if self.nmi || (self.irq && !self.get_flag(StatusFlag::IrqDisable)) { + self.push_stack_word(self.pc); + self.set_flag(StatusFlag::BrkIrq, true); + self.push_stack(self.p); + self.set_flag(StatusFlag::IrqDisable, true); + if self.nmi { + self.pc = match self.read_word(0xFFFA) { + Ok(word) => word, + Err(error) => panic!("{error:?}"), + }; + self.pending_cycles = 8; + } else { + self.pc = match self.read_word(0xFFFE) { + Ok(word) => word, + Err(error) => panic!("{error:?}"), + }; + self.pending_cycles = 7 + } + self.nmi = false; + self.irq = false; + } + } else { + let opcode = match self.read(self.pc) { + Ok(byte) => byte, + Err(error) => panic!("Something went wrong reading the program counter: {error:?}"), + }; + let instruction = get_instruction(opcode); + match instruction { + Instruction::Valid(valid_instruction) => { + println!("accumulator: {a:?}, x: {x:?}, y: {y:?}, program counter: {pc:?}, stack pointer: {s:?}, status register: {p:?}, irq pin: {irq:?}, nmi pin: {nmi:?}", a = self.a, x = self.x, y = self.y, pc = self.pc, s = self.s, p = self.p, irq = self.irq, nmi = self.nmi); + match valid_instruction.opcode.call(self) { + Ok(_) => { + self.pending_cycles += valid_instruction.cycles as usize; + } + Err(error) => match error { + ExecutionError::InvalidInstruction => { + panic!("An invalid instruction was called") + } + ExecutionError::StackOverflow => panic!("Stack overflowed!"), + ExecutionError::MemoryError(error) => match error { + MemoryError::Unmapped => panic!( + "Cpu tried to access memory that hasn't yet been mapped at {:?}", + self.pc + ), + MemoryError::NoDataAtAddress => panic!( + "Cpu tried to read from address {:?} but there was no data", + self.pc + ), + }, + ExecutionError::InterruptsDisabled => panic!("InterruptsDisabled"), + ExecutionError::IncompatibleAddrMode => { + panic!("An IncompatibleAddrMode") + } + }, + } + } + Instruction::Invalid => panic!("An invalid instruction was called"), + } } + self.pending_cycles -= 1; + self.cycle_count += 1; } } @@ -235,25 +328,26 @@ enum Instruction { #[derive(Clone)] struct ValidInstruction { opcode: Opcode, + cycles: Byte, } #[derive(Clone)] enum AddressingMode { AbsoluteA, // a - AbsoluteIndexedIndirect, // (a,x) (4) AbsoluteIndexedWithX, // a, x AbsoluteIndexedWithY, // a, y AbsoluteIndirect, // (a) + AbsoluteIndexedIndirect, // (a,x) (4), only used with the JMP instruction Accumulator, // A Immediate, // # Implied, // i ProgramCounterRelative, // r Stack, // s ZeroPage, // zp - ZeroPageIndexedIndirect, // (zp, x) ZeroPageIndexedWithX, // zp, x ZeroPageIndexedWithY, // zp, y ZeroPageIndirect, // (zp) (4) + ZeroPageIndexedIndirect, // (zp, x) ZeroPageIndirectIndexedWithY, // (zp), y } @@ -264,12 +358,14 @@ fn get_instruction(opcode: u8) -> Instruction { 0x00, Instruction::Valid(ValidInstruction { opcode: Opcode::BRK(AddressingMode::Stack), + cycles: 7, }), ), ( 0x01, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), (0x02, Instruction::Invalid), @@ -278,42 +374,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x04, Instruction::Valid(ValidInstruction { opcode: Opcode::TSB(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x05, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x06, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x07, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB0(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x08, Instruction::Valid(ValidInstruction { opcode: Opcode::PHP(AddressingMode::Stack), + cycles: 3, }), ), ( 0x09, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::Immediate), + cycles: 2, }), ), ( 0x0a, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::Accumulator), + cycles: 2, }), ), (0x0b, Instruction::Invalid), @@ -321,42 +424,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x0c, Instruction::Valid(ValidInstruction { opcode: Opcode::TSB(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0x0d, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x0e, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0x0f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR0(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x10, Instruction::Valid(ValidInstruction { opcode: Opcode::BPL(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0x11, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithY), + cycles: 5, }), ), ( 0x12, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPageIndirect), + cycles: 5, }), ), (0x13, Instruction::Invalid), @@ -364,42 +474,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x14, Instruction::Valid(ValidInstruction { opcode: Opcode::TRB(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x15, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0x16, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::ZeroPageIndexedWithX), + cycles: 6, }), ), ( 0x17, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB1(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x18, Instruction::Valid(ValidInstruction { opcode: Opcode::CLC(AddressingMode::Implied), + cycles: 2, }), ), ( 0x19, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0x1a, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::Accumulator), + cycles: 2, }), ), (0x1b, Instruction::Invalid), @@ -407,36 +524,42 @@ fn get_instruction(opcode: u8) -> Instruction { 0x1c, Instruction::Valid(ValidInstruction { opcode: Opcode::TRB(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0x1d, Instruction::Valid(ValidInstruction { opcode: Opcode::ORA(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0x1e, Instruction::Valid(ValidInstruction { opcode: Opcode::ASL(AddressingMode::AbsoluteIndexedWithX), + cycles: 7, }), ), ( 0x1f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR1(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x20, Instruction::Valid(ValidInstruction { opcode: Opcode::JSR(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0x21, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), (0x22, Instruction::Invalid), @@ -445,42 +568,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x24, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x25, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x26, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x27, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB2(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x28, Instruction::Valid(ValidInstruction { opcode: Opcode::PLP(AddressingMode::Stack), + cycles: 4, }), ), ( 0x29, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::Immediate), + cycles: 2, }), ), ( 0x2a, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::Accumulator), + cycles: 2, }), ), (0x2b, Instruction::Invalid), @@ -488,42 +618,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x2c, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x2d, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x2e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0x2f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR2(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x30, Instruction::Valid(ValidInstruction { opcode: Opcode::BMI(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0x31, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndirectIndexedWithY), + cycles: 5, }), ), ( 0x32, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndirect), + cycles: 5, }), ), (0x33, Instruction::Invalid), @@ -531,42 +668,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x34, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::ZeroPageIndexedWithX), + cycles: 3, }), ), ( 0x35, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0x36, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::ZeroPageIndexedWithX), + cycles: 6, }), ), ( 0x37, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB3(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x38, Instruction::Valid(ValidInstruction { opcode: Opcode::SEC(AddressingMode::Implied), + cycles: 2, }), ), ( 0x39, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0x3a, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::Accumulator), + cycles: 2, }), ), (0x3b, Instruction::Invalid), @@ -574,36 +718,42 @@ fn get_instruction(opcode: u8) -> Instruction { 0x3c, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0x3d, Instruction::Valid(ValidInstruction { opcode: Opcode::AND(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0x3e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROL(AddressingMode::AbsoluteIndexedWithX), + cycles: 7, }), ), ( 0x3f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR3(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x40, Instruction::Valid(ValidInstruction { opcode: Opcode::RTI(AddressingMode::Stack), + cycles: 6, }), ), ( 0x41, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), (0x42, Instruction::Invalid), @@ -613,36 +763,42 @@ fn get_instruction(opcode: u8) -> Instruction { 0x45, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x46, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x47, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB4(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x48, Instruction::Valid(ValidInstruction { opcode: Opcode::PHA(AddressingMode::Stack), + cycles: 3, }), ), ( 0x49, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::Immediate), + cycles: 2, }), ), ( 0x4a, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::Accumulator), + cycles: 2, }), ), (0x4b, Instruction::Invalid), @@ -650,42 +806,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x4c, Instruction::Valid(ValidInstruction { opcode: Opcode::JMP(AddressingMode::AbsoluteA), + cycles: 3, }), ), ( 0x4d, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x4e, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0x4f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR4(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x50, Instruction::Valid(ValidInstruction { opcode: Opcode::BVC(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0x51, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndirectIndexedWithY), + cycles: 5, }), ), ( 0x52, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndirect), + cycles: 5, }), ), (0x53, Instruction::Invalid), @@ -694,36 +857,42 @@ fn get_instruction(opcode: u8) -> Instruction { 0x55, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0x56, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::ZeroPageIndexedWithX), + cycles: 6, }), ), ( 0x57, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB5(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x58, Instruction::Valid(ValidInstruction { opcode: Opcode::CLI(AddressingMode::Implied), + cycles: 2, }), ), ( 0x59, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0x5a, Instruction::Valid(ValidInstruction { opcode: Opcode::PHY(AddressingMode::Stack), + cycles: 3, }), ), (0x5b, Instruction::Invalid), @@ -732,30 +901,35 @@ fn get_instruction(opcode: u8) -> Instruction { 0x5d, Instruction::Valid(ValidInstruction { opcode: Opcode::EOR(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0x5e, Instruction::Valid(ValidInstruction { opcode: Opcode::LSR(AddressingMode::AbsoluteIndexedWithX), + cycles: 7, }), ), ( 0x5f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR5(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x60, Instruction::Valid(ValidInstruction { opcode: Opcode::RTS(AddressingMode::Stack), + cycles: 6, }), ), ( 0x61, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), (0x62, Instruction::Invalid), @@ -764,42 +938,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x64, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x65, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x66, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x67, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB6(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x68, Instruction::Valid(ValidInstruction { opcode: Opcode::PLA(AddressingMode::Stack), + cycles: 4, }), ), ( 0x69, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::Immediate), + cycles: 2, }), ), ( 0x6a, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::Accumulator), + cycles: 2, }), ), (0x6b, Instruction::Invalid), @@ -807,42 +988,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x6c, Instruction::Valid(ValidInstruction { opcode: Opcode::JMP(AddressingMode::AbsoluteIndirect), + cycles: 5, }), ), ( 0x6d, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x6e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0x6f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR6(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x70, Instruction::Valid(ValidInstruction { opcode: Opcode::BVS(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0x71, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndirectIndexedWithY), + cycles: 5, }), ), ( 0x72, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndirect), + cycles: 5, }), ), (0x73, Instruction::Invalid), @@ -850,42 +1038,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x74, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0x75, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0x76, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::ZeroPageIndexedWithX), + cycles: 6, }), ), ( 0x77, Instruction::Valid(ValidInstruction { opcode: Opcode::RMB7(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x78, Instruction::Valid(ValidInstruction { opcode: Opcode::SEI(AddressingMode::Implied), + cycles: 2, }), ), ( 0x79, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0x7a, Instruction::Valid(ValidInstruction { opcode: Opcode::PLY(AddressingMode::Stack), + cycles: 4, }), ), (0x7b, Instruction::Invalid), @@ -893,36 +1088,42 @@ fn get_instruction(opcode: u8) -> Instruction { 0x7c, Instruction::Valid(ValidInstruction { opcode: Opcode::JMP(AddressingMode::AbsoluteIndexedIndirect), + cycles: 6, }), ), ( 0x7d, Instruction::Valid(ValidInstruction { opcode: Opcode::ADC(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0x7e, Instruction::Valid(ValidInstruction { opcode: Opcode::ROR(AddressingMode::AbsoluteIndexedWithX), + cycles: 7, }), ), ( 0x7f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBR7(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x80, Instruction::Valid(ValidInstruction { opcode: Opcode::BRA(AddressingMode::ProgramCounterRelative), + cycles: 3, }), ), ( 0x81, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), (0x82, Instruction::Invalid), @@ -931,42 +1132,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x84, Instruction::Valid(ValidInstruction { opcode: Opcode::STY(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x85, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x86, Instruction::Valid(ValidInstruction { opcode: Opcode::STX(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0x87, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB0(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x88, Instruction::Valid(ValidInstruction { opcode: Opcode::DEY(AddressingMode::Implied), + cycles: 2, }), ), ( 0x89, Instruction::Valid(ValidInstruction { opcode: Opcode::BIT(AddressingMode::Immediate), + cycles: 3, }), ), ( 0x8a, Instruction::Valid(ValidInstruction { opcode: Opcode::TXA(AddressingMode::Implied), + cycles: 2, }), ), (0x8b, Instruction::Invalid), @@ -974,42 +1182,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x8c, Instruction::Valid(ValidInstruction { opcode: Opcode::STY(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x8d, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x8e, Instruction::Valid(ValidInstruction { opcode: Opcode::STX(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x8f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS0(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0x90, Instruction::Valid(ValidInstruction { opcode: Opcode::BCC(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0x91, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndirectIndexedWithY), + cycles: 6, }), ), ( 0x92, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndirect), + cycles: 5, }), ), (0x93, Instruction::Invalid), @@ -1017,42 +1232,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x94, Instruction::Valid(ValidInstruction { opcode: Opcode::STY(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0x95, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0x96, Instruction::Valid(ValidInstruction { opcode: Opcode::STX(AddressingMode::ZeroPageIndexedWithY), + cycles: 4, }), ), ( 0x97, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB1(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0x98, Instruction::Valid(ValidInstruction { opcode: Opcode::TYA(AddressingMode::Implied), + cycles: 2, }), ), ( 0x99, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::AbsoluteIndexedWithY), + cycles: 5, }), ), ( 0x9a, Instruction::Valid(ValidInstruction { opcode: Opcode::TXS(AddressingMode::Implied), + cycles: 2, }), ), (0x9b, Instruction::Invalid), @@ -1060,42 +1282,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0x9c, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0x9d, Instruction::Valid(ValidInstruction { opcode: Opcode::STA(AddressingMode::AbsoluteIndexedWithX), + cycles: 5, }), ), ( 0x9e, Instruction::Valid(ValidInstruction { opcode: Opcode::STZ(AddressingMode::AbsoluteIndexedWithX), + cycles: 5, }), ), ( 0x9f, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS1(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0xa0, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::Immediate), + cycles: 2, }), ), ( 0xa1, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), ( 0xa2, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::Immediate), + cycles: 2, }), ), (0xa3, Instruction::Invalid), @@ -1103,42 +1332,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0xa4, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0xa5, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0xa6, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0xa7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB2(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xa8, Instruction::Valid(ValidInstruction { opcode: Opcode::TAY(AddressingMode::Implied), + cycles: 2, }), ), ( 0xa9, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::Immediate), + cycles: 2, }), ), ( 0xaa, Instruction::Valid(ValidInstruction { opcode: Opcode::TAX(AddressingMode::Implied), + cycles: 2, }), ), (0xab, Instruction::Invalid), @@ -1146,42 +1382,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0xac, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0xad, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0xae, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0xaf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS2(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0xb0, Instruction::Valid(ValidInstruction { opcode: Opcode::BCS(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0xb1, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndirectIndexedWithY), + cycles: 5, }), ), ( 0xb2, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndirect), + cycles: 5, // Unsure, see https://cx16.dk/65c02/reference.html#LDA }), ), (0xb3, Instruction::Invalid), @@ -1189,42 +1432,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0xb4, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0xb5, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0xb6, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::ZeroPageIndexedWithY), + cycles: 4, }), ), ( 0xb7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB3(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xb8, Instruction::Valid(ValidInstruction { opcode: Opcode::CLV(AddressingMode::Implied), + cycles: 2, }), ), ( 0xb9, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0xba, Instruction::Valid(ValidInstruction { opcode: Opcode::TSX(AddressingMode::Implied), + cycles: 2, }), ), (0xbb, Instruction::Invalid), @@ -1232,36 +1482,42 @@ fn get_instruction(opcode: u8) -> Instruction { 0xbc, Instruction::Valid(ValidInstruction { opcode: Opcode::LDY(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0xbd, Instruction::Valid(ValidInstruction { opcode: Opcode::LDA(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0xbe, Instruction::Valid(ValidInstruction { opcode: Opcode::LDX(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0xbf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS3(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0xc0, Instruction::Valid(ValidInstruction { opcode: Opcode::CPY(AddressingMode::Immediate), + cycles: 2, }), ), ( 0xc1, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), (0xc2, Instruction::Invalid), @@ -1270,90 +1526,105 @@ fn get_instruction(opcode: u8) -> Instruction { 0xc4, Instruction::Valid(ValidInstruction { opcode: Opcode::CPY(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0xc5, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0xc6, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xc7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB4(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xc8, Instruction::Valid(ValidInstruction { opcode: Opcode::INY(AddressingMode::Implied), + cycles: 2, }), ), ( 0xc9, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::Immediate), + cycles: 2, }), ), ( 0xca, Instruction::Valid(ValidInstruction { opcode: Opcode::DEX(AddressingMode::Implied), + cycles: 2, }), ), ( 0xcb, Instruction::Valid(ValidInstruction { opcode: Opcode::WAI(AddressingMode::Implied), + cycles: 3, }), ), ( 0xcc, Instruction::Valid(ValidInstruction { opcode: Opcode::CPY(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0xcd, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0xce, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0xcf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS4(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0xd0, Instruction::Valid(ValidInstruction { opcode: Opcode::BNE(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0xd1, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndirectIndexedWithY), + cycles: 5, }), ), ( 0xd2, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndirect), + cycles: 5, // Unsure, look here: https://cx16.dk/65c02/reference.html#CMP }), ), (0xd3, Instruction::Invalid), @@ -1362,42 +1633,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0xd5, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0xd6, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::ZeroPageIndexedWithX), + cycles: 6, }), ), ( 0xd7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB5(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xd8, Instruction::Valid(ValidInstruction { opcode: Opcode::CLD(AddressingMode::Implied), + cycles: 2, }), ), ( 0xd9, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0xda, Instruction::Valid(ValidInstruction { opcode: Opcode::PHX(AddressingMode::Stack), + cycles: 3, }), ), ( 0xdb, Instruction::Valid(ValidInstruction { opcode: Opcode::STP(AddressingMode::Implied), + cycles: 3, }), ), (0xdc, Instruction::Invalid), @@ -1405,30 +1683,35 @@ fn get_instruction(opcode: u8) -> Instruction { 0xdd, Instruction::Valid(ValidInstruction { opcode: Opcode::CMP(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0xde, Instruction::Valid(ValidInstruction { opcode: Opcode::DEC(AddressingMode::AbsoluteIndexedWithX), + cycles: 7, }), ), ( 0xdf, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS5(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0xe0, Instruction::Valid(ValidInstruction { opcode: Opcode::CPX(AddressingMode::Immediate), + cycles: 2, }), ), ( 0xe1, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndexedIndirect), + cycles: 6, }), ), (0xe2, Instruction::Invalid), @@ -1437,42 +1720,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0xe4, Instruction::Valid(ValidInstruction { opcode: Opcode::CPX(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0xe5, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPage), + cycles: 3, }), ), ( 0xe6, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xe7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB6(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xe8, Instruction::Valid(ValidInstruction { opcode: Opcode::INX(AddressingMode::Implied), + cycles: 2, }), ), ( 0xe9, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::Immediate), + cycles: 2, }), ), ( 0xea, Instruction::Valid(ValidInstruction { opcode: Opcode::NOP(AddressingMode::Implied), + cycles: 2, }), ), (0xeb, Instruction::Invalid), @@ -1480,42 +1770,49 @@ fn get_instruction(opcode: u8) -> Instruction { 0xec, Instruction::Valid(ValidInstruction { opcode: Opcode::CPX(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0xed, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::AbsoluteA), + cycles: 4, }), ), ( 0xee, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::AbsoluteA), + cycles: 6, }), ), ( 0xef, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS6(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ( 0xf0, Instruction::Valid(ValidInstruction { opcode: Opcode::BEQ(AddressingMode::ProgramCounterRelative), + cycles: 2, }), ), ( 0xf1, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndirectIndexedWithY), + cycles: 5, }), ), ( 0xf2, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndirect), + cycles: 5, }), ), (0xf3, Instruction::Invalid), @@ -1524,36 +1821,42 @@ fn get_instruction(opcode: u8) -> Instruction { 0xf5, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::ZeroPageIndexedWithX), + cycles: 4, }), ), ( 0xf6, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::ZeroPageIndexedWithX), + cycles: 6, }), ), ( 0xf7, Instruction::Valid(ValidInstruction { opcode: Opcode::SMB7(AddressingMode::ZeroPage), + cycles: 5, }), ), ( 0xf8, Instruction::Valid(ValidInstruction { opcode: Opcode::SED(AddressingMode::Implied), + cycles: 2, }), ), ( 0xf9, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::AbsoluteIndexedWithY), + cycles: 4, }), ), ( 0xfa, Instruction::Valid(ValidInstruction { opcode: Opcode::PLX(AddressingMode::Stack), + cycles: 4, }), ), (0xfb, Instruction::Invalid), @@ -1562,18 +1865,21 @@ fn get_instruction(opcode: u8) -> Instruction { 0xfd, Instruction::Valid(ValidInstruction { opcode: Opcode::SBC(AddressingMode::AbsoluteIndexedWithX), + cycles: 4, }), ), ( 0xfe, Instruction::Valid(ValidInstruction { opcode: Opcode::INC(AddressingMode::AbsoluteIndexedWithX), + cycles: 7, }), ), ( 0xff, Instruction::Valid(ValidInstruction { opcode: Opcode::BBS7(AddressingMode::ProgramCounterRelative), + cycles: 4, }), ), ]); @@ -1682,107 +1988,849 @@ enum Opcode { WAI(AddressingMode), } +#[derive(Clone, Copy)] +enum AddressingModeValue { + Implied, // Used when an instruction doesn't need an operation, e.g. RTS + Relative(Byte), // Used by branch instructions to offset the program counter + Absolute(Word), // Just a plain ol' address + Accumulator(Byte), // This isn't technically accurate, since the datasheet says this would be + // Implied, but we do use the value of the accumulator as the operand so this + // is how i'm gonna do it +} + +impl TryFrom for u16 { + type Error = ExecutionError; + fn try_from(value: AddressingModeValue) -> Result { + match value { + AddressingModeValue::Implied => Err(ExecutionError::IncompatibleAddrMode), + AddressingModeValue::Relative(inner_value) => Ok(inner_value as u16), + AddressingModeValue::Absolute(inner_value) => Ok(inner_value), + AddressingModeValue::Accumulator(inner_value) => Ok(inner_value as u16), + } + } +} + +// These functions also need to increment the cpu program counter by the number of bytes they read from memory +type AddressingModeFunction = fn(&mut Cpu) -> AddressingModeValue; + +fn get_address( + mode: &AddressingMode, + cpu: &mut Cpu, +) -> Result { + fn accumulator(cpu: &mut Cpu) -> AddressingModeValue { + let byte = cpu.a; + AddressingModeValue::Accumulator(byte) + } + + fn implied(_cpu: &mut Cpu) -> AddressingModeValue { + // i + AddressingModeValue::Implied + } + + fn immediate(cpu: &mut Cpu) -> AddressingModeValue { + // # + let address: Word = cpu.pc; + cpu.pc += 1; + AddressingModeValue::Absolute(address) + } + + fn absolute_a(cpu: &mut Cpu) -> Result { + // a + let address = cpu.read_word(cpu.pc)?; + cpu.pc += 2; + Ok(AddressingModeValue::Absolute(address)) + } + + fn zero_page(cpu: &mut Cpu) -> Result { + // zp + let address = cpu.read(cpu.pc)?; + cpu.pc += 2; + Ok(AddressingModeValue::Absolute(address as Word)) + } + + fn absolute_indexed_with_x( + // a, y + cpu: &mut Cpu, + ) -> Result { + let word = cpu.read_word(cpu.pc)?; + cpu.pc += 2; + let address: Word = word + cpu.x as u16; + Ok(AddressingModeValue::Absolute(address)) + } + + fn absolute_indexed_with_y( + // a, y + cpu: &mut Cpu, + ) -> Result { + let word = cpu.read_word(cpu.pc)?; + cpu.pc += 2; + let address: Word = word + cpu.y as u16; + Ok(AddressingModeValue::Absolute(address)) + } + + fn zero_page_indexed_with_x( + // zp, x + cpu: &mut Cpu, + ) -> Result { + let byte = cpu.read(cpu.pc)?; + cpu.pc += 1; + let address: Word = (byte + cpu.x) as u16; + Ok(AddressingModeValue::Absolute(address)) + } + + fn zero_page_indexed_with_y( + // zp, y + cpu: &mut Cpu, + ) -> Result { + let byte = cpu.read(cpu.pc)?; + cpu.pc += 1; + let address: Word = (byte + cpu.y) as u16; + Ok(AddressingModeValue::Absolute(address)) + } + + fn absolute_indirect( + // (a) + cpu: &mut Cpu, + ) -> Result { + let word = cpu.read_word(cpu.pc)?; + let address = cpu.read_word(word)?; + cpu.pc += 2; + Ok(AddressingModeValue::Absolute(address)) + } + + fn absolute_indexed_indirect( + // (a, x), only used with the JMP instruction + cpu: &mut Cpu, + ) -> Result { + let word = cpu.read_word(cpu.pc)?; + let address = cpu.read_word(word + cpu.x as Word)?; + cpu.pc += 2; + Ok(AddressingModeValue::Absolute(address)) + } + + fn zero_page_indexed_indirect( + // (zp, x) + cpu: &mut Cpu, + ) -> Result { + let byte = cpu.read(cpu.pc)?; + let address = cpu.read_word((byte + cpu.x) as Word)?; // Anytime you see something like `byte as Word`, it's using the byte as a zero-page address + cpu.pc += 1; + Ok(AddressingModeValue::Absolute(address)) + } + + fn zero_page_indirect_indexed_with_y( + // (zp), y + cpu: &mut Cpu, + ) -> Result { + let byte: Byte = cpu.read(cpu.pc)?; + let address: Word = cpu.read_word(byte as Word)?; + cpu.pc += 1; + Ok(AddressingModeValue::Absolute(address + cpu.y as Word)) + } + + fn relative( + // r + cpu: &mut Cpu, + ) -> Result { + let byte: Byte = cpu.read(cpu.pc)?; + cpu.pc += 1; + Ok(AddressingModeValue::Relative(byte)) + } + + match mode { + AddressingMode::AbsoluteA => absolute_a(cpu), + AddressingMode::AbsoluteIndexedWithX => absolute_indexed_with_x(cpu), + AddressingMode::AbsoluteIndexedWithY => absolute_indexed_with_y(cpu), + AddressingMode::AbsoluteIndirect => absolute_indirect(cpu), + AddressingMode::AbsoluteIndexedIndirect => absolute_indexed_indirect(cpu), + AddressingMode::Immediate => Ok(immediate(cpu)), + AddressingMode::ProgramCounterRelative => relative(cpu), + AddressingMode::ZeroPage => zero_page(cpu), + AddressingMode::ZeroPageIndexedWithX => zero_page_indexed_with_x(cpu), + AddressingMode::ZeroPageIndexedWithY => zero_page_indexed_with_y(cpu), + AddressingMode::ZeroPageIndirect => todo!(), + AddressingMode::ZeroPageIndexedIndirect => zero_page_indexed_indirect(cpu), + AddressingMode::ZeroPageIndirectIndexedWithY => zero_page_indirect_indexed_with_y(cpu), + AddressingMode::Accumulator => Ok(accumulator(cpu)), + AddressingMode::Implied => Ok(implied(cpu)), + _ => Ok(AddressingModeValue::Implied), + } +} + impl Opcode { - fn call(&self) { + fn call(&self, cpu: &mut Cpu) -> Result<(), ExecutionError> { match self { - Opcode::ADC(_addressing_mode) => {} - Opcode::AND(_addressing_mode) => {} - Opcode::ASL(_addressing_mode) => {} - Opcode::BBR0(_addressing_mode) => {} - Opcode::BBR1(_addressing_mode) => {} - Opcode::BBR2(_addressing_mode) => {} - Opcode::BBR3(_addressing_mode) => {} - Opcode::BBR4(_addressing_mode) => {} - Opcode::BBR5(_addressing_mode) => {} - Opcode::BBR6(_addressing_mode) => {} - Opcode::BBR7(_addressing_mode) => {} - Opcode::BBS0(_addressing_mode) => {} - Opcode::BBS1(_addressing_mode) => {} - Opcode::BBS2(_addressing_mode) => {} - Opcode::BBS3(_addressing_mode) => {} - Opcode::BBS4(_addressing_mode) => {} - Opcode::BBS5(_addressing_mode) => {} - Opcode::BBS6(_addressing_mode) => {} - Opcode::BBS7(_addressing_mode) => {} - Opcode::BCC(_addressing_mode) => {} - Opcode::BCS(_addressing_mode) => {} - Opcode::BEQ(_addressing_mode) => {} - Opcode::BIT(_addressing_mode) => {} - Opcode::BMI(_addressing_mode) => {} - Opcode::BNE(_addressing_mode) => {} - Opcode::BPL(_addressing_mode) => {} - Opcode::BRA(_addressing_mode) => {} - Opcode::BRK(_addressing_mode) => {} - Opcode::BVC(_addressing_mode) => {} - Opcode::BVS(_addressing_mode) => {} - Opcode::CLC(_addressing_mode) => {} - Opcode::CLD(_addressing_mode) => {} - Opcode::CLI(_addressing_mode) => {} - Opcode::CLV(_addressing_mode) => {} - Opcode::CMP(_addressing_mode) => {} - Opcode::CPX(_addressing_mode) => {} - Opcode::CPY(_addressing_mode) => {} - Opcode::DEC(_addressing_mode) => {} - Opcode::DEX(_addressing_mode) => {} - Opcode::DEY(_addressing_mode) => {} - Opcode::EOR(_addressing_mode) => {} - Opcode::INC(_addressing_mode) => {} - Opcode::INX(_addressing_mode) => {} - Opcode::INY(_addressing_mode) => {} - Opcode::JMP(_addressing_mode) => {} - Opcode::JSR(_addressing_mode) => {} - Opcode::LDA(_addressing_mode) => {} - Opcode::LDX(_addressing_mode) => {} - Opcode::LDY(_addressing_mode) => {} - Opcode::LSR(_addressing_mode) => {} - Opcode::NOP(_addressing_mode) => {} - Opcode::ORA(_addressing_mode) => {} - Opcode::PHA(_addressing_mode) => {} - Opcode::PHP(_addressing_mode) => {} - Opcode::PHX(_addressing_mode) => {} - Opcode::PHY(_addressing_mode) => {} - Opcode::PLA(_addressing_mode) => {} - Opcode::PLP(_addressing_mode) => {} - Opcode::PLX(_addressing_mode) => {} - Opcode::PLY(_addressing_mode) => {} - Opcode::RMB0(_addressing_mode) => {} - Opcode::RMB1(_addressing_mode) => {} - Opcode::RMB2(_addressing_mode) => {} - Opcode::RMB3(_addressing_mode) => {} - Opcode::RMB4(_addressing_mode) => {} - Opcode::RMB5(_addressing_mode) => {} - Opcode::RMB6(_addressing_mode) => {} - Opcode::RMB7(_addressing_mode) => {} - Opcode::ROL(_addressing_mode) => {} - Opcode::ROR(_addressing_mode) => {} - Opcode::RTI(_addressing_mode) => {} - Opcode::RTS(_addressing_mode) => {} - Opcode::SBC(_addressing_mode) => {} - Opcode::SEC(_addressing_mode) => {} - Opcode::SED(_addressing_mode) => {} - Opcode::SEI(_addressing_mode) => {} - Opcode::SMB0(_addressing_mode) => {} - Opcode::SMB1(_addressing_mode) => {} - Opcode::SMB2(_addressing_mode) => {} - Opcode::SMB3(_addressing_mode) => {} - Opcode::SMB4(_addressing_mode) => {} - Opcode::SMB5(_addressing_mode) => {} - Opcode::SMB6(_addressing_mode) => {} - Opcode::SMB7(_addressing_mode) => {} - Opcode::STA(_addressing_mode) => {} - Opcode::STP(_addressing_mode) => {} - Opcode::STX(_addressing_mode) => {} - Opcode::STY(_addressing_mode) => {} - Opcode::STZ(_addressing_mode) => {} - Opcode::TAX(_addressing_mode) => {} - Opcode::TAY(_addressing_mode) => {} - Opcode::TRB(_addressing_mode) => {} - Opcode::TSB(_addressing_mode) => {} - Opcode::TSX(_addressing_mode) => {} - Opcode::TXA(_addressing_mode) => {} - Opcode::TXS(_addressing_mode) => {} - Opcode::TYA(_addressing_mode) => {} - Opcode::WAI(_addressing_mode) => {} + Opcode::ADC(mode) => Ok(()), + Opcode::AND(mode) => match mode { + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::ZeroPageIndirect + | AddressingMode::ZeroPageIndexedIndirect + | AddressingMode::ZeroPageIndirectIndexedWithY => { + let address = get_address(mode, cpu)?; + cpu.a &= cpu.read(address.try_into()?)?; + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::ASL(mode) => { + fn asl(cpu: &mut Cpu, value: Byte) -> Byte { + let carry = cpu.get_flag(StatusFlag::Carry) as Byte; + cpu.set_flag(StatusFlag::Carry, value & 0b1000_0000 == 0b1000_0000); + let shifted_value = value << 1; + cpu.set_flag( + StatusFlag::Negative, + value & StatusFlag::Negative as Byte > 0, + ); + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + shifted_value + } + match mode { + AddressingMode::Accumulator => Ok(cpu.a = asl(cpu, cpu.a)), + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX => { + let address = get_address(mode, cpu)?; + let value = cpu.read(address.try_into()?)?; + let result = asl(cpu, value); + Ok(cpu.write(address.try_into()?, result)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + } + } + Opcode::BBR0(mode) => Ok(()), + Opcode::BBR1(mode) => Ok(()), + Opcode::BBR2(mode) => Ok(()), + Opcode::BBR3(mode) => Ok(()), + Opcode::BBR4(mode) => Ok(()), + Opcode::BBR5(mode) => Ok(()), + Opcode::BBR6(mode) => Ok(()), + Opcode::BBR7(mode) => Ok(()), + Opcode::BBS0(mode) => Ok(()), + Opcode::BBS1(mode) => Ok(()), + Opcode::BBS2(mode) => Ok(()), + Opcode::BBS3(mode) => Ok(()), + Opcode::BBS4(mode) => Ok(()), + Opcode::BBS5(mode) => Ok(()), + Opcode::BBS6(mode) => Ok(()), + Opcode::BBS7(mode) => Ok(()), + Opcode::BCC(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if !cpu.get_flag(StatusFlag::Carry) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BCS(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if cpu.get_flag(StatusFlag::Carry) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BEQ(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if cpu.get_flag(StatusFlag::Zero) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BIT(mode) => Ok(()), + Opcode::BMI(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if cpu.get_flag(StatusFlag::Negative) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BNE(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if !cpu.get_flag(StatusFlag::Zero) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BPL(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if !cpu.get_flag(StatusFlag::Negative) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BRA(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BRK(mode) => Ok(()), + Opcode::BVC(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if !cpu.get_flag(StatusFlag::Overflow) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::BVS(mode) => match mode { + AddressingMode::ProgramCounterRelative => { + if cpu.get_flag(StatusFlag::Overflow) { + let address = get_address(mode, cpu)?; + Ok(cpu.pc += u16::try_from(address)?) + } else { + Ok(cpu.pc += 1) + } + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::CLC(mode) => match mode { + AddressingMode::Implied => Ok(cpu.set_flag(StatusFlag::Carry, false)), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::CLD(mode) => match mode { + AddressingMode::Implied => Ok(cpu.set_flag(StatusFlag::Decimal, false)), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::CLI(mode) => match mode { + AddressingMode::Implied => Ok(cpu.set_flag(StatusFlag::IrqDisable, false)), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::CLV(mode) => match mode { + AddressingMode::Implied => Ok(cpu.set_flag(StatusFlag::Overflow, false)), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::CMP(mode) => match mode { + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::ZeroPageIndirect + | AddressingMode::ZeroPageIndexedIndirect + | AddressingMode::ZeroPageIndirectIndexedWithY => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.set_flag(StatusFlag::Carry, cpu.a >= byte); + cpu.set_flag(StatusFlag::Zero, cpu.a == byte); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a - byte)); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::CPX(mode) => match mode { + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::AbsoluteA => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.set_flag(StatusFlag::Carry, cpu.x >= byte); + cpu.set_flag(StatusFlag::Zero, cpu.x == byte); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x - byte)); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::CPY(mode) => match mode { + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::AbsoluteA => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.set_flag(StatusFlag::Carry, cpu.y >= byte); + cpu.set_flag(StatusFlag::Zero, cpu.y == byte); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y - byte)); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::DEC(mode) => match mode { + AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX => { + let address = get_address(mode, cpu)?; + Ok(cpu.write(address.try_into()?, cpu.read(address.try_into()?)? - 1)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::DEX(mode) => match mode { + AddressingMode::Implied => Ok(cpu.x -= 1), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::DEY(mode) => match mode { + AddressingMode::Implied => Ok(cpu.y -= 1), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::EOR(mode) => match mode { + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::ZeroPageIndirect + | AddressingMode::ZeroPageIndexedIndirect + | AddressingMode::ZeroPageIndirectIndexedWithY => { + let address = get_address(mode, cpu)?; + cpu.a ^= cpu.read(address.try_into()?)?; + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::INC(mode) => match mode { + AddressingMode::Accumulator => { + cpu.a += 1; + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + Ok(()) + } + AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.set_flag(StatusFlag::Zero, byte + 1 == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(byte + 1)); + Ok(cpu.write(address.try_into()?, byte + 1)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::INX(mode) => match mode { + AddressingMode::Implied => Ok(cpu.x += 1), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::INY(mode) => match mode { + AddressingMode::Implied => Ok(cpu.y += 1), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::JMP(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndirect + | AddressingMode::AbsoluteIndexedIndirect => { + let address = get_address(mode, cpu)?; + Ok(cpu.pc = address.try_into()?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::JSR(mode) => match mode { + AddressingMode::AbsoluteA => { + let return_address = cpu.pc - 1; + cpu.push_stack_word(return_address)?; + let address = get_address(mode, cpu)?; + Ok(cpu.pc = address.try_into()?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::LDA(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndirect + | AddressingMode::ZeroPageIndexedIndirect + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::ZeroPageIndirectIndexedWithY => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.a = byte; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::LDX(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithY => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.x = byte; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::LDY(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.y = byte; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); + cpu.set_flag(StatusFlag::Zero, cpu.y == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::LSR(mode) => { + fn lsr(cpu: &mut Cpu, value: Byte) -> Byte { + let carry = cpu.get_flag(StatusFlag::Carry) as Byte; + cpu.set_flag(StatusFlag::Carry, value & 1 == 1); + let shifted_value = value >> 1; + cpu.set_flag( + StatusFlag::Negative, + value & StatusFlag::Negative as Byte > 0, + ); + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + shifted_value + } + match mode { + AddressingMode::Accumulator => Ok(cpu.a = lsr(cpu, cpu.a)), + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX => { + let address = get_address(mode, cpu)?; + let value = cpu.read(address.try_into()?)?; + let result = lsr(cpu, value); + Ok(cpu.write(address.try_into()?, result)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + } + } + Opcode::NOP(mode) => Ok(cpu.pc += 1), + Opcode::ORA(mode) => match mode { + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::ZeroPageIndirect + | AddressingMode::ZeroPageIndexedIndirect + | AddressingMode::ZeroPageIndirectIndexedWithY => { + let address = get_address(mode, cpu)?; + cpu.a |= cpu.read(address.try_into()?)?; + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PHA(mode) => match mode { + AddressingMode::Stack => Ok(cpu.push_stack(cpu.a)?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PHP(mode) => match mode { + AddressingMode::Stack => Ok(cpu.push_stack(cpu.p)?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PHX(mode) => match mode { + AddressingMode::Stack => Ok(cpu.push_stack(cpu.x)?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PHY(mode) => match mode { + AddressingMode::Stack => Ok(cpu.push_stack(cpu.y)?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PLA(mode) => match mode { + AddressingMode::Stack => Ok(cpu.a = cpu.pop_stack()?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PLP(mode) => match mode { + AddressingMode::Stack => Ok(cpu.p = cpu.pop_stack()?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PLX(mode) => match mode { + AddressingMode::Stack => Ok(cpu.x = cpu.pop_stack()?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::PLY(mode) => match mode { + AddressingMode::Stack => Ok(cpu.y = cpu.pop_stack()?), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::RMB0(mode) => Ok(()), + Opcode::RMB1(mode) => Ok(()), + Opcode::RMB2(mode) => Ok(()), + Opcode::RMB3(mode) => Ok(()), + Opcode::RMB4(mode) => Ok(()), + Opcode::RMB5(mode) => Ok(()), + Opcode::RMB6(mode) => Ok(()), + Opcode::RMB7(mode) => Ok(()), + Opcode::ROL(mode) => { + fn rol(cpu: &mut Cpu, value: Byte) -> Byte { + let carry = cpu.get_flag(StatusFlag::Carry) as Byte; + cpu.set_flag(StatusFlag::Carry, value >> 7 == 1); + let shifted_value = (value << 1) + carry; + cpu.set_flag( + StatusFlag::Negative, + shifted_value & StatusFlag::Negative as Byte > 0, + ); + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + shifted_value + } + match mode { + AddressingMode::Accumulator => Ok(cpu.a = rol(cpu, cpu.a)), + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX => { + let address = get_address(mode, cpu)?; + let value = cpu.read(address.try_into()?)?; + let result = rol(cpu, value); + Ok(cpu.write(address.try_into()?, result)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + } + } + Opcode::ROR(mode) => { + fn ror(cpu: &mut Cpu, value: Byte) -> Byte { + let carry = cpu.get_flag(StatusFlag::Carry) as Byte; + cpu.set_flag(StatusFlag::Carry, value & 1 == 1); + let shifted_value = (value >> 1) + (carry << 7); + cpu.set_flag( + StatusFlag::Negative, + shifted_value & StatusFlag::Negative as Byte > 0, + ); + cpu.set_flag(StatusFlag::Zero, shifted_value == 0); + shifted_value + } + match mode { + AddressingMode::Accumulator => Ok(cpu.a = ror(cpu, cpu.a)), + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX => { + let address = get_address(mode, cpu)?; + let value = cpu.read(address.try_into()?)?; + let result = ror(cpu, value); + Ok(cpu.write(address.try_into()?, result)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + } + } + Opcode::RTI(mode) => match mode { + AddressingMode::Implied => { + cpu.s = cpu.pop_stack()?; + cpu.p = cpu.pop_stack()?; + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::RTS(mode) => match mode { + AddressingMode::Implied => { + let return_address = cpu.pop_stack_word()?; + Ok(cpu.pc = return_address + 1) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::SBC(mode) => match mode { + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::ZeroPageIndirect + | AddressingMode::ZeroPageIndexedIndirect + | AddressingMode::ZeroPageIndirectIndexedWithY => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + let carry = cpu.get_flag(StatusFlag::Carry); + let result = cpu.a as Word + byte as Word + !carry as Word; + cpu.set_flag(StatusFlag::Carry, result > u16::from(u8::max_value())); + cpu.set_flag(StatusFlag::Zero, result as Byte == 0); + cpu.set_flag( + StatusFlag::Overflow, + (cpu.a ^ byte) & (cpu.a ^ result as Byte) & 0b1000_0000 > 0, + // Truly have no idea what's happening here, + // check out https://docs.rs/emulator_6502/latest/src/emulator_6502/opcodes/mod.rs.html + ); + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(result as Byte)); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::SEC(mode) => match mode { + AddressingMode::Implied => Ok(cpu.set_flag(StatusFlag::Carry, true)), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::SED(mode) => match mode { + AddressingMode::Implied => Ok(cpu.set_flag(StatusFlag::Decimal, true)), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::SEI(mode) => match mode { + AddressingMode::Implied => Ok(cpu.set_flag(StatusFlag::IrqDisable, true)), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::SMB0(mode) => Ok(()), + Opcode::SMB1(mode) => Ok(()), + Opcode::SMB2(mode) => Ok(()), + Opcode::SMB3(mode) => Ok(()), + Opcode::SMB4(mode) => Ok(()), + Opcode::SMB5(mode) => Ok(()), + Opcode::SMB6(mode) => Ok(()), + Opcode::SMB7(mode) => Ok(()), + Opcode::STA(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::AbsoluteIndexedWithY + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndirect + | AddressingMode::ZeroPageIndexedIndirect + | AddressingMode::ZeroPageIndexedWithX + | AddressingMode::ZeroPageIndirectIndexedWithY => { + let address = get_address(mode, cpu)?; + Ok(cpu.write(address.try_into()?, cpu.a)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::STP(mode) => match mode { + AddressingMode::Implied => Ok(cpu.stop()), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::STX(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithY => { + let address = get_address(mode, cpu)?; + Ok(cpu.write(address.try_into()?, cpu.x)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::STY(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX => { + let address = get_address(mode, cpu)?; + Ok(cpu.write(address.try_into()?, cpu.y)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::STZ(mode) => match mode { + AddressingMode::AbsoluteA + | AddressingMode::AbsoluteIndexedWithX + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageIndexedWithX => { + let address = get_address(mode, cpu)?; + Ok(cpu.write(address.try_into()?, 0)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TAX(mode) => match mode { + AddressingMode::Implied => { + cpu.x = cpu.a; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TAY(mode) => match mode { + AddressingMode::Implied => { + cpu.y = cpu.a; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.y)); + cpu.set_flag(StatusFlag::Zero, cpu.y == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TRB(mode) => match mode { + // Still not really sure when you would + // use TRB or TSB, but this was helpful + // (Garth rocks) http://forum.6502.org/viewtopic.php?t=48 + AddressingMode::AbsoluteA | AddressingMode::ZeroPage => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + cpu.write(address.try_into()?, !cpu.a & byte)?; + cpu.set_flag(StatusFlag::Zero, cpu.a & byte > 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TSB(mode) => match mode { + AddressingMode::AbsoluteA | AddressingMode::ZeroPage => { + let address = get_address(mode, cpu)?; + let byte = cpu.read(address.try_into()?)?; + Ok(cpu.write(address.try_into()?, cpu.a | byte)?) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TSX(mode) => match mode { + AddressingMode::Implied => { + cpu.x = cpu.s; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.x)); + cpu.set_flag(StatusFlag::Zero, cpu.x == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TXA(mode) => match mode { + AddressingMode::Implied => { + cpu.a = cpu.x; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TXS(mode) => match mode { + AddressingMode::Implied => { + cpu.s = cpu.x; + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::TYA(mode) => match mode { + AddressingMode::Implied => { + cpu.a = cpu.y; + cpu.set_flag(StatusFlag::Negative, cpu.is_negative(cpu.a)); + cpu.set_flag(StatusFlag::Zero, cpu.a == 0); + Ok(()) + } + _ => Err(ExecutionError::IncompatibleAddrMode), + }, + Opcode::WAI(mode) => match mode { + AddressingMode::Implied => Ok(cpu.wait_for_interrupt()), + _ => Err(ExecutionError::IncompatibleAddrMode), + }, } } } @@ -1808,41 +2856,30 @@ fn main() { Err(error) => println!("Error writing to memory at address 0xFFFD: {:?}", error), Ok(()) => {} }; - match memory.write(0x0000, 0xAE) { + match memory.write(0x0000, 0xEA) { Err(error) => println!("Error writing to memory at address 0x0000: {:?}", error), Ok(()) => {} }; - match memory.write(0x0001, 0xAE) { - Err(error) => println!("Error writing to memory: {:?}", error), + match memory.write(0x0001, 0xA0) { + Err(error) => println!("Error writing to memory at address 0x0000: {:?}", error), + Ok(()) => {} + }; + match memory.write(0x0002, 0xAE) { + Err(error) => println!("Error writing to memory at address 0x0000: {:?}", error), + Ok(()) => {} + }; + match memory.write(0x0003, 0xA2) { + Err(error) => println!("Error writing to memory at address 0x0000: {:?}", error), + Ok(()) => {} + }; + match memory.write(0x0004, 0xA2) { + Err(error) => println!("Error writing to memory at address 0x0000: {:?}", error), Ok(()) => {} }; let mut cpu = Cpu::new(memory); match cpu.reset() { Err(error) => println!("{error:?}"), - Ok(()) => { - println!("accumulator: {a:?}, x: {x:?}, y: {y:?}, program counter: {pc:?}, stack pointer: {s:?}, status register: {p:?}, irq pin: {irq:?}, nmi pin: {nmi:?}", a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); - } + Ok(()) => {} } - match cpu.tick() { - Ok(()) => { - println!("accumulator: {a:?}, x: {x:?}, y: {y:?}, program counter: {pc:?}, stack pointer: {s:?}, status register: {p:?}, irq pin: {irq:?}, nmi pin: {nmi:?}", a = cpu.a, x = cpu.x, y = cpu.y, pc = cpu.pc, s = cpu.s, p = cpu.p, irq = cpu.irq, nmi = cpu.nmi); - cpu.tick().unwrap() - } - Err(error) => match error { - ExecutionError::InvalidInstruction => {} - ExecutionError::StackOverflow => panic!("Stack overflowed!"), - ExecutionError::NoInterrupt => {} - ExecutionError::MemoryError(error) => match error { - MemoryError::Unmapped => panic!( - "Cpu tried to access memory that hasn't yet been mapped at {:?}", - cpu.pc - ), - MemoryError::NoDataAtAddress => panic!( - "Cpu tried to read from address {:?} but there was no data", - cpu.pc - ), - }, - ExecutionError::InterruptsDisabled => (), - }, - }; + cpu.execute(); }