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
|
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();
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
Reference in a new issue