mirror of
https://github.com/mre/mos6502.git
synced 2024-06-28 11:29:57 +00:00
Compare commits
2 Commits
4195a8f4c1
...
906d6e3f19
Author | SHA1 | Date | |
---|---|---|---|
|
906d6e3f19 | ||
|
c58bba1f4c |
34
src/cpu.rs
34
src/cpu.rs
|
@ -47,6 +47,9 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Bus, V: Variant> CPU<M, V> {
|
impl<M: Bus, V: Variant> CPU<M, V> {
|
||||||
|
// Allowing `needless_pass_by_value` to simplify construction. Passing by
|
||||||
|
// value avoids the borrow and improves readability when constructing the
|
||||||
|
// CPU.
|
||||||
#[allow(clippy::needless_pass_by_value)]
|
#[allow(clippy::needless_pass_by_value)]
|
||||||
pub fn new(memory: M, _variant: V) -> CPU<M, V> {
|
pub fn new(memory: M, _variant: V) -> CPU<M, V> {
|
||||||
CPU {
|
CPU {
|
||||||
|
@ -638,18 +641,29 @@ impl<M: Bus, V: Variant> CPU<M, V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Checks if a given `u8` value should be interpreted as negative when
|
||||||
|
/// considered as `i8`.
|
||||||
|
///
|
||||||
|
/// In an 8-bit unsigned integer (`u8`), values range from 0 to 255. When
|
||||||
|
/// these values are interpreted as signed integers (`i8`), values from 128
|
||||||
|
/// to 255 are considered negative, corresponding to the signed range -128
|
||||||
|
/// to -1. This function checks if the provided `u8` value falls within that
|
||||||
|
/// range, effectively determining if the most significant bit is set, which
|
||||||
|
/// indicates a negative number in two's complement form.
|
||||||
|
/// ```
|
||||||
const fn value_is_negative(value: u8) -> bool {
|
const fn value_is_negative(value: u8) -> bool {
|
||||||
value > 127
|
value > 127
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_flags_from_u8(status: &mut Status, value: u8) {
|
fn set_flags_from_u8(status: &mut Status, value: u8) {
|
||||||
let is_zero = value == 0;
|
let is_zero = value == 0;
|
||||||
|
let is_negative = Self::value_is_negative(value);
|
||||||
|
|
||||||
status.set_with_mask(
|
status.set_with_mask(
|
||||||
Status::PS_ZERO | Status::PS_NEGATIVE,
|
Status::PS_ZERO | Status::PS_NEGATIVE,
|
||||||
Status::new(StatusArgs {
|
Status::new(StatusArgs {
|
||||||
zero: is_zero,
|
zero: is_zero,
|
||||||
negative: Self::value_is_negative(value),
|
negative: is_negative,
|
||||||
..StatusArgs::none()
|
..StatusArgs::none()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -951,12 +965,13 @@ impl<M: Bus, V: Variant> CPU<M, V> {
|
||||||
*val = value_new;
|
*val = value_new;
|
||||||
|
|
||||||
let is_zero = value_new == 0;
|
let is_zero = value_new == 0;
|
||||||
|
let is_negative = Self::value_is_negative(value_new);
|
||||||
|
|
||||||
flags.set_with_mask(
|
flags.set_with_mask(
|
||||||
Status::PS_NEGATIVE | Status::PS_ZERO,
|
Status::PS_NEGATIVE | Status::PS_ZERO,
|
||||||
Status::new(StatusArgs {
|
Status::new(StatusArgs {
|
||||||
negative: Self::value_is_negative(value_new),
|
|
||||||
zero: is_zero,
|
zero: is_zero,
|
||||||
|
negative: is_negative,
|
||||||
..StatusArgs::none()
|
..StatusArgs::none()
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
@ -1022,20 +1037,24 @@ impl<M: Bus, V: Variant> CPU<M, V> {
|
||||||
// ...
|
// ...
|
||||||
// The N flag contains most significant bit of the subtraction result.
|
// The N flag contains most significant bit of the subtraction result.
|
||||||
fn compare(&mut self, r: u8, val: u8) {
|
fn compare(&mut self, r: u8, val: u8) {
|
||||||
|
// Setting the CARRY flag: A (unsigned) >= NUM (unsigned)
|
||||||
if r >= val {
|
if r >= val {
|
||||||
self.registers.status.insert(Status::PS_CARRY);
|
self.registers.status.insert(Status::PS_CARRY);
|
||||||
} else {
|
} else {
|
||||||
self.registers.status.remove(Status::PS_CARRY);
|
self.registers.status.remove(Status::PS_CARRY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Setting the ZERO flag: A = NUM
|
||||||
if r == val {
|
if r == val {
|
||||||
self.registers.status.insert(Status::PS_ZERO);
|
self.registers.status.insert(Status::PS_ZERO);
|
||||||
} else {
|
} else {
|
||||||
self.registers.status.remove(Status::PS_ZERO);
|
self.registers.status.remove(Status::PS_ZERO);
|
||||||
}
|
}
|
||||||
|
|
||||||
let diff: i8 = (r as i8).wrapping_sub(val as i8);
|
// Set the NEGATIVE flag based on the MSB of the result of subtraction
|
||||||
if diff < 0 {
|
// This checks if the 8th bit is set (0x80 in hex is 128 in decimal, which is the 8th bit in a byte)
|
||||||
|
let diff = r.wrapping_sub(val);
|
||||||
|
if Self::value_is_negative(diff) {
|
||||||
self.registers.status.insert(Status::PS_NEGATIVE);
|
self.registers.status.insert(Status::PS_NEGATIVE);
|
||||||
} else {
|
} else {
|
||||||
self.registers.status.remove(Status::PS_NEGATIVE);
|
self.registers.status.remove(Status::PS_NEGATIVE);
|
||||||
|
@ -1101,6 +1120,11 @@ impl<M: Bus, V: Variant> core::fmt::Debug for CPU<M, V> {
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
// Casting from signed to unsigned integers is intentional in these tests
|
||||||
|
#![allow(clippy::cast_sign_loss)]
|
||||||
|
// Operations may intentionally wrap due to emulation of 8-bit unsigned
|
||||||
|
// integer arithmetic. We do this to test wrap-around conditions.
|
||||||
|
#![allow(clippy::cast_possible_wrap)]
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::instruction::Nmos6502;
|
use crate::instruction::Nmos6502;
|
||||||
|
@ -1204,6 +1228,8 @@ mod tests {
|
||||||
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
||||||
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
||||||
|
|
||||||
|
// Allow casting from i8 to u8; -127i8 wraps to 129u8, as intended for
|
||||||
|
// two's complement arithmetic.
|
||||||
cpu.add_with_carry(-127i8 as u8);
|
cpu.add_with_carry(-127i8 as u8);
|
||||||
assert_eq!(cpu.registers.accumulator, 0);
|
assert_eq!(cpu.registers.accumulator, 0);
|
||||||
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
||||||
|
|
|
@ -500,6 +500,7 @@ impl crate::Variant for RevisionA {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Emulates the 65C02, which has a few bugfixes, and another addressing mode
|
/// Emulates the 65C02, which has a few bugfixes, and another addressing mode
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
pub struct Cmos6502;
|
pub struct Cmos6502;
|
||||||
|
|
||||||
impl crate::Variant for Cmos6502 {
|
impl crate::Variant for Cmos6502 {
|
||||||
|
|
|
@ -81,6 +81,14 @@ pub trait Bus {
|
||||||
/// Sets the bytes starting at the given address to the given values.
|
/// Sets the bytes starting at the given address to the given values.
|
||||||
///
|
///
|
||||||
/// This is a default implementation that calls `set_byte` for each byte.
|
/// This is a default implementation that calls `set_byte` for each byte.
|
||||||
|
///
|
||||||
|
/// # Note
|
||||||
|
///
|
||||||
|
/// This assumes that the length of `values` is less than or equal to
|
||||||
|
/// [`u16::MAX`] (65535). If the length of `values` is greater than `u16::MAX`,
|
||||||
|
/// this will truncate the length. This assumption is made because the
|
||||||
|
/// maximum addressable memory for the 6502 is 64KB.
|
||||||
|
#[allow(clippy::cast_possible_truncation)]
|
||||||
fn set_bytes(&mut self, start: u16, values: &[u8]) {
|
fn set_bytes(&mut self, start: u16, values: &[u8]) {
|
||||||
for i in 0..values.len() as u16 {
|
for i in 0..values.len() as u16 {
|
||||||
self.set_byte(start + i, values[i as usize]);
|
self.set_byte(start + i, values[i as usize]);
|
||||||
|
@ -102,12 +110,14 @@ impl Bus for Memory {
|
||||||
self.bytes[address as usize]
|
self.bytes[address as usize]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets the byte at the given address to the given value and returns the
|
/// Sets the byte at the given address to the given value and returns the
|
||||||
// previous value at the address.
|
/// previous value at the address.
|
||||||
fn set_byte(&mut self, address: u16, value: u8) {
|
fn set_byte(&mut self, address: u16, value: u8) {
|
||||||
self.bytes[address as usize] = value;
|
self.bytes[address as usize] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Fast way to set multiple bytes in memory when the underlying memory is a
|
||||||
|
/// consecutive block of bytes.
|
||||||
fn set_bytes(&mut self, start: u16, values: &[u8]) {
|
fn set_bytes(&mut self, start: u16, values: &[u8]) {
|
||||||
let start = start as usize;
|
let start = start as usize;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user