*************************** * MMC64 Programming Guide * *(C) 2005 by Oliver Achten* *************************** V0.2 This document adresses coders, who intend to write software for the MMC64 interface card. Being a document for coders, most issues are explained using 6502 assembly code examples. The code is optimized for readability, not for speed. Have fun! ;-) 1. Resetting the Multimedia / Secure Digital Card ------------------------------------------------- Coding for the MMC64 is about learning how SD/MMC flash cards work, as they are not a dumb array of memory cells, but intelligent devices of their own with a unique way of handling. Commucation with the flash card is implemented using a high-speed serial interface running at 8x CPU speed compared to a stock C64. The first way to start should be to initialize the card into a definite state. This is accomplished by deselecting the card and sending 80 clock pulses at 250khz: ; Initialize a flash card plugged into the MMC64 .initmmc64 lda $df11 ;get contents of MMC64 control register and #%10111011 ;set 250khz & write trigger mode ora #%00000010 ;disable card select sta $df11 ;update control register ldx #$0a ;initialize counter (10*8 pulses) ldy #$ff ;initialize value to be written (bits must be all high) .mmcinitloop sty $df10 ;send 8 pulses to the card .busywait lda $df12 ;we catch the status register and #$01 ;all bits shiftet out? bne .busywait ;nope, so wait dex ;decrement counter bne .mmcinitloop;until 80 pulses sent lda $df11 ;pull card chip select line down for SPI communication and #%11111101 sta $df11 rts Note that in order to reset the card properly, the interface must be set to 250khz mode, which means we have to pull the status register to ensure that all data bits have been written to the card. Also note that the interface is operating now in write trigger mode, which means every byte written to $df10 is sent to the card immediately, while simultaneously the card sends a reponse byte to the interface which can be read by reading $df10 without re-triggering another transfer. Now, after initialization is completed, we proceed by sending the reset command to the card. Notice that we are still operating in 250khz mode, which is necessary to ensure compatibility with MMC Cards (Secure Digital Cards donīt need it). I the technical reference manual, this command is referred to as "CMD0" ;Reset the flash card .resetmmc64 ldy #$09 ;we send 8 command bytes to the card .mmc64resetloop lda .resetcmd-1,y ;grab command byte sta $df10 ;and fire it to the card .resetbusy lda $df12 ;we check the busy bit and #$01 ;to ensure that the transfer is safe bne .resetbusy dey ;decrease command counter bne .mmc64resetloop ;until the entire command has been sent lda $df10 ;now we check the card response and #$01 ;did it accept the command ? bne .mmcresponded ;ok, everything is fine ! lda $df12 ;is there a card at all ? and #$08 beq .resetmmc64 ;yup, so we just resend the command .mmcresponded rts .resetcmd .byte $ff,$ff,$95,$00,$00,$00,$00,$40,$ff ;CMD0 I found out that if you interrupt the card from a previous data transfer (resetting the C64 while loading a file), the card needs multiple reset commands to bring it back in a definite state. Thatīs why you have to check the card response and keep on sending reset commands if the card is still in the slot. Now the card is in its initialization stage, and we have to check permanently its status until initialization is complete (weīre still operating at 250khz). In the technical reference manual, this command is referred to as "CMD1" ;check if card has left initialization .initwaitmmc64 ldy #$09 ;we send 8 command bytes to the card .initwaitloop lda .initwaitcmd-1,y ;grab command byte sta $df10 ;and fire it to the card .initwaitbusy lda $df12 ;we check the busy bit and #$01 ;to ensure that the transfer is safe bne .initwaitbusy dey ;decrease command counter bne .initwaitloop ;until entire command has been sent lda $df10 ;did the card left its init state? and #$01 bne .initwaitmmc64 ;no, so we resend the command to check it again rts .initwaitcmd .byte $ff,$ff,$ff,$00,$00,$00,$00,$41,$ff ;CMD1 Done! Now we have completely initialized the flash card, which is ready now for all different kinds of operation. You can now enable the 8Mhz mode for high speed communication. ;Enable 8Mhz mode lda $df11 ora #%00000100 sta $df11 2. Block Read / Block Write --------------------------- Now that we have initialised the card, we want to do some serious stuff, namely getting the data out of or into the card. Data transfers between the card and the C64 are by default done in sizes of 512 bytes, called blocks. To initialize a block transfer, we have to send the appropriate command first, and then suck the data out of the card as fast as possible. Since we have now enabled the 8Mhz transfer mode, there is no need to pull the busy bit anymore, because the data is written to the card before the CPU can decode the next instruction! The block read command is called "CMD17" in the technical reference manual. ;Block read command - set up a 512 byte transfer from $00000100 to $000002ff of the ;SD Card into C64 memory .blockreadcmd ldy #$09 .blockreadcmdloop lda .blockrcmd-1,y sta $df10 dey bne .blockreadcmdloop rts .blockrcmd .byte $ff,$ff,$ff,$00,$01,$00,$00,$51,$ff ;CMD17 This little routine sends the block read command. Notice that we can now fire data to the card at maximum speed. Here is a little explanation of the command structure: byte 1: $FF = sync header byte 2: $51 = command opcode (CMD17 = $11 + $40) byte 3-6: 32bit memory address to read [31..0] byte 7: CRC checksum (disabled by default, just write $FF) byte 8: $FF = end sync header After sending the command, we have to wait until the card is ready to transfer the data. Fortunately, this is very easy. Just wait until the card responds with $FE. ;Wait until card is ready .waitformmcdata lda #$ff sta $df10 ;write all high bits lda $df10 ;to give the possibility to respond cmp #$fe ;has it started? bne .waitformmcdata ;nop, so we continue waiting rts And finally, we can now read the 512 byte data block in one piece: ; Transfer 512 bytes from card into memory .blockread lda $df11 ;set MMC64 into read trigger mode ora #%01000000 ;which means every read triggers a SPI transfer sta $df11 lda $df10 ;we have to start with one dummy read here ldx #$02 ;set up counters ldy #$00 .sectorcopyloop lda $df10 ;get data byte from card sta (memptr),y ;store it into memory ( memptr has to be initialized) iny ;have we copied 256 bytes ? bne .sectorcopyloop ;nope, so go on! inc memptr+1 ;increase memory pointer for next 256 bytes dex ;have we copied 512 bytes ? bne .sectorcopyloop lda $df10 ;we have to end the data transfer with one dummy read lda $df11 ;now we put the hardware back into write trigger mode again and #%10111111 sta $df11 rts If our data transfer stops while a block has not completely been read (for example the file size is not dividable by 512), we have to send a stop command to the card. Otherwise, the card will behave very irregular, which has to be corrected by multiple reset commands. This command is refereed to as "CMD12" in the technical reference manual ;Stop the data transfer .stopcmd ldy #$09 .stopcmdloop lda .stopcmd-1,y sta $df10 dey bne .stopcmdloop rts .stopcmd .byte $ff,$ff,$ff,$00,$00,$00,$00,$4c,$ff ;CMD12 Now we continue with the block write command. Unlike block reads, block writes have no default block size, which means we have to set the data transfer size manually. ;Set blocklength to 512 bytes .sizecmd ldy #$09 .sizecmdloop lda .sizecmd-1,y sta $df10 dey bne .sizecmdloop rts .sizecmd .byte $ff,$ff,$ff,$00,$02,$00,$00,$50,$ff ;CMD16 Having taken care of this formality, we may now proceed by sending the block-write command. ;Block write command - set up a 512 byte transfer to the SD Card from $00000100 to $000002ff .blockwritecmd ldy #$09 .blockwritecmdloop lda .blockwcmd-1,y sta $df10 dey bne .blockwritecmdloop rts .blockwcmd .byte $ff,$ff,$ff,$00,$01,$00,$00,$58,$ff ;CMD24 And now we can write the data to the card: .blockwrite ldx #$ff ;we send the sequence $ff,$ff,$fe to start the transfer stx $df10 stx $df10 dex stx $df10 ldx #$02 ;set up counters ldy #$00 .sectorcopyloop lda (memptr),y ;get byte from C64 memory sta $df10 ;give it to the card iny ;have we copied 256 bytes ? bne .sectorcopyloop inc memptr+1 ;increase memory pointer for next 256 bytes dex ;have we copied 512 bytes ? bne .sectorcopyloop lda #$ff ;send 2 sync bytes sta $df10 sta $df10 .blockwritebusy sta $df10 ;wait until card has left busy state ldx $df10 cpx #$ff bne .blockwritebusy rts 3. Thatīs all for now ! ----------------------- This document is under permanent construction, and i hope i have shed some light on the dark mysteries of SD/MMC card programming. Finally, i want to wish you good luck with your upcoming projects, and thank you for your interest in programming the MMC64! :-) Oliver Achten