diff options
| -rw-r--r-- | Cargo.lock | 7 | ||||
| -rw-r--r-- | Cargo.toml | 1 | ||||
| -rw-r--r-- | src/cpu.rs | 66 | ||||
| -rw-r--r-- | src/instruction.rs | 22 | ||||
| -rw-r--r-- | src/main.rs | 12 |
5 files changed, 101 insertions, 7 deletions
diff --git a/Cargo.lock b/Cargo.lock index 2a5f2ee..1546f56 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -9,6 +9,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" [[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" @@ -31,6 +37,7 @@ name = "mos6502" version = "0.1.0" dependencies = [ "bitflags", + "byteorder", "tracing", "tracing-subscriber", ] diff --git a/Cargo.toml b/Cargo.toml index 8724300..29d8ab1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" [dependencies] bitflags = "2.6.0" +byteorder = "1.5.0" tracing = "0.1.40" tracing-subscriber = "0.3.18" diff --git a/src/cpu.rs b/src/cpu.rs index d2d6d4d..1955a86 100644 --- a/src/cpu.rs +++ b/src/cpu.rs @@ -3,6 +3,7 @@ //! Implementation of the MOS 6502 CPU. use bitflags::bitflags; +use byteorder::{LittleEndian, ReadBytesExt, WriteBytesExt}; use tracing::{debug, trace}; use crate::{ @@ -97,6 +98,29 @@ impl Cpu { data } + /// Fetches the next byte, pointed to by the program counter. + fn fetch_word(&mut self, cycles: &mut u32, memory: &Memory) -> u16 { + debug!("fetching word from address {:#04X}", self.program_counter); + + // MOS6502 is little endian. + let data: u16 = memory + .data + .get(usize::from(self.program_counter)..usize::from(self.program_counter + 2)) + .expect("program_counter indexed outside of memory") + .read_u16::<LittleEndian>() + .expect("could not read word from memory"); + + debug!( + "fetched word {data:#04X} from address {:#04X}", + self.program_counter + ); + + self.program_counter = self.program_counter.wrapping_add(2); + Self::expend_cycles(cycles, 1); + + data + } + /// Sets the LDA status after executing an LDA operation. fn lda_set_status(&mut self) { trace!("setting LDA status"); @@ -125,6 +149,20 @@ impl Cpu { data } + /// Writes a word to memory. + fn write_word(cycles: &mut u32, data: u16, address: u32, memory: &mut Memory) { + debug!("writing word to address {address:#08X}"); + + memory + .data + .get_mut((address as usize)..(address + 2) as usize) + .expect("write_word address indexed out of bounds") + .write_u16::<LittleEndian>(data) + .expect("failed to write data"); + + Self::expend_cycles(cycles, 2); + } + /// Expends the specified number of cycles. This method is for internal use to log expended /// cycles. fn expend_cycles(cycles: &mut u32, count: u32) { @@ -134,7 +172,7 @@ impl Cpu { /// Executes instructions on the [`Cpu`] from memory. Runs the specified /// number of cycles. - pub fn execute(&mut self, cycles: u32, memory: &Memory) { + pub fn execute(&mut self, cycles: u32, memory: &mut Memory) { // Shadow cycles with a mutable copy let mut cycles = cycles; @@ -152,6 +190,32 @@ impl Cpu { ); match (operation.instruction, operation.addressing_mode) { + (Instruction::Jsr, AddressingMode::Absolute) => { + // Fetch the subroutine address from memory. + let subroutine_address: u16 = self.fetch_word(&mut cycles, memory); + + // Write the program counter - 1 to the stack. + Self::write_word( + &mut cycles, + self.program_counter - 1, + self.stack_pointer.into(), + memory, + ); + + // Increment stack pointer. + self.stack_pointer += 1; + + // Set program counter to subroutine address. + self.program_counter = subroutine_address; + Self::expend_cycles(&mut cycles, 1); + } + ( + Instruction::Jsr, + AddressingMode::Immediate + | AddressingMode::ZeroPage + | AddressingMode::ZeroPageX, + ) => unreachable!(), + (Instruction::Lda, AddressingMode::Absolute) => todo!(), (Instruction::Lda, AddressingMode::Immediate) => { let value = self.fetch_byte(&mut cycles, memory); diff --git a/src/instruction.rs b/src/instruction.rs index 4a23992..2390046 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -5,20 +5,35 @@ /// List of all instructions for MOS 6502. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum Instruction { + /// Jump to subroutine. + /// + /// The JSR instruction pushes the address (minus one) of the return point on to the stack and + /// then sets the program counter to the target memory address. + Jsr, + /// Load accumulator. + /// + /// Loads a byte of memory into the accumulator, setting the zero and negative flags as + /// appropriate. Lda, } /// Addressing modes for MOS 6502. #[derive(Clone, Copy, Debug, Eq, PartialEq)] pub enum AddressingMode { - /// Immediate mode + /// Absolute mode. + /// + /// Instructions using absolute addressing contain a full 16 bit address to identify the + /// target location. + Absolute, + + /// Immediate mode. Immediate, - /// Zero Page mode + /// Zero Page mode. ZeroPage, - /// ZeroPage.X mode + /// ZeroPage.X mode. ZeroPageX, } @@ -46,6 +61,7 @@ impl Operation { /// operation exists. pub const fn get_operation(opcode: u8) -> Option<Operation> { match opcode { + 0x20 => Some(Operation::new(Instruction::Jsr, AddressingMode::Absolute)), 0xA5 => Some(Operation::new(Instruction::Lda, AddressingMode::ZeroPage)), 0xA9 => Some(Operation::new(Instruction::Lda, AddressingMode::Immediate)), 0xB5 => Some(Operation::new(Instruction::Lda, AddressingMode::ZeroPageX)), diff --git a/src/main.rs b/src/main.rs index 7f6df89..2fcee18 100644 --- a/src/main.rs +++ b/src/main.rs @@ -28,15 +28,21 @@ fn main() { // little program if let Some(opcode) = memory.data.get_mut(0xFFFC) { - *opcode = 0xA5; + *opcode = 0x20; } if let Some(opcode) = memory.data.get_mut(0xFFFD) { *opcode = 0x42; } - if let Some(opcode) = memory.data.get_mut(0x0042) { + if let Some(opcode) = memory.data.get_mut(0xFFFE) { + *opcode = 0x42; + } + if let Some(opcode) = memory.data.get_mut(0x4242) { + *opcode = 0xA9; + } + if let Some(opcode) = memory.data.get_mut(0x4243) { *opcode = 0x84; } // end program - cpu.execute(3, &memory); + cpu.execute(9, &mut memory); } |