update psx core to 0.9.38.7

This commit is contained in:
zeromus 2015-10-02 00:52:56 -05:00
parent 29767ba522
commit 5cfc1dae2a
8 changed files with 2539 additions and 2420 deletions

Binary file not shown.

View File

@ -81,3 +81,11 @@
[OK] psx/memcard : change to debug output [OK] psx/memcard : change to debug output
0.9.38.4 -> 0.9.38.6 0.9.38.4 -> 0.9.38.6
[OK] psx/gpu & gpu_sprite : Fixed GPU emulation timing bugs that caused graphical glitches in "Mr. Driller G". [OK] psx/gpu & gpu_sprite : Fixed GPU emulation timing bugs that caused graphical glitches in "Mr. Driller G".
0.9.38.5 -> 0.9.38.7
[OK] psx/cpu : Revisions to exception handling
[OK] psx/cpu : Many revisions and cleanups to branch and exception handling in opcode implementations
[OK] psx/dis : Just some basic disassembly changes
[OK] psx/gte : Cleanup
[OK] psx/psx : Cleanup
[OK] psx/timer : Major functional changes
[NO] psx/timer : Added loadstate sanity checks

File diff suppressed because it is too large Load Diff

View File

@ -2,40 +2,40 @@
#define __MDFN_PSX_CPU_H #define __MDFN_PSX_CPU_H
/* /*
Load delay notes: Load delay notes:
// Takes 1 less // Takes 1 less
".set noreorder\n\t" ".set noreorder\n\t"
".set nomacro\n\t" ".set nomacro\n\t"
"lw %0, 0(%2)\n\t" "lw %0, 0(%2)\n\t"
"nop\n\t" "nop\n\t"
"nop\n\t" "nop\n\t"
"or %0, %1, %1\n\t" "or %0, %1, %1\n\t"
// cycle than this: // cycle than this:
".set noreorder\n\t" ".set noreorder\n\t"
".set nomacro\n\t" ".set nomacro\n\t"
"lw %0, 0(%2)\n\t" "lw %0, 0(%2)\n\t"
"nop\n\t" "nop\n\t"
"or %0, %1, %1\n\t" "or %0, %1, %1\n\t"
"nop\n\t" "nop\n\t"
// Both of these // Both of these
".set noreorder\n\t" ".set noreorder\n\t"
".set nomacro\n\t" ".set nomacro\n\t"
"lw %0, 0(%2)\n\t" "lw %0, 0(%2)\n\t"
"nop\n\t" "nop\n\t"
"nop\n\t" "nop\n\t"
"or %1, %0, %0\n\t" "or %1, %0, %0\n\t"
// take same...(which is kind of odd). // take same...(which is kind of odd).
".set noreorder\n\t" ".set noreorder\n\t"
".set nomacro\n\t" ".set nomacro\n\t"
"lw %0, 0(%2)\n\t" "lw %0, 0(%2)\n\t"
"nop\n\t" "nop\n\t"
"or %1, %0, %0\n\t" "or %1, %0, %0\n\t"
"nop\n\t" "nop\n\t"
*/ */
#include "gte.h" #include "gte.h"
@ -45,8 +45,8 @@ namespace MDFN_IEN_PSX
#define PS_CPU_EMULATE_ICACHE 1 #define PS_CPU_EMULATE_ICACHE 1
class PS_CPU class PS_CPU
{ {
public: public:
PS_CPU() MDFN_COLD; PS_CPU() MDFN_COLD;
@ -123,13 +123,13 @@ class PS_CPU
CP0REG_BDA = 5, // Data load/store breakpoint address. CP0REG_BDA = 5, // Data load/store breakpoint address.
CP0REG_TAR = 6, // Target address(???) CP0REG_TAR = 6, // Target address(???)
CP0REG_DCIC = 7, // Cache control CP0REG_DCIC = 7, // Cache control
CP0REG_BADVA = 8,
CP0REG_BDAM = 9, // Data load/store address mask. CP0REG_BDAM = 9, // Data load/store address mask.
CP0REG_BPCM = 11, // PC breakpoint address mask. CP0REG_BPCM = 11, // PC breakpoint address mask.
CP0REG_SR = 12, CP0REG_SR = 12,
CP0REG_CAUSE = 13, CP0REG_CAUSE = 13,
CP0REG_EPC = 14, CP0REG_EPC = 14,
CP0REG_PRID = 15, // Product ID CP0REG_PRID = 15 // Product ID
CP0REG_ERREG = 16
}; };
struct struct
@ -145,9 +145,9 @@ class PS_CPU
uint32 BPC; // RW uint32 BPC; // RW
uint32 Unused04; uint32 Unused04;
uint32 BDA; // RW uint32 BDA; // RW
uint32 TAR; uint32 TAR; // R
uint32 DCIC; // RW uint32 DCIC; // RW
uint32 Unused08; uint32 BADVA; // R
uint32 BDAM; // R/W uint32 BDAM; // R/W
uint32 Unused0A; uint32 Unused0A;
uint32 BPCM; // R/W uint32 BPCM; // R/W
@ -155,7 +155,6 @@ class PS_CPU
uint32 CAUSE; // R/W(partial) uint32 CAUSE; // R/W(partial)
uint32 EPC; // R uint32 EPC; // R
uint32 PRID; // R uint32 PRID; // R
uint32 ERREG; // ?(may not exist, test)
}; };
}; };
} CP0; } CP0;
@ -209,7 +208,7 @@ class PS_CPU
EXCEPTION_OV = 12 // Arithmetic overflow EXCEPTION_OV = 12 // Arithmetic overflow
}; };
uint32 Exception(uint32 code, uint32 PC, const uint32 NPM) MDFN_WARN_UNUSED_RESULT; uint32 Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 NPM, const uint32 instr) MDFN_WARN_UNUSED_RESULT;
template<bool DebugMode, bool BIOSPrintMode, bool ILHMode> pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE; template<bool DebugMode, bool BIOSPrintMode, bool ILHMode> pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE;
@ -223,8 +222,8 @@ class PS_CPU
// Mednafen debugger stuff follows: // Mednafen debugger stuff follows:
// //
public: public:
void SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32 pc), void (*addbt)(uint32 from, uint32 to, bool exception)); void SetCPUHook(void(*cpuh)(const pscpu_timestamp_t timestamp, uint32 pc), void(*addbt)(uint32 from, uint32 to, bool exception));
void CheckBreakpoints(void (*callback)(bool write, uint32 address, unsigned int len), uint32 instr); void CheckBreakpoints(void(*callback)(bool write, uint32 address, unsigned int len), uint32 instr);
void* debug_GetScratchRAMPtr() { return ScratchRAM.data8; } void* debug_GetScratchRAMPtr() { return ScratchRAM.data8; }
void* debug_GetGPRPtr() { return GPR; } void* debug_GetGPRPtr() { return GPR; }
@ -254,9 +253,9 @@ class PS_CPU
void PokeMem32(uint32 A, uint32 V); void PokeMem32(uint32 A, uint32 V);
private: private:
void (*CPUHook)(const pscpu_timestamp_t timestamp, uint32 pc); void(*CPUHook)(const pscpu_timestamp_t timestamp, uint32 pc);
void (*ADDBT)(uint32 from, uint32 to, bool exception); void(*ADDBT)(uint32 from, uint32 to, bool exception);
}; };
} }

