crustacean_6502_emulator/src/emulator/system.rs

205 lines
7.8 KiB
Rust

use super::addressing_modes::{get_size, Address, AddressingMode};
use super::components::{Flags, Ram, Registers};
use super::error;
use super::opcodes;
use super::OpcodeType;
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)
}
static RESET_VEC_ADDR: Address = Address(0xFFFC);
macro_rules! fetch {
($self:ident PC+$off:expr) => {
$self.ram[$self.registers.PC + Address($off)]
};
($self:ident $addr:expr) => {
$self.ram[$addr]
};
($self:ident D $addr:expr) => {
($self.ram[$addr.next()] as u16) << 8 | $self.ram[$addr] as u16
};
}
macro_rules! operation {
($self:ident A+$b:expr) => {
$self.registers.A.wrapping_add($b as u8)
};
($self:ident X+$b:expr) => {
$self.registers.X.wrapping_add($b as u8)
};
(unwrap $arg:ident $addr:ident) => {
$arg.unwrap_or_else(|| invalid_mode($addr.addr_mode))
};
}
pub struct System {
pub cycles: usize,
pub ram: Ram,
pub registers: Registers,
}
impl System {
pub fn new() -> Self {
Self {
cycles: 0,
ram: Ram([0x00; 0x10000]),
registers: Registers::default(),
}
}
pub fn init(&mut self) -> Result<(), error::CpuError> {
let lo: u16 = self.ram[RESET_VEC_ADDR] as u16;
let hi: u16 = self.ram[RESET_VEC_ADDR.same_page_add(1usize)] as u16;
let addr = hi << 8 | lo;
self.registers.PC = addr.into();
Ok(())
}
pub fn step(&mut self) -> Result<(), error::CpuError> {
if self.cycles == 0 {
println!("Initializing");
self.init()?;
}
/* if self.registers.test(Flags::Break) {
return Err(error::EmulatorError::Break);
} */
println!("Step on {:04X}", *self.registers.PC);
let code = self.ram[self.registers.PC];
let code = match opcodes::from_code(code) {
None => return Err(error::CpuError::UnknownOp(code)),
Some(v) => v,
};
println!(" Opcode {:?}", code.name);
let arg: Option<u16> = match code.addr_mode {
AddressingMode::IMPL => None, // No argument
AddressingMode::A => None, // No argument
AddressingMode::IMM => {
// Next byte is the argument
let addr = fetch!(self PC+1);
Some(addr as u16)
}
AddressingMode::ABS => {
// Next 2 bytes are an address from where to fetch the real argument
let addr = self.registers.PC.next();
Some(fetch!(self D addr) as u16)
}
AddressingMode::ZPG => {
// Next byte is an address from the range 0x0000-0x00FF
let addr = self.registers.PC.same_page_add(1u16);
Some(fetch!(self addr) as u16)
}
AddressingMode::INDX => {
// Take the next byte and add it to X,
// then use the result as an address and fetch 2 bytes
let arg = fetch!(self PC+1); // Opcode arg
let addr: Address = operation!(self X+arg).into(); // Zero-page addr
let addr_lo = self.ram[addr] as usize;
let addr_hi = self.ram[addr.next()] as usize;
let res_addr = addr_hi << 8 | addr_lo;
Some(res_addr as u16)
}
AddressingMode::REL => {
// Add PC with the next byte
let arg = fetch!(self PC+1) as u8 as i8 as isize;
let pc = (*self.registers.PC) as isize;
let new_pc = (arg + pc) & 0xFFFF;
Some(new_pc as u16)
}
_ => {
unimplemented!("Unimplemented addressing mode {:?}", code.addr_mode);
}
};
let mut branch_taken = false; // Don't update PC if we take a branch
println!(" Argument: {:#04X?}", arg);
match code.name {
OpcodeType::BRK => {
println!("Stepped on break. Ending");
println!("{:#?}", self.registers);
/* return Err(error::EmulatorError::Break); */
}
OpcodeType::NOP => {}
OpcodeType::LDA => match code.addr_mode {
AddressingMode::IMM => self.registers.set_a(operation!(unwrap arg code) as u8),
AddressingMode::ABS => self
.registers
.set_a(fetch!(self operation!(unwrap arg code).into())),
AddressingMode::ZPG => self
.registers
.set_a(fetch!(self operation!(unwrap arg code).into())),
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
OpcodeType::STA => match code.addr_mode {
AddressingMode::ABS => {
self.ram[operation!(unwrap arg code).into()] = self.registers.A
}
AddressingMode::ZPG => {
self.ram[operation!(unwrap arg code).into()] = self.registers.A
}
AddressingMode::INDX => {
self.ram[operation!(unwrap arg code).into()] = self.registers.A
}
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
OpcodeType::ADC => match code.addr_mode {
AddressingMode::IMM => self.registers.add_a(operation!(unwrap arg code) as u8),
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
OpcodeType::JMP => match code.addr_mode {
AddressingMode::ABS => self.registers.PC = operation!(unwrap arg code).into(),
_ => panic!("Invalid addressing mode for {:?}", code.name),
},
OpcodeType::BEQ if code.addr_mode == AddressingMode::REL => {
if self.registers.test(Flags::Zero) {
self.registers.PC = operation!(unwrap arg code).into();
branch_taken = true;
}
}
OpcodeType::BNE if code.addr_mode == AddressingMode::REL => {
if !self.registers.test(Flags::Zero) {
self.registers.PC = operation!(unwrap arg code).into();
branch_taken = true;
}
}
_ => {
unimplemented!(
"Unimplemented opcode {:?} with {:?}",
code.name,
code.addr_mode
);
}
}
if branch_taken || code.name == OpcodeType::JMP {
println!("Don't increment PC");
} else {
self.registers.PC = self.registers.PC + get_size(code.addr_mode).into();
}
self.cycles += 1;
Ok(())
}
pub fn restart(&mut self) {
self.cycles = 0;
self.ram.load([0x00; 0x10000]);
self.registers = Registers::default();
}
}
mod test {
#[test]
fn test_flags() {
use super::{Flags, Registers};
let mut cpu: Registers = Registers::default();
cpu.set_flag(Flags::Zero, true);
assert_eq!(cpu.test(Flags::Zero), true);
cpu.set_flag(Flags::Zero, false);
assert_eq!(cpu.test(Flags::Zero), false);
cpu.set_flag(Flags::Zero, true);
cpu.set_flag(Flags::Negative, true);
cpu.set_flag(Flags::Int, true);
assert_eq!(cpu.test(Flags::Zero), true);
assert_eq!(cpu.test(Flags::Negative), true);
assert_eq!(cpu.test(Flags::Int), true);
cpu.set_flag(Flags::Negative, false);
assert_eq!(cpu.test(Flags::Zero), true);
assert_eq!(cpu.test(Flags::Negative), false);
assert_eq!(cpu.test(Flags::Int), true);
}
}