1 /* FCE Ultra - NES/Famicom Emulator
3 * Copyright notice for this file:
4 * Copyright (C) 2002 Ben Parnell
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
43 #define M_PI 3.14159265358979323846
49 static int sinetable[32];
52 static uint8 NSFROM[0x30+6]=
56 0x08,0x48,0x8A,0x48,0x98,0x48, /* Store regs */
58 0x8D,0xF2,0x5F, /* NMI has occured */
59 0x68,0xA8,0x68,0xAA,0x68,0x28,
60 0x40, /* Restore regs */
64 0xAD,0xF2,0x5F, /* See if an NMI occured */
65 0xF0,0xFB, /* If it hasn't, loop */
68 0x8D,0xF2,0x5F, /* Clear play pending reg*/
71 0xAD,0xF0,0x5F, /* See if we need to init. */
72 0xF0,0x09, /* If 0, go to JMP */
74 0xAD,0xF1,0x5F, /* Confirm and load A */
75 0xAE,0xF3,0x5F, /* Load X with PAL/NTSC byte */
77 0x20,0x00,0x00, /* JSR to init routine */
79 0x20,0x00,0x00, /* JSR to play routine */
81 0x4C,0x12,0x38, /* Loop */
83 0xA2,0xFF,0x9A, /* Initialize the stack pointer. */
87 static DECLFR(NSFROMRead)
89 return (NSFROM-0x3800)[A];
94 static uint8 *NSFDATA=0;
95 static int NSFMaxBank;
99 static uint16 PlayAddr;
100 static uint16 InitAddr;
101 static uint16 LoadAddr;
103 NSF_HEADER NSFHeader;
110 if(NSFDATA) {free(NSFDATA);NSFDATA=0;}
112 case GI_POWER: NSF_init();break;
116 // First 32KB is reserved for sound chip emulation in the iNES mapper code.
118 #define WRAM (GameMemBlock+32768)
119 #define FDSMEM (GameMemBlock+32768)
121 static INLINE void BANKSET(uint32 A, uint32 bank)
124 if(NSFHeader.SoundChip&4)
125 memcpy(FDSMEM+(A-0x6000),NSFDATA+(bank<<12),4096);
134 FCEU_fseek(fp,0,SEEK_SET);
135 FCEU_fread(&NSFHeader,1,0x80,fp);
136 if (memcmp(NSFHeader.ID,"NESM\x1a",5))
138 NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0;
140 LoadAddr=NSFHeader.LoadAddressLow;
141 LoadAddr|=NSFHeader.LoadAddressHigh<<8;
143 InitAddr=NSFHeader.InitAddressLow;
144 InitAddr|=NSFHeader.InitAddressHigh<<8;
146 PlayAddr=NSFHeader.PlayAddressLow;
147 PlayAddr|=NSFHeader.PlayAddressHigh<<8;
149 NSFSize=FCEU_fgetsize(fp)-0x80;
151 NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096);
152 NSFMaxBank=uppow2(NSFMaxBank);
154 if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096)))
157 FCEU_fseek(fp,0x80,SEEK_SET);
158 memset(NSFDATA,0x00,NSFMaxBank*4096);
159 FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp);
165 BSon|=NSFHeader.BankSwitch[x];
167 FCEUGameInfo.type=GIT_NSF;
168 FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_NONE;
174 NSFROM[x+1]=InitAddr&0xFF;
175 NSFROM[x+2]=InitAddr>>8;
176 NSFROM[x+4]=PlayAddr&0xFF;
177 NSFROM[x+5]=PlayAddr>>8;
182 if(NSFHeader.VideoSystem==0)
183 FCEUGameInfo.vidsys=GIV_NTSC;
184 else if(NSFHeader.VideoSystem==1)
185 FCEUGameInfo.vidsys=GIV_PAL;
196 fruit+=(double)M_PI*2/32;
201 puts("NSF Loaded. File information:\n");
202 printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright);
203 if(NSFHeader.SoundChip)
205 static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"};
207 if(NSFHeader.SoundChip&(1<<x))
209 printf(" Expansion hardware: %s\n",tab[x]);
214 puts(" Bank-switched.");
215 printf(" Load address: $%04x\n Init address: $%04x\n Play address: $%04x\n",LoadAddr,InitAddr,PlayAddr);
216 printf(" %s\n",(NSFHeader.VideoSystem&1)?"PAL":"NTSC");
217 printf(" Starting song: %d / %d\n\n",NSFHeader.StartingSong,NSFHeader.TotalSongs);
226 static DECLFW(NSFFDSWrite)
228 (FDSMEM-0x6000)[A]=V;
231 static DECLFR(NSFFDSRead)
233 return (FDSMEM-0x6000)[A];
238 return WRAM[A-0x6000];
243 if(NSFHeader.SoundChip&4)
245 memset(FDSMEM,0x00,32768+8192);
246 SetWriteHandler(0x6000,0xDFFF,NSFFDSWrite);
247 SetReadHandler(0x6000,0xFFFF,NSFFDSRead);
251 memset(WRAM,0x00,8192);
252 SetReadHandler(0x6000,0x7FFF,AWRAM);
253 SetWriteHandler(0x6000,0x7FFF,BWRAM);
255 SetupCartPRGMapping(0,NSFDATA,((NSFMaxBank+1)*4096),0);
256 SetReadHandler(0x8000,0xFFFF,CartBR);
264 if(NSFHeader.SoundChip&4 && x>=6)
265 BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]);
266 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);
272 for(x=(LoadAddr&0x7000);x<0x8000;x+=0x1000)
273 BANKSET(0x8000+x,((x-(LoadAddr&0x7000))>>12));
276 SetWriteHandler(0x2000,0x3fff,0);
277 SetReadHandler(0x2000,0x37ff,0);
278 SetReadHandler(0x3836,0x3FFF,0);
279 SetReadHandler(0x3800,0x3835,NSFROMRead);
281 SetWriteHandler(0x4020,0x5fff,NSF_write);
282 SetReadHandler(0x4020,0x5fff,NSF_read);
285 if(NSFHeader.SoundChip&1) {
287 } else if (NSFHeader.SoundChip&2) {
289 } else if (NSFHeader.SoundChip&4) {
291 } else if (NSFHeader.SoundChip&8) {
293 } else if (NSFHeader.SoundChip&0x10) {
295 } else if (NSFHeader.SoundChip&0x20) {
298 CurrentSong=NSFHeader.StartingSong;
302 static uint8 DoUpdateStuff=0;
307 case 0x5FF2:if((X.PC&0xF000)==0x3000) DoUpdateStuff=V;break;
310 case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return;
318 case 0x5FFF:if(!BSon) return;
329 if((X.PC&0xF000)==0x3000)
332 case 0x5ff0:x=SongReload;SongReload=0;return x;
335 memset(RAM,0x00,0x800);
336 memset(WRAM,0x00,8192);
337 BWrite[0x4015](0x4015,0xF);
339 {if(x!=0x11) BWrite[0x4015](0x4015,0);}
340 BWrite[0x4015](0x4015,0x0);
342 {if(x!=0x11) BWrite[0x4015](0x4015,0);}
343 BWrite[0x4011](0x4011,0x40);
344 BWrite[0x4015](0x4015,0xF);
345 BWrite[0x4017](0x4017,0x40);
346 if(NSFHeader.SoundChip&4)
347 BWrite[0x4089](0x4089,0x80);
351 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);
353 return (CurrentSong-1);
355 case 0x5FF2:return DoUpdateStuff;
356 case 0x5FF3:return PAL;
361 void DrawNSF(uint8 *XBuf)
376 offs=tmpb+sinetable[((z+y)>>2)&31];
377 memcpy(XBuf2,offs,256);
378 memcpy(XBuf2+(120*272),offs,256);
386 DrawTextTrans(XBuf+10*272+4+(((31-strlen(NSFHeader.SongName))<<2)), 272, NSFHeader.SongName, 38);
387 DrawTextTrans(XBuf+30*272+4+(((31-strlen(NSFHeader.Artist))<<2)), 272, NSFHeader.Artist, 38);
388 DrawTextTrans(XBuf+50*272+4+(((31-strlen(NSFHeader.Copyright))<<2)), 272, NSFHeader.Copyright, 38);
390 DrawTextTrans(XBuf+90*272+4+(((31-strlen("Song:"))<<2)), 272, "Song:", 38);
391 sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);
392 DrawTextTrans(XBuf+102*272+4+(((31-strlen(snbuf))<<2)), 272, snbuf, 38);
394 GetSoundBuffer(&Bufpl);
396 XBuf[x+(224-((((Bufpl[x]>>(7)^128)&255)*3)>>3))*272]=38;
399 void NSFControl(int z)
403 if(CurrentSong<NSFHeader.TotalSongs) CurrentSong++;
407 if(CurrentSong>1) CurrentSong--;