/* 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.kernel.types; import jpcsp.HLE.Modules; import jpcsp.HLE.kernel.managers.FplManager; import jpcsp.HLE.kernel.managers.SceUidManager; import jpcsp.HLE.kernel.managers.ThreadWaitingList; import jpcsp.HLE.modules.SysMemUserForUser.SysMemInfo; import jpcsp.util.Utilities; public class SceKernelFplInfo extends pspAbstractMemoryMappedStructureVariableLength { // PSP info public final String name; public final int attr; public final int blockSize; public final int numBlocks; public int freeBlocks; public final ThreadWaitingList threadWaitingList; private final SysMemInfo sysMemInfo; // Internal info public final int uid; public final int partitionid; public int[] blockAddress; public boolean[] blockAllocated; /** do not instantiate unless there is enough free mem. * use the static helper function tryCreateFpl. */ private SceKernelFplInfo(String name, int partitionid, int attr, int blockSize, int numBlocks, int memType, int memAlign) { this.name = name; this.attr = attr; this.blockSize = blockSize; this.numBlocks = numBlocks; freeBlocks = numBlocks; uid = SceUidManager.getNewUid("ThreadMan-Fpl"); this.partitionid = partitionid; blockAddress = new int[numBlocks]; blockAllocated = new boolean[numBlocks]; for (int i = 0; i < numBlocks; i++) { blockAllocated[i] = false; } // Reserve psp memory int alignedBlockSize = memAlign == 0 ? blockSize : Utilities.alignUp(blockSize, memAlign - 1); int totalFplSize = alignedBlockSize * numBlocks; sysMemInfo = Modules.SysMemUserForUserModule.malloc(partitionid, String.format("ThreadMan-Fpl-0x%x-%s", uid, name), memType, totalFplSize, 0); if (sysMemInfo == null) { throw new RuntimeException("SceKernelFplInfo: not enough free mem"); } // Initialise the block addresses for (int i = 0; i < numBlocks; i++) { blockAddress[i] = sysMemInfo.addr + alignedBlockSize * i; } threadWaitingList = ThreadWaitingList.createThreadWaitingList(SceKernelThreadInfo.PSP_WAIT_FPL, uid, attr, FplManager.PSP_FPL_ATTR_PRIORITY); } public static SceKernelFplInfo tryCreateFpl(String name, int partitionid, int attr, int blockSize, int numBlocks, int memType, int memAlign) { SceKernelFplInfo info = null; int alignedBlockSize = memAlign == 0 ? blockSize : Utilities.alignUp(blockSize, memAlign - 1); int totalFplSize = alignedBlockSize * numBlocks; int maxFreeSize = Modules.SysMemUserForUserModule.maxFreeMemSize(partitionid); if (totalFplSize <= maxFreeSize) { info = new SceKernelFplInfo(name, partitionid, attr, blockSize, numBlocks, memType, memAlign); } else { Modules.log.warn("tryCreateFpl not enough free mem (want=" + totalFplSize + ", free=" + maxFreeSize + ", diff=" + (totalFplSize - maxFreeSize) + ")"); } return info; } @Override protected void write() { super.write(); writeStringNZ(32, name); write32(attr); write32(blockSize); write32(numBlocks); write32(freeBlocks); write32(getNumWaitThreads()); } public boolean isBlockAllocated(int blockId) { return blockAllocated[blockId]; } public void freeBlock(int blockId) { if (!isBlockAllocated(blockId)) { throw new IllegalArgumentException("Block " + blockId + " is not allocated"); } blockAllocated[blockId] = false; freeBlocks++; } /** @return the address of the allocated block */ public int allocateBlock(int blockId) { if (isBlockAllocated(blockId)) { throw new IllegalArgumentException("Block " + blockId + " is already allocated"); } blockAllocated[blockId] = true; freeBlocks--; return blockAddress[blockId]; } /** @return the block index or -1 on failure */ public int findFreeBlock() { for (int i = 0; i < numBlocks; i++) { if (!isBlockAllocated(i)) { return i; } } return -1; } /** @return the block index or -1 on failure */ public int findBlockByAddress(int addr) { for (int i = 0; i < numBlocks; i++) { if (blockAddress[i] == addr) { return i; } } return -1; } public void deleteSysMemInfo() { Modules.SysMemUserForUserModule.free(sysMemInfo); } public int getNumWaitThreads() { return threadWaitingList.getNumWaitingThreads(); } @Override public String toString() { return String.format("SceKernelFplInfo[uid=0x%X, name='%s', attr=0x%X, blockSize=0x%X, numBlocks=0x%X, freeBlocks=0x%X, numWaitThreads=%d]", uid, name, attr, blockSize, numBlocks, freeBlocks, getNumWaitThreads()); } }