💾 Archived View for mirrors.apple2.org.za › archive › apple.cabi.net › System › GSOS.P8.Anatomy › DI… captured on 2024-02-05 at 14:49:07.

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

Disk Devices

Low-level communication with a block-structured data storage device like a 3.5-inch
disk drive or a hard disk is managed by an assembly-language subroutine called a disk
driver. (This name is conventional only - a disk driver may actually communicate with
a block storage device that is not a disk drive.) We say "low level" because the disk
driver is the subroutine every operating system command eventually calls to access
the disk, and it is the disk driver that directly manipulates the 1/0 locations that
control the operation of the drive.

The important tasks conventional disk drivers perform are

z Moving the disk's read/write head over any track on the disk
z Identifying data blocks within each track
z Reading and writing data blocks

z Reading the write-protect (or other) status of the disk
z Formatting the disk

The driver for a 5.25-inch drive performs these tasks using several disk 1/0 locations
for controlling the disk stepper motor, storing a byte on a disk, reading a byte from a
disk, and sensing the write-protect status of the disk.

Under ProDOS 8, it is relatively easy to add a custom disk driver (such as one for
controlling a RAMdisk) to the system - it's just a matter of changing a few bytes in the
system global page to tell ProDOS 8 where you've loaded the driver and what slot and
drive numbers you want to assign to it. The only difficult part is deciding where to put
the driver so that it won't be overwritten by applications.
GS/OS has a more formal mechanism for adding disk drivers, but we do not discuss
them here; GS/OS comes with drivers for all the disk drivers you're ever likely to
need. If you do need to know about how to write CS/OS disk drivers, refer to GS/OS
Reference, Volume 2.

In this chapter, we investigate just how GS/OS and ProDOS 8 determine what disk
devices are available and how they keep track of the disk drivers associated with each

287

of these devices. We also review the general characteristics of a ProDOS 8 disk
and learn how to write one from scratch.

HOW GS/OS AND PRODOS 8 KEEP TRACK OF DISK DEVICES
When GS/OS and ProDOS 8 boot up, one of the first things they do is -
many disk devices are connected to the system and how they may be ac
example, through a card in a slot or a RAM-based driver. (GS/OS also
character devices.) We see how these operating systems identify disk devices
next section.

GS/OS Device Scan

When you boot GS/OS, it scans the IIcs system looking for both block-structure
devices and character devices. When it identifies a device for which a driver
the SYSTEM/DRIVERS/ subdirectory on the boot disk, it loads the driver
memory and installs it. Apple currently provides drivers for 3.5- and 5.25-inch
drives, SCSI drives, and the console (the standard keyboard input and
output system). If no driver for the device exists on disk, CS/OS tries to generate
in memory on the fly; it can generate character drivers for printer and modem
and disk drivers for most SmartPort devices. The disk devices CS/OS cannot
driver for are the 5.25-inch disk drive and the HD20SC SCSI hard disk.

GS/OS assigns a unique device number and device name to each device it finds
the system. It assigns device numbers consecutively, beginning with 1, and the
names begin with a period and can be up to 31 characters long (for example, .,r
and .APPLEDISK3.5). Unlike ProDOS 8, CS/OS does not use unit numbers
are derived from slot and drive numbers) to identifl' disk devices. You can use
GS/OS DInfo command to determine the names of all the devices in the system.

ProDOS 8 Device Scan

Table 7-1 lists all the system global page locations ProDOS 8 uses to manage
devices.

ProDOS 8 stores the number of active disk devices, less 1, in DEVCNT ($11F31)z
the system global page. It stores the physical locations of the disk devices (that is,
their slot and drive numbers) in encoded form in a 14-byte table beginning at
DEVLST ($BF32). As Figure 7-1 shows, the high-order 4 bits of each entry in this
table hold the drive and slot number in packed form, and the low-order 4 bits hold ani
identification code unique to the type of disk device installed (5.25-inch drive, 3.S
inch drive, HD20SC hard disk, and so on).

You can also use the ProDOS 8 ON - LINE command to determine the slot and
drive locations of all the disk drives in the system.

288 Disk Devices

Table 7-1 ProDOS 8 global page areas used for disk drive identification
Address Symbolic Name Description
$BF10 DEVADR01 "No device connected" address
$BF12 DEVADR11 Slot 1, drive 1 driver address
$BF14 DEVADR21 Slot 2, drive 1 driver address
$8F16 DEVADR31 Slot 3, drive 1 driver address
$BF18 DEVADR41 Slot 4, drive 1 driver address
$BF1A DEVADRS1 Slot 5, drive 1 driver address
$BF1C DEVADR61 Slot 6, drive 1 driver address
$BF1E DEVADR71 Slot 7, drive 1 driver address
$8F20 DEVADR02 "No device connected" address
$BF22 DEVADRI2 Slot 1, drive 2 driver address
$8F24 DEVADR22 Slot 2, drive 2 driver address
$BF26 DEVADR32 Slot 3, drive 2 driver address
$BF28 DEVADR42 Slot 4, drive 2 driver address
$BF2A DEVADR52 Slot 5, drive 2 driver address
$BF2C DEVADR62 Slot 6, drive 2 driver address
$8F2E DEVADR72 Slot 7, drive 2 driver address

$8F30 DEVNUM Device code for the last device accessed
$8F31 DEVCNT Number of active devices minus 1
$8F32 DEVLST Table of active disk device codes (14 entries in table)
NOTE: The format of the entries in DEVLST and DEVNUM is the same as shown in Figure 7-1, except
that the low-order 4 bits of DEVNUM are always 0.

Figure 7-1 The format of DEVLST ($BF32) table entries
Each byte in the 14-byte DEVLST table holds the slot, drive, and disk identification
number in a special packed format:
7 6 5 4 3 2 1 0
DR SLOT DISK_ID
where DR = 0 for a drive 1 device

= 1 for a drive 2 device

SLOT = 1-7 (slot number for the device)
DISK - ID = $0 for a 5.25-inch disk drive

