;* ======================================================================== *; ;* The routines and data in this file (prnum32.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/ *; ;* ======================================================================== *; IF (DEFINED PRNUM16) AND ((DEFINED _WITH_PRNUM32)) = 0 ERR "Must INCLUDE PRNUM16 after PRNUM32." ENDI ;; ======================================================================== ;; ;; PRNUM32.l -- Print an unsigned 32-bit number left-justified. ;; ;; PRNUM32.b -- Print an unsigned 32-bit number with leading blanks. ;; ;; PRNUM32.z -- Print an unsigned 32-bit number with leading zeros. ;; ;; ;; ;; AUTHOR ;; ;; Joseph Zbiciak ;; ;; ;; ;; REVISION HISTORY ;; ;; 30-Mar-2003 Initial complete revision ;; ;; ;; ;; INPUTS for all variants ;; ;; R0 Lower 16 bits of number to print ;; ;; R1 Upper 16 bits of number to print ;; ;; R2 Width of field. Must be >= 5. Ignored by PRNUM32.l. ;; ;; R3 Format word, added to digits to set the color, etc. ;; ;; Note: Bit 15 is ignored and manipulated internally. ;; ;; R4 Pointer to location on screen to print number ;; ;; R5 Return address ;; ;; ;; ;; OUTPUTS ;; ;; R0 Zeroed ;; ;; R1 Trashed ;; ;; R2 Unmodified ;; ;; R3 Unmodified, except for bit 15, which may be set. ;; ;; R4 Points to first character after field. ;; ;; R5 Trashed ;; ;; ;; ;; DESCRIPTION ;; ;; These routines print unsigned 32-bit numbers in a field 5 to 10 ;; ;; 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. ;; ;; ;; ;; See PRNUM16 for a table illustrating output formats. ;; ;; ;; ;; TECHNIQUES ;; ;; This routine uses a 32-by-16 integer divide to cut the 32-bit ;; ;; number into two 5-digit segments. It divides the initial number ;; ;; by 100000. The quotient provides the upper half, and the ;; ;; remainder provides the lower half. ;; ;; ;; ;; Both halves are printed by reusing the PRNUM16 routines. The ;; ;; lower half is always printed using PRNUM16.z. The upper half is ;; ;; printed using one of PRNUM16.l, PRNUM16.b, or PRNUM16.z, based on ;; ;; which entry point the caller used for invoking PRNUM32. ;; ;; ;; ;; The number in the lower half can be in the range 00000...99999. ;; ;; Some of this range is outside the range of a 16-bit value. We ;; ;; handle this by passing a special flag to PRNUM16 in bit 15 of ;; ;; the format word. When set, PRNUM16 will add '5' to the leading ;; ;; digit of the number. When clear, it leaves it unmodified. ;; ;; Thus, when our lower half is in the range 65536...99999, we cope ;; ;; by adding 15536 to the lower 16 bits and setting the "add 5" flag. ;; ;; This works, because the lower 16 bits are in the range 0 - 34463. ;; ;; After the 'tweak', they're in the range 15536 - 49999. ;; ;; ;; ;; NOTES ;; ;; The left-justified variant ignores the field width. ;; ;; ;; ;; This code is fully reentrant, although it has a significant stack ;; ;; footprint. ;; ;; ;; ;; 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. ;; ;; ;; ;; This code requires PRNUM16. Since PRNUM16 requires a minor tweak ;; ;; to support PRNUM32, it must either be included after PRNUM32, or ;; ;; assembled with the symbol "_WITH_PRNUM32" set. ;; ;; ;; ;; CODESIZE ;; ;; 87 words (not including PRNUM16.) ;; ;; ;; ;; To save code size, you can define the following symbols to omit ;; ;; some variants: ;; ;; ;; ;; _NO_PRNUM32.l: Disables PRNUM32.l. Saves 10 words. ;; ;; _NO_PRNUM32.b: Disables PRNUM32.b. Saves 10 words. ;; ;; ;; ;; Defining both symbols saves 18 words total, because it omits ;; ;; some code shared by both routines. ;; ;; ;; ;; STACK USAGE ;; ;; This function uses up to 8 words of stack space. That includes ;; ;; 4 words of stack space consumed by PRNUM16 when printing the ;; ;; two halves of the number. ;; ;; ======================================================================== ;; IF (DEFINED PRNUM32) = 0 PRNUM32 PROC IF (DEFINED _NO_PRNUM32.l) = 0 ;; ---------------------------------------------------------------- ;; ;; PRNUM32.l: Print unsigned, left-justified. ;; ;; ---------------------------------------------------------------- ;; @@l: ANDI #$7FFF, R3 ; force bit 15 of R3 clear. TSTR R1 BEQ PRNUM16.l PSHR R5 MVII #PRNUM16.l, R5 B @@com ENDI IF (DEFINED _NO_PRNUM32.b) = 0 ;; ---------------------------------------------------------------- ;; ;; PRNUM32.b: Print unsigned with leading blanks. ;; ;; ---------------------------------------------------------------- ;; @@b: ANDI #$7FFF, R3 ; force bit 15 of R3 clear. TSTR R1 BEQ PRNUM16.b PSHR R5 MVII #PRNUM16.b, R5 B @@com ENDI ;; ---------------------------------------------------------------- ;; ;; PRNUM32.z: Print unsigned with leading zeros. ;; ;; ---------------------------------------------------------------- ;; @@z: ANDI #$7FFF, R3 ; force bit 15 of R3 clear. TSTR R1 BEQ PRNUM16.z PSHR R5 IF NOT ((DEFINED _NO_PRNUM32.l) AND (DEFINED _NO_PRNUM32.b)) MVII #PRNUM16.z, R5 ENDI @@com PSHR R2 ; save our field width PSHR R3 ; save our format word ;; ---------------------------------------------------------------- ;; ;; Divide the 32-bit number by 100000, and remember the remainder. ;; ;; This will give us two 5-digit numbers for the two halves. ;; ;; The upper half will always be in the range 0 to 42949, and so ;; ;; we can print it with one of the PRNUM16 routines. The lower ;; ;; half is in the range 0...99999, so we have to do some fancy ;; ;; footwork in the case that it's above 65535. ;; ;; ;; ;; The loop below looks like it divides by 50000, but really we've ;; ;; aligned the divisor within a 16-bit number. ;; ;; ---------------------------------------------------------------- ;; MVII #50000, R2 ; 100000, right shifted by 1. MVII #1, R3 ; Divide is done when this bit falls out of R3 B @@div1 ; jump to the first divide step. @@div: RLC R0, 1 ;\__ 32 bit left shift of numerator. RLC R1, 1 ;/ BC @@divf ; last shift overflowed, force a subtract @@div1: CMPR R2, R1 ; can we subtract from upper half? BNC @@divn ; nope, then don't subtract. @@divf: SUBR R2, R1 ; subtract upper half. SETC ; usu. the SUBR sets carry, except when forced @@divn: RLC R3, 1 ; left-shift the quotient, including new bit BNC @@div ; loop until quotient is full. ;; ---------------------------------------------------------------- ;; ;; At this point, R1:R0 is our remainder, and R3 is our quotient. ;; ;; The remainder occupies the upper 17 bits of the R1:R0 pair. ;; ;; For now, hide this on the stack. We do this dance carefully ;; ;; since we need to restore two items from the stack as well, ;; ;; and we don't want to lose the values in R4 or R5 either. ;; ;; ;; ;; If there was no "upper half" (eg. our value is 65536...99999) ;; ;; go handle that case specially. Urgl. ;; ;; ---------------------------------------------------------------- ;; SLLC R0, 1 ; put 17th bit into carry MOVR R3, R0 ; save quotient into R0 PULR R3 ; recall our format word into R3 PULR R2 ; recall our field width RLC R1, 1 ; R1 is bits 15 downto 0 of remainder SLL R3, 1 ; \_ Force bit 15 of format word to 1 or 0. RRC R3, 1 ; / This will add 5 to leading digit. TSTR R3 ; BPL @@lt64k ; if there was no carry, rmdr is 0...65535 ADDI #15536, R1 ; add "15536" to rest of digits @@lt64k: TSTR R0 BEQ @@notop ; convert bottom into top if top = 0. PSHR R2 ; save field width on stack PSHR R3 ; save format word for bottom half PSHR R1 ; save quotient for bottom half ;; ---------------------------------------------------------------- ;; ;; Go ahead and print the first 5 digits of the result. ;; ;; ---------------------------------------------------------------- ;; SUBI #5, R2 ; bottom accounts for 5 digits of field width ANDI #$7FFF, R3 ; force bit 15 of R3 clear. IF (DEFINED _NO_PRNUM32.l) AND (DEFINED _NO_PRNUM32.b) CALL PRNUM16.z ELSE MOVR R5, R1 ; R5 contains branch target. MVII #@@ret, R5 ; remember return address. MOVR R1, PC ; call one of PRNUM16.z, .b, or .l as needed @@ret: ENDI ;; ---------------------------------------------------------------- ;; ;; Print the bottom half of the number. We branch into the ;; ;; heart of PRNUM16, which effectively always leads with zeros. ;; ;; ---------------------------------------------------------------- ;; MVII #4, R2 ; bottom is always exactly 5 digits. PULR R0 ; get bits 15 downto 0 of remainder PULR R3 ; get format word. INCR SP ; save garbage in R1 slot on stack MVII #_PW10, R1 ; need to set up power-of-10 pointer first B PRNUM16.dig1 ; chain return via PRNUM16.digit ;; ---------------------------------------------------------------- ;; ;; If there was no "upper half", convert the "lower half" into an ;; ;; upper half. This actually isn't too gross. ;; ;; ---------------------------------------------------------------- ;; @@notop: MOVR R1, R0 ; convert bottom half to top half IF (DEFINED _NO_PRNUM32.l) AND (DEFINED _NO_PRNUM32.b) PULR R5 B PRNUM16.z ELSE MOVR R5, R1 ; \ PULR R5 ; |__ chain return via target PRNUM16 routine JR R1 ; / ENDI ENDP ENDI ;; ======================================================================== ;; ;; End of File: prnum32.asm ;; ;; ======================================================================== ;;