Character Devices
An important feature of GS/OS is that you can use its commands to communicate with
character devices, not just block-structured disk devices. For example, to get keyboard
input, you open the keyboard, read data from it, then close it, just as if it were a file on
a disk drive. Under ProDOS 8, you must use completely different techniques to access
character devices, such as accessing memory-mapped hardware addresses or calling
firmware subroutines.
The character FST is responsible for translating standard GS/OS commands into
commands that the driver for a character device understands. It resides in a file called
CHAR.FST in the SYSTEM/FSTS/ subdirectory of the boot disk.
In this chapter, we see how to use GS/OS commands to communicate with two
particularly important character devices: the keyboard and the video display screen.
The device driver that controls these devices is called the Console Driver; we also
investigate the commands this driver understands.
Note: The Apple IIC has a tool set, called the Text Tool Set, that you can also use
to access character devices. But you should use the GS/OS commands since they
are more powerful and easier to use.
GS/OS COMMANDS FOR CHARACTER DEVICES
The character FST works with a small subset of GS/OS commands: Open, NewLine,
Read, Write, Close, and Flush. (You shouldn't use NewLine, however, because the
Console Driver supports a more powerful way of terminating input prematurely; see
the discussion of terminator characters below.) You can also use the GS/OS device
commands, DInfo, DControl, DRead, DStatus, and DWrite, to communicate directly
with any character-based device driver, including the Console Driver.
329
The name of the Console Driver is usually CON SOLE, but the user may be -
change it when a GS/OS driver configuration program becomes available. To deter--
mine the actual name, call the DInfo command with successively higher device
numbers (starting with 1) until DInfo returns a device - ID - num of $000A. The
name that DInfo returns for the device with this device - ID - num is the actual name
of the Console Driver.
DControl and DStatus are important for setting and returning various parameters
and operating mode flags the Console Driver uses. We summarize the DControl and
DStatus commands near the end of this chapter.
You won't need to use DRead and DWrite to communicate with the Console
Driver (you can use Read and Write instead), so they are not described here.
KEYBOARD INPUT
The Console Driver deals with character input from the Apple IIC keyboard. It reads
data directly from the keyboard hardware or, if the IIC Event Manager is active, from
the operating system event queue. The Console Driver returns standard ASCII
character codes (bit 7 of each code is zero).
The Console Driver supports two main input modes: raw mode and user input mode.
In raw mode, the driver continuously polls for keyboard data until it has read in the
number of characters requested in the Read command parameter table or until the
user enters a terminator character. (More on terminator characters below.) It then
returns these characters, including any terminator character, in the Read command's
data buffer. During a raw mode input operation, no cursor appears on the screen, and
characters are not echoed on the screen. Raw mode is useful for programs that wish to
implement their own user. input and editing routines.
In user input mode, the driver uses an intelligent User Input Routine (UIR) to
return keyboard input. The UIR displays an input field and a cursor, echoes input, and
permits editing according to Apple's human-interface guidelines. An input operation
ends when the user enters a terminator character.
To begin a keyboard input operation, you must first open the "file" called .CON-
SOLE using the GS/OS Open command. After doing this, set up various input
parameters and the appropriate input mode, as follows:
1. Select wait or no-wait mode. When wait mode is active, GS/OS keeps processing
a Read command until the user has typed in the specified number of characters
from the keyboard (in raw mode) or until the user enters a terminator character
(in raw or UIR mode). When no-wait.raw mode is active, GS/OS returns control
to the application as soon as it determines there is no keyboard input available.
(The UIR always operates in wait mode, so control never returns until the user
enters a terminator character.) This gives the application a chance to perform
other tasks during a keyboard input operation, but the application must keep
making Read calls until the user enters a terminator character. The default mode
is wait mode; to switch to no-wait mode, use the GS/OS DControl command.
330 GS/OS Character Devices
2. Set up the input port. The input port is a 17-byte record that keeps track of the status
of a UIR input operation. When you open the Console Driver, GS/OS sets up a
default input port suitable for most input operations. If you want to change some of
the entries in the port, for example, to set the initial cursor position and mode, now is
the time to do it. The procedure to follow is to read in a copy of the current input port
(with DStatus), change the desired fields, and then set the new input port (with
DControl). A description of the fields in the input port appears below.
3. Set up the terminator characters. A terminator character is one that, when entered,
causes a Read operation to end. The Console Driver lets you specify' the termini-
tor character and the combination of modifier keys that must be held down when
the user enters it. When using the UIR, the application must set up a termini-
tor character, typically the Return key, or the user won't be able to end an
input operation. You can set up a list of terminator characters with the DControl
command.
4. Set up the default string. The UIR displays a default string in the input field when
you call the Read command for the first time after an Open. Use the DControl
command to set up the default string.
Once these preliminary steps are out of the way, use the Read command (with the
reference number set to the one returned by Open) to return the number of characters
specified in the request - count field of its parameter table.
On return from the Read command, use DStatus to get a copy of the input port.
The exit - type field of this port (see below) indicates the reason for the return of
control. In normal raw mode, a $00 value indicates that the specified number of
characters has been returned, so input processing can end. In no-wait raw mode, a $00
indicates a no-wait return, and the application must inspect the transfer - count field
to determine if any more characters have to be processed; if so, it must process them,
then call the Read command again (after reducing request - count) until the desired
number of characters have been returned.
Any other value for exit - type, in raw mode or UIR mode, indicates that a
terminator character was pressed. If the value corresponds to an application-defined
interrupt key (see below), you should process it without disturbing the current UIR
environment, and then call the Read command again. (When you call Read again in
UIR mode, you don't have to make any adjustments to the parameter table because
the Console Driver keeps track of the state of the input operation when it was last
exited.) If you wish to abort the input operation instead, use DControl's Abort Input
subcommand. This subcommand zeroes the entry - type field of the input port (see
below) so that the next Read command will not be interpreted as a continuation of the
previous one.
If a non-zero exit-type value does not correspond to an interrupt key, the input
operation is complete. The Console Driver handles the next Read command as an
initial entry to UIR mode.
Keyboard Input 331
When you're through reading keyboard input, call the Close command. This is not
necessary, however, if you still need the Console Driver to process video output or
more input.
The Input Port
As we mentioned, the Console Driver maintains an input port to keep track of the input
environment. The fields in the 17-byte input port are arranged in the following order:
fillchar
defcursor
cursor-mode
beepflag
entry-type
exittype
lastchar
lastmod
last~term~ch
lasttermmod
eursorpos
input - length
input-field
origin~h
originx1
originx2
originv
The values in these fields completely describe the input environment. Here is what
each field means:
fill~char This is the character code that UIR sends to the Console Driver when it
wants to display an empty position in the input field. The default value is $20 (a space).
deft cursor. Three bits in this byte indicate the default cursor mode at the begin-
ning of a UIR session:
332 GS/OS Character Devices
bit 7 0 = put cursor at end of default string
1 = put cursor at beginning of default string
bit 6 0 = don't allow the entry of control characters
1 = allow the entry of control characters
bit 0 0 = use an insert cursor
1 = use an overstrike cursor
The default value is $00.
cursor mode. One bit in this byte indicates the current cursor status in UIR mode:
bit 0 0 = an insert cursor is active
1 = an overstrike cursor is active
beep -~g If this byte is nonzero (the default value), the UIR beeps if the user
attempts an illegal operation. If this byte is zero, there is no beep.
entry - type. When the application calls the Read command, the Console Driver
inspects this byte to determine the current input status. The possible values are
$00 this is the initial entry
$01 this is an interrupt key reentry
$02 this is a no-wait mode reentry (raw mode only)
The Console Driver adjusts this byte whenever it relinquishes control to the applica-
tion, setting it to $00 if a noninterrnpt terminator character was entered. This enables
the Console Driver to properly restart a Read operation that is already in progress.
exit - type. This byte indicates the reason for the exit from the Read request:
$00 a raw-mode exit, because the maximum number of
characters have been read, or a no-wait raw mode
exit
A nonzero value indicates a terminator key was pressed. The value is the entry number of
the terminator character in the terminator table. If the terminator is not an interrupt key,
the Console Driver zeroes the entry - type field so that the next Read operation will begin
from scratch; otherwise, it puts a $01 there so that the Console Driver will continue the
same input operation the next time the application calls Read.
iastcbar. The ASCII code of the most recently typed key. The high-order bit is
always 0.
ia#t - mad. The modifier byte of the most recently typed key. The meanings of the
bits in the modifier byte are the same as those for the bits in the high-order byte of a
terminator modifier (see Figure 9-1).
Keyboard Input 333
Figure 9-1 The format of the terminator mask and the terminator modifier wor;l
~ CSoh~ft key down
ntrol key down
Caps Lock key down
[reserved; must be zero]
Keypad key down
Interrupt key designator
Option key down
Open-Apple down
lastte~ch. The ASCII code of the most recently typed terminator key. i
high-order bit is always 0.
last - tea - mod. The modifier byte of the most recently typed terminator key.
meanings of the bits in the modifier byte are the same as those for the bits in the
high-order byte of a terminator modifier (see Figure 9-1).
cursor pos. The position of the cursor relative to the start of the UIR input field. &
$00 value means the cursor is over the first character in the field. The maximum value
is the length of the field, meaning the cursor can move to the first character past the
end of the field.
input - length. The current length of the string being edited. This is the same as the
number returned in the transfer - count field of the Read command.
input field. This value is for the Console Driver's private use.
originh. The horizontal position of the cursor in UIR mode.
origin x1. This value is for the Console Driver's private use.
origin x2. This value is for the Console Driver's private use.
origin 0. The vertical position of the cursor in UIR mode.
UIR Editing
The UIR supports several standard commands for editing the characters in the input field:
334 GS/OS Character Devicesleft-arrow
* -left-arrow
right-arrow
*-right-arrow
or
* - < or *
* -E or Control-E
Delete or Control-D
or Control-Delete or
*-Delete or *-D
* -F or Control-F
* -X or Control-X
or Clear
* -Y or Control-Y
* -Z or Control-Z
* -Control- <char>
Terminator Characters
Move the cursor one position to the left.
Move the cursor to the start of the previous word (if it's
currently over a space) or to the start of the current word (if
it's not).
Move the cursor one position to the right.
Move the cursor to the end of the next word (if it's currently
over a space) or to the end of the current word (if it's not).
Move the cursor to the end of the input field.
Move the cursor to the beginning of the input field.
Toggle the cursor between insert mode (blinking under-
score) and overstrike mode (blinking box).
Erase the character to the left of the cursor and move the
characters beneath and to the right of the cursor one position
to the left. The cursor also moves one position to the left.
Erase the character underneath the cursor and move the
characters to the right of the cursor one position to the left.
The cursor stays put.
Erase the entire input field.
Erase the characters from the current cursor position to the
end of the input field.
Restore the default input string.
Enter a control character. You can do this only if control
character entry is enabled by setting bit 6 of the def cursor
field in the input port record.
A terminator character is one that when entered, causes a raw mode Read operation to
end even if the user has not yet entered the number of characters specified in the
request - count field of the Read command. Entering a terminator character also
forces a UIR operation to end right away. (In fact, the user must end a UIR operation
by entering a terminator character, so the application must define at least one such
character.) The transfer - count field in the Read parameter table contains the actual
number of characters that Read has returned in the data - buffer field.
When the user enters a terminator character, the exit - type field in the input port
is set to the position number of the terminator character in the terminator list. The
position number of the first item in the list is $01.
The Console Driver lets you specify the terminator character itself, as well as the
modifier keys (Open Apple, Shift, Caps Lock, and so on) that the user must hold down
while entering the character. It uses a data structure called a terminator list to hold
Keyboard Input 335
the definitions of up to 254 terminator characters and their modifiers. The list
with a terminator mask and a terminator count and is followed by the ter
characters and their modifiers.
Here is the meaning of each entry in a terminator list:
Ter'ninator Mask (word). When the user enters a keystroke, the Console
logically ANDs the keystroke data with the terminator mask before checking the list
terminator modifiers for a match. By setting bits of the mask to zero, you can
matches even if the associated modifier keys are being pressed. (Figure 9-1 shows
meaning of the bits in a terminator mask.) If the state of the Caps Lock key
unimportant to your application, for example, you would speci~ a mask of $FBFF (bit
10 = 0).
'Ie~nator Count (word). This word contains the number of entries in the list of
terminator modifiers. If there are no terminators, this word should be set to zero.
Ter'ninator Modifiers (words). A terminator modifier is a 2-byte value describing
the ASCII code of the terminator (low byte) and the modifiers themselves (high byte).
Figure 9-1 shows the meaning of each of the bits in a terminator modifier.
If bit 13, the interrupt bit, of a terminator modifier is set to 1, the terminator
character is considered an interrupt key. When the user enters an interrupt key, the
Read command ends, but the entry - type byte in the input port is set to $01. The next
time the same Read command is called, input processing continues from where the
interruption took place.
One reason Jo define an interrupt key is to implement a help command. To include
a standard * -? help key, for example, set bits 15 and 13 in the modifiers byte and put
the ASCII code for a question mark in the low-order byte. You should also assign * -I
as an interrupt key so that the user can get help without having to press a Shift key
(and I share the same keycap).
VIDEO OUTPUT
The Console Driver also manages all activities related to the display of characters on
the Apple IIC text screen. There are actually two text screens: an 80-column, 24-line
screen and a 40-column, 24-line screen; you can switch between them by sending
control codes to the Console Driver with the GS/OS Write command.
The Console Driver stores video data directly to the video RAM buffers located at
$040~$07I'I' in banks $E0 and $E 1 of memory. As a result, applications that want to
access the screen bytes directly should not look at the "traditional" video RAM buffers
in banks $00 and $01 even if these areas are set up to shadow to banks $E0 and $E1.
See Exploring the Apple tics for a discussion of text screen shadowing.
The Console Driver lets you confine video output operations to any rectangular
window within the fall hardware screen; this window is called a text port. When you first
336 GS/OS Character Devices