Functions for printing floating-point numbers.
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 |
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 |
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 |
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 |
Constant value 0.5, used by FRoundUp
0C0B | 00000080 | ONE_HALF | DD 0.5 |
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 |