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 |
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 |
13624c8f |
303 | Page[0x3800>>11]=NSFROM-0x3800; // this is required for asm core to work.\r |
d97315ac |
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 |
13624c8f |
410 | for (x=0;x<240;x++)\r |
411 | memset(XBuf+SCREEN_OFFS+x*SCREEN_WIDTH,0,256);\r |
d97315ac |
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 |
92764e62 |
429 | XBuf[x+y*SCREEN_WIDTH+SCREEN_OFFS]=3;\r |
d97315ac |
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 |
92764e62 |
446 | XBuf[xp+yp*SCREEN_WIDTH+SCREEN_OFFS]=3;\r |
d97315ac |
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 |
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 |
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 |