[Index] [Previous] [Next]

2.10 Printing Numbers

Functions for printing floating-point numbers.

PrintIN

Prints "IN " and falls into PrintInt. Used by the error handling code to print stuff like "?SN ERROR IN 50".

0B2F E5 PrintIN PUSH H
0B30 218801 LXI H,szIn
0B33 CDA305 CALL PrintString
0B36 E1 POP H

 

PrintInt

Promotes the integer in HL to a floating-point number in FACC, sets the return address to PrintSz-1, and falls into FOut. The promotion from integer to float is interesting : the integer starts off by occupying the least significant bits of the mantissa CDE. The exponent in B is set to 24 (because, thus giving us an unnormalised but perfectly valid floating-point number in no time at all! Took me a while to see that...

0B37 EB PrintInt XCHG DE=integer
0B38 AF XRA A A=0 (ends up in C)
0B39 0698 MVI B,98 B (ie exponent) = 24
0B3B CDEA09 CALL FCharToFloat+5
0B3E 21A205 LXI H,PrintSz-1
0B41 E5 PUSH H

 

FOut

Prints a floating point number to the terminal.

Set HL to FBUFFER, which is where FACCUM gets printed to.
0B42 217401 FOut LXI H,FBUFFER
0B45 E5 PUSH H
Test FACCUM. If it's positive then write a leading space; if it's negative then write a leading minus sign.
0B46 EF RST FTestSign
0B47 3620 MVI M,' '
0B49 F24E0B JP DoZero
0B4C 362D MVI M,'-'

Write a '0', and if FACCUM equals 0 then jump to NullTerm-3, which is a convenient shortcut for null-terminating the output buffer. Jumping to NullTerm-3 means that a spurious byte (in C) gets written immediately following the null-terminator, but this isn't a problem because we're nowhere near the end of the buffer plus doing it this way we save a couple of bytes we would have lost had we insisted on jumping to NullTerm with C explicitly set to 0. If FACCUM is not zero then the '0' gets overwritten a few lines down.

0B4E 23 DoZero INX H
0B4F 3630 MVI M,'0'
0B51 CAF70B JZ NullTerm-3
Make FACCUM a positive number by negating it if it's negative.
0B54 E5 PUSH H
0B55 FCFA09 CM FNegate
Initialise Decimal Exponent Adjustment (hereafter shortened to DecExpAdj) to 0.
0B58 AF XRA A
0B59 F5 PUSH PSW
Here's where we bring FACCUM into range between 100,000 and 1,000,000 by multiplying or dividing by ten a number of times. The first call ensures FACCUM is less than 1,000,000 and the loop that follows makes it more than or equal to 100,000. The decimal exponent that we had to use on FACCUM to get it into this range (referred to as DecExpAdj) is kept on the stack.
0B5A CDFD0B CALL ToUnder1,000,000
0B5D 014391 ToOver100,000 LXI B,9143 BCDE=(float)100,000.
0B60 11F84F LXI D,4FF8
0B63 CD4C0A CALL FCompare If FACCUM >= 100,000
0B66 E27A0B JPO PrepareToPrint then jump to PrepareToPrint.
0B69 F1 POP PSW A=DecExpAdj
0B6A CDFD0A CALL DecimalShiftUp FACCUM*=10; DecExpAdj--;
0B6D F5 PUSH PSW
0B6E C35D0B JMP ToOver100,000
Divide FACCUM by ten and increment DecExpAdj.
0B71 CD2309 CALL DecimalShiftDown
0B74 F1 POP PSW
0B75 3C INR A DecExpAdj++;
0B76 F5 PUSH PSW
0B77 CDFD0B CALL ToUnder1,000,000
Some preparation. We add 0.5 to FACCUM, make it an integer, then finally store the result of that in FACCUM.
0B7A CD0108 PrepareToPrint CALL FAddOneHalf
0B7D 3C INR A
0B7E CD770A CALL FAsInteger
0B81 CD120A CALL FLoadFromBCDE
fixme.
0B84 010602 LXI B,0206
0B87 F1 POP PSW A=DecExpAdj+6.
0B88 81 ADD C
0B89 FA950B JM 0B95 If A<1 or A>6 Then goto fixme.
0B8C FE07 CPI 07
0B8E D2950B JNC 0B95
0B91 3C INR A
0B92 47 MOV B,A
0B93 3E01 MVI A,01 A=1, indicating scientific notation.
fixme.
0B95 3D DCR A
0B96 E1 POP H HL=output buffer
0B97 F5 PUSH PSW Preserve decimal exponent adjustment (and preserve zero flag used to indicate scientific notation wanted).
0B98 110F0C LXI D,DECIMAL_POWERS
NextDigit. This is the outer loop of printing, where each ASCII digit is calculated in turn. We start by writing out the decimal point, but we only advance HL to keep it if B==0, which means (obviously) that the decimal point has been reached.
0B9B 05 NextDigit DCR B
0B9C 362E MVI M,'.'
0B9E CC270A CZ IncHL+Return 0A27 just happens to inc HL and RET.
0BA1 C5 PUSH B
0BA2 E5 PUSH H
0BA3 D5 PUSH D DE=>decimal power
0BA4 CD1D0A CALL FCopyToBCDE Store BCDE to FACCUM.
0BA7 E1 POP H HL=>decimal power.
0BA8 062F MVI B,'0'-1
Work out the digit corresponding to the current decimal power. We do this by subtracting the decimal power (eg 100) from CDE until it overflows, and incrementing the ASCII digit value in B each time. When it overflows, we have our digit. And when it overflows, we call FAddMantissas to undo the last subtraction which was one step too far.
0BAA 04 DigitLoop INR B
0BAB 7B MOV A,E
0BAC 96 SUB M
0BAD 5F MOV E,A
0BAE 23 INX H
0BAF 7A MOV A,D
0BB0 9E SBB M
0BB1 57 MOV D,A
0BB2 23 INX H
0BB3 79 MOV A,C
0BB4 9E SBB M
0BB5 4F MOV C,A
0BB6 2B DCX H
0BB7 2B DCX H
0BB8 D2AA0B JNC DigitLoop
0BBB CDA908 CALL FAddMantissas
0BBE 23 INX H ???
0BBF CD120A CALL FLoadFromBCDE
Write out the digit. If we still have digits to do then loop back.
0BC2 EB XCHG
0BC3 E1 POP H HL=output buffer
0BC4 70 MOV M,B
0BC5 23 INX H
0BC6 C1 POP B B=decimal point place
0BC7 0D DCR C C=digits remaining, minus one.
0BC8 C29B0B JNZ NextDigit
0BCB 05 DCR B
0BCC CADB0B JZ 0BDB
Move HL one byte behind the first trailing zero.
0BCF 2B DCX H
0BD0 7E MOV A,M
0BD1 FE30 CPI '0'
0BD3 CACF0B JZ 0BCF
If we've no decimal point, then increment HL so it's
0BD6 FE2E CPI '.'
0BD8 C4270A CNZ IncHL+Return
0BDB F1 POP PSW
0BDC CAFA0B JZ NullTerm
Write exponent part of scientific format.
0BDF 3645 MVI M,'E' Write 'E'
0BE1 23 INX H
0BE2 362B MVI M,'+' Write '+' or '-'
0BE4 F2EB0B JP 0BEB
0BE7 362D MVI M,'-' Write '-' if it's negative, also
0BE9 2F CMA two's complement the decimal exponent
0BEA 3C INR A so printing it will work.
0BEB 062F MVI B,'0'-1
Work out the first digit of exponent in B. Done by usual method of repeatedly subtracting 10 until it overflows.
0BED 04 ExpDigitLoop INR B
0BEE D60A SUI 0A
0BF0 D2ED0B JNC ExpDigitLoop
0BF3 C63A ADI 3A Adding '0'+10 gives us the 2nd digit
0BF5 23 INX H of the exponent.
0BF6 70 MOV M,B Write first digit.
0BF7 23   INX H
0BF8 77 MOV M,A Write second digit of exponent.
0BF9 23 INX H
0BFA 71 NullTerm MOV M,C Null byte terminator.
0BFB E1 POP H
0BFC C9 RET

 

ToUnder1,000,000

Divides FACCUM by ten until it's less than 1,000,000. This function is semi-recursive... if it needs to recurse (ie

0BFD 017494 ToUnder1,000,000 LXI B,9474

BCDE=(float) 1,000,000

0C00 11F723 LXI D,23F7
0C03 CD4C0A CALL FCompare
0C06 E1 POP H
0C07 E2710B JPO 0B71
0C0A E9 PCHL

 

ONE_HALF

Constant value 0.5, used by FRoundUp

0C0B 00000080 ONE_HALF DD 0.5  

 

DECIMAL_POWERS

Table of powers of ten.

0C0F A08601 DECIMAL_POWERS DT 100000  
0C13 102700   DT 10000  
0C17 E80300   DT 1000  
0C1B 640000   DT 100  
0C1F 0A0000   DT 10  
0C1F 010000   DT 1  

 


[Index] [Previous] [Next]