2bab1549364f45ed0bf5fb961900fd6c4666d310
[picodrive.git] / pico / carthw / carthw.c
1 /*
2  * Support for a few cart mappers and some protection.
3  * (C) notaz, 2008-2011
4  * (C) irixxxx, 2021-2024
5  *
6  * This work is licensed under the terms of MAME license.
7  * See COPYING file in the top-level directory.
8  */
9
10 #include "../pico_int.h"
11 #include "../memory.h"
12 #include "eeprom_spi.h"
13
14
15 static int have_bank(u32 base)
16 {
17   // the loader allocs in 512K quantities
18   if (base >= Pico.romsize) {
19     elprintf(EL_ANOMALY|EL_STATUS, "carthw: missing bank @ %06x", base);
20     return 0;
21   }
22   return 1;
23 }
24
25 /* standard/ssf2 mapper */
26 int carthw_ssf2_active;
27 unsigned char carthw_ssf2_banks[8];
28
29 static carthw_state_chunk carthw_ssf2_state[] =
30 {
31   { CHUNK_CARTHW, sizeof(carthw_ssf2_banks), &carthw_ssf2_banks },
32   { 0,            0,                         NULL }
33 };
34
35 void carthw_ssf2_write8(u32 a, u32 d)
36 {
37   u32 target, base;
38
39   if ((a & ~0x0e) != 0xa130f1 || a == 0xa130f1) {
40     PicoWrite8_io(a, d);
41     return;
42   }
43
44   a &= 0x0e;
45   if (a == 0)
46     return;
47   if (carthw_ssf2_banks[a >> 1] == d)
48     return;
49
50   base = d << 19;
51   target = a << 18;
52   if (!have_bank(base))
53     return;
54   carthw_ssf2_banks[a >> 1] = d;
55
56   cpu68k_map_set(m68k_read8_map,  target, target + 0x80000 - 1, Pico.rom + base, 0);
57   cpu68k_map_set(m68k_read16_map, target, target + 0x80000 - 1, Pico.rom + base, 0);
58 }
59
60 void carthw_ssf2_write16(u32 a, u32 d)
61 {
62   PicoWrite16_io(a, d);
63   if ((a & ~0x0f) == 0xa130f0)
64     carthw_ssf2_write8(a + 1, d);
65 }
66
67 static void carthw_ssf2_mem_setup(void)
68 {
69   cpu68k_map_set(m68k_write8_map,  0xa10000, 0xa1ffff, carthw_ssf2_write8, 1);
70   cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, carthw_ssf2_write16, 1);
71 }
72
73 static void carthw_ssf2_statef(void)
74 {
75   int i, reg;
76   for (i = 1; i < 8; i++) {
77     reg = carthw_ssf2_banks[i];
78     carthw_ssf2_banks[i] = ~reg;
79     carthw_ssf2_write8(0xa130f1 | (i << 1), reg);
80   }
81 }
82
83 static void carthw_ssf2_unload(void)
84 {
85   memset(carthw_ssf2_banks, 0, sizeof(carthw_ssf2_banks));
86   carthw_ssf2_active = 0;
87 }
88
89 void carthw_ssf2_startup(void)
90 {
91   int i;
92
93   elprintf(EL_STATUS, "SSF2 mapper startup");
94
95   // default map
96   for (i = 0; i < 8; i++)
97     carthw_ssf2_banks[i] = i;
98
99   PicoCartMemSetup   = carthw_ssf2_mem_setup;
100   PicoLoadStateHook  = carthw_ssf2_statef;
101   PicoCartUnloadHook = carthw_ssf2_unload;
102   carthw_chunks      = carthw_ssf2_state;
103   carthw_ssf2_active = 1;
104 }
105
106
107 /* Common *-in-1 pirate mapper.
108  * Switches banks based on addr lines when /TIME is set.
109  * TODO: verify
110  */
111 static unsigned int carthw_Xin1_baddr = 0;
112
113 static void carthw_Xin1_do(u32 a, int mask, int shift)
114 {
115         int len;
116
117         carthw_Xin1_baddr = a;
118         a &= mask;
119         a <<= shift;
120         len = Pico.romsize - a;
121         if (len <= 0) {
122                 elprintf(EL_ANOMALY|EL_STATUS, "X-in-1: missing bank @ %06x", a);
123                 return;
124         }
125
126         len = (len + M68K_BANK_MASK) & ~M68K_BANK_MASK;
127         cpu68k_map_set(m68k_read8_map,  0x000000, len - 1, Pico.rom + a, 0);
128         cpu68k_map_set(m68k_read16_map, 0x000000, len - 1, Pico.rom + a, 0);
129 }
130
131 static carthw_state_chunk carthw_Xin1_state[] =
132 {
133         { CHUNK_CARTHW, sizeof(carthw_Xin1_baddr), &carthw_Xin1_baddr },
134         { 0,            0,                         NULL }
135 };
136
137 // TODO: reads should also work, but then we need to handle open bus
138 static void carthw_Xin1_write8(u32 a, u32 d)
139 {
140         if ((a & 0xffff00) != 0xa13000) {
141                 PicoWrite8_io(a, d);
142                 return;
143         }
144
145         carthw_Xin1_do(a, 0x3e, 16);
146 }
147
148 static void carthw_Xin1_write16(u32 a, u32 d)
149 {
150   if ((a & 0xffff00) != 0xa13000) {
151     PicoWrite16_io(a, d);
152     return;
153   }
154
155   carthw_Xin1_write8(a + 1, d);
156 }
157
158 static void carthw_Xin1_mem_setup(void)
159 {
160         cpu68k_map_set(m68k_write8_map,  0xa10000, 0xa1ffff, carthw_Xin1_write8, 1);
161         cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, carthw_Xin1_write16, 1);
162 }
163
164 static void carthw_Xin1_reset(void)
165 {
166         carthw_Xin1_write8(0xa13000, 0);
167 }
168
169 static void carthw_Xin1_statef(void)
170 {
171         carthw_Xin1_write8(carthw_Xin1_baddr, 0);
172 }
173
174 void carthw_Xin1_startup(void)
175 {
176         elprintf(EL_STATUS, "X-in-1 mapper startup");
177
178         PicoCartMemSetup  = carthw_Xin1_mem_setup;
179         PicoResetHook     = carthw_Xin1_reset;
180         PicoLoadStateHook = carthw_Xin1_statef;
181         carthw_chunks     = carthw_Xin1_state;
182 }
183
184
185 /* Realtec, based on TascoDLX doc
186  * http://www.sharemation.com/TascoDLX/REALTEC%20Cart%20Mapper%20-%20description%20v1.txt
187  */
188 static int realtec_bank = 0x80000000, realtec_size = 0x80000000;
189
190 static void carthw_realtec_write8(u32 a, u32 d)
191 {
192         int i, bank_old = realtec_bank, size_old = realtec_size;
193
194         if (a == 0x400000)
195         {
196                 realtec_bank &= 0x0e0000;
197                 realtec_bank |= 0x300000 & (d << 19);
198                 if (realtec_bank != bank_old)
199                         elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
200         }
201         else if (a == 0x402000)
202         {
203                 realtec_size = (d << 17) & 0x3e0000;
204                 if (realtec_size != size_old)
205                         elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
206         }
207         else if (a == 0x404000)
208         {
209                 realtec_bank &= 0x300000;
210                 realtec_bank |= 0x0e0000 & (d << 17);
211                 if (realtec_bank != bank_old)
212                         elprintf(EL_ANOMALY, "write [%06x] %02x @ %06x", a, d, SekPc);
213         }
214         else
215                 elprintf(EL_ANOMALY, "realtec: unexpected write [%06x] %02x @ %06x", a, d, SekPc);
216
217         if (realtec_bank >= 0 && realtec_size >= 0 &&
218                 (realtec_bank != bank_old || realtec_size != size_old))
219         {
220                 elprintf(EL_ANOMALY, "realtec: new bank %06x, size %06x", realtec_bank, realtec_size, SekPc);
221                 if (realtec_size > Pico.romsize - realtec_bank)
222                 {
223                         elprintf(EL_ANOMALY, "realtec: bank too large / out of range?");
224                         return;
225                 }
226
227                 for (i = 0; i < 0x400000; i += realtec_size) {
228                         cpu68k_map_set(m68k_read8_map,  i, realtec_size - 1, Pico.rom + realtec_bank, 0);
229                         cpu68k_map_set(m68k_read16_map, i, realtec_size - 1, Pico.rom + realtec_bank, 0);
230                 }
231         }
232 }
233
234 static void carthw_realtec_reset(void)
235 {
236         int i;
237
238         /* map boot code */
239         for (i = 0; i < 0x400000; i += M68K_BANK_SIZE) {
240                 cpu68k_map_set(m68k_read8_map,  i, i + M68K_BANK_SIZE - 1, Pico.rom + Pico.romsize, 0);
241                 cpu68k_map_set(m68k_read16_map, i, i + M68K_BANK_SIZE - 1, Pico.rom + Pico.romsize, 0);
242         }
243         cpu68k_map_set(m68k_write8_map, 0x400000, 0x400000 + M68K_BANK_SIZE - 1, carthw_realtec_write8, 1);
244         realtec_bank = realtec_size = 0x80000000;
245 }
246
247 void carthw_realtec_startup(void)
248 {
249         int i;
250
251         elprintf(EL_STATUS, "Realtec mapper startup");
252
253         // allocate additional bank for boot code
254         // (we know those ROMs have aligned size)
255         i = PicoCartResize(Pico.romsize + M68K_BANK_SIZE);
256         if (i != 0) {
257                 elprintf(EL_STATUS, "OOM");
258                 return;
259         }
260
261         // create bank for boot code
262         for (i = 0; i < M68K_BANK_SIZE; i += 0x2000)
263                 memcpy(Pico.rom + Pico.romsize + i, Pico.rom + Pico.romsize - 0x2000, 0x2000);
264
265         PicoResetHook = carthw_realtec_reset;
266 }
267
268 /* Radica mapper, based on DevSter's info
269  * http://devster.monkeeh.com/sega/radica/
270  * XXX: mostly the same as X-in-1, merge?
271  */
272 static u32 carthw_radica_read16(u32 a)
273 {
274         if ((a & 0xffff00) != 0xa13000)
275                 return PicoRead16_io(a);
276
277         carthw_Xin1_do(a, 0x7e, 15);
278
279         return 0;
280 }
281
282 static void carthw_radica_mem_setup(void)
283 {
284         cpu68k_map_set(m68k_read16_map, 0xa10000, 0xa1ffff, carthw_radica_read16, 1);
285 }
286
287 static void carthw_radica_statef(void)
288 {
289         carthw_radica_read16(carthw_Xin1_baddr);
290 }
291
292 static void carthw_radica_reset(void)
293 {
294         carthw_radica_read16(0xa13000);
295 }
296
297 void carthw_radica_startup(void)
298 {
299         elprintf(EL_STATUS, "Radica mapper startup");
300
301         PicoCartMemSetup  = carthw_radica_mem_setup;
302         PicoResetHook     = carthw_radica_reset;
303         PicoLoadStateHook = carthw_radica_statef;
304         carthw_chunks     = carthw_Xin1_state;
305 }
306
307
308 /* Pier Solar. Based on my own research */
309 static unsigned char pier_regs[8];
310 static unsigned char pier_dump_prot;
311
312 static carthw_state_chunk carthw_pier_state[] =
313 {
314   { CHUNK_CARTHW,     sizeof(pier_regs),      pier_regs },
315   { CHUNK_CARTHW + 1, sizeof(pier_dump_prot), &pier_dump_prot },
316   { CHUNK_CARTHW + 2, 0,                      NULL }, // filled later
317   { 0,                0,                      NULL }
318 };
319
320 static void carthw_pier_write8(u32 a, u32 d)
321 {
322   u32 a8, target, base;
323
324   if ((a & 0xffff00) != 0xa13000) {
325     PicoWrite8_io(a, d);
326     return;
327   }
328
329   a8 = a & 0x0f;
330   pier_regs[a8 / 2] = d;
331
332       elprintf(EL_UIO, "pier w8  [%06x] %02x @%06x", a, d & 0xffff, SekPc);
333   switch (a8) {
334     case 0x01:
335       break;
336     case 0x03:
337       if (!(pier_regs[0] & 2))
338         goto unmapped;
339       target = 0x280000;
340       base = d << 19;
341       goto do_map;
342     case 0x05:
343       if (!(pier_regs[0] & 2))
344         goto unmapped;
345       target = 0x300000;
346       base = d << 19;
347       goto do_map;
348     case 0x07:
349       if (!(pier_regs[0] & 2))
350         goto unmapped;
351       target = 0x380000;
352       base = d << 19;
353       goto do_map;
354     case 0x09:
355       Pico.sv.changed = 1;
356       eeprom_spi_write(d);
357       break;
358     case 0x0b:
359       // eeprom read
360     default:
361     unmapped:
362       //elprintf(EL_UIO, "pier w8  [%06x] %02x @%06x", a, d & 0xffff, SekPc);
363       elprintf(EL_STATUS, "-- unmapped w8 [%06x] %02x @%06x", a, d & 0xffff, SekPc);
364       break;
365   }
366   return;
367
368 do_map:
369   if (!have_bank(base))
370     return;
371
372   cpu68k_map_set(m68k_read8_map,  target, target + 0x80000 - 1, Pico.rom + base, 0);
373   cpu68k_map_set(m68k_read16_map, target, target + 0x80000 - 1, Pico.rom + base, 0);
374 }
375
376 static void carthw_pier_write16(u32 a, u32 d)
377 {
378   if ((a & 0xffff00) != 0xa13000) {
379     PicoWrite16_io(a, d);
380     return;
381   }
382
383   elprintf(EL_UIO, "pier w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
384   carthw_pier_write8(a + 1, d);
385 }
386
387 static u32 carthw_pier_read8(u32 a)
388 {
389   if ((a & 0xffff00) != 0xa13000)
390     return PicoRead8_io(a);
391
392   if (a == 0xa1300b)
393     return eeprom_spi_read(a);
394
395   elprintf(EL_UIO, "pier r8  [%06x] @%06x", a, SekPc);
396   return 0;
397 }
398
399 static void carthw_pier_statef(void);
400
401 static u32 carthw_pier_prot_read8(u32 a)
402 {
403   /* it takes more than just these reads here to disable ROM protection,
404    * but for game emulation purposes this is enough. */
405   if (pier_dump_prot > 0)
406     pier_dump_prot--;
407   if (pier_dump_prot == 0) {
408     carthw_pier_statef();
409     elprintf(EL_STATUS, "prot off on r8 @%06x", SekPc);
410   }
411   elprintf(EL_UIO, "pier r8  [%06x] @%06x", a, SekPc);
412
413   return Pico.rom[MEM_BE2(a & 0x7fff)];
414 }
415
416 static void carthw_pier_mem_setup(void)
417 {
418   cpu68k_map_set(m68k_write8_map,  0xa10000, 0xa1ffff, carthw_pier_write8, 1);
419   cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, carthw_pier_write16, 1);
420   cpu68k_map_set(m68k_read8_map,   0xa10000, 0xa1ffff, carthw_pier_read8, 1);
421 }
422
423 static void carthw_pier_prot_mem_setup(int prot_enable)
424 {
425   if (prot_enable) {
426     /* the dump protection.. */
427     int a;
428     for (a = 0x000000; a < 0x400000; a += M68K_BANK_SIZE) {
429       cpu68k_map_set(m68k_read8_map,  a, a + 0xffff, Pico.rom + Pico.romsize, 0);
430       cpu68k_map_set(m68k_read16_map, a, a + 0xffff, Pico.rom + Pico.romsize, 0);
431     }
432     cpu68k_map_set(m68k_read8_map, M68K_BANK_SIZE, M68K_BANK_SIZE * 2 - 1,
433       carthw_pier_prot_read8, 1);
434   }
435   else {
436     cpu68k_map_set(m68k_read8_map,  0, 0x27ffff, Pico.rom, 0);
437     cpu68k_map_set(m68k_read16_map, 0, 0x27ffff, Pico.rom, 0);
438   }
439 }
440
441 static void carthw_pier_statef(void)
442 {
443   carthw_pier_prot_mem_setup(pier_dump_prot);
444
445   if (!pier_dump_prot) {
446     /* setup all banks */
447     u32 r0 = pier_regs[0];
448     carthw_pier_write8(0xa13001, 3);
449     carthw_pier_write8(0xa13003, pier_regs[1]);
450     carthw_pier_write8(0xa13005, pier_regs[2]);
451     carthw_pier_write8(0xa13007, pier_regs[3]);
452     carthw_pier_write8(0xa13001, r0);
453   }
454 }
455
456 static void carthw_pier_reset(void)
457 {
458   pier_regs[0] = 1;
459   pier_regs[1] = pier_regs[2] = pier_regs[3] = 0;
460   carthw_pier_statef();
461   eeprom_spi_init(NULL);
462 }
463
464 void carthw_pier_startup(void)
465 {
466   void *eeprom_state;
467   int eeprom_size = 0;
468   int i;
469
470   elprintf(EL_STATUS, "Pier Solar mapper startup");
471
472   // mostly same as for realtec..
473   i = PicoCartResize(Pico.romsize + M68K_BANK_SIZE);
474   if (i != 0) {
475     elprintf(EL_STATUS, "OOM");
476     return;
477   }
478
479   pier_dump_prot = 3;
480
481   // create dump protection bank
482   for (i = 0; i < M68K_BANK_SIZE; i += 0x8000)
483     memcpy(Pico.rom + Pico.romsize + i, Pico.rom, 0x8000);
484
485   // save EEPROM
486   eeprom_state = eeprom_spi_init(&eeprom_size);
487   Pico.sv.flags = 0;
488   Pico.sv.size = 0x10000;
489   Pico.sv.data = calloc(1, Pico.sv.size);
490   if (!Pico.sv.data)
491     Pico.sv.size = 0;
492   carthw_pier_state[2].ptr = eeprom_state;
493   carthw_pier_state[2].size = eeprom_size;
494
495   PicoCartMemSetup  = carthw_pier_mem_setup;
496   PicoResetHook     = carthw_pier_reset;
497   PicoLoadStateHook = carthw_pier_statef;
498   carthw_chunks     = carthw_pier_state;
499 }
500
501 /* superfighter mappers, see mame: mame/src/devices/bus/megadrive/rom.cpp */
502 unsigned int carthw_sf00x_reg;
503
504 static carthw_state_chunk carthw_sf00x_state[] =
505 {
506         { CHUNK_CARTHW, sizeof(carthw_sf00x_reg), &carthw_sf00x_reg },
507         { 0,            0,                         NULL }
508 };
509
510 // SF-001
511
512 // additionally map SRAM at 0x3c0000 for the newer version of sf001
513 static u32 carthw_sf001_read8_sram(u32 a)
514 {
515   return m68k_read8((a & 0xffff) + Pico.sv.start);
516 }
517
518 static u32 carthw_sf001_read16_sram(u32 a)
519 {
520   return m68k_read16((a & 0xffff) + Pico.sv.start);
521 }
522
523 static void carthw_sf001_write8_sram(u32 a, u32 d)
524 {
525   m68k_write8((a & 0xffff) + Pico.sv.start, d);
526 }
527
528 static void carthw_sf001_write16_sram(u32 a, u32 d)
529 {
530   m68k_write16((a & 0xffff) + Pico.sv.start, d);
531 }
532
533 static void carthw_sf001_write8(u32 a, u32 d)
534 {
535   if ((a & 0xf00) != 0xe00 || (carthw_sf00x_reg & 0x20)) // wrong addr / locked
536     return;
537
538   if (d & 0x80) {
539     // bank 0xe at addr 0x000000
540     cpu68k_map_set(m68k_read8_map,  0x000000, 0x040000-1, Pico.rom+0x380000, 0);
541     cpu68k_map_set(m68k_read16_map, 0x000000, 0x040000-1, Pico.rom+0x380000, 0);
542     // SRAM also at 0x3c0000 for newer mapper version
543     cpu68k_map_set(m68k_read8_map,  0x3c0000, 0x400000-1, carthw_sf001_read8_sram, 1);
544     cpu68k_map_set(m68k_read16_map, 0x3c0000, 0x400000-1, carthw_sf001_read16_sram, 1);
545     cpu68k_map_set(m68k_write8_map, 0x3c0000, 0x400000-1, carthw_sf001_write8_sram, 1);
546     cpu68k_map_set(m68k_write16_map,0x3c0000, 0x400000-1, carthw_sf001_write16_sram, 1);
547   } else {
548     // bank 0x0 at addr 0x000000
549     cpu68k_map_set(m68k_read8_map,  0x000000, 0x040000-1, Pico.rom, 0);
550     cpu68k_map_set(m68k_read16_map, 0x000000, 0x040000-1, Pico.rom, 0);
551     // SRAM off, bank 0xf at addr 0x3c0000
552     cpu68k_map_set(m68k_read8_map,  0x3c0000, 0x400000-1, Pico.rom+0x3c0000, 0);
553     cpu68k_map_set(m68k_read16_map, 0x3c0000, 0x400000-1, Pico.rom+0x3c0000, 0);
554     cpu68k_map_set(m68k_write8_map, 0x3c0000, 0x400000-1, Pico.rom+0x3c0000, 0);
555     cpu68k_map_set(m68k_write16_map,0x3c0000, 0x400000-1, Pico.rom+0x3c0000, 0);
556   }
557   carthw_sf00x_reg = d;
558 }
559
560 static void carthw_sf001_write16(u32 a, u32 d)
561 {
562   carthw_sf001_write8(a + 1, d);
563 }
564
565 static void carthw_sf001_mem_setup(void)
566 {
567   // writing to low cartridge addresses
568   cpu68k_map_set(m68k_write8_map,  0x000000, 0x00ffff, carthw_sf001_write8, 1);
569   cpu68k_map_set(m68k_write16_map, 0x000000, 0x00ffff, carthw_sf001_write16, 1);
570 }
571
572 static void carthw_sf001_reset(void)
573 {
574   carthw_sf00x_reg = 0;
575   carthw_sf001_write8(0x0e01, 0);
576 }
577
578 static void carthw_sf001_statef(void)
579 {
580   int reg = carthw_sf00x_reg;
581   carthw_sf00x_reg = 0;
582   carthw_sf001_write8(0x0e01, reg);
583 }
584
585 void carthw_sf001_startup(void)
586 {
587   PicoCartMemSetup  = carthw_sf001_mem_setup;
588   PicoResetHook     = carthw_sf001_reset;
589   PicoLoadStateHook = carthw_sf001_statef;
590   carthw_chunks     = carthw_sf00x_state;
591 }
592
593 // SF-002
594
595 static void carthw_sf002_write8(u32 a, u32 d)
596 {
597   if ((a & 0xf00) != 0xe00)
598     return;
599
600   if (d & 0x80) {
601     // bank 0x00-0x0e on addr 0x20000
602     cpu68k_map_set(m68k_read8_map,  0x200000, 0x3c0000-1, Pico.rom, 0);
603     cpu68k_map_set(m68k_read16_map, 0x200000, 0x3c0000-1, Pico.rom, 0);
604   } else {
605     // bank 0x10-0x1e on addr 0x20000
606     cpu68k_map_set(m68k_read8_map,  0x200000, 0x3c0000-1, Pico.rom+0x200000, 0);
607     cpu68k_map_set(m68k_read16_map, 0x200000, 0x3c0000-1, Pico.rom+0x200000, 0);
608   }
609   carthw_sf00x_reg = d;
610 }
611
612 static void carthw_sf002_write16(u32 a, u32 d)
613 {
614   carthw_sf002_write8(a + 1, d);
615 }
616
617 static void carthw_sf002_mem_setup(void)
618 {
619   // writing to low cartridge addresses
620   cpu68k_map_set(m68k_write8_map,  0x000000, 0x00ffff, carthw_sf002_write8, 1);
621   cpu68k_map_set(m68k_write16_map, 0x000000, 0x00ffff, carthw_sf002_write16, 1);
622 }
623
624 static void carthw_sf002_reset(void)
625 {
626   carthw_sf002_write8(0x0e01, 0);
627 }
628
629 static void carthw_sf002_statef(void)
630 {
631   carthw_sf002_write8(0x0e01, carthw_sf00x_reg);
632 }
633
634 void carthw_sf002_startup(void)
635 {
636   PicoCartMemSetup  = carthw_sf002_mem_setup;
637   PicoResetHook     = carthw_sf002_reset;
638   PicoLoadStateHook = carthw_sf002_statef;
639   carthw_chunks     = carthw_sf00x_state;
640 }
641
642 // SF-004
643
644 // reading from cartridge I/O region returns the current bank index
645 static u32 carthw_sf004_read8(u32 a)
646 {
647   if ((a & ~0xff) == 0xa13000)
648     return carthw_sf00x_reg & 0xf0; // bank index
649   return PicoRead8_io(a);
650 }
651
652 static u32 carthw_sf004_read16(u32 a)
653 {
654   if ((a & ~0xff) == 0xa13000)
655     return carthw_sf00x_reg & 0xf0;
656   return PicoRead16_io(a);
657 }
658
659 // writing to low cartridge adresses changes mappings
660 static void carthw_sf004_write8(u32 a, u32 d)
661 {
662   int idx, i;
663   unsigned bs = 0x40000; // bank size
664
665   // there are 3 byte-sized regs, stored together in carthw_sf00x_reg
666   if (!(carthw_sf00x_reg & 0x8000))
667     return; // locked
668
669   switch (a & 0xf00) {
670   case 0xd00:
671     carthw_sf00x_reg = (carthw_sf00x_reg & ~0xff0000) | ((d & 0xff) << 16);
672     return PicoWrite8_io(0xa130f1, (d & 0x80) ? SRR_MAPPED : 0); // SRAM mapping
673   case 0xe00:
674     carthw_sf00x_reg = (carthw_sf00x_reg & ~0x00ff00) | ((d & 0xff) << 8);
675     break;
676   case 0xf00:
677     carthw_sf00x_reg = (carthw_sf00x_reg & ~0x0000ff) | ((d & 0xff) << 0);
678     break;
679   default:
680     return; // wrong addr
681   }
682
683   // bank mapping changed
684   idx = ((carthw_sf00x_reg>>4) & 0x7); // bank index
685   if ((carthw_sf00x_reg>>8) & 0x40) {
686     // linear bank mapping, starting at idx
687     for (i = 0; i < 8; i++, idx = (idx+1) & 0x7) {
688       cpu68k_map_set(m68k_read8_map,  i*bs, (i+1)*bs-1, Pico.rom + idx*bs, 0);
689       cpu68k_map_set(m68k_read16_map, i*bs, (i+1)*bs-1, Pico.rom + idx*bs, 0);
690     }
691   } else {
692     // single bank mapping
693     for (i = 0; i < 8; i++) {
694       cpu68k_map_set(m68k_read8_map,  i*bs, (i+1)*bs-1, Pico.rom + idx*bs, 0);
695       cpu68k_map_set(m68k_read16_map, i*bs, (i+1)*bs-1, Pico.rom + idx*bs, 0);
696     }
697   }
698 }
699
700 static void carthw_sf004_write16(u32 a, u32 d)
701 {
702   carthw_sf004_write8(a + 1, d);
703 }
704
705 static void carthw_sf004_mem_setup(void)
706 {
707   // writing to low cartridge addresses
708   cpu68k_map_set(m68k_write8_map,  0x000000, 0x00ffff, carthw_sf004_write8, 1);
709   cpu68k_map_set(m68k_write16_map, 0x000000, 0x00ffff, carthw_sf004_write16, 1);
710   // reading from the cartridge I/O region
711   cpu68k_map_set(m68k_read8_map,   0xa10000, 0xa1ffff, carthw_sf004_read8, 1);
712   cpu68k_map_set(m68k_read16_map,  0xa10000, 0xa1ffff, carthw_sf004_read16, 1);
713 }
714
715 static void carthw_sf004_reset(void)
716 {
717   carthw_sf00x_reg = -1;
718   carthw_sf004_write8(0x0d01, 0);
719   carthw_sf004_write8(0x0f01, 0);
720   carthw_sf004_write8(0x0e01, 0x80);
721 }
722
723 static void carthw_sf004_statef(void)
724 {
725   int reg = carthw_sf00x_reg;
726   carthw_sf00x_reg = -1;
727   carthw_sf004_write8(0x0d01, reg >> 16);
728   carthw_sf004_write8(0x0f01, reg >> 0);
729   carthw_sf004_write8(0x0e01, reg >> 8);
730 }
731
732 void carthw_sf004_startup(void)
733 {
734   PicoCartMemSetup  = carthw_sf004_mem_setup;
735   PicoResetHook     = carthw_sf004_reset;
736   PicoLoadStateHook = carthw_sf004_statef;
737   carthw_chunks     = carthw_sf00x_state;
738 }
739
740 /* Simple protection through reading flash ID */
741 static int flash_writecount;
742
743 static carthw_state_chunk carthw_flash_state[] =
744 {
745   { CHUNK_CARTHW, sizeof(flash_writecount), &flash_writecount },
746   { 0,            0,                        NULL }
747 };
748
749 static u32 PicoRead16_flash(u32 a) { return (a&6) ? 0x2257 : 0x0020; }
750
751 static void PicoWrite16_flash(u32 a, u32 d)
752 {
753   int banksz = (1<<M68K_MEM_SHIFT)-1;
754   switch (++flash_writecount) {
755     case 3: cpu68k_map_set(m68k_read16_map, 0, banksz, PicoRead16_flash, 1);
756             break;
757     case 4: cpu68k_map_read_mem(0, banksz, Pico.rom, 0);
758             flash_writecount = 0;
759             break;
760   }
761 }
762
763 static void carthw_flash_mem_setup(void)
764 {
765   cpu68k_map_set(m68k_write16_map, 0, 0x7fffff, PicoWrite16_flash, 1);
766 }
767
768 void carthw_flash_startup(void)
769 {
770   elprintf(EL_STATUS, "Flash prot emu startup");
771
772   PicoCartMemSetup   = carthw_flash_mem_setup;
773   carthw_chunks      = carthw_flash_state;
774 }
775
776 /* Simple unlicensed ROM protection emulation */
777 static struct {
778   u32 addr;
779   u32 mask;
780   u16 val;
781   u16 readonly;
782 } sprot_items[8];
783 static int sprot_item_count;
784
785 static carthw_state_chunk carthw_sprot_state[] =
786 {
787   { CHUNK_CARTHW, sizeof(sprot_items), &sprot_items },
788   { 0,            0,                         NULL }
789 };
790
791 static u16 *carthw_sprot_get_val(u32 a, int rw_only)
792 {
793   int i;
794
795   for (i = 0; i < sprot_item_count; i++)
796     if ((a & sprot_items[i].mask) == sprot_items[i].addr)
797       if (!rw_only || !sprot_items[i].readonly)
798         return &sprot_items[i].val;
799
800   return NULL;
801 }
802
803 static u32 PicoRead8_sprot(u32 a)
804 {
805   u16 *val;
806   u32 d;
807
808   val = carthw_sprot_get_val(a, 0);
809   if (val != NULL) {
810     d = *val;
811     if (!(a & 1))
812       d >>= 8;
813     elprintf(EL_UIO, "prot r8  [%06x]   %02x @%06x", a, d, SekPc);
814     return d;
815   }
816   else if (0xa10000 <= a && a <= 0xa1ffff)
817     return PicoRead8_io(a);
818
819   elprintf(EL_UIO, "prot r8  [%06x] MISS @%06x", a, SekPc);
820   return 0;
821 }
822
823 static u32 PicoRead16_sprot(u32 a)
824 {
825   u16 *val;
826
827   val = carthw_sprot_get_val(a, 0);
828   if (val != NULL) {
829     elprintf(EL_UIO, "prot r16 [%06x] %04x @%06x", a, *val, SekPc);
830     return *val;
831   }
832   else if (0xa10000 <= a && a <= 0xa1ffff)
833     return PicoRead16_io(a);
834
835   elprintf(EL_UIO, "prot r16 [%06x] MISS @%06x", a, SekPc);
836   return 0;
837 }
838
839 static void PicoWrite8_sprot(u32 a, u32 d)
840 {
841   u16 *val;
842
843   val = carthw_sprot_get_val(a, 1);
844   if (val != NULL) {
845     if (a & 1)
846       *val = (*val & 0xff00) | (d | 0xff);
847     else
848       *val = (*val & 0x00ff) | (d << 8);
849     elprintf(EL_UIO, "prot w8  [%06x]   %02x @%06x", a, d & 0xff, SekPc);
850   }
851   else if (0xa10000 <= a && a <= 0xa1ffff)
852     return PicoWrite8_io(a, d);
853
854   elprintf(EL_UIO, "prot w8  [%06x]   %02x MISS @%06x", a, d & 0xff, SekPc);
855 }
856
857 static void PicoWrite16_sprot(u32 a, u32 d)
858 {
859   u16 *val;
860
861   val = carthw_sprot_get_val(a, 1);
862   if (val != NULL) {
863     *val = d;
864     elprintf(EL_UIO, "prot w16 [%06x] %04x @%06x", a, d & 0xffff, SekPc);
865   }
866   else if (0xa10000 <= a && a <= 0xa1ffff)
867     return PicoWrite16_io(a, d);
868
869   elprintf(EL_UIO, "prot w16 [%06x] %04x MISS @%06x", a, d & 0xffff, SekPc);
870 }
871
872 void carthw_sprot_new_location(unsigned int a, unsigned int mask, unsigned short val, int is_ro)
873 {
874   int sprot_elems = sizeof(sprot_items)/sizeof(sprot_items[0]);
875   if (sprot_item_count == sprot_elems) {
876     elprintf(EL_STATUS, "too many sprot items");
877     return;
878   }
879
880   sprot_items[sprot_item_count].addr = a;
881   sprot_items[sprot_item_count].mask = mask;
882   sprot_items[sprot_item_count].val = val;
883   sprot_items[sprot_item_count].readonly = is_ro;
884   sprot_item_count++;
885 }
886
887 static void carthw_sprot_unload(void)
888 {
889   sprot_item_count = 0;
890 }
891
892 static void carthw_sprot_mem_setup(void)
893 {
894   int start;
895
896   // map 0x400000 - 0x7fffff, /TIME areas (which are tipically used)
897   start = (Pico.romsize + M68K_BANK_MASK) & ~M68K_BANK_MASK;
898   if (start < 0x400000) start = 0x400000;
899
900   cpu68k_map_set(m68k_read8_map,   start, 0x7fffff, PicoRead8_sprot, 1);
901   cpu68k_map_set(m68k_read16_map,  start, 0x7fffff, PicoRead16_sprot, 1);
902   cpu68k_map_set(m68k_write8_map,  start, 0x7fffff, PicoWrite8_sprot, 1);
903   cpu68k_map_set(m68k_write16_map, start, 0x7fffff, PicoWrite16_sprot, 1);
904
905   cpu68k_map_set(m68k_read8_map,   0xa10000, 0xa1ffff, PicoRead8_sprot, 1);
906   cpu68k_map_set(m68k_read16_map,  0xa10000, 0xa1ffff, PicoRead16_sprot, 1);
907   cpu68k_map_set(m68k_write8_map,  0xa10000, 0xa1ffff, PicoWrite8_sprot, 1);
908   cpu68k_map_set(m68k_write16_map, 0xa10000, 0xa1ffff, PicoWrite16_sprot, 1);
909 }
910
911 void carthw_sprot_startup(void)
912 {
913   elprintf(EL_STATUS, "Prot emu startup");
914
915   PicoCartMemSetup   = carthw_sprot_mem_setup;
916   PicoCartUnloadHook = carthw_sprot_unload;
917   carthw_chunks      = carthw_sprot_state;
918 }
919
920 /* Protection emulation for Lion King 3. Credits go to Haze */
921 static struct {
922   u32 bank;
923   u8 cmd, data;
924 } carthw_lk3_regs;
925
926 static carthw_state_chunk carthw_lk3_state[] =
927 {
928   { CHUNK_CARTHW, sizeof(carthw_lk3_regs), &carthw_lk3_regs },
929   { 0,            0,                         NULL }
930 };
931
932 static u8 *carthw_lk3_mem; // shadow copy memory
933 static u32 carthw_lk3_madr[0x100000/M68K_BANK_SIZE];
934
935 static u32 PicoRead8_plk3(u32 a)
936 {
937   u32 d = 0;
938   switch (carthw_lk3_regs.cmd) {
939     case 0: d = carthw_lk3_regs.data << 1; break;
940     case 1: d = carthw_lk3_regs.data >> 1; break;
941     case 2: // nibble rotate
942       d = ((carthw_lk3_regs.data >> 4) | (carthw_lk3_regs.data << 4)) & 0xff;
943       break;
944     case 3: // bit rotate
945       d = carthw_lk3_regs.data;
946       d = (d >> 4) | (d << 4);
947       d = ((d & 0xcc) >> 2) | ((d & 0x33) << 2);
948       d = ((d & 0xaa) >> 1) | ((d & 0x55) << 1);
949       break;
950     default:
951       elprintf(EL_UIO, "unhandled prot cmd %02x @%06x", carthw_lk3_regs.cmd, SekPc);
952       break;
953   }
954
955   elprintf(EL_UIO, "prot r8  [%06x]   %02x @%06x", a, d, SekPc);
956   return d;
957 }
958
959 static void PicoWrite8_plk3p(u32 a, u32 d)
960 {
961   elprintf(EL_UIO, "prot w8  [%06x]   %02x @%06x", a, d & 0xff, SekPc);
962   if (a & 2)
963     carthw_lk3_regs.cmd = d & 0x3;
964   else
965     carthw_lk3_regs.data = d;
966 }
967
968 static void PicoWrite8_plk3b(u32 a, u32 d)
969 {
970   u32 addr;
971
972   elprintf(EL_UIO, "prot w8  [%06x]   %02x @%06x", a, d & 0xff, SekPc);
973   addr = d << 15;
974   if (addr+0x10000 >= Pico.romsize) {
975     elprintf(EL_UIO|EL_ANOMALY, "lk3_mapper: bank too large: %02x", d);
976     return;
977   }
978
979   if (addr != carthw_lk3_regs.bank) {
980     // banking is by or'ing the bank address in the 1st megabyte, not adding.
981     // only do linear mapping if map addresses aren't overlapping bank address
982     u32 len = M68K_BANK_SIZE;
983     u32 a, b;
984     for (b = 0x000000; b < 0x0100000; b += len) {
985       if (!((b + (len-1)) & addr)) {
986         cpu68k_map_set(m68k_read8_map,  b, b + (len-1), Pico.rom+addr + b, 0);
987         cpu68k_map_set(m68k_read16_map, b, b + (len-1), Pico.rom+addr + b, 0);
988       } else {
989         // overlap. ugh, need a shadow copy since banks can contain code and
990         // 68K cpu emulator cores need mapped access to code memory
991         if (carthw_lk3_madr[b/len] != addr) // only if shadow isn't the same
992           for (a = b; a < b+M68K_BANK_SIZE; a += 0x8000)
993             memcpy(carthw_lk3_mem + a, Pico.rom + (addr|a), 0x8000);
994         carthw_lk3_madr[b/len] = addr;
995         cpu68k_map_set(m68k_read8_map,  b, b + (len-1), carthw_lk3_mem + b, 0);
996         cpu68k_map_set(m68k_read16_map, b, b + (len-1), carthw_lk3_mem + b, 0);
997       }
998     }
999   }
1000   carthw_lk3_regs.bank = addr;
1001 }
1002
1003 static void carthw_lk3_mem_setup(void)
1004 {
1005   cpu68k_map_set(m68k_read8_map,   0x600000, 0x7fffff, PicoRead8_plk3, 1);
1006   cpu68k_map_set(m68k_write8_map,  0x600000, 0x6fffff, PicoWrite8_plk3p, 1);
1007   cpu68k_map_set(m68k_write8_map,  0x700000, 0x7fffff, PicoWrite8_plk3b, 1);
1008   carthw_lk3_regs.bank = 0;
1009 }
1010
1011 static void carthw_lk3_statef(void)
1012 {
1013   int bank = carthw_lk3_regs.bank >> 15;
1014   carthw_lk3_regs.bank = -1;
1015   PicoWrite8_plk3b(0x700000, bank);
1016 }
1017
1018 static void carthw_lk3_unload(void)
1019 {
1020   free(carthw_lk3_mem);
1021   carthw_lk3_mem = NULL;
1022   memset(carthw_lk3_madr, 0, sizeof(carthw_lk3_madr));
1023 }
1024
1025 void carthw_lk3_startup(void)
1026 {
1027   elprintf(EL_STATUS, "lk3 prot emu startup");
1028
1029   // allocate space for shadow copy
1030   if (carthw_lk3_mem == NULL)
1031     carthw_lk3_mem = malloc(0x100000);
1032   if (carthw_lk3_mem == NULL) {
1033     elprintf(EL_STATUS, "OOM");
1034     return;
1035   }
1036
1037   PicoCartMemSetup   = carthw_lk3_mem_setup;
1038   PicoLoadStateHook  = carthw_lk3_statef;
1039   PicoCartUnloadHook = carthw_lk3_unload;
1040   carthw_chunks      = carthw_lk3_state;
1041 }
1042
1043 /* SMW64 mapper, based on mame source */
1044 static struct {
1045   u32 bank60, bank61;
1046   u16 data[8], ctrl[4];
1047 } carthw_smw64_regs;
1048
1049 static carthw_state_chunk carthw_smw64_state[] =
1050 {
1051   { CHUNK_CARTHW, sizeof(carthw_smw64_regs), &carthw_smw64_regs },
1052   { 0,            0,                         NULL }
1053 };
1054
1055 static u32 PicoRead8_smw64(u32 a)
1056 {
1057   u16 *data = carthw_smw64_regs.data, *ctrl = carthw_smw64_regs.ctrl;
1058   u32 d = 0;
1059
1060   if (a & 1) {
1061     if (a>>16 == 0x66) switch ((a>>1) & 7) {
1062       case 0: d = carthw_smw64_regs.data[0]  ; break;
1063       case 1: d = carthw_smw64_regs.data[0]+1; break;
1064       case 2: d = carthw_smw64_regs.data[1]  ; break;
1065       case 3: d = carthw_smw64_regs.data[1]+1; break;
1066       case 4: d = carthw_smw64_regs.data[2]  ; break;
1067       case 5: d = carthw_smw64_regs.data[2]+1; break;
1068       case 6: d = carthw_smw64_regs.data[2]+2; break;
1069       case 7: d = carthw_smw64_regs.data[2]+3; break;
1070     } else /*0x67*/ { // :-O
1071       if (ctrl[1] & 0x80)
1072         d = ctrl[2] & 0x40 ? data[4]&data[5] : data[4]^0xff;
1073       if (a & 2)
1074         d &= 0x7f;
1075       else if (ctrl[2] & 0x80) {
1076         if (ctrl[2] & 0x20)
1077           data[2] = (data[5] << 2) & 0xfc;
1078         else
1079           data[0] = ((data[4] << 1) ^ data[3]) & 0xfe;
1080       }
1081     }
1082   }
1083
1084   elprintf(EL_UIO, "prot r8  [%06x]   %02x @%06x", a, d, SekPc);
1085   return d;
1086 }
1087
1088 static u32 PicoRead16_smw64(u32 a)
1089 {
1090   return PicoRead8_smw64(a+1);
1091 }
1092
1093 static void PicoWrite8_smw64(u32 a, u32 d)
1094 {
1095   u16 *data = carthw_smw64_regs.data, *ctrl = carthw_smw64_regs.ctrl;
1096
1097   if ((a & 3) == 1) {
1098     switch (a >> 16) {
1099     case 0x60: ctrl[0] = d; break;
1100     case 0x64: data[4] = d; break;
1101     case 0x67:
1102       if (ctrl[1] & 0x80) {
1103         carthw_smw64_regs.bank60 = 0x80000 + ((d<<14) & 0x70000);
1104         cpu68k_map_set(m68k_read8_map,  0x600000, 0x60ffff, Pico.rom + carthw_smw64_regs.bank60, 0);
1105         cpu68k_map_set(m68k_read16_map, 0x600000, 0x60ffff, Pico.rom + carthw_smw64_regs.bank60, 0);
1106       }
1107       ctrl[2] = d;
1108     }
1109   } else if ((a & 3) == 3) {
1110     switch (a >> 16) {
1111     case 0x61: ctrl[1] = d; break;
1112     case 0x64: data[5] = d; break;
1113     case 0x60:
1114       switch (ctrl[0] & 7) { // :-O
1115       case 0: data[0] = (data[0]^data[3] ^ d) & 0xfe; break;
1116       case 1: data[1] = (                  d) & 0xfe; break;
1117       case 7:
1118         carthw_smw64_regs.bank61 = 0x80000 + ((d<<14) & 0x70000);
1119         cpu68k_map_set(m68k_read8_map,  0x610000, 0x61ffff, Pico.rom + carthw_smw64_regs.bank61, 0);
1120         cpu68k_map_set(m68k_read16_map, 0x610000, 0x61ffff, Pico.rom + carthw_smw64_regs.bank61, 0);
1121         break;
1122       }
1123       data[3] = d;
1124     }
1125   }
1126 }
1127
1128 static void PicoWrite16_smw64(u32 a, u32 d)
1129 {
1130   PicoWrite8_smw64(a+1, d);
1131 }
1132
1133 static void carthw_smw64_mem_setup(void)
1134 {
1135   // 1st 512 KB mirrored
1136   cpu68k_map_set(m68k_read8_map,   0x080000, 0x0fffff, Pico.rom, 0);
1137   cpu68k_map_set(m68k_read16_map,  0x080000, 0x0fffff, Pico.rom, 0);
1138
1139   cpu68k_map_set(m68k_read8_map,   0x660000, 0x67ffff, PicoRead8_smw64, 1);
1140   cpu68k_map_set(m68k_read16_map,  0x660000, 0x67ffff, PicoRead16_smw64, 1);
1141   cpu68k_map_set(m68k_write8_map,  0x600000, 0x67ffff, PicoWrite8_smw64, 1);
1142   cpu68k_map_set(m68k_write16_map, 0x600000, 0x67ffff, PicoWrite16_smw64, 1);
1143 }
1144
1145 static void carthw_smw64_statef(void)
1146 {
1147   cpu68k_map_set(m68k_read8_map,   0x600000, 0x60ffff, Pico.rom + carthw_smw64_regs.bank60, 0);
1148   cpu68k_map_set(m68k_read16_map,  0x600000, 0x60ffff, Pico.rom + carthw_smw64_regs.bank60, 0);
1149   cpu68k_map_set(m68k_read8_map,   0x610000, 0x61ffff, Pico.rom + carthw_smw64_regs.bank61, 0);
1150   cpu68k_map_set(m68k_read16_map,  0x610000, 0x61ffff, Pico.rom + carthw_smw64_regs.bank61, 0);
1151 }
1152
1153 static void carthw_smw64_reset(void)
1154 {
1155   memset(&carthw_smw64_regs, 0, sizeof(carthw_smw64_regs));
1156 }
1157
1158 void carthw_smw64_startup(void)
1159 {
1160   elprintf(EL_STATUS, "SMW64 mapper startup");
1161
1162   PicoCartMemSetup  = carthw_smw64_mem_setup;
1163   PicoResetHook     = carthw_smw64_reset;
1164   PicoLoadStateHook = carthw_smw64_statef;
1165   carthw_chunks     = carthw_smw64_state;
1166 }
1167
1168 /* J-Cart */
1169 unsigned char carthw_jcart_th;
1170
1171 static carthw_state_chunk carthw_jcart_state[] =
1172 {
1173   { CHUNK_CARTHW, sizeof(carthw_jcart_th), &carthw_jcart_th },
1174   { 0,            0,                       NULL }
1175 };
1176
1177 static void carthw_jcart_write8(u32 a, u32 d)
1178 {
1179   carthw_jcart_th = (d&1) << 6;
1180 }
1181
1182 static void carthw_jcart_write16(u32 a, u32 d)
1183 {
1184   carthw_jcart_write8(a+1, d);
1185 }
1186
1187 static u32 carthw_jcart_read8(u32 a)
1188 {
1189   u32 v = PicoReadPad(2 + (a&1), 0x3f | carthw_jcart_th);
1190   // some carts additionally have an EEPROM; SDA is also readable in this range
1191   if (Pico.m.sram_reg & SRR_MAPPED)
1192     v |= EEPROM_read() & 0x80; // SDA is always on bit 7 for J-Carts
1193   return v;
1194 }
1195
1196 static u32 carthw_jcart_read16(u32 a)
1197 {
1198   return carthw_jcart_read8(a) | (carthw_jcart_read8(a+1) << 8);
1199 }
1200
1201 static void carthw_jcart_mem_setup(void)
1202 {
1203   cpu68k_map_set(m68k_write8_map,  0x380000, 0x3fffff, carthw_jcart_write8, 1);
1204   cpu68k_map_set(m68k_write16_map, 0x380000, 0x3fffff, carthw_jcart_write16, 1);
1205   cpu68k_map_set(m68k_read8_map,  0x380000, 0x3fffff, carthw_jcart_read8, 1);
1206   cpu68k_map_set(m68k_read16_map, 0x380000, 0x3fffff, carthw_jcart_read16, 1);
1207 }
1208
1209 void carthw_jcart_startup(void)
1210 {
1211   elprintf(EL_STATUS, "J-Cart startup");
1212
1213   PicoCartMemSetup  = carthw_jcart_mem_setup;
1214   carthw_chunks     = carthw_jcart_state;
1215 }
1216
1217 // vim:ts=2:sw=2:expandtab