;MMC.ASM 19-AUG-2001 Loren Blaney loren_blaney@idcomm.com ;Example of a driver for a SanDisk MultiMediaCard (MMC) ;Hardware design by Richard Ottosen http://www.idcomm.com/personal/ottosen ; ;The MMC is connected to a MC68230 parallel I/O chip. The three outputs (PA4-6) ; are inverted by 74LS05 open collector gates, which perform a level shift ; from 5.0 to 3.2 volts. ; PA4 MMC chip select (CS) ; PA5 output data to MMC (-DataIn) ; PA6 output clock to MMC (-Clk) ; H3 input data from MMC (DataOut) ; ;The following test code gives an example of how the MMC routines should be ; called. The "lcdout" procedure (not included) displays the character in the ; al register. p1addr equ 4002h ;port A data direction register p1adr equ 4008h ;port A data register p1sr equ 400Dh ;status register mmctest: mov dx, p1addr ;set up data direction register mov al, 70h ;1 = output out dx, al call InitMMC ;initialize MMC jnc mf10 ;jump if no error detected mov al, 'X' ;else display error message (X) on LCD call lcdout ;(debug code) mf10: mov ax, 89 ;sector number to read into buffer mov bx, offset buffer ;512-byte scratch space call ReadSec jnc mf20 mov al, 'Y' ;error message call lcdout mf20: mov ax, 99 ;sector number to write from buffer mov bx, offset buffer call WriteSec jnc mf30 mov al, 'Z' ;error message call lcdout mf30: jmp $ ;hang (end of test) ;end of test code ;============================ MultiMediaCard (MMC) ============================= CSb equ 10h ;MMC CS bit MOSI equ 20h ;MMC DataIn bit CLOCK equ 40h ;MMC Clk bit MISO equ 40h ;MMC DataOut bit (H3 bit 6 on p1sr) ;------------------------------------------------------------------------------- ;Initialization at power-up. Carry flag is set if an error is detected. ; InitMMC:push ax ;save registers push cx push bx ;(must be last) call RaiseCS mov cx, 10 ;send at least 74 clock cycles im10: call GetByte ;send 10 dummy bytes = 80 clocks loop im10 call LowerCS xor ax, ax ;send command 0 to put MMC in SPI mode xor bx, bx ;SendCmd(0, 0, 95h); mov cl, 95h ;CRC (ch=0) call SendCmd cmp al, 01h ;if response is not = 01h then exit stc ; with carry flag set jne im90 im25: call GetByte ;8 clocks to digest operation mov al, 1 ;send command 1 (argument and CRC don't matter) call SendCmd ;(OCR voltage profile is not used in SPI mode) cmp al, 01h ;loop until response not = 01h je im25 add al, 0FFh ;set carry flag if response not = 00h im90: pushf ;save carry flag call RaiseCS ;deselect call GetByte ;8 more clocks to digest operation popf ;restore carry flag pop bx ;restore registers pop cx pop ax ret ;------------------------------------------------------------------------------- ;Read sector. Carry flag is set if an error is detected. ; Inputs: ; ax = (logical) sector number [0..32767] to read from ; bx = address of 512-byte memory buffer to read sector into ; ReadSec:push ax ;save registers push cx push bx ;(must be last) add ax, ax ;bx:= sector * 2 mov bx, ax call LowerCS mov ax, 17 ;read command xor cx, cx ;dummy CRC (not normally used in SPI mode) call SendCmd add al, 0FFh ;set carry flag if response not = 00h jc rb90 call GetResp ;read start byte FEh pop bx ;get buffer address push bx mov cx, 512 ;for 512 bytes... rb20: call GetByte ;read byte from MMC mov [bx], al ;store it into buffer inc bx loop rb20 call GetByte ;read 16-bit checksum and ignore it call GetByte clc ;indicate successful operation rb90: jmp im90 ;exit ;------------------------------------------------------------------------------- ;Write sector. Carry flag is set if an error is detected. ; Inputs: ; ax = (logical) sector number [0..32767] to write to ; bx = address of 512-byte memory buffer from which to get data to write ; WriteSec: push ax ;save registers push cx push bx ;(must be last) add ax, ax ;bx:= sector * 2 mov bx, ax call LowerCS mov ax, 24 ;write command xor cx, cx ;dummy CRC (not normally used in SPI mode) call SendCmd add al, 0FFh ;set carry flag if response not = 00h jc wb90 ; call GetByte ;should send 8 clocks for Nwr but doesn't matter mov al, 0FEh ;start byte call SendByte pop bx ;get buffer address push bx mov cx, 512 ;for 512 bytes... wb20: mov al, [bx] ;get byte from buffer inc bx call SendByte ;send it to MMC loop wb20 call GetByte ;send dummy 16-bit checksum (=FFFFh) call GetByte call GetByte ;read "data response" byte (=xxx00101b) ; only verifies CRC, which isn't used wb30: call GetByte ;loop until not busy cmp al, 0 ;(it can take awhile to complete a write block) je wb30 clc ;indicate successful operation wb90: jmp im90 ;exit ;------------------------------------------------------------------------------- ;Send command to MMC and return response byte in al ; Inputs: ax, bx, cx ; SendCmd:or al, 40h ;send al = command call SendByte mov al, ah ;send ah = arg0 call SendByte mov al, bh ;send bh = arg1 call SendByte mov al, bl ;send bl = arg2 call SendByte mov al, ch ;send ch = arg3 call SendByte mov al, cl ;send cl = CRC ; or al, 01h ;LSB should be set, but it doesn't matter call SendByte ;fall into GetResp ;------------------------------------------------------------------------------- ;Response comes 1-8 bytes after a command. Input will be 0FFh in the mean time. ; GetResp:push cx ;save register mov cx, 64 ;loop a maximum of 64 times (8 is not enough!) gr10: call GetByte ;get response byte cmp al, 0FFh loope gr10 ;loop while byte = FFh and count not expired pop cx ret ;------------------------------------------------------------------------------- ;Receive a data byte from MMC ; GetByte:mov al, 0FFh ;fall into SendByte ;------------------------------------------------------------------------------- ;Send data byte in "SPI master" fashion. Also receives a byte. Byte to send is ; in register al and received byte is returned in al. Bits are shifted MSB first ; Register ah is unchanged. ; SendByte: push bx ;save registers push cx push dx mov bl, al ;save byte to output mov dx, p1sr ;set up to read H3, MMC's DataOut mov cx, 8 ;for 8 bits... sb10: call InPort ;lower clock and al, not CLOCK call OutPort and al, not MOSI ;output MSB of bl shl bl, 1 ;shift out byte to output jnc sb20 or al, MOSI sb20: call OutPort or al, CLOCK ;raise clock call OutPort in al, dx ;read MMC's DataOut shl al, 1 ;copy bit 6 into high bit of bh shl al, 1 rcl bh, 1 ;shift in received bits, MSB first loop sb10 ;loop for 8 bits mov al, bh ;return input byte in register al pop dx ;restore registers pop cx pop bx ret ;------------------------------------------------------------------------------- ;Raise Chip Select (CS) line ; RaiseCS:push ax call InPort or al, CSb ;set CS bit rcs80: call OutPort pop ax ret ;------------------------------------------------------------------------------- ;Lower Chip Select (CS) line ; LowerCS:push ax call InPort and al, not CSb ;clear CS bit jmp rcs80 ;------------------------------------------------------------------------------- ;Read the MMC output port latch register ; InPort: push dx mov dx, p1adr in al, dx xor al, 70h ;compensate for inverters pop dx ret ;------------------------------------------------------------------------------- ;Output to MMC port ; OutPort:push dx mov dx, p1adr xor al, 70h ;compensate for inverters out dx, al xor al, 70h ;compensate for inverters pop dx ret
SanDisk makes an adapter called a FlashPath that enables an MMC to be read and written in a standard 3.5-inch floppy drive. The first 32 sectors on the MMC are reserved for a partition table, which makes them inaccessible to the FlashPath. If you write to these sectors, destroying the partition table, the FlashPath will not be able to read your MMC.
DOS stores its directory starting at (logical) sector 57. (The first sector is 0.) The first file will be stored starting at sector 89.
Last updated: 17-Jan-2005