Fixes to ALU, passing mGBA carry tests
Former-commit-id: 14a4293b2511c7c63a920e6344e89b209ca7c5ee
This commit is contained in:
parent
88b908f2a0
commit
7e98af80c2
|
@ -71,16 +71,16 @@ pub struct ShiftedRegister {
|
|||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum BarrelShifterValue {
|
||||
ImmediateValue(i32),
|
||||
ImmediateValue(u32),
|
||||
RotatedImmediate(u32, u32),
|
||||
ShiftedRegister(ShiftedRegister),
|
||||
}
|
||||
|
||||
impl BarrelShifterValue {
|
||||
/// Decode operand2 as an immediate value
|
||||
pub fn decode_rotated_immediate(&self) -> Option<i32> {
|
||||
pub fn decode_rotated_immediate(&self) -> Option<u32> {
|
||||
if let BarrelShifterValue::RotatedImmediate(immediate, rotate) = self {
|
||||
return Some(immediate.rotate_right(*rotate) as i32);
|
||||
return Some(immediate.rotate_right(*rotate) as u32);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -269,43 +269,82 @@ impl Core {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_barrel_shifted_value(&mut self, sval: BarrelShifterValue) -> i32 {
|
||||
pub fn get_barrel_shifted_value(&mut self, sval: BarrelShifterValue) -> u32 {
|
||||
// TODO decide if error handling or panic here
|
||||
match sval {
|
||||
BarrelShifterValue::ImmediateValue(offset) => offset,
|
||||
BarrelShifterValue::ImmediateValue(offset) => offset as u32,
|
||||
BarrelShifterValue::ShiftedRegister(shifted_reg) => {
|
||||
let added = shifted_reg.added.unwrap_or(true);
|
||||
let abs = self.register_shift(shifted_reg).unwrap() as i32;
|
||||
let abs = self.register_shift(shifted_reg).unwrap() as u32;
|
||||
if added {
|
||||
abs
|
||||
abs as u32
|
||||
} else {
|
||||
-abs
|
||||
(-(abs as i32)) as u32
|
||||
}
|
||||
}
|
||||
_ => panic!("bad barrel shift"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn alu_sub_flags(a: i32, b: i32, carry: &mut bool, overflow: &mut bool) -> i32 {
|
||||
pub(super) fn alu_sub_flags(
|
||||
&self,
|
||||
a: u32,
|
||||
b: u32,
|
||||
carry: &mut bool,
|
||||
overflow: &mut bool,
|
||||
) -> u32 {
|
||||
let res = a.wrapping_sub(b);
|
||||
*carry = (b as u32) <= (a as u32);
|
||||
let (_, would_overflow) = a.overflowing_sub(b);
|
||||
*overflow = would_overflow;
|
||||
*carry = b <= a;
|
||||
*overflow = (a as i32).overflowing_sub(b as i32).1;
|
||||
res
|
||||
}
|
||||
|
||||
pub fn alu_add_flags(a: i32, b: i32, carry: &mut bool, overflow: &mut bool) -> i32 {
|
||||
let res = a.wrapping_add(b) as u32;
|
||||
*carry = res < a as u32 || res < b as u32;
|
||||
let (_, would_overflow) = a.overflowing_add(b);
|
||||
*overflow = would_overflow;
|
||||
res as i32
|
||||
pub(super) fn alu_add_flags(
|
||||
&self,
|
||||
a: u32,
|
||||
b: u32,
|
||||
carry: &mut bool,
|
||||
overflow: &mut bool,
|
||||
) -> u32 {
|
||||
let res = a.wrapping_add(b);
|
||||
*carry = add_carry_result(a as u64, b as u64);
|
||||
*overflow = (a as i32).overflowing_add(b as i32).1;
|
||||
res
|
||||
}
|
||||
|
||||
pub fn alu_update_flags(&mut self, result: i32, is_arithmetic: bool, c: bool, v: bool) {
|
||||
self.cpsr.set_N(result < 0);
|
||||
pub(super) fn alu_adc_flags(
|
||||
&self,
|
||||
a: u32,
|
||||
b: u32,
|
||||
carry: &mut bool,
|
||||
overflow: &mut bool,
|
||||
) -> u32 {
|
||||
let c = self.cpsr.C() as u64;
|
||||
let res = (a as u64) + (b as u64) + c;
|
||||
*carry = res > 0xffffffff;
|
||||
*overflow = (!(a ^ b) & (b ^ (res as u32))).bit(31);
|
||||
res as u32
|
||||
}
|
||||
|
||||
pub(super) fn alu_sbc_flags(
|
||||
&self,
|
||||
a: u32,
|
||||
b: u32,
|
||||
carry: &mut bool,
|
||||
overflow: &mut bool,
|
||||
) -> u32 {
|
||||
self.alu_adc_flags(a, !b, carry, overflow)
|
||||
}
|
||||
|
||||
pub fn alu_update_flags(&mut self, result: u32, is_arithmetic: bool, c: bool, v: bool) {
|
||||
self.cpsr.set_N((result as i32) < 0);
|
||||
self.cpsr.set_Z(result == 0);
|
||||
self.cpsr.set_C(c);
|
||||
self.cpsr.set_V(v);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn add_carry_result(a: u64, b: u64) -> bool {
|
||||
a.wrapping_add(b) > 0xffffffff
|
||||
}
|
||||
|
|
|
@ -188,7 +188,7 @@ impl ArmInstruction {
|
|||
let (ofs_string, comment) = match offset {
|
||||
BarrelShifterValue::ImmediateValue(value) => {
|
||||
let value_for_commnet = if self.rn() == REG_PC {
|
||||
value + (self.pc as i32) + 8 // account for pipelining
|
||||
value + self.pc + 8 // account for pipelining
|
||||
} else {
|
||||
value
|
||||
};
|
||||
|
|
|
@ -173,9 +173,9 @@ impl Core {
|
|||
|
||||
self.S_cycle32(sb, self.pc);
|
||||
let mut op1 = if insn.rn() == REG_PC {
|
||||
(insn.pc + 8) as i32
|
||||
(insn.pc + 8)
|
||||
} else {
|
||||
self.get_reg(insn.rn()) as i32
|
||||
self.get_reg(insn.rn())
|
||||
};
|
||||
|
||||
let mut s_flag = insn.set_cond_flag();
|
||||
|
@ -190,16 +190,16 @@ impl Core {
|
|||
}
|
||||
_ => {}
|
||||
}
|
||||
let op2 = self.decode_operand2(op2)? as i32;
|
||||
let op2 = self.decode_operand2(op2)?;
|
||||
|
||||
let reg_rd = insn.rd();
|
||||
if !s_flag {
|
||||
match opcode {
|
||||
TEQ => {
|
||||
return self.write_status_register(sb, false, op2 as u32);
|
||||
return self.write_status_register(sb, false, op2);
|
||||
}
|
||||
CMN => {
|
||||
return self.write_status_register(sb, true, op2 as u32);
|
||||
return self.write_status_register(sb, true, op2);
|
||||
}
|
||||
TST => return self.move_from_status_register(sb, reg_rd, false),
|
||||
CMP => return self.move_from_status_register(sb, reg_rd, true),
|
||||
|
@ -212,19 +212,19 @@ impl Core {
|
|||
s_flag = false;
|
||||
}
|
||||
|
||||
let C = self.cpsr.C() as i32;
|
||||
let C = self.cpsr.C() as u32;
|
||||
let alu_res = if s_flag {
|
||||
let mut carry = self.bs_carry_out;
|
||||
let mut overflow = self.cpsr.V();
|
||||
let result = match opcode {
|
||||
AND | TST => op1 & op2,
|
||||
EOR | TEQ => op1 ^ op2,
|
||||
SUB | CMP => Self::alu_sub_flags(op1, op2, &mut carry, &mut overflow),
|
||||
RSB => Self::alu_sub_flags(op2, op1, &mut carry, &mut overflow),
|
||||
ADD | CMN => Self::alu_add_flags(op1, op2, &mut carry, &mut overflow),
|
||||
ADC => Self::alu_add_flags(op1, op2.wrapping_add(C), &mut carry, &mut overflow),
|
||||
SBC => Self::alu_sub_flags(op1, op2.wrapping_add(1 - C), &mut carry, &mut overflow),
|
||||
RSC => Self::alu_sub_flags(op2, op1.wrapping_add(1 - C), &mut carry, &mut overflow),
|
||||
SUB | CMP => self.alu_sub_flags(op1, op2, &mut carry, &mut overflow),
|
||||
RSB => self.alu_sub_flags(op2, op1, &mut carry, &mut overflow),
|
||||
ADD | CMN => self.alu_add_flags(op1, op2, &mut carry, &mut overflow),
|
||||
ADC => self.alu_adc_flags(op1, op2, &mut carry, &mut overflow),
|
||||
SBC => self.alu_sbc_flags(op1, op2, &mut carry, &mut overflow),
|
||||
RSC => self.alu_sbc_flags(op2, op1, &mut carry, &mut overflow),
|
||||
ORR => op1 | op2,
|
||||
MOV => op2,
|
||||
BIC => op1 & (!op2),
|
||||
|
@ -280,7 +280,7 @@ impl Core {
|
|||
addr = insn.pc + 8; // prefetching
|
||||
}
|
||||
let offset = self.get_barrel_shifted_value(insn.ldr_str_offset());
|
||||
let effective_addr = (addr as i32).wrapping_add(offset) as Addr;
|
||||
let effective_addr = (addr as i32).wrapping_add(offset as i32) as Addr;
|
||||
addr = if insn.pre_index_flag() {
|
||||
effective_addr
|
||||
} else {
|
||||
|
@ -341,7 +341,7 @@ impl Core {
|
|||
|
||||
let offset = self.get_barrel_shifted_value(insn.ldr_str_hs_offset().unwrap());
|
||||
|
||||
let effective_addr = (addr as i32).wrapping_add(offset) as Addr;
|
||||
let effective_addr = (addr as i32).wrapping_add(offset as i32) as Addr;
|
||||
addr = if insn.pre_index_flag() {
|
||||
effective_addr
|
||||
} else {
|
||||
|
@ -529,9 +529,9 @@ impl Core {
|
|||
return Err(CpuError::IllegalInstruction);
|
||||
}
|
||||
|
||||
let op1 = self.get_reg(rm) as i32;
|
||||
let op2 = self.get_reg(rs) as i32;
|
||||
let mut result = op1.wrapping_mul(op2) as u32;
|
||||
let op1 = self.get_reg(rm);
|
||||
let op2 = self.get_reg(rs);
|
||||
let mut result = op1.wrapping_mul(op2);
|
||||
|
||||
if insn.accumulate_flag() {
|
||||
result = result.wrapping_add(self.get_reg(rn));
|
||||
|
@ -588,7 +588,7 @@ impl Core {
|
|||
self.set_reg(rd_hi, (result >> 32) as i32 as u32);
|
||||
self.set_reg(rd_lo, (result & 0xffffffff) as i32 as u32);
|
||||
|
||||
let m = self.get_required_multipiler_array_cycles(self.get_reg(rs) as i32);
|
||||
let m = self.get_required_multipiler_array_cycles(self.get_reg(rs));
|
||||
for _ in 0..m {
|
||||
self.add_cycle();
|
||||
}
|
||||
|
|
|
@ -293,9 +293,9 @@ impl ArmInstruction {
|
|||
})
|
||||
} else {
|
||||
let ofs = if self.add_offset_flag() {
|
||||
ofs as i32
|
||||
ofs as u32
|
||||
} else {
|
||||
-(ofs as i32)
|
||||
(-(ofs as i32) as u32)
|
||||
};
|
||||
BarrelShifterValue::ImmediateValue(ofs)
|
||||
}
|
||||
|
@ -320,9 +320,9 @@ impl ArmInstruction {
|
|||
ArmFormat::LDR_STR_HS_IMM => {
|
||||
let offset8 = (self.raw.bit_range(8..12) << 4) + self.raw.bit_range(0..4);
|
||||
let offset8 = if self.add_offset_flag() {
|
||||
offset8 as i32
|
||||
offset8
|
||||
} else {
|
||||
-(offset8 as i32)
|
||||
(-(offset8 as i32)) as u32
|
||||
};
|
||||
Ok(BarrelShifterValue::ImmediateValue(offset8))
|
||||
}
|
||||
|
|
|
@ -242,7 +242,7 @@ impl Core {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn get_required_multipiler_array_cycles(&self, rs: i32) -> usize {
|
||||
pub(super) fn get_required_multipiler_array_cycles(&self, rs: u32) -> usize {
|
||||
if rs & 0xff == rs {
|
||||
1
|
||||
} else if rs & 0xffff == rs {
|
||||
|
|
|
@ -32,9 +32,9 @@ impl Core {
|
|||
bs_op: insn.format1_op(),
|
||||
added: None,
|
||||
})
|
||||
.unwrap() as i32;
|
||||
.unwrap();
|
||||
|
||||
self.set_reg(insn.rd(), op2 as u32);
|
||||
self.set_reg(insn.rd(), op2);
|
||||
self.alu_update_flags(op2, false, self.bs_carry_out, self.cpsr.V());
|
||||
|
||||
self.S_cycle16(sb, self.pc + 2);
|
||||
|
@ -42,19 +42,19 @@ impl Core {
|
|||
}
|
||||
|
||||
fn exec_thumb_add_sub(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let op1 = self.get_reg(insn.rs()) as i32;
|
||||
let op1 = self.get_reg(insn.rs());
|
||||
let op2 = if insn.is_immediate_operand() {
|
||||
insn.rn() as u32 as i32
|
||||
insn.rn() as u32
|
||||
} else {
|
||||
self.get_reg(insn.rn()) as i32
|
||||
self.get_reg(insn.rn())
|
||||
};
|
||||
|
||||
let mut carry = self.cpsr.C();
|
||||
let mut overflow = self.cpsr.V();
|
||||
let result = if insn.is_subtract() {
|
||||
Self::alu_sub_flags(op1, op2, &mut carry, &mut overflow)
|
||||
self.alu_sub_flags(op1, op2, &mut carry, &mut overflow)
|
||||
} else {
|
||||
Self::alu_add_flags(op1, op2, &mut carry, &mut overflow)
|
||||
self.alu_add_flags(op1, op2, &mut carry, &mut overflow)
|
||||
};
|
||||
self.alu_update_flags(result, true, carry, overflow);
|
||||
self.set_reg(insn.rd(), result as u32);
|
||||
|
@ -70,14 +70,14 @@ impl Core {
|
|||
) -> CpuExecResult {
|
||||
use OpFormat3::*;
|
||||
let op = insn.format3_op();
|
||||
let op1 = self.get_reg(insn.rd()) as i32;
|
||||
let op2_imm = (insn.raw & 0xff) as i32;
|
||||
let op1 = self.get_reg(insn.rd());
|
||||
let op2_imm = (insn.raw & 0xff) as u32;
|
||||
let mut carry = self.cpsr.C();
|
||||
let mut overflow = self.cpsr.V();
|
||||
let result = match op {
|
||||
MOV => op2_imm,
|
||||
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),
|
||||
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),
|
||||
};
|
||||
let arithmetic = op == ADD || op == SUB;
|
||||
self.alu_update_flags(result, arithmetic, carry, overflow);
|
||||
|
@ -91,11 +91,11 @@ impl Core {
|
|||
fn exec_thumb_alu_ops(&mut self, sb: &mut SysBus, insn: ThumbInstruction) -> CpuExecResult {
|
||||
let rd = insn.rd();
|
||||
let rs = insn.rs();
|
||||
let dst = self.get_reg(rd) as i32;
|
||||
let src = self.get_reg(rs) as i32;
|
||||
let dst = self.get_reg(rd);
|
||||
let src = self.get_reg(rs);
|
||||
|
||||
let mut carry = self.cpsr.C();
|
||||
let c = self.cpsr.C() as i32;
|
||||
let c = self.cpsr.C() as u32;
|
||||
let mut overflow = self.cpsr.V();
|
||||
|
||||
use ThumbAluOps::*;
|
||||
|
@ -118,15 +118,15 @@ impl Core {
|
|||
bs_op,
|
||||
Some(true),
|
||||
);
|
||||
let result = self.get_barrel_shifted_value(shft) as i32;
|
||||
let result = self.get_barrel_shifted_value(shft);
|
||||
carry = self.bs_carry_out;
|
||||
result
|
||||
}
|
||||
ADC => Self::alu_add_flags(dst, src, &mut carry, &mut overflow).wrapping_add(c),
|
||||
SBC => Self::alu_sub_flags(dst, src, &mut carry, &mut overflow).wrapping_sub(1 - c),
|
||||
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),
|
||||
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),
|
||||
ORR => dst | src,
|
||||
MUL => {
|
||||
let m = self.get_required_multipiler_array_cycles(src);
|
||||
|
@ -176,13 +176,13 @@ impl Core {
|
|||
} else {
|
||||
insn.rs()
|
||||
};
|
||||
let op1 = self.get_reg(dst_reg) as i32;
|
||||
let op2 = self.get_reg(src_reg) as i32;
|
||||
let op1 = self.get_reg(dst_reg);
|
||||
let op2 = self.get_reg(src_reg);
|
||||
|
||||
match op {
|
||||
OpFormat5::BX => return self.exec_thumb_bx(sb, insn),
|
||||
OpFormat5::ADD => {
|
||||
self.set_reg(dst_reg, op1.wrapping_add(op2) as u32);
|
||||
self.set_reg(dst_reg, op1.wrapping_add(op2));
|
||||
if dst_reg == REG_PC {
|
||||
self.flush_pipeline(sb);
|
||||
}
|
||||
|
@ -190,7 +190,7 @@ impl Core {
|
|||
OpFormat5::CMP => {
|
||||
let mut carry = self.cpsr.C();
|
||||
let mut overflow = self.cpsr.V();
|
||||
let result = Self::alu_sub_flags(op1, op2, &mut carry, &mut overflow);
|
||||
let result = self.alu_sub_flags(op1, op2, &mut carry, &mut overflow);
|
||||
self.alu_update_flags(result, true, carry, overflow);
|
||||
}
|
||||
OpFormat5::MOV => {
|
||||
|
|
Reference in a new issue