` COMPUTE! ISSUE 17 / OCTOBER 1981 / PAGE 160`

THE SBC GAZETTE AIM 65 BASIC Floating-Point Arithmetic From Machine Language

Paul Beasley,
Mobile, AL

Writing floating-point operations in machine language on a microprocessor is a "messy" proposition. I avoid it like the plague unless I absolutely must do it. But I have discovered how to use the floating-point routines in the AIM 65 BASIC ROM's. It's so easy even I do not mind floating-point applications any more.

AIM 65 BASIC Floating-Point Numbers

For those who are unfamiliar with floating-point numbers, particularly on the AIM 65, I'll describe the floating-point number format. Floating-point representations are similar to scientific notation. An example of a number written in normalized, scientific notation is .27 × 102 ( = 27). Computers commonly use a similar scheme except instead of 10 as a base, the base 2 is used (e.g., 27 = .84375 × 25). By storing the sign, the exponent of 2, and the mantissa of the number, a broad range of values can be efficiently represented. In the AIM 65, this is accomplished by storing each floating-point number in five consecutive bytes as follows: Note: Bits in a byte are numbered 0 (LSB) to 7 (MSB).

The exponent, E, is a power of 2 and is biased so that E = \$80 actually corresponds to a power of 0, E = \$7F corresponds to -1, E = \$81 corresponds to + 1, etc. When a floating-point number is normalized, the mantissa is shifted so that the first 1 bit of the mantissa falls in bit position 7 of M3. This means that bit 7 of M3 will always be 1 and the exponent reflects the number of bits that the mantissa was shifted in order to have the implied decimal in front of the first 1 bit. E = \$80 means no shifts were required; E = \$81 means the mantissa was shifted right one bit; E = \$7F means the mantissa was shifted left one bit; etc.

Since bit 7 of M3 is always 1 using the above method, it is stripped off and restored only when performing arithmetic operations (this process is explained later). So, when a number is stored in memory, this bit position is used to store the sign of the number — 0 for positive and 1 for negative. (Incidentally, the floating-point representation of 0 is all five bytes equal \$00.) My previous example of the number 27 would be stored in memory as follows:

```85   58  00  00  00
```

AIM 65 BASIC Floating-Point Accumulators

In order to use floating-point numbers in arithmetic operations, BASIC reserves twelve bytes in Page 0 to provide two floating-point accumulators. Accumulator 1 (FPAC1) is in locations \$A9 through \$AE and accumulator 2 (FPAC2) is in locations \$B 1 through \$B6. Each accumulator spans six bytes and has the following format: As I mentioned earlier, when numbers are stored into memory, the sign is put into bit 7 of M3. Technically, this is accomplished as follows: Table 2. Intrinsic Function Subroutine Addresses Basic Function Address Description ABS \$C997 Absolute Value of FPAC1 COS \$CDD2 Cosine of FPAC1 EXP \$CCF1 Raises e to power in FPAC1 INT \$CA0B Integer portion of FPAC1 LOG \$C729 Natural logarithm of FPAC1 NEG \$CCB8 Negation of FPAC1 RND \$CD96 Generates random number SGN \$C978 Sign function of FPAC1 SIN \$CDD9 Sine of FPAC1 SQR \$CC75 Square root of FPAC1 TAN \$CE22 Tangent of FPAC1

The logical OR places the sign bit into M3.

When a number is loaded into one of the accumulators, the sign bit is separated out and made the sixth byte of the accumulator (as shown above) so that bit 7 of M3 can be restored to 1. This makes arithmetic operations easier and explains why the accumulators are six bytes each. My example of the number 27 would appear in an accumulator as:

```85  D8 00 00 00 00
```

In addition to the accumulators, there are two other bytes in Page 0 that you should know about. These are the overflow (at \$B0) and underflow (at \$B8) bytes. The underflow byte is used for rounding M0 of FPAC 1. The overflow byte becomes non-zero when a computational result becomes too large. It is important that these two bytes be initialized to zero before the first floating-point operation is performed. In relation to this, I must give a word of caution. The BASIC floating-point routines still "think" they are operating in the context of a BASIC program. This means that any computation error (e.g., overflow) which is normally trapped by BASIC will still be caught and your program terminated. The termination message may look peculiar since the BASIC statement and variable pointers in Page 0 probably contain meaningless values.

