home *** CD-ROM | disk | FTP | other *** search
/ Simtel MSDOS - Coast to Coast / simteldosarchivecoasttocoast.iso / io_util / komm.asm < prev    next >
Assembly Source File  |  1994-03-04  |  29KB  |  1,095 lines

  1.         page    55,132
  2.         title    COMM - Device drivers for COM1 and COM2.
  3.         subttl    Parameter definitions:
  4. ;
  5. ; COMM.ASM - Installable device drivers for COM1 and COM2.
  6. ;
  7. ; All of this code originated at my dainty fingertips, and I won't be mad
  8. ; at anybody who wants to help himself to it. - Peter Pearson.
  9. ;
  10. ; Version of 20 June 1985.
  11. ;
  12. ; Revision history:
  13. ;    852005 - Send ^S when input buffer approaches fullness, ^Q when it
  14. ;         is nearly empty again.
  15. ;
  16. cseg        segment para public 'code'
  17.         org    0
  18. ;
  19. ; Equates:
  20. ;
  21. CTRLS        equ    19
  22. CTRLQ        equ    17
  23.  
  24. primary     equ    3f8h        ;Base address for primary asynch.
  25. secondary    equ    2f8h        ;Base address for secondary asynch.
  26. prim_int    equ    0ch        ;Hardware interrupt vector number.
  27. sec_int     equ    0bh        ;Hardware interrupt vector number.
  28. ;
  29. ;
  30. ;    Description of the Serial Port:
  31. ;
  32. ;    I/O    I/O
  33. ;    addr    addr
  34. ;    ----    ----
  35. ;    3F8    2F8    TX data, RX data, Div Latch LSB.
  36. ;    3F9    2F9    Interrupt Enable, Div Latch MSB.
  37. ;                7 6 5 4 3 2 1 0
  38. ;                | | | | | | |  \_ Data Available.
  39. ;                | | | | | |  \___ Xmit Holding Reg Empty.
  40. ;                | | | | |  \_____ Receiver Line Status.
  41. ;                | | | |  \_______ Modem Status.
  42. ;                | | |  \_________ = 0.
  43. ;                | |  \___________ = 0.
  44. ;                |  \_____________ = 0.
  45. ;                 \_______________ = 0.
  46. ;
  47. ;    3FA    2FA    Interrupt Identification.
  48. ;                7 6 5 4 3 2 1 0
  49. ;                | | | | |  \_\_\___________ 001 = no interrupt.
  50. ;                | | | | |            110 = rcvr status.
  51. ;                | | | | |            100 = rcvd data.
  52. ;                | | | | |            010 = THR empty.
  53. ;                | | | | |            000 = Modem status.
  54. ;                | | | |  \_______ = 0.
  55. ;                | | |  \_________ = 0.
  56. ;                | |  \___________ = 0.
  57. ;                |  \_____________ = 0.
  58. ;                 \_______________ = 0.
  59. ;
  60. ;    3FB    2FB    Line Control.
  61. ;                7 6 5 4 3 2 1 0
  62. ;                | | | | | | |  \_ Word Length Select Bit 0.
  63. ;                | | | | | |  \___ Word Length Select Bit 1.
  64. ;                | | | | |  \_____ Number Stop Bits.
  65. ;                | | | |  \_______ Parity Enable.
  66. ;                | | |  \_________ Even Parity Select.
  67. ;                | |  \___________ Stick Parity.
  68. ;                |  \_____________ Set Break.
  69. ;                 \_______________ Divisor Latch Access Bit.
  70. ;
  71. ;    3FC    2FC    Modem Control.
  72. ;                7 6 5 4 3 2 1 0
  73. ;                | | | | | | |  \_ Data Terminal Ready.
  74. ;                | | | | | |  \___ Request to Send.
  75. ;                | | | | |  \_____ Out 1.
  76. ;                | | | |  \_______ Out 2. (= 1 to enable ints.)
  77. ;                | | |  \_________ Loop.
  78. ;                | |  \___________ = 0.
  79. ;                |  \_____________ = 0.
  80. ;                 \_______________ = 0.
  81. ;
  82. ;    3FD    2FD    Line Status.
  83. ;                7 6 5 4 3 2 1 0
  84. ;                | | | | | | |  \_ Data Ready.
  85. ;                | | | | | |  \___ Overrun Error.
  86. ;                | | | | |  \_____ Parity Error.
  87. ;                | | | |  \_______ Framing Error.
  88. ;                | | |  \_________ Break interrupt.
  89. ;                | |  \___________ Transmitter Holding Reg Empty.
  90. ;                |  \_____________ Transmitter Shift Reg Empty.
  91. ;                 \_______________ = 0.
  92. ;
  93. ;    3FE    2FE    Modem Status.
  94. ;                7 6 5 4 3 2 1 0
  95. ;                | | | | | | |  \_ Delta Clear to Send.
  96. ;                | | | | | |  \___ Delta Data Set Ready.
  97. ;                | | | | |  \_____ Trailing Edge Ring Indicator.
  98. ;                | | | |  \_______ Delta Rx Line Signal Detect.
  99. ;                | | |  \_________ Clear to Send.
  100. ;                | |  \___________ Data Set Ready.
  101. ;                |  \_____________ Ring Indicator.
  102. ;                 \_______________ Receive Line Signal Detect.
  103. ;
  104. ;
  105. R_tx        equ    0        ;UART data-to-transmit register.
  106. R_rx        equ    0        ;UART received-data register.
  107. R_ie        equ    1        ;UART interrupt-enable register.
  108. R_ii        equ    2        ;UART interrupt-identification register.
  109. R_lc        equ    3        ;UART Line-Control register.
  110. R_mc        equ    4        ;UART Modem-Control register.
  111. R_ls        equ    5        ;UART line-status register.
  112. LC_dlab     equ    80h        ;UART LC reg. DLAB bit.
  113. MC_dtr        equ    1        ;UART MC reg. DTR bit.
  114. MC_rts        equ    2        ;UART MC reg. RTS bit.
  115. MC_out2     equ    8        ;UART MC reg. "OUT2" bit.
  116. LS_dr        equ    1        ;UART LS reg. "data ready" bit.
  117. LS_or        equ    2        ;UART LS reg. "overrun error" bit.
  118. LS_pe        equ    4        ;UART LS reg. "parity error" bit.
  119. LS_fe        equ    8        ;UART LS reg. "framing error" bit.
  120. LS_bi        equ    10h        ;UART LS reg. "Break Interrupt" bit.
  121. LS_thre     equ    20h        ;UART LS reg. "Xmit Hold Reg empty" bit.
  122. LS_tsre     equ    40h        ;UART LS reg. "Xmit Shift Reg Empty" bit.
  123. IE_da        equ    1        ;UART IE reg. "Data Available" bit.
  124. IE_thre     equ    2        ;UART IE reg. "Xmit Hold Reg Empty" bit.
  125. IE_rls        equ    4        ;UART IE reg. "Receive Line Status" bit.
  126. IE_ms        equ    8        ;UART IE reg. "Modem Status" bit.
  127. II_never    equ    0f8h        ;UART II reg: these bits never on.
  128.  
  129. ;        Definitions for the Interrupt Controller Chip:
  130.  
  131. ICC_msk     equ    21h        ;I/O address of ICC's "mask" register.
  132. ICC_ctl     equ    20h        ;I/O address of ICC's "control" reg.
  133. ICC_eoi     equ    20h        ;Non-specific end-of-interrupt.
  134. MSK_d1        equ    10h        ;Bit in MSK: disable IRQ4 (COM1).
  135. MSK_d2        equ    08h        ;Bit in MSK: disable IRQ3 (COM2).
  136.  
  137.         org    0    ;Define layout of Request Header:
  138. RH_rhlen    db    ?        ;Length of Request Header (bytes).
  139. RH_unit     db    ?        ;Unit code. (No meaning for char dev.)
  140. RH_command    db    ?        ;Command code.
  141. RH_status    dw    ?        ;Status.
  142. RH_reserved    db    8 dup(?)    ;Reserved for DOS.
  143. RH_char     db    ?        ;Character of data.
  144. RH_end_addr    label    word        ;Ending address for INIT call.
  145. RH_buf_addr    dd    ?        ;Buffer address for INPUT or OUTPUT.
  146. RH_count    dw    ?        ;Byte count for INPUT or OUTPUT.
  147.         org    0
  148.  
  149. STAT_err    equ    8000h        ;Req Hdr, status reg., "error" bit.
  150. STAT_busy    equ    200h        ;Req Hdr, status reg., "busy" bit.
  151. STAT_done    equ    100h        ;Req Hdr, status reg., "done" bit.
  152. S_E_unkunit    equ    01h        ;Req Hdr, status reg., "unknown unit" error code.
  153. S_E_not_rdy    equ    02h        ;Req Hdr, status reg., "not ready" error code.
  154. S_E_unkcom    equ    03h        ;Req Hdr, status reg., "unknown command" error code.
  155. S_E_write    equ    0ah        ;Req Hdr, status reg., "write fault" error code.
  156. S_E_read    equ    0bh        ;Req Hdr, status reg., "read fault" error code.
  157. S_E_general    equ    0ch        ;Req Hdr, status reg., "general failure" error code.
  158.         page
  159. ;
  160. ; Equates relating to data structures:
  161. ;
  162. ;    Buffers:
  163. ;    --------
  164. ;
  165. ;    When we talk about a "buffer" in this program, we're talking not
  166. ;    just about the area of data storage, but about the pointers to that
  167. ;    data area as well. Four pointers are expected to precede the first
  168. ;    byte of data storage:
  169. ;        BEGINNING - Offset of the first byte of data storage.
  170. ;        END    - Offset of the first byte AFTER data storage.
  171. ;        IN    - Offset at which next byte is to be put into buffer.
  172. ;        OUT    - Offset from which next byte is to be taken out
  173. ;              of buffer.
  174. ;    IN and OUT may range from BEGINNING to END-1. If IN = OUT, the
  175. ;    buffer is empty.
  176. ;       These definitions must be coordinated with the storage-allocation
  177. ;    statements for the buffers near the end of this program (just before
  178. ;    the INIT handler.)
  179. ;
  180. B_beg        equ    0
  181. B_end        equ    2
  182. B_in        equ    4
  183. B_out        equ    6
  184. B_data        equ    8
  185. ;
  186. in_buf_len    equ    2000        ;Length of input buffers.
  187. out_buf_len    equ    100        ;Length of output buffers.
  188. near_full    equ    1200        ;Input buffer is nearly full when it
  189.                     ; has more than this number of data
  190.                     ; bytes.
  191. near_empty    equ    200        ;Input buffer is nearly empty when it
  192.                     ; has fewer than this number of data
  193.                     ; bytes.
  194. ;
  195. ;
  196. ;    Unit Control Blocks:
  197. ;    --------------------
  198. ;
  199. ;    (I just made up that name. Any conflict with any other usage is
  200. ;    unfortunate.)
  201. ;        A UCB is assigned to each asynchronous communications device
  202. ;    (COM) we handle. (E.g., one for COM1, another for COM2.) The
  203. ;    UCB contains
  204. ;        BASE    - The base I/O address for the hardware device.
  205. ;        HOLD    - XON/XOFF flag.
  206. ;              HOLD & 1 nonzero means we have received a CTRLS.
  207. ;              HOLD & 2 nonzero means we have sent a CTRLS.
  208. ;        INBUF    - An input buffer (as described above).
  209. ;        OUTBUF    - An output buffer (as described above).
  210. ;
  211. ;        Take care to keep these definitions compatible with the actual
  212. ;    allocation of these data areas near the end of this program (just
  213. ;    before label "init:").
  214. ;
  215. U_base        equ    0
  216. U_stat        equ    2    ;Status bits, defined below.
  217. U_inbuf     equ    4
  218. U_outbuf    equ    U_inbuf+B_data+in_buf_len
  219. U_totlen    equ    U_outbuf+B_data+out_buf_len
  220. ;
  221. ; Bits in U_stat:
  222. ;
  223. US_outhold    equ    1    ;Host has sent us a "hold it".
  224. US_inhold    equ    2    ;We have sent the host a "hold it".
  225. US_saywait    equ    4    ;We need to send a "hold it".
  226. US_saygo    equ    8    ;We need to send an "OK to send" (un-holdit).
  227.         subttl    Device Headers:
  228.         page
  229. ;
  230. ; Here are the Device Headers:
  231. ;
  232. coms        proc    far
  233.         assume    cs:cseg,ds:cseg
  234. begin:
  235.  
  236. next_dev1    dw    DevHdr2     ;Pointer to next device.
  237.         dw    -1
  238. attribute1    dw    0c000h        ;Attributes.
  239. strategy1    dw    Com1Strat    ;Pointer to "device strategy" entry.
  240. interrupt1    dw    Com1Int     ;Pointer to software "interrupt" entry.
  241. dev_name1    db    'COM1     '
  242.  
  243. DevHdr2:
  244. next_dev2    dw    -1        ;Pointer to next device.
  245.         dw    -1
  246. attribute2    dw    0c000h        ;Attributes.
  247. strategy2    dw    Com2Strat    ;Pointer to "device strategy" entry.
  248. interrupt2    dw    Com2Int     ;Pointer to software "interrupt" entry.
  249. dev_name2    db    'COM2     '
  250.         subttl    Dispatch Table:
  251.         page
  252. ;
  253. ; Here is the dispatch table:
  254. ;
  255. dispatch    label    byte
  256.         dw    init        ;Initialization.
  257.         dw    MediaCheck    ;Media check (block devices only).
  258.         dw    BuildBPB    ;Build BPB  (block devices only).
  259.         dw    IoctlIn     ;IOCTL input.
  260.         dw    input        ;input (read).
  261.         dw    NDInput     ;Non-destructive input, no wait.
  262.         dw    InStat        ;Status for input.
  263.         dw    InFlush     ;Input flush.
  264.         dw    output        ;Output (write).
  265.         dw    OutVerify    ;Output with verify.
  266.         dw    OutStat     ;Status for output.
  267.         dw    OutFlush    ;Output flush.
  268.         dw    IoctlOut    ;IOCTL output.
  269.         subttl    Entry points:
  270.         page
  271. ;
  272. ; Device "Strategy" entry points:
  273. ;
  274. Com1Strat:
  275. Com2Strat:
  276.         mov    cs:ReqHdrSeg,es ;Segment of Request Header Pointer.
  277.         mov    cs:ReqHdrOff,bx ;Offset of Request Header Pointer.
  278.         ret
  279. ;
  280. ; Device "Interrupt" entry points:
  281. ;
  282. Com1Int:    push    ax
  283.         push    bx
  284.         xor    al,al        ;Unit # for COM1 is 0.
  285.         mov    bx,offset UCB1
  286.         jmp    JointInt
  287.  
  288. Com2Int:    push    ax
  289.         push    bx
  290.         mov    al,1        ;Unit # for COM2 is 1.
  291.         mov    bx,offset UCB2
  292.  
  293. JointInt:
  294.         push    cx
  295.         push    dx
  296.         push    di
  297.         push    si
  298.         push    ds
  299.         push    es
  300.  
  301.         push    cs
  302.         pop    ds        ;DS = CS.
  303.  
  304.         mov    unit,al     ;Save unit number.
  305.         mov    dx,[bx]     ;DX = base address for appropriate card.
  306.         les    bx,ReqHdr    ;Load ES and BX pointers to Request Hdr.
  307. ;
  308. ;        Dispatch through the Dispatch Table:
  309. ;
  310.         mov    al,es:[bx]+RH_command    ;Get function byte.
  311.         rol    al,1
  312.         lea    di,dispatch
  313.         xor    ah,ah
  314.         add    di,ax        ;Jump through dispatch table +
  315.         jmp    word ptr[di]    ; 2 * function code.
  316.         subttl    Return paths:
  317.         page
  318. ;
  319. ; Return "Unknown Unit" error code.
  320. ;
  321. BadUnit:    mov    ax,STAT_err+STAT_done+S_E_unkunit
  322.         jmp    SetStat
  323. ;
  324. ; Return "done" status.
  325. ;
  326. NormalDone:    mov    ax,STAT_done
  327. ;
  328. ; Store AX in the STATUS word of the Request Header, then restore registers
  329. ; and return.
  330. ;
  331. SetStat:    lds    bx,cs:ReqHdr
  332.         mov    [bx]+RH_status,ax
  333. ;
  334. ; Restore the original registers and return.
  335. ;
  336. RestRegsRet:
  337.         pop    es
  338.         pop    ds
  339.         pop    si
  340.         pop    di
  341.         pop    dx
  342.         pop    cx
  343.         pop    bx
  344.         pop    ax
  345.         ret
  346.         subttl    Command processors:
  347.         page
  348. ;
  349. ; IOCTL input.
  350. ;    Do the requested function.
  351. ;    Set the actual number of bytes transferred.
  352. ;    Set the status word in the Request header.
  353. ;
  354. IoctlIn:
  355.         call    CheckUnit
  356.         jc    BadUnit
  357.         jmp    NormalDone
  358.         page
  359. ;
  360. ; Input.
  361. ;    Do the requested function.
  362. ;    Set the actual number of bytes transferred.
  363. ;    Set the status word in the Request header.
  364. ;
  365. input:
  366.         mov    cx,es:[bx]+RH_count    ;CX = # bytes to transfer.
  367.         jcxz    NormalDone
  368.         les    di,es:[bx]+RH_buf_addr    ;ES:DI points to destination.
  369.         call    GetBase
  370.         jc    BadUnit
  371.         call    EnabInt
  372.         add    bx,U_inbuf    ;BX -> appropriate input buffer.
  373.         cld
  374. input1:     call    TakeByte        ;Get byte in AL.
  375.         jnc    input6            ;Jump if no more chars avail.
  376.         stosb                ;Store in es:[di++].
  377.         loop    input1            ;Continue.
  378. input6:
  379.         mov    ax,[bx]+U_stat-U_inbuf
  380.         test    ax,US_inhold
  381.         jz    input8            ;If the "I've sent CTRLS" flag
  382.           call    UsedBuff        ; is set and there are fewer
  383.           cmp    ax,near_empty        ; than near_empty data bytes
  384.           jns    input8            ; in the buffer, make a note
  385.                         ; to send a CTRLQ.
  386.             or    word ptr [bx]+U_stat-U_inbuf,US_saygo
  387. input8:
  388.         or    cx,cx            ;If we didn't transfer the
  389.         jz    NormalD2        ; requested number of chars,
  390.         les    bx,ReqHdr        ; calculate the number of
  391.         mov    ax,es:[bx]+RH_count    ; characters moved and store
  392.         sub    ax,cx            ; it in the Request Header.
  393.         mov    es:[bx]+RH_count,ax    ;
  394.         mov    ax,STAT_done+STAT_busy    ;Return BUSY.
  395.         jmp    SetStat
  396.         page
  397. ;
  398. ; Non-Destructive Input, No Wait.
  399. ;    Return a byte from the device.
  400. ;    Set the status word in the Request Header.
  401. ;
  402. NDInput:
  403.         call    GetBase
  404.         jc    BadUnit
  405.         call    EnabInt
  406.         add    bx,U_inbuf        ;BX -> appropriate input buff.
  407.         mov    ax,[bx]+B_in        ;Examine buffer's pointers.
  408.         cmp    ax,[bx]+B_out
  409.         jz    ndin1            ;Jump if buffer empty.
  410.           mov    si,ax
  411.           mov    al,[si]         ;Fetch a character.
  412.           mov    cx,STAT_done
  413.           jmp    ndin2
  414.  
  415. ndin1:        xor    al,al            ;No character available.
  416.         mov    cx,STAT_done+STAT_busy
  417.  
  418. ndin2:        les    bx,ReqHdr
  419.         mov    es:[bx]+RH_char,al
  420.         mov    ax,cx
  421.         jmp    SetStat
  422.         page
  423. ;
  424. ; Status for Input.
  425. ;    Perform the operation.
  426. ;    Set the status word in the Request Header.
  427. ;
  428. InStat:
  429.         call    GetBase
  430.         jc    BadUnit2
  431.         add    bx,U_inbuf        ;BX -> appropriate input buff.
  432.         mov    ax,STAT_done
  433.         mov    cx,[bx]+B_in        ;Examine buffer's pointers.
  434.         cmp    cx,[bx]+B_out
  435.         jnz    inst1            ;Jump if buffer not empty.
  436.           or    ax,STAT_busy        ;If empty, say "busy".
  437. inst1:        jmp    SetStat
  438. ;
  439. ; Input Flush.
  440. ;    Perform the operation.
  441. ;    Set the status word in the Request Header.
  442. ;
  443. InFlush:
  444.         call    GetBase     ;BX -> appropriate input buff.
  445.         jc    BadUnit2
  446.         add    bx,U_inbuf
  447.         mov    ax,[bx]+B_in    ;Set the "out" pointer equal to
  448.         mov    [bx]+B_out,ax    ; the "in" pointer.
  449. NormalD2:    jmp    NormalDone
  450.         page
  451. ;
  452. ; Output (write) and Output with Verify.
  453. ;    Do the requested function.
  454. ;    Set the actual number of bytes transferred.
  455. ;    Set the status word in the Request header.
  456. ;
  457. output:
  458. OutVerify:
  459.         mov    cx,es:[bx]+RH_count    ;CX = # bytes to transfer.
  460.         jcxz    NormalD2
  461.         les    si,es:[bx]+RH_buf_addr    ;ES:SI points to source.
  462.         call    GetBase
  463.         jc    BadUnit2
  464.         add    bx,U_outbuf        ;BX -> appropriate output buf.
  465.         cld
  466. output1:    lods    es:byte ptr [si]    ;Get 1 char in AL.
  467.         call    PutByte         ;Put character in output buff.
  468.         jnc    output8         ;Jump if no room in output buffer.
  469.         loop    output1
  470. output8:    sub    bx,U_outbuf        ;BX -> appropriate UCB.
  471.         call    EnabInt         ;Enable interrupts, if approp.
  472.         or    cx,cx            ;If we didn't transfer the
  473.         jz    NormalD2        ; requested number of chars,
  474.         les    bx,ReqHdr        ; calculate the number of
  475.         mov    ax,es:[bx]+RH_count    ; characters moved and store
  476.         sub    ax,cx            ; it in the Request Header.
  477.         mov    es:[bx]+RH_count,ax    ;
  478.         mov    ax,STAT_done+STAT_busy    ;Return BUSY.
  479.         jmp    SetStat
  480. BadUnit2:    jmp    BadUnit
  481.         page
  482. ;
  483. ; Status for Output.
  484. ;    Perform the operation.
  485. ;    Set the status word in the Request Header.
  486. ;
  487. OutStat:
  488.         call    GetBase
  489.         jc    BadUnit2
  490.         add    bx,U_outbuf        ;BX -> appropriate output buf.
  491.         call    IsRoom            ;Is there room in the buffer?
  492.         mov    ax,STAT_done
  493.         jc    outst1            ;Jump if buffer not full.
  494.           or    ax,STAT_busy        ;If full, say "busy".
  495. outst1:     jmp    SetStat
  496. ;
  497. ; Output Flush.
  498. ;    Perform the operation.
  499. ;    Set the status word in the Request Header.
  500. ;
  501. OutFlush:
  502.         call    CheckUnit
  503.         jc    BadUnit2
  504.         jmp    NormalDone
  505. ;
  506. ; IOCTL output.
  507. ;    Do the requested function.
  508. ;    Set the actual number of bytes transferred.
  509. ;    Set the status word in the Request header.
  510. ;
  511. IoctlOut:
  512.         call    CheckUnit
  513.         jc    BadUnit2
  514.         jmp    NormalDone
  515.         page
  516. ;
  517. ; Entry points for functions not implemented:
  518. ;
  519. NotImplemented:
  520. MediaCheck:
  521. BuildBPB:
  522.         mov    ax,STAT_err+STAT_done+S_E_unkcom
  523.         jmp    SetStat
  524.         subttl    Interrupt handlers:
  525.         page
  526. ;
  527. ; Hardware interrupt handlers.
  528. ;    We run with two interrupts enabled:
  529. ;        Data Available
  530. ;        Transmit Holding Register Empty.
  531. ;
  532. ;    If Data Available is set
  533. ;        If there's room in the IN buffer, put the byte there.
  534. ;    If there are data in the OUT buffer,
  535. ;        If the Transmit Holding Register is empty, output a byte.
  536. ;
  537. Hdw1Int:
  538.         push    ax
  539.         mov    ax,offset UCB1
  540.         jmp    HdwInt
  541.  
  542. Hdw2Int:
  543.         push    ax
  544.         mov    ax,offset UCB2
  545.  
  546. HdwInt:
  547.         push    bx
  548.         push    ds
  549.         push    cx
  550.         push    dx
  551.         push    di
  552.         push    si
  553.         push    es
  554.  
  555.         push    cs
  556.         pop    ds
  557.  
  558.         mov    bx,ax
  559.         mov    di,[bx]     ;DI = base I/O address.
  560.         lea    dx,R_lc[di]
  561.         in    al,dx        ; the Line Control Register.
  562.         and    al,255-LC_dlab
  563.         out    dx,al
  564.         lea    dx,R_ls[di]
  565.         in    al,dx        ;Read the Line Status Reg.
  566.         test    al,LS_dr    ;"Data Ready" set?
  567.         jz    HdwI2
  568.           push    ax
  569.           call    ReadByte    ;Process the incoming byte.
  570.           pop    ax
  571. HdwI2:        test    al,LS_thre    ;"Xmit Holding Reg Empty" set?
  572.         jz    HdwI3
  573.                     ;If we want to send a CTRLS or
  574.                     ; CTRLQ to the host, this is our
  575.                     ; chance to do it.
  576.                     ;Note that, if both are set, the
  577.                     ; CTRLS is not sent.
  578.                     ;
  579.           test    word ptr [bx]+U_stat,US_saywait+US_saygo
  580.           jz    HdwI5
  581.             test   word ptr [bx]+U_stat,US_saygo
  582.             jz       HdwI6
  583.               mov  al,CTRLQ
  584.               and  word ptr [bx]+U_stat,0ffffh-(US_saywait+US_saygo+US_inhold)
  585.               jmp  HdwI7
  586. HdwI6:            ;else
  587.               mov  al,CTRLS
  588.               and  word ptr [bx]+U_stat,0ffffh-(US_saywait+US_saygo)
  589.               or   word ptr [bx]+U_stat,US_inhold
  590. HdwI7:            mov    dx,di
  591.             out    dx,al
  592.  
  593.             jmp    HdwI3
  594.                     ; If we get here, we didn't want to
  595.                     ; send a CTRLS or CTLRQ.
  596. HdwI5:
  597.           test    word ptr [bx]+U_stat,US_outhold
  598.           jnz    HdwI3        ;Jump if output held by CTRL-S.
  599.             add     bx,U_outbuf
  600.             call    TakeByte
  601.             jnc     HdwI4        ;Jump if nothing to output.
  602.               mov    dx,di
  603.               out    dx,al        ;Output another character.
  604. HdwI4:            sub bx,U_outbuf
  605. HdwI3:          ;;;
  606.         pop    es
  607.         pop    si
  608.         pop    di
  609.         pop    dx
  610.         pop    cx
  611.         cli
  612.         mov    al,ICC_eoi    ;Issue "end-of-interrupt" command
  613.         out    ICC_ctl,al    ; to the interrupt-controller chip.
  614.         call    EnabInt     ;Re-enable interrupts at the UART.
  615.         pop    ds
  616.         pop    bx
  617.         pop    ax
  618.         iret
  619.         page
  620. ;
  621. ; Process an incoming character.
  622. ;    Pick up the character in the device.
  623. ;    If it's a CTRLS or CTRLQ,
  624. ;        set or clear the "he said hold" flag for this device;
  625. ;    otherwise
  626. ;        put it into the buffer.
  627. ;
  628. ; Enter with:
  629. ;    BX    pointing to the UCB.
  630. ;    DI    containing the base address for the device.
  631. ;
  632. ; Clobbers:    AX, DX.
  633. ;
  634. ; Side effects:
  635. ;        May set or clear the low-order bit of the "hold" word
  636. ;        for this device.
  637. ;
  638. ReadByte    proc    near
  639.         lea    dx,R_rx[di]
  640.         in    al,dx        ;Read the data byte.
  641.         cmp    al,CTRLS    ;Watch for XOFF and XON (CTRLS and
  642.         jnz    ReadB1        ; CTRLQ).
  643.           mov    ax,US_outhold
  644.           or    word ptr [bx]+U_stat,ax
  645.           ret
  646.  
  647. ReadB1:     cmp    al,CTRLQ
  648.         jnz    ReadB3
  649.           and    word ptr [bx]+U_stat,0ffffh-US_outhold
  650.           ret
  651.  
  652. ReadB3:     add    bx,U_inbuf
  653.         call    PutByte     ;Put it into the buffer.
  654.         call    UsedBuff
  655.         cmp    ax,near_full    ;If the buffer is getting full,
  656.         js    ReadB4        ; and we haven't already asked the
  657.                     ; host to wait, do that.
  658.           test    word ptr [bx]+U_stat-U_inbuf,US_inhold
  659.           jnz    ReadB4
  660.             or    word ptr [bx]+U_stat-U_inbuf,US_saywait
  661. ReadB4:
  662.         sub    bx,U_inbuf
  663.         ret
  664. ReadByte    endp
  665.         subttl    General-Purpose Subroutines:
  666.         page
  667. ;
  668. ; General-purpose subroutines.
  669. ;
  670. ; ----------------------------------------------------------------------
  671. ;
  672. ; Return a pointer to the "base" of the data block for this unit.
  673. ;
  674. ; Enter with: unit number (0 or 1) in the variable "unit".
  675. ;    DS = CS.
  676. ;
  677. ; Returns: BX containing the offset of either UCB1 or UCB2.
  678. ;
  679. GetBase     proc    near
  680.         call    CheckUnit
  681.         jnc    gb0
  682.         ret
  683. gb0:        test    al,0ffh
  684.         jz    gb1
  685.         mov    bx,offset UCB2
  686.         ret
  687. gb1:        mov    bx,offset UCB1
  688.         ret
  689. GetBase     endp
  690. ;
  691. ; ----------------------------------------------------------------------
  692. ;
  693. ; Check the unit number to make sure it's valid.
  694.  
  695. ; Enter with:
  696. ;    the variable "unit" set to the unit number to be checked;
  697. ;    the variable "maxunit" set to the maximum unit number allowed.
  698. ;
  699. ; Returns with:
  700. ;    "carry" condition set if "unit" < 0 or "unit" > "maxunit".
  701. ;    AL = contents of the variable "unit".
  702. ;
  703. CheckUnit    proc    near
  704.         mov    al,unit
  705.         or    al,al
  706.         js    RetCarry
  707.         test    maxunit,080h
  708.         jnz    RetCarry
  709.         cmp    al,maxunit
  710.         jg    RetCarry
  711.         clc
  712.         ret
  713. RetCarry:    stc
  714.         ret
  715. CheckUnit    endp
  716.         page
  717. ;
  718. ; ----------------------------------------------------------------------
  719. ;
  720. ; Take a byte out of a buffer and return it in AL.
  721. ;
  722. ; Enter with:
  723. ;    BX pointing to the buffer from which a byte is to be taken.
  724. ;
  725. ; Returns:
  726. ;    Carry condition and a byte in AL if the buffer was not empty,
  727. ;    NoCarry condition if the buffer was empty.
  728. ;
  729. ; Side effect: The "output" pointer for the buffer is advanced to reflect
  730. ;    the used-upness of the character taken.
  731. ;
  732. ; Note:
  733. ;    Since this subroutine may be interrupted, it is important that
  734. ;    the character be taken out of the buffer before the OUT pointer
  735. ;    is advanced to reflect its absence.
  736. ;
  737. TakeByte    proc    near
  738.         mov    ax,[bx]+B_out        ;Output pointer.
  739.         cmp    ax,[bx]+B_in        ; = input pointer?
  740.         jz    tb9            ;If equal, buffer is empty.
  741.           push    si
  742.           push    cx
  743.           mov    si,ax
  744.           mov    cl,[si]         ;Fetch the character.
  745.           call    advance         ;Advance the "out" pointer.
  746.           mov    [bx]+B_out,ax        ;Store updated ptr.
  747.           mov    al,cl
  748.           pop    cx
  749.           pop    si
  750.           stc                ;Set carry flag.
  751. tb9:        ret
  752. TakeByte    endp
  753.         page
  754. ;
  755. ; ----------------------------------------------------------------------
  756. ;
  757. ; Put a character into a buffer.
  758. ;
  759. ; Enter with:
  760. ;    BX pointing to the beginning of a buffer,
  761. ;    AL holding the character to be put into the buffer.
  762. ;
  763. ; Returns:
  764. ;    NoCarry condition if the buffer is full (character couldn't be put).
  765. ;    Carry condition if the character is successfully put into the buffer.
  766. ;
  767. ; Note:
  768. ;    Since this subroutine may be interrupted, it is important that
  769. ;    the character be put into the buffer before the IN pointer is
  770. ;    adjusted to reflect its availablility.
  771. ;
  772. PutByte     proc    near
  773.         push    cx
  774.         push    di
  775.         mov    cl,al        ;CL = the character.
  776.         mov    ax,[bx]+B_in
  777.         mov    di,ax        ;DI = original IN pointer.
  778.         call    advance     ;AX = updated IN pointer.
  779.         cmp    ax,[bx]+B_out
  780.         jz    pb9        ;Jump if buffer is full.
  781.           mov    [di],cl     ;Store the character.
  782.           mov    [bx]+B_in,ax    ;Update the pointer.
  783.           stc
  784. pb9:        pop    di
  785.         pop    cx
  786.         ret
  787. PutByte     endp
  788.         page
  789. ;
  790. ; ----------------------------------------------------------------------
  791. ;
  792. ; Is there room in this buffer?
  793. ;
  794. ; Enter with BX pointing to the beginning of a buffer.
  795. ;
  796. ; Returns:
  797. ;    NoCarry condition if the buffer is full,
  798. ;    Carry condition if there is room in the buffer.
  799. ;
  800. IsRoom        proc    near
  801.         mov    ax,[bx]+B_in
  802.         call    advance
  803.         cmp    ax,[bx]+B_out
  804.         jz    ir9
  805.         stc
  806. ir9:        ret
  807. IsRoom        endp
  808. ;
  809. ; ----------------------------------------------------------------------
  810. ;
  811. ; How many data bytes are in this buffer?
  812. ;
  813. ; Enter with BX pointing to the beginning of a buffer.
  814. ;
  815. ; Returns:
  816. ;        AX containing the number of bytes used in the buffer.
  817. ;        Z flag set if AX contains 0.
  818. ;
  819. UsedBuff    proc    near
  820.         mov    ax,[bx]+B_in
  821.         sub    ax,[bx]+B_out
  822.         jns    ub9
  823.         add    ax,[bx]+B_end
  824.         sub    ax,[bx]+B_beg
  825. ub9:        ret
  826. UsedBuff    endp
  827.         page
  828. ;
  829. ; ----------------------------------------------------------------------
  830. ;
  831. ; Advance a buffer pointer.
  832. ;
  833. ; Enter with
  834. ;    BX pointing to the beginning of the buffer.
  835. ;    AX containing a pointer into the buffer.
  836. ;
  837. ; Returns with
  838. ;    AX incremented by one, wrapped to the beginning of the buffer
  839. ;        if necessary.
  840. ;
  841. advance     proc    near
  842.         inc    ax
  843.         cmp    ax,[bx]+B_end
  844.         jnz    adv9
  845.         mov    ax,[bx]
  846. adv9:        ret
  847. advance     endp
  848. ;
  849. ; ----------------------------------------------------------------------
  850. ;
  851. ; Reset the input-buffer pointers for one unit.
  852. ;
  853. ; Enter with BX pointing to the Unit Control Block for the unit whose input-
  854. ;    buffer pointers are to be cleared.
  855. ;
  856. ResInbuf    proc    near
  857.         mov    ax,[bx]+U_inbuf+B_beg
  858.         mov    [bx]+U_inbuf+B_in,ax
  859.         mov    [bx]+U_inbuf+B_out,ax
  860.         ret
  861. ResInbuf    endp
  862. ;
  863. ; ----------------------------------------------------------------------
  864. ;
  865. ; Reset the output-buffer pointers for one unit.
  866. ;
  867. ; Enter with BX pointing to the Unit Control Block for the unit whose output-
  868. ;    buffer pointers are to be cleared.
  869. ;
  870. ResOutbuf    proc    near
  871.         mov    ax,[bx]+U_outbuf+B_beg
  872.         mov    [bx]+U_outbuf+B_in,ax
  873.         mov    [bx]+U_outbuf+B_out,ax
  874.         ret
  875. ResOutbuf    endp
  876.         page
  877. ;
  878. ; ----------------------------------------------------------------------
  879. ;
  880. ; Enable appropriate interrupts.
  881. ;
  882. ; Enter with:
  883. ;    BX pointing to the Unit Control Block controlling the device
  884. ;    whose interrupts are to be enabled.
  885. ;
  886. EnabInt     proc    near
  887.         push    cx
  888.         push    dx
  889.         push    di
  890.         xor    cx,cx
  891.         add    bx,U_inbuf
  892.         call    IsRoom
  893.         jnc    ei1        ;If there's room in the input buffer,
  894.           or    cl,IE_da    ; enable "data available" interrupt.
  895. ei1:        sub    bx,U_inbuf
  896.  
  897.                     ;Enable the "transmit holding register
  898.                     ; empty" interrupt if:
  899.                     ; - there's something in the output
  900.                     ;   buffer, and output is not being
  901.                     ;   "held"; or
  902.                     ; - we want to send a CTRLS or CTRLQ.
  903.  
  904.         mov    ax,[bx]+U_outbuf+B_in
  905.         cmp    ax,[bx]+U_outbuf+B_out
  906.         mov    ax,[bx]+U_stat
  907.         jz    ei2
  908.           test    ax,US_outhold
  909.           jz    ei4
  910. ei2:        ;;;
  911.         test    ax,US_saygo+US_saywait
  912.         jz    ei3
  913. ei4:        or    cl,IE_thre
  914. ei3:
  915.         mov    di,[bx]
  916.         lea    dx,R_lc[di]
  917.         in    al,dx        ;Turn off DLAB (just in case).
  918.         and    al,255-LC_dlab
  919.         out    dx,al
  920.         lea    dx,R_ie[di]
  921.         mov    al,0
  922.         out    dx,al        ;Interrupt Enable = 0 (to ensure a
  923.         mov    al,cl        ; transition).
  924.         out    dx,al        ;Set final Interrupt Enable.
  925.  
  926.         lea    dx,R_mc[di]        ;Turn the enabling "out2" bit
  927.         mov    al,MC_out2+MC_rts+MC_dtr ; on.
  928.         out    dx,al
  929.  
  930.         pop    di
  931.         pop    dx
  932.         pop    cx
  933.         ret
  934. EnabInt     endp
  935.         subttl    Storage:
  936.         page
  937. ;
  938. ; Storage for both COM1 and COM2.
  939. ;
  940. ReqHdr        dd    ?    ;The Request Header:
  941. ReqHdrOff    equ    word ptr ReqHdr
  942. ReqHdrSeg    equ    ReqHdrOff+2
  943. ;
  944. ;
  945. unit        db    ?        ;0 for COM1, 1 for COM2.
  946.                     ;(Set according to which "interrupt"
  947.                     ; entry point is used.)
  948. maxunit     db    -1        ;Maximum unit number allowed.
  949.                     ;(Set during INIT.)
  950. end0:                ;Ending address if no asynch cards found.
  951. ;
  952. ; Unit Control Block for COM1:
  953. ; Keep this space allocation consistent with the definitions of U_inbuf,
  954. ; B_end, etc.
  955. ;
  956. UCB1:
  957. base_1:     dw    ?        ;Base address for COM1.
  958.                     ;(Set during INIT.)
  959. hold_1:     dw    ?
  960. inb1_beg:    dw    offset inb1
  961. inb1_end:    dw    offset inb1+in_buf_len
  962. inb1_in:    dw    ?
  963. inb1_out:    dw    ?
  964. inb1:        db    in_buf_len dup(?)
  965.  
  966. outb1_beg:    dw    offset outb1
  967. outb1_end    dw    offset outb1+out_buf_len
  968. outb1_in:    dw    ?
  969. outb1_out:    dw    ?
  970. outb1:        db    out_buf_len dup(?)
  971. end1:                ;Ending address if only 1 asynch card found.
  972. ;
  973. ; Unit Control Block for COM2.
  974. ; Keep this space allocation consistent with the definitions of U_inbuf,
  975. ; B_end, etc.
  976. ;
  977. UCB2:
  978. base_2:     dw    ?        ;Base address for COM2.
  979.                     ;(Set during INIT.)
  980. hold_2:     dw    ?
  981. inb2_beg:    dw    offset inb2
  982. inb2_end:    dw    offset inb2+in_buf_len
  983. inb2_in:    dw    ?
  984. inb2_out:    dw    ?
  985. inb2:        db    in_buf_len dup(?)
  986.  
  987. outb2_beg:    dw    offset outb2
  988. outb2_end    dw    offset outb2+out_buf_len
  989. outb2_in:    dw    ?
  990. outb2_out:    dw    ?
  991. outb2:        db    out_buf_len dup(?)
  992. ;
  993. end2:                ;Ending address if 2 asynch cards found.
  994.         subttl    Initialization:
  995.         page
  996. ;
  997. ; Initialization:
  998. ;    Perform any initialization code.
  999. ;        If COM2, skip this part.
  1000. ;        Fill base_1 and base_2, as appropriate.
  1001. ;        Initialize pointers to input & output buffers.
  1002. ;        Install our hardware interrupt vectors.
  1003. ;    Set up the ending address for resident code.
  1004. ;    Set the status word in the Request Header.
  1005. ;
  1006. init:
  1007.         mov    ax,endaddr    ;Have we been called before?
  1008.         or    ax,ax
  1009.         jnz    initend     ;Skip this part if so.
  1010.           mov    dx,primary
  1011.           mov    bx,offset base_1
  1012.           call    InitUCB
  1013.           call    HaveUnit        ;Is "primary" card there?
  1014.           jnz    init2
  1015.             push dx            ;Assign our interrupt handler
  1016.             mov  dx,NextHandler     ; to its interrupt.
  1017.             mov  al,prim_int
  1018.             mov  ah,25h
  1019.             int  21h
  1020.             mov  ax,offset Hdw2Int    ;Use other interrupt handler
  1021.             mov  NextHandler,ax     ; next time.
  1022.             pop  dx
  1023.             mov [bx],dx         ;Use the first UCB
  1024.             call EnabInt        ; for it.
  1025.             mov bx,offset base_2
  1026.             call InitUCB
  1027.             inc maxunit
  1028. init2:          mov    dx,secondary
  1029.           call    HaveUnit        ;Is "secondary" card there?
  1030.           jnz    init3
  1031.             push dx            ;Assign our interrupt handler
  1032.             mov  dx,NextHandler     ; to its interrupt.
  1033.             mov  al,sec_int
  1034.             mov  ah,25h
  1035.             int  21h
  1036.             pop  dx
  1037.             mov [bx],dx         ;Use for it whichever
  1038.             call EnabInt        ; UCB is indicated by
  1039.             inc maxunit         ; BX.
  1040. init3:          mov    bl,maxunit
  1041.           inc    bl
  1042.           xor    bh,bh
  1043.           rol    bl,1
  1044.           mov    ax,[bx]+endtable
  1045.           mov    endaddr,ax
  1046. initend:    mov    ax,endaddr    ;Store the ending address.
  1047.         lds    bx,cs:ReqHdr
  1048.         mov    [bx]+RH_end_addr,ax
  1049.         mov    [bx]+RH_end_addr+2,cs
  1050.  
  1051.         in    al,ICC_msk    ;Tell the interrupt controller chip
  1052.         and    al,255-(MSK_d1+MSK_d2)    ; to allow these interrupts.
  1053.         out    ICC_msk,al
  1054.  
  1055.         jmp    NormalDone
  1056. ;
  1057. ; Is there an asynch card answering at this address?
  1058. ;
  1059. ; Enter with:
  1060. ;    DX holding the base I/O address to be interrogated.
  1061. ;
  1062. ; Returns:
  1063. ;    Z condition if there is a card there,
  1064. ;    NZ condition if there is NO card there.
  1065. ;
  1066. HaveUnit    proc    near
  1067.         add    dx,R_ii
  1068.         in    al,dx        ;Read the Interrupt Identification
  1069.         sub    dx,R_ii     ; register.
  1070.         and    al,II_never    ;If there's a device there, these bits
  1071.         ret            ; will be zero.
  1072. HaveUnit    endp
  1073. ;
  1074. ; Initialize a Unit Control Block (UCB).
  1075. ;
  1076. ; Enter with
  1077. ;    BX pointing to the data block for the channel whose input
  1078. ;        and output buffer pointers are to be reset.
  1079. ;
  1080. InitUCB     proc    near
  1081.         mov    word ptr [bx]+U_stat,0
  1082.         call    ResInbuf
  1083.         call    ResOutbuf
  1084.         ret
  1085. InitUCB     endp
  1086. ;
  1087. endaddr:    dw    0        ;END address returned on INIT call.
  1088. endtable:    dw    offset end0    ;END address if 0 asynch cards found.
  1089.         dw    offset end1    ;END address if 1 asynch cards found.
  1090.         dw    offset end2    ;END address if 2 asynch cards found.
  1091. NextHandler:    dw    offset Hdw1Int    ;Successive interrupt-handler addresses.
  1092. coms        endp
  1093. cseg        ends
  1094.         end    begin
  1095.