2019-07-20 14:46:00 +01:00
|
|
|
use crate::core::arm7tdmi::*;
|
2019-08-08 17:46:56 +01:00
|
|
|
use crate::core::sysbus::SysBus;
|
2019-12-29 21:03:57 +00:00
|
|
|
use crate::core::Bus;
|
2019-07-02 23:26:48 +01:00
|
|
|
|
2019-11-08 22:55:09 +00:00
|
|
|
use crate::bit::BitIndex;
|
|
|
|
|
2019-07-03 23:56:50 +01:00
|
|
|
use super::*;
|
2019-08-08 17:46:56 +01:00
|
|
|
fn push(cpu: &mut Core, bus: &mut SysBus, r: usize) {
|
2019-07-05 11:07:07 +01:00
|
|
|
cpu.gpr[REG_SP] -= 4;
|
2019-11-08 22:55:09 +00:00
|
|
|
let stack_addr = cpu.gpr[REG_SP] & !3;
|
2019-08-08 17:46:56 +01:00
|
|
|
bus.write_32(stack_addr, cpu.get_reg(r))
|
2019-07-05 11:07:07 +01:00
|
|
|
}
|
2019-08-08 17:46:56 +01:00
|
|
|
fn pop(cpu: &mut Core, bus: &mut SysBus, r: usize) {
|
2020-01-17 11:04:14 +00:00
|
|
|
let val = bus.read_32(cpu.gpr[REG_SP] & !3);
|
2019-07-05 11:07:07 +01:00
|
|
|
cpu.set_reg(r, val);
|
2020-01-17 11:04:14 +00:00
|
|
|
cpu.gpr[REG_SP] += 4;
|
2019-07-05 11:07:07 +01:00
|
|
|
}
|
2019-07-02 14:57:35 +01:00
|
|
|
|
|
|
|
impl Core {
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 1
|
2019-07-05 12:09:04 +01:00
|
|
|
fn exec_thumb_move_shifted_reg(
|
|
|
|
&mut self,
|
2019-08-08 17:46:56 +01:00
|
|
|
sb: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2020-02-10 23:52:18 +00:00
|
|
|
) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = (insn.raw & 0b111) as usize;
|
|
|
|
let rs = insn.raw.bit_range(3..6) as usize;
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
let shift_amount = insn.offset5() as u8 as u32;
|
2020-02-10 23:52:18 +00:00
|
|
|
let op2 = self.barrel_shift_op(
|
|
|
|
insn.format1_op(),
|
|
|
|
self.gpr[rs],
|
|
|
|
shift_amount,
|
|
|
|
self.cpsr.C(),
|
|
|
|
true,
|
|
|
|
);
|
2020-02-08 12:19:57 +00:00
|
|
|
self.gpr[rd] = op2;
|
2019-11-08 22:55:09 +00:00
|
|
|
self.alu_update_flags(op2, false, self.bs_carry_out, self.cpsr.V());
|
|
|
|
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-05 12:09:04 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 2
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_add_sub(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = (insn.raw & 0b111) as usize;
|
2019-11-12 22:56:40 +00:00
|
|
|
let op1 = self.get_reg(insn.rs());
|
2019-07-03 23:56:50 +01:00
|
|
|
let op2 = if insn.is_immediate_operand() {
|
2019-11-12 22:56:40 +00:00
|
|
|
insn.rn() as u32
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-11-12 22:56:40 +00:00
|
|
|
self.get_reg(insn.rn())
|
2019-07-03 23:56:50 +01:00
|
|
|
};
|
2019-11-08 22:55:09 +00:00
|
|
|
|
|
|
|
let mut carry = self.cpsr.C();
|
|
|
|
let mut overflow = self.cpsr.V();
|
|
|
|
let result = if insn.is_subtract() {
|
2019-11-12 22:56:40 +00:00
|
|
|
self.alu_sub_flags(op1, op2, &mut carry, &mut overflow)
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-11-12 22:56:40 +00:00
|
|
|
self.alu_add_flags(op1, op2, &mut carry, &mut overflow)
|
2019-07-03 23:56:50 +01:00
|
|
|
};
|
2019-11-08 22:55:09 +00:00
|
|
|
self.alu_update_flags(result, true, carry, overflow);
|
2020-01-17 14:10:17 +00:00
|
|
|
self.set_reg(rd, result as u32);
|
2019-07-03 23:56:50 +01:00
|
|
|
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 3
|
2019-07-02 23:26:48 +01:00
|
|
|
fn exec_thumb_data_process_imm(
|
|
|
|
&mut self,
|
2019-08-08 17:46:56 +01:00
|
|
|
sb: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2020-02-10 23:28:20 +00:00
|
|
|
) -> CpuAction {
|
2019-11-08 22:55:09 +00:00
|
|
|
use OpFormat3::*;
|
|
|
|
let op = insn.format3_op();
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = insn.raw.bit_range(8..11) as usize;
|
|
|
|
let op1 = self.gpr[rd];
|
2019-11-12 22:56:40 +00:00
|
|
|
let op2_imm = (insn.raw & 0xff) as u32;
|
2019-11-08 22:55:09 +00:00
|
|
|
let mut carry = self.cpsr.C();
|
|
|
|
let mut overflow = self.cpsr.V();
|
|
|
|
let result = match op {
|
|
|
|
MOV => op2_imm,
|
2019-11-12 22:56:40 +00:00
|
|
|
CMP | SUB => self.alu_sub_flags(op1, op2_imm, &mut carry, &mut overflow),
|
|
|
|
ADD => self.alu_add_flags(op1, op2_imm, &mut carry, &mut overflow),
|
2019-11-08 22:55:09 +00:00
|
|
|
};
|
|
|
|
let arithmetic = op == ADD || op == SUB;
|
|
|
|
self.alu_update_flags(result, arithmetic, carry, overflow);
|
|
|
|
if op != CMP {
|
2020-01-17 14:10:17 +00:00
|
|
|
self.gpr[rd] = result as u32;
|
2019-07-05 11:58:26 +01:00
|
|
|
}
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-05 11:58:26 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 4
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = (insn.raw & 0b111) as usize;
|
2019-11-08 22:55:09 +00:00
|
|
|
let rs = insn.rs();
|
2019-11-12 22:56:40 +00:00
|
|
|
let dst = self.get_reg(rd);
|
|
|
|
let src = self.get_reg(rs);
|
2019-11-08 22:55:09 +00:00
|
|
|
|
|
|
|
let mut carry = self.cpsr.C();
|
|
|
|
let mut overflow = self.cpsr.V();
|
|
|
|
|
|
|
|
use ThumbAluOps::*;
|
|
|
|
let op = insn.format4_alu_op();
|
|
|
|
let result = match op {
|
|
|
|
AND | TST => dst & src,
|
|
|
|
EOR => dst ^ src,
|
|
|
|
LSL | LSR | ASR | ROR => {
|
|
|
|
// TODO optimize this second match, keeping it here for code clearity
|
|
|
|
let bs_op = match op {
|
|
|
|
LSL => BarrelShiftOpCode::LSL,
|
|
|
|
LSR => BarrelShiftOpCode::LSR,
|
|
|
|
ASR => BarrelShiftOpCode::ASR,
|
|
|
|
ROR => BarrelShiftOpCode::ROR,
|
|
|
|
_ => unreachable!(),
|
|
|
|
};
|
2020-02-08 12:19:57 +00:00
|
|
|
let result = self.shift_by_register(bs_op, rd, rs, carry);
|
2019-11-08 22:55:09 +00:00
|
|
|
carry = self.bs_carry_out;
|
|
|
|
result
|
|
|
|
}
|
2019-11-12 22:56:40 +00:00
|
|
|
ADC => self.alu_adc_flags(dst, src, &mut carry, &mut overflow),
|
|
|
|
SBC => self.alu_sbc_flags(dst, src, &mut carry, &mut overflow),
|
|
|
|
NEG => self.alu_sub_flags(0, src, &mut carry, &mut overflow),
|
|
|
|
CMP => self.alu_sub_flags(dst, src, &mut carry, &mut overflow),
|
|
|
|
CMN => self.alu_add_flags(dst, src, &mut carry, &mut overflow),
|
2019-11-08 22:55:09 +00:00
|
|
|
ORR => dst | src,
|
|
|
|
MUL => {
|
|
|
|
let m = self.get_required_multipiler_array_cycles(src);
|
|
|
|
for _ in 0..m {
|
|
|
|
self.add_cycle();
|
|
|
|
}
|
|
|
|
// TODO - meaningless values?
|
|
|
|
carry = false;
|
|
|
|
overflow = false;
|
|
|
|
dst.wrapping_mul(src)
|
|
|
|
}
|
|
|
|
BIC => dst & (!src),
|
|
|
|
MVN => !src,
|
2019-07-07 23:47:41 +01:00
|
|
|
};
|
2019-11-08 22:55:09 +00:00
|
|
|
self.alu_update_flags(result, op.is_arithmetic(), carry, overflow);
|
2019-07-07 23:47:41 +01:00
|
|
|
|
2019-11-08 22:55:09 +00:00
|
|
|
if !op.is_setting_flags() {
|
2019-07-07 23:47:41 +01:00
|
|
|
self.set_reg(rd, result as u32);
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 5
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_hi_reg_op_or_bx(
|
|
|
|
&mut self,
|
|
|
|
sb: &mut SysBus,
|
|
|
|
insn: &ThumbInstruction,
|
|
|
|
) -> CpuAction {
|
2019-11-08 22:55:09 +00:00
|
|
|
let op = insn.format5_op();
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = (insn.raw & 0b111) as usize;
|
2019-11-08 22:55:09 +00:00
|
|
|
let dst_reg = if insn.flag(ThumbInstruction::FLAG_H1) {
|
2020-01-17 14:10:17 +00:00
|
|
|
rd + 8
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2020-01-17 14:10:17 +00:00
|
|
|
rd
|
2019-11-08 22:55:09 +00:00
|
|
|
};
|
|
|
|
let src_reg = if insn.flag(ThumbInstruction::FLAG_H2) {
|
|
|
|
insn.rs() + 8
|
|
|
|
} else {
|
|
|
|
insn.rs()
|
|
|
|
};
|
2019-11-12 22:56:40 +00:00
|
|
|
let op1 = self.get_reg(dst_reg);
|
|
|
|
let op2 = self.get_reg(src_reg);
|
2019-11-08 22:55:09 +00:00
|
|
|
|
2020-02-10 23:28:20 +00:00
|
|
|
let mut result = CpuAction::AdvancePC;
|
2019-11-08 22:55:09 +00:00
|
|
|
match op {
|
2020-02-08 12:19:57 +00:00
|
|
|
OpFormat5::BX => {
|
2020-02-10 23:28:20 +00:00
|
|
|
return self.branch_exchange(sb, self.get_reg(src_reg));
|
2020-02-10 23:52:18 +00:00
|
|
|
}
|
2019-11-08 22:55:09 +00:00
|
|
|
OpFormat5::ADD => {
|
2019-11-12 22:56:40 +00:00
|
|
|
self.set_reg(dst_reg, op1.wrapping_add(op2));
|
2019-11-08 22:55:09 +00:00
|
|
|
if dst_reg == REG_PC {
|
2020-02-11 00:24:24 +00:00
|
|
|
result = CpuAction::FlushPipeline;
|
|
|
|
self.reload_pipeline16(sb);
|
2019-11-08 22:55:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
OpFormat5::CMP => {
|
|
|
|
let mut carry = self.cpsr.C();
|
|
|
|
let mut overflow = self.cpsr.V();
|
2019-11-12 22:56:40 +00:00
|
|
|
let result = self.alu_sub_flags(op1, op2, &mut carry, &mut overflow);
|
2019-11-08 22:55:09 +00:00
|
|
|
self.alu_update_flags(result, true, carry, overflow);
|
|
|
|
}
|
|
|
|
OpFormat5::MOV => {
|
|
|
|
self.set_reg(dst_reg, op2 as u32);
|
2019-07-07 22:58:09 +01:00
|
|
|
if dst_reg == REG_PC {
|
2020-02-10 23:28:20 +00:00
|
|
|
result = CpuAction::FlushPipeline;
|
2020-02-11 00:24:24 +00:00
|
|
|
self.reload_pipeline16(sb);
|
2019-07-07 22:58:09 +01:00
|
|
|
}
|
2019-07-05 14:10:21 +01:00
|
|
|
}
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
2019-11-08 22:55:09 +00:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
result
|
2019-07-02 23:26:48 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 6
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_ldr_pc(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = insn.raw.bit_range(8..11) as usize;
|
|
|
|
|
|
|
|
let ofs = insn.word8() as Addr;
|
|
|
|
let addr = (self.pc & !3) + ofs;
|
|
|
|
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2019-11-08 22:55:09 +00:00
|
|
|
let data = self.ldr_word(addr, sb);
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, addr);
|
2019-07-05 11:07:07 +01:00
|
|
|
|
2020-01-17 14:10:17 +00:00
|
|
|
self.gpr[rd] = data;
|
|
|
|
|
2019-07-03 00:15:16 +01:00
|
|
|
// +1I
|
|
|
|
self.add_cycle();
|
|
|
|
|
2020-02-10 23:28:20 +00:00
|
|
|
CpuAction::AdvancePC
|
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,
|
2019-08-08 17:46:56 +01:00
|
|
|
sb: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2019-07-06 13:51:58 +01:00
|
|
|
addr: Addr,
|
2020-02-07 15:16:52 +00:00
|
|
|
is_transferring_bytes: bool,
|
2020-02-10 23:28:20 +00:00
|
|
|
) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = (insn.raw & 0b111) as usize;
|
2019-07-03 23:56:50 +01:00
|
|
|
if insn.is_load() {
|
2020-01-17 14:10:17 +00:00
|
|
|
let data = if is_transferring_bytes {
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle8(sb, addr);
|
|
|
|
sb.read_8(addr) as u32
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle32(sb, addr);
|
|
|
|
self.ldr_word(addr, sb)
|
2019-07-03 23:56:50 +01:00
|
|
|
};
|
|
|
|
|
2020-01-17 14:10:17 +00:00
|
|
|
self.gpr[rd] = data;
|
2019-07-03 23:56:50 +01:00
|
|
|
|
|
|
|
// +1I
|
|
|
|
self.add_cycle();
|
|
|
|
} else {
|
2020-01-17 14:10:17 +00:00
|
|
|
let value = self.get_reg(rd);
|
|
|
|
if is_transferring_bytes {
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle8(sb, addr);
|
2019-11-08 22:55:09 +00:00
|
|
|
self.write_8(addr, value as u8, sb);
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle32(sb, addr);
|
2019-11-08 22:55:09 +00:00
|
|
|
self.write_32(addr, value, sb);
|
2019-07-03 23:56:50 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 7
|
2019-07-06 13:51:58 +01:00
|
|
|
fn exec_thumb_ldr_str_reg_offset(
|
|
|
|
&mut self,
|
2019-08-08 17:46:56 +01:00
|
|
|
bus: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2020-02-10 23:28:20 +00:00
|
|
|
) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rb = insn.raw.bit_range(3..6) as usize;
|
|
|
|
let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]);
|
|
|
|
self.do_exec_thumb_ldr_str(bus, insn, addr, insn.raw.bit(10))
|
2019-07-06 13:51:58 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 8
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_ldr_str_shb(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rb = insn.raw.bit_range(3..6) as usize;
|
|
|
|
let rd = (insn.raw & 0b111) as usize;
|
|
|
|
|
|
|
|
let addr = self.gpr[rb].wrapping_add(self.gpr[insn.ro()]);
|
2019-07-06 21:38:08 +01:00
|
|
|
match (
|
|
|
|
insn.flag(ThumbInstruction::FLAG_SIGN_EXTEND),
|
|
|
|
insn.flag(ThumbInstruction::FLAG_HALFWORD),
|
|
|
|
) {
|
|
|
|
(false, false) =>
|
|
|
|
/* strh */
|
|
|
|
{
|
2019-11-08 22:55:09 +00:00
|
|
|
self.write_16(addr, self.gpr[rd] as u16, sb);
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, addr);
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
(false, true) =>
|
|
|
|
/* ldrh */
|
|
|
|
{
|
2019-08-08 17:46:56 +01:00
|
|
|
self.gpr[rd] = self.ldr_half(addr, sb);
|
|
|
|
self.S_cycle16(sb, addr);
|
|
|
|
self.add_cycle();
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
(true, false) =>
|
|
|
|
/* ldsb */
|
|
|
|
{
|
2019-08-08 17:46:56 +01:00
|
|
|
let val = sb.read_8(addr) as i8 as i32 as u32;
|
2019-07-06 21:38:08 +01:00
|
|
|
self.gpr[rd] = val;
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle8(sb, addr);
|
|
|
|
self.add_cycle();
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
(true, true) =>
|
|
|
|
/* ldsh */
|
|
|
|
{
|
2019-08-08 17:46:56 +01:00
|
|
|
let val = self.ldr_sign_half(addr, sb);
|
2019-07-06 21:38:08 +01:00
|
|
|
self.gpr[rd] = val;
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, addr);
|
|
|
|
self.add_cycle();
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 9
|
2019-07-06 13:51:58 +01:00
|
|
|
fn exec_thumb_ldr_str_imm_offset(
|
|
|
|
&mut self,
|
2019-08-08 17:46:56 +01:00
|
|
|
sb: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2020-02-10 23:28:20 +00:00
|
|
|
) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rb = insn.raw.bit_range(3..6) as usize;
|
|
|
|
|
2019-11-08 22:55:09 +00:00
|
|
|
let offset = if insn.raw.bit(12) {
|
2019-07-06 13:51:58 +01:00
|
|
|
insn.offset5()
|
|
|
|
} else {
|
|
|
|
(insn.offset5() << 3) >> 1
|
|
|
|
};
|
2020-01-17 14:10:17 +00:00
|
|
|
let addr = self.gpr[rb].wrapping_add(offset as u32);
|
|
|
|
self.do_exec_thumb_ldr_str(sb, insn, addr, insn.raw.bit(12))
|
2019-07-06 13:51:58 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 10
|
2019-07-05 13:50:14 +01:00
|
|
|
fn exec_thumb_ldr_str_halfword(
|
|
|
|
&mut self,
|
2019-08-08 17:46:56 +01:00
|
|
|
sb: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2020-02-10 23:28:20 +00:00
|
|
|
) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rb = insn.raw.bit_range(3..6) as usize;
|
|
|
|
let rd = (insn.raw & 0b111) as usize;
|
|
|
|
let base = self.gpr[rb] as i32;
|
2019-07-05 13:50:14 +01:00
|
|
|
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
|
|
|
|
if insn.is_load() {
|
2019-08-08 17:46:56 +01:00
|
|
|
let data = self.ldr_half(addr, sb);
|
|
|
|
self.S_cycle16(sb, addr);
|
2019-07-05 13:50:14 +01:00
|
|
|
self.add_cycle();
|
2020-01-17 14:10:17 +00:00
|
|
|
self.gpr[rd] = data as u32;
|
2019-07-05 13:50:14 +01:00
|
|
|
} else {
|
2020-01-17 14:10:17 +00:00
|
|
|
self.write_16(addr, self.gpr[rd] as u16, sb);
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, addr);
|
2019-07-05 13:50:14 +01:00
|
|
|
}
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-05 13:50:14 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 11
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_ldr_str_sp(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2019-07-06 21:38:08 +01:00
|
|
|
let addr = self.gpr[REG_SP] + (insn.word8() as Addr);
|
2020-02-08 12:19:57 +00:00
|
|
|
let rd = insn.raw.bit_range(8..11) as usize;
|
|
|
|
if insn.is_load() {
|
|
|
|
let data = self.ldr_word(addr, sb);
|
|
|
|
self.S_cycle16(sb, addr);
|
|
|
|
self.add_cycle();
|
|
|
|
self.gpr[rd] = data;
|
|
|
|
} else {
|
|
|
|
self.write_32(addr, self.gpr[rd], sb);
|
|
|
|
self.N_cycle16(sb, addr);
|
|
|
|
}
|
|
|
|
self.N_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 12
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_load_address(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2020-01-17 14:10:17 +00:00
|
|
|
let rd = insn.raw.bit_range(8..11) as usize;
|
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)
|
|
|
|
};
|
2020-01-17 14:10:17 +00:00
|
|
|
self.gpr[rd] = result;
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-05 11:20:19 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 13
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_add_sp(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2019-07-05 01:46:04 +01:00
|
|
|
let op1 = self.gpr[REG_SP] as i32;
|
|
|
|
let op2 = insn.sword7();
|
|
|
|
|
2019-11-08 22:55:09 +00:00
|
|
|
self.gpr[REG_SP] = op1.wrapping_add(op2) as u32;
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-05 01:46:04 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 14
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_push_pop(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2020-02-10 23:28:20 +00:00
|
|
|
let mut result = CpuAction::AdvancePC;
|
|
|
|
|
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();
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, self.pc);
|
|
|
|
let mut first = true;
|
2019-07-05 01:28:02 +01:00
|
|
|
if is_pop {
|
2019-07-20 14:07:19 +01:00
|
|
|
for r in 0..8 {
|
|
|
|
if rlist.bit(r) {
|
2019-08-08 17:46:56 +01:00
|
|
|
pop(self, sb, r);
|
|
|
|
if first {
|
|
|
|
self.add_cycle();
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
self.S_cycle16(sb, self.gpr[REG_SP]);
|
|
|
|
}
|
2019-07-20 14:07:19 +01:00
|
|
|
}
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
|
|
|
if pc_lr_flag {
|
2019-08-08 17:46:56 +01:00
|
|
|
pop(self, sb, REG_PC);
|
2019-07-06 21:38:08 +01:00
|
|
|
self.pc = self.pc & !1;
|
2020-02-10 23:28:20 +00:00
|
|
|
result = CpuAction::FlushPipeline;
|
2020-02-11 00:24:24 +00:00
|
|
|
self.reload_pipeline16(sb);
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2019-07-05 01:28:02 +01:00
|
|
|
} else {
|
|
|
|
if pc_lr_flag {
|
2019-08-08 17:46:56 +01:00
|
|
|
push(self, sb, REG_LR);
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
2019-07-20 14:07:19 +01:00
|
|
|
for r in (0..8).rev() {
|
|
|
|
if rlist.bit(r) {
|
2019-08-08 17:46:56 +01:00
|
|
|
push(self, sb, r);
|
|
|
|
if first {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
self.S_cycle16(sb, self.gpr[REG_SP]);
|
|
|
|
}
|
2019-07-20 14:07:19 +01:00
|
|
|
}
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-10 23:28:20 +00:00
|
|
|
result
|
2019-07-05 01:28:02 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 15
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_ldm_stm(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
2020-02-10 23:28:20 +00:00
|
|
|
let mut result = CpuAction::AdvancePC;
|
|
|
|
|
2019-07-06 21:38:08 +01:00
|
|
|
// (From GBATEK) Execution Time: nS+1N+1I (POP), (n+1)S+2N+1I (POP PC), or (n-1)S+2N (PUSH).
|
|
|
|
|
2020-01-17 14:10:17 +00:00
|
|
|
let rb = insn.raw.bit_range(8..11) as usize;
|
|
|
|
let base_reg = rb;
|
2019-07-06 21:38:08 +01:00
|
|
|
let is_load = insn.is_load();
|
|
|
|
|
2020-01-17 11:07:26 +00:00
|
|
|
let align_preserve = self.gpr[base_reg] & 3;
|
|
|
|
let mut addr = self.gpr[base_reg] & !3;
|
2019-07-06 21:38:08 +01:00
|
|
|
let rlist = insn.register_list();
|
2019-08-08 17:46:56 +01:00
|
|
|
self.N_cycle16(sb, self.pc);
|
|
|
|
let mut first = true;
|
2019-11-08 22:55:09 +00:00
|
|
|
|
|
|
|
if rlist != 0 {
|
|
|
|
if is_load {
|
2020-01-17 11:07:26 +00:00
|
|
|
let writeback = !rlist.bit(base_reg);
|
2019-11-08 22:55:09 +00:00
|
|
|
for r in 0..8 {
|
|
|
|
if rlist.bit(r) {
|
2020-01-17 11:07:26 +00:00
|
|
|
let val = sb.read_32(addr);
|
2019-11-08 22:55:09 +00:00
|
|
|
if first {
|
|
|
|
first = false;
|
|
|
|
self.add_cycle();
|
|
|
|
} else {
|
|
|
|
self.S_cycle16(sb, addr);
|
|
|
|
}
|
|
|
|
addr += 4;
|
2019-08-08 17:46:56 +01:00
|
|
|
self.add_cycle();
|
2019-11-08 22:55:09 +00:00
|
|
|
self.set_reg(r, val);
|
2019-08-08 17:46:56 +01:00
|
|
|
}
|
2019-07-20 14:07:19 +01:00
|
|
|
}
|
2019-11-08 22:55:09 +00:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-01-17 11:07:26 +00:00
|
|
|
if writeback {
|
|
|
|
self.gpr[base_reg] = addr + align_preserve;
|
|
|
|
}
|
2019-11-08 22:55:09 +00:00
|
|
|
} else {
|
|
|
|
for r in 0..8 {
|
|
|
|
if rlist.bit(r) {
|
2020-01-17 11:07:26 +00:00
|
|
|
let v = if r != base_reg {
|
|
|
|
self.gpr[r]
|
|
|
|
} else {
|
|
|
|
if first {
|
|
|
|
addr
|
|
|
|
} else {
|
|
|
|
addr + (rlist.count_ones() - 1) * 4
|
|
|
|
}
|
|
|
|
};
|
2019-11-08 22:55:09 +00:00
|
|
|
if first {
|
|
|
|
first = false;
|
|
|
|
} else {
|
|
|
|
self.S_cycle16(sb, addr);
|
|
|
|
}
|
2020-01-17 11:07:26 +00:00
|
|
|
sb.write_32(addr, v);
|
2019-11-08 22:55:09 +00:00
|
|
|
addr += 4;
|
2019-08-08 17:46:56 +01:00
|
|
|
}
|
2020-01-17 11:07:26 +00:00
|
|
|
self.gpr[base_reg] = addr + align_preserve;
|
2019-07-20 14:07:19 +01:00
|
|
|
}
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
2019-11-08 22:55:09 +00:00
|
|
|
} else {
|
|
|
|
// From gbatek.htm: Empty Rlist: R15 loaded/stored (ARMv4 only), and Rb=Rb+40h (ARMv4-v5).
|
|
|
|
if is_load {
|
2020-01-17 11:07:26 +00:00
|
|
|
let val = sb.read_32(addr);
|
2019-11-08 22:55:09 +00:00
|
|
|
self.set_reg(REG_PC, val & !1);
|
2020-02-10 23:28:20 +00:00
|
|
|
result = CpuAction::FlushPipeline;
|
2020-02-11 00:24:24 +00:00
|
|
|
self.reload_pipeline16(sb);
|
2019-11-08 22:55:09 +00:00
|
|
|
} else {
|
2020-01-17 11:07:26 +00:00
|
|
|
sb.write_32(addr, self.pc + 2);
|
2019-11-08 22:55:09 +00:00
|
|
|
}
|
|
|
|
addr += 0x40;
|
2020-01-17 11:07:26 +00:00
|
|
|
self.gpr[base_reg] = addr + align_preserve;
|
2019-11-08 22:55:09 +00:00
|
|
|
}
|
2019-07-09 00:20:32 +01:00
|
|
|
|
2020-02-10 23:28:20 +00:00
|
|
|
result
|
2019-07-06 21:38:08 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 16
|
2019-07-03 23:56:50 +01:00
|
|
|
fn exec_thumb_branch_with_cond(
|
|
|
|
&mut self,
|
2019-08-08 17:46:56 +01:00
|
|
|
sb: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2020-02-10 23:28:20 +00:00
|
|
|
) -> CpuAction {
|
2019-07-03 23:56:50 +01:00
|
|
|
if !self.check_arm_cond(insn.cond()) {
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc + 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
CpuAction::AdvancePC
|
2019-07-03 23:56:50 +01:00
|
|
|
} else {
|
2019-07-27 19:30:27 +01:00
|
|
|
let offset = insn.bcond_offset();
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc);
|
2019-07-05 14:01:16 +01:00
|
|
|
self.pc = (self.pc as i32).wrapping_add(offset) as u32;
|
2020-02-11 00:24:24 +00:00
|
|
|
self.reload_pipeline16(sb);
|
2020-02-10 23:28:20 +00:00
|
|
|
CpuAction::FlushPipeline
|
2019-07-03 23:56:50 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 17
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_swi(&mut self, sb: &mut SysBus, _insn: &ThumbInstruction) -> CpuAction {
|
2020-02-08 12:19:57 +00:00
|
|
|
self.N_cycle16(sb, self.pc);
|
|
|
|
self.exception(sb, Exception::SoftwareInterrupt, self.pc - 2);
|
2020-02-10 23:28:20 +00:00
|
|
|
CpuAction::FlushPipeline
|
2020-02-08 12:19:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Format 18
|
2020-03-28 12:47:10 +00:00
|
|
|
fn exec_thumb_branch(&mut self, sb: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
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-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc);
|
2020-02-11 00:24:24 +00:00
|
|
|
self.reload_pipeline16(sb);
|
2020-02-10 23:28:20 +00:00
|
|
|
CpuAction::FlushPipeline
|
2019-07-05 14:21:24 +01:00
|
|
|
}
|
|
|
|
|
2020-02-08 12:19:57 +00:00
|
|
|
/// Format 19
|
2019-07-05 13:34:52 +01:00
|
|
|
fn exec_thumb_branch_long_with_link(
|
|
|
|
&mut self,
|
2019-08-08 17:46:56 +01:00
|
|
|
sb: &mut SysBus,
|
2020-03-28 12:47:10 +00:00
|
|
|
insn: &ThumbInstruction,
|
2020-02-10 23:28:20 +00:00
|
|
|
) -> CpuAction {
|
2019-07-05 13:34:52 +01:00
|
|
|
let mut off = insn.offset11();
|
|
|
|
if insn.flag(ThumbInstruction::FLAG_LOW_OFFSET) {
|
2019-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc);
|
2019-07-05 13:34:52 +01:00
|
|
|
off = off << 1;
|
|
|
|
let next_pc = (self.pc - 2) | 1;
|
2019-07-28 23:55:16 +01:00
|
|
|
self.pc = ((self.gpr[REG_LR] & !1) as i32).wrapping_add(off) as u32;
|
2019-07-05 13:34:52 +01:00
|
|
|
self.gpr[REG_LR] = next_pc;
|
2020-02-11 00:24:24 +00:00
|
|
|
self.reload_pipeline16(sb);
|
2020-02-10 23:28:20 +00:00
|
|
|
CpuAction::FlushPipeline
|
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-08-08 17:46:56 +01:00
|
|
|
self.S_cycle16(sb, self.pc);
|
2020-02-10 23:28:20 +00:00
|
|
|
|
|
|
|
CpuAction::AdvancePC
|
2019-07-05 13:34:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-03-28 12:47:10 +00:00
|
|
|
pub fn exec_thumb(&mut self, bus: &mut SysBus, insn: &ThumbInstruction) -> CpuAction {
|
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::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),
|
2020-02-08 12:19:57 +00:00
|
|
|
ThumbFormat::Swi => self.exec_thumb_swi(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-02 23:26:48 +01:00
|
|
|
}
|
2019-07-02 14:57:35 +01:00
|
|
|
}
|
|
|
|
}
|