/* FCE Ultra - NES/Famicom Emulator * * Copyright notice for this file: * Copyright (C) 2002 Xodnizel * Copyright (C) 2005 CaH4e3 * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "mapinc.h" /* #define DEBUG90 */ /* Mapper 090 is simpliest mapper hardware and have not extended nametable control and latched chr banks in 4k mode * Mapper 209 much compicated hardware with decribed above features disabled by default and switchable by command * Mapper 211 the same mapper 209 but with forced nametable control */ static int is209; static int is211; static uint8 IRQMode; /* from $c001 */ static uint8 IRQPre; /* from $c004 */ static uint8 IRQPreSize; /* from $c007 */ static uint8 IRQCount; /* from $c005 */ static uint8 IRQXOR; /* Loaded from $C006 */ static uint8 IRQa; /* $c002, $c003, and $c000 */ static uint8 mul[2]; static uint8 regie; static uint8 tkcom[4]; static uint8 prgb[4]; static uint8 chrlow[8]; static uint8 chrhigh[8]; static uint8 chr[2]; static uint16 names[4]; static uint8 tekker; static SFORMAT Tek_StateRegs[] = { { &IRQMode, 1, "IRQM" }, { &IRQPre, 1, "IRQP" }, { &IRQPreSize, 1, "IRQR" }, { &IRQCount, 1, "IRQC" }, { &IRQXOR, 1, "IRQX" }, { &IRQa, 1, "IRQA" }, { mul, 2, "MUL" }, { ®ie, 1, "REGI" }, { tkcom, 4, "TKCO" }, { prgb, 4, "PRGB" }, { chr, 2, "CLTC" }, { chrlow, 4, "CHRL" }, { chrhigh, 8, "CHRH" }, { &names[0], 2 | FCEUSTATE_RLSB, "NMS0" }, { &names[1], 2 | FCEUSTATE_RLSB, "NMS1" }, { &names[2], 2 | FCEUSTATE_RLSB, "NMS2" }, { &names[3], 2 | FCEUSTATE_RLSB, "NMS3" }, { &tekker, 1, "TEKR" }, { 0 } }; static void mira(void) { if ((tkcom[0] & 0x20 && is209) || is211) { int x; if (tkcom[0] & 0x40) { /* Name tables are ROM-only */ for (x = 0; x < 4; x++) setntamem(CHRptr[0] + (((names[x]) & CHRmask1[0]) << 10), 0, x); } else { /* Name tables can be RAM or ROM. */ for (x = 0; x < 4; x++) { if ((tkcom[1] & 0x80) == (names[x] & 0x80)) /* RAM selected. */ setntamem(NTARAM + ((names[x] & 0x1) << 10), 1, x); else setntamem(CHRptr[0] + (((names[x]) & CHRmask1[0]) << 10), 0, x); } } } else { switch (tkcom[1] & 3) { case 0: setmirror(MI_V); break; case 1: setmirror(MI_H); break; case 2: setmirror(MI_0); break; case 3: setmirror(MI_1); break; } } } static void tekprom(void) { uint32 bankmode = ((tkcom[3] & 6) << 5); switch (tkcom[0] & 7) { case 00: if (tkcom[0] & 0x80) setprg8(0x6000, (((prgb[3] << 2) + 3) & 0x3F) | bankmode); setprg32(0x8000, 0x0F | ((tkcom[3] & 6) << 3)); break; case 01: if (tkcom[0] & 0x80) setprg8(0x6000, (((prgb[3] << 1) + 1) & 0x3F) | bankmode); setprg16(0x8000, (prgb[1] & 0x1F) | ((tkcom[3] & 6) << 4)); setprg16(0xC000, 0x1F | ((tkcom[3] & 6) << 4)); break; case 03: /* bit reversion */ case 02: if (tkcom[0] & 0x80) setprg8(0x6000, (prgb[3] & 0x3F) | bankmode); setprg8(0x8000, (prgb[0] & 0x3F) | bankmode); setprg8(0xa000, (prgb[1] & 0x3F) | bankmode); setprg8(0xc000, (prgb[2] & 0x3F) | bankmode); setprg8(0xe000, 0x3F | bankmode); break; case 04: if (tkcom[0] & 0x80) setprg8(0x6000, (((prgb[3] << 2) + 3) & 0x3F) | bankmode); setprg32(0x8000, (prgb[3] & 0x0F) | ((tkcom[3] & 6) << 3)); break; case 05: if (tkcom[0] & 0x80) setprg8(0x6000, (((prgb[3] << 1) + 1) & 0x3F) | bankmode); setprg16(0x8000, (prgb[1] & 0x1F) | ((tkcom[3] & 6) << 4)); setprg16(0xC000, (prgb[3] & 0x1F) | ((tkcom[3] & 6) << 4)); break; case 07: /* bit reversion */ case 06: if (tkcom[0] & 0x80) setprg8(0x6000, (prgb[3] & 0x3F) | bankmode); setprg8(0x8000, (prgb[0] & 0x3F) | bankmode); setprg8(0xa000, (prgb[1] & 0x3F) | bankmode); setprg8(0xc000, (prgb[2] & 0x3F) | bankmode); setprg8(0xe000, (prgb[3] & 0x3F) | bankmode); break; } } static void tekvrom(void) { int x, bank = 0, mask = 0xFFFF; if (!(tkcom[3] & 0x20)) { bank = (tkcom[3] & 1) | ((tkcom[3] & 0x18) >> 2); switch (tkcom[0] & 0x18) { case 0x00: bank <<= 5; mask = 0x1F; break; case 0x08: bank <<= 6; mask = 0x3F; break; case 0x10: bank <<= 7; mask = 0x7F; break; case 0x18: bank <<= 8; mask = 0xFF; break; } } switch (tkcom[0] & 0x18) { case 0x00: /* 8KB */ setchr8(((chrlow[0] | (chrhigh[0] << 8)) & mask) | bank); break; case 0x08: /* 4KB */ #if 0 for(x=0;x<8;x+=4) setchr4(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank); #endif setchr4(0x0000, ((chrlow[chr[0]] | (chrhigh[chr[0]] << 8)) & mask) | bank); setchr4(0x1000, ((chrlow[chr[1]] | (chrhigh[chr[1]] << 8)) & mask) | bank); break; case 0x10: /* 2KB */ for (x = 0; x < 8; x += 2) setchr2(x << 10, ((chrlow[x] | (chrhigh[x] << 8)) & mask) | bank); break; case 0x18: /* 1KB */ for (x = 0; x < 8; x++) setchr1(x << 10, ((chrlow[x] | (chrhigh[x] << 8)) & mask) | bank); break; } } static DECLFW(M90TekWrite) { switch (A & 0x5C03) { case 0x5800: mul[0] = V; break; case 0x5801: mul[1] = V; break; case 0x5803: regie = V; break; } } static DECLFR(M90TekRead) { switch (A & 0x5C03) { case 0x5800: return(mul[0] * mul[1]); case 0x5801: return((mul[0] * mul[1]) >> 8); case 0x5803: return(regie); default: return tekker; } return(0xff); } static DECLFW(M90PRGWrite) { /* FCEU_printf("bs %04x %02x\n",A,V); */ prgb[A & 3] = V; tekprom(); } static DECLFW(M90CHRlowWrite) { /* FCEU_printf("bs %04x %02x\n",A,V); */ chrlow[A & 7] = V; tekvrom(); } static DECLFW(M90CHRhiWrite) { /* FCEU_printf("bs %04x %02x\n",A,V); */ chrhigh[A & 7] = V; tekvrom(); } static DECLFW(M90NTWrite) { /* FCEU_printf("bs %04x %02x\n",A,V); */ if (A & 4) { names[A & 3] &= 0x00FF; names[A & 3] |= V << 8; } else { names[A & 3] &= 0xFF00; names[A & 3] |= V; } mira(); } static DECLFW(M90IRQWrite) { /* FCEU_printf("bs %04x %02x\n",A,V); */ switch (A & 7) { case 00: /* FCEU_printf("%s IRQ (C000)\n",V&1?"Enable":"Disable"); */ IRQa = V & 1; if (!(V & 1)) X6502_IRQEnd(FCEU_IQEXT); break; case 02: /* FCEU_printf("Disable IRQ (C002) scanline=%d\n", scanline); */ IRQa = 0; X6502_IRQEnd(FCEU_IQEXT); break; case 03: /* FCEU_printf("Enable IRQ (C003) scanline=%d\n", scanline); */ IRQa = 1; break; case 01: IRQMode = V; #if 0 FCEU_printf("IRQ Count method: "); switch (IRQMode&3) { case 00: FCEU_printf("M2 cycles\n");break; case 01: FCEU_printf("PPU A12 toggles\n");break; case 02: FCEU_printf("PPU reads\n");break; case 03: FCEU_printf("Writes to CPU space\n");break; } FCEU_printf("Counter prescaler size: %s\n",(IRQMode&4)?"3 bits":"8 bits"); FCEU_printf("Counter prescaler size adjust: %s\n",(IRQMode&8)?"Used C007":"Normal Operation"); if((IRQMode>>6)==2) FCEU_printf("Counter Down\n"); else if((IRQMode>>6)==1) FCEU_printf("Counter Up\n"); else FCEU_printf("Counter Stopped\n"); #endif break; case 04: /* FCEU_printf("Pre Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR); */ IRQPre = V ^ IRQXOR; break; case 05: /* FCEU_printf("Main Counter Loaded and Xored wiht C006: %d\n",V^IRQXOR); */ IRQCount = V ^ IRQXOR; break; case 06: /* FCEU_printf("Xor Value: %d\n",V); */ IRQXOR = V; break; case 07: #if 0 if(!(IRQMode&8)) FCEU_printf("C001 is clear, no effect applied\n"); else if(V==0xFF) FCEU_printf("Prescaler is changed for 12bits\n"); else FCEU_printf("Counter Stopped\n"); #endif IRQPreSize = V; break; } } static DECLFW(M90ModeWrite) { /* FCEU_printf("bs %04x %02x\n",A,V); */ tkcom[A & 3] = V; tekprom(); tekvrom(); mira(); #ifdef DEBUG90 switch (A & 3) { case 00: FCEU_printf("Main Control Register:\n"); FCEU_printf(" PGR Banking mode: %d\n", V & 7); FCEU_printf(" CHR Banking mode: %d\n", (V >> 3) & 3); FCEU_printf(" 6000-7FFF addresses mapping: %s\n", (V & 0x80) ? "Yes" : "No"); FCEU_printf(" Nametable control: %s\n", (V & 0x20) ? "Enabled" : "Disabled"); if (V & 0x20) FCEU_printf(" Nametable can be: %s\n", (V & 0x40) ? "ROM Only" : "RAM or ROM"); break; case 01: FCEU_printf("Mirroring mode: "); switch (V & 3) { case 0: FCEU_printf("Vertical\n"); break; case 1: FCEU_printf("Horizontal\n"); break; case 2: FCEU_printf("Nametable 0 only\n"); break; case 3: FCEU_printf("Nametable 1 only\n"); break; } FCEU_printf("Mirroring flag: %s\n", (V & 0x80) ? "On" : "Off"); break; case 02: if ((((tkcom[0]) >> 5) & 3) == 1) FCEU_printf("Nametable ROM/RAM select mode: %d\n", V >> 7); break; case 03: FCEU_printf("CHR Banking mode: %s\n", (V & 0x20) ? "Entire CHR ROM" : "256Kb Switching mode"); if (!(V & 0x20)) FCEU_printf("256K CHR bank number: %02x\n", (V & 1) | ((V & 0x18) >> 2)); FCEU_printf("512K PRG bank number: %d\n", (V & 6) >> 1); FCEU_printf("CHR Bank mirroring: %s\n", (V & 0x80) ? "Swapped" : "Normal operate"); } #endif } static DECLFW(M90DummyWrite) { /* FCEU_printf("bs %04x %02x\n",A,V); */ } static void CCL(void) { if ((IRQMode >> 6) == 1) { /* Count Up */ IRQCount++; if ((IRQCount == 0) && IRQa) { X6502_IRQBegin(FCEU_IQEXT); } } else if ((IRQMode >> 6) == 2) { /* Count down */ IRQCount--; if ((IRQCount == 0xFF) && IRQa) { X6502_IRQBegin(FCEU_IQEXT); } } } static void ClockCounter(void) { uint8 premask; if (IRQMode & 0x4) premask = 0x7; else premask = 0xFF; if ((IRQMode >> 6) == 1) { /* Count up */ IRQPre++; if ((IRQPre & premask) == 0) CCL(); } else if ((IRQMode >> 6) == 2) { /* Count down */ IRQPre--; if ((IRQPre & premask) == premask) CCL(); } } void FP_FASTAPASS(1) CPUWrap(int a) { int x; if ((IRQMode & 3) == 0) for (x = 0; x < a; x++) ClockCounter(); } static void SLWrap(void) { int x; if ((IRQMode & 3) == 1) for (x = 0; x < 8; x++) ClockCounter(); } static uint32 lastread; static void FP_FASTAPASS(1) M90PPU(uint32 A) { if ((IRQMode & 3) == 2) { if (lastread != A) { ClockCounter(); ClockCounter(); } lastread = A; } if (is209) { uint8 l, h; h = A >> 8; if (h < 0x20 && ((h & 0x0F) == 0xF)) { l = A & 0xF0; if (l == 0xD0) { chr[(h & 0x10) >> 4] = ((h & 0x10) >> 2); tekvrom(); } else if (l == 0xE0) { chr[(h & 0x10) >> 4] = ((h & 0x10) >> 2) | 2; tekvrom(); } } } else { chr[0] = 0; chr[1] = 4; } } static void togglie(void) { tekker += 0x40; tekker &= 0xC0; FCEU_printf("tekker=%02x\n", tekker); memset(tkcom, 0x00, sizeof(tkcom)); memset(prgb, 0xff, sizeof(prgb)); tekprom(); tekvrom(); } static void M90Restore(int version) { tekprom(); tekvrom(); mira(); } static void M90Power(void) { SetWriteHandler(0x5000, 0x5fff, M90TekWrite); SetWriteHandler(0x8000, 0x8ff0, M90PRGWrite); SetWriteHandler(0x9000, 0x9fff, M90CHRlowWrite); SetWriteHandler(0xA000, 0xAfff, M90CHRhiWrite); SetWriteHandler(0xB000, 0xBfff, M90NTWrite); SetWriteHandler(0xC000, 0xCfff, M90IRQWrite); SetWriteHandler(0xD000, 0xD5ff, M90ModeWrite); SetWriteHandler(0xE000, 0xFfff, M90DummyWrite); SetReadHandler(0x5000, 0x5fff, M90TekRead); SetReadHandler(0x6000, 0xffff, CartBR); mul[0] = mul[1] = regie = 0xFF; memset(tkcom, 0x00, sizeof(tkcom)); memset(prgb, 0xff, sizeof(prgb)); memset(chrlow, 0xff, sizeof(chrlow)); memset(chrhigh, 0xff, sizeof(chrhigh)); memset(names, 0x00, sizeof(names)); if (is211) tekker = 0xC0; else tekker = 0x00; tekprom(); tekvrom(); } void Mapper90_Init(CartInfo *info) { is211 = 0; is209 = 0; info->Reset = togglie; info->Power = M90Power; PPU_hook = M90PPU; MapIRQHook = CPUWrap; GameHBIRQHook2 = SLWrap; GameStateRestore = M90Restore; AddExState(Tek_StateRegs, ~0, 0, 0); } void Mapper209_Init(CartInfo *info) { is211 = 0; is209 = 1; info->Reset = togglie; info->Power = M90Power; PPU_hook = M90PPU; MapIRQHook = CPUWrap; GameHBIRQHook2 = SLWrap; GameStateRestore = M90Restore; AddExState(Tek_StateRegs, ~0, 0, 0); } void Mapper211_Init(CartInfo *info) { is211 = 1; info->Reset = togglie; info->Power = M90Power; PPU_hook = M90PPU; MapIRQHook = CPUWrap; GameHBIRQHook2 = SLWrap; GameStateRestore = M90Restore; AddExState(Tek_StateRegs, ~0, 0, 0); }