1589 lines
44 KiB
C++
1589 lines
44 KiB
C++
/*
|
|
* ppc-translate.cpp - PowerPC dynamic translation
|
|
*
|
|
* Kheperix (C) 2003-2005 Gwenole Beauchesne
|
|
*
|
|
* 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; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* 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 for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "sysdeps.h"
|
|
#include "cpu/ppc/ppc-cpu.hpp"
|
|
#include "cpu/ppc/ppc-instructions.hpp"
|
|
#include "cpu/ppc/ppc-operands.hpp"
|
|
|
|
#if PPC_ENABLE_JIT
|
|
#include "cpu/jit/dyngen-exec.h"
|
|
#endif
|
|
|
|
#ifdef SHEEPSHAVER
|
|
#include "cpu_emulation.h"
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
|
|
#define DEBUG 1
|
|
#include "debug.h"
|
|
|
|
#if ENABLE_MON
|
|
#include "mon.h"
|
|
#include "mon_disass.h"
|
|
#endif
|
|
|
|
// Define to enable const branches optimization
|
|
#define FOLLOW_CONST_JUMPS 1
|
|
|
|
// FIXME: define ROM areas
|
|
static inline bool is_read_only_memory(uintptr addr)
|
|
{
|
|
#ifdef SHEEPSHAVER
|
|
if ((addr - ROMBase) < ROM_AREA_SIZE)
|
|
return true;
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
// Returns TRUE if we can directly generate a jump to the target block
|
|
// XXX mixing front-end and back-end conditions is not a very good idea...
|
|
static inline bool direct_chaining_possible(uint32 bpc, uint32 tpc)
|
|
{
|
|
#ifndef DYNGEN_FAST_DISPATCH
|
|
return false;
|
|
#endif
|
|
return ((bpc ^ tpc) >> 12) == 0 || is_read_only_memory(tpc);
|
|
}
|
|
|
|
|
|
/**
|
|
* Basic block disassemblers
|
|
**/
|
|
|
|
#define TARGET_M68K 0
|
|
#define TARGET_POWERPC 1
|
|
#define TARGET_X86 2
|
|
#define TARGET_AMD64 3
|
|
#if defined(i386) || defined(__i386__)
|
|
#define TARGET_NATIVE TARGET_X86
|
|
#endif
|
|
#if defined(x86_64) || defined(__x86_64__)
|
|
#define TARGET_NATIVE TARGET_AMD64
|
|
#endif
|
|
#if defined(powerpc) || defined(__powerpc__) || defined(__ppc__)
|
|
#define TARGET_NATIVE TARGET_POWERPC
|
|
#endif
|
|
|
|
#if PPC_ENABLE_JIT
|
|
static void disasm_block(int target, uint8 *start, uint32 length)
|
|
{
|
|
#if ENABLE_MON
|
|
char disasm_str[200];
|
|
sprintf(disasm_str, "%s $%x $%x",
|
|
target == TARGET_M68K ? "d68" :
|
|
target == TARGET_X86 ? "d86" :
|
|
target == TARGET_AMD64 ? "d8664" :
|
|
target == TARGET_POWERPC ? "d" : "x",
|
|
start, start + length - 1);
|
|
|
|
const char *arg[] = {"mon",
|
|
#ifdef SHEEPSHAVER
|
|
"-m",
|
|
#endif
|
|
"-r", disasm_str, NULL};
|
|
mon(sizeof(arg)/sizeof(arg[0]) - 1, arg);
|
|
#endif
|
|
}
|
|
|
|
static void disasm_translation(uint32 src_addr, uint32 src_len,
|
|
uint8* dst_addr, uint32 dst_len)
|
|
{
|
|
printf("### Block at %08x translated to %p (%d bytes)\n", src_addr, dst_addr, dst_len);
|
|
printf("IN:\n");
|
|
disasm_block(TARGET_POWERPC, vm_do_get_real_address(src_addr), src_len);
|
|
printf("OUT:\n");
|
|
#ifdef TARGET_NATIVE
|
|
disasm_block(TARGET_NATIVE, dst_addr, dst_len);
|
|
#else
|
|
printf("unsupported disassembler for this archicture\n");
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
|
|
/**
|
|
* DynGen dynamic code translation
|
|
**/
|
|
|
|
#if PPC_ENABLE_JIT
|
|
powerpc_cpu::block_info *
|
|
powerpc_cpu::compile_block(uint32 entry_point)
|
|
{
|
|
#if DEBUG
|
|
bool disasm = false;
|
|
#else
|
|
const bool disasm = false;
|
|
#endif
|
|
|
|
#if PPC_PROFILE_COMPILE_TIME
|
|
compile_count++;
|
|
clock_t start_time = clock();
|
|
#endif
|
|
|
|
powerpc_jit & dg = codegen;
|
|
codegen_context_t cg_context(dg);
|
|
cg_context.entry_point = entry_point;
|
|
again:
|
|
block_info *bi = my_block_cache.new_blockinfo();
|
|
bi->init(entry_point);
|
|
bi->entry_point = dg.gen_start(entry_point);
|
|
|
|
// Direct block chaining support variables
|
|
bool use_direct_block_chaining = false;
|
|
|
|
int compile_status;
|
|
uint32 dpc = entry_point - 4;
|
|
uint32 min_pc, max_pc;
|
|
min_pc = max_pc = entry_point;
|
|
uint32 sync_pc = dpc;
|
|
uint32 sync_pc_offset = 0;
|
|
bool done_compile = false;
|
|
while (!done_compile) {
|
|
uint32 opcode = vm_read_memory_4(dpc += 4);
|
|
const instr_info_t *ii = decode(opcode);
|
|
if (ii->cflow & CFLOW_END_BLOCK)
|
|
done_compile = true;
|
|
|
|
// Assume we can compile this opcode
|
|
compile_status = COMPILE_CODE_OK;
|
|
|
|
#if PPC_FLIGHT_RECORDER
|
|
if (is_logging()) {
|
|
typedef void (*func_t)(dyngen_cpu_base, uint32, uint32);
|
|
func_t func = (func_t)nv_mem_fun((execute_pmf)&powerpc_cpu::do_record_step).ptr();
|
|
dg.gen_invoke_CPU_im_im(func, dpc, opcode);
|
|
}
|
|
#endif
|
|
|
|
union operands_t {
|
|
struct {
|
|
int size, sign;
|
|
int do_update;
|
|
int do_indexed;
|
|
} mem;
|
|
struct {
|
|
uint32 target;
|
|
} jmp;
|
|
};
|
|
operands_t op;
|
|
|
|
switch (ii->mnemo) {
|
|
case PPC_I(LBZ): // Load Byte and Zero
|
|
op.mem.size = 1;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LBZU): // Load Byte and Zero with Update
|
|
op.mem.size = 1;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LBZUX): // Load Byte and Zero with Update Indexed
|
|
op.mem.size = 1;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
case PPC_I(LBZX): // Load Byte and Zero Indexed
|
|
op.mem.size = 1;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
case PPC_I(LHA): // Load Half Word Algebraic
|
|
op.mem.size = 2;
|
|
op.mem.sign = 1;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LHAU): // Load Half Word Algebraic with Update
|
|
op.mem.size = 2;
|
|
op.mem.sign = 1;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LHAUX): // Load Half Word Algebraic with Update Indexed
|
|
op.mem.size = 2;
|
|
op.mem.sign = 1;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
case PPC_I(LHAX): // Load Half Word Algebraic Indexed
|
|
op.mem.size = 2;
|
|
op.mem.sign = 1;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
case PPC_I(LHZ): // Load Half Word and Zero
|
|
op.mem.size = 2;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LHZU): // Load Half Word and Zero with Update
|
|
op.mem.size = 2;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LHZUX): // Load Half Word and Zero with Update Indexed
|
|
op.mem.size = 2;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
case PPC_I(LHZX): // Load Half Word and Zero Indexed
|
|
op.mem.size = 2;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
case PPC_I(LWZ): // Load Word and Zero
|
|
op.mem.size = 4;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LWZU): // Load Word and Zero with Update
|
|
op.mem.size = 4;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_load;
|
|
case PPC_I(LWZUX): // Load Word and Zero with Update Indexed
|
|
op.mem.size = 4;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
case PPC_I(LWZX): // Load Word and Zero Indexed
|
|
op.mem.size = 4;
|
|
op.mem.sign = 0;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_load;
|
|
{
|
|
do_load:
|
|
// Extract RZ operand
|
|
const int rA = rA_field::extract(opcode);
|
|
if (rA == 0 && !op.mem.do_update)
|
|
dg.gen_mov_32_T1_im(0);
|
|
else
|
|
dg.gen_load_T1_GPR(rA);
|
|
|
|
// Extract index operand
|
|
if (op.mem.do_indexed)
|
|
dg.gen_load_T2_GPR(rB_field::extract(opcode));
|
|
|
|
switch (op.mem.size) {
|
|
case 1:
|
|
if (op.mem.do_indexed)
|
|
dg.gen_load_u8_T0_T1_T2();
|
|
else
|
|
dg.gen_load_u8_T0_T1_im(operand_D::get(this, opcode));
|
|
break;
|
|
case 2:
|
|
if (op.mem.do_indexed) {
|
|
if (op.mem.sign)
|
|
dg.gen_load_s16_T0_T1_T2();
|
|
else
|
|
dg.gen_load_u16_T0_T1_T2();
|
|
}
|
|
else {
|
|
const int32 offset = operand_D::get(this, opcode);
|
|
if (op.mem.sign)
|
|
dg.gen_load_s16_T0_T1_im(offset);
|
|
else
|
|
dg.gen_load_u16_T0_T1_im(offset);
|
|
}
|
|
break;
|
|
case 4:
|
|
if (op.mem.do_indexed) {
|
|
dg.gen_load_u32_T0_T1_T2();
|
|
}
|
|
else {
|
|
const int32 offset = operand_D::get(this, opcode);
|
|
dg.gen_load_u32_T0_T1_im(offset);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Commit result
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
|
|
// Update RA
|
|
if (op.mem.do_update) {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_add_32_T1_T2();
|
|
else
|
|
dg.gen_add_32_T1_im(operand_D::get(this, opcode));
|
|
dg.gen_store_T1_GPR(rA);
|
|
}
|
|
break;
|
|
}
|
|
case PPC_I(STB): // Store Byte
|
|
op.mem.size = 1;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_store;
|
|
case PPC_I(STBU): // Store Byte with Update
|
|
op.mem.size = 1;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_store;
|
|
case PPC_I(STBUX): // Store Byte with Update Indexed
|
|
op.mem.size = 1;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_store;
|
|
case PPC_I(STBX): // Store Byte Indexed
|
|
op.mem.size = 1;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_store;
|
|
case PPC_I(STH): // Store Half Word
|
|
op.mem.size = 2;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_store;
|
|
case PPC_I(STHU): // Store Half Word with Update
|
|
op.mem.size = 2;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_store;
|
|
case PPC_I(STHUX): // Store Half Word with Update Indexed
|
|
op.mem.size = 2;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_store;
|
|
case PPC_I(STHX): // Store Half Word Indexed
|
|
op.mem.size = 2;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_store;
|
|
case PPC_I(STW): // Store Word
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_store;
|
|
case PPC_I(STWU): // Store Word with Update
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_store;
|
|
case PPC_I(STWUX): // Store Word with Update Indexed
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_store;
|
|
case PPC_I(STWX): // Store Word Indexed
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_store;
|
|
{
|
|
do_store:
|
|
// Extract RZ operand
|
|
const int rA = rA_field::extract(opcode);
|
|
if (rA == 0 && !op.mem.do_update)
|
|
dg.gen_mov_32_T1_im(0);
|
|
else
|
|
dg.gen_load_T1_GPR(rA);
|
|
|
|
// Extract index operand
|
|
if (op.mem.do_indexed)
|
|
dg.gen_load_T2_GPR(rB_field::extract(opcode));
|
|
|
|
// Load register to commit to memory
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
|
|
switch (op.mem.size) {
|
|
case 1:
|
|
if (op.mem.do_indexed)
|
|
dg.gen_store_8_T0_T1_T2();
|
|
else
|
|
dg.gen_store_8_T0_T1_im(operand_D::get(this, opcode));
|
|
break;
|
|
case 2:
|
|
if (op.mem.do_indexed)
|
|
dg.gen_store_16_T0_T1_T2();
|
|
else
|
|
dg.gen_store_16_T0_T1_im(operand_D::get(this, opcode));
|
|
break;
|
|
case 4:
|
|
if (op.mem.do_indexed)
|
|
dg.gen_store_32_T0_T1_T2();
|
|
else
|
|
dg.gen_store_32_T0_T1_im(operand_D::get(this, opcode));
|
|
break;
|
|
}
|
|
|
|
// Update RA
|
|
if (op.mem.do_update) {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_add_32_T1_T2();
|
|
else
|
|
dg.gen_add_32_T1_im(operand_D::get(this, opcode));
|
|
dg.gen_store_T1_GPR(rA);
|
|
}
|
|
break;
|
|
}
|
|
case PPC_I(LMW): // Load Multiple Word
|
|
case PPC_I(STMW): // Store Multiple Word
|
|
{
|
|
const int rA = rA_field::extract(opcode);
|
|
if (rA == 0)
|
|
dg.gen_mov_32_T0_im(operand_D::get(this, opcode));
|
|
else {
|
|
dg.gen_load_T0_GPR(rA);
|
|
dg.gen_add_32_T0_im(operand_D::get(this, opcode));
|
|
}
|
|
switch (ii->mnemo) {
|
|
case PPC_I(LMW): dg.gen_lmw_T0(rD_field::extract(opcode)); break;
|
|
case PPC_I(STMW): dg.gen_stmw_T0(rS_field::extract(opcode)); break;
|
|
}
|
|
break;
|
|
}
|
|
#if KPX_MAX_CPUS == 1
|
|
case PPC_I(STWCX): // Store Word Conditional Indexed
|
|
case PPC_I(LWARX): // Load Word and Reserve Indexed
|
|
{
|
|
const int rA = rA_field::extract(opcode);
|
|
const int rB = rB_field::extract(opcode);
|
|
if (rA == 0)
|
|
dg.gen_load_T1_GPR(rB);
|
|
else {
|
|
dg.gen_load_T1_GPR(rA);
|
|
dg.gen_load_T2_GPR(rB);
|
|
dg.gen_add_32_T1_T2();
|
|
}
|
|
switch (ii->mnemo) {
|
|
case PPC_I(LWARX):
|
|
dg.gen_lwarx_T0_T1();
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
case PPC_I(STWCX):
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_stwcx_T0_T1();
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
#endif
|
|
case PPC_I(BC): // Branch Conditional
|
|
{
|
|
const int bo = BO_field::extract(opcode);
|
|
#if FOLLOW_CONST_JUMPS
|
|
if (!BO_CONDITIONAL_BRANCH(bo) && !BO_DECREMENT_CTR(bo)) {
|
|
if (LK_field::test(opcode)) {
|
|
const uint32 npc = dpc + 4;
|
|
dg.gen_store_im_LR(npc);
|
|
}
|
|
if (AA_field::test(opcode))
|
|
dpc = 0;
|
|
op.jmp.target = ((dpc + operand_BD::get(this, opcode)) & -4);
|
|
goto do_const_jump;
|
|
}
|
|
#endif
|
|
const uint32 tpc = ((AA_field::test(opcode) ? 0 : dpc) + operand_BD::get(this, opcode)) & -4;
|
|
const uint32 npc = dpc + 4;
|
|
#if DYNGEN_DIRECT_BLOCK_CHAINING
|
|
// Use direct block chaining for in-page jumps or jumps to ROM area
|
|
if (direct_chaining_possible(bi->pc, tpc)) {
|
|
use_direct_block_chaining = true;
|
|
bi->li[0].jmp_pc = tpc;
|
|
// Make sure it's a conditional branch
|
|
if (BO_CONDITIONAL_BRANCH(bo) || BO_DECREMENT_CTR(bo))
|
|
bi->li[1].jmp_pc = npc;
|
|
}
|
|
#endif
|
|
|
|
if (LK_field::test(opcode))
|
|
dg.gen_store_im_LR(npc);
|
|
|
|
dg.gen_bc(bo, BI_field::extract(opcode), tpc, npc, use_direct_block_chaining);
|
|
break;
|
|
}
|
|
case PPC_I(BCCTR): // Branch Conditional to Count Register
|
|
dg.gen_load_T0_CTR_aligned();
|
|
goto do_branch;
|
|
case PPC_I(BCLR): // Branch Conditional to Link Register
|
|
dg.gen_load_T0_LR_aligned();
|
|
goto do_branch;
|
|
{
|
|
do_branch:
|
|
const int bo = BO_field::extract(opcode);
|
|
const int bi = BI_field::extract(opcode);
|
|
|
|
const uint32 npc = dpc + 4;
|
|
if (LK_field::test(opcode))
|
|
dg.gen_store_im_LR(npc);
|
|
|
|
dg.gen_bc(bo, bi, (uint32)-1, npc, use_direct_block_chaining);
|
|
break;
|
|
}
|
|
case PPC_I(B): // Branch
|
|
goto do_call;
|
|
{
|
|
#if FOLLOW_CONST_JUMPS
|
|
do_const_jump:
|
|
sync_pc = dpc = op.jmp.target - 4;
|
|
sync_pc_offset = 0;
|
|
if (dpc < min_pc)
|
|
min_pc = dpc;
|
|
else if (dpc > max_pc)
|
|
max_pc = dpc;
|
|
done_compile = false;
|
|
break;
|
|
#endif
|
|
do_call:
|
|
uint32 tpc = AA_field::test(opcode) ? 0 : dpc;
|
|
tpc = (tpc + operand_LI::get(this, opcode)) & -4;
|
|
|
|
const uint32 npc = dpc + 4;
|
|
if (LK_field::test(opcode))
|
|
dg.gen_store_im_LR(npc);
|
|
#if FOLLOW_CONST_JUMPS
|
|
else {
|
|
op.jmp.target = tpc;
|
|
goto do_const_jump;
|
|
}
|
|
#endif
|
|
|
|
#if DYNGEN_DIRECT_BLOCK_CHAINING
|
|
// Use direct block chaining, addresses will be resolved at execution
|
|
if (direct_chaining_possible(bi->pc, tpc)) {
|
|
use_direct_block_chaining = true;
|
|
bi->li[0].jmp_pc = tpc;
|
|
}
|
|
#endif
|
|
|
|
// BO field is built so that we always branch to pc
|
|
dg.gen_bc(BO_MAKE(0,0,0,0), 0, tpc, 0, use_direct_block_chaining);
|
|
break;
|
|
}
|
|
case PPC_I(CMP): // Compare
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
dg.gen_compare_T0_T1(crfD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(CMPI): // Compare Immediate
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_compare_T0_im(crfD_field::extract(opcode), operand_SIMM::get(this, opcode));
|
|
break;
|
|
}
|
|
case PPC_I(CMPL): // Compare Logical
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
dg.gen_compare_logical_T0_T1(crfD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(CMPLI): // Compare Logical Immediate
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_compare_logical_T0_im(crfD_field::extract(opcode), operand_UIMM::get(this, opcode));
|
|
break;
|
|
}
|
|
case PPC_I(CRAND): // Condition Register AND
|
|
case PPC_I(CRANDC): // Condition Register AND with Complement
|
|
case PPC_I(CREQV): // Condition Register Equivalent
|
|
case PPC_I(CRNAND): // Condition Register NAND
|
|
case PPC_I(CRNOR): // Condition Register NOR
|
|
case PPC_I(CROR): // Condition Register OR
|
|
case PPC_I(CRORC): // Condition Register OR with Complement
|
|
case PPC_I(CRXOR): // Condition Register XOR
|
|
{
|
|
dg.gen_load_T0_crb(crbA_field::extract(opcode));
|
|
dg.gen_load_T1_crb(crbB_field::extract(opcode));
|
|
switch (ii->mnemo) {
|
|
case PPC_I(CRAND): dg.gen_and_32_T0_T1(); break;
|
|
case PPC_I(CRANDC): dg.gen_andc_32_T0_T1(); break;
|
|
case PPC_I(CREQV): dg.gen_eqv_32_T0_T1(); break;
|
|
case PPC_I(CRNAND): dg.gen_nand_32_T0_T1(); break;
|
|
case PPC_I(CRNOR): dg.gen_nor_32_T0_T1(); break;
|
|
case PPC_I(CROR): dg.gen_or_32_T0_T1(); break;
|
|
case PPC_I(CRORC): dg.gen_orc_32_T0_T1(); break;
|
|
case PPC_I(CRXOR): dg.gen_xor_32_T0_T1(); break;
|
|
default: abort();
|
|
}
|
|
dg.gen_store_T0_crb(crbD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(AND): // AND
|
|
case PPC_I(ANDC): // AND with Complement
|
|
case PPC_I(EQV): // Equivalent
|
|
case PPC_I(NAND): // NAND
|
|
case PPC_I(NOR): // NOR
|
|
case PPC_I(ORC): // ORC
|
|
case PPC_I(XOR): // XOR
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
switch (ii->mnemo) {
|
|
case PPC_I(AND): dg.gen_and_32_T0_T1(); break;
|
|
case PPC_I(ANDC): dg.gen_andc_32_T0_T1(); break;
|
|
case PPC_I(EQV): dg.gen_eqv_32_T0_T1(); break;
|
|
case PPC_I(NAND): dg.gen_nand_32_T0_T1(); break;
|
|
case PPC_I(NOR): dg.gen_nor_32_T0_T1(); break;
|
|
case PPC_I(ORC): dg.gen_orc_32_T0_T1(); break;
|
|
case PPC_I(XOR): dg.gen_xor_32_T0_T1(); break;
|
|
default: abort();
|
|
}
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(OR): // OR
|
|
{
|
|
const int rS = rS_field::extract(opcode);
|
|
const int rB = rB_field::extract(opcode);
|
|
const int rA = rA_field::extract(opcode);
|
|
dg.gen_load_T0_GPR(rS);
|
|
if (rS != rB) { // Not MR case
|
|
dg.gen_load_T1_GPR(rB);
|
|
dg.gen_or_32_T0_T1();
|
|
}
|
|
dg.gen_store_T0_GPR(rA);
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(ORI): // OR Immediate
|
|
{
|
|
const int rA = rA_field::extract(opcode);
|
|
const int rS = rS_field::extract(opcode);
|
|
const uint32 val = operand_UIMM::get(this, opcode);
|
|
if (val == 0) {
|
|
if (rA != rS) { // Skip NOP, handle register move
|
|
dg.gen_load_T0_GPR(rS);
|
|
dg.gen_store_T0_GPR(rA);
|
|
}
|
|
}
|
|
else {
|
|
dg.gen_load_T0_GPR(rS);
|
|
dg.gen_or_32_T0_im(val);
|
|
dg.gen_store_T0_GPR(rA);
|
|
}
|
|
break;
|
|
}
|
|
case PPC_I(XORI): // XOR Immediate
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_xor_32_T0_im(operand_UIMM::get(this, opcode));
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(ORIS): // OR Immediate Shifted
|
|
case PPC_I(XORIS): // XOR Immediate Shifted
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
uint32 val = operand_UIMM_shifted::get(this, opcode);
|
|
switch (ii->mnemo) {
|
|
case PPC_I(ORIS): dg.gen_or_32_T0_im(val); break;
|
|
case PPC_I(XORIS): dg.gen_xor_32_T0_im(val); break;
|
|
default: abort();
|
|
}
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(ANDI): // AND Immediate
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_and_32_T0_im(operand_UIMM::get(this, opcode));
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(ANDIS): // AND Immediate Shifted
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_and_32_T0_im(operand_UIMM_shifted::get(this, opcode));
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(EXTSB): // Extend Sign Byte
|
|
case PPC_I(EXTSH): // Extend Sign Half Word
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
switch (ii->mnemo) {
|
|
case PPC_I(EXTSB): dg.gen_se_8_32_T0(); break;
|
|
case PPC_I(EXTSH): dg.gen_se_16_32_T0(); break;
|
|
default: abort();
|
|
}
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(NEG): // Negate
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
if (OE_field::test(opcode))
|
|
dg.gen_nego_T0();
|
|
else
|
|
dg.gen_neg_32_T0();
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(MFCR): // Move from Condition Register
|
|
{
|
|
dg.gen_load_T0_CR();
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(MFSPR): // Move from Special-Purpose Register
|
|
{
|
|
const int spr = operand_SPR::get(this, opcode);
|
|
switch (spr) {
|
|
case powerpc_registers::SPR_XER:
|
|
dg.gen_load_T0_XER();
|
|
break;
|
|
case powerpc_registers::SPR_LR:
|
|
dg.gen_load_T0_LR();
|
|
break;
|
|
case powerpc_registers::SPR_CTR:
|
|
dg.gen_load_T0_CTR();
|
|
break;
|
|
case powerpc_registers::SPR_VRSAVE:
|
|
dg.gen_load_T0_VRSAVE();
|
|
break;
|
|
#ifdef SHEEPSHAVER
|
|
case powerpc_registers::SPR_SDR1:
|
|
dg.gen_mov_32_T0_im(0xdead001f);
|
|
break;
|
|
case powerpc_registers::SPR_PVR: {
|
|
extern uint32 PVR;
|
|
dg.gen_mov_32_T0_im(PVR);
|
|
break;
|
|
}
|
|
default:
|
|
dg.gen_mov_32_T0_im(0);
|
|
break;
|
|
#else
|
|
default: goto do_generic;
|
|
#endif
|
|
}
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(MTSPR): // Move to Special-Purpose Register
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
const int spr = operand_SPR::get(this, opcode);
|
|
switch (spr) {
|
|
case powerpc_registers::SPR_XER:
|
|
dg.gen_store_T0_XER();
|
|
break;
|
|
case powerpc_registers::SPR_LR:
|
|
dg.gen_store_T0_LR();
|
|
break;
|
|
case powerpc_registers::SPR_CTR:
|
|
dg.gen_store_T0_CTR();
|
|
break;
|
|
case powerpc_registers::SPR_VRSAVE:
|
|
dg.gen_store_T0_VRSAVE();
|
|
break;
|
|
#ifndef SHEEPSHAVER
|
|
default: goto do_generic;
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case PPC_I(ADD): // Add
|
|
case PPC_I(ADDC): // Add Carrying
|
|
case PPC_I(ADDE): // Add Extended
|
|
case PPC_I(SUBF): // Subtract From
|
|
case PPC_I(SUBFC): // Subtract from Carrying
|
|
case PPC_I(SUBFE): // Subtract from Extended
|
|
case PPC_I(MULLW): // Multiply Low Word
|
|
case PPC_I(DIVW): // Divide Word
|
|
case PPC_I(DIVWU): // Divide Word Unsigned
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
if (OE_field::test(opcode)) {
|
|
switch (ii->mnemo) {
|
|
case PPC_I(ADD): dg.gen_addo_T0_T1(); break;
|
|
case PPC_I(ADDC): dg.gen_addco_T0_T1(); break;
|
|
case PPC_I(ADDE): dg.gen_addeo_T0_T1(); break;
|
|
case PPC_I(SUBF): dg.gen_subfo_T0_T1(); break;
|
|
case PPC_I(SUBFC): dg.gen_subfco_T0_T1(); break;
|
|
case PPC_I(SUBFE): dg.gen_subfeo_T0_T1(); break;
|
|
case PPC_I(MULLW): dg.gen_mullwo_T0_T1(); break;
|
|
case PPC_I(DIVW): dg.gen_divwo_T0_T1(); break;
|
|
case PPC_I(DIVWU): dg.gen_divwuo_T0_T1(); break;
|
|
default: abort();
|
|
}
|
|
}
|
|
else {
|
|
switch (ii->mnemo) {
|
|
case PPC_I(ADD): dg.gen_add_32_T0_T1(); break;
|
|
case PPC_I(ADDC): dg.gen_addc_T0_T1(); break;
|
|
case PPC_I(ADDE): dg.gen_adde_T0_T1(); break;
|
|
case PPC_I(SUBF): dg.gen_subf_T0_T1(); break;
|
|
case PPC_I(SUBFC): dg.gen_subfc_T0_T1(); break;
|
|
case PPC_I(SUBFE): dg.gen_subfe_T0_T1(); break;
|
|
case PPC_I(MULLW): dg.gen_umul_32_T0_T1(); break;
|
|
case PPC_I(DIVW): dg.gen_divw_T0_T1(); break;
|
|
case PPC_I(DIVWU): dg.gen_divwu_T0_T1(); break;
|
|
default: abort();
|
|
}
|
|
}
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(ADDIC): // Add Immediate Carrying
|
|
case PPC_I(ADDIC_): // Add Immediate Carrying and Record
|
|
case PPC_I(SUBFIC): // Subtract from Immediate Carrying
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
const uint32 val = operand_SIMM::get(this, opcode);
|
|
switch (ii->mnemo) {
|
|
case PPC_I(ADDIC):
|
|
dg.gen_addc_T0_im(val);
|
|
break;
|
|
case PPC_I(ADDIC_):
|
|
dg.gen_addc_T0_im(val);
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
case PPC_I(SUBFIC):
|
|
dg.gen_subfc_T0_im(val);
|
|
break;
|
|
default: abort();
|
|
}
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(ADDME): // Add to Minus One Extended
|
|
case PPC_I(ADDZE): // Add to Zero Extended
|
|
case PPC_I(SUBFME): // Subtract from Minus One Extended
|
|
case PPC_I(SUBFZE): // Subtract from Zero Extended
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
if (OE_field::test(opcode)) {
|
|
switch (ii->mnemo) {
|
|
case PPC_I(ADDME): dg.gen_addmeo_T0(); break;
|
|
case PPC_I(ADDZE): dg.gen_addzeo_T0(); break;
|
|
case PPC_I(SUBFME): dg.gen_subfmeo_T0(); break;
|
|
case PPC_I(SUBFZE): dg.gen_subfzeo_T0(); break;
|
|
default: abort();
|
|
}
|
|
}
|
|
else {
|
|
switch (ii->mnemo) {
|
|
case PPC_I(ADDME): dg.gen_addme_T0(); break;
|
|
case PPC_I(ADDZE): dg.gen_addze_T0(); break;
|
|
case PPC_I(SUBFME): dg.gen_subfme_T0(); break;
|
|
case PPC_I(SUBFZE): dg.gen_subfze_T0(); break;
|
|
default: abort();
|
|
}
|
|
}
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(ADDI): // Add Immediate
|
|
{
|
|
const int rA = rA_field::extract(opcode);
|
|
const int rD = rD_field::extract(opcode);
|
|
if (rA == 0) // li rD,value
|
|
dg.gen_mov_32_T0_im(operand_SIMM::get(this, opcode));
|
|
else {
|
|
dg.gen_load_T0_GPR(rA);
|
|
dg.gen_add_32_T0_im(operand_SIMM::get(this, opcode));
|
|
}
|
|
dg.gen_store_T0_GPR(rD);
|
|
break;
|
|
}
|
|
case PPC_I(ADDIS): // Add Immediate Shifted
|
|
{
|
|
const int rA = rA_field::extract(opcode);
|
|
const int rD = rD_field::extract(opcode);
|
|
if (rA == 0) // lis rD,value
|
|
dg.gen_mov_32_T0_im(operand_SIMM_shifted::get(this, opcode));
|
|
else {
|
|
dg.gen_load_T0_GPR(rA);
|
|
dg.gen_add_32_T0_im(operand_SIMM_shifted::get(this, opcode));
|
|
}
|
|
dg.gen_store_T0_GPR(rD);
|
|
break;
|
|
}
|
|
case PPC_I(RLWIMI): // Rotate Left Word Immediate then Mask Insert
|
|
{
|
|
const int rA = rA_field::extract(opcode);
|
|
const int rS = rS_field::extract(opcode);
|
|
const int SH = SH_field::extract(opcode);
|
|
const int MB = MB_field::extract(opcode);
|
|
const int ME = ME_field::extract(opcode);
|
|
dg.gen_load_T0_GPR(rA);
|
|
dg.gen_load_T1_GPR(rS);
|
|
const uint32 m = mask_operand::compute(MB, ME);
|
|
dg.gen_rlwimi_T0_T1(SH, m);
|
|
dg.gen_store_T0_GPR(rA);
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(RLWINM): // Rotate Left Word Immediate then AND with Mask
|
|
{
|
|
const int rS = rS_field::extract(opcode);
|
|
const int rA = rA_field::extract(opcode);
|
|
const int SH = SH_field::extract(opcode);
|
|
const int MB = MB_field::extract(opcode);
|
|
const int ME = ME_field::extract(opcode);
|
|
const uint32 m = mask_operand::compute(MB, ME);
|
|
dg.gen_load_T0_GPR(rS);
|
|
if (MB == 0) {
|
|
if (ME == 31) {
|
|
// rotlwi rA,rS,SH
|
|
if (SH > 0)
|
|
dg.gen_rol_32_T0_im(SH);
|
|
}
|
|
else if (ME == (31 - SH)) {
|
|
// slwi rA,rS,SH
|
|
dg.gen_lsl_32_T0_im(SH);
|
|
}
|
|
else if (SH == 0) {
|
|
// andi rA,rS,MASK(0,ME)
|
|
dg.gen_and_32_T0_im(m);
|
|
}
|
|
else goto do_generic_rlwinm;
|
|
}
|
|
else if (ME == 31) {
|
|
if (SH == (32 - MB)) {
|
|
// srwi rA,rS,SH
|
|
dg.gen_lsr_32_T0_im(MB);
|
|
}
|
|
else if (SH == 0) {
|
|
// andi rA,rS,MASK(MB,31)
|
|
dg.gen_and_32_T0_im(m);
|
|
}
|
|
else goto do_generic_rlwinm;
|
|
}
|
|
else {
|
|
// rlwinm rA,rS,SH,MB,ME
|
|
do_generic_rlwinm:
|
|
dg.gen_rlwinm_T0_T1(SH, m);
|
|
}
|
|
dg.gen_store_T0_GPR(rA);
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(RLWNM): // Rotate Left Word then AND with Mask
|
|
{
|
|
const int rS = rS_field::extract(opcode);
|
|
const int rB = rB_field::extract(opcode);
|
|
const int rA = rA_field::extract(opcode);
|
|
const int MB = MB_field::extract(opcode);
|
|
const int ME = ME_field::extract(opcode);
|
|
const uint32 m = mask_operand::compute(MB, ME);
|
|
dg.gen_load_T0_GPR(rS);
|
|
dg.gen_load_T1_GPR(rB);
|
|
if (MB == 0 && ME == 31)
|
|
dg.gen_rol_32_T0_T1();
|
|
else
|
|
dg.gen_rlwnm_T0_T1(m);
|
|
dg.gen_store_T0_GPR(rA);
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(CNTLZW): // Count Leading Zeros Word
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_cntlzw_32_T0();
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(SLW): // Shift Left Word
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
dg.gen_slw_T0_T1();
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(SRW): // Shift Right Word
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
dg.gen_srw_T0_T1();
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(SRAW): // Shift Right Algebraic Word
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
dg.gen_sraw_T0_T1();
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(SRAWI): // Shift Right Algebraic Word Immediate
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_sraw_T0_im(SH_field::extract(opcode));
|
|
dg.gen_store_T0_GPR(rA_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(MULHW): // Multiply High Word
|
|
case PPC_I(MULHWU): // Multiply High Word Unsigned
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_load_T1_GPR(rB_field::extract(opcode));
|
|
if (ii->mnemo == PPC_I(MULHW))
|
|
dg.gen_mulhw_T0_T1();
|
|
else
|
|
dg.gen_mulhwu_T0_T1();
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr0_T0();
|
|
break;
|
|
}
|
|
case PPC_I(MULLI): // Multiply Low Immediate
|
|
{
|
|
dg.gen_load_T0_GPR(rA_field::extract(opcode));
|
|
dg.gen_mulli_T0_im(operand_SIMM::get(this, opcode));
|
|
dg.gen_store_T0_GPR(rD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(DCBZ): // Data Cache Block Clear to Zero
|
|
{
|
|
const int rA = rA_field::extract(opcode);
|
|
const int rB = rB_field::extract(opcode);
|
|
if (rA == 0)
|
|
dg.gen_load_T0_GPR(rB);
|
|
else {
|
|
dg.gen_load_T0_GPR(rA);
|
|
dg.gen_load_T1_GPR(rB);
|
|
dg.gen_add_32_T0_T1();
|
|
}
|
|
dg.gen_dcbz_T0();
|
|
break;
|
|
}
|
|
case PPC_I(DCBA): // Data Cache Block Allocate
|
|
case PPC_I(DCBF): // Data Cache Block Flush
|
|
case PPC_I(DCBI): // Data Cache Block Invalidate
|
|
case PPC_I(DCBST): // Data Cache Block Store
|
|
case PPC_I(DCBT): // Data Cache Block Touch
|
|
case PPC_I(DCBTST): // Data Cache Block Touch for Store
|
|
case PPC_I(ECIWX): // External Control In Word Indexed
|
|
case PPC_I(ECOWX): // External Control Out Word Indexed
|
|
case PPC_I(EIEIO): // Enforce In-Order Execution of I/O
|
|
case PPC_I(SYNC): // Synchronize
|
|
{
|
|
break;
|
|
}
|
|
case PPC_I(ISYNC): // Instruction synchronize
|
|
{
|
|
typedef void (*func_t)(dyngen_cpu_base);
|
|
func_t func = (func_t)nv_mem_fun(&powerpc_cpu::execute_invalidate_cache_range).ptr();
|
|
dg.gen_invoke_CPU(func);
|
|
break;
|
|
}
|
|
case PPC_I(MTCRF): // Move to Condition Register Fields
|
|
{
|
|
dg.gen_load_T0_GPR(rS_field::extract(opcode));
|
|
dg.gen_mtcrf_T0_im(field2mask[CRM_field::extract(opcode)]);
|
|
break;
|
|
}
|
|
case PPC_I(MCRF): // Move Condition Register Field
|
|
{
|
|
dg.gen_load_T0_crf(crfS_field::extract(opcode));
|
|
dg.gen_store_T0_crf(crfD_field::extract(opcode));
|
|
break;
|
|
}
|
|
case PPC_I(LFD): // Load Floating-Point Double
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_load;;
|
|
case PPC_I(LFDU): // Load Floating-Point Double with Update
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_load;
|
|
case PPC_I(LFDUX): // Load Floating-Point Double with Update Indexed
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_load;
|
|
case PPC_I(LFDX): // Load Floating-Point Double Indexed
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_load;
|
|
case PPC_I(LFS): // Load Floating-Point Single
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_load;
|
|
case PPC_I(LFSU): // Load Floating-Point Single with Update
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_load;
|
|
case PPC_I(LFSUX): // Load Floating-Point Single with Update Indexed
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_load;
|
|
case PPC_I(LFSX): // Load Floating-Point Single Indexed
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_load;
|
|
{
|
|
do_fp_load:
|
|
// Extract RZ operand
|
|
const int rA = rA_field::extract(opcode);
|
|
if (rA == 0 && !op.mem.do_update)
|
|
dg.gen_mov_32_T1_im(0);
|
|
else
|
|
dg.gen_load_T1_GPR(rA);
|
|
|
|
// Extract index operand
|
|
if (op.mem.do_indexed)
|
|
dg.gen_load_T2_GPR(rB_field::extract(opcode));
|
|
|
|
// Load floating point data
|
|
if (op.mem.size == 8) {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_load_double_FD_T1_T2();
|
|
else
|
|
dg.gen_load_double_FD_T1_im(operand_D::get(this, opcode));
|
|
}
|
|
else {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_load_single_FD_T1_T2();
|
|
else
|
|
dg.gen_load_single_FD_T1_im(operand_D::get(this, opcode));
|
|
}
|
|
|
|
// Commit result
|
|
dg.gen_store_FD_FPR(frD_field::extract(opcode));
|
|
|
|
// Update RA
|
|
if (op.mem.do_update) {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_add_32_T1_T2();
|
|
else
|
|
dg.gen_add_32_T1_im(operand_D::get(this, opcode));
|
|
dg.gen_store_T1_GPR(rA);
|
|
}
|
|
break;
|
|
}
|
|
case PPC_I(STFD): // Store Floating-Point Double
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_store;
|
|
case PPC_I(STFDU): // Store Floating-Point Double with Update
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_store;
|
|
case PPC_I(STFDUX): // Store Floating-Point Double with Update Indexed
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_store;
|
|
case PPC_I(STFDX): // Store Floating-Point Double Indexed
|
|
op.mem.size = 8;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_store;
|
|
case PPC_I(STFS): // Store Floating-Point Single
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_store;
|
|
case PPC_I(STFSU): // Store Floating-Point Single with Update
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 0;
|
|
goto do_fp_store;
|
|
case PPC_I(STFSUX): // Store Floating-Point Single with Update Indexed
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 1;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_store;
|
|
case PPC_I(STFSX): // Store Floating-Point Single Indexed
|
|
op.mem.size = 4;
|
|
op.mem.do_update = 0;
|
|
op.mem.do_indexed = 1;
|
|
goto do_fp_store;
|
|
{
|
|
do_fp_store:
|
|
// Extract RZ operand
|
|
const int rA = rA_field::extract(opcode);
|
|
if (rA == 0 && !op.mem.do_update)
|
|
dg.gen_mov_32_T1_im(0);
|
|
else
|
|
dg.gen_load_T1_GPR(rA);
|
|
|
|
// Extract index operand
|
|
if (op.mem.do_indexed)
|
|
dg.gen_load_T2_GPR(rB_field::extract(opcode));
|
|
|
|
// Load register to commit to memory
|
|
dg.gen_load_F0_FPR(frS_field::extract(opcode));
|
|
|
|
// Store floating point data
|
|
if (op.mem.size == 8) {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_store_double_F0_T1_T2();
|
|
else
|
|
dg.gen_store_double_F0_T1_im(operand_D::get(this, opcode));
|
|
}
|
|
else {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_store_single_F0_T1_T2();
|
|
else
|
|
dg.gen_store_single_F0_T1_im(operand_D::get(this, opcode));
|
|
}
|
|
|
|
// Update RA
|
|
if (op.mem.do_update) {
|
|
if (op.mem.do_indexed)
|
|
dg.gen_add_32_T1_T2();
|
|
else
|
|
dg.gen_add_32_T1_im(operand_D::get(this, opcode));
|
|
dg.gen_store_T1_GPR(rA);
|
|
}
|
|
break;
|
|
}
|
|
#if PPC_ENABLE_FPU_EXCEPTIONS == 0
|
|
case PPC_I(FABS): // Floating Absolute Value
|
|
case PPC_I(FNABS): // Floating Negative Absolute Value
|
|
case PPC_I(FNEG): // Floating Negate
|
|
case PPC_I(FMR): // Floating Move Register
|
|
{
|
|
dg.gen_load_F0_FPR(frB_field::extract(opcode));
|
|
switch (ii->mnemo) {
|
|
case PPC_I(FABS): dg.gen_fabs_FD_F0(); break;
|
|
case PPC_I(FNABS): dg.gen_fnabs_FD_F0(); break;
|
|
case PPC_I(FNEG): dg.gen_fneg_FD_F0(); break;
|
|
case PPC_I(FMR): dg.gen_fmov_FD_F0(); break;
|
|
}
|
|
dg.gen_store_FD_FPR(frD_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr1();
|
|
break;
|
|
}
|
|
case PPC_I(FADD): // Floating Add (Double-Precision)
|
|
case PPC_I(FSUB): // Floating Subtract (Double-Precision)
|
|
case PPC_I(FMUL): // Floating Multiply (Double-Precision)
|
|
case PPC_I(FDIV): // Floating Divide (Double-Precision)
|
|
case PPC_I(FADDS): // Floating Add (Single-Precision)
|
|
case PPC_I(FSUBS): // Floating Subtract (Single-Precision)
|
|
case PPC_I(FMULS): // Floating Multiply (Single-Precision)
|
|
case PPC_I(FDIVS): // Floating Divide (Single-Precision)
|
|
{
|
|
dg.gen_load_F0_FPR(frA_field::extract(opcode));
|
|
if (ii->mnemo == PPC_I(FMUL) || ii->mnemo == PPC_I(FMULS))
|
|
dg.gen_load_F1_FPR(frC_field::extract(opcode));
|
|
else
|
|
dg.gen_load_F1_FPR(frB_field::extract(opcode));
|
|
switch (ii->mnemo) {
|
|
case PPC_I(FADD): dg.gen_fadd_FD_F0_F1(); break;
|
|
case PPC_I(FSUB): dg.gen_fsub_FD_F0_F1(); break;
|
|
case PPC_I(FMUL): dg.gen_fmul_FD_F0_F1(); break;
|
|
case PPC_I(FDIV): dg.gen_fdiv_FD_F0_F1(); break;
|
|
case PPC_I(FADDS): dg.gen_fadds_FD_F0_F1(); break;
|
|
case PPC_I(FSUBS): dg.gen_fsubs_FD_F0_F1(); break;
|
|
case PPC_I(FMULS): dg.gen_fmuls_FD_F0_F1(); break;
|
|
case PPC_I(FDIVS): dg.gen_fdivs_FD_F0_F1(); break;
|
|
}
|
|
dg.gen_store_FD_FPR(frD_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr1();
|
|
break;
|
|
}
|
|
case PPC_I(FMADD): // Floating Multiply-Add (Double-Precision)
|
|
case PPC_I(FMSUB): // Floating Multiply-Subtract (Double-Precision)
|
|
case PPC_I(FNMADD): // Floating Negative Multiply-Add (Double-Precision)
|
|
case PPC_I(FNMSUB): // Floating Negative Multiply-Subract (Double-Precision)
|
|
case PPC_I(FMADDS): // Floating Multiply-Add (Single-Precision)
|
|
case PPC_I(FMSUBS): // Floating Multiply-Subtract (Single-Precision)
|
|
case PPC_I(FNMADDS): // Floating Negative Multiply-Add (Single-Precision)
|
|
case PPC_I(FNMSUBS): // Floating Negative Multiply-Subract (Single-Precision)
|
|
{
|
|
dg.gen_load_F0_FPR(frA_field::extract(opcode));
|
|
dg.gen_load_F1_FPR(frC_field::extract(opcode));
|
|
dg.gen_load_F2_FPR(frB_field::extract(opcode));
|
|
switch (ii->mnemo) {
|
|
case PPC_I(FMADD): dg.gen_fmadd_FD_F0_F1_F2(); break;
|
|
case PPC_I(FMSUB): dg.gen_fmsub_FD_F0_F1_F2(); break;
|
|
case PPC_I(FNMADD): dg.gen_fnmadd_FD_F0_F1_F2(); break;
|
|
case PPC_I(FNMSUB): dg.gen_fnmsub_FD_F0_F1_F2(); break;
|
|
case PPC_I(FMADDS): dg.gen_fmadds_FD_F0_F1_F2(); break;
|
|
case PPC_I(FMSUBS): dg.gen_fmsubs_FD_F0_F1_F2(); break;
|
|
case PPC_I(FNMADDS): dg.gen_fnmadds_FD_F0_F1_F2(); break;
|
|
case PPC_I(FNMSUBS): dg.gen_fnmsubs_FD_F0_F1_F2(); break;
|
|
}
|
|
dg.gen_store_FD_FPR(frD_field::extract(opcode));
|
|
if (Rc_field::test(opcode))
|
|
dg.gen_record_cr1();
|
|
break;
|
|
}
|
|
#endif
|
|
case PPC_I(LVEWX):
|
|
case PPC_I(LVX):
|
|
case PPC_I(LVXL):
|
|
case PPC_I(STVEWX):
|
|
case PPC_I(STVX):
|
|
case PPC_I(STVXL):
|
|
assert(vD_field::mask() == vS_field::mask());
|
|
assert(vA_field::mask() == rA_field::mask());
|
|
assert(vB_field::mask() == rB_field::mask());
|
|
// fall-through
|
|
case PPC_I(VCMPEQFP):
|
|
case PPC_I(VCMPEQUB):
|
|
case PPC_I(VCMPEQUH):
|
|
case PPC_I(VCMPEQUW):
|
|
case PPC_I(VCMPGEFP):
|
|
case PPC_I(VCMPGTFP):
|
|
case PPC_I(VCMPGTSB):
|
|
case PPC_I(VCMPGTSH):
|
|
case PPC_I(VCMPGTSW):
|
|
{
|
|
const int vD = vD_field::extract(opcode);
|
|
const int vA = vA_field::extract(opcode);
|
|
const int vB = vB_field::extract(opcode);
|
|
if (!dg.gen_vector_compare(ii->mnemo, vD, vA, vB, vRc_field::test(opcode)))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
case PPC_I(VADDFP):
|
|
case PPC_I(VADDUBM):
|
|
case PPC_I(VADDUHM):
|
|
case PPC_I(VADDUWM):
|
|
case PPC_I(VAND):
|
|
case PPC_I(VANDC):
|
|
case PPC_I(VAVGUB):
|
|
case PPC_I(VAVGUH):
|
|
case PPC_I(VMAXSH):
|
|
case PPC_I(VMAXUB):
|
|
case PPC_I(VMINSH):
|
|
case PPC_I(VMINUB):
|
|
case PPC_I(VNOR):
|
|
case PPC_I(VOR):
|
|
case PPC_I(VSUBFP):
|
|
case PPC_I(VSUBUBM):
|
|
case PPC_I(VSUBUHM):
|
|
case PPC_I(VSUBUWM):
|
|
case PPC_I(VXOR):
|
|
case PPC_I(VREFP):
|
|
case PPC_I(VRSQRTEFP):
|
|
{
|
|
const int vD = vD_field::extract(opcode);
|
|
const int vA = vA_field::extract(opcode);
|
|
const int vB = vB_field::extract(opcode);
|
|
if (!dg.gen_vector_2(ii->mnemo, vD, vA, vB))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
case PPC_I(VSEL):
|
|
case PPC_I(VPERM):
|
|
case PPC_I(VMADDFP):
|
|
case PPC_I(VNMSUBFP):
|
|
{
|
|
const int vD = vD_field::extract(opcode);
|
|
const int vA = vA_field::extract(opcode);
|
|
const int vB = vB_field::extract(opcode);
|
|
const int vC = vC_field::extract(opcode);
|
|
if (!dg.gen_vector_3(ii->mnemo, vD, vA, vB, vC))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
case PPC_I(VSLDOI):
|
|
{
|
|
const int vD = vD_field::extract(opcode);
|
|
const int vA = vA_field::extract(opcode);
|
|
const int vB = vB_field::extract(opcode);
|
|
const int SH = vSH_field::extract(opcode);
|
|
if (!dg.gen_vector_3(ii->mnemo, vD, vA, vB, SH))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
case PPC_I(MFVSCR):
|
|
{
|
|
if (!dg.gen_vector_1(ii->mnemo, vD_field::extract(opcode)))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
case PPC_I(MTVSCR):
|
|
{
|
|
if (!dg.gen_vector_1(ii->mnemo, vB_field::extract(opcode)))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
case PPC_I(VSPLTISB):
|
|
case PPC_I(VSPLTISH):
|
|
case PPC_I(VSPLTISW):
|
|
{
|
|
const int vD = vD_field::extract(opcode);
|
|
const int SIMM = op_sign_extend_5_32::apply(vUIMM_field::extract(opcode));
|
|
if (!dg.gen_vector_2(ii->mnemo, vD, SIMM, 0))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
case PPC_I(VSPLTB):
|
|
case PPC_I(VSPLTH):
|
|
case PPC_I(VSPLTW):
|
|
{
|
|
const int vD = vD_field::extract(opcode);
|
|
const int UIMM = vUIMM_field::extract(opcode);
|
|
const int vB = vB_field::extract(opcode);
|
|
if (!dg.gen_vector_2(ii->mnemo, vD, UIMM, vB))
|
|
goto do_generic;
|
|
break;
|
|
}
|
|
default: // Direct call to instruction handler
|
|
{
|
|
typedef void (*func_t)(dyngen_cpu_base, uint32);
|
|
func_t func;
|
|
do_generic:
|
|
func = (func_t)ii->execute.ptr();
|
|
goto do_invoke;
|
|
do_illegal:
|
|
func = (func_t)nv_mem_fun(&powerpc_cpu::execute_illegal).ptr();
|
|
goto do_invoke;
|
|
do_invoke:
|
|
#if PPC_PROFILE_GENERIC_CALLS
|
|
if (ii->mnemo <= PPC_I(MAX)) {
|
|
uintptr mem = (uintptr)&generic_calls_count[ii->mnemo];
|
|
if (mem <= 0xffffffff)
|
|
dg.gen_inc_32_mem(mem);
|
|
}
|
|
#endif
|
|
cg_context.pc = dpc;
|
|
cg_context.opcode = opcode;
|
|
cg_context.instr_info = ii;
|
|
cg_context.done_compile = done_compile;
|
|
compile_status = compile1(cg_context);
|
|
switch (compile_status) {
|
|
case COMPILE_FAILURE:
|
|
case COMPILE_EPILOGUE_OK:
|
|
if ((dpc - sync_pc) > sync_pc_offset) {
|
|
sync_pc = dpc;
|
|
sync_pc_offset = 0;
|
|
if (compile_status == COMPILE_EPILOGUE_OK)
|
|
break;
|
|
dg.gen_set_PC_im(dpc);
|
|
}
|
|
sync_pc_offset += 4;
|
|
dg.gen_invoke_CPU_im(func, opcode);
|
|
compile_status = COMPILE_CODE_OK; // could generate code, though a call to handler
|
|
break;
|
|
}
|
|
done_compile = cg_context.done_compile;
|
|
}
|
|
}
|
|
if (dg.full_translation_cache()) {
|
|
// Invalidate cache and start again
|
|
invalidate_cache();
|
|
goto again;
|
|
}
|
|
}
|
|
// Do nothing if block has special epilogue code generated already
|
|
assert(compile_status != COMPILE_FAILURE);
|
|
if (compile_status != COMPILE_EPILOGUE_OK) {
|
|
// In direct block chaining mode, this code is reached only if
|
|
// there are pending spcflags, i.e. get out of this block
|
|
if (!use_direct_block_chaining) {
|
|
// TODO: optimize this to a direct jump to pregenerated code?
|
|
dg.gen_mov_ad_A0_im((uintptr)bi);
|
|
dg.gen_jump_next_A0();
|
|
}
|
|
dg.gen_exec_return();
|
|
}
|
|
bi->end_pc = dpc;
|
|
if (dpc < min_pc)
|
|
min_pc = dpc;
|
|
else if (dpc > max_pc)
|
|
max_pc = dpc;
|
|
bi->min_pc = min_pc;
|
|
bi->max_pc = max_pc;
|
|
|
|
#if DYNGEN_DIRECT_BLOCK_CHAINING
|
|
// Generate backpatch trampolines
|
|
if (use_direct_block_chaining) {
|
|
typedef void *(*func_t)(dyngen_cpu_base);
|
|
func_t func = (func_t)nv_mem_fun(&powerpc_cpu::compile_chain_block).ptr();
|
|
for (int i = 0; i < block_info::MAX_TARGETS; i++) {
|
|
if (bi->li[i].jmp_pc != block_info::INVALID_PC) {
|
|
uint8 *p = dg.gen_align(16);
|
|
dg.gen_mov_ad_A0_im(((uintptr)bi) | i);
|
|
dg.gen_invoke_CPU_A0_ret_A0(func);
|
|
dg.gen_jmp_A0();
|
|
assert(dg.jmp_addr[i] != NULL);
|
|
bi->li[i].jmp_addr = dg.jmp_addr[i];
|
|
bi->li[i].jmp_resolve_addr = p;
|
|
dg_set_jmp_target_noflush(bi->li[i].jmp_addr, bi->li[i].jmp_resolve_addr);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
bi->size = dg.code_ptr() - bi->entry_point;
|
|
if (disasm)
|
|
disasm_translation(entry_point, dpc - entry_point + 4, bi->entry_point, bi->size);
|
|
|
|
dg.gen_end();
|
|
my_block_cache.add_to_cl_list(bi);
|
|
if (is_read_only_memory(bi->pc))
|
|
my_block_cache.add_to_dormant_list(bi);
|
|
else
|
|
my_block_cache.add_to_active_list(bi);
|
|
#if PPC_PROFILE_COMPILE_TIME
|
|
compile_time += (clock() - start_time);
|
|
#endif
|
|
return bi;
|
|
}
|
|
#endif
|