The Art of ASSEMBLY LANGUAGE PROGRAMMING Chapter Fourteen (Part 1) Table of Content Chapter Fourteen (Part 3)
CHAPTER FOURTEEN:
FLOATING POINT ARITHMETIC (Part 2)
14.3 - The UCR Standard Library Floating Point Routines
14.3.1 - Load and Store Routines
14.3.2 - Integer/Floating Point Conversion
14.3.3 - Floating Point Arithmetic
14.3.4 - Float/Text Conversion and Printff

## 14.3 The UCR Standard Library Floating Point Routines

In most assembly language texts which bother to cover floating point arithmetic this section would normally describe how to design your own floating point routines for addition subtraction multiplication and division. This text will not do that for several reasons. First to design a good floating point library requires a solid background in numerical analysis; a prerequisite this text does not assume of its readers. Second the UCR Standard Library already provides a reasonable set of floating point routines in source code form; why waste space in this text when the sources are readily available elsewhere? Third floating point units are quickly becoming standard equipment on all modern CPUs or motherboards; it makes no more sense to describe how to manually perform a floating point computation than it does to describe how to manually perform an integer computation. Therefore this section will describe how to use the UCR Standard Library routines if you do not have an FPU available; a later section will describe the use of the floating point unit.

The UCR Standard Library provides a large number of routines to support floating point computation and I/O. This library uses the same memory format for 32 64 and 80 bit floating point numbers as the 80x87 FPUs. The UCR Standard Library's floating point routines do not exactly follow the IEEE requirements with respect to error conditions and other degenerate cases and it may produce slightly different results than an 80x87 FPU but the results will be very close[5]. Since the UCR Standard Library uses the same memory format for 32 64 and 80 bit numbers as the 80x87 FPUs you can freely mix computations involving floating point between the FPU and the Standard Library routines.

The UCR Standard Library provides numerous routines to manipulate floating point numbes. The following sections describe each of these routines by category.

Since 80x86 CPUs without an FPU do not provide any 80-bit registers the UCR Standard Library must use memory-based variables to hold floating point values during computation. The UCR Standard Library routines use two pseudo registers an accumlator register and an operand register when performing floating point operations. For example the floating point addition routine adds the value in the floating point operand register to the floating point accumulator register leaving the result in the accumulator. The load and store routines allow you to load floating point values into the floating point accumulator and operand registers as well as store the value of the floating point accumulator back to memory. The routines in this category include ```accop xaccop lsfpa ssfpa ldfpa sdfpa lefpa sefpa lefpal lsfpo ldfpo lefpo ``` and lefpol.

The `accop` routine copies the value in the floating point accumulator to the floating point operand register. This routine is useful when you want to use the result of one computation as the second operand of a second computation.

The `xaccop `routine exchanges the values in the floating point accumuator and operand registers. Note that many floating point computations destory the value in the floating point operand register so you cannot blindly assume that the routines preserve the operand register. Therefore calling this routine only makes sense after performing some computation which you know does not affect the floating point operand register.

```Lsfpa ldfpa and lefpa``` load the floating point accumulator with a single double or extended precision floating point value respectively. The UCR Standard Library uses its own internal format for computations. These routines convert the specified values to the internal format during the load. On entry to each of these routines `es:di` must contain the address of the variable you want to load into the floating point accumulator. The following code demonstrates how to call these routines:

```rVar            real4   1.0
drVar           real8   2.0
xrVar           real10  3.0
.
.
.
lesi    rVar
lsfpa
.
.
.
lesi    drVar
ldfpa
.
.
.
lesi    xrVar
lefpa```

The ```lsfpo ldfpo ``` and `lefpo` routines are similar to the `lsfpa` `ldfpa` and `lefpa` routines except of course they load the floating point operand register rather than the floating point accumulator with the value at address `es:di`.

`Lefpal` and `lefpol` load the floating point accumulator or operand register with a literal 80 bit floating point constant appearing in the code stream. To use these two routines simply follow the call with a `real10` directive and the appropriate constant e.g.

```                lefpal
real10  1.0
lefpol
real10  2.0e5```

The `ssfpa` `sdfpa` and `sefpa` routines store the value in the floating point accumulator into the memory based floating point variable whose address appears in `es:di`. There are no corresponding ssfpo sdfpo or sefpo routines because a result you would want to store should never appear in the floating point operand register. If you happen to get a value in the floating point operand that you want to store into memory simply use the xaccop routine to swap the accumulator and operand registers then use the store accumulator routines to save the result. The following code demonstrates the use of these routines:

```rVar            real4   1.0
drVar           real8   2.0
xrVar           real10  3.0
.
.
.
lesi    rVar
ssfpa
.
.
.
lesi    drVar
sdfpa
.
.
.
lesi    xrVar
sefpa```
`14.3.2 Integer/Floating Point Conversion`

The UCR Standard Library includes several routines to convert between binary integers and floating point values. These routines are ```itof utof ltof ultof ftoi ftou ftol ```and `ftoul.` The first four routines convert signed and unsigned integers to floating point format the last four routines truncate floating point values and convert them to an integer value.

`Itof` converts the signed 16-bit value in `ax` to a floating point value and leaves the result in the floating point accumulator. This routine does not affect the floating point operand register. `Utof` converts the unsigned integer in `ax` in a similar fashion. `Ltof` and `ultof` convert the 32 bit signed (`ltof`) or unsigned (`ultof`) integer in `dx:ax` to a floating point value leaving the value in the floating point accumulator. These routines always succeed.

