mirror of
https://github.com/mre/mos6502.git
synced 2024-11-28 07:49:19 +00:00
Merge pull request #53 from mre/addrfix
Proper wrap-around for address calculations
This commit is contained in:
commit
8850934023
@ -20,7 +20,6 @@ Source: [Wikipedia](https://en.wikipedia.org/wiki/MOS_Technology_6502)
|
||||
## How to use this library
|
||||
|
||||
```rust
|
||||
use mos6502::address::Address;
|
||||
use mos6502::cpu;
|
||||
|
||||
fn main() {
|
||||
@ -52,9 +51,9 @@ fn main() {
|
||||
|
||||
let mut cpu = cpu::CPU::new();
|
||||
|
||||
cpu.memory.set_bytes(Address(0x00), &zero_page_data);
|
||||
cpu.memory.set_bytes(Address(0x10), &program);
|
||||
cpu.registers.program_counter = Address(0x10);
|
||||
cpu.memory.set_bytes(0x00, &zero_page_data);
|
||||
cpu.memory.set_bytes(0x10, &program);
|
||||
cpu.registers.program_counter = 0x10;
|
||||
|
||||
cpu.run();
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
extern crate mos6502;
|
||||
use mos6502::address::Address;
|
||||
|
||||
use mos6502::cpu;
|
||||
|
||||
fn main() {
|
||||
@ -35,9 +35,9 @@ fn main() {
|
||||
|
||||
let mut cpu = cpu::CPU::new();
|
||||
|
||||
cpu.memory.set_bytes(Address(0x00), &zero_page_data);
|
||||
cpu.memory.set_bytes(Address(0x10), &program);
|
||||
cpu.registers.program_counter = Address(0x10);
|
||||
cpu.memory.set_bytes(0x00, &zero_page_data);
|
||||
cpu.memory.set_bytes(0x10, &program);
|
||||
cpu.registers.program_counter = 0x10;
|
||||
|
||||
cpu.run();
|
||||
|
||||
|
@ -30,9 +30,6 @@ extern crate mos6502;
|
||||
#[cfg(not(test))]
|
||||
use mos6502::cpu;
|
||||
|
||||
#[cfg(not(test))]
|
||||
use mos6502::address::Address;
|
||||
|
||||
#[cfg(not(test))]
|
||||
fn main() {
|
||||
let mut cpu = cpu::CPU::new();
|
||||
@ -94,11 +91,11 @@ fn main() {
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, // ADC IndirectIndexedY target
|
||||
];
|
||||
|
||||
cpu.memory.set_bytes(Address(0x0000), &zero_page_data);
|
||||
cpu.memory.set_bytes(Address(0x4000), &program);
|
||||
cpu.memory.set_bytes(Address(0x8000), &data);
|
||||
cpu.memory.set_bytes(0x0000, &zero_page_data);
|
||||
cpu.memory.set_bytes(0x4000, &program);
|
||||
cpu.memory.set_bytes(0x8000, &data);
|
||||
|
||||
cpu.registers.program_counter = Address(0x4000);
|
||||
cpu.registers.program_counter = 0x4000;
|
||||
|
||||
cpu.run();
|
||||
|
||||
|
@ -1,91 +0,0 @@
|
||||
// Copyright (C) 2014 The 6502-rs Developers
|
||||
// All rights reserved.
|
||||
//
|
||||
// Redistribution and use in source and binary forms, with or without
|
||||
// modification, are permitted provided that the following conditions
|
||||
// are met:
|
||||
// 1. Redistributions of source code must retain the above copyright
|
||||
// notice, this list of conditions and the following disclaimer.
|
||||
// 2. Redistributions in binary form must reproduce the above copyright
|
||||
// notice, this list of conditions and the following disclaimer in the
|
||||
// documentation and/or other materials provided with the distribution.
|
||||
// 3. Neither the names of the copyright holders nor the names of any
|
||||
// contributors may be used to endorse or promote products derived from this
|
||||
// software without specific prior written permission.
|
||||
//
|
||||
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
||||
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use core::ops::Add;
|
||||
|
||||
// The idea here is that it doesn't make sense to add two addresses, but it
|
||||
// does make sense to add an address and an "address-difference". (If this
|
||||
// is too annoying to work with we should let it go.)
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct AddressDiff(pub i32);
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)]
|
||||
pub struct Address(pub u16);
|
||||
|
||||
impl Add<AddressDiff> for Address {
|
||||
type Output = Address;
|
||||
|
||||
fn add(self, AddressDiff(rhs): AddressDiff) -> Address {
|
||||
let Address(lhs) = self;
|
||||
|
||||
Address(((i32::from(lhs)) + rhs) as u16)
|
||||
}
|
||||
}
|
||||
|
||||
impl Add for AddressDiff {
|
||||
type Output = AddressDiff;
|
||||
|
||||
fn add(self, AddressDiff(rhs): AddressDiff) -> AddressDiff {
|
||||
let AddressDiff(lhs) = self;
|
||||
AddressDiff(lhs + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub struct CheckedAddressDiff(u16);
|
||||
|
||||
impl Add<CheckedAddressDiff> for Address {
|
||||
type Output = Address;
|
||||
|
||||
fn add(self, CheckedAddressDiff(rhs): CheckedAddressDiff) -> Address {
|
||||
let Address(lhs) = self;
|
||||
|
||||
// We probably don't want to overflow when doing arithmetic in our own
|
||||
// code.
|
||||
debug_assert!(lhs.checked_add(rhs).is_some());
|
||||
|
||||
Address(lhs + rhs)
|
||||
}
|
||||
}
|
||||
|
||||
impl Address {
|
||||
pub fn to_u16(self) -> u16 {
|
||||
self.0
|
||||
}
|
||||
|
||||
pub fn to_usize(self) -> usize {
|
||||
self.to_u16() as usize
|
||||
}
|
||||
|
||||
pub fn get_page_number(self) -> u8 {
|
||||
(self.to_u16() & 0xff00 >> 8) as u8
|
||||
}
|
||||
|
||||
pub fn get_offset(self) -> u8 {
|
||||
(self.to_u16() & 0x00ff) as u8
|
||||
}
|
||||
}
|
114
src/cpu.rs
114
src/cpu.rs
@ -25,7 +25,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::address::{Address, AddressDiff};
|
||||
use crate::instruction::{self, DecodedInstr, Instruction, OpInput};
|
||||
use crate::memory::Memory;
|
||||
use crate::registers::{Registers, StackPointer, Status, StatusArgs};
|
||||
@ -60,15 +59,16 @@ impl CPU {
|
||||
match instruction::OPCODES[x as usize] {
|
||||
Some((instr, am)) => {
|
||||
let extra_bytes = am.extra_bytes();
|
||||
let num_bytes = AddressDiff(1) + extra_bytes;
|
||||
let num_bytes = extra_bytes + 1;
|
||||
|
||||
let data_start = self.registers.program_counter + AddressDiff(1);
|
||||
let data_start = self.registers.program_counter.wrapping_add(1);
|
||||
|
||||
let slice = self.memory.get_slice(data_start, extra_bytes);
|
||||
let am_out = am.process(self, slice);
|
||||
|
||||
// Increment program counter
|
||||
self.registers.program_counter = self.registers.program_counter + num_bytes;
|
||||
self.registers.program_counter =
|
||||
self.registers.program_counter.wrapping_add(num_bytes);
|
||||
|
||||
Some((instr, am_out))
|
||||
}
|
||||
@ -110,17 +110,17 @@ impl CPU {
|
||||
}
|
||||
|
||||
(Instruction::BCC, OpInput::UseRelative(rel)) => {
|
||||
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
||||
let addr = self.registers.program_counter.wrapping_add(rel);
|
||||
self.branch_if_carry_clear(addr);
|
||||
}
|
||||
|
||||
(Instruction::BCS, OpInput::UseRelative(rel)) => {
|
||||
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
||||
let addr = self.registers.program_counter.wrapping_add(rel);
|
||||
self.branch_if_carry_set(addr);
|
||||
}
|
||||
|
||||
(Instruction::BEQ, OpInput::UseRelative(rel)) => {
|
||||
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
||||
let addr = self.registers.program_counter.wrapping_add(rel);
|
||||
self.branch_if_equal(addr);
|
||||
}
|
||||
|
||||
@ -150,23 +150,23 @@ impl CPU {
|
||||
}
|
||||
|
||||
(Instruction::BMI, OpInput::UseRelative(rel)) => {
|
||||
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
||||
let addr = self.registers.program_counter.wrapping_add(rel);
|
||||
debug!("branch if minus relative. address: {:?}", addr);
|
||||
self.branch_if_minus(addr);
|
||||
}
|
||||
|
||||
(Instruction::BPL, OpInput::UseRelative(rel)) => {
|
||||
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
||||
let addr = self.registers.program_counter.wrapping_add(rel);
|
||||
self.branch_if_positive(addr);
|
||||
}
|
||||
|
||||
(Instruction::BVC, OpInput::UseRelative(rel)) => {
|
||||
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
||||
let addr = self.registers.program_counter.wrapping_add(rel);
|
||||
self.branch_if_overflow_clear(addr);
|
||||
}
|
||||
|
||||
(Instruction::BVS, OpInput::UseRelative(rel)) => {
|
||||
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
||||
let addr = self.registers.program_counter.wrapping_add(rel);
|
||||
self.branch_if_overflow_set(addr);
|
||||
}
|
||||
|
||||
@ -648,7 +648,7 @@ impl CPU {
|
||||
self.load_accumulator(result);
|
||||
}
|
||||
|
||||
fn decrement_memory(&mut self, addr: Address) {
|
||||
fn decrement_memory(&mut self, addr: u16) {
|
||||
let value_new = self.memory.get_byte(addr).wrapping_sub(1);
|
||||
|
||||
self.memory.set_byte(addr, value_new);
|
||||
@ -671,47 +671,47 @@ impl CPU {
|
||||
self.load_x_register(val - 1);
|
||||
}
|
||||
|
||||
fn jump(&mut self, addr: Address) {
|
||||
fn jump(&mut self, addr: u16) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
|
||||
fn branch_if_carry_clear(&mut self, addr: Address) {
|
||||
fn branch_if_carry_clear(&mut self, addr: u16) {
|
||||
if !self.registers.status.contains(Status::PS_CARRY) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_if_carry_set(&mut self, addr: Address) {
|
||||
fn branch_if_carry_set(&mut self, addr: u16) {
|
||||
if self.registers.status.contains(Status::PS_CARRY) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_if_equal(&mut self, addr: Address) {
|
||||
fn branch_if_equal(&mut self, addr: u16) {
|
||||
if self.registers.status.contains(Status::PS_ZERO) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_if_minus(&mut self, addr: Address) {
|
||||
fn branch_if_minus(&mut self, addr: u16) {
|
||||
if self.registers.status.contains(Status::PS_NEGATIVE) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_if_positive(&mut self, addr: Address) {
|
||||
fn branch_if_positive(&mut self, addr: u16) {
|
||||
if !self.registers.status.contains(Status::PS_NEGATIVE) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_if_overflow_clear(&mut self, addr: Address) {
|
||||
fn branch_if_overflow_clear(&mut self, addr: u16) {
|
||||
if !self.registers.status.contains(Status::PS_OVERFLOW) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
}
|
||||
|
||||
fn branch_if_overflow_set(&mut self, addr: Address) {
|
||||
fn branch_if_overflow_set(&mut self, addr: u16) {
|
||||
if self.registers.status.contains(Status::PS_OVERFLOW) {
|
||||
self.registers.program_counter = addr;
|
||||
}
|
||||
@ -773,13 +773,13 @@ impl CPU {
|
||||
}
|
||||
|
||||
fn push_on_stack(&mut self, val: u8) {
|
||||
let addr = self.registers.stack_pointer.to_address();
|
||||
let addr = self.registers.stack_pointer.to_u16();
|
||||
self.memory.set_byte(addr, val);
|
||||
self.registers.stack_pointer.decrement();
|
||||
}
|
||||
|
||||
fn pull_from_stack(&mut self) -> u8 {
|
||||
let addr = self.registers.stack_pointer.to_address();
|
||||
let addr = self.registers.stack_pointer.to_u16();
|
||||
let out = self.memory.get_byte(addr);
|
||||
self.registers.stack_pointer.increment();
|
||||
out
|
||||
@ -1022,7 +1022,7 @@ mod tests {
|
||||
#[test]
|
||||
fn decrement_memory_test() {
|
||||
let mut cpu = CPU::new();
|
||||
let addr = Address(0xA1B2);
|
||||
let addr: u16 = 0xA1B2;
|
||||
|
||||
cpu.memory.set_byte(addr, 5);
|
||||
|
||||
@ -1135,7 +1135,7 @@ mod tests {
|
||||
#[test]
|
||||
fn jump_test() {
|
||||
let mut cpu = CPU::new();
|
||||
let addr = Address(0xA1B1);
|
||||
let addr: u16 = 0xA1B1;
|
||||
|
||||
cpu.jump(addr);
|
||||
assert_eq!(cpu.registers.program_counter, addr);
|
||||
@ -1146,12 +1146,12 @@ mod tests {
|
||||
let mut cpu = CPU::new();
|
||||
|
||||
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
||||
cpu.branch_if_carry_clear(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0));
|
||||
cpu.branch_if_carry_clear(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0));
|
||||
|
||||
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
||||
cpu.branch_if_carry_clear(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
||||
cpu.branch_if_carry_clear(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1159,24 +1159,24 @@ mod tests {
|
||||
let mut cpu = CPU::new();
|
||||
|
||||
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
||||
cpu.branch_if_carry_set(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0));
|
||||
cpu.branch_if_carry_set(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0));
|
||||
|
||||
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
||||
cpu.branch_if_carry_set(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
||||
cpu.branch_if_carry_set(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_if_equal_test() {
|
||||
let mut cpu = CPU::new();
|
||||
|
||||
cpu.branch_if_equal(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0));
|
||||
cpu.branch_if_equal(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0));
|
||||
|
||||
cpu.registers.status.or(Status::PS_ZERO);
|
||||
cpu.branch_if_equal(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
||||
cpu.branch_if_equal(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1185,9 +1185,9 @@ mod tests {
|
||||
let mut cpu = CPU::new();
|
||||
let registers_before = cpu.registers;
|
||||
|
||||
cpu.branch_if_minus(Address(0xABCD));
|
||||
cpu.branch_if_minus(0xABCD);
|
||||
assert_eq!(cpu.registers, registers_before);
|
||||
assert_eq!(cpu.registers.program_counter, Address(0));
|
||||
assert_eq!(cpu.registers.program_counter, (0));
|
||||
}
|
||||
|
||||
{
|
||||
@ -1196,9 +1196,9 @@ mod tests {
|
||||
cpu.registers.status.or(Status::PS_NEGATIVE);
|
||||
let registers_before = cpu.registers;
|
||||
|
||||
cpu.branch_if_minus(Address(0xABCD));
|
||||
cpu.branch_if_minus(0xABCD);
|
||||
assert_eq!(cpu.registers.status, registers_before.status);
|
||||
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
}
|
||||
|
||||
@ -1207,12 +1207,12 @@ mod tests {
|
||||
let mut cpu = CPU::new();
|
||||
|
||||
cpu.registers.status.insert(Status::PS_NEGATIVE);
|
||||
cpu.branch_if_positive(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0));
|
||||
cpu.branch_if_positive(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0));
|
||||
|
||||
cpu.registers.status.remove(Status::PS_NEGATIVE);
|
||||
cpu.branch_if_positive(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
||||
cpu.branch_if_positive(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
@ -1220,24 +1220,34 @@ mod tests {
|
||||
let mut cpu = CPU::new();
|
||||
|
||||
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
||||
cpu.branch_if_overflow_clear(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0));
|
||||
cpu.branch_if_overflow_clear(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0));
|
||||
|
||||
cpu.registers.status.remove(Status::PS_OVERFLOW);
|
||||
cpu.branch_if_overflow_clear(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
||||
cpu.branch_if_overflow_clear(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_across_end_of_address_space() {
|
||||
let mut cpu = CPU::new();
|
||||
cpu.registers.program_counter = 0xffff;
|
||||
|
||||
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
||||
cpu.branch_if_overflow_set(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn branch_if_overflow_set_test() {
|
||||
let mut cpu = CPU::new();
|
||||
|
||||
cpu.branch_if_overflow_set(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0));
|
||||
cpu.branch_if_overflow_set(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0));
|
||||
|
||||
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
||||
cpu.branch_if_overflow_set(Address(0xABCD));
|
||||
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
||||
cpu.branch_if_overflow_set(0xABCD);
|
||||
assert_eq!(cpu.registers.program_counter, (0xABCD));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
|
@ -25,8 +25,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::address::Address;
|
||||
use crate::address::AddressDiff;
|
||||
use crate::cpu::CPU;
|
||||
|
||||
// Abbreviations
|
||||
@ -113,8 +111,8 @@ pub enum Instruction {
|
||||
pub enum OpInput {
|
||||
UseImplied,
|
||||
UseImmediate(u8),
|
||||
UseRelative(i8),
|
||||
UseAddress(Address),
|
||||
UseRelative(u16),
|
||||
UseAddress(u16),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
@ -136,16 +134,19 @@ pub enum AddressingMode {
|
||||
// zero page address) plus Y register
|
||||
}
|
||||
|
||||
fn arr_to_addr(arr: &[u8]) -> Address {
|
||||
fn xextend(x: u8) -> u16 {
|
||||
u16::from(x)
|
||||
}
|
||||
|
||||
fn arr_to_addr(arr: &[u8]) -> u16 {
|
||||
debug_assert!(arr.len() == 2);
|
||||
|
||||
let x = u16::from(arr[0]) + (u16::from(arr[1]) << 8usize);
|
||||
Address(x)
|
||||
u16::from(arr[0]) + (u16::from(arr[1]) << 8usize)
|
||||
}
|
||||
|
||||
impl AddressingMode {
|
||||
pub fn extra_bytes(self) -> AddressDiff {
|
||||
let x = match self {
|
||||
pub fn extra_bytes(self) -> u16 {
|
||||
match self {
|
||||
AddressingMode::Accumulator => 0,
|
||||
AddressingMode::Implied => 0,
|
||||
AddressingMode::Immediate => 1,
|
||||
@ -159,15 +160,11 @@ impl AddressingMode {
|
||||
AddressingMode::Indirect => 2,
|
||||
AddressingMode::IndexedIndirectX => 1,
|
||||
AddressingMode::IndirectIndexedY => 1,
|
||||
};
|
||||
AddressDiff(x)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn process(self, cpu: &CPU, arr: &[u8]) -> OpInput {
|
||||
debug_assert!({
|
||||
let AddressDiff(x) = self.extra_bytes();
|
||||
arr.len() == x as usize
|
||||
});
|
||||
debug_assert!(arr.len() == self.extra_bytes() as usize);
|
||||
|
||||
let x = cpu.registers.index_x as u8;
|
||||
let y = cpu.registers.index_y as u8;
|
||||
@ -187,24 +184,29 @@ impl AddressingMode {
|
||||
// Use [u8, ..1] from instruction
|
||||
// Interpret as zero page address
|
||||
// (Output: an 8-bit zero-page address)
|
||||
OpInput::UseAddress(Address(u16::from(arr[0])))
|
||||
OpInput::UseAddress(u16::from(arr[0]))
|
||||
}
|
||||
AddressingMode::ZeroPageX => {
|
||||
// Use [u8, ..1] from instruction
|
||||
// Add to X register (as u8 -- the final address is in 0-page)
|
||||
// (Output: an 8-bit zero-page address)
|
||||
OpInput::UseAddress(Address(u16::from(arr[0] + x)))
|
||||
OpInput::UseAddress(u16::from(arr[0].wrapping_add(x)))
|
||||
}
|
||||
AddressingMode::ZeroPageY => {
|
||||
// Use [u8, ..1] from instruction
|
||||
// Add to Y register (as u8 -- the final address is in 0-page)
|
||||
// (Output: an 8-bit zero-page address)
|
||||
OpInput::UseAddress(Address(u16::from(arr[0] + y)))
|
||||
OpInput::UseAddress(u16::from(arr[0].wrapping_add(y)))
|
||||
}
|
||||
AddressingMode::Relative => {
|
||||
// Use [u8, ..1] from instruction
|
||||
// (interpret as relative...)
|
||||
OpInput::UseRelative(arr[0] as i8)
|
||||
// (This is sign extended to a 16-but data type, but an unsigned one: u16. It's a
|
||||
// little weird, but it's so we can add the PC and the offset easily)
|
||||
let offset = arr[0];
|
||||
let sign_extend = if offset & 0x80 == 0x80 { 0xffu8 } else { 0x0 };
|
||||
let rel = u16::from_le_bytes([offset, sign_extend]);
|
||||
OpInput::UseRelative(rel)
|
||||
}
|
||||
AddressingMode::Absolute => {
|
||||
// Use [u8, ..2] from instruction as address
|
||||
@ -214,18 +216,18 @@ impl AddressingMode {
|
||||
AddressingMode::AbsoluteX => {
|
||||
// Use [u8, ..2] from instruction as address, add X
|
||||
// (Output: a 16-bit address)
|
||||
OpInput::UseAddress(arr_to_addr(arr) + AddressDiff(i32::from(x)))
|
||||
OpInput::UseAddress(arr_to_addr(arr).wrapping_add(xextend(x)))
|
||||
}
|
||||
AddressingMode::AbsoluteY => {
|
||||
// Use [u8, ..2] from instruction as address, add Y
|
||||
// (Output: a 16-bit address)
|
||||
OpInput::UseAddress(arr_to_addr(arr) + AddressDiff(i32::from(y)))
|
||||
OpInput::UseAddress(arr_to_addr(arr).wrapping_add(xextend(y)))
|
||||
}
|
||||
AddressingMode::Indirect => {
|
||||
// Use [u8, ..2] from instruction as an address. Interpret the
|
||||
// two bytes starting at that address as an address.
|
||||
// (Output: a 16-bit address)
|
||||
let slice = memory.get_slice(arr_to_addr(arr), AddressDiff(2));
|
||||
let slice = memory.get_slice(arr_to_addr(arr), 2);
|
||||
OpInput::UseAddress(arr_to_addr(slice))
|
||||
}
|
||||
AddressingMode::IndexedIndirectX => {
|
||||
@ -233,8 +235,8 @@ impl AddressingMode {
|
||||
// Add to X register with 0-page wraparound, like ZeroPageX.
|
||||
// This is where the absolute (16-bit) target address is stored.
|
||||
// (Output: a 16-bit address)
|
||||
let start = arr[0] + x;
|
||||
let slice = memory.get_slice(Address(u16::from(start)), AddressDiff(2));
|
||||
let start = arr[0].wrapping_add(x);
|
||||
let slice = memory.get_slice(u16::from(start), 2);
|
||||
OpInput::UseAddress(arr_to_addr(slice))
|
||||
}
|
||||
AddressingMode::IndirectIndexedY => {
|
||||
@ -243,8 +245,8 @@ impl AddressingMode {
|
||||
// Add Y register to this address to get the final address
|
||||
// (Output: a 16-bit address)
|
||||
let start = arr[0];
|
||||
let slice = memory.get_slice(Address(u16::from(start)), AddressDiff(2));
|
||||
OpInput::UseAddress(arr_to_addr(slice) + AddressDiff(i32::from(y)))
|
||||
let slice = memory.get_slice(u16::from(start), 2);
|
||||
OpInput::UseAddress(arr_to_addr(slice).wrapping_add(xextend(y)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -766,3 +768,26 @@ pub static OPCODES: [Option<(Instruction, AddressingMode)>; 256] = [
|
||||
/*0xFF*/
|
||||
None,
|
||||
];
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
|
||||
#[test]
|
||||
fn zeropage_wrap_around() {
|
||||
use crate::instruction::AddressingMode;
|
||||
use crate::instruction::OpInput;
|
||||
use crate::instruction::CPU;
|
||||
|
||||
let mut cpu = CPU::new();
|
||||
cpu.registers.index_x = 9;
|
||||
|
||||
assert!(matches!(
|
||||
AddressingMode::ZeroPageX.process(&cpu, &[10]),
|
||||
OpInput::UseAddress(19)
|
||||
));
|
||||
assert!(matches!(
|
||||
AddressingMode::ZeroPageX.process(&cpu, &[250]),
|
||||
OpInput::UseAddress(3)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -35,7 +35,6 @@ extern crate num;
|
||||
#[macro_use]
|
||||
extern crate bitflags;
|
||||
|
||||
pub mod address;
|
||||
pub mod cpu;
|
||||
pub mod instruction;
|
||||
pub mod memory;
|
||||
|
@ -25,8 +25,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::address::{Address, AddressDiff};
|
||||
|
||||
// JAM: We can probably come up with a better way to represent address ranges.
|
||||
// Address range type?
|
||||
//
|
||||
@ -39,12 +37,12 @@ use crate::address::{Address, AddressDiff};
|
||||
const ADDR_LO_BARE: u16 = 0x0000;
|
||||
const ADDR_HI_BARE: u16 = 0xFFFF;
|
||||
|
||||
pub const MEMORY_ADDRESS_LO: Address = Address(ADDR_LO_BARE);
|
||||
pub const MEMORY_ADDRESS_HI: Address = Address(ADDR_HI_BARE);
|
||||
pub const STACK_ADDRESS_LO: Address = Address(0x0100);
|
||||
pub const STACK_ADDRESS_HI: Address = Address(0x01FF);
|
||||
pub const IRQ_INTERRUPT_VECTOR_LO: Address = Address(0xFFFE);
|
||||
pub const IRQ_INTERRUPT_VECTOR_HI: Address = Address(0xFFFF);
|
||||
pub const MEMORY_ADDRESS_LO: u16 = ADDR_LO_BARE;
|
||||
pub const MEMORY_ADDRESS_HI: u16 = ADDR_HI_BARE;
|
||||
pub const STACK_ADDRESS_LO: u16 = 0x0100;
|
||||
pub const STACK_ADDRESS_HI: u16 = 0x01FF;
|
||||
pub const IRQ_INTERRUPT_VECTOR_LO: u16 = 0xFFFE;
|
||||
pub const IRQ_INTERRUPT_VECTOR_HI: u16 = 0xFFFF;
|
||||
|
||||
const MEMORY_SIZE: usize = (ADDR_HI_BARE - ADDR_LO_BARE) as usize + 1usize;
|
||||
|
||||
@ -67,28 +65,31 @@ impl Memory {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_byte(&self, address: Address) -> u8 {
|
||||
self.bytes[address.to_usize()]
|
||||
pub fn get_byte(&self, address: u16) -> u8 {
|
||||
self.bytes[address as usize]
|
||||
}
|
||||
|
||||
pub fn get_byte_mut_ref(&mut self, address: Address) -> &mut u8 {
|
||||
&mut self.bytes[address.to_usize()]
|
||||
pub fn get_byte_mut_ref(&mut self, address: u16) -> &mut u8 {
|
||||
&mut self.bytes[address as usize]
|
||||
}
|
||||
|
||||
pub fn get_slice(&self, start: Address, diff: AddressDiff) -> &[u8] {
|
||||
&self.bytes[start.to_usize()..(start + diff).to_usize()]
|
||||
pub fn get_slice(&self, start: u16, diff: u16) -> &[u8] {
|
||||
let orig: usize = start.into();
|
||||
let end = orig + diff as usize;
|
||||
|
||||
&self.bytes[orig..end]
|
||||
}
|
||||
|
||||
// Sets the byte at the given address to the given value and returns the
|
||||
// previous value at the address.
|
||||
pub fn set_byte(&mut self, address: Address, value: u8) -> u8 {
|
||||
pub fn set_byte(&mut self, address: u16, value: u8) -> u8 {
|
||||
let old_value = self.get_byte(address);
|
||||
self.bytes[address.to_usize()] = value;
|
||||
self.bytes[address as usize] = value;
|
||||
old_value
|
||||
}
|
||||
|
||||
pub fn set_bytes(&mut self, start: Address, values: &[u8]) {
|
||||
let start = start.to_usize();
|
||||
pub fn set_bytes(&mut self, start: u16, values: &[u8]) {
|
||||
let start = start as usize;
|
||||
|
||||
// This panics if the range is invalid
|
||||
let end = start + values.len();
|
||||
@ -96,8 +97,8 @@ impl Memory {
|
||||
self.bytes[start..end].copy_from_slice(values);
|
||||
}
|
||||
|
||||
pub fn is_stack_address(address: Address) -> bool {
|
||||
(STACK_ADDRESS_LO..=STACK_ADDRESS_HI).contains(&address)
|
||||
pub fn is_stack_address(address: u16) -> bool {
|
||||
address > 0xff && address < 0x200
|
||||
}
|
||||
}
|
||||
|
||||
@ -108,17 +109,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_memory_set_bytes() {
|
||||
let mut memory = Memory::new();
|
||||
memory.set_bytes(Address(0x0100), &[1, 2, 3, 4, 5]);
|
||||
assert_eq!(
|
||||
memory.get_slice(Address(0x00FF), AddressDiff(7)),
|
||||
&[0, 1, 2, 3, 4, 5, 0]
|
||||
);
|
||||
memory.set_bytes(0x0100, &[1, 2, 3, 4, 5]);
|
||||
assert_eq!(memory.get_slice(0x00FF, 7), &[0, 1, 2, 3, 4, 5, 0]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic]
|
||||
fn test_memory_overflow_panic() {
|
||||
let mut memory = Memory::new();
|
||||
memory.set_bytes(Address(0xFFFE), &[1, 2, 3]);
|
||||
memory.set_bytes(0xFFFE, &[1, 2, 3]);
|
||||
}
|
||||
}
|
||||
|
@ -25,9 +25,6 @@
|
||||
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
// POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
use crate::address::{Address, AddressDiff};
|
||||
use crate::memory::{STACK_ADDRESS_HI, STACK_ADDRESS_LO};
|
||||
|
||||
// Useful for constructing Status instances
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct StatusArgs {
|
||||
@ -147,8 +144,9 @@ impl Status {
|
||||
pub struct StackPointer(pub u8);
|
||||
|
||||
impl StackPointer {
|
||||
pub fn to_address(self) -> Address {
|
||||
STACK_ADDRESS_LO + AddressDiff(i32::from(self.0))
|
||||
pub fn to_u16(self) -> u16 {
|
||||
let StackPointer(val) = self;
|
||||
u16::from_le_bytes([val, 0x01])
|
||||
}
|
||||
|
||||
pub fn decrement(&mut self) {
|
||||
@ -166,7 +164,7 @@ pub struct Registers {
|
||||
pub index_x: i8,
|
||||
pub index_y: i8,
|
||||
pub stack_pointer: StackPointer,
|
||||
pub program_counter: Address,
|
||||
pub program_counter: u16,
|
||||
pub status: Status,
|
||||
}
|
||||
|
||||
@ -183,8 +181,8 @@ impl Registers {
|
||||
accumulator: 0,
|
||||
index_x: 0,
|
||||
index_y: 0,
|
||||
stack_pointer: StackPointer(STACK_ADDRESS_HI.get_offset()),
|
||||
program_counter: Address(0),
|
||||
stack_pointer: StackPointer(0),
|
||||
program_counter: 0,
|
||||
status: Status::default(),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user