= $B for a 3.5-inch disk drive
= $F for the [RAM device

= the high-order 4 bits stored at $CnFE if a disk controller adhering to
the extended protocol is being used

NOTE: The /RAM device is logically equivalent to a slot 3, drive 2 disk drive. Its DEVLST entry is $BF.

How GS/OS and ProDOS 8 Keep Track of Disk Devices 289

Suppose you are using a two-drive Apple lIe with an extended 80-column teM
installed in the auxiliary slot and a disk controller card installed in slot 6. Pr"""
sets up DEVCNT and DEVLST as follows:

DEVCNT ($8F31)
DEVLST ($BF32)

$02 <--- three devices
$E0 slot 6, drive 2
$60 <--- slot 6, drive 1
$BF <--- slot 3, drive 2 (IRAN)
$00

11 zero entries
$00

ProDOS 8 reserves a 32-byte area beginning at $BF10 for use as a disk driver
table. This table holds the addresses of the disk driver to be used for each of the
possible slot and drive combinations and 2 impossible ones (slot 0, drive 1 and '
drive 2). The first part of the table, from $BI'10 to $BF1F, holds the addresses for
eight drive 1 devices in ascending slot order (~7); the second part holds
information for the eight drive 2 devices.

Since a disk controller card cannot reside in slot 0 (a slot that doesn't even exist

the Apple lIe, IIc, or 1Ics), ProDOS 8 uses the two slot 0 entries in the disk
vector table for a special purpose: to hold the address of the subroutine that
MLI error $28 if ProDOS 8 calls it. This is the code for the "no device connec
error. If the vector table entry for a given slot and drive combination is this at'
ProDOS 8 has not assigned a disk device to that slot and drive.

The six most common entries in the disk driver vector table are as follows:

$D000 disk driver for a standard 5.25-inch disk drive (in bank-switched RAM
$FF00 disk driver for the [RAM RAMdisk volume (in bank-switched I'lAfl)
$DEAC address of "no device connected" error subroutine (in bank-swltch~
RAM)

$Cn0A UniDisk 3.5 and Apple IIcs SmartPort (n = slot number of the
controller card)

$Cn4E Apple II Memory Expansion card (n = slot number of the memory
card)

The first three addresses are those used by ProDOS 8 version 1.7 only. (The others are
fixed in ROM on firmware or controller cards.) They may change when Apple releases
later versions of ProDOS 8.

290 Disk DevicesHOW GS/OS AND PRODOS 8 IDENTI~ DISK DEVICES
To connect a disk device to an Apple II, you generally attach it to a disk controller
card located in a peripheral expansion slot. (The Ilc and Ilcs both have built-in disk
controllers, so no card is necessary.) This card is responsible for booting the disk and,
in some cases, for transferring data between the Apple and the disk medium.
A controller card holds a program in ROM that occupies the address space from
$Cn00 to ~CnFF (where n is the slot number) and, sometimes, from $C800 to $CFFF.
For standard 5.25-inch disk controllers, this program is capable of only transferring a
short loader program from the disk medium into 11AM and executing it; this loader
then reads in the rest of the disk operating system from disk. (This is where the term
booting comes from: The operating system picks itself up by its own bootstraps.)

Other controllers may contain code that performs much more sophisticated tasks,
such as reading or writing any block on the disk, doing status checks, and formatting a
disk. Intelligent controllers with these capabilities are used with 3.5-inch disks and
hard disks. Apple currently uses an intelligent controller called a SmartPort for
3.5-inch drives and RAMdisk memory cards. (A SmartPort is built in to the II&s and
newer models of the Ilc.)

When ProDOS 8 or CS/OS first starts up, it examines each slot (beginning with 7
and working down to 1) to determine whether a controller card for a disklike device is
present. A controller card contains the following unique pattern of bytes in its ROM (n
is the slot number):

$Cn0l $20
$Cn03 $00
$Cnos $03

The value of the byte stored at $Cn07 is also important. If the three identification
bytes are present and location $Cn07 contains $3C, and if the controller is in a
higher-numbered slot than any other disk controller, the original Apple II system
Monitor program in ROM (the one in the II Plus or the original lIe) automatically
boots the disk in the drive when you turn the system on. Unfortunately, $Cn07 cannot
contain $3C in the ROM of a controller for a disk device other than a 5.25-inch disk
drive because the Apple Pascal operating system erroneously believes any such device
is a 5.25-inch disk drive. As a result, it is not possible to automatically boot from a hard
disk or a 3.5-inch disk when using a system with the original Monitor program.

You can automatically boot a non-5.25-inch disk device'if you have an Apple Ilcs

or an enhanced Apple lIe. This is because the system fi1onitor in these computers
identifies a bootable disk drive by the presence of the first 3 identification bytes only.

If you want to know if the disk controller is a SmartPort (perhaps so that you can
take advantage of the special SmartPort commands described later in this chapter),
check location $Cn07. If it contains $00, it is a SmartPort.

How GS/OS and ProDOS 8 Identijy Disk Devices 291

When ProDOS 8 or CS/OS finds the 3 identification bytes, it looks at the
stored at $CnFF to determine the exact type of controller it has found. If
contains $00, ProDOS 8 and CS/OS consider the card a 5.25-inch disk controller
standard 16-sector-per-track ROMs. In this case, ProDOS 8 places the a
device code in the DEVLST table and the address of the internal 5.25-inch
device driver in the ProDOS 8 disk driver vector table. Note that it actually
two entries in each tahle since each 5.25-inch disk controller can have two drives
volumes) attached to it. (They are referred to as drive 1 and drive 2.) The disk
itself ultimately determines if there is actually a drive 2 device attached and
"device not connected" error code if an attempt is made to access it and it is not

If $CnFF contains $FF, CS/OS and ProDOS 8 consider the card a 5.25-inch
controller with 13-sector-per-track ItOMs. (This was the disk formatting scheme-'-
by Apple's original 5.25-inch drive controller.) CS/OS and ProDOS 8 do not
this type of controller card and so ignore it.

If $CnFF contains any other value, GS/OS and ProDOS 8 assume the
controller has a device driver entry point located in ROM at $CnXX, where XX is
value stored at $CnFF. If bits 0 and 1 of the byte stored at $CnFE are both I
describe the meaning of these bits in the next section), ProDOS 8 stores this
in the device driver vector table and adds an appropriate device code to DEVL
(The low-order 4 bits of the DEVLST entry are set equal to the high-order 4 bits
the byte at $CnFE.) If one, or both, of bits 0 and 1 of $CnFE are 0, CS/OS
ProDOS 8 ignore the disk controller.

ProDOS 8 identifies three special "disk" devices in quite a different way. If it
running on an Apple lIe with an extended 80-column card (the one with 64K
auxiliary 11AM on it), or on an Apple IIc or IIcs, ProDOS 8 installs a special device,
called a 11AMdisk, as the slot 3, drive 2 disk device. The medium for this disk is the
64K auxiliary memory space on the lIe, IIc, or IIcs, and disk 1/0 operations simply
involve the movement of data blocks between auxiliary and main memory. The
volume name for this 11AMdisk is always 111AM.

GS/OS and ProDOS 8 create another type of 11AMdisk using memory on the Apple
IIcs Memory Expansion card (or equivalent) if the Control Panel Minimum 11AM
Disk Size parameter is not set to zero. This 11AMdisk is called 111AM5. The third
special device, again available on the IIcs only, is a ROMdisk. Although Apple's
memory card doesn't support ROMdisk memory, several independent suppliers have
cards that do. Despite the name ROMdisk, the memory for the disk could also be in
battery backed-up static or dynamic 11AM, EEPROM, or EPItOM.

EXTENDED PROTOCOL FOR DISK CONTROLLER CARDS
Apple has also defined a special extended controller card ROM protocol that manu-
facturers of disk devices and disk controller cards must adhere to if their devices are to
work properly with CS/OS and ProDOS 8. (The 5.25-inch disk controllers do not
actually follow this protocol and are handled as special cases by GS/OS and ProDOS

292 Disk Devices

8.) This protocol defines the use of 4 bytes in the controller card ROM space as follows
(n is the slot number of the card):
z $CnFC and $CnFD. The total number of blocks on the volume is stored here
(low-order byte first). This information is for the benefit of formatting programs
that also initialize the volume directory and volume bit map on disk. The
controller for the old 5-megabyte ProFile hard disk has the number $2600
(9728) stored here. If the number is $0000 (as it is for most controller cards),
you must send a status request to the disk driver to determine the volume size;
the number of blocks comes back in the X register (low) and Y register (high).
We see how to make status requests in the next section.

z $CnFE. This is the device characteristics byte. Each bit holds miscellaneous
information about the device:

bit 7 1 = the disk medium is removable
bit 6 1 = the device is interruptible
bits 5,4 The number of drives (or volumes) on the

device (0-3). An even value (0 or 2)
indicates one drive; an odd value (1 or 3)
indicates two drives.

bit 3 1 = the device driver supports format
bit 2 1 = the device driver supports write
bit 1 1 = the device driver supports read
bit 0 1 = the device driver supports status

The controller for the UniDisk 3.5 has the value $BF stored at $CnFE. This means
the disk medium is removable (bit 7 = 1); the UniDisk 3.5 is not interruptible (bit
6 = 0); two volumes are supported (bits 5,4 = 11); and the device driver for the
UniDisk 3.5, located in ROM on the controller card, supports format (bit 3 = 1),
write (bit 2 = 1), read (bit 1 = 1), and status (bit 0 = 1) operations.

z $CnFF. This byte contains the offset (from $Cn00) of the address of the ProDOS 8
disk driver for this device. If the byte at $CnFE indicates that the device can be
read from and its status can be read (that is, bits 0 and 1 of the byte stored at
$CnFE are both 1), the driver address is stored in the "drive 1" portion of the
device driver vector table in the ProDOS 8 global page when ProDOS 8 is first
booted. If the byte at $CnFE indicates that two drives are attached to the
controller, the address of the device driver is also stored in the "drive 2" portion of
the table unless ProDOS 8 is able to determine that a second drive is not actually
connected. After the vector table is updated, bits ~7 of the byte stored at $CnFE
are stored in the low-order 4 bits of the DEVLST entry for the device.

The controller for the UniDisk 3.5 has the value $0A stored at $CnFF, and its
DEVLST entry is of the form nB, where n is the controller slot number. This
means the address of the disk driver is $Cn0A.

Extended Protocol for Disk Controller Cards 293

Special Cases

$CnFF contains $00 for a 16-sector 5.25-inch disk controller and $FF for a 1
5.25-inch disk controller. In these situations, GS/OS and ProDOS 8 ..
special meaning to the values stored at $CnFC, $CnFD, and $CnFE.

If ProDOS 8 finds a 16-sector controller, it assumes the disk medium is a
volume of 280 blocks and uses its own internal disk driver to communicate -
CS/OS uses a similar driver it loads from the SYSTEM/DRIVERS/ su..
CS/OS and ProDOS 8 ignore the older 13-sectorS.2S-inch disk controller.

COMMUNICATING WITH A PRODOS 8 DISK DRIVER
Just before ProDOS 8 calls a disk driver subroutine, it sets up four parameters th
microprocessor's page zero area that serve to inform the disk driver of the
operation to be performed. These parameters define the tye of disk operation
write, format, or check device status), the slot and drive number of the disk device,
address of the 512-byte (one block) data transfer buffer to be used, and the block

The four parameters are stored in locations $42 to $47 and have the loll
meanings:

z COMMAND ($42). This location holds the command code for the disk
to be performed. Four codes are defined:

0 Check device status. On return, the carry flag is
clear and the accumulator is zero if the device is
ready to accept read and write commands. Moreover,
the number of blocks on the disk is in the X
register (low) and Y register (high) but only if
the device's controller ROM adheres to the

1
2
3

extended ProDOS 8 protocol (remember that
5.25-inch disk controllers do not). If the device
is not ready to accept read and write commands,
the carry flag is set, and the accumulator
contains an MLI error code. The standard drivers
for 3.5- and 5.25-inch drives return an error code
on a status request if the disk medium is
write-protected (error $28) or no disk is in the
drive (error $2F).

Read one block from the disk.
Write one block to the disk.

Format the disk. When you format a disk, special
address marks are set up to allow each sector to
be identified by the disk driver. Generally,
the formatting process does not also set up the
boot record, volume directory, and bit map blocks;
this must be done by making write requests. (The
driver for /RAM is an exception.) The format
request is actually not supported by the standard

294 Disk Devices

5.25-inch device driver because of space
limitations; instead, a separate utility program
(such as Filer on the ProDOS 8 master disk) must
be used to format a diskette or hard disk and to
lay out the boot record, volume directory, and bit
map. The source code for the standard diskette
formatting subroutines (called F0RMATTER) can also
be licensed from Apple for use in other formatting
programs. The format request is supported by the
/RAM driver and the 3.5-inch disk driver.

z SLOT-DRIVE ($43). These locations hold the drive and slot numbers of the
disk device to be accessed, in the following format:

bit 7

bits 4,5,6
bits 0,1,2,3

0 (drive 1) or 1 (drive 2)
slot number (1-7)
always 0

For example, a slot 6, drive 2 device would be represented as 11100000 ($E0).

z BUFFER - I11'R ($4~$45). These locations hold the address (low-order byte first)
of the start of a 512-byte area of memory that holds the image of the block to be
written to the disk (COMMAND = 2) or that will hold the block read from the disk
(COMMAND = 1). BUFFER PTR should also be properly set up before malcing a
format request (COMMAND = 3) because the formatting subroutines for some disk
devices (like /11AM) may use the buffer area for temporary data storage.
BLOCK - NUM ($4~$47). These locations hold the number (low-order byte first)
of the block on the disk to be written to (COMMAND=2) or read from
(COMMAND = 1).

The disk driver performs the 1/0 operation dictated by these parameters and then
returns control to the caller. If no error occurred, the carry flag is clear, and the
accumulator is zero.

Errors can occur, of course, when ProDOS 8 communicates with a disk device. The
disk drivers flag error conditions in the standard MLI way: by setting the carry flag
and placing an appropriate MLI error code in the accumulator. Table 7-2 shows the
error codes and conditions supported by the ProDOS 8 disk driver for standard
5.25-inch disk drives. Any other properly implemented disk driver will identify and
report these error conditions in the same way.

THE SMARTPORT CONTROLLER

A SmartPort is the intelligent device controller Apple now uses to interface to all its
high-capacity disk drives, including the UniDisk 3.5, Apple 3.5 Drive, and HD20SC
SCSI hard disk. The SmartPort firmware can handle up to 127 devices chained

The SmartPort Controller 295

Table 7-2 ProDOS 8 disk driver error codes

$28
$2B
$2F

No disk device is connected
The medium is write-protected
The device is off-line

together to the same SmartPort, but the Apple power supply gives out well
then - for the SmartPort on the II&s, for example, Apple recommends c'
more than four 3.5-inch drives.

As we mentioned earlier in this chapter, the SmartPort firmware has the same
basic identification bytes as any other ProDOS-compatible disk controller. A
location $Cn07 serves to uniquely identify the controller as a SmartPort, however,
SmartPort ID type byte at $CnFB gives you a little more information about
SmartPort:

bit 0 1 = supports RAMdisk card
bit 1 1 = supports SCSI devices

bit 2 [reserved]
bit 3 [reserved]
bit 4 [reserved]
bitS [reserved]
bit 6 [reserved]

bit 7 1 = supports extended commands

The SmartPort assigns a unique unit number (from $01 to $7F) to each L..
connected to it. The numbers it assigns are consecutive, starting with $01.
SmartPort controller itself is unit number $00.) Programs use the unit number
identify the device a SmartPort command is directed to.

In general, the SmartPort assigns unit numbers to devices in the order they

in the chain of devices. But on the II&s, the SmartPort considers any BOMdisk
/11AM5 11AMdisk (the 11AMdisk you set up with the Control Panel) in the system to L
part of the SmartPort chain and assigns unit numbers to them first. To complic
matters further, if the startup device (set using the Control Panel) is a SmartPort
device, the SmartPort rearranges unit numbers to ensure the startup device has a unit
number of $01. The only safe way to determine which device corresponds to a given
unit nutnber is to use the SmartPort's Status command (see below).

Many ProDOS 8 commands use slot and drive parameters to identify a disk device,

so ProDOS 8 automatically assigns slot and drive combinations to SmartPort unit
numbers when it first boots up. Assuming the SmartPort is in slot 5, ProDOS 8 assigns

296 Disk Devicesthe first four SmartPort devices to slot 5, drive 1; slot 5, drive 2; slot 2, drive 1; and
slot 2, drive 2. It ignores any other devices that may be connected to the SmartPort.
The phantoming of the third and fourth devices to slot 2 is necessary because ProDOS
8 has space for only two drives per slot in its disk driver vector table.

Using SmartPort Commands

The SmartPort firmware provides several commands a program can use to communi-
cate with a disk device. Under ProDOS 8, you won't have to use them for common
types of disk operations because you can use the disk driver commands described in
the previous section instead. Under GS/OS, you can probably get by with the DInfo,
DRead, DWrite, DStatus, and DControl commands. You will have to use SmartPort
commands to obtain extended status information and to perform special control
operations, however.

To use a SmartPort command, you must first determine the dispatch address of the
command interpreter. This address is always 3 bytes past the standard ProDOS 8
device driver entry point, so its offset into page $Cn00 is the value stored at $CnFF
plus 3.

You call a standard SmartPort command much as you call a ProDOS 8 MLI
command:

JSR DISPATCH DISPATCH = $Cn00+($CnFF)+3
DFB CMONUM ;SmartPort command number
DA PARM_BLK ;Pointer to Smart Port parameters
8CS ERROR ;Carry set if error occurred

where DISPATCH is the SmartPort dispatch address, CMDNUM is the SmartPort
command number, and PARM - BLK is a command-specific parameter block. (If
CS/OS is active on a IIcs, you must call the SmartPort dispatcher in emulation mode
with code that resides in bank $00.) If an error occurs, the carry flag is set, and the
accumulator contains the error code. If the operation was successful, the carry flag is
clear, and the accumulator is zero.

If bit 7 of the SmartPort ID type byte at $CnFB is 1 (and it is for the IIcs
SmartPort), the SmartPort also supports extended SmartPort commands. The com-
mand number for an extended command is the same as the number for the corre-
sponding standard command except that bit 6 is set to 1. That means, for example, if
the standard command number is $01, the extended command number is $41.

You call extended commands just like standard commands except that the pointer

to the parameter block contains a long address (4 bytes) rather than a short address (2
bytes). This permits access to a parameter block located anywhere in the IIcs's 16Mb
memory space. The other difference between a standard and extended command is
the strncture of the parameter block for the command, as we see below.

The SmartPort Controller 297

Important: The IIcs SmartPort clobbers several locations in the caller's
direct page (IIcs ROM version 01) or true zero page (original II&s ROM)
you call a SmartPort command. The affected locations are $57 through
these locations are important to your application, save them before a
call and restore them afterward.

All SmartPorts support a standard set of commands so that ProDOS 8 or GS/O5
communicate with it properly. The ones you probably will never use in an I:
are ReadBlock, WriteBlock, Format, and Init (you can use ProDOS 8 disk dri
GS/OS commands instead) as well as Open, Close, Read, and Write (appropriate
character devices only). Let's now take a close look at the two remaining
Status and Control.

Status Command

The Status command is for determining the status of any device in the
chain or the SmartPort controller itself. Its command number is $00 (standard) or
(extended), and the standard parameter block looks like this:

parameter count (byte, always $03)
unit number (byte, from $00 to $7E)
status list pointer (low byte)
status list pointer (high byte)
status code (byte, from $00 to $FF)

The extended parameter block uses a 4-byte pointer to the status list instead (low-ord~
bytes first). You must reserve space for the status list before calling the Status command

There are four possible values for the status code byte:

$00 return device status
"'I, ',':+',,',7, ~~, ~.trn'I. h'l.nck.
$02 return newline status
$03 return device information block

Of these, you probably won't use code $01 or $02 very often. Code $01 returns a
device.dependent control block, up to 256 bytes long, preceded by a length byte; a
length byte of $00 means the block is 256 bytes long. Code $02 is for character
devices only.

Code $00 (return device status) returns 4 or 5 bffes in the status list depend-
ing on whether a standard or extended call is made. The first bffe is a general device
status byte:

298 Disk Devices

bit 0 1 = disk switched (block device only) or

1 = device is open (character device only)
bit 1 1 = device is interrupting

bit 2 1 = medium is write-protected (block device only)
bit 3 1 = device allows formatting
bit 4 1 = a disk is in the drive
bit 5 1 = device allows reading
bit 6 1 = device allows writing
bit 7 1 = block device

0 = character device

Note that the disk-switched bit is 1 if a disk has been ejected and another disk
(perhaps the same one) has been inserted since the last status check. But this bit is
significant only if the device supports disk-switched errors; it does if bit 6 of the
subtype byte returned by the code $03 status command is 1 (see below). Of Apple's
SmartPort devices, only the Apple 3.5 Drive for the IIcs supports these types of
errors. (The UniDisk 3.5 does not.)

The next 3 bytes (standard call) or 4 bytes (extended call) hold the size of the
device in blocks. These bytes are zero if the device is a character device.

The SmartPort handles a Status call differently if the unit number is $00. In this case,

it returns an 8-byte status list describing the status of the SmartPort controller itself:

byte 0 number of devices the SmartPort controls
byte 1 interrupt status (no interrupt if bit 6 is set)
byte 2 manufacturer of driver:

$00 = unknown
$01 = Apple

$02 = third-party driver

Bytes 3 through 7 are reserved.

Code $03 (return device information block) returns more detailed status informa-
tion in the status list. The form of the list after a standard call is as follows:
device status (byte)
block size (low byte)
block size (medium byte)
block size (high byte)

ID string length (byte)
ID string (16 bytes)
device type (byte)
device subtype (byte)
version (2 bytes)

The SmartPort Controller 299

For an extended call, the block size field occupies 4 bytes instead of 3.

The device status and block size bytes are the same as those returned by a
code $00 call. The ID string is a sequence of up to 16 standard ASCII cL
high-order bit of each character is 0) representing the name of the <'
16-character string space is padded with spaces if necessary.

The device type byte tells you the general nature of the device you're dealing
The currently defined values are as follows:
$00 Memory expansion card RAMdisk
$01 3.5-inch disk drive
$02 ProFile-type hard disk
$03 Generic SCSI hard disk
$04 R0Mdisk
$05 SCSI CD-R0M

$06 SCSI tape or other SCSI sequential device
SCSI hard disk
[reserved]
SCSI printer

5.25-inch disk drive
[reserved]
[reserved]
Printer
Clock
Modem

$07
$08
$09
$0A
$0B
$0C
$00
$0E
$0F

The subtype byte indicates some of the characteristics of the device:
bit 7 1 = supports extended SmartPort commands
bit 6 1 = supports disk-switched errors
bit 5 1 = nonremovable medium

The other 5 bits are reserved.

Version is a word (low-order byte first) describing the version number of
SmartPort device driver.

Control Command

The Control command sends control information to a device. Its command number
$04 (standard) or $44 (extended), and the standard parameter block looks like this:
parameter count (byte, always $03)
unit number (byte, from $00 to $7E)
control list pointer (low byte)
control list pointer (high byte)
control code (byte, from $00 to $FF)

300 Disk Devices

The extended parameter block uses a 4-byte pointer to the control list instead
(low-order bytes first).

The Control command understands five general control codes, only one of which
(eject medium) is particularly useful to most applications: $00 (device reset), $01 (set
device control block), $02 (set newline status), $03 (service device interrupt), and $04
(eject medium). Device-specific control codes are numbered $05 and above.

The most useful control code for most applications is the one that causes a 3.5-inch
disk to eject automatically. For the UniDisk 3.5 SmartPort card and the internal IIcs
SmartPort, the eject control code is $04, and the control list contains two $00 bytes.
(For a summary of other device-specific control codes, see Chapter 7 of Apple tics
Firmware Reference.)

Remember to use the eject command with 3.5-inch drives only. You can easily check
whether you're dealing with a 3.5-inch drive by using the Status command. If you are, the
device type byte is $01, the block size is $000640, and the ID string is DISK 3.5.

If you need to be convinced to do a Status check first, keep in mind that revisions

A and B of the SCSI card (a SmartPort device) for Apple's HD205C hard disk use a
control code of $04 to format the disk! That's not an operation you want to perform
accidentally. (For revision C of the SCSI card, the $04 code is the eject code.)

THE PRODOS 8 RAMDISK: THE /RAM VOLUME

We saw earlier that ProDOS 8 automatically installs a special 11AMdisk driver if you
are using an Apple IIcs, Apple IIc, or Apple lIe with an extended 8O-column text card
and creates a special volume called /RAM. (Apple II Plus users are out of luck.) All
these systems have 64K of auxiliary memory that maps to addresses in exactly the
same way as the standard 64K of main 11AM memory usually used for program and
data storage. In this auxiliary memory, the 11AMdisk driver stores the volume direc-
tory, volume bit map, and file blocks. Figure 7-2 shows a map of the usage of auxiliary
memory by /RAM.

Since no slow-moving mechanical parts are used to perform "disk" operations (all 1/0
operations simply involve block moves from one part of memory to another), the 11AMdisk
responds much more quickly than a conventional disk drive. But its contents are tempo-
rary, so you must be careful to transfer any files from it to a permanent disk medium
before turning off the Apple or rebooting ProDOS 8, or you will lose all of your data.

Characteristics of the /RAM Volume

When ProDOS 8 initializes the /11AM volume, it allocates only one volume directory
block (block 2; recall that standard disks use four directory blocks). This means there is
room for only 12 entries in the volume directory, not the usual 51. If files are created
inside subdirectories, however, you can store as many files as will fit on the volume.

When ProDOS 8 first initializes the /11AM volume, 119 blocks are available for file
storage. (They are numbered from 8 to 126.) Since a 64K space is normally capable of
holding 128 512-byte blocks, you might be wondering about the "missing" 9 blocks.

The Pro DOS 8 RAI$tdisk: The 111AM Volume 301

Figure 7-2 A map of auxiliary memory usage on the Apple lIe, lIe, and IIcs with

ProDOS 8 active

Auxiliary

bank-switched

RAM $EO00
$DO00
~BFrF

Auxiliary
memory

$Ocoo
0 00
0 00

-0400
0200
0100
0000

Bank

vectors

$FEO0..$FFF9 not used

Bank2

/RAM block storage area

(blocks 2,3,8.. I 26)

Unused but reserved
On llc only:

$800..$87F is serial input buffer
$880..$8FF is keyboard buffer
Video RAM (80-column mode)
Part of /RAM device driver

used by /RAM device driver

Two of these are relatively easy to track down: One is used for the volume directory
(block 2) and another for the volume bit map (block 3). There is no room in auxiliary
memory for the other seven blocks (0, 1, ~7, and 127) because space must be
reserved to support the /RAM disk driver itself ($000~$03FF), the 80-column text
screen ($0400-$07FF), the keyboard and serial input buffers on the Apple lIe
($080~$08FF), and the auxiliary memory interrupt vectors ($FFFA-$FFFF). Thus
these seven blocks are marked as "in use" in the 111AM volume bit map.

The areas of auxiliary memory that the 111AM volume or its driver does not use are

as follows:

302 Disk Devices

$0~$3B, $4~$FF
$090~$0BFF
$FE00-$FFF9

Despite the apparent availability of these areas, they should be considered reserved for
future use by later versions of ProDOS 8 and must not be used by nonsystem software.

The first 8K of memory allocated for use by files stored in 1RAM maps to locations
$200~$3FFF in auxiliary memory. This same space is used whenever you activate
page 1 of the double-width high-resolution graphics display mode available on the
IIcs, IIc, or lIe. If you are going to use this graphics mode while 111AM is active, you
must first prevent any meaningful program from being stored at these locations. The
easiest way to do this is to ensure that the first file saved to 111AM is a dummy file
exactly 8K bytes long. You can do this by entering the following command from
Applesoft command mode:

BSAVE /RAM/OUMMY,A$2000,E$3FFF

The second 8K area used to store files in 111AM is mapped to locations $4000-$5FFF,
the same area used as the second page of double-width high-resolution graphics. You
can protect this page by saving another dummy file that is 8K long.

Removing and Reinstalling 111AM

You may want your application to use the auxiliary memory area for purposes other
than as a convenient file-storage device. Other common uses for auxiliary memory are
as a data buffer for a printer spooler or as an input buffer for a communications
program. But before you start overwriting the BAM volume with such data, you must
remove the 111AM volume from the system in an orderly manner. If you don't, the
system could crash when ProDOS 8 tries to interpret what you've written to auxiliary
memory as directory, bit map, or file information.

It's actually quite simple to remove the 111AM device from the system.

1. Examine MACHID ($BF98) to see if you're running in a 128K system. (Bits 4 and
5 of MACHID will both be 1 if you are.) 111AM can exist in only a 128K system.

2. Check that 111AM has not already been removed by locating the $BF device code
(slot 3, drive 2) among the active entries in the DEVLST table. You should also
check for any entry of the form $BX, where X = $3, $7, or $B; by convention,
these slot 3, drive 2 devices, though not equivalent to 111AM, will also use the first
bank of auxiliary memory. (Cards such as RamWorks III and MultiRAM have
several banks of auxiliary memory available.) The actual $BX byte stored in
DE""L8T must be saved if you later want to reinstall the 111AM device.

The ProDOS 8 RAMdisk: The 111AM Volume 303

3. Remove the $BX entry from the DE"'LST table by moving higher-addressed
active entries down one position (starting with the lowest-addressed one).

4. Replace the slot 3, drive 2 entry in the device vector table (at $BF2~$BF27) with
the address stored at the slot 0, drive 1 entry (at $BF1~$BF11). (This will be the
address of the subroutine that generates a "no device connected" error condition.)
The original slot 3, drive 2 entry must be saved if you later want to reinstall the
1RAM device.

5. Decrement DEVCNT ($BF31).

6. Make an ON - LINE call with unit - num set to $B0. This frees up an internal
buffer so that you can have more disk volumes active at once.

After you perform these steps, the 1RAM device disappears from ProDOS 8, and
auxiliary memory can be safely used for other purposes.

When your application ends, it should reinstall 111AM. Do this by performing the
following steps:

1. As a precaution, veri~ that you have not already reinstalled 111AM by checlcing for
a slot 3, drive 2 device code in DEVLST.

2. Restore the original slot 3, drive 2 device vector that you saved before 111AM was
disconnected.

3. Move each active entry in DEVLST to the next higher memory location (starting
with the highest-addressed entry), and then store the 111AM device code (that you
saved before 111AM was disconnected) at the first entry in the list (at $BF32).

4. Increment DEVCNT ($BF31).

5. Initialize the volume directory and volume bit map of the 111AM device by setting
up the disk driver parameters for a format request ($42 = 3, $43 = $B0, $4~
$45 = 512-byte buffer address) and then calling the disk driver. Since the 111AM
device driver resides in bank 1 of bank-switched RAM, you must enable that bank
by reading $C08B twice in succession before making the call. When the call ends,
reenable the Applesoft and motherboard ROMs by reading $C082. Here is a
subroutine that performs all these chores:

LDA #3 Format code
STA $42

LDA #$B0 ;Unit number code
STA $43

LDA $73 ;Set buffer address
STA $44 ; to HIMEM
LDA $74
STA $45

LDA $C08B Read/write enable bank1

304 Disk DevicesLDA $C08B ; (where the driver is)
JSR T0RAM

LDA $C082 ;Reenable Applesoft R0Ms
RTS

T0RAM JMP ($BF26)

;Call the /RAM driver

After you reinstall 111AM like this, it is once again available for use as a file-storage device.
WRITING A PRODOS 8 DISK DRIVER

The best way to learn about disk drivers and how ProDOS 8 installs them is to
actually write one. In this section, we do just that by creating a driver for an 8K
version of 111AM called 1RAM8. It is suitable for use in an Applesoft programming
environment and can be used by all ProDOS 8 users (unlike 111AM, which is not
available to Apple II Plus users). The BAMdisk driver itself resides in page three, and
the "disk" storage space it uses is located from $0800 to $27FF. We ensure that
Applesoft programs do not conflict with the RAMdisk storage space by setting the
Applesoft start-of-program pointer at $67-$68 to $2801 and then initializing the other
Applesoft pointers and data areas by executing a NEW command.

Before we begin to create the disk driver, let's outline the steps to follow to remedy

the Applesoft conflict, bind the driver into ProDOS 8, and then initialize the RAM--
disk. This is really a five-step process.

The first step in the procedure is to adjust the Applesoft pointers so that when you
enter or load BASIC programs, they will not overwrite the 111AM8 volume:

LDA #$01 ;Starting address (low)
STA $67 ;Program pointer (low)
IDA #$28 Starting address (high)
STA $68 ;Program pointer (high)
LDA #0

STA $2800

JSR $0648 ;Applesoft NEW command

(Applesofi insists that the byte preceding the start of the program, $2800, be set to $00.)

Second, a slot and drive number for our new device must be selected. This is most
easily done by examining the DEVLST table to see what combinations are already in
use and picking one that isn't. I,et's assume that slot 3, drive 1 is available.

We then must store $30 in the DEVLST table (this is the code for a slot 3, drive 1
device; see Figure 7-1) and increment DEVCNT. Here's the code to do it:

10A #$30
INC DEVCNT
1DY DEVCNT

STA OEV1ST,Y

;DEV1ST code for slot 3, drive 1
;Adding one device

;DEVCNT now points to next available
position in DEVLST
;Stuff device code in OEY1ST

Writing a Pro DOS 8 Disk Driver 305

The next step is to install the address of the disk driver in the disk driver vector
(low-order byte first). The address of the slot 3, drive 1 entry in this table is
Here s how to store the address:

LOA #<RAMOISK
STA $0F16
IDA #>RAMOISK
STA $8F17

;Get low-order address byte
;Get high-order address byte

BAMDISK is the address of the disk driver that performs the 1/0 operations. (We
what it looks like in a moment.)

Finally, we must initialize the volume directory block and the volume bit map.
before we can do this, we must know three things:

z The number of directory blocks
z The block number of the volume bit map block
z The number of blocks on the volume

Since it's unlikely we'll be saving very many files in the 8K 111AM8 volume, we can
some space by using just one directory block (instead of the four used on standard
This block must be located at block 2 to conform to ProDOS conventions.

The volume bit map block will be stored at block 3, leaving a total of 14 blocks-

for file storage. To keep the file storage area contiguous, we assign these blocks
numbers 4 through 17 and mark blocks 0 and 1 as in use in the volume bit map. (We
can't use block 0 for file storage anyway since ProDOS uses a zero entry in a file index
block as a placeholder for a sparse file.) This means ProDOS will think the volume
size is 18 blocks (instead of 16), but that will not matter since the two extra blocks will
not be available for file storage.
Since a 1 bit in the volume bit map indicates a block is free, the volume bit map
block must begin with a $0F byte (blocks ~ in use, blocks ~7 free), followed by an
$FF byte (blocks 8-15 free) and a $C0 byte (blocks 16 and 17 free). The remaining
bytes in the block will never be used but should be set to zero.

With this background information, it is relatively simple to initialize 111AM8. The
first step is to prepare an image of thc volume directory block and then use the
WRITE - BLOCK command to write it to block 2. (You may want to review Chapter
2 for a description of the structure of such a block.) Every byte in the block will be
zero except the following:
$04 storage type code and name length ($F4)
$O5-$08 ASCII string for "RAM8" ($52 $41 $40 $38)
$22 access code ($C3)
$23 entry length ($27)
$24 entries per block ($00)

306 Disk Devices

$27-$28 block number for volume bit map ($0003)
$29-$2A number of blocks on volume ($0012)

Since the directory links (at $00-$01 and $02-$03 in the block) are both zero, this will
be the only block that ProDOS examines for files in the volume directory.
The final step in the initialization procedure is to write an image of the volume bit
map to block 3.

Now all we have to do is write the special 111AM8 disk driver. Before we begin, we
must decide what memory locations will be used to hold each block in the volume. A
convenient mapping scheme to use is as follows:

block 2 --> $8OO-$9FF
block 3 --> $AOO-$BFF
block 4 --> $COO-$OFF

block 17 --> $26OO-$27FF

(The driver returns an error code if a block number greater than 17 is requested.)
With this scheme in place, the page number for a given block is equal to twice the
block number plus 4. This number can be easily calculated by the driver subroutine.
(To simplily the driver, we also assign block 0 to $40~$5FF and block 1 to
$60~$7FF even though these blocks are never used.)

As we saw earlier in this chapter, when the disk driver takes control, certain parame-
ters are set up in zero page by the calling program. One of these parameters is a command
code that indicates what type of operation is to be performed: read, write, check status, or
format. To save space, our driver won't include the formatting code, so we ignore all
format requests. Status requests will also be ignored because such requests are meaning-
less in the context of a RAMdisk. Here's what the driver will look like:

(required by ProDOS 8)
Save zero page locations

;Check block number (high)
;Error if not zero
Check block number (low)
Is it out of bounds?
It's >=18, so error

Multiply block by 2
.... and add 4 to get
;starting page of block

CLD

LDA $6

STA ZPSAVE
IDA $7

STA ZPSAVE+1

LDA $47

BNE IOERROR
LDA $46
CMP #18

BCS IOERROR

ASL
CLC

ADC #4
STA $7

Writing a ProDOS 8 Disk Driver 307

LDA #0
STA $6

LDA $42 ;Get command code
CMP #3 ;Format?

BEQ EXIT Yes, so exit normally
CMP #0 ;Check status?
BEQ EXIT Yes, so exit normally
CMP #1 ;Read?
BEQ READ Yes, so branch
CMP #2 ;Write?
BEQ WRITE Yes, so branch

EXIT CLC ;CLC ==> no error
LDA #0

EXIT1 PHP
PHA

LDA ZPTEMP ;Restore zero page locations
STA $6

LDA ZPTEMP~1
STA $7

PLA Restore error code
PLP ;Restore carry status
RTS

IOERROR SEC ;SEC ==> error occurred
LDA #$27 1/0 ERROR code

READ
WRITE

ZPTEMP

BNE EXIT1 (always taken)

["read" -,'ubroutine]
JMP EXIT

["write" subroutine]
JMP EXIT

DS 2 ;Temporary storage space

Note that the driver must begin with the CLD instruction that ProDOS 8 checks to s~
a valid driver is installed. The first part of the driver saves the contents of two zero
locations we're going to overwrite and then checks whether the requested block
(stored at $4~$47) is within the allowable range. If it isn't, the driver ends with the
flag set and the error code for "I/O error" ($27) in the accumulator.

The next part simply calculates the address of the requested block and stores it
two consecutive zero page locations ($~$7) so that the driver can access the block
data using the 6502 indirect indexed addressing mode.

The bodies of the READ and WRITE subroutines are both very simple to '+
The READ code is responsible for moving the block of data from the address -

308 Disk Devices

calculated to the address specified by the caller. (This address is stored at $4~$45.)
The WRITE code performs just the opposite transfer. Here are the two subroutines
that will do the trick:

READ LDY #0

RI LDA ($6),Y Get block data
STA ($44),Y ; and move it to caller's buffer
INY

BNE RI Branch until 256 bytes done
INC $6 Move to second half
INC $44

R2 LDA ($6),Y Get block data
STA ($44),Y ; and move it to caller's buffer
INY

BNE R2

WRITE
WI

W2

Branch until 256 bytes done
DEC $44
JMP EXIT

LDY #0

LDA ($44),Y ;Get data from caller's buffer
STA ($6),Y ; and move it to "disk" block
INY

BNE RI Branch until 256 bytes done
INC $44 ;Move to second half
INC $6

LDA ($44),Y ;Get data from caller's buffer
STA ($6),Y ; and move it to "disk" block
INY

BNE R2 Branch until 256 bytes done
DEC $44
JMP EXIT

As you can see, an 1/0 operation is simply the movement of a 512-byte block of data
from one area of memory to another.

Table 7-3 shows the complete source listing for a slightly embellished form of this
driver. One additional feature it includes is the marking of pages 3 and ~7 as "in
use" in the system bit map in the ProDOS 8 global page to prevent the 111AM8
volume from being overwritten. Any attempt to load a file into these areas (using
BLOAD or BRUN) results in a "no buffers available" error.

Use the BRUN command to install the driver program, and then prove to yourself
that it exists by entering the command:
CATALOG /RAM8 (or CATAL0G,S3,DI)

You should see a standard CATALOG listing followed by an indication that there are
14 blocks free and 4 blocks used, as expected. You can now save files to 111AM8 as you
would to any other volume.

Writing a ProDOS 8 Disk Driver 309

Table 7-3 The 111AM8 disk driver program

4
5
6
7
8
9

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33

2000: A0 00 34
2002: 89 F4 20 35
2005: 99 00 03 36
2008: C8 37
2009: CO 7C 38
2008: 00 F5 39

40
41
42
43
44
45
46

2000: AD 58 BF 47
2010: 09 10 48
2012: 80 58 BF 49
2015: A9 FF 50

310 Disk Devices





RAMPTR EQU $6 Pointer to RAMdisk block
COMMAND EQU $42 Command code
BUFFER EQU $44 Buffer address
BLOCK EQU $46 Block number

TXTTAB EQU $67 ;Applesoft program pointer
INITBLK EQU $3000 ;Block buffer
MLI EQU $BF00 ;MLI interface
DEVADR01 EQU $BF10 ;Start of disk driver table
DEVCNT EQU $BF31 ;# of disk devices (minus 1)
DEVLST EQU $BF32 ;Table of slot, drive for disks

BITMAP EQU $BF58
0RG $2000

;Start of system bit map



LDY #0

M0VEC0DE LDA BEGIN,Y
STA RAMDISK,Y
INY

CPY #END-RAMDISK
BNE M0VEC0DE



LDA BITMAP

0RA #$10 Block 3 bit = 1
STA BITMAP
LDA #$FF

Table 7-3 Continued

2017: 80 59 BF 51 STA BITMAP+1 ;Blocks 8. .15
201A: 8D 5A BF 52 STA BITMAP+2 ;Blocks 16. .23
2010: AD 5B BF 53 LDA BITMAP+3

2020: 09 F0 54 0RA #$F0
2022: 80 5B BF 55 STA BITMAP+3

56

2025: AD C7 20 57 LOA SLFAKE
2028:0A 58 ASL
2029:0A 59 ASL
202A: 0A 60 ASL
202B: 0A 61 ASL
202C: AC C8 20 62 LDY OFAKE
202F: CO 01 63 CPY #1
2031: F0 02 64 BEQ SETOS
2033: 09 80 65 ORA #$80
2035: 80 OF 20 66 SETDS STA NEWORSL

67

68 Check for existing device:

;Block 24. .27 bits = 0

;Multiply slot by 16
Drive 1?

;Yes, so branch

;Set bit 7 ("drive 2" bit)

2038: AC 31 BF 69 LOY DEVCNT

203B: B9 32 BF 70 DUPCHECK LOA DEVLST,Y ;Get existing slot, drive
203E: CO OF 20 71 CMP NEWDRSL ;Same as RAMdisk slot, drive?
2041: DO 01 72 BNE OC1

73

2043: 00 74 BRK ;Crash if duplicate found

75

2044:88 76 OC1 OEY

2045: 10 F4 77 BPL OUPCHECK ;No, so on to next device

78

2047: EE 31 BF 79 INC DEVCNT ;Add "disk" drive
204A: AC 31 BF 80 LOY DEVCNT
2040: AD OF 20 81 LOA NEWDRSL

2050: 99 32 BF 82 STA DEVLST,Y ;Save slot, drive code

83

2053: AD C7 20 84 LDA SLFAKE ;Get slot #
2056: 0A 85 ASL ;x2 to step into table
2057: AC C8 20 86 LDY OFAKE
205A: CO 01 87 CPY #1 Drive 1?
205C: F0 03 88 BEQ FIXTABLE Yes, so branch

89

205E: 18 90 CLC

205F: 69 10 91 ADC #16 Offset to drive 2 table

92

2061: A8 93 FIXTABLE TAY

2062: A9 00 94 LOA #<RAMDISK

;Save address of driver

2064: 99 10 BF 95 STA DEVADR01,Y ; in vector table
2067: A9 03 96 LOA #>RAMDISK
2069: 99 11 BF 97 STA DEVAOR01+1,Y

98

99 ************************

Writing a ProDOS 8 Disk Driver 311

Table 7-3 Continued

101 and initialize program space.
102 ************************************
206C: A9 01 103 LDA #1
206E: 85 67 104 STA TXTTAB
2070: A9 28 105 LDA #$28
2072: 85 68 106 STA TXTTAB+1
2074: A9 00 107 LDA #0

2076: 8D 00 28 108 STA $2800 ;Must begin with $00 byte
2079: 20 4B D6 109 JSR $D64B ;Applesoft "NEW" command

110

111 **~~~~~~~~~~~~~~~~~~~~~~~~
112 Initialize the RAMdisk
113 ~~~~~~~~~~~~~~~~~~~~~~~~~~
207C: 20 E4 20 114 JSR ZER0BLK

115

207F: A0 00 116 LDY #0
2081: B9 C9 20 117 D0NAME LDA Y0LNAME,Y
2084: F0 06 118 BEQ SETLEN

2086: 99 05 30 119 STA INITBLK+5,Y ;Put volume name in buffer
2089: C8 120 INY
208A: DO F5 121 BNE DONAME

122

208C: 98 123 SETLEN TYA

208D: 09 FO 124 0RA #$F0 ;Set "directory" bits
208F: BD 04 30 125 STA INITBLK~4 ;Save file type ~ name length

126

127 Store misc. volume parameters:
2092: AO 22 128 LDY #$22
2094: B9 AC 20 129 DOPARMS LDA INITPARM-$22,Y
2097: 99 00 30 130 STA INITBLK,Y
209A: C8 131 INY
209B: CO 2B 132 CPY #$2B
2O9D: DO F5 133 BNE DOPARMS

134

2O9F: A9 02 135 LDA #2

2OA1: 80 E2 20 136 STA BLKNUM ;Writing to block 2
2OA4: A9 00 137 LDA #0
20A6: 80 E3 20 138 STA BLKNUM+1
20A9: 20 07 20 139 JSR DOWRITE

140

141 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~
142 Fix up the volume bit map
143 ******~~~~~~~~~~~~~~~~~~~~~~~
2OAC: 20 E4 20 144 JSR ZEROBLK

2OAF: A9 OF 145 LDA #$OF 0. .3 in use / 4..7 free
2OB1: 80 00 30 146 STA INITBLK
2OB4: A9 FF 147 LDA #$FF ;8..15 free
20B6: 80 01 30 148 STA INITBLK+1

312 Disk Devices

Table 7-3 Continued

20B9: A9 CO 149 LDA #$C0
20BB: 8D 02 30 150 STA INITBLK+2

151

20BE: EE E2 20 152 INC BLKNUM
20C1: 20 D7 20 153 JSR DOWRITE

154

20C4: 4C 00 03 155 JMP $3D0

156

20C7: 03 157 SLFAKE DFB 3
20C8: 01 158 DFAKE DFB 1

159

20C9: 52 41 40 160 VOLNAME ASC 'RAM8',00
20CC: 38 00

161

20CE: C3 162 INITPARM DFB $C3
20CF: 27 163 DFB $27
2000:00 164 DFB 13
2001: 00 00 165 OW 0
2003: 03 00 166 OW 3
2005: 12 00 167 OW 18

168

;16, 17 free

;Change to block 3
;Reconnect ProD0S hooks
;RAMdisk slot
;RAMdisk drive

Volume name

;Access code
Entry length
;Entries/block
;File count

;Block for bit map
Total blocks

169 *******************************

170 * Write a block to the device
171 *******************************
2007: 20 00 BF 172 DOWRITE JSR MLI

200A: 81 173 DFB $81 WRITE_BLOCK command
20DB: DE 20 174 0A CMDLIST
2000:60 175 RTS
176

200E: 03 177 CMDLIST DFB 3

200F: 00 178 NEWDRSL DS 1 Drive and slot
20E0: 00 30 179 0A INITBLK 1/0 buffer
20E2: 00 00 180 BLKNUM Din 0 ;Block # gets filled in here

181

182 ***
183 Zero the block

184 ******************

20E4: A9 00 185 ZEROBLK LDA #0
20E6: A8 186 TAY
20E7: 99 00 30 187 ZB1 STA INITBLK,Y
20EA: C8 188 INY
20EB: DO FA 189 BNE ZB1

2OE0: 99 00 31 190 ZB2 STA INITBLK+256,Y
2OF0: C8 191 INY
2OF1: 00 FA 192 BNE ZB2
2OF3: 60 193 RTS

194

195 BEGIN EQU
196

Writing a ProDOS 8 Disk Driver 313

Table 7-3 Continued

199 for the /RAM8 volume.
200 **~~~*~*~~*~~*~~~~~~~~~~~~~~~
201

202 ORG $300
203

204 RAMDISK EQU
205

0300: D8 206 CLD ;(Required by ProDOS)

207

208 * Save zero page locations:
0301: AS 06 209 LDA RAMPTR
0303: 8D 7A 03 210 STA ZPTEMP
0306: AS 07 211 LDA RAMPTR+1
0308: 8D 7B 03 212 STA ZPTEMP+1

213

214 *********************~~~~~*~***
215 Check for block range error
216 ****************~~~*~*~~~~~*~~~

030B: AS 47 217 LDA BL0CK~1 ;Check block number (high)
0300: DO 34 218 BNE IOERROR ;Error if not zero
030F: AS 46 219 LDA BLOCK ;Check block number (low)
0311: C9 12 220 CMP #18 Is it out of bounds?
0313: B0 2E 221 BCS I0ERROR It's >=18, so error

222

223 **********************************
224 Convert block # to RAM address
225 *****************~*~***~~~~~~~~~~~

0315: 0A 226 ASL Multiply block by 2
0316:18 227 CLC

0317: 69 04 228 ADC #4 ;... and add 4 to get
0319: 85 07 229 STA RAMPTR+1 starting page of block
031B: A9 00 230 LDA #0
0310: 85 06 231 STA RAMPTR

232

233 ~**~~~~~~~~~~~~~~~~~~~
234 Check command code

235

031F: AS 42 236 LDA COMMAND
0321: C9 03 237 CMP #3
0323: FO OC 238 BEQ EXIT
0325: C9 00 239 CMP #0
0327: FO 08 240 BEQ EXIT
0329: C9 01 241 CMP #1
032B: F0 1B 242 BEQ READ
0320: C9 02 243 CMP #2
032F: F0 30 244 BEQ WRITE

245

314 Disk Devices

Get command code
;Format?

;Yes, so exit normally
;Check status?

;Yes, so exit normally
;Read?

;Yes, so branch
Write?

Yes, so write

Table 7-3 Continued

0331: 18 246 EXIT CLC
0332: A9 00 247 LDA #0
0334:08 248 EXIT1 PHP
0335:48 249 PHA
0336: AD 7A 03 250 LDA ZPTEMP
0339: 85 06 251 STA RAMPTR
033B: AD 7B 03 252 LDA ZPTEMP+1
033E: 85 07 253 STA RAMPTR+1
0340:68 254 PLA
0341:28 255 PLP
0342:60 256 RTS
257

0343:38 258 I0ERR0R SEC
0344: A9 27 259 LDA #$27
0346: DO EC 260 BNE EXIT1

261

;CLC ==> no error

;Restore error code
;Restore carry status

;SEC ==> error occurred
;I/0 ERROR code
;(always taken)

262 ******************************

263 Perform READ command by
264 transferring data from the
265 * RAM to the data buffer.
266 ******************************
0348: A0 00 267 READ LDY #0
034A: B1 06 268 FR0MCARD LDA (RAMPTR),Y
034C: 91 44 269 STA (BUFFER),Y
034E: C8 270 INY
034F: DO F9 271 BNE FR0MCARD
0351: E6 07 272 INC RAMPTR+1
0353: E6 45 273 INC BUFFER+1
0355: B1 06 274 FC1 LDA (RAMPTR),Y
0357: 91 44 275 STA (BUFFER),Y
0359: C8 276 INY
035A: DO F9 277 BNE FC1
035C: C6 45 278 DEC BUFFER+1
035E: 4C 31 03 279 JMP EXIT

280

281 *********~*~****~~~~~~~~~~~~~~
282 Perform WRITE command by
283 transferring data from the
284 * data buffer to the RAMcard.*
285 *****~~~****~~~~~~~~~~~~~~~~~~
0361: A0 00 286 WRITE LDY #0
0363: B1 44 287 T0CARD LDA (BUFFER),Y
0365: 91 06 288 STA (RAMPTR),Y
0367: C8 289 INY
0368: DO F9 290 BNE T0CARD
036A: E6 45 291 INC BUFFER+1
036C: E6 07 292 INC RAMPTR+1
036E: B1 44 293 TC1 LDA (BUFFER),Y
0370: 91 06 294 STA (RAMPTR),Y

Writing a ProDOS 8 Disk Driver 315

Table 7-3 Continued

0375: C6 45 297 DEC BUFFER+1
0377: 4C 31 03 298 JMP EXIT

299

037A: 00 00 300 ZPTEMP DS 2

301

302 END EQU

When you use the /RAM8 disk driver, be careful not to run any graphics prograrns
that use the primary high-resolution graphics screen. The video RAM buffer this
screen uses ($2000-$3FFF) overlaps the /RAM8 block storage area. Moreover, the
Applesofr program must not overwrite the device driver in page 3, or the storage
space itself, with POKE statements. If you want to avoid these memory conflicts, you
can relocate the disk driver (and its corresponding storage space) to an area above
HIMEM and the BASIC.SYSTEM general-purpose file buffer using the techniques
described in Chapter 5.

You can remove the /RAM8 device from the system using the technique described
above for the removal of the /RAM volume. You will also have to clear the appropriate
bits in the system bit map, reset the Applesoft program pointer to $801, and execute
an Applesoft NEW command to initialize other important Applesoft data pointers.

316 Disk Devices

In Chapter 2, we saw that the directory entry for each file on a disk formatted for the
ProDOS file system contains 4 bytes for the time and date the file was created and 4
more bytes for the time and date it was last modified. Most other file systems save
similar time and date information.

The ProDOS file system's date-stamping feature is very useful, especially for those
who routinely save several versions of the same file on different disks. Three months
later you won't have to guess which one is the latest version; all you have to do is
compare modification dates. The BASIC.SYSTEM CATALOG command displays
these dates when it lists the names of the files on disk.

GS/OS and ProDOS 8 determine the current time and date by accessing a real-time
clock/calendar chip interfaced to the microprocessor. On the IIcs, this chip is an integral
part of the system and does not occupy a slot or port; on the lIe and II Plus, you must add
an optional clock card. There are also clocks available for the slotless IIc.
A computer clock contains special integrated circuits that allow it to keep track of
the current time and date independently of the microprocessor. It is the Apple's
digital watch, if you like. Clocks keep the correct time even when the Apple is turned
off because they are powered by batteries.
ProDOS 8 uses a special assembly-language program, called a clock driver, to
transfer the time and date from the card to the Apple in an understandable 'form.
ProDOS 8 comes with internal clock drivers for the built-in II&s clock and for any
clock card that understands a standard set of time-related commands originally used in
Thunderware's Thunderclock. ProDOS 8 automatically installs the correct driver into
the system when it first boots up. If there is no recognizable clock, ProDOS 8 installs
a null driver, and application programs should ask the user to enter the correct time
and date if that information is needed. GS/OS always installs a driver for the built-in
clock on the Apple IIcs.

In this chapter, we examine how ProDOS 8 deals with time issues in general. In
particular, we see how it detects the presence of a clock card, how it installs the clock
driver, and how to design and install your own ProDOS 8 clock driver for a nonstand-
ard clock. (Since GS/OS has a built-in driver for the IIcs clock, you will never have to
install your own driver; therefore GS/OS has no mechanism for installing custom clock

317

drivers.) We also go through some useful examples of how to make the
time and date capabilities of GS/OS and ProDOS 8.

HOW GS/OS AND PRODOS BREAD THE TIME AND DATE
Whenever ProDOS 8 needs to know the time and date it always makes the
JSR DATETIME. The code starting at DATETIME ($BF06) is either a 1
instruction (if no ProDOS-compatible clock is in the system) or a 3-byte JMP
tion that passes control to a ProDOS 8 clock driver (if a compatible clock is
In either case, the 2 bytes at $BF07-$BF08 always hold the address of the start
ProDOS 8 clock driver space.

The clock driver reads the time and date from the clock and stores the -
special format at TIME ($BF92-$BF93) and DATE ($BF9~$BF91) in the
global page. Figure 8-1 describes the format used. If no clock driver is present,
and DATE are not modified because the RTS instruction stored at DAi
($BF06) immediately bounces control back to the caller. The only way to set the
and date in this situation is to write directly to the TIME and DATE locations.

The approved method of determining the date and time in a ProDOS 8

is to use the GET-TIME command. Recall from Chapter 4 that you can do
executing a subroutine like this one:

JSR $BF00 Make a call to the MLI
DFB $82 ;GET_TIME

0A $0000 Dummy parameter table
RTS

When this subroutine finishes, the TIME and DATE locations contain the cur
time and date in the format described above.

GS/OS has no equivalent operating system command for returning the current
and date. If you want the time and date, you must use two commands in the -
IIcs Miscellaneous Tool Set: ReadTimeHex and ReadAsciiTime.

ReadTimeHex (toolbox command $0D03) returns the current time and date
eters as binary numbers. Here's how to call it from 65816 full native mode:

PHA ;Space for results
PHA (eight bytes)
PHA
PHA

LDX #$0D03 ;ReadTimeHex
JSL $E10000

PLA WeekDay (high)
PLA ;Month (high), Day (low)
PLA ;CurYear (high), Hour (low)
PLA ;Minute (high), Second (low)

318 Clocks

Figure 8-1 The formats of the ProDOS 8 DATE and TIME bytes
(a) DATE ($BF9~$BF91)

7 6 5 4 3 2 1 0
Y6 Y5 Y4 Y3 Y2 Y1 YO M3 $BF91
7 6 5 4 3 2 1 0
M2 M1 MO D4 D3 02 01 DO $BF90

The year is encoded as Y6 Y5 Y4 Y3 Y2 Y1 YO (bits 1-7 of the high-order byte). Only
the last two digits of the year are stored (that is, 89 for 1989).

The month is encoded as M3 M2 M 1 MO (bits 5-7 of the low-order byte and bit 0

of the high-order byte). January is month 1, and December is month 12.
The day of the month is encoded as D4 D3 D2 D1 DO (bits 0=1 of the low-order byte).
For example, November 30, 1989, would be stored as follows:
High-order byte Low-order byte
1 0 1 1 0 0 1 1 0 1 1 1 1 1 1 0
YYYYYYYYYYYYY M MMMMM DDDDDDDDD

Year ($59) Month ($0B) Day ($1E)
1989 November 30

(b)TIME ($BF92-$BF93)

7 6 5 4 3 2 1 0

0 0 O H4 H3 H2 HI HO $BF93
7 6 5 4 3 2 1 0
O 0 MS M4 M3 M2 M1 MO $BF92

The hour is encoded as H4 H3 H2 HI HO (bits 0=1 of the high-order byte). The hour
is stored in military (24-hour) format.
The minute is encoded as MS M4 M3 M2 M1 MO (bits ~ of the low-order byte).
For example, 9:20 p.m. (21:20) would be stored as follows:

High-order byte
0 0 0 1 0 1 0 1

HHHHHHHHH

Hours ($15)

21

Low-order byte
0 0 0 1 0 1 0 0
Minutes ($14)

20

How GS/CS and ProDOS 8 Read the Time and Date 319

The values ReadTimeHex returns are as follows:
WeekDay 1..7 1 = Sunday, 2 = Monday, and so on
Month 0. .11 0 = January, 1 = February, and so on
Day 0. .30 day of month minus 1
CurYear 0. .99 current year minus 1900
Hour 0. .23 hour in military format
Minute 0. .59
Second 0. .59

ReadAsciiTime (toolbox command $0F03) returns a 20-byte ASCII-encoded
string describing the current time and date. Here is how to call it:

PushPtr TimeString ;Pointer to string area
LDX #$0F03 ;ReadAsciiTime
JSL $E10000
RTS

TimeString DS 20 ;Space for time string

Note that the time string returned is not preceded by a length byte. The
always exactly 20 bytes long, and the high-order bit of each byte is set to 1.

The format of the time string depends on the settings of the date and time

in the Control Panel. There are six possibilities:

mm/dd/yy HH:MM:SS XM
dd/mm/yy HH:MM:SS XM
yy/mm/dd HH:MM:SS XM
mm/dd/yy HH:MM:SS
dd/mm/yy HH:MM:SS
yy/mm/dd HH:MM:SS

XM = AM or PM

24-hour military format

The first format listed here is the Control Panel's default.

HOW PRODOS 8 IDENTIFIES A CLOCK CARD

When you first boot ProDOS 8 on a system other than the IIcs, ProDOS 8 examines
each peripheral expansion slot in the system for a standard clock card. ProDOS 8
identifies such a card by the following unique pattern of bytes in the card's dedicated
$Cn00-$CnFF ROM space (n is the slot number):

$Cn00 $08
$Cn02 $28
$Cn04 $58
$Cn06 $70.

320 ClocksIf it finds a clock card, ProDOS 8 installs its standard clock driver and changes the
RTS opcode ($60) at $BF06 to a JMP opcode ($4C). Since the 2 bytes following this
opcode contain the address of the clock driver space (low-order byte first), the driver
takes control whenever a program executes a JSR $BF06 instruction. Actually, a
program should always use the GET-TIME command to read the time and date; the
GETTIME command handler is what calls the clock driver directly.

The built-in IIcs clock does not occupy a slot or port, so ProDOS 8 can't identify it

by checking bytes in BOM. Instead, it simply checks to see what Apple II model it is
running on; if it's a IIcs, it installs the IIcs clock driver.

ProDOS 8 also sets the clock bit (bit 0) of the machine identification byte,
MACHID ($BF98), to 1 if it finds a clock.

WRITING AND INSTALLING A PRODOS 8 CLOCK DRIVER
If you are using a nonstandard clock, you must write and install your own ProDOS 8
clock driver. Two examples of nonstandard clocks are a clock interfaced through the
serial port of a IIc and a clock on a multifunction peripheral card that does not occupy
a phantom slot.
Writing a clock driver is no easy feat since it requires detailed information concern-
ing how the clock circuitry is interfaced to the Apple and the procedure a programmer
must follow to extract time and date information from the card. If you re lucky, the
manufacturer of the card will have a detailed technical reference manual that contains
this information. But more commonly you will have to beg, borrow, or steal this
information before you can get started. Happily, manufacturers of nonstandard clock
cards have already written their own ProDOS 8 clock drivers and include them on
disk with their hardware.

The general characteristics of a clock driver are:

z It must start with a CLD instruction.

z It must read the time and date from the clock card and store the results in the
proper format in the global page TIME ($BF92-$BF93) and DATE ($BF90-
$BF91) locations.

Once you write a driver, you must move it to an area of memory that other programs
will not use. The best available area is the one the very clock driver you are replacing
uses; you can always find the starting address of this area at $BF07-$BF08 (low-order
byte first).

If you choose to use the standard driver area (and we do recommend this selection),
keep several important considerations in mind:

z Never assume the standard clock driver will reside at the same position in
every version of ProDOS 8. To ensure your driver will run properly at any
address that might be stored at $BF07-$BF08, you should avoid using JMP and

Writing and Installing a Pro DOS 8 Clock Driver 321

JSB instructions or storing data within the main body of the driver. If you
the code will not be relocatable, and you will need to patch it to resolve -
internal absolute address references after you move it to its new position.

z Make sure your clock driver is no longer than 125 bytes. ProDOS 8
this amount of space for its standard drivers, and Apple has guaranteed -
amount of driver space.

z Before moving your clock driver into position, write-enable bank 1 of
switched BAM by reading from location $C08B twice in succession.
standard clock driver resides in bank-switched BAM.) After the move,
the Applesoft and system monitor BOM area by reading from location $C082.

The next step in the installation procedure is to set up a JMP instruction at
that points to your clock driver. Do this by storing $4C (the JMP opcode) at
and the address of the driver at $BF07-$BF08 (low-order byte first). If you have
loaded the driver at the address of the standard clock driver, you can skip the latt~
step since the correct driver address will already be in place.

Finally, you should set bit 0 of MACHID ($BF98) to 1 to indicate that a clock h~
been installed in the system. Do this by executing the following short piece of code:

LDA MACHID ;Get ID byte
0RA #$01 ;Store a 1 in bit 0
STA MACHID ;Update ID byte

The easiest way to install a clock driver is to make the installation program part of the
STARTUP program, whichautomatically runs when ProDOS 8 executes the BASIC. SYSTEM Applesoft interpreter.

TIME/DATE UTILITY PROGRAMS
An Applesoft Time and Date Variable

Some dialects of BASIC have a special variable called TIME$ that always contains the
current time in the standard HH:MM:SS form. This variable is very useful when a
program needs to display the current time, automatically time-stamp printed reports,
calculate elapsed times, perform benchmarking studies, and so on.

You can use the READ.TIME subroutine in Table 8-1 to return the time and date

in the form DD-MM-19YY HH:MM in any Applesoft string variable you speci(y. Alter
loading the subroutine, use it by executing the following statement from within an
Applesofi program:

CALL 768,TM$

TM$ represents the name of the variable that is to hold the time string.
322 Clocks

Table 8-1 READ.TIME, a program to load the time and date into an Applesoft

string variable
1 **************************************
2 READ.TIME
3
4 This program reads the time and
5 * date and stores it in an Applesoft
6 string variable. The syntax is
7
8 CALL 768,TM$
9
10 The TM$ string has the form
11 DD-MM-19YY HH:MM
12
13 Copyright 1985-1988 Gary B. Little
14
15 Last modified: August 28, 1988
16
17 **************************************
18 FRET0P EQU $6F Bottom of string space

19 VARPNT EQU $83
20

21 IN EQU $200
22

23 MLI EQU $BF00
24 DATE EQU $BF90
25 TIME E0U $BF92
26

27 CHKCOM EQU $DEBE
28 PTRGET EQU $DFE3
29 GETSPACE EQU $E452
30 MOVSTR EQU $E5E2
31

32 0RG $300
33

0300: 20 00 BF 34 JSR MLI
0303:82 35 DFB $82
0304: 00 00 36 0A $0000

37

38 "Unpack" the time:
39

0306: AD 92 BF 40 LDA TIME
0309: 8D B8 03 41 STA MINUTES
030C: AD 93 BF 42 LDA TIME+1
030F: 8D B9 03 43 STA HOURS
0312: AD 90 BF 44 LDA DATE
0315: 29 IF 45 AND #$1F
0317: 80 BA 03 46 STA DAY
031A: AD 91 BF 47 LDA DATE+1
0310: 80 BC 03 48 STA YEAR

;Pointer to string data
Input buffer
;Entry point to MLI
;Year + Month + Day
;Minutes + Hours

;Skip comma

;Locate a variable

;Get string space for "A" chars
;Move string to free space

;Call the MLI and
select GET_TIME command
(no parameter table)

Get minutes
and save them
;Get hours

and save them

;Get "day" bits (0. .4),
strip "month" bits,
and store correct number
;Get "year" bits (1. .7)
and month bit (0).

Time/Date Utility Programs 323

Table 8-1 Continued

0326:6A 51 R0R
0327:4A 52 LSR
0328:4A 53 LSR
0329:4A 54 LSR
032A: 4A 55 LSR
032B: 80 BB 03 56 STA MONTH

57

59

032E: A2 O0 60 LDX #0

0330: 8E B7 03 61 STX TIMEPOS Clear ptr to time string
0333: 8A 62 FORMTIME TXA
0334:48 63 PHA

0335: BD BD 03 64 LDA FORMAT,X ;Get formatting byte
0338:08 65 PHP
0339: AE B7 03 66 LDX TIMEPOS
033C: 28 67 PLP

0330: 30 1E 68 BMI NOTNUM Branch if not number
033F: A8 69 TAY ;Get time code in Y

70

0340: B9 B8 03 71 LDA TIMEDATA,Y ;Get binary time/date data
0343: 20 92 03 72 JSR CONVERT ;Convert to BCD
0346: 48 73 PHA ;Save number
0347: 4A 74 LSR Move "tens" digit to
0348: 4A 75 LSR ; lower 4 bits by

0349: 4A 76 LSR
034A: 4A 77 LSR
034B: 09 30 78 0RA #$30
0340: 90 00 02 79 STA IN,X
0350: E8 80 INX
0351:68 81 PLA
0352: 29 OF 82 AND #$OF
0354: 09 30 83 ORA #$30
0356: 90 00 02 84 STA IN,X
0359: E8 85 INX
035A: 4C 63 03 86 JMP T0NEXT

87

0350: 29 7F 88 N0TNUM AND #$7F
035F: 90 00 02 89 STA IN,X
0362: E8 90 INX
0363: 8E B7 03 91 TONEXT STX TIMEP0S
0366:68 92 PLA
0367: AA 93 TAX
0368: E8 94 INX
0369: ED DC 95 CPX #12
036B: DO C6 96 BNE F0RMTIME

97

324 Clocks

shifting right four
times

;Convert to ASCII digit

;Get original number back
;Isolate units digit
;Convert to ASCII digit

Strip high bit for Applesoft
Insert punctuation

Go to next position
At end of template?
;No, so keep going

Table 8-1 Continued

98 Move string to bottom of string space:
99

036D: AD B7 03 100 LDA TIMEP0S ;Get length of string
0370: 20 52 E4 101 JSR GETSPACE ;Make room for it
0373: A2 00 102 LDX #0

0375: A0 02 103 LDY #2 ;Y/X point to string
0377: 20 E2 E5 104 JSR M0VSTR ;Move the string (length in A)

105

106 Point Applesoft variable to time/date string.
107 The string is now positioned at the bottom
108 of string space and is pointed to by FRET0P.
109

037A: 20 BE DE 110 JSR CHKC0M ;Skip over comma
037D: 20 E3 DF 111 JSR PTRGET

0380: AD B7 03 112 LDA TIMEP0S ;Get length of string
0383: AD 00 113 LDY #0

0385: 91 83 114 STA (VARPNT),Y ;... and save it
0387: C8 115 INY
0388: AS 6F 116 LDA FRET0P

038A: 91 83 117 STA (VARPNT),Y Save address (low)
038C: C8 118 INY
0380: AS 70 119 LDA FRET0P+1

038F: 91 83 120 STA (VARPNT),Y ;Save address (high)
0391:60 121 RTS
122

123
124 Binary to BCD Conversion
125 Number must be 0...99
12 ***

0392: 80 B6 03 127 CONVERT STA TEMP
0395: 8E B5 03 128 STX XSAVE
0398: A9 00 129 LDA #0
039A: F8 130 SED
039B: A2 06 131 LDX #6
0390: 4E B6 03 132 NEXTBIT LSR TEMP
03A0: 90 04 133 BCC NOllEIGHT
03A2: 18 134 CLC
03A3: 70 AE 03 135 ADC BIMDEC,X
03A6: CA 136 NOWEIGHT DEX
03A7: 10 F4 137 BPL NEXTBIT
03A9: 08 138 CLD
03AA: AE B5 03 139 LDX XSAVE
03AD: 60 140 RTS

141

Put # into work area
;Start with a 0 result
,Use decimal arithmetic
;Examine bits 0...6
;Move low bit into carry
Branch if it was zero
else add it
to result

,Count down to -1
Branch if more to go
,Return to binary arithmetic

03AE: 64 32 16 142 BINDEC DFB $64,$32,$16 ;These are the weights of
03B1: 08 04 02 143 DFB $08,$04,$02 ;the low 7 bits in
03B4: 01 144 DFB $01 ;a byte (in BCD)

145

03B5: 00 146 XSAVE DS 1 Temporary X location

Time/Date Utility Programs 325

Table 8-1 Continued

O3B7: 00 148 TIMEPOS DS 1

149

150 TIMEDATA EQU
151

O3B8: 00 152 MINUTES DS 1 ;Minutes (0.. .59)
03B9: 00 153 HOURS DS 1 ;Hours (0. .23)
03BA: 00 154 DAY DS 1 Day of month (l...31)
03BB: 00 155 MONTH DS 1 ;Month of year (1. ..12)
03BC: OO 156 YEAR DS 1 ;Year (O...99)

157

158 Formatting template for "'DD-MM-19YY HH:MM"
159 (digits refer to entries in TIMEDATA table)
160

161 FORMAT EQU
162

03BD: 02 163 DFB 2
03BE: AD 03 AD 164 DFB "-",3,"-"
03C1: B1 B9 04 165 OFB "1","9",4
O3C4: AD AD 01 166 DFB $AO,$AO,1
O3C7: BA 00 167 DFB ":",O

When you call READ.T1ME, it first uses the ProDOS 8 CETT1ME command to
read the current time and date into the ProDOS 8 global page locations. It then
unpacks the year, month, and day data from the DATE locations and stores each of
them in its own temporary location. The hours and minutes are already unpacked, but
they are also transferred to temporary locations.

After unpacking, READ.TIME begins to assemble the ASCII time string in the
Applesoft input buffer starting at $200. It does this by scanning a special template
string that contains either ASCII characters or single-digit time codes. The ASCII
characters are transferred directly to the time string. When a time code is encoun-
tered, however, the corresponding time parameter is loaded, converted to a binary-
coded decimal (BCD) number, and then stored as two consecutive ASCII digits in the
time string.

Next, READ.T1ME moves the string from the input buffer to the main Applesoft
string space in the high end of memory to ensure the string will not be overwritten
the next time your program executes an Applesoft INPUT statement. This is done
using two Applesoft ROM subroutines called GETS PACE ($E4B2) and MOVSTB
($E5E2). When you call CETSPACE with the string length in the accumulator, it
makes room for the string by lowering FRETOP ($6F-$70), the pointer to the bottom
of string space, by the appropriate number of bytes. MOVSTR moves a string of
length A, pointed to by Y (high) and X (low), to this free space.

326 Clocks

Once the time string is in position, READ.T1ME locates the TM$ variable in the
Applesoft variable table by executing the following two instructions:

JSR CHKC0M
JSR PTRGET

CHKCOM ($DEBE) and PT11GET ($DFE3) are two more Applesofi ROM subroutines.
The first instruction advances the Applesofr program pointer by 1 byte, effectively slcip-
ping over the comma separating the CALL address from the variable. The second
instruction stores the address of the 3-byte descriptor that defines the string variable in
VARPNT ($83) and VARPNT + 1 ($84). The first byte in the descriptor is the length of the
string; the next 2 bytes contain the pointer to the contents of the string.

The final step is to store the new string length and pointer in the descriptor. The
length (T1MEPOS) is stored in the first descriptor byte, and the pointer to the string,
found at FRETOP ($6F) and FRETOP+ 1 ($70), is stored in the other 2 bytes.

Setting the Time and Date on a Clockless Apple

Even if you do not have a clock in your Apple II, you can still date- and time-stamp a
file by explicitly storing the current date and time in the ProDOS 8 global page
locations just before saving the file to disk. This is somewhat inconvenient, but it's
better than nothing. If you can survive with just the correct date, life becomes much
easier because you have to set the date only once when you first turn the computer on
(assuming, perhaps naively, that you don't work past midnight).

The TIMEDATE program in Table 8-2 lets you enter a time and date in English.
After you do so, the program converts the information into the encoded format used by
ProDOS 8 and then stores it in the ProDOS 8 global page locations.

Time/Date Utility Programs 327

Table 8-2 TIMEDATE, a program to manually set the time and date.

3 REM DECEMBER 21, 1987

100 NOTRACE : TEXT : PRINT CHR$ (21): SPEED= 255: NORMAL : HOME
110 DIM MT$(12)

140 FOR I = 1 TO 12: READ MT$(I): NEXT

150 DATA JANUARY,FEBRUARY,MARCH,APRIL,MAY,JUNE,JULY,AUGUST,

160
165

SEPTEMBER,OCTOBER,N0VEMBER,DECEMBER
PRINT "PR0D0S TIME/DATE SETTER"
PRINT "COPYRIGHT 1985-1987 GARY B. LITTLE"

170 T1 = 49042: REM $BF92 (MINUTES)
180 T2 = 49043: REM $BF93 (HOURS)
190 T3 = 49040: REM $BF90 (MMMDDDDD)
200 T4 = 49041: REM $BF91 (YYYYYYYM)

400 VTAB 6: CALL - 958: INPUT "ENTER YEAR (1900-1999): 19";A$:
YR = VAL (A$): IF YR < 0 OR YR > 99 OR A$ = "" THEN 400

500 VTAB 7: CALL - 958: INPUT "ENTER MONTH (JAN...DEC): ";A$:M$ =
501 IF A$ = "" THEN 500

505 FOR I = 1 TO LEN (A$): IF ASC ( MID$ (A$,I,1)) > = 96 THEN
B$ = B$ + CHR$ ( ASC ( MID$ (A$,I,1)) - 32): GOTO 507

506 B$ = B$ + MID$ (A$,I,1)
507 NEXT :A$ = B$

510 FOR I = 1 TO 12: IF A$ = MT$(I) OR A$ = LEFT$ (MT$(I),3) THEN
MT = 1:1 = 12: NEXT : GOTO 600

520 NEXT : GOTO 500

600 VTAB 8: CALL - 958: INPUT "ENTER DAY OF MONTH (1-31): ";A$:
DY = VAL (A$): IF DY < 1 OR DY > 31 THEN 600

720 VTAB 9: CALL - 958: INPUfi "ENTER HOUR (0-23): ";A$:
HR = VAL (A$): IF HR < 0 OR HR 23 OR A$ = "" THEN 720

800 VTAB 10: CALL - 958: INPUT "ENTER MINUTES (0-59): ";A$:
MN = VAL (A$): IF MN < 0 OR MN > 59 OR A$ = "" THEN 800

1000 PRINT : PRINT "PRESS ANY KEY TO SET":
PRINT "THIS TIME AND DATE: ";: GET A$: PRINT A$

1010 POKE T1,MN
1020 POKE T2,HR

1030 POKE T4,2 YR + INT (MT / 8)
1040 POKE T3,32 * (MT - 8 INT (MT / 8)) + DY
1050 HOME : PRINT "THE TIME AND DATE HAVE NOW BEEN SET."

328 Clocks