; CPLD REGISTERS ; SPI_PORT = 0x3F OUT_PORT = 0x1F ; port for CS control (D1:D0) MMC_0 = 0xF6 ; D0 LOW = SLOT0 active; D3 low = NMI disabled MMC_1 = 0xF5 ; D1 LOW = SLOT1 active; D3 low = NMI disabled ; SD/MMC RELATED DEFINES: MMC_SNAP16 = 1 ; il cluster contiene uno snapshot da 16K MMC_GO_IDLE_STATE = 0x40 MMC_SEND_OP_COND = 0x41 MMC_READ_CID = 0x4A MMC_SET_BLOCK_SIZE = 0x50 MMC_READ_SINGLE_BLOCK = 0x51 MMC_READ_MULTIPLE_BLOCK = 0x52 MCC_TERMINATE_MULTI_READ = 0x4C MMC_WRITE_SINGLE_BLOCK = 0x58 MMC_WRITE_MULTIPLE_BLOCK = 0x59 MMC_STOP_TRAN = 0xFD BLOCKSIZE = 512 ; SD/MMC block size (bytes) FATSIZE = 8 ; dimensioni FAT in termini di blocchi da 64KBytes (in pratica e` l'offset ; da caricare nella word MSB dell'indirizzo della SD per accedere al primo ; cluster di dati, subito oltre la FAT). 8 = 512KBytes (8192 entries da 64 bytes) .area _DATA .globl _found_cards .globl _buff_ptr .globl temp_ram .globl _block_buffer .globl _partition_start .globl _cluster_size .globl _fat_start card_select: .ds 1 _found_cards: .ds 1 _buff_ptr: .ds 2 _block_buffer: .ds 512 temp_ram: .ds 16 .area _CODE .globl _init .globl need_init .globl mmc_init .globl mmc_send_command .globl mmc_waitdata_token .globl mmc_wait_response .globl mmc_read_data _init: di ;; in a,(RXREG) ; clears UART RX register ld hl, #_block_buffer ld (_buff_ptr),hl ld c,#0 ; found cards ld a,#MMC_0 ; SLOT 0 ENABLE (also disables NMI on RS-232 byte reception (D3 low)) ld (card_select),a call sd_check ; controlla eventuale presenza di SD/MMM cp #0 jr nz,no_detect0 ; salta se la card non e` stata rilevata inc c no_detect0: ld a,c ld (_found_cards),a ld b,#0 ei ret sd_check: push bc ld hl,#temp_ram ; tenta di leggere il CID di una eventuale SD/MMC salvandolo a TEMP_RAM call mmc_getcid_poweron pop bc ret mmc_getcid_poweron: call mmc_get_cid ; try to read the MMC CID INFO --> (HL) cp #0 ; if it fails, then che SD/MMC needs to be initialized ret z ; to SPI mode (MMC_INIT) communications (once after power-on) cp #255 ; card is probably not in SPI mode jr z,need_init bit 0,a ; IDLE/still initializing bit jr nz,need_init ld a,#1 ; unknown response: exit ret need_init: call mmc_init cp #0 ret nz ; INIT error: MMC not detected. ld de,#BLOCKSIZE call mmc_send_blocksize jr mmc_getcid_poweron ;----------------------------------------------------------------------------------------- ; READ MMC CID subroutine. Data is stored at address (HL). ; ; Returns error code in A register: ; 0 = no error ; 1 = Read CID command error ; 2 = no wait_data token from MMC ; ; Destroys AF, HL. ;----------------------------------------------------------------------------------------- mmc_get_cid: push bc push de push hl ld hl,#0 ld de,#0 ld a,#MMC_READ_CID call mmc_send_command pop hl cp #0 jr z,cmd_cidok ; MMC_SEND_COMMAND reports 0 (ok) or MMC error code push af ; error code on STACK jr cid_exit cmd_cidok: call mmc_waitdata_token cp #0xFE jr z,waitda_cid_ok ld a,#2 push af jr cid_exit waitda_cid_ok: ld b,#18 ; 16 bytes + CRC? ld c,#SPI_PORT inir ;mmc_cidl ; ini ; jr nz,mmc_cidl xor a push af cid_exit: call cs_high ; set cs high in a,(SPI_PORT) pop af ; error code pop de pop bc ret ;----------------------------------------------------------------------------------------- ; MMC SPI MODE initialization. RETURNS ERROR CODE IN A register: ; ; 0 = OK ; 1 = Card RESET ERROR ; 2 = Card INIT ERROR ; ; Destroys AF, B. ;----------------------------------------------------------------------------------------- mmc_init: call cs_high ; set cs high ld b,#10 ; sends 80 clocks ld a, #0xFF l_init: out (SPI_PORT),a djnz l_init nop call cs_low ; set cs low ld a, #MMC_GO_IDLE_STATE call mmc_write_command call mmc_wait_response cp #01 ; MMC should respond 01 to this command jr nz,mmc_reset_failed ; fail to reset ld bc,#120 ; retry counter*256 (corrisponde a circa 5 secondi @3.5MHz) mmc_reset_ok: call cs_high ; set cs high ld a, #0xFF out (SPI_PORT),a ; 8 extra clock cycles nop nop call cs_low ; set cs low ld a, #MMC_SEND_OP_COND ; Sends OP_COND command call mmc_write_command call mmc_wait_response ; MMC_WAIT_RESPONSE tries to receive a response reading an SPI bit 0,a ; D0 SET = initialization still in progress... jr z,mmc_init_ok djnz mmc_reset_ok ; if no response, tries to send the entire block 254 more times dec c jr nz,mmc_reset_ok ld a,#2 ; error code for INIT ERROR jr mmc_errorx mmc_init_ok: call cs_high ; set cs high in a,(SPI_PORT) ; some extra clock cycles call pause1 xor a ret mmc_reset_failed: ; MMC Reset error ld a,#1 mmc_errorx: call cs_high ret ; ;----------------------------------------------------------------------------------------- ; SENDS BLOCK_SIZE COMMAND TO MMC. ; ; Parameters: DE = nbytes. Destroys AF. ;----------------------------------------------------------------------------------------- mmc_send_blocksize: call cs_low ; set cs low ld a,#MMC_SET_BLOCK_SIZE out (SPI_PORT),a xor a nop out (SPI_PORT),a nop nop out (SPI_PORT),a ld a,d nop out (SPI_PORT),a ld a,e nop out (SPI_PORT),a ld a,#0xff ; fake checksum nop out (SPI_PORT),a nop nop in a,(SPI_PORT) nop nop in a,(SPI_PORT) nop nop call cs_high ret ; ;----------------------------------------------------------------------------------------- ; SEND COMMAND TO MMC subroutine ; ; A = COMMAND CODE; ; H, L, D, E = 32 bit parameter (MSB ... LSB); ; ; Sends a $FF fake checksum ; ; RETURNS: 0 = OK; != 0 = MMC error code ; ; On OK, the CHIP SELECT will be LOW on exit ; On error, the CHIP SELECT will be deasserted on exit ; ; Destroys AF. ;----------------------------------------------------------------------------------------- mmc_send_command: push bc ld c,a call cs_high ; cs high call clock32 nop call cs_low ; cs low ld a,c ; command code is the first byte to be sent out (SPI_PORT),a ld a,h nop out (SPI_PORT),a ld a,l nop out (SPI_PORT),a ld a,d nop out (SPI_PORT),a ld a,e nop out (SPI_PORT),a ld a,#0xff nop out (SPI_PORT),a call mmc_wait_response ; waits for the MMC to reply != $FF cp #0 jr nz,mmc_commande pop bc ret ; 0 = no error mmc_commande: push af ; saves the error code call cs_high ; set cs high in a,(SPI_PORT) pop af pop bc ret ; returns the error code got from MMC ; ;----------------------------------------------------------------------------------------- ; WAIT FOR DATA TOKEN ($FE) subroutine (calls MMC_WAIT_RESPONSE up to 256 times) ; Returns with A = code read from MMC (ok if $FE). Destroys AF. ;----------------------------------------------------------------------------------------- mmc_waitdata_token: push bc ld b,#10 ; retry counter mmc_waitl: call mmc_wait_response cp #0xFE ; waits for the MMC to reply $FE (DATA TOKEN) jr z,exit_wda cp #0xFF ; but if not $FF, exits immediately (error code from MMC) jr nz,exit_wda djnz mmc_waitl exit_wda: pop bc ret ; ;----------------------------------------------------------------------------------------- ; Sends a command with parameters = 00 and checksum = $95. Destroys AF. ;----------------------------------------------------------------------------------------- mmc_write_command: push bc out (SPI_PORT),a ; sends the command ld b,#4 xor a l_sendc0: out (SPI_PORT),a ; then sends four "00" bytes (parameters = NULL) djnz l_sendc0 ld a,#0x95 ; $95 is only needed when the CARD INIT is being performed, nop out (SPI_PORT),a ; then this byte is ignored. pop bc ret ; ;----------------------------------------------------------------------------------------- ; Waits for the MMC to respond. Returns with A = response code; $FF = NO RESPONSE ; ; Responses from CARD are in R1 format for all command except SEND_STATUS. ; When SET, a bit indicates: ; ; D0 = Idle state / init not completed yet ; D1 = Erase Reset ; D2 = Illegal Command ; D3 = Com CRC Error ; D4 = Erase Sequence Error ; D5 = Address Error ; D6 = Parameter Error ; D7 = ALWAYS LOW ; ; Destroys AF. ;----------------------------------------------------------------------------------------- mmc_wait_response: push bc ld bc,#50 ; retry counter l_response: in a,(SPI_PORT) ; reads a byte from MMC cp #0xFF ; $FF = no card data line activity ;; ld (#0x1000),a ; AVF - debug jr nz,resp_ok djnz l_response dec c jr nz,l_response resp_ok: pop bc ret ; ;------------------------------------------------------------------------------------ ; CHIP_SELECT HIGH subroutine. Destroys no registers. Entire port is tied to '1'. ;------------------------------------------------------------------------------------ cs_high: push af ld a,#255 out (OUT_PORT),a pop af ret ; ;------------------------------------------------------------------------------------ ; CHIP_SELECT LOW subroutine. Destroys no registers. The card to be selected should ; specified in CARD_SELECT (D1 = SLOT1, D0 = SLOT0, active LOW) ;------------------------------------------------------------------------------------ cs_low: push af ld a,(card_select) out (OUT_PORT),a pop af ret pause1: push hl ld hl, #0x8000 ; OK for 3.5MHz ONLY (7MHz requires two calls or ld hl,0 and so on) loop3: dec hl ld a,h or l jr nz,loop3 pop hl ret clock32: push bc ld b,#4 l_4bytes: in a,(#SPI_PORT) ; some more clock cycles djnz l_4bytes pop bc ret .globl _mmc_read_block ;; callable from C, parameter is the block number _mmc_read_block: push ix ld ix,#0 add ix,sp ld l,4(ix) ;hl has the block number ld h,5(ix) ; de should have LSB xor a ; clear a ld e,a ; e always 0 add hl,hl ; multipy by 2 ld d,l ld l,h rla ; a = 1 if carry was set ld h,a ; h=0 ld ix,(_buff_ptr) call mmc_read_data ld l,a xor a ld h,a pop ix ret ;; check that buffer ends in 0x55AA ;; on success zero flag is set ;; on error l is set to 2 check_signature: ld iy,#_block_buffer+0x200 ; +/- 0x80 ld a,0xfe(iy) cp #0x55 jr nz,err_ret ld a,0xff(iy) cp #0xAA jr nz,err_ret ld l,#0 ret err_ret: ld l,#2 ret .globl _mmc_mount1 _mmc_mount1: push ix ld hl,#0 ld de,#0 ld ix,(_buff_ptr) call mmc_read_data ; read MBR into buffer or a ; result = 0 ? jr z,res_OK1 ld l,#1 ret res_OK1: call check_signature ret nz ld l,0xc6(iy) ; partition start lo ld h,0xc7(iy) ; partition start hi ld (_partition_start),hl push hl call _mmc_read_block pop af ld a,l and a ; check return code jr z,second_read_OK ld l,#3 ret .globl _puts .globl _root_start .globl _cluster2_start second_read_OK: call check_signature ret nz ld hl,#_block_buffer+0x36 push hl call _puts pop hl ld iy,#_block_buffer ld a,0x0d(iy) ld hl,#_cluster_size ld (hl),a ld e,0x0e(iy) ;reserved sectors lo ld d,0x0f(iy) ;reserved sectors hi ld hl,(_partition_start) add hl,de ld (_fat_start),hl ex de,hl ld l,0x16(iy) ; fat size ld h,0x17(iy) ; fat size add hl,hl ; times 2 copies add hl,de ; plus fat start ld (_root_start),hl ld d,#0 ld e,#32 add hl,de ; cluster2_start = root_start + root_size; ld (_cluster2_start),hl ld l,#0 ret ; ;----------------------------------------------------------------------------------------- ; READ A SINGLE BLOCK OF DATA subroutine ; ; This routine only works for blocksize = 512 (two INIR sequence). ; ; HL, DE= MSB, LSB of 32bit address in MMC memory ; IX = ram buffer address ; ; RETURN code in A: ; 0 = OK ; 1 = read_block command error ; 2 = no wait_data token from MMC ; ; DESTROYS AF, HL ;----------------------------------------------------------------------------------------- mmc_read_data: ld a,#MMC_READ_SINGLE_BLOCK ; Command code for block read call mmc_send_command cp #0 ret nz ; ERRORE call mmc_waitdata_token cp #0xFE jr z,read_mmc_block ; OK ld a,#2 ; no data token from MMC ret read_mmc_block: push ix pop hl ; HL = INIR write pointer push bc ld bc,#SPI_PORT ; B = 0 = 256 bytes for the first INIR; C = I/O port ; inir ; nop ; inir ini_loop3: ini ini ini ini ini ini ini ini ini ini ini ini ini ini ini ini jr nz,ini_loop3 ini_loop4: ini ini ini ini ini ini ini ini ini ini ini ini ini ini ini ini jr nz,ini_loop4 nop nop in a,(SPI_PORT) ; CRC nop nop in a,(SPI_PORT) ; CRC call cs_high ; set cs high call clock32 ; 32 more clock cycles pop bc xor a ; no error ret .globl mmc_write_data .globl _mmc_write_block ;; callable from C, parameter is the block number _mmc_write_block: push ix ld ix,#0 add ix,sp ld l,4(ix) ;hl has the block number ld h,5(ix) ; de should have LSB xor a ; clear a ld e,a ; e always 0 add hl,hl ; multipy by 2 ld d,l ld l,h rla ; a = 1 if carry was set ld h,a ; h=0 ld ix,(_buff_ptr) ld b,#1 ; just one block call mmc_write_data ld l,a xor a ld h,a pop ix ret ; ;----------------------------------------------------------------------------------------- ; WRITE BLOCK OF DATA subroutine. By now, we don't use the MULTIPLE_BLOCK transfer. ; ; This routine only works for blocksize = 512 (two OUTI sequence). ; ; HL, DE= MSB, LSB of 32bit address in MMC memory ; B = number of 512 bytes blocks to write ; IX = ram buffer address ; ; RETURN code in A: ; 0 = OK ; 1 = read_block command error ; 2 = write error (no "5" response from MMC) ; ; Destroys AF, B, DE, HL, IX. ;----------------------------------------------------------------------------------------- mmc_write_data: ld a,#MMC_WRITE_SINGLE_BLOCK ; Command code for block read call mmc_send_command cp #0 ret nz ; ERRORE ld a,#0xFE out (SPI_PORT),a ; first byte to be sent = DATA TOKEN write_mmc_block: push bc ld bc,#0x3F ; C = PORT, B = 0 for 256 bytes on first OTIR push hl push ix pop hl ; otir ; nop ; without this NOP, the 2nd half of the 512 bytes block would miss some write cycles ; otir ; on the CPLD if Z80 runs at 21MHz out_loop1: outi outi outi outi outi outi outi outi outi outi outi outi outi outi outi outi jr nz,out_loop1 out_loop2: outi outi outi outi outi outi outi outi outi outi outi outi outi outi outi outi jr nz,out_loop2 push hl pop ix pop hl ld a,#0x95 ; CRC... (!) out (SPI_PORT),a nop nop out (SPI_PORT),a call mmc_wait_response pop bc and #0x1F ; masks useful response bits cp #5 jr nz,write_error ld a,d ; aggiorna il puntatore al blocco da leggere add #BLOCKSIZE/256 ; 2 se blocksize = 512 jr nc,no_overw inc hl no_overw: ld d,a wait_busy: call mmc_wait_response ; MMC will report "00" until busy cp #0 jr z,wait_busy call cs_high call clock32 ; 32 more clock cycles djnz jrmmc_write_data ; next block xor a ret jrmmc_write_data: jp mmc_write_data write_error: ld a,#2 ; write error code ret