home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-386-Vol-2of3.iso / b / bm_rm.zip / IPRINTF.ASM < prev    next >
Assembly Source File  |  1990-04-10  |  12KB  |  388 lines

  1. MASM51
  2. QUIRKS
  3. ;iprintf() - Integer version of C runtime library routine, printf()
  4. ;
  5. ;This routine is a fast, limited version of printf() that is suitable for
  6. ;most system utilities.  It supports format specifications of the form:
  7. ;
  8. ;  %[sign][width][L]type
  9. ;
  10. ;where sign is '+' or '-', width is an integer output field width, L is 'l' or 
  11. ;'L' (signifying a long integer argument) and type is one of the following 
  12. ;output types:
  13. ;               b = unsigned binary integer
  14. ;               c = character
  15. ;               o = unsigned octal integer
  16. ;               d = signed decimal integer
  17. ;               s = ASCIIZ character string
  18. ;               u = unsigned decimal integer
  19. ;               x = unsigned hexadecimal integer
  20. ;
  21. ;Note: '+' is ignored.
  22. ;      '-' means to left-justify the field.
  23. ;
  24. ;Brian B. McGuinness     V1.1     March, 1990     Borland Turbo Assembler 1.01
  25. ;
  26. ;The double word unsigned integer division routine is based on a routine in the
  27. ;book PC & XT Assembly Language: A Guide for Programmers by Leo J. Scanlon,
  28. ;Robert J. Brady Co. (Bowie, MD: 1983).
  29. ;
  30. ;The code near the label "getnum" assumes that 'd' is the only format type that
  31. ;displays signed values.
  32. ;
  33. ;Note: this version was only tested for the small model.
  34.  
  35.         DOSSEG
  36.         .MODEL small,C
  37.  
  38. ;Get the sizes of various pointers for the current memory model.
  39.  
  40. if @CodeSize              ;Get the size, in bytes, of a jump address.
  41.         ADDRSIZE = 4
  42. else
  43.         ADDRSIZE = 2
  44. endif
  45.  
  46. if @DataSize              ;Get the size, in bytes, of a data pointer.
  47.         PTRSIZE = 4
  48. else
  49.         PTRSIZE = 2
  50. endif
  51.  
  52.         FIRSTARG = ADDRSIZE + 2         ;Offset of first argument from SP.
  53.  
  54.         .CODE
  55.  
  56. ;Note: 'base' must be a word (16-bit) value.
  57.  
  58. outdev    dw 1            ;Handle to write output to 
  59. longint?  db 0            ;Nonzero if current argument is a long integer
  60. sign      db ' '          ;'-' if the number is negative
  61. base      dw 10           ;Radix to use when displaying a number
  62. outbuf$   db 130 dup (?)  ;Output buffer
  63. outend    label byte
  64. padchar   db ' '          ;Character for padding fields: ' ' or '0'
  65. padlocn   db 0            ;0 to pad at beginning (default), 1 to pad at end (-)
  66. iperror   db 0            ;Nonzero if error occurred writing output.
  67.  
  68. digit$    db '0123456789ABCDEF'
  69.  
  70. iprintf proc, x:ptr       ;Specify argument so start & end code is inserted.
  71.  
  72.         mov outdev,1      ;Write output to the standard output device.
  73.  
  74.         pushf
  75.         push ax
  76.         push bx
  77.         push cx
  78.         push dx
  79.         push si
  80.         push di
  81.         push ds
  82.         push es
  83.  
  84. ;Initialize pointers.  The first argument is a pointer to the format string.
  85.  
  86.         add bp,FIRSTARG         ;Point BP to the first argument.
  87.         mov si,[bp]             ;Point SI to the format string.
  88.         add bp,PTRSIZE          ;Point BP to next argument in list.
  89.  
  90.         mov di,offset outbuf$   ;Point ES:DI to the output buffer.
  91.         push cs
  92.         pop es
  93.  
  94.         mov iperror,byte ptr 0  ;Clear the error flag.
  95.  
  96. ;Process each character in the format string.
  97.  
  98. nextch: lodsb
  99.         or al,al        ;Zero byte marks end of format string.
  100.         jz @exit
  101.         cmp al,'%'      ;Check for '%' at beginning of format specification.
  102.         jne @2
  103.         cmp [si],byte ptr '%'   ;'%%' means to print one '%'.
  104.         je @1
  105.  
  106.         call prtarg     ;Process format specification & print next argument.
  107.         jmp short nextch
  108.  
  109. @1:     inc si          ;(So we don't read the second '%' next time)
  110.  
  111. @2:     cmp al,10       ;If it's a linefeed (C language '\n'), 
  112.         jne @3          ;then print a carriage return before printing it.
  113.         push ax
  114.         mov al,13
  115.         call output
  116.         pop ax
  117. @3:     call output     ;Copy char to output buffer.
  118.         jmp short nextch
  119.  
  120. ;Dump remaining buffer contents, if any.  Then restore registers and exit.
  121.  
  122. @exit:  call dumpbuf
  123.  
  124.         pop es
  125.         pop ds
  126.         pop di
  127.         pop si
  128.         pop dx
  129.         pop cx
  130.         pop bx
  131.         pop ax
  132.         popf
  133.         ret
  134.  
  135. iprintf endp
  136.  
  137. ;-------------------------------------------------------------------------------
  138. ;Copy the char in AL to the output buffer.  If the buffer is full, dump it.
  139.  
  140. output  proc
  141.         cld
  142.         stosb                   ;Write character to buffer.
  143.         cmp di,offset outend    ;Is buffer full?
  144.         jne @exit
  145.         call dumpbuf            ;If so, dump the buffer.
  146. @exit:
  147.         ret
  148. output endp
  149.  
  150. ;-------------------------------------------------------------------------------
  151. ;Dump the contents of the output buffer to the proper output device.
  152.  
  153. dumpbuf proc
  154.         push cx                 ;Save padding width.
  155.  
  156.         cmp iperror,byte ptr 0  ;Check for previous error.
  157.         jne @exit
  158.  
  159.         mov bx,outdev           ;Get handle to write to.
  160.         mov dx,offset outbuf$   ;Get address of output buffer.
  161.         mov cx,di               ;Get number of chars to write.
  162.         sub cx,dx
  163.         jz @exit
  164.         push ds
  165.         push es
  166.         pop ds
  167.         mov ah,40H              ;Write the data.
  168.         int 21H
  169.         pop ds
  170.         jc @error               ;Check for error signalled by DOS error flag.
  171.         cmp ax,cx               ;Check if correct # bytes were writtten.
  172.         je @reset
  173.  
  174. @error: mov iperror,1           ;Flag the error.
  175.  
  176. @reset: mov di,offset outbuf$   ;Reset the buffer pointer.
  177.  
  178. @exit:  pop cx
  179.         ret
  180. dumpbuf endp
  181.  
  182. ;-------------------------------------------------------------------------------
  183. ;Decode output specifications and perform the specified operations.
  184.  
  185. prtarg  proc
  186.         mov longint?,0          ;0 for 16-bit integer, 1 for 32-bit integer
  187.         mov sign,' '            ;'-' if number is negative
  188.         mov base,10             ;radix to display value in
  189.         mov padchar,' '         ;char used to pad field
  190.         mov padlocn,0           ;0 = right-justify, 1 = left-justify
  191.         mov cx,0                ;field width (0 if not specified)
  192.  
  193. ;Read & decode format specification until we see one of 'odxcs\0'.
  194.  
  195. nextch: lodsb
  196.         or al,al                ;Watch out for unexpected end of string.
  197.         jnz @plus
  198.         jmp @exit
  199.  
  200. @plus:  cmp al,'+'              ;Ignore plus signs.
  201.         je nextch
  202.  
  203. @minus: cmp al,'-'              ;Pad on the right (left-justify value)
  204.         jne @width
  205.         mov padlocn,1
  206.         jmp short nextch
  207.  
  208. @width: cmp al,'0'              ;'0'-'9': Decode field width
  209.         jb @1
  210.         je @w2
  211.         cmp al,'9'
  212.         ja @1
  213.  
  214. @w1:    sub al,'0'              ;CX <- CX * 10 + AL - '0'
  215.         xor ah,ah
  216.         xchg ax,cx
  217.         mul base                ;At this point, base = 10.
  218.         add cx,ax
  219.         jmp short nextch
  220.  
  221. @w2:    cmp cx,0                ;Leading zero --> pad with '0'
  222.         ja @w1
  223.         mov padchar,'0'
  224.         jmp short nextch
  225.  
  226. @1:     or al,32                ;Assume it's a letter & force lower case.
  227.  
  228.         cmp al,'l'              ;Check for 'l' (signals a long integer).
  229.         jne @b
  230.         mov longint?,1
  231.         jmp short nextch
  232.  
  233. @b:     cmp al,'b'              ;Check for 'b' (binary).
  234.         jne @2
  235.         mov base,2
  236.         jmp getnum
  237.  
  238. @2:     cmp al,'o'              ;Check for 'o' (octal).
  239.         jne @3
  240.         mov base,8
  241.         jmp getnum
  242.  
  243. @3:     cmp al,'d'              ;Check for 'd' (signed decimal).
  244.         je getnum               ;(base was initialized to 10)
  245.  
  246.         cmp al,'u'              ;Check for 'u' (unsigned decimal).
  247.         je getnum
  248.  
  249.         cmp al,'x'              ;Check for 'x' (hexadecimal).
  250.         je @b16
  251.         jmp @c
  252. @b16:   mov base,16
  253.  
  254. getnum: mov bl,al               ;Save output format type.
  255.  
  256.         cmp longint?,1          ;Is it a long (32-bit) integer?
  257.         je @n1
  258.         mov ax,[bp]             ;No, it is a 16-bit integer.
  259.         cwd                     ;Convert to long integer in DX:AX (extend sign).
  260.         cmp bl,'d'              ;If format isn't 'd', zero the high word.
  261.         je @n0
  262.         xor dx,dx
  263. @n0:    add bp,2                ;Point BP to next argument in list.
  264.         jmp short @n2
  265.  
  266. @n1:    mov ax,[bp]             ;Get low word of long integer.
  267.         mov dx,[bp+2]           ;Get high word of long integer.
  268.         add bp,4                ;Point BP to next argument in list.
  269.  
  270. @n2:    cmp bl,'d'              ;For signed decimal, convert negative number
  271.         jne @n3                 ;to positive and store the sign.
  272.         cmp dx,0                ;Is the number negative?
  273.         jge @n3
  274.         dec cx                  ;Decrement padding count to allow room for sign.
  275.         mov sign,'-'            ;Note that the number is negative.
  276.         not dx                  ;Make the (2's complement) number positive.
  277.         not ax
  278.         add ax,1
  279.         adc dx,0
  280.  
  281. @n3:    xor bx,bx               ;Push a zero word to mark top of stack.
  282.         push bx
  283.  
  284.         mov bx,ax               ;Save low word for later use.
  285. @div0:  mov ax,dx               ;Divide high word: DX <- rem, AX <- quotient
  286.         xor dx,dx
  287.         div base
  288.         xchg ax,bx              ;Save quotient, retrieve low word.
  289.         div base                ;Divide remainder & low word.
  290.         xchg dx,bx              ;DX:AX <- full quotient, BX <- remainder.
  291.         push word ptr digit$[BX];Convert remainder (digit) to char & store it.
  292.         dec cx                  ;Decrement # pad characters needed (fld size).
  293.         mov bx,ax               ;If quotient is not zero, get next digit.
  294.         or ax,dx
  295.         jnz @div0
  296.  
  297.         cmp padchar,'0'
  298.         je @n4
  299.         cmp padlocn,0           ;Pad with ' ' at beginning, if needed.
  300.         jne @n4
  301.         call pad
  302. @n4:    cmp sign,' '            ;Store minus sign, if needed.
  303.         je @n5
  304.         xchg al,sign
  305.         call output
  306. @n5:    cmp padchar,'0'
  307.         jne @n6
  308.         cmp padlocn,0           ;Pad with '0' at beginning, if needed.
  309.         jne @n6
  310.         call pad
  311.  
  312. @n6:    pop ax                  ;Pop (next) digit char.
  313.         or al,al                ;If it's zero, we're done.
  314.         jz @exit
  315.         call output             ;Otherwise, write it and go back for next char.
  316.         jmp short @n6
  317.  
  318. @c:     cmp al,'c'              ;'C': Print one character.
  319.         jne @s
  320.         dec cx                  ;Allow 1 space for char.
  321.         cmp padlocn,0           ;Pad at beginning, if needed.
  322.         jne @c1
  323.         call pad
  324. @c1:    mov ax,[bp]             ;Get char to be printed.
  325.         add bp,2                ;Point BP to next argument in list.
  326.         call output             ;Print the char.
  327.         jmp short @exit
  328.  
  329. @s:     cmp al,'s'
  330.         jne @exit               ;If unknown format spec, ignore it.
  331.  
  332.         push si                 ;Save current location in format string.
  333.  
  334. if @DataSize                    ;Get pointer to string to be written.
  335.         push ds
  336.         lds si,[bp]
  337. else
  338.         mov si,[bp]
  339. endif
  340.         add bp,PTRSIZE          ;Point BP to next argument in list.
  341.  
  342.         push si                 ;Get length of ASCIIZ string in BX.
  343. @s1:    lodsb
  344.         or al,al
  345.         jnz @s1
  346.         mov bx,si
  347.         pop si
  348.         sub bx,si
  349.         dec bx
  350.         sub cx,bx               ;Calculate amount of padding required.
  351.         cmp padlocn,0           ;Pad at beginning, if needed.
  352.         jne @s2
  353.         call pad
  354. @s2:    lodsb                   ;Copy string to output, one char at a time.
  355.         or al,al
  356.         jz @s3
  357.         call output
  358.         jmp short @s2
  359. @s3:
  360.  
  361. if @DataSize
  362.         pop ds
  363. endif
  364.         pop si                  ;Restore current location in format string.
  365.  
  366. ;Pad on right side, if needed, and exit.  If padding was already done on the 
  367. ;left side, then CX was already decremented to zero by the "pad" routine and no 
  368. ;more padding will be done.
  369.  
  370. @exit:
  371.         call pad
  372.         ret
  373. prtarg  endp
  374.  
  375. ;-------------------------------------------------------------------------------
  376. ;Pad a field with CX occurrances of padchar.
  377.  
  378. pad     proc
  379.         cmp cx,0
  380.         jle @exit
  381.         mov al,padchar
  382. @pad1:  call output
  383.         loop @pad1
  384. @exit:
  385.         ret
  386. pad     endp
  387.         end
  388.