Files
Sylpheed/tools/ppc-manual/vmx128/vpkd3d128.md
sim f3c512f2ab docs(ppc-manual): check every xenia-rs claim against Canary's source
The hand-written parts of the manual still described how the retired
xenia-rs interpreter behaved: its snapshots, Rust casts and helpers. Each of
those 490 statements is now either restated as what Canary's emitters and
x64 backend actually do (at the pinned canary_experimental commit), or
dropped where it only made sense for xenia-rs.

Checking them turned up claims that were wrong, not just outdated:

- VSCR[SAT] is never modelled in Canary (DID_SATURATE is a stub and mfvscr
  cannot see it); the pages said saturating ops set it stickily.
- Canary does not implement lswi/lswx/stswi/stswx, dcbi, mtfsb0/mtfsb1,
  vmsum*, vmhaddshs, vupkhpx/vupklpx, and most SPRs; pages described them
  as working.
- Traps evaluate TO in Canary; stvebx/stvehx/stvewx store one element, not
  16 bytes; mtmsrd writes only EE; fres/frsqrte/vrsqrtefp precision claims
  and the stfs "rounds under RN / sets FPSCR" claim contradicted the spec.
- Reservations are a 64 KiB block bitmap plus a value compare, not
  per-address tracking.

Claims that neither Canary's source nor a public spec settles are marked
unverified (NI at boot, vmaddcfp128 operand order, estimate bit-exactness).

Generated regions are untouched; re-running the generator changes nothing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-16 21:52:38 +02:00

8.7 KiB
Raw Permalink Blame History

vpkd3d128 — Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert

Category: VMX128 · Form: VX128_4 · Opcode: 0x18000610

Assembler Mnemonics

Mnemonic XML entry Flags Description
vpkd3d128 vpkd3d128 — Vector128 Pack D3Dtype, Rotate Left Immediate and Mask Insert

Syntax

(no disassembly template)

Encoding

vpkd3d128 — form VX128_4

  • Opcode word: 0x18000610
  • Primary opcode (bits 0–5): 6
  • Extended opcode: 1552
  • Synchronising: no
Bits Field Meaning
0–5 OPCD primary opcode (6)
6–10 VD128l destination low 5 bits
11–15 IMM 5-bit immediate
16–20 VB128l source B low 5 bits
21–23 XO extended opcode
24–25 z sub-operation selector
28–29 VD128h destination high 2 bits
30–31 VB128h source B high 2 bits

Operands

Field Role Description
VB vpkd3d128: read Source B vector register.
VD vpkd3d128: write Destination vector register.

Register Effects

vpkd3d128

  • Reads (always): VB
  • Reads (conditional): none
  • Writes (always): VD
  • Writes (conditional): none

Status-Register Effects

No condition-register or status-register effects.

Operation (pseudocode)

; No hand-written pseudocode for this instruction yet.
; The authoritative semantics are the Canary emitter snapshot under
; Implementation References; about half of Canary's emitters open
; with the PPC-style definition as a comment (`RD <- (RA) + (RB)`).
; Every side effect is also enumerated in the Register Effects and
; Status-Register Effects tables above.

C Translation Example

/* No hand-written C yet. Translate the Canary emitter snapshot   */
/* under Implementation References; its HIR maps directly:        */
/*   f.LoadGPR(n) / f.StoreGPR(n, v)  -> r[n] / r[n] = v          */
/*   f.LoadFPR / StoreFPR, f.LoadVR / StoreVR -> f[n], v[n]        */
/*   f.Load(ea, T), f.Store(ea, v) -> raw read / write; emitters   */
/*     wrap them in f.ByteSwap for the big-endian guest value      */
/*   f.UpdateCR(n, v)  -> CR field n from v's LOW 32 BITS vs 0     */
/*   f.LoadCA / f.StoreCA -> xer.CA;  f.StoreSAT -> vscr.SAT       */
/*   i.XO.RA, i.D.DS, ... -> the bit-fields listed under Operands  */
/* The Register Effects and Status-Register Effects tables above  */
/* enumerate every side effect a faithful translation must emit.  */

Implementation References

vpkd3d128

