mirror of
				https://github.com/steve-chamberlin/fpga-disk-controller.git
				synced 2025-10-24 11:00:51 -07:00 
			
		
		
		
	
		
			
				
	
	
		
			817 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			817 lines
		
	
	
		
			37 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
| ******************************************************************
 | |
| *                                                                *
 | |
| *                       THE BOOT PROCESS                         *
 | |
| *                                                                *
 | |
| ******************************************************************
 | |
| *                                                                *
 | |
| *      The following disassembly describes the boot process for  *
 | |
| * a slave disk.  In order to TRULY understand this listing, you  *
 | |
| * should first be familiar with the exact sequence and coding of *
 | |
| * disk bytes used in track formatting.  This information can be  *
 | |
| * found in chapter 3 of the book BENEATH APPLE DOS.  (The        *
 | |
| * distinctions between booting a slave disk versus a master disk *
 | |
| * are described in chapter 8 of the same reference.)             *
 | |
| *      The boot process loads DOS into the machine as a series   *
 | |
| * of discrete packages.  After each package is loaded, it is     *
 | |
| * executed to load in the next section of DOS.  The last         *
 | |
| * instruction of the boot process jumps into DOS's coldstart     *
 | |
| * routine to build the DOS buffers, set up the page-three vector *
 | |
| * table and run the "HELLO" program.                             *
 | |
| *      Because DOS is loaded in stages, any protected disk can   *
 | |
| * cracked by tracing the boot.  If you modify each section of    *
 | |
| * the boot to stop after loading the next section, you can       *
 | |
| * inspect the different stages of the boot to discover the       *
 | |
| * protection scheme(s) used.  (P.S.  In order to modify BOOT0,   *
 | |
| * you must first move it down to a RAM location defined by:      *
 | |
| * $hs00, where h = high nibble of an address that is low enough  *
 | |
| * to accomodate DOS in free memory above and, s = present slot   *
 | |
| * number housing the disk controller card.)                      *
 | |
| *                                                                *
 | |
| ******************************************************************
 | |
| 
 | |
| 
 | |
| 
 | |
| 
 | |
| *::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
 | |
| *                                                                *
 | |
| *                        BOOT 0                                  *
 | |
| *                                                                *
 | |
| *----------------------------------------------------------------*
 | |
| *                                                                *
 | |
| * - relocatable code resident on the disk controller's ROM.      *
 | |
| * - because the code is relocatable, the disk controllers card   *
 | |
| *   can be used in different slots.  Execution begins at $Cs00,  *
 | |
| *   where s = slot number (ex. $C600 for card in slot 6).        *
 | |
| * - When BOOT0 is executed it:                                   *
 | |
| *       (1) builds a table of indices which are used to convert  *
 | |
| *           the disk bytes into 6/2 encoded bytes which are      *
 | |
| *           needed by RWTS to translate the disk bytes into      *
 | |
| *           normal memory bytes.                                 *
 | |
| *       (2) recalibrates the disk arm by moving it to trk0/sec0. *
 | |
| *       (3) reads BOOT1 into $800 to $8FF (from trk0/sec0).      *
 | |
| *       (4) jumps to $801 to begin the execution of BOOT1.       *
 | |
| *                                                                *
 | |
| *::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
 | |
| 
 | |
| (Cs00)
 | |
| BOOT0    LDX #$20     ;Controller card's identification byte.
 | |
| 
 | |
| * Construct a read-translate table which
 | |
| * we will need to convert disk bytes to
 | |
| * encoded memory bytes.  (The encoded memory
 | |
| * bytes later go trough further decoding to
 | |
| * convert them to normal memory bytes.)
 | |
| *
 | |
| * We construct the table by sequentially
 | |
| * incrementing (x) and testing it to see
 | |
| * if it meets the folowing criteria of a
 | |
| * disk byte:
 | |
| *   (1) it must have at least one pair of
 | |
| *       adjacent 1's in bits 0 to 6.
 | |
| *   (2) it must not have more than one pair
 | |
| *       of adjacent 0's in bits 0 to 6.
 | |
| * (Note that we use the x-value to represent
 | |
| * only the lower seven bits of a disk byte
 | |
| * because all disk bytes are negative.)
 | |
| *
 | |
| * Each time we find an (x) that represents
 | |
| * a simulated disk byte, we increment (y).
 | |
| * (Y) is then placed in the read table at an
 | |
| * offset of (x) from the beginning of the table.
 | |
| * The table generated is shown below.  The
 | |
| * empty bytes represent offsets where (x)
 | |
| * did not meet the criteria of a disk byte.
 | |
| * The values in parentheses represent the
 | |
| * (x)-values tested.
 | |
| *      36C-    00   01   --   --
 | |
| *             (16) (17) (18) (19)
 | |
| *      370-    02   03   --   04   05   06   --  --
 | |
| *             (1A) (1B) (1C) (1D) (1E) (1F) (20) (21)
 | |
| *      378-    --   --   --   --   07   08   --   --
 | |
| *             (22) (23) (24) (25) (26) (27) (28) (29)
 | |
| *      380-    --   09   0A   0B   0C   0D   --   --
 | |
| *             (2A) (2B) (2C) (2D) (2E) (2F) (30) (31)
 | |
| *      388-    0E   0F   10   11   12   13   --   14
 | |
| *             (32) (22) (34) (35) (36) (37) (38) (39)
 | |
| *      390-    15   16   17   18   19   1A   --   --
 | |
| *             (3A) (3B) (3C) (3D) (3E) (3F) (40) (41)
 | |
| *      398-    --   --   --   --   --   --   --   --
 | |
| *             (42) (43) (44) (45) (46) (47) (48) (49)
 | |
| *      3A0-    --   1B   --   1C   1D   1E   --   --
 | |
| *             (4A) (4B) (4C) (4D) (4E) (4F) (50) (51)
 | |
| *      3A8-    --   1F   --   --   20   21   --   22
 | |
| *             (52) (53) (54) (55) (56) (57) (58) (59)
 | |
