1
0
mirror of https://github.com/mre/mos6502.git synced 2024-11-25 02:33:26 +00:00

Implement subtract-with-carry and flag setting and clearing instructions

This commit is contained in:
Johannes Muenzel 2014-11-02 14:23:38 -05:00
parent 06a0367fd6
commit 8545ea5597
2 changed files with 201 additions and 54 deletions

View File

@ -32,7 +32,8 @@ use instruction;
use instruction::{DecodedInstr}; use instruction::{DecodedInstr};
use memory::Memory; use memory::Memory;
use registers::{ Registers, StackPointer, Status, StatusArgs }; use registers::{ Registers, StackPointer, Status, StatusArgs };
use registers::{ PS_NEGATIVE, PS_OVERFLOW, PS_ZERO, PS_CARRY }; use registers::{ PS_NEGATIVE, PS_DECIMAL_MODE, PS_OVERFLOW, PS_ZERO, PS_CARRY,
PS_DISABLE_INTERRUPTS };
pub struct Machine { pub struct Machine {
pub registers: Registers, pub registers: Registers,
@ -80,12 +81,12 @@ impl Machine {
(instruction::ADC, instruction::UseImmediate(val)) => { (instruction::ADC, instruction::UseImmediate(val)) => {
debug!("add with carry immediate: {}", val); debug!("add with carry immediate: {}", val);
self.add_with_carry(val as i8); self.add_with_carry(val as i8);
}, }
(instruction::ADC, instruction::UseAddress(addr)) => { (instruction::ADC, instruction::UseAddress(addr)) => {
let val = self.memory.get_byte(addr) as i8; let val = self.memory.get_byte(addr) as i8;
debug!("add with carry. address: {}. value: {}", addr, val); debug!("add with carry. address: {}. value: {}", addr, val);
self.add_with_carry(val); self.add_with_carry(val);
}, }
(instruction::BIT, instruction::UseAddress(addr)) => { (instruction::BIT, instruction::UseAddress(addr)) => {
let a: u8 = self.registers.accumulator as u8; let a: u8 = self.registers.accumulator as u8;
@ -114,7 +115,20 @@ impl Machine {
+ AddressDiff(rel as i32); + AddressDiff(rel as i32);
debug!("branch if minus relative. address: {}", addr); debug!("branch if minus relative. address: {}", addr);
self.branch_if_minus(addr); self.branch_if_minus(addr);
}, }
(instruction::CLC, instruction::UseImplied) => {
self.registers.status.and(!PS_CARRY);
}
(instruction::CLD, instruction::UseImplied) => {
self.registers.status.and(!PS_DECIMAL_MODE);
}
(instruction::CLI, instruction::UseImplied) => {
self.registers.status.and(!PS_DISABLE_INTERRUPTS);
}
(instruction::CLV, instruction::UseImplied) => {
self.registers.status.and(!PS_OVERFLOW);
}
(instruction::DEC, instruction::UseAddress(addr)) => { (instruction::DEC, instruction::UseAddress(addr)) => {
self.decrement_memory(addr) self.decrement_memory(addr)
@ -122,87 +136,109 @@ impl Machine {
(instruction::DEX, instruction::UseImplied) => { (instruction::DEX, instruction::UseImplied) => {
self.dec_x(); self.dec_x();
}, }
(instruction::JMP, instruction::UseAddress(addr)) => { (instruction::JMP, instruction::UseAddress(addr)) => {
self.jump(addr) self.jump(addr)
}, }
(instruction::LDA, instruction::UseImmediate(val)) => { (instruction::LDA, instruction::UseImmediate(val)) => {
debug!("load A immediate: {}", val); debug!("load A immediate: {}", val);
self.load_accumulator(val as i8); self.load_accumulator(val as i8);
}, }
(instruction::LDA, instruction::UseAddress(addr)) => { (instruction::LDA, instruction::UseAddress(addr)) => {
let val = self.memory.get_byte(addr); let val = self.memory.get_byte(addr);
debug!("load A. address: {}. value: {}", addr, val); debug!("load A. address: {}. value: {}", addr, val);
self.load_accumulator(val as i8); self.load_accumulator(val as i8);
}, }
(instruction::LDX, instruction::UseImmediate(val)) => { (instruction::LDX, instruction::UseImmediate(val)) => {
debug!("load X immediate: {}", val); debug!("load X immediate: {}", val);
self.load_x_register(val as i8); self.load_x_register(val as i8);
}, }
(instruction::LDX, instruction::UseAddress(addr)) => { (instruction::LDX, instruction::UseAddress(addr)) => {
let val = self.memory.get_byte(addr); let val = self.memory.get_byte(addr);
debug!("load X. address: {}. value: {}", addr, val); debug!("load X. address: {}. value: {}", addr, val);
self.load_x_register(val as i8); self.load_x_register(val as i8);
}, }
(instruction::LDY, instruction::UseImmediate(val)) => { (instruction::LDY, instruction::UseImmediate(val)) => {
debug!("load Y immediate: {}", val); debug!("load Y immediate: {}", val);
self.load_y_register(val as i8); self.load_y_register(val as i8);
}, }
(instruction::LDY, instruction::UseAddress(addr)) => { (instruction::LDY, instruction::UseAddress(addr)) => {
let val = self.memory.get_byte(addr); let val = self.memory.get_byte(addr);
debug!("load Y. address: {}. value: {}", addr, val); debug!("load Y. address: {}. value: {}", addr, val);
self.load_y_register(val as i8); self.load_y_register(val as i8);
}, }
(instruction::SBC, instruction::UseImmediate(val)) => {
debug!("subtract with carry immediate: {}", val);
self.subtract_with_carry(val as i8);
}
(instruction::SBC, instruction::UseAddress(addr)) => {
let val = self.memory.get_byte(addr) as i8;
debug!("subtract with carry. address: {}. value: {}",
addr, val);
self.subtract_with_carry(val);
}
(instruction::SEC, instruction::UseImplied) => {
self.registers.status.or(PS_CARRY);
}
(instruction::SED, instruction::UseImplied) => {
self.registers.status.or(PS_DECIMAL_MODE);
}
(instruction::SEI, instruction::UseImplied) => {
self.registers.status.or(PS_DISABLE_INTERRUPTS);
}
(instruction::STA, instruction::UseAddress(addr)) => { (instruction::STA, instruction::UseAddress(addr)) => {
self.memory.set_byte(addr, self.registers.accumulator as u8); self.memory.set_byte(addr, self.registers.accumulator as u8);
}, }
(instruction::STX, instruction::UseAddress(addr)) => { (instruction::STX, instruction::UseAddress(addr)) => {
self.memory.set_byte(addr, self.registers.index_x as u8); self.memory.set_byte(addr, self.registers.index_x as u8);
}, }
(instruction::STY, instruction::UseAddress(addr)) => { (instruction::STY, instruction::UseAddress(addr)) => {
self.memory.set_byte(addr, self.registers.index_y as u8); self.memory.set_byte(addr, self.registers.index_y as u8);
}, }
(instruction::TAX, instruction::UseImplied) => { (instruction::TAX, instruction::UseImplied) => {
let val = self.registers.accumulator; let val = self.registers.accumulator;
self.load_x_register(val); self.load_x_register(val);
}, }
(instruction::TAY, instruction::UseImplied) => { (instruction::TAY, instruction::UseImplied) => {
let val = self.registers.accumulator; let val = self.registers.accumulator;
self.load_y_register(val); self.load_y_register(val);
}, }
(instruction::TSX, instruction::UseImplied) => { (instruction::TSX, instruction::UseImplied) => {
let StackPointer(val) = self.registers.stack_pointer; let StackPointer(val) = self.registers.stack_pointer;
let val = val as i8; let val = val as i8;
self.load_x_register(val); self.load_x_register(val);
}, }
(instruction::TXA, instruction::UseImplied) => { (instruction::TXA, instruction::UseImplied) => {
let val = self.registers.index_x; let val = self.registers.index_x;
self.load_accumulator(val); self.load_accumulator(val);
}, }
(instruction::TXS, instruction::UseImplied) => { (instruction::TXS, instruction::UseImplied) => {
// Note that this is the only 'transfer' instruction that does // Note that this is the only 'transfer' instruction that does
// NOT set the zero and negative flags. (Because the target // NOT set the zero and negative flags. (Because the target
// is the stack pointer) // is the stack pointer)
let val = self.registers.index_x; let val = self.registers.index_x;
self.registers.stack_pointer = StackPointer(val as u8); self.registers.stack_pointer = StackPointer(val as u8);
}, }
(instruction::TYA, instruction::UseImplied) => { (instruction::TYA, instruction::UseImplied) => {
let val = self.registers.index_y; let val = self.registers.index_y;
self.load_accumulator(val); self.load_accumulator(val);
}, }
(instruction::NOP, _) => { (instruction::NOP, instruction::UseImplied) => {
debug!("nop instr"); debug!("NOP instruction");
}, }
(_, _) => { (_, _) => {
debug!("attempting to execute unimplemented instruction"); debug!("attempting to execute unimplemented or invalid \
}, instruction");
}
}; };
} }
@ -249,31 +285,81 @@ impl Machine {
value); value);
} }
// TODO akeeton: Implement binary-coded decimal.
fn add_with_carry(&mut self, value: i8) { fn add_with_carry(&mut self, value: i8) {
let a_before: i8 = self.registers.accumulator; if self.registers.status.contains(PS_DECIMAL_MODE) {
let c_before: i8 = self.registers.status.get_carry(); // TODO akeeton: Implement binary-coded decimal.
let a_after: i8 = a_before + c_before + value; debug!("binary-coded decimal not implemented for add_with_carry");
} else {
let a_before: i8 = self.registers.accumulator;
let c_before: i8 = if self.registers.status.contains(PS_CARRY)
{ 1 } else { 0 };
let a_after: i8 = a_before + c_before + value;
debug_assert_eq!(a_after as u8, a_before as u8 + c_before as u8 debug_assert_eq!(a_after as u8, a_before as u8 + c_before as u8
+ value as u8); + value as u8);
let did_carry = (a_after as u8) < (a_before as u8); let did_carry = (a_after as u8) < (a_before as u8);
let did_overflow = let did_overflow =
(a_before < 0 && value < 0 && a_after >= 0) (a_before < 0 && value < 0 && a_after >= 0)
|| (a_before > 0 && value > 0 && a_after <= 0); || (a_before > 0 && value > 0 && a_after <= 0);
let mask = PS_CARRY | PS_OVERFLOW; let mask = PS_CARRY | PS_OVERFLOW;
self.registers.status.set_with_mask(mask, self.registers.status.set_with_mask(mask,
Status::new(StatusArgs { carry: did_carry, Status::new(StatusArgs { carry: did_carry,
overflow: did_overflow, overflow: did_overflow,
..StatusArgs::none() } )); ..StatusArgs::none() } ));
self.load_accumulator(a_after); self.load_accumulator(a_after);
debug!("accumulator: {}", self.registers.accumulator); debug!("accumulator: {}", self.registers.accumulator);
}
}
// TODO: Implement binary-coded decimal
fn subtract_with_carry(&mut self, value: i8) {
if self.registers.status.contains(PS_DECIMAL_MODE) {
debug!("binary-coded decimal not implemented for \
subtract_with_carry");
} else {
// A - M - (1 - C)
// nc -- 'not carry'
let nc: i8 = if self.registers.status.contains(PS_CARRY)
{ 0 } else { 1 };
let a_before: i8 = self.registers.accumulator;
let a_after = a_before - value - nc;
// The carry flag is set on unsigned overflow.
let did_carry = (a_after as u8) > (a_before as u8);
// 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;
let under = (a_before < 0) && (-value - nc < 0)
&& a_after >= 0;
let did_overflow = over || under;
let mask = PS_CARRY | PS_OVERFLOW;
self.registers.status.set_with_mask(mask,
Status::new(StatusArgs { carry: did_carry,
overflow: did_overflow,
..StatusArgs::none() } ));
self.load_accumulator(a_after);
}
} }
fn decrement_memory(&mut self, addr: Address) { fn decrement_memory(&mut self, addr: Address) {
@ -286,12 +372,9 @@ impl Machine {
self.registers.status.set_with_mask( self.registers.status.set_with_mask(
PS_NEGATIVE | PS_ZERO, PS_NEGATIVE | PS_ZERO,
Status::new(StatusArgs { Status::new(StatusArgs { negative: is_negative,
negative: is_negative, zero: is_zero,
zero: is_zero, ..StatusArgs::none() } ));
..StatusArgs::none()
})
);
} }
fn dec_x(&mut self) { fn dec_x(&mut self) {
@ -390,6 +473,66 @@ fn add_with_carry_test() {
assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true); assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true);
} }
#[test]
fn subtract_with_carry_test() {
let mut machine = Machine::new();
machine.execute_instruction((instruction::SEC, instruction::UseImplied));
machine.registers.accumulator = 0;
machine.subtract_with_carry(1);
assert_eq!(machine.registers.accumulator, -1);
assert_eq!(machine.registers.status.contains(PS_CARRY), true);
assert_eq!(machine.registers.status.contains(PS_ZERO), false);
assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);
machine.execute_instruction((instruction::SEC, instruction::UseImplied));
machine.registers.accumulator = -128;
machine.subtract_with_carry(1);
assert_eq!(machine.registers.accumulator, 127);
assert_eq!(machine.registers.status.contains(PS_CARRY), false);
assert_eq!(machine.registers.status.contains(PS_ZERO), false);
assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true);
machine.execute_instruction((instruction::SEC, instruction::UseImplied));
machine.registers.accumulator = 127;
machine.subtract_with_carry(-1);
assert_eq!(machine.registers.accumulator, -128);
assert_eq!(machine.registers.status.contains(PS_CARRY), true);
assert_eq!(machine.registers.status.contains(PS_ZERO), false);
assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true);
machine.execute_instruction((instruction::CLC, instruction::UseImplied));
machine.registers.accumulator = -64;
machine.subtract_with_carry(64);
assert_eq!(machine.registers.accumulator, 127);
assert_eq!(machine.registers.status.contains(PS_CARRY), false);
assert_eq!(machine.registers.status.contains(PS_ZERO), false);
assert_eq!(machine.registers.status.contains(PS_NEGATIVE), false);
assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true);
machine.execute_instruction((instruction::SEC, instruction::UseImplied));
machine.registers.accumulator = 0;
machine.subtract_with_carry(-128);
assert_eq!(machine.registers.accumulator, -128);
assert_eq!(machine.registers.status.contains(PS_CARRY), true);
assert_eq!(machine.registers.status.contains(PS_ZERO), false);
assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
assert_eq!(machine.registers.status.contains(PS_OVERFLOW), true);
machine.execute_instruction((instruction::CLC, instruction::UseImplied));
machine.registers.accumulator = 0;
machine.subtract_with_carry(127);
assert_eq!(machine.registers.accumulator, -128);
assert_eq!(machine.registers.status.contains(PS_CARRY), true);
assert_eq!(machine.registers.status.contains(PS_ZERO), false);
assert_eq!(machine.registers.status.contains(PS_NEGATIVE), true);
assert_eq!(machine.registers.status.contains(PS_OVERFLOW), false);
}
#[test] #[test]
fn decrement_memory_test() { fn decrement_memory_test() {
let mut machine = Machine::new(); let mut machine = Machine::new();
@ -488,7 +631,7 @@ fn branch_if_minus_test() {
{ {
let mut machine = Machine::new(); let mut machine = Machine::new();
machine.registers.status.set_with_mask(PS_NEGATIVE, PS_NEGATIVE); machine.registers.status.or(PS_NEGATIVE);
let registers_before = machine.registers; let registers_before = machine.registers;
machine.branch_if_minus(Address(0xABCD)); machine.branch_if_minus(Address(0xABCD));

View File

@ -104,12 +104,16 @@ impl Status {
out out
} }
pub fn set_with_mask(&mut self, mask: Status, rhs: Status) { pub fn and(&mut self, rhs: Status) {
*self = (*self & !mask) | rhs; *self = *self & rhs;
} }
pub fn get_carry(self) -> i8 { pub fn or(&mut self, rhs: Status) {
if self.contains(PS_CARRY) { 1 } else { 0 } *self = *self | rhs;
}
pub fn set_with_mask(&mut self, mask: Status, rhs: Status) {
*self = (*self & !mask) | rhs;
} }
} }