View File

@ -128,13 +128,15 @@ struct OpEntry
#define MK_OP(mnemonic, format, op, func, extra_mask) { MASK_OP | (op ? 0 : MASK_FUNC) | extra_mask, ((unsigned)op << 26) | func, mnemonic, format } #define MK_OP(mnemonic, format, op, func, extra_mask) { MASK_OP | (op ? 0 : MASK_FUNC) | extra_mask, ((unsigned)op << 26) | func, mnemonic, format }
#define MK_OP_REGIMM(mnemonic, regop) { MASK_OP | MASK_RT, (0x01U << 26) | (regop << 16), mnemonic, "s, p" } #define MK_OP_REGIMM(mnemonic, regop_mask, regop) { MASK_OP | (regop_mask << 16), (0x01U << 26) | (regop << 16), mnemonic, "s, p" }
#define MK_COPZ(z) { MASK_OP | (0x1U << 25), (0x1U << 25) | ((0x10U | z) << 26), "cop" #z, "F" } #define MK_COPZ(z) { MASK_OP | (0x1U << 25), (0x1U << 25) | ((0x10U | z) << 26), "cop" #z, "F" }
#define MK_COP0_FUNC(mnemonic, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x10U << 26) | (0x1U << 25) | func, mnemonic, "" } #define MK_COP0_FUNC(mnemonic, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x10U << 26) | (0x1U << 25) | func, mnemonic, "" }
#define MK_COPZ_XFER(z, mnemonic, format, xf) { MASK_OP | (0x1FU << 21), ((0x10U | z) << 26) | (xf << 21), mnemonic, format } #define MK_COPZ_XFER(z, mnemonic, format, xf) { MASK_OP | (0x1FU << 21), ((0x10U | z) << 26) | (xf << 21), mnemonic, format }
#define MK_COPZ_BCzx(z, x) { MASK_OP | (0x1BU << 21) | (0x01 << 16), ((0x10U | z) << 26) | (0x08 << 21) | (x << 16), (x ? "bc" #z "t" : "bc" #z "f"), "p" }
#define MK_COPZ_BC(z) MK_COPZ_BCzx(z, 0), MK_COPZ_BCzx(z, 1)
#define MK_GTE(mnemonic, format, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x1U << 25) | (0x12U << 26) | func, mnemonic, format } #define MK_GTE(mnemonic, format, func) { MASK_OP | (0x1U << 25) | MASK_FUNC, (0x1U << 25) | (0x12U << 26) | func, mnemonic, format }
@ -180,10 +182,12 @@ static OpEntry ops[] =
MK_OP("slt", "d, s, t", 0, 42, 0), MK_OP("slt", "d, s, t", 0, 42, 0),
MK_OP("sltu", "d, s, t", 0, 43, 0), MK_OP("sltu", "d, s, t", 0, 43, 0),
MK_OP_REGIMM("bgez", 0x01), // keep *al before the non-linking versions, due to mask setup.
MK_OP_REGIMM("bgezal", 0x11), MK_OP_REGIMM("bgezal", 0x1F, 0x11),
MK_OP_REGIMM("bltz", 0x00), MK_OP_REGIMM("bltzal", 0x1F, 0x10),
MK_OP_REGIMM("bltzal", 0x10),
MK_OP_REGIMM("bgez", 0x01, 0x01),
MK_OP_REGIMM("bltz", 0x00, 0x00),
MK_OP("j", "P", 2, 0, 0), MK_OP("j", "P", 2, 0, 0),
@ -225,6 +229,11 @@ static OpEntry ops[] =
MK_COPZ_XFER(2, "ctc2", "t, G", 0x06), MK_COPZ_XFER(2, "ctc2", "t, G", 0x06),
MK_COPZ_XFER(3, "ctc3", "t, ?", 0x06), MK_COPZ_XFER(3, "ctc3", "t, ?", 0x06),
MK_COPZ_BC(0),
MK_COPZ_BC(1),
MK_COPZ_BC(2),
MK_COPZ_BC(3),
// COP0 stuff here // COP0 stuff here
MK_COP0_FUNC("rfe", 0x10), MK_COP0_FUNC("rfe", 0x10),
@ -316,8 +325,8 @@ EW_EXPORT s32 shock_Util_DisassembleMIPS(u32 PC, u32 instr, void* outbuf, s32 bu
static const char *cop0_names[32] = static const char *cop0_names[32] =
{ {
"CPR0", "CPR1", "CPR2", "BPC", "CPR4", "BDA", "TAR", "DCIC", "CPR8", "BDAM", "CPR10", "BPCM", "SR", "CAUSE", "EPC", "PRID", "CPR0", "CPR1", "CPR2", "BPC", "CPR4", "BDA", "TAR", "DCIC", "BADVA", "BDAM", "CPR10", "BPCM", "SR", "CAUSE", "EPC", "PRID",
"ERREG", "CPR17", "CPR18", "CPR19", "CPR20", "CPR21", "CPR22", "CPR23", "CPR24", "CPR25", "CPR26", "CPR27", "CPR28", "CPR29", "CPR30", "CPR31" "CPR16", "CPR17", "CPR18", "CPR19", "CPR20", "CPR21", "CPR22", "CPR23", "CPR24", "CPR25", "CPR26", "CPR27", "CPR28", "CPR29", "CPR30", "CPR31"
}; };
static const char *gte_cr_names[32] = static const char *gte_cr_names[32] =

View File

@ -796,22 +796,6 @@ static INLINE int32 Lm_G(unsigned int which, int32 value)
// limit to 4096, not 4095 // limit to 4096, not 4095
static INLINE int32 Lm_H(int32 value) static INLINE int32 Lm_H(int32 value)
{ {
#if 0
if(FLAGS & (1 << 15))
{
value = 0;
FLAGS |= 1 << 12;
return value;
}
if(FLAGS & (1 << 16))
{
value = 4096;
FLAGS |= 1 << 12;
return value;
}
#endif
if(value < 0) if(value < 0)
{ {
value = 0; value = 0;

View File

@ -272,7 +272,6 @@ static void RebaseTS(const pscpu_timestamp_t timestamp)
void PSX_SetEventNT(const int type, const pscpu_timestamp_t next_timestamp) void PSX_SetEventNT(const int type, const pscpu_timestamp_t next_timestamp)
{ {
assert(type > PSX_EVENT__SYNFIRST && type < PSX_EVENT__SYNLAST);
event_list_entry *e = &events[type]; event_list_entry *e = &events[type];
if(next_timestamp < e->event_time) if(next_timestamp < e->event_time)

View File

@ -31,9 +31,9 @@
For timer2: For timer2:
0x1 = timer stopped(TODO: confirm on real system) 0x1 = timer stopped(TODO: confirm on real system)
Target mode enabled 0x008 Target match counter reset enable 0x008
IRQ enable 0x010 Target match IRQ enable 0x010
--?Affects 0x400 status flag?-- 0x020 Overflow IRQ enable 0x020
IRQ evaluation auto-reset 0x040 IRQ evaluation auto-reset 0x040
--unknown-- 0x080 --unknown-- 0x080
Clock selection 0x100 Clock selection 0x100
@ -43,11 +43,10 @@
Reset to 0 on writes to the mode/status register. Reset to 0 on writes to the mode/status register.
Status flags: Status flags:
Unknown flag 0x0400 Current IRQ line status? 0x0400
Compare flag 0x0800 Compare flag 0x0800
Cleared on mode/status read. Cleared on mode/status read.
Set when: //ever Counter == 0(proooobably, need to investigate lower 3 bits in relation to this). Set repeatedly while counter == target.
Overflow/Carry flag 0x1000 Overflow/Carry flag 0x1000
Cleared on mode/status read. Cleared on mode/status read.
@ -58,20 +57,17 @@
Cleared on writes to the mode/status register, on writes to the count register, and apparently automatically when the counter Cleared on writes to the mode/status register, on writes to the count register, and apparently automatically when the counter
increments if (Mode & 0x40) [Note: If target mode is enabled, and target is 0, IRQ done flag won't be automatically reset] increments if (Mode & 0x40) [Note: If target mode is enabled, and target is 0, IRQ done flag won't be automatically reset]
There seems to be a brief period(edge condition?) where, if count to target is enabled, you can (sometimes?) read the target value in the count There seems to be a brief period(edge condition?) where, if target match reset mode is enabled, you can (sometimes?) read the target value in the count
register before it's reset to 0. I doubt any games rely on this, but who knows. Maybe a PSX equivalent of the PC Engine "Battle Royale"? ;) register before it's reset to 0. Currently not emulated; I doubt any games rely on this, but who knows. Maybe a PSX equivalent
of the PC Engine "Battle Royale"? ;)
When the counter == 0, the compare flag is set. An IRQ will be generated if (Mode & 0x10), and the hidden IRQ done flag will be set. A timer is somewhat unreliable when target match reset mode is enabled and the 33MHz clock is used. Average 2.4 counts seem to be
*/ skipped for that timer every target match reset, but oddly subtracting only 2 from the desired target match value seems to effectively
negate the loss...wonder if my test program is faulty in some way. Currently not emulated.
/* Counters using GPU clock sources(hretrace,dot clock) reportedly will with a low probability return wrong count values on an actual PS1,
Dec. 26, 2011 Note so keep this in mind when writing test programs(IE keep reading the count value until two consecutive reads return the same value).
Due to problems I've had with my GPU timing test program, timer2 appears to be unreliable(clocks are skipped?) when target mode is enabled and the full Currently not emulated.
33MHz clock is used(rather than 33MHz / 8). TODO: Investigate further and confirm(or not).
Jan. 15, 2013 Note:
Counters using GPU clock sources(hretrace,dot clock) reportedly will with a low probability return wrong count values on an actual PS1, so keep this in mind
when writing test programs(IE keep reading the count value until two consecutive reads return the same value).
*/ */
/* /*
@ -86,10 +82,10 @@ namespace MDFN_IEN_PSX
struct Timer struct Timer
{ {
uint32 Mode; uint32 Mode;
int32 Counter; // Only 16-bit, but 32-bit here for detecting counting past target. uint32 Counter; // Only 16-bit, but 32-bit here for detecting counting past target.
int32 Target; uint32 Target;
int32 Div8Counter; uint32 Div8Counter;
bool IRQDone; bool IRQDone;
int32 DoZeCounting; int32 DoZeCounting;
@ -100,17 +96,13 @@ static bool hretrace;
static Timer Timers[3]; static Timer Timers[3];
static pscpu_timestamp_t lastts; static pscpu_timestamp_t lastts;
static int32 CalcNextEvent(int32 next_event) static uint32 CalcNextEvent(void)
{ {
for(int i = 0; i < 3; i++) uint32 next_event = 1024; //
for(unsigned i = 0; i < 3; i++)
{ {
int32 target; if(!(Timers[i].Mode & 0x30)) // If IRQ is disabled, abort for this timer(don't look at IRQDone for this test, or things will break since its resetting is deferred!).
int32 count_delta;
if((i == 0 || i == 1) && (Timers[i].Mode & 0x100)) // If clocked by GPU, abort for this timer(will result in poor granularity for pixel-clock-derived timer IRQs, but whatever).
continue;
if(!(Timers[i].Mode & 0x10)) // If IRQ is disabled, abort for this timer.
continue; continue;
if((Timers[i].Mode & 0x8) && (Timers[i].Counter == 0) && (Timers[i].Target == 0) && !Timers[i].IRQDone) if((Timers[i].Mode & 0x8) && (Timers[i].Counter == 0) && (Timers[i].Target == 0) && !Timers[i].IRQDone)
@ -119,17 +111,10 @@ static int32 CalcNextEvent(int32 next_event)
continue; continue;
} }
target = ((Timers[i].Mode & 0x8) && (Timers[i].Counter < Timers[i].Target)) ? Timers[i].Target : 0x10000; //
//
count_delta = target - Timers[i].Counter; if((i == 0 || i == 1) && (Timers[i].Mode & 0x100)) // If clocked by GPU, abort for this timer(will result in poor granularity for pixel-clock-derived timer IRQs, but whatever).
if(count_delta <= 0)
{
PSX_DBG(PSX_DBG_ERROR, "timer %d count_delta <= 0!!! %d %d\n", i, target, Timers[i].Counter);
continue; continue;
}
{
int32 tmp_clocks;
if(Timers[i].DoZeCounting <= 0) if(Timers[i].DoZeCounting <= 0)
continue; continue;
@ -137,30 +122,83 @@ static int32 CalcNextEvent(int32 next_event)
if((i == 0x2) && (Timers[i].Mode & 0x1)) if((i == 0x2) && (Timers[i].Mode & 0x1))
continue; continue;
//
//
//
const uint32 target = ((Timers[i].Mode & 0x18) && (Timers[i].Counter < Timers[i].Target)) ? Timers[i].Target : 0x10000;
const uint32 count_delta = target - Timers[i].Counter;
uint32 tmp_clocks;
if((i == 0x2) && (Timers[i].Mode & 0x200)) if((i == 0x2) && (Timers[i].Mode & 0x200))
{ tmp_clocks = (count_delta * 8) - Timers[i].Div8Counter;
assert(Timers[i].Div8Counter >= 0 && Timers[i].Div8Counter < 8);
tmp_clocks = ((count_delta - 1) * 8) + (8 - Timers[i].Div8Counter);
}
else else
tmp_clocks = count_delta; tmp_clocks = count_delta;
assert(tmp_clocks > 0);
if(next_event > tmp_clocks) if(next_event > tmp_clocks)
next_event = tmp_clocks; next_event = tmp_clocks;
} }
}
return(next_event); return(next_event);
} }
static bool TimerMatch(unsigned i)
{
bool irq_exact = false;
Timers[i].Mode |= 0x0800;
if(Timers[i].Mode & 0x008)
Timers[i].Counter %= std::max<uint32>(1, Timers[i].Target);
if((Timers[i].Mode & 0x10) && !Timers[i].IRQDone)
{
if(Timers[i].Counter == 0 || Timers[i].Counter == Timers[i].Target)
irq_exact = true;
#if 1
{
const uint16 lateness = (Timers[i].Mode & 0x008) ? Timers[i].Counter : (Timers[i].Counter - Timers[i].Target);
if(lateness > ((i == 1 && (Timers[i].Mode & 0x100)) ? 0 : 3))
PSX_DBG(PSX_DBG_WARNING, "[TIMER] Timer %d match IRQ trigger late: %u\n", i, lateness);
}
#endif
Timers[i].IRQDone = true;
IRQ_Assert(IRQ_TIMER_0 + i, true);
IRQ_Assert(IRQ_TIMER_0 + i, false);
}
return irq_exact;
}
static bool TimerOverflow(unsigned i)
{
bool irq_exact = false;
Timers[i].Mode |= 0x1000;
Timers[i].Counter &= 0xFFFF;
if((Timers[i].Mode & 0x20) && !Timers[i].IRQDone)
{
if(Timers[i].Counter == 0)
irq_exact = true;
#if 1
if(Timers[i].Counter > ((i == 1 && (Timers[i].Mode & 0x100)) ? 0 : 3))
PSX_DBG(PSX_DBG_WARNING, "[TIMER] Timer %d overflow IRQ trigger late: %u\n", i, Timers[i].Counter);
#endif
Timers[i].IRQDone = true;
IRQ_Assert(IRQ_TIMER_0 + i, true);
IRQ_Assert(IRQ_TIMER_0 + i, false);
}
return irq_exact;
}
static void ClockTimer(int i, uint32 clocks) static void ClockTimer(int i, uint32 clocks)
{ {
int32 before = Timers[i].Counter;
int32 target = 0x10000;
bool zero_tm = false;
if(Timers[i].DoZeCounting <= 0) if(Timers[i].DoZeCounting <= 0)
clocks = 0; clocks = 0;
@ -170,7 +208,7 @@ static void ClockTimer(int i, uint32 clocks)
Timers[i].Div8Counter += clocks; Timers[i].Div8Counter += clocks;
d8_clocks = Timers[i].Div8Counter >> 3; d8_clocks = Timers[i].Div8Counter >> 3;
Timers[i].Div8Counter -= d8_clocks << 3; Timers[i].Div8Counter &= 0x7;
if(Timers[i].Mode & 0x200) // Divide by 8, at least for timer 0x2 if(Timers[i].Mode & 0x200) // Divide by 8, at least for timer 0x2
clocks = d8_clocks; clocks = d8_clocks;
@ -179,57 +217,35 @@ static void ClockTimer(int i, uint32 clocks)
clocks = 0; clocks = 0;
} }
if(Timers[i].Mode & 0x008) if((Timers[i].Mode & 0x008) && Timers[i].Target == 0 && Timers[i].Counter == 0)
target = Timers[i].Target; TimerMatch(i);
else if(clocks)
{
uint32 before = Timers[i].Counter;
if(target == 0 && Timers[i].Counter == 0)
zero_tm = true;
else
Timers[i].Counter += clocks; Timers[i].Counter += clocks;
if(clocks && (Timers[i].Mode & 0x40)) if(Timers[i].Mode & 0x40)
Timers[i].IRQDone = false; Timers[i].IRQDone = false;
if((before < target && Timers[i].Counter >= target) || zero_tm || Timers[i].Counter > 0xFFFF) bool irq_exact = false;
{
#if 1
if(Timers[i].Mode & 0x10)
{
if((Timers[i].Counter - target) > 3)
PSX_WARNING("Timer %d IRQ trigger error: %d", i, Timers[i].Counter - target);
}
#endif //
// Target match handling
//
if((before < Timers[i].Target && Timers[i].Counter >= Timers[i].Target) || (Timers[i].Counter >= Timers[i].Target + 0x10000))
irq_exact |= TimerMatch(i);
//
// Overflow handling
//
if(Timers[i].Counter >= 0x10000)
irq_exact |= TimerOverflow(i);
Timers[i].Mode |= 0x0800; //
if((Timers[i].Mode & 0x40) && !irq_exact)
if(Timers[i].Counter > 0xFFFF)
{
Timers[i].Counter -= 0x10000;
if(target == 0x10000)
Timers[i].Mode |= 0x1000;
if(!target)
Timers[i].Counter = 0;
}
if(target)
Timers[i].Counter -= (Timers[i].Counter / target) * target;
if((Timers[i].Mode & 0x10) && !Timers[i].IRQDone)
{
Timers[i].IRQDone = true;
IRQ_Assert(IRQ_TIMER_0 + i, true);
IRQ_Assert(IRQ_TIMER_0 + i, false);
}
if(Timers[i].Counter && (Timers[i].Mode & 0x40))
Timers[i].IRQDone = false; Timers[i].IRQDone = false;
} }
} }
void TIMER_SetVBlank(bool status) void TIMER_SetVBlank(bool status)
@ -242,13 +258,21 @@ void TIMER_SetVBlank(bool status)
case 0x3: case 0x3:
if(vblank && !status) if(vblank && !status)
{
Timers[1].Counter = 0; Timers[1].Counter = 0;
if(Timers[1].Counter == Timers[1].Target)
TimerMatch(1);
}
break; break;
case 0x5: case 0x5:
Timers[1].DoZeCounting = status; Timers[1].DoZeCounting = status;
if(vblank && !status) if(vblank && !status)
{
Timers[1].Counter = 0; Timers[1].Counter = 0;
if(Timers[1].Counter == Timers[1].Target)
TimerMatch(1);
}
break; break;
case 0x7: case 0x7:
@ -272,7 +296,12 @@ void TIMER_SetHRetrace(bool status)
if(hretrace && !status) if(hretrace && !status)
{ {
if((Timers[0].Mode & 0x7) == 0x3) if((Timers[0].Mode & 0x7) == 0x3)
{
Timers[0].Counter = 0; Timers[0].Counter = 0;
if(Timers[0].Counter == Timers[0].Target)
TimerMatch(0);
}
} }
hretrace = status; hretrace = status;
@ -306,7 +335,7 @@ pscpu_timestamp_t TIMER_Update(const pscpu_timestamp_t timestamp)
lastts = timestamp; lastts = timestamp;
return(timestamp + CalcNextEvent(1024)); return(timestamp + CalcNextEvent());
} }
static void CalcCountingStart(unsigned which) static void CalcCountingStart(unsigned which)
@ -349,40 +378,16 @@ void TIMER_Write(const pscpu_timestamp_t timestamp, uint32 A, uint16 V)
if(which >= 3) if(which >= 3)
return; return;
// TODO: See if the "Timers[which].Counter" part of the IRQ if() statements below is what a real PSX does.
switch(A & 0xC) switch(A & 0xC)
{ {
case 0x0: Timers[which].IRQDone = false; case 0x0: Timers[which].IRQDone = false;
#if 1
if(Timers[which].Counter && (V & 0xFFFF) == 0)
{
Timers[which].Mode |= 0x0800;
if((Timers[which].Mode & 0x10) && !Timers[which].IRQDone)
{
Timers[which].IRQDone = true;
IRQ_Assert(IRQ_TIMER_0 + which, true);
IRQ_Assert(IRQ_TIMER_0 + which, false);
}
}
#endif
Timers[which].Counter = V & 0xFFFF; Timers[which].Counter = V & 0xFFFF;
break; break;
case 0x4: Timers[which].Mode = (V & 0x3FF) | (Timers[which].Mode & 0x1C00); case 0x4: Timers[which].Mode = (V & 0x3FF) | (Timers[which].Mode & 0x1C00);
Timers[which].IRQDone = false; Timers[which].IRQDone = false;
#if 1
if(Timers[which].Counter)
{
Timers[which].Mode |= 0x0800;
if((Timers[which].Mode & 0x10) && !Timers[which].IRQDone)
{
Timers[which].IRQDone = true;
IRQ_Assert(IRQ_TIMER_0 + which, true);
IRQ_Assert(IRQ_TIMER_0 + which, false);
}
}
Timers[which].Counter = 0; Timers[which].Counter = 0;
#endif
CalcCountingStart(which); // Call after setting .Mode CalcCountingStart(which); // Call after setting .Mode
break; break;
@ -393,9 +398,10 @@ void TIMER_Write(const pscpu_timestamp_t timestamp, uint32 A, uint16 V)
break; break;
} }
// TIMER_Update(timestamp); if(Timers[which].Counter == Timers[which].Target)
TimerMatch(which);
PSX_SetEventNT(PSX_EVENT_TIMER, timestamp + CalcNextEvent(1024)); PSX_SetEventNT(PSX_EVENT_TIMER, timestamp + CalcNextEvent());
} }
uint16 TIMER_Read(const pscpu_timestamp_t timestamp, uint32 A) uint16 TIMER_Read(const pscpu_timestamp_t timestamp, uint32 A)
@ -418,7 +424,9 @@ uint16 TIMER_Read(const pscpu_timestamp_t timestamp, uint32 A)
break; break;
case 0x4: ret = Timers[which].Mode; case 0x4: ret = Timers[which].Mode;
Timers[which].Mode &= ~0x1800; Timers[which].Mode &= ~0x1000;
if(Timers[which].Counter != Timers[which].Target)
Timers[which].Mode &= ~0x0800;
break; break;
case 0x8: ret = Timers[which].Target; case 0x8: ret = Timers[which].Target;
@ -496,6 +504,9 @@ void TIMER_SetRegister(unsigned int which, uint32 value)
break; break;
} }
if (Timers[tw].Counter == Timers[tw].Target)
TimerMatch(tw);
} }