| *      3B0-    23   24   25   26   27   28   --   --
 | |
| *             (5A) (5B) (5C) (5D) (5E) (5F) (60) (61)
 | |
| *      3B8-    --   --   --   29   2A   2B   --   2C
 | |
| *             (62) (63) (64) (65) (66) (67) (68) (69)
 | |
| *      3C0-    2D   2E   2F   30   31   32   --   --
 | |
| *             (6A) (6B) (6C) (6D) (6E) (6F) (70) (71)
 | |
| *      3C8-    33   34   35   36   37   38   --   39
 | |
| *             (72) (73) (74) (75) (76) (77) (78) (79)
 | |
| *      3D0-    3A   3B   3C   3D   3E   3F   --   --
 | |
| *             (7A) (7B) (7C) (7D) (7E) (7F)
 | |
| 
 | |
| 
 | |
| (Cs02)   LDY #0       ;Initialize the (y) index.
 | |
| (Cs04)   LDX #3       ;"3" is used for controller ID.  Any number
 | |
|                       ;between 0 and #$16 could be used.  Except
 | |
|                       ;for ID purposes, the operand isn't even
 | |
|                       ;relevant until it is #$16 (dec #22).
 | |
| BUILDTBL STX BT0SCRTH ;Save potential index seed in the zero page.
 | |
| (Cs06)
 | |
| 
 | |
| * Transfer (x) to (a) and test to see if it
 | |
| * meets the following disk byte criteria:
 | |
| *  (1) has at least one pair of adjacent 1's
 | |
| *      in bits 0 to 6.
 | |
| *  (2) has no more than one pair of adjacent
 | |
| *      0's in bits 0 to 6.
 | |
| 
 | |
| * Test for adjacent 1's.
 | |
| *
 | |
| * Note:  by comparing a shifted version of
 | |
| * the seed (in accumulator) with the original
 | |
| * version of the seed (in BT0SCRTH, $3C) we are
 | |
| * actually testing adjacent bits as shown below:
 | |
| *        Shifted:  b6   b5   b4   b3   b2   b1  b0   0
 | |
| *        Orignal:  b7   b6   b5   b4   b3   b2  b1  b0
 | |
| *                  -----------------------------------
 | |
| *        Testing: b6,7 b5,6 b4,5 b3,4 b2,3 b1,2 b0,1 -
 | |
| 
 | |
| (Cs08)   TXA
 | |
|          ASL
 | |
| (Cs0A)   BIT BT0SCRTH ;Conditions the z-flag of the status.
 | |
|                       ;(If any bits match, z-flag=1.)
 | |
| (Cs0C)   BEQ GETNEWX  ;Branch if value was illegal.
 | |
|                       ;Illegal value = z-flag=1 = no match = no
 | |
|                       ;adjacent 1's.
 | |
| 
 | |
| * Got at least 1 pair of adjacent 1's, so
 | |
| * now prepare to test for adjacent 0's.
 | |
| * (Note:  the previous "BIT" instruction
 | |
| * alters the z-flag in the status but
 | |
| * does not affect the accumulator.)
 | |
| (Cs0E)   ORA BT0SCRTH ;Merge shifted version of seed with orig.
 | |
| (Cs10)   EOR #$FF     ;Take 1's compliment of shifted version to
 | |
|                       ;swap 1's for 0's and 0's for 1's.
 | |
| (Cs12)   AND #%01111110 ;Throw away the hi and least significant
 | |
|                       ;bits so will be testing:
 | |
|                       ;    b5,6  b4,5  b3,4  b2,3 b1,2 b0,1.
 | |
| 
 | |
| * Test for pairs of adjacent 0's.
 | |
| * Remember, only 1 pair of adjacent 0's is
 | |
| * allowed.  Because we did a 1's compliment
 | |
| * of the merged bits above, a SET BIT NOW
 | |
| * DENOTES A PAIR OF ADJACENT 0's.  We can
 | |
| * therefore test for a pair of adjacent 0's
 | |
| * by shifting a bit into the carry:
 | |
| *  - if (c) = 1 = at least one pair of adjacent 0's
 | |
| *    is present.
 | |
| *  - if (c) = 1 AND the remaining byte = 0 then
 | |
| *    we have only one pair of adjacent 0's so
 | |
| *    value is legal.
 | |
| *  - if (c) = 1 AND the remaining byte < > 0, we
 | |
| *    have more than one pair of adjacent 0's so
 | |
| *    value is illegal.
 | |
| (Cs14)
 | |
| TESTCARY BCS GETNEWX  ;Always fall through on very first entry.
 | |
|                       ;If branch is taken, got illegal value
 | |
|                       ;because more than 1 pr of adjacent 0's.
 | |
