home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / assemblr / library / sampler0 / tsrcomm.asm < prev    next >
Assembly Source File  |  1987-09-09  |  63KB  |  2,061 lines

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