diff --git a/src/address.rs b/src/address.rs index 3ca7918..35dae9b 100644 --- a/src/address.rs +++ b/src/address.rs @@ -25,30 +25,13 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -pub enum AddressingMode { - Accumulator, // LSR A work directly on accumulator - Immediate, // LDA #10 8-bit constant in instruction - ZeroPage, // LDA $00 zero-page address - ZeroPageX, // LDA $80,X address is X register + 8-bit constant - ZeroPageY, // LDX $10,Y address is Y register + 8-bit constant - Relative, // BNE LABEL branch target as signed relative offset - Absolute, // JMP $1000 full 16-bit address - AbsoluteX, // STA $1000,X full 16-bit address plus X register - AbsoluteY, // STA $1000,Y full 16-bit address plus Y register - Indirect, // JMP ($1000) jump to address stored at address - IndexedIndirectX, // LDA ($10,X) load from address stored at (constant - // zero page address plus X register) - IndirectIndexedY, // LDA ($10),Y load from (address stored at constant - // zero page address) plus Y register -} - // The idea here is that it doesn't make sense to add two addresses, but it // does make sense to add an address and an "address-difference". (If this // is too annoying to work with we should let it go.) -#[deriving(PartialEq, Eq, PartialOrd, Ord)] +#[deriving(PartialEq, Eq, PartialOrd, Ord, Show)] pub struct AddressDiff(pub u16); -#[deriving(PartialEq, Eq, PartialOrd, Ord)] +#[deriving(PartialEq, Eq, PartialOrd, Ord, Show)] pub struct Address(pub u16); impl Add for Address { @@ -58,6 +41,13 @@ impl Add for Address { } } +impl Add for AddressDiff { + fn add(&self, &AddressDiff(rhs): &AddressDiff) -> AddressDiff { + let &AddressDiff(lhs) = self; + AddressDiff(lhs + rhs) + } +} + // rustc doesn't seem to like having multiple implementations of Add for // Address. I believe this is a Rust bug (possibly resolved by "associated // types" RFC?). Or I wrote it wrong. Anyway, here's some living dead code: diff --git a/src/bin/main.rs b/src/bin/main.rs index 00dc4f7..99cbafc 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -34,21 +34,119 @@ fn main() { let mut machine = machine::Machine::new(); // "Load" a program - machine.memory.set_byte(&Address(0), 0x69); // ADC immediate opcode - machine.memory.set_byte(&Address(1), 0x07); // Immediate operand - machine.memory.set_byte(&Address(2), 0x69); // ADC immediate opcode - machine.memory.set_byte(&Address(3), 0x08); // ADC immediate opcode - // Obviously this will run the full program, just - // executing a finite num of instructions for simplicity - // right now. - for _ in range(0u, 2u) { - let raw_instruction = machine.fetch_instruction(); - let instruction = machine.decode_instruction(raw_instruction); - machine.execute_instruction(instruction); - } - + // JAM: FIXME: What's the syntax for specifying the array element type, + // but not the length? (For a fixed-size array) + + let zero_page_data: [u8, ..17] = [ + // ZeroPage data start + 0x00, + 0x02, // ADC ZeroPage target + 0x00, + 0x04, // ADC ZeroPageX target + 0x00, + 0x00, + 0x00, + 0x00, + 0x10, // ADC IndexedIndirectX address + 0x80, // ADC IndexedIndirectX address + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x08, // ADC IndirectIndexedY address + 0x80, // ADC IndirectIndexedY address + ]; + + let program: [u8, ..33] = [ + // Code start + 0xA9, // LDA Immediate + 0x01, // Immediate operand + + 0x69, // ADC Immediate + 0x07, // Immediate operand + + 0x65, // ADC ZeroPage + 0x01, // ZeroPage operand + + 0xA2, // LDX Immediate + 0x01, // Immediate operand + + 0x75, // ADC ZeroPageX + 0x02, // ZeroPageX operand + + 0x6D, // ADC Absolute + 0x01, // Absolute operand + 0x80, // Absolute operand + + 0xA2, // LDX immediate + 0x08, // Immediate operand + + 0x7D, // ADC AbsoluteX + 0x00, // AbsoluteX operand + 0x80, // AbsoluteX operand + + 0xA0, // LDY immediate + 0x04, // Immediate operand + + 0x79, // ADC AbsoluteY + 0x00, // AbsoluteY operand + 0x80, // AbsoluteY operand + + 0xA2, // LDX immediate + 0x05, // Immediate operand + + 0x61, // ADC IndexedIndirectX + 0x03, // IndexedIndirectX operand + + 0xA0, // LDY immediate + 0x10, // Immediate operand + + 0x71, // ADC IndirectIndexedY + 0x0F, // IndirectIndexedY operand + + 0xEA, // NOP :) + + 0xFF, // Something invalid -- the end! + ]; + + let data: [u8, ..25] = [ + 0x00, + 0x09, // ADC Absolute target + 0x00, + 0x00, + 0x40, // ADC AbsoluteY target + 0x00, + 0x00, + 0x00, + 0x11, // ADC AbsoluteX target + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x12, // ADC IndexedIndirectX target + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x06, // ADC IndirectIndexedY target + ]; + + machine.memory.set_bytes(Address(0x0000), &zero_page_data); + machine.memory.set_bytes(Address(0x4000), &program); + machine.memory.set_bytes(Address(0x8000), &data); + + machine.registers.program_counter = Address(0x4000); + + machine.run(); + println!("{}", machine); - } diff --git a/src/instruction.rs b/src/instruction.rs index fac9cd4..45d0e99 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -25,6 +25,10 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +use address::Address; +use address::AddressDiff; +use machine::Machine; + // Abbreviations // // General @@ -48,9 +52,7 @@ pub enum Instruction // NV BDIZC A X Y S PC M // // | outputs | inputs -{ - ADC(i8) // ADd with Carry............ | NV ...ZC A = A + M + C - +{ ADC // ADd with Carry................ | NV ...ZC A = A + M + C , AND // logical AND (bitwise)......... | N. ...Z. A = A && M , ASL // Arithmetic Shift Left......... | N. ...ZC A = M << 1 , BCC // Branch if Carry Clear......... | .. ..... PC = !C @@ -111,3 +113,405 @@ pub enum Instruction , TYA // Transfer Y to Accumulator..... | N. ...Z. A = Y } +pub enum AMOut { + UseImplied, + UseImmediate(u8), + UseRelative(i8), + UseAddress(Address), +} + +pub enum AddressingMode +// length +{ Accumulator // 1 LSR A work directly on accumulator +, Implied // 1 BRK +, Immediate // 2 LDA #10 8-bit constant in instruction +, ZeroPage // 2 LDA $00 zero-page address +, ZeroPageX // 2 LDA $80,X address is X register + 8-bit constant +, ZeroPageY // 2 LDX $10,Y address is Y register + 8-bit constant +, Relative // 2 BNE LABEL branch target as signed relative offset +, Absolute // 3 JMP $1000 full 16-bit address +, AbsoluteX // 3 STA $1000,X full 16-bit address plus X register +, AbsoluteY // 3 STA $1000,Y full 16-bit address plus Y register +, Indirect // 3 JMP ($1000) jump to address stored at address +, IndexedIndirectX // 2 LDA ($10,X) load from address stored at (constant + // zero page address plus X register) +, IndirectIndexedY // 2 LDA ($10),Y load from (address stored at constant + // zero page address) plus Y register +} + +fn arr_to_addr(arr: &[u8]) -> Address { + debug_assert!(arr.len() == 2); + + let x = (arr[0] as u16) + (arr[1] as u16 << 8u); + Address(x) +} + +impl AddressingMode { + pub fn extra_bytes(self) -> AddressDiff { + let x = match self { + Accumulator => 0, + Implied => 0, + Immediate => 1, + ZeroPage => 1, + ZeroPageX => 1, + ZeroPageY => 1, + Relative => 1, + Absolute => 2, + AbsoluteX => 2, + AbsoluteY => 2, + Indirect => 2, + IndexedIndirectX => 1, + IndirectIndexedY => 1, + }; + AddressDiff(x) + } + + pub fn process(self, machine: &Machine, arr: &[u8]) -> AMOut { + + debug_assert!({let AddressDiff(x) = self.extra_bytes(); + arr.len() == x as uint}); + + let x = machine.registers.index_x as u8; + let y = machine.registers.index_y as u8; + + let memory = &machine.memory; + + match self { + Accumulator | Implied => { + // Always the same -- no input + UseImplied + }, + Immediate => { + // Use [u8, ..1] specified in instruction as input + UseImmediate(arr[0]) + }, + ZeroPage => { + // Use [u8, ..1] from instruction + // Interpret as zero page address + // (Output: an 8-bit zero-page address) + UseAddress(Address(arr[0] as u16)) + }, + ZeroPageX => { + // Use [u8, ..1] from instruction + // Add to X register (as u8 -- the final address is in 0-page) + // (Output: an 8-bit zero-page address) + UseAddress(Address((arr[0] + x) as u16)) + }, + ZeroPageY => { + // Use [u8, ..1] from instruction + // Add to Y register (as u8 -- the final address is in 0-page) + // (Output: an 8-bit zero-page address) + UseAddress(Address((arr[0] + y) as u16)) + }, + Relative => { + // Use [u8, ..1] from instruction + // (interpret as relative...) + UseRelative(arr[0] as i8) + }, + Absolute => { + // Use [u8, ..2] from instruction as address + // (Output: a 16-bit address) + UseAddress(arr_to_addr(arr)) + }, + AbsoluteX => { + // Use [u8, ..2] from instruction as address, add X + // (Output: a 16-bit address) + UseAddress(arr_to_addr(arr) + AddressDiff(x as u16)) + }, + AbsoluteY => { + // Use [u8, ..2] from instruction as address, add Y + // (Output: a 16-bit address) + UseAddress(arr_to_addr(arr) + AddressDiff(y as u16)) + }, + Indirect => { + // Use [u8, ..2] from instruction as an address. Interpret the + // two bytes starting at that address as an address. + // (Output: a 16-bit address) + let slice = memory.get_slice(arr_to_addr(arr), AddressDiff(2)); + UseAddress(arr_to_addr(slice)) + }, + IndexedIndirectX => { + // Use [u8, ..1] from instruction + // Add to X register with 0-page wraparound, like ZeroPageX. + // This is where the absolute (16-bit) target address is stored. + // (Output: a 16-bit address) + let start = arr[0] + x; + let slice = memory.get_slice(Address(start as u16), + AddressDiff(2)); + UseAddress(arr_to_addr(slice)) + }, + IndirectIndexedY => { + // Use [u8, ..1] from instruction + // This is where the absolute (16-bit) target address is stored. + // Add Y register to this address to get the final address + // (Output: a 16-bit address) + let start = arr[0]; + let slice = memory.get_slice(Address(start as u16), + AddressDiff(2)); + UseAddress(arr_to_addr(slice) + AddressDiff(y as u16)) + }, + } + } +} + +pub type DecodedInstr = (Instruction, AMOut); + +pub static g_opcodes: [Option<(Instruction, AddressingMode)>, ..256] = [ +/*0x00*/ Some((BRK, Implied)), +/*0x01*/ Some((ORA, IndexedIndirectX)), +/*0x02*/ None, +/*0x03*/ None, +/*0x04*/ None, +/*0x05*/ Some((ORA, ZeroPage)), +/*0x06*/ Some((ASL, ZeroPage)), +/*0x07*/ None, +/*0x08*/ Some((PHP, Implied)), +/*0x09*/ Some((ORA, Immediate)), +/*0x0A*/ Some((ASL, Accumulator)), +/*0x0B*/ None, +/*0x0C*/ None, +/*0x0D*/ Some((ORA, Absolute)), +/*0x0E*/ Some((ASL, Absolute)), +/*0x0F*/ None, +/*0x10*/ Some((BPL, Relative)), +/*0x11*/ Some((ORA, IndirectIndexedY)), +/*0x12*/ None, +/*0x13*/ None, +/*0x14*/ None, +/*0x15*/ Some((ORA, ZeroPageX)), +/*0x16*/ Some((ASL, ZeroPageX)), +/*0x17*/ None, +/*0x18*/ Some((CLC, Implied)), +/*0x19*/ Some((ORA, AbsoluteY)), +/*0x1A*/ None, +/*0x1B*/ None, +/*0x1C*/ None, +/*0x1D*/ Some((ORA, AbsoluteX)), +/*0x1E*/ Some((ASL, AbsoluteX)), +/*0x1F*/ None, +/*0x20*/ Some((JSR, Absolute)), +/*0x21*/ Some((AND, IndexedIndirectX)), +/*0x22*/ None, +/*0x23*/ None, +/*0x24*/ Some((BIT, ZeroPage)), +/*0x25*/ Some((AND, ZeroPage)), +/*0x26*/ Some((ROL, ZeroPage)), +/*0x27*/ None, +/*0x28*/ Some((PLP, Implied)), +/*0x29*/ Some((AND, Immediate)), +/*0x2A*/ Some((ROL, Accumulator)), +/*0x2B*/ None, +/*0x2C*/ Some((BIT, Absolute)), +/*0x2D*/ Some((AND, Absolute)), +/*0x2E*/ Some((ROL, Absolute)), +/*0x2F*/ None, +/*0x30*/ Some((BMI, Relative)), +/*0x31*/ Some((AND, IndirectIndexedY)), +/*0x32*/ None, +/*0x33*/ None, +/*0x34*/ None, +/*0x35*/ Some((AND, ZeroPageX)), +/*0x36*/ Some((ROL, ZeroPageX)), +/*0x37*/ None, +/*0x38*/ Some((SEC, Implied)), +/*0x39*/ Some((AND, AbsoluteY)), +/*0x3A*/ None, +/*0x3B*/ None, +/*0x3C*/ None, +/*0x3D*/ Some((AND, AbsoluteX)), +/*0x3E*/ Some((ROL, AbsoluteX)), +/*0x3F*/ None, +/*0x40*/ Some((RTI, Implied)), +/*0x41*/ Some((EOR, IndexedIndirectX)), +/*0x42*/ None, +/*0x43*/ None, +/*0x44*/ None, +/*0x45*/ Some((EOR, ZeroPage)), +/*0x46*/ Some((LSR, ZeroPage)), +/*0x47*/ None, +/*0x48*/ Some((PHA, Implied)), +/*0x49*/ Some((EOR, Immediate)), +/*0x4A*/ Some((LSR, Accumulator)), +/*0x4B*/ None, +/*0x4C*/ Some((JMP, Absolute)), +/*0x4D*/ Some((EOR, Absolute)), +/*0x4E*/ Some((LSR, Absolute)), +/*0x4F*/ None, +/*0x50*/ Some((BVC, Relative)), +/*0x51*/ Some((EOR, IndirectIndexedY)), +/*0x52*/ None, +/*0x53*/ None, +/*0x54*/ None, +/*0x55*/ Some((EOR, ZeroPageX)), +/*0x56*/ Some((LSR, ZeroPageX)), +/*0x57*/ None, +/*0x58*/ None, +/*0x59*/ Some((EOR, AbsoluteY)), +/*0x5A*/ None, +/*0x5B*/ None, +/*0x5C*/ None, +/*0x5D*/ Some((EOR, AbsoluteX)), +/*0x5E*/ Some((LSR, AbsoluteX)), +/*0x5F*/ None, +/*0x60*/ Some((RTS, Implied)), +/*0x61*/ Some((ADC, IndexedIndirectX)), +/*0x62*/ None, +/*0x63*/ None, +/*0x64*/ None, +/*0x65*/ Some((ADC, ZeroPage)), +/*0x66*/ Some((ROR, ZeroPage)), +/*0x67*/ None, +/*0x68*/ Some((PLA, Implied)), +/*0x69*/ Some((ADC, Immediate)), +/*0x6A*/ Some((ROR, Accumulator)), +/*0x6B*/ None, +/*0x6C*/ Some((JMP, Indirect)), +/*0x6D*/ Some((ADC, Absolute)), +/*0x6E*/ Some((ROR, Absolute)), +/*0x6F*/ None, +/*0x70*/ Some((BVS, Relative)), +/*0x71*/ Some((ADC, IndirectIndexedY)), +/*0x72*/ None, +/*0x73*/ None, +/*0x74*/ None, +/*0x75*/ Some((ADC, ZeroPageX)), +/*0x76*/ Some((ROR, ZeroPageX)), +/*0x77*/ None, +/*0x78*/ Some((SEI, Implied)), +/*0x79*/ Some((ADC, AbsoluteY)), +/*0x7A*/ None, +/*0x7B*/ None, +/*0x7C*/ None, +/*0x7D*/ Some((ADC, AbsoluteX)), +/*0x7E*/ Some((ROR, AbsoluteX)), +/*0x7F*/ None, +/*0x80*/ None, +/*0x81*/ Some((STA, IndexedIndirectX)), +/*0x82*/ None, +/*0x83*/ None, +/*0x84*/ Some((STY, ZeroPage)), +/*0x85*/ Some((STA, ZeroPage)), +/*0x86*/ Some((STX, ZeroPage)), +/*0x87*/ None, +/*0x88*/ Some((DEY, Implied)), +/*0x89*/ None, +/*0x8A*/ Some((TXA, Implied)), +/*0x8B*/ None, +/*0x8C*/ Some((STY, Absolute)), +/*0x8D*/ Some((STA, Absolute)), +/*0x8E*/ Some((STX, Absolute)), +/*0x8F*/ None, +/*0x90*/ Some((BCC, Relative)), +/*0x91*/ Some((STA, IndirectIndexedY)), +/*0x92*/ None, +/*0x93*/ None, +/*0x94*/ Some((STY, ZeroPageX)), +/*0x95*/ Some((STA, ZeroPageX)), +/*0x96*/ Some((STX, ZeroPageY)), +/*0x97*/ None, +/*0x98*/ Some((TYA, Implied)), +/*0x99*/ Some((STA, AbsoluteY)), +/*0x9A*/ Some((TXS, Implied)), +/*0x9B*/ None, +/*0x9C*/ None, +/*0x9D*/ Some((STA, AbsoluteX)), +/*0x9E*/ None, +/*0x9F*/ None, +/*0xA0*/ Some((LDY, Immediate)), +/*0xA1*/ Some((LDA, IndexedIndirectX)), +/*0xA2*/ Some((LDX, Immediate)), +/*0xA3*/ None, +/*0xA4*/ Some((LDY, ZeroPage)), +/*0xA5*/ Some((LDA, ZeroPage)), +/*0xA6*/ Some((LDX, ZeroPage)), +/*0xA7*/ None, +/*0xA8*/ Some((TAY, Implied)), +/*0xA9*/ Some((LDA, Immediate)), +/*0xAA*/ Some((TAX, Implied)), +/*0xAB*/ None, +/*0xAC*/ Some((LDY, Absolute)), +/*0xAD*/ Some((LDA, Absolute)), +/*0xAE*/ Some((LDX, Absolute)), +/*0xAF*/ None, +/*0xB0*/ Some((BCS, Relative)), +/*0xB1*/ Some((LDA, IndirectIndexedY)), +/*0xB2*/ None, +/*0xB3*/ None, +/*0xB4*/ Some((LDY, ZeroPageX)), +/*0xB5*/ Some((LDA, ZeroPageX)), +/*0xB6*/ Some((LDX, ZeroPageY)), +/*0xB7*/ None, +/*0xB8*/ Some((CLV, Implied)), +/*0xB9*/ Some((LDA, AbsoluteY)), +/*0xBA*/ Some((TSX, Implied)), +/*0xBB*/ None, +/*0xBC*/ Some((LDY, AbsoluteX)), +/*0xBD*/ Some((LDA, AbsoluteX)), +/*0xBE*/ Some((LDX, AbsoluteY)), +/*0xBF*/ None, +/*0xC0*/ Some((CPY, Immediate)), +/*0xC1*/ Some((CMP, IndexedIndirectX)), +/*0xC2*/ None, +/*0xC3*/ None, +/*0xC4*/ Some((CPY, ZeroPage)), +/*0xC5*/ Some((CMP, ZeroPage)), +/*0xC6*/ Some((DEC, ZeroPage)), +/*0xC7*/ None, +/*0xC8*/ Some((INY, Implied)), +/*0xC9*/ Some((CMP, Immediate)), +/*0xCA*/ Some((DEX, Implied)), +/*0xCB*/ None, +/*0xCC*/ Some((CPY, Absolute)), +/*0xCD*/ Some((CMP, Absolute)), +/*0xCE*/ Some((DEC, Absolute)), +/*0xCF*/ None, +/*0xD0*/ Some((BNE, Relative)), +/*0xD1*/ Some((CMP, IndirectIndexedY)), +/*0xD2*/ None, +/*0xD3*/ None, +/*0xD4*/ None, +/*0xD5*/ Some((CMP, ZeroPageX)), +/*0xD6*/ Some((DEC, ZeroPageX)), +/*0xD7*/ None, +/*0xD8*/ Some((CLD, Implied)), +/*0xD9*/ Some((CMP, AbsoluteY)), +/*0xDA*/ None, +/*0xDB*/ None, +/*0xDC*/ None, +/*0xDD*/ Some((CMP, AbsoluteX)), +/*0xDE*/ Some((DEC, AbsoluteX)), +/*0xDF*/ None, +/*0xE0*/ Some((CPX, Immediate)), +/*0xE1*/ Some((SBC, IndexedIndirectX)), +/*0xE2*/ None, +/*0xE3*/ None, +/*0xE4*/ Some((CPX, ZeroPage)), +/*0xE5*/ Some((SBC, ZeroPage)), +/*0xE6*/ Some((INC, ZeroPage)), +/*0xE7*/ None, +/*0xE8*/ Some((INX, Implied)), +/*0xE9*/ Some((SBC, Immediate)), +/*0xEA*/ Some((NOP, Implied)), +/*0xEB*/ None, +/*0xEC*/ Some((CPX, Absolute)), +/*0xED*/ Some((SBC, Absolute)), +/*0xEE*/ Some((INC, Absolute)), +/*0xEF*/ None, +/*0xF0*/ Some((BEQ, Relative)), +/*0xF1*/ Some((SBC, IndirectIndexedY)), +/*0xF2*/ None, +/*0xF3*/ None, +/*0xF4*/ None, +/*0xF5*/ Some((SBC, ZeroPageX)), +/*0xF6*/ Some((INC, ZeroPageX)), +/*0xF7*/ None, +/*0xF8*/ Some((SED, Implied)), +/*0xF9*/ Some((SBC, AbsoluteY)), +/*0xFA*/ None, +/*0xFB*/ None, +/*0xFC*/ None, +/*0xFD*/ Some((SBC, AbsoluteX)), +/*0xFE*/ Some((INC, AbsoluteX)), +/*0xFF*/ None, +]; + diff --git a/src/lib.rs b/src/lib.rs index bda0793..06e8e8b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,6 +25,19 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +// JAM: 'if let' syntax is great for situations where want to match only a +// single pattern and ignore all others. +// +// if let Some(x) = foo() { ... } +// +#![feature(if_let)] + +// Needed for log! macro +#![feature(phase)] + +#[phase(plugin, link)] +extern crate log; + pub mod address; pub mod instruction; pub mod machine; diff --git a/src/machine.rs b/src/machine.rs index 5886425..e9f70f9 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -25,10 +25,13 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -use address::AddressDiff; -use std::fmt; -use instruction::Instruction; -use instruction::{ADC, NOP}; +use log; + +use std; + +use address::{AddressDiff}; +use instruction; +use instruction::{DecodedInstr}; use memory::Memory; use registers::{ Registers, Status, StatusArgs }; use registers::{ ps_negative, ps_overflow, ps_zero, ps_carry }; @@ -50,35 +53,126 @@ impl Machine { *self = Machine::new(); } - pub fn fetch_instruction(&mut self) -> i8 { - let instr = self.memory.get_byte(&self.registers.program_counter); + pub fn fetch_next_and_decode(&mut self) -> Option { + let x: u8 = self.memory.get_byte(self.registers.program_counter); - // Will need smarter logic to fetch the correct number of bytes - // for instruction - self.registers.program_counter = self.registers.program_counter + AddressDiff(1); - instr as i8 - } + match instruction::g_opcodes[x as uint] { + Some((instr, am)) => { + let extra_bytes = am.extra_bytes(); + let num_bytes = AddressDiff(1) + extra_bytes; - pub fn decode_instruction(&mut self, raw_instruction: i8) -> Instruction { - match raw_instruction { - 0x69 => ADC(self.fetch_instruction()), - _ => NOP - } - } - - pub fn execute_instruction(&mut self, instruction: Instruction) { - match instruction { - ADC(immediate) => { - println!("executing add with carry"); - self.add_with_carry(immediate); - }, - NOP => { - println!("nop instr"); + let data_start = self.registers.program_counter + + AddressDiff(1); + + let slice = self.memory.get_slice(data_start, extra_bytes); + let am_out = am.process(self, slice); + + // Increment program counter + self.registers.program_counter = + self.registers.program_counter + num_bytes; + + Some((instr, am_out)) } - _ => println!("attempting to execute unimplemented instruction") + _ => None + } + } + + pub fn execute_instruction(&mut self, decoded_instr: DecodedInstr) { + match decoded_instr { + (instruction::ADC, instruction::UseImmediate(val)) => { + log!(log::DEBUG, "add with carry immediate: {}", val); + self.add_with_carry(val as i8); + }, + (instruction::ADC, instruction::UseAddress(addr)) => { + let val = self.memory.get_byte(addr) as i8; + log!(log::DEBUG, "add with carry. address: {}. value: {}", + addr, val); + self.add_with_carry(val); + }, + + (instruction::LDA, instruction::UseImmediate(val)) => { + log!(log::DEBUG, "load A immediate: {}", val); + self.load_accumulator(val as i8); + }, + (instruction::LDA, instruction::UseAddress(addr)) => { + let val = self.memory.get_byte(addr); + log!(log::DEBUG, "load A. address: {}. value: {}", addr, val); + self.load_accumulator(val as i8); + }, + + (instruction::LDX, instruction::UseImmediate(val)) => { + log!(log::DEBUG, "load X immediate: {}", val); + self.load_x_register(val as i8); + }, + (instruction::LDX, instruction::UseAddress(addr)) => { + let val = self.memory.get_byte(addr); + log!(log::DEBUG, "load X. address: {}. value: {}", addr, val); + self.load_x_register(val as i8); + }, + + (instruction::LDY, instruction::UseImmediate(val)) => { + log!(log::DEBUG, "load Y immediate: {}", val); + self.load_y_register(val as i8); + }, + (instruction::LDY, instruction::UseAddress(addr)) => { + let val = self.memory.get_byte(addr); + log!(log::DEBUG, "load Y. address: {}. value: {}", addr, val); + self.load_y_register(val as i8); + }, + + (instruction::NOP, _) => { + log!(log::DEBUG, "nop instr"); + }, + (_, _) => { + log!(log::DEBUG, "attempting to execute unimplemented \ + instruction"); + }, }; } - + + pub fn run(&mut self) { + loop { + if let Some(decoded_instr) = self.fetch_next_and_decode() { + self.execute_instruction(decoded_instr); + } else { + break + } + } + } + + fn load_register_with_flags(register: &mut i8, + status: &mut Status, + value: i8) { + *register = value; + + let is_zero = value == 0; + let is_negative = value < 0; + + status.set_with_mask( + ps_zero | ps_negative, + Status::new(StatusArgs { zero: is_zero, + negative: is_negative, + ..StatusArgs::none() } )); + } + + pub fn load_x_register(&mut self, value: i8) { + Machine::load_register_with_flags(&mut self.registers.index_x, + &mut self.registers.status, + value); + } + + pub fn load_y_register(&mut self, value: i8) { + Machine::load_register_with_flags(&mut self.registers.index_y, + &mut self.registers.status, + value); + } + + pub fn load_accumulator(&mut self, value: i8) { + Machine::load_register_with_flags(&mut self.registers.accumulator, + &mut self.registers.status, + value); + } + // TODO akeeton: Implement binary-coded decimal. pub fn add_with_carry(&mut self, value: i8) { let a_before: i8 = self.registers.accumulator; @@ -90,28 +184,27 @@ impl Machine { let did_carry = (a_after as u8) < (a_before as u8); - let is_zero = a_after == 0; - let is_negative = a_after < 0; let did_overflow = (a_before < 0 && value < 0 && a_after >= 0) || (a_before > 0 && value > 0 && a_after <= 0); - let mask = ps_carry | ps_zero | ps_negative | ps_overflow; + let mask = ps_carry | ps_overflow; self.registers.status.set_with_mask(mask, Status::new(StatusArgs { carry: did_carry, - zero: is_zero, - negative: is_negative, overflow: did_overflow, ..StatusArgs::none() } )); - self.registers.accumulator = a_after; + self.load_accumulator(a_after); + + log!(log::DEBUG, "accumulator: {}", self.registers.accumulator); } } -impl fmt::Show for Machine { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "Machine Dump:\n\nAccumulator: {}", self.registers.accumulator) +impl std::fmt::Show for Machine { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "Machine Dump:\n\nAccumulator: {}", + self.registers.accumulator) } } diff --git a/src/memory.rs b/src/memory.rs index ddc9031..db6b5c2 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -25,7 +25,7 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -use address::Address; +use address::{Address, AddressDiff}; // JAM: We can probably come up with a better way to represent address ranges. // Address range type? @@ -56,21 +56,41 @@ impl Memory { pub fn new() -> Memory { Memory { bytes: [0, ..MEMORY_SIZE] } } - - pub fn get_byte(&self, address: &Address) -> u8 { + + pub fn get_byte(&self, address: Address) -> u8 { self.bytes[address.to_uint()] } - + + pub fn get_slice(&self, Address(start): Address, + AddressDiff(diff): AddressDiff) -> &[u8] { + let start = start as uint; + let diff = diff as uint; + let end = start + diff; + self.bytes.slice(start, end) + } + // Sets the byte at the given address to the given value and returns the // previous value at the address. - pub fn set_byte(&mut self, address: &Address, value: u8) -> u8 { + pub fn set_byte(&mut self, address: Address, value: u8) -> u8 { let old_value = self.get_byte(address); self.bytes[address.to_uint()] = value; - - return old_value; + old_value } - + + pub fn set_bytes(&mut self, Address(start): Address, values: &[u8]) { + let start = start as uint; + + // This panics if the range is invalid + let slice = self.bytes.slice_mut(start, start + values.len()); + + // JAM: Is this the best way to do this copy? + for (dest, src) in slice.iter_mut().zip(values.iter()) { + *dest = *src; + } + } + pub fn is_stack_address(address: &Address) -> bool { STACK_ADDRESS_LO <= *address && *address <= STACK_ADDRESS_HI } } +