home *** CD-ROM | disk | FTP | other *** search
/ Oakland CPM Archive / oakcpm.iso / sigm / vol229 / apcserio.lbr / SERIALIO.A86 next >
Text File  |  1986-02-10  |  13KB  |  457 lines

  1.     title    'Interrupt-Driven Serial Port Handler'
  2.  
  3. ;    THIS PROGRAM IS HEREBY PLACED IN THE PUBLIC DOMAIN,
  4. ;        AND MAY BE FREELY COPIED AND DISTRIBUTED.
  5.  
  6. ;            Context Sensitive Inc.
  7. ;            4200 Aurora Ave. N.
  8. ;            Seattle, WA  98103
  9. ;            (206) NEC-0301
  10. ;
  11. ;    Program:    SERIALIO.A86
  12. ;
  13. ;    Version:    1.2            23 February 1984
  14. ;    Author:        Ron Blanford
  15.  
  16.  
  17. ;    This is only a fragment of a program.  The routines given
  18. ;    here can be included in any (assembly language) program,
  19. ;    and called as required to perform input and output to the
  20. ;    primary serial (RS232) port on the NEC Advanced Personal
  21. ;    Computer.  These routines duplicate functions that are
  22. ;    provided by CP/M-86, with the difference that input from
  23. ;    the serial port is handled on an interrupt-driven basis
  24. ;    and characters are stored until requested.
  25. ;
  26. ;    To add these routines to your program, insert just prior
  27. ;    to the END statement of your program the statement
  28. ;        INCLUDE SERIALIO.A86
  29. ;
  30. ;    These routines preserve all registers, except possibly AL
  31. ;    and the flags.
  32. ;
  33. ;    The routines available to your program are:
  34. ;
  35. ;    1.  sio_rxstatus:  Returns -1 in AL if a character is
  36. ;               available in the serial input buffer,
  37. ;               and 0 if no character is waiting.
  38. ;
  39. ;    2.  sio_receive:   Returns a character from the serial
  40. ;               input buffer in AL.  If no character
  41. ;               is available, it waits until one
  42. ;               arrives.
  43. ;
  44. ;    3.  sio_txstatus:  Returns -1 in AL if the transmit
  45. ;               buffer is empty and a character may be
  46. ;               transmitted, and 0 if transmission is
  47. ;               not possible.
  48. ;
  49. ;    4.  sio_transmit:  Sends the character in AL out the
  50. ;               serial port.  If the port is not ready,
  51. ;               it waits until it is.
  52. ;
  53. ;    5.  sio_start:     Called once to initialize the port
  54. ;               before using it.  Sets initial values:
  55. ;                  baud = 300, parity = none
  56. ;                  data = 8, stop = 1.
  57. ;
  58. ;    6.  sio_finish:    Called once to de-initialize the port
  59. ;               just before ending your program.
  60. ;
  61. ;    7.  sio_setmode:   Called to set the baud rate and mode
  62. ;               of the port.  DX contains the parameters
  63. ;               that are described in the CP/M-86 System
  64. ;               Reference Guide, page 5-40, as follows:
  65. ;
  66. ;    DH = baud rate    DL = asynchronous mode byte
  67. ;     0 =   150 bps      7   6   5   4   3   2   1   0
  68. ;     1 =   200 bps    _________________________________
  69. ;     2 =   300 bps    |       |       |       | 1   0 |
  70. ;     3 =   600 bps    ----^-------^-------^-------^----
  71. ;     4 =  1200 bps        |       |       |       +- must be 10
  72. ;     5 =  2400 bps        |       |       +- data bits: 00=5, 01=6
  73. ;     6 =  4800 bps        |       |                     10=7, 11=8
  74. ;     7 =  9600 bps        |       +--- parity: 00 or 10=none
  75. ;     8 = 19200 bps        |                    01=odd, 11=even
  76. ;                +--- stop bits: 01=1, 10=1.5
  77. ;                                11=2, 00 illegal
  78. ;
  79. ;            If DH is greater than 8, the baud rate will
  80. ;            not be changed.  If DL is 0, the mode will
  81. ;            not be changed.
  82. ;
  83. ;
  84. ;    An alternative entry point to these routines which is
  85. ;    more suitable for high-level languages is to use the
  86. ;    initial jump vector table.  The vectors are allocated
  87. ;    in the same sequence as listed above, and are each the
  88. ;    standard 3-byte relative jump instruction.  The address
  89. ;    of the vector table depends on the load address assigned
  90. ;    by the high-level language.
  91.  
  92.  
  93.     cseg $
  94.  
  95. sio_jumptable:
  96.     jmp    sio_rxstatus        ; receive status
  97.     jmp    sio_receive        ; receive character
  98.     jmp    sio_txstatus        ; transmit status
  99.     jmp    sio_transmit        ; transmit character
  100.     jmp    sio_start        ; initialize serial port
  101.     jmp    sio_finish        ; de-initialize serial port
  102.     jmp    sio_setmode        ; set port characteristics
  103.  
  104.  
  105. ; Interrupt vector locations, in data segment 0
  106.  
  107. sio_offset    equ    84h        ; Sio interrupt offset
  108. sio_segment    equ    86h        ; Sio interrupt segment
  109.  
  110.  
  111. ; 8259 Interrupt controller (master) port addresses
  112.  
  113. ic_command    equ    20h        ; Interrupt command register
  114. ic_mask        equ    22h        ; Interrupt mask register
  115.  
  116. ; 8259 commands and masks
  117.  
  118. icmd_endInt    equ    20h        ; End of interrupt
  119. imask_timerOff    equ    08h        ; Disable timer interrupt
  120. imask_sioOff    equ    02h        ; Disable RS232 interrupt
  121.  
  122.  
  123. ; 8253-5 Interval timer port addresses
  124.  
  125. timer_data    equ    2Bh        ; Baud set (for channel 1)
  126. timer_command    equ    2Fh        ; Baud timer command port
  127.  
  128. ; 8253 Timer commands
  129.  
  130. tcmd_ch1    equ    76h        ; Select & init channel 1
  131.  
  132.  
  133. ; 8251A USART controller port addresses
  134.  
  135. sio_data    equ    30h        ;     Data port
  136. sio_sts1    equ    32h        ; in  Status port 1
  137. sio_sts2    equ    34h        ; in  Status port 2
  138. sio_command    equ    32h        ; out Command port
  139. sio_mask    equ    34h        ; out Interrupt mask port
  140. sio_disable    equ    36h        ; out Disable trans. port
  141.  
  142. ; 8251 status port 1 bits
  143.  
  144. sio_RxRDY    equ    02h        ; Receive ready value
  145. sio_TxRDY    equ    01h        ; Send ready value
  146. sio_DSR        equ    80h        ; Data set ready
  147.  
  148. ; 8251 status port 2 bits
  149.  
  150. sio_CS        equ    04h        ; Clear to send
  151.  
  152. ; 8251 initialization instructions
  153.  
  154. ;    command instructions
  155.  
  156. scmd_TxE    equ    01h        ; Transmit enable
  157. scmd_DTR    equ    02h        ; DTR signal high
  158. scmd_RxE    equ    04h        ; Receive enable
  159. scmd_BRK    equ    08h        ; Send break
  160. scmd_ERR    equ    10h        ; Error reset
  161. scmd_RTS    equ    20h        ; RTS signal high
  162. scmd_MODE    equ    40h        ; Reset - accept mode inst.
  163. scmd_HUNT    equ    80h        ; Hunt for sync characters
  164.  
  165. ;    mode instructions
  166.  
  167. smode_1x    equ    01h        ; Baud rate factor: 1x
  168. smode_16x    equ    02h        ;                   16x
  169. smode_64x    equ    03h        ;                   64x
  170. smode_5data    equ    00h        ; Data bits: 5
  171. smode_6data    equ    04h        ;            6
  172. smode_7data    equ    08h        ;            7
  173. smode_8data    equ    0Ch        ;            8
  174. smode_pnone    equ    00h        ; Parity: none
  175. smode_podd    equ    10h        ;         odd
  176. smode_peven    equ    30h        ;         even
  177. smode_1stop    equ    40h        ; Stop bits: 1
  178. smode_15stop    equ    80h        ;            1.5
  179. smode_2stop    equ    0C0h        ;            2
  180.  
  181. ; 8251 interrupt mask port bits
  182.  
  183. sint_txOff    equ    01h        ; Transmit complete int.
  184. sint_rxOff    equ    02h        ; Receive complete int.
  185. sint_tbeOff    equ    04h        ; Trans. buffer empty int.
  186.  
  187.  
  188.  
  189. ; sio_rxstatus:    returns -1 in AL if a character has been received
  190. ;        returns 0 if the input interrupt buffer is empty.
  191.  
  192. sio_rxstatus:
  193.     cmp    sio_chars,0        ; Any chars in the buffer?
  194.     je    siorx1            ; If not, report failure
  195.     or    al,0FFh            ; Otherwise give positive
  196.     jmps    siorx2            ;   response
  197. siorx1:    and    al,0
  198. siorx2:    ret
  199.  
  200.  
  201. ; sio_receive:    returns the next input character in AL
  202. ;        waits for a character if none have arrived.
  203.  
  204. sio_receive:
  205.     call    sio_rxstatus        ; Wait for a character
  206.     jz    sio_receive
  207.     push    bx
  208.     cli                ; Prevent interrupts.
  209.     dec    sio_chars        ; Uncount the character.
  210.     mov    bx,sio_charPtr        ; Get the buffer pointer.
  211.     inc    bx            ; Point to the next char.
  212.     cmp    bx,offset sio_buffer+sio_size    ; Past the end?
  213.     jb    sio_r2
  214.     lea    bx,sio_buffer        ; If so wrap to the start.
  215. sio_r2: mov    sio_charPtr,bx        ; Save the updated pointer.
  216.     mov    al,[bx]            ; Get the character.
  217.     sti                ; Re-enable interrupts.
  218.     pop    bx
  219.     ret
  220.  
  221.  
  222. ; sio_int:    Handles the serial port input interrupts
  223.  
  224. sio_int:
  225.     cli                ; Prevent interrupts.
  226.     push    ax            ; Save registers we'll use.
  227.     push    bx
  228.     push    ds
  229.     mov    ax,cs:sio_dataSeg    ; Get our own data segment.
  230.     mov    ds,ax
  231.  
  232.     call    sio_process        ; Receive and store char.
  233.  
  234.     mov    al,icmd_endInt        ; Signal End of Interrupt.
  235.     out    ic_command,al
  236.     pop    ds            ; Restore registers we used.
  237.     pop    bx
  238.     pop    ax
  239.     iret                ; Return from the interrupt.
  240.  
  241.  
  242. ; sio_process:    Reads the input character from the serial port and
  243. ;        stores it in the ring buffer.
  244.  
  245. sio_process:
  246.     in    al,sio_sts1        ; Get the port status.
  247.     and    al,sio_RxRDY        ; Is a character waiting?
  248.     jz    sio_p3            ;    No, just a false alarm.
  249.     in    al,sio_data        ; Otherwise read character.
  250.     cmp    sio_chars,sio_size    ; Is the buffer full?
  251.     je    sio_p3            ; If so, discard character.
  252.     inc    sio_chars        ; Otherwise count character.
  253.     mov    bx,sio_spacePtr        ; Point to the next space.
  254.     inc    bx            ; Increment pointer.
  255.     cmp    bx,offset sio_buffer+sio_size ; Past the end?
  256.     jb    sio_p2
  257.     lea    bx,sio_buffer        ; Yes, wrap to the start.
  258. sio_p2:    mov    sio_spacePtr,bx        ; Save the pointer.
  259.     mov    [bx],al            ; Save the character.
  260. sio_p3:    ret
  261.  
  262.  
  263. ; sio_txstatus:    returns -1 in AL if the serial port can accept a
  264. ;        character for transmission, returns 0 otherwise.
  265.  
  266. sio_txstatus:
  267.     in    al,sio_sts1        ; Read status port 1.
  268.     and    al,sio_DSR+sio_TxRDY    ; Mask DSR and TxRDY bits.
  269.     xor    al,sio_DSR+sio_TxRDY    ; Check that both are set.
  270.     jnz    siotx1            ; If not, port is not ready.
  271.     in    al,sio_sts2        ; Read status port 2.
  272.     and    al,sio_CS        ; Check for clear to send.
  273.     jz    siotx1            ; If not, return failure.
  274.     or    al,0FFh            ; Otherwise ready to send.
  275.     jmps    siotx2
  276. siotx1:    and    al,0
  277. siotx2:    ret
  278.  
  279.  
  280. ; sio_transmit:    sends the character in AL to the serial port.
  281. ;        doesn't return until the character has been sent.
  282.  
  283. sio_transmit:
  284.     push    ax            ; Save character to be sent.
  285. sio_t1:    call    sio_txstatus        ; Wait for port to be ready.
  286.     jz    sio_t1
  287.     pop    ax
  288.     out    sio_data,al        ; Send the character.
  289.     ret
  290.  
  291.  
  292. ; sio_start:    Initializes the interrupt vectors and controller
  293. ;        and the serial port to default values
  294.  
  295. sio_start:
  296.     cmp    sio_init,0        ; Skip initialization
  297.     jne    sio_s1            ;   if already performed.
  298.     mov    sio_init,0FFh        ; Set "initialized" flag.
  299.  
  300.     push    ax            ; Save registers we'll use.
  301.     push    dx
  302.     push    es
  303.  
  304.     mov    ax,ds            ; Save data segment in CSEG
  305.     mov    cs:sio_dataSeg,ax    ;   for the int. handler.
  306.  
  307.     in    al,ic_mask        ; Get current interrupt mask
  308.     mov    sio_oldInt,al        ; Save for later restoration
  309.  
  310.     mov    ax,0            ; Point interrupt vector
  311.     mov    es,ax            ;   table in page zero.
  312.     mov    ax,es:.sio_segment    ; Save the current vector
  313.     mov    sio_oldSeg,ax        ;   segment and offset.
  314.     mov    ax,es:.sio_offset
  315.     mov    sio_oldOff,ax
  316.  
  317.     cli
  318.     mov    ax,cs            ; Replace with address of
  319.     mov    es:.sio_segment,ax    ;    interrupt handler.
  320.     mov    ax,offset sio_int
  321.     mov    es:.sio_offset,ax
  322.  
  323.     mov    dh,8            ; Set default baud 19200
  324.     mov    dl,smode_1stop+smode_pnone+smode_8data+smode_16x
  325.     call    sio_setmode        ;   and default mode.
  326.  
  327.     mov    al,00h            ; Reset disable register.
  328.     out    sio_disable,al
  329.  
  330.     in    al,sio_data        ; Clear input buffer.
  331.  
  332.     mov    al,sint_txOff+sint_tbeOff ; Set serial int. mask.
  333.     out    sio_mask,al
  334.  
  335.     in    al,ic_mask        ; Set master controller to
  336.     or    al,imask_timerOff    ;   disable timer
  337.     and    al,not imask_sioOff    ;   and enable sio.
  338.     out    ic_mask,al
  339.     sti
  340.  
  341.     pop    es            ; Restore our registers.
  342.     pop    dx
  343.     pop    ax
  344.  
  345. sio_s1:    ret
  346.  
  347.  
  348. ; sio_finish:    This routine restores the interrupt controller
  349. ;        mask and vector table to the original values.
  350.  
  351. sio_finish:
  352.     cmp    sio_init,0        ; Was initialization done?
  353.     je    sio_f1            ;    If not, don't undo it.
  354.     mov    sio_init,0
  355.  
  356.     push    ax            ; Save our registers.
  357.     push    es
  358.     cli                ; Prevent interrupts.
  359.  
  360.     mov    al,sio_oldInt        ; Restore the old mask.
  361.     out    ic_mask,al
  362.  
  363.     mov    ax,0            ; restore the old vector.
  364.     mov    es,ax
  365.     mov    ax,sio_oldSeg
  366.     mov    es:.sio_segment,ax
  367.     mov    ax,sio_oldOff
  368.     mov    es:.sio_offset,ax
  369.  
  370.     sti
  371.     pop    es            ; Restore our registers.
  372.     pop    ax
  373.  
  374. sio_f1:    ret
  375.  
  376.  
  377. ; sio_setmode:    sets the baud rate, data bits, parity, and stop
  378. ;        bits for the serial port.  DH contains an index to
  379. ;        the baud rate table, and DL contains the mode byte.
  380.  
  381. sio_setmode:
  382.     push    ax            ; Save our registers.
  383.     push    bx
  384.  
  385.     or    dl,dl            ; See if mode is to be set:
  386.     jz    sio_m1            ;   not if DL = 0.
  387.  
  388.     mov    al,0            ; Suggested reset sequence:
  389.     out    sio_command,al        ;    3 zeros and scmd_MODE.
  390.     mov    al,0
  391.     out    sio_command,al
  392.     mov    al,0
  393.     out    sio_command,al
  394.     mov    al,scmd_MODE
  395.     out    sio_command,al
  396.  
  397.     push    ax            ; waste some time to allow
  398.     pop    ax            ;   the 8251 time to reset
  399.  
  400.     mov    al,dl            ; Set mode with supplied
  401.     out    sio_command,al        ;   parameters.
  402.  
  403.     mov    al,scmd_RTS+scmd_ERR+scmd_RxE+scmd_DTR+scmd_TxE
  404.     out    sio_command,al         ; RTS & DTR high, Tx & Rx on
  405.  
  406. sio_m1:    mov    al,dh            ; Get the baud rate index.
  407.     cmp    al,8            ; Validate range (0-8).
  408.     ja    sio_m2
  409.  
  410.     lea    bx,cs:sio_baud        ; Point to baud table.
  411.     add    al,al            ; Make index a word offset.
  412.     mov    ah,0
  413.     add    bx,ax            ; Point to desired entry.
  414.     mov    al,tcmd_ch1        ; Set timer channel 1 mode.
  415.     out    timer_command,al
  416.     mov    ax,cs:[bx]        ; Get the baud table entry.
  417.     out    timer_data,al        ; Output low byte.
  418.     mov    al,ah
  419.     out    timer_data,al        ; Output high byte.
  420.  
  421. sio_m2:    pop    bx            ; Restore our registers.
  422.     pop    ax
  423.     ret
  424.  
  425.  
  426.  
  427. sio_dataSeg    dw    0        ; Storage in CSEG to point
  428.                     ; to DSEG.
  429.  
  430.  
  431. ;    Interval Timer values (assumes 16x baud rate mode)
  432.  
  433. sio_baud    dw    0400h        ; 150 baud    0
  434.         dw    0300h        ; 200 baud    1
  435.         dw    0200h        ; 300 baud    2
  436.         dw    0100h        ; 600 baud    3
  437.         dw    0080h        ; 1200 baud    4
  438.         dw    0040h        ; 2400 baud    5
  439.         dw    0020h        ; 4800 baud    6
  440.         dw    0010h        ; 9600 baud    7
  441.         dw    0008h        ; 19200 baud    8
  442.  
  443.  
  444.     dseg $
  445.  
  446. ; Impure storage with contents that are changed dynamically
  447.  
  448. sio_init    db    0        ; "Initialized" flag.
  449. sio_oldInt     db    0        ; 8259 interrupt mask
  450. sio_oldSeg    dw    0        ; Sio interrupt vector
  451. sio_oldOff    dw    0
  452.  
  453. sio_size    equ    128        ; Size of circular buffer.
  454. sio_buffer    rb    sio_size    ; Circular character buffer.
  455. sio_spacePtr    dw    sio_buffer    ; Char. insertion pointer.
  456. sio_charPtr    dw    sio_buffer    ; Char. retrieval pointer.
  457. sio_cha