`Ftoi` converts the value in the floating point accumulator to a signed integer value leaving the result in `ax`. Conversion is by truncation; this routine keeps the integer portion and throws away the fractional part. If an overflow occurs because the resulting integer portion does not fit into 16 bits `ftoi` returns the carry flag set. If the conversion occurs without error `ftoi` return the carry flag clear. `Ftou` works in a similar fashion except it converts the floating point value to an unsigned integer in `ax`; it returns the carry set if the floating point value was negative.

`Ftol` and `ftoul` converts the value in the floating point accumulator to a 32 bit integer leaving the result in `dx:ax`. `Ftol` works on signed values `ftoul` works with unsigned values. As with `ftoi` and `ftou` these routines return the carry flag set if a conversion error occurs.

14.3.3 Floating Point Arithmetic

Floating point arithmetic is handled by the `fpadd` `fp sub` `fpcmp` `fpmul` and `fpdiv` routines. `Fpadd` adds the value in the floating point accumulator to the floating point accumulator. `Fpsub` subtracts the value in the floating point operand from the floating point accumulator. `Fpmul` multiplies the value in the floating accumulator by the floating point operand. `Fpdiv` divides the value in the floating point accumulator by the value in the floating point operand register. `Fpcmp` compares the value in the floating point accumulator against the floating point operand.

The UCR Standard Library arithmetic routines do very little error checking. For example if arithmetic overflow occurs during addition subtraction multiplication or division the Standard Library simply sets the result to the largest legal value and returns. This is one of the major deviations from the IEEE floating point standard. Likewise when underflow occurs the routines simply set the result to zero and return. If you divide any value by zero the Standard Library routines simply set the result to the largest possible value and return. You may need to modify the standard library routines if you need to check for overflow underflow or division by zero in your programs.

The floating point comparison routine (`fpcmp`) compares the floating point accumulator against the floating point operand and returns -1 0 or 1 in the `ax` register if the accumulator is less than equal or greater than the floating point operand. It also compares ax with zero immediately before returning so it sets the flags so you can use the ```jg jge jl jle je ``` and `jne` instructions immediately after calling `fpcmp`. Unlike `fpadd` `fpsub` `fpmul` and `fpdiv` `fpcmp` does not destroy the value in the floating point accumulator or the floating point operand register. Keep in mind the problems associated with comparing floating point numbers!

14.3.4 Float/Text Conversion and Printff

The UCR Standard Library provides three routines `ftoa` `etoa` and `atof` that let you convert floating point numbers to ASCII strings and vice versa; it also provides a special version of `printf` `printff` that includes the ability to print floating point values as well as other data types.

`Ftoa` converts a floating point number to an ASCII string which is a decimal representation of that floating point number. On entry the floating point accumulator contains the number you want to convert to a string. The `es:di` register pair points at a buffer in memory where `ftoa` will store the string. The `al` register contains the field width (number of print positions). The `ah` register contains the number of positions to display to the right of the decimal point. If `ftoa` cannot display the number using the print format specified by `al` and `ah` it will create a string of "#" characters ah characters long. `Es:di` must point at a byte array containing at least `al+1` characters and al should contain at least five. The field width and decimal length values in the al and ah registers are similar to the values appearing after floating point numbers in the Pascal write statement e.g.

`		write(floatVal:al:ah);`
``` ```

`Etoa` outputs the floating point number in exponential form. As with `ftoa` `es:di` points at the buffer where `etoa` will store the result. The `al` register must contain at least eight and is the field width for the number. If `al` contains less than eight `etoa` will output a string of "#" characters. The string that `es:di `points at must contain at least `al+1` characters. This conversion routine is similar to Pascal's write procedure when writing real values with a single field width specification:

write(realvar:al);

The Standard Library `printff` routine provides all the facilities of the standard printf routine plus the ability to handle floating point output. The printff routine includes several new format specifications to print floating point numbers in decimal form or using scientific notation. The specifications are

• %x.yF Prints a 32 bit floating point number in decimal form.
• %x.yGF Prints a 64 bit floating point number in decimal form.
• %x.yLF Prints an 80 bit floating point number in decimal form.
• %zE Prints a 32 bit floating point number using scientific notation.
• %zGE Prints a 64 bit floating point number using scientific notation.
• %zLE Prints an 80 bit floating point value using scientific notation.

In the format strings above x and z are integer constants that denote the field width of the number to print. The y item is also an integer constant that specifies the number of positions to print after the decimal point. The x.y values are comparable to the values passed to `ftoa` in `al` and `ah`. The z value is comparable to the value `etoa` expects in the `al` register.

Other than the addition of these six new formats the `printff` routine is identical to the `printf` routine. If you use the `printff` routine in your assembly language programs you should not use the `printf` routine as well. `Printff` duplicates all the facilities of `printf` and using both would only waste memory.

[5] Note by the way that different floating point chips especially across different CPU lines but even within the Intel family produce slightly different results. So the fact that the UCR Standard Library does not produce the exact same results as a particular FPU is not that important.

 Chapter Fourteen (Part 1) Table of Content Chapter Fourteen (Part 3)

Chapter Fourteen: Floating Point Arithmetics (Part 2)
28 SEP 1996