From 8d01a04bce5ccd3a19865175101e36207d4b6838 Mon Sep 17 00:00:00 2001 From: hexsane Date: Tue, 15 Mar 2022 20:16:31 +0000 Subject: [PATCH] Update 'amigodos.s' Signed-off-by: hexsane --- amigodos.s | 5063 ++++++++++++++++++++++++++-------------------------- 1 file changed, 2532 insertions(+), 2531 deletions(-) diff --git a/amigodos.s b/amigodos.s index 8dde35b..9d5288b 100644 --- a/amigodos.s +++ b/amigodos.s @@ -1,2531 +1,2532 @@ -;------------------------------------------------------------------------------ -; -; Originally coded in 2009-2011 by Matthew A. Hudson hexsane -; at gmail.com -; Code style & format borrowed from CFFA v1 source code -; use ca65 to assemble -; -; HP-IB AMIGO protocol ROM replacement for genuine Apple II GP-IB controller. -; -; Was assembled and working for floppy drive in HP9133xv -; It will work with the hard drive but you will need to change the -; geometry and re assemble. -; -; Note about assembly: I had to offset the code by either 128 or 256 bytes -; (meaning you lose that space in the ROM) but I can't recall why. You may -; need to fiddle around with options to get it in the correct location to -; make the card happy. I've forgotten most of the Apple II I/O layout at this -; point in time. -; -; There is probably code in here from other sources but I couldn't recall from -; who at this point. If you see something that needs credit please let me know -; and I will happily do so. -; - -.define EQU = -.define TRUE 1 -.define FALSE 0 - -;.segment "EXEHDR" ; just to keep the linker happy -;.segment "STARTUP" ; just to keep the linker happy - -; -; Firmware Version Information -; -FIRMWARE_VER EQU $12 ;Version 1.2 (Version of this code) -SPDRIVERVERSION EQU $1200 ;SmartPort version 1.2 -GSOS_DRIVER EQU $02 ;GS/OS driver will check this byte to see if it - ;is still compatible with this firmware. - ;Increment by one, when something changes that - ;would require a change in the GS/OS driver. - ;Otherwise only change the FIRMWARE_VER for all - ;other changes. - ;01 = ProDOS Driver supporting 2 drives - ;02 = SmartPort Driver supporting 4 drives - ;03 = SmartPort Driver supporting 8 drives -; -; Firmware Configuration Settings: -; -SMARTPORT EQU FALSE ; TRUE -BLOCKOFFSET EQU 0 ;0..255: LBA of first block of first partition -PARTITIONS32MB EQU 4 ;Number of 32MB Partitions supported - ;Remember, ProDOS only supports 13 total - ;volumes for all devices, floppies, SCSI drives, - ;RAM drives, etc. - - -;------------------------------------------------------------------------------ -; To enable debug output, set DEBUG = TRUE, only if you have a Apple Super -; Serial card in slot 2. This will output one line of text for each request -; made to the firmware, which can be seen on a computer or terminal attached to -; the SSC. -; -; NOTE: If you use DEBUG=TRUE and you don't have an Apple Super Serial card in -; slot 2, your computer might hang in the routine DSChar, waiting for -; the SSC status bit to say it is okay to write to the 6551 UART. -; -; Set your terminal (software) at the remote end as follows: -; BaudRate: 19200 -; Data Bits: 8 -; Parity: None -; Stop Bits: 1 -; -; Example debug output at terminal from CAT command in ProDOS 8. Card is in -; slot 6. ProDOS makes a ProDOS 8 call to the firmware to read block 2 from -; unit: 60 into buffer memory at $DC00. -; -; P8: Rd B:0002 U:60 A$DC00 Chk$6711 -; -; Rd = ProDOS Read ($01). Also could be Wr = write ($02), St = Status ($00) -; U:60 = ProDOS 8 unit number $60 Slot 6, drive 1 -; A$DC00 = ProDOS buffer address -; Chk$6711 = Simple block checksum used to visually check data integrity -; -; NOTE: When DEBUG is true, some zero-page locations are used. The data at -; these locations are saved and restored and should not impact other programs. - -;DEBUG = FALSE -DEBUG = TRUE - -;------------------------------------------------------------------------------ -; Driver constant definitions -; -INITDONESIG EQU $A5 ;Device init is done signature value -CR EQU $0D -BELL EQU $07 - -; ProDOS request Constants -PRODOS_STATUS EQU $00 -PRODOS_READ EQU $01 -PRODOS_WRITE EQU $02 -PRODOS_FORMAT EQU $03 - -;ProDOS Return Codes -PRODOS_NO_ERROR EQU $00 ;No error -PRODOS_BADCMD EQU $01 ;Bad Command (not implemented) -PRODOS_IO_ERROR EQU $27 ;I/O error -PRODOS_NO_DEVICE EQU $28 ;No Device Connected -PRODOS_WRITE_PROTECT EQU $2B ;Write Protected -PRODOS_BADBLOCK EQU $2D ;Invalid block number requested -PRODOS_OFFLINE EQU $2F ;Device off-line - -;SmartPort return codes -BAD_UNIT_NUMBER EQU $11 - -; HP-IB AMIGO Commands -AMIGO_FORMAT EQU 12 -AMIGO_STATUS EQU 8 -AMIGO_DSJ EQU 16 -AMIGO_SEEK EQU 8 -AMIGO_SEEK_SEC EQU 2 -AMIGO_BUF_WRITE EQU 9 -AMIGO_BUF_WRITE_SEC EQU 8 - -;9914 bits -BIM EQU $20 ;Byte in mask -BOM EQU $10 ;Byte out mask -EOIMK EQU $08 ;EOI mask -RESET EQU $80 ;Software chip reset -RSTCLR EQU $00 ;Stop software reset -HDFA EQU $83 ;Holdoff all data -HDACLR EQU $03 ;Holdoff clear -LON EQU $89 ;Listen only -TON EQU $8A ;Talk only -SIC EQU $8F ;Send interface clear -SICLR EQU $0F ;Clear SIC -RHDF EQU $02 ;Release RFD Holdoff -FEOI EQU $08 ;Send EOI with next byte -GTS EQU $0B ;Go to standby -TCA EQU $0C ;Take control asynchronously -TCS EQU $0D ;Take control synchronouly -PPOL_ON EQU %10001110 -PPOL_OFF EQU %00001110 - - -; ATA Commands Codes -ATACRead EQU $20 -ATACWrite EQU $30 -ATAIdentify EQU $EC - -;Constants for Wait -; Constant = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 - -WAIT_100ms EQU 197 -WAIT_40ms EQU 124 -WAIT_100us EQU 4 -;------------------------------------------------------------------------------ -; Slot I/O definitions -; -mslot = $7F8 ;Apple defined location for the last active slot - -IOBase = $C080 -; TMS9914 I/O Registers -INT0 = IOBase+0 -INTM0 = IOBase+0 -INTM1 = IOBase+1 -AUXCMD = IOBase+3 -BUSSTAT = IOBase+3 -CMD_PT = IOBase+6 -GPIB_DIN = IOBase+7 -GPIB_DOUT = IOBase+7 - - -ATADataHigh = IOBase+0 -SetCSMask = IOBase+1 ;Two special strobe locations to set and clear - ; MASK bit that is used to disable CS0 line to - ; the CompactFlash during the CPU read cycles -ClearCSMask = IOBase+2 ; that occur before every CPU write cycle. - ; The normally inoccuous read cycles were - ; causing the SanDisk CF to double increment - ; during sector writes commands. - -ATADevCtrl = IOBase+6 ;when writing -ATAAltStatus = IOBase+6 ;when reading -ATADataLow = IOBase+8 -ATAError = IOBase+9 -ATASectorCnt = IOBase+10 -ATASector = IOBase+11 -ATACylinder = IOBase+12 -ATACylinderH = IOBase+13 -ATAHead = IOBase+14 -ATACommand = IOBase+15 ; when writing -ATAStatus = IOBase+15 ; when reading - -; Scratchpad RAM base addresses. Access using the Y register containg the slot # -; -;DriveResetDone = $478 ;remember the device has been software reset -;DriveNumber = $4f8 ;normally 0 to 3 for four 32MB partitions -;SerialInitDone = $578 ;For debug: if $A5 then serial init is complete -;DrvBlkCount0 = $5f8 ;low byte of usable block count - ; (excluding first BLOCKOFFSET blocks) -;DrvBlkCount1 = $678 ;bits 8..15 of usable block count -;DrvBlkCount2 = $6f8 ;bits 16..23 of usable block count -;DrvMiscFlags = $778 ;bit 7 = raw LBA block access -;Available2 = $7f8 ;not currently used - -; Scratchpad RAM base address. Access using the Y register containg the slot # -; Remapped for HPIB -DriveResetDone = $478 ;remember the device has been software reset -DriveNumber = $4f8 ;normally 0 to 3 for four 32MB partitions -SerialInitDone = $578 ;For debug: if $A5 then serial init is complete -DrvBlkCount0 = $5f8 ;low byte of usable block count - ; (excluding first BLOCKOFFSET blocks (FIXME?)) -DrvBlkCount1 = $678 ;bits 8..15 of usable block count -DrvBlkCount2 = $6f8 ;bits 16..23 of usable block count -DrvBlkCount3 = $7f8 -DrvMiscFlags = $778 ;bit 7 = raw LBA block access - -Spare = $778 -;Available2 = $7f8 ;not currently used - - - -;------------------------------------------------------------------------------ -; Zero-page RAM memory usage - -.IF DEBUG ;data at these locations saved and restored -MsgPointerLow = $EB -MsgPointerHi = $EC -CheckSumLow = $ED -CheckSumHigh = $EE -.ENDIF - -zpt1 = $EF ;data at this location is saved/restored - -StackBase = $100 - -; ProDOS block interface locations -pdCommandCode = $42 -pdUnitNumber = $43 -pdIOBuffer = $44 -pdIOBufferH = $45 -pdBlockNumber = $46 -pdBlockNumberH = $47 - -; Arbitrary locations for Smartport data, -; these locations are saved/restored before exit. -spCommandCode = pdCommandCode - -spParamList = $48 ;2 bytes -spCmdList = $4A ;2 bytes -spCSCode = $4C -spSlot = $4D -SLOT = $4D -spSlotX16 = $4E -SLOTx16 = $4E -spLastZP = spSlotX16 - -spZeroPgArea = pdCommandCode ; $42 -spZeroPgSize = spLastZP-spZeroPgArea+1 - -DBC0 = $48 ;2 bytes -DBC1 = $49 -DBC2 = $4A ;2 bytes -DBC3 = $4B -Carry = $4C -modSpare = $4D - - -;------------------------------------------------------------------------------ -; Apple II ROM entry points -; -; We can use these at boot time, but not while handling a call through -; our ProDOS or SmartPort entry points, as the ROM may not be available. -; -SetVID = $FE89 -SetKBD = $FE93 -COUT = $FDED -INIT = $FB2F -HOME = $FC58 -ROMWAIT = $FCA8 -AppleSoft = $E000 -MONRTS = $FF58 - -;------------------------------------------------------------------------------ -; Start of Peripheral Card ROM Space $Cn00 to $CnFF -; A macro is used here so that this code can be easily duplicated for each slot -; instead of by hand using the EPROM programmer. This is possible done because -; the hardware does not overlay the C1xx address space at C2xx, C3xx, etc. -; automatically. Instead the base address for the EPROM is $C000 but is enabled -; only when a valid address in the range of $C100 to $CEFF is on the bus. This -; allows for the development of slot specific behaviors in firmware, if desired. -; Currently that is not being done, instead the same slot code is repeated for -; every slot ROM space. Addresses $C000 to $C0FF are not decoded by the -; hardware as it is Apple's internal I/O. Any access of $CF00 to $CFFF is -; decoded by the card to reset the Expansion ROM flip-flop, but remember to -; use always address $CFFF for that task. - -;.macro CnXX SLOTADDR, SLOTx16, SLOT -.ORG $C600 -; .local P8DriverEntry -; .local P8Driver -.IF SMARTPORT - .local SmartPortEntry - .local SPDriver -.ENDIF -; .local Boot -; .local Error -; .local NotScanning -; .local wasteTime -; .local ErrorMsgDisplay -; .local msgLoop -; .local msgDone -; .local ErrorMessage - - lda #$20 ;$20 is a signature for a drive to ProDOS - ldx #$00 ;$00 " - lda #$03 ;$03 " - -.IF SMARTPORT - lda #$00 -.ELSE - lda #$3c ;$3c " -.ENDIF - ;clc - ;bcc Boot - bra Boot - -;------------------- Non-boot P8 driver entry point --------------------- -; The EPROM holding this code is decoded and mapped into $C100 to $CEFF, -; it is not nessecary to dynamically determine which slot we are in, as is -; common in card firmware. This code is in a MACRO and is located absolutely -; per slot. Any code in this MACRO that is not relocatable will have to be -; based on the MACROs parameters SLOTADDR, SLOTx16, SLOT. - -P8DriverEntry: - nop - bra P8DMid - ; FIXME These jumps MUST be closer <128 bytes currently 184 -; jmp P8Driver ;located at $Cn0A for best compatibility - jmp P8Driver ;FIXME -.IF SMARTPORT -SmartPortEntry: ;By definition, SmartPort entry is 3 bytes - ; after ProDOS entry - jmp SPDriver -.ENDIF - -Boot: - jsr MONRTS ;Get this location on stack - tsx ;Find page byte on stack - lda StackBase,x - sta mslot - and #$0f - tay - asl a - asl a - asl a - asl a - tax - lda SLOT - pha - lda SLOTx16 - pha - sty SLOT - stx SLOTx16 - - ;ldy #SLOT ;Y reg now has $0n for accessing scratchpad RAM - ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O -; lda #>SLOTADDR ;loads A with slot# we are in:$Cn -; sta mslot ;Apple defined location reserved for last slot - ; active. MSLOT needs the form of $Cn - bit $cfff ;turn off expansion ROMs - -;; -;; Initialize the 9914 to known state -;; -; jsr Init9914 -;; -;; Call DSJ (Device Specified Jump) for status code and reset DSJ to 0. -;; -; jsr DSJ -;; -;; Get drive status -;; -; jsr STAT -; - -; jsr DSString -; .byte " St",0 -; jsr DisplayParms -; -; Need to wait here (before CheckDevice) in case the CFFA RESET jumper -; is enabled, or a Delkin Devices CF card never becomes ready. -; -; ldy #5 -;wasteTime: -; lda #WAIT_100ms -; jsr ROMWAIT -; dey -; bne wasteTime -; ldy SLOT - -; jsr CheckDevice -; bcs Error - - lda #PRODOS_READ ;Request: READ block - sta pdCommandCode - stz pdIOBuffer ;Into Location $800 - stz pdBlockNumber ;ProDOS block $0000 (the bootloader block) - stz pdBlockNumberH - lda #$08 - sta pdIOBufferH - stx pdUnitNumber ;From unit number: $n0 (where n=slot#), - ; so drive bit is always 0 - - - ;jsr P8Driver ;Read bootloader from device's block 0 into - ; location $800 - - clv - jsr $FF58 - bvc P8Drts - - bcs Error ;Check for error during bootblock read - - lda $800 ;Check the first byte of boot loader code. - cmp #$01 ;If bootload code is there, this byte = $01 - bne Error - lda $801 ;If second byte is a 0, it's invalid - ; (we'd JMP to a BRK) - beq Error - - ldx pdUnitNumber ;X should contain the unit number when jumping - ; to the bootloader - jmp $801 ;No errors, jump to bootloader just read. - -P8DMid: - bra P8Driver - -; If any error occured, like drive not present, check to see if we are in a -; boot scan, if so re-enter scan routine, else drop to Applesoft, aborting boot. -Error: - lda $00 - bne NotScanning - lda $01 - cmp mslot - bne NotScanning - jmp $FABA ;Re-enter Monitor's Autoscan Routine - -;The boot code must have been called manually because we are not in a slot scan. -NotScanning: - jsr SetVID - jsr SetKBD -; -;Display error message -; - jsr INIT ;text mode, full screen, page 1 - jsr HOME - ldy #0 -msgLoop: - lda ErrorMessage,y - beq msgDone - ora #$80 - jsr COUT - iny - bra msgLoop -msgDone: - jmp AppleSoft - -ErrorMessage: - .byte CR,CR,CR - .byte "Dev missing, not formatted,",CR - .byte "or incompatible.",CR -; "vX.Y" built automatically: - .byte "Ver:",$30+(FIRMWARE_VER/16),".",$30+(FIRMWARE_VER & $F) -; Beep, then end-of-message: - .byte BELL,$00 - - -;------------------- Non-boot entry point for driver code ----------------- -; -; Handle a ProDOS call -; -; Setup MSLOT, X and Y registers. -; This must be done every time we enter this driver. -; -P8Drts: - tsx - dex - dex - txs -P8Driver: - jsr MONRTS ;Get this location on stack - tsx ;Find page byte on stack - lda StackBase,x - sta mslot - and #$0f - tay - asl a - asl a - asl a - asl a - tax - ;sty SLOT - ;stx SLOTx16 - - ;ldy SLOT ;Y reg now has $0n for accessing scratchpad RAM - ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O - ;lda #>SLOTADDR ;loads A with slot# we are in:$Cn(where n=slot#) - ;sta mslot ;Apple defined location reserved for last slot - ; active. MSLOT needs the form of $Cn - bit $cfff ;turn off other ROM that might be on - - jsr P8AuxROM - bit $ff58 - rts - -.IF SMARTPORT -; NO SMARTPORT DRIVER BUILT IN -.ENDIF - - .RES $C6F5-* ;skip to $CnF5, where n is the slot# - - ;.org $C6F5 - - .byte GSOS_DRIVER ;GS/OS driver compatibility byte. GS/OS driver - ; checks this byte to see if it is compatible - ; with this version of firmware. This way, - ; changes to firware versions, that do not - ; affect the GS/OS driver will not prevent the - ; GS/OS driver from loading and running. This - ; byte should be incremented each time a change - ; is made that would prevent the GS/OS driver - ; from working correctly. I.e. Partition layout - ; or something similar. - - .byte "CFFA", FIRMWARE_VER ;$CnF6..CnFA: Card Hardware ID, - ; non-standard scheme - - .byte $0 ;$CnFB: SmartPort status byte - ; Not Extended; not SCSI; not RAM card - ; Even if not supporting SmartPort, we need a - ; zero at $CnFB so Apple's RAMCard driver - ; doesn't mistake us for a "Slinky" memory - ; card. - -; Data table for ProDOS drive scan -; $CnFC/FD = disk capacity, if zero use status command to determine -; $CnFE = status bits (BAP p7-14) -; 7 = medium is removable -; 6 = device is interruptable -; 5-4 = number of volumes (0..3 means 1..4) -; 3 = device supports Format call -; 2 = device can be written to -; 1 = device can be read from (must be 1) -; 0 = device status can be read (must be 1) -; -; $CnFF = LSB of block driver - .word $0000 ;$CnFC-D: A zero here will cause prodos to - ; rely on the status command to determine - ; volume size - - .byte $9F ; $CnFE: support 2 ProDOS drives - .byte drive # then StatusSize = $FFFF -; If DrvBlkCount2 = drive # then StatusSize = DrvBlkCount1,DrvBlkCount0 -; If DrvBlkCount2 < drive # then StatusSize = 0 -; -; This scheme has a special case which must be handled because ProDOS -; partitions are not quite 32 meg in size but are only FFFF blocks in size. -; If a device is exactly: 32meg or 10000h blocks in size, it would appear -; as one drive of size FFFF and another drive of size 0000. To handle this -; case, we check for an exact size of 0000 and fall into the NoDrive code. -; -; FIXME: Hmmm. Floppy + HD combo. How to handle? - ;lda DriveNumber,y - ;cmp DrvBlkCount2,y - ;beq ExactSize - bra ExactSize - bcc FullSize - -NoDrive: - ldx #0 - ldy #0 - lda #PRODOS_OFFLINE - sec - bra sExit - -ExactSize: ;If equal, the DrvBlkCount1,DrvBlkCount0 is the - ; drive's exact size - lda DrvBlkCount0,y - ora DrvBlkCount1,y - beq NoDrive ;can't have a 0-block device - - lda DrvBlkCount0,y - tax - lda DrvBlkCount1,y - tay - lda #0 - clc ;no errors - bra sExit - -FullSize: - ldx #$FF ;X gets low byte of size - ldy #$FF ;Y gets high byte of size - lda #0 - clc ;no errors - -sExit: -.IF DEBUG - php ;save the carry's state - pha - jsr DSString - .byte "Rt:",0 - tya - jsr DSByte - txa - jsr DSByteCRLF - pla - plp ;recover the carry -.ENDIF - - rts - -;------------------------------------------------------------------------------ -; ReadBlock - Read a block from device into memory -; -; Input: -; pd Command Block Data $42 - $47 -; X = requested slot number in form $n0 where n = slot 1 to 7 -; -; Output: -; A = ProDOS read return code -; Carry flag: 0 = Okay, 1 = Error -; -; ZeroPage Usage: -; $EF -; w/DEBUG enabled: $EB, $EC, $ED, $EE -; Note: location $EF is saved and restored before driver exits -; -ReadBlock: -.IF DEBUG - jsr DSString - .byte " Rd",0 - ;jsr DisplayParms -.ENDIF - - lda pdIOBufferH - pha - lda zpt1 ; $EF save - pha - -.IF 0 ;DEBUG - lda CheckSumHigh - pha - lda CheckSumLow - pha -.ENDIF - - jsr ReadBlockCore - -.IF 0 ;DEBUG - ply - sty CheckSumLow - ply - sty CheckSumHigh -.ENDIF - - ply - sty zpt1 - ply - sty pdIOBufferH - rts - -ReadBlockCore: -.IF 0 ;DEBUG - stz CheckSumLow - stz CheckSumHigh -.ENDIF - -.IF DEBUG - jsr DSString - .byte " Sk",0 - ;jsr DisplayParms -.ENDIF - - jsr Block2Sector - - jsr HPIBSeek - - ; based on ProDOS address - -.IF DEBUG - jsr DSString - .byte " ORd",0 - ;jsr DisplayParms -.ENDIF - - ;lda #0 - ;sta ATADataHigh,x - - ;lda #ATACRead - ;sta ATACommand,x ;Issue the read command to the drive - ;jsr IDEWaitReady ;Wait for BUSY flag to clear - - jsr GPIBReadOpen - - bcs rShort ;any errors? - -.IF DEBUG - jsr DSString - .byte " GD",0 - ;jsr DisplayParms -.ENDIF - - bra rCommandOK - -.IF DEBUG -;warning this debug code trashes the Acc register - jsr DSString - .byte " Er",0 - lda ATAError,x - jsr DSByteCRLF -.ENDIF - -; -; The drive has returned an error code. Just return I/O error code to PRODOS -; - lda #PRODOS_IO_ERROR - sec - rts -; -; Sector is ready to read -; -rCommandOK: - phy - ldy #2 ;2 itterations - sty zpt1 - ldy #0 ; of 256 cycles Need seek? - -rLoop: -; lda ATAStatus,x ;Note: not using IDEWaitReady, using inline code -; bmi rLoop ;Wait for BUSY (bit 7) to be zero -; and #$08 ;get DRQ status bit -; beq rShort ;if off, didn't get enough data - - jsr GetGPIB - bcc @continue - - ply - phy - - ; Close this sector and open the next - jsr GPIBReadClose - inc pdIOBufferH - dec zpt1 - beq @done - - ; Not done so start reading the next sector - jsr GPIBReadOpen - jsr GetGPIB - - ldy #0 - -@continue: - sta (pdIOBuffer),y - iny - -.IF 0; DEBUG - jsr DSByte -.ENDIF - -.IF 0; DEBUG - clc - adc CheckSumLow - sta CheckSumLow -.ENDIF - -; iny -; bne rLoop - bra rLoop -@done: -; inc pdIOBufferH -; dec zpt1 -; bne rLoop - -.IF 0; DEBUG - jsr DSString - .byte " Chk$",0 - - lda CheckSumHigh - jsr DSByte - lda CheckSumLow -.ENDIF -.IF DEBUG - jsr DSCRLF -.ENDIF - - ply - lda #0 - clc - rts -; -; The Block was short, return I/O error code to PRODOS -; -rShort: -.IF DEBUG - jsr DSString - .byte "RSh", 0 - - lda zpt1 - jsr DSByte - tya - jsr DSByteCRLF -.ENDIF - - lda #PRODOS_IO_ERROR - sec - rts - -;------------------------------------------------------------------------ -; WriteBlock - Write a block in memory to device -; -; Input: -; pd Command Block Data $42 - $47 -; X = requested slot number in form $n0 where n = slot 1 to 7 -; -; Output: -; A = ProDOS write return code -; Carry flag: 0 = Okay, 1 = Error -; -; ZeroPage Usage: -; $EF -; w/DEBUG enabled: $EB, $EC, $ED, $EE -; Note: location $EF is saved and restored before driver exits -; -WriteBlock: -.IF DEBUG - jsr DSString - .byte " W",0 - ;jsr DisplayParms -.ENDIF - - lda pdIOBufferH - pha - lda zpt1 - pha - -.IF 0 ; DEBUG - lda CheckSumHigh - pha - lda CheckSumLow - pha -.ENDIF - - jsr WriteBlockCore - -.IF 0 ; DEBUG - ply - sty CheckSumLow - ply - sty CheckSumHigh -.ENDIF - - ply - sty zpt1 - ply - sty pdIOBufferH - rts - -WriteBlockCore: -.IF 0 ; DEBUG - stz CheckSumLow - stz CheckSumHigh -.ENDIF - - jsr Block2Sector - - jsr HPIBSeek - - ; Check Error on Seek? -; lda ATAStatus,x ;Check for error response from writing command -; and #$09 -; cmp #$01 ;if DRQ=0 and ERR=1 an error occured -; bne wCommandOK - bra wCommandOK - -.IF DEBUG -;warning this debug code trashes the Acc register - jsr DSString - .byte " Er",0 - lda ATAError,x - jsr DSByteCRLF -.ENDIF - -; The drive has returned an error code. Just return I/O error code to PRODOS -; - lda #PRODOS_IO_ERROR - sec - rts -; -; Sector is ready to write -; -wCommandOK: - phy - ldy #2 - sty zpt1 - -wNextSector: - ply - phy - jsr GPIBWriteOpen - clc - ldy #0 -wLoop: - -.IF 0 ;DEBUG - clc - adc CheckSumLow - sta CheckSumLow -.ENDIF - - clc - lda (pdIOBuffer),y - iny - bne @continue - sec ; last byte -@continue: - jsr SendGPIB - -.IF 0 ;DEBUG - adc CheckSumHigh - sta CheckSumHigh -.ENDIF - - cpy #0 - bne wLoop - ply - phy - jsr GPIBWriteClose - inc pdIOBufferH - dec zpt1 - bne wNextSector - - ply - -.IF 0 ;DEBUG -; Display the Checksum -; warning this debug code trashes the Acc register - jsr DSString - .byte " Chk$",0 - lda CheckSumHigh - jsr DSByte - lda CheckSumLow - jsr DSByteCRLF -.ENDIF - - lda #0 - clc - rts -; -; The Block was short, return I/O error code to PRODOS -; -wShort: -.IF DEBUG -; Display "W:Short" - jsr DSString - .byte " WSh", 0 - - lda zpt1 - jsr DSByte - tya - jsr DSByteCRLF -.ENDIF - - jsr GPIBWriteClose - - lda #PRODOS_IO_ERROR - sec - rts - -;------------------------------------------------------------------------------ -; -; Block2Sector - Translates ProDOS blocks into head, cylinder, sector -; -; Input: -; pd Command Block Data $42 - $47 -; X = requested slot number in form $n0 where n = slot 1 to 7 -; Y = $0n (n = slot#) for accessing scratchpad RAM; -; -; Ouput: -; None -; -; ZeroPage Usage: -; None -; -; CPU Registers changed: A, P -; -Block2Sector: - ; FIXME Will need identify table for these values. - ;lda #2 - ;;sta MXHEAD - ;lda #16 ; Need to / 2 (8 blocks = 16 256byte sectors) - ;;sta MXSECTOR - ;lda #35 - ;;sta MXCYLL - ;lda #0 - ;;sta MXCYLH - jsr DSCRLF - lda pdBlockNumberH - pha -.IF 0 ;DEBUG - jsr DSByte -.ENDIF - lda pdBlockNumber - pha -.IF 0 ;DEBUG - jsr DSByteCRLF - pla - pha -.ENDIF - - ; 2 heads - clc - - ;ror pdBlockNumberH ; Only for / 2 need something else for more than 2 heads - ;ror pdBlockNumber - ;lda #0 - ;rol a - ;pha ;push the head - - lda #0 - sta DrvBlkCount1,y -.IF 0;DEBUG - jsr DSByte -.ENDIF - clc - lda #16 ; Divsor = sectors per cylinder - ror a ; 512 byte per block (256 bytes per sector) - sta DrvBlkCount0,y -.IF 0;DEBUG - jsr DSByteCRLF -.ENDIF - - jsr Mod - - ; Block - clc - lda DrvBlkCount2,y - rol a ; Convert Block to Starting Sector (currently always < 256) - sta DrvBlkCount2,y - - jsr DSByte - - ; DrvBlkCount2 = SECTOR - - ;pla ; Pop head - lda #0 - sta DrvBlkCount3,y ; Store Head -.IF 0;DEBUG - jsr DSByteCRLF - ; Cylinder (16 bit) - lda DrvBlkCount1,y - jsr DSByte - lda DrvBlkCount0,y - jsr DSByteCRLF -.ENDIF - - pla - sta pdBlockNumber - pla - sta pdBlockNumberH - - rts - -; FIXME? -; Add BLOCKOFFSET to the ProDOS block number to offset the first drive block we -; use. This keeps the device's first BLOCKOFFSET blocks free, which usually -; includes a MBR at block 0. -; rawread (FIXME?) -; - -;------------------------------------------------------------------------------ -; -; HPIBSeek - Seek drive head to D#,CYLH,CYLL,HEAD,SECTOR -; -; Must call Block2Sector to load values (or manually load them) -; -HPIBSeek: - clc - lda #$20 ; Listen Drive should start at 1 - ora DriveNumber,y - jsr SendGPIB - - lda #$68 ;Secondary Listen - sta GPIB_DOUT,x ;inline the data out for GTS - - lda #GTS ;Go to stand by - sta AUXCMD,x - jsr WaitOut9914 ;Wait for Secondary Listen Out - - lda #$02 ; OpCode (SEEK) - jsr SendGPIB - - lda #$00 ; Disk Unit Number (0) - jsr SendGPIB - - lda DrvBlkCount1,y ;CylH - jsr SendGPIB - - lda DrvBlkCount0,y ;CylL - jsr SendGPIB - - lda DrvBlkCount3,y ;Head - jsr SendGPIB - - sec ; Last byte - lda DrvBlkCount2,y ;Sector - jsr SendGPIB - - lda #$3F ;Unlisten (Seek is unusual in that it wants the this - jsr SendGPIB ; before the Poll). - - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - - jsr HPIBPPoll - - rts - -; FIXME are we sending EOI??? -; Error checking (no read) done via DSJ = 1 (Bad Data) -;------------------------------------------------------------------------------ -; -GPIBReadOpen: - clc - lda #$20 ; Listen Drive should start at 1 - ora DriveNumber,y - jsr SendGPIB - - lda #$6A ;Secondary Listen - sta GPIB_DOUT,x - - lda #GTS ;Go to stand by - sta AUXCMD,x - jsr WaitOut9914 ;Wait for Secondary Listen Out - - lda #$05 ; OpCode (Buffered Read) - jsr SendGPIB - - sec ; Last byte - lda #$00 ; Disk Unit Number (0) - jsr SendGPIB - - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - - jsr HPIBPPoll - - lda #$3F ;Unlisten - jsr SendGPIB - - jsr DSJ ;Any errors? - bcs ROError - - ; Actually read the data - lda #$40 ; TALK Address (MTA) - ora DriveNumber,y - jsr SendGPIB - lda #$60 ; Secondary TALK (MSA) - sta GPIB_DOUT,x - -.IF DEBUG - jsr DSByte -.ENDIF - - lda #LON - sta AUXCMD,x - lda #GTS - sta AUXCMD,x - jsr WaitOut9914 ;SendGPIB - -ROError: - - rts - -;------------------------------------------------------------------------------ -; -GPIBReadClose: - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - - lda #TON - sta AUXCMD,x - - lda #$5f ;UNTALK - jsr SendGPIB - -.IF DEBUG - jsr DSCRLF -.ENDIF - rts - -;------------------------------------------------------------------------------ -; -GPIBWriteOpen: - ;lda #$14 ; Universal Clear - ;jsr SendGPIB - clc - lda #$20 ; Listen Drive should start at 1 - ora DriveNumber,y - jsr SendGPIB - - lda #$69 ;Secondary Listen - sta GPIB_DOUT,x - - lda #GTS ;Go to stand by - sta AUXCMD,x - jsr WaitOut9914 ;Wait for Secondary Listen Out - - lda #$08 ; OpCode (Buffered Write) - jsr SendGPIB - - sec ; Last byte - lda #$00 ; Disk Unit Number (0) - jsr SendGPIB - - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - - jsr HPIBPPoll - - lda #$3F ;Unlisten - jsr SendGPIB - - - lda #$20 ; Listen Drive should start at 1 - ora DriveNumber,y - jsr SendGPIB - - lda #$60 ;Secondary Listen - ;jsr SendGPIB - sta GPIB_DOUT,x - - lda #GTS ;Go to stand by - sta AUXCMD,x - jsr WaitOut9914 ;Wait for Secondary Listen Out - - jsr DSCRLF - - rts - - ;Send Data to write - -;GPIBWriteLoop: - -; jsr SendGPIB -; rts - - -;------------------------------------------------------------------------------ -; -GPIBWriteClose: - - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - - jsr DSCRLF - - jsr HPIBPPoll - - lda #$3F ;Unlisten - jsr SendGPIB - rts - -;------------------------------------------------------------------------------ -; -; HP Identify Command. (HP-IB weirdness and completely non-standard). -; -HPIBId: - lda #$5f ;UNTALK - clc - jsr SendGPIB - lda DriveNumber,y - ora #$60 - sta GPIB_DOUT,x - - lda #LON - sta AUXCMD,x - lda #GTS - sta AUXCMD,x - jsr WaitOut9914 ;SendGPIB - -@More: - jsr GetGPIB -.IF DEBUG - php - jsr DSByte - plp -.ENDIF - bcc @More - - lda #TON - sta AUXCMD,x - - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - -.IF DEBUG - jsr DSCRLF -.ENDIF - - rts - - -;------------------------------------------------------------------------------ -; -; AMIGO Format command. CAUTION: LOW LEVEL FORMATS MEDIA. -; -AMIGOFormat: - clc - lda #$20 ; Listen Drive should start at 1 - ora DriveNumber,y - jsr SendGPIB - - lda #$6C ;Secondary Listen - sta GPIB_DOUT,x - - lda #GTS ;Go to stand by - sta AUXCMD,x - jsr WaitOut9914 ;Wait for Secondary Listen Out - - lda #$18 ; OpCode (FORMAT) - jsr SendGPIB - - lda #$00 ; Disk Unit Number (0) - jsr SendGPIB - - lda #$80 - jsr SendGPIB - - lda #9 - jsr SendGPIB ; Interleave (not used) - sec ; Last byte - lda #$00 - jsr SendGPIB ; Data Byte (not used) - - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - - jsr HPIBPPoll - - lda #$3F ;Unlisten - jsr SendGPIB - - lda #0 ; No PRODOS error to report - clc - rts - - - -;------------------------------------------------------------------------------ -; -; HPIBPPoll: Parallel poll the drive waiting for a response. Drives that are -; busy are 'held off' by having parrallel poll disabled. In essence if it -; doesn't respond to a poll then its busy or offline. -; -; FIXME: Need to set a limit for the number of polls or the amount of time. -; HP says 60 seconds max response time. -; -HPIBPPoll: - phy - -@repoll: - lda #PPOL_ON - sta AUXCMD,x - - lda #WAIT_100ms ; Give drive time to respond to the poll (HP recommends 50us) - jsr Wait - - ply - phy - - lda #$08 - sec - sbc DriveNumber,y - tay - lda #0 - sec -@B: - rol a - dey - bne @B ;HP-IB devices respond to poll on DIO# = 8 - device ID - and CMD_PT,x - php - - pha - lda #PPOL_OFF - sta AUXCMD,x - pla - plp - - beq @repoll - - ply - - clc - rts - -;------------------------------------------------------------------------------ -; -; Find ZP locations to speed this up (use pha loop?) -; -; kudos to Garth Wilson for this function from www.6502.org -Mod: - phy - phx - tya - tax - -; save ZP, fetch cmd + pointer from (PC) - ldy #spZeroPgSize-1 -@save: - lda spZeroPgArea,y - pha - dey - bpl @save - - txa - tay - - lda DrvBlkCount1,y - sta DBC1 - lda DrvBlkCount0,y - sta DBC0 - - ; Will only ever use 16 bit values (Optimize?) - stz DBC3 - stz DBC2 - - ;pdBlockNumber(H) Already adjusted and saved at this point. - - phy - - - SEC ; Detect overflow or /0 condition. - LDA DBC2 ; Divisor must be more than high cell of dividend. To - SBC DBC0 ; find out, subtract divisor from high cell of dividend; - LDA DBC3 ; if carry flag is still set at the end, the divisor was - SBC DBC1 ; not big enough to avoid overflow. This also takes care - BCS @oflo ; of any /0 condition. Branch if overflow or /0 error. - ; We will loop 16 times; but since we shift the dividend - LDX #$11 ; over at the same time as shifting the answer in, the - ; operation must start AND finish with a shift of the - ; low cell of the dividend (which ends up holding the - ; quotient), so we start with 17 (11H) in X. -@loop: - ROL pdBlockNumber ; Move low cell of dividend left one bit, also shifting - ROL pdBlockNumberH ; answer in. The 1st rotation brings in a 0, which later - ; gets pushed off the other end in the last rotation. - DEX - BEQ @end ; Branch to the end if finished. - - ROL DBC2 ; Shift high cell of dividend left one bit, also - ROL DBC3 ; shifting next bit in from high bit of low cell. - STZ Carry ; Zero old bits of CARRY so subtraction works right. - ROL Carry ; Store old high bit of dividend in CARRY. (For STZ - ; one line up, NMOS 6502 will need LDA #0, STA CARRY.) - SEC ; See if divisor will fit into high 17 bits of dividend - LDA DBC2 ; by subtracting and then looking at carry flag. - SBC DBC0 ; First do low byte. - STA modSpare ; Save difference low byte until we know if we need it. - LDA DBC3 ; - SBC DBC1 ; Then do high byte. - TAY ; Save difference high byte until we know if we need it. - LDA Carry ; Bit 0 of CARRY serves as 17th bit. - SBC #0 ; Complete the subtraction by doing the 17th bit before - BCC @loop ; determining if the divisor fit into the high 17 bits - ; of the dividend. If so, the carry flag remains set. - LDA modSpare ; If divisor fit into dividend high 17 bits, update - STA DBC2 ; dividend high cell to what it would be after - STY DBC3 ; subtraction. - BRA @loop - -@oflo: - LDA #$FF ; If overflow occurred, put FF - STA DBC2 ; in remainder low byte - STA DBC3 ; and high byte, - STA pdBlockNumber ; and in quotient low byte - STA pdBlockNumberH ; and high byte. - -@end: - - ply - - lda DBC2 - sta DrvBlkCount2,y - lda DBC3 - sta DrvBlkCount3,y - - lda pdBlockNumberH - sta DrvBlkCount1,y - lda pdBlockNumber - sta DrvBlkCount0,y - - -; Restore zero page - ldy #0 -restore: - pla - sta spZeroPgArea,y - iny - cpy #spZeroPgSize - bcc restore - - plx - ply - - RTS - -;------------------------------------------------------------------------------ -; IDEWaitReady - Waits for BUSY flag to clear, and returns DRQ bit status -; -; Input: -; X = requested slot number in form $n0 where n = slot 1 to 7 -; Ouput: -; Carry flag = DRQ status bit -; -; ZeroPage Usage: -; None -; -; CPU Registers changed: A, P -; -IDEWaitReady: -; lda ATAStatus,x -; bmi IDEWaitReady ;Wait for BUSY (bit 7) to be zero -; ror ;shift DRQ status bit into the Carry bit -; ror -; ror -; ror -; rts - -;------------------------------------------------------------------------------ -; CheckDevice - Check to see if a device is attached to the interface. -; Input: -; X = requested slot number in form $n0 where n = slot 1 to 7 -; Output: -; Carry flag: 0 = Device Present, 1 = Device Missing -; -; CPU Registers changed: A, P -; -; Checks to see if the drive status register is readable and equal to $50 -; If so, return with the Carry clear, otherwise return with the carry set. -; Waits up to 10sec on a standard 1Mhz Apple II for drive to become ready -; -CheckDevice: -; - jsr DSJ - ; If Carry set DSJ <> 0 force status - bcc DeviceFound - - jsr STAT - bcc DeviceFound - -; sec ;set c = 1 if drive is not attached -; ply - rts - -DeviceFound: -; ply - rts - - -;------------------------------------------------------------------------------ -; Status - Gets status of -; -; Input: None -; -; CPU Registers changed: None -; - -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -;; DSJ - Device Specified Jump (Internal Subroutine) -;; -DSJ: - ;jsr DSString - ;.byte " Dsj:",0 - - clc - lda #$40 ; TALK Address (MTA) - ora DriveNumber,y - jsr SendGPIB - - lda #$60 ; Secondary TALK (MSA) - ora #$10 ; Secondary Command - sta GPIB_DOUT,x - - lda #HDFA - sta AUXCMD,x - lda #LON - sta AUXCMD,x - lda #GTS - sta AUXCMD,x - jsr WaitOut9914 ;SendGPIB - - ; Single byte returned from DSJ. -@More: - jsr GetGPIB - ; sta ??? -.IF 0 ;DEBUG - php - jsr DSByte - plp -.ENDIF - bcc @More - - and #$03 - bne @uc - - lda #TCS - sta AUXCMD,x - jsr WaitOut9914;IO - - lda #RHDF - sta AUXCMD,x - - lda #HDACLR - sta AUXCMD,x - - lda #TON - sta AUXCMD,x - - lda #$5f ;UNTALK - clc - jsr SendGPIB - -.IF DEBUG - jsr DSCRLF -.ENDIF - - clc - rts - -@uc: - lda #TCS - sta AUXCMD,x - jsr WaitOut9914;0 - - lda #RHDF - sta AUXCMD,x - - lda #HDACLR - sta AUXCMD,x - - lda #TON - sta AUXCMD,x - - clc - lda #$5f ;UNTALK - jsr SendGPIB -.IF DEBUG - jsr DSCRLF -.ENDIF - -; clc -; lda #$14 ; Universal Clear -; jsr SendGPIB - -; jsr HPIBPPoll - -; Its hanging on new unformatted disk read. Was working when status / universal clear was being sent each time. -; Reads already formatted ProDOS disk (check again). - - sec ;Set Carry to force Status - rts - -;------------------------------------------------------------------------------ -; STAT - Get Drive Status Information. -; -STAT: - lda zpt1 - pha - jsr DSString - .byte "T:",0 - - clc - lda #$20 ;My address (Apple GPIB address) - jsr SendGPIB - - lda #$3F ;Unlisten - jsr SendGPIB - - lda #$20 - ora DriveNumber,y - jsr SendGPIB - - lda #$68 ;Secondary Listen - sta GPIB_DOUT,x - - lda #GTS ;Go to stand by - sta AUXCMD,x - jsr WaitOut9914 ;Wait for Secondary Listen Out - - lda #$03 ; OpCode - jsr SendGPIB - - lda #$00 ; Disk Unit Number (0) - sec - jsr SendGPIB - - lda #TCA - sta AUXCMD,x - jsr WaitOut9914;IO - -jsr HPIBPPoll - - lda #$3F ;Unlisten - jsr SendGPIB - - ;------------------------------------ - ; Get the response from stat request - clc - lda #$40 ; TALK Address (MTA) - ora DriveNumber,y - jsr SendGPIB - - lda #$60 ; Secondary TALK (MSA) - ora #$08 ; Secondary Command - ;jsr SendGPIB - sta GPIB_DOUT,x -.IF 0;DEBUG - jsr DSByte - clc -.ENDIF - lda #HDFA - sta AUXCMD,x - lda #LON - sta AUXCMD,x - lda #GTS - sta AUXCMD,x - jsr WaitOut9914 ;SendGPIB - ;jsr SendGPIB - - ; Multiplie bytes returned. FIXME ignore for now - phy - ldy #0 -@More: - jsr GetGPIB - php - - cpy #2 - bne @chk2 - lsr a - and #$0f - sta zpt1 -; bra @chk2 -;@chk1: -; cpy #3 -; bne @chk2 -; and #$03 - - -@chk2: - -.IF 0;DEBUG - jsr DSByte -.ENDIF - - plp - iny - bcc @More - ply - clc - - lda #TCS - sta AUXCMD,x - jsr WaitOut9914;IO - - lda #RHDF - sta AUXCMD,x - - lda #HDACLR - sta AUXCMD,x - - lda #TON - sta AUXCMD,x - - lda #$5f ;UNTALK - jsr SendGPIB - -.IF 0;DEBUG - jsr DSCRLF -.ENDIF - - lda zpt1 - beq @bad ;use bmi? - cmp #$05 - bne @good - -@bad: - sec - pla - sta zpt1 - lda #PRODOS_IO_ERROR - rts - -@good: - clc - pla - sta zpt1 - lda #0 - rts - -;------------------------------------------------------------------------------ -; Init9914 - Routine to initialize the TMS9914 GPIBA -; -; Input: -; X = requested slot number in form $n0 where n = slot 1 to 7 -; -; 9914 State on exit: -; B0 set -; All interrupts masked off -; Talk Only (TON) on -; Not listener active (LA) -; No holdoffs enabled or active -; 9914 Polled for status -; -; CPU Registers changed: A, P -; -Init9914: - ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O - lda #RESET - sta AUXCMD,x - lda #RSTCLR - sta INTM0,x - sta INTM1,x - sta AUXCMD,x - lda #SIC - sta AUXCMD,x - - txa - pha - tya - pha - - ldx #4 ;5.12 usec delay (FIXME) - ldy #0 -@init1: - dey - bne @init1 - dex - bne @init1 - - pla - tay - pla - tax - - ;Set my address - lda #$00 - sta IOBase+4,x - - ;ldx #SLOTx16 - lda #SICLR - sta AUXCMD,x - lda #$11 - sta AUXCMD,x - lda #$05 - sta AUXCMD,x - lda #TON - sta AUXCMD,x - jsr WaitOut9914 - - rts - -;------------------------------------------------------------------------------ -; SendGPIB - Output data then wait for Byte Out stat change. -; -; Input: -; A = Data Out -; P = Carry Set = Last byte (Assert EOI) -; X = requested slot number in form $n0 where n = slot 1 to 7 -; -; CPU Registers changed: A, P -; -SendGPIB: - bcc @NotLast - clc - pha - jsr DSString - .byte "e",0 - lda #$08 - sta AUXCMD,x - pla -@NotLast: - sta GPIB_DOUT,x -.IF 0;DEBUG - jsr DSByte - clc -.ENDIF - ;FALL THROUGH - -;------------------------------------------------------------------------------ -; WaitOut9914 - Wait for Byte Out stat change. -; -; Input: -; X = requested slot number in form $n0 where n = slot 1 to 7 -; -; CPU Registers changed: A, P -; -; lda BUSSTAT,x -; jsr DSByte -WaitOut9914: -; phy -; ldy #10 ;10 timeouts (~1 second) -@reload: -; lda #WAIT_100ms -; jsr TimeSet -@again: -; jsr TimeCheck -; bcs @continue -; jsr TimeClear -; dey -; beq @timeout -; bra @reload -@continue: - lda INT0,x - and #BOM - beq @again -; jsr TimeClear -; clc -; ply - rts -@timeout: -; jsr TimeClear -.IF DEBUG -; jsr DSString -; .byte "TO",0 -; jsr DSCRLF -.ENDIF -; sec -; ply - rts - -;------------------------------------------------------------------------------ -; WaitIn9914 - Wait for Byte In AND/OR EOI stat change. -; -; Input: -; X = requested slot number in form $n0 where n = slot 1 to 7 -; Output: -; A = Byte received -; C = Set if last byte cleared otherwise -; -; CPU Registers changed: A, P -; -; Timeout without actually waiting??? -GetGPIB: -; phy -; ldy #10 ;10 tries (1 second) -@again: -; lda #WAIT_100ms -; jsr Wait -; dey -; beq @timeout - - lda INT0,x - and #EOIMK+BIM - beq @again - - clc - and #EOIMK - bne @LastByte ; Got EOI Get final byte - lda GPIB_DIN,x - pha - lda #RHDF - sta AUXCMD,x - pla - ; FIXME Release Holdoffs ??? - rts - -@LastByte: - lda GPIB_DIN,x - sec ;Set carry this is the last byte - ; FIXME Need TCS (Take Control Sync) ??? - ; Release HOLDOFFS? - rts - -;------------------------------------------------------------------------------ -; Wait - Copy of Apple's wait routine. Can't use ROM based routine in case -; ROM is not active when we need it. -; -; Input: -; A = desired delay time, where Delay(us) = .5(5A^2 + 27A + 26) -; or more usefully: A = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 -; -; CPU Registers changed: A, P -; -Wait: - sec - -Wait2: - pha - -Wait3: - sbc #1 - bne Wait3 - pla - sbc #1 - bne Wait2 - rts - -;------------------------------------------------------------------------------ -; Timeout Routines -; - TimeSet - A = Wait time (not 100% accurate) -; - TimeCheck - clc = timeout -; - TimeClear - Clears the stack -; -; ZP may be more suited for this task. -; -; CPU Registers changed: A, P -; -TimeSet: - pha - rts - -;------------------------------------- -TimeCheck: - pla - pha - sec - sbc #1 - beq @stilltime - pla - sbc #1 - beq @stilltime2 - ;carry clear = timeout -@stilltime2: - pha -@stilltime: - rts - -;------------------------------------- -TimeClear: - pla - rts - -.IF DEBUG - -;------------------------------------------------------------------------------ -; DisplayParms - Display the parameters of the ProDOS request -; Input: -; None -; Ouput: -; None -; -; ZeroPage Usage: -; None -; -; CPU Registers changed: A, P -; -DisplayParms: -; jsr DSString -; .byte " B:",0 - -; lda pdBlockNumberH -; jsr DSByte -; lda pdBlockNumber -; jsr DSByte - -; jsr DSString -; .byte " U:",0 -; lda pdUnitNumber -; jsr DSByte - -; jsr DSString -; .byte " A$",0 - -; lda pdIOBufferH -; jsr DSByte -; -; lda pdIOBuffer - bra DSByte - - -;------------------------------------------------------------------------------ -; DSString - Sends a String to the Super Serial Card in Slot 2 -; Input: -; string must immediately follow the JSR to this function -; and be terminated with zero byte. -; Ouput: -; None -; -; ZeroPage Usage: -; MsgPointerLow, MsgPointerHi -; -; CPU Registers changed: A, P -; -DSString: - phx ;save the X reg - tsx ;put the stack pointer in X - lda MsgPointerLow - pha ;push zero page location on stack - lda MsgPointerHi - pha ;push zero page location on stack - - lda StackBase+2,x ;determine the location of message to display - clc - adc #$01 ;add 1 because JSR pushes the last byte of its - sta MsgPointerLow ; destination address on the stack - - lda StackBase+3,x - adc #0 - sta MsgPointerHi - -dss1: - lda (MsgPointerLow) - beq dssend - jsr DSChar ;display message - inc MsgPointerLow - bne dss1 - inc MsgPointerHi - bra dss1 - -dssend: - - lda MsgPointerHi - sta StackBase+3,x - lda MsgPointerLow - sta StackBase+2,x ;fix up the return address on the stack. - - pla - sta MsgPointerHi ;restore zero page location - pla - sta MsgPointerLow ;restore zero page location - plx - clc - rts ;return to location after string's null. - -;------------------------------------------------------------------------------ -; DSByteCRLF - Sends a Hex byte followed by a CR LF to the Super Serial -; Card in Slot 2 -; -; Input: -; A = Hex number to display -; Ouput: -; None -; -; CPU Registers changed: A, P -; -DSByteCRLF: - jsr DSByte -DSCRLF: - lda #$0D - jsr DSChar - lda #$0A - bra DSChar - -;------------------------------------------------------------------------------ -; DSByte - Sends a Hex byte to the Super Serial Card in Slot 2 -; Input: -; A = Hex number to display -; Ouput: -; None -; -; CPU Registers changed: A, P -; -DSByte: - pha - lsr a - lsr a - lsr a - lsr a - jsr DSNibble - pla -DSNibble: - and #$0F - ora #$30 - cmp #$30+10 - bcc digit - adc #6 -digit: - bra DSChar - -;------------------------------------------------------------------------------ -; DSChar - Sends a char to the Super Serial Card in Slot 2 -; Input: -; A = Character to Send -; Ouput: -; (data out serial port) -; -; ZeroPage Usage: -; None -; -; CPU Registers changed: P -; -DSBlank: - lda #$20 -DSChar: - pha - phy - lda mslot - and #$0f - tay ;Y reg now has $0n for accessing scratchpad RAM - lda SerialInitDone,y - cmp #$A5 - beq dsc0 - -; Init the serial port if sig byte is not $A5. -; Set up serial port on the Apple Super Serial card. Always assume slot 2. - lda #$1f - sta $c0ab ;control register - lda #$0b - sta $c0aa ;format - lda $c0a9 ;clear status - lda #$A5 - sta SerialInitDone,y - -dsc0: - lda $c0a9 - and #%00010000 ;Transmit register empty? - beq dsc0 ;If not, wait - - ply - pla ;get byte back - sta $c0a8 ;send it - clc - rts - -.ENDIF - - -.WARNING .SPRINTF("%04X",*) -.IF * > $cfff -.WARNING "* TOO BIG!!!" -.ENDIF -.word * +;------------------------------------------------------------------------------ +; +; Originally coded in 2009-2011 by Matthew A. Hudson hexsane +; at gmail.com +; Code style & format borrowed from CFFA v1 source code +; use ca65 to assemble +; +; HP-IB AMIGO protocol ROM replacement for genuine Apple II GP-IB (IEEE-488) controller. +; +; Was assembled and working for floppy drive in HP9133xv +; It will work with the hard drive but you will need to change the +; geometry and re assemble. +; +; Note about assembly: I had to offset the code by 256 bytes +; (meaning you lose that space in the ROM) but I can't recall why other than +; the card didn't init unless I did. Others more familiar with the Apple II design +; will probably understand why. +; +; There is probably code in here from other sources but I couldn't recall from +; who at this point. If you see something that needs credit please let me know +; and I will happily do so. Some code is unused (I took code I worded on separately +; and placed it into the cffa code so I wouldn't have to reinvent the wheel. +; Anything referencing ATA commands can probably be removed. +; + +.define EQU = +.define TRUE 1 +.define FALSE 0 + +;.segment "EXEHDR" ; just to keep the linker happy +;.segment "STARTUP" ; just to keep the linker happy + +; +; Firmware Version Information +; +FIRMWARE_VER EQU $12 ;Version 1.2 (Version of this code) +SPDRIVERVERSION EQU $1200 ;SmartPort version 1.2 +GSOS_DRIVER EQU $02 ;GS/OS driver will check this byte to see if it + ;is still compatible with this firmware. + ;Increment by one, when something changes that + ;would require a change in the GS/OS driver. + ;Otherwise only change the FIRMWARE_VER for all + ;other changes. + ;01 = ProDOS Driver supporting 2 drives + ;02 = SmartPort Driver supporting 4 drives + ;03 = SmartPort Driver supporting 8 drives +; +; Firmware Configuration Settings: +; +SMARTPORT EQU FALSE ; TRUE +BLOCKOFFSET EQU 0 ;0..255: LBA of first block of first partition +PARTITIONS32MB EQU 4 ;Number of 32MB Partitions supported + ;Remember, ProDOS only supports 13 total + ;volumes for all devices, floppies, SCSI drives, + ;RAM drives, etc. + + +;------------------------------------------------------------------------------ +; To enable debug output, set DEBUG = TRUE, only if you have a Apple Super +; Serial card in slot 2. This will output one line of text for each request +; made to the firmware, which can be seen on a computer or terminal attached to +; the SSC. +; +; NOTE: If you use DEBUG=TRUE and you don't have an Apple Super Serial card in +; slot 2, your computer might hang in the routine DSChar, waiting for +; the SSC status bit to say it is okay to write to the 6551 UART. +; +; Set your terminal (software) at the remote end as follows: +; BaudRate: 19200 +; Data Bits: 8 +; Parity: None +; Stop Bits: 1 +; +; Example debug output at terminal from CAT command in ProDOS 8. Card is in +; slot 6. ProDOS makes a ProDOS 8 call to the firmware to read block 2 from +; unit: 60 into buffer memory at $DC00. +; +; P8: Rd B:0002 U:60 A$DC00 Chk$6711 +; +; Rd = ProDOS Read ($01). Also could be Wr = write ($02), St = Status ($00) +; U:60 = ProDOS 8 unit number $60 Slot 6, drive 1 +; A$DC00 = ProDOS buffer address +; Chk$6711 = Simple block checksum used to visually check data integrity +; +; NOTE: When DEBUG is true, some zero-page locations are used. The data at +; these locations are saved and restored and should not impact other programs. + +;DEBUG = FALSE +DEBUG = TRUE + +;------------------------------------------------------------------------------ +; Driver constant definitions +; +INITDONESIG EQU $A5 ;Device init is done signature value +CR EQU $0D +BELL EQU $07 + +; ProDOS request Constants +PRODOS_STATUS EQU $00 +PRODOS_READ EQU $01 +PRODOS_WRITE EQU $02 +PRODOS_FORMAT EQU $03 + +;ProDOS Return Codes +PRODOS_NO_ERROR EQU $00 ;No error +PRODOS_BADCMD EQU $01 ;Bad Command (not implemented) +PRODOS_IO_ERROR EQU $27 ;I/O error +PRODOS_NO_DEVICE EQU $28 ;No Device Connected +PRODOS_WRITE_PROTECT EQU $2B ;Write Protected +PRODOS_BADBLOCK EQU $2D ;Invalid block number requested +PRODOS_OFFLINE EQU $2F ;Device off-line + +;SmartPort return codes +BAD_UNIT_NUMBER EQU $11 + +; HP-IB AMIGO Commands +AMIGO_FORMAT EQU 12 +AMIGO_STATUS EQU 8 +AMIGO_DSJ EQU 16 +AMIGO_SEEK EQU 8 +AMIGO_SEEK_SEC EQU 2 +AMIGO_BUF_WRITE EQU 9 +AMIGO_BUF_WRITE_SEC EQU 8 + +;9914 bits +BIM EQU $20 ;Byte in mask +BOM EQU $10 ;Byte out mask +EOIMK EQU $08 ;EOI mask +RESET EQU $80 ;Software chip reset +RSTCLR EQU $00 ;Stop software reset +HDFA EQU $83 ;Holdoff all data +HDACLR EQU $03 ;Holdoff clear +LON EQU $89 ;Listen only +TON EQU $8A ;Talk only +SIC EQU $8F ;Send interface clear +SICLR EQU $0F ;Clear SIC +RHDF EQU $02 ;Release RFD Holdoff +FEOI EQU $08 ;Send EOI with next byte +GTS EQU $0B ;Go to standby +TCA EQU $0C ;Take control asynchronously +TCS EQU $0D ;Take control synchronouly +PPOL_ON EQU %10001110 +PPOL_OFF EQU %00001110 + + +; ATA Commands Codes +ATACRead EQU $20 +ATACWrite EQU $30 +ATAIdentify EQU $EC + +;Constants for Wait +; Constant = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 + +WAIT_100ms EQU 197 +WAIT_40ms EQU 124 +WAIT_100us EQU 4 +;------------------------------------------------------------------------------ +; Slot I/O definitions +; +mslot = $7F8 ;Apple defined location for the last active slot + +IOBase = $C080 +; TMS9914 I/O Registers +INT0 = IOBase+0 +INTM0 = IOBase+0 +INTM1 = IOBase+1 +AUXCMD = IOBase+3 +BUSSTAT = IOBase+3 +CMD_PT = IOBase+6 +GPIB_DIN = IOBase+7 +GPIB_DOUT = IOBase+7 + + +ATADataHigh = IOBase+0 +SetCSMask = IOBase+1 ;Two special strobe locations to set and clear + ; MASK bit that is used to disable CS0 line to + ; the CompactFlash during the CPU read cycles +ClearCSMask = IOBase+2 ; that occur before every CPU write cycle. + ; The normally inoccuous read cycles were + ; causing the SanDisk CF to double increment + ; during sector writes commands. + +ATADevCtrl = IOBase+6 ;when writing +ATAAltStatus = IOBase+6 ;when reading +ATADataLow = IOBase+8 +ATAError = IOBase+9 +ATASectorCnt = IOBase+10 +ATASector = IOBase+11 +ATACylinder = IOBase+12 +ATACylinderH = IOBase+13 +ATAHead = IOBase+14 +ATACommand = IOBase+15 ; when writing +ATAStatus = IOBase+15 ; when reading + +; Scratchpad RAM base addresses. Access using the Y register containg the slot # +; +;DriveResetDone = $478 ;remember the device has been software reset +;DriveNumber = $4f8 ;normally 0 to 3 for four 32MB partitions +;SerialInitDone = $578 ;For debug: if $A5 then serial init is complete +;DrvBlkCount0 = $5f8 ;low byte of usable block count + ; (excluding first BLOCKOFFSET blocks) +;DrvBlkCount1 = $678 ;bits 8..15 of usable block count +;DrvBlkCount2 = $6f8 ;bits 16..23 of usable block count +;DrvMiscFlags = $778 ;bit 7 = raw LBA block access +;Available2 = $7f8 ;not currently used + +; Scratchpad RAM base address. Access using the Y register containg the slot # +; Remapped for HPIB +DriveResetDone = $478 ;remember the device has been software reset +DriveNumber = $4f8 ;normally 0 to 3 for four 32MB partitions +SerialInitDone = $578 ;For debug: if $A5 then serial init is complete +DrvBlkCount0 = $5f8 ;low byte of usable block count + ; (excluding first BLOCKOFFSET blocks (FIXME?)) +DrvBlkCount1 = $678 ;bits 8..15 of usable block count +DrvBlkCount2 = $6f8 ;bits 16..23 of usable block count +DrvBlkCount3 = $7f8 +DrvMiscFlags = $778 ;bit 7 = raw LBA block access + +Spare = $778 +;Available2 = $7f8 ;not currently used + + + +;------------------------------------------------------------------------------ +; Zero-page RAM memory usage + +.IF DEBUG ;data at these locations saved and restored +MsgPointerLow = $EB +MsgPointerHi = $EC +CheckSumLow = $ED +CheckSumHigh = $EE +.ENDIF + +zpt1 = $EF ;data at this location is saved/restored + +StackBase = $100 + +; ProDOS block interface locations +pdCommandCode = $42 +pdUnitNumber = $43 +pdIOBuffer = $44 +pdIOBufferH = $45 +pdBlockNumber = $46 +pdBlockNumberH = $47 + +; Arbitrary locations for Smartport data, +; these locations are saved/restored before exit. +spCommandCode = pdCommandCode + +spParamList = $48 ;2 bytes +spCmdList = $4A ;2 bytes +spCSCode = $4C +spSlot = $4D +SLOT = $4D +spSlotX16 = $4E +SLOTx16 = $4E +spLastZP = spSlotX16 + +spZeroPgArea = pdCommandCode ; $42 +spZeroPgSize = spLastZP-spZeroPgArea+1 + +DBC0 = $48 ;2 bytes +DBC1 = $49 +DBC2 = $4A ;2 bytes +DBC3 = $4B +Carry = $4C +modSpare = $4D + + +;------------------------------------------------------------------------------ +; Apple II ROM entry points +; +; We can use these at boot time, but not while handling a call through +; our ProDOS or SmartPort entry points, as the ROM may not be available. +; +SetVID = $FE89 +SetKBD = $FE93 +COUT = $FDED +INIT = $FB2F +HOME = $FC58 +ROMWAIT = $FCA8 +AppleSoft = $E000 +MONRTS = $FF58 + +;------------------------------------------------------------------------------ +; Start of Peripheral Card ROM Space $Cn00 to $CnFF +; A macro is used here so that this code can be easily duplicated for each slot +; instead of by hand using the EPROM programmer. This is possible done because +; the hardware does not overlay the C1xx address space at C2xx, C3xx, etc. +; automatically. Instead the base address for the EPROM is $C000 but is enabled +; only when a valid address in the range of $C100 to $CEFF is on the bus. This +; allows for the development of slot specific behaviors in firmware, if desired. +; Currently that is not being done, instead the same slot code is repeated for +; every slot ROM space. Addresses $C000 to $C0FF are not decoded by the +; hardware as it is Apple's internal I/O. Any access of $CF00 to $CFFF is +; decoded by the card to reset the Expansion ROM flip-flop, but remember to +; use always address $CFFF for that task. + +;.macro CnXX SLOTADDR, SLOTx16, SLOT +.ORG $C600 +; .local P8DriverEntry +; .local P8Driver +.IF SMARTPORT + .local SmartPortEntry + .local SPDriver +.ENDIF +; .local Boot +; .local Error +; .local NotScanning +; .local wasteTime +; .local ErrorMsgDisplay +; .local msgLoop +; .local msgDone +; .local ErrorMessage + + lda #$20 ;$20 is a signature for a drive to ProDOS + ldx #$00 ;$00 " + lda #$03 ;$03 " + +.IF SMARTPORT + lda #$00 +.ELSE + lda #$3c ;$3c " +.ENDIF + ;clc + ;bcc Boot + bra Boot + +;------------------- Non-boot P8 driver entry point --------------------- +; The EPROM holding this code is decoded and mapped into $C100 to $CEFF, +; it is not nessecary to dynamically determine which slot we are in, as is +; common in card firmware. This code is in a MACRO and is located absolutely +; per slot. Any code in this MACRO that is not relocatable will have to be +; based on the MACROs parameters SLOTADDR, SLOTx16, SLOT. + +P8DriverEntry: + nop + bra P8DMid + ; FIXME These jumps MUST be closer <128 bytes currently 184 +; jmp P8Driver ;located at $Cn0A for best compatibility + jmp P8Driver ;FIXME +.IF SMARTPORT +SmartPortEntry: ;By definition, SmartPort entry is 3 bytes + ; after ProDOS entry + jmp SPDriver +.ENDIF + +Boot: + jsr MONRTS ;Get this location on stack + tsx ;Find page byte on stack + lda StackBase,x + sta mslot + and #$0f + tay + asl a + asl a + asl a + asl a + tax + lda SLOT + pha + lda SLOTx16 + pha + sty SLOT + stx SLOTx16 + + ;ldy #SLOT ;Y reg now has $0n for accessing scratchpad RAM + ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O +; lda #>SLOTADDR ;loads A with slot# we are in:$Cn +; sta mslot ;Apple defined location reserved for last slot + ; active. MSLOT needs the form of $Cn + bit $cfff ;turn off expansion ROMs + +;; +;; Initialize the 9914 to known state +;; +; jsr Init9914 +;; +;; Call DSJ (Device Specified Jump) for status code and reset DSJ to 0. +;; +; jsr DSJ +;; +;; Get drive status +;; +; jsr STAT +; + +; jsr DSString +; .byte " St",0 +; jsr DisplayParms +; +; Need to wait here (before CheckDevice) in case the CFFA RESET jumper +; is enabled, or a Delkin Devices CF card never becomes ready. +; +; ldy #5 +;wasteTime: +; lda #WAIT_100ms +; jsr ROMWAIT +; dey +; bne wasteTime +; ldy SLOT + +; jsr CheckDevice +; bcs Error + + lda #PRODOS_READ ;Request: READ block + sta pdCommandCode + stz pdIOBuffer ;Into Location $800 + stz pdBlockNumber ;ProDOS block $0000 (the bootloader block) + stz pdBlockNumberH + lda #$08 + sta pdIOBufferH + stx pdUnitNumber ;From unit number: $n0 (where n=slot#), + ; so drive bit is always 0 + + + ;jsr P8Driver ;Read bootloader from device's block 0 into + ; location $800 + + clv + jsr $FF58 + bvc P8Drts + + bcs Error ;Check for error during bootblock read + + lda $800 ;Check the first byte of boot loader code. + cmp #$01 ;If bootload code is there, this byte = $01 + bne Error + lda $801 ;If second byte is a 0, it's invalid + ; (we'd JMP to a BRK) + beq Error + + ldx pdUnitNumber ;X should contain the unit number when jumping + ; to the bootloader + jmp $801 ;No errors, jump to bootloader just read. + +P8DMid: + bra P8Driver + +; If any error occured, like drive not present, check to see if we are in a +; boot scan, if so re-enter scan routine, else drop to Applesoft, aborting boot. +Error: + lda $00 + bne NotScanning + lda $01 + cmp mslot + bne NotScanning + jmp $FABA ;Re-enter Monitor's Autoscan Routine + +;The boot code must have been called manually because we are not in a slot scan. +NotScanning: + jsr SetVID + jsr SetKBD +; +;Display error message +; + jsr INIT ;text mode, full screen, page 1 + jsr HOME + ldy #0 +msgLoop: + lda ErrorMessage,y + beq msgDone + ora #$80 + jsr COUT + iny + bra msgLoop +msgDone: + jmp AppleSoft + +ErrorMessage: + .byte CR,CR,CR + .byte "Dev missing, not formatted,",CR + .byte "or incompatible.",CR +; "vX.Y" built automatically: + .byte "Ver:",$30+(FIRMWARE_VER/16),".",$30+(FIRMWARE_VER & $F) +; Beep, then end-of-message: + .byte BELL,$00 + + +;------------------- Non-boot entry point for driver code ----------------- +; +; Handle a ProDOS call +; +; Setup MSLOT, X and Y registers. +; This must be done every time we enter this driver. +; +P8Drts: + tsx + dex + dex + txs +P8Driver: + jsr MONRTS ;Get this location on stack + tsx ;Find page byte on stack + lda StackBase,x + sta mslot + and #$0f + tay + asl a + asl a + asl a + asl a + tax + ;sty SLOT + ;stx SLOTx16 + + ;ldy SLOT ;Y reg now has $0n for accessing scratchpad RAM + ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O + ;lda #>SLOTADDR ;loads A with slot# we are in:$Cn(where n=slot#) + ;sta mslot ;Apple defined location reserved for last slot + ; active. MSLOT needs the form of $Cn + bit $cfff ;turn off other ROM that might be on + + jsr P8AuxROM + bit $ff58 + rts + +.IF SMARTPORT +; NO SMARTPORT DRIVER BUILT IN +.ENDIF + + .RES $C6F5-* ;skip to $CnF5, where n is the slot# + + ;.org $C6F5 + + .byte GSOS_DRIVER ;GS/OS driver compatibility byte. GS/OS driver + ; checks this byte to see if it is compatible + ; with this version of firmware. This way, + ; changes to firware versions, that do not + ; affect the GS/OS driver will not prevent the + ; GS/OS driver from loading and running. This + ; byte should be incremented each time a change + ; is made that would prevent the GS/OS driver + ; from working correctly. I.e. Partition layout + ; or something similar. + + .byte "CFFA", FIRMWARE_VER ;$CnF6..CnFA: Card Hardware ID, + ; non-standard scheme + + .byte $0 ;$CnFB: SmartPort status byte + ; Not Extended; not SCSI; not RAM card + ; Even if not supporting SmartPort, we need a + ; zero at $CnFB so Apple's RAMCard driver + ; doesn't mistake us for a "Slinky" memory + ; card. + +; Data table for ProDOS drive scan +; $CnFC/FD = disk capacity, if zero use status command to determine +; $CnFE = status bits (BAP p7-14) +; 7 = medium is removable +; 6 = device is interruptable +; 5-4 = number of volumes (0..3 means 1..4) +; 3 = device supports Format call +; 2 = device can be written to +; 1 = device can be read from (must be 1) +; 0 = device status can be read (must be 1) +; +; $CnFF = LSB of block driver + .word $0000 ;$CnFC-D: A zero here will cause prodos to + ; rely on the status command to determine + ; volume size + + .byte $9F ; $CnFE: support 2 ProDOS drives + .byte drive # then StatusSize = $FFFF +; If DrvBlkCount2 = drive # then StatusSize = DrvBlkCount1,DrvBlkCount0 +; If DrvBlkCount2 < drive # then StatusSize = 0 +; +; This scheme has a special case which must be handled because ProDOS +; partitions are not quite 32 meg in size but are only FFFF blocks in size. +; If a device is exactly: 32meg or 10000h blocks in size, it would appear +; as one drive of size FFFF and another drive of size 0000. To handle this +; case, we check for an exact size of 0000 and fall into the NoDrive code. +; +; FIXME: Hmmm. Floppy + HD combo. How to handle? + ;lda DriveNumber,y + ;cmp DrvBlkCount2,y + ;beq ExactSize + bra ExactSize + bcc FullSize + +NoDrive: + ldx #0 + ldy #0 + lda #PRODOS_OFFLINE + sec + bra sExit + +ExactSize: ;If equal, the DrvBlkCount1,DrvBlkCount0 is the + ; drive's exact size + lda DrvBlkCount0,y + ora DrvBlkCount1,y + beq NoDrive ;can't have a 0-block device + + lda DrvBlkCount0,y + tax + lda DrvBlkCount1,y + tay + lda #0 + clc ;no errors + bra sExit + +FullSize: + ldx #$FF ;X gets low byte of size + ldy #$FF ;Y gets high byte of size + lda #0 + clc ;no errors + +sExit: +.IF DEBUG + php ;save the carry's state + pha + jsr DSString + .byte "Rt:",0 + tya + jsr DSByte + txa + jsr DSByteCRLF + pla + plp ;recover the carry +.ENDIF + + rts + +;------------------------------------------------------------------------------ +; ReadBlock - Read a block from device into memory +; +; Input: +; pd Command Block Data $42 - $47 +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; Output: +; A = ProDOS read return code +; Carry flag: 0 = Okay, 1 = Error +; +; ZeroPage Usage: +; $EF +; w/DEBUG enabled: $EB, $EC, $ED, $EE +; Note: location $EF is saved and restored before driver exits +; +ReadBlock: +.IF DEBUG + jsr DSString + .byte " Rd",0 + ;jsr DisplayParms +.ENDIF + + lda pdIOBufferH + pha + lda zpt1 ; $EF save + pha + +.IF 0 ;DEBUG + lda CheckSumHigh + pha + lda CheckSumLow + pha +.ENDIF + + jsr ReadBlockCore + +.IF 0 ;DEBUG + ply + sty CheckSumLow + ply + sty CheckSumHigh +.ENDIF + + ply + sty zpt1 + ply + sty pdIOBufferH + rts + +ReadBlockCore: +.IF 0 ;DEBUG + stz CheckSumLow + stz CheckSumHigh +.ENDIF + +.IF DEBUG + jsr DSString + .byte " Sk",0 + ;jsr DisplayParms +.ENDIF + + jsr Block2Sector + + jsr HPIBSeek + + ; based on ProDOS address + +.IF DEBUG + jsr DSString + .byte " ORd",0 + ;jsr DisplayParms +.ENDIF + + ;lda #0 + ;sta ATADataHigh,x + + ;lda #ATACRead + ;sta ATACommand,x ;Issue the read command to the drive + ;jsr IDEWaitReady ;Wait for BUSY flag to clear + + jsr GPIBReadOpen + + bcs rShort ;any errors? + +.IF DEBUG + jsr DSString + .byte " GD",0 + ;jsr DisplayParms +.ENDIF + + bra rCommandOK + +.IF DEBUG +;warning this debug code trashes the Acc register + jsr DSString + .byte " Er",0 + lda ATAError,x + jsr DSByteCRLF +.ENDIF + +; +; The drive has returned an error code. Just return I/O error code to PRODOS +; + lda #PRODOS_IO_ERROR + sec + rts +; +; Sector is ready to read +; +rCommandOK: + phy + ldy #2 ;2 itterations + sty zpt1 + ldy #0 ; of 256 cycles Need seek? + +rLoop: +; lda ATAStatus,x ;Note: not using IDEWaitReady, using inline code +; bmi rLoop ;Wait for BUSY (bit 7) to be zero +; and #$08 ;get DRQ status bit +; beq rShort ;if off, didn't get enough data + + jsr GetGPIB + bcc @continue + + ply + phy + + ; Close this sector and open the next + jsr GPIBReadClose + inc pdIOBufferH + dec zpt1 + beq @done + + ; Not done so start reading the next sector + jsr GPIBReadOpen + jsr GetGPIB + + ldy #0 + +@continue: + sta (pdIOBuffer),y + iny + +.IF 0; DEBUG + jsr DSByte +.ENDIF + +.IF 0; DEBUG + clc + adc CheckSumLow + sta CheckSumLow +.ENDIF + +; iny +; bne rLoop + bra rLoop +@done: +; inc pdIOBufferH +; dec zpt1 +; bne rLoop + +.IF 0; DEBUG + jsr DSString + .byte " Chk$",0 + + lda CheckSumHigh + jsr DSByte + lda CheckSumLow +.ENDIF +.IF DEBUG + jsr DSCRLF +.ENDIF + + ply + lda #0 + clc + rts +; +; The Block was short, return I/O error code to PRODOS +; +rShort: +.IF DEBUG + jsr DSString + .byte "RSh", 0 + + lda zpt1 + jsr DSByte + tya + jsr DSByteCRLF +.ENDIF + + lda #PRODOS_IO_ERROR + sec + rts + +;------------------------------------------------------------------------ +; WriteBlock - Write a block in memory to device +; +; Input: +; pd Command Block Data $42 - $47 +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; Output: +; A = ProDOS write return code +; Carry flag: 0 = Okay, 1 = Error +; +; ZeroPage Usage: +; $EF +; w/DEBUG enabled: $EB, $EC, $ED, $EE +; Note: location $EF is saved and restored before driver exits +; +WriteBlock: +.IF DEBUG + jsr DSString + .byte " W",0 + ;jsr DisplayParms +.ENDIF + + lda pdIOBufferH + pha + lda zpt1 + pha + +.IF 0 ; DEBUG + lda CheckSumHigh + pha + lda CheckSumLow + pha +.ENDIF + + jsr WriteBlockCore + +.IF 0 ; DEBUG + ply + sty CheckSumLow + ply + sty CheckSumHigh +.ENDIF + + ply + sty zpt1 + ply + sty pdIOBufferH + rts + +WriteBlockCore: +.IF 0 ; DEBUG + stz CheckSumLow + stz CheckSumHigh +.ENDIF + + jsr Block2Sector + + jsr HPIBSeek + + ; Check Error on Seek? +; lda ATAStatus,x ;Check for error response from writing command +; and #$09 +; cmp #$01 ;if DRQ=0 and ERR=1 an error occured +; bne wCommandOK + bra wCommandOK + +.IF DEBUG +;warning this debug code trashes the Acc register + jsr DSString + .byte " Er",0 + lda ATAError,x + jsr DSByteCRLF +.ENDIF + +; The drive has returned an error code. Just return I/O error code to PRODOS +; + lda #PRODOS_IO_ERROR + sec + rts +; +; Sector is ready to write +; +wCommandOK: + phy + ldy #2 + sty zpt1 + +wNextSector: + ply + phy + jsr GPIBWriteOpen + clc + ldy #0 +wLoop: + +.IF 0 ;DEBUG + clc + adc CheckSumLow + sta CheckSumLow +.ENDIF + + clc + lda (pdIOBuffer),y + iny + bne @continue + sec ; last byte +@continue: + jsr SendGPIB + +.IF 0 ;DEBUG + adc CheckSumHigh + sta CheckSumHigh +.ENDIF + + cpy #0 + bne wLoop + ply + phy + jsr GPIBWriteClose + inc pdIOBufferH + dec zpt1 + bne wNextSector + + ply + +.IF 0 ;DEBUG +; Display the Checksum +; warning this debug code trashes the Acc register + jsr DSString + .byte " Chk$",0 + lda CheckSumHigh + jsr DSByte + lda CheckSumLow + jsr DSByteCRLF +.ENDIF + + lda #0 + clc + rts +; +; The Block was short, return I/O error code to PRODOS +; +wShort: +.IF DEBUG +; Display "W:Short" + jsr DSString + .byte " WSh", 0 + + lda zpt1 + jsr DSByte + tya + jsr DSByteCRLF +.ENDIF + + jsr GPIBWriteClose + + lda #PRODOS_IO_ERROR + sec + rts + +;------------------------------------------------------------------------------ +; +; Block2Sector - Translates ProDOS blocks into head, cylinder, sector +; +; Input: +; pd Command Block Data $42 - $47 +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Y = $0n (n = slot#) for accessing scratchpad RAM; +; +; Ouput: +; None +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: A, P +; +Block2Sector: + ; FIXME Will need identify table for these values. + ;lda #2 + ;;sta MXHEAD + ;lda #16 ; Need to / 2 (8 blocks = 16 256byte sectors) + ;;sta MXSECTOR + ;lda #35 + ;;sta MXCYLL + ;lda #0 + ;;sta MXCYLH + jsr DSCRLF + lda pdBlockNumberH + pha +.IF 0 ;DEBUG + jsr DSByte +.ENDIF + lda pdBlockNumber + pha +.IF 0 ;DEBUG + jsr DSByteCRLF + pla + pha +.ENDIF + + ; 2 heads + clc + + ;ror pdBlockNumberH ; Only for / 2 need something else for more than 2 heads + ;ror pdBlockNumber + ;lda #0 + ;rol a + ;pha ;push the head + + lda #0 + sta DrvBlkCount1,y +.IF 0;DEBUG + jsr DSByte +.ENDIF + clc + lda #16 ; Divsor = sectors per cylinder + ror a ; 512 byte per block (256 bytes per sector) + sta DrvBlkCount0,y +.IF 0;DEBUG + jsr DSByteCRLF +.ENDIF + + jsr Mod + + ; Block + clc + lda DrvBlkCount2,y + rol a ; Convert Block to Starting Sector (currently always < 256) + sta DrvBlkCount2,y + + jsr DSByte + + ; DrvBlkCount2 = SECTOR + + ;pla ; Pop head + lda #0 + sta DrvBlkCount3,y ; Store Head +.IF 0;DEBUG + jsr DSByteCRLF + ; Cylinder (16 bit) + lda DrvBlkCount1,y + jsr DSByte + lda DrvBlkCount0,y + jsr DSByteCRLF +.ENDIF + + pla + sta pdBlockNumber + pla + sta pdBlockNumberH + + rts + +; FIXME? +; Add BLOCKOFFSET to the ProDOS block number to offset the first drive block we +; use. This keeps the device's first BLOCKOFFSET blocks free, which usually +; includes a MBR at block 0. +; rawread (FIXME?) +; + +;------------------------------------------------------------------------------ +; +; HPIBSeek - Seek drive head to D#,CYLH,CYLL,HEAD,SECTOR +; +; Must call Block2Sector to load values (or manually load them) +; +HPIBSeek: + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$68 ;Secondary Listen + sta GPIB_DOUT,x ;inline the data out for GTS + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$02 ; OpCode (SEEK) + jsr SendGPIB + + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda DrvBlkCount1,y ;CylH + jsr SendGPIB + + lda DrvBlkCount0,y ;CylL + jsr SendGPIB + + lda DrvBlkCount3,y ;Head + jsr SendGPIB + + sec ; Last byte + lda DrvBlkCount2,y ;Sector + jsr SendGPIB + + lda #$3F ;Unlisten (Seek is unusual in that it wants the this + jsr SendGPIB ; before the Poll). + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + rts + +; FIXME are we sending EOI??? +; Error checking (no read) done via DSJ = 1 (Bad Data) +;------------------------------------------------------------------------------ +; +GPIBReadOpen: + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$6A ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$05 ; OpCode (Buffered Read) + jsr SendGPIB + + sec ; Last byte + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + jsr DSJ ;Any errors? + bcs ROError + + ; Actually read the data + lda #$40 ; TALK Address (MTA) + ora DriveNumber,y + jsr SendGPIB + lda #$60 ; Secondary TALK (MSA) + sta GPIB_DOUT,x + +.IF DEBUG + jsr DSByte +.ENDIF + + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + +ROError: + + rts + +;------------------------------------------------------------------------------ +; +GPIBReadClose: + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + lda #TON + sta AUXCMD,x + + lda #$5f ;UNTALK + jsr SendGPIB + +.IF DEBUG + jsr DSCRLF +.ENDIF + rts + +;------------------------------------------------------------------------------ +; +GPIBWriteOpen: + ;lda #$14 ; Universal Clear + ;jsr SendGPIB + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$69 ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$08 ; OpCode (Buffered Write) + jsr SendGPIB + + sec ; Last byte + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$60 ;Secondary Listen + ;jsr SendGPIB + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + jsr DSCRLF + + rts + + ;Send Data to write + +;GPIBWriteLoop: + +; jsr SendGPIB +; rts + + +;------------------------------------------------------------------------------ +; +GPIBWriteClose: + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr DSCRLF + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + rts + +;------------------------------------------------------------------------------ +; +; HP Identify Command. (HP-IB weirdness and completely non-standard). +; +HPIBId: + lda #$5f ;UNTALK + clc + jsr SendGPIB + lda DriveNumber,y + ora #$60 + sta GPIB_DOUT,x + + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + +@More: + jsr GetGPIB +.IF DEBUG + php + jsr DSByte + plp +.ENDIF + bcc @More + + lda #TON + sta AUXCMD,x + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + +.IF DEBUG + jsr DSCRLF +.ENDIF + + rts + + +;------------------------------------------------------------------------------ +; +; AMIGO Format command. CAUTION: LOW LEVEL FORMATS MEDIA. +; +AMIGOFormat: + clc + lda #$20 ; Listen Drive should start at 1 + ora DriveNumber,y + jsr SendGPIB + + lda #$6C ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$18 ; OpCode (FORMAT) + jsr SendGPIB + + lda #$00 ; Disk Unit Number (0) + jsr SendGPIB + + lda #$80 + jsr SendGPIB + + lda #9 + jsr SendGPIB ; Interleave (not used) + sec ; Last byte + lda #$00 + jsr SendGPIB ; Data Byte (not used) + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + + jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + lda #0 ; No PRODOS error to report + clc + rts + + + +;------------------------------------------------------------------------------ +; +; HPIBPPoll: Parallel poll the drive waiting for a response. Drives that are +; busy are 'held off' by having parrallel poll disabled. In essence if it +; doesn't respond to a poll then its busy or offline. +; +; FIXME: Need to set a limit for the number of polls or the amount of time. +; HP says 60 seconds max response time. +; +HPIBPPoll: + phy + +@repoll: + lda #PPOL_ON + sta AUXCMD,x + + lda #WAIT_100ms ; Give drive time to respond to the poll (HP recommends 50us) + jsr Wait + + ply + phy + + lda #$08 + sec + sbc DriveNumber,y + tay + lda #0 + sec +@B: + rol a + dey + bne @B ;HP-IB devices respond to poll on DIO# = 8 - device ID + and CMD_PT,x + php + + pha + lda #PPOL_OFF + sta AUXCMD,x + pla + plp + + beq @repoll + + ply + + clc + rts + +;------------------------------------------------------------------------------ +; +; Find ZP locations to speed this up (use pha loop?) +; +; kudos to Garth Wilson for this function from www.6502.org +Mod: + phy + phx + tya + tax + +; save ZP, fetch cmd + pointer from (PC) + ldy #spZeroPgSize-1 +@save: + lda spZeroPgArea,y + pha + dey + bpl @save + + txa + tay + + lda DrvBlkCount1,y + sta DBC1 + lda DrvBlkCount0,y + sta DBC0 + + ; Will only ever use 16 bit values (Optimize?) + stz DBC3 + stz DBC2 + + ;pdBlockNumber(H) Already adjusted and saved at this point. + + phy + + + SEC ; Detect overflow or /0 condition. + LDA DBC2 ; Divisor must be more than high cell of dividend. To + SBC DBC0 ; find out, subtract divisor from high cell of dividend; + LDA DBC3 ; if carry flag is still set at the end, the divisor was + SBC DBC1 ; not big enough to avoid overflow. This also takes care + BCS @oflo ; of any /0 condition. Branch if overflow or /0 error. + ; We will loop 16 times; but since we shift the dividend + LDX #$11 ; over at the same time as shifting the answer in, the + ; operation must start AND finish with a shift of the + ; low cell of the dividend (which ends up holding the + ; quotient), so we start with 17 (11H) in X. +@loop: + ROL pdBlockNumber ; Move low cell of dividend left one bit, also shifting + ROL pdBlockNumberH ; answer in. The 1st rotation brings in a 0, which later + ; gets pushed off the other end in the last rotation. + DEX + BEQ @end ; Branch to the end if finished. + + ROL DBC2 ; Shift high cell of dividend left one bit, also + ROL DBC3 ; shifting next bit in from high bit of low cell. + STZ Carry ; Zero old bits of CARRY so subtraction works right. + ROL Carry ; Store old high bit of dividend in CARRY. (For STZ + ; one line up, NMOS 6502 will need LDA #0, STA CARRY.) + SEC ; See if divisor will fit into high 17 bits of dividend + LDA DBC2 ; by subtracting and then looking at carry flag. + SBC DBC0 ; First do low byte. + STA modSpare ; Save difference low byte until we know if we need it. + LDA DBC3 ; + SBC DBC1 ; Then do high byte. + TAY ; Save difference high byte until we know if we need it. + LDA Carry ; Bit 0 of CARRY serves as 17th bit. + SBC #0 ; Complete the subtraction by doing the 17th bit before + BCC @loop ; determining if the divisor fit into the high 17 bits + ; of the dividend. If so, the carry flag remains set. + LDA modSpare ; If divisor fit into dividend high 17 bits, update + STA DBC2 ; dividend high cell to what it would be after + STY DBC3 ; subtraction. + BRA @loop + +@oflo: + LDA #$FF ; If overflow occurred, put FF + STA DBC2 ; in remainder low byte + STA DBC3 ; and high byte, + STA pdBlockNumber ; and in quotient low byte + STA pdBlockNumberH ; and high byte. + +@end: + + ply + + lda DBC2 + sta DrvBlkCount2,y + lda DBC3 + sta DrvBlkCount3,y + + lda pdBlockNumberH + sta DrvBlkCount1,y + lda pdBlockNumber + sta DrvBlkCount0,y + + +; Restore zero page + ldy #0 +restore: + pla + sta spZeroPgArea,y + iny + cpy #spZeroPgSize + bcc restore + + plx + ply + + RTS + +;------------------------------------------------------------------------------ +; IDEWaitReady - Waits for BUSY flag to clear, and returns DRQ bit status +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Ouput: +; Carry flag = DRQ status bit +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: A, P +; +IDEWaitReady: +; lda ATAStatus,x +; bmi IDEWaitReady ;Wait for BUSY (bit 7) to be zero +; ror ;shift DRQ status bit into the Carry bit +; ror +; ror +; ror +; rts + +;------------------------------------------------------------------------------ +; CheckDevice - Check to see if a device is attached to the interface. +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Output: +; Carry flag: 0 = Device Present, 1 = Device Missing +; +; CPU Registers changed: A, P +; +; Checks to see if the drive status register is readable and equal to $50 +; If so, return with the Carry clear, otherwise return with the carry set. +; Waits up to 10sec on a standard 1Mhz Apple II for drive to become ready +; +CheckDevice: +; + jsr DSJ + ; If Carry set DSJ <> 0 force status + bcc DeviceFound + + jsr STAT + bcc DeviceFound + +; sec ;set c = 1 if drive is not attached +; ply + rts + +DeviceFound: +; ply + rts + + +;------------------------------------------------------------------------------ +; Status - Gets status of +; +; Input: None +; +; CPU Registers changed: None +; + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +;; DSJ - Device Specified Jump (Internal Subroutine) +;; +DSJ: + ;jsr DSString + ;.byte " Dsj:",0 + + clc + lda #$40 ; TALK Address (MTA) + ora DriveNumber,y + jsr SendGPIB + + lda #$60 ; Secondary TALK (MSA) + ora #$10 ; Secondary Command + sta GPIB_DOUT,x + + lda #HDFA + sta AUXCMD,x + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + + ; Single byte returned from DSJ. +@More: + jsr GetGPIB + ; sta ??? +.IF 0 ;DEBUG + php + jsr DSByte + plp +.ENDIF + bcc @More + + and #$03 + bne @uc + + lda #TCS + sta AUXCMD,x + jsr WaitOut9914;IO + + lda #RHDF + sta AUXCMD,x + + lda #HDACLR + sta AUXCMD,x + + lda #TON + sta AUXCMD,x + + lda #$5f ;UNTALK + clc + jsr SendGPIB + +.IF DEBUG + jsr DSCRLF +.ENDIF + + clc + rts + +@uc: + lda #TCS + sta AUXCMD,x + jsr WaitOut9914;0 + + lda #RHDF + sta AUXCMD,x + + lda #HDACLR + sta AUXCMD,x + + lda #TON + sta AUXCMD,x + + clc + lda #$5f ;UNTALK + jsr SendGPIB +.IF DEBUG + jsr DSCRLF +.ENDIF + +; clc +; lda #$14 ; Universal Clear +; jsr SendGPIB + +; jsr HPIBPPoll + +; Its hanging on new unformatted disk read. Was working when status / universal clear was being sent each time. +; Reads already formatted ProDOS disk (check again). + + sec ;Set Carry to force Status + rts + +;------------------------------------------------------------------------------ +; STAT - Get Drive Status Information. +; +STAT: + lda zpt1 + pha + jsr DSString + .byte "T:",0 + + clc + lda #$20 ;My address (Apple GPIB address) + jsr SendGPIB + + lda #$3F ;Unlisten + jsr SendGPIB + + lda #$20 + ora DriveNumber,y + jsr SendGPIB + + lda #$68 ;Secondary Listen + sta GPIB_DOUT,x + + lda #GTS ;Go to stand by + sta AUXCMD,x + jsr WaitOut9914 ;Wait for Secondary Listen Out + + lda #$03 ; OpCode + jsr SendGPIB + + lda #$00 ; Disk Unit Number (0) + sec + jsr SendGPIB + + lda #TCA + sta AUXCMD,x + jsr WaitOut9914;IO + +jsr HPIBPPoll + + lda #$3F ;Unlisten + jsr SendGPIB + + ;------------------------------------ + ; Get the response from stat request + clc + lda #$40 ; TALK Address (MTA) + ora DriveNumber,y + jsr SendGPIB + + lda #$60 ; Secondary TALK (MSA) + ora #$08 ; Secondary Command + ;jsr SendGPIB + sta GPIB_DOUT,x +.IF 0;DEBUG + jsr DSByte + clc +.ENDIF + lda #HDFA + sta AUXCMD,x + lda #LON + sta AUXCMD,x + lda #GTS + sta AUXCMD,x + jsr WaitOut9914 ;SendGPIB + ;jsr SendGPIB + + ; Multiplie bytes returned. FIXME ignore for now + phy + ldy #0 +@More: + jsr GetGPIB + php + + cpy #2 + bne @chk2 + lsr a + and #$0f + sta zpt1 +; bra @chk2 +;@chk1: +; cpy #3 +; bne @chk2 +; and #$03 + + +@chk2: + +.IF 0;DEBUG + jsr DSByte +.ENDIF + + plp + iny + bcc @More + ply + clc + + lda #TCS + sta AUXCMD,x + jsr WaitOut9914;IO + + lda #RHDF + sta AUXCMD,x + + lda #HDACLR + sta AUXCMD,x + + lda #TON + sta AUXCMD,x + + lda #$5f ;UNTALK + jsr SendGPIB + +.IF 0;DEBUG + jsr DSCRLF +.ENDIF + + lda zpt1 + beq @bad ;use bmi? + cmp #$05 + bne @good + +@bad: + sec + pla + sta zpt1 + lda #PRODOS_IO_ERROR + rts + +@good: + clc + pla + sta zpt1 + lda #0 + rts + +;------------------------------------------------------------------------------ +; Init9914 - Routine to initialize the TMS9914 GPIBA +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; 9914 State on exit: +; B0 set +; All interrupts masked off +; Talk Only (TON) on +; Not listener active (LA) +; No holdoffs enabled or active +; 9914 Polled for status +; +; CPU Registers changed: A, P +; +Init9914: + ;ldx #SLOTx16 ;X reg now has $n0 for indexing I/O + lda #RESET + sta AUXCMD,x + lda #RSTCLR + sta INTM0,x + sta INTM1,x + sta AUXCMD,x + lda #SIC + sta AUXCMD,x + + txa + pha + tya + pha + + ldx #4 ;5.12 usec delay (FIXME) + ldy #0 +@init1: + dey + bne @init1 + dex + bne @init1 + + pla + tay + pla + tax + + ;Set my address + lda #$00 + sta IOBase+4,x + + ;ldx #SLOTx16 + lda #SICLR + sta AUXCMD,x + lda #$11 + sta AUXCMD,x + lda #$05 + sta AUXCMD,x + lda #TON + sta AUXCMD,x + jsr WaitOut9914 + + rts + +;------------------------------------------------------------------------------ +; SendGPIB - Output data then wait for Byte Out stat change. +; +; Input: +; A = Data Out +; P = Carry Set = Last byte (Assert EOI) +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; CPU Registers changed: A, P +; +SendGPIB: + bcc @NotLast + clc + pha + jsr DSString + .byte "e",0 + lda #$08 + sta AUXCMD,x + pla +@NotLast: + sta GPIB_DOUT,x +.IF 0;DEBUG + jsr DSByte + clc +.ENDIF + ;FALL THROUGH + +;------------------------------------------------------------------------------ +; WaitOut9914 - Wait for Byte Out stat change. +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; +; CPU Registers changed: A, P +; +; lda BUSSTAT,x +; jsr DSByte +WaitOut9914: +; phy +; ldy #10 ;10 timeouts (~1 second) +@reload: +; lda #WAIT_100ms +; jsr TimeSet +@again: +; jsr TimeCheck +; bcs @continue +; jsr TimeClear +; dey +; beq @timeout +; bra @reload +@continue: + lda INT0,x + and #BOM + beq @again +; jsr TimeClear +; clc +; ply + rts +@timeout: +; jsr TimeClear +.IF DEBUG +; jsr DSString +; .byte "TO",0 +; jsr DSCRLF +.ENDIF +; sec +; ply + rts + +;------------------------------------------------------------------------------ +; WaitIn9914 - Wait for Byte In AND/OR EOI stat change. +; +; Input: +; X = requested slot number in form $n0 where n = slot 1 to 7 +; Output: +; A = Byte received +; C = Set if last byte cleared otherwise +; +; CPU Registers changed: A, P +; +; Timeout without actually waiting??? +GetGPIB: +; phy +; ldy #10 ;10 tries (1 second) +@again: +; lda #WAIT_100ms +; jsr Wait +; dey +; beq @timeout + + lda INT0,x + and #EOIMK+BIM + beq @again + + clc + and #EOIMK + bne @LastByte ; Got EOI Get final byte + lda GPIB_DIN,x + pha + lda #RHDF + sta AUXCMD,x + pla + ; FIXME Release Holdoffs ??? + rts + +@LastByte: + lda GPIB_DIN,x + sec ;Set carry this is the last byte + ; FIXME Need TCS (Take Control Sync) ??? + ; Release HOLDOFFS? + rts + +;------------------------------------------------------------------------------ +; Wait - Copy of Apple's wait routine. Can't use ROM based routine in case +; ROM is not active when we need it. +; +; Input: +; A = desired delay time, where Delay(us) = .5(5A^2 + 27A + 26) +; or more usefully: A = (Delay[in uS]/2.5 + 2.09)^.5 - 2.7 +; +; CPU Registers changed: A, P +; +Wait: + sec + +Wait2: + pha + +Wait3: + sbc #1 + bne Wait3 + pla + sbc #1 + bne Wait2 + rts + +;------------------------------------------------------------------------------ +; Timeout Routines +; - TimeSet - A = Wait time (not 100% accurate) +; - TimeCheck - clc = timeout +; - TimeClear - Clears the stack +; +; ZP may be more suited for this task. +; +; CPU Registers changed: A, P +; +TimeSet: + pha + rts + +;------------------------------------- +TimeCheck: + pla + pha + sec + sbc #1 + beq @stilltime + pla + sbc #1 + beq @stilltime2 + ;carry clear = timeout +@stilltime2: + pha +@stilltime: + rts + +;------------------------------------- +TimeClear: + pla + rts + +.IF DEBUG + +;------------------------------------------------------------------------------ +; DisplayParms - Display the parameters of the ProDOS request +; Input: +; None +; Ouput: +; None +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: A, P +; +DisplayParms: +; jsr DSString +; .byte " B:",0 + +; lda pdBlockNumberH +; jsr DSByte +; lda pdBlockNumber +; jsr DSByte + +; jsr DSString +; .byte " U:",0 +; lda pdUnitNumber +; jsr DSByte + +; jsr DSString +; .byte " A$",0 + +; lda pdIOBufferH +; jsr DSByte +; +; lda pdIOBuffer + bra DSByte + + +;------------------------------------------------------------------------------ +; DSString - Sends a String to the Super Serial Card in Slot 2 +; Input: +; string must immediately follow the JSR to this function +; and be terminated with zero byte. +; Ouput: +; None +; +; ZeroPage Usage: +; MsgPointerLow, MsgPointerHi +; +; CPU Registers changed: A, P +; +DSString: + phx ;save the X reg + tsx ;put the stack pointer in X + lda MsgPointerLow + pha ;push zero page location on stack + lda MsgPointerHi + pha ;push zero page location on stack + + lda StackBase+2,x ;determine the location of message to display + clc + adc #$01 ;add 1 because JSR pushes the last byte of its + sta MsgPointerLow ; destination address on the stack + + lda StackBase+3,x + adc #0 + sta MsgPointerHi + +dss1: + lda (MsgPointerLow) + beq dssend + jsr DSChar ;display message + inc MsgPointerLow + bne dss1 + inc MsgPointerHi + bra dss1 + +dssend: + + lda MsgPointerHi + sta StackBase+3,x + lda MsgPointerLow + sta StackBase+2,x ;fix up the return address on the stack. + + pla + sta MsgPointerHi ;restore zero page location + pla + sta MsgPointerLow ;restore zero page location + plx + clc + rts ;return to location after string's null. + +;------------------------------------------------------------------------------ +; DSByteCRLF - Sends a Hex byte followed by a CR LF to the Super Serial +; Card in Slot 2 +; +; Input: +; A = Hex number to display +; Ouput: +; None +; +; CPU Registers changed: A, P +; +DSByteCRLF: + jsr DSByte +DSCRLF: + lda #$0D + jsr DSChar + lda #$0A + bra DSChar + +;------------------------------------------------------------------------------ +; DSByte - Sends a Hex byte to the Super Serial Card in Slot 2 +; Input: +; A = Hex number to display +; Ouput: +; None +; +; CPU Registers changed: A, P +; +DSByte: + pha + lsr a + lsr a + lsr a + lsr a + jsr DSNibble + pla +DSNibble: + and #$0F + ora #$30 + cmp #$30+10 + bcc digit + adc #6 +digit: + bra DSChar + +;------------------------------------------------------------------------------ +; DSChar - Sends a char to the Super Serial Card in Slot 2 +; Input: +; A = Character to Send +; Ouput: +; (data out serial port) +; +; ZeroPage Usage: +; None +; +; CPU Registers changed: P +; +DSBlank: + lda #$20 +DSChar: + pha + phy + lda mslot + and #$0f + tay ;Y reg now has $0n for accessing scratchpad RAM + lda SerialInitDone,y + cmp #$A5 + beq dsc0 + +; Init the serial port if sig byte is not $A5. +; Set up serial port on the Apple Super Serial card. Always assume slot 2. + lda #$1f + sta $c0ab ;control register + lda #$0b + sta $c0aa ;format + lda $c0a9 ;clear status + lda #$A5 + sta SerialInitDone,y + +dsc0: + lda $c0a9 + and #%00010000 ;Transmit register empty? + beq dsc0 ;If not, wait + + ply + pla ;get byte back + sta $c0a8 ;send it + clc + rts + +.ENDIF + + +.WARNING .SPRINTF("%04X",*) +.IF * > $cfff +.WARNING "* TOO BIG!!!" +.ENDIF +.word *