@HLELogging public class sceMeCore_driver extends HLEModule { public static Logger log = Modules.getLogger("sceMeCore_driver"); @Override public String getName() { return "sceMeCore_driver"; } @HLEUnimplemented @HLEFunction(nid = 0x051C1601, version = 500) public int sceMeBootStart500(int unknown) { return 0; } }
public class sceNp extends HLEModule { public static Logger log = Modules.getLogger("sceNp"); protected boolean initialized; @Override public void start() { initialized = false; super.start(); } /** * Initialization. * * @return */ @HLEUnimplemented @HLEFunction(nid = 0x857B47D3, version = 150, checkInsideInterrupt = true) public int sceNp_857B47D3() { // No parameters initialized = true; return 0; } /** * Termination. * * @return */ @HLEUnimplemented @HLEFunction(nid = 0x37E1E274, version = 150, checkInsideInterrupt = true) public int sceNp_37E1E274() { // No parameters initialized = false; return 0; } }
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 static final int PSP_GE_LIST_DONE = 0; public static final int PSP_GE_LIST_QUEUED = 1; public static final int PSP_GE_LIST_DRAWING = 2; public static final int PSP_GE_LIST_STALL_REACHED = 3; public static final int PSP_GE_LIST_END_REACHED = 4; public static final int PSP_GE_LIST_CANCEL_DONE = 5; public static final 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 static final int PSP_GE_SIGNAL_HANDLER_SUSPEND = 0x01; public static final int PSP_GE_SIGNAL_HANDLER_CONTINUE = 0x02; public static final int PSP_GE_SIGNAL_HANDLER_PAUSE = 0x03; public static final int PSP_GE_SIGNAL_SYNC = 0x08; public static final int PSP_GE_SIGNAL_JUMP = 0x10; public static final int PSP_GE_SIGNAL_CALL = 0x11; public static final int PSP_GE_SIGNAL_RETURN = 0x12; public static final int PSP_GE_SIGNAL_TBP0_REL = 0x20; public static final int PSP_GE_SIGNAL_TBP1_REL = 0x21; public static final int PSP_GE_SIGNAL_TBP2_REL = 0x22; public static final int PSP_GE_SIGNAL_TBP3_REL = 0x23; public static final int PSP_GE_SIGNAL_TBP4_REL = 0x24; public static final int PSP_GE_SIGNAL_TBP5_REL = 0x25; public static final int PSP_GE_SIGNAL_TBP6_REL = 0x26; public static final int PSP_GE_SIGNAL_TBP7_REL = 0x27; public static final int PSP_GE_SIGNAL_TBP0_REL_OFFSET = 0x28; public static final int PSP_GE_SIGNAL_TBP1_REL_OFFSET = 0x29; public static final int PSP_GE_SIGNAL_TBP2_REL_OFFSET = 0x2A; public static final int PSP_GE_SIGNAL_TBP3_REL_OFFSET = 0x2B; public static final int PSP_GE_SIGNAL_TBP4_REL_OFFSET = 0x2C; public static final int PSP_GE_SIGNAL_TBP5_REL_OFFSET = 0x2D; public static final int PSP_GE_SIGNAL_TBP6_REL_OFFSET = 0x2E; public static final int PSP_GE_SIGNAL_TBP7_REL_OFFSET = 0x2F; public static final int PSP_GE_SIGNAL_BREAK = 0xFF; public static final int PSP_GE_MATRIX_BONE0 = 0; public static final int PSP_GE_MATRIX_BONE1 = 1; public static final int PSP_GE_MATRIX_BONE2 = 2; public static final int PSP_GE_MATRIX_BONE3 = 3; public static final int PSP_GE_MATRIX_BONE4 = 4; public static final int PSP_GE_MATRIX_BONE5 = 5; public static final int PSP_GE_MATRIX_BONE6 = 6; public static final int PSP_GE_MATRIX_BONE7 = 7; public static final int PSP_GE_MATRIX_WORLD = 8; public static final int PSP_GE_MATRIX_VIEW = 9; public static final int PSP_GE_MATRIX_PROJECTION = 10; public static final 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.callback_addr != 0) { 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.callback_addr, callback.callback_arg_addr, 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; if (log.isDebugEnabled()) { log.debug(String.format("hleGeListEnQueue optParams=%s", optParams)); } } 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; } } 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 (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; } @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()) { 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.uid); } if (callbackFinish != null) { threadMan.hleKernelDeleteCallback(callbackFinish.uid); } SceUidManager.releaseId(cbid, geCallbackPurpose); return 0; } }
public class LwMutexManager { protected static Logger log = Modules.getLogger("ThreadManForUser"); private HashMap<Integer, SceKernelLwMutexInfo> lwMutexMap; private LwMutexWaitStateChecker lwMutexWaitStateChecker; private static final int PSP_LWMUTEX_ATTR_FIFO = 0; private static final int PSP_LWMUTEX_ATTR_PRIORITY = 0x100; private static final int PSP_LWMUTEX_ATTR_ALLOW_RECURSIVE = 0x200; public void reset() { lwMutexMap = new HashMap<Integer, SceKernelLwMutexInfo>(); lwMutexWaitStateChecker = new LwMutexWaitStateChecker(); } private boolean removeWaitingThread(SceKernelThreadInfo thread) { // Update numWaitThreads SceKernelLwMutexInfo info = lwMutexMap.get(thread.wait.LwMutex_id); if (info != null) { info.numWaitThreads--; if (info.numWaitThreads < 0) { log.warn( "removing waiting thread " + Integer.toHexString(thread.uid) + ", lwmutex " + Integer.toHexString(info.uid) + " numWaitThreads underflowed"); info.numWaitThreads = 0; } return true; } return false; } public void onThreadWaitTimeout(SceKernelThreadInfo thread) { // Untrack if (removeWaitingThread(thread)) { // Return WAIT_TIMEOUT thread.cpuContext.gpr[2] = ERROR_KERNEL_WAIT_TIMEOUT; } else { log.warn("LwMutex deleted while we were waiting for it! (timeout expired)"); // Return WAIT_DELETE thread.cpuContext.gpr[2] = ERROR_KERNEL_WAIT_DELETE; } } public void onThreadWaitReleased(SceKernelThreadInfo thread) { // Untrack if (removeWaitingThread(thread)) { // Return ERROR_WAIT_STATUS_RELEASED thread.cpuContext.gpr[2] = ERROR_KERNEL_WAIT_STATUS_RELEASED; } else { log.warn("EventFlag deleted while we were waiting for it!"); // Return WAIT_DELETE thread.cpuContext.gpr[2] = ERROR_KERNEL_WAIT_DELETE; } } public void onThreadDeleted(SceKernelThreadInfo thread) { if (thread.isWaitingForType(PSP_WAIT_LWMUTEX)) { // decrement numWaitThreads removeWaitingThread(thread); } } private void onLwMutexDeleted(int lwmid) { ThreadManForUser threadMan = Modules.ThreadManForUserModule; boolean reschedule = false; for (Iterator<SceKernelThreadInfo> it = threadMan.iterator(); it.hasNext(); ) { SceKernelThreadInfo thread = it.next(); if (thread.isWaitingForType(PSP_WAIT_LWMUTEX) && thread.wait.LwMutex_id == lwmid) { thread.cpuContext.gpr[2] = ERROR_KERNEL_WAIT_DELETE; threadMan.hleChangeThreadState(thread, PSP_THREAD_READY); reschedule = true; } } // Reschedule only if threads waked up. if (reschedule) { threadMan.hleRescheduleCurrentThread(); } } private void onLwMutexModified(SceKernelLwMutexInfo info) { ThreadManForUser threadMan = Modules.ThreadManForUserModule; boolean reschedule = false; if ((info.attr & PSP_LWMUTEX_ATTR_PRIORITY) == PSP_LWMUTEX_ATTR_FIFO) { for (Iterator<SceKernelThreadInfo> it = Modules.ThreadManForUserModule.iterator(); it.hasNext(); ) { SceKernelThreadInfo thread = it.next(); if (thread.isWaitingForType(PSP_WAIT_LWMUTEX) && thread.wait.LwMutex_id == info.uid && tryLockLwMutex(info, thread.wait.LwMutex_count, thread)) { // New thread is taking control of LwMutex. info.threadid = thread.uid; // Update numWaitThreads info.numWaitThreads--; // Return success or failure thread.cpuContext.gpr[2] = 0; // Wakeup threadMan.hleChangeThreadState(thread, PSP_THREAD_READY); reschedule = true; } } } else if ((info.attr & PSP_LWMUTEX_ATTR_PRIORITY) == PSP_LWMUTEX_ATTR_PRIORITY) { for (Iterator<SceKernelThreadInfo> it = Modules.ThreadManForUserModule.iteratorByPriority(); it.hasNext(); ) { SceKernelThreadInfo thread = it.next(); if (thread.isWaitingForType(PSP_WAIT_LWMUTEX) && thread.wait.LwMutex_id == info.uid && tryLockLwMutex(info, thread.wait.LwMutex_count, thread)) { // New thread is taking control of LwMutex. info.threadid = thread.uid; // Update numWaitThreads info.numWaitThreads--; // Return success or failure thread.cpuContext.gpr[2] = 0; // Wakeup threadMan.hleChangeThreadState(thread, PSP_THREAD_READY); reschedule = true; } } } // Reschedule only if threads waked up. if (reschedule) { Modules.ThreadManForUserModule.hleRescheduleCurrentThread(); } } private boolean tryLockLwMutex(SceKernelLwMutexInfo info, int count, SceKernelThreadInfo thread) { if (info.lockedCount == 0) { // If the lwmutex is not locked, allow this thread to lock it. info.threadid = thread.uid; info.lockedCount += count; return true; } else if (info.threadid == thread.uid) { // If the lwmutex is already locked, but it's trying to be locked by the same thread // that acquired it initially, check if recursive locking is allowed. // If not, return an error. if (((info.attr & PSP_LWMUTEX_ATTR_ALLOW_RECURSIVE) == PSP_LWMUTEX_ATTR_ALLOW_RECURSIVE)) { info.lockedCount += count; return true; } } return false; } private int hleKernelLockLwMutex( int uid, int count, int timeout_addr, boolean wait, boolean doCallbacks) { SceKernelLwMutexInfo info = lwMutexMap.get(uid); if (info == null) { log.warn( String.format( "hleKernelLockLwMutex uid=%d, count=%d, timeout_addr=0x%08X, wait=%b, doCallbacks=%b - - unknown UID", uid, count, timeout_addr, wait, doCallbacks)); return ERROR_KERNEL_LWMUTEX_NOT_FOUND; } ThreadManForUser threadMan = Modules.ThreadManForUserModule; SceKernelThreadInfo currentThread = threadMan.getCurrentThread(); if (!tryLockLwMutex(info, count, currentThread)) { if (log.isDebugEnabled()) { log.debug( String.format( "hleKernelLockLwMutex %s, count=%d, timeout_addr=0x%08X, wait=%b, doCallbacks=%b - fast check failed", info.toString(), count, timeout_addr, wait, doCallbacks)); } if (wait && info.threadid != currentThread.uid) { // Failed, but it's ok, just wait a little info.numWaitThreads++; // Wait on a specific lwmutex currentThread.wait.LwMutex_id = uid; currentThread.wait.LwMutex_count = count; threadMan.hleKernelThreadEnterWaitState( PSP_WAIT_LWMUTEX, uid, lwMutexWaitStateChecker, timeout_addr, doCallbacks); } else { if ((info.attr & PSP_LWMUTEX_ATTR_ALLOW_RECURSIVE) != PSP_LWMUTEX_ATTR_ALLOW_RECURSIVE) { return ERROR_KERNEL_LWMUTEX_RECURSIVE_NOT_ALLOWED; } return ERROR_KERNEL_LWMUTEX_LOCKED; } } else { // Success, do not reschedule the current thread. if (log.isDebugEnabled()) { log.debug( String.format( "hleKernelLockLwMutex %s, count=%d, timeout_addr=0x%08X, wait=%b, doCallbacks=%b - fast check succeeded", info.toString(), count, timeout_addr, wait, doCallbacks)); } } return 0; } public int sceKernelCreateLwMutex( int workAreaAddr, int name_addr, int attr, int count, int option_addr) { Memory mem = Processor.memory; String name = Utilities.readStringNZ(mem, name_addr, 32); if (log.isDebugEnabled()) { log.debug( "sceKernelCreateLwMutex (workAreaAddr='" + Integer.toHexString(workAreaAddr) + "', name='" + name + "', attr=0x" + Integer.toHexString(attr) + ", count=0x" + Integer.toHexString(count) + ", option_addr=0x" + Integer.toHexString(option_addr) + ")"); } SceKernelLwMutexInfo info = new SceKernelLwMutexInfo(workAreaAddr, name, count, attr); lwMutexMap.put(info.uid, info); // If the initial count is 0, the lwmutex is not acquired. if (count > 0) { info.threadid = Modules.ThreadManForUserModule.getCurrentThreadID(); } // Return 0 in case of no error, do not return the UID of the created mutex return 0; } public int sceKernelDeleteLwMutex(int workAreaAddr) { Memory mem = Processor.memory; int uid = mem.read32(workAreaAddr); if (log.isDebugEnabled()) { log.debug("sceKernelDeleteLwMutex (workAreaAddr='" + Integer.toHexString(workAreaAddr) + ")"); } SceKernelLwMutexInfo info = lwMutexMap.remove(uid); if (info == null) { log.warn("sceKernelDeleteLwMutex unknown UID " + Integer.toHexString(uid)); return ERROR_KERNEL_LWMUTEX_NOT_FOUND; } mem.write32(workAreaAddr, 0); // Clear uid. onLwMutexDeleted(uid); return 0; } public int sceKernelLockLwMutex(int workAreaAddr, int count, int timeout_addr) { Memory mem = Processor.memory; int uid = mem.read32(workAreaAddr); if (log.isDebugEnabled()) { log.debug("sceKernelLockLwMutex redirecting to hleKernelLockLwMutex"); } return hleKernelLockLwMutex(uid, count, timeout_addr, true, false); } public int sceKernelLockLwMutexCB(int workAreaAddr, int count, int timeout_addr) { Memory mem = Processor.memory; int uid = mem.read32(workAreaAddr); if (log.isDebugEnabled()) { log.debug("sceKernelLockLwMutexCB redirecting to hleKernelLockLwMutex"); } return hleKernelLockLwMutex(uid, count, timeout_addr, true, true); } public int sceKernelTryLockLwMutex(int workAreaAddr, int count) { Memory mem = Processor.memory; int uid = mem.read32(workAreaAddr); if (log.isDebugEnabled()) { log.debug("sceKernelTryLockLwMutex redirecting to hleKernelLockLwMutex"); } return hleKernelLockLwMutex(uid, count, 0, false, false); } public int sceKernelUnlockLwMutex(int workAreaAddr, int count) { Memory mem = Processor.memory; int uid = mem.read32(workAreaAddr); if (log.isDebugEnabled()) { log.debug( "sceKernelUnlockLwMutex (workAreaAddr=0x" + Integer.toHexString(workAreaAddr) + ", count=" + count + ")"); } SceKernelLwMutexInfo info = lwMutexMap.get(uid); if (info == null) { log.warn("sceKernelUnlockLwMutex unknown uid"); return ERROR_KERNEL_LWMUTEX_NOT_FOUND; } if (info.lockedCount == 0) { log.debug("sceKernelUnlockLwMutex not locked"); return ERROR_KERNEL_LWMUTEX_UNLOCKED; } if (info.lockedCount < 0) { log.warn("sceKernelUnlockLwMutex underflow"); return ERROR_KERNEL_LWMUTEX_UNLOCK_UNDERFLOW; } info.lockedCount -= count; if (info.lockedCount == 0) { onLwMutexModified(info); } return 0; } public int sceKernelReferLwMutexStatus(int workAreaAddr, int addr) { Memory mem = Processor.memory; int uid = mem.read32(workAreaAddr); if (log.isDebugEnabled()) { log.debug( "sceKernelReferLwMutexStatus (workAreaAddr=0x" + Integer.toHexString(workAreaAddr) + ", addr=0x" + addr + ")"); } SceKernelLwMutexInfo info = lwMutexMap.get(uid); if (info == null) { log.warn("sceKernelReferLwMutexStatus unknown UID " + Integer.toHexString(uid)); return ERROR_KERNEL_LWMUTEX_NOT_FOUND; } if (!Memory.isAddressGood(addr)) { log.warn("sceKernelReferLwMutexStatus bad address 0x" + Integer.toHexString(addr)); return -1; } info.write(mem, addr); return 0; } public int sceKernelReferLwMutexStatusByID(int uid, int addr) { Memory mem = Processor.memory; if (log.isDebugEnabled()) { log.debug( "sceKernelReferLwMutexStatus (uid=0x" + Integer.toHexString(uid) + ", addr=0x" + addr + ")"); } SceKernelLwMutexInfo info = lwMutexMap.get(uid); if (info == null) { log.warn("sceKernelReferLwMutexStatus unknown UID " + Integer.toHexString(uid)); return ERROR_KERNEL_LWMUTEX_NOT_FOUND; } if (!Memory.isAddressGood(addr)) { log.warn("sceKernelReferLwMutexStatus bad address 0x" + Integer.toHexString(addr)); return -1; } info.write(mem, addr); return 0; } private class LwMutexWaitStateChecker implements IWaitStateChecker { @Override public boolean continueWaitState(SceKernelThreadInfo thread, ThreadWaitInfo wait) { // Check if the thread has to continue its wait state or if the lwmutex // has been unlocked during the callback execution. SceKernelLwMutexInfo info = lwMutexMap.get(wait.LwMutex_id); if (info == null) { thread.cpuContext.gpr[2] = ERROR_KERNEL_LWMUTEX_NOT_FOUND; return false; } // Check the lwmutex. if (tryLockLwMutex(info, wait.LwMutex_count, thread)) { info.numWaitThreads--; thread.cpuContext.gpr[2] = 0; return false; } return true; } } public static final LwMutexManager singleton = new LwMutexManager(); private LwMutexManager() {} }
/* * TODO list: * 1. Use the partitionid in functions that use it as a parameter. * -> Info: * 1 = kernel, 2 = user, 3 = me, 4 = kernel mirror (from potemkin/dash) * http://forums.ps2dev.org/viewtopic.php?p=75341#75341 * 8 = slim, topaddr = 0x8A000000, size = 0x1C00000 (28 MB), attr = 0x0C * 8 = slim, topaddr = 0x8BC00000, size = 0x400000 (4 MB), attr = 0x0C * * 2. Implement format string parsing and reading variable number of parameters * in sceKernelPrintf. */ public class SysMemUserForUser extends HLEModule { public static Logger log = Modules.getLogger("SysMemUserForUser"); protected static Logger stdout = Logger.getLogger("stdout"); protected static HashMap<Integer, SysMemInfo> blockList; protected static MemoryChunkList[] freeMemoryChunks; protected int firmwareVersion = 150; public static final int defaultSizeAlignment = 256; // PspSysMemBlockTypes public static final int PSP_SMEM_Low = 0; public static final int PSP_SMEM_High = 1; public static final int PSP_SMEM_Addr = 2; public static final int PSP_SMEM_LowAligned = 3; public static final int PSP_SMEM_HighAligned = 4; public static final int KERNEL_PARTITION_ID = 1; public static final int USER_PARTITION_ID = 2; protected boolean started = false; private int compiledSdkVersion; protected int compilerVersion; @Override public void load() { reset(); super.load(); } @Override public void start() { if (!started) { reset(); started = true; } compiledSdkVersion = 0; super.start(); } @Override public void stop() { started = false; super.stop(); } private MemoryChunkList createMemoryChunkList(int startAddr, int endAddr) { startAddr &= Memory.addressMask; endAddr &= Memory.addressMask; MemoryChunk initialMemory = new MemoryChunk(startAddr, endAddr - startAddr + 1); return new MemoryChunkList(initialMemory); } public void reset() { blockList = new HashMap<Integer, SysMemInfo>(); // free memory chunks for each partition freeMemoryChunks = new MemoryChunkList[3]; freeMemoryChunks[USER_PARTITION_ID] = createMemoryChunkList(MemoryMap.START_USERSPACE, MemoryMap.END_USERSPACE); freeMemoryChunks[KERNEL_PARTITION_ID] = createMemoryChunkList(MemoryMap.START_KERNEL, MemoryMap.END_KERNEL); } public void setMemory64MB(boolean isMemory64MB) { if (isMemory64MB) { setMemorySize(MemoryMap.END_RAM_64MB - MemoryMap.START_RAM + 1); // 60 MB } else { setMemorySize(MemoryMap.END_RAM_32MB - MemoryMap.START_RAM + 1); // 32 MB } } public void setMemorySize(int memorySize) { if (MemoryMap.SIZE_RAM != memorySize) { int previousMemorySize = MemoryMap.SIZE_RAM; MemoryMap.END_RAM = MemoryMap.START_RAM + memorySize - 1; MemoryMap.END_USERSPACE = MemoryMap.END_RAM; MemoryMap.SIZE_RAM = MemoryMap.END_RAM - MemoryMap.START_RAM + 1; if (!Memory.getInstance().allocate()) { log.error( String.format( "Failed to resize the PSP memory from 0x%X to 0x%X", previousMemorySize, memorySize)); Emulator.PauseEmuWithStatus(Emulator.EMU_STATUS_MEM_ANY); } reset(); } } public static class SysMemInfo implements Comparable<SysMemInfo> { public final int uid; public final int partitionid; public final String name; public final int type; public final int size; public final int allocatedSize; public final int addr; public SysMemInfo( int partitionid, String name, int type, int size, int allocatedSize, int addr) { this.partitionid = partitionid; this.name = name; this.type = type; this.size = size; this.allocatedSize = allocatedSize; this.addr = addr; uid = SceUidManager.getNewUid("SysMem"); blockList.put(uid, this); } @Override public String toString() { return String.format( "SysMemInfo[addr=0x%08X-0x%08X, uid=%x, partition=%d, name='%s', type=%s, size=0x%X (allocated=0x%X)]", addr, addr + allocatedSize, uid, partitionid, name, getTypeName(type), size, allocatedSize); } public void free() { blockList.remove(uid); } @Override public int compareTo(SysMemInfo o) { if (addr == o.addr) { log.warn("Set invariant broken for SysMemInfo " + this); return 0; } return addr < o.addr ? -1 : 1; } } protected static String getTypeName(int type) { String typeName; switch (type) { case PSP_SMEM_Low: typeName = "PSP_SMEM_Low"; break; case PSP_SMEM_High: typeName = "PSP_SMEM_High"; break; case PSP_SMEM_Addr: typeName = "PSP_SMEM_Addr"; break; case PSP_SMEM_LowAligned: typeName = "PSP_SMEM_LowAligned"; break; case PSP_SMEM_HighAligned: typeName = "PSP_SMEM_HighAligned"; break; default: typeName = "UNHANDLED " + type; break; } return typeName; } // Allocates to 256-byte alignment public SysMemInfo malloc(int partitionid, String name, int type, int size, int addr) { int allocatedAddress = 0; int allocatedSize = 0; if (partitionid >= 0 && partitionid < freeMemoryChunks.length && freeMemoryChunks[partitionid] != null) { MemoryChunkList freeMemoryChunk = freeMemoryChunks[partitionid]; int alignment = defaultSizeAlignment - 1; // The allocated size has not to be aligned to the requested alignment // (for PSP_SMEM_LowAligned or PSP_SMEM_HighAligned), // it is only aligned to the default size alignment. allocatedSize = Utilities.alignUp(size, alignment); if (type == PSP_SMEM_LowAligned || type == PSP_SMEM_HighAligned) { // Use the alignment provided in the addr parameter alignment = addr - 1; } switch (type) { case PSP_SMEM_Low: case PSP_SMEM_LowAligned: allocatedAddress = freeMemoryChunk.allocLow(allocatedSize, alignment); break; case PSP_SMEM_High: case PSP_SMEM_HighAligned: allocatedAddress = freeMemoryChunk.allocHigh(allocatedSize, alignment); break; case PSP_SMEM_Addr: allocatedAddress = freeMemoryChunk.alloc(addr, allocatedSize); break; default: log.warn(String.format("malloc: unknown type %s", getTypeName(type))); } } SysMemInfo sysMemInfo; if (allocatedAddress == 0) { log.warn( String.format( "malloc cannot allocate partition=%d, name='%s', type=%s, size=0x%X, addr=0x%08X, maxFreeMem=0x%X, totalFreeMem=0x%X", partitionid, name, getTypeName(type), size, addr, maxFreeMemSize(), totalFreeMemSize())); if (log.isTraceEnabled()) { log.trace("Free list: " + getDebugFreeMem()); log.trace("Allocated blocks:\n" + getDebugAllocatedMem() + "\n"); } sysMemInfo = null; } else { sysMemInfo = new SysMemInfo(partitionid, name, type, size, allocatedSize, allocatedAddress); if (log.isDebugEnabled()) { log.debug( String.format( "malloc partition=%d, name='%s', type=%s, size=0x%X, addr=0x%08X: returns 0x%08X", partitionid, name, getTypeName(type), size, addr, allocatedAddress)); if (log.isTraceEnabled()) { log.trace("Free list after malloc: " + getDebugFreeMem()); log.trace("Allocated blocks after malloc:\n" + getDebugAllocatedMem() + "\n"); } } } return sysMemInfo; } public String getDebugFreeMem() { return freeMemoryChunks[USER_PARTITION_ID].toString(); } public String getDebugAllocatedMem() { StringBuilder result = new StringBuilder(); // Sort allocated blocks by address List<SysMemInfo> sortedBlockList = Collections.list(Collections.enumeration(blockList.values())); Collections.sort(sortedBlockList); for (SysMemInfo sysMemInfo : sortedBlockList) { if (result.length() > 0) { result.append("\n"); } result.append(sysMemInfo.toString()); } return result.toString(); } public void free(SysMemInfo info) { if (info != null) { info.free(); MemoryChunk memoryChunk = new MemoryChunk(info.addr, info.allocatedSize); freeMemoryChunks[info.partitionid].add(memoryChunk); if (log.isDebugEnabled()) { log.debug(String.format("free %s", info.toString())); if (log.isTraceEnabled()) { log.trace("Free list after free: " + getDebugFreeMem()); log.trace("Allocated blocks after free:\n" + getDebugAllocatedMem() + "\n"); } } } } public int maxFreeMemSize() { int maxFreeMemSize = 0; for (MemoryChunk memoryChunk = freeMemoryChunks[USER_PARTITION_ID].getLowMemoryChunk(); memoryChunk != null; memoryChunk = memoryChunk.next) { if (memoryChunk.size > maxFreeMemSize) { maxFreeMemSize = memoryChunk.size; } } return maxFreeMemSize; } public int totalFreeMemSize() { int totalFreeMemSize = 0; for (MemoryChunk memoryChunk = freeMemoryChunks[USER_PARTITION_ID].getLowMemoryChunk(); memoryChunk != null; memoryChunk = memoryChunk.next) { totalFreeMemSize += memoryChunk.size; } return totalFreeMemSize; } public SysMemInfo getSysMemInfo(int uid) { return blockList.get(uid); } /** * @param firmwareVersion : in this format: ABB, where A = major and B = minor, for example 271 */ public void setFirmwareVersion(int firmwareVersion) { this.firmwareVersion = firmwareVersion; } public int getFirmwareVersion() { return firmwareVersion; } // note: we're only looking at user memory, so 0x08800000 - 0x0A000000 // this is mainly to make it fit on one console line public void dumpSysMemInfo() { final int MEMORY_SIZE = 0x1800000; final int SLOT_COUNT = 64; // 0x60000 final int SLOT_SIZE = MEMORY_SIZE / SLOT_COUNT; // 0x60000 boolean[] allocated = new boolean[SLOT_COUNT]; boolean[] fragmented = new boolean[SLOT_COUNT]; int allocatedSize = 0; int fragmentedSize = 0; for (Iterator<SysMemInfo> it = blockList.values().iterator(); it.hasNext(); ) { SysMemInfo info = it.next(); for (int i = info.addr; i < info.addr + info.size; i += SLOT_SIZE) { if (i >= 0x08800000 && i < 0x0A000000) { allocated[(i - 0x08800000) / SLOT_SIZE] = true; } } allocatedSize += info.size; } for (MemoryChunk memoryChunk = freeMemoryChunks[USER_PARTITION_ID].getLowMemoryChunk(); memoryChunk != null; memoryChunk = memoryChunk.next) { for (int i = memoryChunk.addr; i < memoryChunk.addr + memoryChunk.size; i += SLOT_SIZE) { if (i >= 0x08800000 && i < 0x0A000000) { fragmented[(i - 0x08800000) / SLOT_SIZE] = true; } } fragmentedSize += memoryChunk.size; } StringBuilder allocatedDiagram = new StringBuilder(); allocatedDiagram.append("["); for (int i = 0; i < SLOT_COUNT; i++) { allocatedDiagram.append(allocated[i] ? "X" : " "); } allocatedDiagram.append("]"); StringBuilder fragmentedDiagram = new StringBuilder(); fragmentedDiagram.append("["); for (int i = 0; i < SLOT_COUNT; i++) { fragmentedDiagram.append(fragmented[i] ? "X" : " "); } fragmentedDiagram.append("]"); DumpDebugState.log("++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++"); DumpDebugState.log( String.format("Allocated memory: %08X %d bytes", allocatedSize, allocatedSize)); DumpDebugState.log(allocatedDiagram.toString()); DumpDebugState.log( String.format("Fragmented memory: %08X %d bytes", fragmentedSize, fragmentedSize)); DumpDebugState.log(fragmentedDiagram.toString()); DumpDebugState.log("Free list: " + getDebugFreeMem()); DumpDebugState.log("Allocated blocks:\n" + getDebugAllocatedMem() + "\n"); } public int hleKernelPrintf( CpuState cpu, PspString formatString, Logger logger, String sceFunctionName) { // Format and print the message to stdout if (logger.isInfoEnabled()) { String formattedMsg = formatString.getString(); try { // For now, use only the 7 register parameters: $a1-$a3, $t0-$t3 // Further parameters should be retrieved from the stack. Object[] formatParameters = new Object[] {cpu._a1, cpu._a2, cpu._a3, cpu._t0, cpu._t1, cpu._t2, cpu._t3}; // Translate the C-like format string to a Java format string: // - %u or %i -> %d // - %4u -> %4d // - %lld or %ld -> %d // - %p -> %08X String javaMsg = formatString.getString(); javaMsg = javaMsg.replaceAll("\\%(\\d*)l?l?[uid]", "%$1d"); javaMsg = javaMsg.replaceAll("\\%p", "%08X"); // Support for "%s" (at any place and can occur multiple times) int index = -1; for (int parameterIndex = 0; parameterIndex < formatParameters.length; parameterIndex++) { index = javaMsg.indexOf('%', index + 1); if (index < 0) { break; } String parameterFormat = javaMsg.substring(index); if (parameterFormat.startsWith("%s")) { // Convert an integer address to a String by reading // the String at the given address formatParameters[parameterIndex] = Utilities.readStringZ(((Integer) formatParameters[parameterIndex]).intValue()); } } // String.format: If there are more arguments than format specifiers, the extra arguments // are ignored. formattedMsg = String.format(javaMsg, formatParameters); } catch (Exception e) { // Ignore formatting exception } logger.info(formattedMsg); } return 0; } public int hleKernelGetCompiledSdkVersion() { return compiledSdkVersion; } protected void hleSetCompiledSdkVersion(int sdkVersion) { compiledSdkVersion = sdkVersion; } @HLEFunction(nid = 0xA291F107, version = 150) public int sceKernelMaxFreeMemSize() { int maxFreeMemSize = maxFreeMemSize(); // Some games expect size to be rounded down in 16 bytes block maxFreeMemSize &= ~15; if (log.isDebugEnabled()) { log.debug(String.format("sceKernelMaxFreeMemSize returning %d(hex=0x%1$X)", maxFreeMemSize)); } return maxFreeMemSize; } @HLEFunction(nid = 0xF919F628, version = 150) public int sceKernelTotalFreeMemSize() { int totalFreeMemSize = totalFreeMemSize(); if (log.isDebugEnabled()) { log.debug( String.format("sceKernelTotalFreeMemSize returning %d(hex=0x%1$X)", totalFreeMemSize)); } return totalFreeMemSize; } @HLEFunction(nid = 0x237DBD4F, version = 150) public int sceKernelAllocPartitionMemory( int partitionid, String name, int type, int size, int addr) { addr &= Memory.addressMask; if (type < PSP_SMEM_Low || type > PSP_SMEM_HighAligned) { return SceKernelErrors.ERROR_KERNEL_ILLEGAL_MEMBLOCK_ALLOC_TYPE; } SysMemInfo info = malloc(partitionid, name, type, size, addr); if (info == null) { return SceKernelErrors.ERROR_KERNEL_FAILED_ALLOC_MEMBLOCK; } return info.uid; } @HLEFunction(nid = 0xB6D61D02, version = 150) public int sceKernelFreePartitionMemory(int uid) { SceUidManager.checkUidPurpose(uid, "SysMem", true); SysMemInfo info = blockList.remove(uid); if (info == null) { log.warn(String.format("sceKernelFreePartitionMemory unknown uid=0x%X", uid)); return SceKernelErrors.ERROR_KERNEL_ILLEGAL_CHUNK_ID; } free(info); return 0; } @HLEFunction(nid = 0x9D9A5BA1, version = 150) public int sceKernelGetBlockHeadAddr(int uid) { SceUidManager.checkUidPurpose(uid, "SysMem", true); SysMemInfo info = blockList.get(uid); if (info == null) { log.warn(String.format("sceKernelGetBlockHeadAddr unknown uid=0x%X", uid)); return SceKernelErrors.ERROR_KERNEL_ILLEGAL_CHUNK_ID; } return info.addr; } @HLEFunction(nid = 0x13A5ABEF, version = 150) public int sceKernelPrintf(CpuState cpu, PspString formatString) { return hleKernelPrintf(cpu, formatString, stdout, "sceKernelPrintf"); } @HLEFunction(nid = 0x3FC9AE6A, version = 150) public int sceKernelDevkitVersion() { int major = firmwareVersion / 100; int minor = (firmwareVersion / 10) % 10; int revision = firmwareVersion % 10; int devkitVersion = (major << 24) | (minor << 16) | (revision << 8) | 0x10; if (log.isDebugEnabled()) { log.debug(String.format("sceKernelDevkitVersion returning 0x%08X", devkitVersion)); } return devkitVersion; } @HLEFunction(nid = 0xD8DE5C1E, version = 150) public int SysMemUserForUser_D8DE5C1E() { // Seems to always return 0... return 0; } @HLEFunction(nid = 0xFC114573, version = 200) public int sceKernelGetCompiledSdkVersion() { return compiledSdkVersion; } @HLEFunction(nid = 0x7591C7DB, version = 200) public int sceKernelSetCompiledSdkVersion(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0xF77D77CB, version = 200) public int sceKernelSetCompilerVersion(int compilerVersion) { this.compilerVersion = compilerVersion; return 0; } @HLEUnimplemented @HLEFunction(nid = 0xA6848DF8, version = 200) public int SysMemUserForUser_A6848DF8() { return 0; } @HLEUnimplemented @HLEFunction(nid = 0x2A3E5280, version = 280) public int sceKernelQueryMemoryInfo() { return 0; } @HLEUnimplemented @HLEFunction(nid = 0x39F49610, version = 280) public int sceKernelGetPTRIG() { return 0; } @HLEUnimplemented @HLEFunction(nid = 0x6231A71D, version = 280) public int sceKernelSetPTRIG() { return 0; } // sceKernelFreeMemoryBlock (internal name) @HLEFunction(nid = 0x50F61D8A, version = 352) public int SysMemUserForUser_50F61D8A(int uid) { SysMemInfo info = blockList.remove(uid); if (info == null) { log.warn("SysMemUserForUser_50F61D8A(uid=0x" + Integer.toHexString(uid) + ") unknown uid"); return SceKernelErrors.ERROR_KERNEL_UNKNOWN_UID; } free(info); return 0; } @HLEUnimplemented @HLEFunction(nid = 0xACBD88CA, version = 352) public int SysMemUserForUser_ACBD88CA() { return 0; } // sceKernelGetMemoryBlockAddr (internal name) @HLEFunction(nid = 0xDB83A952, version = 352) public int SysMemUserForUser_DB83A952(int uid, TPointer32 addr) { SysMemInfo info = blockList.get(uid); if (info == null) { log.warn( String.format("SysMemUserForUser_DB83A952 uid=0x%X, addr=%s: unknown uid", uid, addr)); return SceKernelErrors.ERROR_KERNEL_UNKNOWN_UID; } addr.setValue(info.addr); return 0; } // sceKernelAllocMemoryBlock (internal name) @HLEFunction(nid = 0xFE707FDF, version = 352) public int SysMemUserForUser_FE707FDF( @StringInfo(maxLength = 32) PspString name, int type, int size, @CanBeNull TPointer paramsAddr) { if (paramsAddr.isNotNull()) { int length = paramsAddr.getValue32(); if (length != 4) { log.warn( String.format("SysMemUserForUser_FE707FDF: unknown parameters with length=%d", length)); } } if (type < PSP_SMEM_Low || type > PSP_SMEM_High) { return SceKernelErrors.ERROR_KERNEL_ILLEGAL_MEMBLOCK_ALLOC_TYPE; } // Always allocate memory in user area (partitionid == 2). SysMemInfo info = malloc(SysMemUserForUser.USER_PARTITION_ID, name.getString(), type, size, 0); if (info == null) { return SceKernelErrors.ERROR_KERNEL_FAILED_ALLOC_MEMBLOCK; } return info.uid; } @HLEFunction(nid = 0x342061E5, version = 370) public int sceKernelSetCompiledSdkVersion370(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0x315AD3A0, version = 380) public int sceKernelSetCompiledSdkVersion380_390(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0xEBD5C3E6, version = 395) public int sceKernelSetCompiledSdkVersion395(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0x91DE343C, version = 500) public int sceKernelSetCompiledSdkVersion500_505(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0x7893F79A, version = 507) public int sceKernelSetCompiledSdkVersion507(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0x35669D4C, version = 600) public int sceKernelSetCompiledSdkVersion600_602(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0x1B4217BC, version = 603) public int sceKernelSetCompiledSdkVersion603_605(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } @HLEFunction(nid = 0x358CA1BB, version = 606) public int sceKernelSetCompiledSdkVersion606(int sdkVersion) { hleSetCompiledSdkVersion(sdkVersion); return 0; } }