update psx core to 0.9.38.7
This commit is contained in:
parent
29767ba522
commit
5cfc1dae2a
Binary file not shown.
|
@ -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
|
@ -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,218 +45,217 @@ 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;
|
||||||
~PS_CPU() MDFN_COLD;
|
~PS_CPU() MDFN_COLD;
|
||||||
|
|
||||||
template<bool isReader>void SyncState(EW::NewState *ns);
|
template<bool isReader>void SyncState(EW::NewState *ns);
|
||||||
|
|
||||||
// FAST_MAP_* enums are in BYTES(8-bit), not in 32-bit units("words" in MIPS context), but the sizes
|
// FAST_MAP_* enums are in BYTES(8-bit), not in 32-bit units("words" in MIPS context), but the sizes
|
||||||
// will always be multiples of 4.
|
// will always be multiples of 4.
|
||||||
enum { FAST_MAP_SHIFT = 16 };
|
enum { FAST_MAP_SHIFT = 16 };
|
||||||
enum { FAST_MAP_PSIZE = 1 << FAST_MAP_SHIFT };
|
enum { FAST_MAP_PSIZE = 1 << FAST_MAP_SHIFT };
|
||||||
|
|
||||||
void SetFastMap(void *region_mem, uint32 region_address, uint32 region_size);
|
void SetFastMap(void *region_mem, uint32 region_address, uint32 region_size);
|
||||||
|
|
||||||
INLINE void SetEventNT(const pscpu_timestamp_t next_event_ts_arg)
|
INLINE void SetEventNT(const pscpu_timestamp_t next_event_ts_arg)
|
||||||
{
|
{
|
||||||
next_event_ts = next_event_ts_arg;
|
next_event_ts = next_event_ts_arg;
|
||||||
}
|
}
|
||||||
|
|
||||||
pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode);
|
pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode);
|
||||||
|
|
||||||
void Power(void) MDFN_COLD;
|
void Power(void) MDFN_COLD;
|
||||||
|
|
||||||
// which ranges 0-5, inclusive
|
// which ranges 0-5, inclusive
|
||||||
void AssertIRQ(unsigned which, bool asserted);
|
void AssertIRQ(unsigned which, bool asserted);
|
||||||
|
|
||||||
void SetHalt(bool status);
|
void SetHalt(bool status);
|
||||||
|
|
||||||
// TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed.
|
// TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed.
|
||||||
void SetBIU(uint32 val);
|
void SetBIU(uint32 val);
|
||||||
uint32 GetBIU(void);
|
uint32 GetBIU(void);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
uint32 GPR[32 + 1]; // GPR[32] Used as dummy in load delay simulation(indexing past the end of real GPR)
|
uint32 GPR[32 + 1]; // GPR[32] Used as dummy in load delay simulation(indexing past the end of real GPR)
|
||||||
|
|
||||||
uint32 LO;
|
uint32 LO;
|
||||||
uint32 HI;
|
uint32 HI;
|
||||||
|
|
||||||
|
|
||||||
uint32 BACKED_PC;
|
uint32 BACKED_PC;
|
||||||
uint32 BACKED_new_PC;
|
uint32 BACKED_new_PC;
|
||||||
uint32 BACKED_new_PC_mask;
|
uint32 BACKED_new_PC_mask;
|
||||||
|
|
||||||
uint32 IPCache;
|
uint32 IPCache;
|
||||||
void RecalcIPCache(void);
|
void RecalcIPCache(void);
|
||||||
bool Halted;
|
bool Halted;
|
||||||
|
|
||||||
uint32 BACKED_LDWhich;
|
uint32 BACKED_LDWhich;
|
||||||
uint32 BACKED_LDValue;
|
uint32 BACKED_LDValue;
|
||||||
uint32 LDAbsorb;
|
uint32 LDAbsorb;
|
||||||
|
|
||||||
pscpu_timestamp_t next_event_ts;
|
pscpu_timestamp_t next_event_ts;
|
||||||
pscpu_timestamp_t gte_ts_done;
|
pscpu_timestamp_t gte_ts_done;
|
||||||
pscpu_timestamp_t muldiv_ts_done;
|
pscpu_timestamp_t muldiv_ts_done;
|
||||||
|
|
||||||
uint32 BIU;
|
uint32 BIU;
|
||||||
|
|
||||||
struct __ICache
|
struct __ICache
|
||||||
{
|
{
|
||||||
uint32 TV;
|
uint32 TV;
|
||||||
uint32 Data;
|
uint32 Data;
|
||||||
};
|
};
|
||||||
|
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
__ICache ICache[1024];
|
__ICache ICache[1024];
|
||||||
uint32 ICache_Bulk[2048];
|
uint32 ICache_Bulk[2048];
|
||||||
};
|
};
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
CP0REG_BPC = 3, // PC breakpoint address.
|
CP0REG_BPC = 3, // PC breakpoint address.
|
||||||
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_BDAM = 9, // Data load/store address mask.
|
CP0REG_BADVA = 8,
|
||||||
CP0REG_BPCM = 11, // PC breakpoint address mask.
|
CP0REG_BDAM = 9, // Data load/store address mask.
|
||||||
CP0REG_SR = 12,
|
CP0REG_BPCM = 11, // PC breakpoint address mask.
|
||||||
CP0REG_CAUSE = 13,
|
CP0REG_SR = 12,
|
||||||
CP0REG_EPC = 14,
|
CP0REG_CAUSE = 13,
|
||||||
CP0REG_PRID = 15, // Product ID
|
CP0REG_EPC = 14,
|
||||||
CP0REG_ERREG = 16
|
CP0REG_PRID = 15 // Product ID
|
||||||
};
|
};
|
||||||
|
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
union
|
union
|
||||||
{
|
{
|
||||||
uint32 Regs[32];
|
uint32 Regs[32];
|
||||||
struct
|
struct
|
||||||
{
|
{
|
||||||
uint32 Unused00;
|
uint32 Unused00;
|
||||||
uint32 Unused01;
|
uint32 Unused01;
|
||||||
uint32 Unused02;
|
uint32 Unused02;
|
||||||
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
|
||||||
uint32 SR; // R/W
|
uint32 SR; // R/W
|
||||||
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;
|
|
||||||
|
|
||||||
#if 1
|
#if 1
|
||||||
//uint32 WrAbsorb;
|
//uint32 WrAbsorb;
|
||||||
//uint8 WrAbsorbShift;
|
//uint8 WrAbsorbShift;
|
||||||
|
|
||||||
// On read:
|
// On read:
|
||||||
//WrAbsorb = 0;
|
//WrAbsorb = 0;
|
||||||
//WrAbsorbShift = 0;
|
//WrAbsorbShift = 0;
|
||||||
|
|
||||||
// On write:
|
// On write:
|
||||||
//WrAbsorb >>= (WrAbsorbShift >> 2) & 8;
|
//WrAbsorb >>= (WrAbsorbShift >> 2) & 8;
|
||||||
//WrAbsorbShift -= (WrAbsorbShift >> 2) & 8;
|
//WrAbsorbShift -= (WrAbsorbShift >> 2) & 8;
|
||||||
|
|
||||||
//WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift;
|
//WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift;
|
||||||
//WrAbsorbShift += 8;
|
//WrAbsorbShift += 8;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
uint8 ReadAbsorb[0x20 + 1];
|
uint8 ReadAbsorb[0x20 + 1];
|
||||||
uint8 ReadAbsorbWhich;
|
uint8 ReadAbsorbWhich;
|
||||||
uint8 ReadFudge;
|
uint8 ReadFudge;
|
||||||
|
|
||||||
//uint32 WriteAbsorb;
|
//uint32 WriteAbsorb;
|
||||||
//uint8 WriteAbsorbCount;
|
//uint8 WriteAbsorbCount;
|
||||||
//uint8 WriteAbsorbMonkey;
|
//uint8 WriteAbsorbMonkey;
|
||||||
uint8 MULT_Tab24[24];
|
uint8 MULT_Tab24[24];
|
||||||
|
|
||||||
MultiAccessSizeMem<1024, false> ScratchRAM;
|
MultiAccessSizeMem<1024, false> ScratchRAM;
|
||||||
|
|
||||||
//PS_GTE GTE;
|
//PS_GTE GTE;
|
||||||
|
|
||||||
uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)];
|
uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)];
|
||||||
uint8 DummyPage[FAST_MAP_PSIZE];
|
uint8 DummyPage[FAST_MAP_PSIZE];
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
EXCEPTION_INT = 0,
|
EXCEPTION_INT = 0,
|
||||||
EXCEPTION_MOD = 1,
|
EXCEPTION_MOD = 1,
|
||||||
EXCEPTION_TLBL = 2,
|
EXCEPTION_TLBL = 2,
|
||||||
EXCEPTION_TLBS = 3,
|
EXCEPTION_TLBS = 3,
|
||||||
EXCEPTION_ADEL = 4, // Address error on load
|
EXCEPTION_ADEL = 4, // Address error on load
|
||||||
EXCEPTION_ADES = 5, // Address error on store
|
EXCEPTION_ADES = 5, // Address error on store
|
||||||
EXCEPTION_IBE = 6, // Instruction bus error
|
EXCEPTION_IBE = 6, // Instruction bus error
|
||||||
EXCEPTION_DBE = 7, // Data bus error
|
EXCEPTION_DBE = 7, // Data bus error
|
||||||
EXCEPTION_SYSCALL = 8, // System call
|
EXCEPTION_SYSCALL = 8, // System call
|
||||||
EXCEPTION_BP = 9, // Breakpoint
|
EXCEPTION_BP = 9, // Breakpoint
|
||||||
EXCEPTION_RI = 10, // Reserved instruction
|
EXCEPTION_RI = 10, // Reserved instruction
|
||||||
EXCEPTION_COPU = 11, // Coprocessor unusable
|
EXCEPTION_COPU = 11, // Coprocessor unusable
|
||||||
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;
|
||||||
|
|
||||||
template<typename T> T PeekMemory(uint32 address) MDFN_COLD;
|
template<typename T> T PeekMemory(uint32 address) MDFN_COLD;
|
||||||
template<typename T> void PokeMemory(uint32 address, T value) MDFN_COLD;
|
template<typename T> void PokeMemory(uint32 address, T value) MDFN_COLD;
|
||||||
template<typename T> T ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24 = false, bool LWC_timing = false);
|
template<typename T> T ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24 = false, bool LWC_timing = false);
|
||||||
template<typename T> void WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24 = false);
|
template<typename T> void WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24 = false);
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// 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; }
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
GSREG_GPR = 0,
|
GSREG_GPR = 0,
|
||||||
GSREG_PC = 32,
|
GSREG_PC = 32,
|
||||||
GSREG_PC_NEXT,
|
GSREG_PC_NEXT,
|
||||||
GSREG_IN_BD_SLOT,
|
GSREG_IN_BD_SLOT,
|
||||||
GSREG_LO,
|
GSREG_LO,
|
||||||
GSREG_HI,
|
GSREG_HI,
|
||||||
GSREG_SR,
|
GSREG_SR,
|
||||||
GSREG_CAUSE,
|
GSREG_CAUSE,
|
||||||
GSREG_EPC,
|
GSREG_EPC,
|
||||||
};
|
};
|
||||||
|
|
||||||
uint32 GetRegister(unsigned int which, char *special, const uint32 special_len);
|
uint32 GetRegister(unsigned int which, char *special, const uint32 special_len);
|
||||||
void SetRegister(unsigned int which, uint32 value);
|
void SetRegister(unsigned int which, uint32 value);
|
||||||
bool PeekCheckICache(uint32 PC, uint32 *iw);
|
bool PeekCheckICache(uint32 PC, uint32 *iw);
|
||||||
|
|
||||||
uint8 PeekMem8(uint32 A);
|
uint8 PeekMem8(uint32 A);
|
||||||
uint16 PeekMem16(uint32 A);
|
uint16 PeekMem16(uint32 A);
|
||||||
uint32 PeekMem32(uint32 A);
|
uint32 PeekMem32(uint32 A);
|
||||||
|
|
||||||
void PokeMem8(uint32 A, uint8 V);
|
void PokeMem8(uint32 A, uint8 V);
|
||||||
void PokeMem16(uint32 A, uint16 V);
|
void PokeMem16(uint32 A, uint16 V);
|
||||||
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);
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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] =
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -31,23 +31,22 @@
|
||||||
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
|
||||||
Divide by 8(timer 2 only?) 0x200
|
Divide by 8(timer 2 only?) 0x200
|
||||||
|
|
||||||
Counter:
|
Counter:
|
||||||
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,48 +111,94 @@ 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;
|
||||||
}
|
|
||||||
|
|
||||||
{
|
if(Timers[i].DoZeCounting <= 0)
|
||||||
int32 tmp_clocks;
|
continue;
|
||||||
|
|
||||||
if(Timers[i].DoZeCounting <= 0)
|
if((i == 0x2) && (Timers[i].Mode & 0x1))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if((i == 0x2) && (Timers[i].Mode & 0x1))
|
//
|
||||||
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);
|
else
|
||||||
tmp_clocks = ((count_delta - 1) * 8) + (8 - Timers[i].Div8Counter);
|
tmp_clocks = count_delta;
|
||||||
}
|
|
||||||
else
|
|
||||||
tmp_clocks = count_delta;
|
|
||||||
|
|
||||||
assert(tmp_clocks > 0);
|
if(next_event > tmp_clocks)
|
||||||
|
next_event = tmp_clocks;
|
||||||
if(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);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue