apple2pi/client/BUILD/PIDRIVE.TEXT#040000

1 line
23 KiB
Plaintext
Executable File

; PiDrive - Based on VSDRIVE by David Schmidt
; 1.0 - Initial release
.TITLE "Apple /// PiDrive Driver"
.PROC PIDRIVE
DriverVersion .EQU 1000 ; Version number
DriverMfgr .EQU 4453 ; Driver Manufacturer - DS
;
; SOS Equates
;
ExtPG .EQU 1401 ; Driver extended bank address offset
AllocSIR .EQU 1913 ; Allocate system internal resource
SysErr .EQU 1928 ; Report error to system
EReg .EQU 0FFDF ; Environment register
ReqCode .EQU 0C0 ; Request code
SOS_Unit .EQU 0C1 ; Unit number
SosBuf .EQU 0C2 ; SOS buffer pointer (2 bytes)
ReqCnt .EQU 0C4 ; Requested byte count
CtlStat .EQU 0C2 ; Control/status code
CSList .EQU 0C3 ; Control/status list pointer
SosBlk .EQU 0C6 ; Starting block number
QtyRead .EQU 0C8 ; Pointer to bytes read returned by D_READ
;
; Our temps in zero page
;
Count .EQU 0CE ; 2 bytes
Timer .EQU 0D0 ; 2 bytes
NumBlks .EQU 0D2 ; 2 bytes lb,hb
DataBuf .EQU 0D4 ; 2 bytes
CmdAck .EQU 0D6 ; 1 byte
;
; Communications hardware constants
;
ACIADR .EQU 0C088 ; ACIA Data register
ACIASR .EQU 0C089 ; ACIA Status register
ACIACMD .EQU 0C08A ; ACIA Command mode register
ACIACTL .EQU 0C08B ; ACIA Control register
;
; SOS Error Codes
;
XDNFERR .EQU 010 ; Device not found
XBADDNUM .EQU 011 ; Invalid device number
XREQCODE .EQU 020 ; Invalid request code
XCTLCODE .EQU 021 ; Invalid control/status code
XCTLPARAM .EQU 022 ; Invalid control/status parameter
XNORESRC .EQU 025 ; Resources not available
XBADOP .EQU 026 ; Invalid operation
XIOERROR .EQU 027 ; I/O error
XNODRIVE .EQU 028 ; Drive not connected
XBYTECNT .EQU 02C ; Byte count not a multiple of 512
XBLKNUM .EQU 02D ; Block number to large
XDISKSW .EQU 02E ; Disk switched
XNORESET .EQU 033 ; Device reset failed
;
; Switch Macro
;
.MACRO switch
.IF "%1" <> "" ; If parameter 1 is present
LDA %1 ; Load A with switch index
.ENDC
CMP #%2+1 ; Do bounds check
BCS $010
ASL A
TAY
LDA %3+1,Y ; Get switch index from table
PHA
LDA %3,Y
PHA
.IF "%4" <> "*" ; If parameter 4 omitted,
RTS ; then go to code
.ENDC
$010 .ENDM
;
; Enter Critical Section
;
.MACRO EnterCritSec
PHA
LDA EReg
ORA #080 ; Set 1MHz switch
STA EReg
PLA
PHP ; Disable interrupts
SEI
.ENDM
;
; Leave Critical Section
;
.MACRO LeaveCritSec
PHA
LDA EReg
AND #07F
STA EReg ; Whatever it was - set it back
PLA
PLP ; Restore interrupt state
.ENDM
;
; Comment Field of driver
;
.WORD 0FFFF ; Signal that we have a comment
.WORD 46. ; Length of comment field... entered manually.
; The Pascal Assembler can't count forward references.
; SCP only shows 78 characters' worth of information.
.ASCII "Apple /// PiDrive Driver by"
.ASCII " David Schmenk 2014"
; 1 2 3 4
; 1234567890123456789012345678901234567890
;------------------------------------
;
; Device identification Block (DIB) - PIDRIVE
;
;------------------------------------
DIB_0 .WORD DIB_1 ; Link pointer
.WORD Entry ; Entry pointer
.BYTE 008 ; Name length byte
.ASCII ".PIDRIVE "; Device name
.BYTE 080 ; Active, no page alignment
PiSlot .BYTE 001 ; Slot number
.BYTE 000 ; Unit number
.BYTE 0E1 ; Type
.BYTE 010 ; Subtype
.BYTE 000 ; Filler
DIB0_Blks .WORD 0000 ; # Blocks in device
.WORD DriverMfgr ; Manufacturer
.WORD DriverVersion ; Driver version
.WORD 0000 ; DCB length followed by DCB
;------------------------------------
;
; Device identification Block (DIB) - VSDRIVE2
;
;------------------------------------
DIB_1 .WORD 0000 ; Link pointer
.WORD Entry ; Entry pointer
.BYTE 009 ; Name length byte
.ASCII ".PIDRIVE2 "; Device name
.BYTE 080 ; Active, no page alignment
.BYTE 001 ; Slot number
.BYTE 001 ; Unit number
.BYTE 0E1 ; Type
.BYTE 010 ; Subtype
.BYTE 000 ; Filler
DIB1_Blks .WORD 0000 ; # Blocks in device
.WORD DriverMfgr ; Manufacturer
.WORD DriverVersion ; Driver version
.WORD 0000 ; DCB length followed by DCB
;------------------------------------
;
; Local storage locations
;
;------------------------------------
LastOP .BLOCK 002, 0FF ; Last operation for D_REPEAT calls
SIRAddr .WORD SIRTbl
SIRTbl .BYTE 010 ; Slot # resource
.BLOCK 004, 000 ; No interrupt handling
SIRLen .EQU *-SIRTbl
RdBlkProc .WORD 0000
WrBlkProc .WORD 0000
StackPtr .BYTE 000
DCB_Idx .BYTE 000 ; DCB 0's blocks
.BYTE DIB1_Blks-DIB0_Blks ; DCB 1's blocks
;------------------------------------
;
; Driver request handlers
;
;------------------------------------
Entry JSR Dispatch ; Call the dispatcher
LDX SOS_Unit ; Get drive number for this unit
LDA ReqCode ; Keep request around for D_REPEAT
STA LastOP,X ; Keep track of last operation
LDA #000
RTS
;
; The Dispatcher. Note that if we came in on a D_INIT call,
; we do a branch to Dispatch normally.
; Dispatch is called as a subroutine!
;
DoTable .WORD DRead-1 ; 0 Read request
.WORD DWrite-1 ; 1 Write request
.WORD DStatus-1 ; 2 Status request
.WORD DControl-1 ; 3 Control request
.WORD BadReq-1 ; 4 Unused
.WORD BadReq-1 ; 5 Unused
.WORD BadOp-1 ; 6 Open - valid for character devices
.WORD BadOp-1 ; 7 Close - valid for character devices
.WORD DInit-1 ; 8 Init request
.WORD DRepeat-1 ; 9 Repeat last read or write request
Dispatch SWITCH ReqCode,9,DoTable ; Serve the request
;
; Dispatch errors
;
BadReq LDA #XREQCODE ; Bad request code!
JSR SysErr ; Return to SOS with error in A
BadOp LDA #XBADOP ; Invalid operation!
JSR SysErr ; Return to SOS with error in A
;
; D_REPEAT - repeat the last D_READ or D_WRITE call
;
DRepeat LDX SOS_Unit
LDA LastOP,X ; Recall the last thing we did
CMP #002 ; Looking for operation < 2
BCS BadOp ; Can only repeat a read or write
STA ReqCode
JMP Dispatch
NoDevice LDA #XDNFERR ; Device not found
JSR SysErr ; Return to SOS with error in A
;
; ACIA addresses for slot # relocation
;
RelocTbl .WORD Reloc1
.WORD Reloc2
.WORD Reloc3
.WORD Reloc4
.WORD Reloc5
.WORD Reloc6
.WORD Reloc7
;
; D_INIT call processing - called at initialization
;
DInit LDA SOS_Unit ; Check if we're initting the zeroeth unit
BNE DInitDone ; If not - skip the serial setup
LDY #001
STY DataBuf+ExtPG
LDX #14.-1
RelocLoop LDA RelocTbl,X
STA DataBuf+1
DEX
LDA RelocTbl,X
STA DataBuf
LDA PiSlot
ASL A
ASL A
ASL A
ASL A
ORA (DataBuf),Y
STA (DataBuf),Y
DEX
BPL RelocLoop
LDA PiSlot
ORA #010
STA SIRTbl
LDA #SIRLen
LDX SIRAddr
LDY SIRAddr+1
JSR AllocSIR ; Allocate the ACIA
BCS NoACIA
CReset EnterCritSec ; Set up the communications environment
LDA #00B ; No parity, no interrupts
Reloc1 STA ACIACMD ; Store via ACIA command register
LDA #010 ; $16=300, $1e=9600, $1f=19200, $10=115k
Reloc2 STA ACIACTL ; Store via ACIA control register
Reloc3 LDA ACIASR ; Clear any prior ACIA interrupts
LDA #080
JSR PutByte ; Init connection to Pi
JSR GetByte ; Check ACK
LeaveCritSec ; Re-enable system state
CMP #081
BNE NoACIA
DInitDone CLC
RTS
NoACIA LDA #XNORESRC
JSR SysErr ; Return to SOS with error in A
;
; D_CONTROL call processing
; $00 = Reset device
; $FE = Perform media formatting
;
DControl LDA CtlStat ; Control command
BEQ CReset
LDA #XCTLCODE ; Control/status code no good
JSR SysErr ; Return to SOS with error in A
;
; D_STATUS call processing
; $00 = Driver Status
; $01 = Report drive size
; $FE = Return preferred bitmap location ($FFFF)
;
DStatus LDA CtlStat ; Which status code to run?
BNE DS0
LDY #000 ; 000 - Driver status, return zero
STA (CSList),Y
CLC
RTS
DS0 CMP #0FE
BNE DSWhat
LDY #000 ; Return preferred bit map locations.
LDA #0FF ; We return FFFF, don't care
STA (CSList),Y
INY
STA (CSList),Y
CLC
RTS
DSWhat LDA #XCTLCODE ; Control/status code no good
JSR SysErr ; Return to SOS with error in A
;
; D_READ call processing
;
DRead TSX
STX StackPtr ; Hang on to the stack pointer for hasty exits
JSR CkCnt ; Checks for validity, aborts if not
JSR CkUnit ; Checks for unit below unit max
LDA #000 ; Zero # bytes read
STA Count ; Local count of bytes read
STA Count+1
TAY
STA (QtyRead),Y ; Userland count of bytes read
INY
STA (QtyRead),Y ; Msb of userland bytes read
LDA NumBlks ; Check for NumBlks greater than zero
ORA NumBlks+1
BEQ RdDone
LDA SosBuf ; Copy out buffer pointers
STA DataBuf
LDA SosBuf+1
STA DataBuf+1
LDA SosBuf+ExtPG
STA DataBuf+ExtPG
JSR FixUp ; Correct for addressing anomalies
RdBlk JSR ReadBlock ; Transfer a block to/from the disk
DEC NumBlks ; Did we get what was asked for?
BNE RdMore
DEC NumBlks+1
BPL RdMore
LDA SosBlk
CMP #002 ; Is this block #2 (lsb=2)?
BNE RdDone
LDA SosBlk+1
BNE RdDone ; Is this block #2 (msb=0)?
LDY #029 ; Yes - store off the disk size
LDA (SosBuf),Y
PHA
INY
LDA (SosBuf),Y
PHA
LDX SOS_Unit ; Get the stats on this unit
LDY DCB_Idx,X
PLA
STA DIB0_Blks+1,Y
PLA
STA DIB0_Blks,Y
RdDone CLC
RTS
RdMore INC SosBlk
BNE RdBlk
INC SosBlk+1
JMP RdBlk
LDY #000
LDA Count ; Local count of bytes read
STA (QtyRead),y ; Update # of bytes actually read
INY
LDA Count+1
STA (QtyRead),y
BCS IOError ; An error occurred
ReadExit RTS ; Exit read routines
IOError LDA #XIOERROR ; I/O error
JSR SysErr ; Return to SOS with error in A
;
; D_WRITE call processing
;
DWrite TSX
STX StackPtr ; Hang on to the stack pointer for hasty exits
JSR CkCnt ; Checks for validity
JSR CkUnit ; Checks for unit below unit max
LDA NumBlks ; Check for NumBlks greater than zero
ORA NumBlks+1
BEQ WrDone ; Quantity to write is zero - so done
LDA SosBuf ; Copy out buffer pointers
STA DataBuf
LDA SosBuf+1
STA DataBuf+1
LDA SosBuf+ExtPG
STA DataBuf+ExtPG
JSR FixUp
WrBlk JSR WriteBlock
DEC NumBlks ; Did we put what was asked for?
BNE WrMore ; Not done yet... go around again
DEC NumBlks+1 ; (16 bit decrement)
BPL WrMore ; Not done yet... go around again
WrDone CLC
RTS ; We're done
WrMore INC SosBlk
BNE WrBlk
INC SosBlk+1
JMP WrBlk
;------------------------------------
;
; Utility routines
;
;------------------------------------
;
; Check ReqCnt to ensure it's a multiple of 512.
;
CkCnt LDA ReqCnt ; Look at the lsb of bytes requested
BNE $1 ; No good! lsb should be 00
STA NumBlks+1 ; Zero out the high byte of blocks
LDA ReqCnt+1 ; Look at the msb
LSR A ; Put bottom bit into carry, 0 into top
STA NumBlks ; Convert bytes to number of blks to xfer
BCC CvtBlk ; Carry is set from LSR to mark error.
$1 LDA #XBYTECNT
JSR SysErr ; Return to SOS with error in A
;
; Test for valid block number; abort on error
;
CvtBlk LDX SOS_Unit
LDY DCB_Idx,X
SEC
LDA DIB0_Blks+1,Y ; Blocks on unit msb
SBC SosBlk+1 ; User requested block number msb
BVS BlkErr ; Not enough blocks on device for request
BEQ $1 ; Equal msb; check lsb.
RTS ; Greater msb; we're ok.
$1 LDA DIB0_Blks,Y ; Blocks on unit lsb
SBC SosBlk ; User requested block number lsb
BVS BlkErr ; Not enough blocks on device for request
RTS ; Equal or greater msb; we're ok.
BlkErr LDA #XBLKNUM
JSR SysErr ; Return to SOS with erorr in A
IncrAdr INC Count+1 ; Increment byte count MSB
INC DataBuf+1 ; Increment DataBuf MSB in userland
;
; Fix up the buffer pointer to correct for addressing
; anomalies. We just need to do the initial checking
; for two cases:
; 00xx bank N -> 80xx bank N-1
; 20xx bank 8F if N was 0
; FDxx bank N -> 7Dxx bank N+1
; If pointer is adjusted, return with carry set
;
FixUp LDA DataBuf+1 ; Look at msb
BEQ $1 ; That's one!
CMP #0FD ; Is it the other one?
BCS $2 ; Yep. fix it!
RTS ; Pointer unchanged, return carry clear.
$1 LDA #080 ; 00xx -> 80xx
STA DataBuf+1
DEC DataBuf+ExtPG ; Bank N -> band N-1
LDA DataBuf+ExtPG ; See if it was bank 0
CMP #07F ; (80) before the DEC.
BNE $3 ; Nope! all fixed.
LDA #020 ; If it was, change both
STA DataBuf+1 ; Msb of address and
LDA #08F
STA DataBuf+ExtPG ; Bank number for bank 8F
RTS ; Return carry set
$2 AND #07F ; Strip off high bit
STA DataBuf+1 ; FDxx ->7Dxx
INC DataBuf+ExtPG ; Bank N -> bank N+1
$3 RTS ; Return carry set
CkUnit CLC
RTS
;
; CmdSend = Send request and block #
;
CmdSend STA CmdAck
JSR PutByte
LDA SosBlk
JSR PutByte
LDA SosBlk+1
JSR PutByte
INC CmdAck
JSR GetByte
CMP CmdAck
BNE RWFail
RTS
;
; RWFail - Complain with an OS I/O error
;
RWFail LeaveCritSec
LDX StackPtr
TXS ; Pop! Goes the stack pointer
LDA #XIOERROR ; Nearby branch point
JSR SysErr ; Return to SOS with error in A
;
; ReadBlock - Read requested blocks from device into memory
;
ReadBlock EnterCritSec
LDA SOS_Unit
ASL A
ORA #0A4 ; Read command
JSR CmdSend
LDY #000
LDX #002 ; Two pages per block
$1 JSR GetByte
BCS RWFail
STA (DataBuf),Y
INY
BNE $1
JSR IncrAdr
DEX
BNE $1
JSR GetByte ; Get status
BNE RWFail
LeaveCritSec
RTS
;
; WriteBlock - write memory out to requested blocks
;
WriteBlock EnterCritSec
LDA SOS_Unit
ASL A
ORA #0A8 ; Write command
JSR CmdSend
LDY #000
LDX #002 ; Two pages per block
$1 LDA (DataBuf),Y
JSR PutByte
INY
BNE $1
JSR IncrAdr
DEX
BNE $1
JSR GetByte ; Get status
BNE RWFail
LeaveCritSec
RTS
;
; GetByte - Get a byte from the ACIA
;
; Carry set on timeout, clear on data (returned in Accumulator)
;
GetByte LDA #000
STA Timer
STA Timer+1
Reloc4
GetByte1 LDA ACIASR ; Check status bits via ACIA status register
AND #008
BNE GETIT ; Data is ready, go get it
INC TIMER
BNE GetByte1 ; Input register empty, no timeout; loop
INC TIMER+1
BNE GetByte1 ; Input register empty, no timeout; loop
SEC ; Timeout; return to caller
RTS
Reloc5
GETIT LDA ACIADR ; Get character via ACIA data register
CLC
RTS
;
; PutByte - Put a byte to the ACIA
;
PutByte PHA ; Push 'character to send' onto the stack
Reloc6
PutByte1 LDA ACIASR ; Check status bits
AND #010
BEQ PutByte1 ; Output register is full, no timeout; so loop
PLA ; Pull 'character to send' back off the stack
Reloc7 STA ACIADR ; Put character
RTS
.END