home *** CD-ROM | disk | FTP | other *** search
/ CP/M / CPM_CDROM.iso / cpm / debug / wadesrc.lbr / PRINTF.AZM / PRINTF.ASM
Assembly Source File  |  1988-06-19  |  17KB  |  933 lines

  1.     title    'PRINTF: Formatted output routine'
  2. ;
  3. ;    Last edited    85-03-17    Wagner
  4. ;
  5. ;
  6. ;    Used for messages inside BIOS, but also useful for user 
  7. ;    written assembly programs (set "bios" to false).
  8. ;    Use is similar to "C"-printf, but watch out for 
  9. ;    pitfalls (deref, see below).
  10. ;
  11. ;
  12. ;    Copyright (c) 1984, 1985 by
  13. ;
  14. ;        Thomas Wagner
  15. ;        Patschkauer Weg 31
  16. ;        1000 Berlin 33
  17. ;        West Germany
  18. ;
  19. ;       Released to the public domain 1987
  20. ;
  21. ;
  22. ;------------------------------------------------------------------------------
  23. ;
  24. ;    ?prntf:        formatted console output.
  25. ;    ?lprnt:        formatted printer output (for BIOS/DIRECT: console)
  26. ;    ?sprnt:        formatted string output.
  27. ;
  28. ;        entry:    Parameter list placed after call as follows:
  29. ;
  30. ;            CALL    ?prntf / ?lprnt / ?sprnt
  31. ;            JMP    xxx        ; jump around parameters
  32. ;    ?sprnt only:    DW    destination string address
  33. ;            DW    conversion string address
  34. ;            DW    param 1
  35. ;            ...
  36. ;
  37. ;            Use macro printf for generation (see "PRINTF.LIB").
  38. ;            The instruction after the call must be 3 bytes long 
  39. ;            (no relative jump allowed).
  40. ;
  41. ;            The ?sprnt entry will insert the string at the
  42. ;            specified destination address, where the first two
  43. ;            bytes will receive the resulting string length
  44. ;            (not counting the terminating zero byte).
  45. ;            This string length may be nonzero upon entry, and then
  46. ;            specifies the offset to the destination address
  47. ;            where the first character is to place.
  48. ;
  49. ;        exit:    -
  50. ;
  51. ;        uses:    -  (up to 20 bytes of stack space)
  52. ;
  53. ;    Printf Macro call:
  54. ;
  55. ;        printf    'Contents of addr %w: %@wH -> %@@s\n',<adr,adr,adr>
  56. ;        sprintf    dest,'Dest is located at %w$',dest
  57. ;
  58. ;    Conversion specification:
  59. ;
  60. ;        '%' <deref ...> <fieldwidth> conversion
  61. ;       or    '\' specialchar
  62. ;
  63. ;    Deref:
  64. ;        @ = dereferencing
  65. ;        + = indexing
  66. ;        # = indexed dereferencing
  67. ;        in any combination.
  68. ;
  69. ;        No deref or index: immediate value (meaningless for string).
  70. ;
  71. ;        Deref: parameter is the address of the value.
  72. ;
  73. ;        Index: the next parameter is added to the value
  74. ;        after any dereferencing of the index value. Indexing implies
  75. ;        that the obtained sum is the address of the value.
  76. ;
  77. ;        Indexed deref (only meaningful after index):
  78. ;        the pointer obtained after indexing is dereferenced.
  79. ;        
  80. ;    For use in the fill-conversion, the special character '&' may be
  81. ;    used to designate the current output position:
  82. ;
  83. ;        %&10f*    means: subtract the current position counter
  84. ;            from 10, output '*' the resulting number of times.
  85. ;
  86. ;    The character '&' may not be combined with dereferencing. 
  87. ;
  88. ;    Example:
  89. ;
  90. ;        ORG    1234h
  91. ;
  92. ;    adr1:    DW    byt1    ; 1234h
  93. ;    adr2:    DW    byt2    ; 1236h
  94. ;    adr3:    DW    byt3    ; 1238h
  95. ;
  96. ;    adr4:    DW    adr1    ; 123Ah
  97. ;
  98. ;    byt1:    DB    1    ; 123Ch
  99. ;    byt2:    DB    2
  100. ;    byt3:    DB    3
  101. ;        DB    4
  102. ;        DB    5
  103. ;
  104. ;    idx:    DW    2
  105. ;
  106. ;        printf    '%w\n',adr1        -> 1234 addr of adr1
  107. ;        printf    '%@w\n',adr1        -> 123C content of adr1
  108. ;        printf    '%@@x\n\n',adr1        -> 01   cont of cont of adr1
  109. ;
  110. ;        printf    '%+x\n',<byt1,1>    -> 02    contents of (byt1 + 1)
  111. ;        printf    '%+@x\n',<byt1,idx>    -> 03    byt1 + @idx = byt1 + 2
  112. ;        printf    '%@+@x\n',<adr1,idx>    -> 03    @adr1=byt1 + @idx
  113. ;        printf    '%@+@+x\n\n',<adr1,idx,1> -> 04 @adr1=byt1 + @idx + 1
  114. ;
  115. ;        printf    '%+#x\n',<adr1,4>    -> 03    @(adr1 + 4)=@adr3=byt3
  116. ;        printf    '%+@#x\n',<adr1,idx>    -> 02    @(adr1 + @idx) = @adr2
  117. ;                            = byt2
  118. ;        printf    '%@+@#+x\n',<adr4,idx,3> -> 05  @(@adr4 + @idx) + 3
  119. ;                            = @(adr1 + @idx) + 3
  120. ;                            = @adr2 + 3 = byt2 + 3
  121. ;
  122. ;
  123. ;    Conversion:
  124. ;
  125. ;        %x - hex output (byte or byte string).
  126. ;        %w - hex output (word or multiple words).
  127. ;        %b - decimal output (byte, unsigned).
  128. ;        %d - decimal output (word, signed).
  129. ;        %u - decimal output (word, unsigned).
  130. ;        %s - string output (must be zero-terminated except if length
  131. ;             is known and specified as fieldlength).
  132. ;        %t - string output, first byte is string length.
  133. ;        %v - string output, next character in conversion string
  134. ;             specifies termination character.
  135. ;        %c - character output.
  136. ;        %a - character output, next character in conversion string
  137. ;             is added to value before output.
  138. ;        %f - fill, next character in conversion string is output,
  139. ;             value specifies number of times to output char (<= 255)
  140. ;
  141. ;        any other char: error ('?' is printed).
  142. ;
  143. ;    Field width (maximum is 255):
  144. ;
  145. ;        hex:     Number of hex digits printed (no space filling).
  146. ;            If an odd number is used, only the upper nibble of
  147. ;            the last byte is printed.
  148. ;            Note that every 2 bytes are reversed for %w.
  149. ;            Default is 2 for %x, 4 for %w, 0 means default.
  150. ;
  151. ;        dec:    Result is right adjusted in the field.
  152. ;            All significant digits will be printed if field width
  153. ;            is smaller than necessary.
  154. ;            Default is 0.
  155. ;
  156. ;        string:    Output is left adjusted.
  157. ;            The string will be truncated if the field width is
  158. ;            smaller than the string length.
  159. ;            Default is printing up to a zero byte for %s or
  160. ;            up to the count for %t, 0 means default.
  161. ;            %t-output will be terminated on a zero byte, too.
  162. ;            For %v, the fieldlength specifies the maximum length
  163. ;            of the string, no padding will take place.
  164. ;            %v-output will not terminate on a zero byte.
  165. ;
  166. ;        char:    Character is right adjusted.
  167. ;            Default is 1, 0 means default.
  168. ;
  169. ;        fill:    If specified, the value is subtracted from the
  170. ;            fieldwidth. If omitted or zero, the value is used
  171. ;            as is. If the value is larger than a nonzero 
  172. ;            fieldwidth, nothing will be output.
  173. ;
  174. ;    Special characters:
  175. ;
  176. ;        \r    carriage return
  177. ;        \n    carriage return + line feed
  178. ;        \l    line feed
  179. ;        \h    backspace
  180. ;        \g    bell
  181. ;        \%    %
  182. ;        \\    \
  183. ;        \dd    with dd = one or two hex digits: value of dd
  184. ;
  185. ;        \&    Reset position counter to zero, no output
  186. ;
  187. ;        any other char: error ('?' is printed).
  188. ;
  189. ;    Parameters:
  190. ;
  191. ;        One parameter must be specified for each %-conversion and
  192. ;        index except for %&, else meaningless values will be output.
  193. ;
  194. ;    Code Example:
  195. ;
  196. ;        CALL    ?prntf
  197. ;        JMP    xxx
  198. ;        DW    string
  199. ;        DW    dstr
  200. ;        DW    dstr
  201. ;    string    DB    'String at %wH: "%@s"\n',0
  202. ;
  203. ;    dstr    DB    'Hello'
  204. ;
  205. ;    Example for %v-string output to print a filename without spaces
  206. ;    from an fcb and fill to a total length of 20 bytes:
  207. ;
  208. ;        printf    '%@c:%@8v .%@3v %&20f ',<fcb,fcb+1,fcb+9>
  209. ;
  210. ;------------------------------------------------------------------------------
  211. ;
  212.     public    ?prntf
  213.     public    ?lprnt
  214.     public    ?sprnt
  215. ;
  216. ;
  217. true    equ    -1
  218. false    equ    not true
  219. ;
  220. direct    equ    false        ; set true for direct port i/o
  221. bios    equ    false        ; set to false for use outside of BIOS
  222. mpm    equ    false        ; set true for use in MP/M XIOS
  223. ;
  224. ;
  225.     IF    direct
  226.     maclib    ports
  227.     ENDIF
  228. ;
  229.     maclib    z80
  230. ;
  231.     IF    bios AND NOT mpm
  232.     dseg            ; in banked memory if used in BIOS
  233.     ELSE
  234.     cseg            ; code space if MPM or not for use in BIOS
  235.     ENDIF
  236. ;
  237. ;------------------------------------------------------------------------------
  238. ;
  239. ?sprnt:
  240.     xthl            ; get retaddr, save hl
  241.     push    h        ; push back retaddr
  242.     push    psw
  243.     push    b
  244.     push    d
  245.     mvi    a,0ffh
  246.     sta    sprint        ; mark string destination
  247.     inx    h        ; second byte of JMP
  248.     inx    h        ; third
  249.     inx    h        ; start of parameter list
  250.     mov    e,m        ; destination string address
  251.     inx    h
  252.     mov    d,m
  253.     inx    h
  254.     xchg            ; HL points to destination string.
  255.     shld    strlen        ; store as length pointer
  256.     mov    c,m
  257.     inx    h
  258.     mov    b,m        ; current length
  259.     inx    h        ; point after length
  260.     dad    b        ; add current length to dest addr
  261.     mov    a,c
  262.     sta    position    ; use lower byte as position
  263.     shld    strptr        ; store as pointer
  264.     xchg            ; get pointer to string into hl again
  265.     jr    prtcom        ; and continue in common part
  266. ;
  267. ?lprnt:
  268.     push    psw
  269.     mvi    a,5        ; list output
  270.     jr    plcom
  271. ;
  272. ?prntf:
  273.     push    psw
  274.     mvi    a,2        ; console out
  275. plcom:
  276.     sta    sprint        ; set output direction
  277.     pop    psw
  278.     xthl            ; get retaddr, save hl
  279.     push    h        ; push back retaddr
  280.     push    psw
  281.     push    b
  282.     push    d
  283.     inx    h        ; second byte of JMP
  284.     inx    h        ; third
  285.     inx    h        ; start of parameter list
  286.     xra    a
  287.     sta    position    ; clear position counter
  288. prtcom:
  289.     mov    e,m        ; string address
  290.     inx    h
  291.     mov    d,m
  292.     inx    h
  293.     xchg            ; DE now contains first parameter address
  294.                 ; HL points to string.
  295. ;
  296. main:
  297.     mov    a,m        ; get string char
  298.     inx    h
  299.     ora    a        ; end of string ?
  300.     jrz    return        ; ready if zero byte.
  301.     cpi    '%'        ; conversion marker ?
  302.     jrz    convert        ; then go format
  303.     cpi    '\'        ; special char ?
  304.     jrz    special        ; then go process
  305. normal:
  306.     call    output        ; else output the character
  307.     jr    main
  308. ;
  309. return:
  310.     pop    d
  311.     pop    b
  312.     pop    psw
  313.     pop    h
  314.     xthl
  315.     ret
  316. ;
  317. ;
  318. special:
  319.     mov    a,m
  320.     inx    h
  321.     call    tolower
  322.     cpi    'r'
  323.     jrz    ocr
  324.     cpi    'l'
  325.     jrz    olf
  326.     cpi    'n'
  327.     jrz    onl
  328.     cpi    't'
  329.     jrz    otb
  330.     cpi    'b'
  331.     jrz    obs
  332.     cpi    'h'
  333.     jrz    obs
  334.     cpi    '&'
  335.     jrz    resposn
  336.     cpi    'g'
  337.     jrnz    sphex
  338.     mvi    a,7
  339.     jr    normal
  340. ;
  341. resposn:
  342.     xra    a
  343.     sta    position
  344.     jr    main
  345. ;
  346. olf:
  347.     mvi    a,0ah
  348.     jr    normal
  349. onl:
  350.     mvi    a,0ah
  351.     call    output
  352. ocr:
  353.     mvi    a,0dh
  354.     jr    normal
  355. obs:
  356.     mvi    a,08h
  357.     jr    normal
  358. otb:
  359.     mvi    a,09h
  360.     jr    normal
  361. ;
  362. sphex:
  363.     call    aschex
  364.     jrc    error        ; skip field if unknown char
  365. sphex0:
  366.     mov    c,a
  367.     mov    a,m
  368.     call    aschex
  369.     jrnc    sphex1
  370.     mov    a,c
  371.     jr    normal        ; if one hex digit only
  372. sphex1:
  373.     inx    h
  374.     mov    b,a
  375.     mov    a,c
  376.     rlc
  377.     rlc
  378.     rlc
  379.     rlc
  380.     ora    b        ; two hex digits
  381.     jr    normal        ; go output
  382. ;
  383. error:
  384.     dcx    h        ; go back one char
  385.     mvi    a,'?'
  386.     jr    normal        ; go output '?'
  387. ;
  388. ;
  389. convert:
  390.     pushix            ; save ix
  391.     push    h
  392.     popix            ; string pointer into ix
  393.     push    d        ; save parameter pointer
  394.     xchg            ; and put into hl
  395.     lxi    b,0
  396.     mvi    a,0ffh
  397.     sta    idxcnt
  398. ;
  399. conref:
  400.     ldx    a,0        ; get conversion spec
  401.     inxix
  402.     cpi    '&'        ; position ref ?
  403.     jrz    refposn
  404.     cpi    '@'        ; deref ?
  405.     jrz    ref
  406.     cpi    '#'        ; indexed deref ?
  407.     jrnz    conv1        ; no deref if not
  408.     dad    b        ; add accumulated value and use as pointer
  409.     lxi    b,0
  410. ref:
  411.     mov    e,m        ; get value of parameter
  412.     inx    h
  413.     mov    d,m        ; into de
  414.     xchg            ; and use as pointer
  415.     jr    conref        ; loop
  416. ;
  417. conv1:
  418.     cpi    '+'        ; indexed ?
  419.     jrnz    conv2
  420.     push    h
  421.     lxi    h,idxcnt
  422.     inr    m
  423.     pop    h
  424.     jrnz    conv11        ; skip implied deref if not first time
  425.     mov    e,m        ; else get value pointed to by hl
  426.     inx    h
  427.     mov    d,m
  428.     xchg
  429. conv11:
  430.     dad    b        ; add in last value
  431.     mov    b,h
  432.     mov    c,l        ; into bc
  433.     pop    h        ; get param pointer
  434.     inx    h
  435.     inx    h
  436.     mov    e,m
  437.     inx    h        ; get next param
  438.     mov    d,m        ; value into de
  439.     dcx    h
  440.     push    h        ; save updated param pointer
  441.     xchg            ; param value into de
  442.     jr    conref        ; go deref
  443. ;
  444. refposn:
  445.     pop    h        ; param pointer
  446.     dcx    h        ; decrease to compensate for increase at end
  447.     dcx    h
  448.     push    h
  449.     lxi    h,position
  450.     jr    conref
  451. ;
  452. conv2:
  453.     dad    b        ; add value
  454.     xchg            ; pointer into de
  455.     pushix
  456.     pop    h        ; string pointer into hl
  457.     mvi    c,0        ; default fieldwidth
  458. conv3:
  459.     cpi    '0'        ; digit ?
  460.     jrc    nodig
  461.     cpi    '9'+1
  462.     jrnc    nodig
  463.     sui    '0'
  464.     mov    b,a        ; save
  465.     mov    a,c
  466.     add    a        ; * 2
  467.     add    a        ; * 4
  468.     add    c        ; * 5
  469.     add    a        ; * 10
  470.     add    b        ; + value
  471.     mov    c,a        ; into c
  472.     mov    a,m        ; get next char
  473.     inx    h
  474.     jr    conv3
  475. nodig:
  476.     push    h        ; save string addr
  477.     call    tolower        ; lower case letters only, convert
  478.     lxi    h,convtab
  479.     mvi    b,cvtblen
  480. fndconv:
  481.     cmp    m
  482.     inx    h
  483.     jrz    cfound
  484.     inx    h
  485.     inx    h
  486.     djnz    fndconv
  487. ;
  488.     pop    h
  489.     pop    d        ; unchanged param pointer
  490.     popix
  491.     jmp    error        ; unknown conversion
  492. ;
  493. cfound:
  494.     mov    a,m
  495.     inx    h
  496.     mov    h,m
  497.     mov    l,a
  498.     mov    b,c        ; b = c = fieldwidth
  499.     mov    a,c
  500.     ora    a        ; set fieldwidth condition code
  501.     call    ipchl        ; enter routine
  502. ;
  503. convex:
  504.     pop    h        ; restore string address
  505.     pop    d
  506.     inx    d
  507.     inx    d        ; point to next parameter
  508.     popix            ; restore ix
  509.     jmp    main
  510. ;
  511. ipchl:    pchl
  512. ;
  513. convtab:            ; note: sorted in ascending order
  514.     db    'a'
  515.     dw    achrout
  516.     db    'b'
  517.     dw    bytout
  518.     db    'c'
  519.     dw    chrout
  520.     db    'd'
  521.     dw    decout
  522.     db    'f'
  523.     dw    fillout
  524.     db    's'
  525.     dw    strout
  526.     db    't'
  527.     dw    ttrout
  528.     db    'u'
  529.     dw    unsout
  530.     db    'v'
  531.     dw    vstrout
  532.     db    'w'
  533.     dw    wrdout
  534.     db    'x'
  535.     dw    hexout
  536. cvtblen    equ    ($-convtab)/3
  537. ;
  538. ;
  539. achrout:
  540.     mvi    c,1
  541.     jr    chro0
  542. ;
  543. chrout:
  544.     mvi    c,0
  545. chro0:
  546.     jrnz    chro1
  547.     mvi    b,1        ; default field width
  548. chro1:
  549.     dcr    b
  550.     jrz    chro2
  551.     mvi    a,' '
  552.     call    output
  553.     jr    chro1
  554. chro2:
  555.     ldax    d
  556.     dcr    c
  557.     jnz    output        ; jump if no offset
  558.     pop    h
  559.     xthl            ; get conversion string pointer
  560.     add    m        ; add character
  561.     inx    h        ; point to next
  562.     xthl
  563.     push    h
  564.     jmp    output
  565. ;
  566. fillout:
  567.     ldax    d        ; fieldwidth value
  568.     jrz    fillout1    ; ok if default
  569.     mov    c,a
  570.     mov    a,b
  571.     sub    c        ; subtract from specified fieldwidth
  572.     jrnc    fillout1    ; ok if smaller 
  573.     xra    a        ; else output nothing
  574. fillout1:
  575.     mov    b,a
  576.     ora    a        ; condition code
  577.     pop    h
  578.     xthl            ; get conversion string pointer
  579.     mov    a,m
  580.     inx    h
  581.     xthl
  582.     push    h
  583.     rz            ; ready if nothing to output
  584. filloutl:
  585.     push    psw
  586.     call    output
  587.     pop    psw
  588.     djnz    filloutl
  589.     ret
  590. ;
  591. ;
  592. ttrout:
  593.     xchg
  594.     mvi    e,0        ; terminator
  595.     mov    b,m        ; max chars = length
  596.     inx    h
  597.     jrnz    ttro1
  598.     mov    c,b        ; fieldwidth = max chars = length 
  599.     jr    stro1
  600. ttro1:
  601.     mov    a,c
  602.     cmp    b
  603.     jrnc    stro1        ; ok if fieldwidth >= length
  604.     mov    b,c        ; else length = fieldwidth
  605.     jr    stro1
  606. ;
  607. vstrout:
  608.     pop    h        ; retaddr
  609.     xthl            ; string addr
  610.     mov    a,m        ; terminator
  611.     inx    h
  612.     xthl
  613.     push    h        ; restore stack
  614.     xchg
  615.     mov    e,a
  616.     mvi    c,0
  617.     jr    stro0
  618. ;
  619. strout:
  620.     xchg            ; parameter into hl
  621.     mvi    e,0
  622. stro0:
  623.     jrnz    stro1
  624.     mvi    b,0ffh        ; max chars
  625. stro1:
  626.     mov    a,m
  627.     inx    h
  628.     cmp    e        ; end of string ?
  629.     jrz    stro2
  630.     call    output        ; output the character
  631.     dcr    c
  632.     jp    stro11
  633.     mvi    c,0
  634. stro11:
  635.     djnz    stro1
  636. ;
  637. stro2:
  638.     dcr    c
  639.     rm
  640.     mvi    a,' '
  641.     call    output
  642.     jr    stro2
  643. ;
  644. ;
  645. wrdout:
  646.     jrnz    wrdo1
  647.     mvi    b,4        ; default field width
  648. wrdo1:
  649.     inx    d
  650.     call    hexbyt
  651.     rz
  652.     dcx    d
  653.     call    hexbyt
  654.     rz
  655.     inx    d
  656.     inx    d
  657.     jr    wrdo1
  658. ;
  659. ;
  660. hexout:
  661.     jrnz    hexo1
  662.     mvi    b,2        ; default field width
  663. hexo1:
  664.     call    hexbyt
  665.     rz
  666.     inx    d
  667.     jr    hexo1
  668. ;
  669. hexbyt:
  670.     ldax    d
  671.     rrc
  672.     rrc
  673.     rrc
  674.     rrc
  675.     call    hexdig
  676.     rz
  677.     ldax    d
  678. hexdig:
  679.     ani    0fh
  680.     adi    '0'
  681.     cpi    '9'+1
  682.     jrc    hdo
  683.     adi    'A'-'0'-10
  684. hdo:
  685.     call    output
  686.     dcr    b
  687.     ret
  688. ;
  689. ;
  690. bytout:
  691.     ldax    d
  692.     mov    e,a        ; load lower byte
  693.     xra    a
  694.     mov    d,a        ; clear upper byte
  695.     sta    sign        ; no sign
  696.     mvi    c,1        ; no. of digits
  697.     jr    deco2
  698. ;
  699. ;
  700. unsout:
  701.     xchg
  702.     mov    e,m
  703.     inx    h
  704.     mov    d,m
  705.     xra    a
  706.     sta    sign
  707.     mvi    c,1        ; no. of digits
  708.     jr    deco2
  709. ;
  710. ;
  711. decout:
  712.     xchg
  713.     mov    e,m
  714.     inx    h
  715.     mov    d,m
  716.     mvi    c,1        ; no. of digits
  717.     mov    a,d
  718.     sta    sign
  719.     ora    a
  720.     jp    deco2        ; skip if positive
  721.     lxi    h,0
  722.     dsbc    d        ; 16-bit complement
  723.     xchg
  724.     inr    c        ; space for minus sign
  725. ;
  726. deco2:                ; determine number of significant digits
  727.     lxi    h,-10
  728.     dad    d
  729.     jrnc    deco3
  730.     inr    c
  731.     lxi    h,-100
  732.     dad    d
  733.     jrnc    deco3
  734.      inr    c
  735.     lxi    h,-1000
  736.     dad    d
  737.     jrnc    deco3
  738.     inr    c
  739.     lxi    h,-10000
  740.     dad    d
  741.     jrnc    deco3
  742.     inr    c
  743. deco3:
  744.     mov    a,b        ; field width
  745.     sub    c        ; space fill ?
  746.     jrz    deco4
  747.     jrc    deco4        ; no space fill if field width <= significant
  748.     mov    b,a        ; else fill
  749. decosp:
  750.     mvi    a,' '
  751.     call    output
  752.     djnz    decosp
  753. ;
  754. deco4:
  755.     lda    sign
  756.     ora    a
  757.     jp    deco5
  758.     mvi    a,'-'        ; output sign
  759.     call    output
  760. deco5:
  761.     xchg            ; get value into hl
  762.     mvi    b,'0'        ; mark zero suppression
  763.     lxi    d,-10000
  764.     call     decdig
  765.     lxi    d,-1000
  766.     call    decdig
  767.     lxi    d,-100
  768.     call    decdig
  769.     lxi    d,-10
  770.     call    decdig
  771.     dcr    b        ; no zero suppression for last digit
  772.     lxi    d,-1
  773. ;                ; fall through to decdig
  774. decdig:
  775.     mvi    c,'0'-1
  776.     push    psw        ; dummy push
  777. decdigl:
  778.     pop    psw
  779.     push    h        ; remainder
  780.     inr    c
  781.     dad    d
  782.     jrc    decdigl        ; subtract until no borrow
  783.     mov    a,c
  784.     cmp    b        ; zero suppression ?
  785.     jrz    supzer
  786.     dcr    b        ; no further zero suppression
  787.     call    output        ; output the digit
  788. supzer:
  789.     pop    h        ; remainder
  790.     ret
  791. ;
  792. ;
  793. tolower:
  794.     cpi    'A'
  795.     rc
  796.     cpi    'Z'+1
  797.     rnc
  798.     adi    'a'-'A'
  799.     ret
  800. ;
  801. ;
  802. aschex:
  803.     cpi    '0'
  804.     rc
  805.     cpi    '9'+1
  806.     jrnc    asch1
  807.     sui    '0'
  808.     ret
  809. asch1:
  810.     call    tolower
  811.     cpi    'a'
  812.     rc
  813.     cpi    'g'
  814.     jrnc    asch2
  815.     sui    'a'-10
  816.     ret
  817. asch2:
  818.     stc
  819.     ret
  820. ;
  821. ;
  822.     IF    bios
  823. ;
  824.     extrn    ?pchr
  825. output:
  826.     push    h
  827.     lxi    h,position
  828.     inr    m
  829.     pop    h
  830.     push    psw
  831.     lda    sprint
  832.     inr    a
  833.     jrz    out1        ; jump if string output
  834.     pop    psw        ; list output goes to console for BIOS
  835.     jmp    ?pchr        ; directly jump to BIOS char out routine
  836. ;
  837. out1:
  838.     pop    psw
  839.     push    h
  840.     lhld    strptr        ; destination pointer
  841.     mov    m,a        ; insert the character
  842.     inx    h        ; increase dest pointer
  843.     shld    strptr        ; store back
  844.     lhld    strlen        ; load string length address
  845.     inr    m        ; increase
  846.     jrnz    outret        ; redy if no overflow
  847.     inx    h        ; increase next byte
  848.     inr    m
  849. outret:
  850.     pop    h
  851.     ret
  852. ;
  853.     ELSE
  854. ;
  855. output:
  856.     push    b
  857.     push    d
  858.     push    h
  859.     lxi    h,position
  860.     inr    m
  861.     mov    e,a
  862.     lda    sprint        ; sprintf ?
  863.     inr    a
  864.     jrz    out1        ; branch if string destination
  865. ;
  866.     IF    direct
  867. ;
  868. outwt:
  869.     in    s2bc
  870.     ani    4        ; Tx Buffer empty ?
  871.     jrz    outwt
  872.     mov    a,e
  873.     out    s2bd
  874. ;
  875.     ELSE
  876. ;
  877.     dcr    a
  878.     mov    c,a        ; BDOS function number
  879.     call    5        ; call BDOS
  880. ;
  881.     ENDIF
  882. ;
  883. outret:
  884.     pop    h
  885.     pop    d
  886.     pop    b
  887.     ret
  888. ;
  889. out1:
  890.     lhld    strptr        ; destination pointer
  891.     mov    m,e        ; insert the character
  892.     inx    h        ; increase dest pointer
  893.     shld    strptr        ; store back
  894.     lhld    strlen        ; load string length address
  895.     inr    m        ; increase
  896.     jrnz    outret        ; redy if no overflow
  897.     inx    h        ; increase next byte
  898.     inr    m
  899.     jr    outret
  900. ;
  901.     ENDIF
  902. ;
  903.     IF    NOT bios
  904.     DSEG            ; data segment if not BIOS
  905.     ENDIF
  906. ;
  907. sprint    ds    1
  908. strptr    ds    2
  909. strlen    ds    2
  910. ;
  911. ;
  912. sign    ds    1
  913. idxcnt    ds    1
  914. position ds    1
  915. ;
  916.     public    ?pfraf,?pfra,?pfrbc,?pfrb,?pfrde,?pfrd
  917.     public    ?pfrhl,?pfrh,?pfrix,?pfriy,?pfrsp
  918. ;
  919. ?pfraf    ds    1
  920. ?pfra    ds    1
  921. ?pfrbc    ds    1
  922. ?pfrb    ds    1
  923. ?pfrde    ds    1
  924. ?pfrd    ds    1
  925. ?pfrhl    ds    1
  926. ?pfrh    ds    1
  927. ?pfrix    ds    2
  928. ?pfriy    ds    2
  929. ?pfrsp    ds    2
  930. ;
  931.     end
  932. 
  933.