some instructions have code and tests

This commit is contained in:
Trevor Merritt 2025-06-30 13:55:35 -04:00
parent 9e0e8b5910
commit d97774e97b
4 changed files with 188 additions and 13 deletions

View File

@ -9,7 +9,7 @@ fn main() {
let mut array: Box<[u8; SIZE_32KB]> = slice.try_into().expect("Unable to make rom in ram"); let mut array: Box<[u8; SIZE_32KB]> = slice.try_into().expect("Unable to make rom in ram");
array[0] = 0xa9; // LDA #$ab array[0] = 0xa9; // LDA #$ab
array[1] = 0xab; array[1] = 0xab; // 1010 1011
// write the rom to disk // write the rom to disk
fs::write("outputfile.bin", array.as_slice()); fs::write("outputfile.bin", array.as_slice());

View File

@ -166,7 +166,7 @@ impl Mos6502Cpu {
let offset = self.pc as usize; let offset = self.pc as usize;
// TODO: this calls opinfo 2x // TODO: this calls opinfo 2x
self.oi = Instruction::opinfo(&self.memory[offset..offset + 4]).unwrap(); self.oi = Instruction::opinfo(&self.memory[offset..offset + 4]).unwrap();
self.ir = Instruction::decode(&self.memory[offset..offset + 2]).unwrap(); self.ir = Instruction::decode(&self.memory[offset..offset + 4]).unwrap();
self.microcode_step = self.oi.cycles; self.microcode_step = self.oi.cycles;
println!("Decoded [[{:?}]]", self.ir); println!("Decoded [[{:?}]]", self.ir);
self.advance_pc(self.oi.length as u16); self.advance_pc(self.oi.length as u16);
@ -207,10 +207,10 @@ impl Mos6502Cpu {
Operation::CPY => {} Operation::CPY => {}
Operation::DEC => {} Operation::DEC => {}
Operation::DEX => { Operation::DEX => {
self.x -= 1; (self.x, _) = self.x.overflowing_sub(1) ;
} }
Operation::DEY => { Operation::DEY => {
self.y -= 1; (self.y, _) = self.y.overflowing_sub(1);
} }
Operation::EOR => { } Operation::EOR => { }
Operation::INC => { } Operation::INC => { }
@ -234,13 +234,21 @@ impl Mos6502Cpu {
AddressMode::Immediate => { AddressMode::Immediate => {
match self.ir.operand { match self.ir.operand {
Operand::Byte(value) => { Operand::Byte(value) => {
println!("Loading {} into A", value); println!("Loading 0x{value:02x} ({value}) into A");
self.a = value; self.a = value;
} }
_ => {} _ => {}
} }
} }
AddressMode::ZeroPage => {} AddressMode::ZeroPage => {
match self.ir.operand {
Operand::Byte(value) => {
println!("Loading from zero page at 0x{value:02x} ({value})");
self.a = self.memory[value as usize];
}
_ => {}
}
}
AddressMode::ZeroPageX => {} AddressMode::ZeroPageX => {}
AddressMode::ZeroPageY => {} AddressMode::ZeroPageY => {}
AddressMode::Absolute => {} AddressMode::Absolute => {}
@ -265,7 +273,9 @@ impl Mos6502Cpu {
Operation::PHP => {} Operation::PHP => {}
Operation::PLA => {} Operation::PLA => {}
Operation::PLP => {} Operation::PLP => {}
Operation::ROL => {} Operation::ROL => {
self.a = self.a.rotate_left(1);
}
Operation::ROR => { Operation::ROR => {
// rotate A // rotate A
self.a = self.a.rotate_right(1); self.a = self.a.rotate_right(1);
@ -280,9 +290,61 @@ impl Mos6502Cpu {
self.flags.set_flag(Decimal); self.flags.set_flag(Decimal);
} }
Operation::SEI => { Operation::SEI => {
self.flags.set_flag(Mos6502Flag::Interrupt); self.flags.set_flag(Interrupt);
}
Operation::STA => {
match self.oi.mode {
AddressMode::ZeroPage => {
// write to the zero page.
match self.ir.operand {
Operand::Byte(target) => {
self.memory[target as usize] = self.a;
}
_ => {
// Invalid parameter
}
}
}
AddressMode::ZeroPageX => {
match self.ir.operand {
Operand::Byte(target) => {
let x = self.x;
self.memory[(x + target) as usize] = self.a;
}
_ => {
// Invalid Parameter
}
}
}
AddressMode::Absolute => {
// write from A to the specified memory location
match self.ir.operand {
Operand::Word(offset) => {
self.memory[offset as usize] = self.a;
}
_ => {
// Invalid Parameter
}
}
}
AddressMode::AbsoluteX => {
match self.ir.operand {
Operand::Word(offset) => {
self.memory[(offset + self.x as u16) as usize] = self.a;
}
_ => {
// Invalid Parameter
}
}
}
AddressMode::AbsoluteY => {}
AddressMode::IndirectX => {}
AddressMode::IndirectY => {}
_ => {
// invalid memory mode
}
}
} }
Operation::STA => {}
Operation::STX => {} Operation::STX => {}
Operation::STY => {} Operation::STY => {}
Operation::TAX => { Operation::TAX => {
@ -291,7 +353,8 @@ impl Mos6502Cpu {
Operation::TAY => { Operation::TAY => {
self.y = self.a; self.y = self.a;
} }
Operation::TSX => {} Operation::TSX => {
}
Operation::TXA => { Operation::TXA => {
self.a = self.x; self.a = self.x;
} }
@ -308,8 +371,8 @@ impl Mos6502Cpu {
} }
pub fn dump(&self) { pub fn dump(&self) {
println!("CPU State: PC: {:04x} / A: {:02x} / X: {:02x} / Y: {:02x} / ADDRESS: {:04x} / DATA: {:02x} / MICROSTEPS: {:02x}", println!("CPU State: PC: {:04x} / A: {:02x} / X: {:02x} / Y: {:02x} / ADDRESS: {:04x} / DATA: {:02x} / MICROSTEPS: {:02x} / S: {}",
self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step); self.pc, self.a, self.x, self.y, self.address_bus, self.data_bus, self.microcode_step, self.flags.dump());
} }
pub fn dump_data(&self) -> ( u16, u8, u8, u8, u16, u8, u8) { pub fn dump_data(&self) -> ( u16, u8, u8, u8, u16, u8, u8) {
@ -320,3 +383,101 @@ impl Mos6502Cpu {
} }
} }
#[cfg(test)]
mod test {
use crate::constants::constants_isa_op::*;
use crate::instruction_table::INSTRUCTION_TABLE;
use super::*;
#[test]
fn clc() {
// setup the CPU for our test
let mut cpu = Mos6502Cpu::default();
cpu.flags.set_flag(Carry);
// Load our 'test program'
cpu.memory[0x6000] = ISA_OP_CLC;
// Start the PC at our program
cpu.pc = 0x6000;
// Tick the CPU through the instruction
for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLC as usize].unwrap().cycles { cpu.tick(); }
assert!(!cpu.peek_flag(Carry));
}
#[test]
fn cld() {
let mut cpu = Mos6502Cpu::default();
cpu.flags.set_flag(Decimal);
cpu.memory[0x6000] = ISA_OP_CLD;
cpu.pc = 0x6000;
for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLD as usize].unwrap().cycles { cpu.tick(); }
assert!(!cpu.peek_flag(Decimal));
}
#[test]
fn cli() {
let mut cpu = Mos6502Cpu::default();
cpu.flags.set_flag(Interrupt);
cpu.memory[0x6000] = ISA_OP_CLI;
cpu.pc = 0x6000;
for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLI as usize].unwrap().cycles { cpu.tick(); }
assert!(!cpu.peek_flag(Interrupt));
}
#[test]
fn clv() {
let mut cpu = Mos6502Cpu::default();
cpu.flags.set_flag(Overflow);
cpu.memory[0x6000] = ISA_OP_CLV;
cpu.pc = 0x6000;
for _ in 0..INSTRUCTION_TABLE[ISA_OP_CLV as usize].unwrap().cycles { cpu.tick(); }
assert!(!cpu.peek_flag(Overflow));
}
#[test]
fn lda_immediate() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x6000] = ISA_OP_LDA_I;
cpu.memory[0x6001] = 0xab;
cpu.pc = 0x6000;
for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_I as usize].unwrap().cycles { cpu.tick(); }
assert_eq!(cpu.a, 0xab);
}
#[test]
fn lda_zeropage() {
let mut cpu = Mos6502Cpu::default();
cpu.memory[0x6000] = ISA_OP_LDA_Z;
cpu.memory[0x6001] = 0xab;
cpu.memory[0x00ab] = 0xbe;
cpu.pc = 0x6000;
for _ in 0..INSTRUCTION_TABLE[ISA_OP_LDA_Z as usize].unwrap().cycles + 1 { cpu.tick(); }
assert_eq!(cpu.a, 0xbe);
}
#[test]
fn dex() {
let mut cpu = Mos6502Cpu::default();
cpu.a = 0xab;
cpu.memory[0x6000] = ISA_OP_DEX;
cpu.pc = 0x6000;
for _ in 0..INSTRUCTION_TABLE[ISA_OP_DEX as usize].unwrap().cycles { cpu.tick(); }
assert_eq!(0xaa, cpu.a);
}
}

View File

@ -20,6 +20,20 @@ pub struct Mos6502Flags {
negative: bool, negative: bool,
} }
impl Mos6502Flags {
pub fn dump(&self) -> String {
format!("{}{}{}{}{}{}{}",
if self.carry { 'C' } else { 'c' },
if self.zero { 'Z' } else { 'z' },
if self.interrupt { 'I' } else { 'i' },
if self.decimal { 'D' } else { 'd' },
if self.break_flag { 'B' } else { 'b' },
if self.overflow { 'O' } else { 'o' },
if self.negative { 'N' } else { 'n' }
)
}
}
impl Mos6502Flags { impl Mos6502Flags {
pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) { pub fn set_flag(&mut self, flag_to_set: Mos6502Flag) {

View File

@ -38,7 +38,7 @@ impl RomChip for At28C256 {
#[cfg(test)] #[cfg(test)]
mod test { mod test {
use crate::mos6502cpu::SIZE_1KB; use crate::constants::constants_system::SIZE_1KB;
use super::*; use super::*;
#[test] #[test]