/** * Manually uncommit a memory region, must match a known mapping in gMemoryMap. */ bool uncommit(ppcaddr_t address, ppcaddr_t size) { auto mapping = findMapping(address, size); if (!mapping) { decaf_abort("Invalid mem::commit attempt, must exactly match a know mapping"); return false; } if (mapping->flags & MapFlags::AutoCommit) { decaf_abort("Attempted to manually uncommit an autocommit mapping"); return false; } if (!mapping->address) { // Already uncommitted return true; } if (!platform::uncommitMemory(mapping->address, size)) { // Failed to uncommit? Really? return false; } mapping->address = 0; return true; }
void GLDriver::drawIndex2(const pm4::DrawIndex2 &data) { if (!checkReadyDraw()) { return; } auto vgt_primitive_type = getRegister<latte::VGT_PRIMITIVE_TYPE>(latte::Register::VGT_PRIMITIVE_TYPE); auto sq_vtx_base_vtx_loc = getRegister<latte::SQ_VTX_BASE_VTX_LOC>(latte::Register::SQ_VTX_BASE_VTX_LOC); auto vgt_dma_index_type = getRegister<latte::VGT_DMA_INDEX_TYPE>(latte::Register::VGT_DMA_INDEX_TYPE); auto vgt_dma_num_instances = getRegister<latte::VGT_DMA_NUM_INSTANCES>(latte::Register::VGT_DMA_NUM_INSTANCES); auto vgt_strmout_en = getRegister<latte::VGT_STRMOUT_EN>(latte::Register::VGT_STRMOUT_EN); // Swap and indexBytes are separate because you can have 32-bit swap, // but 16-bit indices in some cases... This is also why we pre-swap // the data before intercepting QUAD and POLYGON draws. if (vgt_dma_index_type.SWAP_MODE() == latte::VGT_DMA_SWAP_16_BIT) { auto *src = static_cast<uint16_t*>(data.addr.get()); auto indices = std::vector<uint16_t>(data.count); if (vgt_dma_index_type.INDEX_TYPE() != latte::VGT_INDEX_16) { decaf_abort(fmt::format("Unexpected INDEX_TYPE {} for VGT_DMA_SWAP_16_BIT", vgt_dma_index_type.INDEX_TYPE())); } for (auto i = 0u; i < data.count; ++i) { indices[i] = byte_swap(src[i]); } drawPrimitives(data.count, indices.data(), vgt_dma_index_type.INDEX_TYPE()); } else if (vgt_dma_index_type.SWAP_MODE() == latte::VGT_DMA_SWAP_32_BIT) { auto *src = static_cast<uint32_t*>(data.addr.get()); auto indices = std::vector<uint32_t>(data.count); if (vgt_dma_index_type.INDEX_TYPE() != latte::VGT_INDEX_32) { decaf_abort(fmt::format("Unexpected INDEX_TYPE {} for VGT_DMA_SWAP_32_BIT", vgt_dma_index_type.INDEX_TYPE())); } for (auto i = 0u; i < data.count; ++i) { indices[i] = byte_swap(src[i]); } drawPrimitives(data.count, indices.data(), vgt_dma_index_type.INDEX_TYPE()); } else if (vgt_dma_index_type.SWAP_MODE() == latte::VGT_DMA_SWAP_NONE) { drawPrimitives(data.count, data.addr, vgt_dma_index_type.INDEX_TYPE()); } else { decaf_abort(fmt::format("Unimplemented vgt_dma_index_type.SWAP_MODE {}", vgt_dma_index_type.SWAP_MODE())); } }
vk::BlendFactor getVkBlendFactor(latte::CB_BLEND_FUNC func) { switch (func) { case latte::CB_BLEND_FUNC::ZERO: return vk::BlendFactor::eZero; case latte::CB_BLEND_FUNC::ONE: return vk::BlendFactor::eOne; case latte::CB_BLEND_FUNC::SRC_COLOR: return vk::BlendFactor::eSrcColor; case latte::CB_BLEND_FUNC::ONE_MINUS_SRC_COLOR: return vk::BlendFactor::eOneMinusSrcColor; case latte::CB_BLEND_FUNC::SRC_ALPHA: return vk::BlendFactor::eSrcAlpha; case latte::CB_BLEND_FUNC::ONE_MINUS_SRC_ALPHA: return vk::BlendFactor::eOneMinusSrcAlpha; case latte::CB_BLEND_FUNC::DST_ALPHA: return vk::BlendFactor::eDstAlpha; case latte::CB_BLEND_FUNC::ONE_MINUS_DST_ALPHA: return vk::BlendFactor::eOneMinusDstAlpha; case latte::CB_BLEND_FUNC::DST_COLOR: return vk::BlendFactor::eDstColor; case latte::CB_BLEND_FUNC::ONE_MINUS_DST_COLOR: return vk::BlendFactor::eOneMinusDstColor; case latte::CB_BLEND_FUNC::SRC_ALPHA_SATURATE: return vk::BlendFactor::eSrcAlphaSaturate; case latte::CB_BLEND_FUNC::BOTH_SRC_ALPHA: decaf_abort("Unsupported BOTH_SRC_ALPHA blend function"); case latte::CB_BLEND_FUNC::BOTH_INV_SRC_ALPHA: decaf_abort("Unsupported BOTH_INV_SRC_ALPHA blend function"); case latte::CB_BLEND_FUNC::CONSTANT_COLOR: return vk::BlendFactor::eConstantColor; case latte::CB_BLEND_FUNC::ONE_MINUS_CONSTANT_COLOR: return vk::BlendFactor::eOneMinusConstantColor; case latte::CB_BLEND_FUNC::SRC1_COLOR: return vk::BlendFactor::eSrc1Color; case latte::CB_BLEND_FUNC::ONE_MINUS_SRC1_COLOR: return vk::BlendFactor::eOneMinusSrc1Color; case latte::CB_BLEND_FUNC::SRC1_ALPHA: return vk::BlendFactor::eSrc1Alpha; case latte::CB_BLEND_FUNC::ONE_MINUS_SRC1_ALPHA: return vk::BlendFactor::eOneMinusSrc1Alpha; case latte::CB_BLEND_FUNC::CONSTANT_ALPHA: return vk::BlendFactor::eConstantAlpha; case latte::CB_BLEND_FUNC::ONE_MINUS_CONSTANT_ALPHA: return vk::BlendFactor::eOneMinusConstantAlpha; default: decaf_abort("Unexpected blend function"); } }
static uint32_t getAlignedBlockSize(virt_ptr<MEMExpHeapBlock> block, uint32_t alignment, MEMExpHeapDirection dir) { if (dir == MEMExpHeapDirection::FromStart) { auto dataStart = virt_cast<uint8_t *>(block) + sizeof(MEMExpHeapBlock); auto dataEnd = dataStart + block->blockSize; auto alignedDataStart = align_up(dataStart, alignment); if (alignedDataStart >= dataEnd) { return 0; } return static_cast<uint32_t>(dataEnd - alignedDataStart); } else if (dir == MEMExpHeapDirection::FromEnd) { auto dataStart = virt_cast<uint8_t *>(block) + sizeof(MEMExpHeapBlock); auto dataEnd = dataStart + block->blockSize; auto alignedDataEnd = align_down(dataEnd, alignment); if (alignedDataEnd <= dataStart) { return 0; } return static_cast<uint32_t>(alignedDataEnd - dataStart); } else { decaf_abort("Unexpected ExpHeap direction"); } }
bool getDataFormatIsFloat(latte::SQ_DATA_FORMAT format) { switch (format) { case latte::SQ_DATA_FORMAT::FMT_16_FLOAT: case latte::SQ_DATA_FORMAT::FMT_32_FLOAT: case latte::SQ_DATA_FORMAT::FMT_16_16_FLOAT: case latte::SQ_DATA_FORMAT::FMT_32_32_FLOAT: case latte::SQ_DATA_FORMAT::FMT_16_16_16_FLOAT: case latte::SQ_DATA_FORMAT::FMT_32_32_32_FLOAT: case latte::SQ_DATA_FORMAT::FMT_16_16_16_16_FLOAT: case latte::SQ_DATA_FORMAT::FMT_32_32_32_32_FLOAT: return true; case latte::SQ_DATA_FORMAT::FMT_8: case latte::SQ_DATA_FORMAT::FMT_16: case latte::SQ_DATA_FORMAT::FMT_32: case latte::SQ_DATA_FORMAT::FMT_8_8: case latte::SQ_DATA_FORMAT::FMT_16_16: case latte::SQ_DATA_FORMAT::FMT_32_32: case latte::SQ_DATA_FORMAT::FMT_8_8_8: case latte::SQ_DATA_FORMAT::FMT_16_16_16: case latte::SQ_DATA_FORMAT::FMT_32_32_32: case latte::SQ_DATA_FORMAT::FMT_2_10_10_10: case latte::SQ_DATA_FORMAT::FMT_10_10_10_2: case latte::SQ_DATA_FORMAT::FMT_8_8_8_8: case latte::SQ_DATA_FORMAT::FMT_16_16_16_16: case latte::SQ_DATA_FORMAT::FMT_32_32_32_32: return false; default: decaf_abort(fmt::format("Unimplemented attribute format: {}", format)); } }
uint32_t getDataFormatComponentBits(latte::SQ_DATA_FORMAT format) { switch (format) { case latte::SQ_DATA_FORMAT::FMT_8: case latte::SQ_DATA_FORMAT::FMT_8_8: case latte::SQ_DATA_FORMAT::FMT_8_8_8: case latte::SQ_DATA_FORMAT::FMT_8_8_8_8: return 8; case latte::SQ_DATA_FORMAT::FMT_16: case latte::SQ_DATA_FORMAT::FMT_16_FLOAT: case latte::SQ_DATA_FORMAT::FMT_16_16: case latte::SQ_DATA_FORMAT::FMT_16_16_FLOAT: case latte::SQ_DATA_FORMAT::FMT_16_16_16: case latte::SQ_DATA_FORMAT::FMT_16_16_16_FLOAT: case latte::SQ_DATA_FORMAT::FMT_16_16_16_16: case latte::SQ_DATA_FORMAT::FMT_16_16_16_16_FLOAT: return 16; case latte::SQ_DATA_FORMAT::FMT_32: case latte::SQ_DATA_FORMAT::FMT_32_FLOAT: case latte::SQ_DATA_FORMAT::FMT_32_32: case latte::SQ_DATA_FORMAT::FMT_32_32_FLOAT: case latte::SQ_DATA_FORMAT::FMT_32_32_32: case latte::SQ_DATA_FORMAT::FMT_32_32_32_FLOAT: case latte::SQ_DATA_FORMAT::FMT_32_32_32_32: case latte::SQ_DATA_FORMAT::FMT_32_32_32_32_FLOAT: return 32; default: decaf_abort(fmt::format("Unimplemented attribute format: {}", format)); } }
static gl::GLenum getTextureTarget(latte::SQ_TEX_DIM dim) { switch (dim) { case latte::SQ_TEX_DIM_1D: return gl::GL_TEXTURE_1D; case latte::SQ_TEX_DIM_2D: return gl::GL_TEXTURE_2D; case latte::SQ_TEX_DIM_3D: return gl::GL_TEXTURE_3D; case latte::SQ_TEX_DIM_CUBEMAP: return gl::GL_TEXTURE_CUBE_MAP; case latte::SQ_TEX_DIM_1D_ARRAY: return gl::GL_TEXTURE_1D_ARRAY; case latte::SQ_TEX_DIM_2D_ARRAY: return gl::GL_TEXTURE_2D_ARRAY; case latte::SQ_TEX_DIM_2D_MSAA: return gl::GL_TEXTURE_2D_MULTISAMPLE; case latte::SQ_TEX_DIM_2D_ARRAY_MSAA: return gl::GL_TEXTURE_2D_MULTISAMPLE_ARRAY; default: decaf_abort(fmt::format("Unimplemented SQ_TEX_DIM {}", dim)); } }
void decreaseIndent(State &state) { if (state.indent.size() >= SingleIndent.size()) { state.indent.resize(state.indent.size() - SingleIndent.size()); } else { decaf_abort("Invalid decrease indent"); } }
void Driver::decafSetBuffer(const latte::pm4::DecafSetBuffer &data) { SwapChainObject **swapChain; if (data.scanTarget == latte::pm4::ScanTarget::TV) { swapChain = &mTvSwapChain; } else if (data.scanTarget == latte::pm4::ScanTarget::DRC) { swapChain = &mDrcSwapChain; } else { decaf_abort("Unexpected decafSetBuffer target"); } // Release the existing swap chain if we had it... if (*swapChain) { releaseSwapChain(*swapChain); *swapChain = nullptr; } // Allocate a new swap chain auto swapChainDesc = SwapChainDesc { data.buffer, data.width, data.height }; auto newSwapChain = allocateSwapChain(swapChainDesc); // Give the swapchain a name so its easy to see. if (data.scanTarget == latte::pm4::ScanTarget::TV) { setVkObjectName(newSwapChain->image, fmt::format("swapchain_tv").c_str()); } else if (data.scanTarget == latte::pm4::ScanTarget::DRC) { setVkObjectName(newSwapChain->image, fmt::format("swapchain_drc").c_str()); } else { decaf_abort("Unexpected decafSetBuffer target"); } // Assign the new swap chain *swapChain = newSwapChain; // Only make this swapchain presentable after this frame has completed // (we need to run the frame at least once for setup to complete before use). addRetireTask([=](){ newSwapChain->presentable = true; }); }
/** * Find the export from a library handle. * * Note that for TLS there will be a symbol, but no section, * and for normal data / code there with be a section but no symbol. */ int OSDynLoad_FindExport(ModuleHandle handle, int isData, char const *name, be_val<ppcaddr_t> *outAddr) { auto module = handle->ptr; auto addr = module->findExport(name); *outAddr = addr; if (!addr) { gLog->debug("OSDynLoad_FindExport export {} not found", name); return 0xBAD10001; } // Let's first try to verify the export type based off the section it is in auto section = module->findAddressSection(addr); if (section) { if (isData) { if (section->type != kernel::loader::LoadedSectionType::Data) { gLog->debug("OSDynLoad_FindExport export {} expected data, found function", name); return 0xBAD10001; } } else { if (section->type != kernel::loader::LoadedSectionType::Code) { gLog->debug("OSDynLoad_FindExport export {} expected function, found data", name); return 0xBAD10001; } } return 0; } // Now let's try to verify it based off it's symbol auto symbol = module->findSymbol(addr); if (symbol) { if (symbol->type == kernel::loader::SymbolType::TLS) { return 0xBAD10033; } else if (isData && symbol->type != kernel::loader::SymbolType::Data) { gLog->debug("OSDynLoad_FindExport export {} expected data, found function", name); return 0xBAD10001; } else if (!isData && symbol->type != kernel::loader::SymbolType::Function) { gLog->debug("OSDynLoad_FindExport export {} expected function, found data", name); return 0xBAD10001; } return 0; } // Couldn't find a section or a symbol for the export, this should never happen...! decaf_abort(fmt::format("OSDynLoad_FindExport could not find symbol or section for export address 0x{:08X}", addr)); return 0xBAD10001; }
static int32_t syscall(uint32_t id) { switch (id) { case GHSSyscallID::time: return syscall_time(); default: decaf_abort(fmt::format("Unsupported ghs syscalls {:x}", id)); } }
static uint32_t * allocateFromPool(uint32_t wantedSize, uint32_t &allocatedSize) { std::unique_lock<std::mutex> lock(sBufferPoolMutex); // Minimum allocation is 0x100 dwords wantedSize = std::max(0x100u, wantedSize); // Lets make sure we are not trying to make an impossible allocation if (wantedSize > sBufferPoolEnd - sBufferPoolBase) { decaf_abort("Command buffer allocation greater than entire pool size"); } uint32_t availableSize = 0; if (sBufferPoolTailPtr == nullptr) { decaf_check(sBufferPoolHeadPtr == sBufferPoolBase); availableSize = static_cast<uint32_t>(sBufferPoolEnd - sBufferPoolBase); sBufferPoolTailPtr = sBufferPoolHeadPtr; } else { if (sBufferPoolHeadPtr < sBufferPoolTailPtr) { availableSize = static_cast<uint32_t>(sBufferPoolTailPtr - sBufferPoolHeadPtr); if (availableSize < wantedSize) { return nullptr; } } else { availableSize = static_cast<uint32_t>(sBufferPoolEnd - sBufferPoolHeadPtr); if (availableSize < wantedSize) { availableSize = static_cast<uint32_t>(sBufferPoolTailPtr - sBufferPoolBase); if (availableSize < wantedSize) { return nullptr; } // Lets mark down that we wasted some space so that returnToPool is able // to verify that pool releases are always happening in-order. Then we // move the head to the base of the pool to allocate from there. sBufferPoolSkipped = static_cast<uint32_t>(sBufferPoolEnd - sBufferPoolHeadPtr); sBufferPoolHeadPtr = sBufferPoolBase; } } } allocatedSize = std::min(0x20000u, availableSize); auto allocatedBuffer = sBufferPoolHeadPtr; sBufferPoolHeadPtr += allocatedSize; return allocatedBuffer; }
static gl::GLenum getTextureXYFilter(latte::SQ_TEX_XY_FILTER filter) { switch (filter) { case latte::SQ_TEX_XY_FILTER_POINT: return gl::GL_NEAREST; case latte::SQ_TEX_XY_FILTER_BILINEAR: return gl::GL_LINEAR; default: decaf_abort(fmt::format("Unimplemented texture xy filter {}", filter)); } }
void Driver::decafCopyColorToScan(const latte::pm4::DecafCopyColorToScan &data) { flushPendingDraws(); ColorBufferDesc colorBuffer; colorBuffer.base256b = data.cb_color_base.BASE_256B(); colorBuffer.pitchTileMax = data.cb_color_size.PITCH_TILE_MAX(); colorBuffer.sliceTileMax = data.cb_color_size.SLICE_TILE_MAX(); colorBuffer.format = data.cb_color_info.FORMAT(); colorBuffer.numberType = data.cb_color_info.NUMBER_TYPE(); colorBuffer.arrayMode = data.cb_color_info.ARRAY_MODE(); colorBuffer.sliceStart = 0; colorBuffer.sliceEnd = 1; auto surfaceView = getColorBuffer(colorBuffer); auto surface = surfaceView->surface; SwapChainObject *target = nullptr; if (data.scanTarget == latte::pm4::ScanTarget::TV) { target = mTvSwapChain; } else if (data.scanTarget == latte::pm4::ScanTarget::DRC) { target = mDrcSwapChain; } else { decaf_abort("decafCopyColorToScan called for unknown scanTarget"); } transitionSurface(surface, ResourceUsage::TransferSrc, vk::ImageLayout::eTransferSrcOptimal, { 0, 1 }); // TODO: We actually need to call AVMSetTVScale inside of the SetBuffer functions // and then pass that data all the way down to here so we can scale correctly. auto copyWidth = target->desc.width; auto copyHeight = target->desc.height; if (surface->desc.width < target->desc.width) { copyWidth = surface->desc.width; } if (surface->desc.height < target->desc.height) { copyHeight = surface->desc.height; } vk::ImageBlit blitRegion( vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), { vk::Offset3D(0, 0, 0), vk::Offset3D(copyWidth, copyHeight, 1) }, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), { vk::Offset3D(0, 0, 0), vk::Offset3D(target->desc.width, target->desc.height, 1) }); mActiveCommandBuffer.blitImage( surface->image, vk::ImageLayout::eTransferSrcOptimal, target->image, vk::ImageLayout::eTransferDstOptimal, { blitRegion }, vk::Filter::eNearest); }
std::string getDataFormatName(latte::SQ_DATA_FORMAT format) { switch (format) { case latte::SQ_DATA_FORMAT::FMT_8: return "FMT_8"; case latte::SQ_DATA_FORMAT::FMT_16: return "FMT_16"; case latte::SQ_DATA_FORMAT::FMT_16_FLOAT: return "FMT_16_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_32: return "FMT_32"; case latte::SQ_DATA_FORMAT::FMT_32_FLOAT: return "FMT_32_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_8_8: return "FMT_8_8"; case latte::SQ_DATA_FORMAT::FMT_16_16: return "FMT_16_16"; case latte::SQ_DATA_FORMAT::FMT_16_16_FLOAT: return "FMT_16_16_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_32_32: return "FMT_32_32"; case latte::SQ_DATA_FORMAT::FMT_32_32_FLOAT: return "FMT_32_32_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_8_8_8: return "FMT_8_8_8"; case latte::SQ_DATA_FORMAT::FMT_16_16_16: return "FMT_16_16_16"; case latte::SQ_DATA_FORMAT::FMT_16_16_16_FLOAT: return "FMT_16_16_16_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_32_32_32: return "FMT_32_32_32"; case latte::SQ_DATA_FORMAT::FMT_32_32_32_FLOAT: return "FMT_32_32_32_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_8_8_8_8: return "FMT_8_8_8_8"; case latte::SQ_DATA_FORMAT::FMT_16_16_16_16: return "FMT_16_16_16_16"; case latte::SQ_DATA_FORMAT::FMT_16_16_16_16_FLOAT: return "FMT_16_16_16_16_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_32_32_32_32: return "FMT_32_32_32_32"; case latte::SQ_DATA_FORMAT::FMT_32_32_32_32_FLOAT: return "FMT_32_32_32_32_FLOAT"; case latte::SQ_DATA_FORMAT::FMT_2_10_10_10: return "FMT_2_10_10_10"; case latte::SQ_DATA_FORMAT::FMT_10_10_10_2: return "FMT_10_10_10_2"; default: decaf_abort(fmt::format("Unimplemented attribute format: {}", format)); } }
static void cpuFaultFiberEntryPoint(void *addr) { // We may have been in the middle of a kernel function... if (coreinit::internal::isSchedulerLocked()) { coreinit::internal::unlockScheduler(); } // Move back an instruction so we can re-exucute the failed instruction // and so that the debugger shows the right stop point. cpu::this_core::state()->nia -= 4; // Alert the debugger if it cares. if (decaf::config::debugger::enabled) { coreinit::internal::pauseCoreTime(true); debugger::handleDbgBreakInterrupt(); coreinit::internal::pauseCoreTime(false); // This will shut down the thread and reschedule. This is required // since returning from the segfault handler is an error. coreinit::OSExitThread(0); } auto core = cpu::this_core::state(); decaf_assert(core, "Uh oh? CPU fault Handler with invalid core"); gLog->critical("{}", coreStateToString(core)); if (sFaultReason == FaultReason::Segfault) { decaf_abort(fmt::format("Invalid memory access for address {:08X} with nia 0x{:08X}\n", sSegfaultAddress, core->nia)); } else if (sFaultReason == FaultReason::IllInst) { decaf_abort(fmt::format("Invalid instruction at nia 0x{:08X}\n", core->nia)); } else { decaf_abort(fmt::format("Unexpected fault occured, fault reason was {} at 0x{:08X}\n", static_cast<uint32_t>(sFaultReason), core->nia)); } }
// Move from Special Purpose Register static bool mfspr(PPCEmuAssembler& a, Instruction instr) { auto spr = decodeSPR(instr); auto dst = a.loadRegisterWrite(a.gpr[instr.rD]); switch (spr) { case SPR::XER: a.mov(dst, a.loadRegisterRead(a.xer)); break; case SPR::LR: a.mov(dst, a.lrMem); break; case SPR::CTR: a.mov(dst, a.ctrMem); break; case SPR::UGQR0: a.mov(dst, a.loadRegisterRead(a.gqr[0])); break; case SPR::UGQR1: a.mov(dst, a.loadRegisterRead(a.gqr[1])); break; case SPR::UGQR2: a.mov(dst, a.loadRegisterRead(a.gqr[2])); break; case SPR::UGQR3: a.mov(dst, a.loadRegisterRead(a.gqr[3])); break; case SPR::UGQR4: a.mov(dst, a.loadRegisterRead(a.gqr[4])); break; case SPR::UGQR5: a.mov(dst, a.loadRegisterRead(a.gqr[5])); break; case SPR::UGQR6: a.mov(dst, a.loadRegisterRead(a.gqr[6])); break; case SPR::UGQR7: a.mov(dst, a.loadRegisterRead(a.gqr[7])); break; case SPR::UPIR: a.mov(dst, a.coreIdMem); break; default: decaf_abort(fmt::format("Invalid mfspr SPR {}", static_cast<uint32_t>(spr))); } return true; }
latte::SQ_TILE_MODE getArrayModeTileMode(latte::BUFFER_ARRAY_MODE mode) { switch (mode) { case latte::BUFFER_ARRAY_MODE::LINEAR_GENERAL: return latte::SQ_TILE_MODE::DEFAULT; case latte::BUFFER_ARRAY_MODE::LINEAR_ALIGNED: return latte::SQ_TILE_MODE::LINEAR_ALIGNED; case latte::BUFFER_ARRAY_MODE::TILED_2D_THIN1: return latte::SQ_TILE_MODE::TILED_2D_THIN1; default: decaf_abort(fmt::format("Unimplemented surface array mode: {}", mode)); } }
std::string controllerTypeToString(ControllerType type) { switch (type) { case None: return "none"; case Keyboard: return "keyboard"; case Joystick: return "joystick"; } decaf_abort(fmt::format("Invalid controller type {}", type)); }
void OSEnableOverlayArena(uint32_t unk, be_val<uint32_t> *addr, be_val<uint32_t> *size) { if (!sOverlayArenaEnabled) { if (!mem::commit(mem::OverlayArenaBase, mem::OverlayArenaSize)) { decaf_abort("Failed to allocate loader overlay memory"); } sOverlayArenaEnabled = true; } OSGetOverlayArenaRange(addr, size); }
gl::GLenum getBlendFunc(latte::CB_BLEND_FUNC func) { switch (func) { case latte::CB_BLEND_ZERO: return gl::GL_ZERO; case latte::CB_BLEND_ONE: return gl::GL_ONE; case latte::CB_BLEND_SRC_COLOR: return gl::GL_SRC_COLOR; case latte::CB_BLEND_ONE_MINUS_SRC_COLOR: return gl::GL_ONE_MINUS_SRC_COLOR; case latte::CB_BLEND_SRC_ALPHA: return gl::GL_SRC_ALPHA; case latte::CB_BLEND_ONE_MINUS_SRC_ALPHA: return gl::GL_ONE_MINUS_SRC_ALPHA; case latte::CB_BLEND_DST_ALPHA: return gl::GL_DST_ALPHA; case latte::CB_BLEND_ONE_MINUS_DST_ALPHA: return gl::GL_ONE_MINUS_DST_ALPHA; case latte::CB_BLEND_DST_COLOR: return gl::GL_DST_COLOR; case latte::CB_BLEND_ONE_MINUS_DST_COLOR: return gl::GL_ONE_MINUS_DST_COLOR; case latte::CB_BLEND_SRC_ALPHA_SATURATE: return gl::GL_SRC_ALPHA_SATURATE; case latte::CB_BLEND_CONSTANT_COLOR: return gl::GL_CONSTANT_COLOR; case latte::CB_BLEND_ONE_MINUS_CONSTANT_COLOR: return gl::GL_ONE_MINUS_CONSTANT_COLOR; case latte::CB_BLEND_SRC1_COLOR: return gl::GL_SRC1_COLOR; case latte::CB_BLEND_ONE_MINUS_SRC1_COLOR: return gl::GL_ONE_MINUS_SRC1_COLOR; case latte::CB_BLEND_SRC1_ALPHA: return gl::GL_SRC1_ALPHA; case latte::CB_BLEND_ONE_MINUS_SRC1_ALPHA: return gl::GL_ONE_MINUS_SRC1_ALPHA; case latte::CB_BLEND_CONSTANT_ALPHA: return gl::GL_CONSTANT_ALPHA; case latte::CB_BLEND_ONE_MINUS_CONSTANT_ALPHA: return gl::GL_ONE_MINUS_CONSTANT_ALPHA; default: decaf_abort(fmt::format("Unimplemented CB_BLEND_FUNC {}", func)); } }
static uint32_t appIoThreadEntry(uint32_t coreId, void *arg2) { auto queue = &sAppIo->queues[coreId]; OSInitMessageQueue(queue, &sAppIo->messages[coreId * MessagesPerCore], MessagesPerCore); while (true) { OSMessage msg; OSReceiveMessage(queue, &msg, OSMessageFlags::Blocking); auto funcType = static_cast<OSFunctionType>(msg.args[2].value()); switch (funcType) { case OSFunctionType::FsaCmdAsync: { auto result = FSAGetAsyncResult(&msg); if (result->userCallback) { result->userCallback(result->error, result->command, result->request, result->response, result->userContext); } break; } case OSFunctionType::FsCmdAsync: { auto result = FSGetAsyncResult(&msg); if (result->asyncData.userCallback) { result->asyncData.userCallback(result->client, result->block, result->status, result->asyncData.userContext); } break; } case OSFunctionType::FsCmdHandler: { fsCmdBlockHandleResult(reinterpret_cast<FSCmdBlockBody *>(msg.message.get())); break; } default: decaf_abort(fmt::format("Unimplemented OSFunctionType {}", funcType)); } } }
bool DecafSDL::initVulkanGraphics() { #ifdef DECAF_VULKAN mGraphicsDriver = new DecafSDLVulkan(); if (!mGraphicsDriver->initialise(WindowWidth, WindowHeight)) { gCliLog->error("Failed to create Vulkan graphics window"); return false; } sActiveGfx = "Vulkan"; return true; #else decaf_abort("Vulkan support was not included in this build"); #endif }
bool DecafSDL::initDx12Graphics() { #ifdef DECAF_DX12 mGraphicsDriver = new DecafSDLDX12(); if (!mGraphicsDriver->initialise(WindowWidth, WindowHeight)) { gCliLog->error("Failed to create DX12 graphics window"); return false; } sActiveGfx = "DX12"; return true; #else decaf_abort("DX12 support was not included in this build"); #endif }
bool DecafSDL::initGlGraphics() { #ifdef DECAF_GL mGraphicsDriver = new DecafSDLOpenGL(); if (!mGraphicsDriver->initialise(WindowWidth, WindowHeight)) { gCliLog->error("Failed to create GL graphics window"); return false; } sActiveGfx = "GL"; return true; #else decaf_abort("GL support was not included in this build"); #endif }
std::string disassemble(const gsl::span<const uint8_t> &binary, bool isSubroutine) { disassembler::State state; state.binary = binary; state.cfPC = 0; state.groupPC = 0; for (auto i = 0; i < binary.size(); i += sizeof(ControlFlowInst)) { auto cf = *reinterpret_cast<const ControlFlowInst *>(binary.data() + i); auto id = cf.word1.CF_INST(); auto type = cf.word1.CF_INST_TYPE(); switch (type) { case SQ_CF_INST_TYPE_NORMAL: disassembler::disassembleNormal(state, cf); break; case SQ_CF_INST_TYPE_EXPORT: disassembler::disassembleExport(state, cf); break; case SQ_CF_INST_TYPE_ALU: case SQ_CF_INST_TYPE_ALU_EXTENDED: disassembler::disassembleControlFlowALU(state, cf); break; default: decaf_abort(fmt::format("Invalid top level instruction type {}", type)); } if (cf.word1.CF_INST() == SQ_CF_INST_RETURN && isSubroutine) { break; } if (cf.word1.CF_INST_TYPE() == SQ_CF_INST_TYPE_NORMAL || cf.word1.CF_INST_TYPE() == SQ_CF_INST_TYPE_EXPORT) { if (cf.word1.END_OF_PROGRAM()) { break; } } state.cfPC++; state.out << "\n"; } return state.out.str(); }
static bool spinReleaseLock(OSSpinLock *spinlock) { auto thread = OSGetCurrentThread(); auto owner = mem::untranslate(thread); if (spinlock->recursion > 0u) { --spinlock->recursion; return false; } else if (spinlock->owner.load(std::memory_order_relaxed) == owner) { spinlock->owner = 0u; decreaseSpinLockCount(thread); return true; } decaf_abort("Attempt to release spin lock which is not owned."); return true; }
gl::GLenum getBlendEquation(latte::CB_COMB_FUNC func) { switch (func) { case latte::CB_COMB_DST_PLUS_SRC: return gl::GL_FUNC_ADD; case latte::CB_COMB_SRC_MINUS_DST: return gl::GL_FUNC_SUBTRACT; case latte::CB_COMB_MIN_DST_SRC: return gl::GL_MIN; case latte::CB_COMB_MAX_DST_SRC: return gl::GL_MAX; case latte::CB_COMB_DST_MINUS_SRC: return gl::GL_FUNC_REVERSE_SUBTRACT; default: decaf_abort(fmt::format("Unimplemented CB_COMB_FUNC {}", func)); } }
static gl::GLenum getCompressedTextureDataType(latte::SQ_DATA_FORMAT format, uint32_t degamma) { switch (format) { case latte::FMT_BC1: return degamma ? gl::GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT : gl::GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; case latte::FMT_BC2: return degamma ? gl::GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT : gl::GL_COMPRESSED_RGBA_S3TC_DXT3_EXT; case latte::FMT_BC3: return degamma ? gl::GL_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT : gl::GL_COMPRESSED_RGBA_S3TC_DXT5_EXT; case latte::FMT_BC4: return degamma ? gl::GL_COMPRESSED_SIGNED_RED_RGTC1 : gl::GL_COMPRESSED_RED_RGTC1; case latte::FMT_BC5: return degamma ? gl::GL_COMPRESSED_SIGNED_RG_RGTC2 : gl::GL_COMPRESSED_RG_RGTC2; default: decaf_abort(fmt::format("Unimplemented compressed texture format {}", format)); } }
vk::BlendOp getVkBlendOp(latte::CB_COMB_FUNC func) { switch (func) { case latte::CB_COMB_FUNC::DST_PLUS_SRC: return vk::BlendOp::eAdd; case latte::CB_COMB_FUNC::SRC_MINUS_DST: return vk::BlendOp::eSubtract; case latte::CB_COMB_FUNC::MIN_DST_SRC: return vk::BlendOp::eMin; case latte::CB_COMB_FUNC::MAX_DST_SRC: return vk::BlendOp::eMax; case latte::CB_COMB_FUNC::DST_MINUS_SRC: return vk::BlendOp::eReverseSubtract; default: decaf_abort("Unexpected blend op"); } }