summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/machine/spi_sdcard.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/machine/spi_sdcard.cpp')
-rw-r--r--src/devices/machine/spi_sdcard.cpp547
1 files changed, 416 insertions, 131 deletions
diff --git a/src/devices/machine/spi_sdcard.cpp b/src/devices/machine/spi_sdcard.cpp
index 57ef4e87969..5fd7526ef25 100644
--- a/src/devices/machine/spi_sdcard.cpp
+++ b/src/devices/machine/spi_sdcard.cpp
@@ -31,78 +31,309 @@
#include "multibyte.h"
+#include <algorithm>
+
#define LOG_COMMAND (1U << 1)
#define LOG_SPI (1U << 2)
-//#define VERBOSE (LOG_COMMAND)
-#define LOG_OUTPUT_FUNC osd_printf_info
+//#define VERBOSE (LOG_GENERAL | LOG_COMMAND)
+//#define LOG_OUTPUT_FUNC osd_printf_info
#include "logmacro.h"
-static constexpr u8 DATA_RESPONSE_OK = 0x05;
-static constexpr u8 DATA_RESPONSE_IO_ERROR = 0x0d;
-DEFINE_DEVICE_TYPE(SPI_SDCARD, spi_sdcard_sdhc_device, "spi_sdhccard", "SDHC Card (SPI Interface)")
-DEFINE_DEVICE_TYPE(SPI_SDCARDV2, spi_sdcard_sdv2_device, "spi_sdv2card", "SDV2 Card (SPI Interface)")
+namespace {
+
+constexpr u8 DATA_RESPONSE_OK = 0x05;
+constexpr u8 DATA_RESPONSE_IO_ERROR = 0x0d;
+
+enum {
+ CSD_STRUCTURE_V10 = 0x0,
+ CSD_STRUCTURE_V20 = 0x1,
+
+ TAAC_UNIT_1NS = 0x00,
+ TAAC_UNIT_10NS = 0x01,
+ TAAC_UNIT_100NS = 0x02,
+ TAAC_UNIT_1US = 0x03,
+ TAAC_UNIT_10US = 0x04,
+ TAAC_UNIT_100US = 0x05,
+ TAAC_UNIT_1MS = 0x06,
+ TAAC_UNIT_10MS = 0x07,
+
+ TAAC_VALUE_1_0 = 0x08,
+ TAAC_VALUE_1_2 = 0x10,
+ TAAC_VALUE_1_3 = 0x18,
+ TAAC_VALUE_1_5 = 0x20,
+ TAAC_VALUE_2_0 = 0x28,
+ TAAC_VALUE_2_5 = 0x30,
+ TAAC_VALUE_3_0 = 0x38,
+ TAAC_VALUE_3_5 = 0x40,
+ TAAC_VALUE_4_0 = 0x48,
+ TAAC_VALUE_4_5 = 0x50,
+ TAAC_VALUE_5_0 = 0x58,
+ TAAC_VALUE_5_5 = 0x60,
+ TAAC_VALUE_6_0 = 0x68,
+ TAAC_VALUE_7_0 = 0x70,
+ TAAC_VALUE_8_0 = 0x78,
+
+ TRAN_SPEED_UNIT_100K = 0x00,
+ TRAN_SPEED_UNIT_1M = 0x01,
+ TRAN_SPEED_UNIT_10M = 0x02,
+ TRAN_SPEED_UNIT_100M = 0x03,
+
+ TRAN_SPEED_VALUE_1_0 = 0x08,
+ TRAN_SPEED_VALUE_1_2 = 0x10,
+ TRAN_SPEED_VALUE_1_3 = 0x18,
+ TRAN_SPEED_VALUE_1_5 = 0x20,
+ TRAN_SPEED_VALUE_2_0 = 0x28,
+ TRAN_SPEED_VALUE_2_5 = 0x30,
+ TRAN_SPEED_VALUE_3_0 = 0x38,
+ TRAN_SPEED_VALUE_3_5 = 0x40,
+ TRAN_SPEED_VALUE_4_0 = 0x48,
+ TRAN_SPEED_VALUE_4_5 = 0x50,
+ TRAN_SPEED_VALUE_5_0 = 0x58,
+ TRAN_SPEED_VALUE_5_5 = 0x60,
+ TRAN_SPEED_VALUE_6_0 = 0x68,
+ TRAN_SPEED_VALUE_7_0 = 0x70,
+ TRAN_SPEED_VALUE_8_0 = 0x78,
+
+ VDD_CURR_MIN_0_5MA = 0x00,
+ VDD_CURR_MIN_1MA = 0x01,
+ VDD_CURR_MIN_5MA = 0x02,
+ VDD_CURR_MIN_10MA = 0x03,
+ VDD_CURR_MIN_25MA = 0x04,
+ VDD_CURR_MIN_35MA = 0x05,
+ VDD_CURR_MIN_60MA = 0x06,
+ VDD_CURR_MIN_100MA = 0x07,
+
+ VDD_CURR_MAX_1MA = 0x00,
+ VDD_CURR_MAX_5MA = 0x01,
+ VDD_CURR_MAX_10MA = 0x02,
+ VDD_CURR_MAX_25MA = 0x03,
+ VDD_CURR_MAX_35MA = 0x04,
+ VDD_CURR_MAX_45MA = 0x05,
+ VDD_CURR_MAX_80MA = 0x06,
+ VDD_CURR_MAX_200MA = 0x07
+};
+
+} // anonymous namespace
+
+enum spi_sdcard_device::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 which must be revisited
+ SD_STATE_WRITE_WAITFE,
+ SD_STATE_WRITE_DATA
+};
+
+ALLOW_SAVE_TYPE(spi_sdcard_device::sd_state);
+
+
+DEFINE_DEVICE_TYPE(SPI_SDCARD, spi_sdcard_device, "spi_sdcard", "SD Card (SPI interface)")
+
+spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock) :
+ spi_sdcard_device(mconfig, SPI_SDCARD, tag, owner, clock)
+{
+}
-spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) :
+spi_sdcard_device::spi_sdcard_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, u32 clock) :
device_t(mconfig, type, tag, owner, clock),
write_miso(*this),
m_image(*this, "image"),
+ m_preferred_type(SD_TYPE_V2),
+ m_blksize(512),
+ m_type(SD_TYPE_V2),
m_state(SD_STATE_IDLE),
m_ss(0), m_in_bit(0), m_clk_state(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_out_delay(0), m_out_count(0), m_out_ptr(0), m_write_ptr(0), m_xferblk(512), m_blknext(0),
+ m_crc_off(true),
m_bACMD(false)
{
+ std::fill(std::begin(m_csd), std::end(m_csd), 0);
+ std::fill(std::begin(m_cmd), std::end(m_cmd), 0xff);
}
-spi_sdcard_sdv2_device::spi_sdcard_sdv2_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- spi_sdcard_device(mconfig, SPI_SDCARDV2, tag, owner, clock)
-{
- m_type = SD_TYPE_V2;
-}
-
-spi_sdcard_sdhc_device::spi_sdcard_sdhc_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) :
- spi_sdcard_device(mconfig, SPI_SDCARD, tag, owner, clock)
+spi_sdcard_device::~spi_sdcard_device()
{
- 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()
{
+ m_data = make_unique_clear<u8 []>(2048 + 8);
+
+ save_pointer(NAME(m_data), 2048 + 8);
+ save_item(NAME(m_cmd));
save_item(NAME(m_state));
- save_item(NAME(m_in_latch));
- save_item(NAME(m_out_latch));
- save_item(NAME(m_out_ptr));
- save_item(NAME(m_out_count));
save_item(NAME(m_ss));
save_item(NAME(m_in_bit));
save_item(NAME(m_clk_state));
+ save_item(NAME(m_in_latch));
+ save_item(NAME(m_out_latch));
save_item(NAME(m_cur_bit));
+ save_item(NAME(m_out_delay));
+ save_item(NAME(m_out_count));
+ save_item(NAME(m_out_ptr));
save_item(NAME(m_write_ptr));
- save_item(NAME(m_blksize));
+ save_item(NAME(m_xferblk));
save_item(NAME(m_blknext));
- save_item(NAME(m_type));
- save_item(NAME(m_cmd));
- save_item(NAME(m_data));
+ save_item(NAME(m_crc_off));
save_item(NAME(m_bACMD));
}
-void spi_sdcard_device::device_reset()
+std::error_condition spi_sdcard_device::image_loaded(device_image_interface &image)
+{
+ // need block size and total blocks to create CSD
+ auto const info = m_image->get_info();
+ u64 const total_blocks = u64(info.cylinders) * info.heads * info.sectors;
+ if (!total_blocks)
+ {
+ osd_printf_error("%s: SD Card cannot mount a zero-block image\n", tag());
+ return image_error::INVALIDIMAGE;
+ }
+
+ // ensure block size can be expressed in the CSD
+ if ((info.sectorbytes & (info.sectorbytes - 1)) || !info.sectorbytes || (512 > info.sectorbytes) || (2048 < info.sectorbytes))
+ {
+ osd_printf_error("%s: SD Card cannot use sector size %u (must be a power of 2 from 512 to 2048)\n", tag());
+ return image_error::INVALIDIMAGE;
+ }
+ u8 block_size_exp = 0;
+ for (auto i = info.sectorbytes; !BIT(i, 0); i >>= 1)
+ ++block_size_exp;
+
+ // see how we can express the total block count
+ u64 total_mant = total_blocks;
+ u8 total_exp = 0;
+ while (!BIT(total_mant, 0))
+ {
+ total_mant >>= 1;
+ ++total_exp;
+ }
+ bool const sd_ok = (2 <= total_exp) && ((1 << 12) >= (total_mant << ((9 < total_exp) ? (total_exp - 9) : 0)));
+ bool const sdhc_ok = (512 == info.sectorbytes) && (10 <= total_exp) && ((u32(1) << 16) >= (total_mant << (total_exp - 10)));
+ if (!sd_ok && !sdhc_ok)
+ {
+ osd_printf_error("%s: SD Card image size %u blocks of %u bytes is not supported by SD or SDHC\n", tag(), total_blocks, info.sectorbytes);
+ return image_error::INVALIDIMAGE;
+ }
+
+ try
+ {
+ m_sectorbuf.resize(info.sectorbytes);
+ }
+ catch (std::bad_alloc const &)
+ {
+ osd_printf_error("%s: Error allocating %u-byte SD Card sector buffer\n", tag(), info.sectorbytes);
+ return std::errc::not_enough_memory;
+ }
+
+ m_blksize = m_xferblk = info.sectorbytes;
+ m_crc_off = true;
+
+ // set up common CSD fields
+ m_csd[0] = 0x00; // 127: CSD_STRUCTURE:2 (00b) 0:6
+ m_csd[1] = 0x00; // 119: TAAC:8
+ m_csd[2] = 0x00; // 111: NSAC:8
+ m_csd[3] = TRAN_SPEED_UNIT_10M | TRAN_SPEED_VALUE_2_5; // 103: TRAN_SPEED:8 (32h for 25MHz or 5Ah for 50MHz)
+ m_csd[4] = 0x5b; // 95: CCC:12 (01x110110101b)
+ m_csd[5] = 0x50; // .. READ_BL_LN:4
+ m_csd[5] |= block_size_exp;
+ m_csd[6] = 0x00; // 79: READ_BL_PARTIAL:1 WRITE_BLK_MISALIGN:1 READ_BLK_MISALIGN:1 DSR_IMP:1 0:2 C_SIZE:12
+ m_csd[7] = 0x00; // ..
+ m_csd[8] = 0x00; // .. VDD_R_CURR_MIN:3 VDD_R_CURR_MAX:3
+ m_csd[9] = 0x00; // 55: VDD_W_CURR_MIN:3 VDD_W_CURR_MAX:3 C_SIZE_MUL:3
+ m_csd[10] = 0x3f; // .. ERASE_BLK_EN:1 SECTOR_SIZE:7
+ m_csd[11] = 0x80; // .. WP_GRP_SIZE:7
+ m_csd[12] = 0x04; // 31: WP_GRP_ENABLE:1 0:2 R2W_FACTOR:3 WRITE_BL_LEN:4
+ m_csd[12] |= BIT(block_size_exp, 2, 2);
+ m_csd[13] = 0x00; // .. WRITE_BL_PARTIAL:1 0:5
+ m_csd[13] |= BIT(block_size_exp, 0, 2) << 6;
+ m_csd[14] = 0x00; // 15: FILE_FORMAT_GRP:1 COPY:1 PERM_WRITE_PROTECT:1 TMP_WRITE_PROTECT:1 FILE_FORMAT:2 WP_UPC:1 0:1
+ m_csd[15] = 0x01; // 7: CRC7 1:1
+
+ if (sdhc_ok && ((SD_TYPE_HC == m_preferred_type) || !sd_ok))
+ {
+ u32 const c_size = (total_blocks >> 10) - 1;
+ osd_printf_verbose(
+ "%s: SD Card image mounted as SDHC, %u blocks of %u bytes, device size ((%u + 1) << 10) * (1 << %u)\n",
+ tag(),
+ total_blocks, info.sectorbytes,
+ c_size, block_size_exp);
+
+ m_type = SD_TYPE_HC;
+
+ // set up CSD Version 2.0
+ m_csd[0] |= CSD_STRUCTURE_V20 << 6; // 127: CSD_STRUCTURE:2 (00b) 0:6
+ m_csd[1] = TAAC_UNIT_1MS | TAAC_VALUE_1_0; // 119: TAAC:8
+
+ m_csd[7] |= BIT(c_size, 16, 6); // .. C_SIZE:22
+ m_csd[8] |= BIT(c_size, 8, 8); // ..
+ m_csd[9] |= BIT(c_size, 0, 8); // ..
+ }
+ else
+ {
+ u8 const c_size_mult = std::min<u8>(total_exp, 9) - 2;
+ u16 const c_size = (total_blocks >> (c_size_mult + 2)) - 1;
+ osd_printf_verbose(
+ "%s: SD Card image mounted as SD, %u blocks of %u bytes, device size ((%u + 1) << (%u + 2)) * (1 << %u)\n",
+ tag(),
+ total_blocks, info.sectorbytes,
+ c_size, c_size_mult, block_size_exp);
+
+ m_type = SD_TYPE_V2;
+
+ // set up CSD Version 1.0
+ m_csd[0] |= CSD_STRUCTURE_V10 << 6; // 127: CSD_STRUCTURE:2 (00b) 0:6
+ m_csd[1] = TAAC_UNIT_1MS | TAAC_VALUE_1_5; // 119: TAAC:8
+
+ m_csd[6] |= 0x80; // 79: READ_BL_PARTIAL:1 WRITE_BLK_MISALIGN:1 READ_BLK_MISALIGN:1 DSR_IMP:1 0:2 C_SIZE:12
+ m_csd[6] |= BIT(c_size, 10, 2);
+ m_csd[7] |= BIT(c_size, 2, 8); // ..
+ m_csd[8] |= BIT(c_size, 0, 2) << 6; // .. VDD_R_CURR_MIN:3 VDD_R_CURR_MAX:3
+ m_csd[8] |= VDD_CURR_MIN_100MA << 3;
+ m_csd[8] |= VDD_CURR_MAX_80MA;
+ m_csd[9] |= VDD_CURR_MIN_100MA << 5; // 55: VDD_W_CURR_MIN:3 VDD_W_CURR_MAX:3 C_SIZE_MUL:3
+ m_csd[9] |= VDD_CURR_MAX_80MA << 2;
+ m_csd[9] |= BIT(c_size_mult, 1, 2);
+ m_csd[10] |= BIT(c_size_mult, 0, 1) << 7; // .. ERASE_BLK_EN:1 SECTOR_SIZE:7
+ m_csd[11] |= 0x3f; // .. WP_GRP_SIZE:7
+ }
+
+ // TODO: calculate CRC7
+
+ LOG("Generated CSD %016x%016x\n", get_u64be(&m_csd[0]), get_u64be(&m_csd[8]));
+
+ return std::error_condition();
+}
+
+void spi_sdcard_device::image_unloaded(device_image_interface &image)
{
+ std::fill(std::begin(m_csd), std::end(m_csd), 0);
}
void spi_sdcard_device::device_add_mconfig(machine_config &config)
{
- HARDDISK(config, m_image).set_interface("spi_sdcard");
+ HARDDISK(config, m_image).set_interface("sdcard");
+ m_image->set_device_load(FUNC(spi_sdcard_device::image_loaded));
+ m_image->set_device_unload(FUNC(spi_sdcard_device::image_unloaded));
}
-void spi_sdcard_device::send_data(u16 count, sd_state new_state)
+void spi_sdcard_device::send_data(u16 count, sd_state new_state, u8 delay)
{
+ m_out_delay = delay;
m_out_ptr = 0;
m_out_count = count;
change_state(new_state);
@@ -127,6 +358,26 @@ void spi_sdcard_device::spi_clock_w(int state)
m_clk_state = state;
}
+void spi_sdcard_device::spi_ss_w(int state)
+{
+ if (!m_ss && state)
+ {
+ LOGMASKED(LOG_SPI, "SDCARD: selected\n");
+ std::fill(std::begin(m_cmd), std::end(m_cmd), 0xff);
+ m_state = SD_STATE_IDLE;
+ m_in_latch = 0;
+ m_cur_bit = 0;
+ m_out_latch = 0xff;
+ m_out_delay = 0;
+ m_out_count = 0;
+ }
+ else if (m_ss && !state)
+ {
+ LOGMASKED(LOG_SPI, "SDCARD: deselected\n");
+ }
+ m_ss = state;
+}
+
void spi_sdcard_device::latch_in()
{
m_in_latch &= ~0x01;
@@ -136,32 +387,22 @@ void spi_sdcard_device::latch_in()
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)
+ if (m_state == SD_STATE_WRITE_WAITFE)
{
- 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;
}
- break;
-
- case SD_STATE_WRITE_DATA:
+ }
+ else if (m_state == SD_STATE_WRITE_DATA)
+ {
m_data[m_write_ptr++] = m_in_latch;
- if (m_write_ptr == (m_blksize + 2))
+ if (m_write_ptr == (m_xferblk + 2))
{
LOG("writing LBA %x, data %02x %02x %02x %02x\n", m_blknext, m_data[0], m_data[1], m_data[2], m_data[3]);
+ // TODO: this is supposed to be a CRC response, the actual write will take some time
if (m_image->write(m_blknext, &m_data[0]))
{
m_data[0] = DATA_RESPONSE_OK;
@@ -172,28 +413,32 @@ void spi_sdcard_device::latch_in()
}
m_data[1] = 0x01;
- send_data(2, SD_STATE_IDLE);
+ send_data(2, SD_STATE_IDLE, 0); // zero delay - must immediately follow the data
}
- break;
+ }
+ else // receive CMD
+ {
+ std::memmove(m_cmd, m_cmd + 1, 5);
+ m_cmd[5] = m_in_latch;
- case SD_STATE_DATA_MULTI:
- do_command();
- if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0)
+ if (m_state == SD_STATE_DATA_MULTI)
{
- m_data[0] = 0xfe; // data token
- m_image->read(m_blknext++, &m_data[1]);
- util::crc16_t crc16 = util::crc16_creator::simple(&m_data[1], m_blksize);
- put_u16be(&m_data[m_blksize + 1], crc16);
- send_data(1 + m_blksize + 2, SD_STATE_DATA_MULTI);
+ do_command();
+ if (m_state == SD_STATE_DATA_MULTI && m_out_count == 0)
+ {
+ // FIXME: support multi-block read when transfer size is smaller than block size
+ m_data[0] = 0xfe; // data token
+ m_image->read(m_blknext++, &m_data[1]);
+ util::crc16_t crc16 = util::crc16_creator::simple(&m_data[1], m_blksize);
+ put_u16be(&m_data[m_blksize + 1], crc16);
+ LOG("reading LBA %x: [0] %02x %02x .. [%d] %02x %02x [crc16] %04x\n", m_blknext - 1, m_data[1], m_data[2], m_blksize - 2, m_data[m_blksize - 1], m_data[m_blksize], crc16);
+ 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
+ else if ((m_state == SD_STATE_IDLE) || (((m_cmd[0] & 0x70) == 0x40) || (m_out_count == 0))) // CMD0 - GO_IDLE_STATE
{
do_command();
}
- break;
}
}
}
@@ -208,13 +453,13 @@ void spi_sdcard_device::shift_out()
m_cur_bit &= 0x07;
if (m_cur_bit == 0)
{
- if (m_out_ptr < SPI_DELAY_RESPONSE)
+ if (m_out_ptr < m_out_delay)
{
m_out_ptr++;
}
else if (m_out_count > 0)
{
- m_out_latch = m_data[m_out_ptr - SPI_DELAY_RESPONSE];
+ m_out_latch = m_data[m_out_ptr - m_out_delay];
m_out_ptr++;
LOGMASKED(LOG_SPI, "SDCARD: latching %02x (start of shift)\n", m_out_latch);
m_out_count--;
@@ -225,7 +470,7 @@ void spi_sdcard_device::shift_out()
void spi_sdcard_device::do_command()
{
- if (((m_cmd[0] & 0xc0) == 0x40) && (m_cmd[5] & 1))
+ if (((m_cmd[0] & 0xc0) == 0x40) && ((m_cmd[5] & 1) || m_crc_off))
{
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;
@@ -259,21 +504,25 @@ void spi_sdcard_device::do_command()
break;
case 9: // CMD9 - SEND_CSD
- m_data[0] = 0x00; // TODO
- send_data(1, SD_STATE_STBY);
+ m_data[0] = 0x00;
+ m_data[1] = 0xff;
+ m_data[2] = 0xfe;
+ std::copy(std::begin(m_csd), std::end(m_csd), &m_data[3]);
+
+ send_data(3 + std::size(m_csd), SD_STATE_STBY);
break;
case 10: // CMD10 - SEND_CID
- m_data[0] = 0x00; // initial R1 response
- m_data[1] = 0xff; // 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[0] = 0x00; // initial R1 response
+ m_data[1] = 0xff; // 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)
{
@@ -292,26 +541,32 @@ void spi_sdcard_device::do_command()
case 12: // CMD12 - STOP_TRANSMISSION
m_data[0] = 0;
- send_data(1, m_state == SD_STATE_RCV ? SD_STATE_PRG : SD_STATE_TRAN);
+ send_data(1, (m_state == SD_STATE_RCV) ? SD_STATE_PRG : SD_STATE_TRAN);
break;
case 13: // CMD13 - SEND_STATUS
m_data[0] = 0; // TODO
- send_data(1, SD_STATE_STBY);
+ m_data[1] = 0;
+ send_data(2, SD_STATE_STBY);
break;
case 16: // CMD16 - SET_BLOCKLEN
- m_blksize = get_u16be(&m_cmd[3]);
- if (m_image->set_block_size(m_blksize))
+ if (m_image->exists())
{
- m_data[0] = 0;
+ u16 const blocklen = get_u16be(&m_cmd[3]);
+ if (blocklen && ((m_type == SD_TYPE_V2) || (blocklen == m_blksize)) && (blocklen <= m_blksize))
+ {
+ m_xferblk = blocklen;
+ m_data[0] = 0x00;
+ }
+ else
+ {
+ m_data[0] = 0x40; // parameter error
+ }
}
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);
+ m_data[0] = 0xff; // show an error
}
send_data(1, SD_STATE_TRAN);
break;
@@ -325,53 +580,101 @@ void spi_sdcard_device::do_command()
m_data[1] = 0xff;
m_data[2] = 0xfe; // data token
u32 blk = get_u32be(&m_cmd[1]);
- if (m_type == SD_TYPE_V2)
+ if ((m_type == SD_TYPE_V2) && ((blk / m_blksize) != ((blk + (m_xferblk - 1)) / m_blksize)))
{
- blk /= m_blksize;
+ LOG("rejecting read of %u bytes at %u that crosses %u-byte block boundary\n", m_xferblk, blk, m_blksize);
+ m_data[0] = 0x40; // parameter error
+ send_data(1, SD_STATE_TRAN);
}
- LOG("reading LBA %x\n", blk);
- m_image->read(blk, &m_data[3]);
+ else if (m_xferblk == m_blksize)
{
- util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_blksize);
- put_u16be(&m_data[m_blksize + 3], crc16);
+ // optimise for reading an entire block
+ if (m_type == SD_TYPE_V2)
+ {
+ blk /= m_blksize;
+ }
+ m_image->read(blk, &m_data[3]);
+ util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_xferblk);
+ put_u16be(&m_data[m_xferblk + 3], crc16);
+ LOG("reading LBA %x: [0] %02x %02x .. [%d] %02x %02x [crc16] %04x\n", blk, m_data[3], m_data[4], m_xferblk - 2, m_data[m_xferblk + 1], m_data[m_xferblk + 2], crc16);
+ send_data(3 + m_xferblk + 2, SD_STATE_DATA);
+ }
+ else
+ {
+ assert(m_type == SD_TYPE_V2);
+ m_image->read(blk / m_blksize, &m_sectorbuf[0]);
+ std::copy_n(&m_sectorbuf[blk % m_blksize], m_xferblk, &m_data[3]);
+ util::crc16_t crc16 = util::crc16_creator::simple(&m_data[3], m_xferblk);
+ put_u16be(&m_data[m_xferblk + 3], crc16);
+ LOG("reading LBA %x+%x: [0] %02x %02x .. [%d] %02x %02x [crc16] %04x\n", blk / m_blksize, blk % m_blksize, m_data[3], m_data[4], m_xferblk - 2, m_data[m_xferblk + 1], m_data[m_xferblk + 2], crc16);
+ send_data(3 + m_xferblk + 2, SD_STATE_DATA);
}
- send_data(3 + m_blksize + 2, SD_STATE_DATA);
}
else
{
m_data[0] = 0xff; // show an error
- send_data(1, SD_STATE_DATA);
+ send_data(1, SD_STATE_TRAN);
}
break;
case 18: // CMD18 - CMD_READ_MULTIPLE_BLOCK
if (m_image->exists())
{
- 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 = get_u32be(&m_cmd[1]);
- if (m_type == SD_TYPE_V2)
+ if (m_xferblk == m_blksize)
+ {
+ 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 = get_u32be(&m_cmd[1]);
+ if (m_type == SD_TYPE_V2)
+ {
+ m_blknext /= m_xferblk;
+ }
+ send_data(1, SD_STATE_DATA_MULTI);
+ }
+ else
{
- m_blknext /= m_blksize;
+ // FIXME: support multi-block read when transfer size is smaller than block size
+ m_data[0] = 0x40; // parameter error
+ send_data(1, SD_STATE_TRAN);
}
}
else
{
m_data[0] = 0xff; // show an error
+ send_data(1, SD_STATE_TRAN);
}
- send_data(1, SD_STATE_DATA_MULTI);
break;
case 24: // CMD24 - WRITE_BLOCK
- m_data[0] = 0;
- m_blknext = get_u32be(&m_cmd[1]);
- if (m_type == SD_TYPE_V2)
+ if (m_xferblk != m_blksize)
{
- m_blknext /= m_blksize;
+ // partial block write not supported
+ LOG("rejecting write of %u bytes that is not a full %u-byte block\n", m_xferblk, m_blksize);
+ m_data[0] = 0x40; // parameter error
+ send_data(1, SD_STATE_TRAN);
+ }
+ else
+ {
+ m_blknext = get_u32be(&m_cmd[1]);
+ if ((m_type == SD_TYPE_V2) && (m_blknext % m_blksize))
+ {
+ // misaligned write not supported
+ LOG("rejecting write of %u bytes at %u that crosses %u-byte block boundary\n", m_xferblk, m_blknext, m_blksize);
+ m_data[0] = 0x40; // parameter error
+ send_data(1, SD_STATE_TRAN);
+ }
+ else
+ {
+ if (m_type == SD_TYPE_V2)
+ {
+ m_blknext /= m_xferblk;
+ }
+ m_data[0] = 0;
+ send_data(1, SD_STATE_WRITE_WAITFE);
+ }
}
- send_data(1, SD_STATE_WRITE_WAITFE);
break;
case 41:
@@ -394,14 +697,8 @@ void spi_sdcard_device::do_command()
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[1] = 0x80; // Busy Status: 1b - Initialization Complete
+ m_data[1] |= (m_type == SD_TYPE_V2) ? 0 : 0x40; // Card Capacity Status: 0b - SDCS, 1b SDHC, SDXC
m_data[2] = 0;
m_data[3] = 0;
m_data[4] = 0;
@@ -409,34 +706,22 @@ void spi_sdcard_device::do_command()
break;
case 59: // CMD59 - CRC_ON_OFF
+ m_crc_off = !BIT(m_cmd[4], 0);
m_data[0] = 0;
- // TODO CRC 1-on, 0-off
send_data(1, SD_STATE_STBY);
break;
default:
- LOGMASKED(LOG_COMMAND, "SDCARD: Unsupported %02x\n", m_cmd[0] & 0x3f);
+ LOGMASKED(LOG_COMMAND, "SDCARD: Unsupported CMD%02d\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_bACMD = true;
- }
- else
- {
- m_bACMD = false;
- }
+ m_bACMD = (m_cmd[0] & 0x3f) == 55;
if (clean_cmd)
- {
- for (u8 i = 0; i < 6; i++)
- {
- m_cmd[i] = 0xff;
- }
- }
+ memset(m_cmd, 0xff, 6);
}
}