#include #include #include #include #include #include #include "convert.h" #include "N64.h" #include "GLideN64.h" #include "GBI.h" #include "RDP.h" #include "RSP.h" #include "uCodes/F3D.h" #include "uCodes/F3DEX.h" #include "uCodes/F3DEX2.h" #include "uCodes/L3D.h" #include "uCodes/L3DEX.h" #include "uCodes/L3DEX2.h" #include "uCodes/S2DEX.h" #include "uCodes/S2DEX2.h" #include "uCodes/F3DAM.h" #include "uCodes/F3DDKR.h" #include "uCodes/F3DBETA.h" #include "uCodes/F3DPD.h" #include "uCodes/F3DSETA.h" #include "uCodes/F3DGOLDEN.h" #include "uCodes/F3DEX2CBFD.h" #include "uCodes/F3DZEX2.h" #include "uCodes/F3DTEXA.h" #include "uCodes/F3DEX2ACCLAIM.h" #include "uCodes/F3DFLX2.h" #include "uCodes/F5Indi_Naboo.h" #include "uCodes/F5Rogue.h" #include "uCodes/ZSort.h" #include "uCodes/ZSortBOSS.h" #include "CRC.h" #include "Log.h" #include "DebugDump.h" #include "Graphics/Context.h" #include "Graphics/Parameters.h" u32 last_good_ucode = (u32) -1; struct SpecialMicrocodeInfo { u32 type; bool NoN; // ucode does not use near clipping bool negativeY; // Y is inverted bool fast3DPerspNorm; // ucode is from Fast3D family and has G_PERSPNORMALIZE. See #1303 u32 crc; }; static const std::vector specialMicrocodes = { { S2DEX2, false, true, false, 0x02c399dd }, // Animal Forest { F3DEX, false, false, true, 0x0ace4c3f }, // Mario Kart 64 { F3D, true, false, false, 0x16c3a775 }, // AeroFighters { F3DEX2CBFD, true, true, false, 0x1b4ace88 }, // Conker's Bad Fur Day { F3DPD, true, true, false, 0x1c4f7869 }, // Perfect Dark { F3D, false, false, true, 0x1f24cc84 }, // Wayne Gretzky's 3D Hockey (U) { F5Indi_Naboo, false, false, false, 0x23fef05f }, // SW Ep.1 Battle for Naboo { Turbo3D, false, true, false, 0x2bdcfc8a }, // Dark Rift, Turbo3D { F3DSETA, false, true, true, 0x2edee7be }, // RSP SW Version: 2.0D, 04-01-96 { F3DGOLDEN, true, true, false, 0x302bca09 }, // RSP SW Version: 2.0G, 09-30-96 GoldenEye { F3D, false, false, false, 0x4AED6B3B }, // Vivid Dolls [ALECK64] { F3D, true, true, true, 0x54c558ba }, // RSP SW Version: 2.0D, 04-01-96 Pilot Wings, Blast Corps { ZSortBOSS, false, false, false, 0x553538cc }, // World Driver Championship { F3D, false, false, true, 0x55be9bad }, // RSP SW Version: 2.0D, 04-01-96, Mischief Makers, Mortal Combat Trilogy, J.League Live { F3DEX, true, true, true, 0x637b4b58 }, // RSP SW Version: 2.0D, 04-01-96 Power League { F5Indi_Naboo, false, false, false, 0x6859bf8e }, // Indiana Jones { F3D, false, false, true, 0x6932365f }, // Super Mario 64 { ZSortBOSS, false, false, false, 0x6a76f8dd }, // Stunt Racer { F3DDKR, false, true, true, 0x6e6fc893 }, // Diddy Kong Racing { ZSortBOSS, false, false, false, 0x75ed44cc }, // World Driver Championship, European { F3D, true, false, true, 0x77195a68 }, // Dark Rift { L3D, true, true, true, 0x771ce0c4 }, // RSP SW Version: 2.0D, 04-01-96 Blast Corps { F3D, false, false, false, 0x7d372819 }, // Pachinko nichi 365 { F3DDKR, false, true, true, 0x8d91244f }, // Diddy Kong Racing { F3DBETA, false, true, true, 0x94c4c833 }, // Star Wars Shadows of Empire { S2DEX_1_05, false, true, false, 0x9df31081 }, // RSP Gfx ucode S2DEX 1.06 Yoshitaka Yasumoto Nintendo { T3DUX, false, true, false, 0xbad437f2 }, // T3DUX vers 0.83 for Toukon Road { F3DJFG, false, true, true, 0xbde9d1fb }, // Jet Force Gemini, Mickey { T3DUX, false, true, false, 0xd0a1aa3d }, // T3DUX vers 0.85 for Toukon Road 2 { F3DBETA, false, true, true, 0xd17906e2 }, // RSP SW Version: 2.0D, 04-01-96, Wave Race (U) { F3DZEX2MM, true, true, false, 0xd39a0d4f }, // Animal Forest { F3D, false, false, true, 0xd3ab59b2 }, // Cruise'n USA { F5Rogue, false, false, false, 0xda51ccdb }, // Star Wars RS { F3D, false, false, false, 0xe01e14be }, // Eikou no Saint Andrews { F3DEX2ACCLAIM,true, true, false, 0xe44df568 }, // Acclaim games: Turok2 & 3, Armories and South park { F3D, false, true, false, 0xe62a706d }, // Fast3D }; u32 G_RDPHALF_1, G_RDPHALF_2, G_RDPHALF_CONT; u32 G_SPNOOP; u32 G_SETOTHERMODE_H, G_SETOTHERMODE_L; u32 G_DL, G_ENDDL, G_CULLDL, G_BRANCH_Z, G_BRANCH_W; u32 G_LOAD_UCODE; u32 G_MOVEMEM, G_MOVEWORD; u32 G_MTX, G_POPMTX; u32 G_GEOMETRYMODE, G_SETGEOMETRYMODE, G_CLEARGEOMETRYMODE; u32 G_TEXTURE; u32 G_DMA_IO, G_DMA_DL, G_DMA_TRI, G_DMA_MTX, G_DMA_VTX, G_DMA_TEX_OFFSET, G_DMA_OFFSETS; u32 G_SPECIAL_1, G_SPECIAL_2, G_SPECIAL_3; u32 G_VTX, G_MODIFYVTX, G_VTXCOLORBASE; u32 G_TRI1, G_TRI2, G_TRIX; u32 G_QUAD, G_LINE3D; u32 G_RESERVED0, G_RESERVED1, G_RESERVED2, G_RESERVED3; u32 G_SPRITE2D_BASE; u32 G_BG_1CYC, G_BG_COPY; u32 G_OBJ_RECTANGLE, G_OBJ_SPRITE, G_OBJ_MOVEMEM; u32 G_SELECT_DL, G_OBJ_RENDERMODE, G_OBJ_RECTANGLE_R; u32 G_OBJ_LOADTXTR, G_OBJ_LDTX_SPRITE, G_OBJ_LDTX_RECT, G_OBJ_LDTX_RECT_R; u32 G_RDPHALF_0; u32 G_PERSPNORM; u32 G_ZOBJ, G_ZRDPCMD, G_ZWAITSIGNAL, G_ZMTXCAT, G_ZMULT_MPMTX, G_ZLIGHTING; u32 G_MTX_STACKSIZE; u32 G_MTX_MODELVIEW; u32 G_MTX_PROJECTION; u32 G_MTX_MUL; u32 G_MTX_LOAD; u32 G_MTX_NOPUSH; u32 G_MTX_PUSH; u32 G_TEXTURE_ENABLE; u32 G_SHADING_SMOOTH; u32 G_CULL_FRONT; u32 G_CULL_BACK; u32 G_CULL_BOTH; u32 G_CLIPPING; u32 G_MV_VIEWPORT; u32 G_MWO_aLIGHT_1, G_MWO_bLIGHT_1; u32 G_MWO_aLIGHT_2, G_MWO_bLIGHT_2; u32 G_MWO_aLIGHT_3, G_MWO_bLIGHT_3; u32 G_MWO_aLIGHT_4, G_MWO_bLIGHT_4; u32 G_MWO_aLIGHT_5, G_MWO_bLIGHT_5; u32 G_MWO_aLIGHT_6, G_MWO_bLIGHT_6; u32 G_MWO_aLIGHT_7, G_MWO_bLIGHT_7; u32 G_MWO_aLIGHT_8, G_MWO_bLIGHT_8; GBIInfo GBI; void GBI_Unknown( const Gwords words ) { DebugMsg(DEBUG_NORMAL, "UNKNOWN GBI COMMAND 0x%02X", _SHIFTR(words.w0, 24, 8)); LOG(LOG_ERROR, "UNKNOWN GBI COMMAND 0x%02X", _SHIFTR(words.w0, 24, 8)); } void GBIInfo::init() { m_hwlSupported = true; m_pCurrent = nullptr; _flushCommands(); } void GBIInfo::destroy() { m_pCurrent = nullptr; m_list.clear(); } bool GBIInfo::isHWLSupported() const { return m_hwlSupported; } void GBIInfo::setHWLSupported(bool _supported) { m_hwlSupported = _supported; } void GBIInfo::_flushCommands() { std::fill(std::begin(cmd), std::end(cmd), GBI_Unknown); } void GBIInfo::_makeCurrent(MicrocodeInfo * _pCurrent) { if (_pCurrent->type == NONE) { LOG(LOG_ERROR, "[GLideN64]: error - unknown ucode!!!"); return; } if ((_pCurrent->type == ZSortBOSS) && (!REG.SP_STATUS)) { assert(false && "ZSortBOSS ucode needs access to SP_STATUS register'n"); } if (m_pCurrent == nullptr || (m_pCurrent->type != _pCurrent->type)) { m_pCurrent = _pCurrent; _flushCommands(); RDP_Init(); G_TRI1 = G_TRI2 = G_TRIX = G_QUAD = -1; // For correct work of gSPFlushTriangles() gSP.clipRatio = 1U; switch (m_pCurrent->type) { case F3D: case Turbo3D: F3D_Init(); m_hwlSupported = true; break; case F3DEX: F3DEX_Init(); m_hwlSupported = true; gSP.clipRatio = m_pCurrent->Rej ? 2U : 1U; break; case F3DEX2: F3DEX2_Init(); m_hwlSupported = true; gSP.clipRatio = 2U; break; case L3D: L3D_Init(); m_hwlSupported = false; break; case L3DEX: L3DEX_Init(); m_hwlSupported = false; gSP.clipRatio = m_pCurrent->Rej ? 2U : 1U; break; case L3DEX2: L3DEX2_Init(); m_hwlSupported = false; gSP.clipRatio = 2U; break; case S2DEX_1_03: S2DEX_1_03_Init(); m_hwlSupported = false; break; case S2DEX_1_05: S2DEX_1_05_Init(); m_hwlSupported = false; break; case S2DEX_1_07: S2DEX_1_07_Init(); m_hwlSupported = false; break; case S2DEX2: S2DEX2_Init(); m_hwlSupported = false; break; case F3DDKR: F3DDKR_Init(); m_hwlSupported = false; break; case F3DJFG: F3DJFG_Init(); m_hwlSupported = false; break; case F3DBETA: F3DBETA_Init(); m_hwlSupported = true; break; case F3DPD: F3DPD_Init(); m_hwlSupported = true; break; case F3DAM: F3DAM_Init(); m_hwlSupported = true; break; case ZSortp: ZSort_Init(); m_hwlSupported = true; break; case F3DEX2CBFD: F3DEX2CBFD_Init(); m_hwlSupported = false; break; case F3DSETA: F3DSETA_Init(); m_hwlSupported = true; break; case F3DGOLDEN: F3DGOLDEN_Init(); m_hwlSupported = true; break; case F3DZEX2OOT: F3DZEX2_Init(); m_hwlSupported = true; gSP.clipRatio = 2U; break; case F3DZEX2MM: F3DZEX2_Init(); m_hwlSupported = false; gSP.clipRatio = 2U; break; case F3DTEXA: F3DTEXA_Init(); m_hwlSupported = true; break; case T3DUX: F3D_Init(); m_hwlSupported = false; break; case F3DEX2ACCLAIM: F3DEX2ACCLAIM_Init(); m_hwlSupported = false; gSP.clipRatio = 2U; break; case F5Rogue: F5Rogue_Init(); m_hwlSupported = false; break; case F3DFLX2: F3DFLX2_Init(); m_hwlSupported = true; gSP.clipRatio = 2U; break; case ZSortBOSS: ZSortBOSS_Init(); m_hwlSupported = true; break; case F5Indi_Naboo: F5Indi_Naboo_Init(); m_hwlSupported = false; break; } if (m_pCurrent->NoN) gfxContext.setClampMode(graphics::ClampMode::NoNearPlaneClipping); else gfxContext.setClampMode(graphics::ClampMode::ClippingEnabled); if (m_pCurrent->fast3DPersp) { GBI_SetGBI(G_PERSPNORM, F3DBETA_PERSPNORM, F3DBETA_Perpnorm); GBI_SetGBI(G_RDPHALF_1, F3DBETA_RDPHALF_1, F3D_RDPHalf_1); GBI_SetGBI(G_RDPHALF_2, F3DBETA_RDPHALF_2, F3D_RDPHalf_2); } } else if (m_pCurrent->NoN != _pCurrent->NoN) { if (_pCurrent->NoN) gfxContext.setClampMode(graphics::ClampMode::NoNearPlaneClipping); else gfxContext.setClampMode(graphics::ClampMode::ClippingEnabled); } m_pCurrent = _pCurrent; } bool GBIInfo::_makeExistingMicrocodeCurrent(word uc_start, word uc_dstart, u32 uc_dsize) { auto iter = std::find_if(m_list.begin(), m_list.end(), [=](const MicrocodeInfo& info) { return info.address == uc_start && info.dataAddress == uc_dstart && info.dataSize == uc_dsize; }); if (iter == m_list.end()) return false; _makeCurrent(&*iter); return true; } void GBIInfo::loadMicrocode(word uc_start, word uc_dstart, u16 uc_dsize) { if (_makeExistingMicrocodeCurrent(uc_start, uc_dstart, uc_dsize)) return; m_list.emplace_front(); MicrocodeInfo & current = m_list.front(); current.address = uc_start; current.dataAddress = uc_dstart; current.dataSize = uc_dsize; current.type = NONE; // See if we can identify it by CRC #ifdef NATIVE const u32 uc_crc = CRC_Calculate_Strict( 0xFFFFFFFF, (void*)uc_start, 4096 ); #else const u32 uc_crc = CRC_Calculate_Strict( 0xFFFFFFFF, &RDRAM[uc_start & 0x1FFFFFFF], 4096 ); #endif SpecialMicrocodeInfo infoToSearch; infoToSearch.crc = uc_crc; auto it = std::lower_bound(specialMicrocodes.begin(), specialMicrocodes.end(), infoToSearch, [](const SpecialMicrocodeInfo & i, const SpecialMicrocodeInfo & j){ return i.crc < j.crc; }); if (it != specialMicrocodes.end() && it->crc == uc_crc) { const SpecialMicrocodeInfo & info = *it; current.type = info.type; current.NoN = info.NoN; current.negativeY = info.negativeY; current.fast3DPersp = info.fast3DPerspNorm; LOG(LOG_VERBOSE, "Load microcode type: %d crc: 0x%08x romname: %s", current.type, uc_crc, RSP.romname); _makeCurrent(¤t); return; } #ifdef NATIVE const char* uc_data = (const char*)uc_dstart; #else // See if we can identify it by text char uc_data[2048]; UnswapCopyWrap(RDRAM, uc_dstart & 0x1FFFFFFF, (u8*)uc_data, 0, 0x7FF, 2048); #endif char uc_str[256]; strcpy(uc_str, "Not Found"); for (u32 i = 0; i < 2046; ++i) { if ((uc_data[i] == 'R') && (uc_data[i+1] == 'S') && (uc_data[i+2] == 'P')) { u32 j = 0; while (uc_data[i+j] > 0x0A) { uc_str[j] = uc_data[i+j]; j++; } uc_str[j] = 0x00; int type = NONE; if (strncmp(&uc_str[4], "SW", 2) == 0) { type = F3D; } else if (strncmp(&uc_str[4], "Gfx", 3) == 0) { current.NoN = (strstr( uc_str + 4, ".NoN") != nullptr); current.Rej = (strstr(uc_str + 4, ".Rej") != nullptr); if (current.Rej) // For the Z direction, a reject box can be done with the far plane, but not with the near plane. current.NoN = true; if (strncmp( &uc_str[14], "F3D", 3 ) == 0) { if (uc_str[28] == '1' || strncmp(&uc_str[28], "0.95", 4) == 0 || strncmp(&uc_str[28], "0.96", 4) == 0) type = F3DEX; else if (uc_str[31] == '2') { type = F3DEX2; if (uc_str[35] == 'H') current.combineMatrices = true; } if (strncmp(&uc_str[14], "F3DFLX", 6) == 0) { type = F3DFLX2; current.NoN = true; } else if (strncmp(&uc_str[14], "F3DZEX", 6) == 0) { // Zelda games if (uc_str[34] == '6') type = F3DZEX2OOT; else type = F3DZEX2MM; current.combineMatrices = false; } else if (strncmp(&uc_str[14], "F3DTEX/A", 8) == 0) type = F3DTEXA; else if (strncmp(&uc_str[14], "F3DAM", 5) == 0) type = F3DAM; else if (strncmp(&uc_str[14], "F3DLX.Rej", 9) == 0) { current.NoN = true; current.cullBoth = false; } else if (strncmp(&uc_str[14], "F3DLP.Rej", 9) == 0) { current.texturePersp = false; current.NoN = true; } } else if (strncmp( &uc_str[14], "L3D", 3 ) == 0) { u32 t = 22; while (!std::isdigit(uc_str[t]) && t++ < j); if (uc_str[t] == '1') type = L3DEX; else if (uc_str[t] == '2') type = L3DEX2; } else if (strncmp( &uc_str[14], "S2D", 3 ) == 0) { u32 t = 20; while (!std::isdigit(uc_str[t]) && t++ < j); if (uc_str[t] == '1') { if (strncmp(&uc_str[21], "1.03", 4) == 0) type = S2DEX_1_03; else if (strncmp(&uc_str[21], "1.05", 4) == 0) type = S2DEX_1_05; else type = S2DEX_1_07; } else if (uc_str[t] == '2') type = S2DEX2; current.texturePersp = false; } else if (strncmp(&uc_str[14], "ZSortp", 6) == 0) { type = ZSortp; } } if (type != NONE) { current.type = type; LOG(LOG_VERBOSE, "Load microcode (%s) type: %d crc: 0x%08x romname: %s", uc_str, current.type, uc_crc, RSP.romname); _makeCurrent(¤t); return; } break; } } assert(false && "unknown ucode!!!'n"); _makeCurrent(¤t); } #ifdef EXTENDED_GFX Gwords::Gwords() : w0(0), w1(0), w2(0), w3(0) { } Gwords::Gwords(word _w0, word _w1) : w0(_w0), w1(_w1), w2(0), w3(0) { } Gwords::Gwords(word _w0, word _w1, word _w2, word _w3) : w0(_w0), w1(_w1), w2(_w2), w3(_w3) { } #else Gwords::Gwords() : w0(0), w1(0) { } Gwords::Gwords(word _w0, word _w1) : w0(_w0), w1(_w1) { } #endif