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 if -

many disk devices are connected to the system and how they may be an

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

in 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, GS/OS does not use unit numbers

are derived from slot and drive numbers) to identify 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

 

* ProDOS RAMdisk disk driver

*

* This driver controls a 8K RAMdisk

* volume called /RAM8.

*

* Copyright 1985-1988 Gary B. Little

 

* Last modified: August 26, 1988

 

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

 

* Move device driver code into place:

 

LDY #0

 

M0VEC0DE LDA BEGIN,Y

STA RAMDISK,Y

INY

 

CPY #END-RAMDISK

BNE M0VEC0DE

 

* Mark pages 3, 8. .27 as "in use"

* in the system bit map. This

* prevents /RAM8 or its driver

* from being overwritten by BL0AD.*

 

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