mirror of
https://github.com/dborth/fceugx.git
synced 2024-11-01 15:05:05 +01:00
859 lines
23 KiB
C++
859 lines
23 KiB
C++
/* FCE Ultra - NES/Famicom Emulator
|
|
*
|
|
* Copyright notice for this file:
|
|
* Copyright (C) 2002 Xodnizel
|
|
*
|
|
* 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
|
|
*/
|
|
|
|
/* None of this code should use any of the iNES bank switching wrappers. */
|
|
|
|
#include "mapinc.h"
|
|
|
|
static void (*sfun)(int P);
|
|
static void (*psfun)(void);
|
|
|
|
void MMC5RunSound(int Count);
|
|
void MMC5RunSoundHQ(void);
|
|
|
|
static INLINE void MMC5SPRVROM_BANK1(uint32 A, uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask1[0];
|
|
MMC5SPRVPage[(A) >> 10] = &CHRptr[0][(V) << 10] - (A);
|
|
}
|
|
}
|
|
|
|
static INLINE void MMC5BGVROM_BANK1(uint32 A, uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask1[0]; MMC5BGVPage[(A) >> 10] = &CHRptr[0][(V) << 10] - (A);
|
|
}
|
|
}
|
|
|
|
static INLINE void MMC5SPRVROM_BANK2(uint32 A, uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask2[0]; MMC5SPRVPage[(A) >> 10] = MMC5SPRVPage[((A) >> 10) + 1] = &CHRptr[0][(V) << 11] - (A);
|
|
}
|
|
}
|
|
static INLINE void MMC5BGVROM_BANK2(uint32 A, uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask2[0]; MMC5BGVPage[(A) >> 10] = MMC5BGVPage[((A) >> 10) + 1] = &CHRptr[0][(V) << 11] - (A);
|
|
}
|
|
}
|
|
|
|
static INLINE void MMC5SPRVROM_BANK4(uint32 A, uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask4[0]; MMC5SPRVPage[(A) >> 10] = MMC5SPRVPage[((A) >> 10) + 1] = MMC5SPRVPage[((A) >> 10) + 2] = MMC5SPRVPage[((A) >> 10) + 3] = &CHRptr[0][(V) << 12] - (A);
|
|
}
|
|
}
|
|
static INLINE void MMC5BGVROM_BANK4(uint32 A, uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask4[0]; MMC5BGVPage[(A) >> 10] = MMC5BGVPage[((A) >> 10) + 1] = MMC5BGVPage[((A) >> 10) + 2] = MMC5BGVPage[((A) >> 10) + 3] = &CHRptr[0][(V) << 12] - (A);
|
|
}
|
|
}
|
|
|
|
static INLINE void MMC5SPRVROM_BANK8(uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask8[0]; MMC5SPRVPage[0] = MMC5SPRVPage[1] = MMC5SPRVPage[2] = MMC5SPRVPage[3] = MMC5SPRVPage[4] = MMC5SPRVPage[5] = MMC5SPRVPage[6] = MMC5SPRVPage[7] = &CHRptr[0][(V) << 13];
|
|
}
|
|
}
|
|
static INLINE void MMC5BGVROM_BANK8(uint32 V) {
|
|
if (CHRptr[0]) {
|
|
V &= CHRmask8[0]; MMC5BGVPage[0] = MMC5BGVPage[1] = MMC5BGVPage[2] = MMC5BGVPage[3] = MMC5BGVPage[4] = MMC5BGVPage[5] = MMC5BGVPage[6] = MMC5BGVPage[7] = &CHRptr[0][(V) << 13];
|
|
}
|
|
}
|
|
|
|
static uint8 PRGBanks[4];
|
|
static uint8 WRAMPage;
|
|
static uint16 CHRBanksA[8], CHRBanksB[4];
|
|
static uint8 WRAMMaskEnable[2];
|
|
uint8 mmc5ABMode; /* A=0, B=1 */
|
|
|
|
static uint8 IRQScanline, IRQEnable;
|
|
static uint8 CHRMode, NTAMirroring, NTFill, ATFill;
|
|
|
|
static uint8 MMC5IRQR;
|
|
static uint8 MMC5LineCounter;
|
|
static uint8 mmc5psize, mmc5vsize;
|
|
static uint8 mul[2];
|
|
|
|
static uint8 *WRAM = NULL;
|
|
static uint8 *MMC5fill = NULL;
|
|
static uint8 *ExRAM = NULL;
|
|
|
|
static uint8 MMC5WRAMsize;
|
|
static uint8 MMC5WRAMIndex[8];
|
|
|
|
static uint8 MMC5ROMWrProtect[4];
|
|
static uint8 MMC5MemIn[5];
|
|
|
|
static void MMC5CHRA(void);
|
|
static void MMC5CHRB(void);
|
|
|
|
typedef struct __cartdata {
|
|
uint32 crc32;
|
|
uint8 size;
|
|
} cartdata;
|
|
|
|
#define Sprite16 (PPU[0]& 0x20) //Sprites 8x16/8x8
|
|
//#define MMC5SPRVRAMADR(V) &MMC5SPRVPage[(V)>>10][(V)]
|
|
static inline uint8 * MMC5BGVRAMADR(uint32 A) {
|
|
if (!Sprite16) {
|
|
if (mmc5ABMode == 0)
|
|
return &MMC5SPRVPage[(A) >> 10][(A)];
|
|
else
|
|
return &MMC5BGVPage[(A) >> 10][(A)];
|
|
} else return &MMC5BGVPage[(A) >> 10][(A)];
|
|
}
|
|
|
|
static void mmc5_PPUWrite(uint32 A, uint8 V) {
|
|
uint32 tmp = A;
|
|
extern uint8 PALRAM[0x20];
|
|
|
|
if (tmp >= 0x3F00) {
|
|
// hmmm....
|
|
if (!(tmp & 0xf))
|
|
PALRAM[0x00] = PALRAM[0x04] = PALRAM[0x08] = PALRAM[0x0C] = V & 0x3F;
|
|
else if (tmp & 3) PALRAM[(tmp & 0x1f)] = V & 0x3f;
|
|
} else if (tmp < 0x2000) {
|
|
if (PPUCHRRAM & (1 << (tmp >> 10)))
|
|
VPage[tmp >> 10][tmp] = V;
|
|
} else {
|
|
if (PPUNTARAM & (1 << ((tmp & 0xF00) >> 10)))
|
|
vnapage[((tmp & 0xF00) >> 10)][tmp & 0x3FF] = V;
|
|
}
|
|
}
|
|
|
|
uint8 FASTCALL mmc5_PPURead(uint32 A) {
|
|
if (A < 0x2000) {
|
|
if (ppuphase == PPUPHASE_BG)
|
|
return *MMC5BGVRAMADR(A);
|
|
else return MMC5SPRVPage[(A) >> 10][(A)];
|
|
} else {
|
|
return vnapage[(A >> 10) & 0x3][A & 0x3FF];
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// ELROM seems to have 8KB of RAM
|
|
// ETROM seems to have 16KB of WRAM
|
|
// EWROM seems to have 32KB of WRAM
|
|
|
|
cartdata MMC5CartList[] =
|
|
{
|
|
{ 0x9c18762b, 2 }, /* L'Empereur */
|
|
{ 0x26533405, 2 },
|
|
{ 0x6396b988, 2 },
|
|
{ 0xaca15643, 2 }, /* Uncharted Waters */
|
|
{ 0xfe3488d1, 2 }, /* Dai Koukai Jidai */
|
|
{ 0x15fe6d0f, 2 }, /* BKAC */
|
|
{ 0x39f2ce4b, 2 }, /* Suikoden */
|
|
{ 0x8ce478db, 2 }, /* Nobunaga's Ambition 2 */
|
|
{ 0xeee9a682, 2 },
|
|
{ 0xf9b4240f, 2 },
|
|
{ 0x1ced086f, 2 }, /* Ishin no Arashi */
|
|
{ 0xf540677b, 4 }, /* Nobunaga...Bushou Fuuun Roku */
|
|
{ 0x6f4e4312, 4 }, /* Aoki Ookami..Genchou */
|
|
{ 0xf011e490, 4 }, /* Romance of the 3 Kingdoms 2 */
|
|
{ 0x184c2124, 4 }, /* Sangokushi 2 */
|
|
{ 0xee8e6553, 4 },
|
|
};
|
|
|
|
#define MMC5_NOCARTS (sizeof(MMC5CartList) / sizeof(MMC5CartList[0]))
|
|
int DetectMMC5WRAMSize(uint32 crc32) {
|
|
int x;
|
|
for (x = 0; x < MMC5_NOCARTS; x++) {
|
|
if (crc32 == MMC5CartList[x].crc32) {
|
|
FCEU_printf(" >8KB external WRAM present. Use UNIF if you hack the ROM image.\n");
|
|
return(MMC5CartList[x].size * 8);
|
|
}
|
|
}
|
|
|
|
//mbg 04-aug-08 - previously, this was returning 8KB
|
|
//but I changed it to return 64 because unlisted carts are probably homebrews, and they should probably use 64 (why not use it all?)
|
|
//ch4 10-dec-08 - then f***ng for what all this shit above? let's give em all this 64k shit! Damn
|
|
// homebrew must use it's own emulators or standart features.
|
|
//adelikat 20-dec-08 - reverting back to return 64, sounds like it was changed back to 8 simply on principle. FCEUX is all encompassing, and that include
|
|
//rom-hacking. We want it to be the best emulator for such purposes. So unless return 64 harms compatibility with anything else, I see now reason not to have it
|
|
//mbg 29-mar-09 - I should note that mmc5 is in principle capable of 64KB, even if no real carts ever supported it.
|
|
//This does not in principle break any games which share this mapper, and it should be OK for homebrew.
|
|
//if there are games which need 8KB instead of 64KB default then lets add them to the list
|
|
return 64;
|
|
}
|
|
|
|
static void BuildWRAMSizeTable(void) {
|
|
int x;
|
|
for (x = 0; x < 8; x++) {
|
|
switch (MMC5WRAMsize) {
|
|
case 0: MMC5WRAMIndex[x] = 255; break; //X,X,X,X,X,X,X,X
|
|
case 1: MMC5WRAMIndex[x] = (x > 3) ? 255 : 0; break; //0,0,0,0,X,X,X,X
|
|
case 2: MMC5WRAMIndex[x] = (x & 4) >> 2; break; //0,0,0,0,1,1,1,1
|
|
case 4: MMC5WRAMIndex[x] = (x > 3) ? 255 : (x & 3); break; //0,1,2,3,X,X,X,X
|
|
case 8: MMC5WRAMIndex[x] = x; break; //0,1,2,3,4,5,6,7,8
|
|
//mbg 8/6/08 - i added this to support 64KB of wram
|
|
//now, I have at least one example (laser invasion) which actually uses size 1 but isnt in the crc list
|
|
//so, whereas before my change on 8/4/08 we would have selected size 1, now we select size 8
|
|
//this means that we could have just introduced an emulation bug, in case those games happened to
|
|
//address, say, page 3. with size 1 that would resolve to [0] but in size 8 it resolves to [3].
|
|
//so, you know what to do if there are problems.
|
|
}
|
|
}
|
|
}
|
|
|
|
static void MMC5CHRA(void) {
|
|
int x;
|
|
switch (mmc5vsize & 3) {
|
|
case 0:
|
|
setchr8(CHRBanksA[7]);
|
|
MMC5SPRVROM_BANK8(CHRBanksA[7]);
|
|
break;
|
|
case 1:
|
|
setchr4(0x0000, CHRBanksA[3]);
|
|
setchr4(0x1000, CHRBanksA[7]);
|
|
MMC5SPRVROM_BANK4(0x0000, CHRBanksA[3]);
|
|
MMC5SPRVROM_BANK4(0x1000, CHRBanksA[7]);
|
|
break;
|
|
case 2:
|
|
setchr2(0x0000, CHRBanksA[1]);
|
|
setchr2(0x0800, CHRBanksA[3]);
|
|
setchr2(0x1000, CHRBanksA[5]);
|
|
setchr2(0x1800, CHRBanksA[7]);
|
|
MMC5SPRVROM_BANK2(0x0000, CHRBanksA[1]);
|
|
MMC5SPRVROM_BANK2(0x0800, CHRBanksA[3]);
|
|
MMC5SPRVROM_BANK2(0x1000, CHRBanksA[5]);
|
|
MMC5SPRVROM_BANK2(0x1800, CHRBanksA[7]);
|
|
break;
|
|
case 3:
|
|
for (x = 0; x < 8; x++) {
|
|
setchr1(x << 10, CHRBanksA[x]);
|
|
MMC5SPRVROM_BANK1(x << 10, CHRBanksA[x]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void MMC5CHRB(void) {
|
|
int x;
|
|
switch (mmc5vsize & 3) {
|
|
case 0:
|
|
setchr8(CHRBanksB[3]);
|
|
MMC5BGVROM_BANK8(CHRBanksB[3]);
|
|
break;
|
|
case 1:
|
|
setchr4(0x0000, CHRBanksB[3]);
|
|
setchr4(0x1000, CHRBanksB[3]);
|
|
MMC5BGVROM_BANK4(0x0000, CHRBanksB[3]);
|
|
MMC5BGVROM_BANK4(0x1000, CHRBanksB[3]);
|
|
break;
|
|
case 2:
|
|
setchr2(0x0000, CHRBanksB[1]);
|
|
setchr2(0x0800, CHRBanksB[3]);
|
|
setchr2(0x1000, CHRBanksB[1]);
|
|
setchr2(0x1800, CHRBanksB[3]);
|
|
MMC5BGVROM_BANK2(0x0000, CHRBanksB[1]);
|
|
MMC5BGVROM_BANK2(0x0800, CHRBanksB[3]);
|
|
MMC5BGVROM_BANK2(0x1000, CHRBanksB[1]);
|
|
MMC5BGVROM_BANK2(0x1800, CHRBanksB[3]);
|
|
break;
|
|
case 3:
|
|
for (x = 0; x < 8; x++) {
|
|
setchr1(x << 10, CHRBanksB[x & 3]);
|
|
MMC5BGVROM_BANK1(x << 10, CHRBanksB[x & 3]);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void MMC5WRAM(uint32 A, uint32 V) {
|
|
//printf("%02x\n",V);
|
|
V = MMC5WRAMIndex[V & 7];
|
|
if (V != 255) {
|
|
setprg8r(0x10, A, V);
|
|
MMC5MemIn[(A - 0x6000) >> 13] = 1;
|
|
} else
|
|
MMC5MemIn[(A - 0x6000) >> 13] = 0;
|
|
}
|
|
|
|
static void MMC5PRG(void) {
|
|
int x;
|
|
switch (mmc5psize & 3) {
|
|
case 0:
|
|
MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = MMC5ROMWrProtect[2] = MMC5ROMWrProtect[3] = 1;
|
|
setprg32(0x8000, ((PRGBanks[1] & 0x7F) >> 2));
|
|
for (x = 0; x < 4; x++)
|
|
MMC5MemIn[1 + x] = 1;
|
|
break;
|
|
case 1:
|
|
if (PRGBanks[1] & 0x80) {
|
|
MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 1;
|
|
setprg16(0x8000, (PRGBanks[1] >> 1));
|
|
MMC5MemIn[1] = MMC5MemIn[2] = 1;
|
|
} else {
|
|
MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 0;
|
|
MMC5WRAM(0x8000, PRGBanks[1] & 7 & 0xFE);
|
|
MMC5WRAM(0xA000, (PRGBanks[1] & 7 & 0xFE) + 1);
|
|
}
|
|
MMC5MemIn[3] = MMC5MemIn[4] = 1;
|
|
MMC5ROMWrProtect[2] = MMC5ROMWrProtect[3] = 1;
|
|
setprg16(0xC000, (PRGBanks[3] & 0x7F) >> 1);
|
|
break;
|
|
case 2:
|
|
if (PRGBanks[1] & 0x80) {
|
|
MMC5MemIn[1] = MMC5MemIn[2] = 1;
|
|
MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 1;
|
|
setprg16(0x8000, (PRGBanks[1] & 0x7F) >> 1);
|
|
} else {
|
|
MMC5ROMWrProtect[0] = MMC5ROMWrProtect[1] = 0;
|
|
MMC5WRAM(0x8000, PRGBanks[1] & 7 & 0xFE);
|
|
MMC5WRAM(0xA000, (PRGBanks[1] & 7 & 0xFE) + 1);
|
|
}
|
|
if (PRGBanks[2] & 0x80) {
|
|
MMC5ROMWrProtect[2] = 1;
|
|
MMC5MemIn[3] = 1;
|
|
setprg8(0xC000, PRGBanks[2] & 0x7F);
|
|
} else {
|
|
MMC5ROMWrProtect[2] = 0;
|
|
MMC5WRAM(0xC000, PRGBanks[2] & 7);
|
|
}
|
|
MMC5MemIn[4] = 1;
|
|
MMC5ROMWrProtect[3] = 1;
|
|
setprg8(0xE000, PRGBanks[3] & 0x7F);
|
|
break;
|
|
case 3:
|
|
for (x = 0; x < 3; x++)
|
|
if (PRGBanks[x] & 0x80) {
|
|
MMC5ROMWrProtect[x] = 1;
|
|
setprg8(0x8000 + (x << 13), PRGBanks[x] & 0x7F);
|
|
MMC5MemIn[1 + x] = 1;
|
|
} else {
|
|
MMC5ROMWrProtect[x] = 0;
|
|
MMC5WRAM(0x8000 + (x << 13), PRGBanks[x] & 7);
|
|
}
|
|
MMC5MemIn[4] = 1;
|
|
MMC5ROMWrProtect[3] = 1;
|
|
setprg8(0xE000, PRGBanks[3] & 0x7F);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static DECLFW(Mapper5_write) {
|
|
if (A >= 0x5120 && A <= 0x5127) {
|
|
mmc5ABMode = 0;
|
|
CHRBanksA[A & 7] = V | ((MMC50x5130 & 0x3) << 8); //if we had a test case for this then we could test this, but it hasnt been verified
|
|
//CHRBanksA[A&7]=V;
|
|
MMC5CHRA();
|
|
} else switch (A) {
|
|
case 0x5105:
|
|
{
|
|
int x;
|
|
for (x = 0; x < 4; x++) {
|
|
switch ((V >> (x << 1)) & 3) {
|
|
case 0: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM; break;
|
|
case 1: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM + 0x400; break;
|
|
case 2: PPUNTARAM |= 1 << x; vnapage[x] = ExRAM; break;
|
|
case 3: PPUNTARAM &= ~(1 << x); vnapage[x] = MMC5fill; break;
|
|
}
|
|
}
|
|
NTAMirroring = V;
|
|
break;
|
|
}
|
|
case 0x5113: WRAMPage = V; MMC5WRAM(0x6000, V & 7); break;
|
|
case 0x5100: mmc5psize = V; MMC5PRG(); break;
|
|
case 0x5101:
|
|
mmc5vsize = V;
|
|
if (!mmc5ABMode) {
|
|
MMC5CHRB();
|
|
MMC5CHRA();
|
|
} else {
|
|
MMC5CHRA();
|
|
MMC5CHRB();
|
|
}
|
|
break;
|
|
case 0x5114:
|
|
case 0x5115:
|
|
case 0x5116:
|
|
case 0x5117: PRGBanks[A & 3] = V; MMC5PRG(); break;
|
|
case 0x5128:
|
|
case 0x5129:
|
|
case 0x512a:
|
|
case 0x512b:
|
|
mmc5ABMode = 1;
|
|
CHRBanksB[A & 3] = V;
|
|
MMC5CHRB();
|
|
break;
|
|
case 0x5102: WRAMMaskEnable[0] = V; break;
|
|
case 0x5103: WRAMMaskEnable[1] = V; break;
|
|
case 0x5104: CHRMode = V; MMC5HackCHRMode = V & 3; break;
|
|
case 0x5106:
|
|
if (V != NTFill) {
|
|
uint32 t;
|
|
t = V | (V << 8) | (V << 16) | (V << 24);
|
|
FCEU_dwmemset(MMC5fill, t, 0x3c0);
|
|
}
|
|
NTFill = V;
|
|
break;
|
|
case 0x5107:
|
|
if (V != ATFill) {
|
|
unsigned char moop;
|
|
uint32 t;
|
|
moop = V | (V << 2) | (V << 4) | (V << 6);
|
|
t = moop | (moop << 8) | (moop << 16) | (moop << 24);
|
|
FCEU_dwmemset(MMC5fill + 0x3c0, t, 0x40);
|
|
}
|
|
ATFill = V;
|
|
break;
|
|
case 0x5130: MMC50x5130 = V; break;
|
|
|
|
case 0x5200: MMC5HackSPMode = V; break;
|
|
case 0x5201: MMC5HackSPScroll = (V >> 3) & 0x1F; break;
|
|
case 0x5202: MMC5HackSPPage = V & 0x3F; break;
|
|
case 0x5203: X6502_IRQEnd(FCEU_IQEXT); IRQScanline = V; break;
|
|
case 0x5204: X6502_IRQEnd(FCEU_IQEXT); IRQEnable = V & 0x80; break;
|
|
case 0x5205: mul[0] = V; break;
|
|
case 0x5206: mul[1] = V; break;
|
|
}
|
|
}
|
|
|
|
static DECLFR(MMC5_ReadROMRAM) {
|
|
if (MMC5MemIn[(A - 0x6000) >> 13])
|
|
return Page[A >> 11][A];
|
|
else
|
|
return X.DB;
|
|
}
|
|
|
|
static DECLFW(MMC5_WriteROMRAM) {
|
|
if (A >= 0x8000)
|
|
if (MMC5ROMWrProtect[(A - 0x8000) >> 13]) return;
|
|
if (MMC5MemIn[(A - 0x6000) >> 13])
|
|
if (((WRAMMaskEnable[0] & 3) | ((WRAMMaskEnable[1] & 3) << 2)) == 6) Page[A >> 11][A] = V;
|
|
}
|
|
|
|
static DECLFW(MMC5_ExRAMWr) {
|
|
if (MMC5HackCHRMode != 3)
|
|
ExRAM[A & 0x3ff] = V;
|
|
}
|
|
|
|
static DECLFR(MMC5_ExRAMRd) {
|
|
// Not sure if this is correct, so I'll comment it out for now.
|
|
// if(MMC5HackCHRMode>=2)
|
|
return ExRAM[A & 0x3ff];
|
|
// else
|
|
// return(X.DB);
|
|
}
|
|
|
|
static DECLFR(MMC5_read) {
|
|
switch (A) {
|
|
case 0x5204:
|
|
{
|
|
uint8 x;
|
|
X6502_IRQEnd(FCEU_IQEXT);
|
|
x = MMC5IRQR;
|
|
#ifdef FCEUDEF_DEBUGGER
|
|
if (!fceuindbg)
|
|
#endif
|
|
MMC5IRQR &= 0x40;
|
|
return x;
|
|
}
|
|
case 0x5205: return(mul[0] * mul[1]);
|
|
case 0x5206: return((mul[0] * mul[1]) >> 8);
|
|
}
|
|
return(X.DB);
|
|
}
|
|
|
|
void MMC5Synco(void) {
|
|
int x;
|
|
|
|
MMC5PRG();
|
|
for (x = 0; x < 4; x++) {
|
|
switch ((NTAMirroring >> (x << 1)) & 3) {
|
|
case 0: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM; break;
|
|
case 1: PPUNTARAM |= 1 << x; vnapage[x] = NTARAM + 0x400; break;
|
|
case 2: PPUNTARAM |= 1 << x; vnapage[x] = ExRAM; break;
|
|
case 3: PPUNTARAM &= ~(1 << x); vnapage[x] = MMC5fill; break;
|
|
}
|
|
}
|
|
MMC5WRAM(0x6000, WRAMPage & 7);
|
|
if (!mmc5ABMode) {
|
|
MMC5CHRB();
|
|
MMC5CHRA();
|
|
} else {
|
|
MMC5CHRA();
|
|
MMC5CHRB();
|
|
}
|
|
{
|
|
uint32 t;
|
|
t = NTFill | (NTFill << 8) | (NTFill << 16) | (NTFill << 24);
|
|
FCEU_dwmemset(MMC5fill, t, 0x3c0);
|
|
}
|
|
{
|
|
unsigned char moop;
|
|
uint32 t;
|
|
moop = ATFill | (ATFill << 2) | (ATFill << 4) | (ATFill << 6);
|
|
t = moop | (moop << 8) | (moop << 16) | (moop << 24);
|
|
FCEU_dwmemset(MMC5fill + 0x3c0, t, 0x40);
|
|
}
|
|
X6502_IRQEnd(FCEU_IQEXT);
|
|
MMC5HackCHRMode = CHRMode & 3;
|
|
}
|
|
|
|
void MMC5_hb(int scanline) {
|
|
if (scanline == 240) {
|
|
MMC5LineCounter = 0;
|
|
MMC5IRQR = 0x40;
|
|
return;
|
|
}
|
|
if (MMC5LineCounter < 240) {
|
|
if (MMC5LineCounter == IRQScanline) {
|
|
MMC5IRQR |= 0x80;
|
|
if (IRQEnable & 0x80)
|
|
X6502_IRQBegin(FCEU_IQEXT);
|
|
}
|
|
MMC5LineCounter++;
|
|
}
|
|
if (MMC5LineCounter == 240)
|
|
MMC5IRQR = 0;
|
|
}
|
|
|
|
void MMC5_StateRestore(int version) {
|
|
MMC5Synco();
|
|
}
|
|
|
|
typedef struct {
|
|
uint16 wl[2];
|
|
uint8 env[2];
|
|
uint8 enable;
|
|
uint8 running;
|
|
uint8 raw;
|
|
uint8 rawcontrol;
|
|
int32 dcount[2];
|
|
int32 BC[3];
|
|
int32 vcount[2];
|
|
} MMC5APU;
|
|
|
|
static MMC5APU MMC5Sound;
|
|
|
|
|
|
static void Do5PCM() {
|
|
int32 V;
|
|
int32 start, end;
|
|
|
|
start = MMC5Sound.BC[2];
|
|
end = (SOUNDTS << 16) / soundtsinc;
|
|
if (end <= start) return;
|
|
MMC5Sound.BC[2] = end;
|
|
|
|
if (!(MMC5Sound.rawcontrol & 0x40) && MMC5Sound.raw)
|
|
for (V = start; V < end; V++)
|
|
Wave[V >> 4] += MMC5Sound.raw << 1;
|
|
}
|
|
|
|
static void Do5PCMHQ() {
|
|
uint32 V; //mbg merge 7/17/06 made uint32
|
|
if (!(MMC5Sound.rawcontrol & 0x40) && MMC5Sound.raw)
|
|
for (V = MMC5Sound.BC[2]; V < SOUNDTS; V++)
|
|
WaveHi[V] += MMC5Sound.raw << 5;
|
|
MMC5Sound.BC[2] = SOUNDTS;
|
|
}
|
|
|
|
|
|
static DECLFW(Mapper5_SW) {
|
|
A &= 0x1F;
|
|
|
|
GameExpSound.Fill = MMC5RunSound;
|
|
GameExpSound.HiFill = MMC5RunSoundHQ;
|
|
|
|
switch (A) {
|
|
case 0x10: if (psfun) psfun(); MMC5Sound.rawcontrol = V; break;
|
|
case 0x11: if (psfun) psfun(); MMC5Sound.raw = V; break;
|
|
|
|
case 0x0:
|
|
case 0x4:
|
|
if (sfun) sfun(A >> 2);
|
|
MMC5Sound.env[A >> 2] = V;
|
|
break;
|
|
case 0x2:
|
|
case 0x6:
|
|
if (sfun) sfun(A >> 2);
|
|
MMC5Sound.wl[A >> 2] &= ~0x00FF;
|
|
MMC5Sound.wl[A >> 2] |= V & 0xFF;
|
|
break;
|
|
case 0x3:
|
|
case 0x7:
|
|
MMC5Sound.wl[A >> 2] &= ~0x0700;
|
|
MMC5Sound.wl[A >> 2] |= (V & 0x07) << 8;
|
|
MMC5Sound.running |= 1 << (A >> 2);
|
|
break;
|
|
case 0x15:
|
|
if (sfun) {
|
|
sfun(0);
|
|
sfun(1);
|
|
}
|
|
MMC5Sound.running &= V;
|
|
MMC5Sound.enable = V;
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void Do5SQ(int P) {
|
|
static int tal[4] = { 1, 2, 4, 6 };
|
|
int32 V, amp, rthresh, wl;
|
|
int32 start, end;
|
|
|
|
start = MMC5Sound.BC[P];
|
|
end = (SOUNDTS << 16) / soundtsinc;
|
|
if (end <= start) return;
|
|
MMC5Sound.BC[P] = end;
|
|
|
|
wl = MMC5Sound.wl[P] + 1;
|
|
amp = (MMC5Sound.env[P] & 0xF) << 4;
|
|
rthresh = tal[(MMC5Sound.env[P] & 0xC0) >> 6];
|
|
|
|
if (wl >= 8 && (MMC5Sound.running & (P + 1))) {
|
|
int dc, vc;
|
|
|
|
wl <<= 18;
|
|
dc = MMC5Sound.dcount[P];
|
|
vc = MMC5Sound.vcount[P];
|
|
|
|
for (V = start; V < end; V++) {
|
|
if (dc < rthresh)
|
|
Wave[V >> 4] += amp;
|
|
vc -= nesincsize;
|
|
while (vc <= 0) {
|
|
vc += wl;
|
|
dc = (dc + 1) & 7;
|
|
}
|
|
}
|
|
MMC5Sound.dcount[P] = dc;
|
|
MMC5Sound.vcount[P] = vc;
|
|
}
|
|
}
|
|
|
|
static void Do5SQHQ(int P) {
|
|
static int tal[4] = { 1, 2, 4, 6 };
|
|
uint32 V; //mbg merge 7/17/06 made uint32
|
|
int32 amp, rthresh, wl;
|
|
|
|
wl = MMC5Sound.wl[P] + 1;
|
|
amp = ((MMC5Sound.env[P] & 0xF) << 8);
|
|
rthresh = tal[(MMC5Sound.env[P] & 0xC0) >> 6];
|
|
|
|
if (wl >= 8 && (MMC5Sound.running & (P + 1))) {
|
|
int dc, vc;
|
|
|
|
wl <<= 1;
|
|
|
|
dc = MMC5Sound.dcount[P];
|
|
vc = MMC5Sound.vcount[P];
|
|
for (V = MMC5Sound.BC[P]; V < SOUNDTS; V++) {
|
|
if (dc < rthresh)
|
|
WaveHi[V] += amp;
|
|
vc--;
|
|
if (vc <= 0) { /* Less than zero when first started. */
|
|
vc = wl;
|
|
dc = (dc + 1) & 7;
|
|
}
|
|
}
|
|
MMC5Sound.dcount[P] = dc;
|
|
MMC5Sound.vcount[P] = vc;
|
|
}
|
|
MMC5Sound.BC[P] = SOUNDTS;
|
|
}
|
|
|
|
void MMC5RunSoundHQ(void) {
|
|
Do5SQHQ(0);
|
|
Do5SQHQ(1);
|
|
Do5PCMHQ();
|
|
}
|
|
|
|
void MMC5HiSync(int32 ts) {
|
|
int x;
|
|
for (x = 0; x < 3; x++)
|
|
MMC5Sound.BC[x] = ts;
|
|
}
|
|
|
|
void MMC5RunSound(int Count) {
|
|
int x;
|
|
Do5SQ(0);
|
|
Do5SQ(1);
|
|
Do5PCM();
|
|
for (x = 0; x < 3; x++)
|
|
MMC5Sound.BC[x] = Count;
|
|
}
|
|
|
|
void Mapper5_ESI(void) {
|
|
GameExpSound.RChange = Mapper5_ESI;
|
|
if (FSettings.SndRate) {
|
|
if (FSettings.soundq >= 1) {
|
|
sfun = Do5SQHQ;
|
|
psfun = Do5PCMHQ;
|
|
} else {
|
|
sfun = Do5SQ;
|
|
psfun = Do5PCM;
|
|
}
|
|
} else {
|
|
sfun = 0;
|
|
psfun = 0;
|
|
}
|
|
memset(MMC5Sound.BC, 0, sizeof(MMC5Sound.BC));
|
|
memset(MMC5Sound.vcount, 0, sizeof(MMC5Sound.vcount));
|
|
GameExpSound.HiSync = MMC5HiSync;
|
|
}
|
|
|
|
void NSFMMC5_Init(void) {
|
|
memset(&MMC5Sound, 0, sizeof(MMC5Sound));
|
|
mul[0] = mul[1] = 0;
|
|
ExRAM = (uint8*)FCEU_gmalloc(1024);
|
|
Mapper5_ESI();
|
|
SetWriteHandler(0x5c00, 0x5fef, MMC5_ExRAMWr);
|
|
SetReadHandler(0x5c00, 0x5fef, MMC5_ExRAMRd);
|
|
MMC5HackCHRMode = 2;
|
|
SetWriteHandler(0x5000, 0x5015, Mapper5_SW);
|
|
SetWriteHandler(0x5205, 0x5206, Mapper5_write);
|
|
SetReadHandler(0x5205, 0x5206, MMC5_read);
|
|
}
|
|
|
|
void NSFMMC5_Close(void) {
|
|
FCEU_gfree(ExRAM);
|
|
ExRAM = 0;
|
|
}
|
|
|
|
static void GenMMC5Reset(void) {
|
|
int x;
|
|
|
|
for (x = 0; x < 4; x++) PRGBanks[x] = ~0;
|
|
for (x = 0; x < 8; x++) CHRBanksA[x] = ~0;
|
|
for (x = 0; x < 4; x++) CHRBanksB[x] = ~0;
|
|
WRAMMaskEnable[0] = WRAMMaskEnable[1] = ~0;
|
|
|
|
mmc5psize = mmc5vsize = 3;
|
|
CHRMode = 0;
|
|
|
|
NTAMirroring = NTFill = ATFill = 0xFF;
|
|
|
|
MMC5Synco();
|
|
|
|
SetWriteHandler(0x4020, 0x5bff, Mapper5_write);
|
|
SetReadHandler(0x4020, 0x5bff, MMC5_read);
|
|
|
|
SetWriteHandler(0x5c00, 0x5fff, MMC5_ExRAMWr);
|
|
SetReadHandler(0x5c00, 0x5fff, MMC5_ExRAMRd);
|
|
|
|
SetWriteHandler(0x6000, 0xFFFF, MMC5_WriteROMRAM);
|
|
SetReadHandler(0x6000, 0xFFFF, MMC5_ReadROMRAM);
|
|
|
|
SetWriteHandler(0x5000, 0x5015, Mapper5_SW);
|
|
SetWriteHandler(0x5205, 0x5206, Mapper5_write);
|
|
SetReadHandler(0x5205, 0x5206, MMC5_read);
|
|
|
|
// GameHBIRQHook=MMC5_hb;
|
|
FCEU_CheatAddRAM(8, 0x6000, WRAM);
|
|
FCEU_CheatAddRAM(1, 0x5c00, ExRAM);
|
|
}
|
|
|
|
static SFORMAT MMC5_StateRegs[] = {
|
|
{ PRGBanks, 4, "PRGB" },
|
|
{ CHRBanksA, 16, "CHRA" },
|
|
{ CHRBanksB, 8, "CHRB" },
|
|
{ &WRAMPage, 1, "WRMP" },
|
|
{ WRAMMaskEnable, 2, "WRME" },
|
|
{ &mmc5ABMode, 1, "ABMD" },
|
|
{ &IRQScanline, 1, "IRQS" },
|
|
{ &IRQEnable, 1, "IRQE" },
|
|
{ &CHRMode, 1, "CHRM" },
|
|
{ &NTAMirroring, 1, "NTAM" },
|
|
{ &NTFill, 1, "NTFL" },
|
|
{ &ATFill, 1, "ATFL" },
|
|
|
|
{ &MMC5Sound.wl[0], 2 | FCEUSTATE_RLSB, "SDW0" },
|
|
{ &MMC5Sound.wl[1], 2 | FCEUSTATE_RLSB, "SDW1" },
|
|
{ MMC5Sound.env, 2, "SDEV" },
|
|
{ &MMC5Sound.enable, 1, "SDEN" },
|
|
{ &MMC5Sound.running, 1, "SDRU" },
|
|
{ &MMC5Sound.raw, 1, "SDRW" },
|
|
{ &MMC5Sound.rawcontrol, 1, "SDRC" },
|
|
{ 0 }
|
|
};
|
|
|
|
static void GenMMC5_Init(CartInfo *info, int wsize, int battery) {
|
|
if (wsize) {
|
|
WRAM = (uint8*)FCEU_gmalloc(wsize * 1024);
|
|
SetupCartPRGMapping(0x10, WRAM, wsize * 1024, 1);
|
|
AddExState(WRAM, wsize * 1024, 0, "WRAM");
|
|
}
|
|
|
|
MMC5fill = (uint8*)FCEU_gmalloc(1024);
|
|
ExRAM = (uint8*)FCEU_gmalloc(1024);
|
|
|
|
AddExState(MMC5_StateRegs, ~0, 0, 0);
|
|
AddExState(WRAM, wsize * 1024, 0, "WRAM");
|
|
AddExState(ExRAM, 1024, 0, "ERAM");
|
|
AddExState(&MMC5HackSPMode, 1, 0, "SPLM");
|
|
AddExState(&MMC5HackSPScroll, 1, 0, "SPLS");
|
|
AddExState(&MMC5HackSPPage, 1, 0, "SPLP");
|
|
AddExState(&MMC50x5130, 1, 0, "5130");
|
|
|
|
MMC5WRAMsize = wsize / 8;
|
|
BuildWRAMSizeTable();
|
|
GameStateRestore = MMC5_StateRestore;
|
|
info->Power = GenMMC5Reset;
|
|
|
|
if (battery) {
|
|
info->SaveGame[0] = WRAM;
|
|
if (wsize <= 16)
|
|
info->SaveGameLen[0] = 8192;
|
|
else
|
|
info->SaveGameLen[0] = 32768;
|
|
}
|
|
|
|
MMC5HackVROMMask = CHRmask4[0];
|
|
MMC5HackExNTARAMPtr = ExRAM;
|
|
MMC5Hack = 1;
|
|
MMC5HackVROMPTR = CHRptr[0];
|
|
MMC5HackCHRMode = 0;
|
|
MMC5HackSPMode = MMC5HackSPScroll = MMC5HackSPPage = 0;
|
|
Mapper5_ESI();
|
|
|
|
FFCEUX_PPURead = mmc5_PPURead;
|
|
FFCEUX_PPUWrite = mmc5_PPUWrite;
|
|
}
|
|
|
|
void Mapper5_Init(CartInfo *info) {
|
|
GenMMC5_Init(info, DetectMMC5WRAMSize(info->CRC32), info->battery);
|
|
}
|
|
|
|
// ELROM seems to have 0KB of WRAM
|
|
// EKROM seems to have 8KB of WRAM
|
|
// ETROM seems to have 16KB of WRAM
|
|
// EWROM seems to have 32KB of WRAM
|
|
|
|
// ETROM and EWROM are battery-backed, EKROM isn't.
|
|
|
|
void ETROM_Init(CartInfo *info) {
|
|
GenMMC5_Init(info, 16, info->battery);
|
|
}
|
|
|
|
void ELROM_Init(CartInfo *info) {
|
|
GenMMC5_Init(info, 0, 0);
|
|
}
|
|
|
|
void EWROM_Init(CartInfo *info) {
|
|
GenMMC5_Init(info, 32, info->battery);
|
|
}
|
|
|
|
void EKROM_Init(CartInfo *info) {
|
|
GenMMC5_Init(info, 8, info->battery);
|
|
}
|