Issue 89 Issue 90 Contents Issue 91

HOW the HELL ... Do we use Floating Point numbers in Machine Code?

ASCII code nightmares? Disillusioned with disassemblers? Baffled by bytes? If you’re having problems with programming, whether they alliterate or not, Andrew Hewson is your man. Drop him a line and he’ll be investigating the problem before you can say “Ram Dos Buffer Interface Edge Connector”

A couple of letters landed on my desk last month with questions such as, ‘How do you use the floating point calculator?’ from Frode Tennebo in Norway and ‘How do you draw lines and plot in machine code?’ from Gordon Bissell of Stafford. This month I hope to answer both questions with a few practical examples including circle and plot routines in machine code.

Machine code is easy once you have learnt the basics and start to use it more frequently, like learning a foreign language in fact. The numbers we deal with are all INTEGERS and there are no complicated decimal numbers or fractions to worry about. This is great for most applications but at some point in time you will want to do something where the odd cosine or square root is required and with integers, we can’t simply round up the result and expect the calculation to work out. In a game, for example, the main character might have a single byte for each of the X and Y co-ordinates of its screen position. Each byte gives us a resolution of 1 pixel but as this is the smallest resolution of movement then it is perfectly adequate. We treat each byte as an integer and there are no problems. In fact, if we had a co-ordinate system with a resolution down to 0.001 or one-thousandth of a pixel it would just mean designing an overly-complicated routine to handle those smaller floating point numbers — hence the reason for shying away from FP.

This is the standard way of accessing the calculator:

RST 28H
DEFB A2H        ; Stack a half (0.5)
DEFB A4H        ; Stack ten (10) 
DEFB 04H        ; Command code for multiply
DEFB 38H        ; Exit calculator command
JUMP 2DA2H      ; Exit via floating point to 'BC' 

The RST instruction enters the calculator. The two bytes A2H and A4H are special commands that deposit the values 0.5 and 10 on the stack. There are 5 of these ‘special’ values:

After putting 0.5 and 10 on the stack, we tell the calculator to multiply the two items on the top of the stack with the O4H command code. Finally we exit the calculator with the code 38H. However, to be of any use to us we have to get the result into a register for further use. There is a very useful routine which performs the function ‘Floating point to BC’ at address 2DA2 hex. It rounds the floating point number and puts it into BC and the accumulator is a copy of C.

PUTTING NUMBERS INTO THE CALCULATOR

There are a couple of ways to do this. If we are using any of the 5 special values above, we just use the commands A0H to A4H. Typically we would want to be able to put in values from a register pair, say. The routine to do this is at 2D2B hex. It will place at the top of the calculator stack the value held in the BC register pair. This works like so:

; We will demonstrate 21x43  
LD   BC,21        
CALL 2D2BH      ; First we stack 21
LD   BC,43
CALL 2D2BH      ; Now we stack 43
RST  28H        ; Enter the calculator
DEFB 04H        ; Multiply 21x43
DEFB 38H        ; Exit the calculator
JUMP 2DA2H      ; Result in BC

The result here in BC should be 21 x 43 = 903.

THE THREE MODES OF THE CALCULATOR

In a calculation, it may be what is called ‘Unary’ meaning functions such as — 1/x, SQR x, SIN x, etc. The actual calculation is just acting on the ‘last value’ on the stack. These are Unary calculations.

When we perform the calculation 3 x 10, this is known as a binary operation i.e. there are two values on the stack. This use of the word binary is not to be confused with the normal Base 2 ‘1’s and ‘0’s type of binary we all know.

Finally, the calculator has 6 memories which can be used for temporary storage (as well as the calculator stack). Operations using the memories are termed ‘manipulatory’ as they do not actually calculate anything.

Although the calculator stack is not to be confused with the Z80 machine code stack, it still has to be treated like one — we use the DELETE command in the calculator which has the code 02H to tidy up the stack — a bit like the POP instruction in Z80 code.

