home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 4 Drivers / 04-Drivers.zip / scsiopt2.zip / dprintf.asm < prev    next >
Assembly Source File  |  1997-06-05  |  18KB  |  619 lines

  1. ;    SCCSID = @(#)dprintf.asm    6.3 92/05/05
  2. ;    $Source: e:/source/driver/perf/RCS/dprintf.asm,v $
  3.  
  4. ;/***********************************************************************/
  5. ;/*                                    */
  6. ;/* Driver Name: IBM2SCSI.ADD - Adapter Driver for ABIOS SCB Devices    */
  7. ;/*         ---------------------------------------------------    */
  8. ;/*                                    */
  9. ;/* Source File Name: DPRINTF.ASM                    */
  10. ;/*                                    */
  11. ;/* Descriptive Name: Format/Send debug information to COMx:        */
  12. ;/*                                    */
  13. ;/* Function:                                */
  14. ;/*                                    */
  15. ;/*                                    */
  16. ;/*---------------------------------------------------------------------*/
  17. ;/*                                    */
  18. ;/* Copyright (C) 1992 IBM Corporation                    */
  19. ;/*                                    */
  20. ;/* DISCLAIMER OF WARRANTIES.  The following [enclosed] code is     */
  21. ;/* provided to you solely for the purpose of assisting you in        */
  22. ;/* the development of your applications. The code is provided        */
  23. ;/* "AS IS", without warranty of any kind. IBM shall not be liable      */
  24. ;/* for any damages arising out of your use of this code, even if    */
  25. ;/* they have been advised of the possibility of such damages.        */
  26. ;/*                                    */
  27. ;/*---------------------------------------------------------------------*/
  28. ;/*                                    */
  29. ;/* Change Log                                */
  30. ;/*                                    */
  31. ;/* Mark    Date      Programmer  Comment                */
  32. ;/* ----    ----      ----------  -------                */
  33. ;/* @nnnn   mm/dd/yy  NNN                        */
  34. ;/*                                    */
  35. ;/* $Log: dprintf.asm,v $
  36. ;/* Revision 1.1  1997/06/04 23:37:46  vitus
  37. ;/* Initial revision
  38. ;/*
  39. ;/* Revision 1.1  1997/05/07 23:56:42  vitus
  40. ;/* Initial revision
  41. ;/*
  42. ;/* Revision 1.1  1995/10/18 11:06:14  vitus
  43. ;/* Initial revision
  44. ;/*                                */
  45. ;/***********************************************************************/
  46.  
  47. ;
  48. ;****************************************************************************
  49. ;*
  50. ;* dprintf - this routine displays information on the debug terminal. it
  51. ;*         provides limited formatting, which is a subset of c's printf
  52. ;*         function
  53. ;*
  54. ;*    calling sequence:    push selector for insert 'n'
  55. ;*                push offset for insert 'n'
  56. ;*                push selector for 'n-1'
  57. ;*                push offset for 'n-1'
  58. ;*                   ...
  59. ;*                push selector for second insert
  60. ;*                push offset for second insert
  61. ;*                push selector for first insert
  62. ;*                push offset for first insert
  63. ;*                push selector raw string
  64. ;*                push offset for raw string
  65. ;*                call dprintf
  66. ;*                add sp,4 + ('n' * 4)
  67. ;*
  68. ;*            for "%w", just push one word containing the data to
  69. ;*                  be displayed. make sure that the "add sp"
  70. ;*                  cleans the stack correctly.
  71. ;*
  72. ;*            for "%z", just the repeat count, then the selector
  73. ;*                  of the area to display, and then the offset.
  74. ;*                  make sure that the "add sp" cleans the stack
  75. ;*                  correctly.
  76. ;*
  77. ;*    formatting:    prior to being displayed, the raw string is formatted
  78. ;*            by scanning it for format control sequences. as each
  79. ;*            format control sequence is encountered, it is replaced
  80. ;*            by appropriately formatted text obtained from the
  81. ;*            corresponding pointer
  82. ;*
  83. ;*            the following format control sequences are supported:
  84. ;*
  85. ;*            %c -  the corresponding far ptr points to a byte
  86. ;*                  which replaces the "%c"
  87. ;*
  88. ;*            %u -  the corresponding far ptr points to a word
  89. ;*                  which is displayed as an unsigned decimal
  90. ;*                  integer, replacing the "%u"
  91. ;*
  92. ;*            %x -  the corresponding far ptr points to a word
  93. ;*                  which is displayed as upper case hex,
  94. ;*                  replacing the "%X"
  95. ;*
  96. ;*            %lx - the corresponding far ptr points to a double
  97. ;*                  word which is displayed as upper case hex,
  98. ;*                  replacing the "%X"
  99. ;*
  100. ;*            %s -  the corresponding far ptr points to a null
  101. ;*                  terminated string which is displayed unchanged,
  102. ;*                  replacing the "%s"
  103. ;*
  104. ;*            %p -  the corresponding far ptr is displayed as upper
  105. ;*                  case hex in the format "ssss:oooo"
  106. ;*
  107. ;*            %w -  the corresponding word is displayed as upper
  108. ;*                  case hex replacing the "%w". note that in this
  109. ;*                  case, only one word is pushed onto the stack
  110. ;*                  for the %w insert
  111. ;*
  112. ;*            %z -  using the corresponding repeat count and far
  113. ;*                  pointer, a memory dump is produced. note that
  114. ;*                  in this case the stack contains a repeat count
  115. ;*                  and a far pointer to the area to dump
  116. ;*
  117. ;*            %% -  the character "%" is displayed, replacing the
  118. ;*                  "%%"
  119. ;*
  120. ;*
  121.  
  122. .286p
  123. PUBLIC    _dprintf
  124.  
  125.  
  126. COM1_PORT    EQU    03f8h
  127. COM2_PORT    EQU    02f8h
  128.  
  129.  
  130. DEFAULT_PORT    EQU    COM2_PORT
  131.  
  132. CAR_RET     EQU    0DH
  133. LINE_FEED    EQU    0AH
  134. BELL        EQU    07H
  135. COM_LSR     EQU    05H
  136. COM_DAT     EQU    00H
  137.  
  138.  
  139.  
  140. s_frame     struc
  141.  
  142. s_bp        dw    ?        ; callers bp.
  143. s_ptr_delta    dw    ?        ; delta (in bytes) to current pointer
  144.                     ; from first pointer.
  145. s_ret        dw    ?        ; callers ip.
  146. s_string    dd    ?        ; far pointer to raw string.
  147. s_ptrs        dd    ?        ; pointer to first variable.
  148.  
  149. s_frame     ends
  150.  
  151. ;word_10000     dw     10000
  152. ;word_1000     dw     1000
  153. ;word_100     dw     100
  154. ;word_10     dw     10
  155.  
  156.  
  157. _TEXT    segment word public 'CODE'
  158.     assume    cs:_TEXT
  159.  
  160.  
  161. _dprintf    proc    near
  162.         public    _dprintf
  163.  
  164.         push    0        ; zero the delta to current pointer.
  165.  
  166.         push    bp        ; save our callers bp register.
  167.         mov    bp,sp        ; point to our stack frame.
  168.  
  169.         push    ds        ; save our callers ds register.
  170.         push    es        ; save our callers es register.
  171.         pusha            ; save all other caller registers.
  172.  
  173.         lds    si,ss:[bp+s_string] ; point to the raw string.
  174.  
  175. dprintf_loop:    lodsb            ; pick up a byte of the string.
  176.  
  177.         or    al,al        ; is it the end of the string?
  178.         jnz    dprintf_more    ; no, go check for format control.
  179.  
  180.         popa            ; restore all other caller registers.
  181.         pop    es        ; restore our callers es register.
  182.         pop    ds        ; restore our callers ds register.
  183.         pop    bp        ; restore our callers bp register.
  184.         add    sp,2        ; unstack s_ptr_delta.
  185.  
  186.         ret            ; return to our caller.
  187.  
  188. dprintf_more:    cmp    al,'%'          ; no, is it the start of a format
  189.                     ; control sequence?
  190.         je    dprintf_type    ; yes, go see what type of sequence.
  191.         jmp    dprintf_put_ch    ; no, go display character and return.
  192.  
  193. dprintf_type:    lodsb            ; pick up a byte of the string.
  194.         cmp    al,'%'          ; is caller trying to display "%"?
  195.  
  196.         jne    dprintf_try_c    ; no, go see if it is a "c".
  197.  
  198.         mov    al,'%'          ; yes, go display it
  199.         jmp    dprintf_put_ch    ; and exit.
  200.  
  201. dprintf_try_c:    cmp    al,'c'          ; is it a string display?
  202.         jne    dprintf_try_s    ; no, go see if it is a "s".
  203.  
  204.         lea    bx,[bp]+s_ptrs           ; yes, pick up the
  205.         add    bx,ss:[bp+s_ptr_delta] ; corresponding
  206.         les    bx,ss:[bx]           ; pointer.
  207.         add    ss:[bp+s_ptr_delta],4  ; move down to next pointer.
  208.  
  209.         mov    al,es:[bx]    ; pick up a byte.
  210.         jmp    dprintf_put_ch    ; go display character and return.
  211.  
  212. dprintf_try_s:    cmp    al,'s'          ; is it a string display?
  213.         jne    dprintf_try_u    ; no, go see if it is a "u".
  214.  
  215.         lea    bx,[bp]+s_ptrs           ; yes, pick up the
  216.         add    bx,ss:[bp+s_ptr_delta] ; corresponding
  217.         les    bx,ss:[bx]           ; pointer.
  218.         add    ss:[bp+s_ptr_delta],4  ; move down to next pointer.
  219.  
  220. dprintf_next_s: mov    al,es:[bx]    ; pick up a byte.
  221.  
  222.         or    al,al        ; is it the end of the string?
  223.         jz    dprintf_loop    ; yes, go do next raw string byte.
  224.  
  225.         call    put_char    ; no, display the character.
  226.  
  227.         inc    bx        ; move down to the next character
  228.         jmp    dprintf_next_s    ; and go round again.
  229.  
  230. dprintf_try_u:    cmp    al,'u'          ; is it an unsigned short display?
  231.         jne    dprintf_try_x    ; no, go see if it is a "X".
  232.  
  233.         lea    bx,[bp]+s_ptrs           ; yes, pick up the
  234.         add    bx,ss:[bp+s_ptr_delta] ; corresponding
  235.         les    bx,ss:[bx]           ; pointer.
  236.         add    ss:[bp+s_ptr_delta],4  ; move down to next pointer.
  237.  
  238.         mov    ax,es:[bx]    ; pick up the word to display.
  239.  
  240.         xor    dx,dx        ; convert the
  241.         mov    cx, 10000
  242.         div    cx        ; ten thousands
  243. ;        div    word_10000    ; ten thousands
  244.         or    al,'0'          ; digit and
  245.         call    put_char    ; display it.
  246.  
  247.         mov    ax,dx        ; convert the
  248.         xor    dx,dx        ; thousands
  249.         mov    cx, 1000
  250.         div    cx        ; digit
  251. ;        div    word_1000    ; digit
  252.         or    al,'0'          ; and
  253.         call    put_char    ; display it.
  254.  
  255.         mov    ax,dx        ; convert the
  256.         xor    dx,dx        ; hundreds
  257.         mov    cx, 100
  258.         div    cx        ; digit
  259. ;        div    word_100    ; digit
  260.         or    al,'0'          ; and
  261.         call    put_char    ; display it.
  262.  
  263.         mov    ax,dx        ; convert the
  264.         xor    dx,dx        ; tens
  265.         mov    cx, 10
  266.         div    cx        ; digit
  267. ;        div    word_10     ; digit
  268.         or    al,'0'          ; and
  269.         call    put_char    ; display it.
  270.  
  271.         mov    al,dl        ; convert the units digit
  272.         or    al,'0'          ; and go display it
  273.         jmp    dprintf_put_ch    ; and return.
  274.  
  275. dprintf_try_x:    cmp    al,'x'          ; is it an unsigned short hex display?
  276.         jne    dprintf_try_lx    ; no, go see if it is a "lX".
  277.  
  278.         lea    bx,[bp]+s_ptrs           ; yes, pick up the
  279.         add    bx,ss:[bp+s_ptr_delta] ; corresponding
  280.         les    bx,ss:[bx]           ; pointer.
  281.         add    ss:[bp+s_ptr_delta],4  ; move down to next pointer.
  282.  
  283.         call    put_hex_word    ; convert and display the word.
  284.  
  285.         jmp    dprintf_loop    ; go do next raw string byte.
  286.  
  287. dprintf_try_lx: cmp    al,'l'          ; is it an unsigned long hex display?
  288.         jne    dprintf_try_p    ; no, go see if it is a "p".
  289.         lodsb            ; maybe, pick up a byte of the string.
  290.         cmp    al,'x'          ; is the second byte correct?
  291.         je    dprintf_do_lx    ; no, go report
  292.         jmp    dprintf_error    ; the error.
  293.  
  294. dprintf_do_lx:    lea    bx,[bp]+s_ptrs           ; yes, pick up the
  295.         add    bx,ss:[bp+s_ptr_delta] ; corresponding
  296.         les    bx,ss:[bx]           ; pointer.
  297.         add    ss:[bp+s_ptr_delta],4  ; move down to next pointer.
  298.  
  299.         add    bx,2        ; move down to the second word.
  300.         call    put_hex_word    ; convert and display the second word.
  301.         sub    bx,2        ; move back to the first word.
  302.         call    put_hex_word    ; convert and display the first word.
  303.  
  304.         jmp    dprintf_loop    ; go do next raw string byte.
  305.  
  306. dprintf_try_p:    cmp    al,'p'          ; is it a far pointer display?
  307.         jne    dprintf_try_w    ; no, go see if it is a "w".
  308.  
  309.         lea    bx,[bp]+s_ptrs           ; yes, pick up the
  310.         add    bx,ss:[bp+s_ptr_delta] ; corresponding pointer.
  311.         add    ss:[bp+s_ptr_delta],4  ; move down to next pointer.
  312.  
  313.         push    es        ; save the callers data selector.
  314.  
  315.         push    ss        ; set up the proper
  316.         pop    es        ; selector.
  317.  
  318.         add    bx,2        ; move down to the second word.
  319.         call    put_hex_word    ; convert and display the selector.
  320.         mov    al,':'          ; display
  321.         call    put_char    ; the ":".
  322.         sub    bx,2        ; move back to the first word.
  323.         call    put_hex_word    ; convert and display the offset.
  324.  
  325. ;;;        mov    al,' '        ; display
  326. ;;;        call    put_char    ; a couple
  327. ;;;        mov    al,' '        ; of
  328. ;;;        call    put_char    ; spaces.
  329.  
  330.         pop    es        ; recover the callers data selector.
  331.  
  332.         jmp    dprintf_loop    ; go do next raw string byte.
  333.  
  334. dprintf_try_w:    cmp    al,'w'          ; is it an immediate word display?
  335.         jne    dprintf_try_z    ; no, go see if it is a "z".
  336.  
  337.         lea    bx,[bp]+s_ptrs           ; yes, pick up the
  338.         add    bx,ss:[bp+s_ptr_delta] ; corresponding pointer.
  339.         add    ss:[bp+s_ptr_delta],2  ; move down to next pointer.
  340.  
  341.         push    es        ; save the callers data selector.
  342.  
  343.         push    ss        ; set up the proper
  344.         pop    es        ; selector.
  345.  
  346.         call    put_hex_word    ; convert and display the word.
  347.  
  348.         pop    es        ; recover the callers data selector.
  349.  
  350.         jmp    dprintf_loop    ; go do next raw string byte.
  351.  
  352. dprintf_try_z:    cmp    al,'z'          ; is it a memory dump display?
  353.         je    dprintf_do_z    ; no, go report
  354.         jmp    dprintf_error    ; the error.
  355.  
  356. dprintf_do_z:
  357.         lea    bx,[bp]+s_ptrs           ; yes, pick up the
  358.         add    bx,ss:[bp+s_ptr_delta] ; corresponding pointer.
  359.         add    ss:[bp+s_ptr_delta],6  ; move down to next pointer.
  360.  
  361.         mov    cx,ss:[bx+4]    ; pick up the repeat count.
  362.  
  363.         push    es        ; save the callers data selector.
  364.  
  365.         les    bx,ss:[bx]    ; point to the area to display.
  366.  
  367. dprintf_z_a:    mov    ax,es        ; pick up the selector to display.
  368.         xchg    ah,al        ; set up to process the first byte.
  369.         call    put_left_nib    ; display the first byte
  370.         call    put_right_nib    ; of the selector.
  371.         xchg    ah,al        ; set up to process the second byte.
  372.         call    put_left_nib    ; display the second byte
  373.         call    put_right_nib    ; of the selector.
  374.  
  375.         mov    al,':'          ; display a
  376.         call    put_char    ; colon.
  377.  
  378.         mov    ax,bx        ; pick up the offset to display.
  379.         xchg    ah,al        ; set up to process the first byte.
  380.         call    put_left_nib    ; display the first byte
  381.         call    put_right_nib    ; of the offset.
  382.         xchg    ah,al        ; set up to process the second byte.
  383.         call    put_left_nib    ; display the second byte
  384.         call    put_right_nib    ; of the offset.
  385.  
  386.         mov    al,' '          ; display
  387.         call    put_char    ; two
  388.         mov    al,' '          ; seperating
  389.         call    put_char    ; spaces.
  390.  
  391.         push    cx        ; save the repeat count for later.
  392.  
  393.         mov    dx,16*3+1    ; initialize the fill count.
  394.  
  395.         cmp    cx,16        ; are there more than 16 bytes left?
  396.         jbe    dprintf_z_b    ; yes, limit it to 16 bytes
  397.         mov    cx,16        ; for this line.
  398.  
  399. dprintf_z_b:    push    bx        ; save offset and display count
  400.         push    cx        ; for the character display.
  401.  
  402. dprintf_z_c:    mov    al,es:[bx]    ; pick up a byte to display.
  403.         call    put_hex_byte    ; display it in hex.
  404.  
  405.         mov    al,' '          ; set up to display a space.
  406.         cmp    dx,9*3+1    ; should it be a dash?
  407.         jne    dprintf_z_e    ; no, bypass changing it.
  408.         mov    al,'-'          ; yes, set up to display a dash.
  409. dprintf_z_e:    call    put_char    ; display the dash or space.
  410.  
  411.         sub    dx,3        ; down the fill count by one position.
  412.  
  413.         inc    bx        ; move down to the next byte.
  414.  
  415.         loop    dprintf_z_c    ; more to do? yes, go round again?
  416.  
  417.         mov    cx,dx        ; no, pick up remaining fill count.
  418.  
  419. dprintf_z_g:    mov    al,' '          ; display a
  420.         call    put_char    ; space.
  421.  
  422.         loop    dprintf_z_g    ; more to do? yes, go round again.
  423.  
  424.         pop    cx        ; recover the offset and
  425.         pop    bx        ; display count.
  426.  
  427. dprintf_z_i:    mov    al,'.'          ; set up to display a dot.
  428.  
  429.         mov    ah,es:[bx]    ; does the byte
  430.         cmp    ah,20h        ; contain a
  431.         jb    dprintf_z_k    ; valid ascii
  432.         cmp    ah,7fh        ; code?
  433.         ja    dprintf_z_k    ; no, go display the dot.
  434.  
  435.         xchg    al,ah        ; yes, set up to do byte's contents.
  436.  
  437. dprintf_z_k:    call    put_char    ; display a dot or the byte contents.
  438.  
  439.         inc    bx        ; move down to the next byte.
  440.  
  441.         loop    dprintf_z_i    ; more to do on this line?
  442.                     ; yes, go round again.
  443.  
  444.         pop    cx        ; no, recover the repeat count.
  445.  
  446.         sub    cx,16        ; down the repeat count by one line.
  447.  
  448.         jle    dprintf_z_z    ; more to do? no, go exit.
  449.  
  450.         mov    al,CAR_RET    ; perform
  451.         call    put_char    ; a
  452.         mov    al,LINE_FEED    ; new line
  453.         call    put_char    ; operation.
  454.  
  455.         jmp    dprintf_z_a    ; go round and display another line.
  456.  
  457. dprintf_z_z:    pop    es        ; recover the callers data selector.
  458.  
  459.         jmp    dprintf_loop    ; go do next raw string byte.
  460.  
  461. dprintf_error:    mov    ah,al        ; display
  462.         mov    al,'?'          ; an
  463.         call    put_char    ; eye
  464.         mov    al,'\'          ; catching
  465.         call    put_char    ; "invalid
  466.         mov    al,ah        ; format
  467.         call    put_char    ; control"
  468.         mov    al,'\'          ; message
  469.         call    put_char    ; and
  470.         mov    al,BELL     ; beep.
  471.  
  472. dprintf_put_ch: call    put_char    ; display the character.
  473.         jmp    dprintf_loop    ; go process next raw string byte.
  474.  
  475. _dprintf    endp
  476.  
  477. put_left_nib    proc    near
  478.  
  479.         push    ax        ; save the callers ax register.
  480.  
  481.         shr    al,4        ; convert the
  482.         add    al,'0'          ; left nibble
  483.         cmp    al,'9'          ; to an ascii
  484.         jbe    put_left_nib_a    ; hex
  485.         add    al,'A'-'9'-1    ; representation.
  486. put_left_nib_a: call    put_char    ; display the character.
  487.  
  488.         pop    ax        ; restore the callers ax register.
  489.  
  490.         ret            ; return to our caller.
  491.  
  492. put_left_nib    endp
  493.  
  494. put_right_nib    proc    near
  495.  
  496.         push    ax        ; save the callers ax register.
  497.  
  498.         and    al,0fh        ; convert the
  499.         add    al,'0'          ; right nibble
  500.         cmp    al,'9'          ; to an
  501.         jbe    put_rght_nib_a    ; ascii hex
  502.         add    al,'A'-'9'-1    ; representation.
  503. put_rght_nib_a: call    put_char    ; display the character.
  504.  
  505.         pop    ax        ; restore the callers ax register.
  506.  
  507.         ret            ; return to our caller
  508.  
  509. put_right_nib    endp
  510.  
  511. put_hex_byte    proc    near
  512.  
  513.         mov    al,es:[bx]    ; display the left nibble
  514.         call    put_left_nib    ; in ascii hex.
  515.  
  516.         mov    al,es:[bx]    ; display the right nibble
  517.         call    put_right_nib    ; in ascii hex.
  518.  
  519.         ret            ; return to our caller.
  520.  
  521. put_hex_byte    endp
  522.  
  523.  
  524. put_hex_word    proc    near
  525.  
  526.         inc    bx        ; set up to process second byte first.
  527.  
  528.         call    put_hex_byte    ; display the byte in hex.
  529.  
  530.         dec    bx        ; move back to the first byte.
  531.  
  532.         call    put_hex_byte    ; display the byte in hex.
  533.  
  534.         ret            ; return to our caller.
  535.  
  536. put_hex_word    endp
  537.  
  538.  
  539. ;         public  portadr
  540. ;portadr     dw     DEFAULT_PORT     ; change config.h to change this.
  541.                     ; use: com2=02f8H, com1=03f8H
  542.  
  543. IODelay Macro
  544.     local a
  545.     jmp a
  546. a:
  547. endm
  548.  
  549. PollC    PROC    NEAR
  550.  
  551. ;    mov    dx, cs:PortAdr
  552.     mov    dx, DEFAULT_PORT
  553.     add    dx, COM_LSR
  554.  
  555.     in    al,dx            ; get input status
  556. ;    IODelay
  557.  
  558.     and    al,1            ; is there a char in RECV buffer?
  559.     jz    plc1            ; no, go return empty
  560.  
  561. ;    mov    dx, cs:PortAdr
  562.     mov    dx, DEFAULT_PORT
  563.     add    dx, COM_DAT
  564.  
  565.     in    al,dx            ; suck char out of buffer
  566. ;    IODelay
  567.  
  568.     and    al,07fh         ; strip off stupid parity crap
  569. plc1:    ret
  570. PollC    ENDP
  571.  
  572.  
  573. ;**    PUTC - output a single char to COM2 handling ^S
  574.  
  575.  
  576. put_char    proc    near
  577.  
  578.     push    dx
  579.     push    ax
  580.  
  581. ;    See if ^S
  582.  
  583.     call    PollC            ; is there a char at input
  584.     jz    pc2            ; no, go output our char
  585.     cmp    al,'S' - 'A' + 1    ; is it ^S?
  586.     jnz    pc2            ; no, go output our char
  587.  
  588. ;    Saw ^S.  Wait for and eat next char.
  589.  
  590. pc1:    call    PollC            ; look for next char
  591.     jz    pc1            ; no char, go look again
  592.     cmp    al,'S' - 'A' + 1    ; is it ^S again?
  593.     jz    pc1            ; yes, go look for something else
  594.  
  595. ;pc2:     mov     dx, cs:PortAdr
  596. pc2:    mov    dx, DEFAULT_PORT
  597.     add    dx, COM_LSR
  598.     in    al,dx
  599. ;    IODelay
  600.     test    al,020h
  601.     jz    pc2
  602.  
  603. ;    ready.    crank it out!
  604.  
  605. ;    mov    dx, cs:PortAdr
  606.     mov    dx, DEFAULT_PORT
  607.     add    dx, COM_DAT
  608.     pop    ax
  609.     out    dx,al
  610.  
  611.         pop    dx        ; restore the callers dx register.
  612.  
  613.     ret
  614.  
  615. put_char    endp
  616.  
  617. _TEXT    ends
  618.     end
  619.