home *** CD-ROM | disk | FTP | other *** search
/ Phoenix CD 2.0 / Phoenix_CD.cdr / 01e / msjall.zip / MSJV2-3.ZIP / TSRCOMM.ASM < prev   
Assembly Source File  |  1987-10-30  |  63KB  |  2,075 lines

  1. Microsoft Systems Journal
  2. Volume 2; Issue 3; July, 1987
  3.  
  4. Code Listings For:
  5.  
  6.     TSRCOMM.ASM
  7.     pp. 37-50
  8.  
  9. Author(s): Ross M. Greenberg
  10. Title:     Keeping Up With The Real World: Speedy Serial I/O Processing
  11.  
  12.  
  13.  
  14.  
  15. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  16. ;;    TSRCOMM.ASM        A Replacement for Interrupt 14
  17. ;;
  18. ;;    Copyright    1987, Ross M. Greenberg
  19. ;;
  20. ;;    This program is a terminate and stay resident program which allows
  21. ;;    computers such as the IBM PC and compatibles using standard serial
  22. ;;    communications calls to take advantage of the asynchronous interrupt
  23. ;;    capabilities of the 8250 and 8259.
  24. ;;
  25. ;;    The functionality with AH=0,1,2,3 remains the same as always
  26. ;;
  27. ;;    With AH = 4, a new set of commands are now available.
  28. ;;    Sub-functions are set in AL (See below for new function descriptions)
  29. ;;
  30. ;;    To compile this program, you must have MASM 4.0 or a close relative,
  31. ;;    and need only do:
  32. ;;
  33. ;;    C> MASM TSRCOMM;
  34. ;;    C> LINK TSRCOMM;
  35. ;;    C> EXE2BIN TSRCOMM.EXE TSRCOMM.COM
  36. ;;    C> DEL TSRCOMM.EXE
  37. ;;
  38. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  39.  
  40. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  41. ;;
  42. ;;                MACRO DEFINITIONS
  43. ;;
  44. DOSINT    macro    set_ah_to
  45.     mov    ah, set_ah_to
  46.     int    21h
  47. endm
  48. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  49.  
  50.  
  51. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  52. ;;
  53. ;;                CONSTANTS
  54. ;;
  55. FALSE        equ    00h
  56. TRUE        equ    01h    
  57. BELL        equ    07h
  58. ONE_SECOND    equ    18        ; roughly 18 timer ticks
  59. TWO_SECONDS    equ    ONE_SECOND * 2    ; same idea
  60. THIRTY_SECONDS    equ    ONE_SECOND * 30 ;
  61.  
  62.  
  63.  
  64. TIMER_TICK_INT_NO    equ    1ch    ; some might set this to 08h
  65.  
  66.  
  67. ;;    Send an XOFF when you wish the remote to stop sending and
  68. ;;    send an XON when the remote is allowed to continue
  69. ;;
  70.  
  71. XOFF    equ    13h    ; a control-S
  72. XON    equ    11h    ; a control-Q
  73.  
  74.  
  75.  
  76. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  77. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  78. ;;    Define the size of the Communications Buffers
  79. ;;    
  80. ;;    You may want to change these to more accurately reflect your
  81. ;;    actual needs.  The high-water and low-water mark for XON/XOFF
  82. ;;    processing are a function of the size of these two variables
  83.  
  84. P1_INLEN    equ    400h
  85. P2_INLEN    equ    400h
  86. P1_OUTLEN    equ    400h
  87. P2_OUTLEN    equ    400h
  88.  
  89. ;; Be careful with these settings if your have different lengths for each
  90. ;; of the COM_INBUF's: these only play off of COM1_INBUF
  91.  
  92. HIGH_MARK    equ    (P1_INLEN/10 * 8)    ; send XOFF when buffer is
  93.                         ; 80% full
  94. LOW_MARK    equ    (P1_INLEN/10 * 2)    ; send XON when buffer is
  95.                         ; only 20% full
  96. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  97. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  98.  
  99.  
  100. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  101. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  102. ;;
  103. ;;    Definitions of all of the 8250 Registers and their individual
  104. ;;    bit meanings
  105. ;;
  106. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  107. DATA        equ    0h    ; DATA I/O is from the base
  108.  
  109. IER        equ    1h    ; Interrupt Enable Register
  110.  IER_RDA    equ    1h    ;  Received Data Available interrupt bit
  111.  IER_THRE    equ    2h    ;  Transmitter Holding Reg. Empty int bit
  112.  IER_RLS    equ    4h    ;  Receive Line Status int bit
  113.  IER_MS        equ    8h    ;  Modem Status int bit
  114.  
  115. IIR        equ    2    ; Interrupt Identification Register
  116.  IIR_RLS    equ    5h    ;  *equal* to if Receiver Line Status int
  117.  IIR_RDA    equ    4h    ;  *equal* to if character ready
  118.  IIR_THRE    equ    2h    ;  *equal* to if TX Buffer empty
  119.  IIR_PEND    equ    1h    ;  set to zero if any interrupt pending
  120.  IIR_MS        equ    0h    ;  *equal* to if Modem Status int
  121.  
  122. LCR        equ    3h    ; Line Control Register
  123.  LCR_WLS0    equ    0h    ;  Word Length Select Bit 0
  124.  LCR_WLS1    equ    1h    ;  Word Length Select Bit 1
  125.  LCR_STOPBITS    equ    4h    ;  number of stop bits
  126.  LCR_PARITYEN    equ    8h    ;  Enable Parity (see SPARITY & EPARITY)
  127.  LCR_EPARITY    equ    10h    ;  Even Parity Bit
  128.  LCR_SPARITY    equ    20h    ;  Stick Parity
  129.  LCR_BREAK    equ    40h    ;  set if break is desired
  130.  LCR_DLAB    equ    80h    ;  Divisor Latch Access Bit (baudrate setting)
  131.  
  132. MCR        equ    4h    ; Modem Control Register
  133.  MCR_DTR    equ    1h    ;  Data Terminal Ready
  134.  MCR_RTS    equ    2h    ;  Request To Send
  135.  MCR_OUT1    equ    4h    ;  Output 1 (nobody uses this!)
  136.  MCR_OUT2    equ    8h    ;  Out 2 (Sneaky Int enable in hware gates!)
  137.  MCR_LOOP    equ    10h    ;  Loopback enable
  138.  
  139. LSR        equ    5    ; Line Status Register
  140.   LSR_DATA    equ    1h    ;  Data Ready Bit
  141.   LSR_OVERRUN    equ    2h    ;  Overrun Error Bit
  142.   LSR_PARITY    equ    4h    ;  Parity Error Bit
  143.   LSR_FRAMING    equ    8h    ;  Framing Error Bit
  144.   LSR_BREAK    equ    10h    ;  Break Detect (sometimes an error!)
  145.   LSR_THRE    equ    20h    ;  Transmit Holding Register Empty
  146.   LSR_TSRE    equ    40h    ;  Transmit Shift Register Empty
  147.  
  148. MSR        equ    6    ; Modem Status Register
  149.   MSR_DEL_CTS    equ    1h    ;  Delta Clear To Send
  150.   MSR_DEL_DSR    equ    2h    ;  Delta Data Set Ready
  151.   MSR_EDGE_RI    equ    4h    ;  Trailing Edge of Ring Indicator
  152.   MSR_DEL_SIGD    equ    8h    ;  Delta Receive Line Signal Detect (delta DCD)
  153.   MSR_CTS    equ    10h    ;  Clear To Send
  154.   MSR_DSR    equ    20h    ;  Data Set Ready
  155.   MSR_RI    equ    40h    ;  Ring Indicator - during the entire ring
  156.   MSR_DCD    equ    80h    ;  Data Carrier Detect - Someone On-line
  157. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  158. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  159.  
  160. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  161. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  162. ;;    The 8259 interrupt controller is at port I/O INT_CTRL
  163. ;;    To reset from an interrupt, you must output an INT_EOI.
  164. ;;
  165. ;;    To enable or disable interrupts for either of the com ports, read
  166. ;;    the value on port INT_MASK_PORT, turn the appropriate COM1 or COM2 bit
  167. ;;    on or off, and output the new mask back out the port.
  168. ;;
  169. ;;
  170. CTRL_PORT    equ    20h
  171. INT_EOI        equ    20h
  172. ;;
  173. INT_MASK_PORT    equ    21h
  174. COM1_MASK    equ    0efh
  175. COM2_MASK    equ    0f7h
  176. INTNO_COM1    equ    0ch
  177. INTNO_COM2    equ    0bh
  178. ;;
  179. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  180. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  181.  
  182. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  183. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  184. ;;    These ports may or may not respond to the ports at base 40:0.
  185. ;;    If they don't we'll refuse to run.  Not at all subtle, but effective.
  186. ;;    See the first Sidebar for more information
  187. ;;
  188. PORT1    equ    3f8h
  189. PORT2    equ    2f8h
  190. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  191. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  192.  
  193.  
  194. code    segment    para        ; align the code segment to the nearest
  195.                 ; paragraph boundary
  196. assume    cs:code
  197. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  198. org    2ch
  199. env_ptr    label    word        ; this points to the environment address
  200.                 ; as stored in the PSP. We'll free this
  201.                 ; in the startup routine.
  202.  
  203. org    100h            ; COM programs always start at 100h
  204.                 ; just like CP/M!
  205.  
  206. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  207. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  208. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  209. ;;                START OF TSR CODE
  210. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  211. start:    jmp    install
  212.  
  213.  
  214.  
  215. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  216. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  217. ;;            COMM PORT BLOCK    (CPB)
  218. ;;
  219. ;;    Comm Port Block defines information unique for each comm port
  220. ;;    and includes information such as what the original interrupt
  221. ;;    vector pointed to, which parameters are set, etc.
  222. ;;
  223. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  224. CPB    struc
  225.  
  226. cpb_base    dw    ?    ; the base port of the comm port (2F8, 3F8)
  227. cpb_nint_off    dw    ?    ; new interrupt offset address
  228. cpb_pic_mask    db    ?    ; mask for enabling ints from 8259
  229. cpb_int_no    db    ?    ; what interrupt we are
  230.  
  231. cpb_mode    dw    ?    ; whatever modes we have turned on
  232. cpb_timeout    dw    ?    ; individual timeout value off timer tick
  233. cpb_in_xoff    dw    0    ; true if we output an XOFF
  234. cpb_out_xoff    dw    0    ; true if an XOFF was sent to us
  235.  
  236. cpb_inbase    dw    ?    ; start of input buffer
  237. cpb_inlen    dw    ?    ; total length of input buffer allocated
  238. cpb_inhead    dw    ?    ; pointer to next input char location
  239. cpb_intail    dw    ?    ; pointer to last input char location
  240. cpb_incnt    dw    0    ; count of how many inp characters outstanding
  241. cpb_inerrors    dw    ?    ; pointer to the error bits
  242.  
  243. cpb_outbase    dw    ?    ; start of output header
  244. cpb_outlen    dw    ?    ; total length of output buffer allocated
  245. cpb_outhead    dw    ?    ; pointer to next output char location
  246. cpb_outtail    dw    ?    ; pointer to last output char location
  247. cpb_outcnt    dw    0    ; count of how many outp characters outstanding
  248. cpb_outend    dw    ?    ; ptr to the end of the output buffer
  249.  
  250. cpb_tx_stat    dw    0    ; set to no interrupts turned on
  251. cpb_oint_add    dw    ?    ; original interrupt offset:segment order
  252.         dw    ?
  253. CPB    ends
  254. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  255.  
  256.  
  257. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  258. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  259. ;;            HANDSHAKING OPTIONS
  260. ;;    Used for AH=4, AL =1.  Set CX as a combination of the desired options
  261. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  262. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  263. BREAK_IS_ERROR_OPTION    equ    01h
  264. DSR_INPUT_OPTION    equ    02h
  265. DCD_INPUT_OPTION    equ    04h
  266. CTS_OUTPUT_OPTION    equ    08h
  267. XOFF_INPUT_OPTION    equ    10h
  268. XOFF_OUTPUT_OPTION    equ    20h
  269. DTR_OPTION        equ    40h
  270. XON_IS_ANY_OPTION    equ    80h
  271. TX_INTS_ON_OPTION    equ    100h
  272.  
  273. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  274. ;;            DEFAULT MODE
  275. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  276. ;   uncomment *this* DEF_MODE for XON/XOFF.
  277. DEF_MODE equ XON_IS_ANY_OPTION + XOFF_OUTPUT_OPTION
  278. ;   uncomment *this* DEF_MODE for XON/XOFF *plus* some hardware handshaking
  279. ;DEF_MODE equ DSR_INPUT_OPTION + CTS_OUTPUT_OPTION + XON_IS_ANY_OPTION +XOFF_OUTPUT_OPTION
  280.  
  281. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  282. ;;    DEFAULT TIME_OUT INTERVAL, FLAGS, BAUD_RATES, and other various
  283. ;;    denizens.
  284. ;;
  285. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  286. DEF_TIME         equ    THIRTY_SECONDS
  287.  
  288. DEF_FLAGS    equ    DEF_MODE
  289. DEF_T_OUT    equ    DEF_TIME
  290.  
  291. DEF_BAUD    equ    0e0h    ;default baud is 9600
  292. DEF_PARITY    equ    0h    ;default parity is off
  293. DEF_STOP    equ    0h    ;defualt stop bits is 1
  294. DEF_WORDLEN    equ    3h    ;default wordlength is 8
  295.  
  296. DEF_INIT    equ    DEF_BAUD + DEF_PARITY + DEF_STOP + DEF_WORDLEN
  297.  
  298.  
  299. NO_XON        equ    FALSE
  300. NO_CHARS    equ    0
  301.  
  302. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  303. ;; Masm has a 256 byte static initialization limit.  NC is shorter than
  304. ;; NO_CHARS....
  305. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  306. NC        equ    0
  307.  
  308. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  309. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  310. ;;            GLOBAL DATA ALLOCATIONS
  311. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  312. old_int        dw    ?    ; used as a temporary vector for 'weird' ints
  313.         dw    ?
  314.  
  315. orig_timer    dw    ?    ; the original timer vector
  316.         dw    ?
  317.  
  318. special_return_value    dw    0ff0h
  319. timed_out    dw    0    ; "any time outs?" flag
  320.  
  321. my_timer    dw    ?    ; timer for local usage
  322. timer_tick    dw    ?    ; this value used for timer countdown
  323. old_int14    dw    ?    ; the original int14 vector
  324.         dw    ?
  325.  
  326. error_flag    dw    0    ; used as a place holder in the RDA routine
  327.  
  328. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  329. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  330. ;;
  331. ;;    Allocate space for both com ports input and output buffers
  332. ;;    and the input error bits array
  333. ;;
  334. ;; WARNING!    Do not move the error array away from its approriate
  335. ;;        error array, or you'll probably crash at some point!
  336. ;;
  337. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  338. com1_inbuf    db    P1_INLEN dup(0)
  339. com1_errs    db    (P1_INLEN/8) + 1 dup(0)
  340.  
  341. com2_inbuf    db    P2_INLEN dup(0)
  342. com2_errs    db    (P2_INLEN/8) + 1 dup(0)
  343.  
  344. com1_outbuf    db    P1_OUTLEN dup(0)
  345. com2_outbuf    db    P2_OUTLEN dup(0)
  346.  
  347. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  348. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  349. ;;    CPB1 and CPB2
  350. ;;
  351. ;;    Allocate space and initialize the COMM PORT BLOCKS for com1 and com2
  352. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  353. cpb1 CPB <PORT1,offset com1_isr,COM1_MASK,INTNO_COM1,DEF_FLAGS,DEF_T_OUT,NO_XON,NO_XON,offset com1_inbuf,P1_INLEN,offset com1_inbuf,offset com1_inbuf,NC,offset com1_errs,offset com1_outbuf,P1_OUTLEN,offset com1_outbuf,offset com1_outbuf,NC>
  354. cpb2 CPB <PORT2,offset com2_isr,COM2_MASK,INTNO_COM2,DEF_FLAGS,DEF_T_OUT,NO_XON,NO_XON,offset com2_inbuf,P2_INLEN,offset com2_inbuf,offset com2_inbuf,NC,offset com2_errs,offset com2_outbuf,P2_OUTLEN,offset com2_outbuf,offset com2_outbuf,NC>
  355.  
  356.  
  357. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  358. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  359. ;;    EMASK_BYTE
  360. ;;
  361. ;;    Input Parameters:
  362. ;;        Pointer to input buffer in BX
  363. ;;
  364. ;;    Returns:
  365. ;;        Pointer to correct byte in error array in BX,
  366. ;;        with the correct bit mask in AX
  367. ;;
  368. ;;    All other Registers Preserved
  369. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  370. emask_byte    proc    near
  371. assume    ds:code
  372.  
  373.     push    cx
  374.     push    dx
  375.  
  376.     sub    bx, cpb_inbase[si]    ; get the actual offset
  377.     mov    ax, bx            ; get ready to divide the offset
  378.     cwd                ; (make a double word out of it)
  379.     mov    cx, 8
  380.     div    cx            ; number of bits to a word
  381.     add    ax, cpb_inerrors[si]    ; ax now points to the proper byte
  382.     mov    bx, ax
  383.                     ; in the error array
  384.     mov    cx, dx            ; get ready to shift the remainder
  385.                     ; to make the mask
  386.     mov    ax, 1            ; make the temporary mask
  387.     shl    ax, cl            ; and shift it over
  388.  
  389.     pop    dx
  390.     pop    cx
  391.     ret
  392. emask_byte    endp
  393.  
  394. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  395. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  396. ;;    INIT_BUFFERS
  397. ;;
  398. ;;    Input Parameters
  399. ;;        Pointer in SI to current CPB
  400. ;;
  401. ;;    All Registers Preserved
  402. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  403.  
  404. init_buffers    proc    near
  405.     push    ax
  406.     mov    ax, cpb_inbase[si]    ; clear the input buffer
  407.     mov    cpb_inhead[si], ax
  408.     mov    cpb_intail[si], ax
  409.     mov    cpb_incnt[si], NO_CHARS
  410.  
  411.     mov    ax, cpb_outbase[si]    ; and the output buffer
  412.     mov    cpb_outhead[si], ax
  413.     mov    cpb_outtail[si], ax
  414.     mov    cpb_outcnt[si], NO_CHARS
  415.     mov    cpb_outend[si], ax
  416.     mov    ax, cpb_outlen[si]
  417.     add    cpb_outend[si], ax
  418.     pop    ax
  419.     ret
  420. init_buffers    endp
  421.  
  422. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  423. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  424. ;;    TX_ON
  425. ;;
  426. ;;    Input Parameters
  427. ;;        SI points to current CPB, DI to current Base Port.
  428. ;;        This routine enables Transmit Buffer Empty Interrupts
  429. ;;
  430. ;;    All Registers Preserved
  431. ;;
  432. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  433. tx_on    proc    near
  434. assume ds:code
  435.  
  436.     cmp    cpb_tx_stat[si], TRUE    ; are tx ints already enabled?
  437.     jz    tx_on2            ; yes
  438.  
  439.     test    cpb_mode[si], TX_INTS_ON_OPTION
  440.     jz    tx_on2            ; don't turn ints on if not needed
  441.  
  442.     mov    cpb_tx_stat[si], TRUE    ; mark interrupts as enabled
  443.     push    ax
  444.     push    dx
  445.     lea    dx, IER[di]        ; take a look at the Interrupt
  446.     in    al, dx            ; enable register
  447.  
  448.     or    al, IER_THRE        ; turn on the interrupt bit
  449.     out    dx, al            ; and turn it on in the chip
  450.     pop    dx
  451.     pop    ax
  452.  
  453. tx_on2:
  454.     ret                ; ints will be turned on eventually
  455. tx_on    endp
  456.  
  457.  
  458.  
  459. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  460. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  461. ;;    TX_OFF
  462. ;;
  463. ;;    Input Parameters
  464. ;;        SI points to current CPB, DI to current Base Port.
  465. ;;        This routine disables Transmit Buffer Empty Interrupts
  466. ;;
  467. ;;    All Registers Preserved
  468. ;;
  469. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  470. tx_off    proc    near
  471. assume ds:code
  472.  
  473.     cmp    cpb_tx_stat[si], FALSE    ; are tx ints already disabled?
  474.     jz    tx_off2            ; yes
  475.  
  476.     test    cpb_mode[si], TX_INTS_ON_OPTION
  477.     jz    tx_off2            ; shouldn't be on in the first place
  478.  
  479.     mov    cpb_tx_stat[si], FALSE    ; mark interrupts as disabled
  480.     push    ax
  481.     push    dx
  482.     lea    dx, IER[di]        ; take a look at the Interrupt
  483.     in    al, dx            ; enable register
  484.  
  485.     and    al, NOT IER_THRE    ; turn off the interrupt bit
  486.     out    dx, al            ; and turn it on in the chip
  487.     pop    dx
  488.     pop    ax
  489.  
  490. tx_off2:
  491.     ret                ; ints will be turned on eventually
  492. tx_off    endp
  493.  
  494.  
  495. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  496. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  497. ;;    GET_COM_PORT
  498. ;;
  499. ;;    Input Parameters
  500. ;;        SI points to current CPB
  501. ;;
  502. ;;    Returns proper comm port (0 or 1) in DX. All other registers preserved
  503. ;;
  504. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  505. get_com_port proc near
  506. assume    ds:code
  507.  
  508.     cmp    si, offset cpb1
  509.     jnz    not_com1
  510.     mov    dx, 0
  511.     jmp    ret_com_port
  512.  
  513. not_com1:
  514.     cmp    si, offset cpb2
  515.     jnz    not_com2
  516.     mov    dx, 1
  517.     jmp    ret_com_port
  518.  
  519. not_com2:
  520.     mov    dx, 0ffh
  521.  
  522. ret_com_port:
  523.     ret
  524. get_com_port endp
  525.  
  526. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  527. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  528. ;;    GET_CPB
  529. ;;
  530. ;;    Input Parameters
  531. ;;        Comm port (0 or 1) in DX
  532. ;;
  533. ;;    Returns pointer to CPB in SI
  534. ;;
  535. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  536. get_cpb proc near
  537. assume    ds:code
  538.  
  539.     cmp    dx, 0
  540.     jnz    not_comm1
  541.     mov    si, offset cpb1
  542.     jmp    ret_cpb
  543.  
  544. not_comm1:
  545.     cmp    dx, 1
  546.     jnz    not_comm2
  547.     mov    si, offset cpb2
  548.     jmp    ret_cpb
  549.  
  550. not_comm2:
  551.     mov    si, 0
  552.  
  553. ret_cpb:
  554.     ret
  555. get_cpb endp
  556.  
  557.  
  558.  
  559. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  560. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  561. ;;    SEND_CHAR
  562. ;;
  563. ;;    Input Parameters
  564. ;;        Character to be sent in al, pointer to current CPB in SI,
  565. ;;        Base port in DI
  566. ;;
  567. ;;    All Registers Preserved 
  568. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  569. send_char proc    near
  570.  
  571.     push    dx
  572.  
  573.     call    get_com_port
  574.     call    out_char
  575.  
  576.     pop    dx
  577.     ret
  578.  
  579. send_char endp
  580.  
  581.  
  582. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  583. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  584. ;;    OUT_BELL
  585. ;;    SEND_XOFF
  586. ;;    SEND_XON
  587. ;;
  588. ;;
  589. ;;    Input Parameters
  590. ;;        None
  591. ;;
  592. ;;    Sends BELL out the comm port
  593. ;;      "   XON   "   "   "    "
  594. ;;      "   XOFF  "   "   "    "
  595. ;;
  596. ;;    All Registers Preserved 
  597. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  598. out_bell proc near
  599. assume    ds:code
  600.  
  601.     push    ax
  602.     mov    al, BELL
  603.     call    send_char
  604.     pop    ax
  605.     ret
  606.  
  607. out_bell endp
  608.  
  609.  
  610. send_xoff proc near
  611. assume    ds:code
  612.     push    ax
  613.     mov    al, XOFF
  614.     call    send_char
  615.     pop    dx
  616.     ret
  617. send_xoff endp
  618.  
  619. send_xon proc near
  620. assume    ds:code
  621.     push    ax
  622.     mov    al, XON
  623.     call    send_char
  624.     pop    dx
  625.     ret
  626. send_xon endp
  627.  
  628.  
  629. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  630. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  631. ;;    TILL_CLEAR
  632. ;;
  633. ;;    Input Parameters
  634. ;;        Before affecting the transmit interrupts, this routine
  635. ;;        allows the Shift Register to clear, or for timeouts to
  636. ;;        run out before returning.  DI should point to the Base
  637. ;;        Port of the comm port. 
  638. ;;
  639. ;;    Nothing Returned, all registers preserved
  640. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  641. till_clear    proc    near
  642.  
  643.     push    dx
  644.     push    ax
  645.  
  646.     mov    my_timer, TWO_SECONDS    ; for no more than one second
  647.     lea    dx, LSR[di]        ; better check till we're really done
  648. lp1:
  649.     in    al, dx
  650.  
  651.     test    al, LSR_TSRE        ; check the shift register
  652.     jnz    end_lp1            ; done, turn off ints
  653.  
  654.     cmp    my_timer, 0        ; compare the timer tick to zero
  655.     jnz    lp1            ; we'll just give up eventually
  656. end_lp1:
  657.     pop    ax
  658.     pop    dx
  659.     ret
  660. till_clear endp
  661.  
  662. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  663. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  664. ;;    OUT_CHAR
  665. ;;
  666. ;;    Input Parameters
  667. ;;        AL should hold the character to be outputted, SI points
  668. ;;        to the CPB and DI to the base register.
  669. ;;
  670. ;;        All of the various handshaking parameters are checked and
  671. ;;        acted upon.  If a timeout occurs, the timed_out counter
  672. ;;        will be incremented
  673. ;;
  674. ;;    Returns AH=80h if timed out
  675. ;;
  676. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  677. out_char proc    near
  678.  
  679.     push    ax
  680.     mov    bx, cpb_timeout[si]    ; get the timeout
  681.     mov    timer_tick, bx        ; and stick into the autodecrementer
  682.  
  683. top_loop:
  684.     lea    dx, MSR[di]        ; check the status of the serial port
  685.     in    al, dx
  686.  
  687.     test    cpb_mode[si], DSR_INPUT_OPTION
  688.     jz    no_dsr_test        ; not dsr handshaking
  689.     test    al, MSR_DSR        ; we're handshaking...hows it look?
  690.     jz    loop_it            ; not yet
  691.  
  692. no_dsr_test:
  693.     test    cpb_mode[si], CTS_OUTPUT_OPTION
  694.     jz    no_cts_test        ; no cts handshaking
  695.     test    al, MSR_CTS        ; we're handshaking...hows it look?
  696.     jz    loop_it            ; not yet
  697.  
  698. no_cts_test:
  699.     cmp    cpb_out_xoff[si], FALSE    ; in an XOFF state?
  700.     jnz    loop_it            ; yes, so cycle. It may end
  701.  
  702.     lea    dx, LSR[di]        ; finally, check the xmit buffer
  703.                     ; on the chip
  704. chip_check:
  705.     in    al, dx            ; get the status
  706.     test    al, LSR_THRE        ; clear to send it?
  707.     jnz    squirt_it        ; yes! send the character
  708.  
  709. loop_it:
  710.     cmp    timer_tick, 0        ; out of time yet?
  711.     jnz    top_loop        ; not yet, so cycle
  712.  
  713.     pop    ax            ; keep the stack clean
  714.     or    ah, 080h        ; mark a timeout
  715.     inc    timed_out
  716.     ret
  717.  
  718. squirt_it:
  719.     lea    dx, DATA[di]        ; get the port to squirt from
  720.     pop    ax            ; retrieve the original character
  721.     out    dx, al            ; and squirt it.
  722.     xor    ah, ah
  723.     ret
  724. out_char endp
  725.  
  726.  
  727.  
  728. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  729. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  730. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  731. ;;            ASYNC SUB-SERVICE ROUTINES
  732. ;;
  733. ;;    These routines are only entered by the COMMON_ISR routine, which
  734. ;;    validates there is a valid interrupt on the aproriate comm port,
  735. ;;    and sets up SI to point to the correct CPB and DI to the correct
  736. ;;    Base Port.
  737. ;;
  738. ;;    COMMON_ISR also preserves all registers and the flags, and resets
  739. ;;    the EOI required at the end of each interrupt.
  740. ;;
  741. ;;    Finally, COMMON_ISR is, itself, called either by COM1_ISR or COM2_ISR
  742. ;;
  743. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  744. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  745.  
  746.  
  747. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  748. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  749. ;;    MS_INT
  750. ;;
  751. ;;            MODEM STATUS INTERRUPT
  752. ;;    This interrupt should never happen.  If it does, clear the status
  753. ;;    register and return.
  754. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  755. ms_int    proc    near
  756. assume    ds:code
  757.  
  758.     lea    dx, MSR[di]
  759.     in    al, dx
  760.     ret
  761.  
  762. ms_int    endp
  763.  
  764. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  765. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  766. ;;    XMIT_INT
  767. ;;
  768. ;;            TRANSMITTER EMPTY INTERRUPT
  769. ;;    This interrupt is generated whenever interrupts are TX interrupts
  770. ;;    are turned on and the Transmiter Holding Register is empty.
  771. ;;
  772. ;;    If there are more characters to be transmitted, then load up
  773. ;;    another one and move the pointers. If there are no more characters
  774. ;;    to be transmitted, then wait until the the last character has been
  775. ;;    transmitted (the Shift Register must be empty), then turn TX
  776. ;;    interrupts off.  This gives a slight delay on the last character
  777. ;;    but guarantees the last character will be properly transmitted.
  778. ;;
  779. ;;    If XON/XOFF processing on outgoing characters is enabled, then if we
  780. ;;    get a TX int with the XOFF flag set (indicating we had gotten an XOFF
  781. ;;    in the RX routine earlier), we'll simply shut off TX interrupts as if
  782. ;;    we had an empty TX buffer.  Subsequent characters received in the RX
  783. ;;    routine will reset the XON/XOF flag and turn TX interrupts on as well.
  784. ;;
  785. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  786.  
  787. xmit_int    proc    near
  788. assume    ds:code
  789.     cmp    cpb_outcnt[si], 0    ; anywork to do?
  790.     jnz    xmit1            ; yep!
  791.  
  792.     call    till_clear        ; wait for transmitter to clear
  793.  
  794.     call    tx_off            ; turn xmit interrupts off
  795.  
  796.     ret                ; and return
  797.  
  798. xmit1:
  799.     mov    bx, cpb_outtail[si]    ; get the next character to xmit
  800.     inc    bx            ; now point right on it
  801.     cmp    bx, cpb_outend[si]    ; cmp to the end
  802.     jnz    xmit2            ; if not past the end, jump
  803.     mov    bx, cpb_outbase[si]    ; past the end, reset to the head
  804.  
  805. xmit2:
  806.     cli                ; don't get interrupted now
  807.     dec    cpb_outcnt[si]        ; decrement the count of chars to go
  808.     mov    cpb_outtail[si], bx    ; and save a pointer to the next char
  809.     mov    al, [bx]        ; get the character in al
  810.  
  811.     cmp    cpb_outcnt[si], 0    ; any work left to do?
  812.     jnz    out_it            ; yep!
  813.  
  814.     call    till_clear        ; wait for transmitter to clear
  815.     call    tx_off            ; turn xmit interrupts off
  816.  
  817. out_it:
  818.     call    out_char        ; output the character
  819.     ret
  820. xmit_int    endp
  821.  
  822. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  823. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  824. ;;    RDA_INT
  825. ;;
  826. ;;
  827. ;;            CHARACTER READY INTERRUPT
  828. ;;    This interrupt is generated whenever a character is ready on the
  829. ;;    serial port.
  830. ;;
  831. ;;    If the "DSR must be set" option is turned on, and DSR isn't, then
  832. ;;    the character is read, then discarded.  Likewise for the "DCD must
  833. ;;    be high" option.
  834. ;;
  835. ;;    If there is room in the buffer, add it and increment the pointers.
  836. ;;    If there isn't room, then clear the character and discard it, then
  837. ;;    generate a BELL character out the port (in case a human were
  838. ;;    listening).
  839. ;;
  840. ;;    After adding the character, if XON/XOFF processing of incoming
  841. ;;    characters is enabled, the HIGH_WATER level is checked. If the
  842. ;;    character count exceeds it, then if the XOFF flag isn't set, an
  843. ;;    XOFF character is sent and the XOFF flag is incremented.  The XON
  844. ;;    is sent (and the XOFF flag reset) in the "get-a-character" routine
  845. ;;    in the interrupt 0x14 replacement when the low water mark is reached.
  846. ;; 
  847. ;;    When adding a character, the error status is checked (if the
  848. ;;    check_flag is set in the cpb) If an error exists on the port
  849. ;;    the corresponding bit is set in the error buffer for the com port.
  850. ;;    This is later examined in the "get-a-character" routine and the
  851. ;;    "get-status" routine.  A generalized error is returned if the error
  852. ;;    bit is set.
  853. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  854. rda_int        proc    near
  855. assume    ds:code
  856.  
  857.     test    cpb_mode[si], DSR_INPUT_OPTION    + DCD_INPUT_OPTION
  858.                     ; is the DSR or DCD option turned on?
  859.     jz    rda_2            ; nope
  860.     lea    dx, MSR[di]        ; check the modem status
  861.     in    al, dx
  862.     test    cpb_mode[si], DSR_INPUT_OPTION
  863.     jz    just_dcd        ; just check DCD
  864.     test    al, MSR_DSR        ; is it set?
  865.     jz    rda_1            ; nope. discard.
  866.  
  867. just_dcd:
  868.     test    cpb_mode[si], DCD_INPUT_OPTION
  869.     jz    rda_2            ; get the character
  870.     test    al, MSR_DCD        ; is there carrier?
  871.     jnz    rda_2            ; you bet! get the character
  872.  
  873. rda_1:                    ; read the character, discard by
  874.                     ; returning
  875.     lea    dx, DATA[di]
  876.     in    al, dx
  877.     ret
  878.  
  879. rda_2:
  880.     lea    dx, LSR[di]        ; reading status reg clears errors
  881.     in    al, dx
  882.     test    al, LSR_OVERRUN + LSR_PARITY + LSR_FRAMING
  883.     jz    no_err1            ; any of the above errors? No.
  884.     mov    error_flag, TRUE    ; set the error_flag
  885.     jmp    get_char
  886.  
  887. no_err1:                ; break an error?
  888.     test    cpb_mode[si], BREAK_IS_ERROR_OPTION
  889.     jz    get_char        ; nope
  890.     test    al, LSR_BREAK        ; is break on?
  891.     jz    get_char        ; nope
  892.     mov    error_flag, TRUE    ; yep. Set the error
  893.  
  894. get_char:
  895.     lea    dx, DATA[di]        ; get the data
  896.     in    al, dx
  897.  
  898.     cmp    error_flag, FALSE    ; any errors?
  899.     jnz    insert1            ; yep, so skip the XON/XOFF stuff
  900.  
  901.                     ; no errors, so XON/XOFF would be
  902.                     ; valid
  903.  
  904.     test    cpb_mode[si], XOFF_OUTPUT_OPTION
  905.     jz    insert1            ; skip if output xoff not turned on
  906.     cmp    al, XOFF        ; is this an XOFF character?
  907.     jnz    insert0            ; nope
  908.     mov    cpb_out_xoff[si], TRUE    ; turn this on for the next TX int
  909.     call    tx_off            ; turn off tx interrupts
  910.     ret                ; and return
  911.  
  912. insert0:
  913.     cmp    cpb_out_xoff[si], FALSE    ; are we in XOFF mode?
  914.     jz    insert1            ; no, so insert the character
  915.  
  916.                     ; do we care about exact matching only?
  917.     test    cpb_mode[si], XON_IS_ANY_OPTION
  918.     jz    any_char        ; no. Any character will do.
  919.  
  920.     cmp    al, XON            ; is this an XON character?
  921.     jz    any_char        ; yes, so turn things back on
  922.  
  923.     ret                ; XOFF is on, this isn't an XON,
  924.                     ; so simply ignore it
  925. any_char:
  926.     mov    cpb_out_xoff[si], FALSE    ; turn this off and set up for
  927.     call    tx_on            ; the next TX int
  928.     ret                ; and return
  929.  
  930.  
  931. insert1:
  932.                     ; send an XOFF quick as you can if
  933.                     ; needed
  934.     test    cpb_mode[si], XOFF_INPUT_OPTION
  935.     jz    insert2            ; no XOFF processing required
  936.     cmp    cpb_incnt[si], HIGH_MARK; do we need an XOFF?
  937.     jle    insert2            ; no XOFF required right now
  938.  
  939.     cmp    cpb_in_xoff[si], FALSE    ; have we already sent one?
  940.     jnz    insert2            ; yes
  941.  
  942.     mov    cpb_in_xoff[si], TRUE    ; set the flag and
  943.     call    send_xoff        ; send it
  944.  
  945. insert2:
  946.     mov    bx, cpb_inhead[si]
  947.     inc    bx            ; move the head up by one
  948.     cmp    bx, cpb_inerrors[si]    ; have we gone past the end?
  949.     jnz    insert3            ; nope, so stick it in the buffer
  950.     mov    bx, cpb_inbase[si]    ; yep, so start it over
  951.  
  952. insert3:
  953.     cmp    bx, cpb_intail[si]    ; are we sneaking up on ourselves?
  954.     jnz    insert4            ; nope, just insert it
  955.     call    out_bell        ; yep! ring a bell, and 
  956.     mov    error_flag, TRUE    ; set an error on the next character
  957.     sti
  958.     ret                ; and simply return
  959.  
  960. insert4:
  961.     mov    [bx], al        ; put the character in its place and
  962.     cli                ; turn ints off for a little while
  963.     mov    cpb_inhead[si], bx    ; preserve the new value of the head
  964.     inc    cpb_incnt[si]        ; and mark how many characters exist
  965.  
  966.     sti
  967.     cmp    error_flag, FALSE    ; any errors?
  968.     jz    rda_return        ; no
  969.  
  970.     call    emask_byte        ; get the byte and the mask
  971.     or    [bx], ax        ; and make the bit turn on
  972.     mov    error_flag, FALSE    ; and turn them of for the next time
  973.  
  974. rda_return:
  975.     ret
  976. rda_int        endp
  977.  
  978. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  979. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  980. ;;    ERR_INT
  981. ;;
  982. ;;            ERROR INTERRUPT
  983. ;;
  984. ;;    This interrupt should never happen.  If it does, read the 
  985. ;;    register and return.
  986. ;;
  987. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  988. err_int        proc    near
  989. assume    ds:code
  990.  
  991.     lea    dx, LSR[di]        ; get the status word
  992.     in    al, dx
  993.     iret
  994.  
  995. err_int    endp
  996.     
  997. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  998. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  999. ;;    This is a table containing the offsets for each of the expected
  1000. ;;    interrupts from the 8250.  Interesting how the offsets off the IIR
  1001. ;;    happen to be by two.  Great for offseting into this table!
  1002. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1003. interrupt_table    label word        ;;
  1004.     dw    offset ms_int        ;; modem status interrupt
  1005.     dw    offset xmit_int        ;; transmitter interrupt
  1006.     dw    offset rda_int        ;; character ready interrupt
  1007.     dw    offset err_int        ;; receiver line error interrupt
  1008. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1009. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1010.  
  1011.  
  1012. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1013. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1014. ;;
  1015. ;;            COM1_ISR AND COM2_ISR
  1016. ;;
  1017. ;;    These are the entry points for each of the COMM Interrupts
  1018. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1019. com1_isr    proc far
  1020. assume    ds:nothing
  1021.     push    ax
  1022.     mov    ax, offset cs:cpb1
  1023. jmp    short    common_isr
  1024. com1_isr    endp
  1025.  
  1026. com2_isr    proc far
  1027. assume    ds:nothing
  1028.     push    ax
  1029.     mov    ax, offset cs:cpb2
  1030.     jmp    short    common_isr
  1031. com2_isr    endp
  1032.  
  1033.  
  1034. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1035. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1036. ;;    COMMON_ISR
  1037. ;;
  1038. ;;    A common entry point into the "save the registers and call the right
  1039. ;;    interrupt service routine".
  1040. ;;
  1041. ;;    A little twist:  if it does not appear the interrupt came from a
  1042. ;;    source we expect, then jump to the old interrupt vector
  1043. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1044. common_isr    proc    near
  1045. assume    ds:nothing
  1046.  
  1047.     push    bx
  1048.     push    cx
  1049.     push    dx
  1050.     push    si
  1051.     push    di
  1052.     push    ds
  1053.  
  1054.     push    cs            ;addressing off ds as cs
  1055.     pop    ds
  1056. assume    ds:code                ;makes it easier to think
  1057.  
  1058.  
  1059.     mov    si, ax            ; move in the cpb
  1060.     mov    di, cpb_base[si]    ; get the base port
  1061.     lea    dx, IIR[di]        ; and then the interrupt ID Register
  1062.     in    al, dx            ; get the interrupt type
  1063.     test    al, IIR_PEND        ; is there a pending interrupt?
  1064.     jz    is_mine            ; interrupt on *this* chip!
  1065.  
  1066. other_int:
  1067.     cli                ; turn off interrupts since this
  1068.                     ; is non-reentrant
  1069.     mov    ax, cpb_oint_add[di]    ; grab the old interrupt out of
  1070.     mov    old_int, ax        ; the structure
  1071.     mov    ax, cpb_oint_add[di][2]
  1072.     mov    old_int[2], ax
  1073.  
  1074.     pop    ds            ; pop everything back
  1075. assume ds:nothing
  1076.     pop    di
  1077.     pop    si
  1078.     pop    dx
  1079.     pop    cx
  1080.     pop    bx
  1081.     pop    ax
  1082.     jmp    dword ptr cs:[old_int]    ; jump to whatever was there
  1083.  
  1084. polling_loop:                ; this is a requirement to be sure
  1085.                     ; we haven't lost any any interrupts
  1086.  
  1087.     lea    dx, IIR[di]        ; load the interrupt ID Register
  1088.     in    al, dx            ; 
  1089.     test    al, IIR_PEND        ; is there a pending interrupt?
  1090.     jnz    clear            ; no. time to return
  1091.  
  1092. is_mine:
  1093.  
  1094.     and    ax, 06h
  1095.     mov    bx, ax
  1096.     mov    bx, interrupt_table[bx]
  1097.  
  1098.     push    di            ; save di for the polling loop
  1099.     call    bx
  1100.     pop    di
  1101.  
  1102.     jmp    polling_loop        ; time to check for more work
  1103.  
  1104. clear:                    ; no further interrupt processing
  1105.     pop    ds            ; pop everything back
  1106. assume ds:nothing
  1107.     pop    di
  1108.     pop    si
  1109.     pop    dx
  1110.     pop    cx
  1111.     pop    bx
  1112.  
  1113.     cli                ; interrupts off, then reset
  1114.     mov    al, INT_EOI        ; interrupts on the 8259
  1115.     out    CTRL_PORT, al
  1116. no_eoi:
  1117.     pop    ax
  1118.  
  1119.  
  1120.     iret                ; the iret will turn interrupts back on
  1121. common_isr    endp
  1122.  
  1123. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1124. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1125.  
  1126.  
  1127. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1128. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1129. ;;    NEW_14
  1130. ;;
  1131. ;;    The replacement for the interrupt 14 service routines
  1132. ;;    Services 0,1,2,3 are basically the same as before the vector
  1133. ;;    was stolen.
  1134. ;;
  1135. ;;    This routine sets up SI and DI to point to the correct CPB and
  1136. ;;    Base Port. then vecots to the appropriate service routine
  1137. ;;
  1138. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1139. new_14    proc    far
  1140.     assume    ds:nothing
  1141.  
  1142.     sti
  1143.                     ;push everything in sight,
  1144.                     ; *except* ax
  1145.     push    bx
  1146.     push    cx
  1147.     push    dx
  1148.     push    di
  1149.     push    si
  1150.     push    bp
  1151.     push    ds
  1152.  
  1153.     push    cs            ;addressing off ds as cs
  1154.     pop    ds
  1155. assume    ds:code                ;makes it easier to think
  1156.  
  1157.     call    get_cpb            ; get si to point to the proper cpb
  1158.     mov    di, cpb_base[si]    ; get di to point to the base port
  1159.  
  1160.     cmp    ah, 4            ; better check for valid function
  1161.     jle    ok_func            ; function is okay
  1162.     mov    ax, 0ffffh        ; set up as a bad code
  1163.     jmp    return            ; and return
  1164.  
  1165. ok_func:
  1166.     mov    bl, ah
  1167.     xor    bh, bh
  1168.     shl    bx, 1            ; make an offset into the table
  1169.     mov    bx, int14_functions[bx]    ; and get the address of the function
  1170.     call    bx
  1171.  
  1172. return:
  1173.     pop    ds
  1174. assume    ds:nothing
  1175.     pop    bp
  1176.     pop    si
  1177.     pop    di
  1178.     pop    dx
  1179.     pop    cx
  1180.     pop    bx
  1181.  
  1182.     iret
  1183. new_14    endp
  1184.  
  1185.  
  1186.  
  1187. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1188. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1189. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1190. int14_functions    label    word        ;;
  1191.     dw    offset    init14        ;; initialize the port
  1192.     dw    offset    send14        ;; send the character in al
  1193.     dw    offset    get14        ;; return next charaacter in al, status
  1194.                     ;; in ah
  1195.     dw    offset    stat14        ;; get serial status, return in ax
  1196.     dw    offset    newfuncs14    ;; all of the new functions
  1197. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1198. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1199. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1200.  
  1201. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1202. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1203. ;;    INIT14
  1204. ;;
  1205. ;;    Input Parameters
  1206. ;;        The topmost three bits (7,6,5) contain the baudrate:
  1207. ;;            000 -  110
  1208. ;;            001 -  150
  1209. ;;            010 -  300
  1210. ;;            011 -  600
  1211. ;;            100 - 1200
  1212. ;;            101 - 2400
  1213. ;;            110 - 4800
  1214. ;;            111 - 9600
  1215. ;;
  1216. ;;        The next two bits (4,3) contain the parity:
  1217. ;;            00  - None
  1218. ;;            01  - Odd
  1219. ;;            10  - None
  1220. ;;            11  - Even
  1221. ;;
  1222. ;;        The next bit (2), if set, indicates two stopbits, otherwise
  1223. ;;        one stopbit
  1224. ;;
  1225. ;;        The final two bits (1,0) are the word length:
  1226. ;;
  1227. ;;            00 - Undefined
  1228. ;;            01 - Undefined
  1229. ;;            10 - 7 Bits
  1230. ;;            11 - 8 Bits
  1231. ;;
  1232. ;;    This routine is very similar to the original BIOS routine, with
  1233. ;;    the exception that it gets a divisor for determining the baud
  1234. ;;    rate which exceeds the 9600 original in the BIOS. When the
  1235. ;;    comm port is initialized through the old interrupt with AH=0,
  1236. ;;    it is the same as the old, except for the mode and timeout reset.
  1237. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1238. init14    proc    near
  1239. assume    ds:code
  1240.  
  1241.     push    ax
  1242.     lea    dx, LCR[di]            ; get the Latch
  1243.     in    al, dx
  1244.     or    al, LCR_DLAB            ; turn on the divisor
  1245.     out    dx, al                ; in the chip
  1246.     pop    ax                ; get the old ax back
  1247.  
  1248.     push    ax                ; and save it
  1249.     mov    cl, 5                ; we'll shift it over to
  1250.     shr    ax, cl                ; make a table offset of it
  1251.  
  1252.     call    get_baud            ; then get the correct divisor
  1253.                         ; allows higher than 9600
  1254.  
  1255.     lea    dx, DATA[di]            ; get the base address
  1256.     out    dx, ax                ; output the whole word
  1257.  
  1258.     pop    ax                ; get back original ax
  1259.     lea    dx, LCR[di]            ; get the Latch
  1260.     and    al, 01fh            ; just the parity, stop bits,
  1261.                         ; word length
  1262.     out    dx, al                ; set the params
  1263.  
  1264.     mov    cpb_mode[si], DEF_MODE        ; default modes
  1265.     mov    cpb_timeout[si], DEF_TIME    ; default timeout period
  1266.  
  1267.     jmp    stat14                ; the old one returned the
  1268.                         ; status in al
  1269. init14    endp
  1270.  
  1271. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1272. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1273. ;;    GET_BAUD
  1274. ;;
  1275. ;;    Input Parameters
  1276. ;;        AX should be the ofset into the baudrate table.
  1277. ;;        The first eight entries are exactly as the BIOS has them
  1278. ;;        The others were extrapolated for 19200, and 38400 baud
  1279. ;;
  1280. ;;    Returns the divisor in AX
  1281. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1282. get_baud    proc    near
  1283. assume    ds:code
  1284.  
  1285.     shl    ax, 1                ; make the table offset
  1286.     push    bx
  1287.     mov    bx, ax
  1288.     mov    ax, baudrate_table[bx]        ; and get the divisor
  1289.     pop    bx
  1290.     ret
  1291.  
  1292. get_baud    endp
  1293.  
  1294. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1295. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1296. ;;
  1297. ;;    These numbers are in decimal.  As can be seen, to double the baudrate
  1298. ;;    the divisor gets halved.  So....what comes after 384000??
  1299. ;;
  1300. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1301. baudrate_table    label    word
  1302.     dw    1047        ; 110 baud
  1303.     dw    768        ; 150 baud
  1304.     dw    384        ; 300 baud
  1305.     dw    192        ; 600 baud
  1306.     dw    96        ; 1200 baud
  1307.     dw    48        ; 2400 baud
  1308.     dw    24        ; 4800 baud
  1309.     dw    12        ; 9600 baud
  1310.     dw    6        ; 19200 baud
  1311.     dw    3        ; 38400 baud
  1312. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1313.  
  1314.  
  1315. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1316. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1317. ;;    SEND14
  1318. ;;
  1319. ;;    This routine will attempt to send the character in al for the
  1320. ;;    entire timeout period if the TX_INTS_ON_OPTION is off.
  1321. ;;
  1322. ;;    If the option is on, it will queue the item, instead, into the
  1323. ;;    queue, and let the interrupt system handle it from there.
  1324. ;;
  1325. ;;    This routine will return the status of the send (if any) in AX
  1326. ;;
  1327. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1328. send14    proc    near
  1329. assume    ds:code
  1330.  
  1331.     test    cpb_mode[si], TX_INTS_ON_OPTION
  1332.     jz    just_squirt        ; the option isn't being
  1333.                     ; used, so send it the old way
  1334.  
  1335.     mov    bx, cpb_outhead[si]    ; get the current head
  1336.     inc    bx            ; move it forward by one
  1337.     cmp    bx, cpb_outend[si]    ; cmp to the end
  1338.     jnz    send2            ; if not past the end, jump
  1339.     mov    bx, cpb_outbase[si]    ; past the end, reset to the head
  1340.  
  1341. send2:
  1342.     cmp    bx, cpb_outtail[si]    ; about to clobber the tail?
  1343.     jz    junk_char        ; yep! throw this character away!
  1344.  
  1345.     mov    [bx], al        ; stick the character in the queue
  1346.     cli                ; turn off interrupts
  1347.     mov    cpb_outhead[si], bx    ; and save the new value
  1348.     inc    cpb_outcnt[si]        ; and increment the output char cnt
  1349.     call    tx_on
  1350.  
  1351. junk_char:
  1352.     sti
  1353.     ret
  1354.  
  1355. just_squirt:
  1356.     call    out_char
  1357.  
  1358.     jmp    stat14            ; then get the status
  1359.         
  1360. send14    endp
  1361.  
  1362. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1363. ;;    GET14
  1364. ;;
  1365. ;;    This routine is a replacement for the AH = 2 get-a-character
  1366. ;;    routine in the BIOS.
  1367. ;;
  1368. ;;    Input Parameters
  1369. ;;        Set the COM port in DX (0 or 1), Base in DI, CPB in SI
  1370. ;;
  1371. ;;    Returns character (if any) in AL, with partial comm port status
  1372. ;;        in AH. Check the high bit ( & 0x80) to determine timeout,
  1373. ;;        or if AH is non zero it is safe to assume some error has
  1374. ;;        occurred
  1375. ;;
  1376. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1377. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1378. get14    proc    near
  1379. assume    ds:code
  1380.  
  1381.     mov    ax, cpb_timeout[si]
  1382.     mov    timer_tick, ax        ; in case there is no character ready,
  1383.                     ; set the timeout count in advance
  1384.  
  1385.  
  1386. try_again:
  1387.      cmp    cpb_incnt[si], 0    ; any characters out there?
  1388.     jnz    got_one            ; yep!
  1389.  
  1390.     sti                ; make sure interrupts are on while
  1391.     cmp    timer_tick, 0        ; waiting for the timer to zero out
  1392.     jnz    try_again
  1393.     or    ah, 080h        ; set the timeout error bit
  1394.     inc    timed_out        ;
  1395.     ret
  1396.  
  1397.  
  1398. got_one:
  1399.     mov    bx, cpb_intail[si]    ; get the tail
  1400.     inc    bx            ; point to next character
  1401.     cmp    bx, cpb_inerrors[si]    ; past end?
  1402.     jnz    get_char1        ; no
  1403.     mov    bx, cpb_inbase[si]    ; yes, so reset to beginning
  1404.  
  1405. get_char1:
  1406.     mov    al, [bx]        ; get the actual character
  1407.     xor    ah,ah
  1408.     push    ax            ; save it
  1409.     push    bx
  1410.     call    emask_byte        ; to check if an error
  1411.     test    [bx], ax        ; is there an error?
  1412.     pop    bx
  1413.     pop    ax
  1414.  
  1415.     jz    no_inp_error        ; no error on input
  1416.                     ; there was an error or some kind,
  1417.                     ; so turn on all error bits, including
  1418.                     ; break if the flag is set.
  1419.  
  1420.     or    ah, LSR_OVERRUN + LSR_PARITY + LSR_FRAMING
  1421.     test    cpb_mode[si], BREAK_IS_ERROR_OPTION
  1422.     jz    no_inp_error        ; nope
  1423.     or    ah, LSR_BREAK        ; is break on?
  1424.  
  1425. no_inp_error:
  1426.     cli                ; no interruptions
  1427.     dec    cpb_incnt[si]        ; decrease the character count
  1428.     mov    cpb_intail[si], bx    ; and save the next character ptr    
  1429.  
  1430.     test    cpb_mode[si], XOFF_INPUT_OPTION
  1431.     jz    skip_xon        ; do an XON need to be sent?
  1432.     cmp    cpb_in_xoff[si], 0    ; currently in xoff?
  1433.     jz    skip_xon        ; no
  1434.     cmp    cpb_incnt[si], LOW_MARK    ; down low enough for a cushion?
  1435.     ja    skip_xon        ; no
  1436.     mov    cpb_in_xoff[si], FALSE    ; turn off XOFF flag
  1437.     call    send_xon        ; and tell remote to continue sending
  1438.  
  1439.  
  1440. skip_xon:
  1441.     sti                ; and turn interrupts on again
  1442.  
  1443.     ret
  1444.  
  1445. get14    endp
  1446.  
  1447. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1448. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1449. ;;    STAT14
  1450. ;;
  1451. ;;    Input Parameters
  1452. ;;        Set the COM port in DX (0 or 1), Base in DI, CPB in SI
  1453. ;;
  1454. ;;    This routine will return the status of the comm port in AX. A
  1455. ;;    bit of fudging is done, since the low level interrupt driver may be
  1456. ;;    handling the XON/XOFF status.  General logic of the routine:
  1457. ;;
  1458. ;;    Starting with the real status of the device (from the LSR register),
  1459. ;;    turn on the character ready bit if there are any characters in the
  1460. ;;    low level receive buffer.  If there is a character in the buffer,
  1461. ;;    determine if there is an error waiting for the next read. If there
  1462. ;;    is, then set all error conditions, including BREAK (if break is
  1463. ;;    considered an error condition).
  1464. ;;
  1465. ;;    Next, get the modem status, and then check out the XOFF status. If
  1466. ;;    XOFF is set for the outgoing line, make it appear the Transmit Shift
  1467. ;;    and Hold Registers are still busy.   If the option is set, OR the
  1468. ;;    DTR/DSR bit to show the "terminal" is ready.
  1469. ;;
  1470. ;;    Finally, if there is a time out (and timed_out) is a counter, then
  1471. ;;    set the TIMED_OUT flag, bit 7 of the high byte.
  1472. ;;
  1473. ;;    Return in AX
  1474. ;;
  1475. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1476. stat14    proc    near
  1477.  
  1478.     lea    dx, LSR[di]        ; try the *real* status register
  1479.     in    al, dx
  1480.     xor    ah, ah
  1481.  
  1482.     cmp    cpb_incnt[si], 0    ; any characters waiting in the buffer?
  1483.     jz    no_brk_error        ; no.
  1484.     or    ax, LSR_DATA        ; set the bit if not already set.
  1485.  
  1486.                     ; now see if the next char in the
  1487.                     ; buffer will bring an error with it
  1488.  
  1489.     push    ax
  1490.     push    bx
  1491.     mov    bx, cpb_intail[si]    ; get the ptr to the next character
  1492.     call    emask_byte        ; get the error status byte and mask
  1493.     test    [bx], ax        ; and see if an error condition
  1494.     pop    bx
  1495.     pop    ax
  1496.  
  1497.     jz    no_brk_error            ; no errors
  1498.     or    ax, LSR_PARITY + LSR_FRAMING + LSR_OVERRUN
  1499.     test    cpb_mode[si], BREAK_IS_ERROR_OPTION
  1500.     jz    no_brk_error        ; break is not posible cause of
  1501.                     ; error
  1502.     or    ax, LSR_BREAK        ; break considered an error, set bit
  1503.  
  1504. no_brk_error:
  1505.     mov    ah, al            ; now get the actual line status in al
  1506.     lea    dx, MSR[di]        ; modem status register
  1507.     in    al, dx
  1508.  
  1509.                     ; even if DSR is low, if set to ignore
  1510.                     ; its state, set status high
  1511.  
  1512.     cmp    cpb_out_xoff[si], FALSE    ; is state currently in XOFF on output?
  1513.     jz    dsr_stat        ; no XOFF worries, see about DSR
  1514.  
  1515.                     ; XOFF is set, which should look like
  1516.                     ; a device not ready.  Fake it out.
  1517.     and    al, not LSR_TSRE + LSR_THRE
  1518.  
  1519. dsr_stat:
  1520.     test    cpb_mode[si], DTR_OPTION
  1521.     jz    no_dsr_concern        ; nope
  1522.     or    al, MSR_DSR        ; set the status bit
  1523.  
  1524. no_dsr_concern:
  1525.  
  1526.     cmp    timed_out, FALSE    ; any faked time outs?
  1527.     jz    no_time_outs        ; no time_outs;
  1528.     dec    timed_out        ; start zeroing it for next time
  1529.     or    ah, 080h        ; and set the time out flag
  1530.  
  1531. no_time_outs:
  1532.     ret
  1533. stat14    endp
  1534.  
  1535. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1536. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1537. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1538. ;;    New Functions:    All of these functions require that AH = 4, and
  1539. ;;            the sub function can be found in AL as follows:
  1540. ;;
  1541. ;;        if    AL = 0, return 0ff0 in AX to determine load status
  1542. ;;                 (actual value in 'special_return_value')
  1543. ;;
  1544. ;;        if    AL = 1, initialize mode as per CX, clear buffers.
  1545. ;;
  1546. ;;        if    AL = 2, then initialize with baudrates and other
  1547. ;;                params in CL, as if AL of AH = 0 init
  1548. ;;                BIT 6 & 5 ==> Baudrates
  1549. ;;                    0 , 1 ==> 19,200
  1550. ;;                    1 , 0 ==> 38,400
  1551. ;;                BIT 4 & 3 ==> Parity
  1552. ;;                BIT 2     ==> Stop Bits
  1553. ;;                BIT 1 & 0 ==> Word Length
  1554. ;;
  1555. ;;        if    AL = 3, set the timeout value as per CX
  1556. ;;
  1557. ;;        if    AL = 4, Clear the Input Buffer
  1558. ;;
  1559. ;;        if    AL = 5, Return count in Input buffer
  1560. ;;
  1561. ;;        if    AL = 6, Clear the Transmit Buffer
  1562. ;;
  1563. ;;        if    AL = 7, Return the count in the Transmit Buffer
  1564. ;;
  1565. ;;        if    AL = 8, uninstall the TSR driver, then release
  1566. ;;                memory
  1567. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1568. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1569.  
  1570. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1571. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1572. funcs_table    label    word        ;;
  1573.     dw    offset    new00        ;; Each function corresponds to the 
  1574.     dw    offset    new01        ;; AL value used for the sub-function
  1575.     dw    offset    new02        ;;
  1576.     dw    offset    new03        ;;
  1577.     dw    offset    new04        ;;
  1578.     dw    offset    new05        ;;
  1579.     dw    offset    new06        ;;
  1580.     dw    offset    new07        ;;
  1581.     dw    offset    new08        ;;
  1582. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1583. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1584.  
  1585. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1586. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1587. ;;    NEWFUNCS14
  1588. ;;
  1589. ;;    The new function dispatcher. Checks to make sure AL is valid and
  1590. ;;    returns 0xffff if not.
  1591. ;;
  1592. ;;    If AL is valid, then call the proper routine.
  1593. ;;
  1594. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1595. newfuncs14    proc    near
  1596. assume    ds:code
  1597.  
  1598.     cmp    al, 08h        ; out of bounds?
  1599.     jle    dispatch    ; no
  1600.     mov    ax, 0ffffh    ; yes, error code
  1601.     ret
  1602.  
  1603. dispatch:
  1604.     call    get_cpb            ; get si to point to the proper cpb
  1605.     mov    di, cpb_base[si]    ; point the ports!
  1606.     xor    bx, bx
  1607.     mov    bl, al
  1608.     shl    bx, 1
  1609.     mov    bx, funcs_table[bx]
  1610.     call    bx
  1611.     ret
  1612. newfuncs14    endp
  1613.  
  1614. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1615. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1616. ;;        if    AL = 0, return 0ff0 in AX to determine load status
  1617. ;;                 (actual value in 'special_return_value')
  1618. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1619. new00    proc    near
  1620.  
  1621.     mov    ax, special_return_value
  1622.     ret
  1623. new00    endp
  1624.  
  1625. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1626. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1627. ;;        if    AL = 1, initialize mode as per CX, clear buffers.
  1628. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1629. new01    proc    near
  1630.  
  1631.     mov    cpb_mode[si], cx    ; move the new mode in
  1632.     call    init_buffers        ; and reset the pointers
  1633.     ret
  1634. new01    endp
  1635.  
  1636. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1637. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1638. ;;        if    AL = 2, then initialize with baudrates and other
  1639. ;;                params in CL, as if AL of AH = 0 init
  1640. ;;                BIT 6 & 5 ==> Baudrates
  1641. ;;                    0 , 1 ==> 19,200
  1642. ;;                    1 , 0 ==> 38,400
  1643. ;;                BIT 4 & 3 ==> Parity
  1644. ;;                BIT 2     ==> Stop Bits
  1645. ;;                BIT 1 & 0 ==> Word Length
  1646. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1647. new02    proc    near
  1648.  
  1649.     lea    dx, LCR[di]            ; get the Latch
  1650.     in    al, dx
  1651.     or    al, LCR_DLAB            ; turn on the divisor
  1652.     out    dx, al                ; in the chip
  1653.  
  1654.     push    cx
  1655.     mov    ax, cx
  1656.  
  1657.     and    ax, 00e0h            ; only the highest three bits
  1658.     mov    cl, 5
  1659.     shr    ax, cl
  1660.     add    ax, 7                ; makes offset start at 8
  1661.                         ; (19200)
  1662.  
  1663.     call    get_baud            ; then get the correct divisor
  1664.                         ; allows higher than 9600
  1665.     pop    cx
  1666.  
  1667.     lea    dx, DATA[di]            ; get the base address
  1668.     out    dx, ax                ; output the whole word
  1669.  
  1670.     lea    dx, LCR[di]            ; get the Latch
  1671.     mov    al, cl                ; get the other parameters and
  1672.     and    al, 01fh            ; mask only parity, stop bits,
  1673.                         ; word length
  1674.     out    dx, al                ; set the params
  1675.  
  1676.     ret
  1677. new02    endp
  1678.  
  1679. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1680. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1681. ;;        if    AL = 3, set the timeout value as per CX
  1682. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1683. new03    proc    near
  1684.  
  1685.     mov    cpb_timeout[si], cx
  1686.     ret
  1687. new03    endp
  1688.  
  1689. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1690. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1691. ;;        if    AL = 4, Clear the Input Buffer
  1692. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1693. new04    proc    near
  1694.  
  1695.     cli
  1696.     mov    cpb_incnt[si], NO_CHARS
  1697.     mov    ax, cpb_inbase[si]
  1698.     mov    cpb_inhead[si], ax
  1699.     mov    cpb_intail[si], ax
  1700.     sti
  1701.     ret
  1702. new04    endp
  1703.  
  1704.  
  1705. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1706. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1707. ;;        if    AL = 5, Return count in Input buffer
  1708. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1709. new05    proc    near
  1710.     mov    ax, cpb_incnt[si]
  1711.     ret
  1712. new05    endp
  1713.  
  1714. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1715. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1716. ;;        if    AL = 6, Clear the Transmit Buffer
  1717. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1718. new06    proc    near
  1719.  
  1720.     cli
  1721.     mov    cpb_outcnt[si], NO_CHARS
  1722.     mov    ax, cpb_outbase[si]
  1723.     mov    cpb_outhead[si], ax
  1724.     mov    cpb_outtail[si], ax
  1725.     sti
  1726.     ret
  1727. new06    endp
  1728.  
  1729. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1730. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1731. ;;        if    AL = 7, Return the count in the Transmit Buffer
  1732. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1733. new07    proc    near
  1734.     mov    ax, cpb_outcnt[si]
  1735.     ret
  1736. new07    endp
  1737.  
  1738. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1739. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1740. ;;        if    AL = 8, uninstall the TSR driver, then release
  1741. ;;                memory
  1742. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1743. new08    proc    near
  1744. assume    ds:code
  1745.  
  1746.     mov    si, offset cpb1        ; set up for port1
  1747.     cmp    cpb_oint_add[si], 0    ; com port installed?
  1748.     jz    new0801        ; no
  1749.  
  1750.     call    unset_up        ; and kill everything associated
  1751.                     ; with this comm port
  1752.  
  1753. new0801:
  1754.     mov    si, offset cpb2        ; set up for port2
  1755.     cmp    cpb_oint_add[si], 0    ; com port installed?
  1756.     jz    new0802            ; no
  1757.  
  1758.     call    unset_up        ; and kill everything associated
  1759.                     ; with this comm port
  1760.  
  1761.  
  1762. new0802:
  1763.     cli
  1764.     mov    dx, old_int14
  1765.     mov    al, 014h
  1766.     push    ds
  1767.     mov    ds, old_int14[2]
  1768.     DOSINT    25h            ; reset the serial port interrupt
  1769.     pop    ds
  1770.  
  1771.     mov    dx, orig_timer
  1772.     mov    al, TIMER_TICK_INT_NO
  1773.     push    ds
  1774.     mov    ds, orig_timer[2]
  1775.     DOSINT    25h            ; reset the timer_tick interrupt
  1776.     pop    ds
  1777.  
  1778.     push    cs
  1779.     pop    es            ; free up our own memory
  1780.     DOSINT    49h            ; the environment
  1781.     sti
  1782.  
  1783.     ret
  1784. new08    endp
  1785.  
  1786. unset_up proc near
  1787. assume    ds:code
  1788.     mov    di, cpb_base[si]    ; get the base port
  1789.  
  1790.     cli
  1791.     in    al, INT_MASK_PORT    ; get the old flag from the 8259
  1792.     mov    bl, cpb_pic_mask[si]    ; get the interrupt enable mask
  1793.     not    bl            ; and turn it into a disable mask
  1794.     or    al, bl            ; turn off the masked bit
  1795.     out    INT_MASK_PORT, al
  1796.  
  1797.     lea    dx, IIR[di]
  1798.     xor    ax, ax            ; zero out for no ints
  1799.     out    dx, al            ; and turn off interrupts
  1800.  
  1801.     lea    dx, MCR[di]        ; turn off OUT2 on comm port
  1802.     in    al, dx
  1803.     and    al, not MCR_OUT2    ; hardware disable interrupts
  1804.     and    al, not MCR_DTR        ; and drop DTR
  1805.     out    dx, al
  1806.  
  1807.     mov    dx, cpb_oint_add[si]
  1808.     mov    al, cpb_int_no[si]
  1809.     push    ds
  1810.     mov    ds, cpb_oint_add[si][2]
  1811.     DOSINT    25h            ; reset the comm port interrupt
  1812.     pop    ds
  1813.  
  1814.     sti
  1815.     ret
  1816.  
  1817. unset_up endp
  1818.  
  1819.  
  1820. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1821. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1822. ;;    NEW_TICK
  1823. ;;
  1824. ;;    Takes over the timer tick, and simply reduces the timer count
  1825. ;;    every 1/18.2 of a second
  1826. ;;
  1827. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1828. new_tick    proc    far
  1829.     assume    ds:nothing
  1830.  
  1831.     pushf
  1832.     cmp    cs:timer_tick, 0
  1833.     jz    no_dec1
  1834.     dec    cs:timer_tick
  1835.  
  1836. no_dec1:
  1837.     cmp    cs:my_timer, 0
  1838.     jz    no_dec2
  1839.     dec    cs:my_timer
  1840.  
  1841. no_dec2:
  1842.     popf
  1843.     jmp    dword ptr cs:[orig_timer]
  1844. new_tick    endp
  1845.  
  1846.  
  1847. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1848. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1849. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1850. ;;            END OF RESIDENT CODE SPACE
  1851. my_size    equ    (($-code)/16 + 1)    ; waste a paragraph!
  1852. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1853. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1854. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1855.  
  1856.  
  1857. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1858. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1859. ;;        Messages only needed while installing
  1860. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1861. say_hi        db    0dh,0ah,"Installing COM driver....", 0dh,0ah,0ah,'$'
  1862. say_whoops    db    0dh,0ah,"COM driver already installed!",0dh,0ah,0ah,'$'
  1863. say_bad_port1    db    0dh,0ah,"Port 1 exists and is not 3F8!",0dh,0ah,0ah,'$'
  1864. say_bad_port2    db    0dh,0ah,"Port 2 exists and is not 2F8!",0dh,0ah,0ah,'$'
  1865.  
  1866.  
  1867. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1868. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1869. ;;    INITIALIZE
  1870. ;;
  1871. ;;    This routine gets called exactly once from the install routine
  1872. ;;    and sets up the 8250 and 8259 to generate and receive interrupts
  1873. ;;    It also resets and zeroes out each of the buffers
  1874. ;;
  1875. ;;    SI should point to the correct CPB
  1876. ;;
  1877. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1878. initialize    proc near
  1879.  
  1880.     push    ax
  1881.     push    dx
  1882.     push    di
  1883.  
  1884.     mov    di, cpb_base[si]    ;the port base address (2F8, 3F8)
  1885.  
  1886.     cli
  1887.     xor    ax,ax
  1888.     lea    dx, IER[di]        ; load the interrupt register in
  1889.     out    dx, al            ; and turn off *all* interrupts
  1890.     lea    dx, MCR[di]        ; modem control
  1891.     out    dx, al            ; turn off the sneaky bits
  1892.  
  1893.     call    init_buffers        ; then the buffers
  1894.             
  1895.     mov    al, IER_RDA        ; turn on ints for data ready
  1896.     lea    dx, IER[di]        ; load up the interrupt register
  1897.     out    dx, al            ; and turn 'em on
  1898.  
  1899.     in    al, INT_MASK_PORT    ; get the old flag from the 8259
  1900.     and    al, cpb_pic_mask[si]    ; turn off the masked bit
  1901.     out    INT_MASK_PORT, al
  1902.  
  1903.     lea    dx, MCR[di]        ; put OUT2 back on
  1904.     in    al, dx
  1905.     or    al, MCR_OUT2        ; hardware enable interrupts
  1906.     or    al, MCR_DTR        ; bring up DTR
  1907.     out    dx, al
  1908.  
  1909.     lea    dx, data[di]        ; clean out the receive buffer
  1910.     in    al, dx            ; a few times can't hurt
  1911.     in    al, dx
  1912.  
  1913.     pop    di
  1914.     pop    dx
  1915.     pop    ax
  1916.  
  1917.     sti                ;turn interrupts back on
  1918.     ret                ; and return
  1919.  
  1920. initialize    endp
  1921.  
  1922. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1923. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1924. ;;    INSTALL
  1925. ;;
  1926. ;;    First check to make sure TSRCOMM isn't already installed
  1927. ;;
  1928. ;;
  1929. ;;    For each of the comm ports, save the old interrupt vector address,
  1930. ;;    then remap the vector to point to the new routines, then
  1931. ;;    initialize the port with RX and error interrrupts turned on.
  1932. ;;
  1933. ;;    If the comm port  vector isn't what we expect, output an error
  1934. ;;    message and exit.
  1935. ;;
  1936. ;;    Only play with ports which exist
  1937. ;;
  1938. ;;    Take over the old timer_tick interrupt with the new one,
  1939. ;;    and take over the old serial interrupt routine
  1940. ;;
  1941. ;;    Finally, release the pointer to the environment, and go TSR!
  1942. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1943.  
  1944. install    proc near
  1945.     assume    ds:code
  1946.  
  1947.     push    cs        ; set data segment to point to code segment
  1948.     pop    ds
  1949.  
  1950.     mov    ax, 0400h    ; see if we exist
  1951.     int    14h        ; call the interrupt
  1952.  
  1953.     cmp    ax, special_return_value
  1954.     jnz    inst2        ; already loaded....
  1955.     lea    dx, say_whoops
  1956.     DOSINT    9h
  1957.  
  1958.     int    20h
  1959.  
  1960.  
  1961. inst2:
  1962.     lea    dx, say_hi
  1963.     DOSINT    9h
  1964.  
  1965.  
  1966.     push    es
  1967.     mov    si, 040h        ; the segment address for comm ports
  1968.     mov    es, si
  1969.  
  1970.     cmp    es:[0], PORT1        ; is it 3f8?
  1971.     jnz    exist1            ; no
  1972.     mov    ah, TRUE        ; yes. set the flag
  1973.     jmp    chk_port_2
  1974.  
  1975. exist1:
  1976.     cmp    word ptr es:[0], FALSE    ; does it exist?
  1977.     jz    chk_port_2
  1978.     lea    dx, say_bad_port1    ; something is there. No good!
  1979.     jmp    bomb_out
  1980.  
  1981. chk_port_2:
  1982.     cmp    es:[2], PORT2        ; is it 2f8?
  1983.     jnz    exist2            ; no
  1984.     mov    al, TRUE        ; yes. set the flag
  1985.     jmp    install_it
  1986.  
  1987. exist2:
  1988.     cmp    word ptr es:[2], FALSE    ; does it exist?
  1989.     jz    install_it        ; no
  1990.     lea    dx, say_bad_port2    ; something at 40:2 which isn't 2f8!
  1991.  
  1992. bomb_out:
  1993.     pop    es
  1994.     DOSINT    9h
  1995.  
  1996.     int    20h
  1997.  
  1998. install_it:
  1999.     pop    es
  2000.  
  2001.     cmp    ah, TRUE
  2002.     jnz    install_2
  2003.  
  2004.     push    ax
  2005.     mov    si, offset cpb1        ; set up for port1
  2006.     mov    dx, 0
  2007.     call    set_up            ; set it up
  2008.     pop    ax
  2009.  
  2010. install_2:
  2011.  
  2012.     cmp    al, TRUE
  2013.     jnz    install_3
  2014.  
  2015.     mov    si, offset cpb2        ; set up for port2
  2016.     mov    dx, 1
  2017.     call    set_up            ; set it up
  2018.  
  2019.  
  2020. install_3:
  2021.     mov    al, 14h            ; save the old interrupt 14 services
  2022.     DOSINT    35h
  2023.     mov    old_int14, bx
  2024.     mov    old_int14[2], es
  2025.  
  2026.     lea    dx, new_14        ; steal the int 14 vector
  2027.     mov    al, 14h
  2028.     DOSINT    25h
  2029.  
  2030.     mov    al, TIMER_TICK_INT_NO    ; save the "user" interrupt, not 08h
  2031.     DOSINT    35h
  2032.     mov    orig_timer, bx
  2033.     mov    orig_timer[2], es
  2034.  
  2035.     lea    dx, new_tick        ; steal the timer tick
  2036.     mov    al, TIMER_TICK_INT_NO
  2037.     DOSINT    25h
  2038.  
  2039.     mov    es, env_ptr    ; free up the little memory used by
  2040.     DOSINT    49h        ; the environment
  2041.  
  2042.     mov    dx, my_size    ; so, we get to junk outselves!
  2043.     DOSINT    31h
  2044.  
  2045. install    endp
  2046.  
  2047. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2048. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2049. ;;    SET_UP
  2050. ;;
  2051. ;;    Save and replace the approriate interrupt vectors, then call
  2052. ;;    initialize for the CPB pointed to by SI
  2053. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  2054. set_up proc    near
  2055.  
  2056.     mov    al, cpb_int_no[si]
  2057.     DOSINT    35h
  2058.     mov    cpb_oint_add[si], bx
  2059.     mov    cpb_oint_add[si][2], es
  2060.  
  2061.     mov    al, cpb_int_no[si]
  2062.     mov    dx, cpb_nint_off[si]
  2063.     DOSINT    25H
  2064.  
  2065.     mov    al, DEF_INIT        ; init through the BIOS first
  2066.     mov    ah, 0
  2067.     int    14h
  2068.     call    initialize
  2069.     ret
  2070. set_up    endp
  2071.  
  2072. code    ends
  2073.     end    start
  2074.  
  2075.