using System; using System.Text; using System.Runtime.InteropServices; namespace BizHawk.Emulation.CPUs.ARM { /// /// this file contains functions transcribed as closely as possible from arm's docs. theyre all _Prefixed /// partial class ARM { uint _ArchVersion() { return 6; } enum EInstrSet { ARM, THUMB, THUMBEE } EInstrSet _CurrentInstrSet() { return APSR.T == 1 ? EInstrSet.THUMB : EInstrSet.ARM; } void _SelectInstrSet(EInstrSet newset) { //TODO - copy from manual APSR.T = (newset == EInstrSet.THUMB) ? 1U : 0U; } uint _Align(uint value, int level) { return (uint)(value & ~(level - 1)); } bool _InITBlock() { return false; } bool _LastInITBlock() { //how the hell are we going to implement this? return false; } int _LowestSetBit(uint val, int size) { for (int i = 0; i < size; i++) if (_.BITN(i, val) == 1) return i; return size; } void _FlagUnpredictable() { unpredictable = true; } uint _UNPREDICTABLE() { _FlagUnpredictable(); Console.WriteLine("UNPREDICTABLE!"); return 1; } bool _CheckVFPEnabled(bool arg) { return true; } void _SerializeVFP() { } void _VFPExcBarrier() { } uint _PERMANENTLY_UNDEFINED() { Console.WriteLine("PERMANENTLY UNDEFINED! this space will not be allocated in future. (why not?)"); return 0; } ulong _UNKNOWN(int bits, ulong data) { Console.WriteLine("UNKNOWN {0} BITS! (using {0:x16} anyway)", bits, data); return data; } uint _PCStoreValue() { // This function returns the PC value. On architecture versions before ARMv7, it // is permitted to instead return PC+4, provided it does so consistently. It is // used only to describe ARM instructions, so it returns the address of the current // instruction plus 8 (normally) or 12 (when the alternative is permitted). return PC; } uint MemA_Read32(uint addr) { addr = _Align(addr, 4); return bus.Read32(AT.READ, addr); } ulong MemA_Read64(uint addr) { ulong ret = MemA_Read32(addr); ret |= (((ulong)MemA_Read32(addr + 4)) << 32); return ret; } void _SetExclusiveMonitors(uint address, int size) { //TODO!!! boring!! } bool _ExclusiveMonitorsPass(uint address, int size) { //TODO!!! boring!! return true; } void MemA_WriteSingle(uint addr, float val) { MemA_Write32(addr,float_downcast(val)); } float MemA_ReadSingle(uint addr) { return float_upcast(MemA_Read32(addr)); } double MemA_ReadDouble(uint addr) { return double_upcast(MemA_Read64(addr)); } void MemA_WriteDouble(uint addr, double val) { ulong uval = double_downcast(val); //TODO endian MemA_Write32(addr,(uint)uval); MemA_Write32(addr+4,(uint)(uval>>32)); } void MemA_Write32(uint addr, uint val) { addr = _Align(addr, 4); bus.Write32(AT.WRITE, addr, val); } void MemU_Write08(uint addr, uint val) { bus.Write08(AT.WRITE, addr, (byte)val); } byte MemU_Read08(uint addr) { return bus.Read08(AT.READ, addr); } ushort MemU_Read16(uint addr) { return bus.Read08(AT.READ, addr); } void MemU_Write16(uint addr, uint val) { bus.Write16(AT.WRITE, addr, (ushort)val); } void MemU_Write32(uint addr, uint val) { bus.Write32(AT.WRITE, addr, val); } uint MemU_Read32(uint addr) { return bus.Read32(AT.READ, addr); } uint _MemU(uint addr, uint size) { //TODO - differentiate from MemA switch (size) { case 4: return bus.Read32(AT.READ, addr); case 2: return bus.Read16(AT.READ, addr); case 1: return bus.Read08(AT.READ, addr); default: throw new ArgumentException(); } } uint _MemA(uint addr, uint size) { //TODO - differentiate from MemU switch (size) { case 4: return bus.Read32(AT.READ, addr); case 2: return bus.Read16(AT.READ, addr); case 1: return bus.Read08(AT.READ, addr); default: throw new ArgumentException(); } } void _ALUWritePC(uint address) { if (_ArchVersion() >= 7 && _CurrentInstrSet() == EInstrSet.ARM) _BXWritePC(address); else _BranchWritePC(address); } void _BranchWritePC(uint address) { if (_CurrentInstrSet() == EInstrSet.ARM) { if (_ArchVersion() < 6 && ((address & 0x3) != 0)) { _UNPREDICTABLE(); } _BranchTo((uint)(address & ~3)); } else _BranchTo((uint)(address & ~1)); } bool _BigEndian() { return false; } void _BranchTo(uint address) { _R[15] = address; next_instruct_adr = address; } uint _LoadWritePC(uint address) { if (_ArchVersion() >= 5) _BXWritePC(address); else _BranchWritePC(address); return 1; } void _BXWritePC(uint address) { if (_CurrentInstrSet() == EInstrSet.THUMBEE) { throw new InvalidOperationException(); } else { if (_.BIT0(address) == 1) { _SelectInstrSet(EInstrSet.THUMB); _BranchTo((uint)(address & ~1)); } else if (_.BIT1(address) == 0) { _SelectInstrSet(EInstrSet.ARM); _BranchTo(address); } else { _UNPREDICTABLE(); } } } enum SRType { LSL = 0, LSR = 1, ASR = 2, ROR = 3, RRX = 4 } uint _ZeroExtend_32(uint val) { //TODO - any tricky behaviour from doc? return val; } int _SignExtend_32(int bits_present, uint val) { int temp = (int)val; int shift = 32 - bits_present; temp <<= (shift); return temp >> shift; } SRType shift_t; int shift_n; /// /// decodes shifter arguments to shift_t and shift_n /// void _DecodeImmShift(uint arg, uint imm5) { switch (arg) { case _.b00: shift_t = SRType.LSL; shift_n = (int)imm5; break; case _.b01: shift_t = SRType.LSR; if (imm5 == 0) shift_n = 32; else shift_n = (int)imm5; break; case _.b10: shift_t = SRType.LSR; if (imm5 == 0) shift_n = 32; else shift_n = (int)imm5; break; case _.b11: if (imm5 == 0) { shift_t = SRType.RRX; shift_n = 1; } else { shift_t = SRType.ROR; shift_n = (int)imm5; } break; } } bool _UnalignedSupport() { return false; /*TODO*/ } bool _NullCheckIfThumbEE(uint num) { return false; } bool _IsZeroBit(uint value) { return value == 0; } uint _Shift(uint value, SRType type, int amount, Bit carry_in) { uint result; Bit carry_out; _Shift_C(value, type, amount, carry_in, out result, out carry_out); return result; } void _Shift_C(uint value, SRType type, int amount, Bit carry_in, out uint result, out Bit carry_out) { if (type == SRType.RRX && amount == 1) throw new InvalidOperationException("bogus shift"); if (amount == 0) { result = value; carry_out = carry_in; } else { switch (type) { case SRType.LSL: _LSL_C(value, amount, out result, out carry_out); break; case SRType.LSR: _LSR_C(value, amount, out result, out carry_out); break; case SRType.ASR: _ASR_C(value, amount, out result, out carry_out); break; case SRType.ROR: _ROR_C(value, amount, out result, out carry_out); break; case SRType.RRX: _RRX_C(value, carry_in, out result, out carry_out); break; default: throw new ArgumentException(); } } } void _Assert(bool condition) { #if DEBUG System.Diagnostics.Debug.Assert(condition); #endif } void _LSL_C(uint x, int shift, out uint result, out Bit carry_out) { //A2-5 _Assert(shift > 0); ulong extended_x = (ulong)x << shift; result = (uint)extended_x; carry_out = _.BIT32(extended_x); } uint _LSL(uint x, int shift) { //A2-5 _Assert(shift >= 0); if (shift == 0) return x; else { uint result; Bit carry_out; _LSL_C(x, shift, out result, out carry_out); return result; } } void _LSR_C(uint x, int shift, out uint result, out Bit carry_out) { //A2-6 but coded my own way _Assert(shift > 0); result = x >> shift; carry_out = ((x >> (shift - 1)) & 1); } uint _LSR(uint x, int shift) { //A2-6 _Assert(shift >= 0); if (shift == 0) return x; else { uint result; Bit carry_out; _LSR_C(x, shift, out result, out carry_out); return result; } } void _ASR_C(uint x, int shift, out uint result, out Bit carry_out) { //A2-6 but coded my own way _Assert(shift > 0); int temp = (int)x; temp >>= shift; result = (uint)temp; carry_out = ((x >> (shift - 1)) & 1); } uint _ASR(uint x, int shift) { //A2-6 _Assert(shift >= 0); if (shift == 0) return x; else { uint result; Bit carry_out; _ASR_C(x, shift, out result, out carry_out); return result; } } void _ROR_C(uint x, int shift, out uint result, out Bit carry_out) { //A2-6 _Assert(shift != 0); int m = shift & 31; result = _LSR(x, m) | _LSL(x, 32 - m); carry_out = _.BIT31(result); } uint _ROR(uint x, int shift) { //A5-7 //TODO - there is an error in this pseudocode. report it to ARM! (if(n==0) should be if(shift==0) if (shift == 0) return x; else { uint result; Bit carry_out; _ROR_C(x, shift, out result, out carry_out); return result; } } void _RRX_C(uint x, Bit carry_in, out uint result, out Bit carry_out) { //A2-7 result = ((carry_in) ? (uint)0x80000000 : (uint)0) | (x >> 1); carry_out = _.BIT0(x); } uint _RRX(uint x, Bit carry_in) { uint result; Bit carry_out; _RRX_C(x, carry_in, out result, out carry_out); return result; } float _FPAdd(float op1, float op2, bool fpscr_controlled) { //TODO return op1 + op2; } double _FPAdd(double op1, double op2, bool fpscr_controlled) { //TODO return op1 + op2; } bool _CheckAdvSIMDOrVFPEnabled(bool x, bool y) { //TODO return true; } static uint[] __VFPExpandImm32_table; static uint __VFPExpandImm32_calc(uint imm8) { uint a = _.BIT7(imm8); uint B = _.BIT6(imm8)^1; uint b = _.BIT6(imm8); b |= (b << 1); //replicated to 2 bits b |= (b << 2); //replicated to 4 bits b |= (b << 1); //replicated to 5 bits uint cdefgh = imm8 & 0x3F; return (a<<31)|(B<<30)|(b<<29)|(cdefgh<<24); } static ulong[] __VFPExpandImm64_table; static ulong __VFPExpandImm64_calc(uint imm8) { uint a = _.BIT7(imm8); uint B = _.BIT6(imm8) ^ 1; uint b = _.BIT6(imm8); b |= (b << 1); //replicated to 2 bits b |= (b << 2); //replicated to 4 bits b |= (b << 4); //replicated to 8 bits uint cdefgh = imm8 & 0x3F; uint uintret = (a << 31) | (B << 30) | (b << 29) | (cdefgh << 20); ulong ulongret = uintret << 32; return ulongret; } uint _VFPExpandImm32(uint imm8) { if(__VFPExpandImm32_table == null) { __VFPExpandImm32_table = new uint[256]; for(uint i=0;i<256;i++) __VFPExpandImm32_table[i] = __VFPExpandImm32_calc(i); } return __VFPExpandImm32_table[imm8]; } ulong _VFPExpandImm64(uint imm8) { if (__VFPExpandImm64_table == null) { __VFPExpandImm64_table = new ulong[256]; for (uint i = 0; i < 256; i++) __VFPExpandImm64_table[i] = __VFPExpandImm64_calc(i); } return __VFPExpandImm64_table[imm8]; } uint _ARMExpandImm(uint imm12) { //A5-10 uint imm32; Bit trash; _ARMExpandImm_C(imm12, 0, out imm32, out trash); //DEVIATION: docs say to pass in APSR.C even though it doesnt matter. return imm32; } void _ARMExpandImm_C(uint imm12, Bit carry_in, out uint imm32, out Bit carry_out) { //A5-10 uint unrotated_value = imm12 & 0xFF; _Shift_C(unrotated_value, SRType.ROR, 2 * (int)((imm12 >> 8) & 0xF), carry_in, out imm32, out carry_out); } uint alu_result_32; Bit alu_carry_out; Bit alu_overflow; void _AddWithCarry32(uint x, uint y, Bit carry_in) { ulong unsigned_sum = (ulong)x + (ulong)y + (uint)carry_in; long signed_sum = (long)(int)x + (long)(int)y + (uint)carry_in; alu_result_32 = (uint)(unsigned_sum & 0xFFFFFFFF); alu_carry_out = (alu_result_32 == unsigned_sum) ? 0 : 1; alu_overflow = ((long)(int)alu_result_32 == signed_sum) ? 0 : 1; } } }