Cpu: Rewrite pipeline code.
Pipeline code was unreadable up until now, this also fixes a bug: * Some roms have illegal instructions right after branch instructions, and the cpu would error trying to decode them because of pipelining. Former-commit-id: e3201c7b0d2adfc772231a3e2d5909f43c17b50f
This commit is contained in:
parent
1f074e20ad
commit
7119ba2451
|
@ -2,7 +2,7 @@ use crate::bit::BitIndex;
|
||||||
|
|
||||||
use crate::arm7tdmi::alu::*;
|
use crate::arm7tdmi::alu::*;
|
||||||
use crate::arm7tdmi::bus::Bus;
|
use crate::arm7tdmi::bus::Bus;
|
||||||
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
|
use crate::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||||
use crate::arm7tdmi::exception::Exception;
|
use crate::arm7tdmi::exception::Exception;
|
||||||
use crate::arm7tdmi::psr::RegPSR;
|
use crate::arm7tdmi::psr::RegPSR;
|
||||||
use crate::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, DecodedInstruction, REG_PC};
|
use crate::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, DecodedInstruction, REG_PC};
|
||||||
|
@ -12,7 +12,7 @@ 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 Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
if !self.check_arm_cond(insn.cond) {
|
if !self.check_arm_cond(insn.cond) {
|
||||||
return Ok(CpuPipelineAction::IncPC);
|
return Ok(());
|
||||||
}
|
}
|
||||||
match insn.fmt {
|
match insn.fmt {
|
||||||
ArmFormat::BX => self.exec_bx(bus, insn),
|
ArmFormat::BX => self.exec_bx(bus, insn),
|
||||||
|
@ -43,8 +43,9 @@ impl Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
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();
|
||||||
|
|
||||||
Ok(CpuPipelineAction::Flush)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn branch_exchange(&mut self, mut addr: Addr) -> CpuExecResult {
|
pub fn branch_exchange(&mut self, mut addr: Addr) -> CpuExecResult {
|
||||||
|
@ -57,8 +58,9 @@ impl Core {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.pc = addr;
|
self.pc = addr;
|
||||||
|
self.flush_pipeline();
|
||||||
|
|
||||||
Ok(CpuPipelineAction::Flush)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cycles 2S+1N
|
/// Cycles 2S+1N
|
||||||
|
@ -68,7 +70,8 @@ impl Core {
|
||||||
|
|
||||||
fn exec_swi(&mut self, _bus: &mut Bus, _insn: ArmInstruction) -> CpuExecResult {
|
fn exec_swi(&mut self, _bus: &mut Bus, _insn: ArmInstruction) -> CpuExecResult {
|
||||||
self.exception(Exception::SoftwareInterrupt);
|
self.exception(Exception::SoftwareInterrupt);
|
||||||
Ok(CpuPipelineAction::Flush)
|
self.flush_pipeline();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_mrs(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_mrs(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
@ -83,7 +86,7 @@ impl Core {
|
||||||
self.cpsr.get()
|
self.cpsr.get()
|
||||||
};
|
};
|
||||||
self.set_reg(insn.rd(), result);
|
self.set_reg(insn.rd(), result);
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_msr_reg(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_msr_reg(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
@ -105,7 +108,7 @@ impl Core {
|
||||||
}
|
}
|
||||||
self.cpsr = new_psr;
|
self.cpsr = new_psr;
|
||||||
}
|
}
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_msr_flags(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_msr_flags(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
@ -122,7 +125,7 @@ impl Core {
|
||||||
} else {
|
} else {
|
||||||
self.cpsr.set_flag_bits(op);
|
self.cpsr.set_flag_bits(op);
|
||||||
}
|
}
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_operand2(&mut self, op2: BarrelShifterValue, set_flags: bool) -> CpuResult<u32> {
|
fn decode_operand2(&mut self, op2: BarrelShifterValue, set_flags: bool) -> CpuResult<u32> {
|
||||||
|
@ -155,7 +158,6 @@ impl Core {
|
||||||
fn exec_data_processing(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_data_processing(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
// TODO handle carry flag
|
// TODO handle carry flag
|
||||||
|
|
||||||
let mut pipeline_action = CpuPipelineAction::IncPC;
|
|
||||||
|
|
||||||
let op1 = if insn.rn() == REG_PC {
|
let op1 = if insn.rn() == REG_PC {
|
||||||
self.pc as i32 // prefething
|
self.pc as i32 // prefething
|
||||||
|
@ -186,11 +188,11 @@ impl Core {
|
||||||
if let Some(result) = self.alu(opcode, op1, op2, set_flags) {
|
if let Some(result) = self.alu(opcode, op1, op2, set_flags) {
|
||||||
self.set_reg(rd, result as u32);
|
self.set_reg(rd, result as u32);
|
||||||
if rd == REG_PC {
|
if rd == REG_PC {
|
||||||
pipeline_action = CpuPipelineAction::Flush;
|
self.flush_pipeline();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(pipeline_action)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Memory Load/Store
|
/// Memory Load/Store
|
||||||
|
@ -206,7 +208,6 @@ impl Core {
|
||||||
if writeback && insn.rd() == insn.rn() {
|
if writeback && insn.rd() == insn.rn() {
|
||||||
return Err(CpuError::IllegalInstruction);
|
return Err(CpuError::IllegalInstruction);
|
||||||
}
|
}
|
||||||
let mut pipeline_action = CpuPipelineAction::IncPC;
|
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -236,7 +237,7 @@ impl Core {
|
||||||
self.add_cycle();
|
self.add_cycle();
|
||||||
|
|
||||||
if insn.rd() == REG_PC {
|
if insn.rd() == REG_PC {
|
||||||
pipeline_action = CpuPipelineAction::Flush;
|
self.flush_pipeline();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let value = if insn.rd() == REG_PC {
|
let value = if insn.rd() == REG_PC {
|
||||||
|
@ -255,7 +256,7 @@ impl Core {
|
||||||
self.set_reg(insn.rn(), effective_addr as u32)
|
self.set_reg(insn.rn(), effective_addr as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(pipeline_action)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_ldr_str_hs(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_ldr_str_hs(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
@ -264,7 +265,6 @@ impl Core {
|
||||||
return Err(CpuError::IllegalInstruction);
|
return Err(CpuError::IllegalInstruction);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut pipeline_action = CpuPipelineAction::IncPC;
|
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -296,7 +296,7 @@ impl Core {
|
||||||
self.add_cycle();
|
self.add_cycle();
|
||||||
|
|
||||||
if insn.rd() == REG_PC {
|
if insn.rd() == REG_PC {
|
||||||
pipeline_action = CpuPipelineAction::Flush;
|
self.flush_pipeline();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
let value = if insn.rd() == REG_PC {
|
let value = if insn.rd() == REG_PC {
|
||||||
|
@ -317,7 +317,7 @@ impl Core {
|
||||||
self.set_reg(insn.rn(), effective_addr as u32)
|
self.set_reg(insn.rn(), effective_addr as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(pipeline_action)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_ldm_stm(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_ldm_stm(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
@ -326,7 +326,6 @@ impl Core {
|
||||||
let psr_user = insn.psr_and_force_user_flag();
|
let psr_user = insn.psr_and_force_user_flag();
|
||||||
let is_load = insn.load_flag();
|
let is_load = insn.load_flag();
|
||||||
let mut writeback = insn.write_back_flag();
|
let mut writeback = insn.write_back_flag();
|
||||||
let mut pipeline_action = CpuPipelineAction::IncPC;
|
|
||||||
let rn = insn.rn();
|
let rn = insn.rn();
|
||||||
let mut addr = self.gpr[rn] as i32;
|
let mut addr = self.gpr[rn] as i32;
|
||||||
|
|
||||||
|
@ -357,7 +356,7 @@ impl Core {
|
||||||
self.set_reg(r, val);
|
self.set_reg(r, val);
|
||||||
|
|
||||||
if r == REG_PC {
|
if r == REG_PC {
|
||||||
pipeline_action = CpuPipelineAction::Flush;
|
self.flush_pipeline();
|
||||||
}
|
}
|
||||||
|
|
||||||
if !full {
|
if !full {
|
||||||
|
@ -387,7 +386,7 @@ impl Core {
|
||||||
self.set_reg(rn, addr as u32);
|
self.set_reg(rn, addr as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(pipeline_action)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_mul_mla(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_mul_mla(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
@ -422,7 +421,7 @@ impl Core {
|
||||||
self.cpsr.set_Z(result == 0);
|
self.cpsr.set_Z(result == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_mull_mlal(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
fn exec_mull_mlal(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
||||||
|
@ -465,6 +464,6 @@ impl Core {
|
||||||
self.cpsr.set_Z(result == 0);
|
self.cpsr.set_Z(result == 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -374,10 +374,8 @@ mod tests {
|
||||||
assert_eq!(decoded.swi_comment(), 0x1337);
|
assert_eq!(decoded.swi_comment(), 0x1337);
|
||||||
assert_eq!(format!("{}", decoded), "swi\t#0x1337");
|
assert_eq!(format!("{}", decoded), "swi\t#0x1337");
|
||||||
|
|
||||||
assert_eq!(
|
core.exec_arm(&mut mem, decoded).unwrap();
|
||||||
core.exec_arm(&mut mem, decoded),
|
assert_eq!(core.did_pipeline_flush(), true);
|
||||||
Ok(CpuPipelineAction::Flush)
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(core.cpsr.mode(), CpuMode::Supervisor);
|
assert_eq!(core.cpsr.mode(), CpuMode::Supervisor);
|
||||||
assert_eq!(core.pc, Exception::SoftwareInterrupt as u32);
|
assert_eq!(core.pc, Exception::SoftwareInterrupt as u32);
|
||||||
|
@ -401,10 +399,8 @@ mod tests {
|
||||||
let bytes = vec![];
|
let bytes = vec![];
|
||||||
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
||||||
|
|
||||||
assert_eq!(
|
core.exec_arm(&mut mem, decoded).unwrap();
|
||||||
core.exec_arm(&mut mem, decoded),
|
assert_eq!(core.did_pipeline_flush(), true);
|
||||||
Ok(CpuPipelineAction::Flush)
|
|
||||||
);
|
|
||||||
assert_eq!(core.pc, 0x30);
|
assert_eq!(core.pc, 0x30);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -426,10 +422,8 @@ mod tests {
|
||||||
let bytes = vec![];
|
let bytes = vec![];
|
||||||
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
||||||
|
|
||||||
assert_eq!(
|
core.exec_arm(&mut mem, decoded).unwrap();
|
||||||
core.exec_arm(&mut mem, decoded),
|
assert_eq!(core.did_pipeline_flush(), true);
|
||||||
Ok(CpuPipelineAction::Flush)
|
|
||||||
);
|
|
||||||
assert_eq!(core.pc, 0x10);
|
assert_eq!(core.pc, 0x10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -470,10 +464,7 @@ mod tests {
|
||||||
];
|
];
|
||||||
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
||||||
|
|
||||||
assert_eq!(
|
core.exec_arm(&mut mem, decoded).unwrap();
|
||||||
core.exec_arm(&mut mem, decoded),
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
|
||||||
);
|
|
||||||
assert_eq!(core.gpr[2], 0x1337);
|
assert_eq!(core.gpr[2], 0x1337);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -514,10 +505,7 @@ mod tests {
|
||||||
];
|
];
|
||||||
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
||||||
|
|
||||||
assert_eq!(
|
core.exec_arm(&mut mem, decoded).unwrap();
|
||||||
core.exec_arm(&mut mem, decoded),
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
|
||||||
);
|
|
||||||
assert_eq!(mem.read_32(0), 0xabababab);
|
assert_eq!(mem.read_32(0), 0xabababab);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,10 +531,7 @@ mod tests {
|
||||||
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
||||||
|
|
||||||
assert_ne!(mem.read_32(core.get_reg(REG_SP) + 0x10), 0x12345678);
|
assert_ne!(mem.read_32(core.get_reg(REG_SP) + 0x10), 0x12345678);
|
||||||
assert_eq!(
|
core.exec_arm(&mut mem, decoded).unwrap();
|
||||||
core.exec_arm(&mut mem, decoded),
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
|
||||||
);
|
|
||||||
assert_eq!(mem.read_32(core.get_reg(REG_SP) + 0x10), 0x12345678);
|
assert_eq!(mem.read_32(core.get_reg(REG_SP) + 0x10), 0x12345678);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,49 +13,16 @@ use super::{
|
||||||
Addr, CpuMode, CpuResult, CpuState, DecodedInstruction, InstructionDecoder,
|
Addr, CpuMode, CpuResult, CpuState, DecodedInstruction, InstructionDecoder,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug, PartialEq)]
|
||||||
pub struct PipelineContext<D, N>
|
enum PipelineState {
|
||||||
where
|
Refill1,
|
||||||
D: InstructionDecoder,
|
Refill2,
|
||||||
N: Num,
|
Execute,
|
||||||
{
|
|
||||||
fetched: Option<(Addr, N)>,
|
|
||||||
decoded: Option<D>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D, N> Default for PipelineContext<D, N>
|
impl Default for PipelineState {
|
||||||
where
|
fn default() -> PipelineState {
|
||||||
D: InstructionDecoder,
|
PipelineState::Refill1
|
||||||
N: Num,
|
|
||||||
{
|
|
||||||
fn default() -> PipelineContext<D, N> {
|
|
||||||
PipelineContext {
|
|
||||||
fetched: None,
|
|
||||||
decoded: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<D, N> PipelineContext<D, N>
|
|
||||||
where
|
|
||||||
D: InstructionDecoder,
|
|
||||||
N: Num,
|
|
||||||
{
|
|
||||||
pub fn flush(&mut self) {
|
|
||||||
self.fetched = None;
|
|
||||||
self.decoded = None;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_flushed(&self) -> bool {
|
|
||||||
self.fetched.is_none() && self.decoded.is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_only_fetched(&self) -> bool {
|
|
||||||
self.fetched.is_some() && self.decoded.is_none()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn is_ready_to_execute(&self) -> bool {
|
|
||||||
self.fetched.is_some() && self.decoded.is_some()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -73,8 +40,13 @@ pub struct Core {
|
||||||
pub cpsr: RegPSR,
|
pub cpsr: RegPSR,
|
||||||
pub spsr: [RegPSR; 5],
|
pub spsr: [RegPSR; 5],
|
||||||
|
|
||||||
pub pipeline_arm: PipelineContext<ArmInstruction, u32>,
|
pipeline_state: PipelineState,
|
||||||
pub pipeline_thumb: PipelineContext<ThumbInstruction, u16>,
|
fetched_arm: u32,
|
||||||
|
decoded_arm: u32,
|
||||||
|
fetched_thumb: u16,
|
||||||
|
decoded_thumb: u16,
|
||||||
|
last_executed: Option<DecodedInstruction>,
|
||||||
|
|
||||||
pub cycles: usize,
|
pub cycles: usize,
|
||||||
|
|
||||||
// store the gpr before executing an instruction to show diff in the Display impl
|
// store the gpr before executing an instruction to show diff in the Display impl
|
||||||
|
@ -85,13 +57,7 @@ pub struct Core {
|
||||||
pub verbose: bool,
|
pub verbose: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, PartialEq)]
|
pub type CpuExecResult = CpuResult<()>;
|
||||||
pub enum CpuPipelineAction {
|
|
||||||
IncPC,
|
|
||||||
Flush,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type CpuExecResult = CpuResult<CpuPipelineAction>;
|
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
pub fn new() -> Core {
|
pub fn new() -> Core {
|
||||||
|
@ -274,119 +240,110 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step_thumb(
|
fn step_arm_exec(&mut self, insn: u32, sb: &mut Bus) -> CpuResult<()> {
|
||||||
&mut self,
|
let pc = self.pc;
|
||||||
bus: &mut Bus,
|
match self.pipeline_state {
|
||||||
) -> CpuResult<(Option<DecodedInstruction>, CpuPipelineAction)> {
|
PipelineState::Refill1 => {
|
||||||
|
self.pc = pc.wrapping_add(4);
|
||||||
|
self.pipeline_state = PipelineState::Refill2;
|
||||||
|
}
|
||||||
|
PipelineState::Refill2 => {
|
||||||
|
self.pc = pc.wrapping_add(4);
|
||||||
|
self.pipeline_state = PipelineState::Execute;
|
||||||
|
}
|
||||||
|
PipelineState::Execute => {
|
||||||
|
let insn = ArmInstruction::decode(insn, self.pc.wrapping_sub(8))?;
|
||||||
|
self.gpr_previous = self.get_registers();
|
||||||
|
self.exec_arm(sb, insn)?;
|
||||||
|
if !self.did_pipeline_flush() {
|
||||||
|
self.pc = pc.wrapping_add(4);
|
||||||
|
}
|
||||||
|
self.last_executed = Some(DecodedInstruction::Arm(insn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn arm(&mut self, sb: &mut Bus) -> CpuResult<()> {
|
||||||
|
let pc = self.pc;
|
||||||
|
|
||||||
// fetch
|
// fetch
|
||||||
// let new_fetched = bus.read_16(self.pc);
|
let fetched_now = self.load_32(pc, sb);
|
||||||
let new_fetched = self.load_16(self.pc, bus);
|
let executed_now = self.decoded_arm;
|
||||||
|
|
||||||
// decode
|
// decode
|
||||||
let new_decoded = match self.pipeline_thumb.fetched {
|
self.decoded_arm = self.fetched_arm;
|
||||||
Some((addr, i)) => {
|
self.fetched_arm = fetched_now;
|
||||||
let insn = ThumbInstruction::decode(i, addr)?;
|
|
||||||
Some(insn)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// exec
|
// execute
|
||||||
let result = match self.pipeline_thumb.decoded {
|
self.step_arm_exec(executed_now, sb)?;
|
||||||
Some(d) => {
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn did_pipeline_flush(&self) -> bool {
|
||||||
|
self.pipeline_state == PipelineState::Refill1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_thumb_exec(&mut self, insn: u16, sb: &mut Bus) -> CpuResult<()> {
|
||||||
|
let pc = self.pc;
|
||||||
|
match self.pipeline_state {
|
||||||
|
PipelineState::Refill1 => {
|
||||||
|
self.pc = pc.wrapping_add(2);
|
||||||
|
self.pipeline_state = PipelineState::Refill2;
|
||||||
|
}
|
||||||
|
PipelineState::Refill2 => {
|
||||||
|
self.pc = pc.wrapping_add(2);
|
||||||
|
self.pipeline_state = PipelineState::Execute;
|
||||||
|
}
|
||||||
|
PipelineState::Execute => {
|
||||||
|
let insn = ThumbInstruction::decode(insn, self.pc.wrapping_sub(4))?;
|
||||||
self.gpr_previous = self.get_registers();
|
self.gpr_previous = self.get_registers();
|
||||||
let action = self.exec_thumb(bus, d)?;
|
self.exec_thumb(sb, insn)?;
|
||||||
Ok((Some(DecodedInstruction::Thumb(d)), action))
|
if !self.did_pipeline_flush() {
|
||||||
|
self.pc = pc.wrapping_add(2);
|
||||||
}
|
}
|
||||||
None => Ok((None, CpuPipelineAction::IncPC)),
|
self.last_executed = Some(DecodedInstruction::Thumb(insn));
|
||||||
};
|
}
|
||||||
|
}
|
||||||
self.pipeline_thumb.fetched = Some((self.pc, new_fetched));
|
Ok(())
|
||||||
if let Some(d) = new_decoded {
|
|
||||||
self.pipeline_thumb.decoded = Some(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
fn thumb(&mut self, sb: &mut Bus) -> CpuResult<()> {
|
||||||
}
|
let pc = self.pc;
|
||||||
|
|
||||||
fn step_arm(
|
// fetch
|
||||||
&mut self,
|
let fetched_now = self.load_16(pc, sb);
|
||||||
bus: &mut Bus,
|
let executed_now = self.decoded_thumb;
|
||||||
) -> CpuResult<(Option<DecodedInstruction>, CpuPipelineAction)> {
|
|
||||||
// let new_fetched = bus.read_32(self.pc);
|
|
||||||
let new_fetched = self.load_32(self.pc, bus);
|
|
||||||
|
|
||||||
// decode
|
// decode
|
||||||
let new_decoded = match self.pipeline_arm.fetched {
|
self.decoded_thumb = self.fetched_thumb;
|
||||||
Some((addr, i)) => {
|
self.fetched_thumb = fetched_now;
|
||||||
let insn = ArmInstruction::decode(i, addr)?;
|
|
||||||
Some(insn)
|
|
||||||
}
|
|
||||||
None => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
// exec
|
// execute
|
||||||
let result = match self.pipeline_arm.decoded {
|
self.step_thumb_exec(executed_now, sb)?;
|
||||||
Some(d) => {
|
Ok(())
|
||||||
self.gpr_previous = self.get_registers();
|
|
||||||
let action = self.exec_arm(bus, d)?;
|
|
||||||
Ok((Some(DecodedInstruction::Arm(d)), action))
|
|
||||||
}
|
|
||||||
None => Ok((None, CpuPipelineAction::IncPC)),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.pipeline_arm.fetched = Some((self.pc, new_fetched));
|
|
||||||
if let Some(d) = new_decoded {
|
|
||||||
self.pipeline_arm.decoded = Some(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
result
|
pub fn flush_pipeline(&mut self) {
|
||||||
|
self.pipeline_state = PipelineState::Refill1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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<Option<DecodedInstruction>> {
|
pub fn step(&mut self, bus: &mut Bus) -> CpuResult<()> {
|
||||||
let (executed_instruction, pipeline_action) = match self.cpsr.state() {
|
match self.cpsr.state() {
|
||||||
CpuState::ARM => self.step_arm(bus),
|
CpuState::ARM => self.arm(bus),
|
||||||
CpuState::THUMB => self.step_thumb(bus),
|
CpuState::THUMB => self.thumb(bus),
|
||||||
}?;
|
|
||||||
|
|
||||||
match pipeline_action {
|
|
||||||
CpuPipelineAction::IncPC => self.advance_pc(),
|
|
||||||
CpuPipelineAction::Flush => {
|
|
||||||
self.pipeline_arm.flush();
|
|
||||||
self.pipeline_thumb.flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(executed_instruction)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Get's the address of the next instruction that is going to be executed
|
/// Get's the address of the next instruction that is going to be executed
|
||||||
pub fn get_next_pc(&self) -> Addr {
|
pub fn get_next_pc(&self) -> Addr {
|
||||||
match self.cpsr.state() {
|
let insn_size = self.word_size() as u32;
|
||||||
CpuState::ARM => {
|
match self.pipeline_state {
|
||||||
if self.pipeline_arm.is_flushed() {
|
PipelineState::Refill1 => self.pc,
|
||||||
self.pc as Addr
|
PipelineState::Refill2 => self.pc - insn_size,
|
||||||
} else if self.pipeline_arm.is_only_fetched() {
|
PipelineState::Execute => self.pc - 2 * insn_size,
|
||||||
self.pipeline_arm.fetched.unwrap().0
|
|
||||||
} else if self.pipeline_arm.is_ready_to_execute() {
|
|
||||||
self.pipeline_arm.decoded.unwrap().pc
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CpuState::THUMB => {
|
|
||||||
if self.pipeline_thumb.is_flushed() {
|
|
||||||
self.pc as Addr
|
|
||||||
} else if self.pipeline_thumb.is_only_fetched() {
|
|
||||||
self.pipeline_thumb.fetched.unwrap().0
|
|
||||||
} else if self.pipeline_thumb.is_ready_to_execute() {
|
|
||||||
self.pipeline_thumb.decoded.unwrap().pc
|
|
||||||
} else {
|
|
||||||
unreachable!()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -395,8 +352,14 @@ impl Core {
|
||||||
/// 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 Bus) -> CpuResult<DecodedInstruction> {
|
||||||
loop {
|
loop {
|
||||||
if let Some(i) = self.step(bus)? {
|
match self.pipeline_state {
|
||||||
return Ok(i);
|
PipelineState::Execute => {
|
||||||
|
self.step(bus)?;
|
||||||
|
return Ok(self.last_executed.unwrap());
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
self.step(bus)?;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,6 @@ impl Core {
|
||||||
|
|
||||||
// Set PC to vector address
|
// Set PC to vector address
|
||||||
self.pc = vector;
|
self.pc = vector;
|
||||||
self.pipeline_arm.flush();
|
self.flush_pipeline();
|
||||||
self.pipeline_thumb.flush();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::arm7tdmi::bus::Bus;
|
use crate::arm7tdmi::bus::Bus;
|
||||||
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
|
use crate::arm7tdmi::cpu::{Core, CpuExecResult};
|
||||||
use crate::arm7tdmi::*;
|
use crate::arm7tdmi::*;
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
@ -35,7 +35,7 @@ impl Core {
|
||||||
self.set_reg(rd, result as u32);
|
self.set_reg(rd, result as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_add_sub(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
fn exec_thumb_add_sub(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||||
|
@ -56,7 +56,7 @@ impl Core {
|
||||||
self.set_reg(insn.rd(), result as u32);
|
self.set_reg(insn.rd(), result as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_data_process_imm(
|
fn exec_thumb_data_process_imm(
|
||||||
|
@ -72,7 +72,7 @@ impl Core {
|
||||||
self.set_reg(insn.rd(), result as u32);
|
self.set_reg(insn.rd(), result as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_mul(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
fn exec_thumb_mul(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||||
|
@ -83,7 +83,7 @@ impl Core {
|
||||||
self.add_cycle();
|
self.add_cycle();
|
||||||
}
|
}
|
||||||
self.gpr[insn.rd()] = op1.wrapping_mul(op2) as u32;
|
self.gpr[insn.rd()] = op1.wrapping_mul(op2) as u32;
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_alu_ops(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
fn exec_thumb_alu_ops(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||||
|
@ -102,7 +102,7 @@ impl Core {
|
||||||
self.set_reg(rd, result as u32);
|
self.set_reg(rd, result as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Cycles 2S+1N
|
/// Cycles 2S+1N
|
||||||
|
@ -141,10 +141,10 @@ impl Core {
|
||||||
if let Some(result) = result {
|
if let Some(result) = result {
|
||||||
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 {
|
||||||
return Ok(CpuPipelineAction::Flush);
|
self.flush_pipeline();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ impl Core {
|
||||||
// +1I
|
// +1I
|
||||||
self.add_cycle();
|
self.add_cycle();
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_exec_thumb_ldr_str(
|
fn do_exec_thumb_ldr_str(
|
||||||
|
@ -185,7 +185,7 @@ impl Core {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_ldr_str_reg_offset(
|
fn exec_thumb_ldr_str_reg_offset(
|
||||||
|
@ -232,7 +232,7 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_ldr_str_imm_offset(
|
fn exec_thumb_ldr_str_imm_offset(
|
||||||
|
@ -263,7 +263,7 @@ impl Core {
|
||||||
} else {
|
} else {
|
||||||
self.store_16(addr, self.gpr[insn.rd()] as u16, bus);
|
self.store_16(addr, self.gpr[insn.rd()] as u16, bus);
|
||||||
}
|
}
|
||||||
Ok(CpuPipelineAction::IncPC)
|
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 Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||||
|
@ -279,7 +279,7 @@ impl Core {
|
||||||
};
|
};
|
||||||
self.gpr[insn.rd()] = result;
|
self.gpr[insn.rd()] = result;
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn do_exec_thumb_ldr_str_with_addr(
|
fn do_exec_thumb_ldr_str_with_addr(
|
||||||
|
@ -295,7 +295,7 @@ impl Core {
|
||||||
} else {
|
} else {
|
||||||
self.store_32(addr, self.gpr[insn.rd()], bus);
|
self.store_32(addr, self.gpr[insn.rd()], bus);
|
||||||
}
|
}
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_add_sp(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
fn exec_thumb_add_sp(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||||
|
@ -308,14 +308,13 @@ impl Core {
|
||||||
self.gpr[REG_SP] = result as u32;
|
self.gpr[REG_SP] = result as u32;
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_push_pop(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
fn exec_thumb_push_pop(&mut self, bus: &mut Bus, 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 mut pipeline_action = CpuPipelineAction::IncPC;
|
|
||||||
|
|
||||||
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();
|
||||||
|
@ -326,7 +325,7 @@ impl Core {
|
||||||
if pc_lr_flag {
|
if pc_lr_flag {
|
||||||
pop(self, bus, REG_PC);
|
pop(self, bus, REG_PC);
|
||||||
self.pc = self.pc & !1;
|
self.pc = self.pc & !1;
|
||||||
pipeline_action = CpuPipelineAction::Flush;
|
self.flush_pipeline();
|
||||||
}
|
}
|
||||||
self.add_cycle();
|
self.add_cycle();
|
||||||
} else {
|
} else {
|
||||||
|
@ -338,7 +337,7 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(pipeline_action)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_ldm_stm(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
fn exec_thumb_ldm_stm(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
||||||
|
@ -365,7 +364,7 @@ impl Core {
|
||||||
|
|
||||||
self.gpr[rb] = addr as u32;
|
self.gpr[rb] = addr as u32;
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_branch_with_cond(
|
fn exec_thumb_branch_with_cond(
|
||||||
|
@ -374,18 +373,20 @@ impl Core {
|
||||||
insn: ThumbInstruction,
|
insn: ThumbInstruction,
|
||||||
) -> CpuExecResult {
|
) -> CpuExecResult {
|
||||||
if !self.check_arm_cond(insn.cond()) {
|
if !self.check_arm_cond(insn.cond()) {
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
} else {
|
} else {
|
||||||
let offset = ((insn.offset8() as i8) << 1) as i32;
|
let offset = ((insn.offset8() as i8) << 1) as i32;
|
||||||
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
|
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
|
||||||
Ok(CpuPipelineAction::Flush)
|
self.flush_pipeline();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_branch(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
fn exec_thumb_branch(&mut self, _bus: &mut Bus, 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;
|
||||||
Ok(CpuPipelineAction::Flush)
|
self.flush_pipeline();
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exec_thumb_branch_long_with_link(
|
fn exec_thumb_branch_long_with_link(
|
||||||
|
@ -400,12 +401,13 @@ impl Core {
|
||||||
self.pc = (self.gpr[REG_LR] as i32).wrapping_add(off) as u32;
|
self.pc = (self.gpr[REG_LR] as i32).wrapping_add(off) as u32;
|
||||||
self.gpr[REG_LR] = next_pc;
|
self.gpr[REG_LR] = next_pc;
|
||||||
|
|
||||||
Ok(CpuPipelineAction::Flush)
|
self.flush_pipeline();
|
||||||
|
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;
|
||||||
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -355,17 +355,11 @@ impl ThumbInstruction {
|
||||||
/// All instructions constants were generated using an ARM assembler.
|
/// All instructions constants were generated using an ARM assembler.
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::arm7tdmi::{
|
use crate::arm7tdmi::{Core, Bus};
|
||||||
cpu::{Core, CpuPipelineAction},
|
|
||||||
Bus,
|
|
||||||
};
|
|
||||||
use crate::sysbus::BoxedMemory;
|
use crate::sysbus::BoxedMemory;
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn mov_low_reg() {
|
fn mov_low_reg() {
|
||||||
use crate::arm7tdmi::cpu::{Core, CpuPipelineAction};
|
|
||||||
use crate::sysbus::BoxedMemory;
|
|
||||||
|
|
||||||
let bytes = vec![];
|
let bytes = vec![];
|
||||||
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
|
||||||
let mut core = Core::new();
|
let mut core = Core::new();
|
||||||
|
@ -375,10 +369,7 @@ mod tests {
|
||||||
let insn = ThumbInstruction::decode(0x2027, 0).unwrap();
|
let insn = ThumbInstruction::decode(0x2027, 0).unwrap();
|
||||||
|
|
||||||
assert_eq!(format!("{}", insn), "mov\tr0, #0x27");
|
assert_eq!(format!("{}", insn), "mov\tr0, #0x27");
|
||||||
assert_eq!(
|
core.exec_thumb(&mut mem, insn).unwrap();
|
||||||
core.exec_thumb(&mut mem, insn),
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
|
||||||
);
|
|
||||||
assert_eq!(core.get_reg(0), 0x27);
|
assert_eq!(core.get_reg(0), 0x27);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -407,10 +398,7 @@ mod tests {
|
||||||
core.set_reg(0, 0);
|
core.set_reg(0, 0);
|
||||||
|
|
||||||
assert_eq!(format!("{}", insn), "ldr\tr0, [pc, #0x4] ; = #0xc");
|
assert_eq!(format!("{}", insn), "ldr\tr0, [pc, #0x4] ; = #0xc");
|
||||||
assert_eq!(
|
core.exec_thumb(&mut mem, insn).unwrap();
|
||||||
core.exec_thumb(&mut mem, insn),
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
|
||||||
);
|
|
||||||
assert_eq!(core.get_reg(0), 0x12345678);
|
assert_eq!(core.get_reg(0), 0x12345678);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -439,15 +427,9 @@ mod tests {
|
||||||
|
|
||||||
assert_eq!(format!("{}", str_insn), "str\tr0, [r4, r1]");
|
assert_eq!(format!("{}", str_insn), "str\tr0, [r4, r1]");
|
||||||
assert_eq!(format!("{}", ldr_insn), "ldrb\tr2, [r4, r1]");
|
assert_eq!(format!("{}", ldr_insn), "ldrb\tr2, [r4, r1]");
|
||||||
assert_eq!(
|
core.exec_thumb(&mut mem, str_insn).unwrap();
|
||||||
core.exec_thumb(&mut mem, str_insn),
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
|
||||||
);
|
|
||||||
assert_eq!(mem.read_32(0x10), 0x12345678);
|
assert_eq!(mem.read_32(0x10), 0x12345678);
|
||||||
assert_eq!(
|
core.exec_thumb(&mut mem, ldr_insn).unwrap();
|
||||||
core.exec_thumb(&mut mem, ldr_insn),
|
|
||||||
Ok(CpuPipelineAction::IncPC)
|
|
||||||
);
|
|
||||||
assert_eq!(core.get_reg(2), 0x78);
|
assert_eq!(core.get_reg(2), 0x78);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,9 +3,9 @@
|
||||||
use super::arm7tdmi::{exception::*, Core, DecodedInstruction};
|
use super::arm7tdmi::{exception::*, Core, DecodedInstruction};
|
||||||
use super::cartridge::Cartridge;
|
use super::cartridge::Cartridge;
|
||||||
use super::dma::DmaChannel;
|
use super::dma::DmaChannel;
|
||||||
|
use super::gpu::*;
|
||||||
use super::interrupt::*;
|
use super::interrupt::*;
|
||||||
use super::ioregs::consts::*;
|
use super::ioregs::consts::*;
|
||||||
use super::gpu::*;
|
|
||||||
use super::sysbus::SysBus;
|
use super::sysbus::SysBus;
|
||||||
|
|
||||||
use super::{EmuIoDev, GBAError, GBAResult};
|
use super::{EmuIoDev, GBAError, GBAResult};
|
||||||
|
|
|
@ -15,10 +15,10 @@ extern crate ansi_term;
|
||||||
extern crate colored; // not needed in Rust 2018
|
extern crate colored; // not needed in Rust 2018
|
||||||
|
|
||||||
pub mod arm7tdmi;
|
pub mod arm7tdmi;
|
||||||
pub mod gpu;
|
|
||||||
pub mod cartridge;
|
pub mod cartridge;
|
||||||
pub mod debugger;
|
pub mod debugger;
|
||||||
pub mod disass;
|
pub mod disass;
|
||||||
|
pub mod gpu;
|
||||||
pub mod sysbus;
|
pub mod sysbus;
|
||||||
pub use sysbus::SysBus;
|
pub use sysbus::SysBus;
|
||||||
pub mod interrupt;
|
pub mod interrupt;
|
||||||
|
|
Reference in a new issue