Difference between revisions of "Amstrad Whole Memory Guide - BASIC support"

From CPCWiki - THE Amstrad CPC encyclopedia!
Jump to: navigation, search
(New page: Chapter 13 in the book Amstrad Whole Memory Guide. == Scanned pages == <gallery> Image:AWMG page121.jpg|page 121 Image:AWMG page122.jpg|page 122 Image:AWMG page123.jpg|page 123 Image...)
 
 
(3 intermediate revisions by one other user not shown)
Line 1: Line 1:
 
Chapter 13 in the book [[Amstrad Whole Memory Guide]].
 
Chapter 13 in the book [[Amstrad Whole Memory Guide]].
 +
 +
== BASIC SUPPORT ==
 +
 +
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.
 +
 +
== Floating Point ==
 +
 +
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:
 +
 +
<pre>
 +
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
 +
</pre>
 +
 +
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:
 +
 +
<pre>
 +
HOLD 1: B8E5 B8EC
 +
HOLD 2: B8ED
 +
HOLD 3: B8F2 B8F6
 +
</pre>
 +
 +
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.
 +
 +
==== BD3D ====
 +
 +
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>)
 +
 +
==== BD4Ø ====
 +
 +
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.
 +
 +
==== BD43 ====
 +
 +
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.
 +
 +
==== BD46 ====
 +
 +
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
 +
 +
==== BD49 ====
 +
 +
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.
 +
 +
==== BD4C ====
 +
 +
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.
 +
 +
==== BD4F ====
 +
 +
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)
 +
 +
==== BD52 ====
 +
 +
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.
 +
 +
==== BD55 ====
 +
 +
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.
 +
 +
==== BD58 ====
 +
 +
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.
 +
 +
==== BD5B ====
 +
 +
As BD58, but FP(HL)=FP(HL)—FP(DE)
 +
 +
==== BD5E ====
 +
 +
As BD58, but FP(HL)=FP(DE)=FP(HL)
 +
 +
==== BD61 ====
 +
 +
As BD58, but FP(HL)=FP(HL)*FP(DE)
 +
 +
==== BD64 ====
 +
 +
As BD58, but FP(HL)=FP(HL)/FP(DE)
 +
 +
==== BD67 ====
 +
 +
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.
 +
 +
==== BD6A ====
 +
 +
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.
 +
 +
==== BD6D ====
 +
 +
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.
 +
 +
==== BD73 ====
 +
 +
Enterecl with A—0 this sets the RAD (radian) condition. Entry with A 1 sets the DEC (degree) condition.
 +
 +
==== BD76 ====
 +
 +
FPCHL)—PI
 +
 +
==== BD79 ====
 +
 +
FP(HL) is replaced by its square root. (SQR)
 +
 +
==== BD7C ====
 +
 +
FP(HL) = FP(HL) t FP(DE)
 +
 +
==== BD7F ====
 +
 +
FP(HL) is replaced by its natural logarithm. HL is preserved.
 +
 +
==== BD82 ====
 +
 +
As BD7F but the logarithm is to base 10.
 +
 +
==== BD85 ====
 +
 +
FP(HL) — et(FP(HL)). (EXP)
 +
 +
==== BD88 ====
 +
 +
FP(HL) = SIN(FP(HL)).
 +
 +
==== BD8B ====
 +
 +
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.
  
 
== Scanned pages ==
 
== Scanned pages ==
Line 13: Line 225:
 
Image:AWMG page128.jpg|page 128
 
Image:AWMG page128.jpg|page 128
 
</gallery>
 
</gallery>
 +
 +
[[Category:CPC Firmware]]

Latest revision as of 05:49, 25 August 2014

Chapter 13 in the book Amstrad Whole Memory Guide.

BASIC SUPPORT

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.

Floating Point

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.

BD3D

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>)

BD4Ø

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.

BD43

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.

BD46

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

BD49

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.

BD4C

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.

BD4F

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)

BD52

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.

BD55

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.

BD58

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.

BD5B

As BD58, but FP(HL)=FP(HL)—FP(DE)

BD5E

As BD58, but FP(HL)=FP(DE)=FP(HL)

BD61

As BD58, but FP(HL)=FP(HL)*FP(DE)

BD64

As BD58, but FP(HL)=FP(HL)/FP(DE)

BD67

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.

BD6A

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.

BD6D

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.

BD73

Enterecl with A—0 this sets the RAD (radian) condition. Entry with A 1 sets the DEC (degree) condition.

BD76

FPCHL)—PI

BD79

FP(HL) is replaced by its square root. (SQR)

BD7C

FP(HL) = FP(HL) t FP(DE)

BD7F

FP(HL) is replaced by its natural logarithm. HL is preserved.

BD82

As BD7F but the logarithm is to base 10.

BD85

FP(HL) — et(FP(HL)). (EXP)

BD88

FP(HL) = SIN(FP(HL)).

BD8B

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.

Scanned pages