updated bords/mappers/stuff to 0.98.15, lots of them got broken, asmcore support...
[fceu.git] / nsf.c
diff --git a/nsf.c b/nsf.c
index 2db647c..9e09cfb 100644 (file)
--- a/nsf.c
+++ b/nsf.c
-/* FCE Ultra - NES/Famicom Emulator
- *
- * Copyright notice for this file:
- *  Copyright (C) 2002 Ben Parnell
- *
- * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-
-#include "types.h"
-#include "x6502.h"
-#include "fce.h"
-#include "svga.h"
-#include "video.h"
-#include "sound.h"
-#include "ines.h"
-#include "nsf.h"
-#include "nsfbgnew.h"
-#include "general.h"
-#include "memory.h"
-#include "file.h"
-#include "fds.h"
-#include "cart.h"
-#include "input.h"
-
-#ifndef M_PI
-#define M_PI 3.14159265358979323846
-#endif
-
-uint8 SongReload;
-uint8 CurrentSong;
-
-static int sinetable[32];
-
-
-static uint8 NSFROM[0x30+6]=
-{
-/* 0x00 */
-
-0x08,0x48,0x8A,0x48,0x98,0x48,          /* Store regs           */
-0xA9,0xFF,
-0x8D,0xF2,0x5F,                                /* NMI has occured      */
-0x68,0xA8,0x68,0xAA,0x68,0x28,
-0x40,     /* Restore regs         */
-
-/* 0x12 */
-
-0xAD,0xF2,0x5F,                                /* See if an NMI occured */
-0xF0,0xFB,                             /* If it hasn't, loop    */
-
-0xA9,0x00,
-0x8D,0xF2,0x5F,                                /* Clear play pending reg*/
-
-
-0xAD,0xF0,0x5F,                         /* See if we need to init. */
-0xF0,0x09,                              /* If 0, go to JMP         */
-
-0xAD,0xF1,0x5F,                         /* Confirm and load A      */
-0xAE,0xF3,0x5F,                         /* Load X with PAL/NTSC byte */
-
-0x20,0x00,0x00,                         /* JSR to init routine     */
-
-0x20,0x00,0x00,                         /* JSR to play routine  */
-
-0x4C,0x12,0x38,                         /* Loop                 */
-
-0xA2,0xFF,0x9A,                                /* Initialize the stack pointer. */
-0x4C,0x12,0x38
-};
-
-static DECLFR(NSFROMRead)
-{
- return (NSFROM-0x3800)[A];
-}
-
-
-
-static uint8 *NSFDATA=0;
-static int NSFMaxBank;
-
-static int NSFSize;
-static uint8 BSon;
-static uint16 PlayAddr;
-static uint16 InitAddr;
-static uint16 LoadAddr;
-
-NSF_HEADER NSFHeader;
-
-void NSFGI(int h)
-{
- switch(h)
- {
- case GI_CLOSE:
-  if(NSFDATA) {free(NSFDATA);NSFDATA=0;}
-  break;
- case GI_POWER: NSF_init();break;
- }
-}
-
-// First 32KB is reserved for sound chip emulation in the iNES mapper code.
-
-#define WRAM (GameMemBlock+32768)
-#define FDSMEM (GameMemBlock+32768)
-
-static INLINE void BANKSET(uint32 A, uint32 bank)
-{
- bank&=NSFMaxBank;
- if(NSFHeader.SoundChip&4)
-  memcpy(FDSMEM+(A-0x6000),NSFDATA+(bank<<12),4096);
- else
-  setprg4(A,bank);
-}
-
-int NSFLoad(int 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;
-
-  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];
-
- FCEUGameInfo.type=GIT_NSF;
- FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_NONE;
-
- for(x=0;;x++)
- {
-  if(NSFROM[x]==0x20)
-  {
-   NSFROM[x+1]=InitAddr&0xFF;
-   NSFROM[x+2]=InitAddr>>8;
-   NSFROM[x+4]=PlayAddr&0xFF;
-   NSFROM[x+5]=PlayAddr>>8;
-   break;
-  }
- }
-
- if(NSFHeader.VideoSystem==0)
-  FCEUGameInfo.vidsys=GIV_NTSC;
- else if(NSFHeader.VideoSystem==1)
-  FCEUGameInfo.vidsys=GIV_PAL;
-
- {
-  double fruit=0;
-  for(x=0;x<32;x++)
-  {
-   double ta,no;
-
-   ta=sin(fruit)*7;
-   ta+=modf(ta,&no);
-   sinetable[x]=ta;
-   fruit+=(double)M_PI*2/32;
-  }
- }
- GameInterface=NSFGI;
-
- puts("NSF Loaded.  File information:\n");
- 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))
-   {
-    printf(" Expansion hardware:  %s\n",tab[x]);
-    break;
-   }
- }
- if(BSon)
-  puts(" Bank-switched.");
- printf(" Load address:  $%04x\n Init address:  $%04x\n Play address:  $%04x\n",LoadAddr,InitAddr,PlayAddr);
- printf(" %s\n",(NSFHeader.VideoSystem&1)?"PAL":"NTSC");
- printf(" Starting song:  %d / %d\n\n",NSFHeader.StartingSong,NSFHeader.TotalSongs);
- return 1;
-}
-
-static DECLFW(BWRAM)
-{
-                (WRAM-0x6000)[A]=V;
-}
-
-static DECLFW(NSFFDSWrite)
-{
-       (FDSMEM-0x6000)[A]=V;
-}
-
-static DECLFR(NSFFDSRead)
-{
-       return (FDSMEM-0x6000)[A];
-}
-
-static DECLFR(AWRAM)
-{
-       return WRAM[A-0x6000];
-}
-
-void NSF_init(void)
-{
-  if(NSFHeader.SoundChip&4)
-  {
-   memset(FDSMEM,0x00,32768+8192);
-   SetWriteHandler(0x6000,0xDFFF,NSFFDSWrite);
-   SetReadHandler(0x6000,0xFFFF,NSFFDSRead);
-  }
-  else
-  {
-   memset(WRAM,0x00,8192);
-   SetReadHandler(0x6000,0x7FFF,AWRAM);
-   SetWriteHandler(0x6000,0x7FFF,BWRAM);
-   ResetCartMapping();
-   SetupCartPRGMapping(0,NSFDATA,((NSFMaxBank+1)*4096),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&0x7000);x<0x8000;x+=0x1000)
-     BANKSET(0x8000+x,((x-(LoadAddr&0x7000))>>12));
-  }
-
-  SetWriteHandler(0x2000,0x3fff,0);
-  SetReadHandler(0x2000,0x37ff,0);
-  SetReadHandler(0x3836,0x3FFF,0);
-  SetReadHandler(0x3800,0x3835,NSFROMRead);
-
-  SetWriteHandler(0x4020,0x5fff,NSF_write);
-  SetReadHandler(0x4020,0x5fff,NSF_read);
-
-
-  if(NSFHeader.SoundChip&1) {
-   VRC6_ESI(0);
-  } else if (NSFHeader.SoundChip&2) {
-   VRC7_ESI();
-  } else if (NSFHeader.SoundChip&4) {
-   FDSSoundReset();
-  } else if (NSFHeader.SoundChip&8) {
-   Mapper5_ESI();
-  } else if (NSFHeader.SoundChip&0x10) {
-   Mapper19_ESI();
-  } else if (NSFHeader.SoundChip&0x20) {
-   Mapper69_ESI();
-  }
-  CurrentSong=NSFHeader.StartingSong;
-  SongReload=1;
-}
-
-static uint8 DoUpdateStuff=0;
-DECLFW(NSF_write)
-{
-switch(A)
-{
- case 0x5FF2:if((X.PC&0xF000)==0x3000) DoUpdateStuff=V;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);
-             X6502_Rebase();
-            break;
-}
-}
-
-DECLFR(NSF_read)
-{
- int x;
-
- if((X.PC&0xF000)==0x3000)
- switch(A)
- {
- case 0x5ff0:x=SongReload;SongReload=0;return x;
- case 0x5ff1:
-            {
-             memset(RAM,0x00,0x800);
-             memset(WRAM,0x00,8192);
-             BWrite[0x4015](0x4015,0xF);
-             for(x=0;x<0x14;x++)
-              {if(x!=0x11) BWrite[0x4015](0x4015,0);}
-             BWrite[0x4015](0x4015,0x0);
-             for(x=0;x<0x14;x++)
-              {if(x!=0x11) BWrite[0x4015](0x4015,0);}
-            BWrite[0x4011](0x4011,0x40);
-             BWrite[0x4015](0x4015,0xF);
-            BWrite[0x4017](0x4017,0x40);
-            if(NSFHeader.SoundChip&4)
-             BWrite[0x4089](0x4089,0x80);
-             if(BSon)
-             {
-              for(x=0;x<8;x++)
-              BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);
-              X6502_Rebase();
-             }
-             return (CurrentSong-1);
-            }
- case 0x5FF2:return DoUpdateStuff;
- case 0x5FF3:return PAL;
- }
- return 0;
-}
-static int32 *Bufpl;
-void DrawNSF(uint8 *XBuf)
-{
- char snbuf[16];
- static int z=0;
- int x,y;
- uint8 *XBuf2,*tmpb;
-
- XBuf+=8;
- XBuf2=XBuf;
-
- tmpb=NSFBG+8;
- for(y=120;y;y--)
-  {
-   uint8 *offs;
-
-   offs=tmpb+sinetable[((z+y)>>2)&31];
-   memcpy(XBuf2,offs,256);
-   memcpy(XBuf2+(120*272),offs,256);
-
-   XBuf2+=272;
-   tmpb+=272;
-  }
- tmpb=NSFBG+8;
- z=(z+1)&127;
-
- DrawTextTrans(XBuf+10*272+4+(((31-strlen((char *)(NSFHeader.SongName)))<<2)), 272, NSFHeader.SongName, 38);
- DrawTextTrans(XBuf+30*272+4+(((31-strlen((char *)(NSFHeader.Artist)))<<2)), 272, NSFHeader.Artist, 38);
- DrawTextTrans(XBuf+50*272+4+(((31-strlen((char *)(NSFHeader.Copyright)))<<2)), 272, NSFHeader.Copyright, 38);
-
- DrawTextTrans(XBuf+90*272+4+(((31-strlen("Song:"))<<2)), 272, (uint8 *)"Song:", 38);
- sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);
- DrawTextTrans(XBuf+102*272+4+(((31-strlen(snbuf))<<2)), 272, (uint8 *)snbuf, 38);
-
- GetSoundBuffer(&Bufpl);
-  for(x=0;x<256;x++)
-   XBuf[x+(224-((((Bufpl[x]>>(7)^128)&255)*3)>>3))*272]=38;
-}
-
-void NSFControl(int z)
-{
- if(z==1)
- {
-  if(CurrentSong<NSFHeader.TotalSongs) CurrentSong++;
- }
- else if(z==2)
- {
-  if(CurrentSong>1) CurrentSong--;
- }
- SongReload=0xFF;
-}
+/* FCE Ultra - NES/Famicom Emulator\r
+ *\r
+ * Copyright notice for this file:\r
+ *  Copyright (C) 2002 Xodnizel\r
+ *\r
+ * This program is free software; you can redistribute it and/or modify\r
+ * it under the terms of the GNU General Public License as published by\r
+ * the Free Software Foundation; either version 2 of the License, or\r
+ * (at your option) any later version.\r
+ *\r
+ * This program is distributed in the hope that it will be useful,\r
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of\r
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
+ * GNU General Public License for more details.\r
+ *\r
+ * You should have received a copy of the GNU General Public License\r
+ * along with this program; if not, write to the Free Software\r
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA\r
+ */\r
+\r
+#include <stdio.h>\r
+#include <stdlib.h>\r
+#include <string.h>\r
+#include <math.h>\r
+\r
+#include "types.h"\r
+#include "x6502.h"\r
+#include "fce.h"\r
+#include "video.h"\r
+#include "sound.h"\r
+#include "nsf.h"\r
+#include "general.h"\r
+#include "memory.h"\r
+#include "file.h"\r
+#include "fds.h"\r
+#include "cart.h"\r
+#include "input.h"\r
+\r
+#include "svga.h"\r
+\r
+#ifndef M_PI\r
+#define M_PI 3.14159265358979323846\r
+#endif\r
+\r
+static uint8 SongReload;\r
+static int CurrentSong;\r
+\r
+static DECLFW(NSF_write);\r
+static DECLFR(NSF_read);\r
+\r
+static int vismode=1;\r
+\r
+static uint8 NSFROM[0x30+6]=\r
+{\r
+/* 0x00 - NMI */\r
+0x8D,0xF4,0x3F,       /* Stop play routine NMIs. */\r
+0xA2,0xFF,0x9A,       /* Initialize the stack pointer. */\r
+0xAD,0xF0,0x3F,       /* See if we need to init. */\r
+0xF0,0x09,            /* If 0, go to play routine playing. */\r
+\r
+0xAD,0xF1,0x3F,       /* Confirm and load A      */\r
+0xAE,0xF3,0x3F,       /* Load X with PAL/NTSC byte */\r
+\r
+0x20,0x00,0x00,       /* JSR to init routine     */\r
+\r
+0xA9,0x00,\r
+0xAA,\r
+0xA8,\r
+0x20,0x00,0x00,       /* JSR to play routine  */\r
+0x8D,0xF5,0x3F,        /* Start play routine NMIs. */\r
+0x90,0xFE,             /* Loopie time. */\r
+\r
+/* 0x20 */\r
+0x8D,0xF3,0x3F,        /* Init init NMIs */\r
+0x18,\r
+0x90,0xFE        /* Loopie time. */\r
+};\r
+\r
+static DECLFR(NSFROMRead)\r
+{\r
+ return (NSFROM-0x3800)[A];\r
+}\r
+\r
+static int doreset=0;\r
+static int NSFNMIFlags;\r
+static uint8 *NSFDATA=0;\r
+static int NSFMaxBank;\r
+\r
+static int NSFSize;\r
+static uint8 BSon;\r
+static uint16 PlayAddr;\r
+static uint16 InitAddr;\r
+static uint16 LoadAddr;\r
+\r
+static NSF_HEADER NSFHeader;\r
+\r
+void NSFMMC5_Close(void);\r
+static uint8 *ExWRAM=0;\r
+\r
+void NSFGI(int h)\r
+{\r
+ switch(h)\r
+ {\r
+ case GI_CLOSE:\r
+  if(NSFDATA) {free(NSFDATA);NSFDATA=0;}\r
+  if(ExWRAM) {free(ExWRAM);ExWRAM=0;}\r
+  if(NSFHeader.SoundChip&1) {\r
+//   NSFVRC6_Init();\r
+  } else if(NSFHeader.SoundChip&2) {\r
+//   NSFVRC7_Init();\r
+  } else if(NSFHeader.SoundChip&4) {\r
+//   FDSSoundReset();\r
+  } else if(NSFHeader.SoundChip&8) {\r
+   NSFMMC5_Close();\r
+  } else if(NSFHeader.SoundChip&0x10) {\r
+//   NSFN106_Init();\r
+  } else if(NSFHeader.SoundChip&0x20) {\r
+//   NSFAY_Init();\r
+  }\r
+  break;\r
+ case GI_RESETM2:\r
+ case GI_POWER: NSF_init();break;\r
+ }\r
+}\r
+\r
+// First 32KB is reserved for sound chip emulation in the iNES mapper code.\r
+\r
+static INLINE void BANKSET(uint32 A, uint32 bank)\r
+{\r
+ bank&=NSFMaxBank;\r
+ if(NSFHeader.SoundChip&4)\r
+  memcpy(ExWRAM+(A-0x6000),NSFDATA+(bank<<12),4096);\r
+ else\r
+  setprg4(A,bank);\r
+}\r
+\r
+int NSFLoad(int fp)\r
+{\r
+  int x;\r
+\r
+  FCEU_fseek(fp,0,SEEK_SET);\r
+  FCEU_fread(&NSFHeader,1,0x80,fp);\r
+  if(memcmp(NSFHeader.ID,"NESM\x1a",5))\r
+                  return 0;\r
+  NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0;\r
+\r
+  LoadAddr=NSFHeader.LoadAddressLow;\r
+  LoadAddr|=NSFHeader.LoadAddressHigh<<8;\r
+\r
+  if(LoadAddr<0x6000)\r
+  {\r
+   FCEUD_PrintError("Invalid load address.");\r
+   return(0);\r
+  }\r
+  InitAddr=NSFHeader.InitAddressLow;\r
+  InitAddr|=NSFHeader.InitAddressHigh<<8;\r
+\r
+  PlayAddr=NSFHeader.PlayAddressLow;\r
+  PlayAddr|=NSFHeader.PlayAddressHigh<<8;\r
+\r
+  NSFSize=FCEU_fgetsize(fp)-0x80;\r
+\r
+  NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096);\r
+  NSFMaxBank=uppow2(NSFMaxBank);\r
+\r
+  if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096)))\r
+   return 0;\r
+\r
+  FCEU_fseek(fp,0x80,SEEK_SET);\r
+  memset(NSFDATA,0x00,NSFMaxBank*4096);\r
+  FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp);\r
+\r
+  NSFMaxBank--;\r
+\r
+  BSon=0;\r
+  for(x=0;x<8;x++)\r
+   BSon|=NSFHeader.BankSwitch[x];\r
+\r
+ FCEUGameInfo.type=GIT_NSF;\r
+ FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_GAMEPAD;\r
+ FCEUGameInfo.cspecial=SIS_NSF;\r
+\r
+ for(x=0;;x++)\r
+ {\r
+  if(NSFROM[x]==0x20)\r
+  {\r
+   NSFROM[x+1]=InitAddr&0xFF;\r
+   NSFROM[x+2]=InitAddr>>8;\r
+   NSFROM[x+8]=PlayAddr&0xFF;\r
+   NSFROM[x+9]=PlayAddr>>8;\r
+   break;\r
+  }\r
+ }\r
+\r
+ if(NSFHeader.VideoSystem==0)\r
+  FCEUGameInfo.vidsys=GIV_NTSC;\r
+ else if(NSFHeader.VideoSystem==1)\r
+  FCEUGameInfo.vidsys=GIV_PAL;\r
+\r
+ GameInterface=NSFGI;\r
+\r
+ FCEU_printf("NSF Loaded.  File information:\n\n");\r
+ FCEU_printf(" Name:       %s\n Artist:     %s\n Copyright:  %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright);\r
+ if(NSFHeader.SoundChip)\r
+ {\r
+  static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"};\r
+\r
+  for(x=0;x<6;x++)\r
+   if(NSFHeader.SoundChip&(1<<x))\r
+   {\r
+    FCEU_printf(" Expansion hardware:  %s\n",tab[x]);\r
+    NSFHeader.SoundChip=1<<x;  /* Prevent confusing weirdness if more than one bit is set. */\r
+    break;\r
+   }\r
+ }\r
+ if(BSon)\r
+  FCEU_printf(" Bank-switched.\n");\r
+ FCEU_printf(" Load address:  $%04x\n Init address:  $%04x\n Play address:  $%04x\n",LoadAddr,InitAddr,PlayAddr);\r
+ FCEU_printf(" %s\n",(NSFHeader.VideoSystem&1)?"PAL":"NTSC");\r
+ FCEU_printf(" Starting song:  %d / %d\n\n",NSFHeader.StartingSong,NSFHeader.TotalSongs);\r
+\r
+ if(NSFHeader.SoundChip&4)\r
+  ExWRAM=FCEU_gmalloc(32768+8192);\r
+ else\r
+  ExWRAM=FCEU_gmalloc(8192);\r
+\r
+ FCEUI_SetVidSystem(NSFHeader.VideoSystem);\r
+\r
+ return 1;\r
+}\r
+\r
+static DECLFR(NSFVectorRead)\r
+{\r
+ if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2) || doreset)\r
+ {\r
+  if(A==0xFFFA) return(0x00);\r
+  else if(A==0xFFFB) return(0x38);\r
+  else if(A==0xFFFC) return(0x20);\r
+  else if(A==0xFFFD) {doreset=0;return(0x38);}\r
+  return(X.DB);\r
+ }\r
+ else\r
+  return(CartBR(A));\r
+}\r
+\r
+void NSFVRC6_Init(void);\r
+void NSFVRC7_Init(void);\r
+void NSFMMC5_Init(void);\r
+void NSFN106_Init(void);\r
+void NSFAY_Init(void);\r
+\r
+void NSF_init(void)\r
+{\r
+  doreset=1;\r
+\r
+  ResetCartMapping();\r
+  if(NSFHeader.SoundChip&4)\r
+  {\r
+   SetupCartPRGMapping(0,ExWRAM,32768+8192,1);\r
+   setprg32(0x6000,0);\r
+   setprg8(0xE000,4);\r
+   memset(ExWRAM,0x00,32768+8192);\r
+   SetWriteHandler(0x6000,0xDFFF,CartBW);\r
+   SetReadHandler(0x6000,0xFFFF,CartBR);\r
+  }\r
+  else\r
+  {\r
+   memset(ExWRAM,0x00,8192);\r
+   SetReadHandler(0x6000,0x7FFF,CartBR);\r
+   SetWriteHandler(0x6000,0x7FFF,CartBW);\r
+   SetupCartPRGMapping(0,NSFDATA,((NSFMaxBank+1)*4096),0);\r
+   SetupCartPRGMapping(1,ExWRAM,8192,1);\r
+   setprg8r(1,0x6000,0);\r
+   SetReadHandler(0x8000,0xFFFF,CartBR);\r
+  }\r
+\r
+  if(BSon)\r
+  {\r
+   int32 x;\r
+   for(x=0;x<8;x++)\r
+   {\r
+    if(NSFHeader.SoundChip&4 && x>=6)\r
+     BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]);\r
+    BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
+   }\r
+  }\r
+  else\r
+  {\r
+   int32 x;\r
+   for(x=(LoadAddr&0xF000);x<0x10000;x+=0x1000)\r
+    BANKSET(x,((x-(LoadAddr&0x7000))>>12));\r
+  }\r
+\r
+  SetReadHandler(0xFFFA,0xFFFD,NSFVectorRead);\r
+\r
+  SetWriteHandler(0x2000,0x3fff,0);\r
+  SetReadHandler(0x2000,0x37ff,0);\r
+  SetReadHandler(0x3836,0x3FFF,0);\r
+  SetReadHandler(0x3800,0x3835,NSFROMRead);\r
+\r
+  SetWriteHandler(0x5ff6,0x5fff,NSF_write);\r
+\r
+  SetWriteHandler(0x3ff0,0x3fff,NSF_write);\r
+  SetReadHandler(0x3ff0,0x3fff,NSF_read);\r
+\r
+\r
+  if(NSFHeader.SoundChip&1) {\r
+   NSFVRC6_Init();\r
+  } else if(NSFHeader.SoundChip&2) {\r
+   NSFVRC7_Init();\r
+  } else if(NSFHeader.SoundChip&4) {\r
+   FDSSoundReset();\r
+  } else if(NSFHeader.SoundChip&8) {\r
+   NSFMMC5_Init();\r
+  } else if(NSFHeader.SoundChip&0x10) {\r
+   NSFN106_Init();\r
+  } else if(NSFHeader.SoundChip&0x20) {\r
+   NSFAY_Init();\r
+  }\r
+  CurrentSong=NSFHeader.StartingSong;\r
+  SongReload=0xFF;\r
+  NSFNMIFlags=0;\r
+}\r
+\r
+static DECLFW(NSF_write)\r
+{\r
+ switch(A)\r
+ {\r
+  case 0x3FF3:NSFNMIFlags|=1;break;\r
+  case 0x3FF4:NSFNMIFlags&=~2;break;\r
+  case 0x3FF5:NSFNMIFlags|=2;break;\r
+\r
+  case 0x5FF6:\r
+  case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return;\r
+  case 0x5FF8:\r
+  case 0x5FF9:\r
+  case 0x5FFA:\r
+  case 0x5FFB:\r
+  case 0x5FFC:\r
+  case 0x5FFD:\r
+  case 0x5FFE:\r
+  case 0x5FFF:if(!BSon) return;\r
+              A&=0xF;\r
+              BANKSET((A*4096),V);\r
+             break;\r
+ }\r
+}\r
+\r
+static DECLFR(NSF_read)\r
+{\r
+ int x;\r
+\r
+ switch(A)\r
+ {\r
+ case 0x3ff0:x=SongReload;\r
+            if(!fceuindbg)\r
+             SongReload=0;\r
+            return x;\r
+ case 0x3ff1:\r
+           if(!fceuindbg)\r
+           {\r
+             memset(RAM,0x00,0x800);\r
+\r
+             BWrite[0x4015](0x4015,0x0);\r
+             for(x=0;x<0x14;x++)\r
+              BWrite[0x4000+x](0x4000+x,0);\r
+             BWrite[0x4015](0x4015,0xF);\r
+\r
+            if(NSFHeader.SoundChip&4)\r
+            {\r
+        BWrite[0x4017](0x4017,0xC0);  /* FDS BIOS writes $C0 */\r
+             BWrite[0x4089](0x4089,0x80);\r
+             BWrite[0x408A](0x408A,0xE8);\r
+            }\r
+            else\r
+            {\r
+             memset(ExWRAM,0x00,8192);\r
+             BWrite[0x4017](0x4017,0xC0);\r
+              BWrite[0x4017](0x4017,0xC0);\r
+              BWrite[0x4017](0x4017,0x40);\r
+            }\r
+\r
+             if(BSon)\r
+             {\r
+              for(x=0;x<8;x++)\r
+              BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
+             }\r
+             return (CurrentSong-1);\r
+            }\r
+ case 0x3FF3:return PAL;\r
+ }\r
+ return 0;\r
+}\r
+\r
+uint8 FCEU_GetJoyJoy(void);\r
+\r
+static int special=0;\r
+\r
+void DrawNSF(uint8 *XBuf)\r
+{\r
+ char snbuf[16];\r
+ int x;\r
+\r
+ if(vismode==0) return;\r
+\r
+ memset(XBuf,0,256*240);\r
+\r
+\r
+ {\r
+  int32 *Bufpl;\r
+  int32 mul=0;\r
+\r
+  int l;\r
+  l=GetSoundBuffer(&Bufpl);\r
+\r
+  if(special==0)\r
+  {\r
+   if(FSettings.SoundVolume)\r
+    mul=8192*240/(16384*FSettings.SoundVolume/50);\r
+   for(x=0;x<256;x++)\r
+   {\r
+    uint32 y;\r
+    y=142+((Bufpl[(x*l)>>8]*mul)>>14);\r
+    if(y<240)\r
+     XBuf[x+y*256]=3;\r
+   }\r
+  }\r
+  else if(special==1)\r
+  {\r
+   if(FSettings.SoundVolume)\r
+    mul=8192*240/(8192*FSettings.SoundVolume/50);\r
+   for(x=0;x<256;x++)\r
+   {\r
+    double r;\r
+    uint32 xp,yp;\r
+\r
+    r=(Bufpl[(x*l)>>8]*mul)>>14;\r
+    xp=128+r*cos(x*M_PI*2/256);\r
+    yp=120+r*sin(x*M_PI*2/256);\r
+    xp&=255;\r
+    yp%=240;\r
+    XBuf[xp+yp*256]=3;\r
+   }\r
+  }\r
+  else if(special==2)\r
+  {\r
+   static double theta=0;\r
+   if(FSettings.SoundVolume)\r
+    mul=8192*240/(16384*FSettings.SoundVolume/50);\r
+   for(x=0;x<128;x++)\r
+   {\r
+    double xc,yc;\r
+    double r,t;\r
+    uint32 m,n;\r
+\r
+    xc=(double)128-x;\r
+    yc=0-((double)( ((Bufpl[(x*l)>>8]) *mul)>>14));\r
+    t=M_PI+atan(yc/xc);\r
+    r=sqrt(xc*xc+yc*yc);\r
+\r
+    t+=theta;\r
+    m=128+r*cos(t);\r
+    n=120+r*sin(t);\r
+\r
+    if(m<256 && n<240)\r
+     XBuf[m+n*256]=3;\r
+\r
+   }\r
+   for(x=128;x<256;x++)\r
+   {\r
+    double xc,yc;\r
+    double r,t;\r
+    uint32 m,n;\r
+\r
+    xc=(double)x-128;\r
+    yc=(double)((Bufpl[(x*l)>>8]*mul)>>14);\r
+    t=atan(yc/xc);\r
+    r=sqrt(xc*xc+yc*yc);\r
+\r
+    t+=theta;\r
+    m=128+r*cos(t);\r
+    n=120+r*sin(t);\r
+\r
+    if(m<256 && n<240)\r
+     XBuf[m+n*256]=3;\r
+\r
+   }\r
+   theta+=(double)M_PI/256;\r
+  }\r
+ }\r
+\r
+ DrawTextTrans(XBuf+10*256+4+(((31-strlen((char*)NSFHeader.SongName))<<2)), 256, NSFHeader.SongName, 6);\r
+ DrawTextTrans(XBuf+26*256+4+(((31-strlen((char*)NSFHeader.Artist))<<2)), 256,NSFHeader.Artist, 6);\r
+ DrawTextTrans(XBuf+42*256+4+(((31-strlen((char*)NSFHeader.Copyright))<<2)), 256,NSFHeader.Copyright, 6);\r
+\r
+ DrawTextTrans(XBuf+70*256+4+(((31-strlen("Song:"))<<2)), 256, (uint8*)"Song:", 6);\r
+ sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);\r
+ DrawTextTrans(XBuf+82*256+4+(((31-strlen(snbuf))<<2)), 256, (uint8*)snbuf, 6);\r
+\r
+ {\r
+  static uint8 last=0;\r
+  uint8 tmp;\r
+  tmp=FCEU_GetJoyJoy();\r
+  if((tmp&JOY_RIGHT) && !(last&JOY_RIGHT))\r
+  {\r
+   if(CurrentSong<NSFHeader.TotalSongs)\r
+   {\r
+    CurrentSong++;\r
+    SongReload=0xFF;\r
+   }\r
+  }\r
+  else if((tmp&JOY_LEFT) && !(last&JOY_LEFT))\r
+  {\r
+   if(CurrentSong>1)\r
+   {\r
+    CurrentSong--;\r
+    SongReload=0xFF;\r
+   }\r
+  }\r
+  else if((tmp&JOY_UP) && !(last&JOY_UP))\r
+  {\r
+   CurrentSong+=10;\r
+   if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
+   SongReload=0xFF;\r
+  }\r
+  else if((tmp&JOY_DOWN) && !(last&JOY_DOWN))\r
+  {\r
+   CurrentSong-=10;\r
+   if(CurrentSong<1) CurrentSong=1;\r
+   SongReload=0xFF;\r
+  }\r
+  else if((tmp&JOY_START) && !(last&JOY_START))\r
+   SongReload=0xFF;\r
+  else if((tmp&JOY_A) && !(last&JOY_A))\r
+  {\r
+   special=(special+1)%3;\r
+  }\r
+  last=tmp;\r
+ }\r
+}\r
+\r
+void DoNSFFrame(void)\r
+{\r
+ if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))\r
+  TriggerNMI();\r
+}\r
+\r
+void FCEUI_NSFSetVis(int mode)\r
+{\r
+ vismode=mode;\r
+}\r
+\r
+int FCEUI_NSFChange(int amount)\r
+{\r
+   CurrentSong+=amount;\r
+   if(CurrentSong<1) CurrentSong=1;\r
+   else if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
+   SongReload=0xFF;\r
+\r
+   return(CurrentSong);\r
+}\r
+\r
+/* Returns total songs */\r
+int FCEUI_NSFGetInfo(uint8 *name, uint8 *artist, uint8 *copyright, int maxlen)\r
+{\r
+ strncpy((char*)name,(char*)NSFHeader.SongName,maxlen);\r
+ strncpy((char*)artist,(char*)NSFHeader.Artist,maxlen);\r
+ strncpy((char*)copyright,(char*)NSFHeader.Copyright,maxlen);\r
+ return(NSFHeader.TotalSongs);\r
+}\r