# `divdux` — Divide Doubleword Unsigned > **Category:** [Integer ALU](../categories/alu.md) · **Form:** [XO](../forms/XO.md) · **Opcode:** `0x7c000392` ## Assembler Mnemonics | Mnemonic | XML entry | Flags | Description | | --- | --- | --- | --- | | `divdu` | `divdux` | — | Divide Doubleword Unsigned | | `divduo` | `divdux` | OE=1 | Divide Doubleword Unsigned | | `divdu.` | `divdux` | Rc=1 | Divide Doubleword Unsigned | | `divduo.` | `divdux` | OE=1, Rc=1 | Divide Doubleword Unsigned | ## Syntax ```asm divdu[OE][Rc] [RD], [RA], [RB] ``` ## Encoding ### `divdux` — form `XO` - **Opcode word:** `0x7c000392` - **Primary opcode (bits 0–5):** `31` - **Extended opcode:** `457` - **Synchronising:** no | Bits | Field | Meaning | | --- | --- | --- | | 0–5 | `OPCD` | primary opcode (31) | | 6–10 | `RT` | destination GPR | | 11–15 | `RA` | source A | | 16–20 | `RB` | source B | | 21 | `OE` | overflow-enable flag | | 22–30 | `XO` | extended opcode (9 bits) | | 31 | `Rc` | record-form flag | ## Operands | Field | Role | Description | | --- | --- | --- | | `RA` | divdux: read | Source GPR (`r0`–`r31`). | | `RB` | divdux: read | Source GPR. | | `RD` | divdux: write | Destination GPR. | | `OE` | divdux: write (conditional) | Overflow-enable bit. When 1, the instruction updates `XER[OV]` and stickies `XER[SO]` on signed overflow. | | `CR` | divdux: write (conditional) | Condition-register update. When `Rc=1`, CR field 0 (or CR6 for vector compares, CR1 for FPU) is updated from the result. | ## Register Effects ### `divdux` - **Reads (always):** `RA`, `RB` - **Reads (conditional):** _none_ - **Writes (always):** `RD` - **Writes (conditional):** `OE`, `CR` ## Status-Register Effects - `divdux`: **CR0** ← signed-compare(result, 0) with `SO ← XER[SO]`, when `Rc=1`.; **XER[OV]** ← signed-overflow(result); **XER[SO]** stickies, when `OE=1`. ## Operation (pseudocode) ``` RT <- (RA) /u (RB) ; undefined if RB=0 ``` ## C Translation Example ```c /* 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 **`divdux`** - Canary XML: [`tools/ppc-instructions.xml` — search for `mnem="divdux"`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/tools/ppc-instructions.xml) - Canary emitter: [`src/xenia/cpu/ppc/ppc_emit_alu.cc:217`](https://github.com/xenia-canary/xenia-canary/blob/f21ebd49e979e44f081f474df78c3fbfee9cb3f2/src/xenia/cpu/ppc/ppc_emit_alu.cc#L217) - Sylpheed opcode: [`crates/sylpheed-ppc/src/opcode.rs:52`](../../../crates/sylpheed-ppc/src/opcode.rs#L52) - Sylpheed decoder: [`crates/sylpheed-ppc/src/decoder.rs:991`](../../../crates/sylpheed-ppc/src/decoder.rs#L991)
Canary emitter (frozen snapshot @ f21ebd49e9) ```cpp int InstrEmit_divdux(PPCHIRBuilder& f, const InstrData& i) { // dividend <- (RA) // divisor <- (RB) // if divisor = 0 then // if OE = 1 then // XER[OV] <- 1 // return // RT <- dividend ÷ divisor Value* divisor = f.LoadGPR(i.XO.RB); // TODO(benvanik): check if zero // if OE=1, set XER[OV] = 1 // else skip the divide Value* v = f.Div(f.LoadGPR(i.XO.RA), divisor, ARITHMETIC_UNSIGNED); f.StoreGPR(i.XO.RT, v); if (i.XO.OE) { // If we are OE=1 we need to clear the overflow bit. // e.update_xer_with_overflow(e.get_uint64(0)); XEINSTRNOTIMPLEMENTED(); } if (i.XO.Rc) { f.UpdateCR(0, v); } return 0; } ```
## Special Cases & Edge Conditions - **Single undefined case.** Division by zero (`RB == 0`). There is no `INT_MIN/−1` overflow because both operands are unsigned. Canary's emitter does not check for zero (a `TODO`); on the x64 backend `DIV` pre-zeroes the result and skips the divide, so `RT = 0`. Spec leaves `RT` boundedly undefined. - **No trap on Xenon.** As with [`divdx`](divdx.md), the processor does not raise an exception; consuming code must guard `RB` first (typically `cmpdi rb, 0; beq skip`). - **`OE=1` should set `XER[OV]`** on `RB == 0`; Canary's `OE` branch is `XEINSTRNOTIMPLEMENTED()`. - **`Rc=1` CR0 update is 32-bit in Canary.** `f.UpdateCR(0, v)` compares the low 32 bits of the 64-bit unsigned quotient as a signed value: a quotient with bit 32 set reads `LT`, and one whose low word is zero reads `EQ` even if its high word is not. Spec compares the full 64-bit value. - **Slow.** Same ~70-cycle non-pipelined cost as the signed variant; consider reciprocal multiply for hot loops. - **Truncating quotient.** Same C-style toward-zero rounding (trivially equal to floor for unsigned). ## Related Instructions - [`divdx`](divdx.md) — signed 64-bit divide. - [`divwux`](divwux.md), [`divwx`](divwx.md) — 32-bit unsigned/signed. - [`mulldx`](mulldx.md), [`mulhdux`](mulhdux.md) — multiply pair for remainder calculation. - [`cmpli`](cmpli.md), [`cmpl`](cmpl.md) — guard the divisor. ## IBM Reference - [AIX 7.3 — `divdu` (Divide Doubleword Unsigned)](https://www.ibm.com/docs/en/aix/7.3.0?topic=set-divdu-divide-double-word-unsigned-instruction)