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 |
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 |
328 | static 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 |
352 | static 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 |
398 | uint8 FCEU_GetJoyJoy(void);\r |
399 | \r |
400 | static int special=0;\r |
401 | \r |
402 | void 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 |
545 | void DoNSFFrame(void)\r |
546 | {\r |
547 | if(((NSFNMIFlags&1) && SongReload) || (NSFNMIFlags&2))\r |
548 | TriggerNMI();\r |
549 | }\r |
550 | \r |
551 | void FCEUI_NSFSetVis(int mode)\r |
552 | {\r |
553 | vismode=mode;\r |
554 | }\r |
555 | \r |
556 | int 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 |
567 | int 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 |