cadb7264d6284209b74a2611878e2c5bcdf4d696
[fceu.git] / nsf.c
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);
126  else 
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);
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
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
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
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;
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
325 DECLFR(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 }
360 static int32 *Bufpl;
361 void 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
399 void 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 }