mupen64plus-core/src/device/r4300/cached_interp.c
Bobby Smiles efdeaf32c7 Fix interrupt spelling.
s/interupt/interrupt/
2017-03-07 06:17:04 +01:00

670 lines
15 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 <stdint.h>
#include <stdlib.h>
#define __STDC_FORMAT_MACROS
#include <inttypes.h>
#include <string.h>
#include "api/callbacks.h"
#include "api/debugger.h"
#include "api/m64p_types.h"
#include "device/memory/memory.h"
#include "device/r4300/cached_interp.h"
#include "device/r4300/exception.h"
#include "device/r4300/interrupt.h"
#include "device/r4300/macros.h"
#include "device/r4300/ops.h"
#include "device/r4300/recomp.h"
#include "device/r4300/tlb.h"
#include "main/main.h"
#ifdef DBG
#include "debugger/dbg_debugger.h"
#include "debugger/dbg_types.h"
#endif
// -----------------------------------------------------------
// Cached interpreter functions (and fallback for dynarec).
// -----------------------------------------------------------
#ifdef DBG
#define UPDATE_DEBUGGER() if (g_DebuggerActive) update_debugger(*r4300_pc())
#else
#define UPDATE_DEBUGGER() do { } while(0)
#endif
#define PCADDR *r4300_pc()
#define ADD_TO_PC(x) (*r4300_pc_struct()) += x;
#define DECLARE_INSTRUCTION(name) static void name(void)
#define DECLARE_JUMP(name, destination, condition, link, likely, cop1) \
static void name(void) \
{ \
const int take_jump = (condition); \
const uint32_t jump_target = (destination); \
int64_t *link_register = (link); \
if (cop1 && check_cop1_unusable()) return; \
if (link_register != &r4300_regs()[0]) \
{ \
*link_register = SE32(*r4300_pc() + 8); \
} \
if (!likely || take_jump) \
{ \
(*r4300_pc_struct())++; \
g_dev.r4300.delay_slot=1; \
UPDATE_DEBUGGER(); \
(*r4300_pc_struct())->ops(); \
cp0_update_count(); \
g_dev.r4300.delay_slot=0; \
if (take_jump && !g_dev.r4300.skip_jump) \
{ \
(*r4300_pc_struct())=g_dev.r4300.cached_interp.actual->block+((jump_target-g_dev.r4300.cached_interp.actual->start)>>2); \
} \
} \
else \
{ \
(*r4300_pc_struct()) += 2; \
cp0_update_count(); \
} \
g_dev.r4300.cp0.last_addr = *r4300_pc(); \
if (*r4300_cp0_next_interrupt() <= r4300_cp0_regs()[CP0_COUNT_REG]) gen_interrupt(); \
} \
static void name##_OUT(void) \
{ \
const int take_jump = (condition); \
const uint32_t jump_target = (destination); \
int64_t *link_register = (link); \
if (cop1 && check_cop1_unusable()) return; \
if (link_register != &r4300_regs()[0]) \
{ \
*link_register = SE32(*r4300_pc() + 8); \
} \
if (!likely || take_jump) \
{ \
(*r4300_pc_struct())++; \
g_dev.r4300.delay_slot=1; \
UPDATE_DEBUGGER(); \
(*r4300_pc_struct())->ops(); \
cp0_update_count(); \
g_dev.r4300.delay_slot=0; \
if (take_jump && !g_dev.r4300.skip_jump) \
{ \
cached_interpreter_dynarec_jump_to(&g_dev.r4300, jump_target); \
} \
} \
else \
{ \
(*r4300_pc_struct()) += 2; \
cp0_update_count(); \
} \
g_dev.r4300.cp0.last_addr = *r4300_pc(); \
if (*r4300_cp0_next_interrupt() <= r4300_cp0_regs()[CP0_COUNT_REG]) gen_interrupt(); \
} \
static void name##_IDLE(void) \
{ \
uint32_t* cp0_regs = r4300_cp0_regs(); \
const int take_jump = (condition); \
int skip; \
if (cop1 && check_cop1_unusable()) return; \
if (take_jump) \
{ \
cp0_update_count(); \
skip = *r4300_cp0_next_interrupt() - cp0_regs[CP0_COUNT_REG]; \
if (skip > 3) cp0_regs[CP0_COUNT_REG] += (skip & UINT32_C(0xFFFFFFFC)); \
else name(); \
} \
else name(); \
}
#define CHECK_MEMORY() \
if (!g_dev.r4300.cached_interp.invalid_code[*memory_address()>>12]) \
if (g_dev.r4300.cached_interp.blocks[*memory_address()>>12]->block[(*memory_address()&0xFFF)/4].ops != \
g_dev.r4300.current_instruction_table.NOTCOMPILED) \
g_dev.r4300.cached_interp.invalid_code[*memory_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 JR_IDLE(void) __attribute__((used));
static void JALR_IDLE(void) __attribute__((used));
#endif
#include "interpreter.def"
// -----------------------------------------------------------
// Flow control 'fake' instructions
// -----------------------------------------------------------
static void FIN_BLOCK(void)
{
if (!g_dev.r4300.delay_slot)
{
cached_interpreter_dynarec_jump_to(&g_dev.r4300, ((*r4300_pc_struct())-1)->addr+4);
/*#ifdef DBG
if (g_DebuggerActive) update_debugger(*r4300_pc());
#endif
Used by dynarec only, check should be unnecessary
*/
(*r4300_pc_struct())->ops();
if (g_dev.r4300.emumode == EMUMODE_DYNAREC) dyna_jump();
}
else
{
struct precomp_block *blk = g_dev.r4300.cached_interp.actual;
struct precomp_instr *inst = (*r4300_pc_struct());
cached_interpreter_dynarec_jump_to(&g_dev.r4300, ((*r4300_pc_struct())-1)->addr+4);
/*#ifdef DBG
if (g_DebuggerActive) update_debugger(*r4300_pc());
#endif
Used by dynarec only, check should be unnecessary
*/
if (!g_dev.r4300.skip_jump)
{
(*r4300_pc_struct())->ops();
g_dev.r4300.cached_interp.actual = blk;
(*r4300_pc_struct()) = inst+1;
}
else
(*r4300_pc_struct())->ops();
if (g_dev.r4300.emumode == EMUMODE_DYNAREC) dyna_jump();
}
}
static void NOTCOMPILED(void)
{
uint32_t *mem = fast_mem_access(g_dev.r4300.cached_interp.blocks[*r4300_pc()>>12]->start);
#ifdef DBG
DebugMessage(M64MSG_INFO, "NOTCOMPILED: addr = %x ops = %lx", *r4300_pc(), (long) (*r4300_pc_struct())->ops);
#endif
if (mem != NULL)
recompile_block(mem, g_dev.r4300.cached_interp.blocks[*r4300_pc() >> 12], *r4300_pc());
else
DebugMessage(M64MSG_ERROR, "not compiled exception");
/*#ifdef DBG
if (g_DebuggerActive) update_debugger(*r4300_pc());
#endif
The preceeding update_debugger SHOULD be unnecessary since it should have been
called before NOTCOMPILED would have been executed
*/
(*r4300_pc_struct())->ops();
if (g_dev.r4300.emumode == EMUMODE_DYNAREC)
dyna_jump();
}
static void NOTCOMPILED2(void)
{
NOTCOMPILED();
}
// -----------------------------------------------------------
// Cached interpreter instruction table
// -----------------------------------------------------------
const struct 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,
TEQ,
NOP,
RESERVED,
NI,
FIN_BLOCK,
NOTCOMPILED,
NOTCOMPILED2
};
static uint32_t update_invalid_addr(struct r4300_core* r4300, uint32_t addr)
{
if (addr >= 0x80000000 && addr < 0xc0000000)
{
if (r4300->cached_interp.invalid_code[addr>>12]) {
r4300->cached_interp.invalid_code[(addr^0x20000000)>>12] = 1;
}
if (r4300->cached_interp.invalid_code[(addr^0x20000000)>>12]) {
r4300->cached_interp.invalid_code[addr>>12] = 1;
}
return addr;
}
else
{
uint32_t paddr = virtual_to_physical_address(r4300, addr, 2);
if (paddr)
{
uint32_t beg_paddr = paddr - (addr - (addr & ~0xfff));
update_invalid_addr(r4300, paddr);
if (r4300->cached_interp.invalid_code[(beg_paddr+0x000)>>12]) {
r4300->cached_interp.invalid_code[addr>>12] = 1;
}
if (r4300->cached_interp.invalid_code[(beg_paddr+0xffc)>>12]) {
r4300->cached_interp.invalid_code[addr>>12] = 1;
}
if (r4300->cached_interp.invalid_code[addr>>12]) {
r4300->cached_interp.invalid_code[(beg_paddr+0x000)>>12] = 1;
}
if (r4300->cached_interp.invalid_code[addr>>12]) {
r4300->cached_interp.invalid_code[(beg_paddr+0xffc)>>12] = 1;
}
}
return paddr;
}
}
void cached_interpreter_dynarec_jump_to(struct r4300_core* r4300, uint32_t address)
{
struct cached_interp* const cinterp = &r4300->cached_interp;
struct precomp_block** b;
if (r4300->skip_jump) {
return;
}
if (!update_invalid_addr(r4300, address)) {
return;
}
b = &cinterp->blocks[address >> 12];
cinterp->actual = *b;
/* setup new block if invalid */
if (cinterp->invalid_code[address >> 12])
{
if (!*b)
{
*b = (struct precomp_block*)malloc(sizeof(struct precomp_block));
cinterp->actual = *b;
(*b)->code = NULL;
(*b)->block = NULL;
(*b)->jumps_table = NULL;
(*b)->riprel_table = NULL;
}
(*b)->start = (address & ~0xfff);
(*b)->end = (address & ~0xfff) + 0x1000;
init_block(*b);
}
/* set new PC */
(*r4300_pc_struct()) = cinterp->actual->block + ((address - cinterp->actual->start) >> 2);
/* set new PC for dynarec (eg set "return_address")*/
if (r4300->emumode == EMUMODE_DYNAREC) {
dyna_jump();
}
}
void init_blocks(struct cached_interp* cinterp)
{
size_t i;
for (i = 0; i < 0x100000; ++i)
{
cinterp->invalid_code[i] = 1;
cinterp->blocks[i] = NULL;
}
}
void free_blocks(struct cached_interp* cinterp)
{
size_t i;
for (i = 0; i < 0x100000; ++i)
{
if (cinterp->blocks[i])
{
free_block(cinterp->blocks[i]);
free(cinterp->blocks[i]);
cinterp->blocks[i] = NULL;
}
}
}
void invalidate_cached_code_hacktarux(struct r4300_core* r4300, uint32_t address, size_t size)
{
size_t i;
uint32_t addr;
uint32_t addr_max;
if (size == 0)
{
/* invalidate everthing */
memset(r4300->cached_interp.invalid_code, 1, 0x100000);
}
else
{
/* invalidate blocks (if necessary) */
addr_max = address+size;
for(addr = address; addr < addr_max; addr += 4)
{
i = (addr >> 12);
if (r4300->cached_interp.invalid_code[i] == 0)
{
if (r4300->cached_interp.blocks[i] == NULL
|| r4300->cached_interp.blocks[i]->block[(addr & 0xfff) / 4].ops != r4300->current_instruction_table.NOTCOMPILED)
{
r4300->cached_interp.invalid_code[i] = 1;
/* go directly to next i */
addr &= ~0xfff;
addr |= 0xffc;
}
}
else
{
/* go directly to next i */
addr &= ~0xfff;
addr |= 0xffc;
}
}
}
}
void run_cached_interpreter(struct r4300_core* r4300)
{
while (!*r4300_stop())
{
#ifdef COMPARE_CORE
if ((*r4300_pc_struct())->ops == cached_interpreter_table.FIN_BLOCK && ((*r4300_pc_struct())->addr < 0x80000000 || (*r4300_pc_struct())->addr >= 0xc0000000))
virtual_to_physical_address(r4300, (*r4300_pc_struct())->addr, 2);
CoreCompareCallback();
#endif
#ifdef DBG
if (g_DebuggerActive) update_debugger((*r4300_pc_struct())->addr);
#endif
(*r4300_pc_struct())->ops();
}
}