/*
* SH2 recompiler
* (C) notaz, 2009,2010,2013
- * (C) kub, 2018,2019,2020
+ * (C) kub, 2018-2024
*
* This work is licensed under the terms of MAME license.
* See COPYING file in the top-level directory.
#ifdef __arm__
// arm32 offset has an add/sub flag and an unsigned 8 bit value, which only
// allows values of [-255...255]. the value -256 thus can't be used.
- if (*offs + sign == 0) {
+ if (*offs < 0) { // TODO not working at all with negative offsets on ARM?
+ //if (*offs == -sign) {
la -= sign;
*offs += sign;
}
// 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);
+ poffs = dr_ctx_get_mem_ptr(sh2, a + *offs, &mask);
if (poffs == -1)
return -1;
}
#define FLUSH_CYCLES(sr) \
- if (cycles > 0) { \
+ if (cycles > 0) \
emith_sub_r_imm(sr, cycles << 12); \
- cycles = 0; \
- }
+ else if (cycles < 0) /* may happen after a branch not taken */ \
+ emith_add_r_imm(sr, -cycles << 12); \
+ cycles = 0; \
static void *dr_get_pc_base(u32 pc, SH2 *sh2);
static void sh2_smc_rm_blocks(u32 a, int len, int tcache_id, int free);
#if DIV_OPTIMIZER
if (div(opd).div1 == 16 && div(opd).ro == div(opd).rn) {
// divide 32/16
- tmp = rcache_get_tmp_arg(1);
- emith_add_r_r_ptr_imm(tmp, CONTEXT_REG, offsetof(SH2, drc_tmp));
rcache_get_reg_arg(0, div(opd).rn, NULL);
rcache_get_reg_arg(2, div(opd).rm, NULL);
+ tmp = rcache_get_tmp_arg(1);
+ emith_add_r_r_ptr_imm(tmp, CONTEXT_REG, offsetof(SH2, drc_tmp));
rcache_invalidate_tmp();
emith_abicall(sh2_drc_divu32);
tmp = rcache_get_tmp_ret();
emith_or_r_r_r(sr, sr, tmp3); // T
rcache_free_tmp(tmp3);
skip_op = div(opd).div1 + div(opd).rotcl;
+ cycles += skip_op;
}
else if (div(opd).div1 == 32 && div(opd).ro != div(opd).rn) {
// divide 64/32
tmp4 = rcache_get_reg(div(opd).ro, RC_GR_READ, NULL);
emith_ctx_write(tmp4, offsetof(SH2, drc_tmp));
rcache_free(tmp4);
- tmp = rcache_get_tmp_arg(1);
- emith_add_r_r_ptr_imm(tmp, CONTEXT_REG, offsetof(SH2, drc_tmp));
rcache_get_reg_arg(0, div(opd).rn, NULL);
rcache_get_reg_arg(2, div(opd).rm, NULL);
+ tmp = rcache_get_tmp_arg(1);
+ emith_add_r_r_ptr_imm(tmp, CONTEXT_REG, offsetof(SH2, drc_tmp));
rcache_invalidate_tmp();
emith_abicall(sh2_drc_divu64);
tmp = rcache_get_tmp_ret();
emith_or_r_r_lsl(sr, tmp3, Q_SHIFT);
rcache_free_tmp(tmp3);
skip_op = div(opd).div1 + div(opd).rotcl;
+ cycles += skip_op;
}
#endif
break;
#if DIV_OPTIMIZER
if (div(opd).div1 == 16 && div(opd).ro == div(opd).rn) {
// divide 32/16
- tmp = rcache_get_tmp_arg(1);
- emith_add_r_r_ptr_imm(tmp, CONTEXT_REG, offsetof(SH2, drc_tmp));
- rcache_get_reg_arg(0, div(opd).rn, NULL);
+ tmp = rcache_get_reg_arg(0, div(opd).rn, NULL);
tmp2 = rcache_get_reg_arg(2, div(opd).rm, NULL);
- tmp3 = rcache_get_tmp();
+ tmp3 = rcache_get_tmp_arg(1);
emith_lsr(tmp3, tmp2, 31);
emith_or_r_r_lsl(sr, tmp3, M_SHIFT); // M = Rm[31]
+ emith_add_r_r_ptr_imm(tmp3, CONTEXT_REG, offsetof(SH2, drc_tmp));
rcache_invalidate_tmp();
emith_abicall(sh2_drc_divs32);
tmp = rcache_get_tmp_ret();
emith_or_r_r_r(sr, sr, tmp3); // T
rcache_free_tmp(tmp3);
skip_op = div(opd).div1 + div(opd).rotcl;
+ cycles += skip_op;
}
else if (div(opd).div1 == 32 && div(opd).ro != div(opd).rn) {
// divide 64/32
emith_or_r_r_lsl(sr, tmp3, Q_SHIFT); // Q = !Ro[0]^M
rcache_free_tmp(tmp3);
skip_op = div(opd).div1 + div(opd).rotcl;
+ cycles += skip_op;
} else
#endif
{
emith_move_r_imm_s8_patch(rtsadd, tcache_ptr - (u8 *)rtsret);
#endif
- // branch not taken, correct cycle count
+ // branch not taken, correct cycle count (now, cycles < 0)
if (ctaken)
cycles -= ctaken;
// set T bit to reflect branch not taken for OP_BRANCH_CT/CF
printf("~~~\n");
*/
-#if (DRC_DEBUG)
- fflush(stdout);
-#endif
-
return block_entry_ptr;
}
a += rest, len -= rest;
} while (len > 0);
- if (!removed && len <= 4) {
- dbg(2, "rm_blocks called @%08x, no work?", _a);
+ if (!removed) {
+ if (len <= 4)
+ dbg(2, "rm_blocks called @%08x, no work?", _a);
return;
}
// disasm the utils
tcache_dsm_ptrs[0] = tcache;
do_host_disasm(0);
- fflush(stdout);
#endif
#if (DRC_DEBUG & 1)
hash_collisions = 0;
old = r[a / 4];
r[a / 4] = d;
+ // TODO: DRC doesn't correctly extend 'd' parameter register to 64bit :-/
switch (a) {
// division unit (TODO: verify):
case 0x104: // DVDNT: divident L, starts divide
elprintf_sh2(sh2, EL_32XP, "divide %08x / %08x",
- d, r[0x100 / 4]);
+ r[0x104 / 4], r[0x100 / 4]);
if (r[0x100 / 4]) {
signed int divisor = r[0x100 / 4];
- r[0x118 / 4] = r[0x110 / 4] = (signed int)d % divisor;
- r[0x104 / 4] = r[0x11c / 4] = r[0x114 / 4] = (signed int)d / divisor;
+ r[0x118 / 4] = r[0x110 / 4] = (signed int)r[0x104 / 4] % divisor;
+ r[0x104 / 4] = r[0x11c / 4] = r[0x114 / 4] = (signed int)r[0x104 / 4] / divisor;
}
else
r[0x110 / 4] = r[0x114 / 4] = r[0x118 / 4] = r[0x11c / 4] = 0; // ?
break;
case 0x114:
elprintf_sh2(sh2, EL_32XP, "divide %08x%08x / %08x @%08x",
- r[0x110 / 4], d, r[0x100 / 4], sh2_pc(sh2));
+ r[0x110 / 4], r[0x114 / 4], r[0x100 / 4], sh2_pc(sh2));
if (r[0x100 / 4]) {
- signed long long divident = (signed long long)r[0x110 / 4] << 32 | d;
+ signed long long divident = (signed long long)r[0x110 / 4] << 32 | r[0x114 / 4];
signed int divisor = r[0x100 / 4];
// XXX: undocumented mirroring to 0x118,0x11c?
r[0x118 / 4] = r[0x110 / 4] = divident % divisor;