AMIGODOS/amigodos.s

2533 lines
48 KiB
ArmAsm

;------------------------------------------------------------------------------
;
; 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 <P8DriverEntry ; $CnFF: low-order offset to ProDOS entry point
;.endmacro
;-------------------- End of Peripheral Card ROM Space ----------------------
;--------------------- Start of I/O expansion ROM Space --------------------
; Code here starts at $c800
; This code area is absolute and can use absolute addressing
;.ALIGN 256
.ORG $C900 ;note: .ORG does not cause this code to be
; placed at C800. The ".RES" statement above
; offsets this code
.IF 0 ;SMARTPORT
;------------------------------------------------------------------------------
;
; NO SMARTPORT DRIVER BUILT INTO ROM
;
.ENDIF
;------------------------------------------------------------------------------
; P8AuxROM - Handle a ProDOS call
;
P8AuxROM:
; If the ProDOS unit number doesn't match our slot number, add 2 to
; the drive number.
;
; This actually happens: If we're in slot 5, we get calls for slot 2
; that want to access our 3rd and 4th partitions.
txa ; A = $n0
eor pdUnitNumber
and #$70 ;EOR the slot number
beq OurSlot
lda #2 ;Point to drive 3 or 4
OurSlot:
bit pdUnitNumber
bpl DriveOne
inc a
DriveOne:
lda #1 ;forcing 1
sta DriveNumber,y
lda #0
sta DrvMiscFlags,y ;no special flags (such as raw block access)
jsr ResetDriveIfFirstTime
; process the command code and jump to appropriate routine.
.IF DEBUG
;warning this debug code trashes the Acc register
jsr DSString
.byte "P8:",0
jsr DSCRLF
.ENDIF
lda pdCommandCode
cmp #PRODOS_READ
bne chk1
jsr CheckDevice
bcs chk4
jmp ReadBlock
chk1:
cmp #PRODOS_WRITE
bne chk2
jmp WriteBlock
chk2:
cmp #PRODOS_STATUS
bne chk3
jmp GetStatus
chk3:
cmp #PRODOS_FORMAT
bne chk4
jmp AMIGOFormat
chk4:
; An invalid request # has been sent to this driver.
;
.IF DEBUG
pha
jsr DSString
.byte "CE",0
pla
jsr DSByteCRLF
.ENDIF
lda #PRODOS_IO_ERROR
sec
bit $ff58
rts ;return to caller. Should always be ProDOS
;------------------------------------------------------------------------------
; ResetDriveIfFirstTime - Reset the drive once, the first time the driver
; is called ide_devctrl Bit 2 = Software Reset, Bit 1 = nIEN (enable
; assertion of INTRQ)
;
; Input:
; X = requested slot number in form $n0 where n = slot 1 to 7
; Y = $0n (n = slot#) for accessing scratchpad RAM;
;
; ZeroPage Usage:
; None
;
; CPU Registers changed: A, P
;
ResetDriveIfFirstTime:
lda DriveResetDone,Y
cmp #INITDONESIG
beq resetOK
.IF DEBUG
jsr DSCRLF
.ENDIF
;
; 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
;
; Identify the device
;
jsr HPIBId
lda #INITDONESIG
sta DriveResetDone,Y ;Set the init done flag so init only happens
; once.
resetOK:
rts
;------------------------------------------------------------------------------
; GetStatus - Called by ProDOS and SmartPort to get device status and size
;
; Input:
; DriveNumber,y (0 to 3)
; X = slot number in form $n0 where n = slot 1 to 7
; Y = $0n (n = slot#) for accessing scratchpad RAM;
;
; Output:
; A = ProDOS status return code
; X = drive size LSB
; Y = drive size MSB
; Carry flag: 0 = Okay, 1 = Error
; DrvBlkCount0..DrvBlkCount2 = usable blocks on device
;
GetStatus:
.IF DEBUG
;warning this debug code trashes the Acc register
jsr DSString
.byte " St",0
.ENDIF
;
; Determine if a drive/device is present.
;
;jsr CheckDevice ;FIXME
;bcc sDriveOK
bra sDriveOK
ldx #$00
ldy #$00
lda #PRODOS_OFFLINE
sec
jmp sExit
; Device is present
sDriveOK:
; lda #0
; sta ATADataHigh,x ;clear high byte transfer latch
; jsr IDEWaitReady
; lda #ATAIdentify
; sta ATACommand,x ;Issue the read command to the drive
; jsr IDEWaitReady ;Wait for BUSY flag to go away
; lda ATAStatus,x ;Check for error response
; and #$09
; cmp #$01 ;if DRQ=0 and ERR=1 an error occured
; bne sValidATACommand
bra sValidATACommand
.IF 0;DEBUG
;warning this debug code trashes the Acc register
jsr DSString
.byte " IdEr:",0
lda ATAError,x
jsr DSByteCRLF
.ENDIF
; ldx #0
; ldy #0
; lda #PRODOS_IO_ERROR
; sec
; jmp sExit ; Command Error occured, return error
sValidATACommand:
; phy ;save Y, it holds the $0n sratchpad RAM offset
; ldy #$00 ;zero loop counter
sPrefetchloop:
; jsr IDEWaitReady ;See if a word is ready
; bcs sWordRdy
; ply
; lda #PRODOS_IO_ERROR
; jmp sExit
sWordRdy:
; lda ATADataLow,x ;Read words 0 thru 56 but throw them away
; iny
; cpy #57 ;Number of the last word you want to throw away
; bne sPrefetchloop
; ply
sPrefetchDone:
; FIXME: Need identify here
; lda ATADataLow,x ;Read the current capacity in sectors (LBA)
; sec
; sbc #BLOCKOFFSET
lda #$96
sta DrvBlkCount0,y
; lda ATADataHigh,x
; sbc #0
lda #4
sta DrvBlkCount1,y
; lda ATADataLow,x
; sbc #0
lda #0
sta DrvBlkCount2,y
.IF DEBUG
jsr DSString
.byte "Sz:",0
; lda ATADataHigh,x ;get the high byte of high word just for display
; jsr DSByte
lda DrvBlkCount2,y
jsr DSByte
lda DrvBlkCount1,y
jsr DSByte
lda DrvBlkCount0,y
jsr DSByte
jsr DSBlank
.ENDIF
lda DrvBlkCount2,y
cmp #PARTITIONS32MB ;max out at (#PARTITIONS32MB * $10000 + 00FFFF)
; blocks
; bcs maxOutAtN
; lda ATADataHigh,x
; beq lessThan8GB
bra lessThan8GB
maxOutAtN:
lda #$FF ;The device is truly huge! Just set our 3-byte
; block count to $03FFFF
sta DrvBlkCount0,y
sta DrvBlkCount1,y
lda #PARTITIONS32MB-1 ;Number of 32MB devices, set by the equate:
; #PARTITIONS32MB
sta DrvBlkCount2,y
lessThan8GB:
PostFetch:
; jsr IDEWaitReady ;read the rest of the words, until command ends
; bcc sReadComplete
; lda ATADataLow,x
; bra PostFetch
sReadComplete:
; DrvBlkCount2 is the number of 32 MB partitions availiable - 1,
; or the highest drive # supported (zero based).
;
; If DrvBlkCount2 > 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 *