!pr1
!lm12
!rm75
Commented Listing of ProDOS $F90C-F995, $FD00-FE9A, $FEBE-FFFF
                               ............Bob Sander-Cederlof

Last month I printed the commented listing of the disk reading subroutines.  This month's selection covers disk writing, track positioning, and interrupt handling.  Together the two articles cover all the code between $F800 and $FFFF.

Several callers have wondered if this is all there is to ProDOS.  No!  It is only a small piece.  In my opinion, this is the place to start in understanding ProDOS's features:  A faster way of getting information to and from standard floppies.  But remember that ProDOS also supports the ProFILE hard disk, and a RAM disk in the extended Apple //e memory.

Further, ProDOS has a file structure exactly like Apple /// SOS, with a hierarchical directory and file sizes up to 16 megabytes.

Further, ProDOS includes support for a clock/calendar card, 80-columns with Smarterm or //e, and interrupts.

ProDOS uses or reserves all but 255 bytes of the 16384 bytes in the language card area (both $D000-DFFF banks and all #E000-FFFF).  The 255 bytes not reserved are from $D001 through $D0FF in one of the $D000 banks.  The byte at $D000 is reserved, because ProDOS uses it to distinguish which $D000 bank is switched on when an interrupt occurs.  The space at $BF00-BFFF is used by ProDOS for system linkages and variables (called the System Global Page).

In addition, if you are using Applesoft, ProDOS uses memory from $9600-BEFF.  This space does not include any file buffers.  When you OPEN files, buffers are allocated as needed.  CLOSEing automatically de-allocates buffers.  Each buffer is 1024 bytes long.  As you can see, with ProDOS in place your Applesoft program has less room than ever.


Track Seeking:  $F90C-F995

The SEEK.TRACK subroutine begins at $F90C.  The very first instruction multiplies the track number by two, converting ProDOS logical track number to a physical track number.  If you want to access a "half-track" position, you could either store a NOP opcode at $F90C, or enter the subroutine at $F90D.

A table is maintained of the current track position for each of up to 12 drives.  I call it the OLD.TRACK.TABLE.  The subroutine GET.SSSD.IN.X forms an index into OLD.TRACK.TABLE from slot# * 2 + drive#.  There are no entries in the table for drives in slots 0 or 1, which is fine with me.  ProDOS uses these slots as pseudo slots for the RAM-based pseudo-disk and for ProFILE, if I remember correctly.

The code in SEEK.TRACK.ABSOLUTE is similar but not identical to code in DOS 3.3.  The differences do not seem to be significant.


Disk Writing:  $FD00-FE9A

The overall process of writing a sector is handled by code in RWTS, which was listed last month.  After the desired track is found, RWTS calls PRE.NYBBLE to build a block of 86 bytes containing the low-order two bits from each byte in the caller's buffer.  PRE.NYBBLE also stores a number of buffer addresses and slot*16 values inside the WRITE.SECTOR subroutine.  Next RWTS calls READ.ADDRESS to find the sector, and then WRITE.SECTOR to put the data out.

WRITE.SECTOR is the real workhorse.  And it is very critically timed.  Once the write head in your drive is enabled, every machine cycle is closely counted until the last byte is written.  First, five sync bytes are written (ten bits each, 1111111100).  These are written by putting $FF in the write register at 40 cycle intervals.  Following the sync bytes W.S writes a data header of D5 AA AD.

Second, the 86-byte block which PRE.NYBBLE built is written, followed by the coded form of the rest of your buffer.  WRITE.SECTOR picks up bytes directly from your buffer, keeps a running checksum, encodes the high-order six bits into an 8-bit value, and writes it on the disk...one byte every 32 cycles, exactly.  Since your buffer can be any arbitrary place in memory, and since the 6502 adds cycles for indexed instructions that cross page boundaries, WRITE.SECTOR splits the buffer in parts before and after a page boundary.  All the overhead for the split is handled in PRE.NYBBLE, before the timed operations begin.

Finally, the checksum and a data trailer of DE AA EB FF are written.


Empty Space:  $FEBE-FF9A

This space had no code in it.  Nearly a whole page here.


Interrupt & RESET Handling:  $FF9B-FFFF

If the RAM card is switched on when an interrupt or RESET occurs, the vectors at $FFFA-FFFF will be those ProDOS installed rather than the ones permanently coded in ROM.  It turns out the non-maskable interrupt (NMI) is still vectored down into page 3.  But the more interesting IRQ interrupt is now vectored to code at $FF9B inside ProDOS.

The ProDOS IRQ handler performs two functions beyond those built-in to the monitor ROM.  First, the contents of location $45 are saved so that the monitor can safely clobber it.  Second, a flag is set indicating which $D000 bank is currently switched on, so that it can be restored after the interrupt handler is finished.  (The second step is omitted if the interrupt was caused by a BRK opcode.)

If the IRQ was not due to a BRK opcode, a fake "RTI" vector is pushed on the stack.  This consists of a return address of $BF50 and a status of $04.  The status keeps IRQ interrupts disabled, and $BF50 is a short routine which turns the ProDOS memory back on and jumps up to INT.SPLICE at $FFD8:

       BF50-  8D 8B C0  STA $C08B
       BF53-  4C D8 FF  JMP $FFD8

Of course, before coming back via the RTI, ProDOS tries to USE the interrupt.  If you have set up one or more interrupt vectors with the ProDOS system call, they will be called.

INT.SPLICE restores the contents of $45 and switches the main $D000 bank on.  Then it jumps back to $BFD3 with the information about which $D000 bank really should be on.  $BFD3 turns on the other bank if necessary, and returns to the point at which the interrupt occured.

The instruction at $FFC8 is interesting.  STA $C082 turns on the monitor ROM, so the next instruction to be executed is at $FFCB in ROM.  This is an RTS opcode, so the address on the stack at that point is used.  There are two possible values:  $FA41 if an IRQ interrupt is being processed, or $FA61 if a RESET is being processed.  This means the RTS will effectively branch to $FA42 or $FA62.

Uh Oh!  At this point you had better hope that you are not running with the original Apple monitor ROM.  The Apple II Plus ROM (called Autostart Monitor) and the Apple //e ROM are fine.  $FA42 is the second instruction of the IRQ code, and $FA62 is the standard RESET handler.  But the original ROM, like I have in my serial 219 machine, has entirely different code there.

I have an $FF at $FA42, followed by code for the monitor S (single step) command.  And $FA62 is right in the middle of the S command.  There is no telling what might happen, short of actually trying it out.  No thanks.  Just remember that RESET, BRK, and IRQ interrupts will not work correctly if they happen when the RAM area is switched on and you have the old original monitor in ROM.

There is another small empty space from $FFE9 through $FFF9, 17 bytes.

Perhaps I should point out that the listings this month and last are from the latest release of ProDOS, which may not be the final released version.  However, I would expect any differences in the regions I have covered so far to be slight
