Adds BIT and JMP instructions

joeyrp 2 years ago
parent 400e378492
commit d65c7cfd88

@ -1,45 +1,45 @@
{ {
// Use IntelliSense to learn about possible attributes. // Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes. // Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0", "version": "0.2.0",
"configurations": [ "configurations": [
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Debug executable 're6502'", "name": "Debug executable 're6502'",
"cargo": { "cargo": {
"args": [ "args": [
"build", "build",
"--bin=re6502", "--bin=re6502",
"--package=re6502" "--package=re6502"
], ],
"filter": { "filter": {
"name": "re6502", "name": "re6502",
"kind": "bin" "kind": "bin"
} }
}, },
"args": [], "args": [],
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
}, },
{ {
"type": "lldb", "type": "lldb",
"request": "launch", "request": "launch",
"name": "Debug unit tests in executable 're6502'", "name": "Debug unit tests in executable 're6502'",
"cargo": { "cargo": {
"args": [ "args": [
"test", "test",
"--no-run", "--no-run",
"--bin=re6502", "--bin=re6502",
"--package=re6502" "--package=re6502"
], ],
"filter": { "filter": {
"name": "re6502", "name": "re6502",
"kind": "bin" "kind": "bin"
} }
}, },
"args": [], "args": [],
"cwd": "${workspaceFolder}" "cwd": "${workspaceFolder}"
} }
] ]
} }

@ -1,37 +1,37 @@
The code in the methods r6502::instructions::ADC and r6502::instructions::SDC 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: is adapted from code written by javidx9 (David Barr) and is subject to the following license:
License (OLC-3) License (OLC-3)
~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~
Copyright 2018-2019 OneLoneCoder.com Copyright 2018-2019 OneLoneCoder.com
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions modification, are permitted provided that the following conditions
are met: are met:
1. Redistributions or derivations of source code must retain the above 1. Redistributions or derivations of source code must retain the above
copyright notice, this list of conditions and the following disclaimer. copyright notice, this list of conditions and the following disclaimer.
2. Redistributions or derivative works in binary form must reproduce 2. Redistributions or derivative works in binary form must reproduce
the above copyright notice. This list of conditions and the following the above copyright notice. This list of conditions and the following
disclaimer must be reproduced in the documentation and/or other disclaimer must be reproduced in the documentation and/or other
materials provided with the distribution. materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its 3. Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived contributors may be used to endorse or promote products derived
from this software without specific prior written permission. from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

