mirror of
https://github.com/FranLMSP/snes.git
synced 2026-08-03 08:30:01 -04:00
jmp and jsr
This commit is contained in:
@@ -0,0 +1,68 @@
|
|||||||
|
use crate::cpu::{bus::Bus, registers::Registers};
|
||||||
|
use crate::utils::addressing::AddressingMode;
|
||||||
|
|
||||||
|
use super::{CPUInstruction, Decode, get_effective_address};
|
||||||
|
use super::decoder_common;
|
||||||
|
use crate::cpu::cycles;
|
||||||
|
|
||||||
|
static INSTR_NAME: &'static str = "JMP";
|
||||||
|
|
||||||
|
pub struct JMP {
|
||||||
|
addressing_mode: AddressingMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CPUInstruction for JMP {
|
||||||
|
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||||
|
let effective_address = get_effective_address(®isters, bus, self.addressing_mode);
|
||||||
|
let is_long = match self.addressing_mode {
|
||||||
|
AddressingMode::AbsoluteLong |
|
||||||
|
AddressingMode::AbsoluteIndirectLong => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
registers.pc = effective_address as u16;
|
||||||
|
if is_long {
|
||||||
|
registers.pbr = (effective_address >> 16) as u8;
|
||||||
|
}
|
||||||
|
let (bytes, cycles) = cycles::increment_cycles_jmp(self.addressing_mode);
|
||||||
|
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for JMP {
|
||||||
|
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||||
|
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod cpu_instructions_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
let mut registers = Registers::new();
|
||||||
|
let mut bus = Bus::new();
|
||||||
|
registers.pc = 0x0000;
|
||||||
|
bus.write(0x000002, 0xAA);
|
||||||
|
bus.write(0x000001, 0xBB);
|
||||||
|
let instruction = JMP{addressing_mode: AddressingMode::Absolute};
|
||||||
|
instruction.execute(&mut registers, &mut bus);
|
||||||
|
assert_eq!(registers.pc, 0xAABB);
|
||||||
|
assert_eq!(registers.cycles, 3);
|
||||||
|
|
||||||
|
// Test a long address
|
||||||
|
let mut registers = Registers::new();
|
||||||
|
let mut bus = Bus::new();
|
||||||
|
registers.pc = 0x0000;
|
||||||
|
registers.pbr = 0x00;
|
||||||
|
bus.write(0x000003, 0xAA);
|
||||||
|
bus.write(0x000002, 0xBB);
|
||||||
|
bus.write(0x000001, 0xCC);
|
||||||
|
let instruction = JMP{addressing_mode: AddressingMode::AbsoluteLong};
|
||||||
|
instruction.execute(&mut registers, &mut bus);
|
||||||
|
assert_eq!(registers.pbr, 0xAA);
|
||||||
|
assert_eq!(registers.pc, 0xBBCC);
|
||||||
|
assert_eq!(registers.cycles, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,79 @@
|
|||||||
|
use crate::cpu::{bus::Bus, registers::Registers};
|
||||||
|
use crate::utils::addressing::AddressingMode;
|
||||||
|
|
||||||
|
use super::{CPUInstruction, Decode, get_effective_address, push_common};
|
||||||
|
use super::decoder_common;
|
||||||
|
use crate::cpu::cycles;
|
||||||
|
|
||||||
|
static INSTR_NAME: &'static str = "JSR";
|
||||||
|
|
||||||
|
pub struct JSR {
|
||||||
|
addressing_mode: AddressingMode,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CPUInstruction for JSR {
|
||||||
|
fn execute(&self, registers: &mut Registers, bus: &mut Bus) {
|
||||||
|
let effective_address = get_effective_address(registers, bus, self.addressing_mode);
|
||||||
|
let is_long = match self.addressing_mode {
|
||||||
|
AddressingMode::AbsoluteLong |
|
||||||
|
AddressingMode::AbsoluteIndirectLong => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
// We need to push the *next* instruction onto the stack
|
||||||
|
let (bytes, cycles) = cycles::increment_cycles_jsr(self.addressing_mode);
|
||||||
|
registers.increment_pc(bytes); registers.cycles += cycles;
|
||||||
|
let value = registers.get_pc_address();
|
||||||
|
if is_long {
|
||||||
|
push_common::do_push(registers, bus, &[
|
||||||
|
(value >> 16) as u8,
|
||||||
|
(value >> 8) as u8,
|
||||||
|
value as u8,
|
||||||
|
]);
|
||||||
|
} else {
|
||||||
|
push_common::do_push(registers, bus, &[
|
||||||
|
(value >> 8) as u8,
|
||||||
|
value as u8,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
registers.pc = effective_address as u16;
|
||||||
|
if is_long {
|
||||||
|
registers.pbr = (effective_address >> 16) as u8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Decode for JSR {
|
||||||
|
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String {
|
||||||
|
decoder_common::mnemonic_arithmetic(true, opcode, INSTR_NAME, self.addressing_mode, registers, bus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod cpu_instructions_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test() {
|
||||||
|
// Test a long address
|
||||||
|
let mut registers = Registers::new();
|
||||||
|
let mut bus = Bus::new();
|
||||||
|
registers.pc = 0x1234;
|
||||||
|
registers.pbr = 0x00;
|
||||||
|
registers.sp = 0x1FC;
|
||||||
|
bus.write(registers.get_pc_address() + 3, 0xAA);
|
||||||
|
bus.write(registers.get_pc_address() + 2, 0xBB);
|
||||||
|
bus.write(registers.get_pc_address() + 1, 0xCC);
|
||||||
|
// write next instruction
|
||||||
|
let instruction = JSR{addressing_mode: AddressingMode::AbsoluteLong};
|
||||||
|
instruction.execute(&mut registers, &mut bus);
|
||||||
|
assert_eq!(bus.read(0x1FC), 0x00);
|
||||||
|
assert_eq!(bus.read(0x1FB), 0x12);
|
||||||
|
assert_eq!(bus.read(0x1FA), 0x38); // we should store the NEXT instruction
|
||||||
|
assert_eq!(registers.pbr, 0xAA);
|
||||||
|
assert_eq!(registers.pc, 0xBBCC);
|
||||||
|
assert_eq!(registers.sp, 0x1F9);
|
||||||
|
assert_eq!(registers.cycles, 8);
|
||||||
|
assert!(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -32,6 +32,8 @@ pub mod eor;
|
|||||||
pub mod inc;
|
pub mod inc;
|
||||||
pub mod inx;
|
pub mod inx;
|
||||||
pub mod iny;
|
pub mod iny;
|
||||||
|
pub mod jmp;
|
||||||
|
pub mod jsr;
|
||||||
pub mod bit_common;
|
pub mod bit_common;
|
||||||
pub mod dec_common;
|
pub mod dec_common;
|
||||||
pub mod decoder_common;
|
pub mod decoder_common;
|
||||||
@@ -47,6 +49,16 @@ pub trait Decode {
|
|||||||
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String;
|
fn mnemonic(&self, registers: &Registers, bus: &Bus, opcode: u8) -> String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn get_effective_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u32 {
|
||||||
|
addressing_mode.effective_address(
|
||||||
|
bus,
|
||||||
|
registers.get_pc_address(),
|
||||||
|
registers.d,
|
||||||
|
registers.sp,
|
||||||
|
registers.x, registers.y,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn read_8bit_from_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u8 {
|
pub fn read_8bit_from_address(registers: &Registers, bus: &mut Bus, addressing_mode: AddressingMode) -> u8 {
|
||||||
match addressing_mode {
|
match addressing_mode {
|
||||||
AddressingMode::Accumulator => registers.a as u8,
|
AddressingMode::Accumulator => registers.a as u8,
|
||||||
|
|||||||
Reference in New Issue
Block a user