2014-09-30 04:28:46 +00:00
|
|
|
// 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.
|
|
|
|
|
2021-04-07 11:55:41 +00:00
|
|
|
use crate::address::{Address, AddressDiff};
|
|
|
|
use crate::instruction::{self, DecodedInstr, Instruction, OpInput};
|
|
|
|
use crate::memory::Memory;
|
|
|
|
use crate::registers::{Registers, StackPointer, Status, StatusArgs};
|
2014-09-30 04:28:46 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[derive(Clone)]
|
2017-11-13 22:34:31 +00:00
|
|
|
pub struct CPU {
|
2014-09-30 04:28:46 +00:00
|
|
|
pub registers: Registers,
|
2017-08-01 07:55:45 +00:00
|
|
|
pub memory: Memory,
|
2014-09-30 04:28:46 +00:00
|
|
|
}
|
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
impl Default for CPU {
|
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-13 22:34:31 +00:00
|
|
|
impl CPU {
|
|
|
|
pub fn new() -> CPU {
|
|
|
|
CPU {
|
2017-08-01 07:55:45 +00:00
|
|
|
registers: Registers::new(),
|
|
|
|
memory: Memory::new(),
|
|
|
|
}
|
2014-09-30 04:28:46 +00:00
|
|
|
}
|
2014-10-20 01:37:43 +00:00
|
|
|
|
2014-09-30 04:28:46 +00:00
|
|
|
pub fn reset(&mut self) {
|
2017-11-13 22:34:31 +00:00
|
|
|
*self = CPU::new();
|
2014-09-30 04:28:46 +00:00
|
|
|
}
|
2014-10-01 22:02:45 +00:00
|
|
|
|
2014-10-04 20:33:42 +00:00
|
|
|
pub fn fetch_next_and_decode(&mut self) -> Option<DecodedInstr> {
|
|
|
|
let x: u8 = self.memory.get_byte(self.registers.program_counter);
|
|
|
|
|
2015-01-17 22:53:21 +00:00
|
|
|
match instruction::OPCODES[x as usize] {
|
2014-10-04 20:33:42 +00:00
|
|
|
Some((instr, am)) => {
|
|
|
|
let extra_bytes = am.extra_bytes();
|
|
|
|
let num_bytes = AddressDiff(1) + extra_bytes;
|
2014-10-01 22:19:28 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
let data_start = self.registers.program_counter + AddressDiff(1);
|
2014-10-01 22:02:45 +00:00
|
|
|
|
2014-10-04 20:33:42 +00:00
|
|
|
let slice = self.memory.get_slice(data_start, extra_bytes);
|
|
|
|
let am_out = am.process(self, slice);
|
|
|
|
|
|
|
|
// Increment program counter
|
2017-08-01 07:55:45 +00:00
|
|
|
self.registers.program_counter = self.registers.program_counter + num_bytes;
|
2014-10-04 20:33:42 +00:00
|
|
|
|
|
|
|
Some((instr, am_out))
|
|
|
|
}
|
2017-08-01 07:55:45 +00:00
|
|
|
_ => None,
|
2014-10-01 22:38:07 +00:00
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
pub fn execute_instruction(&mut self, decoded_instr: DecodedInstr) {
|
|
|
|
match decoded_instr {
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ADC, OpInput::UseImmediate(val)) => {
|
2014-10-25 22:42:48 +00:00
|
|
|
debug!("add with carry immediate: {}", val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.add_with_carry(val as i8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ADC, OpInput::UseAddress(addr)) => {
|
2014-10-04 20:33:42 +00:00
|
|
|
let val = self.memory.get_byte(addr) as i8;
|
2015-01-17 22:53:21 +00:00
|
|
|
debug!("add with carry. address: {:?}. value: {}", addr, val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.add_with_carry(val);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::AND, OpInput::UseImmediate(val)) => {
|
2014-11-07 00:17:38 +00:00
|
|
|
self.and(val as i8);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::AND, OpInput::UseAddress(addr)) => {
|
2014-11-07 00:17:38 +00:00
|
|
|
let val = self.memory.get_byte(addr) as i8;
|
|
|
|
self.and(val as i8);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ASL, OpInput::UseImplied) => {
|
2014-11-03 02:56:02 +00:00
|
|
|
// Accumulator mode
|
|
|
|
let mut val = self.registers.accumulator as u8;
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::shift_left_with_flags(&mut val, &mut self.registers.status);
|
2014-11-03 02:56:02 +00:00
|
|
|
self.registers.accumulator = val as i8;
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ASL, OpInput::UseAddress(addr)) => {
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::shift_left_with_flags(
|
2014-11-03 02:56:02 +00:00
|
|
|
self.memory.get_byte_mut_ref(addr),
|
2017-08-01 07:55:45 +00:00
|
|
|
&mut self.registers.status,
|
|
|
|
);
|
2014-11-03 02:56:02 +00:00
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BCC, OpInput::UseRelative(rel)) => {
|
2018-10-30 11:32:36 +00:00
|
|
|
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
2014-11-07 00:35:07 +00:00
|
|
|
self.branch_if_carry_clear(addr);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BCS, OpInput::UseRelative(rel)) => {
|
2018-10-30 11:32:36 +00:00
|
|
|
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
2014-11-07 00:42:08 +00:00
|
|
|
self.branch_if_carry_set(addr);
|
|
|
|
}
|
2014-11-07 00:35:07 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BEQ, OpInput::UseRelative(rel)) => {
|
2018-10-30 11:32:36 +00:00
|
|
|
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
2014-11-07 00:50:35 +00:00
|
|
|
self.branch_if_equal(addr);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BIT, OpInput::UseAddress(addr)) => {
|
2014-10-25 23:39:36 +00:00
|
|
|
let a: u8 = self.registers.accumulator as u8;
|
|
|
|
let m: u8 = self.memory.get_byte(addr);
|
|
|
|
let res = a & m;
|
|
|
|
|
|
|
|
// The zero flag is set based on the result of the 'and'.
|
|
|
|
let is_zero = 0 == res;
|
|
|
|
|
|
|
|
// The N flag is set to bit 7 of the byte from memory.
|
|
|
|
let bit7 = 0 != (0x80 & res);
|
|
|
|
|
|
|
|
// The V flag is set to bit 6 of the byte from memory.
|
|
|
|
let bit6 = 0 != (0x40 & res);
|
|
|
|
|
|
|
|
self.registers.status.set_with_mask(
|
2019-10-06 16:56:51 +00:00
|
|
|
Status::PS_ZERO | Status::PS_NEGATIVE | Status::PS_OVERFLOW,
|
2017-08-01 07:55:45 +00:00
|
|
|
Status::new(StatusArgs {
|
|
|
|
zero: is_zero,
|
|
|
|
negative: bit7,
|
|
|
|
overflow: bit6,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2014-10-25 23:39:36 +00:00
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BMI, OpInput::UseRelative(rel)) => {
|
2018-10-30 11:32:36 +00:00
|
|
|
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
2015-01-17 22:53:21 +00:00
|
|
|
debug!("branch if minus relative. address: {:?}", addr);
|
2014-10-25 22:30:05 +00:00
|
|
|
self.branch_if_minus(addr);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BPL, OpInput::UseRelative(rel)) => {
|
2018-10-30 11:32:36 +00:00
|
|
|
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
2014-11-07 01:04:03 +00:00
|
|
|
self.branch_if_positive(addr);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BVC, OpInput::UseRelative(rel)) => {
|
2018-10-30 11:32:36 +00:00
|
|
|
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
2014-11-13 23:43:48 +00:00
|
|
|
self.branch_if_overflow_clear(addr);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::BVS, OpInput::UseRelative(rel)) => {
|
2018-10-30 11:32:36 +00:00
|
|
|
let addr = self.registers.program_counter + AddressDiff(i32::from(rel));
|
2014-11-13 23:52:14 +00:00
|
|
|
self.branch_if_overflow_set(addr);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CLC, OpInput::UseImplied) => {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.and(!Status::PS_CARRY);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CLD, OpInput::UseImplied) => {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.and(!Status::PS_DECIMAL_MODE);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CLI, OpInput::UseImplied) => {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.and(!Status::PS_DISABLE_INTERRUPTS);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CLV, OpInput::UseImplied) => {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.and(!Status::PS_OVERFLOW);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-25 22:30:05 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CMP, OpInput::UseImmediate(val)) => {
|
2014-11-14 01:58:56 +00:00
|
|
|
self.compare_with_a_register(val);
|
2014-11-14 01:15:40 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CMP, OpInput::UseAddress(addr)) => {
|
2014-11-14 01:15:40 +00:00
|
|
|
let val = self.memory.get_byte(addr);
|
2014-11-14 01:58:56 +00:00
|
|
|
self.compare_with_a_register(val);
|
2014-11-14 01:15:40 +00:00
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CPX, OpInput::UseImmediate(val)) => {
|
2014-11-14 02:17:59 +00:00
|
|
|
self.compare_with_x_register(val);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CPX, OpInput::UseAddress(addr)) => {
|
2014-11-14 02:17:59 +00:00
|
|
|
let val = self.memory.get_byte(addr);
|
|
|
|
self.compare_with_x_register(val);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CPY, OpInput::UseImmediate(val)) => {
|
2014-11-14 02:17:59 +00:00
|
|
|
self.compare_with_y_register(val);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::CPY, OpInput::UseAddress(addr)) => {
|
2014-11-14 02:17:59 +00:00
|
|
|
let val = self.memory.get_byte(addr);
|
|
|
|
self.compare_with_y_register(val);
|
|
|
|
}
|
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
(Instruction::DEC, OpInput::UseAddress(addr)) => self.decrement_memory(addr),
|
2014-10-20 01:20:19 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::DEX, OpInput::UseImplied) => {
|
2014-10-08 03:52:38 +00:00
|
|
|
self.dec_x();
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-20 01:37:43 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::EOR, OpInput::UseImmediate(val)) => {
|
2014-11-17 02:14:23 +00:00
|
|
|
self.exclusive_or(val);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::EOR, OpInput::UseAddress(addr)) => {
|
2014-11-17 02:14:23 +00:00
|
|
|
let val = self.memory.get_byte(addr);
|
|
|
|
self.exclusive_or(val);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::INC, OpInput::UseAddress(addr)) => {
|
2017-08-01 07:55:45 +00:00
|
|
|
let m = self.memory.get_byte(addr);
|
|
|
|
let m = m + 1;
|
|
|
|
self.memory.set_byte(addr, m);
|
|
|
|
let i = m as i8;
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_flags_from_i8(&mut self.registers.status, i);
|
2014-11-02 20:40:34 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::INX, OpInput::UseImplied) => {
|
2014-11-02 20:40:34 +00:00
|
|
|
let x = self.registers.index_x + 1;
|
|
|
|
self.load_x_register(x);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::INY, OpInput::UseImplied) => {
|
2014-11-02 20:40:34 +00:00
|
|
|
let y = self.registers.index_y + 1;
|
|
|
|
self.load_y_register(y);
|
|
|
|
}
|
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
(Instruction::JMP, OpInput::UseAddress(addr)) => self.jump(addr),
|
2014-10-08 03:52:38 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LDA, OpInput::UseImmediate(val)) => {
|
2014-10-25 22:42:48 +00:00
|
|
|
debug!("load A immediate: {}", val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.load_accumulator(val as i8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LDA, OpInput::UseAddress(addr)) => {
|
2014-10-04 20:33:42 +00:00
|
|
|
let val = self.memory.get_byte(addr);
|
2015-01-17 22:53:21 +00:00
|
|
|
debug!("load A. address: {:?}. value: {}", addr, val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.load_accumulator(val as i8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LDX, OpInput::UseImmediate(val)) => {
|
2014-10-25 22:42:48 +00:00
|
|
|
debug!("load X immediate: {}", val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.load_x_register(val as i8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LDX, OpInput::UseAddress(addr)) => {
|
2014-10-04 20:33:42 +00:00
|
|
|
let val = self.memory.get_byte(addr);
|
2015-01-17 22:53:21 +00:00
|
|
|
debug!("load X. address: {:?}. value: {}", addr, val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.load_x_register(val as i8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LDY, OpInput::UseImmediate(val)) => {
|
2014-10-25 22:42:48 +00:00
|
|
|
debug!("load Y immediate: {}", val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.load_y_register(val as i8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LDY, OpInput::UseAddress(addr)) => {
|
2014-10-04 20:33:42 +00:00
|
|
|
let val = self.memory.get_byte(addr);
|
2015-01-17 22:53:21 +00:00
|
|
|
debug!("load Y. address: {:?}. value: {}", addr, val);
|
2014-10-04 20:33:42 +00:00
|
|
|
self.load_y_register(val as i8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LSR, OpInput::UseImplied) => {
|
2014-11-02 20:40:34 +00:00
|
|
|
// Accumulator mode
|
|
|
|
let mut val = self.registers.accumulator as u8;
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::shift_right_with_flags(&mut val, &mut self.registers.status);
|
2014-11-02 20:40:34 +00:00
|
|
|
self.registers.accumulator = val as i8;
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::LSR, OpInput::UseAddress(addr)) => {
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::shift_right_with_flags(
|
2014-11-02 20:40:34 +00:00
|
|
|
self.memory.get_byte_mut_ref(addr),
|
2017-08-01 07:55:45 +00:00
|
|
|
&mut self.registers.status,
|
|
|
|
);
|
2014-11-02 20:40:34 +00:00
|
|
|
}
|
|
|
|
|
2014-11-20 23:47:32 +00:00
|
|
|
(Instruction::ORA, OpInput::UseImmediate(val)) => {
|
|
|
|
self.inclusive_or(val);
|
|
|
|
}
|
|
|
|
(Instruction::ORA, OpInput::UseAddress(addr)) => {
|
|
|
|
let val = self.memory.get_byte(addr);
|
|
|
|
self.inclusive_or(val);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::PHA, OpInput::UseImplied) => {
|
2014-11-03 02:56:02 +00:00
|
|
|
// Push accumulator
|
|
|
|
let val = self.registers.accumulator as u8;
|
|
|
|
self.push_on_stack(val);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::PHP, OpInput::UseImplied) => {
|
2014-11-03 02:56:02 +00:00
|
|
|
// Push status
|
|
|
|
let val = self.registers.status.bits();
|
|
|
|
self.push_on_stack(val);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::PLA, OpInput::UseImplied) => {
|
2014-11-03 02:56:02 +00:00
|
|
|
// Pull accumulator
|
|
|
|
let val: u8 = self.pull_from_stack();
|
|
|
|
self.registers.accumulator = val as i8;
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::PLP, OpInput::UseImplied) => {
|
2014-11-03 02:56:02 +00:00
|
|
|
// Pull status
|
|
|
|
let val: u8 = self.pull_from_stack();
|
|
|
|
// The `truncate` here won't do anything because we have a
|
|
|
|
// constant for the single unused flags bit. This probably
|
|
|
|
// corresponds to the behavior of the 6502...? FIXME: verify
|
|
|
|
self.registers.status = Status::from_bits_truncate(val);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ROL, OpInput::UseImplied) => {
|
2014-11-03 02:56:02 +00:00
|
|
|
// Accumulator mode
|
|
|
|
let mut val = self.registers.accumulator as u8;
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::rotate_left_with_flags(&mut val, &mut self.registers.status);
|
2014-11-03 02:56:02 +00:00
|
|
|
self.registers.accumulator = val as i8;
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ROL, OpInput::UseAddress(addr)) => {
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::rotate_left_with_flags(
|
2014-11-03 02:56:02 +00:00
|
|
|
self.memory.get_byte_mut_ref(addr),
|
2017-08-01 07:55:45 +00:00
|
|
|
&mut self.registers.status,
|
|
|
|
);
|
2014-11-03 02:56:02 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ROR, OpInput::UseImplied) => {
|
2014-11-03 02:56:02 +00:00
|
|
|
// Accumulator mode
|
|
|
|
let mut val = self.registers.accumulator as u8;
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::rotate_right_with_flags(&mut val, &mut self.registers.status);
|
2014-11-03 02:56:02 +00:00
|
|
|
self.registers.accumulator = val as i8;
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::ROR, OpInput::UseAddress(addr)) => {
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::rotate_right_with_flags(
|
2014-11-03 02:56:02 +00:00
|
|
|
self.memory.get_byte_mut_ref(addr),
|
2017-08-01 07:55:45 +00:00
|
|
|
&mut self.registers.status,
|
|
|
|
);
|
2014-11-03 02:56:02 +00:00
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::SBC, OpInput::UseImmediate(val)) => {
|
2014-11-02 19:23:38 +00:00
|
|
|
debug!("subtract with carry immediate: {}", val);
|
|
|
|
self.subtract_with_carry(val as i8);
|
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::SBC, OpInput::UseAddress(addr)) => {
|
2014-11-02 19:23:38 +00:00
|
|
|
let val = self.memory.get_byte(addr) as i8;
|
2017-08-01 07:55:45 +00:00
|
|
|
debug!("subtract with carry. address: {:?}. value: {}", addr, val);
|
2014-11-02 19:23:38 +00:00
|
|
|
self.subtract_with_carry(val);
|
|
|
|
}
|
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::SEC, OpInput::UseImplied) => {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.or(Status::PS_CARRY);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::SED, OpInput::UseImplied) => {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.or(Status::PS_DECIMAL_MODE);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::SEI, OpInput::UseImplied) => {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.or(Status::PS_DISABLE_INTERRUPTS);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::STA, OpInput::UseAddress(addr)) => {
|
2014-10-25 22:30:05 +00:00
|
|
|
self.memory.set_byte(addr, self.registers.accumulator as u8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::STX, OpInput::UseAddress(addr)) => {
|
2014-10-25 22:30:05 +00:00
|
|
|
self.memory.set_byte(addr, self.registers.index_x as u8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::STY, OpInput::UseAddress(addr)) => {
|
2014-10-25 22:30:05 +00:00
|
|
|
self.memory.set_byte(addr, self.registers.index_y as u8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-25 22:30:05 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::TAX, OpInput::UseImplied) => {
|
2014-10-25 23:04:52 +00:00
|
|
|
let val = self.registers.accumulator;
|
|
|
|
self.load_x_register(val);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::TAY, OpInput::UseImplied) => {
|
2014-10-25 23:04:52 +00:00
|
|
|
let val = self.registers.accumulator;
|
|
|
|
self.load_y_register(val);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::TSX, OpInput::UseImplied) => {
|
2014-10-25 23:04:52 +00:00
|
|
|
let StackPointer(val) = self.registers.stack_pointer;
|
|
|
|
let val = val as i8;
|
|
|
|
self.load_x_register(val);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::TXA, OpInput::UseImplied) => {
|
2014-10-25 23:04:52 +00:00
|
|
|
let val = self.registers.index_x;
|
|
|
|
self.load_accumulator(val);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::TXS, OpInput::UseImplied) => {
|
2014-10-25 23:04:52 +00:00
|
|
|
// Note that this is the only 'transfer' instruction that does
|
|
|
|
// NOT set the zero and negative flags. (Because the target
|
|
|
|
// is the stack pointer)
|
|
|
|
let val = self.registers.index_x;
|
|
|
|
self.registers.stack_pointer = StackPointer(val as u8);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::TYA, OpInput::UseImplied) => {
|
2014-10-25 23:04:52 +00:00
|
|
|
let val = self.registers.index_y;
|
|
|
|
self.load_accumulator(val);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-25 23:04:52 +00:00
|
|
|
|
2014-11-20 23:33:23 +00:00
|
|
|
(Instruction::NOP, OpInput::UseImplied) => {
|
2014-11-02 19:23:38 +00:00
|
|
|
debug!("NOP instruction");
|
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
(_, _) => {
|
2017-08-01 07:55:45 +00:00
|
|
|
debug!(
|
|
|
|
"attempting to execute unimplemented or invalid \
|
2017-11-13 22:34:31 +00:00
|
|
|
instruction"
|
2017-08-01 07:55:45 +00:00
|
|
|
);
|
2014-11-02 19:23:38 +00:00
|
|
|
}
|
2014-10-01 22:02:45 +00:00
|
|
|
};
|
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
|
|
|
|
pub fn run(&mut self) {
|
2018-11-04 19:26:51 +00:00
|
|
|
while let Some(decoded_instr) = self.fetch_next_and_decode() {
|
|
|
|
self.execute_instruction(decoded_instr);
|
2014-10-04 20:47:42 +00:00
|
|
|
}
|
2014-10-04 20:33:42 +00:00
|
|
|
}
|
|
|
|
|
2014-11-02 20:40:34 +00:00
|
|
|
fn set_flags_from_i8(status: &mut Status, value: i8) {
|
2014-10-04 20:33:42 +00:00
|
|
|
let is_zero = value == 0;
|
|
|
|
let is_negative = value < 0;
|
|
|
|
|
|
|
|
status.set_with_mask(
|
2019-10-06 16:56:51 +00:00
|
|
|
Status::PS_ZERO | Status::PS_NEGATIVE,
|
2017-08-01 07:55:45 +00:00
|
|
|
Status::new(StatusArgs {
|
|
|
|
zero: is_zero,
|
|
|
|
negative: is_negative,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2014-10-04 20:33:42 +00:00
|
|
|
}
|
|
|
|
|
2014-11-03 02:56:02 +00:00
|
|
|
fn shift_left_with_flags(p_val: &mut u8, status: &mut Status) {
|
|
|
|
let mask = 1 << 7;
|
|
|
|
let is_bit_7_set = (*p_val & mask) == mask;
|
|
|
|
let shifted = (*p_val & !(1 << 7)) << 1;
|
|
|
|
*p_val = shifted;
|
|
|
|
status.set_with_mask(
|
2019-10-06 16:56:51 +00:00
|
|
|
Status::PS_CARRY,
|
2017-08-01 07:55:45 +00:00
|
|
|
Status::new(StatusArgs {
|
|
|
|
carry: is_bit_7_set,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_flags_from_i8(status, *p_val as i8);
|
2014-11-03 02:56:02 +00:00
|
|
|
}
|
|
|
|
|
2014-11-02 20:40:34 +00:00
|
|
|
fn shift_right_with_flags(p_val: &mut u8, status: &mut Status) {
|
2014-11-03 02:56:02 +00:00
|
|
|
let mask = 1;
|
|
|
|
let is_bit_0_set = (*p_val & mask) == mask;
|
2018-10-30 11:40:41 +00:00
|
|
|
*p_val >>= 1;
|
2014-11-02 20:40:34 +00:00
|
|
|
status.set_with_mask(
|
2019-10-06 16:56:51 +00:00
|
|
|
Status::PS_CARRY,
|
2017-08-01 07:55:45 +00:00
|
|
|
Status::new(StatusArgs {
|
|
|
|
carry: is_bit_0_set,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_flags_from_i8(status, *p_val as i8);
|
2014-11-03 02:56:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn rotate_left_with_flags(p_val: &mut u8, status: &mut Status) {
|
2019-10-06 16:56:51 +00:00
|
|
|
let is_carry_set = status.contains(Status::PS_CARRY);
|
2014-11-03 02:56:02 +00:00
|
|
|
let mask = 1 << 7;
|
|
|
|
let is_bit_7_set = (*p_val & mask) == mask;
|
|
|
|
let shifted = (*p_val & !(1 << 7)) << 1;
|
|
|
|
*p_val = shifted + if is_carry_set { 1 } else { 0 };
|
|
|
|
status.set_with_mask(
|
2019-10-06 16:56:51 +00:00
|
|
|
Status::PS_CARRY,
|
2017-08-01 07:55:45 +00:00
|
|
|
Status::new(StatusArgs {
|
|
|
|
carry: is_bit_7_set,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_flags_from_i8(status, *p_val as i8);
|
2014-11-03 02:56:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn rotate_right_with_flags(p_val: &mut u8, status: &mut Status) {
|
2019-10-06 16:56:51 +00:00
|
|
|
let is_carry_set = status.contains(Status::PS_CARRY);
|
2014-11-03 02:56:02 +00:00
|
|
|
let mask = 1;
|
|
|
|
let is_bit_0_set = (*p_val & mask) == mask;
|
|
|
|
let shifted = *p_val >> 1;
|
|
|
|
*p_val = shifted + if is_carry_set { 1 << 7 } else { 0 };
|
|
|
|
status.set_with_mask(
|
2019-10-06 16:56:51 +00:00
|
|
|
Status::PS_CARRY,
|
2017-08-01 07:55:45 +00:00
|
|
|
Status::new(StatusArgs {
|
|
|
|
carry: is_bit_0_set,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_flags_from_i8(status, *p_val as i8);
|
2014-11-02 20:40:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn set_i8_with_flags(mem: &mut i8, status: &mut Status, value: i8) {
|
|
|
|
*mem = value;
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_flags_from_i8(status, value);
|
2014-11-02 20:40:34 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 22:40:20 +00:00
|
|
|
fn load_x_register(&mut self, value: i8) {
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_i8_with_flags(
|
2017-08-01 07:55:45 +00:00
|
|
|
&mut self.registers.index_x,
|
|
|
|
&mut self.registers.status,
|
|
|
|
value,
|
|
|
|
);
|
2014-10-04 20:33:42 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 22:40:20 +00:00
|
|
|
fn load_y_register(&mut self, value: i8) {
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_i8_with_flags(
|
2017-08-01 07:55:45 +00:00
|
|
|
&mut self.registers.index_y,
|
|
|
|
&mut self.registers.status,
|
|
|
|
value,
|
|
|
|
);
|
2014-10-04 20:33:42 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 22:40:20 +00:00
|
|
|
fn load_accumulator(&mut self, value: i8) {
|
2017-11-13 22:34:31 +00:00
|
|
|
CPU::set_i8_with_flags(
|
2017-08-01 07:55:45 +00:00
|
|
|
&mut self.registers.accumulator,
|
|
|
|
&mut self.registers.status,
|
|
|
|
value,
|
|
|
|
);
|
2014-10-04 20:33:42 +00:00
|
|
|
}
|
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
fn add_with_carry(&mut self, value: i8) {
|
|
|
|
let a_before: i8 = self.registers.accumulator;
|
|
|
|
let c_before: i8 = if self.registers.status.contains(Status::PS_CARRY) {
|
|
|
|
1
|
|
|
|
} else {
|
|
|
|
0
|
|
|
|
};
|
|
|
|
let a_after: i8 = a_before.wrapping_add(c_before).wrapping_add(value);
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
debug_assert_eq!(
|
|
|
|
a_after as u8,
|
|
|
|
a_before.wrapping_add(c_before).wrapping_add(value) as u8
|
|
|
|
);
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let bcd1: i8 = if (a_after & 0x0f) as u8 > 0x09 {
|
|
|
|
0x06
|
|
|
|
} else {
|
|
|
|
0x00
|
|
|
|
};
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let bcd2: i8 = if (a_after.wrapping_add(bcd1) as u8 & 0xf0) as u8 > 0x90 {
|
|
|
|
0x60
|
|
|
|
} else {
|
|
|
|
0x00
|
|
|
|
};
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let result: i8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) {
|
|
|
|
a_after.wrapping_add(bcd1).wrapping_add(bcd2)
|
|
|
|
} else {
|
|
|
|
a_after
|
|
|
|
};
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2022-06-07 09:03:51 +00:00
|
|
|
let did_carry = (result as u8) < (a_before as u8) || (c_before == 1 && value == -1);
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let did_overflow = (a_before < 0 && value < 0 && a_after >= 0)
|
|
|
|
|| (a_before > 0 && value > 0 && a_after <= 0);
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let mask = Status::PS_CARRY | Status::PS_OVERFLOW;
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
self.registers.status.set_with_mask(
|
|
|
|
mask,
|
|
|
|
Status::new(StatusArgs {
|
|
|
|
carry: did_carry,
|
|
|
|
overflow: did_overflow,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
self.load_accumulator(result);
|
2021-01-25 22:57:28 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
debug!("accumulator: {}", self.registers.accumulator);
|
|
|
|
}
|
2014-11-02 19:23:38 +00:00
|
|
|
|
2014-11-07 00:07:54 +00:00
|
|
|
fn and(&mut self, value: i8) {
|
|
|
|
let a_after = self.registers.accumulator & value;
|
|
|
|
self.load_accumulator(a_after);
|
|
|
|
}
|
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
fn subtract_with_carry(&mut self, value: i8) {
|
|
|
|
// A - M - (1 - C)
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
// nc -- 'not carry'
|
|
|
|
let nc: i8 = if self.registers.status.contains(Status::PS_CARRY) {
|
|
|
|
0
|
|
|
|
} else {
|
|
|
|
1
|
|
|
|
};
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let a_before: i8 = self.registers.accumulator;
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let a_after = a_before.wrapping_sub(value).wrapping_sub(nc);
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
// The overflow flag is set on two's-complement overflow.
|
|
|
|
//
|
|
|
|
// range of A is -128 to 127
|
|
|
|
// range of - M - (1 - C) is -128 to 128
|
|
|
|
// -(127 + 1) to -(-128 + 0)
|
|
|
|
//
|
|
|
|
let over =
|
|
|
|
((nc == 0 && value < 0) || (nc == 1 && value < -1)) && a_before >= 0 && a_after < 0;
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let under = (a_before < 0) && (-value - nc < 0) && a_after >= 0;
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let did_overflow = over || under;
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let mask = Status::PS_CARRY | Status::PS_OVERFLOW;
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let bcd1: i8 = if (a_before & 0x0f).wrapping_sub(nc) < (value & 0x0f) {
|
|
|
|
0x06
|
|
|
|
} else {
|
|
|
|
0x00
|
|
|
|
};
|
2021-01-27 22:44:20 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let bcd2: i8 = if (a_after.wrapping_sub(bcd1) as u8 & 0xf0) as u8 > 0x90 {
|
|
|
|
0x60
|
|
|
|
} else {
|
|
|
|
0x00
|
|
|
|
};
|
2021-01-27 22:44:20 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
let result: i8 = if self.registers.status.contains(Status::PS_DECIMAL_MODE) {
|
|
|
|
a_after.wrapping_sub(bcd1).wrapping_sub(bcd2)
|
|
|
|
} else {
|
|
|
|
a_after
|
|
|
|
};
|
2021-01-27 22:44:20 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
// The carry flag is set on unsigned overflow.
|
|
|
|
let did_carry = (result as u8) > (a_before as u8);
|
2021-01-27 20:31:36 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
self.registers.status.set_with_mask(
|
|
|
|
mask,
|
|
|
|
Status::new(StatusArgs {
|
|
|
|
carry: did_carry,
|
|
|
|
overflow: did_overflow,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2021-01-27 22:44:20 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
self.load_accumulator(result);
|
|
|
|
}
|
2014-10-08 03:52:38 +00:00
|
|
|
|
2014-10-25 22:40:20 +00:00
|
|
|
fn decrement_memory(&mut self, addr: Address) {
|
2017-08-01 07:55:45 +00:00
|
|
|
let value_new = self.memory.get_byte(addr).wrapping_sub(1);
|
2014-10-20 01:20:19 +00:00
|
|
|
|
|
|
|
self.memory.set_byte(addr, value_new);
|
|
|
|
|
|
|
|
let is_negative = (value_new as i8) < 0;
|
2017-08-01 07:55:45 +00:00
|
|
|
let is_zero = value_new == 0;
|
2014-10-20 01:20:19 +00:00
|
|
|
|
|
|
|
self.registers.status.set_with_mask(
|
2019-10-06 16:56:51 +00:00
|
|
|
Status::PS_NEGATIVE | Status::PS_ZERO,
|
2017-08-01 07:55:45 +00:00
|
|
|
Status::new(StatusArgs {
|
|
|
|
negative: is_negative,
|
|
|
|
zero: is_zero,
|
|
|
|
..StatusArgs::none()
|
|
|
|
}),
|
|
|
|
);
|
2014-10-20 01:20:19 +00:00
|
|
|
}
|
|
|
|
|
2014-10-25 22:40:20 +00:00
|
|
|
fn dec_x(&mut self) {
|
2014-10-17 00:05:41 +00:00
|
|
|
let val = self.registers.index_x;
|
|
|
|
self.load_x_register(val - 1);
|
2014-10-08 03:52:38 +00:00
|
|
|
}
|
2014-10-20 01:37:43 +00:00
|
|
|
|
2014-10-25 22:40:20 +00:00
|
|
|
fn jump(&mut self, addr: Address) {
|
2014-10-20 01:37:43 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
2014-10-25 21:13:40 +00:00
|
|
|
|
2014-11-07 00:32:15 +00:00
|
|
|
fn branch_if_carry_clear(&mut self, addr: Address) {
|
2019-10-06 16:56:51 +00:00
|
|
|
if !self.registers.status.contains(Status::PS_CARRY) {
|
2014-11-07 00:32:15 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-07 00:39:46 +00:00
|
|
|
fn branch_if_carry_set(&mut self, addr: Address) {
|
2019-10-06 16:56:51 +00:00
|
|
|
if self.registers.status.contains(Status::PS_CARRY) {
|
2014-11-07 00:39:46 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-07 00:49:54 +00:00
|
|
|
fn branch_if_equal(&mut self, addr: Address) {
|
2019-10-06 16:56:51 +00:00
|
|
|
if self.registers.status.contains(Status::PS_ZERO) {
|
2014-11-07 00:49:54 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-10-25 22:40:20 +00:00
|
|
|
fn branch_if_minus(&mut self, addr: Address) {
|
2019-10-06 16:56:51 +00:00
|
|
|
if self.registers.status.contains(Status::PS_NEGATIVE) {
|
2014-11-07 01:02:40 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn branch_if_positive(&mut self, addr: Address) {
|
2019-10-06 16:56:51 +00:00
|
|
|
if !self.registers.status.contains(Status::PS_NEGATIVE) {
|
2014-10-13 23:39:17 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
|
|
|
}
|
2014-11-03 02:56:02 +00:00
|
|
|
|
2014-11-13 23:42:07 +00:00
|
|
|
fn branch_if_overflow_clear(&mut self, addr: Address) {
|
2019-10-06 16:56:51 +00:00
|
|
|
if !self.registers.status.contains(Status::PS_OVERFLOW) {
|
2014-11-13 23:42:07 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-13 23:50:55 +00:00
|
|
|
fn branch_if_overflow_set(&mut self, addr: Address) {
|
2019-10-06 16:56:51 +00:00
|
|
|
if self.registers.status.contains(Status::PS_OVERFLOW) {
|
2014-11-13 23:50:55 +00:00
|
|
|
self.registers.program_counter = addr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-14 01:08:56 +00:00
|
|
|
// From http://www.6502.org/tutorials/compare_beyond.html:
|
|
|
|
// If the Z flag is 0, then A <> NUM and BNE will branch
|
|
|
|
// If the Z flag is 1, then A = NUM and BEQ will branch
|
|
|
|
// If the C flag is 0, then A (unsigned) < NUM (unsigned) and BCC will branch
|
|
|
|
// If the C flag is 1, then A (unsigned) >= NUM (unsigned) and BCS will branch
|
|
|
|
// ...
|
2017-08-01 07:55:45 +00:00
|
|
|
// The N flag contains most significant bit of the subtraction result.
|
2014-11-14 01:58:56 +00:00
|
|
|
fn compare(&mut self, r: i8, val: u8) {
|
|
|
|
if r as u8 >= val as u8 {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.insert(Status::PS_CARRY);
|
2014-11-14 01:08:56 +00:00
|
|
|
} else {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.remove(Status::PS_CARRY);
|
2014-11-14 01:08:56 +00:00
|
|
|
}
|
|
|
|
|
2014-11-14 01:58:56 +00:00
|
|
|
if r as i8 == val as i8 {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.insert(Status::PS_ZERO);
|
2014-11-14 01:08:56 +00:00
|
|
|
} else {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.remove(Status::PS_ZERO);
|
2014-11-14 01:08:56 +00:00
|
|
|
}
|
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
let diff: i8 = r.wrapping_sub(val as i8);
|
2014-11-14 01:08:56 +00:00
|
|
|
if diff < 0 {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.insert(Status::PS_NEGATIVE);
|
2014-11-14 01:08:56 +00:00
|
|
|
} else {
|
2019-10-06 16:56:51 +00:00
|
|
|
self.registers.status.remove(Status::PS_NEGATIVE);
|
2014-11-14 01:08:56 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-14 01:58:56 +00:00
|
|
|
fn compare_with_a_register(&mut self, val: u8) {
|
|
|
|
let a = self.registers.accumulator;
|
|
|
|
self.compare(a, val);
|
|
|
|
}
|
|
|
|
|
2014-11-14 02:17:59 +00:00
|
|
|
fn compare_with_x_register(&mut self, val: u8) {
|
|
|
|
debug!("compare_with_x_register");
|
|
|
|
|
|
|
|
let x = self.registers.index_x;
|
|
|
|
self.compare(x, val);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn compare_with_y_register(&mut self, val: u8) {
|
|
|
|
let y = self.registers.index_y;
|
|
|
|
self.compare(y, val);
|
|
|
|
}
|
|
|
|
|
2014-11-17 02:20:42 +00:00
|
|
|
fn exclusive_or(&mut self, val: u8) {
|
|
|
|
let a_after = self.registers.accumulator ^ (val as i8);
|
2014-11-20 23:48:42 +00:00
|
|
|
self.load_accumulator(a_after);
|
|
|
|
}
|
|
|
|
|
|
|
|
fn inclusive_or(&mut self, val: u8) {
|
|
|
|
let a_after = self.registers.accumulator | (val as i8);
|
2014-11-17 02:20:42 +00:00
|
|
|
self.load_accumulator(a_after);
|
|
|
|
}
|
|
|
|
|
2014-11-03 02:56:02 +00:00
|
|
|
fn push_on_stack(&mut self, val: u8) {
|
|
|
|
let addr = self.registers.stack_pointer.to_address();
|
|
|
|
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 out = self.memory.get_byte(addr);
|
|
|
|
self.registers.stack_pointer.increment();
|
|
|
|
out
|
|
|
|
}
|
2014-09-30 04:28:46 +00:00
|
|
|
}
|
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
impl core::fmt::Debug for CPU {
|
|
|
|
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
|
2017-08-01 07:55:45 +00:00
|
|
|
write!(
|
|
|
|
f,
|
2017-11-13 22:34:31 +00:00
|
|
|
"CPU Dump:\n\nAccumulator: {}",
|
2017-08-01 07:55:45 +00:00
|
|
|
self.registers.accumulator
|
|
|
|
)
|
2014-10-01 22:07:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
2014-10-08 03:52:38 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
use super::*;
|
|
|
|
use num::range_inclusive;
|
2014-11-06 23:58:09 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
#[test]
|
|
|
|
fn decimal_add_test() {
|
|
|
|
let mut cpu = CPU::new();
|
|
|
|
cpu.registers.status.or(Status::PS_DECIMAL_MODE);
|
|
|
|
|
|
|
|
cpu.add_with_carry(0x09);
|
2021-01-26 20:58:09 +00:00
|
|
|
assert_eq!(cpu.registers.accumulator, 0x09);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2021-01-26 20:58:09 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
cpu.add_with_carry(0x43);
|
2021-01-26 20:58:09 +00:00
|
|
|
assert_eq!(cpu.registers.accumulator, 0x52);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2021-01-26 20:58:09 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
cpu.add_with_carry(0x48);
|
2021-01-26 20:58:09 +00:00
|
|
|
assert_eq!(cpu.registers.accumulator, 0x00);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2021-01-30 15:10:41 +00:00
|
|
|
}
|
2021-01-27 22:44:20 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
#[test]
|
|
|
|
fn decimal_subtract_test() {
|
|
|
|
let mut cpu = CPU::new();
|
|
|
|
cpu.registers
|
|
|
|
.status
|
|
|
|
.or(Status::PS_DECIMAL_MODE | Status::PS_CARRY);
|
|
|
|
cpu.registers.accumulator = 0;
|
|
|
|
|
|
|
|
cpu.subtract_with_carry(0x48);
|
2021-01-27 22:44:20 +00:00
|
|
|
assert_eq!(cpu.registers.accumulator as u8, 0x52);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2021-01-27 22:44:20 +00:00
|
|
|
|
2021-01-30 15:10:41 +00:00
|
|
|
cpu.subtract_with_carry(0x43);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2021-01-30 15:10:41 +00:00
|
|
|
}
|
2021-01-26 20:58:09 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn add_with_carry_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
|
|
|
|
|
|
|
cpu.add_with_carry(1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 1);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.add_with_carry(-1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.add_with_carry(1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 2);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
let mut cpu = CPU::new();
|
|
|
|
|
|
|
|
cpu.add_with_carry(127);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 127);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.add_with_carry(-127);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.remove(Status::PS_CARRY);
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.add_with_carry(-128);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -128);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.add_with_carry(127);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -1);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
let mut cpu = CPU::new();
|
|
|
|
|
|
|
|
cpu.add_with_carry(127);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 127);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.add_with_carry(1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -128);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2022-06-07 09:03:51 +00:00
|
|
|
|
|
|
|
let mut cpu = CPU::new();
|
|
|
|
cpu.registers.status.or(Status::PS_CARRY);
|
|
|
|
cpu.add_with_carry(-1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-10-25 21:13:40 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn and_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
|
|
|
|
|
|
|
cpu.registers.accumulator = 0;
|
|
|
|
cpu.and(-1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.registers.accumulator = -1;
|
|
|
|
cpu.and(0);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.registers.accumulator = -1;
|
|
|
|
cpu.and(0x0f);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0x0f);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.registers.accumulator = -1;
|
|
|
|
cpu.and(-128);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -128);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-07 00:30:05 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn subtract_with_carry_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
|
|
|
cpu.registers.accumulator = 0;
|
|
|
|
|
|
|
|
cpu.subtract_with_carry(1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -1);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
|
|
|
cpu.registers.accumulator = -128;
|
|
|
|
cpu.subtract_with_carry(1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 127);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
|
|
|
cpu.registers.accumulator = 127;
|
|
|
|
cpu.subtract_with_carry(-1);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -128);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
|
|
|
cpu.registers.accumulator = -64;
|
|
|
|
cpu.subtract_with_carry(64);
|
|
|
|
assert_eq!(cpu.registers.accumulator, 127);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
|
|
|
cpu.registers.accumulator = 0;
|
|
|
|
cpu.subtract_with_carry(-128);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -128);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
|
|
|
cpu.registers.accumulator = 0;
|
|
|
|
cpu.subtract_with_carry(127);
|
|
|
|
assert_eq!(cpu.registers.accumulator, -128);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn decrement_memory_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2017-08-01 07:55:45 +00:00
|
|
|
let addr = Address(0xA1B2);
|
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.memory.set_byte(addr, 5);
|
|
|
|
|
|
|
|
cpu.decrement_memory(addr);
|
|
|
|
assert_eq!(cpu.memory.get_byte(addr), 4);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.decrement_memory(addr);
|
|
|
|
assert_eq!(cpu.memory.get_byte(addr), 3);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.decrement_memory(addr);
|
|
|
|
cpu.decrement_memory(addr);
|
|
|
|
cpu.decrement_memory(addr);
|
|
|
|
assert_eq!(cpu.memory.get_byte(addr), 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.decrement_memory(addr);
|
|
|
|
assert_eq!(cpu.memory.get_byte(addr) as i8, -1);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-07 00:38:56 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn logical_shift_right_test() {
|
|
|
|
// Testing UseImplied version (which targets the accumulator) only, for now
|
2014-11-07 00:38:56 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
|
|
|
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(0)));
|
|
|
|
cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied));
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(1)));
|
|
|
|
cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied));
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(255)));
|
|
|
|
cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied));
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0x7F);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(254)));
|
|
|
|
cpu.execute_instruction((Instruction::LSR, OpInput::UseImplied));
|
|
|
|
assert_eq!(cpu.registers.accumulator, 0x7F);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-07 00:38:56 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn dec_x_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
|
|
|
|
|
|
|
cpu.dec_x();
|
|
|
|
assert_eq!(cpu.registers.index_x, -1);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.dec_x();
|
|
|
|
assert_eq!(cpu.registers.index_x, -2);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.load_x_register(5);
|
|
|
|
cpu.dec_x();
|
|
|
|
assert_eq!(cpu.registers.index_x, 4);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.dec_x();
|
|
|
|
cpu.dec_x();
|
|
|
|
cpu.dec_x();
|
|
|
|
cpu.dec_x();
|
|
|
|
|
|
|
|
assert_eq!(cpu.registers.index_x, 0);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2018-11-04 19:26:51 +00:00
|
|
|
|
|
|
|
cpu.dec_x();
|
|
|
|
assert_eq!(cpu.registers.index_x, -1);
|
2022-06-07 17:52:00 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_OVERFLOW));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-07 00:48:48 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn jump_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2017-08-01 07:55:45 +00:00
|
|
|
let addr = Address(0xA1B1);
|
2014-11-07 00:48:48 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.jump(addr);
|
|
|
|
assert_eq!(cpu.registers.program_counter, addr);
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn branch_if_carry_clear_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-10-13 23:39:17 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
|
|
|
cpu.branch_if_carry_clear(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0));
|
2017-08-01 07:55:45 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
|
|
|
cpu.branch_if_carry_clear(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
2014-10-13 23:39:17 +00:00
|
|
|
}
|
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn branch_if_carry_set_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-10-13 23:39:17 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((Instruction::CLC, OpInput::UseImplied));
|
|
|
|
cpu.branch_if_carry_set(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0));
|
2014-10-13 23:39:17 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((Instruction::SEC, OpInput::UseImplied));
|
|
|
|
cpu.branch_if_carry_set(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
2014-10-13 23:39:17 +00:00
|
|
|
}
|
2014-11-07 01:00:09 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn branch_if_equal_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-11-07 01:00:09 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_equal(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0));
|
2014-11-07 01:00:09 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.or(Status::PS_ZERO);
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_equal(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-13 23:27:58 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn branch_if_minus_test() {
|
|
|
|
{
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
|
|
|
let registers_before = cpu.registers;
|
2014-11-13 23:27:58 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_minus(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers, registers_before);
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-13 23:27:58 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
{
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-11-13 23:49:43 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.or(Status::PS_NEGATIVE);
|
2018-11-04 19:26:51 +00:00
|
|
|
let registers_before = cpu.registers;
|
2014-11-13 23:49:43 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_minus(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.status, registers_before.status);
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
|
|
|
}
|
2014-11-13 23:49:43 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn branch_if_positive_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.insert(Status::PS_NEGATIVE);
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_positive(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0));
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.remove(Status::PS_NEGATIVE);
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_positive(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn branch_if_overflow_clear_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_overflow_clear(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0));
|
2014-11-14 01:08:56 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.remove(Status::PS_OVERFLOW);
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_overflow_clear(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn branch_if_overflow_set_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_overflow_set(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0));
|
2014-11-14 01:08:56 +00:00
|
|
|
|
2019-10-06 16:56:51 +00:00
|
|
|
cpu.registers.status.insert(Status::PS_OVERFLOW);
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.branch_if_overflow_set(Address(0xABCD));
|
|
|
|
assert_eq!(cpu.registers.program_counter, Address(0xABCD));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
fn compare_test_helper<F>(compare: &mut F, load_instruction: Instruction)
|
|
|
|
where
|
2017-11-13 22:34:31 +00:00
|
|
|
F: FnMut(&mut CPU, u8),
|
2017-08-01 07:55:45 +00:00
|
|
|
{
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-11-14 01:08:56 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127)));
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
compare(&mut cpu, 127);
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2014-11-14 01:08:56 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127)));
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
compare(&mut cpu, 1);
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2014-11-14 01:08:56 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(1)));
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
compare(&mut cpu, 2);
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2014-11-14 00:52:10 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(20)));
|
2014-11-14 01:58:56 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
compare(&mut cpu, -50i8 as u8);
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2014-11-14 02:17:59 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(1)));
|
2014-11-14 02:17:59 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
compare(&mut cpu, -1i8 as u8);
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2014-11-17 02:11:22 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((load_instruction, OpInput::UseImmediate(127)));
|
2014-11-17 02:11:22 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
compare(&mut cpu, -128i8 as u8);
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_CARRY));
|
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-17 02:11:22 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn compare_with_a_register_test() {
|
|
|
|
compare_test_helper(
|
2018-11-04 19:26:51 +00:00
|
|
|
&mut |cpu: &mut CPU, val: u8| {
|
|
|
|
cpu.compare_with_a_register(val);
|
2017-11-13 22:34:31 +00:00
|
|
|
},
|
2017-08-01 07:55:45 +00:00
|
|
|
Instruction::LDA,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn compare_with_x_register_test() {
|
|
|
|
compare_test_helper(
|
2018-11-04 19:26:51 +00:00
|
|
|
&mut |cpu: &mut CPU, val: u8| {
|
|
|
|
cpu.compare_with_x_register(val);
|
2017-11-13 22:34:31 +00:00
|
|
|
},
|
2017-08-01 07:55:45 +00:00
|
|
|
Instruction::LDX,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn compare_with_y_register_test() {
|
|
|
|
compare_test_helper(
|
2018-11-04 19:26:51 +00:00
|
|
|
&mut |cpu: &mut CPU, val: u8| {
|
|
|
|
cpu.compare_with_y_register(val);
|
2017-11-13 22:34:31 +00:00
|
|
|
},
|
2017-08-01 07:55:45 +00:00
|
|
|
Instruction::LDY,
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn exclusive_or_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2017-08-01 07:55:45 +00:00
|
|
|
|
|
|
|
for a_before in range_inclusive(0u8, 255u8) {
|
|
|
|
for val in range_inclusive(0u8, 255u8) {
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(a_before)));
|
2017-08-01 07:55:45 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.exclusive_or(val);
|
2017-08-01 07:55:45 +00:00
|
|
|
|
|
|
|
let a_after = a_before ^ val;
|
2018-11-04 19:26:51 +00:00
|
|
|
assert_eq!(cpu.registers.accumulator, a_after as i8);
|
2017-08-01 07:55:45 +00:00
|
|
|
|
|
|
|
if a_after == 0 {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
2017-08-01 07:55:45 +00:00
|
|
|
} else {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (a_after as i8) < 0 {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2017-08-01 07:55:45 +00:00
|
|
|
} else {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-17 02:11:22 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-11-20 23:34:07 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
#[test]
|
|
|
|
fn inclusive_or_test() {
|
2018-11-04 19:26:51 +00:00
|
|
|
let mut cpu = CPU::new();
|
2014-11-20 23:34:07 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
for a_before in range_inclusive(0u8, 255u8) {
|
|
|
|
for val in range_inclusive(0u8, 255u8) {
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.execute_instruction((Instruction::LDA, OpInput::UseImmediate(a_before)));
|
2014-11-20 23:34:07 +00:00
|
|
|
|
2018-11-04 19:26:51 +00:00
|
|
|
cpu.inclusive_or(val);
|
2014-11-20 23:34:07 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
let a_after = a_before | val;
|
2018-11-04 19:26:51 +00:00
|
|
|
assert_eq!(cpu.registers.accumulator, a_after as i8);
|
2014-11-20 23:34:07 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
if a_after == 0 {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_ZERO));
|
2017-08-01 07:55:45 +00:00
|
|
|
} else {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_ZERO));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-20 23:34:07 +00:00
|
|
|
|
2017-08-01 07:55:45 +00:00
|
|
|
if (a_after as i8) < 0 {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2017-08-01 07:55:45 +00:00
|
|
|
} else {
|
2019-10-06 16:56:51 +00:00
|
|
|
assert!(!cpu.registers.status.contains(Status::PS_NEGATIVE));
|
2017-08-01 07:55:45 +00:00
|
|
|
}
|
2014-11-20 23:34:07 +00:00
|
|
|
}
|
|
|
|
}
|
2022-06-07 17:30:29 +00:00
|
|
|
}
|
2021-01-30 15:10:41 +00:00
|
|
|
|
2022-06-07 17:30:29 +00:00
|
|
|
#[test]
|
|
|
|
fn stack_underflow() {
|
|
|
|
let mut cpu = CPU::new();
|
|
|
|
let _val: u8 = cpu.pull_from_stack();
|
2014-11-20 23:34:07 +00:00
|
|
|
}
|
|
|
|
}
|