Amstrad Whole Memory Guide - BASIC support
Chapter 13 in the book Amstrad Whole Memory Guide.
- 1 BASIC SUPPORT
- 2 Floating Point
- 2.1 The Entry Points
- 2.1.1 BD3D
- 2.1.2 BD4Ø
- 2.1.3 BD43
- 2.1.4 BD46
- 2.1.5 BD49
- 2.1.6 BD4C
- 2.1.7 BD4F
- 2.1.8 BD52
- 2.1.9 BD55
- 2.1.10 BD58
- 2.1.11 BD5B
- 2.1.12 BD5E
- 2.1.13 BD61
- 2.1.14 BD64
- 2.1.15 BD67
- 2.1.16 BD6A
- 2.1.17 BD6D
- 2.1.18 BD73
- 2.1.19 BD76
- 2.1.20 BD79
- 2.1.21 BD7C
- 2.1.22 BD7F
- 2.1.23 BD82
- 2.1.24 BD85
- 2.1.25 BD88
- 2.1.26 BD8B
- 2.2 Using the Maths Calls
- 2.1 The Entry Points
- 3 Scanned pages
The entries to the 2A98—37FF section of the lower ROM are not officially defined, because they are more a part of the BASIC interpreter than of the operating system. However, the 49 entries hold a host of treasures, especially for those whose programs involve mathematics.
The first entry, via BD3A, accesses the EDIT system, which is rather too specialised to be of general interest, being mainly concerned with the interpretation of various key combinations and the modification of data held in a buffer, the Start of which is defined in HL on entry.
The entries relating to floating point arithmetic are of much more general importance.
A five—byte floating point system is used. For example:
86 65 2E EØ D3
The first byte is the exponent. Subtracting &80 gives 6, so the value of the exponent is 2t6 = 64. The remaining bytes form the mantissa, and the overall value of the number is found by multiplying together the values of the exponent and the mantissa. The most significant bit of the second byte is the sign bit. In this case it is Ø, so the number is positive. However, the true value of the bit in numeric terms is always 1, so the value of the last four bytes — the mantissa — is:
E62EEØD3 = 3,845,054,675
The mantissa, however, is expressed in ‘fractional binary’ which means that the most significant bit has a value of 1/2, the next bit a value of 1/4, and so on. To find the real value of the mantissa we must divide by 2t32. Multiply the result by 64 gives the overall value of the floating point number 57.2957795 = 180/PI A zero value is a special case, the exponent being 0 and the mantissa irrelevant. This can be seen as the next step from u exponent of &Ø1, which has a value 2t127. Combined with the minimum mantissa value, which is 0.5, an overall value of 2t-128 can be represented. The maximum possible value that can be showii is a whisker under 2t127.
Floating point numbers are stored with the bytes in reverse order, the least significant byte of the mantissa first and the exponent last. A number of examples can be found in ROM in the 2E18—3JFF area. Some are placed in the middle of a section of code, which makes disassembly difficult. This is because the ‘power series‘ routine picks up constants from the location following the instruction which calls it. For example:
CD A9 32 CALL 32A9 Ø4 Four entries in table 4C 4B 57 5E 7F 0.4342597 ØD 08 9B 13 80 0.5765815 23 93 33 76 8Ø 0.9618 20 3B AA 38 82 2.8853901
The routine continues at the location following the table, which in this case is taken from the LOG routine.
When a mantissa is brought into registers, it normally occupies DEHLC, C serving to collect any carry bits in right shift operations. These may be needed for rounding purposes.
There are three defined hold locations in RAM for floating point numbers:
HOLD 1: B8E5 B8EC HOLD 2: B8ED HOLD 3: B8F2 B8F6
These are primarily for the use of the basic interpreter.
The floating point and integer arithmetic can be accessed without worrying too much about the detailed working, but some comments have been added to the following tabulation to assist those who wish to investigate the routines more closely. A BASIC program given in the Appendix will help such investigations.
The Entry Points
The jumpblock entries relevant this area use the FIRM JUMP code (&EF) rather than the LOW JUMP code (&CF) used for the rest of the jumpblock but the same rules apply: On jumping to an entry there must be a return address Left on the stack to allow continuation after the called routine has been executed.
A special notation will be used for floating point data, FP(X) meaning a floating paint number pointed to by the contents of register pair X.
The actual routine addresses are not given. They can be found easily enough by examining the jumpblock entries at the addresses stated.
DE and HL on entry point to two floating point numbers (or areas where floating point numbers may exist). FP(DE) is copied to FP(HL). On exit, A ho the exponent of the copied number. Carry is Set. (Note that FP(I-IL) must be in RAM>)
On entry, DE points to an FP number area in RAM, and HL holds an unsigned binary number in the range 0 — 65535. FP(DE) is set to the floating point equivalent of the number in HL. On exit FIL on entry, DE is corrupt, and A holds the most significant byte of the new mantissa.
On entry, HL points to a four byte binary number in RAM. The number, treated as an integer, is overwritten by its FP equivalent. On exit, HL points to the new number and A hold the most significant byte of its—mantissa.
This call is used by the BASIC command CINT. On entry, HL points to an FP number in RAM, the number having a value within the range + 32767. The integer part of the number is set up in HL as a two‘s complement number rounded to the nearest whole number. On exit, A holds the sign byte of the FP number. Carry is set unless overflow occurred due to the number being too large
On entry, HL points to an FP number in RAM. BD4C is called to convert the number to integer form. If the result leaves a remainder greater than 0.5, or if the FP number was negative, the integer is incremented. On exit, C holds the number of non zero bytes in the integer.
This call is used by the BASIC command FIX. On entry, HL points to an FP number in RAM. The number is truncated to signed integer form, the result overwriting the mantissa of the original number. On exit, C holds the number of non—zero bytes in the integer. A holds &FF for a negative number, 0 for a positive number.
This call is used by the BASIC command INT. It is almost identical to BD49, except that sign is sensed, remainder ignored.
The foregoing calls need little explanation, but the next is different matter. It is used in preparation for decimal output, though it does not perform the actual output process.
An interesting algorithm is used to calculate the number the decimal places in the integer part of the number being processed. The real value of the exponent is found by subtracting &BØ, and the result is multiplied by 77/256, which is a close approximation to log10 2. The integer part of the result states the number of decimal places needed.
The calculation can be written:
Log10 N = (log2 N)*(log10 2)
where N is the number concerned.
Nine is subtracted from the number of decimal places, since up to nine can be displayed. If the result is non—zero, the number is multiplied or divided by powers of ten until it lies in the range 3125ØØ to 1Øt9. (3125ØØ — (1Øt7)/32)
On entry, HL points to an FP number in RAM. The number is processed as described above, and set in place of FP(HL). HL is then adjusted to point to the most significant byte.
This is the most difficult to use of all the BASIC support routines, and an alternative approach may be preferable.
On entry, A holds an index value, and HL points to an FP number in RAM. The number is multiplied by 10tA. A may range from -127 to +127, but values outside the range +1/- 76 will be meaningless. The result replaces FP(HL). On exit BC and DE are corrupt and A holds the sign byte of the result mantissa.
On entry, DE and HL point to FP numbers, the latter in RAM). The calculation FP(HL)= FP(HL)+FP(DE) is performed. On exit BC and DE are corrupt. A holds the sign byte of the resulting mantissa.
As BD58, but FP(HL)=FP(HL)—FP(DE)
As BD58, but FP(HL)=FP(DE)=FP(HL)
As BD58, but FP(HL)=FP(HL)*FP(DE)
As BD58, but FP(HL)=FP(HL)/FP(DE)
On entry A holds an index and HL points to an FP number in RAM. A is added to the exponent of the number, effectively multiplying the number by 2tA. A may have any value between -127 and 127. On exit. A holds the new exponent.
On entry, DE and HL point to two FP numbers.
If FP(DE) = FP(HL) the routine exits NC,Z. A=0
If FP(DE) < FP(I-IL) the routine exits NC,NZ. A=1
If FP(DE) > FP(HL), the routine exits C,NZ. A=&FF
The FP numbers are unchanged.
FP(FIL) is negated.
BD]Ø I FP(HL) = 0, the return is with A If FP(HL) > 0 e return s with A—1 If FP(HL) < 0, the return s with A—&FF.
Enterecl with A—0 this sets the RAD (radian) condition. Entry with A 1 sets the DEC (degree) condition.
FP(HL) is replaced by its square root. (SQR)
FP(HL) = FP(HL) t FP(DE)
FP(HL) is replaced by its natural logarithm. HL is preserved.
As BD7F but the logarithm is to base 10.
FP(HL) — et(FP(HL)). (EXP)
FP(HL) = SIN(FP(HL)).
FP(HL) = COS(FP(HL)).
Using the Maths Calls
You should not be deceived by the array of mathematical calls. Using them to full advantage can entail a lot of surrounding code. Nor should it be assumed that the descriptions given above cover all the possibilities. Use the program given in the Appendix to explore the details. (The descriptions are based on examination of the routines and it is on too easy to miss odd points here and there...)
Notable omission are routines for the input and output of numeric data, which is handled by the main interpreter. The input process can be complicated by the need to cover binary, decimal and hexadecimal bases, and by the fact that alphabetic data may also be involved. However, in broad terms the process involves;
(a. Checking that the data is numeric.
(b. Converting from ASCII to binary values.
(c. Multiplying the number already input by the number base.
(d. Adding the new digit.
(e. Looping to A.
Exit from the loop is usual dependent on a non-numeric being found at stage a.
The output process is more difficult, especially exponent forms are to be included. It is often desirable to position the number with care, and for that you need to know the number of decimal digits arid hence the number of leading zeroes. The process involves division by the number base, taking the remainder as a basis for the digit to be displayed, but this produces the last digit first, and a string of codes has to be assembled in reverse order before output can begin.
Access to floating point routines opens the door to many types of machine code program that would otherwise be much more difficult to write, but that does not mean that such programs become easy to create. A good deal of thought may be needed to get everything right, but the results can be very satisfying.