- 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:
Bart Trzynadlowski 2012-07-12 06:13:24 +00:00
parent 3963a7a53c
commit 772c94d122
9 changed files with 306 additions and 266 deletions

View file

@ -239,7 +239,7 @@ typedef struct {
int interrupt_pending;
int external_int;
UINT64 tb; /* 56-bit timebase register */
UINT64 tb; /* 56-bit timebase register */
int (*irq_callback)(int irqline);
@ -247,12 +247,26 @@ typedef struct {
PPC_FETCH_REGION * fetch;
// STUFF added for the 6xx series
UINT32 dec, dec_frac;
UINT32 dec;
UINT32 fpscr;
FPR fpr[32];
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
int is603;
#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 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)
{
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
return ppc.tb + (cycles / 4);
// Timebase is incremented according to timer ratio, so adjust value accordingly
return ppc.tb + (cycles / ppc.timer_ratio);
}
INLINE void ppc_write_timebase_l(UINT32 tbl)
{
ppc_tb_base_icount = ppc_icount;
UINT64 tb = ppc_read_timebase();
ppc.tb &= ~0xffffffff;
ppc.tb |= tbl;
ppc.tb_base_icount = ppc.icount + ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio);
ppc.tb = (tb&~0xffffffff)|tbl;
}
INLINE void ppc_write_timebase_h(UINT32 tbh)
{
ppc_tb_base_icount = ppc_icount;
UINT64 tb = ppc_read_timebase();
ppc.tb &= 0xffffffff;
ppc.tb |= (UINT64)(tbh) << 32;
ppc.tb_base_icount = ppc.icount + ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio);
ppc.tb = (tb&0xffffffff)|((UINT64)(tbh) << 32);
}
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
return DEC - (cycles / (bus_freq_multiplier * 2));
// Decrementer is decremented at same rate as timebase, so adjust value accordingly
return DEC - (cycles / ppc.timer_ratio);
}
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;
// check if decrementer exception occurs during execution
if ((UINT32)(DEC - (ppc_icount / (bus_freq_multiplier * 2))) > (UINT32)(DEC))
{
ppc_dec_trigger_cycle = ((ppc_icount / (bus_freq_multiplier * 2)) - DEC) * 4;
}
// Check if decrementer exception occurs during execution (exception occurs after decrementer
// has passed through zero)
if ((UINT32)(ppc.dec_base_icount / ppc.timer_ratio) > DEC)
ppc.dec_trigger_cycle = ppc.dec_base_icount - ((1 + DEC) * ppc.timer_ratio);
else
{
ppc_dec_trigger_cycle = 0x7fffffff;
}
// printf("DEC = %08X at %08X\n", value, ppc.pc);
ppc.dec_trigger_cycle = 0x7fffffff;
}
/*********************************************************************/
@ -491,12 +504,6 @@ INLINE void ppc_set_spr(int spr, UINT32 value)
case SPR_PVR: return;
case SPR603E_DEC:
if(((value & 0x80000000) && !(DEC & 0x80000000)) || value == 0)
{
/* trigger interrupt */
ppc.interrupt_pending |= 0x2;
ppc603_check_interrupts();
}
write_decrementer(value);
return;
@ -821,13 +828,29 @@ void ppc_init(const PPC_CONFIG *config)
multiplier = (float)((config->bus_frequency_multiplier >> 4) & 0xf) +
(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)
{
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;
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[ppc.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;
}
@ -861,6 +884,31 @@ void ppc_set_fetch(PPC_FETCH_REGION * 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
******************************************************************************/
@ -874,11 +922,10 @@ void ppc_save_state(CBlockFile *SaveState)
{
SaveState->NewBlock("PowerPC", __FILE__);
// Timer and decrementer
SaveState->Write(&ppc_icount, sizeof(ppc_icount));
SaveState->Write(&ppc_tb_base_icount, sizeof(ppc_tb_base_icount));
SaveState->Write(&ppc_dec_base_icount, sizeof(ppc_dec_base_icount));
SaveState->Write(&ppc_dec_trigger_cycle, sizeof(ppc_dec_trigger_cycle));
// Cycle counting
SaveState->Write(&ppc.icount, sizeof(ppc.icount));
SaveState->Write(&ppc.cur_cycles, sizeof(ppc.cur_cycles));
SaveState->Write(&ppc.total_cycles, sizeof(ppc.total_cycles));
// Registers
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.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.fpr, sizeof(ppc.fpr));
@ -955,10 +1002,9 @@ void ppc_load_state(CBlockFile *SaveState)
}
// Timer and decrementer
SaveState->Read(&ppc_icount, sizeof(ppc_icount));
SaveState->Read(&ppc_tb_base_icount, sizeof(ppc_tb_base_icount));
SaveState->Read(&ppc_dec_base_icount, sizeof(ppc_dec_base_icount));
SaveState->Read(&ppc_dec_trigger_cycle, sizeof(ppc_dec_trigger_cycle));
SaveState->Read(&ppc.icount, sizeof(ppc.icount));
SaveState->Read(&ppc.cur_cycles, sizeof(ppc.cur_cycles));
SaveState->Read(&ppc.total_cycles, sizeof(ppc.total_cycles));
// Registers
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.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.fpr, sizeof(ppc.fpr));
@ -1071,6 +1117,7 @@ void ppc_detach_debugger()
Bus = PPCDebug->DetachBus();
PPCDebug = NULL;
}
#endif // SUPERMODEL_DEBUGGER
void ppc_set_pc(UINT32 pc)
{
@ -1113,4 +1160,3 @@ UINT32 ppc_read_msr()
{
return ppc_get_msr();
}
#endif // SUPERMODEL_DEBUGGER

View file

@ -352,6 +352,11 @@ extern void ppc_reset(void);
extern void ppc_shutdown(void);
extern void ppc_init(const PPC_CONFIG *config); // must be called second!
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
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
extern void ppc_attach_debugger(class Debugger::CPPCDebug *PPCDebugPtr);
extern void ppc_detach_debugger();
#endif // SUPERMODEL_DEBUGGER
extern void ppc_set_pc(UINT32 pc);
extern UINT8 ppc_get_cr(unsigned num);
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_sr(unsigned num, UINT32 val);
extern UINT32 ppc_read_msr();
#endif // SUPERMODEL_DEBUGGER
#endif // INCLUDED_PPC_H

View file

@ -247,25 +247,30 @@ void ppc_reset(void)
ppc.hid0 = 1;
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)
{
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;
}
ppc.cur_cycles = cycles;
ppc.icount = cycles;
ppc.tb_base_icount = cycles + ppc.timer_frac;
ppc.dec_base_icount = cycles + ppc.timer_frac;
// Check if decrementer exception occurs during execution (exception occurs after decrementer
// has passed through zero)
if ((UINT32)(ppc.dec_base_icount / ppc.timer_ratio) > DEC)
ppc.dec_trigger_cycle = ppc.dec_base_icount - ((1 + DEC) * ppc.timer_ratio);
else
{
ppc_dec_trigger_cycle = 0x7fffffff;
}
ppc.dec_trigger_cycle = 0x7fffffff;
ppc_change_pc(ppc.npc);
@ -276,8 +281,6 @@ int ppc_execute(int cycles)
DisassemblePowerPC(opcode, ppc.npc, string1, string2, true);
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();
@ -286,7 +289,7 @@ int ppc_execute(int cycles)
PPCDebug->CPUActive();
#endif // SUPERMODEL_DEBUGGER
while( ppc_icount > 0 && !ppc.fatalError)
while( ppc.icount > 0 && !ppc.fatalError)
{
ppc.pc = ppc.npc;
@ -319,14 +322,10 @@ int ppc_execute(int cycles)
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)
//ppc.tb += 4;
if(ppc_icount == ppc_dec_trigger_cycle)
if (ppc.icount == ppc.dec_trigger_cycle)
{
// printf("dec int at %d\n", ppc_icount);
ppc.interrupt_pending |= 0x2;
ppc603_check_interrupts();
}
@ -339,17 +338,11 @@ int ppc_execute(int cycles)
PPCDebug->CPUInactive();
#endif // SUPERMODEL_DEBUGGER
// update timebase
// timebase is incremented once every four core clock cycles, so adjust the cycles accordingly
// NOTE: updating at the end of the time slice breaks things that try to wait on TBL. Performing
// the update inside the execution loop fixes VF3 and allows many decrementer patches to be
// 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));
// Update timebase and decrementer. Both are updated at same rate as specified by timer_ratio.
ppc.timer_frac = ((ppc.tb_base_icount - ppc.icount) % ppc.timer_ratio);
ppc.tb += ((ppc.tb_base_icount - ppc.icount) / ppc.timer_ratio);
DEC -= ((ppc.dec_base_icount - ppc.icount) / ppc.timer_ratio);
/*
{
char string1[200];
@ -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;
}

View file

@ -2221,7 +2221,7 @@ static void ppc_fctiwx(UINT32 op)
}
else if(FPR(b).fd < (INT64)((INT32)0x80000000))
{
FPR(t).id = 0x80000000;
FPR(t).id = 0x80000000;
// FPSCR[FR] = 1
// FPSCR[FI] = 1
// FPSCR[XX] = 1
@ -2255,7 +2255,7 @@ static void ppc_fctiwzx(UINT32 op)
if(r > (INT64)((INT32)0x7fffffff))
{
FPR(t).id = 0x7fffffff;
FPR(t).id = 0x7fffffff;
// FPSCR[FR] = 0
// FPSCR[FI] = 1
// FPSCR[XX] = 1
@ -2263,7 +2263,7 @@ static void ppc_fctiwzx(UINT32 op)
}
else if(r < (INT64)((INT32)0x80000000))
{
FPR(t).id = 0x80000000;
FPR(t).id = 0x80000000;
// FPSCR[FR] = 1
// FPSCR[FI] = 1
// FPSCR[XX] = 1
@ -2368,8 +2368,8 @@ static void ppc_frsqrtex(UINT32 op)
SET_VXSNAN_1(FPR(b));
FPR(t).fd = 1.0 / sqrt(FPR(b).fd); /* verify this */
FPR(t).fd = 1.0 / sqrt(FPR(b).fd); /* verify this */
set_fprf(FPR(t));
if( RCBIT ) {
SET_CR1();
@ -2745,7 +2745,7 @@ static void ppc_fmaddsx(UINT32 op)
SET_VXSNAN(FPR(a), FPR(b));
SET_VXSNAN_1(FPR(c));
FPR(t).fd = (float)((FPR(a).fd * FPR(c).fd) + FPR(b).fd);
FPR(t).fd = (float)((FPR(a).fd * FPR(c).fd) + FPR(b).fd);
set_fprf(FPR(t));
if( RCBIT ) {

View file

@ -1470,6 +1470,88 @@ const struct GameInfo g_Model3GameList[] =
{ NULL, false, NULL, 0, 0, 0, 0, 0, false }
}
},
// 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
{

View file

@ -26,9 +26,12 @@
*
* 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
* 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 :)
*
* PowerPC Address Map (may be slightly out of date/incomplete)
@ -1983,15 +1986,40 @@ void CModel3::RunMainBoardFrame(void)
UINT32 start = CThread::GetTicks();
// Compute display and VBlank timings
unsigned frameCycles = g_Config.GetPowerPCFrequency()*1000000/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 ppcCycles = g_Config.GetPowerPCFrequency() * 1000000;
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;
// 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
if (gpusReady)
{
TileGen.BeginVBlank();
GPU.BeginVBlank();
GPU.BeginVBlank(statusCycles);
IRQ.Assert(0x02);
ppc_execute(vblCycles);
//printf("PC=%08X LR=%08X\n", ppc_get_pc(), ppc_get_lr());
@ -2020,7 +2048,8 @@ void CModel3::RunMainBoardFrame(void)
ppc_execute(200); // give PowerPC time to acknowledge IRQ
IRQ.Deassert(0x40);
ppc_execute(200); // acknowledge that IRQ was deasserted (TODO: is this really needed?)
dispCycles -= 400;
++irqCount;
if (irqCount > 128)
{
@ -2640,15 +2669,7 @@ void CModel3::Reset(void)
// Apply patches to games
void CModel3::Patch(void)
{
if (!strcmp(Game->id, "vf3") || !strcmp(Game->id, "vf3a"))
{
// 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"))
if (!strcmp(Game->id, "lemans24"))
{
// Base offset of program in CROM: 6473C0
*(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[0x73EDD0] = 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"))
{
*(UINT32 *) &crom[0x7374f4] = 0x38840004; // an actual bug in the game code
@ -2739,134 +2714,43 @@ void CModel3::Patch(void)
*(UINT32 *) &crom[0x68468c] = 0x60000000; // protection device
*(UINT32 *) &crom[0x6063c4] = 0x60000000; // needed to allow levels to load
*(UINT32 *) &crom[0x616434] = 0x60000000; // prevents PPC from executing invalid code (MMU?)
*(UINT32 *) &crom[0x69f4e4] = 0x60000000; // ""
*(UINT32 *) &crom[0x600000+0x4C744] = 0x60000000; // decrementer loop?
*(UINT32 *) &crom[0x69f4e4] = 0x60000000; // ""
}
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[0x618B28] = 0x60000000; // MAME says: jump to encrypted code
*(UINT32 *) &crom[0x64CA34] = 0x60000000; // decrementer
*(UINT32 *) &crom[0x618B28] = 0x60000000; // MAME says: jump to encrypted code
}
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"))
{
*(UINT32 *) &crom[0x50E8D4] = 0x60000000;
*(UINT32 *) &crom[0x50E8F4] = 0x60000000;
*(UINT32 *) &crom[0x50FB84] = 0x60000000;
*(UINT32 *) &crom[0x4F736C] = 0x60000000;
*(UINT32 *) &crom[0x4F738C] = 0x60000000;
}
else if (!strcmp(Game->id, "harleyb"))
{
*(UINT32 *) &crom[0x50ECB4] = 0x60000000;
*(UINT32 *) &crom[0x50ECD4] = 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"))
{
*(UINT32 *) &crom[0xF0E48] = 0x60000000;
*(UINT32 *) &crom[0x043DC] = 0x48000090;
*(UINT32 *) &crom[0x043DC] = 0x48000090; // related to joystick feedback
*(UINT32 *) &crom[0x029A0] = 0x60000000;
*(UINT32 *) &crom[0x02A0C] = 0x60000000;
}
else if (!strcmp(Game->id, "swtrilgya"))
{
*(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"))
{
*(UINT32 *) &crom[0x535580] = 0x60000000;
*(UINT32 *) &crom[0x5023B4] = 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.bus_frequency = BUS_FREQUENCY_66MHZ;
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
{
PPCConfig.pvr = PPC_MODEL_603R; // 66 MHz
PPCConfig.bus_frequency = BUS_FREQUENCY_66MHZ;
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);
else
PCIBridge.SetModel(0x105); // MPC105
@ -3055,7 +2942,7 @@ bool CModel3::LoadROMSet(const struct GameInfo *GameList, const char *zipFile)
ppc_init(&PPCConfig);
ppc_attach_bus(this);
PPCFetchRegions[0].start = 0;
PPCFetchRegions[0].end = 0x007FFFFF;
PPCFetchRegions[0].ptr = (UINT32 *) ram;

View file

@ -136,15 +136,17 @@ void CReal3D::LoadState(CBlockFile *SaveState)
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)
{
error = false; // clear error (just needs to be done once per frame)
status &= ~2;
}
UINT32 CReal3D::SyncSnapshots(void)
@ -358,8 +360,9 @@ void CReal3D::WriteDMARegister32(unsigned reg, UINT32 data)
}
else if ((data&0x80000000))
{
dmaUnknownReg ^= 0xFFFFFFFF;
dmaData = dmaUnknownReg;
//dmaUnknownReg ^= 0xFFFFFFFF;
//dmaData = dmaUnknownReg;
dmaData = (ppc_total_cycles() >= statusChange ? 0x0 : 0xFFFFFFFF); // Not sure yet if it is just bit 2 as per ReadRegister above
}
break;
case 0x14: // ?
@ -886,7 +889,10 @@ UINT32 CReal3D::ReadRegister(unsigned reg)
{
DebugLog("Real3D: Read reg %X\n", reg);
if (reg == 0)
{
UINT32 status = (ppc_total_cycles() >= statusChange ? 0x0 : 0x2);
return 0xFFFFFFFD|status;
}
else
return 0xFFFFFFFF;
}
@ -945,7 +951,6 @@ void CReal3D::Reset(void)
queuedUploadTexturesRO.clear();
fifoIdx = 0;
status = 0;
vromTextureAddr = 0;
vromTextureHeader = 0;
tapState = 0;

View file

@ -80,7 +80,7 @@ public:
*
* Must be called before the VBlank starts.
*/
void BeginVBlank(void);
void BeginVBlank(int statusCycles);
/*
* EndVBlank(void)
@ -446,7 +446,7 @@ private:
bool commandPortWrittenRO; // Read-only copy of flag
// Status and command registers
UINT32 status;
UINT64 statusChange;
// JTAG Test Access Port
UINT64 tapCurrentInstruction; // latched IR (not always equal to IR)

View file

@ -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);
}
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)
{
float x[2], y[2];
@ -692,6 +708,7 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
return;
// Set up the viewport and orthogonal projection
glUseProgram(0); // no shaders
glViewport(xOffset, yOffset, xRes, yRes);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
@ -700,8 +717,8 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
glLoadIdentity();
glDisable(GL_TEXTURE_2D); // no texture mapping
glDisable(GL_BLEND); // no blending
glDisable(GL_DEPTH_TEST); // no Z-buffering needed
glUseProgram(NULL); // no shaders
glDisable(GL_DEPTH_TEST); // no Z-buffering needed
glDisable(GL_LIGHTING);
// Convert gun coordinates to viewspace coordinates
x[0] = (float) Inputs->gunX[0]->value;
@ -711,13 +728,15 @@ static void UpdateCrosshairs(CInputs *Inputs, unsigned showCrosshairs)
GunToViewCoords(&x[0], &y[0]);
GunToViewCoords(&x[1], &y[1]);
// Draw visible crosshairs
// Draw visible crosshairs
glBegin(GL_TRIANGLES);
if ((showCrosshairs & 1) && !Inputs->trigger[0]->offscreenValue) // Player 1
DrawCrosshair(x[0], y[0], 1.0f, 0.0f, 0.0f);
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);
glEnd();
glEnd();
//PrintGLError(glGetError());
}