mirror of
https://github.com/balt-dev/r6502.git
synced 2025-01-14 04:30:04 +00:00
Format
This commit is contained in:
parent
7f40ffc5ee
commit
e6050511f0
@ -21,8 +21,9 @@ pub trait WriteCallback {
|
|||||||
/// A helper for easily creating simple implementations of [`ReadCallback`].
|
/// A helper for easily creating simple implementations of [`ReadCallback`].
|
||||||
pub struct FunctionReadCallback<F>(pub F);
|
pub struct FunctionReadCallback<F>(pub F);
|
||||||
|
|
||||||
impl<F> ReadCallback for FunctionReadCallback<F> where
|
impl<F> ReadCallback for FunctionReadCallback<F>
|
||||||
F: FnMut(&mut State, u16) -> u8
|
where
|
||||||
|
F: FnMut(&mut State, u16) -> u8,
|
||||||
{
|
{
|
||||||
fn callback(&mut self, state: &mut State, address: u16) -> u8 {
|
fn callback(&mut self, state: &mut State, address: u16) -> u8 {
|
||||||
self.0(state, address)
|
self.0(state, address)
|
||||||
@ -32,8 +33,9 @@ impl<F> ReadCallback for FunctionReadCallback<F> where
|
|||||||
/// A helper for easily creating simple implementations of [`WriteCallback`].
|
/// A helper for easily creating simple implementations of [`WriteCallback`].
|
||||||
pub struct FunctionWriteCallback<F>(pub F);
|
pub struct FunctionWriteCallback<F>(pub F);
|
||||||
|
|
||||||
impl<F> WriteCallback for FunctionWriteCallback<F> where
|
impl<F> WriteCallback for FunctionWriteCallback<F>
|
||||||
F: FnMut(&mut State, u16, u8)
|
where
|
||||||
|
F: FnMut(&mut State, u16, u8),
|
||||||
{
|
{
|
||||||
fn callback(&mut self, state: &mut State, address: u16, byte: u8) {
|
fn callback(&mut self, state: &mut State, address: u16, byte: u8) {
|
||||||
self.0(state, address, byte);
|
self.0(state, address, byte);
|
||||||
@ -116,7 +118,8 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn with_rom_from(mut self, slice: &[u8], location: u16) -> Self {
|
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
|
self
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -181,7 +184,6 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
self.read(0x100 + u16::from(self.state.stack_pointer))
|
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.
|
/// Sets a callback to be called when memory is read from, allowing for memory-mapped reads.
|
||||||
/// See [`ReadCallback`].
|
/// See [`ReadCallback`].
|
||||||
#[inline]
|
#[inline]
|
||||||
@ -190,7 +192,7 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
Emulator {
|
Emulator {
|
||||||
state: self.state,
|
state: self.state,
|
||||||
read_callback: callback,
|
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 {
|
Emulator {
|
||||||
state: self.state,
|
state: self.state,
|
||||||
read_callback: self.read_callback,
|
read_callback: self.read_callback,
|
||||||
write_callback: callback
|
write_callback: callback,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -212,14 +214,15 @@ impl Default for Emulator<DefaultReadCallback, DefaultWriteCallback> {
|
|||||||
Self {
|
Self {
|
||||||
state: State::default(),
|
state: State::default(),
|
||||||
read_callback: DefaultReadCallback,
|
read_callback: DefaultReadCallback,
|
||||||
write_callback: DefaultWriteCallback
|
write_callback: DefaultWriteCallback,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[allow(clippy::missing_fields_in_debug)]
|
#[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 {
|
fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
|
||||||
fmt.debug_struct("Emulator")
|
fmt.debug_struct("Emulator")
|
||||||
.field("state", &self.state)
|
.field("state", &self.state)
|
||||||
@ -242,7 +245,10 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
Some(match mode {
|
Some(match mode {
|
||||||
Absolute(addr) => addr,
|
Absolute(addr) => addr,
|
||||||
ZeroPage(addr) => u16::from(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) => {
|
AbsoluteIndirect(addr) => {
|
||||||
let high = self.read(addr);
|
let high = self.read(addr);
|
||||||
let low = self.read(addr.wrapping_add(1));
|
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_lhs, low_lhs) = to_bcd_nibbles(lhs);
|
||||||
let (high_rhs, low_rhs) = to_bcd_nibbles(rhs);
|
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 (low_carry, low) = (low_sum / 10, low_sum % 10);
|
||||||
let high_sum = high_lhs + high_rhs + low_carry;
|
let high_sum = high_lhs + high_rhs + low_carry;
|
||||||
let (sum, carry) = from_bcd_nibbles(low, high_sum);
|
let (sum, carry) = from_bcd_nibbles(low, high_sum);
|
||||||
self.state.status.set(Status::CARRY, carry);
|
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));
|
let overflow_sum = u16::from(from_bcd(lhs))
|
||||||
self.state.status.set(Status::OVERFLOW, (lhs & 0x80) != (overflow_sum & 0x80) as u8);
|
+ 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::ZERO, sum == 0);
|
||||||
self.state.status.set(Status::NEGATIVE, (sum as i8) < 0);
|
self.state.status.set(Status::NEGATIVE, (sum as i8) < 0);
|
||||||
self.state.accumulator = sum;
|
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_lhs, low_lhs) = to_bcd_nibbles(lhs);
|
||||||
let (high_rhs, low_rhs) = to_bcd_nibbles(rhs);
|
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;
|
let mut low_borrow = 0;
|
||||||
if low < 0 {
|
if low < 0 {
|
||||||
low += 10;
|
low += 10;
|
||||||
@ -493,18 +507,22 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
let byte = self.byte(mode);
|
let byte = self.byte(mode);
|
||||||
let result = self.state.accumulator & byte;
|
let result = self.state.accumulator & byte;
|
||||||
self.state.status.set(Status::ZERO, result == 0);
|
self.state.status.set(Status::ZERO, result == 0);
|
||||||
self.state.status.set(Status::NEGATIVE, byte & 0b1000_0000 != 0);
|
self.state
|
||||||
self.state.status.set(Status::OVERFLOW, byte & 0b0100_0000 != 0);
|
.status
|
||||||
|
.set(Status::NEGATIVE, byte & 0b1000_0000 != 0);
|
||||||
|
self.state
|
||||||
|
.status
|
||||||
|
.set(Status::OVERFLOW, byte & 0b0100_0000 != 0);
|
||||||
})?,
|
})?,
|
||||||
CMP => opcode.address_mode.map(|mode|
|
CMP => opcode
|
||||||
self.compare(self.state.accumulator, mode)
|
.address_mode
|
||||||
)?,
|
.map(|mode| self.compare(self.state.accumulator, mode))?,
|
||||||
CPX => opcode.address_mode.map(|mode|
|
CPX => opcode
|
||||||
self.compare(self.state.x_register, mode)
|
.address_mode
|
||||||
)?,
|
.map(|mode| self.compare(self.state.x_register, mode))?,
|
||||||
CPY => opcode.address_mode.map(|mode|
|
CPY => opcode
|
||||||
self.compare(self.state.y_register, mode)
|
.address_mode
|
||||||
)?,
|
.map(|mode| self.compare(self.state.y_register, mode))?,
|
||||||
inst @ (BCC | BNE | BPL | BVC | BCS | BEQ | BMI | BVS) => {
|
inst @ (BCC | BNE | BPL | BVC | BCS | BEQ | BMI | BVS) => {
|
||||||
opcode.address_mode.and_then(|mode| {
|
opcode.address_mode.and_then(|mode| {
|
||||||
let mask = match inst {
|
let mask = match inst {
|
||||||
@ -531,7 +549,7 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
PLA => {
|
PLA => {
|
||||||
let new_acc = self.pop();
|
let new_acc = self.pop();
|
||||||
self.state.accumulator = self.set_flags(new_acc);
|
self.state.accumulator = self.set_flags(new_acc);
|
||||||
},
|
}
|
||||||
PHP => self.push((self.state.status | Status::UNUSED | Status::BREAK).bits()),
|
PHP => self.push((self.state.status | Status::UNUSED | Status::BREAK).bits()),
|
||||||
PLP => self.state.status = Status::from_bits_retain(self.pop()) | Status::UNUSED,
|
PLP => self.state.status = Status::from_bits_retain(self.pop()) | Status::UNUSED,
|
||||||
JMP => opcode.address_mode.and_then(|mode| {
|
JMP => opcode.address_mode.and_then(|mode| {
|
||||||
@ -540,8 +558,9 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
Some(())
|
Some(())
|
||||||
})?,
|
})?,
|
||||||
JSR => opcode.address_mode.and_then(|mode| {
|
JSR => opcode.address_mode.and_then(|mode| {
|
||||||
let [high, low] =
|
let [high, low] = self
|
||||||
self.state.program_counter
|
.state
|
||||||
|
.program_counter
|
||||||
.wrapping_add(u16::from(opcode_length))
|
.wrapping_add(u16::from(opcode_length))
|
||||||
.wrapping_sub(1)
|
.wrapping_sub(1)
|
||||||
.to_le_bytes();
|
.to_le_bytes();
|
||||||
@ -580,7 +599,10 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if increment {
|
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)
|
Some(false)
|
||||||
@ -617,16 +639,17 @@ impl<R: ReadCallback, W: WriteCallback> Emulator<R, W> {
|
|||||||
|
|
||||||
/// Computes binary add with carry
|
/// Computes binary add with carry
|
||||||
fn adc(&mut self, rhs: u8) -> u8 {
|
fn adc(&mut self, rhs: u8) -> u8 {
|
||||||
let sum =
|
let sum = u16::from(self.state.accumulator)
|
||||||
u16::from(self.state.accumulator) +
|
+ u16::from(rhs)
|
||||||
u16::from(rhs) +
|
+ u16::from(self.state.status.contains(Status::CARRY));
|
||||||
u16::from(self.state.status.contains(Status::CARRY));
|
|
||||||
|
|
||||||
let acc = u16::from(self.state.accumulator);
|
let acc = u16::from(self.state.accumulator);
|
||||||
let rhs = u16::from(rhs);
|
let rhs = u16::from(rhs);
|
||||||
|
|
||||||
self.state.status.set(Status::CARRY, sum > 0xFF);
|
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;
|
let sum = sum as u8;
|
||||||
|
|
||||||
|
@ -51,7 +51,7 @@ impl fmt::Display for AddressMode {
|
|||||||
AddressMode::ZeroPageX(n) => write!(f, "${n:02X},X"),
|
AddressMode::ZeroPageX(n) => write!(f, "${n:02X},X"),
|
||||||
AddressMode::ZeroPageY(n) => write!(f, "${n:02X},Y"),
|
AddressMode::ZeroPageY(n) => write!(f, "${n:02X},Y"),
|
||||||
AddressMode::ZeroPageIndirectX(n) => write!(f, "(${n:02X},X)"),
|
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.
|
/// Forces a hardware interrupt.
|
||||||
BRK,
|
BRK,
|
||||||
/// Does nothing.
|
/// Does nothing.
|
||||||
NOP
|
NOP,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Instruction {
|
impl Instruction {
|
||||||
@ -222,7 +222,10 @@ impl Opcode {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn new(instruction: Instruction, address_mode: Option<AddressMode>) -> Self {
|
pub fn new(instruction: Instruction, address_mode: Option<AddressMode>) -> Self {
|
||||||
Self { instruction, address_mode }
|
Self {
|
||||||
|
instruction,
|
||||||
|
address_mode,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -378,7 +381,6 @@ macro_rules! opcodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
opcodes! {
|
opcodes! {
|
||||||
Opcode::new(BRK, None) => 0x00,
|
Opcode::new(BRK, None) => 0x00,
|
||||||
Opcode::new(ORA, Some(ZeroPageIndirectX)) => 0x01,
|
Opcode::new(ORA, Some(ZeroPageIndirectX)) => 0x01,
|
||||||
|
11
src/lib.rs
11
src/lib.rs
@ -4,10 +4,13 @@
|
|||||||
#![forbid(unsafe_code)]
|
#![forbid(unsafe_code)]
|
||||||
#![doc = include_str!("../README.md")]
|
#![doc = include_str!("../README.md")]
|
||||||
|
|
||||||
pub mod state;
|
|
||||||
pub mod instructions;
|
|
||||||
pub mod emulation;
|
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 state::{State, Status};
|
||||||
pub use emulation::{Emulator, ReadCallback, WriteCallback, DefaultReadCallback, DefaultWriteCallback, FunctionReadCallback, FunctionWriteCallback};
|
|
||||||
|
@ -4,8 +4,8 @@ extern crate std;
|
|||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use {
|
use {
|
||||||
serde::{Serialize, Deserialize},
|
serde::{Deserialize, Serialize},
|
||||||
serde_with::{As, Bytes}
|
serde_with::{As, Bytes},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Copy, Clone, PartialEq, Eq, core::hash::Hash)]
|
#[derive(Copy, Clone, PartialEq, Eq, core::hash::Hash)]
|
||||||
@ -39,7 +39,7 @@ pub struct State {
|
|||||||
/// but grows the struct's size considerably.
|
/// but grows the struct's size considerably.
|
||||||
/// Consider storing this struct on the heap if this is undesirable and you have one.
|
/// Consider storing this struct on the heap if this is undesirable and you have one.
|
||||||
#[cfg_attr(feature = "serde", serde(with = "As::<Bytes>"))]
|
#[cfg_attr(feature = "serde", serde(with = "As::<Bytes>"))]
|
||||||
pub memory: [u8; 256 * 256]
|
pub memory: [u8; 256 * 256],
|
||||||
}
|
}
|
||||||
|
|
||||||
#[allow(clippy::missing_fields_in_debug)]
|
#[allow(clippy::missing_fields_in_debug)]
|
||||||
@ -66,7 +66,7 @@ impl Default for State {
|
|||||||
stack_pointer: 0xFF,
|
stack_pointer: 0xFF,
|
||||||
status: Status::default(),
|
status: Status::default(),
|
||||||
memory: [0; 256 * 256],
|
memory: [0; 256 * 256],
|
||||||
_padding: 0
|
_padding: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -57,15 +57,15 @@ fn functional_test() -> Result<(), usize> {
|
|||||||
}
|
}
|
||||||
eprintln!("----------------------------[STACK]-----------------------------");
|
eprintln!("----------------------------[STACK]-----------------------------");
|
||||||
for i in 0..16 {
|
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);
|
return Err(0);
|
||||||
}
|
}
|
||||||
if needs_interrupt {
|
if needs_interrupt {
|
||||||
let vector = u16::from_le_bytes([
|
let vector = u16::from_le_bytes([emulator.read(0xFFFE), emulator.read(0xFFFF)]);
|
||||||
emulator.read(0xFFFE),
|
|
||||||
emulator.read(0xFFFF)
|
|
||||||
]);
|
|
||||||
eprintln!("[MASKABLE INTERRUPT, GOING TO IRQ VECTOR @ {vector:02X}]");
|
eprintln!("[MASKABLE INTERRUPT, GOING TO IRQ VECTOR @ {vector:02X}]");
|
||||||
// Go to interrupt vector
|
// Go to interrupt vector
|
||||||
emulator.state.program_counter = vector;
|
emulator.state.program_counter = vector;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user