ehs@jumbo.dec.com (Ed Satterthwaite) (09/28/87)
Hardly what the 8-bit Atari world needs most right now, but here's why I often use this one when I just want a Unix terminal (no file transfers): - It uses the character set I find most legible on my particular monitor (Commodore 1702, composite color). This is a very subjective call, and I make no claims at all about results on a good monochrome monitor. - It has relatively fast implementations of most escape sequences. These provide the terminal capabilities needed to make the use of a screen-oriented editor (Emacs) bearable. - The program is small and simple; it loads and initializes quickly. - Source (in ACTION!) is available for customization and adaptation. I'm posting VT52B in case anyone else has similar requirements. It is an upgrade of the ACTION! program VT52A posted by Michael Jenkin about a year ago. Michael's font looks bolder and more blocky than the ones used by the other 80 column programs that I've tried. In addition, the proportions of some characters are exaggerated a bit to make the corresponding shapes more distinctive. This is a good trade-off for my eyes and monitor; your mileage may differ. I have modified VT52A by reworking the display management, adding a few new escape sequences, and generally trying to streamline what seemed to be the critical loops. My thanks to Michael for permission to use his character set as well as the general framework of his program. I am posting the sources for three reasons. First, I don't have a copy of the ACTION! runtime library to link with the object code. Second, this is an edit-and-compile project; there are no runtime menus, and some customization of the source will probably be necessary. Finally, 80-column screen management is done by an "A:" handler that is comparable to the standard "S:" handler. It might be of some interest for other applications, even if you don't care about a terminal emulator. Most of the revision of VT52A was done as a Sunday afternoon hack, with some further additions and changes suggested by experience with the program. It now does what I need, but there is plenty of room for further development. All my testing has been done with an Atari 800, an Atari 850 interface, and Hayes-incompatible modems at 1200 and 2400 baud. The mainframes have been VAXen running various versions of Ultrix (essentially similar to BSD 4.2 and 4.3 Unix) with Unipress (Gosling) Emacs as the primary editor. I've had considerable trouble getting other terminal emulators to work in this environment. If you have similar problems getting VT52B to work for you, I'd be interested in hearing about it. Some documentation and a suggested Unix termcap entry appear in the next message. The message following that contains the ACTION! source code for VT52B.ACT. Ed Satterthwaite DEC Systems Research Center, Palo Alto, CA Arpa: ehs@src.DEC.COM Uucp: {...}!decwrl!ehs
ehs@jumbo.dec.com (Ed Satterthwaite) (09/28/87)
------------------------------------------------------------------------------
VT52B Documentation
Overview
--------
VT52B consists of two modules. One is a fairly general handler for an
"A:" device that manages a 24 x 80 screen. It provides a
character-oriented output device that recognizes embedded escape
sequences. These escape sequences perform various display control
functions. They support a subset of the VT52 terminal capabilities plus a
few extensions needed for incremental display updates. The other module
is a very simple (perhaps too simple) terminal protocol manager that
attempts to coordinate the K:, R: and A: devices to emulate an
extended VT52 terminal.
The A: handler uses techniques of display list management that are by now
fairly standard and well-known. I hope that most of the code will be
fairly scrutable to an experienced ACTION! programmer. I will not attempt
to explain it here but I will try to answer any specific questions that
arise.
The A: Handler
--------------
The following VT52 escape sequences are supported:
esc-A moves cursor up one line
esc-B moves cursor down one line
esc-C moves cursor right one column
esc-D moves cursor left one column
esc-H homes cursor
esc-I reverse line feed
esc-J clears from cursor to end of screen
esc-K clears from cursor to end of line
esc-Y positions cursor (see VT52 specs for X,Y encoding)
The following extensions are also supported:
esc-F enters 'stand-out' (inverse video) mode
esc-G exits 'stand-out' mode
esc-L inserts blank character space at the cursor
esc-M deletes character at the cursor
esc-N inserts blank line at the cursor
esc-O deletes line containing the cursor
A suitable UNIX termcap entry appears at the end of this file. The
extension codes have been chosen to be compatible with the "VT-52XL"
terminal supported by Chameleon, and the same termcap should be suitable
for both.
The support for 'stand-out' mode has not been extensively tested, but it
is good enough to do Emacs mode lines and the like. Its use is a mixed
blessing, however, since the inverted video can be much less readable. To
disable it, remove the next-to-last line from the suggested termcap.
The code could easily be modified to support 'insert' and 'delete' modes
directly, and the corresponding incremental updates would be considerably
faster. I have not done this because such modality can be disastrous over
a noisy or unreliable line.
When the A: device is opened, it allocates space for its display bit maps
and custom display lists from the top of available memory as recorded in
MEMTOP (location $2E5) and updates MEMTOP. These data structures require
approximately 8200 bytes. If sufficient space is not available, the open
fails and MEMTOP is not changed.
Terminal Emulation
------------------
ASCII characters not on the Atari keyboard can be entered as follows:
{ ctrl-< or shift-<
} ctrl-> or shift->
~ ctrl-backS or shift-backS
` ctrl-.
The emulator does not provide flow control; buffer overruns drop
characters. In my experience, this is never a problem at 1200 baud and is
not a problem at 2400 for screen-at-a-time output. The program cannot
keep up with continuous output at 2400 baud.
The ASCII bell character (^G) produces no visible or audible output.
Installation
------------
You will need an ACTION! cartridge to compile and run this program. You
might also have to edit the source to select the options you require,
since there is no provision for changing these at runtime. The
configuration of the 850 interface (or similar R: interface) is controlled
by the following variables, which are declared and initialized at the
beginning of the main program module:
speed, wsize, sbits, lf, iparity, oparity
See the 850 manual for tables of possible values and their meanings. Note
that the value of 'speed' is 7 less than the tabulated value, e.g.,
speed baud rate
1 300
2 600
3 1200
5 2400
Alternatively, you can modify the calls of XIO_R in the procedure init_R;
these variables are not used elsewhere. As it stands, VT52B supports a
1200 baud modem with 8-bit words, 2 stop bits and no parity.
Many monitors will actually display 25 or 26 lines of text. You can
take advantage of this by changing the following definitions at the
beginning of the module for the A: handler:
NL number of screen lines (normally 24)
NB number of skip lines in top border (normally 3)
LL *must* equal NL-1
DLSize *must* equal NB + 10*NL + 3
I have had good results with NL=25 and NB=2. If you change NL, be sure to
change your termcap entry to agree.
I experimented with several background colors for the display. I
eventually settled on a pale blue that seemed to be the best compromise
between contrast and flicker on my particular monitor. See the procedure
init_A if you want to change this.
The emulator module contains a procedure load_R. This is a machine-code
insert essentially similar to the code in the autorun file that downloads
the R: handlers from the 850. I added it when I was having some trouble
with 850 (re)initialization. I left it in because it makes the object
file somewhat smaller and easier to build. If you have a different
interface unit or prefer a different handler, delete init_R and prepend
your handler to the object file in the usual way.
You must compile this program with lower case enabled. You must also
compile it with space for the R: handlers reserved in low memory. The
simplest way to do this is to load the handler you intend to use (by
executing the AUTORUN.SYS file, for example) before compiling VT52B.ACT.
You can then execute the compiled program directly or save it for later
execution using the DOS "L" command or equivalent. If you deleted init_R,
you must prepend an appropriate R: handler. Alternatively, you can set
the code origin to reserve space for your handler as described on page 144
of the ACTION! manual. You are in terminal emulation mode as soon as
execution begins.
Note: At least some versions of the 850 will not download the R: handlers
a second time until the power has been cycled on either the console
computer or the 850. Thus if you load the handlers (e.g., with the
standard AUTORUN.SYS file), do something that destroys them (e.g., going
to DOS without a MEM.SAV file) and then attempt to reload them, you will
be left without warning with bad handlers.
Suggested Termcap Entry
-----------------------
Here's one that has worked for me:
dw|vt52|decvt52:\
:cr=^M:do=^J:nl=^J:bl=^G:le=^H:bs:cd=\EJ:ce=\EK:\
:cl=\EH\EJ:cm=\EY%+ %+ :co#80:li#24:nd=\EC:ta=^I:pt:sr=\EI:\
:up=\EA:ku=\EA:kd=\EB:kr=\EC:kl=\ED:kb=^H:
a1|atari|atari vt52 emulator:\
:am:pt:tc=vt52:
a2|atari+|vt52xl|atari vt52+ emulator:\
:al=\EN:dl=\EO:im=:ei=:ic=\EL:dm=:ed=:dc=\EM:\
:so=\EF:se=\EG:\
:tc=atari:
Note that it can take some extra work to get your favorite program to
notice changes in the terminal type and capabilities. If the program is
able to use the escape sequences, you should notice a definite difference
between vt52 and atari+ termcap entries. Here's a script from our system
administrator that works for Emacs:
#! /bin/csh -f
#
# vt52b-mode - setup terminal for vt52b mode.
#
stty dec new cr0
set noglob
setenv TERMCAP atari.termcap
setenv TERM atari+
eval `tset -s -I -Q`
set term = atari+
unset noglob
Replace "atari.termcap" with the name of the file containing a termcap
entry similar to the one above.
-----------------------------------------------------------------------------
Ed Satterthwaite
Arpa: ehs@src.DEC.COM
Uucp: {...}!decwrl!ehs
ehs@jumbo.UUCP (09/28/87)
--VT52B.ACT--cut here------------------------- ;********************************* ;* * ;* VT52B.ACT - a VT52+ emulator * ;* written in ACTION(tm) by * ;* * ;* Ed Satterthwaite * ;* ehs@src.DEC.COM * ;* ...!decwrl!ehs * ;* Copyright 1987 * ;* * ;* derived with permission from * ;* * ;********************************* ;* * ;* VT52A.ACT - a VT52+ emulator * ;* written in ACTION(tm) by * ;* * ;* Michael R. M. Jenkin * ;* University of Toronto * ;* ...!utcsri!utai!jenkin * ;* copyright(c) 1985 * ;* * ;********************************* ;* * ;* This program may be copied * ;* and redistributed without * ;* charge for noncommercial use. * ;* All commercial rights * ;* reserved. * ;* * ;********************************* MODULE ;A: handler, by Michael Jenkin ; revised, by Ed Satterthwaite DEFINE NB = "3", ;# top blank lines NL = "24", ;# lines on screen LL = "23", ;indexed 0..LL DLSize = "246" ;NB + 10*NL + 3 DEFINE LDY = "$A0", RTS = "$60", JMP = "$4C" CARD ARRAY ProgEnd(1) ;compiler allocates this last BYTE lmargin = $52, rmargin = $53, rowcrs = $54, oldrow = $5A, colcrs = $55, oldchr = $5D, inesc, need, needx, inv CARD appmhi = $0E, oldcol = $5B, sdlst = $230, memtop = $2E5 BYTE ARRAY InvMask = [$00 $0F] BYTE ARRAY chset = [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6 6 6 6 0 6 0 0 10 10 10 0 0 0 0 10 14 10 14 10 0 0 0 4 14 8 14 2 14 4 0 0 10 2 6 12 8 10 0 0 14 2 6 6 2 14 0 0 6 6 6 0 0 0 0 0 6 12 8 8 12 6 0 0 12 6 2 2 6 12 0 0 10 4 14 4 10 0 0 0 4 4 14 4 4 0 0 0 0 0 0 0 6 6 12 0 0 0 14 0 0 0 0 0 0 0 0 0 6 6 0 0 2 2 4 4 8 8 0 0 14 10 10 10 10 14 0 0 4 12 4 4 4 14 0 0 14 2 2 14 8 14 0 0 14 2 14 2 2 14 0 0 10 10 10 14 2 2 0 0 14 8 14 2 2 14 0 0 14 8 14 10 10 14 0 0 14 2 6 4 4 4 0 0 14 10 14 10 10 14 0 0 14 10 14 2 2 2 0 0 0 6 6 0 6 6 0 0 0 6 6 0 6 6 12 0 2 6 12 12 6 2 0 0 0 14 0 0 14 0 0 0 8 12 6 6 12 8 0 0 4 10 2 4 0 4 0 14 10 10 14 8 8 14 0 0 4 14 10 10 14 10 0 0 12 10 12 10 10 12 0 0 14 10 8 8 10 14 0 0 12 10 10 10 10 12 0 0 14 8 12 8 8 14 0 0 14 8 12 8 8 8 0 0 14 8 8 10 10 14 0 0 10 10 14 10 10 10 0 0 14 4 4 4 4 14 0 0 2 2 2 2 10 14 0 0 10 10 12 12 10 10 0 0 8 8 8 8 8 14 0 0 10 14 14 10 10 10 0 0 12 10 10 10 10 10 0 0 14 10 10 10 10 14 0 0 14 10 14 8 8 8 0 0 14 10 10 10 10 14 2 0 14 10 14 12 10 10 0 0 14 8 14 2 2 14 0 0 14 4 4 4 4 4 0 0 10 10 10 10 10 14 0 0 10 10 10 10 10 4 0 0 10 10 10 14 14 10 0 0 10 10 4 4 10 10 0 0 10 10 4 4 4 4 0 0 14 2 4 4 8 14 0 0 14 8 8 8 8 14 0 0 8 8 4 4 2 2 0 0 14 2 2 2 2 14 0 0 4 4 10 0 0 0 0 0 0 0 0 0 0 15 0 0 4 6 2 0 0 0 0 0 0 14 2 14 10 14 0 0 8 8 14 10 10 14 0 0 0 0 14 8 8 14 0 0 2 2 14 10 10 14 0 0 0 14 10 14 8 14 0 0 0 14 8 12 8 8 0 0 0 14 10 10 14 2 14 0 8 8 14 10 10 10 0 0 6 0 6 6 6 6 0 0 6 0 6 6 6 6 12 0 8 8 10 14 10 10 0 0 12 4 4 4 4 14 0 0 0 10 14 14 10 10 0 0 0 12 10 10 10 10 0 0 0 14 10 10 10 14 0 0 0 14 10 10 14 8 8 0 0 14 10 10 14 2 2 0 0 14 10 8 8 8 0 0 0 14 8 14 2 14 0 0 4 14 4 4 4 4 0 0 0 10 10 10 10 14 0 0 0 10 10 10 10 4 0 0 0 10 10 14 14 10 0 0 0 10 14 4 14 10 0 0 0 10 10 10 14 2 14 0 0 14 2 4 8 14 0 2 4 4 8 4 4 2 0 6 6 6 0 0 6 6 6 8 4 4 2 4 4 8 0 0 10 5 0 0 0 0 0 0 0 0 0 0 0 0 0 ] CARD ARRAY DBase(NL), ;display line bases DLBase(2), ;display list bases LMaps(2) ;line map bases BYTE ARRAY ;storage for line maps LMapA(NL), LMapB(NL) BYTE ARRAY LMap ;logical line -> DBase index BYTE dl ;current display list and line map selection CARD FUNC DAlloc(CARD size, mask) CARD base base = memtop - size IF (base & mask) # ((memtop-1) & mask) THEN base = (memtop & mask) - size FI memtop = base RETURN (base) PROC InitBitMap() ;build GR.8 line chunks BYTE i CARD base FOR i = 0 TO LL DO base = DAlloc(320, $F000) DBase(i) = base IF base >= appmhi THEN Zero(base, 320) FI OD RETURN PROC InitDL() ;build display lists BYTE i, j CARD b, base BYTE ARRAY d LMaps(0) = LMapA LMaps(1) = LMapB LMap = LMaps(0) b = DAlloc(DLSize, $FC00) d = b IF b >= appmhi THEN FOR i = 1 TO NB DO d(0) = $70 ; blank skip d ==+ 1 OD FOR i = 0 TO LL DO base = DBase(i) d(0) = $4F ; mode line + LMS, Gr. 8 d(1) = base & $FF d(2) = base RSH 8 FOR j = 3 TO 9 DO d(j) = $0F ; mode line, Gr. 8 OD LMap(i) = i d ==+ 10 OD d(0) = $41 d(1) = b & $FF d(2) = b RSH 8 FI DLBase(0) = b b = DAlloc(DLSize, $FC00) d = b IF b >= appmhi THEN MoveBlock(b, DLBase(0), DLSize) d(DLSize-2) = b & $FF d(DLSize-1) = b RSH 8 FI DLBase(1) = b dl = 0 sdlst = DLBase(0) RETURN PROC UpdateDL(BYTE del, ins) ; move line from row del to row ins ; and blank it BYTE i, j CARD old, new BYTE ARRAY oldLMap CARD POINTER s, d BYTE m CARD p old = DLBase(dl) oldLMap = LMap dl ==! 1 new = DLBase(dl) LMap = LMaps(dl) s = old + NB + 1 ;skip blank lines d = new + NB + 1 ;and first mode byte m = oldLMap(del) j = 0 FOR i = 0 TO LL DO IF j = del THEN s ==+ 10 j ==+ 1 FI IF i = ins THEN p = DBase(m) d^ = p LMap(i) = m ELSE d^ = s^ ;copy addr for LMS LMap(i) = oldLMap(j) s ==+ 10 j ==+ 1 FI d ==+ 10 OD Zero(p, 320) sdlst = new ;next VBI install dl RETURN PROC Achr(BYTE cx, cy, cc) BYTE POINTER sb, db BYTE i, mask sb = cc & $7F sb = (sb LSH 3) + chset db = (cx RSH 1) + DBase(LMap(cy)) mask = InvMask(inv) IF cx & 1 THEN FOR i = 0 TO 7 DO db^ = ((db^ & $F0) % sb^)!mask sb ==+ 1 db ==+ 40 OD ELSE mask ==LSH 4 FOR i = 0 TO 7 DO db^ = ((db^ & $0F) % (sb^ LSH 4))!mask sb ==+ 1 db ==+ 40 OD FI RETURN PROC Acurse(BYTE cx, cy); invert char BYTE POINTER db BYTE i, mask IF (cx & 1) THEN mask = $0F ELSE mask = $F0 FI db = (cx RSH 1) + DBase(LMap(cy)) FOR i = 0 TO 7 DO db^ ==! mask db ==+ 40 OD RETURN PROC Ains(BYTE cx, cy) CARD lb BYTE POINTER db BYTE i, bx, b BYTE cout, t bx = cx RSH 1 lb = DBase(LMap(cy)) FOR i = 0 TO 7 DO b = bx db = lb + b IF cx & 1 THEN cout = db^ LSH 4 db^ ==& $F0 db ==+ 1 b ==+ 1 ELSE cout = 0 FI WHILE b < 40 DO t = db^ db^ = (t RSH 4) % cout cout = t LSH 4 db ==+ 1 b ==+ 1 OD lb ==+ 40 OD RETURN PROC Adel(BYTE cx, cy) CARD lb BYTE POINTER db BYTE i, bx, b BYTE cout, t bx = cx RSH 1 lb = DBase(LMap(cy)) FOR i = 0 TO 7 DO b = bx db = lb + 39 cout = 0 IF cx & 1 THEN b ==+ 1 FI WHILE b < 40 DO t = db^ db^ = (t LSH 4) % cout cout = t RSH 4 db ==- 1 b ==+ 1 OD IF cx & 1 THEN db^ = (db^ & $F0) % cout FI lb ==+ 40 OD RETURN PROC Ascroll() ; update cursor IF colcrs > rmargin THEN colcrs = lmargin rowcrs ==+ 1 FI IF rowcrs > LL THEN UpdateDL(0, LL) rowcrs = LL FI Acurse(colcrs,rowcrs) RETURN PROC Aesc(BYTE char) ; escape sequence BYTE ch BYTE i IF need = 2 THEN ; 1st ESC Y needx = char - $20 need ==- 1 ELSEIF need = 1 THEN ; 2nd ESC Y char ==- $20 IF (needx <= LL) AND (char <= rmargin) THEN Acurse(colcrs,rowcrs) colcrs = char rowcrs = needx Acurse(colcrs,rowcrs) FI need = 0 ELSEIF char = 'A THEN ; cursor up IF rowcrs > 0 THEN Acurse(colcrs,rowcrs) rowcrs ==- 1 Acurse(colcrs,rowcrs) FI ELSEIF char = 'B THEN ; cursor down IF rowcrs < LL THEN Acurse(colcrs,rowcrs) rowcrs ==+ 1 Acurse(colcrs,rowcrs) FI ELSEIF char = 'C THEN ; cursor right IF colcrs < rmargin THEN Acurse(colcrs,rowcrs) colcrs ==+ 1 Acurse(colcrs,rowcrs) FI ELSEIF char = 'D THEN ; cursor left IF colcrs > 0 THEN Acurse(colcrs,rowcrs) colcrs ==- 1 Acurse(colcrs,rowcrs) FI ELSEIF char = 'F THEN ; inverse on inv = 1 ELSEIF char = 'G THEN ; inverse off inv = 0 ELSEIF char = 'H THEN ; home Acurse(colcrs,rowcrs) colcrs = 0 rowcrs = 0 Acurse(colcrs,rowcrs) ELSEIF char = 'I THEN ; reverse lf Acurse(colcrs,rowcrs) IF rowcrs > 0 THEN rowcrs ==- 1 ELSE UpdateDL(LL, 0) FI Acurse(colcrs,rowcrs) ELSEIF char = 'J THEN ; erase to EOS FOR ch = colcrs TO 79 DO Achr(ch,rowcrs,' ) OD i = rowcrs + 1 WHILE i <= LL DO Zero(DBase(LMap(i)),320) i ==+ 1 OD Acurse(colcrs,rowcrs) ELSEIF char = 'K THEN ; erase to EOL FOR ch = colcrs TO 79 DO Achr(ch,rowcrs,' ) OD Acurse(colcrs,rowcrs) ELSEIF char = 'L THEN ; insert space Acurse(colcrs, rowcrs) Ains(colcrs, rowcrs) Acurse(colcrs,rowcrs) ELSEIF char = 'M THEN ; delete char Adel(colcrs, rowcrs) Acurse(colcrs,rowcrs) ELSEIF char = 'N THEN ; insert line Acurse(colcrs, rowcrs) UpdateDL(LL, rowcrs) colcrs = 0 Acurse(colcrs,rowcrs) ELSEIF char = 'O THEN ; delete line UpdateDL(rowcrs, LL) colcrs = 0 Acurse(colcrs,rowcrs) ELSEIF char = 'Y THEN ; cursor addr need = 2 FI IF need = 0 THEN inesc = 0 FI RETURN PROC Aopen() CARD savetop savetop = memtop InitBitMap() InitDL() IF memtop < appmhi THEN memtop = savetop [LDY 147 RTS] ; fail FI inesc = 0 inv = 0 need = 0 lmargin = 0 rmargin = 79 rowcrs = 0 colcrs = 0 Acurse(colcrs,rowcrs) [LDY 1 RTS] PROC Aclose() [LDY 1 RTS] PROC Aput(BYTE areg) IF inesc =1 THEN; escape sequence Aesc(areg) ELSEIF areg = $1B THEN ; ESC inesc = 1 ELSEIF areg = $9B THEN ; EOL Acurse(colcrs,rowcrs) colcrs = 0 Ascroll() ELSEIF areg = $0A THEN ; lf Acurse(colcrs,rowcrs) rowcrs ==+ 1 Ascroll() ELSEIF areg = $08 THEN ; BS IF colcrs > 0 THEN Acurse(colcrs,rowcrs) colcrs ==- 1 Ascroll() FI ELSEIF areg = $07 THEN ; bell ; do nothing ELSEIF areg = $09 THEN ; TAB Acurse(colcrs,rowcrs) colcrs = (colcrs + 8) & $F8 Ascroll() ELSE Achr(colcrs,rowcrs,areg) colcrs ==+ 1 Ascroll() FI [LDY 1 RTS] PROC Anofunc() [RTS] PROC Adummy() [LDY 1 RTS] PROC Ahandler() BYTE ARRAY hatabs = $031A BYTE pos, found ;do not change the following 3 lines CARD ARRAY atab(6) BYTE Jmp = [JMP] CARD init ; define device entry points atab(0) = Aopen - 1 ;OPEN atab(1) = Aclose - 1 ;CLOSE atab(2) = Anofunc - 1 ;READ atab(3) = Aput - 1 ;WRITE atab(4) = Adummy - 1 ;STATUS atab(5) = Anofunc - 1 ;SPECIAL init = Adummy ;INIT ; find entry in hatabs found = 0 pos = 0 WHILE (pos < 34) AND (found = 0) DO IF hatabs(pos) = 0 THEN found = 1 ELSE pos ==+ 3 FI OD IF found = 0 THEN PrintE("*** A: too many devices") ELSE hatabs(pos) = 'A hatabs(pos + 1) = atab & 255 hatabs(pos + 2) = atab RSH 8 FI RETURN ;**************************** ;* MAIN PROGRAM ;**************************** MODULE BYTE ch = $02FC, shflok = $02BE, speed = [3], wsize = [0], sbits = [0], lf = [0], iparity = [0], oparity = [0] CARD bcount = $02EB ; iocb 3 definitions BYTE iocb3cmd=$372 ; cmd byte CARD iocb3buf=$374, ; buffer address iocb3len=$378 ; buffer length BYTE iocb3aux1=$37A, ; aux1 byte iocb3aux2=$37B ; aux2 byte DEFINE BUFLEN = "1024" BYTE ARRAY BUFFER(BUFLEN) PROC CIO=$E456(BYTE areg, xreg) PROC XIO_R(BYTE cmd, aux1, aux2) ;because library XIO seemed flakey iocb3cmd = cmd iocb3buf = 0 iocb3len = 0 iocb3aux1 = aux1 iocb3aux2 = aux2 CIO(0,$30) RETURN PROC load_R(); load R: handlers [$A9 $50 $8D $00 $03 $A9 $01 $8D $01 $03 $A9 $3F $8D $02 $03 $A9 $40 $8D $03 $03 $A9 $05 $8D $06 $03 $8D $05 $03 $A9 $00 $8D $04 $03 $8D $09 $03 $8D $0A $03 $8D $0B $03 $A9 $0C $8D $08 $03 $20 $59 $E4 $10 $01 $60 $A2 $0B $BD $00 $05 $9D $00 $03 $CA $10 $F7 $20 $59 $E4 $30 $06 $20 $06 $05 $6C $0C $00 $60 ] PROC init_R(); set options for R: Close(3) Open(3,"R1:",13,0) XIO_R(34,192+48,0) XIO_R(38,lf*64+oparity+4*iparity,0) XIO_R(36,speed+7+wsize*16+128*sbits,0) iocb3cmd=40 ; start concurrent I/O iocb3buf=BUFFER iocb3len=BUFLEN CIO(0,$30) bcount = 0 RETURN PROC init_A(); set up A: device Ahandler() ; install A: handler Close(2) Open(2,"A:",8,0) SetColor(1,0,0) ; SetColor(2,12,15) ;white field ; SetColor(2,3,6) ;amber field SetColor(2,10,12) ;lt. blue field RETURN PROC intro() Close(7) Open(7,"K:",4,0) shflok = 0 init_R() init_A() RETURN BYTE FUNC remote(); remote char? iocb3cmd=13 ; get R: status CIO(0,$30) ; *** call CIO *** IF bcount = 0 THEN RETURN(0) FI RETURN(1) BYTE FUNC local() ; local char? RETURN($FF-ch) PROC do_local(); process local BYTE char char = GetD(7) IF char = 127 THEN ;tab char = 9 ELSEIF char = 125 THEN ;left curl char = 123 ELSEIF char = 157 THEN ;right curl char = 125 ELSEIF char = 255 THEN ;right curl char = 125 ELSEIF char = 156 THEN ;tilde char = 126 ELSEIF char = 254 THEN ;tilde char = 126 ELSEIF char = 126 THEN ; delete char = 127 FI PutD(3,char) RETURN PROC main() BYTE char load_R() appmhi = ProgEnd intro() DO IF remote() THEN char = GetD(3) PutD(2,char) ELSEIF local() THEN do_local() FI OD RETURN