use bit::BitIndex; use super::{Core, CpuError, CpuResult, REG_PC}; #[derive(Debug, Primitive, PartialEq)] pub enum AluOpCode { AND = 0b0000, EOR = 0b0001, SUB = 0b0010, RSB = 0b0011, ADD = 0b0100, ADC = 0b0101, SBC = 0b0110, RSC = 0b0111, TST = 0b1000, TEQ = 0b1001, CMP = 0b1010, CMN = 0b1011, ORR = 0b1100, MOV = 0b1101, BIC = 0b1110, MVN = 0b1111, } impl AluOpCode { pub fn is_setting_flags(&self) -> bool { use AluOpCode::*; match self { TST | TEQ | CMP | CMN => true, _ => false, } } pub fn is_logical(&self) -> bool { use AluOpCode::*; match self { MOV | MVN | ORR | EOR | AND | BIC | TST | TEQ => true, _ => false, } } pub fn is_arithmetic(&self) -> bool { use AluOpCode::*; match self { ADD | ADC | SUB | SBC | RSB | RSC | CMP | CMN => true, _ => false, } } } #[derive(Debug, PartialEq, Primitive)] pub enum BarrelShiftOpCode { LSL = 0, LSR = 1, ASR = 2, ROR = 3, } #[derive(Debug, PartialEq)] pub enum ShiftRegisterBy { ByAmount(u32), ByRegister(usize), } #[derive(Debug, PartialEq)] pub struct ShiftedRegister { pub reg: usize, pub shift_by: ShiftRegisterBy, pub bs_op: BarrelShiftOpCode, pub added: Option<bool>, } #[derive(Debug, PartialEq)] pub enum BarrelShifterValue { ImmediateValue(u32), RotatedImmediate(u32, u32), ShiftedRegister(ShiftedRegister), } impl BarrelShifterValue { /// Decode operand2 as an immediate value pub fn decode_rotated_immediate(&self) -> Option<u32> { if let BarrelShifterValue::RotatedImmediate(immediate, rotate) = self { return Some(immediate.rotate_right(*rotate) as u32); } None } pub fn shifted_register( reg: usize, shift_by: ShiftRegisterBy, bs_op: BarrelShiftOpCode, added: Option<bool>, ) -> BarrelShifterValue { let shft_reg = ShiftedRegister { reg, shift_by, bs_op, added, }; BarrelShifterValue::ShiftedRegister(shft_reg) } } impl Core { pub fn lsl(&mut self, val: u32, amount: u32, carry_in: bool) -> u32 { match amount { 0 => { self.bs_carry_out = carry_in; val } x if x < 32 => { self.bs_carry_out = val.wrapping_shr(32 - x) & 1 == 1; val << x } 32 => { self.bs_carry_out = val & 1 == 1; 0 } _ => { self.bs_carry_out = false; 0 } } } pub fn lsr(&mut self, val: u32, amount: u32, carry_in: bool, immediate: bool) -> u32 { if amount != 0 { match amount { x if x < 32 => { self.bs_carry_out = (val >> (amount - 1) & 1) == 1; val >> amount } _ => { self.bs_carry_out = false; 0 } } } else if immediate { self.bs_carry_out = val.bit(31); 0 } else { self.bs_carry_out = carry_in; val } } pub fn asr(&mut self, val: u32, amount: u32, carry_in: bool, immediate: bool) -> u32 { let amount = if immediate && amount == 0 { 32 } else { amount }; match amount { 0 => { self.bs_carry_out = carry_in; val } x if x < 32 => { self.bs_carry_out = val.wrapping_shr(amount - 1) & 1 == 1; (val as i32).wrapping_shr(amount) as u32 } _ => { let bit31 = val.bit(31); self.bs_carry_out = bit31; if bit31 { 0xffffffff } else { 0 } } } } pub fn rrx(&mut self, val: u32, carry_in: bool) -> u32 { let old_c = carry_in as i32; self.bs_carry_out = val & 0b1 != 0; (((val as u32) >> 1) as i32 | (old_c << 31)) as u32 } pub fn ror( &mut self, val: u32, amount: u32, carry_in: bool, immediate: bool, rrx: bool, ) -> u32 { match amount { 0 => { if immediate & rrx { self.rrx(val, carry_in) } else { val } } _ => { let amount = amount % 32; let val = if amount != 0 { val.rotate_right(amount) } else { val }; self.bs_carry_out = (val as u32).bit(31); val } } } /// Performs a generic barrel shifter operation pub fn barrel_shift_op( &mut self, shift: BarrelShiftOpCode, val: u32, amount: u32, carry_in: bool, immediate: bool, ) -> u32 { // // From GBATEK: // Zero Shift Amount (Shift Register by Immediate, with Immediate=0) // LSL#0: No shift performed, ie. directly Op2=Rm, the C flag is NOT affected. // LSR#0: Interpreted as LSR#32, ie. Op2 becomes zero, C becomes Bit 31 of Rm. // ASR#0: Interpreted as ASR#32, ie. Op2 and C are filled by Bit 31 of Rm. // ROR#0: Interpreted as RRX#1 (RCR), like ROR#1, but Op2 Bit 31 set to old C. // // From ARM7TDMI Datasheet: // 1 LSL by 32 has result zero, carry out equal to bit 0 of Rm. // 2 LSL by more than 32 has result zero, carry out zero. // 3 LSR by 32 has result zero, carry out equal to bit 31 of Rm. // 4 LSR by more than 32 has result zero, carry out zero. // 5 ASR by 32 or more has result filled with and carry out equal to bit 31 of Rm. // 6 ROR by 32 has result equal to Rm, carry out equal to bit 31 of Rm. // 7 ROR by n where n is greater than 32 will give the same result and carry out // as ROR by n-32; therefore repeatedly subtract 32 from n until the amount is // in the range 1 to 32 and see above. // match shift { BarrelShiftOpCode::LSL => self.lsl(val, amount, carry_in), BarrelShiftOpCode::LSR => self.lsr(val, amount, carry_in, immediate), BarrelShiftOpCode::ASR => self.asr(val, amount, carry_in, immediate), BarrelShiftOpCode::ROR => self.ror(val, amount, carry_in, immediate, true), } } pub fn register_shift(&mut self, shift: ShiftedRegister) -> CpuResult<u32> { let mut val = self.get_reg(shift.reg); let carry = self.cpsr.C(); match shift.shift_by { ShiftRegisterBy::ByAmount(amount) => { let result = self.barrel_shift_op(shift.bs_op, val, amount, carry, true); Ok(result) } ShiftRegisterBy::ByRegister(rs) => { self.add_cycle(); // +1I if shift.reg == REG_PC { val = val + 4; // PC prefetching } if rs != REG_PC { let amount = self.get_reg(rs) & 0xff; let result = self.barrel_shift_op(shift.bs_op, val, amount, carry, false); Ok(result) } else { Err(CpuError::IllegalInstruction) } } } } 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 as u32, BarrelShifterValue::ShiftedRegister(shifted_reg) => { let added = shifted_reg.added.unwrap_or(true); let abs = self.register_shift(shifted_reg).unwrap() as u32; if added { abs as u32 } else { (-(abs as i32)) as u32 } } _ => panic!("bad barrel shift"), } } 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 <= a; *overflow = (a as i32).overflowing_sub(b as i32).1; res } 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(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 }