// Copyright (C) 2003 Dolphin Project. // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, version 2.0 or later versions. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License 2.0 for more details. // A copy of the GPL 2.0 should have been included with the program. // If not, see http://www.gnu.org/licenses/ // Official SVN repository and contact information can be found at // http://code.google.com/p/dolphin-emu/ #include "Common.h" #include "ArmEmitter.h" #include "CPUDetect.h" #include #include namespace ArmGen { void ARMXEmitter::SetCodePtr(u8 *ptr) { code = ptr; startcode = code; } const u8 *ARMXEmitter::GetCodePtr() const { return code; } u8 *ARMXEmitter::GetWritableCodePtr() { return code; } void ARMXEmitter::ReserveCodeSpace(u32 bytes) { for (u32 i = 0; i < bytes/4; i++) Write32(0xE1200070); //bkpt 0 } const u8 *ARMXEmitter::AlignCode16() { ReserveCodeSpace((-(s32)code) & 15); return code; } const u8 *ARMXEmitter::AlignCodePage() { ReserveCodeSpace((-(s32)code) & 4095); return code; } void ARMXEmitter::Flush() { __builtin___clear_cache (startcode, code); SLEEP(0); } void ARMXEmitter::SetCC(CCFlags cond) { condition = cond << 28; } void ARMXEmitter::NOP(int count) { for (int i = 0; i < count; i++) { Write32(condition | 0x01A00000); } } void ARMXEmitter::SETEND(bool BE) { //SETEND is non-conditional Write32( 0xF1010000 | (BE << 9)); } void ARMXEmitter::BKPT(u16 arg) { Write32(condition | 0x01200070 | (arg << 4 & 0x000FFF00) | (arg & 0x0000000F)); } void ARMXEmitter::YIELD() { Write32(condition | 0x0320F001); } FixupBranch ARMXEmitter::B() { FixupBranch branch; branch.type = 0; // Zero for B branch.ptr = code; branch.condition = condition; //We'll write NOP here for now. Write32(condition | 0x01A00000); return branch; } FixupBranch ARMXEmitter::BL() { FixupBranch branch; branch.type = 1; // Zero for B branch.ptr = code; branch.condition = condition; //We'll write NOP here for now. Write32(condition | 0x01A00000); return branch; } FixupBranch ARMXEmitter::B_CC(CCFlags Cond) { FixupBranch branch; branch.type = 0; // Zero for B branch.ptr = code; branch.condition = Cond << 28; //We'll write NOP here for now. Write32(condition | 0x01A00000); return branch; } void ARMXEmitter::B_CC(CCFlags Cond, const void *fnptr) { s32 distance = (s32)fnptr - (s32(code) + 8); _assert_msg_(DYNA_REC, distance > -33554432 && distance <= 33554432, "B_CC out of range (%p calls %p)", code, fnptr); Write32((Cond << 28) | 0x0A000000 | ((distance >> 2) & 0x00FFFFFF)); } FixupBranch ARMXEmitter::BL_CC(CCFlags Cond) { FixupBranch branch; branch.type = 1; // Zero for B branch.ptr = code; branch.condition = Cond << 28; //We'll write NOP here for now. Write32(condition | 0x01A00000); return branch; } void ARMXEmitter::SetJumpTarget(FixupBranch const &branch) { s32 distance = (s32(code) - 8) - (s32)branch.ptr; _assert_msg_(DYNA_REC, distance > -33554432 && distance <= 33554432, "SetJumpTarget out of range (%p calls %p)", code, branch.ptr); if(branch.type == 0) // B *(u32*)branch.ptr = (u32)(branch.condition | (10 << 24) | ((distance >> 2) & 0x00FFFFFF)); else // BL *(u32*)branch.ptr = (u32)(branch.condition | 0x0B000000 | ((distance >> 2) & 0x00FFFFFF)); } void ARMXEmitter::B (const void *fnptr) { s32 distance = (s32)fnptr - (s32(code) + 8); _assert_msg_(DYNA_REC, distance > -33554432 && distance <= 33554432, "B out of range (%p calls %p)", code, fnptr); Write32(condition | 0x0A000000 | ((distance >> 2) & 0x00FFFFFF)); } void ARMXEmitter::B(ARMReg src) { Write32(condition | (18 << 20) | (0xFFF << 8) | (1 << 4) | src); } void ARMXEmitter::BL(const void *fnptr) { s32 distance = (s32)fnptr - (s32(code) + 8); _assert_msg_(DYNA_REC, distance > -33554432 && distance <= 33554432, "BL out of range (%p calls %p)", code, fnptr); Write32(condition | 0x0B000000 | ((distance >> 2) & 0x00FFFFFF)); } void ARMXEmitter::BL(ARMReg src) { Write32(condition | 0x12FFF30 | src); } void ARMXEmitter::PUSH(const int num, ...) { u16 RegList = 0; u8 Reg; int i; va_list vl; va_start(vl, num); for (i=0;i> 16 : Rm); } void ARMXEmitter::WriteInstruction (u32 Op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags) // This can get renamed later { u32 op = InstOps[Op][Rm.GetType()]; // Type always decided by last operand u32 Data = Rm.GetData(); if (Rm.GetType() == TYPE_IMM) { switch (Op) { // MOV cases that support IMM16 case 16: case 17: Data = Rm.Imm16(); break; default: break; } } if (op == -1) _assert_msg_(DYNA_REC, false, "%s not yet support %d", InstNames[Op], Rm.GetType()); Write32(condition | (op << 21) | (SetFlags ? (1 << 20) : 0) | Rn << 16 | Rd << 12 | Data); } // Data Operations void ARMXEmitter::LSL (ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, false, dest, src, op2);} void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, Operand2 op2) { WriteShiftedDataOp(0, true, dest, src, op2);} void ARMXEmitter::LSL (ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, false, dest, src, op2);} void ARMXEmitter::LSLS(ARMReg dest, ARMReg src, ARMReg op2) { WriteShiftedDataOp(1, true, dest, src, op2);} void ARMXEmitter::MUL (ARMReg dest, ARMReg src, ARMReg op2) { Write32(condition | (dest << 16) | (src << 8) | (9 << 4) | op2); } void ARMXEmitter::MULS(ARMReg dest, ARMReg src, ARMReg op2) { Write32(condition | (1 << 20) | (dest << 16) | (src << 8) | (9 << 4) | op2); } void ARMXEmitter::SXTB (ARMReg dest, ARMReg op2) { Write32(condition | (0x6AF << 16) | (dest << 12) | (7 << 4) | op2); } void ARMXEmitter::SXTH (ARMReg dest, ARMReg op2, u8 rotation) { SXTAH(dest, (ARMReg)15, op2, rotation); } void ARMXEmitter::SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation) { // bits ten and 11 are the rotation amount, see 8.8.232 for more // information Write32(condition | (0x6B << 20) | (src << 16) | (dest << 12) | (rotation << 10) | (7 << 4) | op2); } void ARMXEmitter::REV (ARMReg dest, ARMReg src ) { Write32(condition | (107 << 20) | (15 << 16) | (dest << 12) | (243 << 4) | src); } void ARMXEmitter::_MSR (bool nzcvq, bool g, Operand2 op2) { Write32(condition | (0x320F << 12) | (nzcvq << 19) | (g << 18) | op2.Imm12Mod()); } void ARMXEmitter::_MSR (bool nzcvq, bool g, ARMReg src) { Write32(condition | (0x120F << 12) | (nzcvq << 19) | (g << 18) | src); } void ARMXEmitter::MRS (ARMReg dest) { Write32(condition | (16 << 20) | (15 << 16) | (dest << 12)); } void ARMXEmitter::WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2) { if (op2.GetData() == 0) // Don't index Write32(condition | (op << 20) | (dest << 16) | (src << 12) | op2.Imm12()); else Write32(condition | (op << 20) | (3 << 23) | (dest << 16) | (src << 12) | op2.Imm12()); } void ARMXEmitter::STR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x40, dest, src, op);} void ARMXEmitter::STRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x44, dest, src, op);} void ARMXEmitter::STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) { Write32(condition | (0x60 << 20) | (Index << 24) | (Add << 23) | (dest << 16) | (base << 12) | offset); } void ARMXEmitter::LDREX(ARMReg dest, ARMReg base) { Write32(condition | (25 << 20) | (base << 16) | (dest << 12) | 0xF9F); } void ARMXEmitter::STREX(ARMReg dest, ARMReg base, ARMReg op) { _assert_msg_(DYNA_REC, (dest != base && dest != op), "STREX dest can't be other two registers"); Write32(condition | (24 << 20) | (base << 16) | (dest << 12) | (0xF9 << 4) | op); } void ARMXEmitter::DMB () { Write32(0xF57FF05E); } void ARMXEmitter::LDR (ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x41, src, dest, op);} void ARMXEmitter::LDRB(ARMReg dest, ARMReg src, Operand2 op) { WriteStoreOp(0x45, src, dest, op);} void ARMXEmitter::LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add) { Write32(condition | (0x61 << 20) | (Index << 24) | (Add << 23) | (base << 16) | (dest << 12) | offset); } void ARMXEmitter::WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList) { Write32(condition | (op << 20) | (WriteBack << 21) | (dest << 16) | RegList); } void ARMXEmitter::STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...) { u16 RegList = 0; u8 Reg; int i; va_list vl; va_start(vl, Regnum); for (i=0;i