1 // license:BSD-3-Clause
2 // copyright-holders:Fabio Priuli, MetalliC
3 /***************************************************************************
4 
5 
6  MegaDrive / Genesis Cart + STM95 EEPROM device
7 
8 
9  TO DO: split STM95 to a separate device...
10 
11 ***************************************************************************/
12 
13 
14 #include "burnint.h"
15 #include "bitswap.h"
16 #include "m68000_intf.h"
17 #include "driver.h"
18 
19 #define M95320_SIZE 0x1000
20 
21 enum STMSTATE
22 {
23 	IDLE = 0,
24 	CMD_WRSR,
25 	CMD_RDSR,
26 	M95320_CMD_READ,
27 	CMD_WRITE,
28 	READING,
29 	WRITING
30 };
31 
32 static UINT8 eeprom_data[M95320_SIZE];
33 static INT32 latch;
34 static INT32 reset_line;
35 static INT32 sck_line;
36 static INT32 WEL;
37 static INT32 stm_state;
38 static INT32 stream_pos;
39 static INT32 stream_data;
40 static INT32 eeprom_addr;
41 
42 static UINT8 m_bank[3];
43 static INT32 m_rdcnt;
44 
45 static UINT16 *m_rom;
46 
set_cs_line(INT32 state)47 static void set_cs_line(INT32 state)
48 {
49 	reset_line = state;
50 	if (reset_line != CLEAR_LINE)
51 	{
52 		stream_pos = 0;
53 		stm_state = IDLE;
54 	}
55 }
56 
set_si_line(INT32 state)57 static void set_si_line(INT32 state)
58 {
59 	latch = state;
60 }
61 
get_so_line(void)62 static INT32 get_so_line(void)
63 {
64 	if (stm_state == READING || stm_state == CMD_RDSR)
65 		return (stream_data >> 8) & 1;
66 	else
67 		return 0;
68 }
69 
set_sck_line(INT32 state)70 static void set_sck_line(INT32 state)
71 {
72 	if (reset_line == CLEAR_LINE)
73 	{
74 		if (state == ASSERT_LINE && sck_line == CLEAR_LINE)
75 		{
76 			switch (stm_state)
77 			{
78 				case IDLE:
79 					stream_data = (stream_data << 1) | (latch ? 1 : 0);
80 					stream_pos++;
81 					if (stream_pos == 8)
82 					{
83 						stream_pos = 0;
84 						//printf("STM95 EEPROM: got cmd %02X\n", stream_data&0xff);
85 						switch(stream_data & 0xff)
86 						{
87 							case 0x01:  // write status register
88 								if (WEL != 0)
89 									stm_state = CMD_WRSR;
90 								WEL = 0;
91 								break;
92 							case 0x02:  // write
93 								if (WEL != 0)
94 									stm_state = CMD_WRITE;
95 								stream_data = 0;
96 								WEL = 0;
97 								break;
98 							case 0x03:  // read
99 								stm_state = M95320_CMD_READ;
100 								stream_data = 0;
101 								break;
102 							case 0x04:  // write disable
103 								WEL = 0;
104 								break;
105 							case 0x05:  // read status register
106 								stm_state = CMD_RDSR;
107 								stream_data = WEL<<1;
108 								break;
109 							case 0x06:  // write enable
110 								WEL = 1;
111 								break;
112 						}
113 					}
114 					break;
115 				case CMD_WRSR:
116 					stream_pos++;       // just skip, don't care block protection
117 					if (stream_pos == 8)
118 					{
119 						stm_state = IDLE;
120 						stream_pos = 0;
121 					}
122 					break;
123 				case CMD_RDSR:
124 					stream_data = stream_data<<1;
125 					stream_pos++;
126 					if (stream_pos == 8)
127 					{
128 						stm_state = IDLE;
129 						stream_pos = 0;
130 					}
131 					break;
132 				case M95320_CMD_READ:
133 					stream_data = (stream_data << 1) | (latch ? 1 : 0);
134 					stream_pos++;
135 					if (stream_pos == 16)
136 					{
137 						eeprom_addr = stream_data & (M95320_SIZE - 1);
138 						stream_data = eeprom_data[eeprom_addr];
139 						stm_state = READING;
140 						stream_pos = 0;
141 					}
142 					break;
143 				case READING:
144 					stream_data = stream_data<<1;
145 					stream_pos++;
146 					if (stream_pos == 8)
147 					{
148 						if (++eeprom_addr == M95320_SIZE)
149 							eeprom_addr = 0;
150 						stream_data |= eeprom_data[eeprom_addr];
151 						stream_pos = 0;
152 					}
153 					break;
154 				case CMD_WRITE:
155 					stream_data = (stream_data << 1) | (latch ? 1 : 0);
156 					stream_pos++;
157 					if (stream_pos == 16)
158 					{
159 						eeprom_addr = stream_data & (M95320_SIZE - 1);
160 						stm_state = WRITING;
161 						stream_pos = 0;
162 					}
163 					break;
164 				case WRITING:
165 					stream_data = (stream_data << 1) | (latch ? 1 : 0);
166 					stream_pos++;
167 					if (stream_pos == 8)
168 					{
169 						eeprom_data[eeprom_addr] = stream_data;
170 						if (++eeprom_addr == M95320_SIZE)
171 							eeprom_addr = 0;
172 						stream_pos = 0;
173 					}
174 					break;
175 			}
176 		}
177 	}
178 	sck_line = state;
179 }
180 
md_eeprom_stm95_reset()181 void md_eeprom_stm95_reset()
182 {
183 	stream_pos = 0;
184 	stm_state = IDLE;
185 
186 	m_rdcnt = 0;
187 	m_bank[0] = 0;
188 	m_bank[1] = 0;
189 	m_bank[2] = 0;
190 }
191 
192 /*-------------------------------------------------
193  mapper specific handlers
194  -------------------------------------------------*/
195 
read_word(UINT32 offset)196 static UINT16 __fastcall read_word(UINT32 offset)
197 {
198 	offset /= 2;
199 
200 	if (offset == 0x0015e6/2 || offset == 0x0015e8/2)
201 	{
202 		// ugly hack until we don't know much about game protection
203 		// first 3 reads from 15e6 return 0x00000010, then normal 0x00018010 value for crc check
204 		UINT16 res;
205 		offset -= 0x0015e6/2;
206 
207 		if (m_rdcnt < 6)
208 		{
209 			m_rdcnt++;
210 			res = offset ? 0x10 : 0;
211 		}
212 		else
213 			res = offset ? 0x8010 : 0x0001;
214 		return res;
215 	}
216 	if (offset < 0x280000/2)
217 		return m_rom[offset];
218 	else    // last 0x180000 are bankswitched
219 	{
220 		UINT8 bank = (offset - 0x280000/2) >> 18;
221 		return m_rom[(offset & 0x7ffff/2) + (m_bank[bank] * 0x80000)/2];
222 	}
223 }
224 
md_psolar_rw(UINT32 offset)225 UINT16 md_psolar_rw(UINT32 offset) // dmatransaktionizationimmizer
226 {
227 	return read_word(offset);
228 }
229 
read_byte(UINT32 offset)230 static UINT8 __fastcall read_byte(UINT32 offset)
231 {
232 	if (offset & 1)
233 		return read_word(offset);
234 
235 	return read_word(offset) >> 8;
236 }
237 
read_a13_word(UINT32 offset)238 static UINT16 __fastcall read_a13_word(UINT32 offset)
239 {
240 	offset = (offset/2) & 0x7f;
241 
242 	if (offset == 0x0a/2)
243 	{
244 		return get_so_line() & 1;
245 	}
246 	return 0xffff;
247 }
248 
read_a13_byte(UINT32 offset)249 static UINT8 __fastcall read_a13_byte(UINT32 offset)
250 {
251 	return read_a13_word(offset);
252 }
253 
write_a13_word(UINT32 offset,UINT16 data)254 static void __fastcall write_a13_word(UINT32 offset, UINT16 data)
255 {
256 	offset = (offset/2) & 0x7f;
257 
258 	if (offset < 0x08/2)
259 	{
260 		m_bank[offset - 1] = data & 0x0f;
261 	}
262 	else if (offset < 0x0a/2)
263 	{
264 		set_si_line(BIT(data, 0));
265 		set_sck_line(BIT(data, 1));
266 	//	set_halt_line(BIT(data, 2));
267 		set_cs_line(BIT(data, 3));
268 	}
269 }
270 
write_a13_byte(UINT32 offset,UINT8 data)271 static void __fastcall write_a13_byte(UINT32 offset, UINT8 data)
272 {
273 	write_a13_word(offset,data);
274 }
275 
276 
md_eeprom_stm95_init(UINT8 * rom)277 void md_eeprom_stm95_init(UINT8 *rom)
278 {
279 	m_rom = (UINT16*)rom;
280 
281 	SekOpen(0);
282 
283 	// unmap everything except vectors
284 	for (INT32 i = 0x000; i < 0xa00000; i+= 0x400) {
285 		SekMapMemory(NULL,	i, i+0x3ff, MAP_RAM);
286 	}
287 
288 	SekMapHandler(5,		0x000000, 0x9fffff, MAP_ROM);
289 	SekSetReadByteHandler (5, 	read_byte);
290 	SekSetReadWordHandler (5, 	read_word);
291 
292 	SekMapHandler(6,		0xa13000, 0xa130ff, MAP_RAM);
293 	SekSetReadByteHandler (6, 	read_a13_byte);
294 	SekSetReadWordHandler (6, 	read_a13_word);
295 	SekSetWriteByteHandler(6, 	write_a13_byte);
296 	SekSetWriteWordHandler(6, 	write_a13_word);
297 
298 	SekClose();
299 }
300 
md_eeprom_stm95_scan(INT32 nAction)301 void md_eeprom_stm95_scan(INT32 nAction)
302 {
303 	struct BurnArea ba;
304 
305 	if (nAction & ACB_NVRAM) {
306 		ba.Data		= eeprom_data;
307 		ba.nLen		= M95320_SIZE;
308 		ba.nAddress	= 0xa13000;
309 		ba.szName	= "NV RAM";
310 		BurnAcb(&ba);
311 	}
312 
313 	if (nAction & ACB_DRIVER_DATA) {
314 
315 		SCAN_VAR(latch);
316 		SCAN_VAR(reset_line);
317 		SCAN_VAR(sck_line);
318 		SCAN_VAR(WEL);
319 		SCAN_VAR(stm_state);
320 		SCAN_VAR(stream_pos);
321 		SCAN_VAR(stream_data);
322 		SCAN_VAR(eeprom_addr);
323 
324 		SCAN_VAR(m_bank[0]);
325 		SCAN_VAR(m_bank[1]);
326 		SCAN_VAR(m_bank[2]);
327 		SCAN_VAR(m_rdcnt);
328 	}
329 }
330