Routines |
Prev: 2DD5 | Up: Map | Next: 2F8B |
This subroutine prints x, the 'last value' on the calculator stack. The print format never occupies more than 14 spaces.
The 8 most significant digits of x, correctly rounded, are stored in an ad hoc print buffer in mem-3 and mem-4. Small numbers, numerically less than 1, and large numbers, numerically greater than 2↑27, are dealt with separately. The former are multiplied by 10↑n, where n is the approximate number of leading zeros after the decimal, while the latter are divided by 10↑(n-7), where n is the approximate number of digits before the decimal. This brings all numbers into the middle range, and the number of digits required before the decimal is built up in the second byte of mem-5. Finally the printing is done, using E-format if there are more than 8 digits before the decimal or, for small numbers, more than 4 leading zeros after the decimal.
The following program shows the range of print formats:
10 FOR a=-11 TO 12: PRINT SGN a*9↑a,: NEXT a
i. First the sign of x is taken care of:
|
||||
PRINT_FP | 2DE3 | RST $28 | Use the calculator. | |
2DE4 | DEFB $31 | duplicate: x, x | ||
2DE5 | DEFB $36 | less_0: x, (1/0) Logical value of x. | ||
2DE6 | DEFB $00,$0B | jump_true to PF_NEGTVE: x | ||
2DE8 | DEFB $31 | duplicate: x, x | ||
2DE9 | DEFB $37 | greater_0: x, (1/0) Logical value of x. | ||
2DEA | DEFB $00,$0D | jump_true to PF_POSTVE: x Hereafter x'=ABS x. | ||
2DEC | DEFB $02 | delete: - | ||
2DED | DEFB $38 | end_calc: - | ||
2DEE | LD A,"0" | Enter the character code for '0'. | ||
2DF0 | RST $10 | Print the '0'. | ||
2DF1 | RET | Finished as the 'last value' is zero. | ||
PF_NEGTVE | 2DF2 | DEFB $2A | abs: x' x'=ABS x. | |
2DF3 | DEFB $38 | end_calc: x' | ||
2DF4 | LD A,"-" | Enter the character code for '-'. | ||
2DF6 | RST $10 | Print the '-'. | ||
2DF7 | RST $28 | Use the calculator again. | ||
PF_POSTVE | 2DF8 | DEFB $A0 | stk_zero: The 15 bytes of mem-3, mem-4 and mem-5 are now initialised to zero to be used for a print buffer and two counters. | |
2DF9 | DEFB $C3,$C4,$C5 | |||
2DFC | DEFB $02 | delete: The stack is cleared, except for x'. | ||
2DFD | DEFB $38 | end_calc: x' | ||
2DFE | EXX | HL', which is used to hold calculator offsets (e.g. for 'STR$'), is saved on the machine stack. | ||
2DFF | PUSH HL | |||
2E00 | EXX | |||
ii. This is the start of a loop which deals with large numbers. Every number x is first split into its integer part i and the fractional part f. If i is a small integer, i.e. if -65535<=i<=65535, it is stored in DE' for insertion into the print buffer.
|
||||
PF_LOOP | 2E01 | RST $28 | Use the calculator again. | |
2E02 | DEFB $31 | duplicate: x', x' | ||
2E03 | DEFB $27 | int: x', INT (x')=i | ||
2E04 | DEFB $C2 | st_mem_2: (i is stored in mem-2). | ||
2E05 | DEFB $03 | subtract: x'-i=f | ||
2E06 | DEFB $E2 | get_mem_2: f, i | ||
2E07 | DEFB $01 | exchange: i, f | ||
2E08 | DEFB $C2 | st_mem_2: (f is stored in mem-2). | ||
2E09 | DEFB $02 | delete: i | ||
2E0A | DEFB $38 | end_calc: i | ||
2E0B | LD A,(HL) | Is i a small integer (first byte zero) i.e. is ABS i<=65535? | ||
2E0C | AND A | |||
2E0D | JR NZ,PF_LARGE | Jump if it is not. | ||
2E0F | CALL INT_FETCH | i is copied to DE (i, like x', >=0). | ||
2E12 | LD B,$10 | B is set to count 16 bits. | ||
2E14 | LD A,D | D is copied to A for testing: is it zero? | ||
2E15 | AND A | |||
2E16 | JR NZ,PF_SAVE | Jump if it is not zero. | ||
2E18 | OR E | Now test E. | ||
2E19 | JR Z,PF_SMALL | Jump if DE is zero: x is a pure fraction. | ||
2E1B | LD D,E | Move E to D and set B for 8 bits: D was zero and E was not. | ||
2E1C | LD B,$08 | |||
PF_SAVE | 2E1E | PUSH DE | Transfer DE to DE', via the machine stack, to be moved into the print buffer at PF_BITS. | |
2E1F | EXX | |||
2E20 | POP DE | |||
2E21 | EXX | |||
2E22 | JR PF_BITS | Jump forward. | ||
iii. Pure fractions are multiplied by 10↑n, where n is the approximate number of leading zeros after the decimal; and -n is added to the second byte of mem-5, which holds the number of digits needed before the decimal; a negative number here indicates leading zeros after the decimal.
|
||||
PF_SMALL | 2E24 | RST $28 | i (i=zero here) | |
2E25 | DEFB $E2 | get_mem_2: i, f | ||
2E26 | DEFB $38 | end_calc: i, f | ||
Note that the stack is now unbalanced. An extra byte 'DEFB +02, delete' is needed immediately after the RST $28. Now an expression like "2"+STR$ 0.5 is evaluated incorrectly as 0.5; the zero left on the stack displaces the "2" and is treated as a null string. Similarly all the string comparisons can yield incorrect values if the second string takes the form STR$ x where x is numerically less than 1; e.g. the expression "50"<STR$ 0.1 yields the logical value "true"; once again "" is used instead of "50".
|
||||
2E27 | LD A,(HL) | The exponent byte e of f is copied to A. | ||
2E28 | SUB $7E | A becomes e minus +7E, i.e. e'+2, where e' is the true exponent of f. | ||
2E2A | CALL LOG_2_A | The construction A=ABS INT (LOG (2↑A)) is performed (LOG is to base 10); i.e. A=n, say: n is copied from A to D. | ||
2E2D | LD D,A | |||
2E2E | LD A,($5CAC) | The current count is collected from the second byte of mem-5 and n is subtracted from it. | ||
2E31 | SUB D | |||
2E32 | LD ($5CAC),A | |||
2E35 | LD A,D | n is copied from D to A. | ||
2E36 | CALL e_to_fp | y=f*10↑n is formed and stacked. | ||
2E39 | RST $28 | i, y | ||
2E3A | DEFB $31 | duplicate: i, y, y | ||
2E3B | DEFB $27 | int: i, y, INT (y)=i2 | ||
2E3C | DEFB $C1 | st_mem_1: (i2 is copied to mem-1). | ||
2E3D | DEFB $03 | subtract: i, y-i2 | ||
2E3E | DEFB $E1 | get_mem_1: i, y-i2, i2 | ||
2E3F | DEFB $38 | end_calc: i, f2, i2 (f2=y-i2) | ||
2E40 | CALL FP_TO_A | i2 is transferred from the stack to A. | ||
2E43 | PUSH HL | The pointer to f2 is saved. | ||
2E44 | LD ($5CA1),A | i2 is stored in the first byte of mem-3: a digit for printing. | ||
2E47 | DEC A | i2 will not count as a digit for printing if it is zero; A is manipulated so that zero will produce zero but a non-zero digit will produce 1. | ||
2E48 | RLA | |||
2E49 | SBC A,A | |||
2E4A | INC A | |||
2E4B | LD HL,$5CAB | The zero or one is inserted into the first byte of mem-5 (the number of digits for printing) and added to the second byte of mem-5 (the number of digits before the decimal). | ||
2E4E | LD (HL),A | |||
2E4F | INC HL | |||
2E50 | ADD A,(HL) | |||
2E51 | LD (HL),A | |||
2E52 | POP HL | The pointer to f2 is restored. | ||
2E53 | JP PF_FRACTN | Jump to store f2 in buffer (HL now points to f2, DE to i2). | ||
iv. Numbers greater than 2↑27 are similarly multiplied by 2↑(-n+7), reducing the number of digits before the decimal to 8, and the loop is re-entered at PF_LOOP.
|
||||
PF_LARGE | 2E56 | SUB $80 | e minus +80 is e', the true exponent of i. | |
2E58 | CP $1C | Is e' less than 28? | ||
2E5A | JR C,PF_MEDIUM | Jump if it is less. | ||
2E5C | CALL LOG_2_A | n is formed in A. | ||
2E5F | SUB $07 | And reduced to n-7. | ||
2E61 | LD B,A | Then copied to B. | ||
2E62 | LD HL,$5CAC | n-7 is added in to the second byte of mem-5, the number of digits required before the decimal in x. | ||
2E65 | ADD A,(HL) | |||
2E66 | LD (HL),A | |||
2E67 | LD A,B | Then i is multiplied by 10↑(-n+7). This will bring it into medium range for printing. | ||
2E68 | NEG | |||
2E6A | CALL e_to_fp | |||
2E6D | JR PF_LOOP | Round the loop again to deal with the now medium-sized number. | ||
v. The integer part of x is now stored in the print buffer in mem-3 and mem-4.
|
||||
PF_MEDIUM | 2E6F | EX DE,HL | DE now points to i, HL to f. | |
2E70 | CALL FETCH_TWO | The mantissa of i is now in D', E', D, E. | ||
2E73 | EXX | Get the exchange registers. | ||
2E74 | SET 7,D | True numerical bit 7 to D'. | ||
2E76 | LD A,L | Exponent byte e of i to A. | ||
2E77 | EXX | Back to the main registers. | ||
2E78 | SUB $80 | True exponent e'=e minus +80 to A. | ||
2E7A | LD B,A | This gives the required bit count. | ||
Note that the case where i is a small integer (less than 65536) re-enters here.
|
||||
PF_BITS | 2E7B | SLA E | The mantissa of i is now rotated left and all the bits of i are thus shifted into mem-4 and each byte of mem-4 is decimal adjusted at each shift. | |
2E7D | RL D | |||
2E7F | EXX | |||
2E80 | RL E | |||
2E82 | RL D | |||
2E84 | EXX | Back to the main registers. | ||
2E85 | LD HL,$5CAA | Address of fifth byte of mem-4 to HL; count of 5 bytes to C. | ||
2E88 | LD C,$05 | |||
PF_BYTES | 2E8A | LD A,(HL) | Get the byte of mem-4. | |
2E8B | ADC A,A | Shift it left, taking in the new bit. | ||
2E8C | DAA | Decimal adjust the byte. | ||
2E8D | LD (HL),A | Restore it to mem-4. | ||
2E8E | DEC HL | Point to next byte of mem-4. | ||
2E8F | DEC C | Decrease the byte count by one. | ||
2E90 | JR NZ,PF_BYTES | Jump for each byte of mem-4. | ||
2E92 | DJNZ PF_BITS | Jump for each bit of INT (x). | ||
Decimal adjusting each byte of mem-4 gave 2 decimal digits per byte, there being at most 9 digits. The digits will now be re-packed, one to a byte, in mem-3 and mem-4, using the instruction 'RLD'.
|
||||
2E94 | XOR A | A is cleared to receive the digits. | ||
2E95 | LD HL,$5CA6 | Source address: first byte of mem-4. | ||
2E98 | LD DE,$5CA1 | Destination: first byte of mem-3. | ||
2E9B | LD B,$09 | There are at most 9 digits. | ||
2E9D | RLD | The left nibble of mem-4 is discarded. | ||
2E9F | LD C,$FF | +FF in C will signal a leading zero, +00 will signal a non-leading zero. | ||
PF_DIGITS | 2EA1 | RLD | Left nibble of (HL) to A, right nibble of (HL) to left. | |
2EA3 | JR NZ,PF_INSERT | Jump if digit in A is not zero. | ||
2EA5 | DEC C | Test for a leading zero: it will now give zero reset. | ||
2EA6 | INC C | |||
2EA7 | JR NZ,PF_TEST_2 | Jump if it was a leading zero. | ||
PF_INSERT | 2EA9 | LD (DE),A | Insert the digit now. | |
2EAA | INC DE | Point to next destination. | ||
2EAB | INC (IY+$71) | One more digit for printing, and one more before the decimal. | ||
2EAE | INC (IY+$72) | |||
2EB1 | LD C,$00 | Change the flag from leading zero to other zero. | ||
PF_TEST_2 | 2EB3 | BIT 0,B | The source pointer needs to be incremented on every second passage through the loop, when B is odd. | |
2EB5 | JR Z,PF_ALL_9 | |||
2EB7 | INC HL | |||
PF_ALL_9 | 2EB8 | DJNZ PF_DIGITS | Jump back for all 9 digits. | |
2EBA | LD A,($5CAB) | Get counter from the first byte of mem-5: were there 9 digits excluding leading zeros? | ||
2EBD | SUB $09 | |||
2EBF | JR C,PF_MORE | If not, jump to get more digits. | ||
2EC1 | DEC (IY+$71) | Prepare to round: reduce count to 8. | ||
2EC4 | LD A,$04 | Compare 9th digit, byte 4 of mem-4, with 4 to set carry for rounding up. | ||
2EC6 | CP (IY+$6F) | |||
2EC9 | JR PF_ROUND | Jump forward to round up. | ||
PF_MORE | 2ECB | RST $28 | Use the calculator again. | |
2ECC | DEFB $02 | delete: - (i is now deleted). | ||
2ECD | DEFB $E2 | get_mem_2: f | ||
2ECE | DEFB $38 | end_calc: f | ||
vi. The fractional part of x is now stored in the print buffer.
|
||||
PF_FRACTN | 2ECF | EX DE,HL | DE now points to f. | |
2ED0 | CALL FETCH_TWO | The mantissa of f is now in D', E', D, E. | ||
2ED3 | EXX | Get the exchange registers. | ||
2ED4 | LD A,$80 | The exponent of f is reduced to zero, by shifting the bits of f +80 minus e places right, where L' contained e. | ||
2ED6 | SUB L | |||
2ED7 | LD L,$00 | |||
2ED9 | SET 7,D | True numerical bit to bit 7 of D'. | ||
2EDB | EXX | Restore the main registers. | ||
2EDC | CALL SHIFT_FP | Now make the shift. | ||
PF_FRN_LP | 2EDF | LD A,(IY+$71) | Get the digit count. | |
2EE2 | CP $08 | Are there already 8 digits? | ||
2EE4 | JR C,PF_FR_DGT | If not, jump forward. | ||
2EE6 | EXX | If 8 digits, just use f to round i up, rotating D' left to set the carry. | ||
2EE7 | RL D | |||
2EE9 | EXX | Restore main registers and jump forward to round up. | ||
2EEA | JR PF_ROUND | |||
PF_FR_DGT | 2EEC | LD BC,$0200 | Initial zero to C, count of 2 to B. | |
PF_FR_EXX | 2EEF | LD A,E | D'E'DE is multiplied by 10 in 2 stages, first DE then DE', each byte by byte in 2 steps, and the integer part of the result is obtained in C to be passed into the print buffer. | |
2EF0 | CALL CA_10A_C | |||
2EF3 | LD E,A | |||
2EF4 | LD A,D | |||
2EF5 | CALL CA_10A_C | |||
2EF8 | LD D,A | |||
2EF9 | PUSH BC | The count and the result alternate between BC and BC'. | ||
2EFA | EXX | |||
2EFB | POP BC | |||
2EFC | DJNZ PF_FR_EXX | Loop back once through the exchange registers. | ||
2EFE | LD HL,$5CA1 | The start - 1st byte of mem-3. | ||
2F01 | LD A,C | Result to A for storing. | ||
2F02 | LD C,(IY+$71) | Count of digits so far in number to C. | ||
2F05 | ADD HL,BC | Address the first empty byte. | ||
2F06 | LD (HL),A | Store the next digit. | ||
2F07 | INC (IY+$71) | Step up the count of digits. | ||
2F0A | JR PF_FRN_LP | Loop back until there are 8 digits. | ||
vii. The digits stored in the print buffer are rounded to a maximum of 8 digits for printing.
|
||||
PF_ROUND | 2F0C | PUSH AF | Save the carry flag for the rounding. | |
2F0D | LD HL,$5CA1 | Base address of number: mem-3, byte 1. | ||
2F10 | LD C,(IY+$71) | Offset (number of digits in number) to BC. | ||
2F13 | LD B,$00 | |||
2F15 | ADD HL,BC | Address the last byte of the number. | ||
2F16 | LD B,C | Copy C to B as the counter. | ||
2F17 | POP AF | Restore the carry flag. | ||
PF_RND_LP | 2F18 | DEC HL | This is the last byte of the number. | |
2F19 | LD A,(HL) | Get the byte into A. | ||
2F1A | ADC A,$00 | Add in the carry i.e. round up. | ||
2F1C | LD (HL),A | Store the rounded byte in the buffer. | ||
2F1D | AND A | If the byte is 0 or 10, B will be decremented and the final zero (or the 10) will not be counted for printing. | ||
2F1E | JR Z,PF_R_BACK | |||
2F20 | CP $0A | |||
2F22 | CCF | Reset the carry for a valid digit. | ||
2F23 | JR NC,PF_COUNT | Jump if carry reset. | ||
PF_R_BACK | 2F25 | DJNZ PF_RND_LP | Jump back for more rounding or more final zeros. | |
2F27 | LD (HL),$01 | There is overflow to the left; an extra 1 is needed here. | ||
2F29 | INC B | |||
2F2A | INC (IY+$72) | It is also an extra digit before the decimal. | ||
PF_COUNT | 2F2D | LD (IY+$71),B | B now sets the count of the digits to be printed (final zeros will not be printed). | |
2F30 | RST $28 | f is to be deleted. | ||
2F31 | DEFB $02 | delete: - | ||
2F32 | DEFB $38 | end_calc: - | ||
2F33 | EXX | The calculator offset saved on the stack is restored to HL'. | ||
2F34 | POP HL | |||
2F35 | EXX | |||
viii. The number can now be printed. First C will be set to hold the number of digits to be printed, not counting final zeros, while B will hold the number of digits required before the decimal.
|
||||
2F36 | LD BC,($5CAB) | The counters are set (first two bytes of mem-5). | ||
2F3A | LD HL,$5CA1 | The start of the digits (first byte of mem-3). | ||
2F3D | LD A,B | If more than 9, or fewer than minus 4, digits are required before the decimal, then E-format will be needed. | ||
2F3E | CP $09 | |||
2F40 | JR C,PF_NOT_E | |||
2F42 | CP $FC | Fewer than 4 means more than 4 leading zeros after the decimal. | ||
2F44 | JR C,PF_E_FRMT | |||
PF_NOT_E | 2F46 | AND A | Are there no digits before the decimal? If so, print an initial zero. | |
2F47 | CALL Z,OUT_CODE | |||
The next entry point is also used to print the digits needed for E-format printing.
|
||||
PF_E_SBRN | 2F4A | XOR A | Start by setting A to zero. | |
2F4B | SUB B | Subtract B: minus will mean there are digits before the decimal; jump forward to print them. | ||
2F4C | JP M,PF_OUT_LP | |||
2F4F | LD B,A | A is now required as a counter. | ||
2F50 | JR PF_DC_OUT | Jump forward to print the decimal part. | ||
PF_OUT_LP | 2F52 | LD A,C | Copy the number of digits to be printed to A. If A is 0, there are still final zeros to print (B is non-zero), so jump. | |
2F53 | AND A | |||
2F54 | JR Z,PF_OUT_DT | |||
2F56 | LD A,(HL) | Get a digit from the print buffer. | ||
2F57 | INC HL | Point to the next digit. | ||
2F58 | DEC C | Decrease the count by one. | ||
PF_OUT_DT | 2F59 | CALL OUT_CODE | Print the appropriate digit. | |
2F5C | DJNZ PF_OUT_LP | Loop back until B is zero. | ||
PF_DC_OUT | 2F5E | LD A,C | It is time to print the decimal, unless C is now zero; in that case, return - finished. | |
2F5F | AND A | |||
2F60 | RET Z | |||
2F61 | INC B | Add 1 to B - include the decimal. | ||
2F62 | LD A,"." | Put the code for '.' into A. | ||
PF_DEC_0S | 2F64 | RST $10 | Print the '.'. | |
2F65 | LD A,"0" | Enter the character code for '0'. | ||
2F67 | DJNZ PF_DEC_0S | Loop back to print all needed zeros. | ||
2F69 | LD B,C | Set the count for all remaining digits. | ||
2F6A | JR PF_OUT_LP | Jump back to print them. | ||
PF_E_FRMT | 2F6C | LD D,B | The count of digits is copied to D. | |
2F6D | DEC D | It is decremented to give the exponent. | ||
2F6E | LD B,$01 | One digit is required before the decimal in E-format. | ||
2F70 | CALL PF_E_SBRN | All the part of the number before the 'E' is now printed. | ||
2F73 | LD A,"E" | Enter the character code for 'E'. | ||
2F75 | RST $10 | Print the 'E'. | ||
2F76 | LD C,D | Exponent to C now for printing. | ||
2F77 | LD A,C | And to A for testing. | ||
2F78 | AND A | Its sign is tested. | ||
2F79 | JP P,PF_E_POS | Jump if it is positive. | ||
2F7C | NEG | Otherwise, negate it in A. | ||
2F7E | LD C,A | Then copy it back to C for printing. | ||
2F7F | LD A,"-" | Enter the character code for '-'. | ||
2F81 | JR PF_E_SIGN | Jump to print the sign. | ||
PF_E_POS | 2F83 | LD A,"+" | Enter the character code for '+'. | |
PF_E_SIGN | 2F85 | RST $10 | Now print the sign: '+' or '-'. | |
2F86 | LD B,$00 | BC holds the exponent for printing. | ||
2F88 | JP OUT_NUM_1 | Jump back to print it and finish. |
Prev: 2DD5 | Up: Map | Next: 2F8B |