10 Serial Devices





Serial devices are byte-oriented, sequentially-accessed devices such as asynchronous communication lines (often attached to a "dumb" terminal).

Required Methods

The serial device driver must declare the serial device-type, and must implement the methods open and close, as well as the following:

install-abort
( -- )

Instruct the driver to begin periodic polling for a keyboard abort sequence. install-abort is executed when the device is selected as the console input device.

read
( adr len -- actual )

Read len bytes of data from the device into memory starting at adr. Return the number of bytes actually read, actual, or -2 if no bytes are currently available from the device. -1 is returned if other errors occur.

remove-abort
( -- )

Instruct the driver to cease periodic polling for a keyboard abort sequence. remove-abort is executed when the console input device is changed from this device to another.

write
( adr len -- actual )

Write len bytes of data to the device from memory starting at adr. Return the number of bytes actually written, actual.

Required Properties

The standard properties of a serial driver are:

    Table 10-1 Serial Driver Required Properties

-----------------------------------
Property Name Value -----------------------------------
               
name           " SUNW,thingy"
               
reg            { device-dependent}
               
device_type    " serial"

-----------------------------------

Device Driver Examples

The three examples that follow are serial device drivers for the Zilog 8530 SCC (UART) chip.

Simple Serial FCode Program

--------------------------------------------------
fcode-version1 hex " SUNW,zs" name my-address 10.0000 + my-space 8 reg 7 xdrint " interrupts" attribute 7 0 intr end0 --------------------------------------------------

Extended Serial FCode Program

