smb3 and addams family hacks
[fceu.git] / nsf.c
CommitLineData
d97315ac 1/* FCE Ultra - NES/Famicom Emulator\r
2 *\r
3 * Copyright notice for this file:\r
4 * Copyright (C) 2002 Xodnizel\r
5 *\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
10 *\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
15 *\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
19 */\r
20\r
21#include <stdio.h>\r
22#include <stdlib.h>\r
23#include <string.h>\r
24#include <math.h>\r
25\r
26#include "types.h"\r
27#include "x6502.h"\r
28#include "fce.h"\r
29#include "video.h"\r
30#include "sound.h"\r
31#include "nsf.h"\r
32#include "general.h"\r
33#include "memory.h"\r
34#include "file.h"\r
35#include "fds.h"\r
36#include "cart.h"\r
37#include "input.h"\r
38\r
39#include "svga.h"\r
40\r
41#ifndef M_PI\r
42#define M_PI 3.14159265358979323846\r
43#endif\r
44\r
92764e62 45#define SCREEN_WIDTH 320\r
46#define SCREEN_OFFS 32\r
47\r
d97315ac 48static uint8 SongReload;\r
49static int CurrentSong;\r
50\r
51static DECLFW(NSF_write);\r
52static DECLFR(NSF_read);\r
53\r
54static int vismode=1;\r
55\r
56static uint8 NSFROM[0x30+6]=\r
57{\r
58/* 0x00 - NMI */\r
590x8D,0xF4,0x3F, /* Stop play routine NMIs. */\r
600xA2,0xFF,0x9A, /* Initialize the stack pointer. */\r
610xAD,0xF0,0x3F, /* See if we need to init. */\r
620xF0,0x09, /* If 0, go to play routine playing. */\r
63\r
640xAD,0xF1,0x3F, /* Confirm and load A */\r
650xAE,0xF3,0x3F, /* Load X with PAL/NTSC byte */\r
66\r
670x20,0x00,0x00, /* JSR to init routine */\r
68\r
690xA9,0x00,\r
700xAA,\r
710xA8,\r
720x20,0x00,0x00, /* JSR to play routine */\r
730x8D,0xF5,0x3F, /* Start play routine NMIs. */\r
740x90,0xFE, /* Loopie time. */\r
75\r
76/* 0x20 */\r
770x8D,0xF3,0x3F, /* Init init NMIs */\r
780x18,\r
790x90,0xFE /* Loopie time. */\r
80};\r
81\r
82static DECLFR(NSFROMRead)\r
83{\r
84 return (NSFROM-0x3800)[A];\r
85}\r
86\r
87static int doreset=0;\r
88static int NSFNMIFlags;\r
89static uint8 *NSFDATA=0;\r
90static int NSFMaxBank;\r
91\r
92static int NSFSize;\r
93static uint8 BSon;\r
94static uint16 PlayAddr;\r
95static uint16 InitAddr;\r
96static uint16 LoadAddr;\r
97\r
98static NSF_HEADER NSFHeader;\r
99\r
100void NSFMMC5_Close(void);\r
101static uint8 *ExWRAM=0;\r
102\r
103void NSFGI(int h)\r
104{\r
105 switch(h)\r
106 {\r
107 case GI_CLOSE:\r
108 if(NSFDATA) {free(NSFDATA);NSFDATA=0;}\r
109 if(ExWRAM) {free(ExWRAM);ExWRAM=0;}\r
110 if(NSFHeader.SoundChip&1) {\r
111// NSFVRC6_Init();\r
112 } else if(NSFHeader.SoundChip&2) {\r
113// NSFVRC7_Init();\r
114 } else if(NSFHeader.SoundChip&4) {\r
115// FDSSoundReset();\r
116 } else if(NSFHeader.SoundChip&8) {\r
117 NSFMMC5_Close();\r
118 } else if(NSFHeader.SoundChip&0x10) {\r
119// NSFN106_Init();\r
120 } else if(NSFHeader.SoundChip&0x20) {\r
121// NSFAY_Init();\r
122 }\r
123 break;\r
124 case GI_RESETM2:\r
125 case GI_POWER: NSF_init();break;\r
126 }\r
127}\r
128\r
129// First 32KB is reserved for sound chip emulation in the iNES mapper code.\r
130\r
131static INLINE void BANKSET(uint32 A, uint32 bank)\r
132{\r
133 bank&=NSFMaxBank;\r
134 if(NSFHeader.SoundChip&4)\r
135 memcpy(ExWRAM+(A-0x6000),NSFDATA+(bank<<12),4096);\r
136 else\r
137 setprg4(A,bank);\r
138}\r
139\r
140int NSFLoad(int fp)\r
141{\r
142 int x;\r
143\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
147 return 0;\r
148 NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0;\r
149\r
150 LoadAddr=NSFHeader.LoadAddressLow;\r
151 LoadAddr|=NSFHeader.LoadAddressHigh<<8;\r
152\r
153 if(LoadAddr<0x6000)\r
154 {\r
155 FCEUD_PrintError("Invalid load address.");\r
156 return(0);\r
157 }\r
158 InitAddr=NSFHeader.InitAddressLow;\r
159 InitAddr|=NSFHeader.InitAddressHigh<<8;\r
160\r
161 PlayAddr=NSFHeader.PlayAddressLow;\r
162 PlayAddr|=NSFHeader.PlayAddressHigh<<8;\r
163\r
164 NSFSize=FCEU_fgetsize(fp)-0x80;\r
165\r
166 NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096);\r
167 NSFMaxBank=uppow2(NSFMaxBank);\r
168\r
169 if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096)))\r
170 return 0;\r
171\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
175\r
176 NSFMaxBank--;\r
177\r
178 BSon=0;\r
179 for(x=0;x<8;x++)\r
180 BSon|=NSFHeader.BankSwitch[x];\r
181\r
182 FCEUGameInfo.type=GIT_NSF;\r
183 FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_GAMEPAD;\r
184 FCEUGameInfo.cspecial=SIS_NSF;\r
185\r
186 for(x=0;;x++)\r
187 {\r
188 if(NSFROM[x]==0x20)\r
189 {\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
194 break;\r
195 }\r
196 }\r
197\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
202\r
203 GameInterface=NSFGI;\r
204\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
208 {\r
209 static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"};\r
210\r
211 for(x=0;x<6;x++)\r
212 if(NSFHeader.SoundChip&(1<<x))\r
213 {\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
216 break;\r
217 }\r
218 }\r
219 if(BSon)\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
224\r
225 if(NSFHeader.SoundChip&4)\r
226 ExWRAM=FCEU_gmalloc(32768+8192);\r
227 else\r
228 ExWRAM=FCEU_gmalloc(8192);\r
229\r
230 FCEUI_SetVidSystem(NSFHeader.VideoSystem);\r
231\r
232 return 1;\r
233}\r
234\r
235static DECLFR(NSFVectorRead)\r
236{\r
237 if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2) || doreset)\r
238 {\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
243 return(X.DB);\r
244 }\r
245 else\r
246 return(CartBR(A));\r
247}\r
248\r
249void NSFVRC6_Init(void);\r
250void NSFVRC7_Init(void);\r
251void NSFMMC5_Init(void);\r
252void NSFN106_Init(void);\r
253void NSFAY_Init(void);\r
254\r
255void NSF_init(void)\r
256{\r
257 doreset=1;\r
258\r
259 ResetCartMapping();\r
260 if(NSFHeader.SoundChip&4)\r
261 {\r
262 SetupCartPRGMapping(0,ExWRAM,32768+8192,1);\r
263 setprg32(0x6000,0);\r
264 setprg8(0xE000,4);\r
265 memset(ExWRAM,0x00,32768+8192);\r
266 SetWriteHandler(0x6000,0xDFFF,CartBW);\r
267 SetReadHandler(0x6000,0xFFFF,CartBR);\r
268 }\r
269 else\r
270 {\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
278 }\r
279\r
280 if(BSon)\r
281 {\r
282 int32 x;\r
283 for(x=0;x<8;x++)\r
284 {\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
288 }\r
289 }\r
290 else\r
291 {\r
292 int32 x;\r
293 for(x=(LoadAddr&0xF000);x<0x10000;x+=0x1000)\r
294 BANKSET(x,((x-(LoadAddr&0x7000))>>12));\r
295 }\r
296\r
297 SetReadHandler(0xFFFA,0xFFFD,NSFVectorRead);\r
298\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
13624c8f 303 Page[0x3800>>11]=NSFROM-0x3800; // this is required for asm core to work.\r
d97315ac 304\r
305 SetWriteHandler(0x5ff6,0x5fff,NSF_write);\r
306\r
307 SetWriteHandler(0x3ff0,0x3fff,NSF_write);\r
308 SetReadHandler(0x3ff0,0x3fff,NSF_read);\r
309\r
310\r
311 if(NSFHeader.SoundChip&1) {\r
312 NSFVRC6_Init();\r
313 } else if(NSFHeader.SoundChip&2) {\r
314 NSFVRC7_Init();\r
315 } else if(NSFHeader.SoundChip&4) {\r
316 FDSSoundReset();\r
317 } else if(NSFHeader.SoundChip&8) {\r
318 NSFMMC5_Init();\r
319 } else if(NSFHeader.SoundChip&0x10) {\r
320 NSFN106_Init();\r
321 } else if(NSFHeader.SoundChip&0x20) {\r
322 NSFAY_Init();\r
323 }\r
324 CurrentSong=NSFHeader.StartingSong;\r
325 SongReload=0xFF;\r
326 NSFNMIFlags=0;\r
327}\r
328\r
329static DECLFW(NSF_write)\r
330{\r
331 switch(A)\r
332 {\r
333 case 0x3FF3:NSFNMIFlags|=1;break;\r
334 case 0x3FF4:NSFNMIFlags&=~2;break;\r
335 case 0x3FF5:NSFNMIFlags|=2;break;\r
336\r
337 case 0x5FF6:\r
338 case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return;\r
339 case 0x5FF8:\r
340 case 0x5FF9:\r
341 case 0x5FFA:\r
342 case 0x5FFB:\r
343 case 0x5FFC:\r
344 case 0x5FFD:\r
345 case 0x5FFE:\r
346 case 0x5FFF:if(!BSon) return;\r
347 A&=0xF;\r
348 BANKSET((A*4096),V);\r
349 break;\r
350 }\r
351}\r
352\r
353static DECLFR(NSF_read)\r
354{\r
355 int x;\r
356\r
357 switch(A)\r
358 {\r
359 case 0x3ff0:x=SongReload;\r
360 if(!fceuindbg)\r
361 SongReload=0;\r
362 return x;\r
363 case 0x3ff1:\r
364 if(!fceuindbg)\r
365 {\r
366 memset(RAM,0x00,0x800);\r
367\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
372\r
373 if(NSFHeader.SoundChip&4)\r
374 {\r
375 BWrite[0x4017](0x4017,0xC0); /* FDS BIOS writes $C0 */\r
376 BWrite[0x4089](0x4089,0x80);\r
377 BWrite[0x408A](0x408A,0xE8);\r
378 }\r
379 else\r
380 {\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
385 }\r
386\r
387 if(BSon)\r
388 {\r
389 for(x=0;x<8;x++)\r
390 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
391 }\r
392 return (CurrentSong-1);\r
393 }\r
394 case 0x3FF3:return PAL;\r
395 }\r
396 return 0;\r
397}\r
398\r
399uint8 FCEU_GetJoyJoy(void);\r
400\r
401static int special=0;\r
402\r
403void DrawNSF(uint8 *XBuf)\r
404{\r
405 char snbuf[16];\r
406 int x;\r
407\r
408 if(vismode==0) return;\r
409\r
13624c8f 410 for (x=0;x<240;x++)\r
411 memset(XBuf+SCREEN_OFFS+x*SCREEN_WIDTH,0,256);\r
d97315ac 412\r
413 {\r
414 int32 *Bufpl;\r
415 int32 mul=0;\r
416\r
417 int l;\r
418 l=GetSoundBuffer(&Bufpl);\r
419\r
420 if(special==0)\r
421 {\r
422 if(FSettings.SoundVolume)\r
423 mul=8192*240/(16384*FSettings.SoundVolume/50);\r
424 for(x=0;x<256;x++)\r
425 {\r
426 uint32 y;\r
427 y=142+((Bufpl[(x*l)>>8]*mul)>>14);\r
428 if(y<240)\r
92764e62 429 XBuf[x+y*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 430 }\r
431 }\r
432 else if(special==1)\r
433 {\r
434 if(FSettings.SoundVolume)\r
435 mul=8192*240/(8192*FSettings.SoundVolume/50);\r
436 for(x=0;x<256;x++)\r
437 {\r
438 double r;\r
439 uint32 xp,yp;\r
440\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
444 xp&=255;\r
445 yp%=240;\r
92764e62 446 XBuf[xp+yp*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 447 }\r
448 }\r
449 else if(special==2)\r
450 {\r
451 static double theta=0;\r
452 if(FSettings.SoundVolume)\r
453 mul=8192*240/(16384*FSettings.SoundVolume/50);\r
454 for(x=0;x<128;x++)\r
455 {\r
456 double xc,yc;\r
457 double r,t;\r
458 uint32 m,n;\r
459\r
460 xc=(double)128-x;\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
464\r
465 t+=theta;\r
466 m=128+r*cos(t);\r
467 n=120+r*sin(t);\r
468\r
469 if(m<256 && n<240)\r
92764e62 470 XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 471\r
472 }\r
473 for(x=128;x<256;x++)\r
474 {\r
475 double xc,yc;\r
476 double r,t;\r
477 uint32 m,n;\r
478\r
479 xc=(double)x-128;\r
480 yc=(double)((Bufpl[(x*l)>>8]*mul)>>14);\r
481 t=atan(yc/xc);\r
482 r=sqrt(xc*xc+yc*yc);\r
483\r
484 t+=theta;\r
485 m=128+r*cos(t);\r
486 n=120+r*sin(t);\r
487\r
488 if(m<256 && n<240)\r
92764e62 489 XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 490\r
491 }\r
492 theta+=(double)M_PI/256;\r
493 }\r
494 }\r
495\r
92764e62 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
d97315ac 499\r
92764e62 500 DrawTextTrans(XBuf+70*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen("Song:"))<<2)), SCREEN_WIDTH, (uint8*)"Song:", 6);\r
d97315ac 501 sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);\r
92764e62 502 DrawTextTrans(XBuf+82*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen(snbuf))<<2)), SCREEN_WIDTH, (uint8*)snbuf, 6);\r
d97315ac 503\r
504 {\r
505 static uint8 last=0;\r
506 uint8 tmp;\r
507 tmp=FCEU_GetJoyJoy();\r
508 if((tmp&JOY_RIGHT) && !(last&JOY_RIGHT))\r
509 {\r
510 if(CurrentSong<NSFHeader.TotalSongs)\r
511 {\r
512 CurrentSong++;\r
513 SongReload=0xFF;\r
514 }\r
515 }\r
516 else if((tmp&JOY_LEFT) && !(last&JOY_LEFT))\r
517 {\r
518 if(CurrentSong>1)\r
519 {\r
520 CurrentSong--;\r
521 SongReload=0xFF;\r
522 }\r
523 }\r
524 else if((tmp&JOY_UP) && !(last&JOY_UP))\r
525 {\r
526 CurrentSong+=10;\r
527 if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
528 SongReload=0xFF;\r
529 }\r
530 else if((tmp&JOY_DOWN) && !(last&JOY_DOWN))\r
531 {\r
532 CurrentSong-=10;\r
533 if(CurrentSong<1) CurrentSong=1;\r
534 SongReload=0xFF;\r
535 }\r
536 else if((tmp&JOY_START) && !(last&JOY_START))\r
537 SongReload=0xFF;\r
538 else if((tmp&JOY_A) && !(last&JOY_A))\r
539 {\r
540 special=(special+1)%3;\r
541 }\r
542 last=tmp;\r
543 }\r
544}\r
545\r
546void DoNSFFrame(void)\r
547{\r
548 if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))\r
549 TriggerNMI();\r
550}\r
551\r
552void FCEUI_NSFSetVis(int mode)\r
553{\r
554 vismode=mode;\r
555}\r
556\r
557int FCEUI_NSFChange(int amount)\r
558{\r
559 CurrentSong+=amount;\r
560 if(CurrentSong<1) CurrentSong=1;\r
561 else if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
562 SongReload=0xFF;\r
563\r
564 return(CurrentSong);\r
565}\r
566\r
567/* Returns total songs */\r
568int FCEUI_NSFGetInfo(uint8 *name, uint8 *artist, uint8 *copyright, int maxlen)\r
569{\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
574}\r