crustacean_6502_emulator/src/emulator/emulator.rs

146 lines
4.7 KiB
Rust

use super::error;
use super::opcodes;
use super::OpcodeType;
use super::{addressing_modes::get_size, AddressingMode};
fn invalid_mode<T>(mode_used: AddressingMode) -> T {
panic!("The addressign mode used ({:?}) is either not valid for this opcode, or expects an argument which was not provided",mode_used)
}
macro_rules! fetch {
($self:ident PC+$off:expr) => {
$self.ram[$self.cpu.PC as usize + $off]
};
($self:ident $addr:expr) => {
$self.ram[$addr as usize]
};
($self:ident D $addr:expr) => {
($self.ram[$addr as usize + 1] as u16) << 8 | $self.ram[$addr as usize] as u16
};
}
pub struct Emulator {
pub cycles: usize,
pub ram: [u8; 0x10000],
pub cpu: Cpu,
}
impl Emulator {
pub fn new() -> Self {
Self {
cycles: 0,
ram: [0x00; 0x10000],
cpu: Cpu::default(),
}
}
pub fn step(&mut self) -> Result<(), error::EmulatorError> {
println!("Step on {:04X}", self.cpu.PC);
let code = self.ram[self.cpu.PC as usize];
let code = match opcodes::from_code(code) {
None => return Err(error::EmulatorError::UnknownOp(code)),
Some(v) => v,
};
println!(" Opcode {:?}", code.name);
let arg: Option<u16> = match code.addr_mode {
AddressingMode::IMPL => None,
AddressingMode::A => None,
AddressingMode::IMM => {
let addr = fetch!(self PC+1);
Some(addr as u16)
}
AddressingMode::ABS => {
let addr = self.cpu.PC as usize + 1;
Some(fetch!(self D addr) as u16)
}
AddressingMode::ZPG => {
let addr = self.cpu.PC as usize + 1;
Some(fetch!(self addr) as u16)
}
_ => {
unimplemented!("Unimplemented addressing mode {:?}", code.addr_mode);
}
};
println!(" Argument: {:#04X?}", arg);
match code.name {
OpcodeType::BRK => {
println!("Stepped on break. Ending");
println!("{:#?}", self.cpu);
return Err(error::EmulatorError::Break);
}
OpcodeType::LDA => match code.addr_mode {
AddressingMode::IMM => {
self.cpu.A = arg.unwrap_or_else(|| invalid_mode(code.addr_mode)) as u8
}
AddressingMode::ABS => {
self.cpu.A = fetch!(self arg.unwrap_or_else(||invalid_mode(code.addr_mode)))
}
AddressingMode::ZPG => {
self.cpu.A = fetch!(self arg.unwrap_or_else(||invalid_mode(code.addr_mode)))
}
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
OpcodeType::STA => match code.addr_mode {
AddressingMode::ABS => {
self.ram[arg.unwrap_or_else(|| invalid_mode(code.addr_mode)) as usize] =
self.cpu.A
}
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
OpcodeType::ADC => match code.addr_mode {
AddressingMode::IMM => {
self.cpu.A += arg.unwrap_or_else(|| invalid_mode(code.addr_mode)) as u8
}
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
OpcodeType::JMP => match code.addr_mode {
AddressingMode::ABS => {
self.cpu.PC = arg.unwrap_or_else(|| invalid_mode(code.addr_mode)) as u16
}
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
_ => {
unimplemented!(
"Unimplemented opcode {:?} with {:?}",
code.name,
code.addr_mode
);
}
}
if code.name != OpcodeType::JMP {
self.cpu.PC += get_size(code.addr_mode) as u16;
}
Ok(())
}
pub fn restart(&mut self) {
self.cycles = 0;
self.ram = [0x00; 0x10000];
self.cpu = Cpu::default();
}
}
#[allow(non_snake_case)]
pub struct Cpu {
A: u8,
X: u8,
Y: u8,
PC: u16,
}
impl std::default::Default for Cpu {
fn default() -> Self {
Self {
A: 0x00,
X: 0x00,
Y: 0x00,
PC: 0x0600,
}
}
}
impl std::fmt::Debug for Cpu {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(
f,
"Registers: \n PC: {:04X}\n A: {:02X} X: {:02X} Y: {:02X}\n",
self.PC, self.A, self.X, self.Y,
)
}
}