home *** CD-ROM | disk | FTP | other *** search
- MASM51
- QUIRKS
- ;iprintf() - Integer version of C runtime library routine, printf()
- ;
- ;This routine is a fast, limited version of printf() that is suitable for
- ;most system utilities. It supports format specifications of the form:
- ;
- ; %[sign][width][L]type
- ;
- ;where sign is '+' or '-', width is an integer output field width, L is 'l' or
- ;'L' (signifying a long integer argument) and type is one of the following
- ;output types:
- ; b = unsigned binary integer
- ; c = character
- ; o = unsigned octal integer
- ; d = signed decimal integer
- ; s = ASCIIZ character string
- ; u = unsigned decimal integer
- ; x = unsigned hexadecimal integer
- ;
- ;Note: '+' is ignored.
- ; '-' means to left-justify the field.
- ;
- ;Brian B. McGuinness V1.1 March, 1990 Borland Turbo Assembler 1.01
- ;
- ;The double word unsigned integer division routine is based on a routine in the
- ;book PC & XT Assembly Language: A Guide for Programmers by Leo J. Scanlon,
- ;Robert J. Brady Co. (Bowie, MD: 1983).
- ;
- ;The code near the label "getnum" assumes that 'd' is the only format type that
- ;displays signed values.
- ;
- ;Note: this version was only tested for the small model.
-
- DOSSEG
- .MODEL small,C
-
- ;Get the sizes of various pointers for the current memory model.
-
- if @CodeSize ;Get the size, in bytes, of a jump address.
- ADDRSIZE = 4
- else
- ADDRSIZE = 2
- endif
-
- if @DataSize ;Get the size, in bytes, of a data pointer.
- PTRSIZE = 4
- else
- PTRSIZE = 2
- endif
-
- FIRSTARG = ADDRSIZE + 2 ;Offset of first argument from SP.
-
- .CODE
-
- ;Note: 'base' must be a word (16-bit) value.
-
- outdev dw 1 ;Handle to write output to
- longint? db 0 ;Nonzero if current argument is a long integer
- sign db ' ' ;'-' if the number is negative
- base dw 10 ;Radix to use when displaying a number
- outbuf$ db 130 dup (?) ;Output buffer
- outend label byte
- padchar db ' ' ;Character for padding fields: ' ' or '0'
- padlocn db 0 ;0 to pad at beginning (default), 1 to pad at end (-)
- iperror db 0 ;Nonzero if error occurred writing output.
-
- digit$ db '0123456789ABCDEF'
-
- iprintf proc, x:ptr ;Specify argument so start & end code is inserted.
-
- mov outdev,1 ;Write output to the standard output device.
-
- pushf
- push ax
- push bx
- push cx
- push dx
- push si
- push di
- push ds
- push es
-
- ;Initialize pointers. The first argument is a pointer to the format string.
-
- add bp,FIRSTARG ;Point BP to the first argument.
- mov si,[bp] ;Point SI to the format string.
- add bp,PTRSIZE ;Point BP to next argument in list.
-
- mov di,offset outbuf$ ;Point ES:DI to the output buffer.
- push cs
- pop es
-
- mov iperror,byte ptr 0 ;Clear the error flag.
-
- ;Process each character in the format string.
-
- nextch: lodsb
- or al,al ;Zero byte marks end of format string.
- jz @exit
- cmp al,'%' ;Check for '%' at beginning of format specification.
- jne @2
- cmp [si],byte ptr '%' ;'%%' means to print one '%'.
- je @1
-
- call prtarg ;Process format specification & print next argument.
- jmp short nextch
-
- @1: inc si ;(So we don't read the second '%' next time)
-
- @2: cmp al,10 ;If it's a linefeed (C language '\n'),
- jne @3 ;then print a carriage return before printing it.
- push ax
- mov al,13
- call output
- pop ax
- @3: call output ;Copy char to output buffer.
- jmp short nextch
-
- ;Dump remaining buffer contents, if any. Then restore registers and exit.
-
- @exit: call dumpbuf
-
- pop es
- pop ds
- pop di
- pop si
- pop dx
- pop cx
- pop bx
- pop ax
- popf
- ret
-
- iprintf endp
-
- ;-------------------------------------------------------------------------------
- ;Copy the char in AL to the output buffer. If the buffer is full, dump it.
-
- output proc
- cld
- stosb ;Write character to buffer.
- cmp di,offset outend ;Is buffer full?
- jne @exit
- call dumpbuf ;If so, dump the buffer.
- @exit:
- ret
- output endp
-
- ;-------------------------------------------------------------------------------
- ;Dump the contents of the output buffer to the proper output device.
-
- dumpbuf proc
- push cx ;Save padding width.
-
- cmp iperror,byte ptr 0 ;Check for previous error.
- jne @exit
-
- mov bx,outdev ;Get handle to write to.
- mov dx,offset outbuf$ ;Get address of output buffer.
- mov cx,di ;Get number of chars to write.
- sub cx,dx
- jz @exit
- push ds
- push es
- pop ds
- mov ah,40H ;Write the data.
- int 21H
- pop ds
- jc @error ;Check for error signalled by DOS error flag.
- cmp ax,cx ;Check if correct # bytes were writtten.
- je @reset
-
- @error: mov iperror,1 ;Flag the error.
-
- @reset: mov di,offset outbuf$ ;Reset the buffer pointer.
-
- @exit: pop cx
- ret
- dumpbuf endp
-
- ;-------------------------------------------------------------------------------
- ;Decode output specifications and perform the specified operations.
-
- prtarg proc
- mov longint?,0 ;0 for 16-bit integer, 1 for 32-bit integer
- mov sign,' ' ;'-' if number is negative
- mov base,10 ;radix to display value in
- mov padchar,' ' ;char used to pad field
- mov padlocn,0 ;0 = right-justify, 1 = left-justify
- mov cx,0 ;field width (0 if not specified)
-
- ;Read & decode format specification until we see one of 'odxcs\0'.
-
- nextch: lodsb
- or al,al ;Watch out for unexpected end of string.
- jnz @plus
- jmp @exit
-
- @plus: cmp al,'+' ;Ignore plus signs.
- je nextch
-
- @minus: cmp al,'-' ;Pad on the right (left-justify value)
- jne @width
- mov padlocn,1
- jmp short nextch
-
- @width: cmp al,'0' ;'0'-'9': Decode field width
- jb @1
- je @w2
- cmp al,'9'
- ja @1
-
- @w1: sub al,'0' ;CX <- CX * 10 + AL - '0'
- xor ah,ah
- xchg ax,cx
- mul base ;At this point, base = 10.
- add cx,ax
- jmp short nextch
-
- @w2: cmp cx,0 ;Leading zero --> pad with '0'
- ja @w1
- mov padchar,'0'
- jmp short nextch
-
- @1: or al,32 ;Assume it's a letter & force lower case.
-
- cmp al,'l' ;Check for 'l' (signals a long integer).
- jne @b
- mov longint?,1
- jmp short nextch
-
- @b: cmp al,'b' ;Check for 'b' (binary).
- jne @2
- mov base,2
- jmp getnum
-
- @2: cmp al,'o' ;Check for 'o' (octal).
- jne @3
- mov base,8
- jmp getnum
-
- @3: cmp al,'d' ;Check for 'd' (signed decimal).
- je getnum ;(base was initialized to 10)
-
- cmp al,'u' ;Check for 'u' (unsigned decimal).
- je getnum
-
- cmp al,'x' ;Check for 'x' (hexadecimal).
- je @b16
- jmp @c
- @b16: mov base,16
-
- getnum: mov bl,al ;Save output format type.
-
- cmp longint?,1 ;Is it a long (32-bit) integer?
- je @n1
- mov ax,[bp] ;No, it is a 16-bit integer.
- cwd ;Convert to long integer in DX:AX (extend sign).
- cmp bl,'d' ;If format isn't 'd', zero the high word.
- je @n0
- xor dx,dx
- @n0: add bp,2 ;Point BP to next argument in list.
- jmp short @n2
-
- @n1: mov ax,[bp] ;Get low word of long integer.
- mov dx,[bp+2] ;Get high word of long integer.
- add bp,4 ;Point BP to next argument in list.
-
- @n2: cmp bl,'d' ;For signed decimal, convert negative number
- jne @n3 ;to positive and store the sign.
- cmp dx,0 ;Is the number negative?
- jge @n3
- dec cx ;Decrement padding count to allow room for sign.
- mov sign,'-' ;Note that the number is negative.
- not dx ;Make the (2's complement) number positive.
- not ax
- add ax,1
- adc dx,0
-
- @n3: xor bx,bx ;Push a zero word to mark top of stack.
- push bx
-
- mov bx,ax ;Save low word for later use.
- @div0: mov ax,dx ;Divide high word: DX <- rem, AX <- quotient
- xor dx,dx
- div base
- xchg ax,bx ;Save quotient, retrieve low word.
- div base ;Divide remainder & low word.
- xchg dx,bx ;DX:AX <- full quotient, BX <- remainder.
- push word ptr digit$[BX];Convert remainder (digit) to char & store it.
- dec cx ;Decrement # pad characters needed (fld size).
- mov bx,ax ;If quotient is not zero, get next digit.
- or ax,dx
- jnz @div0
-
- cmp padchar,'0'
- je @n4
- cmp padlocn,0 ;Pad with ' ' at beginning, if needed.
- jne @n4
- call pad
- @n4: cmp sign,' ' ;Store minus sign, if needed.
- je @n5
- xchg al,sign
- call output
- @n5: cmp padchar,'0'
- jne @n6
- cmp padlocn,0 ;Pad with '0' at beginning, if needed.
- jne @n6
- call pad
-
- @n6: pop ax ;Pop (next) digit char.
- or al,al ;If it's zero, we're done.
- jz @exit
- call output ;Otherwise, write it and go back for next char.
- jmp short @n6
-
- @c: cmp al,'c' ;'C': Print one character.
- jne @s
- dec cx ;Allow 1 space for char.
- cmp padlocn,0 ;Pad at beginning, if needed.
- jne @c1
- call pad
- @c1: mov ax,[bp] ;Get char to be printed.
- add bp,2 ;Point BP to next argument in list.
- call output ;Print the char.
- jmp short @exit
-
- @s: cmp al,'s'
- jne @exit ;If unknown format spec, ignore it.
-
- push si ;Save current location in format string.
-
- if @DataSize ;Get pointer to string to be written.
- push ds
- lds si,[bp]
- else
- mov si,[bp]
- endif
- add bp,PTRSIZE ;Point BP to next argument in list.
-
- push si ;Get length of ASCIIZ string in BX.
- @s1: lodsb
- or al,al
- jnz @s1
- mov bx,si
- pop si
- sub bx,si
- dec bx
- sub cx,bx ;Calculate amount of padding required.
- cmp padlocn,0 ;Pad at beginning, if needed.
- jne @s2
- call pad
- @s2: lodsb ;Copy string to output, one char at a time.
- or al,al
- jz @s3
- call output
- jmp short @s2
- @s3:
-
- if @DataSize
- pop ds
- endif
- pop si ;Restore current location in format string.
-
- ;Pad on right side, if needed, and exit. If padding was already done on the
- ;left side, then CX was already decremented to zero by the "pad" routine and no
- ;more padding will be done.
-
- @exit:
- call pad
- ret
- prtarg endp
-
- ;-------------------------------------------------------------------------------
- ;Pad a field with CX occurrances of padchar.
-
- pad proc
- cmp cx,0
- jle @exit
- mov al,padchar
- @pad1: call output
- loop @pad1
- @exit:
- ret
- pad endp
- end
-