diff --git a/psx/octoshock/psx/cdc.cpp b/psx/octoshock/psx/cdc.cpp index c50f535c2f..5682444769 100644 --- a/psx/octoshock/psx/cdc.cpp +++ b/psx/octoshock/psx/cdc.cpp @@ -952,14 +952,12 @@ void PS_CDC::HandlePlayRead(void) { uint8 tr[8]; //zero 14-jun-2016 - useful after all for fixing bugs in "Fantastic Pinball Kyutenkai" -#if 1 uint16 abs_lev_max = 0; bool abs_lev_chselect = SubQBuf_Safe[0x8] & 0x01; for(int i = 0; i < 588; i++) abs_lev_max = std::max(abs_lev_max, std::min(abs((int16)MDFN_de16lsb(&read_buf[i * 4 + (abs_lev_chselect * 2)])), 32767)); abs_lev_max |= abs_lev_chselect << 15; -#endif ReportLastF = SubQBuf_Safe[0x9] >> 4; @@ -980,9 +978,6 @@ void PS_CDC::HandlePlayRead(void) tr[5] = SubQBuf_Safe[0x9]; // A F } - //zero 14-jun-2016 - useful after all for fixing bugs in "Fantastic Pinball Kyutenkai" - //tr[6] = 0; //abs_lev_max >> 0; - //tr[7] = 0; //abs_lev_max >> 8; tr[6] = abs_lev_max >> 0; tr[7] = abs_lev_max >> 8; diff --git a/psx/octoshock/psx/cdc.h b/psx/octoshock/psx/cdc.h index 99489fa0a5..9aab9816ef 100644 --- a/psx/octoshock/psx/cdc.h +++ b/psx/octoshock/psx/cdc.h @@ -22,8 +22,8 @@ class PS_CDC { public: - PS_CDC(); - ~PS_CDC(); + PS_CDC() MDFN_COLD; + ~PS_CDC() MDFN_COLD; templatevoid SyncState(EW::NewState *ns); @@ -31,7 +31,7 @@ class PS_CDC void SetDisc(ShockDiscRef *disc, const char disc_id[4], bool poke); void CloseTray(bool poke); - void Power(void); + void Power(void) MDFN_COLD; void ResetTS(void); int32 CalcNextEvent(void); // Returns in master cycles to next event. diff --git a/psx/octoshock/psx/cpu.cpp b/psx/octoshock/psx/cpu.cpp index 12b1b83917..65f40f3251 100644 --- a/psx/octoshock/psx/cpu.cpp +++ b/psx/octoshock/psx/cpu.cpp @@ -1,232 +1,234 @@ -/* Mednafen - Multi-system Emulator - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA - */ - -#include -#include "psx.h" -#include "cpu.h" -#include "math_ops.h" - +/******************************************************************************/ +/* Mednafen Sony PS1 Emulation Module */ +/******************************************************************************/ +/* cpu.cpp: +** Copyright (C) 2011-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + +#include +#include "psx.h" +#include "cpu.h" +#include "math_ops.h" + #if 0 -#define EXP_ILL_CHECK(n) {n;} + #define EXP_ILL_CHECK(n) {n;} #else -#define EXP_ILL_CHECK(n) {} -#endif - -//not very organized, is it -void* g_ShockTraceCallbackOpaque = NULL; -ShockCallback_Trace g_ShockTraceCallback = NULL; + #define EXP_ILL_CHECK(n) {} +#endif + +//not very organized, is it +void* g_ShockTraceCallbackOpaque = NULL; +ShockCallback_Trace g_ShockTraceCallback = NULL; ShockCallback_Mem g_ShockMemCallback; -eShockMemCb g_ShockMemCbType; -char disasm_buf[128]; - -/* TODO - Make sure load delays are correct. - - Consider preventing IRQs being taken while in a branch delay slot, to prevent potential problems with games that like to be too clever and perform - un-restartable sequences of instructions. -*/ - -#define BIU_ENABLE_ICACHE_S1 0x00000800 // Enable I-cache, set 1 -#define BIU_ENABLE_DCACHE 0x00000080 // Enable D-cache -#define BIU_TAG_TEST_MODE 0x00000004 // Enable TAG test mode(IsC must be set to 1 as well presumably?) -#define BIU_INVALIDATE_MODE 0x00000002 // Enable Invalidate mode(IsC must be set to 1 as well presumably?) -#define BIU_LOCK 0x00000001 // Enable Lock mode(IsC must be set to 1 as well presumably?) - // Does lock mode prevent the actual data payload from being modified, while allowing tags to be modified/updated??? - -namespace MDFN_IEN_PSX -{ - - -PS_CPU::PS_CPU() -{ - Halted = false; - - memset(FastMap, 0, sizeof(FastMap)); - memset(DummyPage, 0xFF, sizeof(DummyPage)); // 0xFF to trigger an illegal instruction exception, so we'll know what's up when debugging. - - for(uint64 a = 0x00000000; a < (1ULL << 32); a += FAST_MAP_PSIZE) - SetFastMap(DummyPage, a, FAST_MAP_PSIZE); - - CPUHook = NULL; - ADDBT = NULL; - - GTE_Init(); - - for(unsigned i = 0; i < 24; i++) - { - uint8 v = 7; - - if(i < 12) - v += 4; - - if(i < 21) - v += 3; - - MULT_Tab24[i] = v; - } -} - -PS_CPU::~PS_CPU() -{ - - -} - -void PS_CPU::SetFastMap(void *region_mem, uint32 region_address, uint32 region_size) -{ - // FAST_MAP_SHIFT - // FAST_MAP_PSIZE - - for(uint64 A = region_address; A < (uint64)region_address + region_size; A += FAST_MAP_PSIZE) - { - FastMap[A >> FAST_MAP_SHIFT] = ((uint8 *)region_mem - region_address); - } -} - -INLINE void PS_CPU::RecalcIPCache(void) -{ - IPCache = 0; - - if((CP0.SR & CP0.CAUSE & 0xFF00) && (CP0.SR & 1)) - IPCache = 0x80; - - if(Halted) - IPCache = 0x80; -} - -void PS_CPU::SetHalt(bool status) -{ - Halted = status; - RecalcIPCache(); -} - -void PS_CPU::Power(void) -{ - assert(sizeof(ICache) == sizeof(ICache_Bulk)); - - memset(GPR, 0, sizeof(GPR)); - memset(&CP0, 0, sizeof(CP0)); - LO = 0; - HI = 0; - - gte_ts_done = 0; - muldiv_ts_done = 0; - - BACKED_PC = 0xBFC00000; - BACKED_new_PC = 4; - BACKED_new_PC_mask = ~0U; - - BACKED_LDWhich = 0x20; - BACKED_LDValue = 0; - LDAbsorb = 0; - memset(ReadAbsorb, 0, sizeof(ReadAbsorb)); - ReadAbsorbWhich = 0; - ReadFudge = 0; - - //WriteAbsorb = 0; - //WriteAbsorbCount = 0; - //WriteAbsorbMonkey = 0; - - CP0.SR |= (1 << 22); // BEV - CP0.SR |= (1 << 21); // TS - - CP0.PRID = 0x2; - - RecalcIPCache(); - - - BIU = 0; - - memset(ScratchRAM.data8, 0, 1024); - - // Not quite sure about these poweron/reset values: - for(unsigned i = 0; i < 1024; i++) - { - ICache[i].TV = 0x2 | ((BIU & 0x800) ? 0x0 : 0x1); - ICache[i].Data = 0; - } - - GTE_Power(); -} - -void PS_CPU::AssertIRQ(unsigned which, bool asserted) -{ - assert(which <= 5); - - CP0.CAUSE &= ~(1 << (10 + which)); - - if(asserted) - CP0.CAUSE |= 1 << (10 + which); - - RecalcIPCache(); -} - -void PS_CPU::SetBIU(uint32 val) -{ - const uint32 old_BIU = BIU; - - BIU = val & ~(0x440); - - if((BIU ^ old_BIU) & 0x800) - { - if(BIU & 0x800) // ICache enabled - { - for(unsigned i = 0; i < 1024; i++) - ICache[i].TV &= ~0x1; - } - else // ICache disabled - { - for(unsigned i = 0; i < 1024; i++) - ICache[i].TV |= 0x1; - } - } - - PSX_DBG(PSX_DBG_SPARSE, "[CPU] Set BIU=0x%08x\n", BIU); -} - -uint32 PS_CPU::GetBIU(void) -{ - return BIU; -} - -static const uint32 addr_mask[8] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x7FFFFFFF, 0x1FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - -template -INLINE T PS_CPU::PeekMemory(uint32 address) -{ - T ret; - address &= addr_mask[address >> 29]; - - if(address >= 0x1F800000 && address <= 0x1F8003FF) - return ScratchRAM.Read(address & 0x3FF); - - //assert(!(CP0.SR & 0x10000)); - - if(sizeof(T) == 1) - ret = PSX_MemPeek8(address); - else if(sizeof(T) == 2) - ret = PSX_MemPeek16(address); - else - ret = PSX_MemPeek32(address); - - return(ret); -} - +eShockMemCb g_ShockMemCbType; +char disasm_buf[128]; + +/* TODO + Make sure load delays are correct. + + Consider preventing IRQs being taken while in a branch delay slot, to prevent potential problems with games that like to be too clever and perform + un-restartable sequences of instructions. +*/ + +#define BIU_ENABLE_ICACHE_S1 0x00000800 // Enable I-cache, set 1 +#define BIU_ICACHE_FSIZE_MASK 0x00000300 // I-cache fill size mask; 0x000 = 2 words, 0x100 = 4 words, 0x200 = 8 words, 0x300 = 16 words +#define BIU_ENABLE_DCACHE 0x00000080 // Enable D-cache +#define BIU_DCACHE_SCRATCHPAD 0x00000008 // Enable scratchpad RAM mode of D-cache +#define BIU_TAG_TEST_MODE 0x00000004 // Enable TAG test mode(IsC must be set to 1 as well presumably?) +#define BIU_INVALIDATE_MODE 0x00000002 // Enable Invalidate mode(IsC must be set to 1 as well presumably?) +#define BIU_LOCK_MODE 0x00000001 // Enable Lock mode(IsC must be set to 1 as well presumably?) + + + +namespace MDFN_IEN_PSX +{ + + +PS_CPU::PS_CPU() +{ + //printf("%zu\n", (size_t)((uintptr_t)ICache - (uintptr_t)this)); + + Halted = false; + + memset(FastMap, 0, sizeof(FastMap)); + memset(DummyPage, 0xFF, sizeof(DummyPage)); // 0xFF to trigger an illegal instruction exception, so we'll know what's up when debugging. + + for(uint64 a = 0x00000000; a < (1ULL << 32); a += FAST_MAP_PSIZE) + SetFastMap(DummyPage, a, FAST_MAP_PSIZE); + + CPUHook = NULL; + ADDBT = NULL; + + GTE_Init(); + + for(unsigned i = 0; i < 24; i++) + { + uint8 v = 7; + + if(i < 12) + v += 4; + + if(i < 21) + v += 3; + + MULT_Tab24[i] = v; + } +} + +PS_CPU::~PS_CPU() +{ + + +} + +void PS_CPU::SetFastMap(void *region_mem, uint32 region_address, uint32 region_size) +{ + // FAST_MAP_SHIFT + // FAST_MAP_PSIZE + + for(uint64 A = region_address; A < (uint64)region_address + region_size; A += FAST_MAP_PSIZE) + { + FastMap[A >> FAST_MAP_SHIFT] = ((uint8 *)region_mem - region_address); + } +} + +INLINE void PS_CPU::RecalcIPCache(void) +{ + IPCache = 0; + + if((CP0.SR & CP0.CAUSE & 0xFF00) && (CP0.SR & 1)) + IPCache = 0x80; + + if(Halted) + IPCache = 0x80; +} + +void PS_CPU::SetHalt(bool status) +{ + Halted = status; + RecalcIPCache(); +} + +void PS_CPU::Power(void) +{ + assert(sizeof(ICache) == sizeof(ICache_Bulk)); + + memset(GPR, 0, sizeof(GPR)); + memset(&CP0, 0, sizeof(CP0)); + LO = 0; + HI = 0; + + gte_ts_done = 0; + muldiv_ts_done = 0; + + BACKED_PC = 0xBFC00000; + BACKED_new_PC = BACKED_PC + 4; + BDBT = 0; + + BACKED_LDWhich = 0x20; + BACKED_LDValue = 0; + LDAbsorb = 0; + memset(ReadAbsorb, 0, sizeof(ReadAbsorb)); + ReadAbsorbWhich = 0; + ReadFudge = 0; + + CP0.SR |= (1 << 22); // BEV + CP0.SR |= (1 << 21); // TS + + CP0.PRID = 0x2; + + RecalcIPCache(); + + + BIU = 0; + + memset(ScratchRAM.data8, 0, 1024); + + // Not quite sure about these poweron/reset values: + for(unsigned i = 0; i < 1024; i++) + { + ICache[i].TV = 0x2 | ((BIU & 0x800) ? 0x0 : 0x1); + ICache[i].Data = 0; + } + + GTE_Power(); +} + +void PS_CPU::AssertIRQ(unsigned which, bool asserted) +{ + assert(which <= 5); + + CP0.CAUSE &= ~(1 << (10 + which)); + + if(asserted) + CP0.CAUSE |= 1 << (10 + which); + + RecalcIPCache(); +} + +void PS_CPU::SetBIU(uint32 val) +{ + const uint32 old_BIU = BIU; + + BIU = val & ~(0x440); + + if((BIU ^ old_BIU) & 0x800) + { + if(BIU & 0x800) // ICache enabled + { + for(unsigned i = 0; i < 1024; i++) + ICache[i].TV &= ~0x1; + } + else // ICache disabled + { + for(unsigned i = 0; i < 1024; i++) + ICache[i].TV |= 0x1; + } + } + + PSX_DBG(PSX_DBG_SPARSE, "[CPU] Set BIU=0x%08x\n", BIU); +} + +uint32 PS_CPU::GetBIU(void) +{ + return BIU; +} + +template +INLINE T PS_CPU::PeekMemory(uint32 address) +{ + T ret; + address &= addr_mask[address >> 29]; + + if(address >= 0x1F800000 && address <= 0x1F8003FF) + return ScratchRAM.Read(address & 0x3FF); + + //assert(!(CP0.SR & 0x10000)); + + if(sizeof(T) == 1) + ret = PSX_MemPeek8(address); + else if(sizeof(T) == 2) + ret = PSX_MemPeek16(address); + else + ret = PSX_MemPeek32(address); + + return(ret); +} + template void PS_CPU::PokeMemory(uint32 address, T value) { @@ -242,144 +244,240 @@ void PS_CPU::PokeMemory(uint32 address, T value) else PSX_MemPoke32(address, value); } - -template -INLINE T PS_CPU::ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24, bool LWC_timing) -{ - T ret; - - //WriteAbsorb >>= WriteAbsorbMonkey * 8; - //WriteAbsorbCount -= WriteAbsorbMonkey; - //WriteAbsorbMonkey = WriteAbsorbCount; - - ReadAbsorb[ReadAbsorbWhich] = 0; - ReadAbsorbWhich = 0; - + +template +INLINE T PS_CPU::ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24, bool LWC_timing) +{ + T ret; + + ReadAbsorb[ReadAbsorbWhich] = 0; + ReadAbsorbWhich = 0; + if (g_ShockMemCallback && (g_ShockMemCbType & eShockMemCb_Read)) g_ShockMemCallback(address, eShockMemCb_Read, DS24 ? 24 : 32, 0); - - address &= addr_mask[address >> 29]; - - if(address >= 0x1F800000 && address <= 0x1F8003FF) - { - LDAbsorb = 0; - - if(DS24) - return ScratchRAM.ReadU24(address & 0x3FF); - else - return ScratchRAM.Read(address & 0x3FF); - } - - timestamp += (ReadFudge >> 4) & 2; - - //assert(!(CP0.SR & 0x10000)); - - pscpu_timestamp_t lts = timestamp; - - if(sizeof(T) == 1) - ret = PSX_MemRead8(lts, address); - else if(sizeof(T) == 2) - ret = PSX_MemRead16(lts, address); - else - { - if(DS24) - ret = PSX_MemRead24(lts, address) & 0xFFFFFF; - else - ret = PSX_MemRead32(lts, address); - } - - if(LWC_timing) - lts += 1; - else - lts += 2; - - LDAbsorb = (lts - timestamp); - timestamp = lts; - - return(ret); -} - -template -INLINE void PS_CPU::WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24) -{ - if (g_ShockMemCallback && (g_ShockMemCbType & eShockMemCb_Write)) - g_ShockMemCallback(address, eShockMemCb_Write, DS24 ? 24 : 32, value); - - if(MDFN_LIKELY(!(CP0.SR & 0x10000))) - { - address &= addr_mask[address >> 29]; - - if(address >= 0x1F800000 && address <= 0x1F8003FF) - { - if(DS24) - ScratchRAM.WriteU24(address & 0x3FF, value); - else - ScratchRAM.Write(address & 0x3FF, value); - - return; - } - - //if(WriteAbsorbCount == 4) - //{ - // WriteAbsorb >>= 8; - // WriteAbsorbCount--; - // - // if(WriteAbsorbMonkey) - // WriteAbsorbMonkey--; - //} - //timestamp += 3; - //WriteAbsorb |= (3U << (WriteAbsorbCount * 8)); - //WriteAbsorbCount++; - - if(sizeof(T) == 1) - PSX_MemWrite8(timestamp, address, value); - else if(sizeof(T) == 2) - PSX_MemWrite16(timestamp, address, value); - else - { - if(DS24) - PSX_MemWrite24(timestamp, address, value); - else - PSX_MemWrite32(timestamp, address, value); - } - } - else - { - if(BIU & 0x800) // Instruction cache is enabled/active - { - if(BIU & 0x4) // TAG test mode. - { - // TODO: Respect written value. - __ICache *ICI = &ICache[((address & 0xFF0) >> 2)]; - const uint8 valid_bits = 0x00; - - ICI[0].TV = ((valid_bits & 0x01) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); - ICI[1].TV = ((valid_bits & 0x02) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); - ICI[2].TV = ((valid_bits & 0x04) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); - ICI[3].TV = ((valid_bits & 0x08) ? 0x00 : 0x02) | ((BIU & 0x800) ? 0x0 : 0x1); - } - else if(!(BIU & 0x1)) - { - ICache[(address & 0xFFC) >> 2].Data = value << ((address & 0x3) * 8); - } - } - - if((BIU & 0x081) == 0x080) // Writes to the scratchpad(TODO test) - { - if(DS24) - ScratchRAM.WriteU24(address & 0x3FF, value); - else - ScratchRAM.Write(address & 0x3FF, value); - } - //printf("IsC WRITE%d 0x%08x 0x%08x -- CP0.SR=0x%08x\n", (int)sizeof(T), address, value, CP0.SR); - } -} - +#if 0 + if(MDFN_UNLIKELY(CP0.SR & 0x10000)) + { + uint32 tmp = 0; // TODO(someday): = open bus -uint32 NO_INLINE PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 NPM, const uint32 instr) + LDAbsorb = 0; + + if(BIU & (BIU_TAG_TEST_MODE | BIU_INVALIDATE_MODE | BIU_LOCK_MODE)) + { + if(BIU & BIU_TAG_TEST_MODE) + { + const __ICache* const ICI = &ICache[((address & 0xFF0) >> 2)]; + + tmp &= ~0x3F; // bits 0-3 validity, bit 4 tag compare match, bit 5 forced to 0(lock bit apparently unimplemented in the PS1). + + // + // Get validity bits. + // + for(unsigned i = 0; i < 4; i++) + tmp |= (!(ICI[i].TV & 0x02)) << i; + + // + // Tag compare. + // + if(!((address ^ ICI[0].TV) & 0xFFFFF000)) + tmp |= 0x10; + } + } + else + { + tmp = ICache[(address & 0xFFC) >> 2].Data; + } + + return tmp >> ((address & 0x3) * 8); + } +#endif + // + // + // + + address &= addr_mask[address >> 29]; + + if(address >= 0x1F800000 && address <= 0x1F8003FF) + { + LDAbsorb = 0; + + if(DS24) + return ScratchRAM.ReadU24(address & 0x3FF); + else + return ScratchRAM.Read(address & 0x3FF); + } + + timestamp += (ReadFudge >> 4) & 2; + + //assert(!(CP0.SR & 0x10000)); + + pscpu_timestamp_t lts = timestamp; + + if(sizeof(T) == 1) + ret = PSX_MemRead8(lts, address); + else if(sizeof(T) == 2) + ret = PSX_MemRead16(lts, address); + else + { + if(DS24) + ret = PSX_MemRead24(lts, address) & 0xFFFFFF; + else + ret = PSX_MemRead32(lts, address); + } + + if(LWC_timing) + lts += 1; + else + lts += 2; + + LDAbsorb = (lts - timestamp); + timestamp = lts; + + return(ret); +} + +template +INLINE void PS_CPU::WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24) +{ + if (g_ShockMemCallback && (g_ShockMemCbType & eShockMemCb_Write)) + g_ShockMemCallback(address, eShockMemCb_Write, DS24 ? 24 : 32, value); + + if(MDFN_LIKELY(!(CP0.SR & 0x10000))) + { + address &= addr_mask[address >> 29]; + + if(address >= 0x1F800000 && address <= 0x1F8003FF) + { + if(DS24) + ScratchRAM.WriteU24(address & 0x3FF, value); + else + ScratchRAM.Write(address & 0x3FF, value); + + return; + } + + if(sizeof(T) == 1) + PSX_MemWrite8(timestamp, address, value); + else if(sizeof(T) == 2) + PSX_MemWrite16(timestamp, address, value); + else + { + if(DS24) + PSX_MemWrite24(timestamp, address, value); + else + PSX_MemWrite32(timestamp, address, value); + } + } + else + { + if(BIU & BIU_ENABLE_ICACHE_S1) // Instruction cache is enabled/active + { + if(BIU & (BIU_TAG_TEST_MODE | BIU_INVALIDATE_MODE | BIU_LOCK_MODE)) + { + const uint8 valid_bits = (BIU & BIU_TAG_TEST_MODE) ? ((value << ((address & 0x3) * 8)) & 0x0F) : 0x00; + __ICache* const ICI = &ICache[((address & 0xFF0) >> 2)]; + + // + // Set validity bits and tag. + // + for(unsigned i = 0; i < 4; i++) + ICI[i].TV = ((valid_bits & (1U << i)) ? 0x00 : 0x02) | (address & 0xFFFFFFF0) | (i << 2); + } + else + { + ICache[(address & 0xFFC) >> 2].Data = value << ((address & 0x3) * 8); + } + } + + if((BIU & 0x081) == 0x080) // Writes to the scratchpad(TODO test) + { + if(DS24) + ScratchRAM.WriteU24(address & 0x3FF, value); + else + ScratchRAM.Write(address & 0x3FF, value); + } + //printf("IsC WRITE%d 0x%08x 0x%08x -- CP0.SR=0x%08x\n", (int)sizeof(T), address, value, CP0.SR); + } +} + +// +// ICache emulation here is not very accurate. More accurate emulation had about a 6% performance penalty for simple +// code that just looped infinitely, with no tangible known benefit for commercially-released games. +// +// We do emulate the tag test mode functionality in regards to loading custom tag, valid bits, and instruction word data, as it could +// hypothetically be useful for homebrew. However, due to inaccuracies, it's possible to load a tag for an address in the non-cached +// 0xA0000000-0xBFFFFFFF range, jump to the address, and have it execute out of instruction cache, which is wrong and not possible on a PS1. +// +// The other major inaccuracy here is how the region 0x80000000-0x9FFFFFFF is handled. On a PS1, when fetching instruction word data +// from this region, the upper bit is forced to 0 before the tag compare(though the tag compare IS a full 20 bit compare), +// and this address with the upper bit set to 0 is also written to the tag on cache miss. We don't do the address masking here, +// so in addition to the tag test mode implications, a program that jumps from somewhere in 0x00000000-0x1FFFFFFF to the corresponding +// location in 0x80000000-0x9FFFFFFF will always cause a cache miss in Mednafen. +// +// On a PS1, icache miss fill size can be programmed to 2, 4, 8, or 16 words(though 4 words is the nominally-correct setting). We always emulate the cache +// miss fill size as 4-words. Fill size of 8-words and 16-words are buggy on a PS1, and will write the wrong instruction data values(or read from the wrong +// addresses?) to cache when a cache miss occurs on an address that isn't aligned to a 4-word boundary. +// Fill size of 2-words seems to work on a PS1, and even behaves as if the line size is 2 words in regards to clearing +// the valid bits(when the tag matches, of course), but is obviously not very efficient unless running code that's just endless branching. +// +INLINE uint32 PS_CPU::ReadInstruction(pscpu_timestamp_t ×tamp, uint32 address) +{ + uint32 instr; + + instr = ICache[(address & 0xFFC) >> 2].Data; + + if(ICache[(address & 0xFFC) >> 2].TV != address) + { + ReadAbsorb[ReadAbsorbWhich] = 0; + ReadAbsorbWhich = 0; + + if(address >= 0xA0000000 || !(BIU & 0x800)) + { + instr = MDFN_de32lsb(&FastMap[address >> FAST_MAP_SHIFT][address]); + timestamp += 4; // Approximate best-case cache-disabled time, per PS1 tests(executing out of 0xA0000000+); it can be 5 in *some* sequences of code(like a lot of sequential "nop"s, probably other simple instructions too). + } + else + { + __ICache *ICI = &ICache[((address & 0xFF0) >> 2)]; + const uint8 *FMP = &FastMap[(address & 0xFFFFFFF0) >> FAST_MAP_SHIFT][address & 0xFFFFFFF0]; + + // | 0x2 to simulate (in)validity bits. + ICI[0x00].TV = (address & 0xFFFFFFF0) | 0x0 | 0x2; + ICI[0x01].TV = (address & 0xFFFFFFF0) | 0x4 | 0x2; + ICI[0x02].TV = (address & 0xFFFFFFF0) | 0x8 | 0x2; + ICI[0x03].TV = (address & 0xFFFFFFF0) | 0xC | 0x2; + + timestamp += 3; + + switch(address & 0xC) + { + case 0x0: + timestamp++; + ICI[0x00].TV &= ~0x2; + ICI[0x00].Data = MDFN_de32lsb(&FMP[0x0]); + case 0x4: + timestamp++; + ICI[0x01].TV &= ~0x2; + ICI[0x01].Data = MDFN_de32lsb(&FMP[0x4]); + case 0x8: + timestamp++; + ICI[0x02].TV &= ~0x2; + ICI[0x02].Data = MDFN_de32lsb(&FMP[0x8]); + case 0xC: + timestamp++; + ICI[0x03].TV &= ~0x2; + ICI[0x03].Data = MDFN_de32lsb(&FMP[0xC]); + break; + } + instr = ICache[(address & 0xFFC) >> 2].Data; + } + } + + return instr; +} + +uint32 NO_INLINE PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 instr) { - const bool AfterBranchInstr = !(NPM & 0x1); - const bool BranchTaken = !(NPM & 0x3); uint32 handler = 0x80000080; assert(code < 16); @@ -391,18 +489,18 @@ uint32 NO_INLINE PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NP, cons "INT", "MOD", "TLBL", "TLBS", "ADEL", "ADES", "IBE", "DBE", "SYSCALL", "BP", "RI", "COPU", "OV", NULL, NULL, NULL }; - PSX_DBG(PSX_DBG_WARNING, "[CPU] Exception %s(0x%02x) @ PC=0x%08x(NP=0x%08x, NPM=0x%08x), Instr=0x%08x, IPCache=0x%02x, CAUSE=0x%08x, SR=0x%08x, IRQC_Status=0x%04x, IRQC_Mask=0x%04x\n", - exmne[code], code, PC, NP, NPM, instr, IPCache, CP0.CAUSE, CP0.SR, IRQ_GetRegister(IRQ_GSREG_STATUS, NULL, 0), IRQ_GetRegister(IRQ_GSREG_MASK, NULL, 0)); + PSX_DBG(PSX_DBG_WARNING, "[CPU] Exception %s(0x%02x) @ PC=0x%08x(NP=0x%08x, BDBT=0x%02x), Instr=0x%08x, IPCache=0x%02x, CAUSE=0x%08x, SR=0x%08x, IRQC_Status=0x%04x, IRQC_Mask=0x%04x\n", + exmne[code], code, PC, NP, BDBT, instr, IPCache, CP0.CAUSE, CP0.SR, IRQ_GetRegister(IRQ_GSREG_STATUS, NULL, 0), IRQ_GetRegister(IRQ_GSREG_MASK, NULL, 0)); } if(CP0.SR & (1 << 22)) // BEV handler = 0xBFC00180; CP0.EPC = PC; - if(AfterBranchInstr) + if(BDBT & 2) { CP0.EPC -= 4; - CP0.TAR = (PC & (NPM | 3)) + NP; + CP0.TAR = NP; } if(ADDBT) @@ -415,230 +513,161 @@ uint32 NO_INLINE PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NP, cons CP0.CAUSE &= 0x0000FF00; CP0.CAUSE |= code << 2; - // If EPC was adjusted -= 4 because we are after a branch instruction, set bit 31. - CP0.CAUSE |= AfterBranchInstr << 31; - CP0.CAUSE |= BranchTaken << 30; + CP0.CAUSE |= BDBT << 30; CP0.CAUSE |= (instr << 2) & (0x3 << 28); // CE + // + // + // RecalcIPCache(); + BDBT = 0; + return(handler); -} - -#define BACKING_TO_ACTIVE \ - PC = BACKED_PC; \ - new_PC = BACKED_new_PC; \ - new_PC_mask = BACKED_new_PC_mask; \ - LDWhich = BACKED_LDWhich; \ - LDValue = BACKED_LDValue; - -#define ACTIVE_TO_BACKING \ - BACKED_PC = PC; \ - BACKED_new_PC = new_PC; \ - BACKED_new_PC_mask = new_PC_mask; \ - BACKED_LDWhich = LDWhich; \ - BACKED_LDValue = LDValue; - +} + +#define BACKING_TO_ACTIVE \ + PC = BACKED_PC; \ + new_PC = BACKED_new_PC; \ + LDWhich = BACKED_LDWhich; \ + LDValue = BACKED_LDValue; + +#define ACTIVE_TO_BACKING \ + BACKED_PC = PC; \ + BACKED_new_PC = new_PC; \ + BACKED_LDWhich = LDWhich; \ + BACKED_LDValue = LDValue; + // // Should come before DO_LDS() so the EXP_ILL_CHECK() emulator debugging macro in GPR_DEP() will work properly. // #define GPR_DEPRES_BEGIN { uint8 back = ReadAbsorb[0]; #define GPR_DEP(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; EXP_ILL_CHECK(if(LDWhich > 0 && LDWhich < 0x20 && LDWhich == tn) { PSX_DBG(PSX_DBG_WARNING, "[CPU] Instruction at PC=0x%08x in load delay slot has dependency on load target register(0x%02x): SR=0x%08x\n", PC, LDWhich, CP0.SR); }) } #define GPR_RES(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; } -#define GPR_DEPRES_END ReadAbsorb[0] = back; } - -template -pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) -{ - register pscpu_timestamp_t timestamp = timestamp_in; - - register uint32 PC; - register uint32 new_PC; - register uint32 new_PC_mask; - register uint32 LDWhich; - register uint32 LDValue; - - //printf("%d %d\n", gte_ts_done, muldiv_ts_done); - - gte_ts_done += timestamp; - muldiv_ts_done += timestamp; - - BACKING_TO_ACTIVE; - - do - { - //printf("Running: %d %d\n", timestamp, next_event_ts); - while(MDFN_LIKELY(timestamp < next_event_ts)) - { - uint32 instr; - uint32 opf; - - // Zero must be zero...until the Master Plan is enacted. - GPR[0] = 0; - - if(DebugMode && CPUHook) - { - ACTIVE_TO_BACKING; - - // For save states in step mode. - gte_ts_done -= timestamp; - muldiv_ts_done -= timestamp; - - CPUHook(timestamp, PC); - - // For save states in step mode. - gte_ts_done += timestamp; - muldiv_ts_done += timestamp; - - BACKING_TO_ACTIVE; - } - - if(BIOSPrintMode) - { - if(PC == 0xB0) - { - if(MDFN_UNLIKELY(GPR[9] == 0x3D)) - { - PSX_DBG_BIOS_PUTC(GPR[4]); - } - } - } - - // We can't fold this into the ICache[] != PC handling, since the lower 2 bits of TV - // are already used for cache management purposes and it assumes that the lower 2 bits of PC will be 0. - if(MDFN_UNLIKELY(PC & 0x3)) - { - // This will block interrupt processing, but since we're going more for keeping broken homebrew/hacks from working - // than super-duper-accurate pipeline emulation, it shouldn't be a problem. - CP0.BADVA = PC; - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, 0); - new_PC_mask = 0; - goto OpDone; - } - - instr = ICache[(PC & 0xFFC) >> 2].Data; - - if(ICache[(PC & 0xFFC) >> 2].TV != PC) - { - //WriteAbsorb = 0; - //WriteAbsorbCount = 0; - //WriteAbsorbMonkey = 0; - ReadAbsorb[ReadAbsorbWhich] = 0; - ReadAbsorbWhich = 0; - - // FIXME: Handle executing out of scratchpad. - if(PC >= 0xA0000000 || !(BIU & 0x800)) - { - instr = MDFN_de32lsb(&FastMap[PC >> FAST_MAP_SHIFT][PC]); - timestamp += 4; // Approximate best-case cache-disabled time, per PS1 tests(executing out of 0xA0000000+); it can be 5 in *some* sequences of code(like a lot of sequential "nop"s, probably other simple instructions too). - } - else - { - __ICache *ICI = &ICache[((PC & 0xFF0) >> 2)]; - const uint8 *FMP = &FastMap[(PC &~ 0xF) >> FAST_MAP_SHIFT][PC &~ 0xF]; - - // | 0x2 to simulate (in)validity bits. - ICI[0x00].TV = (PC &~ 0xF) | 0x00 | 0x2; - ICI[0x01].TV = (PC &~ 0xF) | 0x04 | 0x2; - ICI[0x02].TV = (PC &~ 0xF) | 0x08 | 0x2; - ICI[0x03].TV = (PC &~ 0xF) | 0x0C | 0x2; - - timestamp += 3; - - switch(PC & 0xC) - { - case 0x0: - timestamp++; - ICI[0x00].TV &= ~0x2; - ICI[0x00].Data = MDFN_de32lsb(&FMP[0x0]); - case 0x4: - timestamp++; - ICI[0x01].TV &= ~0x2; - ICI[0x01].Data = MDFN_de32lsb(&FMP[0x4]); - case 0x8: - timestamp++; - ICI[0x02].TV &= ~0x2; - ICI[0x02].Data = MDFN_de32lsb(&FMP[0x8]); - case 0xC: - timestamp++; - ICI[0x03].TV &= ~0x2; - ICI[0x03].Data = MDFN_de32lsb(&FMP[0xC]); - break; - } - instr = ICache[(PC & 0xFFC) >> 2].Data; - } - } - - //printf("PC=%08x, SP=%08x - op=0x%02x - funct=0x%02x - instr=0x%08x\n", PC, GPR[29], instr >> 26, instr & 0x3F, instr); - //for(int i = 0; i < 32; i++) - // printf("%02x : %08x\n", i, GPR[i]); - //printf("\n"); - if (g_ShockTraceCallback) - { - //_asm int 3; - shock_Util_DisassembleMIPS(PC, instr, disasm_buf, ARRAY_SIZE(disasm_buf)); - g_ShockTraceCallback(NULL, PC, instr, disasm_buf); - } - - opf = instr & 0x3F; - - if(instr & (0x3F << 26)) - opf = 0x40 | (instr >> 26); - - opf |= IPCache; - -#if 0 - { - uint32 tmp = (ReadAbsorb[ReadAbsorbWhich] + 0x7FFFFFFF) >> 31; - ReadAbsorb[ReadAbsorbWhich] -= tmp; - timestamp = timestamp + 1 - tmp; - } -#else - if(ReadAbsorb[ReadAbsorbWhich]) - ReadAbsorb[ReadAbsorbWhich]--; - //else if((uint8)WriteAbsorb) - //{ - // WriteAbsorb--; - // if(!WriteAbsorb) - // { - // WriteAbsorbCount--; - // if(WriteAbsorbMonkey) - // WriteAbsorbMonkey--; - // WriteAbsorb >>= 8; - // } - //} - else - timestamp++; -#endif - - #define DO_LDS() { GPR[LDWhich] = LDValue; ReadAbsorb[LDWhich] = LDAbsorb; ReadFudge = LDWhich; ReadAbsorbWhich |= LDWhich & 0x1F; LDWhich = 0x20; } - #define BEGIN_OPF(name) { op_##name: - #define END_OPF goto OpDone; } - +#define GPR_DEPRES_END ReadAbsorb[0] = back; } -#define DO_BRANCH(arg_cond, arg_offset, arg_mask, arg_dolink, arg_linkreg)\ +template +pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) +{ + register pscpu_timestamp_t timestamp = timestamp_in; + + register uint32 PC; + register uint32 new_PC; + register uint32 LDWhich; + register uint32 LDValue; + + //printf("%d %d\n", gte_ts_done, muldiv_ts_done); + + gte_ts_done += timestamp; + muldiv_ts_done += timestamp; + + BACKING_TO_ACTIVE; + + do + { + //printf("Running: %d %d\n", timestamp, next_event_ts); + while(MDFN_LIKELY(timestamp < next_event_ts)) + { + uint32 instr; + uint32 opf; + + // Zero must be zero...until the Master Plan is enacted. + GPR[0] = 0; + + if(DebugMode && CPUHook) + { + ACTIVE_TO_BACKING; + + // For save states in step mode. + gte_ts_done -= timestamp; + muldiv_ts_done -= timestamp; + + CPUHook(timestamp, PC); + + // For save states in step mode. + gte_ts_done += timestamp; + muldiv_ts_done += timestamp; + + BACKING_TO_ACTIVE; + } + + if(BIOSPrintMode) + { + if(PC == 0xB0) + { + if(MDFN_UNLIKELY(GPR[9] == 0x3D)) + { + PSX_DBG_BIOS_PUTC(GPR[4]); + } + } + } + + // + // Instruction fetch + // + if(MDFN_UNLIKELY(PC & 0x3)) + { + // This will block interrupt processing, but since we're going more for keeping broken homebrew/hacks from working + // than super-duper-accurate pipeline emulation, it shouldn't be a problem. + CP0.BADA = PC; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, 0); + goto OpDone; + } + + instr = ReadInstruction(timestamp, PC); + + + // + // Instruction decode + // + //printf("PC=%08x, SP=%08x - op=0x%02x - funct=0x%02x - instr=0x%08x\n", PC, GPR[29], instr >> 26, instr & 0x3F, instr); + //for(int i = 0; i < 32; i++) + // printf("%02x : %08x\n", i, GPR[i]); + //printf("\n"); + if (g_ShockTraceCallback) + { + //_asm int 3; + shock_Util_DisassembleMIPS(PC, instr, disasm_buf, ARRAY_SIZE(disasm_buf)); + g_ShockTraceCallback(NULL, PC, instr, disasm_buf); + } + + opf = instr & 0x3F; + + if(instr & (0x3F << 26)) + opf = 0x40 | (instr >> 26); + + opf |= IPCache; + + if(ReadAbsorb[ReadAbsorbWhich]) + ReadAbsorb[ReadAbsorbWhich]--; + else + timestamp++; + + #define DO_LDS() { GPR[LDWhich] = LDValue; ReadAbsorb[LDWhich] = LDAbsorb; ReadFudge = LDWhich; ReadAbsorbWhich |= LDWhich & 0x1F; LDWhich = 0x20; } + #define BEGIN_OPF(name) { op_##name: + #define END_OPF goto OpDone; } + + #define DO_BRANCH(arg_cond, arg_offset, arg_mask, arg_dolink, arg_linkreg)\ { \ const bool cond = (arg_cond); \ const uint32 offset = (arg_offset); \ const uint32 mask = (arg_mask); \ const uint32 old_PC = PC; \ \ - EXP_ILL_CHECK(if(!(new_PC_mask & 0x03)) { PSX_DBG(PSX_DBG_WARNING, "[CPU] Branch instruction at PC=0x%08x in branch delay slot: SR=0x%08x\n", PC, CP0.SR);}) \ + EXP_ILL_CHECK(if(BDBT) { PSX_DBG(PSX_DBG_WARNING, "[CPU] Branch instruction at PC=0x%08x in branch delay slot: SR=0x%08x\n", PC, CP0.SR);}) \ \ - PC = (PC & new_PC_mask) + new_PC; \ - \ - /* Clear lower bit to signify being after a branch instruction (overloaded behavior for performance). */ \ - new_PC_mask = ~1U; \ - new_PC = 4; \ + PC = new_PC; \ + new_PC += 4; \ + BDBT = 2; \ \ if(arg_dolink) \ - GPR[(arg_linkreg)] = PC + 4; \ + GPR[(arg_linkreg)] = new_PC; \ \ if(cond) \ { \ if(ILHMode) \ { \ - if(old_PC == ((PC & mask) + offset)) \ + if(old_PC == (((new_PC - 4) & mask) + offset)) \ { \ if(MDFN_densb(&FastMap[PC >> FAST_MAP_SHIFT][PC]) == 0) \ { \ @@ -650,35 +679,45 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) } \ } \ \ - /* Lower bits of new_PC_mask being clear signifies being in a branch delay slot. (overloaded behavior for performance) */ \ - new_PC = offset; \ - new_PC_mask = mask & ~3; \ + new_PC = ((new_PC - 4) & mask) + offset; \ + BDBT = 3; \ \ if(DebugMode && ADDBT) \ { \ - ADDBT(PC, (PC & new_PC_mask) + new_PC, false); \ + ADDBT(PC, new_PC, false); \ } \ } \ + \ goto SkipNPCStuff; \ - } - - #define ITYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = (int32)(int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/ - #define ITYPE_ZE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/ - #define JTYPE uint32 target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/ - #define RTYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 rd MDFN_NOWARN_UNUSED = (instr >> 11) & 0x1F; uint32 shamt MDFN_NOWARN_UNUSED = (instr >> 6) & 0x1F; /*printf(" rs=%02x(%08x), rt=%02x(%08x), rd=%02x(%08x) ", rs, GPR[rs], rt, GPR[rt], rd, GPR[rd]);*/ - -#if HAVE_COMPUTED_GOTO - #define CGBEGIN static const void *const op_goto_table[256] = { - #define CGE(l) &&l, - #define CGEND }; goto *op_goto_table[opf]; -#else - /* (uint8) cast for cheaper alternative to generated branch+compare bounds check instructions, but still more - expensive than computed goto which needs no masking nor bounds checking. - */ - #define CGBEGIN { enum { CGESB = 1 + __COUNTER__ }; switch((uint8)opf) { - #define CGE(l) case __COUNTER__ - CGESB: goto l; - #define CGEND } } -#endif + } + + #define ITYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = (int32)(int16)(instr & 0xFFFF); /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/ + #define ITYPE_ZE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/ + #define JTYPE uint32 target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/ + #define RTYPE uint32 rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32 rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32 rd MDFN_NOWARN_UNUSED = (instr >> 11) & 0x1F; uint32 shamt MDFN_NOWARN_UNUSED = (instr >> 6) & 0x1F; /*printf(" rs=%02x(%08x), rt=%02x(%08x), rd=%02x(%08x) ", rs, GPR[rs], rt, GPR[rt], rd, GPR[rd]);*/ + +#if HAVE_COMPUTED_GOTO + #if 0 + // + // These truncated 32-bit table values apparently can't be calculated at compile/link time by gcc on x86_64, so gcc inserts initialization code, but + // the compare for the initialization code path is placed sub-optimally(near where the table value is used instead of at the beginning of the function). + // + #define CGBEGIN static const uint32 const op_goto_table[256] = { + #define CGE(l) (uint32)(uintptr_t)&&l, + #define CGEND }; goto *(void*)(uintptr_t)op_goto_table[opf]; + #else + #define CGBEGIN static const void *const op_goto_table[256] = { + #define CGE(l) &&l, + #define CGEND }; goto *op_goto_table[opf]; + #endif +#else + /* (uint8) cast for cheaper alternative to generated branch+compare bounds check instructions, but still more + expensive than computed goto which needs no masking nor bounds checking. + */ + #define CGBEGIN { enum { CGESB = 1 + __COUNTER__ }; switch((uint8)opf) { + #define CGE(l) case __COUNTER__ - CGESB: goto l; + #define CGEND } } +#endif CGBEGIN CGE(op_SLL) CGE(op_ILL) CGE(op_SRL) CGE(op_SRA) CGE(op_SLLV) CGE(op_ILL) CGE(op_SRLV) CGE(op_SRAV) @@ -722,10 +761,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) { BEGIN_OPF(ILL); - PSX_WARNING("[CPU] Unknown instruction @%08x = %08x, op=%02x, funct=%02x", PC, instr, instr >> 26, (instr & 0x3F)); DO_LDS(); - new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_RI, PC, new_PC, instr); END_OPF; // @@ -747,8 +784,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(ep)) { - new_PC = Exception(EXCEPTION_OV, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_OV, PC, new_PC, instr); } else GPR[rd] = result; @@ -773,8 +809,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(ep)) { - new_PC = Exception(EXCEPTION_OV, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_OV, PC, new_PC, instr); } else GPR[rt] = result; @@ -954,8 +989,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) // BEGIN_OPF(BREAK); DO_LDS(); - new_PC = Exception(EXCEPTION_BP, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_BP, PC, new_PC, instr); END_OPF; // Cop "instructions": CFCz(no CP0), COPz, CTCz(no CP0), LWCz(no CP0), MFCz, MTCz, SWCz(no CP0) @@ -977,8 +1011,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) case 0x02: case 0x06: DO_LDS(); - new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_RI, PC, new_PC, instr); break; case 0x00: // MFC0 - Move from Coprocessor @@ -990,8 +1023,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) case 0x04: case 0x0A: DO_LDS(); - new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_RI, PC, new_PC, instr); break; case 0x03: @@ -1032,8 +1064,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) case 0x02: case 0x04: case 0x0A: - new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_RI, PC, new_PC, instr); break; case CP0REG_BPC: @@ -1067,9 +1098,6 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) break; case CP0REG_SR: - if((CP0.SR ^ val) & 0x10000) - PSX_DBG(PSX_DBG_SPARSE, "[CPU] IsC %u->%u\n", (bool)(CP0.SR & (1U << 16)), (bool)(val & (1U << 16))); - CP0.SR = val & ~( (0x3 << 26) | (0x3 << 23) | (0x3 << 6)); RecalcIPCache(); break; @@ -1103,8 +1131,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) } else if(cp0_op == 0x01 || cp0_op == 0x02 || cp0_op == 0x06 || cp0_op == 0x08) // TLBR, TLBWI, TLBWR, TLBP { - new_PC = Exception(EXCEPTION_RI, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_RI, PC, new_PC, instr); } } break; @@ -1123,8 +1150,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(!(CP0.SR & (1U << (28 + 2))))) { DO_LDS(); - new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, instr); } else switch(sub_op) { @@ -1218,8 +1244,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(!(CP0.SR & (1U << (28 + ((instr >> 26) & 0x3))))) { - new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, instr); } else { @@ -1248,16 +1273,14 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(!(CP0.SR & (1U << (28 + ((instr >> 26) & 0x3))))) { - new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, instr); } else { if(MDFN_UNLIKELY(address & 3)) { - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, instr); } else { @@ -1279,9 +1302,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(address & 3)) { - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, instr); } else { @@ -1304,16 +1326,14 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(!(CP0.SR & (1U << (28 + ((instr >> 26) & 0x3))))) { - new_PC = Exception(EXCEPTION_COPU, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_COPU, PC, new_PC, instr); } else { if(MDFN_UNLIKELY(address & 0x3)) { - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, instr); } else { @@ -1332,9 +1352,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(address & 0x3)) { - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, instr); } else { @@ -1912,8 +1931,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(ep)) { - new_PC = Exception(EXCEPTION_OV, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_OV, PC, new_PC, instr); } else GPR[rd] = result; @@ -1948,8 +1966,7 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) BEGIN_OPF(SYSCALL); DO_LDS(); - new_PC = Exception(EXCEPTION_SYSCALL, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_SYSCALL, PC, new_PC, instr); END_OPF; @@ -2053,9 +2070,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) { DO_LDS(); - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, instr); } else { @@ -2085,9 +2101,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) { DO_LDS(); - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, instr); } else { @@ -2118,9 +2133,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) { DO_LDS(); - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC, instr); } else { @@ -2167,9 +2181,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(address & 0x1)) { - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, instr); } else WriteMemory(timestamp, address, GPR[rt]); @@ -2192,9 +2205,8 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(MDFN_UNLIKELY(address & 0x3)) { - CP0.BADVA = address; - new_PC = Exception(EXCEPTION_ADES, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + CP0.BADA = address; + new_PC = Exception(EXCEPTION_ADES, PC, new_PC, instr); } else WriteMemory(timestamp, address, GPR[rt]); @@ -2361,17 +2373,15 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) { DO_LDS(); - new_PC = Exception(EXCEPTION_INT, PC, new_PC, new_PC_mask, instr); - new_PC_mask = 0; + new_PC = Exception(EXCEPTION_INT, PC, new_PC, instr); } END_OPF; } OpDone: ; - - PC = (PC & new_PC_mask) + new_PC; - new_PC_mask = ~0U; - new_PC = 4; + PC = new_PC; + new_PC = new_PC + 4; + BDBT = 0; SkipNPCStuff: ; @@ -2385,142 +2395,184 @@ pscpu_timestamp_t PS_CPU::RunReal(pscpu_timestamp_t timestamp_in) if(muldiv_ts_done > 0) muldiv_ts_done -= timestamp; - ACTIVE_TO_BACKING; - return(timestamp); -} - -pscpu_timestamp_t PS_CPU::Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode) -{ - if(CPUHook || ADDBT) - return(RunReal(timestamp_in)); - else - { - if(ILHMode) - return(RunReal(timestamp_in)); - else - { - if(BIOSPrintMode) - return(RunReal(timestamp_in)); - else - return(RunReal(timestamp_in)); - } - } -} - -void PS_CPU::SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32 pc), void (*addbt)(uint32 from, uint32 to, bool exception)) -{ - ADDBT = addbt; - CPUHook = cpuh; -} - -uint32 PS_CPU::GetRegister(unsigned int which, char *special, const uint32 special_len) -{ - uint32 ret = 0; - - if(which >= GSREG_GPR && which < (GSREG_GPR + 32)) - ret = GPR[which]; - else switch(which) - { - case GSREG_PC: - ret = BACKED_PC; - break; - - case GSREG_PC_NEXT: - ret = BACKED_new_PC; - break; - - case GSREG_IN_BD_SLOT: - ret = !(BACKED_new_PC_mask & 3); - break; - - case GSREG_LO: - ret = LO; - break; - - case GSREG_HI: - ret = HI; - break; - - case GSREG_SR: - ret = CP0.SR; - break; - - case GSREG_CAUSE: - ret = CP0.CAUSE; - break; - - case GSREG_EPC: - ret = CP0.EPC; - break; - - } - - return(ret); -} - -void PS_CPU::SetRegister(unsigned int which, uint32 value) -{ - if(which >= GSREG_GPR && which < (GSREG_GPR + 32)) - { - if(which != (GSREG_GPR + 0)) - GPR[which] = value; - } - else switch(which) - { - case GSREG_PC: - BACKED_PC = value & ~0x3; // Remove masking if we ever add proper misaligned PC exception - break; - - case GSREG_LO: - LO = value; - break; - - case GSREG_HI: - HI = value; - break; - - case GSREG_SR: - CP0.SR = value; // TODO: mask - break; - - case GSREG_CAUSE: - CP0.CAUSE = value; - break; - - case GSREG_EPC: - CP0.EPC = value & ~0x3U; - break; - - - } -} - -bool PS_CPU::PeekCheckICache(uint32 PC, uint32 *iw) -{ - if(ICache[(PC & 0xFFC) >> 2].TV == PC) - { - *iw = ICache[(PC & 0xFFC) >> 2].Data; - return(true); - } - - return(false); -} - - -uint8 PS_CPU::PeekMem8(uint32 A) -{ - return PeekMemory(A); -} - -uint16 PS_CPU::PeekMem16(uint32 A) -{ - return PeekMemory(A); -} - -uint32 PS_CPU::PeekMem32(uint32 A) -{ - return PeekMemory(A); -} + ACTIVE_TO_BACKING; + + return(timestamp); +} + +pscpu_timestamp_t PS_CPU::Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode) +{ + if(CPUHook || ADDBT) + return(RunReal(timestamp_in)); + else + { + if(ILHMode) + return(RunReal(timestamp_in)); + else + { + if(BIOSPrintMode) + return(RunReal(timestamp_in)); + else + return(RunReal(timestamp_in)); + } + } +} + +void PS_CPU::SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32 pc), void (*addbt)(uint32 from, uint32 to, bool exception)) +{ + ADDBT = addbt; + CPUHook = cpuh; +} + +uint32 PS_CPU::GetRegister(unsigned int which, char *special, const uint32 special_len) +{ + uint32 ret = 0; + + if(which >= GSREG_GPR && which < (GSREG_GPR + 32)) + ret = GPR[which]; + else switch(which) + { + case GSREG_PC: + ret = BACKED_PC; + break; + + case GSREG_PC_NEXT: + ret = BACKED_new_PC; + break; + + case GSREG_IN_BD_SLOT: + ret = BDBT; + break; + + case GSREG_LO: + ret = LO; + break; + + case GSREG_HI: + ret = HI; + break; + + case GSREG_BPC: + ret = CP0.BPC; + break; + + case GSREG_BDA: + ret = CP0.BDA; + break; + + case GSREG_TAR: + ret = CP0.TAR; + break; + + case GSREG_DCIC: + ret = CP0.DCIC; + break; + + case GSREG_BADA: + ret = CP0.BADA; + break; + + case GSREG_BDAM: + ret = CP0.BDAM; + break; + + case GSREG_BPCM: + ret = CP0.BPCM; + break; + + case GSREG_SR: + ret = CP0.SR; + break; + + case GSREG_CAUSE: + ret = CP0.CAUSE; + if(special) + { + snprintf(special, special_len, "BD: %u, BT: %u, CE: %u, IP: 0x%02x, Sw: %u, ExcCode: 0x%01x", + (ret >> 31) & 1, (ret >> 30) & 1, (ret >> 28) & 3, (ret >> 10) & 0x3F, (ret >> 8) & 3, (ret >> 2) & 0xF); + } + break; + + case GSREG_EPC: + ret = CP0.EPC; + break; + + } + + return(ret); +} + +void PS_CPU::SetRegister(unsigned int which, uint32 value) +{ + if(which >= GSREG_GPR && which < (GSREG_GPR + 32)) + { + if(which != (GSREG_GPR + 0)) + GPR[which] = value; + } + else switch(which) + { + case GSREG_PC: + BACKED_PC = value; + break; + + case GSREG_PC_NEXT: + BACKED_new_PC = value; + break; + + case GSREG_IN_BD_SLOT: + BDBT = value & 0x3; + break; + + case GSREG_LO: + LO = value; + break; + + case GSREG_HI: + HI = value; + break; + + case GSREG_SR: + CP0.SR = value; // TODO: mask + break; + + case GSREG_CAUSE: + CP0.CAUSE = value; + break; + + case GSREG_EPC: + CP0.EPC = value & ~0x3U; + break; + + + } +} + +bool PS_CPU::PeekCheckICache(uint32 PC, uint32 *iw) +{ + if(ICache[(PC & 0xFFC) >> 2].TV == PC) + { + *iw = ICache[(PC & 0xFFC) >> 2].Data; + return(true); + } + + return(false); +} + + +uint8 PS_CPU::PeekMem8(uint32 A) +{ + return PeekMemory(A); +} + +uint16 PS_CPU::PeekMem16(uint32 A) +{ + return PeekMemory(A); +} + +uint32 PS_CPU::PeekMem32(uint32 A) +{ + return PeekMemory(A); +} void PS_CPU::PokeMem8(uint32 A, uint8 V) { @@ -2535,225 +2587,225 @@ void PS_CPU::PokeMem16(uint32 A, uint16 V) void PS_CPU::PokeMem32(uint32 A, uint32 V) { PokeMemory(A, V); -} - -#undef BEGIN_OPF -#undef END_OPF -#undef MK_OPF - -#define MK_OPF(op, funct) ((op) ? (0x40 | (op)) : (funct)) -#define BEGIN_OPF(op, funct) case MK_OPF(op, funct): { -#define END_OPF } break; - -// FIXME: should we breakpoint on an illegal address? And with LWC2/SWC2 if CP2 isn't enabled? -void PS_CPU::CheckBreakpoints(void (*callback)(bool write, uint32 address, unsigned int len), uint32 instr) -{ - uint32 opf; - - opf = instr & 0x3F; - - if(instr & (0x3F << 26)) - opf = 0x40 | (instr >> 26); - - - switch(opf) - { - default: - break; - - // - // LB - Load Byte - // - BEGIN_OPF(0x20, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(false, address, 1); - END_OPF; - - // - // LBU - Load Byte Unsigned - // - BEGIN_OPF(0x24, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(false, address, 1); - END_OPF; - - // - // LH - Load Halfword - // - BEGIN_OPF(0x21, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(false, address, 2); - END_OPF; - - // - // LHU - Load Halfword Unsigned - // - BEGIN_OPF(0x25, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(false, address, 2); - END_OPF; - - - // - // LW - Load Word - // - BEGIN_OPF(0x23, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(false, address, 4); - END_OPF; - - // - // SB - Store Byte - // - BEGIN_OPF(0x28, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(true, address, 1); - END_OPF; - - // - // SH - Store Halfword - // - BEGIN_OPF(0x29, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(true, address, 2); - END_OPF; - - // - // SW - Store Word - // - BEGIN_OPF(0x2B, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(true, address, 4); - END_OPF; - - // - // LWL - Load Word Left - // - BEGIN_OPF(0x22, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - do - { - callback(false, address, 1); - } while((address--) & 0x3); - - END_OPF; - - // - // SWL - Store Word Left - // - BEGIN_OPF(0x2A, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - do - { - callback(true, address, 1); - } while((address--) & 0x3); - - END_OPF; - - // - // LWR - Load Word Right - // - BEGIN_OPF(0x26, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - do - { - callback(false, address, 1); - } while((++address) & 0x3); - - END_OPF; - - // - // SWR - Store Word Right - // - BEGIN_OPF(0x2E, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - do - { - callback(true, address, 1); - } while((++address) & 0x3); - - END_OPF; - - // - // LWC2 - // - BEGIN_OPF(0x32, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(false, address, 4); - END_OPF; - - // - // SWC2 - // - BEGIN_OPF(0x3A, 0); - ITYPE; - uint32 address = GPR[rs] + immediate; - - callback(true, address, 4); - END_OPF; - - } -} - - - -SYNCFUNC(PS_CPU) -{ - NSS(GPR); - NSS(LO); - NSS(HI); - NSS(BACKED_PC); - NSS(BACKED_new_PC); - NSS(BACKED_new_PC_mask); - - NSS(IPCache); - NSS(Halted); - - NSS(BACKED_LDWhich); - NSS(BACKED_LDValue); - NSS(LDAbsorb); - - NSS(next_event_ts); - NSS(gte_ts_done); - NSS(muldiv_ts_done); - - NSS(BIU); - NSS(ICache_Bulk); - - NSS(CP0.Regs); - - NSS(ReadAbsorb); - NSS(ReadAbsorbWhich); - NSS(ReadFudge); - - NSS(ScratchRAM.data8); - -} //SYNCFUNC(CPU) - -} //namespace MDFN_IEN_PSX +} + +#undef BEGIN_OPF +#undef END_OPF +#undef MK_OPF + +#define MK_OPF(op, funct) ((op) ? (0x40 | (op)) : (funct)) +#define BEGIN_OPF(op, funct) case MK_OPF(op, funct): { +#define END_OPF } break; + +// FIXME: should we breakpoint on an illegal address? And with LWC2/SWC2 if CP2 isn't enabled? +void PS_CPU::CheckBreakpoints(void (*callback)(bool write, uint32 address, unsigned int len), uint32 instr) +{ + uint32 opf; + + opf = instr & 0x3F; + + if(instr & (0x3F << 26)) + opf = 0x40 | (instr >> 26); + + + switch(opf) + { + default: + break; + + // + // LB - Load Byte + // + BEGIN_OPF(0x20, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(false, address, 1); + END_OPF; + + // + // LBU - Load Byte Unsigned + // + BEGIN_OPF(0x24, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(false, address, 1); + END_OPF; + + // + // LH - Load Halfword + // + BEGIN_OPF(0x21, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(false, address, 2); + END_OPF; + + // + // LHU - Load Halfword Unsigned + // + BEGIN_OPF(0x25, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(false, address, 2); + END_OPF; + + + // + // LW - Load Word + // + BEGIN_OPF(0x23, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(false, address, 4); + END_OPF; + + // + // SB - Store Byte + // + BEGIN_OPF(0x28, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(true, address, 1); + END_OPF; + + // + // SH - Store Halfword + // + BEGIN_OPF(0x29, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(true, address, 2); + END_OPF; + + // + // SW - Store Word + // + BEGIN_OPF(0x2B, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(true, address, 4); + END_OPF; + + // + // LWL - Load Word Left + // + BEGIN_OPF(0x22, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + do + { + callback(false, address, 1); + } while((address--) & 0x3); + + END_OPF; + + // + // SWL - Store Word Left + // + BEGIN_OPF(0x2A, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + do + { + callback(true, address, 1); + } while((address--) & 0x3); + + END_OPF; + + // + // LWR - Load Word Right + // + BEGIN_OPF(0x26, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + do + { + callback(false, address, 1); + } while((++address) & 0x3); + + END_OPF; + + // + // SWR - Store Word Right + // + BEGIN_OPF(0x2E, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + do + { + callback(true, address, 1); + } while((++address) & 0x3); + + END_OPF; + + // + // LWC2 + // + BEGIN_OPF(0x32, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(false, address, 4); + END_OPF; + + // + // SWC2 + // + BEGIN_OPF(0x3A, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + callback(true, address, 4); + END_OPF; + + } +} + + + +SYNCFUNC(PS_CPU) +{ + NSS(GPR); + NSS(LO); + NSS(HI); + NSS(BACKED_PC); + NSS(BACKED_new_PC); + NSS(BDBT); + + NSS(IPCache); + NSS(Halted); + + NSS(BACKED_LDWhich); + NSS(BACKED_LDValue); + NSS(LDAbsorb); + + NSS(next_event_ts); + NSS(gte_ts_done); + NSS(muldiv_ts_done); + + NSS(BIU); + NSS(ICache_Bulk); + + NSS(CP0.Regs); + + NSS(ReadAbsorb); + NSS(ReadAbsorbWhich); + NSS(ReadFudge); + + NSS(ScratchRAM.data8); + +} //SYNCFUNC(CPU) + +} //namespace MDFN_IEN_PSX diff --git a/psx/octoshock/psx/cpu.h b/psx/octoshock/psx/cpu.h index 729a178a81..b2603cef83 100644 --- a/psx/octoshock/psx/cpu.h +++ b/psx/octoshock/psx/cpu.h @@ -1,41 +1,62 @@ +/******************************************************************************/ +/* Mednafen Sony PS1 Emulation Module */ +/******************************************************************************/ +/* cpu.h: +** Copyright (C) 2011-2016 Mednafen Team +** +** This program is free software; you can redistribute it and/or +** modify it under the terms of the GNU General Public License +** as published by the Free Software Foundation; either version 2 +** of the License, or (at your option) any later version. +** +** This program is distributed in the hope that it will be useful, +** but WITHOUT ANY WARRANTY; without even the implied warranty of +** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +** GNU General Public License for more details. +** +** You should have received a copy of the GNU General Public License +** along with this program; if not, write to the Free Software Foundation, Inc., +** 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +*/ + #ifndef __MDFN_PSX_CPU_H #define __MDFN_PSX_CPU_H /* -Load delay notes: + Load delay notes: -// Takes 1 less -".set noreorder\n\t" -".set nomacro\n\t" -"lw %0, 0(%2)\n\t" -"nop\n\t" -"nop\n\t" -"or %0, %1, %1\n\t" + // Takes 1 less + ".set noreorder\n\t" + ".set nomacro\n\t" + "lw %0, 0(%2)\n\t" + "nop\n\t" + "nop\n\t" + "or %0, %1, %1\n\t" -// cycle than this: -".set noreorder\n\t" -".set nomacro\n\t" -"lw %0, 0(%2)\n\t" -"nop\n\t" -"or %0, %1, %1\n\t" -"nop\n\t" + // cycle than this: + ".set noreorder\n\t" + ".set nomacro\n\t" + "lw %0, 0(%2)\n\t" + "nop\n\t" + "or %0, %1, %1\n\t" + "nop\n\t" -// Both of these -".set noreorder\n\t" -".set nomacro\n\t" -"lw %0, 0(%2)\n\t" -"nop\n\t" -"nop\n\t" -"or %1, %0, %0\n\t" + // Both of these + ".set noreorder\n\t" + ".set nomacro\n\t" + "lw %0, 0(%2)\n\t" + "nop\n\t" + "nop\n\t" + "or %1, %0, %0\n\t" -// take same...(which is kind of odd). -".set noreorder\n\t" -".set nomacro\n\t" -"lw %0, 0(%2)\n\t" -"nop\n\t" -"or %1, %0, %0\n\t" -"nop\n\t" + // take same...(which is kind of odd). + ".set noreorder\n\t" + ".set nomacro\n\t" + "lw %0, 0(%2)\n\t" + "nop\n\t" + "or %1, %0, %0\n\t" + "nop\n\t" */ #include "gte.h" @@ -45,217 +66,220 @@ namespace MDFN_IEN_PSX #define PS_CPU_EMULATE_ICACHE 1 - class PS_CPU - { - public: +class PS_CPU +{ + public: - PS_CPU() MDFN_COLD; - ~PS_CPU() MDFN_COLD; + PS_CPU() MDFN_COLD; + ~PS_CPU() MDFN_COLD; templatevoid SyncState(EW::NewState *ns); + // 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. + enum { FAST_MAP_SHIFT = 16 }; + enum { FAST_MAP_PSIZE = 1 << FAST_MAP_SHIFT }; - // 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. - enum { FAST_MAP_SHIFT = 16 }; - 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) + { + next_event_ts = next_event_ts_arg; + } - INLINE void SetEventNT(const pscpu_timestamp_t 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 + void AssertIRQ(unsigned which, bool asserted); - // which ranges 0-5, inclusive - 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. + void SetBIU(uint32 val); + uint32 GetBIU(void); - // TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed. - void SetBIU(uint32 val); - 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 HI; + uint32 LO; + uint32 HI; - uint32 BACKED_PC; - uint32 BACKED_new_PC; - uint32 BACKED_new_PC_mask; + uint32 BACKED_PC; + uint32 BACKED_new_PC; - uint32 IPCache; - void RecalcIPCache(void); - bool Halted; + uint32 IPCache; + uint8 BDBT; - uint32 BACKED_LDWhich; - uint32 BACKED_LDValue; - uint32 LDAbsorb; + uint8 ReadAbsorb[0x20 + 1]; + uint8 ReadAbsorbWhich; + uint8 ReadFudge; - pscpu_timestamp_t next_event_ts; - pscpu_timestamp_t gte_ts_done; - pscpu_timestamp_t muldiv_ts_done; + void RecalcIPCache(void); + bool Halted; - uint32 BIU; + uint32 BACKED_LDWhich; + uint32 BACKED_LDValue; + uint32 LDAbsorb; - struct __ICache - { - uint32 TV; - uint32 Data; - }; + pscpu_timestamp_t next_event_ts; + pscpu_timestamp_t gte_ts_done; + pscpu_timestamp_t muldiv_ts_done; - union - { - __ICache ICache[1024]; - uint32 ICache_Bulk[2048]; - }; + uint32 BIU; - enum - { - CP0REG_BPC = 3, // PC breakpoint address. - CP0REG_BDA = 5, // Data load/store breakpoint address. - CP0REG_TAR = 6, // Target address(???) - CP0REG_DCIC = 7, // Cache control - CP0REG_BADVA = 8, - CP0REG_BDAM = 9, // Data load/store address mask. - CP0REG_BPCM = 11, // PC breakpoint address mask. - CP0REG_SR = 12, - CP0REG_CAUSE = 13, - CP0REG_EPC = 14, - CP0REG_PRID = 15 // Product ID - }; + const uint32 addr_mask[8] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x7FFFFFFF, 0x1FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - struct - { - union - { - uint32 Regs[32]; - struct - { - uint32 Unused00; - uint32 Unused01; - uint32 Unused02; - uint32 BPC; // RW - uint32 Unused04; - uint32 BDA; // RW - uint32 TAR; // R - uint32 DCIC; // RW - uint32 BADVA; // R - uint32 BDAM; // R/W - uint32 Unused0A; - uint32 BPCM; // R/W - uint32 SR; // R/W - uint32 CAUSE; // R/W(partial) - uint32 EPC; // R - uint32 PRID; // R - }; - }; - } CP0; + enum + { + CP0REG_BPC = 3, // PC breakpoint address. + CP0REG_BDA = 5, // Data load/store breakpoint address. + CP0REG_TAR = 6, // Target address(???) + CP0REG_DCIC = 7, // Cache control + CP0REG_BADA = 8, + CP0REG_BDAM = 9, // Data load/store address mask. + CP0REG_BPCM = 11, // PC breakpoint address mask. + CP0REG_SR = 12, + CP0REG_CAUSE = 13, + CP0REG_EPC = 14, + CP0REG_PRID = 15 // Product ID + }; -#if 1 - //uint32 WrAbsorb; - //uint8 WrAbsorbShift; + struct + { + union + { + uint32 Regs[32]; + struct + { + uint32 Unused00; + uint32 Unused01; + uint32 Unused02; + uint32 BPC; // RW + uint32 Unused04; + uint32 BDA; // RW + uint32 TAR; // R + uint32 DCIC; // RW + uint32 BADA; // R + uint32 BDAM; // R/W + uint32 Unused0A; + uint32 BPCM; // R/W + uint32 SR; // R/W + uint32 CAUSE; // R/W(partial) + uint32 EPC; // R + uint32 PRID; // R + }; + }; + } CP0; - // On read: - //WrAbsorb = 0; - //WrAbsorbShift = 0; + uint8 MULT_Tab24[24]; - // On write: - //WrAbsorb >>= (WrAbsorbShift >> 2) & 8; - //WrAbsorbShift -= (WrAbsorbShift >> 2) & 8; + struct __ICache + { + /* + TV: + Mask 0x00000001: 0x0 = icache enabled((BIU & 0x800) == 0x800), 0x1 = icache disabled(changed in bulk on BIU value changes; preserve everywhere else!) + Mask 0x00000002: 0x0 = valid, 0x2 = invalid + Mask 0x00000FFC: Always 0 + Mask 0xFFFFF000: Tag. + */ + uint32 TV; + uint32 Data; + }; - //WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift; - //WrAbsorbShift += 8; -#endif + union + { + __ICache ICache[1024]; + uint32 ICache_Bulk[2048]; + }; - uint8 ReadAbsorb[0x20 + 1]; - uint8 ReadAbsorbWhich; - uint8 ReadFudge; + MultiAccessSizeMem<1024, false> ScratchRAM; - //uint32 WriteAbsorb; - //uint8 WriteAbsorbCount; - //uint8 WriteAbsorbMonkey; - uint8 MULT_Tab24[24]; + //PS_GTE GTE; - MultiAccessSizeMem<1024, false> ScratchRAM; + uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)]; + uint8 DummyPage[FAST_MAP_PSIZE]; - //PS_GTE GTE; + enum + { + EXCEPTION_INT = 0, + EXCEPTION_MOD = 1, + EXCEPTION_TLBL = 2, + EXCEPTION_TLBS = 3, + EXCEPTION_ADEL = 4, // Address error on load + EXCEPTION_ADES = 5, // Address error on store + EXCEPTION_IBE = 6, // Instruction bus error + EXCEPTION_DBE = 7, // Data bus error + EXCEPTION_SYSCALL = 8, // System call + EXCEPTION_BP = 9, // Breakpoint + EXCEPTION_RI = 10, // Reserved instruction + EXCEPTION_COPU = 11, // Coprocessor unusable + EXCEPTION_OV = 12 // Arithmetic overflow + }; - uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)]; - uint8 DummyPage[FAST_MAP_PSIZE]; + uint32 Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 instr) MDFN_WARN_UNUSED_RESULT; - enum - { - EXCEPTION_INT = 0, - EXCEPTION_MOD = 1, - EXCEPTION_TLBL = 2, - EXCEPTION_TLBS = 3, - EXCEPTION_ADEL = 4, // Address error on load - EXCEPTION_ADES = 5, // Address error on store - EXCEPTION_IBE = 6, // Instruction bus error - EXCEPTION_DBE = 7, // Data bus error - EXCEPTION_SYSCALL = 8, // System call - EXCEPTION_BP = 9, // Breakpoint - EXCEPTION_RI = 10, // Reserved instruction - EXCEPTION_COPU = 11, // Coprocessor unusable - EXCEPTION_OV = 12 // Arithmetic overflow - }; + template pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE; - uint32 Exception(uint32 code, uint32 PC, const uint32 NP, const uint32 NPM, const uint32 instr) MDFN_WARN_UNUSED_RESULT; + template T PeekMemory(uint32 address) MDFN_COLD; + template void PokeMemory(uint32 address, T value) MDFN_COLD; + template T ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24 = false, bool LWC_timing = false); + template void WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24 = false); - template pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE; + uint32 ReadInstruction(pscpu_timestamp_t ×tamp, uint32 address); - template T PeekMemory(uint32 address) MDFN_COLD; - template void PokeMemory(uint32 address, T value) MDFN_COLD; - template T ReadMemory(pscpu_timestamp_t ×tamp, uint32 address, bool DS24 = false, bool LWC_timing = false); - template void WriteMemory(pscpu_timestamp_t ×tamp, uint32 address, uint32 value, bool DS24 = false); + // + // Mednafen debugger stuff follows: + // + public: + 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* debug_GetScratchRAMPtr() { return ScratchRAM.data8; } + void* debug_GetGPRPtr() { return GPR; } + + enum + { + GSREG_GPR = 0, + GSREG_PC = 32, + GSREG_PC_NEXT, + GSREG_IN_BD_SLOT, + GSREG_LO, + GSREG_HI, + // + // + GSREG_BPC, + GSREG_BDA, + GSREG_TAR, + GSREG_DCIC, + GSREG_BADA, + GSREG_BDAM, + GSREG_BPCM, + GSREG_SR, + GSREG_CAUSE, + GSREG_EPC + }; + + uint32 GetRegister(unsigned int which, char *special, const uint32 special_len); + void SetRegister(unsigned int which, uint32 value); + bool PeekCheckICache(uint32 PC, uint32 *iw); - // - // Mednafen debugger stuff follows: - // - public: - 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* debug_GetScratchRAMPtr() { return ScratchRAM.data8; } - void* debug_GetGPRPtr() { return GPR; } + uint8 PeekMem8(uint32 A); + uint16 PeekMem16(uint32 A); + uint32 PeekMem32(uint32 A); - enum - { - GSREG_GPR = 0, - GSREG_PC = 32, - GSREG_PC_NEXT, - GSREG_IN_BD_SLOT, - GSREG_LO, - GSREG_HI, - GSREG_SR, - GSREG_CAUSE, - GSREG_EPC, - }; + void PokeMem8(uint32 A, uint8 V); + void PokeMem16(uint32 A, uint16 V); + void PokeMem32(uint32 A, uint32 V); - uint32 GetRegister(unsigned int which, char *special, const uint32 special_len); - void SetRegister(unsigned int which, uint32 value); - bool PeekCheckICache(uint32 PC, uint32 *iw); - - uint8 PeekMem8(uint32 A); - uint16 PeekMem16(uint32 A); - uint32 PeekMem32(uint32 A); - - void PokeMem8(uint32 A, uint8 V); - void PokeMem16(uint32 A, uint16 V); - void PokeMem32(uint32 A, uint32 V); - - private: - void(*CPUHook)(const pscpu_timestamp_t timestamp, uint32 pc); - void(*ADDBT)(uint32 from, uint32 to, bool exception); - }; + private: + void (*CPUHook)(const pscpu_timestamp_t timestamp, uint32 pc); + void (*ADDBT)(uint32 from, uint32 to, bool exception); +}; }