// license:BSD-3-Clause // copyright-holders:Fabio Priuli, MetalliC /*************************************************************************** MegaDrive / Genesis Cart + STM95 EEPROM device TO DO: split STM95 to a separate device... ***************************************************************************/ #include "burnint.h" #include "bitswap.h" #include "m68000_intf.h" #include "driver.h" #define M95320_SIZE 0x1000 enum STMSTATE { IDLE = 0, CMD_WRSR, CMD_RDSR, M95320_CMD_READ, CMD_WRITE, READING, WRITING }; static UINT8 eeprom_data[M95320_SIZE]; static INT32 latch; static INT32 reset_line; static INT32 sck_line; static INT32 WEL; static INT32 stm_state; static INT32 stream_pos; static INT32 stream_data; static INT32 eeprom_addr; static UINT8 m_bank[3]; static INT32 m_rdcnt; static UINT16 *m_rom; static void set_cs_line(INT32 state) { reset_line = state; if (reset_line != CLEAR_LINE) { stream_pos = 0; stm_state = IDLE; } } static void set_si_line(INT32 state) { latch = state; } static INT32 get_so_line(void) { if (stm_state == READING || stm_state == CMD_RDSR) return (stream_data >> 8) & 1; else return 0; } static void set_sck_line(INT32 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; } } 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; } void md_eeprom_stm95_reset() { stream_pos = 0; stm_state = IDLE; m_rdcnt = 0; m_bank[0] = 0; m_bank[1] = 0; m_bank[2] = 0; } /*------------------------------------------------- mapper specific handlers -------------------------------------------------*/ static UINT16 __fastcall read_word(UINT32 offset) { offset /= 2; 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 res; offset -= 0x0015e6/2; 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 bank = (offset - 0x280000/2) >> 18; return m_rom[(offset & 0x7ffff/2) + (m_bank[bank] * 0x80000)/2]; } } UINT16 md_psolar_rw(UINT32 offset) // dmatransaktionizationimmizer { return read_word(offset); } static UINT8 __fastcall read_byte(UINT32 offset) { if (offset & 1) return read_word(offset); return read_word(offset) >> 8; } static UINT16 __fastcall read_a13_word(UINT32 offset) { offset = (offset/2) & 0x7f; if (offset == 0x0a/2) { return get_so_line() & 1; } return 0xffff; } static UINT8 __fastcall read_a13_byte(UINT32 offset) { return read_a13_word(offset); } static void __fastcall write_a13_word(UINT32 offset, UINT16 data) { offset = (offset/2) & 0x7f; if (offset < 0x08/2) { m_bank[offset - 1] = data & 0x0f; } else if (offset < 0x0a/2) { set_si_line(BIT(data, 0)); set_sck_line(BIT(data, 1)); // set_halt_line(BIT(data, 2)); set_cs_line(BIT(data, 3)); } } static void __fastcall write_a13_byte(UINT32 offset, UINT8 data) { write_a13_word(offset,data); } void md_eeprom_stm95_init(UINT8 *rom) { m_rom = (UINT16*)rom; SekOpen(0); // unmap everything except vectors for (INT32 i = 0x000; i < 0xa00000; i+= 0x400) { SekMapMemory(NULL, i, i+0x3ff, MAP_RAM); } SekMapHandler(5, 0x000000, 0x9fffff, MAP_ROM); SekSetReadByteHandler (5, read_byte); SekSetReadWordHandler (5, read_word); SekMapHandler(6, 0xa13000, 0xa130ff, MAP_RAM); SekSetReadByteHandler (6, read_a13_byte); SekSetReadWordHandler (6, read_a13_word); SekSetWriteByteHandler(6, write_a13_byte); SekSetWriteWordHandler(6, write_a13_word); SekClose(); } void md_eeprom_stm95_scan(INT32 nAction) { struct BurnArea ba; if (nAction & ACB_NVRAM) { ba.Data = eeprom_data; ba.nLen = M95320_SIZE; ba.nAddress = 0xa13000; ba.szName = "NV RAM"; BurnAcb(&ba); } if (nAction & ACB_DRIVER_DATA) { SCAN_VAR(latch); SCAN_VAR(reset_line); SCAN_VAR(sck_line); SCAN_VAR(WEL); SCAN_VAR(stm_state); SCAN_VAR(stream_pos); SCAN_VAR(stream_data); SCAN_VAR(eeprom_addr); SCAN_VAR(m_bank[0]); SCAN_VAR(m_bank[1]); SCAN_VAR(m_bank[2]); SCAN_VAR(m_rdcnt); } }