| 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 | X6502_Rebase(); |
| 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); |
| 347 | if(NSFHeader.SoundChip&4) |
| 348 | BWrite[0x4089](0x4089,0x80); |
| 349 | if(BSon) |
| 350 | { |
| 351 | for(x=0;x<8;x++) |
| 352 | BANKSET(0x8000+x*4096,NSFHeader.BankSwitch[x]); |
| 353 | X6502_Rebase(); |
| 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 | |
| 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); |
| 391 | |
| 392 | DrawTextTrans(XBuf+90*272+4+(((31-strlen("Song:"))<<2)), 272, (uint8 *)"Song:", 38); |
| 393 | sprintf(snbuf,"<%d/%d>",CurrentSong,NSFHeader.TotalSongs); |
| 394 | DrawTextTrans(XBuf+102*272+4+(((31-strlen(snbuf))<<2)), 272, (uint8 *)snbuf, 38); |
| 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) |
| 402 | { |
| 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 | } |