MMC Source Code

Here is an example of a driver that reads and writes a 16 MB SanDisk MultiMediaCard (MMC). It is written in 8088 assembly language.

Schematic

;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.


Links


Back to the XPL0 home page

Last updated: 17-Jan-2005