From 6a9ce4e12e29e7fe9e781031f9f51858d425730d Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Mon, 13 Nov 2017 23:34:31 +0100 Subject: [PATCH] Rename machine to cpu and the crate to mos6502 --- Cargo.toml | 8 +- src/bin/main.rs | 36 +- src/instruction.rs | 1065 ++++++++++++++++++------------------ src/lib.rs | 2 +- src/machine.rs | 1295 -------------------------------------------- 5 files changed, 546 insertions(+), 1860 deletions(-) delete mode 100644 src/machine.rs diff --git a/Cargo.toml b/Cargo.toml index 10a86ac..0f0cad5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,17 +26,17 @@ # POSSIBILITY OF SUCH DAMAGE. [package] -name = "emu6502" +name = "mos6502" version = "0.0.1" authors = ["The 6502-rs Developers"] [lib] # This will look in src/lib.rs -name = "emu6502" +name = "mos6502" [[bin]] -# This will look in src/bin/emu6502.rs -name = "emu6502" +# This will look in src/bin/mos6502.rs +name = "mos6502" [dependencies] bitflags = "0.9.1" diff --git a/src/bin/main.rs b/src/bin/main.rs index a9b7322..22e0586 100644 --- a/src/bin/main.rs +++ b/src/bin/main.rs @@ -25,17 +25,17 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -extern crate emu6502; +extern crate mos6502; #[cfg(not(test))] -use emu6502::machine; +use mos6502::cpu; #[cfg(not(test))] -use emu6502::address::Address; +use mos6502::address::Address; #[cfg(not(test))] fn main() { - let mut machine = machine::Machine::new(); + let mut cpu = cpu::CPU::new(); // "Load" a program @@ -67,51 +67,36 @@ fn main() { // 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! ]; @@ -143,14 +128,13 @@ fn main() { 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); + cpu.memory.set_bytes(Address(0x0000), &zero_page_data); + cpu.memory.set_bytes(Address(0x4000), &program); + cpu.memory.set_bytes(Address(0x8000), &data); - machine.registers.program_counter = Address(0x4000); + cpu.registers.program_counter = Address(0x4000); - machine.run(); + cpu.run(); - println!("{:?}", machine); + println!("{:?}", cpu); } - diff --git a/src/instruction.rs b/src/instruction.rs index a3c52cd..cf77feb 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -27,7 +27,7 @@ use address::Address; use address::AddressDiff; -use machine::Machine; +use cpu::CPU; // Abbreviations // @@ -119,17 +119,17 @@ pub enum OpInput { #[derive(Copy, Clone)] pub enum AddressingMode { - 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 + 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 @@ -163,21 +163,19 @@ impl AddressingMode { AddressDiff(x) } - pub fn process(self, machine: &Machine, arr: &[u8]) -> OpInput { - + pub fn process(self, cpu: &CPU, arr: &[u8]) -> OpInput { debug_assert!({ let AddressDiff(x) = self.extra_bytes(); arr.len() == x as usize }); - let x = machine.registers.index_x as u8; - let y = machine.registers.index_y as u8; + let x = cpu.registers.index_x as u8; + let y = cpu.registers.index_y as u8; - let memory = &machine.memory; + let memory = &cpu.memory; match self { - AddressingMode::Accumulator | - AddressingMode::Implied => { + AddressingMode::Accumulator | AddressingMode::Implied => { // Always the same -- no input OpInput::UseImplied } @@ -254,518 +252,517 @@ impl AddressingMode { pub type DecodedInstr = (Instruction, OpInput); -pub static OPCODES: [Option<(Instruction, AddressingMode)>; 256] = - [ - /*0x00*/ - Some((Instruction::BRK, AddressingMode::Implied)), - /*0x01*/ - Some((Instruction::ORA, AddressingMode::IndexedIndirectX)), - /*0x02*/ - None, - /*0x03*/ - None, - /*0x04*/ - None, - /*0x05*/ - Some((Instruction::ORA, AddressingMode::ZeroPage)), - /*0x06*/ - Some((Instruction::ASL, AddressingMode::ZeroPage)), - /*0x07*/ - None, - /*0x08*/ - Some((Instruction::PHP, AddressingMode::Implied)), - /*0x09*/ - Some((Instruction::ORA, AddressingMode::Immediate)), - /*0x0A*/ - Some((Instruction::ASL, AddressingMode::Accumulator)), - /*0x0B*/ - None, - /*0x0C*/ - None, - /*0x0D*/ - Some((Instruction::ORA, AddressingMode::Absolute)), - /*0x0E*/ - Some((Instruction::ASL, AddressingMode::Absolute)), - /*0x0F*/ - None, - /*0x10*/ - Some((Instruction::BPL, AddressingMode::Relative)), - /*0x11*/ - Some((Instruction::ORA, AddressingMode::IndirectIndexedY)), - /*0x12*/ - None, - /*0x13*/ - None, - /*0x14*/ - None, - /*0x15*/ - Some((Instruction::ORA, AddressingMode::ZeroPageX)), - /*0x16*/ - Some((Instruction::ASL, AddressingMode::ZeroPageX)), - /*0x17*/ - None, - /*0x18*/ - Some((Instruction::CLC, AddressingMode::Implied)), - /*0x19*/ - Some((Instruction::ORA, AddressingMode::AbsoluteY)), - /*0x1A*/ - None, - /*0x1B*/ - None, - /*0x1C*/ - None, - /*0x1D*/ - Some((Instruction::ORA, AddressingMode::AbsoluteX)), - /*0x1E*/ - Some((Instruction::ASL, AddressingMode::AbsoluteX)), - /*0x1F*/ - None, - /*0x20*/ - Some((Instruction::JSR, AddressingMode::Absolute)), - /*0x21*/ - Some((Instruction::AND, AddressingMode::IndexedIndirectX)), - /*0x22*/ - None, - /*0x23*/ - None, - /*0x24*/ - Some((Instruction::BIT, AddressingMode::ZeroPage)), - /*0x25*/ - Some((Instruction::AND, AddressingMode::ZeroPage)), - /*0x26*/ - Some((Instruction::ROL, AddressingMode::ZeroPage)), - /*0x27*/ - None, - /*0x28*/ - Some((Instruction::PLP, AddressingMode::Implied)), - /*0x29*/ - Some((Instruction::AND, AddressingMode::Immediate)), - /*0x2A*/ - Some((Instruction::ROL, AddressingMode::Accumulator)), - /*0x2B*/ - None, - /*0x2C*/ - Some((Instruction::BIT, AddressingMode::Absolute)), - /*0x2D*/ - Some((Instruction::AND, AddressingMode::Absolute)), - /*0x2E*/ - Some((Instruction::ROL, AddressingMode::Absolute)), - /*0x2F*/ - None, - /*0x30*/ - Some((Instruction::BMI, AddressingMode::Relative)), - /*0x31*/ - Some((Instruction::AND, AddressingMode::IndirectIndexedY)), - /*0x32*/ - None, - /*0x33*/ - None, - /*0x34*/ - None, - /*0x35*/ - Some((Instruction::AND, AddressingMode::ZeroPageX)), - /*0x36*/ - Some((Instruction::ROL, AddressingMode::ZeroPageX)), - /*0x37*/ - None, - /*0x38*/ - Some((Instruction::SEC, AddressingMode::Implied)), - /*0x39*/ - Some((Instruction::AND, AddressingMode::AbsoluteY)), - /*0x3A*/ - None, - /*0x3B*/ - None, - /*0x3C*/ - None, - /*0x3D*/ - Some((Instruction::AND, AddressingMode::AbsoluteX)), - /*0x3E*/ - Some((Instruction::ROL, AddressingMode::AbsoluteX)), - /*0x3F*/ - None, - /*0x40*/ - Some((Instruction::RTI, AddressingMode::Implied)), - /*0x41*/ - Some((Instruction::EOR, AddressingMode::IndexedIndirectX)), - /*0x42*/ - None, - /*0x43*/ - None, - /*0x44*/ - None, - /*0x45*/ - Some((Instruction::EOR, AddressingMode::ZeroPage)), - /*0x46*/ - Some((Instruction::LSR, AddressingMode::ZeroPage)), - /*0x47*/ - None, - /*0x48*/ - Some((Instruction::PHA, AddressingMode::Implied)), - /*0x49*/ - Some((Instruction::EOR, AddressingMode::Immediate)), - /*0x4A*/ - Some((Instruction::LSR, AddressingMode::Accumulator)), - /*0x4B*/ - None, - /*0x4C*/ - Some((Instruction::JMP, AddressingMode::Absolute)), - /*0x4D*/ - Some((Instruction::EOR, AddressingMode::Absolute)), - /*0x4E*/ - Some((Instruction::LSR, AddressingMode::Absolute)), - /*0x4F*/ - None, - /*0x50*/ - Some((Instruction::BVC, AddressingMode::Relative)), - /*0x51*/ - Some((Instruction::EOR, AddressingMode::IndirectIndexedY)), - /*0x52*/ - None, - /*0x53*/ - None, - /*0x54*/ - None, - /*0x55*/ - Some((Instruction::EOR, AddressingMode::ZeroPageX)), - /*0x56*/ - Some((Instruction::LSR, AddressingMode::ZeroPageX)), - /*0x57*/ - None, - /*0x58*/ - None, - /*0x59*/ - Some((Instruction::EOR, AddressingMode::AbsoluteY)), - /*0x5A*/ - None, - /*0x5B*/ - None, - /*0x5C*/ - None, - /*0x5D*/ - Some((Instruction::EOR, AddressingMode::AbsoluteX)), - /*0x5E*/ - Some((Instruction::LSR, AddressingMode::AbsoluteX)), - /*0x5F*/ - None, - /*0x60*/ - Some((Instruction::RTS, AddressingMode::Implied)), - /*0x61*/ - Some((Instruction::ADC, AddressingMode::IndexedIndirectX)), - /*0x62*/ - None, - /*0x63*/ - None, - /*0x64*/ - None, - /*0x65*/ - Some((Instruction::ADC, AddressingMode::ZeroPage)), - /*0x66*/ - Some((Instruction::ROR, AddressingMode::ZeroPage)), - /*0x67*/ - None, - /*0x68*/ - Some((Instruction::PLA, AddressingMode::Implied)), - /*0x69*/ - Some((Instruction::ADC, AddressingMode::Immediate)), - /*0x6A*/ - Some((Instruction::ROR, AddressingMode::Accumulator)), - /*0x6B*/ - None, - /*0x6C*/ - Some((Instruction::JMP, AddressingMode::Indirect)), - /*0x6D*/ - Some((Instruction::ADC, AddressingMode::Absolute)), - /*0x6E*/ - Some((Instruction::ROR, AddressingMode::Absolute)), - /*0x6F*/ - None, - /*0x70*/ - Some((Instruction::BVS, AddressingMode::Relative)), - /*0x71*/ - Some((Instruction::ADC, AddressingMode::IndirectIndexedY)), - /*0x72*/ - None, - /*0x73*/ - None, - /*0x74*/ - None, - /*0x75*/ - Some((Instruction::ADC, AddressingMode::ZeroPageX)), - /*0x76*/ - Some((Instruction::ROR, AddressingMode::ZeroPageX)), - /*0x77*/ - None, - /*0x78*/ - Some((Instruction::SEI, AddressingMode::Implied)), - /*0x79*/ - Some((Instruction::ADC, AddressingMode::AbsoluteY)), - /*0x7A*/ - None, - /*0x7B*/ - None, - /*0x7C*/ - None, - /*0x7D*/ - Some((Instruction::ADC, AddressingMode::AbsoluteX)), - /*0x7E*/ - Some((Instruction::ROR, AddressingMode::AbsoluteX)), - /*0x7F*/ - None, - /*0x80*/ - None, - /*0x81*/ - Some((Instruction::STA, AddressingMode::IndexedIndirectX)), - /*0x82*/ - None, - /*0x83*/ - None, - /*0x84*/ - Some((Instruction::STY, AddressingMode::ZeroPage)), - /*0x85*/ - Some((Instruction::STA, AddressingMode::ZeroPage)), - /*0x86*/ - Some((Instruction::STX, AddressingMode::ZeroPage)), - /*0x87*/ - None, - /*0x88*/ - Some((Instruction::DEY, AddressingMode::Implied)), - /*0x89*/ - None, - /*0x8A*/ - Some((Instruction::TXA, AddressingMode::Implied)), - /*0x8B*/ - None, - /*0x8C*/ - Some((Instruction::STY, AddressingMode::Absolute)), - /*0x8D*/ - Some((Instruction::STA, AddressingMode::Absolute)), - /*0x8E*/ - Some((Instruction::STX, AddressingMode::Absolute)), - /*0x8F*/ - None, - /*0x90*/ - Some((Instruction::BCC, AddressingMode::Relative)), - /*0x91*/ - Some((Instruction::STA, AddressingMode::IndirectIndexedY)), - /*0x92*/ - None, - /*0x93*/ - None, - /*0x94*/ - Some((Instruction::STY, AddressingMode::ZeroPageX)), - /*0x95*/ - Some((Instruction::STA, AddressingMode::ZeroPageX)), - /*0x96*/ - Some((Instruction::STX, AddressingMode::ZeroPageY)), - /*0x97*/ - None, - /*0x98*/ - Some((Instruction::TYA, AddressingMode::Implied)), - /*0x99*/ - Some((Instruction::STA, AddressingMode::AbsoluteY)), - /*0x9A*/ - Some((Instruction::TXS, AddressingMode::Implied)), - /*0x9B*/ - None, - /*0x9C*/ - None, - /*0x9D*/ - Some((Instruction::STA, AddressingMode::AbsoluteX)), - /*0x9E*/ - None, - /*0x9F*/ - None, - /*0xA0*/ - Some((Instruction::LDY, AddressingMode::Immediate)), - /*0xA1*/ - Some((Instruction::LDA, AddressingMode::IndexedIndirectX)), - /*0xA2*/ - Some((Instruction::LDX, AddressingMode::Immediate)), - /*0xA3*/ - None, - /*0xA4*/ - Some((Instruction::LDY, AddressingMode::ZeroPage)), - /*0xA5*/ - Some((Instruction::LDA, AddressingMode::ZeroPage)), - /*0xA6*/ - Some((Instruction::LDX, AddressingMode::ZeroPage)), - /*0xA7*/ - None, - /*0xA8*/ - Some((Instruction::TAY, AddressingMode::Implied)), - /*0xA9*/ - Some((Instruction::LDA, AddressingMode::Immediate)), - /*0xAA*/ - Some((Instruction::TAX, AddressingMode::Implied)), - /*0xAB*/ - None, - /*0xAC*/ - Some((Instruction::LDY, AddressingMode::Absolute)), - /*0xAD*/ - Some((Instruction::LDA, AddressingMode::Absolute)), - /*0xAE*/ - Some((Instruction::LDX, AddressingMode::Absolute)), - /*0xAF*/ - None, - /*0xB0*/ - Some((Instruction::BCS, AddressingMode::Relative)), - /*0xB1*/ - Some((Instruction::LDA, AddressingMode::IndirectIndexedY)), - /*0xB2*/ - None, - /*0xB3*/ - None, - /*0xB4*/ - Some((Instruction::LDY, AddressingMode::ZeroPageX)), - /*0xB5*/ - Some((Instruction::LDA, AddressingMode::ZeroPageX)), - /*0xB6*/ - Some((Instruction::LDX, AddressingMode::ZeroPageY)), - /*0xB7*/ - None, - /*0xB8*/ - Some((Instruction::CLV, AddressingMode::Implied)), - /*0xB9*/ - Some((Instruction::LDA, AddressingMode::AbsoluteY)), - /*0xBA*/ - Some((Instruction::TSX, AddressingMode::Implied)), - /*0xBB*/ - None, - /*0xBC*/ - Some((Instruction::LDY, AddressingMode::AbsoluteX)), - /*0xBD*/ - Some((Instruction::LDA, AddressingMode::AbsoluteX)), - /*0xBE*/ - Some((Instruction::LDX, AddressingMode::AbsoluteY)), - /*0xBF*/ - None, - /*0xC0*/ - Some((Instruction::CPY, AddressingMode::Immediate)), - /*0xC1*/ - Some((Instruction::CMP, AddressingMode::IndexedIndirectX)), - /*0xC2*/ - None, - /*0xC3*/ - None, - /*0xC4*/ - Some((Instruction::CPY, AddressingMode::ZeroPage)), - /*0xC5*/ - Some((Instruction::CMP, AddressingMode::ZeroPage)), - /*0xC6*/ - Some((Instruction::DEC, AddressingMode::ZeroPage)), - /*0xC7*/ - None, - /*0xC8*/ - Some((Instruction::INY, AddressingMode::Implied)), - /*0xC9*/ - Some((Instruction::CMP, AddressingMode::Immediate)), - /*0xCA*/ - Some((Instruction::DEX, AddressingMode::Implied)), - /*0xCB*/ - None, - /*0xCC*/ - Some((Instruction::CPY, AddressingMode::Absolute)), - /*0xCD*/ - Some((Instruction::CMP, AddressingMode::Absolute)), - /*0xCE*/ - Some((Instruction::DEC, AddressingMode::Absolute)), - /*0xCF*/ - None, - /*0xD0*/ - Some((Instruction::BNE, AddressingMode::Relative)), - /*0xD1*/ - Some((Instruction::CMP, AddressingMode::IndirectIndexedY)), - /*0xD2*/ - None, - /*0xD3*/ - None, - /*0xD4*/ - None, - /*0xD5*/ - Some((Instruction::CMP, AddressingMode::ZeroPageX)), - /*0xD6*/ - Some((Instruction::DEC, AddressingMode::ZeroPageX)), - /*0xD7*/ - None, - /*0xD8*/ - Some((Instruction::CLD, AddressingMode::Implied)), - /*0xD9*/ - Some((Instruction::CMP, AddressingMode::AbsoluteY)), - /*0xDA*/ - None, - /*0xDB*/ - None, - /*0xDC*/ - None, - /*0xDD*/ - Some((Instruction::CMP, AddressingMode::AbsoluteX)), - /*0xDE*/ - Some((Instruction::DEC, AddressingMode::AbsoluteX)), - /*0xDF*/ - None, - /*0xE0*/ - Some((Instruction::CPX, AddressingMode::Immediate)), - /*0xE1*/ - Some((Instruction::SBC, AddressingMode::IndexedIndirectX)), - /*0xE2*/ - None, - /*0xE3*/ - None, - /*0xE4*/ - Some((Instruction::CPX, AddressingMode::ZeroPage)), - /*0xE5*/ - Some((Instruction::SBC, AddressingMode::ZeroPage)), - /*0xE6*/ - Some((Instruction::INC, AddressingMode::ZeroPage)), - /*0xE7*/ - None, - /*0xE8*/ - Some((Instruction::INX, AddressingMode::Implied)), - /*0xE9*/ - Some((Instruction::SBC, AddressingMode::Immediate)), - /*0xEA*/ - Some((Instruction::NOP, AddressingMode::Implied)), - /*0xEB*/ - None, - /*0xEC*/ - Some((Instruction::CPX, AddressingMode::Absolute)), - /*0xED*/ - Some((Instruction::SBC, AddressingMode::Absolute)), - /*0xEE*/ - Some((Instruction::INC, AddressingMode::Absolute)), - /*0xEF*/ - None, - /*0xF0*/ - Some((Instruction::BEQ, AddressingMode::Relative)), - /*0xF1*/ - Some((Instruction::SBC, AddressingMode::IndirectIndexedY)), - /*0xF2*/ - None, - /*0xF3*/ - None, - /*0xF4*/ - None, - /*0xF5*/ - Some((Instruction::SBC, AddressingMode::ZeroPageX)), - /*0xF6*/ - Some((Instruction::INC, AddressingMode::ZeroPageX)), - /*0xF7*/ - None, - /*0xF8*/ - Some((Instruction::SED, AddressingMode::Implied)), - /*0xF9*/ - Some((Instruction::SBC, AddressingMode::AbsoluteY)), - /*0xFA*/ - None, - /*0xFB*/ - None, - /*0xFC*/ - None, - /*0xFD*/ - Some((Instruction::SBC, AddressingMode::AbsoluteX)), - /*0xFE*/ - Some((Instruction::INC, AddressingMode::AbsoluteX)), - /*0xFF*/ - None, - ]; +pub static OPCODES: [Option<(Instruction, AddressingMode)>; 256] = [ + /*0x00*/ + Some((Instruction::BRK, AddressingMode::Implied)), + /*0x01*/ + Some((Instruction::ORA, AddressingMode::IndexedIndirectX)), + /*0x02*/ + None, + /*0x03*/ + None, + /*0x04*/ + None, + /*0x05*/ + Some((Instruction::ORA, AddressingMode::ZeroPage)), + /*0x06*/ + Some((Instruction::ASL, AddressingMode::ZeroPage)), + /*0x07*/ + None, + /*0x08*/ + Some((Instruction::PHP, AddressingMode::Implied)), + /*0x09*/ + Some((Instruction::ORA, AddressingMode::Immediate)), + /*0x0A*/ + Some((Instruction::ASL, AddressingMode::Accumulator)), + /*0x0B*/ + None, + /*0x0C*/ + None, + /*0x0D*/ + Some((Instruction::ORA, AddressingMode::Absolute)), + /*0x0E*/ + Some((Instruction::ASL, AddressingMode::Absolute)), + /*0x0F*/ + None, + /*0x10*/ + Some((Instruction::BPL, AddressingMode::Relative)), + /*0x11*/ + Some((Instruction::ORA, AddressingMode::IndirectIndexedY)), + /*0x12*/ + None, + /*0x13*/ + None, + /*0x14*/ + None, + /*0x15*/ + Some((Instruction::ORA, AddressingMode::ZeroPageX)), + /*0x16*/ + Some((Instruction::ASL, AddressingMode::ZeroPageX)), + /*0x17*/ + None, + /*0x18*/ + Some((Instruction::CLC, AddressingMode::Implied)), + /*0x19*/ + Some((Instruction::ORA, AddressingMode::AbsoluteY)), + /*0x1A*/ + None, + /*0x1B*/ + None, + /*0x1C*/ + None, + /*0x1D*/ + Some((Instruction::ORA, AddressingMode::AbsoluteX)), + /*0x1E*/ + Some((Instruction::ASL, AddressingMode::AbsoluteX)), + /*0x1F*/ + None, + /*0x20*/ + Some((Instruction::JSR, AddressingMode::Absolute)), + /*0x21*/ + Some((Instruction::AND, AddressingMode::IndexedIndirectX)), + /*0x22*/ + None, + /*0x23*/ + None, + /*0x24*/ + Some((Instruction::BIT, AddressingMode::ZeroPage)), + /*0x25*/ + Some((Instruction::AND, AddressingMode::ZeroPage)), + /*0x26*/ + Some((Instruction::ROL, AddressingMode::ZeroPage)), + /*0x27*/ + None, + /*0x28*/ + Some((Instruction::PLP, AddressingMode::Implied)), + /*0x29*/ + Some((Instruction::AND, AddressingMode::Immediate)), + /*0x2A*/ + Some((Instruction::ROL, AddressingMode::Accumulator)), + /*0x2B*/ + None, + /*0x2C*/ + Some((Instruction::BIT, AddressingMode::Absolute)), + /*0x2D*/ + Some((Instruction::AND, AddressingMode::Absolute)), + /*0x2E*/ + Some((Instruction::ROL, AddressingMode::Absolute)), + /*0x2F*/ + None, + /*0x30*/ + Some((Instruction::BMI, AddressingMode::Relative)), + /*0x31*/ + Some((Instruction::AND, AddressingMode::IndirectIndexedY)), + /*0x32*/ + None, + /*0x33*/ + None, + /*0x34*/ + None, + /*0x35*/ + Some((Instruction::AND, AddressingMode::ZeroPageX)), + /*0x36*/ + Some((Instruction::ROL, AddressingMode::ZeroPageX)), + /*0x37*/ + None, + /*0x38*/ + Some((Instruction::SEC, AddressingMode::Implied)), + /*0x39*/ + Some((Instruction::AND, AddressingMode::AbsoluteY)), + /*0x3A*/ + None, + /*0x3B*/ + None, + /*0x3C*/ + None, + /*0x3D*/ + Some((Instruction::AND, AddressingMode::AbsoluteX)), + /*0x3E*/ + Some((Instruction::ROL, AddressingMode::AbsoluteX)), + /*0x3F*/ + None, + /*0x40*/ + Some((Instruction::RTI, AddressingMode::Implied)), + /*0x41*/ + Some((Instruction::EOR, AddressingMode::IndexedIndirectX)), + /*0x42*/ + None, + /*0x43*/ + None, + /*0x44*/ + None, + /*0x45*/ + Some((Instruction::EOR, AddressingMode::ZeroPage)), + /*0x46*/ + Some((Instruction::LSR, AddressingMode::ZeroPage)), + /*0x47*/ + None, + /*0x48*/ + Some((Instruction::PHA, AddressingMode::Implied)), + /*0x49*/ + Some((Instruction::EOR, AddressingMode::Immediate)), + /*0x4A*/ + Some((Instruction::LSR, AddressingMode::Accumulator)), + /*0x4B*/ + None, + /*0x4C*/ + Some((Instruction::JMP, AddressingMode::Absolute)), + /*0x4D*/ + Some((Instruction::EOR, AddressingMode::Absolute)), + /*0x4E*/ + Some((Instruction::LSR, AddressingMode::Absolute)), + /*0x4F*/ + None, + /*0x50*/ + Some((Instruction::BVC, AddressingMode::Relative)), + /*0x51*/ + Some((Instruction::EOR, AddressingMode::IndirectIndexedY)), + /*0x52*/ + None, + /*0x53*/ + None, + /*0x54*/ + None, + /*0x55*/ + Some((Instruction::EOR, AddressingMode::ZeroPageX)), + /*0x56*/ + Some((Instruction::LSR, AddressingMode::ZeroPageX)), + /*0x57*/ + None, + /*0x58*/ + None, + /*0x59*/ + Some((Instruction::EOR, AddressingMode::AbsoluteY)), + /*0x5A*/ + None, + /*0x5B*/ + None, + /*0x5C*/ + None, + /*0x5D*/ + Some((Instruction::EOR, AddressingMode::AbsoluteX)), + /*0x5E*/ + Some((Instruction::LSR, AddressingMode::AbsoluteX)), + /*0x5F*/ + None, + /*0x60*/ + Some((Instruction::RTS, AddressingMode::Implied)), + /*0x61*/ + Some((Instruction::ADC, AddressingMode::IndexedIndirectX)), + /*0x62*/ + None, + /*0x63*/ + None, + /*0x64*/ + None, + /*0x65*/ + Some((Instruction::ADC, AddressingMode::ZeroPage)), + /*0x66*/ + Some((Instruction::ROR, AddressingMode::ZeroPage)), + /*0x67*/ + None, + /*0x68*/ + Some((Instruction::PLA, AddressingMode::Implied)), + /*0x69*/ + Some((Instruction::ADC, AddressingMode::Immediate)), + /*0x6A*/ + Some((Instruction::ROR, AddressingMode::Accumulator)), + /*0x6B*/ + None, + /*0x6C*/ + Some((Instruction::JMP, AddressingMode::Indirect)), + /*0x6D*/ + Some((Instruction::ADC, AddressingMode::Absolute)), + /*0x6E*/ + Some((Instruction::ROR, AddressingMode::Absolute)), + /*0x6F*/ + None, + /*0x70*/ + Some((Instruction::BVS, AddressingMode::Relative)), + /*0x71*/ + Some((Instruction::ADC, AddressingMode::IndirectIndexedY)), + /*0x72*/ + None, + /*0x73*/ + None, + /*0x74*/ + None, + /*0x75*/ + Some((Instruction::ADC, AddressingMode::ZeroPageX)), + /*0x76*/ + Some((Instruction::ROR, AddressingMode::ZeroPageX)), + /*0x77*/ + None, + /*0x78*/ + Some((Instruction::SEI, AddressingMode::Implied)), + /*0x79*/ + Some((Instruction::ADC, AddressingMode::AbsoluteY)), + /*0x7A*/ + None, + /*0x7B*/ + None, + /*0x7C*/ + None, + /*0x7D*/ + Some((Instruction::ADC, AddressingMode::AbsoluteX)), + /*0x7E*/ + Some((Instruction::ROR, AddressingMode::AbsoluteX)), + /*0x7F*/ + None, + /*0x80*/ + None, + /*0x81*/ + Some((Instruction::STA, AddressingMode::IndexedIndirectX)), + /*0x82*/ + None, + /*0x83*/ + None, + /*0x84*/ + Some((Instruction::STY, AddressingMode::ZeroPage)), + /*0x85*/ + Some((Instruction::STA, AddressingMode::ZeroPage)), + /*0x86*/ + Some((Instruction::STX, AddressingMode::ZeroPage)), + /*0x87*/ + None, + /*0x88*/ + Some((Instruction::DEY, AddressingMode::Implied)), + /*0x89*/ + None, + /*0x8A*/ + Some((Instruction::TXA, AddressingMode::Implied)), + /*0x8B*/ + None, + /*0x8C*/ + Some((Instruction::STY, AddressingMode::Absolute)), + /*0x8D*/ + Some((Instruction::STA, AddressingMode::Absolute)), + /*0x8E*/ + Some((Instruction::STX, AddressingMode::Absolute)), + /*0x8F*/ + None, + /*0x90*/ + Some((Instruction::BCC, AddressingMode::Relative)), + /*0x91*/ + Some((Instruction::STA, AddressingMode::IndirectIndexedY)), + /*0x92*/ + None, + /*0x93*/ + None, + /*0x94*/ + Some((Instruction::STY, AddressingMode::ZeroPageX)), + /*0x95*/ + Some((Instruction::STA, AddressingMode::ZeroPageX)), + /*0x96*/ + Some((Instruction::STX, AddressingMode::ZeroPageY)), + /*0x97*/ + None, + /*0x98*/ + Some((Instruction::TYA, AddressingMode::Implied)), + /*0x99*/ + Some((Instruction::STA, AddressingMode::AbsoluteY)), + /*0x9A*/ + Some((Instruction::TXS, AddressingMode::Implied)), + /*0x9B*/ + None, + /*0x9C*/ + None, + /*0x9D*/ + Some((Instruction::STA, AddressingMode::AbsoluteX)), + /*0x9E*/ + None, + /*0x9F*/ + None, + /*0xA0*/ + Some((Instruction::LDY, AddressingMode::Immediate)), + /*0xA1*/ + Some((Instruction::LDA, AddressingMode::IndexedIndirectX)), + /*0xA2*/ + Some((Instruction::LDX, AddressingMode::Immediate)), + /*0xA3*/ + None, + /*0xA4*/ + Some((Instruction::LDY, AddressingMode::ZeroPage)), + /*0xA5*/ + Some((Instruction::LDA, AddressingMode::ZeroPage)), + /*0xA6*/ + Some((Instruction::LDX, AddressingMode::ZeroPage)), + /*0xA7*/ + None, + /*0xA8*/ + Some((Instruction::TAY, AddressingMode::Implied)), + /*0xA9*/ + Some((Instruction::LDA, AddressingMode::Immediate)), + /*0xAA*/ + Some((Instruction::TAX, AddressingMode::Implied)), + /*0xAB*/ + None, + /*0xAC*/ + Some((Instruction::LDY, AddressingMode::Absolute)), + /*0xAD*/ + Some((Instruction::LDA, AddressingMode::Absolute)), + /*0xAE*/ + Some((Instruction::LDX, AddressingMode::Absolute)), + /*0xAF*/ + None, + /*0xB0*/ + Some((Instruction::BCS, AddressingMode::Relative)), + /*0xB1*/ + Some((Instruction::LDA, AddressingMode::IndirectIndexedY)), + /*0xB2*/ + None, + /*0xB3*/ + None, + /*0xB4*/ + Some((Instruction::LDY, AddressingMode::ZeroPageX)), + /*0xB5*/ + Some((Instruction::LDA, AddressingMode::ZeroPageX)), + /*0xB6*/ + Some((Instruction::LDX, AddressingMode::ZeroPageY)), + /*0xB7*/ + None, + /*0xB8*/ + Some((Instruction::CLV, AddressingMode::Implied)), + /*0xB9*/ + Some((Instruction::LDA, AddressingMode::AbsoluteY)), + /*0xBA*/ + Some((Instruction::TSX, AddressingMode::Implied)), + /*0xBB*/ + None, + /*0xBC*/ + Some((Instruction::LDY, AddressingMode::AbsoluteX)), + /*0xBD*/ + Some((Instruction::LDA, AddressingMode::AbsoluteX)), + /*0xBE*/ + Some((Instruction::LDX, AddressingMode::AbsoluteY)), + /*0xBF*/ + None, + /*0xC0*/ + Some((Instruction::CPY, AddressingMode::Immediate)), + /*0xC1*/ + Some((Instruction::CMP, AddressingMode::IndexedIndirectX)), + /*0xC2*/ + None, + /*0xC3*/ + None, + /*0xC4*/ + Some((Instruction::CPY, AddressingMode::ZeroPage)), + /*0xC5*/ + Some((Instruction::CMP, AddressingMode::ZeroPage)), + /*0xC6*/ + Some((Instruction::DEC, AddressingMode::ZeroPage)), + /*0xC7*/ + None, + /*0xC8*/ + Some((Instruction::INY, AddressingMode::Implied)), + /*0xC9*/ + Some((Instruction::CMP, AddressingMode::Immediate)), + /*0xCA*/ + Some((Instruction::DEX, AddressingMode::Implied)), + /*0xCB*/ + None, + /*0xCC*/ + Some((Instruction::CPY, AddressingMode::Absolute)), + /*0xCD*/ + Some((Instruction::CMP, AddressingMode::Absolute)), + /*0xCE*/ + Some((Instruction::DEC, AddressingMode::Absolute)), + /*0xCF*/ + None, + /*0xD0*/ + Some((Instruction::BNE, AddressingMode::Relative)), + /*0xD1*/ + Some((Instruction::CMP, AddressingMode::IndirectIndexedY)), + /*0xD2*/ + None, + /*0xD3*/ + None, + /*0xD4*/ + None, + /*0xD5*/ + Some((Instruction::CMP, AddressingMode::ZeroPageX)), + /*0xD6*/ + Some((Instruction::DEC, AddressingMode::ZeroPageX)), + /*0xD7*/ + None, + /*0xD8*/ + Some((Instruction::CLD, AddressingMode::Implied)), + /*0xD9*/ + Some((Instruction::CMP, AddressingMode::AbsoluteY)), + /*0xDA*/ + None, + /*0xDB*/ + None, + /*0xDC*/ + None, + /*0xDD*/ + Some((Instruction::CMP, AddressingMode::AbsoluteX)), + /*0xDE*/ + Some((Instruction::DEC, AddressingMode::AbsoluteX)), + /*0xDF*/ + None, + /*0xE0*/ + Some((Instruction::CPX, AddressingMode::Immediate)), + /*0xE1*/ + Some((Instruction::SBC, AddressingMode::IndexedIndirectX)), + /*0xE2*/ + None, + /*0xE3*/ + None, + /*0xE4*/ + Some((Instruction::CPX, AddressingMode::ZeroPage)), + /*0xE5*/ + Some((Instruction::SBC, AddressingMode::ZeroPage)), + /*0xE6*/ + Some((Instruction::INC, AddressingMode::ZeroPage)), + /*0xE7*/ + None, + /*0xE8*/ + Some((Instruction::INX, AddressingMode::Implied)), + /*0xE9*/ + Some((Instruction::SBC, AddressingMode::Immediate)), + /*0xEA*/ + Some((Instruction::NOP, AddressingMode::Implied)), + /*0xEB*/ + None, + /*0xEC*/ + Some((Instruction::CPX, AddressingMode::Absolute)), + /*0xED*/ + Some((Instruction::SBC, AddressingMode::Absolute)), + /*0xEE*/ + Some((Instruction::INC, AddressingMode::Absolute)), + /*0xEF*/ + None, + /*0xF0*/ + Some((Instruction::BEQ, AddressingMode::Relative)), + /*0xF1*/ + Some((Instruction::SBC, AddressingMode::IndirectIndexedY)), + /*0xF2*/ + None, + /*0xF3*/ + None, + /*0xF4*/ + None, + /*0xF5*/ + Some((Instruction::SBC, AddressingMode::ZeroPageX)), + /*0xF6*/ + Some((Instruction::INC, AddressingMode::ZeroPageX)), + /*0xF7*/ + None, + /*0xF8*/ + Some((Instruction::SED, AddressingMode::Implied)), + /*0xF9*/ + Some((Instruction::SBC, AddressingMode::AbsoluteY)), + /*0xFA*/ + None, + /*0xFB*/ + None, + /*0xFC*/ + None, + /*0xFD*/ + Some((Instruction::SBC, AddressingMode::AbsoluteX)), + /*0xFE*/ + Some((Instruction::INC, AddressingMode::AbsoluteX)), + /*0xFF*/ + None, +]; diff --git a/src/lib.rs b/src/lib.rs index f79a6e3..fd23caf 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -36,6 +36,6 @@ extern crate bitflags; pub mod address; pub mod instruction; -pub mod machine; +pub mod cpu; pub mod memory; pub mod registers; diff --git a/src/machine.rs b/src/machine.rs deleted file mode 100644 index 7c9e1c8..0000000 --- a/src/machine.rs +++ /dev/null @@ -1,1295 +0,0 @@ -// Copyright (C) 2014 The 6502-rs Developers -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions -// are met: -// 1. Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// 2. Redistributions in binary form must reproduce the above copyright -// notice, this list of conditions and the following disclaimer in the -// documentation and/or other materials provided with the distribution. -// 3. Neither the names of the copyright holders nor the names of any -// contributors may be used to endorse or promote products derived from this -// software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE -// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -// POSSIBILITY OF SUCH DAMAGE. - -use std; - -use address::{Address, AddressDiff}; -use instruction; -use instruction::{DecodedInstr, Instruction, OpInput}; -use memory::Memory; -use registers::{Registers, StackPointer, Status, StatusArgs}; -use registers::{PS_NEGATIVE, PS_DECIMAL_MODE, PS_OVERFLOW, PS_ZERO, PS_CARRY, - PS_DISABLE_INTERRUPTS}; - -#[derive(Clone)] -pub struct Machine { - pub registers: Registers, - pub memory: Memory, -} - -impl Machine { - pub fn new() -> Machine { - Machine { - registers: Registers::new(), - memory: Memory::new(), - } - } - - pub fn reset(&mut self) { - *self = Machine::new(); - } - - pub fn fetch_next_and_decode(&mut self) -> Option { - let x: u8 = self.memory.get_byte(self.registers.program_counter); - - match instruction::OPCODES[x as usize] { - Some((instr, am)) => { - let extra_bytes = am.extra_bytes(); - let num_bytes = AddressDiff(1) + extra_bytes; - - 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)) - } - _ => None, - } - } - - pub fn execute_instruction(&mut self, decoded_instr: DecodedInstr) { - match decoded_instr { - (Instruction::ADC, OpInput::UseImmediate(val)) => { - debug!("add with carry immediate: {}", val); - self.add_with_carry(val as i8); - } - (Instruction::ADC, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr) as i8; - debug!("add with carry. address: {:?}. value: {}", addr, val); - self.add_with_carry(val); - } - - (Instruction::AND, OpInput::UseImmediate(val)) => { - self.and(val as i8); - } - (Instruction::AND, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr) as i8; - self.and(val as i8); - } - - (Instruction::ASL, OpInput::UseImplied) => { - // Accumulator mode - let mut val = self.registers.accumulator as u8; - Machine::shift_left_with_flags(&mut val, &mut self.registers.status); - self.registers.accumulator = val as i8; - - } - (Instruction::ASL, OpInput::UseAddress(addr)) => { - Machine::shift_left_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); - } - - (Instruction::BCC, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter + AddressDiff(rel as i32); - self.branch_if_carry_clear(addr); - } - - (Instruction::BCS, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter + AddressDiff(rel as i32); - self.branch_if_carry_set(addr); - } - - (Instruction::BEQ, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter + AddressDiff(rel as i32); - self.branch_if_equal(addr); - } - - (Instruction::BIT, OpInput::UseAddress(addr)) => { - let a: u8 = self.registers.accumulator as u8; - let m: u8 = self.memory.get_byte(addr); - let res = a & m; - - // The zero flag is set based on the result of the 'and'. - let is_zero = 0 == res; - - // The N flag is set to bit 7 of the byte from memory. - let bit7 = 0 != (0x80 & res); - - // The V flag is set to bit 6 of the byte from memory. - let bit6 = 0 != (0x40 & res); - - self.registers.status.set_with_mask( - PS_ZERO | PS_NEGATIVE | PS_OVERFLOW, - Status::new(StatusArgs { - zero: is_zero, - negative: bit7, - overflow: bit6, - ..StatusArgs::none() - }), - ); - } - - (Instruction::BMI, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter + AddressDiff(rel as i32); - debug!("branch if minus relative. address: {:?}", addr); - self.branch_if_minus(addr); - } - - (Instruction::BPL, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter + AddressDiff(rel as i32); - self.branch_if_positive(addr); - } - - (Instruction::BVC, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter + AddressDiff(rel as i32); - self.branch_if_overflow_clear(addr); - } - - (Instruction::BVS, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter + AddressDiff(rel as i32); - self.branch_if_overflow_set(addr); - } - - (Instruction::CLC, OpInput::UseImplied) => { - self.registers.status.and(!PS_CARRY); - } - (Instruction::CLD, OpInput::UseImplied) => { - self.registers.status.and(!PS_DECIMAL_MODE); - } - (Instruction::CLI, OpInput::UseImplied) => { - self.registers.status.and(!PS_DISABLE_INTERRUPTS); - } - (Instruction::CLV, OpInput::UseImplied) => { - self.registers.status.and(!PS_OVERFLOW); - } - - (Instruction::CMP, OpInput::UseImmediate(val)) => { - self.compare_with_a_register(val); - } - (Instruction::CMP, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - self.compare_with_a_register(val); - } - - (Instruction::CPX, OpInput::UseImmediate(val)) => { - self.compare_with_x_register(val); - } - (Instruction::CPX, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - self.compare_with_x_register(val); - } - - (Instruction::CPY, OpInput::UseImmediate(val)) => { - self.compare_with_y_register(val); - } - (Instruction::CPY, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - self.compare_with_y_register(val); - } - - (Instruction::DEC, OpInput::UseAddress(addr)) => self.decrement_memory(addr), - - (Instruction::DEX, OpInput::UseImplied) => { - self.dec_x(); - } - - (Instruction::EOR, OpInput::UseImmediate(val)) => { - self.exclusive_or(val); - } - (Instruction::EOR, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - self.exclusive_or(val); - } - - (Instruction::INC, OpInput::UseAddress(addr)) => { - let m = self.memory.get_byte(addr); - let m = m + 1; - self.memory.set_byte(addr, m); - let i = m as i8; - Machine::set_flags_from_i8(&mut self.registers.status, i); - } - (Instruction::INX, OpInput::UseImplied) => { - let x = self.registers.index_x + 1; - self.load_x_register(x); - } - (Instruction::INY, OpInput::UseImplied) => { - let y = self.registers.index_y + 1; - self.load_y_register(y); - } - - (Instruction::JMP, OpInput::UseAddress(addr)) => self.jump(addr), - - (Instruction::LDA, OpInput::UseImmediate(val)) => { - debug!("load A immediate: {}", val); - self.load_accumulator(val as i8); - } - (Instruction::LDA, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - debug!("load A. address: {:?}. value: {}", addr, val); - self.load_accumulator(val as i8); - } - - (Instruction::LDX, OpInput::UseImmediate(val)) => { - debug!("load X immediate: {}", val); - self.load_x_register(val as i8); - } - (Instruction::LDX, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - debug!("load X. address: {:?}. value: {}", addr, val); - self.load_x_register(val as i8); - } - - (Instruction::LDY, OpInput::UseImmediate(val)) => { - debug!("load Y immediate: {}", val); - self.load_y_register(val as i8); - } - (Instruction::LDY, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - debug!("load Y. address: {:?}. value: {}", addr, val); - self.load_y_register(val as i8); - } - - (Instruction::LSR, OpInput::UseImplied) => { - // Accumulator mode - let mut val = self.registers.accumulator as u8; - Machine::shift_right_with_flags(&mut val, &mut self.registers.status); - self.registers.accumulator = val as i8; - } - (Instruction::LSR, OpInput::UseAddress(addr)) => { - Machine::shift_right_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); - } - - (Instruction::ORA, OpInput::UseImmediate(val)) => { - self.inclusive_or(val); - } - (Instruction::ORA, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr); - self.inclusive_or(val); - } - - (Instruction::PHA, OpInput::UseImplied) => { - // Push accumulator - let val = self.registers.accumulator as u8; - self.push_on_stack(val); - } - (Instruction::PHP, OpInput::UseImplied) => { - // Push status - let val = self.registers.status.bits(); - self.push_on_stack(val); - } - (Instruction::PLA, OpInput::UseImplied) => { - // Pull accumulator - let val: u8 = self.pull_from_stack(); - self.registers.accumulator = val as i8; - } - (Instruction::PLP, OpInput::UseImplied) => { - // Pull status - let val: u8 = self.pull_from_stack(); - // The `truncate` here won't do anything because we have a - // constant for the single unused flags bit. This probably - // corresponds to the behavior of the 6502...? FIXME: verify - self.registers.status = Status::from_bits_truncate(val); - } - - (Instruction::ROL, OpInput::UseImplied) => { - // Accumulator mode - let mut val = self.registers.accumulator as u8; - Machine::rotate_left_with_flags(&mut val, &mut self.registers.status); - self.registers.accumulator = val as i8; - } - (Instruction::ROL, OpInput::UseAddress(addr)) => { - Machine::rotate_left_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); - } - (Instruction::ROR, OpInput::UseImplied) => { - // Accumulator mode - let mut val = self.registers.accumulator as u8; - Machine::rotate_right_with_flags(&mut val, &mut self.registers.status); - self.registers.accumulator = val as i8; - } - (Instruction::ROR, OpInput::UseAddress(addr)) => { - Machine::rotate_right_with_flags( - self.memory.get_byte_mut_ref(addr), - &mut self.registers.status, - ); - } - - (Instruction::SBC, OpInput::UseImmediate(val)) => { - debug!("subtract with carry immediate: {}", val); - self.subtract_with_carry(val as i8); - } - (Instruction::SBC, OpInput::UseAddress(addr)) => { - let val = self.memory.get_byte(addr) as i8; - debug!("subtract with carry. address: {:?}. value: {}", addr, val); - self.subtract_with_carry(val); - } - - (Instruction::SEC, OpInput::UseImplied) => { - self.registers.status.or(PS_CARRY); - } - (Instruction::SED, OpInput::UseImplied) => { - self.registers.status.or(PS_DECIMAL_MODE); - } - (Instruction::SEI, OpInput::UseImplied) => { - self.registers.status.or(PS_DISABLE_INTERRUPTS); - } - - (Instruction::STA, OpInput::UseAddress(addr)) => { - self.memory.set_byte(addr, self.registers.accumulator as u8); - } - (Instruction::STX, OpInput::UseAddress(addr)) => { - self.memory.set_byte(addr, self.registers.index_x as u8); - } - (Instruction::STY, OpInput::UseAddress(addr)) => { - self.memory.set_byte(addr, self.registers.index_y as u8); - } - - (Instruction::TAX, OpInput::UseImplied) => { - let val = self.registers.accumulator; - self.load_x_register(val); - } - (Instruction::TAY, OpInput::UseImplied) => { - let val = self.registers.accumulator; - self.load_y_register(val); - } - (Instruction::TSX, OpInput::UseImplied) => { - let StackPointer(val) = self.registers.stack_pointer; - let val = val as i8; - self.load_x_register(val); - } - (Instruction::TXA, OpInput::UseImplied) => { - let val = self.registers.index_x; - self.load_accumulator(val); - } - (Instruction::TXS, OpInput::UseImplied) => { - // Note that this is the only 'transfer' instruction that does - // NOT set the zero and negative flags. (Because the target - // is the stack pointer) - let val = self.registers.index_x; - self.registers.stack_pointer = StackPointer(val as u8); - } - (Instruction::TYA, OpInput::UseImplied) => { - let val = self.registers.index_y; - self.load_accumulator(val); - } - - (Instruction::NOP, OpInput::UseImplied) => { - debug!("NOP instruction"); - } - (_, _) => { - debug!( - "attempting to execute unimplemented or invalid \ - 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 set_flags_from_i8(status: &mut Status, value: i8) { - 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() - }), - ); - } - - fn shift_left_with_flags(p_val: &mut u8, status: &mut Status) { - let mask = 1 << 7; - let is_bit_7_set = (*p_val & mask) == mask; - let shifted = (*p_val & !(1 << 7)) << 1; - *p_val = shifted; - status.set_with_mask( - PS_CARRY, - Status::new(StatusArgs { - carry: is_bit_7_set, - ..StatusArgs::none() - }), - ); - Machine::set_flags_from_i8(status, *p_val as i8); - } - - fn shift_right_with_flags(p_val: &mut u8, status: &mut Status) { - let mask = 1; - let is_bit_0_set = (*p_val & mask) == mask; - *p_val = *p_val >> 1; - status.set_with_mask( - PS_CARRY, - Status::new(StatusArgs { - carry: is_bit_0_set, - ..StatusArgs::none() - }), - ); - Machine::set_flags_from_i8(status, *p_val as i8); - } - - fn rotate_left_with_flags(p_val: &mut u8, status: &mut Status) { - let is_carry_set = status.contains(PS_CARRY); - let mask = 1 << 7; - let is_bit_7_set = (*p_val & mask) == mask; - let shifted = (*p_val & !(1 << 7)) << 1; - *p_val = shifted + if is_carry_set { 1 } else { 0 }; - status.set_with_mask( - PS_CARRY, - Status::new(StatusArgs { - carry: is_bit_7_set, - ..StatusArgs::none() - }), - ); - Machine::set_flags_from_i8(status, *p_val as i8); - } - - fn rotate_right_with_flags(p_val: &mut u8, status: &mut Status) { - let is_carry_set = status.contains(PS_CARRY); - let mask = 1; - let is_bit_0_set = (*p_val & mask) == mask; - let shifted = *p_val >> 1; - *p_val = shifted + if is_carry_set { 1 << 7 } else { 0 }; - status.set_with_mask( - PS_CARRY, - Status::new(StatusArgs { - carry: is_bit_0_set, - ..StatusArgs::none() - }), - ); - Machine::set_flags_from_i8(status, *p_val as i8); - } - - fn set_i8_with_flags(mem: &mut i8, status: &mut Status, value: i8) { - *mem = value; - Machine::set_flags_from_i8(status, value); - } - - fn load_x_register(&mut self, value: i8) { - Machine::set_i8_with_flags( - &mut self.registers.index_x, - &mut self.registers.status, - value, - ); - } - - fn load_y_register(&mut self, value: i8) { - Machine::set_i8_with_flags( - &mut self.registers.index_y, - &mut self.registers.status, - value, - ); - } - - fn load_accumulator(&mut self, value: i8) { - Machine::set_i8_with_flags( - &mut self.registers.accumulator, - &mut self.registers.status, - value, - ); - } - - fn add_with_carry(&mut self, value: i8) { - if self.registers.status.contains(PS_DECIMAL_MODE) { - // TODO akeeton: Implement binary-coded decimal. - debug!("binary-coded decimal not implemented for add_with_carry"); - } else { - let a_before: i8 = self.registers.accumulator; - let c_before: i8 = if self.registers.status.contains(PS_CARRY) { - 1 - } else { - 0 - }; - let a_after: i8 = a_before.wrapping_add(c_before).wrapping_add(value); - - debug_assert_eq!( - a_after as u8, - a_before.wrapping_add(c_before).wrapping_add(value) as u8 - ); - - let did_carry = (a_after as u8) < (a_before as u8); - - let did_overflow = (a_before < 0 && value < 0 && a_after >= 0) || - (a_before > 0 && value > 0 && a_after <= 0); - - let mask = PS_CARRY | PS_OVERFLOW; - - self.registers.status.set_with_mask( - mask, - Status::new(StatusArgs { - carry: did_carry, - overflow: did_overflow, - ..StatusArgs::none() - }), - ); - - self.load_accumulator(a_after); - - debug!("accumulator: {}", self.registers.accumulator); - } - } - - fn and(&mut self, value: i8) { - let a_after = self.registers.accumulator & value; - self.load_accumulator(a_after); - } - - // TODO: Implement binary-coded decimal - fn subtract_with_carry(&mut self, value: i8) { - if self.registers.status.contains(PS_DECIMAL_MODE) { - debug!( - "binary-coded decimal not implemented for \ - subtract_with_carry" - ); - } else { - // A - M - (1 - C) - - // nc -- 'not carry' - let nc: i8 = if self.registers.status.contains(PS_CARRY) { - 0 - } else { - 1 - }; - - let a_before: i8 = self.registers.accumulator; - - let a_after = a_before.wrapping_sub(value).wrapping_sub(nc); - - // The carry flag is set on unsigned overflow. - let did_carry = (a_after as u8) > (a_before as u8); - - // The overflow flag is set on two's-complement overflow. - // - // range of A is -128 to 127 - // range of - M - (1 - C) is -128 to 128 - // -(127 + 1) to -(-128 + 0) - // - let over = ((nc == 0 && value < 0) || (nc == 1 && value < -1)) && a_before >= 0 && - a_after < 0; - - let under = (a_before < 0) && (-value - nc < 0) && a_after >= 0; - - let did_overflow = over || under; - - let mask = PS_CARRY | PS_OVERFLOW; - - self.registers.status.set_with_mask( - mask, - Status::new(StatusArgs { - carry: did_carry, - overflow: did_overflow, - ..StatusArgs::none() - }), - ); - - self.load_accumulator(a_after); - } - } - - fn decrement_memory(&mut self, addr: Address) { - let value_new = self.memory.get_byte(addr).wrapping_sub(1); - - self.memory.set_byte(addr, value_new); - - let is_negative = (value_new as i8) < 0; - let is_zero = value_new == 0; - - self.registers.status.set_with_mask( - PS_NEGATIVE | PS_ZERO, - Status::new(StatusArgs { - negative: is_negative, - zero: is_zero, - ..StatusArgs::none() - }), - ); - } - - fn dec_x(&mut self) { - let val = self.registers.index_x; - self.load_x_register(val - 1); - } - - fn jump(&mut self, addr: Address) { - self.registers.program_counter = addr; - } - - fn branch_if_carry_clear(&mut self, addr: Address) { - if !self.registers.status.contains(PS_CARRY) { - self.registers.program_counter = addr; - } - } - - fn branch_if_carry_set(&mut self, addr: Address) { - if self.registers.status.contains(PS_CARRY) { - self.registers.program_counter = addr; - } - } - - fn branch_if_equal(&mut self, addr: Address) { - if self.registers.status.contains(PS_ZERO) { - self.registers.program_counter = addr; - } - } - - fn branch_if_minus(&mut self, addr: Address) { - if self.registers.status.contains(PS_NEGATIVE) { - self.registers.program_counter = addr; - } - } - - fn branch_if_positive(&mut self, addr: Address) { - if !self.registers.status.contains(PS_NEGATIVE) { - self.registers.program_counter = addr; - } - } - - fn branch_if_overflow_clear(&mut self, addr: Address) { - if !self.registers.status.contains(PS_OVERFLOW) { - self.registers.program_counter = addr; - } - } - - fn branch_if_overflow_set(&mut self, addr: Address) { - if self.registers.status.contains(PS_OVERFLOW) { - self.registers.program_counter = addr; - } - } - - // From http://www.6502.org/tutorials/compare_beyond.html: - // If the Z flag is 0, then A <> NUM and BNE will branch - // If the Z flag is 1, then A = NUM and BEQ will branch - // If the C flag is 0, then A (unsigned) < NUM (unsigned) and BCC will branch - // If the C flag is 1, then A (unsigned) >= NUM (unsigned) and BCS will branch - // ... - // The N flag contains most significant bit of the subtraction result. - fn compare(&mut self, r: i8, val: u8) { - if r as u8 >= val as u8 { - self.registers.status.insert(PS_CARRY); - } else { - self.registers.status.remove(PS_CARRY); - } - - if r as i8 == val as i8 { - self.registers.status.insert(PS_ZERO); - } else { - self.registers.status.remove(PS_ZERO); - } - - let diff: i8 = r.wrapping_sub(val as i8); - if diff < 0 { - self.registers.status.insert(PS_NEGATIVE); - } else { - self.registers.status.remove(PS_NEGATIVE); - } - } - - fn compare_with_a_register(&mut self, val: u8) { - let a = self.registers.accumulator; - self.compare(a, val); - } - - fn compare_with_x_register(&mut self, val: u8) { - debug!("compare_with_x_register"); - - let x = self.registers.index_x; - self.compare(x, val); - } - - fn compare_with_y_register(&mut self, val: u8) { - let y = self.registers.index_y; - self.compare(y, val); - } - - fn exclusive_or(&mut self, val: u8) { - let a_after = self.registers.accumulator ^ (val as i8); - self.load_accumulator(a_after); - } - - fn inclusive_or(&mut self, val: u8) { - let a_after = self.registers.accumulator | (val as i8); - self.load_accumulator(a_after); - } - - fn push_on_stack(&mut self, val: u8) { - let addr = self.registers.stack_pointer.to_address(); - self.memory.set_byte(addr, val); - self.registers.stack_pointer.decrement(); - } - - fn pull_from_stack(&mut self) -> u8 { - let addr = self.registers.stack_pointer.to_address(); - let out = self.memory.get_byte(addr); - self.registers.stack_pointer.increment(); - out - } -} - -impl std::fmt::Debug for Machine { - fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { - write!( - f, - "Machine Dump:\n\nAccumulator: {}", - self.registers.accumulator - ) - } -} - -#[cfg(test)] -mod tests { - - use super::*; - use num::range_inclusive; - - #[test] - fn add_with_carry_test() { - let mut machine = Machine::new(); - - machine.add_with_carry(1); - assert_eq!(machine.registers.accumulator, 1); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.add_with_carry(-1); - assert_eq!(machine.registers.accumulator, 0); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.add_with_carry(1); - assert_eq!(machine.registers.accumulator, 2); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - let mut machine = Machine::new(); - - machine.add_with_carry(127); - assert_eq!(machine.registers.accumulator, 127); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.add_with_carry(-127); - assert_eq!(machine.registers.accumulator, 0); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.registers.status.remove(PS_CARRY); - machine.add_with_carry(-128); - assert_eq!(machine.registers.accumulator, -128); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.add_with_carry(127); - assert_eq!(machine.registers.accumulator, -1); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - let mut machine = Machine::new(); - - machine.add_with_carry(127); - assert_eq!(machine.registers.accumulator, 127); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.add_with_carry(1); - assert_eq!(machine.registers.accumulator, -128); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true); - } - - #[test] - fn and_test() { - let mut machine = Machine::new(); - - machine.registers.accumulator = 0; - machine.and(-1); - assert_eq!(machine.registers.accumulator, 0); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - - machine.registers.accumulator = -1; - machine.and(0); - assert_eq!(machine.registers.accumulator, 0); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - - machine.registers.accumulator = -1; - machine.and(0x0f); - assert_eq!(machine.registers.accumulator, 0x0f); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - - machine.registers.accumulator = -1; - machine.and(-128); - assert_eq!(machine.registers.accumulator, -128); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - } - - #[test] - fn subtract_with_carry_test() { - let mut machine = Machine::new(); - - machine.execute_instruction((Instruction::SEC, OpInput::UseImplied)); - machine.registers.accumulator = 0; - - machine.subtract_with_carry(1); - assert_eq!(machine.registers.accumulator, -1); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.execute_instruction((Instruction::SEC, OpInput::UseImplied)); - machine.registers.accumulator = -128; - machine.subtract_with_carry(1); - assert_eq!(machine.registers.accumulator, 127); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true); - - machine.execute_instruction((Instruction::SEC, OpInput::UseImplied)); - machine.registers.accumulator = 127; - machine.subtract_with_carry(-1); - assert_eq!(machine.registers.accumulator, -128); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true); - - machine.execute_instruction((Instruction::CLC, OpInput::UseImplied)); - machine.registers.accumulator = -64; - machine.subtract_with_carry(64); - assert_eq!(machine.registers.accumulator, 127); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true); - - machine.execute_instruction((Instruction::SEC, OpInput::UseImplied)); - machine.registers.accumulator = 0; - machine.subtract_with_carry(-128); - assert_eq!(machine.registers.accumulator, -128); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true); - - machine.execute_instruction((Instruction::CLC, OpInput::UseImplied)); - machine.registers.accumulator = 0; - machine.subtract_with_carry(127); - assert_eq!(machine.registers.accumulator, -128); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - } - - #[test] - fn decrement_memory_test() { - let mut machine = Machine::new(); - let addr = Address(0xA1B2); - - machine.memory.set_byte(addr, 5); - - machine.decrement_memory(addr); - assert_eq!(machine.memory.get_byte(addr), 4); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - - machine.decrement_memory(addr); - assert_eq!(machine.memory.get_byte(addr), 3); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - - machine.decrement_memory(addr); - machine.decrement_memory(addr); - machine.decrement_memory(addr); - assert_eq!(machine.memory.get_byte(addr), 0); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - - machine.decrement_memory(addr); - assert_eq!(machine.memory.get_byte(addr) as i8, -1); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - } - - #[test] - fn logical_shift_right_test() { - // Testing UseImplied version (which targets the accumulator) only, for now - - let mut machine = Machine::new(); - machine.execute_instruction((Instruction::LDA, OpInput::UseImmediate(0))); - machine.execute_instruction((Instruction::LSR, OpInput::UseImplied)); - assert_eq!(machine.registers.accumulator, 0); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.execute_instruction((Instruction::LDA, OpInput::UseImmediate(1))); - machine.execute_instruction((Instruction::LSR, OpInput::UseImplied)); - assert_eq!(machine.registers.accumulator, 0); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.execute_instruction((Instruction::LDA, OpInput::UseImmediate(255))); - machine.execute_instruction((Instruction::LSR, OpInput::UseImplied)); - assert_eq!(machine.registers.accumulator, 0x7F); - assert_eq!(machine.registers.status.contains(PS_CARRY), true); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.execute_instruction((Instruction::LDA, OpInput::UseImmediate(254))); - machine.execute_instruction((Instruction::LSR, OpInput::UseImplied)); - assert_eq!(machine.registers.accumulator, 0x7F); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - } - - #[test] - fn dec_x_test() { - let mut machine = Machine::new(); - - machine.dec_x(); - assert_eq!(machine.registers.index_x, -1); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.dec_x(); - assert_eq!(machine.registers.index_x, -2); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.load_x_register(5); - machine.dec_x(); - assert_eq!(machine.registers.index_x, 4); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.dec_x(); - machine.dec_x(); - machine.dec_x(); - machine.dec_x(); - - assert_eq!(machine.registers.index_x, 0); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), true); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - - machine.dec_x(); - assert_eq!(machine.registers.index_x, -1); - assert_eq!(machine.registers.status.contains(PS_CARRY), false); - assert_eq!(machine.registers.status.contains(PS_ZERO), false); - assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true); - assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false); - } - - #[test] - fn jump_test() { - let mut machine = Machine::new(); - let addr = Address(0xA1B1); - - machine.jump(addr); - assert_eq!(machine.registers.program_counter, addr); - } - - #[test] - fn branch_if_carry_clear_test() { - let mut machine = Machine::new(); - - machine.execute_instruction((Instruction::SEC, OpInput::UseImplied)); - machine.branch_if_carry_clear(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0)); - - machine.execute_instruction((Instruction::CLC, OpInput::UseImplied)); - machine.branch_if_carry_clear(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0xABCD)); - } - - #[test] - fn branch_if_carry_set_test() { - let mut machine = Machine::new(); - - machine.execute_instruction((Instruction::CLC, OpInput::UseImplied)); - machine.branch_if_carry_set(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0)); - - machine.execute_instruction((Instruction::SEC, OpInput::UseImplied)); - machine.branch_if_carry_set(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0xABCD)); - } - - #[test] - fn branch_if_equal_test() { - let mut machine = Machine::new(); - - machine.branch_if_equal(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0)); - - machine.registers.status.or(PS_ZERO); - machine.branch_if_equal(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0xABCD)); - } - - #[test] - fn branch_if_minus_test() { - { - let mut machine = Machine::new(); - let registers_before = machine.registers; - - machine.branch_if_minus(Address(0xABCD)); - assert_eq!(machine.registers, registers_before); - assert_eq!(machine.registers.program_counter, Address(0)); - } - - { - let mut machine = Machine::new(); - - machine.registers.status.or(PS_NEGATIVE); - let registers_before = machine.registers; - - machine.branch_if_minus(Address(0xABCD)); - assert_eq!(machine.registers.status, registers_before.status); - assert_eq!(machine.registers.program_counter, Address(0xABCD)); - } - } - - #[test] - fn branch_if_positive_test() { - let mut machine = Machine::new(); - - machine.registers.status.insert(PS_NEGATIVE); - machine.branch_if_positive(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0)); - - machine.registers.status.remove(PS_NEGATIVE); - machine.branch_if_positive(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0xABCD)); - } - - #[test] - fn branch_if_overflow_clear_test() { - let mut machine = Machine::new(); - - machine.registers.status.insert(PS_OVERFLOW); - machine.branch_if_overflow_clear(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0)); - - machine.registers.status.remove(PS_OVERFLOW); - machine.branch_if_overflow_clear(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0xABCD)); - } - - #[test] - fn branch_if_overflow_set_test() { - let mut machine = Machine::new(); - - machine.branch_if_overflow_set(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0)); - - machine.registers.status.insert(PS_OVERFLOW); - machine.branch_if_overflow_set(Address(0xABCD)); - assert_eq!(machine.registers.program_counter, Address(0xABCD)); - } - - #[cfg(test)] - fn compare_test_helper(compare: &mut F, load_instruction: Instruction) - where - F: FnMut(&mut Machine, u8), - { - let mut machine = Machine::new(); - - machine.execute_instruction((load_instruction, OpInput::UseImmediate(127))); - - compare(&mut machine, 127); - assert!(machine.registers.status.contains(PS_ZERO)); - assert!(machine.registers.status.contains(PS_CARRY)); - assert!(!machine.registers.status.contains(PS_NEGATIVE)); - - - machine.execute_instruction((load_instruction, OpInput::UseImmediate(127))); - - compare(&mut machine, 1); - assert!(!machine.registers.status.contains(PS_ZERO)); - assert!(machine.registers.status.contains(PS_CARRY)); - assert!(!machine.registers.status.contains(PS_NEGATIVE)); - - - machine.execute_instruction((load_instruction, OpInput::UseImmediate(1))); - - compare(&mut machine, 2); - assert!(!machine.registers.status.contains(PS_ZERO)); - assert!(!machine.registers.status.contains(PS_CARRY)); - assert!(machine.registers.status.contains(PS_NEGATIVE)); - - - machine.execute_instruction((load_instruction, OpInput::UseImmediate(20))); - - compare(&mut machine, -50i8 as u8); - assert!(!machine.registers.status.contains(PS_ZERO)); - assert!(!machine.registers.status.contains(PS_CARRY)); - assert!(!machine.registers.status.contains(PS_NEGATIVE)); - - - machine.execute_instruction((load_instruction, OpInput::UseImmediate(1))); - - compare(&mut machine, -1i8 as u8); - assert!(!machine.registers.status.contains(PS_ZERO)); - assert!(!machine.registers.status.contains(PS_CARRY)); - assert!(!machine.registers.status.contains(PS_NEGATIVE)); - - - machine.execute_instruction((load_instruction, OpInput::UseImmediate(127))); - - compare(&mut machine, -128i8 as u8); - assert!(!machine.registers.status.contains(PS_ZERO)); - assert!(!machine.registers.status.contains(PS_CARRY)); - assert!(machine.registers.status.contains(PS_NEGATIVE)); - } - - #[test] - fn compare_with_a_register_test() { - compare_test_helper( - &mut |machine: &mut Machine, val: u8| { machine.compare_with_a_register(val); }, - Instruction::LDA, - ); - } - - #[test] - fn compare_with_x_register_test() { - compare_test_helper( - &mut |machine: &mut Machine, val: u8| { machine.compare_with_x_register(val); }, - Instruction::LDX, - ); - } - - #[test] - fn compare_with_y_register_test() { - compare_test_helper( - &mut |machine: &mut Machine, val: u8| { machine.compare_with_y_register(val); }, - Instruction::LDY, - ); - } - - #[test] - fn exclusive_or_test() { - let mut machine = Machine::new(); - - for a_before in range_inclusive(0u8, 255u8) { - for val in range_inclusive(0u8, 255u8) { - machine.execute_instruction((Instruction::LDA, OpInput::UseImmediate(a_before))); - - machine.exclusive_or(val); - - let a_after = a_before ^ val; - assert_eq!(machine.registers.accumulator, a_after as i8); - - if a_after == 0 { - assert!(machine.registers.status.contains(PS_ZERO)); - } else { - assert!(!machine.registers.status.contains(PS_ZERO)); - } - - if (a_after as i8) < 0 { - assert!(machine.registers.status.contains(PS_NEGATIVE)); - } else { - assert!(!machine.registers.status.contains(PS_NEGATIVE)); - } - } - } - } - - #[test] - fn inclusive_or_test() { - let mut machine = Machine::new(); - - for a_before in range_inclusive(0u8, 255u8) { - for val in range_inclusive(0u8, 255u8) { - machine.execute_instruction((Instruction::LDA, OpInput::UseImmediate(a_before))); - - machine.inclusive_or(val); - - let a_after = a_before | val; - assert_eq!(machine.registers.accumulator, a_after as i8); - - if a_after == 0 { - assert!(machine.registers.status.contains(PS_ZERO)); - } else { - assert!(!machine.registers.status.contains(PS_ZERO)); - } - - if (a_after as i8) < 0 { - assert!(machine.registers.status.contains(PS_NEGATIVE)); - } else { - assert!(!machine.registers.status.contains(PS_NEGATIVE)); - } - } - } - } -}