+// Codemasters mapper. Similar to Sega, but different addresses
+static void write_bank_codem(unsigned short a, unsigned char d)
+{
+ if (a >= 0xc000 || (a & 0x3fff)) return; // address is 0x0000, 0x4000, 0x8000?
+ // don't detect linear mapping to avoid confusing with MSX
+ if (Pico.ms.mapper != PMS_MAP_CODEM && (Pico.ms.mapper || (a>>14) == d)) return;
+ elprintf(EL_Z80BNK, "bank codem %04x %02x @ %04x", a, d, z80_pc());
+ Pico.ms.mapper = PMS_MAP_CODEM;
+ if (Pico.ms.carthw[a>>14] == d) return;
+ Pico.ms.carthw[a>>14] = d;
+
+ d &= bank_mask;
+ z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0);
+ if (Pico.ms.carthw[1] & 0x80) {
+ z80_map_set(z80_read_map, 0xa000, 0xbfff, PicoMem.vram+0x4000, 0);
+ z80_map_set(z80_write_map, 0xa000, 0xbfff, PicoMem.vram+0x4000, 0);
+ } else {
+ d = Pico.ms.carthw[2] & bank_mask;
+ z80_map_set(z80_read_map, 0xa000, 0xbfff, Pico.rom + (d << 14)+0x2000, 0);
+ z80_map_set(z80_write_map, 0xa000, 0xbfff, xwrite, 1);
+ }
+}
+
+// MSX mapper. 4 selectable 8KB banks at the top
+static void write_bank_msx(unsigned short a, unsigned char d)
+{
+ if (a > 0x0003) return;
+ // don't detect linear mapping to avoid confusing with Codemasters
+ if (Pico.ms.mapper != PMS_MAP_MSX && (Pico.ms.mapper || (a|d) == 0 || d >= 0x80)) return;
+ elprintf(EL_Z80BNK, "bank msx %04x %02x @ %04x", a, d, z80_pc());
+ Pico.ms.mapper = PMS_MAP_MSX;
+ Pico.ms.carthw[a] = d;
+
+ a = (a^2)*0x2000 + 0x4000;
+ d &= 2*bank_mask + 1;
+ z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0);
+}
+
+// Korea mapping, 1 selectable 16KB bank at the top
+static void write_bank_korea(unsigned short a, unsigned char d)
+{
+ if (a != 0xa000) return;
+ if (Pico.ms.mapper != PMS_MAP_KOREA && (Pico.ms.mapper)) return;
+ elprintf(EL_Z80BNK, "bank korea %04x %02x @ %04x", a, d, z80_pc());
+ Pico.ms.mapper = PMS_MAP_KOREA;
+ Pico.ms.carthw[0xf] = d;
+
+ d &= bank_mask;
+ z80_map_set(z80_read_map, 0x8000, 0xbfff, Pico.rom + (d << 14), 0);
+}
+
+// Korean n-in-1 mapping. 1 selectable 32KB bank at the bottom
+static void write_bank_n32k(unsigned short a, unsigned char d)
+{
+ if (a != 0xffff) return;
+ // code must be in RAM since all visible ROM space is swapped
+ if (Pico.ms.mapper != PMS_MAP_N32K && (Pico.ms.mapper || z80_pc() < 0xc000)) return;
+ elprintf(EL_Z80BNK, "bank 32k %04x %02x @ %04x", a, d, z80_pc());
+ Pico.ms.mapper = PMS_MAP_N32K;
+ Pico.ms.carthw[0xf] = d;
+
+ d &= bank_mask >> 1;
+ z80_map_set(z80_read_map, 0, 0x7fff, Pico.rom + (d << 15), 0);
+}
+
+// Korean 4-in-1. 2 selectable 16KB banks, top bank is shifted by bottom one
+static void write_bank_n16k(unsigned short a, unsigned char d)
+{
+ if (a != 0x3ffe && a != 0x7fff && a != 0xbfff) return;
+ // code must be in RAM since all visible ROM space is swapped
+ if (Pico.ms.mapper != PMS_MAP_N16K && (Pico.ms.mapper || z80_pc() < 0xc000)) return;
+ elprintf(EL_Z80BNK, "bank 16k %04x %02x @ %04x", a, d, z80_pc());
+ Pico.ms.mapper = PMS_MAP_N16K;
+ Pico.ms.carthw[a>>14] = d;
+
+ d &= bank_mask;
+ a = a & 0xc000;
+ // the top bank shifts with the bottom bank.
+ if (a == 0x8000) d += Pico.ms.carthw[0] & 0x30;
+ z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0);
+}
+
+// MSX-Nemesis mapper. 4 selectable 8KB banks at the top
+static void write_bank_msxn(unsigned short a, unsigned char d)
+{
+ if (a > 0x0003) return;
+ // never autodetected, selectable only via config
+ if (Pico.ms.mapper != PMS_MAP_NEMESIS) return;
+ elprintf(EL_Z80BNK, "bank nems %04x %02x @ %04x", a, d, z80_pc());
+ Pico.ms.carthw[a] = d;
+
+ a = (a^2)*0x2000 + 0x4000;
+ d &= 2*bank_mask + 1;
+ z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0);
+}
+
+// Korean Janggun mapper. 4 selectable 8KB banks at the top, hardware byte flip
+static unsigned char read_flipped_jang(unsigned a)
+{
+ static unsigned char flipper[16] = // reversed nibble bit order
+ { 0x0,0x8,0x4,0xc,0x2,0xa,0x6,0xe,0x1,0x9,0x5,0xd,0x3,0xb,0x7,0xf };
+ unsigned char c;
+
+ // return value at address a in reversed bit order
+ c = Pico.rom[(Pico.ms.carthw[a>>13] << 13) + (a & 0x1fff)];
+ return (flipper[c&0xf]<<4) | flipper[c>>4];
+}
+
+static void write_bank_jang(unsigned short a, unsigned char d)
+{
+ // address is 0xfffe, 0xffff, 0x4000, 0x6000, 0x8000, 0xa000
+ if ((a|1) != 0xffff && (a < 0x4000 || a > 0xa000 || (a & 0x1fff))) return;
+ // never autodetected, selectable only via config
+ if (Pico.ms.mapper != PMS_MAP_JANGGUN) return;
+ elprintf(EL_Z80BNK, "bank jang %04x %02x @ %04x", a, d, z80_pc());
+
+ if ((a|1) == 0xffff) {
+ int x = a & 1, f = d & 0x40;
+ Pico.ms.carthw[x] = d;
+ d &= bank_mask;
+ Pico.ms.carthw[2*x + 2] = 2*d, Pico.ms.carthw[2*x + 3] = 2*d+1;
+ a = (x+1) * 0x4000;
+ if (!f)
+ z80_map_set(z80_read_map, a, a+0x3fff, Pico.rom + (d << 14), 0);
+ else
+ z80_map_set(z80_read_map, a, a+0x3fff, read_flipped_jang, 1);
+ } else {
+ d &= 2*bank_mask + 1;
+ Pico.ms.carthw[a>>13] = d;
+ if (!(Pico.ms.carthw[(a>>15)&1] & 0x40))
+ z80_map_set(z80_read_map, a, a+0x1fff, Pico.rom + (d << 13), 0);
+ else
+ z80_map_set(z80_read_map, a, a+0x1fff, read_flipped_jang, 1);
+ }
+}
+
+// Korean 188-in-1. 4 8KB banks from 0x4000, selected by xor'd bank index
+static void write_bank_xor(unsigned short a, unsigned char d)
+{
+ // 4x8KB bank select @0x2000
+ if ((a&0xff00) != 0x2000) return;
+ if (Pico.ms.mapper != PMS_MAP_XOR && Pico.ms.mapper) return;
+
+ elprintf(EL_Z80BNK, "bank xor %04x %02x @ %04x", a, d, z80_pc());
+ Pico.ms.mapper = PMS_MAP_XOR;
+
+ Pico.ms.carthw[0] = d;
+ z80_map_set(z80_read_map, 0x4000, 0x5fff, Pico.rom + ((d^0x1f) << 13), 0);
+ z80_map_set(z80_read_map, 0x6000, 0x7fff, Pico.rom + ((d^0x1e) << 13), 0);
+ z80_map_set(z80_read_map, 0x8000, 0x9fff, Pico.rom + ((d^0x1d) << 13), 0);
+ z80_map_set(z80_read_map, 0xa000, 0xbfff, Pico.rom + ((d^0x1c) << 13), 0);
+}
+
+// SG-1000 8KB RAM Adaptor mapper. 8KB RAM at address 0x2000
+static void write_bank_x8k(unsigned short a, unsigned char d)
+{
+ // 8KB address range @ 0x2000 (adaptor) or @ 0x8000 (cartridge)
+ if (((a&0xe000) != 0x2000 && (a&0xe000) != 0x8000) || (a & 0x0f) == 5) return;
+ if (Pico.ms.mapper != PMS_MAP_8KBRAM && Pico.ms.mapper) return;
+
+ elprintf(EL_Z80BNK, "bank x8k %04x %02x @ %04x", a, d, z80_pc());
+ ((unsigned char *)(PicoMem.vram+0x4000))[a&0x1fff] = d;
+ Pico.ms.mapper = PMS_MAP_8KBRAM;
+
+ a &= 0xe000;
+ Pico.ms.carthw[0] = a >> 12;
+ z80_map_set(z80_read_map, a, a+0x1fff, PicoMem.vram+0x4000, 0);
+ z80_map_set(z80_write_map, a, a+0x1fff, PicoMem.vram+0x4000, 0);
+}
+
+// SC-3000 32KB RAM mapper for BASIC level IIIB. 32KB RAM at address 0x8000
+static void write_bank_x32k(unsigned short a, unsigned char d)
+{
+ // 32KB address range @ 0x8000
+ if ((a&0xc000) != 0x8000) return;
+ if (Pico.ms.mapper != PMS_MAP_32KBRAM &&
+ (Pico.ms.mapper || Pico.romsize > 0x8000)) return;
+
+ elprintf(EL_Z80BNK, "bank x32k %04x %02x @ %04x", a, d, z80_pc());
+ ((unsigned char *)(PicoMem.vram+0x4000))[a&0x7fff] = d;
+ Pico.ms.mapper = PMS_MAP_32KBRAM;
+
+ a &= 0xc000;
+ Pico.ms.carthw[0] = a >> 12;
+ // NB this deactivates internal RAM and all mapper detection
+ z80_map_set(z80_read_map, a, a+0x7fff, PicoMem.vram+0x4000, 0);
+ z80_map_set(z80_write_map, a, a+0x7fff, PicoMem.vram+0x4000, 0);
+}
+
+char *mappers[] = {
+ [PMS_MAP_SEGA] = "Sega",
+ [PMS_MAP_CODEM] = "Codemasters",
+ [PMS_MAP_KOREA] = "Korea",
+ [PMS_MAP_MSX] = "Korea MSX",
+ [PMS_MAP_N32K] = "Korea X-in-1",
+ [PMS_MAP_N16K] = "Korea 4-Pak",
+ [PMS_MAP_JANGGUN] = "Korea Janggun",
+ [PMS_MAP_NEMESIS] = "Korea Nemesis",
+ [PMS_MAP_8KBRAM] = "Taiwan 8K RAM",
+ [PMS_MAP_XOR] = "Korea XOR",
+ [PMS_MAP_32KBRAM] = "Sega 32K RAM",
+};
+
+// TODO auto-selecting is not really reliable.
+// Before adding more mappers this should be revised.