mirror of
https://github.com/FranLMSP/snes.git
synced 2026-10-05 00:45:18 -04:00
JMP instruction
This commit is contained in:
@@ -210,6 +210,22 @@ impl CPU {
|
|||||||
pub fn increment_cycles_nop(&mut self) {
|
pub fn increment_cycles_nop(&mut self) {
|
||||||
self.registers.increment_pc(1); self.cycles += 2;
|
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)]
|
#[cfg(test)]
|
||||||
|
|||||||
@@ -6,6 +6,16 @@ use crate::utils::num_trait::SnesNum;
|
|||||||
use crate::common::flags::Flags;
|
use crate::common::flags::Flags;
|
||||||
|
|
||||||
impl CPU {
|
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 {
|
fn get_8bit_from_address(&self, bus: &Bus, addressing_mode: AddressingMode) -> u8 {
|
||||||
match addressing_mode {
|
match addressing_mode {
|
||||||
AddressingMode::Accumulator => self.registers.a as u8,
|
AddressingMode::Accumulator => self.registers.a as u8,
|
||||||
@@ -481,6 +491,20 @@ impl CPU {
|
|||||||
self.increment_cycles_nop();
|
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) {
|
pub fn execute_opcode(&mut self, opcode: u8, bus: &mut Bus) {
|
||||||
type A = AddressingMode;
|
type A = AddressingMode;
|
||||||
type I = IndexRegister;
|
type I = IndexRegister;
|
||||||
@@ -637,6 +661,12 @@ impl CPU {
|
|||||||
0xE8 => self.inx(),
|
0xE8 => self.inx(),
|
||||||
// INY
|
// INY
|
||||||
0xC8 => self.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
|
// LSR
|
||||||
0x4A => self.lsr(bus, A::Accumulator),
|
0x4A => self.lsr(bus, A::Accumulator),
|
||||||
0x4E => self.lsr(bus, A::Absolute),
|
0x4E => self.lsr(bus, A::Absolute),
|
||||||
@@ -1371,4 +1401,29 @@ mod cpu_instructions_tests {
|
|||||||
assert_eq!(cpu.registers.pc, 0x01);
|
assert_eq!(cpu.registers.pc, 0x01);
|
||||||
assert_eq!(cpu.cycles, 2);
|
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)
|
((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
|
/// OPCODE long
|
||||||
pub fn absolute_long(bus: &Bus, pc_addr: u32) -> u32 {
|
pub fn absolute_long(bus: &Bus, pc_addr: u32) -> u32 {
|
||||||
(bus.read(pc_addr + 1) as 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)
|
((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
|
/// OPCODE dp
|
||||||
pub fn direct_page(bus: &Bus, pc_addr: u32, direct_page_register: u16) -> u32 {
|
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
|
(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)
|
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,X
|
||||||
/// OPCODE long,Y
|
/// OPCODE long,Y
|
||||||
pub fn absolute_long_indexed(bus: &Bus, pc_addr: u32, xy: u16) -> u32 {
|
pub fn absolute_long_indexed(bus: &Bus, pc_addr: u32, xy: u16) -> u32 {
|
||||||
@@ -96,11 +119,14 @@ pub enum AddressingMode {
|
|||||||
Accumulator,
|
Accumulator,
|
||||||
Immediate,
|
Immediate,
|
||||||
Absolute,
|
Absolute,
|
||||||
|
AbsoluteIndirect,
|
||||||
|
AbsoluteIndirectLong,
|
||||||
AbsoluteLong,
|
AbsoluteLong,
|
||||||
DirectPage,
|
DirectPage,
|
||||||
DirectPageIndirect,
|
DirectPageIndirect,
|
||||||
DirectPageIndirectLong,
|
DirectPageIndirectLong,
|
||||||
AbsoluteIndexed(IndexRegister),
|
AbsoluteIndexed(IndexRegister),
|
||||||
|
AbsoluteIndexedIndirect(IndexRegister),
|
||||||
AbsoluteLongIndexed(IndexRegister),
|
AbsoluteLongIndexed(IndexRegister),
|
||||||
DirectPageIndexed(IndexRegister),
|
DirectPageIndexed(IndexRegister),
|
||||||
DirectPageIndexedIndirect(IndexRegister),
|
DirectPageIndexedIndirect(IndexRegister),
|
||||||
@@ -118,11 +144,14 @@ impl AddressingMode {
|
|||||||
Self::Accumulator => pc_addr,
|
Self::Accumulator => pc_addr,
|
||||||
Self::Immediate => immediate(pc_addr),
|
Self::Immediate => immediate(pc_addr),
|
||||||
Self::Absolute => absolute(bus, 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::AbsoluteLong => absolute_long(bus, pc_addr),
|
||||||
Self::DirectPage => direct_page(bus, pc_addr, direct_page_register),
|
Self::DirectPage => direct_page(bus, pc_addr, direct_page_register),
|
||||||
Self::DirectPageIndirect => direct_page_indirect(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::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::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::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::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::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);
|
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]
|
#[test]
|
||||||
fn test_absolute_long() {
|
fn test_absolute_long() {
|
||||||
let mut bus = Bus::new();
|
let mut bus = Bus::new();
|
||||||
@@ -197,6 +251,19 @@ mod addressing_modes_tests {
|
|||||||
assert_eq!(absolute_long(&bus, pc_addr), 0x030201);
|
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]
|
#[test]
|
||||||
fn test_direct_page() {
|
fn test_direct_page() {
|
||||||
let mut bus = Bus::new();
|
let mut bus = Bus::new();
|
||||||
@@ -271,6 +338,17 @@ mod addressing_modes_tests {
|
|||||||
assert_eq!(absolute_indexed(&bus, pc_addr, 0x02), 0x7F0203);
|
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]
|
#[test]
|
||||||
fn test_absolute_long_indexed() {
|
fn test_absolute_long_indexed() {
|
||||||
let mut bus = Bus::new();
|
let mut bus = Bus::new();
|
||||||
|
|||||||
Reference in New Issue
Block a user