mirror of
https://github.com/dborth/fceugx.git
synced 2024-11-01 15:05:05 +01:00
449 lines
9.7 KiB
C++
449 lines
9.7 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
|
|
*/
|
|
|
|
#include "mapinc.h"
|
|
|
|
static uint16 IRQCount;
|
|
static uint8 IRQa;
|
|
|
|
static uint8 WRAM[8192];
|
|
static uint8 IRAM[128];
|
|
|
|
static DECLFR(AWRAM) {
|
|
return(WRAM[A - 0x6000]);
|
|
}
|
|
|
|
static DECLFW(BWRAM) {
|
|
WRAM[A - 0x6000] = V;
|
|
}
|
|
|
|
void Mapper19_ESI(void);
|
|
|
|
static uint8 NTAPage[4];
|
|
|
|
static uint8 dopol;
|
|
static uint8 gorfus;
|
|
static uint8 gorko;
|
|
|
|
static void NamcoSound(int Count);
|
|
static void NamcoSoundHack(void);
|
|
static void DoNamcoSound(int32 *Wave, int Count);
|
|
static void DoNamcoSoundHQ(void);
|
|
static void SyncHQ(int32 ts);
|
|
|
|
static int is210; /* Lesser mapper. */
|
|
|
|
static uint8 PRG[3];
|
|
static uint8 CHR[8];
|
|
|
|
static SFORMAT N106_StateRegs[] = {
|
|
{ PRG, 3, "PRG" },
|
|
{ CHR, 8, "CHR" },
|
|
{ NTAPage, 4, "NTA" },
|
|
{ &gorfus, 1, "GORF" },
|
|
{ &dopol, 1, "DOPO" },
|
|
{ &gorko, 1, "GORK" },
|
|
{ 0 }
|
|
};
|
|
|
|
static void SyncMirror()
|
|
{
|
|
switch(gorko) {
|
|
case 0: setmirror(MI_0); break;
|
|
case 1: setmirror(MI_V); break;
|
|
case 2: setmirror(MI_H); break;
|
|
case 3: setmirror(MI_0); break;
|
|
}
|
|
|
|
}
|
|
|
|
static void SyncPRG(void) {
|
|
setprg8(0x8000, PRG[0]);
|
|
setprg8(0xa000, PRG[1]);
|
|
setprg8(0xc000, PRG[2]);
|
|
setprg8(0xe000, 0x3F);
|
|
}
|
|
|
|
static void NamcoIRQHook(int a) {
|
|
if (IRQa) {
|
|
IRQCount += a;
|
|
if (IRQCount >= 0x7FFF) {
|
|
X6502_IRQBegin(FCEU_IQEXT);
|
|
IRQa = 0;
|
|
IRQCount = 0x7FFF; //7FFF;
|
|
}
|
|
}
|
|
}
|
|
|
|
static DECLFR(Namco_Read4800) {
|
|
uint8 ret = IRAM[dopol & 0x7f];
|
|
/* Maybe I should call NamcoSoundHack() here? */
|
|
#ifdef FCEUDEF_DEBUGGER
|
|
if (!fceuindbg)
|
|
#endif
|
|
if (dopol & 0x80)
|
|
dopol = (dopol & 0x80) | ((dopol + 1) & 0x7f);
|
|
return ret;
|
|
}
|
|
|
|
static DECLFR(Namco_Read5000) {
|
|
return(IRQCount);
|
|
}
|
|
|
|
static DECLFR(Namco_Read5800) {
|
|
return(IRQCount >> 8);
|
|
}
|
|
|
|
static void DoNTARAMROM(int w, uint8 V) {
|
|
NTAPage[w] = V;
|
|
if (V >= 0xE0)
|
|
setntamem(NTARAM + ((V & 1) << 10), 1, w);
|
|
else{
|
|
V &= CHRmask1[0];
|
|
setntamem(CHRptr[0] + (V << 10), 0, w);
|
|
}
|
|
}
|
|
|
|
static void FixNTAR(void) {
|
|
int x;
|
|
for (x = 0; x < 4; x++)
|
|
DoNTARAMROM(x, NTAPage[x]);
|
|
}
|
|
|
|
static void DoCHRRAMROM(int x, uint8 V) {
|
|
CHR[x] = V;
|
|
if (!is210 && !((gorfus >> ((x >> 2) + 6)) & 1) && (V >= 0xE0)) {
|
|
// printf("BLAHAHA: %d, %02x\n",x,V);
|
|
// setchr1r(0x10,x<<10,V&7);
|
|
} else
|
|
setchr1(x << 10, V);
|
|
}
|
|
|
|
static void FixCRR(void) {
|
|
int x;
|
|
for (x = 0; x < 8; x++)
|
|
DoCHRRAMROM(x, CHR[x]);
|
|
}
|
|
|
|
static DECLFW(Mapper19C0D8_write) {
|
|
DoNTARAMROM((A - 0xC000) >> 11, V);
|
|
}
|
|
|
|
static uint32 FreqCache[8];
|
|
static uint32 EnvCache[8];
|
|
static uint32 LengthCache[8];
|
|
|
|
static void FixCache(int a, int V) {
|
|
int w = (a >> 3) & 0x7;
|
|
switch (a & 0x07) {
|
|
case 0x00: FreqCache[w] &= ~0x000000FF; FreqCache[w] |= V; break;
|
|
case 0x02: FreqCache[w] &= ~0x0000FF00; FreqCache[w] |= V << 8; break;
|
|
case 0x04:
|
|
FreqCache[w] &= ~0x00030000; FreqCache[w] |= (V & 3) << 16;
|
|
LengthCache[w] = (8 - ((V >> 2) & 7)) << 2;
|
|
break;
|
|
case 0x07: EnvCache[w] = (double)(V & 0xF) * 576716; break;
|
|
}
|
|
}
|
|
|
|
static DECLFW(Mapper19_write) {
|
|
A &= 0xF800;
|
|
if (A >= 0x8000 && A <= 0xb800)
|
|
DoCHRRAMROM((A - 0x8000) >> 11, V);
|
|
else
|
|
switch (A) {
|
|
case 0x4800:
|
|
if (dopol & 0x40) {
|
|
if (FSettings.SndRate) {
|
|
NamcoSoundHack();
|
|
GameExpSound.Fill = NamcoSound;
|
|
GameExpSound.HiFill = DoNamcoSoundHQ;
|
|
GameExpSound.HiSync = SyncHQ;
|
|
}
|
|
FixCache(dopol, V);
|
|
}
|
|
IRAM[dopol & 0x7f] = V;
|
|
if (dopol & 0x80)
|
|
dopol = (dopol & 0x80) | ((dopol + 1) & 0x7f);
|
|
break;
|
|
case 0xf800:
|
|
dopol = V; break;
|
|
case 0x5000:
|
|
IRQCount &= 0xFF00; IRQCount |= V; X6502_IRQEnd(FCEU_IQEXT); break;
|
|
case 0x5800:
|
|
IRQCount &= 0x00ff; IRQCount |= (V & 0x7F) << 8;
|
|
IRQa = V & 0x80;
|
|
X6502_IRQEnd(FCEU_IQEXT);
|
|
break;
|
|
case 0xE000:
|
|
PRG[0] = V & 0x3F;
|
|
if(is210) {
|
|
gorko = V>>6;
|
|
SyncMirror();
|
|
}
|
|
SyncPRG();
|
|
break;
|
|
case 0xE800:
|
|
gorfus = V & 0xC0;
|
|
FixCRR();
|
|
PRG[1] = V & 0x3F;
|
|
SyncPRG();
|
|
break;
|
|
case 0xF000:
|
|
PRG[2] = V & 0x3F;
|
|
SyncPRG();
|
|
break;
|
|
}
|
|
}
|
|
|
|
static int dwave = 0;
|
|
|
|
static void NamcoSoundHack(void) {
|
|
int32 z, a;
|
|
if (FSettings.soundq >= 1) {
|
|
DoNamcoSoundHQ();
|
|
return;
|
|
}
|
|
z = ((SOUNDTS << 16) / soundtsinc) >> 4;
|
|
a = z - dwave;
|
|
if (a) DoNamcoSound(&Wave[dwave], a);
|
|
dwave += a;
|
|
}
|
|
|
|
static void NamcoSound(int Count) {
|
|
int32 z, a;
|
|
z = ((SOUNDTS << 16) / soundtsinc) >> 4;
|
|
a = z - dwave;
|
|
if (a) DoNamcoSound(&Wave[dwave], a);
|
|
dwave = 0;
|
|
}
|
|
|
|
static uint32 PlayIndex[8];
|
|
static int32 vcount[8];
|
|
static int32 CVBC;
|
|
|
|
#define TOINDEX (16 + 1)
|
|
|
|
// 16:15
|
|
static void SyncHQ(int32 ts) {
|
|
CVBC = ts;
|
|
}
|
|
|
|
|
|
/* Things to do:
|
|
1 Read freq low
|
|
2 Read freq mid
|
|
3 Read freq high
|
|
4 Read envelope
|
|
...?
|
|
*/
|
|
|
|
static INLINE uint32 FetchDuff(uint32 P, uint32 envelope) {
|
|
uint32 duff;
|
|
duff = IRAM[((IRAM[0x46 + (P << 3)] + (PlayIndex[P] >> TOINDEX)) & 0xFF) >> 1];
|
|
if ((IRAM[0x46 + (P << 3)] + (PlayIndex[P] >> TOINDEX)) & 1)
|
|
duff >>= 4;
|
|
duff &= 0xF;
|
|
duff = (duff * envelope) >> 16;
|
|
return(duff);
|
|
}
|
|
|
|
static void DoNamcoSoundHQ(void) {
|
|
int32 P, V;
|
|
int32 cyclesuck = (((IRAM[0x7F] >> 4) & 7) + 1) * 15;
|
|
|
|
for (P = 7; P >= (7 - ((IRAM[0x7F] >> 4) & 7)); P--) {
|
|
if ((IRAM[0x44 + (P << 3)] & 0xE0) && (IRAM[0x47 + (P << 3)] & 0xF)) {
|
|
uint32 freq;
|
|
int32 vco;
|
|
uint32 duff2, lengo, envelope;
|
|
|
|
vco = vcount[P];
|
|
freq = FreqCache[P];
|
|
envelope = EnvCache[P];
|
|
lengo = LengthCache[P];
|
|
|
|
duff2 = FetchDuff(P, envelope);
|
|
for (V = CVBC << 1; V < (int)SOUNDTS << 1; V++) {
|
|
WaveHi[V >> 1] += duff2;
|
|
if (!vco) {
|
|
PlayIndex[P] += freq;
|
|
while ((PlayIndex[P] >> TOINDEX) >= lengo) PlayIndex[P] -= lengo << TOINDEX;
|
|
duff2 = FetchDuff(P, envelope);
|
|
vco = cyclesuck;
|
|
}
|
|
vco--;
|
|
}
|
|
vcount[P] = vco;
|
|
}
|
|
}
|
|
CVBC = SOUNDTS;
|
|
}
|
|
|
|
|
|
static void DoNamcoSound(int32 *Wave, int Count) {
|
|
int P, V;
|
|
for (P = 7; P >= 7 - ((IRAM[0x7F] >> 4) & 7); P--) {
|
|
if ((IRAM[0x44 + (P << 3)] & 0xE0) && (IRAM[0x47 + (P << 3)] & 0xF)) {
|
|
int32 inc;
|
|
uint32 freq;
|
|
int32 vco;
|
|
uint32 duff, duff2, lengo, envelope;
|
|
|
|
vco = vcount[P];
|
|
freq = FreqCache[P];
|
|
envelope = EnvCache[P];
|
|
lengo = LengthCache[P];
|
|
|
|
if (!freq) { /*printf("Ack");*/
|
|
continue;
|
|
}
|
|
|
|
{
|
|
int c = ((IRAM[0x7F] >> 4) & 7) + 1;
|
|
inc = (long double)(FSettings.SndRate << 15) / ((long double)freq * 21477272 / ((long double)0x400000 * c * 45));
|
|
}
|
|
|
|
duff = IRAM[(((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 0xFF) >> 1)];
|
|
if ((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 1)
|
|
duff >>= 4;
|
|
duff &= 0xF;
|
|
duff2 = (duff * envelope) >> 19;
|
|
for (V = 0; V < Count * 16; V++) {
|
|
if (vco >= inc) {
|
|
PlayIndex[P]++;
|
|
if (PlayIndex[P] >= lengo)
|
|
PlayIndex[P] = 0;
|
|
vco -= inc;
|
|
duff = IRAM[(((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 0xFF) >> 1)];
|
|
if ((IRAM[0x46 + (P << 3)] + PlayIndex[P]) & 1)
|
|
duff >>= 4;
|
|
duff &= 0xF;
|
|
duff2 = (duff * envelope) >> 19;
|
|
}
|
|
Wave[V >> 4] += duff2;
|
|
vco += 0x8000;
|
|
}
|
|
vcount[P] = vco;
|
|
}
|
|
}
|
|
}
|
|
|
|
static void Mapper19_StateRestore(int version) {
|
|
SyncPRG();
|
|
FixNTAR();
|
|
FixCRR();
|
|
SyncMirror();
|
|
int x;
|
|
for (x = 0x40; x < 0x80; x++)
|
|
FixCache(x, IRAM[x]);
|
|
}
|
|
|
|
static void M19SC(void) {
|
|
if (FSettings.SndRate)
|
|
Mapper19_ESI();
|
|
}
|
|
|
|
void Mapper19_ESI(void) {
|
|
GameExpSound.RChange = M19SC;
|
|
memset(vcount, 0, sizeof(vcount));
|
|
memset(PlayIndex, 0, sizeof(PlayIndex));
|
|
CVBC = 0;
|
|
}
|
|
|
|
void NSFN106_Init(void) {
|
|
SetWriteHandler(0xf800, 0xffff, Mapper19_write);
|
|
SetWriteHandler(0x4800, 0x4fff, Mapper19_write);
|
|
SetReadHandler(0x4800, 0x4fff, Namco_Read4800);
|
|
Mapper19_ESI();
|
|
}
|
|
|
|
static int battery = 0;
|
|
|
|
static void N106_Power(void) {
|
|
int x;
|
|
SetReadHandler(0x8000, 0xFFFF, CartBR);
|
|
SetWriteHandler(0x8000, 0xffff, Mapper19_write);
|
|
SetWriteHandler(0x4020, 0x5fff, Mapper19_write);
|
|
if (!is210) {
|
|
SetWriteHandler(0xc000, 0xdfff, Mapper19C0D8_write);
|
|
SetReadHandler(0x4800, 0x4fff, Namco_Read4800);
|
|
SetReadHandler(0x5000, 0x57ff, Namco_Read5000);
|
|
SetReadHandler(0x5800, 0x5fff, Namco_Read5800);
|
|
NTAPage[0] = NTAPage[1] = NTAPage[2] = NTAPage[3] = 0xFF;
|
|
FixNTAR();
|
|
}
|
|
|
|
SetReadHandler(0x6000, 0x7FFF, AWRAM);
|
|
SetWriteHandler(0x6000, 0x7FFF, BWRAM);
|
|
FCEU_CheatAddRAM(8, 0x6000, WRAM);
|
|
|
|
gorfus = 0xFF;
|
|
SyncPRG();
|
|
FixCRR();
|
|
|
|
if (!battery) {
|
|
FCEU_MemoryRand(WRAM, sizeof(WRAM), true);
|
|
FCEU_MemoryRand(IRAM, sizeof(IRAM), true);
|
|
}
|
|
for (x = 0x40; x < 0x80; x++)
|
|
FixCache(x, IRAM[x]);
|
|
}
|
|
|
|
void Mapper19_Init(CartInfo *info) {
|
|
is210 = 0;
|
|
battery = info->battery;
|
|
info->Power = N106_Power;
|
|
|
|
MapIRQHook = NamcoIRQHook;
|
|
GameStateRestore = Mapper19_StateRestore;
|
|
GameExpSound.RChange = M19SC;
|
|
|
|
if (FSettings.SndRate)
|
|
Mapper19_ESI();
|
|
|
|
FCEU_MemoryRand(WRAM, sizeof(WRAM), true);
|
|
FCEU_MemoryRand(IRAM, sizeof(IRAM), true);
|
|
AddExState(WRAM, 8192, 0, "WRAM");
|
|
AddExState(IRAM, 128, 0, "IRAM");
|
|
AddExState(N106_StateRegs, ~0, 0, 0);
|
|
|
|
if (info->battery) {
|
|
info->SaveGame[0] = WRAM;
|
|
info->SaveGameLen[0] = 8192;
|
|
info->SaveGame[1] = IRAM;
|
|
info->SaveGameLen[1] = 128;
|
|
}
|
|
}
|
|
|
|
static void Mapper210_StateRestore(int version) {
|
|
SyncPRG();
|
|
FixCRR();
|
|
}
|
|
|
|
void Mapper210_Init(CartInfo *info) {
|
|
is210 = 1;
|
|
GameStateRestore = Mapper210_StateRestore;
|
|
info->Power = N106_Power;
|
|
FCEU_MemoryRand(WRAM, sizeof(WRAM), true);
|
|
AddExState(WRAM, 8192, 0, "WRAM");
|
|
AddExState(N106_StateRegs, ~0, 0, 0);
|
|
}
|