***************************
* 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