2012-11-01 15:19:01 +00:00
|
|
|
// 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
|
2012-11-23 18:41:35 +00:00
|
|
|
// the Free Software Foundation, version 2.0.
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
// 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/
|
|
|
|
|
|
|
|
// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!!
|
|
|
|
|
|
|
|
#ifndef _DOLPHIN_ARM_CODEGEN_
|
|
|
|
#define _DOLPHIN_ARM_CODEGEN_
|
|
|
|
|
|
|
|
#include "Common.h"
|
|
|
|
#include "MemoryUtil.h"
|
2012-12-13 03:15:20 +00:00
|
|
|
#ifdef __SYMBIAN32__
|
|
|
|
#include <signal.h>
|
|
|
|
#endif
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
namespace ArmGen
|
|
|
|
{
|
2012-11-23 18:41:35 +00:00
|
|
|
#undef _IP
|
|
|
|
#undef _SP
|
|
|
|
#undef _LR
|
|
|
|
#undef _PC
|
2012-11-01 15:19:01 +00:00
|
|
|
enum ARMReg
|
|
|
|
{
|
|
|
|
// GPRs
|
|
|
|
R0 = 0, R1, R2, R3, R4, R5,
|
|
|
|
R6, R7, R8, R9, R10, R11,
|
|
|
|
|
|
|
|
// SPRs
|
|
|
|
// R13 - R15 are SP, LR, and PC.
|
|
|
|
// Almost always referred to by name instead of register number
|
|
|
|
R12 = 12, R13 = 13, R14 = 14, R15 = 15,
|
|
|
|
_IP = 12, _SP = 13, _LR = 14, _PC = 15,
|
|
|
|
|
|
|
|
|
|
|
|
// VFP single precision registers
|
2012-11-23 18:41:35 +00:00
|
|
|
S0, S1, S2, S3, S4, S5, S6,
|
2012-11-01 15:19:01 +00:00
|
|
|
S7, S8, S9, S10, S11, S12, S13,
|
|
|
|
S14, S15, S16, S17, S18, S19, S20,
|
|
|
|
S21, S22, S23, S24, S25, S26, S27,
|
|
|
|
S28, S29, S30, S31,
|
|
|
|
|
|
|
|
// VFP Double Precision registers
|
2012-11-23 18:41:35 +00:00
|
|
|
D0, D1, D2, D3, D4, D5, D6, D7,
|
2012-11-01 15:19:01 +00:00
|
|
|
D8, D9, D10, D11, D12, D13, D14, D15,
|
|
|
|
D16, D17, D18, D19, D20, D21, D22, D23,
|
|
|
|
D24, D25, D26, D27, D28, D29, D30, D31,
|
2012-11-23 18:41:35 +00:00
|
|
|
|
|
|
|
// ASIMD Quad-Word registers
|
|
|
|
Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7,
|
|
|
|
Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15,
|
2012-11-01 15:19:01 +00:00
|
|
|
INVALID_REG = 0xFFFFFFFF
|
|
|
|
};
|
|
|
|
|
|
|
|
enum CCFlags
|
|
|
|
{
|
|
|
|
CC_EQ = 0, // Equal
|
|
|
|
CC_NEQ, // Not equal
|
2012-11-02 10:58:56 +00:00
|
|
|
CC_CS, // Carry Set
|
2012-11-01 15:19:01 +00:00
|
|
|
CC_CC, // Carry Clear
|
2012-11-02 10:58:56 +00:00
|
|
|
CC_MI, // Minus (Negative)
|
2012-11-01 15:19:01 +00:00
|
|
|
CC_PL, // Plus
|
|
|
|
CC_VS, // Overflow
|
|
|
|
CC_VC, // No Overflow
|
|
|
|
CC_HI, // Unsigned higher
|
|
|
|
CC_LS, // Unsigned lower or same
|
|
|
|
CC_GE, // Signed greater than or equal
|
|
|
|
CC_LT, // Signed less than
|
|
|
|
CC_GT, // Signed greater than
|
|
|
|
CC_LE, // Signed less than or equal
|
|
|
|
CC_AL, // Always (unconditional) 14
|
2012-11-02 10:58:56 +00:00
|
|
|
CC_HS = 2, // Alias of CC_CS
|
|
|
|
CC_LO = 3, // Alias of CC_CC
|
2012-11-01 15:19:01 +00:00
|
|
|
};
|
|
|
|
const u32 NO_COND = 0xE0000000;
|
|
|
|
|
|
|
|
enum ShiftType
|
|
|
|
{
|
2012-11-23 18:41:35 +00:00
|
|
|
ST_LSL = 0,
|
|
|
|
ST_ASL = 0,
|
|
|
|
ST_LSR = 1,
|
|
|
|
ST_ASR = 2,
|
|
|
|
ST_ROR = 3,
|
|
|
|
ST_RRX = 4
|
2012-11-01 15:19:01 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
enum
|
|
|
|
{
|
|
|
|
NUMGPRs = 13,
|
|
|
|
};
|
|
|
|
|
|
|
|
class ARMXEmitter;
|
|
|
|
|
|
|
|
enum OpType
|
|
|
|
{
|
|
|
|
TYPE_IMM = 0,
|
2012-11-02 10:58:56 +00:00
|
|
|
TYPE_REG,
|
|
|
|
TYPE_IMMSREG,
|
|
|
|
TYPE_RSR,
|
|
|
|
TYPE_MEM
|
2012-11-01 15:19:01 +00:00
|
|
|
};
|
2012-11-02 10:58:56 +00:00
|
|
|
|
2013-01-07 23:26:42 +00:00
|
|
|
|
|
|
|
// This is no longer a proper operand2 class. Need to split up.
|
2012-11-01 15:19:01 +00:00
|
|
|
class Operand2
|
|
|
|
{
|
2012-11-02 10:58:56 +00:00
|
|
|
friend class ARMXEmitter;
|
|
|
|
protected:
|
|
|
|
u32 Value;
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2012-11-02 10:58:56 +00:00
|
|
|
private:
|
2012-11-01 15:19:01 +00:00
|
|
|
OpType Type;
|
2012-11-02 10:58:56 +00:00
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
// IMM types
|
|
|
|
u8 Rotation; // Only for u8 values
|
|
|
|
|
|
|
|
// Register types
|
2012-11-02 10:58:56 +00:00
|
|
|
u8 IndexOrShift;
|
2012-11-01 15:19:01 +00:00
|
|
|
ShiftType Shift;
|
|
|
|
public:
|
2012-11-02 10:58:56 +00:00
|
|
|
OpType GetType()
|
|
|
|
{
|
|
|
|
return Type;
|
|
|
|
}
|
|
|
|
Operand2() {}
|
|
|
|
Operand2(u32 imm, OpType type = TYPE_IMM)
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
|
|
|
Type = type;
|
|
|
|
Value = imm;
|
2012-11-02 10:58:56 +00:00
|
|
|
Rotation = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Operand2(ARMReg Reg)
|
|
|
|
{
|
|
|
|
Type = TYPE_REG;
|
|
|
|
Value = Reg;
|
|
|
|
Rotation = 0;
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
Operand2(u8 imm, u8 rotation)
|
|
|
|
{
|
|
|
|
Type = TYPE_IMM;
|
|
|
|
Value = imm;
|
|
|
|
Rotation = rotation;
|
|
|
|
}
|
2012-11-02 10:58:56 +00:00
|
|
|
Operand2(ARMReg base, ShiftType type, ARMReg shift) // RSR
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2012-11-02 10:58:56 +00:00
|
|
|
Type = TYPE_RSR;
|
2012-11-23 18:41:35 +00:00
|
|
|
_assert_msg_(DYNA_REC, type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount");
|
2012-11-01 15:19:01 +00:00
|
|
|
IndexOrShift = shift;
|
|
|
|
Shift = type;
|
2012-11-02 10:58:56 +00:00
|
|
|
Value = base;
|
|
|
|
}
|
|
|
|
|
|
|
|
Operand2(u8 shift, ShiftType type, ARMReg base)// For IMM shifted register
|
|
|
|
{
|
|
|
|
if(shift == 32) shift = 0;
|
|
|
|
switch (type)
|
|
|
|
{
|
2012-11-23 18:41:35 +00:00
|
|
|
case ST_LSL:
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSL %u", shift);
|
|
|
|
break;
|
2012-11-23 18:41:35 +00:00
|
|
|
case ST_LSR:
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, shift <= 32, "Invalid Operand2: LSR %u", shift);
|
|
|
|
if (!shift)
|
2012-11-23 18:41:35 +00:00
|
|
|
type = ST_LSL;
|
2012-11-02 10:58:56 +00:00
|
|
|
if (shift == 32)
|
|
|
|
shift = 0;
|
|
|
|
break;
|
2012-11-23 18:41:35 +00:00
|
|
|
case ST_ASR:
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: LSR %u", shift);
|
|
|
|
if (!shift)
|
2012-11-23 18:41:35 +00:00
|
|
|
type = ST_LSL;
|
2012-11-02 10:58:56 +00:00
|
|
|
if (shift == 32)
|
|
|
|
shift = 0;
|
|
|
|
break;
|
2012-11-23 18:41:35 +00:00
|
|
|
case ST_ROR:
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, shift < 32, "Invalid Operand2: ROR %u", shift);
|
|
|
|
if (!shift)
|
2012-11-23 18:41:35 +00:00
|
|
|
type = ST_LSL;
|
2012-11-02 10:58:56 +00:00
|
|
|
break;
|
2012-11-23 18:41:35 +00:00
|
|
|
case ST_RRX:
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount");
|
2012-11-23 18:41:35 +00:00
|
|
|
type = ST_ROR;
|
2012-11-02 10:58:56 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
IndexOrShift = shift;
|
|
|
|
Shift = type;
|
|
|
|
Value = base;
|
|
|
|
Type = TYPE_IMMSREG;
|
|
|
|
}
|
|
|
|
const u32 GetData()
|
|
|
|
{
|
|
|
|
switch(Type)
|
|
|
|
{
|
2013-01-08 22:52:11 +00:00
|
|
|
case TYPE_IMM:
|
2012-11-02 10:58:56 +00:00
|
|
|
return Imm12Mod(); // This'll need to be changed later
|
2013-01-08 22:52:11 +00:00
|
|
|
case TYPE_REG:
|
2012-11-02 10:58:56 +00:00
|
|
|
return Rm();
|
2013-01-08 22:52:11 +00:00
|
|
|
case TYPE_IMMSREG:
|
2012-11-02 10:58:56 +00:00
|
|
|
return IMMSR();
|
2013-01-08 22:52:11 +00:00
|
|
|
case TYPE_RSR:
|
2012-11-02 10:58:56 +00:00
|
|
|
return RSR();
|
2013-01-08 22:52:11 +00:00
|
|
|
default:
|
|
|
|
_assert_msg_(DYNA_REC, false, "GetData with Invalid Type");
|
|
|
|
return 0;
|
2012-11-02 10:58:56 +00:00
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
2012-11-02 10:58:56 +00:00
|
|
|
const u32 IMMSR() // IMM shifted register
|
2012-11-01 15:19:01 +00:00
|
|
|
{
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, Type = TYPE_IMMSREG, "IMMSR must be imm shifted register");
|
|
|
|
return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value);
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
|
|
|
const u32 RSR() // Register shifted register
|
|
|
|
{
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, Type == TYPE_RSR, "RSR must be RSR Of Course");
|
|
|
|
return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value;
|
|
|
|
}
|
|
|
|
const u32 Rm()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, Type == TYPE_REG, "Rm must be with Reg");
|
|
|
|
return Value;
|
|
|
|
}
|
|
|
|
|
|
|
|
const u32 Imm5()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm5 not IMM value");
|
|
|
|
return ((Value & 0x0000001F) << 7);
|
2012-11-01 15:19:01 +00:00
|
|
|
}
|
2012-11-23 18:41:35 +00:00
|
|
|
const u32 Imm8()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value");
|
|
|
|
return Value & 0xFF;
|
|
|
|
}
|
2012-11-01 15:19:01 +00:00
|
|
|
const u32 Imm8Rot() // IMM8 with Rotation
|
|
|
|
{
|
2012-11-02 10:58:56 +00:00
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8Rot not IMM value");
|
2012-11-01 15:19:01 +00:00
|
|
|
_assert_msg_(DYNA_REC, (Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation);
|
|
|
|
return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF);
|
|
|
|
}
|
|
|
|
const u32 Imm12()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm12 not IMM");
|
|
|
|
return (Value & 0x00000FFF);
|
|
|
|
}
|
|
|
|
|
|
|
|
const u32 Imm12Mod()
|
|
|
|
{
|
|
|
|
// This is a IMM12 with the top four bits being rotation and the
|
|
|
|
// bottom eight being a IMM. This is for instructions that need to
|
|
|
|
// expand a 8bit IMM to a 32bit value and gives you some rotation as
|
|
|
|
// well.
|
2012-11-02 10:58:56 +00:00
|
|
|
// Each rotation rotates to the right by 2 bits
|
2012-11-01 15:19:01 +00:00
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm12Mod not IMM");
|
|
|
|
return ((Rotation & 0xF) << 8) | (Value & 0xFF);
|
|
|
|
}
|
|
|
|
const u32 Imm16()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM");
|
|
|
|
return ( (Value & 0xF000) << 4) | (Value & 0x0FFF);
|
|
|
|
}
|
|
|
|
const u32 Imm16Low()
|
|
|
|
{
|
|
|
|
return Imm16();
|
|
|
|
}
|
|
|
|
const u32 Imm16High() // Returns high 16bits
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM");
|
|
|
|
return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF);
|
|
|
|
}
|
|
|
|
const u32 Imm24()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm16 not IMM");
|
|
|
|
return (Value & 0x0FFFFFFF);
|
|
|
|
}
|
2012-11-23 18:41:35 +00:00
|
|
|
// NEON and ASIMD specific
|
|
|
|
const u32 Imm8ASIMD()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8ASIMD not IMM");
|
|
|
|
return ((Value & 0x80) << 17) | ((Value & 0x70) << 12) | (Value & 0xF);
|
|
|
|
}
|
|
|
|
const u32 Imm8VFP()
|
|
|
|
{
|
|
|
|
_assert_msg_(DYNA_REC, (Type == TYPE_IMM), "Imm8VFP not IMM");
|
|
|
|
return ((Value & 0xF0) << 12) | (Value & 0xF);
|
|
|
|
}
|
2012-11-02 10:58:56 +00:00
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
};
|
2012-11-02 10:58:56 +00:00
|
|
|
inline Operand2 R(ARMReg Reg) { return Operand2(Reg, TYPE_REG); }
|
|
|
|
inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); }
|
|
|
|
inline Operand2 Mem(void *ptr) { return Operand2((u32)ptr, TYPE_IMM); }
|
2012-11-01 15:19:01 +00:00
|
|
|
//usage: int a[]; ARRAY_OFFSET(a,10)
|
|
|
|
#define ARRAY_OFFSET(array,index) ((u32)((u64)&(array)[index]-(u64)&(array)[0]))
|
|
|
|
//usage: struct {int e;} s; STRUCT_OFFSET(s,e)
|
|
|
|
#define STRUCT_OFFSET(str,elem) ((u32)((u64)&(str).elem-(u64)&(str)))
|
|
|
|
|
|
|
|
struct FixupBranch
|
|
|
|
{
|
|
|
|
u8 *ptr;
|
|
|
|
u32 condition; // Remembers our codition at the time
|
|
|
|
int type; //0 = B 1 = BL
|
|
|
|
};
|
|
|
|
|
|
|
|
typedef const u8* JumpTarget;
|
|
|
|
|
|
|
|
class ARMXEmitter
|
|
|
|
{
|
|
|
|
friend struct OpArg; // for Write8 etc
|
|
|
|
private:
|
|
|
|
u8 *code, *startcode;
|
|
|
|
u32 condition;
|
|
|
|
|
|
|
|
void WriteStoreOp(u32 op, ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void WriteRegStoreOp(u32 op, ARMReg dest, bool WriteBack, u16 RegList);
|
2012-11-02 10:58:56 +00:00
|
|
|
void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, ARMReg op2);
|
|
|
|
void WriteShiftedDataOp(u32 op, bool SetFlags, ARMReg dest, ARMReg src, Operand2 op2);
|
2012-11-23 18:41:35 +00:00
|
|
|
void WriteSignedMultiply(u32 Op, u32 Op2, u32 Op3, ARMReg dest, ARMReg r1, ARMReg r2);
|
2012-11-01 15:19:01 +00:00
|
|
|
|
2012-11-02 10:58:56 +00:00
|
|
|
// New Ops
|
|
|
|
void WriteInstruction(u32 op, ARMReg Rd, ARMReg Rn, Operand2 Rm, bool SetFlags = false);
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
protected:
|
|
|
|
inline void Write32(u32 value) {*(u32*)code = value; code+=4;}
|
|
|
|
|
|
|
|
public:
|
|
|
|
ARMXEmitter() { code = NULL; startcode = NULL; condition = CC_AL << 28;}
|
|
|
|
ARMXEmitter(u8 *code_ptr) { code = code_ptr; startcode = code_ptr; condition = CC_AL << 28;}
|
|
|
|
virtual ~ARMXEmitter() {}
|
|
|
|
|
|
|
|
void SetCodePtr(u8 *ptr);
|
|
|
|
void ReserveCodeSpace(u32 bytes);
|
|
|
|
const u8 *AlignCode16();
|
|
|
|
const u8 *AlignCodePage();
|
|
|
|
const u8 *GetCodePtr() const;
|
2013-01-08 22:52:11 +00:00
|
|
|
void FlushIcache();
|
2012-11-01 15:19:01 +00:00
|
|
|
u8 *GetWritableCodePtr();
|
|
|
|
|
|
|
|
void SetCC(CCFlags cond = CC_AL);
|
|
|
|
|
2012-11-02 10:58:56 +00:00
|
|
|
// Special purpose instructions
|
|
|
|
|
|
|
|
// Dynamic Endian Switching
|
|
|
|
void SETEND(bool BE);
|
2012-11-01 15:19:01 +00:00
|
|
|
// Debug Breakpoint
|
|
|
|
void BKPT(u16 arg);
|
|
|
|
|
|
|
|
// Hint instruction
|
|
|
|
void YIELD();
|
|
|
|
|
|
|
|
// Do nothing
|
|
|
|
void NOP(int count = 1); //nop padding - TODO: fast nop slides, for amd and intel (check their manuals)
|
|
|
|
|
|
|
|
#ifdef CALL
|
|
|
|
#undef CALL
|
|
|
|
#endif
|
|
|
|
|
2012-11-02 10:58:56 +00:00
|
|
|
// Branching
|
2012-11-01 15:19:01 +00:00
|
|
|
FixupBranch B();
|
2012-11-02 10:58:56 +00:00
|
|
|
FixupBranch B_CC(CCFlags Cond);
|
|
|
|
void B_CC(CCFlags Cond, const void *fnptr);
|
2012-11-01 15:19:01 +00:00
|
|
|
FixupBranch BL();
|
2012-11-02 10:58:56 +00:00
|
|
|
FixupBranch BL_CC(CCFlags Cond);
|
2012-11-01 15:19:01 +00:00
|
|
|
void SetJumpTarget(FixupBranch const &branch);
|
2012-11-02 10:58:56 +00:00
|
|
|
|
|
|
|
void B (const void *fnptr);
|
|
|
|
void B (ARMReg src);
|
|
|
|
void BL(const void *fnptr);
|
|
|
|
void BL(ARMReg src);
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
void PUSH(const int num, ...);
|
|
|
|
void POP(const int num, ...);
|
2012-11-02 10:58:56 +00:00
|
|
|
|
|
|
|
// New Data Ops
|
|
|
|
void AND (ARMReg Rd, ARMReg Rn, Operand2 Rm);
|
|
|
|
void ANDS(ARMReg Rd, ARMReg Rn, Operand2 Rm);
|
2012-11-01 15:19:01 +00:00
|
|
|
void EOR (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void EORS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void SUB (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void SUBS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void RSB (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void RSBS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void ADD (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void ADDS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void ADC (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void ADCS(ARMReg dest, ARMReg src, Operand2 op2);
|
2012-11-02 10:58:56 +00:00
|
|
|
void LSL (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void LSL (ARMReg dest, ARMReg src, ARMReg op2);
|
|
|
|
void LSLS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void LSLS(ARMReg dest, ARMReg src, ARMReg op2);
|
2012-11-01 15:19:01 +00:00
|
|
|
void SBC (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void SBCS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void REV (ARMReg dest, ARMReg src );
|
|
|
|
void RSC (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void RSCS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void TST ( ARMReg src, Operand2 op2);
|
|
|
|
void TEQ ( ARMReg src, Operand2 op2);
|
|
|
|
void CMP ( ARMReg src, Operand2 op2);
|
|
|
|
void CMN ( ARMReg src, Operand2 op2);
|
|
|
|
void ORR (ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void ORRS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void MOV (ARMReg dest, Operand2 op2);
|
|
|
|
void MOVS(ARMReg dest, Operand2 op2);
|
2012-11-23 18:41:35 +00:00
|
|
|
void BIC (ARMReg dest, ARMReg src, Operand2 op2); // BIC = ANDN
|
2012-11-01 15:19:01 +00:00
|
|
|
void BICS(ARMReg dest, ARMReg src, Operand2 op2);
|
|
|
|
void MVN (ARMReg dest, Operand2 op2);
|
|
|
|
void MVNS(ARMReg dest, Operand2 op2);
|
2012-11-02 10:58:56 +00:00
|
|
|
void MOVW(ARMReg dest, Operand2 op2);
|
|
|
|
void MOVT(ARMReg dest, Operand2 op2, bool TopBits = false);
|
|
|
|
|
2012-11-23 18:41:35 +00:00
|
|
|
// UDIV and SDIV are only available on CPUs that have
|
|
|
|
// the idiva hardare capacity
|
|
|
|
void UDIV(ARMReg dest, ARMReg dividend, ARMReg divisor);
|
|
|
|
void SDIV(ARMReg dest, ARMReg dividend, ARMReg divisor);
|
2012-11-02 10:58:56 +00:00
|
|
|
|
|
|
|
void MUL (ARMReg dest, ARMReg src, ARMReg op2);
|
|
|
|
void MULS(ARMReg dest, ARMReg src, ARMReg op2);
|
|
|
|
void SXTB(ARMReg dest, ARMReg op2);
|
|
|
|
void SXTH(ARMReg dest, ARMReg op2, u8 rotation = 0);
|
|
|
|
void SXTAH(ARMReg dest, ARMReg src, ARMReg op2, u8 rotation = 0);
|
2012-11-01 15:19:01 +00:00
|
|
|
// Using just MSR here messes with our defines on the PPC side of stuff
|
|
|
|
// Just need to put an underscore here, bit annoying.
|
|
|
|
void _MSR (bool nzcvq, bool g, Operand2 op2);
|
|
|
|
void _MSR (bool nzcvq, bool g, ARMReg src );
|
|
|
|
void MRS (ARMReg dest);
|
|
|
|
|
|
|
|
// Memory load/store operations
|
2012-11-02 10:58:56 +00:00
|
|
|
void LDR (ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
|
|
|
// Offset adds to the base register in LDR
|
|
|
|
void LDR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
|
|
|
void LDRB(ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
|
|
|
void STR (ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
|
|
|
// Offset adds on to the destination register in STR
|
|
|
|
void STR (ARMReg dest, ARMReg base, ARMReg offset, bool Index, bool Add);
|
|
|
|
|
|
|
|
void STRB(ARMReg dest, ARMReg src, Operand2 op2 = 0);
|
2012-11-01 15:19:01 +00:00
|
|
|
void STMFD(ARMReg dest, bool WriteBack, const int Regnum, ...);
|
|
|
|
void LDMFD(ARMReg dest, bool WriteBack, const int Regnum, ...);
|
|
|
|
|
2012-11-02 10:58:56 +00:00
|
|
|
// Exclusive Access operations
|
|
|
|
void LDREX(ARMReg dest, ARMReg base);
|
|
|
|
// dest contains the result if the instruction managed to store the value
|
|
|
|
void STREX(ARMReg dest, ARMReg base, ARMReg op);
|
|
|
|
void DMB ();
|
2012-11-23 18:41:35 +00:00
|
|
|
|
|
|
|
// NEON and ASIMD instructions
|
|
|
|
// None of these will be created with conditional since ARM
|
|
|
|
// is deprecating conditional execution of ASIMD instructions.
|
|
|
|
// Some ASIMD instructions don't even have a conditional encoding.
|
|
|
|
|
|
|
|
void VLDR(ARMReg dest, ARMReg Base, Operand2 op);
|
|
|
|
void VMOV(ARMReg Dest, ARMReg Src);
|
|
|
|
|
2012-11-01 15:19:01 +00:00
|
|
|
// Utility functions
|
2012-11-02 10:58:56 +00:00
|
|
|
void ARMABI_CallFunction(void *func);
|
|
|
|
void ARMABI_CallFunctionC(void *func, u32 Arg0);
|
2013-01-08 16:03:17 +00:00
|
|
|
void ARMABI_CallFunctionCNoSave(void *func, u32 Arg0);
|
2012-11-02 10:58:56 +00:00
|
|
|
void ARMABI_CallFunctionCC(void *func, u32 Arg1, u32 Arg2);
|
2012-11-23 18:41:35 +00:00
|
|
|
void ARMABI_CallFunctionCCC(void *func, u32 Arg1, u32 Arg2, u32 Arg3);
|
2012-11-01 15:19:01 +00:00
|
|
|
void ARMABI_PushAllCalleeSavedRegsAndAdjustStack();
|
|
|
|
void ARMABI_PopAllCalleeSavedRegsAndAdjustStack();
|
2012-11-02 10:58:56 +00:00
|
|
|
void ARMABI_MOVI2R(ARMReg reg, Operand2 val);
|
|
|
|
void ARMABI_MOVI2M(Operand2 op, Operand2 val);
|
2012-11-23 18:41:35 +00:00
|
|
|
void ARMABI_MOVI2M(u32 addr, Operand2 val);
|
2012-11-02 10:58:56 +00:00
|
|
|
void ARMABI_ShowConditions();
|
2012-11-23 18:41:35 +00:00
|
|
|
void ARMABI_Return();
|
2012-11-01 15:19:01 +00:00
|
|
|
|
|
|
|
void UpdateAPSR(bool NZCVQ, u8 Flags, bool GE, u8 GEval);
|
|
|
|
|
|
|
|
}; // class ARMXEmitter
|
|
|
|
|
|
|
|
|
|
|
|
// Everything that needs to generate X86 code should inherit from this.
|
|
|
|
// You get memory management for free, plus, you can use all the MOV etc functions without
|
|
|
|
// having to prefix them with gen-> or something similar.
|
|
|
|
class ARMXCodeBlock : public ARMXEmitter
|
|
|
|
{
|
|
|
|
protected:
|
|
|
|
u8 *region;
|
|
|
|
size_t region_size;
|
|
|
|
|
|
|
|
public:
|
|
|
|
ARMXCodeBlock() : region(NULL), region_size(0) {}
|
|
|
|
virtual ~ARMXCodeBlock() { if (region) FreeCodeSpace(); }
|
|
|
|
|
|
|
|
// Call this before you generate any code.
|
|
|
|
void AllocCodeSpace(int size)
|
|
|
|
{
|
|
|
|
region_size = size;
|
|
|
|
region = (u8*)AllocateExecutableMemory(region_size);
|
|
|
|
SetCodePtr(region);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Always clear code space with breakpoints, so that if someone accidentally executes
|
|
|
|
// uninitialized, it just breaks into the debugger.
|
|
|
|
void ClearCodeSpace()
|
|
|
|
{
|
|
|
|
// x86/64: 0xCC = breakpoint
|
|
|
|
memset(region, 0xCC, region_size);
|
|
|
|
ResetCodePtr();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Call this when shutting down. Don't rely on the destructor, even though it'll do the job.
|
|
|
|
void FreeCodeSpace()
|
|
|
|
{
|
|
|
|
FreeMemoryPages(region, region_size);
|
|
|
|
region = NULL;
|
|
|
|
region_size = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool IsInCodeSpace(u8 *ptr)
|
|
|
|
{
|
|
|
|
return ptr >= region && ptr < region + region_size;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Cannot currently be undone. Will write protect the entire code region.
|
|
|
|
// Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()).
|
|
|
|
void WriteProtect()
|
|
|
|
{
|
|
|
|
WriteProtectMemory(region, region_size, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
void ResetCodePtr()
|
|
|
|
{
|
|
|
|
SetCodePtr(region);
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t GetSpaceLeft() const
|
|
|
|
{
|
|
|
|
return region_size - (GetCodePtr() - region);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
#endif // _DOLPHIN_INTEL_CODEGEN_
|