2012-11-01 16:19:01 +01:00
// Copyright (c) 2012- PPSSPP Project.
// 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
2012-11-04 23:01:49 +01:00
// the Free Software Foundation, version 2.0 or later versions.
2012-11-01 16:19:01 +01:00
// 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 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official git repository and contact information can be found at
// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/.
2013-09-08 11:49:24 -07:00
# include <map>
# include <vector>
2017-02-27 21:57:46 +01:00
# include <mutex>
2013-09-08 11:49:24 -07:00
2020-08-10 00:12:51 -07:00
# include "Common/Serialize/Serializer.h"
# include "Common/Serialize/SerializeFuncs.h"
# include "Common/Serialize/SerializeList.h"
# include "Common/Serialize/SerializeMap.h"
2020-10-05 20:58:33 +02:00
# include "Common/Data/Collections/ThreadSafeList.h"
2013-09-08 11:49:24 -07:00
# include "Core/HLE/HLE.h"
2014-03-15 11:22:19 -07:00
# include "Core/HLE/FunctionWrappers.h"
2013-09-08 11:49:24 -07:00
# include "Core/MIPS/MIPS.h"
# include "Core/CoreParameter.h"
# include "Core/CoreTiming.h"
2015-04-05 18:09:35 -07:00
# include "Core/MemMapHelpers.h"
2013-09-08 11:49:24 -07:00
# include "Core/Reporting.h"
2015-04-05 18:09:35 -07:00
# include "Core/System.h"
2013-09-08 11:49:24 -07:00
# include "Core/HLE/sceGe.h"
# include "Core/HLE/sceKernelMemory.h"
# include "Core/HLE/sceKernelThread.h"
# include "Core/HLE/sceKernelInterrupt.h"
# include "Core/HLE/KernelWaitHelpers.h"
# include "GPU/GPUState.h"
2024-12-02 10:32:57 +01:00
# include "GPU/GPUCommon.h"
2012-11-01 16:19:01 +01:00
2015-07-24 19:52:42 +02:00
static const int LIST_ID_MAGIC = 0x35000000 ;
2012-12-21 22:50:35 -08:00
static PspGeCallbackData ge_callback_data [ 16 ] ;
static bool ge_used_callbacks [ 16 ] = { 0 } ;
2013-09-08 11:49:24 -07:00
typedef std : : vector < SceUID > WaitingThreadList ;
static std : : map < int , WaitingThreadList > listWaitingThreads ;
static WaitingThreadList drawWaitingThreads ;
2015-07-24 19:52:42 +02:00
struct GeInterruptData {
2013-02-04 00:41:16 +01:00
int listid ;
u32 pc ;
2014-04-17 22:33:13 -07:00
u32 cmd ;
2013-02-04 00:41:16 +01:00
} ;
2014-04-27 15:52:36 -07:00
static ThreadSafeList < GeInterruptData > ge_pending_cb ;
2013-04-07 12:45:42 -07:00
static int geSyncEvent ;
static int geInterruptEvent ;
2013-08-11 13:41:42 -07:00
static int geCycleEvent ;
2015-07-24 19:52:42 +02:00
class GeIntrHandler : public IntrHandler {
2013-02-04 00:41:16 +01:00
public :
GeIntrHandler ( ) : IntrHandler ( PSP_GE_INTR ) { }
2015-07-24 19:52:42 +02:00
bool run ( PendingInterrupt & pend ) override {
2015-10-04 10:32:15 +02:00
if ( ge_pending_cb . empty ( ) ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : sceGe , " Unable to run GE interrupt: no pending interrupt " ) ;
2015-10-04 10:32:15 +02:00
return false ;
}
2013-02-04 00:41:16 +01:00
GeInterruptData intrdata = ge_pending_cb . front ( ) ;
DisplayList * dl = gpu - > getList ( intrdata . listid ) ;
2015-07-24 19:52:42 +02:00
if ( dl = = NULL ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : sceGe , " Unable to run GE interrupt: list doesn't exist: %d " , intrdata . listid ) ;
2013-04-05 00:21:47 -07:00
return false ;
2013-02-03 23:22:27 -08:00
}
2015-07-24 19:52:42 +02:00
if ( ! dl - > interruptsEnabled ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : sceGe , " Unable to run GE interrupt: list has interrupts disabled, should not happen " ) ;
2013-09-04 14:07:52 -04:00
return false ;
}
2013-04-06 08:19:54 -07:00
gpu - > InterruptStart ( intrdata . listid ) ;
2013-02-04 00:41:16 +01:00
2014-04-17 22:33:13 -07:00
const u32 cmd = intrdata . cmd ;
2013-04-06 09:59:24 -07:00
int subintr = - 1 ;
2015-07-24 19:52:42 +02:00
if ( dl - > subIntrBase > = 0 ) {
switch ( dl - > signal ) {
2013-04-06 10:30:12 -07:00
case PSP_GE_SIGNAL_SYNC :
case PSP_GE_SIGNAL_JUMP :
case PSP_GE_SIGNAL_CALL :
case PSP_GE_SIGNAL_RET :
// Do nothing.
break ;
case PSP_GE_SIGNAL_HANDLER_PAUSE :
if ( cmd = = GE_CMD_FINISH )
subintr = dl - > subIntrBase | PSP_GE_SUBINTR_SIGNAL ;
break ;
default :
if ( cmd = = GE_CMD_SIGNAL )
subintr = dl - > subIntrBase | PSP_GE_SUBINTR_SIGNAL ;
else
subintr = dl - > subIntrBase | PSP_GE_SUBINTR_FINISH ;
break ;
}
2013-04-06 09:59:24 -07:00
}
2013-02-04 00:41:16 +01:00
2014-04-11 23:49:33 -07:00
// Set the list as complete once the interrupt starts.
// In other words, not before another interrupt finishes.
if ( dl - > signal ! = PSP_GE_SIGNAL_HANDLER_PAUSE & & cmd = = GE_CMD_FINISH ) {
dl - > state = PSP_GE_DL_STATE_COMPLETED ;
}
2013-04-06 09:59:24 -07:00
SubIntrHandler * handler = get ( subintr ) ;
2015-07-24 19:52:42 +02:00
if ( handler ! = NULL ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : CPU , " Entering GE interrupt handler %08x " , handler - > handlerAddress ) ;
2013-02-04 00:41:16 +01:00
currentMIPS - > pc = handler - > handlerAddress ;
2013-04-05 00:21:47 -07:00
u32 data = dl - > subIntrToken ;
2013-02-04 00:41:16 +01:00
currentMIPS - > r [ MIPS_REG_A0 ] = data & 0xFFFF ;
currentMIPS - > r [ MIPS_REG_A1 ] = handler - > handlerArg ;
currentMIPS - > r [ MIPS_REG_A2 ] = sceKernelGetCompiledSdkVersion ( ) < = 0x02000010 ? 0 : intrdata . pc + 4 ;
// RA is already taken care of in __RunOnePendingInterrupt
return true ;
}
2014-07-20 21:35:11 -07:00
if ( dl - > signal = = PSP_GE_SIGNAL_HANDLER_SUSPEND ) {
if ( sceKernelGetCompiledSdkVersion ( ) < = 0x02000010 ) {
if ( dl - > state ! = PSP_GE_DL_STATE_NONE & & dl - > state ! = PSP_GE_DL_STATE_COMPLETED ) {
dl - > state = PSP_GE_DL_STATE_QUEUED ;
}
}
}
2013-02-04 00:41:16 +01:00
ge_pending_cb . pop_front ( ) ;
2013-04-06 08:19:54 -07:00
gpu - > InterruptEnd ( intrdata . listid ) ;
2013-04-21 00:23:35 +02:00
// Seen in GoW.
2013-04-07 12:45:42 -07:00
if ( subintr > = 0 )
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " Ignoring interrupt for display list %d, already been released. " , intrdata . listid ) ;
2024-12-03 22:34:08 +01:00
// Hm. This might be really tricky to get to behave the same in both modes. Here we are in __KernelReschedule, CoreTiming::Advance, ProcessEvents, GeExecuteInterrupt, ... .... __RunOnePendingInterrupt
// But not sure how much it will matter. The test pause2 hits here.
2024-12-04 20:04:42 +01:00
gpu - > ProcessDLQueue ( ) ;
2013-02-04 00:41:16 +01:00
return false ;
}
2015-07-24 19:52:42 +02:00
void handleResult ( PendingInterrupt & pend ) override {
2013-02-04 00:41:16 +01:00
GeInterruptData intrdata = ge_pending_cb . front ( ) ;
ge_pending_cb . pop_front ( ) ;
2013-04-05 00:21:47 -07:00
DisplayList * dl = gpu - > getList ( intrdata . listid ) ;
2015-07-24 19:52:42 +02:00
if ( ! dl - > interruptsEnabled ) {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : sceGe , " Unable to finish GE interrupt: list has interrupts disabled, should not happen " ) ;
2013-09-01 10:16:29 -07:00
return ;
}
2013-04-05 00:21:47 -07:00
2015-07-24 19:52:42 +02:00
switch ( dl - > signal ) {
2013-04-05 00:21:47 -07:00
case PSP_GE_SIGNAL_HANDLER_SUSPEND :
2015-07-24 19:52:42 +02:00
if ( sceKernelGetCompiledSdkVersion ( ) < = 0x02000010 ) {
2013-04-05 00:21:47 -07:00
// uofw says dl->state = endCmd & 0xFF;
DisplayListState newState = static_cast < DisplayListState > ( Memory : : ReadUnchecked_U32 ( intrdata . pc - 4 ) & 0xFF ) ;
//dl->status = static_cast<DisplayListStatus>(Memory::ReadUnchecked_U32(intrdata.pc) & 0xFF);
//if(dl->status < 0 || dl->status > PSP_GE_LIST_PAUSED)
2024-07-14 14:42:59 +02:00
// ERROR_LOG(Log::sceGe, "Weird DL status after signal suspend %x", dl->status);
2015-07-24 19:52:42 +02:00
if ( newState ! = PSP_GE_DL_STATE_RUNNING ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG_REPORT ( Log : : sceGe , " GE Interrupt: newState might be %d " , newState ) ;
2015-07-24 19:52:42 +02:00
}
2013-04-05 00:21:47 -07:00
2014-06-22 23:19:35 -07:00
if ( dl - > state ! = PSP_GE_DL_STATE_NONE & & dl - > state ! = PSP_GE_DL_STATE_COMPLETED ) {
dl - > state = PSP_GE_DL_STATE_QUEUED ;
}
2013-04-05 00:21:47 -07:00
}
break ;
default :
break ;
}
2013-04-06 08:19:54 -07:00
gpu - > InterruptEnd ( intrdata . listid ) ;
2024-12-04 20:04:42 +01:00
// TODO: This is called from __KernelReturnFromInterrupt which does a bunch of stuff afterwards.
// Using hleSplitSyscallOverGe here breaks the gpu/signals/suspend.prx test, for that reason.
// So we just process inline and sacrifice debuggability a little.
gpu - > ProcessDLQueue ( ) ;
2013-02-04 00:41:16 +01:00
}
} ;
2015-07-24 19:52:42 +02:00
static void __GeExecuteSync ( u64 userdata , int cyclesLate ) {
2013-04-07 12:45:42 -07:00
int listid = userdata > > 32 ;
2014-03-29 17:02:41 -07:00
GPUSyncType type = ( GPUSyncType ) ( userdata & 0xFFFFFFFF ) ;
bool wokeThreads = __GeTriggerWait ( type , listid ) ;
gpu - > SyncEnd ( type , listid , wokeThreads ) ;
2013-04-07 12:45:42 -07:00
}
2015-07-24 19:52:42 +02:00
static void __GeExecuteInterrupt ( u64 userdata , int cyclesLate ) {
2013-04-07 16:47:29 -07:00
__TriggerInterrupt ( PSP_INTR_IMMEDIATE , PSP_GE_INTR , PSP_INTR_SUB_NONE ) ;
2013-04-07 12:45:42 -07:00
}
2015-07-24 19:52:42 +02:00
static void __GeCheckCycles ( u64 userdata , int cyclesLate ) {
2017-11-05 23:07:37 +01:00
// Deprecated
2013-08-11 13:41:42 -07:00
}
2015-07-24 19:52:42 +02:00
void __GeInit ( ) {
2012-12-21 22:50:35 -08:00
memset ( & ge_used_callbacks , 0 , sizeof ( ge_used_callbacks ) ) ;
2015-07-24 19:52:42 +02:00
memset ( & ge_callback_data , 0 , sizeof ( ge_callback_data ) ) ;
2013-02-04 00:41:16 +01:00
ge_pending_cb . clear ( ) ;
__RegisterIntrHandler ( PSP_GE_INTR , new GeIntrHandler ( ) ) ;
2013-04-07 12:45:42 -07:00
geSyncEvent = CoreTiming : : RegisterEvent ( " GeSyncEvent " , & __GeExecuteSync ) ;
geInterruptEvent = CoreTiming : : RegisterEvent ( " GeInterruptEvent " , & __GeExecuteInterrupt ) ;
2017-11-05 23:07:37 +01:00
// Deprecated
2013-08-11 13:41:42 -07:00
geCycleEvent = CoreTiming : : RegisterEvent ( " GeCycleEvent " , & __GeCheckCycles ) ;
2013-09-08 11:49:24 -07:00
listWaitingThreads . clear ( ) ;
drawWaitingThreads . clear ( ) ;
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
struct GeInterruptData_v1 {
2014-04-17 22:33:13 -07:00
int listid ;
u32 pc ;
} ;
2015-07-24 19:52:42 +02:00
void __GeDoState ( PointerWrap & p ) {
2014-04-17 22:33:13 -07:00
auto s = p . Section ( " sceGe " , 1 , 2 ) ;
2013-09-14 20:23:03 -07:00
if ( ! s )
return ;
2020-08-09 21:20:42 -07:00
DoArray ( p , ge_callback_data , ARRAY_SIZE ( ge_callback_data ) ) ;
DoArray ( p , ge_used_callbacks , ARRAY_SIZE ( ge_used_callbacks ) ) ;
2014-04-17 22:33:13 -07:00
if ( s > = 2 ) {
2020-08-09 21:20:42 -07:00
Do ( p , ge_pending_cb ) ;
2014-04-17 22:33:13 -07:00
} else {
std : : list < GeInterruptData_v1 > old ;
2020-08-09 21:20:42 -07:00
Do ( p , old ) ;
2014-04-17 22:33:13 -07:00
ge_pending_cb . clear ( ) ;
2023-12-20 12:11:16 +03:00
for ( const auto & ge : old ) {
GeInterruptData intrdata = { ge . listid , ge . pc } ;
intrdata . cmd = Memory : : ReadUnchecked_U32 ( ge . pc - 4 ) > > 24 ;
2014-04-17 22:33:13 -07:00
ge_pending_cb . push_back ( intrdata ) ;
}
}
2013-04-07 12:45:42 -07:00
2020-08-09 21:20:42 -07:00
Do ( p , geSyncEvent ) ;
2013-04-07 12:45:42 -07:00
CoreTiming : : RestoreRegisterEvent ( geSyncEvent , " GeSyncEvent " , & __GeExecuteSync ) ;
2020-08-09 21:20:42 -07:00
Do ( p , geInterruptEvent ) ;
2013-04-07 12:45:42 -07:00
CoreTiming : : RestoreRegisterEvent ( geInterruptEvent , " GeInterruptEvent " , & __GeExecuteInterrupt ) ;
2020-08-09 21:20:42 -07:00
Do ( p , geCycleEvent ) ;
2013-08-11 13:41:42 -07:00
CoreTiming : : RestoreRegisterEvent ( geCycleEvent , " GeCycleEvent " , & __GeCheckCycles ) ;
2013-04-07 12:45:42 -07:00
2020-08-09 21:20:42 -07:00
Do ( p , listWaitingThreads ) ;
Do ( p , drawWaitingThreads ) ;
2013-09-08 11:49:24 -07:00
2012-12-21 22:50:35 -08:00
// Everything else is done in sceDisplay.
2012-12-28 02:22:39 -08:00
}
2015-07-24 19:52:42 +02:00
void __GeShutdown ( ) {
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
bool __GeTriggerSync ( GPUSyncType type , int id , u64 atTicks ) {
u64 userdata = ( u64 ) id < < 32 | ( u64 ) type ;
2013-04-07 17:52:57 -07:00
s64 future = atTicks - CoreTiming : : GetTicks ( ) ;
2015-07-24 19:52:42 +02:00
if ( type = = GPU_SYNC_DRAW ) {
2017-11-06 00:27:51 +01:00
s64 left = CoreTiming : : UnscheduleEvent ( geSyncEvent , userdata ) ;
2013-04-07 17:52:57 -07:00
if ( left > future )
future = left ;
}
2017-11-06 00:27:51 +01:00
CoreTiming : : ScheduleEvent ( future , geSyncEvent , userdata ) ;
2013-04-07 12:45:42 -07:00
return true ;
}
2013-02-12 07:42:07 -08:00
2015-07-24 19:52:42 +02:00
bool __GeTriggerInterrupt ( int listid , u32 pc , u64 atTicks ) {
2014-04-17 22:33:13 -07:00
GeInterruptData intrdata ;
intrdata . listid = listid ;
intrdata . pc = pc ;
intrdata . cmd = Memory : : ReadUnchecked_U32 ( pc - 4 ) > > 24 ;
ge_pending_cb . push_back ( intrdata ) ;
2013-04-07 12:45:42 -07:00
u64 userdata = ( u64 ) listid < < 32 | ( u64 ) pc ;
2017-11-06 00:27:51 +01:00
CoreTiming : : ScheduleEvent ( atTicks - CoreTiming : : GetTicks ( ) , geInterruptEvent , userdata ) ;
2013-04-06 02:25:17 -07:00
return true ;
2013-02-04 00:41:16 +01:00
}
2015-07-24 19:52:42 +02:00
void __GeWaitCurrentThread ( GPUSyncType type , SceUID waitId , const char * reason ) {
2014-03-29 17:02:41 -07:00
WaitType waitType ;
if ( type = = GPU_SYNC_DRAW ) {
2013-09-08 11:49:24 -07:00
drawWaitingThreads . push_back ( __KernelGetCurThread ( ) ) ;
2014-03-29 17:02:41 -07:00
waitType = WAITTYPE_GEDRAWSYNC ;
} else if ( type = = GPU_SYNC_LIST ) {
2013-09-08 11:49:24 -07:00
listWaitingThreads [ waitId ] . push_back ( __KernelGetCurThread ( ) ) ;
2014-03-29 17:02:41 -07:00
waitType = WAITTYPE_GELISTSYNC ;
} else {
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : sceGe , " __GeWaitCurrentThread: bad wait type " ) ;
2014-03-29 17:02:41 -07:00
return ;
}
2013-09-08 11:49:24 -07:00
2014-03-29 17:02:41 -07:00
__KernelWaitCurThread ( waitType , waitId , 0 , 0 , false , reason ) ;
2013-08-10 18:13:48 -07:00
}
2015-07-24 19:52:42 +02:00
static bool __GeTriggerWait ( WaitType waitType , SceUID waitId , WaitingThreadList & waitingThreads ) {
2013-09-08 11:49:24 -07:00
// TODO: Do they ever get a result other than 0?
bool wokeThreads = false ;
2023-12-20 12:11:16 +03:00
for ( int threadID : waitingThreads )
wokeThreads | = HLEKernel : : ResumeFromWait ( threadID , waitType , waitId , 0 ) ;
2013-09-08 11:49:24 -07:00
waitingThreads . clear ( ) ;
return wokeThreads ;
}
2015-07-24 19:52:42 +02:00
bool __GeTriggerWait ( GPUSyncType type , SceUID waitId ) {
2014-03-31 22:35:57 -07:00
// We check for the old type for old savestate compatibility.
2014-05-21 08:00:31 -07:00
if ( type = = GPU_SYNC_DRAW | | ( WaitType ) type = = WAITTYPE_GEDRAWSYNC )
2014-03-29 17:02:41 -07:00
return __GeTriggerWait ( WAITTYPE_GEDRAWSYNC , waitId , drawWaitingThreads ) ;
2014-05-21 08:00:31 -07:00
else if ( type = = GPU_SYNC_LIST | | ( WaitType ) type = = WAITTYPE_GELISTSYNC )
2014-03-29 17:02:41 -07:00
return __GeTriggerWait ( WAITTYPE_GELISTSYNC , waitId , listWaitingThreads [ waitId ] ) ;
2013-09-08 11:49:24 -07:00
else
2024-07-14 14:42:59 +02:00
ERROR_LOG_REPORT ( Log : : sceGe , " __GeTriggerWait: bad wait type " ) ;
2013-09-08 11:49:24 -07:00
return false ;
2013-08-10 18:13:48 -07:00
}
2015-07-24 19:52:42 +02:00
static u32 sceGeEdramGetAddr ( ) {
2012-11-01 16:19:01 +01:00
u32 retVal = 0x04000000 ;
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " %08x = sceGeEdramGetAddr " , retVal ) ;
2013-05-28 01:37:13 -07:00
hleEatCycles ( 150 ) ;
2012-11-01 16:19:01 +01:00
return retVal ;
}
2015-07-24 19:52:42 +02:00
// TODO: Return a different value for the PS3 enhanced-emulator games?
static u32 sceGeEdramGetSize ( ) {
const u32 retVal = 0x00200000 ;
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " %08x = sceGeEdramGetSize() " , retVal ) ;
2012-11-01 16:19:01 +01:00
return retVal ;
}
2015-07-24 19:52:42 +02:00
static int __GeSubIntrBase ( int callbackId ) {
2013-02-04 00:41:16 +01:00
return callbackId * 2 ;
2012-12-21 22:50:35 -08:00
}
2015-07-24 19:52:42 +02:00
u32 sceGeListEnQueue ( u32 listAddress , u32 stallAddress , int callbackId , u32 optParamAddr ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe ,
2024-12-03 21:32:38 +01:00
" sceGeListEnQueue(addr=%08x, stall=%08x, cbid=%08x, param=%08x) ticks=%d " ,
listAddress , stallAddress , callbackId , optParamAddr , CoreTiming : : GetTicks ( ) ) ;
2013-12-16 23:47:34 -08:00
auto optParam = PSPPointer < PspGeListArgs > : : Create ( optParamAddr ) ;
2013-09-20 09:42:09 -07:00
2024-12-03 22:34:08 +01:00
bool runList ;
u32 listID = gpu - > EnqueueList ( listAddress , stallAddress , __GeSubIntrBase ( callbackId ) , optParam , false , & runList ) ;
2024-12-03 21:34:55 +01:00
if ( ( int ) listID > = 0 )
listID = LIST_ID_MAGIC ^ listID ;
2024-12-03 22:34:08 +01:00
if ( runList ) {
2024-12-04 20:04:42 +01:00
if ( gpu - > ShouldSplitOverGe ( ) ) {
hleSplitSyscallOverGe ( ) ;
} else {
gpu - > ProcessDLQueue ( ) ;
}
2024-12-03 22:34:08 +01:00
}
hleEatCycles ( 490 ) ;
2024-12-04 20:04:42 +01:00
hleCoreTimingForceCheck ( ) ;
return listID ; // We already logged above, logs get confusing if we use hleLogSuccess.
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
u32 sceGeListEnQueueHead ( u32 listAddress , u32 stallAddress , int callbackId , u32 optParamAddr ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe ,
2024-12-03 21:32:38 +01:00
" sceGeListEnQueueHead(addr=%08x, stall=%08x, cbid=%08x, param=%08x) ticks=%d " ,
listAddress , stallAddress , callbackId , optParamAddr , CoreTiming : : GetTicks ( ) ) ;
2013-12-16 23:47:34 -08:00
auto optParam = PSPPointer < PspGeListArgs > : : Create ( optParamAddr ) ;
2013-09-20 09:42:09 -07:00
2024-12-03 22:34:08 +01:00
bool runList ;
u32 listID = gpu - > EnqueueList ( listAddress , stallAddress , __GeSubIntrBase ( callbackId ) , optParam , true , & runList ) ;
2024-12-03 21:34:55 +01:00
if ( ( int ) listID > = 0 )
listID = LIST_ID_MAGIC ^ listID ;
2024-12-03 22:34:08 +01:00
if ( runList ) {
2024-12-04 20:04:42 +01:00
if ( gpu - > ShouldSplitOverGe ( ) ) {
hleSplitSyscallOverGe ( ) ;
} else {
gpu - > ProcessDLQueue ( ) ;
}
2024-12-03 22:34:08 +01:00
}
hleEatCycles ( 480 ) ;
2024-12-04 20:04:42 +01:00
hleCoreTimingForceCheck ( ) ;
return listID ; // We already logged above, logs get confusing if we use hleLogSuccess.
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
static int sceGeListDeQueue ( u32 listID ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : sceGe , " sceGeListDeQueue(%08x) " , listID ) ;
2015-07-24 19:52:42 +02:00
int result = gpu - > DequeueList ( LIST_ID_MAGIC ^ listID ) ;
2013-09-08 11:49:24 -07:00
hleReSchedule ( " dlist dequeued " ) ;
return result ;
2012-12-21 22:11:44 -08:00
}
2015-07-24 19:52:42 +02:00
static int sceGeListUpdateStallAddr ( u32 displayListID , u32 stallAddress ) {
2013-09-21 14:42:38 -07:00
// Advance() might cause an interrupt, so defer the Advance but do it ASAP.
// Final Fantasy Type-0 has a graphical artifact without this (timing issue.)
2013-05-04 23:39:16 -07:00
hleEatCycles ( 190 ) ;
2024-12-04 20:04:42 +01:00
hleCoreTimingForceCheck ( ) ;
2013-09-21 14:42:38 -07:00
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " sceGeListUpdateStallAddr(dlid=%i, stalladdr=%08x) " , displayListID , stallAddress ) ;
2024-12-03 22:34:08 +01:00
bool runList ;
int retval = gpu - > UpdateStall ( LIST_ID_MAGIC ^ displayListID , stallAddress , & runList ) ;
if ( runList ) {
2024-12-04 20:04:42 +01:00
if ( gpu - > ShouldSplitOverGe ( ) ) {
hleSplitSyscallOverGe ( ) ;
} else {
gpu - > ProcessDLQueue ( ) ;
}
2024-12-03 22:34:08 +01:00
}
return retval ;
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
// 0 : wait for completion. 1:check and return
int sceGeListSync ( u32 displayListID , u32 mode ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " sceGeListSync(dlid=%08x, mode=%08x) " , displayListID , mode ) ;
2023-02-07 22:59:46 +01:00
hleEatCycles ( 220 ) ; // Fudged without measuring, copying sceGeContinue.
2015-07-24 19:52:42 +02:00
return gpu - > ListSync ( LIST_ID_MAGIC ^ displayListID , mode ) ;
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
static u32 sceGeDrawSync ( u32 mode ) {
2012-11-01 16:19:01 +01:00
//wait/check entire drawing state
2017-02-03 15:30:22 +01:00
if ( PSP_CoreParameter ( ) . compat . flags ( ) . DrawSyncEatCycles )
hleEatCycles ( 500000 ) ; //HACK(?) : Potential fix for Crash Tag Team Racing and a few Gundam games
2024-09-10 19:28:49 +02:00
else if ( ! PSP_CoreParameter ( ) . compat . flags ( ) . DrawSyncInstant )
2021-08-14 20:18:52 -07:00
hleEatCycles ( 1240 ) ;
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " sceGeDrawSync(mode=%d) (0=wait for completion, 1=peek) " , mode ) ;
2013-03-31 23:23:03 -07:00
return gpu - > DrawSync ( mode ) ;
2012-11-01 16:19:01 +01:00
}
2024-12-01 20:42:16 +01:00
static int sceGeContinue ( ) {
2024-12-03 21:00:08 +01:00
DEBUG_LOG ( Log : : sceGe , " sceGeContinue() " ) ;
2024-12-03 22:34:08 +01:00
bool runList ;
int ret = gpu - > Continue ( & runList ) ;
if ( runList ) {
2024-12-04 20:04:42 +01:00
if ( gpu - > ShouldSplitOverGe ( ) ) {
hleSplitSyscallOverGe ( ) ;
} else {
gpu - > ProcessDLQueue ( ) ;
}
2024-12-03 22:34:08 +01:00
}
2014-05-15 22:45:09 -07:00
hleEatCycles ( 220 ) ;
hleReSchedule ( " ge continue " ) ;
return ret ;
2012-11-28 13:45:22 +01:00
}
2015-07-24 19:52:42 +02:00
static int sceGeBreak ( u32 mode , u32 unknownPtr ) {
if ( mode > 1 ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : sceGe , " sceGeBreak(mode=%d, unknown=%08x): invalid mode " , mode , unknownPtr ) ;
2013-09-21 19:07:02 -07:00
return SCE_KERNEL_ERROR_INVALID_MODE ;
}
// Not sure what this is supposed to be for...
2022-01-25 19:50:35 -08:00
if ( ( int ) unknownPtr < 0 | | ( int ) ( unknownPtr + 16 ) < 0 ) {
2024-07-14 14:42:59 +02:00
WARN_LOG_REPORT ( Log : : sceGe , " sceGeBreak(mode=%d, unknown=%08x): invalid ptr " , mode , unknownPtr ) ;
2014-04-03 18:12:22 +02:00
return SCE_KERNEL_ERROR_PRIV_REQUIRED ;
2015-07-24 19:52:42 +02:00
} else if ( unknownPtr ! = 0 ) {
2024-07-14 14:42:59 +02:00
WARN_LOG_REPORT ( Log : : sceGe , " sceGeBreak(mode=%d, unknown=%08x): unknown ptr (%s) " , mode , unknownPtr , Memory : : IsValidAddress ( unknownPtr ) ? " valid " : " invalid " ) ;
2015-07-24 19:52:42 +02:00
}
2013-09-21 19:07:02 -07:00
2012-12-05 22:19:14 +01:00
//mode => 0 : current dlist 1: all drawing
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " sceGeBreak(mode=%d, unknown=%08x) " , mode , unknownPtr ) ;
2013-09-21 19:31:05 -07:00
int result = gpu - > Break ( mode ) ;
2015-07-24 19:52:42 +02:00
if ( result > = 0 & & mode = = 0 ) {
return LIST_ID_MAGIC ^ result ;
}
2013-09-21 19:31:05 -07:00
return result ;
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
static u32 sceGeSetCallback ( u32 structAddr ) {
2012-12-21 22:50:35 -08:00
int cbID = - 1 ;
2015-07-24 19:52:42 +02:00
for ( size_t i = 0 ; i < ARRAY_SIZE ( ge_used_callbacks ) ; + + i ) {
if ( ! ge_used_callbacks [ i ] ) {
2013-02-18 09:04:43 -08:00
cbID = ( int ) i ;
2012-12-21 22:50:35 -08:00
break ;
}
2015-07-24 19:52:42 +02:00
}
2012-11-01 16:19:01 +01:00
2015-07-24 19:52:42 +02:00
if ( cbID = = - 1 ) {
2024-07-14 14:42:59 +02:00
return hleLogWarning ( Log : : sceGe , SCE_KERNEL_ERROR_OUT_OF_MEMORY , " out of callback ids " ) ;
2012-12-21 23:08:17 -08:00
}
2012-12-21 22:50:35 -08:00
ge_used_callbacks [ cbID ] = true ;
2022-09-03 09:01:23 -07:00
auto callbackData = PSPPointer < PspGeCallbackData > : : Create ( structAddr ) ;
ge_callback_data [ cbID ] = * callbackData ;
callbackData . NotifyRead ( " GeSetCallback " ) ;
2012-12-21 22:50:35 -08:00
int subIntrBase = __GeSubIntrBase ( cbID ) ;
2015-07-24 19:52:42 +02:00
if ( ge_callback_data [ cbID ] . finish_func ! = 0 ) {
2012-12-21 22:50:35 -08:00
sceKernelRegisterSubIntrHandler ( PSP_GE_INTR , subIntrBase | PSP_GE_SUBINTR_FINISH ,
ge_callback_data [ cbID ] . finish_func , ge_callback_data [ cbID ] . finish_arg ) ;
sceKernelEnableSubIntr ( PSP_GE_INTR , subIntrBase | PSP_GE_SUBINTR_FINISH ) ;
2012-11-09 13:40:09 +01:00
}
2015-07-24 19:52:42 +02:00
if ( ge_callback_data [ cbID ] . signal_func ! = 0 ) {
2012-12-21 22:50:35 -08:00
sceKernelRegisterSubIntrHandler ( PSP_GE_INTR , subIntrBase | PSP_GE_SUBINTR_SIGNAL ,
ge_callback_data [ cbID ] . signal_func , ge_callback_data [ cbID ] . signal_arg ) ;
sceKernelEnableSubIntr ( PSP_GE_INTR , subIntrBase | PSP_GE_SUBINTR_SIGNAL ) ;
2012-11-09 13:40:09 +01:00
}
2012-11-04 11:54:45 +01:00
2024-07-14 14:42:59 +02:00
return hleLogSuccessI ( Log : : sceGe , cbID ) ;
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
static int sceGeUnsetCallback ( u32 cbID ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " sceGeUnsetCallback(cbid=%08x) " , cbID ) ;
2012-12-21 22:11:44 -08:00
2015-07-24 19:52:42 +02:00
if ( cbID > = ARRAY_SIZE ( ge_used_callbacks ) ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : sceGe , " sceGeUnsetCallback(cbid=%08x): invalid callback id " , cbID ) ;
2012-12-21 22:50:35 -08:00
return SCE_KERNEL_ERROR_INVALID_ID ;
2012-12-21 23:08:17 -08:00
}
2012-12-21 22:50:35 -08:00
2015-07-24 19:52:42 +02:00
if ( ge_used_callbacks [ cbID ] ) {
2012-12-21 22:50:35 -08:00
int subIntrBase = __GeSubIntrBase ( cbID ) ;
sceKernelReleaseSubIntrHandler ( PSP_GE_INTR , subIntrBase | PSP_GE_SUBINTR_FINISH ) ;
sceKernelReleaseSubIntrHandler ( PSP_GE_INTR , subIntrBase | PSP_GE_SUBINTR_SIGNAL ) ;
2015-07-24 19:52:42 +02:00
} else {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : sceGe , " sceGeUnsetCallback(cbid=%08x): ignoring unregistered callback id " , cbID ) ;
2015-07-24 19:52:42 +02:00
}
2012-12-21 22:50:35 -08:00
ge_used_callbacks [ cbID ] = false ;
2012-12-21 22:11:44 -08:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2012-11-18 13:04:49 +01:00
// Points to 512 32-bit words, where we can probably layout the context however we want
// unless some insane game pokes it and relies on it...
2015-07-24 19:52:42 +02:00
u32 sceGeSaveContext ( u32 ctxAddr ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " sceGeSaveContext(%08x) " , ctxAddr ) ;
2013-08-08 00:10:30 -07:00
2015-07-24 19:52:42 +02:00
if ( gpu - > BusyDrawing ( ) ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : sceGe , " sceGeSaveContext(%08x): lists in process, aborting " , ctxAddr ) ;
2013-09-20 09:42:41 -07:00
// Real error code.
return - 1 ;
2012-11-18 13:04:49 +01:00
}
// Let's just dump gstate.
2015-07-24 19:52:42 +02:00
if ( Memory : : IsValidAddress ( ctxAddr ) ) {
2013-09-20 00:33:32 -07:00
gstate . Save ( ( u32_le * ) Memory : : GetPointer ( ctxAddr ) ) ;
2012-11-18 13:04:49 +01:00
}
// This action should probably be pushed to the end of the queue of the display thread -
// when we have one.
return 0 ;
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
u32 sceGeRestoreContext ( u32 ctxAddr ) {
2024-07-14 14:42:59 +02:00
DEBUG_LOG ( Log : : sceGe , " sceGeRestoreContext(%08x) " , ctxAddr ) ;
2012-11-18 13:04:49 +01:00
2015-07-24 19:52:42 +02:00
if ( gpu - > BusyDrawing ( ) ) {
2024-07-14 14:42:59 +02:00
WARN_LOG ( Log : : sceGe , " sceGeRestoreContext(%08x): lists in process, aborting " , ctxAddr ) ;
2013-09-20 09:42:41 -07:00
return SCE_KERNEL_ERROR_BUSY ;
2012-11-18 13:04:49 +01:00
}
2015-07-24 19:52:42 +02:00
if ( Memory : : IsValidAddress ( ctxAddr ) ) {
2013-09-20 00:33:32 -07:00
gstate . Restore ( ( u32_le * ) Memory : : GetPointer ( ctxAddr ) ) ;
2012-11-18 13:04:49 +01:00
}
2016-01-06 23:49:02 +01:00
gpu - > ReapplyGfxState ( ) ;
2012-11-18 13:04:49 +01:00
return 0 ;
2012-11-01 16:19:01 +01:00
}
2014-12-08 04:40:08 -05:00
static int sceGeGetMtx ( int type , u32 matrixPtr ) {
2022-09-27 20:20:46 -07:00
int size = type = = GE_MTX_PROJECTION ? 16 : 12 ;
if ( ! Memory : : IsValidRange ( matrixPtr , size * sizeof ( float ) ) ) {
2024-07-14 14:42:59 +02:00
return hleLogError ( Log : : sceGe , - 1 , " bad matrix ptr " ) ;
2013-01-05 19:26:37 +01:00
}
2022-09-27 22:29:55 -07:00
u32_le * dest = ( u32_le * ) Memory : : GetPointerWriteUnchecked ( matrixPtr ) ;
// Note: this reads the CPU-visible matrix values, which may differ from the actual used values.
// They only differ when more DATA commands are sent than are valid for a matrix.
if ( ! gpu | | ! gpu - > GetMatrix24 ( GEMatrixType ( type ) , dest , 0 ) )
2024-07-14 14:42:59 +02:00
return hleLogError ( Log : : sceGe , SCE_KERNEL_ERROR_INVALID_INDEX , " invalid matrix " ) ;
2022-09-27 20:20:46 -07:00
2024-07-14 14:42:59 +02:00
return hleLogSuccessInfoI ( Log : : sceGe , 0 ) ;
2012-12-21 22:11:44 -08:00
}
2014-12-08 04:40:08 -05:00
static u32 sceGeGetCmd ( int cmd ) {
2013-10-05 10:31:57 -07:00
if ( cmd > = 0 & & cmd < ( int ) ARRAY_SIZE ( gstate . cmdmem ) ) {
2022-09-05 19:37:58 -07:00
// Does not mask away the high bits. But matrix regs don't read back.
u32 val = gstate . cmdmem [ cmd ] ;
switch ( cmd ) {
case GE_CMD_BONEMATRIXDATA :
case GE_CMD_WORLDMATRIXDATA :
case GE_CMD_VIEWMATRIXDATA :
case GE_CMD_PROJMATRIXDATA :
case GE_CMD_TGENMATRIXDATA :
val & = 0xFF000000 ;
break ;
case GE_CMD_BONEMATRIXNUMBER :
val & = 0xFF00007F ;
break ;
case GE_CMD_WORLDMATRIXNUMBER :
case GE_CMD_VIEWMATRIXNUMBER :
case GE_CMD_PROJMATRIXNUMBER :
case GE_CMD_TGENMATRIXNUMBER :
val & = 0xFF00000F ;
break ;
default :
break ;
}
2024-07-14 14:42:59 +02:00
return hleLogSuccessInfoX ( Log : : sceGe , val ) ;
2013-09-21 10:23:41 -07:00
}
2024-07-14 14:42:59 +02:00
return hleLogError ( Log : : sceGe , SCE_KERNEL_ERROR_INVALID_INDEX ) ;
2012-11-01 16:19:01 +01:00
}
2014-12-08 04:40:08 -05:00
static int sceGeGetStack ( int index , u32 stackPtr ) {
2024-07-14 14:42:59 +02:00
WARN_LOG_REPORT ( Log : : sceGe , " sceGeGetStack(%i, %08x) " , index , stackPtr ) ;
2013-09-21 10:03:49 -07:00
return gpu - > GetStack ( index , stackPtr ) ;
}
2022-10-01 23:18:42 -07:00
static u32 sceGeEdramSetAddrTranslation ( u32 new_size ) {
2013-09-19 23:03:34 -07:00
bool outsideRange = new_size ! = 0 & & ( new_size < 0x200 | | new_size > 0x1000 ) ;
bool notPowerOfTwo = ( new_size & ( new_size - 1 ) ) ! = 0 ;
2013-09-21 10:32:09 -07:00
if ( outsideRange | | notPowerOfTwo ) {
2024-07-14 14:42:59 +02:00
return hleLogWarning ( Log : : sceGe , SCE_KERNEL_ERROR_INVALID_VALUE , " invalid value " ) ;
2022-10-01 23:18:42 -07:00
}
if ( ! gpu ) {
2024-07-14 14:42:59 +02:00
return hleLogError ( Log : : sceGe , - 1 , " GPUInterface not available " ) ;
2013-09-19 23:03:34 -07:00
}
2024-07-14 14:42:59 +02:00
return hleReportDebug ( Log : : sceGe , gpu - > SetAddrTranslation ( new_size ) ) ;
2012-11-01 16:19:01 +01:00
}
2015-07-24 19:52:42 +02:00
const HLEFunction sceGe_user [ ] = {
2015-03-22 16:57:56 -07:00
{ 0XE47E40E4 , & WrapU_V < sceGeEdramGetAddr > , " sceGeEdramGetAddr " , ' x ' , " " } ,
2020-05-21 22:16:13 -07:00
{ 0XAB49E76A , & WrapU_UUIU < sceGeListEnQueue > , " sceGeListEnQueue " , ' x ' , " xxip " } ,
{ 0X1C0D95A6 , & WrapU_UUIU < sceGeListEnQueueHead > , " sceGeListEnQueueHead " , ' x ' , " xxip " } ,
2015-03-22 16:57:56 -07:00
{ 0XE0D68148 , & WrapI_UU < sceGeListUpdateStallAddr > , " sceGeListUpdateStallAddr " , ' i ' , " xx " } ,
{ 0X03444EB4 , & WrapI_UU < sceGeListSync > , " sceGeListSync " , ' i ' , " xx " } ,
{ 0XB287BD61 , & WrapU_U < sceGeDrawSync > , " sceGeDrawSync " , ' x ' , " x " } ,
{ 0XB448EC0D , & WrapI_UU < sceGeBreak > , " sceGeBreak " , ' i ' , " xx " } ,
{ 0X4C06E472 , & WrapI_V < sceGeContinue > , " sceGeContinue " , ' i ' , " " } ,
2022-09-03 09:01:23 -07:00
{ 0XA4FC06A4 , & WrapU_U < sceGeSetCallback > , " sceGeSetCallback " , ' i ' , " p " } ,
2015-03-22 16:57:56 -07:00
{ 0X05DB22CE , & WrapI_U < sceGeUnsetCallback > , " sceGeUnsetCallback " , ' i ' , " x " } ,
{ 0X1F6752AD , & WrapU_V < sceGeEdramGetSize > , " sceGeEdramGetSize " , ' x ' , " " } ,
2022-10-01 23:18:42 -07:00
{ 0XB77905EA , & WrapU_U < sceGeEdramSetAddrTranslation > , " sceGeEdramSetAddrTranslation " , ' x ' , " x " } ,
2015-03-22 16:57:56 -07:00
{ 0XDC93CFEF , & WrapU_I < sceGeGetCmd > , " sceGeGetCmd " , ' x ' , " i " } ,
2022-09-27 20:20:46 -07:00
{ 0X57C8945B , & WrapI_IU < sceGeGetMtx > , " sceGeGetMtx " , ' i ' , " ip " } ,
2015-03-22 16:57:56 -07:00
{ 0X438A385A , & WrapU_U < sceGeSaveContext > , " sceGeSaveContext " , ' x ' , " x " } ,
{ 0X0BF608FB , & WrapU_U < sceGeRestoreContext > , " sceGeRestoreContext " , ' x ' , " x " } ,
{ 0X5FB86AB0 , & WrapI_U < sceGeListDeQueue > , " sceGeListDeQueue " , ' i ' , " x " } ,
{ 0XE66CB92E , & WrapI_IU < sceGeGetStack > , " sceGeGetStack " , ' i ' , " ix " } ,
2012-11-01 16:19:01 +01:00
} ;
2015-07-24 19:52:42 +02:00
void Register_sceGe_user ( ) {
2012-11-01 16:19:01 +01:00
RegisterModule ( " sceGe_user " , ARRAY_SIZE ( sceGe_user ) , sceGe_user ) ;
}