2533 lines
48 KiB
ArmAsm
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 *
|