/*
This file is part of jpcsp.
Jpcsp 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 3 of the License, or
(at your option) any later version.
Jpcsp 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 Jpcsp. If not, see <http://www.gnu.org/licenses/>.
*/
package jpcsp.HLE.modules;
import jpcsp.HLE.CanBeNull;
import jpcsp.HLE.CheckArgument;
import jpcsp.HLE.HLEFunction;
import jpcsp.HLE.HLEModule;
import jpcsp.HLE.HLEUnimplemented;
import jpcsp.HLE.SceKernelErrorException;
import jpcsp.HLE.TPointer;
import java.util.HashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import jpcsp.Emulator;
import jpcsp.Memory;
import jpcsp.MemoryMap;
import jpcsp.HLE.Modules;
import jpcsp.HLE.kernel.managers.IntrManager;
import jpcsp.HLE.kernel.managers.SceUidManager;
import jpcsp.HLE.kernel.types.IAction;
import jpcsp.HLE.kernel.types.IWaitStateChecker;
import jpcsp.HLE.kernel.types.PspGeList;
import jpcsp.HLE.kernel.types.SceKernelCallbackInfo;
import jpcsp.HLE.kernel.types.SceKernelErrors;
import jpcsp.HLE.kernel.types.SceKernelThreadInfo;
import jpcsp.HLE.kernel.types.ThreadWaitInfo;
import jpcsp.HLE.kernel.types.pspGeCallbackData;
import jpcsp.HLE.kernel.types.pspGeListOptParam;
import jpcsp.HLE.kernel.types.interrupts.GeCallbackInterruptHandler;
import jpcsp.HLE.kernel.types.interrupts.GeInterruptHandler;
import jpcsp.graphics.GeCommands;
import jpcsp.graphics.VideoEngine;
import jpcsp.graphics.RE.externalge.ExternalGE;
import jpcsp.memory.IMemoryReader;
import jpcsp.memory.MemoryReader;
import jpcsp.util.Utilities;
import org.apache.log4j.Logger;
public class sceGe_user extends HLEModule {
public static Logger log = Modules.getLogger("sceGe_user");
public volatile boolean waitingForSync;
public volatile boolean syncDone;
private HashMap<Integer, SceKernelCallbackInfo> signalCallbacks;
private HashMap<Integer, SceKernelCallbackInfo> finishCallbacks;
private static final String geCallbackPurpose = "sceGeCallback";
// PSP has an array of 64 GE lists
private static final int NUMBER_GE_LISTS = 64;
private PspGeList[] allGeLists;
private ConcurrentLinkedQueue<PspGeList> listFreeQueue;
private ConcurrentLinkedQueue<Integer> deferredThreadWakeupQueue;
public final static int PSP_GE_LIST_DONE = 0;
public final static int PSP_GE_LIST_QUEUED = 1;
public final static int PSP_GE_LIST_DRAWING = 2;
public final static int PSP_GE_LIST_STALL_REACHED = 3;
public final static int PSP_GE_LIST_END_REACHED = 4;
public final static int PSP_GE_LIST_CANCEL_DONE = 5;
public final static String[] PSP_GE_LIST_STRINGS = {
"PSP_GE_LIST_DONE",
"PSP_GE_LIST_QUEUED",
"PSP_GE_LIST_DRAWING",
"PSP_GE_LIST_STALL_REACHED",
"PSP_GE_LIST_END_REACHED",
"PSP_GE_LIST_CANCEL_DONE"
};
public final static int PSP_GE_SIGNAL_HANDLER_SUSPEND = 0x01;
public final static int PSP_GE_SIGNAL_HANDLER_CONTINUE = 0x02;
public final static int PSP_GE_SIGNAL_HANDLER_PAUSE = 0x03;
public final static int PSP_GE_SIGNAL_SYNC = 0x08;
public final static int PSP_GE_SIGNAL_JUMP = 0x10;
public final static int PSP_GE_SIGNAL_CALL = 0x11;
public final static int PSP_GE_SIGNAL_RETURN = 0x12;
public final static int PSP_GE_SIGNAL_TBP0_REL = 0x20;
public final static int PSP_GE_SIGNAL_TBP1_REL = 0x21;
public final static int PSP_GE_SIGNAL_TBP2_REL = 0x22;
public final static int PSP_GE_SIGNAL_TBP3_REL = 0x23;
public final static int PSP_GE_SIGNAL_TBP4_REL = 0x24;
public final static int PSP_GE_SIGNAL_TBP5_REL = 0x25;
public final static int PSP_GE_SIGNAL_TBP6_REL = 0x26;
public final static int PSP_GE_SIGNAL_TBP7_REL = 0x27;
public final static int PSP_GE_SIGNAL_TBP0_REL_OFFSET = 0x28;
public final static int PSP_GE_SIGNAL_TBP1_REL_OFFSET = 0x29;
public final static int PSP_GE_SIGNAL_TBP2_REL_OFFSET = 0x2A;
public final static int PSP_GE_SIGNAL_TBP3_REL_OFFSET = 0x2B;
public final static int PSP_GE_SIGNAL_TBP4_REL_OFFSET = 0x2C;
public final static int PSP_GE_SIGNAL_TBP5_REL_OFFSET = 0x2D;
public final static int PSP_GE_SIGNAL_TBP6_REL_OFFSET = 0x2E;
public final static int PSP_GE_SIGNAL_TBP7_REL_OFFSET = 0x2F;
public final static int PSP_GE_SIGNAL_BREAK = 0xFF;
public final static int PSP_GE_MATRIX_BONE0 = 0;
public final static int PSP_GE_MATRIX_BONE1 = 1;
public final static int PSP_GE_MATRIX_BONE2 = 2;
public final static int PSP_GE_MATRIX_BONE3 = 3;
public final static int PSP_GE_MATRIX_BONE4 = 4;
public final static int PSP_GE_MATRIX_BONE5 = 5;
public final static int PSP_GE_MATRIX_BONE6 = 6;
public final static int PSP_GE_MATRIX_BONE7 = 7;
public final static int PSP_GE_MATRIX_WORLD = 8;
public final static int PSP_GE_MATRIX_VIEW = 9;
public final static int PSP_GE_MATRIX_PROJECTION = 10;
public final static int PSP_GE_MATRIX_TEXGEN = 11;
public int eDRAMMemoryWidth;
@Override
public void start() {
log.debug(String.format("Starting %s", getName()));
waitingForSync = false;
syncDone = false;
signalCallbacks = new HashMap<Integer, SceKernelCallbackInfo>();
finishCallbacks = new HashMap<Integer, SceKernelCallbackInfo>();
listFreeQueue = new ConcurrentLinkedQueue<PspGeList>();
allGeLists = new PspGeList[NUMBER_GE_LISTS];
for (int i = 0; i < NUMBER_GE_LISTS; i++) {
allGeLists[i] = new PspGeList(i);
listFreeQueue.add(allGeLists[i]);
}
deferredThreadWakeupQueue = new ConcurrentLinkedQueue<Integer>();
eDRAMMemoryWidth = 1024;
super.start();
}
@Override
public void stop() {
log.debug(String.format("Stopping %s", getName()));
if (ExternalGE.isActive()) {
ExternalGE.onGeUserStop();
}
}
public void step() {
ThreadManForUser threadMan = Modules.ThreadManForUserModule;
for (Integer thid = deferredThreadWakeupQueue.poll(); thid != null; thid = deferredThreadWakeupQueue.poll()) {
if (log.isDebugEnabled()) {
log.debug("really waking thread " + Integer.toHexString(thid) + "(" + threadMan.getThreadName(thid) + ")");
}
threadMan.hleUnblockThread(thid);
ExternalGE.onGeStopWaitList();
}
}
private void triggerAsyncCallback(int cbid, int listId, int listPc, int behavior, int signalId, HashMap<Integer, SceKernelCallbackInfo> callbacks) {
SceKernelCallbackInfo callback = callbacks.get(cbid);
if (callback != null && callback.hasCallbackFunction()) {
if (log.isDebugEnabled()) {
log.debug(String.format("Scheduling Async Callback %s, listId=0x%X, listPc=0x%08X, behavior=%d, signalId=0x%X", callback.toString(), listId, listPc, behavior, signalId));
}
GeCallbackInterruptHandler geCallbackInterruptHandler = new GeCallbackInterruptHandler(callback.getCallbackFunction(), callback.getCallbackArgument(), listPc);
GeInterruptHandler geInterruptHandler = new GeInterruptHandler(geCallbackInterruptHandler, listId, behavior, signalId);
Emulator.getScheduler().addAction(geInterruptHandler);
} else {
hleGeOnAfterCallback(listId, behavior, false);
}
}
private void blockCurrentThreadOnList(PspGeList list, IAction action) {
ThreadManForUser threadMan = Modules.ThreadManForUserModule;
boolean blockCurrentThread = false;
boolean executeAction = false;
synchronized (this) {
int currentThreadId = threadMan.getCurrentThreadID();
if (list.isDone()) {
// There has been some race condition: the list has just completed
// do not block the thread
if (log.isDebugEnabled()) {
log.debug("blockCurrentThreadOnList not blocking thread " + Integer.toHexString(currentThreadId) + ", list completed " + list);
}
executeAction = true;
} else {
if (log.isDebugEnabled()) {
log.debug("blockCurrentThreadOnList blocking thread " + Integer.toHexString(currentThreadId) + " on list " + list);
}
list.blockedThreadIds.add(currentThreadId);
blockCurrentThread = true;
}
}
// Execute the action outside of the synchronized block
if (executeAction && action != null) {
action.execute();
}
// Block the thread outside of the synchronized block
if (blockCurrentThread) {
// Block the thread, but do not execute callbacks.
threadMan.hleBlockCurrentThread(SceKernelThreadInfo.JPCSP_WAIT_GE_LIST, list.id, false, action, new ListSyncWaitStateChecker(list));
ExternalGE.onGeStartWaitList();
}
}
// sceGeDrawSync is resetting all the lists having status PSP_GE_LIST_DONE
private void hleGeAfterDrawSyncAction() {
synchronized (this) {
for (int i = 0; i < NUMBER_GE_LISTS; i++) {
if (allGeLists[i].status == PSP_GE_LIST_DONE) {
allGeLists[i].reset();
}
}
}
}
/** Called from VideoEngine */
public void hleGeListSyncDone(PspGeList list) {
if (log.isDebugEnabled()) {
String msg = "hleGeListSyncDone list " + list;
if (list.isDone()) {
msg += ", done";
} else {
msg += ", NOT done";
}
if (list.blockedThreadIds.size() > 0 && list.status != PSP_GE_LIST_END_REACHED) {
msg += ", waking thread";
for (int threadId : list.blockedThreadIds) {
msg += " " + Integer.toHexString(threadId);
}
}
log.debug(msg);
}
synchronized (this) {
if (list.blockedThreadIds.size() > 0 && list.status != PSP_GE_LIST_END_REACHED) {
// things might go wrong if the thread already exists in the queue
deferredThreadWakeupQueue.addAll(list.blockedThreadIds);
}
if (list.isDone()) {
listFreeQueue.add(list);
}
}
}
public void hleGeOnAfterCallback(int listId, int behavior, boolean hasCallback) {
// (gid15) I could not make any difference between
// PSP_GE_BEHAVIOR_CONTINUE and PSP_GE_BEHAVIOR_SUSPEND
// Both wait for the completion of the callback before continuing
// the list processing...
if (behavior == PSP_GE_SIGNAL_HANDLER_CONTINUE
|| behavior == PSP_GE_SIGNAL_HANDLER_SUSPEND
|| !hasCallback) {
if (listId >= 0 && listId < NUMBER_GE_LISTS) {
PspGeList list = allGeLists[listId];
if (log.isDebugEnabled()) {
log.debug("hleGeOnAfterCallback restarting list " + list);
}
list.restartList();
}
}
}
/** safe to call from the Async display thread */
public void triggerFinishCallback(int cbid, int listId, int listPc, int callbackNotifyArg1) {
triggerAsyncCallback(cbid, listId, listPc, PSP_GE_SIGNAL_HANDLER_SUSPEND, callbackNotifyArg1, finishCallbacks);
}
/** safe to call from the Async display thread */
public void triggerSignalCallback(int cbid, int listId, int listPc, int behavior, int callbackNotifyArg1) {
triggerAsyncCallback(cbid, listId, listPc, behavior, callbackNotifyArg1, signalCallbacks);
}
public PspGeList getGeList(int id) {
if (id < 0 || id >= NUMBER_GE_LISTS) {
return null;
}
return allGeLists[id];
}
static class DeferredCallbackInfo {
public final int cbid;
public final int callbackIndex;
public final int listId;
public final int behavior;
public final int callbackNotifyArg1;
public DeferredCallbackInfo(int cbid, int callbackIndex, int callbackNotifyArg1) {
this.cbid = cbid;
this.callbackIndex = callbackIndex;
this.listId = -1;
this.behavior = PSP_GE_SIGNAL_HANDLER_SUSPEND;
this.callbackNotifyArg1 = callbackNotifyArg1;
}
public DeferredCallbackInfo(int cbid, int callbackIndex, int listId, int behavior, int callbackNotifyArg1) {
this.cbid = cbid;
this.callbackIndex = callbackIndex;
this.listId = listId;
this.behavior = behavior;
this.callbackNotifyArg1 = callbackNotifyArg1;
}
}
private class HLEAfterDrawSyncAction implements IAction {
@Override
public void execute() {
hleGeAfterDrawSyncAction();
}
}
private static class ListSyncWaitStateChecker implements IWaitStateChecker {
private PspGeList list;
public ListSyncWaitStateChecker(PspGeList list) {
this.list = list;
}
@Override
public boolean continueWaitState(SceKernelThreadInfo thread, ThreadWaitInfo wait) {
// Continue the wait state until the list is done
boolean contineWait = !list.isDone();
if (!contineWait) {
ExternalGE.onGeStopWaitList();
}
return contineWait;
}
}
public int checkListId(int id) {
if (id < 0 || id >= NUMBER_GE_LISTS) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_INVALID_ID);
}
return id;
}
public int checkMode(int mode) {
if (mode < 0 || mode > 1) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_INVALID_MODE);
}
return mode;
}
public int hleGeListEnQueue(TPointer listAddr, @CanBeNull TPointer stallAddr, int cbid, @CanBeNull TPointer argAddr, int saveContextAddr, boolean enqueueHead) {
pspGeListOptParam optParams = null;
int stackAddr = 0;
if (argAddr.isNotNull()) {
optParams = new pspGeListOptParam();
optParams.read(argAddr);
stackAddr = optParams.stackAddr;
saveContextAddr = optParams.contextAddr;
if (log.isDebugEnabled()) {
log.debug(String.format("hleGeListEnQueue optParams=%s", optParams));
}
}
boolean useCachedMemory = false;
if (Modules.SysMemUserForUserModule.hleKernelGetCompiledSdkVersion() >= 0x02000000) {
boolean isBusy;
if (ExternalGE.isActive()) {
isBusy = ExternalGE.hasDrawList(listAddr.getAddress(), stackAddr);
} else {
isBusy = VideoEngine.getInstance().hasDrawList(listAddr.getAddress(), stackAddr);
}
if (isBusy) {
log.warn(String.format("hleGeListEnQueue can't enqueue duplicate list address %s, stack 0x%08X", listAddr, stackAddr));
return SceKernelErrors.ERROR_BUSY;
}
} else {
// Old games (i.e. having PSP SDK version < 2.00) are sometimes
// reusing the same address for multiple lists, without waiting
// for the previous list to complete. They assume that the lists
// are being executed quite quickly, which is not the case when
// using the OpenGL rendering engine. There is some delay before
// the OpenGL frame refresh is being processed.
useCachedMemory = true;
}
// No need to cache any memory when using the external software renderer
if (ExternalGE.isActive()) {
useCachedMemory = false;
}
int result;
synchronized (this) {
PspGeList list = listFreeQueue.poll();
if (list == null) {
log.warn("hleGeListEnQueue no more free list available!");
if (log.isDebugEnabled()) {
for (int i = 0; i < NUMBER_GE_LISTS; i++) {
log.debug(String.format("List#%d: %s", i, allGeLists[i]));
}
}
return SceKernelErrors.ERROR_OUT_OF_MEMORY;
}
list.init(listAddr.getAddress(), stallAddr.getAddress(), cbid, optParams);
list.setSaveContextAddr(saveContextAddr);
if (useCachedMemory) {
setStallAddressWithCachedMemory(list, stallAddr.getAddress());
}
if (enqueueHead) {
// Send the list to the VideoEngine at the head of the queue.
list.startListHead();
} else {
// Send the list to the VideoEngine before triggering the display (setting GE dirty)
list.startList();
}
Modules.sceDisplayModule.setGeDirty(true);
result = list.id;
}
if (log.isDebugEnabled()) {
log.debug(String.format("hleGeListEnQueue returning 0x%X", result));
}
return result;
}
public int hleGeListSync(int id) {
if (id < 0 || id >= NUMBER_GE_LISTS) {
return -1;
}
PspGeList list = null;
int result;
synchronized (this) {
list = allGeLists[id];
result = list.status;
}
return result;
}
private void setStallAddressWithCachedMemory(PspGeList list, int stallAddr) {
int startAddress = list.list_addr;
int length;
if (stallAddr != 0) {
length = stallAddr - startAddress;
} else {
// The list has no stall address, scan for the FINISH command
IMemoryReader memoryReader = MemoryReader.getMemoryReader(startAddress, 4);
length = 0;
while (true) {
int instruction = memoryReader.readNext();
int command = VideoEngine.command(instruction);
if (command == GeCommands.FINISH) {
// Add 4 to include the END command that follows the FINISH command
length = memoryReader.getCurrentAddress() - startAddress + 4;
break;
}
}
}
if (length >= 0) {
int[] baseMemoryInts = Utilities.readInt32(startAddress, length);
list.setStallAddr(stallAddr, MemoryReader.getMemoryReader(startAddress, baseMemoryInts, 0, length), startAddress, startAddress + length);
if (log.isDebugEnabled()) {
log.debug(String.format("setStallAddressWithCachedMemory [0x%08X-0x%08X] %s", startAddress, startAddress + length, list));
}
} else {
list.setStallAddr(stallAddr);
}
}
@HLEFunction(nid = 0x1F6752AD, version = 150)
public int sceGeEdramGetSize() {
return MemoryMap.SIZE_VRAM;
}
@HLEFunction(nid = 0xE47E40E4, version = 150)
public int sceGeEdramGetAddr() {
return MemoryMap.START_VRAM;
}
@HLEFunction(nid = 0xB77905EA, version = 150)
public int sceGeEdramSetAddrTranslation(int size) {
// Faking. There's no need for real memory width conversion.
int previousWidth = eDRAMMemoryWidth;
eDRAMMemoryWidth = size;
return previousWidth;
}
@HLEFunction(nid = 0xDC93CFEF, version = 150)
public int sceGeGetCmd(int cmd) {
VideoEngine ve = VideoEngine.getInstance();
int value;
if (ExternalGE.isActive()) {
value = ExternalGE.getCmd(cmd);
} else {
value = ve.getCommandValue(cmd);
}
if (log.isInfoEnabled()) {
log.info(String.format("sceGeGetCmd %s: cmd=0x%X, value=0x%06X", ve.commandToString(cmd).toUpperCase(), cmd, value));
}
return value;
}
@HLEFunction(nid = 0x57C8945B, version = 150)
public int sceGeGetMtx(int mtxType, TPointer mtxAddr) {
if (mtxType < 0 || mtxType > PSP_GE_MATRIX_TEXGEN) {
log.warn(String.format("sceGeGetMtx invalid type mtxType=%d", mtxType));
return SceKernelErrors.ERROR_INVALID_INDEX;
}
float[] mtx;
if (ExternalGE.isActive()) {
mtx = ExternalGE.getMatrix(mtxType);
} else {
mtx = VideoEngine.getInstance().getMatrix(mtxType);
}
for (int i = 0; i < mtx.length; i++) {
// Float value is returned in lower 24 bits.
mtxAddr.setValue32(i << 2, Float.floatToRawIntBits(mtx[i]) >>> 8);
}
if (log.isInfoEnabled()) {
log.info(String.format("sceGeGetMtx mtxType=%d, mtxAddr=%s, mtx=%s", mtxType, mtxAddr, mtx));
}
return 0;
}
@HLEFunction(nid = 0x438A385A, version = 150)
public int sceGeSaveContext(TPointer contextAddr) {
if (ExternalGE.isActive()) {
return ExternalGE.saveContext(contextAddr.getAddress());
}
VideoEngine.getInstance().hleSaveContext(contextAddr.getAddress());
return 0;
}
@HLEFunction(nid = 0x0BF608FB, version = 150)
public int sceGeRestoreContext(TPointer contextAddr) {
if (ExternalGE.isActive()) {
return ExternalGE.restoreContext(contextAddr.getAddress());
}
VideoEngine.getInstance().hleRestoreContext(contextAddr.getAddress());
return 0;
}
@HLEFunction(nid = 0xAB49E76A, version = 150)
public int sceGeListEnQueue(TPointer listAddr, @CanBeNull TPointer stallAddr, int cbid, @CanBeNull TPointer argAddr) {
return hleGeListEnQueue(listAddr, stallAddr, cbid, argAddr, 0, false);
}
@HLEFunction(nid = 0x1C0D95A6, version = 150)
public int sceGeListEnQueueHead(TPointer listAddr, @CanBeNull TPointer stallAddr, int cbid, @CanBeNull TPointer argAddr) {
return hleGeListEnQueue(listAddr, stallAddr, cbid, argAddr, 0, true);
}
@HLEFunction(nid = 0x5FB86AB0, version = 150)
public int sceGeListDeQueue(@CheckArgument("checkListId") int id) {
synchronized (this) {
PspGeList list = allGeLists[id];
list.reset();
if (!listFreeQueue.contains(list)) {
listFreeQueue.add(list);
}
}
return 0;
}
@HLEFunction(nid = 0xE0D68148, version = 150)
public int sceGeListUpdateStallAddr(@CheckArgument("checkListId") int id, @CanBeNull TPointer stallAddr) {
synchronized (this) {
PspGeList list = allGeLists[id];
if (list.getStallAddr() != stallAddr.getAddress()) {
if (list.hasBaseMemoryReader()) {
setStallAddressWithCachedMemory(list, stallAddr.getAddress());
} else {
list.setStallAddr(stallAddr.getAddress());
}
Modules.sceDisplayModule.setGeDirty(true);
}
}
return 0;
}
@HLEFunction(nid = 0x03444EB4, version = 150)
public int sceGeListSync(@CheckArgument("checkListId") int id, @CheckArgument("checkMode") int mode) {
if (mode == 0 && IntrManager.getInstance().isInsideInterrupt()) {
log.debug("sceGeListSync (mode==0) cannot be called inside an interrupt handler!");
return SceKernelErrors.ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT;
}
PspGeList list = null;
boolean blockCurrentThread = false;
int result;
synchronized (this) {
list = allGeLists[id];
if (log.isDebugEnabled()) {
log.debug(String.format("sceGeListSync on list: %s", list));
}
if (list.isReset()) {
throw new SceKernelErrorException(SceKernelErrors.ERROR_INVALID_ID);
}
if (mode == 0 && !list.isDone()) {
result = 0;
blockCurrentThread = true;
} else {
result = list.status;
}
}
// Block the current thread outside of the synchronized block
if (blockCurrentThread) {
blockCurrentThreadOnList(list, null);
}
return result;
}
@HLEFunction(nid = 0xB287BD61, version = 150)
public int sceGeDrawSync(@CheckArgument("checkMode") int mode) {
if (mode == 0 && IntrManager.getInstance().isInsideInterrupt()) {
log.debug("sceGeDrawSync (mode==0) cannot be called inside an interrupt handler!");
return SceKernelErrors.ERROR_KERNEL_CANNOT_BE_CALLED_FROM_INTERRUPT;
}
// no synchronization on "this" required because we are not accessing
// local data, only list information from the VideoEngine.
int result = 0;
if (mode == 0) {
PspGeList lastList;
if (ExternalGE.isActive()) {
lastList = ExternalGE.getLastDrawList();
} else {
lastList = VideoEngine.getInstance().getLastDrawList();
}
if (lastList != null) {
blockCurrentThreadOnList(lastList, new HLEAfterDrawSyncAction());
} else {
if (log.isDebugEnabled()) {
log.debug("sceGeDrawSync all lists completed, not waiting");
}
hleGeAfterDrawSyncAction();
Modules.ThreadManForUserModule.hleRescheduleCurrentThread();
}
} else if (mode == 1) {
PspGeList currentList;
if (ExternalGE.isActive()) {
currentList = ExternalGE.getFirstDrawList();
} else {
currentList = VideoEngine.getInstance().getFirstDrawList();
}
if (currentList != null) {
result = currentList.status;
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceGeDrawSync mode=%d, returning %d", mode, result));
}
}
return result;
}
@HLEFunction(nid = 0xB448EC0D, version = 150)
public int sceGeBreak(@CheckArgument("checkMode") int mode, TPointer brk_addr) {
int result = 0;
PspGeList list;
if (ExternalGE.isActive()) {
list = ExternalGE.getCurrentList();
} else {
list = VideoEngine.getInstance().getCurrentList();
}
if (mode == 0) { // Pause the current list only.
if (list != null) {
list.pauseList();
result = list.id;
}
} else if (mode == 1) { // Pause the current list and cancel the rest of the queue.
if (list != null) {
list.pauseList();
for (int i = 0; i < NUMBER_GE_LISTS; i++) {
allGeLists[i].status = PSP_GE_LIST_CANCEL_DONE;
}
result = list.id;
}
}
return result;
}
@HLEFunction(nid = 0x4C06E472, version = 150)
public int sceGeContinue() {
PspGeList list;
if (ExternalGE.isActive()) {
list = ExternalGE.getCurrentList();
} else {
list = VideoEngine.getInstance().getCurrentList();
}
if (list != null) {
synchronized (this) {
if (list.status == PSP_GE_LIST_END_REACHED) {
Memory mem = Memory.getInstance();
if (mem.read32(list.getPc()) == (GeCommands.FINISH << 24) &&
mem.read32(list.getPc() + 4) == (GeCommands.END << 24)) {
list.readNextInstruction();
list.readNextInstruction();
}
}
list.restartList();
}
}
return 0;
}
@HLEFunction(nid = 0xA4FC06A4, version = 150, checkInsideInterrupt = true)
public int sceGeSetCallback(TPointer cbdata_addr) {
pspGeCallbackData cbdata = new pspGeCallbackData();
cbdata.read(cbdata_addr);
// The cbid returned has a value in the range [0..15].
int cbid = SceUidManager.getNewId(geCallbackPurpose, 0, 15);
if (cbid == SceUidManager.INVALID_ID) {
log.warn(String.format("sceGeSetCallback no more callback ID available"));
return SceKernelErrors.ERROR_OUT_OF_MEMORY;
}
if (log.isDebugEnabled()) {
log.debug(String.format("sceGeSetCallback signalFunc=0x%08X, signalArg=0x%08X, finishFunc=0x%08X, finishArg=0x%08X, result cbid=0x%X", cbdata.signalFunction, cbdata.signalArgument, cbdata.finishFunction, cbdata.finishArgument, cbid));
}
ThreadManForUser threadMan = Modules.ThreadManForUserModule;
SceKernelCallbackInfo callbackSignal = threadMan.hleKernelCreateCallback("GeCallbackSignal", cbdata.signalFunction, cbdata.signalArgument);
SceKernelCallbackInfo callbackFinish = threadMan.hleKernelCreateCallback("GeCallbackFinish", cbdata.finishFunction, cbdata.finishArgument);
signalCallbacks.put(cbid, callbackSignal);
finishCallbacks.put(cbid, callbackFinish);
return cbid;
}
@HLEFunction(nid = 0x05DB22CE, version = 150, checkInsideInterrupt = true)
public int sceGeUnsetCallback(int cbid) {
ThreadManForUser threadMan = Modules.ThreadManForUserModule;
SceKernelCallbackInfo callbackSignal = signalCallbacks.remove(cbid);
SceKernelCallbackInfo callbackFinish = finishCallbacks.remove(cbid);
if (callbackSignal != null) {
threadMan.hleKernelDeleteCallback(callbackSignal.getUid());
}
if (callbackFinish != null) {
threadMan.hleKernelDeleteCallback(callbackFinish.getUid());
}
SceUidManager.releaseId(cbid, geCallbackPurpose);
return 0;
}
/**
* Sets the EDRAM size.
*
* @param size The size (0x200000 or 0x400000).
*
* @return Zero on success, otherwise less than zero.
*/
@HLEUnimplemented
@HLEFunction(nid = 0x5BAA5439, version = 150)
public int sceGeEdramSetSize(int size) {
return 0;
}
/**
* Gets the EDRAM physical size.
*
* @return The EDRAM physical size.
*/
@HLEFunction(nid = 0x547EC5F0, version = 660)
public int sceGeEdramGetHwSize() {
return MemoryMap.SIZE_VRAM;
}
}