15. The Floating-Point Coprocessor
Part of
the Hawk Manual
|
15.1. Floating-Point Registers
15.1.1. Coprocessor Status Register — 0
15.1.2. Floating-Point-Low Register — 1
15.1.3. Floating-Point Accumulators — 2 and 3
15.2. Floating-Point Operations
15.2.1. Integer to Floating Conversion — 4 and 5
15.2.2. Floating Square Root — 6 and 7
15.2.3. Floating Add — 8 and 9
15.2.4. Floating Subtract — 10 and 11
15.2.5. Floating Multiply — 12 and 13
15.2.6. Floating Divide — 14 and 15
15.2.7. Floating Negate — 10 and 11
15.2.8. Floating Absolute Value — 12 and 13
15.3. Examples
The floating-point coprocessor has 3 interface registers in addition to the COSTAT register that it shares with other coprocessors. These are referenced using the COGET and COSET instructions. These three 32-bit registers allow direct access to two 64-bit floating-point accumulators. The following constants should be defined by the assembly header file float.h to support access to these registers:
COSTAT = 0 ; this definition should be redundant FPLOW = 1 ; the low half of long floating operands FPA0 = 2 ; floating-point accumulator 0 FPA1 = 3 ; floating-point accumulator 1
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
unused | sl | u | 0 0 1 | unused | en | u |
The floating-point coprocessor is coprocessor number 1. Thus it is enabled when COSTAT:1 (en) is set, and it is selected when COSTAT:10:8 (select) is set to 001.
COSTAT:12 (sl), part of the coprocessor operation field (coop), controls whether the coprocessor uses short (32-bit) or long (64-bit) floating point representations. The other bits in coop are unused, although they may be used in enhanced floating-point coprocessors.
The following constants should be defined by the assembly header file float.h to support use of the floating-point coprocessor; these can be added together (or ored together) to create a value to be placed in the status register:
FPENAB = #0002 ; enable the floating-point unit FPSEL = #0100 ; select the floating-point unit FPLONG = #1000 ; operate in long format FPSHORT = #0000 ; operate in short format (default) FPENBIT = 1 ; bit number of FPENAB
Strictly speaking, there is no reason to explicitly specify short format, since it is the default, but an explicit statement of short mode makes programs easier to read.
To enable and select the floating-point coprocessor for long floating operations while turning off any other coprocessors in the system, use the following instruction sequence:
LIL R1,FPENAB+FPSEL+FPLONG COSET R1,COSTAT
Most applications will not use multiple coprocessors concurrently. For those that keep several coprocessors enabled, to select the already enabled floating-point unit and and set it in long mode, use the following instruction sequence:
COGET R1,COSTAT TRUNC R1,8 ADDI R1,R1,FPSEL+FPLONG COSET R1,COSTAT
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
mantissa:31:0 |
The floating-point low (FPLOW) register holds the least significant 32-bits of a long floating-point operand. These are the low 32 bits of the mantissa. The contents of FPLOW are not defined when operating in 32-bit mode.
In 64-bit mode, FPLOW must be set before using COSET to set or operate on the high half of a floating-point accumulator, and it is automatically set as a side effect of using COGET to read the low half of a floating-point accumulator. The COGET instruction applied to FPLOW has an undefined effect on the condition codes.
The floating-point coprocessor contains two 64-bit floating-point accumulators, FPA0 and FPA1. The apparent format of these registers appears different in short floating-point mode, where they appear to be only 32 bits. In fact, the hardware always retains an 11 bit exponent and a 52-bit mantissa, at minimum, with the extra bits suppressed in short mode. IEEE standard floating-point format is supported. This means that the 53-bit mantissa is usually 54 bits, with a hidden one-bit (bit 52) except in the case of un-normalized numbers, where this hidden bit is zero, signified by a zero exponent.
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 |
s | exp:7:0 | mantissa:51:29 |
In short mode, the floating-point operands are 32 bits, and the floating low register is not used. IEEE floating-point format is used. In short format, the 11-bit exponent is shortened to just 8 bits, and only the most significant 24 bits of the mantissa (including the hidden bit defined by the IEEE format) are used.
31 | 30 | 29 | 28 | 27 | 26 | 25 | 24 | 23 | 22 | 21 | 20 | 19 | 18 | 17 | 16 | 15 | 14 | 13 | 12 | 11 | 10 | 09 | 08 | 07 | 06 | 05 | 04 | 03 | 02 | 01 | 00 | ||||||
s | exp:10:0 | mantissa:51:32 |
In long mode, the floating-point operand registers are 64 bits, and the floating low bits are used to access the least significant 32 bits of the operand. IEEE floating-point format is used. In long format, the full 11-bit exponent is used, and the most significant 21 bits of the mantissa (including the hidden bit defined by the IEEE format) are given here, while the least significant 32 bits are found in FPLOW. To load FPA1 from with a 64-bit floating-point number stored in R3 and R4, least significant half first, use:
COSET R3,FPLOW COSET R4,FPA1 ; uses FPLOW
The corresponding code to get a 64-bit number from FPA1 into R3 and R4 is:
COGET R4,FPA1 ; sets FPLOW COGET R3,FPLOW
Conversion between short and long format may be done by changing the value of the coprocessor operation field in COSTAT between the time a value is put into a floating-point operand register and the time it is retrieved.
COGET | N = r[dst]:31 | — result is negative | ||
Z = (r[dst] = 0) | — result is zero | |||
V = 0 | ||||
C = (exp = 111111111112) — not a number |
In both long and short format, COGET sets the condition codes after reading FPA0 or FPA1 to report on the entire floating-point number, including that part (if any) loaded into FPLOW. N and Z indicate a negative or zero value, while C is used to report values that are NANs (not a number) or infinity. The overflow conditiion code, V, is always reset. As a result, the signed comparison instructions such as BGT and BLE will correctly report the relationship of the operand to zero.
Floating-point operations are initiated by accessing coprocessor register numbers 4 through 15. That is, the coprocessor register number is used partially to select one of the floating-point accumulators and partly as a coprocessor operation code. Since 4 register numbers have already been used, as defined above, This allows for a total of 12 operations on COSET and 12 operations on COGET. In each group of 12, 6 apply to each each floating-point accumulator.
The following operations are initiated by COSET instructions. Because the accumulators themselves have register numbers 2 and 3, the definitions provided in float.h are off by two from the actual values -- this allows these values to be added to the accumulator numbers to form the operation:
FPINT = 2 ; register numbers 4, 5 FPSQRT = 4 ; register numbers 6, 7 FPADD = 6 ; register numbers 8, 9 FPSUB = 8 ; register numbers 10, 11 FPMUL = 10 ; register numbers 12, 13 FPDIV = 12 ; register numbers 14, 15
The following operations are done by COGET instructions. None of these change the contents of the floating-point accumulators. Again, because the accumulators themselves have register numbers 2 and 3, the definitions provided in float.h are off by two from the actual values -- this allows these values to be added to the accumulator numbers to form the operation:
FPNEG = 2 ; register numbers 4, 5 FPABS = 4 ; register numbers 6, 7
COSET to floating-point unit register numbers 4 or 5 converts the integer operand to a normalized floating-point value in either FPA0 or FPA1. For example, if registers R3 and R4 contain a 64-bit signed integer, least significant word first, and if the floating-point unit is in long mode, the integer may be converted to floating-point in FPA1 as follows:
COSET R3,FPLOW COSET R4,FPINT+FPA1
If register R3 contains a 32-bit unsigned integer, and if the floating-point unit is in long mode, the integer may be converted to floating-point in FPA1 as follows:
COSET R3,FPLOW COSET R0,FPINT+FPA1
If register R3 contains a 32-bit signed integer, and if the floating-point unit is in short mode, the integer may be converted to floating-point in FPA1 as follows:
COSET R3,FPINT+FPA1
Floating to integer conversion, truncated or rounded, must be done by
software.
COSET to floating-point unit register numbers 6 or 7 puts the square root of the floating-point operand into either FPA0 or FPA1. For example, if R3 contains a short floating-point number, and if the floating-point unit is in short mode, the square root may be taken in FPA1 as follows:
COSET R3,FPSQRT+FPA1
COSET to floating-point unit register numbers 8 or 9 adds the floating-point operand to either FPA0 or FPA1. For example, if R3 and R4 contain a long floating-point number (least significant word first), and if the floating-point unit is in long mode, the number may be added to FPA1 as follows:
COSET R3,FPLOW COSET R4,FPADD+FPA1
COSET to floating-point unit register numbers 10 or 11 subtracts the floating-point operand from either FPA0 or FPA1. For example, if R3 contains a short floating-point number, and if the floating-point unit is in short mode, the number may be subtracted from FPA1 as follows:
COSET R3,FPSUB+FPA1
Note that, unlike integer arithmetic, the subtract operation does not set the condition codes. To compare the two short floating-point numbers in R3 and R4, use this sequence:
COSET R3,FPA1 COSET R4,FPSUB+FPA1 COGET R0,FPA1 ; sets the condition codes
COSET to floating-point unit register numbers 12 or 13 multiplies either FPA0 or FPA1 by the floating-point operand. For example, if R3 and R4 contain a long floating-point number (least significant word first), and if the floating-point unit is in long mode, FPA1 may be multiplied by the number as follows:
COSET R3,FPLOW COSET R4,FPMUL+FPA1
COSET to floating-point unit register numbers 14 or 15 divides either FPA0 or FPA1 by the floating-point operand. For example, if R3 contains a short floating-point number, and if the floating-point unit is in short mode, from FPA1 may be divided by R3 as follows:
COSET R3,FPDIV+FPA1
COGET from floating-point unit register numbers 4 or 5 retrieves the negated floating-point value of FPA0 or FPA1, setting the condition codes to report on the result. For example, in long mode, the value in FPA0 may negated and loaded into the double register R3-R4 as follows:
COGET R4,FPA1+FPNEG ; high half COGET R3,FPLOW ; low half
The value in FPA0 can be negated and moved it to FPA1 via R1, in either short or long mode, as follows:
COGET R1,FPA0+FPNEG COSET R1,FPA1
To negate the short floating-point value in R3 without using the floating-point unit, the following instruction sequence will work:
CMP R1,R1 ; this always sets C ADJUST R3,CMSB ; toggle the sign bit
COGET from floating-point unit register numbers 6 or 7 retrieves the absolute floating-point value of FPA0 or FPA1, setting the condition codes to report on the result. For example, in long mode, to divide FPA0 by the absolute value of FPA1 this instruction sequence will work:
COGET R3,FPA1+FPABS COSET R3,FPDIV+FPA0
To take the absolute value of a long floating-point value in R3-R4 without using the floating-point unit, the following instruction sequence will work:
SL R4,1 SRU R4,1 ; toggle the sign bit
Consider computing y = ax2 + bx + c, where y, a, b, c and x are short floating-point numbers in R3 to R7, in that order. Also, assume that coprocessors are turned off when not in use. The following code works:
LIL R3,FPENAB+FPSEL+FPSHORT COSET R3,COSTAT ; turn on floating coprocessor COSET R4,FPA1 ; FPA1 = a COSET R7,FPMUL+FPA1 ; FPA1 = ax COSET R5,FPADD+FPA1 ; FPA1 = ax + b COSET R7,FPMUL+FPA1 ; FPA1 = (ax + b)x COSET R6,FPADD+FPA1 ; FPA1 = (ax + b)x + c COGET R3,FPA1 COSET R0,COSTAT ; turn off floating coprocessor
Saving and restoring the state of the coprocessor in an interrupt service routine requires care. If the coprocessor is not enabled, only the status needs saving. If enabled, it must be selected before accessing the registers to save them. Saving the registers in long mode is always safe. Note that FPLOW must always be saved because it need not represent the low half of either accumulator at the time of interrupt.
Subroutines that use the floating-point coprocessor can be written so that they restore the coprocessor status word to its former state on return, and so that they do not turn off any other coprocessors that may be in use while they turn on and select the floating-point coprocessor. The following code illustrates this, using a variable indexed off of R2 to hold the saved floating-point status, and using R3 and R4 as temporaries:
COGET R3,COSTAT STORE R3,R2,SVCOSTAT ; save old COSTAT TRUNC R3,8 ; clear coop and select fields LIL R4,FPENAB + FPSEL ... OR R3,R4 ; set enable, select and mode bits COSET R3,COSTAT ; update COSTAT ... code using floating point ... LOAD R3,R2,SVCOSTAT COSET R3,COSTAT ; restore COSTAT
Many interrupt and trap service routines make no use of the floating-point coprocessor and return to the same code that was interrupted or that caused the trap. For such trap service routines, there is no need to take any action with regard to the floating-point coprocessor. When, however, a trap or interrupt service routine needs to do floating-point computation or performs a context switch, the entire state of the floating-point coprocessor must be saved. Here is code to save the entire coprocessor state into registers R8 through R13:
COGET R8,COSTAT ; COSTAT into R8 BITTST R8,FPENBIT BBR NOFPSV ; if floating-point coprocessor enabled EXTB R9,R8,R0 ; move just the enable bits ADDI R9,R9,FPSEL+FPLONG COSET R9,COSTAT ; select floting-point long mode COGET R9,FPLOW ; FPLOW into R9 COGET R11,FPA0 COGET R10,FPLOW ; FPA0 into R10 (low) and R11 (high) COGET R13,FPA1 COGET R12,FPLOW ; FPA1 into R12 (low) and R13 (high) NOFPSV: ; endif
The registers cannot be restored in the order they were saved because of COSET side effects and because the value in COSTAT during saving is not the same as the saved value of COSTAT.
In addition to defining values for fields of COSTAT, float.h should define the following:
F 3.1415 ; like W, but with a floating value F 3E2 ; equivalent to 300.0 F +1.0e-2 ; equivalent to 0.01 LIF -123.45E+7 ; like LIW, but with a floating value
Decimal floating-point values have an optional sign on both mantissa and optional exponent, which comes after the letter E or e. The fractional part of the mantissa is also optional.