ppsspp/Common/ppcEmitter.cpp

553 lines
14 KiB
C++
Raw Normal View History

2013-08-10 14:37:09 +00:00
#include <xtl.h>
#include "ppcEmitter.h"
// Helper
#define X_FORM(OPCD, D, A, B, XO, Rc) { \
int a = (A), b = (B), d = (D); \
Write32((OPCD << 26) | (d << 21) | (a << 16) | (b << 11) | (((XO) & 0x3ff) << 1) | (Rc)); \
}
2013-08-10 14:37:09 +00:00
namespace PpcGen {
// Arithmetics ops
void PPCXEmitter::ADD (PPCReg Rd, PPCReg Ra, PPCReg Rb) {
u32 instr = (0x7C000214 | (Rd << 21) | (Ra << 16) | (Rb << 11));
Write32(instr);
}
void PPCXEmitter::ADDI (PPCReg Rd, PPCReg Ra, short imm) {
2013-08-10 14:37:09 +00:00
u32 instr = (0x38000000 | (Rd << 21) | (Ra << 16) | ((imm) & 0xffff));
Write32(instr);
}
void PPCXEmitter::ADDIS (PPCReg Rd, PPCReg Ra, short imm) {
2013-08-10 14:37:09 +00:00
u32 instr = (0x3C000000 | (Rd << 21) | (Ra << 16) | ((imm) & 0xffff));
Write32(instr);
}
void PPCXEmitter::AND (PPCReg Rs, PPCReg Ra, PPCReg Rb) {
u32 instr = (0x7C000038 | (Ra << 21) | (Rs << 16) | (Rb << 11));
Write32(instr);
}
void PPCXEmitter::ANDI (PPCReg Rdest, PPCReg Ra, unsigned short imm) {
u32 instr = (0x70000000 | (Ra << 21) | (Rdest << 16) | ((imm) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
2013-08-16 13:46:50 +00:00
void PPCXEmitter::ANDIS (PPCReg Rdest, PPCReg Ra, unsigned short imm) {
u32 instr = (0x74000000 | (Ra << 21) | (Rdest << 16) | ((imm) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
// Memory load/store operations
void PPCXEmitter::LI(PPCReg dest, unsigned short imm) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x38000000 | (dest << 21) | ((imm) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::LIS(PPCReg dest, unsigned short imm) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x3C000000 | (dest << 21) | ((imm) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::LBZ (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x88000000 | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
2013-08-16 13:46:50 +00:00
void PPCXEmitter::LBZX (PPCReg dest, PPCReg a, PPCReg b) {
2013-08-16 13:46:50 +00:00
u32 instr = ((31<<26) | (dest << 21) | (a << 16) | (b << 11) | (87<<1));
Write32(instr);
}
2013-08-10 14:37:09 +00:00
void PPCXEmitter::LHZ (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0xA0000000 | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::LHBRX (PPCReg dest, PPCReg src, PPCReg offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x7C00062C | (dest << 21) | (src << 16) | (offset << 11));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::LWZ (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x80000000 | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::LWBRX (PPCReg dest, PPCReg src, PPCReg offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x7C00042C | (dest << 21) | (src << 16) | (offset << 11));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::STB (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x98000000 | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::STBX (PPCReg dest, PPCReg a, PPCReg b) {
2013-08-16 13:46:50 +00:00
u32 instr = ((31<<26) | (dest << 21) | (a << 16) | (b << 11) | (215 << 1));
Write32(instr);
}
2013-08-10 14:37:09 +00:00
void PPCXEmitter::STH (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0xB0000000 | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::STHBRX (PPCReg dest, PPCReg src, PPCReg offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x7C00072C | (dest << 21) | (src << 16) | (offset << 11));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::STW (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x90000000 | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::STWU (PPCReg dest, PPCReg src, int offset) {
u32 instr = (0x94000000 | (dest << 21) | (src << 16) | ((offset) & 0xffff));
Write32(instr);
}
void PPCXEmitter::STWBRX (PPCReg dest, PPCReg src, PPCReg offset) {
2013-08-16 13:46:50 +00:00
u32 instr = (0x7C00052C | (dest << 21) | (src << 16) | (offset << 11));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
2013-08-16 13:46:50 +00:00
2013-08-10 14:37:09 +00:00
void PPCXEmitter::LD (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = ((58 << 26) | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::STD (PPCReg dest, PPCReg src, int offset) {
2013-08-16 13:46:50 +00:00
u32 instr = ((62 << 26) | (dest << 21) | (src << 16) | ((offset) & 0xffff));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
// Branch operations
void PPCXEmitter::B (const void *fnptr) {
s32 func = (s32)fnptr - s32(code);
u32 instr = (0x48000000 | ((s32)((func) & 0x3fffffc)));
Write32(instr);
}
void PPCXEmitter::BL(const void *fnptr) {
s32 func = (s32)fnptr - s32(code);
u32 instr = (0x48000001 | ((s32)((func) & 0x3fffffc)));
Write32(instr);
}
void PPCXEmitter::BA (const void *fnptr) {
2013-08-11 09:27:12 +00:00
s32 func = (s32)fnptr;
2013-08-10 14:37:09 +00:00
u32 instr = (0x48000002 | ((s32)((func) & 0x3fffffc)));
Write32(instr);
}
2013-08-16 13:46:50 +00:00
2013-08-10 14:37:09 +00:00
void PPCXEmitter::BLA (const void *fnptr) {
2013-08-11 09:27:12 +00:00
s32 func = (s32)fnptr;
2013-08-10 14:37:09 +00:00
u32 instr = (0x48000003 | ((s32)((func) & 0x3fffffc)));
Write32(instr);
}
2013-08-11 10:57:59 +00:00
2013-08-12 17:41:12 +00:00
#define IS_SMALL_JUMP (((u32)code - (u32)fnptr)>=-32767 && ((u32)code - (u32)fnptr)<=-32767)
#define CHECK_SMALL_JUMP { if(IS_SMALL_JUMP) { DebugBreak(); } }
2013-08-16 13:46:50 +00:00
2013-08-10 14:37:09 +00:00
void PPCXEmitter::BEQ (const void *fnptr) {
2013-08-12 17:41:12 +00:00
CHECK_SMALL_JUMP
2013-08-11 10:57:59 +00:00
2013-08-16 13:46:50 +00:00
s32 func = (s32)fnptr - s32(code);
2013-08-11 09:27:12 +00:00
u32 instr = (0x41820000 | ( func & 0xfffc));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
2013-08-16 13:46:50 +00:00
2013-08-10 14:37:09 +00:00
void PPCXEmitter::BGT(const void *fnptr) {
2013-08-12 17:41:12 +00:00
CHECK_SMALL_JUMP
2013-08-11 10:57:59 +00:00
2013-08-16 13:46:50 +00:00
s32 func = (s32)fnptr - s32(code);
2013-08-11 09:27:12 +00:00
u32 instr = (0x41810000 | (((s16)(((func)+1))) & 0xfffc));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
2013-08-16 13:46:50 +00:00
2013-08-11 10:57:59 +00:00
void PPCXEmitter::BLTCTR() {
Write32((19 << 26) | (12 << 21) | (528 <<1));
2013-08-16 13:46:50 +00:00
// Break();
2013-08-11 10:57:59 +00:00
}
2013-08-10 14:37:09 +00:00
void PPCXEmitter::BLT (const void *fnptr) {
2013-08-11 10:57:59 +00:00
//CHECK_JUMP
2013-08-12 17:41:12 +00:00
if (!IS_SMALL_JUMP) {
2013-08-11 10:57:59 +00:00
u32 func_addr = (u32) fnptr;
// Load func address
MOVI2R(R0, func_addr);
// Set it to link register
MTCTR(R0);
// Branch
BLTCTR();
return;
}
2013-08-10 14:37:09 +00:00
s32 func = (s32)fnptr - s32(code);
2013-08-11 09:27:12 +00:00
u32 instr = (0x41800000 | (((s16)(((func)+1))) & 0xfffc));
Write32(instr);
}
2013-08-11 10:57:59 +00:00
void PPCXEmitter::BLE (const void *fnptr) {
2013-08-12 17:41:12 +00:00
CHECK_SMALL_JUMP
2013-08-11 10:57:59 +00:00
2013-08-16 17:24:16 +00:00
s32 func = (s32)fnptr - s32(code);
2013-08-11 09:27:12 +00:00
u32 instr = (0x40810000 | (((s16)(((func)+1))) & 0xfffc));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
void PPCXEmitter::BCTRL() {
Write32(0x4E800421);
}
void PPCXEmitter::BCTR() {
Write32(0x4E800420);
}
// Link Register
void PPCXEmitter::MFLR(PPCReg r) {
Write32(0x7C0802A6 | r << 21);
}
void PPCXEmitter::MTLR(PPCReg r) {
Write32(0x7C0803A6 | r << 21);
}
void PPCXEmitter::MTCTR(PPCReg r) {
Write32(0x7C0903A6 | r << 21);
}
void PPCXEmitter::BLR() {
Write32(0x4E800020);
}
void PPCXEmitter::BGTLR() {
Write32(0x4D810020);
}
// Fixup
FixupBranch PPCXEmitter::B()
{
FixupBranch branch;
branch.type = _B;
branch.ptr = code;
branch.condition = condition;
//We'll write NOP here for now.
Write32(0x60000000);
return branch;
}
FixupBranch PPCXEmitter::BL()
{
FixupBranch branch;
branch.type = _BL;
branch.ptr = code;
branch.condition = condition;
//We'll write NOP here for now.
Write32(0x60000000);
return branch;
}
2013-08-16 13:46:50 +00:00
2013-08-10 14:37:09 +00:00
FixupBranch PPCXEmitter::BNE() {
FixupBranch branch;
branch.type = _BNE;
branch.ptr = code;
branch.condition = condition;
//We'll write NOP here for now.
Write32(0x60000000);
return branch;
}
FixupBranch PPCXEmitter::BLT() {
FixupBranch branch;
branch.type = _BLT;
branch.ptr = code;
branch.condition = condition;
//We'll write NOP here for now.
Write32(0x60000000);
return branch;
}
2013-08-11 09:27:12 +00:00
FixupBranch PPCXEmitter::BLE() {
FixupBranch branch;
branch.type = _BLE;
branch.ptr = code;
branch.condition = condition;
//We'll write NOP here for now.
Write32(0x60000000);
return branch;
}
2013-08-10 14:37:09 +00:00
FixupBranch PPCXEmitter::B_Cond(FixupBranchType type) {
FixupBranch branch;
branch.type = type;
branch.ptr = code;
branch.condition = condition;
//We'll write NOP here for now.
Write32(0x60000000);
return branch;
}
void PPCXEmitter::SetJumpTarget(FixupBranch const &branch)
{
s32 distance = s32(code) - (s32)branch.ptr;
2013-08-12 17:41:12 +00:00
_assert_msg_(DYNA_REC, distance > -32767
&& distance <= 32767,
2013-08-10 14:37:09 +00:00
"SetJumpTarget out of range (%p calls %p)", code,
branch.ptr);
switch(branch.type) {
case _B:
*(u32*)branch.ptr = (0x48000000 | ((s32)((distance) & 0x3fffffc)));
break;
case _BL:
*(u32*)branch.ptr = (0x48000001 | ((s32)((distance) & 0x3fffffc)));
break;
case _BEQ:
*(u32*)branch.ptr = (0x41820000 | ((s16)(((distance)+1)) & 0xfffc));
break;
case _BNE:
*(u32*)branch.ptr = (0x40820000 | ((s16)(((distance)+1)) & 0xfffc));
break;
2013-08-11 10:57:59 +00:00
case _BLT:
2013-08-10 14:37:09 +00:00
*(u32*)branch.ptr = (0x41800000 | ((s16)(((distance)+1)) & 0xfffc));
break;
case _BLE:
*(u32*)branch.ptr = (0x40810000 | ((s16)(((distance)+1)) & 0xfffc));
break;
case _BGT:
*(u32*)branch.ptr = (0x41810000 | ((s16)(((distance)+1)) & 0xfffc));
break;
case _BGE:
*(u32*)branch.ptr = (0x40800000 | ((s16)(((distance)+1)) & 0xfffc));
break;
default:
// Error !!!
_assert_msg_(DYNA_REC, 0, "SetJumpTarget unknow branch type: %d", branch.type);
break;
}
}
2013-08-16 13:53:20 +00:00
// Compare (Only use CR0 atm...)
2013-08-11 09:27:12 +00:00
void PPCXEmitter::CMPI(PPCReg dest, unsigned short imm) {
Write32((11<<26) | (dest << 16) | ((imm) & 0xffff));
}
2013-08-10 14:37:09 +00:00
void PPCXEmitter::CMPLI(PPCReg dest, unsigned short imm) {
2013-08-11 09:27:12 +00:00
Write32((10<<26) | (dest << 16) | ((imm) & 0xffff));
2013-08-10 14:37:09 +00:00
}
2013-08-16 13:46:50 +00:00
2013-08-10 14:37:09 +00:00
void PPCXEmitter::CMP(PPCReg a, PPCReg b) {
Write32((31 << 26) | (a << 16) | (b << 11));
}
2013-08-12 17:41:12 +00:00
void PPCXEmitter::CMPL(PPCReg a, PPCReg b) {
Write32((31 << 26) | (a << 16) | (b << 11) | (1<<6));
}
2013-08-19 07:47:53 +00:00
void PPCXEmitter::MFCR (PPCReg dest) {
Write32(0x7C000026 | (dest << 21));
}
void PPCXEmitter::MTCR (PPCReg dest) {
Write32(0x7C000120 | (dest << 21) | (0xff<<12));
}
2013-08-10 14:37:09 +00:00
// Others operation
void PPCXEmitter::ORI(PPCReg Rd, PPCReg Ra, unsigned short imm) {
u32 instr = (0x60000000 | (Ra << 21) | (Rd << 16) | (imm & 0xffff));
2013-08-12 17:41:12 +00:00
Write32(instr);
2013-08-10 14:37:09 +00:00
}
2013-08-19 11:56:15 +00:00
void PPCXEmitter::XORI (PPCReg Rdest, PPCReg Ra, unsigned short imm) {
u32 instr = (0x68000000 | (Ra << 21) | (Rdest << 16) | (imm & 0xffff));
2013-08-19 11:56:15 +00:00
Write32(instr);
}
2013-08-10 14:37:09 +00:00
void PPCXEmitter::OR(PPCReg Rdest, PPCReg Ra, PPCReg Rb) {
u32 instr = (0x7C000378 | (Ra << 21) | (Rdest << 16) | (Rb << 11));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
2013-08-16 13:46:50 +00:00
2013-08-15 12:18:23 +00:00
void PPCXEmitter::XOR(PPCReg Rd, PPCReg Ra, PPCReg Rb) {
u32 instr = (0x7C000278 | (Ra << 21) | (Rd << 16) | (Rb << 11));
Write32(instr);
}
void PPCXEmitter::NOR(PPCReg Rd, PPCReg Ra, PPCReg Rb) {
u32 instr = (0x7C0000f8 | (Ra << 21) | (Rd << 16) | (Rb << 11));
Write32(instr);
}
2013-08-16 13:46:50 +00:00
2013-08-10 18:31:40 +00:00
void PPCXEmitter::SUBF(PPCReg Rd, PPCReg Ra, PPCReg Rb, int RCFlags) {
u32 instr = (0x7C000050 | (Rd << 21) | (Ra << 16) | (Rb << 11) | (RCFlags & 1));
2013-08-10 14:37:09 +00:00
Write32(instr);
}
// Quick Call
// dest = LIS(imm) + ORI(+imm)
void PPCXEmitter::MOVI2R(PPCReg dest, unsigned int imm) {
2013-08-26 12:47:23 +00:00
/*if (imm == (unsigned short)imm) {
2013-08-16 13:53:20 +00:00
// 16bit
LI(dest, imm & 0xFFFF);
2013-08-26 12:47:23 +00:00
} else */{
2013-08-10 14:37:09 +00:00
// HI 16bit
LIS(dest, imm>>16);
if ((imm & 0xFFFF) != 0) {
// LO 16bit
ORI(dest, dest, imm & 0xFFFF);
}
}
}
void PPCXEmitter::QuickCallFunction(void *func) {
2013-08-16 13:53:20 +00:00
/** TODO : can use simple jump **/
2013-08-10 14:37:09 +00:00
u32 func_addr = (u32) func;
// Load func address
MOVI2R(R0, func_addr);
// Set it to link register
MTCTR(R0);
// Branch
BCTRL();
}
2013-08-16 13:46:50 +00:00
2013-08-13 15:28:08 +00:00
// sign
void PPCXEmitter::EXTSB (PPCReg dest, PPCReg src) {
Write32((0x7C000774 | (src << 21) | (dest << 16)));
}
void PPCXEmitter::EXTSH (PPCReg dest, PPCReg src) {
Write32(0x7C000734 | (src << 21) | (dest << 16));
}
void PPCXEmitter::RLWINM (PPCReg dest, PPCReg src, int shift, int start, int end) {
Write32((21<<26) | (src << 21) | (dest << 16) | (shift << 11) | (start << 6) | (end << 1));
}
// Shift Instructions
void PPCXEmitter::SRAW (PPCReg dest, PPCReg src, PPCReg shift) {
X_FORM(31, src, dest, shift, 792, 0);
}
void PPCXEmitter::SRAWI (PPCReg dest, PPCReg src, unsigned short imm) {
X_FORM(31, src, dest, imm, 824, 0);
}
void PPCXEmitter::SLW (PPCReg dest, PPCReg src, PPCReg shift) {
X_FORM(31, src, dest, shift, 24, 0);
}
void PPCXEmitter::SLWI (PPCReg dest, PPCReg src, unsigned short imm) {
RLWINM(dest, src, imm, 0, (31-imm));
}
void PPCXEmitter::SRW (PPCReg dest, PPCReg src, PPCReg shift) {
X_FORM(31, src, dest, shift, 536, 0);
}
void PPCXEmitter::SRWI (PPCReg dest, PPCReg src, unsigned short imm) {
RLWINM(dest, src, (32-imm), imm, 31);
}
// Prologue / epilogue
void PPCXEmitter::Prologue() {
// Save regs
u32 regSize = 8; // 4 in 32bit system
u32 stackFrameSize = 32*32;//(35 - 12) * regSize;
// Write Prologue (setup stack frame etc ...)
// Save Lr
MFLR(R12);
for(int i = 14; i < 32; i ++) {
STD((PPCReg)i, R1, -((33 - i) * regSize));
}
// Save r12
STW(R12, R1, -0x8);
// allocate stack
STWU(R1, R1, -stackFrameSize);
}
void PPCXEmitter::Epilogue() {
u32 regSize = 8; // 4 in 32bit system
u32 stackFrameSize = 32*32;//(35 - 12) * regSize;
// Write Epilogue (restore stack frame, return)
// free stack
ADDI(R1, R1, stackFrameSize);
// Restore regs
for(int i = 14; i < 32; i ++) {
LD((PPCReg)i, R1, -((33 - i) * regSize));
}
// recover r12 (LR saved register)
LWZ (R12, R1, -0x8);
// Restore Lr
MTLR(R12);
}
2013-08-10 14:37:09 +00:00
// Others ...
void PPCXEmitter::SetCodePtr(u8 *ptr)
{
code = ptr;
startcode = code;
lastCacheFlushEnd = ptr;
}
const u8 *PPCXEmitter::GetCodePtr() const
{
return code;
}
u8 *PPCXEmitter::GetWritableCodePtr()
{
return code;
}
void PPCXEmitter::ReserveCodeSpace(u32 bytes)
{
for (u32 i = 0; i < bytes/4; i++)
2013-08-16 13:46:50 +00:00
Write32(0x60000000); //nop
2013-08-10 14:37:09 +00:00
}
const u8 *PPCXEmitter::AlignCode16()
{
ReserveCodeSpace((-(s32)code) & 15);
return code;
}
const u8 *PPCXEmitter::AlignCodePage()
{
ReserveCodeSpace((-(s32)code) & 4095);
return code;
}
void PPCXEmitter::FlushIcache()
{
FlushIcacheSection(lastCacheFlushEnd, code);
lastCacheFlushEnd = code;
}
void PPCXEmitter::FlushIcacheSection(u8 *start, u8 *end)
{
u8 * addr = start;
while(addr < end) {
__asm dcbst r0, addr
__asm icbi r0, addr
addr += 4;
}
__emit(0x7c0004ac);//sync
__emit(0x4C00012C);//isync
}
} // namespace