PRACTICAL USE OF THE CALCULATOR

Circles can be drawn with clever algorithms that don’t need floating point arithmetic but here I will demonstrate a relatively standard trigonometric method of drawing circles. The routine is based around an interactive solution where we only have to use the slow COSINE and SINE calculations once for the whole circle. For avid mathematicians out there, the formulae can be found on page 73 of the book Computer Graphics by John Lansdown and published by Hodder and Stoughton.

The Spectrum machine code looks like this:

                        ORG  35000
88B8 EF                 RST  28H                  ; Enter calculator
88B9 A3                 DEFB $A3                  ; Stack PI/2
88BA 34                 DEFB 34H                  ; Stack data (90 decimal)
88BB 40B0005A           DEFB 40H,$B0,00H,5AH
88BF 05                 DEFB 05H                  ; Divide (PI/2)/90 = PI/180
88C0 34                 DEFB 34H                  ; Stack 360 decimal
88C1 80B0000168         DEFB 80H,$B0,00H,01H,68H
88C6 34                 DEFB 34H                  ; Stack the number of steps
88C7 40B00064           DEFB 40H,$B0,00H,64H      ; In this case 100 decimal
88CB 05                 DEFB 05H                  ; Divide 360/steps
88CC 04                 DEFB 04H                  ; Multiply PI/180
88CD 31                 DEFB 31H                  ; Duplicate it
88CE 1F                 DEFB 1FH                  ; Get the sine of it
88CF 01                 DEFB 01H                  ; Exchange the top stack items
88D0 20                 DEFB 20H                  ; Get the cosine of the angle
88D1 C0                 DEFB $C0                  ; Store it in memo
88D2 02                 DEFB 02H                  ; Delete from the top of stack
88D3 C1                 DEFB $C1                  ; Store the sine in Mem1
88D4 02                 DEFB 02H                  ; Delete to clear the stack
88D5 34                 DEFB 34H                  ; Stack 77 decimal
88D6 40B0004D           DEFB 40H,$B0,00H,4DH
88DA C2                 DEFB $C2                  ; Store it in Mem2
88DB 02                 DEFB 02H                  ; Delete it
88DC 34                 DEFB 34H                  ; Stack 87 decimal
88DD 40B00057           DEFB 40H,$B0,00H,57H
88E1 C3                 DEFB $C3                  ; Store it in Mem3
88E2 02                 DEFB 02H                  ; Clear the stack by deleting
88E3 38                 DEFB 38H                  ; Now exit the calculator
88E4 0664               LD   B,100                ; Use B as a counter

88E6 C5         CLOOP:  PUSH BC                   ; Save it for later
88E7 EF                 RST  28H                  ; Enter calculator again
88E8 E2                 DEFB $E2                  ; Get contents of Mem2
88E9 34                 DEFB 34H                  ; Stack the X centre of screen
88EA 40B0007F           DEFB 40H,$B0,00H,7FH      ; Which is 127 decimal
88EE 03                 DEFB 03H                  ; Subract it from Mem2
88EF C4                 DEFB $C4                  ; And re-store it in Mem4
88F0 02                 DEFB 02H                  ; Tidy the stack
88F1 E3                 DEFB $E3                  ; Get Mem3
88F2 34                 DEFB 34H                  ; Stack the Y centre of screen
88F3 40B00057           DEFB 40H,$B0,00H,57H      ; Which is 87 decimal
88F7 03                 DEFB 03H                  ; Subtract it from Mem3
88F8 C5                 DEFB $C5                  ; Store result in Mem5
88F9 02                 DEFB 02H                  ; Tidy up stack
88FA E4                 DEFB $E4                  ; Get Mem4 - X diff
88FB E0                 DEFB $E0                  ; Get memo - cos angle
88FC 04                 DEFB 04H                  ; Multiply

