BASICVFP considerations
=======================

"BASICVFP" aims to produce a release of BASIC which uses the VFP instruction set for floating point math, in place of the obsolete FPA instruction set used by BASIC64.

FPA supports single, double, and extended double precision IEEE 754 floats. VFP only supports single and double precision. Lukcily BASIC64 only uses double precision floats, so on the surface it would appear that a VFP version of BASIC64 would be trivial to produce, without requiring any loss of precision. However there are some other differences between FPA and VFP which make things a bit more complicated.


FPA vs. VFP issues
==================

Word order
----------

FPA stored double-precision floats using a big-endian word order, i.e. when using STFD to store a double-precision float in memory the most significant 32 bits of the 64 bit value were stored in the lowest word of the pair.

On the other hand, VFP uses a little-endian word order (or more correctly, the endianness of the entire 64-bit value will match the configured data endian mode of the CPU).

This has the potential to cause compatibility issues with any interfaces where the memory representation of BASIC floats are exposed to the wider world.

No support for trig/pow instructions
------------------------------------

The FPA instruction set has a wide range of trigonometric and power functions available, and BASIC64 made use of them for implementing its trig and power operations.

The only power function VFP implements is square root. Therefore additional effort will be required to implement the full range of trig/power operations in BASICVFP.

Since double-precision floats offer higher precision than BASIC 5-byte floats, we cannot simply re-use the 5-byte float code. However as a stopgap solution we could conceivably perform the operations using the FPA instruction set (which would in turn rely on FPEmulator to implement the operation in software, at 80 bit precision)

Exception handling
------------------

FPA had full support for hardware trapping of floating point exceptions (division by zero, NaN, etc.). BASIC64 relied on this for generating most of the floating point errors.

On the other hand, hardware trapping of exceptions is optional in VFP. The hardware can still detect when an error has occured and set the relevant bit(s) in the FPSCR, but in most VFP implementations software must manually poll the register to detect any errors. This will likely result in the VFP code sequences being longer than the FPA sequences, and some care may be needed to try and avoid pipeline stalls caused by reading the FPSCR.

VFP context creation
--------------------

FPEmulator provides the system with a default FPA context which programs can make use of. VFPSupport, on the other hand, requires that each program makes its own VFP context. Therefore some extra logic will be needed on startup/shutdown to create/destroy a VFP context for BASICVFP. Care will also be needed on any entry points (e.g. error environment handler) to make sure that BASIC restores its VFP context before calling any FP code.


VFP advantages
==============

Some VFP implementations can perform operations on vectors of numbers, which could be used to speed up array/matrix operations.

There's also the potential to use the NEON instruction set for integer operations (again, most likely for array/matrix operations)


BASIC64 specification review
============================

The following are key points from the BBC BASIC reference manual (ISBN 1 85250 103 0), with regards to BASIC64

"Exchanging data between BBC BASIC and other languages, like C, is now easier" (p6)

  Although the Norcroft C compiler is currently restricted to FPA, GCC has supported VFP under RISC OS for some time now. So it's worth considering which word order (VFP or FPA) will make data exchange easier in the future.

"The '|' indirection operator" (p161)

  Depending on whether BASIC V or BASIC VI are in use, this will access either a 5-byte float or a (FPA) double-precision float. Consideration is necessary for what word order to use with VFP.

"When an error occurs ... the values of all the variables and so on will still be intact" (p164)

  I.e. when handling VFP errors we need to make sure we check for any error before we write back potentially erroneous values to program variables.

"Format of the CALL parameter block" (p224)

  CALL and USR allow 8-byte floats to be passed to assembler routines. This type will only ever be used in BASIC64, but some consideration is necessary as to whether FPA or VFP word order should be used in BASICVFP.

  If we are to aim for BASICVFP to be a drop-in replacement for BASIC64 then it is only natural for FPA word order to be used. However since FPA is obsolete, it would be more convenient if there was a version of BASIC available which used VFP word ordering, and allowed assembler code to make use of the VFP context which BASIC had created. This would then allow BASICVFP programs to easily be augmented with VFP or NEON assembler routines.

