ProDOS 8 Comman GS/OS and ProDOS 8 both have a low-level command interpreter that serves as an application's gateway to the operating system's commands. (The ProDOS 8 command interpreter is called the machine language interface or MLI.) Applications call the interpreter to perform various file-related operations, such as creating, deleting, open- ing, closing, reading, and writing files. The command interpreter for GS/OS supports 47 commands, and the one for ProDOS 8 supports 26. (These totals will undoubtedly increase as Apple releases new versions of the operating systems.) You invoke these commands from an assembly- language program in the same general way, using standard calling protocols defined by Apple. The protocols for GS/OS and ProDOS 8 are structurally similar but not identical. In this chapter, we take a close look at the GS/OS and ProDOS 8 MLI commands and see how to use them in assembly-language programs. In particular, we see how to z Call specific commands z Set up command parameter tables z Identify error conditions z Interpret error codes Along the way we look at several brief programming examples which should clarify how to use operating system commands in your own programs. 71 USING PRODOS 8 MLI COMMANDS It is very easy to execute a ProDOS 8 MLI command. A typical calling sequence looks something like this: [place values in the parameter table before calling the MLI] JSR $BF00 DFB CMDNUM DA PARMTBL BCS ERROR [continue your program here] RTS ERROR [put an error handler here] RTS PARMTBL DFB NPARMS ;$BF00 is the ProDOS 8 MLI entry point The MLI command number Address of command parameter table Carry is set if error occurred ;NPARMS = of parameters in table [place the rest of the parameters here in the order the MLI command expects] The key instruction here is JSR ~BF00. $BF00 is the address of the entry point to the ProDOS 8 MLI interpreter in main memory. This interpreter determines what MLI command the application is requesting and passes control to the appropriate ProDOS 8 subroutine to handle the request. The flowchart in Figure 4-1 shows what happens when an application executes a JSR $BF00 instruction. As soon as the MLI takes control, it modifies four important variables in the ProDOS 8 global page area: MLIACTY' ($BF9B), CMDADR ($BF9C/ $BF9D), SAVEX ($BF9E), and SAVEY ($BF9F). First, it changes bit 7 of MLIAC'IF' from 0 to 1 so that an interrupt-handling subroutine can determine if the interrupt condition occurred in the middle of an MLI operation. (We see why it's important to know this information in Chapter 6.) Next, it saves the current values in the X and Y registers in SAVEX and SAVEY. Finally, it stores the address of the instruction immediately following the three data bytes after the JSR $BF00 instruction at CMDADR. 72 GS/OS and ProDOS 8 Commands Figure 4-1 Flowchart of ProDOS 8 MLI operations JSR $BF00 Set bit 7 of Y in SAVEY MLIACTV = $BF9B CMDADR = $BF9c/$BF9D Store return SAVEX = $BF 9E address + 4 SAVEY = $BF9F in CMDADR Using ProDOS 8 MLI Commands 73 Control passes to this address after ProDOS 8 executes the MlLI command. (The MLI modifies the return address that the JSR places on the stack to ensure that control passes to this address rather than to the address following the JSR $BF00 instruction.) The MLI determines which command the application is requesting by examining the value stored in the byte immediately following the JSR $BF00 instruction. This byte contains the unique identifier code (or command number) associated with the MLI command. If the MLI encounters an unknown command number, a system error occurs. (We see how to identify and handle such errors later in this chapter.) Table 4-1 lists all 26 ProDOS 8 commands and command numbers. The 2 bytes following the command number contain the address (low-order byte first) of a parameter table the MLI command uses. This table begins with a byte holding the number of parameters in the table; the rest of the table holds data that the MLI command requires to process your request. After the MLI executes the com- mand, the table also holds any results that are returned. We describe the contents of the parameter table for each MLI command later in this chapter. The parameters an application passes to a ProDOS 8 MLI subroutine are of two types: pointers and values. A pointer is a 2-byte quantity that holds the address (low-order byte first) of a data structure it is said to be pointing to. (Typical data structures are an I/O buffer or an ASCII pathname preceded by a length byte.) A value is a 1-, 2-, or 3-byte quantity that holds a binary number. Multibyte values are always stored with the low-order bytes first. The parameters returned by an MLI subroutine are called results. A result is usually a 1-, 2-, or 3-byte numeric quantity (with the low-order bytes first), but it can also be a 2-byte pointer, depending on the command involved. If the number at the start of the parameter table does not correspond to the parameter count expected by the command, a system error occurs. Otherwise, the MLI proceeds to execute the command. While a command is being executed, a critical error condition may occur. Critical errors are very rare and occur only if ProDOS 8 data areas have been overwritten by a runaway program or if an interrupt occurs and no interrupt handler is available to deal with it. You cannot recover from such errors without rebooting the system. When a critical error occurs, the MLI executes a JSR $BF0C instruction. The subroutine at $BF0C (SYSDEATH) causes the following message to appear: INSERT SYSTEM DISK AND RESTART -ERR xx where xx is a two-digit hexadecimal error code. Four error conditions are possible: 01 unclaimed interrupt error 0A volume control block damaged 08 file control block damaged DC allocation block damaged 74 GS/OS and ProDOS 8 Commands Table 4-1 The ProDOS 8 MLI commands (in numerical order) Command Name (number) ALLOC INTERRUPT ($40) DEALLOC INTERRUPT ($41) QUIT ($65) READ BLOCK ($80) WRITE BLOCK ($81) GETTIME ($82) CREATE ($C0) DESTROY ($C1) RENAME ($C2) SET~FILEINFO ($C3) GET~FILEINFO ($C4) ON-LINE ($C5) SET PREFIX ($C6) GET PREFIX ($C7) OPEN ($C8) NEWLINE ($C9) READ ($CA) WRITE ($CB) CLOSE ($CC) FLUSH ($CD) SETH MARK ($CE) GET MARK ($CF) Function Installs an interrupt-handling subroutine Removes an interrupt-handling subroutine Transfers control to another system program, usually through a dispatcher program Reads a data block from disk Writes a data block to disk Reads the current date and time Creates a directory entry for a new file Removes the directory entry for an existing file or subdirectory and frees up the space it uses on disk Renames a file Changes the attributes for a file Returns the attributes for a file Determines the name of the volume directory for a disk Sets the default pathname prefix Returns the default pathname prefix Opens a file for I/O operations Sets the character that terminates a file read operation Reads data from a file Writes data to a file Closes a file Flushes a file buffer Sets the value of the Mark (position-in-file) pointer Returns the value of the Mark (position-in-file) pointer Using ProDOS 8 MLI Commands 75 Table 4-1 Continued Command Name (number) SETEOF ($D0) GETEOF ($D1) SETBUF ($D2) GETBUF ($D3) Function Sets the value of the EOF (end-of-file) pointer Returns the value of the EOF (end-of-file) pointer Changes the position of a file buffer Returns the position of a file buffer The volume control, file control, and allocation blocks are internal data structures ProDOS 8 uses to handle disk volumes and to open files. Normally, the MLI command starts finishing up by restoring the values of the X and Y registers (from SAVEX and SAVEY) and then, if a system error has occurred (see the next section), by executing a JSR $BF09 instruction. The subroutine at $BF09 (SYSERR) stores an error code in SERR ($BF0F). Since the MLI preserves the contents of the X and Y registers, there is no need for the application to do so. Finally, control passes to the instruction immediately following the pointer to the parameter table (BCS ERROR in the above example). Recall that the MLI interpreter stored this address at CMDADR ($BF9C/$BF9D) when it first took over. USING GS/OS COMMANDS The general procedure for calling a GS/OS command is similar to the one for calling a ProDOS 8 MLI command. It goes something like this: JSL $E100A8 ;Call GS/OS entry point DC I2'CommandNum' ;GS/OS command number DC I4'ParmTable' ;Address of parameter table BCS Error (Control resumes here after call) $E100A8 is the address of the GS/OS command interpreter entry point. You can call this entry point while the IIgs's 65816 microprocessor is in either native or emulation mode. Immediately following the JSL $E100A8 instruction is a word containing the identification number of the GS/OS command you wish to use. Table 4-2 lists all the GS/OS commands and command numbers. Following the command number is the long address (4 bytes, low-order bytes first) of a parameter table containing parameters required by the command and spaces for results returned by the command. The parameters can be one- or two-word numeric values (a word is 2 bytes) or long pointers (4 bytes) and are stored with the low-order bytes first. 76 GS/OS and ProDOS 8 Commands Table 4-2 The GS/OS commands (in numerical order) Command Name (number) Function Create ($2001) Destroy ($2002) OSShutdown ($2003) ChangePath ($2004) SetFileInfo ($2005) GetFileInfo ($2006) Volume ($2008) SetPrefix ($2009) GetPrefix ($200A) ClearBackup ($200B) SetSysPrefs ($200C) Null ($200D) ExpandPath ($200E) GetSysPrefs ($200F) Open ($2010) Newline ($2011) Read ($2012) Write ($2013) Close ($2014) Flush ($2015) SetMark ($2016) GetMark ($2017) Creates a directory entry for a new file Removes the directory entry for an existing file or subdirectory and frees up the space it uses on disk Shuts down GS/OS in preparation for a cold reboot or a power down Renames a file or moves a file's directory entry to another subdirectory Changes the attributes for a file Returns the attributes for a file Returns the volume name, total number of blocks on the volume, number of free blocks on the volume, and the file system identification number for a given disk device Sets the pathname prefix for any of the standard GS/OS prefixes (except */) Returns the pathname prefix for any of the standard GS/OS prefixes (except */) Clears the backup bit in the file's access code byte Sets system preferences Executes all queued signals Creates a full pathname string Returns system preferences Opens a file for I/O operations Sets the character that terminates a file read operation Reads data from a file Writes data to a file Closes a file Flushes a file buffer Sets the value of the Mark (position-in-file) pointer Returns the value of the Mark (position-in-file) pointer Using GS/OS Commands 77 Table 4-2 Continued Command Name (number) Function SetEOF ($2018) GetEOF ($2019) SetLevel ($201A) GetLevel ($201B) GetDirEntry ($201C) BeginSession ($201D) EndSession ($201E) SessionStatus ($201F) GetDevNumber ($2020) Format ($2024) EraseDisk ($2025) ResetCache ($2026) GetName ($2027) GetBootVol ($2028) Quit ($2029) GetVersion ($202A) GetFSTInfo ($202B) DInfo ($202C) DStatus ($202D) DControl ($202E) DRead ($202F) DWrite ($2030) Sets the value of the EOF (end-of-file) pointer Returns the value of the EOF (end-of-file) pointer Sets the value of the system file level Returns the current value of the system file level Returns information about the file entries in a directory Begins a write-deferral session Ends a write-deferral session Returns write-deferral session status Returns the device number for a given device name Formats a disk and writes out the boot blocks, volume bit map, and an empty root directory Writes out the boot blocks, volume bit map, and an empty root directory to a disk Resizes the disk cache to the size stored in Battery RAM Returns the name of the application that is currently running Returns the name of the disk GS/OS was booted from; (this is the name assigned to the boot prefix, */) Transfers control to another system program, usually through a dispatcher program Returns the GS/OS version number Returns information about a file system translator Returns the device name corresponding to a given device number Returns the status of a device Sends control commands to a device Reads data from a device Writes data to a device 78 GS/OS and ProDOS 8 Commands Table 4-2 Continued Command Name (number) Function BindInt ($2031) UnbindInt ($2032) FSTSpecific ($2033) Installs an interrupt-handling subroutine Removes an interrupt-handling subroutine Sends FST-specific commands to a file system translator The exact structure of the parameter table varies from command to command, but it always begins with a parameter count word called pcount. Generally, each GS/OS com- mand allows a range of values for pcount, giving the application the choice of just how much information it wants to provide to the command and just how much it wants returned. The minimum and maximum pcount values for each GS/OS command are in the descriptions of the command table parameters, which we present later in this chapter. When a command finishes, GS/OS adds 6 to the return address pushed on the stack by the JSL instruction and then ends with an RTL instruction. This causes control to pass to the code beginning just after the pointer to the parameter table. On return, all registers remain unchanged except the accumulator (which contains an error code), the program counter (of course), and the status register. (The m, x, D, I, and e flags are unchanged; N and V are undefined; the carry flag and zero flag reflect the error status.) At this stage, you can check the state of the carry flag to determine whether an error occurred: If the carry flag is clear, there was no error; if it is not clear, an error did occur. Alternatively, you can check the zero flag; if an error occurred, it will be clear. An error code indicating the nature of the error comes back in the accumulator; the accumulator will contain 0 if no error occurred. We describe GS/OS and ProDOS 8 error codes in detail in the next section. The Apple Programmer's Workshop (APW) comes with a set of macros you can use to make it easier to call GS/OS commands. The macros are stored in a file called M16.GSOS on the APW disk. To use a GS/OS command with a macro, use an instruction of the form: _CmdName ParmTbl where CmdName represents the name of the command and ParmTbl represents the address of the parameter table associated with the command. At assembly time, this macro expands into the standard GS/OS calling sequence. Note: All the macros for GS/OS commands in the M16.GSOS file have names that include a GS suffix. The macro for the Open command, for example, is called OpenGS. The reason for using the suffix is to ensure that the GS/OS macro names Using GS/OS Commands 79 are different from their ProDOS 16 counterparts, making it possible to develop programs that use both GS/OS and ProDOS 16 commands. Since it's unlikely you'd ever want to mix commands, consider editing the M16.GSOS file to remove the suffixes. That way you won't have to worry about forgetting to include the suffix. The GS/OS command names used in this book do not include the GS suffix. The main advantage of using the macros is you do not have to memorize command numbers, only command names. It also makes assembly-language programs that use GS/OS much easier to read. Stack-Based Calling Method You can also call a GS/OS command using a stack-based command interpreter entry point at $E 100B0. Here is what such a call looks like: PushPtr ParmTbl PushWord #CommandNum JSL $E100B0 ;Push addr of parameter table Push GS/OS command number Call stack-based entry point To use this method, first push the 4-byte address of the command's parameter table and a 2-byte command number, and then perform a JSL $E100B0 instruction. PushPtr and PushWord are standard APW macros for doing this. GS/OS AND PRODOS 8 ERROR HANDLING Any error that is not a critical error is called a system error. These errors can result for many reasons: specifying an illegal pathname, writing to a write-protected disk, opening a nonexistent file, and so on. If no system error occurred during execution of a command, the accumulator is 0, the carry flag is clear (0), and the zero flag is set (1). If an error did occur, the accumulator holds the error code number, the carry flag is set (1), and the zero flag is clear (0). This means you can use a BCS or a BNE instruction to branch to the error-handling portion of your code. You should always check for error conditions when a ProDOS 8 or GS/OS com- mand ends. If you don't, you will undoubtedly have a program that won't always work properly. (For example, think of the consequences of writing to a file that could not be opened because it did not exist.) For debugging, it is often handy to have a special subroutine available that the application can call to print out helpful status information when an error occurs. Table 4-3 shows such a subroutine for ProDOS 8. When an application calls it, the message MLI ERROR $xx OCCURRED AT LOCATION $yyyy 80 GS/OS and ProDOS 8 Commands