747 lines
25 KiB
ArmAsm
747 lines
25 KiB
ArmAsm
; VSDrive
|
|
|
|
; 0.01A - Initial release
|
|
; 0.02B - Fill in block sizes, implement reset control action
|
|
; 1.26 - More bounds checking for block writes, remove underscrores from
|
|
; names, clean up module name, synchronize version number with ADTPro release
|
|
; 1.28 - Use newer time-transporting protocol, add second drive as .VSDRIVE2
|
|
; 1.31 - Add null interrupt handler for ACIA
|
|
|
|
.TITLE "Apple /// Virtual Serial Drive Driver"
|
|
.PROC VSDRIVE
|
|
|
|
DriverVersion .EQU 1310 ; 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
|
|
EnvCmd .EQU 0D6 ; 1 byte envelope command
|
|
Checksum .EQU 0D7 ; 1 byte checksum calc
|
|
;
|
|
; Communications hardware constants
|
|
;
|
|
ACIADR .EQU 0c0f0 ; ACIA Data register
|
|
ACIASR .EQU 0c0f1 ; ACIA Status register
|
|
ACIACMD .EQU 0c0f2 ; ACIA Command mode register
|
|
ACIACTL .EQU 0c0f3 ; 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
|
|
|
|
;
|
|
; GoSlow macro - slow down via E-Register
|
|
;
|
|
.MACRO GoSlow
|
|
PHA
|
|
LDA EReg
|
|
ORA #080 ; Set 1MHz switch
|
|
STA EReg
|
|
PLA
|
|
.ENDM
|
|
|
|
;
|
|
; GoFast macro - speed up via E-Register
|
|
;
|
|
.MACRO GoFast
|
|
PHA
|
|
LDA EReg
|
|
AND #07f
|
|
STA EReg ; Whatever it was - set it back
|
|
PLA
|
|
.ENDM
|
|
|
|
;
|
|
; Comment Field of driver
|
|
;
|
|
.WORD 0FFFF ; Signal that we have a comment
|
|
.WORD 66. ; Length of comment field... entered manually.
|
|
; The Pascal Assembler can't count forward references.
|
|
; SCP only shows 78 characters' worth of information.
|
|
.ASCII "Apple /// Virtual Serial Drive Driver by"
|
|
.ASCII " David Schmidt 2012 - 2014"
|
|
; 1 2 3 4
|
|
; 1234567890123456789012345678901234567890
|
|
|
|
;------------------------------------
|
|
;
|
|
; Device identification Block (DIB) - VSDRIVE
|
|
;
|
|
;------------------------------------
|
|
|
|
DIB_0 .WORD DIB_1 ; Link pointer
|
|
.WORD Entry ; Entry pointer
|
|
.BYTE 008 ; Name length byte
|
|
.ASCII ".VSDRIVE "; Device name
|
|
.BYTE 080 ; Active, no page alignment
|
|
.BYTE 000 ; 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 ".VSDRIVE2 "; Device name
|
|
.BYTE 080 ; Active, no page alignment
|
|
.BYTE 000 ; 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 001 ; ACIA resource
|
|
.BYTE 000
|
|
.WORD 0FEC4 ; Do-nothing interrupt vector
|
|
.BYTE 000 ; Bank register
|
|
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
|
|
|
|
;
|
|
; 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
|
|
LDA #SIRLen
|
|
LDX SIRAddr
|
|
LDY SIRAddr+1
|
|
JSR AllocSIR ; Allocate the ACIA
|
|
BCS NoACIA
|
|
|
|
PHP
|
|
SEI ; Disable system interrupts
|
|
GoSlow ; Set up the communications environment
|
|
LDA #00b ; No parity, no interrupts
|
|
STA ACIACMD ; Store via ACIA command register
|
|
LDA #010 ; $16=300, $1e=9600, $1f=19200, $10=115k
|
|
STA ACIACTL ; Store via ACIA control register
|
|
LDA ACIASR ; Clear any prior ACIA interrupts
|
|
GoFast
|
|
PLP ; Re-enable system interrupt state
|
|
|
|
DInitDone
|
|
CLC
|
|
RTS
|
|
NoACIA
|
|
LDA #XNORESRC
|
|
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 ReadExit
|
|
JSR FixUp ; Correct for addressing anomalies
|
|
JSR ReadBlock ; Transfer a block to/from the disk
|
|
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 WriteExit ; Quantity to write is zero - so done
|
|
JSR FixUp
|
|
JSR WriteBlock
|
|
BCS IOError
|
|
WriteExit RTS
|
|
|
|
;
|
|
; 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_CONTROL call processing
|
|
; $00 = Reset device
|
|
; $FE = Perform media formatting
|
|
;
|
|
DControl
|
|
LDA CtlStat ; Control command
|
|
BEQ CReset
|
|
JMP DCWhat ; Control code no good!
|
|
CReset GoSlow
|
|
BIT ACIADR ; Clear ACIA Data register
|
|
GoFast
|
|
DCDone RTS
|
|
DCNoReset LDA #XNORESET ; Things went bad after reset
|
|
JSR SysErr ; Return to SOS with error in A
|
|
DCWhat LDA #XCTLCODE ; Control/status code no good
|
|
JSR SysErr ; Return to SOS with error in A
|
|
|
|
;------------------------------------
|
|
;
|
|
; Utility routines
|
|
;
|
|
;------------------------------------
|
|
|
|
;
|
|
; ReadBlock - Read requested blocks from device into memory
|
|
;
|
|
ReadBlock
|
|
LDA SosBuf ; Copy out buffer pointers
|
|
STA DataBuf
|
|
LDA SosBuf+1
|
|
STA DataBuf+1
|
|
LDA SosBuf+ExtPG
|
|
STA DataBuf+ExtPG
|
|
|
|
LDA #003 ; Read request with current time information
|
|
CLC
|
|
ADC SOS_Unit ; Add two to the request if this is the second unit
|
|
ADC SOS_Unit
|
|
STA EnvCmd
|
|
ReadSend
|
|
JSR SendEnvelope
|
|
JSR DTReceiveEnvelope
|
|
BCS ReadFail
|
|
|
|
LDY #000
|
|
STY Checksum
|
|
RdBlk2 JSR GETC
|
|
BCS ReadFail
|
|
STA (DataBuf),Y
|
|
EOR Checksum
|
|
STA Checksum
|
|
INY
|
|
BNE RdBlk2
|
|
JSR IncrAdr
|
|
RdBlk3 JSR GETC
|
|
BCS ReadFail
|
|
STA (DataBuf),Y
|
|
EOR Checksum
|
|
STA Checksum
|
|
INY
|
|
BNE RdBlk3
|
|
JSR IncrAdr
|
|
JSR GETC ; Pull Checksum
|
|
BCS ReadFail
|
|
CMP Checksum
|
|
BNE ReadFail
|
|
|
|
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 ReadSend
|
|
INC SosBlk+1
|
|
JMP ReadSend
|
|
|
|
;
|
|
; ReadFail - Complain with an OS I/O error
|
|
;
|
|
ReadFail
|
|
LDX StackPtr
|
|
TXS ; Pop! Goes the stack pointer
|
|
LDA #XIOERROR ; Nearby branch point
|
|
JSR SysErr ; Return to SOS with error in A
|
|
|
|
;
|
|
; WriteBlock - write memory out to requested blocks
|
|
;
|
|
WriteBlock
|
|
LDA SosBuf ; Copy out buffer pointers
|
|
STA DataBuf
|
|
LDA SosBuf+1
|
|
STA DataBuf+1
|
|
LDA SosBuf+ExtPG
|
|
STA DataBuf+ExtPG
|
|
|
|
LDA #002 ; Write request
|
|
CLC
|
|
ADC SOS_Unit ; Add two to the request if this is the second unit
|
|
ADC SOS_Unit
|
|
STA EnvCmd
|
|
WriteSend
|
|
JSR SendEnvelope
|
|
LDX #000
|
|
STX Checksum
|
|
WrBkLoop
|
|
LDY #000
|
|
WRLOOP:
|
|
LDA (DataBuf),Y
|
|
JSR PUTCC
|
|
INY
|
|
BNE WRLOOP
|
|
|
|
JSR IncrAdr
|
|
INX
|
|
CPX #002
|
|
BNE WRBKLOOP
|
|
|
|
LDA Checksum ; Checksum
|
|
JSR PUTC
|
|
|
|
JSR ReceiveEnvelope
|
|
BCS WriteFail
|
|
|
|
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
|
|
CLC
|
|
RTS ; We're done
|
|
|
|
WrMore INC SosBlk
|
|
BNE WriteSend
|
|
INC SosBlk+1
|
|
JMP WriteSend
|
|
|
|
;
|
|
; WriteFail - Complain with an OS I/O error
|
|
;
|
|
WriteFail
|
|
LDX StackPtr
|
|
TXS ; Pop! Goes the stack pointer
|
|
LDA #XIOERROR
|
|
JSR SysErr ; Return to SOS with error in A
|
|
|
|
;
|
|
; SendEnvelope - send the command envelope
|
|
;
|
|
SendEnvelope ; Send a command envelope
|
|
LDA #000
|
|
STA Checksum
|
|
LDA #0c5 ; "E"
|
|
JSR PUTCC ; Envelope
|
|
LDA EnvCmd
|
|
JSR PUTCC ; Send command
|
|
LDA SosBlk
|
|
JSR PUTCC ; Send LSB of requested block
|
|
LDA SosBlk+1
|
|
JSR PUTCC ; Send MSB of requested block
|
|
LDA Checksum
|
|
JSR PUTC ; Send envelope Checksum
|
|
RTS ; Carry is clear, return
|
|
|
|
;
|
|
; DTReceiveEnvelope - receive the command envelope back from host, with time data
|
|
;
|
|
; Note that we can't set the date and time through a SOS call, since device drivers
|
|
; are not allowed to make SOS calls.
|
|
;
|
|
DTReceiveEnvelope
|
|
LDA #000
|
|
STA Checksum
|
|
JSR GETC
|
|
BCS DTEnvelopeFail
|
|
CMP #0c5 ; "E" - S/B Command envelope
|
|
BNE DTEnvelopeFail
|
|
EOR Checksum
|
|
STA Checksum
|
|
JSR GETC
|
|
BCS DTEnvelopeFail
|
|
CMP EnvCmd ; Command requested
|
|
BNE DTEnvelopeFail
|
|
EOR Checksum
|
|
STA Checksum
|
|
JSR GETC ; Read LSB of requested block
|
|
BCS DTEnvelopeFail
|
|
CMP SosBlk
|
|
BNE DTEnvelopeFail
|
|
EOR Checksum
|
|
STA Checksum
|
|
JSR GETC ; Read MSB of requested block
|
|
BCS DTEnvelopeFail
|
|
CMP SosBlk+1
|
|
BNE DTEnvelopeFail
|
|
EOR Checksum
|
|
STA Checksum
|
|
LDX #004 ; Pull the four date/time bytes
|
|
DTRETime JSR GETC ; Ignore except for checksum calculations
|
|
BCS DTEnvelopeFail
|
|
EOR Checksum
|
|
STA Checksum
|
|
DEX
|
|
BNE DTRETime
|
|
JSR GETC ; Checksum
|
|
BCS DTEnvelopeFail
|
|
CMP Checksum
|
|
BNE DTEnvelopeFail
|
|
LDA #000
|
|
CLC
|
|
RTS
|
|
DTEnvelopeFail
|
|
SEC
|
|
RTS
|
|
|
|
;
|
|
; ReceiveEnvelope - receive the command envelope back from host
|
|
;
|
|
ReceiveEnvelope
|
|
JSR GETC
|
|
BCS EnvelopeFail
|
|
CMP #0c5 ; "E" - S/B Command envelope
|
|
BNE EnvelopeFail
|
|
JSR GETC
|
|
BCS EnvelopeFail
|
|
CMP EnvCmd ; Command requested
|
|
BNE EnvelopeFail
|
|
JSR GETC ; Read LSB of requested block
|
|
BCS EnvelopeFail
|
|
CMP SosBlk
|
|
BNE EnvelopeFail
|
|
JSR GETC ; Read MSB of requested block
|
|
BCS EnvelopeFail
|
|
CMP SosBlk+1
|
|
BNE EnvelopeFail
|
|
JSR GETC ; Checksum
|
|
BCS EnvelopeFail
|
|
CMP Checksum
|
|
BNE EnvelopeFail
|
|
LDA #000
|
|
CLC
|
|
RTS
|
|
EnvelopeFail
|
|
SEC
|
|
RTS
|
|
|
|
;
|
|
; CalcChecksum - Calculate the checksum of the block at DataBuf
|
|
;
|
|
CalcChecksum ; Calculate the checksum
|
|
LDA SosBuf ; Copy out buffer pointers again
|
|
STA DataBuf
|
|
LDA SosBuf+1
|
|
STA DataBuf+1
|
|
|
|
LDA #000 ; Clean everyone out
|
|
TAX
|
|
TAY
|
|
CCLoop:
|
|
EOR (DataBuf),Y
|
|
STA Checksum ; Save that tally in CHECKSUM
|
|
INY
|
|
BNE CCLoop
|
|
JSR IncrAdr ; Y just wrapped; bump buffer MSB
|
|
INX ; Need two loops
|
|
CPX #002 ; Second loop?
|
|
BNE CCLoop
|
|
|
|
RTS
|
|
|
|
;
|
|
; GETC - Get a byte from the ACIA
|
|
;
|
|
; Carry set on timeout, clear on data (returned in Accumulator)
|
|
;
|
|
GETC
|
|
LDA #000
|
|
STA Timer
|
|
STA Timer+1
|
|
GoSlow
|
|
GETC1 LDA ACIASR ; Check status bits via ACIA status register
|
|
AND #068
|
|
CMP #008
|
|
BEQ GETIT ; Data is ready, go get it
|
|
INC TIMER
|
|
BNE GETC1 ; Input register empty, no timeout; loop
|
|
INC TIMER+1
|
|
BNE GETC1 ; Input register empty, no timeout; loop
|
|
GoFast
|
|
SEC ; Timeout; return to caller
|
|
RTS
|
|
GETIT
|
|
LDA ACIADR ; Get character via ACIA data register
|
|
GoFast
|
|
CLC
|
|
RTS
|
|
|
|
;
|
|
; PUTCC - Put a byte to the ACIA, adding to the checksum
|
|
;
|
|
PUTCC PHA
|
|
EOR Checksum
|
|
STA Checksum
|
|
JMP PUTC0
|
|
;
|
|
; PUTC - Put a byte to the ACIA
|
|
;
|
|
PUTC
|
|
PHA ; Push 'character to send' onto the stack
|
|
PUTC0 LDA #000
|
|
STA Timer
|
|
STA Timer+1
|
|
GoSlow
|
|
PUTC1
|
|
LDA ACIASR ; Check status bits
|
|
AND #070
|
|
CMP #010
|
|
BNE PUTC1 ; Output register is full, no timeout; so loop
|
|
PLA ; Pull 'character to send' back off the stack
|
|
STA ACIADR ; Put character
|
|
GoFast
|
|
RTS
|
|
|
|
;
|
|
; 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
|
|
|
|
.END
|