home *** CD-ROM | disk | FTP | other *** search
/ ftp.shrubbery.net / 2015-02-07.ftp.shrubbery.net.tar / ftp.shrubbery.net / pub / pc / unix / unx.arc / SPRINTF.ASM < prev    next >
Assembly Source File  |  1985-09-16  |  11KB  |  518 lines

  1. ;---- sprintf.asm -------------------------------------------
  2. ; An implementation of sprintf in 8086 assembly.
  3. ; Calling convention:
  4. ;    call near sprintf(s, format {,item {,item ...}})
  5. ;    word char *s, *format;
  6. ; Prints the items to the specified string.
  7. ; Arguments are pushed from right to left before the call; upon
  8. ; return, it is the caller's duty to clean the parameters off the stack.
  9. ; All registers used, except BP.
  10. ; All pointers are DS-relative except s, which is ES-relative.
  11. ; Format string items:
  12. ;  \n -> cr lf, \r -> cr
  13. ;  \t -> tab, \f -> form feed, \O -> null, \\ -> \
  14. ;  \number -> anychar  (Number is octal if leading char is zero)
  15. ;  %% -> %
  16. ;  %c -> word item's low byte is a char to print
  17. ;  %d -> word item is to be printed as decimal number
  18. ;  %x -> word item is to be printed as hex number (no leading 0x, though)
  19. ;  %s -> word item is a pointer to a null-terminated string
  20. ;  %nnc, where nn is a field width, and c is d,s, or x ->
  21. ;         nn<0: left justified; nn>0: right justified
  22. ;  %hd, %hx -> short word item (default)
  23. ;  %ld, %lx -> long word item; high order word pushed first.
  24. ; Unrecognized % or \ sequences act as if the % or \ was not given.
  25. ;---------------------------------------------------------------
  26.  
  27.     PUBLIC    sprintf
  28.  
  29. code segment para public 'CODE'
  30. assume cs:code
  31.  
  32. ; main routine variables
  33. lpc    db    ?        ; kludge - left padding character
  34.  
  35. ; i_to_a variables
  36. i2a_fw    dw    ?
  37. i2a_buf    dw    ?
  38. i2a_lpc    db    ?        ; left padding char- either ' ' or '0'
  39.  
  40.  
  41. sprintf    proc    near
  42.  
  43.     push    bp
  44.     mov    bp,sp
  45.     mov    si,4        ; offset of first argument from bp
  46.     cld            ; strings increment
  47.  
  48.     ; di = s; 
  49.     mov    di, [bp+si]    ; di gets target string pointer (ES relative)
  50.     add    si, 2
  51.     ; bx = format;
  52.     mov    bx, [bp+si]    ; bx gets format string pointer (DS relative)
  53.     add    si, 2        ; bump si to first vararg
  54.  
  55.     ; while (al = *bx++) {
  56. spf_while:
  57.     mov    al, byte ptr [bx]
  58.     inc    bx
  59.     or    al, al
  60.     jnz    spfbody
  61.     jmp    spfdone
  62. spfbody:
  63.         ; if (al == '\') {
  64.         cmp    al, '\'
  65.         jnz    not_backsl
  66.             ;-------- BEGIN BACKSLASH --------
  67.             ; al = *bx++;
  68.             mov    al, byte ptr [bx]
  69.             inc    bx
  70.             or    al, al
  71.             jnz    bs_nnuke
  72.                 jmp    spfdone
  73. bs_nnuke:
  74.             ; if (al=='\')
  75.             cmp    al, '\'
  76.             jz    storem
  77.             ; else if (al=='f')
  78.             cmp    al, 'f'
  79.             jnz    bs_nf
  80.                 mov    al, 12
  81.                 jmp    short storem
  82. bs_nf:            ; else if (al=='n')
  83.             cmp    al, 'n'
  84.             jnz    bs_nn
  85.                 mov    al, 13
  86.                 stosb
  87.                 mov    al, 10
  88.                 jmp    short storem
  89. bs_nn:            ; else if (al=='O')
  90.             cmp    al, 'O'
  91.             jnz    bs_nO             
  92.                 mov    al, 0
  93.                 jmp    short storem
  94. bs_nO:            ; else if (al=='r')
  95.             cmp    al, 'r'
  96.             jnz    bs_nr
  97.                 mov    al, 13
  98.                 jmp    short storem
  99. bs_nr:            ; else if (al=='t')
  100.             cmp    al, 't'
  101.             jnz    bs_nt
  102.                 mov    al, 9
  103.                 jmp    short storem
  104. bs_nt:            ; else if (isdigit(al)) {
  105.             cmp    al, '0'
  106.             jb    storem
  107.             cmp    al, '9'
  108.             ja    storem
  109.                 dec    bx    ; point to numeric str
  110.                 call    a_to_i    ; result to ax
  111. storem:            stosb
  112.             jmp    spfwend
  113.             ;----------- END BACKSLASH -----------
  114. not_backsl:    ; } else if (al != '%')
  115.         cmp    al, '%'
  116.         jnz    storem            ; ---- NORMAL CHARS ----
  117.         ; else {     /* al == '%' */
  118.             ;----------- BEGIN PERCENT ----------
  119.             ; al = *bx++;
  120.             mov    al, byte ptr [bx]
  121.             inc    bx
  122.             or    al, al
  123.             jz    spfdgate
  124.             ; if (al == '%')
  125.             cmp    al, '%'
  126.             jz    storem
  127.  
  128.             ; fieldwidth = DEFAULT;
  129.             mov    cx, 08000h
  130.             ; if (al == '-' || isdigit(al)) {
  131.             cmp    al, '-'
  132.             jz    pc_npc
  133.             cmp    al, '9'
  134.             ja    pc_nfw
  135.             cmp    al, '0'
  136.             jb    pc_nfw
  137.                 ; if (al == '0') leftpadchar = '0' else ' '
  138.                 mov    lpc, ' '
  139.                 jnz    pc_npc
  140.                     mov    lpc, '0'
  141.                     inc    bx    ; skip zero
  142. pc_npc:                
  143.                 ; fieldwidth = a_to_i(&bx);
  144.                 dec    bx
  145.                 call    a_to_i
  146.                 mov    cx, ax
  147.                 ; if (!(al=*bx++)) break;
  148.                 mov    al, [bx]
  149.                 inc    bx
  150.                 or    al, al
  151.                 jnz    pc_nfw
  152. spfdgate:            jmp    spfdone
  153.  
  154. pc_nfw:
  155.             ; At this point, CX contains the fieldwidth, or
  156.             ; 8000h for default, and AL contains next character.
  157.  
  158.             ; size = short
  159.             mov    ah, 0
  160.             ; if (al == 'l')
  161.             cmp    al, 'l'
  162.             jnz    pc_nh
  163.                 ; size = long
  164.                 mov    ah, 1
  165.                 mov    al, [bx]
  166.                 inc    bx
  167.                 or    al, al
  168.                 jz    spfdgate
  169.                 jmp    short pc_nh    ; don't allow l AND h
  170. pc_nl:
  171.  
  172.             ; if (al == 'h')
  173.             cmp    al, 'h'
  174.             jnz    pc_nh
  175.                 ; default is short; just throw away 'h'
  176.                 mov    al, [bx]
  177.                 inc    bx
  178.                 or    al, al
  179.                 jz    spfdgate
  180. pc_nh:
  181.             ; radix = 10
  182.             mov    dx, 10
  183.             ; if (al == 'd')
  184.             cmp    al, 'd'
  185.             jz    got_integer
  186.             cmp    al, 'x'
  187.             jnz    pc_ni
  188.                 ; radix = 16
  189.                 mov    dx, 16
  190. got_integer:
  191.                 push    bx        ; save format ptr
  192.                 mov    bx, dx        ; set radix
  193.                 ; if (size == long)
  194.                 cmp    ah, 1
  195.                 mov    ax, [bp+si]    ; get ax=item
  196.                 mov    dx, 0        ; high word zero
  197.                 jnz    pc_short
  198.                     add    si,2
  199.                     mov    dx,[bp+si]
  200. pc_short:
  201.                 add    si, 2
  202.                 mov    bh, lpc        ; set left pad char
  203.                 call    i_to_a    
  204.                 pop    bx
  205.                 jmp    spfwend
  206. pc_ni:            ; if (al == 'c')
  207.             cmp    al, 'c'
  208.             jnz    pc_nc
  209.                 ; Jest a char.
  210.                 mov    ax, [bp+si]
  211.                 add    si, 2
  212.                 jmp    storem
  213. pc_nc:            ; if (al == 's')
  214.             cmp    al, 's'
  215.             jnz    pc_ns
  216.                 mov    ax, [bp+si]
  217.                 add    si, 2
  218.                 push    si
  219.                 mov    si, ax
  220.                 call    doprint
  221.                 pop    si
  222.                 jmp    spfwend
  223. pc_ns:            ; bad format char
  224.             stosb
  225. spfwend:    jmp    spf_while
  226.  
  227. spfdone:
  228.     mov    al, 0
  229.     stosb
  230.     pop    bp
  231.  
  232.     ret
  233.  
  234. sprintf    endp
  235.  
  236.  
  237.  
  238. ;----- doprint -----------------------------------------------
  239. ; Input string pointer in DS:SI, output string pointer in ES:SI,
  240. ; field width in CX.
  241. ; Preserves BX, uses all others.
  242.  
  243. doprint    proc    near
  244.     push    bx
  245.     mov    bx, cx
  246.  
  247.     ; find length
  248.     push    si
  249.     mov    cx, 0
  250.     mov    al, 0
  251. lflp:        lodsb
  252.         or    al, al
  253.         jz    lfex
  254.         inc    cx
  255.         jmp    lflp
  256. lfex:    pop    si                
  257.  
  258.     ; Pad or truncate?
  259.     cmp    bx, 8000h
  260.     jz    dop_xfer    ; neither.
  261.     cmp    bx, cx
  262.     jz    dop_xfer
  263.     cmp    bx, 0
  264.     jl    dop_right
  265.  
  266.     ; Field width > 0; pad or truncate on left.
  267.         sub    bx, cx    ; bx = field width - string length
  268.         jl    dop_tl    ; truncate on left
  269.         ; pad on left
  270.         push    cx
  271.         mov    cx, bx
  272.         mov    al, ' '
  273.         rep    stosb
  274.         pop    cx
  275.         jmp    short dop_xfer
  276. dop_tl:        ; Truncate; bx is negative dist to truncate.
  277. ; only if dot specified
  278. ;        sub    si, bx    ; add truncate size to start
  279. ;        add    cx, bx    ; and sub from length
  280.         jmp    short dop_xfer
  281.  
  282. dop_right:
  283.     ; Field width < 0; pad or truncate on right.
  284.         neg    bx
  285.         sub    bx, cx
  286.         jl    dop_tr    ; truncate on right
  287.         ; pad on right.
  288.         rep    movsb    ; do the copy
  289.         mov    cx, bx
  290.         mov    al, ' '
  291.         rep    stosb
  292.         jmp    short dop_done
  293. dop_tr:        ; truncate on right; bx is negative
  294. ; only if dot specified
  295. ;        add    cx, bx
  296. dop_xfer:
  297.     rep    movsb
  298. dop_done:
  299.     pop    bx
  300.     ret
  301. doprint    endp
  302.  
  303.  
  304. ;---- i_to_a -------------------------------------------------------------
  305. ; Input integer in DX:AX; output radix in BL; output buffer pointer in ES:DI,
  306. ; Field width in CX, left padding char in BH.
  307. ; Preserves SI, BP.
  308. ; Does not terminate string with a null; lets caller do it by
  309. ;  mov al, 0
  310. ;  stosb
  311. ; if he likes.
  312.  
  313. i_to_a    proc    near
  314.     push    si
  315.     push    bp
  316.  
  317.     mov    i2a_fw, cx
  318.     mov    i2a_buf, di
  319.     mov    i2a_lpc, bh    ; save padding char
  320.     mov    bh, 0        ; make radix a word
  321.     call    dw_stackem    ; convert DX:AX to BCD radix BX on stack
  322.                 ; returns number of digits in CX.
  323.     mov    bx, i2a_fw
  324.     mov    di, i2a_buf
  325.  
  326.     ; Check out the padding situation
  327.     ; Pad on left if field width > 0
  328.     cmp    bx, 8000h
  329.     jz    ttprint
  330.     cmp    bx, 0
  331.     jle    ttprint
  332.         ; If digit string length < field width, pad it.
  333.         mov    bp, bx
  334.         sub    bp, cx
  335.         jle    ttprint
  336.         mov    al, i2a_lpc
  337.         xchg    cx, bp
  338.         rep    stosb
  339.         xchg    cx, bp
  340. ttprint:
  341.     mov    bp, cx        ; save string length
  342.     ; loop2: print them
  343. ttd2:    pop    ax
  344.     cmp    al, 10
  345.     jb    tt_decimal
  346.     add    al, 7
  347. tt_decimal:
  348.     add    al, '0'
  349.     stosb
  350.     loop    ttd2
  351.  
  352.     ; Check out the padding situation on other side
  353.     cmp    bx, 8000h    ; if (field width == DEFAULT)
  354.     jz    ttdone        ; don't pad
  355.     ; Pad on right if field width >0
  356.     neg    bx
  357.     jle    ttdone
  358.         ; If digit string length < field width, pad it.
  359.         sub    bx, bp
  360.         jle    ttdone
  361.         mov    al, ' '
  362.         mov    cx, bx
  363.         rep    stosb
  364. ttdone:
  365.     pop    bp
  366.     pop    si        ; restore vararg
  367.     ret
  368.  
  369. i_to_a    endp
  370.  
  371. ;----- a_to_i -------------------------------------------------
  372. ; Given bx pointing to the leading digit of a numeric string, or a
  373. ; minus sign, returns the value of that field in AX.
  374. ; Returns BX pointing to first non-numeric char.
  375. ; Preserves SI and DI, changes all others.
  376.  
  377. a_to_i    proc    near
  378.     push    si
  379.     push    [bx]
  380.     cmp    byte ptr [bx], '-'
  381.     jnz    at_notneg
  382.         inc    bx
  383. at_notneg:
  384.     ; radix = leading_zero ? 8 : 10;
  385.     mov    si, 10
  386.     cmp    al, '0'
  387.     jnz    at_dec
  388.     mov    si, 8
  389. at_dec:    ; fieldwidth = 0
  390.     mov    cx, 0
  391. at_lp:    ; while ((al=*bx) && isdigit(al))
  392.     mov    al, [bx]
  393.     or    al, al
  394.     jz    at_ex    
  395.     cmp    al, '0'
  396.     jb    at_ex
  397.     cmp    al, '9'
  398.     ja    at_ex
  399.         ; fw = fw*radix + al-'0';
  400.         sub    al, '0'
  401.         mov    ah, 0
  402.         xchg    ax, cx
  403.         mul    si
  404.         add    cx, ax
  405.         ; bx++;
  406.         inc    bx
  407.         jmp    at_lp
  408. at_ex:
  409.     xchg    ax, cx    ; get value to ax
  410.     pop    cx    ; get first char agin
  411.     cmp    cl, '-'
  412.     jnz    at_dontneg
  413.         neg    ax
  414. at_dontneg:
  415.     pop    si
  416.     ret
  417. a_to_i    endp
  418.  
  419. ;------ dw_stackem ------------------------------------------------
  420. ; Converts a longword to an unpacked BCD string on the stack.
  421. ; Input: longword in DX:AX, output radix in BX (must be 10 or 16)
  422. ; Output: cx=number of digits, TOS=leftmost digit...LOS=rightmost
  423. ; Uses all registers except DI, including BP.
  424. ; Returns radix unchanged in BX.
  425.  
  426. dw_stackem    proc    near
  427.  
  428.     pop    bp        ; return address to BP
  429.  
  430.     xor    cx, cx
  431.     cmp    bx, 10
  432.     jz    dws_dec
  433.         ; Begin stacking hexadecimal digits.
  434. dws_hl:        mov    si, 0        ; clear remainder
  435.         
  436.         shr    dx,1        ; divide by 2
  437.         rcr    ax,1
  438.         rcr    si,1
  439.         shr    dx,1        ; divide by 2
  440.         rcr    ax,1
  441.         rcr    si,1
  442.         shr    dx,1        ; divide by 2
  443.         rcr    ax,1
  444.         rcr    si,1
  445.         shr    dx,1        ; divide by 2
  446.         rcr    ax,1
  447.         rcr    si,1
  448.  
  449.         push    cx
  450.         mov    cl,4
  451.         rol    si,cl
  452.         pop    cx        ; ech
  453.  
  454.         push    si        ; push remainder of division
  455.         inc    cx
  456.         or    ax,ax
  457.         jnz    dws_hl
  458.         jmp    bp
  459. dws_dec:
  460.  
  461.     ; Divide the number by 100000; stack remainder, then quotient.
  462.     ; The trick is to divide by 2, then by 50000.
  463.     ; The overall remainder is (x & 1) + (x/2 % 50000)*2.
  464.     shr    dx, 1        ; high word
  465.     rcr    ax, 1        ; low word; remainder in C.
  466.     pushf
  467.     mov    bx, 50000
  468.     div    bx        ; unsigned divide; remainder to dx.
  469.     mov    si, ax        ; quotient to si for later use.
  470.     mov    ax, dx        ; ax is now low word of remainder
  471.     mov    dx, 0
  472.     add    ax, ax        ; dx:ax = (x/2 % 50000) * 2
  473.     adc    dx, 0        ; carry to high word
  474.     popf
  475.     adc    ax, 0        ; dx:ax += (x & 1)
  476.     adc    dx, 0        ; carry to high word
  477.  
  478.     mov    bx, 10
  479.  
  480.     ;------ SI = N/100000; BX = radix; DX:AX = N % 100000 ---------------
  481.     ; Stack digits, starting with the low digit of the remainder.
  482.     ; Blank leading zeroes.
  483.     ; Count digits produced in CX.
  484.  
  485.     ; loop1: stack BCD digits of remainder
  486.     ; If quotient zero, suppress leading zeroes.
  487.     mov    cx, 0
  488. dtd1:    div    bx
  489.     push    dx        ; push remainder of division
  490.     xor    dx, dx        ; clear remainder
  491.     inc    cx
  492.     or    ax, ax        ; If any digits left, not done.
  493.     jnz    dtd1
  494.     or    si, si        ; else if Quotient from above zero, done.
  495.     jz    dtd1_x
  496.     cmp    cx, 5        ; else if < 5 digits printed, not done.
  497.     jnz    dtd1
  498. dtd1_x:
  499.     ; loop2: stack BCD digits of quotient
  500.     mov    ax, si
  501.     or    ax, ax
  502.     jz    d2ddone
  503.  
  504. dtd2:    xor    dx,dx
  505.     div    bx
  506.     push    dx        ; push remainder of division
  507.     inc    cx
  508.     or    ax,ax
  509.     jnz    dtd2
  510. d2ddone:
  511.     jmp    bp        ; return
  512.  
  513. dw_stackem    endp
  514.  
  515. code    ends
  516.  
  517.     end
  518.