2019-06-28 09:46:36 +01:00
|
|
|
use crate::bit::BitIndex;
|
|
|
|
|
2019-07-02 14:42:55 +01:00
|
|
|
use crate::arm7tdmi::bus::{Bus, MemoryAccessType::*, MemoryAccessWidth::*};
|
2019-06-30 14:59:19 +01:00
|
|
|
use crate::arm7tdmi::cpu::{Core, CpuExecResult, CpuPipelineAction};
|
|
|
|
use crate::arm7tdmi::exception::Exception;
|
2019-07-02 14:42:55 +01:00
|
|
|
use crate::arm7tdmi::psr::RegPSR;
|
2019-07-02 14:57:35 +01:00
|
|
|
use crate::arm7tdmi::{Addr, CpuError, CpuResult, CpuState, DecodedInstruction, REG_PC};
|
2019-06-30 14:59:19 +01:00
|
|
|
|
2019-06-26 22:48:43 +01:00
|
|
|
use super::{
|
2019-07-02 14:57:35 +01:00
|
|
|
ArmFormat, ArmInstruction, ArmOpCode, ArmRegisterShift, ArmShiftType, ArmShiftedValue,
|
2019-06-26 22:48:43 +01:00
|
|
|
};
|
2019-06-25 03:35:52 +01:00
|
|
|
|
|
|
|
impl Core {
|
2019-07-05 11:07:07 +01:00
|
|
|
pub fn exec_arm(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuExecResult {
|
2019-06-28 23:52:10 +01:00
|
|
|
if !self.check_arm_cond(insn.cond) {
|
|
|
|
return Ok(CpuPipelineAction::IncPC);
|
|
|
|
}
|
|
|
|
match insn.fmt {
|
2019-07-05 11:07:07 +01:00
|
|
|
ArmFormat::BX => self.exec_bx(bus, insn),
|
|
|
|
ArmFormat::B_BL => self.exec_b_bl(bus, insn),
|
|
|
|
ArmFormat::DP => self.exec_data_processing(bus, insn),
|
|
|
|
ArmFormat::SWI => self.exec_swi(bus, insn),
|
|
|
|
ArmFormat::LDR_STR => self.exec_ldr_str(bus, insn),
|
|
|
|
ArmFormat::MSR_REG => self.exec_msr_reg(bus, insn),
|
2019-07-02 14:57:35 +01:00
|
|
|
_ => Err(CpuError::UnimplementedCpuInstruction(
|
|
|
|
insn.pc,
|
|
|
|
insn.raw,
|
|
|
|
DecodedInstruction::Arm(insn),
|
|
|
|
)),
|
2019-06-28 23:52:10 +01:00
|
|
|
}
|
2019-06-26 22:48:43 +01:00
|
|
|
}
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
/// Cycles 2S+1N
|
2019-07-05 11:07:07 +01:00
|
|
|
fn exec_b_bl(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuResult<CpuPipelineAction> {
|
2019-06-25 11:28:02 +01:00
|
|
|
if insn.link_flag() {
|
2019-07-01 17:24:52 +01:00
|
|
|
self.set_reg(14, (insn.pc + (self.word_size() as u32)) & !0b1);
|
2019-06-25 11:28:02 +01:00
|
|
|
}
|
2019-07-01 15:45:29 +01:00
|
|
|
|
2019-07-02 14:42:55 +01:00
|
|
|
self.pc = (self.pc as i32).wrapping_add(insn.branch_offset()) as u32 & !1;
|
2019-06-30 14:59:19 +01:00
|
|
|
|
2019-06-28 23:52:10 +01:00
|
|
|
Ok(CpuPipelineAction::Flush)
|
2019-06-25 11:28:02 +01:00
|
|
|
}
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
/// Cycles 2S+1N
|
2019-07-05 11:07:07 +01:00
|
|
|
fn exec_bx(&mut self, bus: &mut Bus, insn: ArmInstruction) -> CpuResult<CpuPipelineAction> {
|
2019-06-25 11:28:02 +01:00
|
|
|
let rn = self.get_reg(insn.rn());
|
|
|
|
if rn.bit(0) {
|
2019-06-26 22:48:43 +01:00
|
|
|
self.cpsr.set_state(CpuState::THUMB);
|
2019-06-25 11:28:02 +01:00
|
|
|
} else {
|
2019-06-26 22:48:43 +01:00
|
|
|
self.cpsr.set_state(CpuState::ARM);
|
2019-06-25 11:28:02 +01:00
|
|
|
}
|
|
|
|
|
2019-06-28 23:52:10 +01:00
|
|
|
self.pc = rn & !1;
|
2019-06-30 14:59:19 +01:00
|
|
|
|
2019-06-28 23:52:10 +01:00
|
|
|
Ok(CpuPipelineAction::Flush)
|
2019-06-25 11:28:02 +01:00
|
|
|
}
|
|
|
|
|
2019-07-05 11:07:07 +01:00
|
|
|
fn exec_swi(&mut self, _bus: &mut Bus, _insn: ArmInstruction) -> CpuResult<CpuPipelineAction> {
|
2019-06-28 09:46:36 +01:00
|
|
|
self.exception(Exception::SoftwareInterrupt);
|
2019-06-28 23:52:10 +01:00
|
|
|
Ok(CpuPipelineAction::Flush)
|
2019-06-28 09:46:36 +01:00
|
|
|
}
|
|
|
|
|
2019-07-02 14:42:55 +01:00
|
|
|
fn exec_msr_reg(
|
|
|
|
&mut self,
|
2019-07-05 11:07:07 +01:00
|
|
|
bus: &mut Bus,
|
2019-07-02 14:42:55 +01:00
|
|
|
insn: ArmInstruction,
|
|
|
|
) -> CpuResult<CpuPipelineAction> {
|
|
|
|
let new_psr = RegPSR::new(self.get_reg(insn.rm()));
|
|
|
|
let old_mode = self.cpsr.mode();
|
|
|
|
if insn.spsr_flag() {
|
|
|
|
if let Some(index) = old_mode.spsr_index() {
|
|
|
|
self.spsr[index] = new_psr;
|
|
|
|
} else {
|
|
|
|
panic!("tried to change spsr from invalid mode {}", old_mode)
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if old_mode != new_psr.mode() {
|
|
|
|
self.change_mode(new_psr.mode());
|
|
|
|
}
|
|
|
|
self.cpsr = new_psr;
|
|
|
|
}
|
|
|
|
Ok(CpuPipelineAction::IncPC)
|
|
|
|
}
|
|
|
|
|
2019-06-28 09:46:36 +01:00
|
|
|
fn barrel_shift(val: i32, amount: u32, shift: ArmShiftType) -> i32 {
|
2019-06-26 22:48:43 +01:00
|
|
|
match shift {
|
|
|
|
ArmShiftType::LSL => val.wrapping_shl(amount),
|
|
|
|
ArmShiftType::LSR => (val as u32).wrapping_shr(amount) as i32,
|
|
|
|
ArmShiftType::ASR => val.wrapping_shr(amount),
|
|
|
|
ArmShiftType::ROR => val.rotate_right(amount),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-28 09:46:36 +01:00
|
|
|
fn register_shift(&mut self, reg: usize, shift: ArmRegisterShift) -> CpuResult<i32> {
|
2019-06-26 22:48:43 +01:00
|
|
|
let val = self.get_reg(reg) as i32;
|
|
|
|
match shift {
|
2019-06-28 09:46:36 +01:00
|
|
|
ArmRegisterShift::ShiftAmount(amount, shift) => {
|
|
|
|
Ok(Core::barrel_shift(val, amount, shift))
|
|
|
|
}
|
2019-06-26 22:48:43 +01:00
|
|
|
ArmRegisterShift::ShiftRegister(reg, shift) => {
|
2019-06-28 23:52:10 +01:00
|
|
|
if reg != REG_PC {
|
2019-06-28 09:46:36 +01:00
|
|
|
Ok(Core::barrel_shift(val, self.get_reg(reg) & 0xff, shift))
|
|
|
|
} else {
|
|
|
|
Err(CpuError::IllegalInstruction)
|
|
|
|
}
|
2019-06-26 22:48:43 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn alu_sub_update_carry(a: i32, b: i32, carry: &mut bool) -> i32 {
|
|
|
|
let res = a.wrapping_sub(b);
|
|
|
|
*carry = res > a;
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
|
|
|
fn alu_add_update_carry(a: i32, b: i32, carry: &mut bool) -> i32 {
|
2019-07-01 17:25:42 +01:00
|
|
|
let res = a.wrapping_add(b);
|
2019-06-26 22:48:43 +01:00
|
|
|
*carry = res < a;
|
|
|
|
res
|
|
|
|
}
|
|
|
|
|
2019-07-03 00:15:16 +01:00
|
|
|
pub fn alu(
|
|
|
|
&mut self,
|
|
|
|
opcode: ArmOpCode,
|
|
|
|
op1: i32,
|
|
|
|
op2: i32,
|
|
|
|
set_cond_flags: bool,
|
|
|
|
) -> Option<i32> {
|
2019-06-26 22:48:43 +01:00
|
|
|
let C = self.cpsr.C() as i32;
|
|
|
|
|
|
|
|
let mut carry = self.cpsr.C();
|
|
|
|
let mut overflow = self.cpsr.V();
|
|
|
|
|
|
|
|
let result = match opcode {
|
|
|
|
ArmOpCode::AND | ArmOpCode::TST => op1 & op2,
|
|
|
|
ArmOpCode::EOR | ArmOpCode::TEQ => op1 ^ op2,
|
|
|
|
ArmOpCode::SUB | ArmOpCode::CMP => Self::alu_sub_update_carry(op1, op2, &mut carry),
|
|
|
|
ArmOpCode::RSB => Self::alu_sub_update_carry(op2, op1, &mut carry),
|
|
|
|
ArmOpCode::ADD | ArmOpCode::CMN => Self::alu_add_update_carry(op1, op2, &mut carry),
|
|
|
|
ArmOpCode::ADC => Self::alu_add_update_carry(op1, op2.wrapping_add(C), &mut carry),
|
|
|
|
ArmOpCode::SBC => Self::alu_add_update_carry(op1, op2.wrapping_sub(1 - C), &mut carry),
|
|
|
|
ArmOpCode::RSC => Self::alu_add_update_carry(op2, op1.wrapping_sub(1 - C), &mut carry),
|
|
|
|
ArmOpCode::ORR => op1 | op2,
|
|
|
|
ArmOpCode::MOV => op2,
|
|
|
|
ArmOpCode::BIC => op1 & (!op2),
|
|
|
|
ArmOpCode::MVN => !op2,
|
|
|
|
};
|
|
|
|
|
|
|
|
if set_cond_flags {
|
|
|
|
self.cpsr.set_N(result < 0);
|
|
|
|
self.cpsr.set_Z(result == 0);
|
|
|
|
self.cpsr.set_C(carry);
|
|
|
|
self.cpsr.set_V(overflow);
|
|
|
|
}
|
|
|
|
|
|
|
|
match opcode {
|
|
|
|
ArmOpCode::TST | ArmOpCode::TEQ | ArmOpCode::CMP | ArmOpCode::CMN => None,
|
|
|
|
_ => Some(result),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
/// Logical/Arithmetic ALU operations
|
2019-07-01 15:45:29 +01:00
|
|
|
///
|
2019-06-30 14:59:19 +01:00
|
|
|
/// Cycles: 1S+x+y (from GBATEK)
|
|
|
|
/// Add x=1I cycles if Op2 shifted-by-register. Add y=1S+1N cycles if Rd=R15.
|
2019-06-26 22:48:43 +01:00
|
|
|
fn exec_data_processing(
|
|
|
|
&mut self,
|
2019-07-05 11:07:07 +01:00
|
|
|
bus: &mut Bus,
|
2019-06-26 22:48:43 +01:00
|
|
|
insn: ArmInstruction,
|
|
|
|
) -> CpuResult<CpuPipelineAction> {
|
|
|
|
// TODO handle carry flag
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
let mut pipeline_action = CpuPipelineAction::IncPC;
|
|
|
|
|
2019-07-02 14:42:55 +01:00
|
|
|
let op1 = if insn.rn() == REG_PC {
|
|
|
|
self.pc as i32 // prefething
|
|
|
|
} else {
|
|
|
|
self.get_reg(insn.rn()) as i32
|
|
|
|
};
|
2019-06-26 22:48:43 +01:00
|
|
|
let op2 = insn.operand2()?;
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
let rd = insn.rd();
|
|
|
|
|
2019-06-26 22:48:43 +01:00
|
|
|
let op2: i32 = match op2 {
|
|
|
|
ArmShiftedValue::RotatedImmediate(immediate, rotate) => {
|
2019-06-28 09:46:36 +01:00
|
|
|
Ok(immediate.rotate_right(rotate) as i32)
|
2019-06-26 22:48:43 +01:00
|
|
|
}
|
|
|
|
ArmShiftedValue::ShiftedRegister {
|
|
|
|
reg,
|
|
|
|
shift,
|
|
|
|
added: _,
|
2019-06-30 14:59:19 +01:00
|
|
|
} => {
|
|
|
|
// +1I
|
|
|
|
self.add_cycle();
|
|
|
|
self.register_shift(reg, shift)
|
|
|
|
}
|
2019-06-26 22:48:43 +01:00
|
|
|
_ => unreachable!(),
|
2019-06-28 09:46:36 +01:00
|
|
|
}?;
|
2019-06-26 22:48:43 +01:00
|
|
|
|
|
|
|
let opcode = insn.opcode().unwrap();
|
2019-06-28 09:46:36 +01:00
|
|
|
let set_flags = opcode.is_setting_flags() || insn.set_cond_flag();
|
|
|
|
if let Some(result) = self.alu(opcode, op1, op2, set_flags) {
|
2019-06-30 14:59:19 +01:00
|
|
|
self.set_reg(rd, result as u32);
|
2019-07-02 14:42:55 +01:00
|
|
|
if rd == REG_PC {
|
2019-06-30 14:59:19 +01:00
|
|
|
pipeline_action = CpuPipelineAction::Flush;
|
|
|
|
}
|
2019-06-26 22:48:43 +01:00
|
|
|
}
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
Ok(pipeline_action)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn get_rn_offset(&mut self, insn: &ArmInstruction) -> i32 {
|
|
|
|
// TODO decide if error handling or panic here
|
|
|
|
match insn.ldr_str_offset().unwrap() {
|
|
|
|
ArmShiftedValue::ImmediateValue(offset) => offset,
|
|
|
|
ArmShiftedValue::ShiftedRegister {
|
|
|
|
reg,
|
|
|
|
shift,
|
2019-07-01 15:45:29 +01:00
|
|
|
added: Some(added),
|
2019-06-30 14:59:19 +01:00
|
|
|
} => {
|
|
|
|
let abs = self.register_shift(reg, shift).unwrap();
|
2019-07-01 15:45:29 +01:00
|
|
|
if added {
|
|
|
|
abs
|
|
|
|
} else {
|
|
|
|
-abs
|
|
|
|
}
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
2019-07-01 15:45:29 +01:00
|
|
|
_ => panic!("bad barrel shift"),
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
2019-06-28 23:52:10 +01:00
|
|
|
}
|
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
/// Memory Load/Store
|
|
|
|
/// Instruction | Cycles | Flags | Expl.
|
|
|
|
/// ------------------------------------------------------------------------------
|
|
|
|
/// LDR{cond}{B}{T} Rd,<Address> | 1S+1N+1I+y | ---- | Rd=[Rn+/-<offset>]
|
|
|
|
/// STR{cond}{B}{T} Rd,<Address> | 2N | ---- | [Rn+/-<offset>]=Rd
|
|
|
|
/// ------------------------------------------------------------------------------
|
|
|
|
/// For LDR, add y=1S+1N if Rd=R15.
|
2019-06-28 23:52:10 +01:00
|
|
|
fn exec_ldr_str(
|
|
|
|
&mut self,
|
2019-07-05 11:07:07 +01:00
|
|
|
bus: &mut Bus,
|
2019-06-28 23:52:10 +01:00
|
|
|
insn: ArmInstruction,
|
|
|
|
) -> CpuResult<CpuPipelineAction> {
|
2019-06-30 14:59:19 +01:00
|
|
|
if insn.write_back_flag() && insn.rd() == insn.rn() {
|
|
|
|
return Err(CpuError::IllegalInstruction);
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut pipeline_action = CpuPipelineAction::IncPC;
|
|
|
|
|
|
|
|
let mut addr = self.get_reg(insn.rn());
|
|
|
|
if insn.rn() == REG_PC {
|
2019-07-02 14:42:55 +01:00
|
|
|
addr = insn.pc + 8; // prefetching
|
2019-06-30 14:59:19 +01:00
|
|
|
}
|
|
|
|
let dest = self.get_reg(insn.rd());
|
|
|
|
|
|
|
|
let offset = self.get_rn_offset(&insn);
|
|
|
|
|
|
|
|
let effective_addr = (addr as i32).wrapping_add(offset) as Addr;
|
|
|
|
addr = if insn.pre_index_flag() {
|
|
|
|
effective_addr
|
2019-07-01 15:45:29 +01:00
|
|
|
} else {
|
2019-06-30 14:59:19 +01:00
|
|
|
addr
|
|
|
|
};
|
|
|
|
|
|
|
|
if insn.load_flag() {
|
|
|
|
let data = if insn.transfer_size() == 1 {
|
|
|
|
// +1N
|
2019-07-05 11:07:07 +01:00
|
|
|
self.load_8(addr, bus) as u32
|
2019-06-30 14:59:19 +01:00
|
|
|
} else {
|
|
|
|
// +1N
|
2019-07-05 11:07:07 +01:00
|
|
|
self.load_32(addr, bus)
|
2019-06-30 14:59:19 +01:00
|
|
|
};
|
2019-07-01 15:45:29 +01:00
|
|
|
|
2019-06-30 14:59:19 +01:00
|
|
|
self.set_reg(insn.rd(), data);
|
|
|
|
|
|
|
|
// +1I
|
|
|
|
self.add_cycle();
|
|
|
|
// +y
|
|
|
|
if insn.rd() == REG_PC {
|
|
|
|
pipeline_action = CpuPipelineAction::Flush;
|
|
|
|
}
|
|
|
|
} else {
|
2019-07-03 23:56:11 +01:00
|
|
|
let value = self.get_reg(insn.rd());
|
2019-06-30 14:59:19 +01:00
|
|
|
if insn.transfer_size() == 1 {
|
2019-07-05 11:07:07 +01:00
|
|
|
self.store_8(addr, value as u8, bus);
|
2019-06-30 14:59:19 +01:00
|
|
|
} else {
|
|
|
|
// +1N
|
2019-07-05 11:07:07 +01:00
|
|
|
self.store_32(addr, value, bus);
|
2019-06-30 14:59:19 +01:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
if insn.write_back_flag() {
|
|
|
|
self.set_reg(insn.rn(), effective_addr as u32)
|
|
|
|
}
|
|
|
|
|
2019-07-01 15:45:29 +01:00
|
|
|
Ok(pipeline_action)
|
2019-06-25 03:35:52 +01:00
|
|
|
}
|
2019-06-26 22:48:43 +01:00
|
|
|
}
|