diff --git a/src/drivers/libretro/fceu/nsf.c b/src/drivers/libretro/fceu/nsf.c new file mode 100644 index 0000000..5c54535 --- /dev/null +++ b/src/drivers/libretro/fceu/nsf.c @@ -0,0 +1,519 @@ +/* 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 +#include +#include +#include + +#include "fceu-types.h" +#include "x6502.h" +#include "fceu.h" +#include "video.h" +#include "sound.h" +#include "nsf.h" +#include "general.h" +#include "fceu-memory.h" +#include "file.h" +#include "fds.h" +#include "cart.h" +#include "input.h" + +#ifndef M_PI +#define M_PI 3.14159265358979323846 +#endif + +static uint8 SongReload; +static int CurrentSong; + +static DECLFW(NSF_write); +static DECLFR(NSF_read); + +static int vismode = 1; + +static uint8 NSFROM[0x30 + 6] = +{ +/* 0x00 - NMI */ + 0x8D, 0xF4, 0x3F, /* Stop play routine NMIs. */ + 0xA2, 0xFF, 0x9A, /* Initialize the stack pointer. */ + 0xAD, 0xF0, 0x3F, /* See if we need to init. */ + 0xF0, 0x09, /* If 0, go to play routine playing. */ + + 0xAD, 0xF1, 0x3F, /* Confirm and load A */ + 0xAE, 0xF3, 0x3F, /* Load X with PAL/NTSC byte */ + + 0x20, 0x00, 0x00, /* JSR to init routine */ + + 0xA9, 0x00, + 0xAA, + 0xA8, + 0x20, 0x00, 0x00, /* JSR to play routine */ + 0x8D, 0xF5, 0x3F, /* Start play routine NMIs. */ + 0x90, 0xFE, /* Loopie time. */ + +/* 0x20 */ + 0x8D, 0xF3, 0x3F, /* Init init NMIs */ + 0x18, + 0x90, 0xFE /* Loopie time. */ +}; + +static DECLFR(NSFROMRead) { + return (NSFROM - 0x3800)[A]; +} + +static int doreset = 0; +static int NSFNMIFlags; +static uint8 *NSFDATA = 0; +static int NSFMaxBank; + +static int NSFSize; +static uint8 BSon; +static uint16 PlayAddr; +static uint16 InitAddr; +static uint16 LoadAddr; + +static NSF_HEADER NSFHeader; + +void NSFMMC5_Close(void); +static uint8 *ExWRAM = 0; + +void NSFGI(int h) { + switch (h) { + case GI_CLOSE: + if (NSFDATA) { + free(NSFDATA); NSFDATA = 0; + } + if (ExWRAM) { + free(ExWRAM); ExWRAM = 0; + } + if (NSFHeader.SoundChip & 1) { +// NSFVRC6_Init(); + } else if (NSFHeader.SoundChip & 2) { +// NSFVRC7_Init(); + } else if (NSFHeader.SoundChip & 4) { +// FDSSoundReset(); + } else if (NSFHeader.SoundChip & 8) { + NSFMMC5_Close(); + } else if (NSFHeader.SoundChip & 0x10) { +// NSFN106_Init(); + } else if (NSFHeader.SoundChip & 0x20) { +// NSFAY_Init(); + } + break; + case GI_RESETM2: + case GI_POWER: NSF_init(); break; + } +} + +// First 32KB is reserved for sound chip emulation in the iNES mapper code. + +static INLINE void BANKSET(uint32 A, uint32 bank) { + bank &= NSFMaxBank; + if (NSFHeader.SoundChip & 4) + memcpy(ExWRAM + (A - 0x6000), NSFDATA + (bank << 12), 4096); + else + setprg4(A, bank); +} + +int NSFLoad(FCEUFILE *fp) { + int x; + + FCEU_fseek(fp, 0, SEEK_SET); + FCEU_fread(&NSFHeader, 1, 0x80, fp); + if (memcmp(NSFHeader.ID, "NESM\x1a", 5)) + return 0; + NSFHeader.SongName[31] = NSFHeader.Artist[31] = NSFHeader.Copyright[31] = 0; + + LoadAddr = NSFHeader.LoadAddressLow; + LoadAddr |= NSFHeader.LoadAddressHigh << 8; + + if (LoadAddr < 0x6000) { + FCEUD_PrintError("Invalid load address."); + return(0); + } + InitAddr = NSFHeader.InitAddressLow; + InitAddr |= NSFHeader.InitAddressHigh << 8; + + PlayAddr = NSFHeader.PlayAddressLow; + PlayAddr |= NSFHeader.PlayAddressHigh << 8; + + NSFSize = FCEU_fgetsize(fp) - 0x80; + + NSFMaxBank = ((NSFSize + (LoadAddr & 0xfff) + 4095) / 4096); + NSFMaxBank = uppow2(NSFMaxBank); + + if (!(NSFDATA = (uint8*)FCEU_malloc(NSFMaxBank * 4096))) + return 0; + + FCEU_fseek(fp, 0x80, SEEK_SET); + memset(NSFDATA, 0x00, NSFMaxBank * 4096); + FCEU_fread(NSFDATA + (LoadAddr & 0xfff), 1, NSFSize, fp); + + NSFMaxBank--; + + BSon = 0; + for (x = 0; x < 8; x++) + BSon |= NSFHeader.BankSwitch[x]; + + GameInfo->type = GIT_NSF; + GameInfo->input[0] = GameInfo->input[1] = SI_GAMEPAD; + GameInfo->cspecial = SIS_NSF; + + for (x = 0;; x++) { + if (NSFROM[x] == 0x20) { + NSFROM[x + 1] = InitAddr & 0xFF; + NSFROM[x + 2] = InitAddr >> 8; + NSFROM[x + 8] = PlayAddr & 0xFF; + NSFROM[x + 9] = PlayAddr >> 8; + break; + } + } + + if (NSFHeader.VideoSystem == 0) + GameInfo->vidsys = GIV_NTSC; + else if (NSFHeader.VideoSystem == 1) + GameInfo->vidsys = GIV_PAL; + + GameInterface = NSFGI; + + FCEU_printf("NSF Loaded. File information:\n\n"); + FCEU_printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n", NSFHeader.SongName, NSFHeader.Artist, NSFHeader.Copyright); + if (NSFHeader.SoundChip) { + static char *tab[6] = { "Konami VRCVI", "Konami VRCVII", "Nintendo FDS", "Nintendo MMC5", "Namco 106", "Sunsoft FME-07" }; + + for (x = 0; x < 6; x++) + if (NSFHeader.SoundChip & (1 << x)) { + FCEU_printf(" Expansion hardware: %s\n", tab[x]); + NSFHeader.SoundChip = 1 << x; /* Prevent confusing weirdness if more than one bit is set. */ + break; + } + } + if (BSon) + FCEU_printf(" Bank-switched.\n"); + FCEU_printf(" Load address: $%04x\n Init address: $%04x\n Play address: $%04x\n", LoadAddr, InitAddr, PlayAddr); + FCEU_printf(" %s\n", (NSFHeader.VideoSystem & 1) ? "PAL" : "NTSC"); + FCEU_printf(" Starting song: %d / %d\n\n", NSFHeader.StartingSong, NSFHeader.TotalSongs); + + if (NSFHeader.SoundChip & 4) + ExWRAM = FCEU_gmalloc(32768 + 8192); + else + ExWRAM = FCEU_gmalloc(8192); + return 1; +} + +static DECLFR(NSFVectorRead) { + if (((NSFNMIFlags & 1) && SongReload) || (NSFNMIFlags & 2) || doreset) { + if (A == 0xFFFA) return(0x00); + else if (A == 0xFFFB) return(0x38); + else if (A == 0xFFFC) return(0x20); + else if (A == 0xFFFD) { + doreset = 0; return(0x38); + } + return(X.DB); + } else + return(CartBR(A)); +} + +void NSFVRC6_Init(void); +void NSFVRC7_Init(void); +void NSFMMC5_Init(void); +void NSFN106_Init(void); +void NSFAY_Init(void); + +void NSF_init(void) { + doreset = 1; + + ResetCartMapping(); + if (NSFHeader.SoundChip & 4) { + SetupCartPRGMapping(0, ExWRAM, 32768 + 8192, 1); + setprg32(0x6000, 0); + setprg8(0xE000, 4); + memset(ExWRAM, 0x00, 32768 + 8192); + SetWriteHandler(0x6000, 0xDFFF, CartBW); + SetReadHandler(0x6000, 0xFFFF, CartBR); + } else { + memset(ExWRAM, 0x00, 8192); + SetReadHandler(0x6000, 0x7FFF, CartBR); + SetWriteHandler(0x6000, 0x7FFF, CartBW); + SetupCartPRGMapping(0, NSFDATA, ((NSFMaxBank + 1) * 4096), 0); + SetupCartPRGMapping(1, ExWRAM, 8192, 1); + setprg8r(1, 0x6000, 0); + SetReadHandler(0x8000, 0xFFFF, CartBR); + } + + if (BSon) { + int32 x; + for (x = 0; x < 8; x++) { + if (NSFHeader.SoundChip & 4 && x >= 6) + BANKSET(0x6000 + (x - 6) * 4096, NSFHeader.BankSwitch[x]); + BANKSET(0x8000 + x * 4096, NSFHeader.BankSwitch[x]); + } + } else { + int32 x; + for (x = (LoadAddr & 0xF000); x < 0x10000; x += 0x1000) + BANKSET(x, ((x - (LoadAddr & 0x7000)) >> 12)); + } + + SetReadHandler(0xFFFA, 0xFFFD, NSFVectorRead); + + SetWriteHandler(0x2000, 0x3fff, 0); + SetReadHandler(0x2000, 0x37ff, 0); + SetReadHandler(0x3836, 0x3FFF, 0); + SetReadHandler(0x3800, 0x3835, NSFROMRead); + + SetWriteHandler(0x5ff6, 0x5fff, NSF_write); + + SetWriteHandler(0x3ff0, 0x3fff, NSF_write); + SetReadHandler(0x3ff0, 0x3fff, NSF_read); + + + if (NSFHeader.SoundChip & 1) { + NSFVRC6_Init(); + } else if (NSFHeader.SoundChip & 2) { + NSFVRC7_Init(); + } else if (NSFHeader.SoundChip & 4) { + FDSSoundReset(); + } else if (NSFHeader.SoundChip & 8) { + NSFMMC5_Init(); + } else if (NSFHeader.SoundChip & 0x10) { + NSFN106_Init(); + } else if (NSFHeader.SoundChip & 0x20) { + NSFAY_Init(); + } + CurrentSong = NSFHeader.StartingSong; + SongReload = 0xFF; + NSFNMIFlags = 0; +} + +static DECLFW(NSF_write) { + switch (A) { + case 0x3FF3: NSFNMIFlags |= 1; break; + case 0x3FF4: NSFNMIFlags &= ~2; break; + case 0x3FF5: NSFNMIFlags |= 2; break; + + case 0x5FF6: + case 0x5FF7: if (!(NSFHeader.SoundChip & 4)) return; + case 0x5FF8: + case 0x5FF9: + case 0x5FFA: + case 0x5FFB: + case 0x5FFC: + case 0x5FFD: + case 0x5FFE: + case 0x5FFF: if (!BSon) return; + A &= 0xF; + BANKSET((A * 4096), V); + break; + } +} + +static DECLFR(NSF_read) { + int x; + + switch (A) { + case 0x3ff0: x = SongReload; + #ifdef FCEUDEF_DEBUGGER + if (!fceuindbg) + #endif + SongReload = 0; + return x; + case 0x3ff1: + #ifdef FCEUDEF_DEBUGGER + if (!fceuindbg) + #endif + { + memset(RAM, 0x00, 0x800); + + BWrite[0x4015](0x4015, 0x0); + for (x = 0; x < 0x14; x++) + BWrite[0x4000 + x](0x4000 + x, 0); + BWrite[0x4015](0x4015, 0xF); + + if (NSFHeader.SoundChip & 4) { + BWrite[0x4017](0x4017, 0xC0); /* FDS BIOS writes $C0 */ + BWrite[0x4089](0x4089, 0x80); + BWrite[0x408A](0x408A, 0xE8); + } else { + memset(ExWRAM, 0x00, 8192); + BWrite[0x4017](0x4017, 0xC0); + BWrite[0x4017](0x4017, 0xC0); + BWrite[0x4017](0x4017, 0x40); + } + + if (BSon) { + for (x = 0; x < 8; x++) + BANKSET(0x8000 + x * 4096, NSFHeader.BankSwitch[x]); + } + return(CurrentSong - 1); + } + case 0x3FF3: return PAL; + } + return 0; +} + +uint8 FCEU_GetJoyJoy(void); + +static int special = 0; + +void DrawNSF(uint8 *XBuf) { + char snbuf[16]; + int x; + + if (vismode == 0) return; + + memset(XBuf, 0, 256 * 240); + + + { + int32 *Bufpl; + int32 mul = 0; + + int l; + l = GetSoundBuffer(&Bufpl); + + if (special == 0) { + if (FSettings.SoundVolume) + mul = 8192 * 240 / (16384 * FSettings.SoundVolume / 50); + for (x = 0; x < 256; x++) { + uint32 y; + y = 142 + ((Bufpl[(x * l) >> 8] * mul) >> 14); + if (y < 240) + XBuf[x + y * 256] = 3; + } + } else if (special == 1) { + if (FSettings.SoundVolume) + mul = 8192 * 240 / (8192 * FSettings.SoundVolume / 50); + for (x = 0; x < 256; x++) { + double r; + uint32 xp, yp; + + r = (Bufpl[(x * l) >> 8] * mul) >> 14; + xp = 128 + r*cos(x*M_PI*2 / 256); + yp = 120 + r*sin(x*M_PI*2 / 256); + xp &= 255; + yp %= 240; + XBuf[xp + yp * 256] = 3; + } + } else if (special == 2) { + static double theta = 0; + if (FSettings.SoundVolume) + mul = 8192 * 240 / (16384 * FSettings.SoundVolume / 50); + for (x = 0; x < 128; x++) { + double xc, yc; + double r, t; + uint32 m, n; + + xc = (double)128 - x; + yc = 0 - ((double)(((Bufpl[(x * l) >> 8]) * mul) >> 14)); + t = M_PI + atan(yc / xc); + r = sqrt(xc * xc + yc * yc); + + t += theta; + m = 128 + r*cos(t); + n = 120 + r*sin(t); + + if (m < 256 && n < 240) + XBuf[m + n * 256] = 3; + } + for (x = 128; x < 256; x++) { + double xc, yc; + double r, t; + uint32 m, n; + + xc = (double)x - 128; + yc = (double)((Bufpl[(x * l) >> 8] * mul) >> 14); + t = atan(yc / xc); + r = sqrt(xc * xc + yc * yc); + + t += theta; + m = 128 + r*cos(t); + n = 120 + r*sin(t); + + if (m < 256 && n < 240) + XBuf[m + n * 256] = 3; + } + theta += (double)M_PI / 256; + } + } + + DrawTextTrans(XBuf + 10 * 256 + 4 + (((31 - strlen((char*)NSFHeader.SongName)) << 2)), 256, NSFHeader.SongName, 6); + DrawTextTrans(XBuf + 26 * 256 + 4 + (((31 - strlen((char*)NSFHeader.Artist)) << 2)), 256, NSFHeader.Artist, 6); + DrawTextTrans(XBuf + 42 * 256 + 4 + (((31 - strlen((char*)NSFHeader.Copyright)) << 2)), 256, NSFHeader.Copyright, 6); + + DrawTextTrans(XBuf + 70 * 256 + 4 + (((31 - strlen("Song:")) << 2)), 256, (uint8*)"Song:", 6); + sprintf(snbuf, "<%d/%d>", CurrentSong, NSFHeader.TotalSongs); + DrawTextTrans(XBuf + 82 * 256 + 4 + (((31 - strlen(snbuf)) << 2)), 256, (uint8*)snbuf, 6); + + { + static uint8 last = 0; + uint8 tmp; + tmp = FCEU_GetJoyJoy(); + if ((tmp & JOY_RIGHT) && !(last & JOY_RIGHT)) { + if (CurrentSong < NSFHeader.TotalSongs) { + CurrentSong++; + SongReload = 0xFF; + } + } else if ((tmp & JOY_LEFT) && !(last & JOY_LEFT)) { + if (CurrentSong > 1) { + CurrentSong--; + SongReload = 0xFF; + } + } else if ((tmp & JOY_UP) && !(last & JOY_UP)) { + CurrentSong += 10; + if (CurrentSong > NSFHeader.TotalSongs) CurrentSong = NSFHeader.TotalSongs; + SongReload = 0xFF; + } else if ((tmp & JOY_DOWN) && !(last & JOY_DOWN)) { + CurrentSong -= 10; + if (CurrentSong < 1) CurrentSong = 1; + SongReload = 0xFF; + } else if ((tmp & JOY_START) && !(last & JOY_START)) + SongReload = 0xFF; + else if ((tmp & JOY_A) && !(last & JOY_A)) { + special = (special + 1) % 3; + } + last = tmp; + } +} + +void DoNSFFrame(void) { + if (((NSFNMIFlags & 1) && SongReload) || (NSFNMIFlags & 2)) + TriggerNMI(); +} + +void FCEUI_NSFSetVis(int mode) { + vismode = mode; +} + +int FCEUI_NSFChange(int amount) { + CurrentSong += amount; + if (CurrentSong < 1) CurrentSong = 1; + else if (CurrentSong > NSFHeader.TotalSongs) CurrentSong = NSFHeader.TotalSongs; + SongReload = 0xFF; + + return(CurrentSong); +} + +/* Returns total songs */ +int FCEUI_NSFGetInfo(uint8 *name, uint8 *artist, uint8 *copyright, int maxlen) { + strncpy(name, NSFHeader.SongName, maxlen); + strncpy(artist, NSFHeader.Artist, maxlen); + strncpy(copyright, NSFHeader.Copyright, maxlen); + return(NSFHeader.TotalSongs); +} diff --git a/src/drivers/libretro/griffin.c b/src/drivers/libretro/griffin.c index 3175662..105769e 100644 --- a/src/drivers/libretro/griffin.c +++ b/src/drivers/libretro/griffin.c @@ -21,7 +21,7 @@ #include "md5.c" //#include "movie.c" //#include "netplay.c" -#include "nsf.c" +#include "drivers/libretro/fceu/nsf.c" #include "palette.c" #include "ppu.c" #include "sound.c"