mirror of
https://github.com/RetroDECK/Supermodel.git
synced 2024-11-22 05:45:38 +00:00
- Committing Nik's timing changes: the PowerPC's DEC and TBR now count at the correct rate and time is accurately maintained between calls to the emulator. A VBlank bit was identified in the Real3D status register space and is now timed. Unfortunately, there are still some problems and not all games are perfect, but FV2, VS2, and others are working much better or perfectly.
- Added a new dump of Scud Race Plus (scudp1). Strangely, this one uses an MPC106 instead of the MPC105 used by every other Step 1.5 game...
This commit is contained in:
parent
3963a7a53c
commit
772c94d122
|
@ -239,7 +239,7 @@ typedef struct {
|
||||||
int interrupt_pending;
|
int interrupt_pending;
|
||||||
int external_int;
|
int external_int;
|
||||||
|
|
||||||
UINT64 tb; /* 56-bit timebase register */
|
UINT64 tb; /* 56-bit timebase register */
|
||||||
|
|
||||||
int (*irq_callback)(int irqline);
|
int (*irq_callback)(int irqline);
|
||||||
|
|
||||||
|
@ -247,12 +247,26 @@ typedef struct {
|
||||||
PPC_FETCH_REGION * fetch;
|
PPC_FETCH_REGION * fetch;
|
||||||
|
|
||||||
// STUFF added for the 6xx series
|
// STUFF added for the 6xx series
|
||||||
UINT32 dec, dec_frac;
|
UINT32 dec;
|
||||||
UINT32 fpscr;
|
UINT32 fpscr;
|
||||||
|
|
||||||
FPR fpr[32];
|
FPR fpr[32];
|
||||||
UINT32 sr[16];
|
UINT32 sr[16];
|
||||||
|
|
||||||
|
// Timing related
|
||||||
|
int timer_ratio;
|
||||||
|
UINT32 timer_frac;
|
||||||
|
int tb_base_icount;
|
||||||
|
int dec_base_icount;
|
||||||
|
int dec_trigger_cycle;
|
||||||
|
|
||||||
|
// Cycle related
|
||||||
|
UINT64 total_cycles;
|
||||||
|
int icount;
|
||||||
|
int cur_cycles;
|
||||||
|
int bus_freq_multiplier;
|
||||||
|
int cycles_per_second;
|
||||||
|
|
||||||
#if HAS_PPC603
|
#if HAS_PPC603
|
||||||
int is603;
|
int is603;
|
||||||
#endif
|
#endif
|
||||||
|
@ -271,11 +285,6 @@ typedef struct {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
static int ppc_icount;
|
|
||||||
static int ppc_tb_base_icount;
|
|
||||||
static int ppc_dec_base_icount;
|
|
||||||
static int ppc_dec_trigger_cycle;
|
|
||||||
static int bus_freq_multiplier = 1;
|
|
||||||
static PPC_REGS ppc;
|
static PPC_REGS ppc;
|
||||||
static UINT32 ppc_rotate_mask[32][32];
|
static UINT32 ppc_rotate_mask[32][32];
|
||||||
|
|
||||||
|
@ -424,53 +433,57 @@ INLINE UINT32 check_condition_code(UINT32 bo, UINT32 bi)
|
||||||
|
|
||||||
INLINE UINT64 ppc_read_timebase(void)
|
INLINE UINT64 ppc_read_timebase(void)
|
||||||
{
|
{
|
||||||
int cycles = ppc_tb_base_icount - ppc_icount;
|
int cycles = ppc.tb_base_icount - ppc.icount;
|
||||||
|
|
||||||
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
|
// Timebase is incremented according to timer ratio, so adjust value accordingly
|
||||||
return ppc.tb + (cycles / 4);
|
return ppc.tb + (cycles / ppc.timer_ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE void ppc_write_timebase_l(UINT32 tbl)
|
INLINE void ppc_write_timebase_l(UINT32 tbl)
|
||||||
{
|
{
|
||||||
ppc_tb_base_icount = ppc_icount;
|
UINT64 tb = ppc_read_timebase();
|
||||||
|
|
||||||
ppc.tb &= ~0xffffffff;
|
ppc.tb_base_icount = ppc.icount + ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio);
|
||||||
ppc.tb |= tbl;
|
|
||||||
|
ppc.tb = (tb&~0xffffffff)|tbl;
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE void ppc_write_timebase_h(UINT32 tbh)
|
INLINE void ppc_write_timebase_h(UINT32 tbh)
|
||||||
{
|
{
|
||||||
ppc_tb_base_icount = ppc_icount;
|
UINT64 tb = ppc_read_timebase();
|
||||||
|
|
||||||
ppc.tb &= 0xffffffff;
|
ppc.tb_base_icount = ppc.icount + ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio);
|
||||||
ppc.tb |= (UINT64)(tbh) << 32;
|
|
||||||
|
ppc.tb = (tb&0xffffffff)|((UINT64)(tbh) << 32);
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE UINT32 read_decrementer(void)
|
INLINE UINT32 read_decrementer(void)
|
||||||
{
|
{
|
||||||
int cycles = ppc_dec_base_icount - ppc_icount;
|
int cycles = ppc.dec_base_icount - ppc.icount;
|
||||||
|
|
||||||
// decrementer is decremented once every four bus clock cycles, so adjust the cycles accordingly
|
// Decrementer is decremented at same rate as timebase, so adjust value accordingly
|
||||||
return DEC - (cycles / (bus_freq_multiplier * 2));
|
return DEC - (cycles / ppc.timer_ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
INLINE void write_decrementer(UINT32 value)
|
INLINE void write_decrementer(UINT32 value)
|
||||||
{
|
{
|
||||||
ppc_dec_base_icount = ppc_icount + (ppc_dec_base_icount - ppc_icount) % (bus_freq_multiplier * 2);
|
if (((value&0x80000000) && !(read_decrementer()&0x80000000)))
|
||||||
|
{
|
||||||
|
/* trigger interrupt */
|
||||||
|
ppc.interrupt_pending |= 0x2;
|
||||||
|
ppc603_check_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
|
ppc.dec_base_icount = ppc.icount + ((ppc.dec_base_icount - ppc.icount) % ppc.timer_ratio);
|
||||||
|
|
||||||
DEC = value;
|
DEC = value;
|
||||||
|
|
||||||
// check if decrementer exception occurs during execution
|
// Check if decrementer exception occurs during execution (exception occurs after decrementer
|
||||||
if ((UINT32)(DEC - (ppc_icount / (bus_freq_multiplier * 2))) > (UINT32)(DEC))
|
// has passed through zero)
|
||||||
{
|
if ((UINT32)(ppc.dec_base_icount / ppc.timer_ratio) > DEC)
|
||||||
ppc_dec_trigger_cycle = ((ppc_icount / (bus_freq_multiplier * 2)) - DEC) * 4;
|
ppc.dec_trigger_cycle = ppc.dec_base_icount - ((1 + DEC) * ppc.timer_ratio);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
ppc.dec_trigger_cycle = 0x7fffffff;
|
||||||
ppc_dec_trigger_cycle = 0x7fffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
// printf("DEC = %08X at %08X\n", value, ppc.pc);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*********************************************************************/
|
/*********************************************************************/
|
||||||
|
@ -491,12 +504,6 @@ INLINE void ppc_set_spr(int spr, UINT32 value)
|
||||||
case SPR_PVR: return;
|
case SPR_PVR: return;
|
||||||
|
|
||||||
case SPR603E_DEC:
|
case SPR603E_DEC:
|
||||||
if(((value & 0x80000000) && !(DEC & 0x80000000)) || value == 0)
|
|
||||||
{
|
|
||||||
/* trigger interrupt */
|
|
||||||
ppc.interrupt_pending |= 0x2;
|
|
||||||
ppc603_check_interrupts();
|
|
||||||
}
|
|
||||||
write_decrementer(value);
|
write_decrementer(value);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -821,13 +828,29 @@ void ppc_init(const PPC_CONFIG *config)
|
||||||
|
|
||||||
multiplier = (float)((config->bus_frequency_multiplier >> 4) & 0xf) +
|
multiplier = (float)((config->bus_frequency_multiplier >> 4) & 0xf) +
|
||||||
(float)(config->bus_frequency_multiplier & 0xf) / 10.0f;
|
(float)(config->bus_frequency_multiplier & 0xf) / 10.0f;
|
||||||
bus_freq_multiplier = (int)(multiplier * 2);
|
ppc.bus_freq_multiplier = (int)(multiplier * 2);
|
||||||
|
|
||||||
|
// tb and dec are incremented every four bus cycles, so calculate default timer ratio
|
||||||
|
ppc.timer_ratio = 2 * ppc.bus_freq_multiplier;
|
||||||
|
|
||||||
|
switch (config->bus_frequency)
|
||||||
|
{
|
||||||
|
case BUS_FREQUENCY_16MHZ: ppc.cycles_per_second = multiplier * 16000000; break;
|
||||||
|
case BUS_FREQUENCY_20MHZ: ppc.cycles_per_second = multiplier * 20000000; break;
|
||||||
|
case BUS_FREQUENCY_25MHZ: ppc.cycles_per_second = multiplier * 25000000; break;
|
||||||
|
case BUS_FREQUENCY_33MHZ: ppc.cycles_per_second = multiplier * 33000000; break;
|
||||||
|
case BUS_FREQUENCY_40MHZ: ppc.cycles_per_second = multiplier * 40000000; break;
|
||||||
|
case BUS_FREQUENCY_50MHZ: ppc.cycles_per_second = multiplier * 50000000; break;
|
||||||
|
case BUS_FREQUENCY_60MHZ: ppc.cycles_per_second = multiplier * 60000000; break;
|
||||||
|
case BUS_FREQUENCY_66MHZ: ppc.cycles_per_second = multiplier * 66000000; break;
|
||||||
|
case BUS_FREQUENCY_75MHZ: ppc.cycles_per_second = multiplier * 75000000; break;
|
||||||
|
}
|
||||||
|
|
||||||
switch(config->pvr)
|
switch(config->pvr)
|
||||||
{
|
{
|
||||||
case PPC_MODEL_603E: pll_config = mpc603e_pll_config[bus_freq_multiplier-1][config->bus_frequency]; break;
|
case PPC_MODEL_603E: pll_config = mpc603e_pll_config[ppc.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_603EV: pll_config = mpc603ev_pll_config[ppc.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;
|
case PPC_MODEL_603R: pll_config = mpc603r_pll_config[ppc.bus_freq_multiplier-1][config->bus_frequency]; break;
|
||||||
default: break;
|
default: break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -861,6 +884,31 @@ void ppc_set_fetch(PPC_FETCH_REGION * fetch)
|
||||||
ppc.fetch = fetch;
|
ppc.fetch = fetch;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UINT64 ppc_total_cycles(void)
|
||||||
|
{
|
||||||
|
return ppc.total_cycles + (UINT64)(ppc.cur_cycles - ppc.icount);
|
||||||
|
}
|
||||||
|
|
||||||
|
int ppc_get_cycles_per_sec()
|
||||||
|
{
|
||||||
|
return ppc.cycles_per_second;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ppc_get_bus_freq_multipler()
|
||||||
|
{
|
||||||
|
return ppc.bus_freq_multiplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ppc_set_timer_ratio(int ratio)
|
||||||
|
{
|
||||||
|
ppc.timer_ratio = ratio;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ppc_get_timer_ratio()
|
||||||
|
{
|
||||||
|
return ppc.timer_ratio;
|
||||||
|
}
|
||||||
|
|
||||||
/******************************************************************************
|
/******************************************************************************
|
||||||
Supermodel Interface
|
Supermodel Interface
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
@ -874,11 +922,10 @@ void ppc_save_state(CBlockFile *SaveState)
|
||||||
{
|
{
|
||||||
SaveState->NewBlock("PowerPC", __FILE__);
|
SaveState->NewBlock("PowerPC", __FILE__);
|
||||||
|
|
||||||
// Timer and decrementer
|
// Cycle counting
|
||||||
SaveState->Write(&ppc_icount, sizeof(ppc_icount));
|
SaveState->Write(&ppc.icount, sizeof(ppc.icount));
|
||||||
SaveState->Write(&ppc_tb_base_icount, sizeof(ppc_tb_base_icount));
|
SaveState->Write(&ppc.cur_cycles, sizeof(ppc.cur_cycles));
|
||||||
SaveState->Write(&ppc_dec_base_icount, sizeof(ppc_dec_base_icount));
|
SaveState->Write(&ppc.total_cycles, sizeof(ppc.total_cycles));
|
||||||
SaveState->Write(&ppc_dec_trigger_cycle, sizeof(ppc_dec_trigger_cycle));
|
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
SaveState->Write(ppc.r, sizeof(ppc.r));
|
SaveState->Write(ppc.r, sizeof(ppc.r));
|
||||||
|
@ -939,7 +986,7 @@ void ppc_save_state(CBlockFile *SaveState)
|
||||||
SaveState->Write(&ppc.tb, sizeof(ppc.tb));
|
SaveState->Write(&ppc.tb, sizeof(ppc.tb));
|
||||||
|
|
||||||
SaveState->Write(&ppc.dec, sizeof(ppc.dec));
|
SaveState->Write(&ppc.dec, sizeof(ppc.dec));
|
||||||
SaveState->Write(&ppc.dec_frac, sizeof(ppc.dec_frac));
|
SaveState->Write(&ppc.timer_frac, sizeof(ppc.timer_frac));
|
||||||
SaveState->Write(&ppc.fpscr, sizeof(ppc.fpscr));
|
SaveState->Write(&ppc.fpscr, sizeof(ppc.fpscr));
|
||||||
|
|
||||||
SaveState->Write(ppc.fpr, sizeof(ppc.fpr));
|
SaveState->Write(ppc.fpr, sizeof(ppc.fpr));
|
||||||
|
@ -955,10 +1002,9 @@ void ppc_load_state(CBlockFile *SaveState)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Timer and decrementer
|
// Timer and decrementer
|
||||||
SaveState->Read(&ppc_icount, sizeof(ppc_icount));
|
SaveState->Read(&ppc.icount, sizeof(ppc.icount));
|
||||||
SaveState->Read(&ppc_tb_base_icount, sizeof(ppc_tb_base_icount));
|
SaveState->Read(&ppc.cur_cycles, sizeof(ppc.cur_cycles));
|
||||||
SaveState->Read(&ppc_dec_base_icount, sizeof(ppc_dec_base_icount));
|
SaveState->Read(&ppc.total_cycles, sizeof(ppc.total_cycles));
|
||||||
SaveState->Read(&ppc_dec_trigger_cycle, sizeof(ppc_dec_trigger_cycle));
|
|
||||||
|
|
||||||
// Registers
|
// Registers
|
||||||
SaveState->Read(ppc.r, sizeof(ppc.r));
|
SaveState->Read(ppc.r, sizeof(ppc.r));
|
||||||
|
@ -1019,7 +1065,7 @@ void ppc_load_state(CBlockFile *SaveState)
|
||||||
SaveState->Read(&ppc.tb, sizeof(ppc.tb));
|
SaveState->Read(&ppc.tb, sizeof(ppc.tb));
|
||||||
|
|
||||||
SaveState->Read(&ppc.dec, sizeof(ppc.dec));
|
SaveState->Read(&ppc.dec, sizeof(ppc.dec));
|
||||||
SaveState->Read(&ppc.dec_frac, sizeof(ppc.dec_frac));
|
SaveState->Read(&ppc.timer_frac, sizeof(ppc.timer_frac));
|
||||||
SaveState->Read(&ppc.fpscr, sizeof(ppc.fpscr));
|
SaveState->Read(&ppc.fpscr, sizeof(ppc.fpscr));
|
||||||
|
|
||||||
SaveState->Read(ppc.fpr, sizeof(ppc.fpr));
|
SaveState->Read(ppc.fpr, sizeof(ppc.fpr));
|
||||||
|
@ -1071,6 +1117,7 @@ void ppc_detach_debugger()
|
||||||
Bus = PPCDebug->DetachBus();
|
Bus = PPCDebug->DetachBus();
|
||||||
PPCDebug = NULL;
|
PPCDebug = NULL;
|
||||||
}
|
}
|
||||||
|
#endif // SUPERMODEL_DEBUGGER
|
||||||
|
|
||||||
void ppc_set_pc(UINT32 pc)
|
void ppc_set_pc(UINT32 pc)
|
||||||
{
|
{
|
||||||
|
@ -1113,4 +1160,3 @@ UINT32 ppc_read_msr()
|
||||||
{
|
{
|
||||||
return ppc_get_msr();
|
return ppc_get_msr();
|
||||||
}
|
}
|
||||||
#endif // SUPERMODEL_DEBUGGER
|
|
|
@ -352,6 +352,11 @@ extern void ppc_reset(void);
|
||||||
extern void ppc_shutdown(void);
|
extern void ppc_shutdown(void);
|
||||||
extern void ppc_init(const PPC_CONFIG *config); // must be called second!
|
extern void ppc_init(const PPC_CONFIG *config); // must be called second!
|
||||||
extern void ppc_set_fetch(PPC_FETCH_REGION * fetch);
|
extern void ppc_set_fetch(PPC_FETCH_REGION * fetch);
|
||||||
|
extern UINT64 ppc_total_cycles(void);
|
||||||
|
extern int ppc_get_cycles_per_sec(void);
|
||||||
|
extern int ppc_get_bus_freq_multipler(void);
|
||||||
|
extern int ppc_get_timer_ratio(void);
|
||||||
|
extern void ppc_set_timer_ratio(int ratio);
|
||||||
|
|
||||||
// These have been added to support the new Supermodel
|
// These have been added to support the new Supermodel
|
||||||
extern void ppc_attach_bus(class CBus *BusPtr); // must be called first!
|
extern void ppc_attach_bus(class CBus *BusPtr); // must be called first!
|
||||||
|
@ -367,6 +372,7 @@ extern UINT32 ppc_read_sr(unsigned num);
|
||||||
// These have been added to support the Supermodel debugger
|
// These have been added to support the Supermodel debugger
|
||||||
extern void ppc_attach_debugger(class Debugger::CPPCDebug *PPCDebugPtr);
|
extern void ppc_attach_debugger(class Debugger::CPPCDebug *PPCDebugPtr);
|
||||||
extern void ppc_detach_debugger();
|
extern void ppc_detach_debugger();
|
||||||
|
#endif // SUPERMODEL_DEBUGGER
|
||||||
extern void ppc_set_pc(UINT32 pc);
|
extern void ppc_set_pc(UINT32 pc);
|
||||||
extern UINT8 ppc_get_cr(unsigned num);
|
extern UINT8 ppc_get_cr(unsigned num);
|
||||||
extern void ppc_set_cr(unsigned num, UINT8 val);
|
extern void ppc_set_cr(unsigned num, UINT8 val);
|
||||||
|
@ -375,6 +381,4 @@ extern void ppc_set_fpr(unsigned num, double val);
|
||||||
extern void ppc_write_spr(unsigned spr, UINT32 val);
|
extern void ppc_write_spr(unsigned spr, UINT32 val);
|
||||||
extern void ppc_write_sr(unsigned num, UINT32 val);
|
extern void ppc_write_sr(unsigned num, UINT32 val);
|
||||||
extern UINT32 ppc_read_msr();
|
extern UINT32 ppc_read_msr();
|
||||||
#endif // SUPERMODEL_DEBUGGER
|
|
||||||
|
|
||||||
#endif // INCLUDED_PPC_H
|
#endif // INCLUDED_PPC_H
|
||||||
|
|
|
@ -247,25 +247,30 @@ void ppc_reset(void)
|
||||||
ppc.hid0 = 1;
|
ppc.hid0 = 1;
|
||||||
|
|
||||||
ppc.interrupt_pending = 0;
|
ppc.interrupt_pending = 0;
|
||||||
|
|
||||||
|
ppc.tb = 0;
|
||||||
|
ppc.timer_frac = 0;
|
||||||
|
DEC = 0xffffffff;
|
||||||
|
ppc.total_cycles = 0;
|
||||||
|
ppc.cur_cycles = 0;
|
||||||
|
ppc.icount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ppc_execute(int cycles)
|
int ppc_execute(int cycles)
|
||||||
{
|
{
|
||||||
UINT32 opcode;
|
UINT32 opcode;
|
||||||
|
|
||||||
ppc_icount = cycles;
|
ppc.cur_cycles = cycles;
|
||||||
ppc_tb_base_icount = cycles;
|
ppc.icount = cycles;
|
||||||
ppc_dec_base_icount = cycles + ppc.dec_frac;
|
ppc.tb_base_icount = cycles + ppc.timer_frac;
|
||||||
|
ppc.dec_base_icount = cycles + ppc.timer_frac;
|
||||||
|
|
||||||
// check if decrementer exception occurs during execution
|
// Check if decrementer exception occurs during execution (exception occurs after decrementer
|
||||||
if ((UINT32)(DEC - (cycles / (bus_freq_multiplier * 2))) > (UINT32)(DEC))
|
// has passed through zero)
|
||||||
{
|
if ((UINT32)(ppc.dec_base_icount / ppc.timer_ratio) > DEC)
|
||||||
ppc_dec_trigger_cycle = ((cycles / (bus_freq_multiplier * 2)) - DEC) * 4;
|
ppc.dec_trigger_cycle = ppc.dec_base_icount - ((1 + DEC) * ppc.timer_ratio);
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
ppc.dec_trigger_cycle = 0x7fffffff;
|
||||||
ppc_dec_trigger_cycle = 0x7fffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
ppc_change_pc(ppc.npc);
|
ppc_change_pc(ppc.npc);
|
||||||
|
|
||||||
|
@ -276,8 +281,6 @@ int ppc_execute(int cycles)
|
||||||
DisassemblePowerPC(opcode, ppc.npc, string1, string2, true);
|
DisassemblePowerPC(opcode, ppc.npc, string1, string2, true);
|
||||||
printf("%08X: %s %s\n", ppc.npc, string1, string2);
|
printf("%08X: %s %s\n", ppc.npc, string1, string2);
|
||||||
}*/
|
}*/
|
||||||
// printf("trigger cycle %d (%08X)\n", ppc_dec_trigger_cycle, ppc_dec_trigger_cycle);
|
|
||||||
// printf("tb = %08X %08X\n", (UINT32)(ppc.tb >> 32), (UINT32)(ppc.tb));
|
|
||||||
|
|
||||||
ppc603_check_interrupts();
|
ppc603_check_interrupts();
|
||||||
|
|
||||||
|
@ -286,7 +289,7 @@ int ppc_execute(int cycles)
|
||||||
PPCDebug->CPUActive();
|
PPCDebug->CPUActive();
|
||||||
#endif // SUPERMODEL_DEBUGGER
|
#endif // SUPERMODEL_DEBUGGER
|
||||||
|
|
||||||
while( ppc_icount > 0 && !ppc.fatalError)
|
while( ppc.icount > 0 && !ppc.fatalError)
|
||||||
{
|
{
|
||||||
ppc.pc = ppc.npc;
|
ppc.pc = ppc.npc;
|
||||||
|
|
||||||
|
@ -319,14 +322,10 @@ int ppc_execute(int cycles)
|
||||||
default: optable[opcode >> 26](opcode); break;
|
default: optable[opcode >> 26](opcode); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
ppc_icount--;
|
ppc.icount--;
|
||||||
|
|
||||||
// Updating TB four times per core cycle fixes VF3 timing but breaks other games (Daytona 2 too fast, Spikeout has some geometry flickering)
|
if (ppc.icount == ppc.dec_trigger_cycle)
|
||||||
//ppc.tb += 4;
|
|
||||||
|
|
||||||
if(ppc_icount == ppc_dec_trigger_cycle)
|
|
||||||
{
|
{
|
||||||
// printf("dec int at %d\n", ppc_icount);
|
|
||||||
ppc.interrupt_pending |= 0x2;
|
ppc.interrupt_pending |= 0x2;
|
||||||
ppc603_check_interrupts();
|
ppc603_check_interrupts();
|
||||||
}
|
}
|
||||||
|
@ -339,16 +338,10 @@ int ppc_execute(int cycles)
|
||||||
PPCDebug->CPUInactive();
|
PPCDebug->CPUInactive();
|
||||||
#endif // SUPERMODEL_DEBUGGER
|
#endif // SUPERMODEL_DEBUGGER
|
||||||
|
|
||||||
// update timebase
|
// Update timebase and decrementer. Both are updated at same rate as specified by timer_ratio.
|
||||||
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
|
ppc.timer_frac = ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio);
|
||||||
// NOTE: updating at the end of the time slice breaks things that try to wait on TBL. Performing
|
ppc.tb += ((ppc.tb_base_icount - ppc.icount) / ppc.timer_ratio);
|
||||||
// the update inside the execution loop fixes VF3 and allows many decrementer patches to be
|
DEC -= ((ppc.dec_base_icount - ppc.icount) / ppc.timer_ratio);
|
||||||
// removed but it adversely affects Spikeout and other games.
|
|
||||||
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));
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
{
|
{
|
||||||
|
@ -360,5 +353,9 @@ int ppc_execute(int cycles)
|
||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
return cycles - ppc_icount;
|
int executed = cycles - ppc.icount;
|
||||||
|
ppc.total_cycles += executed;
|
||||||
|
ppc.cur_cycles = 0;
|
||||||
|
ppc.icount = 0;
|
||||||
|
return executed;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1471,6 +1471,88 @@ const struct GameInfo g_Model3GameList[] =
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// Scud Race Plus (No Revision -- original? Requires MPC106...)
|
||||||
|
{
|
||||||
|
"scudp1",
|
||||||
|
"scud",
|
||||||
|
"Scud Race Plus (First Version?)",
|
||||||
|
"Sega",
|
||||||
|
1997,
|
||||||
|
0x15,
|
||||||
|
0x200000, // 2 MB of fixed CROM
|
||||||
|
true, // 64 MB of banked CROM (Mirror)
|
||||||
|
0x2000000, // 32 MB of VROM
|
||||||
|
0x800000, // 8 MB of sample ROMs
|
||||||
|
GAME_INPUT_COMMON|GAME_INPUT_VEHICLE|GAME_INPUT_VR|GAME_INPUT_SHIFT4,
|
||||||
|
1, // DSB1 MPEG board
|
||||||
|
true, // drive board
|
||||||
|
|
||||||
|
{
|
||||||
|
// Fixed CROM (mirroring behavior here is special and handled manually by CModel3)
|
||||||
|
{ "CROM", false, "epr-20095", 0x44467BC1, 0x80000, 2, 0x0600000, 8, true },
|
||||||
|
{ "CROM", false, "epr-20094", 0x299B6257, 0x80000, 2, 0x0600002, 8, true },
|
||||||
|
{ "CROM", false, "epr-20093", 0x9A85C611, 0x80000, 2, 0x0600004, 8, true },
|
||||||
|
{ "CROM", false, "epr-20092", 0x6F9161C1, 0x80000, 2, 0x0600006, 8, true },
|
||||||
|
|
||||||
|
// Banked CROM0
|
||||||
|
{ "CROMxx", false, "mpr-19661.04", 0x8E3FD241, 0x400000, 2, 0x0000000, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19660.03", 0xD999C935, 0x400000, 2, 0x0000002, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19659.02", 0xC47E7002, 0x400000, 2, 0x0000004, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19658.01", 0xD523235C, 0x400000, 2, 0x0000006, 8, true },
|
||||||
|
|
||||||
|
// Banked CROM1
|
||||||
|
{ "CROMxx", false, "mpr-19665.08", 0xF97C78F9, 0x400000, 2, 0x1000000, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19664.07", 0xB9D11294, 0x400000, 2, 0x1000002, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19663.06", 0xF6AF1CA4, 0x400000, 2, 0x1000004, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19662.05", 0x3C700EFF, 0x400000, 2, 0x1000006, 8, true },
|
||||||
|
|
||||||
|
// Banked CROM2
|
||||||
|
{ "CROMxx", false, "mpr-19669.12", 0xCDC43C61, 0x400000, 2, 0x2000000, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19668.11", 0x0B4DD8D5, 0x400000, 2, 0x2000002, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19667.10", 0xA8676799, 0x400000, 2, 0x2000004, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-19666.09", 0xB53DC97F, 0x400000, 2, 0x2000006, 8, true },
|
||||||
|
|
||||||
|
// Banked CROM3
|
||||||
|
{ "CROMxx", false, "mpr-20100.16", 0xC99E2C01, 0x400000, 2, 0x3000000, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-20099.15", 0xFC9BD7D9, 0x400000, 2, 0x3000002, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-20098.14", 0x8355FA41, 0x400000, 2, 0x3000004, 8, true },
|
||||||
|
{ "CROMxx", false, "mpr-20097.13", 0x269A9DBE, 0x400000, 2, 0x3000006, 8, true },
|
||||||
|
|
||||||
|
// Video ROM
|
||||||
|
{ "VROM", false, "mpr-19672.26", 0x588C29FD, 0x200000, 2, 0, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19673.27", 0x156ABAA9, 0x200000, 2, 2, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19674.28", 0xC7B0F98C, 0x200000, 2, 4, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19675.29", 0xFF113396, 0x200000, 2, 6, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19676.30", 0xFD852EAD, 0x200000, 2, 8, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19677.31", 0xC6AC0347, 0x200000, 2, 10, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19678.32", 0xB8819CFE, 0x200000, 2, 12, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19679.33", 0xE126C3E3, 0x200000, 2, 14, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19680.34", 0x00EA5CEF, 0x200000, 2, 16, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19681.35", 0xC949325F, 0x200000, 2, 18, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19682.36", 0xCE5CA065, 0x200000, 2, 20, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19683.37", 0xE5856419, 0x200000, 2, 22, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19684.38", 0x56F6EC97, 0x200000, 2, 24, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19685.39", 0x42B49304, 0x200000, 2, 26, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19686.40", 0x84EED592, 0x200000, 2, 28, 32, false },
|
||||||
|
{ "VROM", false, "mpr-19687.41", 0x776CE694, 0x200000, 2, 30, 32, false },
|
||||||
|
|
||||||
|
// Sound ROMs
|
||||||
|
{ "SndProg", false, "epr-20096a.21",0x0FEF288B, 0x80000, 2, 0, 2, true },
|
||||||
|
{ "Samples", false, "mpr-19670.22", 0xBD31CC06, 0x400000, 2, 0x000000, 2, true },
|
||||||
|
{ "Samples", false, "mpr-20101.24", 0x66D1E31F, 0x400000, 2, 0x400000, 2, true },
|
||||||
|
{ "DSBProg", false, "epr-19612.2", 0x13978FD4, 0x20000, 2, 0, 2, false },
|
||||||
|
{ "DSBMPEG", false, "mpr-19603.57", 0xB1B1765F, 0x200000, 2, 0x000000, 2, false },
|
||||||
|
{ "DSBMPEG", false, "mpr-19604.58", 0x6AC85B49, 0x200000, 2, 0x200000, 2, false },
|
||||||
|
{ "DSBMPEG", false, "mpr-19605.59", 0xBEC891EB, 0x200000, 2, 0x400000, 2, false },
|
||||||
|
{ "DSBMPEG", false, "mpr-19606.60", 0xADAD46B2, 0x200000, 2, 0x600000, 2, false },
|
||||||
|
|
||||||
|
// Drive Board ROM
|
||||||
|
{ "DriveBd", true, "epr-19338a.bin", 0xC9FAC464, 0x10000, 2, 0, 2, false },
|
||||||
|
|
||||||
|
{ NULL, false, NULL, 0, 0, 0, 0, 0, false }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
// Ski Champ
|
// Ski Champ
|
||||||
{
|
{
|
||||||
"skichamp",
|
"skichamp",
|
||||||
|
|
|
@ -26,9 +26,12 @@
|
||||||
*
|
*
|
||||||
* To-Do List
|
* To-Do List
|
||||||
* ----------
|
* ----------
|
||||||
|
* - Save state format has changed slightly. No longer need dmaUnknownRegister
|
||||||
|
* in Real3D.cpp. PowerPC timing variables have changed. Before 0.3a
|
||||||
|
* release, important to change format version #.
|
||||||
* - ROM sets should probably be handled with a class that manages ROM
|
* - ROM sets should probably be handled with a class that manages ROM
|
||||||
* loading, the game list, as well as ROM patching
|
* loading, the game list, as well as ROM patching
|
||||||
* - Wrap up CPU emulation inside a class (hah!)
|
* - Wrap up CPU emulation inside a class.
|
||||||
* - Update the to-do list! I forgot lots of other stuff here :)
|
* - Update the to-do list! I forgot lots of other stuff here :)
|
||||||
*
|
*
|
||||||
* PowerPC Address Map (may be slightly out of date/incomplete)
|
* PowerPC Address Map (may be slightly out of date/incomplete)
|
||||||
|
@ -1983,15 +1986,40 @@ void CModel3::RunMainBoardFrame(void)
|
||||||
UINT32 start = CThread::GetTicks();
|
UINT32 start = CThread::GetTicks();
|
||||||
|
|
||||||
// Compute display and VBlank timings
|
// Compute display and VBlank timings
|
||||||
unsigned frameCycles = g_Config.GetPowerPCFrequency()*1000000/60;
|
unsigned ppcCycles = g_Config.GetPowerPCFrequency() * 1000000;
|
||||||
unsigned vblCycles = (unsigned) ((float) frameCycles * 2.5f/100.0f); // 2.5% vblank (ridiculously short and wrong but bigger values cause flicker in Daytona)
|
unsigned frameCycles = ppcCycles / 60;
|
||||||
|
unsigned vblCycles = (unsigned)((float) frameCycles * 2.5f/100.0f); // 2.5% vblank (ridiculously short and wrong but bigger values cause flicker in Daytona)
|
||||||
unsigned dispCycles = frameCycles - vblCycles;
|
unsigned dispCycles = frameCycles - vblCycles;
|
||||||
|
|
||||||
|
// Scale PPC timer ratio according to speed at which the PowerPC is being emulated so that the observed running frequency of the PPC timer
|
||||||
|
// registers is more or less correct. This is needed to get the Virtua Striker 2 series of games running at the right speed (they are
|
||||||
|
// too slow otherwise). Other games appear to not be affected by this ratio so much as their running speed depends more on the timing of
|
||||||
|
// the Real3D status bit below.
|
||||||
|
ppc_set_timer_ratio(ppc_get_bus_freq_multipler() * 2 * ppcCycles / ppc_get_cycles_per_sec());
|
||||||
|
|
||||||
|
// Compute timing of the Real3D status bit. This value directly affects the speed at which all the games except Virtua Stiker 2 run.
|
||||||
|
// Currently it is not known exactly what this bit represents nor why such wildly varying values are needed for the different step models.
|
||||||
|
// The values below were arrived at by trial and error and clearly more investigation is required. If it turns out that the status bit is
|
||||||
|
// connected to the end of VBlank then the code below should be removed and the timing handled via GPU.VBlankEnd() instead.
|
||||||
|
unsigned statusCycles;
|
||||||
|
if (Game->step >= 0x20)
|
||||||
|
{
|
||||||
|
// For some reason, Fighting Vipers 2 and Daytona USA 2 require completely different timing to the rest of the step 2.x games
|
||||||
|
if (!strcmp(Game->id, "daytona2") || (Game->step == 0x20 && !strcmp(Game->id, "fvipers2")))
|
||||||
|
statusCycles = (unsigned)((float)frameCycles * 24.0f/100.0f);
|
||||||
|
else
|
||||||
|
statusCycles = (unsigned)((float)frameCycles * 9.12f/100.0f);
|
||||||
|
}
|
||||||
|
else if (Game->step == 0x15)
|
||||||
|
statusCycles = (unsigned)((float)frameCycles * 4.8f/100.0f);
|
||||||
|
else
|
||||||
|
statusCycles = (unsigned)((float)frameCycles * 48.0f/100.0f);
|
||||||
|
|
||||||
// VBlank
|
// VBlank
|
||||||
if (gpusReady)
|
if (gpusReady)
|
||||||
{
|
{
|
||||||
TileGen.BeginVBlank();
|
TileGen.BeginVBlank();
|
||||||
GPU.BeginVBlank();
|
GPU.BeginVBlank(statusCycles);
|
||||||
IRQ.Assert(0x02);
|
IRQ.Assert(0x02);
|
||||||
ppc_execute(vblCycles);
|
ppc_execute(vblCycles);
|
||||||
//printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr());
|
//printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr());
|
||||||
|
@ -2020,6 +2048,7 @@ void CModel3::RunMainBoardFrame(void)
|
||||||
ppc_execute(200); // give PowerPC time to acknowledge IRQ
|
ppc_execute(200); // give PowerPC time to acknowledge IRQ
|
||||||
IRQ.Deassert(0x40);
|
IRQ.Deassert(0x40);
|
||||||
ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?)
|
ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?)
|
||||||
|
dispCycles -= 400;
|
||||||
|
|
||||||
++irqCount;
|
++irqCount;
|
||||||
if (irqCount > 128)
|
if (irqCount > 128)
|
||||||
|
@ -2640,15 +2669,7 @@ void CModel3::Reset(void)
|
||||||
// Apply patches to games
|
// Apply patches to games
|
||||||
void CModel3::Patch(void)
|
void CModel3::Patch(void)
|
||||||
{
|
{
|
||||||
if (!strcmp(Game->id, "vf3") || !strcmp(Game->id, "vf3a"))
|
if (!strcmp(Game->id, "lemans24"))
|
||||||
{
|
|
||||||
// Base offset of program in CROM: 0x710000
|
|
||||||
//*(UINT32 *) &crom[0x713C7C] = 0x60000000; // this patch may not be needed anymore (find out why)
|
|
||||||
*(UINT32 *) &crom[0x713E54] = 0x60000000; // affects timing but prevents game from coining up -- investigate this carefully
|
|
||||||
//*(UINT32 *) &crom[0x7125B0] = 0x60000000; // this patch may not be needed anymore (find out why)
|
|
||||||
//*(UINT32 *) &crom[0x7125D0] = 0x60000000; // this patch may not be needed anymore (find out why)
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "lemans24"))
|
|
||||||
{
|
{
|
||||||
// Base offset of program in CROM: 6473C0
|
// Base offset of program in CROM: 6473C0
|
||||||
*(UINT32 *) &crom[0x6D8C4C] = 0x00000002; // comm. mode: 00=master, 01=slave, 02=satellite
|
*(UINT32 *) &crom[0x6D8C4C] = 0x00000002; // comm. mode: 00=master, 01=slave, 02=satellite
|
||||||
|
@ -2656,54 +2677,8 @@ void CModel3::Patch(void)
|
||||||
*(UINT32 *) &crom[0x73EB5C] = 0x60000000;
|
*(UINT32 *) &crom[0x73EB5C] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x73EDD0] = 0x60000000;
|
*(UINT32 *) &crom[0x73EDD0] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x73EDC4] = 0x60000000;
|
*(UINT32 *) &crom[0x73EDC4] = 0x60000000;
|
||||||
//*(UINT32 *) &crom[0x6473C0+0xF8BD0] = 0x60000000; // waiting for something from network card, called at F8CD8
|
|
||||||
//*(UINT32 *) &crom[0x6473C0+0xF8B80] = 0x60000000; // "", called at 0xF8D90
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "scud"))
|
|
||||||
{
|
|
||||||
// Base offset of program in CROM: 0x710000
|
|
||||||
*(UINT32 *) &crom[0x712734] = 0x60000000; // skips some ridiculously slow delay loop during boot-up
|
|
||||||
*(UINT32 *) &crom[0x71AEBC] = 0x60000000; // waiting for some flag in RAM that never gets modified (IRQ problem? try emulating VBL on Real3D)
|
|
||||||
*(UINT32 *) &crom[0x712268] = 0x60000000; // this corrects the boot-up menu (but why?)
|
|
||||||
crom[0x787B36^3] = 0x00; // Link ID: 00=single, 01=master, 02=slave (can bypass network board error)
|
|
||||||
*(UINT32 *) &crom[0x71277C] = 0x60000000; // seems to allow the game to start
|
|
||||||
*(UINT32 *) &crom[0x74072C] = 0x60000000; // ... ditto
|
|
||||||
|
|
||||||
//*(UINT32 *)&crom[0x799DE8] = 0x00050208; // debug menu
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(Game->id, "scuda"))
|
|
||||||
{
|
|
||||||
*(UINT32 *) &crom[0x712734] = 0x60000000; // skips some ridiculously slow delay loop during boot-up
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "scudj"))
|
|
||||||
{
|
|
||||||
*(UINT32 *) &crom[0x7126C8] = 0x60000000; // skips some ridiculously slow delay loop during boot-up
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "scudp"))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* RAM program structure:
|
|
||||||
*
|
|
||||||
* 1540: Reset vector transfers control here. Effective start of
|
|
||||||
* program. On error, game often resets here.
|
|
||||||
* 14844: Appears to be beginning of the actual boot-up process.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Base offset of program in CROM: 710000
|
|
||||||
// *(UINT32 *) &crom[0x713724] = 0x60000000;
|
|
||||||
// *(UINT32 *) &crom[0x713744] = 0x60000000;
|
|
||||||
// *(UINT32 *) &crom[0x741f48] = 0x60000000;
|
|
||||||
|
|
||||||
*(UINT32 *) &crom[0x741f68] = 0x60000000;
|
|
||||||
*(UINT32 *) &crom[0x7126B8] = 0x60000000; // waits for something in RAM
|
|
||||||
|
|
||||||
crom[0x7C62B2^3] = 0x00; // link ID is copied to 0x10011E, set it to single
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "von2"))
|
|
||||||
{
|
|
||||||
*(UINT32 *) &crom[0x1B0] = 0x7C631A78; // eliminate annoyingly long delay loop
|
|
||||||
*(UINT32 *) &crom[0x1B4] = 0x60000000; // ""
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "lostwsga"))
|
else if (!strcmp(Game->id, "lostwsga"))
|
||||||
{
|
{
|
||||||
*(UINT32 *) &crom[0x7374f4] = 0x38840004; // an actual bug in the game code
|
*(UINT32 *) &crom[0x7374f4] = 0x38840004; // an actual bug in the game code
|
||||||
|
@ -2740,133 +2715,42 @@ void CModel3::Patch(void)
|
||||||
*(UINT32 *) &crom[0x6063c4] = 0x60000000; // needed to allow levels to load
|
*(UINT32 *) &crom[0x6063c4] = 0x60000000; // needed to allow levels to load
|
||||||
*(UINT32 *) &crom[0x616434] = 0x60000000; // prevents PPC from executing invalid code (MMU?)
|
*(UINT32 *) &crom[0x616434] = 0x60000000; // prevents PPC from executing invalid code (MMU?)
|
||||||
*(UINT32 *) &crom[0x69f4e4] = 0x60000000; // ""
|
*(UINT32 *) &crom[0x69f4e4] = 0x60000000; // ""
|
||||||
*(UINT32 *) &crom[0x600000+0x4C744] = 0x60000000; // decrementer loop?
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(Game->id, "dayto2pe"))
|
else if (!strcmp(Game->id, "dayto2pe"))
|
||||||
{
|
{
|
||||||
*(UINT32 *) &crom[0x606784] = 0x60000000;
|
//*(UINT32 *) &crom[0x606784] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x69A3FC] = 0x60000000; // MAME says: jump to encrypted code
|
*(UINT32 *) &crom[0x69A3FC] = 0x60000000; // MAME says: jump to encrypted code
|
||||||
*(UINT32 *) &crom[0x618B28] = 0x60000000; // MAME says: jump to encrypted code
|
*(UINT32 *) &crom[0x618B28] = 0x60000000; // MAME says: jump to encrypted code
|
||||||
*(UINT32 *) &crom[0x64CA34] = 0x60000000; // decrementer
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(Game->id, "fvipers2"))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Game code is copied to RAM in a non-trivial fashion (it may be
|
|
||||||
* compressed) just prior to the following sequence of code, which then
|
|
||||||
* transfers control to the RAM program:
|
|
||||||
*
|
|
||||||
* FFF0153C: 3C200070 li r1,0x00700000
|
|
||||||
* FFF01540: 3C60FF80 li r3,0xFF800000
|
|
||||||
* FFF01544: 38800000 li r4,0x00000000
|
|
||||||
* FFF01548: 48000751 bl 0xFFF01C98
|
|
||||||
* FFF0154C: 7C7F42A6 mfspr r3,pvr
|
|
||||||
* FFF01550: 90600080 stw r3,0x00000080
|
|
||||||
* FFF01554: 92E00084 stw r23,0x00000084
|
|
||||||
* FFF01558: 38600100 li r3,0x00000100
|
|
||||||
* FFF0155C: 7C6803A6 mtspr lr,r3
|
|
||||||
* FFF01560: 4E800020 bclr 0x14,0
|
|
||||||
*
|
|
||||||
* In order to patch the necessary portions of the RAM program, we must
|
|
||||||
* insert a routine that executes after the program is loaded. There is
|
|
||||||
* ample room in the vector table between 0xFFF00004 and 0xFFF000FC.
|
|
||||||
* The patching routine must terminate with a "bclr 0x14,0" to jump to
|
|
||||||
* the RAM program.
|
|
||||||
*/
|
|
||||||
*(UINT32 *) &crom[0xFFF01560-0xFF800000] = 0x4BF00006; // ba 0xFFF00004
|
|
||||||
*(UINT32 *) &crom[0xFFF00004-0xFF800000] = (31<<26)|(316<<1); // xor r0,r0,r0 ; R0 = 0
|
|
||||||
*(UINT32 *) &crom[0xFFF00008-0xFF800000] = (15<<26)|(2<<21)|0x6000; // addis r2,0,0x6000 ; R2 = nop
|
|
||||||
*(UINT32 *) &crom[0xFFF0000C-0xFF800000] = (24<<26)|(1<<16)|0xA2E8; // ori r1,r0,0xA2E8 ; [A2E8] <- nop (decrementer loop)
|
|
||||||
*(UINT32 *) &crom[0xFFF00010-0xFF800000] = (36<<26)|(2<<21)|(1<<16); // stw r2,0(r1)
|
|
||||||
*(UINT32 *) &crom[0xFFF00014-0xFF800000] = 0x4E800020; // bclr 0x14,0 ; return to RAM code
|
|
||||||
|
|
||||||
/* // Security board patch for Andy
|
|
||||||
*(UINT32 *) &crom[0xFFF01560-0xFF800000] = 0x4BF00006; // ba 0xFFF00004
|
|
||||||
*(UINT32 *) &crom[0xFFF00004-0xFF800000] = (31<<26)|(316<<1); // xor r0,r0,r0 ; R0 = 0
|
|
||||||
*(UINT32 *) &crom[0xFFF00008-0xFF800000] = (15<<26)|(2<<21)|0x3860; // addis r2,0,0x3860 ; R2 = "li r3,0xFFFFFFFF"
|
|
||||||
*(UINT32 *) &crom[0xFFF0000C-0xFF800000] = (24<<26)|(2<<21)|(2<<16)|0xFFFF; // ori r2,r2,0xFFFF
|
|
||||||
*(UINT32 *) &crom[0xFFF00010-0xFF800000] = (24<<26)|(1<<16)|0x9CF8; // ori r1,r0,0x9CF8 ; [9CF8] <- set return value to "success"
|
|
||||||
*(UINT32 *) &crom[0xFFF00014-0xFF800000] = (36<<26)|(2<<21)|(1<<16); // stw r2,0(r1)
|
|
||||||
*(UINT32 *) &crom[0xFFF00018-0xFF800000] = (15<<26)|(2<<21)|0x4800; // addis r2,0,0x4800 ; R2 = "ba 0x9D40"
|
|
||||||
*(UINT32 *) &crom[0xFFF0001C-0xFF800000] = (24<<26)|(2<<21)|(2<<16)|0x9D42; // ori r2,r2,0xFFFF
|
|
||||||
*(UINT32 *) &crom[0xFFF00020-0xFF800000] = (24<<26)|(1<<16)|0x9CFC; // ori r1,r0,0x9CFC ; [9CFC] <- jump to end of security board routine
|
|
||||||
*(UINT32 *) &crom[0xFFF00024-0xFF800000] = (36<<26)|(2<<21)|(1<<16); // stw r2,0(r1)
|
|
||||||
*(UINT32 *) &crom[0xFFF00028-0xFF800000] = 0x4E800020; // bclr 0x14,0 ; return to RAM code
|
|
||||||
*/
|
|
||||||
|
|
||||||
// NOTE: At 32714, a test is made that determines the message: ONE PROCESSOR DETECTED, TWO "", etc.
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "harley"))
|
else if (!strcmp(Game->id, "harley"))
|
||||||
{
|
{
|
||||||
*(UINT32 *) &crom[0x50E8D4] = 0x60000000;
|
*(UINT32 *) &crom[0x50E8D4] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x50E8F4] = 0x60000000;
|
*(UINT32 *) &crom[0x50E8F4] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x50FB84] = 0x60000000;
|
*(UINT32 *) &crom[0x50FB84] = 0x60000000;
|
||||||
|
|
||||||
*(UINT32 *) &crom[0x4F736C] = 0x60000000;
|
|
||||||
*(UINT32 *) &crom[0x4F738C] = 0x60000000;
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(Game->id, "harleyb"))
|
else if (!strcmp(Game->id, "harleyb"))
|
||||||
{
|
{
|
||||||
*(UINT32 *) &crom[0x50ECB4] = 0x60000000;
|
*(UINT32 *) &crom[0x50ECB4] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x50ECD4] = 0x60000000;
|
*(UINT32 *) &crom[0x50ECD4] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x50FF64] = 0x60000000;
|
*(UINT32 *) &crom[0x50FF64] = 0x60000000;
|
||||||
|
|
||||||
*(UINT32 *) &crom[0x4F774C] = 0x60000000;
|
|
||||||
*(UINT32 *) &crom[0x4F776C] = 0x60000000;
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "oceanhun"))
|
|
||||||
{
|
|
||||||
// Base address of program in CROM: 588FD8-108FD8=480000
|
|
||||||
//*(UINT32 *) &crom[0x480000+0x108FE0] = 0x60000000; // bad DMA copies from CROM
|
|
||||||
//*(UINT32 *) &crom[0x480000+0x112020] = 0x60000000; // reads from invalid addresses (due to CROM?)
|
|
||||||
*(UINT32 *) &crom[0x480000+0xF995C] = 0x60000000; // decrementer
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(Game->id, "swtrilgy"))
|
else if (!strcmp(Game->id, "swtrilgy"))
|
||||||
{
|
{
|
||||||
*(UINT32 *) &crom[0xF0E48] = 0x60000000;
|
*(UINT32 *) &crom[0xF0E48] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x043DC] = 0x48000090;
|
*(UINT32 *) &crom[0x043DC] = 0x48000090; // related to joystick feedback
|
||||||
*(UINT32 *) &crom[0x029A0] = 0x60000000;
|
*(UINT32 *) &crom[0x029A0] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x02A0C] = 0x60000000;
|
*(UINT32 *) &crom[0x02A0C] = 0x60000000;
|
||||||
}
|
}
|
||||||
else if (!strcmp(Game->id, "swtrilgya"))
|
else if (!strcmp(Game->id, "swtrilgya"))
|
||||||
{
|
{
|
||||||
*(UINT32 *) &crom[0xF6DD0] = 0x60000000; // from MAME
|
*(UINT32 *) &crom[0xF6DD0] = 0x60000000; // from MAME
|
||||||
|
|
||||||
//*(UINT32 *) &crom[0xF1128] = 0x60000000; // these bypass required delay loops and break game timing
|
|
||||||
//*(UINT32 *) &crom[0xF10E0] = 0x60000000;
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(Game->id, "eca") || !strcmp(Game->id, "ecax"))
|
else if (!strcmp(Game->id, "eca") || !strcmp(Game->id, "ecax"))
|
||||||
{
|
{
|
||||||
|
|
||||||
*(UINT32 *) &crom[0x535580] = 0x60000000;
|
*(UINT32 *) &crom[0x535580] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x5023B4] = 0x60000000;
|
*(UINT32 *) &crom[0x5023B4] = 0x60000000;
|
||||||
*(UINT32 *) &crom[0x5023D4] = 0x60000000;
|
*(UINT32 *) &crom[0x5023D4] = 0x60000000;
|
||||||
|
|
||||||
*(UINT32 *) &crom[0x535560] = 0x60000000; // decrementer loop
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "spikeout"))
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* Decrementer loop at 0x31994 seems to work until a few frames into
|
|
||||||
* the attract mode and game, at which point a very large value is
|
|
||||||
* loaded into the decrementer and locks up the CPU (the usual
|
|
||||||
* decrementer problem). "Insert Coin" keeps flashing because it is
|
|
||||||
* managed via an IRQ, evidently.
|
|
||||||
*
|
|
||||||
* 0x00031994: 0x7C9602A6 mfspr r4,dec
|
|
||||||
* 0x00031998: 0x2C040000 cmpi cr0,0,r4,0x0000
|
|
||||||
* 0x0003199C: 0x41A0FFF8 bt cr0[lt],0x00031994
|
|
||||||
*/
|
|
||||||
|
|
||||||
*(UINT32 *) &crom[0x600000+0x3199C] = 0x60000000;
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "spikeofe"))
|
|
||||||
{
|
|
||||||
*(UINT32 *) &crom[0x600000+0x36F2C] = 0x60000000; // decrementer loop (see Spikeout)
|
|
||||||
}
|
|
||||||
else if (!strcmp(Game->id, "skichamp"))
|
|
||||||
{
|
|
||||||
// Base address of program in CROM: 0x480000
|
|
||||||
*(UINT32 *) &crom[0x480000+0x96B9C] = 0x60000000; // decrementer loop
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3036,14 +2920,17 @@ bool CModel3::LoadROMSet(const struct GameInfo *GameList, const char *zipFile)
|
||||||
PPCConfig.pvr = PPC_MODEL_603E; // 100 MHz
|
PPCConfig.pvr = PPC_MODEL_603E; // 100 MHz
|
||||||
PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ;
|
PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ;
|
||||||
PPCConfig.bus_frequency_multiplier = 0x15; // 1.5X multiplier
|
PPCConfig.bus_frequency_multiplier = 0x15; // 1.5X multiplier
|
||||||
PCIBridge.SetModel(0x105); // MPC105
|
if (!strcmp(Game->id, "scudp1")) // some Step 1.x games use MPC106
|
||||||
|
PCIBridge.SetModel(0x106);
|
||||||
|
else
|
||||||
|
PCIBridge.SetModel(0x105); // MPC105
|
||||||
}
|
}
|
||||||
else if (Game->step == 0x10) // Step 1.0
|
else if (Game->step == 0x10) // Step 1.0
|
||||||
{
|
{
|
||||||
PPCConfig.pvr = PPC_MODEL_603R; // 66 MHz
|
PPCConfig.pvr = PPC_MODEL_603R; // 66 MHz
|
||||||
PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ;
|
PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ;
|
||||||
PPCConfig.bus_frequency_multiplier = 0x10; // 1X multiplier
|
PPCConfig.bus_frequency_multiplier = 0x10; // 1X multiplier
|
||||||
if (!strcmp(Game->id, "bass") || !strcmp(Game->id, "getbass")) // some Step 1.0 games use MPC106
|
if (!strcmp(Game->id, "bass") || !strcmp(Game->id, "getbass")) // some Step 1.x games use MPC106
|
||||||
PCIBridge.SetModel(0x106);
|
PCIBridge.SetModel(0x106);
|
||||||
else
|
else
|
||||||
PCIBridge.SetModel(0x105); // MPC105
|
PCIBridge.SetModel(0x105); // MPC105
|
||||||
|
|
|
@ -136,15 +136,17 @@ void CReal3D::LoadState(CBlockFile *SaveState)
|
||||||
Rendering
|
Rendering
|
||||||
******************************************************************************/
|
******************************************************************************/
|
||||||
|
|
||||||
void CReal3D::BeginVBlank(void)
|
void CReal3D::BeginVBlank(int statusCycles)
|
||||||
{
|
{
|
||||||
status |= 2; // VBlank bit
|
// Calculate point at which status bit should change value. Currently the same timing is used for both the status bit in ReadRegister
|
||||||
|
// and in WriteDMARegister32/ReadDMARegister32, however it may be that they are completely unrelated. It appears that step 1.x games
|
||||||
|
// access just the former while step 2.x access the latter. It is not known yet what this bit/these bits actually represent.
|
||||||
|
statusChange = ppc_total_cycles() + statusCycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CReal3D::EndVBlank(void)
|
void CReal3D::EndVBlank(void)
|
||||||
{
|
{
|
||||||
error = false; // clear error (just needs to be done once per frame)
|
error = false; // clear error (just needs to be done once per frame)
|
||||||
status &= ~2;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
UINT32 CReal3D::SyncSnapshots(void)
|
UINT32 CReal3D::SyncSnapshots(void)
|
||||||
|
@ -358,8 +360,9 @@ void CReal3D::WriteDMARegister32(unsigned reg, UINT32 data)
|
||||||
}
|
}
|
||||||
else if ((data&0x80000000))
|
else if ((data&0x80000000))
|
||||||
{
|
{
|
||||||
dmaUnknownReg ^= 0xFFFFFFFF;
|
//dmaUnknownReg ^= 0xFFFFFFFF;
|
||||||
dmaData = dmaUnknownReg;
|
//dmaData = dmaUnknownReg;
|
||||||
|
dmaData = (ppc_total_cycles() >= statusChange ? 0x0 : 0xFFFFFFFF); // Not sure yet if it is just bit 2 as per ReadRegister above
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 0x14: // ?
|
case 0x14: // ?
|
||||||
|
@ -886,7 +889,10 @@ UINT32 CReal3D::ReadRegister(unsigned reg)
|
||||||
{
|
{
|
||||||
DebugLog("Real3D: Read reg %X\n", reg);
|
DebugLog("Real3D: Read reg %X\n", reg);
|
||||||
if (reg == 0)
|
if (reg == 0)
|
||||||
|
{
|
||||||
|
UINT32 status = (ppc_total_cycles() >= statusChange ? 0x0 : 0x2);
|
||||||
return 0xFFFFFFFD|status;
|
return 0xFFFFFFFD|status;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return 0xFFFFFFFF;
|
return 0xFFFFFFFF;
|
||||||
}
|
}
|
||||||
|
@ -945,7 +951,6 @@ void CReal3D::Reset(void)
|
||||||
queuedUploadTexturesRO.clear();
|
queuedUploadTexturesRO.clear();
|
||||||
|
|
||||||
fifoIdx = 0;
|
fifoIdx = 0;
|
||||||
status = 0;
|
|
||||||
vromTextureAddr = 0;
|
vromTextureAddr = 0;
|
||||||
vromTextureHeader = 0;
|
vromTextureHeader = 0;
|
||||||
tapState = 0;
|
tapState = 0;
|
||||||
|
|
|
@ -80,7 +80,7 @@ public:
|
||||||
*
|
*
|
||||||
* Must be called before the VBlank starts.
|
* Must be called before the VBlank starts.
|
||||||
*/
|
*/
|
||||||
void BeginVBlank(void);
|
void BeginVBlank(int statusCycles);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* EndVBlank(void)
|
* EndVBlank(void)
|
||||||
|
@ -446,7 +446,7 @@ private:
|
||||||
bool commandPortWrittenRO; // Read-only copy of flag
|
bool commandPortWrittenRO; // Read-only copy of flag
|
||||||
|
|
||||||
// Status and command registers
|
// Status and command registers
|
||||||
UINT32 status;
|
UINT64 statusChange;
|
||||||
|
|
||||||
// JTAG Test Access Port
|
// JTAG Test Access Port
|
||||||
UINT64 tapCurrentInstruction; // latched IR (not always equal to IR)
|
UINT64 tapCurrentInstruction; // latched IR (not always equal to IR)
|
||||||
|
|
|
@ -683,6 +683,22 @@ static void DrawCrosshair(float x, float y, float r, float g, float b)
|
||||||
glVertex2f(x+dist+height, y+(base/2.0f)*a);
|
glVertex2f(x+dist+height, y+(base/2.0f)*a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void PrintGLError(GLenum error)
|
||||||
|
{
|
||||||
|
switch (error)
|
||||||
|
{
|
||||||
|
case GL_INVALID_ENUM: printf("invalid enum\n"); break;
|
||||||
|
case GL_INVALID_VALUE: printf("invalid value\n"); break;
|
||||||
|
case GL_INVALID_OPERATION: printf("invalid operation\n"); break;
|
||||||
|
case GL_STACK_OVERFLOW: printf("stack overflow\n"); break;
|
||||||
|
case GL_STACK_UNDERFLOW: printf("stack underflow\n"); break;
|
||||||
|
case GL_OUT_OF_MEMORY: printf("out of memory\n"); break;
|
||||||
|
case GL_TABLE_TOO_LARGE: printf("table too large\n"); break;
|
||||||
|
case GL_NO_ERROR: break;
|
||||||
|
default: printf("unknown error\n"); break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
|
static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
|
||||||
{
|
{
|
||||||
float x[2], y[2];
|
float x[2], y[2];
|
||||||
|
@ -692,6 +708,7 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Set up the viewport and orthogonal projection
|
// Set up the viewport and orthogonal projection
|
||||||
|
glUseProgram(0); // no shaders
|
||||||
glViewport(xOffset, yOffset, xRes, yRes);
|
glViewport(xOffset, yOffset, xRes, yRes);
|
||||||
glMatrixMode(GL_PROJECTION);
|
glMatrixMode(GL_PROJECTION);
|
||||||
glLoadIdentity();
|
glLoadIdentity();
|
||||||
|
@ -701,7 +718,7 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
|
||||||
glDisable(GL_TEXTURE_2D); // no texture mapping
|
glDisable(GL_TEXTURE_2D); // no texture mapping
|
||||||
glDisable(GL_BLEND); // no blending
|
glDisable(GL_BLEND); // no blending
|
||||||
glDisable(GL_DEPTH_TEST); // no Z-buffering needed
|
glDisable(GL_DEPTH_TEST); // no Z-buffering needed
|
||||||
glUseProgram(NULL); // no shaders
|
glDisable(GL_LIGHTING);
|
||||||
|
|
||||||
// Convert gun coordinates to viewspace coordinates
|
// Convert gun coordinates to viewspace coordinates
|
||||||
x[0] = (float) Inputs->gunX[0]->value;
|
x[0] = (float) Inputs->gunX[0]->value;
|
||||||
|
@ -718,6 +735,8 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
|
||||||
if ((showCrosshairs & 2) && !Inputs->trigger[1]->offscreenValue) // Player 2
|
if ((showCrosshairs & 2) && !Inputs->trigger[1]->offscreenValue) // Player 2
|
||||||
DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f);
|
DrawCrosshair(x[1], y[1], 0.0f, 1.0f, 0.0f);
|
||||||
glEnd();
|
glEnd();
|
||||||
|
|
||||||
|
//PrintGLError(glGetError());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue