******************************** * * Apple][Sd Firmware * Version 0.5 * * (c) Florian Reitz, 2017 * * X register usually contains SLOT16 * Y register is used for counting or SLOT * ******************************** DAT XC ; enable 65C02 code DEBUG = 0 DO DEBUG ORG $8000 ELSE ORG $C800 ; Expansion ROM FIN * Memory defines SLOT16 = $2B ; $s0 -> slot * 16 WORK = $3C SLOT = $3D ; $0s CMDLO = $40 CMDHI = $41 CURSLOT = $07F8 ; $Cs DATA = $C080 CTRL = DATA+1 DIV = DATA+2 SS = DATA+3 R30 = $0478 R31 = $04F8 R32 = $0578 R33 = $05F8 INITED = $0678 * Constants SSNONE = $0F SS0 = $0E DUMMY = $FF ******************************** * * Install SD card driver * ******************************** * signature bytes LDX #$20 LDY #$00 LDX #$03 ===== Page 2 ===== STX WORK * find slot nr DO DEBUG LDA #$04 STA SLOT LDA #$C4 STA CURSLOT LDA #$40 STA SLOT16 ELSE JSR $FF58 TSX LDA $0100,X STA CURSLOT ; $Cs AND #$0F STA SLOT ; $0s ASL A ASL A ASL A ASL A STA SLOT16 ; $s0 FIN TAX ; X holds now SLOT16 BIT $CFFF JSR INIT DO 0 * * TODO: check for init error * * see if slot has a driver already LDX $BF31 ; get devcnt INSLP LDA $BF32,X ; get a devnum AND #$70 ; isolate slot CMP SLOT16 ; slot? BEQ INSOUT ; yes, skip it DEX BPL INSLP ; keep up the search * restore the devnum to the list LDX $BF31 ; get devcnt again CPX #$0D ; device table full? BNE INSLP2 JSR $FF3A ; bell JMP INSOUT ; do something! INSLP2 LDA $BF32-1,X ; move all entries down STA $BF32,X ; to make room at front DEX ; for a new entry BNE INSLP2 ===== Page 3 ===== LDA #$04 ; ProFile type device ORA SLOT16 STA $BF32 ; slot, drive 1 at top of list INC $BF31 ; update devcnt * now insert the device driver vector LDA SLOT ASL TAX LDA #DRIVER ELSE LDA CURSLOT FIN STA $BF11,X INSOUT RTS FIN BOOT LDA #$01 STA $42 ; load command LDA SLOT16 TAX STA $43 ; slot number STZ $44 ; buffer lo LDA #$08 STA $45 ; buffer hi STZ $46 ; block lo STZ $47 ; block hi BIT $CFFF JSR READ ; call driver JMP $801 ; goto bootloader ******************************** * * Jump table * ******************************** DRIVER CLD DO DEBUG LDA #$04 STA SLOT LDA #$C4 STA CURSLOT LDA #$40 STA SLOT16 ELSE JJR $FF58 ; find slot nr TSS LDA $0100,X STA CURSLOT ; $Cs AND #$0F ===== Page 4 ===== STA SLOT ; $0s ASL A ASL A ASL A ASL A STA SLOT16 ; $s0 FIN TAX ; X holds now SLOT16 BIT $CFFF LDY SLOT LDA INITED,Y ; check for init CMP #$01 BCC :INIT :CMD LDA $42 ; get command CMP #$00 BEQ :STATUS CMP #$01 BEQ :READ CMP #$02 BEQ :WRITE CMP #$03 BEQ :FORMAT SEC ; unknown command LDA #$01 RTS :STATUS JMP STATUS :READ JMP READ :WRITE JMP WRITE :FORMAT JMP FORMAT :INIT JSR INIT BRA :CMD * Signature bytes DS \ ; fill with zeroes DS -4 ; locate to $C8FC DW $FFFF ; 65535 blocks DB $47 ; Status bits DB #CMD0 STA CMDHI JSR CMD JSR GETR1 ; get response CMP #$01 BNE :ERROR1 ; error! LDA #CMD8 STA CMDHI JSR CMD JSR GETR3 CMP #$01 BNE :SDV1 ; may be SD Ver. 1 * check for $01aa match! :SDV2 LDA #CMD55 STA CMDHI JSR CMD JSR GETR1 LDA #ACMD4140 STA CMDHI JSR CMD JSR GETR1 CMP #$01 BEQ :SDV2 ; wait for ready CMP #$00 BNE :ERROR1 ; error! * send CMD58 * SD Ver. 2 initialized! JMP :BLOCKSZ :ERROR1 JMP :IOERROR ; needed for far jump ===== Page 6 ===== :SDV1 LDA #CMD55 STA CMDHI JSR CMD ; ignore response LDA #ACMD410 STA CMDHI JSR CMD JSR GETR1 CMP #$01 BEQ :SDV1 ; wait for ready CMP #$00 BNE :MMC ; may be MMC card * SD Ver. 1 initialized! JMP :BLOCKSZ :MMC LDA #CMD1 STA CMDHI :LOOP1 JSR CMD JSR GETR1 CMP #$01 BEQ :LOOP1 ; wait for ready CMP #$00 BNE :IOERROR ; error! * MMC Ver. 3 initialized! :BLOCKSZ LDA #CMD16 STA CMDHI JSR CMD JSR GETR1 CMP #$00 BNE :IOERROR ; error! :END LDY SLOT ; all ok LDA #$01 STA INITED,Y ; initialized CLC LDY #0 BCC :END1 :CDERROR SEC LDY #$28 ; no card error BCS :END1 :IOERROR SEC LDY #$27 ; init error :END1 LDA #SSNONE ; deselect card STA SS,X LDA #0 ; set div to 2 STA DIV,X TYA ; retval in A RTS ===== Page 7 ===== ******************************** * * Send SD command * Call with command in CMDHI and CMDLO * ******************************** CMD PHY LDY #0 :LOOP LDA (CMDLO),Y STA DATA,X :WAIT BIT CTRL,X ; TC is in N BPL :WAIT INY CPY #6 BCC :LOOP PLY RTS ******************************** * * Get R1 * R1 is in A * ******************************** GETR1 LDA #DUMMY STA DATA,X :WAIT BIT CTRL,X BPL :WAIT LDA DATA,X ; get response STA WORK ; save R1 AND #$80 BNE GETR1 ; wait for MSB=0 LDA #DUMMY STA DATA,X ; send another dummy LDA WORK ; restore R1 RTS ******************************** * * Get R3 * R1 is in A * R3 is in scratchpad ram * ******************************** GETR3 JSR GETR1 ; get R1 first PHA ; save R1 PHY ; save Y LDY #04 ; load counter :LOOP LDA #DUMMY ; send dummy STA DATA,X :WAIT BIT CTRL,X ===== Page 8 ===== BPL :WAIT LDA DATA,X PHA DEY BNE :LOOP ; do 4 times LDY SLOT PLA STA R33,Y ; save R3 PLA STA R32,Y PLA STA R31,Y PLA STA R30,Y PLY ; restore Y LDA #DUMMY STA DATA,X ; send another dummy PLA ; restore R1 RTS ******************************** * * Calculate block address * Block no is in $46-47 * Address is in R30-R33 * ******************************** BLOCK PHX ; save X PHY ; save Y LDX SLOT LDA $46 ; store block num STA R33,X ; in R30-R33 LDA $47 STA R32,X LDA #0 STA R31,X STA R30,X LDY #9 ; ASL can't be used with Y :LOOP ASL R33,X ; mul block num ROL R32,X ; by 512 to get ROL R31,X ; real address ROL R30,X DEY BNE :LOOP PLY ; restore Y PLX ; restore X RTS ******************************** * * Send SD command * Cmd is in A * ===== Page 9 ===== ******************************** COMMAND PHY ; save Y LDY SLOT STA DATA,X ; send command :WAIT BIT CTRL,X BPL :WAIT :ARG LDA R30,Y ; get arg from R30 on STA DATA,X :WAIT1 BIT CTRL,X BPL :WAIT1 LDA R31,Y STA DATA,X :WAIT11 BIT CTRL,X BPL :WAIT11 LDA R32,Y STA DATA,X :WAIT12 BIT CTRL,X BPL :WAIT12 LDA R33,Y STA DATA,X :WAIT13 BIT CTRL,X BPL :WAIT13 LDA #DUMMY STA DATA,X ; dummy crc :WAIT2 BIT CTRL,X BPL :WAIT2 JSR GETR1 PLY ; restore Y RTS ******************************** * * Status request * $43 Unit number DSSS000 * $44-45 Unused * $46-47 Unused * * C Clear - No error * Set - Error * A $00 - No error * $27 - I/O error * $28 - No card inserted / no init * $2B - Card write protected * x - Blocks avail (low byte) * y - Blocks avail (high byte) * ******************************** STATUS CLC ; no error LDA #0 LDX #$FF ; 32 MB partition LDY #$FF RTS * TODO: check for card detect and write protect! ===== Page 10 ===== ******************************** * * Read 512 byte block * $43 Unit number DSSS0000 * $44-45 Address (LO/HI) of buffer * $46-47 Block number (LO/HI) * * C Clear - No error * Set - Error * A $00 - No error * $27 - Bad block number * $28 - No card inserted * ******************************** * TODO: check for card detect! READ JSR BLOCK ; calc block address LDA #SS0 ; enable /CS STA SS,X LDA #$51 ; send CMD17 JSR COMMAND ; send command :GETTOK LDA #DUMMY ; get daata token STA DATA,X :WAIT BIT CTRL,X BPL :WAIT LDA DATA,X ; get response * * TODO: check for error! * CMP #$FE BNE :GETTOK ; wait for $FE LDY #2 ; read data from card :LOOPY STZ WORK :LOOPW LDA #DUMMY STA DATA,X :WAIT1 BITT CTRL,X BPL :WAIT1 LDA DATA,X STA ($44) INC $44 BNE :INW INC $45 ; inc msb on page boundary :INW INC WORK BNE :LOOPW DEY BNE :LOOPY JSR GETR3 ; read 2 bytes crc LDA #SSNONE STA SS,X ; disable /CS CLC ; no error ===== Page 11 ===== LDA #$00 RTS ******************************** * * Write 512 byte block * $43 Unit number DSSS000 * $44-45 Address (LO/HI) of buffer * $46-47 Block number (LO/HI) * * C Clear - No error * Set - Error * A $00 - No error * $27 - I/O error or bad block number * $28 - No card inserted * $2B - Card write protected * ******************************** * TODO: check for card detect and write protect! WRITE JSR BLOCK ; calc block address LDA #SS0 ; enable /CS STA SS,X LDA #$58 ; send CMD24 JSR COMMAND ; send command LDA #DUMMY STA DATA,X ; send dummy :WAIT1 BIT CTRL,X BPL :WAIT1 LDA #$FE STA DATA,X ; send data token :WAIT2 BIT CTRL,X BPL :WAIT2 LDY #2 ; send data to card :LOOPY STZ WORK :LOOPW LDA ($44) STA DATA,X :WAIT3 BIT CTRL,X BPL :WAIT3 INC $44 BNE :INW INC $45 ; inc msb on page boundary :INW INC WORK BNE :LOOPW DEY BNE :LOOPY LDY #2 ; send 2 dummy crc bytes :CRC STA DATA,X :WAIT4 BIT CTRL,X BPL :WAIT4 DEY ===== Page 12 ===== BNE :CRC LDA #DUMMY ; get data response STA DATA,X :WAIT5 BIT CTRL,X BPL :WAIT5 LDA DATA,X AND #$1F CMP #$05 BNE :ERROR ; check for write error :WAIT6 LDA #DUMMY ; wait for write cycle STA DATA,X ; to complete :WAIT61 BIT CTRL,X BPL :WAIT61 LDA DATA,X CMP #$00 BEQ :WAIT6 LDA #SSNONE ; disablee CS STA SS,X CLC ; no error LDA #0 RTS :ERROR :WAIT7 LDA #DUMMY ; wait for write cycle STA DATA,X ; to complete :WAIT71 BIT CTRL,X BPL :WAIT71 LDA DATA,X CMP #$00 BEQ :WAIT7 LDA #SSNONE STA SS,X ; disable /CS SEC ; an error occured LDA #$27 RTS ******************************** * * Format * not supported! * ******************************** FORMAT SEC LDA #$01 ; invalid command RTS CMD0 HEX 400000 HEX 000095 CMD1 HEX 410000 HEX 0000F9 CMD8 HEX 480000 ===== Page 13 ===== HEX 01AA87 CMD16 HEX 500000 HEX 0200FF CMD55 HEX 770000 HEX 000065 ACMD4140 HEX 694000 HEX 000077 ACMD410 HEX 690000 HEX 0000FF