MACHINE CODE CALCULATOR
part 1 of 5
by Toni Baker
from ZX Computing, July 1986

Toni Baker takes a calculated delve into the Spectrum ROM.


This is the first part of a new five part series on the use of the
Spectrum's Machine Code Calculator. This is a feature built into the
ROM of the Spectrum, and it enables you to perform complicated
mathematics entirely in machine code. In fact, it is true to say that
the Machine Code Calculator can do many things which you simply cannot
do in BASIC.

It has always been a disadvantage of machine code that the instruction
set does not allow for arithmetic other than addition and subtraction
of small integers. With the Spectrum there is also a subroutine in the
ROM you can use (CALL HL=HL*DE) at address 30A9 which does
multiplication of small integers and corrupts none of the registers
except A. On exit the carry is reset normally, but set if the result
is too big to fit into HL. But even so, there is no division, let
alone more complicated stuff like square roots and random numbers.

In fact, it is because machine code is normally restricted to working
on small integers that higher level arithmetic is not possible. You
cannot divide one by two, because there is no register which can hold
the value one-half. The Spectrum Calculator neatly avoids this
problem, by representing all numbers in five bytes, rather than two.
Before we examine the operation of the calculator in detail, let's
study how this five byte representation actually works ...

Positive Integers Between 0 and 65535
If the high byte of an integer is 'pp' and the low byte of an integer
is qq; then the five byte form is '00 00 qq pp 00'.

Negative integers Between -1 and -65535
Writing -1 as FFFF, -2 as FFFE, and so on, if the high byte of such a
negative integer is 'pp' and the low byte is 'qq' then the five byte
form is '00 FF qq pp 00'.


All Other Numbers

