| 1 | /* |
| 2 | Copyright (C) 2002 Rice1964 |
| 3 | |
| 4 | This program is free software; you can redistribute it and/or |
| 5 | modify it under the terms of the GNU General Public License |
| 6 | as published by the Free Software Foundation; either version 2 |
| 7 | of the License, or (at your option) any later version. |
| 8 | |
| 9 | This program is distributed in the hope that it will be useful, |
| 10 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 12 | GNU General Public License for more details. |
| 13 | |
| 14 | You should have received a copy of the GNU General Public License |
| 15 | along with this program; if not, write to the Free Software |
| 16 | Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
| 17 | |
| 18 | */ |
| 19 | |
| 20 | #include "Render.h" |
| 21 | #include "Timing.h" |
| 22 | |
| 23 | void RSP_GBI1_Vtx(Gfx *gfx) |
| 24 | { |
| 25 | uint32 addr = RSPSegmentAddr((gfx->gbi1vtx.addr)); |
| 26 | uint32 v0 = gfx->gbi1vtx.v0; |
| 27 | uint32 n = gfx->gbi1vtx.n; |
| 28 | |
| 29 | LOG_UCODE(" Address 0x%08x, v0: %d, Num: %d, Length: 0x%04x", addr, v0, n, gfx->gbi1vtx.len); |
| 30 | |
| 31 | if (addr > g_dwRamSize) |
| 32 | { |
| 33 | TRACE0(" Address out of range - ignoring load"); |
| 34 | return; |
| 35 | } |
| 36 | |
| 37 | if ((v0 + n) > 80) |
| 38 | { |
| 39 | TRACE5("Warning, invalid vertex positions, N=%d, v0=%d, Addr=0x%08X, Cmd=%08X-%08X", |
| 40 | n, v0, addr, gfx->words.w0, gfx->words.w1); |
| 41 | return; |
| 42 | } |
| 43 | |
| 44 | ProcessVertexData(addr, v0, n); |
| 45 | status.dwNumVertices += n; |
| 46 | DisplayVertexInfo(addr, v0, n); |
| 47 | } |
| 48 | |
| 49 | void RSP_GBI1_ModifyVtx(Gfx *gfx) |
| 50 | { |
| 51 | SP_Timing(RSP_GBI1_ModifyVtx); |
| 52 | |
| 53 | if( gRSP.ucode == 5 && ((gfx->words.w0)&0x00FFFFFF) == 0 && ((gfx->words.w1)&0xFF000000) == 0x80000000 ) |
| 54 | { |
| 55 | DLParser_Bomberman2TextRect(gfx); |
| 56 | } |
| 57 | else |
| 58 | { |
| 59 | uint32 dwWhere = ((gfx->words.w0) >> 16) & 0xFF; |
| 60 | uint32 dwVert = (((gfx->words.w0) ) & 0xFFFF) / 2; |
| 61 | uint32 dwValue = (gfx->words.w1); |
| 62 | |
| 63 | if( dwVert > 80 ) |
| 64 | { |
| 65 | RSP_RDP_NOIMPL("RSP_GBI1_ModifyVtx: Invalid vertex number: %d", dwVert, 0); |
| 66 | return; |
| 67 | } |
| 68 | |
| 69 | // Data for other commands? |
| 70 | switch (dwWhere) |
| 71 | { |
| 72 | case RSP_MV_WORD_OFFSET_POINT_RGBA: // Modify RGBA |
| 73 | case RSP_MV_WORD_OFFSET_POINT_XYSCREEN: // Modify X,Y |
| 74 | case RSP_MV_WORD_OFFSET_POINT_ZSCREEN: // Modify C |
| 75 | case RSP_MV_WORD_OFFSET_POINT_ST: // Texture |
| 76 | ModifyVertexInfo(dwWhere, dwVert, dwValue); |
| 77 | break; |
| 78 | default: |
| 79 | RSP_RDP_NOIMPL("RSP_GBI1_ModifyVtx: Setting unk value: 0x%02x, 0x%08x", dwWhere, dwValue); |
| 80 | break; |
| 81 | } |
| 82 | } |
| 83 | } |
| 84 | |
| 85 | void RSP_GBI1_Tri2(Gfx *gfx) |
| 86 | { |
| 87 | status.primitiveType = PRIM_TRI2; |
| 88 | bool bTrisAdded = false; |
| 89 | bool bTexturesAreEnabled = CRender::g_pRender->IsTextureEnabled(); |
| 90 | |
| 91 | // While the next command pair is Tri2, add vertices |
| 92 | uint32 dwPC = gDlistStack[gDlistStackPointer].pc; |
| 93 | |
| 94 | do { |
| 95 | // Vertex indices are multiplied by 10 for Mario64, by 2 for MarioKart |
| 96 | uint32 dwV0 = gfx->gbi1tri2.v0/gRSP.vertexMult; |
| 97 | uint32 dwV1 = gfx->gbi1tri2.v1/gRSP.vertexMult; |
| 98 | uint32 dwV2 = gfx->gbi1tri2.v2/gRSP.vertexMult; |
| 99 | |
| 100 | uint32 dwV3 = gfx->gbi1tri2.v3/gRSP.vertexMult; |
| 101 | uint32 dwV4 = gfx->gbi1tri2.v4/gRSP.vertexMult; |
| 102 | uint32 dwV5 = gfx->gbi1tri2.v5/gRSP.vertexMult; |
| 103 | |
| 104 | // Do first tri |
| 105 | if (IsTriangleVisible(dwV0, dwV1, dwV2)) |
| 106 | { |
| 107 | DEBUG_DUMP_VERTEXES("Tri2 1/2", dwV0, dwV1, dwV2); |
| 108 | if (!bTrisAdded) |
| 109 | { |
| 110 | if( bTexturesAreEnabled ) |
| 111 | { |
| 112 | PrepareTextures(); |
| 113 | InitVertexTextureConstants(); |
| 114 | } |
| 115 | |
| 116 | CRender::g_pRender->SetCombinerAndBlender(); |
| 117 | bTrisAdded = true; |
| 118 | } |
| 119 | PrepareTriangle(dwV0, dwV1, dwV2); |
| 120 | } |
| 121 | |
| 122 | // Do second tri |
| 123 | if (IsTriangleVisible(dwV3, dwV4, dwV5)) |
| 124 | { |
| 125 | DEBUG_DUMP_VERTEXES("Tri2 2/2", dwV3, dwV4, dwV5); |
| 126 | if (!bTrisAdded) |
| 127 | { |
| 128 | if( bTexturesAreEnabled ) |
| 129 | { |
| 130 | PrepareTextures(); |
| 131 | InitVertexTextureConstants(); |
| 132 | } |
| 133 | |
| 134 | CRender::g_pRender->SetCombinerAndBlender(); |
| 135 | bTrisAdded = true; |
| 136 | } |
| 137 | PrepareTriangle(dwV3, dwV4, dwV5); |
| 138 | } |
| 139 | |
| 140 | gfx++; |
| 141 | dwPC += 8; |
| 142 | #ifdef DEBUGGER |
| 143 | } while (!(pauseAtNext && eventToPause==NEXT_TRIANGLE) && gfx->words.cmd == (uint8)RSP_TRI2); |
| 144 | #else |
| 145 | } while( gfx->words.cmd == (uint8)RSP_TRI2); |
| 146 | #endif |
| 147 | |
| 148 | |
| 149 | gDlistStack[gDlistStackPointer].pc = dwPC-8; |
| 150 | |
| 151 | |
| 152 | if (bTrisAdded) |
| 153 | { |
| 154 | CRender::g_pRender->DrawTriangles(); |
| 155 | } |
| 156 | |
| 157 | DEBUG_TRIANGLE(TRACE0("Pause at GBI1 TRI1")); |
| 158 | } |
| 159 | |
| 160 | extern XVECTOR4 g_vtxNonTransformed[MAX_VERTS]; |
| 161 | |
| 162 | void RSP_GBI1_BranchZ(Gfx *gfx) |
| 163 | { |
| 164 | SP_Timing(RSP_GBI1_BranchZ); |
| 165 | |
| 166 | uint32 vtx = ((gfx->words.w0)&0xFFF)>>1; |
| 167 | float vtxdepth = g_vecProjected[vtx].z/g_vecProjected[vtx].w; |
| 168 | |
| 169 | #ifdef DEBUGGER |
| 170 | if( debuggerEnableZBuffer==FALSE || vtxdepth <= (s32)gfx->words.w1 || g_curRomInfo.bForceDepthBuffer ) |
| 171 | #else |
| 172 | if( vtxdepth <= (s32)(gfx->words.w1) || g_curRomInfo.bForceDepthBuffer ) |
| 173 | #endif |
| 174 | { |
| 175 | uint32 dwPC = gDlistStack[gDlistStackPointer].pc; // This points to the next instruction |
| 176 | uint32 dwDL = *(uint32 *)(g_pRDRAMu8 + dwPC-12); |
| 177 | uint32 dwAddr = RSPSegmentAddr(dwDL); |
| 178 | |
| 179 | LOG_UCODE("BranchZ to DisplayList 0x%08x", dwAddr); |
| 180 | gDlistStack[gDlistStackPointer].pc = dwAddr; |
| 181 | gDlistStack[gDlistStackPointer].countdown = MAX_DL_COUNT; |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | #ifdef DEBUGGER |
| 186 | void DumpUcodeInfo(UcodeInfo &info) |
| 187 | { |
| 188 | DebuggerAppendMsg("Loading Unknown Ucode:\n%08X-%08X-%08X-%08X, Size=0x%X, CRC=0x%08X\nCode:\n", |
| 189 | info.ucDWORD1, info.ucDWORD2, info.ucDWORD3, info.ucDWORD4, |
| 190 | info.ucSize, info.ucCRC); |
| 191 | DumpHex(info.ucStart,20); |
| 192 | TRACE0("Data:\n"); |
| 193 | DumpHex(info.ucDStart,20); |
| 194 | } |
| 195 | #endif |
| 196 | |
| 197 | void RSP_GBI1_LoadUCode(Gfx *gfx) |
| 198 | { |
| 199 | SP_Timing(RSP_GBI1_LoadUCode); |
| 200 | |
| 201 | //TRACE0("Load ucode"); |
| 202 | uint32 dwPC = gDlistStack[gDlistStackPointer].pc; |
| 203 | uint32 dwUcStart = RSPSegmentAddr((gfx->words.w1)); |
| 204 | uint32 dwSize = ((gfx->words.w0)&0xFFFF)+1; |
| 205 | uint32 dwUcDStart = RSPSegmentAddr(*(uint32 *)(g_pRDRAMu8 + dwPC-12)); |
| 206 | |
| 207 | uint32 ucode = DLParser_CheckUcode(dwUcStart, dwUcDStart, dwSize, 8); |
| 208 | RSP_SetUcode(ucode, dwUcStart, dwUcDStart, dwSize); |
| 209 | |
| 210 | DEBUGGER_PAUSE_AND_DUMP(NEXT_SWITCH_UCODE,{DebuggerAppendMsg("Pause at loading ucode");}); |
| 211 | } |
| 212 | |
| 213 | void RSP_GFX_Force_Matrix(uint32 dwAddr) |
| 214 | { |
| 215 | if (dwAddr + 64 > g_dwRamSize) |
| 216 | { |
| 217 | DebuggerAppendMsg("ForceMtx: Address invalid (0x%08x)", dwAddr); |
| 218 | return; |
| 219 | } |
| 220 | |
| 221 | // Load matrix from dwAddr |
| 222 | LoadMatrix(dwAddr); |
| 223 | |
| 224 | CRender::g_pRender->SetWorldProjectMatrix(matToLoad); |
| 225 | |
| 226 | DEBUGGER_PAUSE_AND_DUMP(NEXT_MATRIX_CMD,{TRACE0("Paused at ModMatrix Cmd");}); |
| 227 | } |
| 228 | |
| 229 | |
| 230 | void DisplayVertexInfo(uint32 dwAddr, uint32 dwV0, uint32 dwN) |
| 231 | { |
| 232 | #ifdef DEBUGGER |
| 233 | s8 *pcSrc = (s8 *)(g_pRDRAMu8 + dwAddr); |
| 234 | short *psSrc = (short *)(g_pRDRAMu8 + dwAddr); |
| 235 | |
| 236 | for (uint32 dwV = dwV0; dwV < dwV0 + dwN; dwV++) |
| 237 | { |
| 238 | float x = (float)psSrc[0^0x1]; |
| 239 | float y = (float)psSrc[1^0x1]; |
| 240 | float z = (float)psSrc[2^0x1]; |
| 241 | |
| 242 | //uint32 wFlags = g_dwVtxFlags[dwV]; //(uint16)psSrc[3^0x1]; |
| 243 | uint32 wFlags = 0; |
| 244 | |
| 245 | uint8 a = pcSrc[12^0x3]; |
| 246 | uint8 b = pcSrc[13^0x3]; |
| 247 | uint8 c = pcSrc[14^0x3]; |
| 248 | uint8 d = pcSrc[15^0x3]; |
| 249 | |
| 250 | //int nTU = (int)(short)(psSrc[4^0x1]<<4); |
| 251 | //int nTV = (int)(short)(psSrc[5^0x1]<<4); |
| 252 | |
| 253 | //float tu = (float)(nTU>>4); |
| 254 | //float tv = (float)(nTV>>4); |
| 255 | float tu = (float)(short)(psSrc[4^0x1]); |
| 256 | float tv = (float)(short)(psSrc[5^0x1]); |
| 257 | |
| 258 | XVECTOR4 & t = g_vecProjected[dwV]; |
| 259 | |
| 260 | psSrc += 8; // Increase by 16 bytes |
| 261 | pcSrc += 16; |
| 262 | |
| 263 | LOG_UCODE(" #%02d Flags: 0x%04x Pos: {% 6f,% 6f,% 6f} Tex: {%+7.2f,%+7.2f}, Extra: %02x %02x %02x %02x (transf: {% 6f,% 6f,% 6f})", |
| 264 | dwV, wFlags, x, y, z, tu, tv, a, b, c, d, t.x, t.y, t.z ); |
| 265 | } |
| 266 | #endif |
| 267 | } |
| 268 | |
| 269 | void RSP_MoveMemLight(uint32 dwLight, uint32 dwAddr) |
| 270 | { |
| 271 | if( dwLight >= 16 ) |
| 272 | { |
| 273 | DebuggerAppendMsg("Warning: invalid light # = %d", dwLight); |
| 274 | return; |
| 275 | } |
| 276 | |
| 277 | s8 * pcBase = g_pRDRAMs8 + dwAddr; |
| 278 | uint32 * pdwBase = (uint32 *)pcBase; |
| 279 | |
| 280 | |
| 281 | float range = 0, x, y, z; |
| 282 | if( options.enableHackForGames == HACK_FOR_ZELDA_MM && (pdwBase[0]&0xFF) == 0x08 && (pdwBase[1]&0xFF) == 0xFF ) |
| 283 | { |
| 284 | gRSPn64lights[dwLight].dwRGBA = pdwBase[0]; |
| 285 | gRSPn64lights[dwLight].dwRGBACopy = pdwBase[1]; |
| 286 | short* pdwBase16 = (short*)pcBase; |
| 287 | x = pdwBase16[5]; |
| 288 | y = pdwBase16[4]; |
| 289 | z = pdwBase16[7]; |
| 290 | range = pdwBase16[6]; |
| 291 | } |
| 292 | else |
| 293 | { |
| 294 | gRSPn64lights[dwLight].dwRGBA = pdwBase[0]; |
| 295 | gRSPn64lights[dwLight].dwRGBACopy = pdwBase[1]; |
| 296 | x = pcBase[8 ^ 0x3]; |
| 297 | y = pcBase[9 ^ 0x3]; |
| 298 | z = pcBase[10 ^ 0x3]; |
| 299 | } |
| 300 | |
| 301 | |
| 302 | LOG_UCODE(" RGBA: 0x%08x, RGBACopy: 0x%08x, x: %d, y: %d, z: %d", |
| 303 | gRSPn64lights[dwLight].dwRGBA, |
| 304 | gRSPn64lights[dwLight].dwRGBACopy, |
| 305 | x, y, z); |
| 306 | |
| 307 | LIGHT_DUMP(TRACE3("Move Light: %08X, %08X, %08X", pdwBase[0], pdwBase[1], pdwBase[2])); |
| 308 | |
| 309 | |
| 310 | if (dwLight == gRSP.ambientLightIndex) |
| 311 | { |
| 312 | LOG_UCODE(" (Ambient Light)"); |
| 313 | |
| 314 | uint32 dwCol = COLOR_RGBA( (gRSPn64lights[dwLight].dwRGBA >> 24)&0xFF, |
| 315 | (gRSPn64lights[dwLight].dwRGBA >> 16)&0xFF, |
| 316 | (gRSPn64lights[dwLight].dwRGBA >> 8)&0xFF, 0xff); |
| 317 | |
| 318 | SetAmbientLight( dwCol ); |
| 319 | } |
| 320 | else |
| 321 | { |
| 322 | LOG_UCODE(" (Normal Light)"); |
| 323 | |
| 324 | SetLightCol(dwLight, gRSPn64lights[dwLight].dwRGBA); |
| 325 | if (pdwBase[2] == 0) // Direction is 0! |
| 326 | { |
| 327 | LOG_UCODE(" Light is invalid"); |
| 328 | } |
| 329 | SetLightDirection(dwLight, x, y, z, range); |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | void RSP_MoveMemViewport(uint32 dwAddr) |
| 334 | { |
| 335 | if( dwAddr+16 >= g_dwRamSize ) |
| 336 | { |
| 337 | TRACE0("MoveMem Viewport, invalid memory"); |
| 338 | return; |
| 339 | } |
| 340 | |
| 341 | short scale[4]; |
| 342 | short trans[4]; |
| 343 | |
| 344 | // dwAddr is offset into RD_RAM of 8 x 16bits of data... |
| 345 | scale[0] = *(short *)(g_pRDRAMu8 + ((dwAddr+(0*2))^0x2)); |
| 346 | scale[1] = *(short *)(g_pRDRAMu8 + ((dwAddr+(1*2))^0x2)); |
| 347 | scale[2] = *(short *)(g_pRDRAMu8 + ((dwAddr+(2*2))^0x2)); |
| 348 | scale[3] = *(short *)(g_pRDRAMu8 + ((dwAddr+(3*2))^0x2)); |
| 349 | |
| 350 | trans[0] = *(short *)(g_pRDRAMu8 + ((dwAddr+(4*2))^0x2)); |
| 351 | trans[1] = *(short *)(g_pRDRAMu8 + ((dwAddr+(5*2))^0x2)); |
| 352 | trans[2] = *(short *)(g_pRDRAMu8 + ((dwAddr+(6*2))^0x2)); |
| 353 | trans[3] = *(short *)(g_pRDRAMu8 + ((dwAddr+(7*2))^0x2)); |
| 354 | |
| 355 | |
| 356 | int nCenterX = trans[0]/4; |
| 357 | int nCenterY = trans[1]/4; |
| 358 | int nWidth = scale[0]/4; |
| 359 | int nHeight = scale[1]/4; |
| 360 | |
| 361 | // Check for some strange games |
| 362 | if( nWidth < 0 ) nWidth = -nWidth; |
| 363 | if( nHeight < 0 ) nHeight = -nHeight; |
| 364 | |
| 365 | int nLeft = nCenterX - nWidth; |
| 366 | int nTop = nCenterY - nHeight; |
| 367 | int nRight= nCenterX + nWidth; |
| 368 | int nBottom= nCenterY + nHeight; |
| 369 | |
| 370 | //int maxZ = scale[2]; |
| 371 | int maxZ = 0x3FF; |
| 372 | |
| 373 | CRender::g_pRender->SetViewport(nLeft, nTop, nRight, nBottom, maxZ); |
| 374 | |
| 375 | LOG_UCODE(" Scale: %d %d %d %d = %d,%d", scale[0], scale[1], scale[2], scale[3], nWidth, nHeight); |
| 376 | LOG_UCODE(" Trans: %d %d %d %d = %d,%d", trans[0], trans[1], trans[2], trans[3], nCenterX, nCenterY); |
| 377 | } |
| 378 | |
| 379 | |
| 380 | // S2DEX uses this - 0xc1 |
| 381 | void RSP_S2DEX_SPObjLoadTxtr_Ucode1(Gfx *gfx) |
| 382 | { |
| 383 | SP_Timing(RSP_S2DEX_SPObjLoadTxtr_Ucode1); |
| 384 | |
| 385 | // Add S2DEX ucode supporting to F3DEX, see game DT and others |
| 386 | status.bUseModifiedUcodeMap = true; |
| 387 | RSP_SetUcode(1); |
| 388 | memcpy( &LoadedUcodeMap, &ucodeMap1, sizeof(UcodeMap)); |
| 389 | |
| 390 | LoadedUcodeMap[S2DEX_OBJ_MOVEMEM] = &RSP_S2DEX_OBJ_MOVEMEM; |
| 391 | LoadedUcodeMap[S2DEX_OBJ_LOADTXTR] = &RSP_S2DEX_SPObjLoadTxtr; |
| 392 | LoadedUcodeMap[S2DEX_OBJ_LDTX_SPRITE] = &RSP_S2DEX_SPObjLoadTxSprite; |
| 393 | LoadedUcodeMap[S2DEX_OBJ_LDTX_RECT] = &RSP_S2DEX_SPObjLoadTxRect; |
| 394 | LoadedUcodeMap[S2DEX_OBJ_LDTX_RECT_R] = &RSP_S2DEX_SPObjLoadTxRectR; |
| 395 | |
| 396 | RSP_S2DEX_SPObjLoadTxtr(gfx); |
| 397 | } |
| 398 | |