Console Input/Output
Gene Zumchak
Buffalo, NY
Perhaps I'm stepping out of my domain to write an article on a software topic; however, since no "expert" has volunteered an article on the subject, I'd like to say a few words about the very important subject of console input/output.
Input/Output is the interface between the computer and the outside world. Simple input/output consists of switches, relay contacts, indicators, etc. Two other classes of I/O are console I/O and mass storage I/O. The latter would include tape or disk or any other method (usually using magnetic medium) of storing and retrieving large records or files. I'm limiting my discussion here to console I/O.
My experience is mainly with single-board computer types like the KIM, SYM, and AIM, and I will use them as examples, though the principles will apply as well to console systems like APPLE and PET.
A general-purpose computer system is of little value unless a user can communicate with it. This requires two things. First, the computer must have some minimal operating system to permit communication. Second, the computer must be connected to a console device. Traditionally, a computer's primary console device was a teletypewriter. This provides input (keyboard) and output (printer) and sometimes mass I/O in the form of punched paper tape. As a bonus the teletypewriter provides hard copy. More recently, the teletypewriter has been replaced by a CRT, or a CRT substitute, as the console device.
A CRT terminal, like the teletypewriter, is a serial device. It usually has a RS-232C voltage interface however, as opposed to the current loop interface of the TTY. There is, of course, no reason why console input cannot be a parallel keyboard, or the output a parallel or memory mapped display. Most computers with a built-in console device usually treat I/O directly in parallel.
The way that console I/O is treated is a function of the sophistication of the operating system software. At one extreme, some systems permit any devices to serve as console input or console output. At the other end, only a specific device pair can serve as console input and output. The earliest 6502 computer, the KIM, is between these two extremes.
How To Use Non-Serial Devices On KIM
The KIM has two console options: either the built-in keyboard and display or serial teletype format I/O. The Choice is made by a jumper on the application connector. (The KIM actually uses separate programs to treat I/O from the two console options). The user cannot, however, communicate with the operating system with a non-serial I/O device (a parallel video display for example) since the KIM makes no provision for interfacing any non-serial console devices. There are other problems. The tape routines cannot be employed in user programs since they terminate with a jump to the MONITOR instead of an RTS. This is an important point. If you are going to write any kind of routine that might find use elsewhere, write it as a subroutine. Still, the KIM with its monitor is really quite remarkable, considering that it was available within weeks of the 6502 chip itself. The hex keyboard and display, the built-in serial interface, and the built-in tape interface were important innovations.
How does one use a non-serial console device on the KIM? The only choice is to do without the KIM's monitor and replace it with one of your own that can accommodate your console I/O. Since all the KIM monitor does is inspect and change memory, giving it up is not a great loss. For other systems with somewhat more extensive operating systems, replacing the operating system with one of your own is no small project.
SYM Avoids The Problem
The author of the SYM operating system recognized this potential problem and avoided it. This was done by "vectoring" the console I/O. When the SYM is reset, it initializes a block of operating system RAM. Among the locations initialized are an input vector (INVEC) and an output vector (OUTVEC). These occupy three RAM locations each. The first contains the JMP op code, $4C; the next two locations are the low and high address of the specified routine. Thus, JSR INVEC will cause the program to run the routine whose address is found at INVEC, and return to the instruction following the JSR.
The SYM initializes INVEC and OUTVEC to point to the routines that service the on-board keyboard and display. However, should the serial input bit become active before a key on the keyboard is pressed, the SYM will switch the vectors to point to the serial I/O routines. At any time after reset, the user is free to change either the input or output vectors to point to his own routines. For example, suppose you wished to talk to the SYM using a parallel ASCII keyboard, but wanted to retain the on-board display. You would write a routine to service the ASCII keyboard and put the address of your program in INVEC. Now when the SYM looked for input, it would get it from the ASCII keyboard via INVEC.
Recently, as an experiment in a course I was giving, I wrote a routine to service an ASCII keyboard, attached to one of the SYM's ports. The ASCII data went to the low-seven bits; the keyboard strobe went to the high, or sign bit. I changed the input vector to point at my program. When I attempted to use the SYM, however, something strange happened. As I entered the monitor command, nothing happened until I hit the carriage return required to execute a command. That is, I did not see my command being entered on the SYM's display. This problem illustrated that there are two distinct kinds of input routine.
In a pure input routine, the program waits for an input, returning with the value (in the accumulator) when the input occurs. The SYM, however, expects an input routine with echo. Such a routine, before returning and giving up the character, causes the character to be sent to the output device. Thus, you are able to see the character as it is entered. Inputs are generally echoed, but there appears to be no agreement as to whether the echoing should take place as part of the input routine, or that the routine calling for the input should echo the character before processing it. Examples of both are common.
If you are writing a routine to service an input device, you should include both styles. Given a pure input routine, INPUT, an input with echo routine is just two instructions:
INWITHECHO JSR INPUT JMP OUTPUT;(OR OUTVEC)
Another approach is to write a common input routine for both styles, and have the routine determine whether echo is desired or not with a flag. This is the method used by the SYM for its serial input routine. A RAM location called TECHO determines whether echo is desired. Instead of first inputting a character and then echoing it out, the SYM just causes the output bit to follow the input bit as the input character is being received.
It should be noted that in the INWITHECHO routine above, the OUTPUT routine must not destroy the character being output. This is a very important property that all output routines should have.
When I wrote my operating system for my KIM to accommodate a parallel ASCII keyboard and parallel video display, I did not know about vectoring. I then wrote some action games for the video display which used the video output routine which I had in EPROM. A problem arose when I upgraded my I/O routines. The locations of the video output routine changed and, when I tried to load and run a game, it would bomb since it was pointing to a non-existent output routine. This problem could have been avoided if my operating system used vectored I/O. The game program then, would always point to the output vector. Even if the location of the actual output program changed, the vector could be changed to point to the new output routine. That is, I would not have to make modifications to the game program every time the operating system was changed.
AIM Software: A Curious Mix
The lack of vectoring for console I/O is most evident in the AIM system. The AIM software is a curious mixture of very clever programming and serious oversights. Like the KIM, the AIM has two choices for console input, the built-in keyboard and display printer, and a serial (TTY or CRT) interface. The choice is made by the slide switch. The switch affects both input and output simultaneously. (It should be understood that the vectors UIN and UOUT on the AIM have to do with mass I/O and do not affect console I/O.) For example, suppose you had a serial video device which you wanted to use for output, but you wished to use the AIM's ASCII style keyboard for input. If you put the switch in the TTY position to get serial output, the AIM would now look for input from the serial channel and you could not use the keyboard. The switch should have been used to initialize the I/O vectors. Then, after the fact, the user could change the input vector, the output vector, or both, to accomodate any special console I/O.
In all fairness, the console output is vectored in a fashion. A vector called DILINKS was included so that output could be echoed to a video display. However, a carriage return appears as $8D and not $0D. A backspace is echoed as a space. Thus, any video device will not be able to respond properly to a backspace or delete. Instead of backing up one, it will go ahead one. The reason for this is that the AIM processes the delete by backing up the display pointer and overwriting the previously written character with a space. Incredibly, the delete is processed in the input routine. An input routine should be responsible for returning characters, period. It should not make value judgements on the characters or play around with output, except for straight echoing.
Although the AIM keyboard resembles that on a CRT, complete with Shift and Control keys, it can be used only as a TTY style (uppercase only) keyboard. While it would not be difficult to write a new input routine to produce lowercase characters and provide for "Caps lock" when appropriate since console input is not vectored, there's no way to tell the AIM that it should use your keyboard routine.
The lack of vectored I/O is evident in the AIM's software listing. In many places in the program, changes were made by jumping to a "patch" area near the end of the listing and then jumping back onto the program. Why didn't the authors just insert the necessary changes and reassemble? Apparently, the I/O addresses from an early version were used when making the BASIC or assembler ROMs, making those addresses inviolate. Thus, changes to the monitor, however necessary, could be made only if they did not affect the addresses of the I/O routines. Had vectors been used, the monitor could be updated and improved at any time, without affecting compatibility with ROMed accessories like BASIC.
Console input/output is an essential element in any general-purpose computer. The ability to customize and personalize a computer system's console will depend upon whether or not console I/O is vectored. Non-vectored console I/O places serious restraints on the system and on the user.
In a later installment, I plan to show how vectored I/O can be taken advantage of to "massage" canned I/O routines and overcome objections to ROMed software accessories.