1; 2;TBBlue / ZX Spectrum Next project 3; 4;Copyright (c) 2015 Fabio Belavenuto & Victor Trucco 5; 6;This program is free software: you can redistribute it and/or modify 7;it under the terms of the GNU General Public License as published by 8;the Free Software Foundation, either version 3 of the License, or 9;(at your option) any later version. 10; 11;This program is distributed in the hope that it will be useful, 12;but WITHOUT ANY WARRANTY; without even the implied warranty of 13;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14;GNU General Public License for more details. 15; 16;You should have received a copy of the GNU General Public License 17;along with this program. If not, see <http://www.gnu.org/licenses/>. 18; 19 20SECTION bss_user 21 22mmc_type: defs 1 23 24SECTION code_user 25 26EXTERN __IO_SPI_CONTROL 27EXTERN __IO_SPI_DATA 28 29; Comandos SPI: 30CMD0 = 0 | 0x40 31CMD1 = 1 | 0x40 32CMD8 = 8 | 0x40 33CMD9 = 9 | 0x40 34CMD10 = 10 | 0x40 35CMD12 = 12 | 0x40 36CMD16 = 16 | 0x40 37CMD17 = 17 | 0x40 38CMD18 = 18 | 0x40 39CMD24 = 24 | 0x40 40CMD25 = 25 | 0x40 41CMD55 = 55 | 0x40 42CMD58 = 58 | 0x40 43ACMD23 = 23 | 0x40 44ACMD41 = 41 | 0x40 45 46 47; ------------------------------------------------ 48; Algorithm to initialize an SD card 49; ------------------------------------------------ 50; unsigned char MMC_Init(); 51; 52 53PUBLIC _MMC_Init 54 55_MMC_Init: 56 ld a, 0xFF 57 out (__IO_SPI_CONTROL), a ; disable SD 58 ld b, 10 ; send 80 clock pulses with sd card disabled 59enviaClocksInicio: 60 ld a, 0xFF 61 out (__IO_SPI_DATA), a 62 djnz enviaClocksInicio 63 ld a, 0xFE 64 out (__IO_SPI_CONTROL), a ; enable SD 65 ld a, CMD12 66 call SD_SEND_CMD ; cancel any multi-sector read 67 ld b, 9 68wait_cancel: 69 in a, (__IO_SPI_DATA) ; read 9 bytes to ensure cancellation takes effect 70 djnz wait_cancel 71 ld b, 16 ; 16 attempts to CMD0 72SD_SEND_CMD0: 73 ld a, CMD0 ; first command: CMD0 74 ld de, 0 75 push bc 76 call SD_SEND_CMD_2_ARGS_TEST_BUSY 77 pop bc 78 jp nc, testaSDCV2 ; sd card responded to CMD0, jump 79 djnz SD_SEND_CMD0 80 ld l, 0 ; cartao nao respondeu ao CMD0, retornar 0 81IF __SCCZ80 82 ld h, l 83ENDIF 84 ld a, 0xFF 85 out (__IO_SPI_CONTROL), a ; desabilita SD 86 ret 87testaSDCV2: 88 ld a, CMD8 89 ld de, 0x1AA 90 call SD_SEND_CMD_2_ARGS_GET_R3 91 ld hl, SD_SEND_CMD1 ; HL aponta para rotina correta 92 jr c, pula4 ; cartao recusou CMD8, enviar comando CMD1 93 ld hl, SD_SEND_ACMD41 ; cartao aceitou CMD8, enviar comando ACMD41 94pula4: 95 ld bc, 120 ; 120 tentativas 96loop: 97 push bc 98 call jumpHL ; chamar rotina correta em HL 99 pop bc 100 jp nc, iniciou 101 djnz loop 102 dec c 103 jr nz, loop 104deuerroi: 105 ld l, 0 ; erro, retornar 0 106IF __SCCZ80 107 ld h, l 108ENDIF 109 ld a, 0xFF 110 out (__IO_SPI_CONTROL), a ; desabilita SD 111 ret 112jumpHL: 113 jp (hl) ; chamar rotina correta em HL 114iniciou: 115 ld a, CMD58 ; ler OCR 116 ld de, 0 117 call SD_SEND_CMD_2_ARGS_GET_R3 ; enviar comando e receber resposta tipo R3 118 jp c, deuerroi 119 ld a, b ; testa bit CCS do OCR que informa se cartao eh SDV1 ou SDV2 120 and 0x40 121 ld (mmc_type), a ; salva informacao da versao do SD (V1 ou V2) 122 call z, mudarTamanhoBlocoPara512 ; se bit CCS do OCR for 1, eh cartao SDV2 (Block address - SDHC ou SDXD) 123 ; e nao precisamos mudar tamanho do bloco para 512 124 ld a, 0xFF 125 out (__IO_SPI_CONTROL), a ; desabilita SD 126 ld a, (mmc_type) ; retornar tipo de cartao 127IF __SCCZ80 128 ld h, 0 129ENDIF 130 ld l, 3 131 cp 0x40 132 ret z 133 ld l, 2 134 ret 135 136; ------------------------------------------------ 137; Setar o tamanho do bloco para 512 se o cartao 138; for SDV1 139; ------------------------------------------------ 140mudarTamanhoBlocoPara512: 141 ld a, CMD16 142 ld bc, 0 143 ld de, 512 144 jp SD_SEND_CMD_GET_ERROR 145 146 147 148; ------------------------------------------------ 149; Ler um bloco de 512 bytes do cartao 150; ------------------------------------------------ 151; unsigned char MMC_Read(unsigned long lba, unsigned int *buffer) 152; callee linkage 153 154PUBLIC _MMC_Read 155PUBLIC asm_MMC_Read 156 157_MMC_Read: 158 159IF __SDCC 160 pop hl 161 pop de 162 pop bc 163 ex (sp),hl 164ENDIF 165 166IF __SCCZ80 167 pop af 168 pop hl 169 pop de 170 pop bc 171 push af 172ENDIF 173 174asm_MMC_Read: 175 176 ; bcde = unsigned long lba 177 ; hl = unsigned char *buffer 178 179 ld a, 0xFE 180 out (__IO_SPI_CONTROL), a ; habilita SD 181 182 ld a, (mmc_type) ; verificar se eh SDV1 ou SDV2 183 or a 184 call z, blocoParaByte ; se for SDV1 converter blocos para bytes 185 186 ld a, CMD17 ; ler somente um bloco com CMD17 = Read Single Block 187 call SD_SEND_CMD_GET_ERROR 188 jr nc, ok2 189erro2: 190 ld l, 0 ; informar erro 191IF __SCCZ80 192 ld h, l 193ENDIF 194 ret 195ok2: 196 call WAIT_RESP_FE 197 jr c, erro2 198 ld bc, __IO_SPI_DATA 199 inir 200 inir 201 nop 202 in a, (__IO_SPI_DATA) ; descarta CRC 203 nop 204 in a, (__IO_SPI_DATA) 205 ld l, 1 206IF __SCCZ80 207 ld h, 0 208ENDIF 209 ret 210 211; ------------------------------------------------ 212; Converte blocos para bytes. Na pratica faz 213; BC DE = (BC DE) * 512 214; ------------------------------------------------ 215blocoParaByte: 216 ld b, c 217 ld c, d 218 ld d, e 219 ld e, 0 220 sla d 221 rl c 222 rl b 223 ret 224 225; ------------------------------------------------ 226; Enviar CMD1 para cartao. Carry indica erro 227; Destroi AF, BC, DE 228; ------------------------------------------------ 229SD_SEND_CMD1: 230 ld a, CMD1 231SD_SEND_CMD_NO_ARGS: 232 ld bc, 0 233 ld d, b 234 ld e, c 235SD_SEND_CMD_GET_ERROR: 236 call SD_SEND_CMD 237 or a 238 ret z ; se A=0 nao houve erro, retornar 239 ; fall throw 240 241; ------------------------------------------------ 242; Informar erro 243; Nao destroi registradores 244; ------------------------------------------------ 245setaErro: 246 scf 247 ret 248 249; ------------------------------------------------ 250; Enviar comando ACMD41 251; ------------------------------------------------ 252SD_SEND_ACMD41: 253 ld a, CMD55 254 call SD_SEND_CMD_NO_ARGS 255 ld a, ACMD41 256 ld bc, 0x4000 257 ld d, c 258 ld e, c 259 jr SD_SEND_CMD_GET_ERROR 260 261; ------------------------------------------------ 262; Enviar comando em A com 2 bytes de parametros 263; em DE e testar retorno BUSY 264; Retorna em A a resposta do cartao 265; Destroi AF, BC 266; ------------------------------------------------ 267SD_SEND_CMD_2_ARGS_TEST_BUSY: 268 ld bc, 0 269 call SD_SEND_CMD 270 ld b, a 271 and 0xFE ; testar bit 0 (flag BUSY) 272 ld a, b 273 jr nz, setaErro ; BUSY em 1, informar erro 274 ret ; sem erros 275 276; ------------------------------------------------ 277; Enviar comando em A com 2 bytes de parametros 278; em DE e ler resposta do tipo R3 em BC DE 279; Retorna em A a resposta do cartao 280; Destroi AF, BC, DE, HL 281; ------------------------------------------------ 282SD_SEND_CMD_2_ARGS_GET_R3: 283 call SD_SEND_CMD_2_ARGS_TEST_BUSY 284 ret c 285 push af 286 call WAIT_RESP_NO_FF 287 ld h, a 288 call WAIT_RESP_NO_FF 289 ld l, a 290 call WAIT_RESP_NO_FF 291 ld d, a 292 call WAIT_RESP_NO_FF 293 ld e, a 294 ld b, h 295 ld c, l 296 pop af 297 ret 298 299; ------------------------------------------------ 300; Enviar comando em A com 4 bytes de parametros 301; em BC DE e enviar CRC correto se for CMD0 ou 302; CMD8 e aguardar processamento do cartao 303; Destroi AF, BC 304; ------------------------------------------------ 305SD_SEND_CMD: 306 out (__IO_SPI_DATA), a 307 push af 308 ld a, b 309 nop 310 out (__IO_SPI_DATA), a 311 ld a, c 312 nop 313 out (__IO_SPI_DATA), a 314 ld a, d 315 nop 316 out (__IO_SPI_DATA), a 317 ld a, e 318 nop 319 out (__IO_SPI_DATA), a 320 pop af 321 cp CMD0 322 ld b, 0x95 ; CRC para CMD0 323 jr z, enviaCRC 324 cp CMD8 325 ld b, 0x87 ; CRC para CMD8 326 jr z, enviaCRC 327 ld b, 0xFF ; CRC dummy 328enviaCRC: 329 ld a, b 330 out (__IO_SPI_DATA), a 331 jr WAIT_RESP_NO_FF 332 333; ------------------------------------------------ 334; Esperar que resposta do cartao seja $FE 335; Destroi AF, B 336; ------------------------------------------------ 337WAIT_RESP_FE: 338 ld b, 10 ; 10 tentativas 339loop1: 340 push bc 341 call WAIT_RESP_NO_FF ; esperar resposta diferente de $FF 342 pop bc 343 cp 0xFE ; resposta � $FE ? 344 ret z ; sim, retornamos com carry=0 345 djnz loop1 346 scf ; erro, carry=1 347 ret 348 349; ------------------------------------------------ 350; Esperar que resposta do cartao seja diferente 351; de $FF 352; Destroi AF, BC 353; ------------------------------------------------ 354WAIT_RESP_NO_FF: 355 ld bc, 100 ; 100 tentativas 356loop2: 357 in a, (__IO_SPI_DATA) 358 cp 0xFF ; testa $FF 359 ret nz ; sai se nao for $FF 360 djnz loop2 361 dec c 362 jr nz, loop2 363 ret 364