mirror of https://github.com/zxdos/zxuno.git
465 lines
6.5 KiB
C
465 lines
6.5 KiB
C
#include <sms.h>
|
|
#include "sd.h"
|
|
#include "console.h"
|
|
#include "debug.h"
|
|
|
|
#define DEBUG_SD2
|
|
|
|
const UBYTE *card_type = 0xc080;
|
|
|
|
void card_SDHC(BYTE val)
|
|
{
|
|
card_type[0] = val;
|
|
}
|
|
|
|
void spi_set_speed(BYTE delay)
|
|
{
|
|
#asm
|
|
ld hl, 2
|
|
add hl, sp
|
|
ld a, (hl)
|
|
and a, $7f
|
|
or a,$80
|
|
out ($c0),a
|
|
#endasm
|
|
}
|
|
|
|
void spi_assert_cs()
|
|
{
|
|
#asm
|
|
in a,($00)
|
|
and a,$7f
|
|
out ($c0),a
|
|
#endasm
|
|
}
|
|
|
|
void spi_deassert_cs()
|
|
{
|
|
#asm
|
|
in a,($00)
|
|
or a,$80
|
|
out ($c0),a
|
|
#endasm
|
|
}
|
|
|
|
void spi_wait()
|
|
{
|
|
#asm
|
|
send_byte_loop:
|
|
in a,($00)
|
|
and a,$80
|
|
jr z,send_byte_loop
|
|
#endasm
|
|
}
|
|
|
|
void spi_send_byte(BYTE data)
|
|
{
|
|
#asm
|
|
ld hl, 2
|
|
add hl, sp
|
|
ld a, (hl)
|
|
out ($c1),a
|
|
#endasm
|
|
spi_wait();
|
|
}
|
|
|
|
void spi_delay()
|
|
{
|
|
spi_send_byte(0xff);
|
|
}
|
|
|
|
UBYTE spi_receive_byte()
|
|
{
|
|
spi_delay();
|
|
#asm
|
|
in a,($01)
|
|
ld l,a
|
|
ld h,0
|
|
#endasm
|
|
}
|
|
|
|
|
|
UBYTE sd_wait_r1()
|
|
{
|
|
BYTE r,timeout;
|
|
for (timeout=0xa; timeout>0; --timeout) {
|
|
r = spi_receive_byte();
|
|
if ((r&0x80)==0) {
|
|
break;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
UBYTE sd_wait_r58()
|
|
{
|
|
BYTE r,timeout;
|
|
for (timeout=0xa; timeout>0; --timeout) {
|
|
r = spi_receive_byte();
|
|
if (r==0x01 || r==0xc0 || r==0x80 || r==0x20) {
|
|
break;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
UBYTE sd_wait_ready()
|
|
{
|
|
BYTE timeout,r;
|
|
spi_receive_byte();
|
|
for (timeout=0xa; timeout>0; --timeout) {
|
|
r = spi_receive_byte();
|
|
if (r==0xff) {
|
|
break;
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
UBYTE sd_cmd0()
|
|
{
|
|
BYTE r;
|
|
sd_wait_ready();
|
|
|
|
spi_send_byte(0x40);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x95);
|
|
|
|
r = sd_wait_r1();
|
|
|
|
#ifdef DEBUG_SD
|
|
debug_puts("cmd0:");
|
|
debug_print_byte(r);
|
|
debug_puts("\n");
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
UBYTE sd_cmd1() //MMC
|
|
{
|
|
BYTE r;
|
|
spi_delay();
|
|
sd_wait_ready();
|
|
|
|
spi_send_byte(0x41);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0xFF);
|
|
|
|
r = sd_wait_r1();
|
|
|
|
return r;
|
|
}
|
|
|
|
UBYTE sd_cmd8()
|
|
{
|
|
BYTE r;
|
|
sd_wait_ready();
|
|
|
|
spi_send_byte(0x48);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x01);
|
|
spi_send_byte(0xaa);
|
|
spi_send_byte(0x87);
|
|
|
|
r = sd_wait_r1();
|
|
spi_delay();
|
|
spi_delay();
|
|
spi_delay();
|
|
spi_delay();
|
|
spi_delay();
|
|
|
|
#ifdef DEBUG_SD
|
|
debug_puts("cmd8:");
|
|
debug_print_byte(r);
|
|
debug_puts("\n");
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
UBYTE sd_acmd41(UBYTE byte0)
|
|
{
|
|
BYTE r;
|
|
|
|
spi_send_byte(0x77); // CMD55
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0xff);
|
|
|
|
r = sd_wait_r1();
|
|
|
|
#ifdef DEBUG_CMD
|
|
console_gotoxy(0,2);
|
|
console_puts("A41R1 = "); //q debug
|
|
console_print_byte(r); //q debug
|
|
#endif
|
|
|
|
if (r>1) {
|
|
#ifdef DEBUG_SD
|
|
debug_puts("cmd55 failed:");
|
|
debug_print_byte(r);
|
|
debug_puts("\n");
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
sd_wait_ready();
|
|
|
|
spi_send_byte(0x69); // CMD41
|
|
spi_send_byte(byte0);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0xff);
|
|
|
|
r = sd_wait_r1();
|
|
|
|
#ifdef DEBUG_CMD
|
|
console_gotoxy(12,2);
|
|
console_puts("A41R2 = "); //q debug
|
|
console_print_byte(r); //q debug
|
|
#endif
|
|
|
|
spi_delay();
|
|
spi_delay();
|
|
|
|
#ifdef DEBUG_SD
|
|
debug_puts("acmd41:");
|
|
debug_print_byte(r);
|
|
debug_puts("\n");
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
UBYTE sd_cmd58()
|
|
{
|
|
BYTE r;
|
|
UBYTE r58;
|
|
sd_wait_ready();
|
|
|
|
spi_send_byte(0x7a);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0x00);
|
|
spi_send_byte(0xff);
|
|
|
|
r = sd_wait_r1();
|
|
|
|
#ifdef DEBUG_CMD
|
|
console_gotoxy(0,3);
|
|
console_puts("58R1 = "); //q debug
|
|
console_print_byte(r); //q debug
|
|
#endif
|
|
|
|
r58 = spi_receive_byte();
|
|
|
|
#ifdef DEBUG_CMD
|
|
console_puts(" - 58R2 = "); //q debug
|
|
console_print_byte(r58); //q debug
|
|
console_puts("\n");
|
|
#endif
|
|
|
|
if (r58==0xc0) //Q
|
|
card_SDHC(1);
|
|
else
|
|
card_SDHC(0);
|
|
|
|
spi_delay(); // if &0xc0==0xc0 => SDHC
|
|
spi_delay();
|
|
spi_delay();
|
|
spi_delay();
|
|
spi_delay();
|
|
|
|
#ifdef DEBUG_SD
|
|
debug_puts("cmd58:");
|
|
debug_print_byte(r);
|
|
debug_puts("\n");
|
|
#endif
|
|
return r;
|
|
}
|
|
|
|
|
|
int sd_init()
|
|
{
|
|
UBYTE timeout;
|
|
|
|
spi_set_speed(0x7f); // min speed
|
|
|
|
// wait a bit
|
|
spi_assert_cs();
|
|
for(timeout=0x10; timeout>0; --timeout) {
|
|
spi_send_byte(0xff);
|
|
}
|
|
spi_deassert_cs();
|
|
spi_send_byte(0xff);
|
|
spi_send_byte(0xff);
|
|
spi_assert_cs();
|
|
|
|
// go into idle state
|
|
timeout = 0xff;
|
|
while (sd_cmd0() != 0x01) {
|
|
if (timeout==0) {
|
|
spi_deassert_cs();
|
|
return FALSE;
|
|
}
|
|
timeout = timeout-1;
|
|
}
|
|
|
|
if (sd_cmd8() == 0x01) {
|
|
// SD card V2+
|
|
#ifdef DEBUG_SD
|
|
debug_puts("SD card V2+\n");
|
|
#endif
|
|
// initialize card
|
|
spi_delay(); //q delay
|
|
timeout = 0xff;
|
|
while ((sd_acmd41(0x40)&1)!=0) {
|
|
if (timeout==0) {
|
|
spi_deassert_cs();
|
|
return FALSE;
|
|
}
|
|
timeout = timeout-1;
|
|
}
|
|
|
|
// read OCR //Q MOD for SDHC
|
|
if (sd_cmd58()!=0) {
|
|
spi_deassert_cs();
|
|
return FALSE;
|
|
}
|
|
|
|
} else {
|
|
// SD card V1 or MMC
|
|
if (sd_acmd41(0x00)<=1) {
|
|
// SD V1
|
|
#ifdef DEBUG_SD
|
|
debug_puts("SD V1\n");
|
|
#endif
|
|
timeout = 0xff;
|
|
while ((sd_acmd41(0x00)&1)!=0) {
|
|
if (timeout==0) {
|
|
spi_deassert_cs();
|
|
return FALSE;
|
|
}
|
|
timeout = timeout-1;
|
|
}
|
|
} else {
|
|
// MMC Card : probamos inicializar con CMD1
|
|
timeout = 0xff;
|
|
while (sd_cmd1()!=0) {
|
|
if (timeout==0) {
|
|
spi_deassert_cs();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
spi_deassert_cs();
|
|
spi_set_speed(0x00); // max speed
|
|
return TRUE;
|
|
}
|
|
|
|
/* loads $200 bytes from spi */
|
|
void load_data(UBYTE *target)
|
|
{
|
|
/*
|
|
int i;
|
|
for (i=0; i<0x200; i++) {
|
|
*target++ = spi_receive_byte();
|
|
}
|
|
*/
|
|
#asm
|
|
ld hl, 2
|
|
add hl, sp
|
|
ld e, (hl)
|
|
inc hl
|
|
ld d, (hl)
|
|
ex de,hl
|
|
ld bc,$0002
|
|
load_data_loop:
|
|
ld a,$ff
|
|
out ($c1),a
|
|
load_data_wait:
|
|
in a,($00)
|
|
and a,$80
|
|
jr z,load_data_wait
|
|
in a,($01)
|
|
ld (hl),a
|
|
inc hl
|
|
djnz load_data_loop
|
|
dec c
|
|
jr nz,load_data_loop
|
|
#endasm
|
|
}
|
|
|
|
int sd_load_sector(UBYTE* target, DWORD sector)
|
|
{
|
|
DWORD address;
|
|
BYTE r;
|
|
BYTE timeout;
|
|
|
|
address = sector<<9; //SD no HD = byte address
|
|
if (card_type[0] == 1)
|
|
address = sector; //Q SDHC = block addres
|
|
|
|
#ifdef DEBUG_SD2
|
|
debug_puts("loading address ");
|
|
debug_print_dword(address);
|
|
debug_puts("\n");
|
|
#endif
|
|
|
|
sd_wait_ready();
|
|
// read block
|
|
spi_assert_cs();
|
|
spi_send_byte(0x51); // CMD17
|
|
spi_send_byte((address>>24)&0xff);
|
|
spi_send_byte((address>>16)&0xff);
|
|
spi_send_byte((address>>8)&0xff);
|
|
spi_send_byte(address&0xff);
|
|
spi_send_byte(0xff);
|
|
|
|
r = sd_wait_r1();
|
|
|
|
#ifdef DEBUG_SD
|
|
debug_puts("cmd17:");
|
|
debug_print_byte(r);
|
|
debug_puts("\n");
|
|
#endif
|
|
if ((r&0x80)!=0) {
|
|
spi_deassert_cs();
|
|
return FALSE;
|
|
}
|
|
|
|
// wait for 0xfe (start of block)
|
|
timeout = 0xff;
|
|
while (spi_receive_byte()!=0xfe) {
|
|
if (timeout==0) {
|
|
spi_deassert_cs();
|
|
return FALSE;
|
|
}
|
|
timeout = timeout-1;
|
|
}
|
|
|
|
// read block
|
|
load_data(target);
|
|
|
|
// skip crc
|
|
spi_delay();
|
|
spi_delay();
|
|
|
|
// shutdown
|
|
spi_delay();
|
|
spi_deassert_cs();
|
|
return TRUE;
|
|
}
|