updated bords/mappers/stuff to 0.98.15, lots of them got broken, asmcore support...
[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
45static uint8 SongReload;\r
46static int CurrentSong;\r
47\r
48static DECLFW(NSF_write);\r
49static DECLFR(NSF_read);\r
50\r
51static int vismode=1;\r
52\r
53static uint8 NSFROM[0x30+6]=\r
54{\r
55/* 0x00 - NMI */\r
560x8D,0xF4,0x3F, /* Stop play routine NMIs. */\r
570xA2,0xFF,0x9A, /* Initialize the stack pointer. */\r
580xAD,0xF0,0x3F, /* See if we need to init. */\r
590xF0,0x09, /* If 0, go to play routine playing. */\r
60\r
610xAD,0xF1,0x3F, /* Confirm and load A */\r
620xAE,0xF3,0x3F, /* Load X with PAL/NTSC byte */\r
63\r
640x20,0x00,0x00, /* JSR to init routine */\r
65\r
660xA9,0x00,\r
670xAA,\r
680xA8,\r
690x20,0x00,0x00, /* JSR to play routine */\r
700x8D,0xF5,0x3F, /* Start play routine NMIs. */\r
710x90,0xFE, /* Loopie time. */\r
72\r
73/* 0x20 */\r
740x8D,0xF3,0x3F, /* Init init NMIs */\r
750x18,\r
760x90,0xFE /* Loopie time. */\r
77};\r
78\r
79static DECLFR(NSFROMRead)\r
80{\r
81 return (NSFROM-0x3800)[A];\r
82}\r
83\r
84static int doreset=0;\r
85static int NSFNMIFlags;\r
86static uint8 *NSFDATA=0;\r
87static int NSFMaxBank;\r
88\r
89static int NSFSize;\r
90static uint8 BSon;\r
91static uint16 PlayAddr;\r
92static uint16 InitAddr;\r
93static uint16 LoadAddr;\r
94\r
95static NSF_HEADER NSFHeader;\r
96\r
97void NSFMMC5_Close(void);\r
98static uint8 *ExWRAM=0;\r
99\r
100void 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
128static 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
137int 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
232static 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
246void NSFVRC6_Init(void);\r
247void NSFVRC7_Init(void);\r
248void NSFMMC5_Init(void);\r
249void NSFN106_Init(void);\r
250void NSFAY_Init(void);\r
251\r
252void 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
325static 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
349static 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
395uint8 FCEU_GetJoyJoy(void);\r
396\r
397static int special=0;\r
398\r
399void 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
542void DoNSFFrame(void)\r
543{\r
544 if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))\r
545 TriggerNMI();\r
546}\r
547\r
548void FCEUI_NSFSetVis(int mode)\r
549{\r
550 vismode=mode;\r
551}\r
552\r
553int 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
564int 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