PET Super Editor
Create strings on screen from single keystrokes, prevent scrolls, softkey, define control keys, transfer the entire screen into an array – these and other techniques can be achieved with this versatile screen editing subroutine. For data bases, mailing lists, assemblers, or any other program which requires frequent user input, the ideas and examples in this article should prove of value. It works on any PET/CBM.
One of the first items many people buy for their computers is a word processing program. A word processor (or its cousin, the text editor) allows text data to be entered, changed, added, or deleted at will. Because a word processor is screen-oriented, the user can manipulate the displayed text and quickly perform editing functions.
Word processing is not the only application which requires the input of extensive text data. Other applications, such as mailing list management or data base management, also involve the entry of much text data. In many of these programs, however, input is laborious and inflexible, limited to line-by-line entries.
With a text editor, text entry is easy. Input for other applications can be just as easy. Although most word processor and text editor programs are written in assembly language, a simple, fast BASIC routine provides some of the advantages of the dedicated text processors, without resorting to machine language. This routine can be incorporated into any program.
This article introduces a use of the GET command that gives the programmer full control of the keyboard and the screen. I have used it to write a text editor, a mailing list program, and an assembler-editor. The routine described below is screen-oriented, displays a blinking cursor, lets each key act normally unless altered by the programmer, and is as fast as the fastest typist. Although I have written this routine for the PET, the idea can be used with many computers. It is necessary to know only a few operating system locations.
What GET Does
The GET command in most BASICs polls the keyboard and returns a value if a key has been struck since the last inquiry. The TRS-80 equivalent is INKEY$. If a key has been struck, GET returns the ASCII value of the key struck; otherwise, it returns the null string (string of length zero). Hitting RETURN is not necessary, and the key hit does not appear on the screen, unless the program provides for that. GET is often used in games for a waiting loop:
10 PRINT "HIT ANY KEY TO CONTINUE." 20 GET Z$: IF Z$ = "" THEN 20 :REM NULL STRING 30 < PROGRAM CONTINUES >
In another common use of GET, the answer from the user will appear on the screen as soon as a valid key is hit:
10 PRINT "DO YOU WANT [QUESTION]? ANSWER 'Y' OR 'N' ldquo;; 20 GET Z$ : IF Z$ <> "Y" AND Z$ <> "N" THEN 20 30 IF Z$ = "Y" THEN PRINT "YES": … YE S RESPONSE 40 PRINT "NO" : … NO RESPONSE
The previous example demonstrates two things. First, the keyboard can be selectively enabled. (This is sometimes called softkey, since the keys are defined by software, not hardware.) Each key can have its usual meaning, a special meaning, or no meaning. (If the key has no meaning, it is said to be disabled.) Second, the program determines what screen output, if any, there is for each key. (By "key" we mean a value that can be input from the keyboard. Most keys have a shifted and an unshifted value.)
Combining GET With Softkeys
These two features can be combined to allow full-screen editing and input under program control. This is far superior to the line-by-line function of the INPUT statement. The routine below has the following advantages:
- full-screen editing.
- windows and margins may be defined for all PETs.
- use of all cursor and edit keys.
- all characters permitted, except the double quote mark (the double quote mark is disabled). The colon and comma are permitted.
- TAB function can be simulated without a TAB key.
- blinking cursor (without footprints).
- normal or special use of every key.
Program 1: Kernel Of Screen Editor
90 PRINT HOME$; : REM *HOME$ = CHR$(19) 100 P=PEEK (196) * 256+PEEK(197)+POS(0) : IF PO S(0)=MB THEN PRINT BELL$; : REM *B ELL$=CHR$(7) 110 CH= PEEK(P): K= 128 120 POKE P, CH+K: T= TIME+ 30 130 IF TIME> T THEN K= 128-K: GOTO 120 140 GET Z$: IF Z$= "" OR Z$= QT$ THEN 130 : REM *QT$= CHR$(34) 200 POKE P, CH: PRINT Z$; ESC$; : GOTO 100
The Kernel Routine
Program 1 is the kernel of a screen editor. I use this in any program that involves extensive input. So far, that includes a text editor, a mailing list program, an assembler-editor, and a sales account program.
90 Puts cursor in top left corner. Not mandatory.
100 p is the location in screen RAM of the cursor. If the cursor has advanced to the margin minus 4 (mb), then the bell rings.
110 ch is the screen character at location p.
120-130 The automatic cursor, once a second, alternates the character at the position it is over with the character in reverse video. This can be done manually. Adding 128 to the screen code results in the reverse video character. The variable k changes its value every 30 jiffies (1/2 second) from 0 to 128, providing a simulation of the cursor. (It is assumed that there are originally no reverse video characters on the screen. If there are, change line 110 to: ch = peek(p): kc = 128 : if ch> kc then kc= -kc: k = 0, and change line 130 to: if time> t then K = kc-k: goto 120.)
140 The wait loop illustrated above, with one difference: the double quote mark is disabled so that later the program can take data off the screen using the INPUT statement.
150-190 This is where all sorts of special work can be done.
200 Puts the character into its original video mode and prints the new character. The program prints the invisible character, esc$, to avoid insert mode, and (CBM 8000 only) to prevent the user from breaking the window through successive HOME's, for Upgrade ROM PETs, use POKE 205, 0. For Original PETs, use POKE 234, 0.
Here are four examples of how to use this control of keyboard and screen. The line numbers given replace or add to the lines in Program 1.
1. To set a bottom margin and prevent scrolling. When accepting lines by the screenful, it is inconvenient to have lines scroll off the top of the screen. It takes special programming not to lose that data. To avoid that, I allow the user to work on only what can fit on the screen, and I do not permit any lines to scroll up. Lines 200-210 work because a p value greater than 34687 means that the cursor is on the last line.
200 POKE P, CH: IF P> 34687 THEN IF Z$=CR$ O R Z$=CD$ THEN 100 : REM 34687=327 68+80*24-1 210 PRINT Z$; ESC$; : GOTO 100 (cr$ = chr$(13) = car. return, cd$ = chr$(17) = cursor down)
This kind of bottom margin that prevents scrolling is different from the CBM 8000 Set Bottom command, which allows scrolling.
2. To set a top margin (must be used with bottom margin to prevent scrolling). The PET stores the row number (0-24) of the cursor at memory location 216. "tmargin" is the number of the top row of the margin.
105 IF PEEK(216) < TMARGIN THEN PRINT : GO TO 100
3. To set a left margin.
106 IF POS(0) < LMARGIN THEN TAB(LMARGIN - 1); : GOTO 100
4. To set a right margin.
115 IF POS(0) < RMARGIN THEN PRINT CHR$ (15 7); : Z$= CR$ : GOTO 200 (chr$(157) is cursor left.)
To develop special key functions, use IF statements. For example, the backslash (\) key is seldom used. It could be defined to print an often-used phrase, such as the name of your company.
150 : IF Z$= "\" THEN Z$= "ACME SOFTWARE, INC." : GOTO 200
In this way the TAB key for PETs can be simulated. Here we will use the RVS key for a TAB key. Tabs are at 5, 10, 20, and 30.
Given: dim tb(4): tb(0)= 4: tb(1) = 9: tb(2) = 19: tb(3) = 29: tb(4) = 40 150 IF Z$<> CHR$ (18) THEN 200 160 X= -1 170 X= X+1 : IF POS(0)> TB(X) THEN 170 180 POKE P, CH: PRINT TAB(TB(X));: GOTO 100
Adding Control And Function Keys
The most powerful use of this feature is the implementation of two-key sequences, with the first key acting like a control or SHIFT key. If desired, every key can be given an additional meaning. The CBM 8000 offers many special editing features that do not correspond to a single key.
I designated one key, the backslash, as a control key. (This special key can be any one of your choosing. The keys are all soft now. If you are using Charles Brannon's Keyprint utility, change the definition of B$ below.) Certain keys after a backslash were given new functions. If the keys are not preceded by a backslash, they operate normally. The four special edit functions I implemented are: delete line (\DEL), insert line (\INST), erase end (\CRSR right), and erase begin (\CRSR left). Given: B$= " \ "
150 IF Z$ <> B$ THEN 200 155 POKE P, CH+ 128: REM REVERSE CHAR SO U SER KNOWS PROGRAM WAITING FOR NEX T KEY 160 GET Z$: IF Z$ " " THEN 160 170 IF Z$ = CHR$ (148) THEN Z$ = CHR$(149): GOT O 200 175 IF Z$= CHR$(20) THEN Z$= CHR$(21) : GOT O 200 180 IF Z$= CHR$(29) THEN Z$= CHR$(22) : GOT O 200 185 IF Z$= CHR$ (157) THEN Z$= CHR$ (150) : GOT O 200 190 Z$= "" : REM INVALID KEY HIT; IGNORE B ACKSLASH
Another use of this feature allows you to define the keys to have certain string values. In a mailing list program, I allowed the user to define up to four keys. The user (in my area) might define them:
\m = "Mr. and Mrs. "
\d = "Dr. and Mrs. "
\p = "Philadelphia PA 191 "
\n = "New York NY 100"
Both the keys used and the strings assigned are changeable.
Accepting Data From The Screen
The PET has a feature that makes accepting a screenful of data possible: an addressable keyboard buffer. Here is how the screen can be accepted:
Given: dim a$(24) Given: in$ = chr$(148) + qt$ + esc$ + chr$(157) + chr$ (148) + chr$(148) chr$(148) is the insert key; chr$(157) is the cursor left. (Due to the use of esc$, PET <4.0 may have to use a POKE statement to get out of quote mode.)
Important restriction: The maximum length of the line is three less than the screen width; for example, 80 -3 = 77. This can be enforced by using either a left or right margin (explained above).
Here's the program to accept the screen:
400 PRINT HOME$; HOME$ : FOR I=1 TO 10: GE T Z$: NEXT I : REM EMPTY BUFFER 410 FOR I= 0 TO 24 420 : POKE 623, 13: POKE 158, 1 430 : PRINT IN$; : INPUT A$ (I) 440 : NEXT I
The whole screen is now in a$ array. One other restriction: it is important that no key be struck during the few seconds required to accept the screen.
The screen is altered after in$ is printed. This is not important if the next action, for example, is to print the menu. If it is important, all traces can be erased by printing deletes. But then only 24 lines at a time can be taken in: the top 24 for other than CBM 8000, or the bottom 24 with CBM 8000 and the use of the scroll down command. This is because a carriage return will be executed after the last INPUT command. If the bottom screen line is INPUT, then when the carriage return is executed, the line will scroll up. I take only 24 lines at a time anyway, in order to use the top line for instructions and messages.
Speed: The routine in Program 2 is very fast. It will accept typing at the rate of 110 words a minute. Three things are done to attain this speed. All constants are replaced by variables. The variables used most often are the first defined. And the routine is written into the first lines of the program.
Program 2 is an example configuration for a CBM 8000. Lines 100-220 are the GET routine. Lines 300-420 are for the programmer to define his special functions. After a double backslash (\\), the data on the screen is accepted into a$ array in lines 500-660. The top line is used for messages. A **\\ appears in the top right corner when \\ is hit so that the user knows another keystroke is needed. The text data is displayed in screen pages of 24 lines each. The routine corrects for the insertions and deletions of lines. The screen will not scroll. Lines 1000-1100 define the variables and constants (order is important). Lines 2000-2200 are the beginning of a main program.
Since the strings in the a$ array may contain commas and colons, the strings must be enclosed in quotes to save on tape. Also the a$ array may contain null strings. The PET cannot read a null string from tape. Therefore, use the following for reading and writing:
100 FOR I= 0 TO LAST 110 PRINT #1, QT$; CHR$ (32); A$ (I); QT$; C R$; : NEXT I : REM '; CR$;' IS FOR ˜ < 4.0 ONLY 200 FOR I= 0 TO LAST 210 INPUT #1, Z$: A$(I) = MID$(Z$,2) : NEXT ˜ I (chr$(32) may be almost any character, since it is discarded upon reading.)