Add many thumb instructions..

TODO add more tests for all the instructions I've got implemented so
far.
Also, I need to rewrite the whole "sysbus" module again because it's
crap and I keep refactoring it as I go.
I've added the "Dummy" because the bios for some reason tries to memzero
an unmapped region on the work ram (the thumb loop that ends at 0x126)


Former-commit-id: 67befd0935ee10df9ac8ceeaebd14f69767a7f16
This commit is contained in:
Michel Heily 2019-07-04 01:56:50 +03:00
parent 984b17fa39
commit 702a08e30c
3 changed files with 254 additions and 10 deletions

View file

@ -1,11 +1,37 @@
use crate::arm7tdmi::arm::*;
use crate::arm7tdmi::bus::{Bus, MemoryAccessType::*, MemoryAccessWidth::*};
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
use crate::arm7tdmi::{Addr, REG_PC};
use crate::arm7tdmi::{Addr, REG_PC, CpuState};
use super::{ThumbFormat, ThumbInstruction};
use super::*;
impl Core {
fn exec_thumb_add_sub(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
let op1 = self.get_reg(insn.rs()) as i32;
let op2 = if insn.is_immediate_operand() {
insn.rn() as u32 as i32
} else {
self.get_reg(insn.rn()) as i32
};
let arm_alu_op = if insn.is_subtract() {
ArmOpCode::SUB
} else {
ArmOpCode::ADD
};
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(
insn.pc + (self.word_size() as u32),
bus,
Seq + MemoryAccess32,
);
Ok(CpuPipelineAction::IncPC)
}
fn exec_thumb_data_process_imm(
&mut self,
bus: &mut Bus,
@ -19,12 +45,60 @@ impl Core {
self.set_reg(insn.rd(), result as u32);
}
// +1S
self.add_cycles(
insn.pc + (self.word_size() as u32),
bus,
Seq + MemoryAccess16,
);
Ok(CpuPipelineAction::IncPC)
}
/// Cycles 2S+1N
fn exec_thumb_bx(
&mut self,
bus: &mut Bus,
insn: ThumbInstruction,
) -> CpuExecResult {
let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) {
insn.rs() + 8
} else {
insn.rs()
};
let addr = self.get_reg(src_reg);
if addr.bit(0) {
self.cpsr.set_state(CpuState::THUMB);
} else {
self.cpsr.set_state(CpuState::ARM);
}
// +1N
self.add_cycles(self.pc, bus, NonSeq + MemoryAccess32);
self.pc = addr & !1;
// +2S
self.add_cycles(self.pc, bus, Seq + MemoryAccess32);
self.add_cycles(
self.pc + (self.word_size() as u32),
bus,
Seq + MemoryAccess32,
);
Ok(CpuPipelineAction::IncPC)
Ok(CpuPipelineAction::Flush)
}
fn exec_thumb_hi_reg_op_or_bx(
&mut self,
bus: &mut Bus,
insn: ThumbInstruction,
) -> CpuExecResult {
if OpFormat5::BX == insn.format5_op() {
self.exec_thumb_bx(bus, insn)
} else {
unimplemented!("Sorry, I'm tired");
Ok(CpuPipelineAction::IncPC)
}
}
fn exec_thumb_ldr_pc(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
@ -34,9 +108,9 @@ impl Core {
self.add_cycles(addr, bus, NonSeq + MemoryAccess32);
// +1S
self.add_cycles(
self.pc + (self.word_size() as u32),
insn.pc + (self.word_size() as u32),
bus,
Seq + MemoryAccess32,
Seq + MemoryAccess16,
);
self.set_reg(insn.rd(), data);
// +1I
@ -45,11 +119,93 @@ impl Core {
Ok(CpuPipelineAction::IncPC)
}
fn exec_thumb_ldr_str_reg_offset(
&mut self,
bus: &mut Bus,
insn: ThumbInstruction,
) -> CpuExecResult {
let addr = self
.get_reg(insn.rb())
.wrapping_add(self.get_reg(insn.ro()));
if insn.is_load() {
let data = if insn.is_transfering_bytes() {
// +1N
self.add_cycles(addr, bus, NonSeq + MemoryAccess8);
bus.read_8(addr) as u32
} else {
// +1N
self.add_cycles(addr, bus, NonSeq + MemoryAccess32);
bus.read_32(addr)
};
// +1S
self.add_cycles(
insn.pc + (self.word_size() as u32),
bus,
Seq + MemoryAccess32,
);
self.set_reg(insn.rd(), data);
// +1I
self.add_cycle();
} else {
self.add_cycles(addr, bus, NonSeq + MemoryAccess32);
let value = self.get_reg(insn.rd());
if insn.is_transfering_bytes() {
// +1N
self.add_cycles(addr, bus, NonSeq + MemoryAccess8);
bus.write_8(addr, value as u8).expect("bus error");
} else {
// +1N
self.add_cycles(addr, bus, NonSeq + MemoryAccess32);
bus.write_32(addr, value).expect("bus error");
};
}
Ok(CpuPipelineAction::IncPC)
}
fn exec_thumb_branch_with_cond(
&mut self,
bus: &mut Bus,
insn: ThumbInstruction,
) -> CpuExecResult {
if !self.check_arm_cond(insn.cond()) {
self.add_cycles(
insn.pc + (self.word_size() as u32),
bus,
Seq + MemoryAccess16,
);
Ok(CpuPipelineAction::IncPC)
} else {
// +1N
self.add_cycles(insn.pc, bus, NonSeq + MemoryAccess32);
let offset = insn.offset8() as i8 as i32;
self.pc = (insn.pc as i32).wrapping_add(offset) as u32;
// +2S
self.add_cycles(self.pc, bus, Seq + MemoryAccess32);
self.add_cycles(
self.pc + (self.word_size() as u32),
bus,
Seq + MemoryAccess32,
);
Ok(CpuPipelineAction::Flush)
}
}
pub fn exec_thumb(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
match insn.fmt {
ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn),
ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(bus, insn),
ThumbFormat::HiRegOpOrBranchExchange => self.exec_thumb_hi_reg_op_or_bx(bus, insn),
ThumbFormat::LdrPc => self.exec_thumb_ldr_pc(bus, insn),
_ => unimplemented!("thumb not implemented {:#}", insn),
ThumbFormat::LdrStrRegOffset => self.exec_thumb_ldr_str_reg_offset(bus, insn),
ThumbFormat::BranchConditional => self.exec_thumb_branch_with_cond(bus, insn),
_ => unimplemented!("thumb not implemented {:#?}", insn),
}
}
}

