initial fce ultra 0.81 import
[fceu.git] / nsf.c
CommitLineData
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
46uint8 SongReload;
47uint8 CurrentSong;
48
49static int sinetable[32];
50
51
52static uint8 NSFROM[0x30+6]=
53{
54/* 0x00 */
55
560x08,0x48,0x8A,0x48,0x98,0x48, /* Store regs */
570xA9,0xFF,
580x8D,0xF2,0x5F, /* NMI has occured */
590x68,0xA8,0x68,0xAA,0x68,0x28,
600x40, /* Restore regs */
61
62/* 0x12 */
63
640xAD,0xF2,0x5F, /* See if an NMI occured */
650xF0,0xFB, /* If it hasn't, loop */
66
670xA9,0x00,
680x8D,0xF2,0x5F, /* Clear play pending reg*/
69
70
710xAD,0xF0,0x5F, /* See if we need to init. */
720xF0,0x09, /* If 0, go to JMP */
73
740xAD,0xF1,0x5F, /* Confirm and load A */
750xAE,0xF3,0x5F, /* Load X with PAL/NTSC byte */
76
770x20,0x00,0x00, /* JSR to init routine */
78
790x20,0x00,0x00, /* JSR to play routine */
80
810x4C,0x12,0x38, /* Loop */
82
830xA2,0xFF,0x9A, /* Initialize the stack pointer. */
840x4C,0x12,0x38
85};
86
87static DECLFR(NSFROMRead)
88{
89 return (NSFROM-0x3800)[A];
90}
91
92
93
94static uint8 *NSFDATA=0;
95static int NSFMaxBank;
96
97static int NSFSize;
98static uint8 BSon;
99static uint16 PlayAddr;
100static uint16 InitAddr;
101static uint16 LoadAddr;
102
103NSF_HEADER NSFHeader;
104
105void 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
121static 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);
126 else
127 setprg4(A,bank);
128}
129
130int 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);
160
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;
192
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
221static DECLFW(BWRAM)
222{
223 (WRAM-0x6000)[A]=V;
224}
225
226static DECLFW(NSFFDSWrite)
227{
228 (FDSMEM-0x6000)[A]=V;
229}
230
231static DECLFR(NSFFDSRead)
232{
233 return (FDSMEM-0x6000)[A];
234}
235
236static DECLFR(AWRAM)
237{
238 return WRAM[A-0x6000];
239}
240
241void 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
285 if(NSFHeader.SoundChip&1) {
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
302static uint8 DoUpdateStuff=0;
303DECLFW(NSF_write)
304{
305switch(A)
306{
307 case 0x5FF2:if((X.PC&0xF000)==0x3000) DoUpdateStuff=V;break;
308
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);
321 break;
322}
323}
324
325DECLFR(NSF_read)
326{
327 int x;
328
329 if((X.PC&0xF000)==0x3000)
330 switch(A)
331 {
332 case 0x5ff0:x=SongReload;SongReload=0;return x;
333 case 0x5ff1:
334 {
335 memset(RAM,0x00,0x800);
336 memset(WRAM,0x00,8192);
337 BWrite[0x4015](0x4015,0xF);
338 for(x=0;x<0x14;x++)
339 {if(x!=0x11) BWrite[0x4015](0x4015,0);}
340 BWrite[0x4015](0x4015,0x0);
341 for(x=0;x<0x14;x++)
342 {if(x!=0x11) BWrite[0x4015](0x4015,0);}
343 BWrite[0x4011](0x4011,0x40);
344 BWrite[0x4015](0x4015,0xF);
345 BWrite[0x4017](0x4017,0x40);
346 if(NSFHeader.SoundChip&4)
347 BWrite[0x4089](0x4089,0x80);
348 if(BSon)
349 {
350 for(x=0;x<8;x++)
351 BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]);
352 }
353 return (CurrentSong-1);
354 }
355 case 0x5FF2:return DoUpdateStuff;
356 case 0x5FF3:return PAL;
357 }
358 return 0;
359}
360static int32 *Bufpl;
361void DrawNSF(uint8 *XBuf)
362{
363 char snbuf[16];
364 static int z=0;
365 int x,y;
366 uint8 *XBuf2,*tmpb;
367
368 XBuf+=8;
369 XBuf2=XBuf;
370
371 tmpb=NSFBG+8;
372 for(y=120;y;y--)
373 {
374 uint8 *offs;
375
376 offs=tmpb+sinetable[((z+y)>>2)&31];
377 memcpy(XBuf2,offs,256);
378 memcpy(XBuf2+(120*272),offs,256);
379
380 XBuf2+=272;
381 tmpb+=272;
382 }
383 tmpb=NSFBG+8;
384 z=(z+1)&127;
385
386 DrawTextTrans(XBuf+10*272+4+(((31-strlen(NSFHeader.SongName))<<2)), 272, NSFHeader.SongName, 38);
387 DrawTextTrans(XBuf+30*272+4+(((31-strlen(NSFHeader.Artist))<<2)), 272, NSFHeader.Artist, 38);
388 DrawTextTrans(XBuf+50*272+4+(((31-strlen(NSFHeader.Copyright))<<2)), 272, NSFHeader.Copyright, 38);
389
390 DrawTextTrans(XBuf+90*272+4+(((31-strlen("Song:"))<<2)), 272, "Song:", 38);
391 sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs);
392 DrawTextTrans(XBuf+102*272+4+(((31-strlen(snbuf))<<2)), 272, snbuf, 38);
393
394 GetSoundBuffer(&Bufpl);
395 for(x=0;x<256;x++)
396 XBuf[x+(224-((((Bufpl[x]>>(7)^128)&255)*3)>>3))*272]=38;
397}
398
399void NSFControl(int z)
400{
401 if(z==1)
402 {
403 if(CurrentSong<NSFHeader.TotalSongs) CurrentSong++;
404 }
405 else if(z==2)
406 {
407 if(CurrentSong>1) CurrentSong--;
408 }
409 SongReload=0xFF;
410}