diff --git a/src/guest/arm7/arm7.c b/src/guest/arm7/arm7.c index e612e69d..10804516 100644 --- a/src/guest/arm7/arm7.c +++ b/src/guest/arm7/arm7.c @@ -198,7 +198,7 @@ static int arm7_init(struct device *dev) { arm->guest->offset_interrupts = (int)offsetof(struct armv3_context, pending_interrupts); arm->guest->data = arm; - arm->guest->interrupt_check = &arm7_check_pending_interrupts; + arm->guest->check_interrupts = &arm7_check_pending_interrupts; arm->guest->ctx = &arm->ctx; arm->guest->mem = as_translate(arm->memory_if->space, 0x0); diff --git a/src/guest/dreamcast.c b/src/guest/dreamcast.c index 6b206eee..97deb76d 100644 --- a/src/guest/dreamcast.c +++ b/src/guest/dreamcast.c @@ -70,36 +70,6 @@ void dc_suspend(struct dreamcast *dc) { dc->running = 0; } -static int dc_load_bin(struct dreamcast *dc, const char *path) { - if (!strstr(path, ".bin")) { - return 0; - } - - FILE *fp = fopen(path, "rb"); - if (!fp) { - return 0; - } - - fseek(fp, 0, SEEK_END); - int size = ftell(fp); - fseek(fp, 0, SEEK_SET); - - /* load to 0x0c010000 (area 3) which is where 1ST_READ.BIN is loaded to */ - uint8_t *data = memory_translate(dc->memory, "system ram", 0x00010000); - int n = (int)fread(data, sizeof(uint8_t), size, fp); - fclose(fp); - - if (n != size) { - LOG_WARNING("failed to read %s", path); - return 0; - } - - sh4_reset(dc->sh4, 0x0c010000); - dc_resume(dc); - - return 1; -} - static int dc_load_disc(struct dreamcast *dc, const char *path) { struct disc *disc = disc_create(path); @@ -124,7 +94,7 @@ int dc_load(struct dreamcast *dc, const char *path) { LOG_INFO("loading %s", path); - if (dc_load_disc(dc, path) || dc_load_bin(dc, path)) { + if (dc_load_disc(dc, path)) { return 1; } diff --git a/src/guest/sh4/sh4.c b/src/guest/sh4/sh4.c index 3744ef40..832dbc1c 100644 --- a/src/guest/sh4/sh4.c +++ b/src/guest/sh4/sh4.c @@ -28,31 +28,29 @@ DEFINE_AGGREGATE_COUNTER(sh4_instrs); DEFINE_AGGREGATE_COUNTER(sh4_sr_updates); /* callbacks to service sh4_reg_read / sh4_reg_write calls */ -struct reg_cb sh4_cb[NUM_SH4_REGS]; +struct reg_cb sh4_cb[SH4_NUM_REGS]; -static void sh4_swap_gpr_bank(struct sh4 *sh4) { - for (int s = 0; s < 8; s++) { - uint32_t tmp = sh4->ctx.r[s]; - sh4->ctx.r[s] = sh4->ctx.ralt[s]; - sh4->ctx.ralt[s] = tmp; - } -} +struct sh4_exception_info sh4_exceptions[SH4_NUM_EXCEPTIONS] = { +#define SH4_EXC(name, expevt, offset, prilvl, priord) \ + {expevt, offset, prilvl, priord}, +#include "guest/sh4/sh4_exc.inc" +#undef SH4_EXC +}; -static void sh4_swap_fpr_bank(struct sh4 *sh4) { - for (int s = 0; s <= 15; s++) { - uint32_t tmp = sh4->ctx.fr[s]; - sh4->ctx.fr[s] = sh4->ctx.xf[s]; - sh4->ctx.xf[s] = tmp; - } -} +struct sh4_interrupt_info sh4_interrupts[SH4_NUM_INTERRUPTS] = { +#define SH4_INT(name, intevt, pri, ipr, ipr_shift) \ + {intevt, pri, ipr, ipr_shift}, +#include "guest/sh4/sh4_int.inc" +#undef SH4_INT +}; -void sh4_sr_updated(struct sh4 *sh4, uint32_t old_sr) { +static void sh4_sr_updated(struct sh4 *sh4, uint32_t old_sr) { struct sh4_context *ctx = &sh4->ctx; prof_counter_add(COUNTER_sh4_sr_updates, 1); if ((ctx->sr & RB_MASK) != (old_sr & RB_MASK)) { - sh4_swap_gpr_bank(sh4); + sh4_swap_gpr_bank(ctx); } if ((ctx->sr & I_MASK) != (old_sr & I_MASK) || @@ -61,11 +59,11 @@ void sh4_sr_updated(struct sh4 *sh4, uint32_t old_sr) { } } -void sh4_fpscr_updated(struct sh4 *sh4, uint32_t old_fpscr) { +static void sh4_fpscr_updated(struct sh4 *sh4, uint32_t old_fpscr) { struct sh4_context *ctx = &sh4->ctx; if ((ctx->fpscr & FR_MASK) != (old_fpscr & FR_MASK)) { - sh4_swap_fpr_bank(sh4); + sh4_swap_fpr_bank(ctx); } } @@ -101,18 +99,62 @@ static void sh4_sleep(void *data) { sh4->ctx.sleep_mode = 1; } -static void sh4_invalid_instr(void *data) { - struct sh4 *sh4 = data; +static void sh4_exception(struct sh4 *sh4, enum sh4_exception exc) { + struct sh4_exception_info *exc_info = &sh4_exceptions[exc]; - if (bios_invalid_instr(sh4->bios)) { + /* let the custom exception handler have a first chance */ + if (sh4->exc_handler && sh4->exc_handler(sh4->exc_handler_data, exc)) { return; } - if (sh4_dbg_invalid_instr(sh4)) { + /* let internal systems have a second chance for illegal instructions */ + if (exc == SH4_EXC_ILLINSTR) { + if (bios_invalid_instr(sh4->bios)) { + return; + } + + if (sh4_dbg_invalid_instr(sh4)) { + return; + } + } + + /* ensure sr is up to date */ + sh4_implode_sr(&sh4->ctx); + + *sh4->EXPEVT = exc_info->expevt; + sh4->ctx.spc = sh4->ctx.pc; + sh4->ctx.ssr = sh4->ctx.sr; + sh4->ctx.sgr = sh4->ctx.r[15]; + sh4->ctx.sr |= (BL_MASK | MD_MASK | RB_MASK); + sh4->ctx.pc = sh4->ctx.vbr + exc_info->offset; + sh4_sr_updated(sh4, sh4->ctx.ssr); +} + +static void sh4_check_interrupts(struct sh4 *sh4) { + if (!sh4->ctx.pending_interrupts) { return; } - LOG_FATAL("unhandled invalid instruction at 0x%08x", sh4->ctx.pc); + /* process the highest priority in the pending vector */ + int n = 63 - clz64(sh4->ctx.pending_interrupts); + enum sh4_interrupt intr = sh4->sorted_interrupts[n]; + struct sh4_interrupt_info *int_info = &sh4_interrupts[intr]; + + /* ensure sr is up to date */ + sh4_implode_sr(&sh4->ctx); + + *sh4->INTEVT = int_info->intevt; + sh4->ctx.spc = sh4->ctx.pc; + sh4->ctx.ssr = sh4->ctx.sr; + sh4->ctx.sgr = sh4->ctx.r[15]; + sh4->ctx.sr |= (BL_MASK | MD_MASK | RB_MASK); + sh4->ctx.pc = sh4->ctx.vbr + 0x600; + sh4->ctx.sleep_mode = 0; + sh4_sr_updated(sh4, sh4->ctx.ssr); +} + +static void sh4_invalid_instr(struct sh4 *sh4) { + sh4_exception(sh4, SH4_EXC_ILLINSTR); } static void sh4_run(struct device *dev, int64_t ns) { @@ -157,13 +199,12 @@ static int sh4_init(struct device *dev) { sh4->guest->offset_instrs = (int)offsetof(struct sh4_context, ran_instrs); sh4->guest->offset_interrupts = (int)offsetof(struct sh4_context, pending_interrupts); - sh4->guest->interrupt_check = (jit_interrupt_cb)&sh4_intc_check_pending; + sh4->guest->check_interrupts = (jit_interrupt_cb)&sh4_check_interrupts; sh4->guest->ctx = &sh4->ctx; sh4->guest->mem = as_translate(sh4->memory_if->space, 0x0); sh4->guest->space = sh4->memory_if->space; sh4->guest->invalid_instr = (sh4_invalid_instr_cb)&sh4_invalid_instr; - sh4->guest->trap = (sh4_trap_cb)&sh4_intc_trap; sh4->guest->ltlb = (sh4_ltlb_cb)&sh4_mmu_ltlb; sh4->guest->pref = (sh4_pref_cb)&sh4_ccn_pref; sh4->guest->sleep = (sh4_sleep_cb)&sh4_sleep; @@ -194,6 +235,12 @@ void sh4_raise_interrupt(struct sh4 *sh4, enum sh4_interrupt intr) { sh4_intc_update_pending(sh4); } +void sh4_set_exception_handler(struct sh4 *sh4, + sh4_exception_handler_cb handler, void *data) { + sh4->exc_handler = handler; + sh4->exc_handler_data = data; +} + void sh4_reset(struct sh4 *sh4, uint32_t pc) { jit_free_blocks(sh4->jit); diff --git a/src/guest/sh4/sh4.h b/src/guest/sh4/sh4.h index 35608234..4b7e32e3 100644 --- a/src/guest/sh4/sh4.h +++ b/src/guest/sh4/sh4.h @@ -5,7 +5,7 @@ #include "guest/dreamcast.h" #include "guest/memory.h" #include "guest/sh4/sh4_types.h" -#include "jit/frontend/sh4/sh4_context.h" +#include "jit/frontend/sh4/sh4_guest.h" #include "jit/jit.h" struct dreamcast; @@ -21,6 +21,8 @@ enum { SH4_DMA_TO_ADDR, }; +typedef int (*sh4_exception_handler_cb)(void *, enum sh4_exception); + struct sh4_dtr { int channel; int dir; @@ -45,11 +47,15 @@ struct sh4 { struct device; struct sh4_context ctx; - uint32_t reg[NUM_SH4_REGS]; + uint32_t reg[SH4_NUM_REGS]; #define SH4_REG(addr, name, default, type) type *name; #include "guest/sh4/sh4_regs.inc" #undef SH4_REG + /* custom exception handler */ + sh4_exception_handler_cb exc_handler; + void *exc_handler_data; + /* jit */ struct jit *jit; struct sh4_guest *guest; @@ -60,8 +66,8 @@ struct sh4 { struct list breakpoints; /* intc */ - enum sh4_interrupt sorted_interrupts[NUM_SH_INTERRUPTS]; - uint64_t sort_id[NUM_SH_INTERRUPTS]; + enum sh4_interrupt sorted_interrupts[SH4_NUM_INTERRUPTS]; + uint64_t sort_id[SH4_NUM_INTERRUPTS]; uint64_t priority_mask[16]; uint64_t requested_interrupts; /* pending interrupts moved to context for fast jit access */ @@ -74,7 +80,9 @@ struct sh4 { struct timer *tmu_timers[3]; }; -extern struct reg_cb sh4_cb[NUM_SH4_REGS]; +extern struct reg_cb sh4_cb[SH4_NUM_REGS]; +extern struct sh4_exception_info sh4_exceptions[SH4_NUM_EXCEPTIONS]; +extern struct sh4_interrupt_info sh4_interrupts[SH4_NUM_INTERRUPTS]; DECLARE_COUNTER(sh4_instrs); @@ -112,9 +120,7 @@ void sh4_dmac_ddt(struct sh4 *sh, struct sh4_dtr *dtr); /* intc */ void sh4_intc_update_pending(struct sh4 *sh4); -void sh4_intc_check_pending(struct sh4 *sh4); void sh4_intc_reprioritize(struct sh4 *sh4); -void sh4_intc_trap(struct sh4 *sh4, uint32_t num); /* mmu */ void sh4_mmu_ltlb(struct sh4 *sh4); @@ -129,9 +135,11 @@ struct sh4 *sh4_create(struct dreamcast *dc); void sh4_destroy(struct sh4 *sh4); void sh4_debug_menu(struct sh4 *sh4); void sh4_reset(struct sh4 *sh4, uint32_t pc); + +void sh4_set_exception_handler(struct sh4 *sh4, + sh4_exception_handler_cb handler, void *data); + void sh4_raise_interrupt(struct sh4 *sh4, enum sh4_interrupt intr); void sh4_clear_interrupt(struct sh4 *sh4, enum sh4_interrupt intr); -void sh4_sr_updated(struct sh4 *sh4, uint32_t old_sr); -void sh4_fpscr_updated(struct sh4 *sh4, uint32_t old_fpscr); #endif diff --git a/src/guest/sh4/sh4_exc.inc b/src/guest/sh4/sh4_exc.inc new file mode 100644 index 00000000..fbae4a92 --- /dev/null +++ b/src/guest/sh4/sh4_exc.inc @@ -0,0 +1,19 @@ +/* NAME, EXPEVT, OFFSET, PRILVL, PRIORD */ +SH4_EXC(USRBRKB, 0x1e0, 0x100, 2, 0) +SH4_EXC(IADR, 0x0e0, 0x100, 2, 1) +SH4_EXC(ITLBMISS, 0x040, 0x400, 2, 2) +SH4_EXC(ITLBPROT, 0x0a0, 0x100, 2, 3) +SH4_EXC(ILLINSTR, 0x180, 0x100, 2, 4) +SH4_EXC(ILLSLOT, 0x1a0, 0x100, 2, 4) +SH4_EXC(FPUDIS, 0x800, 0x100, 2, 4) +SH4_EXC(FPUDISSLOT, 0x820, 0x100, 2, 4) +SH4_EXC(DADRRD, 0x0e0, 0x100, 2, 5) +SH4_EXC(DADRWR, 0x100, 0x100, 2, 5) +SH4_EXC(DTLBMISSR, 0x040, 0x400, 2, 6) +SH4_EXC(DTLBMISSW, 0x060, 0x400, 2, 6) +SH4_EXC(DTLBPROTR, 0x0a0, 0x100, 2, 7) +SH4_EXC(DTLBPROTW, 0x0c0, 0x100, 2, 7) +SH4_EXC(FPU, 0x120, 0x100, 2, 8) +SH4_EXC(PAGEWRITE, 0x080, 0x100, 2, 9) +SH4_EXC(TRAPA, 0x160, 0x100, 2, 4) +SH4_EXC(USRBRKA, 0x1e0, 0x100, 2, 10) diff --git a/src/guest/sh4/sh4_intc.c b/src/guest/sh4/sh4_intc.c index 5ca77eff..e90cbba7 100644 --- a/src/guest/sh4/sh4_intc.c +++ b/src/guest/sh4/sh4_intc.c @@ -1,20 +1,5 @@ #include "guest/sh4/sh4.h" -struct sh4_interrupt_info { - int intevt, default_priority, ipr, ipr_shift; -}; - -static struct sh4_interrupt_info sh4_interrupts[NUM_SH_INTERRUPTS] = { -#define SH4_INT(name, intevt, pri, ipr, ipr_shift) \ - {intevt, pri, ipr, ipr_shift}, -#include "guest/sh4/sh4_int.inc" -#undef SH4_INT -}; - -void sh4_intc_trap(struct sh4 *sh4, uint32_t num) { - LOG_FATAL("sh4_intc_trap %d", num); -} - /* generate a sorted set of interrupts based on their priority. these sorted ids are used to represent all of the currently requested interrupts as a simple bitmask */ @@ -27,7 +12,7 @@ void sh4_intc_reprioritize(struct sh4 *sh4) { for (int level = 0; level < 16; level++) { /* iterate backwards, giving priority to lower id interrupts when the priorities are equal */ - for (int i = NUM_SH_INTERRUPTS - 1; i >= 0; i--) { + for (int i = SH4_NUM_INTERRUPTS - 1; i >= 0; i--) { struct sh4_interrupt_info *int_info = &sh4_interrupts[i]; /* get current priority for interrupt */ @@ -61,29 +46,6 @@ void sh4_intc_reprioritize(struct sh4 *sh4) { sh4_intc_update_pending(sh4); } -void sh4_intc_check_pending(struct sh4 *sh4) { - if (!sh4->ctx.pending_interrupts) { - return; - } - - /* process the highest priority in the pending vector */ - int n = 63 - clz64(sh4->ctx.pending_interrupts); - enum sh4_interrupt intr = sh4->sorted_interrupts[n]; - struct sh4_interrupt_info *int_info = &sh4_interrupts[intr]; - - /* ensure sr is up to date */ - sh4_implode_sr(&sh4->ctx); - - *sh4->INTEVT = int_info->intevt; - sh4->ctx.ssr = sh4->ctx.sr; - sh4->ctx.spc = sh4->ctx.pc; - sh4->ctx.sgr = sh4->ctx.r[15]; - sh4->ctx.sr |= (BL_MASK | MD_MASK | RB_MASK); - sh4->ctx.pc = sh4->ctx.vbr + 0x600; - sh4->ctx.sleep_mode = 0; - sh4_sr_updated(sh4, sh4->ctx.ssr); -} - void sh4_intc_update_pending(struct sh4 *sh4) { int min_priority = (sh4->ctx.sr & I_MASK) >> I_BIT; uint64_t mask = ~sh4->priority_mask[min_priority]; diff --git a/src/guest/sh4/sh4_types.h b/src/guest/sh4/sh4_types.h index 30c711b7..08a54ceb 100644 --- a/src/guest/sh4/sh4_types.h +++ b/src/guest/sh4/sh4_types.h @@ -139,14 +139,35 @@ enum { #define SH4_REG(addr, name, default, type) name = SH4_REG_OFFSET(addr), #include "guest/sh4/sh4_regs.inc" #undef SH4_REG - NUM_SH4_REGS = SH4_REG_OFFSET(0xffffffff) + 1 + SH4_NUM_REGS = SH4_REG_OFFSET(0xffffffff) + 1 +}; + +enum sh4_exception { +#define SH4_EXC(name, expevt, offset, prilvl, priord) SH4_EXC_##name, +#include "guest/sh4/sh4_exc.inc" +#undef SH4_EXC + SH4_NUM_EXCEPTIONS }; enum sh4_interrupt { #define SH4_INT(name, intevt, pri, ipr, ipr_shift) SH4_INT_##name, #include "guest/sh4/sh4_int.inc" #undef SH4_INT - NUM_SH_INTERRUPTS + SH4_NUM_INTERRUPTS +}; + +struct sh4_exception_info { + int expevt; + int offset; + int prilvl; + int priord; +}; + +struct sh4_interrupt_info { + int intevt; + int default_priority; + int ipr; + int ipr_shift; }; #endif diff --git a/src/jit/backend/interp/interp_backend.c b/src/jit/backend/interp/interp_backend.c index d26f24be..f8ab2935 100644 --- a/src/jit/backend/interp/interp_backend.c +++ b/src/jit/backend/interp/interp_backend.c @@ -37,7 +37,7 @@ static void interp_backend_run_code(struct jit_backend *base, int cycles) { *run_cycles -= cycles; *ran_instrs += instrs; - guest->interrupt_check(guest->data); + guest->check_interrupts(guest->data); } } diff --git a/src/jit/backend/x64/x64_dispatch.cc b/src/jit/backend/x64/x64_dispatch.cc index 3534dd13..ac6a4e05 100644 --- a/src/jit/backend/x64/x64_dispatch.cc +++ b/src/jit/backend/x64/x64_dispatch.cc @@ -147,7 +147,7 @@ void x64_dispatch_emit_thunks(struct x64_backend *backend) { backend->dispatch_interrupt = e.getCurr(); e.mov(arg0, (uint64_t)jit->guest->data); - e.call(jit->guest->interrupt_check); + e.call(jit->guest->check_interrupts); e.jmp(backend->dispatch_dynamic); } diff --git a/src/jit/frontend/sh4/sh4_context.h b/src/jit/frontend/sh4/sh4_context.h deleted file mode 100644 index 45212d48..00000000 --- a/src/jit/frontend/sh4/sh4_context.h +++ /dev/null @@ -1,146 +0,0 @@ -#ifndef SH4_CONTEXT_H -#define SH4_CONTEXT_H - -#include - -/* - * SR bits - */ - -/* true / false condition or carry/borrow bit */ -#define T_BIT 0 -/* specifies a saturation operation for a MAC instruction */ -#define S_BIT 1 -/* interrupt mask level */ -#define I_BIT 4 -/* used by the DIV0S, DIV0U, and DIV1 instructions */ -#define Q_BIT 8 -#define M_BIT 9 -/* an FPU instr causes a general FPU disable exception */ -#define FD_BIT 15 -/* interrupt requests are masked */ -#define BL_BIT 28 -/* general register bank specifier in privileged mode (set - to 1 by a reset, exception, or interrupt) */ -#define RB_BIT 29 -/* processor mode (0 is user mode, 1 is privileged mode) */ -#define MD_BIT 30 - -#define T_MASK (1u << T_BIT) -#define S_MASK (1u << S_BIT) -#define I_MASK 0xf0 -#define Q_MASK (1u << Q_BIT) -#define M_MASK (1u << M_BIT) -#define FD_MASK (1u << FD_BIT) -#define BL_MASK (1u << BL_BIT) -#define RB_MASK (1u << RB_BIT) -#define MD_MASK (1u << MD_BIT) - -#define SR_MASK \ - (MD_MASK | RB_MASK | BL_MASK | FD_MASK | M_MASK | Q_MASK | I_MASK | S_MASK | \ - T_MASK) - -/* - * FPSCR bits - */ - -/* denormalization mode */ -#define DN_BIT 18 -/* precision mode */ -#define PR_BIT 19 -/* transfer size mode */ -#define SZ_BIT 20 -/* floating-point register bank */ -#define FR_BIT 21 - -#define RM_MASK 0x3 -#define FLAG_MASK 0x7c -#define ENABLE_MASK 0xf80 -#define CAUSE_MASK 0x3f000 -#define DN_MASK (1u << DN_BIT) -#define PR_MASK (1u << PR_BIT) -#define SZ_MASK (1u << SZ_BIT) -#define FR_MASK (1u << FR_BIT) - -#define FPSCR_MASK \ - (RM_MASK | FLAG_MASK | ENABLE_MASK | CAUSE_MASK | DN_MASK | PR_MASK | \ - SZ_MASK | FR_MASK) - -struct sh4_context { - /* there are 24 32-bit general registers, r0_bank0-r7_bank0, r0_bank1-r7_bank1 - and r8-r15. r contains the active bank's r0-r7 as well as r8-r15. ralt - contains the inactive bank's r0-r7 and is swapped in when the processor - mode changes */ - uint32_t r[16], ralt[8]; - - /* there are 32 32-bit floating point registers, fr0-fr15 and xf0-xf15. these - registers are banked, and swapped with eachother when the bank bit of - FPSCR changes. in addition, fr0–fr15 can be used as the eight registers - dr0/2/4/6/8/10/12/14 (double-precision, or pair registers) or the four - registers fv0/4/8/12 (vector registers). while xf0-xf15 can be used as - the eight registers xd0/2/4/6/8/10/12/14 (pair registers) or register - matrix XMTRX - - note, the sh4 does not support endian conversion for 64-bit data. - therefore, if 64-bit floating point access is performed in little endian - mode, the upper and lower 32 bits will be reversed. for example, dr2 - aliases fr2 and fr3, but fr3 is actually the low-order word - - in order to avoid swapping the words in every double-precision opcode, the - mapping for each pair of single-precision registers is instead swapped by - XOR'ing the actual index with 1. for example, fr2 becomes fr[3] and fr3 - becomes fr[2], enabling dr2 to perfectly alias fr[2] - - note note, this incorrectly causes fv registers to be swizzled. fv0 should - be loaded as {fr0, fr1, fr2, fr3} but it's actually loaded as - {fr1, fr0, fr3, fr2}. however, due to the way the FV registers are - used (FIPR and FTRV) this doesn't actually affect the results */ - uint32_t fr[16], xf[16]; - - /* sr_t and sr_s are the S and T bits from the status register, they are - kept in their own unique context slots to avoid excessive shifting and - masking to load / store them - - sr_m and sr_qm are a similar story. sr_m stores the M bit from the status - register, while sr_qm stores a value that repsresents if Q == M, which is - all that's needed to emulate div1. these two values can be used to derive - the real QM bits when building the status register in sh4_implode_sr */ - uint32_t pc, pr, sr, sr_t, sr_s, sr_m, sr_qm; - - uint32_t fpscr; - uint32_t dbr, gbr, vbr; - uint32_t fpul, mach, macl; - uint32_t sgr, spc, ssr; - uint64_t pending_interrupts; - uint32_t sq[2][8]; - - /* processor sleep state */ - uint32_t sleep_mode; - - /* the main dispatch loop is ran until remaining_cycles is <= 0 */ - int32_t run_cycles; - - /* debug information */ - int32_t ran_instrs; - - uint8_t cache[0x2000]; -}; - -static inline void sh4_implode_sr(struct sh4_context *ctx) { - uint32_t sr_q = (ctx->sr_qm >> 31) == ctx->sr_m; - - ctx->sr &= ~(M_MASK | Q_MASK | S_MASK | T_MASK); - ctx->sr |= (ctx->sr_m << M_BIT) | (sr_q << Q_BIT) | (ctx->sr_s << S_BIT) | - (ctx->sr_t << T_BIT); -} - -static inline void sh4_explode_sr(struct sh4_context *ctx) { - ctx->sr_t = (ctx->sr & T_MASK) >> T_BIT; - ctx->sr_s = (ctx->sr & S_MASK) >> S_BIT; - ctx->sr_m = (ctx->sr & M_MASK) >> M_BIT; - - uint32_t sr_q = (ctx->sr & Q_MASK) >> Q_BIT; - ctx->sr_qm = (sr_q == ctx->sr_m) << 31; -} - -#endif diff --git a/src/jit/frontend/sh4/sh4_fallback.c b/src/jit/frontend/sh4/sh4_fallback.c index ac4f7c91..91156b09 100644 --- a/src/jit/frontend/sh4/sh4_fallback.c +++ b/src/jit/frontend/sh4/sh4_fallback.c @@ -2,7 +2,6 @@ #include "core/assert.h" #include "core/log.h" #include "core/math.h" -#include "jit/frontend/sh4/sh4_context.h" #include "jit/frontend/sh4/sh4_frontend.h" #include "jit/frontend/sh4/sh4_guest.h" #include "jit/jit.h" @@ -383,8 +382,6 @@ typedef int32_t int128_t[4]; #define INVALID_INSTR() guest->invalid_instr(guest->data) -#define TRAP(num) guest->trap(guest->data, num) - #define LDTLB() guest->ltlb(guest->data) #define PREF_COND(c, addr) if (c) { guest->pref(guest->data, addr); } diff --git a/src/jit/frontend/sh4/sh4_frontend.c b/src/jit/frontend/sh4/sh4_frontend.c index 14cc3032..c922eaff 100644 --- a/src/jit/frontend/sh4/sh4_frontend.c +++ b/src/jit/frontend/sh4/sh4_frontend.c @@ -1,7 +1,6 @@ #include "jit/frontend/sh4/sh4_frontend.h" #include "core/profiler.h" #include "jit/frontend/jit_frontend.h" -#include "jit/frontend/sh4/sh4_context.h" #include "jit/frontend/sh4/sh4_disasm.h" #include "jit/frontend/sh4/sh4_fallback.h" #include "jit/frontend/sh4/sh4_guest.h" diff --git a/src/jit/frontend/sh4/sh4_guest.h b/src/jit/frontend/sh4/sh4_guest.h index b622dfda..d334fa2a 100644 --- a/src/jit/frontend/sh4/sh4_guest.h +++ b/src/jit/frontend/sh4/sh4_guest.h @@ -1,10 +1,169 @@ #ifndef SH4_GUEST_H #define SH4_GUEST_H +#include #include "jit/jit.h" +/* + * sh4 guest context + */ + +/* sr bits */ + +/* true / false condition or carry/borrow bit */ +#define T_BIT 0 +/* specifies a saturation operation for a MAC instruction */ +#define S_BIT 1 +/* interrupt mask level */ +#define I_BIT 4 +/* used by the DIV0S, DIV0U, and DIV1 instructions */ +#define Q_BIT 8 +#define M_BIT 9 +/* an FPU instr causes a general FPU disable exception */ +#define FD_BIT 15 +/* interrupt requests are masked */ +#define BL_BIT 28 +/* general register bank specifier in privileged mode (set + to 1 by a reset, exception, or interrupt) */ +#define RB_BIT 29 +/* processor mode (0 is user mode, 1 is privileged mode) */ +#define MD_BIT 30 + +#define T_MASK (1u << T_BIT) +#define S_MASK (1u << S_BIT) +#define I_MASK 0xf0 +#define Q_MASK (1u << Q_BIT) +#define M_MASK (1u << M_BIT) +#define FD_MASK (1u << FD_BIT) +#define BL_MASK (1u << BL_BIT) +#define RB_MASK (1u << RB_BIT) +#define MD_MASK (1u << MD_BIT) + +#define SR_MASK \ + (MD_MASK | RB_MASK | BL_MASK | FD_MASK | M_MASK | Q_MASK | I_MASK | S_MASK | \ + T_MASK) + +/* fpscr bits */ + +/* denormalization mode */ +#define DN_BIT 18 +/* precision mode */ +#define PR_BIT 19 +/* transfer size mode */ +#define SZ_BIT 20 +/* floating-point register bank */ +#define FR_BIT 21 + +#define RM_MASK 0x3 +#define FLAG_MASK 0x7c +#define ENABLE_MASK 0xf80 +#define CAUSE_MASK 0x3f000 +#define DN_MASK (1u << DN_BIT) +#define PR_MASK (1u << PR_BIT) +#define SZ_MASK (1u << SZ_BIT) +#define FR_MASK (1u << FR_BIT) + +#define FPSCR_MASK \ + (RM_MASK | FLAG_MASK | ENABLE_MASK | CAUSE_MASK | DN_MASK | PR_MASK | \ + SZ_MASK | FR_MASK) + +struct sh4_context { + /* there are 24 32-bit general registers, r0_bank0-r7_bank0, r0_bank1-r7_bank1 + and r8-r15. r contains the active bank's r0-r7 as well as r8-r15. ralt + contains the inactive bank's r0-r7 and is swapped in when the processor + mode changes */ + uint32_t r[16], ralt[8]; + + /* there are 32 32-bit floating point registers, fr0-fr15 and xf0-xf15. these + registers are banked, and swapped with eachother when the bank bit of + FPSCR changes. in addition, fr0–fr15 can be used as the eight registers + dr0/2/4/6/8/10/12/14 (double-precision, or pair registers) or the four + registers fv0/4/8/12 (vector registers). while xf0-xf15 can be used as + the eight registers xd0/2/4/6/8/10/12/14 (pair registers) or register + matrix XMTRX + + note, the sh4 does not support endian conversion for 64-bit data. + therefore, if 64-bit floating point access is performed in little endian + mode, the upper and lower 32 bits will be reversed. for example, dr2 + aliases fr2 and fr3, but fr3 is actually the low-order word + + in order to avoid swapping the words in every double-precision opcode, the + mapping for each pair of single-precision registers is instead swapped by + XOR'ing the actual index with 1. for example, fr2 becomes fr[3] and fr3 + becomes fr[2], enabling dr2 to perfectly alias fr[2] + + note note, this incorrectly causes fv registers to be swizzled. fv0 should + be loaded as {fr0, fr1, fr2, fr3} but it's actually loaded as + {fr1, fr0, fr3, fr2}. however, due to the way the FV registers are + used (FIPR and FTRV) this doesn't actually affect the results */ + uint32_t fr[16], xf[16]; + + /* sr_t and sr_s are the S and T bits from the status register, they are + kept in their own unique context slots to avoid excessive shifting and + masking to load / store them + + sr_m and sr_qm are a similar story. sr_m stores the M bit from the status + register, while sr_qm stores a value that repsresents if Q == M, which is + all that's needed to emulate div1. these two values can be used to derive + the real QM bits when building the status register in sh4_implode_sr */ + uint32_t pc, pr, sr, sr_t, sr_s, sr_m, sr_qm; + + uint32_t fpscr; + uint32_t dbr, gbr, vbr; + uint32_t fpul, mach, macl; + uint32_t sgr, spc, ssr; + uint64_t pending_interrupts; + uint32_t sq[2][8]; + + /* processor sleep state */ + uint32_t sleep_mode; + + /* the main dispatch loop is ran until run_cycles is <= 0 */ + int32_t run_cycles; + + /* debug information */ + int32_t ran_instrs; + + uint8_t cache[0x2000]; +}; + +static inline void sh4_swap_gpr_bank(struct sh4_context *ctx) { + for (int s = 0; s < 8; s++) { + uint32_t tmp = ctx->r[s]; + ctx->r[s] = ctx->ralt[s]; + ctx->ralt[s] = tmp; + } +} + +static inline void sh4_swap_fpr_bank(struct sh4_context *ctx) { + for (int s = 0; s < 16; s++) { + uint32_t tmp = ctx->fr[s]; + ctx->fr[s] = ctx->xf[s]; + ctx->xf[s] = tmp; + } +} + +static inline void sh4_implode_sr(struct sh4_context *ctx) { + uint32_t sr_q = (ctx->sr_qm >> 31) == ctx->sr_m; + + ctx->sr &= ~(M_MASK | Q_MASK | S_MASK | T_MASK); + ctx->sr |= (ctx->sr_m << M_BIT) | (sr_q << Q_BIT) | (ctx->sr_s << S_BIT) | + (ctx->sr_t << T_BIT); +} + +static inline void sh4_explode_sr(struct sh4_context *ctx) { + ctx->sr_t = (ctx->sr & T_MASK) >> T_BIT; + ctx->sr_s = (ctx->sr & S_MASK) >> S_BIT; + ctx->sr_m = (ctx->sr & M_MASK) >> M_BIT; + + uint32_t sr_q = (ctx->sr & Q_MASK) >> Q_BIT; + ctx->sr_qm = (sr_q == ctx->sr_m) << 31; +} + +/* + * sh4 guest runtime interface + */ typedef void (*sh4_invalid_instr_cb)(void *); -typedef void (*sh4_trap_cb)(void *, uint32_t); typedef void (*sh4_ltlb_cb)(void *); typedef void (*sh4_pref_cb)(void *, uint32_t); typedef void (*sh4_sleep_cb)(void *); @@ -14,9 +173,7 @@ typedef void (*sh4_fpscr_updated_cb)(void *, uint32_t); struct sh4_guest { struct jit_guest; - /* runtime interface */ sh4_invalid_instr_cb invalid_instr; - sh4_trap_cb trap; sh4_ltlb_cb ltlb; sh4_pref_cb pref; sh4_sleep_cb sleep; diff --git a/src/jit/frontend/sh4/sh4_instr.h b/src/jit/frontend/sh4/sh4_instr.h index 717b2cc2..c199d242 100644 --- a/src/jit/frontend/sh4/sh4_instr.h +++ b/src/jit/frontend/sh4/sh4_instr.h @@ -1575,7 +1575,7 @@ INSTR(STSMPR) { /* TRAPA #imm */ INSTR(TRAPA) { - TRAP(i.imm.imm); + LOG_FATAL("unsupported TRAPA"); } /* FLDI0 FRn 1111nnnn10001101 */ diff --git a/src/jit/frontend/sh4/sh4_translate.c b/src/jit/frontend/sh4/sh4_translate.c index c8051d19..578ba902 100644 --- a/src/jit/frontend/sh4/sh4_translate.c +++ b/src/jit/frontend/sh4/sh4_translate.c @@ -1,7 +1,6 @@ #include "jit/frontend/sh4/sh4_translate.h" #include "core/assert.h" #include "core/log.h" -#include "jit/frontend/sh4/sh4_context.h" #include "jit/frontend/sh4/sh4_frontend.h" #include "jit/frontend/sh4/sh4_guest.h" #include "jit/ir/ir.h" @@ -427,12 +426,6 @@ static void store_fpscr(struct sh4_guest *guest, struct ir *ir, ir_call_1(ir, invalid_instr, data); \ } -#define TRAP(num) { \ - struct ir_value *trap = ir_alloc_i64(ir, (uint64_t)guest->trap); \ - struct ir_value *data = ir_alloc_i64(ir, (uint64_t)guest->data); \ - struct ir_value *imm = ir_alloc_i32(ir, num); \ - ir_call_2(ir, trap, data, imm); \ - } #define LDTLB() { \ struct ir_value *ltlb = ir_alloc_i64(ir, (uint64_t)guest->ltlb); \ diff --git a/src/jit/jit.h b/src/jit/jit.h index d5f120eb..87ef1057 100644 --- a/src/jit/jit.h +++ b/src/jit/jit.h @@ -101,7 +101,7 @@ struct jit_guest { int offset_cycles; int offset_instrs; int offset_interrupts; - jit_interrupt_cb interrupt_check; + jit_interrupt_cb check_interrupts; /* memory interface */ void *ctx;