@ -1,259 +1,310 @@
#![allow(unused_variables, dead_code, non_snake_case)] #![allow(unused_variables, dead_code, non_snake_case)]
use super::{R6502, Bus}; use super::{R6502, Bus};
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub enum ModeID pub enum ModeID
{ {
IMP, // Implied IMP, // Implied
ACM, // Accumulator ACM, // Accumulator
IMM, // Immediate IMM, // Immediate
ZP0, // Zero Page ZP0, // Zero Page
ZPX, // Zero Page, X ZPX, // Zero Page, X
ZPY, // Zero Page, Y ZPY, // Zero Page, Y
REL, // Relative REL, // Relative
ABS, // Absolute ABS, // Absolute
ABX, // Absolute, X ABX, // Absolute, X
ABY, // Aboslute, Y ABY, // Aboslute, Y
IND, // Indirect IND, // Indirect
IZX, // Indirect, X IZX, // Indirect, X
IZY, // Indirect, Y IZY, // Indirect, Y
ERR, // Error mode - this is an invalid mode ERR, // Error mode - this is an invalid mode
} }
// Instruction decoding: // Instruction decoding:
// https://llx.com/Neil/a2/opcodes.html // https://llx.com/Neil/a2/opcodes.html
// GROUP ONE ADDRESS MODES // GROUP ONE ADDRESS MODES
// 000 (zero page,X) IZX // 000 (zero page,X) IZX
// 001 zero page ZP0 // 001 zero page ZP0
// 010 #immediate IMM // 010 #immediate IMM
// 011 absolute ABS // 011 absolute ABS
// 100 (zero page),Y IZY // 100 (zero page),Y IZY
// 101 zero page,X ZPX // 101 zero page,X ZPX
// 110 absolute,Y ABY // 110 absolute,Y ABY
// 111 absolute,X ABX // 111 absolute,X ABX
// GROUP TWO ADDRESS MODES // GROUP TWO ADDRESS MODES
// 000 #immediate IMM // 000 #immediate IMM
// 001 zero page ZP0 // 001 zero page ZP0
// 010 accumulator ACM // 010 accumulator ACM
// 011 absolute ABS // 011 absolute ABS
// 100 NONE ERR // 100 NONE ERR
// 101 zero page,X ZPX // 101 zero page,X ZPX
// 110 NONE ERR // 110 NONE ERR
// 111 absolute,X ABX // 111 absolute,X ABX
pub struct AddressingModes; // GROUP THREE ADDRES MODES
impl AddressingModes // 000 #immediate IMM
{ // 001 zero page ZP0
pub const GROUP_ONE_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ // 011 absolute ABS
AddressingModes::IZX, // 101 zero page,X ZPX
AddressingModes::ZP0, // 111 absolute,X ABX
AddressingModes::IMM,
AddressingModes::ABS, pub struct AddressingModes;
AddressingModes::IZY, impl AddressingModes
AddressingModes::ZPX, {
AddressingModes::ABY, pub const GROUP_ONE_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [
AddressingModes::ABX, AddressingModes::IZX,
]; AddressingModes::ZP0,
AddressingModes::IMM,
pub const GROUP_TWO_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [ AddressingModes::ABS,
AddressingModes::IMM, AddressingModes::IZY,
AddressingModes::ZP0, AddressingModes::ZPX,
AddressingModes::ACM, AddressingModes::ABY,
AddressingModes::ABS, AddressingModes::ABX,
AddressingModes::ERR, ];
AddressingModes::ZPX,
AddressingModes::ERR, pub const GROUP_TWO_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [
AddressingModes::ABX, AddressingModes::IMM,
]; AddressingModes::ZP0,
} AddressingModes::ACM,
AddressingModes::ABS,
impl AddressingModes AddressingModes::ERR,
{ AddressingModes::ZPX,
AddressingModes::ERR,
pub fn ERR(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID AddressingModes::ABX,
{ ];
ModeID::ERR
} pub const GROUP_THREE_ADDRS: [fn(&mut R6502, &mut dyn Bus) -> ModeID; 8] = [
AddressingModes::IMM,
pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID AddressingModes::ZP0,
{ AddressingModes::ERR,
ModeID::IMP AddressingModes::ABS,
} AddressingModes::ERR,
AddressingModes::ZPX,
pub fn ACM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID AddressingModes::ERR,
{ AddressingModes::ABX,
cpu.working_data = cpu.a as u16; ];
ModeID::ACM }
}
impl AddressingModes
pub fn IMM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID {
{
cpu.working_data = bus.read(cpu.pc) as u16; pub fn ERR(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
cpu.pc += 1; {
// TODO: Better error handling
ModeID::IMM ModeID::ERR
} }
pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID pub fn IMP(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{ {
cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF; ModeID::IMP
cpu.pc += 1; }
cpu.working_data = bus.read(cpu.working_addr) as u16; pub fn ACM(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
ModeID::ZP0 cpu.working_data = cpu.a as u16;
} ModeID::ACM
}
pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{ pub fn IMM(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.working_data = bus.read(cpu.pc) as u16;
cpu.pc += 1; cpu.pc += 1;
cpu.working_data = bus.read(cpu.working_addr) as u16; ModeID::IMM
}
ModeID::ZPX
} pub fn ZP0(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF;
{ cpu.pc += 1;
cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF;
cpu.working_addr += cpu.y as u16; cpu.working_data = bus.read(cpu.working_addr) as u16;
cpu.pc += 1;
ModeID::ZP0
cpu.working_data = bus.read(cpu.working_addr) as u16; }
ModeID::ZPY pub fn ZPX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
} {
cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF;
pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID cpu.working_addr += cpu.x as u16;
{ cpu.pc += 1;
// NOTE: Not sure if we can use the working_data variable for this.
// if any instruction using this address mode needs extra data read cpu.working_data = bus.read(cpu.working_addr) as u16;
// then we need another variable to store this address
// ModeID::ZPX
// Use working_addr to just like the other modes }
cpu.working_data = bus.read(cpu.pc) as u16;
cpu.pc += 1; pub fn ZPY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
ModeID::REL cpu.working_addr = bus.read(cpu.pc) as u16 & 0x00FF;
} cpu.working_addr += cpu.y as u16;
cpu.pc += 1;
pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{ cpu.working_data = bus.read(cpu.working_addr) as u16;
cpu.working_addr = bus.read(cpu.pc) as u16;
cpu.pc += 1; ModeID::ZPY
cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; }
cpu.pc += 1;
pub fn REL(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; {
// NOTE: Not sure if we can use the working_data variable for this.
ModeID::ABS // if any instruction using this address mode needs extra data read
} // then we need another variable to store this address
//
pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID // Use working_addr to just like the other modes
{ cpu.working_data = bus.read(cpu.pc) as u16;
cpu.working_addr = bus.read(cpu.pc) as u16; cpu.pc += 1;
cpu.pc += 1;
cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8; ModeID::REL
cpu.pc += 1; }
cpu.working_addr += cpu.x as u16; pub fn ABS(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; cpu.working_addr = bus.read(cpu.pc) as u16;
cpu.pc += 1;
ModeID::ABX cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8;
} cpu.pc += 1;
pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF;
{
cpu.working_addr = bus.read(cpu.pc) as u16; ModeID::ABS
cpu.pc += 1; }
cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8;
cpu.pc += 1; pub fn ABX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
cpu.working_addr += cpu.y as u16; cpu.working_addr = bus.read(cpu.pc) as u16;
cpu.pc += 1;
cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8;
cpu.pc += 1;
ModeID::ABY
} cpu.working_addr += cpu.x as u16;
pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF;
{
ModeID::ABX
ModeID::IND }
}
pub fn ABY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
// Indexed Indirect Addressing (IND, X) cpu.working_addr = bus.read(cpu.pc) as u16;
// In indexed indirect addressing (referred to as (Indirect, X)), the second byte of the cpu.pc += 1;
// instruction is added to the contents of the X register, discarding the carry. cpu.working_addr |= (bus.read(cpu.pc) as u16) << 8;
// The result of the addition points to a memory location on the Zero Page which contains cpu.pc += 1;
// 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 cpu.working_addr += cpu.y as u16;
// the effective address must be in the Zero Page.
// cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF;
// Info from:
// https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf ModeID::ABY
pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID }
{
let offset = bus.read(cpu.pc) as u16; // https://www.nesdev.org/obelisk-6502-guide/addressing.html#IND
cpu.pc += 1; // JMP is the only 6502 instruction to support indirection.
let mut pointer = cpu.x as u16 + offset; // 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
// discard the carry and wrap // target of the instruction.
// If the addition goes beyond the Zero Page
// it should wrap around back to the beginning // For example if location $0120 contains $FC and location $0121 contains $BA then
pointer = pointer & 0x00FF; // the instruction JMP ($0120) will cause the next instruction execution to occur at
// $BAFC (e.g. the contents of $0120 and $0121).
let lo_byte = bus.read(pointer) as u16;
let hi_byte = bus.read(pointer + 1) as u16; pub fn IND(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
cpu.working_addr = (hi_byte << 0x08) | lo_byte; {
// https://www.nesdev.org/obelisk-6502-guide/reference.html#JMP
cpu.working_data = bus.read(cpu.working_addr) as u16 & 0x00FF; // 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).
ModeID::IZX // 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;
// Indirect Indexed Addressing (IND), Y let ptr_hi = bus.read(cpu.pc) as u16;
// In indirect indexed addressing, the second byte of the instruction points to cpu.pc += 1;
// 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. let ptr = (ptr_hi << 8) | ptr_lo;
// 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. let addr_lo = bus.read(ptr) as u16;
// let mut addr_hi = bus.read(ptr + 1) as u16;
// Info from:
// https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf // Emulate the bug
pub fn IZY(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID if ptr_lo == 0xFF
{ {
// zp_pointer points to a location in zero page addr_hi = bus.read(ptr & 0xFF00) as u16;
let zp_pointer = bus.read(cpu.pc) as u16; }
cpu.pc += 1;
cpu.working_addr = (addr_hi << 8) | addr_lo;
// The value at zp_pointer is added to the Y register
let zp_value = bus.read(zp_pointer) as u16; ModeID::IND
let sum = zp_value + cpu.y as u16; }
// The sum with the carry discarded is the lo byte
let lo_byte = sum & 0x00FF; // Indexed Indirect Addressing (IND, X)
// In indexed indirect addressing (referred to as (Indirect, X)), the second byte of the
// The carry plus the value at the next zero page address is the hi byte // instruction is added to the contents of the X register, discarding the carry.
let zp_next = bus.read(zp_pointer + 1) as u16; // The result of the addition points to a memory location on the Zero Page which contains
let temp = (sum & 0xFF00) >> 0x08; // the low order byte of the effective address. The next memory location in page zero,
let temp2 = temp + zp_next; // contains the high order byte of the effective address. Both memory locations specifying
let hi_byte: u8 = (((sum & 0xFF00) >> 0x08) + zp_next) as u8; // the effective address must be in the Zero Page.
//
// Store the final address and read the data // Info from:
cpu.working_addr = ((hi_byte as u16) << 0x08) | lo_byte; // https://web.archive.org/web/20221112231348if_/http://archive.6502.org/datasheets/rockwell_r650x_r651x.pdf
cpu.working_data = bus.read(cpu.working_addr) as u16; pub fn IZX(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
ModeID::IZY 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
}
} }

@ -1,424 +1,503 @@
#![allow(dead_code, non_snake_case)] #![allow(dead_code, non_snake_case)]
use super::{R6502, Bus, Flags, addressing_modes::{AddressingModes, ModeID}}; use super::{R6502, Bus, Flags, addressing_modes::{AddressingModes, ModeID}};
// Instruction decoding: // Instruction decoding:
// https://llx.com/Neil/a2/opcodes.html // https://llx.com/Neil/a2/opcodes.html
// GROUP ONE // GROUP ONE
// 000 ORA // 000 ORA
// 001 AND // 001 AND
// 010 EOR // 010 EOR
// 011 ADC // 011 ADC
// 100 STA // 100 STA
// 101 LDA // 101 LDA
// 110 CMP // 110 CMP
// 111 SBC // 111 SBC
pub struct Instructions; pub struct Instructions;
impl Instructions impl Instructions
{ {
pub const GROUP_ONE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ pub const GROUP_ONE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [
Instructions::ORA, Instructions::ORA,
Instructions::AND, Instructions::AND,
Instructions::EOR, Instructions::EOR,
Instructions::ADC, Instructions::ADC,
Instructions::STA, Instructions::STA,
Instructions::LDA, Instructions::LDA,
Instructions::CMP, Instructions::CMP,
Instructions::SBC, Instructions::SBC,
]; ];
pub const GROUP_TWO_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [ pub const GROUP_TWO_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [
Instructions::ASL, // 000 Instructions::ASL, // 000
Instructions::ROL, // 001 Instructions::ROL, // 001
Instructions::LSR, // 010 Instructions::LSR, // 010
Instructions::ROR, // 011 Instructions::ROR, // 011
Instructions::STX, // 100 Instructions::STX, // 100
Instructions::LDX, // 101 Instructions::LDX, // 101
Instructions::DEC, // 110 Instructions::DEC, // 110
Instructions::INC, // 111 Instructions::INC, // 111
]; ];
}
pub const GROUP_THREE_OPS: [fn(&mut R6502, &mut dyn Bus); 8] = [
impl Instructions Instructions::ERR,
{ Instructions::BIT, // 001 BIT
/////////////////////////////////////////////////////////// Instructions::JMP, // 010 JMP
// GROUP ONE Instructions::JMP, // 011 JMP (abs)
pub fn ORA(cpu: &mut R6502, _bus: &mut dyn Bus) Instructions::STY, // 100 STY
{ Instructions::LDY, // 101 LDY
let data = cpu.working_data as u8; Instructions::CPY, // 110 CPY
cpu.a = cpu.a | data; Instructions::CPX, // 111 CPX
if cpu.a == 0 ];
{
cpu.set_flag(Flags::Z);
}
}
if cpu.a & 0x80 != 0
{ impl Instructions
cpu.set_flag(Flags::N); {
} pub fn ERR(_cpu: &mut R6502, _bus: &mut dyn Bus)
} {
// TODO: Better error handling
pub fn AND(cpu: &mut R6502, bus: &mut dyn Bus) println!("ERROR: Invalid Instruction");
{ }
let data = cpu.working_data as u8;
cpu.a = cpu.a & data; ///////////////////////////////////////////////////////////
if cpu.a == 0 // GROUP ONE
{ pub fn ORA(cpu: &mut R6502, _bus: &mut dyn Bus)
cpu.set_flag(Flags::Z); {
} let data = cpu.working_data as u8;
cpu.a = cpu.a | data;
if cpu.a & 0x80 != 0 if cpu.a == 0
{ {
cpu.set_flag(Flags::N); cpu.set_flag(Flags::Z);
} }
}
if cpu.a & 0x80 != 0
pub fn EOR(cpu: &mut R6502, bus: &mut dyn Bus) {
{ cpu.set_flag(Flags::N);
let data = cpu.working_data as u8; }
cpu.a = cpu.a ^ data; }
if cpu.a == 0
{ pub fn AND(cpu: &mut R6502, bus: &mut dyn Bus)
cpu.set_flag(Flags::Z); {
} let data = cpu.working_data as u8;
cpu.a = cpu.a & data;
if cpu.a & 0x80 != 0 if cpu.a == 0
{ {
cpu.set_flag(Flags::N); cpu.set_flag(Flags::Z);
} }
}
if cpu.a & 0x80 != 0
// Using a technique written javidx9 {
// The code in this function falls under the License (OLC-3) SEE LICENSE FILE cpu.set_flag(Flags::N);
// 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; pub fn EOR(cpu: &mut R6502, bus: &mut dyn Bus)
{
// 16 bit addition to capture the carry easier let data = cpu.working_data as u8;
let temp: u16 = cpu.a as u16 + cpu.working_data + carry; cpu.a = cpu.a ^ data;
if cpu.a == 0
if temp > 255 {
{ cpu.set_flag(Flags::Z);
cpu.set_flag(Flags::C); }
}
if cpu.a & 0x80 != 0
if temp == 0 {
{ cpu.set_flag(Flags::N);
cpu.set_flag(Flags::Z); }
} }
let did_overflow = (!((cpu.a as u16) ^ (cpu.working_data as u16)) & ((cpu.a as u16) ^ temp)) & 0x0080; // Using a technique written javidx9
cpu.clear_flag(Flags::V); // The code in this function falls under the License (OLC-3) SEE LICENSE FILE
if did_overflow > 0 // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L659
{ pub fn ADC(cpu: &mut R6502, bus: &mut dyn Bus)
cpu.set_flag(Flags::V); {
} let carry = cpu.check_flag(Flags::C) as u16;
cpu.clear_flag(Flags::N); // 16 bit addition to capture the carry easier
if temp & 0x80 > 0 let temp: u16 = cpu.a as u16 + cpu.working_data + carry;
{
cpu.set_flag(Flags::N); if temp > 255
} {
cpu.set_flag(Flags::C);
cpu.a = (temp & 0x00FF) as u8; }
}
if temp == 0
pub fn STA(cpu: &mut R6502, bus: &mut dyn Bus) {
{ cpu.set_flag(Flags::Z);
bus.write(cpu.working_addr, cpu.a); }
}
let did_overflow = (!((cpu.a as u16) ^ (cpu.working_data as u16)) & ((cpu.a as u16) ^ temp)) & 0x0080;
pub fn LDA(cpu: &mut R6502, bus: &mut dyn Bus) cpu.clear_flag(Flags::V);
{ if did_overflow > 0
let data = cpu.working_data as u8; {
cpu.a = data; cpu.set_flag(Flags::V);
}
if cpu.a == 0
{ cpu.clear_flag(Flags::N);
cpu.set_flag(Flags::Z); if temp & 0x80 > 0
} {
cpu.set_flag(Flags::N);
if cpu.a & 0x80 != 0 }
{
cpu.set_flag(Flags::N); cpu.a = (temp & 0x00FF) as u8;
} }
}
pub fn STA(cpu: &mut R6502, bus: &mut dyn Bus)
pub fn CMP(cpu: &mut R6502, bus: &mut dyn Bus) {
{ bus.write(cpu.working_addr, cpu.a);
let data = cpu.working_data as u8; }
if cpu.a >= data
{ pub fn LDA(cpu: &mut R6502, bus: &mut dyn Bus)
cpu.set_flag(Flags::C); {
} let data = cpu.working_data as u8;
else cpu.a = data;
{
cpu.clear_flag(Flags::C); if cpu.a == 0
} {
cpu.set_flag(Flags::Z);
if cpu.a == data }
{
cpu.set_flag(Flags::Z); if cpu.a & 0x80 != 0
} {
else cpu.set_flag(Flags::N);
{ }
cpu.clear_flag(Flags::Z); }
}
pub fn CMP(cpu: &mut R6502, bus: &mut dyn Bus)
if cpu.a < data {
{ let data = cpu.working_data as u8;
cpu.set_flag(Flags::N); if cpu.a >= data
} {
else cpu.set_flag(Flags::C);
{ }
cpu.clear_flag(Flags::N); else
} {
} cpu.clear_flag(Flags::C);
}
// Using a technique written javidx9
// The code in this function falls under the License (OLC-3) SEE LICENSE FILE if cpu.a == data
// https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L714 {
// cpu.set_flag(Flags::Z);
// More info about the carry bit: }
// http://forum.6502.org/viewtopic.php?t=18 else
pub fn SBC(cpu: &mut R6502, bus: &mut dyn Bus) {
{ cpu.clear_flag(Flags::Z);
let value = cpu.working_data ^ 0x00FF; }
let carry = cpu.check_flag(Flags::C) as u16;
if cpu.a < data
let temp: u16 = cpu.a as u16 + value + carry; {
cpu.set_flag(Flags::N);
cpu.clear_flag(Flags::C); }
if temp > 255 else
{ {
cpu.set_flag(Flags::C); cpu.clear_flag(Flags::N);
} }
}
cpu.clear_flag(Flags::Z);
if temp == 0 // Using a technique written javidx9
{ // The code in this function falls under the License (OLC-3) SEE LICENSE FILE
cpu.set_flag(Flags::Z); // https://github.com/OneLoneCoder/olcNES/blob/master/Part%232%20-%20CPU/olc6502.cpp#L714
} //
// More info about the carry bit:
let did_overflow = (!((cpu.a as u16) ^ (value)) & ((cpu.a as u16) ^ temp)) & 0x0080; // http://forum.6502.org/viewtopic.php?t=18
cpu.clear_flag(Flags::V); pub fn SBC(cpu: &mut R6502, bus: &mut dyn Bus)
if did_overflow > 0 {
{ let value = cpu.working_data ^ 0x00FF;
cpu.set_flag(Flags::V); let carry = cpu.check_flag(Flags::C) as u16;
}
let temp: u16 = cpu.a as u16 + value + carry;
cpu.clear_flag(Flags::N);
if temp & 0x80 > 0 cpu.clear_flag(Flags::C);
{ if temp > 255
cpu.set_flag(Flags::N); {
} cpu.set_flag(Flags::C);
}
cpu.a = (temp & 0x00FF) as u8;
} cpu.clear_flag(Flags::Z);
if temp == 0
/////////////////////////////////////////////////////////// {
// GROUP TWO cpu.set_flag(Flags::Z);
pub fn ASL(cpu: &mut R6502, bus: &mut dyn Bus) }
{
cpu.clear_flag(Flags::C); let did_overflow = (!((cpu.a as u16) ^ (value)) & ((cpu.a as u16) ^ temp)) & 0x0080;
if cpu.working_data as u8 & 0x80 > 0 cpu.clear_flag(Flags::V);
{ if did_overflow > 0
cpu.set_flag(Flags::C); {
} cpu.set_flag(Flags::V);
}
let result = cpu.working_data << 1;
cpu.clear_flag(Flags::N);
cpu.clear_flag(Flags::Z); if temp & 0x80 > 0
if result == 0 {
{ cpu.set_flag(Flags::N);
cpu.set_flag(Flags::Z); }
}
cpu.a = (temp & 0x00FF) as u8;
if result as u8 & 0x80 > 0 }
{
cpu.set_flag(Flags::N); ///////////////////////////////////////////////////////////
} // GROUP TWO
pub fn ASL(cpu: &mut R6502, bus: &mut dyn Bus)
if cpu.addr_mode == ModeID::ACM {
{ cpu.clear_flag(Flags::C);
cpu.a = result as u8; if cpu.working_data as u8 & 0x80 > 0
} {
else cpu.set_flag(Flags::C);
{ }
bus.write(cpu.working_addr, result as u8);
} let result = cpu.working_data << 1;
}
cpu.clear_flag(Flags::Z);
pub fn ROL(cpu: &mut R6502, bus: &mut dyn Bus) if result == 0
{ {
let old_bit_7 = (cpu.working_data & 0x80) > 0; cpu.set_flag(Flags::Z);
let carry = cpu.check_flag(Flags::C) as u16; }
let result = (cpu.working_data << 1) ^ carry;
if result as u8 & 0x80 > 0
cpu.clear_flag(Flags::C); {
if old_bit_7 cpu.set_flag(Flags::N);
{ }
cpu.set_flag(Flags::C);
} if cpu.addr_mode == ModeID::ACM
{
cpu.clear_flag(Flags::N); cpu.a = result as u8;
if result & 0x80 > 0 }
{ else
cpu.set_flag(Flags::N); {
} bus.write(cpu.working_addr, result as u8);
}
cpu.clear_flag(Flags::Z); }
if result == 0
{ pub fn ROL(cpu: &mut R6502, bus: &mut dyn Bus)
cpu.set_flag(Flags::Z); {
} let old_bit_7 = (cpu.working_data & 0x80) > 0;
let carry = cpu.check_flag(Flags::C) as u16;
if cpu.addr_mode == ModeID::ACM let result = (cpu.working_data << 1) ^ carry;
{
cpu.a = result as u8; cpu.clear_flag(Flags::C);
} if old_bit_7
else {
{ cpu.set_flag(Flags::C);
bus.write(cpu.working_addr, result as u8); }
}
cpu.clear_flag(Flags::N);
} if result & 0x80 > 0
{
pub fn LSR(cpu: &mut R6502, bus: &mut dyn Bus) cpu.set_flag(Flags::N);
{ }
let old_bit_0 = (cpu.working_data & 0x01) > 0;
let carry = cpu.check_flag(Flags::C) as u16; cpu.clear_flag(Flags::Z);
let result = cpu.working_data >> 1; if result == 0
{
cpu.clear_flag(Flags::C); cpu.set_flag(Flags::Z);
if old_bit_0 }
{
cpu.set_flag(Flags::C); if cpu.addr_mode == ModeID::ACM
} {
cpu.a = result as u8;
cpu.clear_flag(Flags::N); }
if result & 0x80 > 0 else
{ {
cpu.set_flag(Flags::N); bus.write(cpu.working_addr, result as u8);
} }
cpu.clear_flag(Flags::Z); }
if result == 0
{ pub fn LSR(cpu: &mut R6502, bus: &mut dyn Bus)
cpu.set_flag(Flags::Z); {
} let old_bit_0 = (cpu.working_data & 0x01) > 0;
let carry = cpu.check_flag(Flags::C) as u16;
if cpu.addr_mode == ModeID::ACM let result = cpu.working_data >> 1;
{
cpu.a = result as u8; cpu.clear_flag(Flags::C);
} if old_bit_0
else {
{ cpu.set_flag(Flags::C);
bus.write(cpu.working_addr, result as u8); }
}
} cpu.clear_flag(Flags::N);
if result & 0x80 > 0
pub fn ROR(cpu: &mut R6502, bus: &mut dyn Bus) {
{ cpu.set_flag(Flags::N);
let old_bit_0 = (cpu.working_data & 0x01) > 0; }
let carry = cpu.check_flag(Flags::C) as u16;
let temp = carry << 7; cpu.clear_flag(Flags::Z);
let result = (cpu.working_data >> 1) ^ (carry << 7); if result == 0
{
cpu.clear_flag(Flags::C); cpu.set_flag(Flags::Z);
if old_bit_0 }
{
cpu.set_flag(Flags::C); if cpu.addr_mode == ModeID::ACM
} {
cpu.a = result as u8;
cpu.clear_flag(Flags::N); }
if result & 0x80 > 0 else
{ {
cpu.set_flag(Flags::N); bus.write(cpu.working_addr, result as u8);
} }
}
cpu.clear_flag(Flags::Z);
if result == 0 pub fn ROR(cpu: &mut R6502, bus: &mut dyn Bus)
{ {
cpu.set_flag(Flags::Z); let old_bit_0 = (cpu.working_data & 0x01) > 0;
} let carry = cpu.check_flag(Flags::C) as u16;
let temp = carry << 7;
if cpu.addr_mode == ModeID::ACM let result = (cpu.working_data >> 1) ^ (carry << 7);
{
cpu.a = result as u8; cpu.clear_flag(Flags::C);
} if old_bit_0
else {
{ cpu.set_flag(Flags::C);
bus.write(cpu.working_addr, result as u8); }
}
} cpu.clear_flag(Flags::N);
if result & 0x80 > 0
pub fn STX(cpu: &mut R6502, bus: &mut dyn Bus) {
{ cpu.set_flag(Flags::N);
bus.write(cpu.working_addr, cpu.x); }
}
cpu.clear_flag(Flags::Z);
pub fn LDX(cpu: &mut R6502, bus: &mut dyn Bus) if result == 0
{ {
let data = cpu.working_data as u8; cpu.set_flag(Flags::Z);
cpu.x = data; }
if cpu.x == 0 if cpu.addr_mode == ModeID::ACM
{ {
cpu.set_flag(Flags::Z); cpu.a = result as u8;
} }
else
if cpu.x & 0x80 != 0 {
{ bus.write(cpu.working_addr, result as u8);
cpu.set_flag(Flags::N); }
} }
}
pub fn STX(cpu: &mut R6502, bus: &mut dyn Bus)
pub fn DEC(cpu: &mut R6502, bus: &mut dyn Bus) {
{ bus.write(cpu.working_addr, cpu.x);
let dec_val = bus.read(cpu.working_addr) - 1; }
bus.write(cpu.working_addr, dec_val);
pub fn LDX(cpu: &mut R6502, bus: &mut dyn Bus)
cpu.clear_flag(Flags::Z); {
if dec_val == 0 let data = cpu.working_data as u8;
{ cpu.x = data;
cpu.set_flag(Flags::Z);
} if cpu.x == 0
{
cpu.clear_flag(Flags::N); cpu.set_flag(Flags::Z);
if dec_val & 0x80 > 0 }
{
cpu.set_flag(Flags::N); if cpu.x & 0x80 != 0
} {
cpu.set_flag(Flags::N);
} }
}
pub fn INC(cpu: &mut R6502, bus: &mut dyn Bus)
{ 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); 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.clear_flag(Flags::Z);
{ if dec_val == 0
cpu.set_flag(Flags::Z); {
} cpu.set_flag(Flags::Z);
}
cpu.clear_flag(Flags::N);
if dec_val & 0x80 > 0 cpu.clear_flag(Flags::N);
{ if dec_val & 0x80 > 0
cpu.set_flag(Flags::N); {
} cpu.set_flag(Flags::N);
} }
/////////////////////////////////////////////////////////// }
// GROUP THREE
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
} }

@ -1,239 +1,258 @@
#![allow(unused_variables, dead_code, non_snake_case)] #![allow(unused_variables, dead_code, non_snake_case)]
mod addressing_modes; mod addressing_modes;
mod instructions; mod instructions;
use addressing_modes::{AddressingModes, ModeID}; use addressing_modes::{AddressingModes, ModeID};
use instructions::Instructions; use instructions::Instructions;
pub trait Bus pub trait Bus
{ {
fn read(&self, addr: u16) -> u8; fn read(&self, addr: u16) -> u8;
fn write(&mut self, addr: u16, value: u8); fn write(&mut self, addr: u16, value: u8);
} }
// impl Sized for Bus // impl Sized for Bus
// { // {
// } // }
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum Flags pub enum Flags
{ {
C = (1 << 0), // Carry Flag C = (1 << 0), // Carry Flag
Z = (1 << 1), // Zero Flag Z = (1 << 1), // Zero Flag
I = (1 << 2), // Interrupt Disable I = (1 << 2), // Interrupt Disable
D = (1 << 3), // Decimal Mode Flag D = (1 << 3), // Decimal Mode Flag
B = (1 << 4), // Break Command B = (1 << 4), // Break Command
U = (1 << 5), // Unused U = (1 << 5), // Unused
V = (1 << 6), // Overflow Flag V = (1 << 6), // Overflow Flag
N = (1 << 7), // Negative Flag N = (1 << 7), // Negative Flag
} }
pub enum Registers pub enum Registers
{ {
A, A,
X, X,
Y, Y,
PC, PC,
SP, SP,
STATUS, STATUS,
} }
#[derive(Clone, Copy, PartialEq)] #[derive(Clone, Copy, PartialEq)]
pub struct R6502 pub struct R6502
{ {
a: u8, // Accumulator a: u8, // Accumulator
x: u8, // X Register x: u8, // X Register
y: u8, // Y Register y: u8, // Y Register
pc: u16, // Program Counter pc: u16, // Program Counter
sp: u8, // Stack Pointer sp: u8, // Stack Pointer
status: u8, // Status Flags status: u8, // Status Flags
cycles: u32, // Track cycles cycles: u32, // Track cycles
// Helper Vars // Helper Vars
addr_mode: ModeID, addr_mode: ModeID,
working_data: u16, // value fetched for the ALU working_data: u16, // value fetched for the ALU
working_addr: u16, working_addr: u16,
} }
impl R6502 impl R6502
{ {
// constructor // constructor
pub fn new() -> R6502 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 } 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 // Debug Access
pub fn debug_get_reg(&self, reg: Registers) -> u16 pub fn debug_get_reg(&self, reg: Registers) -> u16
{ {
match reg match reg
{ {
Registers::A => self.a as u16, Registers::A => self.a as u16,
Registers::X => self.x as u16, Registers::X => self.x as u16,
Registers::Y => self.y as u16, Registers::Y => self.y as u16,
Registers::PC => self.pc, Registers::PC => self.pc,
Registers::SP => self.sp as u16, Registers::SP => self.sp as u16,
Registers::STATUS => self.status as u16, Registers::STATUS => self.status as u16,
} }
} }
pub fn debug_set_reg(&mut self, reg: Registers, value: u16) pub fn debug_set_reg(&mut self, reg: Registers, value: u16)
{ {
match reg match reg
{ {
Registers::A => self.a = value as u8, Registers::A => self.a = value as u8,
Registers::X => self.x = value as u8, Registers::X => self.x = value as u8,
Registers::Y => self.y = value as u8, Registers::Y => self.y = value as u8,
Registers::PC => self.pc = value, Registers::PC => self.pc = value,
Registers::SP => self.sp = value as u8, Registers::SP => self.sp = value as u8,
Registers::STATUS => self.status = value as u8, Registers::STATUS => self.status = value as u8,
} }
} }
// signals // signals
pub fn clock(&mut self, bus: &mut dyn Bus) pub fn clock(&mut self, bus: &mut dyn Bus)
{ {
// TODO: Track instructions cycles // TODO: Track instructions cycles
// if self.cycles == 0 // if self.cycles == 0
//{ //{
let opcode = bus.read(self.pc); let opcode = bus.read(self.pc);
self.pc += 1; self.pc += 1;
execute(opcode, self, bus); execute(opcode, self, bus);
//self.pc += 1; //self.pc += 1;
//} //}
self.cycles -= 1; self.cycles -= 1;
} }
pub fn reset(&mut self, bus: &mut dyn Bus) pub fn reset(&mut self, bus: &mut dyn Bus)
{ {
self.a = 0; self.a = 0;
self.x = 0; self.x = 0;
self.y = 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.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.status = 0;
self.set_flag(Flags::U); self.set_flag(Flags::U);
let lo: u16 = bus.read(0xFFFC) as u16; let lo: u16 = bus.read(0xFFFC) as u16;
let hi: u16 = bus.read(0xFFFD) as u16; let hi: u16 = bus.read(0xFFFD) as u16;
self.pc = (hi << 8) | lo; self.pc = (hi << 8) | lo;
// internal helper variables // internal helper variables
self.working_data = 0; self.working_data = 0;
// self.working_addr = 0; // self.working_addr = 0;
self.cycles = 8; self.cycles = 8;
} }
pub fn irq(&mut self, bus: &mut impl Bus) pub fn irq(&mut self, bus: &mut impl Bus)
{ {
} }
pub fn nmi(&mut self, bus: &mut impl Bus) pub fn nmi(&mut self, bus: &mut impl Bus)
{ {
} }
// helpers // helpers
pub fn set_flag(&mut self, bit: Flags) pub fn set_flag(&mut self, bit: Flags)
{ {
self.status |= bit as u8; self.status |= bit as u8;
} }
pub fn clear_flag(&mut self, bit: Flags) pub fn clear_flag(&mut self, bit: Flags)
{ {
self.status &= !(bit as u8); self.status &= !(bit as u8);
} }
pub fn check_flag(&mut self, bit: Flags) -> u8 pub fn check_flag(&mut self, bit: Flags) -> u8
{ {
if self.status & (bit as u8) > 0 if self.status & (bit as u8) > 0
{ {
return 1; return 1;
} }
return 0; return 0;
} }
} }
fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus) fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus)
{ {
// Instruction decoding: // Instruction decoding:
// https://llx.com/Neil/a2/opcodes.html // https://llx.com/Neil/a2/opcodes.html
let group_code = instruction & 0x03; // group one has a bit pattern of xxxxxx01 let group_code = instruction & 0x03; // group one has a bit pattern of xxxxxx01
match group_code match group_code
{ {
0x01 => exe_group_one(instruction, cpu, bus), 0x01 => exe_group_one(instruction, cpu, bus),
0x02 => exe_group_two(instruction, cpu, bus), 0x02 => exe_group_two(instruction, cpu, bus),
0x00 => exe_group_three(instruction, cpu, bus),
// TODO: Conditionals and specially formatted instructions
// TODO: Conditionals and specially formatted instructions
_ => panic!("UNKNOWN INSTRUCTION ADDRESS MODE: {}", group_code)
} _ => panic!("UNKNOWN INSTRUCTION ADDRESS MODE: {}", group_code)
} }
}
fn exe_group_one(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus)
{ 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; 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); 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)
{ 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; 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; // With STX and LDX, "zero page,X" addressing becomes "zero page,Y", and with LDX, "absolute,X" becomes "absolute,Y".
const LDX_ZPX: u8 = 0xB6; const STX_ZPX: u8 = 0x96;
const LDX_ABX: u8 = 0xBE; const LDX_ZPX: u8 = 0xB6;
const LDX_ABX: u8 = 0xBE;
match instruction
{ match instruction
STX_ZPX => {
{ STX_ZPX =>
cpu.addr_mode = AddressingModes::ZPY(cpu, bus); {
Instructions::STX(cpu, bus); cpu.addr_mode = AddressingModes::ZPY(cpu, bus);
}, Instructions::STX(cpu, bus);
},
LDX_ZPX =>
{ LDX_ZPX =>
cpu.addr_mode = AddressingModes::ZPY(cpu, bus); {
Instructions::LDX(cpu, bus); cpu.addr_mode = AddressingModes::ZPY(cpu, bus);
} Instructions::LDX(cpu, bus);
}
LDX_ABX =>
{ LDX_ABX =>
{
cpu.addr_mode = AddressingModes::ABY(cpu, bus);
Instructions::LDX(cpu, bus); 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); 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);
}

@ -1,300 +1,300 @@
#![allow(dead_code, non_snake_case)] #![allow(dead_code, non_snake_case)]
use crate::tests::test_bus::RAMBus; use crate::tests::test_bus::RAMBus;
use crate::r6502::{R6502, Bus, Registers}; use crate::r6502::{R6502, Bus, Registers};
#[test] #[test]
fn IMP() fn IMP()
{ {
} }
#[test] #[test]
fn ACM() fn ACM()
{ {
} }
#[test] #[test]
fn IMM() fn IMM()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xA9); // LDA - Immediate mode bus.write(addr, 0xA9); // LDA - Immediate mode
bus.write(addr + 1, 0x08); // Argument bus.write(addr + 1, 0x08); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }
#[test] #[test]
fn ZP0() fn ZP0()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Manually put 0x08 into memory in the zero page // Manually put 0x08 into memory in the zero page
bus.write(0x000A, 0x08); bus.write(0x000A, 0x08);
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xA5); // LDA - Zero Page mode bus.write(addr, 0xA5); // LDA - Zero Page mode
bus.write(addr + 1, 0x0A); // Argument bus.write(addr + 1, 0x0A); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }
#[test] #[test]
fn ZPX() fn ZPX()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Manually put 0x08 into memory in the zero page // Manually put 0x08 into memory in the zero page
bus.write(0x000A, 0x08); bus.write(0x000A, 0x08);
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xB5); // LDA - Zero Page, X mode bus.write(addr, 0xB5); // LDA - Zero Page, X mode
bus.write(addr + 1, 0x04); // Argument bus.write(addr + 1, 0x04); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::X, 0x06); cpu.debug_set_reg(Registers::X, 0x06);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }
#[test] #[test]
fn ZPY() fn ZPY()
{ {
} }
#[test] #[test]
fn REL() fn REL()
{ {
} }
#[test] #[test]
fn ABS() fn ABS()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Manually put 0x08 into memory in the zero page // Manually put 0x08 into memory in the zero page
bus.write(0x010A, 0x08); bus.write(0x010A, 0x08);
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xAD); // LDA - Absolute mode bus.write(addr, 0xAD); // LDA - Absolute mode
bus.write(addr + 1, 0x0A); // Argument lo word bus.write(addr + 1, 0x0A); // Argument lo word
bus.write(addr + 2, 0x01); // Argument hi word bus.write(addr + 2, 0x01); // Argument hi word
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }
#[test] #[test]
fn ABX() fn ABX()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Manually put 0x08 into memory in the zero page // Manually put 0x08 into memory in the zero page
bus.write(0x010B, 0x08); bus.write(0x010B, 0x08);
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xBD); // LDA - Absolute, X mode bus.write(addr, 0xBD); // LDA - Absolute, X mode
bus.write(addr + 1, 0x0A); // Argument lo word bus.write(addr + 1, 0x0A); // Argument lo word
bus.write(addr + 2, 0x01); // Argument hi word bus.write(addr + 2, 0x01); // Argument hi word
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::X, 0x01); cpu.debug_set_reg(Registers::X, 0x01);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }
#[test] #[test]
fn ABY() fn ABY()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Manually put 0x08 into memory in the zero page // Manually put 0x08 into memory in the zero page
bus.write(0x010B, 0x08); bus.write(0x010B, 0x08);
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xB9); // LDA - Absolute, X mode bus.write(addr, 0xB9); // LDA - Absolute, X mode
bus.write(addr + 1, 0x0A); // Argument lo word bus.write(addr + 1, 0x0A); // Argument lo word
bus.write(addr + 2, 0x01); // Argument hi word bus.write(addr + 2, 0x01); // Argument hi word
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::Y, 0x01); cpu.debug_set_reg(Registers::Y, 0x01);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }
#[test] #[test]
fn IND() fn IND()
{ {
} }
#[test] #[test]
fn IZX() fn IZX()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Manually put 0x08 into memory // Manually put 0x08 into memory
bus.write(0x010B, 0x08); bus.write(0x010B, 0x08);
// Manuall put 0x010B into the Zero page at 0x000A // Manuall put 0x010B into the Zero page at 0x000A
bus.write(0x000A, 0x0B); // Pointer lo byte bus.write(0x000A, 0x0B); // Pointer lo byte
bus.write(0x000B, 0x01); // Pointer hi byte bus.write(0x000B, 0x01); // Pointer hi byte
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xA1); // LDA - Indirect, X mode bus.write(addr, 0xA1); // LDA - Indirect, X mode
bus.write(addr + 1, 0x09); // Argument - gets added to X reg bus.write(addr + 1, 0x09); // Argument - gets added to X reg
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::X, 0x01); cpu.debug_set_reg(Registers::X, 0x01);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }
#[test] #[test]
fn IZY() fn IZY()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Manually put 0x08 into memory // Manually put 0x08 into memory
bus.write(0x020B, 0x08); bus.write(0x020B, 0x08);
// Manuall put 0x01FC into the Zero page at 0x000A // Manuall put 0x01FC into the Zero page at 0x000A
// This will be added to the Y register (which will store 0x0F) // This will be added to the Y register (which will store 0x0F)
bus.write(0x000A, 0xFC); // Pointer lo byte bus.write(0x000A, 0xFC); // Pointer lo byte
bus.write(0x000B, 0x01); // Pointer hi byte bus.write(0x000B, 0x01); // Pointer hi byte
// Program to load 0x08 into the accumulator // Program to load 0x08 into the accumulator
bus.write(addr, 0xB1); // LDA - Indirect, Y mode bus.write(addr, 0xB1); // LDA - Indirect, Y mode
bus.write(addr + 1, 0x0A); // Argument - Pointer into the Zero Page bus.write(addr + 1, 0x0A); // Argument - Pointer into the Zero Page
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::Y, 0x0F); // Offset of the value at the zero page address 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) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x08 in the A register? // Is 0x08 in the A register?
assert_eq!(0x08, cpu.debug_get_reg(Registers::A)); assert_eq!(0x08, cpu.debug_get_reg(Registers::A));
} }