"VARIND" (p229), "STOREA" (p229), "EXPR" (p231)

  The BASIC64 version of these routines use the F0 register to pass and return floating-point values. For BASICVFP this raises two possibilities:

  * Maintain use of the F0 register. For optimal performance BASIC could have two versions of the routines; an internal routine which uses the VFP D0 register, and the external form (as exposed to assembler routines) which uses the FPA F0 register. However this may be difficult to achieve as some routines are specified as not using the stack.

  * Only use VFP D0. This will result in optimal performance but will break compatibility with BASIC64 assembler.

"INPUT#" (p294)
"PRINT#" (p347)

  An exact specification of the BASIC64 float format is given here, describing the fact that the words are stored in big-endian order. Additionally, both versions of BASIC are capable of reading both 5-byte and 8-byte floats. Therefore for continued interoperability of data files the only sensible choice is to have BASICVFP use FPA word ordering for floats when reading and writing files.

"OSCLI" (p334)

  This makes mention of how some of the interpreter state (e.g. CALL environment information pointer) is exposed to the executed command. Notably, there does not appear to be any way for the executed command to determine whether BASIC or BASIC64 is in use. Potentially a program could probe the end of the CALL environment block to look for the 5-byte float routines. However this does not help us if we require executed commands to be able to differentiate calls made from BASIC64 and calls made from BASICVFP.

"Numeric types" (p411)

  The diagram on p411 represents the storage format of 8-byte floats in BASIC64 (i.e. FPA double precision floats)


BASIC64 code review
===================

FPSR
----

On entry to the interpreter, BASIC64 initialises the FPSR to &70000, i.e. the Invalid Operation, Division by Zero and Overflow exception traps will be enabled. At no other point during execution is the FPSR read or written, creating an implicit contract between BASIC64 and the user program that correct operation is only guaranteed if the program does not program the FPSR with conflicting settings.

Also of note is that the FPSR is not reset when an error occurs - any code which manipulates the FPSR (e.g. a SWI) and then generates an error without restoring the correct value may break BASIC.

FPOINT
------

The FPOINT assembly constant is used to select between BASIC (FPOINT=0) and BASIC64 (FPOINT=1), allowing all relevant code to easily be located. An obvious extension of this would be to use FPOINT=2 for BASICVFP.

Workspace layout
----------------

Although the program and its variables can be relocated (by manipulating the PAGE, TOP, LOMEM and HIMEM variables), BASIC is also reliant on a block of non-relocatable workspace which is anchored to the start of application space (&8000). This static workspace would make a suitable place for storing the VFP context.


BASICVFP proposal
=================

Two of the primary advantages of BASIC64 over standard BASIC are the increased accuracy of floating point calculations, and speed (when FPA hardware is available).

BASICVFP can't improve on precision (FPA supported IEEE extended double precision floats but VFP only supports single and double precision), but due to the prevalence of VFP hardware it can provide significant speed improvements over BASIC64. Therefore the main focus on this proposal is on gaining as much speed as possible, without being too concerned about compatibility with programs that made advanced use of BASIC64 features. I.e. "Generic" BASIC programs which don't make any assumptions about the floating point format (and thus run fine under BASIC or BASIC64) will continue to run under BASICVFP, but programs which assume FPA word ordering or FPA register usage will fail.

In detail:

* INPUT# and PRINT# will use FPA word ordering for double-precision floats, to maintain data file compatibility between different BASIC versions

* All other interfaces will use VFP word ordering and VFP registers (e.g. the "|" operator will use VFP word ordering, and VARIND will return any floating-point value in D0)

