+static void rcache_free_tmp(int hr)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+ if (reg_temp[i].hreg == hr)
+ break;
+
+ if (i == ARRAY_SIZE(reg_temp) || reg_temp[i].type != HR_TEMP) {
+ printf("rcache_free_tmp fail: #%i hr %d, type %d\n", i, hr, reg_temp[i].type);
+ return;
+ }
+
+ reg_temp[i].type = HR_FREE;
+ reg_temp[i].flags = 0;
+}
+
+static void rcache_unlock(int hr)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+ if (reg_temp[i].type == HR_CACHED && reg_temp[i].hreg == hr)
+ reg_temp[i].flags &= ~HRF_LOCKED;
+}
+
+static void rcache_unlock_all(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+ reg_temp[i].flags &= ~HRF_LOCKED;
+}
+
+static void rcache_clean(void)
+{
+ int i;
+ gconst_clean();
+
+ for (i = 0; i < ARRAY_SIZE(reg_temp); i++)
+ if (reg_temp[i].type == HR_CACHED && (reg_temp[i].flags & HRF_DIRTY)) {
+ // writeback
+ emith_ctx_write(reg_temp[i].hreg, reg_temp[i].greg * 4);
+ reg_temp[i].flags &= ~HRF_DIRTY;
+ }
+}
+
+static void rcache_invalidate(void)
+{
+ int i;
+ for (i = 0; i < ARRAY_SIZE(reg_temp); i++) {
+ reg_temp[i].type = HR_FREE;
+ reg_temp[i].flags = 0;
+ }
+ rcache_counter = 0;
+
+ gconst_invalidate();
+}
+
+static void rcache_flush(void)
+{
+ rcache_clean();
+ rcache_invalidate();
+}
+
+// ---------------------------------------------------------------
+
+static int emit_get_rbase_and_offs(u32 a, u32 *offs)
+{
+ u32 mask = 0;
+ int poffs;
+ int hr;
+
+ poffs = dr_ctx_get_mem_ptr(a, &mask);
+ if (poffs == -1)
+ return -1;
+
+ // XXX: could use some related reg
+ hr = rcache_get_tmp();
+ emith_ctx_read(hr, poffs);
+ emith_add_r_imm(hr, a & mask & ~0xff);
+ *offs = a & 0xff; // XXX: ARM oriented..
+ return hr;
+}
+
+static void emit_move_r_imm32(sh2_reg_e dst, u32 imm)
+{
+#if PROPAGATE_CONSTANTS
+ gconst_new(dst, imm);
+#else
+ int hr = rcache_get_reg(dst, RC_GR_WRITE);
+ emith_move_r_imm(hr, imm);
+#endif
+}
+
+static void emit_move_r_r(sh2_reg_e dst, sh2_reg_e src)
+{
+ int hr_d = rcache_get_reg(dst, RC_GR_WRITE);
+ int hr_s = rcache_get_reg(src, RC_GR_READ);
+
+ emith_move_r_r(hr_d, hr_s);
+}
+
+// T must be clear, and comparison done just before this
+static void emit_or_t_if_eq(int srr)
+{
+ EMITH_SJMP_START(DCOND_NE);
+ emith_or_r_imm_c(DCOND_EQ, srr, T);
+ EMITH_SJMP_END(DCOND_NE);
+}
+
+// arguments must be ready
+// reg cache must be clean before call
+static int emit_memhandler_read_(int size, int ram_check)
+{
+ int arg0, arg1;
+ host_arg2reg(arg0, 0);
+
+ rcache_clean();
+
+ // must writeback cycles for poll detection stuff
+ // FIXME: rm
+ if (reg_map_g2h[SHR_SR] != -1)
+ emith_ctx_write(reg_map_g2h[SHR_SR], SHR_SR * 4);
+
+ arg1 = rcache_get_tmp_arg(1);
+ emith_move_r_r(arg1, CONTEXT_REG);
+
+#ifndef PDB_NET
+ if (ram_check && Pico.rom == (void *)0x02000000 && Pico32xMem->sdram == (void *)0x06000000) {
+ int tmp = rcache_get_tmp();
+ emith_and_r_r_imm(tmp, arg0, 0xfb000000);
+ emith_cmp_r_imm(tmp, 0x02000000);
+ switch (size) {
+ case 0: // 8
+ EMITH_SJMP3_START(DCOND_NE);
+ emith_eor_r_imm_c(DCOND_EQ, arg0, 1);
+ emith_read8_r_r_offs_c(DCOND_EQ, arg0, arg0, 0);
+ EMITH_SJMP3_MID(DCOND_NE);
+ emith_call_cond(DCOND_NE, sh2_drc_read8);
+ EMITH_SJMP3_END();
+ break;
+ case 1: // 16
+ EMITH_SJMP3_START(DCOND_NE);
+ emith_read16_r_r_offs_c(DCOND_EQ, arg0, arg0, 0);
+ EMITH_SJMP3_MID(DCOND_NE);
+ emith_call_cond(DCOND_NE, sh2_drc_read16);
+ EMITH_SJMP3_END();
+ break;
+ case 2: // 32
+ EMITH_SJMP3_START(DCOND_NE);
+ emith_read_r_r_offs_c(DCOND_EQ, arg0, arg0, 0);
+ emith_ror_c(DCOND_EQ, arg0, arg0, 16);
+ EMITH_SJMP3_MID(DCOND_NE);
+ emith_call_cond(DCOND_NE, sh2_drc_read32);
+ EMITH_SJMP3_END();
+ break;
+ }
+ }
+ else
+#endif
+ {
+ switch (size) {
+ case 0: // 8
+ emith_call(sh2_drc_read8);
+ break;
+ case 1: // 16
+ emith_call(sh2_drc_read16);
+ break;
+ case 2: // 32
+ emith_call(sh2_drc_read32);
+ break;
+ }
+ }
+ 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);
+}
+
+static int emit_memhandler_read(int size)
+{
+ return emit_memhandler_read_(size, 1);
+}
+
+static int emit_memhandler_read_rr(sh2_reg_e rd, sh2_reg_e rs, u32 offs, int size)
+{
+ int hr, hr2, ram_check = 1;
+ u32 val, offs2;
+
+ if (gconst_get(rs, &val)) {
+ hr = emit_get_rbase_and_offs(val + offs, &offs2);
+ if (hr != -1) {
+ hr2 = rcache_get_reg(rd, RC_GR_WRITE);
+ switch (size) {
+ case 0: // 8
+ emith_read8_r_r_offs(hr2, hr, offs2 ^ 1);
+ emith_sext(hr2, hr2, 8);
+ break;
+ case 1: // 16
+ emith_read16_r_r_offs(hr2, hr, offs2);
+ emith_sext(hr2, hr2, 16);
+ break;
+ case 2: // 32
+ emith_read_r_r_offs(hr2, hr, offs2);
+ emith_ror(hr2, hr2, 16);
+ break;
+ }
+ rcache_free_tmp(hr);
+ return hr2;
+ }
+
+ ram_check = 0;
+ }
+
+ hr = rcache_get_reg_arg(0, rs);
+ if (offs != 0)
+ emith_add_r_imm(hr, offs);
+ hr = emit_memhandler_read_(size, ram_check);
+ hr2 = rcache_get_reg(rd, RC_GR_WRITE);
+ if (size != 2) {
+ emith_sext(hr2, hr, (size == 1) ? 16 : 8);
+ } else
+ emith_move_r_r(hr2, hr);
+ rcache_free_tmp(hr);
+
+ return hr2;
+}
+
+static void emit_memhandler_write(int size, u32 pc, int delay)
+{
+ 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
+ if (delay) {
+ emith_call(sh2_drc_write8_slot);
+ } else {
+ emit_move_r_imm32(SHR_PC, pc);
+ rcache_clean();
+ emith_call(sh2_drc_write8);
+ }
+ break;
+ case 1: // 16
+ if (delay) {
+ emith_call(sh2_drc_write16_slot);
+ } else {
+ emit_move_r_imm32(SHR_PC, pc);
+ rcache_clean();
+ emith_call(sh2_drc_write16);
+ }
+ break;
+ case 2: // 32
+ emith_move_r_r(ctxr, CONTEXT_REG);
+ 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();
+}
+
+// @(Rx,Ry)
+static int emit_indirect_indexed_read(int rx, int ry, int size)
+{
+ int a0, t;
+ a0 = rcache_get_reg_arg(0, rx);
+ t = rcache_get_reg(ry, RC_GR_READ);
+ emith_add_r_r(a0, t);
+ return emit_memhandler_read(size);
+}
+
+// read @Rn, @rm
+static void emit_indirect_read_double(u32 *rnr, u32 *rmr, int rn, int rm, int size)
+{
+ int tmp;
+
+ rcache_get_reg_arg(0, rn);
+ tmp = emit_memhandler_read(size);
+ emith_ctx_write(tmp, offsetof(SH2, drc_tmp));
+ rcache_free_tmp(tmp);
+ tmp = rcache_get_reg(rn, RC_GR_RMW);
+ emith_add_r_imm(tmp, 1 << size);
+ rcache_unlock(tmp);
+
+ rcache_get_reg_arg(0, rm);
+ *rmr = emit_memhandler_read(size);
+ *rnr = rcache_get_tmp();
+ emith_ctx_read(*rnr, offsetof(SH2, drc_tmp));
+ tmp = rcache_get_reg(rm, RC_GR_RMW);
+ emith_add_r_imm(tmp, 1 << size);
+ rcache_unlock(tmp);
+}
+
+static void emit_do_static_regs(int is_write, int tmpr)
+{
+ int i, r, count;
+
+ for (i = 0; i < ARRAY_SIZE(reg_map_g2h); i++) {
+ r = reg_map_g2h[i];
+ if (r == -1)
+ continue;
+
+ for (count = 1; i < ARRAY_SIZE(reg_map_g2h) - 1; i++, r++) {
+ if (reg_map_g2h[i + 1] != r + 1)
+ break;
+ count++;
+ }
+
+ if (count > 1) {
+ // i, r point to last item
+ if (is_write)
+ emith_ctx_write_multiple(r - count + 1, (i - count + 1) * 4, count, tmpr);
+ else
+ emith_ctx_read_multiple(r - count + 1, (i - count + 1) * 4, count, tmpr);
+ } else {
+ if (is_write)
+ emith_ctx_write(r, i * 4);
+ else
+ emith_ctx_read(r, i * 4);
+ }
+ }
+}
+
+static void emit_block_entry(void)
+{
+ int arg0;
+
+ host_arg2reg(arg0, 0);
+
+#if (DRC_DEBUG & 8) || defined(PDB)
+ int arg1, arg2;
+ host_arg2reg(arg1, 1);
+ host_arg2reg(arg2, 2);
+
+ 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));
+ emith_call(sh2_drc_log_entry);
+ rcache_invalidate();
+#endif
+ emith_tst_r_r(arg0, arg0);
+ EMITH_SJMP_START(DCOND_EQ);
+ emith_jump_reg_c(DCOND_NE, arg0);
+ EMITH_SJMP_END(DCOND_EQ);
+}
+
+#define DELAYED_OP \
+ drcf.delayed_op = 2
+
+#define DELAY_SAVE_T(sr) { \
+ emith_bic_r_imm(sr, T_save); \
+ emith_tst_r_imm(sr, T); \
+ EMITH_SJMP_START(DCOND_EQ); \
+ emith_or_r_imm_c(DCOND_NE, sr, T_save); \
+ EMITH_SJMP_END(DCOND_EQ); \
+ drcf.use_saved_t = 1; \
+}
+
+#define FLUSH_CYCLES(sr) \
+ if (cycles > 0) { \
+ emith_sub_r_imm(sr, cycles << 12); \
+ cycles = 0; \
+ }
+
+#define CHECK_UNHANDLED_BITS(mask) { \
+ if ((op & (mask)) != 0) \
+ goto default_; \
+}
+
+#define GET_Fx() \
+ ((op >> 4) & 0x0f)
+
+#define GET_Rm GET_Fx
+
+#define GET_Rn() \
+ ((op >> 8) & 0x0f)
+
+#define CHECK_FX_LT(n) \
+ if (GET_Fx() >= n) \
+ goto default_
+
+static void *dr_get_pc_base(u32 pc, int is_slave);
+
+static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
+{
+ // XXX: maybe use structs instead?
+ u32 branch_target_pc[MAX_LOCAL_BRANCHES];
+ void *branch_target_ptr[MAX_LOCAL_BRANCHES];
+ int branch_target_blkid[MAX_LOCAL_BRANCHES];
+ int branch_target_count = 0;
+ void *branch_patch_ptr[MAX_LOCAL_BRANCHES];
+ u32 branch_patch_pc[MAX_LOCAL_BRANCHES];
+ int branch_patch_count = 0;
+ u32 literal_addr[MAX_LITERALS];
+ int literal_addr_count = 0;
+ int pending_branch_cond = -1;
+ int pending_branch_pc = 0;
+ u8 op_flags[BLOCK_CYCLE_LIMIT];
+ struct {
+ u32 delayed_op:2;
+ u32 test_irq:1;
+ u32 use_saved_t:1; // delayed op modifies T
+ } drcf = { 0, };
+
+ // PC of current, first, last, last_target_blk SH2 insn
+ u32 pc, base_pc, end_pc, out_pc;
+ void *block_entry;
+ block_desc *this_block;
+ u16 *dr_pc_base;
+ int blkid_main = 0;
+ int skip_op = 0;
+ u32 tmp, tmp2;
+ int cycles;
+ int op;
+ int i;
+
+ base_pc = sh2->pc;
+
+ // get base/validate PC
+ dr_pc_base = dr_get_pc_base(base_pc, sh2->is_slave);
+ if (dr_pc_base == (void *)-1) {
+ printf("invalid PC, aborting: %08x\n", base_pc);
+ // FIXME: be less destructive
+ exit(1);
+ }
+
+ tcache_ptr = tcache_ptrs[tcache_id];
+
+ // predict tcache overflow
+ tmp = tcache_ptr - tcache_bases[tcache_id];
+ if (tmp > tcache_sizes[tcache_id] - MAX_BLOCK_SIZE) {
+ dbg(1, "tcache %d overflow", tcache_id);
+ return NULL;
+ }
+
+ // 1st pass: scan forward for local branches
+ scan_block(base_pc, sh2->is_slave, op_flags, &end_pc);
+
+ this_block = dr_add_block(base_pc, end_pc + MAX_LITERAL_OFFSET, // XXX
+ sh2->is_slave, &blkid_main);
+ if (this_block == NULL)
+ return NULL;
+
+ block_entry = tcache_ptr;
+ 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);
+
+ // collect branch_targets that don't land on delay slots
+ for (pc = base_pc; pc <= end_pc; pc += 2) {
+ if (!(OP_FLAGS(pc) & OF_TARGET))
+ continue;
+ if (OP_FLAGS(pc) & OF_DELAY_OP) {
+ OP_FLAGS(pc) &= ~OF_TARGET;
+ continue;
+ }
+ ADD_TO_ARRAY(branch_target_pc, branch_target_count, pc, break);
+ }
+
+ 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
+ out_pc = 0;
+ pc = base_pc;
+ for (cycles = 0; pc <= end_pc || drcf.delayed_op; )
+ {
+ u32 tmp3, tmp4, sr;
+
+ if (drcf.delayed_op > 0)
+ drcf.delayed_op--;
+
+ op = FETCH_OP(pc);
+
+ if ((OP_FLAGS(pc) & OF_TARGET) || pc == base_pc)
+ {
+ i = find_in_array(branch_target_pc, branch_target_count, pc);
+ if (pc != base_pc)
+ {
+ /* make "subblock" - just a mid-block entry */
+ block_desc *subblock;
+
+ sr = rcache_get_reg(SHR_SR, RC_GR_RMW);
+ FLUSH_CYCLES(sr);
+ // decide if to flush rcache
+ if ((op & 0xf0ff) == 0x4010 && FETCH_OP(pc + 2) == 0x8bfd) // DT; BF #-2
+ rcache_clean();
+ else
+ rcache_flush();
+ do_host_disasm(tcache_id);
+
+ 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, end_pc + MAX_LITERAL_OFFSET, // XXX
+ sh2->is_slave, &branch_target_blkid[i]);
+ if (subblock == NULL)
+ return NULL;
+
+ // since we made a block entry, link any other blocks that jump to current pc
+ dr_link_blocks(tcache_ptr, pc, tcache_id);
+ }
+ if (i >= 0)
+ branch_target_ptr[i] = tcache_ptr;
+
+ // must update PC
+ emit_move_r_imm32(SHR_PC, pc);
+ rcache_clean();
+
+ // check cycles
+ sr = rcache_get_reg(SHR_SR, RC_GR_READ);
+ emith_cmp_r_imm(sr, 0);
+ emith_jump_cond(DCOND_LE, sh2_drc_exit);
+ do_host_disasm(tcache_id);
+ rcache_unlock_all();
+ }