1 EMULATING THE APPLE HIGH SPEED SCSI CARD: AN EXERCISE IN DIGITAL ARCHAEOLOGY
5 ~~==< Brought to you in Glorious 80-Column Monospace-o-Vision(TM) >==~~
11 While reading 4am's Twitter feed one day, he talked about his "Pitch Dark" hard
12 drive image, which looked incredibly cool and like something that I would very
13 much be interested in. But in reading about it, I came across a seemingly
14 throwaway line about how all decent emulators can run them, which, sadly,
15 Apple2 could not at the time. And so, in order to save Apple2 from indecency
16 (and because I wanted to see if I could get 4am's "Pitch Dark" to work because
17 it looked cool and interesting), I set about for finding some documentation on
18 how hard drives interfaced to Apple IIs--and ran into a complete dearth of
19 information. There were little things sprinkled around here and there, but
20 nothing of any deep, satisfying, technical significance.
23 In Order To Run A Hard Drive Image, You Must First Create The Universe
24 ----------------------------------------------------------------------
26 While it's a nice bit of hyperbole, it's not exactly true that you have to
27 first create the Universe, as fortunately, that part has largely been taken
28 care of. However, you still have to figure out how to emulate it if you are
29 keen on running a hard drive image on your emulator of choice. And in so
30 doing, you have to figure what the requirements are; what the minimal pieces
31 are that are required to have a functioning hard drive system; you also have to
32 figure out how that system talks to the emulated computer. And that all
33 requires information. I wasn't asking for much, but something along the lines
34 of Jim Sather's "Understanding The Apple IIe" for hard drives would have been a
38 The Next Part, In Which Nice Things To Have Are Not Forthcoming
39 ---------------------------------------------------------------
41 Unfortunately, Jim Sather, and nobody else as far as I can tell, ever wrote
42 such a document, and so I did what any lazy programmer would do: I took a look
43 at some other project's source--in this case, AppleWin's source. I didn't
44 really *want* to look at it, having looked at it before and recoiled in horror
45 at the sight, but, my search-fu apparently being not up to the task of finding
46 relevant information drove me to it. And looking at it didn't really provide
47 any illumination; to me it looked like some kind of hacky thing and I wasn't
48 interested in that kind of approach at all--so I abandoned the idea. As I dug
49 a little deeper into the minute literature that existed as such on the subject,
50 I learned that pretty much any time you wanted to hook up a hard drive to your
51 Apple II, you had to use an interface card, and typically that meant some kind
52 of SCSI card. And looking here, there was no shortage of SCSI cards that you
53 could use to hook up your hard drive therewith.
55 So, that being a promising looking path to pursue on the road to this
56 particular perdition, the question then became, which one should I choose? At
57 first I thought the RAMFast card would fit the bill as it seemed to be very
58 popular, but there was literally no technical infomation on the thing. The
59 Apple SCSI card looked promising, but then I saw that it "ghosted" a slot,
60 meaning that it would have to occupy two consecutive slots in order to work and
61 I didn't much care for that. And so, after looking at, and rejecting, card
62 after card for pretty much the same reason, I settled on the Apple High Speed
63 SCSI card for a few reasons--one, it was purportedly fast; two, it worked on
64 the Apple IIe (as well as the IIgs, but I didn't really care that much about
65 that to be honest); three, it had a users manual that wasn't completely devoid
66 of technical information; four, it had a schematic; and five, it had a firmware
67 image. This looked like a promising start--how hard could it be to make this
71 Things Aren't Exactly Hard, But They Aren't Exactly Soft Either
72 ---------------------------------------------------------------
74 One of the necessary things that I didn't have out of all of that was good
75 information on how the thing worked. I knew that it was a SCSI card, and I
76 knew that it talked to the SCSI bus using an NCR 53C80 chip, but I had no idea
77 exactly how. But I did have something that *did* know how to talk to it: the
78 firmware for the card.
80 Now when you take a look at the firmware, the first thing you notice is that
81 it's 32K in size--which is *much* larger than the typical 256 bytes that you
82 encounter when looking at Apple II card drivers. It also happens to be quite a
83 bit larger than the 2K "bonus" space that Apple II cards have available to them
84 in the $C800 to $CFFF address space. So what gives?
86 Fortunately for me, Apple2 has a built-in disassembler (which will probably
87 stay in for all time, as it turns out to be a very useful thing to have on
88 hand), and so I split that out into a stand-alone command line driven program,
89 called d65c02, in order to be able to disassemble such things as device driver
90 firmware blobs. It isn't fancy, it doesn't do any analysis on what is code and
91 what is data, but it gets the job done in turning incomprehensible binary
92 gibberish (except to certain mad geniuses who will go heretofore unnamed) into
93 human readable ASCII gibberish. Thus I used said tool to disassemble the
96 Pulling up the results in my text editor, I could see that at least the front
97 of the listing looked like it could plausibly be code that would go into the
98 usual 256 byte card slot address space of $Cx00 to $CxFF, where x ranges from 1
99 to 7 depending on the slot number. Looking further, I could see this first 256
100 bytes of code was repeated three times, meaning that this was a good candidate
101 for the slot device code. I could also see that it was written as relocatable
102 code, and it contained this little tidbit:
104 001B: A9 60 LDA #$60 ; Stuff an RTS into RAM somewhere
105 001D: 8D F8 07 STA $07F8
106 0020: 20 F8 07 JSR $07F8 ; Jump there and return in order to get evidence
107 ; of where in memory we did it from
108 0023: BA TSX ; Retrieve the stack pointer
109 0024: BD 00 01 LDA $0100,X ; Get the hi byte of the address we just pushed on
110 ; the stack in order to come back here
111 0027: 8D F8 07 STA $07F8 ; & save it for later perusal
113 which meant that it was an excellent candidate for the slot device code. But
117 A Short Digression Into Why Slot Code Must Be Relocatable
118 ---------------------------------------------------------
120 Slot code must be relocatable because such a card may be installed into any
121 given slot in an Apple II--which means its code will show up anywhere from
122 $C100 to $C700 (it always shows up on a page boundary). By virtue of this, it
123 also means that the I/O address for the card will also show up in the
124 corresponding $C090 to $C0F0 address range (it always shows up on a 16-byte
125 boundary). And so, because of this, you have to write your slot code in such a
126 way that it will work regardless of which slot it's installed in, which means
127 the code must be relocatable--which ultimately means you can't use any JMP
128 instructions to addresses in your driver, and you can't use absolute addressing
129 to refer to stuff in the slot address space.
131 So, using the above code, a clever coder can figure out what slot their code is
132 executing in and they can then use that knowledge to figure out which is the
133 proper I/O range to use for the card. All this being necessary in order to
134 make a seamless experience for the end user of the card.
137 The Next Part, In Which 32K Is Still Larger Than 256
138 ----------------------------------------------------
140 So, in looking at the code that comes after the Code Which Looks Like It
141 Belongs In Slot Memory (which makes the wonderful acronym CWLLIBISM), I noticed
142 that it seemed to be organized in 1K chunks. And further persual of said
143 chunks made it seem very likely that they resided in the $CC00 to $CFFF memory
144 space. However, the "extra" memory space given to cards to use starts 1K
145 earlier--at $C800. What could this mean?
147 Well, in looking at the schematic for the card, one not only finds the 32K ROM
148 chip, but also an 8K static RAM. Which means that it's very likely that the
149 address space from $C800 to $CBFF is mapped to that 8K static RAM. But 8K is
150 larger than 1K; how does that work?
152 As it turns out, it's bank switched, but I didn't know it at the time--we'll
153 get to that eventually. In the meantime, with further perusal of the code (the
154 code gets perused quite a bit), it seems very likely that the 1K address range
155 from $C800 to $CBFF is said RAM as that range is written to by the 1K code
156 chunks quite frequently.
158 Finding that the code in the firmware is divvied up into 1K chunks would seem
159 to imply that it's bank switched into the $CC00 to $CFFF range. And in looking
160 at the CWLLIBISM, we see the following:
162 005C: A9 0B LDA #$0B ; Get 11 in the accumulator
163 005E: AE 08 C8 LDX $C808 ; Get offset to proper I/O space in X
164 0061: 5A PHY ; Save Y on the stack for later
165 0062: A8 TAY ; Copy the accumulator to Y
166 0063: 29 1F AND #$1F ; Strip off the upper three bits
167 0065: 9D 6E C0 STA $C06E,X ; & write to card I/O location $E
169 which implies it heavily. Taking the number put into the accumulator and then
170 masking out the lower 5 bits creates a range that goes from 0 to 31, which is
171 32 distinct values, which corresponds to 32 1K chunks of code.
173 The above code, which is part of the initialization of the card, heavily
174 implies that it's selecting a 1K chunk of code from bank 11 (counting from
175 zero, naturally) to put into the $CC00 to $CFFF address range. And so we get
176 to(*) look there for a start.
178 (*) While changing 'have to' to 'get to' can make life awesome in many ways,
179 this is far from a universal truth. 'Getting to' have one's arm amputated is
183 The Next Part, In Which We Sadly Bid Adeiu To CWLLIBISM
184 -------------------------------------------------------
186 But before we do that, in order to understand what's going on in those wicked
187 little 1K chunks of code, we should first take a closer look at CWLLIBISM. So
190 0000: A2 20 LDX #$20 ; The bytes after the LDX # identify this card as
191 0002: A2 00 LDX #$00 ; being capable of SmartPort calls, and the $82 at
192 0004: A2 03 LDX #$03 ; $FB further identifies it as a SCSI card ($2)
193 0006: A2 00 LDX #$00 ; that supports extended calls ($8).
195 The way that I was able to find out that this seemingly useless bit of code was
196 a way of identifying SmartPort capable cards was in the serendipitous find of
197 the "Technical Manual for the Apple SCSI Card"(*), which, while helpful in some
198 ways, was almost completely useless in trying to figure out the what the card
201 (*) No relation to the Apple High Speed SCSI Card
203 0008: 2C 58 FF BIT $FF58 ; Check byte in ROM (usually, an RTS lives here)
204 000B: 70 05 BVS $0012 ; Bit 6 set? >> $12 (which means, this branch
207 This little tidbit checks a ROM location that usually carries an RTS (at least
208 it does in the Apple IIe), which is $60. Which means that the following BVS
209 will always be taken and skip over the following:
211 000D: 38 SEC ; ProDOS entry point
212 000E: B0 01 BCS $0011 ; Branch over the following CLC
213 0010: 18 CLC ; SmartPort DISPATCH
214 0011: B8 CLV ; Signal we're doing normal I/O, not init code
216 So this clever little bit here, according to the "Technical Manual for the
217 Apple SCSI Card", sets some flags so that later on in the firmware, it can
218 discern whether it's being called from ProDOS (in which the carry flag will be
219 set) or if it's a SmartPort call (in which the carry flag will be clear).
220 Either way, the overflow flag is cleared to let the firmware know that this is
221 a request to talk to the drive, and not initialization. Initialization skips
222 over this code and ends up here:
224 0012: D8 CLD ; Clear the decimal flag, to prevent bad math
225 0013: 08 PHP ; Save the carry & overflow flags for later
226 0014: 78 SEI ; Turn IRQs off
227 0015: AD FF CF LDA $CFFF ; Turn INTC8ROM off (puts card in $C800-CFFF)
228 0018: 8D 00 CC STA $CC00 ; ???
230 This bit of code is a bit of housekeeping; making sure the decimal flag isn't
231 set so that ADC & SBC both work as expected, saving the flags register so that
232 the firmware code later can determine whether it's an initialization call or a
233 regular I/O call, making sure that IRQs don't happen while in the firmware
234 code, and turning on the "extra" addresses in the $C800 to $CFFF range.
236 The store to $CC00 is mysterious, as it's a ROM location and stores to ROM
237 locations are usually void and of null effect. This likely means that it's
238 some kind of soft-switch that controls something in card, but exactly what
239 would require a few things that I don't have, namely: the contents of the two
240 PALs on the card (which sit between the address lines of the slot and the rest
241 of the card), and a description of what the ports on the Sandwich II do (the
242 chip that sits between the Apple IIe proper and the NCR 53C80). So, moving
245 001B: A9 60 LDA #$60 ; See where we're executing from
246 001D: 8D F8 07 STA $07F8
247 0020: 20 F8 07 JSR $07F8
249 0024: BD 00 01 LDA $0100,X ; Get the address we just pushed on the stack
250 0027: 8D F8 07 STA $07F8 ; Save it
252 We've seen this already, this is the code that determines which slot it's
253 sitting in. Say, for example, that it's sitting in slot 7; the byte that it
254 will retrieve from the stack will be $C7 (for the sake of completeness, the lo
255 byte will be $22--as to why, this is left as an exercise for the reader). In
256 order to turn that into something that it can use to hit the proper slot I/O
257 addresses, it does the following:
259 002A: 29 0F AND #$0F ; Get the lo nybble
260 002C: 0A ASL A ; Multiply it x16
265 0031: 69 20 ADC #$20 ; Add $20 to it for some reason
266 0033: AA TAX ; & stick in the X register
268 The important part of the $C7 hi byte of the address we found through
269 cleverness and trickery is the slot number, which will always fall in the lower
270 4 bits. And, in order to be useful to find the correct slot I/O address range,
271 that slot number needs to be multiplied by 16, as each of the slot I/O address
272 ranges cover exactly sixteen bytes. Note that masking off the bottom 4 bits,
273 as is done with the AND #$0F instruction, is unnecessary as the four ASL A
274 instructions after it will necessarily shift the top four bits out of the
277 The one thing that stands out as not typical of this kind of device driver code
278 is the adding of $20 to the index. Typically, writers of this kind of I/O code
279 will use $C080 to $C08F (plus the contents of the X register to reach the
280 correct slot I/O range) as the base address for slot I/O, but, for some reason,
281 the writers of this card's firmware chose to use $C060 to $C06F, thus
282 necessitating the addition of $20 to the value in the X register to reach the
283 correct range for slot I/O.
285 0034: A9 00 LDA #$00 ;
286 0036: 9D 6E C0 STA $C06E,X ; Select bank #0 (register $E, lower 5 bits)
288 003B: 9D 6F C0 STA $C06F,X ; Store a $F in register $F
289 003E: 8E 08 C8 STX $C808 ; Put slot # at $C808 (banked RAM in $C800-CBFF)
290 0041: 9C 09 C8 STZ $C809 ; Put zero at $C809
291 0044: 9C F2 C8 STZ $C8F2 ; & $C8F2
293 One thing I forgot to mention is that the Apple High Speed SCSI card is only
294 usable by enhanced Apple IIe and IIgs machines, and that's because it relies on
295 instructions only found in the 65C02 like STZ and PHY; a regular 6502 will not
296 even remotely do the same things that those instructions do on the 65C02--so
299 At any rate, the above code does some writing to the slot I/O address range and
300 sets up some values in the card's static RAM, including saving the contents of
301 the X register for later.
303 0047: A2 22 LDX #$22 ; Transfer 35 bytes from ZP ($40) to $C82D
304 0049: B5 40 LDA $40,X
305 004B: 9D 2D C8 STA $C82D,X
307 004F: 10 F8 BPL $0049
309 This bit of code transfers 35 bytes in page zero RAM to the card's static RAM,
310 presumably to restore them later.
312 0051: AD F8 07 LDA $07F8 ; Get original $Cx byte again
313 0054: 8D 01 C8 STA $C801 ; Put it in $C801
314 0057: A9 61 LDA #$61 ;
315 0059: 8D 00 C8 STA $C800 ; Put $61 in $C800 (= $Cx61)
317 005E: AE 08 C8 LDX $C808 ; Get X from $C808
319 This little bit of code sets up for the code that comes below; it sets up
320 locations $C800-1 as a location for an indirect jump that seems to happen a lot
321 in the 1K chunks that come later. The address it sets up as the jump target is
322 the code that comes next:
324 0061: 5A PHY ; Save Y (follow on bank, passed in by caller)
325 0062: A8 TAY ; Save A register
326 0063: 29 1F AND #$1F ; Mask off the lower 5 bits
327 0065: 9D 6E C0 STA $C06E,X ; First time, select bank 11:0 (I/O register $E)
328 0068: 98 TYA ; Restore the A register
329 0069: 29 E0 AND #$E0 ; Mask off the upper 3 bits
330 006B: 4A LSR A ; & shift them down
334 006F: A8 TAY ; Use as an index into a table (Y x 2)
336 What this does is save the Y register on the stack, then separates the
337 accumulator into a upper 3-bit part and a lower 5-bit part. The lower 5 bits
338 go into I/O slot register $E, which presumably selects which 1K chunk of code
339 will appear in the $CC00 to $CFFF address range while the upper 3 bits are used
340 as an index into a table that appears near the end of each 1K chunk:
342 0070: B9 F0 CF LDA $CFF0,Y ; Get address of current 1K bank
343 0073: 85 54 STA $54 ; & stuff it into $54/55
344 0075: B9 F1 CF LDA $CFF1,Y
347 So it uses the Y register as index into the current selected bank's $CFF0
348 address range and stuffs them into $54 and $55, so that it can jump to the
349 address at some point.
351 007A: AD F8 07 LDA $07F8 ; Get original $Cx byte again
352 007D: A8 TAY ; Put it in Y
353 007E: 48 PHA ; Put it to the stack
355 0081: 48 PHA ; Push $86: return address is now $Cx87
357 What this does is set up the stack for what I'm going to name (for lack of a
358 better term, or any at all to be honest) an "RTS call". This takes advantage
359 of how the CPU uses the stack to return execution to the instruction after a
360 JSR instruction: when the CPU encounters a JSR opcode, it pushes the the
361 location of the program counter, plus two, onto the stack before loading the
362 program counter with the address that comes after the JSR. When an RTS opcode
363 is then encountered, it restores the program counter from the stack and adds
364 one to it before resuming execution.
366 The upshot of this is that you can transfer execution of a program from one
367 place to the next, without using JMP, JSR or branch instructions by simulating
368 this behavior--which also turns out to be a necessity when you're writing
369 relocatable code. So what the above code does is set up the stack so that it
370 will jump to location $Cx87 when it encounters an RTS.
372 0082: 5A PHY ; Push $Cx
373 0083: A9 8B LDA #$8B ; Push $8B: return address is now $Cx8C
376 Similarly, this code sets up the stack so it will jump to $Cx8C when it
377 encounters an RTS as well. So it will go there first, then to $Cx87 second
378 when the routine first called via RTS call, er, uh, returns.
380 0086: 60 RTS ; First time, will "return" to $Cx8C
382 Thus, this first RTS transfers control to the JMP ($0054) down below, which was
383 set up above as an address somewhere in a 1K code chunk. Since the code that
384 goes into the 1K code chunk is a JMP instruction, once that code returns, it
385 will then find the address that was pushed on the stack earlier, and execute
388 0087: 68 PLA ; After the $CCxx block is done, it comes here
389 0088: 9D 6E C0 STA $C06E,X ; Restore last block (one passed in Y reg)
390 008B: 60 RTS ; & return to calling code in that block
392 This code pops the Y register that was saved way back up at location $Cx61 and
393 uses it to set the I/O register at $E, which, presumably, is the bank switch
394 I/O address for the card. This will turn out to be of vital importance later,
395 but we'll leave it for now. The RTS, finally, returns from initialization and
396 back from whence it came.
398 008C: 6C 54 00 JMP ($0054) ; Jump to the $CCxx block code
400 This indirect JMP instruction, called up above via RTS call, kicks things off.
402 008F-00FA: 00 ; $6B worth of zeroes
403 00FB: 82 00 00 BF 0D ; ID/offset bytes
405 So these bytes that look like a bit of detritus actually do serve a useful
406 function in ProDOS. The $0D at the very end serves as an offset from the
407 beginning of the code to the ProDOS entry point, which in this case works out
408 to $Cx0D. It also serves as the entry point for SmartPort calls (by adding 3
409 to it), which works out to $Cx10.
411 Further, the "Technical Manual for the Apple SCSI Card" says the following
412 about the byte at $FB: "An additional byte, at $CnFB, should contain $82,
413 indicating that the device is the SCSI card ($2) and that it supports extended
414 calls ($8)." This just happens to be one of a small handful of those
415 aforementioned tiny bits of useful information that I was able to glean from
418 And so, at last, we come to the realization that this is definitely the slot
419 ROM code, and thus CWLLIBISM becomes CWSISM (Code Which Sits In Slot Memory).
422 And Now For Something Not Quite So Completely Different
423 -------------------------------------------------------
425 And with that digression into CWSISM, we turn our attention back to the 1K
426 chunk of initialization code that sits in bank 11. In looking at the table
427 that we discovered sits at $CFF0, we find the following in the 11th (counting
433 CFF6: 00 00 00 00 00 00 00 00 00 00
435 This tells us that there are only three valid addresses in the table (as the
436 zeroes will take you nowhere), and that further, they are $CC00, $CE91 and
437 $CD9A. And since the CWSISM set up the $Cx61 dispatch call with $0B (at
438 $Cx5C), it will pick the zeroeth address in that list, namely, $CC00. So,
439 looking at the code that lies there, what we see looks promising:
441 CC00: 68 PLA ; Discard the 2nd return path (bank switch back)
443 CC02: 68 PLA ; Discard the follow on bank #, as there is none
445 Since this is initialization code, we can discard the RTS call from the stack
446 since we aren't calling this code from another bank. Which also means that we
447 can discard that parameter which tells the RTS call what bank to select before
450 CC03: 86 5E STX $5E ; Save slot # (+$20) in $5E
451 CC05: 9C 93 C8 STZ $C893 ; Zero out $C893 & $5D
453 CC0A: 20 C1 CC JSR $CCC1 ; Test for GS hardware + DMA switch
455 This is basically housekeeping, and the routine called at $CCC1 tests if the
456 card is running on an Apple IIgs and sets bit 6 of zero page location $5D if it
457 detects that. It also checks the physical DMA on/off switch on the card as
458 well; if it's set, it sets bit 5 of $5D. The following bit of code checks $5D
459 to see if bit 6 is clear and skips the instructions at $CC11 to $CC19 if
460 so--and since I'm emulating an Enhanced Apple IIe, it *will* skip those
463 CC0D: 24 5D BIT $5D ; Check if bit 6 of $5D is set (means it's a GS)
464 CC0F: 50 0B BVC $CC1C ; Skip over if not set (it's not a IIgs)
465 CC11: AD 36 C0 LDA $C036 ; IIgs Speed Reg.
466 CC14: 8D 96 C8 STA $C896 ; Save it for later...
467 CC17: 09 80 ORA #$80 ; Set speed to 2.8 MHz
468 CC19: 8D 36 C0 STA $C036 ; & modify
470 Luckily there exists a very good techinical reference manual for the Apple
471 IIgs; unluckily, it's a bit hard to track down. But once you do, the
472 information in it is quite good. The above bit of code shows that the card
473 firmware shifts the IIgs into high gear while running on the card. However, we
474 don't really care about that bit of code; which is why we spent so much time
475 explaining what it does.
477 CC1C: 68 PLA ; Get flags from slot init
479 Way back in CWSISM, at slot location $Cx13, there was an innocuous looking PHP
480 instuction; here is where we finally take a look at the contents of it.
482 CC1D: A8 TAY ; Save them in Y
483 CC1E: 29 04 AND #$04 ; Check if I flag is set
484 CC20: F0 05 BEQ $CC27 ; Skip if I is not set
485 CC22: A9 80 LDA #$80 ; Else, signal I flag is set ($80 -> $C893)
486 CC24: 8D 93 C8 STA $C893
488 Here we look at the interrupt disable bit in the processor flags that we saved
489 earlier; if it's not set we skip on over to the next bit of code below.
490 Otherwise, the code sets $80 into memory location $C983 to signal that
491 initialization code was called with the I flag set.
493 CC27: 98 TYA ; Restore flags from Y
494 CC28: 09 04 ORA #$04 ; Set I flag
495 CC2A: 48 PHA ; Push them to the stack
496 CC2B: 28 PLP ; & restore flags for real
498 Since we need to get the values of the overflow and carry flags back, which
499 were set way back in CWSISM at addresses $Cx0D through $Cx11, we have to
500 retrieve them from the Y register, then push them onto the stack and then use a
501 PLP to get them back into the flags register proper. Along the way, we set the
502 interrupt disable flag at $CC28 (the ORA #$04 instruction).
504 And in looking at code as we're doing here, it's hard not to look at it with a
505 critical eye and notice that the coder could have saved a byte by deleting the
506 ORA #$04 (which takes two bytes) and putting an SEI after the PLP (which takes
507 one byte). And, since we don't have any source code to look at, we may never
508 know what the intention was; though it's quite likely that this was just a
511 CC2C: 50 09 BVC $CC37 ; If SmartPort call, skip over
513 Here we see that if the card firmware was called via the SmartPort vector at
514 $Cx10, the overflow flag would be clear and we would skip over the following.
515 But, since the flag was definitely set, we know that we will execute what
518 CC2E: BA TSX ; Slot init & regular ProDOS dispatch get here
519 CC2F: 8E 07 C8 STX $C807 ; Save stack pointer in $C807
521 CC34: 4C 5F CF JMP $CF5F ; Jump to bank 15:0 for rest of init
523 This saves the stack pointer and sets up to jump to a new bank, which means we
524 won't be coming back here. Onward:
526 CF5F: A6 5E LDX $5E ; Restore slot # (+$20) in X
527 CF61: A0 0B LDY #$0B ; Y gets loaded with bank to return to on RTS
528 CF63: 6C 00 C8 JMP ($C800) ; & go!
530 There are variants of this piece of code throughout every 1K bank of firmware
531 code. And since we took a good long look at CWSISM, we know that CWSISM set up
532 location $C800 and $C801 to point to the card slot I/O location of $Cx61, and
533 suddenly it becomes clear what that bit of code does.
535 Since the firmware code bounces around a lot in different banks (as we will
536 discover shortly), it needs a mechanism to get back to the place that called it
537 in the first place. The problem is this: once a new 1K bank of code is
538 switched into the $CC00 to $CFFF address space, there's no way for the 65C02 to
539 get back to the caller with a simple RTS; any code that attempted to do so
540 would end up executing the wrong code as the 65C02 knows nothing about bank
541 switching and has no built-in mechanism to handle such things.
543 And so, by virtue of this, the code needs a way to do this manually. Which is
544 why the $Cx61 code in CWSISM saves the bank number on stack, and then sets up a
545 pair of RTS calls which first, sets the correct bank and calls the correct
546 function number in that bank and second, sets the bank to the bank that made
547 the call in the first place before executing a final RTS which then goes back
548 to the correct address.
550 And since we saw up above that it passed $0F into the calling routine (well,
551 actually, it jumped there), we know that it's going to call function #0 in bank
552 15. As it turns out, the function table for bank 15 looks like this:
555 CFF2: 00 00 00 00 00 00 00 00 00 00 00 00 00 00
557 which means bank 15 only contains one function, and it starts at $CC00.
560 The Next Part, In Which We Peruse Bank 15
561 -----------------------------------------
563 The story so far: we started in slot ROM, set up a bunch of variables, then
564 bounced to bank 11, and just now bounced to bank 15.
567 CC02: 8D 09 C8 STA $C809 ; Put $40 into $C809
568 CC05: 8D 32 BF STA $BF32 ; & $BF32(!)
569 CC08: 9C 0A C8 STZ $C80A ; Zero out $C80A
571 So far this is all normal housekeeping boilerplate, though putting the value
572 $40 into RAM at address $BF32 makes me raise an eyebrow (to this day, I still
573 have no idea what that's supposed to do). So then we come to the heart of the
577 CC0D: 20 AF CF JSR $CFAF ; Call bank 3:0 (enumerate all connected drives)
579 Here is the first proper JSR into bank switched code, and in taking a cursory
580 glance at the code there, well... It's a bit of a Gordian knot. So we'll
581 ignore the stones in the field for now, and keep on plowing ahead:
583 CC10: AE 08 C8 LDX $C808 ; Restore slot # (+$20) to X
585 CC15: F0 03 BEQ $CC1A ; Skip over if call was successful ($4F == 0)
586 CC17: 4C F0 CC JMP $CCF0 ; Else, do a LDA #2B, JMP $CFAF to bank 11:1
588 So here the code retrieves the slot I/O offset in X from the location set way
589 back in CWSISM, then checks what looks like some kind of error condition. If
590 it fails, it skips on over to function 1 in bank 11; otherwise, it keeps going
593 CC1A: 24 5D BIT $5D ; Are we running on a IIgs?
594 CC1C: 70 05 BVS $CC23 ; If so, skip over & keep going
596 Since we're not running on a IIgs, this branch is not taken and thus it can be
597 safely ignored. Continuing on:
599 CC1E: A9 4B LDA #$4B ; Else, jump to bank 11:2 (normal success path)
600 CC20: 4C AF CF JMP $CFAF
602 CFAF: A6 5E LDX $5E ; Restore slot (+$20) in X
603 CFB1: A0 0F LDY #$0F ; Make sure we come back here...
604 CFB3: 6C 00 C8 JMP ($C800) ; & go!!
606 So what this means is that if the function call to bank 3:0 succeeded, the code
607 will then bounce to function 2 in bank 11. And, as we saw above, function 2
608 starts at $CD9A in bank 11.
611 The Next Part, In Which Be Bounce Back To Bank 11 And Find Something Familiar
612 -----------------------------------------------------------------------------
614 So far, this little expedition is proving to be circuituitous, but not
615 impenetrable. And it makes sense that we would come back to bank 11, as that's
616 where the initialization code sent us in the first place. And so, pressing on,
619 CD9A: 86 5E STX $5E ; Save X in $5E
620 CD9C: A9 01 LDA #$01 ; Put 1 in $43, $44
623 CDA2: 64 46 STZ $46 ; Zero out $46, $47, $48, $49
627 CDAA: A9 08 LDA #$08 ; Put $08 in $41
629 CDAE: 64 40 STZ $40 ; Zero out $40, $42
632 This is again more housekeeping boilerplate, initializing a bunch of zero page
633 locations. Then we find this:
636 CDB4: 20 5F CF JSR $CF5F ; Call bank 9:0 (directly)
638 So this calls function 0 in bank 9, which lives at $CC00. And looking through
639 that code, well, let's just put that aside for now as it's long and involved
640 and will require a fair amount of study. Continuing:
643 CDB9: D0 0C BNE $CDC7 ; Fail if $4F is non-zero
645 This looks at the error flag we saw up above in bank 15, and jumps to function
646 1 in this bank if the error flag is non-zero.
648 CDBB: AD 01 08 LDA $0801 ; Get byte @ $801 (!)
649 CDBE: F0 07 BEQ $CDC7 ; Fail if it's zero
651 Now here is something interesting! Why this is interesting is because when
652 booting from a floppy disk, the disk driver typically loads at least one sector
653 (256 bytes of data) into location $800. So we can deduce that the above call
654 into function 0 in bank 9 is loading something similar from the hard drive into
655 memory at a similar address. With this bit of knowledge, we can see up above
656 where it puts address $800 into zero page locations $40 and $41 that those
657 locations must be a loading address.
659 CDC0: AD 00 08 LDA $0800 ; Get byte @ $800 (!)
661 CDC5: F0 03 BEQ $CDCA ; Keep going if it's equal to 1
662 CDC7: 4C 91 CE JMP $CE91 ; Else, jump to function 1 (failure point)
664 Again, this interesting because with floppy disks, the first byte of the first
665 sector loaded into memory at $800 contains the number of sectors that the
666 floppy driver should load into memory; this looks eerily similar--only in this
667 case, it will jump to the failure path if it sees it wanting more than one
668 block. Assuming all is well, we then have this:
670 CDCA: 8D 09 C8 STA $C809 ; Put a 1 into $C809
671 CDCD: AD F8 07 LDA $07F8 ; Get $7F8
676 CDD4: AA TAX ; Store it in X
677 CDD5: A9 00 LDA #$00 ; Stuff 0 in $C035 (GS location?)
678 CDD7: 8D 35 C0 STA $C035
679 CDDA: 8D 01 CC STA $CC01 ; What does this do?
680 CDDD: 4C 01 08 JMP $0801 ; Run the code from block 0
682 And here we see it hand off execution to data that it pulled from the hard
683 drive by jumping to $801, and thus we see that this must be the end of the hard
684 drive boot logic. As far as the firmware is concerned, its initialization job
685 of bootstrapping the hard drive is concluded.
687 However, we still really don't know anything that tells us what the slot I/O
688 addresses do (aside from location $E) and we still have no idea how the card
689 talks to the hard drive. At least we have a pretty good idea of where to look.
692 What Are All These Eels, And What Are They Doing In My Hovercraft
693 -----------------------------------------------------------------
695 So at last we get to take a look at function 0 in bank 3. And, much like a
696 hovercraft full of eels, it's a twisty mass of slippery, squirming code. And,
697 looking at it more closely, it does a bunch of things which don't make much
698 sense until you understand other code, which bounces around to lots of other
699 banks. And a lot of it is opaque unless you somewhat understand what the ports
700 on the NCR 53C80 do and how the SCSI protocol works.
702 So while we have an excellent start on understanding, for the most part, the
703 broad outlines of how the card works, we are still stuck with a profound lack
704 of critical knowledge on how the thing talks to the the hard drive and,
705 conversely, how the hard drive talks to the card. And without that knowledge,
709 The Next Part, In Which We Are Not Ready To Perish
710 --------------------------------------------------
712 Fortunately, the NCR 5380 and, by extension, the 53C80 is well documented and
713 said documentation is readily available, and so I availed myself of it. I took
714 another look at the schematic for the card and noticed that the 53C80 had three
715 address lines on it, which implied that it had eight ports for controlling it.
716 Unfortunately, there's an error on the schematic in which they have the address
717 lines hooked up in reverse, and this caused me no small amount of consternation.
719 It seemed obvious that those eight ports were hooked up to the slot I/O
720 addresses, and also seemed very plausible, after having looked at and analyzed
721 a lot of code heretofore unmentioned, that it was connected to the lower half
722 of that address space. So, in order to confirm my suspicions, I started
723 writing the hard drive emulator.
725 This started out, simply, as a bunch of statements that output human readable
726 words to a log file whenever the slot I/O addresses were accessed by the card
727 firmware; I used the firmware's access to the slot I/O to tell me what it said
728 and what it was listening for. Well, that, and some code to properly handle
729 the bank selection of the ROM space as well. In this way, I was able to
730 enlarge my understanding of what the card expected to see as well as what the
731 ports that weren't connected to the 53C80 (which were likely connected to the
732 Sandwich II) might be up to.
734 So in fits and starts, I used the code that writes to the Mode Register of the
735 53C80 to get the code to successfully... do something. It was at that point I
736 could see that it was getting through the initialization phase of the card's
737 firmware as Apple2 would be able to boot a floppy image inserted into a drive
738 in slot 6 at that point. But in tracing the reads and writes to the slot I/O
739 address space in the log I could see that it was getting through the card's
740 firmware in a failure mode. It was progress, of a sort. Even failure tells
743 And what it told me was that I needed to dig into the SCSI specification to
744 figure out how the protocol worked. Looking back I can see that I was getting
745 through to the MESSAGE phase and, because of the way I was responding to that
746 message, that the firmware would then send an ABORT message, but that's all
747 pretty much meaningless as I haven't explained anything about the SCSI protocol
750 And here, while there is a lot of information about the latter day iterations
751 of the SCSI protocol, there wasn't much pertaining to the kind of SCSI that the
752 Apple High Speed SCSI card spoke, which in its case, has been retroactively
755 And when looking at the SCSI protocol, the first thing that hits you is that
756 it's a very well designed, robust protocol and it's nothing short of a minor
757 miracle that it survived and still survives to this day. However, the
758 documentation on how it *really* works is a bit lacking. Yes, you can discover
759 that there are nine phases, and the first three are fairly easy to understand;
760 it's what comes after that where things get murky.
766 So here is a crash course in the SCSI-1 protocol. The SCSI bus is engineered
767 such that it allows for eight devices to connect to said bus; devices connected
768 to the bus can have Initiator and/or Target roles. Devices can talk to each
769 other by passing messages over this bus, however only one pair of devices can
770 use the bus at any one time. In order to prevent deadlock from happening when
771 more than one device attempts to take control of the bus, there is an enforced
772 hierarchy of devices wherein they all have a unique ID; a device that contends
773 for use of the bus at the same time as another device wins this contention if
774 and only if its device ID is higher than the other device's ID (1 in this case
775 being the highest, and 128 being the lowest). The bus is an 8-bit parallel
776 data bus that is controlled by a variety of signals (and these are typically
779 In contending for and utilizing the bus, there are nine phases that all SCSI
780 devices must understand and negotiate. They are as follows:
792 In the Bus Free phase, as one might expect, no devices are using the bus. This
793 is the ground state of the SCSI protocol, the phase from whence all
794 communication starts and where it all ends. Any device that wishes to talk to
795 another device on the bus must start here.
797 Once a device sees that the bus is free, it can enter the Arbitration phase as
798 an Initiator; it does so by first setting the bit that corresponds to its
799 device ID on the data bus. If another device tries to do this at the same
800 time, the device with the lower ID will remove its bit from the data bus and
801 try again when it detects that the bus is free again. When the Initiator has
802 waited a certain amount of time with no other contention, it then asserts the
803 SEL line and goes into the Selection phase.
805 In the Selection phase, the Initiator sets the bit that corresponds to the
806 device ID it wants to talk to (the Target) on the data bus. Every other device
807 on the bus, by virtue of the asserted SEL line, knows it's in the Selection
808 phase and can see the device ID bits being asserted on the data bus; if none of
809 the bits match its own ID, it will stay silent. If the Target device doesn't
810 respond in a timely manner, the device that tried "calling" it drops the bits
811 it asserted on the data bus and drops the SEL line. Otherwise, if the Target
812 device sees its ID on the data bus, it responds by asserting the BSY (BuSY)
815 The device that started all of this (the Initiator) then drops the SEL line and
816 the Initiator and Target devices then enter the next phase. What phase that is
817 took some teasing out of lots of different papers, datasheets and manuals--as
818 well as much trial and error in the emulation code. And what I found was this:
819 once the devices are in the Selection phase, they typically(*) dance through
820 the following set of phases, in order, before being done with their
821 transaction: Message Out(**), Command, Data In/Out, Status, Message In.
823 (*) One exception to this is the TEST UNIT READY command, which will skip the
826 (**) Note that the qualifiers "In" and "Out" come strictly from the perspective
829 Once the devices have successfully negotiated the Message In phase at the end
830 of their phase dance, the Target device drops the BSY line and the bus is then
831 free again for another transaction.
833 One thing I forgot to mention is that each phase transition, once the devices
834 are in the Selection phase, is punctuated by a REQ/ACK handshake. Typically,
835 the Target asserts and drops the REQ line while the Initiator asserts and drops
836 the ACK line. Basically, when the Target is ready to move to a different
837 phase, it will assert the REQ line; the Initiator will see this and then assert
838 the ACK line. Once the Target sees the ACK line asserted, it will drop the REQ
839 line; the Initiator, seeing this, will then drop the ACK line. And thus hands
840 are shaken, and all are in agreement as to where they are and what they are
843 One interesting consequence of this kind of handshaking is that it means that
844 every phase past Arbitration is driven by the Target device.
850 And so having deciphered the proper steps in the post-Selection phase dance, we
851 come as last to the heart of the matter: the Command phase. Commands come in a
852 few different flavors: the six byte, the ten byte and the twelve byte. The
853 flavor is given by the top three bits of first byte while the command itself is
854 given by the bottom five bits. Treating those top three bits as a number from
855 zero to seven, the flavors fall into the following groups:
861 Yes, 3, 4, 6 and 7 are all missing, and, for the purposes of this crash course,
862 can be safely ignored(*).
864 (*) For the terminally curious, 3 and 4 are (were?) "reserved", and 6 and 7 are
865 for "vendor specific" commands
867 Having now discerned their form, the question arises: just what do these
868 commands do? Basically, they tell the Target what the Initiator wants from it.
869 For example, let's say that the Initiator wants to know if a device on the bus
870 is ready to receive commands. It would send out, during the Command phase, a
871 TEST UNIT READY command which has the following form:
875 Assuming the device receiving this command actually is ready to receive
876 commands, it would then send back a status message (in the Message In phase
877 following the Status phase) saying "Good" (which, in this case, is coded as
880 Other commands follow basically the same form; only instead of going directly
881 to the Status phase, as the TEST UNIT READY command does, it will go into
882 either the Data In or Data Out phase before going to the Status
883 phase--depending on what the command does. For example, a READ command will go
884 to the Data In phase, because the Initiator is requesting data from the Target;
885 likewise, a WRITE command will go to the Data Out phase because the Initiator
886 wants to send data to the Target.
889 Back To Our Regularly Scheduled Analysis
890 ----------------------------------------
892 So, before we diverged into a crash course of the SCSI-1 protocol, we were
893 looking at where I had been able to have the card's firmware return back to the
894 Apple IIe's Autostart program, but in a failure mode. Which, while ultimately
895 unsatisfying, *was* a step in the right direction.
897 So I could see that with my hard-coded responses to the firmware's inquiries, I
898 was getting an IDENTIFY message ($80) followed by an ABORT message ($06). It
899 was a this point I could also see that I was going to have to start writing the
900 actual hard drive device emulator code as well, as trying to keep track of all
901 the phase changes in the slot I/O register code was turning into an
902 impenetrable mess and wasn't going to be fruitful in the long run.
904 This also necessitated a closer look at the code for function 0 in bank 3. I
905 took copious notes on where the code went and what it did, and eventually found
906 that almost everything, at some point, seemed to end up calling function 0 in
910 All Roads Lead To Bank 16:0
911 ---------------------------
913 The one thing I was trying to figure out from this code was: what was the
914 failure mode that would get you out cleanly? Because in order for the code
915 that called here to work properly, it would have to have some kind of clean
916 failure mode to indicate that there was no drive present at this device ID;
917 also in my first attempts to get the firmware code to successfully run (for
918 some value of "successfully" > 0), it would hang up somewhere in this code.
919 And that meant, since I didn't understand the SCSI chip, that I would have to
920 understand the SCSI chip and how it worked to have any hope of untangling the
921 tangled mass of code here.
923 So before we take a quick look at that, let's take a look at the top level code
924 that lives at function 0, bank 16. At first glance, it doesn't look all that
927 CC00: 8D 00 CD STA $CD00 ; Write to $CD00 (what does it do?)
928 CC03: 20 D0 CD JSR $CDD0 ; Clear DMA bit (1) from reg. $2, init some stuff
929 CC06: 20 CE CE JSR $CECE ; Check if reg. $4 has 0, 2 (/SEL) or 4 (/I/O)
930 CC09: B0 16 BCS $CC21 ; If failure, skip over
932 This is pretty straightforward stuff; the routine at $CECE will set the carry
933 flag if slot I/O register $4 is not exactly one of: 0, 2, or 4. If the carry
934 is set, it bypasses the following sections of code:
936 CC0B: 20 42 CF JSR $CF42 ; Check if bit 7 in $C893 is set (success == yes)
937 CC0E: 20 24 CC JSR $CC24 ; Do Arbitration phase
938 CC11: B0 03 BCS $CC16 ; If Arbitration timed out, jump over Selection
940 It wasn't obvious when I first encountered this code, but, once I delved into
941 the SCSI protocol I was able to figure out that the code at $CC24 was
942 negotiating the Arbitration phase.
944 CC13: 20 7A CC JSR $CC7A ; Do Selection phase
946 Likewise, it was not obvious that the code at $CC7A was negotiating the
947 Selection phase--but I was able to figure out that the code could cleanly exit
948 this bank (in a failure mode, naturally) if the BSY line was not asserted.
950 CC16: 20 58 CF JSR $CF58 ; Check if bit 7 in $C893 is set (success = yes)
951 CC19: B0 06 BCS $CC21 ; Skip over if it failed
953 Since the address at $C893 got loaded with $80 way back in function 0 in bank
954 11, the carry flag will be clear and we will execute the following:
956 CC1B: 20 E4 CC JSR $CCE4 ; Do SCSI communication with target
957 CC1E: 20 A0 CD JSR $CDA0 ; Do nothing if $C88F is nonzero, else check on
960 The code at $CCE4 was quite mystifying for some time, even after I had educated
961 myself on the intricacies of the SCSI protocol and the ins and outs of the NCR
962 53C80's ports. I wasn't able to make sense of this until I was able to
963 understand the phases after Selection and how they were expected to be
966 CC21: 4C 18 CE JMP $CE18 ; Do some post cleanup before returning
968 The code at $CE18 basically does some error checking and cleanup before
969 returning back to whence it came; it's fairly easy to digest. But before we
970 dig into subroutines of bank 16:0, we need to take a short digression into how
971 the ports of the 53C80 work.
974 A Somewhat Brief Digression Into The 53C80's Ports
975 --------------------------------------------------
977 And so, having avoided looking into the 53C80 and how it works up until this
978 point, we find we can no longer avoid it and thus, finally bite the bullet.
979 The 53C80 has eight ports (also called registers) with which the Apple IIe's
980 CPU can communicate. They are:
982 $0 - Data on the SCSI bus
983 $1 - Initiator Command
986 $4 - Current SCSI Bus Status (R), Select Enable (W)
987 $5 - Bus and Status (R), Start DMA Send (W)
988 $6 - Input Data (R), Start DMA Target Receive (W)
989 $7 - Reset Parity/Interrupt (R), Start DMA Initiator Receive (W)
991 Note too that there is a one-to-one correspondence with the port numbers as
992 they appear on the 53C80 and their location in the slot I/O address range.
993 What follows is an explanation of what the registers do:
995 Register $0 is pretty much what it says it is; data on the SCSI bus will appear
996 here barring this caveat: it only works when bit 0 of register $1 (ASSERT DATA
997 BUS) is set. Which bring us to...
999 Register $1 is used to monitor and assert signals on the SCSI bus. The bits
1003 RST AIP/TEST MODE LA/DIFF ENBL ACK BSY SEL ATN DATA BUS
1005 RST (ReSeT) sets the RST signal on the SCSI bus and resets the internal state
1006 of the 53C80; it stays in the reset state until this bit is cleared. AIP/TEST
1007 MODE (Arbitration In Progress) is a bit that is split between two functions:
1008 when read, it signals whether or not the Arbitration phase is in progress; when
1009 a one is written to it, it disables all output from the chip (zero restores
1010 output). LA/DIFF ENABL (Lost Arbitration) is another split signal: when read,
1011 it signals whether or not Arbitration was lost; writing has no effect. ACK
1012 (ACKnowledge) sets or clears the ACK line, BSY (BuSY), SEL (SELect), ATN
1013 (ATteNtion) and DATA BUS all do the same.
1015 The important thing to note here is that by setting the ATN line on the SCSI
1016 bus, the initiator signals to the Target that it wants to send a message and
1017 so, at the appropriate time, the Target will then assert the MSG and C/D lines
1020 Register $2 controls various modes of the 53C80, as well as whether or not
1021 certain interrupts will be triggered. The bits are:
1024 BLOCK TARGET ENABLE ENABLE ENABLE EOP MONITOR DMA ARBITRATE
1025 MODE MODE PARITY PARITY INTERRUPT BUSY MODE
1026 DMA CHECKING INTERRUPT
1028 The only two of real interest are bits 1 (DMA MODE) and 0 (ARBITRATE); the
1029 former sets the chip into DMA mode, readying it for a DMA transfer while the
1030 latter tells the chip to start the Arbitration phase.
1032 Register $3 is used mainly if the chip is operating in Target mode, as all the
1033 lines controlled by it are typically only controllable by the Target device.
1034 The only exception is when the Initiator is sending data to the Target; in that
1035 case, bits 0, 1 and 2 must match the lines being asserted by the Target. The
1036 bits are (where X means unused):
1039 LAST BYTE SENT X X X REQ MSG C/D I/O
1041 Register $4 is another split register. When read, it returns the state of the
1042 following lines on the SCSI bus:
1045 RST BSY REQ MSG C/D I/O SEL DBP
1047 When written to, it enables an interrupt to occur if the device ID written to
1048 the SCSI bus is present, BSY is clear and SEL is set.
1050 The important thing about this register is that it allows monitoring of the
1051 MSG, C/D and I/O lines of the SCSI bus. These three bits are what the Target
1052 uses to signal moves from phase to phase; without these three bits it would be
1053 impossible, as an initiator, to figure out what to do once in the Selection
1056 And with three bits, you would expect there to be eight phases controlled here,
1057 but only six are controlled from these signals--having MSG set to 1 while C/D
1058 is set to 0 is an illegal combination, and that knocks two of the combinations
1059 right out of contention. Each legal combination corresponds to a phase, and
1060 this is, as it turns out, vital information:
1062 Data Out: MSG = 0, C/D = 0, I/O = 0 (0)
1063 Data In: MSG = 0, C/D = 0, I/O = 1 (1)
1064 Command: MSG = 0, C/D = 1, I/O = 0 (2)
1065 Status: MSG = 0, C/D = 1, I/O = 1 (3)
1066 Message Out: MSG = 1, C/D = 1, I/O = 0 (6)
1067 Message In: MSG = 1, C/D = 1, I/O = 1 (7)
1069 Note that there's nothing magical about the order of these three lines; they
1070 could be in any order whatsoever and they would still work the same way. The
1071 only reason that they are presented this way is one, this is how they are laid
1072 out in the NCR 53C80 chip (in this register in particular) and two, this is
1073 order that they are used in the firmware.
1075 Register $5 is--you guessed it--another split register. When read, it returns
1076 some internal state registers as well as a couple more SCSI bus lines:
1079 END OF DMA PARITY IRQ PHASE BUSY ATN ACK
1080 DMA REQUEST ERROR ACTIVE MATCH ERROR
1082 When written to, it initiates a DMA send transfer from memory to the SCSI bus.
1084 Register $6, another split register, when read, holds data coming from the SCSI
1085 bus during a DMA transfer. When written to, it initiates a DMA receive
1086 transfer from the SCSI bus (the Target) to memory.
1088 And finally, register $7 is yet another split register, that when read, resets
1089 the internal PARITY ERROR, IRQ ACTIVE and BUSY ERROR bits in register $5; when
1090 written to in initiates a DMA receive transfer from the SCSI bus (the
1091 Initiator) to memory.
1097 So, with that info-dump out of the way, let's return back to the first
1098 subroutine of the initial code of bank 16:0. We start with the routine at
1101 CC24: 9E 63 C0 STZ $C063,X ; Zero reg $3 (Target Command)
1102 CC27: 20 2F CF JSR $CF2F ; Toggle bit 7 of reg. $E (ON-off-ON)
1103 CC2A: AD DA C8 LDA $C8DA ; Get SCSI ID of initiator device
1104 CC2D: 9D 60 C0 STA $C060,X ; & put it in reg. $0 (Output Data)
1106 CC30: 9E 62 C0 STZ $C062,X ; Zero out reg. $2 (Mode)
1107 CC33: A9 01 LDA #$01
1108 CC35: 9D 62 C0 STA $C062,X ; Set bit 0 (ARBITRATE) of reg. $2
1110 This code zeroes out the Target Command register, then toggles bit 7 of
1111 register $E on, then off, then back on. It then puts the SCSI ID of the
1112 initiator device into the SCSI Data Bus register, then clears and sets the
1113 ARBITRATE bit of the Mode register. This is the start of the Arbitrate phase.
1115 CC38: BD 6C C0 LDA $C06C,X ; Get reg. $C
1116 CC3B: 89 10 BIT #$10 ; Check bit 4
1117 CC3D: D0 05 BNE $CC44 ; Skip over this if it's set
1118 CC3F: 20 0C CF JSR $CF0C ; Toggle bit 7 of register $E ON-off-ON
1119 ; # of times before C is set is in $C817/8
1120 CC42: B0 2E BCS $CC72 ; Signal failure is C is set
1122 There is a lot of this code and variants thereof sprinkled liberally throughout
1123 the firmware code. I'm still not sure what bit 4 of register $C is a signal
1124 for, but it seems clear that it indicates some kind of error condition because
1125 whenever it's not set, it toggles bit 7 of register $E and will eventually,
1126 when this has happened enough times, signal an error and exit.
1128 CC44: 3C 61 C0 BIT $C061,X ; Check bit 6 (AIP) of reg. $1
1129 CC47: 50 E7 BVC $CC30 ; Try again if it's not set
1131 This little bit of code checks the AIP (Arbitration In Progress) bit, and loops
1132 back to try again if it's not set.
1134 CC49: EA NOP ; Do a small delay
1136 CC4B: A9 20 LDA #$20
1137 CC4D: 3D 61 C0 AND $C061,X ; Check if bit 5 (LA) of reg. $1 is set
1138 CC50: D0 DE BNE $CC30 ; Try again if it's set
1140 After checking to see if the AIP bit is set, it then waits a short amount of
1141 time before checking to see if the LA (Lost Arbitration) bit is set; if it's
1142 set, it loops back to try again.
1144 CC52: BD 60 C0 LDA $C060,X ; Get reg. $0
1145 CC55: 4D DA C8 EOR $C8DA ; EOR it with what we put there to begin with
1146 CC58: F0 05 BEQ $CC5F ; If it's the same, bypass (we won arbitration)
1147 CC5A: CD DA C8 CMP $C8DA ; Otherwise, see if the EORed value is >= orig
1148 CC5D: B0 D1 BCS $CC30 ; Try again if so
1150 Here we look at the data on the SCSI bus and see if there were any other
1151 devices attempting to arbitrate at the same time. If there were, and their
1152 SCSI ID was higher than ours, then loop back and try again; otherwise, we won
1153 arbitration and continue on:
1155 CC5F: A9 20 LDA #$20
1156 CC61: 3D 61 C0 AND $C061,X ; Check if bit 5 (LA) of reg. $1 is set
1157 CC64: D0 CA BNE $CC30 ; Try again if so
1159 We check the LA bit one more time to ensure it's not set; if it is, then loop
1162 CC66: A9 06 LDA #$06 ; Set bits 1-2 (ASSERT /ATN, /SEL) of reg. $1
1163 CC68: 1D 61 C0 ORA $C061,X
1164 CC6B: 29 9F AND #$9F ; And clear bits 5-6 (TEST MODE, DIFF ENBL) of $1
1165 CC6D: 9D 61 C0 STA $C061,X
1166 CC70: 18 CLC ; Signal success
1167 CC71: 60 RTS ; & return
1169 Now that we've won the Arbitration phase, we assert the ATN and SEL lines and
1170 make sure that the TEST MODE and DIFF ENBL lines are dropped. By setting the
1171 ATN line, we signal to the Target that we want to go to the Message Out phase
1172 after the Selection phase is done. Once that's done, we signal success and
1175 CC72: A9 80 LDA #$80
1176 CC74: 8D 8F C8 STA $C88F
1177 CC77: 4C 91 CD JMP $CD91 ; Signal failure
1179 This bit is called if the code that checks register $C fails; this is the only
1180 failure path for the Arbitration phase code.
1183 A Fine SELECTion Of Devices
1184 ---------------------------
1186 Now that the Initiator (us) has won the Arbitration phase, it's time to see if
1187 the device we want to talk to exists, and is ready and able to talk.
1189 CC7A: 9E 64 C0 STZ $C064,X ; Zero out reg. $4 (Select Enable)
1190 CC7D: AD DA C8 LDA $C8DA ; Host ID
1191 CC80: 0D DB C8 ORA $C8DB ; Target ID
1192 CC83: 9D 60 C0 STA $C060,X ; Store $C8DA & DB (ORed) into reg. $0 (Data Bus)
1193 CC86: A9 41 LDA #$41 ; Set bits 0 (DATA BUS) & 6 (TEST MODE) in reg. $1
1194 CC88: 1D 61 C0 ORA $C061,X ; Then clear bits 5-6 (DIFF ENBL, TEST MODE) in $1
1195 CC8B: 29 9F AND #$9F
1196 CC8D: 9D 61 C0 STA $C061,X
1198 The code here clears the Select Enable register to ensure no IRQs are generated
1199 during the Select phase, then puts both the Initiator's SCSI ID and the
1200 Target's SCSI ID into the 53C80's data register. It then does something that
1201 doesn't seem to make any sense, as it sets the DATA BUS ENABLE and TEST MODE
1202 bits. The former puts the 53C80's data register onto the SCSI data bus, while
1203 the latter disables all outputs of the 53C80. Maybe this was necessary because
1204 of the Sandwich II chip and the way it was hooked up to the slot I/O bus and
1205 the 53C80, but there's no way to know for sure without access to actual
1208 After this, it disables the TEST MODE bit, which then enables the outputs of
1209 the 53C80, and thus the Target's SCSI ID is then visible to all the devices
1210 connected to the SCSI bus.
1212 CC90: A9 FE LDA #$FE ; Clear bit 0 (ARBITRATE) in reg. $2
1213 CC92: 3D 62 C0 AND $C062,X
1214 CC95: 9D 62 C0 STA $C062,X
1215 CC98: A9 02 LDA #$02 ; Set bit 1 (DMA MODE) in reg. $2
1216 CC9A: 1D 61 C0 ORA $C061,X
1217 CC9D: 9D 61 C0 STA $C061,X
1218 CCA0: AD DC C8 LDA $C8DC ; Get $C8DC, set hi bit, save in $C821
1219 CCA3: 09 80 ORA #$80
1220 CCA5: 8D 21 C8 STA $C821
1221 CCA8: A9 F7 LDA #$F7 ; Clear bit 3 (ASSERT /BSY) in reg. $1
1222 CCAA: 3D 61 C0 AND $C061,X
1223 CCAD: 9D 61 C0 STA $C061,X
1225 This is all pretty straightforward stuff. It clears the ARBITRATE bit, sets
1226 the DMA MODE bit, and clears BSY (if it was set before; more likely than not,
1227 it will have been cleared already). It also sets bit 7 of $C8DC and saves it
1228 in $C821, but it's not clear just why yet.
1230 CCB0: 20 51 CD JSR $CD51 ; Wait for bit 6 (/BSY) of reg. $4 to be set
1231 CCB3: 90 03 BCC $CCB8 ; Skip over JSR if success
1232 CCB5: 20 75 CD JSR $CD75 ; Shorter wait for bit 6 in reg. $4 to be set
1234 This bit of code waits for the Target to assert the BSY line; if it fails after
1235 the first attempt, it will try again with a shorter wait time.
1237 CCB8: A9 FB LDA #$FB ; Clear bit 2 (ASSERT /SEL) in reg. $1
1238 CCBA: 3D 61 C0 AND $C061,X
1239 CCBD: 9D 61 C0 STA $C061,X
1240 CCC0: 90 10 BCC $CCD2 ; Skip over if the JSR was successful
1242 This code drops the SEL line, and depending on whether or not the Target
1243 asserted the BSY line, will either drop through to the failure path or skip
1244 over to the success path.
1246 CCC2: A9 FE LDA #$FE ; Clear bit 0 (DATA BUS) in reg. $1
1247 CCC4: 3D 61 C0 AND $C061,X
1248 CCC7: 9D 61 C0 STA $C061,X
1249 CCCA: A9 81 LDA #$81 ; Put $81 in $C88F
1250 CCCC: 8D 8F C8 STA $C88F
1251 CCCF: 4C 91 CD JMP $CD91 ; Signal failure
1253 This is the only failure path in the Selection phase code, but, unlike the
1254 Arbitration phase code, this code path will *not* lock up waiting for signals.
1255 It will wait only so long for the Target to assert the BSY line before giving
1256 up and signalling failure. It will also bail out of this bank completely, so
1257 it will not try any further communication--for now.
1259 CCD2: A9 9D LDA #$9D ; Clear bits 1, 5-6 (TEST, DIFF E., DMA) in $1
1260 CCD4: 3D 61 C0 AND $C061,X
1261 CCD7: 9D 61 C0 STA $C061,X
1262 CCDA: A9 FE LDA #$FE ; Then clear bit 0 (DATA BUS) in $1
1263 CCDC: 3D 61 C0 AND $C061,X
1264 CCDF: 9D 61 C0 STA $C061,X
1265 CCE2: 18 CLC ; Signal success
1266 CCE3: 60 RTS ; & return
1268 Otherwise, the code clears TEST MODE, DIFF ENBL and DMA MODE before clearing
1269 DATA BUS, signalling success and returning.
1272 The Next Part, In Which We Find Ourselves In A Maze Of Twisty Code
1273 ------------------------------------------------------------------
1275 Now that we've successfully navigated the Selection phase, it's time to talk
1276 SCSI. For the sake of brevity, we will refer to this code as The Code That
1277 Comes After Selection, or TCTCAS for short. This bit of code calls a bunch of
1278 other code which in turns calls even more code; keeping it all straight was
1279 quite the challenge.
1281 CCE4: BD 6C C0 LDA $C06C,X ; Get $C
1282 CCE7: 89 10 BIT #$10 ; Is bit 4 set?
1283 CCE9: D0 05 BNE $CCF0 ; Skip ahead if so
1284 CCEB: 20 0C CF JSR $CF0C ; Else, toggle bit 7 of $E (ON-off-ON) w/countdown
1285 CCEE: B0 40 BCS $CD30 ; Exit if countdown hit zero
1287 Here again we see the boilerplate checking of bit 4 of register $C.
1289 CCF0: BD 64 C0 LDA $C064,X ; Get reg. $4
1290 CCF3: 29 42 AND #$42 ; Are bits 1 (/SEL) & 6 (/BSY) clear?
1291 CCF5: F0 3A BEQ $CD31 ; If so, we're done (jump down, signal error)
1293 Here we're checking the BSY and SEL lines; if both have been dropped after the
1294 last phase, we jump down to $CD31 and do some final checking before exiting.
1296 CCF7: C9 40 CMP #$40 ; Is only bit 6 (/BSY) set?
1297 CCF9: D0 E9 BNE $CCE4 ; Loop back if not...
1299 The second check looks to see if only BSY is set; if not it loops back to the
1300 start of this subroutine, otherwise it continues on:
1302 CCFB: BD 62 C0 LDA $C062,X ; Clear bit 1 (DMA MODE) of reg. $2
1304 CCFF: 29 FD AND #$FD
1305 CD01: 9D 62 C0 STA $C062,X
1306 CD04: 98 TYA ; Then restore its previous state
1307 CD05: 1D 62 C0 ORA $C062,X
1308 CD08: 9D 62 C0 STA $C062,X
1310 This little bit of code toggles DMA MODE line off then on if it was set to
1311 begin with, otherwise it does nothing. Well, it doesn't *do* nothing, but the
1312 effect is null and void.
1314 CD0B: BD 64 C0 LDA $C064,X ; Is bit 5 (/REQ) of reg. $4 clear?
1316 CD0F: 29 20 AND #$20
1317 CD11: F0 D1 BEQ $CCE4 ; Loop back if so...
1319 This checks to see if the REQ line has been asserted by the target yet, and if
1320 not, loop back to the beginning of the subroutine.
1322 CD13: AD 1F C8 LDA $C81F ; Save $C81F in $C820 (last 3-bit pattern we saw)
1323 CD16: 8D 20 C8 STA $C820
1325 Here we save the last phase that was seen in $C820.
1327 CD19: 98 TYA ; Restore reg. $4 from Y
1328 CD1A: 29 1C AND #$1C ; Keep only bits 2-4 (/I/O, /C/D, /MSG)
1329 CD1C: 8D 1F C8 STA $C81F ; & save in $C81F
1331 Earlier we saved the contents of register $4 (which holds the MSG, C/D and I/O
1332 bits) in the Y register, now we retrieve them and mask off the MSG, C/D and I/O
1333 bits and save them for later. By virtue of this, every time we get here the
1334 previous value that was in $C81F must be different than the last value we saw
1337 As to why: when I first encountered this code, I approached it the way I
1338 usually approach unknown code: by feeding it zeroes. However, when I did that,
1339 these lines of code caused a failure mode later on. And so I had to dig a
1340 little deeper into all things SCSI and 53C80 to figure out why--we'll see why
1341 that caused a failure later on.
1344 CD20: 8D 2B C8 STA $C82B ; & put /2 in $C82B
1346 Here we shift it right one bit and stuff it into $C82B; this is also a clever
1347 way of making it into an index for a jump table.
1349 CD23: A8 TAY ; & use as index into jump table
1350 CD24: 4A LSR A ; & /2 again
1351 CD25: 9D 63 C0 STA $C063,X ; Write it to reg. $3 (Target Command)
1353 Here we put it into the Y register and then shift it to the right one more time
1354 to set the bits in the Target Command register properly. The Initiator needs
1355 to set this register properly at each phase change, otherwise the 53C80 will
1356 signal a phase match error.
1358 CD28: 20 48 CD JSR $CD48 ; Use Y as idx to jump table and go there
1360 So here the code uses the three phase bits (MSG, C/D and I/O) as an index into
1361 a jump table to handle the six phases after the Selection phase (Data Out, Data
1362 In, Command, Status, Message Out, Message In). We'll have more to say about
1365 CD2B: 2C 06 C8 BIT $C806 ; Is bit 7 of $C806 clear?
1366 CD2E: 10 B4 BPL $CCE4 ; Loop back if so...
1369 This simply checks bit 7 of $C806, which only gets set under very specific
1370 circumstances; those being that MSG, C/D and I/O are all asserted (Message In
1371 phase), and that the value returned from the Target is a "Good" message, and
1372 that the prior phase was either Message In, Message Out, or Status.
1374 CD31: AD 8F C8 LDA $C88F ; Get $C88F
1375 CD34: D0 08 BNE $CD3E ; If $C88F is != 0, just return
1376 CD36: A9 82 LDA #$82 ; Stuff $82 into $C88F
1377 CD38: 8D 8F C8 STA $C88F
1378 CD3B: 4C 91 CD JMP $CD91 ; Signal failure (?) & return
1379 CD3E: 80 F0 BRA $CD30
1381 This is the code path taken if the BSY and SEL lines are dropped. It signals
1382 that something went wrong before returning.
1385 The Next Part, In Which Things Start To Make Sense
1386 --------------------------------------------------
1388 So TCTCAS is, as it turns out, where the Target drives the Initiator; which in
1389 this case is the hard drive driving the card. As I mentioned up above, when I
1390 first started poking around at this code, I was feeding it zeroes at first as a
1391 place to start seeing if I could get it to do something meaningful. However,
1392 when you try that, you run into the following bit of code which says, "No,
1395 CEE5: AD 1F C8 LDA $C81F ; Get the current MSG, C/D, I/O values
1396 CEE8: CD 20 C8 CMP $C820 ; Compare it to the previous values
1397 CEEB: D0 05 BNE $CEF2 ; If they're different, skip over
1398 CEED: A9 27 LDA #$27 ; (This is ignored by the jump target)
1399 CEEF: 4C 6C CE JMP $CE6C ; Else, do a soft, then a hard reset of the card
1402 And so, after looking over the SCSI documentation for the umpteenth time, I
1403 realized that what it was saying is that you can't do a Data Out phase directly
1404 after the Selection phase; it has to be Something Else. And this is because
1405 $C81F gets initialized with zero (which corresponds to the Data Out
1406 phase)--which means starting with zero Won't Work.
1408 As luck would have it, however, we know that in the Selection phase, it
1409 asserted the ATN line, which in turn tells the Target to assert the MSG and C/D
1410 lines (but not I/O). Which means that we *know* that the Target will first go
1411 to the Message Out phase, every time.
1413 And so, by writing the hard drive emulator to properly respond to the MSG, C/D
1414 and I/O lines I got it to handshake the Message Out phase properly. But I
1415 could see that after that, it wasn't exiting; it was running through another
1416 round of seeing what was in MSG, C/D and I/O and running the appropriate
1419 Now I was a bit stuck here, as there was *no* documentation on how a Target
1420 device, such as a hard drive, would drive the handshaking for the Initiator
1421 device. And it wasn't clear what phase the firmware was expecting to come
1422 next, so guessing wasn't likely to yield positive results.
1424 So, by the serendipitous luck of the Search Engine gods, I stumbled upon a page
1425 which looked like a scan of a book mixed with some bespoke images made by
1426 someone whose primary language was not English. One of the images, which had
1427 misaligned text set next to it, was, however, suggestive. It showed a sequence
1428 of phases that went from Bus Free to Arbitration to Selection to Message Out to
1429 Command to Data In to Status to Message In to Bus Free. This was the first
1430 time I had seen anything like this; in all of the SCSI literature that I had
1431 surveyed, there was nothing beyond the vaguest hints that there was a typical
1432 order to the phases. Sure, they would say that one *could* go from one phase
1433 to another, and how the handshaking worked, but there was *nothing* saying that
1434 there was a definite order to the phases that should be observed.
1436 So, as I said, this image was highly suggestive. Could this be the key to the
1437 whole thing that I was missing?
1439 I had set things up in the hard drive emulation to go to the Message Out phase
1440 after the Selection phase, and so I added code to go to the Command phase after
1441 that. I could see that the firmware was sending something in the Command phase
1442 at this point, which was the following six bytes: 00 00 00 00 00 00. And
1443 looking that up in the SCSI literature showed that to be the TEST UNIT READY
1444 command. But the firmware was still looking for more.
1446 From what I saw in the logs, it didn't look like it was going for a Data In
1447 phase next, so I set it up to go to the Status phase, and that got things going
1448 a little bit further. To me, this looked like it should be the end of the
1449 dance, but the firmware was *still* looking for more.
1451 But even though a byte was sent from the Target to the Initiator during the
1452 Status phase, it seemed that the Status reponse was actually sent in the
1453 Message In phase. Once I had coded this into the hard drive emulation, I could
1454 see the TEST UNIT READY command going into TCTCAS and coming out of it in a
1457 The dance has steps, and they must be followed in order.
1463 However, something is still not quite right; my assumption--that all the
1464 firmware needed to do to see if there was a drive on the bus was to probe
1465 through to the Selection phase and then, if anything responded, to see if it
1466 successfully responded to the TEST UNIT READY command--turned out to be wrong.
1467 How wrong? Let's take a look back at the code in bank 3:0 which attempts to
1468 enumerate all devices it can see on the SCSI bus:
1470 CC55: A0 07 LDY #$07
1471 CC57: 8C 73 C8 STY $C873 ; Save Y in $C873
1472 CC5A: 9C DC C8 STZ $C8DC ; Zero out $C8DC
1473 CC5D: B9 F4 CF LDA $CFF4,Y ; Get SCSI ID from table into A
1474 CC60: CD DA C8 CMP $C8DA ; Compare it to our SCSI ID (default is $01)
1475 CC63: F0 1F BEQ $CC84 ; Skip over if it's equal (don't query our SCSI ID)
1477 So here it's looping through all eight SCSI IDs, starting with the lowest
1478 priority and working its way up to the highest (for reference, the table at
1479 $CFF4 has the following values: $01, $02, $04, $08, $10, $20, $40, $80). It
1480 compares the SCSI ID from the table to the SCSI ID of the card, and skips over
1481 the following code (down to $CC84) if it's the same.
1483 CC65: 8D DB C8 STA $C8DB ; Else, put SCSI ID to look at in $C8DB
1484 CC68: 64 4F STZ $4F ; Zero out $4F (error flag)
1485 CC6A: 20 5F CF JSR $CF5F ; Do TEST UNIT READY (calls bank 16:0)
1487 This is the code that I was now able to successfully navigate with my hard
1488 drive emulation. It emulated exactly one SCSI ID, and that one ID returned
1489 here successfully (every other ID, obviously with nothing connected to the bus,
1490 returned failure). However, I could see from the log file that it was trying
1491 to issue some more commands--which was puzzling, but told me that I needed to
1492 dig even deeper into the code.
1494 CC6D: A5 4F LDA $4F ; Get error code
1495 CC6F: D0 0F BNE $CC80 ; Skip over if error occurred
1497 This is fairly straightforward; it checks the error code returned from the call
1498 we made to bank 16:0, and if it's anything but zero, skip over the following
1501 CC71: EE 0D C8 INC $C80D ; Success means add one to $C80D (# of devices)
1502 CC74: 20 9F CC JSR $CC9F ; & call Function 1 in this bank (INQUIRY + MORE)
1503 CC77: 90 0B BCC $CC84 ; Check next ID if C == 0
1505 So here we increment a counter, which we suppose to be a count of the number of
1506 valid devices we have found on the SCSI bus. And here, we come to the
1507 realization that it isn't just hard drives that can talk to the Apple High
1508 Speed SCSI card, it's also printers, scanners, tape drives and whatnot. And
1509 so, it makes perfect sense that TEST UNIT READY is only the first step in
1510 discovering if a device is a hard drive or not because here, it calls function
1511 1 of bank 3 (the bank we're currently in) which is what issues more commands to
1512 figure out what the device it's talking to actually *is*.
1514 CC79: A9 99 LDA #$99 ; Else, stuff $99 into $C887
1515 CC7B: 8D 87 C8 STA $C887
1516 CC7E: 80 17 BRA $CC97 ; & signal success
1518 So if the call to $CC9F (INQUIRY + MORE) returned with the carry flag set, it
1519 stuffs a magic number into $C887, signals success and returns.
1521 CC80: C9 80 CMP #$80 ; Was error $80?
1522 CC82: F0 16 BEQ $CC9A ; Signal NoDrive error if so
1524 This is where it lands if the TEST UNIT READY call returned a non-zero result
1525 in the "error code" memory location. if it equals $80, it puts the ProDOS error
1526 code for a "NoDrive" error into the error code and returns.
1528 CC84: AC 73 C8 LDY $C873 ; Restore Y
1529 CC87: 88 DEY ; Done looking at all IDs?
1530 CC88: 10 CD BPL $CC57 ; Go back if not.
1532 Here we decrement the counter and loop back if we haven't looked at all eight
1533 (except for the card's) SCSI IDs. Otherwise, we've finished, and fall through
1536 CC8A: A9 77 LDA #$77 ; Else, stuff $77 into $C80A & $C887
1537 CC8C: 8D 0A C8 STA $C80A
1538 CC8F: 8D 87 C8 STA $C887
1539 CC92: AD 0D C8 LDA $C80D ; Did we find any devices?
1540 CC95: F0 03 BEQ $CC9A ; Signal NoDrive if not
1541 CC97: 64 4F STZ $4F ; Else, signal success
1542 CC99: 60 RTS ; & return
1544 So here it stuffs the magic number $77 into $C887 and $C80A; it also checks the
1545 "number of devices found" memory location, and signals a "NoDrive" error if the
1546 count is equal to zero.
1548 CC9A: A9 28 LDA #$28 ; Return $28 (NoDrive) in $4F
1552 This is the landing location for the various failure modes seen up above; it
1553 simply puts the ProDOS "NoDrive" error into the error flag and returns.
1555 So now I get to figure out what the commands are in that call to 3:1 that are
1556 causing the card to return in a failure mode.
1559 The Test Is Easy, When You Have The Answer Key
1560 ----------------------------------------------
1562 At this point, even though I had the hard drive emulation doing a proper dance
1563 through the TEST UNIT COMMAND, it was in a very crude state and couldn't really
1564 do anything else. And so I had to take a closer look at the seemingly
1565 impenetrable code that set up a bunch of memory locations before calling bank
1566 16:0 to see if I could make sense of it.
1568 Rather than go through every last one, I will go through part of the first such
1569 piece of code, as it's instructive:
1571 CD0E: 20 A4 CF JSR $CFA4 ; Set $60/1 to $C923, $56/7 to $C92F
1572 CD11: 20 B9 CF JSR $CFB9 ; Put $C9C3 into $C92F/30, zero $C931
1573 CD14: A9 12 LDA #$12 ; Put $12 into $C923
1574 CD16: 8D 23 C9 STA $C923
1575 CD19: 9C 24 C9 STZ $C924 ; Zero out $C924-6, $C928
1576 CD1C: 9C 25 C9 STZ $C925
1577 CD1F: 9C 26 C9 STZ $C926
1578 CD22: 9C 28 C9 STZ $C928
1579 CD25: A9 1E LDA #$1E ; Put $1E in $C927, $C933 (length of reply, 30)
1580 CD27: 8D 27 C9 STA $C927
1581 CD2A: 8D 33 C9 STA $C933
1583 So we can see right off the bat that it's setting up zero page locations $60
1584 and $61 to point to memory at $C923, and that it sets up six bytes at that
1585 location with the following:
1587 C923: 12 00 00 00 1E 00
1589 Reaching back to our crash course on SCSI commands, we can see by the first
1590 byte, since the top three bits are all zero, that this must be a six-byte
1591 command. And after that, uh, well, we don't really know much of anything. So
1592 after digging around some more for something even remotely relevant, I found a
1593 document dealing with SCSI-2 and SCSI-3 hard disk interfacing--which told me,
1594 first of all, that $12 was the INQUIRY command, and second, that the fifth byte
1595 in the command was the length of the message that the Initiator was expecting
1596 back from the target in response to this command. Progress!
1598 CD2D: 20 CB CF JSR $CFCB ; Call bank 16:0 (Do INQUIRY command)
1600 CD32: F0 05 BEQ $CD39 ; Skip over if no error
1602 And this, as we now know, does the phase to phase dance from start to finish,
1603 and checks the resulting error code to do any necessary error handling. But
1604 what of the response? How do we know what to say from our emulated hard disk
1605 back to the firmware? The hard disk interface document had something that
1606 looked plausible, if overlong (it seems that latter day SCSI drives are
1607 expected to return 148 bytes instead of 30). So I expected that I could adapt
1608 that to suit the purposes of the emulation.
1610 It was obvious that I had to write code to handle more than just the TEST UNIT
1611 READY command, and that it had to be able to send and receive data over the
1612 SCSI bus, which it, in its current state, couldn't do. Eventually I was able
1613 to get that working and I could see that the firmware was successfully
1614 negotiating the INQUIRY command *and* coming to the conclusion that it was
1615 talking to a hard disk. More progress!
1617 And, as it turns out, this first call in bank 3:1 is what determines what the
1618 device we're talking to actually is, and it sets up appropriate memory
1619 locations to signal that to other parts of the firmware. This is another one
1620 of those places where the "Technical Manual for the Apple SCSI Card" had a
1621 useful tidbit, namely a small table that looked something like this:
1624 ------------------------------
1625 $03 Nonspecific SCSI
1627 $06 Direct-access tape drive
1632 These device codes are different from the device codes that the INQUIRY command
1633 returns, and this bit of code also does the translation from one to the other.
1636 The Next Part, In Which More Progress Is Made
1637 ---------------------------------------------
1639 And so, in using similar analysis in the other parts of the code called by bank
1640 3:1, I was able to discern that after the INQUIRY command, it was calling the
1641 MODE SENSE, MODE SELECT, READ CAPACITY and READ commands afterward. And since
1642 I didn't know exactly what these commands returned, I used the time honored
1643 method of returning messages consisting of all zeroes.
1645 And, in fixing up the hard drive emulation to respond to these commands, I
1646 could see the firmware was making it all the way through the bank 3:1 code
1647 successfully, and not in a failure mode. It didn't boot anything yet, as I
1648 hadn't written the code to load a hard disk image much less dole it out over
1649 the SCSI bus, but it was a good result and I could finally see the end of this
1650 Herculean task coming into view.
1652 However, I could see from the log file that something still wasn't quite right.
1655 The Next Part, In Which Things Start Getting LUN-ey
1656 ---------------------------------------------------
1658 The problem was one of too much success. It wasn't going through the set of
1659 INQUIRY, MODE SENSE, MODE SELECT, READ CAPACITY and READ commands just once, it
1660 was doing it *eight* times. And in looking for the culprit, I found the
1663 CCE5: EE DC C8 INC $C8DC ; Increment a counter
1664 CCE8: AD DC C8 LDA $C8DC
1665 CCEB: C9 08 CMP #$08
1666 CCED: D0 B0 BNE $CC9F ; Loop back if we haven't checked 8 times yet
1668 It wasn't obvious on first examination, but I eventually figured out that
1669 location $C8DC was being put into byte one of every command being sent over the
1670 SCSI bus--as I could see the INQUIRY command was changing every time it was
1682 And so, after more digging into the hard disk interface document, I could see
1683 that the field being modified was called the Logical Unit Number, or LUN for
1684 short. Further, hard disks conforming to the SCSI-2 and SCSI-3 had a
1685 commandment, that being as follows:
1687 The LUN Shall Be Zero, And Zero Shall The LUN Be. It Shall Be No Other Number
1688 Save For Zero, For Any Other Number Shall Be An Abomination Before The Drive.
1690 Well, going by simple logic, it would appear that the SCSI-1 protocol was not
1691 bound by such a rule, and so you could have eight Logical Units for each SCSI
1692 device on the bus. But this presents an interesting challenge. We need to
1693 tell the firmware to pound sand for all but one LUN.
1696 Failure Is An Option
1697 --------------------
1699 And so I found myself in the position of needing to have the hard drive
1700 emulation fail in a meaningful way; which sounds like an oxymoron but really
1701 isn't. I needed to code the hard drive emulation to respond with a CHECK SENSE
1702 message, which is how, I eventually discovered, that you signal an error
1703 condition in the SCSI protocol. When I did this, the firmware then sent a
1704 REQUEST SENSE command, which I wasn't sure how to craft a response that would
1705 signal failure for an invalid LUN. Responding with all zeroes didn't signal
1706 failure as I hoped it would, so it was back to the hard disk interface document
1707 to find the missing information.
1709 There I found out that byte two of the response is a four-bit "Sense Key", and
1710 that zero corresponds to "No Sense", which means the command was successful.
1711 Which, as it turns out, is no way to signal failure. The one that fit the bill
1712 was five, which corresponds to "Illegal Request".
1714 And so it seems that 16 Sense Keys was not enough for the designers of the SCSI
1715 protocol, so those Sense Keys correspond to broad categories. To give even
1716 more fine-grained responses to what went wrong, there are at least two more
1717 eight-bit bytes called the "Additional Sense Code" and "Additional Sense Code
1718 Qualifier", which, taken together, provide for 65,536 different combinations.
1719 And, in the interface document, I found $08 $00 which corresponds to "Logical
1720 Unit Communication Failure" which seemed like a reasonable message for this
1723 Coding up the meaningful failure path and running the emulation showed that
1724 this mostly satisfied the firmware; it would almost get all the way to the
1725 point where it attempted to read block zero from the hard drive in a
1726 non-failure mode, but there was still a small problem.
1729 Every Problem Is Small, From A Certain Point Of View
1730 ----------------------------------------------------
1732 There is a call in the bank 3:1 code that calls bank 4:0 to read a block from
1733 the disk and do some analysis on what it finds. The logs also showed that this
1734 code was also doing a lot of writing to slot I/O register $F. Much of it being
1735 calls to the following brief routine:
1737 CFB4: AD 86 C8 LDA $C886 ; Get the value in $C886
1738 CFB7: 4A LSR A ; Shift the hi nybble to the lo nybble
1742 CFBB: 09 08 ORA #$08 ; Set the high bit of the lo nybble
1743 CFBD: 9D 6F C0 STA $C06F,X ; & store it in slot I/O register $F
1746 This was some highly suggestive code, and what it suggested was that it was
1747 using three bits of a value set up elsewhere which made for eight combinations.
1748 The only significant loose end, as far as the hardware was concerned, was the
1749 8K static RAM; in all of the analysis I had done up to this point, it *seemed*
1750 that only 1K of it was ever used. But this code suggested otherwise.
1752 It was suggesting that slot I/O register $F was a bank select soft switch for
1753 the 8K static RAM; once I coded it up as such, the firmware was then completely
1754 satisfied and would get all the way to where it attempted to read block zero
1755 from the hard drive in a non-failure mode.
1761 And so, having studiously and painstakingly laid the foundation for the actual
1762 purpose of the hard drive emulation--that being the transfer of data to and
1763 from the thing--I came at last to the part where I had to actually write code
1764 to have real data flowing to and from the emulated hard disk. And this, as it
1765 turns out, was the least interesting part of the whole thing; getting the
1766 contents of files into memory and parsing them is a really trivial thing and
1767 usually quite boring.
1769 So in writing this bit of code, I used 4am's "Pitch Dark" hard drive image, and
1770 added the necessary code to serve up appropriate slices of it in response to
1771 the firmware's READ command. And, of course, after running the new emulation
1772 it failed to load anything.
1774 It was then that I remembered that I sent back messages of all zeroes to
1775 requests from commands, for the most part, with a few exceptions. One of these
1776 that was sure to cause problems without a proper response was the READ CAPACITY
1777 command. When the firmware inquired about the size of the hard drive, the
1778 emulator would happily tell it that it had zero capacity--which meant that any
1779 attempted reads by the firmware would be out of range.
1781 So I coded up a proper response for the size of the hard drive image I was
1782 using and fired up the emulator and... It still didn't work. The logs told me
1783 that it was sending a ten-byte command, and one I hadn't seen before, which was
1784 basically the ten-byte variant of the READ command. Once I had *that* coded up
1785 properly, I fired up the emulator and after a few seconds, found myself in the
1788 What? Why? How does this even--
1790 To quell the questions that were pooling up in my head I wrote some hooks into
1791 the emulator to trigger a code trace at the appropriate time; that being where
1792 the code transfered control to memory address $801, the ostensible location
1793 where the firmware allegedly read from block zero and placed it in memory at
1794 $800. And I knew that it was getting to that point successfully because the
1795 firmware doesn't get there unless everything is working on the SCSI bus as it
1796 should, and the trace in the log file confirmed this.
1798 There are worse things than being dumped into the Apple II monitor; at least I
1799 could poke around memory and disassemble things to try to figure out what was
1800 going wrong. And I could see that the block that was loaded into memory was
1801 looking at the slot ROM for a certain value that caused it to take a branch
1802 that landed it in a crash zone. This made no sense whatsoever.
1804 Fortunately for me though, I have the ability to disassemble a snapshot of any
1805 memory range that I desire--so I disassembled the entire block from $800 to
1806 $9FF. And what I saw there was still strange; near the end of the block it
1807 just kind of ran out of instructions, like something was missing. And looking
1808 near the middle of the block, I saw something eerily similar to what I saw at
1811 Then I realized it wasn't similar, it was *identical*. Looking through the
1812 hard drive emulator code, I was not surprised to find this:
1814 static uint8_t * buf;
1815 static uint8_t bufPtr;
1817 Yes, I had made a rookie mistake of using too small of a value for my buffer
1818 pointer; it was loading the correct block, but, because the buffer pointer was
1819 only eight bits wide, it only copied the first 256 bytes out of the hard drive
1822 As embarrassing as this was, it was also good news, as it meant that firmware
1823 bootstrap code was working; it was reading real data from the hard drive
1824 emulation and running correctly. Which meant that once I fixed the size of my
1825 buffer pointer, the emulated hard drive should boot up correctly.
1827 And once I coded up the fix and started up the emulator once more, after six or
1828 so seconds, "Pitch Dark" came up on the screen and it was glorious...
1831 Sic Transit Gloria Mundi
1832 ------------------------
1834 I was able to navigate forward and back through the various games on the hard
1835 drive image; I could even view the artwork that came with each one. And lo and
1836 behold: the games worked!
1838 I was playing through a bit Wishbringer when I got to a point where I wanted to
1839 save my game. And, even though there was no WRITE command hooked up yet, I
1840 tried it anyway and got a nice hard lockup on the emulator. This would never
1841 do--to have a hard disk that was read-only--so I coded up the WRITE command
1844 And upon booting up the hard drive, it looked like it was OK, only there were
1845 problems; namely, while you could navigate through the various games, you could
1846 not launch them. As a matter of fact, the only game that *could* be launched
1847 was Zork I, which was the first game to pop up on the menu. So after looking
1848 the code, I noticed that there was an asymmetry in the ports used for reading
1849 and writing to the SCSI bus. Which requires a brief digression into data
1853 To DMA, Or Not To DMA, That Is The Question
1854 -------------------------------------------
1856 As it turns out, I was finally able to figure out that the physical DMA on/off
1857 switch on the card was wired to bit 6 of slot I/O register $C. I further found
1858 out that, since I was defaulting to zero for any unknown bit in the slot I/O
1859 registers, that it was treating the DMA switch as if it were in the off
1860 position. However, even so, the firmware was still treating this as a DMA
1863 And, looking at the 53C80 manual, I could see that it supported three distinct
1864 kinds of bus I/O: Programmed I/O (or PIO for short), Direct Memory Access (or
1865 DMA for short) and Pseudo DMA. Of these three, PIO is the slowest, as it
1866 relies 100% on handshaking on the SCSI bus for data transfer, while DMA is the
1867 fastest, as all you need to do is set some registers and tell the 53C80 to go
1868 and it handles the transfer all in the background without the need for any
1869 intervention from the CPU whatsoever. But what the firmware was doing, in this
1870 DMA switch in the off position mode, was Pseudo DMA.
1872 How it works for reading data from the SCSI bus is that the CPU monitors bit 6
1873 (DMA REQ) in the slot I/O register $5, then reads the data that shows up in
1874 slot I/O register $6 when the DMA REQ bit is asserted. For this kind of
1875 transfer to work, however, there must be some kind of address decoding that
1876 will assert the DACK (Dma ACKnowledge) line once the data is read. Because
1877 this code works, we can logically deduce that the read to slot I/O register $6
1878 is wired to produce this signal, even if we can't prove it conclusively through
1879 the schematic of the card.
1881 Writing works in a similar manner by monitoring the DMA REQ line, but instead
1882 of writing to slot I/O register $6 (which is a trigger for starting a DMA
1883 transfer) it writes to slot I/O register $0. And, as we inferred through logic
1884 about the setting of the DACK line in the reading case, we can similarly infer
1885 that the DACK line is being set in a similar manner in the writing case.
1887 The upshot is, even though Pseudo DMA transfers are still CPU intensive, they
1888 are faster than PIO transfers. And when it comes to relatively slow CPUs like
1889 the 65C02, faster is better.
1892 And They All Lived Happily Ever After-ish
1893 -----------------------------------------
1895 So in looking at the code for the WRITE command, I could see that I had it
1896 using register $6 for the data transfer, which, as we can see from the short
1897 digression above, won't work. Fixing this to look at the correct register ($0)
1898 brought things into alignment, and a thorough test of "Pitch Dark" confirmed
1899 that I had indeed solved the problem.
1901 So, in the final analysis, I was finally able to restore decency to Apple2 and
1902 play "Pitch Dark" on it to boot. But was it worth it? In my opinion the
1903 answer is an unequivocal "yes", and not just because it enables the use of hard
1904 drive images in emulators.
1906 The reason this little exercise in digital archaeology was worth the effort
1907 expended is that it underscores a problem that seems to have gone largely
1908 underappreciated: the early microcomputers, in some respects, are very well
1909 documented; however, in many others, they are not--and the knowledge of exactly
1910 how they worked is in danger of disappearing. The fact that the documentation
1911 for the Apple High Speed SCSI card is of a consumer oriented nature with very
1912 little technical content was of little use in figuring out how it really
1913 worked, and shows a marked contrast to the early days of Apple where they
1914 published very detailed information about their computers and how they worked,
1915 including schematics and source code.
1917 All that is to say that unless those of us who still remember these artifacts
1918 and have the ability to analyze them to tease out their inner workings actually
1919 *do* so, these things *will* disappear, and they will pass out of human memory