This commit is contained in:
baltdev 2024-04-01 23:32:56 -05:00
parent 7f40ffc5ee
commit e6050511f0
5 changed files with 224 additions and 196 deletions

View File

@ -21,8 +21,9 @@ pub trait WriteCallback {
/// A helper for easily creating simple implementations of [`ReadCallback`].
pub struct FunctionReadCallback<F>(pub F);
impl<F> ReadCallback for FunctionReadCallback<F> where
F: FnMut(&mut State, u16) -> u8
impl<F> ReadCallback for FunctionReadCallback<F>
where
F: FnMut(&mut State, u16) -> u8,
{
fn callback(&mut self, state: &mut State, address: u16) -> u8 {
self.0(state, address)
@ -32,8 +33,9 @@ impl<F> ReadCallback for FunctionReadCallback<F> where
/// A helper for easily creating simple implementations of [`WriteCallback`].
pub struct FunctionWriteCallback<F>(pub F);
impl<F> WriteCallback for FunctionWriteCallback<F> where
F: FnMut(&mut State, u16, u8)
impl<F> WriteCallback for FunctionWriteCallback<F>
where
F: FnMut(&mut State, u16, u8),
{
fn callback(&mut self, state: &mut State, address: u16, byte: u8) {
self.0(state, address, byte);
@ -116,7 +118,8 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
#[inline]
#[must_use]
pub fn with_rom_from(mut self, slice: &[u8], location: u16) -> Self {
self.state.memory[location as usize..location as usize + slice.len()].copy_from_slice(slice);
self.state.memory[location as usize..location as usize + slice.len()]
.copy_from_slice(slice);
self
}
@ -181,7 +184,6 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
self.read(0x100 + u16::from(self.state.stack_pointer))
}
/// Sets a callback to be called when memory is read from, allowing for memory-mapped reads.
/// See [`ReadCallback`].
#[inline]
@ -190,7 +192,7 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
Emulator {
state: self.state,
read_callback: callback,
write_callback: self.write_callback
write_callback: self.write_callback,
}
}
@ -202,7 +204,7 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
Emulator {
state: self.state,
read_callback: self.read_callback,
write_callback: callback
write_callback: callback,
}
}
}
@ -212,14 +214,15 @@ impl Default for Emulator<DefaultReadCallback, DefaultWriteCallback> {
Self {
state: State::default(),
read_callback: DefaultReadCallback,
write_callback: DefaultWriteCallback
write_callback: DefaultWriteCallback,
}
}
}
#[allow(clippy::missing_fields_in_debug)]
impl<R: ReadCallback + core::fmt::Debug, W: WriteCallback + core::fmt::Debug> core::fmt::Debug for Emulator<R, W> {
impl<R: ReadCallback + core::fmt::Debug, W: WriteCallback + core::fmt::Debug> core::fmt::Debug
for Emulator<R, W>
{
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
fmt.debug_struct("Emulator")
.field("state", &self.state)
@ -242,7 +245,10 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
Some(match mode {
Absolute(addr) => addr,
ZeroPage(addr) => u16::from(addr),
Relative(offset) => self.state.program_counter.wrapping_add_signed(i16::from(offset)),
Relative(offset) => self
.state
.program_counter
.wrapping_add_signed(i16::from(offset)),
AbsoluteIndirect(addr) => {
let high = self.read(addr);
let low = self.read(addr.wrapping_add(1));
@ -384,13 +390,19 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
let (high_lhs, low_lhs) = to_bcd_nibbles(lhs);
let (high_rhs, low_rhs) = to_bcd_nibbles(rhs);
let low_sum = low_lhs + low_rhs + u8::from(self.state.status.contains(Status::CARRY));
let low_sum =
low_lhs + low_rhs + u8::from(self.state.status.contains(Status::CARRY));
let (low_carry, low) = (low_sum / 10, low_sum % 10);
let high_sum = high_lhs + high_rhs + low_carry;
let (sum, carry) = from_bcd_nibbles(low, high_sum);
self.state.status.set(Status::CARRY, carry);
let overflow_sum = u16::from(from_bcd(lhs)) + u16::from(from_bcd(rhs)) + u16::from(self.state.status.contains(Status::CARRY));
self.state.status.set(Status::OVERFLOW, (lhs & 0x80) != (overflow_sum & 0x80) as u8);
let overflow_sum = u16::from(from_bcd(lhs))
+ u16::from(from_bcd(rhs))
+ u16::from(self.state.status.contains(Status::CARRY));
self.state.status.set(
Status::OVERFLOW,
(lhs & 0x80) != (overflow_sum & 0x80) as u8,
);
self.state.status.set(Status::ZERO, sum == 0);
self.state.status.set(Status::NEGATIVE, (sum as i8) < 0);
self.state.accumulator = sum;
@ -405,7 +417,9 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
let (high_lhs, low_lhs) = to_bcd_nibbles(lhs);
let (high_rhs, low_rhs) = to_bcd_nibbles(rhs);
let mut low = low_lhs as i8 - low_rhs as i8 - i8::from(!self.state.status.contains(Status::CARRY));
let mut low = low_lhs as i8
- low_rhs as i8
- i8::from(!self.state.status.contains(Status::CARRY));
let mut low_borrow = 0;
if low < 0 {
low += 10;
@ -493,18 +507,22 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
let byte = self.byte(mode);
let result = self.state.accumulator & byte;
self.state.status.set(Status::ZERO, result == 0);
self.state.status.set(Status::NEGATIVE, byte & 0b1000_0000 != 0);
self.state.status.set(Status::OVERFLOW, byte & 0b0100_0000 != 0);
self.state
.status
.set(Status::NEGATIVE, byte & 0b1000_0000 != 0);
self.state
.status
.set(Status::OVERFLOW, byte & 0b0100_0000 != 0);
})?,
CMP => opcode.address_mode.map(|mode|
self.compare(self.state.accumulator, mode)
)?,
CPX => opcode.address_mode.map(|mode|
self.compare(self.state.x_register, mode)
)?,
CPY => opcode.address_mode.map(|mode|
self.compare(self.state.y_register, mode)
)?,
CMP => opcode
.address_mode
.map(|mode| self.compare(self.state.accumulator, mode))?,
CPX => opcode
.address_mode
.map(|mode| self.compare(self.state.x_register, mode))?,
CPY => opcode
.address_mode
.map(|mode| self.compare(self.state.y_register, mode))?,
inst @ (BCC | BNE | BPL | BVC | BCS | BEQ | BMI | BVS) => {
opcode.address_mode.and_then(|mode| {
let mask = match inst {
@ -531,7 +549,7 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
PLA => {
let new_acc = self.pop();
self.state.accumulator = self.set_flags(new_acc);
},
}
PHP => self.push((self.state.status | Status::UNUSED | Status::BREAK).bits()),
PLP => self.state.status = Status::from_bits_retain(self.pop()) | Status::UNUSED,
JMP => opcode.address_mode.and_then(|mode| {
@ -540,11 +558,12 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
Some(())
})?,
JSR => opcode.address_mode.and_then(|mode| {
let [high, low] =
self.state.program_counter
.wrapping_add(u16::from(opcode_length))
.wrapping_sub(1)
.to_le_bytes();
let [high, low] = self
.state
.program_counter
.wrapping_add(u16::from(opcode_length))
.wrapping_sub(1)
.to_le_bytes();
self.state.program_counter = self.addr(mode)?;
self.push(low);
self.push(high);
@ -580,7 +599,10 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
}
if increment {
self.state.program_counter = self.state.program_counter.wrapping_add(u16::from(opcode_length));
self.state.program_counter = self
.state
.program_counter
.wrapping_add(u16::from(opcode_length));
}
Some(false)
@ -617,16 +639,17 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
/// Computes binary add with carry
fn adc(&mut self, rhs: u8) -> u8 {
let sum =
u16::from(self.state.accumulator) +
u16::from(rhs) +
u16::from(self.state.status.contains(Status::CARRY));
let sum = u16::from(self.state.accumulator)
+ u16::from(rhs)
+ u16::from(self.state.status.contains(Status::CARRY));
let acc = u16::from(self.state.accumulator);
let rhs = u16::from(rhs);
self.state.status.set(Status::CARRY, sum > 0xFF);
self.state.status.set(Status::OVERFLOW, !(acc ^ rhs) & (acc ^ sum) & 0x80 != 0);
self.state
.status
.set(Status::OVERFLOW, !(acc ^ rhs) & (acc ^ sum) & 0x80 != 0);
let sum = sum as u8;

View File

@ -51,7 +51,7 @@ impl fmt::Display for AddressMode {
AddressMode::ZeroPageX(n) => write!(f, "${n:02X},X"),
AddressMode::ZeroPageY(n) => write!(f, "${n:02X},Y"),
AddressMode::ZeroPageIndirectX(n) => write!(f, "(${n:02X},X)"),
AddressMode::ZeroPageYIndirect(n) => write!(f, "(${n:02X}),Y")
AddressMode::ZeroPageYIndirect(n) => write!(f, "(${n:02X}),Y"),
}
}
}
@ -179,7 +179,7 @@ pub enum Instruction {
/// Forces a hardware interrupt.
BRK,
/// Does nothing.
NOP
NOP,
}
impl Instruction {
@ -222,7 +222,10 @@ impl Opcode {
#[must_use]
#[inline]
pub fn new(instruction: Instruction, address_mode: Option<AddressMode>) -> Self {
Self { instruction, address_mode }
Self {
instruction,
address_mode,
}
}
}
@ -378,158 +381,157 @@ macro_rules! opcodes {
}
}
opcodes! {
Opcode::new(BRK, None) => 0x00,
Opcode::new(ORA, Some(ZeroPageIndirectX)) => 0x01,
Opcode::new(ORA, Some(ZeroPage)) => 0x05,
Opcode::new(ASL, Some(ZeroPage)) => 0x06,
Opcode::new(PHP, None) => 0x08,
Opcode::new(ORA, Some(Immediate)) => 0x09,
Opcode::new(ASL, Some(Accumulator)) => 0x0A,
Opcode::new(ORA, Some(Absolute)) => 0x0D,
Opcode::new(ASL, Some(Absolute)) => 0x0E,
Opcode::new(ORA, Some(ZeroPageIndirectX)) => 0x01,
Opcode::new(ORA, Some(ZeroPage)) => 0x05,
Opcode::new(ASL, Some(ZeroPage)) => 0x06,
Opcode::new(PHP, None) => 0x08,
Opcode::new(ORA, Some(Immediate)) => 0x09,
Opcode::new(ASL, Some(Accumulator)) => 0x0A,
Opcode::new(ORA, Some(Absolute)) => 0x0D,
Opcode::new(ASL, Some(Absolute)) => 0x0E,
Opcode::new(BPL, Some(Relative)) => 0x10,
Opcode::new(ORA, Some(ZeroPageYIndirect)) => 0x11,
Opcode::new(ORA, Some(ZeroPageX)) => 0x15,
Opcode::new(ASL, Some(ZeroPageX)) => 0x16,
Opcode::new(CLC, None) => 0x18,
Opcode::new(ORA, Some(AbsoluteY)) => 0x19,
Opcode::new(ORA, Some(AbsoluteX)) => 0x1D,
Opcode::new(ASL, Some(AbsoluteX)) => 0x1E,
Opcode::new(ORA, Some(ZeroPageYIndirect)) => 0x11,
Opcode::new(ORA, Some(ZeroPageX)) => 0x15,
Opcode::new(ASL, Some(ZeroPageX)) => 0x16,
Opcode::new(CLC, None) => 0x18,
Opcode::new(ORA, Some(AbsoluteY)) => 0x19,
Opcode::new(ORA, Some(AbsoluteX)) => 0x1D,
Opcode::new(ASL, Some(AbsoluteX)) => 0x1E,
Opcode::new(JSR, Some(Absolute)) => 0x20,
Opcode::new(AND, Some(ZeroPageIndirectX)) => 0x21,
Opcode::new(BIT, Some(ZeroPage)) => 0x24,
Opcode::new(AND, Some(ZeroPage)) => 0x25,
Opcode::new(ROL, Some(ZeroPage)) => 0x26,
Opcode::new(PLP, None) => 0x28,
Opcode::new(AND, Some(Immediate)) => 0x29,
Opcode::new(ROL, Some(Accumulator)) => 0x2A,
Opcode::new(BIT, Some(Absolute)) => 0x2C,
Opcode::new(AND, Some(Absolute)) => 0x2D,
Opcode::new(ROL, Some(Absolute)) => 0x2E,
Opcode::new(AND, Some(ZeroPageIndirectX)) => 0x21,
Opcode::new(BIT, Some(ZeroPage)) => 0x24,
Opcode::new(AND, Some(ZeroPage)) => 0x25,
Opcode::new(ROL, Some(ZeroPage)) => 0x26,
Opcode::new(PLP, None) => 0x28,
Opcode::new(AND, Some(Immediate)) => 0x29,
Opcode::new(ROL, Some(Accumulator)) => 0x2A,
Opcode::new(BIT, Some(Absolute)) => 0x2C,
Opcode::new(AND, Some(Absolute)) => 0x2D,
Opcode::new(ROL, Some(Absolute)) => 0x2E,
Opcode::new(BMI, Some(Relative)) => 0x30,
Opcode::new(AND, Some(ZeroPageYIndirect)) => 0x31,
Opcode::new(AND, Some(ZeroPageX)) => 0x35,
Opcode::new(ROL, Some(ZeroPageX)) => 0x36,
Opcode::new(SEC, None) => 0x38,
Opcode::new(AND, Some(AbsoluteY)) => 0x39,
Opcode::new(AND, Some(AbsoluteX)) => 0x3D,
Opcode::new(ROL, Some(AbsoluteX)) => 0x3E,
Opcode::new(AND, Some(ZeroPageYIndirect)) => 0x31,
Opcode::new(AND, Some(ZeroPageX)) => 0x35,
Opcode::new(ROL, Some(ZeroPageX)) => 0x36,
Opcode::new(SEC, None) => 0x38,
Opcode::new(AND, Some(AbsoluteY)) => 0x39,
Opcode::new(AND, Some(AbsoluteX)) => 0x3D,
Opcode::new(ROL, Some(AbsoluteX)) => 0x3E,
Opcode::new(RTI, None) => 0x40,
Opcode::new(EOR, Some(ZeroPageIndirectX)) => 0x41,
Opcode::new(EOR, Some(ZeroPage)) => 0x45,
Opcode::new(LSR, Some(ZeroPage)) => 0x46,
Opcode::new(PHA, None) => 0x48,
Opcode::new(EOR, Some(Immediate)) => 0x49,
Opcode::new(LSR, Some(Accumulator)) => 0x4A,
Opcode::new(JMP, Some(Absolute)) => 0x4C,
Opcode::new(EOR, Some(Absolute)) => 0x4D,
Opcode::new(LSR, Some(Absolute)) => 0x4E,
Opcode::new(EOR, Some(ZeroPageIndirectX)) => 0x41,
Opcode::new(EOR, Some(ZeroPage)) => 0x45,
Opcode::new(LSR, Some(ZeroPage)) => 0x46,
Opcode::new(PHA, None) => 0x48,
Opcode::new(EOR, Some(Immediate)) => 0x49,
Opcode::new(LSR, Some(Accumulator)) => 0x4A,
Opcode::new(JMP, Some(Absolute)) => 0x4C,
Opcode::new(EOR, Some(Absolute)) => 0x4D,
Opcode::new(LSR, Some(Absolute)) => 0x4E,
Opcode::new(BVC, Some(Relative)) => 0x50,
Opcode::new(EOR, Some(ZeroPageYIndirect)) => 0x51,
Opcode::new(EOR, Some(ZeroPageX)) => 0x55,
Opcode::new(LSR, Some(ZeroPageX)) => 0x56,
Opcode::new(CLI, None) => 0x58,
Opcode::new(EOR, Some(AbsoluteY)) => 0x59,
Opcode::new(EOR, Some(AbsoluteX)) => 0x5D,
Opcode::new(LSR, Some(AbsoluteX)) => 0x5E,
Opcode::new(EOR, Some(ZeroPageYIndirect)) => 0x51,
Opcode::new(EOR, Some(ZeroPageX)) => 0x55,
Opcode::new(LSR, Some(ZeroPageX)) => 0x56,
Opcode::new(CLI, None) => 0x58,
Opcode::new(EOR, Some(AbsoluteY)) => 0x59,
Opcode::new(EOR, Some(AbsoluteX)) => 0x5D,
Opcode::new(LSR, Some(AbsoluteX)) => 0x5E,
Opcode::new(RTS, None) => 0x60,
Opcode::new(ADC, Some(ZeroPageIndirectX)) => 0x61,
Opcode::new(ADC, Some(ZeroPage)) => 0x65,
Opcode::new(ROR, Some(ZeroPage)) => 0x66,
Opcode::new(PLA, None) => 0x68,
Opcode::new(ADC, Some(Immediate)) => 0x69,
Opcode::new(ROR, Some(Accumulator)) => 0x6A,
Opcode::new(JMP, Some(AbsoluteIndirect)) => 0x6C,
Opcode::new(ADC, Some(Absolute)) => 0x6D,
Opcode::new(ROR, Some(Absolute)) => 0x6E,
Opcode::new(ADC, Some(ZeroPageIndirectX)) => 0x61,
Opcode::new(ADC, Some(ZeroPage)) => 0x65,
Opcode::new(ROR, Some(ZeroPage)) => 0x66,
Opcode::new(PLA, None) => 0x68,
Opcode::new(ADC, Some(Immediate)) => 0x69,
Opcode::new(ROR, Some(Accumulator)) => 0x6A,
Opcode::new(JMP, Some(AbsoluteIndirect)) => 0x6C,
Opcode::new(ADC, Some(Absolute)) => 0x6D,
Opcode::new(ROR, Some(Absolute)) => 0x6E,
Opcode::new(BVS, Some(Relative)) => 0x70,
Opcode::new(ADC, Some(ZeroPageYIndirect)) => 0x71,
Opcode::new(ADC, Some(ZeroPageX)) => 0x75,
Opcode::new(ROR, Some(ZeroPageX)) => 0x76,
Opcode::new(SEI, None) => 0x78,
Opcode::new(ADC, Some(AbsoluteY)) => 0x79,
Opcode::new(ADC, Some(AbsoluteX)) => 0x7D,
Opcode::new(ROR, Some(AbsoluteX)) => 0x7E,
Opcode::new(STA, Some(ZeroPageIndirectX)) => 0x81,
Opcode::new(STY, Some(ZeroPage)) => 0x84,
Opcode::new(STA, Some(ZeroPage)) => 0x85,
Opcode::new(STX, Some(ZeroPage)) => 0x86,
Opcode::new(DEY, None) => 0x88,
Opcode::new(BIT, Some(Immediate)) => 0x89,
Opcode::new(TXA, None) => 0x8A,
Opcode::new(STY, Some(Absolute)) => 0x8C,
Opcode::new(STA, Some(Absolute)) => 0x8D,
Opcode::new(STX, Some(Absolute)) => 0x8E,
Opcode::new(ADC, Some(ZeroPageYIndirect)) => 0x71,
Opcode::new(ADC, Some(ZeroPageX)) => 0x75,
Opcode::new(ROR, Some(ZeroPageX)) => 0x76,
Opcode::new(SEI, None) => 0x78,
Opcode::new(ADC, Some(AbsoluteY)) => 0x79,
Opcode::new(ADC, Some(AbsoluteX)) => 0x7D,
Opcode::new(ROR, Some(AbsoluteX)) => 0x7E,
Opcode::new(STA, Some(ZeroPageIndirectX)) => 0x81,
Opcode::new(STY, Some(ZeroPage)) => 0x84,
Opcode::new(STA, Some(ZeroPage)) => 0x85,
Opcode::new(STX, Some(ZeroPage)) => 0x86,
Opcode::new(DEY, None) => 0x88,
Opcode::new(BIT, Some(Immediate)) => 0x89,
Opcode::new(TXA, None) => 0x8A,
Opcode::new(STY, Some(Absolute)) => 0x8C,
Opcode::new(STA, Some(Absolute)) => 0x8D,
Opcode::new(STX, Some(Absolute)) => 0x8E,
Opcode::new(BCC, Some(Relative)) => 0x90,
Opcode::new(STA, Some(ZeroPageYIndirect)) => 0x91,
Opcode::new(STY, Some(ZeroPageX)) => 0x94,
Opcode::new(STA, Some(ZeroPageX)) => 0x95,
Opcode::new(STX, Some(ZeroPageY)) => 0x96,
Opcode::new(TYA, None) => 0x98,
Opcode::new(STA, Some(AbsoluteY)) => 0x99,
Opcode::new(TXS, None) => 0x9A,
Opcode::new(STA, Some(AbsoluteX)) => 0x9D,
Opcode::new(LDY, Some(Immediate)) => 0xA0,
Opcode::new(LDA, Some(ZeroPageIndirectX)) => 0xA1,
Opcode::new(LDX, Some(Immediate)) => 0xA2,
Opcode::new(LDY, Some(ZeroPage)) => 0xA4,
Opcode::new(LDA, Some(ZeroPage)) => 0xA5,
Opcode::new(LDX, Some(ZeroPage)) => 0xA6,
Opcode::new(TAY, None) => 0xA8,
Opcode::new(LDA, Some(Immediate)) => 0xA9,
Opcode::new(TAX, None) => 0xAA,
Opcode::new(LDY, Some(Absolute)) => 0xAC,
Opcode::new(LDA, Some(Absolute)) => 0xAD,
Opcode::new(LDX, Some(Absolute)) => 0xAE,
Opcode::new(STA, Some(ZeroPageYIndirect)) => 0x91,
Opcode::new(STY, Some(ZeroPageX)) => 0x94,
Opcode::new(STA, Some(ZeroPageX)) => 0x95,
Opcode::new(STX, Some(ZeroPageY)) => 0x96,
Opcode::new(TYA, None) => 0x98,
Opcode::new(STA, Some(AbsoluteY)) => 0x99,
Opcode::new(TXS, None) => 0x9A,
Opcode::new(STA, Some(AbsoluteX)) => 0x9D,
Opcode::new(LDY, Some(Immediate)) => 0xA0,
Opcode::new(LDA, Some(ZeroPageIndirectX)) => 0xA1,
Opcode::new(LDX, Some(Immediate)) => 0xA2,
Opcode::new(LDY, Some(ZeroPage)) => 0xA4,
Opcode::new(LDA, Some(ZeroPage)) => 0xA5,
Opcode::new(LDX, Some(ZeroPage)) => 0xA6,
Opcode::new(TAY, None) => 0xA8,
Opcode::new(LDA, Some(Immediate)) => 0xA9,
Opcode::new(TAX, None) => 0xAA,
Opcode::new(LDY, Some(Absolute)) => 0xAC,
Opcode::new(LDA, Some(Absolute)) => 0xAD,
Opcode::new(LDX, Some(Absolute)) => 0xAE,
Opcode::new(BCS, Some(Relative)) => 0xB0,
Opcode::new(LDA, Some(ZeroPageYIndirect)) => 0xB1,
Opcode::new(LDY, Some(ZeroPageX)) => 0xB4,
Opcode::new(LDA, Some(ZeroPageX)) => 0xB5,
Opcode::new(LDX, Some(ZeroPageY)) => 0xB6,
Opcode::new(CLV, None) => 0xB8,
Opcode::new(LDA, Some(AbsoluteY)) => 0xB9,
Opcode::new(TSX, None) => 0xBA,
Opcode::new(LDY, Some(AbsoluteX)) => 0xBC,
Opcode::new(LDA, Some(AbsoluteX)) => 0xBD,
Opcode::new(LDX, Some(AbsoluteY)) => 0xBE,
Opcode::new(LDA, Some(ZeroPageYIndirect)) => 0xB1,
Opcode::new(LDY, Some(ZeroPageX)) => 0xB4,
Opcode::new(LDA, Some(ZeroPageX)) => 0xB5,
Opcode::new(LDX, Some(ZeroPageY)) => 0xB6,
Opcode::new(CLV, None) => 0xB8,
Opcode::new(LDA, Some(AbsoluteY)) => 0xB9,
Opcode::new(TSX, None) => 0xBA,
Opcode::new(LDY, Some(AbsoluteX)) => 0xBC,
Opcode::new(LDA, Some(AbsoluteX)) => 0xBD,
Opcode::new(LDX, Some(AbsoluteY)) => 0xBE,
Opcode::new(CPY, Some(Immediate)) => 0xC0,
Opcode::new(CMP, Some(ZeroPageIndirectX)) => 0xC1,
Opcode::new(CPY, Some(ZeroPage)) => 0xC4,
Opcode::new(CMP, Some(ZeroPage)) => 0xC5,
Opcode::new(DEC, Some(ZeroPage)) => 0xC6,
Opcode::new(INY, None) => 0xC8,
Opcode::new(CMP, Some(Immediate)) => 0xC9,
Opcode::new(DEX, None) => 0xCA,
Opcode::new(CPY, Some(Absolute)) => 0xCC,
Opcode::new(CMP, Some(Absolute)) => 0xCD,
Opcode::new(DEC, Some(Absolute)) => 0xCE,
Opcode::new(CMP, Some(ZeroPageIndirectX)) => 0xC1,
Opcode::new(CPY, Some(ZeroPage)) => 0xC4,
Opcode::new(CMP, Some(ZeroPage)) => 0xC5,
Opcode::new(DEC, Some(ZeroPage)) => 0xC6,
Opcode::new(INY, None) => 0xC8,
Opcode::new(CMP, Some(Immediate)) => 0xC9,
Opcode::new(DEX, None) => 0xCA,
Opcode::new(CPY, Some(Absolute)) => 0xCC,
Opcode::new(CMP, Some(Absolute)) => 0xCD,
Opcode::new(DEC, Some(Absolute)) => 0xCE,
Opcode::new(BNE, Some(Relative)) => 0xD0,
Opcode::new(CMP, Some(ZeroPageYIndirect)) => 0xD1,
Opcode::new(CMP, Some(ZeroPageX)) => 0xD5,
Opcode::new(DEC, Some(ZeroPageX)) => 0xD6,
Opcode::new(CLD, None) => 0xD8,
Opcode::new(CMP, Some(AbsoluteY)) => 0xD9,
Opcode::new(CMP, Some(AbsoluteX)) => 0xDD,
Opcode::new(DEC, Some(AbsoluteX)) => 0xDE,
Opcode::new(CMP, Some(ZeroPageYIndirect)) => 0xD1,
Opcode::new(CMP, Some(ZeroPageX)) => 0xD5,
Opcode::new(DEC, Some(ZeroPageX)) => 0xD6,
Opcode::new(CLD, None) => 0xD8,
Opcode::new(CMP, Some(AbsoluteY)) => 0xD9,
Opcode::new(CMP, Some(AbsoluteX)) => 0xDD,
Opcode::new(DEC, Some(AbsoluteX)) => 0xDE,
Opcode::new(CPX, Some(Immediate)) => 0xE0,
Opcode::new(SBC, Some(ZeroPageIndirectX)) => 0xE1,
Opcode::new(CPX, Some(ZeroPage)) => 0xE4,
Opcode::new(SBC, Some(ZeroPage)) => 0xE5,
Opcode::new(INC, Some(ZeroPage)) => 0xE6,
Opcode::new(INX, None) => 0xE8,
Opcode::new(SBC, Some(Immediate)) => 0xE9,
Opcode::new(NOP, None) => 0xEA,
Opcode::new(CPX, Some(Absolute)) => 0xEC,
Opcode::new(SBC, Some(Absolute)) => 0xED,
Opcode::new(INC, Some(Absolute)) => 0xEE,
Opcode::new(SBC, Some(ZeroPageIndirectX)) => 0xE1,
Opcode::new(CPX, Some(ZeroPage)) => 0xE4,
Opcode::new(SBC, Some(ZeroPage)) => 0xE5,
Opcode::new(INC, Some(ZeroPage)) => 0xE6,
Opcode::new(INX, None) => 0xE8,
Opcode::new(SBC, Some(Immediate)) => 0xE9,
Opcode::new(NOP, None) => 0xEA,
Opcode::new(CPX, Some(Absolute)) => 0xEC,
Opcode::new(SBC, Some(Absolute)) => 0xED,
Opcode::new(INC, Some(Absolute)) => 0xEE,
Opcode::new(BEQ, Some(Relative)) => 0xF0,
Opcode::new(SBC, Some(ZeroPageYIndirect)) => 0xF1,
Opcode::new(SBC, Some(ZeroPageX)) => 0xF5,
Opcode::new(INC, Some(ZeroPageX)) => 0xF6,
Opcode::new(SED, None) => 0xF8,
Opcode::new(SBC, Some(AbsoluteY)) => 0xF9,
Opcode::new(SBC, Some(AbsoluteX)) => 0xFD,
Opcode::new(INC, Some(AbsoluteX)) => 0xFE,
Opcode::new(SBC, Some(ZeroPageYIndirect)) => 0xF1,
Opcode::new(SBC, Some(ZeroPageX)) => 0xF5,
Opcode::new(INC, Some(ZeroPageX)) => 0xF6,
Opcode::new(SED, None) => 0xF8,
Opcode::new(SBC, Some(AbsoluteY)) => 0xF9,
Opcode::new(SBC, Some(AbsoluteX)) => 0xFD,
Opcode::new(INC, Some(AbsoluteX)) => 0xFE,
}

View File

@ -4,10 +4,13 @@
#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
pub mod state;
pub mod instructions;
pub mod emulation;
pub mod instructions;
pub mod state;
pub use instructions::{Opcode, Instruction, AddressMode};
pub use emulation::{
DefaultReadCallback, DefaultWriteCallback, Emulator, FunctionReadCallback,
FunctionWriteCallback, ReadCallback, WriteCallback,
};
pub use instructions::{AddressMode, Instruction, Opcode};
pub use state::{State, Status};
pub use emulation::{Emulator, ReadCallback, WriteCallback, DefaultReadCallback, DefaultWriteCallback, FunctionReadCallback, FunctionWriteCallback};

View File

@ -4,8 +4,8 @@ extern crate std;
#[cfg(feature = "serde")]
use {
serde::{Serialize, Deserialize},
serde_with::{As, Bytes}
serde::{Deserialize, Serialize},
serde_with::{As, Bytes},
};
#[derive(Copy, Clone, PartialEq, Eq, core::hash::Hash)]
@ -39,7 +39,7 @@ pub struct State {
/// but grows the struct's size considerably.
/// Consider storing this struct on the heap if this is undesirable and you have one.
#[cfg_attr(feature = "serde", serde(with = "As::<Bytes>"))]
pub memory: [u8; 256 * 256]
pub memory: [u8; 256 * 256],
}
#[allow(clippy::missing_fields_in_debug)]
@ -66,7 +66,7 @@ impl Default for State {
stack_pointer: 0xFF,
status: Status::default(),
memory: [0; 256 * 256],
_padding: 0
_padding: 0,
}
}
}

View File

@ -18,7 +18,7 @@ fn all_suite_a() -> Result<(), usize> {
if trap_number != 0xFF {
eprintln!("!!! ENTERED TRAP !!!");
eprintln!("{:02x?}", emulator.state);
eprintln!("Failed test: {trap_number}", );
eprintln!("Failed test: {trap_number}",);
return Err(0);
}
break;
@ -57,18 +57,18 @@ fn functional_test() -> Result<(), usize> {
}
eprintln!("----------------------------[STACK]-----------------------------");
for i in 0..16 {
eprintln!("{:02x?}", &emulator.state.memory[0x100 + (i * 16)..0x100 + ((i + 1) * 16)])
eprintln!(
"{:02x?}",
&emulator.state.memory[0x100 + (i * 16)..0x100 + ((i + 1) * 16)]
)
}
return Err(0);
}
if needs_interrupt {
let vector = u16::from_le_bytes([
emulator.read(0xFFFE),
emulator.read(0xFFFF)
]);
let vector = u16::from_le_bytes([emulator.read(0xFFFE), emulator.read(0xFFFF)]);
eprintln!("[MASKABLE INTERRUPT, GOING TO IRQ VECTOR @ {vector:02X}]");
// Go to interrupt vector
emulator.state.program_counter = vector;
}
}
}
}