ca65 V2.6.9 - (C) Copyright 1998-2000 Ullrich von Bassewitz Main file : SPDRV.S Current file: SPDRV.S 000000r ;------------------------------------------------------------------------------ 000000r ; ProDOS/SmartPort driver for CompactFlash/IDE Interface for Apple II computers 000000r ; SPDRV.S Version 1.2 - 04/12/2002 000000r ; 000000r ; Firmware Contributors: Email: 000000r ; Chris Schumann cschumann@twp-llc.com 000000r ; Rich Dreher rich@dreher.net 000000r ; Dave Lyons dlyons@lyons42.com 000000r ; 000000r ; 000000r ; This code requires a 65C02 or 65C816 equipped machine. 000000r ; 000000r ; Tools used to build this driver: CA65: 6502 Cross Assembler 000000r ; http://www.cc65.org/ 000000r ; 000000r ; Here is the copyright from that tool using --version option 000000r ; ca65 V2.6.9 - (C) Copyright 1998-2000 Ullrich von Bassewitz 000000r ; 000000r ; Example build instructions on an M$DOS based machine: 000000r ;------------------------------------------------------ 000000r ; Assumes you have installed the CC65 package and set your path, etc. 000000r ; 000000r ; 1) c:\firmware> ca65 -t apple2 --cpu 65C02 -l spdrv.s 000000r ; 2) c:\firmware> ld65 -t apple2 spdrv.o -o spdrv.bin 000000r ; 3) Because the EPROM can hold up to 8 user selectable version of firmware 000000r ; you must load spdrv.bin into your EPROM programmer with one of the 000000r ; following offsets: 000000r ; (Note: this offset has nothing to do with the card slot offsets) 000000r ; 000000r ; for driver #0: use offset $0100. Selected with: A14= IN, A13= IN, A12= IN 000000r ; for driver #1: use offset $1100. Selected with: A14= IN, A13= IN, A12=OUT 000000r ; for driver #2: use offset $2100. Selected with: A14= IN, A13=OUT, A12= IN 000000r ; for driver #3: use offset $3100. Selected with: A14= IN, A13=OUT, A12=OUT 000000r ; for driver #4: use offset $4100. Selected with: A14=OUT, A13= IN, A12= IN 000000r ; for driver #5: use offset $5100. Selected with: A14=OUT, A13= IN, A12=OUT 000000r ; for driver #6: use offset $6100. Selected with: A14=OUT, A13=OUT, A12= IN 000000r ; for driver #7: use offset $7100. Selected with: A14=OUT, A13=OUT, A12=OUT 000000r ; 000000r ; where IN = jumper shorted, and OUT = jumper open 000000r ; Driver #0 through #7 correspond to the user selectable 000000r ; jumpers: J6 (A14,A13,A12) on the interface card. 000000r ; 000000r ; 4) Load as many firmware versions, up to 8, into the EPROM programmer as you 000000r ; want. Remember that most programmers will, by default, clear all unused 000000r ; memory to $FF when you load a binary file. You will need to disable that 000000r ; feature after loading the first file. 000000r ; 000000r ; 5) Now you have an EPROM ready image to be programmed. 000000r ; Using a standard 27C256 EPROM or similar, program your EPROM. 000000r ; 000000r ; Firmware Version History 000000r ;------------------------- 000000r ; Version 1.2 000000r ; - Start of SmartPort driver. Based on Version 1.1 ProDOS driver 000000r ; 000000r ; Version 1.1 000000r ; - dynamically calculate drive sizes in GetStatus function 000000r ; - turn off interrupts in case boot code is called manually 000000r ; - add cardID/firmware revision bytes: CFFA$xx 000000r ; - added continuation of boot scan if device not present 000000r ; - added continuation of boot scan if boot block code looks invalid 000000r ; - reformatted this source file, removed tabs and added function headers 000000r ; 000000r ; Version 1.0 000000r ; - initial version for Prototype #2 with descrete latches 000000r ; 000000r ; PLD Logic Firmware Information 000000r ;------------------------------- 000000r ; This version of firmware assumes you are using PLD logic of at 000000r ; least version 1.2. The source files for U6, the Altera PLD are: 000000r ; Appleideinterface.gdf 000000r ; Appleidelogic.tdf 000000r ; 000000r ; The programmer ready output file for the PLD logic is: 000000r ; Appleideinterface.pof 000000r ; 000000r ; These files are not included with this code. 000000r ; 000000r ; Acknowledgements 000000r ;----------------- 000000r ;Thanks to: 000000r ; Chris Schuman - for his extensive initial development work 000000r ; David Lyons - for technical information, many improvement ideas and 000000r ; SmartPort code development 000000r ; 000000r 000000r .define EQU = 000000r .define TRUE 1 000000r .define FALSE 0 000000r 000000r 000000r ; 000000r ; Firmware Version Information 000000r ; 000000r FIRMWARE_VER EQU $12 ;Version 1.2 (Version of this code) 000000r SPDRIVERVERSION EQU $1200 ;SmartPort version 1.2 000000r GSOS_DRIVER EQU $02 ;GS/OS driver will check this byte to see if it 000000r ;is still compatible with this firmware. 000000r ;Increment by one, when something changes that 000000r ;would require a change in the GS/OS driver. 000000r ;Otherwise only change the FIRMWARE_VER for all 000000r ;other changes. 000000r ;01 = ProDOS Driver supporting 2 drives 000000r ;02 = SmartPort Driver supporting 4 drives 000000r ;03 = SmartPort Driver supporting 8 drives 000000r ; 000000r ; Firmware Configuration Settings: 000000r ; 000000r SMARTPORT EQU TRUE 000000r BLOCKOFFSET EQU 0 ;0..255: LBA of first block of first partition 000000r PARTITIONS32MB EQU 4 ;Number of 32MB Partitions supported 000000r ;Remember, ProDOS only supports 13 total 000000r ;volumes for all devices, floppies, SCSI drives, 000000r ;RAM drives, etc. 000000r 000000r 000000r ;------------------------------------------------------------------------------ 000000r ; To enable debug output, set DEBUG = TRUE, only if you have a Apple Super 000000r ; Serial card in slot 2. This will output one line of text for each request 000000r ; made to the firmware, which can be seen on a computer or terminal attached to 000000r ; the SSC. 000000r ; 000000r ; NOTE: If you use DEBUG=TRUE and you don't have an Apple Super Serial card in 000000r ; slot 2, your computer might hang in the routine DSChar, waiting for 000000r ; the SSC status bit to say it is okay to write to the 6551 UART. 000000r ; 000000r ; Set your terminal (software) at the remote end as follows: 000000r ; BaudRate: 19200 000000r ; Data Bits: 8 000000r ; Parity: None 000000r ; Stop Bits: 1 000000r ; 000000r ; Example debug output at terminal from CAT command in ProDOS 8. Card is in 000000r ; slot 6. ProDOS makes a ProDOS 8 call to the firmware to read block 2 from 000000r ; unit: 60 into buffer memory at $DC00. 000000r ; 000000r ; P8: Rd B:0002 U:60 A$DC00 Chk$6711 000000r ; 000000r ; Rd = ProDOS Read ($01). Also could be Wr = write ($02), St = Status ($00) 000000r ; U:60 = ProDOS 8 unit number $60 Slot 6, drive 1 000000r ; A$DC00 = ProDOS buffer address 000000r ; Chk$6711 = Simple block checksum used to visually check data integrity 000000r ; 000000r ; NOTE: When DEBUG is true, some zero-page locations are used. The data at 000000r ; these locations are saved and restored and should not impact other programs. 000000r 000000r DEBUG = FALSE 000000r ;DEBUG = TRUE 000000r 000000r ;------------------------------------------------------------------------------ 000000r ; Driver constant definitions 000000r ; 000000r INITDONESIG EQU $A5 ;Device init is done signature value 000000r CR EQU $0D 000000r BELL EQU $07 000000r 000000r ; ProDOS request Constants 000000r PRODOS_STATUS EQU $00 000000r PRODOS_READ EQU $01 000000r PRODOS_WRITE EQU $02 000000r PRODOS_FORMAT EQU $03 000000r 000000r ;ProDOS Return Codes 000000r PRODOS_NO_ERROR EQU $00 ;No error 000000r PRODOS_BADCMD EQU $01 ;Bad Command (not implemented) 000000r PRODOS_IO_ERROR EQU $27 ;I/O error 000000r PRODOS_NO_DEVICE EQU $28 ;No Device Connected 000000r PRODOS_WRITE_PROTECT EQU $2B ;Write Protected 000000r PRODOS_BADBLOCK EQU $2D ;Invalid block number requested 000000r PRODOS_OFFLINE EQU $2F ;Device off-line 000000r 000000r ;SmartPort return codes 000000r BAD_UNIT_NUMBER EQU $11 000000r 000000r ; ATA Commands Codes 000000r ATACRead EQU $20 000000r ATACWrite EQU $30 000000r ATAIdentify EQU $EC 000000r 000000r ;Constants for Wait 000000r ; Constant = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 000000r 000000r WAIT_100ms EQU 197 000000r WAIT_40ms EQU 124 000000r WAIT_100us EQU 4 000000r ;------------------------------------------------------------------------------ 000000r ; Slot I/O definitions 000000r ; 000000r mslot = $7F8 ;Apple defined location for the last active slot 000000r 000000r IOBase = $C080 000000r ATADataHigh = IOBase+0 000000r SetCSMask = IOBase+1 ;Two special strobe locations to set and clear 000000r ; MASK bit that is used to disable CS0 line to 000000r ; the CompactFlash during the CPU read cycles 000000r ClearCSMask = IOBase+2 ; that occur before every CPU write cycle. 000000r ; The normally inoccuous read cycles were 000000r ; causing the SanDisk CF to double increment 000000r ; during sector writes commands. 000000r 000000r ATADevCtrl = IOBase+6 ;when writing 000000r ATAAltStatus = IOBase+6 ;when reading 000000r ATADataLow = IOBase+8 000000r ATAError = IOBase+9 000000r ATASectorCnt = IOBase+10 000000r ATASector = IOBase+11 000000r ATACylinder = IOBase+12 000000r ATACylinderH = IOBase+13 000000r ATAHead = IOBase+14 000000r ATACommand = IOBase+15 ; when writing 000000r ATAStatus = IOBase+15 ; when reading 000000r 000000r ; Scratchpad RAM base addresses. Access using the Y register containg the slot # 000000r ; 000000r DriveResetDone = $478 ;remember the device has been software reset 000000r DriveNumber = $4f8 ;normally 0 to 3 for four 32MB partitions 000000r SerialInitDone = $578 ;For debug: if $A5 then serial init is complete 000000r DrvBlkCount0 = $5f8 ;low byte of usable block count 000000r ; (excluding first BLOCKOFFSET blocks) 000000r DrvBlkCount1 = $678 ;bits 8..15 of usable block count 000000r DrvBlkCount2 = $6f8 ;bits 16..23 of usable block count 000000r DrvMiscFlags = $778 ;bit 7 = raw LBA block access 000000r Available2 = $7f8 ;not currently used 000000r 000000r ;------------------------------------------------------------------------------ 000000r ; Zero-page RAM memory usage 000000r 000000r .IF DEBUG ;data at these locations saved and restored 000000r MsgPointerLow = $EB 000000r MsgPointerHi = $EC 000000r CheckSumLow = $ED 000000r CheckSumHigh = $EE 000000r .ENDIF 000000r 000000r zpt1 = $EF ;data at this location is saved/restored 000000r 000000r StackBase = $100 000000r 000000r ; ProDOS block interface locations 000000r pdCommandCode = $42 000000r pdUnitNumber = $43 000000r pdIOBuffer = $44 000000r pdIOBufferH = $45 000000r pdBlockNumber = $46 000000r pdBlockNumberH = $47 000000r 000000r ; Arbitrary locations for Smartport data, 000000r ; these locations are saved/restored before exit. 000000r spCommandCode = pdCommandCode 000000r 000000r spParamList = $48 ;2 bytes 000000r spCmdList = $4A ;2 bytes 000000r spCSCode = $4C 000000r spSlot = $4D 000000r spSlotX16 = $4E 000000r spLastZP = spSlotX16 000000r 000000r spZeroPgArea = pdCommandCode ; $42 000000r spZeroPgSize = spLastZP-spZeroPgArea+1 000000r 000000r 000000r ;------------------------------------------------------------------------------ 000000r ; Apple II ROM entry points 000000r ; 000000r ; We can use these at boot time, but not while handling a call through 000000r ; our ProDOS or SmartPort entry points, as the ROM may not be available. 000000r ; 000000r SetVID = $FE89 000000r SetKBD = $FE93 000000r COUT = $FDED 000000r INIT = $FB2F 000000r HOME = $FC58 000000r ROMWAIT = $FCA8 000000r AppleSoft = $E000 000000r 000000r ;------------------------------------------------------------------------------ 000000r ; Start of Peripheral Card ROM Space $Cn00 to $CnFF 000000r ; A macro is used here so that this code can be easily duplicated for each slot 000000r ; instead of by hand using the EPROM programmer. This is possible done because 000000r ; the hardware does not overlay the C1xx address space at C2xx, C3xx, etc. 000000r ; automatically. Instead the base address for the EPROM is $C000 but is enabled 000000r ; only when a valid address in the range of $C100 to $CEFF is on the bus. This 000000r ; allows for the development of slot specific behaviors in firmware, if desired. 000000r ; Currently that is not being done, instead the same slot code is repeated for 000000r ; every slot ROM space. Addresses $C000 to $C0FF are not decoded by the 000000r ; hardware as it is Apple's internal I/O. Any access of $CF00 to $CFFF is 000000r ; decoded by the card to reset the Expansion ROM flip-flop, but remember to 000000r ; use always address $CFFF for that task. 000000r 000000r .macro CnXX SLOTADDR, SLOTx16, SLOT 000000r .local P8DriverEntry 000000r .local P8Driver 000000r .local SmartPortEntry 000000r .local SPDriver 000000r .local Boot 000000r .local Error 000000r .local NotScanning 000000r .local wasteTime 000000r .local ErrorMsgDisplay 000000r .local msgLoop 000000r .local msgDone 000000r .local ErrorMessage 000000r 000000r lda #$20 ;$20 is a signature for a drive to ProDOS 000000r ldx #$00 ;$00 " 000000r lda #$03 ;$03 " 000000r 000000r .IF SMARTPORT 000000r lda #$00 000000r .ELSE 000000r lda #$3c ;$3c " 000000r .ENDIF 000000r bra Boot 000000r 000000r ;------------------- Non-boot P8 driver entry point --------------------- 000000r ; The EPROM holding this code is decoded and mapped into $C100 to $CEFF, 000000r ; it is not nessecary to dynamically determine which slot we are in, as is 000000r ; common in card firmware. This code is in a MACRO and is located absolutely 000000r ; per slot. Any code in this MACRO that is not relocatable will have to be 000000r ; based on the MACROs parameters SLOTADDR, SLOTx16, SLOT. 000000r 000000r P8DriverEntry: 000000r jmp P8Driver ;located at $Cn0A for best compatibility 000000r SmartPortEntry: ;By definition, SmartPort entry is 3 bytes 000000r ; after ProDOS entry 000000r jmp SPDriver 000000r 000000r Boot: 000000r ldy #SLOT ;Y reg now has $0n for accessing scratchpad RAM 000000r ldx #SLOTx16 ;X reg now has $n0 for indexing I/O 000000r lda #>SLOTADDR ;loads A with slot# we are in:$Cn 000000r sta mslot ;Apple defined location reserved for last slot 000000r ; active. MSLOT needs the form of $Cn 000000r bit $cfff ;turn off expansion ROMs 000000r 000000r ; 000000r ; Need to wait here (before CheckDevice) in case the CFFA RESET jumper 000000r ; is enabled, or a Delkin Devices CF card never becomes ready. 000000r ; 000000r ldy #5 000000r wasteTime: 000000r lda #WAIT_100ms 000000r jsr ROMWAIT 000000r dey 000000r bne wasteTime 000000r ldy #SLOT 000000r 000000r jsr CheckDevice 000000r bcs Error 000000r 000000r lda #PRODOS_READ ;Request: READ block 000000r sta pdCommandCode 000000r stz pdIOBuffer ;Into Location $800 000000r stz pdBlockNumber ;ProDOS block $0000 (the bootloader block) 000000r stz pdBlockNumberH 000000r lda #$08 000000r sta pdIOBufferH 000000r stx pdUnitNumber ;From unit number: $n0 (where n=slot#), 000000r ; so drive bit is always 0 000000r 000000r jsr P8Driver ;Read bootloader from device's block 0 into 000000r ; location $800 000000r bcs Error ;Check for error during bootblock read 000000r 000000r lda $800 ;Check the first byte of boot loader code. 000000r cmp #$01 ;If bootload code is there, this byte = $01 000000r bne Error 000000r lda $801 ;If second byte is a 0, it's invalid 000000r ; (we'd JMP to a BRK) 000000r beq Error 000000r 000000r ldx pdUnitNumber ;X should contain the unit number when jumping 000000r ; to the bootloader 000000r jmp $801 ;No errors, jump to bootloader just read. 000000r 000000r 000000r ; If any error occured, like drive not present, check to see if we are in a 000000r ; boot scan, if so re-enter scan routine, else drop to Applesoft, aborting boot. 000000r Error: 000000r lda $00 000000r bne NotScanning 000000r lda $01 000000r cmp mslot 000000r bne NotScanning 000000r jmp $FABA ;Re-enter Monitor's Autoscan Routine 000000r 000000r ;The boot code must have been called manually because we are not in a slot scan. 000000r NotScanning: 000000r jsr SetVID 000000r jsr SetKBD 000000r ; 000000r ;Display error message 000000r ; 000000r jsr INIT ;text mode, full screen, page 1 000000r jsr HOME 000000r ldy #0 000000r msgLoop: 000000r lda ErrorMessage,y 000000r beq msgDone 000000r ora #$80 000000r jsr COUT 000000r iny 000000r bra msgLoop 000000r msgDone: 000000r jmp AppleSoft 000000r 000000r ErrorMessage: 000000r .byte CR,CR,CR,CR,CR 000000r .byte "CFFA: Device missing, not formatted,",CR 000000r .byte "or incompatible.",CR 000000r ; "vX.Y" built automatically: 000000r .byte "Ver:",$30+(FIRMWARE_VER/16),".",$30+(FIRMWARE_VER & $F) 000000r ; Beep, then end-of-message: 000000r .byte BELL,$00 000000r 000000r 000000r ;------------------- Non-boot entry point for driver code ----------------- 000000r ; 000000r ; Handle a ProDOS call 000000r ; 000000r ; Setup MSLOT, X and Y registers. 000000r ; This must be done every time we enter this driver. 000000r ; 000000r P8Driver: 000000r ldy #SLOT ;Y reg now has $0n for accessing scratchpad RAM 000000r ldx #SLOTx16 ;X reg now has $n0 for indexing I/O 000000r lda #>SLOTADDR ;loads A with slot# we are in:$Cn(where n=slot#) 000000r sta mslot ;Apple defined location reserved for last slot 000000r ; active. MSLOT needs the form of $Cn 000000r bit $cfff ;turn off other ROM that might be on 000000r bit ClearCSMask,x ;reset MASK bit in PLD for normal CS0 signaling 000000r jmp P8AuxROM 000000r 000000r ;--------------------- SmartPort call handler code ----------------------- 000000r ; 000000r ; Called from jmp at $Cn0D. 000000r ; 1) Push a block of zero-page locations onto the stack, creating a work space. 000000r ; 2) Get the request parameters that follow the call to this driver 000000r ; 3) Using request parameters as pointers get request specific information 000000r ; and set up the P8 driver request registers $42-$47. 000000r ; 4) Call P8Driver code 000000r ; 5) On return from P8Driver code, restore zero page work space, and return to 000000r ; caller. 000000r ; 000000r SPDriver: 000000r lda #>SLOTADDR 000000r sta mslot 000000r bit $cfff 000000r 000000r ldx #SLOTx16 ;X reg now has $n0 for indexing I/O 000000r bit ClearCSMask,x ;reset MASK bit in PLD for normal CS0 signaling 000000r jmp SPAuxROM 000000r 000000r 000000r .RES SLOTADDR+$F5-* ;skip to $CnF5, where n is the slot# 000000r 000000r .byte GSOS_DRIVER ;GS/OS driver compatibility byte. GS/OS driver 000000r ; checks this byte to see if it is compatible 000000r ; with this version of firmware. This way, 000000r ; changes to firware versions, that do not 000000r ; affect the GS/OS driver will not prevent the 000000r ; GS/OS driver from loading and running. This 000000r ; byte should be incremented each time a change 000000r ; is made that would prevent the GS/OS driver 000000r ; from working correctly. I.e. Partition layout 000000r ; or something similar. 000000r 000000r .byte "CFFA", FIRMWARE_VER ;$CnF6..CnFA: Card Hardware ID, 000000r ; non-standard scheme 000000r 000000r .byte $0 ;$CnFB: SmartPort status byte 000000r ; Not Extended; not SCSI; not RAM card 000000r ; Even if not supporting SmartPort, we need a 000000r ; zero at $CnFB so Apple's RAMCard driver 000000r ; doesn't mistake us for a "Slinky" memory 000000r ; card. 000000r 000000r ; Data table for ProDOS drive scan 000000r ; $CnFC/FD = disk capacity, if zero use status command to determine 000000r ; $CnFE = status bits (BAP p7-14) 000000r ; 7 = medium is removable 000000r ; 6 = device is interruptable 000000r ; 5-4 = number of volumes (0..3 means 1..4) 000000r ; 3 = device supports Format call 000000r ; 2 = device can be written to 000000r ; 1 = device can be read from (must be 1) 000000r ; 0 = device status can be read (must be 1) 000000r ; 000000r ; $CnFF = LSB of block driver 000000r .word $0000 ;$CnFC-D: A zero here will cause prodos to 000000r ; rely on the status command to determine 000000r ; volume size 000000r 000000r .byte $17 ; $CnFE: support 2 ProDOS drives 000000r .byte drive # then StatusSize = $FFFF 00CB5F: ; If DrvBlkCount2 = drive # then StatusSize = DrvBlkCount1,DrvBlkCount0 00CB5F: ; If DrvBlkCount2 < drive # then StatusSize = 0 00CB5F: ; 00CB5F: ; This scheme has a special case which must be handled because ProDOS 00CB5F: ; partitions are not quite 32 meg in size but are only FFFF blocks in size. 00CB5F: ; If a device is exactly: 32meg or 10000h blocks in size, it would appear 00CB5F: ; as one drive of size FFFF and another drive of size 0000. To handle this 00CB5F: ; case, we check for an exact size of 0000 and fall into the NoDrive code. 00CB5F: ; 00CB5F:B9 F8 04 lda DriveNumber,y 00CB62:D9 F8 06 cmp DrvBlkCount2,y 00CB65:F0 0B beq ExactSize 00CB67:90 1E bcc FullSize 00CB69: 00CB69: NoDrive: 00CB69:A2 00 ldx #0 00CB6B:A0 00 ldy #0 00CB6D:A9 2F lda #PRODOS_OFFLINE 00CB6F:38 sec 00CB70:80 1C bra sExit 00CB72: 00CB72: ExactSize: ;If equal, the DrvBlkCount1,DrvBlkCount0 is the 00CB72: ; drive's exact size 00CB72:B9 F8 05 lda DrvBlkCount0,y 00CB75:19 78 06 ora DrvBlkCount1,y 00CB78:F0 EF beq NoDrive ;can't have a 0-block device 00CB7A: 00CB7A:B9 F8 05 lda DrvBlkCount0,y 00CB7D:AA tax 00CB7E:B9 78 06 lda DrvBlkCount1,y 00CB81:A8 tay 00CB82:A9 00 lda #0 00CB84:18 clc ;no errors 00CB85:80 07 bra sExit 00CB87: 00CB87: FullSize: 00CB87:A2 FF ldx #$FF ;X gets low byte of size 00CB89:A0 FF ldy #$FF ;Y gets high byte of size 00CB8B:A9 00 lda #0 00CB8D:18 clc ;no errors 00CB8E: 00CB8E: sExit: 00CB8E: .IF DEBUG 00CB8E: php ;save the carry's state 00CB8E: pha 00CB8E: jsr DSString 00CB8E: .byte "Retd:",0 00CB8E: tya 00CB8E: jsr DSByte 00CB8E: txa 00CB8E: jsr DSByteCRLF 00CB8E: pla 00CB8E: plp ;recover the carry 00CB8E: .ENDIF 00CB8E: 00CB8E:60 rts 00CB8F: 00CB8F: ;------------------------------------------------------------------------------ 00CB8F: ; ReadBlock - Read a block from device into memory 00CB8F: ; 00CB8F: ; Input: 00CB8F: ; pd Command Block Data $42 - $47 00CB8F: ; X = requested slot number in form $n0 where n = slot 1 to 7 00CB8F: ; 00CB8F: ; Output: 00CB8F: ; A = ProDOS read return code 00CB8F: ; Carry flag: 0 = Okay, 1 = Error 00CB8F: ; 00CB8F: ; ZeroPage Usage: 00CB8F: ; $EF 00CB8F: ; w/DEBUG enabled: $EB, $EC, $ED, $EE 00CB8F: ; Note: location $EF is saved and restored before driver exits 00CB8F: ; 00CB8F: ReadBlock: 00CB8F: .IF DEBUG 00CB8F: jsr DSString 00CB8F: .byte " Rd",0 00CB8F: jsr DisplayParms 00CB8F: .ENDIF 00CB8F: 00CB8F:A5 45 lda pdIOBufferH 00CB91:48 pha 00CB92:A5 EF lda zpt1 00CB94:48 pha 00CB95: 00CB95: .IF DEBUG 00CB95: lda CheckSumHigh 00CB95: pha 00CB95: lda CheckSumLow 00CB95: pha 00CB95: .ENDIF 00CB95: 00CB95:20 9F CB jsr ReadBlockCore 00CB98: 00CB98: .IF DEBUG 00CB98: ply 00CB98: sty CheckSumLow 00CB98: ply 00CB98: sty CheckSumHigh 00CB98: .ENDIF 00CB98: 00CB98:7A ply 00CB99:84 EF sty zpt1 00CB9B:7A ply 00CB9C:84 45 sty pdIOBufferH 00CB9E:60 rts 00CB9F: 00CB9F: ReadBlockCore: 00CB9F: .IF DEBUG 00CB9F: stz CheckSumLow 00CB9F: stz CheckSumHigh 00CB9F: .ENDIF 00CB9F: 00CB9F:20 78 CC jsr IDEWaitReady 00CBA2:20 4D CC jsr Block2LBA ;Program the device's task file registers 00CBA5: ; based on ProDOS address 00CBA5: 00CBA5:A9 00 lda #0 00CBA7:9D 80 C0 sta ATADataHigh,x 00CBAA: 00CBAA:A9 20 lda #ATACRead 00CBAC:9D 8F C0 sta ATACommand,x ;Issue the read command to the drive 00CBAF:20 78 CC jsr IDEWaitReady ;Wait for BUSY flag to clear 00CBB2: 00CBB2:BD 8F C0 lda ATAStatus,x ;Check for error response from device 00CBB5:29 09 and #$09 00CBB7:C9 01 cmp #$01 ;If DRQ=0 and ERR=1 a device error occured 00CBB9:D0 04 bne rCommandOK 00CBBB: 00CBBB: .IF DEBUG 00CBBB: ;warning this debug code trashes the Acc register 00CBBB: jsr DSString 00CBBB: .byte " Err!",0 00CBBB: lda ATAError,x 00CBBB: jsr DSByteCRLF 00CBBB: .ENDIF 00CBBB: 00CBBB: ; 00CBBB: ; The drive has returned an error code. Just return I/O error code to PRODOS 00CBBB: ; 00CBBB:A9 27 lda #PRODOS_IO_ERROR 00CBBD:38 sec 00CBBE:60 rts 00CBBF: ; 00CBBF: ; Sector is ready to read 00CBBF: ; 00CBBF: rCommandOK: 00CBBF:A0 02 ldy #2 00CBC1:84 EF sty zpt1 00CBC3:A0 00 ldy #0 00CBC5: 00CBC5: rLoop: 00CBC5:BD 8F C0 lda ATAStatus,x ;Note: not using IDEWaitReady, using inline code 00CBC8:30 FB bmi rLoop ;Wait for BUSY (bit 7) to be zero 00CBCA:29 08 and #$08 ;get DRQ status bit 00CBCC:F0 18 beq rShort ;if off, didn't get enough data 00CBCE: 00CBCE:BD 88 C0 lda ATADataLow,x 00CBD1:91 44 sta (pdIOBuffer),y 00CBD3:C8 iny 00CBD4: 00CBD4: .IF DEBUG 00CBD4: clc 00CBD4: adc CheckSumLow 00CBD4: sta CheckSumLow 00CBD4: .ENDIF 00CBD4: 00CBD4:BD 80 C0 lda ATADataHigh,x 00CBD7:91 44 sta (pdIOBuffer),y 00CBD9: 00CBD9: .IF DEBUG 00CBD9: adc CheckSumHigh 00CBD9: sta CheckSumHigh 00CBD9: .ENDIF 00CBD9: 00CBD9:C8 iny 00CBDA:D0 E9 bne rLoop 00CBDC:E6 45 inc pdIOBufferH 00CBDE:C6 EF dec zpt1 00CBE0:D0 E3 bne rLoop 00CBE2: 00CBE2: .IF DEBUG 00CBE2: jsr DSString 00CBE2: .byte " Chk$",0 00CBE2: 00CBE2: lda CheckSumHigh 00CBE2: jsr DSByte 00CBE2: lda CheckSumLow 00CBE2: jsr DSByteCRLF 00CBE2: .ENDIF 00CBE2: 00CBE2:A9 00 lda #0 00CBE4:18 clc 00CBE5:60 rts 00CBE6: ; 00CBE6: ; The Block was short, return I/O error code to PRODOS 00CBE6: ; 00CBE6: rShort: 00CBE6: .IF DEBUG 00CBE6: jsr DSString 00CBE6: .byte " Short blk", 0 00CBE6: 00CBE6: lda zpt1 00CBE6: jsr DSByte 00CBE6: tya 00CBE6: jsr DSByteCRLF 00CBE6: .ENDIF 00CBE6: 00CBE6:A9 27 lda #PRODOS_IO_ERROR 00CBE8:38 sec 00CBE9:60 rts 00CBEA: 00CBEA: ;------------------------------------------------------------------------ 00CBEA: ; WriteBlock - Write a block in memory to device 00CBEA: ; 00CBEA: ; Input: 00CBEA: ; pd Command Block Data $42 - $47 00CBEA: ; X = requested slot number in form $n0 where n = slot 1 to 7 00CBEA: ; 00CBEA: ; Output: 00CBEA: ; A = ProDOS write return code 00CBEA: ; Carry flag: 0 = Okay, 1 = Error 00CBEA: ; 00CBEA: ; ZeroPage Usage: 00CBEA: ; $EF 00CBEA: ; w/DEBUG enabled: $EB, $EC, $ED, $EE 00CBEA: ; Note: location $EF is saved and restored before driver exits 00CBEA: ; 00CBEA: WriteBlock: 00CBEA: .IF DEBUG 00CBEA: jsr DSString 00CBEA: .byte " Wt",0 00CBEA: jsr DisplayParms 00CBEA: .ENDIF 00CBEA: 00CBEA:A5 45 lda pdIOBufferH 00CBEC:48 pha 00CBED:A5 EF lda zpt1 00CBEF:48 pha 00CBF0: 00CBF0: .IF DEBUG 00CBF0: lda CheckSumHigh 00CBF0: pha 00CBF0: lda CheckSumLow 00CBF0: pha 00CBF0: .ENDIF 00CBF0: 00CBF0:20 FA CB jsr WriteBlockCore 00CBF3: 00CBF3: .IF DEBUG 00CBF3: ply 00CBF3: sty CheckSumLow 00CBF3: ply 00CBF3: sty CheckSumHigh 00CBF3: .ENDIF 00CBF3: 00CBF3:7A ply 00CBF4:84 EF sty zpt1 00CBF6:7A ply 00CBF7:84 45 sty pdIOBufferH 00CBF9:60 rts 00CBFA: 00CBFA: WriteBlockCore: 00CBFA: .IF DEBUG 00CBFA: stz CheckSumLow 00CBFA: stz CheckSumHigh 00CBFA: .ENDIF 00CBFA: 00CBFA:A9 00 lda #0 00CBFC:9D 80 C0 sta ATADataHigh,x ;Clear the high byte of the 16 bit interface 00CBFF: ; data latch 00CBFF: 00CBFF:20 78 CC jsr IDEWaitReady 00CC02:20 4D CC jsr Block2LBA ;program IDE task file 00CC05: 00CC05: ; Write sector from RAM 00CC05:A9 30 lda #ATACWrite 00CC07:9D 8F C0 sta ATACommand,x 00CC0A:20 78 CC jsr IDEWaitReady 00CC0D: 00CC0D:BD 8F C0 lda ATAStatus,x ;Check for error response from writing command 00CC10:29 09 and #$09 00CC12:C9 01 cmp #$01 ;if DRQ=0 and ERR=1 an error occured 00CC14:D0 04 bne wCommandOK 00CC16: 00CC16: .IF DEBUG 00CC16: ;warning this debug code trashes the Acc register 00CC16: jsr DSString 00CC16: .byte " Err!:",0 00CC16: lda ATAError,x 00CC16: jsr DSByteCRLF 00CC16: .ENDIF 00CC16: 00CC16: ; The drive has returned an error code. Just return I/O error code to PRODOS 00CC16: ; 00CC16:A9 27 lda #PRODOS_IO_ERROR 00CC18:38 sec 00CC19:60 rts 00CC1A: ; 00CC1A: ; Sector is ready to write 00CC1A: ; 00CC1A: wCommandOK: 00CC1A:A0 02 ldy #2 00CC1C:84 EF sty zpt1 00CC1E:A0 00 ldy #0 00CC20: 00CC20: wLoop: 00CC20:BD 8F C0 lda ATAStatus,x ;Note: not using IDEWaitReady, using inline code 00CC23:30 FB bmi wLoop ;Wait for BUSY (bit 7) to be zero 00CC25:29 08 and #$08 ;get DRQ status bit 00CC27:F0 20 beq wShort ;if off, didn't get enough data 00CC29: 00CC29:B1 44 lda (pdIOBuffer),y 00CC2B:48 pha 00CC2C: 00CC2C: .IF DEBUG 00CC2C: clc 00CC2C: adc CheckSumLow 00CC2C: sta CheckSumLow 00CC2C: .ENDIF 00CC2C: 00CC2C:C8 iny 00CC2D:B1 44 lda (pdIOBuffer),y 00CC2F:9D 80 C0 sta ATADataHigh,x 00CC32: 00CC32: .IF DEBUG 00CC32: adc CheckSumHigh 00CC32: sta CheckSumHigh 00CC32: .ENDIF 00CC32: 00CC32:68 pla 00CC33:3C 81 C0 bit SetCSMask,x ;any access sets mask bit to block IDE -CS0 on 00CC36: ; I/O read to drive 00CC36:9D 88 C0 sta ATADataLow,x ;Remember that all write cycles are 00CC39: ; preceded by a read cycle on the 6502 00CC39:3C 82 C0 bit ClearCSMask,x ;Set back to normal, allow CS0 assertions on 00CC3C: ; read cycles 00CC3C: 00CC3C:C8 iny 00CC3D:D0 E1 bne wLoop 00CC3F:E6 45 inc pdIOBufferH 00CC41:C6 EF dec zpt1 00CC43:D0 DB bne wLoop 00CC45: 00CC45: .IF DEBUG 00CC45: ; Display the Checksum 00CC45: ; warning this debug code trashes the Acc register 00CC45: jsr DSString 00CC45: .byte " Chk$",0 00CC45: lda CheckSumHigh 00CC45: jsr DSByte 00CC45: lda CheckSumLow 00CC45: jsr DSByteCRLF 00CC45: .ENDIF 00CC45: 00CC45:A9 00 lda #0 00CC47:18 clc 00CC48:60 rts 00CC49: ; 00CC49: ; The Block was short, return I/O error code to PRODOS 00CC49: ; 00CC49: wShort: 00CC49: .IF DEBUG 00CC49: ; Display "W:Short" 00CC49: jsr DSString 00CC49: .byte " W:Shrt:", 0 00CC49: 00CC49: lda zpt1 00CC49: jsr DSByte 00CC49: tya 00CC49: jsr DSByteCRLF 00CC49: .ENDIF 00CC49: 00CC49:A9 27 lda #PRODOS_IO_ERROR 00CC4B:38 sec 00CC4C:60 rts 00CC4D: 00CC4D: ;------------------------------------------------------------------------------ 00CC4D: ; Block2LBA - Translates ProDOS block# into LBA and programs devices' task file 00CC4D: ; registers. 00CC4D: ; 00CC4D: ; Input: 00CC4D: ; pd Command Block Data $42 - $47 00CC4D: ; X = requested slot number in form $n0 where n = slot 1 to 7 00CC4D: ; Y = $0n (n = slot#) for accessing scratchpad RAM; 00CC4D: ; 00CC4D: ; Ouput: 00CC4D: ; None 00CC4D: ; 00CC4D: ; ZeroPage Usage: 00CC4D: ; None 00CC4D: ; 00CC4D: ; CPU Registers changed: A, P 00CC4D: ; 00CC4D: ; This function translates the block number sent in the PRODOS request 00CC4D: ; packet, into an ATA Logical Block Address (LBA). 00CC4D: ; The least significant 16 bits becomes the ProDOS block#. 00CC4D: ; The most significant 16 becomes the ProDOS Drive # 00CC4D: ; 00CC4D: ; A ProDOS block and a ATA sector are both 512 bytes. 00CC4D: ; 00CC4D: ; Logical Block Mode, the Logical Block Address is interpreted as follows: 00CC4D: ; LBA07-LBA00: Sector Number Register D7-D0. 00CC4D: ; LBA15-LBA08: Cylinder Low Register D7-D0. 00CC4D: ; LBA23-LBA16: Cylinder High Register D7-D0. 00CC4D: ; LBA27-LBA24: Drive/Head Register bits HS3-HS0. 00CC4D: 00CC4D: 00CC4D: Block2LBA: 00CC4D: 00CC4D:A9 E0 lda #$E0 ;1, (LBA), 1, (Drive), LBA 27-24, where LBA=1, 00CC4F: ; Drive=0 00CC4F:9D 8E C0 sta ATAHead,x ;Talk to the Master device and use LBA mode. 00CC52: ; Remember that this write will seen by both 00CC52: ; the master and slave devices. 00CC52: ; 00CC52: ; Add BLOCKOFFSET to the ProDOS block number to offset the first drive block we 00CC52: ; use. This keeps the device's first BLOCKOFFSET blocks free, which usually 00CC52: ; includes a MBR at block 0. 00CC52: ; 00CC52:B9 78 07 lda DrvMiscFlags,y ; bit 7 = raw block access 00CC55:29 80 and #$80 00CC57:49 80 eor #$80 00CC59:F0 02 beq rawBlocks 00CC5B:A9 00 lda #BLOCKOFFSET 00CC5D: rawBlocks: ; A = $00 or BLOCKOFFSET 00CC5D:18 clc 00CC5E:65 46 adc pdBlockNumber 00CC60:9D 8B C0 sta ATASector,x ;store ProDOS Low block # into LBA 0-7 00CC63: 00CC63:A5 47 lda pdBlockNumberH 00CC65:69 00 adc #0 ;account for any overflow in LBA 0-7 00CC67:9D 8C C0 sta ATACylinder,x ;store ProDOS High block # into LBA 15-8 00CC6A: 00CC6A:B9 F8 04 lda DriveNumber,y 00CC6D:69 00 adc #0 ;account for overflow from LBA 8-15 00CC6F:9D 8D C0 sta ATACylinderH,x ;store LBA bits 23-16 00CC72: 00CC72:A9 01 lda #1 00CC74:9D 8A C0 sta ATASectorCnt,x 00CC77:60 rts 00CC78: 00CC78: ;------------------------------------------------------------------------------ 00CC78: ; IDEWaitReady - Waits for BUSY flag to clear, and returns DRQ bit status 00CC78: ; 00CC78: ; Input: 00CC78: ; X = requested slot number in form $n0 where n = slot 1 to 7 00CC78: ; Ouput: 00CC78: ; Carry flag = DRQ status bit 00CC78: ; 00CC78: ; ZeroPage Usage: 00CC78: ; None 00CC78: ; 00CC78: ; CPU Registers changed: A, P 00CC78: ; 00CC78: IDEWaitReady: 00CC78:BD 8F C0 lda ATAStatus,x 00CC7B:30 FB bmi IDEWaitReady ;Wait for BUSY (bit 7) to be zero 00CC7D:6A ror ;shift DRQ status bit into the Carry bit 00CC7E:6A ror 00CC7F:6A ror 00CC80:6A ror 00CC81:60 rts 00CC82: 00CC82: ;------------------------------------------------------------------------------ 00CC82: ; CheckDevice - Check to see if a device is attached to the interface. 00CC82: ; Input: 00CC82: ; X = requested slot number in form $n0 where n = slot 1 to 7 00CC82: ; Output: 00CC82: ; Carry flag: 0 = Device Present, 1 = Device Missing 00CC82: ; 00CC82: ; CPU Registers changed: A, P 00CC82: ; 00CC82: ; Checks to see if the drive status register is readable and equal to $50 00CC82: ; If so, return with the Carry clear, otherwise return with the carry set. 00CC82: ; Waits up to 10sec on a standard 1Mhz Apple II for drive to become ready 00CC82: ; 00CC82: CheckDevice: 00CC82:5A phy 00CC83:3C 82 C0 bit ClearCSMask,x ;reset MASK bit in PLD for normal CS0 signaling 00CC86:A9 E0 lda #$E0 ;$E0 = [1, LBA, 1, Drive, LBA 27-24] where 00CC88: ; LBA=1, Drive=0 00CC88:9D 8E C0 sta ATAHead,x ;Make sure ATA master drive is accessed 00CC8B: 00CC8B:A0 00 ldy #0 00CC8D: chkLoop: 00CC8D:BD 8F C0 lda ATAStatus,x 00CC90:29 D0 and #%11010000 00CC92:C9 50 cmp #$50 ;if BUSY= 0 and RDY=1 and DSC=1 00CC94:F0 0D beq DeviceFound 00CC96:A9 C5 lda #WAIT_100ms 00CC98:20 A6 CC jsr Wait ;Wait 100ms for device to be ready 00CC9B:C8 iny 00CC9C:C0 64 cpy #100 ;Wait up to 10 seconds for drive to be ready 00CC9E: ; This time may turn out to be much shorter on 00CC9E: ; accelerated Apple IIs 00CC9E:D0 ED bne chkLoop 00CCA0: 00CCA0:38 sec ;set c = 1 if drive is not attached 00CCA1:7A ply 00CCA2:60 rts 00CCA3: 00CCA3: DeviceFound: 00CCA3:18 clc ;set c = 0 if drive is attached 00CCA4:7A ply 00CCA5:60 rts 00CCA6: 00CCA6: ;------------------------------------------------------------------------------ 00CCA6: ; Wait - Copy of Apple's wait routine. Can't use ROM based routine in case 00CCA6: ; ROM is not active when we need it. 00CCA6: ; 00CCA6: ; Input: 00CCA6: ; A = desired delay time, where Delay(us) = .5(5A^2 + 27A + 26) 00CCA6: ; or more usefully: A = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 00CCA6: ; 00CCA6: ; CPU Registers changed: A, P 00CCA6: ; 00CCA6: Wait: 00CCA6:38 sec 00CCA7: 00CCA7: Wait2: 00CCA7:48 pha 00CCA8: 00CCA8: Wait3: 00CCA8:E9 01 sbc #1 00CCAA:D0 FC bne Wait3 00CCAC:68 pla 00CCAD:E9 01 sbc #1 00CCAF:D0 F6 bne Wait2 00CCB1:60 rts 00CCB2: 00CCB2: 00CCB2: 00CCB2: 00CCB2: .IF DEBUG 00CCB2: 00CCB2: ;------------------------------------------------------------------------------ 00CCB2: ; DisplayParms - Display the parameters of the ProDOS request 00CCB2: ; Input: 00CCB2: ; None 00CCB2: ; Ouput: 00CCB2: ; None 00CCB2: ; 00CCB2: ; ZeroPage Usage: 00CCB2: ; None 00CCB2: ; 00CCB2: ; CPU Registers changed: A, P 00CCB2: ; 00CCB2: DisplayParms: 00CCB2: jsr DSString 00CCB2: .byte " B:",0 00CCB2: 00CCB2: lda pdBlockNumberH 00CCB2: jsr DSByte 00CCB2: lda pdBlockNumber 00CCB2: jsr DSByte 00CCB2: 00CCB2: jsr DSString 00CCB2: .byte " U:",0 00CCB2: lda pdUnitNumber 00CCB2: jsr DSByte 00CCB2: 00CCB2: jsr DSString 00CCB2: .byte " A$",0 00CCB2: 00CCB2: lda pdIOBufferH 00CCB2: jsr DSByte 00CCB2: 00CCB2: lda pdIOBuffer 00CCB2: bra DSByte 00CCB2: 00CCB2: 00CCB2: ;------------------------------------------------------------------------------ 00CCB2: ; DSString - Sends a String to the Super Serial Card in Slot 2 00CCB2: ; Input: 00CCB2: ; string must immediately follow the JSR to this function 00CCB2: ; and be terminated with zero byte. 00CCB2: ; Ouput: 00CCB2: ; None 00CCB2: ; 00CCB2: ; ZeroPage Usage: 00CCB2: ; MsgPointerLow, MsgPointerHi 00CCB2: ; 00CCB2: ; CPU Registers changed: A, P 00CCB2: ; 00CCB2: DSString: 00CCB2: phx ;save the X reg 00CCB2: tsx ;put the stack pointer in X 00CCB2: lda MsgPointerLow 00CCB2: pha ;push zero page location on stack 00CCB2: lda MsgPointerHi 00CCB2: pha ;push zero page location on stack 00CCB2: 00CCB2: lda StackBase+2,x ;determine the location of message to display 00CCB2: clc 00CCB2: adc #$01 ;add 1 because JSR pushes the last byte of its 00CCB2: sta MsgPointerLow ; destination address on the stack 00CCB2: 00CCB2: lda StackBase+3,x 00CCB2: adc #0 00CCB2: sta MsgPointerHi 00CCB2: 00CCB2: dss1: 00CCB2: lda (MsgPointerLow) 00CCB2: beq dssend 00CCB2: jsr DSChar ;display message 00CCB2: inc MsgPointerLow 00CCB2: bne dss1 00CCB2: inc MsgPointerHi 00CCB2: bra dss1 00CCB2: 00CCB2: dssend: 00CCB2: 00CCB2: lda MsgPointerHi 00CCB2: sta StackBase+3,x 00CCB2: lda MsgPointerLow 00CCB2: sta StackBase+2,x ;fix up the return address on the stack. 00CCB2: 00CCB2: pla 00CCB2: sta MsgPointerHi ;restore zero page location 00CCB2: pla 00CCB2: sta MsgPointerLow ;restore zero page location 00CCB2: plx 00CCB2: rts ;return to location after string's null. 00CCB2: 00CCB2: ;------------------------------------------------------------------------------ 00CCB2: ; DSByteCRLF - Sends a Hex byte followed by a CR LF to the Super Serial 00CCB2: ; Card in Slot 2 00CCB2: ; 00CCB2: ; Input: 00CCB2: ; A = Hex number to display 00CCB2: ; Ouput: 00CCB2: ; None 00CCB2: ; 00CCB2: ; CPU Registers changed: A, P 00CCB2: ; 00CCB2: DSByteCRLF: 00CCB2: jsr DSByte 00CCB2: DSCRLF: 00CCB2: lda #$0D 00CCB2: jsr DSChar 00CCB2: lda #$0A 00CCB2: bra DSChar 00CCB2: 00CCB2: ;------------------------------------------------------------------------------ 00CCB2: ; DSByte - Sends a Hex byte to the Super Serial Card in Slot 2 00CCB2: ; Input: 00CCB2: ; A = Hex number to display 00CCB2: ; Ouput: 00CCB2: ; None 00CCB2: ; 00CCB2: ; CPU Registers changed: A, P 00CCB2: ; 00CCB2: DSByte: 00CCB2: pha 00CCB2: lsr a 00CCB2: lsr a 00CCB2: lsr a 00CCB2: lsr a 00CCB2: jsr DSNibble 00CCB2: pla 00CCB2: DSNibble: 00CCB2: and #$0F 00CCB2: ora #$30 00CCB2: cmp #$30+10 00CCB2: bcc digit 00CCB2: adc #6 00CCB2: digit: 00CCB2: bra DSChar 00CCB2: 00CCB2: ;------------------------------------------------------------------------------ 00CCB2: ; DSChar - Sends a char to the Super Serial Card in Slot 2 00CCB2: ; Input: 00CCB2: ; A = Character to Send 00CCB2: ; Ouput: 00CCB2: ; (data out serial port) 00CCB2: ; 00CCB2: ; ZeroPage Usage: 00CCB2: ; None 00CCB2: ; 00CCB2: ; CPU Registers changed: P 00CCB2: ; 00CCB2: DSBlank: 00CCB2: lda #$20 00CCB2: DSChar: 00CCB2: pha 00CCB2: phy 00CCB2: lda mslot 00CCB2: and #$0f 00CCB2: tay ;Y reg now has $0n for accessing scratchpad RAM 00CCB2: lda SerialInitDone,y 00CCB2: cmp #$A5 00CCB2: beq dsc0 00CCB2: 00CCB2: ; Init the serial port if sig byte is not $A5. 00CCB2: ; Set up serial port on the Apple Super Serial card. Always assume slot 2. 00CCB2: lda #$1f 00CCB2: sta $c0ab ;control register 00CCB2: lda #$0b 00CCB2: sta $c0aa ;format 00CCB2: lda $c0a9 ;clear status 00CCB2: lda #$A5 00CCB2: sta SerialInitDone,y 00CCB2: 00CCB2: dsc0: 00CCB2: lda $c0a9 00CCB2: and #%00010000 ;Transmit register empty? 00CCB2: beq dsc0 ;If not, wait 00CCB2: 00CCB2: ply 00CCB2: pla ;get byte back 00CCB2: sta $c0a8 ;send it 00CCB2: rts 00CCB2: 00CCB2: .ENDIF 00CCB2: