some warnings fixed, nsf fixed, palettes, more code backported
[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
303\r
304 SetWriteHandler(0x5ff6,0x5fff,NSF_write);\r
305\r
306 SetWriteHandler(0x3ff0,0x3fff,NSF_write);\r
307 SetReadHandler(0x3ff0,0x3fff,NSF_read);\r
308\r
309\r
310 if(NSFHeader.SoundChip&1) {\r
311 NSFVRC6_Init();\r
312 } else if(NSFHeader.SoundChip&2) {\r
313 NSFVRC7_Init();\r
314 } else if(NSFHeader.SoundChip&4) {\r
315 FDSSoundReset();\r
316 } else if(NSFHeader.SoundChip&8) {\r
317 NSFMMC5_Init();\r
318 } else if(NSFHeader.SoundChip&0x10) {\r
319 NSFN106_Init();\r
320 } else if(NSFHeader.SoundChip&0x20) {\r
321 NSFAY_Init();\r
322 }\r
323 CurrentSong=NSFHeader.StartingSong;\r
324 SongReload=0xFF;\r
325 NSFNMIFlags=0;\r
326}\r
327\r
328static DECLFW(NSF_write)\r
329{\r
330 switch(A)\r
331 {\r
332 case 0x3FF3:NSFNMIFlags|=1;break;\r
333 case 0x3FF4:NSFNMIFlags&=~2;break;\r
334 case 0x3FF5:NSFNMIFlags|=2;break;\r
335\r
336 case 0x5FF6:\r
337 case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return;\r
338 case 0x5FF8:\r
339 case 0x5FF9:\r
340 case 0x5FFA:\r
341 case 0x5FFB:\r
342 case 0x5FFC:\r
343 case 0x5FFD:\r
344 case 0x5FFE:\r
345 case 0x5FFF:if(!BSon) return;\r
346 A&=0xF;\r
347 BANKSET((A*4096),V);\r
348 break;\r
349 }\r
350}\r
351\r
352static DECLFR(NSF_read)\r
353{\r
354 int x;\r
355\r
356 switch(A)\r
357 {\r
358 case 0x3ff0:x=SongReload;\r
359 if(!fceuindbg)\r
360 SongReload=0;\r
361 return x;\r
362 case 0x3ff1:\r
363 if(!fceuindbg)\r
364 {\r
365 memset(RAM,0x00,0x800);\r
366\r
367 BWrite[0x4015](0x4015,0x0);\r
368 for(x=0;x<0x14;x++)\r
369 BWrite[0x4000+x](0x4000+x,0);\r
370 BWrite[0x4015](0x4015,0xF);\r
371\r
372 if(NSFHeader.SoundChip&4)\r
373 {\r
374 BWrite[0x4017](0x4017,0xC0); /* FDS BIOS writes $C0 */\r
375 BWrite[0x4089](0x4089,0x80);\r
376 BWrite[0x408A](0x408A,0xE8);\r
377 }\r
378 else\r
379 {\r
380 memset(ExWRAM,0x00,8192);\r
381 BWrite[0x4017](0x4017,0xC0);\r
382 BWrite[0x4017](0x4017,0xC0);\r
383 BWrite[0x4017](0x4017,0x40);\r
384 }\r
385\r
386 if(BSon)\r
387 {\r
388 for(x=0;x<8;x++)\r
389 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
390 }\r
391 return (CurrentSong-1);\r
392 }\r
393 case 0x3FF3:return PAL;\r
394 }\r
395 return 0;\r
396}\r
397\r
398uint8 FCEU_GetJoyJoy(void);\r
399\r
400static int special=0;\r
401\r
402void DrawNSF(uint8 *XBuf)\r
403{\r
404 char snbuf[16];\r
405 int x;\r
406\r
407 if(vismode==0) return;\r
408\r
92764e62 409 memset(XBuf,0,320*240);\r
d97315ac 410\r
411\r
412 {\r
413 int32 *Bufpl;\r
414 int32 mul=0;\r
415\r
416 int l;\r
417 l=GetSoundBuffer(&Bufpl);\r
418\r
419 if(special==0)\r
420 {\r
421 if(FSettings.SoundVolume)\r
422 mul=8192*240/(16384*FSettings.SoundVolume/50);\r
423 for(x=0;x<256;x++)\r
424 {\r
425 uint32 y;\r
426 y=142+((Bufpl[(x*l)>>8]*mul)>>14);\r
427 if(y<240)\r
92764e62 428 XBuf[x+y*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 429 }\r
430 }\r
431 else if(special==1)\r
432 {\r
433 if(FSettings.SoundVolume)\r
434 mul=8192*240/(8192*FSettings.SoundVolume/50);\r
435 for(x=0;x<256;x++)\r
436 {\r
437 double r;\r
438 uint32 xp,yp;\r
439\r
440 r=(Bufpl[(x*l)>>8]*mul)>>14;\r
441 xp=128+r*cos(x*M_PI*2/256);\r
442 yp=120+r*sin(x*M_PI*2/256);\r
443 xp&=255;\r
444 yp%=240;\r
92764e62 445 XBuf[xp+yp*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 446 }\r
447 }\r
448 else if(special==2)\r
449 {\r
450 static double theta=0;\r
451 if(FSettings.SoundVolume)\r
452 mul=8192*240/(16384*FSettings.SoundVolume/50);\r
453 for(x=0;x<128;x++)\r
454 {\r
455 double xc,yc;\r
456 double r,t;\r
457 uint32 m,n;\r
458\r
459 xc=(double)128-x;\r
460 yc=0-((double)( ((Bufpl[(x*l)>>8]) *mul)>>14));\r
461 t=M_PI+atan(yc/xc);\r
462 r=sqrt(xc*xc+yc*yc);\r
463\r
464 t+=theta;\r
465 m=128+r*cos(t);\r
466 n=120+r*sin(t);\r
467\r
468 if(m<256 && n<240)\r
92764e62 469 XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 470\r
471 }\r
472 for(x=128;x<256;x++)\r
473 {\r
474 double xc,yc;\r
475 double r,t;\r
476 uint32 m,n;\r
477\r
478 xc=(double)x-128;\r
479 yc=(double)((Bufpl[(x*l)>>8]*mul)>>14);\r
480 t=atan(yc/xc);\r
481 r=sqrt(xc*xc+yc*yc);\r
482\r
483 t+=theta;\r
484 m=128+r*cos(t);\r
485 n=120+r*sin(t);\r
486\r
487 if(m<256 && n<240)\r
92764e62 488 XBuf[m+n*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 489\r
490 }\r
491 theta+=(double)M_PI/256;\r
492 }\r
493 }\r
494\r
92764e62 495 DrawTextTrans(XBuf+10*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen((char*)NSFHeader.SongName))<<2)), SCREEN_WIDTH, NSFHeader.SongName, 6);\r
496 DrawTextTrans(XBuf+26*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen((char*)NSFHeader.Artist))<<2)), SCREEN_WIDTH,NSFHeader.Artist, 6);\r
497 DrawTextTrans(XBuf+42*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen((char*)NSFHeader.Copyright))<<2)), SCREEN_WIDTH,NSFHeader.Copyright, 6);\r
d97315ac 498\r
92764e62 499 DrawTextTrans(XBuf+70*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen("Song:"))<<2)), SCREEN_WIDTH, (uint8*)"Song:", 6);\r
d97315ac 500 sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);\r
92764e62 501 DrawTextTrans(XBuf+82*SCREEN_WIDTH+SCREEN_OFFS+4+(((31-strlen(snbuf))<<2)), SCREEN_WIDTH, (uint8*)snbuf, 6);\r
d97315ac 502\r
503 {\r
504 static uint8 last=0;\r
505 uint8 tmp;\r
506 tmp=FCEU_GetJoyJoy();\r
507 if((tmp&JOY_RIGHT) && !(last&JOY_RIGHT))\r
508 {\r
509 if(CurrentSong<NSFHeader.TotalSongs)\r
510 {\r
511 CurrentSong++;\r
512 SongReload=0xFF;\r
513 }\r
514 }\r
515 else if((tmp&JOY_LEFT) && !(last&JOY_LEFT))\r
516 {\r
517 if(CurrentSong>1)\r
518 {\r
519 CurrentSong--;\r
520 SongReload=0xFF;\r
521 }\r
522 }\r
523 else if((tmp&JOY_UP) && !(last&JOY_UP))\r
524 {\r
525 CurrentSong+=10;\r
526 if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
527 SongReload=0xFF;\r
528 }\r
529 else if((tmp&JOY_DOWN) && !(last&JOY_DOWN))\r
530 {\r
531 CurrentSong-=10;\r
532 if(CurrentSong<1) CurrentSong=1;\r
533 SongReload=0xFF;\r
534 }\r
535 else if((tmp&JOY_START) && !(last&JOY_START))\r
536 SongReload=0xFF;\r
537 else if((tmp&JOY_A) && !(last&JOY_A))\r
538 {\r
539 special=(special+1)%3;\r
540 }\r
541 last=tmp;\r
542 }\r
543}\r
544\r
545void DoNSFFrame(void)\r
546{\r
547 if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))\r
548 TriggerNMI();\r
549}\r
550\r
551void FCEUI_NSFSetVis(int mode)\r
552{\r
553 vismode=mode;\r
554}\r
555\r
556int FCEUI_NSFChange(int amount)\r
557{\r
558 CurrentSong+=amount;\r
559 if(CurrentSong<1) CurrentSong=1;\r
560 else if(CurrentSong>NSFHeader.TotalSongs) CurrentSong=NSFHeader.TotalSongs;\r
561 SongReload=0xFF;\r
562\r
563 return(CurrentSong);\r
564}\r
565\r
566/* Returns total songs */\r
567int FCEUI_NSFGetInfo(uint8 *name, uint8 *artist, uint8 *copyright, int maxlen)\r
568{\r
569 strncpy((char*)name,(char*)NSFHeader.SongName,maxlen);\r
570 strncpy((char*)artist,(char*)NSFHeader.Artist,maxlen);\r
571 strncpy((char*)copyright,(char*)NSFHeader.Copyright,maxlen);\r
572 return(NSFHeader.TotalSongs);\r
573}\r