+ emith_jump(sh2_drc_dispatcher);
+ break;
+ }
+
+end_op:
+ rcache_unlock_all();
+
+ cycles += opd->cycles;
+
+ if (op_flags[i+1] & OF_DELAY_OP) {
+ do_host_disasm(tcache_id);
+ continue;
+ }
+
+ // test irq?
+ if (drcf.test_irq && !drcf.pending_branch_direct) {
+ sr = rcache_get_reg(SHR_SR, RC_GR_RMW);
+ FLUSH_CYCLES(sr);
+ if (!drcf.pending_branch_indirect)
+ emit_move_r_imm32(SHR_PC, pc);
+ rcache_flush();
+ emith_call(sh2_drc_test_irq);
+ drcf.test_irq = 0;
+ }
+
+ // branch handling (with/without delay)
+ if (drcf.pending_branch_direct)
+ {
+ struct op_data *opd_b =
+ (op_flags[i] & OF_DELAY_OP) ? &ops[i-1] : opd;
+ u32 target_pc = opd_b->imm;
+ int cond = -1;
+ void *target = NULL;
+
+ sr = rcache_get_reg(SHR_SR, RC_GR_RMW);
+ FLUSH_CYCLES(sr);
+
+ if (opd_b->op != OP_BRANCH)
+ cond = (opd_b->op == OP_BRANCH_CF) ? DCOND_EQ : DCOND_NE;
+ if (cond != -1) {
+ int ctaken = (op_flags[i] & OF_DELAY_OP) ? 1 : 2;
+
+ if (delay_dep_fw & BITMASK1(SHR_T))
+ emith_tst_r_imm(sr, T_save);
+ else
+ emith_tst_r_imm(sr, T);
+
+ emith_sub_r_imm_c(cond, sr, ctaken<<12);
+ }
+ rcache_clean();
+
+#if LINK_BRANCHES
+ if (find_in_array(branch_target_pc, branch_target_count, target_pc) >= 0)
+ {
+ // local branch
+ // XXX: jumps back can be linked already
+ if (branch_patch_count < MAX_LOCAL_BRANCHES) {
+ target = tcache_ptr;
+ branch_patch_pc[branch_patch_count] = target_pc;
+ branch_patch_ptr[branch_patch_count] = target;
+ branch_patch_count++;
+ }
+ else
+ dbg(1, "warning: too many local branches");
+ }
+
+ if (target == NULL)
+#endif
+ {
+ // can't resolve branch locally, make a block exit
+ emit_move_r_imm32(SHR_PC, target_pc);
+ rcache_clean();
+
+ target = dr_prepare_ext_branch(target_pc, sh2->is_slave, tcache_id);
+ if (target == NULL)
+ return NULL;
+ }
+
+ if (cond != -1)
+ emith_jump_cond_patchable(cond, target);
+ else {
+ emith_jump_patchable(target);
+ rcache_invalidate();
+ }
+
+ drcf.pending_branch_direct = 0;
+ }
+ else if (drcf.pending_branch_indirect) {
+ sr = rcache_get_reg(SHR_SR, RC_GR_RMW);
+ FLUSH_CYCLES(sr);
+ rcache_flush();
+ emith_jump(sh2_drc_dispatcher);
+ drcf.pending_branch_indirect = 0;
+ }
+
+ do_host_disasm(tcache_id);
+ }
+
+ tmp = rcache_get_reg(SHR_SR, RC_GR_RMW);
+ FLUSH_CYCLES(tmp);
+ rcache_flush();
+
+ // check the last op
+ if (op_flags[i-1] & OF_DELAY_OP)
+ opd = &ops[i-2];
+ else
+ opd = &ops[i-1];
+
+ if (opd->op != OP_BRANCH && opd->op != OP_BRANCH_R
+ && opd->op != OP_BRANCH_RF && opd->op != OP_RTE)
+ {
+ void *target;
+
+ emit_move_r_imm32(SHR_PC, pc);
+ rcache_flush();
+
+ target = dr_prepare_ext_branch(pc, sh2->is_slave, tcache_id);
+ if (target == NULL)
+ return NULL;
+ emith_jump_patchable(target);
+ }
+
+ // link local branches
+ for (i = 0; i < branch_patch_count; i++) {
+ void *target;
+ int t;
+ 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 (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();
+ emith_jump(sh2_drc_dispatcher);
+ }
+ emith_jump_patch(branch_patch_ptr[i], target);
+ }
+
+ // mark memory blocks as containing compiled code
+ // override any overlay blocks as they become unreachable anyway
+ if ((block->addr & 0xc7fc0000) == 0x06000000
+ || (block->addr & 0xfffff000) == 0xc0000000)
+ {
+ u16 *drc_ram_blk = NULL;
+ u32 addr, mask = 0, shift = 0;
+
+ if (tcache_id != 0) {
+ // data array, BIOS
+ drc_ram_blk = Pico32xMem->drcblk_da[sh2->is_slave];
+ shift = SH2_DRCBLK_DA_SHIFT;
+ mask = 0xfff;
+ }
+ else {
+ // SDRAM
+ drc_ram_blk = Pico32xMem->drcblk_ram;
+ shift = SH2_DRCBLK_RAM_SHIFT;
+ mask = 0x3ffff;
+ }
+
+ // mark recompiled insns
+ drc_ram_blk[(base_pc & mask) >> shift] = 1;
+ for (pc = base_pc; pc < end_pc; pc += 2)
+ drc_ram_blk[(pc & mask) >> shift] = 1;
+
+ // mark literals
+ for (i = 0; i < literal_addr_count; i++) {
+ tmp = literal_addr[i];
+ drc_ram_blk[(tmp & mask) >> shift] = 1;
+ }
+
+ // add to invalidation lookup lists
+ addr = base_pc & ~(INVAL_PAGE_SIZE - 1);
+ for (; addr < end_literals; addr += INVAL_PAGE_SIZE) {
+ i = (addr & mask) / INVAL_PAGE_SIZE;
+ add_to_block_list(&inval_lookup[tcache_id][i], block);
+ }
+ }
+
+ tcache_ptrs[tcache_id] = tcache_ptr;
+
+ host_instructions_updated(block_entry_ptr, tcache_ptr);
+
+ do_host_disasm(tcache_id);
+
+ if (drcf.literals_disabled && literal_addr_count)
+ dbg(1, "literals_disabled && literal_addr_count?");
+ dbg(2, " block #%d,%d tcache %d/%d, insns %d -> %d %.3f",
+ tcache_id, blkid_main,
+ tcache_ptr - tcache_bases[tcache_id], tcache_sizes[tcache_id],
+ insns_compiled, host_insn_count, (float)host_insn_count / insns_compiled);
+ if ((sh2->pc & 0xc6000000) == 0x02000000) // ROM
+ dbg(2, " hash collisions %d/%d", hash_collisions, block_counts[tcache_id]);
+/*
+ printf("~~~\n");
+ tcache_dsm_ptrs[tcache_id] = block_entry_ptr;
+ do_host_disasm(tcache_id);
+ printf("~~~\n");
+*/
+
+#if (DRC_DEBUG & 4)
+ fflush(stdout);
+#endif
+
+ return block_entry_ptr;
+}
+
+static void sh2_generate_utils(void)
+{
+ int arg0, arg1, arg2, sr, tmp;
+
+ sh2_drc_write32 = p32x_sh2_write32;
+ sh2_drc_read8 = p32x_sh2_read8;
+ sh2_drc_read16 = p32x_sh2_read16;
+ sh2_drc_read32 = p32x_sh2_read32;
+
+ host_arg2reg(arg0, 0);
+ host_arg2reg(arg1, 1);
+ host_arg2reg(arg2, 2);
+ emith_move_r_r(arg0, arg0); // nop
+
+ // sh2_drc_exit(void)
+ sh2_drc_exit = (void *)tcache_ptr;
+ emit_do_static_regs(1, arg2);
+ emith_sh2_drc_exit();
+
+ // sh2_drc_dispatcher(void)
+ sh2_drc_dispatcher = (void *)tcache_ptr;
+ sr = rcache_get_reg(SHR_SR, RC_GR_READ);
+ emith_cmp_r_imm(sr, 0);
+ emith_jump_cond(DCOND_LT, sh2_drc_exit);
+ rcache_invalidate();
+ emith_ctx_read(arg0, SHR_PC * 4);
+ emith_ctx_read(arg1, offsetof(SH2, is_slave));
+ emith_add_r_r_imm(arg2, CONTEXT_REG, offsetof(SH2, drc_tmp));
+ emith_call(dr_lookup_block);
+ emit_block_entry();
+ // lookup failed, call sh2_translate()
+ emith_move_r_r(arg0, CONTEXT_REG);
+ emith_ctx_read(arg1, offsetof(SH2, drc_tmp)); // tcache_id
+ emith_call(sh2_translate);
+ emit_block_entry();
+ // sh2_translate() failed, flush cache and retry
+ emith_ctx_read(arg0, offsetof(SH2, drc_tmp));
+ emith_call(flush_tcache);
+ emith_move_r_r(arg0, CONTEXT_REG);
+ emith_ctx_read(arg1, offsetof(SH2, drc_tmp));
+ emith_call(sh2_translate);
+ emit_block_entry();
+ // XXX: can't translate, fail
+ emith_call(dr_failure);
+
+ // sh2_drc_test_irq(void)
+ // assumes it's called from main function (may jump to dispatcher)
+ sh2_drc_test_irq = (void *)tcache_ptr;
+ emith_ctx_read(arg1, offsetof(SH2, pending_level));
+ sr = rcache_get_reg(SHR_SR, RC_GR_READ);
+ emith_lsr(arg0, sr, I_SHIFT);
+ emith_and_r_imm(arg0, 0x0f);
+ emith_cmp_r_r(arg1, arg0); // pending_level > ((sr >> 4) & 0x0f)?
+ EMITH_SJMP_START(DCOND_GT);
+ emith_ret_c(DCOND_LE); // nope, return
+ EMITH_SJMP_END(DCOND_GT);
+ // adjust SP
+ tmp = rcache_get_reg(SHR_SP, RC_GR_RMW);
+ emith_sub_r_imm(tmp, 4*2);
+ rcache_clean();
+ // push SR
+ tmp = rcache_get_reg_arg(0, SHR_SP);
+ emith_add_r_imm(tmp, 4);
+ tmp = rcache_get_reg_arg(1, SHR_SR);
+ emith_clear_msb(tmp, tmp, 22);
+ emith_move_r_r(arg2, CONTEXT_REG);
+ emith_call(p32x_sh2_write32); // XXX: use sh2_drc_write32?
+ rcache_invalidate();
+ // push PC
+ rcache_get_reg_arg(0, SHR_SP);
+ emith_ctx_read(arg1, SHR_PC * 4);
+ emith_move_r_r(arg2, CONTEXT_REG);
+ emith_call(p32x_sh2_write32);
+ rcache_invalidate();
+ // update I, cycles, do callback
+ emith_ctx_read(arg1, offsetof(SH2, pending_level));
+ sr = rcache_get_reg(SHR_SR, RC_GR_RMW);
+ emith_bic_r_imm(sr, I);
+ emith_or_r_r_lsl(sr, arg1, I_SHIFT);
+ emith_sub_r_imm(sr, 13 << 12); // at least 13 cycles
+ rcache_flush();
+ emith_move_r_r(arg0, CONTEXT_REG);
+ emith_call_ctx(offsetof(SH2, irq_callback)); // vector = sh2->irq_callback(sh2, level);
+ // obtain new PC
+ emith_lsl(arg0, arg0, 2);
+ emith_ctx_read(arg1, SHR_VBR * 4);
+ emith_add_r_r(arg0, arg1);
+ emit_memhandler_read(2);
+ emith_ctx_write(arg0, SHR_PC * 4);
+#ifdef __i386__
+ emith_add_r_imm(xSP, 4); // fix stack
+#endif
+ emith_jump(sh2_drc_dispatcher);
+ rcache_invalidate();
+
+ // sh2_drc_entry(SH2 *sh2)
+ sh2_drc_entry = (void *)tcache_ptr;
+ emith_sh2_drc_entry();
+ emith_move_r_r(CONTEXT_REG, arg0); // move ctx, arg0
+ emit_do_static_regs(0, arg2);
+ emith_call(sh2_drc_test_irq);
+ emith_jump(sh2_drc_dispatcher);
+
+ // sh2_drc_write8(u32 a, u32 d)
+ sh2_drc_write8 = (void *)tcache_ptr;
+ emith_ctx_read(arg2, offsetof(SH2, write8_tab));
+ emith_sh2_wcall(arg0, arg2);
+
+ // sh2_drc_write16(u32 a, u32 d)
+ sh2_drc_write16 = (void *)tcache_ptr;
+ emith_ctx_read(arg2, offsetof(SH2, write16_tab));
+ emith_sh2_wcall(arg0, arg2);
+
+#ifdef PDB_NET
+ // debug
+ #define MAKE_READ_WRAPPER(func) { \
+ void *tmp = (void *)tcache_ptr; \
+ emith_push_ret(); \
+ emith_call(func); \
+ emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[0])); \
+ emith_addf_r_r(arg2, arg0); \
+ emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[0])); \
+ emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[1])); \
+ emith_adc_r_imm(arg2, 0x01000000); \
+ emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[1])); \
+ emith_pop_and_ret(); \
+ func = tmp; \
+ }
+ #define MAKE_WRITE_WRAPPER(func) { \
+ void *tmp = (void *)tcache_ptr; \
+ emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[0])); \
+ emith_addf_r_r(arg2, arg1); \
+ emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[0])); \
+ emith_ctx_read(arg2, offsetof(SH2, pdb_io_csum[1])); \
+ emith_adc_r_imm(arg2, 0x01000000); \
+ emith_ctx_write(arg2, offsetof(SH2, pdb_io_csum[1])); \
+ emith_move_r_r(arg2, CONTEXT_REG); \
+ emith_jump(func); \
+ func = tmp; \
+ }
+
+ MAKE_READ_WRAPPER(sh2_drc_read8);
+ MAKE_READ_WRAPPER(sh2_drc_read16);
+ MAKE_READ_WRAPPER(sh2_drc_read32);
+ MAKE_WRITE_WRAPPER(sh2_drc_write8);
+ MAKE_WRITE_WRAPPER(sh2_drc_write16);
+ MAKE_WRITE_WRAPPER(sh2_drc_write32);
+#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);
+ host_dasm_new_symbol(sh2_drc_write32);
+#endif
+#endif
+
+ rcache_invalidate();
+#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);
+ host_dasm_new_symbol(sh2_drc_test_irq);
+ host_dasm_new_symbol(sh2_drc_write8);
+ host_dasm_new_symbol(sh2_drc_write16);
+#endif
+}
+
+static void sh2_smc_rm_block_entry(struct block_desc *bd, int tcache_id, u32 ram_mask)
+{
+ struct block_link *bl, *bl_next, *bl_unresolved;
+ u32 i, addr, end_addr;
+ void *tmp;
+
+ dbg(2, " killing entry %08x-%08x-%08x, blkid %d,%d",
+ bd->addr, bd->addr + bd->size_nolit, bd->addr + bd->size,
+ tcache_id, bd - block_tables[tcache_id]);
+ if (bd->addr == 0 || bd->entry_count == 0) {
+ dbg(1, " killing dead block!? %08x", bd->addr);
+ return;
+ }
+
+ // remove from inval_lookup
+ addr = bd->addr & ~(INVAL_PAGE_SIZE - 1);
+ end_addr = bd->addr + bd->size;
+ for (; addr < end_addr; addr += INVAL_PAGE_SIZE) {
+ i = (addr & ram_mask) / INVAL_PAGE_SIZE;
+ rm_from_block_list(&inval_lookup[tcache_id][i], bd);
+ }
+
+ tmp = tcache_ptr;
+ bl_unresolved = unresolved_links[tcache_id];
+
+ // remove from hash table, make incoming links unresolved
+ // XXX: maybe patch branches w/flush instead?
+ for (i = 0; i < bd->entry_count; i++) {
+ rm_from_hashlist(&bd->entryp[i], tcache_id);
+
+ // since we never reuse tcache space of dead blocks,
+ // insert jump to dispatcher for blocks that are linked to this
+ tcache_ptr = bd->entryp[i].tcache_ptr;
+ emit_move_r_imm32(SHR_PC, bd->entryp[i].pc);
+ rcache_flush();
+ emith_jump(sh2_drc_dispatcher);
+
+ host_instructions_updated(bd->entryp[i].tcache_ptr, tcache_ptr);
+
+ for (bl = bd->entryp[i].links; bl != NULL; ) {
+ bl_next = bl->next;
+ bl->next = bl_unresolved;
+ bl_unresolved = bl;
+ bl = bl_next;
+ }
+ }
+
+ tcache_ptr = tmp;
+ unresolved_links[tcache_id] = bl_unresolved;
+
+ bd->addr = bd->size = bd->size_nolit = 0;
+ bd->entry_count = 0;
+}
+
+static void sh2_smc_rm_block(u32 a, u16 *drc_ram_blk, int tcache_id, u32 shift, u32 mask)
+{
+ struct block_list **blist = NULL, *entry;
+ u32 from = ~0, to = 0, end_addr, taddr, i;
+ struct block_desc *block;
+
+ blist = &inval_lookup[tcache_id][(a & mask) / INVAL_PAGE_SIZE];
+ entry = *blist;
+ while (entry != NULL) {
+ block = entry->block;
+ end_addr = block->addr + block->size;
+ if (block->addr <= a && a < end_addr) {
+ // get addr range that includes all removed blocks
+ if (from > block->addr)
+ from = block->addr;
+ if (to < end_addr)
+ to = end_addr;
+
+ sh2_smc_rm_block_entry(block, tcache_id, mask);
+ if (a >= block->addr + block->size_nolit)
+ literal_disabled_frames = 3;
+
+ // entry lost, restart search
+ entry = *blist;
+ continue;
+ }
+ entry = entry->next;
+ }
+
+ if (from >= to)
+ return;
+
+ // update range around a to match latest state
+ from &= ~(INVAL_PAGE_SIZE - 1);
+ to |= (INVAL_PAGE_SIZE - 1);
+ for (taddr = from; taddr < to; taddr += INVAL_PAGE_SIZE) {
+ i = (taddr & mask) / INVAL_PAGE_SIZE;
+ entry = inval_lookup[tcache_id][i];
+
+ for (; entry != NULL; entry = entry->next) {
+ block = entry->block;
+
+ if (block->addr > a) {
+ if (to > block->addr)
+ to = block->addr;
+ }
+ else {
+ end_addr = block->addr + block->size;
+ if (from < end_addr)
+ from = end_addr;
+ }
+ }
+ }
+
+ // clear code marks
+ if (from < to) {
+ u16 *p = drc_ram_blk + ((from & mask) >> shift);
+ memset(p, 0, (to - from) >> (shift - 1));
+ }
+}
+
+void sh2_drc_wcheck_ram(unsigned int a, int val, int cpuid)
+{
+ 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(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);
+}
+
+int sh2_execute_drc(SH2 *sh2c, int cycles)
+{
+ int ret_cycles;
+
+ // cycles are kept in SHR_SR unused bits (upper 20)
+ // bit11 contains T saved for delay slot
+ // others are usual SH2 flags
+ sh2c->sr &= 0x3f3;
+ sh2c->sr |= cycles << 12;
+ sh2_drc_entry(sh2c);
+
+ // TODO: irq cycles
+ ret_cycles = (signed int)sh2c->sr >> 12;
+ if (ret_cycles > 0)
+ dbg(1, "warning: drc returned with cycles: %d", ret_cycles);
+
+ sh2c->sr &= 0x3f3;
+ return ret_cycles;
+}
+
+#if (DRC_DEBUG & 2)
+void block_stats(void)
+{
+ int c, b, i, total = 0;
+
+ printf("block stats:\n");
+ for (b = 0; b < ARRAY_SIZE(block_tables); b++)
+ for (i = 0; i < block_counts[b]; i++)
+ if (block_tables[b][i].addr != 0)
+ total += block_tables[b][i].refcount;
+
+ for (c = 0; c < 10; c++) {
+ struct block_desc *blk, *maxb = NULL;
+ int max = 0;
+ for (b = 0; b < ARRAY_SIZE(block_tables); b++) {
+ for (i = 0; i < block_counts[b]; i++) {
+ blk = &block_tables[b][i];
+ if (blk->addr != 0 && blk->refcount > max) {
+ max = blk->refcount;
+ maxb = blk;
+ }
+ }
+ }
+ if (maxb == NULL)
+ break;
+ printf("%08x %9d %2.3f%%\n", maxb->addr, maxb->refcount,
+ (double)maxb->refcount / total * 100.0);
+ maxb->refcount = 0;
+ }
+
+ for (b = 0; b < ARRAY_SIZE(block_tables); b++)
+ for (i = 0; i < block_counts[b]; i++)
+ block_tables[b][i].refcount = 0;
+}
+#else
+#define block_stats()
+#endif
+
+void sh2_drc_flush_all(void)
+{
+ block_stats();
+ flush_tcache(0);
+ flush_tcache(1);
+ flush_tcache(2);
+}
+
+void sh2_drc_mem_setup(SH2 *sh2)
+{
+ // fill the convenience pointers
+ sh2->p_bios = sh2->is_slave ? Pico32xMem->sh2_rom_s.w : Pico32xMem->sh2_rom_m.w;
+ sh2->p_da = sh2->data_array;
+ sh2->p_sdram = Pico32xMem->sdram;
+ sh2->p_rom = Pico.rom;
+}
+
+void sh2_drc_frame(void)
+{
+ if (literal_disabled_frames > 0)
+ literal_disabled_frames--;
+}
+
+int sh2_drc_init(SH2 *sh2)
+{
+ int i;
+
+ if (block_tables[0] == NULL)
+ {
+ for (i = 0; i < TCACHE_BUFFERS; i++) {
+ block_tables[i] = calloc(block_max_counts[i], sizeof(*block_tables[0]));
+ if (block_tables[i] == NULL)
+ goto fail;
+ // max 2 block links (exits) per block
+ block_link_pool[i] = calloc(block_link_pool_max_counts[i],
+ sizeof(*block_link_pool[0]));
+ if (block_link_pool[i] == NULL)
+ goto fail;
+
+ inval_lookup[i] = calloc(ram_sizes[i] / INVAL_PAGE_SIZE,
+ sizeof(inval_lookup[0]));
+ if (inval_lookup[i] == NULL)
+ goto fail;
+
+ hash_tables[i] = calloc(hash_table_sizes[i], sizeof(*hash_tables[0]));
+ if (hash_tables[i] == NULL)
+ goto fail;
+ }
+ memset(block_counts, 0, sizeof(block_counts));
+ memset(block_link_pool_counts, 0, sizeof(block_link_pool_counts));
+
+ drc_cmn_init();
+ tcache_ptr = tcache;
+ sh2_generate_utils();
+ host_instructions_updated(tcache, tcache_ptr);
+
+ tcache_bases[0] = tcache_ptrs[0] = tcache_ptr;
+ for (i = 1; i < ARRAY_SIZE(tcache_bases); i++)
+ tcache_bases[i] = tcache_ptrs[i] = tcache_bases[i - 1] + tcache_sizes[i - 1];
+
+#if (DRC_DEBUG & 4)
+ for (i = 0; i < ARRAY_SIZE(block_tables); i++)
+ tcache_dsm_ptrs[i] = tcache_bases[i];
+ // disasm the utils
+ tcache_dsm_ptrs[0] = tcache;
+ do_host_disasm(0);
+#endif
+#if (DRC_DEBUG & 1)
+ hash_collisions = 0;
+#endif
+ }
+
+ return 0;
+
+fail:
+ sh2_drc_finish(sh2);
+ return -1;
+}
+
+void sh2_drc_finish(SH2 *sh2)
+{
+ int i;
+
+ if (block_tables[0] == NULL)
+ return;
+
+ sh2_drc_flush_all();
+
+ for (i = 0; i < TCACHE_BUFFERS; i++) {
+#if (DRC_DEBUG & 4)
+ printf("~~~ tcache %d\n", i);
+ tcache_dsm_ptrs[i] = tcache_bases[i];
+ tcache_ptr = tcache_ptrs[i];
+ do_host_disasm(i);
+#endif
+
+ if (block_tables[i] != NULL)
+ free(block_tables[i]);
+ block_tables[i] = NULL;
+ if (block_link_pool[i] == NULL)
+ free(block_link_pool[i]);
+ block_link_pool[i] = NULL;
+
+ if (inval_lookup[i] == NULL)
+ free(inval_lookup[i]);
+ inval_lookup[i] = NULL;
+
+ if (hash_tables[i] != NULL) {
+ free(hash_tables[i]);
+ hash_tables[i] = NULL;
+ }
+ }
+
+ drc_cmn_cleanup();
+}
+
+#endif /* DRC_SH2 */
+
+static void *dr_get_pc_base(u32 pc, int is_slave)
+{
+ void *ret = NULL;
+ u32 mask = 0;
+
+ if ((pc & ~0x7ff) == 0) {
+ // BIOS
+ ret = is_slave ? Pico32xMem->sh2_rom_s.w : Pico32xMem->sh2_rom_m.w;
+ mask = 0x7ff;
+ }
+ else if ((pc & 0xfffff000) == 0xc0000000) {
+ // data array
+ ret = sh2s[is_slave].data_array;
+ mask = 0xfff;
+ }
+ else if ((pc & 0xc6000000) == 0x06000000) {
+ // SDRAM
+ ret = Pico32xMem->sdram;
+ mask = 0x03ffff;
+ }
+ else if ((pc & 0xc6000000) == 0x02000000) {
+ // ROM
+ if ((pc & 0x3fffff) < Pico.romsize)
+ ret = Pico.rom;
+ mask = 0x3fffff;
+ }
+
+ if (ret == NULL)
+ return (void *)-1; // NULL is valid value
+
+ return (char *)ret - (pc & ~mask);
+}
+
+void scan_block(u32 base_pc, int is_slave, u8 *op_flags, u32 *end_pc_out,
+ u32 *end_literals_out)
+{
+ u16 *dr_pc_base;
+ u32 pc, op, tmp;
+ u32 end_pc, end_literals = 0;
+ u32 lowest_mova = 0;
+ struct op_data *opd;
+ int next_is_delay = 0;
+ int end_block = 0;
+ int i, i_end;
+
+ memset(op_flags, 0, BLOCK_INSN_LIMIT);
+
+ dr_pc_base = dr_get_pc_base(base_pc, is_slave);
+
+ // 1st pass: disassemble
+ for (i = 0, pc = base_pc; ; i++, pc += 2) {
+ // we need an ops[] entry after the last one initialized,
+ // so do it before end_block checks
+ opd = &ops[i];
+ opd->op = OP_UNHANDLED;
+ opd->rm = -1;
+ opd->source = opd->dest = 0;
+ opd->cycles = 1;
+ opd->imm = 0;
+
+ if (next_is_delay) {
+ op_flags[i] |= OF_DELAY_OP;
+ next_is_delay = 0;
+ }
+ else if (end_block || i >= BLOCK_INSN_LIMIT - 2)
+ break;
+
+ op = FETCH_OP(pc);
+ switch ((op & 0xf000) >> 12)
+ {
+ /////////////////////////////////////////////
+ case 0x00:
+ switch (op & 0x0f)
+ {
+ case 0x02:
+ switch (GET_Fx())
+ {
+ case 0: // STC SR,Rn 0000nnnn00000010
+ tmp = SHR_SR;
+ break;
+ case 1: // STC GBR,Rn 0000nnnn00010010
+ tmp = SHR_GBR;
+ break;
+ case 2: // STC VBR,Rn 0000nnnn00100010
+ tmp = SHR_VBR;
+ break;
+ default:
+ goto undefined;
+ }
+ opd->op = OP_MOVE;
+ opd->source = BITMASK1(tmp);
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x03:
+ CHECK_UNHANDLED_BITS(0xd0, undefined);
+ // BRAF Rm 0000mmmm00100011
+ // BSRF Rm 0000mmmm00000011
+ opd->op = OP_BRANCH_RF;
+ opd->rm = GET_Rn();
+ opd->source = BITMASK1(opd->rm);
+ opd->dest = BITMASK1(SHR_PC);
+ if (!(op & 0x20))
+ opd->dest |= BITMASK1(SHR_PR);
+ opd->cycles = 2;
+ next_is_delay = 1;
+ end_block = 1;
+ break;
+ case 0x04: // MOV.B Rm,@(R0,Rn) 0000nnnnmmmm0100
+ case 0x05: // MOV.W Rm,@(R0,Rn) 0000nnnnmmmm0101
+ case 0x06: // MOV.L Rm,@(R0,Rn) 0000nnnnmmmm0110
+ opd->source = BITMASK3(GET_Rm(), SHR_R0, GET_Rn());
+ break;
+ case 0x07:
+ // MUL.L Rm,Rn 0000nnnnmmmm0111
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(SHR_MACL);
+ opd->cycles = 2;
+ break;
+ case 0x08:
+ CHECK_UNHANDLED_BITS(0xf00, undefined);
+ switch (GET_Fx())
+ {
+ case 0: // CLRT 0000000000001000
+ opd->op = OP_SETCLRT;
+ opd->dest = BITMASK1(SHR_T);
+ opd->imm = 0;
+ break;
+ case 1: // SETT 0000000000011000
+ opd->op = OP_SETCLRT;
+ opd->dest = BITMASK1(SHR_T);
+ opd->imm = 1;
+ break;
+ case 2: // CLRMAC 0000000000101000
+ opd->dest = BITMASK3(SHR_T, SHR_MACL, SHR_MACH);
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+ case 0x09:
+ switch (GET_Fx())
+ {
+ case 0: // NOP 0000000000001001
+ CHECK_UNHANDLED_BITS(0xf00, undefined);
+ break;
+ case 1: // DIV0U 0000000000011001
+ CHECK_UNHANDLED_BITS(0xf00, undefined);
+ opd->dest = BITMASK2(SHR_SR, SHR_T);
+ break;
+ case 2: // MOVT Rn 0000nnnn00101001
+ opd->source = BITMASK1(SHR_T);
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+ case 0x0a:
+ switch (GET_Fx())
+ {
+ case 0: // STS MACH,Rn 0000nnnn00001010
+ tmp = SHR_MACH;
+ break;
+ case 1: // STS MACL,Rn 0000nnnn00011010
+ tmp = SHR_MACL;
+ break;
+ case 2: // STS PR,Rn 0000nnnn00101010
+ tmp = SHR_PR;
+ break;
+ default:
+ goto undefined;
+ }
+ opd->op = OP_MOVE;
+ opd->source = BITMASK1(tmp);
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x0b:
+ CHECK_UNHANDLED_BITS(0xf00, undefined);
+ switch (GET_Fx())
+ {
+ case 0: // RTS 0000000000001011
+ opd->op = OP_BRANCH_R;
+ opd->rm = SHR_PR;
+ opd->source = BITMASK1(opd->rm);
+ opd->dest = BITMASK1(SHR_PC);
+ opd->cycles = 2;
+ next_is_delay = 1;
+ end_block = 1;
+ break;
+ case 1: // SLEEP 0000000000011011
+ opd->op = OP_SLEEP;
+ end_block = 1;
+ break;
+ case 2: // RTE 0000000000101011
+ opd->op = OP_RTE;
+ opd->source = BITMASK1(SHR_SP);
+ opd->dest = BITMASK2(SHR_SR, SHR_PC);
+ opd->cycles = 4;
+ next_is_delay = 1;
+ end_block = 1;
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+ case 0x0c: // MOV.B @(R0,Rm),Rn 0000nnnnmmmm1100
+ case 0x0d: // MOV.W @(R0,Rm),Rn 0000nnnnmmmm1101
+ case 0x0e: // MOV.L @(R0,Rm),Rn 0000nnnnmmmm1110
+ opd->source = BITMASK2(GET_Rm(), SHR_R0);
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x0f: // MAC.L @Rm+,@Rn+ 0000nnnnmmmm1111
+ opd->source = BITMASK5(GET_Rm(), GET_Rn(), SHR_SR, SHR_MACL, SHR_MACH);
+ opd->dest = BITMASK4(GET_Rm(), GET_Rn(), SHR_MACL, SHR_MACH);
+ opd->cycles = 3;
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+
+ /////////////////////////////////////////////
+ case 0x01:
+ // MOV.L Rm,@(disp,Rn) 0001nnnnmmmmdddd
+ opd->source = BITMASK1(GET_Rm());
+ opd->source = BITMASK1(GET_Rn());
+ opd->imm = (op & 0x0f) * 4;
+ break;
+
+ /////////////////////////////////////////////
+ case 0x02:
+ switch (op & 0x0f)
+ {
+ case 0x00: // MOV.B Rm,@Rn 0010nnnnmmmm0000
+ case 0x01: // MOV.W Rm,@Rn 0010nnnnmmmm0001
+ case 0x02: // MOV.L Rm,@Rn 0010nnnnmmmm0010
+ opd->source = BITMASK1(GET_Rm());
+ opd->source = BITMASK1(GET_Rn());
+ break;
+ case 0x04: // MOV.B Rm,@-Rn 0010nnnnmmmm0100
+ case 0x05: // MOV.W Rm,@-Rn 0010nnnnmmmm0101
+ case 0x06: // MOV.L Rm,@-Rn 0010nnnnmmmm0110
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x07: // DIV0S Rm,Rn 0010nnnnmmmm0111
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(SHR_SR);
+ break;
+ case 0x08: // TST Rm,Rn 0010nnnnmmmm1000
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(SHR_T);
+ break;
+ case 0x09: // AND Rm,Rn 0010nnnnmmmm1001
+ case 0x0a: // XOR Rm,Rn 0010nnnnmmmm1010
+ case 0x0b: // OR Rm,Rn 0010nnnnmmmm1011
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x0c: // CMP/STR Rm,Rn 0010nnnnmmmm1100
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(SHR_T);
+ break;
+ case 0x0d: // XTRCT Rm,Rn 0010nnnnmmmm1101
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x0e: // MULU.W Rm,Rn 0010nnnnmmmm1110
+ case 0x0f: // MULS.W Rm,Rn 0010nnnnmmmm1111
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(SHR_MACL);
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+
+ /////////////////////////////////////////////
+ case 0x03:
+ switch (op & 0x0f)
+ {
+ case 0x00: // CMP/EQ Rm,Rn 0011nnnnmmmm0000
+ case 0x02: // CMP/HS Rm,Rn 0011nnnnmmmm0010
+ case 0x03: // CMP/GE Rm,Rn 0011nnnnmmmm0011
+ case 0x06: // CMP/HI Rm,Rn 0011nnnnmmmm0110
+ case 0x07: // CMP/GT Rm,Rn 0011nnnnmmmm0111
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(SHR_T);
+ break;
+ case 0x04: // DIV1 Rm,Rn 0011nnnnmmmm0100
+ opd->source = BITMASK3(GET_Rm(), GET_Rn(), SHR_SR);
+ opd->dest = BITMASK2(GET_Rn(), SHR_SR);
+ break;
+ case 0x05: // DMULU.L Rm,Rn 0011nnnnmmmm0101
+ case 0x0d: // DMULS.L Rm,Rn 0011nnnnmmmm1101
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK2(SHR_MACL, SHR_MACH);
+ opd->cycles = 2;
+ break;
+ case 0x08: // SUB Rm,Rn 0011nnnnmmmm1000
+ case 0x0c: // ADD Rm,Rn 0011nnnnmmmm1100
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x0a: // SUBC Rm,Rn 0011nnnnmmmm1010
+ case 0x0e: // ADDC Rm,Rn 0011nnnnmmmm1110
+ opd->source = BITMASK3(GET_Rm(), GET_Rn(), SHR_T);
+ opd->dest = BITMASK2(GET_Rn(), SHR_T);
+ break;
+ case 0x0b: // SUBV Rm,Rn 0011nnnnmmmm1011
+ case 0x0f: // ADDV Rm,Rn 0011nnnnmmmm1111
+ opd->source = BITMASK2(GET_Rm(), GET_Rn());
+ opd->dest = BITMASK2(GET_Rn(), SHR_T);
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+
+ /////////////////////////////////////////////
+ case 0x04:
+ switch (op & 0x0f)
+ {
+ case 0x00:
+ switch (GET_Fx())
+ {
+ case 0: // SHLL Rn 0100nnnn00000000
+ case 2: // SHAL Rn 0100nnnn00100000
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK2(GET_Rn(), SHR_T);
+ break;
+ case 1: // DT Rn 0100nnnn00010000
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK2(GET_Rn(), SHR_T);
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+ case 0x01:
+ switch (GET_Fx())
+ {
+ case 0: // SHLR Rn 0100nnnn00000001
+ case 2: // SHAR Rn 0100nnnn00100001
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK2(GET_Rn(), SHR_T);
+ break;
+ case 1: // CMP/PZ Rn 0100nnnn00010001
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK1(SHR_T);
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+ case 0x02:
+ case 0x03:
+ switch (op & 0x3f)
+ {
+ case 0x02: // STS.L MACH,@-Rn 0100nnnn00000010
+ tmp = SHR_MACH;
+ break;
+ case 0x12: // STS.L MACL,@-Rn 0100nnnn00010010
+ tmp = SHR_MACL;
+ break;
+ case 0x22: // STS.L PR,@-Rn 0100nnnn00100010
+ tmp = SHR_PR;
+ break;
+ case 0x03: // STC.L SR,@-Rn 0100nnnn00000011
+ tmp = SHR_SR;
+ opd->cycles = 2;
+ break;
+ case 0x13: // STC.L GBR,@-Rn 0100nnnn00010011
+ tmp = SHR_GBR;
+ opd->cycles = 2;
+ break;
+ case 0x23: // STC.L VBR,@-Rn 0100nnnn00100011
+ tmp = SHR_VBR;
+ opd->cycles = 2;
+ break;
+ default:
+ goto undefined;
+ }
+ opd->source = BITMASK2(GET_Rn(), tmp);
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x04:
+ case 0x05:
+ switch (op & 0x3f)
+ {
+ case 0x04: // ROTL Rn 0100nnnn00000100
+ case 0x05: // ROTR Rn 0100nnnn00000101
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK2(GET_Rn(), SHR_T);
+ break;
+ case 0x24: // ROTCL Rn 0100nnnn00100100
+ case 0x25: // ROTCR Rn 0100nnnn00100101
+ opd->source = BITMASK2(GET_Rn(), SHR_T);
+ opd->dest = BITMASK2(GET_Rn(), SHR_T);
+ break;
+ case 0x15: // CMP/PL Rn 0100nnnn00010101
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK1(SHR_T);
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+ case 0x06:
+ case 0x07:
+ switch (op & 0x3f)
+ {
+ case 0x06: // LDS.L @Rm+,MACH 0100mmmm00000110
+ tmp = SHR_MACH;
+ break;
+ case 0x16: // LDS.L @Rm+,MACL 0100mmmm00010110
+ tmp = SHR_MACL;
+ break;
+ case 0x26: // LDS.L @Rm+,PR 0100mmmm00100110
+ tmp = SHR_PR;
+ break;
+ case 0x07: // LDC.L @Rm+,SR 0100mmmm00000111
+ tmp = SHR_SR;
+ opd->cycles = 3;
+ break;
+ case 0x17: // LDC.L @Rm+,GBR 0100mmmm00010111
+ tmp = SHR_GBR;
+ opd->cycles = 3;
+ break;
+ case 0x27: // LDC.L @Rm+,VBR 0100mmmm00100111
+ tmp = SHR_VBR;
+ opd->cycles = 3;
+ break;
+ default:
+ goto undefined;
+ }
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK2(GET_Rn(), tmp);
+ break;
+ case 0x08:
+ case 0x09:
+ switch (GET_Fx())
+ {
+ case 0:
+ // SHLL2 Rn 0100nnnn00001000
+ // SHLR2 Rn 0100nnnn00001001
+ break;
+ case 1:
+ // SHLL8 Rn 0100nnnn00011000
+ // SHLR8 Rn 0100nnnn00011001
+ break;
+ case 2:
+ // SHLL16 Rn 0100nnnn00101000
+ // SHLR16 Rn 0100nnnn00101001
+ break;
+ default:
+ goto undefined;
+ }
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK1(GET_Rn());
+ break;
+ case 0x0a:
+ switch (GET_Fx())
+ {
+ case 0: // LDS Rm,MACH 0100mmmm00001010
+ tmp = SHR_MACH;
+ break;
+ case 1: // LDS Rm,MACL 0100mmmm00011010
+ tmp = SHR_MACL;
+ break;
+ case 2: // LDS Rm,PR 0100mmmm00101010
+ tmp = SHR_PR;
+ break;
+ default:
+ goto undefined;
+ }
+ opd->op = OP_MOVE;
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK1(tmp);
+ break;
+ case 0x0b:
+ switch (GET_Fx())
+ {
+ case 0: // JSR @Rm 0100mmmm00001011
+ opd->dest = BITMASK1(SHR_PR);
+ case 2: // JMP @Rm 0100mmmm00101011
+ opd->op = OP_BRANCH_R;
+ opd->rm = GET_Rn();
+ opd->source = BITMASK1(opd->rm);
+ opd->dest |= BITMASK1(SHR_PC);
+ opd->cycles = 2;
+ next_is_delay = 1;
+ end_block = 1;
+ break;
+ case 1: // TAS.B @Rn 0100nnnn00011011
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK1(SHR_T);
+ opd->cycles = 4;
+ break;
+ default:
+ goto undefined;
+ }
+ break;
+ case 0x0e:
+ switch (GET_Fx())
+ {
+ case 0: // LDC Rm,SR 0100mmmm00001110
+ tmp = SHR_SR;
+ break;
+ case 1: // LDC Rm,GBR 0100mmmm00011110
+ tmp = SHR_GBR;
+ break;
+ case 2: // LDC Rm,VBR 0100mmmm00101110
+ tmp = SHR_VBR;
+ break;
+ default:
+ goto undefined;
+ }
+ opd->op = OP_MOVE;
+ opd->source = BITMASK1(GET_Rn());
+ opd->dest = BITMASK1(tmp);
+ break;
+ case 0x0f:
+ // MAC.W @Rm+,@Rn+ 0100nnnnmmmm1111
+ opd->source = BITMASK5(GET_Rm(), GET_Rn(), SHR_SR, SHR_MACL, SHR_MACH);
+ opd->dest = BITMASK4(GET_Rm(), GET_Rn(), SHR_MACL, SHR_MACH);
+ opd->cycles = 3;
+ break;
+ default:
+ goto undefined;
+ }