diff --git a/.vscode/launch.json b/.vscode/launch.json index 28ebbe7..b9713b2 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -1,45 +1,45 @@ -{ - // Use IntelliSense to learn about possible attributes. - // Hover to view descriptions of existing attributes. - // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 - "version": "0.2.0", - "configurations": [ - { - "type": "lldb", - "request": "launch", - "name": "Debug executable 're6502'", - "cargo": { - "args": [ - "build", - "--bin=re6502", - "--package=re6502" - ], - "filter": { - "name": "re6502", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - }, - { - "type": "lldb", - "request": "launch", - "name": "Debug unit tests in executable 're6502'", - "cargo": { - "args": [ - "test", - "--no-run", - "--bin=re6502", - "--package=re6502" - ], - "filter": { - "name": "re6502", - "kind": "bin" - } - }, - "args": [], - "cwd": "${workspaceFolder}" - } - ] +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "type": "lldb", + "request": "launch", + "name": "Debug executable 're6502'", + "cargo": { + "args": [ + "build", + "--bin=re6502", + "--package=re6502" + ], + "filter": { + "name": "re6502", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + }, + { + "type": "lldb", + "request": "launch", + "name": "Debug unit tests in executable 're6502'", + "cargo": { + "args": [ + "test", + "--no-run", + "--bin=re6502", + "--package=re6502" + ], + "filter": { + "name": "re6502", + "kind": "bin" + } + }, + "args": [], + "cwd": "${workspaceFolder}" + } + ] } \ No newline at end of file diff --git a/LICENSE b/LICENSE index baf519e..3022cd8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,37 +1,37 @@ - - -The code in the methods r6502::instructions::ADC and r6502::instructions::SDC -is adapted from code written by javidx9 (David Barr) and is subject to the following license: - - License (OLC-3) - ~~~~~~~~~~~~~~~ - - Copyright 2018-2019 OneLoneCoder.com - - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - - 1. Redistributions or derivations of source code must retain the above - copyright notice, this list of conditions and the following disclaimer. - - 2. Redistributions or derivative works in binary form must reproduce - the above copyright notice. This list of conditions and the following - disclaimer must be reproduced in the documentation and/or other - materials provided with the distribution. - - 3. Neither the name of the copyright holder nor the names of its - contributors may be used to endorse or promote products derived - from this software without specific prior written permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR - A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT - HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT - LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY - THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + + +The code in the methods r6502::instructions::ADC and r6502::instructions::SDC +is adapted from code written by javidx9 (David Barr) and is subject to the following license: + + License (OLC-3) + ~~~~~~~~~~~~~~~ + + Copyright 2018-2019 OneLoneCoder.com + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions or derivations of source code must retain the above + copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions or derivative works in binary form must reproduce + the above copyright notice. This list of conditions and the following + disclaimer must be reproduced in the documentation and/or other + materials provided with the distribution. + + 3. Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \ No newline at end of file diff --git a/src/r6502/addressing_modes.rs b/src/r6502/addressing_modes.rs index 8c4dd4c..075c838 100644 --- a/src/r6502/addressing_modes.rs +++ b/src/r6502/addressing_modes.rs @@ -1,259 +1,310 @@ - -#![allow(unused_variables, dead_code, non_snake_case)] - -use super::{R6502, Bus}; - -#[derive(Clone, Copy, PartialEq)] -pub enum ModeID -{ - IMP, // Implied - ACM, // Accumulator - IMM, // Immediate - ZP0, // Zero Page - ZPX, // Zero Page, X - ZPY, // Zero Page, Y - REL, // Relative - ABS, // Absolute - ABX, // Absolute, X - ABY, // Aboslute, Y - IND, // Indirect - IZX, // Indirect, X - IZY, // Indirect, Y - ERR, // Error mode - this is an invalid mode - -} - -// Instruction decoding: -// https://llx.com/Neil/a2/opcodes.html - -// GROUP ONE ADDRESS MODES -// 000 (zero page,X) IZX -// 001 zero page ZP0 -// 010 #immediate IMM -// 011 absolute ABS -// 100 (zero page),Y IZY -// 101 zero page,X ZPX -// 110 absolute,Y ABY -// 111 absolute,X ABX - -// GROUP TWO ADDRESS MODES -// 000 #immediate IMM -// 001 zero page ZP0 -// 010 accumulator ACM -// 011 absolute ABS -// 100 NONE ERR -// 101 zero page,X ZPX -// 110 NONE ERR -// 111 absolute,X ABX - -pub struct AddressingModes; -impl AddressingModes -{ - pub const GROUP_ONE_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ - AddressingModes::IZX, - AddressingModes::ZP0, - AddressingModes::IMM, - AddressingModes::ABS, - AddressingModes::IZY, - AddressingModes::ZPX, - AddressingModes::ABY, - AddressingModes::ABX, - ]; - - pub const GROUP_TWO_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ - AddressingModes::IMM, - AddressingModes::ZP0, - AddressingModes::ACM, - AddressingModes::ABS, - AddressingModes::ERR, - AddressingModes::ZPX, - AddressingModes::ERR, - AddressingModes::ABX, - ]; -} - -impl AddressingModes -{ - - pub fn ERR(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - ModeID::ERR - } - - pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - ModeID::IMP - } - - pub fn ACM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_data = cpu.a as u16; - ModeID::ACM - } - - pub fn IMM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_data = bus.read(cpu.pc) as u16; - cpu.pc += 1; - - ModeID::IMM - } - - pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; - cpu.pc += 1; - - cpu.working_data = bus.read(cpu.working_addr) as u16; - - ModeID::ZP0 - } - - pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; - cpu.working_addr += cpu.x as u16; - cpu.pc += 1; - - cpu.working_data = bus.read(cpu.working_addr) as u16; - - ModeID::ZPX - } - - pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; - cpu.working_addr += cpu.y as u16; - cpu.pc += 1; - - cpu.working_data = bus.read(cpu.working_addr) as u16; - - ModeID::ZPY - } - - pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - // NOTE: Not sure if we can use the working_data variable for this. - // if any instruction using this address mode needs extra data read - // then we need another variable to store this address - // - // Use working_addr to just like the other modes - cpu.working_data = bus.read(cpu.pc) as u16; - cpu.pc += 1; - - ModeID::REL - } - - pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_addr = bus.read(cpu.pc) as u16; - cpu.pc += 1; - cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; - cpu.pc += 1; - - cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; - - ModeID::ABS - } - - pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_addr = bus.read(cpu.pc) as u16; - cpu.pc += 1; - cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; - cpu.pc += 1; - - cpu.working_addr += cpu.x as u16; - - cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; - - ModeID::ABX - } - - pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - cpu.working_addr = bus.read(cpu.pc) as u16; - cpu.pc += 1; - cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; - cpu.pc += 1; - - cpu.working_addr += cpu.y as u16; - - cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; - - ModeID::ABY - } - - pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - - ModeID::IND - } - - - // Indexed Indirect Addressing (IND, X) - // In indexed indirect addressing (referred to as (Indirect, X)), the second byte of the - // instruction is added to the contents of the X register, discarding the carry. - // The result of the addition points to a memory location on the Zero Page which contains - // the low order byte of the effective address. The next memory location in page zero, - // contains the high order byte of the effective address. Both memory locations specifying - // the effective address must be in the Zero Page. - // - // Info from: - // https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf - pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - let offset = bus.read(cpu.pc) as u16; - cpu.pc += 1; - let mut pointer = cpu.x as u16 + offset; - - // discard the carry and wrap - // If the addition goes beyond the Zero Page - // it should wrap around back to the beginning - pointer = pointer & 0x00FF; - - let lo_byte = bus.read(pointer) as u16; - let hi_byte = bus.read(pointer + 1) as u16; - cpu.working_addr = (hi_byte << 0x08) | lo_byte; - - cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; - - ModeID::IZX - } - - - // Indirect Indexed Addressing (IND), Y - // In indirect indexed addressing, the second byte of the instruction points to - // a memory location in page zero. The contents of this memory location are added to - // the contents of the Y register. The result is the low order byte of the effective address. - // The carry from this addition is added to the contents of the next page zero memory - // location, to form the high order byte of the effective address. - // - // Info from: - // https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf - pub fn IZY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID - { - // zp_pointer points to a location in zero page - let zp_pointer = bus.read(cpu.pc) as u16; - cpu.pc += 1; - - // The value at zp_pointer is added to the Y register - let zp_value = bus.read(zp_pointer) as u16; - let sum = zp_value + cpu.y as u16; - - // The sum with the carry discarded is the lo byte - let lo_byte = sum & 0x00FF; - - // The carry plus the value at the next zero page address is the hi byte - let zp_next = bus.read(zp_pointer + 1) as u16; - let temp = (sum & 0xFF00) >> 0x08; - let temp2 = temp + zp_next; - let hi_byte: u8 = (((sum & 0xFF00) >> 0x08) + zp_next) as u8; - - // Store the final address and read the data - cpu.working_addr = ((hi_byte as u16) << 0x08) | lo_byte; - cpu.working_data = bus.read(cpu.working_addr) as u16; - - ModeID::IZY - } + +#![allow(unused_variables, dead_code, non_snake_case)] + +use super::{R6502, Bus}; + +#[derive(Clone, Copy, PartialEq)] +pub enum ModeID +{ + IMP, // Implied + ACM, // Accumulator + IMM, // Immediate + ZP0, // Zero Page + ZPX, // Zero Page, X + ZPY, // Zero Page, Y + REL, // Relative + ABS, // Absolute + ABX, // Absolute, X + ABY, // Aboslute, Y + IND, // Indirect + IZX, // Indirect, X + IZY, // Indirect, Y + ERR, // Error mode - this is an invalid mode + +} + +// Instruction decoding: +// https://llx.com/Neil/a2/opcodes.html + +// GROUP ONE ADDRESS MODES +// 000 (zero page,X) IZX +// 001 zero page ZP0 +// 010 #immediate IMM +// 011 absolute ABS +// 100 (zero page),Y IZY +// 101 zero page,X ZPX +// 110 absolute,Y ABY +// 111 absolute,X ABX + +// GROUP TWO ADDRESS MODES +// 000 #immediate IMM +// 001 zero page ZP0 +// 010 accumulator ACM +// 011 absolute ABS +// 100 NONE ERR +// 101 zero page,X ZPX +// 110 NONE ERR +// 111 absolute,X ABX + +// GROUP THREE ADDRES MODES +// 000 #immediate IMM +// 001 zero page ZP0 +// 011 absolute ABS +// 101 zero page,X ZPX +// 111 absolute,X ABX + +pub struct AddressingModes; +impl AddressingModes +{ + pub const GROUP_ONE_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ + AddressingModes::IZX, + AddressingModes::ZP0, + AddressingModes::IMM, + AddressingModes::ABS, + AddressingModes::IZY, + AddressingModes::ZPX, + AddressingModes::ABY, + AddressingModes::ABX, + ]; + + pub const GROUP_TWO_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ + AddressingModes::IMM, + AddressingModes::ZP0, + AddressingModes::ACM, + AddressingModes::ABS, + AddressingModes::ERR, + AddressingModes::ZPX, + AddressingModes::ERR, + AddressingModes::ABX, + ]; + + pub const GROUP_THREE_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ + AddressingModes::IMM, + AddressingModes::ZP0, + AddressingModes::ERR, + AddressingModes::ABS, + AddressingModes::ERR, + AddressingModes::ZPX, + AddressingModes::ERR, + AddressingModes::ABX, + ]; +} + +impl AddressingModes +{ + + pub fn ERR(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + // TODO: Better error handling + ModeID::ERR + } + + pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + ModeID::IMP + } + + pub fn ACM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_data = cpu.a as u16; + ModeID::ACM + } + + pub fn IMM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_data = bus.read(cpu.pc) as u16; + cpu.pc += 1; + + ModeID::IMM + } + + pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + cpu.pc += 1; + + cpu.working_data = bus.read(cpu.working_addr) as u16; + + ModeID::ZP0 + } + + pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + cpu.working_addr += cpu.x as u16; + cpu.pc += 1; + + cpu.working_data = bus.read(cpu.working_addr) as u16; + + ModeID::ZPX + } + + pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; + cpu.working_addr += cpu.y as u16; + cpu.pc += 1; + + cpu.working_data = bus.read(cpu.working_addr) as u16; + + ModeID::ZPY + } + + pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + // NOTE: Not sure if we can use the working_data variable for this. + // if any instruction using this address mode needs extra data read + // then we need another variable to store this address + // + // Use working_addr to just like the other modes + cpu.working_data = bus.read(cpu.pc) as u16; + cpu.pc += 1; + + ModeID::REL + } + + pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_addr = bus.read(cpu.pc) as u16; + cpu.pc += 1; + cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; + cpu.pc += 1; + + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; + + ModeID::ABS + } + + pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_addr = bus.read(cpu.pc) as u16; + cpu.pc += 1; + cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; + cpu.pc += 1; + + cpu.working_addr += cpu.x as u16; + + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; + + ModeID::ABX + } + + pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + cpu.working_addr = bus.read(cpu.pc) as u16; + cpu.pc += 1; + cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; + cpu.pc += 1; + + cpu.working_addr += cpu.y as u16; + + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; + + ModeID::ABY + } + + // https://www.nesdev.org/obelisk-6502-guide/addressing.html#IND + // JMP is the only 6502 instruction to support indirection. + // The instruction contains a 16 bit address which identifies the location of + // the least significant byte of another 16 bit memory address which is the real + // target of the instruction. + + // For example if location $0120 contains $FC and location $0121 contains $BA then + // the instruction JMP ($0120) will cause the next instruction execution to occur at + // $BAFC (e.g. the contents of $0120 and $0121). + + pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + // https://www.nesdev.org/obelisk-6502-guide/reference.html#JMP + // NOTE: An original 6502 does not correctly fetch the target address + // if the indirect vector falls on a page boundary (e.g. $xxFF where xx is any value from $00 to $FF). + // In this case it fetches the LSB from $xxFF as expected but takes the MSB from $xx00. + + let ptr_lo = bus.read(cpu.pc) as u16; + cpu.pc += 1; + let ptr_hi = bus.read(cpu.pc) as u16; + cpu.pc += 1; + + let ptr = (ptr_hi << 8) | ptr_lo; + + let addr_lo = bus.read(ptr) as u16; + let mut addr_hi = bus.read(ptr + 1) as u16; + + // Emulate the bug + if ptr_lo == 0xFF + { + addr_hi = bus.read(ptr & 0xFF00) as u16; + } + + cpu.working_addr = (addr_hi << 8) | addr_lo; + + ModeID::IND + } + + + // Indexed Indirect Addressing (IND, X) + // In indexed indirect addressing (referred to as (Indirect, X)), the second byte of the + // instruction is added to the contents of the X register, discarding the carry. + // The result of the addition points to a memory location on the Zero Page which contains + // the low order byte of the effective address. The next memory location in page zero, + // contains the high order byte of the effective address. Both memory locations specifying + // the effective address must be in the Zero Page. + // + // Info from: + // https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf + pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + let offset = bus.read(cpu.pc) as u16; + cpu.pc += 1; + let mut pointer = cpu.x as u16 + offset; + + // discard the carry and wrap + // If the addition goes beyond the Zero Page + // it should wrap around back to the beginning + pointer = pointer & 0x00FF; + + let lo_byte = bus.read(pointer) as u16; + let hi_byte = bus.read(pointer + 1) as u16; + cpu.working_addr = (hi_byte << 0x08) | lo_byte; + + cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; + + ModeID::IZX + } + + + // Indirect Indexed Addressing (IND), Y + // In indirect indexed addressing, the second byte of the instruction points to + // a memory location in page zero. The contents of this memory location are added to + // the contents of the Y register. The result is the low order byte of the effective address. + // The carry from this addition is added to the contents of the next page zero memory + // location, to form the high order byte of the effective address. + // + // Info from: + // https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf + pub fn IZY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID + { + // zp_pointer points to a location in zero page + let zp_pointer = bus.read(cpu.pc) as u16; + cpu.pc += 1; + + // The value at zp_pointer is added to the Y register + let zp_value = bus.read(zp_pointer) as u16; + let sum = zp_value + cpu.y as u16; + + // The sum with the carry discarded is the lo byte + let lo_byte = sum & 0x00FF; + + // The carry plus the value at the next zero page address is the hi byte + let zp_next = bus.read(zp_pointer + 1) as u16; + let temp = (sum & 0xFF00) >> 0x08; + let temp2 = temp + zp_next; + let hi_byte: u8 = (((sum & 0xFF00) >> 0x08) + zp_next) as u8; + + // Store the final address and read the data + cpu.working_addr = ((hi_byte as u16) << 0x08) | lo_byte; + cpu.working_data = bus.read(cpu.working_addr) as u16; + + ModeID::IZY + } } \ No newline at end of file diff --git a/src/r6502/instructions.rs b/src/r6502/instructions.rs index 5a9d77e..cd8c7e5 100644 --- a/src/r6502/instructions.rs +++ b/src/r6502/instructions.rs @@ -1,424 +1,503 @@ - - -#![allow(dead_code, non_snake_case)] - -use super::{R6502, Bus, Flags, addressing_modes::{AddressingModes, ModeID}}; - -// Instruction decoding: -// https://llx.com/Neil/a2/opcodes.html - -// GROUP ONE -// 000 ORA -// 001 AND -// 010 EOR -// 011 ADC -// 100 STA -// 101 LDA -// 110 CMP -// 111 SBC - -pub struct Instructions; - -impl Instructions -{ - pub const GROUP_ONE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ - Instructions::ORA, - Instructions::AND, - Instructions::EOR, - Instructions::ADC, - Instructions::STA, - Instructions::LDA, - Instructions::CMP, - Instructions::SBC, - ]; - - pub const GROUP_TWO_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ - Instructions::ASL, // 000 - Instructions::ROL, // 001 - Instructions::LSR, // 010 - Instructions::ROR, // 011 - Instructions::STX, // 100 - Instructions::LDX, // 101 - Instructions::DEC, // 110 - Instructions::INC, // 111 - ]; -} - -impl Instructions -{ - /////////////////////////////////////////////////////////// - // GROUP ONE - pub fn ORA(cpu: &mut R6502, _bus: &mut dyn Bus) - { - let data = cpu.working_data as u8; - cpu.a = cpu.a | data; - if cpu.a == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.a & 0x80 != 0 - { - cpu.set_flag(Flags::N); - } - } - - pub fn AND(cpu: &mut R6502, bus: &mut dyn Bus) - { - let data = cpu.working_data as u8; - cpu.a = cpu.a & data; - if cpu.a == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.a & 0x80 != 0 - { - cpu.set_flag(Flags::N); - } - } - - pub fn EOR(cpu: &mut R6502, bus: &mut dyn Bus) - { - let data = cpu.working_data as u8; - cpu.a = cpu.a ^ data; - if cpu.a == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.a & 0x80 != 0 - { - cpu.set_flag(Flags::N); - } - } - - // Using a technique written javidx9 - // The code in this function falls under the License (OLC-3) SEE LICENSE FILE - // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L659 - pub fn ADC(cpu: &mut R6502, bus: &mut dyn Bus) - { - let carry = cpu.check_flag(Flags::C) as u16; - - // 16 bit addition to capture the carry easier - let temp: u16 = cpu.a as u16 + cpu.working_data + carry; - - if temp > 255 - { - cpu.set_flag(Flags::C); - } - - if temp == 0 - { - cpu.set_flag(Flags::Z); - } - - let did_overflow = (!((cpu.a as u16) ^ (cpu.working_data as u16)) & ((cpu.a as u16) ^ temp)) & 0x0080; - cpu.clear_flag(Flags::V); - if did_overflow > 0 - { - cpu.set_flag(Flags::V); - } - - cpu.clear_flag(Flags::N); - if temp & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - - cpu.a = (temp & 0x00FF) as u8; - } - - pub fn STA(cpu: &mut R6502, bus: &mut dyn Bus) - { - bus.write(cpu.working_addr, cpu.a); - } - - pub fn LDA(cpu: &mut R6502, bus: &mut dyn Bus) - { - let data = cpu.working_data as u8; - cpu.a = data; - - if cpu.a == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.a & 0x80 != 0 - { - cpu.set_flag(Flags::N); - } - } - - pub fn CMP(cpu: &mut R6502, bus: &mut dyn Bus) - { - let data = cpu.working_data as u8; - if cpu.a >= data - { - cpu.set_flag(Flags::C); - } - else - { - cpu.clear_flag(Flags::C); - } - - if cpu.a == data - { - cpu.set_flag(Flags::Z); - } - else - { - cpu.clear_flag(Flags::Z); - } - - if cpu.a < data - { - cpu.set_flag(Flags::N); - } - else - { - cpu.clear_flag(Flags::N); - } - } - - // Using a technique written javidx9 - // The code in this function falls under the License (OLC-3) SEE LICENSE FILE - // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L714 - // - // More info about the carry bit: - // http://forum.6502.org/viewtopic.php?t=18 - pub fn SBC(cpu: &mut R6502, bus: &mut dyn Bus) - { - let value = cpu.working_data ^ 0x00FF; - let carry = cpu.check_flag(Flags::C) as u16; - - let temp: u16 = cpu.a as u16 + value + carry; - - cpu.clear_flag(Flags::C); - if temp > 255 - { - cpu.set_flag(Flags::C); - } - - cpu.clear_flag(Flags::Z); - if temp == 0 - { - cpu.set_flag(Flags::Z); - } - - let did_overflow = (!((cpu.a as u16) ^ (value)) & ((cpu.a as u16) ^ temp)) & 0x0080; - cpu.clear_flag(Flags::V); - if did_overflow > 0 - { - cpu.set_flag(Flags::V); - } - - cpu.clear_flag(Flags::N); - if temp & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - - cpu.a = (temp & 0x00FF) as u8; - } - - /////////////////////////////////////////////////////////// - // GROUP TWO - pub fn ASL(cpu: &mut R6502, bus: &mut dyn Bus) - { - cpu.clear_flag(Flags::C); - if cpu.working_data as u8 & 0x80 > 0 - { - cpu.set_flag(Flags::C); - } - - let result = cpu.working_data << 1; - - cpu.clear_flag(Flags::Z); - if result == 0 - { - cpu.set_flag(Flags::Z); - } - - if result as u8 & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - - if cpu.addr_mode == ModeID::ACM - { - cpu.a = result as u8; - } - else - { - bus.write(cpu.working_addr, result as u8); - } - } - - pub fn ROL(cpu: &mut R6502, bus: &mut dyn Bus) - { - let old_bit_7 = (cpu.working_data & 0x80) > 0; - let carry = cpu.check_flag(Flags::C) as u16; - let result = (cpu.working_data << 1) ^ carry; - - cpu.clear_flag(Flags::C); - if old_bit_7 - { - cpu.set_flag(Flags::C); - } - - cpu.clear_flag(Flags::N); - if result & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - - cpu.clear_flag(Flags::Z); - if result == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.addr_mode == ModeID::ACM - { - cpu.a = result as u8; - } - else - { - bus.write(cpu.working_addr, result as u8); - } - - } - - pub fn LSR(cpu: &mut R6502, bus: &mut dyn Bus) - { - let old_bit_0 = (cpu.working_data & 0x01) > 0; - let carry = cpu.check_flag(Flags::C) as u16; - let result = cpu.working_data >> 1; - - cpu.clear_flag(Flags::C); - if old_bit_0 - { - cpu.set_flag(Flags::C); - } - - cpu.clear_flag(Flags::N); - if result & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - - cpu.clear_flag(Flags::Z); - if result == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.addr_mode == ModeID::ACM - { - cpu.a = result as u8; - } - else - { - bus.write(cpu.working_addr, result as u8); - } - } - - pub fn ROR(cpu: &mut R6502, bus: &mut dyn Bus) - { - let old_bit_0 = (cpu.working_data & 0x01) > 0; - let carry = cpu.check_flag(Flags::C) as u16; - let temp = carry << 7; - let result = (cpu.working_data >> 1) ^ (carry << 7); - - cpu.clear_flag(Flags::C); - if old_bit_0 - { - cpu.set_flag(Flags::C); - } - - cpu.clear_flag(Flags::N); - if result & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - - cpu.clear_flag(Flags::Z); - if result == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.addr_mode == ModeID::ACM - { - cpu.a = result as u8; - } - else - { - bus.write(cpu.working_addr, result as u8); - } - } - - pub fn STX(cpu: &mut R6502, bus: &mut dyn Bus) - { - bus.write(cpu.working_addr, cpu.x); - } - - pub fn LDX(cpu: &mut R6502, bus: &mut dyn Bus) - { - let data = cpu.working_data as u8; - cpu.x = data; - - if cpu.x == 0 - { - cpu.set_flag(Flags::Z); - } - - if cpu.x & 0x80 != 0 - { - cpu.set_flag(Flags::N); - } - } - - pub fn DEC(cpu: &mut R6502, bus: &mut dyn Bus) - { - let dec_val = bus.read(cpu.working_addr) - 1; - bus.write(cpu.working_addr, dec_val); - - cpu.clear_flag(Flags::Z); - if dec_val == 0 - { - cpu.set_flag(Flags::Z); - } - - cpu.clear_flag(Flags::N); - if dec_val & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - - } - - pub fn INC(cpu: &mut R6502, bus: &mut dyn Bus) - { - let dec_val = bus.read(cpu.working_addr) + 1; - bus.write(cpu.working_addr, dec_val); - - cpu.clear_flag(Flags::Z); - if dec_val == 0 - { - cpu.set_flag(Flags::Z); - } - - cpu.clear_flag(Flags::N); - if dec_val & 0x80 > 0 - { - cpu.set_flag(Flags::N); - } - } - - /////////////////////////////////////////////////////////// - // GROUP THREE - - + + +#![allow(dead_code, non_snake_case)] + +use super::{R6502, Bus, Flags, addressing_modes::{AddressingModes, ModeID}}; + +// Instruction decoding: +// https://llx.com/Neil/a2/opcodes.html + +// GROUP ONE +// 000 ORA +// 001 AND +// 010 EOR +// 011 ADC +// 100 STA +// 101 LDA +// 110 CMP +// 111 SBC + +pub struct Instructions; + +impl Instructions +{ + pub const GROUP_ONE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + Instructions::ORA, + Instructions::AND, + Instructions::EOR, + Instructions::ADC, + Instructions::STA, + Instructions::LDA, + Instructions::CMP, + Instructions::SBC, + ]; + + pub const GROUP_TWO_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + Instructions::ASL, // 000 + Instructions::ROL, // 001 + Instructions::LSR, // 010 + Instructions::ROR, // 011 + Instructions::STX, // 100 + Instructions::LDX, // 101 + Instructions::DEC, // 110 + Instructions::INC, // 111 + ]; + + pub const GROUP_THREE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ + Instructions::ERR, + Instructions::BIT, // 001 BIT + Instructions::JMP, // 010 JMP + Instructions::JMP, // 011 JMP (abs) + Instructions::STY, // 100 STY + Instructions::LDY, // 101 LDY + Instructions::CPY, // 110 CPY + Instructions::CPX, // 111 CPX + ]; + + + +} + +impl Instructions +{ + pub fn ERR(_cpu: &mut R6502, _bus: &mut dyn Bus) + { + // TODO: Better error handling + println!("ERROR: Invalid Instruction"); + } + + /////////////////////////////////////////////////////////// + // GROUP ONE + pub fn ORA(cpu: &mut R6502, _bus: &mut dyn Bus) + { + let data = cpu.working_data as u8; + cpu.a = cpu.a | data; + if cpu.a == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.a & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } + } + + pub fn AND(cpu: &mut R6502, bus: &mut dyn Bus) + { + let data = cpu.working_data as u8; + cpu.a = cpu.a & data; + if cpu.a == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.a & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } + } + + pub fn EOR(cpu: &mut R6502, bus: &mut dyn Bus) + { + let data = cpu.working_data as u8; + cpu.a = cpu.a ^ data; + if cpu.a == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.a & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } + } + + // Using a technique written javidx9 + // The code in this function falls under the License (OLC-3) SEE LICENSE FILE + // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L659 + pub fn ADC(cpu: &mut R6502, bus: &mut dyn Bus) + { + let carry = cpu.check_flag(Flags::C) as u16; + + // 16 bit addition to capture the carry easier + let temp: u16 = cpu.a as u16 + cpu.working_data + carry; + + if temp > 255 + { + cpu.set_flag(Flags::C); + } + + if temp == 0 + { + cpu.set_flag(Flags::Z); + } + + let did_overflow = (!((cpu.a as u16) ^ (cpu.working_data as u16)) & ((cpu.a as u16) ^ temp)) & 0x0080; + cpu.clear_flag(Flags::V); + if did_overflow > 0 + { + cpu.set_flag(Flags::V); + } + + cpu.clear_flag(Flags::N); + if temp & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + cpu.a = (temp & 0x00FF) as u8; + } + + pub fn STA(cpu: &mut R6502, bus: &mut dyn Bus) + { + bus.write(cpu.working_addr, cpu.a); + } + + pub fn LDA(cpu: &mut R6502, bus: &mut dyn Bus) + { + let data = cpu.working_data as u8; + cpu.a = data; + + if cpu.a == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.a & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } + } + + pub fn CMP(cpu: &mut R6502, bus: &mut dyn Bus) + { + let data = cpu.working_data as u8; + if cpu.a >= data + { + cpu.set_flag(Flags::C); + } + else + { + cpu.clear_flag(Flags::C); + } + + if cpu.a == data + { + cpu.set_flag(Flags::Z); + } + else + { + cpu.clear_flag(Flags::Z); + } + + if cpu.a < data + { + cpu.set_flag(Flags::N); + } + else + { + cpu.clear_flag(Flags::N); + } + } + + // Using a technique written javidx9 + // The code in this function falls under the License (OLC-3) SEE LICENSE FILE + // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L714 + // + // More info about the carry bit: + // http://forum.6502.org/viewtopic.php?t=18 + pub fn SBC(cpu: &mut R6502, bus: &mut dyn Bus) + { + let value = cpu.working_data ^ 0x00FF; + let carry = cpu.check_flag(Flags::C) as u16; + + let temp: u16 = cpu.a as u16 + value + carry; + + cpu.clear_flag(Flags::C); + if temp > 255 + { + cpu.set_flag(Flags::C); + } + + cpu.clear_flag(Flags::Z); + if temp == 0 + { + cpu.set_flag(Flags::Z); + } + + let did_overflow = (!((cpu.a as u16) ^ (value)) & ((cpu.a as u16) ^ temp)) & 0x0080; + cpu.clear_flag(Flags::V); + if did_overflow > 0 + { + cpu.set_flag(Flags::V); + } + + cpu.clear_flag(Flags::N); + if temp & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + cpu.a = (temp & 0x00FF) as u8; + } + + /////////////////////////////////////////////////////////// + // GROUP TWO + pub fn ASL(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.clear_flag(Flags::C); + if cpu.working_data as u8 & 0x80 > 0 + { + cpu.set_flag(Flags::C); + } + + let result = cpu.working_data << 1; + + cpu.clear_flag(Flags::Z); + if result == 0 + { + cpu.set_flag(Flags::Z); + } + + if result as u8 & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + if cpu.addr_mode == ModeID::ACM + { + cpu.a = result as u8; + } + else + { + bus.write(cpu.working_addr, result as u8); + } + } + + pub fn ROL(cpu: &mut R6502, bus: &mut dyn Bus) + { + let old_bit_7 = (cpu.working_data & 0x80) > 0; + let carry = cpu.check_flag(Flags::C) as u16; + let result = (cpu.working_data << 1) ^ carry; + + cpu.clear_flag(Flags::C); + if old_bit_7 + { + cpu.set_flag(Flags::C); + } + + cpu.clear_flag(Flags::N); + if result & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + cpu.clear_flag(Flags::Z); + if result == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.addr_mode == ModeID::ACM + { + cpu.a = result as u8; + } + else + { + bus.write(cpu.working_addr, result as u8); + } + + } + + pub fn LSR(cpu: &mut R6502, bus: &mut dyn Bus) + { + let old_bit_0 = (cpu.working_data & 0x01) > 0; + let carry = cpu.check_flag(Flags::C) as u16; + let result = cpu.working_data >> 1; + + cpu.clear_flag(Flags::C); + if old_bit_0 + { + cpu.set_flag(Flags::C); + } + + cpu.clear_flag(Flags::N); + if result & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + cpu.clear_flag(Flags::Z); + if result == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.addr_mode == ModeID::ACM + { + cpu.a = result as u8; + } + else + { + bus.write(cpu.working_addr, result as u8); + } + } + + pub fn ROR(cpu: &mut R6502, bus: &mut dyn Bus) + { + let old_bit_0 = (cpu.working_data & 0x01) > 0; + let carry = cpu.check_flag(Flags::C) as u16; + let temp = carry << 7; + let result = (cpu.working_data >> 1) ^ (carry << 7); + + cpu.clear_flag(Flags::C); + if old_bit_0 + { + cpu.set_flag(Flags::C); + } + + cpu.clear_flag(Flags::N); + if result & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + cpu.clear_flag(Flags::Z); + if result == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.addr_mode == ModeID::ACM + { + cpu.a = result as u8; + } + else + { + bus.write(cpu.working_addr, result as u8); + } + } + + pub fn STX(cpu: &mut R6502, bus: &mut dyn Bus) + { + bus.write(cpu.working_addr, cpu.x); + } + + pub fn LDX(cpu: &mut R6502, bus: &mut dyn Bus) + { + let data = cpu.working_data as u8; + cpu.x = data; + + if cpu.x == 0 + { + cpu.set_flag(Flags::Z); + } + + if cpu.x & 0x80 != 0 + { + cpu.set_flag(Flags::N); + } + } + + pub fn DEC(cpu: &mut R6502, bus: &mut dyn Bus) + { + let dec_val = bus.read(cpu.working_addr) - 1; + bus.write(cpu.working_addr, dec_val); + + cpu.clear_flag(Flags::Z); + if dec_val == 0 + { + cpu.set_flag(Flags::Z); + } + + cpu.clear_flag(Flags::N); + if dec_val & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + + } + + pub fn INC(cpu: &mut R6502, bus: &mut dyn Bus) + { + let dec_val = bus.read(cpu.working_addr) + 1; + bus.write(cpu.working_addr, dec_val); + + cpu.clear_flag(Flags::Z); + if dec_val == 0 + { + cpu.set_flag(Flags::Z); + } + + cpu.clear_flag(Flags::N); + if dec_val & 0x80 > 0 + { + cpu.set_flag(Flags::N); + } + } + + /////////////////////////////////////////////////////////// + // GROUP THREE + pub fn BIT(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.set_flag(Flags::Z); + if cpu.a & (cpu.working_data as u8) > 0 + { + cpu.clear_flag(Flags::Z); + } + + cpu.clear_flag(Flags::V); + if cpu.working_data & 0x0040 > 0 + { + cpu.set_flag(Flags::V); + } + + cpu.clear_flag(Flags::N); + if cpu.working_data & 0x0080 > 0 + { + cpu.set_flag(Flags::N); + } + } + + pub fn JMP(cpu: &mut R6502, bus: &mut dyn Bus) + { + cpu.pc = cpu.working_addr; + } + + // JMP (abs) + // pub fn JPA(cpu: &mut R6502, bus: &mut dyn Bus) + // { + // cpu.pc = cpu.working_addr; + + // } + + pub fn STY(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn LDY(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn CPY(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + pub fn CPX(cpu: &mut R6502, bus: &mut dyn Bus) + { + + } + + /////////////////////////////////////////////////////////// + // BRANCHING + + + /////////////////////////////////////////////////////////// + // INTERRUPT AND SUBROUTINE + + } \ No newline at end of file diff --git a/src/r6502/mod.rs b/src/r6502/mod.rs index 0ca2471..834a5bd 100644 --- a/src/r6502/mod.rs +++ b/src/r6502/mod.rs @@ -1,239 +1,258 @@ - -#![allow(unused_variables, dead_code, non_snake_case)] - -mod addressing_modes; -mod instructions; - -use addressing_modes::{AddressingModes, ModeID}; -use instructions::Instructions; - -pub trait Bus -{ - fn read(&self, addr: u16) -> u8; - fn write(&mut self, addr: u16, value: u8); -} - -// impl Sized for Bus -// { - -// } - - -#[derive(Copy, Clone, Debug)] -pub enum Flags -{ - C = (1 << 0), // Carry Flag - Z = (1 << 1), // Zero Flag - I = (1 << 2), // Interrupt Disable - D = (1 << 3), // Decimal Mode Flag - B = (1 << 4), // Break Command - U = (1 << 5), // Unused - V = (1 << 6), // Overflow Flag - N = (1 << 7), // Negative Flag -} - -pub enum Registers -{ - A, - X, - Y, - PC, - SP, - STATUS, -} - -#[derive(Clone, Copy, PartialEq)] -pub struct R6502 -{ - a: u8, // Accumulator - x: u8, // X Register - y: u8, // Y Register - - pc: u16, // Program Counter - sp: u8, // Stack Pointer - status: u8, // Status Flags - - cycles: u32, // Track cycles - - // Helper Vars - addr_mode: ModeID, - working_data: u16, // value fetched for the ALU - working_addr: u16, -} - -impl R6502 -{ - // constructor - pub fn new() -> R6502 - { - R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, addr_mode: ModeID::IMP, working_data: 0, working_addr: 0 } - } - - // Debug Access - pub fn debug_get_reg(&self, reg: Registers) -> u16 - { - match reg - { - Registers::A => self.a as u16, - Registers::X => self.x as u16, - Registers::Y => self.y as u16, - - Registers::PC => self.pc, - Registers::SP => self.sp as u16, - - Registers::STATUS => self.status as u16, - } - } - - pub fn debug_set_reg(&mut self, reg: Registers, value: u16) - { - match reg - { - Registers::A => self.a = value as u8, - Registers::X => self.x = value as u8, - Registers::Y => self.y = value as u8, - - Registers::PC => self.pc = value, - Registers::SP => self.sp = value as u8, - - Registers::STATUS => self.status = value as u8, - } - } - - // signals - pub fn clock(&mut self, bus: &mut dyn Bus) - { - // TODO: Track instructions cycles - // if self.cycles == 0 - //{ - let opcode = bus.read(self.pc); - self.pc += 1; - - execute(opcode, self, bus); - //self.pc += 1; - //} - - self.cycles -= 1; - } - - pub fn reset(&mut self, bus: &mut dyn Bus) - { - self.a = 0; - self.x = 0; - self.y = 0; - self.sp = 0xFF; // stack actually starts at 0x01FF but we only need the low byte since the end of the stack is at 0x0100 - self.status = 0; - self.set_flag(Flags::U); - - let lo: u16 = bus.read(0xFFFC) as u16; - let hi: u16 = bus.read(0xFFFD) as u16; - - self.pc = (hi << 8) | lo; - - // internal helper variables - self.working_data = 0; - // self.working_addr = 0; - - self.cycles = 8; - } - - pub fn irq(&mut self, bus: &mut impl Bus) - { - - } - - pub fn nmi(&mut self, bus: &mut impl Bus) - { - - } - - // helpers - pub fn set_flag(&mut self, bit: Flags) - { - self.status |= bit as u8; - } - - pub fn clear_flag(&mut self, bit: Flags) - { - self.status &= !(bit as u8); - } - - pub fn check_flag(&mut self, bit: Flags) -> u8 - { - if self.status & (bit as u8) > 0 - { - return 1; - } - - return 0; - } -} - - - -fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) -{ - // Instruction decoding: - // https://llx.com/Neil/a2/opcodes.html - - let group_code = instruction & 0x03; // group one has a bit pattern of xxxxxx01 - match group_code - { - 0x01 => exe_group_one(instruction, cpu, bus), - 0x02 => exe_group_two(instruction, cpu, bus), - - // TODO: Conditionals and specially formatted instructions - - _ => panic!("UNKNOWN INSTRUCTION ADDRESS MODE: {}", group_code) - } -} - -fn exe_group_one(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) -{ - let addr_mask = (instruction & 0x1C) >> 2; - let op_mask = (instruction & 0xE0) >> 5; - - cpu.addr_mode = AddressingModes::GROUP_ONE_ADDRS[addr_mask as usize](cpu, bus); - Instructions::GROUP_ONE_OPS[op_mask as usize](cpu, bus); -} - -fn exe_group_two(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) -{ - let addr_mask = (instruction & 0x1C) >> 2; - let op_mask = (instruction & 0xE0) >> 5; - - // With STX and LDX, "zero page,X" addressing becomes "zero page,Y", and with LDX, "absolute,X" becomes "absolute,Y". - const STX_ZPX: u8 = 0x96; - const LDX_ZPX: u8 = 0xB6; - const LDX_ABX: u8 = 0xBE; - - - match instruction - { - STX_ZPX => - { - cpu.addr_mode = AddressingModes::ZPY(cpu, bus); - Instructions::STX(cpu, bus); - }, - - LDX_ZPX => - { - cpu.addr_mode = AddressingModes::ZPY(cpu, bus); - Instructions::LDX(cpu, bus); - } - - LDX_ABX => - { - - cpu.addr_mode = AddressingModes::ABY(cpu, bus); - Instructions::LDX(cpu, bus); - } - - _ => - { - cpu.addr_mode = AddressingModes::GROUP_TWO_ADDRS[addr_mask as usize](cpu, bus); - Instructions::GROUP_TWO_OPS[op_mask as usize](cpu, bus); - } - } -} - + +#![allow(unused_variables, dead_code, non_snake_case)] + +mod addressing_modes; +mod instructions; + +use addressing_modes::{AddressingModes, ModeID}; +use instructions::Instructions; + +pub trait Bus +{ + fn read(&self, addr: u16) -> u8; + fn write(&mut self, addr: u16, value: u8); +} + +// impl Sized for Bus +// { + +// } + + +#[derive(Copy, Clone, Debug)] +pub enum Flags +{ + C = (1 << 0), // Carry Flag + Z = (1 << 1), // Zero Flag + I = (1 << 2), // Interrupt Disable + D = (1 << 3), // Decimal Mode Flag + B = (1 << 4), // Break Command + U = (1 << 5), // Unused + V = (1 << 6), // Overflow Flag + N = (1 << 7), // Negative Flag +} + +pub enum Registers +{ + A, + X, + Y, + PC, + SP, + STATUS, +} + +#[derive(Clone, Copy, PartialEq)] +pub struct R6502 +{ + a: u8, // Accumulator + x: u8, // X Register + y: u8, // Y Register + + pc: u16, // Program Counter + sp: u8, // Stack Pointer + status: u8, // Status Flags + + cycles: u32, // Track cycles + + // Helper Vars + addr_mode: ModeID, + working_data: u16, // value fetched for the ALU + working_addr: u16, +} + +impl R6502 +{ + // constructor + pub fn new() -> R6502 + { + R6502 { a: 0, x: 0, y: 0, pc: 0, sp: 0, status: 0, cycles: 0, addr_mode: ModeID::IMP, working_data: 0, working_addr: 0 } + } + + // Debug Access + pub fn debug_get_reg(&self, reg: Registers) -> u16 + { + match reg + { + Registers::A => self.a as u16, + Registers::X => self.x as u16, + Registers::Y => self.y as u16, + + Registers::PC => self.pc, + Registers::SP => self.sp as u16, + + Registers::STATUS => self.status as u16, + } + } + + pub fn debug_set_reg(&mut self, reg: Registers, value: u16) + { + match reg + { + Registers::A => self.a = value as u8, + Registers::X => self.x = value as u8, + Registers::Y => self.y = value as u8, + + Registers::PC => self.pc = value, + Registers::SP => self.sp = value as u8, + + Registers::STATUS => self.status = value as u8, + } + } + + // signals + pub fn clock(&mut self, bus: &mut dyn Bus) + { + // TODO: Track instructions cycles + // if self.cycles == 0 + //{ + let opcode = bus.read(self.pc); + self.pc += 1; + + execute(opcode, self, bus); + //self.pc += 1; + //} + + self.cycles -= 1; + } + + pub fn reset(&mut self, bus: &mut dyn Bus) + { + self.a = 0; + self.x = 0; + self.y = 0; + self.sp = 0xFF; // stack actually starts at 0x01FF but we only need the low byte since the end of the stack is at 0x0100 + self.status = 0; + self.set_flag(Flags::U); + + let lo: u16 = bus.read(0xFFFC) as u16; + let hi: u16 = bus.read(0xFFFD) as u16; + + self.pc = (hi << 8) | lo; + + // internal helper variables + self.working_data = 0; + // self.working_addr = 0; + + self.cycles = 8; + } + + pub fn irq(&mut self, bus: &mut impl Bus) + { + + } + + pub fn nmi(&mut self, bus: &mut impl Bus) + { + + } + + // helpers + pub fn set_flag(&mut self, bit: Flags) + { + self.status |= bit as u8; + } + + pub fn clear_flag(&mut self, bit: Flags) + { + self.status &= !(bit as u8); + } + + pub fn check_flag(&mut self, bit: Flags) -> u8 + { + if self.status & (bit as u8) > 0 + { + return 1; + } + + return 0; + } +} + + + +fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + // Instruction decoding: + // https://llx.com/Neil/a2/opcodes.html + + let group_code = instruction & 0x03; // group one has a bit pattern of xxxxxx01 + match group_code + { + 0x01 => exe_group_one(instruction, cpu, bus), + 0x02 => exe_group_two(instruction, cpu, bus), + 0x00 => exe_group_three(instruction, cpu, bus), + + // TODO: Conditionals and specially formatted instructions + + _ => panic!("UNKNOWN INSTRUCTION ADDRESS MODE: {}", group_code) + } +} + +fn exe_group_one(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + let addr_mask = (instruction & 0x1C) >> 2; + let op_mask = (instruction & 0xE0) >> 5; + + cpu.addr_mode = AddressingModes::GROUP_ONE_ADDRS[addr_mask as usize](cpu, bus); + Instructions::GROUP_ONE_OPS[op_mask as usize](cpu, bus); +} + +fn exe_group_two(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + let addr_mask = (instruction & 0x1C) >> 2; + let op_mask = (instruction & 0xE0) >> 5; + + // With STX and LDX, "zero page,X" addressing becomes "zero page,Y", and with LDX, "absolute,X" becomes "absolute,Y". + const STX_ZPX: u8 = 0x96; + const LDX_ZPX: u8 = 0xB6; + const LDX_ABX: u8 = 0xBE; + + + match instruction + { + STX_ZPX => + { + cpu.addr_mode = AddressingModes::ZPY(cpu, bus); + Instructions::STX(cpu, bus); + }, + + LDX_ZPX => + { + cpu.addr_mode = AddressingModes::ZPY(cpu, bus); + Instructions::LDX(cpu, bus); + } + + LDX_ABX => + { + + cpu.addr_mode = AddressingModes::ABY(cpu, bus); + Instructions::LDX(cpu, bus); + } + + _ => + { + cpu.addr_mode = AddressingModes::GROUP_TWO_ADDRS[addr_mask as usize](cpu, bus); + Instructions::GROUP_TWO_OPS[op_mask as usize](cpu, bus); + } + } +} + +fn exe_group_three(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) +{ + let addr_mask = (instruction & 0x1C) >> 2; + let op_mask = (instruction & 0xE0) >> 5; + + // SPECIAL CASE FOR JMP (abs) + const JMP_IND: u8 = 0x6C; + if instruction == JMP_IND + { + cpu.addr_mode = AddressingModes::IND(cpu, bus); + } + else + { + cpu.addr_mode = AddressingModes::GROUP_THREE_ADDRS[addr_mask as usize](cpu, bus); + } + + Instructions::GROUP_THREE_OPS[op_mask as usize](cpu, bus); +} \ No newline at end of file diff --git a/src/tests/addressing_modes/mod.rs b/src/tests/addressing_modes/mod.rs index a4c2a83..f77ca2b 100644 --- a/src/tests/addressing_modes/mod.rs +++ b/src/tests/addressing_modes/mod.rs @@ -1,300 +1,300 @@ - -#![allow(dead_code, non_snake_case)] - -use crate::tests::test_bus::RAMBus; -use crate::r6502::{R6502, Bus, Registers}; - -#[test] -fn IMP() -{ - -} - -#[test] -fn ACM() -{ - -} - -#[test] -fn IMM() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xA9); // LDA - Immediate mode - bus.write(addr + 1, 0x08); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); -} - -#[test] -fn ZP0() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x000A, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xA5); // LDA - Zero Page mode - bus.write(addr + 1, 0x0A); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); -} - -#[test] -fn ZPX() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x000A, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xB5); // LDA - Zero Page, X mode - bus.write(addr + 1, 0x04); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::X, 0x06); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); -} - -#[test] -fn ZPY() -{ - -} - -#[test] -fn REL() -{ - -} - - -#[test] -fn ABS() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x010A, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xAD); // LDA - Absolute mode - bus.write(addr + 1, 0x0A); // Argument lo word - bus.write(addr + 2, 0x01); // Argument hi word - - // Restart cpu - cpu.reset(&mut bus); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); -} - -#[test] -fn ABX() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x010B, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xBD); // LDA - Absolute, X mode - bus.write(addr + 1, 0x0A); // Argument lo word - bus.write(addr + 2, 0x01); // Argument hi word - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::X, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); -} - -#[test] -fn ABY() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory in the zero page - bus.write(0x010B, 0x08); - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xB9); // LDA - Absolute, X mode - bus.write(addr + 1, 0x0A); // Argument lo word - bus.write(addr + 2, 0x01); // Argument hi word - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::Y, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); -} - -#[test] -fn IND() -{ - -} - -#[test] -fn IZX() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory - bus.write(0x010B, 0x08); - - // Manuall put 0x010B into the Zero page at 0x000A - bus.write(0x000A, 0x0B); // Pointer lo byte - bus.write(0x000B, 0x01); // Pointer hi byte - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xA1); // LDA - Indirect, X mode - bus.write(addr + 1, 0x09); // Argument - gets added to X reg - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::X, 0x01); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); -} - -#[test] -fn IZY() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Manually put 0x08 into memory - bus.write(0x020B, 0x08); - - // Manuall put 0x01FC into the Zero page at 0x000A - // This will be added to the Y register (which will store 0x0F) - bus.write(0x000A, 0xFC); // Pointer lo byte - bus.write(0x000B, 0x01); // Pointer hi byte - - // Program to load 0x08 into the accumulator - bus.write(addr, 0xB1); // LDA - Indirect, Y mode - bus.write(addr + 1, 0x0A); // Argument - Pointer into the Zero Page - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::Y, 0x0F); // Offset of the value at the zero page address - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x08 in the A register? - assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers}; + +#[test] +fn IMP() +{ + +} + +#[test] +fn ACM() +{ + +} + +#[test] +fn IMM() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xA9); // LDA - Immediate mode + bus.write(addr + 1, 0x08); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZP0() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x000A, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xA5); // LDA - Zero Page mode + bus.write(addr + 1, 0x0A); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZPX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x000A, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xB5); // LDA - Zero Page, X mode + bus.write(addr + 1, 0x04); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x06); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ZPY() +{ + +} + +#[test] +fn REL() +{ + +} + + +#[test] +fn ABS() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x010A, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xAD); // LDA - Absolute mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x010B, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xBD); // LDA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn ABY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory in the zero page + bus.write(0x010B, 0x08); + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xB9); // LDA - Absolute, X mode + bus.write(addr + 1, 0x0A); // Argument lo word + bus.write(addr + 2, 0x01); // Argument hi word + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn IND() +{ + +} + +#[test] +fn IZX() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory + bus.write(0x010B, 0x08); + + // Manuall put 0x010B into the Zero page at 0x000A + bus.write(0x000A, 0x0B); // Pointer lo byte + bus.write(0x000B, 0x01); // Pointer hi byte + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xA1); // LDA - Indirect, X mode + bus.write(addr + 1, 0x09); // Argument - gets added to X reg + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::X, 0x01); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); +} + +#[test] +fn IZY() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Manually put 0x08 into memory + bus.write(0x020B, 0x08); + + // Manuall put 0x01FC into the Zero page at 0x000A + // This will be added to the Y register (which will store 0x0F) + bus.write(0x000A, 0xFC); // Pointer lo byte + bus.write(0x000B, 0x01); // Pointer hi byte + + // Program to load 0x08 into the accumulator + bus.write(addr, 0xB1); // LDA - Indirect, Y mode + bus.write(addr + 1, 0x0A); // Argument - Pointer into the Zero Page + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::Y, 0x0F); // Offset of the value at the zero page address + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x08 in the A register? + assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); } \ No newline at end of file diff --git a/src/tests/instructions/ADC.rs b/src/tests/instructions/ADC.rs index ed9955c..7a362c4 100644 --- a/src/tests/instructions/ADC.rs +++ b/src/tests/instructions/ADC.rs @@ -1,109 +1,109 @@ - -#![allow(dead_code, non_snake_case)] - -use crate::tests::test_bus::RAMBus; -use crate::r6502::{R6502, Bus, Registers, Flags}; - -#[test] -fn basic() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Write the program to memory - // ADC 6 - bus.write(addr, 0x69); // ADC - Immediate mode - bus.write(addr + 1, 0x06); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x04); - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0A in the A register? - assert_eq!(0x0A, cpu.debug_get_reg(Registers::A), "Wrong answer"); - assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); - assert_eq!(0, cpu.check_flag(Flags::C), "Carry bit should not be set"); - -} - -#[test] -fn with_carry() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Add with carry in - // Write the program to memory - // ADC 6 - bus.write(addr, 0x69); // ADC - Immediate mode - bus.write(addr + 1, 0x06); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu internals - cpu.debug_set_reg(Registers::A, 0x04); - cpu.set_flag(Flags::C); - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x0B in the A register? - assert_eq!(0x0B, cpu.debug_get_reg(Registers::A), "Wrong answer"); - assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); - -} - -#[test] -fn with_overflow() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Add with overflow - // Write the program to memory - // ADC 126 - bus.write(addr, 0x69); // ADC - Immediate mode - bus.write(addr + 1, 126); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu internals - cpu.debug_set_reg(Registers::A, 0x04); - - // Clock the cpu twice (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 130 in the A register? - assert_eq!(130, cpu.debug_get_reg(Registers::A)); - - // Is the overflow bit set? - assert_eq!(1, cpu.check_flag(Flags::V), "Failed addition with overflow"); + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers, Flags}; + +#[test] +fn basic() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Write the program to memory + // ADC 6 + bus.write(addr, 0x69); // ADC - Immediate mode + bus.write(addr + 1, 0x06); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x04); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0A in the A register? + assert_eq!(0x0A, cpu.debug_get_reg(Registers::A), "Wrong answer"); + assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); + assert_eq!(0, cpu.check_flag(Flags::C), "Carry bit should not be set"); + +} + +#[test] +fn with_carry() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Add with carry in + // Write the program to memory + // ADC 6 + bus.write(addr, 0x69); // ADC - Immediate mode + bus.write(addr + 1, 0x06); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu internals + cpu.debug_set_reg(Registers::A, 0x04); + cpu.set_flag(Flags::C); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x0B in the A register? + assert_eq!(0x0B, cpu.debug_get_reg(Registers::A), "Wrong answer"); + assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); + +} + +#[test] +fn with_overflow() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Add with overflow + // Write the program to memory + // ADC 126 + bus.write(addr, 0x69); // ADC - Immediate mode + bus.write(addr + 1, 126); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu internals + cpu.debug_set_reg(Registers::A, 0x04); + + // Clock the cpu twice (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 130 in the A register? + assert_eq!(130, cpu.debug_get_reg(Registers::A)); + + // Is the overflow bit set? + assert_eq!(1, cpu.check_flag(Flags::V), "Failed addition with overflow"); } \ No newline at end of file diff --git a/src/tests/instructions/CMP.rs b/src/tests/instructions/CMP.rs index 50f8a2f..330d8b5 100644 --- a/src/tests/instructions/CMP.rs +++ b/src/tests/instructions/CMP.rs @@ -1,110 +1,110 @@ - -#![allow(dead_code, non_snake_case)] - -use crate::tests::test_bus::RAMBus; -use crate::r6502::{R6502, Bus, Registers, Flags}; - -#[test] -fn less_than() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - /////////////////////// - // Parameter is less than A reg - - // Program to compare 0x10 with 0x05 (0x05 will be the argument) - bus.write(addr, 0xC9); // CMP - Immediate mode - bus.write(addr + 1, 0x05); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x10); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // C Flag should be 1, Z N Flags should be 0 - assert_eq!(1, cpu.check_flag(Flags::C)); - assert_eq!(0, cpu.check_flag(Flags::Z)); - assert_eq!(0, cpu.check_flag(Flags::N)); -} - -#[test] -fn equal_to() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - /////////////////////// - // Parameter is equal to the A reg - - // Program to compare 0x10 with 0x10 - bus.write(addr, 0xC9); // CMP - Immediate mode - bus.write(addr + 1, 0x10); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x10); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // C Z Flags should be 1, N Flag should be 0 - assert_eq!(1, cpu.check_flag(Flags::C)); - assert_eq!(1, cpu.check_flag(Flags::Z)); - assert_eq!(0, cpu.check_flag(Flags::N)); -} - -#[test] -fn greater_than() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - /////////////////////// - // Parameter is greater than A reg - - // Program to compare 0x05 with 0x10 - bus.write(addr, 0xC9); // CMP - Immediate mode - bus.write(addr + 1, 0x10); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu registers - cpu.debug_set_reg(Registers::A, 0x05); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // C Z Flags should be 0, N Flag should be 1 - assert_eq!(0, cpu.check_flag(Flags::C)); - assert_eq!(0, cpu.check_flag(Flags::Z)); - assert_eq!(1, cpu.check_flag(Flags::N)); + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers, Flags}; + +#[test] +fn less_than() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + /////////////////////// + // Parameter is less than A reg + + // Program to compare 0x10 with 0x05 (0x05 will be the argument) + bus.write(addr, 0xC9); // CMP - Immediate mode + bus.write(addr + 1, 0x05); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Flag should be 1, Z N Flags should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); +} + +#[test] +fn equal_to() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + /////////////////////// + // Parameter is equal to the A reg + + // Program to compare 0x10 with 0x10 + bus.write(addr, 0xC9); // CMP - Immediate mode + bus.write(addr + 1, 0x10); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x10); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 1, N Flag should be 0 + assert_eq!(1, cpu.check_flag(Flags::C)); + assert_eq!(1, cpu.check_flag(Flags::Z)); + assert_eq!(0, cpu.check_flag(Flags::N)); +} + +#[test] +fn greater_than() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + /////////////////////// + // Parameter is greater than A reg + + // Program to compare 0x05 with 0x10 + bus.write(addr, 0xC9); // CMP - Immediate mode + bus.write(addr + 1, 0x10); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu registers + cpu.debug_set_reg(Registers::A, 0x05); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // C Z Flags should be 0, N Flag should be 1 + assert_eq!(0, cpu.check_flag(Flags::C)); + assert_eq!(0, cpu.check_flag(Flags::Z)); + assert_eq!(1, cpu.check_flag(Flags::N)); } \ No newline at end of file diff --git a/src/tests/instructions/SBC.rs b/src/tests/instructions/SBC.rs index d7224ba..c16f189 100644 --- a/src/tests/instructions/SBC.rs +++ b/src/tests/instructions/SBC.rs @@ -1,105 +1,105 @@ - -#![allow(dead_code, non_snake_case)] - -use crate::tests::test_bus::RAMBus; -use crate::r6502::{R6502, Bus, Registers, Flags}; - - -#[test] -fn basic() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Program to subtract 0x06 from 0x0A - bus.write(addr, 0xE9); // SBC - Immediate mode - bus.write(addr + 1, 0x06); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu state - cpu.set_flag(Flags::C); - cpu.debug_set_reg(Registers::A, 0x0A); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is 0x04 in the A register? - assert_eq!(0x04, cpu.debug_get_reg(Registers::A), "wrong answer"); - assert_eq!(1, cpu.check_flag(Flags::C), "Carry bit should be set"); - assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); -} - -#[test] -fn with_carry() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Program to subtract 0x09 from 0x08 - bus.write(addr, 0xE9); // SBC - Immediate mode - bus.write(addr + 1, 0x09); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu state - cpu.set_flag(Flags::C); - cpu.debug_set_reg(Registers::A, 0x08); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - // Is -1 in the A register? - assert_eq!(0xFF as u16, cpu.debug_get_reg(Registers::A), "Wrong answer"); - assert_eq!(0, cpu.check_flag(Flags::C), "Carry bit should not be set"); - assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); -} - -#[test] -fn with_overflow() -{ - let mut cpu = R6502::new(); - let mut bus = RAMBus::new(); - - // program address - let addr = 0x0020 as u16; - - // Set the program counter address - bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte - bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte - - // Program to subtract 0x7E from 0xFB (-5 - 126) - bus.write(addr, 0xE9); // SBC - Immediate mode - bus.write(addr + 1, 0x7E); // Argument - - // Restart cpu - cpu.reset(&mut bus); - - // manually setup the cpu state - cpu.set_flag(Flags::C); - cpu.debug_set_reg(Registers::A, 0xFB); - - // Clock the cpu to run the program (Clock essentially runs one full instruction) - cpu.clock(&mut bus); - - - assert_eq!(0x7D, cpu.debug_get_reg(Registers::A), "Wrong answer"); - assert_eq!(1, cpu.check_flag(Flags::C), "Carry bit should be set"); - assert_eq!(1, cpu.check_flag(Flags::V), "Overflow bit should be set"); -} + +#![allow(dead_code, non_snake_case)] + +use crate::tests::test_bus::RAMBus; +use crate::r6502::{R6502, Bus, Registers, Flags}; + + +#[test] +fn basic() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Program to subtract 0x06 from 0x0A + bus.write(addr, 0xE9); // SBC - Immediate mode + bus.write(addr + 1, 0x06); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu state + cpu.set_flag(Flags::C); + cpu.debug_set_reg(Registers::A, 0x0A); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is 0x04 in the A register? + assert_eq!(0x04, cpu.debug_get_reg(Registers::A), "wrong answer"); + assert_eq!(1, cpu.check_flag(Flags::C), "Carry bit should be set"); + assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); +} + +#[test] +fn with_carry() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Program to subtract 0x09 from 0x08 + bus.write(addr, 0xE9); // SBC - Immediate mode + bus.write(addr + 1, 0x09); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu state + cpu.set_flag(Flags::C); + cpu.debug_set_reg(Registers::A, 0x08); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is -1 in the A register? + assert_eq!(0xFF as u16, cpu.debug_get_reg(Registers::A), "Wrong answer"); + assert_eq!(0, cpu.check_flag(Flags::C), "Carry bit should not be set"); + assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); +} + +#[test] +fn with_overflow() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Program to subtract 0x7E from 0xFB (-5 - 126) + bus.write(addr, 0xE9); // SBC - Immediate mode + bus.write(addr + 1, 0x7E); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // manually setup the cpu state + cpu.set_flag(Flags::C); + cpu.debug_set_reg(Registers::A, 0xFB); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + + assert_eq!(0x7D, cpu.debug_get_reg(Registers::A), "Wrong answer"); + assert_eq!(1, cpu.check_flag(Flags::C), "Carry bit should be set"); + assert_eq!(1, cpu.check_flag(Flags::V), "Overflow bit should be set"); +} diff --git a/src/tests/instructions/mod.rs b/src/tests/instructions/mod.rs index 6132451..4144c69 100644 --- a/src/tests/instructions/mod.rs +++ b/src/tests/instructions/mod.rs @@ -457,4 +457,81 @@ fn INC() // Is 0x11 in memory at 0x08? assert_eq!(0x11, bus.read(0x08)); +} + + +///////////////////////////////////////////////////////////////////// +// GROUP THREE +///////////////////////////////////////////////////////////////////// +#[test] +fn BIT() +{ + // TODO: Could add more tests for this instruction + // Maybe move into it's own file and test + // every result pattern. + + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Put value to test into memory + bus.write(0x08, 0x10); + + // BIT test program + bus.write(addr, 0x24); // BIT - Zero Page + bus.write(addr + 1, 0x08); // Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Preload A register with bit mask + cpu.debug_set_reg(Registers::A, 0x05); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is the Z flag set? + assert_eq!(1, cpu.check_flag(Flags::Z)); + + // Is the N flag and V flag not set? + assert_eq!(0, cpu.check_flag(Flags::V)); + assert_eq!(0, cpu.check_flag(Flags::N)); +} + +#[test] +fn JMP() +{ + let mut cpu = R6502::new(); + let mut bus = RAMBus::new(); + + // program address + let addr = 0x0020 as u16; + + // Set the program counter address + bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte + bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte + + // Put jump location into memory + bus.write(0x08, 0x34); + bus.write(0x09, 0x12); + + // JMP test program + bus.write(addr, 0x6C); // JMP - IND + bus.write(addr + 1, 0x08); // LO Argument + bus.write(addr + 2, 0x00); // HI Argument + + // Restart cpu + cpu.reset(&mut bus); + + // Clock the cpu to run the program (Clock essentially runs one full instruction) + cpu.clock(&mut bus); + + // Is the program counter now 0x1234? + assert_eq!(0x1234, cpu.debug_get_reg(Registers::PC)); } \ No newline at end of file diff --git a/src/tests/test_bus.rs b/src/tests/test_bus.rs index 91ac7b2..eb6821e 100644 --- a/src/tests/test_bus.rs +++ b/src/tests/test_bus.rs @@ -1,28 +1,28 @@ -use crate::r6502::Bus; - -// All-RAM bus for testing -pub struct RAMBus -{ - ram: [u8; 64 * 1024] -} - -impl RAMBus -{ - pub fn new() -> RAMBus - { - RAMBus { ram: [0; 64 * 1024] } - } -} - -impl Bus for RAMBus -{ - fn read(&self, addr: u16) -> u8 - { - self.ram[addr as usize] - } - - fn write(&mut self, addr: u16, value: u8) - { - self.ram[addr as usize] = value; - } +use crate::r6502::Bus; + +// All-RAM bus for testing +pub struct RAMBus +{ + ram: [u8; 64 * 1024] +} + +impl RAMBus +{ + pub fn new() -> RAMBus + { + RAMBus { ram: [0; 64 * 1024] } + } +} + +impl Bus for RAMBus +{ + fn read(&self, addr: u16) -> u8 + { + self.ram[addr as usize] + } + + fn write(&mut self, addr: u16, value: u8) + { + self.ram[addr as usize] = value; + } } \ No newline at end of file diff --git a/todo/todo.todo b/todo/todo.todo index 1820346..59f57ff 100644 --- a/todo/todo.todo +++ b/todo/todo.todo @@ -50,9 +50,9 @@ Instructions: ✔ 111 INC @done(24-01-08 16:01) GROUP THREE: - ☐ 001 BIT - ☐ 010 JMP - ☐ 011 JMP (abs) + ✔ 001 BIT @done(24-01-09 16:16) + ✔ 010 JMP @done(24-01-09 16:16) + ✔ 011 JMP (abs) @done(24-01-09 16:16) ☐ 100 STY ☐ 101 LDY ☐ 110 CPY