Implement thumb format3 instruction and add a test for it.
Former-commit-id: 8cf6664027dc3d5dbeb6d2ca3d089820baac2709
This commit is contained in:
parent
4011911cca
commit
b82874809f
|
@ -162,7 +162,7 @@ impl Core {
|
|||
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 mut carry = self.cpsr.C();
|
||||
|
|
|
@ -39,7 +39,7 @@ impl fmt::Display for DecodedInstruction {
|
|||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
match self {
|
||||
DecodedInstruction::Arm(a) => write!(f, "{}", a),
|
||||
DecodedInstruction::Thumb(t) => write!(f, "{:#?}", t),
|
||||
DecodedInstruction::Thumb(t) => write!(f, "{}", t),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,7 +20,7 @@ impl ThumbInstruction {
|
|||
fn fmt_data_process_imm(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{op}\t{Rd}, #{Offset8}",
|
||||
"{op}\t{Rd}, #{Offset8:#x}",
|
||||
op = self.format3_op(),
|
||||
Rd = reg_string(self.rd()),
|
||||
Offset8 = self.offset8()
|
||||
|
|
|
@ -1,9 +1,36 @@
|
|||
use super::super::cpu::{Core, CpuExecResult};
|
||||
use super::ThumbInstruction;
|
||||
use crate::arm7tdmi::bus::Bus;
|
||||
use crate::arm7tdmi::arm::exec::*;
|
||||
use crate::arm7tdmi::arm::ArmOpCode;
|
||||
use crate::arm7tdmi::bus::{Bus, MemoryAccessType::*, MemoryAccessWidth::*};
|
||||
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
|
||||
|
||||
use super::{ThumbFormat, ThumbInstruction};
|
||||
|
||||
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 {
|
||||
unimplemented!("thumb not implemented {:#}", insn)
|
||||
match insn.fmt {
|
||||
ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(sysbus, insn),
|
||||
_ => unimplemented!("thumb not implemented {:#}", insn),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use crate::bit::BitIndex;
|
|||
use crate::byteorder::{LittleEndian, ReadBytesExt};
|
||||
use crate::num::FromPrimitive;
|
||||
|
||||
use super::arm::{ArmCond, ArmShiftType};
|
||||
use super::arm::{ArmCond, ArmOpCode, ArmShiftType};
|
||||
use super::{Addr, InstructionDecoder, InstructionDecoderError};
|
||||
|
||||
pub mod display;
|
||||
|
@ -159,6 +159,17 @@ pub enum OpFormat3 {
|
|||
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)]
|
||||
pub enum OpFormat5 {
|
||||
ADD = 0,
|
||||
|
@ -167,12 +178,29 @@ pub enum OpFormat5 {
|
|||
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 {
|
||||
const FLAG_H1: usize = 7;
|
||||
const FLAG_H2: usize = 6;
|
||||
|
||||
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 {
|
||||
|
@ -239,3 +267,30 @@ impl ThumbInstruction {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,13 +10,23 @@ const PALETTE_RAM_SIZE: usize = 1 * 1024;
|
|||
const OAM_SIZE: usize = 1 * 1024;
|
||||
|
||||
#[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)]
|
||||
struct WaitState {
|
||||
access8: usize,
|
||||
access16: usize,
|
||||
access32: usize,
|
||||
pub struct WaitState {
|
||||
pub access8: usize,
|
||||
pub access16: usize,
|
||||
pub access32: usize,
|
||||
}
|
||||
|
||||
impl WaitState {
|
||||
|
|
Reference in a new issue