/* 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 // for(x=0;x<8;x+=4) // setchr4(x<<10,((chrlow[x]|(chrhigh[x]<<8))&mask)|bank); 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; // 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"); 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(!(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"); 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 CPUWrap(int a) { int x; if((IRQMode&3)==0) for(x=0;x>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() { 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); }