home *** CD-ROM | disk | FTP | other *** search
/ Power-Programmierung / CD1.mdf / forth / compiler / fpc / source / rs232ib.seq < prev    next >
Encoding:
Text File  |  1990-01-29  |  20.1 KB  |  500 lines

  1. \\ RS232IB.SEQ     A BUFFERED CHIP LEVEL RS-232 driver for 8250 chip
  2.  
  3.   This code supports both RS232 ports, but ONLY ONE AT A TIME !!  You
  4. can select either COM1, or COM2 for serial operation. Input is buffered
  5. with a 256 character buffer, so you shouldn't have trouble with receive
  6. overrun.
  7.  
  8.   A simple terminal program is included as an example of usage. If you
  9. use TCOM to target compile this file, you will get a VERY SIMPLE terminal
  10. program that runs in FULL DUPLEX.
  11.  
  12.   This file can be compiled on either F-PC, or TCOM. If you compile it
  13. on F-PC, then the installed interrupt vectors will automatically be
  14. un-installed when you leave F-PC.
  15.  
  16. ****************************** WARNING ******************************
  17.  
  18.           IF YOU USE TCOM TO COMPILE THIS FILE THEN YOU WILL
  19.         NEED TO USE ?REST_COM1: OR ?REST_COM2: TO RESTORE WHICH
  20.         EVER COMM PORT INTERRUPTS YOU WERE USING IN THE
  21.         APPLICATION BEFORE LEAVING THE PROGRAM OR YOU WILL
  22.         SURELY **CRASH** LATER!!!!
  23.  
  24. **********************************************************************
  25.  
  26. {
  27.  
  28. decimal
  29.  
  30. \ ***************************************************************************
  31. \ the next few lines define immediate words that allow definitions to
  32. \ select what will be compiled from source lines for either F-PC or TCOM.
  33.  
  34. DEFINED TARGET-INIT NIP 0= #IF  \ Test for NOT target compiling
  35.  
  36. ' noop alias \F immediate       \ create \F as a NOOP while in F-PC
  37. ' \    alias \T immediate       \ create \T as "\" while in F-PC
  38.  
  39. #ELSE
  40.  
  41. ' \    alias \F immediate       \ create \F as "\" while TCOMing
  42. ' noop alias \T immediate       \ create \T as a NOOP while TCOMing
  43.  
  44. #ENDIF
  45.  
  46. \f code int3       ( -- )
  47. \f                 int 3
  48. \f                 next            end-code
  49.  
  50. \ ***************************************************************************
  51.  
  52. variable rs232_base     \ holds physical address where rs232 boards reside.
  53.                         \ $40:00 is where first  board resides,
  54.                         \ $40:02 is where second board resides if present.
  55. $40 rs232_base !
  56.  
  57. $20 constant EOI        \ End Of Interrupt
  58. $20 constant ictla      \ 8259 interrupt controller #1
  59.  
  60. variable rs232_port     \ port value initialized by COM-INIT and used by
  61.                         \ other com words.
  62. $00 rs232_port !
  63.  
  64. \       COM port buffer configuration.
  65.  
  66. \       [ cnt ][ data up to COMLIMIT ... ]
  67.  
  68. 256 constant bufsize
  69.  
  70. variable imask          \ 8259 interrupt mask bit variable
  71.                         \ COM1: = $10, COM2: = $08
  72. variable comEOI         \ 8259 End Of Interrupt, for specific interrupt
  73.                         \ INT4 (com1) = $64, INT3 (com2) = $63
  74. variable bufin          \ pointer for buffer input
  75. variable bufout         \ pointer for buffer extract
  76. variable intcnt
  77.  
  78. bufsize array combuf
  79.  
  80. variable interrupts     \ counter for the nubmer of interrupts received
  81.  
  82. 0 value rate
  83.  
  84. : baud          ( n1 --- )   \ convert baud value to timer value "rate"
  85.                 115200. rot um/mod nip =: rate ;
  86.  
  87. \ Use in the form "9600 baud". Any of the standard baud rates can be used
  88. \ up to 115200 baud. Non-standard baud rates can also be used, but
  89. \ accuracy will suffer at non0standard baud rates above 2400 baud.
  90.  
  91.  
  92. code com-init   ( parity+stops+bits  baud_val com_port --- )
  93. \T              CLEAR_LABELS
  94. \T              SAVE_BX
  95. \T              xchg si, sp
  96.                 pop bx                          \ comm port # 0 or 1
  97.                 shl bx, # 1                     \ convert to word offset
  98.                 push ds                         \ preserve DS
  99.                 mov ds, rs232_base              \ setup ds to get board base
  100.                 mov dx, 0 [bx]                  \ get port address of board
  101.                                                 \ from $40:$00 or $40:$02.
  102.                                                 \ DX = BASE + 0
  103.                 pop ds                          \ restore DS
  104.                 mov rs232_port dx               \ save port # in value
  105.                 add dx, # 3                     \ DX = BASE + 3
  106.                 mov al, # $80                   \ [DLAB]
  107.                 out dx, al
  108.                 sub dx, # 2                     \ DX = BASE + 1
  109.                 pop ax                          \ baud timer value
  110.                 xchg al, ah                     \ get high part into AL
  111.                 out dx, al                      \ set high part
  112.                 dec dx                          \ DX = BASE + 0
  113.                 jmp 0 $                         \ wait a few clock cycles
  114.            0 $: xchg al, ah                     \ low part into AL
  115.                 out dx, al                      \ set low part
  116.                 add dx, # 3                     \ adj to control reg
  117.                                                 \ DX = BASE + 3
  118.                 pop ax                          \ pop parity, stops, & bits
  119.                 out dx, al                      \ set parity, stops, & bits
  120.                 jmp  1 $                        \ wait some more clocks
  121.            1 $: sub dx, # 2                     \ DX = BASE + 1
  122. \ interrupt enable stuff follows...
  123.                 in al, dx                       \ get current intterupt stat
  124.                 or al, # $01                    \ include DAV interrupt bit
  125.                 out dx, al                      \ interrupt enables off
  126.                 add dx, # $03                   \ DX = BASE + 4
  127.                 in al, dx                       \ PC bus interrupt control
  128.                 or al, # $08                    \ enable chip to bus
  129.                 out dx, al                      \ put back into register
  130.  
  131.                 in al, # ictla 1+               \ get int mask reg from 8259
  132.                 jmp 3 $
  133.            3 $: mov ah, imask                   \ mask bit in 8259
  134.                                                 \ $10 = IRQ4, $08 = IRQ3
  135.                 not ah                          \ compliment for AND to follow
  136.                 and al, ah                      \ clear com port INT bit
  137.                 out # ictla 1+ al               \ restore new mask
  138.  
  139.                 mov intcnt # 0 word             \ zero out interrupt counter
  140.                 mov dx, rs232_port              \ get the comport we're using
  141.                 in al, dx                       \ read port to clear it
  142.                 jmp 2 $
  143.            2 $: in al, dx                       \ read port to clear it again
  144. \F              next
  145. \T              XCHG SI, SP
  146. \T              LOAD_BX
  147. \T              RET
  148.                 end-code
  149.  
  150. code rs232_intoff ( -- )                \ interrupt clear stuff follows...
  151. \T              CLEAR_LABELS
  152.                 \ about to SET port interrupt mask bits to disable interrupts
  153.                 cli
  154.                 in al, # ictla 1+               \ get int mask reg from 8259
  155.                 jmp 0 $
  156.            0 $: or al, imask                    \ SET com port MASK INT bits
  157.                                                 \ $10 = IRQ4, $08 = IRQ3
  158.                 out # ictla 1+ al               \ restore new mask
  159.                 mov al, comEOI          \ Specific EOI for interrupt 4
  160.                                         \ INT4=$64, INT3=$63
  161.                 out # ictla al
  162.                 sti
  163.                 mov dx, rs232_port
  164.                 inc dx                          \ DX = BASE + 1
  165.                 in al, dx                       \ get current intterupt stat
  166.                 mov ah, # $01                   \ DAV interrupt bit
  167.                 not ah                          \ compliment for and
  168.                 and al, ah                      \ clear DAV interrupt bit
  169.                 out dx, al                      \ interrupt enables off
  170.                 add dx, # $03                   \ DX = BASE + 4
  171.                 in al, dx                       \ PC bus interrupt control
  172.                 mov ah, # $08                   \ chip bus enable bit
  173.                 not ah                          \ we want to clear it
  174.                 and al, ah                      \ clear bus enable bit
  175.                 out dx, al                      \ put back into register
  176. \F              next
  177. \T              RET
  178.                 end-code
  179.  
  180.  
  181. code com-out    ( c1 --- )
  182. \T              CLEAR_LABELS
  183.                 mov dx, rs232_port              \ get the comport we're using
  184.                 mov di, dx                      \ copy into DI
  185.                 add dx, # 5
  186.                 mov cx, # $FFFF                 \ timeout value
  187.            2 $: dec cx
  188.                 j0= 3 $                         \ leave if timed out
  189.                 in al, dx                       \ get status port
  190.                 and al, # $20
  191.                 cmp al, # $20                   \ transmit register empty
  192.                 j<> 2 $
  193.            3 $: mov dx, di                      \ back to data port
  194. \F              pop ax                          \ get actual data to send
  195. \T              mov ax, bx
  196.                 out dx, al                      \ and then send the byte
  197. \F              next
  198. \T              LOAD_BX
  199. \T              RET
  200.                 end-code
  201.  
  202. code com-in     ( --- c1 )
  203. \T              CLEAR_LABELS
  204. \T              SAVE_BX
  205.                 mov dx, rs232_port              \ get the comport we're using
  206.                 mov di, dx                      \ copy into DI
  207.                 add dx, # 5
  208.                 mov cx, # $FFFF                 \ time out value
  209.            4 $: dec cx                          \ start time out
  210.                 j0= 5 $                         \ leave if timeout
  211.                 in al, dx                       \ get status port
  212.                 and al, # $01
  213.                 cmp al, # $01                   \ receive buffer full?
  214.                 j<> 4 $
  215.            5 $: mov dx, di                      \ back to data port
  216.                 in al, dx                       \ get the byte
  217.                 sub ah, ah                      \ clear high byte
  218. \F              1push
  219. \T              MOV BX, AX
  220. \T              RET
  221.                 end-code
  222.  
  223. code com-stat   ( --- c1 )
  224. \T              SAVE_BX
  225.                 mov dx, rs232_port              \ get the comport we're using
  226.                 mov di, dx                      \ copy into DI
  227.                 add dx, # 5
  228.                 in al, dx                       \ get status port
  229.                 sub ah, ah                      \ clear high byte
  230. \F              1push
  231. \T              MOV BX, AX
  232. \T              RET
  233.                 end-code
  234.  
  235. \T HERE-T CONSTANT COM_INT
  236.  
  237. \T CODE %%com_int  ( --- )         \ COM port interrupt handler
  238. \T              CLEAR_LABELS
  239. \F LABEL com_int
  240.                 push es         \ save all of the registers
  241.                 push ds
  242.                 push dx
  243.                 push si
  244.                 push di
  245.                 push cx
  246.                 push bx
  247.                 push ax
  248.                 mov ax, cs                      \ setup DS: = CS:
  249. \T              add ax, cs: $103 \ address contains offset to base of DS: seg
  250.                 mov ds, ax
  251.                 inc interrupts word
  252. \ test for a waiting char, and get it into AL
  253.                 mov dx, rs232_port              \ get the comport we're using
  254.                 mov di, dx                      \ copy into DI
  255.            2 $: add dx, # 5
  256.                 in al, dx                       \ get status port
  257.                 and al, # $01
  258.                 cmp al, # $01                   \ receive buffer full?
  259.                 j<> 1 $                         \ ignore if not valid
  260.                 mov dx, di                      \ back to data port
  261.                 in al, dx                       \ get the byte
  262. \ save away the character and bump buffer counter
  263.                 mov bl, bufin                   \ get buffer input pointer
  264.                 cmp bl, bufout                  \ is buffer full?
  265.                 j=  1 $                         \ if full discard char
  266.                 sub bh, bh                      \ clear high part of BX
  267.                 add bx, # combuf                \ add base of COMBUF
  268.                 mov 0 [bx], al                  \ save the character
  269.                 inc bufin byte                  \ bump count
  270.                 sub ax, ax
  271.                 j 2 $                           \ may need to get additional
  272. \ restore everything and return from interrupt
  273.            1 $: mov al, # EOI
  274.                 out # ictla al
  275.                 inc intcnt word                 \ bump interrupt counter
  276.                 pop ax          \ restore all of the registers
  277.                 pop bx
  278.                 pop cx
  279.                 pop di
  280.                 pop si
  281.                 pop dx
  282.                 pop ds
  283.                 pop es
  284.                 iret            end-code
  285.  
  286. variable com1_int#      $0C com1_int# !
  287. variable com2_int#      $0B com2_int# !
  288.  
  289. 0 value commport
  290. 2variable comsave1              \ a place to save the old interrupt vector 4
  291. 2variable comsave2              \ a place to save the old interrupt vector 3
  292.  
  293. code rest_com1: ( --- )                 \ restores COM1: interrupt vector
  294.                 push ds
  295.                 mov dx, comsave1
  296.                 mov ax, # $2500
  297.                 add ax, com1_int#
  298.                 mov ds, comsave1 2+
  299.                 int $21
  300.                 pop ds
  301. \F              next
  302. \T              RET
  303.                 end-code
  304.  
  305. code rest_com2: ( --- )                 \ restores COM2: interrupt vector
  306.                 push ds
  307.                 mov dx, comsave2
  308.                 mov ax, # $2500
  309.                 add ax, com2_int#
  310.                 mov ds, comsave2 2+
  311.                 int $21
  312.                 pop ds
  313. \F              next
  314. \T              RET
  315.                 end-code
  316.  
  317. : ?rest_com1:   ( --- )
  318.                 comsave1 2@ or           \ don't restore vector thats not set
  319.                 if      rs232_intoff
  320.                         rest_com1:
  321.                         0.0 comsave1 2!
  322.                 then
  323. \F              defers byefunc          \ the restore is high priority
  324.                 ;
  325.  
  326. \F ' ?rest_com1: is byefunc
  327.  
  328. : ?rest_com2:   ( --- )
  329.                 comsave2 2@ or           \ don't restore vector thats not set
  330.                 if      rs232_intoff
  331.                         rest_com2:
  332.                         0.0 comsave2 2!
  333.                 then
  334. \F              defers byefunc          \ the restore is high priority
  335.                 ;
  336.  
  337. \F ' ?rest_com2: is byefunc
  338.  
  339. code set_com1:  ( --- )                 \ Set the COM1: interrupt vector
  340.                 push ds
  341.                 push es
  342.                 mov dx, # com_int
  343.                 mov ax, # $2500
  344.                 add ax, com1_int#       \ COM1: interrupt vector = 04
  345.                 mov cx, cs
  346.                 mov ds, cx
  347.                 int $21
  348.                 pop es
  349.                 pop ds
  350. \F              next
  351. \T              RET
  352.                 end-code
  353.  
  354. code set_com2:  ( --- )                 \ Set the COM2: interrupt vector
  355.                 push ds
  356.                 push es
  357.                 mov dx, # com_int
  358.                 mov ax, # $2500
  359.                 add ax, com2_int#       \ COM2: interrupt vector = 03
  360.                 mov cx, cs
  361.                 mov ds, cx
  362.                 int $21
  363.                 pop es
  364.                 pop ds
  365. \F              next
  366. \T              RET
  367.                 end-code
  368.  
  369. code save_com1: ( --- )                 \ Save the COM1: interrupt vector IRQ4
  370. \T              PUSH BX
  371.                 push es
  372.                 mov ax, # $3500
  373.                 add ax, com1_int#       \ get the interrupt vector for com1:
  374.                 int $21
  375.                 mov comsave1 bx
  376.                 mov comsave1 2+ es       \ save old vector
  377.                 pop es
  378. \F              next
  379. \T              POP BX
  380. \T              RET
  381.                 end-code
  382.  
  383. code save_com2: ( --- )                 \ Save the COM2: interrupt vector IRQ3
  384. \T              PUSH BX
  385.                 push es
  386.                 mov ax, # $3500
  387.                 add ax, com2_int#       \ get the interrupt vector for com2:
  388.                 int $21
  389.                 mov comsave2 bx
  390.                 mov comsave2 2+ es       \ save old vector
  391.                 pop es
  392. \F              next
  393. \T              POP BX
  394. \T              RET
  395.                 end-code
  396.  
  397. : com-cnt       ( -- count )  \  get how many bytes are in the buffer
  398.                 bufin c@  bufout c@ -  1-  $ff and  ;
  399.  
  400.  
  401. : com-get       ( -- char )   \  get a character from the buffer
  402.                 com-cnt  0<>                    \ is buffer empty?
  403.                                                 \ if not...
  404.                 if      bufout c@ 1+ 255 and
  405.                         combuf +   c@           \ get the byte
  406.                         1 bufout +!             \ and increment the pointer
  407.                 else    0                       \ return junk (0) if empty
  408.                 then  ;
  409.  
  410.  
  411. : clr-buf       ( -- )   \ empty the buffer
  412.                 1 bufin !   0 bufout !  combuf bufsize erase ;
  413.  
  414. : .buf          ( --- )     \ display the buffer contents
  415.                 base @ hex
  416.                 cr ."  next byte " bufout c@ 1+ $ff and combuf + u.
  417.                 5 spaces ." last byte " bufin c@ 1- $ff and combuf + u.
  418.                 base !     5 spaces
  419.                 com-cnt dup 0= if ." empty" else dup . ." bytes,  " then
  420.                     bufsize 1- = if ." full" then
  421.                 combuf bufsize dump ;
  422.  
  423.  
  424. 0 constant nopar   8 constant oddpar  $18 constant evenpar \ parity control
  425. 0 constant 1stop   4 constant 2stop                        \ stop bit control
  426. 2 constant 7bit    3 constant 8bit                         \ length control
  427.  
  428. nopar 1stop 8bit + + value parity&bits \ default no parity, 8 bit, 2 stops
  429.  
  430. : com2:         ( --- )   \ initialize the communications port stuff
  431.                 2 =: commport
  432.                 $08 imask !
  433.                 $63 comEOI !
  434.                 cr ." Comm Port #2 selected" cr
  435.                 clr-buf
  436.                 comsave2 2@ d0=
  437.                 if      save_com2:      \ save the current vector into comsave
  438.                         set_com2:       \ set our com1 interrupt vector
  439.                         parity&bits rate 1 com-init      \ init the port 2
  440.                 then    ;
  441.  
  442. : com1:         ( -- )
  443.                 1 =: commport
  444.                 $10 imask !
  445.                 $64 comEOI !
  446.                 cr ." Comm Port #1 selected" cr
  447.                 clr-buf
  448.                 comsave1 2@ d0=
  449.                 if      save_com1:
  450.                         set_com1:       \ set our com1 interrupt vector
  451.                         parity&bits rate 0 com-init     \ init the port 1
  452.                 then    ;
  453.  
  454. : terminal      ( -- )
  455.                 DECIMAL                         \ always select decimal
  456. \T              MARGIN_INIT
  457. \T              DOSIO_INIT                      \ init EMIT, TYPE & SPACES
  458.                 9600 baud                       \ initialize the ports speed
  459.                 COM1:
  460.                 begin   key?
  461.                         if      key dup $1B =
  462.                                 if      drop
  463.                                         ?rest_com1:
  464.                                         cr true abort" Quitting"
  465.                                 then    com-out
  466.                         then
  467.                         com-cnt
  468.                         if      com-get emit
  469.                         then
  470.                 again   ;
  471.  
  472. \T \S           STOP HERE IF TARGET COMPILING
  473.  
  474. : ser_typel     ( seg a1 n1 -- )
  475.                 dup>r
  476.                 bounds
  477.                 ?do     dup i c@L com-out
  478.                 loop    drop r> #out +! ;
  479.  
  480. : ser_out       ( c1 -- )
  481.                 com-out #out incr ;
  482.  
  483. : ser_key       ( -- c1 )
  484.                 begin   com-cnt   until
  485.                 com-get ;
  486.  
  487. : toserial      ( -- )  \ re-direct Forth I/O to the serial port COM1:
  488.                 slow
  489.                 ['] (expect) is expect
  490.                 statoff
  491.                 9600 baud
  492.                 com1:
  493.                 ['] ser_typel is typel
  494.                 ['] ser_out   is emit
  495.                 ['] com-cnt   is key?
  496.                 ['] ser_key   is key ;
  497.  
  498. }
  499.  
  500.