; PRDEC32.ASM -- Include-able library function file ; Mach32_DecASC: 32-bit unsigned machine integer to ASCII decimal conversion ; K. Heidenstrom (kheidens@actrix.gen.nz) ; Modified: ; ; KH.940729.001 Started ; KH.940730.002 First fully-defined working version, renamed all labels ; KH.941114.003 Version for posting on the net in comp.lang.asm.x86 ; Notes ; ; This function converts a 32-bit unsigned machine longword in DX|AX to ASCII ; decimal representation. It is similar to the itoa() function in C. ; ; Output is controlled by two parameters specified in CX on entry. ; ; CL contains the number of digits desired in the output. This may be any ; value, from 0 to 255, but is normally in the range 1 to 10. ; ; CH contains the padder character for right-justified numbers. If this ; register is set to zero, no padding is used, and only the digits necessary ; to represent the number are output. If CH is not zero, then it is taken ; as an ASCII character to be used to pad the number on the left, to the width ; specified in CL. In this case, a space or a "0" are normally used in CH. ; ; If the number of digits specified in CL is insufficient to contain the value ; represented by DX|AX, then all columns are output as "#" characters (this is ; controlled by the PrDecOvrflowChr equate below). In this case, the routine ; will return with carry set. It will also return with carry set if CL was ; zero (i.e. no digits at all). ; ; The subroutine's stack space requirement is sixteen bytes, or twelve bytes ; plus the stack space required by the output function, whichever is greater. ; ; The output method must be specified at assembly time, by defining a ; PrDec_Output macro. This macro will be assembled into the part of the ; subroutine where the character output is required. The requirements for ; the character output macro are as follows: ; ; On entry, AL contains the character to be displayed or output. ; ; AX DX BP DI These MAY be modified by the macro code. ; BX CX SI These MUST NOT be modified by the macro code. ; DI DS ES These are not used at all by the Mach32_DecASC subroutine. ; ; This last group of registers are not used or changed by the subroutine code, ; and may be set up before entering the subroutine, then used by the output ; macro, and will be returned by the subroutine. This is intended to support ; use of STOSB as the output function macro. DI can be set up before entry to ; the subroutine, and the output function macro can contain a STOSB, and on ; return from the subroutine, DI will point past the last stored character. ; Note that the subroutine does not use, or modify, the direction flag. ; ; The output function macro is executed in left-to-right sequence as the ; digits of the output value are calculated. ; ; The total length of the PrDec_Output macro must be limited to 33 bytes (at ; the moment - this may change), in order to avoid a bad branch error at the ; end of the subroutine. ; ; Example PrDec_Output macros: ; ; MACRO PrDec_Output ; stosb ; Add digit to string being generated ; ENDM ; ; MACRO PrDec_Output ; push bx ; Preserve ; xor bx,bx ; Output to first display page ; mov ah,0Eh ; TTY output ; int 10h ; Output to BIOS ; pop bx ; Restore ; ENDM ; ; MACRO PrDec_Output ; mov dl,al ; Digit to DL ; mov ah,2 ; Char output ; int 21h ; Output to DOS ; ENDM PrDecOvrflowChr = "#" ; Char to display on overflow PROC Mach32_DecASC near ; Func: Convert unsigned long machine integer ; to ASCII decimal representation ; In: DX|AX = Value to be converted ; CL = Number of digits to display ; (normally in range 1 - 10 but all ; values are handled correctly) ; CH = Padder (typically " " or "0", or ; 0 for no leading padding) ; Out: Characters are output according to the ; PrDec_Output macro which must be defined ; before this file is included. ; CF = Overflow status: ; NC = Alright, no overflow ; CY = Overflow occurred ; Lost: Flags (DF is not affected) ; DI, DS, and ES may be modified by the ; output function macro. ; Note: Input value is treated as an unsigned ; longword, in the range 0 - 4294967295. ; Note: If the value won't fit into the number ; of digits specified, all digits are ; output as '#' symbols, indicating ; overflow, and carry is set on return. push si push bp push dx push cx push bx push ax ; Preserve registers test cl,cl jz PrDec_Error ; If no digits specified xchg ax,bx ; Loword to BX mov si,dx ; Hiword to SI ; Check digit position is within range PrDec_DigitLp: cmp cl,11 ; Check that we're at a valid digit jae PrDec_DoPad ; If not yet, just do space padding mov dx,1 ; Init loword of divisor to 1 sub cl,dl ; Get digit position jb PrDec_Exit ; If no digits, or just done last digit ; Calculate divisor in BP|DX as 10 ^ digit position number push cx ; Keep misc info in CL and CH xor bp,bp ; Hiword of divisor to zero PrDec_DivLoop: sub cl,1 ; Count down the shifts jb PrDec_GotDiv ; If done, have divisor push bp ; Push hiword mov ax,dx ; Keep loword shl dx,1 ; Multiply by two rcl bp,1 ; Carry into hiword shl dx,1 ; Multiply by two again rcl bp,1 ; Carry into hiword add dx,ax ; Now add original value to value x 4 pop ax ; Restore old hiword adc bp,ax ; Add hiwords too shl dx,1 ; Now have value x 5, double it again rcl bp,1 ; And hiword jmp SHORT PrDec_DivLoop ; Continue PrDec_GotDiv: pop cx ; Have divisor, restore CX ; Calculate digit by repeatedly dividing value in SI|BX by divisor in BP|DX xor ax,ax ; Count of divides PrDec_CalcLoop: inc ax ; Increment counter cmp al,10 ; Make sure digit is within range ja PrDec_Overflow ; If not, display overflow indication sub bx,dx ; Subtract loword of divisor from value sbb si,bp ; Borrow from hiword jnb PrDec_CalcLoop ; Borrowed below zero yet? If not, loop add bx,dx ; Restore remainder loword adc si,bp ; Restore hiword too add al,"0"-1 ; Calculate digit value in ASCII ; Check value of digit cmp al,"0" ; Is digit zero? jne PrDec_SetZero ; If not test cl,cl ; Final position? jz PrDec_GotDigit ; Force printing final zero PrDec_DoPad: mov al,ch ; If digit is zero, substitute padder jmp SHORT PrDec_GotDigit ; Continue PrDec_Overflow: mov si,-1 ; Force overflow indication from now on mov al,PrDecOvrflowChr ; Indicate overflow PrDec_SetZero: mov ch,"0" ; Had non-zero - no more padding now! PrDec_GotDigit: test al,al jz PrDec_DigitLp ; If zero, and no padding requested ; Output character in AL using appropriate output mode PrDec_Output jmp SHORT PrDec_DigitLp ; Loop for more digits PrDec_Exit: test si,si jz PrDec_Exit2 ; If didn't have overflow, exit no carry PrDec_Error: stc ; If overflow occurred, exit carry set PrDec_Exit2: pop ax pop bx pop cx pop dx pop bp pop si ; Restore registers ret ENDP Mach32_DecASC