| (Cs16)   LSR          ;Shift a bit into the carry (if carry set
 | |
|                       ;have at least 1 pr of adjacent 0's).
 | |
| (Cs17)   BNE TESTCARY ;Take branch when remaining byte is not
 | |
|                       ;zero.  Got at least 1 pr of adjacent 0's.
 | |
|                       ;Go test carry to see if another pair has
 | |
|                       ;already been detected.
 | |
| 
 | |
| * Index byte was legal.  We either got no
 | |
| * adjacent 0's or else only one pair of
 | |
| * adjacent 0's.
 | |
| (Cs19)   TYA          ;Store the counter that corresponds to a
 | |
| (Cs1A)   STA BTNIBL-$16,X ;legal nibble.  The first (x) value used
 | |
|                       ;is #$16, last is #$7F.  The first (y) value
 | |
|                       ;stored is 0, the last is #$3F.
 | |
| (Cs1D)   INY
 | |
| GETNEWX  INX
 | |
| (Cs1F)   BPL BUILDTBL ;Keep on trying to build table until (x)
 | |
|                       ;increments to #$80.
 | |
| 
 | |
| * Find out which slot the disk controller
 | |
| * card resides in.
 | |
| (Cs21)   JSR MONRTS   ;Jsr to an RTS to put the present address on
 | |
|                       ;the stack.  The hi byte of the present addr
 | |
|                       ;($Cs) tells us what slot (s) the card is
 | |
|                       ;located in.
 | |
| 
 | |
|                       * An RTS instruction in monitor ROM.
 | |
|                       (FF58)
 | |
|                       MONRTS   RTS
 | |
| 
 | |
| (Cs24)   TSX          ;Put the value of the stack pointer in (x).
 | |
| (Cs25)   LDA STACK,X  ;Get the hi byte of the controllers address
 | |
|                       ;($Cs) from the stack.
 | |
| (Cs28)   ASL          ;Multiply by 16 (throwing away original hi
 | |
|          ASL          ;nibble) so we are left with #$s0.
 | |
|          ASL
 | |
|          ASL          ;(a) = slot * 16.
 | |
|          STA SLT16ZPG ;Save slot*16 in a zero-page location.
 | |
| (Cs2E)   TAX          ;Set (x) = slot * 16 so we can index the
 | |
|                       ;base addresses associated with the drive
 | |
|                       ;functions.
 | |
| (Cs2F)   LDA Q7L,X    ;Set the READ mode.
 | |
|          LDA Q6L,X
 | |
|          LDA SELDRV1  ;Select drive 1.
 | |
| (Cs38)   LDA MTRON,X  ;Spin the disk.
 | |
| 
 | |
| * Move disk arm to track 0 by doing a
 | |
| * recalibration.  (That is, force the arm
 | |
| * against the stop by pretending that it
 | |
| * is presently at trk decimal 40.)
 | |
| * (The arm is moved by sequentially turning
 | |
| * a series of magnets off and on.)
 | |
| (Cs3B)   LDY #80      ;Pretend arm is at trk40 (dec 80 half-trks).
 | |
| MAGNTOFF LDA MAG0FF,X ;Turn the presently aligned magnet off.
 | |
| (Cs40)   TYA          ;Calculate the next magnet that should be
 | |
|                       ;turned on to suck the arm over.
 | |
| (Cs41)   AND #%00000011 ;Only keep the lower two bits because we
 | |
|                       ;only want a maximum value of 3 because
 | |
|                       ;there are only 4 magnets (which are indexed
 | |
|                       ;by values 0 to 3).  The sequence used in
 | |
|                       ;this loop is: 3,2,1,0,3...
 | |
| (Cs43)   ASL          ;Multiply by 2 so the index is directed to
 | |
|                       ;an address that turns a magnet ON.  The
 | |
|                       ;sequence used is:  6,4,2,0,6...0.
 | |
| (Cs44)   ORA SLT16ZPG ;Merge the index with the slot * 16 value.
 | |
|          TAX          ;Put the calculated index in (x).
 | |
|          LDA MAG0N,X  ;Turn the appropriate magnet ON.
 | |
| (Cs4A)   LDA #$56     ;Delay approximately 20 000 machine cycles
 | |
|                       ;(approximately 20 milliseconds.)  (Gives 
 | |
| (Cs4C)   JSR WAIT     ;arm time to align with energized magnet
 | |
|                       ;and reduces overshoot or bounce.)
 | |
| 
 | |
|                       * Monitor ROM's main delay routine.
 | |
|                       * Delay z number of cycles based on
 | |
|                       * the formula:
 | |
|                       *    z = ((5 * a^2) + (27 * a) +26) / 2
 | |
|                       *    where a = value in the accumulator on entry.
 | |
|                       (FCA8)
 | |
|                       WAIT     SEC          ;Prepare for subtraction.
 | |
|                       WAIT2    PHA          ;Save (a) on the stack.
 | |
|                       WAIT3    SBC #1       ;Keep on reducing (a) until it equals 0.
 | |
|                                BNE WAIT3
 | |
|                                PLA
 | |
|                                SBC #1       ;Reduce the original (a) down to 0 again.
 | |
|                                BNE WAIT2
 | |
|                       (FCB3)   RTS
 | |
| 
 | |
| (Cs4F)   DEY          ;Reduce trk # count.
 | |
| (Cs50)   BPL MAGNTOFF ;Not at trk0 yet, so go move arm some more.
 | |
| 
 | |
| * Initialize the buffer pointer and trk/sec values.
 | |
| * (On entry:  (x) = slot *16, (y) = #$FF & (a) = $00.)
 | |
| (Cs52)   STA PT2BTBUF ;Set the low byte of the buf pointer to $00.
 | |
|          STA BOOTSEC  ;Initialize for sector 0 on track 0.
 | |
|          STA BOOTRK
 | |
|          LDA #8       ;Set the hi byte of the buf pointer to $08
 | |
| (Cs5A)   STA PT2BTBUF+1 ;(that is, direct pointer at $800).
 | |
| 
 | |
| * Prepare to start reading a prologue.
 | |
| (Cs5C)
 | |
| BTRDSEC  CLC          ;(c) = 0 = signal to read an addr prologue.
 | |
| 
 | |
| * Begin reading a prologue.
 | |
| (Cs5D)
 | |
| PRSRVFLG PHP          ;Preserve the status denoting if reading
 | |
|                       ;address ((c)=0) or data ((c)=1) prologue.
 | |
| 
 | |
| * Look for an address prologue ("D5 AA 96")
 | |
| * or a data prologue ("D5 AA AD").
 | |
| (Cs5E)
 | |
| STARTSEQ LDA Q6L,X    ;Read a disk byte.
 | |
|          BPL STARTSEQ ;Wait for a full byte.
 | |
| BTRYD5   EOR #$D5     ;Is it a "D5"?
 | |
|          BNE STARTSEQ ;No - restart sequence.
 | |
| BTRYAA   LDA Q6L,X    ;Read next byte in header.
 | |
|          BPL BTRYAA   ;Wait for a full byte.
 | |
|          CMP #$AA     ;Is it an "AA"?
 | |
|          BNE BTRYD5   ;No - restart sequence.
 | |
|          NOP          ;Delay 2 cycles.
 | |
| BTRY96   LDA Q6L,X    ;Read third byte in header.
 | |
|          BPL BTRY96   ;Wait for a full byte.
 | |
|          CMP #$96     ;Is it a "96"?
 | |
| Cs78)    BEQ RDVLTKSC ;Found an address prologue, so now go read
 | |
|                       ;the vol, trk, sec values in the header.
 | |
| 
 | |
| * The first two bytes were "D5 AA".
 | |
| * The 3rd byte was not "96".  Therefore,
 | |
| * although we know this isn't an address
 | |
| * prologue, maybe it is a data prologue.
 | |
| (Cs7A)   PLP          ;Get the status back off the stack so we can
 | |
|                       ;check if we were looking for an address or
 | |
|                       ;data prologue.
 | |
| (Cs7B)   BCC BTRDSEC  ;Branch back to try again if we were looking
 | |
|                       ;for an address prologue but didn't find it.
 | |
| 
 | |
| * We were looking for a data prologue so
 | |
| * now compare the 3rd byte with that of a
 | |
| * data prologue.
 | |
| (Cs7D)
 | |
| BTRYAD   EOR #$AD     ;Is it an "AD"?
 | |
|          BEQ RDBTDATA ;Yes - found data prol so now read in data.
 | |
| (Cs81)   BNE BTRDSEC  ;No - go try again to find sequence 4 data.
 | |
| 
 | |
| * Read volume, track and sector values in
 | |
| * the address header.
 | |
| * Remember, @ of these pieces of information
 | |
| * are housed in two bytes in an odd-even encoded
 | |
| * format:   1rst byte:   1 b7  1 b5  1 b3  1 b1  (odd-encoded).
 | |
| *            2nd byte:   1 b6  1 b4  1 b2  1 b1  (even-encoded).
 | |
| * We must decode these bytes to check if we located
 | |
| * the correct volume, track and sector.
 | |
| (Cs83)
 | |
| RDVLTKSC LDY #3       ;Set counter for 3 decoded bytes.
 | |
| MOREBYTS STA BOOTEMP  ;Only relevant the last time through the
 | |
| (Cs85)                ;loop at which time it contains the decoded
 | |
| (Cs87)                ;track number read off the disk.
 | |
| BTRDODD  LDA Q6L,X    ;Read odd-encoded byte.
 | |
|          BPL BTRDODD  ;Wait for a full byte.
 | |
| (Cs8C)   ROL          ;Throw away the hi bit & shift the odd bits
 | |
|                       ;to their regular position.
 | |
| (Cs8D)   STA BT0SCRTH ;Save realigned version of odd-encoded byte.
 | |
| BTRDEVEN LDA Q6L,X    ;Read the even-encoded byte.
 | |
|          BPL BTRDEVEN ;Wait for a full byte.
 | |
|          AND BT0SCRTH ;Merge the two bytes.
 | |
|          DEY          ;Reduce counter for # of bytes to rebuild.
 | |
|          BNE MOREBYTS ;Branch if more bytes to patch back together.
 | |
|          PLP          ;Throw the status on the stack away.
 | |
|          CMP BOOTSEC  ;Is the sector read = sector wanted?
 | |
|          BNE BTRDSEC  ;No - go back to find correct sector.
 | |
|          LDA BOOTEMP  ;Get decoded trk # just read off disk.
 | |
|          CMP BOOTRK   ;Is trk found = trk wanted?
 | |
|          BNE BTRDSEC  ;No - go back to try again.
 | |
| (CsA4)   BCS PRSRVFLG ;ALWAYS - just read addr field, so now go
 | |
|                       ;         read the data prologue.
 | |
| 
 | |
| * Read the sector's data bytes.
 | |
| 
 | |
| * Read the first 86 ($56) bytes of the sector.  Use
 | |
| * the disk byte as an index to the BTNIBL table ($36C-$3D5).
 | |
| * Get the value from BTNIBL table & EOR it with the
 | |
| * previous EOR result (except on entry, use
 | |
| * #0 EOR BTNIBL-$96,Y) to produce a 2-encoded nibble.
 | |
| * (On entry, (a) = 0.)
 | |
| (CsA6)
 | |
| BTRDATA  LDY #$56     ;Read $56 (dec #86) bytes.
 | |
| KEEPCNT1 STY BT0SCRTH ;Save the counter.
 | |
| RDDSK1   LDY Q6L,X    ;Read a disk data byte.
 | |
|          BPL RDDSK1   ;Wait for a full byte.
 | |
| (CsAF)   EOR BTNIBL-$96,Y ;Use disk byte as an index to the table
 | |
|                       ;and EOR to decode to a 2-encoded nibble.
 | |
| (CsB2)   LDY BT0SCRTH ;Retrieve the counter.
 | |
|          DEY          ;Reduce the counter (& condition z-flag).
 | |
|          STA BUF300,Y ;Store 2-encoded nibble in page 3 buffer.
 | |
| (CSB8)   BNE KEEPCNT1 ;Branch if more bytes to read.
 | |
| 
 | |
| * Read the rest of the sector (256 disk bytes
 | |
| * remaining).  Use disk byte as an index to BTNIBL
 | |
| * table.  Get value from nibble table and EOR it
 | |
| * with previous EOR result to yeild a 6-encoded
 | |
| * nibble.  (On entry, (y) = 0.)
 | |
| (CsBA)
 | |
| KEEPCNT2 STY BT0SCRTH ;Set disk byte counter = 0.
 | |
| RDDSK2   LDY Q6L,X    ;Read a disk byte.
 | |
|          BPL RDDSK2   ;Wait for a full byte.
 | |
| (CsC1)   EOR BTNIBL-$96,Y ;Use disk byte as an index to the nibble
 | |
|                       ;table and EOR it with previous result to
 | |
|                       ;produce a 6-encoded nibble.
 | |
| (CsC4)   LDY BT0SCRTH ;Get index to buffer.
 | |
|          STA (PT2BTBUF),Y ;Store 6-encoded nibble in buffer.
 | |
|          INY          ;Kick up offset into buffer.
 | |
| (CsC9)   BNE KEEPCNT2
 | |
| 
 | |
| * Read and test the data checksum.
 | |
| * On entry, (a) = result of previous cummulative
 | |
| * EORing.  Therefore, any non-cancelling errors are
 | |
| * detected at the BNE instruction below.
 | |
| (CsCB)
 | |
| RDCK     LDY Q6L,X    ;Read the data checksum.
 | |
|          BPL RDCK     ;Wait for a full byte.
 | |
| (CsD0)   EOR BTNIBL-$96,Y ;EOR byte read with the previous
 | |
|                       ;cummulative result.
 | |
| TSTRERD  BNE BTRDSEC  ;Bad checksum so branch back to re-read.
 | |
| (CsD3)                ;(Also branches to here if got a good read
 | |
|                       ;but there are more sectors to read if the
 | |
|                       ;first byte of BOOT1 is modified to allow
 | |
|                       ;BOOT0 to read more than 1 sector (see
 | |
|                       ;comments below).
 | |
| 
 | |
| * Convert 6-and-2 encoded buffer bytes to
 | |
| * normal 8-bit memory bytes.
 | |
| (CsD5)   LDY #0       ;Initialize index to target memory byte.
 | |
| SETX56   LDX #$56     ;Set index to buf containing encoded bytes.
 | |
| DOWNX    DEX          ;Reduce index for next buffer byte.
 | |
|          BMI SETX56   ;Reset index to encoded buffer.
 | |
|          LDA (PT2BTBUF),Y ;Get (a) = 6-encoded nibble.
 | |
|          LSR BUF300,Y ;Put a bit from the 2-encoded buffer in (c)
 | |
|          ROL          ;and then roll it into the 6-encoded nibble.
 | |
|          LSR BUF300,Y ;Do the same with the next bit of the pair.
 | |
|          ROL
 | |
|          STA (PT2BTBUF),Y ;Store the re-constructed 8-bit byte in memory.
 | |
|          INY          ;Increase the offset to the buffer.
 | |
| (CsE9)   BNE DOWNX    ;Branch back if more bytes to reconstruct.
 | |
| 
 | |
| * Prepare to read in the next sector.
 | |
| * NOTE:  Normaly only trk0/sec0 (which
 | |
| * represents BOOT1) is read in by BOOT0.
 | |
| * The number of sectors read in by BOOT0 is
 | |
| * determined by the first byte of BOOT1.
 | |
| * Whereas BOOT1 resides in memory on an 48K
 | |
| * INITed disk at $B600 - $B6FF, we can zap a
 | |
| * disk at $B600 with the # of sectors we 
 | |
| * we would like BOOT0 to read in if we want
 | |
| * it to read in more than 1 sector.
 | |
| (CsEB)   INC PT2BTBUF+1 ;Just crossed page boundary, so kick up
 | |
|                       ;the hi byte of the target address.
 | |
| (CsED)   INC BOOTSEC  ;Set value for next sector wanted.
 | |
|          LDA BOOTSEC  ;Get next sector wanted.
 | |
| (CsF1)   CMP $800     ;Test if read enough sectors.
 | |
|                       ;First byte of image of BOOT1 normally
 | |
|                       ;contains #$01 which denotes only 1 sector
 | |
|                       ;(sec0/trk0) should be read in by BOOT0.
 | |
| (CsF4)   LDX SLT16ZPG ;(x) = slot *16.
 | |
|          BCC TSTRERD  ;Branch back to read more sectors.
 | |
| (CsF8)   JMP BT1EXC08 ;Jumps into BOOT1 (which was copied into
 | |
|          ------------ ;page 8 from trk0/sec0) to begin execution
 | |
|                       ;of BOOT1.
 | |
| 
 | |
| 
 | |
| *::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
 | |
| *                                                                *
 | |
| *                        BOOT 1                                  *
 | |
| *                                                                *
 | |
| *----------------------------------------------------------------*
 | |
| *                                                                *
 | |
| * - stored on trk0/sec0.                                         *
 | |
| * - IMAGE in memory on a 48K system resides at $B600 - $B6FF.    *
 | |
| * - read into $800 to $8FF by the disk controller ROM (BOOT0).   *
 | |
| * - execution begins at $801 & uses the controller's read sector *
 | |
| *   subroutine (BTRDSEC, Cs00, where s = slot # of card) to read *
 | |
| *   in trk0/sec9 down to trk0/sec1 ($BFFF --> $B600).            *
 | |
| * - NOTE:  In order to generate an accurate symbol table that    *
 | |
| *   can be applied 2 both the formatted & linear disassemblies,  *
 | |
| *   and because different assemblers vary in their abilities to  *
 | |
| *   accept certain OBJect values or re-ORG during assembly, the  *
 | |
| *   following special label system has been created:             *
 | |
| *   Image label/adr   Execution label/adr         Comments       *
 | |
| *   ---------------   -------------------   -------------------- *
 | |
| *   SEC2RDB6, $B600    SEC2RD08, $800      ;Defines # of secs to *
 | |
| *                                          ;be read in by boot0. *
 | |
| *   BT1EXCB6, $B601    BT1EXC08, $801      ;Start of boot1.      *
 | |
| *                                          ;Boot0 jumps to this  *
 | |
| *                                          ;location.            *
 | |
| *   SKPRELB6, $B61F    SKPREL08, $81F      ;Target labl 4 brnch. *
 | |
| *   PRP4B2B6, $B639    PRP4B208, $839      ;Target labl 4 brnch. *
 | |
| *     IMG8FD, $B6FD    BT1LDADR, $8FD      ;Boot1 load address.  *
 | |
| *                                          ;Varies from $B600 to *
 | |
| *                                          ;$BF00.  Eventually   *
 | |
| *                                          ;pts 2 start of boot2 *
 | |
| *                                          ;($B700).             *
 | |
| *     IMG8FF, $B6FF    BT1PG2RD, $8FF      ;Contains # of secs 2 *
 | |
| *                                          ;be read in when      *
 | |
| *                                          ;executing boot1. Also*
 | |
| *                                          ;doubles as logical   *
 | |
| *                                          ;sec #. Varies from:  *
 | |
| *                                          ;$09 --> $00 --> $FF. *
 | |
| * - As indicated above, SEC2RD08 ($800) defines the number of    *
 | |
| *   sectors to be read in by boot0.  This value is normally $01  *
 | |
| *   (meaning read only sector0 of track0).  However, you can zap *
 | |
| *   trk0/sec0/offset0 with a larger value ($01 to $10) to read   *
 | |
| *   more sectors from trk0.  Also note that most references say  *
 | |
| *   that SEC2RD08 normally contains a "$00" (rather than a       *
 | |
| *   "$01").  Because the test at $CsF6 uses the carry, either    *
 | |
| *   value will only cause one sector to be read in.  However,    *
 | |
| *   "$01" is the value used by DOS.  (Confusion may stem from    *
 | |
| *   the fact that Applesoft later stores a $00 in memory at      *
 | |
| *   $800.)                                                       *
 | |
| *                                                                *
 | |
| *::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
 | |
| 
 | |
| 
 | |
| (B600)  <============= image address.
 | |
| SEC2RDB6
 | |
| (800)  <============== execution address.
 | |
| SEC2RD08 HEX 01       ;Defines the number of sectors to be read
 | |
|                       ;in from track 0 during BOOT0.
 | |
| 
 | |
| (B601)
 | |
| BT1EXCB6
 | |
| (801)
 | |
| BT1EXC08 LDA PT2BUF+1 ;Get the next page to read in.
 | |
|          CMP #$09     ;Is it page 9 (ie. first page read by BOOT1)?
 | |
|          BNE SKPREL08 ;No - already used by BOOT1 to read page 9,
 | |
|                       ;so skip pointer initialization given below.
 | |
| 
 | |
| * Initialize the pointer (PT2RDSC) to point
 | |
| * at BOOT0's read sector subroutine (BTRDSEC,
 | |
| * $Cs5C, where s = slot #, normally $C65C).
 | |
| (B607)
 | |
| (807)    LDA SLT16ZPG ;(a) = slot *16.
 | |
|          LSR          ;Divide by 16.
 | |
|          LSR
 | |
|          LSR
 | |
|          LSR
 | |
|          ORA $C0      ;Merge with $C0 to get $Cs, where s=slot#.
 | |
|          STA PTR2RDSC+1 ;Store the hi byte of the address of the
 | |
|                       ;controller's read sector subroutine.
 | |
|          LDA #<BTRDSEC ;Get low byte of addr of subroutine.
 | |
|          STA PTR2RDSC ;Low byte is a constant (#$5C) and is
 | |
|                       ;therefore not variable with the slot used
 | |
|                       ;(as is the hi byte).
 | |
| 
 | |
| * Read in the 9 sectors represented by
 | |
| * trk0/sec9 down to trk0/sec1 into
 | |
| * $BFFF to $B600.  Note that the sectors
 | |
| * are read in from higher to lower memory.
 | |
| * These sectors contain the IMAGE of BOOT1,
 | |
| * part of the File Manager and almost all
 | |
| * of RWTS and it's associated routines.
 | |
| 
 | |
| * Calculate target address for the first sector
 | |
| * to be read in.
 | |
| (B615)
 | |
| (815)    CLC
 | |
|          LDA BT1LDADR+1 ;Contains $B6 on 48K slave.
 | |
|          ADC BT1PG2RD ;Contains #$09 on 48K slave.
 | |
|          STA BT1LDADR+1 ;(a) = #$BF on 48K slave.
 | |
| 
 | |
| * Determine the number of sectors left to read,
 | |
| * the physical sector number & the target address.
 | |
| * Then, go read in the next sector.
 | |
| (B61F)
 | |
| SKPRELB6
 | |
| (81F)
 | |
| SKPREL08 LDX BT1PG2RD ;(x) = pages left to read in less 1.
 | |
|                       ;(Also doubles as logical sector number.
 | |
|                       ;Varies from $09 --> $FF.)
 | |
|          BMI PRP4B208 ;When (x) = $FF, we have read all the
 | |
|                       ;sectors in so go exit.
 | |
|          LDA PHYSECP8-$AE00,X ;Convert the logical sector number
 | |
|                       ;to a physical sector number. (Equivalent
 | |
|                       ;to "LDA $84D,X".)
 | |
|          STA BOOTSEC  ;Store physical sector number in page0.
 | |
|          DEC BT1PG2RD ;Reduce sectors (pages) left to read for
 | |
|                       ;the next time around.
 | |
|          LDA BT1LDADR+1 ;Point the buffer pointer at the target
 | |
|          STA PT2BTBUF+1 ;address. (Varies from $BF to $B6 on a
 | |
|                       ;48K slave.)
 | |
|          DEC BT1LDADR+1 ;Reduce the hi byte of the I/O buffer for
 | |
|                       ;the next time around. (Varies from $BF to
 | |
|                       ;$B5 on a 48K slave.)
 | |
|          LDX SLT16ZPG ;Set (x) = slot*16.
 | |
| (836)    JMP (PTR2RDSC) ;Equivalent to "JMP ($8FD)" or "JMP $Cs5C"
 | |
| (B636)   ------------ ;to go read in the next sector.
 | |
|                       ;***************** NOTE *******************
 | |
|                       ;* GOES TO BT1EXC08 ($801) AFTER @ SECTOR *
 | |
|                       ;* IS READ IN. (BT1EXCB6 is a carbon copy *
 | |
|                       ;* of BT1EXC08.)                          *
 | |
|                       ;******************************************
 | |
| 
 | |
| * Prepare for BOOT2.
 | |
| (B639)
 | |
| PRP4B2B6
 | |
| (839)
 | |
| PRP4B208 INC BT1LDADR+1 ;Point at the load address for BOOT2.
 | |
|          INC BT1LDADR+1 ;(After the INCs = $B7 on 48K slave.)
 | |
| 
 | |
| * Set full screen text & designate the
 | |
| * keyboard and screen as the I/O devices.
 | |
| (B63F)
 | |
| (83F)    JSR SETKBD   ;Simulate an IN#0.  (See dis'mbly below.)
 | |
|          JSR SETVID   ;Simulate an PR#0.  (See dis'mbly below.)
 | |
|          JSR INIT     ;Simulate a "TEXT" statement. (See dis'mbly
 | |
|                       ;in APPLE II REFERENCE MANUAL at $FB2F.)
 | |
|          LDX SLT16ZPG ;(x) = slot * 16.
 | |
|      
 | |
| * Go to BOOT2.
 | |
| (B64A)
 | |
| (84A)    JMP (BT1LDADR) ;Jump to BOOT2 ($B700 on 48K slave).
 | |
|          --------------
 | |
| 
 | |
| 
 | |
| *::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
 | |
| *                                                                *
 | |
| *                       BOOT2                                    *
 | |
| *                                                                *
 | |
| *----------------------------------------------------------------*
 | |
| *                                                                *
 | |
| * - Reads in the rest of DOS starting at trk02/sec04 down to     *
 | |
| *   trk00/sec0A ($B5FF --> $9B00).  (P.S. Sectors 0A and 0B of   *
 | |
| *   track 0 are empty ($9CFF - $9B00).)                          *
 | |
| * - After the rest of DOS is read in, execution jumps to DOS's   *
 | |
| *   coldstart routine (DOSCLD, $9D84).                           *
 | |
| * - Note that on entry:  (x) = slot * 16                         *
 | |
| *                                                                *
 | |
| *::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::*
 | |
| 
 | |
| 
 | |
| * Prepare RWTS's input-output block (IOB)
 | |
| * and designate the number of sectors to read.
 | |
| (B700)
 | |
| BOOT2    STX IBSLOT   ;(x) = slot*16 wanted.
 | |
|          STX IOBPSN   ;Last-used slot*16 value.
 | |
|          LDA #1       ;Set both the last-used and wanted drives
 | |
|          STA IOBDPDN  ;as drive #1.
 | |
|          STA IBDRVN
 | |
|          LDA NMPG2RD  ;Set number of pages (ie. secs) to read.
 | |
|          STA BT2PGCTR ;Counter for for number of pages to read.
 | |
|          LDA #2       ;Start with trk02/sec04.
 | |
|          STA IBTRK    ;Track.
 | |
|          LDA #4
 | |
|          STA IBSECT   ;Sector.
 | |
| (B71E)   LDY BT1MGADR+1 ;(y) = hi byte of the address of the image
 | |
|                       ;of BOOT1 (#$B6 on a 48K slave).
 | |
| (B721)   DEY          ;Define I/O buf as 1 page below boot1.
 | |
|          STY IBBUFP+1
 | |
|          LDA #1       ;Opcode for read.
 | |
| (B727)   STA IBCMD
 | |
| 
 | |
| * Convert from (x) = slot*16 to (x) = slot.
 | |
| (B72A)   TXA          ;(x) = slot * 16.
 | |
|          LSR          ;Divide by 16.
 | |
|          LSR
 | |
|          LSR
 | |
|          LSR
 | |
| (B72F)   TAX          ;(x) = slot.
 | |
| 
 | |
| * Initialize the page-four locations with
 | |
| * the track numbers associated with the drives.
 | |
| (B730)   LDA #0
 | |
|          STA TRK4DRV2,X
 | |
| (B735)   STA TRK4DRV1,X
 | |
| 
 | |
| * Call the routine to read in the rest of DOS.
 | |
| (B738)   JSR RWPAGES
 | |
| 
 | |
|                       * READ/write a group of pages.
 | |
|                       (B793)
 | |
|                       RWPAGES  LDA ADROFIOB+1 ;Init (a)/(y) with the hi/low bytes of the
 | |
|                                LDY ADROFIOB ;addr of RWTS's IOB for entry to RWTS.
 | |
|                       (B799)   JSR ENTERWTS ;Enter RWTS to read/write sector.
 | |
| 
 | |
|                                             * Entry to RWTS.
 | |
|                                             (B7B5)
 | |
|                                             ENTERWTS PHP          ;Save status register on the stack.
 | |
|                                             (B7B6)   SEI          ;Set the interrupt disable flag to prevent
 | |
|                                                                   ;any further maskable interrupts when doing
 | |
|                                                                   ;real-time programming.
 | |
|                                             (B7B7)   JSR RWTS     ;Enter RWTS proper to do the operation:
 | |
|                                                                   ; $00=seek, $01=read, $02=write, $03=format.
 | |
| 
 | |
|                                                                   * RWTS proper.
 | |
|                                                                   (BD00)
 | |
|                                                                   RWTS     .
 | |
|                                                                            .
 | |
|                                                                   (See dis'mbly in RWTSDRVR using READ.)
 | |
|                                                                            .
 | |
|                                                                            .
 | |
|                                                                            (RTS)
 | |
| 
 | |
|                                             (B7BA)   BCS ERRENTER ;Operation was NOT successful.
 | |
|                                                      PLP          ;Retrieve saved status off the stack.
 | |
|                                             (B7BE)   RTS
 | |
|                                                      ============
 | |
| 
 | |
|                                             (B7BF)
 | |
|                                             ERRENTER PLP          ;Throw the saved status off the stack.
 | |
|                                                      SEC          ;Signal operation was unsuccessful.
 | |
|                                             (B7C1)   RTS
 | |
|                                                      ============
 | |
| 
 | |
|                       (B79C)   LDY IBSECT   ;Get # of the sector just read or written.
 | |
|                       (B79F)   DEY          ;Set value for next sector 2 read.  When
 | |
|                                             ;executing BOOT1, goes from $09 to $FF.
 | |
|                       (B7A0)   BPL SAMETRK  ;Branch to use the same track.
 | |
| 
 | |
|                       * Start a new track.
 | |
|                       (B7A2)   LDY #$0F     ;Start with sector 15.
 | |
|                                NOP
 | |
|                                NOP
 | |
|                       (B7A6)   DEC IBTRK    ;Reduce number of track wanted.
 | |
| 
 | |
|                       * Reduce the addr of the target memory location.
 | |
|                       * Test if more sectors to read.
 | |
|                       (B7A9)
 | |
|                       SAMETRK  STY IBSECT   ;Store the sector wanted.
 | |
|                                DEC IBBUFP+1 ;Reduce buf addr of target memory location.
 | |
|                                DEC BT2PGCTR ;Reduce counter for # of sectors to read.
 | |
|                                BNE RWPAGES  ;More sectors to read.
 | |
|                       (B7B4)   RTS
 | |
| 
 | |
| (B73B)   LDX #$FF     ;Completely clear out the stack.
 | |
|          TXS
 | |
|          STX IBVOL    ;Set the volume to $FF (compliment of 0).
 | |
| (B741)   JMP CLOBCARD
 | |
|          ------------
 | |
| 
 | |
| * Patch to clobber the language card
 | |
| * and set video output.
 | |
| (BFC8)
 | |
| CLOBCARD JSR SETVID   ;Select the video screen.
 | |
| 
 | |
|                       * Monitor ROM's routine to designate the
 | |
|                       * video screen as the output device.
 | |
|                       * (Simulate a "PR#0" statement.)
 | |
|                       (FE93)
 | |
|                       SETVID   LDA #0       ;Designate slot 0.
 | |
|                       OUTPORT  STA A2L
 | |
|                       OUTPRT   LDX #<CSW    ;Set offset from start of zero page to OUTPUT hook.
 | |
|                                LDY #<COUT1
 | |
|                       IOPRT    LDA A2L
 | |
|                                AND #$0F
 | |
|                       (FE9F)   BEQ IOPRT1   ;ALWAYS.
 | |
| 
 | |
|                       (FEA7)
 | |
|                       IOPRT1   LDA #>COUT1  ;(Hi byte of addr of KEYIN & COUT1 are equal.)
 | |
|                       IOPRT2   STY LOC0,X   ;Set CSW: COUT1.
 | |
|                                STA LOC1,X
 | |
|                       (FEAD)   RTS
 | |
| 
 | |
| (BFCB)   LDA $C081    ;Write enable the RAM card.
 | |
|          LDA $C081    ;(Read motherboard / write card bank 2.)
 | |
|          LDA #0       ;Set the language identifying byte on the
 | |
| (BFD3)   STA BASICCLD ;card to $00 so if card is tested (during
 | |
|                       ;an FP command), the machine will be forced
 | |
|                       ;to use the motherboard version of FP.
 | |
| (BFD6)   JSR CONTCLOB ;Now clobber the 80-column card.
 | |
| 
 | |
|                       * Clobber the 80-column card.
 | |
|                       (BA76)
 | |
|                       CONTCLOB LDA #$FF     ;Set the mode flag for card.
 | |
|                                STA $4FB     ;Scratch pad memory for slot 3 peripheral.
 | |
|                                STA $C00C    ;Turn off the alternate character set.
 | |
|                                STA $C00E
 | |
|                       (BA81)   JMP INIT     ;Simimulate a TEXT statement.
 | |
|                                ------------
 | |
| 
 | |
|                       * Monitor ROM's Init routine.
 | |
|                       (FB2F)
 | |
|                       INIT     .
 | |
|                                .
 | |
|                       (See dis'mbly in APPLE II REFERENCE MANUAL.)
 | |
|                                .
 | |
|                                .
 | |
|                       - simulate a text statement.
 | |
|                         (Ie. set window to full screen text.)
 | |
|                                .
 | |
|                                .
 | |
|                                (RTS)
 | |
| 
 | |
| (BFD9)   JMP BK2BOOT2 ;Return to original part of BOOT2.
 | |
|          ------------
 | |
| 
 | |
| * Return back to original part of BOOT2.
 | |
| (B744)
 | |
| BK2BOOT2 JSR SETKBD   ;Select the keyboard.
 | |
| 
 | |
|                       * Monitor ROM's routine to designate the
 | |
|                       * keyboard as the input device.
 | |
|                       * (Simulate a "IN#0" statement.)
 | |
|                       (FE89)
 | |
|                       SETKBD   LDA #0       ;Pretend using slot 0.
 | |
|                       INPORT   STA A2L
 | |
|                       INPRT    LDX #<KSW    ;Set offset from start of zero page to INPUT hook.
 | |
|                                LDY #<KEYIN
 | |
|                       (FE91)   BNE IOPRT    ;ALWAYS.
 | |
| 
 | |
|                       (FE9B)
 | |
|                       IOPRT    LDA A2L
 | |
|                                AND #$0F
 | |
|                       (FE9F)   BEQ IOPRT1   ;ALWAYS.
 | |
| 
 | |
|                       (FEA7)
 | |
|                       IOPRT1   LDA #>COUT1  ;(Hi byte of the addr of KEYIN & COUT1 are equal.)
 | |
|                       IOPRT2   STY LOC0,X   ;Set KSW: KEYIN.
 | |
|                                STA LOC1,X
 | |
|                       (FEAD)   RTS
 | |
| 
 | |
| (B747)   JMP DOSCOLD  ;Jump into DOS's coldstart routine to build
 | |
|          ------------ ;the DOS buffers and the page-three vector
 | |
|          .            ;table and then run the "HELLO" program.
 | |
|          .            ;*************** N O T E *****************
 | |
|          .            ;* This instruction is a hacker's dream. *
 | |
|          .            ;* For instance, you can change the jump *
 | |
|          .            ;* to point to you own password or time- *
 | |
|          .            ;* bomb routine that you have deviously  *
 | |
|          .            ;* embedded in an unused section of DOS. *
 | |
|          .            ;*****************************************
 | |
|          .
 | |
|          .
 | |
|          .
 | |
| *---------------------*
 | |
| * SEE dis'mbly titled *
 | |
| *    "DOSCOLDSTART"   *
 | |
| *---------------------*
 | |
|          .
 | |
|          .
 | |
|          . |