sh2 drc, register cache optimisations

This commit is contained in:
kub 2019-05-22 21:04:59 +02:00
parent 49daa9e093
commit adf39a13f9
3 changed files with 197 additions and 151 deletions

View file

@ -138,10 +138,11 @@ enum op_types {
OP_MOVE, // register move OP_MOVE, // register move
OP_LOAD_CONST,// load const to register OP_LOAD_CONST,// load const to register
OP_LOAD_POOL, // literal pool load, imm is address OP_LOAD_POOL, // literal pool load, imm is address
OP_MOVA, OP_MOVA, // MOVA instruction
OP_SLEEP, OP_SLEEP, // SLEEP instruction
OP_RTE, OP_RTE, // RTE instruction
OP_TRAPA, OP_TRAPA, // TRAPA instruction
OP_LDC, // LDC instruction
OP_UNDEFINED, OP_UNDEFINED,
}; };
@ -552,31 +553,25 @@ static int dr_is_rom(u32 a)
return (a & 0xc6000000) == 0x02000000 && (a & 0x3f0000) < 0x3e0000; return (a & 0xc6000000) == 0x02000000 && (a & 0x3f0000) < 0x3e0000;
} }
static int dr_ctx_get_mem_ptr(u32 a, u32 *mask) static int dr_ctx_get_mem_ptr(SH2 *sh2, u32 a, u32 *mask)
{ {
void *memptr;
int poffs = -1; int poffs = -1;
if ((a & ~0x7ff) == 0) { // check if region is mapped memory
// BIOS memptr = p32x_sh2_get_mem_ptr(a, mask, sh2);
if (memptr == NULL /*|| (a & ((1 << SH2_READ_SHIFT)-1) & ~*mask) != 0*/)
return poffs;
if (memptr == sh2->p_bios) // BIOS
poffs = offsetof(SH2, p_bios); poffs = offsetof(SH2, p_bios);
*mask = 0x7ff; else if (memptr == sh2->p_da) // data array
}
else if ((a & 0xfffff000) == 0xc0000000) {
// data array
// FIXME: access sh2->data_array instead // FIXME: access sh2->data_array instead
poffs = offsetof(SH2, p_da); poffs = offsetof(SH2, p_da);
*mask = 0xfff; else if (memptr == sh2->p_sdram) // SDRAM
}
else if ((a & 0xc6000000) == 0x06000000) {
// SDRAM
poffs = offsetof(SH2, p_sdram); poffs = offsetof(SH2, p_sdram);
*mask = 0x03ffff; else if (memptr == sh2->p_rom) // ROM
}
else if ((a & 0xc6000000) == 0x02000000) {
// ROM
poffs = offsetof(SH2, p_rom); poffs = offsetof(SH2, p_rom);
*mask = 0x3fffff;
}
return poffs; return poffs;
} }
@ -1365,6 +1360,7 @@ static u32 rcache_locked;
static u32 rcache_hint_soon; static u32 rcache_hint_soon;
static u32 rcache_hint_late; static u32 rcache_hint_late;
static u32 rcache_hint_write; static u32 rcache_hint_write;
static u32 rcache_hint_clean;
#define rcache_hint (rcache_hint_soon|rcache_hint_late) #define rcache_hint (rcache_hint_soon|rcache_hint_late)
static void rcache_unmap_vreg(int x) static void rcache_unmap_vreg(int x)
@ -1396,16 +1392,19 @@ static void rcache_clean_vreg(int x)
emith_move_r_r(cache_regs[guest_regs[r].sreg].hreg, cache_regs[guest_regs[r].vreg].hreg); emith_move_r_r(cache_regs[guest_regs[r].sreg].hreg, cache_regs[guest_regs[r].vreg].hreg);
rcache_remove_vreg_alias(x, r); rcache_remove_vreg_alias(x, r);
rcache_add_vreg_alias(guest_regs[r].sreg, r); rcache_add_vreg_alias(guest_regs[r].sreg, r);
cache_regs[guest_regs[r].sreg].flags |= HRF_DIRTY;
} else { } else {
// must evict since sreg is locked // must evict since sreg is locked
emith_ctx_write(cache_regs[x].hreg, r * 4); emith_ctx_write(cache_regs[x].hreg, r * 4);
guest_regs[r].flags &= ~GRF_DIRTY;
guest_regs[r].vreg = -1; guest_regs[r].vreg = -1;
} }
} }
} else } else if (~rcache_hint_write & (1 << r)) {
emith_ctx_write(cache_regs[x].hreg, r * 4); emith_ctx_write(cache_regs[x].hreg, r * 4);
} guest_regs[r].flags &= ~GRF_DIRTY;
guest_regs[r].flags &= ~GRF_DIRTY;) }
})
} }
} }
@ -1654,7 +1653,7 @@ static int rcache_get_reg_(sh2_reg_e r, rc_gr_mode mode, int do_locking, int *hr
(cache_regs[i].flags & HRF_LOCKED) || (cache_regs[i].flags & HRF_LOCKED) ||
(cache_regs[i].type == HR_STATIC && !(guest_regs[r].flags & GRF_STATIC))) { (cache_regs[i].type == HR_STATIC && !(guest_regs[r].flags & GRF_STATIC))) {
// need to split up. take reg out here to avoid unnecessary writebacks // need to split up. take reg out here to avoid unnecessary writebacks
cache_regs[i].gregs &= ~(1 << r); rcache_remove_vreg_alias(i, r);
split = i; split = i;
} else { } else {
// aliases not needed anytime soon, remove them // aliases not needed anytime soon, remove them
@ -1809,7 +1808,8 @@ static int rcache_get_reg_arg(int arg, sh2_reg_e r, int *hr)
// r is needed later on anyway // r is needed later on anyway
srcr = rcache_get_reg_(r, RC_GR_READ, 0, NULL); srcr = rcache_get_reg_(r, RC_GR_READ, 0, NULL);
is_cached = (cache_regs[reg_map_host[srcr]].type == HR_CACHED); is_cached = (cache_regs[reg_map_host[srcr]].type == HR_CACHED);
} else if ((guest_regs[r].flags & GRF_CDIRTY) && gconst_get(r, &val)) { } else if (!(rcache_hint_clean & (1 << r)) &&
(guest_regs[r].flags & GRF_CDIRTY) && gconst_get(r, &val)) {
// r has an uncomitted const - load into arg, but keep constant uncomitted // r has an uncomitted const - load into arg, but keep constant uncomitted
srcr = dstr; srcr = dstr;
is_const = 1; is_const = 1;
@ -1822,7 +1822,7 @@ static int rcache_get_reg_arg(int arg, sh2_reg_e r, int *hr)
srcr = dstr; srcr = dstr;
if (rcache_static & (1 << r)) if (rcache_static & (1 << r))
srcr = rcache_get_reg_(r, RC_GR_READ, 0, NULL); srcr = rcache_get_reg_(r, RC_GR_READ, 0, NULL);
else if (gconst_try_read(guest_regs[r].vreg, r)) else if (gconst_try_read(dstid, r))
dirty = 1; dirty = 1;
else else
emith_ctx_read(srcr, r * 4); emith_ctx_read(srcr, r * 4);
@ -1856,14 +1856,18 @@ static int rcache_get_reg_arg(int arg, sh2_reg_e r, int *hr)
} else if (hr != NULL) { } else if (hr != NULL) {
// caller will modify arg, so it will soon be out of sync with r // caller will modify arg, so it will soon be out of sync with r
if (dirty || src_dirty) { if (dirty || src_dirty) {
emith_ctx_write(dstr, r * 4); // must clean since arg will be modified if (~rcache_hint_write & (1 << r)) {
guest_regs[r].flags &= ~GRF_DIRTY; emith_ctx_write(dstr, r * 4); // must clean since arg will be modified
guest_regs[r].flags &= ~GRF_DIRTY;
}
} }
} else if (guest_regs[r].vreg < 0) { } else {
// keep arg as vreg for r // keep arg as vreg for r
cache_regs[dstid].type = HR_CACHED; cache_regs[dstid].type = HR_CACHED;
cache_regs[dstid].gregs = 1 << r; if (guest_regs[r].vreg < 0) {
guest_regs[r].vreg = dstid; cache_regs[dstid].gregs = 1 << r;
guest_regs[r].vreg = dstid;
}
if (dirty || src_dirty) { // mark as modifed for cleaning later on if (dirty || src_dirty) { // mark as modifed for cleaning later on
cache_regs[dstid].flags |= HRF_DIRTY; cache_regs[dstid].flags |= HRF_DIRTY;
guest_regs[r].flags |= GRF_DIRTY; guest_regs[r].flags |= GRF_DIRTY;
@ -2057,9 +2061,9 @@ static void rcache_clean_mask(u32 mask)
{ {
int i; int i;
// XXX consider gconst? if (!(mask &= ~rcache_static))
if (!(mask &= ~rcache_static & ~gconst_dirty_mask()))
return; return;
rcache_hint_clean |= mask;
// clean only vregs where all aliases are covered by the mask // clean only vregs where all aliases are covered by the mask
for (i = 0; i < ARRAY_SIZE(cache_regs); i++) for (i = 0; i < ARRAY_SIZE(cache_regs); i++)
@ -2120,7 +2124,7 @@ static void rcache_invalidate(void)
} }
rcache_counter = 0; rcache_counter = 0;
rcache_hint_soon = rcache_hint_late = rcache_hint_write = 0; rcache_hint_soon = rcache_hint_late = rcache_hint_write = rcache_hint_clean = 0;
gconst_invalidate(); gconst_invalidate();
} }
@ -2164,48 +2168,76 @@ static void rcache_init(void)
// --------------------------------------------------------------- // ---------------------------------------------------------------
static int emit_get_rbase_and_offs(SH2 *sh2, u32 a, u32 *offs) // NB may return either REG or TEMP
static int emit_get_rbase_and_offs(SH2 *sh2, sh2_reg_e r, int rmod, u32 *offs)
{ {
u32 omask = 0xff; // offset mask, XXX: ARM oriented.. uptr omask = 0xff; // offset mask, XXX: ARM oriented..
u32 mask = 0; u32 mask = 0;
u32 a;
int poffs; int poffs;
int hr; int hr, hr2;
unsigned long la; uptr la;
poffs = dr_ctx_get_mem_ptr(a, &mask); // is r constant and points to a memory region?
if (! gconst_get(r, &a))
return -1;
poffs = dr_ctx_get_mem_ptr(sh2, a, &mask);
if (poffs == -1) if (poffs == -1)
return -1; return -1;
hr = rcache_get_tmp();
if (mask < 0x1000) { if (mask < 0x1000) {
// can't access data array or BIOS directly from ROM or SDRAM, // data array or BIOS, can't safely access directly since translated code
// since code may run on both SH2s (tcache_id of translation block needed)) // may run on both SH2s
hr = rcache_get_tmp();
emith_ctx_read_ptr(hr, poffs); emith_ctx_read_ptr(hr, poffs);
a += *offs;
if (a & mask & ~omask) if (a & mask & ~omask)
emith_add_r_r_ptr_imm(hr, hr, a & mask & ~omask); emith_add_r_r_ptr_imm(hr, hr, a & mask & ~omask);
*offs = a & omask; *offs = a & omask;
return hr;
}
la = (uptr)*(void **)((char *)sh2 + poffs);
// accessing ROM or SDRAM, code location doesn't matter. The host address
// for these should be mmapped to be equal to the SH2 address.
// if r is in rcache or needed soon anyway, and offs is relative to region
// use rcached const to avoid loading a literal on ARM
if ((guest_regs[r].vreg >= 0 || ((guest_regs[r].flags & GRF_CDIRTY) &&
((rcache_hint_soon|rcache_hint_clean) & (1 << r)))) && !(*offs & ~mask)) {
u32 odd = a & 1; // need to fix odd address for correct byte addressing
la -= (s32)((a & ~mask) - *offs - odd); // diff between reg and memory
// if reg is modified later on, allocate it RMW to remove aliases here
// else the aliases vreg stays locked and a vreg shortage may occur.
hr = hr2 = rcache_get_reg(r, rmod ? RC_GR_RMW : RC_GR_READ, NULL);
if ((la & ~omask) - odd) {
hr = rcache_get_tmp();
emith_add_r_r_ptr_imm(hr, hr2, (la & ~omask) - odd);
}
*offs = (la & omask);
} else { } else {
// known fixed host address // known fixed host address
la = (unsigned long)*(void **)((char *)sh2 + poffs) + (a & mask); la += (a + *offs) & mask;
*offs = la & omask; hr = rcache_get_tmp();
emith_move_r_ptr_imm(hr, la & ~omask); emith_move_r_ptr_imm(hr, la & ~omask);
*offs = la & omask;
} }
return hr; return hr;
} }
// read const data from const ROM address // read const data from const ROM address
static int emit_get_rom_data(sh2_reg_e r, u32 offs, int size, u32 *val) static int emit_get_rom_data(SH2 *sh2, sh2_reg_e r, u32 offs, int size, u32 *val)
{ {
u32 tmp; u32 a, mask;
*val = 0; *val = 0;
if (gconst_get(r, &tmp)) { if (gconst_get(r, &a)) {
tmp += offs; a += offs;
if (dr_is_rom(tmp)) { // check if rom is memory mapped (not bank switched), and address is in rom
if (dr_is_rom(a) && p32x_sh2_get_mem_ptr(a, &mask, sh2)) {
switch (size & MF_SIZEMASK) { switch (size & MF_SIZEMASK) {
case 0: *val = (s8)p32x_sh2_read8(tmp, sh2s); break; // 8 case 0: *val = (s8)p32x_sh2_read8(a, sh2s); break; // 8
case 1: *val = (s16)p32x_sh2_read16(tmp, sh2s); break; // 16 case 1: *val = (s16)p32x_sh2_read16(a, sh2s); break; // 16
case 2: *val = p32x_sh2_read32(tmp, sh2s); break; // 32 case 2: *val = p32x_sh2_read32(a, sh2s); break; // 32
} }
return 1; return 1;
} }
@ -2315,10 +2347,10 @@ static void emit_memhandler_write(int size)
static int emit_memhandler_read_rr(SH2 *sh2, sh2_reg_e rd, sh2_reg_e rs, u32 offs, int size) static int emit_memhandler_read_rr(SH2 *sh2, sh2_reg_e rd, sh2_reg_e rs, u32 offs, int size)
{ {
int hr, hr2; int hr, hr2;
u32 val, offs2; u32 val;
#if PROPAGATE_CONSTANTS #if PROPAGATE_CONSTANTS
if (emit_get_rom_data(rs, offs, size, &val)) { if (emit_get_rom_data(sh2, rs, offs, size, &val)) {
if (rd == SHR_TMP) { if (rd == SHR_TMP) {
hr2 = rcache_get_tmp(); hr2 = rcache_get_tmp();
emith_move_r_imm(hr2, val); emith_move_r_imm(hr2, val);
@ -2331,47 +2363,49 @@ static int emit_memhandler_read_rr(SH2 *sh2, sh2_reg_e rd, sh2_reg_e rs, u32 off
return hr2; return hr2;
} }
if (gconst_get(rs, &val)) { hr = emit_get_rbase_and_offs(sh2, rs, size & MF_POSTINCR, &offs);
hr = emit_get_rbase_and_offs(sh2, val + offs, &offs2); if (hr != -1) {
if (hr != -1) { if (rd == SHR_TMP)
if (rd == SHR_TMP) hr2 = rcache_get_tmp();
hr2 = rcache_get_tmp(); else
else hr2 = rcache_get_reg(rd, RC_GR_WRITE, NULL);
hr2 = rcache_get_reg(rd, RC_GR_WRITE, NULL); switch (size & MF_SIZEMASK) {
switch (size & MF_SIZEMASK) { case 0: emith_read8s_r_r_offs(hr2, hr, offs ^ 1); break; // 8
case 0: // 8 case 1: emith_read16s_r_r_offs(hr2, hr, offs); break; // 16
emith_read8s_r_r_offs(hr2, hr, offs2 ^ 1); case 2: emith_read_r_r_offs(hr2, hr, offs); emith_ror(hr2, hr2, 16); break;
break;
case 1: // 16
emith_read16s_r_r_offs(hr2, hr, offs2);
break;
case 2: // 32
emith_read_r_r_offs(hr2, hr, offs2);
emith_ror(hr2, hr2, 16);
break;
}
rcache_free_tmp(hr);
if (size & MF_POSTINCR)
gconst_new(rs, val + (1 << (size & MF_SIZEMASK)));
return hr2;
} }
if (cache_regs[reg_map_host[hr]].type == HR_TEMP) // may also return REG
rcache_free_tmp(hr);
if (size & MF_POSTINCR) {
int isgc = gconst_get(rs, &val);
if (!isgc || guest_regs[rs].vreg >= 0) {
// already loaded
hr = rcache_get_reg(rs, RC_GR_RMW, NULL);
emith_add_r_r_imm(hr, hr, 1 << (size & MF_SIZEMASK));
if (isgc)
gconst_set(rs, val + (1 << (size & MF_SIZEMASK)));
} else
gconst_new(rs, val + (1 << (size & MF_SIZEMASK)));
}
return hr2;
} }
#endif #endif
if (gconst_get(rs, &val) && (!(size & MF_POSTINCR) /*|| !(rcache_hint_soon & (1 << rs))*/)) {
if (gconst_get(rs, &val) && guest_regs[rs].vreg < 0 && !(rcache_hint_soon & (1 << rs))) {
hr = rcache_get_tmp_arg(0); hr = rcache_get_tmp_arg(0);
emith_move_r_imm(hr, val + offs); emith_move_r_imm(hr, val + offs);
if (size & MF_POSTINCR) if (size & MF_POSTINCR)
gconst_new(rs, val + (1 << (size & MF_SIZEMASK))); gconst_new(rs, val + (1 << (size & MF_SIZEMASK)));
} else if (offs || (size & MF_POSTINCR)) { } else if (size & MF_POSTINCR) {
hr = rcache_get_tmp_arg(0);
hr2 = rcache_get_reg(rs, RC_GR_RMW, NULL);
emith_add_r_r_imm(hr, hr2, offs);
emith_add_r_imm(hr2, 1 << (size & MF_SIZEMASK));
} else {
hr = rcache_get_reg_arg(0, rs, &hr2); hr = rcache_get_reg_arg(0, rs, &hr2);
if (offs || hr != hr2) if (offs || hr != hr2)
emith_add_r_r_imm(hr, hr2, offs); emith_add_r_r_imm(hr, hr2, offs);
if (size & MF_POSTINCR) { }
hr = rcache_get_reg(rs, RC_GR_WRITE, NULL);
emith_add_r_r_imm(hr, hr2, 1 << (size & MF_SIZEMASK));
}
} else
rcache_get_reg_arg(0, rs, NULL);
hr = emit_memhandler_read(size); hr = emit_memhandler_read(size);
size &= MF_SIZEMASK; size &= MF_SIZEMASK;
@ -2405,7 +2439,7 @@ static void emit_memhandler_write_rr(SH2 *sh2, sh2_reg_e rd, sh2_reg_e rs, u32 o
} else } else
hr2 = rcache_get_reg_arg(1, rd, NULL); hr2 = rcache_get_reg_arg(1, rd, NULL);
if (gconst_get(rs, &val) && (!(size & MF_PREDECR) /*|| !(rcache_hint_soon & (1 << rs))*/)) { if (gconst_get(rs, &val) && guest_regs[rs].vreg < 0 && !(rcache_hint_soon & (1 << rs))) {
if (size & MF_PREDECR) { if (size & MF_PREDECR) {
val -= 1 << (size & MF_SIZEMASK); val -= 1 << (size & MF_SIZEMASK);
gconst_new(rs, val); gconst_new(rs, val);
@ -2551,7 +2585,7 @@ static void emit_block_entry(void)
cycles = 0; \ cycles = 0; \
} }
static void *dr_get_pc_base(u32 pc, int is_slave); static void *dr_get_pc_base(u32 pc, SH2 *sh2);
static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id) static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
{ {
@ -2591,7 +2625,7 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
base_pc = sh2->pc; base_pc = sh2->pc;
// get base/validate PC // get base/validate PC
dr_pc_base = dr_get_pc_base(base_pc, sh2->is_slave); dr_pc_base = dr_get_pc_base(base_pc, sh2);
if (dr_pc_base == (void *)-1) { if (dr_pc_base == (void *)-1) {
printf("invalid PC, aborting: %08x\n", base_pc); printf("invalid PC, aborting: %08x\n", base_pc);
// FIXME: be less destructive // FIXME: be less destructive
@ -2637,6 +2671,8 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
op_flags[i] &= ~OF_BTARGET; op_flags[i] &= ~OF_BTARGET;
if (op_flags[i] & OF_BTARGET) if (op_flags[i] & OF_BTARGET)
ADD_TO_ARRAY(branch_target_pc, branch_target_count, pc, ); ADD_TO_ARRAY(branch_target_pc, branch_target_count, pc, );
if (ops[i].op == OP_LDC && (ops[i].dest & BITMASK1(SHR_SR)) && pc+2 < end_pc)
op_flags[i+1] |= OF_BTARGET; // RTE entrypoint in case of SR(IMASK) change
#if LOOP_DETECTION #if LOOP_DETECTION
// loop types detected: // loop types detected:
// 1. target: ... BRA target -> idle loop // 1. target: ... BRA target -> idle loop
@ -2930,7 +2966,8 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
u32 late = 0; // regs read by future ops u32 late = 0; // regs read by future ops
u32 write = 0; // regs written to (to detect write before read) u32 write = 0; // regs written to (to detect write before read)
u32 soon = 0; // regs read soon u32 soon = 0; // regs read soon
tmp = OP_ISBRANCH(opd[0].op); // branch insn detected tmp = (OP_ISBRANCH(opd[0].op) || opd[0].op == OP_RTE || // branching insns
opd[0].op == OP_TRAPA || opd[0].op == OP_UNDEFINED);
for (v = 1; v <= 9; v++) { for (v = 1; v <= 9; v++) {
// no sense in looking any further than the next rcache flush // no sense in looking any further than the next rcache flush
if (pc + 2*v < end_pc && !(op_flags[i+v] & OF_BTARGET) && if (pc + 2*v < end_pc && !(op_flags[i+v] & OF_BTARGET) &&
@ -2944,7 +2981,6 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
rcache_clean_mask(rcache_dirty_mask() & ~tmp2); rcache_clean_mask(rcache_dirty_mask() & ~tmp2);
break; break;
} }
// XXX must also include test-irq locations!
tmp |= (OP_ISBRANCH(opd[v].op) || opd[v].op == OP_RTE || tmp |= (OP_ISBRANCH(opd[v].op) || opd[v].op == OP_RTE ||
opd[v].op == OP_TRAPA || opd[v].op == OP_UNDEFINED); opd[v].op == OP_TRAPA || opd[v].op == OP_UNDEFINED);
// regs needed in the next few instructions // regs needed in the next few instructions
@ -2953,7 +2989,8 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
} }
rcache_set_hint_soon(late); // insns 1-3 rcache_set_hint_soon(late); // insns 1-3
rcache_set_hint_late(late & ~soon); // insns 4-9 rcache_set_hint_late(late & ~soon); // insns 4-9
rcache_set_hint_write(write & ~(late|soon)); // next access is write rcache_set_hint_write(write & ~(late|soon) & ~opd[0].source);
// overwritten without being used
} }
rcache_set_locked(opd[0].source); // try not to evict src regs for this op rcache_set_locked(opd[0].source); // try not to evict src regs for this op
@ -2973,32 +3010,22 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
case OP_BRANCH_R: case OP_BRANCH_R:
if (opd->dest & BITMASK1(SHR_PR)) if (opd->dest & BITMASK1(SHR_PR))
emit_move_r_imm32(SHR_PR, pc + 2); emit_move_r_imm32(SHR_PR, pc + 2);
if (gconst_get(opd->rm, &u)) { emit_move_r_r(SHR_PC, opd->rm);
opd->imm = u; drcf.pending_branch_indirect = 1;
drcf.pending_branch_direct = 1;
} else {
emit_move_r_r(SHR_PC, opd->rm);
drcf.pending_branch_indirect = 1;
}
goto end_op; goto end_op;
case OP_BRANCH_RF: case OP_BRANCH_RF:
if (gconst_get(GET_Rn(), &u)) { tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ, NULL);
if (opd->dest & BITMASK1(SHR_PR)) tmp = rcache_get_reg(SHR_PC, RC_GR_WRITE, NULL);
emit_move_r_imm32(SHR_PR, pc + 2); emith_move_r_imm(tmp, pc + 2);
opd->imm = pc + 2 + u; if (opd->dest & BITMASK1(SHR_PR)) {
drcf.pending_branch_direct = 1; tmp3 = rcache_get_reg(SHR_PR, RC_GR_WRITE, NULL);
} else { emith_move_r_r(tmp3, tmp);
tmp2 = rcache_get_reg(GET_Rn(), RC_GR_READ, NULL);
tmp = rcache_get_reg(SHR_PC, RC_GR_WRITE, NULL);
emith_move_r_imm(tmp, pc + 2);
if (opd->dest & BITMASK1(SHR_PR)) {
tmp3 = rcache_get_reg(SHR_PR, RC_GR_WRITE, NULL);
emith_move_r_r(tmp3, tmp);
}
emith_add_r_r(tmp, tmp2);
drcf.pending_branch_indirect = 1;
} }
emith_add_r_r(tmp, tmp2);
if (gconst_get(GET_Rn(), &u))
gconst_set(SHR_PC, pc + 2 + u);
drcf.pending_branch_indirect = 1;
goto end_op; goto end_op;
case OP_SLEEP: // SLEEP 0000000000011011 case OP_SLEEP: // SLEEP 0000000000011011
@ -3041,10 +3068,7 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
// obtain new PC // obtain new PC
emit_memhandler_read_rr(sh2, SHR_PC, SHR_VBR, opd->imm * 4, 2); emit_memhandler_read_rr(sh2, SHR_PC, SHR_VBR, opd->imm * 4, 2);
// indirect jump -> back to dispatcher // indirect jump -> back to dispatcher
sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL); drcf.pending_branch_indirect = 1;
FLUSH_CYCLES(sr);
rcache_flush();
emith_jump(sh2_drc_dispatcher);
goto end_op; goto end_op;
case OP_LOAD_POOL: case OP_LOAD_POOL:
@ -3483,7 +3507,7 @@ static void REGPARM(2) *sh2_translate(SH2 *sh2, int tcache_id)
if (drcf.delay_reg == -1) if (drcf.delay_reg == -1)
drcf.delay_reg = GET_Rn(); drcf.delay_reg = GET_Rn();
else else
drcf.loop_type = 0; drcf.polling = drcf.loop_type = 0;
} }
#endif #endif
emith_bic_r_imm(sr, T); emith_bic_r_imm(sr, T);
@ -3925,8 +3949,6 @@ end_op:
emit_move_r_imm32(SHR_PC, pc); emit_move_r_imm32(SHR_PC, pc);
rcache_flush(); rcache_flush();
emith_call(sh2_drc_test_irq); emith_call(sh2_drc_test_irq);
if (pc < end_pc) // mark next insns as entry point for RTE
op_flags[i+1] |= OF_BTARGET;
drcf.test_irq = 0; drcf.test_irq = 0;
} }
@ -3950,7 +3972,7 @@ end_op:
{ {
// idle or delay loop // idle or delay loop
emith_sh2_delay_loop(cycles, drcf.delay_reg); emith_sh2_delay_loop(cycles, drcf.delay_reg);
drcf.loop_type = 0; drcf.polling = drcf.loop_type = 0;
} }
#endif #endif
@ -4011,15 +4033,30 @@ end_op:
drcf.pending_branch_direct = 0; drcf.pending_branch_direct = 0;
if (target_pc >= base_pc && target_pc < pc) if (target_pc >= base_pc && target_pc < pc)
drcf.loop_type = 0; drcf.polling = drcf.loop_type = 0;
} }
else if (drcf.pending_branch_indirect) { else if (drcf.pending_branch_indirect) {
struct op_data *opd_b =
(op_flags[i] & OF_DELAY_OP) ? opd-1 : opd;
void *target;
u32 target_pc;
sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL); sr = rcache_get_reg(SHR_SR, RC_GR_RMW, NULL);
FLUSH_CYCLES(sr); FLUSH_CYCLES(sr);
rcache_flush(); rcache_clean();
emith_jump(sh2_drc_dispatcher); if (gconst_get(SHR_PC, &target_pc)) {
// JMP const, treat like unconditional direct branch
target = dr_prepare_ext_branch(block->entryp, target_pc, sh2->is_slave, tcache_id);
if (target == NULL)
return NULL;
emith_jump_patchable(target);
} else {
// JMP
emith_jump(sh2_drc_dispatcher);
}
rcache_invalidate();
drcf.pending_branch_indirect = 0; drcf.pending_branch_indirect = 0;
drcf.loop_type = 0; drcf.polling = drcf.loop_type = 0;
} }
do_host_disasm(tcache_id); do_host_disasm(tcache_id);
@ -4836,33 +4873,12 @@ void sh2_drc_finish(SH2 *sh2)
#endif /* DRC_SH2 */ #endif /* DRC_SH2 */
static void *dr_get_pc_base(u32 pc, int is_slave) static void *dr_get_pc_base(u32 pc, SH2 *sh2)
{ {
void *ret = NULL; void *ret = NULL;
u32 mask = 0; u32 mask = 0;
if ((pc & ~0x7ff) == 0) { ret = p32x_sh2_get_mem_ptr(pc, &mask, sh2);
// 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) if (ret == NULL)
return (void *)-1; // NULL is valid value return (void *)-1; // NULL is valid value
@ -4889,7 +4905,7 @@ u16 scan_block(u32 base_pc, int is_slave, u8 *op_flags, u32 *end_pc_out,
memset(op_flags, 0, sizeof(*op_flags) * BLOCK_INSN_LIMIT); memset(op_flags, 0, sizeof(*op_flags) * BLOCK_INSN_LIMIT);
op_flags[0] |= OF_BTARGET; // block start is always a target op_flags[0] |= OF_BTARGET; // block start is always a target
dr_pc_base = dr_get_pc_base(base_pc, is_slave); dr_pc_base = dr_get_pc_base(base_pc, &sh2s[!!is_slave]);
// 1st pass: disassemble // 1st pass: disassemble
for (i = 0, pc = base_pc; ; i++, pc += 2) { for (i = 0, pc = base_pc; ; i++, pc += 2) {
@ -5274,14 +5290,17 @@ u16 scan_block(u32 base_pc, int is_slave, u8 *op_flags, u32 *end_pc_out,
break; break;
case 0x07: // LDC.L @Rm+,SR 0100mmmm00000111 case 0x07: // LDC.L @Rm+,SR 0100mmmm00000111
tmp = SHR_SR; tmp = SHR_SR;
opd->op = OP_LDC;
opd->cycles = 3; opd->cycles = 3;
break; break;
case 0x17: // LDC.L @Rm+,GBR 0100mmmm00010111 case 0x17: // LDC.L @Rm+,GBR 0100mmmm00010111
tmp = SHR_GBR; tmp = SHR_GBR;
opd->op = OP_LDC;
opd->cycles = 3; opd->cycles = 3;
break; break;
case 0x27: // LDC.L @Rm+,VBR 0100mmmm00100111 case 0x27: // LDC.L @Rm+,VBR 0100mmmm00100111
tmp = SHR_VBR; tmp = SHR_VBR;
opd->op = OP_LDC;
opd->cycles = 3; opd->cycles = 3;
break; break;
default: default:
@ -5372,7 +5391,7 @@ u16 scan_block(u32 base_pc, int is_slave, u8 *op_flags, u32 *end_pc_out,
default: default:
goto undefined; goto undefined;
} }
opd->op = OP_MOVE; opd->op = OP_LDC;
opd->source = BITMASK1(GET_Rn()); opd->source = BITMASK1(GET_Rn());
opd->dest = BITMASK1(tmp); opd->dest = BITMASK1(tmp);
break; break;

View file

@ -1730,6 +1730,32 @@ void REGPARM(3) p32x_sh2_write32(u32 a, u32 d, SH2 *sh2)
wh(a, d, sh2); wh(a, d, sh2);
} }
void *p32x_sh2_get_mem_ptr(u32 a, u32 *mask, SH2 *sh2)
{
const sh2_memmap *mm = sh2->read8_map;
void *ret = (void *)-1;
u32 am;
mm += a >> SH2_READ_SHIFT;
am = a & ((1 << SH2_READ_SHIFT)-1);
if (!map_flag_set(mm->addr) && !(am & ~mm->mask)) {
// directly mapped memory (SDRAM, ROM, data array)
ret = (void *)(mm->addr << 1);
*mask = mm->mask;
} else if ((a & ~0x7ff) == 0) {
// BIOS, has handler function since it shares its segment with I/O
ret = sh2->is_slave ? Pico32xMem->sh2_rom_s.w : Pico32xMem->sh2_rom_m.w;
*mask = 0x7ff;
} else if ((a & 0xc6000000) == 0x02000000) {
// banked ROM. Return bank address
u32 bank = carthw_ssf2_banks[(a >> 19) & 7] << 19;
ret = sh2->p_rom + bank;
*mask = 0x07ffff;
}
return ret;
}
// ----------------------------------------------------------------- // -----------------------------------------------------------------
static void z80_md_bank_write_32x(unsigned int a, unsigned char d) static void z80_md_bank_write_32x(unsigned int a, unsigned char d)

View file

@ -933,6 +933,7 @@ void Pico32xMemStateLoaded(void);
void p32x_update_banks(void); void p32x_update_banks(void);
void p32x_m68k_poll_event(unsigned int flags); void p32x_m68k_poll_event(unsigned int flags);
void p32x_sh2_poll_memory(unsigned int a, SH2 *sh2); void p32x_sh2_poll_memory(unsigned int a, SH2 *sh2);
void *p32x_sh2_get_mem_ptr(unsigned int a, unsigned int *mask, SH2 *sh2);
void p32x_sh2_poll_event(SH2 *sh2, unsigned int flags, unsigned int m68k_cycles); void p32x_sh2_poll_event(SH2 *sh2, unsigned int flags, unsigned int m68k_cycles);
// 32x/draw.c // 32x/draw.c