Before I describe the general five byte format for numbers, I should
point out that the Spectrum can only handle numbers between
-1.7014118E+38 and +1.7014118E+38. Numbers outside this range cannot
be handled by the Spectrum and generally result in report code "6
Number too big" if they occur. To store numbers in five bytes involves
an ingenious little trick. The trick is to keep dividing the number by
two until it gets small enough to handle, and to count how many times
you needed to divide. This way you can store the main part of the
number (after it's divided) in four bytes, and the number of divides
in one more byte. We can then reconstruct the original number at will
by multiplying it by two the given number of times. Clever isn't it?

How it works is this: Start off with a number. Keep dividing it by two
until all you have left is a fraction between -1 and +1. The number of
times you needed to divide is called the EXPONENT, and what's left of
the number is called the MANTISSA. The exponent takes one byte, and
the mantissa takes four. Hence we have five bytes altogether.

Note that if the original number is a fraction to begin with then you
obviously don't need to divide it by two any more. Instead you
multiply by two over and over again until the number is greater than
or equal to one-half (or less than or equal to minus-one-half). In
this case the exponent may be zero or negative.

It is impossible to store the number zero in this manner, since
however many times you multiply or divide zero by two you'll always
get zero. For this reason, the number zero is always stored in five
bytes as a small integer. Zero in five byte form is invariably '00 00
00 00 00'.

The five byte form of such a number is now quite easy to explain. The
first byte is the exponent plus 128d (ie. in hex with bit seven
complemented). The remaining four bytes store ABS of the mantissa
(which is always at least one-half, and less than one, so there is an
imaginary hexadecimal point at the left of the mantissa). Finally, bit
seven of the second byte is made to contain one if the number was
negative; zero if the number was positive.

You can see this algorithm working in the program of Figure One [see
MCCALC.TAP "part 1"]. Lines 1030-1040 decide whether the number is a small
integer or a floating point number. Lines 1050 to 1110 deal with the
simple case of a small integer. Numbers between -1.469367E-39 and
+1.469367E-39 are too small for the Spectrum to deal with, and so are
treated as zero - lines 112O-1130 deal with this case and calculate the
exponent. Line 1140 calculates the mantissa. Line 1150 decides the first
of the five bytes, and lines 1160-1210 initialise the remaining four.
Lines 1220-1290 round the mantissa up if necessary, and line 1300 resets
bit seven of byte two if the number was positive. The rest of the
program just prints the result.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Figure One

1010 DIM b(5)
1020 INPUT x: PRINT x
1030 IF ABS x>65535 THEN GO TO 1120
1040 IF x<>INT x THEN GO TO 1120
1050 LET b(1)=0
1060 LET b(2)=255*(x<0)
1070 IF b(2)=255 THEN LET x=x+65536
1080 LET b(4)=INT (x/256)
1090 LET b(3)=x-256*b(4)
1100 LET b(5)=0
1110 GO TO 1310
1120 LET exponent=1+INT (LN ABS x/LN 2)
1130 IF exponent<-127 THEN LET x=0: GO TO 1050
1140 LET mantissa=ABS x/2^exponent
1150 LET b(1)=exponent+128
1160 LET a=mantissa
1170 FOR i=2 TO 5
1180 LET a=256*a
1190 LET b(i)=INT a
1200 LET a=a-b(i)
1210 NEXT i
1220 LET c=a>=.5
1230 FOR i=5 TO 2 STEP -1
1240 LET b(i)=b(i)+c
1250 LET c=b(i)=256
1260 IF c THEN LET b(i)=0
1270 NEXT i
1280 IF c THEN LET b(2)=128
1290 IF c THEN LET b(1)=b(1)+1
1300 IF x>0 THEN LET b(2)=b(2)-128
1310 FOR i=1 TO 5
1320 PRINT b(i);" ";
1330 NEXT i
1340 PRINT ''
1350 GO TO 1020
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

Figure Two [see MCCALC.TAP "part 1"] illustrates the reverse process
of turning a five byte form back to a decimal number. Line 2030 decides
whether the number is a small integer or a floating point number.
Lines 2040-2060 deal with small integers, whereas lines 2070-2120 deal with
floating point numbers. In fact this algorithm, because it is shorter
and simpler, is considerably easier to visualise than that of Figure One.

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Figure Two

2010 INPUT a;" ";e;" ";d;" ";c;" ";b
2020 PRINT a;" ";e;" ";d;" ";c;" ";b
2030 IF a<>0 THEN GO TO 2070
2040 LET number=256*c+d
2050 IF e=255 THEN LET number=number-65536
2060 GO TO 2130
2070 LET exponent=a-128
2080 LET sign=-1
2090 IF e<128 THEN LET sign=1
2100 IF e<128 THEN LET e=e+128
2110 LET mantissa=e/256+d/65536+c/16777216+b/4294967296
2120 LET number=sign*2^exponent*mantissa
2130 PRINT number''
2140 GO TO 2010
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -


Strings

Strings can also be represented in a five byte form, but the text of
the string must be stored elsewhere. For instance, to represent the
string "Stonehenge" you must first of all store the ten bytes of the
string sequentially somewhere in memory (usually in the workspace, but
you could equally well use space above RAMTOP). Then, if the address
of the first character (of the "S" in this case) has high byte 'dd'
and low byte 'ee', and if the length of the string (in this case ten)
has high byte 'bb' and low byte 'cc', then the five byte form for this
string is '00 ee dd cc bb'.


The Calculator Stack

The Calculator Stack is where all calculator operations take place. It
is the area of memory immediately above the workspace, and below spare
RAM. It is a stack which works very much like the ordinary machine
stack, except that it grows upwards instead of downwards, and that
each item on the stack is five bytes long, not two. The system
variable STKBOT points to the base of the stack - the first byte of
RAM within the calculator stack. The system variable STKEND points one
byte beyond the end of this stack - to the first byte of spare RAM. If
the stack is empty then (STKBOT) will equal (STKEND). The topmost item
on the stack will therefore occupy addresses (STKEND)-5 to (STKEND)-1.

Use of the calculator stack is essential if the awesome power of the
calculator itself is to be tapped. Essentially you merely have to
perform three tasks: to push numbers and strings onto the calculator
stack; to manipulate this stack (eg. to perform arithmetic); and to
pop the results from the stack. In this article I shall explain how to
push and pop things, and in the subsequent four articles I shall
explain how to use the calculator to manipulate the stack.


Pushing Things Onto The Stack

Integers Between 0 and +255d
Load the A register with the required integer, then CALL STACK_A (at
address 2D28).

Integers Between 0 and +65535d
Load the BC register pair with the required number, then CALL STACK_BC
(address 2D2B).

All Other Numbers
Convert the number to five byte form by the procedure described
earlier, and store in the register quintuplet AEDCB (with A containing
the exponent and EDCB containing the mantissa high byte first), then
CALL STACK_AEDCB (address 2AB6) [Actually called STK_STORE in the ROM
disassembly. JimG].

The Empty String
Load the A register with zero, then CALL STACK_A (address 2D28).

All Other Strings
Load DE with the address of the first character of the string, and BC
with the number of bytes in that string, then CALL STACK_AEDCB
(address 2AB6). Note that the value of the first byte (from the A reg)
is irrelevant.


Popping Things From The Stack

Strings
CALL FP_TO_AEDCB (at address 2BF1) [Actually called STK_FETCH in the
ROM disassembly. JimG]. DE will now contain the address of the string,
and BC will contain the length.

Floating Point Numbers
CALL FP_TO_AEDCB (at address 2BF1). The A register will contain the
exponent byte (or 00 if this is a small integer) and EDCB will contain
the sign bit and the mantissa. If the A register is zero then the
number is a small integer with C containing the high byte and B the
low; E will then contain the sign byte.

Integers Between -65535d and +65535d
CALL FP_TO_BC (at address 2DA2). If the number was greater than or
equal to 65535.5 (or less than or equal to -65535.5) then the carry
flag is set and BC will contain rubbish. Otherwise ABS of the number
is rounded up or down to the nearest integer and stored in BC. The
carry flag is reset. If the number was negative then the zero flag
will be reset, otherwise it will be set. See Figure Three [MCCALC1.GIF].

Integers Between -255d and +255d
CALL FP_TO_A (address 2DD5). If the number was greater than or equal
to 255.5 (or less than or equal to -255.5) then the carry flag is set
and A will contain rubbish. Otherwise ABS of the number is rounded up
or down to the nearest integer and stored in the A reg. The carry flag
is reset. If the number was negative then the zero flag will be reset,
otherwise it will be set See Figure Three [MCCALC1.GIF].


Manipulating The Calculator

The key to arithmetic on the calculator is a magic little machine code
instruction called RST 28 - To Be Continued ...