Canary emitter (frozen snapshot @ f21ebd49e9)
int InstrEmit_vpkd3d128(PPCHIRBuilder& f, const InstrData& i) {
  const uint32_t vd = i.VX128_4.VD128l | (i.VX128_4.VD128h << 5);
  const uint32_t vb = i.VX128_4.VB128l | (i.VX128_4.VB128h << 5);
  uint32_t type = i.VX128_4.IMM >> 2;
  uint32_t pack = i.VX128_4.IMM & 0x3;
  uint32_t shift = i.VX128_4.z;
  Value* v = f.LoadVR(vb);
  switch (type) {
    case 0:  // VPACK_D3DCOLOR
      v = f.Pack(v, PACK_TYPE_D3DCOLOR);
      break;
    case 1:  // VPACK_NORMSHORT2
      v = f.Pack(v, PACK_TYPE_SHORT_2);
      break;
    case 2:  // VPACK_NORMPACKED32 2_10_10_10 w_z_y_x
      v = f.Pack(v, PACK_TYPE_UINT_2101010);
      break;
    case 3:  // VPACK_FLOAT16_2 DXGI_FORMAT_R16G16_FLOAT
      v = f.Pack(v, PACK_TYPE_FLOAT16_2);
      break;
    case 4:  // VPACK_NORMSHORT4
      v = f.Pack(v, PACK_TYPE_SHORT_4);
      break;
    case 5:  // VPACK_FLOAT16_4 DXGI_FORMAT_R16G16B16A16_FLOAT
      v = f.Pack(v, PACK_TYPE_FLOAT16_4);
      break;
    case 6:  // VPACK_NORMPACKED64 4_20_20_20 w_z_y_x
      // Used in 54540829 and other installments in the series, pretty rarely in
      // general.
      v = f.Pack(v, PACK_TYPE_ULONG_4202020);
      break;
    default:
      assert_unhandled_case(type);
      return 1;
  }
  // https://hlssmod.net/he_code/public/pixelwriter.h
  // control = prev:0123 | new:4567
  uint32_t control = kIdentityPermuteMask;  // original
  switch (pack) {
    case 1:  // VPACK_32
             // VPACK_32 & shift = 3 puts lower 32 bits in x (leftmost slot).
      switch (shift) {
        case 0:
          control = MakePermuteMask(0, 0, 0, 1, 0, 2, 1, 3);
          break;
        case 1:
          control = MakePermuteMask(0, 0, 0, 1, 1, 3, 0, 3);
          break;
        case 2:
          control = MakePermuteMask(0, 0, 1, 3, 0, 2, 0, 3);
          break;
        case 3:
          control = MakePermuteMask(1, 3, 0, 1, 0, 2, 0, 3);
          break;
        default:
          assert_unhandled_case(shift);
          return 1;
      }
      break;
    case 2:  // 64bit
      switch (shift) {
        case 0:
          control = MakePermuteMask(0, 0, 0, 1, 1, 2, 1, 3);
          break;
        case 1:
          control = MakePermuteMask(0, 0, 1, 2, 1, 3, 0, 3);
          break;
        case 2:
          control = MakePermuteMask(1, 2, 1, 3, 0, 2, 0, 3);
          break;
        case 3:
          control = MakePermuteMask(1, 3, 0, 1, 0, 2, 0, 3);
          break;
        default:
          assert_unhandled_case(shift);
          return 1;
      }
      break;
    case 3:  // 64bit
      switch (shift) {
        case 0:
          control = MakePermuteMask(0, 0, 0, 1, 1, 2, 1, 3);
          break;
        case 1:
          control = MakePermuteMask(0, 0, 1, 2, 1, 3, 0, 3);
          break;
        case 2:
          control = MakePermuteMask(1, 2, 1, 3, 0, 2, 0, 3);
          break;
        case 3:
          control = MakePermuteMask(0, 0, 0, 1, 0, 2, 1, 2);
          break;
        default:
          assert_unhandled_case(shift);
          return 1;
      }
      break;
    default:
      assert_unhandled_case(pack);
      return 1;
  }
  v = f.Permute(f.LoadConstantUint32(control), f.LoadVR(vd), v, INT32_TYPE);
  f.StoreVR(vd, v);
  return 0;
}

Special Cases & Edge Conditions

  • Pack four float lanes into a single D3D-format 32-bit word. In Canary's decoding (VX128_4 form), IMM >> 2 chooses which D3D format to emit, IMM & 3 how wide a slot it goes into, and z the lane shift:
    • D3dColor — pack 4×float [0.0, 1.0] lanes into a 32-bit RGBA8 (A in high byte, B in low byte) — the canonical Direct3D 9 D3DCOLOR format. Canary emits Pack(PACK_TYPE_D3DCOLOR).
    • Canary also packs NORMSHORT2, NORMPACKED32 (2:10:10:10), FLOAT16_2, NORMSHORT4, FLOAT16_4 and NORMPACKED64 (4:20:20:20); format 7 is unhandled and reported as an unimplemented instruction.
  • Also merges into the existing VD. The mnemonic is "Pack D3Dtype, Rotate Left Immediate and Mask Insert": the result of the pack step is placed into an existing VD rather than overwriting it. Canary does this with a permute whose control depends on IMM & 3 and z, keeping the other lanes of VD.
  • Sub-operation via the z field (2 bits) + IMM (5 bits) gives 7 bits of format selection; the practical set used by Xenon games is small (D3DCOLOR is the dominant one).
  • No saturation signal. The packer saturates floats beyond [0.0, 1.0] silently; VSCR[SAT] is not touched.
  • VMX128 register-fusion on VD and VB.
  • No IBM AIX entry — Xenon-only.
  • No Rc, no XER.
  • vupkd3d128 — the inverse (unpack a D3D-format word back into 4 floats).
  • vpkpx — the standard Altivec 1-5-5-5 pixel pack.
  • vpkshus, vpkuhus — byte-range saturating packs (an alternative colour-packing path).
  • vcfpsxws128, vcfpuxws128 — conversion with explicit scale; software sometimes pre-scales floats to [0, 255] before using these in place of vpkd3d128.

IBM Reference

  • No IBM AIX entry — Xbox 360 VMX128 extension only. The "D3D" in the mnemonic refers directly to Direct3D 9 vertex/pixel formats (the D3DDECLTYPE_* enumeration).
  • Xbox 360 XDK, Altivec-128 (VMX128) extensions.
  • Microsoft D3D9 documentation: D3DDECLTYPE_D3DCOLOR, D3DDECLTYPE_UBYTE4N, etc.