Z88dk: newlib - maths library using Am9511A

Created on 27 Jul 2020  路  13Comments  路  Source: z88dk/z88dk

Feeling inspired by this whole Am9511A maths thing for the DAI, I laid out this Am9511A Module for the RC2014 today.

Am9511A Module

The Module supports using interrupt mode, with the Z80 NMI. But since the NMI jump address 0x66 clashes with CP/M it is not really useful, and I don't plan to implement interrupt access initially.

The library will use the same linkage as math32, but the back end will be supported by either "demand wait" or "polling" mode access to the Am9511A. Bindings to IEEE-754 32-bit floating point will be provided, so that the use of the APU is transparent.

classiclib enhancement newlib

All 13 comments

Nice idea hiding it behind math32 format - if we can create 8080 variants of the converters/utility routines we should be able to get it running on the dai as well.

Stretch goal.
As it is modular, multi-APU config should be possible (but totally impractical).

FWIW, I've added an AM9511a emulator into ticks - the code has been taken from an Enterprise128 emulator.

I've not hooked it in yet, it fudges the clock cycles but it should make testing this library a bit easier when it's done.

FWIW, I've added an AM9511a emulator into ticks.

Looks very useful.

The emulator uses a fixed multiplier for APU:CPU ticks. It would be a useful extension to make the ticks multiplier configurable, depending on the F_CPU and implementation. The APU Module above has a 1:3 multiplier off a 7.3MHz CPU. I guess the DAI is 1:1 off a 2MHz CPU. The YAZ180 is 1:16 FWIW.

I'll need to read the command register for the result flags (zero, overflow, NaN, etc) as part of the result unloading and proper conversion to IEEE-754. That was omitted from the current routines, because of being lazy. And the _APU_F_BUSY would be important to have available to break out of a "demand wait" or "polling" loop. But, I guess that for the emulator adding the correct number of ticks and returning not busy flag would be nearly the same as implementing a demand wait? But, predicting what the CPU could be doing during 7,000 ticks of polling is anyone's guess.

Reference for MBF32 format, to potentially integrate as back end to Microsoft Basic v4.7.

Synchronicity. Literally push the last commit on #1547, and the PCB package shows up. :tada:
Out with the iron and scope.

__EDIT__ So hardware and software passes the smoke test. Now to nail bugs.
__EDIT__ Fixed a hardware issue, and can see transactions, but more work required.

This is version 1 of the hardware.
IMG_0841

Some benchmarking. Generally using the APU results in between 4x speedup over math48, and a 3x speedup over math32 (eg Whetstone & n-body) and at the least about a 2x speedup over math48 (eg fasta).

@suborb The Am9511 doesn't provide a remainder in its divide function. Is it useful to provide a 'l_long_div' that only provides a quotient result?

On 8080 there鈥檚 l_long_mod that returns the remainder. l_long_div on 8080 just returns the quotient.

But on z80 the remainder is expected to be in the alt register set on return from l_long_div

Yes, that's what I thought. I can't think of a simple algorithm to generate a remainder (except subtracting the quotient multiplied), so perhaps it is not worth doing? But perhaps even subtracting the quotient multiplied would be faster than doing it in software?

__EDIT__
I think with a bit of APU agile RPN stack juggling it might be possible to generate a remainder the long way, without too much trouble. But, it would be nicer if it were unnecessary.

I'll need to check into whether and how the remainder is actually used in z80 l_long_div.

__EDIT__
For SDCC it would be sufficient to generate the quotient, and hide behind __divslong and __divslong_callee, as the remainder is only used in the __modslong and __modslong_callee functions when % is used in C.

For SCCZ80 I could also hide behind the l_long_div function as the remainder is not used. Only issue is that the mod function is generated by calling the division and then adding an exx rather than by using a separate l_long_mod call.

So, for SDCC I could generate a 32s_32/32 division without the remainder with no issue. And separately generate a remainder for the mod function x%y call by load_x push push push load_y div mul sub.

Would it be possible (at some stage) to get SCCZ80 to generate l_xxx_mod specifically?
That would enable me to provide differentiated function responses.

I was coming to the same conclusion that a separate function might be the answer.

Take a look at ldiv as well - that might need some changes to cope with the core division routine not returning the remainder.

I've done library functions for SDCC __mullong __divslong __modslong and I'm working on the __divulong __modulong functions now. I think the unsigned versions just need to reset the most significant bit of the divisor, if I'm not mistaken.

__EDIT__
Added the __XXXsint versions along the way too.

I'll also need to generate a new ldiv() too, by piping the quotient out early from the modulus routine.

__EDIT__

Removed the __XXXsint linkage, because for the RC2014 they are slower than using the Z80. Other targets with a better APU/CPU ratio will behave differently.

Ran spectral-norm benchmark (yawn)... with the integer & long support integrated.
Only the integer multiply is additionally activated by the code (along with the floating point code of course).
sdcc/newlib - RC2014 Z80 @ 7.3728Mhz

__math32__
_28 minutes 57 seconds_
1.2742190

__am9511__
_6 min 35 seconds_
1.2742140

Admittedly math32 doesn't do this particular benchmark very well, never-the-less, pretty happy with the outcome.

Was this page helpful?
0 / 5 - 0 ratings