mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-26 15:45:41 +00:00
1560 lines
40 KiB
C
1560 lines
40 KiB
C
/* Simple PowerPC recompiler */
|
|
|
|
#include "model3.h"
|
|
#include "ppc_drc.h"
|
|
#include "genx86.h"
|
|
|
|
#define COMPILE_OPS 1
|
|
#define DISABLE_UNTESTED_OPS 0
|
|
#define ENABLE_FASTRAM_PATH 1
|
|
#define ENABLE_FASTRAM_PATH_FPU 1
|
|
#define COMPILE_FPU_OPS 1
|
|
|
|
#define DONT_COMPILE_ADD 0
|
|
#define DONT_COMPILE_ADDC 0
|
|
#define DONT_COMPILE_ADDE 0
|
|
#define DONT_COMPILE_ADDI 0
|
|
#define DONT_COMPILE_ADDIC 0
|
|
#define DONT_COMPILE_ADDIC_RC 0
|
|
#define DONT_COMPILE_ADDIS 0
|
|
#define DONT_COMPILE_ADDME 0
|
|
#define DONT_COMPILE_ADDZE 0
|
|
#define DONT_COMPILE_AND 0
|
|
#define DONT_COMPILE_ANDC 0
|
|
#define DONT_COMPILE_ANDI_RC 0
|
|
#define DONT_COMPILE_ANDIS_RC 0
|
|
#define DONT_COMPILE_CMP 0
|
|
#define DONT_COMPILE_CMPI 0
|
|
#define DONT_COMPILE_CMPL 0
|
|
#define DONT_COMPILE_CMPLI 0
|
|
#define DONT_COMPILE_EXTSB 0
|
|
#define DONT_COMPILE_EXTSH 0
|
|
#define DONT_COMPILE_LBZ 0
|
|
#define DONT_COMPILE_LBZU 0
|
|
#define DONT_COMPILE_LBZUX 0
|
|
#define DONT_COMPILE_LBZX 0
|
|
#define DONT_COMPILE_LHA 0
|
|
#define DONT_COMPILE_LHAU 0
|
|
#define DONT_COMPILE_LHAUX 0
|
|
#define DONT_COMPILE_LHAX 0
|
|
#define DONT_COMPILE_LHBRX 1
|
|
#define DONT_COMPILE_LHZ 0
|
|
#define DONT_COMPILE_LHZU 0
|
|
#define DONT_COMPILE_LHZUX 0
|
|
#define DONT_COMPILE_LHZX 0
|
|
#define DONT_COMPILE_LWBRX 1
|
|
#define DONT_COMPILE_LWZ 0
|
|
#define DONT_COMPILE_LWZU 0
|
|
#define DONT_COMPILE_LWZUX 0
|
|
#define DONT_COMPILE_LWZX 0
|
|
#define DONT_COMPILE_MFSPR 0 // slow?
|
|
#define DONT_COMPILE_MTSPR 0 // slow?
|
|
#define DONT_COMPILE_MULHW 0
|
|
#define DONT_COMPILE_MULHWU 0
|
|
#define DONT_COMPILE_MULLI 0
|
|
#define DONT_COMPILE_MULLW 0
|
|
#define DONT_COMPILE_NAND 0
|
|
#define DONT_COMPILE_NEG 0
|
|
#define DONT_COMPILE_NOR 0
|
|
#define DONT_COMPILE_OR 0
|
|
#define DONT_COMPILE_ORC 0
|
|
#define DONT_COMPILE_ORI 0
|
|
#define DONT_COMPILE_ORIS 0
|
|
#define DONT_COMPILE_RLWIMI 1 // slow?
|
|
#define DONT_COMPILE_RLWINM 1 // slow?
|
|
#define DONT_COMPILE_RLWNM 1 // slow?
|
|
#define DONT_COMPILE_SLW 1 // slow?
|
|
#define DONT_COMPILE_SRW 1 // slow?
|
|
#define DONT_COMPILE_STB 0
|
|
#define DONT_COMPILE_STBU 0
|
|
#define DONT_COMPILE_STBUX 0
|
|
#define DONT_COMPILE_STBX 0
|
|
#define DONT_COMPILE_STH 1
|
|
#define DONT_COMPILE_STW 0
|
|
#define DONT_COMPILE_STWU 0
|
|
#define DONT_COMPILE_STWUX 0
|
|
#define DONT_COMPILE_STWX 0
|
|
#define DONT_COMPILE_SUBF 0
|
|
#define DONT_COMPILE_SUBFC 0
|
|
#define DONT_COMPILE_SUBFIC 0
|
|
#define DONT_COMPILE_XOR 0
|
|
#define DONT_COMPILE_XORI 0
|
|
#define DONT_COMPILE_XORIS 0
|
|
#define DONT_COMPILE_LFS 0
|
|
#define DONT_COMPILE_LFSU 0
|
|
#define DONT_COMPILE_LFSUX 0
|
|
#define DONT_COMPILE_LFSX 0
|
|
#define DONT_COMPILE_LFD 0
|
|
#define DONT_COMPILE_LFDU 0
|
|
#define DONT_COMPILE_LFDUX 0
|
|
#define DONT_COMPILE_LFDX 0
|
|
#define DONT_COMPILE_STFS 0
|
|
#define DONT_COMPILE_STFSU 0
|
|
#define DONT_COMPILE_STFSUX 0
|
|
#define DONT_COMPILE_STFSX 0
|
|
#define DONT_COMPILE_STFD 0
|
|
#define DONT_COMPILE_STFDU 0
|
|
#define DONT_COMPILE_STFDUX 0
|
|
#define DONT_COMPILE_STFDX 0
|
|
|
|
|
|
void **drccore_get_lookup_ptr(UINT32 pc);
|
|
void drccore_insert_dispatcher(void);
|
|
void drccore_insert_cycle_check(int cycles, UINT32 pc);
|
|
void drccore_insert_sanity_check(UINT32 value);
|
|
void drccore_init(void);
|
|
void drccore_shutdown(void);
|
|
void drccore_reset(void);
|
|
void drccore_execute(void);
|
|
|
|
|
|
// for fastram path
|
|
extern UINT8 *ram;
|
|
|
|
|
|
#define DRC_END_BLOCK 0x1
|
|
|
|
|
|
#define RD ((op >> 21) & 0x1F)
|
|
#define RT ((op >> 21) & 0x1f)
|
|
#define RS ((op >> 21) & 0x1f)
|
|
#define RA ((op >> 16) & 0x1f)
|
|
#define RB ((op >> 11) & 0x1f)
|
|
#define RC ((op >> 6) & 0x1f)
|
|
|
|
#define MB ((op >> 6) & 0x1f)
|
|
#define ME ((op >> 1) & 0x1f)
|
|
#define SH ((op >> 11) & 0x1f)
|
|
#define BO ((op >> 21) & 0x1f)
|
|
#define BI ((op >> 16) & 0x1f)
|
|
#define CRFD ((op >> 23) & 0x7)
|
|
#define CRFA ((op >> 18) & 0x7)
|
|
#define FXM ((op >> 12) & 0xff)
|
|
#define SPR (((op >> 16) & 0x1f) | ((op >> 6) & 0x3e0))
|
|
|
|
#define SIMM16 (INT32)(INT16)(op & 0xffff)
|
|
#define UIMM16 (UINT32)(op & 0xffff)
|
|
|
|
#define RCBIT (op & 0x1)
|
|
#define OEBIT (op & 0x400)
|
|
#define AABIT (op & 0x2)
|
|
#define LKBIT (op & 0x1)
|
|
|
|
#define REG(x) (ppc.r[x])
|
|
#define LR (ppc.lr)
|
|
#define CTR (ppc.ctr)
|
|
#define XER (ppc.xer)
|
|
#define CR(x) (ppc.cr[x])
|
|
#define MSR (ppc.msr)
|
|
#define SRR0 (ppc.srr0)
|
|
#define SRR1 (ppc.srr1)
|
|
#define SRR2 (ppc.srr2)
|
|
#define SRR3 (ppc.srr3)
|
|
#define EVPR (ppc.evpr)
|
|
#define EXIER (ppc.exier)
|
|
#define EXISR (ppc.exisr)
|
|
#define DEC (ppc.dec)
|
|
#define FPR(x) (ppc.fpr[x])
|
|
#define FM ((op >> 17) & 0xFF)
|
|
#define SPRF (((op >> 6) & 0x3E0) | ((op >> 16) & 0x1F))
|
|
|
|
#define CHECK_SUPERVISOR() \
|
|
if((ppc.msr & 0x4000) != 0){ \
|
|
}
|
|
|
|
#define CHECK_FPU_AVAILABLE() \
|
|
if((ppc.msr & 0x2000) == 0){ \
|
|
}
|
|
|
|
static UINT32 ppc_field_xlat[256];
|
|
|
|
#define FPSCR_FX 0x80000000
|
|
#define FPSCR_FEX 0x40000000
|
|
#define FPSCR_VX 0x20000000
|
|
#define FPSCR_OX 0x10000000
|
|
#define FPSCR_UX 0x08000000
|
|
#define FPSCR_ZX 0x04000000
|
|
#define FPSCR_XX 0x02000000
|
|
|
|
|
|
|
|
#define BITMASK_0(n) (UINT32)(((UINT64)1 << n) - 1)
|
|
#define CRBIT(x) ((ppc.cr[x / 4] & (1 << (3 - (x % 4)))) ? 1 : 0)
|
|
#define _BIT(n) (1 << (n))
|
|
#define GET_ROTATE_MASK(mb,me) (ppc_rotate_mask[mb][me])
|
|
#define ADD_CA(r,a,b) ((UINT32)r < (UINT32)a)
|
|
#define SUB_CA(r,a,b) (!((UINT32)a < (UINT32)b))
|
|
#define ADD_OV(r,a,b) ((~((a) ^ (b)) & ((a) ^ (r))) & 0x80000000)
|
|
#define SUB_OV(r,a,b) (( ((a) ^ (b)) & ((a) ^ (r))) & 0x80000000)
|
|
|
|
#define XER_SO 0x80000000
|
|
#define XER_OV 0x40000000
|
|
#define XER_CA 0x20000000
|
|
|
|
#define MSR_POW 0x00040000 /* Power Management Enable */
|
|
#define MSR_WE 0x00040000
|
|
#define MSR_CE 0x00020000
|
|
#define MSR_ILE 0x00010000 /* Interrupt Little Endian Mode */
|
|
#define MSR_EE 0x00008000 /* External Interrupt Enable */
|
|
#define MSR_PR 0x00004000 /* Problem State */
|
|
#define MSR_FP 0x00002000 /* Floating Point Available */
|
|
#define MSR_ME 0x00001000 /* Machine Check Enable */
|
|
#define MSR_FE0 0x00000800
|
|
#define MSR_SE 0x00000400 /* Single Step Trace Enable */
|
|
#define MSR_BE 0x00000200 /* Branch Trace Enable */
|
|
#define MSR_DE 0x00000200
|
|
#define MSR_FE1 0x00000100
|
|
#define MSR_IP 0x00000040 /* Interrupt Prefix */
|
|
#define MSR_IR 0x00000020 /* Instruction Relocate */
|
|
#define MSR_DR 0x00000010 /* Data Relocate */
|
|
#define MSR_PE 0x00000008
|
|
#define MSR_PX 0x00000004
|
|
#define MSR_RI 0x00000002 /* Recoverable Interrupt Enable */
|
|
#define MSR_LE 0x00000001
|
|
|
|
#define TSR_ENW 0x80000000
|
|
#define TSR_WIS 0x40000000
|
|
|
|
#define BYTE_REVERSE16(x) ((((x) >> 8) & 0xff) | (((x) << 8) & 0xff00))
|
|
#define BYTE_REVERSE32(x) ((((x) >> 24) & 0xff) | (((x) >> 8) & 0xff00) | (((x) << 8) & 0xff0000) | (((x) << 24) & 0xff000000))
|
|
|
|
typedef union {
|
|
UINT64 id;
|
|
double fd;
|
|
} FPR;
|
|
|
|
typedef union {
|
|
UINT32 i;
|
|
float f;
|
|
} FPR32;
|
|
|
|
typedef struct {
|
|
UINT32 u;
|
|
UINT32 l;
|
|
} BATENT;
|
|
|
|
|
|
typedef struct {
|
|
UINT32 r[32];
|
|
UINT32 pc;
|
|
UINT32 npc;
|
|
|
|
UINT32 *op;
|
|
|
|
UINT32 lr;
|
|
UINT32 ctr;
|
|
UINT32 xer;
|
|
UINT32 msr;
|
|
UINT8 cr[8];
|
|
UINT32 pvr;
|
|
UINT32 srr0;
|
|
UINT32 srr1;
|
|
UINT32 srr2;
|
|
UINT32 srr3;
|
|
UINT32 hid0;
|
|
UINT32 hid1;
|
|
UINT32 hid2;
|
|
UINT32 sdr1;
|
|
UINT32 sprg[4];
|
|
|
|
BATENT ibat[4];
|
|
BATENT dbat[4];
|
|
|
|
int reserved;
|
|
UINT32 reserved_address;
|
|
|
|
int interrupt_pending;
|
|
int external_int;
|
|
|
|
UINT64 tb; /* 56-bit timebase register */
|
|
|
|
PPC_FETCH_REGION cur_fetch;
|
|
PPC_FETCH_REGION * fetch;
|
|
|
|
UINT32 dec, dec_frac;
|
|
UINT32 fpscr;
|
|
|
|
FPR fpr[32];
|
|
UINT32 sr[16];
|
|
} PPC_REGS;
|
|
|
|
static int ppc_icount;
|
|
static int ppc_stolen_cycles;
|
|
static int ppc_decrementer_exception_scheduled;
|
|
static int ppc_tb_base_icount;
|
|
static int ppc_dec_base_icount;
|
|
static int ppc_dec_trigger_cycle;
|
|
static int ppc_dec_divider;
|
|
static int bus_freq_multiplier = 1;
|
|
static PPC_REGS ppc;
|
|
static UINT32 ppc_rotate_mask[32][32];
|
|
|
|
|
|
#define TB_DIVIDER 8 // NOTE: Daytona 2 won't work with 4
|
|
|
|
INLINE int CYCLES_TO_DEC(int cycles)
|
|
{
|
|
return cycles / ppc_dec_divider;
|
|
}
|
|
|
|
INLINE int DEC_TO_CYCLES(int dec)
|
|
{
|
|
return dec * ppc_dec_divider;
|
|
}
|
|
|
|
|
|
|
|
static void ppc_change_pc(UINT32 newpc)
|
|
{
|
|
UINT i;
|
|
|
|
if (ppc.cur_fetch.start <= newpc && newpc <= ppc.cur_fetch.end)
|
|
{
|
|
ppc.op = (UINT32 *)((UINT32)ppc.cur_fetch.ptr + (UINT32)(newpc - ppc.cur_fetch.start));
|
|
return;
|
|
}
|
|
|
|
for(i = 0; ppc.fetch[i].ptr != NULL; i++)
|
|
{
|
|
if (ppc.fetch[i].start <= newpc && newpc <= ppc.fetch[i].end)
|
|
{
|
|
ppc.cur_fetch.start = ppc.fetch[i].start;
|
|
ppc.cur_fetch.end = ppc.fetch[i].end;
|
|
ppc.cur_fetch.ptr = ppc.fetch[i].ptr;
|
|
|
|
ppc.op = (UINT32 *)((UINT32)ppc.cur_fetch.ptr + (UINT32)(newpc - ppc.cur_fetch.start));
|
|
|
|
return;
|
|
}
|
|
}
|
|
|
|
error("Invalid PC %08X, previous PC %08X\n", newpc, ppc.pc);
|
|
}
|
|
|
|
INLINE UINT8 READ8(UINT32 address)
|
|
{
|
|
return m3_ppc_read_8(address);
|
|
}
|
|
|
|
INLINE UINT16 READ16(UINT32 address)
|
|
{
|
|
return m3_ppc_read_16(address);
|
|
}
|
|
|
|
INLINE UINT32 READ32(UINT32 address)
|
|
{
|
|
return m3_ppc_read_32(address);
|
|
}
|
|
|
|
INLINE UINT64 READ64(UINT32 address)
|
|
{
|
|
return m3_ppc_read_64(address);
|
|
}
|
|
|
|
INLINE void WRITE8(UINT32 address, UINT8 data)
|
|
{
|
|
m3_ppc_write_8(address, data);
|
|
}
|
|
|
|
INLINE void WRITE16(UINT32 address, UINT16 data)
|
|
{
|
|
m3_ppc_write_16(address, data);
|
|
}
|
|
|
|
INLINE void WRITE32(UINT32 address, UINT32 data)
|
|
{
|
|
m3_ppc_write_32(address, data);
|
|
}
|
|
|
|
INLINE void WRITE64(UINT32 address, UINT64 data)
|
|
{
|
|
m3_ppc_write_64(address, data);
|
|
}
|
|
|
|
|
|
/*********************************************************************/
|
|
|
|
|
|
INLINE void SET_CR0(INT32 rd)
|
|
{
|
|
if( rd < 0 ) {
|
|
CR(0) = 0x8;
|
|
} else if( rd > 0 ) {
|
|
CR(0) = 0x4;
|
|
} else {
|
|
CR(0) = 0x2;
|
|
}
|
|
|
|
if( XER & XER_SO )
|
|
CR(0) |= 0x1;
|
|
}
|
|
|
|
INLINE void SET_CR1(void)
|
|
{
|
|
CR(1) = (ppc.fpscr >> 28) & 0xf;
|
|
}
|
|
|
|
INLINE void SET_ADD_OV(UINT32 rd, UINT32 ra, UINT32 rb)
|
|
{
|
|
if( ADD_OV(rd, ra, rb) )
|
|
XER |= XER_SO | XER_OV;
|
|
else
|
|
XER &= ~XER_OV;
|
|
}
|
|
|
|
INLINE void SET_SUB_OV(UINT32 rd, UINT32 ra, UINT32 rb)
|
|
{
|
|
if( SUB_OV(rd, ra, rb) )
|
|
XER |= XER_SO | XER_OV;
|
|
else
|
|
XER &= ~XER_OV;
|
|
}
|
|
|
|
INLINE void SET_ADD_CA(UINT32 rd, UINT32 ra, UINT32 rb)
|
|
{
|
|
if( ADD_CA(rd, ra, rb) )
|
|
XER |= XER_CA;
|
|
else
|
|
XER &= ~XER_CA;
|
|
}
|
|
|
|
INLINE void SET_SUB_CA(UINT32 rd, UINT32 ra, UINT32 rb)
|
|
{
|
|
if( SUB_CA(rd, ra, rb) )
|
|
XER |= XER_CA;
|
|
else
|
|
XER &= ~XER_CA;
|
|
}
|
|
|
|
INLINE UINT32 check_condition_code(UINT32 bo, UINT32 bi)
|
|
{
|
|
UINT32 ctr_ok;
|
|
UINT32 condition_ok;
|
|
UINT32 bo0 = (bo & 0x10) ? 1 : 0;
|
|
UINT32 bo1 = (bo & 0x08) ? 1 : 0;
|
|
UINT32 bo2 = (bo & 0x04) ? 1 : 0;
|
|
UINT32 bo3 = (bo & 0x02) ? 1 : 0;
|
|
|
|
if( bo2 == 0 )
|
|
--CTR;
|
|
|
|
ctr_ok = bo2 | ((CTR != 0) ^ bo3);
|
|
condition_ok = bo0 | (CRBIT(bi) ^ (~bo1 & 0x1));
|
|
|
|
return ctr_ok && condition_ok;
|
|
}
|
|
|
|
INLINE UINT64 ppc_read_timebase(void)
|
|
{
|
|
int cycles = ppc_tb_base_icount - ppc_icount;
|
|
|
|
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
|
|
return ppc.tb + (cycles / TB_DIVIDER);
|
|
}
|
|
|
|
INLINE void ppc_write_timebase_l(UINT32 tbl)
|
|
{
|
|
ppc_tb_base_icount = ppc_icount;
|
|
|
|
ppc.tb &= ~0xffffffff;
|
|
ppc.tb |= tbl;
|
|
}
|
|
|
|
INLINE void ppc_write_timebase_h(UINT32 tbh)
|
|
{
|
|
ppc_tb_base_icount = ppc_icount;
|
|
|
|
ppc.tb &= 0xffffffff;
|
|
ppc.tb |= (UINT64)(tbh) << 32;
|
|
}
|
|
|
|
INLINE UINT32 read_decrementer(void)
|
|
{
|
|
int cycles = ppc_dec_base_icount - ppc_icount;
|
|
int dec_cycles = CYCLES_TO_DEC(cycles);
|
|
|
|
// decrementer is decremented once every four bus clock cycles, so adjust the cycles accordingly
|
|
return DEC - dec_cycles;
|
|
}
|
|
|
|
INLINE void write_decrementer(UINT32 value)
|
|
{
|
|
int dec_cycles;
|
|
//ppc_dec_base_icount = ppc_icount + (ppc_dec_base_icount - ppc_icount) % (bus_freq_multiplier * 2);
|
|
ppc_dec_base_icount = ppc_icount;
|
|
|
|
DEC = value;
|
|
|
|
dec_cycles = CYCLES_TO_DEC(ppc_icount);
|
|
|
|
// check if decrementer exception occurs during execution
|
|
if ((UINT32)(DEC - dec_cycles) > (UINT32)(DEC))
|
|
{
|
|
ppc_dec_trigger_cycle = DEC_TO_CYCLES((dec_cycles - DEC));
|
|
|
|
ppc_stolen_cycles += ppc_dec_trigger_cycle;
|
|
ppc_icount = ppc_icount - ppc_dec_trigger_cycle;
|
|
|
|
ppc_decrementer_exception_scheduled = 1;
|
|
}
|
|
else
|
|
{
|
|
ppc_dec_trigger_cycle = 0x7fffffff;
|
|
|
|
ppc_decrementer_exception_scheduled = 0;
|
|
}
|
|
}
|
|
|
|
/*********************************************************************/
|
|
|
|
INLINE void ppc_set_spr(int spr, UINT32 value)
|
|
{
|
|
switch (spr)
|
|
{
|
|
case SPR_LR: LR = value; return;
|
|
case SPR_CTR: CTR = value; return;
|
|
case SPR_XER: XER = value; return;
|
|
case SPR_SRR0: ppc.srr0 = value; return;
|
|
case SPR_SRR1: ppc.srr1 = value; return;
|
|
case SPR_SPRG0: ppc.sprg[0] = value; return;
|
|
case SPR_SPRG1: ppc.sprg[1] = value; return;
|
|
case SPR_SPRG2: ppc.sprg[2] = value; return;
|
|
case SPR_SPRG3: ppc.sprg[3] = value; return;
|
|
case SPR_PVR: return;
|
|
|
|
case SPR603E_DEC:
|
|
if(((value & 0x80000000) && !(DEC & 0x80000000)) || value == 0)
|
|
{
|
|
/* trigger interrupt */
|
|
//ppc.interrupt_pending |= 0x2;
|
|
ppc.interrupt_pending |= 0x2;
|
|
if (ppc.msr & MSR_EE)
|
|
{
|
|
ppc_stolen_cycles += ppc_icount;
|
|
ppc_icount = 0;
|
|
}
|
|
|
|
DEC = value;
|
|
return;
|
|
}
|
|
write_decrementer(value);
|
|
return;
|
|
|
|
case SPR603E_TBL_W:
|
|
case SPR603E_TBL_R: // special 603e case
|
|
ppc_write_timebase_l(value);
|
|
return;
|
|
|
|
case SPR603E_TBU_R:
|
|
case SPR603E_TBU_W: // special 603e case
|
|
ppc_write_timebase_h(value);
|
|
return;
|
|
|
|
case SPR603E_HID0: ppc.hid0 = value; return;
|
|
case SPR603E_HID1: ppc.hid1 = value; return;
|
|
case SPR603E_HID2: ppc.hid2 = value; return;
|
|
|
|
case SPR603E_IBAT0L: ppc.ibat[0].l = value; return;
|
|
case SPR603E_IBAT0U: ppc.ibat[0].u = value; return;
|
|
case SPR603E_IBAT1L: ppc.ibat[1].l = value; return;
|
|
case SPR603E_IBAT1U: ppc.ibat[1].u = value; return;
|
|
case SPR603E_IBAT2L: ppc.ibat[2].l = value; return;
|
|
case SPR603E_IBAT2U: ppc.ibat[2].u = value; return;
|
|
case SPR603E_IBAT3L: ppc.ibat[3].l = value; return;
|
|
case SPR603E_IBAT3U: ppc.ibat[3].u = value; return;
|
|
case SPR603E_DBAT0L: ppc.dbat[0].l = value; return;
|
|
case SPR603E_DBAT0U: ppc.dbat[0].u = value; return;
|
|
case SPR603E_DBAT1L: ppc.dbat[1].l = value; return;
|
|
case SPR603E_DBAT1U: ppc.dbat[1].u = value; return;
|
|
case SPR603E_DBAT2L: ppc.dbat[2].l = value; return;
|
|
case SPR603E_DBAT2U: ppc.dbat[2].u = value; return;
|
|
case SPR603E_DBAT3L: ppc.dbat[3].l = value; return;
|
|
case SPR603E_DBAT3U: ppc.dbat[3].u = value; return;
|
|
|
|
case SPR603E_SDR1:
|
|
ppc.sdr1 = value;
|
|
return;
|
|
}
|
|
|
|
error("ppc: set_spr: unknown spr %d (%03X) !", spr, spr);
|
|
}
|
|
|
|
INLINE UINT32 ppc_get_spr(int spr)
|
|
{
|
|
switch(spr)
|
|
{
|
|
case SPR_LR: return LR;
|
|
case SPR_CTR: return CTR;
|
|
case SPR_XER: return XER;
|
|
case SPR_SRR0: return ppc.srr0;
|
|
case SPR_SRR1: return ppc.srr1;
|
|
case SPR_SPRG0: return ppc.sprg[0];
|
|
case SPR_SPRG1: return ppc.sprg[1];
|
|
case SPR_SPRG2: return ppc.sprg[2];
|
|
case SPR_SPRG3: return ppc.sprg[3];
|
|
case SPR_PVR: return ppc.pvr;
|
|
case SPR603E_TBL_R:
|
|
error("ppc: get_spr: TBL_R ");
|
|
break;
|
|
|
|
case SPR603E_TBU_R:
|
|
error("ppc: get_spr: TBU_R ");
|
|
break;
|
|
|
|
case SPR603E_TBL_W: return (UINT32)(ppc_read_timebase());
|
|
case SPR603E_TBU_W: return (UINT32)(ppc_read_timebase() >> 32);
|
|
case SPR603E_HID0: return ppc.hid0;
|
|
case SPR603E_HID1: return ppc.hid1;
|
|
case SPR603E_HID2: return ppc.hid2;
|
|
case SPR603E_DEC: return read_decrementer();
|
|
case SPR603E_SDR1: return ppc.sdr1;
|
|
case SPR603E_IBAT0L: return ppc.ibat[0].l;
|
|
case SPR603E_IBAT0U: return ppc.ibat[0].u;
|
|
case SPR603E_IBAT1L: return ppc.ibat[1].l;
|
|
case SPR603E_IBAT1U: return ppc.ibat[1].u;
|
|
case SPR603E_IBAT2L: return ppc.ibat[2].l;
|
|
case SPR603E_IBAT2U: return ppc.ibat[2].u;
|
|
case SPR603E_IBAT3L: return ppc.ibat[3].l;
|
|
case SPR603E_IBAT3U: return ppc.ibat[3].u;
|
|
case SPR603E_DBAT0L: return ppc.dbat[0].l;
|
|
case SPR603E_DBAT0U: return ppc.dbat[0].u;
|
|
case SPR603E_DBAT1L: return ppc.dbat[1].l;
|
|
case SPR603E_DBAT1U: return ppc.dbat[1].u;
|
|
case SPR603E_DBAT2L: return ppc.dbat[2].l;
|
|
case SPR603E_DBAT2U: return ppc.dbat[2].u;
|
|
case SPR603E_DBAT3L: return ppc.dbat[3].l;
|
|
case SPR603E_DBAT3U: return ppc.dbat[3].u;
|
|
}
|
|
|
|
error("ppc: get_spr: unknown spr %d (%03X) !", spr, spr);
|
|
return 0;
|
|
}
|
|
|
|
INLINE void ppc_set_msr(UINT32 value)
|
|
{
|
|
if( value & (MSR_ILE | MSR_LE) )
|
|
error("ppc: set_msr: little_endian mode not supported !");
|
|
|
|
if (value & MSR_EE)
|
|
{
|
|
if (ppc.interrupt_pending != 0)
|
|
{
|
|
ppc_stolen_cycles = ppc_icount;
|
|
ppc_icount = 0;
|
|
}
|
|
}
|
|
|
|
MSR = value;
|
|
}
|
|
|
|
INLINE UINT32 ppc_get_msr(void)
|
|
{
|
|
return MSR;
|
|
}
|
|
|
|
INLINE void ppc_set_cr(UINT32 value)
|
|
{
|
|
CR(0) = (value >> 28) & 0xf;
|
|
CR(1) = (value >> 24) & 0xf;
|
|
CR(2) = (value >> 20) & 0xf;
|
|
CR(3) = (value >> 16) & 0xf;
|
|
CR(4) = (value >> 12) & 0xf;
|
|
CR(5) = (value >> 8) & 0xf;
|
|
CR(6) = (value >> 4) & 0xf;
|
|
CR(7) = (value >> 0) & 0xf;
|
|
}
|
|
|
|
INLINE UINT32 ppc_get_cr(void)
|
|
{
|
|
return CR(0) << 28 | CR(1) << 24 | CR(2) << 20 | CR(3) << 16 | CR(4) << 12 | CR(5) << 8 | CR(6) << 4 | CR(7);
|
|
}
|
|
|
|
static UINT32 (* drc_op_table19[1024])(UINT32);
|
|
static UINT32 (* drc_op_table31[1024])(UINT32);
|
|
static UINT32 (* drc_op_table59[1024])(UINT32);
|
|
static UINT32 (* drc_op_table63[1024])(UINT32);
|
|
static UINT32 (* drc_op_table[64])(UINT32);
|
|
|
|
#include "ppc_itp/ppc_ops.c"
|
|
#include "ppc_drc_ops.c"
|
|
|
|
static void init_ppc_drc(void)
|
|
{
|
|
int i;
|
|
for (i=0; i < 64; i++)
|
|
{
|
|
drc_op_table[i] = drc_invalid;
|
|
}
|
|
for (i=0; i < 1024; i++)
|
|
{
|
|
drc_op_table19[i] = drc_invalid;
|
|
drc_op_table31[i] = drc_invalid;
|
|
drc_op_table59[i] = drc_invalid;
|
|
drc_op_table63[i] = drc_invalid;
|
|
}
|
|
|
|
drc_op_table[ 3] = drc_twi;
|
|
drc_op_table[ 7] = drc_mulli;
|
|
drc_op_table[ 8] = drc_subfic;
|
|
drc_op_table[10] = drc_cmpli;
|
|
drc_op_table[11] = drc_cmpi;
|
|
drc_op_table[12] = drc_addic;
|
|
drc_op_table[13] = drc_addic_rc;
|
|
drc_op_table[14] = drc_addi;
|
|
drc_op_table[15] = drc_addis;
|
|
drc_op_table[16] = drc_bc;
|
|
drc_op_table[17] = drc_sc;
|
|
drc_op_table[18] = drc_b;
|
|
drc_op_table[20] = drc_rlwimi;
|
|
drc_op_table[21] = drc_rlwinm;
|
|
drc_op_table[23] = drc_rlwnm;
|
|
drc_op_table[24] = drc_ori;
|
|
drc_op_table[25] = drc_oris;
|
|
drc_op_table[26] = drc_xori;
|
|
drc_op_table[27] = drc_xoris;
|
|
drc_op_table[28] = drc_andi_rc;
|
|
drc_op_table[29] = drc_andis_rc;
|
|
drc_op_table[32] = drc_lwz;
|
|
drc_op_table[33] = drc_lwzu;
|
|
drc_op_table[34] = drc_lbz;
|
|
drc_op_table[35] = drc_lbzu;
|
|
drc_op_table[36] = drc_stw;
|
|
drc_op_table[37] = drc_stwu;
|
|
drc_op_table[38] = drc_stb;
|
|
drc_op_table[39] = drc_stbu;
|
|
drc_op_table[40] = drc_lhz;
|
|
drc_op_table[41] = drc_lhzu;
|
|
drc_op_table[42] = drc_lha;
|
|
drc_op_table[43] = drc_lhau;
|
|
drc_op_table[44] = drc_sth;
|
|
drc_op_table[45] = drc_sthu;
|
|
drc_op_table[46] = drc_lmw;
|
|
drc_op_table[47] = drc_stmw;
|
|
drc_op_table[48] = drc_lfs;
|
|
drc_op_table[49] = drc_lfsu;
|
|
drc_op_table[50] = drc_lfd;
|
|
drc_op_table[51] = drc_lfdu;
|
|
drc_op_table[52] = drc_stfs;
|
|
drc_op_table[53] = drc_stfsu;
|
|
drc_op_table[54] = drc_stfd;
|
|
drc_op_table[55] = drc_stfdu;
|
|
|
|
drc_op_table19[ 0] = drc_mcrf;
|
|
drc_op_table19[ 16] = drc_bclr;
|
|
drc_op_table19[ 33] = drc_crnor;
|
|
drc_op_table19[ 50] = drc_rfi;
|
|
drc_op_table19[ 129] = drc_crandc;
|
|
drc_op_table19[ 150] = drc_isync;
|
|
drc_op_table19[ 193] = drc_crxor;
|
|
drc_op_table19[ 225] = drc_crnand;
|
|
drc_op_table19[ 257] = drc_crand;
|
|
drc_op_table19[ 289] = drc_creqv;
|
|
drc_op_table19[ 417] = drc_crorc;
|
|
drc_op_table19[ 449] = drc_cror;
|
|
drc_op_table19[ 528] = drc_bcctr;
|
|
|
|
drc_op_table31[0x000] = drc_cmp;
|
|
drc_op_table31[0x004] = drc_tw;
|
|
drc_op_table31[0x008] = drc_subfc;
|
|
drc_op_table31[0x208] = drc_subfc;
|
|
drc_op_table31[0x00a] = drc_addc;
|
|
drc_op_table31[0x20a] = drc_addc;
|
|
drc_op_table31[0x00b] = drc_mulhwu;
|
|
drc_op_table31[0x013] = drc_mfcr;
|
|
drc_op_table31[0x014] = drc_lwarx;
|
|
drc_op_table31[0x017] = drc_lwzx;
|
|
drc_op_table31[0x018] = drc_slw;
|
|
drc_op_table31[0x01a] = drc_cntlzw;
|
|
drc_op_table31[0x01c] = drc_and;
|
|
drc_op_table31[0x020] = drc_cmpl;
|
|
drc_op_table31[0x028] = drc_subf;
|
|
drc_op_table31[0x228] = drc_subf;
|
|
drc_op_table31[0x036] = drc_dcbst;
|
|
drc_op_table31[0x037] = drc_lwzux;
|
|
drc_op_table31[0x03c] = drc_andc;
|
|
drc_op_table31[0x04b] = drc_mulhw;
|
|
drc_op_table31[0x053] = drc_mfmsr;
|
|
drc_op_table31[0x056] = drc_dcbf;
|
|
drc_op_table31[0x057] = drc_lbzx;
|
|
drc_op_table31[0x068] = drc_neg;
|
|
drc_op_table31[0x268] = drc_neg;
|
|
drc_op_table31[0x077] = drc_lbzux;
|
|
drc_op_table31[0x07c] = drc_nor;
|
|
drc_op_table31[0x088] = drc_subfe;
|
|
drc_op_table31[0x288] = drc_subfe;
|
|
drc_op_table31[0x08a] = drc_adde;
|
|
drc_op_table31[0x28a] = drc_adde;
|
|
drc_op_table31[0x090] = drc_mtcrf;
|
|
drc_op_table31[0x092] = drc_mtmsr;
|
|
drc_op_table31[0x096] = drc_stwcx_rc;
|
|
drc_op_table31[0x097] = drc_stwx;
|
|
drc_op_table31[0x0b7] = drc_stwux;
|
|
drc_op_table31[0x0c8] = drc_subfze;
|
|
drc_op_table31[0x2c8] = drc_subfze;
|
|
drc_op_table31[0x0ca] = drc_addze;
|
|
drc_op_table31[0x2ca] = drc_addze;
|
|
drc_op_table31[0x0d2] = drc_mtsr;
|
|
drc_op_table31[0x0d7] = drc_stbx;
|
|
drc_op_table31[0x0e8] = drc_subfme;
|
|
drc_op_table31[0x2e8] = drc_subfme;
|
|
drc_op_table31[0x0ea] = drc_addme;
|
|
drc_op_table31[0x2ea] = drc_addme;
|
|
drc_op_table31[0x0eb] = drc_mullw;
|
|
drc_op_table31[0x2eb] = drc_mullw;
|
|
drc_op_table31[0x0f2] = drc_mtsrin;
|
|
drc_op_table31[0x0f6] = drc_dcbtst;
|
|
drc_op_table31[0x0f7] = drc_stbux;
|
|
drc_op_table31[0x10a] = drc_add;
|
|
drc_op_table31[0x30a] = drc_add;
|
|
drc_op_table31[0x116] = drc_dcbt;
|
|
drc_op_table31[0x117] = drc_lhzx;
|
|
drc_op_table31[0x11c] = drc_eqv;
|
|
drc_op_table31[0x132] = drc_tlbie;
|
|
drc_op_table31[0x136] = drc_eciwx;
|
|
drc_op_table31[0x137] = drc_lhzux;
|
|
drc_op_table31[0x13c] = drc_xor;
|
|
drc_op_table31[0x153] = drc_mfspr;
|
|
drc_op_table31[0x157] = drc_lhax;
|
|
drc_op_table31[0x172] = drc_tlbia;
|
|
drc_op_table31[0x173] = drc_mftb;
|
|
drc_op_table31[0x177] = drc_lhaux;
|
|
drc_op_table31[0x197] = drc_sthx;
|
|
drc_op_table31[0x19c] = drc_orc;
|
|
drc_op_table31[0x1b6] = drc_ecowx;
|
|
drc_op_table31[0x1b7] = drc_sthux;
|
|
drc_op_table31[0x1bc] = drc_or;
|
|
drc_op_table31[0x1cb] = drc_divwu;
|
|
drc_op_table31[0x3cb] = drc_divwu;
|
|
drc_op_table31[0x1d3] = drc_mtspr;
|
|
drc_op_table31[0x1d6] = drc_dcbi;
|
|
drc_op_table31[0x1dc] = drc_nand;
|
|
drc_op_table31[0x1eb] = drc_divw;
|
|
drc_op_table31[0x3eb] = drc_divw;
|
|
drc_op_table31[0x200] = drc_mcrxr;
|
|
drc_op_table31[0x215] = drc_lswx;
|
|
drc_op_table31[0x216] = drc_lwbrx;
|
|
drc_op_table31[0x217] = drc_lfsx;
|
|
drc_op_table31[0x218] = drc_srw;
|
|
drc_op_table31[0x236] = drc_tlbsync;
|
|
drc_op_table31[0x237] = drc_lfsux;
|
|
drc_op_table31[0x253] = drc_mfsr;
|
|
drc_op_table31[0x255] = drc_lswi;
|
|
drc_op_table31[0x256] = drc_sync;
|
|
drc_op_table31[0x257] = drc_lfdx;
|
|
drc_op_table31[0x277] = drc_lfdux;
|
|
drc_op_table31[0x293] = drc_mfsrin;
|
|
drc_op_table31[0x295] = drc_stswx;
|
|
drc_op_table31[0x296] = drc_stwbrx;
|
|
drc_op_table31[0x297] = drc_stfsx;
|
|
drc_op_table31[0x2b7] = drc_stfsux;
|
|
drc_op_table31[0x2d5] = drc_stswi;
|
|
drc_op_table31[0x2d7] = drc_stfdx;
|
|
drc_op_table31[0x2f6] = drc_dcba;
|
|
drc_op_table31[0x2f7] = drc_stfdux;
|
|
drc_op_table31[0x316] = drc_lhbrx;
|
|
drc_op_table31[0x318] = drc_sraw;
|
|
drc_op_table31[0x338] = drc_srawi;
|
|
drc_op_table31[0x356] = drc_eieio;
|
|
drc_op_table31[0x396] = drc_sthbrx;
|
|
drc_op_table31[0x39a] = drc_extsh;
|
|
drc_op_table31[0x3ba] = drc_extsb;
|
|
drc_op_table31[0x3d6] = drc_icbi;
|
|
drc_op_table31[0x3d7] = drc_stfiwx;
|
|
drc_op_table31[0x3f6] = drc_dcbz;
|
|
|
|
drc_op_table59[0x012] = drc_fdivs;
|
|
drc_op_table59[0x014] = drc_fsubs;
|
|
drc_op_table59[0x015] = drc_fadds;
|
|
drc_op_table59[0x016] = drc_fsqrts;
|
|
drc_op_table59[0x018] = drc_fres;
|
|
for (i=0; i < 32; i++)
|
|
{
|
|
drc_op_table59[(i << 5) | 0x019] = drc_fmuls;
|
|
drc_op_table59[(i << 5) | 0x01c] = drc_fmsubs;
|
|
drc_op_table59[(i << 5) | 0x01d] = drc_fmadds;
|
|
drc_op_table59[(i << 5) | 0x01e] = drc_fnmsubs;
|
|
drc_op_table59[(i << 5) | 0x01f] = drc_fnmadds;
|
|
}
|
|
|
|
drc_op_table63[0x000] = drc_fcmpu;
|
|
drc_op_table63[0x00c] = drc_frsp;
|
|
drc_op_table63[0x00e] = drc_fctiw;
|
|
drc_op_table63[0x00f] = drc_fctiwz;
|
|
drc_op_table63[0x012] = drc_fdiv;
|
|
drc_op_table63[0x014] = drc_fsub;
|
|
drc_op_table63[0x015] = drc_fadd;
|
|
drc_op_table63[0x016] = drc_fsqrt;
|
|
drc_op_table63[0x01a] = drc_frsqrte;
|
|
drc_op_table63[0x020] = drc_fcmpo;
|
|
drc_op_table63[0x026] = drc_mtfsb1;
|
|
drc_op_table63[0x028] = drc_fneg;
|
|
drc_op_table63[0x040] = drc_mcrfs;
|
|
drc_op_table63[0x046] = drc_mtfsb0;
|
|
drc_op_table63[0x048] = drc_fmr;
|
|
drc_op_table63[0x086] = drc_mtfsfi;
|
|
drc_op_table63[0x088] = drc_fnabs;
|
|
drc_op_table63[0x108] = drc_fabs;
|
|
drc_op_table63[0x247] = drc_mffs;
|
|
drc_op_table63[0x2c7] = drc_mtfsf;
|
|
for (i=0; i < 32; i++)
|
|
{
|
|
drc_op_table63[(i << 5) | 0x017] = drc_fsel;
|
|
drc_op_table63[(i << 5) | 0x019] = drc_fmul;
|
|
drc_op_table63[(i << 5) | 0x01c] = drc_fmsub;
|
|
drc_op_table63[(i << 5) | 0x01d] = drc_fmadd;
|
|
drc_op_table63[(i << 5) | 0x01e] = drc_fnmsub;
|
|
drc_op_table63[(i << 5) | 0x01f] = drc_fnmadd;
|
|
}
|
|
|
|
drccore_init();
|
|
}
|
|
|
|
void ppc_init(const PPC_CONFIG *config)
|
|
{
|
|
int pll_config = 0;
|
|
float multiplier;
|
|
int i, j;
|
|
|
|
/* Calculate rotate mask table */
|
|
for( i=0; i < 32; i++ ) {
|
|
for( j=0; j < 32; j++ ) {
|
|
UINT32 mask;
|
|
int mb = i;
|
|
int me = j;
|
|
mask = ((UINT32)0xFFFFFFFF >> mb) ^ ((me >= 31) ? 0 : ((UINT32)0xFFFFFFFF >> (me + 1)));
|
|
if( mb > me )
|
|
mask = ~mask;
|
|
|
|
ppc_rotate_mask[i][j] = mask;
|
|
}
|
|
}
|
|
|
|
for(i = 0; i < 256; i++)
|
|
{
|
|
ppc_field_xlat[i] =
|
|
((i & 0x80) ? 0xF0000000 : 0) |
|
|
((i & 0x40) ? 0x0F000000 : 0) |
|
|
((i & 0x20) ? 0x00F00000 : 0) |
|
|
((i & 0x10) ? 0x000F0000 : 0) |
|
|
((i & 0x08) ? 0x0000F000 : 0) |
|
|
((i & 0x04) ? 0x00000F00 : 0) |
|
|
((i & 0x02) ? 0x000000F0 : 0) |
|
|
((i & 0x01) ? 0x0000000F : 0);
|
|
}
|
|
|
|
ppc.pvr = config->pvr;
|
|
|
|
multiplier = (float)((config->bus_frequency_multiplier >> 4) & 0xf) +
|
|
(float)(config->bus_frequency_multiplier & 0xf) / 10.0f;
|
|
bus_freq_multiplier = (int)(multiplier * 2);
|
|
|
|
switch(config->pvr)
|
|
{
|
|
case PPC_MODEL_603E: pll_config = mpc603e_pll_config[bus_freq_multiplier-1][config->bus_frequency]; break;
|
|
case PPC_MODEL_603EV: pll_config = mpc603ev_pll_config[bus_freq_multiplier-1][config->bus_frequency]; break;
|
|
case PPC_MODEL_603R: pll_config = mpc603r_pll_config[bus_freq_multiplier-1][config->bus_frequency]; break;
|
|
default: break;
|
|
}
|
|
|
|
ppc_dec_divider = (int)(multiplier * 4);
|
|
|
|
if (pll_config == -1)
|
|
{
|
|
error("PPC: Invalid bus/multiplier combination (bus frequency = %d, multiplier = %1.1f)", config->bus_frequency, multiplier);
|
|
}
|
|
|
|
ppc.hid1 = pll_config << 28;
|
|
|
|
init_ppc_drc();
|
|
}
|
|
|
|
void ppc_shutdown(void)
|
|
{
|
|
drccore_shutdown();
|
|
}
|
|
|
|
void ppc_exception(int exception)
|
|
{
|
|
switch (exception)
|
|
{
|
|
case EXCEPTION_IRQ: /* External Interrupt */
|
|
{
|
|
if( ppc_get_msr() & MSR_EE )
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.pc;
|
|
SRR1 = msr & 0xff73;
|
|
|
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
|
if( msr & MSR_ILE )
|
|
msr |= MSR_LE;
|
|
else
|
|
msr &= ~MSR_LE;
|
|
ppc_set_msr(msr);
|
|
|
|
if( msr & MSR_IP )
|
|
ppc.pc = 0xfff00000 | 0x0500;
|
|
else
|
|
ppc.pc = 0x00000000 | 0x0500;
|
|
|
|
ppc.interrupt_pending &= ~0x1;
|
|
ppc_change_pc(ppc.pc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_DECREMENTER: /* Decrementer overflow exception */
|
|
{
|
|
if (ppc_get_msr() & MSR_EE)
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.pc;
|
|
SRR1 = msr & 0xff73;
|
|
|
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
|
if( msr & MSR_ILE )
|
|
msr |= MSR_LE;
|
|
else
|
|
msr &= ~MSR_LE;
|
|
ppc_set_msr(msr);
|
|
|
|
if( msr & MSR_IP )
|
|
ppc.pc = 0xfff00000 | 0x0900;
|
|
else
|
|
ppc.pc = 0x00000000 | 0x0900;
|
|
|
|
ppc.interrupt_pending &= ~0x2;
|
|
ppc_change_pc(ppc.pc);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case EXCEPTION_TRAP: /* Program exception / Trap */
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.pc;
|
|
SRR1 = (msr & 0xff73) | 0x20000; /* 0x20000 = TRAP bit */
|
|
|
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
|
if( msr & MSR_ILE )
|
|
msr |= MSR_LE;
|
|
else
|
|
msr &= ~MSR_LE;
|
|
ppc_set_msr(msr);
|
|
|
|
if( msr & MSR_IP )
|
|
ppc.pc = 0xfff00000 | 0x0700;
|
|
else
|
|
ppc.pc = 0x00000000 | 0x0700;
|
|
ppc_change_pc(ppc.pc);
|
|
}
|
|
break;
|
|
|
|
case EXCEPTION_SYSTEM_CALL: /* System call */
|
|
{
|
|
UINT32 msr = ppc_get_msr();
|
|
|
|
SRR0 = ppc.pc;
|
|
SRR1 = (msr & 0xff73);
|
|
|
|
msr &= ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI);
|
|
if( msr & MSR_ILE )
|
|
msr |= MSR_LE;
|
|
else
|
|
msr &= ~MSR_LE;
|
|
ppc_set_msr(msr);
|
|
|
|
if( msr & MSR_IP )
|
|
ppc.pc = 0xfff00000 | 0x0c00;
|
|
else
|
|
ppc.pc = 0x00000000 | 0x0c00;
|
|
ppc_change_pc(ppc.pc);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
error("ppc: Unhandled exception %d", exception);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void ppc_set_irq_line(int irqline)
|
|
{
|
|
ppc.interrupt_pending |= 0x1;
|
|
|
|
if (ppc.msr & MSR_EE)
|
|
{
|
|
ppc_stolen_cycles += ppc_icount;
|
|
ppc_icount = 0;
|
|
}
|
|
}
|
|
|
|
UINT32 ppc_get_pc(void)
|
|
{
|
|
return ppc.pc;
|
|
}
|
|
|
|
void ppc_set_fetch(PPC_FETCH_REGION * fetch)
|
|
{
|
|
ppc.fetch = fetch;
|
|
}
|
|
|
|
void ppc_reset(void)
|
|
{
|
|
ppc.pc = ppc.npc = 0xfff00100;
|
|
|
|
ppc_set_msr(0x40);
|
|
ppc_change_pc(ppc.pc);
|
|
|
|
ppc.hid0 = 1;
|
|
|
|
ppc.interrupt_pending = 0;
|
|
|
|
drccore_reset();
|
|
}
|
|
|
|
void ppc_check_interrupts(void)
|
|
{
|
|
if (ppc.msr & MSR_EE)
|
|
{
|
|
if (ppc.interrupt_pending & 0x1)
|
|
{
|
|
ppc_exception(EXCEPTION_IRQ);
|
|
}
|
|
else if (ppc.interrupt_pending & 0x2)
|
|
{
|
|
ppc_exception(EXCEPTION_DECREMENTER);
|
|
}
|
|
}
|
|
}
|
|
|
|
int ppc_execute(int cycles)
|
|
{
|
|
int run_cycles;
|
|
int cumulative_cycles;
|
|
int cycles_left;
|
|
UINT32 opcode;
|
|
|
|
/*
|
|
ppc_icount = cycles;
|
|
ppc_tb_base_icount = cycles;
|
|
ppc_dec_base_icount = cycles + ppc.dec_frac;
|
|
|
|
// check if decrementer exception occurs during execution
|
|
if ((UINT32)(DEC - (cycles / (bus_freq_multiplier * 2))) > (UINT32)(DEC))
|
|
{
|
|
ppc_dec_trigger_cycle = ((cycles / (bus_freq_multiplier * 2)) - DEC) * 4;
|
|
}
|
|
else
|
|
{
|
|
ppc_dec_trigger_cycle = 0x7fffffff;
|
|
}
|
|
|
|
run_cycles = ppc_icount;
|
|
ppc_icount = run_cycles / 2;
|
|
|
|
ppc_change_pc(ppc.pc);
|
|
ppc_check_interrupts();
|
|
drccore_execute();
|
|
|
|
ppc_icount = run_cycles / 2;
|
|
ppc.interrupt_pending |= 2;
|
|
ppc_change_pc(ppc.pc);
|
|
ppc_check_interrupts();
|
|
drccore_execute();
|
|
*/
|
|
|
|
cumulative_cycles = 0;
|
|
cycles_left = cycles;
|
|
while (cumulative_cycles < cycles)
|
|
{
|
|
int dec_count;
|
|
int real_cycles;
|
|
int target_cycles;
|
|
ppc_stolen_cycles = 0;
|
|
|
|
ppc_decrementer_exception_scheduled = 0;
|
|
|
|
//ppc_change_pc(ppc.pc);
|
|
ppc_check_interrupts();
|
|
|
|
//ppc_tb_base_icount = cycles_left;
|
|
//ppc_dec_base_icount = cycles_left + ppc.dec_frac;
|
|
|
|
// check if decrementer exception occurs during execution
|
|
dec_count = CYCLES_TO_DEC(cycles_left);
|
|
if ((UINT32)(DEC - dec_count) > (UINT32)(DEC))
|
|
{
|
|
ppc_dec_trigger_cycle = DEC_TO_CYCLES((dec_count - DEC));
|
|
}
|
|
else
|
|
{
|
|
ppc_dec_trigger_cycle = 0x7fffffff;
|
|
}
|
|
|
|
if (ppc_dec_trigger_cycle == cycles_left)
|
|
{
|
|
ppc_dec_trigger_cycle = 0x7fffffff;
|
|
}
|
|
|
|
if (ppc_dec_trigger_cycle != 0x7fffffff)
|
|
{
|
|
target_cycles = ppc_icount = cycles_left - ppc_dec_trigger_cycle;
|
|
ppc_decrementer_exception_scheduled = 1;
|
|
}
|
|
else
|
|
{
|
|
target_cycles = ppc_icount = cycles_left;
|
|
ppc_decrementer_exception_scheduled = 0;
|
|
}
|
|
|
|
ppc_tb_base_icount = ppc_icount;
|
|
ppc_dec_base_icount = ppc_icount + ppc.dec_frac;
|
|
|
|
drccore_execute();
|
|
if (ppc_decrementer_exception_scheduled)
|
|
{
|
|
ppc.interrupt_pending |= 2;
|
|
//ppc_change_pc(ppc.pc);
|
|
//ppc_check_interrupts();
|
|
}
|
|
|
|
real_cycles = (target_cycles - ppc_stolen_cycles) - ppc_icount;
|
|
cycles_left -= real_cycles;
|
|
cumulative_cycles += real_cycles;
|
|
|
|
// printf("run for %d cycles, stolen cycles %d, trigger cycle %d\n", real_cycles, ppc_stolen_cycles, ppc_dec_trigger_cycle);
|
|
|
|
{
|
|
int tb_cycles = (ppc_tb_base_icount) - ppc_stolen_cycles;
|
|
int dec_cycles = (ppc_dec_base_icount) - ppc_stolen_cycles;
|
|
|
|
// update timebase
|
|
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
|
|
ppc.tb += ((tb_cycles) / TB_DIVIDER);
|
|
|
|
// update decrementer
|
|
ppc.dec_frac = ((dec_cycles) % (bus_freq_multiplier * 2));
|
|
DEC -= CYCLES_TO_DEC(dec_cycles);
|
|
}
|
|
}
|
|
|
|
/*
|
|
// update timebase
|
|
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
|
|
ppc.tb += ((ppc_tb_base_icount - ppc_icount) / 4);
|
|
|
|
// update decrementer
|
|
ppc.dec_frac = ((ppc_dec_base_icount - ppc_icount) % (bus_freq_multiplier * 2));
|
|
DEC -= ((ppc_dec_base_icount - ppc_icount) / (bus_freq_multiplier * 2));
|
|
*/
|
|
|
|
/*
|
|
{
|
|
char string1[200];
|
|
char string2[200];
|
|
opcode = BSWAP32(*ppc.op);
|
|
DisassemblePowerPC(opcode, ppc.pc, string1, string2, TRUE);
|
|
printf("%08X: %s %s\n", ppc.pc, string1, string2);
|
|
}
|
|
*/
|
|
|
|
return cycles - ppc_icount;
|
|
}
|
|
|
|
|
|
|
|
|
|
static void **pc_lookup;
|
|
static void **pc_ram_lookup;
|
|
static void **pc_rom_lookup;
|
|
static void **pc_dummy_lookup;
|
|
|
|
#define PC_LOOKUP_SIZE 0x200 // 9 top bits of address
|
|
#define PC_LOOKUP_SIZE_SUB 0x200000 // 23 bits - 2 unused LSB bits
|
|
|
|
#define PC_LOOKUP_SHIFT 23 // how much to shift address to index main lookup
|
|
|
|
static void (* drc_entry_point)(void);
|
|
static void (* drc_exit_point)(void);
|
|
static void (* drc_compiler_point)(void);
|
|
static void (* drc_invalid_area_point)(void);
|
|
static void ( *drc_check_interrupts)(void);
|
|
static void (* drc_irq_exception)(void);
|
|
static void (* drc_decrementer_exception)(void);
|
|
|
|
void **drccore_get_lookup_ptr(UINT32 pc)
|
|
{
|
|
switch (pc >> PC_LOOKUP_SHIFT)
|
|
{
|
|
case 0x000: return &pc_ram_lookup[(pc & 0x7fffff) / 4];
|
|
case 0x1ff: return &pc_rom_lookup[(pc & 0x7fffff) / 4];
|
|
default: return &pc_dummy_lookup[(pc & 0x7fffff) / 4];
|
|
}
|
|
}
|
|
|
|
void drccore_insert_dispatcher(void)
|
|
{
|
|
gen(MOVMR, REG_ECX, (UINT32)&ppc.pc);
|
|
gen(MOV, REG_EAX, REG_ECX);
|
|
gen(SHRI, REG_EAX, PC_LOOKUP_SHIFT);
|
|
gen_mov_dprs_to_reg(REG_EAX, (UINT32)pc_lookup, REG_EAX, 4);
|
|
gen(ANDI, REG_ECX, (PC_LOOKUP_SIZE_SUB - 1) << 2);
|
|
gen_jmp_rpr(REG_EAX, REG_ECX);
|
|
}
|
|
|
|
static void out_of_cycles_sanity_check(void)
|
|
{
|
|
printf("out of cycles sanity check\n");
|
|
}
|
|
|
|
void drccore_insert_cycle_check(int cycles, UINT32 pc)
|
|
{
|
|
JUMP_TARGET timeslice_not_expired;
|
|
JUMP_TARGET no_decrementer_check;
|
|
JUMP_TARGET no_decrementer_exception;
|
|
JUMP_TARGET no_interrupts;
|
|
JUMP_TARGET interrupts_not_enabled;
|
|
init_jmp_target(×lice_not_expired);
|
|
init_jmp_target(&no_decrementer_check);
|
|
init_jmp_target(&no_decrementer_exception);
|
|
init_jmp_target(&no_interrupts);
|
|
init_jmp_target(&interrupts_not_enabled);
|
|
|
|
|
|
//gen(SUBIM, (UINT32)&ppc_icount, cycles);
|
|
|
|
// AAAAAARGH this is complex :-/
|
|
|
|
/*gen(MOVMR, REG_EAX, (UINT32)&ppc_icount);
|
|
gen(CMPIM, (UINT32)&ppc_dec_trigger_cycle, 0x7fffffff);
|
|
gen_jmp(JNZ, &no_decrementer_check);
|
|
|
|
gen(MOVMR, REG_EDX, (UINT32)&ppc_dec_trigger_cycle);
|
|
gen(CMP, REG_EDX, REG_EAX);
|
|
gen_jmp(JNS, &no_decrementer_exception);
|
|
|
|
gen(ORIM, (UINT32)&ppc.interrupt_pending, 0x2);
|
|
gen_jmp_target(&no_decrementer_exception);
|
|
|
|
gen_jmp_target(&no_decrementer_check);
|
|
|
|
|
|
|
|
gen(CMPIM, (UINT32)&ppc.interrupt_pending, 0);
|
|
gen_jmp(JZ, &no_interrupts);
|
|
|
|
gen(MOVMR, REG_EDX, (UINT32)&ppc.msr);
|
|
gen(TESTI, REG_EDX, MSR_EE);
|
|
gen_jmp(JZ, &interrupts_not_enabled);
|
|
gen(MOVIM, (UINT32)&ppc.pc, pc);
|
|
gen(JMPI, (UINT32)drc_check_interrupts, 0);
|
|
|
|
gen_jmp_target(&interrupts_not_enabled);
|
|
|
|
gen_jmp_target(&no_interrupts);
|
|
|
|
gen(CMPI, REG_EAX, 0);
|
|
*/
|
|
|
|
gen(CMPIM, (UINT32)&ppc_icount, 0);
|
|
|
|
gen_jmp(JNS, ×lice_not_expired); // continue if cycles >= 0
|
|
gen(MOVIM, (UINT32)&ppc.pc, pc);
|
|
gen(JMPI, (UINT32)drc_exit_point, 0);
|
|
|
|
gen_jmp_target(×lice_not_expired);
|
|
}
|
|
|
|
static void sanity_check(UINT32 value)
|
|
{
|
|
printf("ppc_sanity_check: %08X, cycles = %d\n", value, ppc_icount);
|
|
//printf("CTR = %08X, CR0 = %01X, CR1 = %01X\n", ppc.ctr, ppc.cr[0], ppc.cr[1]);
|
|
}
|
|
|
|
void drccore_insert_sanity_check(UINT32 value)
|
|
{
|
|
//gen(PUSHI, value, 0);
|
|
//gen(CALLI, (UINT32)&sanity_check, 0);
|
|
//gen(ADDI, REG_ESP, 4);
|
|
}
|
|
|
|
static UINT32 entry_point_stackptr;
|
|
static void exit_point_sanity_check(UINT32 stackptr)
|
|
{
|
|
printf("exit point sanity check: current ESP %08X, start %08X\n", stackptr, entry_point_stackptr);
|
|
}
|
|
|
|
static void check_irqs_sanity_check(void)
|
|
{
|
|
printf("check IRQs sanity check\n");
|
|
}
|
|
|
|
static void exception_sanity_check(void)
|
|
{
|
|
printf("exception sanity check\n");
|
|
}
|
|
|
|
static void drccore_insert_check_interrupts(void)
|
|
{
|
|
JUMP_TARGET interrupt_not_irq;
|
|
init_jmp_target(&interrupt_not_irq);
|
|
|
|
gen(MOVMR, REG_EAX, (UINT32)&ppc.interrupt_pending);
|
|
gen(TESTI, REG_EAX, 0x1);
|
|
gen_jmp(JZ, &interrupt_not_irq);
|
|
// IRQ
|
|
gen(MOVMR, REG_EDX, (UINT32)&ppc.pc);
|
|
gen(MOVRM, (UINT32)&ppc.srr0, REG_EDX);
|
|
gen(JMPM, (UINT32)&drc_irq_exception, 0);
|
|
|
|
gen_jmp_target(&interrupt_not_irq);
|
|
// decrementer
|
|
gen(MOVMR, REG_EDX, (UINT32)&ppc.pc);
|
|
gen(MOVRM, (UINT32)&ppc.srr0, REG_EDX);
|
|
gen(JMPM, (UINT32)&drc_decrementer_exception, 0);
|
|
}
|
|
|
|
static void drccore_insert_exception_generator(int exception)
|
|
{
|
|
JUMP_TARGET clear_le, exception_base_0;
|
|
init_jmp_target(&clear_le);
|
|
init_jmp_target(&exception_base_0);
|
|
|
|
gen(MOVMR, REG_EAX, (UINT32)&ppc.msr);
|
|
gen(ANDI, REG_EAX, 0xff73);
|
|
gen(MOVRM, (UINT32)&ppc.srr1, REG_EAX);
|
|
|
|
// Clear POW, EE, PR, FP, FE0, SE, BE, FE1, IR, DR, RI
|
|
gen(ANDI, REG_EAX, ~(MSR_POW | MSR_EE | MSR_PR | MSR_FP | MSR_FE0 | MSR_SE | MSR_BE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI));
|
|
|
|
// Set LE to ILE
|
|
gen(ANDI, REG_EAX, ~MSR_LE); // clear LE first
|
|
gen(TESTI, REG_EAX, MSR_ILE);
|
|
gen_jmp(JZ, &clear_le); // if Z == 0, bit = 1
|
|
gen(ORI, REG_EAX, MSR_LE); // set LE
|
|
gen_jmp_target(&clear_le);
|
|
|
|
gen(MOVRM, (UINT32)&ppc.msr, REG_EAX);
|
|
|
|
switch (exception)
|
|
{
|
|
case EXCEPTION_IRQ:
|
|
{
|
|
gen(MOVI, REG_EDI, 0x00000500); // exception vector
|
|
gen(TESTI, REG_EAX, MSR_IP);
|
|
gen_jmp(JZ, &exception_base_0); // if Z == 1, bit = 0 means base == 0x00000000
|
|
gen(ORI, REG_EDI, 0xfff00000);
|
|
gen_jmp_target(&exception_base_0);
|
|
|
|
gen(ANDIM, (UINT32)&ppc.interrupt_pending, ~0x1); // clear decrementer exception
|
|
|
|
gen(MOVRM, (UINT32)&ppc.pc, REG_EDI);
|
|
drccore_insert_dispatcher();
|
|
break;
|
|
}
|
|
case EXCEPTION_DECREMENTER:
|
|
{
|
|
gen(MOVI, REG_EDI, 0x00000900); // exception vector
|
|
gen(TESTI, REG_EAX, MSR_IP);
|
|
gen_jmp(JZ, &exception_base_0); // if Z == 1, bit = 0 means base == 0x00000000
|
|
gen(ORI, REG_EDI, 0xfff00000);
|
|
gen_jmp_target(&exception_base_0);
|
|
|
|
gen(ANDIM, (UINT32)&ppc.interrupt_pending, ~0x2); // clear decrementer exception
|
|
|
|
gen(MOVRM, (UINT32)&ppc.pc, REG_EDI);
|
|
drccore_insert_dispatcher();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void drccore_init(void)
|
|
{
|
|
int i;
|
|
|
|
init_genx86();
|
|
|
|
pc_lookup = malloc(sizeof(void*) * PC_LOOKUP_SIZE);
|
|
|
|
// these lookups are the only ones we need runtime for Model 3
|
|
pc_ram_lookup = malloc(sizeof(void*) * PC_LOOKUP_SIZE_SUB);
|
|
pc_rom_lookup = malloc(sizeof(void*) * PC_LOOKUP_SIZE_SUB);
|
|
|
|
// this table will catch all the possible jumps outside ram/rom
|
|
pc_dummy_lookup = malloc(sizeof(void*) * PC_LOOKUP_SIZE_SUB);
|
|
|
|
// initialize main lookup
|
|
for (i=0; i < PC_LOOKUP_SIZE; i++)
|
|
{
|
|
pc_lookup[i] = pc_dummy_lookup;
|
|
}
|
|
|
|
// we know that rom is at location 0xff800000 - 0xffffffff
|
|
pc_lookup[0x1ff] = pc_rom_lookup;
|
|
// we know that ram is at location 0x00000000 - 0x007fffff
|
|
pc_lookup[0x000] = pc_ram_lookup;
|
|
}
|
|
|
|
void drccore_shutdown(void)
|
|
{
|
|
shutdown_genx86();
|
|
}
|
|
|
|
void drccore_reset(void)
|
|
{
|
|
int i;
|
|
|
|
gen_reset_cache();
|
|
|
|
// insert dynarec entry point
|
|
drc_entry_point = (void*)gen_get_cache_pos();
|
|
gen(PUSHAD, 0, 0);
|
|
drccore_insert_dispatcher();
|
|
|
|
// insert dynarec exit point
|
|
drc_exit_point = (void*)gen_get_cache_pos();
|
|
gen(POPAD, 0, 0);
|
|
gen(RET, 0, 0);
|
|
|
|
// insert dynarec compiler point
|
|
drc_compiler_point = (void*)gen_get_cache_pos();
|
|
gen(CALLI, (UINT32)drc_recompile_block, 0);
|
|
drccore_insert_dispatcher();
|
|
|
|
// insert dynarec invalid area point
|
|
drc_invalid_area_point = (void*)gen_get_cache_pos();
|
|
gen(CALLI, (UINT32)drc_invalid_area, 0);
|
|
|
|
// insert check interrupts
|
|
drc_check_interrupts = (void*)gen_get_cache_pos();
|
|
drccore_insert_check_interrupts();
|
|
|
|
// insert irq exception generator
|
|
drc_irq_exception = (void*)gen_get_cache_pos();
|
|
drccore_insert_exception_generator(EXCEPTION_IRQ);
|
|
|
|
// insert decrementer exception generator
|
|
drc_decrementer_exception = (void*)gen_get_cache_pos();
|
|
drccore_insert_exception_generator(EXCEPTION_DECREMENTER);
|
|
|
|
// initialize sub lookups
|
|
for (i=0; i < PC_LOOKUP_SIZE_SUB; i++)
|
|
{
|
|
// point valid areas to recompile handler
|
|
pc_ram_lookup[i] = (void*)drc_compiler_point;
|
|
pc_rom_lookup[i] = (void*)drc_compiler_point;
|
|
|
|
// point invalid areas to invalid area handler
|
|
pc_dummy_lookup[i] = (void*)drc_invalid_area;
|
|
}
|
|
}
|
|
|
|
void drccore_execute(void)
|
|
{
|
|
drc_entry_point();
|
|
} |