armwrestler-fix: Properly handle misaligned addresses LDR/LDRH/LDRSH
Former-commit-id: 742a7c2b8413fa9d45df1575a0b14b8d1ab697c4
This commit is contained in:
parent
2a66e525b1
commit
009e46f6d5
|
@ -98,11 +98,12 @@ impl BarrelShifterValue {
|
||||||
|
|
||||||
impl Core {
|
impl Core {
|
||||||
/// Performs a generic barrel shifter operation
|
/// Performs a generic barrel shifter operation
|
||||||
fn barrel_shift(
|
pub fn barrel_shift(
|
||||||
&mut self,
|
&mut self,
|
||||||
val: i32,
|
val: i32,
|
||||||
amount: u32,
|
amount: u32,
|
||||||
shift: BarrelShiftOpCode,
|
shift: BarrelShiftOpCode,
|
||||||
|
carry: &mut bool,
|
||||||
immediate: bool,
|
immediate: bool,
|
||||||
) -> i32 {
|
) -> i32 {
|
||||||
//
|
//
|
||||||
|
@ -128,33 +129,33 @@ impl Core {
|
||||||
BarrelShiftOpCode::LSL => match amount {
|
BarrelShiftOpCode::LSL => match amount {
|
||||||
0 => val,
|
0 => val,
|
||||||
x if x < 32 => {
|
x if x < 32 => {
|
||||||
self.cpsr.set_C(val.wrapping_shr(32 - x) & 1 == 1);
|
*carry = val.wrapping_shr(32 - x) & 1 == 1;
|
||||||
val << x
|
val << x
|
||||||
}
|
}
|
||||||
32 => {
|
32 => {
|
||||||
self.cpsr.set_C(val & 1 == 1);
|
*carry = val & 1 == 1;
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.cpsr.set_C(false);
|
*carry = false;
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
BarrelShiftOpCode::LSR => match amount {
|
BarrelShiftOpCode::LSR => match amount {
|
||||||
0 | 32 => {
|
0 | 32 => {
|
||||||
if immediate {
|
if immediate {
|
||||||
self.cpsr.set_C((val as u32).bit(31));
|
*carry = (val as u32).bit(31);
|
||||||
0
|
0
|
||||||
} else {
|
} else {
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x if x < 32 => {
|
x if x < 32 => {
|
||||||
self.cpsr.set_C(val >> (amount - 1) & 1 == 1);
|
*carry = val >> (amount - 1) & 1 == 1;
|
||||||
((val as u32) >> amount) as i32
|
((val as u32) >> amount) as i32
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
self.cpsr.set_C(false);
|
*carry = false;
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -162,7 +163,7 @@ impl Core {
|
||||||
0 => {
|
0 => {
|
||||||
if immediate {
|
if immediate {
|
||||||
let bit31 = (val as u32).bit(31);
|
let bit31 = (val as u32).bit(31);
|
||||||
self.cpsr.set_C(bit31);
|
*carry = bit31;
|
||||||
if bit31 {
|
if bit31 {
|
||||||
-1
|
-1
|
||||||
} else {
|
} else {
|
||||||
|
@ -173,12 +174,12 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
x if x < 32 => {
|
x if x < 32 => {
|
||||||
self.cpsr.set_C(val.wrapping_shr(amount - 1) & 1 == 1);
|
*carry = val.wrapping_shr(amount - 1) & 1 == 1;
|
||||||
val.wrapping_shr(amount)
|
val.wrapping_shr(amount)
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
let bit31 = (val as u32).bit(31);
|
let bit31 = (val as u32).bit(31);
|
||||||
self.cpsr.set_C(bit31);
|
*carry = bit31;
|
||||||
if bit31 {
|
if bit31 {
|
||||||
-1
|
-1
|
||||||
} else {
|
} else {
|
||||||
|
@ -192,7 +193,7 @@ impl Core {
|
||||||
if immediate {
|
if immediate {
|
||||||
/* RRX */
|
/* RRX */
|
||||||
let old_c = self.cpsr.C() as i32;
|
let old_c = self.cpsr.C() as i32;
|
||||||
self.cpsr.set_C(val & 0b1 != 0);
|
*carry = val & 0b1 != 0;
|
||||||
((val as u32) >> 1) as i32 | (old_c << 31)
|
((val as u32) >> 1) as i32 | (old_c << 31)
|
||||||
} else {
|
} else {
|
||||||
val
|
val
|
||||||
|
@ -205,7 +206,7 @@ impl Core {
|
||||||
} else {
|
} else {
|
||||||
val
|
val
|
||||||
};
|
};
|
||||||
self.cpsr.set_C((val as u32).bit(31));
|
*carry = (val as u32).bit(31);
|
||||||
val
|
val
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -215,13 +216,19 @@ impl Core {
|
||||||
|
|
||||||
pub fn register_shift(&mut self, reg: usize, shift: ShiftedRegister) -> CpuResult<i32> {
|
pub fn register_shift(&mut self, reg: usize, shift: ShiftedRegister) -> CpuResult<i32> {
|
||||||
let val = self.get_reg(reg) as i32;
|
let val = self.get_reg(reg) as i32;
|
||||||
|
let mut carry = self.cpsr.C();
|
||||||
match shift {
|
match shift {
|
||||||
ShiftedRegister::ByAmount(amount, shift) => {
|
ShiftedRegister::ByAmount(amount, shift) => {
|
||||||
Ok(self.barrel_shift(val, amount, shift, true))
|
let result = self.barrel_shift(val, amount, shift, &mut carry, true);
|
||||||
|
self.cpsr.set_C(carry);
|
||||||
|
Ok(result)
|
||||||
}
|
}
|
||||||
ShiftedRegister::ByRegister(reg, shift) => {
|
ShiftedRegister::ByRegister(reg, shift) => {
|
||||||
if reg != REG_PC {
|
if reg != REG_PC {
|
||||||
Ok(self.barrel_shift(val, self.get_reg(reg) & 0xff, shift, false))
|
let result =
|
||||||
|
self.barrel_shift(val, self.get_reg(reg) & 0xff, shift, &mut carry, false);
|
||||||
|
self.cpsr.set_C(carry);
|
||||||
|
Ok(result)
|
||||||
} else {
|
} else {
|
||||||
Err(CpuError::IllegalInstruction)
|
Err(CpuError::IllegalInstruction)
|
||||||
}
|
}
|
||||||
|
|
|
@ -232,7 +232,7 @@ impl Core {
|
||||||
let data = if insn.transfer_size() == 1 {
|
let data = if insn.transfer_size() == 1 {
|
||||||
self.load_8(addr, bus) as u32
|
self.load_8(addr, bus) as u32
|
||||||
} else {
|
} else {
|
||||||
self.load_32(addr, bus)
|
self.ldr_word(addr, bus)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_reg(insn.rd(), data);
|
self.set_reg(insn.rd(), data);
|
||||||
|
@ -287,10 +287,8 @@ impl Core {
|
||||||
if insn.load_flag() {
|
if insn.load_flag() {
|
||||||
let data = match insn.halfword_data_transfer_type().unwrap() {
|
let data = match insn.halfword_data_transfer_type().unwrap() {
|
||||||
ArmHalfwordTransferType::SignedByte => self.load_8(addr, bus) as u8 as i8 as u32,
|
ArmHalfwordTransferType::SignedByte => self.load_8(addr, bus) as u8 as i8 as u32,
|
||||||
ArmHalfwordTransferType::SignedHalfwords => {
|
ArmHalfwordTransferType::SignedHalfwords => self.ldr_sign_half(addr, bus),
|
||||||
self.load_16(addr, bus) as u16 as i16 as u32
|
ArmHalfwordTransferType::UnsignedHalfwords => self.ldr_half(addr, bus),
|
||||||
}
|
|
||||||
ArmHalfwordTransferType::UnsignedHalfwords => self.load_16(addr, bus) as u16 as u32,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_reg(insn.rd(), data);
|
self.set_reg(insn.rd(), data);
|
||||||
|
|
|
@ -5,6 +5,7 @@ use num_traits::Num;
|
||||||
|
|
||||||
pub use super::exception::Exception;
|
pub use super::exception::Exception;
|
||||||
use super::{
|
use super::{
|
||||||
|
alu::*,
|
||||||
arm::*,
|
arm::*,
|
||||||
bus::{Bus, MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*},
|
bus::{Bus, MemoryAccess, MemoryAccessType, MemoryAccessType::*, MemoryAccessWidth::*},
|
||||||
psr::RegPSR,
|
psr::RegPSR,
|
||||||
|
@ -75,7 +76,7 @@ impl Core {
|
||||||
match r {
|
match r {
|
||||||
0...14 => self.gpr[r],
|
0...14 => self.gpr[r],
|
||||||
15 => self.pc,
|
15 => self.pc,
|
||||||
_ => panic!("invalid register"),
|
_ => panic!("invalid register {}", r),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -123,6 +124,48 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn ror(&mut self, value: u32, rotation: u32) -> u32 {
|
||||||
|
let mut carry = false; // we don't need to update the carry flag
|
||||||
|
self.barrel_shift(
|
||||||
|
value as i32,
|
||||||
|
rotation,
|
||||||
|
BarrelShiftOpCode::ROR,
|
||||||
|
&mut carry,
|
||||||
|
false,
|
||||||
|
) as u32
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function for "ldr" instruction that handles misaligned addresses
|
||||||
|
pub fn ldr_word(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||||
|
if addr & 0x3 != 0 {
|
||||||
|
let rotation = (addr & 0x3) << 3;
|
||||||
|
let value = self.load_32(addr & !0x3, bus);
|
||||||
|
self.ror(value, rotation)
|
||||||
|
} else {
|
||||||
|
self.load_32(addr, bus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function for "ldrh" instruction that handles misaligned addresses
|
||||||
|
pub fn ldr_half(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||||
|
if addr & 0x1 != 0 {
|
||||||
|
let rotation = (addr & 0x1) << 3;
|
||||||
|
let value = self.load_16(addr & !0x1, bus);
|
||||||
|
self.ror(value as u32, rotation)
|
||||||
|
} else {
|
||||||
|
self.load_16(addr, bus) as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function for "ldrsh" instruction that handles misaligned addresses
|
||||||
|
pub fn ldr_sign_half(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||||
|
if addr & 0x1 != 0 {
|
||||||
|
self.load_8(addr, bus) as i8 as i32 as u32
|
||||||
|
} else {
|
||||||
|
self.load_16(addr, bus) as i16 as i32 as u32
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn get_registers(&self) -> [u32; 15] {
|
pub fn get_registers(&self) -> [u32; 15] {
|
||||||
self.gpr.clone()
|
self.gpr.clone()
|
||||||
}
|
}
|
||||||
|
@ -211,20 +254,20 @@ impl Core {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_32(&mut self, addr: Addr, bus: &mut Bus) -> u32 {
|
pub fn load_32(&mut self, addr: Addr, bus: &Bus) -> u32 {
|
||||||
self.add_cycles(addr, bus, self.cycle_type(addr) + MemoryAccess32);
|
self.add_cycles(addr, bus, self.cycle_type(addr) + MemoryAccess32);
|
||||||
self.memreq = addr;
|
self.memreq = addr;
|
||||||
bus.read_32(addr)
|
bus.read_32(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_16(&mut self, addr: Addr, bus: &mut Bus) -> u16 {
|
pub fn load_16(&mut self, addr: Addr, bus: &Bus) -> u16 {
|
||||||
let cycle_type = self.cycle_type(addr);
|
let cycle_type = self.cycle_type(addr);
|
||||||
self.add_cycles(addr, bus, cycle_type + MemoryAccess16);
|
self.add_cycles(addr, bus, cycle_type + MemoryAccess16);
|
||||||
self.memreq = addr;
|
self.memreq = addr;
|
||||||
bus.read_16(addr)
|
bus.read_16(addr)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn load_8(&mut self, addr: Addr, bus: &mut Bus) -> u8 {
|
pub fn load_8(&mut self, addr: Addr, bus: &Bus) -> u8 {
|
||||||
let cycle_type = self.cycle_type(addr);
|
let cycle_type = self.cycle_type(addr);
|
||||||
self.add_cycles(addr, bus, cycle_type + MemoryAccess8);
|
self.add_cycles(addr, bus, cycle_type + MemoryAccess8);
|
||||||
self.memreq = addr;
|
self.memreq = addr;
|
||||||
|
|
|
@ -169,7 +169,7 @@ impl Core {
|
||||||
let data = if insn.is_transferring_bytes() {
|
let data = if insn.is_transferring_bytes() {
|
||||||
self.load_8(addr, bus) as u32
|
self.load_8(addr, bus) as u32
|
||||||
} else {
|
} else {
|
||||||
self.load_32(addr, bus)
|
self.ldr_word(addr, bus)
|
||||||
};
|
};
|
||||||
|
|
||||||
self.set_reg(insn.rd(), data);
|
self.set_reg(insn.rd(), data);
|
||||||
|
@ -216,7 +216,7 @@ impl Core {
|
||||||
(false, true) =>
|
(false, true) =>
|
||||||
/* ldrh */
|
/* ldrh */
|
||||||
{
|
{
|
||||||
self.gpr[rd] = self.load_16(addr, bus) as u32
|
self.gpr[rd] = self.ldr_half(addr, bus)
|
||||||
}
|
}
|
||||||
(true, false) =>
|
(true, false) =>
|
||||||
/* ldsb */
|
/* ldsb */
|
||||||
|
@ -227,7 +227,7 @@ impl Core {
|
||||||
(true, true) =>
|
(true, true) =>
|
||||||
/* ldsh */
|
/* ldsh */
|
||||||
{
|
{
|
||||||
let val = self.load_16(addr, bus) as i16 as i32 as u32;
|
let val = self.ldr_sign_half(addr, bus);
|
||||||
self.gpr[rd] = val;
|
self.gpr[rd] = val;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,7 +257,7 @@ impl Core {
|
||||||
let base = self.gpr[insn.rb()] as i32;
|
let base = self.gpr[insn.rb()] as i32;
|
||||||
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
|
let addr = base.wrapping_add((insn.offset5() << 1) as i32) as Addr;
|
||||||
if insn.is_load() {
|
if insn.is_load() {
|
||||||
let data = self.load_16(addr, bus);
|
let data = self.ldr_half(addr, bus);
|
||||||
self.add_cycle();
|
self.add_cycle();
|
||||||
self.gpr[insn.rd()] = data as u32;
|
self.gpr[insn.rd()] = data as u32;
|
||||||
} else {
|
} else {
|
||||||
|
|
Reference in a new issue