mame/src/devices/cpu/rsp/rspdrc.cpp

1293 lines
41 KiB
C++

// license:BSD-3-Clause
// copyright-holders:Ryan Holtz
/***************************************************************************
rspdrc.c
Universal machine language-based Nintendo/SGI RSP emulator.
Written by Ryan Holtz
SIMD versions of vector multiplication opcodes provided by Marathon Man
of the CEN64 team.
****************************************************************************
Future improvements/changes:
* Confer with Aaron Giles about adding a memory hash-based caching
system and static recompilation for maximum overhead minimization
***************************************************************************/
#include "emu.h"
#include "debugger.h"
#include "rsp.h"
#include "rspfe.h"
#include "rspcp2.h"
#include "cpu/drcfe.h"
#include "cpu/drcuml.h"
#include "cpu/drcumlsh.h"
using namespace uml;
CPU_DISASSEMBLE( rsp );
extern offs_t rsp_dasm_one(char *buffer, offs_t pc, uint32_t op);
/***************************************************************************
CONSTANTS
***************************************************************************/
/* map variables */
#define MAPVAR_PC M0
#define MAPVAR_CYCLES M1
/* exit codes */
#define EXECUTE_OUT_OF_CYCLES 0
#define EXECUTE_MISSING_CODE 1
#define EXECUTE_UNMAPPED_CODE 2
#define EXECUTE_RESET_CACHE 3
/***************************************************************************
Macros
***************************************************************************/
#define R32(reg) m_regmap[reg]
/***************************************************************************
Inline Functions
***************************************************************************/
/*-------------------------------------------------
epc - compute the exception PC from a
descriptor
-------------------------------------------------*/
static inline uint32_t epc(const opcode_desc *desc)
{
return ((desc->flags & OPFLAG_IN_DELAY_SLOT) ? (desc->pc - 3) : desc->pc) | 0x1000;
}
/*-------------------------------------------------
alloc_handle - allocate a handle if not
already allocated
-------------------------------------------------*/
static inline void alloc_handle(drcuml_state *drcuml, code_handle **handleptr, const char *name)
{
if (*handleptr == nullptr)
*handleptr = drcuml->handle_alloc(name);
}
/*-------------------------------------------------
load_fast_iregs - load any fast integer
registers
-------------------------------------------------*/
inline void rsp_device::load_fast_iregs(drcuml_block *block)
{
int regnum;
for (regnum = 0; regnum < ARRAY_LENGTH(m_regmap); regnum++)
if (m_regmap[regnum].is_int_register())
UML_MOV(block, ireg(m_regmap[regnum].ireg() - REG_I0), mem(&m_rsp_state->r[regnum]));
}
/*-------------------------------------------------
save_fast_iregs - save any fast integer
registers
-------------------------------------------------*/
inline void rsp_device::save_fast_iregs(drcuml_block *block)
{
int regnum;
for (regnum = 0; regnum < ARRAY_LENGTH(m_regmap); regnum++)
if (m_regmap[regnum].is_int_register())
UML_MOV(block, mem(&m_rsp_state->r[regnum]), ireg(m_regmap[regnum].ireg() - REG_I0));
}
/***************************************************************************
CORE CALLBACKS
***************************************************************************/
inline void rsp_device::ccfunc_read8()
{
m_rsp_state->arg0 = DM_READ8(m_rsp_state->arg0);
}
static void cfunc_read8(void *param)
{
((rsp_device *)param)->ccfunc_read8();
}
inline void rsp_device::ccfunc_read16()
{
m_rsp_state->arg0 = DM_READ16(m_rsp_state->arg0);
}
static void cfunc_read16(void *param)
{
((rsp_device *)param)->ccfunc_read16();
}
inline void rsp_device::ccfunc_read32()
{
m_rsp_state->arg0 = DM_READ32(m_rsp_state->arg0);
}
static void cfunc_read32(void *param)
{
((rsp_device *)param)->ccfunc_read32();;
}
inline void rsp_device::ccfunc_write8()
{
DM_WRITE8(m_rsp_state->arg0, m_rsp_state->arg1);
}
static void cfunc_write8(void *param)
{
((rsp_device *)param)->ccfunc_write8();;
}
inline void rsp_device::ccfunc_write16()
{
DM_WRITE16(m_rsp_state->arg0, m_rsp_state->arg1);
}
static void cfunc_write16(void *param)
{
((rsp_device *)param)->ccfunc_write16();;
}
inline void rsp_device::ccfunc_write32()
{
DM_WRITE32(m_rsp_state->arg0, m_rsp_state->arg1);
}
static void cfunc_write32(void *param)
{
((rsp_device *)param)->ccfunc_write32();;
}
/*****************************************************************************/
/*-------------------------------------------------
rspdrc_set_options - configure DRC options
-------------------------------------------------*/
void rsp_device::rspdrc_set_options(uint32_t options)
{
if (!allow_drc()) return;
m_drcoptions = options;
}
inline void rsp_device::ccfunc_get_cop0_reg()
{
int reg = m_rsp_state->arg0;
int dest = m_rsp_state->arg1;
if (reg >= 0 && reg < 8)
{
if(dest)
{
m_rsp_state->r[dest] = m_sp_reg_r_func(reg, 0xffffffff);
}
}
else if (reg >= 8 && reg < 16)
{
if(dest)
{
m_rsp_state->r[dest] = m_dp_reg_r_func(reg - 8, 0xffffffff);
}
}
else
{
fatalerror("RSP: cfunc_get_cop0_reg: %d\n", reg);
}
}
static void cfunc_get_cop0_reg(void *param)
{
((rsp_device *)param)->ccfunc_get_cop0_reg();
}
inline void rsp_device::ccfunc_set_cop0_reg()
{
int reg = m_rsp_state->arg0;
uint32_t data = m_rsp_state->arg1;
if (reg >= 0 && reg < 8)
{
m_sp_reg_w_func(reg, data, 0xffffffff);
}
else if (reg >= 8 && reg < 16)
{
m_dp_reg_w_func(reg - 8, data, 0xffffffff);
}
else
{
fatalerror("RSP: set_cop0_reg: %d, %08X\n", reg, data);
}
}
static void cfunc_set_cop0_reg(void *param)
{
((rsp_device *)param)->ccfunc_set_cop0_reg();
}
/*****************************************************************************/
void rsp_device::rspcom_init()
{
}
inline void rsp_device::ccfunc_sp_set_status_cb()
{
m_sp_set_status_func(0, m_rsp_state->arg0, 0xffffffff);
}
void cfunc_sp_set_status_cb(void *param)
{
((rsp_device *)param)->ccfunc_sp_set_status_cb();
}
void rsp_device::execute_run_drc()
{
drcuml_state *drcuml = m_drcuml.get();
int execute_result;
/* reset the cache if dirty */
if (m_cache_dirty)
code_flush_cache();
m_cache_dirty = false;
/* execute */
do
{
if( m_sr & ( RSP_STATUS_HALT | RSP_STATUS_BROKE ) )
{
m_rsp_state->icount = std::min(m_rsp_state->icount, 0);
break;
}
/* run as much as we can */
execute_result = drcuml->execute(*m_entry);
/* if we need to recompile, do it */
if (execute_result == EXECUTE_MISSING_CODE)
{
code_compile_block(m_rsp_state->pc);
}
else if (execute_result == EXECUTE_UNMAPPED_CODE)
{
fatalerror("Attempted to execute unmapped code at PC=%08X\n", m_rsp_state->pc);
}
else if (execute_result == EXECUTE_RESET_CACHE)
{
code_flush_cache();
}
} while (execute_result != EXECUTE_OUT_OF_CYCLES);
}
/***************************************************************************
CACHE MANAGEMENT
***************************************************************************/
/*-------------------------------------------------
rspdrc_flush_drc_cache - outward-facing
accessor to code_flush_cache
-------------------------------------------------*/
void rsp_device::rspdrc_flush_drc_cache()
{
if (!allow_drc()) return;
m_cache_dirty = true;
}
/*-------------------------------------------------
code_flush_cache - flush the cache and
regenerate static code
-------------------------------------------------*/
void rsp_device::code_flush_cache()
{
/* empty the transient cache contents */
m_drcuml->reset();
try
{
/* generate the entry point and out-of-cycles handlers */
static_generate_entry_point();
static_generate_nocode_handler();
static_generate_out_of_cycles();
/* add subroutines for memory accesses */
static_generate_memory_accessor(1, false, "read8", m_read8);
static_generate_memory_accessor(1, true, "write8", m_write8);
static_generate_memory_accessor(2, false, "read16", m_read16);
static_generate_memory_accessor(2, true, "write16", m_write16);
static_generate_memory_accessor(4, false, "read32", m_read32);
static_generate_memory_accessor(4, true, "write32", m_write32);
}
catch (drcuml_block::abort_compilation &)
{
fatalerror("Unable to generate static RSP code\n");
}
}
/*-------------------------------------------------
code_compile_block - compile a block of the
given mode at the specified pc
-------------------------------------------------*/
void rsp_device::code_compile_block(offs_t pc)
{
drcuml_state *drcuml = m_drcuml.get();
compiler_state compiler = { 0 };
const opcode_desc *seqhead, *seqlast;
const opcode_desc *desclist;
int override = false;
drcuml_block *block;
g_profiler.start(PROFILER_DRC_COMPILE);
/* get a description of this sequence */
desclist = m_drcfe->describe_code(pc);
bool succeeded = false;
while (!succeeded)
{
try
{
/* start the block */
block = drcuml->begin_block(4096);
/* loop until we get through all instruction sequences */
for (seqhead = desclist; seqhead != nullptr; seqhead = seqlast->next())
{
const opcode_desc *curdesc;
uint32_t nextpc;
/* add a code log entry */
if (drcuml->logging())
block->append_comment("-------------------------"); // comment
/* determine the last instruction in this sequence */
for (seqlast = seqhead; seqlast != nullptr; seqlast = seqlast->next())
if (seqlast->flags & OPFLAG_END_SEQUENCE)
break;
assert(seqlast != nullptr);
/* if we don't have a hash for this mode/pc, or if we are overriding all, add one */
if (override || !drcuml->hash_exists(0, seqhead->pc))
UML_HASH(block, 0, seqhead->pc); // hash mode,pc
/* if we already have a hash, and this is the first sequence, assume that we */
/* are recompiling due to being out of sync and allow future overrides */
else if (seqhead == desclist)
{
override = true;
UML_HASH(block, 0, seqhead->pc); // hash mode,pc
}
/* otherwise, redispatch to that fixed PC and skip the rest of the processing */
else
{
UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc
UML_HASHJMP(block, 0, seqhead->pc, *m_nocode);
// hashjmp <0>,seqhead->pc,nocode
continue;
}
/* validate this code block if we're not pointing into ROM */
if (m_program->get_write_ptr(seqhead->physpc) != nullptr)
generate_checksum_block(block, &compiler, seqhead, seqlast);
/* label this instruction, if it may be jumped to locally */
if (seqhead->flags & OPFLAG_IS_BRANCH_TARGET)
UML_LABEL(block, seqhead->pc | 0x80000000); // label seqhead->pc
/* iterate over instructions in the sequence and compile them */
for (curdesc = seqhead; curdesc != seqlast->next(); curdesc = curdesc->next())
generate_sequence_instruction(block, &compiler, curdesc);
/* if we need to return to the start, do it */
if (seqlast->flags & OPFLAG_RETURN_TO_START)
nextpc = pc;
/* otherwise we just go to the next instruction */
else
nextpc = seqlast->pc + (seqlast->skipslots + 1) * 4;
/* count off cycles and go there */
generate_update_cycles(block, &compiler, nextpc, true); // <subtract cycles>
/* if the last instruction can change modes, use a variable mode; otherwise, assume the same mode */
if (seqlast->next() == nullptr || seqlast->next()->pc != nextpc)
UML_HASHJMP(block, 0, nextpc, *m_nocode); // hashjmp <mode>,nextpc,nocode
}
/* end the sequence */
block->end();
g_profiler.stop();
succeeded = true;
}
catch (drcuml_block::abort_compilation &)
{
code_flush_cache();
}
}
}
/***************************************************************************
C FUNCTION CALLBACKS
***************************************************************************/
/*-------------------------------------------------
cfunc_unimplemented - handler for
unimplemented opcdes
-------------------------------------------------*/
inline void rsp_device::ccfunc_unimplemented()
{
uint32_t opcode = m_rsp_state->arg0;
fatalerror("PC=%08X: Unimplemented op %08X (%02X,%02X)\n", m_rsp_state->pc, opcode, opcode >> 26, opcode & 0x3f);
}
static void cfunc_unimplemented(void *param)
{
((rsp_device *)param)->ccfunc_unimplemented();
}
/*-------------------------------------------------
cfunc_fatalerror - a generic fatalerror call
-------------------------------------------------*/
#ifdef UNUSED_CODE
static void cfunc_fatalerror(void *param)
{
fatalerror("fatalerror\n");
}
#endif
/***************************************************************************
STATIC CODEGEN
***************************************************************************/
/*-------------------------------------------------
ferate_entry_point - generate a
static entry point
-------------------------------------------------*/
void rsp_device::static_generate_entry_point()
{
drcuml_state *drcuml = m_drcuml.get();
drcuml_block *block;
/* begin generating */
block = drcuml->begin_block(20);
/* forward references */
alloc_handle(drcuml, &m_nocode, "nocode");
alloc_handle(drcuml, &m_entry, "entry");
UML_HANDLE(block, *m_entry); // handle entry
/* load fast integer registers */
load_fast_iregs(block);
/* generate a hash jump via the current mode and PC */
UML_HASHJMP(block, 0, mem(&m_rsp_state->pc), *m_nocode); // hashjmp <mode>,<pc>,nocode
block->end();
}
/*-------------------------------------------------
static_generate_nocode_handler - generate an
exception handler for "out of code"
-------------------------------------------------*/
void rsp_device::static_generate_nocode_handler()
{
drcuml_state *drcuml = m_drcuml.get();
drcuml_block *block;
/* begin generating */
block = drcuml->begin_block(10);
/* generate a hash jump via the current mode and PC */
alloc_handle(drcuml, &m_nocode, "nocode");
UML_HANDLE(block, *m_nocode); // handle nocode
UML_GETEXP(block, I0); // getexp i0
UML_MOV(block, mem(&m_rsp_state->pc), I0); // mov [pc],i0
save_fast_iregs(block);
UML_EXIT(block, EXECUTE_MISSING_CODE); // exit EXECUTE_MISSING_CODE
block->end();
}
/*-------------------------------------------------
static_generate_out_of_cycles - generate an
out of cycles exception handler
-------------------------------------------------*/
void rsp_device::static_generate_out_of_cycles()
{
drcuml_state *drcuml = m_drcuml.get();
drcuml_block *block;
/* begin generating */
block = drcuml->begin_block(10);
/* generate a hash jump via the current mode and PC */
alloc_handle(drcuml, &m_out_of_cycles, "out_of_cycles");
UML_HANDLE(block, *m_out_of_cycles); // handle out_of_cycles
UML_GETEXP(block, I0); // getexp i0
UML_MOV(block, mem(&m_rsp_state->pc), I0); // mov <pc>,i0
save_fast_iregs(block);
UML_EXIT(block, EXECUTE_OUT_OF_CYCLES); // exit EXECUTE_OUT_OF_CYCLES
block->end();
}
/*------------------------------------------------------------------
static_generate_memory_accessor
------------------------------------------------------------------*/
void rsp_device::static_generate_memory_accessor(int size, int iswrite, const char *name, code_handle *&handleptr)
{
/* on entry, address is in I0; data for writes is in I1 */
/* on exit, read result is in I0 */
/* routine trashes I0-I1 */
drcuml_state *drcuml = m_drcuml.get();
drcuml_block *block;
/* begin generating */
block = drcuml->begin_block(1024);
/* add a global entry for this */
alloc_handle(drcuml, &handleptr, name);
UML_HANDLE(block, *handleptr); // handle *handleptr
// write:
if (iswrite)
{
if (size == 1)
{
UML_MOV(block, mem(&m_rsp_state->arg0), I0); // mov [arg0],i0 ; address
UML_MOV(block, mem(&m_rsp_state->arg1), I1); // mov [arg1],i1 ; data
UML_CALLC(block, cfunc_write8, this); // callc cfunc_write8
}
else if (size == 2)
{
UML_MOV(block, mem(&m_rsp_state->arg0), I0); // mov [arg0],i0 ; address
UML_MOV(block, mem(&m_rsp_state->arg1), I1); // mov [arg1],i1 ; data
UML_CALLC(block, cfunc_write16, this); // callc cfunc_write16
}
else if (size == 4)
{
UML_MOV(block, mem(&m_rsp_state->arg0), I0); // mov [arg0],i0 ; address
UML_MOV(block, mem(&m_rsp_state->arg1), I1); // mov [arg1],i1 ; data
UML_CALLC(block, cfunc_write32, this); // callc cfunc_write32
}
}
else
{
if (size == 1)
{
UML_MOV(block, mem(&m_rsp_state->arg0), I0); // mov [arg0],i0 ; address
UML_CALLC(block, cfunc_read8, this); // callc read8
UML_MOV(block, I0, mem(&m_rsp_state->arg0)); // mov i0,[arg0],i0 ; result
}
else if (size == 2)
{
UML_MOV(block, mem(&m_rsp_state->arg0), I0); // mov [arg0],i0 ; address
UML_CALLC(block, cfunc_read16, this); // callc cfunc_read16
UML_MOV(block, I0, mem(&m_rsp_state->arg0)); // mov i0,[arg0],i0 ; result
}
else if (size == 4)
{
UML_MOV(block, mem(&m_rsp_state->arg0), I0); // mov [arg0],i0 ; address
UML_CALLC(block, cfunc_read32, this); // callc cfunc_read32
UML_MOV(block, I0, mem(&m_rsp_state->arg0)); // mov i0,[arg0],i0 ; result
}
}
UML_RET(block);
block->end();
}
/***************************************************************************
CODE GENERATION
***************************************************************************/
/*-------------------------------------------------
generate_update_cycles - generate code to
subtract cycles from the icount and generate
an exception if out
-------------------------------------------------*/
void rsp_device::generate_update_cycles(drcuml_block *block, compiler_state *compiler, uml::parameter param, bool allow_exception)
{
/* account for cycles */
if (compiler->cycles > 0)
{
UML_SUB(block, mem(&m_rsp_state->icount), mem(&m_rsp_state->icount), MAPVAR_CYCLES); // sub icount,icount,cycles
UML_MAPVAR(block, MAPVAR_CYCLES, 0); // mapvar cycles,0
UML_EXHc(block, COND_S, *m_out_of_cycles, param);
}
compiler->cycles = 0;
}
/*-------------------------------------------------
generate_checksum_block - generate code to
validate a sequence of opcodes
-------------------------------------------------*/
void rsp_device::generate_checksum_block(drcuml_block *block, compiler_state *compiler, const opcode_desc *seqhead, const opcode_desc *seqlast)
{
const opcode_desc *curdesc;
if (m_drcuml->logging())
{
block->append_comment("[Validation for %08X]", seqhead->pc | 0x1000); // comment
}
/* loose verify or single instruction: just compare and fail */
if (!(m_drcoptions & RSPDRC_STRICT_VERIFY) || seqhead->next() == nullptr)
{
if (!(seqhead->flags & OPFLAG_VIRTUAL_NOOP))
{
uint32_t sum = seqhead->opptr.l[0];
void *base = m_direct->read_ptr(seqhead->physpc | 0x1000);
UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword
if (seqhead->delay.first() != nullptr && seqhead->physpc != seqhead->delay.first()->physpc)
{
base = m_direct->read_ptr(seqhead->delay.first()->physpc | 0x1000);
assert(base != nullptr);
UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword
UML_ADD(block, I0, I0, I1); // add i0,i0,i1
sum += seqhead->delay.first()->opptr.l[0];
}
UML_CMP(block, I0, sum); // cmp i0,opptr[0]
UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc
}
}
/* full verification; sum up everything */
else
{
uint32_t sum = 0;
void *base = m_direct->read_ptr(seqhead->physpc | 0x1000);
UML_LOAD(block, I0, base, 0, SIZE_DWORD, SCALE_x4); // load i0,base,0,dword
sum += seqhead->opptr.l[0];
for (curdesc = seqhead->next(); curdesc != seqlast->next(); curdesc = curdesc->next())
if (!(curdesc->flags & OPFLAG_VIRTUAL_NOOP))
{
base = m_direct->read_ptr(curdesc->physpc | 0x1000);
assert(base != nullptr);
UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword
UML_ADD(block, I0, I0, I1); // add i0,i0,i1
sum += curdesc->opptr.l[0];
if (curdesc->delay.first() != nullptr && (curdesc == seqlast || (curdesc->next() != nullptr && curdesc->next()->physpc != curdesc->delay.first()->physpc)))
{
base = m_direct->read_ptr(curdesc->delay.first()->physpc | 0x1000);
assert(base != nullptr);
UML_LOAD(block, I1, base, 0, SIZE_DWORD, SCALE_x4); // load i1,base,dword
UML_ADD(block, I0, I0, I1); // add i0,i0,i1
sum += curdesc->delay.first()->opptr.l[0];
}
}
UML_CMP(block, I0, sum); // cmp i0,sum
UML_EXHc(block, COND_NE, *m_nocode, epc(seqhead)); // exne nocode,seqhead->pc
}
}
/*-------------------------------------------------
generate_sequence_instruction - generate code
for a single instruction in a sequence
-------------------------------------------------*/
void rsp_device::generate_sequence_instruction(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc)
{
offs_t expc;
/* add an entry for the log */
if (m_drcuml->logging() && !(desc->flags & OPFLAG_VIRTUAL_NOOP))
log_add_disasm_comment(block, desc->pc, desc->opptr.l[0]);
/* set the PC map variable */
expc = (desc->flags & OPFLAG_IN_DELAY_SLOT) ? desc->pc - 3 : desc->pc;
UML_MAPVAR(block, MAPVAR_PC, expc); // mapvar PC,expc
/* accumulate total cycles */
compiler->cycles += desc->cycles;
/* update the icount map variable */
UML_MAPVAR(block, MAPVAR_CYCLES, compiler->cycles); // mapvar CYCLES,compiler->cycles
/* if we are debugging, call the debugger */
if ((machine().debug_flags & DEBUG_FLAG_ENABLED) != 0)
{
UML_MOV(block, mem(&m_rsp_state->pc), desc->pc); // mov [pc],desc->pc
save_fast_iregs(block);
UML_DEBUG(block, desc->pc); // debug desc->pc
}
/* if we hit an unmapped address, fatal error */
#if 0
if (desc->flags & OPFLAG_COMPILER_UNMAPPED)
{
UML_MOV(block, mem(&m_rsp_state->pc), desc->pc); // mov [pc],desc->pc
save_fast_iregs(block);
UML_EXIT(block, EXECUTE_UNMAPPED_CODE); // exit EXECUTE_UNMAPPED_CODE
}
#endif
/* otherwise, unless this is a virtual no-op, it's a regular instruction */
/*else*/ if (!(desc->flags & OPFLAG_VIRTUAL_NOOP))
{
/* compile the instruction */
if (!generate_opcode(block, compiler, desc))
{
UML_MOV(block, mem(&m_rsp_state->pc), desc->pc); // mov [pc],desc->pc
UML_MOV(block, mem(&m_rsp_state->arg0), desc->opptr.l[0]); // mov [arg0],desc->opptr.l
UML_CALLC(block, cfunc_unimplemented, this); // callc cfunc_unimplemented
}
}
}
/*------------------------------------------------------------------
generate_branch
------------------------------------------------------------------*/
void rsp_device::generate_branch(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc)
{
compiler_state compiler_temp = *compiler;
/* update the cycles and jump through the hash table to the target */
if (desc->targetpc != BRANCH_TARGET_DYNAMIC)
{
generate_update_cycles(block, &compiler_temp, desc->targetpc, true); // <subtract cycles>
if (desc->flags & OPFLAG_INTRABLOCK_BRANCH)
UML_JMP(block, desc->targetpc | 0x80000000); // jmp desc->targetpc
else
UML_HASHJMP(block, 0, desc->targetpc, *m_nocode); // hashjmp <mode>,desc->targetpc,nocode
}
else
{
generate_update_cycles(block, &compiler_temp, mem(&m_rsp_state->jmpdest), true); // <subtract cycles>
UML_HASHJMP(block, 0, mem(&m_rsp_state->jmpdest), *m_nocode); // hashjmp <mode>,<rsreg>,nocode
}
}
/*------------------------------------------------------------------
generate_delay_slot_and_branch
------------------------------------------------------------------*/
void rsp_device::generate_delay_slot_and_branch(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc, uint8_t linkreg)
{
compiler_state compiler_temp = *compiler;
uint32_t op = desc->opptr.l[0];
/* fetch the target register if dynamic, in case it is modified by the delay slot */
if (desc->targetpc == BRANCH_TARGET_DYNAMIC)
{
UML_AND(block, mem(&m_rsp_state->jmpdest), R32(RSREG), 0x00000fff);
UML_OR(block, mem(&m_rsp_state->jmpdest), mem(&m_rsp_state->jmpdest), 0x1000);
}
/* set the link if needed -- before the delay slot */
if (linkreg != 0)
{
UML_MOV(block, R32(linkreg), (int32_t)(desc->pc + 8)); // mov <linkreg>,desc->pc + 8
}
/* compile the delay slot using temporary compiler state */
assert(desc->delay.first() != nullptr);
generate_sequence_instruction(block, &compiler_temp, desc->delay.first()); // <next instruction>
generate_branch(block, compiler, desc);
/* update the label */
compiler->labelnum = compiler_temp.labelnum;
/* reset the mapvar to the current cycles and account for skipped slots */
compiler->cycles += desc->skipslots;
UML_MAPVAR(block, MAPVAR_CYCLES, compiler->cycles); // mapvar CYCLES,compiler->cycles
}
bool rsp_device::generate_opcode(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc)
{
int in_delay_slot = ((desc->flags & OPFLAG_IN_DELAY_SLOT) != 0);
uint32_t op = desc->opptr.l[0];
uint8_t opswitch = op >> 26;
code_label skip;
switch (opswitch)
{
/* ----- sub-groups ----- */
case 0x00: /* SPECIAL - MIPS I */
return generate_special(block, compiler, desc);
case 0x01: /* REGIMM - MIPS I */
return generate_regimm(block, compiler, desc);
/* ----- jumps and branches ----- */
case 0x02: /* J - MIPS I */
generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp>
return true;
case 0x03: /* JAL - MIPS I */
generate_delay_slot_and_branch(block, compiler, desc, 31); // <next instruction + hashjmp>
return true;
case 0x04: /* BEQ - MIPS I */
UML_CMP(block, R32(RSREG), R32(RTREG)); // cmp <rsreg>,<rtreg>
UML_JMPc(block, COND_NE, skip = compiler->labelnum++); // jmp skip,NE
generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp>
UML_LABEL(block, skip); // skip:
return true;
case 0x05: /* BNE - MIPS I */
UML_CMP(block, R32(RSREG), R32(RTREG)); // dcmp <rsreg>,<rtreg>
UML_JMPc(block, COND_E, skip = compiler->labelnum++); // jmp skip,E
generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp>
UML_LABEL(block, skip); // skip:
return true;
case 0x06: /* BLEZ - MIPS I */
if (RSREG != 0)
{
UML_CMP(block, R32(RSREG), 0); // dcmp <rsreg>,0
UML_JMPc(block, COND_G, skip = compiler->labelnum++); // jmp skip,G
generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp>
UML_LABEL(block, skip); // skip:
}
else
generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp>
return true;
case 0x07: /* BGTZ - MIPS I */
UML_CMP(block, R32(RSREG), 0); // dcmp <rsreg>,0
UML_JMPc(block, COND_LE, skip = compiler->labelnum++); // jmp skip,LE
generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp>
UML_LABEL(block, skip); // skip:
return true;
/* ----- immediate arithmetic ----- */
case 0x0f: /* LUI - MIPS I */
if (RTREG != 0)
UML_MOV(block, R32(RTREG), SIMMVAL << 16); // dmov <rtreg>,SIMMVAL << 16
return true;
case 0x08: /* ADDI - MIPS I */
case 0x09: /* ADDIU - MIPS I */
if (RTREG != 0)
{
UML_ADD(block, R32(RTREG), R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL,V
}
return true;
case 0x0a: /* SLTI - MIPS I */
if (RTREG != 0)
{
UML_CMP(block, R32(RSREG), SIMMVAL); // dcmp <rsreg>,SIMMVAL
UML_SETc(block, COND_L, R32(RTREG)); // dset <rtreg>,l
}
return true;
case 0x0b: /* SLTIU - MIPS I */
if (RTREG != 0)
{
UML_CMP(block, R32(RSREG), SIMMVAL); // dcmp <rsreg>,SIMMVAL
UML_SETc(block, COND_B, R32(RTREG)); // dset <rtreg>,b
}
return true;
case 0x0c: /* ANDI - MIPS I */
if (RTREG != 0)
UML_AND(block, R32(RTREG), R32(RSREG), UIMMVAL); // dand <rtreg>,<rsreg>,UIMMVAL
return true;
case 0x0d: /* ORI - MIPS I */
if (RTREG != 0)
UML_OR(block, R32(RTREG), R32(RSREG), UIMMVAL); // dor <rtreg>,<rsreg>,UIMMVAL
return true;
case 0x0e: /* XORI - MIPS I */
if (RTREG != 0)
UML_XOR(block, R32(RTREG), R32(RSREG), UIMMVAL); // dxor <rtreg>,<rsreg>,UIMMVAL
return true;
/* ----- memory load operations ----- */
case 0x20: /* LB - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_CALLH(block, *m_read8); // callh read8
if (RTREG != 0)
UML_SEXT(block, R32(RTREG), I0, SIZE_BYTE); // dsext <rtreg>,i0,byte
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x21: /* LH - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_CALLH(block, *m_read16); // callh read16
if (RTREG != 0)
UML_SEXT(block, R32(RTREG), I0, SIZE_WORD); // dsext <rtreg>,i0,word
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x23: /* LW - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_CALLH(block, *m_read32); // callh read32
if (RTREG != 0)
UML_MOV(block, R32(RTREG), I0);
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x24: /* LBU - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_CALLH(block, *m_read8); // callh read8
if (RTREG != 0)
UML_AND(block, R32(RTREG), I0, 0xff); // dand <rtreg>,i0,0xff
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x25: /* LHU - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_CALLH(block, *m_read16); // callh read16
if (RTREG != 0)
UML_AND(block, R32(RTREG), I0, 0xffff); // dand <rtreg>,i0,0xffff
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x32: /* LWC2 - MIPS I */
return m_cop2->generate_lwc2(block, compiler, desc);
/* ----- memory store operations ----- */
case 0x28: /* SB - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_MOV(block, I1, R32(RTREG)); // mov i1,<rtreg>
UML_CALLH(block, *m_write8); // callh write8
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x29: /* SH - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_MOV(block, I1, R32(RTREG)); // mov i1,<rtreg>
UML_CALLH(block, *m_write16); // callh write16
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x2b: /* SW - MIPS I */
UML_ADD(block, I0, R32(RSREG), SIMMVAL); // add i0,<rsreg>,SIMMVAL
UML_MOV(block, I1, R32(RTREG)); // mov i1,<rtreg>
UML_CALLH(block, *m_write32); // callh write32
if (!in_delay_slot)
generate_update_cycles(block, compiler, desc->pc + 4, true);
return true;
case 0x3a: /* SWC2 - MIPS I */
return m_cop2->generate_swc2(block, compiler, desc);
/* ----- coprocessor instructions ----- */
case 0x10: /* COP0 - MIPS I */
return generate_cop0(block, compiler, desc);
case 0x12: /* COP2 - MIPS I */
return m_cop2->generate_cop2(block, compiler, desc);
/* ----- unimplemented/illegal instructions ----- */
//default: /* ??? */ invalid_instruction(op); break;
}
return false;
}
/*-------------------------------------------------
generate_special - compile opcodes in the
'SPECIAL' group
-------------------------------------------------*/
bool rsp_device::generate_special(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc)
{
uint32_t op = desc->opptr.l[0];
uint8_t opswitch = op & 63;
//code_label skip;
switch (opswitch)
{
/* ----- shift instructions ----- */
case 0x00: /* SLL - MIPS I */
if (RDREG != 0)
{
UML_SHL(block, R32(RDREG), R32(RTREG), SHIFT);
}
return true;
case 0x02: /* SRL - MIPS I */
if (RDREG != 0)
{
UML_SHR(block, R32(RDREG), R32(RTREG), SHIFT);
}
return true;
case 0x03: /* SRA - MIPS I */
if (RDREG != 0)
{
UML_SAR(block, R32(RDREG), R32(RTREG), SHIFT);
}
return true;
case 0x04: /* SLLV - MIPS I */
if (RDREG != 0)
{
UML_SHL(block, R32(RDREG), R32(RTREG), R32(RSREG));
}
return true;
case 0x06: /* SRLV - MIPS I */
if (RDREG != 0)
{
UML_SHR(block, R32(RDREG), R32(RTREG), R32(RSREG));
}
return true;
case 0x07: /* SRAV - MIPS I */
if (RDREG != 0)
{
UML_SAR(block, R32(RDREG), R32(RTREG), R32(RSREG));
}
return true;
/* ----- basic arithmetic ----- */
case 0x20: /* ADD - MIPS I */
case 0x21: /* ADDU - MIPS I */
if (RDREG != 0)
{
UML_ADD(block, R32(RDREG), R32(RSREG), R32(RTREG));
}
return true;
case 0x22: /* SUB - MIPS I */
case 0x23: /* SUBU - MIPS I */
if (RDREG != 0)
{
UML_SUB(block, R32(RDREG), R32(RSREG), R32(RTREG));
}
return true;
/* ----- basic logical ops ----- */
case 0x24: /* AND - MIPS I */
if (RDREG != 0)
{
UML_AND(block, R32(RDREG), R32(RSREG), R32(RTREG)); // dand <rdreg>,<rsreg>,<rtreg>
}
return true;
case 0x25: /* OR - MIPS I */
if (RDREG != 0)
{
UML_OR(block, R32(RDREG), R32(RSREG), R32(RTREG)); // dor <rdreg>,<rsreg>,<rtreg>
}
return true;
case 0x26: /* XOR - MIPS I */
if (RDREG != 0)
{
UML_XOR(block, R32(RDREG), R32(RSREG), R32(RTREG)); // dxor <rdreg>,<rsreg>,<rtreg>
}
return true;
case 0x27: /* NOR - MIPS I */
if (RDREG != 0)
{
UML_OR(block, I0, R32(RSREG), R32(RTREG)); // dor i0,<rsreg>,<rtreg>
UML_XOR(block, R32(RDREG), I0, (uint64_t)~0); // dxor <rdreg>,i0,~0
}
return true;
/* ----- basic comparisons ----- */
case 0x2a: /* SLT - MIPS I */
if (RDREG != 0)
{
UML_CMP(block, R32(RSREG), R32(RTREG)); // dcmp <rsreg>,<rtreg>
UML_SETc(block, COND_L, R32(RDREG)); // dset <rdreg>,l
}
return true;
case 0x2b: /* SLTU - MIPS I */
if (RDREG != 0)
{
UML_CMP(block, R32(RSREG), R32(RTREG)); // dcmp <rsreg>,<rtreg>
UML_SETc(block, COND_B, R32(RDREG)); // dset <rdreg>,b
}
return true;
/* ----- jumps and branches ----- */
case 0x08: /* JR - MIPS I */
generate_delay_slot_and_branch(block, compiler, desc, 0); // <next instruction + hashjmp>
return true;
case 0x09: /* JALR - MIPS I */
generate_delay_slot_and_branch(block, compiler, desc, RDREG); // <next instruction + hashjmp>
return true;
/* ----- system calls ----- */
case 0x0d: /* BREAK - MIPS I */
UML_MOV(block, mem(&m_rsp_state->arg0), 3); // mov [arg0],3
UML_CALLC(block, cfunc_sp_set_status_cb, this); // callc cfunc_sp_set_status_cb
UML_MOV(block, mem(&m_rsp_state->icount), 0); // mov icount, #0
UML_MOV(block, mem(&m_rsp_state->jmpdest), desc->targetpc);
generate_branch(block, compiler, desc);
UML_EXIT(block, EXECUTE_OUT_OF_CYCLES);
return true;
}
return false;
}
/*-------------------------------------------------
generate_regimm - compile opcodes in the
'REGIMM' group
-------------------------------------------------*/
bool rsp_device::generate_regimm(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc)
{
uint32_t op = desc->opptr.l[0];
uint8_t opswitch = RTREG;
code_label skip;
switch (opswitch)
{
case 0x00: /* BLTZ */
case 0x10: /* BLTZAL */
if (RSREG != 0)
{
UML_CMP(block, R32(RSREG), 0); // dcmp <rsreg>,0
UML_JMPc(block, COND_GE, skip = compiler->labelnum++); // jmp skip,GE
generate_delay_slot_and_branch(block, compiler, desc, (opswitch & 0x10) ? 31 : 0);
// <next instruction + hashjmp>
UML_LABEL(block, skip); // skip:
}
return true;
case 0x01: /* BGEZ */
case 0x11: /* BGEZAL */
if (RSREG != 0)
{
UML_CMP(block, R32(RSREG), 0); // dcmp <rsreg>,0
UML_JMPc(block, COND_L, skip = compiler->labelnum++); // jmp skip,L
generate_delay_slot_and_branch(block, compiler, desc, (opswitch & 0x10) ? 31 : 0);
// <next instruction + hashjmp>
UML_LABEL(block, skip); // skip:
}
else
generate_delay_slot_and_branch(block, compiler, desc, (opswitch & 0x10) ? 31 : 0);
// <next instruction + hashjmp>
return true;
}
return false;
}
/*-------------------------------------------------
generate_cop0 - compile COP0 opcodes
-------------------------------------------------*/
bool rsp_device::generate_cop0(drcuml_block *block, compiler_state *compiler, const opcode_desc *desc)
{
uint32_t op = desc->opptr.l[0];
uint8_t opswitch = RSREG;
switch (opswitch)
{
case 0x00: /* MFCz */
if (RTREG != 0)
{
UML_MOV(block, mem(&m_rsp_state->arg0), RDREG); // mov [arg0],<rdreg>
UML_MOV(block, mem(&m_rsp_state->arg1), RTREG); // mov [arg1],<rtreg>
UML_CALLC(block, cfunc_get_cop0_reg, this); // callc cfunc_get_cop0_reg
if(RDREG == 2)
{
generate_update_cycles(block, compiler, mem(&m_rsp_state->pc), true);
UML_HASHJMP(block, 0, mem(&m_rsp_state->pc), *m_nocode);
}
}
return true;
case 0x04: /* MTCz */
UML_MOV(block, mem(&m_rsp_state->arg0), RDREG); // mov [arg0],<rdreg>
UML_MOV(block, mem(&m_rsp_state->arg1), R32(RTREG)); // mov [arg1],rtreg
UML_CALLC(block, cfunc_set_cop0_reg, this); // callc cfunc_set_cop0_reg
return true;
}
return false;
}
/***************************************************************************
CODE LOGGING HELPERS
***************************************************************************/
/*-------------------------------------------------
log_add_disasm_comment - add a comment
including disassembly of a RSP instruction
-------------------------------------------------*/
void rsp_device::log_add_disasm_comment(drcuml_block *block, uint32_t pc, uint32_t op)
{
if (m_drcuml->logging())
{
char buffer[100];
rsp_dasm_one(buffer, pc, op);
block->append_comment("%08X: %s", pc, buffer); // comment
}
}