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