Performing The Floating-Point Operations

I have prepared Table 1 as a reference for the fundamental floating-point operations along with their appropriate machine language calling sequences. All operations are executed with the subroutine jump instruction (JSR) plus minimal parameter set-up. In preparing the table I used the following notation:

AL–Address Low; the least significant 8 bits of the source or destination memory address.

AH–Address High; the most significant 8 bits of the source or destination memory address.

IL–Integer Low; the memory address of the least significant 8 bits of a 2-byte integer value.

IH–Integer High; the memory address of the most significant 8 bits of a 2-byte integer value.

FPAC1–Floating Point Accumulator 1.

FPAC2–Floating Point Accumulator 2.

In addition to the fundamental operations in Table 1, the BASIC intrinsic functions may also be used. The common calling sequence for these functions is as follows:

```load FPAC1 with the argument value
JSR  \$xxxx (select function address from Table 2)
(result in FPAC1)
```

The entry point address for each of the functions is given in Table 2.

Sample Program

In order to illustrate what I have just described, I have included the following sample program. It is a very simple calculation of the volume of a cylinder using the formula V = π r2h, where r = radius and h = height. I know that r2 can be computed as r times r very efficiently, but I used the power function to illustrate its use. When the program finishes (successfully), it will display V = 88357.2935. Another tidbit I'll point out is that the floating-point representation for 2 π is at location \$CE53 of the BASIC ROM's.

Sample Program: Calculate Volume of Cylinder (V = π r2h)

```* = \$0220
COMIN = \$R1A1                        ; monitor entry for command input
EQUAL = \$E7D8                        ; output "=" to display/printer
OUTPUT = \$E97A                       ; output char.in A to diaplay/printer
CRLOW = \$EA13                        ; output CR & LF to display/printer
FMUL = \$C76F                         ; floating-point multiply
CONVIF = \$C0D1                       ; convert fixed-point to floating-point
CONVFA = \$CB1C                       ; convert floating-point to ASCII string
FST1 = \$C913                         ; store FPAC1
FLD2 = \$C7CB                         ; load FPAC2
CPY12 = \$C94B                        ; copy FPAC1 to FPAC2
FDIV = \$C851                         ; division
FPWR = \$CC7F                         ; power operation
PI2 = \$CE53                          ; 2*
START        LDY      R              ; get radius
LDA     #0
STA     \$B8             ; initialize underflow
STA     \$B0             ; and overflow bytes
JSR     CONVIF
LDX     #<TEMP
LDY     #>TEMP
JSR     FST1            ; store R in TEMP
LDY     #2
LDA     #0
JSR     CONVIF          ; exponent 2 in FPAC 1
LDA     #<TEMP
LDY     #>TEMP
JSR     FLD2            ; load R in FPAC2
JSR     FPWR            ; raise R to power 2
LDX     #<TEMP
LDY     #>TEMP
JSR     FST1            ; store R squared in TEMP
LDY H
LDA #0
JSR CONVIF              ; height H in FPAC 1
LDA #<TEMP
LDY #>TEMP
JSR FLD2                ; load FPAC2 with R squared
JSR FMUL                ; FPAC1 = H times R squared
LDA #<PI2
LDY #>PI2
JSR FLD2                ; load 2* innto FPAC2
JSR FMUL                ; FPAC1 = H times R squared times 2
JSR CPY12               ; save FPAC1 in FPAC2
LDY #2
LDA #0
JSR CONVIF              ; FPAC1=2
JSR FDIV                ; divide by 2
JSR CONVFA              ; resulting volume in FPAC1
JSR CRLOW
LDA #'V'
JSR OUTPUT              ; display 'V'
JSR EQUAL               ; display '='
LDX #0
LABEL 1      LDA \$0200,X             ; fetch & display ASCII digits
BEQ LABEL2
JSR OUTPUT
INX
JMP LABEL1
LABEL 2      JSR CRLOW
JMP COMIN
R            .BYTE 25                ; radius = 25
H            .BYTE 45                ; height = 45
TEMP         .BYTE 0,0,0,0,0
.END
```