summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
-rw-r--r--.gitignore1
-rw-r--r--src/devices/machine/spi_sdcard.cpp440
-rw-r--r--src/devices/machine/spi_sdcard.h29
3 files changed, 282 insertions, 188 deletions
diff --git a/.gitignore b/.gitignore
index 4cd57d9f2bf..f5231d17f98 100644
--- a/.gitignore
+++ b/.gitignore
@@ -42,6 +42,7 @@
!/*.bdf
!/LICENSE
/.idea
+.vscode
docs/build
docs/source/_build
regtests/chdman/temp
diff --git a/src/devices/machine/spi_sdcard.cpp b/src/devices/machine/spi_sdcard.cpp
index 3ace0591742..588727d83d4 100644
--- a/src/devices/machine/spi_sdcard.cpp
+++ b/src/devices/machine/spi_sdcard.cpp
@@ -18,6 +18,8 @@
Refrences:
https://www.sdcard.org/downloads/pls/ (Physical Layer Simplified Specification)
+ REF: tags are refering to the spec form above. 'Physical Layer Simplified Specification v8.00'
+
http://www.dejazzer.com/ee379/lecture_notes/lec12_sd_card.pdf
https://embdev.net/attachment/39390/TOSHIBA_SD_Card_Specification.pdf
http://elm-chan.org/docs/mmc/mmc_e.html
@@ -46,9 +48,12 @@ spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type
device_t(mconfig, type, tag, owner, clock),
write_miso(*this),
m_image(*this, "image"),
+ m_state(SD_STATE_IDLE),
m_harddisk(nullptr),
- m_in_latch(0), m_out_latch(0), m_cmd_ptr(0), m_state(0), m_out_ptr(0), m_out_count(0), m_ss(0), m_in_bit(0),
- m_cur_bit(0), m_write_ptr(0), m_blksize(512), m_bACMD(false)
+ m_ss(0), m_in_bit(0),
+ m_in_latch(0), m_out_latch(0xff), m_cur_bit(0),
+ m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_blksize(512), m_blknext(0),
+ m_bACMD(false)
{
}
@@ -64,13 +69,15 @@ spi_sdcard_sdhc_device::spi_sdcard_sdhc_device(const machine_config &mconfig, co
m_type = SD_TYPE_HC;
}
+ALLOW_SAVE_TYPE(spi_sdcard_device::sd_state);
+ALLOW_SAVE_TYPE(spi_sdcard_device::sd_type);
+
void spi_sdcard_device::device_start()
{
write_miso.resolve_safe();
+ save_item(NAME(m_state));
save_item(NAME(m_in_latch));
save_item(NAME(m_out_latch));
- save_item(NAME(m_cmd_ptr));
- save_item(NAME(m_state));
save_item(NAME(m_out_ptr));
save_item(NAME(m_out_count));
save_item(NAME(m_ss));
@@ -78,6 +85,7 @@ void spi_sdcard_device::device_start()
save_item(NAME(m_cur_bit));
save_item(NAME(m_write_ptr));
save_item(NAME(m_blksize));
+ save_item(NAME(m_blknext));
save_item(NAME(m_type));
save_item(NAME(m_cmd));
save_item(NAME(m_data));
@@ -94,10 +102,11 @@ void spi_sdcard_device::device_add_mconfig(machine_config &config)
HARDDISK(config, m_image).set_interface("spi_sdcard");
}
-void spi_sdcard_device::send_data(int count)
+void spi_sdcard_device::send_data(u16 count, sd_state new_state)
{
m_out_ptr = 0;
m_out_count = count;
+ change_state(new_state);
}
void spi_sdcard_device::spi_clock_w(int state)
@@ -121,56 +130,73 @@ void spi_sdcard_device::spi_clock_w(int state)
if (m_cur_bit == 8)
{
LOGMASKED(LOG_SPI, "SDCARD: got %02x\n", m_in_latch);
+ for (u8 i = 0; i < 5; i++)
+ {
+ m_cmd[i] = m_cmd[i + 1];
+ }
+ m_cmd[5] = m_in_latch;
switch (m_state)
{
- case SD_STATE_IDLE:
- for (int i = 0; i < 5; i++)
+ case SD_STATE_IDLE:
+ do_command();
+ break;
+
+ case SD_STATE_WRITE_WAITFE:
+ if (m_in_latch == 0xfe)
+ {
+ m_state = SD_STATE_WRITE_DATA;
+ m_out_latch = 0xff;
+ m_write_ptr = 0;
+ change_state(SD_STATE_RCV);
+ }
+ break;
+
+ case SD_STATE_WRITE_DATA:
+ m_data[m_write_ptr++] = m_in_latch;
+ if (m_write_ptr == (m_blksize + 2))
+ {
+ u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]);
+ if (m_type == SD_TYPE_V2)
{
- m_cmd[i] = m_cmd[i + 1];
+ blk /= m_blksize;
}
- m_cmd[5] = m_in_latch;
- if ((((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1)) && (m_out_count == 0))
+ LOGMASKED(LOG_GENERAL, "writing LBA %x, data %02x %02x %02x %02x\n", blk, m_data[0], m_data[1], m_data[2], m_data[3]);
+ if (hard_disk_write(m_harddisk, blk, &m_data[0]))
{
- do_command();
+ m_data[0] = DATA_RESPONSE_OK;
}
- break;
-
- case SD_STATE_WRITE_WAITFE:
- if (m_in_latch == 0xfe)
+ else
{
- m_state = SD_STATE_WRITE_DATA;
- m_out_latch = 0xff;
- m_write_ptr = 0;
+ m_data[0] = DATA_RESPONSE_IO_ERROR;
}
- break;
-
- case SD_STATE_WRITE_DATA:
- m_data[m_write_ptr++] = m_in_latch;
- if (m_write_ptr == (m_blksize + 2))
- {
- u32 blk = (m_cmd[1] << 24) | (m_cmd[2] << 16) | (m_cmd[3] << 8) | m_cmd[4];
- if (m_type == SD_TYPE_V2)
- {
- blk /= m_blksize;
- }
-
- LOGMASKED(LOG_GENERAL, "writing LBA %x, data %02x %02x %02x %02x\n", blk, m_data[0], m_data[1], m_data[2], m_data[3]);
- if (hard_disk_write(m_harddisk, blk, &m_data[0]))
- {
- m_data[0] = DATA_RESPONSE_OK;
- }
- else
- {
- m_data[0] = DATA_RESPONSE_IO_ERROR;
- }
- m_data[1] = 0x01;
-
- m_state = SD_STATE_IDLE;
- send_data(2);
- }
- break;
+ m_data[1] = 0x01;
+
+ send_data(2, SD_STATE_IDLE);
+ }
+ break;
+
+ case SD_STATE_DATA_MULTI:
+ do_command();
+ if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0)
+ {
+ m_data[0] = 0xfe; // data token
+ hard_disk_read(m_harddisk, m_blknext++, &m_data[1]);
+ util::crc16_t crc16 = util::crc16_creator::simple(
+ &m_data[1], m_blksize);
+ m_data[m_blksize + 1] = (crc16 >> 8) & 0xff;
+ m_data[m_blksize + 2] = (crc16 & 0xff);
+ send_data(1 + m_blksize + 2, SD_STATE_DATA_MULTI);
+ }
+ break;
+
+ default:
+ if (((m_cmd[0] & 0x70) == 0x40) || (m_out_count == 0)) // CMD0 - GO_IDLE_STATE
+ {
+ do_command();
+ }
+ break;
}
}
}
@@ -178,12 +204,11 @@ void spi_sdcard_device::spi_clock_w(int state)
{
m_in_latch <<= 1;
m_out_latch <<= 1;
- LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch, m_out_latch, m_cur_bit);
- if (m_cur_bit == 8)
- {
- m_cur_bit = 0;
- }
+ m_out_latch |= 1;
+ LOGMASKED(LOG_SPI, "\tsdcard: S %02x %02x (%d)\n", m_in_latch,
+ m_out_latch, m_cur_bit);
+ m_cur_bit &= 0x07;
if (m_cur_bit == 0)
{
if (m_out_count > 0)
@@ -193,163 +218,214 @@ void spi_sdcard_device::spi_clock_w(int state)
m_out_count--;
}
}
-
- write_miso(BIT(m_out_latch, 7) ? ASSERT_LINE : CLEAR_LINE);
+ write_miso(BIT(m_out_latch, 7));
}
}
}
void spi_sdcard_device::do_command()
{
- LOGMASKED(LOG_COMMAND, "SDCARD: cmd %02d %02x %02x %02x %02x %02x\n", m_cmd[0] & 0x3f, m_cmd[1], m_cmd[2], m_cmd[3], m_cmd[4], m_cmd[5]);
- switch (m_cmd[0] & 0x3f)
+ if (((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1))
{
- case 0: // CMD0 - GO_IDLE_STATE
- if (m_harddisk)
- {
- m_data[0] = 0x01;
- }
- else
- {
- m_data[0] = 0x00;
- }
- send_data(1);
- break;
-
- case 8: // CMD8 - SEND_IF_COND (SD v2 only)
- m_data[0] = 0x01;
- m_data[1] = 0;
- m_data[2] = 0;
- m_data[3] = 0;
- m_data[4] = 0xaa;
- send_data(5);
- break;
-
- case 10: // CMD10 - SEND_CID
- m_data[0] = 0x01; // initial R1 response
- m_data[1] = 0x00; // throwaway byte before data transfer
- m_data[2] = 0xfe; // data token
- m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME
- m_data[4] = 'M'; // OEM ID - MD for MAMEdev
- m_data[5] = 'D';
- m_data[6] = 'M'; // Product Name - "MCARD"
- m_data[7] = 'C';
- m_data[8] = 'A';
- m_data[9] = 'R';
- m_data[10] = 'D';
- m_data[11] = 0x10; // Product Revision in BCD (1.0)
- {
- u32 uSerial = 0x12345678;
- m_data[12] = (uSerial>>24) & 0xff; // PSN - Product Serial Number
- m_data[13] = (uSerial>>16) & 0xff;
- m_data[14] = (uSerial>>8) & 0xff;
- m_data[15] = (uSerial & 0xff);
- }
- m_data[16] = 0x01; // MDT - Manufacturing Date
- m_data[17] = 0x59; // 0x15 9 = 2021, September
- m_data[18] = 0x00; // CRC7, bit 0 is always 0
+ LOGMASKED(LOG_COMMAND, "SDCARD: cmd %02d %02x %02x %02x %02x %02x\n", m_cmd[0] & 0x3f, m_cmd[1], m_cmd[2], m_cmd[3], m_cmd[4], m_cmd[5]);
+ bool clean_cmd = true;
+ switch (m_cmd[0] & 0x3f)
{
- util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], 16);
- m_data[19] = (crc16 >> 8) & 0xff;
- m_data[20] = (crc16 & 0xff);
- }
- send_data(3 + 16 + 2);
- break;
+ case 0: // CMD0 - GO_IDLE_STATE
+ if (m_harddisk)
+ {
+ m_data[0] = 0x01;
+ send_data(1, SD_STATE_IDLE);
+ }
+ else
+ {
+ m_data[0] = 0x00;
+ send_data(1, SD_STATE_INA);
+ }
+ break;
+
+ case 8: // CMD8 - SEND_IF_COND (SD v2 only)
+ m_data[0] = 0; // 0x01;
+ m_data[1] = m_cmd[1]; // 0;
+ m_data[2] = m_cmd[2]; // 0;
+ m_data[3] = m_cmd[3]; // 0x01;
+ m_data[4] = m_cmd[4]; // 0xaa;
+ send_data(5, SD_STATE_IDLE);
+ break;
+
+ case 10: // CMD10 - SEND_CID
+ m_data[0] = 0x01; // initial R1 response
+ m_data[1] = 0x00; // throwaway byte before data transfer
+ m_data[2] = 0xfe; // data token
+ m_data[3] = 'M'; // Manufacturer ID - we'll use M for MAME
+ m_data[4] = 'M'; // OEM ID - MD for MAMEdev
+ m_data[5] = 'D';
+ m_data[6] = 'M'; // Product Name - "MCARD"
+ m_data[7] = 'C';
+ m_data[8] = 'A';
+ m_data[9] = 'R';
+ m_data[10] = 'D';
+ m_data[11] = 0x10; // Product Revision in BCD (1.0)
+ {
+ u32 uSerial = 0x12345678;
+ m_data[12] = (uSerial >> 24) & 0xff; // PSN - Product Serial Number
+ m_data[13] = (uSerial >> 16) & 0xff;
+ m_data[14] = (uSerial >> 8) & 0xff;
+ m_data[15] = (uSerial & 0xff);
+ }
+ m_data[16] = 0x01; // MDT - Manufacturing Date
+ m_data[17] = 0x59; // 0x15 9 = 2021, September
+ m_data[18] = 0x00; // CRC7, bit 0 is always 0
+ {
+ util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], 16);
+ m_data[19] = (crc16 >> 8) & 0xff;
+ m_data[20] = (crc16 & 0xff);
+ }
+ send_data(3 + 16 + 2, SD_STATE_STBY);
+ break;
- case 16: // CMD16 - SET_BLOCKLEN
- m_blksize = (m_cmd[3] << 8) | m_cmd[4];
- if (hard_disk_set_block_size(m_harddisk, m_blksize))
- {
+ case 12: // CMD12 - STOP_TRANSMISSION
m_data[0] = 0;
- }
- else
- {
- m_data[0] = 0xff; // indicate an error
- // if false was returned, it means the hard disk is a CHD file, and we can't resize the
- // blocks on CHD files.
- logerror("spi_sdcard: Couldn't change block size to %d, wrong CHD file?", m_blksize);
- }
- send_data(1);
- break;
+ send_data(1,
+ m_state == SD_STATE_RCV ? SD_STATE_PRG : SD_STATE_TRAN);
+ break;
- case 17: // CMD17 - READ_SINGLE_BLOCK
- if (m_harddisk)
- {
- m_data[0] = 0x00; // initial R1 response
- // data token occurs some time after the R1 response. A2SD expects at least 1
- // byte of space between R1 and the data packet.
- m_data[2] = 0xfe; // data token
- u32 blk = (m_cmd[1] << 24) | (m_cmd[2] << 16) | (m_cmd[3] << 8) | m_cmd[4];
- if (m_type == SD_TYPE_V2)
+ case 16: // CMD16 - SET_BLOCKLEN
+ m_blksize = (u16(m_cmd[3]) << 8) | u16(m_cmd[4]);
+ if (hard_disk_set_block_size(m_harddisk, m_blksize))
{
- blk /= m_blksize;
+ m_data[0] = 0;
}
- LOGMASKED(LOG_GENERAL, "reading LBA %x\n", blk);
- hard_disk_read(m_harddisk, blk, &m_data[3]);
+ else
{
- util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize);
- m_data[m_blksize + 3] = (crc16 >> 8) & 0xff;
- m_data[m_blksize + 4] = (crc16 & 0xff);
+ m_data[0] = 0xff; // indicate an error
+ // if false was returned, it means the hard disk is a CHD file, and we can't resize the
+ // blocks on CHD files.
+ logerror("spi_sdcard: Couldn't change block size to %d, wrong "
+ "CHD file?",
+ m_blksize);
}
- send_data(3 + m_blksize + 2);
- }
- else
- {
- m_data[0] = 0xff; // show an error
- send_data(1);
- }
- break;
+ send_data(1, SD_STATE_TRAN);
+ break;
- case 24: // CMD24 - WRITE_BLOCK
- m_data[0] = 0;
- send_data(1);
- m_state = SD_STATE_WRITE_WAITFE;
- break;
+ case 17: // CMD17 - READ_SINGLE_BLOCK
+ if (m_harddisk)
+ {
+ m_data[0] = 0x00; // initial R1 response
+ // data token occurs some time after the R1 response. A2SD expects at least 1
+ // byte of space between R1 and the data packet.
+ m_data[1] = 0xff;
+ m_data[2] = 0xfe; // data token
+ u32 blk = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]);
+ if (m_type == SD_TYPE_V2)
+ {
+ blk /= m_blksize;
+ }
+ LOGMASKED(LOG_GENERAL, "reading LBA %x\n", blk);
+ hard_disk_read(m_harddisk, blk, &m_data[3]);
+ {
+ util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize);
+ m_data[m_blksize + 3] = (crc16 >> 8) & 0xff;
+ m_data[m_blksize + 4] = (crc16 & 0xff);
+ }
+ send_data(3 + m_blksize + 2, SD_STATE_DATA);
+ }
+ else
+ {
+ m_data[0] = 0xff; // show an error
+ send_data(1, SD_STATE_DATA);
+ }
+ break;
- case 41:
- if (m_bACMD) // ACMD41 - SD_SEND_OP_COND
- {
+ case 18: // CMD18 - CMD_READ_MULTIPLE_BLOCK
+ if (m_harddisk)
+ {
+ m_data[0] = 0x00; // initial R1 response
+ // data token occurs some time after the R1 response. A2SD
+ // expects at least 1 byte of space between R1 and the data
+ // packet.
+ m_blknext = (u32(m_cmd[1]) << 24) | (u32(m_cmd[2]) << 16) | (u32(m_cmd[3]) << 8) | u32(m_cmd[4]);
+ if (m_type == SD_TYPE_V2)
+ {
+ m_blknext /= m_blksize;
+ }
+ }
+ else
+ {
+ m_data[0] = 0xff; // show an error
+ }
+ send_data(1, SD_STATE_DATA_MULTI);
+ break;
+
+ case 24: // CMD24 - WRITE_BLOCK
m_data[0] = 0;
- }
- else // CMD41 - illegal
- {
- m_data[0] = 0xff;
- }
- send_data(1);
- break;
+ send_data(1, SD_STATE_RCV);
+ break;
- case 55: // CMD55 - APP_CMD
- m_data[0] = 0x01;
- send_data(1);
- break;
+ case 41:
+ if (m_bACMD) // ACMD41 - SD_SEND_OP_COND
+ {
+ m_data[0] = 0;
+ send_data(1, SD_STATE_READY); // + SD_STATE_IDLE
+ }
+ else // CMD41 - illegal
+ {
+ m_data[0] = 0xff;
+ send_data(1, SD_STATE_INA);
+ }
+ break;
+
+ case 55: // CMD55 - APP_CMD
+ m_data[0] = 0x01;
+ send_data(1, SD_STATE_IDLE);
+ break;
- case 58: // CMD58 - READ_OCR
- m_data[0] = 0;
- if (m_type == SD_TYPE_HC)
+ case 58: // CMD58 - READ_OCR
+ m_data[0] = 0;
+ if (m_type == SD_TYPE_HC)
+ {
+ m_data[1] = 0x40; // indicate SDHC support
+ }
+ else
+ {
+ m_data[1] = 0;
+ }
+ m_data[2] = 0;
+ m_data[3] = 0;
+ m_data[4] = 0;
+ send_data(5, SD_STATE_DATA);
+ break;
+
+ default:
+ LOGMASKED(LOG_COMMAND, "SDCARD: Unsupported %02x\n", m_cmd[0] & 0x3f);
+ clean_cmd = false;
+ break;
+ }
+
+ // if this is command 55, that's a prefix indicating the next command is an "app command" or "ACMD"
+ if ((m_cmd[0] & 0x3f) == 55)
{
- m_data[1] = 0x40; // indicate SDHC support
+ m_bACMD = true;
}
else
{
- m_data[1] = 0;
+ m_bACMD = false;
}
- m_data[2] = 0;
- m_data[3] = 0;
- m_data[4] = 0;
- send_data(5);
- break;
-
- default:
- break;
- }
- // if this is command 55, that's a prefix indicating the next command is an "app command" or "ACMD"
- if ((m_cmd[0] & 0x3f) == 55)
- {
- m_bACMD = true;
- }
- else
- {
- m_bACMD = false;
+ if (clean_cmd)
+ {
+ for (u8 i = 0; i < 6; i++)
+ {
+ m_cmd[i] = 0xff;
+ }
+ }
}
}
+
+void spi_sdcard_device::change_state(sd_state new_state)
+{
+ // TODO validate if transition is valid using refs below.
+ // REF Figure 4-13:SD Memory Card State Diagram (Transition Mode)
+ // REF Table 4-35:Card State Transition Table
+ m_state = new_state;
+}
diff --git a/src/devices/machine/spi_sdcard.h b/src/devices/machine/spi_sdcard.h
index 23a292664d2..544c884510f 100644
--- a/src/devices/machine/spi_sdcard.h
+++ b/src/devices/machine/spi_sdcard.h
@@ -21,7 +21,7 @@ public:
devcb_write_line write_miso;
protected:
- enum
+ enum sd_type : u8
{
SD_TYPE_V2 = 0,
SD_TYPE_HC
@@ -35,24 +35,41 @@ protected:
required_device<harddisk_image_device> m_image;
- int m_type;
+ sd_type m_type;
private:
- enum
+ enum sd_state : u8
{
+ //REF Table 4-1:Overview of Card States vs. Operation Mode
SD_STATE_IDLE = 0,
+ SD_STATE_READY,
+ SD_STATE_IDENT,
+ SD_STATE_STBY,
+ SD_STATE_TRAN,
+ SD_STATE_DATA,
+ SD_STATE_DATA_MULTI, // synthetical state for this implementation
+ SD_STATE_RCV,
+ SD_STATE_PRG,
+ SD_STATE_DIS,
+ SD_STATE_INA,
+
+ //FIXME Existing states wich must be revisited
SD_STATE_WRITE_WAITFE,
SD_STATE_WRITE_DATA
};
+ sd_state m_state;
- void send_data(int count);
+ void send_data(u16 count, sd_state new_state);
void do_command();
+ void change_state(sd_state new_state);
u8 m_data[520], m_cmd[6];
hard_disk_file *m_harddisk;
- u8 m_in_latch, m_out_latch;
- int m_cmd_ptr, m_state, m_out_ptr, m_out_count, m_ss, m_in_bit, m_cur_bit, m_write_ptr, m_blksize;
+ int m_ss, m_in_bit;
+ u8 m_in_latch, m_out_latch, m_cur_bit;
+ u16 m_out_count, m_out_ptr, m_write_ptr, m_blksize;
+ u32 m_blknext;
bool m_bACMD;
};