;* ======================================================================== *; ;* The routines and data in this file (prnum16.asm) are dedicated to the *; ;* public domain via the Creative Commons CC0 v1.0 license by its author, *; ;* Joseph Zbiciak. *; ;* *; ;* https://creativecommons.org/publicdomain/zero/1.0/ *; ;* ======================================================================== *; ;; ======================================================================== ;; ;; _PW10 ;; ;; Lookup table holding the first 5 powers of 10 (1 thru 10000) as ;; ;; 16-bit numbers. ;; ;; ======================================================================== ;; IF (DEFINED _PW10) = 0 _PW10 PROC ; 0 thru 10000 DECLE 10000, 1000, 100, 10, 1, 0 ENDP ENDI ;; ======================================================================== ;; ;; PRNUM16.l -- Print an unsigned 16-bit number left-justified. ;; ;; PRNUM16.b -- Print an unsigned 16-bit number with leading blanks. ;; ;; PRNUM16.z -- Print an unsigned 16-bit number with leading zeros. ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak ;; ;; ;; ;; REVISION HISTORY ;; ;; 30-Mar-2003 Initial complete revision ;; ;; ;; ;; INPUTS for all variants ;; ;; R0 Number to print. ;; ;; R2 Width of field. Ignored by PRNUM16.l. ;; ;; R3 Format word, added to digits to set the color. ;; ;; Note: Bit 15 MUST be cleared when building with PRNUM32. ;; ;; R4 Pointer to location on screen to print number ;; ;; ;; ;; OUTPUTS ;; ;; R0 Zeroed ;; ;; R1 Unmodified ;; ;; R2 Unmodified ;; ;; R3 Unmodified ;; ;; R4 Points to first character after field. ;; ;; ;; ;; DESCRIPTION ;; ;; These routines print unsigned 16-bit numbers in a field up to 5 ;; ;; positions wide. The number is printed either in left-justified ;; ;; or right-justified format. Right-justified numbers are padded ;; ;; with leading blanks or leading zeros. Left-justified numbers ;; ;; are not padded on the right. ;; ;; ;; ;; This code handles fields wider than 5 characters, padding with ;; ;; zeros or blanks as necessary. ;; ;; ;; ;; Routine Value(hex) Field Output ;; ;; ---------- ---------- ---------- ---------- ;; ;; PRNUM16.l $0045 n/a "69" ;; ;; PRNUM16.b $0045 4 " 69" ;; ;; PRNUM16.b $0045 6 " 69" ;; ;; PRNUM16.z $0045 4 "0069" ;; ;; PRNUM16.z $0045 6 "000069" ;; ;; ;; ;; TECHNIQUES ;; ;; This routine uses repeated subtraction to divide the number ;; ;; to display by various powers of 10. This is cheaper than a ;; ;; full divide, at least when the input number is large. It's ;; ;; also easier to get right. :-) ;; ;; ;; ;; The printing routine first pads out fields wider than 5 spaces ;; ;; with zeros or blanks as requested. It then scans the power-of-10 ;; ;; table looking for the first power of 10 that is <= the number to ;; ;; display. While scanning for this power of 10, it outputs leading ;; ;; blanks or zeros, if requested. This eliminates "leading digit" ;; ;; logic from the main digit loop. ;; ;; ;; ;; Once in the main digit loop, we discover the value of each digit ;; ;; by repeated subtraction. We build up our digit value while ;; ;; subtracting the power-of-10 repeatedly. We iterate until we go ;; ;; a step too far, and then we add back on power-of-10 to restore ;; ;; the remainder. ;; ;; ;; ;; NOTES ;; ;; The left-justified variant ignores field width. ;; ;; ;; ;; The code is fully reentrant. ;; ;; ;; ;; This code does not handle numbers which are too large to be ;; ;; displayed in the provided field. If the number is too large, ;; ;; non-digit characters will be displayed in the initial digit ;; ;; position. Also, the run time of this routine may get excessively ;; ;; large, depending on the magnitude of the overflow. ;; ;; ;; ;; When using with PRNUM32, one must either include PRNUM32 before ;; ;; this function, or define the symbol _WITH_PRNUM32. PRNUM32 ;; ;; needs a tiny bit of support from PRNUM16 to handle numbers in ;; ;; the range 65536...99999 correctly. ;; ;; ;; ;; CODESIZE ;; ;; 73 words, including power-of-10 table ;; ;; 80 words, if compiled with PRNUM32. ;; ;; ;; ;; To save code size, you can define the following symbols to omit ;; ;; some variants: ;; ;; ;; ;; _NO_PRNUM16.l: Disables PRNUM16.l. Saves 10 words ;; ;; _NO_PRNUM16.b: Disables PRNUM16.b. Saves 3 words. ;; ;; ;; ;; Defining both symbols saves 17 words total, because it omits ;; ;; some code shared by both routines. ;; ;; ;; ;; STACK USAGE ;; ;; This function uses up to 4 words of stack space. ;; ;; ======================================================================== ;; IF (DEFINED PRNUM16) = 0 PRNUM16 PROC ;; ---------------------------------------------------------------- ;; ;; PRNUM16.l: Print unsigned, left-justified. ;; ;; ---------------------------------------------------------------- ;; IF (DEFINED _NO_PRNUM16.l) = 0 @@l: PSHR R5 ; save return address @@l1: MVII #$1, R5 ; set R5 to 1 to counteract screen ptr update ; in the 'find initial power of 10' loop PSHR R2 MVII #5, R2 ; force effective field width to 5. B @@z2 ENDI ;; ---------------------------------------------------------------- ;; ;; PRNUM16.b: Print unsigned with leading blanks. ;; ;; ---------------------------------------------------------------- ;; IF (DEFINED _NO_PRNUM16.b) = 0 @@b: PSHR R5 @@b1: CLRR R5 ; let the blank loop do its thing INCR PC ; skip the PSHR R5 ENDI ;; ---------------------------------------------------------------- ;; ;; PRNUM16.z: Print unsigned with leading zeros. ;; ;; ---------------------------------------------------------------- ;; @@z: PSHR R5 @@z1: PSHR R2 @@z2: PSHR R1 ;; ---------------------------------------------------------------- ;; ;; Find the initial power of 10 to use for display. ;; ;; Note: For fields wider than 5, fill the extra spots above 5 ;; ;; with blanks or zeros as needed. ;; ;; ---------------------------------------------------------------- ;; MVII #_PW10+5,R1 ; Point to end of power-of-10 table SUBR R2, R1 ; Subtract the field width to get right power PSHR R3 ; save format word IF ((DEFINED _NO_PRNUM16.l) AND (DEFINED _NO_PRNUM16.b)) = 0 CMPI #2, R5 ; are we leading with zeros? BNC @@lblnk ; no: then do the loop w/ blanks IF (DEFINED _NO_PRNUM16.l) = 0 CLRR R5 ; force R5==0 ENDI ENDI ADDI #$80, R3 ; yes: do the loop with zeros B @@lblnk @@llp MVO@ R3, R4 ; print a blank/zero IF (DEFINED _NO_PRNUM16.l) = 0 SUBR R5, R4 ; rewind pointer if needed. ENDI INCR R1 ; get next power of 10 @@lblnk DECR R2 ; decrement available digits BEQ @@ldone CMPI #5, R2 ; field too wide? BGE @@llp ; just force blanks/zeros 'till we're narrower. CMP@ R1, R0 ; Is this power of 10 too big? BNC @@llp ; Yes: Put a blank and go to next @@ldone PULR R3 ; restore format word ;; ---------------------------------------------------------------- ;; ;; The digit loop prints at least one digit. It discovers digits ;; ;; by repeated subtraction. ;; ;; ---------------------------------------------------------------- ;; @@digit TSTR R0 ; If the number is zero, print zero and leave BNEQ @@dig1 ; no: print the number MOVR R3, R5 ;\ ADDI #$80, R5 ; |-- print a 0 there. MVO@ R5, R4 ;/ B @@done IF (DEFINED PRNUM32) OR (DEFINED _WITH_PRNUM32) @@dig1: MOVR R3, R5 ; for first digit only: check to see if BPL @@cont ; we need to add '5'. ADDI #$80+4*8,R5 ; start our digit as one just before '5' B @@spcl ELSE @@dig1: ENDI @@nxdig MOVR R3, R5 ; save display format word @@cont: ADDI #$80-8, R5 ; start our digit as one just before '0' @@spcl: ;; ---------------------------------------------------------------- ;; ;; Divide by repeated subtraction. This divide is constructed ;; ;; to go "one step too far" and then back up. ;; ;; ---------------------------------------------------------------- ;; @@div: ADDI #8, R5 ; increment our digit SUB@ R1, R0 ; subtract power of 10 BC @@div ; loop until we go too far ADD@ R1, R0 ; add back the extra power of 10. MVO@ R5, R4 ; display the digit. INCR R1 ; point to next power of 10 DECR R2 ; any room left in field? BPL @@nxdig ; keep going until R2 < 0. @@done: PULR R1 ; restore R1 PULR R2 ; restore R2 PULR PC ; return ENDP ENDI ;; ======================================================================== ;; ;; End of File: prnum16.asm ;; ;; ======================================================================== ;;