macemu/SheepShaver/src/kpx_cpu/src/cpu/ppc/ppc-translate.cpp

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