JMP instruction

This commit is contained in:
2022-11-27 12:44:02 -05:00
parent 41111c3158
commit d705c2138c
3 changed files with 149 additions and 0 deletions
+16
View File
@@ -210,6 +210,22 @@ impl CPU {
pub fn increment_cycles_nop(&mut self) {
self.registers.increment_pc(1); self.cycles += 2;
}
pub fn increment_cycles_jmp(&mut self, addressing_mode: AddressingMode) {
let (_, cycles) = match addressing_mode {
A::Absolute => (3, 3),
A::AbsoluteIndirect => (3, 5),
A::AbsoluteIndexedIndirect(_) => (3, 6),
A::AbsoluteLong => (4, 4),
A::AbsoluteIndirectLong => (3, 6),
_ => unreachable!(),
};
// Incrementing PC here is kind of irrelevant since we
// are performing a JMP anyway.
// However, we have to keep in mind PBR if we happen to increment PC at 0xFFFF
// self.registers.increment_pc(bytes); // TODO: consider above comment
self.cycles += cycles;
}
}
#[cfg(test)]
+55
View File
@@ -6,6 +6,16 @@ use crate::utils::num_trait::SnesNum;
use crate::common::flags::Flags;
impl CPU {
fn get_effective_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u32 {
addressing_mode.effective_address(
bus,
self.registers.get_pc_address(),
self.registers.d,
self.registers.sp,
self.registers.x, self.registers.y
)
}
fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 {
match addressing_mode {
AddressingMode::Accumulator => self.registers.a as u8,
@@ -481,6 +491,20 @@ impl CPU {
self.increment_cycles_nop();
}
pub fn jmp(&mut self, bus: &Bus, addressing_mode: AddressingMode) {
let effective_address = self.get_effective_address(bus, addressing_mode);
let is_long = match addressing_mode {
AddressingMode::AbsoluteLong |
AddressingMode::AbsoluteIndirectLong => true,
_ => false,
};
self.registers.pc = effective_address as u16;
if is_long {
self.registers.pbr = (effective_address >> 16) as u8;
}
self.increment_cycles_jmp(addressing_mode);
}
pub fn execute_opcode(&mut self, opcode: u8, bus: &mut Bus) {
type A = AddressingMode;
type I = IndexRegister;
@@ -637,6 +661,12 @@ impl CPU {
0xE8 => self.inx(),
// INY
0xC8 => self.iny(),
// JMP
0x4C => self.jmp(bus, A::Absolute),
0x6C => self.jmp(bus, A::AbsoluteIndirect),
0x7C => self.jmp(bus, A::AbsoluteIndexedIndirect(I::X)),
0x5C => self.jmp(bus, A::AbsoluteLong),
0xDC => self.jmp(bus, A::AbsoluteIndirectLong),
// LSR
0x4A => self.lsr(bus, A::Accumulator),
0x4E => self.lsr(bus, A::Absolute),
@@ -1371,4 +1401,29 @@ mod cpu_instructions_tests {
assert_eq!(cpu.registers.pc, 0x01);
assert_eq!(cpu.cycles, 2);
}
#[test]
fn test_jmp() {
let mut cpu = CPU::new();
let mut bus = Bus::new();
cpu.registers.pc = 0x0000;
bus.write(0x000002, 0xAA);
bus.write(0x000001, 0xBB);
cpu.jmp(&bus, AddressingMode::Absolute);
assert_eq!(cpu.registers.pc, 0xAABB);
assert_eq!(cpu.cycles, 3);
// Test a long address
let mut cpu = CPU::new();
let mut bus = Bus::new();
cpu.registers.pc = 0x0000;
cpu.registers.pbr = 0x00;
bus.write(0x000003, 0xAA);
bus.write(0x000002, 0xBB);
bus.write(0x000001, 0xCC);
cpu.jmp(&bus, AddressingMode::AbsoluteLong);
assert_eq!(cpu.registers.pbr, 0xAA);
assert_eq!(cpu.registers.pc, 0xBBCC);
assert_eq!(cpu.cycles, 4);
}
}
+78
View File
@@ -12,6 +12,13 @@ pub fn absolute(bus: &Bus, pc_addr: u32) -> u32 {
((bus.read(pc_addr + 2) as u32) << 8)
}
/// OPCODE (addr)
pub fn absolute_indirect(bus: &Bus, pc_addr: u32) -> u32 {
let addr = absolute(bus, pc_addr);
let dbr = pc_addr & 0xFF0000;
dbr | ((bus.read(addr) as u32) << 8) | (bus.read(addr + 1) as u32)
}
/// OPCODE long
pub fn absolute_long(bus: &Bus, pc_addr: u32) -> u32 {
(bus.read(pc_addr + 1) as u32) |
@@ -19,6 +26,14 @@ pub fn absolute_long(bus: &Bus, pc_addr: u32) -> u32 {
((bus.read(pc_addr + 3) as u32) << 16)
}
/// OPCODE (addr)
pub fn absolute_indirect_long(bus: &Bus, pc_addr: u32) -> u32 {
let addr = absolute(bus, pc_addr);
((bus.read(addr) as u32) << 16) |
((bus.read(addr + 1) as u32) << 8) |
(bus.read(addr + 2) as u32)
}
/// OPCODE dp
pub fn direct_page(bus: &Bus, pc_addr: u32, direct_page_register: u16) -> u32 {
(bus.read(pc_addr + 1) as u32) + direct_page_register as u32
@@ -45,6 +60,14 @@ pub fn absolute_indexed(bus: &Bus, pc_addr: u32, xy: u16) -> u32 {
absolute(bus, pc_addr) + (xy as u32)
}
/// OPCODE (addr)
pub fn absolute_indexed_indirect(bus: &Bus, pc_addr: u32, xy: u16) -> u32 {
let addr = absolute_indexed(bus, pc_addr, xy);
let dbr = pc_addr & 0xFF0000;
dbr | ((bus.read(addr) as u32) << 8) | (bus.read(addr + 1) as u32)
}
/// OPCODE long,X
/// OPCODE long,Y
pub fn absolute_long_indexed(bus: &Bus, pc_addr: u32, xy: u16) -> u32 {
@@ -96,11 +119,14 @@ pub enum AddressingMode {
Accumulator,
Immediate,
Absolute,
AbsoluteIndirect,
AbsoluteIndirectLong,
AbsoluteLong,
DirectPage,
DirectPageIndirect,
DirectPageIndirectLong,
AbsoluteIndexed(IndexRegister),
AbsoluteIndexedIndirect(IndexRegister),
AbsoluteLongIndexed(IndexRegister),
DirectPageIndexed(IndexRegister),
DirectPageIndexedIndirect(IndexRegister),
@@ -118,11 +144,14 @@ impl AddressingMode {
Self::Accumulator => pc_addr,
Self::Immediate => immediate(pc_addr),
Self::Absolute => absolute(bus, pc_addr),
Self::AbsoluteIndirect => absolute_indirect(bus, pc_addr),
Self::AbsoluteIndirectLong => absolute_indirect_long(bus, pc_addr),
Self::AbsoluteLong => absolute_long(bus, pc_addr),
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(idx) => absolute_indexed(bus, pc_addr, if idx == X {x} else {y}),
Self::AbsoluteIndexedIndirect(idx) => absolute_indexed_indirect(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}),
@@ -180,6 +209,31 @@ mod addressing_modes_tests {
assert_eq!(absolute(&bus, pc_addr), 0x7F0201);
}
#[test]
fn test_absolute_indirect() {
let mut bus = Bus::new();
let pc_addr = 0x000000;
let addr = 0x55;
bus.write(pc_addr + 1, addr);
bus.write(addr as u32, 0x02);
bus.write((addr + 1) as u32, 0x01);
assert_eq!(absolute_indirect(&bus, pc_addr), 0x000201);
let pc_addr = 0x7E0010;
let addr = 0x55;
bus.write(pc_addr + 1, addr);
bus.write(addr as u32, 0x02);
bus.write((addr + 1) as u32, 0x01);
assert_eq!(absolute_indirect(&bus, pc_addr), 0x7E0201);
let pc_addr = 0x7E0010;
let addr = 0x55;
bus.write(pc_addr + 1, addr);
bus.write(addr as u32, 0x02);
bus.write((addr + 1) as u32, 0x01);
assert_eq!(absolute_indirect(&bus, pc_addr), 0x7E0201);
}
#[test]
fn test_absolute_long() {
let mut bus = Bus::new();
@@ -197,6 +251,19 @@ mod addressing_modes_tests {
assert_eq!(absolute_long(&bus, pc_addr), 0x030201);
}
#[test]
fn test_absolute_indirect_long() {
let mut bus = Bus::new();
let pc_addr = 0x000000;
let addr = 0x55;
bus.write(pc_addr + 1, addr);
bus.write(addr as u32, 0x03);
bus.write((addr + 1) as u32, 0x02);
bus.write((addr + 2) as u32, 0x01);
assert_eq!(absolute_indirect_long(&bus, pc_addr), 0x030201);
}
#[test]
fn test_direct_page() {
let mut bus = Bus::new();
@@ -271,6 +338,17 @@ mod addressing_modes_tests {
assert_eq!(absolute_indexed(&bus, pc_addr, 0x02), 0x7F0203);
}
#[test]
fn test_absolute_indexed_indirect() {
let mut bus = Bus::new();
let pc_addr = 0x000000;
let addr = 0x55;
bus.write(pc_addr + 1, addr);
bus.write(addr as u32, 0x02);
bus.write((addr + 1) as u32, 0x01);
assert_eq!(absolute_indexed_indirect(&bus, pc_addr, 0), 0x000201);
}
#[test]
fn test_absolute_long_indexed() {
let mut bus = Bus::new();