/// All PPC opcodes supported by the Xbox 360, including VMX128 extensions. /// Directly mirrors the C++ PPCOpcode enum from ppc_opcode.h. #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] #[repr(u32)] #[allow(non_camel_case_types)] pub enum PpcOpcode { // ALU addcx, addex, addi, addic, addicx, addis, addmex, addx, addzex, andcx, andisx, andix, andx, // Branch bcctrx, bclrx, bcx, bx, // Compare cmp, cmpi, cmpl, cmpli, // Count leading zeros cntlzdx, cntlzwx, // Condition register crand, crandc, creqv, crnand, crnor, cror, crorc, crxor, // Data cache dcbf, dcbi, dcbst, dcbt, dcbtst, dcbz, dcbz128, // Division divdux, divdx, divwux, divwx, // Sync/barrier eieio, // Logical eqvx, extsbx, extshx, extswx, // FPU 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, // Instruction cache icbi, isync, // Load byte lbz, lbzu, lbzux, lbzx, // Load doubleword ld, ldarx, ldbrx, ldu, ldux, ldx, // Load float lfd, lfdu, lfdux, lfdx, lfs, lfsu, lfsux, lfsx, // Load halfword lha, lhau, lhaux, lhax, lhbrx, lhz, lhzu, lhzux, lhzx, // Load multiple/string lmw, lswi, lswx, // Load vector lvebx, lvehx, lvewx, lvewx128, lvlx, lvlx128, lvlxl, lvlxl128, lvrx, lvrx128, lvrxl, lvrxl128, lvsl, lvsl128, lvsr, lvsr128, lvx, lvx128, lvxl, lvxl128, // Load word lwa, lwarx, lwaux, lwax, lwbrx, lwz, lwzu, lwzux, lwzx, // Move CR mcrf, mcrfs, mcrxr, // Move from special mfcr, mffsx, mfmsr, mfspr, mftb, mfvscr, // Move to special mtcrf, mtfsb0x, mtfsb1x, mtfsfix, mtfsfx, mtmsr, mtmsrd, mtspr, mtvscr, // Multiply mulhdux, mulhdx, mulhwux, mulhwx, mulldx, mulli, mullwx, // Logical nandx, negx, norx, orcx, ori, oris, orx, // Rotate rldclx, rldcrx, rldiclx, rldicrx, rldicx, rldimix, rlwimix, rlwinmx, rlwnmx, // System call sc, // Shift sldx, slwx, sradix, sradx, srawix, srawx, srdx, srwx, // Store byte stb, stbu, stbux, stbx, // Store doubleword std, stdbrx, stdcx, stdu, stdux, stdx, // Store float stfd, stfdu, stfdux, stfdx, stfiwx, stfs, stfsu, stfsux, stfsx, // Store halfword sth, sthbrx, sthu, sthux, sthx, // Store multiple/string stmw, stswi, stswx, // Store vector stvebx, stvehx, stvewx, stvewx128, stvlx, stvlx128, stvlxl, stvlxl128, stvrx, stvrx128, stvrxl, stvrxl128, stvx, stvx128, stvxl, stvxl128, // Store word stw, stwbrx, stwcx, stwu, stwux, stwx, // Subtract subfcx, subfex, subficx, subfmex, subfx, subfzex, // Sync sync, // Trap td, tdi, tw, twi, // VMX integer vaddcuw, vaddfp, vaddfp128, vaddsbs, vaddshs, vaddsws, vaddubm, vaddubs, vadduhm, vadduhs, vadduwm, vadduws, vand, vand128, vandc, vandc128, vavgsb, vavgsh, vavgsw, vavgub, vavguh, vavguw, vcfpsxws128, vcfpuxws128, vcfsx, vcfux, vcmpbfp, vcmpbfp128, vcmpeqfp, vcmpeqfp128, vcmpequb, vcmpequh, vcmpequw, vcmpequw128, vcmpgefp, vcmpgefp128, vcmpgtfp, vcmpgtfp128, vcmpgtsb, vcmpgtsh, vcmpgtsw, vcmpgtub, vcmpgtuh, vcmpgtuw, vcsxwfp128, vctsxs, vctuxs, vcuxwfp128, vexptefp, vexptefp128, vlogefp, vlogefp128, vmaddcfp128, vmaddfp, vmaddfp128, vmaxfp, vmaxfp128, vmaxsb, vmaxsh, vmaxsw, vmaxub, vmaxuh, vmaxuw, vmhaddshs, vmhraddshs, vminfp, vminfp128, vminsb, vminsh, vminsw, vminub, vminuh, vminuw, vmladduhm, vmrghb, vmrghh, vmrghw, vmrghw128, vmrglb, vmrglh, vmrglw, vmrglw128, vmsum3fp128, vmsum4fp128, vmsummbm, vmsumshm, vmsumshs, vmsumubm, vmsumuhm, vmsumuhs, vmulesb, vmulesh, vmuleub, vmuleuh, vmulfp128, vmulosb, vmulosh, vmuloub, vmulouh, vnmsubfp, vnmsubfp128, vnor, vnor128, vor, vor128, vperm, vperm128, vpermwi128, vpkd3d128, vpkpx, vpkshss, vpkshss128, vpkshus, vpkshus128, vpkswss, vpkswss128, vpkswus, vpkswus128, vpkuhum, vpkuhum128, vpkuhus, vpkuhus128, vpkuwum, vpkuwum128, vpkuwus, vpkuwus128, vrefp, vrefp128, vrfim, vrfim128, vrfin, vrfin128, vrfip, vrfip128, vrfiz, vrfiz128, vrlb, vrlh, vrlimi128, vrlw, vrlw128, vrsqrtefp, vrsqrtefp128, vsel, vsel128, vsl, vslb, vsldoi, vsldoi128, vslh, vslo, vslo128, vslw, vslw128, vspltb, vsplth, vspltisb, vspltish, vspltisw, vspltisw128, vspltw, vspltw128, vsr, vsrab, vsrah, vsraw, vsraw128, vsrb, vsrh, vsro, vsro128, vsrw, vsrw128, vsubcuw, vsubfp, vsubfp128, vsubsbs, vsubshs, vsubsws, vsububm, vsububs, vsubuhm, vsubuhs, vsubuwm, vsubuws, vsum2sws, vsum4sbs, vsum4shs, vsum4ubs, vsumsws, vupkd3d128, vupkhpx, vupkhsb, vupkhsb128, vupkhsh, vupklpx, vupklsb, vupklsb128, vupklsh, vxor, vxor128, // XOR immediate xori, xoris, xorx, // Invalid Invalid, } impl PpcOpcode { /// Returns true if this opcode is a branch instruction. pub fn is_branch(&self) -> bool { matches!(self, Self::bx | Self::bcx | Self::bclrx | Self::bcctrx) } /// Returns true if this opcode is a system call. pub fn is_syscall(&self) -> bool { matches!(self, Self::sc) } /// Returns true if this opcode unconditionally ends a basic block: /// any branch, system call, trap, or `Invalid` (decoder couldn't /// recognize the instruction — execution will hit the /// `Unimplemented` arm and we don't want to swallow the boundary /// inside a cached block). /// /// Notably *not* terminating: `mtmsr`/`mtmsrd`/`isync`/`mfmsr`. /// On real hardware these have synchronization semantics (a context /// synchronizing event for `isync`, MSR rewrite for the `mt*`s) but /// our interpreter has no asynchronous-exception model and no /// out-of-order execution — they execute as plain ALU/move ops and /// don't change control flow synchronously. Block-cache replay is /// still bit-for-bit identical to per-instruction dispatch for /// those. /// /// Used by the basic-block cache (`block_cache.rs`) to know when to /// stop accumulating instructions during a forward decode walk. pub fn terminates_block(&self) -> bool { matches!( self, Self::bx | Self::bcx | Self::bclrx | Self::bcctrx | Self::sc | Self::td | Self::tdi | Self::tw | Self::twi | Self::Invalid ) } /// Returns true if this is a load instruction. pub fn is_load(&self) -> bool { matches!(self, Self::lbz | Self::lbzu | Self::lbzux | Self::lbzx | Self::lhz | Self::lhzu | Self::lhzux | Self::lhzx | Self::lha | Self::lhau | Self::lhaux | Self::lhax | Self::lwz | Self::lwzu | Self::lwzux | Self::lwzx | Self::lwa | Self::lwax | Self::lwaux | Self::ld | Self::ldu | Self::ldux | Self::ldx | Self::lfs | Self::lfsu | Self::lfsux | Self::lfsx | Self::lfd | Self::lfdu | Self::lfdux | Self::lfdx | Self::lhbrx | Self::lwbrx | Self::ldbrx | Self::lmw | Self::lswi | Self::lswx | Self::lwarx | Self::ldarx ) } /// Returns true if this is a store instruction. pub fn is_store(&self) -> bool { matches!(self, Self::stb | Self::stbu | Self::stbux | Self::stbx | Self::sth | Self::sthu | Self::sthux | Self::sthx | Self::stw | Self::stwu | Self::stwux | Self::stwx | Self::std | Self::stdu | Self::stdux | Self::stdx | Self::stfs | Self::stfsu | Self::stfsux | Self::stfsx | Self::stfd | Self::stfdu | Self::stfdux | Self::stfdx | Self::sthbrx | Self::stwbrx | Self::stdbrx | Self::stmw | Self::stswi | Self::stswx | Self::stwcx | Self::stdcx | Self::stfiwx ) } /// Returns true if this opcode is a cross-thread synchronization /// point at which the superblock runner MUST yield back to the /// round-robin scheduler so the lockstep interleaving stays /// fine-grained enough to preserve correct cross-thread ordering: /// /// - reserved load/store (`lwarx`/`ldarx`/`stwcx.`/`stdcx.`): the /// atomic primitive other threads race on. Running past one /// without returning to the scheduler would let a single slot /// win/lose a reservation across many blocks before any peer /// observes it. /// - memory barriers (`sync`/`eieio`/`isync`): the guest explicitly /// demands a global ordering point here; honour it by ending the /// superblock so the scheduler re-interleaves. /// /// Purely a function of the opcode (no guest data), so the yield /// decision is deterministic and the schedule reproduces byte-identically. /// Note: `sc` (syscall) and traps already `terminates_block`, and /// import-thunk / halt-sentinel PCs are handled by the per-block /// prologue re-check in the superblock loop — they are not listed here. #[inline] pub fn is_sync_sensitive(&self) -> bool { matches!( self, Self::lwarx | Self::ldarx | Self::stwcx | Self::stdcx | Self::sync | Self::eieio | Self::isync ) } pub fn name(&self) -> &'static str { match self { Self::Invalid => "invalid", _ => { // Use debug formatting to get the variant name // This is a placeholder - in practice we'd have a lookup table "?" } } } } impl std::fmt::Display for PpcOpcode { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Debug::fmt(self, f) } } #[cfg(test)] mod tests { use super::*; #[test] fn terminates_block_includes_all_branches() { assert!(PpcOpcode::bx.terminates_block()); assert!(PpcOpcode::bcx.terminates_block()); assert!(PpcOpcode::bclrx.terminates_block()); assert!(PpcOpcode::bcctrx.terminates_block()); } #[test] fn terminates_block_includes_sc_and_traps() { assert!(PpcOpcode::sc.terminates_block()); assert!(PpcOpcode::td.terminates_block()); assert!(PpcOpcode::tdi.terminates_block()); assert!(PpcOpcode::tw.terminates_block()); assert!(PpcOpcode::twi.terminates_block()); } #[test] fn terminates_block_includes_invalid() { // Decoder failure must end the block — otherwise an unknown // opcode would be replayed inside a cached block without going // through the per-instruction Unimplemented path. assert!(PpcOpcode::Invalid.terminates_block()); } #[test] fn terminates_block_excludes_straight_line_ops() { // Common ALU and load/store ops must NOT terminate a block. assert!(!PpcOpcode::addi.terminates_block()); assert!(!PpcOpcode::addis.terminates_block()); assert!(!PpcOpcode::addx.terminates_block()); assert!(!PpcOpcode::cmpi.terminates_block()); assert!(!PpcOpcode::cmp.terminates_block()); assert!(!PpcOpcode::lwz.terminates_block()); assert!(!PpcOpcode::stw.terminates_block()); assert!(!PpcOpcode::lbzx.terminates_block()); assert!(!PpcOpcode::ori.terminates_block()); assert!(!PpcOpcode::oris.terminates_block()); assert!(!PpcOpcode::rlwinmx.terminates_block()); } #[test] fn terminates_block_excludes_msr_and_sync_ops() { // Documented decision: synchronizing ops execute as ALU within // a block since the interpreter has no async-exception model. assert!(!PpcOpcode::mtmsr.terminates_block()); assert!(!PpcOpcode::mtmsrd.terminates_block()); assert!(!PpcOpcode::isync.terminates_block()); assert!(!PpcOpcode::sync.terminates_block()); assert!(!PpcOpcode::mfmsr.terminates_block()); } }