@ -1,109 +1,109 @@
#![allow(dead_code, non_snake_case)] #![allow(dead_code, non_snake_case)]
use crate::tests::test_bus::RAMBus; use crate::tests::test_bus::RAMBus;
use crate::r6502::{R6502, Bus, Registers, Flags}; use crate::r6502::{R6502, Bus, Registers, Flags};
#[test] #[test]
fn basic() fn basic()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Write the program to memory // Write the program to memory
// ADC 6 // ADC 6
bus.write(addr, 0x69); // ADC - Immediate mode bus.write(addr, 0x69); // ADC - Immediate mode
bus.write(addr + 1, 0x06); // Argument bus.write(addr + 1, 0x06); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::A, 0x04); cpu.debug_set_reg(Registers::A, 0x04);
// Clock the cpu twice (Clock essentially runs one full instruction) // Clock the cpu twice (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x0A in the A register? // Is 0x0A in the A register?
assert_eq!(0x0A, cpu.debug_get_reg(Registers::A), "Wrong answer"); 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::V), "Overflow bit should not be set");
assert_eq!(0, cpu.check_flag(Flags::C), "Carry bit should not be set"); assert_eq!(0, cpu.check_flag(Flags::C), "Carry bit should not be set");
} }
#[test] #[test]
fn with_carry() fn with_carry()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Add with carry in // Add with carry in
// Write the program to memory // Write the program to memory
// ADC 6 // ADC 6
bus.write(addr, 0x69); // ADC - Immediate mode bus.write(addr, 0x69); // ADC - Immediate mode
bus.write(addr + 1, 0x06); // Argument bus.write(addr + 1, 0x06); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu internals // manually setup the cpu internals
cpu.debug_set_reg(Registers::A, 0x04); cpu.debug_set_reg(Registers::A, 0x04);
cpu.set_flag(Flags::C); cpu.set_flag(Flags::C);
// Clock the cpu twice (Clock essentially runs one full instruction) // Clock the cpu twice (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x0B in the A register? // Is 0x0B in the A register?
assert_eq!(0x0B, cpu.debug_get_reg(Registers::A), "Wrong answer"); 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"); assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set");
} }
#[test] #[test]
fn with_overflow() fn with_overflow()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Add with overflow // Add with overflow
// Write the program to memory // Write the program to memory
// ADC 126 // ADC 126
bus.write(addr, 0x69); // ADC - Immediate mode bus.write(addr, 0x69); // ADC - Immediate mode
bus.write(addr + 1, 126); // Argument bus.write(addr + 1, 126); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu internals // manually setup the cpu internals
cpu.debug_set_reg(Registers::A, 0x04); cpu.debug_set_reg(Registers::A, 0x04);
// Clock the cpu twice (Clock essentially runs one full instruction) // Clock the cpu twice (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 130 in the A register? // Is 130 in the A register?
assert_eq!(130, cpu.debug_get_reg(Registers::A)); assert_eq!(130, cpu.debug_get_reg(Registers::A));
// Is the overflow bit set? // Is the overflow bit set?
assert_eq!(1, cpu.check_flag(Flags::V), "Failed addition with overflow"); assert_eq!(1, cpu.check_flag(Flags::V), "Failed addition with overflow");
} }

