cpu: Refactor instructions to use explicit cycle counting.
The way cycles were counted up untill now was not accurate enough, I've avoided doing so because the instruction implementation looks bloated this way, but I've had problems with cycle accuracy with tonc's timer demo. This is not entirely correct though, and I'm 100% sure there are some mistakes, but works good enough for now. Former-commit-id: 748faaf99fe2f42925c0a2110192c6a01e5d27d4
This commit is contained in:
parent
d86cc87c79
commit
acd0e4f338
|
@ -38,7 +38,6 @@ fn run_emulator(matches: &ArgMatches) -> GBAResult<()> {
|
|||
println!("loaded rom: {:#?}", gamepak.header);
|
||||
|
||||
let mut core = Core::new();
|
||||
core.reset();
|
||||
if skip_bios {
|
||||
core.gpr[13] = 0x0300_7f00;
|
||||
core.gpr_banked_r13[0] = 0x0300_7f00; // USR/SYS
|
||||
|
|
|
@ -240,6 +240,7 @@ impl Core {
|
|||
Ok(result)
|
||||
}
|
||||
ShiftRegisterBy::ByRegister(rs) => {
|
||||
self.add_cycle(); // +1I
|
||||
if shift.reg == REG_PC {
|
||||
val = val + 4; // PC prefetching
|
||||
}
|
||||
|
|
|
@ -4,25 +4,27 @@ use super::super::alu::*;
|
|||
use crate::core::arm7tdmi::bus::Bus;
|
||||
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||
use crate::core::arm7tdmi::psr::RegPSR;
|
||||
use crate::core::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, REG_PC};
|
||||
use crate::core::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, REG_LR, REG_PC};
|
||||
use crate::core::sysbus::SysBus;
|
||||
|
||||
use super::*;
|
||||
|
||||
impl Core {
|
||||
pub fn exec_arm(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
pub fn exec_arm(&mut self, bus: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
if !self.check_arm_cond(insn.cond) {
|
||||
self.S_cycle32(bus, self.pc);
|
||||
return Ok(());
|
||||
}
|
||||
match insn.fmt {
|
||||
ArmFormat::BX => self.exec_bx(bus, insn),
|
||||
ArmFormat::B_BL => self.exec_b_bl(bus, insn),
|
||||
ArmFormat::DP => self.exec_data_processing(bus, insn),
|
||||
ArmFormat::SWI => self.exec_swi(),
|
||||
ArmFormat::SWI => self.exec_swi(bus),
|
||||
ArmFormat::LDR_STR => self.exec_ldr_str(bus, insn),
|
||||
ArmFormat::LDR_STR_HS_IMM => self.exec_ldr_str_hs(bus, insn),
|
||||
ArmFormat::LDR_STR_HS_REG => self.exec_ldr_str_hs(bus, insn),
|
||||
ArmFormat::LDM_STM => self.exec_ldm_stm(bus, insn),
|
||||
ArmFormat::MRS => self.exec_mrs(insn),
|
||||
ArmFormat::MRS => self.exec_mrs(bus, insn),
|
||||
ArmFormat::MSR_REG => self.exec_msr_reg(bus, insn),
|
||||
ArmFormat::MSR_FLAGS => self.exec_msr_flags(bus, insn),
|
||||
ArmFormat::MUL_MLA => self.exec_mul_mla(bus, insn),
|
||||
|
@ -32,18 +34,23 @@ impl Core {
|
|||
}
|
||||
|
||||
/// Cycles 2S+1N
|
||||
fn exec_b_bl(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_b_bl(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
self.S_cycle32(sb, self.pc);
|
||||
if insn.link_flag() {
|
||||
self.set_reg(14, (insn.pc + (self.word_size() as u32)) & !0b1);
|
||||
self.set_reg(REG_LR, (insn.pc + (self.word_size() as u32)) & !0b1);
|
||||
}
|
||||
|
||||
self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32 & !1;
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn branch_exchange(&mut self, mut addr: Addr) -> CpuExecResult {
|
||||
pub fn branch_exchange(&mut self, sb: &mut SysBus, mut addr: Addr) -> CpuExecResult {
|
||||
match self.cpsr.state() {
|
||||
CpuState::ARM => self.S_cycle32(sb, self.pc),
|
||||
CpuState::THUMB => self.S_cycle16(sb, self.pc),
|
||||
}
|
||||
if addr.bit(0) {
|
||||
addr = addr & !0x1;
|
||||
self.cpsr.set_state(CpuState::THUMB);
|
||||
|
@ -53,17 +60,17 @@ impl Core {
|
|||
}
|
||||
|
||||
self.pc = addr;
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb); // +1S+1N
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cycles 2S+1N
|
||||
fn exec_bx(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
self.branch_exchange(self.get_reg(insn.rn()))
|
||||
fn exec_bx(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
self.branch_exchange(sb, self.get_reg(insn.rn()))
|
||||
}
|
||||
|
||||
fn exec_mrs(&mut self, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_mrs(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
let mode = self.cpsr.mode();
|
||||
let result = if insn.spsr_flag() {
|
||||
if let Some(index) = mode.spsr_index() {
|
||||
|
@ -75,14 +82,15 @@ impl Core {
|
|||
self.cpsr.get()
|
||||
};
|
||||
self.set_reg(insn.rd(), result);
|
||||
self.S_cycle32(sb, self.pc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_msr_reg(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
self.exec_msr(insn, self.get_reg(insn.rm()))
|
||||
fn exec_msr_reg(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
self.exec_msr(sb, insn, self.get_reg(insn.rm()))
|
||||
}
|
||||
|
||||
fn exec_msr(&mut self, insn: ArmInstruction, value: u32) -> CpuExecResult {
|
||||
fn exec_msr(&mut self, sb: &mut SysBus, insn: ArmInstruction, value: u32) -> CpuExecResult {
|
||||
let new_psr = RegPSR::new(value);
|
||||
let old_mode = self.cpsr.mode();
|
||||
if insn.spsr_flag() {
|
||||
|
@ -97,10 +105,12 @@ impl Core {
|
|||
}
|
||||
self.cpsr = new_psr;
|
||||
}
|
||||
self.S_cycle32(sb, self.pc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_msr_flags(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_msr_flags(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
self.S_cycle32(sb, self.pc);
|
||||
let op = insn.operand2()?;
|
||||
let op = self.decode_operand2(op, false)?;
|
||||
|
||||
|
@ -124,8 +134,6 @@ impl Core {
|
|||
Ok(result)
|
||||
}
|
||||
BarrelShifterValue::ShiftedRegister(x) => {
|
||||
// +1I
|
||||
self.add_cycle();
|
||||
let result = self.register_shift(x)?;
|
||||
if set_flags {
|
||||
self.cpsr.set_C(self.bs_carry_out);
|
||||
|
@ -151,7 +159,8 @@ impl Core {
|
|||
///
|
||||
/// Cycles: 1S+x+y (from GBATEK)
|
||||
/// Add x=1I cycles if Op2 shifted-by-register. Add y=1S+1N cycles if Rd=R15.
|
||||
fn exec_data_processing(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_data_processing(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
self.S_cycle32(sb, self.pc);
|
||||
let op1 = if insn.rn() == REG_PC {
|
||||
self.pc as i32
|
||||
} else {
|
||||
|
@ -167,9 +176,9 @@ impl Core {
|
|||
if !s_flag {
|
||||
match opcode {
|
||||
AluOpCode::TEQ | AluOpCode::CMN => {
|
||||
return self.exec_msr(insn, op2 as u32);
|
||||
return self.exec_msr(sb, insn, op2 as u32);
|
||||
}
|
||||
AluOpCode::TST | AluOpCode::CMP => return self.exec_mrs(insn),
|
||||
AluOpCode::TST | AluOpCode::CMP => return self.exec_mrs(sb, insn),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
@ -185,7 +194,7 @@ impl Core {
|
|||
if let Some(result) = alu_res {
|
||||
if rd == REG_PC {
|
||||
self.transfer_spsr_mode();
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
self.set_reg(rd, result as u32);
|
||||
}
|
||||
|
@ -200,16 +209,13 @@ impl Core {
|
|||
/// STR{cond}{B}{T} Rd,<Address> | 2N | ---- | [Rn+/-<offset>]=Rd
|
||||
/// ------------------------------------------------------------------------------
|
||||
/// For LDR, add y=1S+1N if Rd=R15.
|
||||
fn exec_ldr_str(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_ldr_str(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
let mut writeback = insn.write_back_flag();
|
||||
|
||||
let mut addr = self.get_reg(insn.rn());
|
||||
if insn.rn() == REG_PC {
|
||||
addr = insn.pc + 8; // prefetching
|
||||
}
|
||||
|
||||
let offset = self.get_barrel_shifted_value(insn.ldr_str_offset());
|
||||
|
||||
let effective_addr = (addr as i32).wrapping_add(offset) as Addr;
|
||||
addr = if insn.pre_index_flag() {
|
||||
effective_addr
|
||||
|
@ -220,12 +226,14 @@ impl Core {
|
|||
if writeback && insn.rd() == insn.rn() {
|
||||
writeback = false;
|
||||
}
|
||||
|
||||
if insn.load_flag() {
|
||||
self.S_cycle32(sb, self.pc);
|
||||
let data = if insn.transfer_size() == 1 {
|
||||
self.load_8(addr, bus) as u32
|
||||
self.N_cycle8(sb, addr);
|
||||
sb.read_8(addr) as u32
|
||||
} else {
|
||||
self.ldr_word(addr, bus)
|
||||
self.N_cycle32(sb, addr);
|
||||
self.ldr_word(addr, sb)
|
||||
};
|
||||
|
||||
self.set_reg(insn.rd(), data);
|
||||
|
@ -234,7 +242,7 @@ impl Core {
|
|||
self.add_cycle();
|
||||
|
||||
if insn.rd() == REG_PC {
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
} else {
|
||||
let value = if insn.rd() == REG_PC {
|
||||
|
@ -243,10 +251,13 @@ impl Core {
|
|||
self.get_reg(insn.rd())
|
||||
};
|
||||
if insn.transfer_size() == 1 {
|
||||
self.store_8(addr, value as u8, bus);
|
||||
self.N_cycle8(sb, addr);
|
||||
sb.write_8(addr, value as u8);
|
||||
} else {
|
||||
self.store_32(addr & !0x3, value, bus);
|
||||
self.N_cycle32(sb, addr);
|
||||
sb.write_32(addr & !0x3, value);
|
||||
};
|
||||
self.N_cycle32(sb, self.pc);
|
||||
}
|
||||
|
||||
if writeback {
|
||||
|
@ -256,7 +267,7 @@ impl Core {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_ldr_str_hs(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_ldr_str_hs(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
let mut writeback = insn.write_back_flag();
|
||||
|
||||
let mut addr = self.get_reg(insn.rn());
|
||||
|
@ -277,10 +288,20 @@ impl Core {
|
|||
writeback = false;
|
||||
}
|
||||
if insn.load_flag() {
|
||||
self.S_cycle32(sb, self.pc);
|
||||
let data = match insn.halfword_data_transfer_type().unwrap() {
|
||||
ArmHalfwordTransferType::SignedByte => self.load_8(addr, bus) as u8 as i8 as u32,
|
||||
ArmHalfwordTransferType::SignedHalfwords => self.ldr_sign_half(addr, bus),
|
||||
ArmHalfwordTransferType::UnsignedHalfwords => self.ldr_half(addr, bus),
|
||||
ArmHalfwordTransferType::SignedByte => {
|
||||
self.N_cycle8(sb, addr);
|
||||
sb.read_8(addr) as u8 as i8 as u32
|
||||
}
|
||||
ArmHalfwordTransferType::SignedHalfwords => {
|
||||
self.N_cycle16(sb, addr);
|
||||
self.ldr_sign_half(addr, sb)
|
||||
}
|
||||
ArmHalfwordTransferType::UnsignedHalfwords => {
|
||||
self.N_cycle16(sb, addr);
|
||||
self.ldr_half(addr, sb)
|
||||
}
|
||||
};
|
||||
|
||||
self.set_reg(insn.rd(), data);
|
||||
|
@ -289,7 +310,7 @@ impl Core {
|
|||
self.add_cycle();
|
||||
|
||||
if insn.rd() == REG_PC {
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
} else {
|
||||
let value = if insn.rd() == REG_PC {
|
||||
|
@ -300,7 +321,9 @@ impl Core {
|
|||
|
||||
match insn.halfword_data_transfer_type().unwrap() {
|
||||
ArmHalfwordTransferType::UnsignedHalfwords => {
|
||||
self.store_16(addr, value as u16, bus)
|
||||
self.N_cycle32(sb, addr);
|
||||
sb.write_16(addr, value as u16);
|
||||
self.N_cycle32(sb, self.pc);
|
||||
}
|
||||
_ => panic!("invalid HS flags for L=0"),
|
||||
};
|
||||
|
@ -313,7 +336,7 @@ impl Core {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_ldm_stm(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_ldm_stm(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
let full = insn.pre_index_flag();
|
||||
let ascending = insn.add_offset_flag();
|
||||
let psr_user_flag = insn.psr_and_force_user_flag();
|
||||
|
@ -347,6 +370,8 @@ impl Core {
|
|||
let psr_transfer = psr_user_flag & is_load & rlist.bit(REG_PC);
|
||||
|
||||
if is_load {
|
||||
self.add_cycle();
|
||||
self.N_cycle32(sb, self.pc);
|
||||
for r in 0..16 {
|
||||
let r = if ascending { r } else { 15 - r };
|
||||
if rlist.bit(r) {
|
||||
|
@ -357,8 +382,8 @@ impl Core {
|
|||
addr = addr.wrapping_add(step);
|
||||
}
|
||||
|
||||
self.add_cycle();
|
||||
let val = self.load_32(addr as Addr, bus);
|
||||
let val = sb.read_32(addr as Addr);
|
||||
self.S_cycle32(sb, self.pc);
|
||||
if user_bank_transfer {
|
||||
self.set_reg_user(r, val);
|
||||
} else {
|
||||
|
@ -369,7 +394,7 @@ impl Core {
|
|||
if psr_transfer {
|
||||
self.transfer_spsr_mode();
|
||||
}
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
|
||||
if !full {
|
||||
|
@ -378,6 +403,7 @@ impl Core {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
let mut first = true;
|
||||
for r in 0..16 {
|
||||
let r = if ascending { r } else { 15 - r };
|
||||
if rlist.bit(r) {
|
||||
|
@ -394,13 +420,20 @@ impl Core {
|
|||
self.get_reg(r)
|
||||
}
|
||||
};
|
||||
self.store_32(addr as Addr, val, bus);
|
||||
if first {
|
||||
self.N_cycle32(sb, addr as u32);
|
||||
first = false;
|
||||
} else {
|
||||
self.S_cycle32(sb, addr as u32);
|
||||
}
|
||||
sb.write_32(addr as Addr, val);
|
||||
|
||||
if !full {
|
||||
addr = addr.wrapping_add(step);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.N_cycle32(sb, self.pc);
|
||||
}
|
||||
|
||||
if writeback {
|
||||
|
@ -410,7 +443,7 @@ impl Core {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_mul_mla(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_mul_mla(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
let (rd, rn, rs, rm) = (insn.rd(), insn.rn(), insn.rs(), insn.rm());
|
||||
|
||||
// check validity
|
||||
|
@ -444,10 +477,11 @@ impl Core {
|
|||
self.cpsr.set_V(false);
|
||||
}
|
||||
|
||||
self.S_cycle32(sb, self.pc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_mull_mlal(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_mull_mlal(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
let (rd_hi, rd_lo, rn, rs, rm) =
|
||||
(insn.rd_hi(), insn.rd_lo(), insn.rn(), insn.rs(), insn.rm());
|
||||
|
||||
|
@ -491,21 +525,27 @@ impl Core {
|
|||
self.cpsr.set_V(false);
|
||||
}
|
||||
|
||||
self.S_cycle32(sb, self.pc);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_arm_swp(&mut self, sb: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||
fn exec_arm_swp(&mut self, sb: &mut SysBus, insn: ArmInstruction) -> CpuExecResult {
|
||||
let base_addr = self.get_reg(insn.rn());
|
||||
self.add_cycle();
|
||||
if insn.transfer_size() == 1 {
|
||||
let t = self.load_8(base_addr, sb);
|
||||
self.store_8(base_addr, self.get_reg(insn.rm()) as u8, sb);
|
||||
let t = sb.read_8(base_addr);
|
||||
self.N_cycle8(sb, base_addr);
|
||||
sb.write_8(base_addr, self.get_reg(insn.rm()) as u8);
|
||||
self.S_cycle8(sb, base_addr);
|
||||
self.set_reg(insn.rd(), t as u32);
|
||||
} else {
|
||||
let t = self.load_32(base_addr, sb);
|
||||
self.store_32(base_addr, self.get_reg(insn.rm()), sb);
|
||||
let t = sb.read_32(base_addr);
|
||||
self.N_cycle32(sb, base_addr);
|
||||
sb.write_32(base_addr, self.get_reg(insn.rm()));
|
||||
self.S_cycle32(sb, base_addr);
|
||||
self.set_reg(insn.rd(), t as u32);
|
||||
}
|
||||
|
||||
self.N_cycle32(sb, self.pc);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,12 +4,12 @@ use ansi_term::{Colour, Style};
|
|||
|
||||
pub use super::exception::Exception;
|
||||
use super::{
|
||||
arm::*,
|
||||
bus::{Bus, MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*},
|
||||
psr::RegPSR,
|
||||
reg_string,
|
||||
thumb::ThumbInstruction,
|
||||
Addr, CpuMode, CpuResult, CpuState, DecodedInstruction, InstructionDecoder,
|
||||
arm::*, bus::Bus, psr::RegPSR, reg_string, thumb::ThumbInstruction, Addr, CpuMode, CpuResult,
|
||||
CpuState, DecodedInstruction, InstructionDecoder,
|
||||
};
|
||||
|
||||
use super::super::sysbus::{
|
||||
MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*, SysBus,
|
||||
};
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
|
@ -125,33 +125,33 @@ impl Core {
|
|||
}
|
||||
|
||||
/// Helper function for "ldr" instruction that handles misaligned addresses
|
||||
pub fn ldr_word(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||
pub fn ldr_word(&mut self, addr: Addr, bus: &SysBus) -> u32 {
|
||||
if addr & 0x3 != 0 {
|
||||
let rotation = (addr & 0x3) << 3;
|
||||
let value = self.load_32(addr & !0x3, bus);
|
||||
let value = bus.read_32(addr & !0x3);
|
||||
self.ror(value, rotation, self.cpsr.C(), false, false)
|
||||
} else {
|
||||
self.load_32(addr, bus)
|
||||
bus.read_32(addr)
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function for "ldrh" instruction that handles misaligned addresses
|
||||
pub fn ldr_half(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||
pub fn ldr_half(&mut self, addr: Addr, bus: &SysBus) -> u32 {
|
||||
if addr & 0x1 != 0 {
|
||||
let rotation = (addr & 0x1) << 3;
|
||||
let value = self.load_16(addr & !0x1, bus);
|
||||
let value = bus.read_16(addr & !0x1);
|
||||
self.ror(value as u32, rotation, self.cpsr.C(), false, false)
|
||||
} else {
|
||||
self.load_16(addr, bus) as u32
|
||||
bus.read_16(addr) as u32
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function for "ldrsh" instruction that handles misaligned addresses
|
||||
pub fn ldr_sign_half(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||
pub fn ldr_sign_half(&mut self, addr: Addr, bus: &SysBus) -> u32 {
|
||||
if addr & 0x1 != 0 {
|
||||
self.load_8(addr, bus) as i8 as i32 as u32
|
||||
bus.read_8(addr) as i8 as i32 as u32
|
||||
} else {
|
||||
self.load_16(addr, bus) as i16 as i32 as u32
|
||||
bus.read_16(addr) as i16 as i32 as u32
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -194,8 +194,8 @@ impl Core {
|
|||
}
|
||||
|
||||
/// Resets the cpu
|
||||
pub fn reset(&mut self) {
|
||||
self.exception(Exception::Reset);
|
||||
pub fn reset(&mut self, sb: &mut SysBus) {
|
||||
self.exception(sb, Exception::Reset);
|
||||
}
|
||||
|
||||
pub fn word_size(&self) -> usize {
|
||||
|
@ -214,9 +214,10 @@ impl Core {
|
|||
self.cycles += 1;
|
||||
}
|
||||
|
||||
pub fn add_cycles(&mut self, addr: Addr, bus: &Bus, access: MemoryAccess) {
|
||||
// println!("<cycle {:#x} {}> total: {}", addr, access, self.cycles);
|
||||
self.cycles += bus.get_cycles(addr, access);
|
||||
pub fn add_cycles(&mut self, addr: Addr, bus: &SysBus, access: MemoryAccess) {
|
||||
let cycles_to_add = 1 + bus.get_cycles(addr, access);
|
||||
// println!("<cycle {:#x} {}> took: {}", addr, access, cycles_to_add);
|
||||
self.cycles += cycles_to_add;
|
||||
}
|
||||
|
||||
pub fn cycle_type(&self, addr: Addr) -> MemoryAccessType {
|
||||
|
@ -239,45 +240,40 @@ impl Core {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn load_32(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||
self.add_cycles(addr, bus, self.cycle_type(addr) + MemoryAccess32);
|
||||
self.memreq = addr;
|
||||
bus.read_32(addr)
|
||||
#[allow(non_snake_case)]
|
||||
pub fn S_cycle32(&mut self, sb: &SysBus, addr: u32) {
|
||||
self.cycles += 1;
|
||||
self.cycles += sb.get_cycles(addr, Seq + MemoryAccess32);
|
||||
}
|
||||
|
||||
pub fn load_16(&mut self, addr: Addr, bus: &Bus) -> u16 {
|
||||
let cycle_type = self.cycle_type(addr);
|
||||
self.add_cycles(addr, bus, cycle_type + MemoryAccess16);
|
||||
self.memreq = addr;
|
||||
bus.read_16(addr)
|
||||
#[allow(non_snake_case)]
|
||||
pub fn S_cycle16(&mut self, sb: &SysBus, addr: u32) {
|
||||
self.cycles += 1;
|
||||
self.cycles += sb.get_cycles(addr, Seq + MemoryAccess16);
|
||||
}
|
||||
|
||||
pub fn load_8(&mut self, addr: Addr, bus: &Bus) -> u8 {
|
||||
let cycle_type = self.cycle_type(addr);
|
||||
self.add_cycles(addr, bus, cycle_type + MemoryAccess8);
|
||||
self.memreq = addr;
|
||||
bus.read_8(addr)
|
||||
#[allow(non_snake_case)]
|
||||
pub fn S_cycle8(&mut self, sb: &SysBus, addr: u32) {
|
||||
self.cycles += 1;
|
||||
self.cycles += sb.get_cycles(addr, Seq + MemoryAccess8);
|
||||
}
|
||||
|
||||
pub fn store_32(&mut self, addr: Addr, value: u32, bus: &mut Bus) {
|
||||
let cycle_type = self.cycle_type(addr);
|
||||
self.add_cycles(addr, bus, cycle_type + MemoryAccess32);
|
||||
self.memreq = addr;
|
||||
bus.write_32(addr, value);
|
||||
#[allow(non_snake_case)]
|
||||
pub fn N_cycle32(&mut self, sb: &SysBus, addr: u32) {
|
||||
self.cycles += 1;
|
||||
self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess32);
|
||||
}
|
||||
|
||||
pub fn store_16(&mut self, addr: Addr, value: u16, bus: &mut Bus) {
|
||||
let cycle_type = self.cycle_type(addr);
|
||||
self.add_cycles(addr, bus, cycle_type + MemoryAccess16);
|
||||
self.memreq = addr;
|
||||
bus.write_16(addr, value);
|
||||
#[allow(non_snake_case)]
|
||||
pub fn N_cycle16(&mut self, sb: &SysBus, addr: u32) {
|
||||
self.cycles += 1;
|
||||
self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess16);
|
||||
}
|
||||
|
||||
pub fn store_8(&mut self, addr: Addr, value: u8, bus: &mut Bus) {
|
||||
let cycle_type = self.cycle_type(addr);
|
||||
self.add_cycles(addr, bus, cycle_type + MemoryAccess8);
|
||||
self.memreq = addr;
|
||||
bus.write_8(addr, value);
|
||||
#[allow(non_snake_case)]
|
||||
pub fn N_cycle8(&mut self, sb: &SysBus, addr: u32) {
|
||||
self.cycles += 1;
|
||||
self.cycles += sb.get_cycles(addr, NonSeq + MemoryAccess8);
|
||||
}
|
||||
|
||||
pub fn check_arm_cond(&self, cond: ArmCond) -> bool {
|
||||
|
@ -301,13 +297,16 @@ impl Core {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn exec_swi(&mut self) -> CpuExecResult {
|
||||
self.exception(Exception::SoftwareInterrupt);
|
||||
self.flush_pipeline();
|
||||
pub fn exec_swi(&mut self, sb: &mut SysBus) -> CpuExecResult {
|
||||
match self.cpsr.state() {
|
||||
CpuState::ARM => self.N_cycle32(sb, self.pc),
|
||||
CpuState::THUMB => self.N_cycle16(sb, self.pc),
|
||||
};
|
||||
self.exception(sb, Exception::SoftwareInterrupt);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn step_arm_exec(&mut self, insn: u32, sb: &mut Bus) -> CpuResult<()> {
|
||||
fn step_arm_exec(&mut self, insn: u32, sb: &mut SysBus) -> CpuResult<()> {
|
||||
let pc = self.pc;
|
||||
match self.pipeline_state {
|
||||
PipelineState::Refill1 => {
|
||||
|
@ -331,11 +330,11 @@ impl Core {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn arm(&mut self, sb: &mut Bus) -> CpuResult<()> {
|
||||
fn arm(&mut self, sb: &mut SysBus) -> CpuResult<()> {
|
||||
let pc = self.pc;
|
||||
|
||||
// fetch
|
||||
let fetched_now = self.load_32(pc, sb);
|
||||
let fetched_now = sb.read_32(pc);
|
||||
let executed_now = self.decoded_arm;
|
||||
|
||||
// decode
|
||||
|
@ -351,7 +350,7 @@ impl Core {
|
|||
self.pipeline_state == PipelineState::Refill1
|
||||
}
|
||||
|
||||
fn step_thumb_exec(&mut self, insn: u16, sb: &mut Bus) -> CpuResult<()> {
|
||||
fn step_thumb_exec(&mut self, insn: u16, sb: &mut SysBus) -> CpuResult<()> {
|
||||
let pc = self.pc;
|
||||
match self.pipeline_state {
|
||||
PipelineState::Refill1 => {
|
||||
|
@ -375,11 +374,11 @@ impl Core {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn thumb(&mut self, sb: &mut Bus) -> CpuResult<()> {
|
||||
fn thumb(&mut self, sb: &mut SysBus) -> CpuResult<()> {
|
||||
let pc = self.pc;
|
||||
|
||||
// fetch
|
||||
let fetched_now = self.load_16(pc, sb);
|
||||
let fetched_now = sb.read_16(pc);
|
||||
let executed_now = self.decoded_thumb;
|
||||
|
||||
// decode
|
||||
|
@ -391,13 +390,23 @@ impl Core {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
pub fn flush_pipeline(&mut self) {
|
||||
pub fn flush_pipeline(&mut self, sb: &mut SysBus) {
|
||||
self.pipeline_state = PipelineState::Refill1;
|
||||
match self.cpsr.state() {
|
||||
CpuState::ARM => {
|
||||
self.N_cycle32(sb, self.pc);
|
||||
self.S_cycle32(sb, self.pc + 4);
|
||||
}
|
||||
CpuState::THUMB => {
|
||||
self.N_cycle16(sb, self.pc);
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Perform a pipeline step
|
||||
/// If an instruction was executed in this step, return it.
|
||||
pub fn step(&mut self, bus: &mut Bus) -> CpuResult<()> {
|
||||
pub fn step(&mut self, bus: &mut SysBus) -> CpuResult<()> {
|
||||
match self.cpsr.state() {
|
||||
CpuState::ARM => self.arm(bus),
|
||||
CpuState::THUMB => self.thumb(bus),
|
||||
|
@ -417,7 +426,7 @@ impl Core {
|
|||
/// A step that returns only once an instruction was executed.
|
||||
/// Returns the address of PC before executing an instruction,
|
||||
/// and the address of the next instruction to be executed;
|
||||
pub fn step_one(&mut self, bus: &mut Bus) -> CpuResult<DecodedInstruction> {
|
||||
pub fn step_one(&mut self, bus: &mut SysBus) -> CpuResult<DecodedInstruction> {
|
||||
loop {
|
||||
match self.pipeline_state {
|
||||
PipelineState::Execute => {
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
use super::super::sysbus::SysBus;
|
||||
use super::cpu::Core;
|
||||
use super::REG_LR;
|
||||
use super::{cpu::Core, CpuMode, CpuState};
|
||||
use super::{CpuMode, CpuState};
|
||||
use colored::*;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq)]
|
||||
|
@ -31,7 +33,7 @@ impl From<Exception> for CpuMode {
|
|||
}
|
||||
|
||||
impl Core {
|
||||
pub fn exception(&mut self, e: Exception) {
|
||||
pub fn exception(&mut self, sb: &mut SysBus, e: Exception) {
|
||||
let vector = e as u32;
|
||||
let new_mode = CpuMode::from(e);
|
||||
if self.verbose {
|
||||
|
@ -58,6 +60,6 @@ impl Core {
|
|||
|
||||
// Set PC to vector address
|
||||
self.pc = vector;
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,16 +1,17 @@
|
|||
use crate::core::arm7tdmi::bus::Bus;
|
||||
use crate::core::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||
use crate::core::arm7tdmi::*;
|
||||
use crate::core::sysbus::SysBus;
|
||||
|
||||
use super::*;
|
||||
fn push(cpu: &mut Core, bus: &mut Bus, r: usize) {
|
||||
fn push(cpu: &mut Core, bus: &mut SysBus, r: usize) {
|
||||
cpu.gpr[REG_SP] -= 4;
|
||||
let stack_addr = cpu.gpr[REG_SP];
|
||||
cpu.store_32(stack_addr, cpu.get_reg(r), bus)
|
||||
bus.write_32(stack_addr, cpu.get_reg(r))
|
||||
}
|
||||
fn pop(cpu: &mut Core, bus: &mut Bus, r: usize) {
|
||||
fn pop(cpu: &mut Core, bus: &mut SysBus, r: usize) {
|
||||
let stack_addr = cpu.gpr[REG_SP];
|
||||
let val = cpu.load_32(stack_addr, bus);
|
||||
let val = bus.read_32(stack_addr);
|
||||
cpu.set_reg(r, val);
|
||||
cpu.gpr[REG_SP] = stack_addr + 4;
|
||||
}
|
||||
|
@ -18,7 +19,7 @@ fn pop(cpu: &mut Core, bus: &mut Bus, r: usize) {
|
|||
impl Core {
|
||||
fn exec_thumb_move_shifted_reg(
|
||||
&mut self,
|
||||
_bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
let op2 = self
|
||||
|
@ -37,11 +38,11 @@ impl Core {
|
|||
if let Some(result) = result {
|
||||
self.set_reg(rd, result as u32);
|
||||
}
|
||||
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_add_sub(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_add_sub(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let op1 = self.get_reg(insn.rs()) as i32;
|
||||
let op2 = if insn.is_immediate_operand() {
|
||||
insn.rn() as u32 as i32
|
||||
|
@ -58,13 +59,13 @@ impl Core {
|
|||
if let Some(result) = result {
|
||||
self.set_reg(insn.rd(), result as u32);
|
||||
}
|
||||
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_data_process_imm(
|
||||
&mut self,
|
||||
_bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
let arm_alu_op: AluOpCode = insn.format3_op().into();
|
||||
|
@ -74,11 +75,11 @@ impl Core {
|
|||
if let Some(result) = result {
|
||||
self.set_reg(insn.rd(), result as u32);
|
||||
}
|
||||
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_mul(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_mul(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let op1 = self.get_reg(insn.rd()) as i32;
|
||||
let op2 = self.get_reg(insn.rs()) as i32;
|
||||
let m = self.get_required_multipiler_array_cycles(op2);
|
||||
|
@ -89,10 +90,11 @@ impl Core {
|
|||
self.cpsr.set_N((result as i32) < 0);
|
||||
self.cpsr.set_Z(result == 0);
|
||||
self.gpr[insn.rd()] = result;
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_alu_ops(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let rd = insn.rd();
|
||||
|
||||
let (arm_alu_op, shft) = insn.alu_opcode();
|
||||
|
@ -107,27 +109,27 @@ impl Core {
|
|||
if let Some(result) = result {
|
||||
self.set_reg(rd, result as u32);
|
||||
}
|
||||
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Cycles 2S+1N
|
||||
fn exec_thumb_bx(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_bx(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) {
|
||||
insn.rs() + 8
|
||||
} else {
|
||||
insn.rs()
|
||||
};
|
||||
self.branch_exchange(self.get_reg(src_reg))
|
||||
self.branch_exchange(sb, self.get_reg(src_reg))
|
||||
}
|
||||
|
||||
fn exec_thumb_hi_reg_op_or_bx(
|
||||
&mut self,
|
||||
bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
if OpFormat5::BX == insn.format5_op() {
|
||||
self.exec_thumb_bx(bus, insn)
|
||||
self.exec_thumb_bx(sb, insn)
|
||||
} else {
|
||||
let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) {
|
||||
insn.rd() + 8
|
||||
|
@ -151,16 +153,19 @@ impl Core {
|
|||
if let Some(result) = alu_res {
|
||||
self.set_reg(dst_reg, result as u32);
|
||||
if dst_reg == REG_PC {
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
}
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_thumb_ldr_pc(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_ldr_pc(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let addr = (insn.pc & !0b10) + 4 + (insn.word8() as Addr);
|
||||
let data = self.load_32(addr, bus);
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
let data = sb.read_32(addr);
|
||||
self.N_cycle16(sb, addr);
|
||||
|
||||
self.set_reg(insn.rd(), data);
|
||||
// +1I
|
||||
|
@ -171,15 +176,17 @@ impl Core {
|
|||
|
||||
fn do_exec_thumb_ldr_str(
|
||||
&mut self,
|
||||
bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
addr: Addr,
|
||||
) -> CpuExecResult {
|
||||
if insn.is_load() {
|
||||
let data = if insn.is_transferring_bytes() {
|
||||
self.load_8(addr, bus) as u32
|
||||
self.S_cycle8(sb, addr);
|
||||
sb.read_8(addr) as u32
|
||||
} else {
|
||||
self.ldr_word(addr, bus)
|
||||
self.S_cycle32(sb, addr);
|
||||
self.ldr_word(addr, sb)
|
||||
};
|
||||
|
||||
self.set_reg(insn.rd(), data);
|
||||
|
@ -189,18 +196,21 @@ impl Core {
|
|||
} else {
|
||||
let value = self.get_reg(insn.rd());
|
||||
if insn.is_transferring_bytes() {
|
||||
self.store_8(addr, value as u8, bus);
|
||||
self.N_cycle8(sb, addr);
|
||||
sb.write_8(addr, value as u8);
|
||||
} else {
|
||||
self.store_32(addr, value, bus);
|
||||
self.N_cycle32(sb, addr);
|
||||
sb.write_32(addr, value);
|
||||
};
|
||||
}
|
||||
|
||||
self.N_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_ldr_str_reg_offset(
|
||||
&mut self,
|
||||
bus: &mut Bus,
|
||||
bus: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
let addr = self
|
||||
|
@ -209,7 +219,7 @@ impl Core {
|
|||
self.do_exec_thumb_ldr_str(bus, insn, addr)
|
||||
}
|
||||
|
||||
fn exec_thumb_ldr_str_shb(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_ldr_str_shb(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let addr = self
|
||||
.get_reg(insn.rb())
|
||||
.wrapping_add(self.get_reg(insn.ro()));
|
||||
|
@ -221,33 +231,41 @@ impl Core {
|
|||
(false, false) =>
|
||||
/* strh */
|
||||
{
|
||||
self.store_16(addr, self.gpr[rd] as u16, bus)
|
||||
sb.write_16(addr, self.gpr[rd] as u16);
|
||||
self.N_cycle16(sb, addr);
|
||||
}
|
||||
(false, true) =>
|
||||
/* ldrh */
|
||||
{
|
||||
self.gpr[rd] = self.ldr_half(addr, bus)
|
||||
self.gpr[rd] = self.ldr_half(addr, sb);
|
||||
self.S_cycle16(sb, addr);
|
||||
self.add_cycle();
|
||||
}
|
||||
(true, false) =>
|
||||
/* ldsb */
|
||||
{
|
||||
let val = self.load_8(addr, bus) as i8 as i32 as u32;
|
||||
let val = sb.read_8(addr) as i8 as i32 as u32;
|
||||
self.gpr[rd] = val;
|
||||
self.S_cycle8(sb, addr);
|
||||
self.add_cycle();
|
||||
}
|
||||
(true, true) =>
|
||||
/* ldsh */
|
||||
{
|
||||
let val = self.ldr_sign_half(addr, bus);
|
||||
let val = self.ldr_sign_half(addr, sb);
|
||||
self.gpr[rd] = val;
|
||||
self.S_cycle16(sb, addr);
|
||||
self.add_cycle();
|
||||
}
|
||||
}
|
||||
|
||||
self.N_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_ldr_str_imm_offset(
|
||||
&mut self,
|
||||
bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
let offset = if insn.is_transferring_bytes() {
|
||||
|
@ -256,92 +274,115 @@ impl Core {
|
|||
(insn.offset5() << 3) >> 1
|
||||
};
|
||||
let addr = self.get_reg(insn.rb()).wrapping_add(offset as u32);
|
||||
self.do_exec_thumb_ldr_str(bus, insn, addr)
|
||||
self.do_exec_thumb_ldr_str(sb, insn, addr)
|
||||
}
|
||||
|
||||
fn exec_thumb_ldr_str_halfword(
|
||||
&mut self,
|
||||
bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
let base = self.gpr[insn.rb()] as i32;
|
||||
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
|
||||
if insn.is_load() {
|
||||
let data = self.ldr_half(addr, bus);
|
||||
let data = self.ldr_half(addr, sb);
|
||||
self.S_cycle16(sb, addr);
|
||||
self.add_cycle();
|
||||
self.gpr[insn.rd()] = data as u32;
|
||||
} else {
|
||||
self.store_16(addr, self.gpr[insn.rd()] as u16, bus);
|
||||
sb.write_16(addr, self.gpr[insn.rd()] as u16);
|
||||
self.N_cycle16(sb, addr);
|
||||
}
|
||||
self.N_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_ldr_str_sp(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_ldr_str_sp(&mut self, bus: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let addr = self.gpr[REG_SP] + (insn.word8() as Addr);
|
||||
self.do_exec_thumb_ldr_str_with_addr(bus, insn, addr)
|
||||
}
|
||||
|
||||
fn exec_thumb_load_address(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_load_address(
|
||||
&mut self,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
let result = if insn.flag(ThumbInstruction::FLAG_SP) {
|
||||
self.gpr[REG_SP] + (insn.word8() as Addr)
|
||||
} else {
|
||||
(insn.pc & !0b10) + 4 + (insn.word8() as Addr)
|
||||
};
|
||||
self.gpr[insn.rd()] = result;
|
||||
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn do_exec_thumb_ldr_str_with_addr(
|
||||
&mut self,
|
||||
bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
addr: Addr,
|
||||
) -> CpuExecResult {
|
||||
if insn.is_load() {
|
||||
let data = self.load_32(addr, bus);
|
||||
let data = sb.read_32(addr);
|
||||
self.S_cycle16(sb, addr);
|
||||
self.add_cycle();
|
||||
self.gpr[insn.rd()] = data;
|
||||
} else {
|
||||
self.store_32(addr, self.gpr[insn.rd()], bus);
|
||||
sb.write_32(addr, self.gpr[insn.rd()]);
|
||||
self.N_cycle16(sb, addr);
|
||||
}
|
||||
self.N_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_add_sp(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_add_sp(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let op1 = self.gpr[REG_SP] as i32;
|
||||
let op2 = insn.sword7();
|
||||
let arm_alu_op = AluOpCode::ADD;
|
||||
|
||||
self.gpr[REG_SP] = self.alu(arm_alu_op, op1, op2) as u32;
|
||||
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_push_pop(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_push_pop(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
// (From GBATEK) Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH).
|
||||
let is_pop = insn.is_load();
|
||||
let pc_lr_flag = insn.flag(ThumbInstruction::FLAG_R);
|
||||
let rlist = insn.register_list();
|
||||
self.N_cycle16(sb, self.pc);
|
||||
let mut first = true;
|
||||
if is_pop {
|
||||
for r in 0..8 {
|
||||
if rlist.bit(r) {
|
||||
pop(self, bus, r);
|
||||
pop(self, sb, r);
|
||||
if first {
|
||||
self.add_cycle();
|
||||
first = false;
|
||||
} else {
|
||||
self.S_cycle16(sb, self.gpr[REG_SP]);
|
||||
}
|
||||
}
|
||||
}
|
||||
if pc_lr_flag {
|
||||
pop(self, bus, REG_PC);
|
||||
pop(self, sb, REG_PC);
|
||||
self.pc = self.pc & !1;
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
self.add_cycle();
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
} else {
|
||||
if pc_lr_flag {
|
||||
push(self, bus, REG_LR);
|
||||
push(self, sb, REG_LR);
|
||||
}
|
||||
for r in (0..8).rev() {
|
||||
if rlist.bit(r) {
|
||||
push(self, bus, r);
|
||||
push(self, sb, r);
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
self.S_cycle16(sb, self.gpr[REG_SP]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -349,7 +390,7 @@ impl Core {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_ldm_stm(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_ldm_stm(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
// (From GBATEK) Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH).
|
||||
|
||||
let is_load = insn.is_load();
|
||||
|
@ -357,19 +398,33 @@ impl Core {
|
|||
|
||||
let mut addr = self.gpr[rb];
|
||||
let rlist = insn.register_list();
|
||||
self.N_cycle16(sb, self.pc);
|
||||
let mut first = true;
|
||||
if is_load {
|
||||
for r in 0..8 {
|
||||
if rlist.bit(r) {
|
||||
let val = self.load_32(addr, bus);
|
||||
let val = sb.read_32(addr);
|
||||
if first {
|
||||
first = false;
|
||||
self.add_cycle();
|
||||
} else {
|
||||
self.S_cycle16(sb, addr);
|
||||
}
|
||||
addr += 4;
|
||||
self.add_cycle();
|
||||
self.set_reg(r, val);
|
||||
}
|
||||
}
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
} else {
|
||||
for r in 0..8 {
|
||||
if rlist.bit(r) {
|
||||
self.store_32(addr, self.gpr[r], bus);
|
||||
if first {
|
||||
first = false;
|
||||
} else {
|
||||
self.S_cycle16(sb, addr);
|
||||
}
|
||||
sb.write_32(addr, self.gpr[r]);
|
||||
addr += 4;
|
||||
}
|
||||
}
|
||||
|
@ -382,49 +437,53 @@ impl Core {
|
|||
|
||||
fn exec_thumb_branch_with_cond(
|
||||
&mut self,
|
||||
_bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
if !self.check_arm_cond(insn.cond()) {
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
Ok(())
|
||||
} else {
|
||||
let offset = insn.bcond_offset();
|
||||
self.S_cycle16(sb, self.pc);
|
||||
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn exec_thumb_branch(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
fn exec_thumb_branch(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let offset = ((insn.offset11() << 21) >> 20) as i32;
|
||||
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
|
||||
self.flush_pipeline();
|
||||
self.S_cycle16(sb, self.pc);
|
||||
self.flush_pipeline(sb);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn exec_thumb_branch_long_with_link(
|
||||
&mut self,
|
||||
_bus: &mut Bus,
|
||||
sb: &mut SysBus,
|
||||
insn: ThumbInstruction,
|
||||
) -> CpuExecResult {
|
||||
let mut off = insn.offset11();
|
||||
if insn.flag(ThumbInstruction::FLAG_LOW_OFFSET) {
|
||||
self.S_cycle16(sb, self.pc);
|
||||
off = off << 1;
|
||||
let next_pc = (self.pc - 2) | 1;
|
||||
self.pc = ((self.gpr[REG_LR] & !1) as i32).wrapping_add(off) as u32;
|
||||
self.gpr[REG_LR] = next_pc;
|
||||
|
||||
self.flush_pipeline();
|
||||
self.flush_pipeline(sb);
|
||||
Ok(())
|
||||
} else {
|
||||
off = (off << 21) >> 9;
|
||||
self.gpr[REG_LR] = (self.pc as i32).wrapping_add(off) as u32;
|
||||
|
||||
self.S_cycle16(sb, self.pc);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn exec_thumb(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
match insn.fmt {
|
||||
ThumbFormat::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn),
|
||||
ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn),
|
||||
|
@ -443,7 +502,7 @@ impl Core {
|
|||
ThumbFormat::PushPop => self.exec_thumb_push_pop(bus, insn),
|
||||
ThumbFormat::LdmStm => self.exec_thumb_ldm_stm(bus, insn),
|
||||
ThumbFormat::BranchConditional => self.exec_thumb_branch_with_cond(bus, insn),
|
||||
ThumbFormat::Swi => self.exec_swi(),
|
||||
ThumbFormat::Swi => self.exec_swi(bus),
|
||||
ThumbFormat::Branch => self.exec_thumb_branch(bus, insn),
|
||||
ThumbFormat::BranchLongWithLink => self.exec_thumb_branch_long_with_link(bus, insn),
|
||||
}
|
||||
|
|
|
@ -93,7 +93,7 @@ impl GameBoyAdvance {
|
|||
if !self.cpu.cpsr.irq_disabled() {
|
||||
io.intc.request_irqs(irqs);
|
||||
if io.intc.irq_pending() {
|
||||
self.cpu.exception(Exception::Irq);
|
||||
self.cpu.exception(&mut self.sysbus, Exception::Irq);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ impl GameBoyAdvance {
|
|||
|
||||
self.emulate_peripherals(cycles);
|
||||
|
||||
if self.io.borrow().gpu.state == GpuState::HBlank {
|
||||
if self.io.borrow().gpu.state == GpuState::VBlank {
|
||||
self.backend.render(self.io.borrow().gpu.render());
|
||||
}
|
||||
|
||||
|
|
|
@ -350,8 +350,8 @@ impl Gpu {
|
|||
}
|
||||
}
|
||||
BGMode::BGMode2 => {
|
||||
self.scanline_mode0(2, sb);
|
||||
self.scanline_mode0(3, sb);
|
||||
self.scanline_mode0(2, sb);
|
||||
}
|
||||
BGMode::BGMode3 => {
|
||||
self.scanline_mode3(2, sb);
|
||||
|
|
|
@ -3,8 +3,8 @@ use std::rc::Rc;
|
|||
|
||||
use super::arm7tdmi::{Addr, Bus};
|
||||
use super::gba::IoDevices;
|
||||
use super::sysbus::BoxedMemory;
|
||||
use super::keypad;
|
||||
use super::sysbus::BoxedMemory;
|
||||
|
||||
pub mod consts {
|
||||
use super::*;
|
||||
|
@ -188,9 +188,7 @@ impl Bus for IoRegs {
|
|||
REG_POSTFLG => self.post_boot_flag as u16,
|
||||
REG_HALTCNT => 0,
|
||||
REG_KEYINPUT => self.keyinput as u16,
|
||||
_ => {
|
||||
self.mem.read_16(addr)
|
||||
}
|
||||
_ => self.mem.read_16(addr),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -232,39 +232,33 @@ impl SysBus {
|
|||
|
||||
// TODO handle EWRAM accesses
|
||||
match addr & 0xff000000 {
|
||||
EWRAM_ADDR => {
|
||||
match access.1 {
|
||||
MemoryAccessWidth::MemoryAccess32 => cycles += 6,
|
||||
_ => cycles += 3
|
||||
}
|
||||
}
|
||||
EWRAM_ADDR => match access.1 {
|
||||
MemoryAccessWidth::MemoryAccess32 => cycles += 6,
|
||||
_ => cycles += 3,
|
||||
},
|
||||
OAM_ADDR | VRAM_ADDR | PALRAM_ADDR => {
|
||||
match access.1 {
|
||||
MemoryAccessWidth::MemoryAccess32 => cycles += 2,
|
||||
_ => cycles += 1
|
||||
_ => cycles += 1,
|
||||
}
|
||||
if self.io.borrow().gpu.state == GpuState::HDraw {
|
||||
cycles += 1;
|
||||
}
|
||||
}
|
||||
GAMEPAK_WS0_ADDR => {
|
||||
match access.0 {
|
||||
MemoryAccessType::NonSeq => {
|
||||
match access.1 {
|
||||
MemoryAccessWidth::MemoryAccess32 => {
|
||||
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
|
||||
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||
}
|
||||
_ => {
|
||||
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
MemoryAccessType::Seq => {
|
||||
GAMEPAK_WS0_ADDR => match access.0 {
|
||||
MemoryAccessType::NonSeq => match access.1 {
|
||||
MemoryAccessWidth::MemoryAccess32 => {
|
||||
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
|
||||
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||
}
|
||||
_ => {
|
||||
cycles += nonseq_cycles[self.ioregs.waitcnt.ws0_first_access() as usize];
|
||||
}
|
||||
},
|
||||
MemoryAccessType::Seq => {
|
||||
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||
if access.1 == MemoryAccessWidth::MemoryAccess32 {
|
||||
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||
if access.1 == MemoryAccessWidth::MemoryAccess32 {
|
||||
cycles += seq_cycles[self.ioregs.waitcnt.ws0_second_access() as usize];
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -49,61 +49,61 @@ impl Command {
|
|||
DisplayInfo => println!("GPU: {:#?}", debugger.gba.io.borrow().gpu),
|
||||
Step(count) => {
|
||||
for _ in 0..count {
|
||||
if let Some(bp) = debugger.check_breakpoint() {
|
||||
println!("hit breakpoint #0x{:08x}!", bp);
|
||||
debugger.delete_breakpoint(bp);
|
||||
} else {
|
||||
match debugger.gba.step() {
|
||||
Ok(insn) => {
|
||||
print!(
|
||||
"{}\t{}",
|
||||
Colour::Black
|
||||
.bold()
|
||||
.italic()
|
||||
.on(Colour::White)
|
||||
.paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)),
|
||||
insn
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
Colour::Purple.dimmed().italic().paint(format!(
|
||||
"\t\t/// Next instruction at @0x{:08x}",
|
||||
debugger.gba.cpu.get_next_pc()
|
||||
))
|
||||
)
|
||||
}
|
||||
Err(GBAError::CpuError(e)) => {
|
||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||
println!("cpu: {:x?}", debugger.gba.cpu)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
match debugger.gba.step() {
|
||||
Ok(insn) => {
|
||||
print!(
|
||||
"{}\t{}",
|
||||
Colour::Black
|
||||
.bold()
|
||||
.italic()
|
||||
.on(Colour::White)
|
||||
.paint(format!("Executed at @0x{:08x}:", insn.get_pc(),)),
|
||||
insn
|
||||
);
|
||||
println!(
|
||||
"{}",
|
||||
Colour::Purple.dimmed().italic().paint(format!(
|
||||
"\t\t/// Next instruction at @0x{:08x}",
|
||||
debugger.gba.cpu.get_next_pc()
|
||||
))
|
||||
)
|
||||
}
|
||||
Err(GBAError::CpuError(e)) => {
|
||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||
println!("cpu: {:x?}", debugger.gba.cpu)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
println!("{}\n", debugger.gba.cpu);
|
||||
}
|
||||
Continue => loop {
|
||||
if let Some(bp) = debugger.check_breakpoint() {
|
||||
println!("hit breakpoint #0x{:08x}!", bp);
|
||||
debugger.delete_breakpoint(bp);
|
||||
break;
|
||||
}
|
||||
match debugger.gba.step() {
|
||||
// Ok(insn) => {
|
||||
// println!(
|
||||
// "@0x{:08x}:\t{}",
|
||||
// insn.get_pc(),
|
||||
// Colour::Yellow.italic().paint(format!("{} ", insn))
|
||||
// );
|
||||
// }
|
||||
Err(GBAError::CpuError(e)) => {
|
||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||
println!("cpu: {:x?}", debugger.gba.cpu);
|
||||
Continue => {
|
||||
let start_cycles = debugger.gba.cpu.cycles();
|
||||
loop {
|
||||
if let Some(bp) = debugger.check_breakpoint() {
|
||||
match debugger.gba.step() {
|
||||
Err(GBAError::CpuError(e)) => {
|
||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||
println!("cpu: {:x?}", debugger.gba.cpu);
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
let num_cycles = debugger.gba.cpu.cycles() - start_cycles;
|
||||
println!("hit breakpoint #0x{:08x} after {} cycles !", bp, num_cycles);
|
||||
break;
|
||||
} else {
|
||||
match debugger.gba.step() {
|
||||
Err(GBAError::CpuError(e)) => {
|
||||
println!("{}: {}", "cpu encountered an error".red(), e);
|
||||
println!("cpu: {:x?}", debugger.gba.cpu);
|
||||
break;
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
}
|
||||
_ => (),
|
||||
};
|
||||
},
|
||||
}
|
||||
}
|
||||
Frame(count) => {
|
||||
use super::time::PreciseTime;
|
||||
let start = PreciseTime::now();
|
||||
|
@ -160,7 +160,7 @@ impl Command {
|
|||
TileView(bg) => create_tile_view(bg, &debugger.gba),
|
||||
Reset => {
|
||||
println!("resetting cpu...");
|
||||
debugger.gba.cpu.reset();
|
||||
debugger.gba.cpu.reset(&mut debugger.gba.sysbus);
|
||||
println!("cpu is restarted!")
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue