From 0a3f62816897c6409acdf2f44c963b0ea1ffe425 Mon Sep 17 00:00:00 2001 From: Matthias Endler Date: Tue, 1 Aug 2017 09:55:45 +0200 Subject: [PATCH] Fix compilation with latest rustc --- AUTHORS.txt | 1 + Cargo.toml | 5 +- src/address.rs | 10 +- src/instruction.rs | 978 +++++++++++++++++++++------------- src/lib.rs | 9 +- src/machine.rs | 1274 ++++++++++++++++++++++---------------------- src/memory.rs | 23 +- src/range_incl.rs | 34 -- src/registers.rs | 119 +++-- 9 files changed, 1346 insertions(+), 1107 deletions(-) delete mode 100644 src/range_incl.rs diff --git a/AUTHORS.txt b/AUTHORS.txt index b4c91ce..d8a7e51 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -3,3 +3,4 @@ Alex Weisberger Andrew Keeton Johannes Muenzel +Matthias Endler \ No newline at end of file diff --git a/Cargo.toml b/Cargo.toml index c887853..10a86ac 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,5 +39,6 @@ name = "emu6502" name = "emu6502" [dependencies] -log = "0.2.3" - +bitflags = "0.9.1" +log = "0.3.8" +num = "0.1" diff --git a/src/address.rs b/src/address.rs index 2e4f0ac..0479a96 100644 --- a/src/address.rs +++ b/src/address.rs @@ -25,16 +25,15 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -use std::num::Int; use std::ops::Add; // 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.) -#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct AddressDiff(pub i32); -#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct Address(pub u16); impl Add for Address { @@ -57,7 +56,7 @@ impl Add for AddressDiff { } } -#[derive(Copy, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct CheckedAddressDiff(u16); impl Add for Address { @@ -77,7 +76,7 @@ impl Add for Address { impl Address { pub fn to_u16(&self) -> u16 { match *self { - Address(address_) => address_ + Address(address_) => address_, } } @@ -93,4 +92,3 @@ impl Address { (self.to_u16() & 0x00ff) as u8 } } - diff --git a/src/instruction.rs b/src/instruction.rs index 8220aba..a3c52cd 100644 --- a/src/instruction.rs +++ b/src/instruction.rs @@ -46,74 +46,70 @@ use machine::Machine; // PC | program counter // -#[derive(Copy, Debug, PartialEq, Eq)] -pub enum Instruction - // i/o vars should be listed as follows: - // NV BDIZC A X Y S PC M - // - // | outputs | inputs -{ 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 -, BCS // Branch if Carry Set........... | .. ..... PC = C -, BEQ // Branch if Equal (to zero?).... | .. ..... PC = Z -, BIT // BIT test...................... | NV ...Z. = A & M -, BMI // Branch if Minus............... | .. ..... PC = N -, BNE // Branch if Not Equal........... | .. ..... PC = !Z -, BPL // Branch if Positive............ | .. ..... PC = Z -, BRK // BReaK......................... | .. B.... S PC = -, BVC // Branch if oVerflow Clear...... | .. ..... PC = !V -, BVS // Branch if oVerflow Set........ | .. ..... PC = V -, CLC // CLear Carry flag.............. | .. ....C = 0 -, CLD // Clear Decimal Mode............ | .. .D... = 0 -, CLI // Clear Interrupt Disable....... | .. ..I.. = 0 -, CLV // Clear oVerflow flag........... | .V ..... = 0 -, CMP // Compare....................... | N. ...ZC = A - M -, CPX // Compare X register............ | N. ...ZC = X - M -, CPY // Compare Y register............ | N. ...ZC = Y - M -, DEC // DECrement memory.............. | N. ...Z. M = M - 1 -, DEX // DEcrement X register.......... | N. ...Z. X = X - 1 -, DEY // DEcrement Y register.......... | N. ...Z. Y = Y - 1 -, EOR // Exclusive OR (bitwise)........ | N. ...Z. A = A ^ M -, INC // INCrement memory.............. | N. ...Z. M = M + 1 -, INX // INcrement X register.......... | N. ...Z. X = X + 1 -, INY // INcrement Y register.......... | N. ...Z. Y = Y + 1 -, JMP // JuMP.......................... | .. ..... S PC = -, JSR // Jump to SubRoutine............ | .. ..... S PC = -, LDA // LoaD Accumulator.............. | N. ...Z. A = M -, LDX // LoaD X register............... | N. ...Z. X = M -, LDY // LoaD Y register............... | N. ...Z. Y = M -, LSR // Logical Shift Right........... | N. ...ZC A = A/2 - // or N. ...ZC M = M/2 -, NOP // No OPeration.................. | .. ..... = -, ORA // inclusive OR (bitwise)........ | N. ...Z. A = A | M -, PHA // PusH Accumulator.............. | .. ..... S M = A -, PHP // PusH Processor status......... | .. ..... S M = F -, PLA // PuLl Accumulator.............. | N. ...Z. A S = M (stack) -, PLP // PuLl Processor status......... | NV BDIZC S = M (stack) -, ROL // ROtate Left................... | N. ...ZC A = C A rotated - // or N. ...ZC M = C M rotated -, ROR // ROtate Right.................. | N. ...ZC A = C A rotated - // or N. ...ZC M = C M rotated -, RTI // ReTurn from Interrupt......... | NV BDIZC PC = M (stack) -, RTS // ReTurn from Subroutine........ | .. ..... PC = M (stack) -, SBC // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C) -, SEC // SEt Carry flag................ | .. ....C = 1 -, SED // SEt Decimal flag.............. | .. .D... = 1 -, SEI // SEt Interrupt disable......... | .. ..I.. = 1 -, STA // STore Accumulator............. | .. ..... M = A -, STX // STore X register.............. | .. ..... M = X -, STY // STore Y register.............. | .. ..... M = Y -, TAX // Transfer Accumulator to X..... | N. ...Z. X = A -, TAY // Transfer Accumulator to Y..... | N. ...Z. Y = A -, TSX // Transfer Stack pointer to X... | N. ...Z. X = S -, TXA // Transfer X to Accumulator..... | N. ...Z. A = X -, TXS // Transfer X to Stack pointer... | .. ..... S = X -, TYA // Transfer Y to Accumulator..... | N. ...Z. A = Y +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum Instruction { + 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 + BCS, // Branch if Carry Set........... | .. ..... PC = C + BEQ, // Branch if Equal (to zero?).... | .. ..... PC = Z + BIT, // BIT test...................... | NV ...Z. = A & M + BMI, // Branch if Minus............... | .. ..... PC = N + BNE, // Branch if Not Equal........... | .. ..... PC = !Z + BPL, // Branch if Positive............ | .. ..... PC = Z + BRK, // BReaK......................... | .. B.... S PC = + BVC, // Branch if oVerflow Clear...... | .. ..... PC = !V + BVS, // Branch if oVerflow Set........ | .. ..... PC = V + CLC, // CLear Carry flag.............. | .. ....C = 0 + CLD, // Clear Decimal Mode............ | .. .D... = 0 + CLI, // Clear Interrupt Disable....... | .. ..I.. = 0 + CLV, // Clear oVerflow flag........... | .V ..... = 0 + CMP, // Compare....................... | N. ...ZC = A - M + CPX, // Compare X register............ | N. ...ZC = X - M + CPY, // Compare Y register............ | N. ...ZC = Y - M + DEC, // DECrement memory.............. | N. ...Z. M = M - 1 + DEX, // DEcrement X register.......... | N. ...Z. X = X - 1 + DEY, // DEcrement Y register.......... | N. ...Z. Y = Y - 1 + EOR, // Exclusive OR (bitwise)........ | N. ...Z. A = A ^ M + INC, // INCrement memory.............. | N. ...Z. M = M + 1 + INX, // INcrement X register.......... | N. ...Z. X = X + 1 + INY, // INcrement Y register.......... | N. ...Z. Y = Y + 1 + JMP, // JuMP.......................... | .. ..... S PC = + JSR, // Jump to SubRoutine............ | .. ..... S PC = + LDA, // LoaD Accumulator.............. | N. ...Z. A = M + LDX, // LoaD X register............... | N. ...Z. X = M + LDY, // LoaD Y register............... | N. ...Z. Y = M + LSR, // Logical Shift Right........... | N. ...ZC A = A/2 + // or N. ...ZC M = M/2 + NOP, // No OPeration.................. | .. ..... = + ORA, // inclusive OR (bitwise)........ | N. ...Z. A = A | M + PHA, // PusH Accumulator.............. | .. ..... S M = A + PHP, // PusH Processor status......... | .. ..... S M = F + PLA, // PuLl Accumulator.............. | N. ...Z. A S = M (stack) + PLP, // PuLl Processor status......... | NV BDIZC S = M (stack) + ROL, // ROtate Left................... | N. ...ZC A = C A rotated + // or N. ...ZC M = C M rotated + ROR, // ROtate Right.................. | N. ...ZC A = C A rotated + // or N. ...ZC M = C M rotated + RTI, // ReTurn from Interrupt......... | NV BDIZC PC = M (stack) + RTS, // ReTurn from Subroutine........ | .. ..... PC = M (stack) + SBC, // SuBtract with Carry........... | NV ...ZC A = A-M-(1-C) + SEC, // SEt Carry flag................ | .. ....C = 1 + SED, // SEt Decimal flag.............. | .. .D... = 1 + SEI, // SEt Interrupt disable......... | .. ..I.. = 1 + STA, // STore Accumulator............. | .. ..... M = A + STX, // STore X register.............. | .. ..... M = X + STY, // STore Y register.............. | .. ..... M = Y + TAX, // Transfer Accumulator to X..... | N. ...Z. X = A + TAY, // Transfer Accumulator to Y..... | N. ...Z. Y = A + TSX, // Transfer Stack pointer to X... | N. ...Z. X = S + TXA, // Transfer X to Accumulator..... | N. ...Z. A = X + TXS, // Transfer X to Stack pointer... | .. ..... S = X + TYA, // Transfer Y to Accumulator..... | N. ...Z. A = Y } -#[derive(Copy)] +#[derive(Copy, Clone)] pub enum OpInput { UseImplied, UseImmediate(u8), @@ -121,30 +117,29 @@ pub enum OpInput { UseAddress(Address), } -#[derive(Copy)] -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 +#[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 + 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) << 8us); + let x = (arr[0] as u16) + ((arr[1] as u16) << 8usize); Address(x) } @@ -170,8 +165,10 @@ impl AddressingMode { pub fn process(self, machine: &Machine, arr: &[u8]) -> OpInput { - debug_assert!({let AddressDiff(x) = self.extra_bytes(); - arr.len() == x as usize}); + 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; @@ -179,341 +176,596 @@ impl AddressingMode { let memory = &machine.memory; match self { - AddressingMode::Accumulator | AddressingMode::Implied => { + AddressingMode::Accumulator | + AddressingMode::Implied => { // Always the same -- no input OpInput::UseImplied - }, + } AddressingMode::Immediate => { // Use [u8, ..1] specified in instruction as input OpInput::UseImmediate(arr[0]) - }, + } AddressingMode::ZeroPage => { // Use [u8, ..1] from instruction // Interpret as zero page address // (Output: an 8-bit zero-page address) OpInput::UseAddress(Address(arr[0] as u16)) - }, + } AddressingMode::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) OpInput::UseAddress(Address((arr[0] + x) as u16)) - }, + } AddressingMode::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) OpInput::UseAddress(Address((arr[0] + y) as u16)) - }, + } AddressingMode::Relative => { // Use [u8, ..1] from instruction // (interpret as relative...) OpInput::UseRelative(arr[0] as i8) - }, + } AddressingMode::Absolute => { // Use [u8, ..2] from instruction as address // (Output: a 16-bit address) OpInput::UseAddress(arr_to_addr(arr)) - }, + } AddressingMode::AbsoluteX => { // Use [u8, ..2] from instruction as address, add X // (Output: a 16-bit address) OpInput::UseAddress(arr_to_addr(arr) + AddressDiff(x as i32)) - }, + } AddressingMode::AbsoluteY => { // Use [u8, ..2] from instruction as address, add Y // (Output: a 16-bit address) OpInput::UseAddress(arr_to_addr(arr) + AddressDiff(y as i32)) - }, + } AddressingMode::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)); OpInput::UseAddress(arr_to_addr(slice)) - }, + } AddressingMode::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)); + let slice = memory.get_slice(Address(start as u16), AddressDiff(2)); OpInput::UseAddress(arr_to_addr(slice)) - }, + } AddressingMode::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)); + let slice = memory.get_slice(Address(start as u16), AddressDiff(2)); OpInput::UseAddress(arr_to_addr(slice) + AddressDiff(y as i32)) - }, + } } } } 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 f8c689b..f79a6e3 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -25,20 +25,17 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. -#![feature(core)] -#![feature(hash)] -#![feature(rustc_private)] - // Needed for debug! / log! macros #[macro_use] extern crate log; +extern crate num; + #[macro_use] -extern crate rustc_bitflags; +extern crate bitflags; pub mod address; pub mod instruction; pub mod machine; pub mod memory; -pub mod range_incl; pub mod registers; diff --git a/src/machine.rs b/src/machine.rs index 6cd37b7..7c9e1c8 100644 --- a/src/machine.rs +++ b/src/machine.rs @@ -31,27 +31,26 @@ use address::{Address, AddressDiff}; use instruction; use instruction::{DecodedInstr, Instruction, OpInput}; use memory::Memory; -use range_incl::range_incl; -use registers::{ Registers, StackPointer, Status, StatusArgs }; -use registers::{ PS_NEGATIVE, PS_DECIMAL_MODE, PS_OVERFLOW, PS_ZERO, PS_CARRY, - PS_DISABLE_INTERRUPTS }; +use registers::{Registers, StackPointer, Status, StatusArgs}; +use registers::{PS_NEGATIVE, PS_DECIMAL_MODE, PS_OVERFLOW, PS_ZERO, PS_CARRY, + PS_DISABLE_INTERRUPTS}; -#[derive(Copy)] +#[derive(Clone)] pub struct Machine { pub registers: Registers, - pub memory: Memory + pub memory: Memory, } impl Machine { pub fn new() -> Machine { - Machine{ - registers: Registers::new(), - memory: Memory::new() - } + Machine { + registers: Registers::new(), + memory: Memory::new(), + } } pub fn reset(&mut self) { - *self = Machine::new(); + *self = Machine::new(); } pub fn fetch_next_and_decode(&mut self) -> Option { @@ -62,19 +61,17 @@ impl Machine { let extra_bytes = am.extra_bytes(); let num_bytes = AddressDiff(1) + extra_bytes; - let data_start = self.registers.program_counter - + AddressDiff(1); + 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; + self.registers.program_counter = self.registers.program_counter + num_bytes; Some((instr, am_out)) } - _ => None + _ => None, } } @@ -101,32 +98,29 @@ impl Machine { (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); + 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); + &mut self.registers.status, + ); } (Instruction::BCC, OpInput::UseRelative(rel)) => { - let addr = self.registers.program_counter - + AddressDiff(rel as i32); + 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); + 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); + let addr = self.registers.program_counter + AddressDiff(rel as i32); self.branch_if_equal(addr); } @@ -146,34 +140,33 @@ impl Machine { self.registers.status.set_with_mask( PS_ZERO | PS_NEGATIVE | PS_OVERFLOW, - Status::new(StatusArgs { zero: is_zero, - negative: bit7, - overflow: bit6, - ..StatusArgs::none() } )); + 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); + 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); + 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); + 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); + let addr = self.registers.program_counter + AddressDiff(rel as i32); self.branch_if_overflow_set(addr); } @@ -214,9 +207,7 @@ impl Machine { self.compare_with_y_register(val); } - (Instruction::DEC, OpInput::UseAddress(addr)) => { - self.decrement_memory(addr) - } + (Instruction::DEC, OpInput::UseAddress(addr)) => self.decrement_memory(addr), (Instruction::DEX, OpInput::UseImplied) => { self.dec_x(); @@ -231,11 +222,11 @@ impl Machine { } (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); + 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; @@ -246,9 +237,7 @@ impl Machine { self.load_y_register(y); } - (Instruction::JMP, OpInput::UseAddress(addr)) => { - self.jump(addr) - } + (Instruction::JMP, OpInput::UseAddress(addr)) => self.jump(addr), (Instruction::LDA, OpInput::UseImmediate(val)) => { debug!("load A immediate: {}", val); @@ -283,14 +272,14 @@ impl Machine { (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); + 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); + &mut self.registers.status, + ); } (Instruction::ORA, OpInput::UseImmediate(val)) => { @@ -328,26 +317,26 @@ impl Machine { (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); + 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); + &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); + 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); + &mut self.registers.status, + ); } (Instruction::SBC, OpInput::UseImmediate(val)) => { @@ -356,8 +345,7 @@ impl Machine { } (Instruction::SBC, OpInput::UseAddress(addr)) => { let val = self.memory.get_byte(addr) as i8; - debug!("subtract with carry. address: {:?}. value: {}", - addr, val); + debug!("subtract with carry. address: {:?}. value: {}", addr, val); self.subtract_with_carry(val); } @@ -414,8 +402,10 @@ impl Machine { debug!("NOP instruction"); } (_, _) => { - debug!("attempting to execute unimplemented or invalid \ - instruction"); + debug!( + "attempting to execute unimplemented or invalid \ + instruction" + ); } }; } @@ -425,7 +415,7 @@ impl Machine { if let Some(decoded_instr) = self.fetch_next_and_decode() { self.execute_instruction(decoded_instr); } else { - break + break; } } } @@ -436,9 +426,12 @@ impl Machine { status.set_with_mask( PS_ZERO | PS_NEGATIVE, - Status::new(StatusArgs { zero: is_zero, - negative: is_negative, - ..StatusArgs::none() } )); + Status::new(StatusArgs { + zero: is_zero, + negative: is_negative, + ..StatusArgs::none() + }), + ); } fn shift_left_with_flags(p_val: &mut u8, status: &mut Status) { @@ -448,8 +441,11 @@ impl Machine { *p_val = shifted; status.set_with_mask( PS_CARRY, - Status::new(StatusArgs { carry: is_bit_7_set, - ..StatusArgs::none() } )); + Status::new(StatusArgs { + carry: is_bit_7_set, + ..StatusArgs::none() + }), + ); Machine::set_flags_from_i8(status, *p_val as i8); } @@ -459,8 +455,11 @@ impl Machine { *p_val = *p_val >> 1; status.set_with_mask( PS_CARRY, - Status::new(StatusArgs { carry: is_bit_0_set, - ..StatusArgs::none() } )); + Status::new(StatusArgs { + carry: is_bit_0_set, + ..StatusArgs::none() + }), + ); Machine::set_flags_from_i8(status, *p_val as i8); } @@ -472,8 +471,11 @@ impl Machine { *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() } )); + Status::new(StatusArgs { + carry: is_bit_7_set, + ..StatusArgs::none() + }), + ); Machine::set_flags_from_i8(status, *p_val as i8); } @@ -485,8 +487,11 @@ impl Machine { *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() } )); + Status::new(StatusArgs { + carry: is_bit_0_set, + ..StatusArgs::none() + }), + ); Machine::set_flags_from_i8(status, *p_val as i8); } @@ -496,21 +501,27 @@ impl Machine { } fn load_x_register(&mut self, value: i8) { - Machine::set_i8_with_flags(&mut self.registers.index_x, - &mut self.registers.status, - value); + 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); + 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); + Machine::set_i8_with_flags( + &mut self.registers.accumulator, + &mut self.registers.status, + value, + ); } fn add_with_carry(&mut self, value: i8) { @@ -519,25 +530,33 @@ impl Machine { 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 + c_before + value; + 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 as u8 + c_before as u8 - + value as u8); + 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 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.registers.status.set_with_mask( + mask, + Status::new(StatusArgs { + carry: did_carry, + overflow: did_overflow, + ..StatusArgs::none() + }), + ); self.load_accumulator(a_after); @@ -553,18 +572,23 @@ impl Machine { // 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"); + 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 nc: i8 = if self.registers.status.contains(PS_CARRY) { + 0 + } else { + 1 + }; let a_before: i8 = self.registers.accumulator; - let a_after = a_before - value - nc; + 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); @@ -575,39 +599,44 @@ impl Machine { // 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 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 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.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) - 1; + 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; + 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() } )); + Status::new(StatusArgs { + negative: is_negative, + zero: is_zero, + ..StatusArgs::none() + }), + ); } fn dec_x(&mut self) { @@ -667,7 +696,7 @@ impl Machine { // 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 of the subtraction result. + // 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); @@ -681,7 +710,7 @@ impl Machine { self.registers.status.remove(PS_ZERO); } - let diff: i8 = (r as i8) - (val as i8); + let diff: i8 = r.wrapping_sub(val as i8); if diff < 0 { self.registers.status.insert(PS_NEGATIVE); } else { @@ -732,555 +761,534 @@ impl Machine { 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) + write!( + f, + "Machine Dump:\n\nAccumulator: {}", + self.registers.accumulator + ) } } -#[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(); +mod tests { - machine.execute_instruction( - (load_instruction, OpInput::UseImmediate(127)) - ); + use super::*; + use num::range_inclusive; - 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)); + #[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)) - ); + 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)); + 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)) - ); + 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)); + 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)) - ); + machine.execute_instruction((load_instruction, OpInput::UseImmediate(20))); - compare(&mut machine, -50); - assert!(!machine.registers.status.contains(PS_ZERO )); - assert!(!machine.registers.status.contains(PS_CARRY )); - assert!(!machine.registers.status.contains(PS_NEGATIVE)); + 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)) - ); + machine.execute_instruction((load_instruction, OpInput::UseImmediate(1))); - 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)); + 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)) - ); + machine.execute_instruction((load_instruction, OpInput::UseImmediate(127))); - compare(&mut machine, -128); - assert!(!machine.registers.status.contains(PS_ZERO )); - assert!(!machine.registers.status.contains(PS_CARRY )); - assert!( machine.registers.status.contains(PS_NEGATIVE)); -} + 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_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_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 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(); + #[test] + fn exclusive_or_test() { + let mut machine = Machine::new(); - for a_before in range_incl(0u8, 255u8) { - for val in range_incl(0u8, 255u8) { - machine.execute_instruction( - (Instruction::LDA, OpInput::UseImmediate(a_before)) - ); + 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); + machine.exclusive_or(val); - let a_after = a_before ^ val; - assert_eq!(machine.registers.accumulator, a_after as i8); + 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 == 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)); + } } + } + } - 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_incl(0u8, 255u8) { - for val in range_incl(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)); + #[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)); + } } } } diff --git a/src/memory.rs b/src/memory.rs index 3ee0dce..5a0b45d 100644 --- a/src/memory.rs +++ b/src/memory.rs @@ -25,11 +25,12 @@ // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. +use std::iter::repeat; use address::{Address, AddressDiff}; // JAM: We can probably come up with a better way to represent address ranges. // Address range type? -// +// // // Address range -- inclusive on both sides // pub struct AddressRangeIncl { // begin: Address, @@ -39,24 +40,24 @@ use address::{Address, AddressDiff}; const ADDR_LO_BARE: u16 = 0x0000; const ADDR_HI_BARE: u16 = 0xFFFF; -pub const MEMORY_ADDRESS_LO: Address = Address(ADDR_LO_BARE); -pub const MEMORY_ADDRESS_HI: Address = Address(ADDR_HI_BARE); -pub const STACK_ADDRESS_LO: Address = Address(0x0100); -pub const STACK_ADDRESS_HI: Address = Address(0x01FF); +pub const MEMORY_ADDRESS_LO: Address = Address(ADDR_LO_BARE); +pub const MEMORY_ADDRESS_HI: Address = Address(ADDR_HI_BARE); +pub const STACK_ADDRESS_LO: Address = Address(0x0100); +pub const STACK_ADDRESS_HI: Address = Address(0x01FF); pub const IRQ_INTERRUPT_VECTOR_LO: Address = Address(0xFFFE); pub const IRQ_INTERRUPT_VECTOR_HI: Address = Address(0xFFFF); -const MEMORY_SIZE: usize = (ADDR_HI_BARE - ADDR_LO_BARE) as usize + 1us; +const MEMORY_SIZE: usize = (ADDR_HI_BARE - ADDR_LO_BARE) as usize + 1usize; // FIXME: Should this use indirection for `bytes`? -#[derive(Copy)] +#[derive(Clone)] pub struct Memory { - bytes: [u8; MEMORY_SIZE] + bytes: Vec, } impl Memory { pub fn new() -> Memory { - Memory { bytes: [0; MEMORY_SIZE] } + Memory { bytes: repeat(0).take(MEMORY_SIZE).collect() } } pub fn get_byte(&self, address: Address) -> u8 { @@ -67,8 +68,7 @@ impl Memory { &mut self.bytes[address.to_usize()] } - pub fn get_slice(&self, Address(start): Address, - AddressDiff(diff): AddressDiff) -> &[u8] { + pub fn get_slice(&self, Address(start): Address, AddressDiff(diff): AddressDiff) -> &[u8] { let start = start as usize; let diff = diff as usize; let end = start + diff; @@ -100,4 +100,3 @@ impl Memory { STACK_ADDRESS_LO <= *address && *address <= STACK_ADDRESS_HI } } - diff --git a/src/range_incl.rs b/src/range_incl.rs deleted file mode 100644 index 522bc6e..0000000 --- a/src/range_incl.rs +++ /dev/null @@ -1,34 +0,0 @@ -use std::num::Int; - -pub struct RangeIncl { - state: Option, - end: T, -} - -pub fn range_incl(begin: T, end: T) -> RangeIncl { - RangeIncl { state: Some(begin), end: end } -} - -trait One : Int { - fn my_one(_: Option) -> Self { - Int::one() - } -} -impl One for T where T: Int {} - -impl Iterator for RangeIncl { - type Item = T; - fn next(&mut self) -> Option<::Item> { - match self.state { - Some(current) => { - self.state = - if current == self.end { None } - else { Some(current + One::my_one(None::)) }; - Some(current) - }, - None => { - None - } - } - } -} diff --git a/src/registers.rs b/src/registers.rs index 5c1af8d..77ea196 100644 --- a/src/registers.rs +++ b/src/registers.rs @@ -29,7 +29,7 @@ use address::{Address, AddressDiff}; use memory::{STACK_ADDRESS_LO, STACK_ADDRESS_HI}; // Useful for constructing Status instances -#[derive(Copy)] +#[derive(Copy, Clone)] pub struct StatusArgs { pub negative: bool, pub overflow: bool, @@ -43,31 +43,32 @@ pub struct StatusArgs { impl StatusArgs { pub fn none() -> StatusArgs { - StatusArgs { negative: false, - overflow: false, - unused: false, - brk: false, - decimal_mode: false, - disable_interrupts: false, - zero: false, - carry: false, } + StatusArgs { + negative: false, + overflow: false, + unused: false, + brk: false, + decimal_mode: false, + disable_interrupts: false, + zero: false, + carry: false, + } } } -pub bitflags! { -#[derive(Debug)] - flags Status: u8 { - const PS_NEGATIVE = 0b10000000, - const PS_OVERFLOW = 0b01000000, - const PS_UNUSED = 0b00100000, // JAM: Should this exist? +bitflags! { + pub struct Status: u8 { + const PS_NEGATIVE = 0b10000000; + const PS_OVERFLOW = 0b01000000; + const PS_UNUSED = 0b00100000; // JAM: Should this exist? // (note that it affects the // behavior of things like // from_bits_truncate) - const PS_BRK = 0b00010000, - const PS_DECIMAL_MODE = 0b00001000, - const PS_DISABLE_INTERRUPTS = 0b00000100, - const PS_ZERO = 0b00000010, - const PS_CARRY = 0b00000001, + const PS_BRK = 0b00010000; + const PS_DECIMAL_MODE = 0b00001000; + const PS_DISABLE_INTERRUPTS = 0b00000100; + const PS_ZERO = 0b00000010; + const PS_CARRY = 0b00000001; } } @@ -75,14 +76,16 @@ impl Status { pub fn default() -> Status { // TODO akeeton: Revisit these defaults. - Status::new(StatusArgs { negative: false, - overflow: false, - unused: true, - brk: false, - decimal_mode: false, - disable_interrupts: true, - zero: false, - carry: false, } ) + Status::new(StatusArgs { + negative: false, + overflow: false, + unused: true, + brk: false, + decimal_mode: false, + disable_interrupts: true, + zero: false, + carry: false, + }) } pub fn new(StatusArgs { negative, @@ -96,14 +99,30 @@ impl Status { { let mut out = Status::empty(); - if negative { out = out | PS_NEGATIVE } - if overflow { out = out | PS_OVERFLOW } - if unused { out = out | PS_UNUSED } - if brk { out = out | PS_BRK } - if decimal_mode { out = out | PS_DECIMAL_MODE } - if disable_interrupts { out = out | PS_DISABLE_INTERRUPTS } - if zero { out = out | PS_ZERO } - if carry { out = out | PS_CARRY } + if negative { + out = out | PS_NEGATIVE + } + if overflow { + out = out | PS_OVERFLOW + } + if unused { + out = out | PS_UNUSED + } + if brk { + out = out | PS_BRK + } + if decimal_mode { + out = out | PS_DECIMAL_MODE + } + if disable_interrupts { + out = out | PS_DISABLE_INTERRUPTS + } + if zero { + out = out | PS_ZERO + } + if carry { + out = out | PS_CARRY + } out } @@ -121,12 +140,11 @@ impl Status { } } -#[derive(Copy, PartialEq, Eq, PartialOrd, Ord, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] pub struct StackPointer(pub u8); impl StackPointer { - pub fn to_address(&self) -> Address - { + pub fn to_address(&self) -> Address { let StackPointer(sp) = *self; STACK_ADDRESS_LO + AddressDiff(sp as i32) } @@ -144,27 +162,26 @@ impl StackPointer { } } -#[derive(Copy, PartialEq, Eq, Debug)] +#[derive(Copy, Clone, PartialEq, Eq, Debug)] pub struct Registers { - pub accumulator: i8, - pub index_x: i8, - pub index_y: i8, - pub stack_pointer: StackPointer, + pub accumulator: i8, + pub index_x: i8, + pub index_y: i8, + pub stack_pointer: StackPointer, pub program_counter: Address, - pub status: Status + pub status: Status, } impl Registers { pub fn new() -> Registers { // TODO akeeton: Revisit these defaults. Registers { - accumulator: 0, - index_x: 0, - index_y: 0, - stack_pointer: StackPointer(STACK_ADDRESS_HI.get_offset()), + accumulator: 0, + index_x: 0, + index_y: 0, + stack_pointer: StackPointer(STACK_ADDRESS_HI.get_offset()), program_counter: Address(0), - status: Status::default() + status: Status::default(), } } } -