mirror of
https://github.com/FranLMSP/snes.git
synced 2026-08-03 08:30:01 -04:00
JMP instruction
This commit is contained in:
@@ -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)]
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user