// license:BSD-3-Clause // copyright-holders:Fabio Priuli, MetalliC /*************************************************************************** MegaDrive / Genesis Cart + STM95 EEPROM device TO DO: split STM95 to a separate device... ***************************************************************************/ #include "emu.h" #include "stm95.h" stm95_eeprom_device::stm95_eeprom_device(running_machine &machine, uint8_t *eeprom) : stm_state(IDLE), stream_pos(0), m_machine(machine) { eeprom_data = eeprom; m_machine.save().save_item(latch, "STM95/latch"); m_machine.save().save_item(reset_line, "STM95/reset_line"); m_machine.save().save_item(sck_line, "STM95/sck_line"); m_machine.save().save_item(WEL, "STM95/WEL"); m_machine.save().save_item(stream_pos, "STM95/stream_pos"); m_machine.save().save_item(stream_data, "STM95/stream_data"); m_machine.save().save_item(eeprom_addr, "STM95/eeprom_addr"); } void stm95_eeprom_device::set_cs_line(int state) { reset_line = state; if (reset_line != CLEAR_LINE) { stream_pos = 0; stm_state = IDLE; } } void stm95_eeprom_device::set_si_line(int state) { latch = state; } int stm95_eeprom_device::get_so_line(void) { if (stm_state == READING || stm_state == CMD_RDSR) return (stream_data >> 8) & 1; else return 0; } void stm95_eeprom_device::set_sck_line(int state) { if (reset_line == CLEAR_LINE) { if (state == ASSERT_LINE && sck_line == CLEAR_LINE) { switch (stm_state) { case IDLE: stream_data = (stream_data << 1) | (latch ? 1 : 0); stream_pos++; if (stream_pos == 8) { stream_pos = 0; //printf("STM95 EEPROM: got cmd %02X\n", stream_data&0xff); switch(stream_data & 0xff) { case 0x01: // write status register if (WEL != 0) stm_state = CMD_WRSR; WEL = 0; break; case 0x02: // write if (WEL != 0) stm_state = CMD_WRITE; stream_data = 0; WEL = 0; break; case 0x03: // read stm_state = M95320_CMD_READ; stream_data = 0; break; case 0x04: // write disable WEL = 0; break; case 0x05: // read status register stm_state = CMD_RDSR; stream_data = WEL<<1; break; case 0x06: // write enable WEL = 1; break; default: machine().logerror("STM95 EEPROM: unknown cmd %02X\n", stream_data&0xff); } } break; case CMD_WRSR: stream_pos++; // just skip, don't care block protection if (stream_pos == 8) { stm_state = IDLE; stream_pos = 0; } break; case CMD_RDSR: stream_data = stream_data<<1; stream_pos++; if (stream_pos == 8) { stm_state = IDLE; stream_pos = 0; } break; case M95320_CMD_READ: stream_data = (stream_data << 1) | (latch ? 1 : 0); stream_pos++; if (stream_pos == 16) { eeprom_addr = stream_data & (M95320_SIZE - 1); stream_data = eeprom_data[eeprom_addr]; stm_state = READING; stream_pos = 0; } break; case READING: stream_data = stream_data<<1; stream_pos++; if (stream_pos == 8) { if (++eeprom_addr == M95320_SIZE) eeprom_addr = 0; stream_data |= eeprom_data[eeprom_addr]; stream_pos = 0; } break; case CMD_WRITE: stream_data = (stream_data << 1) | (latch ? 1 : 0); stream_pos++; if (stream_pos == 16) { eeprom_addr = stream_data & (M95320_SIZE - 1); stm_state = WRITING; stream_pos = 0; } break; case WRITING: stream_data = (stream_data << 1) | (latch ? 1 : 0); stream_pos++; if (stream_pos == 8) { eeprom_data[eeprom_addr] = stream_data; if (++eeprom_addr == M95320_SIZE) eeprom_addr = 0; stream_pos = 0; } break; } } } sck_line = state; } //------------------------------------------------- // md_rom_device - constructor //------------------------------------------------- DEFINE_DEVICE_TYPE(MD_EEPROM_STM95, md_eeprom_stm95_device, "md_eeprom_stm95", "MD Cart + EEPROM STM95") md_eeprom_stm95_device::md_eeprom_stm95_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : device_t(mconfig, type, tag, owner, clock) , device_md_cart_interface(mconfig, *this) , m_rdcnt(0) { } md_eeprom_stm95_device::md_eeprom_stm95_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : md_eeprom_stm95_device(mconfig, MD_EEPROM_STM95, tag, owner, clock) { } void md_eeprom_stm95_device::device_start() { nvram_alloc(stm95_eeprom_device::M95320_SIZE); m_stm95 = std::make_unique(machine(), (uint8_t*)get_nvram_base()); save_item(NAME(m_rdcnt)); save_item(NAME(m_bank)); } void md_eeprom_stm95_device::device_reset() { m_rdcnt = 0; m_bank[0] = 0; m_bank[1] = 0; m_bank[2] = 0; } /*------------------------------------------------- mapper specific handlers -------------------------------------------------*/ uint16_t md_eeprom_stm95_device::read(offs_t offset) { if (offset == 0x0015e6/2 || offset == 0x0015e8/2) { // ugly hack until we don't know much about game protection // first 3 reads from 15e6 return 0x00000010, then normal 0x00018010 value for crc check uint16_t res; offset -= 0x0015e6/2; logerror("read 0x15e6 %d\n", m_rdcnt); if (m_rdcnt < 6) { m_rdcnt++; res = offset ? 0x10 : 0; } else res = offset ? 0x8010 : 0x0001; return res; } if (offset < 0x280000/2) return m_rom[offset]; else // last 0x180000 are bankswitched { uint8_t bank = (offset - 0x280000/2) >> 18; return m_rom[(offset & 0x7ffff/2) + (m_bank[bank] * 0x80000)/2]; } } uint16_t md_eeprom_stm95_device::read_a13(offs_t offset) { if (offset == 0x0a/2) { return m_stm95->get_so_line() & 1; } return 0xffff; } void md_eeprom_stm95_device::write_a13(offs_t offset, uint16_t data) { if (offset == 0x00/2) { logerror("A13001 write %02x\n", data); } else if (offset < 0x08/2) { m_bank[offset - 1] = data & 0x0f; } else if (offset < 0x0a/2) { m_stm95->set_si_line(BIT(data, 0)); m_stm95->set_sck_line(BIT(data, 1)); m_stm95->set_halt_line(BIT(data, 2)); m_stm95->set_cs_line(BIT(data, 3)); } }