Serial devices are byte-oriented, sequentially-accessed devices such as asynchronous communication lines (often attached to a "dumb" terminal).
The serial device driver must declare the serial device-type, and must implement the methods open and close, as well as the following:
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 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.
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 len bytes of data to the device from memory starting at adr. Return the number of bytes actually written, actual.
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" -----------------------------------
The three examples that follow are serial device drivers for the Zilog 8530 SCC (UART) chip.
--------------------------------------------------
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
\ 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 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 ------------------------------------------------------------------------------