/*
- * vim:shiftwidth=2:expandtab
+ * SH2 recompiler
+ * (C) notaz, 2009,2010
+ *
+ * This work is licensed under the terms of MAME license.
+ * See COPYING file in the top-level directory.
*
* notes:
* - tcache, block descriptor, link buffer overflows result in sh2_translate()
}
#include "mame/sh2dasm.h"
-#include <platform/linux/host_dasm.h>
+#include <platform/libpicofe/linux/host_dasm.h>
static int insns_compiled, hash_collisions, host_insn_count;
#define COUNT_OP \
host_insn_count++
#define dbg(...)
#endif
-#if (DRC_DEBUG & 2)
+#if (DRC_DEBUG & 4)
static u8 *tcache_dsm_ptrs[3];
static char sh2dasm_buff[64];
#define do_host_disasm(tcid) \
#define do_host_disasm(x)
#endif
-#if (DRC_DEBUG & 4) || defined(PDB)
+#if (DRC_DEBUG & 8) || defined(PDB)
static void REGPARM(3) *sh2_drc_log_entry(void *block, SH2 *sh2, u32 sr)
{
if (block != NULL) {
- dbg(4, "= %csh2 enter %08x %p, c=%d", sh2->is_slave ? 's' : 'm',
+ dbg(8, "= %csh2 enter %08x %p, c=%d", sh2->is_slave ? 's' : 'm',
sh2->pc, block, (signed int)sr >> 12);
pdb_step(sh2, sh2->pc);
}
u32 addr; // SH2 PC address
void *tcache_ptr; // translated block for above PC
struct block_desc_ *next; // next block with the same PC hash
-#if (DRC_DEBUG & 1)
+#if (DRC_DEBUG & 2)
int refcount;
#endif
} block_desc;
// note: reg_temp[] must have at least the amount of
// registers used by handlers in worst case (currently 4)
-#ifdef ARM
+#ifdef __arm__
#include "../drc/emit_arm.c"
static const int reg_map_g2h[] = {
4, 5, 6, 7,
8, -1, -1, -1,
-1, -1, -1, -1,
- -1, -1, -1, 9,
- -1, -1, -1, 10,
- -1, -1, -1, -1,
+ -1, -1, -1, 9, // r12 .. sp
+ -1, -1, -1, 10, // SHR_PC, SHR_PPC, SHR_PR, SHR_SR,
+ -1, -1, -1, -1, // SHR_GBR, SHR_VBR, SHR_MACH, SHR_MACL,
};
static temp_reg_t reg_temp[] = {
}
else
memset(Pico32xMem->drcblk_da[tcid - 1], 0, sizeof(Pico32xMem->drcblk_da[0]));
-#if (DRC_DEBUG & 2)
+#if (DRC_DEBUG & 4)
tcache_dsm_ptrs[tcid] = tcache_bases[tcid];
#endif
}
int cnt = block_link_counts[tcache_id];
if (cnt >= block_max_counts[tcache_id] * 2) {
- printf("bl overflow for tcache %d\n", tcache_id);
+ dbg(1, "bl overflow for tcache %d\n", tcache_id);
return -1;
}
bd = dr_get_bd(addr, is_slave, &tcache_id);
if (bd != NULL) {
- dbg(1, "block override for %08x", addr);
+ dbg(2, "block override for %08x", addr);
bd->tcache_ptr = tcache_ptr;
*blk_id = bd - block_tables[tcache_id];
return bd;
bcount = &block_counts[tcache_id];
if (*bcount >= block_max_counts[tcache_id]) {
- printf("bd overflow for tcache %d\n", tcache_id);
+ dbg(1, "bd overflow for tcache %d", tcache_id);
return NULL;
}
if (*bcount == 0)
if ((addr & 0xc6000000) == 0x02000000) { // ROM
bd->next = HASH_FUNC(hash_table, addr);
HASH_FUNC(hash_table, addr) = bd;
-#if (DRC_DEBUG & 1)
+#if (DRC_DEBUG & 2)
if (bd->next != NULL) {
printf(" hash collision with %08x\n", bd->next->addr);
hash_collisions++;
if (bd != NULL)
block = bd->tcache_ptr;
-#if (DRC_DEBUG & 1)
+#if (DRC_DEBUG & 2)
if (bd != NULL)
bd->refcount++;
#endif
return block;
}
+static void *dr_failure(void)
+{
+ lprintf("recompilation failed\n");
+ exit(1);
+}
+
static void *dr_prepare_ext_branch(u32 pc, SH2 *sh2, int tcache_id)
{
#if LINK_BRANCHES
for (i = 0; i < cnt; i++) {
if (bl[i].target_pc == pc) {
- dbg(1, "- link from %p", bl[i].jump);
+ dbg(2, "- link from %p", bl[i].jump);
emith_jump_patch(bl[i].jump, target);
// XXX: sync ARM caches (old jump should be fine)?
}
#define ADD_TO_ARRAY(array, count, item, failcode) \
array[count++] = item; \
if (count >= ARRAY_SIZE(array)) { \
- printf("warning: " #array " overflow\n"); \
+ dbg(1, "warning: " #array " overflow"); \
failcode; \
}
}
}
rcache_invalidate();
+
+ if (reg_map_g2h[SHR_SR] != -1)
+ emith_ctx_read(reg_map_g2h[SHR_SR], SHR_SR * 4);
+
// assuming arg0 and retval reg matches
return rcache_get_tmp_arg(0);
}
{
int ctxr;
host_arg2reg(ctxr, 2);
+ if (reg_map_g2h[SHR_SR] != -1)
+ emith_ctx_write(reg_map_g2h[SHR_SR], SHR_SR * 4);
+
switch (size) {
case 0: // 8
// XXX: consider inlining sh2_drc_write8
emith_call(sh2_drc_write32);
break;
}
+
+ if (reg_map_g2h[SHR_SR] != -1)
+ emith_ctx_read(reg_map_g2h[SHR_SR], SHR_SR * 4);
rcache_invalidate();
}
static void emit_block_entry(void)
{
- int arg0, arg1, arg2;
+ int arg0;
host_arg2reg(arg0, 0);
+
+#if (DRC_DEBUG & 8) || defined(PDB)
+ int arg1, arg2;
host_arg2reg(arg1, 1);
host_arg2reg(arg2, 2);
-#if (DRC_DEBUG & 4) || defined(PDB)
emit_do_static_regs(1, arg2);
emith_move_r_r(arg1, CONTEXT_REG);
emith_move_r_r(arg2, rcache_get_reg(SHR_SR, RC_GR_READ));
// predict tcache overflow
tmp = tcache_ptr - tcache_bases[tcache_id];
if (tmp > tcache_sizes[tcache_id] - MAX_BLOCK_SIZE) {
- printf("tcache %d overflow\n", tcache_id);
+ dbg(1, "tcache %d overflow", tcache_id);
return NULL;
}
block_entry = tcache_ptr;
- dbg(1, "== %csh2 block #%d,%d %08x -> %p", sh2->is_slave ? 's' : 'm',
+ dbg(2, "== %csh2 block #%d,%d %08x -> %p", sh2->is_slave ? 's' : 'm',
tcache_id, blkid_main, base_pc, block_entry);
dr_link_blocks(tcache_ptr, base_pc, tcache_id);
pc = branch_target_pc[i];
if (base_pc <= pc && pc <= end_pc && !(OP_FLAGS(pc) & OF_DELAY_OP))
branch_target_pc[tmp++] = branch_target_pc[i];
+
+ if (i == branch_target_count - 1) // workaround gcc 4.5.2 bug?
+ break;
}
+
branch_target_count = tmp;
- memset(branch_target_ptr, 0, sizeof(branch_target_ptr[0]) * branch_target_count);
- memset(branch_target_blkid, 0, sizeof(branch_target_blkid[0]) * branch_target_count);
+ if (branch_target_count > 0) {
+ memset(branch_target_ptr, 0, sizeof(branch_target_ptr[0]) * branch_target_count);
+ memset(branch_target_blkid, 0, sizeof(branch_target_blkid[0]) * branch_target_count);
+ }
// -------------------------------------------------
// 2nd pass: actual compilation
rcache_flush();
do_host_disasm(tcache_id);
- dbg(1, "-- %csh2 subblock #%d,%d %08x -> %p", sh2->is_slave ? 's' : 'm',
+ dbg(2, "-- %csh2 subblock #%d,%d %08x -> %p", sh2->is_slave ? 's' : 'm',
tcache_id, branch_target_blkid[i], pc, tcache_ptr);
subblock = dr_add_block(pc, sh2->is_slave, &branch_target_blkid[i]);
rcache_unlock_all();
}
-#if (DRC_DEBUG & 3)
- insns_compiled++;
#if (DRC_DEBUG & 2)
+ insns_compiled++;
+#if (DRC_DEBUG & 4)
DasmSH2(sh2dasm_buff, pc, op);
printf("%08x %04x %s\n", pc, op, sh2dasm_buff);
#endif
branch_patch_count++;
if (branch_patch_count == MAX_LOCAL_BRANCHES) {
- printf("warning: too many local branches\n");
+ dbg(1, "warning: too many local branches");
break;
}
}
t = find_in_array(branch_target_pc, branch_target_count, branch_patch_pc[i]);
target = branch_target_ptr[t];
if (target == NULL) {
- // flush pc and go back to dispatcher (should no longer happen)
- printf("stray branch to %08x %p\n", branch_patch_pc[i], tcache_ptr);
+ // flush pc and go back to dispatcher (this should no longer happen)
+ dbg(1, "stray branch to %08x %p", branch_patch_pc[i], tcache_ptr);
target = tcache_ptr;
emit_move_r_imm32(SHR_PC, branch_patch_pc[i]);
rcache_flush();
// mark literals
for (i = 0; i < literal_addr_count; i++) {
tmp = literal_addr[i];
- //printf("marking literal %08x\n", tmp);
drc_ram_blk[(tmp >> shift) & mask] = blkid_main << 1;
if (!(tmp & 3)) // assume long
drc_ram_blk[((tmp + 2) >> shift) & mask] = blkid_main << 1;
host_instructions_updated(block_entry, tcache_ptr);
do_host_disasm(tcache_id);
- dbg(1, " block #%d,%d tcache %d/%d, insns %d -> %d %.3f",
+ dbg(2, " block #%d,%d tcache %d/%d, insns %d -> %d %.3f",
tcache_id, block_counts[tcache_id],
tcache_ptr - tcache_bases[tcache_id], tcache_sizes[tcache_id],
insns_compiled, host_insn_count, (double)host_insn_count / insns_compiled);
if ((sh2->pc & 0xc6000000) == 0x02000000) // ROM
- dbg(1, " hash collisions %d/%d", hash_collisions, block_counts[tcache_id]);
+ dbg(2, " hash collisions %d/%d", hash_collisions, block_counts[tcache_id]);
/*
printf("~~~\n");
tcache_dsm_ptrs[tcache_id] = block_entry;
printf("~~~\n");
*/
-#if (DRC_DEBUG & 2)
+#if (DRC_DEBUG & 4)
fflush(stdout);
#endif
emith_call(sh2_translate);
emit_block_entry();
// XXX: can't translate, fail
- emith_call(exit);
+ emith_call(dr_failure);
// sh2_drc_test_irq(void)
// assumes it's called from main function (may jump to dispatcher)
MAKE_WRITE_WRAPPER(sh2_drc_write16);
MAKE_WRITE_WRAPPER(sh2_drc_write16_slot);
MAKE_WRITE_WRAPPER(sh2_drc_write32);
-#if (DRC_DEBUG & 2)
+#if (DRC_DEBUG & 4)
host_dasm_new_symbol(sh2_drc_read8);
host_dasm_new_symbol(sh2_drc_read16);
host_dasm_new_symbol(sh2_drc_read32);
#endif
rcache_invalidate();
-#if (DRC_DEBUG & 2)
+#if (DRC_DEBUG & 4)
host_dasm_new_symbol(sh2_drc_entry);
host_dasm_new_symbol(sh2_drc_dispatcher);
host_dasm_new_symbol(sh2_drc_exit);
void *tmp;
// XXX: kill links somehow?
- dbg(1, " killing entry %08x, blkid %d", bd->addr, bd - block_tables[tcache_id]);
+ dbg(2, " killing entry %08x, blkid %d", bd->addr, bd - block_tables[tcache_id]);
if (bd->addr == 0 || bd->tcache_ptr == NULL) {
- printf(" killing dead block!? %08x\n", bd->addr);
+ dbg(1, " killing dead block!? %08x", bd->addr);
return bd->tcache_ptr;
}
break;
if (!(*p & 1)) {
- printf("smc rm: missing block start for %08x?\n", a);
+ dbg(1, "smc rm: missing block start for %08x?", a);
p = pt;
}
void sh2_drc_wcheck_ram(unsigned int a, int val, int cpuid)
{
- dbg(1, "%csh2 smc check @%08x", cpuid ? 's' : 'm', a);
+ dbg(2, "%csh2 smc check @%08x", cpuid ? 's' : 'm', a);
sh2_smc_rm_block(a, Pico32xMem->drcblk_ram, 0, SH2_DRCBLK_RAM_SHIFT, 0x3ffff);
}
void sh2_drc_wcheck_da(unsigned int a, int val, int cpuid)
{
- dbg(1, "%csh2 smc check @%08x", cpuid ? 's' : 'm', a);
+ dbg(2, "%csh2 smc check @%08x", cpuid ? 's' : 'm', a);
sh2_smc_rm_block(a, Pico32xMem->drcblk_da[cpuid],
1 + cpuid, SH2_DRCBLK_DA_SHIFT, 0xfff);
}
-void sh2_execute(SH2 *sh2c, int cycles)
+int sh2_execute(SH2 *sh2c, int cycles)
{
int ret_cycles;
- sh2 = sh2c; // XXX
- sh2c->cycles_aim += cycles;
- cycles = sh2c->cycles_aim - sh2c->cycles_done;
+ sh2c->cycles_timeslice = cycles;
// cycles are kept in SHR_SR unused bits (upper 20)
- // bit19 contains T saved for delay slot
+ // bit11 contains T saved for delay slot
// others are usual SH2 flags
sh2c->sr &= 0x3f3;
sh2c->sr |= cycles << 12;
// TODO: irq cycles
ret_cycles = (signed int)sh2c->sr >> 12;
if (ret_cycles > 0)
- printf("warning: drc returned with cycles: %d\n", ret_cycles);
+ dbg(1, "warning: drc returned with cycles: %d", ret_cycles);
- sh2c->cycles_done += cycles - ret_cycles;
+ return sh2c->cycles_timeslice - ret_cycles;
}
-#if (DRC_DEBUG & 1)
+#if (DRC_DEBUG & 2)
void block_stats(void)
{
int c, b, i, total = 0;
// tmp
PicoOpt |= POPT_DIS_VDP_FIFO;
-#if (DRC_DEBUG & 2)
+#if (DRC_DEBUG & 4)
for (i = 0; i < ARRAY_SIZE(block_tables); i++)
tcache_dsm_ptrs[i] = tcache_bases[i];
// disasm the utils
block_stats();
for (i = 0; i < TCACHE_BUFFERS; i++) {
-#if (DRC_DEBUG & 2)
+#if (DRC_DEBUG & 4)
printf("~~~ tcache %d\n", i);
tcache_dsm_ptrs[i] = tcache_bases[i];
tcache_ptr = tcache_ptrs[i];
hash_table = NULL;
}
}
+
+// vim:shiftwidth=2:expandtab