raise sh4 exception on illegal instruction

This commit is contained in:
Anthony Pesch 2017-07-30 12:12:45 -04:00
parent 8cca5f2e69
commit 37974d0073
16 changed files with 299 additions and 272 deletions

View File

@ -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);

View File

@ -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;
}

View File

@ -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);

View File

@ -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

19
src/guest/sh4/sh4_exc.inc Normal file
View File

@ -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)

View File

@ -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];

View File

@ -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

View File

@ -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);
}
}

View File

@ -147,7 +147,7 @@ void x64_dispatch_emit_thunks(struct x64_backend *backend) {
backend->dispatch_interrupt = e.getCurr<void *>();
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);
}

View File

@ -1,146 +0,0 @@
#ifndef SH4_CONTEXT_H
#define SH4_CONTEXT_H
#include <stdint.h>
/*
* 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, fr0fr15 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

View File

@ -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); }

View File

@ -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"

View File

@ -1,10 +1,169 @@
#ifndef SH4_GUEST_H
#define SH4_GUEST_H
#include <stdint.h>
#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, fr0fr15 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;

View File

@ -1575,7 +1575,7 @@ INSTR(STSMPR) {
/* TRAPA #imm */
INSTR(TRAPA) {
TRAP(i.imm.imm);
LOG_FATAL("unsupported TRAPA");
}
/* FLDI0 FRn 1111nnnn10001101 */

View File

@ -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); \

View File

@ -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;