Refactor opcode execution and adc instruction

This commit is contained in:
2022-10-13 00:37:03 -05:00
parent 6acf97c7bd
commit e1f9542cca
5 changed files with 134 additions and 394 deletions
-133
View File
@@ -1,4 +1,3 @@
use crate::bus::Bus;
use super::registers::Registers;
pub struct CPU {
@@ -13,136 +12,4 @@ impl CPU {
cycles: 0,
}
}
pub fn get_absolute(&self, bus: &Bus) -> u16 {
let pc = self.registers.get_pc_address();
(bus.read(pc + 1) as u16) | ((bus.read(pc + 2) as u16) << 8)
}
pub fn get_absolute_long(&self, bus: &Bus) -> u32 {
let pc = self.registers.get_pc_address();
(bus.read(pc + 1) as u32) | ((bus.read(pc + 2) as u32) << 8) | ((bus.read(pc + 3) as u32) << 16)
}
pub fn get_direct_page(&self, bus: &Bus) -> u8 {
let pc = self.registers.get_pc_address();
bus.read(pc + 1)
}
pub fn get_immediate(&self, bus: &Bus) -> u16 {
// If the "m" flag is set to 1, read only 8 bits.
// Otherwise, read 16 bits
let address = self.registers.get_pc_address();
if self.registers.get_memory_select_flag() {
return bus.read(address + 1) as u16;
} else {
return (bus.read(address + 1) as u16) | ((bus.read(address + 2) as u16) << 8);
}
}
fn execute_opcode(&mut self, opcode: u8, bus: &Bus) {
match opcode {
0x69 => self.adc_const(bus),
0x6D => self.adc_addr(bus),
0x6F => self.adc_long(bus),
0x65 => self.adc_dp(bus),
0x72 => self.adc_dp_indirect(bus),
0x67 => self.adc_dp_indirect_long(bus),
_ => todo!("Missing opcode implementation: {:02X}", opcode),
}
}
pub fn tick(&mut self, bus: &Bus) {
let address = self.registers.get_pc_address();
let opcode = bus.read(address);
self.execute_opcode(opcode, bus);
}
}
#[cfg(test)]
mod cpu_tests {
use super::*;
#[test]
fn test_get_immediate() {
let mut bus = Bus::new();
let mut cpu = CPU::new();
cpu.registers.set_memory_select_flag(true);
cpu.registers.pc = 0x0000;
cpu.registers.pbr = 0x00;
// write to WRAM
bus.write(0x00_0001, 0x01);
bus.write(0x00_0002, 0x02);
assert_eq!(cpu.get_immediate(&bus), 0x0001);
cpu.registers.set_memory_select_flag(false);
assert_eq!(cpu.get_immediate(&bus), 0x0201);
cpu.registers.set_memory_select_flag(true);
cpu.registers.pc = 0x0010;
cpu.registers.pbr = 0x7E;
// write to WRAM
bus.write(0x7E_0011, 0x01);
bus.write(0x7E_0012, 0x02);
assert_eq!(cpu.get_immediate(&bus), 0x0001);
cpu.registers.set_memory_select_flag(false);
assert_eq!(cpu.get_immediate(&bus), 0x0201);
}
#[test]
fn test_get_absolute() {
let mut bus = Bus::new();
let mut cpu = CPU::new();
cpu.registers.pc = 0x0000;
cpu.registers.pbr = 0x00;
// write to WRAM
bus.write(0x00_0001, 0x01);
bus.write(0x00_0002, 0x02);
assert_eq!(cpu.get_absolute(&bus), 0x0201);
cpu.registers.pc = 0x0010;
cpu.registers.pbr = 0x7E;
// write to WRAM
bus.write(0x7E_0011, 0x01);
bus.write(0x7E_0012, 0x02);
assert_eq!(cpu.get_absolute(&bus), 0x0201);
}
#[test]
fn test_get_absolute_long() {
let mut bus = Bus::new();
let mut cpu = CPU::new();
cpu.registers.pc = 0x0000;
cpu.registers.pbr = 0x00;
// write to WRAM
bus.write(0x00_0001, 0x01);
bus.write(0x00_0002, 0x02);
bus.write(0x00_0003, 0x03);
assert_eq!(cpu.get_absolute_long(&bus), 0x030201);
cpu.registers.pc = 0x0010;
cpu.registers.pbr = 0x7E;
// write to WRAM
bus.write(0x7E_0011, 0x01);
bus.write(0x7E_0012, 0x02);
bus.write(0x7E_0013, 0x03);
assert_eq!(cpu.get_absolute_long(&bus), 0x030201);
}
#[test]
fn test_get_direct_page() {
let mut bus = Bus::new();
let mut cpu = CPU::new();
cpu.registers.pc = 0x0000;
cpu.registers.pbr = 0x00;
// write to WRAM
bus.write(0x00_0001, 0x01);
assert_eq!(cpu.get_direct_page(&bus), 0x01);
cpu.registers.pc = 0x0010;
cpu.registers.pbr = 0x7E;
// write to WRAM
bus.write(0x7E_0011, 0x01);
assert_eq!(cpu.get_direct_page(&bus), 0x01);
}
}
+74 -200
View File
@@ -1,192 +1,84 @@
use super::cpu::CPU;
use crate::bus::Bus;
use crate::utils::addressing::{AddressingMode, IndexRegister};
use crate::utils::alu;
impl CPU {
fn adc8bcd(&mut self, value: u8) {
let carry = self.registers.get_carry_flag();
let a = self.registers.a as u8;
let carry_result = match a.checked_add(value) {
None => true,
Some(res) => match res.checked_add(carry as u8) {
None => true,
Some(_) => false,
},
};
let result = a
.wrapping_add(value)
.wrapping_add(carry as u8);
self.registers.a = (self.registers.a & 0xFF00) | (result as u16);
self.registers.set_carry_flag(carry_result);
self.registers.set_negative_flag((result >> 7) == 1);
self.registers.set_zero_flag(result == 0);
fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 {
addressing_mode.value_8bit(
bus,
self.registers.get_pc_address(),
self.registers.d,
self.registers.sp,
self.registers.x, self.registers.y
)
}
fn adc16bcd(&mut self, value: u16) {
let carry = self.registers.get_carry_flag();
let carry_result = match self.registers.a.checked_add(value) {
None => true,
Some(res) => match res.checked_add(carry as u16) {
None => true,
Some(_) => false,
},
};
let result = self.registers.a
.wrapping_add(value)
.wrapping_add(carry as u16);
self.registers.a = result;
self.registers.set_carry_flag(carry_result);
self.registers.set_negative_flag((result >> 7) == 1);
self.registers.set_zero_flag(result == 0);
fn get_16bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u16 {
addressing_mode.value_16bit(
bus,
self.registers.get_pc_address(),
self.registers.d,
self.registers.sp,
self.registers.x, self.registers.y
)
}
fn adc8(&mut self, value: u8) {
let carry = self.registers.get_carry_flag();
let a = self.registers.a as u8;
let carry_result = match a.checked_add(value) {
None => true,
Some(res) => match res.checked_add(carry as u8) {
None => true,
Some(_) => false,
},
};
let result = a
.wrapping_add(value)
.wrapping_add(carry as u8);
self.registers.a = (self.registers.a & 0xFF00) | (result as u16);
self.registers.set_carry_flag(carry_result);
self.registers.set_negative_flag((result >> 7) == 1);
self.registers.set_zero_flag(result == 0);
}
fn adc16(&mut self, value: u16) {
let carry = self.registers.get_carry_flag();
let carry_result = match self.registers.a.checked_add(value) {
None => true,
Some(res) => match res.checked_add(carry as u16) {
None => true,
Some(_) => false,
},
};
let result = self.registers.a
.wrapping_add(value)
.wrapping_add(carry as u16);
self.registers.a = result;
self.registers.set_carry_flag(carry_result);
self.registers.set_negative_flag((result >> 15) == 1);
self.registers.set_zero_flag(result == 0);
}
fn adc(&mut self, value: u16) {
fn adc(&mut self, bus: &Bus, addressing_mode: AddressingMode) {
// if the M flag is set, perform 8 bit addition.
// Otherwise, 16 bit addition
if self.registers.get_memory_select_flag() {
if self.registers.get_decimal_mode_flag() {
self.adc8bcd(value as u8);
} else {
self.adc8(value as u8);
let carry_flag = self.registers.get_carry_flag();
let is_decimal_mode = self.registers.get_decimal_mode_flag();
let is_8bit = self.registers.get_memory_select_flag();
let target = self.registers.a;
match is_8bit {
true => {
let value = self.get_8bit_from_address(bus, addressing_mode);
let (result, is_carry, is_negative, is_zero) = match is_decimal_mode {
true => alu::adc8bcd(target as u8, value, carry_flag),
false => alu::adc8bin(target as u8, value, carry_flag),
};
self.registers.a = result as u16;
self.registers.set_carry_flag(is_carry);
self.registers.set_negative_flag(is_negative);
self.registers.set_zero_flag(is_zero);
},
false => {
let value = self.get_16bit_from_address(bus, addressing_mode);
let (result, is_carry, is_negative, is_zero) = match is_decimal_mode {
true => alu::adc16bcd(target, value, carry_flag),
false => alu::adc16bin(target, value, carry_flag),
};
self.registers.a = result;
self.registers.set_carry_flag(is_carry);
self.registers.set_negative_flag(is_negative);
self.registers.set_zero_flag(is_zero);
}
} else {
if self.registers.get_decimal_mode_flag() {
self.adc16bcd(value);
} else {
self.adc16(value);
}
}
};
}
pub fn adc_const(&mut self, bus: &Bus) {
let value = self.get_immediate(bus);
self.registers.pc = self.registers.pc.wrapping_add(2);
self.cycles += 2;
if self.registers.get_memory_select_flag() {
self.registers.pc = self.registers.pc.wrapping_add(1);
self.cycles += 1;
pub fn execute_opcode(&mut self, opcode: u8, bus: &Bus) {
type A = AddressingMode;
type I = IndexRegister;
match opcode {
// ADC
0x69 => self.adc(bus, A::Immediate),
0x6D => self.adc(bus, A::Absolute),
0x6F => self.adc(bus, A::AbsoluteLong),
0x65 => self.adc(bus, A::DirectPage),
0x72 => self.adc(bus, A::DirectPageIndirect),
0x67 => self.adc(bus, A::DirectPageIndirectLong),
0x7D => self.adc(bus, A::AbsoluteIndexed(I::X)),
0x7F => self.adc(bus, A::AbsoluteLongIndexed(I::X)),
0x79 => self.adc(bus, A::AbsoluteIndexed(I::Y)),
0x75 => self.adc(bus, A::DirectPageIndexed(I::X)),
0x61 => self.adc(bus, A::DirectPageIndexedIndirect(I::X)),
0x71 => self.adc(bus, A::DirectPageIndirectIndexed(I::Y)),
0x77 => self.adc(bus, A::DirectPageIndirectLongIndexed(I::Y)),
0x63 => self.adc(bus, A::StackRelative),
0x73 => self.adc(bus, A::StackRelativeIndirectIndexed(I::Y)),
_ => println!("Invalid opcode: {:02X}", opcode),
}
if self.registers.get_decimal_mode_flag() {
self.cycles += 1;
}
self.adc(value);
}
pub fn adc_addr(&mut self, bus: &Bus) {
let address = ((self.registers.pbr as u32) << 16) | (self.get_absolute(bus) as u32);
self.registers.pc = self.registers.pc.wrapping_add(3);
self.cycles += 4;
if self.registers.get_memory_select_flag() {
self.cycles += 1;
}
if self.registers.get_decimal_mode_flag() {
self.cycles += 1;
}
let value = bus.read(address);
self.adc(value as u16);
}
pub fn adc_long(&mut self, bus: &Bus) {
let address = self.get_absolute_long(bus);
self.registers.pc = self.registers.pc.wrapping_add(4);
self.cycles += 5;
if self.registers.get_memory_select_flag() {
self.cycles += 1;
}
if self.registers.get_decimal_mode_flag() {
self.cycles += 1;
}
let value = bus.read(address);
self.adc(value as u16);
}
pub fn adc_dp(&mut self, bus: &Bus) {
let address = self.get_direct_page(bus);
self.registers.pc = self.registers.pc.wrapping_add(2);
self.cycles += 3;
if self.registers.get_memory_select_flag() {
self.cycles += 1;
}
if address != 0 {
self.cycles += 1;
}
if self.registers.get_decimal_mode_flag() {
self.cycles += 1;
}
let value = bus.read(address as u32);
self.adc(value as u16);
}
pub fn adc_dp_indirect(&mut self, bus: &Bus) {
let pointer = self.get_direct_page(bus) as u32;
let address = ((bus.read(pointer) as u32) << 8) | (bus.read(pointer + 1) as u32);
self.registers.pc = self.registers.pc.wrapping_add(2);
self.cycles += 3;
if self.registers.get_memory_select_flag() {
self.cycles += 1;
}
if address != 0 {
self.cycles += 1;
}
if self.registers.get_decimal_mode_flag() {
self.cycles += 1;
}
let value = bus.read(address);
self.adc(value as u16);
}
pub fn adc_dp_indirect_long(&mut self, bus: &Bus) {
let pointer = self.get_direct_page(bus) as u32;
let address = ((bus.read(pointer) as u32) << 16) | ((bus.read(pointer + 1) as u32) << 8) | (bus.read(pointer + 2) as u32);
self.registers.pc = self.registers.pc.wrapping_add(2);
self.cycles += 3;
if self.registers.get_memory_select_flag() {
self.cycles += 1;
}
if address != 0 {
self.cycles += 1;
}
if self.registers.get_decimal_mode_flag() {
self.cycles += 1;
}
let value = bus.read(address);
self.adc(value as u16);
}
}
@@ -197,32 +89,14 @@ mod cpu_instructions_tests {
#[test]
fn test_adc() {
let mut cpu = CPU::new();
cpu.registers.a = 0x00;
cpu.adc(0x40);
let mut bus = Bus::new();
cpu.registers.a = 0x0000;
cpu.registers.pbr = 0x00;
cpu.registers.pc = 0x0000;
cpu.registers.set_memory_select_flag(true);
bus.write(0x000001, 0x40);
cpu.adc(&bus, AddressingMode::Immediate);
assert_eq!(cpu.registers.a, 0x40);
assert!(!cpu.registers.get_carry_flag());
cpu.registers.a = 0xFFFF;
cpu.adc(0x01);
assert_eq!(cpu.registers.a, 0x0000);
assert!(cpu.registers.get_carry_flag());
cpu.registers.a = 0xFFFF;
cpu.registers.set_carry_flag(true);
cpu.adc(0x01);
assert_eq!(cpu.registers.a, 0x0001);
assert!(cpu.registers.get_carry_flag());
cpu.registers.a = 0xFFFD;
cpu.registers.set_carry_flag(true);
cpu.adc(0x01);
assert_eq!(cpu.registers.a, 0xFFFF);
assert!(!cpu.registers.get_carry_flag());
cpu.registers.a = 0x0000;
cpu.registers.set_carry_flag(true);
cpu.adc(0x01);
assert_eq!(cpu.registers.a, 0x0002);
assert!(!cpu.registers.get_carry_flag());
}
}
+27 -21
View File
@@ -86,6 +86,11 @@ pub fn stack_relative_indirect_indexed(bus: &Bus, pc_addr: u32, stack_pointer: u
absolute_indexed(bus, pc_addr, stack_pointer) + (xy as u32)
}
#[derive(Copy, Clone, PartialEq)]
pub enum IndexRegister {
X, Y,
}
#[derive(Copy, Clone)]
pub enum AddressingMode {
Immediate,
@@ -94,18 +99,19 @@ pub enum AddressingMode {
DirectPage,
DirectPageIndirect,
DirectPageIndirectLong,
AbsoluteIndexed,
AbsoluteLongIndexed,
DirectPageIndexed,
DirectPageIndexedIndirect,
DirectPageIndirectIndexed,
DirectPageIndirectLongIndexed,
AbsoluteIndexed(IndexRegister),
AbsoluteLongIndexed(IndexRegister),
DirectPageIndexed(IndexRegister),
DirectPageIndexedIndirect(IndexRegister),
DirectPageIndirectIndexed(IndexRegister),
DirectPageIndirectLongIndexed(IndexRegister),
StackRelative,
StackRelativeIndirectIndexed,
StackRelativeIndirectIndexed(IndexRegister),
}
impl AddressingMode {
pub fn effective_address(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, xy: u16) -> u32 {
pub fn effective_address(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16) -> u32 {
use IndexRegister::X as X;
match self {
Self::Immediate => immediate(pc_addr),
Self::Absolute => absolute(bus, pc_addr),
@@ -113,24 +119,24 @@ impl AddressingMode {
Self::DirectPage => direct_page(bus, pc_addr, direct_page_register),
Self::DirectPageIndirect => direct_page_indirect(bus, pc_addr, direct_page_register),
Self::DirectPageIndirectLong => direct_page_indirect_long(bus, pc_addr, direct_page_register),
Self::AbsoluteIndexed => absolute_indexed(bus, pc_addr, xy),
Self::AbsoluteLongIndexed => absolute_long_indexed(bus, pc_addr, xy),
Self::DirectPageIndexed => direct_page_indexed(bus, pc_addr, direct_page_register, xy),
Self::DirectPageIndexedIndirect => direct_page_indexed_indirect(bus, pc_addr, direct_page_register, xy),
Self::DirectPageIndirectIndexed => direct_page_indirect_indexed(bus, pc_addr, direct_page_register, xy),
Self::DirectPageIndirectLongIndexed => direct_page_indirect_long_indexed(bus, pc_addr, direct_page_register, xy),
Self::AbsoluteIndexed(idx) => absolute_indexed(bus, pc_addr, if idx == X {x} else {y}),
Self::AbsoluteLongIndexed(idx) => absolute_long_indexed(bus, pc_addr, if idx == X {x} else {y}),
Self::DirectPageIndexed(idx) => direct_page_indexed(bus, pc_addr, direct_page_register, if idx == X {x} else {y}),
Self::DirectPageIndexedIndirect(idx) => direct_page_indexed_indirect(bus, pc_addr, direct_page_register, if idx == X {x} else {y}),
Self::DirectPageIndirectIndexed(idx) => direct_page_indirect_indexed(bus, pc_addr, direct_page_register, if idx == X {x} else {y}),
Self::DirectPageIndirectLongIndexed(idx) => direct_page_indirect_long_indexed(bus, pc_addr, direct_page_register, if idx == X {x} else {y}),
Self::StackRelative => stack_relative(bus, pc_addr, stack_pointer),
Self::StackRelativeIndirectIndexed => stack_relative_indirect_indexed(bus, pc_addr, stack_pointer, xy),
Self::StackRelativeIndirectIndexed(idx) => stack_relative_indirect_indexed(bus, pc_addr, stack_pointer, if idx == X {x} else {y}),
}
}
pub fn value_8bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, xy: u16) -> u8 {
let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, xy);
pub fn value_8bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16) -> u8 {
let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, x, y);
return bus.read(address);
}
pub fn value_16bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, xy: u16) -> u16 {
let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, xy);
pub fn value_16bit(self, bus: &Bus, pc_addr: u32, direct_page_register: u16, stack_pointer: u16, x: u16, y: u16) -> u16 {
let address = self.effective_address(bus, pc_addr, direct_page_register, stack_pointer, x, y);
return (bus.read(address) as u16) | ((bus.read(address + 1) as u16) << 8);
}
}
@@ -389,7 +395,7 @@ mod addressing_modes_tests {
bus.write(pc_addr + 1, 0x20);
bus.write(pc_addr + 2, 0x10);
bus.write(0x001020, 0xFE);
let val = AddressingMode::Absolute.value_8bit(&bus, pc_addr, 0x00, 0x00, 0x00);
let val = AddressingMode::Absolute.value_8bit(&bus, pc_addr, 0x00, 0x00, 0x00, 0x00);
assert_eq!(val, 0xFE);
let mut bus = Bus::new();
@@ -398,7 +404,7 @@ mod addressing_modes_tests {
bus.write(pc_addr + 2, 0x10);
bus.write(0x001020, 0xFF);
bus.write(0x001021, 0xEE);
let val = AddressingMode::Absolute.value_16bit(&bus, pc_addr, 0x00, 0x00, 0x00);
let val = AddressingMode::Absolute.value_16bit(&bus, pc_addr, 0x00, 0x00, 0x00, 0x00);
assert_eq!(val, 0xEEFF);
}
}
+32 -40
View File
@@ -32,26 +32,15 @@ pub fn adc16bin(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool)
pub fn adc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) {
let mut is_carry = carry;
let mut result: u8 = 0;
let mut operand_mask: u8 = 0x0F;
let mut result_mask: u8 = 0x00;
let mut carry_check: u8= 0x09;
let mut carry_add: u8 = 0x06;
let mut is_carry_shifts: u8 = 0;
for _ in 0..2 {
result = (result & result_mask)
.wrapping_add(target & operand_mask)
.wrapping_add(value & operand_mask)
.wrapping_add((is_carry as u8) << is_carry_shifts);
is_carry = result > carry_check;
if is_carry {
result = result.wrapping_add(carry_add);
}
operand_mask = operand_mask << 4;
carry_add = carry_add << 4;
carry_check = (carry_check << 4) | 0x0F;
result_mask = (result_mask << 4) | 0x0F;
is_carry_shifts += 4;
let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u8);
is_carry = result > 9;
if is_carry {
result += 6;
}
result = (result & 0xF0) + (target & 0xF0) + (value & 0xF0) + ((is_carry as u8) << 4);
is_carry = result > 0x9F;
if is_carry {
result = result.wrapping_add(0x60);
}
let is_negative = (result >> 7) == 1;
let is_zero = result == 0;
@@ -60,26 +49,29 @@ pub fn adc8bcd(target: u8, value: u8, carry: bool) -> (u8, bool, bool, bool) {
pub fn adc16bcd(target: u16, value: u16, carry: bool) -> (u16, bool, bool, bool) {
let mut is_carry = carry;
let mut result: u16 = 0;
let mut operand_mask: u16 = 0x0F;
let mut result_mask: u16 = 0x00;
let mut carry_check: u16 = 0x09;
let mut carry_add: u16 = 0x06;
let mut is_carry_shifts: u16 = 0;
for _ in 0..4 {
result = (result & result_mask)
.wrapping_add(target & operand_mask)
.wrapping_add(value & operand_mask)
.wrapping_add((is_carry as u16) << is_carry_shifts);
is_carry = result > carry_check;
if is_carry {
result = result.wrapping_add(carry_add);
}
operand_mask = operand_mask << 4;
carry_add = carry_add << 4;
carry_check = (carry_check << 4) | 0x0F;
result_mask = (result_mask << 4) | 0x0F;
is_carry_shifts += 4;
let mut result = (target & 0xF) + (value & 0xF) + (is_carry as u16);
is_carry = false;
if result > 9 {
result += 6;
is_carry = true;
}
result = (result & 0xF0) + (target & 0xF0) + (value & 0xF0) + ((is_carry as u16) << 4);
is_carry = false;
if result > 0x9F {
result += 0x60;
is_carry = true;
}
result = (result & 0xF00) + (target & 0xF00) + (value & 0xF00) + ((is_carry as u16) << 8);
is_carry = false;
if result > 0x9FF {
result += 0x600;
is_carry = true;
}
result = (result & 0xF000) + (target & 0xF000) + (target & 0xF000) + ((is_carry as u16) << 12);
is_carry = false;
if result > 0x9FFF {
result += 0x6000;
is_carry = true;
}
let is_negative = (result >> 15) == 1;
let is_zero = result == 0;