1 /* FCE Ultra - NES/Famicom Emulator
\r
3 * Copyright notice for this file:
\r
4 * Copyright (C) 2002 Xodnizel
\r
6 * This program is free software; you can redistribute it and/or modify
\r
7 * it under the terms of the GNU General Public License as published by
\r
8 * the Free Software Foundation; either version 2 of the License, or
\r
9 * (at your option) any later version.
\r
11 * This program is distributed in the hope that it will be useful,
\r
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
\r
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
\r
14 * GNU General Public License for more details.
\r
16 * You should have received a copy of the GNU General Public License
\r
17 * along with this program; if not, write to the Free Software
\r
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
\r
32 #include "general.h"
\r
42 #define M_PI 3.14159265358979323846
\r
45 #define SCREEN_WIDTH 320
\r
46 #define SCREEN_OFFS 32
\r
48 static uint8 SongReload;
\r
49 static int CurrentSong;
\r
51 static DECLFW(NSF_write);
\r
52 static DECLFR(NSF_read);
\r
54 static int vismode=1;
\r
56 static uint8 NSFROM[0x30+6]=
\r
59 0x8D,0xF4,0x3F, /* Stop play routine NMIs. */
\r
60 0xA2,0xFF,0x9A, /* Initialize the stack pointer. */
\r
61 0xAD,0xF0,0x3F, /* See if we need to init. */
\r
62 0xF0,0x09, /* If 0, go to play routine playing. */
\r
64 0xAD,0xF1,0x3F, /* Confirm and load A */
\r
65 0xAE,0xF3,0x3F, /* Load X with PAL/NTSC byte */
\r
67 0x20,0x00,0x00, /* JSR to init routine */
\r
72 0x20,0x00,0x00, /* JSR to play routine */
\r
73 0x8D,0xF5,0x3F, /* Start play routine NMIs. */
\r
74 0x90,0xFE, /* Loopie time. */
\r
77 0x8D,0xF3,0x3F, /* Init init NMIs */
\r
79 0x90,0xFE /* Loopie time. */
\r
82 static DECLFR(NSFROMRead)
\r
84 return (NSFROM-0x3800)[A];
\r
87 static int doreset=0;
\r
88 static int NSFNMIFlags;
\r
89 static uint8 *NSFDATA=0;
\r
90 static int NSFMaxBank;
\r
94 static uint16 PlayAddr;
\r
95 static uint16 InitAddr;
\r
96 static uint16 LoadAddr;
\r
98 static NSF_HEADER NSFHeader;
\r
100 void NSFMMC5_Close(void);
\r
101 static uint8 *ExWRAM=0;
\r
108 if(NSFDATA) {free(NSFDATA);NSFDATA=0;}
\r
109 if(ExWRAM) {free(ExWRAM);ExWRAM=0;}
\r
110 if(NSFHeader.SoundChip&1) {
\r
112 } else if(NSFHeader.SoundChip&2) {
\r
114 } else if(NSFHeader.SoundChip&4) {
\r
115 // FDSSoundReset();
\r
116 } else if(NSFHeader.SoundChip&8) {
\r
118 } else if(NSFHeader.SoundChip&0x10) {
\r
120 } else if(NSFHeader.SoundChip&0x20) {
\r
125 case GI_POWER: NSF_init();break;
\r
129 // First 32KB is reserved for sound chip emulation in the iNES mapper code.
\r
131 static INLINE void BANKSET(uint32 A, uint32 bank)
\r
134 if(NSFHeader.SoundChip&4)
\r
135 memcpy(ExWRAM+(A-0x6000),NSFDATA+(bank<<12),4096);
\r
140 int NSFLoad(int fp)
\r
144 FCEU_fseek(fp,0,SEEK_SET);
\r
145 FCEU_fread(&NSFHeader,1,0x80,fp);
\r
146 if(memcmp(NSFHeader.ID,"NESM\x1a",5))
\r
148 NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0;
\r
150 LoadAddr=NSFHeader.LoadAddressLow;
\r
151 LoadAddr|=NSFHeader.LoadAddressHigh<<8;
\r
153 if(LoadAddr<0x6000)
\r
155 FCEUD_PrintError("Invalid load address.");
\r
158 InitAddr=NSFHeader.InitAddressLow;
\r
159 InitAddr|=NSFHeader.InitAddressHigh<<8;
\r
161 PlayAddr=NSFHeader.PlayAddressLow;
\r
162 PlayAddr|=NSFHeader.PlayAddressHigh<<8;
\r
164 NSFSize=FCEU_fgetsize(fp)-0x80;
\r
166 NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096);
\r
167 NSFMaxBank=uppow2(NSFMaxBank);
\r
169 if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096)))
\r
172 FCEU_fseek(fp,0x80,SEEK_SET);
\r
173 memset(NSFDATA,0x00,NSFMaxBank*4096);
\r
174 FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp);
\r
180 BSon|=NSFHeader.BankSwitch[x];
\r
182 FCEUGameInfo.type=GIT_NSF;
\r
183 FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_GAMEPAD;
\r
184 FCEUGameInfo.cspecial=SIS_NSF;
\r
188 if(NSFROM[x]==0x20)
\r
190 NSFROM[x+1]=InitAddr&0xFF;
\r
191 NSFROM[x+2]=InitAddr>>8;
\r
192 NSFROM[x+8]=PlayAddr&0xFF;
\r
193 NSFROM[x+9]=PlayAddr>>8;
\r
198 if(NSFHeader.VideoSystem==0)
\r
199 FCEUGameInfo.vidsys=GIV_NTSC;
\r
200 else if(NSFHeader.VideoSystem==1)
\r
201 FCEUGameInfo.vidsys=GIV_PAL;
\r
203 GameInterface=NSFGI;
\r
205 FCEU_printf("NSF Loaded. File information:\n\n");
\r
206 FCEU_printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright);
\r
207 if(NSFHeader.SoundChip)
\r
209 static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"};
\r
212 if(NSFHeader.SoundChip&(1<<x))
\r
214 FCEU_printf(" Expansion hardware: %s\n",tab[x]);
\r
215 NSFHeader.SoundChip=1<<x; /* Prevent confusing weirdness if more than one bit is set. */
\r
220 FCEU_printf(" Bank-switched.\n");
\r
221 FCEU_printf(" Load address: $%04x\n Init address: $%04x\n Play address: $%04x\n",LoadAddr,InitAddr,PlayAddr);
\r
222 FCEU_printf(" %s\n",(NSFHeader.VideoSystem&1)?"PAL":"NTSC");
\r
223 FCEU_printf(" Starting song: %d / %d\n\n",NSFHeader.StartingSong,NSFHeader.TotalSongs);
\r
225 if(NSFHeader.SoundChip&4)
\r
226 ExWRAM=FCEU_gmalloc(32768+8192);
\r
228 ExWRAM=FCEU_gmalloc(8192);
\r
230 FCEUI_SetVidSystem(NSFHeader.VideoSystem);
\r
235 static DECLFR(NSFVectorRead)
\r
237 if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2) || doreset)
\r
239 if(A==0xFFFA) return(0x00);
\r
240 else if(A==0xFFFB) return(0x38);
\r
241 else if(A==0xFFFC) return(0x20);
\r
242 else if(A==0xFFFD) {doreset=0;return(0x38);}
\r
249 void NSFVRC6_Init(void);
\r
250 void NSFVRC7_Init(void);
\r
251 void NSFMMC5_Init(void);
\r
252 void NSFN106_Init(void);
\r
253 void NSFAY_Init(void);
\r
255 void NSF_init(void)
\r
259 ResetCartMapping();
\r
260 if(NSFHeader.SoundChip&4)
\r
262 SetupCartPRGMapping(0,ExWRAM,32768+8192,1);
\r
263 setprg32(0x6000,0);
\r
265 memset(ExWRAM,0x00,32768+8192);
\r
266 SetWriteHandler(0x6000,0xDFFF,CartBW);
\r
267 SetReadHandler(0x6000,0xFFFF,CartBR);
\r
271 memset(ExWRAM,0x00,8192);
\r
272 SetReadHandler(0x6000,0x7FFF,CartBR);
\r
273 SetWriteHandler(0x6000,0x7FFF,CartBW);
\r
274 SetupCartPRGMapping(0,NSFDATA,((NSFMaxBank+1)*4096),0);
\r
275 SetupCartPRGMapping(1,ExWRAM,8192,1);
\r
276 setprg8r(1,0x6000,0);
\r
277 SetReadHandler(0x8000,0xFFFF,CartBR);
\r
285 if(NSFHeader.SoundChip&4 && x>=6)
\r
286 BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]);
\r
287 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);
\r
293 for(x=(LoadAddr&0xF000);x<0x10000;x+=0x1000)
\r
294 BANKSET(x,((x-(LoadAddr&0x7000))>>12));
\r
297 SetReadHandler(0xFFFA,0xFFFD,NSFVectorRead);
\r
299 SetWriteHandler(0x2000,0x3fff,0);
\r
300 SetReadHandler(0x2000,0x37ff,0);
\r
301 SetReadHandler(0x3836,0x3FFF,0);
\r
302 SetReadHandler(0x3800,0x3835,NSFROMRead);
\r
303 Page[0x3800>>11]=NSFROM-0x3800; // this is required for asm core to work.
\r
305 SetWriteHandler(0x5ff6,0x5fff,NSF_write);
\r
307 SetWriteHandler(0x3ff0,0x3fff,NSF_write);
\r
308 SetReadHandler(0x3ff0,0x3fff,NSF_read);
\r
311 if(NSFHeader.SoundChip&1) {
\r
313 } else if(NSFHeader.SoundChip&2) {
\r
315 } else if(NSFHeader.SoundChip&4) {
\r
317 } else if(NSFHeader.SoundChip&8) {
\r
319 } else if(NSFHeader.SoundChip&0x10) {
\r
321 } else if(NSFHeader.SoundChip&0x20) {
\r
324 CurrentSong=NSFHeader.StartingSong;
\r
329 static DECLFW(NSF_write)
\r
333 case 0x3FF3:NSFNMIFlags|=1;break;
\r
334 case 0x3FF4:NSFNMIFlags&=~2;break;
\r
335 case 0x3FF5:NSFNMIFlags|=2;break;
\r
338 case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return;
\r
346 case 0x5FFF:if(!BSon) return;
\r
348 BANKSET((A*4096),V);
\r
353 static DECLFR(NSF_read)
\r
359 case 0x3ff0:x=SongReload;
\r
366 memset(RAM,0x00,0x800);
\r
368 BWrite[0x4015](0x4015,0x0);
\r
369 for(x=0;x<0x14;x++)
\r
370 BWrite[0x4000+x](0x4000+x,0);
\r
371 BWrite[0x4015](0x4015,0xF);
\r
373 if(NSFHeader.SoundChip&4)
\r
375 BWrite[0x4017](0x4017,0xC0); /* FDS BIOS writes $C0 */
\r
376 BWrite[0x4089](0x4089,0x80);
\r
377 BWrite[0x408A](0x408A,0xE8);
\r
381 memset(ExWRAM,0x00,8192);
\r
382 BWrite[0x4017](0x4017,0xC0);
\r
383 BWrite[0x4017](0x4017,0xC0);
\r
384 BWrite[0x4017](0x4017,0x40);
\r
390 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);
\r
392 return (CurrentSong-1);
\r
394 case 0x3FF3:return PAL;
\r
399 uint8 FCEU_GetJoyJoy(void);
\r
401 static int special=0;
\r
403 void DrawNSF(uint8 *XBuf)
\r
408 if(vismode==0) return;
\r
410 for (x=0;x<240;x++)
\r
411 memset(XBuf+SCREEN_OFFS+x*SCREEN_WIDTH,0,256);
\r
418 l=GetSoundBuffer(&Bufpl);
\r
422 if(FSettings.SoundVolume)
\r
423 mul=8192*240/(16384*FSettings.SoundVolume/50);
\r
427 y=142+((Bufpl[(x*l)>>8]*mul)>>14);
\r
429 XBuf[x+y*SCREEN_WIDTH+SCREEN_OFFS]=3;
\r
432 else if(special==1)
\r
434 if(FSettings.SoundVolume)
\r
435 mul=8192*240/(8192*FSettings.SoundVolume/50);
\r
441 r=(Bufpl[(x*l)>>8]*mul)>>14;
\r
442 xp=128+r*cos(x*M_PI*2/256);
\r
443 yp=120+r*sin(x*M_PI*2/256);
\r
446 XBuf[xp+yp*SCREEN_WIDTH+SCREEN_OFFS]=3;
\r
449 else if(special==2)
\r
451 static double theta=0;
\r
452 if(FSettings.SoundVolume)
\r
453 mul=8192*240/(16384*FSettings.SoundVolume/50);
\r
461 yc=0-((double)( ((Bufpl[(x*l)>>8]) *mul)>>14));
\r
462 t=M_PI+atan(yc/xc);
\r
463 r=sqrt(xc*xc+yc*yc);
\r
470 XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;
\r
473 for(x=128;x<256;x++)
\r
480 yc=(double)((Bufpl[(x*l)>>8]*mul)>>14);
\r
482 r=sqrt(xc*xc+yc*yc);
\r
489 XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;
\r
492 theta+=(double)M_PI/256;
\r
496 DrawTextTrans(XBuf+10*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen((char*)NSFHeader.SongName))<<2)), SCREEN_WIDTH, NSFHeader.SongName, 6);
\r
497 DrawTextTrans(XBuf+26*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen((char*)NSFHeader.Artist))<<2)), SCREEN_WIDTH,NSFHeader.Artist, 6);
\r
498 DrawTextTrans(XBuf+42*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen((char*)NSFHeader.Copyright))<<2)), SCREEN_WIDTH,NSFHeader.Copyright, 6);
\r
500 DrawTextTrans(XBuf+70*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen("Song:"))<<2)), SCREEN_WIDTH, (uint8*)"Song:", 6);
\r
501 sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);
\r
502 DrawTextTrans(XBuf+82*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen(snbuf))<<2)), SCREEN_WIDTH, (uint8*)snbuf, 6);
\r
505 static uint8 last=0;
\r
507 tmp=FCEU_GetJoyJoy();
\r
508 if((tmp&JOY_RIGHT) && !(last&JOY_RIGHT))
\r
510 if(CurrentSong<NSFHeader.TotalSongs)
\r
516 else if((tmp&JOY_LEFT) && !(last&JOY_LEFT))
\r
524 else if((tmp&JOY_UP) && !(last&JOY_UP))
\r
527 if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;
\r
530 else if((tmp&JOY_DOWN) && !(last&JOY_DOWN))
\r
533 if(CurrentSong<1) CurrentSong=1;
\r
536 else if((tmp&JOY_START) && !(last&JOY_START))
\r
538 else if((tmp&JOY_A) && !(last&JOY_A))
\r
540 special=(special+1)%3;
\r
546 void DoNSFFrame(void)
\r
548 if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))
\r
552 void FCEUI_NSFSetVis(int mode)
\r
557 int FCEUI_NSFChange(int amount)
\r
559 CurrentSong+=amount;
\r
560 if(CurrentSong<1) CurrentSong=1;
\r
561 else if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;
\r
564 return(CurrentSong);
\r
567 /* Returns total songs */
\r
568 int FCEUI_NSFGetInfo(uint8 *name, uint8 *artist, uint8 *copyright, int maxlen)
\r
570 strncpy((char*)name,(char*)NSFHeader.SongName,maxlen);
\r
571 strncpy((char*)artist,(char*)NSFHeader.Artist,maxlen);
\r
572 strncpy((char*)copyright,(char*)NSFHeader.Copyright,maxlen);
\r
573 return(NSFHeader.TotalSongs);
\r