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