Implement thumb format3 instruction and add a test for it.

Former-commit-id: 8cf6664027dc3d5dbeb6d2ca3d089820baac2709
This commit is contained in:
Michel Heily 2019-07-03 01:26:48 +03:00
parent 4011911cca
commit b82874809f
6 changed files with 106 additions and 14 deletions

View file

@ -162,7 +162,7 @@ impl Core {
res res
} }
fn alu(&mut self, opcode: ArmOpCode, op1: i32, op2: i32, set_cond_flags: bool) -> Option<i32> { pub fn alu(&mut self, opcode: ArmOpCode, op1: i32, op2: i32, set_cond_flags: bool) -> Option<i32> {
let C = self.cpsr.C() as i32; let C = self.cpsr.C() as i32;
let mut carry = self.cpsr.C(); let mut carry = self.cpsr.C();

View file

@ -39,7 +39,7 @@ impl fmt::Display for DecodedInstruction {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self { match self {
DecodedInstruction::Arm(a) => write!(f, "{}", a), DecodedInstruction::Arm(a) => write!(f, "{}", a),
DecodedInstruction::Thumb(t) => write!(f, "{:#?}", t), DecodedInstruction::Thumb(t) => write!(f, "{}", t),
} }
} }
} }

View file

@ -20,7 +20,7 @@ impl ThumbInstruction {
fn fmt_data_process_imm(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt_data_process_imm(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!( write!(
f, f,
"{op}\t{Rd}, #{Offset8}", "{op}\t{Rd}, #{Offset8:#x}",
op = self.format3_op(), op = self.format3_op(),
Rd = reg_string(self.rd()), Rd = reg_string(self.rd()),
Offset8 = self.offset8() Offset8 = self.offset8()

View file

@ -1,9 +1,36 @@
use super::super::cpu::{Core, CpuExecResult}; use crate::arm7tdmi::arm::exec::*;
use super::ThumbInstruction; use crate::arm7tdmi::arm::ArmOpCode;
use crate::arm7tdmi::bus::Bus; use crate::arm7tdmi::bus::{Bus, MemoryAccessType::*, MemoryAccessWidth::*};
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
use super::{ThumbFormat, ThumbInstruction};
impl Core { impl Core {
fn exec_thumb_data_process_imm(
&mut self,
sysbus: &mut Bus,
insn: ThumbInstruction,
) -> CpuExecResult {
let arm_alu_op: ArmOpCode = insn.format3_op().into();
let op1 = self.get_reg(insn.rd()) as i32;
let op2 = insn.offset8() as i32;
let result = self.alu(arm_alu_op, op1, op2, true);
if let Some(result) = result {
self.set_reg(insn.rd(), result as u32);
}
// +1S
self.add_cycles(
self.pc + (self.word_size() as u32),
sysbus,
Seq + MemoryAccess32,
);
Ok(CpuPipelineAction::IncPC)
}
pub fn exec_thumb(&mut self, sysbus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult { pub fn exec_thumb(&mut self, sysbus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
unimplemented!("thumb not implemented {:#}", insn) match insn.fmt {
ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(sysbus, insn),
_ => unimplemented!("thumb not implemented {:#}", insn),
}
} }
} }

View file

@ -4,7 +4,7 @@ use crate::bit::BitIndex;
use crate::byteorder::{LittleEndian, ReadBytesExt}; use crate::byteorder::{LittleEndian, ReadBytesExt};
use crate::num::FromPrimitive; use crate::num::FromPrimitive;
use super::arm::{ArmCond, ArmShiftType}; use super::arm::{ArmCond, ArmOpCode, ArmShiftType};
use super::{Addr, InstructionDecoder, InstructionDecoderError}; use super::{Addr, InstructionDecoder, InstructionDecoderError};
pub mod display; pub mod display;
@ -159,6 +159,17 @@ pub enum OpFormat3 {
SUB = 3, SUB = 3,
} }
impl From<OpFormat3> for ArmOpCode {
fn from(op: OpFormat3) -> ArmOpCode {
match op {
OpFormat3::MOV => ArmOpCode::MOV,
OpFormat3::CMP => ArmOpCode::CMP,
OpFormat3::ADD => ArmOpCode::ADD,
OpFormat3::SUB => ArmOpCode::SUB,
}
}
}
#[derive(Debug, Primitive)] #[derive(Debug, Primitive)]
pub enum OpFormat5 { pub enum OpFormat5 {
ADD = 0, ADD = 0,
@ -167,12 +178,29 @@ pub enum OpFormat5 {
BX = 3, BX = 3,
} }
impl From<OpFormat5> for ArmOpCode {
fn from(op: OpFormat5) -> ArmOpCode {
match op {
OpFormat5::ADD => ArmOpCode::ADD,
OpFormat5::CMP => ArmOpCode::CMP,
OpFormat5::MOV => ArmOpCode::MOV,
_ => unreachable!(), // this should not be called if op = BX
}
}
}
impl ThumbInstruction { impl ThumbInstruction {
const FLAG_H1: usize = 7; const FLAG_H1: usize = 7;
const FLAG_H2: usize = 6; const FLAG_H2: usize = 6;
pub fn rd(&self) -> usize { pub fn rd(&self) -> usize {
(self.raw & 0b111) as usize match self.fmt {
ThumbFormat::DataProcessImm
| ThumbFormat::LdrPc
| ThumbFormat::LdrStrSp
| ThumbFormat::LdrAddress => self.raw.bit_range(8..11) as usize,
_ => (self.raw & 0b111) as usize,
}
} }
pub fn rs(&self) -> usize { pub fn rs(&self) -> usize {
@ -239,3 +267,30 @@ impl ThumbInstruction {
self.raw.bit(bit) self.raw.bit(bit)
} }
} }
#[cfg(test)]
/// All instructions constants were generated using an ARM assembler.
mod tests {
use super::*;
#[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();
core.set_reg(0, 0);
// movs r0, #0x27
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)
);
assert_eq!(core.get_reg(0), 0x27);
}
}

View file

@ -10,13 +10,23 @@ const PALETTE_RAM_SIZE: usize = 1 * 1024;
const OAM_SIZE: usize = 1 * 1024; const OAM_SIZE: usize = 1 * 1024;
#[derive(Debug)] #[derive(Debug)]
struct BoxedMemory(Box<[u8]>, WaitState); pub struct BoxedMemory(Box<[u8]>, WaitState);
impl BoxedMemory {
pub fn new(boxed_slice: Box<[u8]>) -> BoxedMemory {
BoxedMemory(boxed_slice, Default::default())
}
pub fn new_with_waitstate(boxed_slice: Box<[u8]>, ws: WaitState) -> BoxedMemory {
BoxedMemory(boxed_slice, ws)
}
}
#[derive(Debug)] #[derive(Debug)]
struct WaitState { pub struct WaitState {
access8: usize, pub access8: usize,
access16: usize, pub access16: usize,
access32: usize, pub access32: usize,
} }
impl WaitState { impl WaitState {