mirror of https://github.com/inolen/redream.git
raise sh4 exception on illegal instruction
This commit is contained in:
parent
8cca5f2e69
commit
37974d0073
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
|
@ -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];
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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, 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
|
|
@ -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); }
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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, 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;
|
||||
|
|
|
@ -1575,7 +1575,7 @@ INSTR(STSMPR) {
|
|||
|
||||
/* TRAPA #imm */
|
||||
INSTR(TRAPA) {
|
||||
TRAP(i.imm.imm);
|
||||
LOG_FATAL("unsupported TRAPA");
|
||||
}
|
||||
|
||||
/* FLDI0 FRn 1111nnnn10001101 */
|
||||
|
|
|
@ -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); \
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue