updated bords/mappers/stuff to 0.98.15, lots of them got broken, asmcore support...
[fceu.git] / nsf.c
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
45 static uint8 SongReload;\r
46 static int CurrentSong;\r
47 \r
48 static DECLFW(NSF_write);\r
49 static DECLFR(NSF_read);\r
50 \r
51 static int vismode=1;\r
52 \r
53 static uint8 NSFROM[0x30+6]=\r
54 {\r
55 /* 0x00 - NMI */\r
56 0x8D,0xF4,0x3F,       /* Stop play routine NMIs. */\r
57 0xA2,0xFF,0x9A,       /* Initialize the stack pointer. */\r
58 0xAD,0xF0,0x3F,       /* See if we need to init. */\r
59 0xF0,0x09,            /* If 0, go to play routine playing. */\r
60 \r
61 0xAD,0xF1,0x3F,       /* Confirm and load A      */\r
62 0xAE,0xF3,0x3F,       /* Load X with PAL/NTSC byte */\r
63 \r
64 0x20,0x00,0x00,       /* JSR to init routine     */\r
65 \r
66 0xA9,0x00,\r
67 0xAA,\r
68 0xA8,\r
69 0x20,0x00,0x00,       /* JSR to play routine  */\r
70 0x8D,0xF5,0x3F,        /* Start play routine NMIs. */\r
71 0x90,0xFE,             /* Loopie time. */\r
72 \r
73 /* 0x20 */\r
74 0x8D,0xF3,0x3F,        /* Init init NMIs */\r
75 0x18,\r
76 0x90,0xFE        /* Loopie time. */\r
77 };\r
78 \r
79 static DECLFR(NSFROMRead)\r
80 {\r
81  return (NSFROM-0x3800)[A];\r
82 }\r
83 \r
84 static int doreset=0;\r
85 static int NSFNMIFlags;\r
86 static uint8 *NSFDATA=0;\r
87 static int NSFMaxBank;\r
88 \r
89 static int NSFSize;\r
90 static uint8 BSon;\r
91 static uint16 PlayAddr;\r
92 static uint16 InitAddr;\r
93 static uint16 LoadAddr;\r
94 \r
95 static NSF_HEADER NSFHeader;\r
96 \r
97 void NSFMMC5_Close(void);\r
98 static uint8 *ExWRAM=0;\r
99 \r
100 void NSFGI(int h)\r
101 {\r
102  switch(h)\r
103  {\r
104  case GI_CLOSE:\r
105   if(NSFDATA) {free(NSFDATA);NSFDATA=0;}\r
106   if(ExWRAM) {free(ExWRAM);ExWRAM=0;}\r
107   if(NSFHeader.SoundChip&1) {\r
108 //   NSFVRC6_Init();\r
109   } else if(NSFHeader.SoundChip&2) {\r
110 //   NSFVRC7_Init();\r
111   } else if(NSFHeader.SoundChip&4) {\r
112 //   FDSSoundReset();\r
113   } else if(NSFHeader.SoundChip&8) {\r
114    NSFMMC5_Close();\r
115   } else if(NSFHeader.SoundChip&0x10) {\r
116 //   NSFN106_Init();\r
117   } else if(NSFHeader.SoundChip&0x20) {\r
118 //   NSFAY_Init();\r
119   }\r
120   break;\r
121  case GI_RESETM2:\r
122  case GI_POWER: NSF_init();break;\r
123  }\r
124 }\r
125 \r
126 // First 32KB is reserved for sound chip emulation in the iNES mapper code.\r
127 \r
128 static INLINE void BANKSET(uint32 A, uint32 bank)\r
129 {\r
130  bank&=NSFMaxBank;\r
131  if(NSFHeader.SoundChip&4)\r
132   memcpy(ExWRAM+(A-0x6000),NSFDATA+(bank<<12),4096);\r
133  else\r
134   setprg4(A,bank);\r
135 }\r
136 \r
137 int NSFLoad(int fp)\r
138 {\r
139   int x;\r
140 \r
141   FCEU_fseek(fp,0,SEEK_SET);\r
142   FCEU_fread(&NSFHeader,1,0x80,fp);\r
143   if(memcmp(NSFHeader.ID,"NESM\x1a",5))\r
144                   return 0;\r
145   NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0;\r
146 \r
147   LoadAddr=NSFHeader.LoadAddressLow;\r
148   LoadAddr|=NSFHeader.LoadAddressHigh<<8;\r
149 \r
150   if(LoadAddr<0x6000)\r
151   {\r
152    FCEUD_PrintError("Invalid load address.");\r
153    return(0);\r
154   }\r
155   InitAddr=NSFHeader.InitAddressLow;\r
156   InitAddr|=NSFHeader.InitAddressHigh<<8;\r
157 \r
158   PlayAddr=NSFHeader.PlayAddressLow;\r
159   PlayAddr|=NSFHeader.PlayAddressHigh<<8;\r
160 \r
161   NSFSize=FCEU_fgetsize(fp)-0x80;\r
162 \r
163   NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096);\r
164   NSFMaxBank=uppow2(NSFMaxBank);\r
165 \r
166   if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096)))\r
167    return 0;\r
168 \r
169   FCEU_fseek(fp,0x80,SEEK_SET);\r
170   memset(NSFDATA,0x00,NSFMaxBank*4096);\r
171   FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp);\r
172 \r
173   NSFMaxBank--;\r
174 \r
175   BSon=0;\r
176   for(x=0;x<8;x++)\r
177    BSon|=NSFHeader.BankSwitch[x];\r
178 \r
179  FCEUGameInfo.type=GIT_NSF;\r
180  FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_GAMEPAD;\r
181  FCEUGameInfo.cspecial=SIS_NSF;\r
182 \r
183  for(x=0;;x++)\r
184  {\r
185   if(NSFROM[x]==0x20)\r
186   {\r
187    NSFROM[x+1]=InitAddr&0xFF;\r
188    NSFROM[x+2]=InitAddr>>8;\r
189    NSFROM[x+8]=PlayAddr&0xFF;\r
190    NSFROM[x+9]=PlayAddr>>8;\r
191    break;\r
192   }\r
193  }\r
194 \r
195  if(NSFHeader.VideoSystem==0)\r
196   FCEUGameInfo.vidsys=GIV_NTSC;\r
197  else if(NSFHeader.VideoSystem==1)\r
198   FCEUGameInfo.vidsys=GIV_PAL;\r
199 \r
200  GameInterface=NSFGI;\r
201 \r
202  FCEU_printf("NSF Loaded.  File information:\n\n");\r
203  FCEU_printf(" Name:       %s\n Artist:     %s\n Copyright:  %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright);\r
204  if(NSFHeader.SoundChip)\r
205  {\r
206   static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"};\r
207 \r
208   for(x=0;x<6;x++)\r
209    if(NSFHeader.SoundChip&(1<<x))\r
210    {\r
211     FCEU_printf(" Expansion hardware:  %s\n",tab[x]);\r
212     NSFHeader.SoundChip=1<<x;  /* Prevent confusing weirdness if more than one bit is set. */\r
213     break;\r
214    }\r
215  }\r
216  if(BSon)\r
217   FCEU_printf(" Bank-switched.\n");\r
218  FCEU_printf(" Load address:  $%04x\n Init address:  $%04x\n Play address:  $%04x\n",LoadAddr,InitAddr,PlayAddr);\r
219  FCEU_printf(" %s\n",(NSFHeader.VideoSystem&1)?"PAL":"NTSC");\r
220  FCEU_printf(" Starting song:  %d / %d\n\n",NSFHeader.StartingSong,NSFHeader.TotalSongs);\r
221 \r
222  if(NSFHeader.SoundChip&4)\r
223   ExWRAM=FCEU_gmalloc(32768+8192);\r
224  else\r
225   ExWRAM=FCEU_gmalloc(8192);\r
226 \r
227  FCEUI_SetVidSystem(NSFHeader.VideoSystem);\r
228 \r
229  return 1;\r
230 }\r
231 \r
232 static DECLFR(NSFVectorRead)\r
233 {\r
234  if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2) || doreset)\r
235  {\r
236   if(A==0xFFFA) return(0x00);\r
237   else if(A==0xFFFB) return(0x38);\r
238   else if(A==0xFFFC) return(0x20);\r
239   else if(A==0xFFFD) {doreset=0;return(0x38);}\r
240   return(X.DB);\r
241  }\r
242  else\r
243   return(CartBR(A));\r
244 }\r
245 \r
246 void NSFVRC6_Init(void);\r
247 void NSFVRC7_Init(void);\r
248 void NSFMMC5_Init(void);\r
249 void NSFN106_Init(void);\r
250 void NSFAY_Init(void);\r
251 \r
252 void NSF_init(void)\r
253 {\r
254   doreset=1;\r
255 \r
256   ResetCartMapping();\r
257   if(NSFHeader.SoundChip&4)\r
258   {\r
259    SetupCartPRGMapping(0,ExWRAM,32768+8192,1);\r
260    setprg32(0x6000,0);\r
261    setprg8(0xE000,4);\r
262    memset(ExWRAM,0x00,32768+8192);\r
263    SetWriteHandler(0x6000,0xDFFF,CartBW);\r
264    SetReadHandler(0x6000,0xFFFF,CartBR);\r
265   }\r
266   else\r
267   {\r
268    memset(ExWRAM,0x00,8192);\r
269    SetReadHandler(0x6000,0x7FFF,CartBR);\r
270    SetWriteHandler(0x6000,0x7FFF,CartBW);\r
271    SetupCartPRGMapping(0,NSFDATA,((NSFMaxBank+1)*4096),0);\r
272    SetupCartPRGMapping(1,ExWRAM,8192,1);\r
273    setprg8r(1,0x6000,0);\r
274    SetReadHandler(0x8000,0xFFFF,CartBR);\r
275   }\r
276 \r
277   if(BSon)\r
278   {\r
279    int32 x;\r
280    for(x=0;x<8;x++)\r
281    {\r
282     if(NSFHeader.SoundChip&4 && x>=6)\r
283      BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]);\r
284     BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
285    }\r
286   }\r
287   else\r
288   {\r
289    int32 x;\r
290    for(x=(LoadAddr&0xF000);x<0x10000;x+=0x1000)\r
291     BANKSET(x,((x-(LoadAddr&0x7000))>>12));\r
292   }\r
293 \r
294   SetReadHandler(0xFFFA,0xFFFD,NSFVectorRead);\r
295 \r
296   SetWriteHandler(0x2000,0x3fff,0);\r
297   SetReadHandler(0x2000,0x37ff,0);\r
298   SetReadHandler(0x3836,0x3FFF,0);\r
299   SetReadHandler(0x3800,0x3835,NSFROMRead);\r
300 \r
301   SetWriteHandler(0x5ff6,0x5fff,NSF_write);\r
302 \r
303   SetWriteHandler(0x3ff0,0x3fff,NSF_write);\r
304   SetReadHandler(0x3ff0,0x3fff,NSF_read);\r
305 \r
306 \r
307   if(NSFHeader.SoundChip&1) {\r
308    NSFVRC6_Init();\r
309   } else if(NSFHeader.SoundChip&2) {\r
310    NSFVRC7_Init();\r
311   } else if(NSFHeader.SoundChip&4) {\r
312    FDSSoundReset();\r
313   } else if(NSFHeader.SoundChip&8) {\r
314    NSFMMC5_Init();\r
315   } else if(NSFHeader.SoundChip&0x10) {\r
316    NSFN106_Init();\r
317   } else if(NSFHeader.SoundChip&0x20) {\r
318    NSFAY_Init();\r
319   }\r
320   CurrentSong=NSFHeader.StartingSong;\r
321   SongReload=0xFF;\r
322   NSFNMIFlags=0;\r
323 }\r
324 \r
325 static DECLFW(NSF_write)\r
326 {\r
327  switch(A)\r
328  {\r
329   case 0x3FF3:NSFNMIFlags|=1;break;\r
330   case 0x3FF4:NSFNMIFlags&=~2;break;\r
331   case 0x3FF5:NSFNMIFlags|=2;break;\r
332 \r
333   case 0x5FF6:\r
334   case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return;\r
335   case 0x5FF8:\r
336   case 0x5FF9:\r
337   case 0x5FFA:\r
338   case 0x5FFB:\r
339   case 0x5FFC:\r
340   case 0x5FFD:\r
341   case 0x5FFE:\r
342   case 0x5FFF:if(!BSon) return;\r
343               A&=0xF;\r
344               BANKSET((A*4096),V);\r
345               break;\r
346  }\r
347 }\r
348 \r
349 static DECLFR(NSF_read)\r
350 {\r
351  int x;\r
352 \r
353  switch(A)\r
354  {\r
355  case 0x3ff0:x=SongReload;\r
356              if(!fceuindbg)\r
357               SongReload=0;\r
358              return x;\r
359  case 0x3ff1:\r
360             if(!fceuindbg)\r
361             {\r
362              memset(RAM,0x00,0x800);\r
363 \r
364              BWrite[0x4015](0x4015,0x0);\r
365              for(x=0;x<0x14;x++)\r
366               BWrite[0x4000+x](0x4000+x,0);\r
367              BWrite[0x4015](0x4015,0xF);\r
368 \r
369              if(NSFHeader.SoundChip&4)\r
370              {\r
371         BWrite[0x4017](0x4017,0xC0);  /* FDS BIOS writes $C0 */\r
372               BWrite[0x4089](0x4089,0x80);\r
373               BWrite[0x408A](0x408A,0xE8);\r
374              }\r
375              else\r
376              {\r
377               memset(ExWRAM,0x00,8192);\r
378               BWrite[0x4017](0x4017,0xC0);\r
379               BWrite[0x4017](0x4017,0xC0);\r
380               BWrite[0x4017](0x4017,0x40);\r
381              }\r
382 \r
383              if(BSon)\r
384              {\r
385               for(x=0;x<8;x++)\r
386                BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
387              }\r
388              return (CurrentSong-1);\r
389              }\r
390  case 0x3FF3:return PAL;\r
391  }\r
392  return 0;\r
393 }\r
394 \r
395 uint8 FCEU_GetJoyJoy(void);\r
396 \r
397 static int special=0;\r
398 \r
399 void DrawNSF(uint8 *XBuf)\r
400 {\r
401  char snbuf[16];\r
402  int x;\r
403 \r
404  if(vismode==0) return;\r
405 \r
406  memset(XBuf,0,256*240);\r
407 \r
408 \r
409  {\r
410   int32 *Bufpl;\r
411   int32 mul=0;\r
412 \r
413   int l;\r
414   l=GetSoundBuffer(&Bufpl);\r
415 \r
416   if(special==0)\r
417   {\r
418    if(FSettings.SoundVolume)\r
419     mul=8192*240/(16384*FSettings.SoundVolume/50);\r
420    for(x=0;x<256;x++)\r
421    {\r
422     uint32 y;\r
423     y=142+((Bufpl[(x*l)>>8]*mul)>>14);\r
424     if(y<240)\r
425      XBuf[x+y*256]=3;\r
426    }\r
427   }\r
428   else if(special==1)\r
429   {\r
430    if(FSettings.SoundVolume)\r
431     mul=8192*240/(8192*FSettings.SoundVolume/50);\r
432    for(x=0;x<256;x++)\r
433    {\r
434     double r;\r
435     uint32 xp,yp;\r
436 \r
437     r=(Bufpl[(x*l)>>8]*mul)>>14;\r
438     xp=128+r*cos(x*M_PI*2/256);\r
439     yp=120+r*sin(x*M_PI*2/256);\r
440     xp&=255;\r
441     yp%=240;\r
442     XBuf[xp+yp*256]=3;\r
443    }\r
444   }\r
445   else if(special==2)\r
446   {\r
447    static double theta=0;\r
448    if(FSettings.SoundVolume)\r
449     mul=8192*240/(16384*FSettings.SoundVolume/50);\r
450    for(x=0;x<128;x++)\r
451    {\r
452     double xc,yc;\r
453     double r,t;\r
454     uint32 m,n;\r
455 \r
456     xc=(double)128-x;\r
457     yc=0-((double)( ((Bufpl[(x*l)>>8]) *mul)>>14));\r
458     t=M_PI+atan(yc/xc);\r
459     r=sqrt(xc*xc+yc*yc);\r
460 \r
461     t+=theta;\r
462     m=128+r*cos(t);\r
463     n=120+r*sin(t);\r
464 \r
465     if(m<256 && n<240)\r
466      XBuf[m+n*256]=3;\r
467 \r
468    }\r
469    for(x=128;x<256;x++)\r
470    {\r
471     double xc,yc;\r
472     double r,t;\r
473     uint32 m,n;\r
474 \r
475     xc=(double)x-128;\r
476     yc=(double)((Bufpl[(x*l)>>8]*mul)>>14);\r
477     t=atan(yc/xc);\r
478     r=sqrt(xc*xc+yc*yc);\r
479 \r
480     t+=theta;\r
481     m=128+r*cos(t);\r
482     n=120+r*sin(t);\r
483 \r
484     if(m<256 && n<240)\r
485      XBuf[m+n*256]=3;\r
486 \r
487    }\r
488    theta+=(double)M_PI/256;\r
489   }\r
490  }\r
491 \r
492  DrawTextTrans(XBuf+10*256+4+(((31-strlen((char*)NSFHeader.SongName))<<2)), 256, NSFHeader.SongName, 6);\r
493  DrawTextTrans(XBuf+26*256+4+(((31-strlen((char*)NSFHeader.Artist))<<2)), 256,NSFHeader.Artist, 6);\r
494  DrawTextTrans(XBuf+42*256+4+(((31-strlen((char*)NSFHeader.Copyright))<<2)), 256,NSFHeader.Copyright, 6);\r
495 \r
496  DrawTextTrans(XBuf+70*256+4+(((31-strlen("Song:"))<<2)), 256, (uint8*)"Song:", 6);\r
497  sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);\r
498  DrawTextTrans(XBuf+82*256+4+(((31-strlen(snbuf))<<2)), 256, (uint8*)snbuf, 6);\r
499 \r
500  {\r
501   static uint8 last=0;\r
502   uint8 tmp;\r
503   tmp=FCEU_GetJoyJoy();\r
504   if((tmp&JOY_RIGHT) && !(last&JOY_RIGHT))\r
505   {\r
506    if(CurrentSong<NSFHeader.TotalSongs)\r
507    {\r
508     CurrentSong++;\r
509     SongReload=0xFF;\r
510    }\r
511   }\r
512   else if((tmp&JOY_LEFT) && !(last&JOY_LEFT))\r
513   {\r
514    if(CurrentSong>1)\r
515    {\r
516     CurrentSong--;\r
517     SongReload=0xFF;\r
518    }\r
519   }\r
520   else if((tmp&JOY_UP) && !(last&JOY_UP))\r
521   {\r
522    CurrentSong+=10;\r
523    if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
524    SongReload=0xFF;\r
525   }\r
526   else if((tmp&JOY_DOWN) && !(last&JOY_DOWN))\r
527   {\r
528    CurrentSong-=10;\r
529    if(CurrentSong<1) CurrentSong=1;\r
530    SongReload=0xFF;\r
531   }\r
532   else if((tmp&JOY_START) && !(last&JOY_START))\r
533    SongReload=0xFF;\r
534   else if((tmp&JOY_A) && !(last&JOY_A))\r
535   {\r
536    special=(special+1)%3;\r
537   }\r
538   last=tmp;\r
539  }\r
540 }\r
541 \r
542 void DoNSFFrame(void)\r
543 {\r
544  if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))\r
545   TriggerNMI();\r
546 }\r
547 \r
548 void FCEUI_NSFSetVis(int mode)\r
549 {\r
550  vismode=mode;\r
551 }\r
552 \r
553 int FCEUI_NSFChange(int amount)\r
554 {\r
555    CurrentSong+=amount;\r
556    if(CurrentSong<1) CurrentSong=1;\r
557    else if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
558    SongReload=0xFF;\r
559 \r
560    return(CurrentSong);\r
561 }\r
562 \r
563 /* Returns total songs */\r
564 int FCEUI_NSFGetInfo(uint8 *name, uint8 *artist, uint8 *copyright, int maxlen)\r
565 {\r
566  strncpy((char*)name,(char*)NSFHeader.SongName,maxlen);\r
567  strncpy((char*)artist,(char*)NSFHeader.Artist,maxlen);\r
568  strncpy((char*)copyright,(char*)NSFHeader.Copyright,maxlen);\r
569  return(NSFHeader.TotalSongs);\r
570 }\r