Prev: 11733 Up: Map Next: 12171
11747: THE 'PRINT A FLOATING-POINT NUMBER' SUBROUTINE
Used by the routines at PR_ITEM_1 and str.
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:
  • If x is negative, the subroutine jumps to PF_NEGTVE, takes ABS x and prints the minus sign.
  • If x is zero, x is deleted from the calculator stack, a '0' is printed and a return is made from the subroutine.
  • If x is positive, the subroutine just continues.
PRINT_FP 11747 RST 40 Use the calculator.
11748 DEFB 49 duplicate: x, x
11749 DEFB 54 less_0: x, (1/0) Logical value of x.
11750 DEFB 0,11 jump_true to PF_NEGTVE: x
11752 DEFB 49 duplicate: x, x
11753 DEFB 55 greater_0: x, (1/0) Logical value of x.
11754 DEFB 0,13 jump_true to PF_POSTVE: x Hereafter x'=ABS x.
11756 DEFB 2 delete: -
11757 DEFB 56 end_calc: -
11758 LD A,"0" Enter the character code for '0'.
11760 RST 16 Print the '0'.
11761 RET Finished as the 'last value' is zero.
PF_NEGTVE 11762 DEFB 42 abs: x' x'=ABS x.
11763 DEFB 56 end_calc: x'
11764 LD A,"-" Enter the character code for '-'.
11766 RST 16 Print the '-'.
11767 RST 40 Use the calculator again.
PF_POSTVE 11768 DEFB 160 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.
11769 DEFB 195,196,197
11772 DEFB 2 delete: The stack is cleared, except for x'.
11773 DEFB 56 end_calc: x'
11774 EXX HL', which is used to hold calculator offsets (e.g. for 'STR$'), is saved on the machine stack.
11775 PUSH HL
11776 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 11777 RST 40 Use the calculator again.
11778 DEFB 49 duplicate: x', x'
11779 DEFB 39 int: x', INT (x')=i
11780 DEFB 194 st_mem_2: (i is stored in mem-2).
11781 DEFB 3 subtract: x'-i=f
11782 DEFB 226 get_mem_2: f, i
11783 DEFB 1 exchange: i, f
11784 DEFB 194 st_mem_2: (f is stored in mem-2).
11785 DEFB 2 delete: i
11786 DEFB 56 end_calc: i
11787 LD A,(HL) Is i a small integer (first byte zero) i.e. is ABS i<=65535?
11788 AND A
11789 JR NZ,PF_LARGE Jump if it is not.
11791 CALL INT_FETCH i is copied to DE (i, like x', >=0).
11794 LD B,16 B is set to count 16 bits.
11796 LD A,D D is copied to A for testing: is it zero?
11797 AND A
11798 JR NZ,PF_SAVE Jump if it is not zero.
11800 OR E Now test E.
11801 JR Z,PF_SMALL Jump if DE is zero: x is a pure fraction.
11803 LD D,E Move E to D and set B for 8 bits: D was zero and E was not.
11804 LD B,8
PF_SAVE 11806 PUSH DE Transfer DE to DE', via the machine stack, to be moved into the print buffer at PF_BITS.
11807 EXX
11808 POP DE
11809 EXX
11810 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 11812 RST 40 i (i=zero here)
11813 DEFB 226 get_mem_2: i, f
11814 DEFB 56 end_calc: i, f
Note that the stack is now unbalanced. An extra byte 'DEFB 2, delete' is needed immediately after the RST 40. 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".
11815 LD A,(HL) The exponent byte e of f is copied to A.
11816 SUB 126 A becomes e minus 126, i.e. e'+2, where e' is the true exponent of f.
11818 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.
11821 LD D,A
11822 LD A,(23724) The current count is collected from the second byte of mem-5 and n is subtracted from it.
11825 SUB D
11826 LD (23724),A
11829 LD A,D n is copied from D to A.
11830 CALL e_to_fp y=f*10↑n is formed and stacked.
11833 RST 40 i, y
11834 DEFB 49 duplicate: i, y, y
11835 DEFB 39 int: i, y, INT (y)=i2
11836 DEFB 193 st_mem_1: (i2 is copied to mem-1).
11837 DEFB 3 subtract: i, y-i2
11838 DEFB 225 get_mem_1: i, y-i2, i2
11839 DEFB 56 end_calc: i, f2, i2 (f2=y-i2)
11840 CALL FP_TO_A i2 is transferred from the stack to A.
11843 PUSH HL The pointer to f2 is saved.
11844 LD (23713),A i2 is stored in the first byte of mem-3: a digit for printing.
11847 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.
11848 RLA
11849 SBC A,A
11850 INC A
11851 LD HL,23723 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).
11854 LD (HL),A
11855 INC HL
11856 ADD A,(HL)
11857 LD (HL),A
11858 POP HL The pointer to f2 is restored.
11859 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 11862 SUB 128 e minus 128 is e', the true exponent of i.
11864 CP 28 Is e' less than 28?
11866 JR C,PF_MEDIUM Jump if it is less.
11868 CALL LOG_2_A n is formed in A.
11871 SUB 7 And reduced to n-7.
11873 LD B,A Then copied to B.
11874 LD HL,23724 n-7 is added in to the second byte of mem-5, the number of digits required before the decimal in x.
11877 ADD A,(HL)
11878 LD (HL),A
11879 LD A,B Then i is multiplied by 10↑(-n+7). This will bring it into medium range for printing.
11880 NEG
11882 CALL e_to_fp
11885 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 11887 EX DE,HL DE now points to i, HL to f.
11888 CALL FETCH_TWO The mantissa of i is now in D', E', D, E.
11891 EXX Get the exchange registers.
11892 SET 7,D True numerical bit 7 to D'.
11894 LD A,L Exponent byte e of i to A.
11895 EXX Back to the main registers.
11896 SUB 128 True exponent e'=e minus 128 to A.
11898 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 11899 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.
11901 RL D
11903 EXX
11904 RL E
11906 RL D
11908 EXX Back to the main registers.
11909 LD HL,23722 Address of fifth byte of mem-4 to HL; count of 5 bytes to C.
11912 LD C,5
PF_BYTES 11914 LD A,(HL) Get the byte of mem-4.
11915 ADC A,A Shift it left, taking in the new bit.
11916 DAA Decimal adjust the byte.
11917 LD (HL),A Restore it to mem-4.
11918 DEC HL Point to next byte of mem-4.
11919 DEC C Decrease the byte count by one.
11920 JR NZ,PF_BYTES Jump for each byte of mem-4.
11922 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'.
11924 XOR A A is cleared to receive the digits.
11925 LD HL,23718 Source address: first byte of mem-4.
11928 LD DE,23713 Destination: first byte of mem-3.
11931 LD B,9 There are at most 9 digits.
11933 RLD The left nibble of mem-4 is discarded.
11935 LD C,255 255 in C will signal a leading zero, 0 will signal a non-leading zero.
PF_DIGITS 11937 RLD Left nibble of (HL) to A, right nibble of (HL) to left.
11939 JR NZ,PF_INSERT Jump if digit in A is not zero.
11941 DEC C Test for a leading zero: it will now give zero reset.
11942 INC C
11943 JR NZ,PF_TEST_2 Jump if it was a leading zero.
PF_INSERT 11945 LD (DE),A Insert the digit now.
11946 INC DE Point to next destination.
11947 INC (IY+113) One more digit for printing, and one more before the decimal.
11950 INC (IY+114)
11953 LD C,0 Change the flag from leading zero to other zero.
PF_TEST_2 11955 BIT 0,B The source pointer needs to be incremented on every second passage through the loop, when B is odd.
11957 JR Z,PF_ALL_9
11959 INC HL
PF_ALL_9 11960 DJNZ PF_DIGITS Jump back for all 9 digits.
11962 LD A,(23723) Get counter from the first byte of mem-5: were there 9 digits excluding leading zeros?
11965 SUB 9
11967 JR C,PF_MORE If not, jump to get more digits.
11969 DEC (IY+113) Prepare to round: reduce count to 8.
11972 LD A,4 Compare 9th digit, byte 4 of mem-4, with 4 to set carry for rounding up.
11974 CP (IY+111)
11977 JR PF_ROUND Jump forward to round up.
PF_MORE 11979 RST 40 Use the calculator again.
11980 DEFB 2 delete: - (i is now deleted).
11981 DEFB 226 get_mem_2: f
11982 DEFB 56 end_calc: f
vi. The fractional part of x is now stored in the print buffer.
PF_FRACTN 11983 EX DE,HL DE now points to f.
11984 CALL FETCH_TWO The mantissa of f is now in D', E', D, E.
11987 EXX Get the exchange registers.
11988 LD A,128 The exponent of f is reduced to zero, by shifting the bits of f 128 minus e places right, where L' contained e.
11990 SUB L
11991 LD L,0
11993 SET 7,D True numerical bit to bit 7 of D'.
11995 EXX Restore the main registers.
11996 CALL SHIFT_FP Now make the shift.
PF_FRN_LP 11999 LD A,(IY+113) Get the digit count.
12002 CP 8 Are there already 8 digits?
12004 JR C,PF_FR_DGT If not, jump forward.
12006 EXX If 8 digits, just use f to round i up, rotating D' left to set the carry.
12007 RL D
12009 EXX Restore main registers and jump forward to round up.
12010 JR PF_ROUND
PF_FR_DGT 12012 LD BC,512 Initial zero to C, count of 2 to B.
PF_FR_EXX 12015 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.
12016 CALL CA_10A_C
12019 LD E,A
12020 LD A,D
12021 CALL CA_10A_C
12024 LD D,A
12025 PUSH BC The count and the result alternate between BC and BC'.
12026 EXX
12027 POP BC
12028 DJNZ PF_FR_EXX Loop back once through the exchange registers.
12030 LD HL,23713 The start - 1st byte of mem-3.
12033 LD A,C Result to A for storing.
12034 LD C,(IY+113) Count of digits so far in number to C.
12037 ADD HL,BC Address the first empty byte.
12038 LD (HL),A Store the next digit.
12039 INC (IY+113) Step up the count of digits.
12042 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 12044 PUSH AF Save the carry flag for the rounding.
12045 LD HL,23713 Base address of number: mem-3, byte 1.
12048 LD C,(IY+113) Offset (number of digits in number) to BC.
12051 LD B,0
12053 ADD HL,BC Address the last byte of the number.
12054 LD B,C Copy C to B as the counter.
12055 POP AF Restore the carry flag.
PF_RND_LP 12056 DEC HL This is the last byte of the number.
12057 LD A,(HL) Get the byte into A.
12058 ADC A,0 Add in the carry i.e. round up.
12060 LD (HL),A Store the rounded byte in the buffer.
12061 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.
12062 JR Z,PF_R_BACK
12064 CP 10
12066 CCF Reset the carry for a valid digit.
12067 JR NC,PF_COUNT Jump if carry reset.
PF_R_BACK 12069 DJNZ PF_RND_LP Jump back for more rounding or more final zeros.
12071 LD (HL),1 There is overflow to the left; an extra 1 is needed here.
12073 INC B
12074 INC (IY+114) It is also an extra digit before the decimal.
PF_COUNT 12077 LD (IY+113),B B now sets the count of the digits to be printed (final zeros will not be printed).
12080 RST 40 f is to be deleted.
12081 DEFB 2 delete: -
12082 DEFB 56 end_calc: -
12083 EXX The calculator offset saved on the stack is restored to HL'.
12084 POP HL
12085 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.
12086 LD BC,(23723) The counters are set (first two bytes of mem-5).
12090 LD HL,23713 The start of the digits (first byte of mem-3).
12093 LD A,B If more than 9, or fewer than minus 4, digits are required before the decimal, then E-format will be needed.
12094 CP 9
12096 JR C,PF_NOT_E
12098 CP 252 Fewer than 4 means more than 4 leading zeros after the decimal.
12100 JR C,PF_E_FRMT
PF_NOT_E 12102 AND A Are there no digits before the decimal? If so, print an initial zero.
12103 CALL Z,OUT_CODE
The next entry point is also used to print the digits needed for E-format printing.
PF_E_SBRN 12106 XOR A Start by setting A to zero.
12107 SUB B Subtract B: minus will mean there are digits before the decimal; jump forward to print them.
12108 JP M,PF_OUT_LP
12111 LD B,A A is now required as a counter.
12112 JR PF_DC_OUT Jump forward to print the decimal part.
PF_OUT_LP 12114 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.
12115 AND A
12116 JR Z,PF_OUT_DT
12118 LD A,(HL) Get a digit from the print buffer.
12119 INC HL Point to the next digit.
12120 DEC C Decrease the count by one.
PF_OUT_DT 12121 CALL OUT_CODE Print the appropriate digit.
12124 DJNZ PF_OUT_LP Loop back until B is zero.
PF_DC_OUT 12126 LD A,C It is time to print the decimal, unless C is now zero; in that case, return - finished.
12127 AND A
12128 RET Z
12129 INC B Add 1 to B - include the decimal.
12130 LD A,"." Put the code for '.' into A.
PF_DEC_0S 12132 RST 16 Print the '.'.
12133 LD A,"0" Enter the character code for '0'.
12135 DJNZ PF_DEC_0S Loop back to print all needed zeros.
12137 LD B,C Set the count for all remaining digits.
12138 JR PF_OUT_LP Jump back to print them.
PF_E_FRMT 12140 LD D,B The count of digits is copied to D.
12141 DEC D It is decremented to give the exponent.
12142 LD B,1 One digit is required before the decimal in E-format.
12144 CALL PF_E_SBRN All the part of the number before the 'E' is now printed.
12147 LD A,"E" Enter the character code for 'E'.
12149 RST 16 Print the 'E'.
12150 LD C,D Exponent to C now for printing.
12151 LD A,C And to A for testing.
12152 AND A Its sign is tested.
12153 JP P,PF_E_POS Jump if it is positive.
12156 NEG Otherwise, negate it in A.
12158 LD C,A Then copy it back to C for printing.
12159 LD A,"-" Enter the character code for '-'.
12161 JR PF_E_SIGN Jump to print the sign.
PF_E_POS 12163 LD A,"+" Enter the character code for '+'.
PF_E_SIGN 12165 RST 16 Now print the sign: '+' or '-'.
12166 LD B,0 BC holds the exponent for printing.
12168 JP OUT_NUM_1 Jump back to print it and finish.
Prev: 11733 Up: Map Next: 12171