* On startup BASIC will create a "full" VFP context, i.e. supporting both VFP+NEON and with all data registers available. This will allow assembler code to easily reuse the context for its own VFP/NEON calculations (although it is still the responsibility of the program to ensure the appropriate instructions/registers are available before it attempts to use them)

* The VFP context will be stored in the non-relocatable workspace located at &8000. However since the context size is variable, the amount of memory to reserve for the context will have to be determined at runtime.

* The FPSCR will be initialised to zero. It will be reset to zero by the error handler (ensuring correct recovery from code which uses VFP short vectors), but other than that it's expected that code will not return to BASIC with it set to a value that will conflict with BASIC's VFP usage.

* FP exceptions will be detected by polling the cumulative exception bits in the FPSCR. For consistency with BASIC64 only the following exceptions will generate errors:
  * Invalid Operation
  * Division by Zero
  * Overflow

* The initial version of BASICVFP is expected to rely on FPA/FPEmulator for implementing the trig/power operations that are not supported by VFP. Future versions may provide more optimal routines, e.g. based on the routines used by Steve Drain's "Float" module.

* The initial version of BASICVFP is not expected to use VFP short vectors or NEON vectors to accelerate array/matrix operations. However these are viable future improvements.

* The initial version of BASICVFP is not expected to tackle the issue of allowing commands executed via OSCLI to detect that BASICVFP is in use.

* A BASICVFP build of the module will be selected by setting FPOINT to 2.

* Since BASICVFP will be incompatible with some BASIC/BASIC64 programs, it will have to respond to a separate command (i.e. '*BASICVFP'), and will most likely be a separate module. A future goal might be to produce a version of BASICVFP which provides "full" BASIC64 compatibility, at the expense of some speed.

* DCFD behaviour will be left alone, for now at least (see discussion below)


Other considerations
====================

Trig/power operations
---------------------

Since VFP doesn't provide trig/power operations, it may be desirable to expose BASICVFP's trig/power functions to assembler code in a similar manner to how the 5-byte float operations are exposed to BASIC V assembler routines. The addition of these extra routines could also serve as a way for commands executed via OSCLI to detect that BASICVFP is in use. However, there is no standard defined for how this list of routines should be extended, so some care may be needed in order to avoid compatibility issues with code which detects BASIC/BASIC64 via checking for the presence of the 5-byte float ops.

ROM space
---------

If BASICVFP is to be included in ROM, it is worth revisiting the idea to split the assembler out into a separate module, to allow it to be shared between all three implementations: https://www.riscosopen.org/forum/forums/3/topics/903

Alternatively some ROM space could be saved by dropping BASIC64 from ROM for machines where VFP is available.

DCFD
----

The DCFD assembler directive has three forms:

* DCFD <number> - produces a FPA-format double precision float
* DCFD.fpa <number> - produces a FPA-format double precision float
* DCFD.vfp <number> - produces a VFP-format double precision float

Arguments for whether the plain "DCFD" form should be changed to produce VFP-format double precision floats go both ways.

Arguments for leaving DCFD the same:

* It would result in consistent behaviour across all BASIC versions
* Since any float-using assembler code is likely to require a rewrite to work with BASICVFP (e.g. to switch from using FPA instructions to VFP), it's not a big deal to require the author to also update any DCFD directives

Arguments for changing:

* Apart from INPUT# and PRINT#, all other code which interacts with double-precision floats expects them to be in VFP format. Therefore it makes sense for DCFD to match the native float type.
* Making features related to obsolete instruction sets easier to use than features relating to current instruction sets is counter-productive. Therefore "DCFD" (which is shorter and therefore easier to type than "DCFD.vfp") should default to VFP format in BASICVFP.
* Programmers might forget to add the suffix (DCFS, DCFE and DCFH require no suffixes)

A potential solution might be to introduce a new OPT bit or some other configuration variable which controls the default behaviour. However this may run into issues if the program is run on versions of BASIC which do not recognise the option.