@ -1,110 +1,110 @@
#![allow(dead_code, non_snake_case)] #![allow(dead_code, non_snake_case)]
use crate::tests::test_bus::RAMBus; use crate::tests::test_bus::RAMBus;
use crate::r6502::{R6502, Bus, Registers, Flags}; use crate::r6502::{R6502, Bus, Registers, Flags};
#[test] #[test]
fn less_than() fn less_than()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
/////////////////////// ///////////////////////
// Parameter is less than A reg // Parameter is less than A reg
// Program to compare 0x10 with 0x05 (0x05 will be the argument) // Program to compare 0x10 with 0x05 (0x05 will be the argument)
bus.write(addr, 0xC9); // CMP - Immediate mode bus.write(addr, 0xC9); // CMP - Immediate mode
bus.write(addr + 1, 0x05); // Argument bus.write(addr + 1, 0x05); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::A, 0x10); cpu.debug_set_reg(Registers::A, 0x10);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// C Flag should be 1, Z N Flags should be 0 // C Flag should be 1, Z N Flags should be 0
assert_eq!(1, cpu.check_flag(Flags::C)); assert_eq!(1, cpu.check_flag(Flags::C));
assert_eq!(0, cpu.check_flag(Flags::Z)); assert_eq!(0, cpu.check_flag(Flags::Z));
assert_eq!(0, cpu.check_flag(Flags::N)); assert_eq!(0, cpu.check_flag(Flags::N));
} }
#[test] #[test]
fn equal_to() fn equal_to()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
/////////////////////// ///////////////////////
// Parameter is equal to the A reg // Parameter is equal to the A reg
// Program to compare 0x10 with 0x10 // Program to compare 0x10 with 0x10
bus.write(addr, 0xC9); // CMP - Immediate mode bus.write(addr, 0xC9); // CMP - Immediate mode
bus.write(addr + 1, 0x10); // Argument bus.write(addr + 1, 0x10); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::A, 0x10); cpu.debug_set_reg(Registers::A, 0x10);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// C Z Flags should be 1, N Flag should be 0 // 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::C));
assert_eq!(1, cpu.check_flag(Flags::Z)); assert_eq!(1, cpu.check_flag(Flags::Z));
assert_eq!(0, cpu.check_flag(Flags::N)); assert_eq!(0, cpu.check_flag(Flags::N));
} }
#[test] #[test]
fn greater_than() fn greater_than()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
/////////////////////// ///////////////////////
// Parameter is greater than A reg // Parameter is greater than A reg
// Program to compare 0x05 with 0x10 // Program to compare 0x05 with 0x10
bus.write(addr, 0xC9); // CMP - Immediate mode bus.write(addr, 0xC9); // CMP - Immediate mode
bus.write(addr + 1, 0x10); // Argument bus.write(addr + 1, 0x10); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu registers // manually setup the cpu registers
cpu.debug_set_reg(Registers::A, 0x05); cpu.debug_set_reg(Registers::A, 0x05);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// C Z Flags should be 0, N Flag should be 1 // 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::C));
assert_eq!(0, cpu.check_flag(Flags::Z)); assert_eq!(0, cpu.check_flag(Flags::Z));
assert_eq!(1, cpu.check_flag(Flags::N)); assert_eq!(1, cpu.check_flag(Flags::N));
} }