View file

@ -151,7 +151,7 @@ impl InstructionDecoder for ThumbInstruction {
}
}
#[derive(Debug, Primitive)]
#[derive(Debug, Primitive, PartialEq)]
pub enum OpFormat3 {
MOV = 0,
CMP = 1,
@ -170,7 +170,7 @@ impl From<OpFormat3> for ArmOpCode {
}
}
#[derive(Debug, Primitive)]
#[derive(Debug, Primitive, PartialEq)]
pub enum OpFormat5 {
ADD = 0,
CMP = 1,
@ -321,4 +321,44 @@ mod tests {
);
assert_eq!(core.get_reg(0), 0x12345678);
}
#[test]
fn ldr_str_reg_offset() {
use crate::arm7tdmi::{
cpu::{Core, CpuPipelineAction},
Bus,
};
use crate::sysbus::BoxedMemory;
// str r0, [r4, r1]
let str_insn = ThumbInstruction::decode(0x5060, 0x6).unwrap();
// ldrb r2, [r4, r1]
let ldr_insn = ThumbInstruction::decode(0x5c62, 0x6).unwrap();
let mut core = Core::new();
core.set_reg(0, 0x12345678);
core.set_reg(2, 0);
core.set_reg(1, 0x4);
core.set_reg(4, 0xc);
let bytes = vec![
/* 0: */ 0xaa, 0xbb, 0xcc, 0xdd, /* 4: */ 0xaa, 0xbb, 0xcc, 0xdd,
/* 8: */ 0xaa, 0xbb, 0xcc, 0xdd, /* c: */ 0xaa, 0xbb, 0xcc, 0xdd,
/* 10: */ 0xaa, 0xbb, 0xcc, 0xdd,
];
let mut mem = BoxedMemory::new(bytes.into_boxed_slice());
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)
);
assert_eq!(mem.read_32(0x10), 0x12345678);
assert_eq!(
core.exec_thumb(&mut mem, ldr_insn),
Ok(CpuPipelineAction::IncPC)
);
assert_eq!(core.get_reg(2), 0x78);
}
}

View file

@ -65,6 +65,46 @@ impl Bus for BoxedMemory {
}
}
#[derive(Debug)]
struct DummyBus([u8; 4]);
impl Bus for DummyBus {
fn read_32(&self, addr: Addr) -> u32 {
0
}
fn read_16(&self, addr: Addr) -> u16 {
0
}
fn read_8(&self, addr: Addr) -> u8 {
0
}
fn write_32(&mut self, addr: Addr, value: u32) -> Result<(), io::Error> {
Ok(())
}
fn write_16(&mut self, addr: Addr, value: u16) -> Result<(), io::Error> {
Ok(())
}
fn write_8(&mut self, addr: Addr, value: u8) -> Result<(), io::Error> {
Ok(())
}
fn get_bytes(&self, addr: Addr) -> &[u8] {
&self.0
}
fn get_bytes_mut(&mut self, addr: Addr) -> &mut [u8] {
&mut self.0
}
fn get_cycles(&self, addr: Addr, access: MemoryAccess) -> usize {
1
}
}
#[derive(Debug)]
pub struct SysBus {
bios: BoxedMemory,
@ -76,6 +116,7 @@ pub struct SysBus {
vram: BoxedMemory,
oam: BoxedMemory,
gamepak: Cartridge,
dummy: DummyBus,
}
impl SysBus {
@ -98,6 +139,7 @@ impl SysBus {
),
oam: BoxedMemory::new(vec![0; OAM_SIZE].into_boxed_slice()),
gamepak: gamepak,
dummy: DummyBus([0; 4]),
}
}
@ -111,7 +153,10 @@ impl SysBus {
0x0600_0000...0x0601_7fff => &self.vram,
0x0700_0000...0x0700_03ff => &self.oam,
0x0800_0000...0x09ff_ffff => &self.gamepak,
_ => panic!("unmapped address @0x{:08x}", addr),
_ => {
println!("unmapped address @0x{:08x}", addr);
&self.dummy
}
}
}
@ -126,7 +171,10 @@ impl SysBus {
0x0600_0000...0x0601_7fff => &mut self.vram,
0x0700_0000...0x0700_03ff => &mut self.oam,
0x0800_0000...0x09ff_ffff => &mut self.gamepak,
_ => panic!("unmapped address @0x{:08x}", addr),
_ => {
println!("unmapped address @0x{:08x}", addr);
&mut self.dummy
}
}
}
}