479 lines
12 KiB
C
479 lines
12 KiB
C
/* 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);
|
|
}
|