2019-07-05 13:34:52 +01:00
|
|
|
use crate::arm7tdmi::bus::Bus;
|
2019-07-20 12:44:49 +01:00
|
|
|
use crate::arm7tdmi::cpu::{Core, CpuExecResult};
|
2019-07-05 13:34:52 +01:00
|
|
|
use crate::arm7tdmi::*;
|
2019-07-02 23:26:48 +01:00
|
|
|
|
2019-07-03 23:56:50 +01:00
|
|
|
use super::*;
|
2019-07-05 01:28:02 +01:00
|
|
|
fn push(cpu: &mut Core, bus: &mut Bus, r: usize) {
|
2019-07-05 11:07:07 +01:00
|
|
|
cpu.gpr[REG_SP] -= 4;
|
|
|
|
let stack_addr = cpu.gpr[REG_SP];
|
|
|
|
cpu.store_32(stack_addr, cpu.get_reg(r), bus)
|
|
|
|
}
|
2019-07-05 01:28:02 +01:00
|
|
|
fn pop(cpu: &mut Core, bus: &mut Bus, r: usize) {
|
2019-07-05 11:07:07 +01:00
|
|
|
let stack_addr = cpu.gpr[REG_SP];
|
|
|
|
let val = cpu.load_32(stack_addr, bus);
|
|
|
|
cpu.set_reg(r, val);
|
|
|
|
cpu.gpr[REG_SP] = stack_addr + 4;
|
|
|
|
}
|
2019-07-02 14:57:35 +01:00
|
|
|
|
|
|
|
impl Core {
|
2019-07-05 12:09:04 +01:00
|
|
|
fn exec_thumb_move_shifted_reg(
|
|
|
|
&mut self,
|
2019-07-05 13:34:52 +01:00
|
|
|
_bus: &mut Bus,
|
2019-07-05 12:09:04 +01:00
|
|
|
insn: ThumbInstruction,
|
|
|
|
) -> CpuExecResult {
|
2019-07-08 18:58:22 +01:00
|
|
|
let op2 = self
|
2019-07-05 12:09:04 +01:00
|
|
|
.register_shift(
|
|
|
|
insn.rs(),
|
2019-07-08 17:56:12 +01:00
|
|
|
ShiftedRegister::ByAmount(insn.offset5() as u8 as u32, insn.format1_op()),
|
2019-07-05 12:09:04 +01:00
|
|
|
)
|
|
|
|
.unwrap();
|
2019-07-08 18:58:22 +01:00
|
|
|
|
|
|
|
let rd = insn.rd();
|
|
|
|
let op1 = self.get_reg(rd) as i32;
|
|
|
|
let result = self.alu(AluOpCode::MOV, op1, op2, true);
|
|
|
|
if let Some(result) = result {
|
|
|
|
self.set_reg(rd, result as u32);
|
|
|
|
}
|
2019-07-05 12:09:04 +01:00
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 12:09:04 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn exec_thumb_add_sub(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-03 23:56:50 +01:00
|
|
|
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() {
|
2019-07-08 17:56:12 +01:00
|
|
|
AluOpCode::SUB
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-07-08 17:56:12 +01:00
|
|
|
AluOpCode::ADD
|
2019-07-03 23:56:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
let result = self.alu(arm_alu_op, op1, op2, true);
|
|
|
|
if let Some(result) = result {
|
|
|
|
self.set_reg(insn.rd(), result as u32);
|
|
|
|
}
|
2019-07-05 11:07:07 +01:00
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
2019-07-02 23:26:48 +01:00
|
|
|
fn exec_thumb_data_process_imm(
|
|
|
|
&mut self,
|
2019-07-05 13:34:52 +01:00
|
|
|
_bus: &mut Bus,
|
2019-07-02 23:26:48 +01:00
|
|
|
insn: ThumbInstruction,
|
|
|
|
) -> CpuExecResult {
|
2019-07-08 17:56:12 +01:00
|
|
|
let arm_alu_op: AluOpCode = insn.format3_op().into();
|
2019-07-02 23:26:48 +01:00
|
|
|
let op1 = self.get_reg(insn.rd()) as i32;
|
2019-07-05 11:58:26 +01:00
|
|
|
let op2 = insn.offset8() as u8 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);
|
|
|
|
}
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 11:58:26 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn exec_thumb_mul(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-05 11:58:26 +01:00
|
|
|
let op1 = self.get_reg(insn.rd()) as i32;
|
|
|
|
let op2 = self.get_reg(insn.rs()) as i32;
|
|
|
|
let m = self.get_required_multipiler_array_cycles(op2);
|
2019-07-05 13:34:52 +01:00
|
|
|
for _ in 0..m {
|
2019-07-05 11:58:26 +01:00
|
|
|
self.add_cycle();
|
|
|
|
}
|
2019-07-13 21:33:37 +01:00
|
|
|
self.gpr[insn.rd()] = op1.wrapping_mul(op2) as u32;
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 11:58:26 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn exec_thumb_alu_ops(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-07 23:47:41 +01:00
|
|
|
let rd = insn.rd();
|
|
|
|
|
|
|
|
let (arm_alu_op, shft) = insn.alu_opcode();
|
|
|
|
let op1 = self.get_reg(rd) as i32;
|
|
|
|
let op2 = if let Some(shft) = shft {
|
2019-07-08 23:30:24 +01:00
|
|
|
self.get_barrel_shifted_value(shft)
|
2019-07-07 23:47:41 +01:00
|
|
|
} else {
|
|
|
|
self.get_reg(insn.rs()) as i32
|
|
|
|
};
|
|
|
|
|
2019-07-02 23:26:48 +01:00
|
|
|
let result = self.alu(arm_alu_op, op1, op2, true);
|
|
|
|
if let Some(result) = result {
|
2019-07-07 23:47:41 +01:00
|
|
|
self.set_reg(rd, result as u32);
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
2019-07-05 11:07:07 +01:00
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Cycles 2S+1N
|
2019-07-05 13:34:52 +01:00
|
|
|
fn exec_thumb_bx(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-03 23:56:50 +01:00
|
|
|
let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) {
|
|
|
|
insn.rs() + 8
|
|
|
|
} else {
|
|
|
|
insn.rs()
|
|
|
|
};
|
2019-07-10 19:44:48 +01:00
|
|
|
self.branch_exchange(self.get_reg(src_reg))
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
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 {
|
2019-07-05 14:10:21 +01:00
|
|
|
let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) {
|
|
|
|
insn.rd() + 8
|
|
|
|
} else {
|
|
|
|
insn.rd()
|
|
|
|
};
|
|
|
|
let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) {
|
|
|
|
insn.rs() + 8
|
|
|
|
} else {
|
|
|
|
insn.rs()
|
|
|
|
};
|
2019-07-08 17:56:12 +01:00
|
|
|
let arm_alu_op: AluOpCode = insn.format5_op().into();
|
2019-07-08 23:28:14 +01:00
|
|
|
let set_flags = arm_alu_op.is_setting_flags();
|
2019-07-05 14:21:24 +01:00
|
|
|
let op1 = self.get_reg(dst_reg) as i32;
|
|
|
|
let op2 = self.get_reg(src_reg) as i32;
|
2019-07-08 23:28:14 +01:00
|
|
|
let result = self.alu(arm_alu_op, op1, op2, set_flags);
|
2019-07-05 14:10:21 +01:00
|
|
|
if let Some(result) = result {
|
2019-07-05 14:21:24 +01:00
|
|
|
self.set_reg(dst_reg, result as u32);
|
2019-07-07 22:58:09 +01:00
|
|
|
if dst_reg == REG_PC {
|
2019-07-20 12:44:49 +01:00
|
|
|
self.flush_pipeline();
|
2019-07-07 22:58:09 +01:00
|
|
|
}
|
2019-07-05 14:10:21 +01:00
|
|
|
}
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 00:15:16 +01:00
|
|
|
fn exec_thumb_ldr_pc(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
|
|
|
let addr = (insn.pc & !0b10) + 4 + (insn.word8() as Addr);
|
2019-07-05 11:07:07 +01:00
|
|
|
let data = self.load_32(addr, bus);
|
|
|
|
|
2019-07-03 00:15:16 +01:00
|
|
|
self.set_reg(insn.rd(), data);
|
|
|
|
// +1I
|
|
|
|
self.add_cycle();
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-03 00:15:16 +01:00
|
|
|
}
|
|
|
|
|
2019-07-06 13:51:58 +01:00
|
|
|
fn do_exec_thumb_ldr_str(
|
2019-07-03 23:56:50 +01:00
|
|
|
&mut self,
|
|
|
|
bus: &mut Bus,
|
|
|
|
insn: ThumbInstruction,
|
2019-07-06 13:51:58 +01:00
|
|
|
addr: Addr,
|
2019-07-03 23:56:50 +01:00
|
|
|
) -> CpuExecResult {
|
|
|
|
if insn.is_load() {
|
2019-07-06 13:51:58 +01:00
|
|
|
let data = if insn.is_transferring_bytes() {
|
2019-07-05 11:07:07 +01:00
|
|
|
self.load_8(addr, bus) as u32
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-07-05 11:07:07 +01:00
|
|
|
self.load_32(addr, bus)
|
2019-07-03 23:56:50 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
self.set_reg(insn.rd(), data);
|
|
|
|
|
|
|
|
// +1I
|
|
|
|
self.add_cycle();
|
|
|
|
} else {
|
|
|
|
let value = self.get_reg(insn.rd());
|
2019-07-06 13:51:58 +01:00
|
|
|
if insn.is_transferring_bytes() {
|
2019-07-05 11:07:07 +01:00
|
|
|
self.store_8(addr, value as u8, bus);
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-07-05 11:07:07 +01:00
|
|
|
self.store_32(addr, value, bus);
|
2019-07-03 23:56:50 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
2019-07-06 13:51:58 +01:00
|
|
|
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()));
|
|
|
|
self.do_exec_thumb_ldr_str(bus, insn, addr)
|
|
|
|
}
|
|
|
|
|
2019-07-06 21:38:08 +01:00
|
|
|
fn exec_thumb_ldr_str_shb(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
|
|
|
let addr = self
|
|
|
|
.get_reg(insn.rb())
|
|
|
|
.wrapping_add(self.get_reg(insn.ro()));
|
|
|
|
let rd = insn.rd();
|
|
|
|
match (
|
|
|
|
insn.flag(ThumbInstruction::FLAG_SIGN_EXTEND),
|
|
|
|
insn.flag(ThumbInstruction::FLAG_HALFWORD),
|
|
|
|
) {
|
|
|
|
(false, false) =>
|
|
|
|
/* strh */
|
|
|
|
{
|
|
|
|
self.store_16(addr, self.gpr[rd] as u16, bus)
|
|
|
|
}
|
|
|
|
(false, true) =>
|
|
|
|
/* ldrh */
|
|
|
|
{
|
|
|
|
self.gpr[rd] = self.load_16(addr, bus) as u32
|
|
|
|
}
|
|
|
|
(true, false) =>
|
|
|
|
/* ldsb */
|
|
|
|
{
|
|
|
|
let val = self.load_8(addr, bus) as i8 as i32 as u32;
|
|
|
|
self.gpr[rd] = val;
|
|
|
|
}
|
|
|
|
(true, true) =>
|
|
|
|
/* ldsh */
|
|
|
|
{
|
|
|
|
let val = self.load_16(addr, bus) as i16 as i32 as u32;
|
|
|
|
self.gpr[rd] = val;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
|
2019-07-06 13:51:58 +01:00
|
|
|
fn exec_thumb_ldr_str_imm_offset(
|
|
|
|
&mut self,
|
|
|
|
bus: &mut Bus,
|
|
|
|
insn: ThumbInstruction,
|
|
|
|
) -> CpuExecResult {
|
|
|
|
let offset = if insn.is_transferring_bytes() {
|
|
|
|
insn.offset5()
|
|
|
|
} else {
|
|
|
|
(insn.offset5() << 3) >> 1
|
|
|
|
};
|
|
|
|
let addr = self.get_reg(insn.rb()).wrapping_add(offset as u32);
|
|
|
|
self.do_exec_thumb_ldr_str(bus, insn, addr)
|
|
|
|
}
|
|
|
|
|
2019-07-05 13:50:14 +01:00
|
|
|
fn exec_thumb_ldr_str_halfword(
|
|
|
|
&mut self,
|
|
|
|
bus: &mut Bus,
|
|
|
|
insn: ThumbInstruction,
|
|
|
|
) -> CpuExecResult {
|
|
|
|
let base = self.gpr[insn.rb()] as i32;
|
|
|
|
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
|
|
|
|
if insn.is_load() {
|
|
|
|
let data = self.load_16(addr, bus);
|
|
|
|
self.add_cycle();
|
|
|
|
self.gpr[insn.rd()] = data as u32;
|
|
|
|
} else {
|
|
|
|
self.store_16(addr, self.gpr[insn.rd()] as u16, bus);
|
|
|
|
}
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 13:50:14 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 11:20:19 +01:00
|
|
|
fn exec_thumb_ldr_str_sp(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-06 21:38:08 +01:00
|
|
|
let addr = self.gpr[REG_SP] + (insn.word8() as Addr);
|
|
|
|
self.do_exec_thumb_ldr_str_with_addr(bus, insn, addr)
|
|
|
|
}
|
|
|
|
|
2019-07-08 17:56:12 +01:00
|
|
|
fn exec_thumb_load_address(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-07 22:49:03 +01:00
|
|
|
let result = if insn.flag(ThumbInstruction::FLAG_SP) {
|
2019-07-06 21:38:08 +01:00
|
|
|
self.gpr[REG_SP] + (insn.word8() as Addr)
|
|
|
|
} else {
|
|
|
|
(insn.pc & !0b10) + 4 + (insn.word8() as Addr)
|
|
|
|
};
|
2019-07-07 22:49:03 +01:00
|
|
|
self.gpr[insn.rd()] = result;
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
fn do_exec_thumb_ldr_str_with_addr(
|
|
|
|
&mut self,
|
|
|
|
bus: &mut Bus,
|
|
|
|
insn: ThumbInstruction,
|
|
|
|
addr: Addr,
|
|
|
|
) -> CpuExecResult {
|
2019-07-05 11:20:19 +01:00
|
|
|
if insn.is_load() {
|
|
|
|
let data = self.load_32(addr, bus);
|
|
|
|
self.add_cycle();
|
|
|
|
self.gpr[insn.rd()] = data;
|
|
|
|
} else {
|
|
|
|
self.store_32(addr, self.gpr[insn.rd()], bus);
|
|
|
|
}
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 11:20:19 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn exec_thumb_add_sp(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-05 01:46:04 +01:00
|
|
|
let op1 = self.gpr[REG_SP] as i32;
|
|
|
|
let op2 = insn.sword7();
|
2019-07-08 17:56:12 +01:00
|
|
|
let arm_alu_op = AluOpCode::ADD;
|
2019-07-05 01:46:04 +01:00
|
|
|
|
|
|
|
let result = self.alu(arm_alu_op, op1, op2, false);
|
|
|
|
if let Some(result) = result {
|
|
|
|
self.gpr[REG_SP] = result as u32;
|
|
|
|
}
|
2019-07-05 11:07:07 +01:00
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 01:46:04 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 11:07:07 +01:00
|
|
|
fn exec_thumb_push_pop(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-05 01:28:02 +01:00
|
|
|
// (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 pc_lr_flag = insn.flag(ThumbInstruction::FLAG_R);
|
|
|
|
let rlist = insn.register_list();
|
|
|
|
if is_pop {
|
|
|
|
for r in rlist {
|
|
|
|
pop(self, bus, r);
|
|
|
|
}
|
|
|
|
if pc_lr_flag {
|
|
|
|
pop(self, bus, REG_PC);
|
2019-07-06 21:38:08 +01:00
|
|
|
self.pc = self.pc & !1;
|
2019-07-20 12:44:49 +01:00
|
|
|
self.flush_pipeline();
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
|
|
|
self.add_cycle();
|
|
|
|
} else {
|
|
|
|
if pc_lr_flag {
|
|
|
|
push(self, bus, REG_LR);
|
|
|
|
}
|
|
|
|
for r in rlist.into_iter().rev() {
|
2019-07-05 11:07:07 +01:00
|
|
|
push(self, bus, r);
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
|
|
|
|
2019-07-06 21:38:08 +01:00
|
|
|
fn exec_thumb_ldm_stm(&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_load = insn.is_load();
|
|
|
|
let rb = insn.rb();
|
|
|
|
|
|
|
|
let mut addr = self.gpr[rb];
|
|
|
|
let rlist = insn.register_list();
|
|
|
|
if is_load {
|
|
|
|
for r in rlist {
|
|
|
|
let val = self.load_32(addr, bus);
|
|
|
|
addr += 4;
|
|
|
|
self.add_cycle();
|
|
|
|
self.set_reg(r, val);
|
|
|
|
}
|
|
|
|
} else {
|
2019-07-10 19:44:00 +01:00
|
|
|
for r in rlist {
|
2019-07-06 21:38:08 +01:00
|
|
|
self.store_32(addr, self.gpr[r], bus);
|
|
|
|
addr += 4;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-09 00:20:32 +01:00
|
|
|
self.gpr[rb] = addr as u32;
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
|
2019-07-03 23:56:50 +01:00
|
|
|
fn exec_thumb_branch_with_cond(
|
|
|
|
&mut self,
|
2019-07-05 11:07:07 +01:00
|
|
|
_bus: &mut Bus,
|
2019-07-03 23:56:50 +01:00
|
|
|
insn: ThumbInstruction,
|
|
|
|
) -> CpuExecResult {
|
|
|
|
if !self.check_arm_cond(insn.cond()) {
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-07-05 14:01:16 +01:00
|
|
|
let offset = ((insn.offset8() as i8) << 1) as i32;
|
|
|
|
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
|
2019-07-20 12:44:49 +01:00
|
|
|
self.flush_pipeline();
|
|
|
|
Ok(())
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-05 16:38:20 +01:00
|
|
|
fn exec_thumb_branch(&mut self, _bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-05 14:21:24 +01:00
|
|
|
let offset = ((insn.offset11() << 21) >> 20) as i32;
|
|
|
|
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
|
2019-07-20 12:44:49 +01:00
|
|
|
self.flush_pipeline();
|
|
|
|
Ok(())
|
2019-07-05 14:21:24 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 13:34:52 +01:00
|
|
|
fn exec_thumb_branch_long_with_link(
|
|
|
|
&mut self,
|
|
|
|
_bus: &mut Bus,
|
|
|
|
insn: ThumbInstruction,
|
|
|
|
) -> CpuExecResult {
|
|
|
|
let mut off = insn.offset11();
|
|
|
|
if insn.flag(ThumbInstruction::FLAG_LOW_OFFSET) {
|
|
|
|
off = off << 1;
|
|
|
|
let next_pc = (self.pc - 2) | 1;
|
|
|
|
self.pc = (self.gpr[REG_LR] as i32).wrapping_add(off) as u32;
|
|
|
|
self.gpr[REG_LR] = next_pc;
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
self.flush_pipeline();
|
|
|
|
Ok(())
|
2019-07-05 13:34:52 +01:00
|
|
|
} else {
|
|
|
|
off = (off << 21) >> 9;
|
|
|
|
self.gpr[REG_LR] = (self.pc as i32).wrapping_add(off) as u32;
|
|
|
|
|
2019-07-20 12:44:49 +01:00
|
|
|
Ok(())
|
2019-07-05 13:34:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-07-03 00:15:16 +01:00
|
|
|
pub fn exec_thumb(&mut self, bus: &mut Bus, insn: ThumbInstruction) -> CpuExecResult {
|
2019-07-02 23:26:48 +01:00
|
|
|
match insn.fmt {
|
2019-07-05 12:09:04 +01:00
|
|
|
ThumbFormat::MoveShiftedReg => self.exec_thumb_move_shifted_reg(bus, insn),
|
2019-07-03 23:56:50 +01:00
|
|
|
ThumbFormat::AddSub => self.exec_thumb_add_sub(bus, insn),
|
2019-07-03 00:15:16 +01:00
|
|
|
ThumbFormat::DataProcessImm => self.exec_thumb_data_process_imm(bus, insn),
|
2019-07-05 11:58:26 +01:00
|
|
|
ThumbFormat::Mul => self.exec_thumb_mul(bus, insn),
|
|
|
|
ThumbFormat::AluOps => self.exec_thumb_alu_ops(bus, insn),
|
2019-07-03 23:56:50 +01:00
|
|
|
ThumbFormat::HiRegOpOrBranchExchange => self.exec_thumb_hi_reg_op_or_bx(bus, insn),
|
2019-07-03 00:15:16 +01:00
|
|
|
ThumbFormat::LdrPc => self.exec_thumb_ldr_pc(bus, insn),
|
2019-07-03 23:56:50 +01:00
|
|
|
ThumbFormat::LdrStrRegOffset => self.exec_thumb_ldr_str_reg_offset(bus, insn),
|
2019-07-06 21:38:08 +01:00
|
|
|
ThumbFormat::LdrStrSHB => self.exec_thumb_ldr_str_shb(bus, insn),
|
2019-07-06 13:51:58 +01:00
|
|
|
ThumbFormat::LdrStrImmOffset => self.exec_thumb_ldr_str_imm_offset(bus, insn),
|
2019-07-05 13:50:14 +01:00
|
|
|
ThumbFormat::LdrStrHalfWord => self.exec_thumb_ldr_str_halfword(bus, insn),
|
2019-07-05 11:20:19 +01:00
|
|
|
ThumbFormat::LdrStrSp => self.exec_thumb_ldr_str_sp(bus, insn),
|
2019-07-07 22:49:03 +01:00
|
|
|
ThumbFormat::LoadAddress => self.exec_thumb_load_address(bus, insn),
|
2019-07-05 01:46:04 +01:00
|
|
|
ThumbFormat::AddSp => self.exec_thumb_add_sp(bus, insn),
|
2019-07-05 01:28:02 +01:00
|
|
|
ThumbFormat::PushPop => self.exec_thumb_push_pop(bus, insn),
|
2019-07-06 21:38:08 +01:00
|
|
|
ThumbFormat::LdmStm => self.exec_thumb_ldm_stm(bus, insn),
|
2019-07-03 23:56:50 +01:00
|
|
|
ThumbFormat::BranchConditional => self.exec_thumb_branch_with_cond(bus, insn),
|
2019-07-05 14:21:24 +01:00
|
|
|
ThumbFormat::Branch => self.exec_thumb_branch(bus, insn),
|
2019-07-05 13:34:52 +01:00
|
|
|
ThumbFormat::BranchLongWithLink => self.exec_thumb_branch_long_with_link(bus, insn),
|
2019-07-05 12:09:04 +01:00
|
|
|
_ => unimplemented!("thumb not implemented {:#x?}", insn),
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
2019-07-02 14:57:35 +01:00
|
|
|
}
|
|
|
|
}
|