From 7e98af80c2ff6b9b48e0aa6bd4fd6836a08e84a6 Mon Sep 17 00:00:00 2001 From: Michel Heily Date: Wed, 13 Nov 2019 00:56:40 +0200 Subject: [PATCH] Fixes to ALU, passing mGBA carry tests Former-commit-id: 14a4293b2511c7c63a920e6344e89b209ca7c5ee --- src/core/arm7tdmi/alu.rs | 79 ++++++++++++++++++++++++-------- src/core/arm7tdmi/arm/display.rs | 2 +- src/core/arm7tdmi/arm/exec.rs | 36 +++++++-------- src/core/arm7tdmi/arm/mod.rs | 8 ++-- src/core/arm7tdmi/cpu.rs | 2 +- src/core/arm7tdmi/thumb/exec.rs | 48 +++++++++---------- 6 files changed, 107 insertions(+), 68 deletions(-) diff --git a/src/core/arm7tdmi/alu.rs b/src/core/arm7tdmi/alu.rs index 280f5aa..3607dd9 100644 --- a/src/core/arm7tdmi/alu.rs +++ b/src/core/arm7tdmi/alu.rs @@ -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 { + pub fn decode_rotated_immediate(&self) -> Option { 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 +} diff --git a/src/core/arm7tdmi/arm/display.rs b/src/core/arm7tdmi/arm/display.rs index 467c262..8dc89fe 100644 --- a/src/core/arm7tdmi/arm/display.rs +++ b/src/core/arm7tdmi/arm/display.rs @@ -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 }; diff --git a/src/core/arm7tdmi/arm/exec.rs b/src/core/arm7tdmi/arm/exec.rs index 0bc9754..dda30a3 100644 --- a/src/core/arm7tdmi/arm/exec.rs +++ b/src/core/arm7tdmi/arm/exec.rs @@ -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(); } diff --git a/src/core/arm7tdmi/arm/mod.rs b/src/core/arm7tdmi/arm/mod.rs index 3b06e9e..226b563 100644 --- a/src/core/arm7tdmi/arm/mod.rs +++ b/src/core/arm7tdmi/arm/mod.rs @@ -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)) } diff --git a/src/core/arm7tdmi/cpu.rs b/src/core/arm7tdmi/cpu.rs index be6f8b0..74350e9 100644 --- a/src/core/arm7tdmi/cpu.rs +++ b/src/core/arm7tdmi/cpu.rs @@ -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 { diff --git a/src/core/arm7tdmi/thumb/exec.rs b/src/core/arm7tdmi/thumb/exec.rs index ccd53f2..4bb62b3 100644 --- a/src/core/arm7tdmi/thumb/exec.rs +++ b/src/core/arm7tdmi/thumb/exec.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 => {