Classic Maths Libraries - z88dk/z88dk GitHub Wiki

Fixed point maths (z80/z80n/z180/ez80_z80/kc160/Rabbit)

The classic library provides a Q8.8 fixed point ( 1 sign bit, 7 bits integer, 8 bits fraction) maths library that is defined via <math/math_fix16.h>. A full range of mathematical functions are available, however due to the limited size of the data type accuracy may be limited.

The Q8.8 is defined as type Accum and is natively supported by sccz80 as the _Accum type but not by sdcc. As a result, with sccz80 the regular arithmetical operators can be used, but with sdcc, mulk, divk functions need to be called explicitly. Due to the properties of the data type, addition and subtraction can be performed using the +/- operators, though addk and subk are provided for completeness.

Mathematical operations are provided and can be accessed by suffixing k to the usual floating point function name, for example to call the sin function, use sink().

Conversions are provided to convert from Q8.8 to integer FIX16_TO_INT(x) and floating point FIX16_TO_FLOAT() formats. Conversion from an integer is available via FIX16_FROM_INT(x) and from a floating point number with FIX16_FROM_FLOAT(x). These conversions are constant operations and can be evaluated at compile time if necessary.

If you need to print fixed point numbers, the Accum value should be converted to a floating point value (using any floating point library described below) using the FIX16_TO_FLOAT(x) macro.

No additional compiler options are needed to use fixed point maths.

Floating point maths

The classic library supports applications being compiled with different floating point maths libraries. Selecting a different library will have implications regarding the size, performance and which machine configuration an application can run in.

When compiling with zsdcc a float in C code is always allocated 4 bytes, and is represented in IEEE 754 single-precision format. However with sccz80, the size of a float depends on which maths library is being used. Traditionally, sccz80 has always allocated a 6 byte (48 bit) block for representing floats, but it also supports maths libraries that use a 4 byte (32 bit) and a 8 byte (64 bit) floating point number.

Generally the 4 byte floating point libraries are substantially faster than the more accurate 6 byte libraries, as handling the additional mantissa resolution does not come for free. For non-critical applications, such as graphics and games, single-precision 4 byte floating point libraries will provide sufficient accuracy with either compiler.

To obtain maximum floating point granularity the combination of sccz80 and math48 should be preferred, as sccz80 preserves the full resolution generated by the math48 library.

When using sdcc the additional granularity of the 6 byte floating point libraries is not useful, as the compiler internal IEEE 754 single-precision 4 byte floating point format abandons the least significant 16 bits of the mantissa on each conversion.

48 bit genmath - z80/z80n/ez80_z80 - -lm

Genmath is z88dk's traditional maths library. It uses a 6 byte (48 bit) floating point format, with an 8 bit exponent and a 40 bit mantissa. It utilises a single index register and runs on all of classic's targets (subject to memory). It should be noted that the library makes extensive use of the ixh and ixy registers and as such cannot be used on the Z180 or clones that do not support them.

Genmath can only be used with the sccz80 compiler.

48 bit maths by Anders Hejlsberg - z80/z180/z80n/kc160/ez80_z80/Rabbit - -lmath48

math48 is the default newlib floating point library, and it has been imported into classic from the newlib. It uses a 6 byte (48 bit) floating point format, with an 8 bit exponent and a 40 bit mantissa. It utilises the z80 alternate register set and therefore can't run on all classic targets.

math48 can be used with both sccz80 and zsdcc compilers, and is marginally faster than genmath.

BBC Maths - (40 bit maths from BBC BASIC for z80) - z80/z180/z80n/ez80_z80/kc160/Rabbit

The BBC maths library provides a 32 bit mantissa and 8 bit exponent. It's the same library as the native maths library on the z88. The library makes extensive use of the alternate register set. It can be linked with the alias: --math-bbc. BBC Maths can only be used with the sccz80 compiler.

CPC Maths - (40 bit maths from Amstrad CPC) - z80/z180/z80n/ez80_z80

The CPC maths library provides a 32 bit mantissa and 8 bit exponent. It's the same library as the native maths library on the CPC. It can be linked with the alias: --math-cpc. It utilises both index registers so care must be taken when using it.

CPC Maths can only be used with the sccz80 compiler.

MBF32 - (32 bit maths extracted from Microsoft BASIC) - 8080/8085/gbz80/z80/z180/z80n/ez80_z80/kc160/Rabbit

Support has been added for the 4 byte (32 bit, 8 bit exponent, 24 bit mantissa) Microsoft single-precision library.

This library can be used in two ways. As a self-contained maths library that can run on any of the supported processors and as stub library that utilises the ROM code for targets that run Microsoft BASIC and the appropriate entry points have been located. To date, the following machines are supported for this use case:

  • VTech Laser 500
  • Mitsubishi Multi 8 (10k mode only)
  • Mattel Aquarius
  • TRS80/Color Genie
  • CCE MC-1000

