Commented disassembly of the Apple II SCSI card ROM (341-0437-A rev.c).
6502bench sourcegen from faddensoft has been used and dis65 files are provided.
The included makefile will reassemble the complete rom to use in an EPROM programmer or with MAME.
The rom contains sixteen 1KB banks that are mapped at $cc00. Bank 0 contains boot code that is also shadowed at $cn00. The boot code is used by the Apple IIgs SCSI Driver to identify the card (the apple driver contains a copy of this section and performs a full compare upon load). Any changes to this section will make the driver fail to load.
The ram on the card is also banked (eight 1KB banks) but it seems the on board firmware only uses bank 0. Bank 1 is used for the Patch1Call and two additional unidentified calls.
ROM Bank 0 and Bank 8 are identical except the last 4 bytes. This is required because A13 of the ROM chip is not gated by IOSEL# (HW bug in the card). Bank 0 or 8 will be randomly selected when IOSEL# is asserted depending on the last state of bit 3 in the BANK_SEL register.
Code at $cfcc allow calling functions in different banks.
In Appendix B the TRM incorrectly documents some of the BANKSWITCH calls. For some calls the nibbles have been swapped.
DataXin is FN_01 ($10 in TRM)
DoStatus is FN_30 ($03 in TRM)
LongData is likely FN_29 ($92 in TRM)
In the text description 1 should be subtracted to both the ROM and RAM bank numbers to get to the correct location.
The TRM documents SmartPort Control code $17 (ReceiveDiag) which is disabled in the command interpreter even if the code seems to be there.
SDAT end address is wrong in TRM: manual says SDATs run $C831-$C897, but 7 entries * 17 bytes = 119 bytes, so end is $C8A7.
Figure 3-2: DIBTAB field label is incorrect: manual labels bytes 2-5 as “block size”, but the text and source behavior treat them as device block count (size in blocks). Source fills DIBTAB from DEV_SIZE, not bytes-per-block. See bank13.bin_cc65.S:107.
Patch1Call RAM bank: manual says patched code is in RAM bank 2 at $C803; source initializes and executes $C803 in RAM bank 1. See bank05.bin_cc65.S:205 and bank11.bin_cc65.S:329.
bank01.bin_cc65.S:340 times out as success in WAIT_FOR_REQ_NOT_STATUS. If REQ never asserts, both counters expire and execution falls into @req_found, returning carry clear. Callers such as FN_61/FN_71 can then start DMA even though the target never requested a transfer.
bank02.bin_cc65.S:277 rejects SmartPort control $17 as ERROR_BADCTL, making SP_RECV_DIAG code unreachable. bank11.bin_cc65.S:249 has a SP_RECV_DIAG implementation in the $16-$1e handler table. Unclear if the Receive Diagnostic code works.
bank03.bin_cc65.S:643 is the only bank-switch epilogue that returns without clc. Other banks normalize carry on successful bank return; bank 3 can leak carry from the callee and make callers misread a successful banked call as failure.
bank06.bin_cc65.S:555 arbitration failure returns without clearing the 5380 arbitrate bit set at bank06.bin_cc65.S:543. FN_36 treats carry as failure and exits, leaving the controller in arbitration mode. Later SCSI operations may inherit a bad controller state.
bank06.bin_cc65.S:311 and bank09.bin_cc65.S:406 ense key is masked with #$07; SCSI sense key is a low nibble, so this should likely be #$0f.
bank15.bin_cc65.S:272 saves BUF_PTR_XH to TMP_BUF_PTR4+3 / $c9fa, but restores from $c9fb. That corrupts the restored 4-byte buffer pointer after disk-change handling.
TIMEOUT byte order seem to be used inconsistenly in differet banks. Bank14 TIMEOUT decrement seems buggy.
The Apple TRM defines apple device type $06 as direct access tape drive (???). This is inconsistent with the SCSI documentation as SCSI type $01 (sequential access) and all direct access removable devices (SCSI type $00) that are not CD_ROMs end up as type $06. From the disassemby Apple type $06 is sequential access or removable direct access (the block device flag can be used to identify direct access (block dev ) vs. sequential (char dev)), but excludes devices of SCSI type $07 (Optical memory).
SCSI uses big endian for multi bytes quantities, both prodos and smartport use little endian. The FW has a mix of both (i.e. SDAT is big endian, DIBTAB is little endian) making following code quite difficult and error prone. I don't exclude there are some bugs hiding in the block calculations.
DEV_IDX seems to start either at 0 or 1 depending on the code path.
All banks contain the same code fragment at $cfcc to support banks switching. Bank03 is missing a CLC before the RTS that ends the bankswitch code.
bank07.bin_cc65.S:157 has an unconditional recursion path in FN_07. LCC51 calls @LCC95, @LCC95 calls @LCC8B, and @LCC8B falls straight back into @LCC95 because the beq @rts after dex is never true from #$06.
This ends up to the following code fragment:
@rts:
rts
LCC8B:
lda #$00
ldx #$06
sta SCSI_CDB_CMD,x
dex
beq @rts
LCC95:
jsr LCC8B
which will crash the machine due to a stack overflow. FN_07 is called from FN_09 (Smartport read) and FN_19 (Smartport Write) when the targeted device is a character device. It's likely that character devices do not work at all.
Removable direct access devices are incorrectly assigned apple type $06 (Sequential access) instead of $07 (HDD) or $03 (Generic SCSI). The Apple HS SCSI card firmware assigns $07 to the same device.