removed 64bit stuff in sound.c
[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 #define SCREEN_WIDTH 320\r
46 #define SCREEN_OFFS 32\r
47 \r
48 static uint8 SongReload;\r
49 static int CurrentSong;\r
50 \r
51 static DECLFW(NSF_write);\r
52 static DECLFR(NSF_read);\r
53 \r
54 static int vismode=1;\r
55 \r
56 static uint8 NSFROM[0x30+6]=\r
57 {\r
58 /* 0x00 - NMI */\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
63 \r
64 0xAD,0xF1,0x3F,       /* Confirm and load A      */\r
65 0xAE,0xF3,0x3F,       /* Load X with PAL/NTSC byte */\r
66 \r
67 0x20,0x00,0x00,       /* JSR to init routine     */\r
68 \r
69 0xA9,0x00,\r
70 0xAA,\r
71 0xA8,\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
75 \r
76 /* 0x20 */\r
77 0x8D,0xF3,0x3F,        /* Init init NMIs */\r
78 0x18,\r
79 0x90,0xFE        /* Loopie time. */\r
80 };\r
81 \r
82 static DECLFR(NSFROMRead)\r
83 {\r
84  return (NSFROM-0x3800)[A];\r
85 }\r
86 \r
87 static int doreset=0;\r
88 static int NSFNMIFlags;\r
89 static uint8 *NSFDATA=0;\r
90 static int NSFMaxBank;\r
91 \r
92 static int NSFSize;\r
93 static uint8 BSon;\r
94 static uint16 PlayAddr;\r
95 static uint16 InitAddr;\r
96 static uint16 LoadAddr;\r
97 \r
98 static NSF_HEADER NSFHeader;\r
99 \r
100 void NSFMMC5_Close(void);\r
101 static uint8 *ExWRAM=0;\r
102 \r
103 void 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
131 static 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
140 int 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
235 static 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
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
254 \r
255 void 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
303   Page[0x3800>>11]=NSFROM-0x3800; // this is required for asm core to work.\r
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
329 static 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
353 static 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
399 uint8 FCEU_GetJoyJoy(void);\r
400 \r
401 static int special=0;\r
402 \r
403 void 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
410  for (x=0;x<240;x++)\r
411   memset(XBuf+SCREEN_OFFS+x*SCREEN_WIDTH,0,256);\r
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
429      XBuf[x+y*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
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
446     XBuf[xp+yp*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
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
470      XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
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
489      XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
490 \r
491    }\r
492    theta+=(double)M_PI/256;\r
493   }\r
494  }\r
495 \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
499 \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
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
546 void DoNSFFrame(void)\r
547 {\r
548  if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))\r
549   TriggerNMI();\r
550 }\r
551 \r
552 void FCEUI_NSFSetVis(int mode)\r
553 {\r
554  vismode=mode;\r
555 }\r
556 \r
557 int 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
568 int 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