Mercurial > repos > blastem
comparison romdb.c @ 1414:d94855080529
Move I2C EEPROM and NOR Flash functions out of romdb.c into new files
author | Michael Pavone <pavone@retrodev.com> |
---|---|
date | Fri, 23 Jun 2017 21:48:38 -0700 |
parents | 3d7f668dce3d |
children | f7d653bb8899 |
comparison
equal
deleted
inserted
replaced
1413:3d7f668dce3d | 1414:d94855080529 |
---|---|
6 #include "hash.h" | 6 #include "hash.h" |
7 #include "genesis.h" | 7 #include "genesis.h" |
8 #include "menu.h" | 8 #include "menu.h" |
9 #include "xband.h" | 9 #include "xband.h" |
10 #include "realtec.h" | 10 #include "realtec.h" |
11 #include "nor.h" | |
11 | 12 |
12 #define DOM_TITLE_START 0x120 | 13 #define DOM_TITLE_START 0x120 |
13 #define DOM_TITLE_END 0x150 | 14 #define DOM_TITLE_END 0x150 |
14 #define TITLE_START DOM_TITLE_END | 15 #define TITLE_START DOM_TITLE_END |
15 #define TITLE_END (TITLE_START+48) | 16 #define TITLE_END (TITLE_START+48) |
26 return "EEPROM"; | 27 return "EEPROM"; |
27 } else if(save_type == SAVE_NOR) { | 28 } else if(save_type == SAVE_NOR) { |
28 return "NOR Flash"; | 29 return "NOR Flash"; |
29 } | 30 } |
30 return "SRAM"; | 31 return "SRAM"; |
31 } | |
32 | |
33 enum { | |
34 I2C_IDLE, | |
35 I2C_START, | |
36 I2C_DEVICE_ACK, | |
37 I2C_ADDRESS_HI, | |
38 I2C_ADDRESS_HI_ACK, | |
39 I2C_ADDRESS, | |
40 I2C_ADDRESS_ACK, | |
41 I2C_READ, | |
42 I2C_READ_ACK, | |
43 I2C_WRITE, | |
44 I2C_WRITE_ACK | |
45 }; | |
46 | |
47 char * i2c_states[] = { | |
48 "idle", | |
49 "start", | |
50 "device ack", | |
51 "address hi", | |
52 "address hi ack", | |
53 "address", | |
54 "address ack", | |
55 "read", | |
56 "read_ack", | |
57 "write", | |
58 "write_ack" | |
59 }; | |
60 | |
61 void eeprom_init(eeprom_state *state, uint8_t *buffer, uint32_t size) | |
62 { | |
63 state->slave_sda = 1; | |
64 state->host_sda = state->scl = 0; | |
65 state->buffer = buffer; | |
66 state->size = size; | |
67 state->state = I2C_IDLE; | |
68 } | |
69 | |
70 void set_host_sda(eeprom_state *state, uint8_t val) | |
71 { | |
72 if (state->scl) { | |
73 if (val & ~state->host_sda) { | |
74 //low to high, stop condition | |
75 state->state = I2C_IDLE; | |
76 state->slave_sda = 1; | |
77 } else if (~val & state->host_sda) { | |
78 //high to low, start condition | |
79 state->state = I2C_START; | |
80 state->slave_sda = 1; | |
81 state->counter = 8; | |
82 } | |
83 } | |
84 state->host_sda = val; | |
85 } | |
86 | |
87 void set_scl(eeprom_state *state, uint8_t val) | |
88 { | |
89 if (val & ~state->scl) { | |
90 //low to high transition | |
91 switch (state->state) | |
92 { | |
93 case I2C_START: | |
94 case I2C_ADDRESS_HI: | |
95 case I2C_ADDRESS: | |
96 case I2C_WRITE: | |
97 state->latch = state->host_sda | state->latch << 1; | |
98 state->counter--; | |
99 if (!state->counter) { | |
100 switch (state->state & 0x7F) | |
101 { | |
102 case I2C_START: | |
103 state->state = I2C_DEVICE_ACK; | |
104 break; | |
105 case I2C_ADDRESS_HI: | |
106 state->address = state->latch << 8; | |
107 state->state = I2C_ADDRESS_HI_ACK; | |
108 break; | |
109 case I2C_ADDRESS: | |
110 state->address |= state->latch; | |
111 state->state = I2C_ADDRESS_ACK; | |
112 break; | |
113 case I2C_WRITE: | |
114 state->buffer[state->address] = state->latch; | |
115 state->state = I2C_WRITE_ACK; | |
116 break; | |
117 } | |
118 } | |
119 break; | |
120 case I2C_DEVICE_ACK: | |
121 if (state->latch & 1) { | |
122 state->state = I2C_READ; | |
123 state->counter = 8; | |
124 if (state->size < 256) { | |
125 state->address = state->latch >> 1; | |
126 } | |
127 state->latch = state->buffer[state->address]; | |
128 } else { | |
129 if (state->size < 256) { | |
130 state->address = state->latch >> 1; | |
131 state->state = I2C_WRITE; | |
132 } else if (state->size < 4096) { | |
133 state->address = (state->latch & 0xE) << 7; | |
134 state->state = I2C_ADDRESS; | |
135 } else { | |
136 state->state = I2C_ADDRESS_HI; | |
137 } | |
138 state->counter = 8; | |
139 } | |
140 break; | |
141 case I2C_ADDRESS_HI_ACK: | |
142 state->state = I2C_ADDRESS; | |
143 state->counter = 8; | |
144 break; | |
145 case I2C_ADDRESS_ACK: | |
146 state->state = I2C_WRITE; | |
147 state->address &= state->size-1; | |
148 state->counter = 8; | |
149 break; | |
150 case I2C_READ: | |
151 state->counter--; | |
152 if (!state->counter) { | |
153 state->state = I2C_READ_ACK; | |
154 } | |
155 break; | |
156 case I2C_READ_ACK: | |
157 state->state = I2C_READ; | |
158 state->counter = 8; | |
159 state->address++; | |
160 //TODO: page mask | |
161 state->address &= state->size-1; | |
162 state->latch = state->buffer[state->address]; | |
163 break; | |
164 case I2C_WRITE_ACK: | |
165 state->state = I2C_WRITE; | |
166 state->counter = 8; | |
167 state->address++; | |
168 //TODO: page mask | |
169 state->address &= state->size-1; | |
170 break; | |
171 } | |
172 } else if (~val & state->scl) { | |
173 //high to low transition | |
174 switch (state->state & 0x7F) | |
175 { | |
176 case I2C_DEVICE_ACK: | |
177 case I2C_ADDRESS_HI_ACK: | |
178 case I2C_ADDRESS_ACK: | |
179 case I2C_READ_ACK: | |
180 case I2C_WRITE_ACK: | |
181 state->slave_sda = 0; | |
182 break; | |
183 case I2C_READ: | |
184 state->slave_sda = state->latch >> 7; | |
185 state->latch = state->latch << 1; | |
186 break; | |
187 default: | |
188 state->slave_sda = 1; | |
189 break; | |
190 } | |
191 } | |
192 state->scl = val; | |
193 } | |
194 | |
195 uint8_t get_sda(eeprom_state *state) | |
196 { | |
197 return state->host_sda & state->slave_sda; | |
198 } | |
199 | |
200 enum { | |
201 NOR_NORMAL, | |
202 NOR_PRODUCTID, | |
203 NOR_BOOTBLOCK | |
204 }; | |
205 | |
206 enum { | |
207 NOR_CMD_IDLE, | |
208 NOR_CMD_AA, | |
209 NOR_CMD_55 | |
210 }; | |
211 | |
212 //Technically this value shoudl be slightly different between NTSC and PAL | |
213 //as it's defined as 200 micro-seconds, not in clock cycles | |
214 #define NOR_WRITE_PAUSE 10690 | |
215 | |
216 void nor_flash_init(nor_state *state, uint8_t *buffer, uint32_t size, uint32_t page_size, uint16_t product_id, uint8_t bus_flags) | |
217 { | |
218 state->buffer = buffer; | |
219 state->page_buffer = malloc(page_size); | |
220 memset(state->page_buffer, 0xFF, page_size); | |
221 state->size = size; | |
222 state->page_size = page_size; | |
223 state->product_id = product_id; | |
224 state->last_write_cycle = 0xFFFFFFFF; | |
225 state->mode = NOR_NORMAL; | |
226 state->cmd_state = NOR_CMD_IDLE; | |
227 state->alt_cmd = 0; | |
228 state->bus_flags = bus_flags; | |
229 } | |
230 | |
231 void nor_run(nor_state *state, uint32_t cycle) | |
232 { | |
233 if (state->last_write_cycle == 0xFFFFFFFF) { | |
234 return; | |
235 } | |
236 if (cycle - state->last_write_cycle >= NOR_WRITE_PAUSE) { | |
237 state->last_write_cycle = 0xFFFFFFFF; | |
238 for (uint32_t i = 0; i < state->page_size; i++) { | |
239 state->buffer[state->current_page + i] = state->page_buffer[i]; | |
240 } | |
241 memset(state->page_buffer, 0xFF, state->page_size); | |
242 } | |
243 } | |
244 | |
245 uint8_t nor_flash_read_b(uint32_t address, void *vcontext) | |
246 { | |
247 m68k_context *m68k = vcontext; | |
248 genesis_context *gen = m68k->system; | |
249 nor_state *state = &gen->nor; | |
250 if ( | |
251 ((address & 1) && state->bus_flags == RAM_FLAG_EVEN) || | |
252 (!(address & 1) && state->bus_flags == RAM_FLAG_ODD) | |
253 ) { | |
254 return 0xFF; | |
255 } | |
256 if (state->bus_flags != RAM_FLAG_BOTH) { | |
257 address = address >> 1; | |
258 } | |
259 | |
260 nor_run(state, m68k->current_cycle); | |
261 switch (state->mode) | |
262 { | |
263 case NOR_NORMAL: | |
264 return state->buffer[address & (state->size-1)]; | |
265 break; | |
266 case NOR_PRODUCTID: | |
267 switch (address & (state->size - 1)) | |
268 { | |
269 case 0: | |
270 return state->product_id >> 8; | |
271 case 1: | |
272 return state->product_id; | |
273 case 2: | |
274 //TODO: Implement boot block protection | |
275 return 0xFE; | |
276 default: | |
277 return 0xFE; | |
278 } | |
279 break; | |
280 case NOR_BOOTBLOCK: | |
281 break; | |
282 } | |
283 return 0xFF; | |
284 } | |
285 | |
286 uint16_t nor_flash_read_w(uint32_t address, void *context) | |
287 { | |
288 uint16_t value = nor_flash_read_b(address, context) << 8; | |
289 value |= nor_flash_read_b(address+1, context); | |
290 return value; | |
291 } | |
292 | |
293 void nor_write_byte(nor_state *state, uint32_t address, uint8_t value, uint32_t cycle) | |
294 { | |
295 switch(state->mode) | |
296 { | |
297 case NOR_NORMAL: | |
298 if (state->last_write_cycle != 0xFFFFFFFF) { | |
299 state->current_page = address & (state->size - 1) & ~(state->page_size - 1); | |
300 } | |
301 state->page_buffer[address & (state->page_size - 1)] = value; | |
302 break; | |
303 case NOR_PRODUCTID: | |
304 break; | |
305 case NOR_BOOTBLOCK: | |
306 //TODO: Implement boot block protection | |
307 state->mode = NOR_NORMAL; | |
308 break; | |
309 } | |
310 } | |
311 | |
312 void *nor_flash_write_b(uint32_t address, void *vcontext, uint8_t value) | |
313 { | |
314 m68k_context *m68k = vcontext; | |
315 genesis_context *gen = m68k->system; | |
316 nor_state *state = &gen->nor; | |
317 if ( | |
318 ((address & 1) && state->bus_flags == RAM_FLAG_EVEN) || | |
319 (!(address & 1) && state->bus_flags == RAM_FLAG_ODD) | |
320 ) { | |
321 return vcontext; | |
322 } | |
323 if (state->bus_flags != RAM_FLAG_BOTH) { | |
324 address = address >> 1; | |
325 } | |
326 | |
327 nor_run(state, m68k->current_cycle); | |
328 switch (state->cmd_state) | |
329 { | |
330 case NOR_CMD_IDLE: | |
331 if (value == 0xAA && (address & (state->size - 1)) == 0x5555) { | |
332 state->cmd_state = NOR_CMD_AA; | |
333 } else { | |
334 nor_write_byte(state, address, value, m68k->current_cycle); | |
335 state->cmd_state = NOR_CMD_IDLE; | |
336 } | |
337 break; | |
338 case NOR_CMD_AA: | |
339 if (value == 0x55 && (address & (state->size - 1)) == 0x2AAA) { | |
340 state->cmd_state = NOR_CMD_55; | |
341 } else { | |
342 nor_write_byte(state, 0x5555, 0xAA, m68k->current_cycle); | |
343 nor_write_byte(state, address, value, m68k->current_cycle); | |
344 state->cmd_state = NOR_CMD_IDLE; | |
345 } | |
346 break; | |
347 case NOR_CMD_55: | |
348 if ((address & (state->size - 1)) == 0x5555) { | |
349 if (state->alt_cmd) { | |
350 switch(value) | |
351 { | |
352 case 0x10: | |
353 puts("UNIMPLEMENTED: NOR flash erase"); | |
354 break; | |
355 case 0x20: | |
356 puts("UNIMPLEMENTED: NOR flash disable protection"); | |
357 break; | |
358 case 0x40: | |
359 state->mode = NOR_BOOTBLOCK; | |
360 break; | |
361 case 0x60: | |
362 state->mode = NOR_PRODUCTID; | |
363 break; | |
364 } | |
365 } else { | |
366 switch(value) | |
367 { | |
368 case 0x80: | |
369 state->alt_cmd = 1; | |
370 break; | |
371 case 0x90: | |
372 state->mode = NOR_PRODUCTID; | |
373 break; | |
374 case 0xA0: | |
375 puts("UNIMPLEMENTED: NOR flash enable protection"); | |
376 break; | |
377 case 0xF0: | |
378 state->mode = NOR_NORMAL; | |
379 break; | |
380 default: | |
381 printf("Unrecognized unshifted NOR flash command %X\n", value); | |
382 } | |
383 } | |
384 } else { | |
385 nor_write_byte(state, 0x5555, 0xAA, m68k->current_cycle); | |
386 nor_write_byte(state, 0x2AAA, 0x55, m68k->current_cycle); | |
387 nor_write_byte(state, address, value, m68k->current_cycle); | |
388 } | |
389 state->cmd_state = NOR_CMD_IDLE; | |
390 break; | |
391 } | |
392 return vcontext; | |
393 } | |
394 | |
395 void *nor_flash_write_w(uint32_t address, void *vcontext, uint16_t value) | |
396 { | |
397 nor_flash_write_b(address, vcontext, value >> 8); | |
398 return nor_flash_write_b(address + 1, vcontext, value); | |
399 } | 32 } |
400 | 33 |
401 uint16_t read_sram_w(uint32_t address, m68k_context * context) | 34 uint16_t read_sram_w(uint32_t address, m68k_context * context) |
402 { | 35 { |
403 genesis_context * gen = context->system; | 36 genesis_context * gen = context->system; |
528 { | 161 { |
529 if (address & 1) { | 162 if (address & 1) { |
530 write_bank_reg_w(address, context, value); | 163 write_bank_reg_w(address, context, value); |
531 } | 164 } |
532 return context; | 165 return context; |
533 } | |
534 eeprom_map *find_eeprom_map(uint32_t address, genesis_context *gen) | |
535 { | |
536 for (int i = 0; i < gen->num_eeprom; i++) | |
537 { | |
538 if (address >= gen->eeprom_map[i].start && address <= gen->eeprom_map[i].end) { | |
539 return gen->eeprom_map + i; | |
540 } | |
541 } | |
542 return NULL; | |
543 } | |
544 | |
545 void * write_eeprom_i2c_w(uint32_t address, void * context, uint16_t value) | |
546 { | |
547 genesis_context *gen = ((m68k_context *)context)->system; | |
548 eeprom_map *map = find_eeprom_map(address, gen); | |
549 if (!map) { | |
550 fatal_error("Could not find EEPROM map for address %X\n", address); | |
551 } | |
552 if (map->scl_mask) { | |
553 set_scl(&gen->eeprom, (value & map->scl_mask) != 0); | |
554 } | |
555 if (map->sda_write_mask) { | |
556 set_host_sda(&gen->eeprom, (value & map->sda_write_mask) != 0); | |
557 } | |
558 return context; | |
559 } | |
560 | |
561 void * write_eeprom_i2c_b(uint32_t address, void * context, uint8_t value) | |
562 { | |
563 genesis_context *gen = ((m68k_context *)context)->system; | |
564 eeprom_map *map = find_eeprom_map(address, gen); | |
565 if (!map) { | |
566 fatal_error("Could not find EEPROM map for address %X\n", address); | |
567 } | |
568 | |
569 uint16_t expanded, mask; | |
570 if (address & 1) { | |
571 expanded = value; | |
572 mask = 0xFF; | |
573 } else { | |
574 expanded = value << 8; | |
575 mask = 0xFF00; | |
576 } | |
577 if (map->scl_mask & mask) { | |
578 set_scl(&gen->eeprom, (expanded & map->scl_mask) != 0); | |
579 } | |
580 if (map->sda_write_mask & mask) { | |
581 set_host_sda(&gen->eeprom, (expanded & map->sda_write_mask) != 0); | |
582 } | |
583 return context; | |
584 } | |
585 | |
586 uint16_t read_eeprom_i2c_w(uint32_t address, void * context) | |
587 { | |
588 genesis_context *gen = ((m68k_context *)context)->system; | |
589 eeprom_map *map = find_eeprom_map(address, gen); | |
590 if (!map) { | |
591 fatal_error("Could not find EEPROM map for address %X\n", address); | |
592 } | |
593 uint16_t ret = 0; | |
594 if (map->sda_read_bit < 16) { | |
595 ret = get_sda(&gen->eeprom) << map->sda_read_bit; | |
596 } | |
597 return ret; | |
598 } | |
599 | |
600 uint8_t read_eeprom_i2c_b(uint32_t address, void * context) | |
601 { | |
602 genesis_context *gen = ((m68k_context *)context)->system; | |
603 eeprom_map *map = find_eeprom_map(address, gen); | |
604 if (!map) { | |
605 fatal_error("Could not find EEPROM map for address %X\n", address); | |
606 } | |
607 uint8_t bit = address & 1 ? map->sda_read_bit : map->sda_read_bit - 8; | |
608 uint8_t ret = 0; | |
609 if (bit < 8) { | |
610 ret = get_sda(&gen->eeprom) << bit; | |
611 } | |
612 return ret; | |
613 } | 166 } |
614 | 167 |
615 tern_node *load_rom_db() | 168 tern_node *load_rom_db() |
616 { | 169 { |
617 tern_node *db = parse_bundled_config("rom.db"); | 170 tern_node *db = parse_bundled_config("rom.db"); |