mirror of
https://github.com/robjustice/Apple3.git
synced 2024-06-08 00:29:30 +00:00
559 lines
19 KiB
ArmAsm
559 lines
19 KiB
ArmAsm
; Modified problock driver to be used as a virtual floppy disk
|
|
; The loader needs to setup $3000 to hold the two index blocks for the
|
|
; image file and then pretends to be a 280 block floppy
|
|
;
|
|
; Not relocatable, hard coded to be at bank 4, at $3600 (256k required)
|
|
;
|
|
;
|
|
; By Robert Justice
|
|
;
|
|
; .TITLE "Apple /// Virtual Floppy Image Driver"
|
|
|
|
.segment "RAM"
|
|
.setcpu "6502"
|
|
|
|
.export VDrvLAddr
|
|
.export VDrvSize
|
|
.export DInit
|
|
.export BlockioStb ; entry point for blockio stub
|
|
.export StartStub
|
|
.export EndStub
|
|
.export StubLen
|
|
|
|
|
|
|
|
VDrvLAddr = $3600 ; org for this code
|
|
VDrvSize = EndCode - StartCode
|
|
|
|
IndxBlk1 = VDrvLAddr - $600 ; Index block 1
|
|
IndxBlk2 = VDrvLAddr - $400 ; Index block 2
|
|
Buffer = VDrvLAddr - $200 ; Buffer for card block read/writes
|
|
|
|
; this needs to be in an unused bit of non banked memory
|
|
BlockioStb = $1940 ; entry point for blockio stub
|
|
BankSave = BlockioStb + (Bank_Tmp -StartStub)
|
|
SectioStb = BlockioStb + (SectIOT - StartStub) ; entry point for sectio stub
|
|
StubLen = EndStub - StartStub
|
|
|
|
|
|
IBBUFP = $85
|
|
IBCMD = $87
|
|
BUF = $9b ; monitor diskio routines buffer
|
|
|
|
;
|
|
; SOS Equates
|
|
;
|
|
ExtPG = $1401 ; Driver extended bank address offset
|
|
EReg = $FFDF ; Environment register
|
|
E_IFR = $FFED ; VIA E Interrupt Flag Register
|
|
E_IER = $FFEE ; VIA E Interrupt Enable Register
|
|
Bank_Reg = $FFEF ; Bank register
|
|
CWrtOff = $C0DA ; Character loading off
|
|
CWrtOn = $C0DB ; Character loading on
|
|
|
|
XDNFERR = $10 ; Device not found
|
|
|
|
;
|
|
; Extra zero page variables
|
|
;
|
|
ScreenBase = $E5 ; 2 bytes lb,hb for save/restore screenholes
|
|
Pointer = $E7 ; 2 byte pointer for signature check
|
|
|
|
D_TRACK = $16d8 ; for softcard sectorio
|
|
D_SECTOR = $16d9 ; these are the inputs
|
|
D_UNITNUM = $16da
|
|
D_BUFL = $16db
|
|
D_BUFH = $16dc
|
|
D_BUFX = $16dd
|
|
D_COMMAND = $16de
|
|
|
|
OrigDriver = $9cc5 ; Softcard floppy driver entry
|
|
|
|
ScanStart = $04 ; Slot number to start scan from
|
|
|
|
.SEGMENT "RAM"
|
|
.org VDrvLAddr
|
|
StartCode = *
|
|
|
|
jmp BlockIO ; entry point for BLOCKIO - 3600
|
|
jmp SectIO ; entry point for SectorIO - 3603
|
|
Slot: .byte 0 ; slot for card
|
|
EnvSave: .byte 0 ; save current environment state
|
|
OrigBlk: .byte 0 ; temp location for original block num
|
|
; only patch blks <256, so 8bits is enough
|
|
|
|
;------------------------------------
|
|
;
|
|
; Local storage locations
|
|
;
|
|
;------------------------------------
|
|
|
|
TmpScrH: .res $10, $00 ; Storage for screenholes, slot0 & current slot
|
|
TmpZero: .res $30, $00 ; Storage to save zero page locations
|
|
; we set the prodos call data in here and then swap
|
|
; it in before we call the interface
|
|
ProCommand = TmpZero + $22 ; Command - $42
|
|
ProUnit = TmpZero + $23 ; Unit number - $43
|
|
ProBuf = TmpZero + $24 ; buffer pointer - $44
|
|
ProBlock = TmpZero + $26 ; Block number - $46
|
|
|
|
ProBufOff = $44 ; buffer pointer - $44
|
|
|
|
Signature: .byte $FF, $20, $FF, $00 ; Disk card signature for disk controller
|
|
.byte $FF, $03
|
|
|
|
DInit: lda EReg ; save current environment so we can
|
|
sta EnvSave ; restore on exit
|
|
ora #$c0 ; enable Cxxx I/O and set 1MHz, grappler+ softsp needs this
|
|
sta EReg
|
|
lda #ScanStart ; load starting scan slot
|
|
ora #$C0 ; Form a $Cs00 address, where s = slot #
|
|
sta Pointer+1
|
|
lda #$00
|
|
sta Pointer
|
|
sta Pointer+ExtPG ; zero out the xbyte
|
|
|
|
CheckNext: ldy #$05 ; We check all 3 sig bytes, starting from last
|
|
@1: lda (Pointer),y
|
|
cmp Signature,Y
|
|
bne NoMatch ; No device if bytes don't match
|
|
dey
|
|
dey
|
|
bpl @1
|
|
|
|
ldy #$ff ; $CxFF - check last byte
|
|
lda (Pointer),y
|
|
cmp #$00
|
|
beq NoMatch ; if $00, is a Disk II 16 sector device, error
|
|
cmp #$ff
|
|
bne Match ; if its not $ff (Disk II 13 sector device)
|
|
; Then we found an intelligent disk controller :-)
|
|
|
|
NoMatch: dec Pointer+1 ; try next slot
|
|
lda Pointer+1
|
|
and #$07
|
|
bne CheckNext ; Check next slot
|
|
beq NoDevice ; Otherwise we did not find one :-(
|
|
|
|
Match: sta ProDrvAdr+1 ; Set card driver entry low byte
|
|
lda Pointer+1
|
|
sta ProDrvAdr+2 ; Set card driver entry high byte
|
|
and #$07
|
|
sta Slot
|
|
lda EnvSave
|
|
sta EReg
|
|
lda #0
|
|
rts
|
|
|
|
NoDevice: lda EnvSave
|
|
sta EReg
|
|
lda #XDNFERR ; Device not found
|
|
rts
|
|
|
|
;
|
|
; jsr to card firmware driver
|
|
; We update the address based on the slot and firmware CxFF byte
|
|
;
|
|
ProDriver: sei ; disable interrupts while changing things
|
|
jsr SaveMem ; save and swap in card screen hole & zeropage
|
|
jsr GoSlow
|
|
ProDrvAdr: jsr $0000 ; call device entry
|
|
sei ; Keep interrupts off incase card firmware reenabled
|
|
jsr GoFast
|
|
jsr RestMem ; save and swap out card screen hole & zeropage
|
|
lda #$18 ; Clear CB1 & CB2 flags - VBL
|
|
sta E_IFR ; this seems more for mame, its a little different
|
|
cli ; enable interrupts again
|
|
rts
|
|
|
|
;
|
|
; Blockio routine. This replicates the rom routine behaviour
|
|
; x = block number msb
|
|
; a = block number lsb
|
|
; IBCMD = command, 1=read , write?
|
|
; IBBUFP = dest pointer
|
|
;
|
|
; For the Softcard ///, the blockio routine is only used to load in
|
|
; the boot block 0 and then the next 24 blocks with the 6502 IO routines and CPM
|
|
;
|
|
; We'll patch the two blocks to redirect them to our new routines here
|
|
; as we read them.
|
|
;
|
|
; Block 0 - offset $df -> 00 13 (new blockio)
|
|
; Block 3 - offset $1b4 -> 16 13 (new sectio)
|
|
;
|
|
|
|
BlockIO: cpx #0 ; check lk num msb
|
|
beq Low ; block is <256
|
|
tax
|
|
lda IndxBlk2,X
|
|
sta ProBlock
|
|
lda IndxBlk2+256,X
|
|
sta ProBlock+1
|
|
jmp HDone
|
|
|
|
Low: sta OrigBlk ; store for patch check
|
|
tax ; map the block number
|
|
lda IndxBlk1,X
|
|
sta ProBlock ; and store for prodos call
|
|
lda IndxBlk1+256,X
|
|
sta ProBlock+1
|
|
|
|
HDone: lda EReg ; save current environment so we can
|
|
sta EnvSave ; restore on exit
|
|
lda IBCMD
|
|
sta ProCommand
|
|
lda IBBUFP
|
|
sta BUF
|
|
lda IBBUFP+1
|
|
sta BUF+1
|
|
jsr SetProUnit
|
|
jsr SetProBuf
|
|
lda EReg ; enable I/O
|
|
ora #$40
|
|
sta EReg
|
|
jsr ProDriver
|
|
|
|
jsr CheckPatch ; check if we need to patch it
|
|
|
|
ldy #0 ; copy the buffer over
|
|
looppt1: lda Buffer,y ; using extended addressing
|
|
sta (BUF),y ;1940.50.60
|
|
iny
|
|
bne looppt1
|
|
|
|
inc BUF+1
|
|
looppt2: lda Buffer+256,y
|
|
sta (BUF),y
|
|
iny
|
|
bne looppt2
|
|
|
|
dec BUF+1
|
|
|
|
lda EnvSave
|
|
sta EReg
|
|
|
|
lda #0 ; dummy no error
|
|
clc
|
|
|
|
rts
|
|
|
|
; check and patch the block to redirect here
|
|
; and also repatch the blockio and sectio stubs
|
|
; as these are located in memory that get loaded from
|
|
; the floppy
|
|
CheckPatch: lda OrigBlk
|
|
bne nxtchk
|
|
ldy #$df ; offset for blockio jsr address in boot block
|
|
lda #<BlockioStb
|
|
sta Buffer,Y
|
|
iny
|
|
lda #>BlockioStb
|
|
sta Buffer,Y
|
|
bne donep
|
|
|
|
nxtchk: cmp #3
|
|
bne donep
|
|
ldy #$b4 ; offset for sectio jsr address in block 3 (1b4)
|
|
lda #<SectioStb
|
|
sta Buffer+256,y
|
|
iny
|
|
lda #>SectioStb
|
|
sta Buffer+256,y
|
|
|
|
ldy #0 ; also in blk3, we patch in the blockio & sectio stub
|
|
@p1: lda StartStub,y ; as these would get overwritten with this blk read
|
|
sta Buffer+256+$40,y
|
|
iny
|
|
cpy #StubLen
|
|
bne @p1
|
|
|
|
donep: rts
|
|
|
|
;
|
|
; Sectio routine. This replicates the softcard floppy sector routine
|
|
; 256 byte sectors are mapped to the appropriate blocks
|
|
;
|
|
; Inputs
|
|
; D_TRACK .eq $16d8
|
|
; D_SECTOR .eq $16d9
|
|
; D_UNITNUM .eq $16da
|
|
; D_BUFL .eq $16db
|
|
; D_BUFH .eq $16dc
|
|
; D_BUFX .eq $16dd
|
|
; D_COMMAND .eq $16de
|
|
;
|
|
;
|
|
SectMap: .byte 0,4,0,4,1,5,1,5,2,6,2,6,3,7,3,7 ;phys to blk
|
|
SectHalf: .byte 0,0,1,1,0,0,1,1,0,0,1,1,0,0,1,1 ;first 256 or second 256 in block
|
|
|
|
|
|
SectIO: lda EReg ; save current environment reg so we can
|
|
sta EnvSave ; restore on exit
|
|
|
|
lda D_TRACK ; get track number to convert to block num
|
|
asl ; multiply x 8
|
|
asl
|
|
asl
|
|
bcc Lt256 ; blknum < 256
|
|
ldx D_SECTOR ; use sector number as index into map
|
|
clc
|
|
adc SectMap,x
|
|
tax ; then map the block number to image file blocks
|
|
lda IndxBlk2,X
|
|
sta ProBlock ; and store for prodos call
|
|
lda IndxBlk2+256,X
|
|
sta ProBlock+1
|
|
jmp HDone2
|
|
|
|
Lt256: ldx D_SECTOR ; use sector number as index into map
|
|
adc SectMap,x
|
|
tax ; then map the block number to image file blocks
|
|
lda IndxBlk1,X
|
|
sta ProBlock ; and store for prodos call
|
|
lda IndxBlk1+256,X
|
|
sta ProBlock+1
|
|
|
|
HDone2: lda #1 ; read the block regardless of write or read
|
|
sta ProCommand
|
|
jsr SetProUnit
|
|
jsr SetProBuf
|
|
lda EReg ; enable Cxxx I/O
|
|
ora #$40
|
|
sta EReg
|
|
jsr ProDriver ; get block
|
|
bcs DskError
|
|
|
|
lda D_BUFL ; dest pointer lsb
|
|
sta BUF ; rom diskio buf lsb
|
|
lda D_BUFH ; dest pointer msb
|
|
sta BUF+1 ; rom diskio buf msb
|
|
lda D_BUFX ; dest pointer xbyte
|
|
sta $149c ; rom diskio buf xbyte
|
|
|
|
lda D_COMMAND ; now check if its a read/write 1=read, 0=write
|
|
lsr a
|
|
bcc SectWrite ; yes
|
|
|
|
;fall through for read
|
|
ldy #0
|
|
ldx D_SECTOR ; use sector number as index into map
|
|
lda SectHalf,x
|
|
beq firsthr
|
|
|
|
secondhr: lda Buffer+256,y ; copy half block buffer into sector buffer
|
|
sta (BUF),Y
|
|
iny
|
|
bne secondhr
|
|
beq done2
|
|
|
|
firsthr: lda Buffer,y ; copy half block buffer into sector buffer
|
|
sta (BUF),Y
|
|
iny
|
|
bne firsthr
|
|
beq done2
|
|
|
|
SectWrite: ldy #0
|
|
ldx D_SECTOR ; use sector number as index into map
|
|
lda SectHalf,x
|
|
beq firsthw
|
|
|
|
secondhw: lda (BUF),Y ; copy sector into block buffer
|
|
sta Buffer+256,y
|
|
iny
|
|
bne secondhw
|
|
beq cont
|
|
|
|
firsthw: lda (BUF),Y ; copy sector into block buffer
|
|
sta Buffer,y
|
|
iny
|
|
bne firsthw
|
|
|
|
cont: lda #2 ; write
|
|
sta ProCommand
|
|
jsr ProDriver ; write block buffer back
|
|
bcs DskError
|
|
|
|
done2: lda EnvSave
|
|
sta EReg
|
|
|
|
lda #0 ; set no error
|
|
clc
|
|
|
|
rts
|
|
|
|
DskError: lda #$27 ; I/O error?
|
|
sec
|
|
rts
|
|
|
|
|
|
;
|
|
; Throttle back to 1 MHz
|
|
;
|
|
GoSlow: pha
|
|
php
|
|
lda EReg
|
|
ora #$80
|
|
sta EReg
|
|
plp
|
|
pla
|
|
rts
|
|
|
|
;
|
|
; Throttle up to 2 MHz
|
|
;
|
|
GoFast: pha
|
|
php
|
|
lda EReg
|
|
and #$7F
|
|
sta EReg
|
|
plp
|
|
pla
|
|
rts
|
|
|
|
;
|
|
; Save current screenholes and restore peripheral card values (current slot + slot0)
|
|
; and save zeropage $42-$47
|
|
;
|
|
SaveMem: lda E_IER
|
|
and #$18 ; See if either CB1 or CB2 interrupts were enabled
|
|
beq SkipVbl ; No, they were not, skip the wait for vbl
|
|
|
|
; now wait for 2xVBL before we muck with the screenholes
|
|
lda #$18 ; Clear CB2 flag - VBL
|
|
sta E_IFR
|
|
VWait: bit E_IFR ; Wait for vertical retrace
|
|
beq VWait
|
|
; wait for another one to ensure font data is loaded
|
|
lda #$18 ; Clear CB2 flag - VBL
|
|
sta E_IFR
|
|
VWait2: bit E_IFR ; Wait for vertical retrace
|
|
beq VWait2
|
|
|
|
SkipVbl: bit CWrtOff ; disable font loading
|
|
|
|
jsr SwapScrH ; Swap in the screenhole data with ours
|
|
jsr SwapZero ; swap zeropage
|
|
rts
|
|
|
|
;
|
|
; Save Card screen holes and restore original values
|
|
; and restore zeropage $42-$47
|
|
;
|
|
RestMem: pha
|
|
php ; keep carry error indication
|
|
jsr SwapZero ; swap zeropage
|
|
jsr SwapScrH ; Swap back the original screenhole data
|
|
plp
|
|
pla
|
|
rts
|
|
|
|
;
|
|
; Swap screenholes with driver values
|
|
; we do Slot 0 and current slot values
|
|
; This nice code is from Peter Ferrie, thanks
|
|
;
|
|
SwapScrH: lda #$07 ; Init ZP Screenbase, X and A
|
|
sta ScreenBase+1
|
|
lda #$00
|
|
sta ScreenBase
|
|
sta ScreenBase+ExtPG
|
|
ldx #$0F
|
|
lda #$F8
|
|
@loop:
|
|
tay ; A holds screen page index at loop entry
|
|
lda (ScreenBase),Y
|
|
pha ; Save current screen hole byte on stack.
|
|
lda TmpScrH,X
|
|
sta (ScreenBase),Y ; Restore screen hole byte from array
|
|
pla
|
|
sta TmpScrH,X ; Copy saved screen hole byte into array.
|
|
dex
|
|
bmi done ; Exit when array is full (all 16 bytes are copied, X<0).
|
|
txa ; TXA/LSR tests whether array index is odd or even
|
|
lsr ; and sets carry accordingly (1 = odd).
|
|
tya ; Bring screen index into A for manipulation
|
|
eor Slot ; Cycle page index between $x8 and $x8+n as long as N in 1..7
|
|
bcc @loop ; Take branch every other loop, using array index odd/even
|
|
; (carry still valid from TXA/LSR)
|
|
eor #$80 ; Cycle page index between $F8 and $78
|
|
bpl @loop ; If flipping from $78->$F8 (now negative), continue
|
|
dec ScreenBase+1 ; Go to next page counting down ($07->$06, etc.)
|
|
bne @loop ; Always -- equiv. to BRA or JMP. Never reaches 0.
|
|
done: rts
|
|
|
|
;
|
|
; Swap zeropage with driver values
|
|
;
|
|
SwapZero: ldx #$2f
|
|
@loop2: lda $20,X
|
|
pha ; Save current zeropage byte on stack.
|
|
lda TmpZero,X
|
|
sta $20,X ; Restore zeropage byte from temp storage
|
|
pla
|
|
sta TmpZero,X ; Copy saved zeropage byte into temp storage.
|
|
dex
|
|
bpl @loop2
|
|
rts
|
|
|
|
;
|
|
; Set Prodos unit number, assume unit0 only
|
|
;
|
|
SetProUnit: lda Slot ; create the slot/unit byte
|
|
asl ; 7 6 5 4 3 2 1 0
|
|
asl ; D S S S 0 0 0 0
|
|
asl
|
|
asl
|
|
sta ProUnit
|
|
rts
|
|
|
|
;
|
|
; Set Prodos buffer pointer
|
|
;
|
|
SetProBuf: lda #<Buffer
|
|
sta ProBuf
|
|
lda #>Buffer
|
|
sta ProBuf+1
|
|
lda #0 ; turn off extended addressing
|
|
sta ProBufOff+ExtPG
|
|
rts
|
|
|
|
;
|
|
;Blockio stub
|
|
; This hides in non banked memory
|
|
;
|
|
StartStub: ldy Bank_Reg ;save current bank
|
|
sty BankSave
|
|
ldy #4 ;set virtual disk driver bank
|
|
sty Bank_Reg
|
|
jsr BlockIO ;go read/write the block
|
|
ldy BankSave ;restore bank
|
|
sty Bank_Reg
|
|
rts
|
|
Bank_Tmp: .byte 0 ;temp holder for calling bank
|
|
;
|
|
;Sector IO stub
|
|
; This hides in non banked memory
|
|
; we only catch unit 0 (D1) as virtual drive
|
|
; other wise we pass to the orignal floppy driver
|
|
;
|
|
SectIOT: lda D_UNITNUM
|
|
beq drive1
|
|
jmp OrigDriver ;continue on with original driver
|
|
|
|
drive1: ldy Bank_Reg ;save current bank
|
|
sty BankSave
|
|
ldy #4 ;set virtual disk driver bank
|
|
sty Bank_Reg
|
|
jsr SectIO ;go read/write the sector
|
|
ldy BankSave ;restore bank
|
|
sty Bank_Reg
|
|
rts
|
|
EndStub =*
|
|
|
|
|
|
|
|
|
|
EndCode = *
|
|
|
|
.END |