diff --git a/output/dll/octoshock.dll b/output/dll/octoshock.dll index 5e1ad60a3c..ff4b05eb8c 100644 Binary files a/output/dll/octoshock.dll and b/output/dll/octoshock.dll differ diff --git a/psx/octoshock/bizhawk/octoshock.vcxproj b/psx/octoshock/bizhawk/octoshock.vcxproj index 34e123e3ad..e4107108e7 100644 --- a/psx/octoshock/bizhawk/octoshock.vcxproj +++ b/psx/octoshock/bizhawk/octoshock.vcxproj @@ -93,11 +93,7 @@ - - - - diff --git a/psx/octoshock/bizhawk/octoshock.vcxproj.filters b/psx/octoshock/bizhawk/octoshock.vcxproj.filters index 060c5e61e5..81b017d989 100644 --- a/psx/octoshock/bizhawk/octoshock.vcxproj.filters +++ b/psx/octoshock/bizhawk/octoshock.vcxproj.filters @@ -229,21 +229,9 @@ psx - - psx - psx - - psx - - - psx - - - psx - psx diff --git a/psx/octoshock/docs/upstreaminfo.txt b/psx/octoshock/docs/upstreaminfo.txt index 4701abf64a..4ebc3719ad 100644 --- a/psx/octoshock/docs/upstreaminfo.txt +++ b/psx/octoshock/docs/upstreaminfo.txt @@ -31,25 +31,29 @@ [!!] psx/psx : LoadEXE/PSF1 changes (TODO - need these) [OK] psx/spu* : register renames and misc bugfixes 0.9.37-UNSTABLE -> 0.9.37.1 -[NO] psx/cpu : fix some savestate problem with ReadAbsorbDummy, seems alarming +[NO] psx/cpu : fix some savestate problem with ReadAbsorbDummy, seems alarming but maybe was just backwards compatibility [OK] psx/spu : clamp some sound output presumably messed up by prior reverb changes 0.9.37.1 -> 0.9.38-UNSTABLE [not integrated yet] (major CDIF changes) (simplefifo refactored) [!!] psx/cdutility : substantial revisions to disc sector synthesis with respect to leadout, "UDAPP", and "TSRE" (thread-safe re-entrant) which I dont understand. Need to study for DiscSystem. +[OK] required math_ops changes +[OK] SimpleFifo changed to PSX-only FastFIFO (TOD) [OK] psx/cdc : set SoftReset Mode to 0x20 [OK] psx/cdc : Edits to MakeStatus and BeginResults and ReadResult [OK] psx/cdc : in HandlePlayRead, handling of leadout sector synth moved to CDIF [OK] psx/cdc : in Update, Mode handling and reading of subcode changed +[OK] psx/cdc : in Update, reading of subcode changed. This isn't very important, it just saved reading the 2352 sector bytes when it wasn't needed. [OK] psx/cdc : error handling in Command_Setloc [OK] psx/cdc : PreSeekHack edits [OK] psx/cdc : Command_GetTD improvements [OK] psx/cdc : Command_SeekL timing changes -[OK] psx/cpu : helpful changes to case ranges and jump table +[OK] psx/cpu : helpful changes to case ranges and jump table. double check for savestate changes. [OK] psx/dma : easy bugfixes [??] psx/gpu : display timing changes ?? study more [OK] psx/gpu : BlitterFifo changes, related to FBWrite/Read [OK] psx/gpu : a bunch of bugfixes +[OK] psx/gpu : video standard mismatch warning suppressed during DisplayOff [OK] psx/gpu : be aware of savestate changes [OK] psx/gpu_line : easy bugfixes [OK] psx/gte : division bugfixes diff --git a/psx/octoshock/emuware/emuware.h b/psx/octoshock/emuware/emuware.h index fe96bb65fa..20704c062e 100644 --- a/psx/octoshock/emuware/emuware.h +++ b/psx/octoshock/emuware/emuware.h @@ -45,41 +45,6 @@ typedef __uint8_t uint8; #define final #define noexcept -#ifdef _MSC_VER -#include -//http://stackoverflow.com/questions/355967/how-to-use-msvc-intrinsics-to-get-the-equivalent-of-this-gcc-code -//if needed -//uint32_t __inline ctz( uint32_t value ) -//{ -// DWORD trailing_zero = 0; -// -// if ( _BitScanForward( &trailing_zero, value ) ) -// { -// return trailing_zero; -// } -// else -// { -// // This is undefined, I better choose 32 than 0 -// return 32; -// } -//} - -uint32 __inline __builtin_clz( uint32_t value ) -{ - unsigned long leading_zero = 0; - - if ( _BitScanReverse( &leading_zero, value ) ) - { - return 31 - leading_zero; - } - else - { - // Same remarks as above - return 32; - } -} -#endif - //#if MDFN_GCC_VERSION >= MDFN_MAKE_GCCV(4,7,0) // #define MDFN_ASSUME_ALIGNED(p, align) __builtin_assume_aligned((p), (align)) //#else diff --git a/psx/octoshock/math_ops.h b/psx/octoshock/math_ops.h index 09aa131365..de107b2856 100644 --- a/psx/octoshock/math_ops.h +++ b/psx/octoshock/math_ops.h @@ -1,6 +1,125 @@ #pragma once -#include "emuware/emuware.h" +#include + +// +// Result is defined for all possible inputs(including 0). +// +static INLINE unsigned MDFN_lzcount32(uint32 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return v ? __builtin_clz(v) : 32; + #elif defined(_MSC_VER) && defined(_WIN64) + unsigned long idx; + + if(!v) + return 32; + + _BitScanReverse(&idx, v); + + return 31 - idx; + #else + unsigned ret = 0; + + if(!v) + return(32); + + if(!(v & 0xFFFF0000)) + { + v <<= 16; + ret += 16; + } + + if(!(v & 0xFF000000)) + { + v <<= 8; + ret += 8; + } + + if(!(v & 0xF0000000)) + { + v <<= 4; + ret += 4; + } + + if(!(v & 0xC0000000)) + { + v <<= 2; + ret += 2; + } + + if(!(v & 0x80000000)) + { + v <<= 1; + ret += 1; + } + + return(ret); + #endif +} + +// +// Result is defined for all possible inputs(including 0). +// +static INLINE unsigned MDFN_lzcount64(uint64 v) +{ + #if defined(__GNUC__) || defined(__clang__) || defined(__ICC) || defined(__INTEL_COMPILER) + return v ? __builtin_clzll(v) : 64; + #elif defined(_MSC_VER) && defined(_WIN64) + unsigned long idx; + + if(!v) + return 64; + + _BitScanReverse64(&idx, v); + + return 63 - idx; + #else + unsigned ret = 0; + + if(!(v & 0xFFFFFFFFFFFFFFFFULL)) + return(64); + + if(!(v & 0xFFFFFFFF00000000ULL)) + { + v <<= 32; + ret += 32; + } + + if(!(v & 0xFFFF000000000000ULL)) + { + v <<= 16; + ret += 16; + } + + if(!(v & 0xFF00000000000000ULL)) + { + v <<= 8; + ret += 8; + } + + if(!(v & 0xF000000000000000ULL)) + { + v <<= 4; + ret += 4; + } + + if(!(v & 0xC000000000000000ULL)) + { + v <<= 2; + ret += 2; + } + + if(!(v & 0x8000000000000000ULL)) + { + v <<= 1; + ret += 1; + } + + return(ret); + #endif +} + // Source: http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 // Rounds up to the nearest power of 2. @@ -84,4 +203,3 @@ template static INLINE void clamp(T *val, U *val = maximum; } } - diff --git a/psx/octoshock/psx/FastFIFO.h b/psx/octoshock/psx/FastFIFO.h new file mode 100644 index 0000000000..de995fdf32 --- /dev/null +++ b/psx/octoshock/psx/FastFIFO.h @@ -0,0 +1,88 @@ +#ifndef __MDFN_FASTFIFO_H +#define __MDFN_FASTFIFO_H + +// size should be a power of 2. +template +class FastFIFO +{ + public: + + FastFIFO() + { + memset(data, 0, sizeof(data)); + read_pos = 0; + write_pos = 0; + in_count = 0; + } + + INLINE ~FastFIFO() + { + + } + + INLINE void SaveStatePostLoad(void) + { + read_pos %= size; + write_pos %= size; + in_count %= (size + 1); + } + + INLINE uint32 CanRead(void) + { + return(in_count); + } + + INLINE uint32 CanWrite(void) + { + return(size - in_count); + } + + INLINE T Peek(void) + { + return data[read_pos]; + } + + INLINE T Read(void) + { + T ret = data[read_pos]; + + read_pos = (read_pos + 1) & (size - 1); + in_count--; + + return(ret); + } + + INLINE void Write(const T& wr_data) + { + data[write_pos] = wr_data; + write_pos = (write_pos + 1) & (size - 1); + in_count++; + } + + INLINE void Flush(void) + { + read_pos = 0; + write_pos = 0; + in_count = 0; + } + + T data[size]; + uint32 read_pos; // Read position + uint32 write_pos; // Write position + uint32 in_count; // Number of units in the FIFO + + template void SyncState(EW::NewState *ns) + { + //I dont like this class... + + NSS(data); + NSS(read_pos); + NSS(write_pos); + NSS(in_count); + + SaveStatePostLoad(); + } +}; + + +#endif diff --git a/psx/octoshock/psx/cdc.cpp b/psx/octoshock/psx/cdc.cpp index 13e65c82db..cc20f13426 100644 --- a/psx/octoshock/psx/cdc.cpp +++ b/psx/octoshock/psx/cdc.cpp @@ -214,7 +214,7 @@ void PS_CDC::SoftReset(void) PendingCommandPhase = 0; PendingCommandCounter = 0; - Mode = 0; + Mode = 0x20; HeaderBufValid = false; DriveStatus = DS_STOPPED; @@ -375,67 +375,77 @@ void PS_CDC::WriteIRQ(uint8 V) RecalcIRQ(); } -void PS_CDC::BeginResults(void) -{ - //if(ResultsIn) - // { - // printf("Cleared %d results. IRQBuffer=0x%02x\n", ResultsIn, IRQBuffer); - //} - - // TODO: test semantics on real thing. - ResultsIn = 0; - ResultsWP = 0; - ResultsRP = 0; -} - -void PS_CDC::WriteResult(uint8 V) -{ - ResultsBuffer[ResultsWP] = V; - ResultsWP = (ResultsWP + 1) & 0xF; - ResultsIn = (ResultsIn + 1) & 0x1F; - - if(!ResultsIn) - PSX_WARNING("[CDC] Results buffer overflow!"); -} - -uint8 PS_CDC::ReadResult(void) -{ - uint8 ret = ResultsBuffer[ResultsRP]; - - ResultsRP = (ResultsRP + 1) & 0xF; - ResultsIn = (ResultsIn - 1) & 0x1F; - - return ret; -} - -uint8 PS_CDC::MakeStatus(bool cmd_error) -{ - uint8 ret = 0; - - // Are these bit positions right? - - if(DriveStatus == DS_PLAYING) - ret |= 0x80; - - if(DriveStatus == DS_SEEKING || DriveStatus == DS_SEEKING_LOGICAL) - ret |= 0x40; - - if(DriveStatus == DS_READING) - ret |= 0x20; - - // TODO: shell open and seek error - if(!Cur_disc || DiscChanged) - ret |= 0x10; - - if(DriveStatus != DS_STOPPED) - ret |= 0x02; - - if(cmd_error) - ret |= 0x01; - - DiscChanged = false; - - return(ret); +void PS_CDC::BeginResults(void) +{ + //if(ResultsIn) + // { + // printf("Cleared %d results. IRQBuffer=0x%02x\n", ResultsIn, IRQBuffer); + //} + + ResultsIn = 0; + ResultsWP = 0; + ResultsRP = 0; + + memset(ResultsBuffer, 0x00, sizeof(ResultsBuffer)); +} + +void PS_CDC::WriteResult(uint8 V) +{ + ResultsBuffer[ResultsWP] = V; + ResultsWP = (ResultsWP + 1) & 0xF; + ResultsIn = (ResultsIn + 1) & 0x1F; + + if(!ResultsIn) + PSX_WARNING("[CDC] Results buffer overflow!"); +} + +uint8 PS_CDC::ReadResult(void) +{ + uint8 ret = ResultsBuffer[ResultsRP]; + + if(!ResultsIn) + PSX_WARNING("[CDC] Results buffer underflow!"); + + ResultsRP = (ResultsRP + 1) & 0xF; + ResultsIn = (ResultsIn - 1) & 0x1F; + + return ret; +} + +uint8 PS_CDC::MakeStatus(bool cmd_error) +{ + uint8 ret = 0; + + // Are these bit positions right? + + if(DriveStatus == DS_PLAYING) + ret |= 0x80; + + // Probably will want to be careful with this HeaderBufValid versus seek/read bit business in the future as it is a bit fragile; + // "Gran Turismo 1"'s music(or erroneous lack of) is a good test case. + if(DriveStatus == DS_READING) + { + if(!HeaderBufValid) + ret |= 0x40; + else + ret |= 0x20; + } + else if(DriveStatus == DS_SEEKING || DriveStatus == DS_SEEKING_LOGICAL) + ret |= 0x40; + + // TODO: shell open and seek error + if(!Cur_CDIF || DiscChanged) + ret |= 0x10; + + if(DriveStatus != DS_STOPPED) + ret |= 0x02; + + if(cmd_error) + ret |= 0x01; + + DiscChanged = false; + + return(ret); } bool PS_CDC::DecodeSubQ(uint8 *subpw) @@ -463,13 +473,13 @@ bool PS_CDC::DecodeSubQ(uint8 *subpw) static const int16 CDADPCMImpulse[7][25] = { - { 0, -5, 17, -35, 70, -23, -68, 347, -839, 2062, -4681, 15367, 21472, -5882, 2810, -1352, 635, -235, 26, 43, -35, 16, -8, 2, 0, }, /* 0 */ - { 0, -2, 10, -34, 65, -84, 52, 9, -266, 1024, -2680, 9036, 26516, -6016, 3021, -1571, 848, -365, 107, 10, -16, 17, -8, 3, -1, }, /* 1 */ - { -2, 0, 3, -19, 60, -75, 162, -227, 306, -67, -615, 3229, 29883, -4532, 2488, -1471, 882, -424, 166, -27, 5, 6, -8, 3, -1, }, /* 2 */ - { -1, 3, -2, -5, 31, -74, 179, -402, 689, -926, 1272, -1446, 31033, -1446, 1272, -926, 689, -402, 179, -74, 31, -5, -2, 3, -1, }, /* 3 */ - { -1, 3, -8, 6, 5, -27, 166, -424, 882, -1471, 2488, -4532, 29883, 3229, -615, -67, 306, -227, 162, -75, 60, -19, 3, 0, -2, }, /* 4 */ - { -1, 3, -8, 17, -16, 10, 107, -365, 848, -1571, 3021, -6016, 26516, 9036, -2680, 1024, -266, 9, 52, -84, 65, -34, 10, -2, 0, }, /* 5 */ - { 0, 2, -8, 16, -35, 43, 26, -235, 635, -1352, 2810, -5882, 21472, 15367, -4681, 2062, -839, 347, -68, -23, 70, -35, 17, -5, 0, }, /* 6 */ + { 0, -5, 17, -35, 70, -23, -68, 347, -839, 2062, -4681, 15367, 21472, -5882, 2810, -1352, 635, -235, 26, 43, -35, 16, -8, 2, 0, }, /* 0 */ + { 0, -2, 10, -34, 65, -84, 52, 9, -266, 1024, -2680, 9036, 26516, -6016, 3021, -1571, 848, -365, 107, 10, -16, 17, -8, 3, -1, }, /* 1 */ + { -2, 0, 3, -19, 60, -75, 162, -227, 306, -67, -615, 3229, 29883, -4532, 2488, -1471, 882, -424, 166, -27, 5, 6, -8, 3, -1, }, /* 2 */ + { -1, 3, -2, -5, 31, -74, 179, -402, 689, -926, 1272, -1446, 31033, -1446, 1272, -926, 689, -402, 179, -74, 31, -5, -2, 3, -1, }, /* 3 */ + { -1, 3, -8, 6, 5, -27, 166, -424, 882, -1471, 2488, -4532, 29883, 3229, -615, -67, 306, -227, 162, -75, 60, -19, 3, 0, -2, }, /* 4 */ + { -1, 3, -8, 17, -16, 10, 107, -365, 848, -1571, 3021, -6016, 26516, 9036, -2680, 1024, -266, 9, 52, -84, 65, -34, 10, -2, 0, }, /* 5 */ + { 0, 2, -8, 16, -35, 43, 26, -235, 635, -1352, 2810, -5882, 21472, 15367, -4681, 2062, -839, 347, -68, -23, 70, -35, 17, -5, 0, }, /* 6 */ }; void PS_CDC::ReadAudioBuffer(int32 samples[2]) @@ -880,17 +890,11 @@ void PS_CDC::HandlePlayRead(void) if(CurSector >= (int32)toc.tracks[100].lba) { PSX_WARNING("[CDC] In leadout area: %u", CurSector); + //ZERO TODO - this is the critical point for testing leadout-reading. + } - // " Synthesis is a bit of a kludge " but we've taken it out of here - //synth_leadout_sector_lba(0x02, toc, CurSector, read_buf); - Cur_disc->ReadLBA2448(CurSector,read_buf); // FIXME: error out on error. - } - else - { - Cur_disc->ReadLBA2448(CurSector,read_buf); // FIXME: error out on error. - } - - DecodeSubQ(read_buf + 2352); + Cur_disc->ReadLBA2448(CurSector,read_buf); // FIXME: error out on error. + DecodeSubQ(read_buf + 2352); if(SubQBuf_Safe[1] == 0xAA && (DriveStatus == DS_PLAYING || (!(SubQBuf_Safe[0] & 0x40) && (Mode & MODE_CDDA)))) @@ -1109,7 +1113,7 @@ pscpu_timestamp_t PS_CDC::Update(const pscpu_timestamp_t timestamp) if(PSRCounter > 0) { - uint8 buf[2352 + 96]; + uint8 pwbuf[96]; PSRCounter -= chunk_clocks; @@ -1126,7 +1130,7 @@ pscpu_timestamp_t PS_CDC::Update(const pscpu_timestamp_t timestamp) SectorPipe_Pos = SectorPipe_In = 0; SectorsRead = 0; - Mode = 0; + Mode = 0x20; // Confirmed(and see "This Is Football 2"). CurSector = 0; CommandLoc = 0; @@ -1136,8 +1140,8 @@ pscpu_timestamp_t PS_CDC::Update(const pscpu_timestamp_t timestamp) else if(DriveStatus == DS_SEEKING) { CurSector = SeekTarget; - Cur_disc->ReadLBA2448(CurSector,buf); - DecodeSubQ(buf + 2352); + Cur_disc->ReadLBA_PW(pwbuf,CurSector,false); + DecodeSubQ(pwbuf); DriveStatus = StatusAfterSeek; @@ -1146,26 +1150,25 @@ pscpu_timestamp_t PS_CDC::Update(const pscpu_timestamp_t timestamp) PSRCounter = 33868800 / (75 * ((Mode & MODE_SPEED) ? 2 : 1)); } } - else if(DriveStatus == DS_SEEKING_LOGICAL) - { - CurSector = SeekTarget; - Cur_disc->ReadLBA2448(CurSector, buf); - DecodeSubQ(buf + 2352); - memcpy(HeaderBuf, buf + 12, 12); - - DriveStatus = StatusAfterSeek; - - if(DriveStatus != DS_PAUSED && DriveStatus != DS_STANDBY) - { - // TODO: SetAIP(CDCIRQ_DISC_ERROR, MakeStatus() | 0x04, 0x04); when !(Mode & MODE_CDDA) and the sector isn't a data sector. - PSRCounter = 33868800 / (75 * ((Mode & MODE_SPEED) ? 2 : 1)); - } - } - else if(DriveStatus == DS_READING || DriveStatus == DS_PLAYING) - { - HandlePlayRead(); - } - } + else if(DriveStatus == DS_SEEKING_LOGICAL) + { + CurSector = SeekTarget; + Cur_disc->ReadLBA_PW(pwbuf, CurSector, false); + DecodeSubQ(pwbuf); + + DriveStatus = StatusAfterSeek; + + if(DriveStatus != DS_PAUSED && DriveStatus != DS_STANDBY) + { + // TODO: SetAIP(CDCIRQ_DISC_ERROR, MakeStatus() | 0x04, 0x04); when !(Mode & MODE_CDDA) and the sector isn't a data sector. + PSRCounter = 33868800 / (75 * ((Mode & MODE_SPEED) ? 2 : 1)); + } + } + else if(DriveStatus == DS_READING || DriveStatus == DS_PLAYING) + { + HandlePlayRead(); + } + } } if(PendingCommandCounter > 0) @@ -1552,22 +1555,32 @@ int32 PS_CDC::Command_Nop(const int arg_count, const uint8 *args) return(0); } - -int32 PS_CDC::Command_Setloc(const int arg_count, const uint8 *args) -{ - uint8 m, s, f; - - m = BCD_to_U8(args[0] & 0x7F); - s = BCD_to_U8(args[1]); - f = BCD_to_U8(args[2]); - - CommandLoc = f + 75 * s + 75 * 60 * m - 150; - CommandLoc_Dirty = true; - - WriteResult(MakeStatus()); - WriteIRQ(CDCIRQ_ACKNOWLEDGE); - - return(0); + +int32 PS_CDC::Command_Setloc(const int arg_count, const uint8 *args) +{ + uint8 m, s, f; + + if((args[0] & 0x0F) > 0x09 || args[0] > 0x99 || + (args[1] & 0x0F) > 0x09 || args[1] > 0x59 || + (args[2] & 0x0F) > 0x09 || args[2] > 0x74) + { + WriteResult(MakeStatus(true)); + WriteResult(ERRCODE_BAD_ARGVAL); + WriteIRQ(CDCIRQ_DISC_ERROR); + return(0); + } + + m = BCD_to_U8(args[0]); + s = BCD_to_U8(args[1]); + f = BCD_to_U8(args[2]); + + CommandLoc = f + 75 * s + 75 * 60 * m - 150; + CommandLoc_Dirty = true; + + WriteResult(MakeStatus()); + WriteIRQ(CDCIRQ_ACKNOWLEDGE); + + return(0); } int32 PS_CDC::CalcSeekTime(int32 initial, int32 target, bool motor_on, bool paused) @@ -1624,34 +1637,27 @@ void PS_CDC::BeginSeek(uint32 target, int after_seek) } #endif -// Remove this function when we have better seek emulation; it's here because the Rockman complete works games(at least 2 and 4) apparently have finicky fubared CD -// access code. -void PS_CDC::PreSeekHack(bool logical, uint32 target) -{ - uint8 buf[2352 + 96]; - int max_try = 32; - bool NeedHBuf = logical; - - CurSector = target; // If removing/changing this, take into account how it will affect ReadN/ReadS/Play/etc command calls that interrupt a seek. - - // If removing this SubQ reading bit, think about how it will interact with a Read command of data(or audio :b) sectors when Mode bit0 is 1. - if(target < toc.tracks[100].lba) - { - do - { - Cur_disc->ReadLBA2448(target++, buf); - - // GetLocL related kludge, for Gran Turismo 1 music, perhaps others? - if(NeedHBuf) - { - NeedHBuf = false; - memcpy(HeaderBuf, buf + 12, 12); - HeaderBufValid = true; - } - } while(!DecodeSubQ(buf + 2352) && --max_try > 0 && target < toc.tracks[100].lba); - } -} - + +// Remove this function when we have better seek emulation; it's here because the Rockman complete works games(at least 2 and 4) apparently have finicky fubared CD +// access code. +void PS_CDC::PreSeekHack(int32 target) +{ + uint8 pwbuf[96]; + int max_try = 32; + + CurSector = target; // If removing/changing this, take into account how it will affect ReadN/ReadS/Play/etc command calls that interrupt a seek. + + // If removing this SubQ reading bit, think about how it will interact with a Read command of data(or audio :b) sectors when Mode bit0 is 1. + do + { + Cur_disc->ReadLBA_PW(pwbuf, target++, true); + } while(!DecodeSubQ(pwbuf) && --max_try > 0); +} + +/* + Play command with a track argument that's not a valid BCD quantity causes interesting half-buggy behavior on an actual PS1(unlike some of the other commands, + an error doesn't seem to be generated for a bad BCD argument). +*/ int32 PS_CDC::Command_Play(const int arg_count, const uint8 *args) { if(!CommandCheckDiscPresent()) @@ -1675,7 +1681,7 @@ int32 PS_CDC::Command_Play(const int arg_count, const uint8 *args) } else if(track > toc.last_track) { - PSX_WARNING("[CDC] Attempt to play track before first track."); + PSX_WARNING("[CDC] Attempt to play track after last track."); track = toc.last_track; } @@ -1690,7 +1696,7 @@ int32 PS_CDC::Command_Play(const int arg_count, const uint8 *args) SeekTarget = toc.tracks[track].lba; PSRCounter = CalcSeekTime(CurSector, SeekTarget, DriveStatus != DS_STOPPED, DriveStatus == DS_PAUSED); HeaderBufValid = false; - PreSeekHack(false, SeekTarget); + PreSeekHack(SeekTarget); ReportLastF = 0xFF; @@ -1712,7 +1718,7 @@ int32 PS_CDC::Command_Play(const int arg_count, const uint8 *args) PSRCounter = CalcSeekTime(CurSector, SeekTarget, DriveStatus != DS_STOPPED, DriveStatus == DS_PAUSED); HeaderBufValid = false; - PreSeekHack(false, SeekTarget); + PreSeekHack(SeekTarget); ReportLastF = 0xFF; @@ -1785,7 +1791,7 @@ void PS_CDC::ReadBase(void) PSRCounter = /*903168 * 1.5 +*/ CalcSeekTime(CurSector, SeekTarget, DriveStatus != DS_STOPPED, DriveStatus == DS_PAUSED); HeaderBufValid = false; - PreSeekHack(true, SeekTarget); + PreSeekHack(SeekTarget); DriveStatus = DS_SEEKING_LOGICAL; StatusAfterSeek = DS_READING; @@ -2062,59 +2068,60 @@ int32 PS_CDC::Command_GetTN(const int arg_count, const uint8 *args) return(0); } - -int32 PS_CDC::Command_GetTD(const int arg_count, const uint8 *args) -{ - if(!CommandCheckDiscPresent()) - return(0); - - int track; - uint8 m, s, f; - - if(!args[0] || args[0] == 0xAA) - track = 100; - else - { - track= BCD_to_U8(args[0]); - - if(track < toc.first_track || track > toc.last_track) // Error - { - WriteResult(MakeStatus(true)); - WriteIRQ(CDCIRQ_ACKNOWLEDGE); - return(0); - } - } - - LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f); - - WriteResult(MakeStatus()); - WriteResult(U8_to_BCD(m)); - WriteResult(U8_to_BCD(s)); - //WriteResult(U8_to_BCD(f)); - - WriteIRQ(CDCIRQ_ACKNOWLEDGE); - - return(0); + +int32 PS_CDC::Command_GetTD(const int arg_count, const uint8 *args) +{ + if(!CommandCheckDiscPresent()) + return(0); + + int track; + uint8 m, s, f; + + if(!args[0]) + track = 100; + else + { + track = BCD_to_U8(args[0]); + + if(!BCD_is_valid(args[0]) || track < toc.first_track || track > toc.last_track) // Error + { + WriteResult(MakeStatus(true)); + WriteResult(ERRCODE_BAD_ARGVAL); + WriteIRQ(CDCIRQ_DISC_ERROR); + return(0); + } + } + + LBA_to_AMSF(toc.tracks[track].lba, &m, &s, &f); + + WriteResult(MakeStatus()); + WriteResult(U8_to_BCD(m)); + WriteResult(U8_to_BCD(s)); + //WriteResult(U8_to_BCD(f)); + + WriteIRQ(CDCIRQ_ACKNOWLEDGE); + + return(0); } - -int32 PS_CDC::Command_SeekL(const int arg_count, const uint8 *args) -{ - if(!CommandCheckDiscPresent()) - return(0); - - WriteResult(MakeStatus()); - WriteIRQ(CDCIRQ_ACKNOWLEDGE); - - SeekTarget = CommandLoc; - - PSRCounter = CalcSeekTime(CurSector, SeekTarget, DriveStatus != DS_STOPPED, DriveStatus == DS_PAUSED); - HeaderBufValid = false; - PreSeekHack(true, SeekTarget); - DriveStatus = DS_SEEKING_LOGICAL; - StatusAfterSeek = DS_STANDBY; - ClearAIP(); - - return(PSRCounter); + +int32 PS_CDC::Command_SeekL(const int arg_count, const uint8 *args) +{ + if(!CommandCheckDiscPresent()) + return(0); + + WriteResult(MakeStatus()); + WriteIRQ(CDCIRQ_ACKNOWLEDGE); + + SeekTarget = CommandLoc; + + PSRCounter = (33868800 / (75 * ((Mode & MODE_SPEED) ? 2 : 1))) + CalcSeekTime(CurSector, SeekTarget, DriveStatus != DS_STOPPED, DriveStatus == DS_PAUSED); + HeaderBufValid = false; + PreSeekHack(SeekTarget); + DriveStatus = DS_SEEKING_LOGICAL; + StatusAfterSeek = DS_STANDBY; + ClearAIP(); + + return(PSRCounter); } int32 PS_CDC::Command_SeekP(const int arg_count, const uint8 *args) @@ -2129,7 +2136,7 @@ int32 PS_CDC::Command_SeekP(const int arg_count, const uint8 *args) PSRCounter = CalcSeekTime(CurSector, SeekTarget, DriveStatus != DS_STOPPED, DriveStatus == DS_PAUSED); HeaderBufValid = false; - PreSeekHack(false, SeekTarget); + PreSeekHack(SeekTarget); DriveStatus = DS_SEEKING; StatusAfterSeek = DS_STANDBY; ClearAIP(); diff --git a/psx/octoshock/psx/cdc.h b/psx/octoshock/psx/cdc.h index ff50155a0e..1b88ab7078 100644 --- a/psx/octoshock/psx/cdc.h +++ b/psx/octoshock/psx/cdc.h @@ -2,6 +2,7 @@ #define __MDFN_PSX_CDC_H #include "cdrom/CDUtility.h" +#include "cdrom/SimpleFIFO.h" class ShockDiscRef; @@ -232,8 +233,7 @@ class PS_CDC int32 (PS_CDC::*func2)(void); }; - void BeginSeek(uint32 target); - void PreSeekHack(bool logical, uint32 target); + void PreSeekHack(int32 target); void ReadBase(void); static CDC_CTEntry Commands[0x20]; diff --git a/psx/octoshock/psx/cpu.cpp b/psx/octoshock/psx/cpu.cpp index 463e4ade44..747b34141e 100644 --- a/psx/octoshock/psx/cpu.cpp +++ b/psx/octoshock/psx/cpu.cpp @@ -1,994 +1,2605 @@ -/* 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 "psx.h" -#include "cpu.h" -#include "gte.h" - -//not very organized, is it -void* g_ShockTraceCallbackOpaque = NULL; -ShockCallback_Trace g_ShockTraceCallback = NULL; - -/* 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_t region_address, uint32_t region_size) -{ - uint64_t A; - // FAST_MAP_SHIFT - // FAST_MAP_PSIZE - - for(A = region_address; A < (uint64)region_address + region_size; A += FAST_MAP_PSIZE) - FastMap[A >> FAST_MAP_SHIFT] = ((uint8_t *)region_mem - region_address); -} - -INLINE void PS_CPU::RecalcIPCache(void) -{ - IPCache = 0; - - if(((CP0.SR & CP0.CAUSE & 0xFF00) && (CP0.SR & 1)) || Halted) - IPCache = 0x80; -} - -void PS_CPU::SetHalt(bool status) -{ - Halted = status; - RecalcIPCache(); -} - -void PS_CPU::Power(void) -{ - unsigned i; - - 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(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_t val) -{ - unsigned i; - const uint32_t old_BIU = BIU; - - BIU = val & ~(0x440); - - if((BIU ^ old_BIU) & 0x800) - { - if(BIU & 0x800) // ICache enabled - { - for(i = 0; i < 1024; i++) - ICache[i].TV &= ~0x1; - } - else // ICache disabled - { - for(i = 0; i < 1024; i++) - ICache[i].TV |= 0x1; - } - } - - PSX_DBG(PSX_DBG_SPARSE, "[CPU] Set BIU=0x%08x\n", BIU); -} - -uint32_t PS_CPU::GetBIU(void) -{ - return BIU; -} - -static const uint32_t addr_mask[8] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, - 0x7FFFFFFF, 0x1FFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; - -template -INLINE T PS_CPU::PeekMemory(uint32_t 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 -INLINE T PS_CPU::ReadMemory(pscpu_timestamp_t ×tamp, uint32_t address, bool DS24, bool LWC_timing) -{ - T ret; - - //WriteAbsorb >>= WriteAbsorbMonkey * 8; - //WriteAbsorbCount -= WriteAbsorbMonkey; - //WriteAbsorbMonkey = WriteAbsorbCount; - - ReadAbsorb[ReadAbsorbWhich] = 0; - ReadAbsorbWhich = 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_t address, uint32_t value, bool DS24) -{ - 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_t 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); - } -} - -uint32_t PS_CPU::Exception(uint32_t code, uint32_t PC, const uint32_t NPM) -{ - const bool InBDSlot = !(NPM & 0x3); - uint32_t handler = 0x80000080; - - assert(code < 16); - - if(code != EXCEPTION_INT && code != EXCEPTION_BP && code != EXCEPTION_SYSCALL) - { - PSX_DBG(PSX_DBG_WARNING, "Exception: %08x @ PC=0x%08x(IBDS=%d) -- IPCache=0x%02x -- IPEND=0x%02x -- SR=0x%08x ; IRQC_Status=0x%04x -- IRQC_Mask=0x%04x\n", code, PC, InBDSlot, IPCache, (CP0.CAUSE >> 8) & 0xFF, 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(InBDSlot) - CP0.EPC -= 4; - - if(ADDBT) - ADDBT(PC, handler, true); - - // "Push" IEc and KUc(so that the new IEc and KUc are 0) - CP0.SR = (CP0.SR & ~0x3F) | ((CP0.SR << 2) & 0x3F); - - // Setup cause register - CP0.CAUSE &= 0x0000FF00; - CP0.CAUSE |= code << 2; - - // If EPC was adjusted -= 4 because we were in a branch delay slot, set the bit. - if(InBDSlot) - CP0.CAUSE |= 0x80000000; - - RecalcIPCache(); - - 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 GPR_DEPRES_BEGIN { uint8_t back = ReadAbsorb[0]; -#define GPR_DEP(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; } -#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_t PC; - register uint32_t new_PC; - register uint32_t new_PC_mask; - register uint32_t LDWhich; - register uint32_t 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_t instr; - uint32_t 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. - new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); - 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"); - - opf = instr & 0x3F; - - if(instr & (0x3F << 26)) - opf = 0x40 | (instr >> 26); - - opf |= IPCache; - -#if 0 - { - uint32_t 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, arg_op, arg_funct) { op_##name: /*assert( ((arg_op) ? (0x40 | (arg_op)) : (arg_funct)) == opf); */ -#define END_OPF goto OpDone; } - -#define DO_BRANCH(offset, mask) \ - { \ - if(ILHMode) \ - { \ - uint32_t old_PC = PC; \ - PC = (PC & new_PC_mask) + new_PC; \ - if(old_PC == ((PC & (mask)) + (offset))) \ - { \ - if(MDFN_densb(&FastMap[PC >> FAST_MAP_SHIFT][PC]) == 0) \ - { \ - if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */ \ - { \ - timestamp = next_event_ts; \ - } \ - } \ - } \ - } \ - else \ - PC = (PC & new_PC_mask) + new_PC; \ - new_PC = (offset); \ - new_PC_mask = (mask) & ~3; \ - /* Lower bits of new_PC_mask being clear signifies being in a branch delay slot. (overloaded behavior for performance) */ \ - \ - if(DebugMode && ADDBT) \ - { \ - ADDBT(PC, (PC & new_PC_mask) + 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_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32_t immediate = instr & 0xFFFF; /*printf(" rs=%02x(%08x), rt=%02x(%08x), immediate=(%08x) ", rs, GPR[rs], rt, GPR[rt], immediate);*/ -#define JTYPE uint32_t target = instr & ((1 << 26) - 1); /*printf(" target=(%08x) ", target);*/ -#define RTYPE uint32_t rs MDFN_NOWARN_UNUSED = (instr >> 21) & 0x1F; uint32_t rt MDFN_NOWARN_UNUSED = (instr >> 16) & 0x1F; uint32_t rd MDFN_NOWARN_UNUSED = (instr >> 11) & 0x1F; uint32_t 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(g_ShockTraceCallback) - { - char tmp[100]; - shock_Util_DisassembleMIPS(PC, instr, tmp, 100); - g_ShockTraceCallback(g_ShockTraceCallbackOpaque, PC, instr, tmp); - } - } - -#if 1 -#include "cpu_bigswitch.inc" -#else -#include "cpu_coputedgoto.inc" -#endif - -OpDone: ; - - PC = (PC & new_PC_mask) + new_PC; - new_PC_mask = ~0U; - new_PC = 4; - -SkipNPCStuff: ; - - //printf("\n"); - } - } while(MDFN_LIKELY(PSX_EventHandler(timestamp))); - - if(gte_ts_done > 0) - gte_ts_done -= timestamp; - - 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_t pc), void (*addbt)(uint32_t from, uint32_t to, bool exception)) -{ - ADDBT = addbt; - CPUHook = cpuh; -} - -uint32_t PS_CPU::GetRegister(unsigned int which, char *special, const uint32_t special_len) -{ - uint32_t ret = 0; - - if(which >= GSREG_GPR && which < (GSREG_GPR + 32)) - return GPR[which]; - - 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_t 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_t PC, uint32_t *iw) -{ - if(ICache[(PC & 0xFFC) >> 2].TV == PC) - { - *iw = ICache[(PC & 0xFFC) >> 2].Data; - return(true); - } - - return(false); -} - - -uint8_t PS_CPU::PeekMem8(uint32_t A) -{ - return PeekMemory(A); -} - -uint16_t PS_CPU::PeekMem16(uint32_t A) -{ - return PeekMemory(A); -} - -uint32_t PS_CPU::PeekMem32(uint32_t A) -{ - return PeekMemory(A); -} - - -#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_t address, unsigned int len), uint32_t instr) -{ - uint32_t 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_t address = GPR[rs] + immediate; - - callback(false, address, 1); - END_OPF; - - // - // LBU - Load Byte Unsigned - // - BEGIN_OPF(0x24, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(false, address, 1); - END_OPF; - - // - // LH - Load Halfword - // - BEGIN_OPF(0x21, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(false, address, 2); - END_OPF; - - // - // LHU - Load Halfword Unsigned - // - BEGIN_OPF(0x25, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(false, address, 2); - END_OPF; - - - // - // LW - Load Word - // - BEGIN_OPF(0x23, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(false, address, 4); - END_OPF; - - // - // SB - Store Byte - // - BEGIN_OPF(0x28, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(true, address, 1); - END_OPF; - - // - // SH - Store Halfword - // - BEGIN_OPF(0x29, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(true, address, 2); - END_OPF; - - // - // SW - Store Word - // - BEGIN_OPF(0x2B, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(true, address, 4); - END_OPF; - - // - // LWL - Load Word Left - // - BEGIN_OPF(0x22, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - do - { - callback(false, address, 1); - } while((address--) & 0x3); - - END_OPF; - - // - // SWL - Store Word Left - // - BEGIN_OPF(0x2A, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - do - { - callback(true, address, 1); - } while((address--) & 0x3); - - END_OPF; - - // - // LWR - Load Word Right - // - BEGIN_OPF(0x26, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - do - { - callback(false, address, 1); - } while((++address) & 0x3); - - END_OPF; - - // - // SWR - Store Word Right - // - BEGIN_OPF(0x2E, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - do - { - callback(true, address, 1); - } while((++address) & 0x3); - - END_OPF; - - // - // LWC2 - // - BEGIN_OPF(0x32, 0); - ITYPE; - uint32_t address = GPR[rs] + immediate; - - callback(false, address, 4); - END_OPF; - - // - // SWC2 - // - BEGIN_OPF(0x3A, 0); - ITYPE; - uint32_t 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(ReadAbsorbDummy); - NSS(ReadAbsorbWhich); - NSS(ReadFudge); - - NSS(ScratchRAM.data8); - -} //SYNCFUNC(CPU) - -} //namespace MDFN_IEN_PSX +/* 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" + +//not very organized, is it +void* g_ShockTraceCallbackOpaque = NULL; +ShockCallback_Trace g_ShockTraceCallback = NULL; + +/* 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); +} + +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; + + 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(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); + } +} + +uint32 PS_CPU::Exception(uint32 code, uint32 PC, const uint32 NPM) +{ + const bool InBDSlot = !(NPM & 0x3); + uint32 handler = 0x80000080; + + assert(code < 16); + + if(code != EXCEPTION_INT && code != EXCEPTION_BP && code != EXCEPTION_SYSCALL) + { + PSX_DBG(PSX_DBG_WARNING, "Exception: %08x @ PC=0x%08x(IBDS=%d) -- IPCache=0x%02x -- IPEND=0x%02x -- SR=0x%08x ; IRQC_Status=0x%04x -- IRQC_Mask=0x%04x\n", code, PC, InBDSlot, IPCache, (CP0.CAUSE >> 8) & 0xFF, 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(InBDSlot) + CP0.EPC -= 4; + + if(ADDBT) + ADDBT(PC, handler, true); + + // "Push" IEc and KUc(so that the new IEc and KUc are 0) + CP0.SR = (CP0.SR & ~0x3F) | ((CP0.SR << 2) & 0x3F); + + // Setup cause register + CP0.CAUSE &= 0x0000FF00; + CP0.CAUSE |= code << 2; + + // If EPC was adjusted -= 4 because we were in a branch delay slot, set the bit. + if(InBDSlot) + CP0.CAUSE |= 0x80000000; + + RecalcIPCache(); + + 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 GPR_DEPRES_BEGIN { uint8 back = ReadAbsorb[0]; +#define GPR_DEP(n) { unsigned tn = (n); ReadAbsorb[tn] = 0; } +#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. + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); + 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"); + + 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, arg_op, arg_funct) { op_##name: /*assert( ((arg_op) ? (0x40 | (arg_op)) : (arg_funct)) == opf); */ + #define END_OPF goto OpDone; } + + #define DO_BRANCH(offset, mask) \ + { \ + if(ILHMode) \ + { \ + uint32 old_PC = PC; \ + PC = (PC & new_PC_mask) + new_PC; \ + if(old_PC == ((PC & (mask)) + (offset))) \ + { \ + if(MDFN_densb(&FastMap[PC >> FAST_MAP_SHIFT][PC]) == 0) \ + { \ + if(next_event_ts > timestamp) /* Necessary since next_event_ts might be set to something like "0" to force a call to the event handler. */ \ + { \ + timestamp = next_event_ts; \ + } \ + } \ + } \ + } \ + else \ + PC = (PC & new_PC_mask) + new_PC; \ + new_PC = (offset); \ + new_PC_mask = (mask) & ~3; \ + /* Lower bits of new_PC_mask being clear signifies being in a branch delay slot. (overloaded behavior for performance) */ \ + \ + if(DebugMode && ADDBT) \ + { \ + ADDBT(PC, (PC & new_PC_mask) + 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 + + 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) + CGE(op_JR) CGE(op_JALR) CGE(op_ILL) CGE(op_ILL) CGE(op_SYSCALL) CGE(op_BREAK) CGE(op_ILL) CGE(op_ILL) + CGE(op_MFHI) CGE(op_MTHI) CGE(op_MFLO) CGE(op_MTLO) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_MULT) CGE(op_MULTU) CGE(op_DIV) CGE(op_DIVU) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ADD) CGE(op_ADDU) CGE(op_SUB) CGE(op_SUBU) CGE(op_AND) CGE(op_OR) CGE(op_XOR) CGE(op_NOR) + CGE(op_ILL) CGE(op_ILL) CGE(op_SLT) CGE(op_SLTU) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + + CGE(op_ILL) CGE(op_BCOND) CGE(op_J) CGE(op_JAL) CGE(op_BEQ) CGE(op_BNE) CGE(op_BLEZ) CGE(op_BGTZ) + CGE(op_ADDI) CGE(op_ADDIU) CGE(op_SLTI) CGE(op_SLTIU) CGE(op_ANDI) CGE(op_ORI) CGE(op_XORI) CGE(op_LUI) + CGE(op_COP0) CGE(op_COP1) CGE(op_COP2) CGE(op_COP3) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_LB) CGE(op_LH) CGE(op_LWL) CGE(op_LW) CGE(op_LBU) CGE(op_LHU) CGE(op_LWR) CGE(op_ILL) + CGE(op_SB) CGE(op_SH) CGE(op_SWL) CGE(op_SW) CGE(op_ILL) CGE(op_ILL) CGE(op_SWR) CGE(op_ILL) + CGE(op_LWC0) CGE(op_LWC1) CGE(op_LWC2) CGE(op_LWC3) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + CGE(op_SWC0) CGE(op_SWC1) CGE(op_SWC2) CGE(op_SWC3) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) CGE(op_ILL) + + // Interrupt portion of this table is constructed so that an interrupt won't be taken when the PC is pointing to a GTE instruction, + // to avoid problems caused by pipeline vs coprocessor nuances that aren't emulated. + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + + CGE(op_ILL) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_COP2) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) CGE(op_INTERRUPT) + CGEND + + { + BEGIN_OPF(ILL, 0, 0); + 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_mask); + new_PC_mask = 0; + END_OPF; + + // + // ADD - Add Word + // + BEGIN_OPF(ADD, 0, 0x20); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] + GPR[rt]; + bool ep = ((~(GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000; + + DO_LDS(); + + if(MDFN_UNLIKELY(ep)) + { + new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask); + new_PC_mask = 0; + } + else + GPR[rd] = result; + + END_OPF; + + // + // ADDI - Add Immediate Word + // + BEGIN_OPF(ADDI, 0x08, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] + immediate; + bool ep = ((~(GPR[rs] ^ immediate)) & (GPR[rs] ^ result)) & 0x80000000; + + DO_LDS(); + + if(MDFN_UNLIKELY(ep)) + { + new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask); + new_PC_mask = 0; + } + else + GPR[rt] = result; + + END_OPF; + + // + // ADDIU - Add Immediate Unsigned Word + // + BEGIN_OPF(ADDIU, 0x09, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] + immediate; + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + // + // ADDU - Add Unsigned Word + // + BEGIN_OPF(ADDU, 0, 0x21); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] + GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // AND - And + // + BEGIN_OPF(AND, 0, 0x24); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] & GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // ANDI - And Immediate + // + BEGIN_OPF(ANDI, 0x0C, 0); + ITYPE_ZE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] & immediate; + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + // + // BEQ - Branch on Equal + // + BEGIN_OPF(BEQ, 0x04, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + bool result = (GPR[rs] == GPR[rt]); + + DO_LDS(); + + if(result) + { + DO_BRANCH((immediate << 2), ~0U); + } + END_OPF; + + // Bah, why does MIPS encoding have to be funky like this. :( + // Handles BGEZ, BGEZAL, BLTZ, BLTZAL + BEGIN_OPF(BCOND, 0x01, 0); + const uint32 tv = GPR[(instr >> 21) & 0x1F]; + uint32 riv = (instr >> 16) & 0x1F; + uint32 immediate = (int32)(int16)(instr & 0xFFFF); + bool result = (int32)(tv ^ (riv << 31)) < 0; + + GPR_DEPRES_BEGIN + GPR_DEP((instr >> 21) & 0x1F); + + if(riv & 0x10) + GPR_RES(31); + + GPR_DEPRES_END + + + DO_LDS(); + + if(riv & 0x10) // Unconditional link reg setting. + GPR[31] = PC + 8; + + if(result) + { + DO_BRANCH((immediate << 2), ~0U); + } + + END_OPF; + + + // + // BGTZ - Branch on Greater than Zero + // + BEGIN_OPF(BGTZ, 0x07, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + bool result = (int32)GPR[rs] > 0; + + DO_LDS(); + + if(result) + { + DO_BRANCH((immediate << 2), ~0U); + } + END_OPF; + + // + // BLEZ - Branch on Less Than or Equal to Zero + // + BEGIN_OPF(BLEZ, 0x06, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + bool result = (int32)GPR[rs] <= 0; + + DO_LDS(); + + if(result) + { + DO_BRANCH((immediate << 2), ~0U); + } + + END_OPF; + + // + // BNE - Branch on Not Equal + // + BEGIN_OPF(BNE, 0x05, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + bool result = GPR[rs] != GPR[rt]; + + DO_LDS(); + + if(result) + { + DO_BRANCH((immediate << 2), ~0U); + } + + END_OPF; + + // + // BREAK - Breakpoint + // + BEGIN_OPF(BREAK, 0, 0x0D); + PSX_WARNING("[CPU] BREAK BREAK BREAK BREAK DAAANCE -- PC=0x%08x", PC); + + DO_LDS(); + new_PC = Exception(EXCEPTION_BP, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + // Cop "instructions": CFCz(no CP0), COPz, CTCz(no CP0), LWCz(no CP0), MFCz, MTCz, SWCz(no CP0) + // + // COP0 instructions + BEGIN_OPF(COP0, 0x10, 0); + uint32 sub_op = (instr >> 21) & 0x1F; + + if(sub_op & 0x10) + sub_op = 0x10 + (instr & 0x3F); + + //printf("COP0 thing: %02x\n", sub_op); + switch(sub_op) + { + default: + DO_LDS(); + break; + + case 0x00: // MFC0 - Move from Coprocessor + { + uint32 rt = (instr >> 16) & 0x1F; + uint32 rd = (instr >> 11) & 0x1F; + + //printf("MFC0: rt=%d <- rd=%d(%08x)\n", rt, rd, CP0.Regs[rd]); + DO_LDS(); + + LDAbsorb = 0; + LDWhich = rt; + LDValue = CP0.Regs[rd]; + } + break; + + case 0x04: // MTC0 - Move to Coprocessor + { + uint32 rt = (instr >> 16) & 0x1F; + uint32 rd = (instr >> 11) & 0x1F; + uint32 val = GPR[rt]; + + if(rd != CP0REG_PRID && rd != CP0REG_CAUSE && rd != CP0REG_SR && val) + { + PSX_WARNING("[CPU] Unimplemented MTC0: rt=%d(%08x) -> rd=%d", rt, GPR[rt], rd); + } + + switch(rd) + { + case CP0REG_BPC: + CP0.BPC = val; + break; + + case CP0REG_BDA: + CP0.BDA = val; + break; + + case CP0REG_TAR: + CP0.TAR = val; + break; + + case CP0REG_DCIC: + CP0.DCIC = val & 0xFF80003F; + break; + + case CP0REG_BDAM: + CP0.BDAM = val; + break; + + case CP0REG_BPCM: + CP0.BPCM = val; + break; + + case CP0REG_CAUSE: + CP0.CAUSE &= ~(0x3 << 8); + CP0.CAUSE |= val & (0x3 << 8); + RecalcIPCache(); + 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; + } + } + DO_LDS(); + break; + + case (0x10 + 0x10): // RFE + // "Pop" + DO_LDS(); + CP0.SR = (CP0.SR & ~0x0F) | ((CP0.SR >> 2) & 0x0F); + RecalcIPCache(); + break; + } + END_OPF; + + // + // COP1 + // + BEGIN_OPF(COP1, 0x11, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + // + // COP2 + // + BEGIN_OPF(COP2, 0x12, 0); + uint32 sub_op = (instr >> 21) & 0x1F; + + switch(sub_op) + { + default: + DO_LDS(); + break; + + case 0x00: // MFC2 - Move from Coprocessor + { + uint32 rt = (instr >> 16) & 0x1F; + uint32 rd = (instr >> 11) & 0x1F; + + DO_LDS(); + + if(timestamp < gte_ts_done) + { + LDAbsorb = gte_ts_done - timestamp; + timestamp = gte_ts_done; + } + else + LDAbsorb = 0; + + LDWhich = rt; + LDValue = GTE_ReadDR(rd); + } + break; + + case 0x04: // MTC2 - Move to Coprocessor + { + uint32 rt = (instr >> 16) & 0x1F; + uint32 rd = (instr >> 11) & 0x1F; + uint32 val = GPR[rt]; + + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + //printf("GTE WriteDR: %d %d\n", rd, val); + GTE_WriteDR(rd, val); + DO_LDS(); + } + break; + + case 0x02: // CFC2 + { + uint32 rt = (instr >> 16) & 0x1F; + uint32 rd = (instr >> 11) & 0x1F; + + DO_LDS(); + + if(timestamp < gte_ts_done) + { + LDAbsorb = gte_ts_done - timestamp; + timestamp = gte_ts_done; + } + else + LDAbsorb = 0; + + LDWhich = rt; + LDValue = GTE_ReadCR(rd); + + //printf("GTE ReadCR: %d %d\n", rd, GPR[rt]); + } + break; + + case 0x06: // CTC2 + { + uint32 rt = (instr >> 16) & 0x1F; + uint32 rd = (instr >> 11) & 0x1F; + uint32 val = GPR[rt]; + + //printf("GTE WriteCR: %d %d\n", rd, val); + + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + GTE_WriteCR(rd, val); + DO_LDS(); + } + break; + + case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x16: case 0x17: + case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1E: case 0x1F: + //printf("%08x\n", PC); + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + gte_ts_done = timestamp + GTE_Instruction(instr); + DO_LDS(); + break; + } + END_OPF; + + // + // COP3 + // + BEGIN_OPF(COP3, 0x13, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + // + // LWC0 + // + BEGIN_OPF(LWC0, 0x30, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + // + // LWC1 + // + BEGIN_OPF(LWC1, 0x31, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + // + // LWC2 + // + BEGIN_OPF(LWC2, 0x32, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + if(MDFN_UNLIKELY(address & 3)) + { + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); + new_PC_mask = 0; + } + else + { + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + GTE_WriteDR(rt, ReadMemory(timestamp, address, false, true)); + } + // GTE stuff here + END_OPF; + + // + // LWC3 + // + BEGIN_OPF(LWC3, 0x33, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + + // + // SWC0 + // + BEGIN_OPF(SWC0, 0x38, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + // + // SWC1 + // + BEGIN_OPF(SWC1, 0x39, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_COPU, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + // + // SWC2 + // + BEGIN_OPF(SWC2, 0x3A, 0); + ITYPE; + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 0x3)) + { + new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask); + new_PC_mask = 0; + } + else + { + if(timestamp < gte_ts_done) + timestamp = gte_ts_done; + + WriteMemory(timestamp, address, GTE_ReadDR(rt)); + } + DO_LDS(); + END_OPF; + + // + // SWC3 + /// + BEGIN_OPF(SWC3, 0x3B, 0); + DO_LDS(); + new_PC = Exception(EXCEPTION_RI, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + + // + // DIV - Divide Word + // + BEGIN_OPF(DIV, 0, 0x1A); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + if(!GPR[rt]) + { + if(GPR[rs] & 0x80000000) + LO = 1; + else + LO = 0xFFFFFFFF; + + HI = GPR[rs]; + } + else if(GPR[rs] == 0x80000000 && GPR[rt] == 0xFFFFFFFF) + { + LO = 0x80000000; + HI = 0; + } + else + { + LO = (int32)GPR[rs] / (int32)GPR[rt]; + HI = (int32)GPR[rs] % (int32)GPR[rt]; + } + muldiv_ts_done = timestamp + 37; + + DO_LDS(); + + END_OPF; + + + // + // DIVU - Divide Unsigned Word + // + BEGIN_OPF(DIVU, 0, 0x1B); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + if(!GPR[rt]) + { + LO = 0xFFFFFFFF; + HI = GPR[rs]; + } + else + { + LO = GPR[rs] / GPR[rt]; + HI = GPR[rs] % GPR[rt]; + } + muldiv_ts_done = timestamp + 37; + + DO_LDS(); + END_OPF; + + // + // J - Jump + // + BEGIN_OPF(J, 0x02, 0); + JTYPE; + + DO_LDS(); + + DO_BRANCH(target << 2, 0xF0000000); + END_OPF; + + // + // JAL - Jump and Link + // + BEGIN_OPF(JAL, 0x03, 0); + JTYPE; + + //GPR_DEPRES_BEGIN + GPR_RES(31); + //GPR_DEPRES_END + + DO_LDS(); + + GPR[31] = PC + 8; + + DO_BRANCH(target << 2, 0xF0000000); + END_OPF; + + // + // JALR - Jump and Link Register + // + BEGIN_OPF(JALR, 0, 0x09); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 tmp = GPR[rs]; + + DO_LDS(); + + GPR[rd] = PC + 8; + + DO_BRANCH(tmp, 0); + + END_OPF; + + // + // JR - Jump Register + // + BEGIN_OPF(JR, 0, 0x08); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 bt = GPR[rs]; + + DO_LDS(); + + DO_BRANCH(bt, 0); + + END_OPF; + + // + // LUI - Load Upper Immediate + // + BEGIN_OPF(LUI, 0x0F, 0); + ITYPE_ZE; // Actually, probably would be sign-extending...if we were emulating a 64-bit MIPS chip :b + + GPR_DEPRES_BEGIN + GPR_RES(rt); + GPR_DEPRES_END + + DO_LDS(); + + GPR[rt] = immediate << 16; + + END_OPF; + + // + // MFHI - Move from HI + // + BEGIN_OPF(MFHI, 0, 0x10); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_RES(rd); + GPR_DEPRES_END + + DO_LDS(); + + if(timestamp < muldiv_ts_done) + { + if(timestamp == muldiv_ts_done - 1) + muldiv_ts_done--; + else + { + do + { + if(ReadAbsorb[ReadAbsorbWhich]) + ReadAbsorb[ReadAbsorbWhich]--; + timestamp++; + } while(timestamp < muldiv_ts_done); + } + } + + GPR[rd] = HI; + + END_OPF; + + + // + // MFLO - Move from LO + // + BEGIN_OPF(MFLO, 0, 0x12); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_RES(rd); + GPR_DEPRES_END + + DO_LDS(); + + if(timestamp < muldiv_ts_done) + { + if(timestamp == muldiv_ts_done - 1) + muldiv_ts_done--; + else + { + do + { + if(ReadAbsorb[ReadAbsorbWhich]) + ReadAbsorb[ReadAbsorbWhich]--; + timestamp++; + } while(timestamp < muldiv_ts_done); + } + } + + GPR[rd] = LO; + + END_OPF; + + + // + // MTHI - Move to HI + // + BEGIN_OPF(MTHI, 0, 0x11); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + HI = GPR[rs]; + + DO_LDS(); + + END_OPF; + + // + // MTLO - Move to LO + // + BEGIN_OPF(MTLO, 0, 0x13); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + LO = GPR[rs]; + + DO_LDS(); + + END_OPF; + + + // + // MULT - Multiply Word + // + BEGIN_OPF(MULT, 0, 0x18); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint64 result; + + result = (int64)(int32)GPR[rs] * (int32)GPR[rt]; + muldiv_ts_done = timestamp + MULT_Tab24[MDFN_lzcount32((GPR[rs] ^ ((int32)GPR[rs] >> 31)) | 0x400)]; + DO_LDS(); + + LO = result; + HI = result >> 32; + + END_OPF; + + // + // MULTU - Multiply Unsigned Word + // + BEGIN_OPF(MULTU, 0, 0x19); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint64 result; + + result = (uint64)GPR[rs] * GPR[rt]; + muldiv_ts_done = timestamp + MULT_Tab24[MDFN_lzcount32(GPR[rs] | 0x400)]; + DO_LDS(); + + LO = result; + HI = result >> 32; + + END_OPF; + + + // + // NOR - NOR + // + BEGIN_OPF(NOR, 0, 0x27); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = ~(GPR[rs] | GPR[rt]); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // OR - OR + // + BEGIN_OPF(OR, 0, 0x25); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] | GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // ORI - OR Immediate + // + BEGIN_OPF(ORI, 0x0D, 0); + ITYPE_ZE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] | immediate; + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + + // + // SLL - Shift Word Left Logical + // + BEGIN_OPF(SLL, 0, 0x00); // SLL + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] << shamt; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SLLV - Shift Word Left Logical Variable + // + BEGIN_OPF(SLLV, 0, 0x04); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] << (GPR[rs] & 0x1F); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // SLT - Set on Less Than + // + BEGIN_OPF(SLT, 0, 0x2A); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = (bool)((int32)GPR[rs] < (int32)GPR[rt]); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SLTI - Set on Less Than Immediate + // + BEGIN_OPF(SLTI, 0x0A, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = (bool)((int32)GPR[rs] < (int32)immediate); + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + + // + // SLTIU - Set on Less Than Immediate, Unsigned + // + BEGIN_OPF(SLTIU, 0x0B, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = (bool)(GPR[rs] < (uint32)immediate); + + DO_LDS(); + + GPR[rt] = result; + + END_OPF; + + + // + // SLTU - Set on Less Than, Unsigned + // + BEGIN_OPF(SLTU, 0, 0x2B); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = (bool)(GPR[rs] < GPR[rt]); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SRA - Shift Word Right Arithmetic + // + BEGIN_OPF(SRA, 0, 0x03); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = ((int32)GPR[rt]) >> shamt; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SRAV - Shift Word Right Arithmetic Variable + // + BEGIN_OPF(SRAV, 0, 0x07); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = ((int32)GPR[rt]) >> (GPR[rs] & 0x1F); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SRL - Shift Word Right Logical + // + BEGIN_OPF(SRL, 0, 0x02); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] >> shamt; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // SRLV - Shift Word Right Logical Variable + // + BEGIN_OPF(SRLV, 0, 0x06); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rt] >> (GPR[rs] & 0x1F); + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SUB - Subtract Word + // + BEGIN_OPF(SUB, 0, 0x22); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] - GPR[rt]; + bool ep = (((GPR[rs] ^ GPR[rt])) & (GPR[rs] ^ result)) & 0x80000000; + + DO_LDS(); + + if(MDFN_UNLIKELY(ep)) + { + new_PC = Exception(EXCEPTION_OV, PC, new_PC_mask); + new_PC_mask = 0; + } + else + GPR[rd] = result; + + END_OPF; + + + // + // SUBU - Subtract Unsigned Word + // + BEGIN_OPF(SUBU, 0, 0x23); // SUBU + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] - GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + + // + // SYSCALL + // + BEGIN_OPF(SYSCALL, 0, 0x0C); + DO_LDS(); + + new_PC = Exception(EXCEPTION_SYSCALL, PC, new_PC_mask); + new_PC_mask = 0; + END_OPF; + + + // + // XOR + // + BEGIN_OPF(XOR, 0, 0x26); + RTYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_RES(rd); + GPR_DEPRES_END + + uint32 result = GPR[rs] ^ GPR[rt]; + + DO_LDS(); + + GPR[rd] = result; + + END_OPF; + + // + // XORI - Exclusive OR Immediate + // + BEGIN_OPF(XORI, 0x0E, 0); + ITYPE_ZE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_RES(rt); + GPR_DEPRES_END + + uint32 result = GPR[rs] ^ immediate; + + DO_LDS(); + + GPR[rt] = result; + END_OPF; + + // + // Memory access instructions(besides the coprocessor ones) follow: + // + + // + // LB - Load Byte + // + BEGIN_OPF(LB, 0x20, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + LDWhich = rt; + LDValue = (int32)ReadMemory(timestamp, address); + END_OPF; + + // + // LBU - Load Byte Unsigned + // + BEGIN_OPF(LBU, 0x24, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + LDWhich = rt; + LDValue = ReadMemory(timestamp, address); + END_OPF; + + // + // LH - Load Halfword + // + BEGIN_OPF(LH, 0x21, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + if(MDFN_UNLIKELY(address & 1)) + { + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); + new_PC_mask = 0; + } + else + { + LDWhich = rt; + LDValue = (int32)ReadMemory(timestamp, address); + } + END_OPF; + + // + // LHU - Load Halfword Unsigned + // + BEGIN_OPF(LHU, 0x25, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + if(MDFN_UNLIKELY(address & 1)) + { + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); + new_PC_mask = 0; + } + else + { + LDWhich = rt; + LDValue = ReadMemory(timestamp, address); + } + END_OPF; + + + // + // LW - Load Word + // + BEGIN_OPF(LW, 0x23, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + DO_LDS(); + + if(MDFN_UNLIKELY(address & 3)) + { + new_PC = Exception(EXCEPTION_ADEL, PC, new_PC_mask); + new_PC_mask = 0; + } + else + { + LDWhich = rt; + LDValue = ReadMemory(timestamp, address); + } + END_OPF; + + // + // SB - Store Byte + // + BEGIN_OPF(SB, 0x28, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + WriteMemory(timestamp, address, GPR[rt]); + + DO_LDS(); + END_OPF; + + // + // SH - Store Halfword + // + BEGIN_OPF(SH, 0x29, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 0x1)) + { + new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask); + new_PC_mask = 0; + } + else + WriteMemory(timestamp, address, GPR[rt]); + + DO_LDS(); + END_OPF; + + // + // SW - Store Word + // + BEGIN_OPF(SW, 0x2B, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + if(MDFN_UNLIKELY(address & 0x3)) + { + new_PC = Exception(EXCEPTION_ADES, PC, new_PC_mask); + new_PC_mask = 0; + } + else + WriteMemory(timestamp, address, GPR[rt]); + + DO_LDS(); + END_OPF; + + // LWL and LWR load delay slot tomfoolery appears to apply even to MFC0! (and probably MFCn and CFCn as well, though they weren't explicitly tested) + + // + // LWL - Load Word Left + // + BEGIN_OPF(LWL, 0x22, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + //GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + uint32 v = GPR[rt]; + + if(LDWhich == rt) + { + v = LDValue; + ReadFudge = 0; + } + else + { + DO_LDS(); + } + + LDWhich = rt; + switch(address & 0x3) + { + case 0: LDValue = (v & ~(0xFF << 24)) | (ReadMemory(timestamp, address & ~3) << 24); + break; + + case 1: LDValue = (v & ~(0xFFFF << 16)) | (ReadMemory(timestamp, address & ~3) << 16); + break; + + case 2: LDValue = (v & ~(0xFFFFFF << 8)) | (ReadMemory(timestamp, address & ~3, true) << 8); + break; + + case 3: LDValue = (v & ~(0xFFFFFFFF << 0)) | (ReadMemory(timestamp, address & ~3) << 0); + break; + } + END_OPF; + + // + // SWL - Store Word Left + // + BEGIN_OPF(SWL, 0x2A, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + switch(address & 0x3) + { + case 0: WriteMemory(timestamp, address & ~3, GPR[rt] >> 24); + break; + + case 1: WriteMemory(timestamp, address & ~3, GPR[rt] >> 16); + break; + + case 2: WriteMemory(timestamp, address & ~3, GPR[rt] >> 8, true); + break; + + case 3: WriteMemory(timestamp, address & ~3, GPR[rt] >> 0); + break; + } + DO_LDS(); + + END_OPF; + + // + // LWR - Load Word Right + // + BEGIN_OPF(LWR, 0x26, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + //GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + uint32 v = GPR[rt]; + + if(LDWhich == rt) + { + v = LDValue; + ReadFudge = 0; + } + else + { + DO_LDS(); + } + + LDWhich = rt; + switch(address & 0x3) + { + case 0: LDValue = (v & ~(0xFFFFFFFF)) | ReadMemory(timestamp, address); + break; + + case 1: LDValue = (v & ~(0xFFFFFF)) | ReadMemory(timestamp, address, true); + break; + + case 2: LDValue = (v & ~(0xFFFF)) | ReadMemory(timestamp, address); + break; + + case 3: LDValue = (v & ~(0xFF)) | ReadMemory(timestamp, address); + break; + } + END_OPF; + + // + // SWR - Store Word Right + // + BEGIN_OPF(SWR, 0x2E, 0); + ITYPE; + + GPR_DEPRES_BEGIN + GPR_DEP(rs); + GPR_DEP(rt); + GPR_DEPRES_END + + uint32 address = GPR[rs] + immediate; + + switch(address & 0x3) + { + case 0: WriteMemory(timestamp, address, GPR[rt]); + break; + + case 1: WriteMemory(timestamp, address, GPR[rt], true); + break; + + case 2: WriteMemory(timestamp, address, GPR[rt]); + break; + + case 3: WriteMemory(timestamp, address, GPR[rt]); + break; + } + + DO_LDS(); + + END_OPF; + + // + // Mednafen special instruction + // + BEGIN_OPF(INTERRUPT, 0x3F, 0); + if(Halted) + { + goto SkipNPCStuff; + } + else + { + DO_LDS(); + + new_PC = Exception(EXCEPTION_INT, PC, new_PC_mask); + new_PC_mask = 0; + } + END_OPF; + } + + OpDone: ; + + PC = (PC & new_PC_mask) + new_PC; + new_PC_mask = ~0U; + new_PC = 4; + + SkipNPCStuff: ; + + //printf("\n"); + } + } while(MDFN_LIKELY(PSX_EventHandler(timestamp))); + + if(gte_ts_done > 0) + gte_ts_done -= timestamp; + + 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); +} + + +#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 diff --git a/psx/octoshock/psx/cpu.h b/psx/octoshock/psx/cpu.h index 51d79aa01d..7de9ed3532 100644 --- a/psx/octoshock/psx/cpu.h +++ b/psx/octoshock/psx/cpu.h @@ -1,264 +1,256 @@ -#ifndef __MDFN_PSX_CPU_H -#define __MDFN_PSX_CPU_H - -/* - 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" - - // 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" - - // 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" - -namespace MDFN_IEN_PSX -{ - -#define PS_CPU_EMULATE_ICACHE 1 - -class PS_CPU -{ - public: - - 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 }; - - void SetFastMap(void *region_mem, uint32_t region_address, uint32_t region_size); - - 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); - - void Power(void) MDFN_COLD; - - // which ranges 0-5, inclusive - void AssertIRQ(unsigned which, bool asserted); - - - void SetHalt(bool status); - - // TODO eventually: factor BIU address decoding directly in the CPU core somehow without hurting speed. - void SetBIU(uint32_t val); - uint32_t GetBIU(void); - - private: - - struct - { - uint32_t GPR[32]; - uint32_t GPR_dummy; // Used in load delay simulation(indexing past the end of GPR) - }; - uint32_t LO; - uint32_t HI; - - - uint32_t BACKED_PC; - uint32_t BACKED_new_PC; - uint32_t BACKED_new_PC_mask; - - uint32_t IPCache; - void RecalcIPCache(void); - bool Halted; - - uint32_t BACKED_LDWhich; - uint32_t BACKED_LDValue; - uint32_t LDAbsorb; - - pscpu_timestamp_t next_event_ts; - pscpu_timestamp_t gte_ts_done; - pscpu_timestamp_t muldiv_ts_done; - - uint32_t BIU; - - struct __ICache - { - uint32_t TV; - uint32_t Data; - }; - - union - { - __ICache ICache[1024]; - uint32 ICache_Bulk[2048]; - }; - - 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_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 - CP0REG_ERREG = 16 - }; - - struct - { - union - { - uint32_t Regs[32]; - struct - { - uint32_t Unused00; - uint32_t Unused01; - uint32_t Unused02; - uint32_t BPC; // RW - uint32_t Unused04; - uint32_t BDA; // RW - uint32_t TAR; - uint32_t DCIC; // RW - uint32_t Unused08; - uint32_t BDAM; // R/W - uint32_t Unused0A; - uint32_t BPCM; // R/W - uint32_t SR; // R/W - uint32_t CAUSE; // R/W(partial) - uint32_t EPC; // R - uint32_t PRID; // R - uint32_t ERREG; // ?(may not exist, test) - }; - }; - } CP0; - -#if 1 - //uint32_t WrAbsorb; - //uint8_t WrAbsorbShift; - - // On read: - //WrAbsorb = 0; - //WrAbsorbShift = 0; - - // On write: - //WrAbsorb >>= (WrAbsorbShift >> 2) & 8; - //WrAbsorbShift -= (WrAbsorbShift >> 2) & 8; - - //WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift; - //WrAbsorbShift += 8; -#endif - - struct - { - uint8_t ReadAbsorb[0x20]; - uint8_t ReadAbsorbDummy; - }; - uint8_t ReadAbsorbWhich; - uint8_t ReadFudge; - - //uint32_t WriteAbsorb; - //uint8_t WriteAbsorbCount; - //uint8_t WriteAbsorbMonkey; - uint8 MULT_Tab24[24]; - - MultiAccessSizeMem<1024, false> ScratchRAM; - - //PS_GTE GTE; - - uint8_t *FastMap[1 << (32 - FAST_MAP_SHIFT)]; - uint8_t DummyPage[FAST_MAP_PSIZE]; - - 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 - }; - - uint32_t Exception(uint32_t code, uint32_t PC, const uint32_t NPM) MDFN_WARN_UNUSED_RESULT; - - template pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE; - - template T PeekMemory(uint32_t address) MDFN_COLD; - template T ReadMemory(pscpu_timestamp_t ×tamp, uint32_t address, bool DS24 = false, bool LWC_timing = false); - template void WriteMemory(pscpu_timestamp_t ×tamp, uint32_t address, uint32_t value, bool DS24 = false); - - - // - // Mednafen debugger stuff follows: - // - public: - void SetCPUHook(void (*cpuh)(const pscpu_timestamp_t timestamp, uint32_t pc), void (*addbt)(uint32_t from, uint32_t to, bool exception)); - void CheckBreakpoints(void (*callback)(bool write, uint32_t address, unsigned int len), uint32_t 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_SR, - GSREG_CAUSE, - GSREG_EPC, - }; - - uint32_t GetRegister(unsigned int which, char *special, const uint32_t special_len); - void SetRegister(unsigned int which, uint32_t value); - bool PeekCheckICache(uint32_t PC, uint32_t *iw); - uint8_t PeekMem8(uint32_t A); - uint16_t PeekMem16(uint32_t A); - uint32_t PeekMem32(uint32_t A); - private: - void (*CPUHook)(const pscpu_timestamp_t timestamp, uint32_t pc); - void (*ADDBT)(uint32_t from, uint32_t to, bool exception); -}; - -} - -#endif +#ifndef __MDFN_PSX_CPU_H +#define __MDFN_PSX_CPU_H + +/* + 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" + + // 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" + + // 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" + +namespace MDFN_IEN_PSX +{ + +#define PS_CPU_EMULATE_ICACHE 1 + +class PS_CPU +{ + public: + + 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 }; + + 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; + } + + pscpu_timestamp_t Run(pscpu_timestamp_t timestamp_in, bool BIOSPrintMode, bool ILHMode); + + void Power(void) MDFN_COLD; + + // which ranges 0-5, inclusive + void AssertIRQ(unsigned which, bool asserted); + + 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); + + private: + + 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 BACKED_PC; + uint32 BACKED_new_PC; + uint32 BACKED_new_PC_mask; + + uint32 IPCache; + void RecalcIPCache(void); + bool Halted; + + uint32 BACKED_LDWhich; + uint32 BACKED_LDValue; + uint32 LDAbsorb; + + pscpu_timestamp_t next_event_ts; + pscpu_timestamp_t gte_ts_done; + pscpu_timestamp_t muldiv_ts_done; + + uint32 BIU; + + struct __ICache + { + uint32 TV; + uint32 Data; + }; + + union + { + __ICache ICache[1024]; + uint32 ICache_Bulk[2048]; + }; + + 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_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 + CP0REG_ERREG = 16 + }; + + struct + { + union + { + uint32 Regs[32]; + struct + { + uint32 Unused00; + uint32 Unused01; + uint32 Unused02; + uint32 BPC; // RW + uint32 Unused04; + uint32 BDA; // RW + uint32 TAR; + uint32 DCIC; // RW + uint32 Unused08; + 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 + uint32 ERREG; // ?(may not exist, test) + }; + }; + } CP0; + +#if 1 + //uint32 WrAbsorb; + //uint8 WrAbsorbShift; + + // On read: + //WrAbsorb = 0; + //WrAbsorbShift = 0; + + // On write: + //WrAbsorb >>= (WrAbsorbShift >> 2) & 8; + //WrAbsorbShift -= (WrAbsorbShift >> 2) & 8; + + //WrAbsorb |= (timestamp - pre_write_timestamp) << WrAbsorbShift; + //WrAbsorbShift += 8; +#endif + + uint8 ReadAbsorb[0x20 + 1]; + uint8 ReadAbsorbWhich; + uint8 ReadFudge; + + //uint32 WriteAbsorb; + //uint8 WriteAbsorbCount; + //uint8 WriteAbsorbMonkey; + uint8 MULT_Tab24[24]; + + MultiAccessSizeMem<1024, false> ScratchRAM; + + //PS_GTE GTE; + + uint8 *FastMap[1 << (32 - FAST_MAP_SHIFT)]; + uint8 DummyPage[FAST_MAP_PSIZE]; + + 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 + }; + + uint32 Exception(uint32 code, uint32 PC, const uint32 NPM) MDFN_WARN_UNUSED_RESULT; + + template pscpu_timestamp_t RunReal(pscpu_timestamp_t timestamp_in) NO_INLINE; + + template T PeekMemory(uint32 address) 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_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); + uint8 PeekMem8(uint32 A); + uint16 PeekMem16(uint32 A); + uint32 PeekMem32(uint32 A); + private: + void (*CPUHook)(const pscpu_timestamp_t timestamp, uint32 pc); + void (*ADDBT)(uint32 from, uint32 to, bool exception); +}; + +} + +#endif diff --git a/psx/octoshock/psx/dma.cpp b/psx/octoshock/psx/dma.cpp index 0d73a94492..145f2d711c 100644 --- a/psx/octoshock/psx/dma.cpp +++ b/psx/octoshock/psx/dma.cpp @@ -20,10 +20,6 @@ #include "cdc.h" #include "spu.h" -//#include - -// Notes: DMA tested to abort when - /* Notes: Channel 4(SPU): @@ -84,7 +80,7 @@ static Channel DMACH[7]; static pscpu_timestamp_t lastts; -static const char *PrettyChannelNames[7] = { "MDEC IN", "MDEC OUT", "GPU", "CDC", "SPU", "PIO", "OTC" }; +// static const char *PrettyChannelNames[7] = { "MDEC IN", "MDEC OUT", "GPU", "CDC", "SPU", "PIO", "OTC" }; void DMA_Init(void) { @@ -96,17 +92,17 @@ void DMA_Kill(void) } -static INLINE void RecalcIRQOut(void) -{ - bool irqo; - - irqo = (bool)DMAIntStatus; - irqo &= (DMAIntControl >> 23) & 1; - irqo |= (DMAIntControl >> 15) & 1; - - IRQOut = irqo; - IRQ_Assert(IRQ_DMA, irqo); -} +static INLINE void RecalcIRQOut(void) +{ + bool irqo; + + irqo = (bool)DMAIntStatus; + irqo &= (DMAIntControl >> 23) & 1; + irqo |= (DMAIntControl >> 15) & 1; + + IRQOut = irqo; + IRQ_Assert(IRQ_DMA, irqo); +} void DMA_ResetTS(void) { @@ -127,8 +123,6 @@ void DMA_Power(void) RecalcIRQOut(); } -void PSX_SetDMASuckSuck(unsigned); - static INLINE bool ChCan(const unsigned ch, const uint32 CRModeCache) { switch(ch) @@ -226,19 +220,16 @@ static void RecalcHalt(void) if(tmp > 0) tmp--; - if(tmp > 200) // Due to 8-bit limitations in the CPU core. - tmp = 200; - - PSX_SetDMASuckSuck(tmp); + PSX_SetDMACycleSteal(tmp); } else - PSX_SetDMASuckSuck(0); + PSX_SetDMACycleSteal(0); CPU->SetHalt(Halt); } -static INLINE void ChRW(const unsigned ch, const uint32 CRModeCache, uint32 *V, int32 *offset) +static INLINE void ChRW(const unsigned ch, const uint32 CRModeCache, uint32 *V, uint32 *offset) { unsigned extra_cyc_overhead = 0; @@ -424,7 +415,7 @@ static INLINE void RunChannelI(const unsigned ch, const uint32 CRModeCache, int3 // { uint32 vtmp; - int32 voffs = 0; + uint32 voffs = 0; if(MDFN_UNLIKELY(DMACH[ch].CurAddr & 0x800000)) { @@ -626,143 +617,142 @@ static void CheckLinkedList(uint32 addr) } #endif - -void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V) -{ - int ch = (A & 0x7F) >> 4; - - //if(ch == 2 || ch == 7) - //PSX_WARNING("[DMA] Write: %08x %08x, DMAIntStatus=%08x", A, V, DMAIntStatus); - - // FIXME if we ever have "accurate" bus emulation - V <<= (A & 3) * 8; - - DMA_Update(timestamp); - - if(ch == 7) - { - switch(A & 0xC) - { - case 0x0: //fprintf(stderr, "Global DMA control: 0x%08x\n", V); - DMAControl = V; - RecalcHalt(); - break; - - case 0x4: - DMAIntControl = V & 0x00ff803f; - DMAIntStatus &= ~(V >> 24); - RecalcIRQOut(); - break; - - default: PSX_WARNING("[DMA] Unknown write: %08x %08x", A, V); - break; - } - return; - } - switch(A & 0xC) - { - case 0x0: DMACH[ch].BaseAddr = V & 0xFFFFFF; - break; - - case 0x4: DMACH[ch].BlockControl = V; - break; - - case 0xC: - case 0x8: - { - uint32 OldCC = DMACH[ch].ChanControl; - - //printf("CHCR: %u, %08x --- 0x%08x\n", ch, V, DMACH[ch].BlockControl); - // - // Kludge for DMA timing granularity and other issues. Needs to occur before setting all bits of ChanControl to the new value, to accommodate the - // case of a game cancelling DMA and changing the type of DMA(read/write, etc.) at the same time. - // - if((DMACH[ch].ChanControl & (1 << 24)) && !(V & (1 << 24))) - { - DMACH[ch].ChanControl &= ~(1 << 24); // Clear bit before RunChannel(), so it will only finish the block it's on at most. - RunChannel(timestamp, 128 * 16, ch); - DMACH[ch].WordCounter = 0; - -#if 0 // TODO(maybe, need to work out worst-case performance for abnormally/brokenly large block sizes) - DMACH[ch].ClockCounter = (1 << 30); - RunChannel(timestamp, 1, ch); - DMACH[ch].ClockCounter = 0; -#endif - PSX_WARNING("[DMA] Forced stop for channel %d -- scanline=%d", ch, GPU->GetScanlineNum()); - //MDFN_DispMessage("[DMA] Forced stop for channel %d", ch); - } - - if(ch == 6) - DMACH[ch].ChanControl = (V & 0x51000000) | 0x2; - else - DMACH[ch].ChanControl = V & 0x71770703; - - if(!(OldCC & (1 << 24)) && (V & (1 << 24))) - { - //if(ch == 0 || ch == 1) - // PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum()); - - DMACH[ch].WordCounter = 0; - DMACH[ch].ClockCounter = 0; - - // - // Viewpoint starts a short MEM->GPU LL DMA and apparently has race conditions that can cause a crash if it doesn't finish almost immediately( - // or at least very quickly, which the current DMA granularity has issues with, so run the channel ahead a bit to take of this issue and potentially - // games with similar issues). - // - // Though, Viewpoint isn't exactly a good game, so maybe we shouldn't bother? ;) - // - // Also, it's needed for RecalcHalt() to work with some semblance of workiness. - // - RunChannel(timestamp, 64, ch); //std::max(128 - DMACycleCounter, 1)); //64); //1); //128 - DMACycleCounter); - } - - RecalcHalt(); - } - break; - } - PSX_SetEventNT(PSX_EVENT_DMA, timestamp + CalcNextEvent(0x10000000)); -} - -uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A) -{ - int ch = (A & 0x7F) >> 4; - uint32 ret = 0; - - if(ch == 7) - { - switch(A & 0xC) - { - default: PSX_WARNING("[DMA] Unknown read: %08x", A); - break; - - case 0x0: ret = DMAControl; - break; - - case 0x4: ret = DMAIntControl | (DMAIntStatus << 24) | (IRQOut << 31); - break; - } - } - else switch(A & 0xC) - { - case 0x0: ret = DMACH[ch].BaseAddr; - break; - - case 0x4: ret = DMACH[ch].BlockControl; - break; - - case 0xC: - case 0x8: ret = DMACH[ch].ChanControl; - break; - - } - - ret >>= (A & 3) * 8; - - //PSX_WARNING("[DMA] Read: %08x %08x", A, ret); - - return(ret); -} +void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V) +{ + int ch = (A & 0x7F) >> 4; + + //if(ch == 2 || ch == 7) + //PSX_WARNING("[DMA] Write: %08x %08x, DMAIntStatus=%08x", A, V, DMAIntStatus); + + // FIXME if we ever have "accurate" bus emulation + V <<= (A & 3) * 8; + + DMA_Update(timestamp); + + if(ch == 7) + { + switch(A & 0xC) + { + case 0x0: //fprintf(stderr, "Global DMA control: 0x%08x\n", V); + DMAControl = V; + RecalcHalt(); + break; + + case 0x4: + DMAIntControl = V & 0x00ff803f; + DMAIntStatus &= ~(V >> 24); + RecalcIRQOut(); + break; + + default: PSX_WARNING("[DMA] Unknown write: %08x %08x", A, V); + break; + } + return; + } + switch(A & 0xC) + { + case 0x0: DMACH[ch].BaseAddr = V & 0xFFFFFF; + break; + + case 0x4: DMACH[ch].BlockControl = V; + break; + + case 0xC: + case 0x8: + { + uint32 OldCC = DMACH[ch].ChanControl; + + //printf("CHCR: %u, %08x --- 0x%08x\n", ch, V, DMACH[ch].BlockControl); + // + // Kludge for DMA timing granularity and other issues. Needs to occur before setting all bits of ChanControl to the new value, to accommodate the + // case of a game cancelling DMA and changing the type of DMA(read/write, etc.) at the same time. + // + if((DMACH[ch].ChanControl & (1 << 24)) && !(V & (1 << 24))) + { + DMACH[ch].ChanControl &= ~(1 << 24); // Clear bit before RunChannel(), so it will only finish the block it's on at most. + RunChannel(timestamp, 128 * 16, ch); + DMACH[ch].WordCounter = 0; + +#if 0 // TODO(maybe, need to work out worst-case performance for abnormally/brokenly large block sizes) + DMACH[ch].ClockCounter = (1 << 30); + RunChannel(timestamp, 1, ch); + DMACH[ch].ClockCounter = 0; +#endif + PSX_WARNING("[DMA] Forced stop for channel %d -- scanline=%d", ch, GPU->GetScanlineNum()); + //MDFN_DispMessage("[DMA] Forced stop for channel %d", ch); + } + + if(ch == 6) + DMACH[ch].ChanControl = (V & 0x51000000) | 0x2; + else + DMACH[ch].ChanControl = V & 0x71770703; + + if(!(OldCC & (1 << 24)) && (V & (1 << 24))) + { + //if(ch == 0 || ch == 1) + // PSX_WARNING("[DMA] Started DMA for channel=%d --- CHCR=0x%08x --- BCR=0x%08x --- scanline=%d", ch, DMACH[ch].ChanControl, DMACH[ch].BlockControl, GPU->GetScanlineNum()); + + DMACH[ch].WordCounter = 0; + DMACH[ch].ClockCounter = 0; + + // + // Viewpoint starts a short MEM->GPU LL DMA and apparently has race conditions that can cause a crash if it doesn't finish almost immediately( + // or at least very quickly, which the current DMA granularity has issues with, so run the channel ahead a bit to take of this issue and potentially + // games with similar issues). + // + // Though, Viewpoint isn't exactly a good game, so maybe we shouldn't bother? ;) + // + // Also, it's needed for RecalcHalt() to work with some semblance of workiness. + // + RunChannel(timestamp, 64, ch); //std::max(128 - DMACycleCounter, 1)); //64); //1); //128 - DMACycleCounter); + } + + RecalcHalt(); + } + break; + } + PSX_SetEventNT(PSX_EVENT_DMA, timestamp + CalcNextEvent(0x10000000)); +} + +uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A) +{ + int ch = (A & 0x7F) >> 4; + uint32 ret = 0; + + if(ch == 7) + { + switch(A & 0xC) + { + default: PSX_WARNING("[DMA] Unknown read: %08x", A); + break; + + case 0x0: ret = DMAControl; + break; + + case 0x4: ret = DMAIntControl | (DMAIntStatus << 24) | (IRQOut << 31); + break; + } + } + else switch(A & 0xC) + { + case 0x0: ret = DMACH[ch].BaseAddr; + break; + + case 0x4: ret = DMACH[ch].BlockControl; + break; + + case 0xC: + case 0x8: ret = DMACH[ch].ChanControl; + break; + + } + + ret >>= (A & 3) * 8; + + //PSX_WARNING("[DMA] Read: %08x %08x", A, ret); + + return(ret); +} void DMA_SyncState(bool isReader, EW::NewState *ns) diff --git a/psx/octoshock/psx/dma.h b/psx/octoshock/psx/dma.h index 665485912d..261efaf300 100644 --- a/psx/octoshock/psx/dma.h +++ b/psx/octoshock/psx/dma.h @@ -4,18 +4,16 @@ namespace MDFN_IEN_PSX { -bool DMA_GPUWriteActive(void); - pscpu_timestamp_t DMA_Update(const pscpu_timestamp_t timestamp); void DMA_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V); uint32 DMA_Read(const pscpu_timestamp_t timestamp, uint32 A); void DMA_ResetTS(void); -void DMA_Power(void) MDFN_COLD; - -void DMA_Init(void) MDFN_COLD; -void DMA_Kill(void) MDFN_COLD; +void DMA_Power(void) MDFN_COLD; + +void DMA_Init(void) MDFN_COLD; +void DMA_Kill(void) MDFN_COLD; diff --git a/psx/octoshock/psx/frontio.cpp b/psx/octoshock/psx/frontio.cpp index ef47b12407..7d119db4a0 100644 --- a/psx/octoshock/psx/frontio.cpp +++ b/psx/octoshock/psx/frontio.cpp @@ -15,6 +15,8 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include + #include "psx.h" #include "frontio.h" diff --git a/psx/octoshock/psx/gpu.cpp b/psx/octoshock/psx/gpu.cpp index 33793a47f7..3d738414f7 100644 --- a/psx/octoshock/psx/gpu.cpp +++ b/psx/octoshock/psx/gpu.cpp @@ -15,8 +15,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "psx.h" #include "timer.h" +#include "math_ops.h" /* GPU display timing master clock is nominally 53.693182 MHz for NTSC PlayStations, and 53.203425 MHz for PAL PlayStations. @@ -104,7 +106,7 @@ void PS_GPU::StaticInitialize() memcpy(&Commands[0x80], Commands_80_FF, sizeof(Commands_80_FF)); } -PS_GPU::PS_GPU(bool pal_clock_and_tv) : BlitterFIFO(0x20) // 0x10 on actual PS1 GPU, 0x20 here(see comment at top of gpu.h) // 0x10) +PS_GPU::PS_GPU(bool pal_clock_and_tv) { //todo - not thread safe StaticInitialize(); @@ -172,179 +174,173 @@ void PS_GPU::FillVideoParams(MDFNGI* gi) gi->mouse_scale_y = 1.0; gi->mouse_offs_y = LineVisFirst; } - -void PS_GPU::SoftReset(void) // Control command 0x00 -{ - IRQPending = false; - IRQ_Assert(IRQ_GPU, IRQPending); - - InvalidateCache(); - - DMAControl = 0; - - if(DrawTimeAvail < 0) - DrawTimeAvail = 0; - - BlitterFIFO.Flush(); - InCmd = INCMD_NONE; - - DisplayOff = 1; - DisplayFB_XStart = 0; - DisplayFB_YStart = 0; - - if(HardwarePALType) - { - DisplayMode = 0x08; - - // FIXME, timing values(I need a PAL PS1 to derive them); for now, just copy the NTSC ones. - HorizStart = 0x200; - HorizEnd = 0xC00; - - VertStart = 0x10; - VertEnd = 0x100; - } - else - { - DisplayMode = 0; - - HorizStart = 0x200; - HorizEnd = 0xC00; - - VertStart = 0x10; - VertEnd = 0x100; - } - - // - TexPageX = 0; - TexPageY = 0; - - SpriteFlip = 0; - - abr = 0; - TexMode = 0; - - dtd = 0; - dfe = 0; - - // - tww = 0; - twh = 0; - twx = 0; - twy = 0; - - RecalcTexWindowStuff(); - - // - ClipX0 = 0; - ClipY0 = 0; - - // - ClipX1 = 0; - ClipY1 = 0; - - // - OffsX = 0; - OffsY = 0; - - // - MaskSetOR = 0; - MaskEvalAND = 0; -} - -void PS_GPU::Power(void) -{ - memset(GPURAM, 0, sizeof(GPURAM)); - - memset(CLUT_Cache, 0, sizeof(CLUT_Cache)); - CLUT_Cache_VB = ~0U; - - memset(TexCache, 0xFF, sizeof(TexCache)); - - DMAControl = 0; - - ClipX0 = 0; - ClipY0 = 0; - ClipX1 = 1023; - ClipY1 = 1023; - - OffsX = 0; - OffsY = 0; - - dtd = false; - dfe = false; - - MaskSetOR = 0; - MaskEvalAND = 0; - - tww = 0; - twh = 0; - twx = 0; - twy = 0; - - TexPageX = 0; - TexPageY = 0; - SpriteFlip = 0; - - abr = 0; - TexMode = 0; - - RecalcTexWindowStuff(); - - BlitterFIFO.Flush(); - - InCmd = INCMD_NONE; - FBRW_X = 0; - FBRW_Y = 0; - FBRW_W = 0; - FBRW_H = 0; - FBRW_CurY = 0; - FBRW_CurX = 0; - - DisplayMode = 0; - DisplayOff = 1; - DisplayFB_XStart = 0; - DisplayFB_YStart = 0; - - HorizStart = 0; - HorizEnd = 0; - - VertStart = 0; - VertEnd = 0; - - // - // - // - DisplayFB_CurYOffset = 0; - DisplayFB_CurLineYReadout = 0; - InVBlank = true; - - // TODO: factor out in a separate function. - LinesPerField = 263; - - // - // - // - scanline = 0; - field = 0; - field_ram_readout = 0; - PhaseChange = 0; - - // - // - // - DotClockCounter = 0; - GPUClockCounter = 0; - LineClockCounter = 3412 - 200; - LinePhase = 0; - - DrawTimeAvail = 0; - - lastts = 0; - - SoftReset(); - - IRQ_Assert(IRQ_VBLANK, InVBlank); - TIMER_SetVBlank(InVBlank); + +void PS_GPU::SoftReset(void) // Control command 0x00 +{ + IRQPending = false; + IRQ_Assert(IRQ_GPU, IRQPending); + + InvalidateCache(); + + DMAControl = 0; + + if(DrawTimeAvail < 0) + DrawTimeAvail = 0; + + BlitterFIFO.Flush(); + DataReadBufferEx = 0; + InCmd = INCMD_NONE; + + DisplayOff = 1; + DisplayFB_XStart = 0; + DisplayFB_YStart = 0; + + DisplayMode = 0; + + HorizStart = 0x200; + HorizEnd = 0xC00; + + VertStart = 0x10; + VertEnd = 0x100; + + // + TexPageX = 0; + TexPageY = 0; + + SpriteFlip = 0; + + abr = 0; + TexMode = 0; + + dtd = 0; + dfe = 0; + + // + tww = 0; + twh = 0; + twx = 0; + twy = 0; + + RecalcTexWindowStuff(); + + // + ClipX0 = 0; + ClipY0 = 0; + + // + ClipX1 = 0; + ClipY1 = 0; + + // + OffsX = 0; + OffsY = 0; + + // + MaskSetOR = 0; + MaskEvalAND = 0; + + TexDisable = false; + TexDisableAllowChange = false; +} + +void PS_GPU::Power(void) +{ + memset(GPURAM, 0, sizeof(GPURAM)); + + memset(CLUT_Cache, 0, sizeof(CLUT_Cache)); + CLUT_Cache_VB = ~0U; + + memset(TexCache, 0xFF, sizeof(TexCache)); + + DMAControl = 0; + + ClipX0 = 0; + ClipY0 = 0; + ClipX1 = 0; + ClipY1 = 0; + + OffsX = 0; + OffsY = 0; + + dtd = false; + dfe = false; + + MaskSetOR = 0; + MaskEvalAND = 0; + + TexDisable = false; + TexDisableAllowChange = false; + + tww = 0; + twh = 0; + twx = 0; + twy = 0; + + TexPageX = 0; + TexPageY = 0; + SpriteFlip = 0; + + abr = 0; + TexMode = 0; + + RecalcTexWindowStuff(); + + BlitterFIFO.Flush(); + DataReadBuffer = 0; // Don't reset in SoftReset() + DataReadBufferEx = 0; + InCmd = INCMD_NONE; + FBRW_X = 0; + FBRW_Y = 0; + FBRW_W = 0; + FBRW_H = 0; + FBRW_CurY = 0; + FBRW_CurX = 0; + + DisplayMode = 0; + DisplayOff = 1; + DisplayFB_XStart = 0; + DisplayFB_YStart = 0; + + HorizStart = 0; + HorizEnd = 0; + + VertStart = 0; + VertEnd = 0; + + // + // + // + DisplayFB_CurYOffset = 0; + DisplayFB_CurLineYReadout = 0; + InVBlank = true; + + // TODO: factor out in a separate function. + LinesPerField = 263; + + // + // + // + scanline = 0; + field = 0; + field_ram_readout = 0; + PhaseChange = 0; + + // + // + // + DotClockCounter = 0; + GPUClockCounter = 0; + LineClockCounter = 3412 - 200; + LinePhase = 0; + + DrawTimeAvail = 0; + + lastts = 0; + + SoftReset(); + + IRQ_Assert(IRQ_VBLANK, InVBlank); + TIMER_SetVBlank(InVBlank); } void PS_GPU::ResetTS(void) @@ -437,1034 +433,1078 @@ INLINE void PS_GPU::Command_FBCopy(const uint32 *cb) } -INLINE void PS_GPU::Command_FBWrite(const uint32 *cb) -{ - assert(InCmd == INCMD_NONE); - - FBRW_X = (cb[1] >> 0) & 0x3FF; - FBRW_Y = (cb[1] >> 16) & 0x3FF; - - FBRW_W = (cb[2] >> 0) & 0x7FF; - FBRW_H = (cb[2] >> 16) & 0x3FF; - - if(FBRW_W > 0x400) - FBRW_W &= 0x3FF; - - if(FBRW_H > 0x200) - FBRW_H &= 0x1FF; - - FBRW_CurX = FBRW_X; - FBRW_CurY = FBRW_Y; - - if(FBRW_W != 0 && FBRW_H != 0) - InCmd = INCMD_FBWRITE; -} - -INLINE void PS_GPU::Command_FBRead(const uint32 *cb) -{ - assert(InCmd == INCMD_NONE); - - FBRW_X = (cb[1] >> 0) & 0x3FF; - FBRW_Y = (cb[1] >> 16) & 0x3FF; - - FBRW_W = (cb[2] >> 0) & 0x7FF; - FBRW_H = (cb[2] >> 16) & 0x3FF; - - if(FBRW_W > 0x400) - FBRW_W &= 0x3FF; - - if(FBRW_H > 0x200) - FBRW_H &= 0x1FF; - - FBRW_CurX = FBRW_X; - FBRW_CurY = FBRW_Y; - - if(FBRW_W != 0 && FBRW_H != 0) - InCmd = INCMD_FBREAD; -} - -/* -INLINE void PS_GPU::RecalcTexPageStuff(uint32 tpage) -{ - - -} -*/ -INLINE void PS_GPU::Command_DrawMode(const uint32 *cb) -{ - TexPageX = (*cb & 0xF) * 64; - TexPageY = (*cb & 0x10) * 16; - - SpriteFlip = *cb & 0x3000; - - abr = (*cb >> 5) & 0x3; - TexMode = (*cb >> 7) & 0x3; - - dtd = (*cb >> 9) & 1; - dfe = (*cb >> 10) & 1; - - RecalcTexWindowStuff(); - //printf("*******************DFE: %d -- scanline=%d\n", dfe, scanline); -} - -INLINE void PS_GPU::Command_TexWindow(const uint32 *cb) -{ - tww = (*cb & 0x1F); - twh = ((*cb >> 5) & 0x1F); - twx = ((*cb >> 10) & 0x1F); - twy = ((*cb >> 15) & 0x1F); - - RecalcTexWindowStuff(); -} - -INLINE void PS_GPU::Command_Clip0(const uint32 *cb) -{ - ClipX0 = *cb & 1023; - ClipY0 = (*cb >> 10) & 1023; - - //fprintf(stderr, "[GPU] Clip0: x=%d y=%d, raw=0x%08x --- %d\n", ClipX0, ClipY0, *cb, scanline); -} - -INLINE void PS_GPU::Command_Clip1(const uint32 *cb) -{ - ClipX1 = *cb & 1023; - ClipY1 = (*cb >> 10) & 1023; - - //fprintf(stderr, "[GPU] Clip1: x=%d y=%d, raw=0x%08x --- %d\n", ClipX1, ClipY1, *cb, scanline); -} - -INLINE void PS_GPU::Command_DrawingOffset(const uint32 *cb) -{ - OffsX = sign_x_to_s32(11, (*cb & 2047)); - OffsY = sign_x_to_s32(11, ((*cb >> 11) & 2047)); - - //fprintf(stderr, "[GPU] Drawing offset: x=%d y=%d, raw=0x%08x --- %d\n", OffsX, OffsY, *cb, scanline); -} - -INLINE void PS_GPU::Command_MaskSetting(const uint32 *cb) -{ - //printf("Mask setting: %08x\n", *cb); - MaskSetOR = (*cb & 1) ? 0x8000 : 0x0000; - MaskEvalAND = (*cb & 2) ? 0x8000 : 0x0000; -} - -void PS_GPU::InvalidateCache(void) -{ - CLUT_Cache_VB = ~0U; - - for(int i=0;iCommand_ClearCache(cb); -} - -static void G_Command_IRQ(PS_GPU* g, const uint32 *cb) -{ - g->Command_IRQ(cb); -} - -static void G_Command_FBFill(PS_GPU* g, const uint32 *cb) -{ - g->Command_FBFill(cb); -} - -static void G_Command_FBCopy(PS_GPU* g, const uint32 *cb) -{ - g->Command_FBCopy(cb); -} - -static void G_Command_FBWrite(PS_GPU* g, const uint32 *cb) -{ - g->Command_FBWrite(cb); -} - -static void G_Command_FBRead(PS_GPU* g, const uint32 *cb) -{ - g->Command_FBRead(cb); -} - -static void G_Command_DrawMode(PS_GPU* g, const uint32 *cb) -{ - g->Command_DrawMode(cb); -} - -static void G_Command_TexWindow(PS_GPU* g, const uint32 *cb) -{ - g->Command_TexWindow(cb); -} - -static void G_Command_Clip0(PS_GPU* g, const uint32 *cb) -{ - g->Command_Clip0(cb); -} - -static void G_Command_Clip1(PS_GPU* g, const uint32 *cb) -{ - g->Command_Clip1(cb); -} - -static void G_Command_DrawingOffset(PS_GPU* g, const uint32 *cb) -{ - g->Command_DrawingOffset(cb); -} - -static void G_Command_MaskSetting(PS_GPU* g, const uint32 *cb) -{ - g->Command_MaskSetting(cb); -} - -CTEntry PS_GPU::Commands[0x100]; - -const CTEntry PS_GPU::Commands_00_1F[0x20] = -{ - /* 0x00 */ - NULLCMD(), - OTHER_HELPER(1, 2, false, G_Command_ClearCache), - OTHER_HELPER(3, 3, false, G_Command_FBFill), - - NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), - NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), - - /* 0x10 */ - NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), - NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), - - /* 0x1F */ - OTHER_HELPER(1, 1, false, G_Command_IRQ) -}; - -const CTEntry PS_GPU::Commands_80_FF[0x80] = -{ - /* 0x80 ... 0x9F */ - OTHER_HELPER_X32(4, 2, false, G_Command_FBCopy), - - /* 0xA0 ... 0xBF */ - OTHER_HELPER_X32(3, 2, false, G_Command_FBWrite), - - /* 0xC0 ... 0xDF */ - OTHER_HELPER_X32(3, 2, false, G_Command_FBRead), - - /* 0xE0 */ - - NULLCMD(), - OTHER_HELPER(1, 2, false, G_Command_DrawMode), - OTHER_HELPER(1, 2, false, G_Command_TexWindow), - OTHER_HELPER(1, 1, true, G_Command_Clip0), - OTHER_HELPER(1, 1, true, G_Command_Clip1), - OTHER_HELPER(1, 1, true, G_Command_DrawingOffset), - OTHER_HELPER(1, 2, false, G_Command_MaskSetting), - - NULLCMD(), - NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), - - /* 0xF0 */ - NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), - NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD() -}; - -void PS_GPU::ProcessFIFO(void) -{ - if(!BlitterFIFO.CanRead()) - return; - - switch(InCmd) - { - default: - abort(); - break; - - case INCMD_NONE: - break; - - case INCMD_FBREAD: - puts("BOGUS SALAMANDERS, CAPTAIN!"); - return; - - case INCMD_FBWRITE: - { - uint32 InData = BlitterFIFO.ReadUnit(); - - for(int i = 0; i < 2; i++) - { - if(!(GPURAM[FBRW_CurY & 511][FBRW_CurX & 1023] & MaskEvalAND)) - GPURAM[FBRW_CurY & 511][FBRW_CurX & 1023] = InData | MaskSetOR; - - FBRW_CurX++; - if(FBRW_CurX == (FBRW_X + FBRW_W)) - { - FBRW_CurX = FBRW_X; - FBRW_CurY++; - if(FBRW_CurY == (FBRW_Y + FBRW_H)) - { - InCmd = INCMD_NONE; - break; // Break out of the for() loop. - } - } - InData >>= 16; - } - return; - } - break; - - case INCMD_QUAD: - { - if(DrawTimeAvail < 0) - return; - - const uint32 cc = InCmd_CC; - const CTEntry *command = &Commands[cc]; - unsigned vl = 1 + (bool)(cc & 0x4) + (bool)(cc & 0x10); - uint32 CB[3]; - - if(BlitterFIFO.CanRead() >= vl) - { - for(unsigned i = 0; i < vl; i++) - { - CB[i] = BlitterFIFO.ReadUnit(); - } - - command->func[abr][TexMode | (MaskEvalAND ? 0x4 : 0x0)](this, CB); - } - return; - } - break; - - case INCMD_PLINE: - { - if(DrawTimeAvail < 0) - return; - - const uint32 cc = InCmd_CC; - const CTEntry *command = &Commands[cc]; - unsigned vl = 1 + (bool)(InCmd_CC & 0x10); - uint32 CB[2]; - - if((BlitterFIFO.ReadUnit(true) & 0xF000F000) == 0x50005000) - { - BlitterFIFO.ReadUnit(); - InCmd = INCMD_NONE; - return; - } - - if(BlitterFIFO.CanRead() >= vl) - { - for(unsigned i = 0; i < vl; i++) - { - CB[i] = BlitterFIFO.ReadUnit(); - } - - command->func[abr][TexMode | (MaskEvalAND ? 0x4 : 0x0)](this, CB); - } - return; - } - break; - } - - const uint32 cc = BlitterFIFO.ReadUnit(true) >> 24; - const CTEntry *command = &Commands[cc]; - - if(DrawTimeAvail < 0 && !command->ss_cmd) - return; - - if(BlitterFIFO.CanRead() >= command->len) - { - uint32 CB[0x10]; - - for(unsigned i = 0; i < command->len; i++) - CB[i] = BlitterFIFO.ReadUnit(); - - if(!command->ss_cmd) - DrawTimeAvail -= 2; - -#if 0 - PSX_WARNING("[GPU] Command: %08x %s %d %d %d", CB[0], command->name, command->len, scanline, DrawTimeAvail); - if(1) - { - printf("[GPU] "); - for(unsigned i = 0; i < command->len; i++) - printf("0x%08x ", CB[i]); - printf("\n"); - } -#endif - // A very very ugly kludge to support texture mode specialization. fixme/cleanup/SOMETHING in the future. - if(cc >= 0x20 && cc <= 0x3F && (cc & 0x4)) - { - uint32 tpage; - - tpage = CB[4 + ((cc >> 4) & 0x1)] >> 16; - - TexPageX = (tpage & 0xF) * 64; - TexPageY = (tpage & 0x10) * 16; - - SpriteFlip = tpage & 0x3000; - - abr = (tpage >> 5) & 0x3; - TexMode = (tpage >> 7) & 0x3; - RecalcTexWindowStuff(); - } - - if(!command->func[abr][TexMode]) - { - if(CB[0]) - PSX_WARNING("[GPU] Unknown command: %08x, %d", CB[0], scanline); - } - else - { - command->func[abr][TexMode | (MaskEvalAND ? 0x4 : 0x0)](this, CB); - } - } -} - -INLINE void PS_GPU::WriteCB(uint32 InData) -{ - if(BlitterFIFO.CanRead() >= 0x10 && (InCmd != INCMD_NONE || (BlitterFIFO.CanRead() - 0x10) >= Commands[BlitterFIFO.ReadUnit(true) >> 24].fifo_fb_len)) - { - PSX_DBG(PSX_DBG_WARNING, "GPU FIFO overflow!!!\n"); - return; - } - - BlitterFIFO.WriteUnit(InData); - ProcessFIFO(); -} - -void PS_GPU::Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V) -{ - V <<= (A & 3) * 8; - - if(A & 4) // GP1 ("Control") - { - uint32 command = V >> 24; - - V &= 0x00FFFFFF; - - //PSX_WARNING("[GPU] Control command: %02x %06x %d", command, V, scanline); - - switch(command) - { - /* - 0x40-0xFF do NOT appear to be mirrors, at least not on my PS1's GPU. - */ - default: PSX_WARNING("[GPU] Unknown control command %02x - %06x", command, V); - break; - - case 0x00: // Reset GPU - //printf("\n\n************ Soft Reset %u ********* \n\n", scanline); - SoftReset(); - break; - - case 0x01: // Reset command buffer - if(DrawTimeAvail < 0) - DrawTimeAvail = 0; - BlitterFIFO.Flush(); - InCmd = INCMD_NONE; - break; - - case 0x02: // Acknowledge IRQ - IRQPending = false; - IRQ_Assert(IRQ_GPU, IRQPending); - break; - - case 0x03: // Display enable - DisplayOff = V & 1; - break; - - case 0x04: // DMA Setup - DMAControl = V & 0x3; - break; - - case 0x05: // Start of display area in framebuffer - DisplayFB_XStart = V & 0x3FE; // Lower bit is apparently ignored. - DisplayFB_YStart = (V >> 10) & 0x1FF; - break; - - case 0x06: // Horizontal display range - HorizStart = V & 0xFFF; - HorizEnd = (V >> 12) & 0xFFF; - break; - - case 0x07: - VertStart = V & 0x3FF; - VertEnd = (V >> 10) & 0x3FF; - break; - - case 0x08: - //printf("\n\nDISPLAYMODE SET: 0x%02x, %u *************************\n\n\n", V & 0xFF, scanline); - DisplayMode = V & 0xFF; - break; - - case 0x10: // GPU info(?) - switch(V & 0xF) - { - // DataReadBuffer must remain unchanged for any unhandled GPU info index. - default: break; - - case 0x2: DataReadBuffer = (tww << 0) | (twh << 5) | (twx << 10) | (twy << 15); - break; - - case 0x3: DataReadBuffer = (ClipY0 << 10) | ClipX0; - break; - - case 0x4: DataReadBuffer = (ClipY1 << 10) | ClipX1; - break; - - case 0x5: DataReadBuffer = (OffsX & 2047) | ((OffsY & 2047) << 11); - break; - - case 0x7: DataReadBuffer = 2; - break; - - case 0x8: DataReadBuffer = 0; - break; - } - //fprintf(stderr, "[GPU] CC 0x10:0x%02x, DRB=0x%02x\n", V & 0xF, DataReadBuffer); - break; - - } - } - else // GP0 ("Data") - { - //uint32 command = V >> 24; - //printf("Meow command: %02x\n", command); - //assert(!(DMAControl & 2)); - WriteCB(V); - } -} - - -void PS_GPU::WriteDMA(uint32 V) -{ - WriteCB(V); -} - -INLINE uint32 PS_GPU::ReadData(void) -{ - if(InCmd == INCMD_FBREAD) - { - DataReadBuffer = 0; - for(int i = 0; i < 2; i++) - { - DataReadBuffer |= GPURAM[FBRW_CurY & 511][FBRW_CurX & 1023] << (i * 16); - - FBRW_CurX++; - if(FBRW_CurX == (FBRW_X + FBRW_W)) - { - FBRW_CurX = FBRW_X; - FBRW_CurY++; - if(FBRW_CurY == (FBRW_Y + FBRW_H)) - { - InCmd = INCMD_NONE; - break; - } - } - } - } - - return DataReadBuffer; -} - -uint32 PS_GPU::ReadDMA(void) -{ - return ReadData(); -} - -uint32 PS_GPU::Read(const pscpu_timestamp_t timestamp, uint32 A) -{ - uint32 ret = 0; - - if(A & 4) // Status - { - ret = (((DisplayMode << 1) & 0x7F) | ((DisplayMode >> 6) & 1)) << 16; - - ret |= DMAControl << 29; - - ret |= (DisplayFB_CurLineYReadout & 1) << 31; - - ret |= (!field) << 13; - - if(DMAControl & 0x02) - ret |= 1 << 25; - - ret |= IRQPending << 24; - - ret |= DisplayOff << 23; - - if(InCmd == INCMD_NONE && DrawTimeAvail >= 0 && BlitterFIFO.CanRead() == 0x00) // GPU idle bit. - ret |= 1 << 26; - - if(InCmd == INCMD_FBREAD) // Might want to more accurately emulate this in the future? - ret |= (1 << 27); - - ret |= CalcFIFOReadyBit() << 28; // FIFO has room bit? (kinda). - - // - // - ret |= TexPageX >> 6; - ret |= TexPageY >> 4; - ret |= abr << 5; - ret |= TexMode << 7; - - ret |= dtd << 9; - ret |= dfe << 10; - - if(MaskSetOR) - ret |= 1 << 11; - - if(MaskEvalAND) - ret |= 1 << 12; - } - else // "Data" - ret = ReadData(); - - if(DMAControl & 2) - { - //PSX_WARNING("[GPU READ WHEN (DMACONTROL&2)] 0x%08x - ret=0x%08x, scanline=%d", A, ret, scanline); - } - - return(ret >> ((A & 3) * 8)); -} - -#if 0 -static INLINE uint32 MDFN_NOWARN_UNUSED ShiftHelper(uint32 val, int shamt, uint32 mask) -{ - if(shamt < 0) - return((val >> (-shamt)) & mask); - else - return((val << shamt) & mask); -} -#endif - -#pragma GCC push_options -#pragma GCC optimize("no-unroll-loops,no-peel-loops,no-crossjumping") -INLINE void PS_GPU::ReorderRGB_Var(uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift, bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x) -{ - if(bpp24) // 24bpp - { - for(int32 x = dx_start; MDFN_LIKELY(x < dx_end); x++) - { - uint32 srcpix; - - srcpix = src[(fb_x >> 1) + 0] | (src[((fb_x >> 1) + 1) & 0x7FF] << 16); - srcpix >>= (fb_x & 1) * 8; - - dest[x] = (((srcpix >> 0) << out_Rshift) & (0xFF << out_Rshift)) | (((srcpix >> 8) << out_Gshift) & (0xFF << out_Gshift)) | - (((srcpix >> 16) << out_Bshift) & (0xFF << out_Bshift)); - - fb_x = (fb_x + 3) & 0x7FF; - } - } // 15bpp - else - { - for(int32 x = dx_start; MDFN_LIKELY(x < dx_end); x++) - { - uint32 srcpix = src[fb_x >> 1]; - -#if 1 - dest[x] = OutputLUT[(uint8)srcpix] | (OutputLUT + 256)[(srcpix >> 8) & 0x7F]; -#else - dest[x] = ShiftHelper(srcpix, out_Rshift + 3 - 0, (0xF8 << out_Rshift)) | - ShiftHelper(srcpix, out_Gshift + 3 - 5, (0xF8 << out_Gshift)) | - ShiftHelper(srcpix, out_Bshift + 3 - 10, (0xF8 << out_Bshift)); -#endif - fb_x = (fb_x + 2) & 0x7FF; - } - } - -} - -template -void NO_INLINE PS_GPU::ReorderRGB(bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x) -{ - ReorderRGB_Var(out_Rshift, out_Gshift, out_Bshift, bpp24, src, dest, dx_start, dx_end, fb_x); -} -#pragma GCC pop_options - -pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp) -{ - static const uint32 DotClockRatios[5] = { 10, 8, 5, 4, 7 }; - const uint32 dmc = (DisplayMode & 0x40) ? 4 : (DisplayMode & 0x3); - const uint32 dmw = 2800 / DotClockRatios[dmc]; // Must be <= (768 - 32) - const uint32 dmpa = (2800 - (hide_hoverscan ? 2640 : 2800)) / DotClockRatios[dmc] / 2; // Must be <= 32 - const uint32 drxbo = 32; - - int32 sys_clocks = sys_timestamp - lastts; - int32 gpu_clocks; - - //printf("GPUISH: %d\n", sys_timestamp - lastts); - - if(!sys_clocks) - goto TheEnd; - - DrawTimeAvail += sys_clocks << 1; - - if(DrawTimeAvail > 256) - DrawTimeAvail = 256; - - ProcessFIFO(); - - //puts("GPU Update Start"); - - GPUClockCounter += (uint64)sys_clocks * GPUClockRatio; - - gpu_clocks = GPUClockCounter >> 16; - GPUClockCounter -= gpu_clocks << 16; - - while(gpu_clocks > 0) - { - int32 chunk_clocks = gpu_clocks; - int32 dot_clocks; - - if(chunk_clocks > LineClockCounter) - { - //printf("Chunk: %u, LCC: %u\n", chunk_clocks, LineClockCounter); - chunk_clocks = LineClockCounter; - } - - gpu_clocks -= chunk_clocks; - LineClockCounter -= chunk_clocks; - - DotClockCounter += chunk_clocks; - dot_clocks = DotClockCounter / DotClockRatios[DisplayMode & 0x3]; - DotClockCounter -= dot_clocks * DotClockRatios[DisplayMode & 0x3]; - - TIMER_AddDotClocks(dot_clocks); - - - if(!LineClockCounter) - { - PSX_SetEventNT(PSX_EVENT_TIMER, TIMER_Update(sys_timestamp)); // We could just call this at the top of GPU_Update(), but do it here for slightly less CPU usage(presumably). - - LinePhase = (LinePhase + 1) & 1; - - if(LinePhase) - { - TIMER_SetHRetrace(true); - LineClockCounter = 200; - TIMER_ClockHRetrace(); - } - else - { - const unsigned int FirstVisibleLine = LineVisFirst + (HardwarePALType ? 20 : 16); - const unsigned int VisibleLineCount = LineVisLast + 1 - LineVisFirst; //HardwarePALType ? 288 : 240; - - TIMER_SetHRetrace(false); - - if(DisplayMode & 0x08) - LineClockCounter = 3405 - 200; - else - LineClockCounter = 3412 + PhaseChange - 200; - - scanline = (scanline + 1) % LinesPerField; - PhaseChange = !PhaseChange; - -#ifdef WANT_DEBUGGER - DBG_GPUScanlineHook(scanline); -#endif - - // - // - // - if(scanline == (HardwarePALType ? 308 : 256)) // Will need to be redone if we ever allow for visible vertical overscan with NTSC. - { - if(sl_zero_reached) - { - //printf("Req Exit(visible fallthrough case): %u\n", scanline); - PSX_RequestMLExit(); - } - } - - if(scanline == (LinesPerField - 1)) - { - if(sl_zero_reached) - { - //printf("Req Exit(final fallthrough case): %u\n", scanline); - PSX_RequestMLExit(); - } - - if(DisplayMode & 0x20) - field = !field; - else - field = 0; - } - - if(scanline == 0) - { - assert(sl_zero_reached == false); - sl_zero_reached = true; - - if(DisplayMode & 0x20) - { - skip = false; - - if(DisplayMode & 0x08) // PAL - LinesPerField = 313 - field; - else // NTSC - LinesPerField = 263 - field; - } - else - { - field = 0; // May not be the correct place for this? - - if(DisplayMode & 0x08) // PAL - LinesPerField = 314; - else // NTSC - LinesPerField = 263; - } - - if(espec) - { - if((bool)(DisplayMode & 0x08) != HardwarePALType) - { - const uint32 black = surface->MakeColor(0, 0, 0); - - DisplayRect->x = 0; - DisplayRect->y = 0; - DisplayRect->w = 384; - DisplayRect->h = VisibleLineCount; - - for(int32 y = 0; y < DisplayRect->h; y++) - { - uint32 *dest = surface->pixels + y * surface->pitch32; - - LineWidths[y] = 384; - - for(int32 x = 0; x < 384; x++) - { - dest[x] = black; - } - } - char buffer[256]; - - snprintf(buffer, sizeof(buffer), ("VIDEO STANDARD MISMATCH")); - //DrawTextTrans(surface->pixels + ((DisplayRect->h / 2) - (13 / 2)) * surface->pitch32, surface->pitch32 << 2, DisplayRect->w, buffer, - //surface->MakeColor(0x00, 0xFF, 0x00), true, MDFN_FONT_6x13_12x13); - } - else - { - const uint32 black = surface->MakeColor(0, 0, 0); - - espec->InterlaceOn = (bool)(DisplayMode & 0x20); - espec->InterlaceField = (bool)(DisplayMode & 0x20) && field; - - DisplayRect->x = drxbo; - DisplayRect->y = 0; - DisplayRect->w = 0; - DisplayRect->h = VisibleLineCount << (bool)(DisplayMode & 0x20); - - // Clear ~0 state. - LineWidths[0] = 0; - - for(int i = 0; i < (DisplayRect->y + DisplayRect->h); i++) - { - surface->pixels[i * surface->pitch32 + 0] = - surface->pixels[i * surface->pitch32 + 1] = black; - LineWidths[i] = 2; - } - } - } - } - - // - // Don't mess with the order of evaluation of these scanline == VertXXX && (InVblankwhatever) if statements and the following IRQ/timer vblank stuff - // unless you know what you're doing!!! (IE you've run further tests to refine the behavior) - // - if(scanline == VertEnd && !InVBlank) - { - if(sl_zero_reached) - { - // Gameplay in Descent(NTSC) has vblank at scanline 236 - if(scanline >= (FirstVisibleLine + VisibleLineCount) || (scanline >= (HardwarePALType ? 260 : 232))) - { - //printf("Req Exit(vblank case): %u\n", scanline); - PSX_RequestMLExit(); - } - else - { - //printf("VBlank too early, chickening out early exit!\n"); - } - } - - //printf("VBLANK: %u\n", scanline); - InVBlank = true; - - DisplayFB_CurYOffset = 0; - - if((DisplayMode & 0x24) == 0x24) - field_ram_readout = !field; - else - field_ram_readout = 0; - } - - if(scanline == VertStart && InVBlank) - { - InVBlank = false; - - // Note to self: X-Men Mutant Academy relies on this being set on the proper scanline in 480i mode(otherwise it locks up on startup). - //if(HeightMode) - // DisplayFB_CurYOffset = field; - } - - IRQ_Assert(IRQ_VBLANK, InVBlank); - TIMER_SetVBlank(InVBlank); - // - // - // - - // Needs to occur even in vblank. - // Not particularly confident about the timing of this in regards to vblank and the upper bit(ODE) of the GPU status port, though the test that - // showed an oddity was pathological in that VertEnd < VertStart in it. - if((DisplayMode & 0x24) == 0x24) - DisplayFB_CurLineYReadout = (DisplayFB_YStart + (DisplayFB_CurYOffset << 1) + (InVBlank ? 0 : field_ram_readout)) & 0x1FF; - else - DisplayFB_CurLineYReadout = (DisplayFB_YStart + DisplayFB_CurYOffset) & 0x1FF; - - if((bool)(DisplayMode & 0x08) == HardwarePALType && scanline >= FirstVisibleLine && scanline < (FirstVisibleLine + VisibleLineCount) && !skip && espec) - { - uint32 *dest; - int32 dest_line; - int32 fb_x = DisplayFB_XStart * 2; - int32 dx_start = HorizStart, dx_end = HorizEnd; - - dest_line = ((scanline - FirstVisibleLine) << espec->InterlaceOn) + espec->InterlaceField; - dest = surface->pixels + (drxbo - dmpa) + dest_line * surface->pitch32; - - if(dx_end < dx_start) - dx_end = dx_start; - - dx_start = dx_start / DotClockRatios[dmc]; - dx_end = dx_end / DotClockRatios[dmc]; - - dx_start -= hmc_to_visible / DotClockRatios[dmc]; - dx_end -= hmc_to_visible / DotClockRatios[dmc]; - dx_start += 7; - dx_end += 7; - - if(dx_start < 0) - { - fb_x -= dx_start * ((DisplayMode & 0x10) ? 3 : 2); - fb_x &= 0x7FF; //0x3FF; - dx_start = 0; - } - - if((uint32)dx_end > dmw) - dx_end = dmw; - - if(InVBlank || DisplayOff) - dx_start = dx_end = 0; - - LineWidths[dest_line] = dmw - dmpa * 2; - - { - const uint16 *src = GPURAM[DisplayFB_CurLineYReadout]; - const uint32 black = surface->MakeColor(0, 0, 0); - - for(int32 x = 0; x < dx_start; x++) - dest[x] = black; - - //printf("%d %d %d - %d %d\n", scanline, dx_start, dx_end, HorizStart, HorizEnd); - if(surface->format.Rshift == 0 && surface->format.Gshift == 8 && surface->format.Bshift == 16) - ReorderRGB<0, 8, 16>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); - else if(surface->format.Rshift == 8 && surface->format.Gshift == 16 && surface->format.Bshift == 24) - ReorderRGB<8, 16, 24>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); - else if(surface->format.Rshift == 16 && surface->format.Gshift == 8 && surface->format.Bshift == 0) - ReorderRGB<16, 8, 0>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); - else if(surface->format.Rshift == 24 && surface->format.Gshift == 16 && surface->format.Bshift == 8) - ReorderRGB<24, 16, 8>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); - else - ReorderRGB_Var(surface->format.Rshift, surface->format.Gshift, surface->format.Bshift, DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); - - for(uint32 x = dx_end; x < dmw; x++) - dest[x] = black; - } - - //if(scanline == 64) - // printf("%u\n", sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio); - - PSX_GPULineHook(sys_timestamp, sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio, scanline == 0, dest, &surface->format, dmw, (hmc_to_visible - 220) / DotClockRatios[dmc], (HardwarePALType ? 53203425 : 53693182) / DotClockRatios[dmc], DotClockRatios[dmc]); - } - else - { - PSX_GPULineHook(sys_timestamp, sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio, scanline == 0, NULL, &surface->format, 0, 0, 0, 0); - } - - if(!InVBlank) - { - DisplayFB_CurYOffset = (DisplayFB_CurYOffset + 1) & 0x1FF; - } - } - } - } - - //puts("GPU Update End"); - - TheEnd: - lastts = sys_timestamp; - - { - int32 next_dt = LineClockCounter; - - next_dt = (((int64)next_dt << 16) - GPUClockCounter + GPUClockRatio - 1) / GPUClockRatio; - - next_dt = std::max(1, next_dt); - next_dt = std::min(128, next_dt); - - //printf("%d\n", next_dt); - - return(sys_timestamp + next_dt); - } -} - -void PS_GPU::StartFrame(EmulateSpecStruct *espec_arg) -{ - sl_zero_reached = false; - - if(!espec_arg) - { - espec = NULL; - surface = NULL; - DisplayRect = NULL; - LineWidths = NULL; - skip = true; - return; - } - - espec = espec_arg; - - surface = espec->surface; - DisplayRect = &espec->DisplayRect; - LineWidths = espec->LineWidths; - skip = espec->skip; - - if(espec->VideoFormatChanged) - { - const auto& f = surface->format; - - for(int rc = 0; rc < 0x8000; rc++) - { - const uint8 a = rc; - const uint8 b = rc >> 8; - - (OutputLUT + 0)[a] = ((a & 0x1F) << (3 + f.Rshift)) | ((a >> 5) << (3 + f.Gshift)); - (OutputLUT + 256)[b] = ((b & 0x3) << (6 + f.Gshift)) | (((b >> 2) & 0x1F) << (3 + f.Bshift)); - } - } -} + +INLINE void PS_GPU::Command_FBWrite(const uint32 *cb) +{ + assert(InCmd == INCMD_NONE); + + FBRW_X = (cb[1] >> 0) & 0x3FF; + FBRW_Y = (cb[1] >> 16) & 0x3FF; + + FBRW_W = (cb[2] >> 0) & 0x3FF; + FBRW_H = (cb[2] >> 16) & 0x1FF; + + if(!FBRW_W) + FBRW_W = 0x400; + + if(!FBRW_H) + FBRW_H = 0x200; + + FBRW_CurX = FBRW_X; + FBRW_CurY = FBRW_Y; + + if(FBRW_W != 0 && FBRW_H != 0) + InCmd = INCMD_FBWRITE; +} + +// +// FBRead: PS1 GPU in SCPH-5501 gives odd, inconsistent results when raw_height == 0, or +// raw_height != 0x200 && (raw_height & 0x1FF) == 0 +// +INLINE void PS_GPU::Command_FBRead(const uint32 *cb) +{ + assert(InCmd == INCMD_NONE); + + FBRW_X = (cb[1] >> 0) & 0x3FF; + FBRW_Y = (cb[1] >> 16) & 0x3FF; + + FBRW_W = (cb[2] >> 0) & 0x3FF; + FBRW_H = (cb[2] >> 16) & 0x3FF; + + if(!FBRW_W) + FBRW_W = 0x400; + + if(FBRW_H > 0x200) + FBRW_H &= 0x1FF; + + FBRW_CurX = FBRW_X; + FBRW_CurY = FBRW_Y; + + if(FBRW_W != 0 && FBRW_H != 0) + InCmd = INCMD_FBREAD; +} + +/* +INLINE void PS_GPU::RecalcTexPageStuff(uint32 tpage) +{ + + +} +*/ +INLINE void PS_GPU::Command_DrawMode(const uint32 *cb) +{ + const uint32 cmdw = *cb; + + TexPageX = (cmdw & 0xF) * 64; + TexPageY = (cmdw & 0x10) * 16; + + SpriteFlip = cmdw & 0x3000; + + abr = (cmdw >> 5) & 0x3; + TexMode = (cmdw >> 7) & 0x3; + + dtd = (cmdw >> 9) & 1; + dfe = (cmdw >> 10) & 1; + + if(TexDisableAllowChange) + { + TexDisable = (cmdw >> 11) & 1; + //printf("TexDisable: %02x\n", TexDisable); + } + + RecalcTexWindowStuff(); + //printf("*******************DFE: %d -- scanline=%d\n", dfe, scanline); +} + +INLINE void PS_GPU::Command_TexWindow(const uint32 *cb) +{ + tww = (*cb & 0x1F); + twh = ((*cb >> 5) & 0x1F); + twx = ((*cb >> 10) & 0x1F); + twy = ((*cb >> 15) & 0x1F); + + RecalcTexWindowStuff(); +} + +INLINE void PS_GPU::Command_Clip0(const uint32 *cb) +{ + ClipX0 = *cb & 1023; + ClipY0 = (*cb >> 10) & 1023; + + //fprintf(stderr, "[GPU] Clip0: x=%d y=%d, raw=0x%08x --- %d\n", ClipX0, ClipY0, *cb, scanline); +} + +INLINE void PS_GPU::Command_Clip1(const uint32 *cb) +{ + ClipX1 = *cb & 1023; + ClipY1 = (*cb >> 10) & 1023; + + //fprintf(stderr, "[GPU] Clip1: x=%d y=%d, raw=0x%08x --- %d\n", ClipX1, ClipY1, *cb, scanline); +} + +INLINE void PS_GPU::Command_DrawingOffset(const uint32 *cb) +{ + OffsX = sign_x_to_s32(11, (*cb & 2047)); + OffsY = sign_x_to_s32(11, ((*cb >> 11) & 2047)); + + //fprintf(stderr, "[GPU] Drawing offset: x=%d y=%d, raw=0x%08x --- %d\n", OffsX, OffsY, *cb, scanline); +} + +INLINE void PS_GPU::Command_MaskSetting(const uint32 *cb) +{ + //printf("Mask setting: %08x\n", *cb); + MaskSetOR = (*cb & 1) ? 0x8000 : 0x0000; + MaskEvalAND = (*cb & 2) ? 0x8000 : 0x0000; +} + +void PS_GPU::InvalidateCache(void) +{ + CLUT_Cache_VB = ~0U; + + for(int i=0;iCommand_ClearCache(cb); +} + +static void G_Command_IRQ(PS_GPU* g, const uint32 *cb) +{ + g->Command_IRQ(cb); +} + +static void G_Command_FBFill(PS_GPU* g, const uint32 *cb) +{ + g->Command_FBFill(cb); +} + +static void G_Command_FBCopy(PS_GPU* g, const uint32 *cb) +{ + g->Command_FBCopy(cb); +} + +static void G_Command_FBWrite(PS_GPU* g, const uint32 *cb) +{ + g->Command_FBWrite(cb); +} + +static void G_Command_FBRead(PS_GPU* g, const uint32 *cb) +{ + g->Command_FBRead(cb); +} + +static void G_Command_DrawMode(PS_GPU* g, const uint32 *cb) +{ + g->Command_DrawMode(cb); +} + +static void G_Command_TexWindow(PS_GPU* g, const uint32 *cb) +{ + g->Command_TexWindow(cb); +} + +static void G_Command_Clip0(PS_GPU* g, const uint32 *cb) +{ + g->Command_Clip0(cb); +} + +static void G_Command_Clip1(PS_GPU* g, const uint32 *cb) +{ + g->Command_Clip1(cb); +} + +static void G_Command_DrawingOffset(PS_GPU* g, const uint32 *cb) +{ + g->Command_DrawingOffset(cb); +} + +static void G_Command_MaskSetting(PS_GPU* g, const uint32 *cb) +{ + g->Command_MaskSetting(cb); +} + +CTEntry PS_GPU::Commands[0x100]; + +const CTEntry PS_GPU::Commands_00_1F[0x20] = +{ + /* 0x00 */ + NULLCMD(), + OTHER_HELPER(1, 2, false, G_Command_ClearCache), + OTHER_HELPER(3, 3, false, G_Command_FBFill), + + NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), + NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), + + /* 0x10 */ + NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), + NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), + + /* 0x1F */ + OTHER_HELPER(1, 1, false, G_Command_IRQ) +}; + +const CTEntry PS_GPU::Commands_80_FF[0x80] = +{ + /* 0x80 ... 0x9F */ + OTHER_HELPER_X32(4, 2, false, G_Command_FBCopy), + + /* 0xA0 ... 0xBF */ + OTHER_HELPER_X32(3, 2, false, G_Command_FBWrite), + + /* 0xC0 ... 0xDF */ + OTHER_HELPER_X32(3, 2, false, G_Command_FBRead), + + /* 0xE0 */ + + NULLCMD(), + OTHER_HELPER(1, 2, false, G_Command_DrawMode), + OTHER_HELPER(1, 2, false, G_Command_TexWindow), + OTHER_HELPER(1, 1, true, G_Command_Clip0), + OTHER_HELPER(1, 1, true, G_Command_Clip1), + OTHER_HELPER(1, 1, true, G_Command_DrawingOffset), + OTHER_HELPER(1, 2, false, G_Command_MaskSetting), + + NULLCMD(), + NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), + + /* 0xF0 */ + NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), + NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD(), NULLCMD() +}; + +void PS_GPU::ProcessFIFO(void) +{ + if(!BlitterFIFO.CanRead()) + return; + + switch(InCmd) + { + default: + abort(); + break; + + case INCMD_NONE: + break; + + case INCMD_FBREAD: + puts("BOGUS SALAMANDERS, CAPTAIN!"); + return; + + case INCMD_FBWRITE: + { + uint32 InData = BlitterFIFO.Read(); + + for(int i = 0; i < 2; i++) + { + if(!(GPURAM[FBRW_CurY & 511][FBRW_CurX & 1023] & MaskEvalAND)) + GPURAM[FBRW_CurY & 511][FBRW_CurX & 1023] = InData | MaskSetOR; + + FBRW_CurX++; + if(FBRW_CurX == (FBRW_X + FBRW_W)) + { + FBRW_CurX = FBRW_X; + FBRW_CurY++; + if(FBRW_CurY == (FBRW_Y + FBRW_H)) + { + InCmd = INCMD_NONE; + break; // Break out of the for() loop. + } + } + InData >>= 16; + } + return; + } + break; + + case INCMD_QUAD: + { + if(DrawTimeAvail < 0) + return; + + const uint32 cc = InCmd_CC; + const CTEntry *command = &Commands[cc]; + unsigned vl = 1 + (bool)(cc & 0x4) + (bool)(cc & 0x10); + uint32 CB[3]; + + if(BlitterFIFO.CanRead() >= vl) + { + for(unsigned i = 0; i < vl; i++) + { + CB[i] = BlitterFIFO.Read(); + } + + command->func[abr][TexMode | (MaskEvalAND ? 0x4 : 0x0)](this, CB); + } + return; + } + break; + + case INCMD_PLINE: + { + if(DrawTimeAvail < 0) + return; + + const uint32 cc = InCmd_CC; + const CTEntry *command = &Commands[cc]; + unsigned vl = 1 + (bool)(InCmd_CC & 0x10); + uint32 CB[2]; + + if((BlitterFIFO.Peek() & 0xF000F000) == 0x50005000) + { + BlitterFIFO.Read(); + InCmd = INCMD_NONE; + return; + } + + if(BlitterFIFO.CanRead() >= vl) + { + for(unsigned i = 0; i < vl; i++) + { + CB[i] = BlitterFIFO.Read(); + } + + command->func[abr][TexMode | (MaskEvalAND ? 0x4 : 0x0)](this, CB); + } + return; + } + break; + } + + const uint32 cc = BlitterFIFO.Peek() >> 24; + const CTEntry *command = &Commands[cc]; + + if(DrawTimeAvail < 0 && !command->ss_cmd) + return; + + if(BlitterFIFO.CanRead() >= command->len) + { + uint32 CB[0x10]; + + for(unsigned i = 0; i < command->len; i++) + CB[i] = BlitterFIFO.Read(); + + if(!command->ss_cmd) + DrawTimeAvail -= 2; + +#if 0 + PSX_WARNING("[GPU] Command: %08x %s %d %d %d", CB[0], command->name, command->len, scanline, DrawTimeAvail); + if(1) + { + printf("[GPU] "); + for(unsigned i = 0; i < command->len; i++) + printf("0x%08x ", CB[i]); + printf("\n"); + } +#endif + // A very very ugly kludge to support texture mode specialization. fixme/cleanup/SOMETHING in the future. + if(cc >= 0x20 && cc <= 0x3F && (cc & 0x4)) + { + // + // Don't alter SpriteFlip here. + // + const uint32 tpage = CB[4 + ((cc >> 4) & 0x1)] >> 16; + + TexPageX = (tpage & 0xF) * 64; + TexPageY = (tpage & 0x10) * 16; + + abr = (tpage >> 5) & 0x3; + TexMode = (tpage >> 7) & 0x3; + RecalcTexWindowStuff(); + } + + if(!command->func[abr][TexMode]) + { + if(CB[0]) + PSX_WARNING("[GPU] Unknown command: %08x, %d", CB[0], scanline); + } + else + { + command->func[abr][TexMode | (MaskEvalAND ? 0x4 : 0x0)](this, CB); + } + } +} + +INLINE void PS_GPU::WriteCB(uint32 InData) +{ + if(BlitterFIFO.CanRead() >= 0x10 && (InCmd != INCMD_NONE || (BlitterFIFO.CanRead() - 0x10) >= Commands[BlitterFIFO.Peek() >> 24].fifo_fb_len)) + { + PSX_DBG(PSX_DBG_WARNING, "GPU FIFO overflow!!!\n"); + return; + } + + BlitterFIFO.Write(InData); + ProcessFIFO(); +} + +void PS_GPU::Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V) +{ + V <<= (A & 3) * 8; + + if(A & 4) // GP1 ("Control") + { + uint32 command = V >> 24; + + V &= 0x00FFFFFF; + + //PSX_WARNING("[GPU] Control command: %02x %06x %d", command, V, scanline); + + switch(command) + { + /* + 0x40-0xFF do NOT appear to be mirrors, at least not on my PS1's GPU. + */ + default: PSX_WARNING("[GPU] Unknown control command %02x - %06x", command, V); + break; + + case 0x00: // Reset GPU + //printf("\n\n************ Soft Reset %u ********* \n\n", scanline); + SoftReset(); + break; + + case 0x01: // Reset command buffer + if(DrawTimeAvail < 0) + DrawTimeAvail = 0; + BlitterFIFO.Flush(); + InCmd = INCMD_NONE; + break; + + case 0x02: // Acknowledge IRQ + IRQPending = false; + IRQ_Assert(IRQ_GPU, IRQPending); + break; + + case 0x03: // Display enable + DisplayOff = V & 1; + break; + + case 0x04: // DMA Setup + DMAControl = V & 0x3; + break; + + case 0x05: // Start of display area in framebuffer + DisplayFB_XStart = V & 0x3FE; // Lower bit is apparently ignored. + DisplayFB_YStart = (V >> 10) & 0x1FF; + break; + + case 0x06: // Horizontal display range + HorizStart = V & 0xFFF; + HorizEnd = (V >> 12) & 0xFFF; + break; + + case 0x07: + VertStart = V & 0x3FF; + VertEnd = (V >> 10) & 0x3FF; + break; + + case 0x08: + //printf("\n\nDISPLAYMODE SET: 0x%02x, %u *************************\n\n\n", V & 0xFF, scanline); + DisplayMode = V & 0xFF; + break; + + case 0x09: + TexDisableAllowChange = V & 1; + break; + + case 0x10: // GPU info(?) + switch(V & 0xF) + { + // DataReadBuffer must remain unchanged for any unhandled GPU info index. + default: break; + + case 0x2: DataReadBufferEx &= 0xFFF00000; + DataReadBufferEx |= (tww << 0) | (twh << 5) | (twx << 10) | (twy << 15); + DataReadBuffer = DataReadBufferEx; + break; + + case 0x3: DataReadBufferEx &= 0xFFF00000; + DataReadBufferEx |= (ClipY0 << 10) | ClipX0; + DataReadBuffer = DataReadBufferEx; + break; + + case 0x4: DataReadBufferEx &= 0xFFF00000; + DataReadBufferEx |= (ClipY1 << 10) | ClipX1; + DataReadBuffer = DataReadBufferEx; + break; + + case 0x5: DataReadBufferEx &= 0xFFC00000; + DataReadBufferEx |= (OffsX & 2047) | ((OffsY & 2047) << 11); + DataReadBuffer = DataReadBufferEx; + break; + + case 0x7: DataReadBufferEx = 2; + DataReadBuffer = DataReadBufferEx; + break; + + case 0x8: DataReadBufferEx = 0; + DataReadBuffer = DataReadBufferEx; + break; + } + //fprintf(stderr, "[GPU] CC 0x10:0x%02x, DRB=0x%02x\n", V & 0xF, DataReadBuffer); + break; + + } + } + else // GP0 ("Data") + { + //uint32 command = V >> 24; + //printf("Meow command: %02x\n", command); + //assert(!(DMAControl & 2)); + WriteCB(V); + } +} + + +void PS_GPU::WriteDMA(uint32 V) +{ + WriteCB(V); +} + +INLINE uint32 PS_GPU::ReadData(void) +{ + if(InCmd == INCMD_FBREAD) + { + DataReadBufferEx = 0; + for(int i = 0; i < 2; i++) + { + DataReadBufferEx |= GPURAM[FBRW_CurY & 511][FBRW_CurX & 1023] << (i * 16); + + FBRW_CurX++; + if(FBRW_CurX == (FBRW_X + FBRW_W)) + { + if((FBRW_CurY + 1) == (FBRW_Y + FBRW_H)) + { + InCmd = INCMD_NONE; + } + else + { + FBRW_CurY++; + FBRW_CurX = FBRW_X; + } + } + } + + return DataReadBufferEx; + } + + return DataReadBuffer; +} + +uint32 PS_GPU::ReadDMA(void) +{ + return ReadData(); +} + +uint32 PS_GPU::Read(const pscpu_timestamp_t timestamp, uint32 A) +{ + uint32 ret = 0; + + if(A & 4) // Status + { + ret = (((DisplayMode << 1) & 0x7F) | ((DisplayMode >> 6) & 1)) << 16; + + ret |= (DisplayMode & 0x80) << 7; + + ret |= DMAControl << 29; + + ret |= (DisplayFB_CurLineYReadout & 1) << 31; + + ret |= (!field) << 13; + + if(DMAControl & 0x02) + ret |= 1 << 25; + + ret |= IRQPending << 24; + + ret |= DisplayOff << 23; + + if(InCmd == INCMD_NONE && DrawTimeAvail >= 0 && BlitterFIFO.CanRead() == 0x00) // GPU idle bit. + ret |= 1 << 26; + + if(InCmd == INCMD_FBREAD) // Might want to more accurately emulate this in the future? + ret |= (1 << 27); + + ret |= CalcFIFOReadyBit() << 28; // FIFO has room bit? (kinda). + + // + // + ret |= TexPageX >> 6; + ret |= TexPageY >> 4; + ret |= abr << 5; + ret |= TexMode << 7; + + ret |= dtd << 9; + ret |= dfe << 10; + + if(MaskSetOR) + ret |= 1 << 11; + + if(MaskEvalAND) + ret |= 1 << 12; + + ret |= TexDisable << 15; + } + else // "Data" + ret = ReadData(); + + if(DMAControl & 2) + { + //PSX_WARNING("[GPU READ WHEN (DMACONTROL&2)] 0x%08x - ret=0x%08x, scanline=%d", A, ret, scanline); + } + + return(ret >> ((A & 3) * 8)); +} + +#if 0 +static INLINE uint32 MDFN_NOWARN_UNUSED ShiftHelper(uint32 val, int shamt, uint32 mask) +{ + if(shamt < 0) + return((val >> (-shamt)) & mask); + else + return((val << shamt) & mask); +} +#endif + +#pragma GCC push_options +#pragma GCC optimize("no-unroll-loops,no-peel-loops,no-crossjumping") +INLINE void PS_GPU::ReorderRGB_Var(uint32 out_Rshift, uint32 out_Gshift, uint32 out_Bshift, bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x) +{ + if(bpp24) // 24bpp + { + for(int32 x = dx_start; MDFN_LIKELY(x < dx_end); x++) + { + uint32 srcpix; + + srcpix = src[(fb_x >> 1) + 0] | (src[((fb_x >> 1) + 1) & 0x7FF] << 16); + srcpix >>= (fb_x & 1) * 8; + + dest[x] = (((srcpix >> 0) << out_Rshift) & (0xFF << out_Rshift)) | (((srcpix >> 8) << out_Gshift) & (0xFF << out_Gshift)) | + (((srcpix >> 16) << out_Bshift) & (0xFF << out_Bshift)); + + fb_x = (fb_x + 3) & 0x7FF; + } + } // 15bpp + else + { + for(int32 x = dx_start; MDFN_LIKELY(x < dx_end); x++) + { + uint32 srcpix = src[fb_x >> 1]; + +#if 1 + dest[x] = OutputLUT[(uint8)srcpix] | (OutputLUT + 256)[(srcpix >> 8) & 0x7F]; +#else + dest[x] = ShiftHelper(srcpix, out_Rshift + 3 - 0, (0xF8 << out_Rshift)) | + ShiftHelper(srcpix, out_Gshift + 3 - 5, (0xF8 << out_Gshift)) | + ShiftHelper(srcpix, out_Bshift + 3 - 10, (0xF8 << out_Bshift)); +#endif + fb_x = (fb_x + 2) & 0x7FF; + } + } + +} + +template +void NO_INLINE PS_GPU::ReorderRGB(bool bpp24, const uint16 *src, uint32 *dest, const int32 dx_start, const int32 dx_end, int32 fb_x) +{ + ReorderRGB_Var(out_Rshift, out_Gshift, out_Bshift, bpp24, src, dest, dx_start, dx_end, fb_x); +} +#pragma GCC pop_options + +pscpu_timestamp_t PS_GPU::Update(const pscpu_timestamp_t sys_timestamp) +{ + static const uint32 DotClockRatios[5] = { 10, 8, 5, 4, 7 }; + const uint32 dmc = (DisplayMode & 0x40) ? 4 : (DisplayMode & 0x3); + const uint32 dmw = 2800 / DotClockRatios[dmc]; // Must be <= (768 - 32) + const uint32 dmpa = (2800 - (hide_hoverscan ? 2640 : 2800)) / DotClockRatios[dmc] / 2; // Must be <= 32 + const uint32 drxbo = 32; + + int32 sys_clocks = sys_timestamp - lastts; + int32 gpu_clocks; + + //printf("GPUISH: %d\n", sys_timestamp - lastts); + + if(!sys_clocks) + goto TheEnd; + + DrawTimeAvail += sys_clocks << 1; + + if(DrawTimeAvail > 256) + DrawTimeAvail = 256; + + ProcessFIFO(); + + //puts("GPU Update Start"); + + GPUClockCounter += (uint64)sys_clocks * GPUClockRatio; + + gpu_clocks = GPUClockCounter >> 16; + GPUClockCounter -= gpu_clocks << 16; + + while(gpu_clocks > 0) + { + int32 chunk_clocks = gpu_clocks; + int32 dot_clocks; + + if(chunk_clocks > LineClockCounter) + { + //printf("Chunk: %u, LCC: %u\n", chunk_clocks, LineClockCounter); + chunk_clocks = LineClockCounter; + } + + gpu_clocks -= chunk_clocks; + LineClockCounter -= chunk_clocks; + + DotClockCounter += chunk_clocks; + dot_clocks = DotClockCounter / DotClockRatios[DisplayMode & 0x3]; + DotClockCounter -= dot_clocks * DotClockRatios[DisplayMode & 0x3]; + + TIMER_AddDotClocks(dot_clocks); + + + if(!LineClockCounter) + { + PSX_SetEventNT(PSX_EVENT_TIMER, TIMER_Update(sys_timestamp)); // We could just call this at the top of GPU_Update(), but do it here for slightly less CPU usage(presumably). + + LinePhase = (LinePhase + 1) & 1; + + if(LinePhase) + { + TIMER_SetHRetrace(true); + LineClockCounter = 200; + TIMER_ClockHRetrace(); + } + else + { + const unsigned int FirstVisibleLine = LineVisFirst + (HardwarePALType ? 20 : 16); + const unsigned int VisibleLineCount = LineVisLast + 1 - LineVisFirst; //HardwarePALType ? 288 : 240; + + TIMER_SetHRetrace(false); + + if(DisplayMode & 0x08) + LineClockCounter = 3405 - 200; + else + LineClockCounter = 3412 + PhaseChange - 200; + + scanline = (scanline + 1) % LinesPerField; + PhaseChange = !PhaseChange; + +#ifdef WANT_DEBUGGER + DBG_GPUScanlineHook(scanline); +#endif + + // + // + // + if(scanline == (HardwarePALType ? 308 : 256)) // Will need to be redone if we ever allow for visible vertical overscan with NTSC. + { + if(sl_zero_reached) + { + //printf("Req Exit(visible fallthrough case): %u\n", scanline); + PSX_RequestMLExit(); + } + } + + if(scanline == (LinesPerField - 1)) + { + if(sl_zero_reached) + { + //printf("Req Exit(final fallthrough case): %u\n", scanline); + PSX_RequestMLExit(); + } + + if(DisplayMode & 0x20) + field = !field; + else + field = 0; + } + + if(scanline == 0) + { + assert(sl_zero_reached == false); + sl_zero_reached = true; + + if(DisplayMode & 0x20) + { + skip = false; + + if(DisplayMode & 0x08) // PAL + LinesPerField = 313 - field; + else // NTSC + LinesPerField = 263 - field; + } + else + { + field = 0; // May not be the correct place for this? + + if(DisplayMode & 0x08) // PAL + LinesPerField = 314; + else // NTSC + LinesPerField = 263; + } + + if(espec) + { + if((bool)(DisplayMode & 0x08) != HardwarePALType) + { + const uint32 black = surface->MakeColor(0, 0, 0); + + DisplayRect->x = 0; + DisplayRect->y = 0; + DisplayRect->w = 384; + DisplayRect->h = VisibleLineCount; + + for(int32 y = 0; y < DisplayRect->h; y++) + { + uint32 *dest = surface->pixels + y * surface->pitch32; + + LineWidths[y] = 384; + + for(int32 x = 0; x < 384; x++) + { + dest[x] = black; + } + } + + if(!DisplayOff) + { + char buffer[256]; + + snprintf(buffer, sizeof(buffer), ("VIDEO STANDARD MISMATCH")); + //DrawTextTrans(surface->pixels + ((DisplayRect->h / 2) - (13 / 2)) * surface->pitch32, surface->pitch32 << 2, DisplayRect->w, buffer, + //surface->MakeColor(0x00, 0xFF, 0x00), true, MDFN_FONT_6x13_12x13); + } + } + else + { + const uint32 black = surface->MakeColor(0, 0, 0); + + espec->InterlaceOn = (bool)(DisplayMode & 0x20); + espec->InterlaceField = (bool)(DisplayMode & 0x20) && field; + + DisplayRect->x = drxbo; + DisplayRect->y = 0; + DisplayRect->w = 0; + DisplayRect->h = VisibleLineCount << (bool)(DisplayMode & 0x20); + + // Clear ~0 state. + LineWidths[0] = 0; + + for(int i = 0; i < (DisplayRect->y + DisplayRect->h); i++) + { + surface->pixels[i * surface->pitch32 + 0] = + surface->pixels[i * surface->pitch32 + 1] = black; + LineWidths[i] = 2; + } + } + } + } + + // + // Don't mess with the order of evaluation of these scanline == VertXXX && (InVblankwhatever) if statements and the following IRQ/timer vblank stuff + // unless you know what you're doing!!! (IE you've run further tests to refine the behavior) + // + if(scanline == VertEnd && !InVBlank) + { + if(sl_zero_reached) + { + // Gameplay in Descent(NTSC) has vblank at scanline 236 + // + // Mikagura Shoujo Tanteidan has vblank at scanline 192 during intro + // FMV(which we don't handle here because low-latency in that case is not so important). + // + if(scanline >= (HardwarePALType ? 260 : 232)) + { + //printf("Req Exit(vblank case): %u\n", scanline); + PSX_RequestMLExit(); + } + else + { + //printf("VBlank too early, chickening out early exit: %u!\n", scanline); + } + } + + //printf("VBLANK: %u\n", scanline); + InVBlank = true; + + DisplayFB_CurYOffset = 0; + + if((DisplayMode & 0x24) == 0x24) + field_ram_readout = !field; + else + field_ram_readout = 0; + } + + if(scanline == VertStart && InVBlank) + { + InVBlank = false; + + // Note to self: X-Men Mutant Academy relies on this being set on the proper scanline in 480i mode(otherwise it locks up on startup). + //if(HeightMode) + // DisplayFB_CurYOffset = field; + } + + IRQ_Assert(IRQ_VBLANK, InVBlank); + TIMER_SetVBlank(InVBlank); + // + // + // + + // Needs to occur even in vblank. + // Not particularly confident about the timing of this in regards to vblank and the upper bit(ODE) of the GPU status port, though the test that + // showed an oddity was pathological in that VertEnd < VertStart in it. + if((DisplayMode & 0x24) == 0x24) + DisplayFB_CurLineYReadout = (DisplayFB_YStart + (DisplayFB_CurYOffset << 1) + (InVBlank ? 0 : field_ram_readout)) & 0x1FF; + else + DisplayFB_CurLineYReadout = (DisplayFB_YStart + DisplayFB_CurYOffset) & 0x1FF; + + if((bool)(DisplayMode & 0x08) == HardwarePALType && scanline >= FirstVisibleLine && scanline < (FirstVisibleLine + VisibleLineCount) && !skip && espec) + { + uint32 *dest; + int32 dest_line; + int32 fb_x = DisplayFB_XStart * 2; + int32 dx_start = HorizStart, dx_end = HorizEnd; + + dest_line = ((scanline - FirstVisibleLine) << espec->InterlaceOn) + espec->InterlaceField; + dest = surface->pixels + (drxbo - dmpa) + dest_line * surface->pitch32; + + if(dx_end < dx_start) + dx_end = dx_start; + + dx_start = dx_start / DotClockRatios[dmc]; + dx_end = dx_end / DotClockRatios[dmc]; + + dx_start -= hmc_to_visible / DotClockRatios[dmc]; + dx_end -= hmc_to_visible / DotClockRatios[dmc]; + dx_start += 7; + dx_end += 7; + + if(dx_start < 0) + { + fb_x -= dx_start * ((DisplayMode & 0x10) ? 3 : 2); + fb_x &= 0x7FF; //0x3FF; + dx_start = 0; + } + + if((uint32)dx_end > dmw) + dx_end = dmw; + + if(InVBlank || DisplayOff) + dx_start = dx_end = 0; + + LineWidths[dest_line] = dmw - dmpa * 2; + + { + const uint16 *src = GPURAM[DisplayFB_CurLineYReadout]; + const uint32 black = surface->MakeColor(0, 0, 0); + + for(int32 x = 0; x < dx_start; x++) + dest[x] = black; + + //printf("%d %d %d - %d %d\n", scanline, dx_start, dx_end, HorizStart, HorizEnd); + if(surface->format.Rshift == 0 && surface->format.Gshift == 8 && surface->format.Bshift == 16) + ReorderRGB<0, 8, 16>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); + else if(surface->format.Rshift == 8 && surface->format.Gshift == 16 && surface->format.Bshift == 24) + ReorderRGB<8, 16, 24>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); + else if(surface->format.Rshift == 16 && surface->format.Gshift == 8 && surface->format.Bshift == 0) + ReorderRGB<16, 8, 0>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); + else if(surface->format.Rshift == 24 && surface->format.Gshift == 16 && surface->format.Bshift == 8) + ReorderRGB<24, 16, 8>(DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); + else + ReorderRGB_Var(surface->format.Rshift, surface->format.Gshift, surface->format.Bshift, DisplayMode & 0x10, src, dest, dx_start, dx_end, fb_x); + + for(uint32 x = dx_end; x < dmw; x++) + dest[x] = black; + } + + //if(scanline == 64) + // printf("%u\n", sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio); + + PSX_GPULineHook(sys_timestamp, sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio, scanline == 0, dest, &surface->format, dmw, (hmc_to_visible - 220) / DotClockRatios[dmc], (HardwarePALType ? 53203425 : 53693182) / DotClockRatios[dmc], DotClockRatios[dmc]); + } + else + { + PSX_GPULineHook(sys_timestamp, sys_timestamp - ((uint64)gpu_clocks * 65536) / GPUClockRatio, scanline == 0, NULL, &surface->format, 0, 0, 0, 0); + } + + if(!InVBlank) + { + DisplayFB_CurYOffset = (DisplayFB_CurYOffset + 1) & 0x1FF; + } + } + PSX_SetEventNT(PSX_EVENT_TIMER, TIMER_Update(sys_timestamp)); // Mostly so the next event time gets recalculated properly in regards to our calls + // to TIMER_SetVBlank() and TIMER_SetHRetrace(). + } // end if(!LineClockCounter) + } // end while(gpu_clocks > 0) + + //puts("GPU Update End"); + + TheEnd: + lastts = sys_timestamp; + + { + int32 next_dt = LineClockCounter; + + next_dt = (((int64)next_dt << 16) - GPUClockCounter + GPUClockRatio - 1) / GPUClockRatio; + + next_dt = std::max(1, next_dt); + next_dt = std::min(128, next_dt); + + //printf("%d\n", next_dt); + + return(sys_timestamp + next_dt); + } +} + +void PS_GPU::StartFrame(EmulateSpecStruct *espec_arg) +{ + sl_zero_reached = false; + + if(!espec_arg) + { + espec = NULL; + surface = NULL; + DisplayRect = NULL; + LineWidths = NULL; + skip = true; + return; + } + + espec = espec_arg; + + surface = espec->surface; + DisplayRect = &espec->DisplayRect; + LineWidths = espec->LineWidths; + skip = espec->skip; + + if(espec->VideoFormatChanged) + { + const auto& f = surface->format; + + for(int rc = 0; rc < 0x8000; rc++) + { + const uint8 a = rc; + const uint8 b = rc >> 8; + + (OutputLUT + 0)[a] = ((a & 0x1F) << (3 + f.Rshift)) | ((a >> 5) << (3 + f.Gshift)); + (OutputLUT + 256)[b] = ((b & 0x3) << (6 + f.Gshift)) | (((b >> 2) & 0x1F) << (3 + f.Bshift)); + } + } +} SYNCFUNC(PS_GPU) { @@ -1490,6 +1530,9 @@ SYNCFUNC(PS_GPU) NSS(MaskSetOR); NSS(MaskEvalAND); + NSS(TexDisable); + NSS(TexDisableAllowChange); + NSS(tww); NSS(twh); NSS(twx); @@ -1506,6 +1549,7 @@ SYNCFUNC(PS_GPU) SSS(BlitterFIFO); NSS(DataReadBuffer); + NSS(DataReadBufferEx); NSS(IRQPending); diff --git a/psx/octoshock/psx/gpu.h b/psx/octoshock/psx/gpu.h index 6402a3e9a3..c96e4dce2e 100644 --- a/psx/octoshock/psx/gpu.h +++ b/psx/octoshock/psx/gpu.h @@ -3,7 +3,7 @@ #pragma once -#include "cdrom/SimpleFIFO.h" +#include "FastFIFO.h" #include "git.h" struct ShockRenderOptions; @@ -72,7 +72,7 @@ class PS_GPU if(InCmd & (INCMD_FBREAD | INCMD_FBWRITE)) return(false); - if(BlitterFIFO.CanRead() >= Commands[BlitterFIFO.ReadUnit(true) >> 24].fifo_fb_len) + if(BlitterFIFO.CanRead() >= Commands[BlitterFIFO.Peek() >> 24].fifo_fb_len) return(false); return(true); @@ -155,18 +155,21 @@ class PS_GPU int32 OffsX; int32 OffsY; - - bool dtd; - bool dfe; - - uint32 MaskSetOR; - uint32 MaskEvalAND; - - uint8 tww, twh, twx, twy; - - int32 TexPageX; - int32 TexPageY; - + + bool dtd; + bool dfe; + + uint32 MaskSetOR; + uint32 MaskEvalAND; + + bool TexDisable; + bool TexDisableAllowChange; + + uint8 tww, twh, twx, twy; + + uint32 TexPageX; + uint32 TexPageY; + uint32 SpriteFlip; uint32 abr; @@ -230,9 +233,9 @@ class PS_GPU static const CTEntry Commands_60_7F[0x20]; static const CTEntry Commands_80_FF[0x80]; - SimpleFIFO BlitterFIFO; - - uint32 DataReadBuffer; + FastFIFO BlitterFIFO; // 0x10 on actual PS1 GPU, 0x20 here(see comment at top of gpu.h) + uint32 DataReadBuffer; + uint32 DataReadBufferEx; bool IRQPending; // diff --git a/psx/octoshock/psx/gpu_line.cpp b/psx/octoshock/psx/gpu_line.cpp index ed4e52573d..34791eb849 100644 --- a/psx/octoshock/psx/gpu_line.cpp +++ b/psx/octoshock/psx/gpu_line.cpp @@ -17,6 +17,7 @@ #include "psx.h" #include "gpu.h" +#include "math_ops.h" namespace MDFN_IEN_PSX { @@ -24,8 +25,8 @@ namespace MDFN_IEN_PSX struct line_fxp_coord { - int64 x, y; - int32 r, g, b; + uint64 x, y; + uint32 r, g, b; }; struct line_fxp_step @@ -40,8 +41,8 @@ enum { Line_RGB_FractBits = 12 }; template static INLINE void LinePointToFXPCoord(const line_point &point, const line_fxp_step &step, line_fxp_coord &coord) { - coord.x = ((int64)point.x << Line_XY_FractBits) | (1LL << (Line_XY_FractBits - 1)); - coord.y = ((int64)point.y << Line_XY_FractBits) | (1LL << (Line_XY_FractBits - 1)); + coord.x = ((uint64)point.x << Line_XY_FractBits) | (1ULL << (Line_XY_FractBits - 1)); + coord.y = ((uint64)point.y << Line_XY_FractBits) | (1ULL << (Line_XY_FractBits - 1)); coord.x -= 1024; @@ -56,10 +57,9 @@ static INLINE void LinePointToFXPCoord(const line_point &point, const line_fxp_s } } -template -static INLINE T LineDivide(T delta, int32 dk) +static INLINE int64 LineDivide(int64 delta, int32 dk) { - delta <<= bits; + delta = (uint64)delta << Line_XY_FractBits; if(delta < 0) delta -= dk - 1; @@ -86,28 +86,28 @@ static INLINE void LinePointsToFXPStep(const line_point &point0, const line_poin return; } - step.dx_dk = LineDivide(point1.x - point0.x, dk); - step.dy_dk = LineDivide(point1.y - point0.y, dk); + step.dx_dk = LineDivide(point1.x - point0.x, dk); + step.dy_dk = LineDivide(point1.y - point0.y, dk); if(goraud) { - step.dr_dk = ((point1.r - point0.r) << Line_RGB_FractBits) / dk; - step.dg_dk = ((point1.g - point0.g) << Line_RGB_FractBits) / dk; - step.db_dk = ((point1.b - point0.b) << Line_RGB_FractBits) / dk; + step.dr_dk = (int32)((uint32)(point1.r - point0.r) << Line_RGB_FractBits) / dk; + step.dg_dk = (int32)((uint32)(point1.g - point0.g) << Line_RGB_FractBits) / dk; + step.db_dk = (int32)((uint32)(point1.b - point0.b) << Line_RGB_FractBits) / dk; } } template -static INLINE void AddLineStep(line_fxp_coord &point, const line_fxp_step &step, int32 count = 1) +static INLINE void AddLineStep(line_fxp_coord &point, const line_fxp_step &step) { - point.x += step.dx_dk * count; - point.y += step.dy_dk * count; + point.x += step.dx_dk; + point.y += step.dy_dk; if(goraud) { - point.r += step.dr_dk * count; - point.g += step.dg_dk * count; - point.b += step.db_dk * count; + point.r += step.dr_dk; + point.g += step.dg_dk; + point.b += step.db_dk; } } @@ -125,19 +125,12 @@ void PS_GPU::DrawLine(line_point *points) k = (i_dx > i_dy) ? i_dx : i_dy; if(i_dx >= 1024) - { - PSX_DBG(PSX_DBG_WARNING, "[GPU] Line too long: i_dx=%d\n", i_dx); return; - } if(i_dy >= 512) - { - PSX_DBG(PSX_DBG_WARNING, "[GPU] Line too long: i_dy=%d\n", i_dy); return; - } - // May not be correct(do tests for the case of k == i_dy on real thing. - if(points[0].x > points[1].x) + if(points[0].x >= points[1].x && k) { line_point tmp = points[1]; @@ -145,7 +138,7 @@ void PS_GPU::DrawLine(line_point *points) points[0] = tmp; } - DrawTimeAvail -= k * ((BlendMode >= 0) ? 2 : 1); + DrawTimeAvail -= k * 2; // // @@ -181,7 +174,7 @@ void PS_GPU::DrawLine(line_point *points) b = points[0].b; } - if(goraud && dtd) + if(dtd) { pix |= DitherLUT[y & 3][x & 3][r] << 0; pix |= DitherLUT[y & 3][x & 3][g] << 5; diff --git a/psx/octoshock/psx/gpu_polygon.cpp b/psx/octoshock/psx/gpu_polygon.cpp index f1504a5929..a02f1095a2 100644 --- a/psx/octoshock/psx/gpu_polygon.cpp +++ b/psx/octoshock/psx/gpu_polygon.cpp @@ -19,6 +19,7 @@ #include "psx.h" #include "gpu.h" +#include "math_ops.h" namespace MDFN_IEN_PSX { @@ -406,7 +407,7 @@ INLINE void PS_GPU::DrawTriangle(tri_vertex *vertices) tp->x_step[right_facing] = bound_coord_us; tp->x_coord[!right_facing] = base_coord + ((vertices[vo].y - vertices[0].y) * base_step); tp->x_step[!right_facing] = base_step; - tp->dec_mode = !!vo; + tp->dec_mode = vo; } { @@ -418,7 +419,7 @@ INLINE void PS_GPU::DrawTriangle(tri_vertex *vertices) tp->x_step[right_facing] = bound_coord_ls; tp->x_coord[!right_facing] = base_coord + ((vertices[1 ^ vp].y - vertices[0].y) * base_step); //base_coord + ((vertices[1].y - vertices[0].y) * base_step); tp->x_step[!right_facing] = base_step; - tp->dec_mode = !!vp; + tp->dec_mode = vp; } for(unsigned i = 0; i < 2; i++) //2; i++) diff --git a/psx/octoshock/psx/gpu_sprite.cpp b/psx/octoshock/psx/gpu_sprite.cpp index 5bb5343815..47f82396b3 100644 --- a/psx/octoshock/psx/gpu_sprite.cpp +++ b/psx/octoshock/psx/gpu_sprite.cpp @@ -17,6 +17,7 @@ #include "psx.h" #include "gpu.h" +#include "math_ops.h" namespace MDFN_IEN_PSX { diff --git a/psx/octoshock/psx/input/memcard.cpp b/psx/octoshock/psx/input/memcard.cpp index 792ddc7332..10df3df475 100644 --- a/psx/octoshock/psx/input/memcard.cpp +++ b/psx/octoshock/psx/input/memcard.cpp @@ -17,6 +17,7 @@ // I could find no other commands than 'R', 'W', and 'S' (not sure what 'S' is for, however) +#include #include "../psx.h" #include "../frontio.h" #include "memcard.h" @@ -309,220 +310,215 @@ bool InputDevice_Memcard::Clock(bool TxD, int32 &dsr_pulse_delay) } - switch(command_phase) - { - case 0: - if(receive_buffer != 0x81) - command_phase = -1; - else - { - //printf("[MCR] Device selected\n"); - transmit_buffer = presence_new ? 0x08 : 0x00; - transmit_count = 1; - command_phase++; - } - break; - - case 1: - command = receive_buffer; - //printf("[MCR] Command received: %c\n", command); - if(command == 'R' || command == 'W') - { - command_phase++; - transmit_buffer = 0x5A; - transmit_count = 1; - } - else - { - if(command == 'S') - { - PSX_WARNING("[MCR] Memcard S command unsupported."); - } - - command_phase = -1; - transmit_buffer = 0; - transmit_count = 0; - } - break; - - case 2: - transmit_buffer = 0x5D; - transmit_count = 1; - command_phase++; - break; - - case 3: - transmit_buffer = 0x00; - transmit_count = 1; - if(command == 'R') - command_phase = 1000; - else if(command == 'W') - command_phase = 2000; - break; - - // - // Read - // - case 1000: - addr = receive_buffer << 8; - transmit_buffer = receive_buffer; - transmit_count = 1; - command_phase++; - break; - - case 1001: - addr |= receive_buffer & 0xFF; - transmit_buffer = '\\'; - transmit_count = 1; - command_phase++; - break; - - case 1002: - //printf("[MCR] READ ADDR=0x%04x\n", addr); - if(addr >= (sizeof(card_data) >> 7)) - addr = 0xFFFF; - - calced_xor = 0; - transmit_buffer = ']'; - transmit_count = 1; - command_phase++; - - // TODO: enable this code(or something like it) when CPU instruction timing is a bit better. - // - //dsr_pulse_delay = 32000; - //goto SkipDPD; - // - - break; - - case 1003: - transmit_buffer = addr >> 8; - calced_xor ^= transmit_buffer; - transmit_count = 1; - command_phase++; - break; - - case 1004: - transmit_buffer = addr & 0xFF; - calced_xor ^= transmit_buffer; - - if(addr == 0xFFFF) - { - transmit_count = 1; - command_phase = -1; - } - else - { - transmit_count = 1; - command_phase = 1024; - } - break; - - // Transmit actual 128 bytes data - //case (1024 + 0) ... (1024 + 128 - 1): - BIGCASE128(1024) - transmit_buffer = card_data[(addr << 7) + (command_phase - 1024)]; - calced_xor ^= transmit_buffer; - transmit_count = 1; - command_phase++; - break; - - // XOR - case (1024 + 128): - transmit_buffer = calced_xor; - transmit_count = 1; - command_phase++; - break; - - // End flag - case (1024 + 129): - transmit_buffer = 'G'; - transmit_count = 1; - command_phase = -1; - break; - - // - // Write - // - case 2000: - calced_xor = receive_buffer; - addr = receive_buffer << 8; - transmit_buffer = receive_buffer; - transmit_count = 1; - command_phase++; - break; - - case 2001: - calced_xor ^= receive_buffer; - addr |= receive_buffer & 0xFF; - //printf("[MCR] WRITE ADDR=0x%04x\n", addr); - transmit_buffer = receive_buffer; - transmit_count = 1; - command_phase = 2048; - break; - - //case (2048 + 0) ... (2048 + 128 - 1): - BIGCASE128(2048) - calced_xor ^= receive_buffer; - rw_buffer[command_phase - 2048] = receive_buffer; - - transmit_buffer = receive_buffer; - transmit_count = 1; - command_phase++; - break; - - case (2048 + 128): // XOR - write_xor = receive_buffer; - transmit_buffer = '\\'; - transmit_count = 1; - command_phase++; - break; - - case (2048 + 129): - transmit_buffer = ']'; - transmit_count = 1; - command_phase++; - break; - - case (2048 + 130): // End flag - //MDFN_DispMessage("%02x %02x", calced_xor, write_xor); - //printf("[MCR] Write End. Actual_XOR=0x%02x, CW_XOR=0x%02x\n", calced_xor, write_xor); - - if(calced_xor != write_xor) - transmit_buffer = 'N'; - else if(addr >= (sizeof(card_data) >> 7)) - transmit_buffer = 0xFF; - else - { - transmit_buffer = 'G'; - presence_new = false; - - // If the current data is different from the data to be written, increment the dirty count. - // memcpy()'ing over to card_data is also conditionalized here for a slight optimization. - if(memcmp(&card_data[addr << 7], rw_buffer, 128)) - { - memcpy(&card_data[addr << 7], rw_buffer, 128); - dirty_count++; - data_used = true; - } - } - - transmit_count = 1; - command_phase = -1; - break; - - } - - //if(command_phase != -1 || transmit_count) - // printf("[MCR] Receive: 0x%02x, Send: 0x%02x -- %d\n", receive_buffer, transmit_buffer, command_phase); - } - - if(!bitpos && transmit_count) - dsr_pulse_delay = 0x100; - - //SkipDPD: ; - - return(ret); + + if(command_phase == 0) + { + if(receive_buffer != 0x81) + command_phase = -1; + else + { + //printf("[MCR] Device selected\n"); + transmit_buffer = presence_new ? 0x08 : 0x00; + transmit_count = 1; + command_phase++; + } + } + else if(command_phase == 1) + { + command = receive_buffer; + //printf("[MCR] Command received: %c\n", command); + if(command == 'R' || command == 'W') + { + command_phase++; + transmit_buffer = 0x5A; + transmit_count = 1; + } + else + { + if(command == 'S') + { + PSX_WARNING("[MCR] Memcard S command unsupported."); + } + + command_phase = -1; + transmit_buffer = 0; + transmit_count = 0; + } + } + else if(command_phase == 2) + { + transmit_buffer = 0x5D; + transmit_count = 1; + command_phase++; + } + else if(command_phase == 3) + { + transmit_buffer = 0x00; + transmit_count = 1; + if(command == 'R') + command_phase = 1000; + else if(command == 'W') + command_phase = 2000; + } + // + // Read + // + else if(command_phase == 1000) + { + addr = receive_buffer << 8; + transmit_buffer = receive_buffer; + transmit_count = 1; + command_phase++; + } + else if(command_phase == 1001) + { + addr |= receive_buffer & 0xFF; + transmit_buffer = '\\'; + transmit_count = 1; + command_phase++; + } + else if(command_phase == 1002) + { + //printf("[MCR] READ ADDR=0x%04x\n", addr); + if(addr >= (sizeof(card_data) >> 7)) + addr = 0xFFFF; + + calced_xor = 0; + transmit_buffer = ']'; + transmit_count = 1; + command_phase++; + + // TODO: enable this code(or something like it) when CPU instruction timing is a bit better. + // + //dsr_pulse_delay = 32000; + //goto SkipDPD; + // + } + else if(command_phase == 1003) + { + transmit_buffer = addr >> 8; + calced_xor ^= transmit_buffer; + transmit_count = 1; + command_phase++; + } + else if(command_phase == 1004) + { + transmit_buffer = addr & 0xFF; + calced_xor ^= transmit_buffer; + + if(addr == 0xFFFF) + { + transmit_count = 1; + command_phase = -1; + } + else + { + transmit_count = 1; + command_phase = 1024; + } + } + // Transmit actual 128 bytes data + else if(command_phase >= (1024 + 0) && command_phase <= (1024 + 128 - 1)) + { + transmit_buffer = card_data[(addr << 7) + (command_phase - 1024)]; + calced_xor ^= transmit_buffer; + transmit_count = 1; + command_phase++; + } + // XOR + else if(command_phase == (1024 + 128)) + { + transmit_buffer = calced_xor; + transmit_count = 1; + command_phase++; + } + // End flag + else if(command_phase == (1024 + 129)) + { + transmit_buffer = 'G'; + transmit_count = 1; + command_phase = -1; + } + // + // Write + // + else if(command_phase == 2000) + { + calced_xor = receive_buffer; + addr = receive_buffer << 8; + transmit_buffer = receive_buffer; + transmit_count = 1; + command_phase++; + } + else if(command_phase == 2001) + { + calced_xor ^= receive_buffer; + addr |= receive_buffer & 0xFF; + //printf("[MCR] WRITE ADDR=0x%04x\n", addr); + transmit_buffer = receive_buffer; + transmit_count = 1; + command_phase = 2048; + } + else if(command_phase >= (2048 + 0) && command_phase <= (2048 + 128 - 1)) + { + calced_xor ^= receive_buffer; + rw_buffer[command_phase - 2048] = receive_buffer; + + transmit_buffer = receive_buffer; + transmit_count = 1; + command_phase++; + } + else if(command_phase == (2048 + 128)) // XOR + { + write_xor = receive_buffer; + transmit_buffer = '\\'; + transmit_count = 1; + command_phase++; + } + else if(command_phase == (2048 + 129)) + { + transmit_buffer = ']'; + transmit_count = 1; + command_phase++; + } + else if(command_phase == (2048 + 130)) // End flag + { + //MDFN_DispMessage("%02x %02x", calced_xor, write_xor); + //printf("[MCR] Write End. Actual_XOR=0x%02x, CW_XOR=0x%02x\n", calced_xor, write_xor); + + if(calced_xor != write_xor) + transmit_buffer = 'N'; + else if(addr >= (sizeof(card_data) >> 7)) + transmit_buffer = 0xFF; + else + { + transmit_buffer = 'G'; + presence_new = false; + + // If the current data is different from the data to be written, increment the dirty count. + // memcpy()'ing over to card_data is also conditionalized here for a slight optimization. + if(memcmp(&card_data[addr << 7], rw_buffer, 128)) + { + memcpy(&card_data[addr << 7], rw_buffer, 128); + dirty_count++; + data_used = true; + } + } + + transmit_count = 1; + command_phase = -1; + } + + //if(command_phase != -1 || transmit_count) + // printf("[MCR] Receive: 0x%02x, Send: 0x%02x -- %d\n", receive_buffer, transmit_buffer, command_phase); + } + + if(!bitpos && transmit_count) + dsr_pulse_delay = 0x100; + + //SkipDPD: ; + + return(ret); } uint32 InputDevice_Memcard::GetNVSize(void) diff --git a/psx/octoshock/psx/input/multitap.cpp b/psx/octoshock/psx/input/multitap.cpp index 4bc97c1f7f..bc11ccb569 100644 --- a/psx/octoshock/psx/input/multitap.cpp +++ b/psx/octoshock/psx/input/multitap.cpp @@ -15,6 +15,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "../psx.h" #include "../frontio.h" #include "multitap.h" @@ -128,11 +129,12 @@ void InputDevice_Multitap::Power(void) full_mode = false; full_mode_setting = false; + prev_fm_success = false; + memset(sb, 0, sizeof(sb)); + fm_dp = 0; memset(fm_buffer, 0, sizeof(fm_buffer)); fm_deferred_error_temp = false; - fm_deferred_error = false; - fm_command_error = false; for(int i = 0; i < 4; i++) { @@ -241,40 +243,50 @@ pscpu_timestamp_t InputDevice_Multitap::GPULineHook(const pscpu_timestamp_t line return(ret); } -void InputDevice_Multitap::SetDTR(bool new_dtr) -{ - bool old_dtr = dtr; - dtr = new_dtr; - - if(!dtr) - { - if(old_dtr) - { - //printf("Multitap stop.\n"); - } - - bit_counter = 0; - receive_buffer = 0; - selected_device = -1; - mc_mode = false; - full_mode = false; - } - - if(!old_dtr && dtr) - { - full_mode = full_mode_setting; - - byte_counter = 0; - - //if(full_mode) - // printf("Multitap start: %d\n", full_mode); - } - - for(int i = 0; i < 4; i++) - { - pad_devices[i]->SetDTR(dtr); - mc_devices[i]->SetDTR(dtr); - } + +void InputDevice_Multitap::SetDTR(bool new_dtr) +{ + bool old_dtr = dtr; + dtr = new_dtr; + + if(!dtr) + { + if(old_dtr) + { + //printf("Multitap stop.\n"); + } + + bit_counter = 0; + receive_buffer = 0; + selected_device = -1; + mc_mode = false; + full_mode = false; + } + + if(!old_dtr && dtr) + { + full_mode = full_mode_setting; + + if(!prev_fm_success) + { + memset(sb, 0, sizeof(sb)); + for(unsigned i = 0; i < 4; i++) + sb[i][0] = 0x42; + } + + prev_fm_success = false; + + byte_counter = 0; + + //if(full_mode) + // printf("Multitap start: %d\n", full_mode); + } + + for(int i = 0; i < 4; i++) + { + pad_devices[i]->SetDTR(dtr); + mc_devices[i]->SetDTR(dtr); + } } bool InputDevice_Multitap::GetDSR(void) @@ -282,213 +294,209 @@ bool InputDevice_Multitap::GetDSR(void) return(0); } -bool InputDevice_Multitap::Clock(bool TxD, int32 &dsr_pulse_delay) -{ - if(!dtr) - return(1); - - bool ret = 1; - int32 tmp_pulse_delay[2][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; - - //printf("Receive bit: %d\n", TxD); - //printf("TxD %d\n", TxD); - - receive_buffer &= ~ (1 << bit_counter); - receive_buffer |= TxD << bit_counter; - - if(1) - { - if(byte_counter == 0) - { - bool mangled_txd = TxD; - - if(bit_counter < 4) - mangled_txd = (0x01 >> bit_counter) & 1; - - for(unsigned i = 0; i < 4; i++) - { - pad_devices[i]->Clock(mangled_txd, tmp_pulse_delay[0][i]); - mc_devices[i]->Clock(mangled_txd, tmp_pulse_delay[1][i]); - } - } - else - { - if(full_mode) - { - if(byte_counter == 1) - { - ret = (0x80 >> bit_counter) & 1; - - for(unsigned i = 0; i < 4; i++) - { - fm_buffer[i][0] &= (pad_devices[i]->Clock(TxD, tmp_pulse_delay[0][i]) << bit_counter) | (~(1U << bit_counter)); - } - } - else if(byte_counter == 2) - { - ret = (0x5A >> bit_counter) & 1; - } - // || byte_counter == (0x03 + 0x08 * 1) || byte_counter == (0x03 + 0x08 * 2) || byte_counter == (0x03 + 0x08 * 3)) - else if(byte_counter >= 0x03 && byte_counter < 0x03 + 0x08 * 4) - { - if(!fm_command_error && byte_counter >= (0x03 + 1) && byte_counter < (0x03 + 0x08)) - { - for(unsigned i = 0; i < 4; i++) - { - fm_buffer[i][byte_counter - 0x03] &= (pad_devices[i]->Clock(0, tmp_pulse_delay[0][i]) << bit_counter) | (~(1U << bit_counter)); - } - } - ret &= ((&fm_buffer[0][0])[byte_counter - 0x03] >> bit_counter) & 1; - } - } - else // to if(full_mode) - { - if((unsigned)selected_device < 4) - { - ret &= pad_devices[selected_device]->Clock(TxD, tmp_pulse_delay[0][selected_device]); - ret &= mc_devices[selected_device]->Clock(TxD, tmp_pulse_delay[1][selected_device]); - } - } - } // end else to if(byte_counter == 0) - } - - // - // - // - - bit_counter = (bit_counter + 1) & 0x7; - if(bit_counter == 0) - { - //printf("Receive: 0x%02x\n", receive_buffer); - if(byte_counter == 0) - { - mc_mode = (bool)(receive_buffer & 0xF0); - if(mc_mode) - full_mode = false; - - //printf("Zoomba: 0x%02x\n", receive_buffer); - //printf("Full mode: %d %d %d\n", full_mode, bit_counter, byte_counter); - - if(full_mode) - { - memset(fm_buffer, 0xFF, sizeof(fm_buffer)); - selected_device = 0; - } - else - { - //printf("Device select: %02x\n", receive_buffer); - fm_deferred_error = false; - selected_device = ((receive_buffer & 0xF) - 1) & 0xFF; - } - } - - if(byte_counter == 1) - { - command = receive_buffer; - - //printf("Multitap sub-command: %02x\n", command); - - if(full_mode) - { - if(command != 0x42) - fm_command_error = true; - else - fm_command_error = fm_deferred_error; - } - else - { - fm_command_error = false; - } - fm_deferred_error = false; - } - - if((!mc_mode || full_mode) && byte_counter == 2) - { - //printf("Full mode setting: %02x\n", receive_buffer); - full_mode_setting = receive_buffer & 0x01; - } - - if(full_mode) - { - if(byte_counter == (3 + 8 * 0) || byte_counter == (3 + 8 * 1) || byte_counter == (3 + 8 * 2) || byte_counter == (3 + 8 * 3)) - { - unsigned index = (byte_counter - 3) >> 3; - assert(index < 4); - - if(index == 0) - fm_deferred_error_temp = false; - - if((fm_dp & (1U << index)) && receive_buffer != 0x42) - { - //printf("Multitap command check failed: %u, 0x%02x\n", byte_counter, receive_buffer); - fm_deferred_error_temp = true; - } - } - - if(byte_counter == 33) - fm_deferred_error = fm_deferred_error_temp; - } - - // Handle DSR stuff - if(full_mode) - { - if(byte_counter == 0) // Next byte: 0x80 - { - dsr_pulse_delay = 1000; - - fm_dp = 0; - for(unsigned i = 0; i < 4; i++) - fm_dp |= (((bool)(tmp_pulse_delay[0][i])) << i); - } - else if(byte_counter == 1) // Next byte: 0x5A - dsr_pulse_delay = 0x40; - else if(byte_counter == 2) // Next byte(typically, controller-dependent): 0x41 - { - if(fm_dp) - dsr_pulse_delay = 0x40; - else - dsr_pulse_delay = 0; - } - else if(byte_counter >= 3 && byte_counter < 34) // Next byte when byte_counter==3 (typically, controller-dependent): 0x5A - { - if(byte_counter < 10) - { - int d = 0x40; - - for(unsigned i = 0; i < 4; i++) - if(tmp_pulse_delay[0][i] > d) - d = tmp_pulse_delay[0][i]; - - dsr_pulse_delay = d; - } - else - dsr_pulse_delay = 0x20; - - if(byte_counter == 3 && fm_command_error) - dsr_pulse_delay = 0; - } - } // end if(full_mode) - else - { - if((unsigned)selected_device < 4) - { - dsr_pulse_delay = std::max(tmp_pulse_delay[0][selected_device], tmp_pulse_delay[1][selected_device]); - } - } - - - // - // - // - - //printf("Byte Counter Increment\n"); - if(byte_counter < 255) - byte_counter++; - } - - - - return(ret); -} - -} +bool InputDevice_Multitap::Clock(bool TxD, int32 &dsr_pulse_delay) +{ + if(!dtr) + return(1); + + bool ret = 1; + int32 tmp_pulse_delay[2][4] = { { 0, 0, 0, 0 }, { 0, 0, 0, 0 } }; + + //printf("Receive bit: %d\n", TxD); + //printf("TxD %d\n", TxD); + + receive_buffer &= ~ (1 << bit_counter); + receive_buffer |= TxD << bit_counter; + + if(1) + { + if(byte_counter == 0) + { + bool mangled_txd = TxD; + + if(bit_counter < 4) + mangled_txd = (0x01 >> bit_counter) & 1; + + for(unsigned i = 0; i < 4; i++) + { + pad_devices[i]->Clock(mangled_txd, tmp_pulse_delay[0][i]); + mc_devices[i]->Clock(mangled_txd, tmp_pulse_delay[1][i]); + } + } + else + { + if(full_mode) + { + if(byte_counter == 1) + ret = (0x80 >> bit_counter) & 1; + else if(byte_counter == 2) + ret = (0x5A >> bit_counter) & 1; + else if(byte_counter >= 0x03 && byte_counter < 0x03 + 0x08 * 4) + { + if(!fm_command_error && byte_counter < (0x03 + 0x08)) + { + for(unsigned i = 0; i < 4; i++) + { + fm_buffer[i][byte_counter - 0x03] &= (pad_devices[i]->Clock((sb[i][byte_counter - 0x03] >> bit_counter) & 1, tmp_pulse_delay[0][i]) << bit_counter) | (~(1U << bit_counter)); + } + } + ret &= ((&fm_buffer[0][0])[byte_counter - 0x03] >> bit_counter) & 1; + } + } + else // to if(full_mode) + { + if((unsigned)selected_device < 4) + { + ret &= pad_devices[selected_device]->Clock(TxD, tmp_pulse_delay[0][selected_device]); + ret &= mc_devices[selected_device]->Clock(TxD, tmp_pulse_delay[1][selected_device]); + } + } + } // end else to if(byte_counter == 0) + } + + // + // + // + + bit_counter = (bit_counter + 1) & 0x7; + if(bit_counter == 0) + { + //printf("MT Receive: 0x%02x\n", receive_buffer); + if(byte_counter == 0) + { + mc_mode = (bool)(receive_buffer & 0xF0); + if(mc_mode) + full_mode = false; + + //printf("Zoomba: 0x%02x\n", receive_buffer); + //printf("Full mode: %d %d %d\n", full_mode, bit_counter, byte_counter); + + if(full_mode) + { + memset(fm_buffer, 0xFF, sizeof(fm_buffer)); + selected_device = 0; + } + else + { + //printf("Device select: %02x\n", receive_buffer); + selected_device = ((receive_buffer & 0xF) - 1) & 0xFF; + } + } + + if(byte_counter == 1) + { + command = receive_buffer; + + //printf("Multitap sub-command: %02x\n", command); + + if(full_mode) + { + if(command != 0x42) + fm_command_error = true; + else + fm_command_error = false; + } + else + { + fm_command_error = false; + } + } + + if((!mc_mode || full_mode) && byte_counter == 2) + { + //printf("Full mode setting: %02x\n", receive_buffer); + full_mode_setting = receive_buffer & 0x01; + } + + if(full_mode) + { + if(byte_counter >= 3 + 8 * 0 && byte_counter < (3 + 8 * 4)) + { + const unsigned adjbi = byte_counter - 3; + + sb[adjbi >> 3][adjbi & 0x7] = receive_buffer; + } + + if(byte_counter == 33) + prev_fm_success = true; + } + + // Handle DSR stuff + if(full_mode) + { + if(byte_counter == 0) // Next byte: 0x80 + { + dsr_pulse_delay = 1000; + + fm_dp = 0; + for(unsigned i = 0; i < 4; i++) + fm_dp |= (((bool)(tmp_pulse_delay[0][i])) << i); + } + else if(byte_counter == 1) // Next byte: 0x5A + dsr_pulse_delay = 0x40; + else if(byte_counter == 2) // Next byte(typically, controller-dependent): 0x41 + { + if(fm_dp) + dsr_pulse_delay = 0x40; + else + { + byte_counter = 255; + dsr_pulse_delay = 0; + } + } + else if(byte_counter >= 3 && byte_counter < 34) // Next byte when byte_counter==3 (typically, controller-dependent): 0x5A + { + if(byte_counter < 10) + { + int d = 0x40; + + for(unsigned i = 0; i < 4; i++) + { + int32 tpd = tmp_pulse_delay[0][i]; + + if(byte_counter == 3 && (fm_dp & (1U << i)) && tpd == 0) + { + //printf("SNORG: %u %02x\n", i, sb[i][0]); + fm_command_error = true; + } + + if(tpd > d) + d = tpd; + } + + dsr_pulse_delay = d; + } + else + dsr_pulse_delay = 0x20; + + if(byte_counter == 3 && fm_command_error) + { + byte_counter = 255; + dsr_pulse_delay = 0; + } + } + } // end if(full_mode) + else + { + if((unsigned)selected_device < 4) + { + dsr_pulse_delay = std::max(tmp_pulse_delay[0][selected_device], tmp_pulse_delay[1][selected_device]); + } + } + + + // + // + // + + //printf("Byte Counter Increment\n"); + if(byte_counter < 255) + byte_counter++; + } + + + + return(ret); +} + +} diff --git a/psx/octoshock/psx/input/multitap.h b/psx/octoshock/psx/input/multitap.h index b4a3699004..a17c2cc80c 100644 --- a/psx/octoshock/psx/input/multitap.h +++ b/psx/octoshock/psx/input/multitap.h @@ -39,10 +39,13 @@ class InputDevice_Multitap final : public InputDevice bool full_mode; bool mc_mode; + bool prev_fm_success; uint8 fm_dp; // Device-present. uint8 fm_buffer[4][8]; + uint8 sb[4][8]; + bool fm_deferred_error_temp; bool fm_deferred_error; bool fm_command_error; diff --git a/psx/octoshock/psx/mdec.cpp b/psx/octoshock/psx/mdec.cpp index a6c1a9d0df..ccecc5c140 100644 --- a/psx/octoshock/psx/mdec.cpp +++ b/psx/octoshock/psx/mdec.cpp @@ -26,7 +26,7 @@ if(InFIFO.CanWrite()) { - InFIFO.WriteUnit(V); + InFIFO.Write(V); if(InCommand) { @@ -57,9 +57,10 @@ #include "psx.h" #include "mdec.h" +#include "FastFIFO.h" +#include "math_ops.h" #include "masmem.h" -#include "cdrom/SimpleFIFO.h" #if defined(__SSE2__) #include @@ -79,8 +80,8 @@ namespace MDFN_IEN_PSX static int32 ClockCounter; static unsigned MDRPhase; -static SimpleFIFO InFIFO(0x20); -static SimpleFIFO OutFIFO(0x20); +static FastFIFO InFIFO; +static FastFIFO OutFIFO; static int8 block_y[8][8]; static int8 block_cb[8][8]; // [y >> 1][x >> 1] @@ -443,10 +444,10 @@ static INLINE void WriteImageData(uint16 V, int32* eat_cycles) int ci = sign_10_to_s16(V & 0x3FF); int tmp; - if(q != 0) - tmp = ((ci * q) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); - else - tmp = (ci * 2) << 4; + if(q != 0) + tmp = (int32)((uint32)(ci * q) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); + else + tmp = (uint32)(ci * 2) << 4; // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8? Coeff[ZigZag[0]] = std::min(0x3FFF, std::max(-0x4000, tmp)); @@ -476,10 +477,10 @@ static INLINE void WriteImageData(uint16 V, int32* eat_cycles) int ci = sign_10_to_s16(V & 0x3FF); int tmp; - if(q != 0) - tmp = (((ci * q) >> 3) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); - else - tmp = (ci * 2) << 4; + if(q != 0) + tmp = (int32)((uint32)((ci * q) >> 3) << 4) + (ci ? ((ci < 0) ? 8 : -8) : 0); + else + tmp = (uint32)(ci * 2) << 4; // Not sure if it should be 0x3FFF or 0x3FF0 or maybe 0x3FF8? Coeff[ZigZag[CoeffIndex]] = std::min(0x3FFF, std::max(-0x4000, tmp)); @@ -504,10 +505,12 @@ static INLINE void WriteImageData(uint16 V, int32* eat_cycles) case 5: IDCT(Coeff, &block_y[0][0]); break; } - // - // Approximate, actual timing seems to be a bit complex. - // - *eat_cycles += 341; + // + // Timing in the actual PS1 MDEC is complex due to (apparent) pipelining, but the average when decoding a large number of blocks is + // about 512. We'll go with a lower value here to be conservative due to timing granularity and other timing deficiencies in Mednafen. BUT, don't + // go lower than 460, or Parasite Eve 2's 3D models will stutter like crazy during FMV-background sequences. + // + *eat_cycles += 474; if(DecodeWB >= 2) { @@ -529,8 +532,8 @@ static INLINE void WriteImageData(uint16 V, int32* eat_cycles) // #define MDEC_WAIT_COND(n) { case __COUNTER__: if(!(n)) { MDRPhase = __COUNTER__ - MDRPhaseBias - 1; return; } } -#define MDEC_WRITE_FIFO(n) { MDEC_WAIT_COND(OutFIFO.CanWrite()); OutFIFO.WriteUnit(n); } -#define MDEC_READ_FIFO(n) { MDEC_WAIT_COND(InFIFO.CanRead()); n = InFIFO.ReadUnit(); } +#define MDEC_WRITE_FIFO(n) { MDEC_WAIT_COND(OutFIFO.CanWrite()); OutFIFO.Write(n); } +#define MDEC_READ_FIFO(n) { MDEC_WAIT_COND(InFIFO.CanRead()); n = InFIFO.Read(); } #define MDEC_EAT_CLOCKS(n) { ClockCounter -= (n); MDEC_WAIT_COND(ClockCounter > 0); } void MDEC_Run(int32 clocks) @@ -671,50 +674,52 @@ void MDEC_Run(int32 clocks) } #endif -void MDEC_DMAWrite(uint32 V) -{ - if(InFIFO.CanWrite()) - { - InFIFO.WriteUnit(V); - MDEC_Run(0); - } - else - { - PSX_DBG(PSX_DBG_WARNING, "Input FIFO DMA write overflow?!\n"); - } -} - -uint32 MDEC_DMARead(int32* offs) -{ - uint32 V = 0; - - *offs = 0; - - if(MDFN_LIKELY(OutFIFO.CanRead())) - { - V = OutFIFO.ReadUnit(); - - *offs = (RAMOffsetY & 0x7) * RAMOffsetWWS; - - if(RAMOffsetY & 0x08) - { - *offs = (*offs - RAMOffsetWWS*7); - } - - RAMOffsetCounter--; - if(!RAMOffsetCounter) - { - RAMOffsetCounter = RAMOffsetWWS; - RAMOffsetY++; - } - } - else - { - PSX_DBG(PSX_DBG_WARNING, "[MDEC] BONUS GNOMES\n"); - V = rand(); - } - - return(V); + +void MDEC_DMAWrite(uint32 V) +{ + if(InFIFO.CanWrite()) + { + InFIFO.Write(V); + MDEC_Run(0); + } + else + { + PSX_DBG(PSX_DBG_WARNING, "[MDEC] DMA write when input FIFO is full!!\n"); + } +} + +uint32 MDEC_DMARead(uint32* offs) +{ + uint32 V = 0; + + *offs = 0; + + if(MDFN_LIKELY(OutFIFO.CanRead())) + { + V = OutFIFO.Read(); + + *offs = (RAMOffsetY & 0x7) * RAMOffsetWWS; + + if(RAMOffsetY & 0x08) + { + *offs = (*offs - RAMOffsetWWS*7); + } + + RAMOffsetCounter--; + if(!RAMOffsetCounter) + { + RAMOffsetCounter = RAMOffsetWWS; + RAMOffsetY++; + } + + MDEC_Run(0); + } + else + { + PSX_DBG(PSX_DBG_WARNING, "[MDEC] DMA read when output FIFO is empty!\n"); + } + + return(V); } bool MDEC_DMACanWrite(void) @@ -759,7 +764,7 @@ void MDEC_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V) { if(InFIFO.CanWrite()) { - InFIFO.WriteUnit(V); + InFIFO.Write(V); if(!InCommand) { @@ -799,7 +804,7 @@ uint32 MDEC_Read(const pscpu_timestamp_t timestamp, uint32 A) else { if(OutFIFO.CanRead()) - ret = OutFIFO.ReadUnit(); + ret = OutFIFO.Read(); } //PSX_WARNING("[MDEC] Read: 0x%08x 0x%08x -- %d %d", A, ret, InputBuffer.CanRead(), InCounter); diff --git a/psx/octoshock/psx/mdec.h b/psx/octoshock/psx/mdec.h index 0f7de66576..91a5a40974 100644 --- a/psx/octoshock/psx/mdec.h +++ b/psx/octoshock/psx/mdec.h @@ -6,7 +6,7 @@ namespace MDFN_IEN_PSX void MDEC_DMAWrite(uint32 V); -uint32 MDEC_DMARead(int32* offs); +uint32 MDEC_DMARead(uint32* offs); void MDEC_Write(const pscpu_timestamp_t timestamp, uint32 A, uint32 V); uint32 MDEC_Read(const pscpu_timestamp_t timestamp, uint32 A); diff --git a/psx/octoshock/psx/psx.cpp b/psx/octoshock/psx/psx.cpp index 6cbc2036e7..116af6e77f 100644 --- a/psx/octoshock/psx/psx.cpp +++ b/psx/octoshock/psx/psx.cpp @@ -47,8 +47,8 @@ namespace MDFN_IEN_PSX { -static unsigned psx_dbg_level = 0; #if PSX_DBGPRINT_ENABLE +static unsigned psx_dbg_level = 0; void PSX_DBG_BIOS_PUTC(uint8 c) noexcept { @@ -81,9 +81,10 @@ void PSX_DBG(unsigned level, const char *format, ...) noexcept va_end(ap); } } +#else +static unsigned const psx_dbg_level = 0; #endif - struct MDFN_PseudoRNG // Based off(but not the same as) public-domain "JKISS" PRNG. { MDFN_PseudoRNG() @@ -212,7 +213,16 @@ static struct }; } SysControl; - +static unsigned DMACycleSteal = 0; // Doesn't need to be saved in save states, since it's recalculated in the ForceEventUpdates() call chain. + +void PSX_SetDMACycleSteal(unsigned stealage) +{ + if(stealage > 200) // Due to 8-bit limitations in the CPU core. + stealage = 200; + + DMACycleSteal = stealage; +} + // // Event stuff // @@ -337,43 +347,12 @@ void ForceEventUpdates(const pscpu_timestamp_t timestamp) bool PSX_EventHandler(const pscpu_timestamp_t timestamp) { event_list_entry *e = events[PSX_EVENT__SYNFIRST].next; -#if PSX_EVENT_SYSTEM_CHECKS - pscpu_timestamp_t prev_event_time = 0; -#endif -#if 0 - { - printf("EventHandler - timestamp=%8d\n", timestamp); - event_list_entry *moo = &events[PSX_EVENT__SYNFIRST]; - while(moo) - { - printf("%u: %8d\n", moo->which, moo->event_time); - moo = moo->next; - } - } -#endif - -#if PSX_EVENT_SYSTEM_CHECKS - assert(Running == 0 || timestamp >= e->event_time); // If Running == 0, our EventHandler -#endif while(timestamp >= e->event_time) // If Running = 0, PSX_EventHandler() may be called even if there isn't an event per-se, so while() instead of do { ... } while { event_list_entry *prev = e->prev; pscpu_timestamp_t nt; -#if PSX_EVENT_SYSTEM_CHECKS - // Sanity test to make sure events are being evaluated in temporal order. - if(e->event_time < prev_event_time) - abort(); - prev_event_time = e->event_time; -#endif - - //printf("Event: %u %8d\n", e->which, e->event_time); -#if PSX_EVENT_SYSTEM_CHECKS - if((timestamp - e->event_time) > 50) - printf("Late: %u %d --- %8d\n", e->which, timestamp - e->event_time, timestamp); -#endif - switch(e->which) { default: abort(); @@ -408,26 +387,6 @@ bool PSX_EventHandler(const pscpu_timestamp_t timestamp) e = prev->next; } -#if PSX_EVENT_SYSTEM_CHECKS - for(int i = PSX_EVENT__SYNFIRST + 1; i < PSX_EVENT__SYNLAST; i++) - { - if(timestamp >= events[i].event_time) - { - printf("BUG: %u\n", i); - - event_list_entry *moo = &events[PSX_EVENT__SYNFIRST]; - - while(moo) - { - printf("%u: %8d\n", moo->which, moo->event_time); - moo = moo->next; - } - - abort(); - } - } -#endif - return(Running); } @@ -443,14 +402,6 @@ void PSX_RequestMLExit(void) // End event stuff // -void DMA_CheckReadDebug(uint32 A); - -static unsigned sucksuck = 0; -void PSX_SetDMASuckSuck(unsigned suckage) -{ - sucksuck = suckage; -} - // Remember to update MemPeek<>() when we change address decoding in MemRW() template static INLINE void MemRW(pscpu_timestamp_t ×tamp, uint32 A, uint32 &V) @@ -462,14 +413,10 @@ template static INLINE void MemRW(pscpu printf("Read%d: %08x(orig=%08x)\n", (int)(sizeof(T) * 8), A & mask[A >> 29], A); #endif - if(!IsWrite) - timestamp += sucksuck; - - //if(A == 0xa0 && IsWrite) - // DBG_Break(); + if(!IsWrite) + timestamp += DMACycleSteal; if(A < 0x00800000) - //if(A <= 0x1FFFFF) { if(IsWrite) { @@ -480,8 +427,6 @@ template static INLINE void MemRW(pscpu timestamp += 3; } - //DMA_CheckReadDebug(A); - //assert(A <= 0x1FFFFF); if(Access24) { if(IsWrite) @@ -797,7 +742,6 @@ void PSX_MemWrite16(pscpu_timestamp_t timestamp, uint32 A, uint32 V) void PSX_MemWrite24(pscpu_timestamp_t timestamp, uint32 A, uint32 V) { - //assert(0); MemRW(timestamp, A, V); } @@ -2357,6 +2301,18 @@ Breakout: return SHOCK_OK; } +bool ShockDiscRef::ReadLBA_PW(uint8* pwbuf96, int32 lba, bool hint_fullread) +{ + //TODO - whats that hint mean + //TODO - should return false if out of range totally + //reference: static const int32 LBA_Read_Minimum = -150; + //reference: static const int32 LBA_Read_Maximum = 449849; // 100 * 75 * 60 - 150 - 1 + u8 tmp[2448]; + ReadLBA2448(lba,tmp); + memcpy(pwbuf96,tmp+2352,96); + return true; +} + s32 ShockDiscRef::ReadLBA2448(s32 lba, void* dst2448) { return InternalReadLBA2448(lba, dst2448, true); diff --git a/psx/octoshock/psx/psx.h b/psx/octoshock/psx/psx.h index 63dd9a4a4b..edcfcbef88 100644 --- a/psx/octoshock/psx/psx.h +++ b/psx/octoshock/psx/psx.h @@ -89,6 +89,8 @@ namespace MDFN_IEN_PSX #define PSX_EVENT_MAXTS 0x20000000 void PSX_SetEventNT(const int type, const pscpu_timestamp_t next_timestamp); + void PSX_SetDMACycleSteal(unsigned stealage); + void PSX_GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider); uint32 PSX_GetRandU32(uint32 mina, uint32 maxa); @@ -237,8 +239,18 @@ public: return mcbReadTOC(mOpaque, read_target, tracks); } + //formerly ReadRawSector + //Reads 2352 + 96 s32 ReadLBA2448(s32 lba, void* dst2448); - s32 ReadLBA2048(s32 lba, void* dst2048); + + //formerly ReadRawSectorPWOnly + //Reads 96 bytes (of raw subchannel PW data) into pwbuf. + //Probably the same format as what's at the end of ReadLBA2448 + //TODO - reorder args + bool ReadLBA_PW(uint8* pwbuf96, int32 lba, bool hint_fullread); + + //only used by disc analysis stuff which should be refactored anyway. should eventually be removed + s32 ReadLBA2048(s32 lba, void* dst2048); private: s32 InternalReadLBA2448(s32 lba, void* dst2448, bool needSubcode); diff --git a/psx/octoshock/psx/spu.cpp b/psx/octoshock/psx/spu.cpp index 2f5e8a3872..bf51a7b37e 100644 --- a/psx/octoshock/psx/spu.cpp +++ b/psx/octoshock/psx/spu.cpp @@ -846,6 +846,7 @@ while(sample_clocks > 0) accum[lr] += ((reverb[lr] * ReverbVol[lr]) >> 15); clamp(&accum[lr], -32768, 32767); output[lr] = (accum[lr] * GlobalSweep[lr].ReadVolume()) >> 15; + clamp(&output[lr], -32768, 32767); } if(IntermediateBufferPos < 4096) // Overflow might occur in some debugger use cases. @@ -1304,6 +1305,10 @@ uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 speci break; } } + else if(which >= GSREG_FB_SRC_A && which <= GSREG_IN_COEF_R) + { + ret = ReverbRegs[which - GSREG_FB_SRC_A]; + } else switch(which) { case GSREG_SPUCONTROL: @@ -1377,43 +1382,6 @@ uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 speci case GSREG_BLOCKEND: ret = BlockEnd; break; - - - //case GSREG_FB_SRC_A ... GSREG_IN_COEF_R: - case GSREG_FB_SRC_A: - case GSREG_FB_SRC_B: - case GSREG_IIR_ALPHA: - case GSREG_ACC_COEF_A: - case GSREG_ACC_COEF_B: - case GSREG_ACC_COEF_C: - case GSREG_ACC_COEF_D: - case GSREG_IIR_COEF: - case GSREG_FB_ALPHA: - case GSREG_FB_X: - case GSREG_IIR_DEST_A0: - case GSREG_IIR_DEST_A1: - case GSREG_ACC_SRC_A0: - case GSREG_ACC_SRC_A1: - case GSREG_ACC_SRC_B0: - case GSREG_ACC_SRC_B1: - case GSREG_IIR_SRC_A0: - case GSREG_IIR_SRC_A1: - case GSREG_IIR_DEST_B0: - case GSREG_IIR_DEST_B1: - case GSREG_ACC_SRC_C0: - case GSREG_ACC_SRC_C1: - case GSREG_ACC_SRC_D0: - case GSREG_ACC_SRC_D1: - case GSREG_IIR_SRC_B1: - case GSREG_IIR_SRC_B0: - case GSREG_MIX_DEST_A0: - case GSREG_MIX_DEST_A1: - case GSREG_MIX_DEST_B0: - case GSREG_MIX_DEST_B1: - case GSREG_IN_COEF_L: - case GSREG_IN_COEF_R: - ret = ReverbRegs[which - GSREG_FB_SRC_A]; - break; } return(ret); @@ -1421,8 +1389,11 @@ uint32 PS_SPU::GetRegister(unsigned int which, char *special, const uint32 speci void PS_SPU::SetRegister(unsigned int which, uint32 value) { - - switch(which) + if(which >= GSREG_FB_SRC_A && which <= GSREG_IN_COEF_R) + { + ReverbRegs[which - GSREG_FB_SRC_A] = value; + } + else switch(which) { case GSREG_SPUCONTROL: SPUControl = value; @@ -1499,44 +1470,6 @@ void PS_SPU::SetRegister(unsigned int which, uint32 value) case GSREG_BLOCKEND: BlockEnd = value & 0xFFFFFF; break; - - - //case GSREG_FB_SRC_A ... GSREG_IN_COEF_R: - case GSREG_FB_SRC_A: - case GSREG_FB_SRC_B: - case GSREG_IIR_ALPHA: - case GSREG_ACC_COEF_A: - case GSREG_ACC_COEF_B: - case GSREG_ACC_COEF_C: - case GSREG_ACC_COEF_D: - case GSREG_IIR_COEF: - case GSREG_FB_ALPHA: - case GSREG_FB_X: - case GSREG_IIR_DEST_A0: - case GSREG_IIR_DEST_A1: - case GSREG_ACC_SRC_A0: - case GSREG_ACC_SRC_A1: - case GSREG_ACC_SRC_B0: - case GSREG_ACC_SRC_B1: - case GSREG_IIR_SRC_A0: - case GSREG_IIR_SRC_A1: - case GSREG_IIR_DEST_B0: - case GSREG_IIR_DEST_B1: - case GSREG_ACC_SRC_C0: - case GSREG_ACC_SRC_C1: - case GSREG_ACC_SRC_D0: - case GSREG_ACC_SRC_D1: - case GSREG_IIR_SRC_B1: - case GSREG_IIR_SRC_B0: - case GSREG_MIX_DEST_A0: - case GSREG_MIX_DEST_A1: - case GSREG_MIX_DEST_B0: - case GSREG_MIX_DEST_B1: - case GSREG_IN_COEF_L: - case GSREG_IN_COEF_R: - ReverbRegs[which - GSREG_FB_SRC_A] = value; - break; - } } diff --git a/psx/octoshock/psx/timer.cpp b/psx/octoshock/psx/timer.cpp index a00fd859dc..88a94ed2e4 100644 --- a/psx/octoshock/psx/timer.cpp +++ b/psx/octoshock/psx/timer.cpp @@ -15,6 +15,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +#include #include "psx.h" #include "timer.h"