diff --git a/README.md b/README.md new file mode 100644 index 0000000..e0ce1c2 --- /dev/null +++ b/README.md @@ -0,0 +1,9 @@ +# Apple /// Driver Depot + +### Repository for all Apple /// drivers, buildable with `ca65` + +## Idea + +Be able to maintain all known Apple /// device drivers with "modern" tools (Have _you_ used the Apple Pascal Assembler?!?) + +The `ca65` assembler is part of the `cc65` toolchain: [cc65](https://github.com/cc65/cc65) \ No newline at end of file diff --git a/build/README.md b/build/README.md new file mode 100644 index 0000000..4fc5e01 --- /dev/null +++ b/build/README.md @@ -0,0 +1,19 @@ +# Build System + +### Json database + +``` + { + "driver_dib1": ".VSDRIVE", + "description": "Apple /// Virtual Serial Drive Driver by David Schmidt 2012 - 2014", + "dir": "vsdrive", + "repo": "https://github.com/ADTPro/adtpro", + "asm": "blob/main/src/client/sos/serial/drive/vsdrive.asm", + "sha": "552170569181b05e810bdbcca6828efab3b3ae83", + "local_asm": "vsdrive.asm" + } +``` +`"repo`: The GitHub repo, or other source of truth for this driver +`"asm"`: The upstream source of the main assembly file +`"sha`: If from a GitHub repo, the sha of the commit resulting in the upstream source +`"local_asm"`: The downstream ca65-assembly version of `"asm"` \ No newline at end of file diff --git a/build/manifest.json b/build/manifest.json new file mode 100644 index 0000000..aa3a55e --- /dev/null +++ b/build/manifest.json @@ -0,0 +1,29 @@ +[ + { + "driver_dib1": ".VSDRIVE", + "description": "Apple /// Virtual Serial Drive Driver by David Schmidt 2012 - 2014", + "dir": "vsdrive", + "repo": "https://github.com/ADTPro/adtpro", + "asm": "blob/main/src/client/sos/serial/drive/vsdrive.asm", + "sha": "552170569181b05e810bdbcca6828efab3b3ae83", + "local_asm": "vsdrive.asm" + }, + { + "driver_dib1": ".CFFA3000D1", + "description": "Apple /// CFFA3000 (Compact Flash For Apple 3000) Driver by David Schmidt 2011", + "dir": "cffa3000", + "repo": "https://github.com/david-schmidt/apple-iii-cffa3000-driver", + "asm": "blob/master/src/CFFA3000.s", + "sha": "1500353920197bec5ddfe96803340063b4cacc38", + "local_asm": "CFFA3000.s" + }, + { + "driver_dib1": ".FOCUSD1", + "description": "Apple /// Focus Driver - by David Schmidt 2019", + "dir": "FocusDrive3", + "repo": "https://github.com/david-schmidt/FocusDrive3", + "asm": "blob/master/src/focus3.s" + "sha": "5699c3146c4f8c3577b1cbe1f9b8b726c09e0cfd", + "local_asm": "focus3.s" + } +] \ No newline at end of file diff --git a/drivers/FocusDrive3/README.md b/drivers/FocusDrive3/README.md new file mode 100644 index 0000000..9bd37b8 --- /dev/null +++ b/drivers/FocusDrive3/README.md @@ -0,0 +1 @@ +# Apple /// driver for the Focus IDE Interface Card \ No newline at end of file diff --git a/drivers/FocusDrive3/focus3.s b/drivers/FocusDrive3/focus3.s new file mode 100644 index 0000000..635669d --- /dev/null +++ b/drivers/FocusDrive3/focus3.s @@ -0,0 +1,1084 @@ +; Focus3 + +; .TITLE "Apple /// Focus Drive Driver" + .PROC Focus3 + + .setcpu "6502" + .reloc + +DriverVersion = $005B ; Version number +DriverMfgr = $4453 ; Driver Manufacturer - DS +DriverType = $E1 ; No formatter present for the time being +DriverSubtype = $02 ; +InitialSlot = $02 ; Slot number to assume we're in + +; +; SOS Equates +; +ExtPG = $1401 ; Driver extended bank address offset +AllocSIR = $1913 ; Allocate system internal resource +SELC800 = $1922 ; Enable Expansion Rom Space +DeAlcSIR = $1916 ; De-allocate system internal resource +SysErr = $1928 ; Report error to system +EReg = $FFDF ; Environment register + +; +; SOS Zero page parameters +; +ReqCode = $C0 ; Request code +SOS_Unit = $C1 ; Unit number +SosBuf = $C2 ; SOS buffer pointer +ReqCnt = $C4 ; Requested byte count +CtlStat = $C2 ; Control/status code +CSList = $C3 ; Control/status list pointer +SosBlk = $C6 ; Starting block number +QtyRead = $C8 ; Bytes read return by D_READ + +; +; Focus Hardware +; +ATOffset = $8F ; Base offset for $C0FF,X addressing + +ATData8 = $C080-ATOffset+8 ; 8 bit data port +ATError = $C081-ATOffset+8 ; Error flags +ATSectorCount = $C082-ATOffset+8 ; Number of sectors to process +ATSectorNumber = $C083-ATOffset+8 ; Sector number requested +ATCylL = $C084-ATOffset+8 ; Cylinder # Low +ATCylH = $C085-ATOffset+8 ; Cylinder # High +ATHead = $C086-ATOffset+8 ; Head # +ATStatus = $C087-ATOffset+8 ; (R) Status of drive +ATCommand = $C087-ATOffset+8 ; (W) Issue a command +ATData16 = $C088-ATOffset-8 ; 16 bit data port, accessed with MSlot16 index value +ATReset = $C08B-ATOffset-8 ; (W) Reset the drive +ATAltStatus = $C08E-ATOffset-8 ; (R) Status of drive (Don't clear) +ATDigOut = $C08E-ATOffset-8 ; (W) Reset +ATDriveAdr = $C08F-ATOffset-8 ; (R) Drive address + +; +; Parameter block specific to current SOS request +; +Num_Blks = $D2 ; Number of blocks requested (we'll never ever have > 128 blocks) +Count = $D3 ; 2 bytes lb,hb + +; +; Focus zero page +; +Track = $D6 ; (3) Current track / ProDOS block +Head = $D9 ; (1) Current head +Sector = $DA ; (1) Current sector +Temp = $DB ; (2) Timer temp +RetryCount = $DD ; (1) Number of read retries +OkFlag = $DE ; (1) Compare byte for return value +ProCommand = $DF ; (1) 0=Status,1=Read,2=Write,3=Format +Ytemp = $E1 ; (1) Temp space for Y +ProBlock = $E2 ; (3) Block # to work with + +; +; SOS Error Codes +; +XDNFERR = $10 ; Device not found +XBADDNUM = $11 ; Invalid device number +XREQCODE = $20 ; Invalid request code +XCTLCODE = $21 ; Invalid control/status code +XCTLPARAM = $22 ; Invalid control/status parameter +XNORESRC = $25 ; Resource not available +XBADOP = $26 ; Invalid operation +XIOERROR = $27 ; I/O error +XNODRIVE = $28 ; Drive not connected +XBYTECNT = $2C ; Byte count not a multiple of 512 +XBLKNUM = $2D ; Block number to large +XDISKSW = $2E ; Disk switched +XDCMDERR = $31 ; device command ABORTED error occurred +XCKDEVER = $32 ; Check device readiness routine failed +XNORESET = $33 ; Device reset failed +XNODEVIC = $38 ; Device not connected + +; +; Switch Macro +; +.MACRO SWITCH index,bounds,adrs_table,noexec ; See SOS Reference +.IFNBLANK index ; If PARM1 is present, + lda index ; load A with switch index +.ENDIF +.IFNBLANK bounds ; If PARM2 is present, + cmp #bounds+1 ; perform bounds checking + bcs @110 ; on switch index +.ENDIF + asl A ; Multiply by 2 for table index + tay + lda adrs_table+1,y ; Get switch address from table + pha ; and push onto Stack + lda adrs_table,y + pha +.IFBLANK noexec + rts ; Exit to code +.ENDIF +@110: +.ENDMACRO + + .SEGMENT "TEXT" + +; +; Comment Field of driver +; + .word $FFFF ; Signal that we have a comment + .word COMMENT_END - COMMENT +COMMENT: .byte "Apple /// Focus Driver - by David Schmidt 2019" +COMMENT_END: + + .SEGMENT "DATA" + +;------------------------------------ +; +; Device identification Block (DIB) - Volume #0 +; +;------------------------------------ + +DIB_0: .word DIB_1 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD1 "; Device name + .byte $80 ; Active, no page alignment +DIB0_Slot: .byte InitialSlot ; Slot number + .byte $00 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; 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) - Volume #1 +; Page alignment begins here +; +DIB_1: .word DIB_2 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD2 "; Device name + .byte $80 ; Active +DIB1_Slot: .byte InitialSlot ; Slot number + .byte $01 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; Filler +DIB1_Blks: .word $0000 ; # Blocks in device + .word DriverMfgr ; Driver manufacturer + .word DriverVersion ; Driver version + .word $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #2 +; +DIB_2: .word DIB_3 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD3 "; Device name + .byte $80 ; Active +DIB2_Slot: .byte InitialSlot ; Slot number + .byte $02 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; Filler +DIB2_Blks: .word $0000 ; # Blocks in device + .word DriverMfgr ; Driver manufacturer + .word DriverVersion ; Driver version + .word $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #3 +; +DIB_3: .word DIB_4 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD4 "; Device name + .byte $80 ; Active +DIB3_Slot: .byte InitialSlot ; Slot number + .byte $03 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; Filler +DIB3_Blks: .word $0000 ; # Blocks in device + .word DriverMfgr ; Driver manufacturer + .word DriverVersion ; Driver version + .word $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #4 +; +DIB_4: .word DIB_5 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD5 "; Device name + .byte $00 ; Inactive +DIB4_Slot: .byte InitialSlot ; Slot number + .byte $04 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; Filler +DIB4_Blks: .word $0000 ; # Blocks in device + .word DriverMfgr ; Driver manufacturer + .word DriverVersion ; Driver version + .word $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #5 +; +DIB_5: .word DIB_6 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD6 "; Device name + .byte $00 ; Inactive +DIB5_Slot: .byte InitialSlot ; Slot number + .byte $05 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; Filler +DIB5_Blks: .word $0000 ; # Blocks in device + .word DriverMfgr ; Driver manufacturer + .word DriverVersion ; Driver version + .word $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #6 +; +DIB_6: .word DIB_7 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD7 "; Device name + .byte $00 ; Inactive +DIB6_Slot: .byte InitialSlot ; Slot number + .byte $06 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; Filler +DIB6_Blks: .word $0000 ; # Blocks in device + .word DriverMfgr ; Driver manufacturer + .word DriverVersion ; Driver version + .word $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #7 +; +DIB_7: .word $0000 ; Link pointer + .word Entry ; Entry pointer + .byte $08 ; Name length byte + .byte ".FOCUSD8 "; Device name + .byte $00 ; Inactive +DIB7_Slot: .byte InitialSlot ; Slot number + .byte $07 ; Unit number + .byte DriverType ; Type + .byte DriverSubtype ; Subtype + .byte $00 ; Filler +DIB7_Blks: .word $0000 ; # Blocks in device + .word DriverMfgr ; Driver manufacturer + .word DriverVersion ; Driver version + .word $0000 ; DCB length followed by DCB + +;------------------------------------ +; +; Local storage locations +; +;------------------------------------ + +LastOP: .res $08, $FF ; Last operation for D_REPEAT calls +SIR_Addr: .word SIR_Tbl +SIR_Tbl: .res $05, $00 +SIR_Len = *-SIR_Tbl +RdBlk_Proc: .word $0000 +WrBlk_Proc: .word $0000 +MaxUnits: .byte $08 ; The maximum number of units +DriveType: .byte $00 ; Type of drive +DCB_Idx: .byte $00 ; DCB 0's blocks + .byte DIB1_Blks-DIB0_Blks ; DCB 1's blocks + .byte DIB2_Blks-DIB0_Blks ; DCB 2's blocks + .byte DIB3_Blks-DIB0_Blks ; DCB 3's blocks + .byte DIB4_Blks-DIB0_Blks ; DCB 4's blocks + .byte DIB5_Blks-DIB0_Blks ; DCB 5's blocks + .byte DIB6_Blks-DIB0_Blks ; DCB 6's blocks + .byte DIB7_Blks-DIB0_Blks ; DCB 7's blocks +SigFocus: .byte "Parsons Engin." ; Focus card signature in memory +CardIsOK: .byte $00 ; Have we found the Focus card yet? +LastError: .byte $00 ; Recent error RC from Focus +StatusBlks: .word $0000 ; Temp storage for number of blocks + +; +; Storage items from Focus ROM +; + +; Partition sizes for max of 8 partitions +PartLo: .res $08, $00 ; Partition start block +PartMd: .res $08, $00 ; wait for it... +PartHi: .res $08, $00 ; 24 bit! + +; Temps +PartSizeLo1: .byte $00 +PartSizeMid1: .byte $00 +PartSizeHi1: .byte $00 +SmartFlag: .byte $00 +MSlot16: .byte $00 ; Offset to hardware port based on slot + +;------------------------------------ +; +; Driver request handlers +; +;------------------------------------ + +Entry: + lda DIB0_Slot ; Slot we're in (all DIBx_Slot values are the same) + jsr SELC800 ; Turn on C800 ROM space from our slot + jsr GoSlow + jsr Dispatch ; Call the dispatcher + jsr GoFast + 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 #$00 + jsr SELC800 ; Unselect C800 ROM space + 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 #$02 ; 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 once each for all volumes. +; +DInit: + lda SOS_Unit ; Check if we're initting the zeroeth unit + cmp #$00 + bne DInitDone ; No - then skip the signature check + +CheckSig: lda #$08 ; Prepare MSlot16 slot address calculation + clc + adc DIB0_Slot ; A is now $08+slot# + asl + asl + asl + asl ; A is now $80+(slot#*16) in the high nibble, $0 in low + clc + adc #$0F ; A is now $9F through $CF depending on slot + sta MSlot16 ; Hang on to this for MSlot16 math + lda #$C0 ; Form a $CsED address, where s = slot # + ora DIB0_Slot ; Add in slot number + sta Count+1 + lda #$ED ; $CxED is where "Parsons Engin." string lives + sta Count + ldy #$0D +@1: lda (Count),y + cmp SigFocus,y ; Check for 'Parsons Engin.' signature in our slot + bne NoDevice ; No device if all bytes don't match + dey + bpl @1 + + lda DIB0_Slot ; Found a Focus! + ora #$10 ; SIR = $10+slot# + sta SIR_Tbl + sta CardIsOK ; Remember that we found the card + lda #SIR_Len + ldx SIR_Addr + ldy SIR_Addr+1 + jsr AllocSIR ; Claim SIR + bcs NoDevice + lda #$00 ; 0=status + sta ProCommand ; Command for Focus + jsr InitDrive ; Hit the hardware, load up defaults + bcs NoDevice +DInitDone: + lda CardIsOK ; Did we previously find a card? + bne :+ + jmp NoDevice ; If not... then bail +: clc + rts + +; +; D_READ call processing +; +DRead: + lda CardIsOK ; Did we previously find a card? + bne DReadGo + jmp NoDevice ; If not... then bail +DReadGo: + jsr CkCnt ; Checks for validity, aborts if not + jsr CkUnit ; Checks for unit below unit max + lda #$00 ; Zero # bytes read + sta Count ; Local count of bytes read + sta Count+1 + sta ProBlock+2 + sta QtyRead ; Userland count of bytes read + sta QtyRead+1 ; Msb of userland bytes read + lda Num_Blks ; Check for block count greater than zero + beq ReadExit + jsr FixUp ; Correct for addressing anomalies + lda #$01 ; 1=read + sta ProCommand ; Prepare to read a block + lda SosBlk + sta ProBlock + lda SosBlk+1 + sta ProBlock+1 +ReadOne: jsr ReadBlock + bcs IO_Error + inc ProBlock + bne SkipReadMSBIncrement + inc ProBlock+1 +SkipReadMSBIncrement: + inc SosBuf+1 ; Go get the next block in the buffer + jsr BumpSosBuf ; ...512 bytes in, and check the pointer + dec Num_Blks + bne ReadOne + lda Count ; Local count of bytes read + sta QtyRead ; Update # of bytes actually read + lda Count+1 + sta QtyRead+1 + clc +ReadExit: + rts ; Exit read routines +IO_Error: lda #XIOERROR ; I/O error + jsr SysErr ; Return to SOS with error in A + +; +; D_WRITE call processing +; +DWrite: + lda CardIsOK ; Did we previously find a card? + bne DWriteGo + jmp NoDevice ; If not... then bail + +DWriteGo: + jsr CkCnt ; Checks for validity, aborts if not + jsr CkUnit ; Checks for unit below unit max + lda #$00 ; 0=Status + lda Num_Blks ; Check for block count greater than zero + beq WriteExit + jsr FixUp ; Correct for addressing anomalies + lda #$02 ; 2=write + sta ProCommand + lda SosBlk + sta ProBlock + lda SosBlk+1 + sta ProBlock+1 + lda #$00 + sta ProBlock+2 +WriteOne: jsr WriteBlock + bcs IO_Error + inc ProBlock ; Bump the block number + bne SkipWriteMSBIncrement + inc ProBlock+1 +SkipWriteMSBIncrement: + inc SosBuf+1 ; Go get the next block in the buffer + jsr BumpSosBuf ; ...512 bytes in, and check the pointer + dec Num_Blks + bne WriteOne + clc +WriteExit: + rts + +; +; D_STATUS call processing +; $00 = Driver Status +; $FE = Return preferred bitmap location ($FFFF) +; +DStatus: + lda CardIsOK ; Did we previously find a card? + bne DStatusGo + jmp NoDevice ; If not... then bail + +DStatusGo: + lda CtlStat ; Which status code to run? + bne DS0 + clc + rts +DS0: cmp #$FE + bne DSWhat + + ldy #$00 ; Return preferred bit map locations. + lda #$FF ; 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 +; +DControl: + LDA CardIsOK ; Did we previously find a card? + BNE DContGo + JMP NoDevice ; If not... then bail + +DContGo: LDA CtlStat ; Control command + BEQ CReset + JMP DCWhat ; Control code no good! +CReset: clc ; No-op +DCDone: rts +DCWhat: lda #XCTLCODE ; Control/status code no good + jsr SysErr ; Return to SOS with error in A + +;------------------------------------ +; +; Utility routines +; +;------------------------------------ + +; +; Check ReqCnt to ensure it is a multiple of 512. +; +CkCnt: lda ReqCnt ; Look at the lsb of bytes requested + bne @1 ; No good! lsb should be 00 + lda ReqCnt+1 ; Look at the msb + lsr A ; Put bottom bit into carry, 0 into top + sta Num_Blks ; 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 error in A + +BumpSosBuf: inc SosBuf+1 ; Increment SosBuf MSB + ; fallthrough to FixUp + +; +; 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 SosBuf+1 ; Look at msb + beq @1 ; That's one! + cmp #$FD ; Is it the other one? + bcs @2 ; Yep. fix it! + rts ; Pointer unchanged, return carry clear. +@1: lda #$80 ; 00xx -> 80xx + sta SosBuf+1 + dec SosBuf+ExtPG ; Bank N -> band N-1 + lda SosBuf+ExtPG ; See if it was bank 0 + cmp #$7F ; (80) before the DEC. + bne @3 ; Nope! all fixed. + lda #$20 ; If it was, change both + sta SosBuf+1 ; Msb of address and + lda #$8F + sta SosBuf+ExtPG ; Bank number for bank 8F + rts ; Return carry set +@2: and #$7F ; Strip off high bit + sta SosBuf+1 ; FDxx ->7Dxx + INC SosBuf+ExtPG ; Bank N -> bank N+1 +@3: rts ; Return carry set + +CkUnit: lda SOS_Unit ; Checks for unit below unit max + cmp MaxUnits + bmi UnitOk +NoUnit: lda XBADDNUM ; Report no unit to SOS + jsr SysErr +UnitOk: clc + rts + +; +; Throttle back to 1 MHz +; +GoSlow: pha + php + lda EReg + ora #$80 + sta EReg + plp + pla + rts + +; +; Throttle up to 2 MHz +; +CGoFast: clc +GoFast: pha + php + lda EReg + and #$7F + sta EReg + plp + pla + rts + +; +; Gorp copied from ROM +; + +DriveCount: + .word $0012 +DSectors: + .word $001B,$001B,$001B,$0021 + .word $0021,$0021,$0021,$001F + .word $002B,$0011,$0011,$0018 + .word $0011,$0026,$002B,$002B + .word $0023,$0026 +DHeads: .word $0004,$0003,$0002,$0002 + .word $0004,$0006,$0008,$0004 + .word $0002,$0004,$0005,$0004 + .word $0005,$0004,$0004,$0004 + .word $0003,$0008 +DCylinders: + .word $030E,$030E,$030E,$0536 + .word $0536,$0536,$0536,$029E + .word $03D1,$0266,$FFFF,$0368 + .word $03D1,$0223,$03D1,$03CD + .word $0317,$0224 +DPark: .word $035D,$035D,$035D,$0537 + .word $0537,$0537,$0537,$029E + .word $03DE,$0276,$FFFF,$0369 + .word $03D4,$0228,$03DE,$03DE + .word $0320,$0224 +DHeadCyl: + .word $006C,$0051,$0036,$0042 + .word $0084,$00C6,$0108,$007C + .word $0056,$0044,$0055,$0060 + .word $0055,$0098,$00AC,$00AC + .word $0069,$0130 + +InvalidCommand: + lda #$01 + rts + +WaitDataOut: +WaitDataIn: + lda #$08 + bne DoCheck + lda #$00 +DoCheck: sta OkFlag + lda #$00 + sta Temp + lda #$F1 + sta Temp+1 + ldx MSlot16 +@B: ldy #$00 +@A: lda ATStatus,x + and #$88 + cmp OkFlag + beq OK1 + iny + bne @A + inc Temp + bne @B + inc Temp+1 + bne @B + sec + rts + +OK1: clc + rts + +InitDrive: + jsr SetUpDefaults + bcs Err2 + +OK2: + lda #$00 + clc + rts + +Err2: + sec +LocalRTS: + rts + +SetUpDefaults: + jsr ReadPartition ; Read partition data + lda #$27 + bcs LocalRTS ; Didn't work + ldx MSlot16 + lda ATData16,x ; 0x00 + sta Track + lda ATData16+1,x ; 0x01 'Pa' (Parsons Tech) + sta Track+1 + ldy #$06 ; Index to sectors/Heads + jsr KillYWords ; 0x02..0x0d killed + lda ATData16,x ; 0x0e wasteage + lda ATData16+1,x ; 0x0f get # active partitions + and #$7f ; Sanitize: strip off high bit + cmp #$08 ; We support at most 8 + bmi :+ + lda #$08 ; If they have > 8... they have 8 +: sta MaxUnits + ldy #$06 ; Index to sectors/Heads + jsr KillYWords ; 0x10..0x1b killed + lda ATData16,x ; 0x1c Get drive type (ignore 0x1d) + asl a + sta DriveType + ldy #$01 ; Index to zeroeth partition start +@A: jsr KillYWords ; 0x1e..0x1f killed + ; Note: y is now 0x00 +PartStart: + lda ATData16,x ; (Part*0x10)+0x20 get start block 1 of 4 + sta PartLo,y ; Save Volume size data + lda ATData16+1,x ; (Part*0x10)+0x21 get start block 2 of 4 + sta PartMd,y + lda ATData16,x ; (Part*0x10)+0x22 get start block 3 of 4 + sta PartHi,y + lda ATData16+1,x ; (Part*0x10)+0x23 discard start block 4 of 4 + lda ATData16,x ; (Part*0x10)+0x24 get block count 1 of 4 + sta PartSizeLo1 + lda ATData16+1,x ; (Part*0x10)+0x25 get block count 2 of 4 + sta PartSizeMid1 + lda ATData16,x ; (Part*0x10)+0x26 get block count 3 of 4 (ignore 4 of 4) + sta PartSizeHi1 + lda ATData16,x ; (Part*0x10)+0x28 get write protect flag (ignore 2 of 2) + sty Ytemp ; Stash current partition number + ldy #$03 ; Kill File system ID, end of part + jsr KillYWords ; (Part*0x10)+0x2a..0x2f killed + + ldx Ytemp ; X now holds partition number + ldy DCB_Idx,x ; Y now holds offset to DIBx_Blks of this partition/unit + lda PartSizeLo1 + sta DIB0_Blks,y + lda PartSizeMid1 + sta DIB0_Blks+1,y + lda PartSizeHi1 + cmp #$01 ; Do we have 65536 (or more) blocks? + bne :+ + lda #$FF ; Then we really only have 65535 blocks. + sta DIB0_Blks,y + sta DIB0_Blks+1,y +: ldx MSlot16 ; Reload X with MSlot16 + ldy Ytemp + iny + cpy MaxUnits + bne PartStart +; Now we need to burn the rest of the partition table + jsr KillToDRQ ; Kill the rest of the sector + jsr WaitStatus ; Exit (Return Carry Clear/Set) + bcs Err1 + ldx Track + cpx #$50 ; 'P' + bne Invalid + ldx Track+1 + cpx #$61 ; 'a' + bne Invalid + ldy DriveType + ldx MSlot16 + lda DHeads,y + sec + sbc #$01 + ora #$A0 + sta ATHead,x + lda DSectors,y + sta ATSectorCount,x + lda #$91 ; Setup size command + sta ATCommand,x + jsr WaitStatus ; Wait for ack + bcc NoErr2 +Err1: lda #$28 + rts + +NoErr2: lda #$00 + clc + rts + +Invalid: + lda #$00 + sta MaxUnits + lda #$28 + sec + rts + +ReadPartition: + lda #$02 + sta RetryCount +TryRead: lda #$00 + sta Track + sta Track+1 + sta Head + sta Sector + jsr TRYME + bcc VERR + dec RetryCount + beq VERR + jsr RecalDrive + jmp TryRead + +TRYME: lda #$20 ; Send a read command (But don't actually read yet) + jsr SendPacket + jmp WaitDataIn ; Wait for readiness + +VERR: rts + +RecalDrive: + ldx MSlot16 + lda #$00 + sta ATReset,x + lda #$80 + sta ATReset,x + lda #$10 + jsr SendPacket + jmp WaitStatus + +WaitStatus: + ldx MSlot16 + ldy #$00 + sty Temp + lda #$F1 + sta Temp+1 +@A: lda ATStatus,x + bpl NotBusy + iny + bne @A + inc Temp + bne @A + inc Temp+1 + bne @A +IOErr: lda #$27 + sec + rts + +NotBusy: and #$01 + bne IOErr + clc + rts + +; +; Remove y*2 bytes (y words) from port (Note that this will only retain the low 8 bits!) +; +KillYWords: + lda ATStatus,x + and #$08 + beq KillYWords + lda ATData16,x + dey + bne KillYWords + rts + +; +; Remove all bytes from port until the Data Transfer Requested bit is off +; +KillToDRQ: + lda ATData16,x + lda ATStatus,x + and #$08 + bne KillToDRQ + rts + +ReadBlock: +WriteBlock: jsr FindBlock + lda #$04 + sta RetryCount +OneTry: lda ProCommand + lsr a + bcs DoRead + jsr Write1Block + bcs Retry +NoErr1: lda #$00 + tax + rts + +DoRead: jsr Read1Block + bcc NoErr1 +Retry: jsr RecalDrive + dec RetryCount + bne OneTry +IOError: sec + lda #$27 + rts + +WriteErr: + lda #$2B + ldx #$00 + ldy #$00 + rts + +Read1Block: + lda #$20 + jsr SendPacket + jsr WaitDataIn + bcs OhWell + ldx MSlot16 + ldy #$00 +@1: lda ATData16,x + sta (SosBuf),y + iny + lda ATData16+1,x + sta (SosBuf),y + iny + bne @1 + jsr BumpSosBuf +@2: lda ATData16,x + sta (SosBuf),y + iny + lda ATData16+1,x + sta (SosBuf),y + iny + bne @2 + dec SosBuf+1 + jmp WaitStatus + +OhWell: sec + rts + +Write1Block: + lda #$30 + jsr SendPacket + jsr WaitDataOut + ldy #$00 +WriteSector: + ldx MSlot16 + lda (SosBuf),y + sta ATData16,x ; C090=S1,C0A0=S2,C0B0=S3,C0C0=S4 + iny + lda (SosBuf),y + sta ATData16+1,x + iny + bne WriteSector + jsr BumpSosBuf +Local: + lda (SosBuf),y + sta ATData16,x + iny + lda (SosBuf),y + sta ATData16+1,x + iny + bne Local + dec SosBuf+1 + jmp WaitStatus ; Return through WaitStatus + +SendPacket: + pha + ldx MSlot16 + lda Head + ora #$A0 + sta ATHead,x + lda #$01 + sta ATSectorCount,x + clc + adc Sector + sta ATSectorNumber,x + lda Track + sta ATCylL,x + lda Track+1 + sta ATCylH,x + pla + sta ATCommand,x + rts + +; +; Locate the drive block number given SOS_Unit, ProBlock(+1+2) +; Sets Track, Sector, Head +; +FindBlock: + ldy SOS_Unit + clc + lda PartLo,y + adc ProBlock + sta Track + lda PartMd,y + adc ProBlock+1 + sta Track+1 + lda PartHi,y + adc ProBlock+2 + sta Track+2 + ldx DriveType + lda DHeadCyl,x + sta Head + lda DHeadCyl+1,x + sta Sector + ldx #$00 + stx Temp + stx Temp+1 + ldy #$18 + lda Track+2 + bne Do24Bit + lda Track+1 + beq Do8Bit + sta Track+2 + lda Track + sta Track+1 + ldy #$10 + bne Do16Bit +Do8Bit: lda Track + sta Track+2 + ldy #$08 + stx Track+1 +Do16Bit: stx Track +Do24Bit: asl Track + rol Track+1 + rol Track+2 + rol Temp + rol Temp+1 + sec + lda Temp + sbc Head + tax + lda Temp+1 + sbc Sector + bcc @A + stx Temp + sta Temp+1 + inc Track +@A: dey + bne Do24Bit + sty Head + ldx DriveType +@B: sec + lda Temp + sbc DSectors,x + tay + lda Temp+1 + sbc DSectors+1,x + bcc CF + sty Temp + sta Temp+1 + inc Head + bne @B +CF: lda Temp + sta Sector + rts + + .ENDPROC + .END \ No newline at end of file diff --git a/drivers/cffa3000/CFFA3000.s b/drivers/cffa3000/CFFA3000.s new file mode 100644 index 0000000..93f177f --- /dev/null +++ b/drivers/cffa3000/CFFA3000.s @@ -0,0 +1,985 @@ +; CFFA3000 + +; .TITLE "Apple /// Compact Flash For Apple 3000 (CFFA3000) Driver" + .PROC CFFA3000 + + .feature labels_without_colons + .setcpu "6502" + .reloc + +DriverVersion = $1000 ; Version number +DriverMfgr = $4453 ; Driver Manufacturer - DS + +; +; SOS Equates +; +ExtPG = $1401 ; Driver extended bank address offset +AllocSIR = $1913 ; Allocate system internal resource +SELC800 = $1922 ; Enable Expansion ROM Space +SysErr = $1928 ; Report error to system +EReg = $FFDF ; Environment register +ReqCode = $C0 ; Request code +SOS_Unit = $C1 ; Unit number +SosBuf = $C2 ; SOS buffer pointer (2 bytes) +ReqCnt = $C4 ; Requested byte count +CtlStat = $C2 ; Control/status code +CSList = $C3 ; Control/status list pointer +SosBlk = $C6 ; Starting block number +QtyRead = $C8 ; Pointer to bytes read return by D_READ +; +; Our temps in zero page +; +Count = $CD ; 2 bytes +C800Ptr = $CF ; 2 bytes +; +; Parameter block specific to current SOS request +; +CFFAUnit = $D1 +Num_Blks = $D4 ; 2 bytes lsb, msb +DataBuf = $D6 ; 2 bytes +; +; SOS Error Codes +; +XDNFERR = $10 ; Device not found +XBADDNUM = $11 ; Invalid device number +XREQCODE = $20 ; Invalid request code +XCTLCODE = $21 ; Invalid control/status code +XCTLPARAM = $22 ; Invalid control/status parameter +XNORESRC = $25 ; Resources not available +XBADOP = $26 ; Invalid operation +XIOERROR = $27 ; I/O error +XNODRIVE = $28 ; Drive not connected +XBYTECNT = $2C ; Byte count not a multiple of 512 +XBLKNUM = $2D ; Block number to large +XDISKSW = $2E ; Disk switched +XNORESET = $33 ; Device reset failed +; +; CFFA3000 Constants +; +kCmd3K_SetEnvironment = $01 ; Set X = 0xa3 for Apple /// +kCmd3K_Status = $10 +kCmd3K_Read = $11 +kCmd3K_Write = $12 +CFFA3K_API = $CFED ; CFFA3000 API entry point +shUnitNumber = $CFDF +shStatusByte = $CFE0 +shBlockNumber = $CFE1 +; +; Switch Macro +; + .MACRO SWITCH index,bounds,adrs_table,noexec ;See SOS Reference + .IFNBLANK index ;If PARM1 is present, + LDA index ; load A with switch index + .ENDIF + .IFNBLANK bounds ;If PARM2 is present, + CMP #bounds+1 ; perform bounds checking + BCS @110 ; on switch index + .ENDIF + ASL A ;Multiply by 2 for table index + TAY + LDA adrs_table+1,Y ;Get switch address from table + PHA ; and push onto Stack + LDA adrs_table,Y + PHA + .IFBLANK noexec + ; .IF noexec <> '*' ;If PARM4 is omitted, + RTS ; exit to code + ; .ENDIF + .ENDIF +@110 + .ENDMACRO + + .SEGMENT "TEXT" +; +; Comment Field of driver +; + .WORD $FFFF ; Signal that we have a comment + .WORD COMMENT_END - COMMENT +COMMENT: .BYTE "Apple /// CFFA3000 (Compact Flash For Apple 3000) Driver by David Schmidt 2018" +COMMENT_END: + + .SEGMENT "DATA" +;------------------------------------ +; +; Device identification Block (DIB) - Volume #0 +; +;------------------------------------ + +DIB_0 .WORD DIB_1 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D1 "; Device name + .BYTE $80 ; Active, no page alignment +DIB0_Slot .BYTE $01 ; Slot number + .BYTE $00 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; 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) - Volume #1 +; Page alignment begins here +; +DIB_1 .WORD DIB_2 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D2 "; Device name + .BYTE $80 ; Active +DIB1_Slot .BYTE $01 ; Slot number + .BYTE $01 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; Filler +DIB1_Blks .WORD $0000 ; # Blocks in device + .WORD DriverMfgr ; Driver manufacturer + .WORD DriverVersion ; Driver version + .WORD $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #2 +; +DIB_2 .WORD DIB_3 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D3 "; Device name + .BYTE $80 ; Active +DIB2_Slot .BYTE $01 ; Slot number + .BYTE $02 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; Filler +DIB2_Blks .WORD $0000 ; # Blocks in device + .WORD DriverMfgr ; Driver manufacturer + .WORD DriverVersion ; Driver version + .WORD $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #3 +; +DIB_3 .WORD DIB_4 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D4 "; Device name + .BYTE $80 ; Active +DIB3_Slot .BYTE $01 ; Slot number + .BYTE $03 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; Filler +DIB3_Blks .WORD $0000 ; # Blocks in device + .WORD DriverMfgr ; Driver manufacturer + .WORD DriverVersion ; Driver version + .WORD $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #4 +; +DIB_4 .WORD DIB_5 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D5 "; Device name + .BYTE $80 ; Active +DIB4_Slot .BYTE $01 ; Slot number + .BYTE $04 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; Filler +DIB4_Blks .WORD $0000 ; # Blocks in device + .WORD DriverMfgr ; Driver manufacturer + .WORD DriverVersion ; Driver version + .WORD $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #5 +; +DIB_5 .WORD DIB_6 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D6 "; Device name + .BYTE $80 ; Active +DIB5_Slot .BYTE $01 ; Slot number + .BYTE $05 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; Filler +DIB5_Blks .WORD $0000 ; # Blocks in device + .WORD DriverMfgr ; Driver manufacturer + .WORD DriverVersion ; Driver version + .WORD $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #6 +; +DIB_6 .WORD DIB_7 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D7 "; Device name + .BYTE $80 ; Active +DIB6_Slot .BYTE $01 ; Slot number + .BYTE $06 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; Filler +DIB6_Blks .WORD $0000 ; # Blocks in device + .WORD DriverMfgr ; Driver manufacturer + .WORD DriverVersion ; Driver version + .WORD $0000 ; DCB length followed by DCB +; +; Device identification Block (DIB) - Volume #7 +; +DIB_7 .WORD $0000 ; Link pointer + .WORD Entry ; Entry pointer + .BYTE $0B ; Name length byte + .BYTE ".CFFA3000D8 "; Device name + .BYTE $80 ; Active +DIB7_Slot .BYTE $01 ; Slot number + .BYTE $07 ; Unit number + .BYTE $F1 ; Type + .BYTE $10 ; Subtype + .BYTE $00 ; Filler +DIB7_Blks .WORD $0000 ; # Blocks in device + .WORD DriverMfgr ; Driver manufacturer + .WORD DriverVersion ; Driver version + .WORD $0000 ; DCB length followed by DCB + +;------------------------------------ +; +; Local storage locations +; +;------------------------------------ + +LastOP .RES $08, $FF ; Last operation for D_REPEAT calls +SIR_Addr .WORD SIR_Tbl +SIR_Tbl .RES $05, $00 +SIR_Len = *-SIR_Tbl +RdBlk_Proc .WORD $0000 +WrBlk_Proc .WORD $0000 +MaxUnits .BYTE $00 ; The maximum number of units +DCB_Idx .BYTE $00 ; DCB 0's blocks + .BYTE DIB1_Blks-DIB0_Blks ; DCB 1's blocks + .BYTE DIB2_Blks-DIB0_Blks ; DCB 2's blocks + .BYTE DIB3_Blks-DIB0_Blks ; DCB 3's blocks + .BYTE DIB4_Blks-DIB0_Blks ; DCB 4's blocks + .BYTE DIB5_Blks-DIB0_Blks ; DCB 5's blocks + .BYTE DIB6_Blks-DIB0_Blks ; DCB 6's blocks + .BYTE DIB7_Blks-DIB0_Blks ; DCB 7's blocks +SigCF3K .BYTE "CF3K" ; CFFA3000 signature in memory +CardIsOK .BYTE $00 ; Have we found the CFFA3000 yet? +LastError .BYTE $00 ; Recent error RC from CFFA3000 + +;------------------------------------ +; +; Driver request handlers +; +;------------------------------------ + +Entry LDA DIB0_Slot + JSR SELC800 ; Turn on C800 ROM space from our slot + LDA SOS_Unit + STA CFFAUnit + INC CFFAUnit ; CFFA3000 unit is 1-based vs. SOS 0-based + 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 #$00 + JSR SELC800 ; Unselect C800 ROM space + 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 #$02 ; 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 once each for all volumes. +; +DInit LDA SOS_Unit ; Check if we're initting the zeroeth unit + BNE UnitInit ; No - then skip the signature check + +CheckSig LDA #$C0 ; Form a $CsF6 address, where s = slot # + ORA DIB0_Slot ; Add in slot number + STA Count+1 + LDA #$F6 + STA Count + LDY #$03 +@1 LDA (Count),Y + CMP SigCF3K,Y ; Check for 'CF3K' signature in our slot + BNE NoDevice ; No device if all four bytes don't match + DEY + BPL @1 + + LDA DIB0_Slot ; Found a CFFA3000! + ORA #$10 ; SIR = 16+slot# + STA SIR_Tbl + STA CardIsOK ; Remember that we found the card + LDA #SIR_Len + LDX SIR_Addr + LDY SIR_Addr+1 + JSR AllocSIR ; This one's mine! + BCS NoDevice + + LDX #$A3 + LDA #kCmd3K_SetEnvironment + JSR CFFA3K_API ; Get CFFA3000 set up for us + BCS NoDevice + LDA #$00 + STA shUnitNumber + LDA #kCmd3K_Status + JSR CFFA3K_API ; How many units can we expect at maximum? + BCS NoDevice + LDA shUnitNumber + STA MaxUnits + +UnitInit + LDA CardIsOK ; Did we previously find a card? + BEQ NoDevice ; If not... then bail + LDA CFFAUnit ; Which unit did we get called with? + STA shUnitNumber + LDA #kCmd3K_Status ; Ask for the status of this unit + JSR CFFA3K_API + LDA #$00 + STA shBlockNumber + STA shBlockNumber+1 + STA shBlockNumber+2 + STA shBlockNumber+3 + LDA #kCmd3K_Read ; Do a dummy read of block zero + JSR CFFA3K_API ; to clear out a device switched error + LDA #kCmd3K_Status + JSR CFFA3K_API + BCS NoDevice + +SaveCapacity + LDX SOS_Unit ; Get the stats on this unit + LDY DCB_Idx,X + LDA shBlockNumber + STA DIB0_Blks,Y + LDA shBlockNumber+1 + STA DIB0_Blks+1,Y + LDA shBlockNumber+2 + CMP #$01 ; Do we have 65536 (or more) blocks? + BNE UIDone + LDA #$FF ; Then we really only have 65535 blocks. + STA DIB0_Blks,Y + STA DIB0_Blks+1,Y + +UIDone CLC + RTS + +; +; D_READ call processing +; +DRead + LDA CardIsOK ; Did we previously find a card? + BNE DReadGo + JMP NoDevice ; If not... then bail +DReadGo + JSR CkCnt ; Checks for validity, aborts if not + JSR CkUnit ; Checks for unit below unit max + LDA #$00 ; 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 Num_Blks ; Check for Num_Blks greater than zero + ORA Num_Blks+1 + BEQ ReadExit + JSR FixUp ; Correct for addressing anomalies + JSR Read_Block ; Transfer a block to/from the disk + LDY #$00 + LDA Count ; Local count of bytes read + STA (QtyRead),y ; Update # of bytes actually read + INY + LDA Count+1 + STA (QtyRead),y + BCS IO_Error ; An error occurred +ReadExit RTS ; Exit read routines +IO_Error LDA #XIOERROR ; I/O error + JSR SysErr ; Return to SOS with error in A + +; +; D_WRITE call processing +; +DWrite + LDA CardIsOK ; Did we previously find a card? + BNE DWriteGo + JMP NoDevice ; If not... then bail + +DWriteGo + JSR CkCnt ; Checks for validity + JSR CkUnit ; Checks for unit below unit max +CWrite LDA Num_Blks ; Check for Num_Blks greater than zero + ORA Num_Blks+1 + BEQ WriteExit ; Quantity to write is zero - so done + JSR FixUp + JSR Write_Block + BCS IO_Error +WriteExit RTS + +; +; D_STATUS call processing +; $00 = Driver Status +; $FE = Return preferrred bitmap location ($FFFF) +; +DStatus + LDA CardIsOK ; Did we previously find a card? + BNE DStatusGo + JMP NoDevice ; If not... then bail + +DStatusGo + LDA CFFAUnit ; Get the unit number we're talking about + STA shUnitNumber + LDA CtlStat ; Which status code to run? + BNE DS0 + LDA #kCmd3K_Status ; Status code 0 - return the status byte + JSR CFFA3K_API + BCS DS1 + LDY #$00 + LDA shStatusByte + STA (CSList),Y + JSR SaveCapacity + CLC + RTS +DS1 CMP #$2F ; Did we get a fancy new $2f error? + BNE DS2 + LDA #XDNFERR ; Then change it to XDNFERR instead. +DS2 JSR SysErr ; Return to SOS with error in A +DS0 CMP #$FE + BNE DSWhat + + LDY #$00 ; Return preferred bit map locations. + LDA #$FF ; 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 CardIsOK ; Did we previously find a card? + BNE DContGo + JMP NoDevice ; If not... then bail + +DContGo LDA CtlStat ; Control command + BEQ CReset + CMP #$FE ; Format? + BEQ DCFormat + JMP DCWhat ; Control code no good! +CReset JSR UnitInit ; Reset this unit + BCS DCNoReset +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 + +DCFormat +; +; Write Block0, Block1 to disk +; + LDX SOS_Unit ; Get the stats on this unit + LDY DCB_Idx,X + LDA DIB0_Blks,Y + STA VolBlks ; Fill VolBlks with capacity + LDA DIB0_Blks+1,Y + STA VolBlks+1 + LDA #$00 + STA VolBlks+2 + STA shBlockNumber + STA shBlockNumber+1 + STA shBlockNumber+2 + STA shBlockNumber+3 + + JSR ZeroFillC800 + LDA CFFAUnit ; Get the unit number we're talking about + STA shUnitNumber + LDA #kCmd3K_Write + JSR CFFA3K_API + BCS Error + INC shBlockNumber + LDA #kCmd3K_Write + JSR CFFA3K_API + BCS Error + JSR FormatFill + JSR Catalog ; Write Directory information to the disk + RTS +Error SEC + JSR SysErr ; Return to SOS with error in A + +;------------------------------------ +; +; Utility routines +; +;------------------------------------ + +; +; Read_Block - Read requested blocks from device into memory +; +Read_Block + LDA SosBuf ; Copy out buffer pointers + STA DataBuf + LDA SosBuf+1 + STA DataBuf+1 + LDA SosBuf+ExtPG + STA DataBuf+ExtPG + + LDA CFFAUnit + STA shUnitNumber ; Now we have the CFFA3000 unit + + LDA SosBlk + STA shBlockNumber + LDA SosBlk+1 + STA shBlockNumber+1 + LDA #$00 + STA shBlockNumber+2 + STA shBlockNumber+3 +Read3k LDA #kCmd3K_Read + JSR CFFA3K_API + BCC @1 ; Branch past error + STA LastError + CMP #XDISKSW + BNE @0 + JSR ZeroUnit + JSR UnitInit ; Re-initialize this unit + JMP Dispatch ; Go around again! +@0 LDA LastError + JSR SysErr ; Return to SOS with error in A +@1 LDA #$00 + STA C800Ptr + LDA #$C8 + STA C800Ptr+1 ; Establish a pointer to C800 + LDY #$00 +@2 LDA (C800Ptr),Y + STA (DataBuf),Y + INY + BNE @2 + JSR IncrAdr +@3 LDA (C800Ptr),Y + STA (DataBuf),Y + INY + BNE @3 + JSR IncrAdr + + DEC Num_Blks ; Did we get what was asked for? + BNE RdBlk2 + DEC Num_Blks+1 + BPL RdBlk2 + CLC + RTS + +RdBlk2 INC shBlockNumber ; 16-bit increment of block number + BNE Read3k + INC shBlockNumber+1 + JMP Read3k + +; +; Write_Block - write memory out to requested blocks +; +Write_Block + LDA SosBuf ; Copy out buffer pointers + STA DataBuf + LDA SosBuf+1 + STA DataBuf+1 + LDA SosBuf+ExtPG + STA DataBuf+ExtPG + + LDA SOS_Unit + STA shUnitNumber + CLC + INC shUnitNumber ; Now we have the CFFA3000 unit + + LDA SosBlk + STA shBlockNumber + LDA SosBlk+1 + STA shBlockNumber+1 + LDA #$00 + STA shBlockNumber+2 + STA shBlockNumber+3 + +Write3k LDA #$00 + TAY + STA C800Ptr + LDA #$C8 + STA C800Ptr+1 ; Establish a pointer to C800 +@2 LDA (DataBuf),Y + STA (C800Ptr),Y + INY + BNE @2 + JSR IncrAdr +@3 LDA (DataBuf),Y + STA (C800Ptr),Y + INY + BNE @3 + + LDA #kCmd3K_Write + JSR CFFA3K_API + BCC @1 ; Branch past error + STA LastError + CMP #XDISKSW + BNE @0 + JSR ZeroUnit + JSR UnitInit ; Re-initialize this unit + JMP Dispatch ; Go around again! +@0 LDA LastError + JSR SysErr ; Return to SOS with error in A + +@1 JSR IncrAdr + DEC Num_Blks ; Did we put what was asked for? + BNE WrBlk2 ; Not done yet... go around again + DEC Num_Blks+1 ; (16 bit decrement) + BPL WrBlk2 ; Not done yet... go around again + CLC + RTS ; We're done + +WrBlk2 INC shBlockNumber ; 16-bit increment of block number + BNE Write3k + INC shBlockNumber+1 + JMP Write3k + +; +; ZeroUnit - clear out the capacity bytes of this unit +; +ZeroUnit LDX SOS_Unit + LDY DCB_Idx,X + LDA #$00 + STA DIB0_Blks,Y + STA DIB0_Blks+1,Y + 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 Num_Blks+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 Num_Blks ; 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 error in A + + +IncrAdr INC C800Ptr+1 ; Increment buffer MSB from CFFA3000 + INC Count+1 ; Increment byte count MSB +BumpAdr 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 #$FD ; Is it the other one? + BCS @2 ; Yep. fix it! + RTS ; Pointer unchanged, return carry clear. +@1 LDA #$80 ; 00xx -> 80xx + STA DataBuf+1 + DEC DataBuf+ExtPG ; Bank N -> band N-1 + LDA DataBuf+ExtPG ; See if it was bank 0 + CMP #$7F ; (80) before the DEC. + BNE @3 ; Nope! all fixed. + LDA #$20 ; If it was, change both + STA DataBuf+1 ; Msb of address and + LDA #$8F + STA DataBuf+ExtPG ; Bank number for bank 8F + RTS ; Return carry set +@2 AND #$7F ; Strip off high bit + STA DataBuf+1 ; FDxx ->7Dxx + INC DataBuf+ExtPG ; Bank N -> bank N+1 +@3 RTS ; Return carry set + +CkUnit LDA SOS_Unit ; Checks for unit below unit max + CMP MaxUnits + BMI UnitOk +NoUnit LDA #$11 ; Report no unit to SOS + JSR SysErr +UnitOk CLC + RTS + +; +; Prepare BitMap and Link blocks for writing to disk +; Part of formatting support +; +FormatFill + LDA #$05 ; Block 5 on Disk + STA shBlockNumber + STA Storage ; Length of DirTbl + JSR ZeroFillC800 +LLink LDX Storage + LDA DirTbl,X ; Move Directory Link values into Buffer + STA $C802 ; Store next Directory block # + DEX + LDA DirTbl,X ; Fetch another # from DirTbl + STA $C800 ; Store previous Directory block # + DEX + STX Storage + LDA #kCmd3K_Write + JSR CFFA3K_API ; Write Directory Link values to disk +LDec DEC shBlockNumber ; Decrement MLI block number + LDA shBlockNumber ; See if MLIBlk = 2 + CMP #$02 + BNE LLink ; Process another Link block + +; +; Calculate BitMap Size and cndo +; Part of formatting support +; +BlkCount ; Fill full pages first, then remainder + LDA #$06 ; First block to deal with: $06 + STA shBlockNumber + CLC + LDA VolBlks+1 + STA FullPages + LDA VolBlks+2 + BEQ @1 + SEC +@1 ROR FullPages ; VolBlks is now divided by 512 + LSR FullPages ; ... by 1024 + LSR FullPages ; ... by 2048 + LSR FullPages ; ... by 4096 + + BEQ LastBlock ; No full blocks? Skip to remainder part. + + JSR FFFillC800 ; Set up to fill pages with $FFs + LDA #kCmd3K_Write + JSR CFFA3K_API + LDA #$00 + STA BlkCnt + STA $C800 ; Mark first blocks as used + STA $C801 + LDA #$03 + STA $C802 + +@2 + LDA #kCmd3K_Write + JSR CFFA3K_API ; Write Buffer BitMap to block on the disk + LDA #$FF ; Mark first blocks as unused again + STA $C800 + STA $C801 + STA $C802 + INC shBlockNumber + INC BlkCnt + LDA BlkCnt + CMP FullPages + BNE @2 + +LastBlock + JSR BlkRemainder + LDA #kCmd3K_Write + JSR CFFA3K_API + RTS + +BlkRemainder + JSR ZeroFillC800 + LDA VolBlks+1 ; Where # of blocks are stored + LDX VolBlks + LDY #$00 + STX Storage+1 ; Divide the # of blocks by 8 for bitmap + LSR A ; calculation + ROR Storage+1 + LSR A + ROR Storage+1 + LSR A + ROR Storage+1 + STA Storage+2 +BitMapCode + LDA FullPages ; Only tick off 7 blocks if + BNE BitMapNotFirst ; this is the only page in the BAM + LDA #$01 ; Clear first 7 blocks (i.e. %00000001) + STA (C800Ptr),Y + JMP BitMapGo +BitMapNotFirst + LDA #$FF + STA (C800Ptr),Y +BitMapGo + LDY Storage+1 ; Original low block count value + BNE Jump11 ; If it is 0 then make FF + DEY ; Make FF + DEC Storage+2 ; Make 256 blocks less one + STY Storage+1 ; Make FF new low block value +Jump11 LDX Storage+2 ; High Block Value + BNE Jump15 ; If it isn't equal to 0 then branch + LDY Storage+1 + JMP Jump19 + +Jump15 LDA #$C9 ; Set the address of the upper part of + STA C800Ptr+1 ; Block in bitmap being created + LDA #$FF + LDY Storage+1 ; Using the low byte count +Jump20 DEY + STA (C800Ptr),Y ; Store them + BEQ Jump17 + JMP Jump20 +Jump17 DEY ; Fill in first part of block + LDA #$C8 + STA C800Ptr+1 +Jump19 + LDA #$FF + DEY + STA (C800Ptr),Y + CPY #$01 ; Except the first byte. + BEQ Jump18 + JMP Jump19 +Jump18 RTS + +BlkCnt .BYTE $00 + +; +; Catalog - Build a Directory Track +; Part of formatting support +; +Catalog CLC + LDA #$06 + ADC FullPages + STA shBlockNumber + LDA #kCmd3K_Write + JSR CFFA3K_API ; Write Buffer (BitMap) to block #6 + JSR ZeroFillC800 + LDY #$2A ; Move Block2 information to $C800 +CLoop LDA Block2,Y + STA (C800Ptr),Y + DEY + BPL CLoop + LDA #$02 ; Write block #2 to the disk + STA shBlockNumber + LDA #kCmd3K_Write + JSR CFFA3K_API + RTS + +; +; FillC800: clear buffer at $C800 +; +FFFillC800 + LDA #$FF + PHA + JMP FillC800Go +ZeroFillC800 + LDA #$00 + PHA +FillC800Go + LDA #$C8 + STA C800Ptr+1 + LDA #$00 + STA C800Ptr + TAY + LDX #$01 ; Loop twice... 512 bytes + PLA +FillLoop + STA (C800Ptr),Y + INY + BNE FillLoop + INC C800Ptr+1 + DEX + BPL FillLoop + + LDA #$C8 + STA C800Ptr+1 + LDA #$00 + STA C800Ptr + RTS + +; +; Formatter Variable Storage Area +; +VolBlks .BYTE $00, $00, $00 ; Number of blocks available +DirTbl .BYTE $02, $04, $03 ; Linked list for directory blocks + .BYTE $05, $04, $00 +BitTbl .BYTE $7f ; '01111111' ; BitMap mask for bad blocks + .BYTE $bf ; '10111111' + .BYTE $df ; '11011111' + .BYTE $ef ; '11101111' + .BYTE $f7 ; '11110111' + .BYTE $fb ; '11111011' + .BYTE $fd ; '11111101' + .BYTE $fe ; '11111110' +Storage .BYTE $00, $00, $00 ; General purpose counter/storage byte +Pointer .BYTE $00, $00 ; Storage for track count (8 blocks/track) +Track .BYTE $00, $00 ; Track number being FORMATted +Sector .BYTE $00, $00 ; Current sector number (max=16) +SlotF .BYTE $00, $00 ; Slot/Drive of device to FORMAT +TRKcur .BYTE $00, $00 ; Current track position +TRKdes .BYTE $00, $00 ; Destination track position +TRKbeg .BYTE $00 ; Starting track number +TRKend .BYTE $35 ; Ending track number +FullPages + .BYTE $00 ; Number of BAM pages to fill +DevIndex .BYTE $00 ; Space for index into DEVICES table +Util .BYTE $00 + + +Block2 .BYTE $00, $00, $03, $00 ; Image of block 2 - for $42 bytes +VolLen .BYTE $F5 ; $F0 + length of Volume Name +Volnam .BYTE "BLANK " ; Volume Name +Reserved .BYTE $00, $00, $00, $00, $00, $00 +UpLowCase + .BYTE $00, $00 +Datime .BYTE $00, $00, $00, $00 +Version .BYTE $01 + .BYTE $00, $C3, $27, $0D + .BYTE $00, $00, $06, $00 + + .ENDPROC + .END \ No newline at end of file diff --git a/drivers/cffa3000/README.md b/drivers/cffa3000/README.md new file mode 100644 index 0000000..fce5334 --- /dev/null +++ b/drivers/cffa3000/README.md @@ -0,0 +1 @@ +# Apple /// driver for the Compact Flash for Apple 3000 Interface Card \ No newline at end of file diff --git a/drivers/vsdrive/README.md b/drivers/vsdrive/README.md new file mode 100644 index 0000000..a029bf6 --- /dev/null +++ b/drivers/vsdrive/README.md @@ -0,0 +1 @@ +# Apple /// driver for ADTPro Virtual Serial Drive \ No newline at end of file diff --git a/drivers/vsdrive/vsdrive.txt b/drivers/vsdrive/vsdrive.txt new file mode 100644 index 0000000..cc4da3e --- /dev/null +++ b/drivers/vsdrive/vsdrive.txt @@ -0,0 +1,746 @@ +; 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