# Xenia → Rust Port: Technical Feasibility Report v2.0 This report consolidates the full analysis from both the JIT-based and interpreter-based approaches, grounded in the actual codebase structure. --- ## 1. Project Architecture Xenia is organized into ~12 major subsystems, all owned by the central `Emulator` class: ``` src/xenia/ ├── app/ # Entry point, premake build ├── apu/ # Audio (XAudio2, SDL2 backends) ├── base/ # Platform abstractions, threading, memory ├── cpu/ # PPC frontend, HIR, compiler passes, x64 JIT backend │ ├── ppc/ # Decoder, HIR emitters, scanner │ ├── hir/ # HIR opcodes, builder, values │ ├── compiler/ # Optimization passes │ └── backend/x64/ # Xbyak-based x64 emitter ├── gpu/ # Xenos GPU, DXBC/SPIR-V shader translators, shader interpreter ├── hid/ # Input (SDL2, XInput, etc.) ├── kernel/ # HLE kernel (xboxkrnl/, xam/) ├── vfs/ # Virtual filesystem (disc image, STFS, ZArchive) ├── ui/ # ImGui, Vulkan/D3D12 window ├── memory.cc/.h # 4GB+ guest address space └── emulator.cc/.h ``` [1](#2-0) --- ## 2. The CPU Pipeline in Detail The current execution path for guest code is: ``` PPC binary → PPCScanner (basic blocks) → PPCHIRBuilder (HIR) → Compiler passes (CFG, simplification, const-prop, DCE, regalloc) → X64Emitter (Xbyak) → native x64 machine code ``` [2](#2-1) There is **no existing PPC interpreter**. The `ppc_emit_*.cc` files emit HIR, not execute instructions directly. The `PPCScanner` does basic block analysis for the JIT: [3](#2-2) The register file is `PPCContext`: 32 GPRs, 32 FPRs, 128 VMX registers, plus LR/CTR/XER/CR: [4](#2-3) Each guest thread owns a `ThreadState` which holds a `PPCContext*`: [5](#2-4) The full `PPCOpcode` enum covers ~250 opcodes including many Xbox 360-specific VMX128 extensions: [6](#2-5) --- ## 3. The Two Approaches Compared ### Approach A: JIT Port (keep the JIT, rewrite everything else in Rust) The JIT backend (`X64Emitter`) inherits from `Xbyak::CodeGenerator` — a C++ header-only library with no Rust equivalent: [7](#2-6) The MMIO system relies on hardware exceptions (`SIGSEGV`/`VEH`) to intercept guest memory accesses made by JIT-compiled code, then decodes the faulting x64 instruction at runtime: [8](#2-7) This is fundamentally incompatible with safe Rust. Signal handlers in Rust are `unsafe` and severely restricted in what they can do. The host↔guest thunks emit raw x64 at runtime and depend on `PPCContext` being at hardcoded `offsetof` positions: [9](#2-8) **Verdict:** The JIT approach requires keeping the entire `cpu/backend/x64/` subtree in C++ and bridging via FFI. The `cxx` crate is the most ergonomic tool for this, but the FFI boundary would be complex and the safety gains minimal in the hottest code path. --- ### Approach B: Interpreter Port (execute PPC instructions directly, no JIT) This eliminates the three hardest blockers: | Blocker | JIT | Interpreter | |---|---|---| | Xbyak / x64 codegen | Blocker | Eliminated | | MMIO exception handler | Blocker | Eliminated | | Host↔guest ABI thunks | Blocker | Eliminated | | `PPCContext` layout coupling to JIT | Hard | Eliminated | In an interpreter, every memory access is a function call. The codebase already has the explicit MMIO-check pattern in `MMIOAwareLoad`: [10](#2-9) And `MMIOHandler` exposes `CheckLoad`/`CheckStore` for exactly this: [11](#2-10) The opcode dispatch infrastructure (`LookupOpcode`, `PPCDecodeData`, `PPCOpcode` enum) is pure data-manipulation code with no JIT dependency — directly portable to Rust: [12](#2-11) The `ppc_emit_*.cc` files serve as a complete semantic reference for every instruction, even though they emit HIR rather than executing directly. The VMX128 instruction encoding macros are documented there: [13](#2-12) **The dominant cost of Approach B is performance.** The Xbox 360 has 3 PPC cores × 2 hardware threads. A naive interpreter runs ~20–100× slower than JIT. Most 3D games would be unplayable. --- ## 4. Subsystem-by-Subsystem Feasibility ### 4.1 Memory System — `unsafe`, but contained The 4GB+ virtual address space reservation via OS file mappings is required regardless of approach: [14](#2-13) This is `unsafe` Rust via `libc`/`windows-sys`, but it is a well-defined, bounded `unsafe` block. The `xe::be` big-endian wrapper maps cleanly to a Rust newtype: [15](#2-14) ### 4.2 Kernel HLE — Medium difficulty The kernel has a massive HLE surface: `xboxkrnl_table.inc` alone is 96KB, covering threading, memory, I/O, RTL, video, audio, crypto, and more. The `X_KTHREAD` struct is a packed big-endian layout that lives in guest memory: [16](#2-15) In Rust, this becomes a `#[repr(C, packed)]` struct with `be` newtypes. The `unsafe` is explicit and auditable. The threading HLE (`xboxkrnl_threading.cc` at 66KB) is the most complex piece. ### 4.3 GPU — Hard The GPU has two shader translation pipelines (DXBC and SPIR-V) totaling tens of thousands of lines, translating Xenos microcode to host shader IR. The SPIR-V translator uses the glslang `SpirvBuilder` C++ API directly: [17](#2-16) There is no Rust equivalent of `SpirvBuilder`. The `rspirv` crate provides partial SPIR-V construction but lacks the full feature set. **Notable:** The GPU subsystem already has a `ShaderInterpreter` for Xenos microcode — used for internal rectangles and clears where texture fetches are not needed: [18](#2-17) This demonstrates the pattern: interpreter as a correctness baseline, translator for performance. ### 4.4 VFS — High feasibility The VFS devices (disc image, ZArchive, STFS/XContent) are pure data-parsing code with no platform dependencies beyond file I/O: These are ideal candidates for safe Rust. The `stfs_xbox.h` format definitions (15KB of packed structs) translate directly to `#[repr(C)]` Rust structs. ### 4.5 Concurrency — Improved by Rust The codebase uses custom spinlocks (`xe_fast_mutex`, `xe_global_mutex`, `xe_unlikely_mutex`) alongside `std::atomic`. Rust's `Send`/`Sync` traits would catch data races at compile time that are currently only caught at runtime: [19](#2-18) ### 4.6 Third-party C++ dependencies | Library | Purpose | Rust status | |---|---|---| | Xbyak | x64 JIT codegen | No equivalent; `dynasm-rs` is limited | | glslang/SPIRV-Tools | Shader compilation | `rspirv` (partial) | | capstone | x86 disassembly | `capstone` crate (binding) | | Dear ImGui | UI | `imgui-rs` (binding) | | FFmpeg | Audio/video decode | `ffmpeg-next` (binding) | | D3D12/XAudio2 | Windows GPU/audio | `windows-rs` (binding) | | Vulkan | GPU | `ash` (good) | | snappy/xxhash | Compression/hashing | Native Rust crates exist | [20](#2-19) --- ## 5. The Interpreter + Basic Block Cache Path The most realistic path to a working Rust port that is both architecturally feasible and eventually performant: ``` Phase 1: Pure interpreter - PPCContext as #[repr(C)] Rust struct - match on PPCOpcode enum → one fn per opcode - Explicit MMIO check on every load/store (no signal handlers) - Correct but slow (~20-100x slower than JIT) Phase 2: Basic block cache - PPCScanner already identifies basic block boundaries - Cache translated basic blocks as Vec - Invalidate on self-modifying code - Recovers significant performance without a full JIT Phase 3: Optional: LLVM via inkwell - Replace the basic block cache with LLVM IR generation - Eliminates Xbyak entirely; LLVM handles x64/ARM64 - Major architectural change but Rust-native ``` The `PPCScanner` already provides the basic block boundary detection needed for Phase 2: [3](#2-2) --- ## 6. Summary Matrix ``` Subsystem | Approach A (JIT) | Approach B (Interpreter) ---------------------------|------------------|------------------------- VFS / file formats | High | High Config / logging | High | High Kernel HLE (non-threading) | Medium | Medium Kernel threading HLE | Low-Medium | Low-Medium Memory system | Low (unsafe) | Low (unsafe, same) GPU shader translation | Very Low | Very Low (same) VMX128 instructions | Hard (HIR emit) | Hard (fn per opcode) lwarx/stwcx atomics | Hard (x64 asm) | Medium (AtomicU32 CAS) MMIO handler | Blocker | Eliminated x64 JIT / Xbyak | Blocker | Eliminated Host↔guest thunks | Blocker | Eliminated Performance | Good | Poor (Phase 1) ``` **Bottom line:** A full JIT port is not practical without either keeping the JIT in C++ (hybrid) or replacing Xbyak with LLVM. An interpreter port is architecturally feasible in Rust and eliminates the three hardest blockers, but requires a basic block cache layer to approach playable performance. The GPU shader translation pipeline is the second major obstacle regardless of CPU approach, with no clean Rust-native equivalent of the glslang `SpirvBuilder` API. ### Citations **File:** src/xenia/emulator.h (L336-349) ```text std::unique_ptr memory_; std::unique_ptr processor_; std::unique_ptr audio_system_; std::unique_ptr audio_media_player_; std::unique_ptr graphics_system_; std::unique_ptr input_system_; std::unique_ptr export_resolver_; std::unique_ptr file_system_; std::unique_ptr patcher_; std::unique_ptr plugin_loader_; std::unique_ptr kernel_state_; ``` **File:** src/xenia/cpu/ppc/ppc_translator.cc (L44-112) ```text PPCTranslator::PPCTranslator(PPCFrontend* frontend) : frontend_(frontend) { Backend* backend = frontend->processor()->backend(); scanner_.reset(new PPCScanner(frontend)); builder_.reset(new PPCHIRBuilder(frontend)); compiler_.reset(new Compiler(frontend->processor())); assembler_ = backend->CreateAssembler(); assembler_->Initialize(); bool validate = cvars::validate_hir; // Merge blocks early. This will let us use more context in other passes. // The CFG is required for simplification and dirtied by it. compiler_->AddPass(std::make_unique()); compiler_->AddPass(std::make_unique()); // Passes are executed in the order they are added. Multiple of the same // pass type may be used. if (!cvars::disable_context_promotion) { if (validate) { compiler_->AddPass(std::make_unique()); } compiler_->AddPass(std::make_unique()); if (validate) { compiler_->AddPass(std::make_unique()); } } // Grouped simplification + constant propagation. // Loops until no changes are made. auto sap = std::make_unique(); sap->AddPass(std::make_unique()); if (validate) sap->AddPass(std::make_unique()); sap->AddPass(std::make_unique()); if (validate) sap->AddPass(std::make_unique()); compiler_->AddPass(std::move(sap)); if (backend->machine_info()->supports_extended_load_store) { // Backend supports the advanced LOAD/STORE instructions. // These will save us a lot of HIR opcodes. compiler_->AddPass( std::make_unique()); if (validate) compiler_->AddPass(std::make_unique()); } compiler_->AddPass(std::make_unique()); if (validate) compiler_->AddPass(std::make_unique()); // compiler_->AddPass(std::make_unique()); // if (validate) // compiler_->AddPass(std::make_unique()); compiler_->AddPass(std::make_unique()); if (validate) compiler_->AddPass(std::make_unique()); //// Removes all unneeded variables. Try not to add new ones after this. // compiler_->AddPass(new passes::ValueReductionPass()); // if (validate) compiler_->AddPass(new passes::ValidationPass()); // Register allocation for the target backend. // Will modify the HIR to add loads/stores. // This should be the last pass before finalization, as after this all // registers are assigned and ready to be emitted. compiler_->AddPass(std::make_unique( backend->machine_info())); if (validate) compiler_->AddPass(std::make_unique()); // Must come last. The HIR is not really HIR after this. compiler_->AddPass(std::make_unique()); } ``` **File:** src/xenia/cpu/ppc/ppc_scanner.h (L29-42) ```text class PPCScanner { public: explicit PPCScanner(PPCFrontend* frontend); ~PPCScanner(); bool Scan(GuestFunction* function, FunctionDebugInfo* debug_info); std::vector FindBlocks(GuestFunction* function); private: bool IsRestGprLr(uint32_t address); PPCFrontend* frontend_ = nullptr; }; ``` **File:** src/xenia/cpu/ppc/ppc_context.h (L376-400) ```text // Most frequently used registers first. uint64_t r[32]; // 0x20 General purpose registers uint64_t ctr; // 0x18 Count register uint64_t lr; // 0x10 Link register uint64_t msr; // machine state register double f[32]; // 0x120 Floating-point registers vec128_t v[128]; // 0x220 VMX128 vector registers vec128_t vscr_vec; // XER register: // Split to make it easier to do individual updates. uint8_t xer_ca; uint8_t xer_ov; uint8_t xer_so; // Condition registers: // These are split to make it easier to do DCE on unused stores. uint64_t cr() const; void set_cr(uint64_t value); // todo: remove, saturation should be represented by a vector uint8_t vscr_sat; uint32_t vrsave; ``` **File:** src/xenia/cpu/thread_state.h (L24-50) ```text class ThreadState { public: ThreadState(Processor* processor, uint32_t thread_id, uint32_t stack_base = 0, uint32_t pcr_address = 0); ~ThreadState(); Processor* processor() const { return processor_; } Memory* memory() const { return memory_; } void* backend_data() const { return backend_data_; } ppc::PPCContext* context() const { return context_; } uint32_t thread_id() const { return thread_id_; } static void Bind(ThreadState* thread_state); static ThreadState* Get(); static uint32_t GetThreadID(); private: Processor* processor_; Memory* memory_; void* backend_data_; uint32_t pcr_address_ = 0; uint32_t thread_id_ = 0; // NOTE: must be 64b aligned for SSE ops. ppc::PPCContext* context_; }; ``` **File:** src/xenia/cpu/ppc/ppc_opcode.h (L14-200) ```text enum class PPCOpcode : uint32_t { addcx, addex, addi, addic, addicx, addis, addmex, addx, addzex, andcx, andisx, andix, andx, bcctrx, bclrx, bcx, bx, cmp, cmpi, cmpl, cmpli, cntlzdx, cntlzwx, crand, crandc, creqv, crnand, crnor, cror, crorc, crxor, dcbf, dcbi, dcbst, dcbt, dcbtst, dcbz, dcbz128, divdux, divdx, divwux, divwx, eieio, eqvx, extsbx, extshx, extswx, fabsx, faddsx, faddx, fcfidx, fcmpo, fcmpu, fctidx, fctidzx, fctiwx, fctiwzx, fdivsx, fdivx, fmaddsx, fmaddx, fmrx, fmsubsx, fmsubx, fmulsx, fmulx, fnabsx, fnegx, fnmaddsx, fnmaddx, fnmsubsx, fnmsubx, fresx, frspx, frsqrtex, fselx, fsqrtsx, fsqrtx, fsubsx, fsubx, icbi, isync, lbz, lbzu, lbzux, lbzx, ld, ldarx, ldbrx, ldu, ldux, ldx, lfd, lfdu, lfdux, lfdx, lfs, lfsu, lfsux, lfsx, lha, lhau, lhaux, lhax, lhbrx, lhz, lhzu, lhzux, lhzx, lmw, lswi, lswx, lvebx, lvehx, lvewx, lvewx128, lvlx, lvlx128, lvlxl, lvlxl128, lvrx, lvrx128, lvrxl, lvrxl128, lvsl, lvsl128, lvsr, lvsr128, lvx, lvx128, lvxl, lvxl128, lwa, lwarx, lwaux, lwax, lwbrx, lwz, lwzu, lwzux, lwzx, mcrf, mcrfs, mcrxr, mfcr, mffsx, mfmsr, mfspr, mftb, mfvscr, mtcrf, mtfsb0x, mtfsb1x, mtfsfix, mtfsfx, mtmsr, mtmsrd, mtspr, mtvscr, mulhdux, mulhdx, mulhwux, mulhwx, mulldx, mulli, mullwx, nandx, negx, norx, orcx, ori, oris, orx, rldclx, rldcrx, rldiclx, rldicrx, rldicx, rldimix, rlwimix, rlwinmx, rlwnmx, sc, sldx, slwx, sradix, ``` **File:** src/xenia/cpu/backend/x64/x64_emitter.h (L208-212) ```text class X64Emitter : public Xbyak::CodeGenerator { public: X64Emitter(X64Backend* backend, XbyakAllocator* allocator); virtual ~X64Emitter(); ``` **File:** src/xenia/cpu/mmio_handler.cc (L402-470) ```text bool MMIOHandler::ExceptionCallback(Exception* ex) { if (ex->code() != Exception::Code::kAccessViolation) { return false; } Exception::AccessViolationOperation operation = ex->access_violation_operation(); if (operation != Exception::AccessViolationOperation::kRead && operation != Exception::AccessViolationOperation::kWrite) { // Data Execution Prevention or something else uninteresting. return false; } bool is_write = operation == Exception::AccessViolationOperation::kWrite; if (ex->fault_address() < uint64_t(virtual_membase_) || ex->fault_address() > uint64_t(memory_end_)) { // Quick kill anything outside our mapping. return false; } void* fault_host_address = reinterpret_cast(ex->fault_address()); // Access violations are pretty rare, so we can do a linear search here. // Only check if in the virtual range, as we only support virtual ranges. const MMIORange* range = nullptr; uint32_t fault_guest_virtual_address = 0; if (ex->fault_address() < uint64_t(physical_membase_)) { fault_guest_virtual_address = host_to_guest_virtual_( host_to_guest_virtual_context_, fault_host_address); for (const auto& test_range : mapped_ranges_) { if ((fault_guest_virtual_address & test_range.mask) == test_range.address) { // Address is within the range of this mapping. range = &test_range; break; } } } if (!range) { // Recheck if the pages are still protected (race condition - another thread // clears the watch we just hit). // Do this under the lock so we don't introduce another race condition. auto lock = global_critical_region_.Acquire(); memory::PageAccess cur_access; size_t page_length = memory::page_size(); memory::QueryProtect(fault_host_address, page_length, cur_access); if (cur_access != memory::PageAccess::kNoAccess && (!is_write || cur_access != memory::PageAccess::kReadOnly)) { // Another thread has cleared this watch. Abort. XELOGD("Race condition on watch, was already cleared by another thread!"); return true; } // The address is not found within any range, so either a write watch or an // actual access violation. if (access_violation_callback_) { return access_violation_callback_(std::move(lock), access_violation_callback_context_, fault_host_address, is_write); } return false; } auto rip = ex->pc(); auto p = reinterpret_cast(rip); DecodedLoadStore decoded_load_store; if (!TryDecodeLoadStore(p, decoded_load_store)) { XELOGE("Unable to decode MMIO load or store instruction at {}", static_cast(p)); assert_always("Unknown MMIO instruction type"); return false; } ``` **File:** src/xenia/cpu/backend/x64/x64_backend.cc (L630-726) ```text HostToGuestThunk X64HelperEmitter::EmitHostToGuestThunk() { #ifdef XE_PLATFORM_WIN32 // rcx = target // rdx = arg0 (context) // r8 = arg1 (guest return address) _code_offsets code_offsets = {}; constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; code_offsets.prolog = getSize(); // rsp + 0 = return address mov(qword[rsp + 8 * 3], r8); mov(qword[rsp + 8 * 2], rdx); mov(qword[rsp + 8 * 1], rcx); sub(rsp, stack_size); code_offsets.prolog_stack_alloc = getSize(); code_offsets.body = getSize(); // Save nonvolatile registers. EmitSaveNonvolatileRegs(); mov(rax, rcx); mov(rsi, rdx); // context mov(rdi, ptr[rdx + offsetof(ppc::PPCContext, virtual_membase)]); // membase mov(rcx, r8); // return address call(rax); vzeroupper(); EmitLoadNonvolatileRegs(); code_offsets.epilog = getSize(); add(rsp, stack_size); mov(rcx, qword[rsp + 8 * 1]); mov(rdx, qword[rsp + 8 * 2]); mov(r8, qword[rsp + 8 * 3]); ret(); #elif XE_PLATFORM_LINUX || XE_PLATFORM_MAC // System-V ABI args: // rdi = target // rsi = arg0 (context) // rdx = arg1 (guest return address) struct _code_offsets { size_t prolog; size_t prolog_stack_alloc; size_t body; size_t epilog; size_t tail; } code_offsets = {}; constexpr size_t stack_size = StackLayout::THUNK_STACK_SIZE; code_offsets.prolog = getSize(); // rsp + 0 = return address sub(rsp, stack_size); code_offsets.prolog_stack_alloc = getSize(); code_offsets.body = getSize(); // Save nonvolatile registers. EmitSaveNonvolatileRegs(); mov(rax, rdi); // mov(rsi, rsi); // context mov(rdi, ptr[rsi + offsetof(ppc::PPCContext, virtual_membase)]); // membase mov(rcx, rdx); // return address call(rax); EmitLoadNonvolatileRegs(); code_offsets.epilog = getSize(); add(rsp, stack_size); ret(); #else assert_always("Unknown platform ABI in host to guest thunk!"); #endif code_offsets.tail = getSize(); assert_zero(code_offsets.prolog); EmitFunctionInfo func_info = {}; func_info.code_size.total = getSize(); func_info.code_size.prolog = code_offsets.body - code_offsets.prolog; func_info.code_size.body = code_offsets.epilog - code_offsets.body; func_info.code_size.epilog = code_offsets.tail - code_offsets.epilog; func_info.code_size.tail = getSize() - code_offsets.tail; func_info.prolog_stack_alloc_offset = code_offsets.prolog_stack_alloc - code_offsets.prolog; func_info.stack_size = stack_size; void* fn = Emplace(func_info); return (HostToGuestThunk)fn; } ``` **File:** src/xenia/cpu/backend/x64/x64_seq_memory.cc (L1215-1237) ```text template static T MMIOAwareLoad(void* _ctx, unsigned int guestaddr) { T value; if (guestaddr >= 0xE0000000) { guestaddr += 0x1000; } auto ctx = reinterpret_cast(_ctx); auto gaddr = ctx->processor->memory()->LookupVirtualMappedRange(guestaddr); if (!gaddr) { value = *reinterpret_cast(ctx->virtual_membase + guestaddr); if (swap) { value = xe::byte_swap(value); } } else { /* was having issues, found by comparing the values used with exceptions to these that we were reversed... */ value = gaddr->read(nullptr, gaddr->callback_context, guestaddr); } ``` **File:** src/xenia/cpu/mmio_handler.h (L73-74) ```text bool CheckLoad(uint32_t virtual_address, uint32_t* out_value); bool CheckStore(uint32_t virtual_address, uint32_t value); ``` **File:** src/xenia/cpu/ppc/ppc_opcode_lookup_gen.cc (L261-285) ```text switch ((ExtractBits(code, 22, 25) << 2)|(ExtractBits(code, 27, 27) << 0)) { case 0b000101: PPC_DECODER_HIT(vrlw128); case 0b001101: PPC_DECODER_HIT(vslw128); case 0b010101: PPC_DECODER_HIT(vsraw128); case 0b011101: PPC_DECODER_HIT(vsrw128); case 0b101000: PPC_DECODER_HIT(vmaxfp128); case 0b101100: PPC_DECODER_HIT(vminfp128); case 0b110000: PPC_DECODER_HIT(vmrghw128); case 0b110100: PPC_DECODER_HIT(vmrglw128); case 0b111000: PPC_DECODER_HIT(vupkhsb128); case 0b111100: PPC_DECODER_HIT(vupklsb128); } PPC_DECODER_MISS; case 7: PPC_DECODER_HIT(mulli); case 8: PPC_DECODER_HIT(subficx); case 10: PPC_DECODER_HIT(cmpli); case 11: PPC_DECODER_HIT(cmpi); case 12: PPC_DECODER_HIT(addic); case 13: PPC_DECODER_HIT(addicx); case 14: PPC_DECODER_HIT(addi); case 15: PPC_DECODER_HIT(addis); case 16: PPC_DECODER_HIT(bcx); case 17: PPC_DECODER_HIT(sc); case 18: PPC_DECODER_HIT(bx); case 19: ``` **File:** src/xenia/cpu/ppc/ppc_emit_altivec.cc (L36-50) ```text #define OP(x) ((((uint32_t)(x)) & 0x3f) << 26) #define VX128(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x3d0)) #define VX128_1(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x7f3)) #define VX128_2(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x210)) #define VX128_3(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x7f0)) #define VX128_4(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x730)) #define VX128_5(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x10)) #define VX128_P(op, xop) (OP(op) | (((uint32_t)(xop)) & 0x630)) #define VX128_VD128 (i.VX128.VD128l | (i.VX128.VD128h << 5)) #define VX128_VA128 \ (i.VX128.VA128l | (i.VX128.VA128h << 5) | (i.VX128.VA128H << 6)) #define VX128_VB128 (i.VX128.VB128l | (i.VX128.VB128h << 5)) #define VX128_1_VD128 (i.VX128_1.VD128l | (i.VX128_1.VD128h << 5)) #define VX128_2_VD128 (i.VX128_2.VD128l | (i.VX128_2.VD128h << 5)) ``` **File:** src/xenia/memory.cc (L137-168) ```text bool Memory::Initialize() { file_name_ = fmt::format("xenia_memory_{}", Clock::QueryHostTickCount()); // Create main page file-backed mapping. This is all reserved but // uncommitted (so it shouldn't expand page file). mapping_ = xe::memory::CreateFileMappingHandle( file_name_, // entire 4gb space + 512mb physical: 0x11FFFFFFF, xe::memory::PageAccess::kReadWrite, false); if (mapping_ == xe::memory::kFileMappingHandleInvalid) { XELOGE("Unable to reserve the 4gb guest address space."); assert_always(); return false; } // Attempt to create our views. This may fail at the first address // we pick, so try a few times. mapping_base_ = 0; for (size_t n = 32; n < 64; n++) { auto mapping_base = reinterpret_cast(1ull << n); if (!MapViews(mapping_base)) { mapping_base_ = mapping_base; break; } } if (!mapping_base_) { XELOGE("Unable to find a continuous block in the 64bit address space."); assert_always(); return false; } virtual_membase_ = mapping_base_; physical_membase_ = mapping_base_ + 0x100000000ull; ``` **File:** src/xenia/base/byte_order.h (L83-136) ```text template struct endian_store { endian_store() = default; endian_store(const T& src) { set(src); } endian_store(const endian_store& other) { set(other); } operator T() const { return get(); } void set(const T& src) { if constexpr (std::endian::native == E) { value = src; } else { value = xe::byte_swap(src); } } void set(const endian_store& other) { value = other.value; } T get() const { if constexpr (std::endian::native == E) { return value; } return xe::byte_swap(value); } endian_store& operator+=(int a) { *this = *this + a; return *this; } endian_store& operator-=(int a) { *this = *this - a; return *this; } endian_store& operator++() { *this += 1; return *this; } // ++a endian_store operator++(int) { *this += 1; return (*this - 1); } // a++ endian_store& operator--() { *this -= 1; return *this; } // --a endian_store operator--(int) { *this -= 1; return (*this + 1); } // a-- T value; }; template using be = endian_store; template using le = endian_store; ``` **File:** src/xenia/kernel/xthread.h (L249-280) ```text struct X_KTHREAD { X_DISPATCH_HEADER header; // 0x0 xe::be unk_10; // 0x10 xe::be unk_14; // 0x14 X_KTIMER wait_timeout_timer; // 0x18 X_KWAIT_BLOCK wait_timeout_block; // 0x40 uint8_t unk_58[0x4]; // 0x58 xe::be stack_base; // 0x5C xe::be stack_limit; // 0x60 xe::be stack_kernel; // 0x64 xe::be tls_address; // 0x68 // state = is thread running, suspended, etc uint8_t thread_state; // 0x6C // 0x70 = priority? uint8_t alerted[2]; // 0x6D uint8_t alertable; // 0x6F uint8_t priority; // 0x70 uint8_t fpu_exceptions_on; // 0x71 // these two process types both get set to the same thing, process_type is // referenced most frequently, however process_type_dup gets referenced a few // times while the process is being created uint8_t process_type_dup; uint8_t process_type; // apc_mode determines which list an apc goes into util::X_TYPED_LIST apc_lists[2]; TypedGuestPointer process; // 0x84 uint8_t executing_kernel_apc; // 0x88 // when context switch happens, this is copied into // apc_software_interrupt_state for kpcr uint8_t deferred_apc_software_interrupt_state; // 0x89 uint8_t user_apc_pending; // 0x8A uint8_t may_queue_apcs; // 0x8B ``` **File:** src/xenia/gpu/spirv_shader_translator.cc (L153-170) ```text void SpirvShaderTranslator::StartTranslation() { // TODO(Triang3l): Logger. builder_ = std::make_unique( features_.spirv_version, (kSpirvMagicToolId << 16) | 1, nullptr); builder_->addCapability(IsSpirvTessEvalShader() ? spv::CapabilityTessellation : spv::CapabilityShader); if (features_.spirv_version < spv::Spv_1_4) { if (features_.signed_zero_inf_nan_preserve_float32 || features_.denorm_flush_to_zero_float32 || features_.rounding_mode_rte_float32) { builder_->addExtension("SPV_KHR_float_controls"); } } ext_inst_glsl_std_450_ = builder_->import("GLSL.std.450"); builder_->setMemoryModel(spv::AddressingModelLogical, spv::MemoryModelGLSL450); builder_->setSource(spv::SourceLanguageUnknown, 0); ``` **File:** src/xenia/gpu/shader_interpreter.h (L26-60) ```text class ShaderInterpreter { public: ShaderInterpreter(const RegisterFile& register_file, const Memory& memory) : register_file_(register_file), memory_(memory) {} class ExportSink { public: virtual ~ExportSink() = default; virtual void AllocExport(ucode::AllocType type, uint32_t size) {} virtual void Export(ucode::ExportRegister export_register, const float* value, uint32_t value_mask) {} }; void SetTraceWriter(TraceWriter* new_trace_writer) { trace_writer_ = new_trace_writer; } ExportSink* GetExportSink() const { return export_sink_; } void SetExportSink(ExportSink* new_export_sink) { export_sink_ = new_export_sink; } const float* temp_registers() const { return &temp_registers_[0][0]; } float* temp_registers() { return &temp_registers_[0][0]; } static bool CanInterpretShader(const Shader& shader) { assert_true(shader.is_ucode_analyzed()); // Texture instructions are not very common in vertex shaders (and not used // in Direct3D 9's internal rectangles such as clears) and are extremely // complex, not implemented. if (shader.uses_texture_fetch_instruction_results()) { return false; } return true; } ``` **File:** src/xenia/base/mutex.h (L27-87) ```text class alignas(4096) xe_global_mutex { XE_MAYBE_UNUSED char detail[64]; public: xe_global_mutex(); ~xe_global_mutex(); void lock(); void unlock(); bool try_lock(); }; using global_mutex_type = xe_global_mutex; class alignas(64) xe_fast_mutex { XE_MAYBE_UNUSED char detail[64]; public: xe_fast_mutex(); ~xe_fast_mutex(); void lock(); void unlock(); bool try_lock(); }; // a mutex that is extremely unlikely to ever be locked // use for race conditions that have extremely remote odds of happening class xe_unlikely_mutex { std::atomic mut; bool _tryget() { uint32_t lock_expected = 0; return mut.compare_exchange_strong(lock_expected, 1); } public: xe_unlikely_mutex() : mut(0) {} ~xe_unlikely_mutex() { mut = 0; } void lock() { if (XE_LIKELY(_tryget())) { return; } else { do { // chrispy: warning, if no SMT, mm_pause does nothing... #if XE_ARCH_AMD64 == 1 _mm_pause(); #endif } while (!_tryget()); } } void unlock() { mut.exchange(0); } bool try_lock() { return _tryget(); } }; using xe_mutex = xe_fast_mutex; #else using global_mutex_type = std::recursive_mutex; using xe_mutex = std::mutex; using xe_unlikely_mutex = std::mutex; #endif ``` **File:** src/xenia/app/premake5.lua (L31-42) ```lua "glslang-spirv", "imgui", "libavcodec", "libavutil", "mspack", "snappy", "xxhash", }) defines({ "XBYAK_NO_OP_NAMES", "XBYAK_ENABLE_OMITTED_OPERAND", }) ```