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. KE"'ROARD 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 specif' the termina- 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 termina- 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~chaT 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- 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 their characters and their modifiers. Here is the meaning of each entry in a terminator list: Terminator 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 specify~ a mask of $FBFF (bit 10 = 0). 'terminator~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. Terminator 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 to 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 GS1OS Character Devices