@ -1,105 +1,105 @@
#![allow(dead_code, non_snake_case)] #![allow(dead_code, non_snake_case)]
use crate::tests::test_bus::RAMBus; use crate::tests::test_bus::RAMBus;
use crate::r6502::{R6502, Bus, Registers, Flags}; use crate::r6502::{R6502, Bus, Registers, Flags};
#[test] #[test]
fn basic() fn basic()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Program to subtract 0x06 from 0x0A // Program to subtract 0x06 from 0x0A
bus.write(addr, 0xE9); // SBC - Immediate mode bus.write(addr, 0xE9); // SBC - Immediate mode
bus.write(addr + 1, 0x06); // Argument bus.write(addr + 1, 0x06); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu state // manually setup the cpu state
cpu.set_flag(Flags::C); cpu.set_flag(Flags::C);
cpu.debug_set_reg(Registers::A, 0x0A); cpu.debug_set_reg(Registers::A, 0x0A);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is 0x04 in the A register? // Is 0x04 in the A register?
assert_eq!(0x04, cpu.debug_get_reg(Registers::A), "wrong answer"); 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!(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"); assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set");
} }
#[test] #[test]
fn with_carry() fn with_carry()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Program to subtract 0x09 from 0x08 // Program to subtract 0x09 from 0x08
bus.write(addr, 0xE9); // SBC - Immediate mode bus.write(addr, 0xE9); // SBC - Immediate mode
bus.write(addr + 1, 0x09); // Argument bus.write(addr + 1, 0x09); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu state // manually setup the cpu state
cpu.set_flag(Flags::C); cpu.set_flag(Flags::C);
cpu.debug_set_reg(Registers::A, 0x08); cpu.debug_set_reg(Registers::A, 0x08);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
// Is -1 in the A register? // Is -1 in the A register?
assert_eq!(0xFF as u16, cpu.debug_get_reg(Registers::A), "Wrong answer"); 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::C), "Carry bit should not be set");
assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set"); assert_eq!(0, cpu.check_flag(Flags::V), "Overflow bit should not be set");
} }
#[test] #[test]
fn with_overflow() fn with_overflow()
{ {
let mut cpu = R6502::new(); let mut cpu = R6502::new();
let mut bus = RAMBus::new(); let mut bus = RAMBus::new();
// program address // program address
let addr = 0x0020 as u16; let addr = 0x0020 as u16;
// Set the program counter address // Set the program counter address
bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte bus.write(0xFFFC, (addr & 0x00FF) as u8); // low byte
bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte bus.write(0xFFFD, ((addr & 0xFF00) >> 8) as u8); // high byte
// Program to subtract 0x7E from 0xFB (-5 - 126) // Program to subtract 0x7E from 0xFB (-5 - 126)
bus.write(addr, 0xE9); // SBC - Immediate mode bus.write(addr, 0xE9); // SBC - Immediate mode
bus.write(addr + 1, 0x7E); // Argument bus.write(addr + 1, 0x7E); // Argument
// Restart cpu // Restart cpu
cpu.reset(&mut bus); cpu.reset(&mut bus);
// manually setup the cpu state // manually setup the cpu state
cpu.set_flag(Flags::C); cpu.set_flag(Flags::C);
cpu.debug_set_reg(Registers::A, 0xFB); cpu.debug_set_reg(Registers::A, 0xFB);
// Clock the cpu to run the program (Clock essentially runs one full instruction) // Clock the cpu to run the program (Clock essentially runs one full instruction)
cpu.clock(&mut bus); cpu.clock(&mut bus);
assert_eq!(0x7D, cpu.debug_get_reg(Registers::A), "Wrong answer"); 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::C), "Carry bit should be set");
assert_eq!(1, cpu.check_flag(Flags::V), "Overflow bit should be set"); assert_eq!(1, cpu.check_flag(Flags::V), "Overflow bit should be set");
} }

@ -457,4 +457,81 @@ fn INC()
// Is 0x11 in memory at 0x08? // Is 0x11 in memory at 0x08?
assert_eq!(0x11, bus.read(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));
} }

@ -1,28 +1,28 @@
use crate::r6502::Bus; use crate::r6502::Bus;
// All-RAM bus for testing // All-RAM bus for testing
pub struct RAMBus pub struct RAMBus
{ {
ram: [u8; 64 * 1024] ram: [u8; 64 * 1024]
} }
impl RAMBus impl RAMBus
{ {
pub fn new() -> RAMBus pub fn new() -> RAMBus
{ {
RAMBus { ram: [0; 64 * 1024] } RAMBus { ram: [0; 64 * 1024] }
} }
} }
impl Bus for RAMBus impl Bus for RAMBus
{ {
fn read(&self, addr: u16) -> u8 fn read(&self, addr: u16) -> u8
{ {
self.ram[addr as usize] self.ram[addr as usize]
} }
fn write(&mut self, addr: u16, value: u8) fn write(&mut self, addr: u16, value: u8)
{ {
self.ram[addr as usize] = value; self.ram[addr as usize] = value;
} }
} }
Loading…
Cancel
Save