cog/Frameworks/lazyusf2/lazyusf2/r4300/cached_interp.c

576 lines
12 KiB
C

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Mupen64plus - cached_interp.c *
* Mupen64Plus homepage: http://code.google.com/p/mupen64plus/ *
* Copyright (C) 2002 Hacktarux *
* *
* 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., *
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
#include "cached_interp.h"
#include "api/m64p_types.h"
#include "api/callbacks.h"
#include "main/main.h"
#include "memory/memory.h"
#include "r4300.h"
#include "cp0.h"
#include "cp1.h"
#include "ops.h"
#include "exception.h"
#include "interupt.h"
#include "macros.h"
#include "recomp.h"
#include "tlb.h"
// -----------------------------------------------------------
// Cached interpreter functions (and fallback for dynarec).
// -----------------------------------------------------------
#define UPDATE_DEBUGGER() do { } while(0)
#define PCADDR state->PC->addr
#define ADD_TO_PC(x) state->PC += x;
#define DECLARE_INSTRUCTION(name) static void osal_fastcall name(usf_state_t * state)
#define DECLARE_JUMP(name, destination, condition, link, likely, cop1) \
static void osal_fastcall name(usf_state_t * state) \
{ \
const int take_jump = (condition); \
const unsigned int jump_target = (destination); \
long long int *link_register = (link); \
if (cop1 && check_cop1_unusable(state)) return; \
if (link_register != &state->reg[0]) \
{ \
*link_register=state->PC->addr + 8; \
sign_extended(*link_register); \
} \
if (!likely || take_jump) \
{ \
state->PC++; \
state->delay_slot=1; \
UPDATE_DEBUGGER(); \
state->PC->ops(state); \
update_count(state); \
state->delay_slot=0; \
if (take_jump && !state->skip_jump) \
{ \
state->PC=state->actual->block+((jump_target-state->actual->start)>>2); \
} \
} \
else \
{ \
state->PC += 2; \
update_count(state); \
} \
state->last_addr = state->PC->addr; \
if (state->cycle_count >=0) gen_interupt(state); \
} \
static void osal_fastcall name##_OUT(usf_state_t * state) \
{ \
const int take_jump = (condition); \
const unsigned int jump_target = (destination); \
long long int *link_register = (link); \
if (cop1 && check_cop1_unusable(state)) return; \
if (link_register != &state->reg[0]) \
{ \
*link_register=state->PC->addr + 8; \
sign_extended(*link_register); \
} \
if (!likely || take_jump) \
{ \
state->PC++; \
state->delay_slot=1; \
UPDATE_DEBUGGER(); \
state->PC->ops(state); \
update_count(state); \
state->delay_slot=0; \
if (take_jump && !state->skip_jump) \
{ \
jump_to(jump_target); \
} \
} \
else \
{ \
state->PC += 2; \
update_count(state); \
} \
state->last_addr = state->PC->addr; \
if (state->cycle_count >=0) gen_interupt(state); \
} \
static void osal_fastcall name##_IDLE(usf_state_t * state) \
{ \
const int take_jump = (condition); \
if (cop1 && check_cop1_unusable(state)) return; \
if (take_jump) \
{ \
if (state->cycle_count < 0) \
{ \
state->g_cp0_regs[CP0_COUNT_REG] -= state->cycle_count; \
state->cycle_count = 0; \
} \
} \
name(state); \
}
#define CHECK_MEMORY() \
if (!state->invalid_code[state->address>>12]) \
if (state->blocks[state->address>>12]->block[(state->address&0xFFF)/4].ops != \
state->current_instruction_table.NOTCOMPILED) \
state->invalid_code[state->address>>12] = 1;
// two functions are defined from the macros above but never used
// these prototype declarations will prevent a warning
#if defined(__GNUC__)
static void osal_fastcall JR_IDLE(usf_state_t *) __attribute__((used));
static void osal_fastcall JALR_IDLE(usf_state_t *) __attribute__((used));
#endif
#include "interpreter.def"
// -----------------------------------------------------------
// Flow control 'fake' instructions
// -----------------------------------------------------------
static void osal_fastcall FIN_BLOCK(usf_state_t * state)
{
if (!state->delay_slot)
{
jump_to((state->PC-1)->addr+4);
/*#ifdef DBG
if (g_DebuggerActive) update_debugger(PC->addr);
#endif
Used by dynarec only, check should be unnecessary
*/
state->PC->ops(state);
#ifdef DYNAREC
if (state->r4300emu == CORE_DYNAREC) dyna_jump(state);
#endif
}
else
{
precomp_block *blk = state->actual;
precomp_instr *inst = state->PC;
jump_to((state->PC-1)->addr+4);
/*#ifdef DBG
if (g_DebuggerActive) update_debugger(PC->addr);
#endif
Used by dynarec only, check should be unnecessary
*/
if (!state->skip_jump)
{
state->PC->ops(state);
state->actual = blk;
state->PC = inst+1;
}
else
state->PC->ops(state);
#ifdef DYNAREC
if (state->r4300emu == CORE_DYNAREC) dyna_jump(state);
#endif
}
}
static void osal_fastcall NOTCOMPILED(usf_state_t * state)
{
unsigned int *mem = fast_mem_access(state, state->blocks[state->PC->addr>>12]->start);
if (mem != NULL)
recompile_block(state, (int *)mem, state->blocks[state->PC->addr >> 12], state->PC->addr);
else
DebugMessage(state, M64MSG_ERROR, "not compiled exception");
/*#ifdef DBG
if (g_DebuggerActive) update_debugger(PC->addr);
#endif
The preceeding update_debugger SHOULD be unnecessary since it should have been
called before NOTCOMPILED would have been executed
*/
state->PC->ops(state);
#ifdef DYNAREC
if (state->r4300emu == CORE_DYNAREC)
dyna_jump(state);
#endif
}
static void osal_fastcall NOTCOMPILED2(usf_state_t * state)
{
NOTCOMPILED(state);
}
// -----------------------------------------------------------
// Cached interpreter instruction table
// -----------------------------------------------------------
const cpu_instruction_table cached_interpreter_table = {
LB,
LBU,
LH,
LHU,
LW,
LWL,
LWR,
SB,
SH,
SW,
SWL,
SWR,
LD,
LDL,
LDR,
LL,
LWU,
SC,
SD,
SDL,
SDR,
SYNC,
ADDI,
ADDIU,
SLTI,
SLTIU,
ANDI,
ORI,
XORI,
LUI,
DADDI,
DADDIU,
ADD,
ADDU,
SUB,
SUBU,
SLT,
SLTU,
AND,
OR,
XOR,
NOR,
DADD,
DADDU,
DSUB,
DSUBU,
MULT,
MULTU,
DIV,
DIVU,
MFHI,
MTHI,
MFLO,
MTLO,
DMULT,
DMULTU,
DDIV,
DDIVU,
J,
J_OUT,
J_IDLE,
JAL,
JAL_OUT,
JAL_IDLE,
// Use the _OUT versions of JR and JALR, since we don't know
// until runtime if they're going to jump inside or outside the block
JR_OUT,
JALR_OUT,
BEQ,
BEQ_OUT,
BEQ_IDLE,
BNE,
BNE_OUT,
BNE_IDLE,
BLEZ,
BLEZ_OUT,
BLEZ_IDLE,
BGTZ,
BGTZ_OUT,
BGTZ_IDLE,
BLTZ,
BLTZ_OUT,
BLTZ_IDLE,
BGEZ,
BGEZ_OUT,
BGEZ_IDLE,
BLTZAL,
BLTZAL_OUT,
BLTZAL_IDLE,
BGEZAL,
BGEZAL_OUT,
BGEZAL_IDLE,
BEQL,
BEQL_OUT,
BEQL_IDLE,
BNEL,
BNEL_OUT,
BNEL_IDLE,
BLEZL,
BLEZL_OUT,
BLEZL_IDLE,
BGTZL,
BGTZL_OUT,
BGTZL_IDLE,
BLTZL,
BLTZL_OUT,
BLTZL_IDLE,
BGEZL,
BGEZL_OUT,
BGEZL_IDLE,
BLTZALL,
BLTZALL_OUT,
BLTZALL_IDLE,
BGEZALL,
BGEZALL_OUT,
BGEZALL_IDLE,
BC1TL,
BC1TL_OUT,
BC1TL_IDLE,
BC1FL,
BC1FL_OUT,
BC1FL_IDLE,
SLL,
SRL,
SRA,
SLLV,
SRLV,
SRAV,
DSLL,
DSRL,
DSRA,
DSLLV,
DSRLV,
DSRAV,
DSLL32,
DSRL32,
DSRA32,
MTC0,
MFC0,
TLBR,
TLBWI,
TLBWR,
TLBP,
CACHE,
ERET,
LWC1,
SWC1,
MTC1,
MFC1,
CTC1,
CFC1,
BC1T,
BC1T_OUT,
BC1T_IDLE,
BC1F,
BC1F_OUT,
BC1F_IDLE,
DMFC1,
DMTC1,
LDC1,
SDC1,
CVT_S_D,
CVT_S_W,
CVT_S_L,
CVT_D_S,
CVT_D_W,
CVT_D_L,
CVT_W_S,
CVT_W_D,
CVT_L_S,
CVT_L_D,
ROUND_W_S,
ROUND_W_D,
ROUND_L_S,
ROUND_L_D,
TRUNC_W_S,
TRUNC_W_D,
TRUNC_L_S,
TRUNC_L_D,
CEIL_W_S,
CEIL_W_D,
CEIL_L_S,
CEIL_L_D,
FLOOR_W_S,
FLOOR_W_D,
FLOOR_L_S,
FLOOR_L_D,
ADD_S,
ADD_D,
SUB_S,
SUB_D,
MUL_S,
MUL_D,
DIV_S,
DIV_D,
ABS_S,
ABS_D,
MOV_S,
MOV_D,
NEG_S,
NEG_D,
SQRT_S,
SQRT_D,
C_F_S,
C_F_D,
C_UN_S,
C_UN_D,
C_EQ_S,
C_EQ_D,
C_UEQ_S,
C_UEQ_D,
C_OLT_S,
C_OLT_D,
C_ULT_S,
C_ULT_D,
C_OLE_S,
C_OLE_D,
C_ULE_S,
C_ULE_D,
C_SF_S,
C_SF_D,
C_NGLE_S,
C_NGLE_D,
C_SEQ_S,
C_SEQ_D,
C_NGL_S,
C_NGL_D,
C_LT_S,
C_LT_D,
C_NGE_S,
C_NGE_D,
C_LE_S,
C_LE_D,
C_NGT_S,
C_NGT_D,
SYSCALL,
BREAK,
TEQ,
NOP,
RESERVED,
NI,
FIN_BLOCK,
NOTCOMPILED,
NOTCOMPILED2
};
static unsigned int osal_fastcall update_invalid_addr(usf_state_t * state, unsigned int addr)
{
if (addr >= 0x80000000 && addr < 0xc0000000)
{
if (state->invalid_code[addr>>12]) state->invalid_code[(addr^0x20000000)>>12] = 1;
if (state->invalid_code[(addr^0x20000000)>>12]) state->invalid_code[addr>>12] = 1;
return addr;
}
else
{
unsigned int paddr = virtual_to_physical_address(state, addr, 2);
if (paddr)
{
unsigned int beg_paddr = paddr - (addr - (addr&~0xFFF));
update_invalid_addr(state, paddr);
if (state->invalid_code[(beg_paddr+0x000)>>12]) state->invalid_code[addr>>12] = 1;
if (state->invalid_code[(beg_paddr+0xFFC)>>12]) state->invalid_code[addr>>12] = 1;
if (state->invalid_code[addr>>12]) state->invalid_code[(beg_paddr+0x000)>>12] = 1;
if (state->invalid_code[addr>>12]) state->invalid_code[(beg_paddr+0xFFC)>>12] = 1;
}
return paddr;
}
}
#define addr state->jump_to_address
void osal_fastcall jump_to_func(usf_state_t * state)
{
unsigned int paddr;
if (state->skip_jump) return;
paddr = update_invalid_addr(state, addr);
if (!paddr) return;
state->actual = state->blocks[addr>>12];
if (state->invalid_code[addr>>12])
{
if (!state->blocks[addr>>12])
{
state->blocks[addr>>12] = (precomp_block *) malloc(sizeof(precomp_block));
state->actual = state->blocks[addr>>12];
state->blocks[addr>>12]->code = NULL;
state->blocks[addr>>12]->block = NULL;
state->blocks[addr>>12]->jumps_table = NULL;
state->blocks[addr>>12]->riprel_table = NULL;
}
state->blocks[addr>>12]->start = addr & ~0xFFF;
state->blocks[addr>>12]->end = (addr & ~0xFFF) + 0x1000;
init_block(state, state->blocks[addr>>12]);
}
state->PC=state->actual->block+((addr-state->actual->start)>>2);
#ifdef DYNAREC
if (state->r4300emu == CORE_DYNAREC) dyna_jump(state);
#endif
}
#undef addr
void osal_fastcall init_blocks(usf_state_t * state)
{
int i;
for (i=0; i<0x100000; i++)
{
state->invalid_code[i] = 1;
state->blocks[i] = NULL;
}
}
void osal_fastcall free_blocks(usf_state_t * state)
{
int i;
for (i=0; i<0x100000; i++)
{
if (state->blocks[i])
{
free_block(state, state->blocks[i]);
free(state->blocks[i]);
state->blocks[i] = NULL;
}
}
}