Fixes to ALU, passing mGBA carry tests

Former-commit-id: 14a4293b2511c7c63a920e6344e89b209ca7c5ee
This commit is contained in:
Michel Heily 2019-11-13 00:56:40 +02:00
parent 88b908f2a0
commit 7e98af80c2
6 changed files with 107 additions and 68 deletions

View file

@ -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
}

View file

@ -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
};

View file

@ -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();
}

View file

@ -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))
}

View file

@ -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 {

View file

@ -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 => {