; Minimal libgcc-equivalent runtime for the W65816 / Apple IIgs. ; Provides the helpers that the LLVM backend lowers integer multiply, ; shift, divide, and modulo operations to. Implementations are ; correct-but-unoptimised; they exist to unblock end-to-end testing, ; not to compete with hand-tuned 65816 math libraries. ; ; Calling convention (matches W65816ISelLowering::LowerCall): ; - Arg 0 in A (16-bit M). ; - Arg 1 pushed via PHA before the JSL. Reads as (4,S) inside the ; callee (3-byte JSL return address sits at 1..3,S). ; - Return value in A. Caller releases pushed args. ; - Routines run in 16-bit M, 16-bit X (REP #$30 by convention). ; ; Direct-page scratch lives at DP+$E0..DP+$EF (16 bytes). Programs ; that use this runtime must keep DP=0 or remap accordingly. ; ; Assembled with: tools/llvm-mos-build/bin/llvm-mc -arch=w65816 \ ; -filetype=obj ; runtime/src/libgcc.s ; -o runtime/libgcc.o .text ; -------------------------------------------------------------------- ; Indirect-call trampoline. An indirect call (function pointer) stores ; the target's 16-bit address to __indirTarget before JSL'ing here. ; This routine does a JMP indirect through that variable: control ; transfers to the target with the original caller's JSL frame still ; on the stack, so target's RTL returns to the original caller (one ; frame, no double-RTL). ; ; Caller emit sequence in W65816ISelLowering::LowerCall: ; sta __indirTarget ; store ptr (must precede any A clobber for args) ; ... arg pushes ... ; jsl __jsl_indir ; ; __indirTarget MUST live at a fixed bank-0 storage slot. On the ; 65816, JMP (abs) (opcode 0x6C) reads its vector from BANK 0 ; unconditionally — it ignores both PBR and DBR. If __indirTarget ; were in normal BSS, GS/OS Loader / GNO would relocate the BSS ; storage into the program's bank (e.g., $01:9180), but the JMP would ; still read from $00:9180 — wherever, garbage, crash. Pinning it ; at DP $B8 sidesteps the relocation: DP=0 in our runtime, so DP slot ; $B8 IS bank-0 byte $00:00B8. Callers store via `sta $B8` (DP form), ; which writes to the same $00:00B8 regardless of DBR; the trampoline ; below reads via `jmp ($00B8)` (16-bit abs operand into bank-0 ; indirect). Single-bank assumption on the *target's* code remains ; (JMP indirect transfers to current PBR), but program data location ; no longer matters. ; -------------------------------------------------------------------- .globl __indirTarget __indirTarget = 0xb8 .text .globl __jsl_indir __jsl_indir: ; PC <- mem[$00:00B8]. Indirect dispatch through the bank-0- ; anchored function-pointer slot — opcode 0x6C (JMP (abs)). jmp (__indirTarget) ; -------------------------------------------------------------------- ; __run_cxa_atexit — weak no-op fallback. ; ; Each crt0 variant calls `jsl __run_cxa_atexit` after main() returns, ; so pure-C programs that don't link libcxxabi must still resolve the ; symbol. libcxxabi.c overrides this with a strong implementation that ; walks the registered dtor table. ; -------------------------------------------------------------------- .weak __run_cxa_atexit __run_cxa_atexit: rtl ; -------------------------------------------------------------------- ; __srandInitFromTime — weak no-op fallback. ; ; crt0Gsos / crt0Gno call `jsl __srandInitFromTime` after .init_array to ; seed rand() from ReadTimeHex. Programs that don't link extras.o (smoke ; harness link-tests) must still resolve the symbol; the no-op fallback ; leaves rand() at its deterministic seed-1 starting state. ; -------------------------------------------------------------------- .weak __srandInitFromTime __srandInitFromTime: rtl ; -------------------------------------------------------------------- ; __mulhi3 — 16-bit multiply. A * (4,S) -> A. ; Signed and unsigned share an implementation: only the low 16 bits of ; the product are returned, which is identical for both. Uses ; shift-and-add over the multiplier bits. ; -------------------------------------------------------------------- .globl __mulhi3 __mulhi3: sta 0xe0 ; multiplier lda 0x4, s sta 0xe2 ; multiplicand stz 0xe4 ; running product = 0 .Lmul_loop: lda 0xe0 beq .Lmul_done lsr a sta 0xe0 bcc .Lmul_skip lda 0xe4 clc adc 0xe2 sta 0xe4 .Lmul_skip: asl 0xe2 bra .Lmul_loop .Lmul_done: lda 0xe4 rtl ; -------------------------------------------------------------------- ; __umulhisi3 — unsigned 16x16 -> 32 multiply. A = multiplier (16-bit), ; (4,s) = multiplicand (16-bit). Returns A:X = 32-bit product (A=lo, ; X=hi). Used by the i32-mul-of-zext-i16 DAG combine in the W65816 ; backend to avoid the full __mulsi3 (32x32 -> 32) call when the user ; writes `(u32)a * b` with a/b as u16 (e.g. `unsigned long s += i*i;`). ; ~16 iterations instead of 16-32 for the equivalent __mulsi3 fast path, ; AND avoids zext/zero-fill overhead in arg setup. ; ; The accumulator and shifted multiplicand both need 32-bit space: ; $e0/$e1 multiplier (shifted right; tested for add) ; $e2..$e5 multiplicand (16-bit input, shifts up into the high ; half over the loop) ; $e6..$e9 32-bit product accumulator ; -------------------------------------------------------------------- .globl __umulhisi3 __umulhisi3: ; Fast path: if both args fit in 8 bits, use a quarter-square ; table for 8x8 → 16 (result hi half is always 0). Identity: ; a*b = T[a+b] - T[|a-b|] where T[k] = floor(k²/4) ; 511-entry, 2-byte table = 1022 bytes ROM. Fast path is ~55 ; cyc vs ~150 cyc for the shift-and-add fallback at i ~ 50. ; Hot in `i*i` patterns where i is a loop counter ≤ 255. sta 0xe0 ; save arg0 ora 0x4, s ; OR with arg1 and #0xff00 ; isolate hi bytes bne .Lumulhisi_slow ; if either hi byte nonzero, fallback ; Both args fit in 8 bits — quarter-square path. lda 0xe0 ; arg0 clc adc 0x4, s ; A = a + b asl a ; A = 2*(a+b) — word index tax lda __umulhisi3_qsq, x ; T[a+b] sta 0xe2 ; stash lda 0xe0 ; arg0 sec sbc 0x4, s ; A = a - b bpl .Lumulhisi_qsq_pos ; A negative; negate to get |a-b|. eor #0xffff inc a .Lumulhisi_qsq_pos: asl a ; A = 2*|a-b| tax lda 0xe2 ; T[a+b] sec sbc __umulhisi3_qsq, x ; T[a+b] - T[|a-b|] ldx #0 ; result hi half = 0 rtl .Lumulhisi_slow: ; Multiplier already at $e0 from the fast-path check above. lda 0x4, s sta 0xe2 ; multiplicand lo in $e2/$e3 stz 0xe4 ; multiplicand hi (initially 0) in $e4/$e5 stz 0xe6 ; product lo at $e6/$e7 stz 0xe8 ; product hi at $e8/$e9 .Lumulhisi_loop: lda 0xe0 beq .Lumulhisi_done lsr a sta 0xe0 bcc .Lumulhisi_skip ; Add 32-bit multiplicand to 32-bit product. clc lda 0xe6 adc 0xe2 sta 0xe6 lda 0xe8 adc 0xe4 sta 0xe8 .Lumulhisi_skip: ; Shift 32-bit multiplicand left by 1. asl 0xe2 rol 0xe4 bra .Lumulhisi_loop .Lumulhisi_done: ldx 0xe8 lda 0xe6 rtl ; -------------------------------------------------------------------- ; __ashlhi3 — A << (4,S) -> A. Shift count is i16 but only the low 4 ; bits are meaningful (counts >=16 are undefined behaviour in C). ; -------------------------------------------------------------------- .globl __ashlhi3 __ashlhi3: pha ; save value on stack so we can free A lda 0x6, s ; arg 1 sits at 6,s now (PHA shifted by 2) tax pla ; restore value .Lashl_loop: cpx #0x0 beq .Lashl_done asl a dex bra .Lashl_loop .Lashl_done: rtl ; -------------------------------------------------------------------- ; __lshrhi3 — A logical >> (4,S) -> A. Same shape as __ashlhi3 with ; LSR instead of ASL. ; -------------------------------------------------------------------- .globl __lshrhi3 __lshrhi3: pha lda 0x6, s tax pla .Llshr_loop: cpx #0x0 beq .Llshr_done lsr a dex bra .Llshr_loop .Llshr_done: rtl ; -------------------------------------------------------------------- ; __ashrhi3 — A arithmetic >> (4,S) -> A. Sign bit is preserved by ; copying it into carry before each ROR via CMP #$8000 (which sets ; carry exactly when the sign bit is set on a 16-bit unsigned compare). ; -------------------------------------------------------------------- .globl __ashrhi3 __ashrhi3: pha lda 0x6, s tax pla .Lashr_loop: cpx #0x0 beq .Lashr_done cmp #0x8000 ror a dex bra .Lashr_loop .Lashr_done: rtl ; -------------------------------------------------------------------- ; __udivhi3 — A unsigned / (4,S) -> A. ; Restoring shift-subtract division. Common helper; __umodhi3 reuses ; the algorithm and returns the remainder instead. ; Scratch: $e6 = numerator, $e8 = denominator, ; $ea = quotient, $ec = remainder. ; -------------------------------------------------------------------- .globl __udivhi3 __udivhi3: ; Public entry: A=dividend, (4,S)=divisor. Set up scratch and ; call the same JSR-based core used by signed divide. sta 0xe6 lda 0x4, s sta 0xe8 jsr __udivmod_core lda 0xea rtl .globl __umodhi3 __umodhi3: sta 0xe6 lda 0x4, s sta 0xe8 jsr __udivmod_core lda 0xec rtl ; -------------------------------------------------------------------- ; __divhi3 / __modhi3 — signed 16-bit divide and modulo. Strategy: ; - Stash sign of dividend in $ee bit 0 (used by modulo). ; - Stash result sign of quotient (sign(a) XOR sign(b)) in $ee bit 1 ; (used by divide). ; - Take absolute values, run the unsigned core, then negate the ; appropriate result if its sign bit is set. ; C99: quotient truncates toward zero; remainder takes the sign of the ; dividend. ; -------------------------------------------------------------------- .globl __divhi3 __divhi3: jsr __divmod_setup jsr __udivmod_core ; Quotient is in $ea. Negate if bit 1 of $ee is set. pei 0xea lda 0xee and #0x2 beq .Ldiv_pos pla eor #0xffff clc adc #0x1 rtl .Ldiv_pos: pla rtl .globl __modhi3 __modhi3: jsr __divmod_setup jsr __udivmod_core ; Remainder is in $ec. Negate if bit 0 of $ee is set (dividend ; was negative). pei 0xec lda 0xee and #0x1 beq .Lmod_pos pla eor #0xffff clc adc #0x1 rtl .Lmod_pos: pla rtl ; -------------------------------------------------------------------- ; __divmod_setup — common prologue for __divhi3/__modhi3. Reads ; A=dividend and (4,S)=divisor (the public-entry stack frame is intact ; because we used JSR not JSL, so (4,S) still points to the user's ; pushed arg1 relative to the original JSL). Computes |a| -> $e6, ; |b| -> $e8, and sign tracker -> $ee: ; bit 0 = 1 if dividend was negative (modulo result sign) ; bit 1 = 1 if dividend XOR divisor signs differ (quotient sign) ; Uses JSR/RTS, same bank. ; -------------------------------------------------------------------- __divmod_setup: ; Sign tracker. STZ doesn't touch A — preserves the value ; we still need below. stz 0xee ; Dividend sign + abs value. cmp #0x8000 bcc .Lset_a_pos ; Negative: set bits 0 and 1 (dividend sign, result sign so far). pha lda 0xee ora #0x3 sta 0xee pla eor #0xffff clc adc #0x1 .Lset_a_pos: sta 0xe6 ; Divisor sign + abs value. After our JSR (pushed 2 bytes of ; near-return), the user's arg1 has shifted up by 2 from (4,S) ; to (6,S). lda 0x6, s cmp #0x8000 bcc .Lset_b_pos ; Negative: flip bit 1 of $ee (XOR with sign of dividend). pha lda 0xee eor #0x2 sta 0xee pla eor #0xffff clc adc #0x1 .Lset_b_pos: sta 0xe8 rts ; -------------------------------------------------------------------- ; __udivmod_core — internal restoring divide. Inputs at $e6/$e8, ; outputs quotient at $ea, remainder at $ec. JSR/RTS local helper. ; -------------------------------------------------------------------- __udivmod_core: stz 0xea stz 0xec ldx #0x10 .Lcore_loop: asl 0xe6 rol 0xec asl 0xea lda 0xec cmp 0xe8 bcc .Lcore_skip sec sbc 0xe8 sta 0xec inc 0xea .Lcore_skip: dex bne .Lcore_loop rts ; ==================================================================== ; 32-bit (long / si) helpers. ; ; ABI for these is the natural extension of the i16 libcalls: ; - arg0_lo in A ; - arg0_hi at (4,s) ; - arg1_lo at (6,s) (or shift count, for the shift helpers) ; - arg1_hi at (8,s) ; - return: result_lo in A, result_hi in X ; ; All are correct-but-unoptimised; goal is unblocking end-to-end builds, ; not winning a 65816 codegolf. ; ; Direct-page scratch for these: ; $e0..$e3 = a (lo, hi) [renamed from $e0/$e2 for the i16 ones] ; $e4..$e7 = b (lo, hi) ; $e8..$eb = result / quotient (lo, hi) ; $ec..$ef = remainder (lo, hi) ; ==================================================================== ; -------------------------------------------------------------------- ; __mulsi3 — 32-bit multiply. Shift-and-add over 32 bits of the ; multiplier. Result = (a * b) mod 2^32. ; ; ABI: A = a_lo, X = a_hi (the i32-first-arg in A:X convention), ; (4,s) = b_lo, (6,s) = b_hi. Result returned in A:X (lo:hi). ; -------------------------------------------------------------------- .globl __mulsi3 __mulsi3: ; Stash a (multiplier) into $e0/$e2. sta 0xe0 stx 0xe2 ; Stash b (multiplicand) into $e4/$e6. lda 0x4, s sta 0xe4 lda 0x6, s sta 0xe6 ; Clear running product at $e8/$ea. stz 0xe8 stz 0xea ; Asymmetric-size swap: the loop iterates based on the multiplier's ; bit width. If a is large (a_hi != 0) but b is small (b_hi == 0), ; swap so the small one becomes the multiplier — the early-exit ; check (lda 0xe0; beq done) then fires sooner. Saves a lot for ; djb2-style `h * 33` patterns where h grows but the constant ; multiplier is small. Cost: ~12 cyc unconditional check; benefit: ; ~10 cyc per saved iter × ~10 iters = 100 cyc. lda 0xe2 beq .Lmulsi_no_swap ; a_hi == 0, no need to swap (or already covered by u16 path below) lda 0xe6 bne .Lmulsi_no_swap ; b_hi != 0, both large, no benefit ; Swap (a_lo, a_hi) ↔ (b_lo, b_hi). Both are at DP; XOR-swap or ; load/store; we use load/store with A scratch since the values are ; 16 bits each. lda 0xe0 ldx 0xe4 stx 0xe0 sta 0xe4 lda 0xe2 ldx 0xe6 stx 0xe2 sta 0xe6 .Lmulsi_no_swap: ; Fast path: if multiplier's high half ($e2) is 0, we only ; need 16 loop iterations AND we can drop the multiplier-hi ; shift step entirely (lsr $e2 + bcc + ora #$8000) — that step ; only ever fires when hi has bits to shift out, which it ; doesn't here. Saves ~8 cyc/iter × 16 iters = ~128 cyc/call ; vs the generic 16-iter path. Common in C code where both ; source operands are zext'd from i16 (e.g. `i*i` with i a ; `unsigned short`) — sumOfSquares benchmark hits this on every ; iteration. lda 0xe2 bne .Lmulsi_full ; Quarter-square fast path: when BOTH args fit in 8 bits (e2==0 ; already verified, e6==0, e0<256, e4<256), use the same T[k]=k^2/4 ; table that __umulhisi3 uses to compute a*b = T[a+b] - T[|a-b|] ; in ~50 cyc vs the u16 loop's ~150-250 cyc for small multipliers. ; Common when dotProduct etc. sign-extend small i16 values to i32. lda 0xe6 bne .Lmulsi_u16_entry lda 0xe0 ora 0xe4 and #0xff00 bne .Lmulsi_u16_entry ; T[a+b] -> $e8 (product lo). lda 0xe0 clc adc 0xe4 asl a tax lda __umulhisi3_qsq, x sta 0xe8 ; |a-b| as word index. lda 0xe0 sec sbc 0xe4 bpl .Lmulsi_qsq_pos eor #0xffff inc a .Lmulsi_qsq_pos: asl a tax lda 0xe8 sec sbc __umulhisi3_qsq, x ldx #0 rtl .Lmulsi_u16_entry: ldy #0x10 .Lmulsi_u16_loop: ; Shift multiplier right; bit-out tested for add. Bottom of loop ; checks multiplier==0 BEFORE the multiplicand shift, so on the ; iter that clears the multiplier we save 14 cyc of unused ; asl/rol on the multiplicand. Combined with the early-exit ; saves ~30 cyc/call on small multipliers (1-50 range typical ; for sumOfSquares). lda 0xe0 lsr a sta 0xe0 bcc .Lmulsi_u16_noadd clc lda 0xe8 adc 0xe4 sta 0xe8 lda 0xea adc 0xe6 sta 0xea .Lmulsi_u16_noadd: lda 0xe0 beq .Lmulsi_done asl 0xe4 rol 0xe6 dey bne .Lmulsi_u16_loop .Lmulsi_done: ldx 0xea lda 0xe8 rtl .Lmulsi_full: ldy #0x20 .Lmulsi_loop: ; Test bit 0 of multiplier (lo word). lda 0xe0 lsr a sta 0xe0 bcc .Lmulsi_noadd ; Add multiplicand to product (32-bit). clc lda 0xe8 adc 0xe4 sta 0xe8 lda 0xea adc 0xe6 sta 0xea .Lmulsi_noadd: ; Stream multiplier hi's LSB into lo's MSB so subsequent iters ; test bits 16..31 via the same lo-bit test. lsr 0xe2 bcc .Lmulsi_no_borrow lda 0xe0 ora #0x8000 sta 0xe0 .Lmulsi_no_borrow: ; Early exit: if BOTH halves of multiplier are 0, no more bits ; remain. Saves the multiplicand shift on the terminating iter ; AND the rest of the loop on small multipliers. lda 0xe0 bne .Lmulsi_shift_mc lda 0xe2 beq .Lmulsi_full_done .Lmulsi_shift_mc: ; Shift multiplicand left for the next iter's potential add. asl 0xe4 rol 0xe6 dey bne .Lmulsi_loop .Lmulsi_full_done: ; Result is in $e8 (lo) / $ea (hi). ldx 0xea lda 0xe8 rtl ; -------------------------------------------------------------------- ; __ashlsi3 — (A:X) << (4,s) -> A:X. Shift count is i16 in low byte; ; counts >= 32 are UB in C. Uses a per-bit loop (cheap on 65816 — one ; ASL + ROL per bit). ; ; ABI: A = a_lo, X = a_hi (i32-first-arg in A:X), (4,s) = count. ; -------------------------------------------------------------------- .globl __ashlsi3 __ashlsi3: sta 0xe0 ; lo stx 0xe2 ; hi lda 0x4, s tay ; count -> Y; sets Z if 0 beq .Lashlsi_done ; count==0: skip loop .Lashlsi_loop: ; Per-bit: 7+7+2+3 = 19 cyc (was 25 with cpy/beq/bra). asl 0xe0 rol 0xe2 dey bne .Lashlsi_loop .Lashlsi_done: ldx 0xe2 lda 0xe0 rtl ; -------------------------------------------------------------------- ; __lshrsi3 — logical >> shift. LSR hi, ROR lo: hi gets a 0, lo gets ; hi's old bit 0. Per-bit loop. ; -------------------------------------------------------------------- .globl __lshrsi3 __lshrsi3: sta 0xe0 stx 0xe2 lda 0x4, s tay beq .Llshrsi_done .Llshrsi_loop: lsr 0xe2 ror 0xe0 dey bne .Llshrsi_loop .Llshrsi_done: ldx 0xe2 lda 0xe0 rtl ; -------------------------------------------------------------------- ; __ashrsi3 — arithmetic >> shift. Sign bit must be preserved on each ; iteration: copy bit 15 of hi into carry (via CMP #$8000), then ROR ; hi, ROR lo. Per-bit loop. ; -------------------------------------------------------------------- .globl __ashrsi3 __ashrsi3: sta 0xe0 stx 0xe2 lda 0x4, s tay beq .Lashrsi_done .Lashrsi_loop: ; CMP #$8000 sets C iff the unsigned value >= 0x8000, i.e. bit 15 ; is set — exactly the sign bit. lda 0xe2 cmp #0x8000 ror 0xe2 ror 0xe0 dey bne .Lashrsi_loop .Lashrsi_done: ldx 0xe2 lda 0xe0 rtl ; -------------------------------------------------------------------- ; __udivmodsi_core — internal 32-bit unsigned divide. Inputs in ; $e0/$e2 (numerator) and $e4/$e6 (denominator); outputs quotient in ; $e8/$ea and remainder in $ec/$ee. 32-iteration restoring divide. ; JSR/RTS local helper. ; -------------------------------------------------------------------- __udivmodsi_core: stz 0xe8 stz 0xea stz 0xec stz 0xee ; was `sta 0xee` — A held b_hi at entry, ; so for divisors > 0xFFFF (b_hi != 0) ; the remainder started contaminated and ; produced wrong quotients. Bug masked ; for b_hi==0 (e.g. /60, /1000) because ; sta-of-zero == stz. Caught by /86400. ldy #0x20 .Lcoresi_loop: ; Shift numerator left through remainder. asl 0xe0 rol 0xe2 rol 0xec rol 0xee ; Shift quotient left. asl 0xe8 rol 0xea ; Compare remainder to denominator (32-bit). lda 0xee cmp 0xe6 bcc .Lcoresi_skip bne .Lcoresi_take lda 0xec cmp 0xe4 bcc .Lcoresi_skip .Lcoresi_take: ; Remainder >= denominator: subtract and set quotient bit 0. sec lda 0xec sbc 0xe4 sta 0xec lda 0xee sbc 0xe6 sta 0xee inc 0xe8 .Lcoresi_skip: dey bne .Lcoresi_loop rts ; -------------------------------------------------------------------- ; __udivsi3 — unsigned 32/32 -> 32 divide. ; -------------------------------------------------------------------- .globl __udivsi3 __udivsi3: ; ABI: A = a_lo, X = a_hi, (4,s) = b_lo, (6,s) = b_hi. sta 0xe0 stx 0xe2 lda 0x4, s sta 0xe4 lda 0x6, s sta 0xe6 jsr __udivmodsi_core ldx 0xea lda 0xe8 rtl ; -------------------------------------------------------------------- ; __umodsi3 — unsigned 32/32 -> 32 modulo. ; -------------------------------------------------------------------- .globl __umodsi3 __umodsi3: sta 0xe0 stx 0xe2 lda 0x4, s sta 0xe4 lda 0x6, s sta 0xe6 jsr __udivmodsi_core ldx 0xee lda 0xec rtl ; -------------------------------------------------------------------- ; __divsi3 / __modsi3 — signed 32-bit divide / modulo. Strategy mirrors ; the i16 helpers: stash signs, take abs, run unsigned core, negate ; result(s) as needed. Sign tracker bits in $f0: ; bit 0 = dividend was negative (modulo result sign) ; bit 1 = quotient sign (sign(a) XOR sign(b)) ; -------------------------------------------------------------------- .globl __divsi3 __divsi3: jsr __divmodsi_setup jsr __udivmodsi_core ; Quotient at $e8/$ea. Negate if bit 1 of $f0 is set. lda 0xf0 and #0x2 beq .Ldivsi_pos ; 32-bit two's complement of quotient. lda 0xe8 eor #0xffff clc adc #0x1 sta 0xe8 lda 0xea eor #0xffff adc #0x0 sta 0xea .Ldivsi_pos: ldx 0xea lda 0xe8 rtl .globl __modsi3 __modsi3: jsr __divmodsi_setup jsr __udivmodsi_core ; Remainder at $ec/$ee. Negate if bit 0 of $f0 set (dividend ; was negative — C99 remainder takes dividend's sign). lda 0xf0 and #0x1 beq .Lmodsi_pos lda 0xec eor #0xffff clc adc #0x1 sta 0xec lda 0xee eor #0xffff adc #0x0 sta 0xee .Lmodsi_pos: ldx 0xee lda 0xec rtl ; -------------------------------------------------------------------- ; __divmodsi_setup — common prologue for __divsi3 / __modsi3. ; Reads A=a_lo, X=a_hi (i32-first-arg ABI), (4,s)=b_lo, (6,s)=b_hi. ; Writes |a| to $e0/$e2, |b| to $e4/$e6, sign bits to $f0. JSR/RTS. ; After JSR's 2-byte ret push, callee-relative offsets are (6,s)=b_lo, ; (8,s)=b_hi. ; -------------------------------------------------------------------- __divmodsi_setup: ; Clear sign tracker. STZ preserves A. stz 0xf0 ; |a|: A=a_lo, X=a_hi. Save them first (we need a_hi for sign test). sta 0xe0 ; tentative a_lo (may negate below) stx 0xe2 ; tentative a_hi cpx #0x8000 bcc .Lsetsi_a_pos ; a is negative. Set sign tracker bits 0+1 and negate. lda 0xf0 ora #0x3 sta 0xf0 ; 32-bit negate: invert + 1. lda 0xe0 eor #0xffff clc adc #0x1 sta 0xe0 lda 0xe2 eor #0xffff adc #0x0 sta 0xe2 .Lsetsi_a_pos: ; |b|. Args shifted by 2 (the JSR ret push). lda 0x6, s sta 0xe4 lda 0x8, s sta 0xe6 cmp #0x8000 bcc .Lsetsi_b_pos ; b is negative. Flip bit 1 of $f0. lda 0xf0 eor #0x2 sta 0xf0 lda 0xe4 eor #0xffff clc adc #0x1 sta 0xe4 lda 0xe6 eor #0xffff adc #0x0 sta 0xe6 .Lsetsi_b_pos: rts ; ==================================================================== ; i64 (long long) helpers. ; ; Calling convention (i64 first arg is split via i32-first-arg path): ; A = arg0_lo[0..15] (lowest word) ; X = arg0_lo[16..31] ; 4,S = arg0_hi[0..15] ; 6,S = arg0_hi[16..31] (highest word) ; For binary ops (mul/div/mod), arg1 follows on the stack: ; 8,S = arg1_lo[0..15] ; 10,S = arg1_lo[16..31] ; 12,S = arg1_hi[0..15] ; 14,S = arg1_hi[16..31] ; For shift ops, the count occupies a single i16 at 8,S. ; ; Return ABI (matches LowerReturn for i64): ; A = result_lo[0..15] ; X = result_lo[16..31] ; Y = result_hi[0..15] ; DP $F0..$F1 = result_hi[16..31] ; ; Scratch DP layout (per-libcall, no overlap between concurrent calls): ; $E0..$E7 = a (8 bytes; 4 16-bit words) ; $E8..$EF = b OR product (8 bytes) ; ; All routines run with REP #$30 (M=0, X=0). ; ==================================================================== ; -------------------------------------------------------------------- ; __divmoddi4_stash — common entry point. Stashes a -> $E0..$E7, ; b -> $E8..$EF. Used by __udivdi3 / __umoddi3 / __divdi3 / __moddi3 ; setup; signed variants flip signs around it. ; -------------------------------------------------------------------- __divmoddi4_stash: ; Called via JSR from another libgcc helper that was itself ; called via JSL. Stack layout inside this routine: ; slot 1..2 = JSR return address (2 bytes, same-bank) ; slot 3..5 = JSL return address (3 bytes, long) ; slot 6..7 = first 16-bit stack arg (caller's first push) ; slot 8..9 = second ; ... etc. ; Earlier code read slots 4, 6, 8, 10, 12, 14 — which lands on ; the JSL ret address bytes, treating them as args. Caught by ; `u64mul(0x12, 0x12)` returning the result at $E2 (mid-low) ; instead of $E0 (lo) plus 0x678-shaped garbage at $E6. sta 0xe0 ; a_lo_lo stx 0xe2 ; a_lo_hi lda 0x6, s sta 0xe4 ; a_hi_lo lda 0x8, s sta 0xe6 ; a_hi_hi lda 0xa, s sta 0xe8 ; b_lo_lo lda 0xc, s sta 0xea ; b_lo_hi lda 0xe, s sta 0xec ; b_hi_lo lda 0x10, s sta 0xee ; b_hi_hi rts ; -------------------------------------------------------------------- ; Helper: pack the result at $E0..$E7 into the i64 return ABI. ; Trashes A, Y. Caller falls through to RTL. ; -------------------------------------------------------------------- __retdi: lda 0xe6 sta 0xf0 lda 0xe4 tay lda 0xe2 tax lda 0xe0 rtl ; -------------------------------------------------------------------- ; __ashldi3 — i64 left shift by n. Per-bit loop. Y holds count. ; -------------------------------------------------------------------- .globl __ashldi3 __ashldi3: sta 0xe0 stx 0xe2 lda 0x4, s sta 0xe4 lda 0x6, s sta 0xe6 lda 0x8, s tay ; Y = count .Lashldi_loop: cpy #0x0 beq .Lashldi_done asl 0xe0 rol 0xe2 rol 0xe4 rol 0xe6 dey bra .Lashldi_loop .Lashldi_done: brl __retdi ; -------------------------------------------------------------------- ; __lshrdi3 — i64 logical right shift. LSR top word, ROR rest. ; -------------------------------------------------------------------- .globl __lshrdi3 __lshrdi3: sta 0xe0 stx 0xe2 lda 0x4, s sta 0xe4 lda 0x6, s sta 0xe6 lda 0x8, s tay .Llshrdi_loop: cpy #0x0 beq .Llshrdi_done lsr 0xe6 ror 0xe4 ror 0xe2 ror 0xe0 dey bra .Llshrdi_loop .Llshrdi_done: brl __retdi ; -------------------------------------------------------------------- ; __ashrdi3 — i64 arithmetic right shift. Same as lshrdi3 but the top ; bit replicates: sign-extend by ASL/ROR which would clear; instead ; take a copy of the sign and OR it back, OR use cmp/sbc trick — use ; the standard idiom: capture sign before LSR via "asl; ror" so C is ; preserved. Simpler: copy bit 15 of $E7 into C before each shift. ; -------------------------------------------------------------------- .globl __ashrdi3 __ashrdi3: sta 0xe0 stx 0xe2 lda 0x4, s sta 0xe4 lda 0x6, s sta 0xe6 lda 0x8, s tay .Lashrdi_loop: cpy #0x0 beq .Lashrdi_done ; "ASL $E6" sets C from bit 15 (the sign), then we ROR $E6 back. ; Net effect on $E6: arithmetic right shift by 1 (sign preserved). ; The carry chain into $E4..$E0 is the new bit 15. lda 0xe6 asl a ; C = sign bit; A = (sign<<1) | rest ror 0xe6 ; $E6: (sign << 15) | ($E6 >> 1) ror 0xe4 ror 0xe2 ror 0xe0 dey bra .Lashrdi_loop .Lashrdi_done: brl __retdi ; -------------------------------------------------------------------- ; __muldi3 — i64 multiply (low 64 bits of 64x64 product). ; Shift-and-add over a (64 bits). Product accumulates at $F2..$F9 ; (above the return DP slot, scratch). Need a fresh 8-byte product ; slot since $E0..$EF holds operands. ; -------------------------------------------------------------------- .globl __muldi3 __muldi3: jsr __divmoddi4_stash ; Clear product P0..P3 at $F2..$F8. stz 0xf2 stz 0xf4 stz 0xf6 stz 0xf8 ; Short-circuit when a's high half ($E4/$E6) is zero: bits 32..63 ; of a are 0, so the 32 high iterations would add nothing. Saves ; ~50% of __muldi3 cost in mulhi64Aligned (softDouble.c), which ; passes only u32-wide operands. b's high half is irrelevant for ; this short-circuit — even if b is full-width, iters 32..63 only ; shift b and add zero. lda 0xe4 ora 0xe6 bne .Lmuldi_long ldy #0x20 bra .Lmuldi_loop .Lmuldi_long: ldy #0x40 .Lmuldi_loop: ; Right-shift the 64-bit `a` by 1. $E0=lo..$E6=hi (matches the ; stash + __retdi convention). Must shift HI first (LSR loses ; bit 63 of $E6) so each ROR carries the previous half's bit 0 ; INTO the top of the next-LOWER half — that's the actual ; right-shift direction in a $E0=lo layout. After the chain, ; C = orig $E0_b0 = bit 0 of the 64-bit value, which drops out ; and is what we want to BCC on. The earlier code shifted lo ; first which ran the shift in the WRONG direction (lo → hi) ; and tested $E6_b0 (bit 48) instead of bit 0 — every multiply ; involving bits 16+ came back garbage. lda 0xe6 lsr a sta 0xe6 lda 0xe4 ror a sta 0xe4 lda 0xe2 ror a sta 0xe2 lda 0xe0 ror a sta 0xe0 bcc .Lmuldi_noadd ; Add b ($E8..$EE) to product ($F2..$F8). clc lda 0xf2 adc 0xe8 sta 0xf2 lda 0xf4 adc 0xea sta 0xf4 lda 0xf6 adc 0xec sta 0xf6 lda 0xf8 adc 0xee sta 0xf8 .Lmuldi_noadd: ; Shift b left by 1 (so each iteration uses next bit position). asl 0xe8 rol 0xea rol 0xec rol 0xee dey bne .Lmuldi_loop ; Move product into return slots ($E0..$E7) and tail-call __retdi. lda 0xf2 sta 0xe0 lda 0xf4 sta 0xe2 lda 0xf6 sta 0xe4 lda 0xf8 sta 0xe6 brl __retdi ; -------------------------------------------------------------------- ; __ucmpdi2 — unsigned i64 compare. Returns 0 if ab (libgcc convention). We emit i16 result in A (with the ; high bytes don't-care). ; -------------------------------------------------------------------- .globl __ucmpdi2 __ucmpdi2: ; Compare from MSB downwards. Stash a/b first so we have a stable ; layout. jsr __divmoddi4_stash ; Compare $E6 vs $EE (a_hi_hi vs b_hi_hi). lda 0xe6 cmp 0xee bne .Lucmpdi_decided lda 0xe4 cmp 0xec bne .Lucmpdi_decided lda 0xe2 cmp 0xea bne .Lucmpdi_decided lda 0xe0 cmp 0xe8 bne .Lucmpdi_decided ; Equal. lda #0x1 rtl .Lucmpdi_decided: ; Carry clear -> a < b -> return 0. ; Carry set, Z clear -> a > b -> return 2. bcc .Lucmpdi_lt lda #0x2 rtl .Lucmpdi_lt: lda #0x0 rtl ; -------------------------------------------------------------------- ; __cmpdi2 — signed i64 compare. Same {0,1,2} return convention. ; Implemented by flipping the high-word sign bits before doing an ; unsigned compare ($N XOR $8000 swaps the signed-int order to ; unsigned-int order). ; -------------------------------------------------------------------- .globl __cmpdi2 __cmpdi2: jsr __divmoddi4_stash lda 0xe6 eor #0x8000 sta 0xe6 lda 0xee eor #0x8000 sta 0xee ; Unsigned compare on the rewritten values. lda 0xe6 cmp 0xee bne .Lcmpdi_decided lda 0xe4 cmp 0xec bne .Lcmpdi_decided lda 0xe2 cmp 0xea bne .Lcmpdi_decided lda 0xe0 cmp 0xe8 bne .Lcmpdi_decided lda #0x1 rtl .Lcmpdi_decided: bcc .Lcmpdi_lt lda #0x2 rtl .Lcmpdi_lt: lda #0x0 rtl ; -------------------------------------------------------------------- ; __udivdi3 / __umoddi3 — unsigned 64-bit divide / modulo. Restoring ; division: shift dividend left into a remainder register, conditionally ; subtract the divisor. The two libcalls share the core; quotient ; lands at $E0..$E7, remainder at $F2..$F8. Each entry sets a flag in ; X to select which to return. ; -------------------------------------------------------------------- .globl __udivdi3 __udivdi3: jsr __divmoddi4_stash jsr __udivmoddi_core brl __retdi .globl __umoddi3 __umoddi3: jsr __divmoddi4_stash jsr __udivmoddi_core ; Move remainder ($F2..$F8) -> $E0..$E7 for return. lda 0xf2 sta 0xe0 lda 0xf4 sta 0xe2 lda 0xf6 sta 0xe4 lda 0xf8 sta 0xe6 brl __retdi ; Core: dividend at $E0..$E6, divisor at $E8..$EE. ; Output: quotient at $E0..$E6, remainder at $F2..$F8. __udivmoddi_core: ; Clear remainder $F2..$F8. stz 0xf2 stz 0xf4 stz 0xf6 stz 0xf8 ldy #0x40 .Ludivmoddi_loop: ; Shift left: dividend (becomes quotient) and remainder together ; as a 128-bit register. bit shifted out of dividend top -> remainder LSB. asl 0xe0 rol 0xe2 rol 0xe4 rol 0xe6 rol 0xf2 rol 0xf4 rol 0xf6 rol 0xf8 ; Try remainder - divisor. If no borrow, accept and set quotient bit. sec lda 0xf2 sbc 0xe8 sta 0xfa ; tentative subtract result at $FA..$ lda 0xf4 sbc 0xea sta 0xfc lda 0xf6 sbc 0xec sta 0xfe lda 0xf8 sbc 0xee ; A holds new high word. C = !borrow. bcc .Ludivmoddi_skip ; Accept: remainder = remainder - divisor, quotient bit 0 = 1. sta 0xf8 lda 0xfe sta 0xf6 lda 0xfc sta 0xf4 lda 0xfa sta 0xf2 ; Set bit 0 of dividend (which we shifted left, so position is open). lda 0xe0 ora #0x1 sta 0xe0 .Ludivmoddi_skip: dey bne .Ludivmoddi_loop rts ; -------------------------------------------------------------------- ; __divdi3 / __moddi3 — signed 64-bit divide / modulo. Take absolute ; values, run the unsigned core, fix up the sign. ; div: sign(quotient) = sign(a) XOR sign(b) ; mod: sign(remainder) = sign(a) ; -------------------------------------------------------------------- .globl __divdi3 __divdi3: jsr __divmoddi4_stash ; Track signs: bit 15 of $E6 (a) and $EE (b). Save XOR in a temp. lda 0xe6 eor 0xee and #0x8000 sta 0xfa ; sign of quotient at $FA ; Abs(a) jsr __absdi_a ; Abs(b) jsr __absdi_b jsr __udivmoddi_core ; Fix quotient sign: if $FA != 0, negate $E0..$E6. lda 0xfa beq .Ldivdi_pos jsr __negdi_a .Ldivdi_pos: brl __retdi .globl __moddi3 __moddi3: jsr __divmoddi4_stash ; Mod sign = sign of a. lda 0xe6 and #0x8000 sta 0xfa jsr __absdi_a jsr __absdi_b jsr __udivmoddi_core ; Move remainder to $E0..$E6. lda 0xf2 sta 0xe0 lda 0xf4 sta 0xe2 lda 0xf6 sta 0xe4 lda 0xf8 sta 0xe6 ; Apply sign. lda 0xfa beq .Lmoddi_pos jsr __negdi_a .Lmoddi_pos: brl __retdi ; --- subroutines used by signed div/mod --- ; __absdi_a: if $E6 has sign bit set, negate $E0..$E6. __absdi_a: lda 0xe6 bpl .Labsdi_a_done jsr __negdi_a .Labsdi_a_done: rts ; __absdi_b: if $EE has sign bit set, negate $E8..$EE. __absdi_b: lda 0xee bpl .Labsdi_b_done jsr __negdi_b .Labsdi_b_done: rts ; __negdi_a: 2's complement negate $E0..$E6. __negdi_a: sec lda #0x0 sbc 0xe0 sta 0xe0 lda #0x0 sbc 0xe2 sta 0xe2 lda #0x0 sbc 0xe4 sta 0xe4 lda #0x0 sbc 0xe6 sta 0xe6 rts ; __negdi_b: 2's complement negate $E8..$EE. __negdi_b: sec lda #0x0 sbc 0xe8 sta 0xe8 lda #0x0 sbc 0xea sta 0xea lda #0x0 sbc 0xec sta 0xec lda #0x0 sbc 0xee sta 0xee rts ; -------------------------------------------------------------------- ; setjmp(jmp_buf env) - save calling environment, return 0 ; longjmp(jmp_buf env, int val) - restore environment, return val (or 1 if val == 0) ; ; jmp_buf layout (8 bytes): ; [0..1] = caller's stack pointer (SP+3 at entry to setjmp) ; [2..3] = return address PC lo:hi (16 bits) ; [4] = return address bank (1 byte) ; [5..6] = direct page register (DP) ; [7] = reserved / padding ; ; Caller-save convention: longjmp doesn't restore X / Y / A — caller's ; setjmp returned 0 with all-callee-savable regs already preserved by ; setjmp's caller. ; -------------------------------------------------------------------- ; setjmp / longjmp use the (dp),y indirect mode (opcodes 0x91/0xb1) ; to write through the jmp_buf pointer in $E0. Y is set explicitly ; before each indirect access; M=0 except where noted. .globl setjmp setjmp: sta 0xe0 ; jmp_buf addr -> DP scratch tsc ; A = current SP clc adc #0x3 ; A = caller's SP (undo JSL push) ldy #0 sta (0xe0), y ; env[0..1] = caller SP (16-bit M) lda 0x1, s ; A = retaddr lo:hi ldy #2 sta (0xe0), y ; env[2..3] = retaddr lo:hi sep #0x20 lda 0x3, s ; A_lo = bank ldy #4 sta (0xe0), y ; env[4] = bank (8-bit M) rep #0x20 tdc ; A = DP ldy #5 sta (0xe0), y ; env[5..6] = DP lda #0 ; setjmp returns 0 rtl .globl longjmp longjmp: sta 0xe0 ; jmp_buf addr -> DP scratch lda 0x4, s ; A = val (2nd arg, on stack) sta 0xe2 ; save val ; Restore SP: env[0..1]. TCS directly — the PHAs below consume ; 3 bytes (1 bank + 2 retaddr); RTL pulls them back; net post-RTL ; S = saved_SP. (Earlier this subtracted 3 before TCS, leaving ; S = saved_SP - 3 after RTL, mangling caller's stack-relative ; reads — caught by an SJLJ EH test where main's locals shifted ; by 3 bytes after longjmp.) ldy #0 lda (0xe0), y ; A = saved SP (16-bit) tcs ; SP = saved_SP ; Push retaddr: bank, then 16-bit lo:hi. RTL pulls lo, hi, bank. sep #0x20 ldy #4 lda (0xe0), y ; bank (8-bit) pha rep #0x20 ldy #2 lda (0xe0), y ; lo:hi (16-bit) pha ; Restore DP. ldy #5 lda (0xe0), y ; DP (16-bit) tcd ; Compute return value: val if nonzero, else 1. lda 0xe2 bne .Llj_done lda #1 .Llj_done: rtl ; -------------------------------------------------------------------- ; __umulhisi3_qsq: quarter-square lookup table. ; T[k] = floor(k² / 4) for k ∈ [0, 510]. Used by __umulhisi3's fast ; 8x8 path: a*b = T[a+b] - T[|a-b|]. 511 entries × 2 bytes = 1022 B. ; -------------------------------------------------------------------- __umulhisi3_qsq: .short 0x0000, 0x0000, 0x0001, 0x0002, 0x0004, 0x0006, 0x0009, 0x000c .short 0x0010, 0x0014, 0x0019, 0x001e, 0x0024, 0x002a, 0x0031, 0x0038 .short 0x0040, 0x0048, 0x0051, 0x005a, 0x0064, 0x006e, 0x0079, 0x0084 .short 0x0090, 0x009c, 0x00a9, 0x00b6, 0x00c4, 0x00d2, 0x00e1, 0x00f0 .short 0x0100, 0x0110, 0x0121, 0x0132, 0x0144, 0x0156, 0x0169, 0x017c .short 0x0190, 0x01a4, 0x01b9, 0x01ce, 0x01e4, 0x01fa, 0x0211, 0x0228 .short 0x0240, 0x0258, 0x0271, 0x028a, 0x02a4, 0x02be, 0x02d9, 0x02f4 .short 0x0310, 0x032c, 0x0349, 0x0366, 0x0384, 0x03a2, 0x03c1, 0x03e0 .short 0x0400, 0x0420, 0x0441, 0x0462, 0x0484, 0x04a6, 0x04c9, 0x04ec .short 0x0510, 0x0534, 0x0559, 0x057e, 0x05a4, 0x05ca, 0x05f1, 0x0618 .short 0x0640, 0x0668, 0x0691, 0x06ba, 0x06e4, 0x070e, 0x0739, 0x0764 .short 0x0790, 0x07bc, 0x07e9, 0x0816, 0x0844, 0x0872, 0x08a1, 0x08d0 .short 0x0900, 0x0930, 0x0961, 0x0992, 0x09c4, 0x09f6, 0x0a29, 0x0a5c .short 0x0a90, 0x0ac4, 0x0af9, 0x0b2e, 0x0b64, 0x0b9a, 0x0bd1, 0x0c08 .short 0x0c40, 0x0c78, 0x0cb1, 0x0cea, 0x0d24, 0x0d5e, 0x0d99, 0x0dd4 .short 0x0e10, 0x0e4c, 0x0e89, 0x0ec6, 0x0f04, 0x0f42, 0x0f81, 0x0fc0 .short 0x1000, 0x1040, 0x1081, 0x10c2, 0x1104, 0x1146, 0x1189, 0x11cc .short 0x1210, 0x1254, 0x1299, 0x12de, 0x1324, 0x136a, 0x13b1, 0x13f8 .short 0x1440, 0x1488, 0x14d1, 0x151a, 0x1564, 0x15ae, 0x15f9, 0x1644 .short 0x1690, 0x16dc, 0x1729, 0x1776, 0x17c4, 0x1812, 0x1861, 0x18b0 .short 0x1900, 0x1950, 0x19a1, 0x19f2, 0x1a44, 0x1a96, 0x1ae9, 0x1b3c .short 0x1b90, 0x1be4, 0x1c39, 0x1c8e, 0x1ce4, 0x1d3a, 0x1d91, 0x1de8 .short 0x1e40, 0x1e98, 0x1ef1, 0x1f4a, 0x1fa4, 0x1ffe, 0x2059, 0x20b4 .short 0x2110, 0x216c, 0x21c9, 0x2226, 0x2284, 0x22e2, 0x2341, 0x23a0 .short 0x2400, 0x2460, 0x24c1, 0x2522, 0x2584, 0x25e6, 0x2649, 0x26ac .short 0x2710, 0x2774, 0x27d9, 0x283e, 0x28a4, 0x290a, 0x2971, 0x29d8 .short 0x2a40, 0x2aa8, 0x2b11, 0x2b7a, 0x2be4, 0x2c4e, 0x2cb9, 0x2d24 .short 0x2d90, 0x2dfc, 0x2e69, 0x2ed6, 0x2f44, 0x2fb2, 0x3021, 0x3090 .short 0x3100, 0x3170, 0x31e1, 0x3252, 0x32c4, 0x3336, 0x33a9, 0x341c .short 0x3490, 0x3504, 0x3579, 0x35ee, 0x3664, 0x36da, 0x3751, 0x37c8 .short 0x3840, 0x38b8, 0x3931, 0x39aa, 0x3a24, 0x3a9e, 0x3b19, 0x3b94 .short 0x3c10, 0x3c8c, 0x3d09, 0x3d86, 0x3e04, 0x3e82, 0x3f01, 0x3f80 .short 0x4000, 0x4080, 0x4101, 0x4182, 0x4204, 0x4286, 0x4309, 0x438c .short 0x4410, 0x4494, 0x4519, 0x459e, 0x4624, 0x46aa, 0x4731, 0x47b8 .short 0x4840, 0x48c8, 0x4951, 0x49da, 0x4a64, 0x4aee, 0x4b79, 0x4c04 .short 0x4c90, 0x4d1c, 0x4da9, 0x4e36, 0x4ec4, 0x4f52, 0x4fe1, 0x5070 .short 0x5100, 0x5190, 0x5221, 0x52b2, 0x5344, 0x53d6, 0x5469, 0x54fc .short 0x5590, 0x5624, 0x56b9, 0x574e, 0x57e4, 0x587a, 0x5911, 0x59a8 .short 0x5a40, 0x5ad8, 0x5b71, 0x5c0a, 0x5ca4, 0x5d3e, 0x5dd9, 0x5e74 .short 0x5f10, 0x5fac, 0x6049, 0x60e6, 0x6184, 0x6222, 0x62c1, 0x6360 .short 0x6400, 0x64a0, 0x6541, 0x65e2, 0x6684, 0x6726, 0x67c9, 0x686c .short 0x6910, 0x69b4, 0x6a59, 0x6afe, 0x6ba4, 0x6c4a, 0x6cf1, 0x6d98 .short 0x6e40, 0x6ee8, 0x6f91, 0x703a, 0x70e4, 0x718e, 0x7239, 0x72e4 .short 0x7390, 0x743c, 0x74e9, 0x7596, 0x7644, 0x76f2, 0x77a1, 0x7850 .short 0x7900, 0x79b0, 0x7a61, 0x7b12, 0x7bc4, 0x7c76, 0x7d29, 0x7ddc .short 0x7e90, 0x7f44, 0x7ff9, 0x80ae, 0x8164, 0x821a, 0x82d1, 0x8388 .short 0x8440, 0x84f8, 0x85b1, 0x866a, 0x8724, 0x87de, 0x8899, 0x8954 .short 0x8a10, 0x8acc, 0x8b89, 0x8c46, 0x8d04, 0x8dc2, 0x8e81, 0x8f40 .short 0x9000, 0x90c0, 0x9181, 0x9242, 0x9304, 0x93c6, 0x9489, 0x954c .short 0x9610, 0x96d4, 0x9799, 0x985e, 0x9924, 0x99ea, 0x9ab1, 0x9b78 .short 0x9c40, 0x9d08, 0x9dd1, 0x9e9a, 0x9f64, 0xa02e, 0xa0f9, 0xa1c4 .short 0xa290, 0xa35c, 0xa429, 0xa4f6, 0xa5c4, 0xa692, 0xa761, 0xa830 .short 0xa900, 0xa9d0, 0xaaa1, 0xab72, 0xac44, 0xad16, 0xade9, 0xaebc .short 0xaf90, 0xb064, 0xb139, 0xb20e, 0xb2e4, 0xb3ba, 0xb491, 0xb568 .short 0xb640, 0xb718, 0xb7f1, 0xb8ca, 0xb9a4, 0xba7e, 0xbb59, 0xbc34 .short 0xbd10, 0xbdec, 0xbec9, 0xbfa6, 0xc084, 0xc162, 0xc241, 0xc320 .short 0xc400, 0xc4e0, 0xc5c1, 0xc6a2, 0xc784, 0xc866, 0xc949, 0xca2c .short 0xcb10, 0xcbf4, 0xccd9, 0xcdbe, 0xcea4, 0xcf8a, 0xd071, 0xd158 .short 0xd240, 0xd328, 0xd411, 0xd4fa, 0xd5e4, 0xd6ce, 0xd7b9, 0xd8a4 .short 0xd990, 0xda7c, 0xdb69, 0xdc56, 0xdd44, 0xde32, 0xdf21, 0xe010 .short 0xe100, 0xe1f0, 0xe2e1, 0xe3d2, 0xe4c4, 0xe5b6, 0xe6a9, 0xe79c .short 0xe890, 0xe984, 0xea79, 0xeb6e, 0xec64, 0xed5a, 0xee51, 0xef48 .short 0xf040, 0xf138, 0xf231, 0xf32a, 0xf424, 0xf51e, 0xf619, 0xf714 .short 0xf810, 0xf90c, 0xfa09, 0xfb06, 0xfc04, 0xfd02, 0xfe01