I'm looking at building some support for the Am9511 on a z80 platform, and part of that effort would be translating from fixed to float (and back to fixed) representations.
There are some uclibc (and glibc) methods represented in single.h. And there are other methods like in avr-libc.
I think possibly the glibc version is better, because it is more general allowing configuration of the components, noting that the Am9511 does NOT support IEEE754 standard floats (the IEEE754 mantissa is one bit shorter and the exponent is 2 complement, not biased).
The library in z88dk is a 48bit version, but it doesn't seem to support IEEE754 representations, or any standard if I read correctly.
Any thoughts?
The library in z88dk is a 48bit version, but it doesn't seem to support IEEE754 representations, or any standard if I read correctly.
Yes that's right. There are two standalone float implementations in z88dk - one is called genmath and the other is called math48. Both are 48-bit float implementations. This is a natural maximum size for the z80 since two complete float parameters can be kept in registers at the same time (in math48's case the two floats are held in BCDEHL and BCDEHL').
The new c lib only supports math48 at the moment and the classic lib uses genmath except I think suborb's got sdcc working with math48 in the classic lib (partially at least?). Math48 has a little better performance in speed and size but genmath has been adapted to work with some restrictive targets like the zx81 that disallows use of some registers.
Neither math48 nor genmath are ieee and neither understands things like nan, infinity and so on, however the set of math functions provided is pretty large.
I think possibly the glibc version is better, because it is more general allowing configuration of the components, noting that the Am9511 does NOT support IEEE754 standard floats (the IEEE754 mantissa is one bit shorter).
The compilers are stuck at 32-bit floats for sdcc and 48-bit floats for sccz80. They would need modification to support more than one float type and float types of different sizes. What we do for sdcc is we convert the 48-bit float type to 32-bit and vice versa at interfaces between the sdcc compiler and the library so that sdcc only ever sees its 32-bit float and the library only ever sees its 48-bit float.
For any sort of new float type, this same kind of conversion can be done at the interface between the compilers and library. Keep in mind that sdcc's float, although 32-bit, is not formatted as an ieee float so although a 32-bit ieee float is the same size, the conversion still has to be there.
So that's the bad news. The good news is the new c library is written to accommodate different float types. The printing and scanning of floats is can (hopefully) accommodate any float library backend and it understands things like infinity, nan and so on. When given a number it queries the float library for classification (is it nan, infinity, etc) and it asks for number of significant decimal/hex digits to know when it can stop producing or reading mantissa digits.
Each float implementation can coexist in the same target library because their functions are namespaced. In math48 you can look into the z80 and c subdirectories and see the direct interface to the math48 library is all prefixed by "am48_" and "cm48_". These are the functions in all target libraries. The connection to the compilers are made when "-lm" appears on the compile line. The "m" library is composed of the functions in the lm subdirectory of math48. This too has a c and z80 subdirectory implementing the c and z80 interface and every function there is just an equate to the math48 implementation with cm48_ and am48_ prefix. The C compilers will call those lm functions by name and the -lm library will cause jumps into the math48 implementation. Likewise asm programs should call the lm asm implementation.
Any new float library would be given a new namespace and all its functions would also be in every target's library. Then it would also supply a separate -l library like "-lieee32" or something which would connect the C compiler calls to that float library implementation.
So long story short, yes it can be done. Something like the AM9511 may speed things up and reduce code size as well.
The only thing I'm concerned about in using, eg, the glibc version unmodified is that it looks fairly large with all the inlining done and it's written in C. sdcc's 32-bit (non-ieee) float library is also written in C and it comes in at about 5k larger than math48. I think longer term serious effort at porting would have to look at implementation in asm.
So that's the bad news. The good news is the new c library is written to accommodate different float types.
That is good news!
So I'll just leave my selfish thought process here.
Although the Am9511A is a really old device, it is IMHO pretty cool, as it was the first hardware Arithmetic Processing Unit APU. It predated the first FPU, the Am9512, by a few years, and actually outlived it in terms of market success.
I'm planning on building a driver for the Am9511A over the next weeks/months, and will lean heavily on the code proposed by now Prof Borko Furth in June 1984, "An Efficient Software Driver for Am9511 Arithmetic
Processor Implementation". This code focuses on the interrupt and buffer management in assembler, but the final interface into C was never built.
I was wondering why AMD used a floating point format that was quite restrictive, and would have difficulty with extremely small or large scientific constants like Planck or Avogadro. Very poor planning, IMHO.
So I dug deeper and see that that the AMD developers were simply following the precedent set by the Lawrence Livermore Labs, with their 1975 Floating Point Library. And, even better, it turns out that some people still maintain this library (2015), and it is available as 8080 ASM code on Herb Johnson's retro technology pages.
Now we have a clean contest... Does a 1975 8080 floating point library implemented on a present day Z180, outperform an Am9511A FPU (APU) from 1977? The gloves are off!
Seriously though, once I've finished playing around in ASM and Basic with these two items, it would be great to be able to include their floating point format (32bit, 24bit mantissa, 2 complement exponent) into z88dk. Particularly the LLL floating point library code, as it represents "history" passed down directly to us.
sdcc's 32-bit (non-ieee) float library is also written in C and...
_SDCC supports (single precision 4 bytes) floating point numbers; the format is somewhat similar to IEEE, but it is not IEEE; in particular, denormalized floating -point numbers are not supported._
it comes in at about 5k larger than math48.
_The floating point support routines are derived from gcc鈥檚 floatlib.c_
Is the sdcc format really non-IEEE754 enough to matter? I must admit not knowing whether the restriction on denormalised numbers is actually a big issue. Certainly for hardware FPU implementations the restriction is a "good thing".
It would be interesting to challenge the glibc floating point code in sdcc libraries to a competition with the LLL code, to see if we've learned anything in the last 40 years.
Now we have a clean contest... Does a 1975 8080 floating point library implemented on a present day Z180, outperform an Am9511A FPU (APU) from 1977? The gloves are off!
Seriously though, once I've finished playing around in ASM and Basic with these two items, it would be great to be able to include their floating point format (32bit, 24bit mantissa, 2 complement exponent) into z88dk. Particularly the LLL floating point library code, as it represents "history" passed down directly to us.
Yes it would be interesting to see. We did run the Whetstone 1.2 benchmark on a few compilers a while ago to compare floating point performance. I am planning to do a bunch of benchmarks again, this time with more z80 compilers such as zilog's (ez80 in z80 mode if it works), IAR and Softools which some people have access to and have offered to run tests. None of the compilers listed in that benchmark do ieee-32 and I found the hitech ones a bit unreliable as they sometimes generated answers that had a lot of error.
The floating point support routines are derived from gcc鈥檚 floatlib.c
Is the sdcc format really non-IEEE754 enough to matter? I must admit not knowing whether the restriction on denormalised numbers is actually a big issue. Certainly for hardware FPU implementations the restriction is a "good thing".
I looked at the documented sdcc format again (you can infer it from float.h and some of the masking macros) and it seems to imply an exponent bias of 126 rather than ieee754's 127. I can't remember if that's right or not but I do remember when implementing the interface to sdcc that there were some gotchas in the format.
If the sdcc format is the same except for denormalized numbers, nan, infinity then maybe you can get away without any conversion. However if that exponent is off by one there will have to be a conversion and a way to encode nan,infinit,denorm numbers that maps uniquely into sdcc's representation.
The reason is the compiler determines how float constants in the program are encoded. So if you run across a statement like "if (a > 5.0)..." the 5.0 is encoded in sdcc format and the "a" and sdcc-"5.0" get passed to the library. If the sdcc-"5.0" is not compatible with the library, you have to have all numbers in sdcc format when they get handed back to the compiler (and hence a conversion between compiler and library) because there is no way for the library to know which if any operands were encoded by sdcc or the library.
It would be interesting to challenge the glibc floating point code in sdcc libraries to a competition with the LLL code, to see if we've learned anything in the last 50 years.
Well, sdcc's implementation does not perform well in space or speed so it's very likely it will be beaten by the LLL implementation :) It might be interesting to see how it performs against other implementations though. The LLL one also has a drawback -- it's not reentrant which is common for 8080 code.
I'm not sure what the lineage of genmath is but math48 also has one. It was originally written by Anders Hjelsberg (he of Turbo Pascal, Delphi, C# fame) and some version of it ended up in Turbo Pascal. When we got hold of it, we fixed it up a bit and expanded the number of functions so that it's probably the most complete math library available for z80 machines. It's also reentrant and fast, working on two operands at once held in registers. In speed tests it cannot beat 32-bit libraries written in asm (see the Whetstone tests above) but it is small and so far seems faster per bit of mantissa by some margin. While fixing it up I do think a z80 float implementation could be improved by taking advantage of fast integer math routines but Anders chose to go another way in keeping everything in registers instead and that of course has speed gains as well as the reentrancy property. I have to say it is a joy to use from assembler, not just C.
The new c lib only supports math48 at the moment and the classic lib uses genmath except I think suborb's got sdcc working with math48 in the classic lib (partially at least?).
Yes it's working for both sdcc and sccz80 in classic.
The reason is the compiler determines how float constants in the program are encoded. So if you run across a statement like "if (a > 5.0)..." the 5.0 is encoded in sdcc format and the "a" and sdcc-"5.0" get passed to the library.
If you use sccz80, then you can pass -doublestr which evaluates fp at runtime (via a call to atof) which allows different formats to be supported.
There's a file z80math.zip that I've got that has an ieee754 implementation for the z80. I can't remember where I managed to download it from, but an extant link to it is here: http://www.classiccmp.org/cpmarchives/cpm/Miscellany/Emulatrs/Windows%20Z80%20Simulator/Z80%20Temple.htm
I've attached it as is, there's no licence associated with it however: z80math.zip
I believe genmath came from Xitan Disk Basic.
Just leaving this here, because the Internet never forgets (but it can't remember if it is not written down).
I finally got the Am9511A to talk to me. For a long time, I could see that the APU was trying, but just wasn鈥檛 making it.
All of the datasheets assume 74 logic is being used to create the chip select from 3 to 8 address decoding, for example. What they don鈥檛 point out is that the Am9511A APU chip select MUST be generated from the address lines, because the Z80 (Z180) IORQ line has incorrect timing to drive the Am9511A.
The Am9511A needs an 30ns of chip select and address validity after WR is raised, which the Z80 IORQ timing doesn鈥檛 provide. Using the address lines, which are held valid by the Z80 for longer, to generate the chip select, partially solves the issue.
In my solution, having "infinite" logic in a GAL available, I had (incorrectly) included the IORQ line in the APU_CS logic. Whilst I鈥檇 recognised the issue, it took three lines in an obscure 1981 PhD dissertation to focus the spotlight of clarity. Thank you Mr Haining.
Also, as well as the address lines, the Chip Select and Data bus need to be valid for 30ns following the release of the write signal. On the Z180 this can be achieved by gating the write signal through the E Clock. The E Clock is one half T cycle shorter on than write, and this provides the necessary 30ns hold for select and data signals.
Something that may be of interest here, as I was playing with this cool old chip too: http://obex.parallax.com/object/901
https://github.com/zpekic/am9511
Now being addressed in #1505, and by sccz80.