The library can be linked with the following alias: --math-mbf32.

Some basic optimisation for the 8085 (using undocumented instructions) and z80 has been undertaken. Mainly this involves more effective load and store routines, and improved register shift processes.

MBF32 can only be used with the sccz80 compiler.

DAI32 Maths - (32 bit maths extracted from the DAI target ROM) - 8080/z80/z180/z80n/ez80_z80/kc160/Rabbit

This maths library was extracted from the DAI computer, it offers, a 4 byte, 7 bit exponent, 24 bit mantissa floating point number. It's of particular interest since the floating point format is compatible with the AM9511.

The library can be linked with the following alias: --math-dai32.

DAI maths can only be used with the sccz80 compiler.

MBF64 - (64 bit maths from Microsoft) - z80

Support has been added for the (8 byte, 8 bit exponent, 56 bit mantissa) Microsoft double precision library. This is available for machines that run Microsoft BASIC and the appropriate entry points have been located. To date, the following machines are supported:

  • VTech Laser 500
  • TRS80/Color Genie

mbf64 can only be used with the sccz80 compiler.

32 bit maths using IEEE-754 format - z80/z180/z80n/ez80_z80/kc160/Rabbit - --math32

math32 provides a 32 bit floating point format that is mostly compliant with IEEE-754, which is also the native floating point format of zsdcc. The library can be used with both sccz80 and zsdcc.

math32 supports the z180 (ez80_z80) and ZX Spectrum Next z80n unsigned 16_8x8 hardware multiply instructions, providing accelerated performance for these platforms. The r2ka (Rabbit) signed 32_16x16 hardware multiply and 16-bit register shift instructions are supported, providing very good accelerated performance. The z80 CPU is supported through 24_16x8 and 32_16x16 unrolled mantissa multiply routines which provide good performance.

The intrinsic functions are written in assembler. The higher level functions (trigonometric, exp, pow) are implemented by C functions extracted from the Cephes Maths Library and the Hi-Tech C Floating point library.

More details on the library can be found within the repository.

16 bit maths using IEEE-754 format - z80/z180/z80n/ez80_z80/kc160/Rabbit - --math16

math16 provides a 16 bit floating point format that is mostly compliant with IEEE-754. The library can be used with both sccz80 and zsdcc.

math16 supports the z180 (ez80) and ZX Spectrum Next z80n unsigned 16_8x8 hardware multiply instructions, providing accelerated performance for both these platforms. The r2ka (Rabbit) signed 32_16x16 hardware multiply and 16-bit register shift instructions are supported, providing very good accelerated performance. The z80 CPU is supported through 32_16x16 unrolled mantissa multiply routines which provide good performance.

The IEEE 16 bit floating point standard supports a maximum of 3.5 significant digits of accuracy, but it is very fast. It is useful for graphics, as per this Cambridge Z88 example comparing the IEEE16 library with the standard maths library.

Cube example on Cambridge Z88

The intrinsic functions are written in assembler. The intrinsic type is _Float16 for sccz80, or half_t for both sccz80 or sdcc.

The sccz80 compiler supports _Float16 as a native type, and therefore arithmetic or comparison operations on variables defined as _Float16 or half_t will be done without conversion. The sdcc compiler doesn't support intrinsic 16 bit floating point operations, but functions can still be called as needed.

The math16 library is considered an adjunct library. When printing format conversion and other facilities are required, it is linked together with a main maths library (which provides the conversion routines). The invocation command will typically include --math32 --math16, or -lm --math16 to provide both main and adjunct maths libraries.

More details on the library can be found within the repository.

Machine specific libraries

The following machines have maths libraries that utilise the ROM routines which can result in a compact binary. Link with -lmz to use these libraries:

  • Cambridge z88
  • ZX Spectrum
  • ZX 81
  • Amstrad CPC

Additionally, various machines that run Microsoft BASIC can use --math-mbf32 and the ROM routines will be automatically used.

These libraries can only be used with the sccz80 compiler.

Maths library aliases

Aliases are provided to make usage of maths libraries straight forward. Including the alias in the zcc invocation will provide all necessary configuration options relevant for each maths library.

