Library — math32
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)