refactor out GP2X specific stuff completely
[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
c4980f9e 103void NSFGI(int h, void *param)\r
d97315ac 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
c4980f9e 126 case GI_INFOSTRING:\r
127 sprintf(param, "NSF, %s", PAL?"PAL":"NTSC");\r
128 break;\r
d97315ac 129 }\r
130}\r
131\r
132// First 32KB is reserved for sound chip emulation in the iNES mapper code.\r
133\r
134static INLINE void BANKSET(uint32 A, uint32 bank)\r
135{\r
136 bank&=NSFMaxBank;\r
137 if(NSFHeader.SoundChip&4)\r
138 memcpy(ExWRAM+(A-0x6000),NSFDATA+(bank<<12),4096);\r
139 else\r
140 setprg4(A,bank);\r
141}\r
142\r
143int NSFLoad(int fp)\r
144{\r
145 int x;\r
146\r
147 FCEU_fseek(fp,0,SEEK_SET);\r
148 FCEU_fread(&NSFHeader,1,0x80,fp);\r
149 if(memcmp(NSFHeader.ID,"NESM\x1a",5))\r
150 return 0;\r
151 NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0;\r
152\r
153 LoadAddr=NSFHeader.LoadAddressLow;\r
154 LoadAddr|=NSFHeader.LoadAddressHigh<<8;\r
155\r
156 if(LoadAddr<0x6000)\r
157 {\r
158 FCEUD_PrintError("Invalid load address.");\r
159 return(0);\r
160 }\r
161 InitAddr=NSFHeader.InitAddressLow;\r
162 InitAddr|=NSFHeader.InitAddressHigh<<8;\r
163\r
164 PlayAddr=NSFHeader.PlayAddressLow;\r
165 PlayAddr|=NSFHeader.PlayAddressHigh<<8;\r
166\r
167 NSFSize=FCEU_fgetsize(fp)-0x80;\r
168\r
169 NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096);\r
170 NSFMaxBank=uppow2(NSFMaxBank);\r
171\r
172 if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096)))\r
173 return 0;\r
174\r
175 FCEU_fseek(fp,0x80,SEEK_SET);\r
176 memset(NSFDATA,0x00,NSFMaxBank*4096);\r
177 FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp);\r
178\r
179 NSFMaxBank--;\r
180\r
181 BSon=0;\r
182 for(x=0;x<8;x++)\r
183 BSon|=NSFHeader.BankSwitch[x];\r
184\r
185 FCEUGameInfo.type=GIT_NSF;\r
186 FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_GAMEPAD;\r
187 FCEUGameInfo.cspecial=SIS_NSF;\r
188\r
189 for(x=0;;x++)\r
190 {\r
191 if(NSFROM[x]==0x20)\r
192 {\r
193 NSFROM[x+1]=InitAddr&0xFF;\r
194 NSFROM[x+2]=InitAddr>>8;\r
195 NSFROM[x+8]=PlayAddr&0xFF;\r
196 NSFROM[x+9]=PlayAddr>>8;\r
197 break;\r
198 }\r
199 }\r
200\r
201 if(NSFHeader.VideoSystem==0)\r
202 FCEUGameInfo.vidsys=GIV_NTSC;\r
203 else if(NSFHeader.VideoSystem==1)\r
204 FCEUGameInfo.vidsys=GIV_PAL;\r
205\r
206 GameInterface=NSFGI;\r
207\r
208 FCEU_printf("NSF Loaded. File information:\n\n");\r
209 FCEU_printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright);\r
210 if(NSFHeader.SoundChip)\r
211 {\r
212 static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"};\r
213\r
214 for(x=0;x<6;x++)\r
215 if(NSFHeader.SoundChip&(1<<x))\r
216 {\r
217 FCEU_printf(" Expansion hardware: %s\n",tab[x]);\r
218 NSFHeader.SoundChip=1<<x; /* Prevent confusing weirdness if more than one bit is set. */\r
219 break;\r
220 }\r
221 }\r
222 if(BSon)\r
223 FCEU_printf(" Bank-switched.\n");\r
224 FCEU_printf(" Load address: $%04x\n Init address: $%04x\n Play address: $%04x\n",LoadAddr,InitAddr,PlayAddr);\r
225 FCEU_printf(" %s\n",(NSFHeader.VideoSystem&1)?"PAL":"NTSC");\r
226 FCEU_printf(" Starting song: %d / %d\n\n",NSFHeader.StartingSong,NSFHeader.TotalSongs);\r
227\r
228 if(NSFHeader.SoundChip&4)\r
229 ExWRAM=FCEU_gmalloc(32768+8192);\r
230 else\r
231 ExWRAM=FCEU_gmalloc(8192);\r
232\r
233 FCEUI_SetVidSystem(NSFHeader.VideoSystem);\r
234\r
235 return 1;\r
236}\r
237\r
238static DECLFR(NSFVectorRead)\r
239{\r
240 if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2) || doreset)\r
241 {\r
242 if(A==0xFFFA) return(0x00);\r
243 else if(A==0xFFFB) return(0x38);\r
244 else if(A==0xFFFC) return(0x20);\r
245 else if(A==0xFFFD) {doreset=0;return(0x38);}\r
246 return(X.DB);\r
247 }\r
248 else\r
249 return(CartBR(A));\r
250}\r
251\r
252void NSFVRC6_Init(void);\r
253void NSFVRC7_Init(void);\r
254void NSFMMC5_Init(void);\r
255void NSFN106_Init(void);\r
256void NSFAY_Init(void);\r
257\r
258void NSF_init(void)\r
259{\r
260 doreset=1;\r
261\r
262 ResetCartMapping();\r
263 if(NSFHeader.SoundChip&4)\r
264 {\r
265 SetupCartPRGMapping(0,ExWRAM,32768+8192,1);\r
266 setprg32(0x6000,0);\r
267 setprg8(0xE000,4);\r
268 memset(ExWRAM,0x00,32768+8192);\r
269 SetWriteHandler(0x6000,0xDFFF,CartBW);\r
270 SetReadHandler(0x6000,0xFFFF,CartBR);\r
271 }\r
272 else\r
273 {\r
274 memset(ExWRAM,0x00,8192);\r
275 SetReadHandler(0x6000,0x7FFF,CartBR);\r
276 SetWriteHandler(0x6000,0x7FFF,CartBW);\r
277 SetupCartPRGMapping(0,NSFDATA,((NSFMaxBank+1)*4096),0);\r
278 SetupCartPRGMapping(1,ExWRAM,8192,1);\r
279 setprg8r(1,0x6000,0);\r
280 SetReadHandler(0x8000,0xFFFF,CartBR);\r
281 }\r
282\r
283 if(BSon)\r
284 {\r
285 int32 x;\r
286 for(x=0;x<8;x++)\r
287 {\r
288 if(NSFHeader.SoundChip&4 && x>=6)\r
289 BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]);\r
290 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
291 }\r
292 }\r
293 else\r
294 {\r
295 int32 x;\r
296 for(x=(LoadAddr&0xF000);x<0x10000;x+=0x1000)\r
297 BANKSET(x,((x-(LoadAddr&0x7000))>>12));\r
298 }\r
299\r
300 SetReadHandler(0xFFFA,0xFFFD,NSFVectorRead);\r
301\r
302 SetWriteHandler(0x2000,0x3fff,0);\r
303 SetReadHandler(0x2000,0x37ff,0);\r
304 SetReadHandler(0x3836,0x3FFF,0);\r
305 SetReadHandler(0x3800,0x3835,NSFROMRead);\r
13624c8f 306 Page[0x3800>>11]=NSFROM-0x3800; // this is required for asm core to work.\r
d97315ac 307\r
308 SetWriteHandler(0x5ff6,0x5fff,NSF_write);\r
309\r
310 SetWriteHandler(0x3ff0,0x3fff,NSF_write);\r
311 SetReadHandler(0x3ff0,0x3fff,NSF_read);\r
312\r
313\r
314 if(NSFHeader.SoundChip&1) {\r
315 NSFVRC6_Init();\r
316 } else if(NSFHeader.SoundChip&2) {\r
317 NSFVRC7_Init();\r
318 } else if(NSFHeader.SoundChip&4) {\r
319 FDSSoundReset();\r
320 } else if(NSFHeader.SoundChip&8) {\r
321 NSFMMC5_Init();\r
322 } else if(NSFHeader.SoundChip&0x10) {\r
323 NSFN106_Init();\r
324 } else if(NSFHeader.SoundChip&0x20) {\r
325 NSFAY_Init();\r
326 }\r
327 CurrentSong=NSFHeader.StartingSong;\r
328 SongReload=0xFF;\r
329 NSFNMIFlags=0;\r
330}\r
331\r
332static DECLFW(NSF_write)\r
333{\r
334 switch(A)\r
335 {\r
336 case 0x3FF3:NSFNMIFlags|=1;break;\r
337 case 0x3FF4:NSFNMIFlags&=~2;break;\r
338 case 0x3FF5:NSFNMIFlags|=2;break;\r
339\r
340 case 0x5FF6:\r
341 case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return;\r
342 case 0x5FF8:\r
343 case 0x5FF9:\r
344 case 0x5FFA:\r
345 case 0x5FFB:\r
346 case 0x5FFC:\r
347 case 0x5FFD:\r
348 case 0x5FFE:\r
349 case 0x5FFF:if(!BSon) return;\r
350 A&=0xF;\r
351 BANKSET((A*4096),V);\r
352 break;\r
353 }\r
354}\r
355\r
356static DECLFR(NSF_read)\r
357{\r
358 int x;\r
359\r
360 switch(A)\r
361 {\r
362 case 0x3ff0:x=SongReload;\r
363 if(!fceuindbg)\r
364 SongReload=0;\r
365 return x;\r
366 case 0x3ff1:\r
367 if(!fceuindbg)\r
368 {\r
369 memset(RAM,0x00,0x800);\r
370\r
371 BWrite[0x4015](0x4015,0x0);\r
372 for(x=0;x<0x14;x++)\r
373 BWrite[0x4000+x](0x4000+x,0);\r
374 BWrite[0x4015](0x4015,0xF);\r
375\r
376 if(NSFHeader.SoundChip&4)\r
377 {\r
378 BWrite[0x4017](0x4017,0xC0); /* FDS BIOS writes $C0 */\r
379 BWrite[0x4089](0x4089,0x80);\r
380 BWrite[0x408A](0x408A,0xE8);\r
381 }\r
382 else\r
383 {\r
384 memset(ExWRAM,0x00,8192);\r
385 BWrite[0x4017](0x4017,0xC0);\r
386 BWrite[0x4017](0x4017,0xC0);\r
387 BWrite[0x4017](0x4017,0x40);\r
388 }\r
389\r
390 if(BSon)\r
391 {\r
392 for(x=0;x<8;x++)\r
393 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);\r
394 }\r
395 return (CurrentSong-1);\r
396 }\r
397 case 0x3FF3:return PAL;\r
398 }\r
399 return 0;\r
400}\r
401\r
402uint8 FCEU_GetJoyJoy(void);\r
403\r
404static int special=0;\r
405\r
406void DrawNSF(uint8 *XBuf)\r
407{\r
408 char snbuf[16];\r
c4980f9e 409 int32 mul=0;\r
d97315ac 410 int x;\r
411\r
412 if(vismode==0) return;\r
413\r
13624c8f 414 for (x=0;x<240;x++)\r
415 memset(XBuf+SCREEN_OFFS+x*SCREEN_WIDTH,0,256);\r
d97315ac 416\r
c4980f9e 417 if(FSettings.SoundVolume)\r
21afaa36 418 mul=8192*240/(16384*FSettings.SoundVolume/50);\r
c4980f9e 419\r
d97315ac 420 {\r
c4980f9e 421 int16 *Bufpl;\r
d97315ac 422\r
423 int l;\r
424 l=GetSoundBuffer(&Bufpl);\r
425\r
426 if(special==0)\r
427 {\r
d97315ac 428 for(x=0;x<256;x++)\r
429 {\r
430 uint32 y;\r
431 y=142+((Bufpl[(x*l)>>8]*mul)>>14);\r
432 if(y<240)\r
92764e62 433 XBuf[x+y*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 434 }\r
435 }\r
436 else if(special==1)\r
437 {\r
d97315ac 438 for(x=0;x<256;x++)\r
439 {\r
440 double r;\r
441 uint32 xp,yp;\r
442\r
443 r=(Bufpl[(x*l)>>8]*mul)>>14;\r
444 xp=128+r*cos(x*M_PI*2/256);\r
445 yp=120+r*sin(x*M_PI*2/256);\r
446 xp&=255;\r
447 yp%=240;\r
92764e62 448 XBuf[xp+yp*SCREEN_WIDTH+SCREEN_OFFS]=3;\r
d97315ac 449 }\r
450 }\r
451 else if(special==2)\r
452 {\r
453 static double theta=0;\r
d97315ac 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