classic

  • --math-mbf32 is alias for-Cc-fp-mode=mbf32 -lmbf32@{ZCC_LIBCPU} -pragma-define:CLIB_32BIT_FLOATS=1 -Cc-D__MATH_MBF32 -Ca-D__MATH_MBF32 -D__MATH_MBF32
  • --math-mbf64 is alias for-Cc-fp-mode=mbf64 -pragma-define:CLIB_64BIT_FLOATS=1 --lmbf64
  • --math-bbc is alias for-Cc-fp-mode=z88 -lbbc_math@{ZCC_LIBCPU}
  • --math-dai32 is alias for-Cc-fp-mode=am9511 -ldaimath32@{ZCC_LIBCPU} -pragma-define:CLIB_32BIT_FLOATS=1 -Cc-D__MATH_DAI32 -Ca-D__MATH_DAI32 -D__MATH_DAI32
  • --math-am9511 is alias for-Cc-fp-mode=ieee -lam9511@{ZCC_LIBCPU} -pragma-define:CLIB_32BIT_FLOATS=1 -Cc-D__MATH_AM9511 -Ca-D__MATH_AM9511 -D__MATH_AM9511
  • --math-cpc is alias for-Cc-fp-exponent-bias=128 -Cc-fp-mantissa-size=5 -lcpcz80_math -D__MATH_CPC

Where @{ZCC_LIBCPU} is replaced with the appropriate library for the selected CPU type.

classic + newlib

The --math16 and --math32 aliases are supported across z80, z180, and z80n (SpectrumNext) architectures, and automatically provides each target with the correct hardware multiply opcodes (or for z80 emulation code) to enable best performance.

The math16 library uses 16-bit (integer) memory moves extensively, and therefore optimising code by in-lining memory get and put subroutines has a positive impact on performance, without increasing program size substantially.

  • --math16 is alias for-lmath16@{ZCC_LIBCPU} --opt-code-speed=inlineints -Cc-D__MATH_MATH16 -Ca-D__MATH_MATH16 -D__MATH_MATH16
  • --math32 is alias for-Cc-fp-mode=ieee -lmath32@{ZCC_LIBCPU} -pragma-define:CLIB_32BIT_FLOATS=1 -Cc-D__MATH_MATH32 -Ca-D__MATH_MATH32 -D__MATH_MATH32
  • --am9511 is alias for-Cc-fp-mode=ieee -lam9511 -pragma-define:CLIB_32BIT_FLOATS=1 -Cc-D__MATH_AM9511 -Ca-D__MATH_AM9511 -D__MATH_AM9511

Where @{ZCC_LIBCPU} is replaced with the appropriate CPU for classic, and with an empty string for newlib.

Benchmarks

The maths libraries have been lightly benchmarked using a couple of test programs within the repository. These were tested using the classic library and the following compilation line:

zcc +test -DPRINTF [file.c] [maths flags]

As a result, the numbers include the time spent printing, however this isn't particularly time consuming in the overall scheme:

n-body

Library Compiler Value 1 Value 2 Ticks
correct values --> -0.169075164 -0.169087605
genmath sccz80 -0.169075164 -0.169087605 3_652_736_949
mbf32 (gbz80) sccz80 -0.169075083 -0.169086337 2_555_855_304
math48 sccz80 -0.169075164 -0.169087605 2_377_856_525
cpcmath sccz80 -0.16907516 -0.16908760 2_110_177_022
mbf32 (z80) sccz80 -0.169075083 -0.169086337 1_936_249_932
daimath32 sccz80 -0.1690751 -0.1690863 1_899_271_269
bbcmath sccz80 -0.16907516 -0.16908760 1_655_789_776
math32 sccz80 -0.1690752 -0.1690867 0_993_265_277 *
math32_z80n sccz80 -0.1690752 -0.1690867 0_576_942_516 *
math32_z180 (ez80) sccz80 -0.1690752 -0.1690867 0_563_700_933 *

spectral-norm

Library Compiler Value Ticks
correct value --> 1.274219991
genmath sccz80 1.274219989 14_817_735_124
math32 zsdcc 1.274219 10_003_973_822
math32 sccz80 1.274219 09_718_997_187
math48 sccz80 1.274219989 09_035_519_932
bbcmath sccz80 1.27421999 08_017_859_189
mbf32 (gbz80) sccz80 1.274220 07_941_220_888
daimath32 sccz80 1.274220 07_483_466_471
cpcmath sccz80 1.27421998 06_834_032_841
mbf32 (z80) sccz80 1.274220 06_754_491_551
math32_z80n sccz80 1.274219 06_396_544_633
math32_z180 (ez80) sccz80 1.274219 06_120_760_761

mandelbrot

Library Compiler Ticks
genmath sccz80 3_589_992_847
math48 zsdcc 3_766_086_833
math48 sccz80 3_266_168_305
math32 zsdcc 1_410_662_416
math32 sccz80 1_142_045_217 *
math16 sccz80 1_103_113_465
math32_z80n sccz80 0_922_658_537 *
math32_z180 (ez80) sccz80 0_892_842_610 *