88FD E5                 DEFB $E5                  ; Get Mem5 - Y diff
88FE E1                 DEFB $E1                  ; Get Mem1 - sin angle
88FF 04                 DEFB 04H                  ; Multiply
8900 03                 DEFB 03H                  ; (X diff x cos) - (Y diff x sin)
8901 34                 DEFB 34H                  ; Stack X centre of screen
8902 40B0007F           DEFB 40H,$B0,00H,7FH      ; Which is 127 dec
8906 0F                 DEFB 0FH                  ; Add it to above
8907 C2                 DEFB $C2                  ; Store for next loop in Mem2
8908 38                 DEFB 38H                  ; Exit calculator
8909 CDA22D             CALL 2DA2H                ; Put last value in BC - this
890C C5                 PUSH BC                   ; is the X co-ordinate of the
                                                  ; next circle point
890D EF                 RST  28H                  ; Enter calculator again
890E 02                 DEFB 02H                  ; Delete last value
890F E4                 DEFB $E4                  ; Get Mem4 - X diff
8910 E1                 DEFB $E1                  ; Get Mem1 - sin angle
8911 04                 DEFB 04H                  ; Multipy
8912 E5                 DEFB $E5                  ; Get Mem5 - Y diff
8913 E0                 DEFB $E0                  ; Get memo - cos angle
8914 04                 DEFB 04H                  ; Multiply
8915 0F                 DEFB 0FH                  ; (X diff x sin) + (Y diff x cos)
8916 34                 DEFB 34H                  ; Stack Y centre of screen
8917 40B00057           DEFB 40H,$B0,00H,57H      ; Which is 87 dec
891B 0F                 DEFB 0FH                  ; Add to above calculation
891C C3                 DEFB $C3                  ; Store in Mem3 for next loop
891D 38                 DEFB 38H                  ; Exit from calculator
891E CDA22D             CALL 2DA2H                ; Put last value in BC - this
                                                  ; is the Y co-ordinate of the
                                                  ; next circle point
8921 D1                 POP  DE                   ; Retrieve the X cp-ordinate
8922 51                 LD   D,C                  ; E and Y co-ordinate in D
8923 CD2A89             CALL PLOT                 ; Plot this point
8926 C1                 POP  BC                   ; Restore the counter in B
8927 10BD               DJNZ CLOOP                ; Loop back until finished
8929 C9                 RET

892A 7A         PLOT:   LD   A,D                  ; Test the Y co-ordinate for
892B FEC0               CP   192                  ; Off-screen
892D D0                 RET  NC                   ; Return if it is
892E E6C0               AND  $C0                  ; Calculate the screen address
8930 1F                 RRA                       ; from the co-ordinates
8931 37                 SCF                       ; with a few bit-manipulations
8932 1F                 RRA
8933 1F                 RRA
8934 AA                 XOR  D
8935 E6F8               AND  $F8
8937 AA                 XOR  D
8938 67                 LD   H,A
8939 7B                 LD   A,E
893A 07                 RLCA
893B 07                 RLCA
893C 07                 RLCA
893D AA                 XOR  D
893E E6C7               AND  $C7
8940 AA                 XOR  D
8941 07                 RLCA
8942 07                 RLCA
8943 6F                 LD   L,A                  ; Screen address now in HL
8944 7B                 LD   A,E                  ; Now we work out the bit to
8945 E607               AND  07H                  ; be plotted
8947 3C                 INC  A
8948 47                 LD   B,A                  ; Use B as A 1 to B counter
8949 3E01               LD   A,1                  ; Set bit 0 of A

894B 0F         PLOT10: RRCA                      ; Rotate into required position
894C 10FD               DJNZ PLOT10
894E B6                 OR   (HL)                 ; OR it with the screen
894F 77                 LD   (HL),A               ; and then store it
8950 C9                 RET                       ; Return to the circle routine

Previous article in series (issue 89)
Next article in series (issue 91)



Issue 89 Issue 90 Contents Issue 91

Sinclair User
September 1989
Transcribed by Richard Milne