/* 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.Allegrex.compiler.nativeCode.graphics; import org.apache.log4j.Logger; import jpcsp.Memory; import jpcsp.Allegrex.compiler.nativeCode.AbstractNativeCodeSequence; import jpcsp.HLE.Modules; import jpcsp.HLE.kernel.types.PspGeList; import jpcsp.graphics.AsyncVertexCache; import jpcsp.graphics.GeCommands; import jpcsp.graphics.VideoEngine; import jpcsp.graphics.RE.IRenderingEngine; import jpcsp.memory.IMemoryReader; import jpcsp.memory.IMemoryWriter; import jpcsp.memory.MemoryReader; import jpcsp.memory.MemoryWriter; import jpcsp.util.Utilities; /** * @author gid15 * */ public class sceGu extends AbstractNativeCodeSequence { protected static Logger log = VideoEngine.log; private static final boolean writeTFLUSH = false; private static final boolean writeTSYNC = false; private static final boolean writeDUMMY = false; static private void sceGeListUpdateStallAddr(int addr) { // Simplification: we can update the stall address only if the VideoEngine // is processing one and only one GE list. VideoEngine videoEngine = VideoEngine.getInstance(); if (videoEngine.numberDrawLists() == 0) { PspGeList geList = videoEngine.getCurrentList(); if (geList != null && geList.getStallAddr() != 0) { addr &= Memory.addressMask; geList.setStallAddr(addr); } } } static public void sceGuDrawArray(int contextAddr1, int contextAddr2, int listCurrentOffset, int updateStall) { Memory mem = getMemory(); int prim = getGprA0(); int vtype = getGprA1(); int count = getGprA2(); int indices = getGprA3(); int vertices = getGprT0(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; if (vtype != 0) { cmd = (GeCommands.VTYPE << 24) | (vtype & 0x00FFFFFF); mem.write32(listCurrent, cmd); listCurrent += 4; } if (indices != 0) { cmd = (GeCommands.BASE << 24) | ((indices >> 8) & 0x000F0000); mem.write32(listCurrent, cmd); listCurrent += 4; cmd = (GeCommands.IADDR << 24) | (indices & 0x00FFFFFF); mem.write32(listCurrent, cmd); listCurrent += 4; } if (vertices != 0) { cmd = (GeCommands.BASE << 24) | ((vertices >> 8) & 0x000F0000); mem.write32(listCurrent, cmd); listCurrent += 4; cmd = (GeCommands.VADDR << 24) | (vertices & 0x00FFFFFF); mem.write32(listCurrent, cmd); listCurrent += 4; } cmd = (GeCommands.PRIM << 24) | ((prim & 0x7) << 16) | count; mem.write32(listCurrent, cmd); listCurrent += 4; mem.write32(context + listCurrentOffset, listCurrent); if (updateStall != 0) { sceGeListUpdateStallAddr(listCurrent); } if (VideoEngine.getInstance().useAsyncVertexCache()) { AsyncVertexCache.getInstance().addAsyncCheck(prim, vtype, count, indices, vertices); } } static public void sceGuDrawArrayN(int contextAddr1, int contextAddr2, int listCurrentOffset, int updateStall) { Memory mem = getMemory(); int prim = getGprA0(); int vtype = getGprA1(); int count = getGprA2(); int n = getGprA3(); int indices = getGprT0(); int vertices = getGprT1(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; IMemoryWriter listWriter = MemoryWriter.getMemoryWriter(listCurrent, (5 + n) << 2, 4); if (vtype != 0) { cmd = (GeCommands.VTYPE << 24) | (vtype & 0x00FFFFFF); listWriter.writeNext(cmd); listCurrent += 4; } if (indices != 0) { cmd = (GeCommands.BASE << 24) | ((indices >> 8) & 0x000F0000); listWriter.writeNext(cmd); listCurrent += 4; cmd = (GeCommands.IADDR << 24) | (indices & 0x00FFFFFF); listWriter.writeNext(cmd); listCurrent += 4; } if (vertices != 0) { cmd = (GeCommands.BASE << 24) | ((vertices >> 8) & 0x000F0000); listWriter.writeNext(cmd); listCurrent += 4; cmd = (GeCommands.VADDR << 24) | (vertices & 0x00FFFFFF); listWriter.writeNext(cmd); listCurrent += 4; } if (n > 0) { cmd = (GeCommands.PRIM << 24) | ((prim & 0x7) << 16) | count; for (int i = 0; i < n; i++) { listWriter.writeNext(cmd); } listCurrent += (n << 2); } mem.write32(context + listCurrentOffset, listCurrent); if (updateStall != 0) { sceGeListUpdateStallAddr(listCurrent); } } static public void sceGuDrawSpline(int contextAddr1, int contextAddr2, int listCurrentOffset, int updateStall) { Memory mem = getMemory(); int vtype = getGprA0(); int ucount = getGprA1(); int vcount = getGprA2(); int uedge = getGprA3(); int vedge = getGprT0(); int indices = getGprT1(); int vertices = getGprT2(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; if (vtype != 0) { cmd = (GeCommands.VTYPE << 24) | (vtype & 0x00FFFFFF); mem.write32(listCurrent, cmd); listCurrent += 4; } if (indices != 0) { cmd = (GeCommands.BASE << 24) | ((indices >> 8) & 0x000F0000); mem.write32(listCurrent, cmd); listCurrent += 4; cmd = (GeCommands.IADDR << 24) | (indices & 0x00FFFFFF); mem.write32(listCurrent, cmd); listCurrent += 4; } if (vertices != 0) { cmd = (GeCommands.BASE << 24) | ((vertices >> 8) & 0x000F0000); mem.write32(listCurrent, cmd); listCurrent += 4; cmd = (GeCommands.VADDR << 24) | (vertices & 0x00FFFFFF); mem.write32(listCurrent, cmd); listCurrent += 4; } cmd = (GeCommands.SPLINE << 24) | (vedge << 18) | (uedge << 16) | (vcount << 8) | ucount; mem.write32(listCurrent, cmd); listCurrent += 4; mem.write32(context + listCurrentOffset, listCurrent); if (updateStall != 0) { sceGeListUpdateStallAddr(listCurrent); } } static public void sceGuCopyImage(int contextAddr1, int contextAddr2, int listCurrentOffset) { Memory mem = getMemory(); int psm = getGprA0(); int sx = getGprA1(); int sy = getGprA2(); int width = getGprA3(); int height = getGprT0(); int srcw = getGprT1(); int src = getGprT2(); int dx = getGprT3(); int dy = getStackParam0(); int destw = getStackParam1(); int dest = getStackParam2(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; IMemoryWriter listWriter = MemoryWriter.getMemoryWriter(listCurrent, 32, 4); cmd = (GeCommands.TRXSBP << 24) | (src & 0x00FFFFFF); listWriter.writeNext(cmd); cmd = (GeCommands.TRXSBW << 24) | ((src >> 8) & 0x000F0000) | srcw; listWriter.writeNext(cmd); cmd = (GeCommands.TRXPOS << 24) | (sy << 10) | sx; listWriter.writeNext(cmd); cmd = (GeCommands.TRXDBP << 24) | (dest & 0x00FFFFFF); listWriter.writeNext(cmd); cmd = (GeCommands.TRXDBW << 24) | ((dest >> 8) & 0x000F0000) | destw; listWriter.writeNext(cmd); cmd = (GeCommands.TRXDPOS << 24) | (dy << 10) | dx; listWriter.writeNext(cmd); cmd = (GeCommands.TRXSIZE << 24) | ((height - 1) << 10) | (width - 1); listWriter.writeNext(cmd); cmd = (GeCommands.TRXKICK << 24) | (IRenderingEngine.sizeOfTextureType[psm] == 4 ? GeCommands.TRXKICK_32BIT_TEXEL_SIZE : GeCommands.TRXKICK_16BIT_TEXEL_SIZE); listWriter.writeNext(cmd); listWriter.flush(); mem.write32(context + listCurrentOffset, listCurrent + 32); } static public void sceGuTexImage(int contextAddr1, int contextAddr2, int listCurrentOffset) { Memory mem = getMemory(); int mipmap = getGprA0(); int width = getGprA1(); int height = getGprA2(); int tbw = getGprA3(); int tbp = getGprT0(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; cmd = ((GeCommands.TBP0 + mipmap) << 24) | (tbp & 0x00FFFFFF); mem.write32(listCurrent, cmd); listCurrent += 4; cmd = ((GeCommands.TBW0 + mipmap) << 24) | ((tbp >> 8) & 0x000F0000) | tbw; mem.write32(listCurrent, cmd); listCurrent += 4; // widthExp = 31 - CLZ(width) int widthExp = 31 - Integer.numberOfLeadingZeros(width); int heightExp = 31 - Integer.numberOfLeadingZeros(height); cmd = ((GeCommands.TSIZE0 + mipmap) << 24) | (heightExp << 8) | widthExp; mem.write32(listCurrent, cmd); listCurrent += 4; if (writeTFLUSH) { cmd = (GeCommands.TFLUSH << 24); mem.write32(listCurrent, cmd); listCurrent += 4; } mem.write32(context + listCurrentOffset, listCurrent); } static public void sceGuTexSync(int contextAddr1, int contextAddr2, int listCurrentOffset) { if (writeTSYNC) { Memory mem = getMemory(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; cmd = (GeCommands.TSYNC << 24); mem.write32(listCurrent, cmd); listCurrent += 4; mem.write32(context + listCurrentOffset, listCurrent); } } static public void sceGuTexMapMode(int contextAddr1, int contextAddr2, int listCurrentOffset, int texProjMapOffset, int texMapModeOffset) { Memory mem = getMemory(); int texMapMode = getGprA0() & 0x3; int texShadeU = getGprA1(); int texShadeV = getGprA2(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; int texProjMap = mem.read32(context + texProjMapOffset); mem.write32(context + texMapModeOffset, texMapMode); cmd = (GeCommands.TMAP << 24) | (texProjMap << 8) | texMapMode; mem.write32(listCurrent, cmd); listCurrent += 4; cmd = (GeCommands.TEXTURE_ENV_MAP_MATRIX << 24) | (texShadeV << 8) | texShadeU; mem.write32(listCurrent, cmd); listCurrent += 4; mem.write32(context + listCurrentOffset, listCurrent); } static public void sceGuTexProjMapMode(int contextAddr1, int contextAddr2, int listCurrentOffset, int texProjMapOffset, int texMapModeOffset) { Memory mem = getMemory(); int texProjMap = getGprA0() & 0x3; int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; int texMapMode = mem.read32(context + texMapModeOffset); mem.write32(context + texProjMapOffset, texProjMap); cmd = (GeCommands.TMAP << 24) | (texProjMap << 8) | texMapMode; mem.write32(listCurrent, cmd); listCurrent += 4; mem.write32(context + listCurrentOffset, listCurrent); } static public void sceGuTexLevelMode(int contextAddr1, int contextAddr2, int listCurrentOffset) { Memory mem = getMemory(); int mode = getGprA0(); float bias = getFprF12(); int context = mem.read32(getRelocatedAddress(contextAddr1, contextAddr2)); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; int offset = (int) (bias * 16.f); if (offset > 127) { offset = 127; } else if (offset < -128) { offset = -128; } cmd = (GeCommands.TBIAS << 24) | (offset << 16) | mode; mem.write32(listCurrent, cmd); listCurrent += 4; mem.write32(context + listCurrentOffset, listCurrent); } static public void sceGuMaterial(int contextAddr1, int contextAddr2, int listCurrentOffset) { int mode = getGprA0(); int color = getGprA1(); int context = getMemory().read32(getRelocatedAddress(contextAddr1, contextAddr2)); sceGuMaterial(context, listCurrentOffset, mode, color); } static public void sceGuMaterial(int listCurrentOffset) { int context = getGprA0(); int mode = getGprA1(); int color = getGprA2(); sceGuMaterial(context, listCurrentOffset, mode, color); } static private void sceGuMaterial(int context, int listCurrentOffset, int mode, int color) { Memory mem = getMemory(); int listCurrent = mem.read32(context + listCurrentOffset); int cmd; int rgb = color & 0x00FFFFFF; if ((mode & 0x01) != 0) { cmd = (GeCommands.AMC << 24) | rgb; mem.write32(listCurrent, cmd); listCurrent += 4; cmd = (GeCommands.AMA << 24) | (color >>> 24); mem.write32(listCurrent, cmd); listCurrent += 4; } if ((mode & 0x02) != 0) { cmd = (GeCommands.DMC << 24) | rgb; mem.write32(listCurrent, cmd); listCurrent += 4; } if ((mode & 0x04) != 0) { cmd = (GeCommands.SMC << 24) | rgb; mem.write32(listCurrent, cmd); listCurrent += 4; } mem.write32(context + listCurrentOffset, listCurrent); } static public void sceGuSetMatrix(int contextAddr1, int contextAddr2, int listCurrentOffset) { int type = getGprA0(); int matrix = getGprA1(); int context = getMemory().read32(getRelocatedAddress(contextAddr1, contextAddr2)); sceGuSetMatrix(context, listCurrentOffset, type, matrix); } static public void sceGuSetMatrix(int listCurrentOffset) { int context = getGprA0(); int type = getGprA1(); int matrix = getGprA2(); sceGuSetMatrix(context, listCurrentOffset, type, matrix); } static private int sceGuSetMatrix4x4(IMemoryWriter listWriter, IMemoryReader matrixReader, int startCmd, int matrixCmd, int index) { listWriter.writeNext((startCmd << 24) + index); int cmd = matrixCmd << 24; for (int i = 0; i < 16; i++) { listWriter.writeNext(cmd | (matrixReader.readNext() >>> 8)); } return 68; } static private int sceGuSetMatrix4x3(IMemoryWriter listWriter, IMemoryReader matrixReader, int startCmd, int matrixCmd, int index) { listWriter.writeNext((startCmd << 24) + index); int cmd = matrixCmd << 24; for (int i = 0; i < 4; i++) { for (int j = 0; j < 3; j++) { listWriter.writeNext(cmd | (matrixReader.readNext() >>> 8)); } matrixReader.skip(1); } return 52; } static private void sceGuSetMatrix(int context, int listCurrentOffset, int type, int matrix) { Memory mem = getMemory(); int listCurrent = mem.read32(context + listCurrentOffset); IMemoryWriter listWriter = MemoryWriter.getMemoryWriter(listCurrent, 68, 4); IMemoryReader matrixReader = MemoryReader.getMemoryReader(matrix, 64, 4); switch (type) { case 0: listCurrent += sceGuSetMatrix4x4(listWriter, matrixReader, GeCommands.PMS, GeCommands.PROJ, 0); break; case 1: listCurrent += sceGuSetMatrix4x3(listWriter, matrixReader, GeCommands.VMS, GeCommands.VIEW, 0); break; case 2: listCurrent += sceGuSetMatrix4x3(listWriter, matrixReader, GeCommands.MMS, GeCommands.MODEL, 0); break; case 3: listCurrent += sceGuSetMatrix4x3(listWriter, matrixReader, GeCommands.TMS, GeCommands.TMATRIX, 0); break; } listWriter.flush(); mem.write32(context + listCurrentOffset, listCurrent); } static public void sceGuBoneMatrix(int contextAddr1, int contextAddr2, int listCurrentOffset) { int index = getGprA0(); int matrix = getGprA1(); int context = getMemory().read32(getRelocatedAddress(contextAddr1, contextAddr2)); sceGuBoneMatrix(context, listCurrentOffset, index, matrix); } static public void sceGuBoneMatrix(int listCurrentOffset) { int context = getGprA0(); int index = getGprA1(); int matrix = getGprA2(); sceGuBoneMatrix(context, listCurrentOffset, index, matrix); } static private void sceGuBoneMatrix(int context, int listCurrentOffset, int index, int matrix) { Memory mem = getMemory(); int listCurrent = mem.read32(context + listCurrentOffset); IMemoryWriter listWriter = MemoryWriter.getMemoryWriter(listCurrent, 56, 4); if (writeDUMMY) { listWriter.writeNext(GeCommands.DUMMY << 24); listCurrent += 4; } IMemoryReader matrixReader = MemoryReader.getMemoryReader(matrix, 64, 4); listCurrent += sceGuSetMatrix4x3(listWriter, matrixReader, GeCommands.BOFS, GeCommands.BONE, index * 12); listWriter.flush(); mem.write32(context + listCurrentOffset, listCurrent); } static public void sceGuDrawSprite(int contextAddr1, int contextAddr2, int listCurrentOffset, int wOffset, int hOffset, int dxOffset, int dyOffset) { int x = getGprA0(); int y = getGprA1(); int z = getGprA2(); int u = getGprA3(); int v = getGprT0(); int flip = getGprT1(); int rotation = getGprT2(); int context = getMemory().read32(getRelocatedAddress(contextAddr1, contextAddr2)); sceGuDrawSprite(context, listCurrentOffset, x, y, z, u, v, flip, rotation, wOffset, hOffset, dxOffset, dyOffset); } static public void sceGuDrawSprite(int listCurrentOffset, int wOffset, int hOffset, int dxOffset, int dyOffset) { int context = getGprA0(); int x = getGprA1(); int y = getGprA2(); int z = getGprA3(); int u = getGprT0(); int v = getGprT1(); int flip = getGprT2(); int rotation = getGprT3(); sceGuDrawSprite(context, listCurrentOffset, x, y, z, u, v, flip, rotation, wOffset, hOffset, dxOffset, dyOffset); } static private void sceGuDrawSprite(int context, int listCurrentOffset, int x, int y, int z, int u, int v, int flip, int rotation, int wOffset, int hOffset, int dxOffset, int dyOffset) { Memory mem = getMemory(); int listCurrent = mem.read32(context + listCurrentOffset); int w = mem.read32(context + wOffset); int h = mem.read32(context + hOffset); int dx = mem.read32(context + dxOffset); int dy = mem.read32(context + dyOffset); int cmd; IMemoryWriter listWriter = MemoryWriter.getMemoryWriter(listCurrent, 44, 4); int vertexAddress = listCurrent + 8; IMemoryWriter vertexWriter = MemoryWriter.getMemoryWriter(vertexAddress, 20, 2); int v0u = u; int v0v = v; int v0x = x; int v0y = y; int v1u = u + w; int v1v = v + h; int v1x = x + dx; int v1y = y + dy; if ((flip & 1) != 0) { int tmp = v0u; v0u = v1u; v1u = tmp; } if ((flip & 2) != 0) { int tmp = v0v; v0v = v1v; v1v = tmp; } switch (rotation) { case 1: { int tmp = v0y; v0y = v1y; v1y = tmp; break; } case 2: { int tmp = v0x; v0x = v1x; v1x = tmp; tmp = v0y; v0y = v1y; v1y = tmp; break; } case 3: { int tmp = v0x; v0x = v1x; v1x = tmp; break; } } vertexWriter.writeNext(v0u); vertexWriter.writeNext(v0v); vertexWriter.writeNext(v0x); vertexWriter.writeNext(v0y); vertexWriter.writeNext(z); vertexWriter.writeNext(v1u); vertexWriter.writeNext(v1v); vertexWriter.writeNext(v1x); vertexWriter.writeNext(v1y); vertexWriter.writeNext(z); vertexWriter.flush(); int jumpAddr = vertexAddress + 20; cmd = (GeCommands.BASE << 24) | ((jumpAddr >> 8) & 0x00FF0000); listWriter.writeNext(cmd); cmd = (GeCommands.JUMP << 24) | (jumpAddr & 0x00FFFFFF); listWriter.writeNext(cmd); // Skip the 2 vertex entries listWriter.skip(5); cmd = (GeCommands.VTYPE << 24) | ((GeCommands.VTYPE_TRANSFORM_PIPELINE_RAW_COORD << 23) | (GeCommands.VTYPE_POSITION_FORMAT_16_BIT << 7) | GeCommands.VTYPE_TEXTURE_FORMAT_16_BIT); listWriter.writeNext(cmd); cmd = (GeCommands.BASE << 24) | ((vertexAddress >> 8) & 0x00FF0000); listWriter.writeNext(cmd); cmd = (GeCommands.VADDR << 24) | (vertexAddress & 0x00FFFFFF); listWriter.writeNext(cmd); cmd = (GeCommands.PRIM << 24) | (GeCommands.PRIM_SPRITES << 16) | 2; listWriter.writeNext(cmd); listWriter.flush(); mem.write32(context + listCurrentOffset, jumpAddr + 16); } static private int getListSize(int listAddr) { IMemoryReader memoryReader = MemoryReader.getMemoryReader(listAddr, 4); for (int i = 1; true; i++) { int instruction = memoryReader.readNext(); int cmd = VideoEngine.command(instruction); if (cmd == GeCommands.RET) { return i; } } } static public void sceGuCallList() { int callAddr = getGprA0(); if (Modules.ThreadManForUserModule.isCurrentThreadStackAddress(callAddr)) { // Some games are calling GE lists stored on the thread stack... dirty programming! // Such a list can be overwritten as the thread stack gets used in further calls. // These changes are however not seen immediately by the GE engine due to the memory caching. // The developer of the games probably never found this bug due to the PSP hardware memory caching. // This is however an issue with Jpcsp as no memory caching is implemented. // So, we simulate a memory cache here by reading the called list into an array and force the // VideoEngine to reuse these cached values when processing the GE list. int listSize = getListSize(callAddr); int memorySize = listSize << 2; if (log.isInfoEnabled()) { log.info(String.format("sceGuCallList Stack address 0x%08X-0x%08X", callAddr, callAddr + memorySize)); } int[] instructions = Utilities.readInt32(callAddr, memorySize); VideoEngine.getInstance().addCachedInstructions(callAddr, instructions); } } static public void saveGeToMemoryHook() { int geTopAddress = getGprA0(); Modules.sceDisplayModule.copyGeToMemory(geTopAddress); } }