Adds BIT and JMP instructions

joeyrp 2 years ago
parent 400e378492
commit d65c7cfd88

@ -46,6 +46,13 @@ pub enum ModeID
// 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
{
@ -70,6 +77,17 @@ impl AddressingModes
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
@ -77,6 +95,7 @@ impl AddressingModes
pub fn ERR(cpu: &mut R6502, bus: &mut dyn Bus) -> ModeID
{
// TODO: Better error handling
ModeID::ERR
}
@ -184,8 +203,40 @@ impl AddressingModes
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
}

@ -42,10 +42,30 @@ impl Instructions
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)
@ -419,6 +439,65 @@ impl Instructions
///////////////////////////////////////////////////////////
// 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
}

@ -180,7 +180,8 @@ fn execute(instruction: u8, cpu: &mut R6502, bus: &mut dyn Bus)
match group_code
{
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
@ -237,3 +238,21 @@ fn exe_group_two(instruction: u8, cpu: &mut R6502, bus: &mut dyn 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);
}

@ -458,3 +458,80 @@ 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));
}
Loading…
Cancel
Save