-------------------------------------------------------------------------------
\ Extended Serial FCode Program
\ In addition to publishing the properties, this sample driver 
\ provides methods to access and control the serial ports.
\  
\ The following main methods are provided:
\ - usea   ( -- )
\    Selects serial port A. All subsequent operations will
\         be directed to port A
\ - useb   ( -- )
\         Selects serial port B. All subsequent operations will
\         be directed to port B
\ - uemit  ( char -- )
\ Emits a given character to the selected serial port.
\ - ukey   ( -- key )         
\ Retrieves a character from the selected serial port.
\ - read   ( adr len -- #read )
\  Reads "len" number of characters from the selected port,
\    and store them at "adr".
\ - write  ( adr len -- #written )
\  Writes "len" number of characters from the buffer located 
\      at "adr" to the selected serial port.
fcode-version2
hex
   my-address 10.0000 + constant phys-addr
   my-space   constant  my-sbus-space
   my-address constant  my-sbus-address
   " SUNW,zs"                name
   phys-addr my-sbus-space 8 reg
   7 xdrint " interrupts"    attribute
   7 0                  intr
   : phys-adr  ( offset -- adr space )  
     my-sbus-address +  my-sbus-space  
   ;
   : do-map-in  ( offset size -- va )  
     r phys-adr r  " map-in" $call-parent  
   ;
   : do-map-out ( va size -- )  " map-out" $call-parent  ;
   : rc! c! ;
   : rc@ c@ ;
   : /string  ( adr len n -- adr+n len-n )  tuck  -  -rot  +  swap  ;
   1 constant RXREADY  \ received character available
   4 constant TXREADY  \ transmit buffer empty
  : instance  ( -- )   \ verify that "instance" is defined
    ['] instance ['] ferror <  if
        instance
    then
  ;
   0 instance value  uart              \ define uart as an "per-instance" value.
   0 instance value  uartbase
   h# ff instance value  mask-#data    \ mask for #data bits 
   h# 10 instance buffer: mode-buf
   \ The following line assumes that A2 selects the channel within the chip
   : usea   ( -- )    uartbase 4 + is uart  ;
   : useb   ( -- )    uartbase is uart  ;
   : uctl!  ( c -- )  uart  rc!  ;
   : uctl@  ( -- c )  uart  rc@  ;
   \ The following line assumes that A1 chooses the command vs. data port
   : udata!  ( c -- )  uart  2 + rc!  ;
   : udata@  ( -- c )  uart  2 + rc@  ;
   \ Test for "break" character received.
   : ubreak?  ( -- flag )  10 uctl!  uctl@  h# 80 and  0<  ;
   \ Clear the break flag
   : clear-break  ( -- )
      begin  ubreak? 0=  until    \ Let break finish
      udata@ drop                 \ Eat the null character
      30 uctl!                    \ Reset errors
   ;
   : uemit? ( -- flag )  uctl@ TXREADY and  ;
   : uemit  ( char -- )  begin  uemit?  until  udata!  ;
   : ukey? ( -- flag )  uctl@ RXREADY and  ;
   : ukey  ( -- key )   begin  ukey?  until  udata@  ;
   : uwrite              ( adr len -- #written )
      tuck  bounds ?do   ( len )
      i c@  uemit        ( len )
      loop               ( len )
   ;
   : uread  ( adr len -- #read )              \ -2 for none available right now
      ukey? 0=  if  2drop -2  exit  then      ( adr len )
      tuck                                    ( len adr len )
      begin  dup 0<   ukey? 0<  and  while  ( len adr len )
        over  ukey mask-#data and swap c!     ( len adr len )
        1 /string                             ( len adr' len' )
      repeat                                  ( len adr' len' )
      nip -                                   ( #read )
   ;
external
   : read   ( adr len -- #read )     uread   ;
   : write  ( adr len -- #written )  uwrite  ;
end0

-------------------------------------------------------------------------------

Complete Serial FCode Program

------------------------------------------------------------------------------
\ Complete Serial driver.
\ In addition to the methods defined in the above driver sample, 
\ this version defines more methods to initialize, test, and access 
\ the serial ports.
\ The new main methods are:
\ - inituarts      ( -- )
\    Initializes both serial ports A and B.
\ - open           ( -- okay? )
\    Maps in the uart chip.  Selects port A on default, then check
\    my-args, if port B was specified, then selects port B instead.
\ - close          ( -- )
\    Unmap the uart chip.
\ - selftest       ( -- )
\    Performs selftest on both Port A and B.
\ - install-abort  ( -- )
\    Sets up alarm to do poll-tty every 10 miliseconds.
\ - remove-abort   ( -- )
\    Removes the poll-tty alarm.
fcode-version2
hex
   my-address 10.0000 +      constant  phys-adr
   my-space                  constant  my-sbus-space
   my-address                constant  my-sbus-address
   " SUNW,zs"                name
   phys-addr my-sbus-space 8 reg
   7 xdrint " interrupts" attribute
   7 0                  intr
   " serial"            device-type
   : phys-adr  ( offset -- adr space )
        my-sbus-address +  my-sbus-space
   ;
   : do-map-in  ( offset size -- va )
        r phys-adr r  " map-in" $call-parent
   ;
   : do-map-out ( va size -- )  " map-out" $call-parent  ;
   : rc! c! ;
   : rc@ c@ ;
   : /string  ( adr len n -- adr+n len-n )  tuck  -  -rot  +  swap  ;
  : instance  ( -- )   \ verify that "instance" is defined
    ['] instance ['] ferror <  if
        instance
    then
  ;
   fload inituarts.fth
   fload ttydriver.fth
end0
\----------------------------------------------------------------------------
\ inituarts.fth 
hex
headerless
create uart-init-table
\ 9 c, c0 c,    \ Master reset channel a (80), channel b (40)
 9 c,  2 c,     \ Don't respond to intack cycles (02)
 
 4 c, 44 c,     \ No parity (00), 1 stop bit (04), x16 clock (40)
 3 c, c0 c,     \ receive 8 bit characters (c0)
 5 c, 60 c,     \ transmit 8 bits (60)
 e c, 82 c,     \ Processor clock is baud rate source (02)
 
 b c, 55 c,     \ TRxC = xmit clk (01), enable TRxC (04), Tx clk is baud (10),
                \ Rx clk is baud (40)
 c c,  e c,     \ Time constant low
 d c,  0 c,     \ Time constant high
 
 3 c, c1 c,     \ receive 8 bit characters (c0), enable (01)
 5 c, 68 c,     \ transmit 8 bits (60), enable (08)
 e c, 83 c,     \ Processor clock is baud rate source (02), Tx enable (01)
 
 0 c, 10 c,     \ Reset status bit latches
 
ff c, ff c,     \ Mark end of data
\----------------------------------------------------------------------------
\ ttydriver.fth - Driver for Zilog 8530 SCC (UART) chips.
hex
0 instance value uartbase
create default-mode
\   0      1      2      3      4      5      6      7
   00 c,  00 c,  00 c,  c1 c,  44 c,  68 c,  00 c,  00 c,
\   8      9      a      b      c      d      e      f
   00 c,  02 c,  00 c,  55 c,  0e c,  00 c,  83 c,  00 c,
       0 instance value  uart        \ define uart as an "per-instance" value.
   h# ff instance value  mask-#data  \ mask for #data bits
   h# 10 instance buffer: mode-buf
   create masks   1f c,  7f c,  3f c,  ff c,
   \ The following line assumes that A2 selects the channel within the chip
   : usea   ( -- )    uartbase 4 + is uart  ;
   : useb   ( -- )    uartbase is uart  ;
   : uctl!  ( c -- )  uart  rc!  ;
   : uctl@  ( -- c )  uart  rc@  ;
   \ The following line assumes that A1 chooses the command vs. data port
   : udata!  ( c -- )  uart  2 + rc!  ;
   : udata@  ( -- c )  uart  2 + rc@  ;
   \ Write all the initialization sequence to both uarts
   : inituart  ( -- )
      uart-init-table
      begin   dup c@ ff <  while
        dup c@ uctl!  dup ca1+ c@ uctl!
        /c 2* +
      repeat
      drop
   ;
   : inituarts  ( -- )   usea inituart   useb inituart  usea  ;
   \ Test for "break" character received.
   : ubreak?  ( -- break? )  10 uctl!  uctl@  h# 80 and  0<  ;
   \ Clear the break flag
   : clear-break  ( -- )
      begin  ubreak? 0=  until  \ Let break finish
      udata@ drop               \ Eat the null character
      30 uctl!                  \ Reset errors
   ;
   1 constant RXREADY           \ received character available
   4 constant TXREADY           \ transmit buffer empty
   : uemit? ( -- emit? ) uctl@ TXREADY and  ;
   : uemit ( char -- )  begin  uemit?  until  udata!  ;
   : ukey? ( -- key? )  uctl@ RXREADY and  ;
   : ukey  ( -- key )  begin  ukey?  until  udata@  ;
   : uwrite  ( adr len -- #written )
      tuck  bounds ?do   ( len )
        i c@  uemit      ( len )
      loop               ( len )
   ;
   : uread  ( adr len -- #read )              \ -2 for none available right now
      ukey? 0=  if  2drop -2  exit  then      ( adr len )
      tuck                                    ( len adr len )
      begin  dup 0<   ukey? 0<  and  while  ( len adr len )
        over  ukey mask-#data and swap c!     ( len adr len )
        1 /string                             ( len adr' len' )
      repeat                                  ( len adr' len' )
      nip -                                   ( #read )
   ;
   : poll-tty  ( -- )
      ttylock @ if  exit  then
      ubreak?  if  clear-break  user-abort  then
   ;
external
   : open  ( -- okay? )
      phys-adr 8 do-map-in is uartbase
      usea
      my-args                               ( arg-str )
      ascii ,  left-parse-string  if        ( rem adr )
        c@  ascii b  =  if                  ( rem )
          2drop                             ( )
          useb                              ( )
        then                                ( rem )
        else                                ( rem adr )
          drop 2drop                        ( )
      then                                  ( )
      true
   ;
   : close  ( -- )  uartbase 8 do-map-out  ;
headers
   : utest  ( -- 0 )  h# 7f  bl  ?do  i uemit  loop 0  ;
external
   : selftest  ( -- error? )
      open  0=  if  ." Can't open device" true exit  then
      my-args  if       ( adr )
        c@  case
          ascii a  of usea  endof
          ascii b  of useb  endof
          ( default ) ." Bad zs port letter" drop false exit
        endcase
      else  \ No port letter so test both ports.
        drop
        usea utest
        useb utest
        or close exit        ( fail? )
      then
      utest                  ( fail? )
      close
   ;
   : read   ( adr len -- #read )     uread   ;
   : write  ( adr len -- #written )  uwrite  ;
   : install-abort  ( -- )  ['] poll-tty d# 10 alarm  ;
   : remove-abort   ( -- )  ['] poll-tty 0 alarm  ;
   \ "seek" might be implemented to select a load file name
   \ Implement "load" ( optional )
headers

------------------------------------------------------------------------------