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 mem3 and mem4. 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↑(n7), 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 mem5. Finally the printing is done, using Eformat 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 mem3, mem4 and mem5 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 mem2).  
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 mem2).  
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 mem5, 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 mem5 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 mem1).  
2E3D  DEFB $03  subtract: i, yi2  
2E3E  DEFB $E1  get_mem_1: i, yi2, i2  
2E3F  DEFB $38  end_calc: i, f2, i2 (f2=yi2)  
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 mem3: 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 nonzero 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 mem5 (the number of digits for printing) and added to the second byte of mem5 (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 reentered 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 n7.  
2E61  LD B,A  Then copied to B.  
2E62  LD HL,$5CAC  n7 is added in to the second byte of mem5, 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 mediumsized number.  
v. The integer part of x is now stored in the print buffer in mem3 and mem4.


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) reenters here.


PF_BITS  2E7B  SLA E  The mantissa of i is now rotated left and all the bits of i are thus shifted into mem4 and each byte of mem4 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 mem4 to HL; count of 5 bytes to C.  
2E88  LD C,$05  
PF_BYTES  2E8A  LD A,(HL)  Get the byte of mem4.  
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 mem4.  
2E8E  DEC HL  Point to next byte of mem4.  
2E8F  DEC C  Decrease the byte count by one.  
2E90  JR NZ,PF_BYTES  Jump for each byte of mem4.  
2E92  DJNZ PF_BITS  Jump for each bit of INT (x).  
Decimal adjusting each byte of mem4 gave 2 decimal digits per byte, there being at most 9 digits. The digits will now be repacked, one to a byte, in mem3 and mem4, using the instruction 'RLD'.


2E94  XOR A  A is cleared to receive the digits.  
2E95  LD HL,$5CA6  Source address: first byte of mem4.  
2E98  LD DE,$5CA1  Destination: first byte of mem3.  
2E9B  LD B,$09  There are at most 9 digits.  
2E9D  RLD  The left nibble of mem4 is discarded.  
2E9F  LD C,$FF  +FF in C will signal a leading zero, +00 will signal a nonleading 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 mem5: 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 mem4, 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 mem3.  
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: mem3, 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 mem5).  
2F3A  LD HL,$5CA1  The start of the digits (first byte of mem3).  
2F3D  LD A,B  If more than 9, or fewer than minus 4, digits are required before the decimal, then Eformat 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 Eformat 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 nonzero), 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 Eformat.  
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 