void FifoPlayer::LoadMemory() { UReg_MSR newMSR; newMSR.DR = 1; newMSR.IR = 1; MSR = newMSR.Hex; PowerPC::ppcState.spr[SPR_IBAT0U] = 0x80001fff; PowerPC::ppcState.spr[SPR_IBAT0L] = 0x00000002; PowerPC::ppcState.spr[SPR_DBAT0U] = 0x80001fff; PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002; PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff; PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a; Memory::Clear(); SetupFifo(); u32 *regs = m_File->GetBPMem(); for (int i = 0; i < FifoDataFile::BP_MEM_SIZE; ++i) { if (ShouldLoadBP(i)) LoadBPReg(i, regs[i]); } regs = m_File->GetCPMem(); LoadCPReg(0x30, regs[0x30]); LoadCPReg(0x40, regs[0x40]); LoadCPReg(0x50, regs[0x50]); LoadCPReg(0x60, regs[0x60]); for (int i = 0; i < 8; ++i) { LoadCPReg(0x70 + i, regs[0x70 + i]); LoadCPReg(0x80 + i, regs[0x80 + i]); LoadCPReg(0x90 + i, regs[0x90 + i]); } for (int i = 0; i < 16; ++i) { LoadCPReg(0xa0 + i, regs[0xa0 + i]); LoadCPReg(0xb0 + i, regs[0xb0 + i]); } regs = m_File->GetXFMem(); for (int i = 0; i < FifoDataFile::XF_MEM_SIZE; i += 16) LoadXFMem16(i, ®s[i]); regs = m_File->GetXFRegs(); for (int i = 0; i < FifoDataFile::XF_REGS_SIZE; ++i) LoadXFReg(i, regs[i]); FlushWGP(); }
void FifoPlayer::LoadMemory() { Memory::Clear(); SetupFifo(); u32 *regs = m_File->GetBPMem(); for (int i = 0; i < FifoDataFile::BP_MEM_SIZE; ++i) { if (ShouldLoadBP(i)) LoadBPReg(i, regs[i]); } regs = m_File->GetCPMem(); LoadCPReg(0x30, regs[0x30]); LoadCPReg(0x40, regs[0x40]); LoadCPReg(0x50, regs[0x50]); LoadCPReg(0x60, regs[0x60]); for (int i = 0; i < 8; ++i) { LoadCPReg(0x70 + i, regs[0x70 + i]); LoadCPReg(0x80 + i, regs[0x80 + i]); LoadCPReg(0x90 + i, regs[0x90 + i]); } for (int i = 0; i < 16; ++i) { LoadCPReg(0xa0 + i, regs[0xa0 + i]); LoadCPReg(0xb0 + i, regs[0xb0 + i]); } regs = m_File->GetXFMem(); for (int i = 0; i < FifoDataFile::XF_MEM_SIZE; i += 16) LoadXFMem16(i, ®s[i]); regs = m_File->GetXFRegs(); for (int i = 0; i < FifoDataFile::XF_REGS_SIZE; ++i) LoadXFReg(i, regs[i]); FlushWGP(); }
void FifoPlayer::LoadRegisters() { const u32* regs = m_File->GetBPMem(); for (int i = 0; i < FifoDataFile::BP_MEM_SIZE; ++i) { if (ShouldLoadBP(i)) LoadBPReg(i, regs[i]); } regs = m_File->GetCPMem(); LoadCPReg(0x30, regs[0x30]); LoadCPReg(0x40, regs[0x40]); LoadCPReg(0x50, regs[0x50]); LoadCPReg(0x60, regs[0x60]); for (int i = 0; i < 8; ++i) { LoadCPReg(0x70 + i, regs[0x70 + i]); LoadCPReg(0x80 + i, regs[0x80 + i]); LoadCPReg(0x90 + i, regs[0x90 + i]); } for (int i = 0; i < 16; ++i) { LoadCPReg(0xa0 + i, regs[0xa0 + i]); LoadCPReg(0xb0 + i, regs[0xb0 + i]); } regs = m_File->GetXFMem(); for (int i = 0; i < FifoDataFile::XF_MEM_SIZE; i += 16) LoadXFMem16(i, ®s[i]); regs = m_File->GetXFRegs(); for (int i = 0; i < FifoDataFile::XF_REGS_SIZE; ++i) LoadXFReg(i, regs[i]); }
inline u32 Decode(const u8* end) { const u8 *opcodeStart = g_VideoData.GetReadPosition(); if (opcodeStart == end) return 0; u8 cmd_byte = g_VideoData.Read<u8>(); size_t distance = (size_t)(end - g_VideoData.GetReadPosition()); u32 cycles; switch (cmd_byte) { case GX_NOP: { cycles = GX_NOP_CYCLES; // Hm, this means that we scan over nop streams pretty slowly... } break; case GX_UNKNOWN_RESET: { cycles = GX_NOP_CYCLES; // Datel software uses this command DEBUG_LOG(VIDEO, "GX Reset?: %08x", cmd_byte); } break; case GX_LOAD_CP_REG: { if (sizeCheck && distance < GX_LOAD_CP_REG_SIZE) return 0; cycles = GX_LOAD_CP_REG_CYCLES; u8 sub_cmd = g_VideoData.Read<u8>(); u32 value = g_VideoData.Read<u32>(); LoadCPReg(sub_cmd, value); INCSTAT(stats.thisFrame.numCPLoads); } break; case GX_LOAD_XF_REG: { if (sizeCheck && distance < GX_LOAD_XF_REG_SIZE) return 0; u32 Cmd2 = g_VideoData.Read<u32>(); distance -= GX_LOAD_XF_REG_SIZE; int transfer_size = ((Cmd2 >> 16) & 15) + 1; if (sizeCheck && (distance < (transfer_size * sizeof(u32)))) return 0; cycles = GX_LOAD_XF_REG_BASE_CYCLES + GX_LOAD_XF_REG_TRANSFER_CYCLES * transfer_size; u32 xf_address = Cmd2 & 0xFFFF; LoadXFReg(transfer_size, xf_address); INCSTAT(stats.thisFrame.numXFLoads); } break; case GX_LOAD_INDX_A: //used for position matrices { if (sizeCheck && distance < GX_LOAD_INDX_A_SIZE) return 0; cycles = GX_LOAD_INDX_A_CYCLES; LoadIndexedXF(g_VideoData.Read<u32>(), 0xC); } break; case GX_LOAD_INDX_B: //used for normal matrices { if (sizeCheck && distance < GX_LOAD_INDX_B_SIZE) return 0; cycles = GX_LOAD_INDX_B_CYCLES; LoadIndexedXF(g_VideoData.Read<u32>(), 0xD); } break; case GX_LOAD_INDX_C: //used for postmatrices { if (sizeCheck && distance < GX_LOAD_INDX_C_SIZE) return 0; cycles = GX_LOAD_INDX_C_CYCLES; LoadIndexedXF(g_VideoData.Read<u32>(), 0xE); } break; case GX_LOAD_INDX_D: //used for lights { if (sizeCheck && distance < GX_LOAD_INDX_D_SIZE) return 0; cycles = GX_LOAD_INDX_D_CYCLES; LoadIndexedXF(g_VideoData.Read<u32>(), 0xF); } break; case GX_CMD_CALL_DL: { if (sizeCheck && distance < GX_CMD_CALL_DL_SIZE) return 0; u32 address = g_VideoData.Read<u32>(); u32 count = g_VideoData.Read<u32>(); cycles = GX_CMD_CALL_DL_BASE_CYCLES + InterpretDisplayList(address, count); } break; case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that { cycles = GX_CMD_UNKNOWN_METRICS_CYCLES; DEBUG_LOG(VIDEO, "GX 0x44: %08x", cmd_byte); } break; case GX_CMD_INVL_VC: // Invalidate Vertex Cache { cycles = GX_CMD_INVL_VC_CYCLES; DEBUG_LOG(VIDEO, "Invalidate (vertex cache?)"); } break; case GX_LOAD_BP_REG: { if (sizeCheck && distance < GX_LOAD_BP_REG_SIZE) return 0; cycles = GX_LOAD_BP_REG_CYCLES; u32 bp_cmd = g_VideoData.Read<u32>(); LoadBPReg(bp_cmd); INCSTAT(stats.thisFrame.numBPLoads); } break; // draw primitives default: if ((cmd_byte & GX_DRAW_PRIMITIVES) == 0x80) { // load vertices if (sizeCheck && distance < GX_DRAW_PRIMITIVES_SIZE) return 0; u32 count = g_VideoData.Read<u16>(); distance -= GX_DRAW_PRIMITIVES_SIZE; if (count) { VertexLoaderParameters parameters; parameters.count = count; parameters.buf_size = distance; parameters.primitive = (cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT; parameters.vtx_attr_group = cmd_byte & GX_VAT_MASK; parameters.needloaderrefresh = (g_main_cp_state.attr_dirty & (1 << parameters.vtx_attr_group)) != 0; parameters.skip_draw = g_bSkipCurrentFrame; parameters.VtxDesc = &g_main_cp_state.vtx_desc; parameters.VtxAttr = &g_main_cp_state.vtx_attr[parameters.vtx_attr_group]; parameters.source = g_VideoData.GetReadPosition(); g_main_cp_state.attr_dirty &= ~(1 << parameters.vtx_attr_group); u32 readsize = 0; u32 writesize = 0; if (VertexLoaderManager::ConvertVertices(parameters, readsize, writesize)) { cycles = GX_NOP_CYCLES + GX_DRAW_PRIMITIVES_CYCLES * parameters.count; g_VideoData.ReadSkip(readsize); VertexManagerBase::s_pCurBufferPointer += writesize; } else { return 0; } } else { cycles = GX_NOP_CYCLES; } } else { if (!s_bFifoErrorSeen)
u8* OpcodeDecoder_Run(DataReader src, u32* cycles, bool in_display_list) { u32 totalCycles = 0; u8* opcodeStart; while (true) { opcodeStart = src.GetPointer(); if (!src.size()) goto end; u8 cmd_byte = src.Read<u8>(); int refarray; switch (cmd_byte) { case GX_NOP: totalCycles += 6; // Hm, this means that we scan over nop streams pretty slowly... break; case GX_UNKNOWN_RESET: totalCycles += 6; // Datel software uses this command DEBUG_LOG(VIDEO, "GX Reset?: %08x", cmd_byte); break; case GX_LOAD_CP_REG: { if (src.size() < 1 + 4) goto end; totalCycles += 12; u8 sub_cmd = src.Read<u8>(); u32 value = src.Read<u32>(); LoadCPReg(sub_cmd, value, is_preprocess); if (!is_preprocess) INCSTAT(stats.thisFrame.numCPLoads); } break; case GX_LOAD_XF_REG: { if (src.size() < 4) goto end; u32 Cmd2 = src.Read<u32>(); int transfer_size = ((Cmd2 >> 16) & 15) + 1; if (src.size() < transfer_size * sizeof(u32)) goto end; totalCycles += 18 + 6 * transfer_size; if (!is_preprocess) { u32 xf_address = Cmd2 & 0xFFFF; LoadXFReg(transfer_size, xf_address, src); INCSTAT(stats.thisFrame.numXFLoads); } src.Skip<u32>(transfer_size); } break; case GX_LOAD_INDX_A: //used for position matrices refarray = 0xC; goto load_indx; case GX_LOAD_INDX_B: //used for normal matrices refarray = 0xD; goto load_indx; case GX_LOAD_INDX_C: //used for postmatrices refarray = 0xE; goto load_indx; case GX_LOAD_INDX_D: //used for lights refarray = 0xF; goto load_indx; load_indx: if (src.size() < 4) goto end; totalCycles += 6; if (is_preprocess) PreprocessIndexedXF(src.Read<u32>(), refarray); else LoadIndexedXF(src.Read<u32>(), refarray); break; case GX_CMD_CALL_DL: { if (src.size() < 8) goto end; u32 address = src.Read<u32>(); u32 count = src.Read<u32>(); if (in_display_list) { totalCycles += 6; WARN_LOG(VIDEO,"recursive display list detected"); } else { if (is_preprocess) InterpretDisplayListPreprocess(address, count); else totalCycles += 6 + InterpretDisplayList(address, count); } } break; case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that totalCycles += 6; DEBUG_LOG(VIDEO, "GX 0x44: %08x", cmd_byte); break; case GX_CMD_INVL_VC: // Invalidate Vertex Cache totalCycles += 6; DEBUG_LOG(VIDEO, "Invalidate (vertex cache?)"); break; case GX_LOAD_BP_REG: // In skipped_frame case: We have to let BP writes through because they set // tokens and stuff. TODO: Call a much simplified LoadBPReg instead. { if (src.size() < 4) goto end; totalCycles += 12; u32 bp_cmd = src.Read<u32>(); if (is_preprocess) { LoadBPRegPreprocess(bp_cmd); } else { LoadBPReg(bp_cmd); INCSTAT(stats.thisFrame.numBPLoads); } } break; // draw primitives default: if ((cmd_byte & 0xC0) == 0x80) { // load vertices if (src.size() < 2) goto end; u16 num_vertices = src.Read<u16>(); int bytes = VertexLoaderManager::RunVertices( cmd_byte & GX_VAT_MASK, // Vertex loader index (0 - 7) (cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT, num_vertices, src, Fifo::g_bSkipCurrentFrame, is_preprocess); if (bytes < 0) goto end; src.Skip(bytes); // 4 GPU ticks per vertex, 3 CPU ticks per GPU tick totalCycles += num_vertices * 4 * 3 + 6; } else { if (!s_bFifoErrorSeen) UnknownOpcode(cmd_byte, opcodeStart, is_preprocess); ERROR_LOG(VIDEO, "FIFO: Unknown Opcode(0x%02x @ %p, preprocessing = %s)", cmd_byte, opcodeStart, is_preprocess ? "yes" : "no"); s_bFifoErrorSeen = true; totalCycles += 1; } break; } // Display lists get added directly into the FIFO stream if (!is_preprocess && g_bRecordFifoData && cmd_byte != GX_CMD_CALL_DL) { u8* opcodeEnd; opcodeEnd = src.GetPointer(); FifoRecorder::GetInstance().WriteGPCommand(opcodeStart, u32(opcodeEnd - opcodeStart)); } }
u8* OpcodeDecoder_Run(DataReader& reader, u32* cycles) { u32 totalCycles = 0; u8* opcodeStart; while (true) { opcodeStart = reader.GetReadPosition(); if (!reader.size()) goto end; u8 cmd_byte = reader.Read<u8>(); size_t distance = reader.size(); switch (cmd_byte) { case GX_NOP: { totalCycles += GX_NOP_CYCLES; // Hm, this means that we scan over nop streams pretty slowly... } break; case GX_UNKNOWN_RESET: { totalCycles += GX_NOP_CYCLES; // Datel software uses this command DEBUG_LOG(VIDEO, "GX Reset?: %08x", cmd_byte); } break; case GX_LOAD_CP_REG: { if (sizeCheck && distance < GX_LOAD_CP_REG_SIZE) goto end; totalCycles += GX_LOAD_CP_REG_CYCLES; u8 sub_cmd = reader.Read<u8>(); u32 value = reader.Read<u32>(); LoadCPReg<is_preprocess>(sub_cmd, value); if (!is_preprocess) INCSTAT(stats.thisFrame.numCPLoads); } break; case GX_LOAD_XF_REG: { if (sizeCheck && distance < GX_LOAD_XF_REG_SIZE) goto end; u32 Cmd2 = reader.Read<u32>(); distance -= GX_LOAD_XF_REG_SIZE; int transfer_size = ((Cmd2 >> 16) & 15) + 1; if (sizeCheck && distance < (transfer_size * sizeof(u32))) goto end; totalCycles += GX_LOAD_XF_REG_BASE_CYCLES + GX_LOAD_XF_REG_TRANSFER_CYCLES * transfer_size; if (is_preprocess) { reader.ReadSkip(transfer_size * sizeof(u32)); } else { u32 xf_address = Cmd2 & 0xFFFF; LoadXFReg(transfer_size, xf_address); INCSTAT(stats.thisFrame.numXFLoads); } } break; case GX_LOAD_INDX_A: //used for position matrices case GX_LOAD_INDX_B: //used for normal matrices case GX_LOAD_INDX_C: //used for postmatrices case GX_LOAD_INDX_D: //used for lights { if (sizeCheck && distance < GX_LOAD_INDX_SIZE) goto end; totalCycles += GX_LOAD_INDX_CYCLES; const s32 ref_array = (cmd_byte >> 3) + 8; if (is_preprocess) PreprocessIndexedXF(reader.Read<u32>(), ref_array); else LoadIndexedXF(reader.Read<u32>(), ref_array); } break; case GX_CMD_CALL_DL: { if (sizeCheck && distance < GX_CMD_CALL_DL_SIZE) goto end; u32 address = reader.Read<u32>(); u32 count = reader.Read<u32>(); if (is_preprocess) InterpretDisplayListPreprocess(address, count); else totalCycles += GX_CMD_CALL_DL_BASE_CYCLES + InterpretDisplayList(address, count); } break; case GX_CMD_UNKNOWN_METRICS: // zelda 4 swords calls it and checks the metrics registers after that { totalCycles += GX_CMD_UNKNOWN_METRICS_CYCLES; DEBUG_LOG(VIDEO, "GX 0x44: %08x", cmd_byte); } break; case GX_CMD_INVL_VC: // Invalidate Vertex Cache { totalCycles += GX_CMD_INVL_VC_CYCLES; DEBUG_LOG(VIDEO, "Invalidate (vertex cache?)"); } break; case GX_LOAD_BP_REG: { if (sizeCheck && distance < GX_LOAD_BP_REG_SIZE) goto end; totalCycles += GX_LOAD_BP_REG_CYCLES; u32 bp_cmd = reader.Read<u32>(); if (is_preprocess) { LoadBPRegPreprocess(bp_cmd); } else { LoadBPReg(bp_cmd); INCSTAT(stats.thisFrame.numBPLoads); } } break; // draw primitives default: if ((cmd_byte & GX_DRAW_PRIMITIVES) == 0x80) { // load vertices if (sizeCheck && distance < GX_DRAW_PRIMITIVES_SIZE) goto end; u32 count = reader.Read<u16>(); distance -= GX_DRAW_PRIMITIVES_SIZE; if (count) { CPState& state = is_preprocess ? g_preprocess_cp_state : g_main_cp_state; VertexLoaderParameters parameters; parameters.count = count; parameters.buf_size = distance; parameters.primitive = (cmd_byte & GX_PRIMITIVE_MASK) >> GX_PRIMITIVE_SHIFT; u32 vtx_attr_group = cmd_byte & GX_VAT_MASK; parameters.vtx_attr_group = vtx_attr_group; parameters.needloaderrefresh = (state.attr_dirty & (1u << vtx_attr_group)) != 0; parameters.skip_draw = g_bSkipCurrentFrame; parameters.VtxDesc = &state.vtx_desc; parameters.VtxAttr = &state.vtx_attr[vtx_attr_group]; parameters.source = reader.GetReadPosition(); state.attr_dirty &= ~(1 << vtx_attr_group); u32 readsize = 0; if (is_preprocess) { u32 components = 0; VertexLoaderManager::GetVertexSizeAndComponents(parameters, readsize, components); readsize *= count; if (distance >= readsize) { totalCycles += GX_NOP_CYCLES + GX_DRAW_PRIMITIVES_CYCLES * parameters.count; reader.ReadSkip(readsize); } else { goto end; } } else { u32 writesize = 0; if (VertexLoaderManager::ConvertVertices(parameters, readsize, writesize)) { totalCycles += GX_NOP_CYCLES + GX_DRAW_PRIMITIVES_CYCLES * parameters.count; reader.ReadSkip(readsize); VertexManagerBase::s_pCurBufferPointer += writesize; } else { goto end; } } } else { totalCycles += GX_NOP_CYCLES; } } else { if (!s_bFifoErrorSeen) UnknownOpcode(cmd_byte, opcodeStart, is_preprocess); ERROR_LOG(VIDEO, "FIFO: Unknown Opcode(0x%02x @ %p, preprocessing = %s)", cmd_byte, opcodeStart, is_preprocess ? "yes" : "no"); s_bFifoErrorSeen = true; totalCycles += 1; } break; }