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:
Michel Heily 2019-07-20 14:44:49 +03:00
parent 1f074e20ad
commit 7119ba2451
8 changed files with 171 additions and 241 deletions

View file

@ -2,7 +2,7 @@ use crate::bit::BitIndex;
use crate::arm7tdmi::alu::*;
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::psr::RegPSR;
use crate::arm7tdmi::{Addr, CpuError, CpuMode, CpuResult, CpuState, DecodedInstruction, REG_PC};
@ -12,7 +12,7 @@ use super::*;
impl Core {
pub fn exec_arm(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
if !self.check_arm_cond(insn.cond) {
return Ok(CpuPipelineAction::IncPC);
return Ok(());
}
match insn.fmt {
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.flush_pipeline();
Ok(CpuPipelineAction::Flush)
Ok(())
}
pub fn branch_exchange(&mut self, mut addr: Addr) -> CpuExecResult {
@ -57,8 +58,9 @@ impl Core {
}
self.pc = addr;
self.flush_pipeline();
Ok(CpuPipelineAction::Flush)
Ok(())
}
/// Cycles 2S+1N
@ -68,7 +70,8 @@ impl Core {
fn exec_swi(&mut self, _bus: &mut Bus, _insn: ArmInstruction) -> CpuExecResult {
self.exception(Exception::SoftwareInterrupt);
Ok(CpuPipelineAction::Flush)
self.flush_pipeline();
Ok(())
}
fn exec_mrs(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
@ -83,7 +86,7 @@ impl Core {
self.cpsr.get()
};
self.set_reg(insn.rd(), result);
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_msr_reg(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
@ -105,7 +108,7 @@ impl Core {
}
self.cpsr = new_psr;
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_msr_flags(&mut self, _bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
@ -122,7 +125,7 @@ impl Core {
} else {
self.cpsr.set_flag_bits(op);
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
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 {
// TODO handle carry flag
let mut pipeline_action = CpuPipelineAction::IncPC;
let op1 = if insn.rn() == REG_PC {
self.pc as i32 // prefething
@ -186,11 +188,11 @@ impl Core {
if let Some(result) = self.alu(opcode, op1, op2, set_flags) {
self.set_reg(rd, result as u32);
if rd == REG_PC {
pipeline_action = CpuPipelineAction::Flush;
self.flush_pipeline();
}
}
Ok(pipeline_action)
Ok(())
}
/// Memory Load/Store
@ -206,7 +208,6 @@ impl Core {
if writeback && insn.rd() == insn.rn() {
return Err(CpuError::IllegalInstruction);
}
let mut pipeline_action = CpuPipelineAction::IncPC;
let mut addr = self.get_reg(insn.rn());
if insn.rn() == REG_PC {
@ -236,7 +237,7 @@ impl Core {
self.add_cycle();
if insn.rd() == REG_PC {
pipeline_action = CpuPipelineAction::Flush;
self.flush_pipeline();
}
} else {
let value = if insn.rd() == REG_PC {
@ -255,7 +256,7 @@ impl Core {
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 {
@ -264,7 +265,6 @@ impl Core {
return Err(CpuError::IllegalInstruction);
}
let mut pipeline_action = CpuPipelineAction::IncPC;
let mut addr = self.get_reg(insn.rn());
if insn.rn() == REG_PC {
@ -296,7 +296,7 @@ impl Core {
self.add_cycle();
if insn.rd() == REG_PC {
pipeline_action = CpuPipelineAction::Flush;
self.flush_pipeline();
}
} else {
let value = if insn.rd() == REG_PC {
@ -317,7 +317,7 @@ impl Core {
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 {
@ -326,7 +326,6 @@ impl Core {
let psr_user = insn.psr_and_force_user_flag();
let is_load = insn.load_flag();
let mut writeback = insn.write_back_flag();
let mut pipeline_action = CpuPipelineAction::IncPC;
let rn = insn.rn();
let mut addr = self.gpr[rn] as i32;
@ -357,7 +356,7 @@ impl Core {
self.set_reg(r, val);
if r == REG_PC {
pipeline_action = CpuPipelineAction::Flush;
self.flush_pipeline();
}
if !full {
@ -387,7 +386,7 @@ impl Core {
self.set_reg(rn, addr as u32);
}
Ok(pipeline_action)
Ok(())
}
fn exec_mul_mla(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
@ -422,7 +421,7 @@ impl Core {
self.cpsr.set_Z(result == 0);
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_mull_mlal(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
@ -465,6 +464,6 @@ impl Core {
self.cpsr.set_Z(result == 0);
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
}

View file

@ -374,10 +374,8 @@ mod tests {
assert_eq!(decoded.swi_comment(), 0x1337);
assert_eq!(format!("{}", decoded), "swi\t#0x1337");
assert_eq!(
core.exec_arm(&mut mem, decoded),
Ok(CpuPipelineAction::Flush)
);
core.exec_arm(&mut mem, decoded).unwrap();
assert_eq!(core.did_pipeline_flush(), true);
assert_eq!(core.cpsr.mode(), CpuMode::Supervisor);
assert_eq!(core.pc, Exception::SoftwareInterrupt as u32);
@ -401,10 +399,8 @@ mod tests {
let bytes = vec![];
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
assert_eq!(
core.exec_arm(&mut mem, decoded),
Ok(CpuPipelineAction::Flush)
);
core.exec_arm(&mut mem, decoded).unwrap();
assert_eq!(core.did_pipeline_flush(), true);
assert_eq!(core.pc, 0x30);
}
@ -426,10 +422,8 @@ mod tests {
let bytes = vec![];
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
assert_eq!(
core.exec_arm(&mut mem, decoded),
Ok(CpuPipelineAction::Flush)
);
core.exec_arm(&mut mem, decoded).unwrap();
assert_eq!(core.did_pipeline_flush(), true);
assert_eq!(core.pc, 0x10);
}
@ -470,10 +464,7 @@ mod tests {
];
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
assert_eq!(
core.exec_arm(&mut mem, decoded),
Ok(CpuPipelineAction::IncPC)
);
core.exec_arm(&mut mem, decoded).unwrap();
assert_eq!(core.gpr[2], 0x1337);
}
@ -514,10 +505,7 @@ mod tests {
];
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
assert_eq!(
core.exec_arm(&mut mem, decoded),
Ok(CpuPipelineAction::IncPC)
);
core.exec_arm(&mut mem, decoded).unwrap();
assert_eq!(mem.read_32(0), 0xabababab);
}
@ -543,10 +531,7 @@ mod tests {
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
assert_ne!(mem.read_32(core.get_reg(REG_SP) + 0x10), 0x12345678);
assert_eq!(
core.exec_arm(&mut mem, decoded),
Ok(CpuPipelineAction::IncPC)
);
core.exec_arm(&mut mem, decoded).unwrap();
assert_eq!(mem.read_32(core.get_reg(REG_SP) + 0x10), 0x12345678);
}
}

View file

@ -13,49 +13,16 @@ use super::{
Addr, CpuMode, CpuResult, CpuState, DecodedInstruction, InstructionDecoder,
};
#[derive(Debug)]
pub struct PipelineContext<D, N>
where
D: InstructionDecoder,
N: Num,
{
fetched: Option<(Addr, N)>,
decoded: Option<D>,
#[derive(Debug, PartialEq)]
enum PipelineState {
Refill1,
Refill2,
Execute,
}
impl<D, N> Default for PipelineContext<D, N>
where
D: InstructionDecoder,
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()
impl Default for PipelineState {
fn default() -> PipelineState {
PipelineState::Refill1
}
}
@ -73,8 +40,13 @@ pub struct Core {
pub cpsr: RegPSR,
pub spsr: [RegPSR; 5],
pub pipeline_arm: PipelineContext<ArmInstruction, u32>,
pub pipeline_thumb: PipelineContext<ThumbInstruction, u16>,
pipeline_state: PipelineState,
fetched_arm: u32,
decoded_arm: u32,
fetched_thumb: u16,
decoded_thumb: u16,
last_executed: Option<DecodedInstruction>,
pub cycles: usize,
// store the gpr before executing an instruction to show diff in the Display impl
@ -85,13 +57,7 @@ pub struct Core {
pub verbose: bool,
}
#[derive(Debug, PartialEq)]
pub enum CpuPipelineAction {
IncPC,
Flush,
}
pub type CpuExecResult = CpuResult<CpuPipelineAction>;
pub type CpuExecResult = CpuResult<()>;
impl Core {
pub fn new() -> Core {
@ -274,119 +240,110 @@ impl Core {
}
}
fn step_thumb(
&mut self,
bus: &mut Bus,
) -> CpuResult<(Option<DecodedInstruction>, CpuPipelineAction)> {
fn step_arm_exec(&mut self, insn: u32, sb: &mut Bus) -> CpuResult<()> {
let pc = self.pc;
match self.pipeline_state {
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
// let new_fetched = bus.read_16(self.pc);
let new_fetched = self.load_16(self.pc, bus);
let fetched_now = self.load_32(pc, sb);
let executed_now = self.decoded_arm;
// decode
let new_decoded = match self.pipeline_thumb.fetched {
Some((addr, i)) => {
let insn = ThumbInstruction::decode(i, addr)?;
Some(insn)
}
None => None,
};
self.decoded_arm = self.fetched_arm;
self.fetched_arm = fetched_now;
// exec
let result = match self.pipeline_thumb.decoded {
Some(d) => {
// execute
self.step_arm_exec(executed_now, sb)?;
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();
let action = self.exec_thumb(bus, d)?;
Ok((Some(DecodedInstruction::Thumb(d)), action))
self.exec_thumb(sb, insn)?;
if !self.did_pipeline_flush() {
self.pc = pc.wrapping_add(2);
}
None => Ok((None, CpuPipelineAction::IncPC)),
};
self.pipeline_thumb.fetched = Some((self.pc, new_fetched));
if let Some(d) = new_decoded {
self.pipeline_thumb.decoded = Some(d);
self.last_executed = Some(DecodedInstruction::Thumb(insn));
}
}
Ok(())
}
result
}
fn thumb(&mut self, sb: &mut Bus) -> CpuResult<()> {
let pc = self.pc;
fn step_arm(
&mut self,
bus: &mut Bus,
) -> CpuResult<(Option<DecodedInstruction>, CpuPipelineAction)> {
// let new_fetched = bus.read_32(self.pc);
let new_fetched = self.load_32(self.pc, bus);
// fetch
let fetched_now = self.load_16(pc, sb);
let executed_now = self.decoded_thumb;
// decode
let new_decoded = match self.pipeline_arm.fetched {
Some((addr, i)) => {
let insn = ArmInstruction::decode(i, addr)?;
Some(insn)
}
None => None,
};
self.decoded_thumb = self.fetched_thumb;
self.fetched_thumb = fetched_now;
// exec
let result = match self.pipeline_arm.decoded {
Some(d) => {
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);
// execute
self.step_thumb_exec(executed_now, sb)?;
Ok(())
}
result
pub fn flush_pipeline(&mut self) {
self.pipeline_state = PipelineState::Refill1;
}
/// Perform a pipeline step
/// If an instruction was executed in this step, return it.
pub fn step(&mut self, bus: &mut Bus) -> CpuResult<Option<DecodedInstruction>> {
let (executed_instruction, pipeline_action) = match self.cpsr.state() {
CpuState::ARM => self.step_arm(bus),
CpuState::THUMB => self.step_thumb(bus),
}?;
match pipeline_action {
CpuPipelineAction::IncPC => self.advance_pc(),
CpuPipelineAction::Flush => {
self.pipeline_arm.flush();
self.pipeline_thumb.flush();
pub fn step(&mut self, bus: &mut Bus) -> CpuResult<()> {
match self.cpsr.state() {
CpuState::ARM => self.arm(bus),
CpuState::THUMB => self.thumb(bus),
}
}
Ok(executed_instruction)
}
/// Get's the address of the next instruction that is going to be executed
pub fn get_next_pc(&self) -> Addr {
match self.cpsr.state() {
CpuState::ARM => {
if self.pipeline_arm.is_flushed() {
self.pc as Addr
} else if self.pipeline_arm.is_only_fetched() {
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!()
}
}
let insn_size = self.word_size() as u32;
match self.pipeline_state {
PipelineState::Refill1 => self.pc,
PipelineState::Refill2 => self.pc - insn_size,
PipelineState::Execute => self.pc - 2 * insn_size,
}
}
@ -395,8 +352,14 @@ impl Core {
/// and the address of the next instruction to be executed;
pub fn step_one(&mut self, bus: &mut Bus) -> CpuResult<DecodedInstruction> {
loop {
if let Some(i) = self.step(bus)? {
return Ok(i);
match self.pipeline_state {
PipelineState::Execute => {
self.step(bus)?;
return Ok(self.last_executed.unwrap());
}
_ => {
self.step(bus)?;
}
}
}
}

View file

@ -49,7 +49,6 @@ impl Core {
// Set PC to vector address
self.pc = vector;
self.pipeline_arm.flush();
self.pipeline_thumb.flush();
self.flush_pipeline();
}
}

View file

@ -1,5 +1,5 @@
use crate::arm7tdmi::bus::Bus;
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
use crate::arm7tdmi::cpu::{Core, CpuExecResult};
use crate::arm7tdmi::*;
use super::*;
@ -35,7 +35,7 @@ impl Core {
self.set_reg(rd, result as u32);
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
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);
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_thumb_data_process_imm(
@ -72,7 +72,7 @@ impl Core {
self.set_reg(insn.rd(), result as u32);
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_thumb_mul(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
@ -83,7 +83,7 @@ impl Core {
self.add_cycle();
}
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 {
@ -102,7 +102,7 @@ impl Core {
self.set_reg(rd, result as u32);
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
/// Cycles 2S+1N
@ -141,10 +141,10 @@ impl Core {
if let Some(result) = result {
self.set_reg(dst_reg, result as u32);
if dst_reg == REG_PC {
return Ok(CpuPipelineAction::Flush);
self.flush_pipeline();
}
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
}
@ -156,7 +156,7 @@ impl Core {
// +1I
self.add_cycle();
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn do_exec_thumb_ldr_str(
@ -185,7 +185,7 @@ impl Core {
};
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_thumb_ldr_str_reg_offset(
@ -232,7 +232,7 @@ impl Core {
}
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_thumb_ldr_str_imm_offset(
@ -263,7 +263,7 @@ impl Core {
} else {
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 {
@ -279,7 +279,7 @@ impl Core {
};
self.gpr[insn.rd()] = result;
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn do_exec_thumb_ldr_str_with_addr(
@ -295,7 +295,7 @@ impl Core {
} else {
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 {
@ -308,14 +308,13 @@ impl Core {
self.gpr[REG_SP] = result as u32;
}
Ok(CpuPipelineAction::IncPC)
Ok(())
}
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).
let is_pop = insn.is_load();
let mut pipeline_action = CpuPipelineAction::IncPC;
let pc_lr_flag = insn.flag(ThumbInstruction::FLAG_R);
let rlist = insn.register_list();
@ -326,7 +325,7 @@ impl Core {
if pc_lr_flag {
pop(self, bus, REG_PC);
self.pc = self.pc & !1;
pipeline_action = CpuPipelineAction::Flush;
self.flush_pipeline();
}
self.add_cycle();
} else {
@ -338,7 +337,7 @@ impl Core {
}
}
Ok(pipeline_action)
Ok(())
}
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;
Ok(CpuPipelineAction::IncPC)
Ok(())
}
fn exec_thumb_branch_with_cond(
@ -374,18 +373,20 @@ impl Core {
insn: ThumbInstruction,
) -> CpuExecResult {
if !self.check_arm_cond(insn.cond()) {
Ok(CpuPipelineAction::IncPC)
Ok(())
} else {
let offset = ((insn.offset8() as i8) << 1) as i32;
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 {
let offset = ((insn.offset11() << 21) >> 20) as i32;
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(
@ -400,12 +401,13 @@ impl Core {
self.pc = (self.gpr[REG_LR] as i32).wrapping_add(off) as u32;
self.gpr[REG_LR] = next_pc;
Ok(CpuPipelineAction::Flush)
self.flush_pipeline();
Ok(())
} else {
off = (off << 21) >> 9;
self.gpr[REG_LR] = (self.pc as i32).wrapping_add(off) as u32;
Ok(CpuPipelineAction::IncPC)
Ok(())
}
}

View file

@ -355,17 +355,11 @@ impl ThumbInstruction {
/// All instructions constants were generated using an ARM assembler.
mod tests {
use super::*;
use crate::arm7tdmi::{
cpu::{Core, CpuPipelineAction},
Bus,
};
use crate::arm7tdmi::{Core, Bus};
use crate::sysbus::BoxedMemory;
#[test]
fn mov_low_reg() {
use crate::arm7tdmi::cpu::{Core, CpuPipelineAction};
use crate::sysbus::BoxedMemory;
let bytes = vec![];
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
let mut core = Core::new();
@ -375,10 +369,7 @@ mod tests {
let insn = ThumbInstruction::decode(0x2027, 0).unwrap();
assert_eq!(format!("{}", insn), "mov\tr0, #0x27");
assert_eq!(
core.exec_thumb(&mut mem, insn),
Ok(CpuPipelineAction::IncPC)
);
core.exec_thumb(&mut mem, insn).unwrap();
assert_eq!(core.get_reg(0), 0x27);
}
@ -407,10 +398,7 @@ mod tests {
core.set_reg(0, 0);
assert_eq!(format!("{}", insn), "ldr\tr0, [pc, #0x4] ; = #0xc");
assert_eq!(
core.exec_thumb(&mut mem, insn),
Ok(CpuPipelineAction::IncPC)
);
core.exec_thumb(&mut mem, insn).unwrap();
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!("{}", ldr_insn), "ldrb\tr2, [r4, r1]");
assert_eq!(
core.exec_thumb(&mut mem, str_insn),
Ok(CpuPipelineAction::IncPC)
);
core.exec_thumb(&mut mem, str_insn).unwrap();
assert_eq!(mem.read_32(0x10), 0x12345678);
assert_eq!(
core.exec_thumb(&mut mem, ldr_insn),
Ok(CpuPipelineAction::IncPC)
);
core.exec_thumb(&mut mem, ldr_insn).unwrap();
assert_eq!(core.get_reg(2), 0x78);
}

View file

@ -3,9 +3,9 @@
use super::arm7tdmi::{exception::*, Core, DecodedInstruction};
use super::cartridge::Cartridge;
use super::dma::DmaChannel;
use super::gpu::*;
use super::interrupt::*;
use super::ioregs::consts::*;
use super::gpu::*;
use super::sysbus::SysBus;
use super::{EmuIoDev, GBAError, GBAResult};

View file

@ -15,10 +15,10 @@ extern crate ansi_term;
extern crate colored; // not needed in Rust 2018
pub mod arm7tdmi;
pub mod gpu;
pub mod cartridge;
pub mod debugger;
pub mod disass;
pub mod gpu;
pub mod sysbus;
pub use sysbus::SysBus;
pub mod interrupt;