Library — math32
SkillDev toolsmath32 IEEE single float library: multi-CPU layout (asm/z80, asm/8085, asm/8080, asm/gbz80), products math32*.lib including ez80_z80 / gbz80, rounding policy, div=restoring / inv=NR, force rebuild. Use when editing libsrc/math/float/math32 or A/B float divide/mul.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Library — math32 skill
What this skill tells your AI
The instructions your AI receives, as published by z88dk/z88dk in .agents/skills/library-math32/SKILL.md and read by ahel’s review.
Home: libsrc/math/float/math32/. Docs of record: libsrc/math/float/math32/readme.md.
Link via --math32 (-lmath32@{ZCC_LIBCPU}).
0b. Math32 multi-CPU float library (layout + policy)
Home: libsrc/math/float/math32/. Products: math32.lib (plain z80) plus
math32_{z80n,z180,ez80_z80,r2ka,kc160,8085,8080,gbz80,…}.lib. Link via
--math32 (-lmath32@{ZCC_LIBCPU} — e.g. 8085 → math32_8085, 8080 →
math32_8080, gbz80 → math32_gbz80, ez80_z80 → math32_ez80_z80; no
separate --math32_8085 flag).
Layout
| Tree | Role |
|---|---|
asm/z80/ | Z80-family cores; shared by z80n/z180/ez80_z80/r2ka/… when the lst points here |
asm/8085/ | Stack-only 8085 cores (no EXX / IX / IY); extended opcodes + synthetics. After rl de, test exp with inc d/dec d — RDEL does not write Z |
asm/8080/ | Stack-only 8080 cores (original ISA; no 8085 extras). ld hl,sp+n; park HL |
asm/gbz80/ | Stack-only Game Boy cores (ld hl,sp+*, bit 7 leading-one; no cheap ex) |
c/z80/, c/8085/, c/8080/, c/gbz80/ | Higher functions (C → precompiled asm); 8080/8085/gbz80 higher via sccz80 only |
newlibfiles_*.lst | Which modules land in each product (newlibfiles_ez80_z80.lst, newlibfiles_gbz80.lst, …) |
CPU-specific = same operation name, different ISA file (same one-op-per-file map as §0). Do not invent a second taxonomy for 8085 / gbz80.
eZ80 Z80-mode product (mlt)
eZ80 Z80-mode has the same mlt encodings as Z180 (ED 4C/5C/6C/7C).
math32_ez80_z80.lib is built from newlibfiles_ez80_z80.lst, which
selects the Z180 mantissa helpers (f32_z180_mulu_* / f32_z180_sqr_*).
Those helpers are gated:
IF __CPU_Z180__ | __CPU_EZ80__ | __CPU_EZ80_Z80__
| Assemble as | Define set |
|---|---|
-mz180 | __CPU_Z180__ |
-mez80 (ADL) | __CPU_EZ80__ |
-mez80_z80 | __CPU_EZ80_Z80__ |
Do not build eZ80 from @newlibfiles_z80.lst alone: the plain-Z80 helpers
are IF __CPU_Z80__ only and assemble empty on -mez80_z80. Suite gate:
test/suites/math → test_math32_ez80_z80.bin.
Rounding policy (do not mix casually)
| Class | Policy (current math32) |
|---|---|
| mul / sqr / div / poly / sqrt pack | IEEE RNE on residual below the kept mantissa |
| add / sub | Digi jam-sticky: lost align/overflow bits → OR 1 into mant LSB; pack has no RNE residual |
Long add chains (e.g. n-body energy) are sensitive to add rounding: jam keeps second-energy error ~1e−6 class; full RNE-on-add has been measured to worsen E1 (~5e−5 class) at higher cost. Prefer matching z80 and 8085 policy even when engines are not bit-identical.
Divide / inverse (current policy)
| Op | Algorithm | Notes |
|---|---|---|
div / m32_fsdiv | Restoring 24-bit mantissa | z80 + 8085 + 8080 + gbz80 cores; z80n/z180/ez80_z80 share z80 asm/z80/f32_fsdiv.asm |
inv / m32_fsinv | Newton–Raphson | Slower than div for a reciprocal. HW mul helps inv only |
invsqrt / m32_fsinvsqrt | Quake seed + 3× NR | Still the fastest 1/sqrt. Do not replace with 1.0/sqrt |
| math16 | Same split: restoring asm_f16_div, NR asm_f16_inv |
Do not reintroduce NR trampoline fsdiv = fsinv+fsmul without A/B proof.
Docs: math32/readme.md § div/inv; measurement: z88dk-tooling § A/B.
In C higher functions (c/m32_*.c):
| Need | Write | Do not write |
|---|---|---|
Reciprocal 1/n | 1.0/x (restoring div) | m32_invf(x) |
| Inverse square root | m32_invsqrtf(x) | 1.0/m32_sqrtf(x) |
pow(x, -1) / sinh / cosh / tanh / atan recip / asinh / acosh use divide. pow(x, -0.5) keeps m32_invsqrtf.
IEEE bit punning: use union float_long from c/m32_math.h (float f; int32_t l). Do not invent a local uint32_t union. Assign NAN_* / INFINITY_* as (int32_t)NAN_NEG_F32 so zsdcc does not warn on unsigned→signed.
Micro-opt patterns that port
| Pattern | Idea | Notes |
|---|---|---|
| Implicit-1 CF | ld a,255 / add a,h → CF=(exp≠0) instead of or a / jr Z / scf | ~8–9 T per unpack; works on Z80 and 8085 |
| Z80 non-callee stack load | 3×pop + 3×push vs hl=sp+2 walk | ~8 T; 8085 stack-slot paths already different |
| Hot tiny helpers | Inline 2–4 insn jam sticky at call sites | Saves call/ret; size often net-neutral or smaller |
| Unused stack pad | Drop frame slots only after proving no SP offsets still use them | Remeasure; fix every sp+N comment |
Callee linkage: float helpers that pop a return address + stack args must be
call’d, not bare jp (floor/ceil class bugs). Keep that rule when
editing pack/add glue.
sccz80 + newlib: plain names vs *_fastcall (issue #3061)
math32.lib products are assembled with -D__CLASSIC. Under that flag,
lm32/c/sccz80/{sin,sqrt,log,…}.asm expose stack-arg bridges for plain
names; the DEHL entry is *_fastcall (defc sin_fastcall = _m32_sinf).
| Consumer | What must happen |
|---|---|
Classic include/math/math_math32.h | #define sin(x) sin_fastcall(x) (and peers) — already correct |
Newlib sccz80 + --math32 | Same remaps under __MATH_MATH32 in include/_DEVELOPMENT/proto/math.h, then make -C include/_DEVELOPMENT common/math.h |
| SDCC | Single-arg __DPROTO already emits #define sin(a) sin_fastcall(a) — OK without the sccz80 block |
| math48 default newlib | Plain sin is true DEHL (cm48_sccz80_sin) — do not force math32 remaps off math48 |
Without the newlib remaps, sccz80 marks plain sin as __z88dk_fastcall and
emits call sin with DEHL, but the linked object is the stack bridge (ignores
DEHL). Symptom: hotspots show zero m32_fsinv / m32_fsinvsqrt /
m32_fsmul32x32 entry hits, TIMER “too fast” (e.g. Whetstone ~15 KWIPS vs ~11),
wrong numerics. Map proof of a healthy build: sin_fastcall / sqrt_fastcall
present; app .asm shows callsin_fastcall not callsin.
Do not “fair up” classic vs newlib by adding bench-only invsqrt() optims
to one side only (n-body). Align source (1.0/sqrt vs half invsqrtf16) and
remeasure both products after header fixes.
Higher-function C regen (c/Makefile)
Z80 higher funcs: make -C libsrc/math/float/math32/c → c/z80/*.asm (SDCC).
8085: make -C …/c 8085 → c/8085/*.asm (sccz80 only).
8080: make -C …/c 8080 → c/8080/*.asm (sccz80 only).
gbz80: make -C …/c gbz80 → c/gbz80/*.asm (sccz80 only). make clean must
only remove C-derived objects — never wipe hand-written peers in the same dir
(math16: keep cm16_sccz80_*.asm under c/8085/).
Measure / rebuild / wiki: methodology-measure
(§ math32 rebuild, benches, classic/newlib A/B, wiki bold rules). Docs of record:
libsrc/math/float/math32/readme.md, support/benchmarks/*/readme.txt.
Force rebuild (multi-CPU)
Math32 / multi-CPU float libs (force rebuild)
Sources: libsrc/math/float/math32/ (per-CPU under asm/z80/, asm/8085/, …).
Shared Z80-family add lives in asm/z80/d32_fsadd.asm and is assembled into
each of math32.lib, math32_z80n.lib, math32_z180.lib,
math32_ez80_z80.lib, math32_r2ka.lib, math32_kc160.lib, … Changing that
file requires rebuilding every product that lists it, not only
math32_8085.lib.
cd libsrc/math/float/math32
# force one object + relink (example: 8085 add)
rm -f obj/8085/math/float/math32/asm/8085/f32_fsadd.o ../../../math32_8085.lib
z88dk-z80asm -d -I"$ZCCCFG/.." -O=obj/8085/x/x/x -I.. -m8085 -D__CLASSIC \
@newlibfiles_8085.lst
TYPE=8085 z88dk-z80asm -d -I"$ZCCCFG/.." -I.. -m8085 \
-x../../../math32_8085 @math32.lst
cp -f ../../../math32_8085.lib ../../../lib/clibs/ # or: make -C libsrc install
# Z80-family products that share asm/z80/d32_fsadd.asm (repeat per CPU)
for cpu in z80 z80n z180 ez80_z80 r2ka kc160; do
case $cpu in
z80) lst=newlibfiles_z80.lst; lib=math32; masm=z80 ;;
ez80_z80) lst=newlibfiles_ez80_z80.lst; lib=math32_ez80_z80; masm=ez80_z80 ;;
*) lst=newlibfiles_${cpu}.lst; lib=math32_$cpu; masm=$cpu ;;
esac
rm -f obj/$cpu/math/float/math32/asm/z80/d32_fsadd.o ../../../$lib.lib
z88dk-z80asm -d -I"$ZCCCFG/.." -O=obj/$cpu/x/x/x -I.. -m$masm -D__CLASSIC @$lst
TYPE=$cpu z88dk-z80asm -d -I"$ZCCCFG/.." -I.. -m$masm -x../../../$lib @math32.lst
cp -f ../../../$lib.lib ../../../lib/clibs/
done
Or: make -C libsrc/math/float/math32 then install all math32*.lib into
lib/clibs/. After install, delete suite/bench .bin/.map before remeasure.
Prove the object is current: z88dk-z80nm lib/clibs/math32_8085.lib | rg 'f32_fsadd|ay16_njam'.
For eZ80: z88dk-z80nm lib/clibs/math32_ez80_z80.lib | rg 'm32_mulu_32h|f32_z180'.
Related
- Half float:
library-math16 - Measure / A/B:
methodology-measure - Newlib headers (math remaps):
library-newlib· edit proto then regenerate - 8085 cores:
cpu-8085· 8080:cpu-8080· gbz80:cpu-gbz80 - Z180 / eZ80 Z80-mode
mlt:cpu-z180 - Issue class: z88dk #3061 (classic vs newlib Whetstone)
- Suite:
test/suites/math(test_math32*.bin, includingtest_math32_ez80_z80.bin)
Signals
- GitHub stars
- 1k
- Forks
- 206
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
library-math32- Source
- github.com/z88dk/z88dk