c62d2810 |
1 | /* FCE Ultra - NES/Famicom Emulator |
2 | * |
3 | * Copyright notice for this file: |
4 | * Copyright (C) 2002 Ben Parnell |
5 | * |
6 | * This program is free software; you can redistribute it and/or modify |
7 | * it under the terms of the GNU General Public License as published by |
8 | * the Free Software Foundation; either version 2 of the License, or |
9 | * (at your option) any later version. |
10 | * |
11 | * This program is distributed in the hope that it will be useful, |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | * GNU General Public License for more details. |
15 | * |
16 | * You should have received a copy of the GNU General Public License |
17 | * along with this program; if not, write to the Free Software |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
19 | */ |
20 | |
21 | #include <stdio.h> |
22 | #include <stdlib.h> |
23 | #include <string.h> |
24 | #include <math.h> |
25 | |
26 | #include "types.h" |
27 | #include "x6502.h" |
28 | #include "fce.h" |
29 | #include "svga.h" |
30 | #include "video.h" |
31 | #include "sound.h" |
32 | #include "ines.h" |
33 | #include "nsf.h" |
34 | #include "nsfbgnew.h" |
35 | #include "general.h" |
36 | #include "memory.h" |
37 | #include "file.h" |
38 | #include "fds.h" |
39 | #include "cart.h" |
40 | #include "input.h" |
41 | |
42 | #ifndef M_PI |
43 | #define M_PI 3.14159265358979323846 |
44 | #endif |
45 | |
46 | uint8 SongReload; |
47 | uint8 CurrentSong; |
48 | |
49 | static int sinetable[32]; |
50 | |
51 | |
52 | static uint8 NSFROM[0x30+6]= |
53 | { |
54 | /* 0x00 */ |
55 | |
56 | 0x08,0x48,0x8A,0x48,0x98,0x48, /* Store regs */ |
57 | 0xA9,0xFF, |
58 | 0x8D,0xF2,0x5F, /* NMI has occured */ |
59 | 0x68,0xA8,0x68,0xAA,0x68,0x28, |
60 | 0x40, /* Restore regs */ |
61 | |
62 | /* 0x12 */ |
63 | |
64 | 0xAD,0xF2,0x5F, /* See if an NMI occured */ |
65 | 0xF0,0xFB, /* If it hasn't, loop */ |
66 | |
67 | 0xA9,0x00, |
68 | 0x8D,0xF2,0x5F, /* Clear play pending reg*/ |
69 | |
70 | |
71 | 0xAD,0xF0,0x5F, /* See if we need to init. */ |
72 | 0xF0,0x09, /* If 0, go to JMP */ |
73 | |
74 | 0xAD,0xF1,0x5F, /* Confirm and load A */ |
75 | 0xAE,0xF3,0x5F, /* Load X with PAL/NTSC byte */ |
76 | |
77 | 0x20,0x00,0x00, /* JSR to init routine */ |
78 | |
79 | 0x20,0x00,0x00, /* JSR to play routine */ |
80 | |
81 | 0x4C,0x12,0x38, /* Loop */ |
82 | |
83 | 0xA2,0xFF,0x9A, /* Initialize the stack pointer. */ |
84 | 0x4C,0x12,0x38 |
85 | }; |
86 | |
87 | static DECLFR(NSFROMRead) |
88 | { |
89 | return (NSFROM-0x3800)[A]; |
90 | } |
91 | |
92 | |
93 | |
94 | static uint8 *NSFDATA=0; |
95 | static int NSFMaxBank; |
96 | |
97 | static int NSFSize; |
98 | static uint8 BSon; |
99 | static uint16 PlayAddr; |
100 | static uint16 InitAddr; |
101 | static uint16 LoadAddr; |
102 | |
103 | NSF_HEADER NSFHeader; |
104 | |
105 | void NSFGI(int h) |
106 | { |
107 | switch(h) |
108 | { |
109 | case GI_CLOSE: |
110 | if(NSFDATA) {free(NSFDATA);NSFDATA=0;} |
111 | break; |
112 | case GI_POWER: NSF_init();break; |
113 | } |
114 | } |
115 | |
116 | // First 32KB is reserved for sound chip emulation in the iNES mapper code. |
117 | |
118 | #define WRAM (GameMemBlock+32768) |
119 | #define FDSMEM (GameMemBlock+32768) |
120 | |
121 | static INLINE void BANKSET(uint32 A, uint32 bank) |
122 | { |
123 | bank&=NSFMaxBank; |
124 | if(NSFHeader.SoundChip&4) |
125 | memcpy(FDSMEM+(A-0x6000),NSFDATA+(bank<<12),4096); |
c0bf6f9f |
126 | else |
c62d2810 |
127 | setprg4(A,bank); |
128 | } |
129 | |
130 | int NSFLoad(int fp) |
131 | { |
132 | int x; |
133 | |
134 | FCEU_fseek(fp,0,SEEK_SET); |
135 | FCEU_fread(&NSFHeader,1,0x80,fp); |
136 | if (memcmp(NSFHeader.ID,"NESM\x1a",5)) |
137 | return 0; |
138 | NSFHeader.SongName[31]=NSFHeader.Artist[31]=NSFHeader.Copyright[31]=0; |
139 | |
140 | LoadAddr=NSFHeader.LoadAddressLow; |
141 | LoadAddr|=NSFHeader.LoadAddressHigh<<8; |
142 | |
143 | InitAddr=NSFHeader.InitAddressLow; |
144 | InitAddr|=NSFHeader.InitAddressHigh<<8; |
145 | |
146 | PlayAddr=NSFHeader.PlayAddressLow; |
147 | PlayAddr|=NSFHeader.PlayAddressHigh<<8; |
148 | |
149 | NSFSize=FCEU_fgetsize(fp)-0x80; |
150 | |
151 | NSFMaxBank=((NSFSize+(LoadAddr&0xfff)+4095)/4096); |
152 | NSFMaxBank=uppow2(NSFMaxBank); |
153 | |
154 | if(!(NSFDATA=(uint8 *)FCEU_malloc(NSFMaxBank*4096))) |
155 | return 0; |
156 | |
157 | FCEU_fseek(fp,0x80,SEEK_SET); |
158 | memset(NSFDATA,0x00,NSFMaxBank*4096); |
159 | FCEU_fread(NSFDATA+(LoadAddr&0xfff),1,NSFSize,fp); |
c0bf6f9f |
160 | |
c62d2810 |
161 | NSFMaxBank--; |
162 | |
163 | BSon=0; |
164 | for(x=0;x<8;x++) |
165 | BSon|=NSFHeader.BankSwitch[x]; |
166 | |
167 | FCEUGameInfo.type=GIT_NSF; |
168 | FCEUGameInfo.input[0]=FCEUGameInfo.input[1]=SI_NONE; |
169 | |
170 | for(x=0;;x++) |
171 | { |
172 | if(NSFROM[x]==0x20) |
173 | { |
174 | NSFROM[x+1]=InitAddr&0xFF; |
175 | NSFROM[x+2]=InitAddr>>8; |
176 | NSFROM[x+4]=PlayAddr&0xFF; |
177 | NSFROM[x+5]=PlayAddr>>8; |
178 | break; |
179 | } |
180 | } |
181 | |
182 | if(NSFHeader.VideoSystem==0) |
183 | FCEUGameInfo.vidsys=GIV_NTSC; |
184 | else if(NSFHeader.VideoSystem==1) |
185 | FCEUGameInfo.vidsys=GIV_PAL; |
186 | |
187 | { |
188 | double fruit=0; |
189 | for(x=0;x<32;x++) |
190 | { |
191 | double ta,no; |
c0bf6f9f |
192 | |
c62d2810 |
193 | ta=sin(fruit)*7; |
194 | ta+=modf(ta,&no); |
195 | sinetable[x]=ta; |
196 | fruit+=(double)M_PI*2/32; |
197 | } |
198 | } |
199 | GameInterface=NSFGI; |
200 | |
201 | puts("NSF Loaded. File information:\n"); |
202 | printf(" Name: %s\n Artist: %s\n Copyright: %s\n\n",NSFHeader.SongName,NSFHeader.Artist,NSFHeader.Copyright); |
203 | if(NSFHeader.SoundChip) |
204 | { |
205 | static char *tab[6]={"Konami VRCVI","Konami VRCVII","Nintendo FDS","Nintendo MMC5","Namco 106","Sunsoft FME-07"}; |
206 | for(x=0;x<6;x++) |
207 | if(NSFHeader.SoundChip&(1<<x)) |
208 | { |
209 | printf(" Expansion hardware: %s\n",tab[x]); |
210 | break; |
211 | } |
212 | } |
213 | if(BSon) |
214 | puts(" Bank-switched."); |
215 | printf(" Load address: $%04x\n Init address: $%04x\n Play address: $%04x\n",LoadAddr,InitAddr,PlayAddr); |
216 | printf(" %s\n",(NSFHeader.VideoSystem&1)?"PAL":"NTSC"); |
217 | printf(" Starting song: %d / %d\n\n",NSFHeader.StartingSong,NSFHeader.TotalSongs); |
218 | return 1; |
219 | } |
220 | |
221 | static DECLFW(BWRAM) |
222 | { |
223 | (WRAM-0x6000)[A]=V; |
224 | } |
225 | |
226 | static DECLFW(NSFFDSWrite) |
227 | { |
228 | (FDSMEM-0x6000)[A]=V; |
229 | } |
230 | |
231 | static DECLFR(NSFFDSRead) |
232 | { |
233 | return (FDSMEM-0x6000)[A]; |
234 | } |
235 | |
236 | static DECLFR(AWRAM) |
237 | { |
238 | return WRAM[A-0x6000]; |
239 | } |
240 | |
241 | void NSF_init(void) |
242 | { |
243 | if(NSFHeader.SoundChip&4) |
244 | { |
245 | memset(FDSMEM,0x00,32768+8192); |
246 | SetWriteHandler(0x6000,0xDFFF,NSFFDSWrite); |
247 | SetReadHandler(0x6000,0xFFFF,NSFFDSRead); |
248 | } |
249 | else |
250 | { |
251 | memset(WRAM,0x00,8192); |
252 | SetReadHandler(0x6000,0x7FFF,AWRAM); |
253 | SetWriteHandler(0x6000,0x7FFF,BWRAM); |
254 | ResetCartMapping(); |
255 | SetupCartPRGMapping(0,NSFDATA,((NSFMaxBank+1)*4096),0); |
256 | SetReadHandler(0x8000,0xFFFF,CartBR); |
257 | } |
258 | |
259 | if(BSon) |
260 | { |
261 | int32 x; |
262 | for(x=0;x<8;x++) |
263 | { |
264 | if(NSFHeader.SoundChip&4 && x>=6) |
265 | BANKSET(0x6000+(x-6)*4096,NSFHeader.BankSwitch[x]); |
266 | BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]); |
267 | } |
268 | } |
269 | else |
270 | { |
271 | int32 x; |
272 | for(x=(LoadAddr&0x7000);x<0x8000;x+=0x1000) |
273 | BANKSET(0x8000+x,((x-(LoadAddr&0x7000))>>12)); |
274 | } |
275 | |
276 | SetWriteHandler(0x2000,0x3fff,0); |
277 | SetReadHandler(0x2000,0x37ff,0); |
278 | SetReadHandler(0x3836,0x3FFF,0); |
279 | SetReadHandler(0x3800,0x3835,NSFROMRead); |
280 | |
281 | SetWriteHandler(0x4020,0x5fff,NSF_write); |
282 | SetReadHandler(0x4020,0x5fff,NSF_read); |
283 | |
284 | |
c0bf6f9f |
285 | if(NSFHeader.SoundChip&1) { |
c62d2810 |
286 | VRC6_ESI(0); |
287 | } else if (NSFHeader.SoundChip&2) { |
288 | VRC7_ESI(); |
289 | } else if (NSFHeader.SoundChip&4) { |
290 | FDSSoundReset(); |
291 | } else if (NSFHeader.SoundChip&8) { |
292 | Mapper5_ESI(); |
293 | } else if (NSFHeader.SoundChip&0x10) { |
294 | Mapper19_ESI(); |
295 | } else if (NSFHeader.SoundChip&0x20) { |
296 | Mapper69_ESI(); |
297 | } |
298 | CurrentSong=NSFHeader.StartingSong; |
299 | SongReload=1; |
300 | } |
301 | |
302 | static uint8 DoUpdateStuff=0; |
303 | DECLFW(NSF_write) |
304 | { |
305 | switch(A) |
306 | { |
307 | case 0x5FF2:if((X.PC&0xF000)==0x3000) DoUpdateStuff=V;break; |
c0bf6f9f |
308 | |
c62d2810 |
309 | case 0x5FF6: |
310 | case 0x5FF7:if(!(NSFHeader.SoundChip&4)) return; |
311 | case 0x5FF8: |
312 | case 0x5FF9: |
313 | case 0x5FFA: |
314 | case 0x5FFB: |
315 | case 0x5FFC: |
316 | case 0x5FFD: |
317 | case 0x5FFE: |
318 | case 0x5FFF:if(!BSon) return; |
319 | A&=0xF; |
320 | BANKSET((A*4096),V); |
c0bf6f9f |
321 | X6502_Rebase(); |
c62d2810 |
322 | break; |
323 | } |
324 | } |
325 | |
326 | DECLFR(NSF_read) |
327 | { |
328 | int x; |
329 | |
330 | if((X.PC&0xF000)==0x3000) |
331 | switch(A) |
332 | { |
333 | case 0x5ff0:x=SongReload;SongReload=0;return x; |
334 | case 0x5ff1: |
335 | { |
336 | memset(RAM,0x00,0x800); |
337 | memset(WRAM,0x00,8192); |
338 | BWrite[0x4015](0x4015,0xF); |
339 | for(x=0;x<0x14;x++) |
340 | {if(x!=0x11) BWrite[0x4015](0x4015,0);} |
341 | BWrite[0x4015](0x4015,0x0); |
342 | for(x=0;x<0x14;x++) |
343 | {if(x!=0x11) BWrite[0x4015](0x4015,0);} |
344 | BWrite[0x4011](0x4011,0x40); |
345 | BWrite[0x4015](0x4015,0xF); |
346 | BWrite[0x4017](0x4017,0x40); |
c0bf6f9f |
347 | if(NSFHeader.SoundChip&4) |
c62d2810 |
348 | BWrite[0x4089](0x4089,0x80); |
349 | if(BSon) |
350 | { |
351 | for(x=0;x<8;x++) |
352 | BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]); |
c0bf6f9f |
353 | X6502_Rebase(); |
c62d2810 |
354 | } |
355 | return (CurrentSong-1); |
356 | } |
357 | case 0x5FF2:return DoUpdateStuff; |
358 | case 0x5FF3:return PAL; |
359 | } |
360 | return 0; |
361 | } |
362 | static int32 *Bufpl; |
363 | void DrawNSF(uint8 *XBuf) |
364 | { |
365 | char snbuf[16]; |
366 | static int z=0; |
367 | int x,y; |
368 | uint8 *XBuf2,*tmpb; |
369 | |
370 | XBuf+=8; |
371 | XBuf2=XBuf; |
372 | |
373 | tmpb=NSFBG+8; |
374 | for(y=120;y;y--) |
375 | { |
376 | uint8 *offs; |
377 | |
378 | offs=tmpb+sinetable[((z+y)>>2)&31]; |
379 | memcpy(XBuf2,offs,256); |
380 | memcpy(XBuf2+(120*272),offs,256); |
381 | |
382 | XBuf2+=272; |
383 | tmpb+=272; |
384 | } |
385 | tmpb=NSFBG+8; |
386 | z=(z+1)&127; |
387 | |
5232c20c |
388 | DrawTextTrans(XBuf+10*272+4+(((31-strlen((char *)(NSFHeader.SongName)))<<2)), 272, NSFHeader.SongName, 38); |
389 | DrawTextTrans(XBuf+30*272+4+(((31-strlen((char *)(NSFHeader.Artist)))<<2)), 272, NSFHeader.Artist, 38); |
390 | DrawTextTrans(XBuf+50*272+4+(((31-strlen((char *)(NSFHeader.Copyright)))<<2)), 272, NSFHeader.Copyright, 38); |
c62d2810 |
391 | |
5232c20c |
392 | DrawTextTrans(XBuf+90*272+4+(((31-strlen("Song:"))<<2)), 272, (uint8 *)"Song:", 38); |
c62d2810 |
393 | sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs); |
5232c20c |
394 | DrawTextTrans(XBuf+102*272+4+(((31-strlen(snbuf))<<2)), 272, (uint8 *)snbuf, 38); |
c62d2810 |
395 | |
396 | GetSoundBuffer(&Bufpl); |
397 | for(x=0;x<256;x++) |
398 | XBuf[x+(224-((((Bufpl[x]>>(7)^128)&255)*3)>>3))*272]=38; |
399 | } |
400 | |
401 | void NSFControl(int z) |
c0bf6f9f |
402 | { |
c62d2810 |
403 | if(z==1) |
404 | { |
405 | if(CurrentSong<NSFHeader.TotalSongs) CurrentSong++; |
406 | } |
407 | else if(z==2) |
408 | { |
409 | if(CurrentSong>1) CurrentSong--; |
410 | } |
411 | SongReload=0xFF; |
412 | } |