/* 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" }, { 0 } }; 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: gorko = V & 0xC0; PRG[0] = V & 0x3F; 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 < 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(); 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_dwmemset(WRAM, 0, 8192); FCEU_dwmemset(IRAM, 0, 128); } 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(); 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; AddExState(WRAM, 8192, 0, "WRAM"); AddExState(N106_StateRegs, ~0, 0, 0); }