| 1 | /* FCE Ultra - NES/Famicom Emulator |
| 2 | * |
| 3 | * Copyright notice for this file: |
| 4 | * Copyright (C) 1998 BERO |
| 5 | * Copyright (C) 2002 Ben Parnell |
| 6 | * |
| 7 | * This program is free software; you can redistribute it and/or modify |
| 8 | * it under the terms of the GNU General Public License as published by |
| 9 | * the Free Software Foundation; either version 2 of the License, or |
| 10 | * (at your option) any later version. |
| 11 | * |
| 12 | * This program is distributed in the hope that it will be useful, |
| 13 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| 14 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| 15 | * GNU General Public License for more details. |
| 16 | * |
| 17 | * You should have received a copy of the GNU General Public License |
| 18 | * along with this program; if not, write to the Free Software |
| 19 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 20 | */ |
| 21 | |
| 22 | #include <stdio.h> |
| 23 | #include <stdlib.h> |
| 24 | #include <string.h> |
| 25 | |
| 26 | #include "types.h" |
| 27 | #include "x6502.h" |
| 28 | #include "fce.h" |
| 29 | #define INESPRIV |
| 30 | #include "ines.h" |
| 31 | #include "version.h" |
| 32 | #include "svga.h" |
| 33 | #include "general.h" |
| 34 | #include "state.h" |
| 35 | #include "file.h" |
| 36 | #include "memory.h" |
| 37 | #include "cart.h" |
| 38 | #include "crc32.h" |
| 39 | #include "cheat.h" |
| 40 | |
| 41 | static DECLFR(VSRead); |
| 42 | |
| 43 | static uint8 *trainerpoo=0; |
| 44 | static uint8 *ROM=NULL; |
| 45 | uint8 *VROM=NULL; |
| 46 | |
| 47 | |
| 48 | static uint32 ROM_size; |
| 49 | uint32 VROM_size; |
| 50 | |
| 51 | static void CheckVSUni(void); |
| 52 | static int MMC_init(int type); |
| 53 | void (*MapClose)(void); |
| 54 | void (*MapperReset)(void); |
| 55 | |
| 56 | static int MapperNo; |
| 57 | static int SaveGame=0; |
| 58 | |
| 59 | static iNES_HEADER head; |
| 60 | |
| 61 | /* MapperReset() is called when the NES is reset(with the reset button). |
| 62 | Mapperxxx_init is called when the NES has been powered on. |
| 63 | */ |
| 64 | |
| 65 | static void iNESGI(int h) |
| 66 | { |
| 67 | switch(h) |
| 68 | { |
| 69 | case GI_RESETM2: |
| 70 | if(MapperReset) |
| 71 | MapperReset(); |
| 72 | break; |
| 73 | case GI_POWER: |
| 74 | SetReadHandler(0x8000,0xFFFF,CartBR); |
| 75 | MMC_init(MapperNo); |
| 76 | break; |
| 77 | case GI_CLOSE: |
| 78 | { |
| 79 | FILE *sp; |
| 80 | |
| 81 | if(ROM) {free(ROM);ROM=0;} |
| 82 | if(VROM) {free(VROM);VROM=0;} |
| 83 | |
| 84 | if(SaveGame) |
| 85 | { |
| 86 | char *soot; |
| 87 | SaveGame=0; |
| 88 | soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav"); |
| 89 | sp=fopen(soot,"wb"); |
| 90 | if (sp==NULL) |
| 91 | FCEU_PrintError("WRAM file \"%s\" cannot be written to.\n",soot); |
| 92 | else |
| 93 | { |
| 94 | void *ptr; |
| 95 | uint32 amount; |
| 96 | ptr=WRAM; |
| 97 | amount=8192; |
| 98 | |
| 99 | if(MapperNo==1) |
| 100 | { |
| 101 | extern uint8 MMC1WRAMsize; |
| 102 | if(MMC1WRAMsize==2) ptr=WRAM+8192; |
| 103 | } |
| 104 | else if(MapperNo==5) |
| 105 | { |
| 106 | extern uint8 MMC5WRAMsize; |
| 107 | if(MMC5WRAMsize==4) |
| 108 | amount=32768; |
| 109 | } |
| 110 | |
| 111 | fwrite(ptr,1,amount,sp); |
| 112 | fclose(sp); |
| 113 | } |
| 114 | } |
| 115 | if(MapClose) MapClose(); |
| 116 | if(trainerpoo) {free(trainerpoo);trainerpoo=0;} |
| 117 | } |
| 118 | break; |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | uint32 iNESGameCRC32; |
| 123 | |
| 124 | struct CRCMATCH { |
| 125 | uint32 crc; |
| 126 | char *name; |
| 127 | }; |
| 128 | |
| 129 | static void CheckBad(void) |
| 130 | { |
| 131 | int x; |
| 132 | #define CRCNT 7 |
| 133 | struct CRCMATCH tab[CRCNT]={ |
| 134 | {0x28d183ac,"Antarctic Adventure"}, |
| 135 | {0x7095ac65,"Akumajo Densetsu"}, |
| 136 | {0x1bd7ed5a,"Gradius 2"}, |
| 137 | {0x81c3aa66,"Crisis Force"}, |
| 138 | {0xfe3088df,"Fire Emblem Gaiden"}, |
| 139 | {0xfa8339a5,"Bucky O'Hare"}, |
| 140 | {0x3c539d78,"Ganbare Goemon 2"}, |
| 141 | }; |
| 142 | for(x=0;x<CRCNT;x++) |
| 143 | if(tab[x].crc == iNESGameCRC32) |
| 144 | { |
| 145 | FCEU_PrintError("The copy of the game you have loaded, %s, is a bad dump, has been hacked, or both. It will not work correctly on FCE Ultra. Use a good copy of the ROM image instead.",tab[x].name); |
| 146 | break; |
| 147 | } |
| 148 | } |
| 149 | |
| 150 | struct INPSEL { |
| 151 | uint32 crc32; |
| 152 | int input1; |
| 153 | int input2; |
| 154 | int inputfc; |
| 155 | }; |
| 156 | |
| 157 | /* This is mostly for my personal use. So HA. */ |
| 158 | static void SetInput(void) |
| 159 | { |
| 160 | static struct INPSEL moo[]= |
| 161 | { |
| 162 | {0xad9c63e2,SI_GAMEPAD,-1,SIFC_SHADOW}, /* Space Shadow */ |
| 163 | {0x24598791,-1,SI_ZAPPER,0}, /* Duck Hunt */ |
| 164 | {0xff24d794,-1,SI_ZAPPER,0}, /* Hogan's Alley */ |
| 165 | {0xbeb8ab01,-1,SI_ZAPPER,0}, /* Gumshoe */ |
| 166 | {0xde8fd935,-1,SI_ZAPPER,0}, /* To the Earth */ |
| 167 | {0xedc3662b,-1,SI_ZAPPER,0}, /* Operation Wolf */ |
| 168 | {0x23d17f5e,SI_GAMEPAD,SI_ZAPPER,0}, /* The Lone Ranger */ |
| 169 | {0xb8b9aca3,-1,SI_ZAPPER,0}, /* Wild Gunman */ |
| 170 | {0x5112dc21,-1,SI_ZAPPER,0}, /* Wild Gunman */ |
| 171 | {0x4318a2f8,-1,SI_ZAPPER,0}, /* Barker Bill's Trick Shooting */ |
| 172 | {0x5ee6008e,-1,SI_ZAPPER,0}, /* Mechanized Attack */ |
| 173 | {0x3e58a87e,-1,SI_ZAPPER,0}, /* Freedom Force */ |
| 174 | {0x851eb9be,SI_GAMEPAD,SI_ZAPPER,0}, /* Shooting Range */ |
| 175 | {0x74bea652,SI_GAMEPAD,SI_ZAPPER,0}, /* Supergun 3-in-1 */ |
| 176 | {0x32fb0583,-1,SI_ARKANOID,0}, /* Arkanoid(NES) */ |
| 177 | {0xd89e5a67,-1,-1,SIFC_ARKANOID}, /* Arkanoid (J) */ |
| 178 | {0x0f141525,-1,-1,SIFC_ARKANOID}, /* Arkanoid 2(J) */ |
| 179 | |
| 180 | {0xf7606810,-1,-1,SIFC_FKB}, /* Family BASIC 2.0A */ |
| 181 | {0x895037bc,-1,-1,SIFC_FKB}, /* Family BASIC 2.1a */ |
| 182 | {0xb2530afc,-1,-1,SIFC_FKB}, /* Family BASIC 3.0 */ |
| 183 | {0,-1,-1,-1} |
| 184 | }; |
| 185 | int x=0; |
| 186 | |
| 187 | while(moo[x].input1>=0 || moo[x].input2>=0 || moo[x].inputfc>=0) |
| 188 | { |
| 189 | if(moo[x].crc32==iNESGameCRC32) |
| 190 | { |
| 191 | FCEUGameInfo.input[0]=moo[x].input1; |
| 192 | FCEUGameInfo.input[1]=moo[x].input2; |
| 193 | FCEUGameInfo.inputfc=moo[x].inputfc; |
| 194 | break; |
| 195 | } |
| 196 | x++; |
| 197 | } |
| 198 | } |
| 199 | |
| 200 | struct CHINF { |
| 201 | uint32 crc32; |
| 202 | int32 mapper; |
| 203 | int32 mirror; |
| 204 | }; |
| 205 | |
| 206 | #define SAVCNT 14 |
| 207 | static void CheckHInfo(void) |
| 208 | { |
| 209 | /* ROM images that have the battery-backed bit set in the header that really |
| 210 | don't have battery-backed RAM is not that big of a problem, so I'll |
| 211 | treat this differently. |
| 212 | */ |
| 213 | |
| 214 | static uint32 savie[SAVCNT]= |
| 215 | { |
| 216 | 0x7cab2e9b,0x3ee43cda,0xe1383deb, /* Mouryou Senki Madara */ |
| 217 | 0x3b3f88f0,0x2545214c, /* Dragon Warrior PRG 0 and 1 */ |
| 218 | 0x8c5a784e, /* DW 2 */ |
| 219 | 0xa86a5318, /* DW 3 */ |
| 220 | 0x506e259d, /* DW 4 */ |
| 221 | 0xcebd2a31,0xb8b88130, /* Final Fantasy */ |
| 222 | 0x466efdc2, /* FF(J) */ |
| 223 | 0xc9556b36, /* FF1+2*/ |
| 224 | 0xd29db3c7, /* FF2 */ |
| 225 | 0x57e220d0, /* FF3 */ |
| 226 | }; |
| 227 | |
| 228 | static struct CHINF moo[]= |
| 229 | { |
| 230 | {0xc68363f6,180,0}, /* Crazy Climber */ |
| 231 | {0xbe939fce,9,1}, /* Punchout*/ |
| 232 | {0x5e66eaea,13,1}, /* Videomation */ |
| 233 | {0xaf5d7aa2,-1,0}, /* Clu Clu Land */ |
| 234 | |
| 235 | {0xc2730c30,34,0}, /* Deadly Towers */ |
| 236 | {0x932ff06e,34,1}, /* Classic Concentration */ |
| 237 | {0x4c7c1af3,34,1}, /* Caesar's Palace */ |
| 238 | {0x9ea1dc76,2,0}, /* Rainbow Islands */ |
| 239 | |
| 240 | {0x9eefb4b4,4,8}, /* Pachi Slot Adventure 2 */ |
| 241 | {0x5337f73c,4,8}, /* Niji no Silk Road */ |
| 242 | {0x7474ac92,4,8}, /* Kabuki: Quantum Fighter */ |
| 243 | |
| 244 | {0x970bd9c2,1,8}, /* Hanjuku Hero */ |
| 245 | |
| 246 | {0xbb7c5f7a,89,8}, /* Mito Koumon or something similar */ |
| 247 | /* Probably a Namco MMC3-workalike */ |
| 248 | {0xa5e6baf9,4,1|4}, /* Dragon Slayer 4 */ |
| 249 | {0xe40b4973,4,1|4}, /* Metro Cross */ |
| 250 | {0xd97c31b0,4,1|4}, /* Rasaaru Ishii no Childs Quest */ |
| 251 | |
| 252 | {0x84382231,9,0}, /* Punch Out (J) */ |
| 253 | |
| 254 | {0xfcdaca80,0,0}, /* Elevator Action */ |
| 255 | {0xe492d45a,0,0}, /* Zippy Race */ |
| 256 | {0x32fa246f,0,0}, /* Tag Team Pro Wrestling */ |
| 257 | {0x6d65cac6,2,0}, /* Terra Cresta */ |
| 258 | {0x28c11d24,2,1}, /* Sukeban Deka */ |
| 259 | {0x02863604,2,1}, /* Sukeban Deka */ |
| 260 | {0x2bb6a0f8,2,1}, /* Sherlock Holmes */ |
| 261 | {0x55773880,2,1}, /* Gilligan's Island */ |
| 262 | {0x419461d0,2,1}, /* Super Cars */ |
| 263 | {0x6e0eb43e,2,1}, /* Puss n Boots */ |
| 264 | {0xfc3e5c86,2,1}, /* Trojan */ |
| 265 | |
| 266 | {0x291bcd7d,1,8}, /* Pachio Kun 2 */ |
| 267 | {0xf74dfc91,1,-1}, /* Win, Lose, or Draw */ |
| 268 | |
| 269 | {0x59280bec,4,8}, /* Jackie Chan */ |
| 270 | |
| 271 | {0xfe364be5,1,8}, /* Deep Dungeon 4 */ |
| 272 | {0xd8ee7669,1,8}, /* Adventures of Rad Gravity */ |
| 273 | {0xa5e8d2cd,1,8}, /* Breakthru */ |
| 274 | {0xf6fa4453,1,8}, /* Bigfoot */ |
| 275 | {0x37ba3261,1,8}, /* Back to the Future 2 and 3 */ |
| 276 | {0x934db14a,1,-1}, /* All-Pro Basketball */ |
| 277 | {0xe94d5181,1,8}, /* Mirai Senshi - Lios */ |
| 278 | {0x7156cb4d,1,8}, /* Muppet Adventure Carnival thingy */ |
| 279 | {0x5b6ca654,1,8}, /* Barbie rev X*/ |
| 280 | {0x57c12280,1,8}, /* Demon Sword */ |
| 281 | |
| 282 | {0xcf322bb3,3,1}, /* John Elway's Quarterback */ |
| 283 | {0x9bde3267,3,1}, /* Adventures of Dino Riki */ |
| 284 | {0x02cc3973,3,8}, /* Ninja Kid */ |
| 285 | {0xb5d28ea2,3,1}, /* Mystery Quest - mapper 3?*/ |
| 286 | {0xbc065fc3,3,1}, /* Pipe Dream */ |
| 287 | |
| 288 | {0x5b837e8d,1,8}, /* Alien Syndrome */ |
| 289 | {0x283ad224,32,8}, /* Ai Sensei no Oshiete */ |
| 290 | {0x5555fca3,32,8}, /* "" "" */ |
| 291 | {0x243a8735,32,0x10|4}, /* Major League */ |
| 292 | |
| 293 | {0x6bc65d7e,66,1}, /* Youkai Club*/ |
| 294 | {0xc2df0a00,66,1}, /* Bio Senshi Dan(hacked) */ |
| 295 | {0xbde3ae9b,66,1}, /* Doraemon */ |
| 296 | {0xd26efd78,66,1}, /* SMB Duck Hunt */ |
| 297 | {0x811f06d9,66,1}, /* Dragon Power */ |
| 298 | |
| 299 | {0x3293afea,66,1}, /* Mississippi Satsujin Jiken */ |
| 300 | {0xe84274c5,66,1}, /* "" "" */ |
| 301 | |
| 302 | |
| 303 | {0x6e68e31a,16,8}, /* Dragon Ball 3*/ |
| 304 | |
| 305 | {0xba51ac6f,78,2}, |
| 306 | {0x3d1c3137,78,8}, |
| 307 | |
| 308 | {0xbda8f8e4,152,8}, /* Gegege no Kitarou 2 */ |
| 309 | {0x026c5fca,152,8}, /* Saint Seiya Ougon Densetsu */ |
| 310 | {0x0f141525,152,8}, /* Arkanoid 2 (Japanese) */ |
| 311 | {0xb1a94b82,152,8}, /* Pocket Zaurus */ |
| 312 | |
| 313 | {0x3f15d20d,153,8}, /* Famicom Jump 2 */ |
| 314 | |
| 315 | {0xbba58be5,70,-1}, /* Family Trainer - Manhattan Police */ |
| 316 | {0x370ceb65,70,-1}, /* Family Trainer - Meiro Dai Sakusen */ |
| 317 | {0xdd8ed0f7,70,1}, /* Kamen Rider Club */ |
| 318 | |
| 319 | {0x90c773c1,118,-1}, /* Goal! 2 */ |
| 320 | {0xb9b4d9e0,118,-1}, /* NES Play Action Football */ |
| 321 | {0x78b657ac,118,-1}, /* Armadillo */ |
| 322 | {0x37b62d04,118,-1}, /* Ys 3 */ |
| 323 | {0x07d92c31,118,-1}, /* RPG Jinsei Game */ |
| 324 | {0x2705eaeb,234,-1}, /* Maxi 15 */ |
| 325 | {0x404b2e8b,4,2}, /* Rad Racer 2 */ |
| 326 | |
| 327 | {0x1356f6a6,4,8}, /* "Cattou Ninden Teyandee" English tranlation. |
| 328 | Should I have even bothered to do this? */ |
| 329 | {0x50fd4fd6,4,8}, /* "" "" */ |
| 330 | |
| 331 | {0xa912b064,51|0x800,8}, /* 11-in-1 Ball Games(has CHR ROM when it shouldn't) */ |
| 332 | {0,-1,-1} |
| 333 | }; |
| 334 | int tofix=0; |
| 335 | int x=0; |
| 336 | |
| 337 | do |
| 338 | { |
| 339 | if(moo[x].crc32==iNESGameCRC32) |
| 340 | { |
| 341 | if(moo[x].mapper>=0) |
| 342 | { |
| 343 | if(moo[x].mapper&0x800 && VROM_size) |
| 344 | { |
| 345 | VROM_size=0; |
| 346 | free(VROM); |
| 347 | tofix|=8; |
| 348 | } |
| 349 | if(MapperNo!=(moo[x].mapper&0xFF)) |
| 350 | { |
| 351 | tofix|=1; |
| 352 | MapperNo=moo[x].mapper&0xFF; |
| 353 | } |
| 354 | } |
| 355 | if(moo[x].mirror>=0) |
| 356 | { |
| 357 | if(moo[x].mirror==8) |
| 358 | { |
| 359 | if(Mirroring==2) /* Anything but hard-wired(four screen). */ |
| 360 | { |
| 361 | tofix|=2; |
| 362 | Mirroring=0; |
| 363 | } |
| 364 | } |
| 365 | else if(Mirroring!=moo[x].mirror) |
| 366 | { |
| 367 | if(Mirroring!=(moo[x].mirror&~4)) |
| 368 | if((moo[x].mirror&~4)<=2) /* Don't complain if one-screen mirroring |
| 369 | needs to be set(the iNES header can't |
| 370 | hold this information). |
| 371 | */ |
| 372 | tofix|=2; |
| 373 | Mirroring=moo[x].mirror; |
| 374 | } |
| 375 | } |
| 376 | break; |
| 377 | } |
| 378 | x++; |
| 379 | } while(moo[x].mirror>=0 || moo[x].mapper>=0); |
| 380 | |
| 381 | for(x=0;x<SAVCNT;x++) |
| 382 | { |
| 383 | if(savie[x]==iNESGameCRC32) |
| 384 | { |
| 385 | if(!(head.ROM_type&2)) |
| 386 | { |
| 387 | tofix|=4; |
| 388 | head.ROM_type|=2; |
| 389 | } |
| 390 | } |
| 391 | } |
| 392 | |
| 393 | /* Games that use these iNES mappers tend to have the four-screen bit set |
| 394 | when it should not be. |
| 395 | */ |
| 396 | if((MapperNo==118 || MapperNo==24 || MapperNo==26) && (Mirroring==2)) |
| 397 | { |
| 398 | Mirroring=0; |
| 399 | tofix|=2; |
| 400 | } |
| 401 | |
| 402 | if(tofix) |
| 403 | { |
| 404 | char gigastr[768]; |
| 405 | strcpy(gigastr,"The iNES header contains incorrect information. For now, the information will be corrected in RAM. "); |
| 406 | if(tofix&1) |
| 407 | sprintf(gigastr+strlen(gigastr),"The mapper number should be set to %d. ",MapperNo); |
| 408 | if(tofix&2) |
| 409 | { |
| 410 | char *mstr[3]={"Horizontal","Vertical","Four-screen"}; |
| 411 | sprintf(gigastr+strlen(gigastr),"Mirroring should be set to \"%s\". ",mstr[Mirroring&3]); |
| 412 | } |
| 413 | if(tofix&4) |
| 414 | strcat(gigastr,"The battery-backed bit should be set. "); |
| 415 | if(tofix&8) |
| 416 | strcat(gigastr,"This game should not have any CHR ROM. "); |
| 417 | strcat(gigastr,"\n"); |
| 418 | FCEUD_PrintError(gigastr); |
| 419 | } |
| 420 | } |
| 421 | |
| 422 | int iNESLoad(char *name, int fp) |
| 423 | { |
| 424 | FILE *sp; |
| 425 | |
| 426 | if(FCEU_fread(&head,1,16,fp)!=16) |
| 427 | return 0; |
| 428 | |
| 429 | if(memcmp(&head,"NES\x1a",4)) |
| 430 | return 0; |
| 431 | |
| 432 | if(!memcmp((char *)(&head)+0x7,"DiskDude",8)) |
| 433 | { |
| 434 | memset((char *)(&head)+0x7,0,0x9); |
| 435 | } |
| 436 | |
| 437 | if(!memcmp((char *)(&head)+0x7,"demiforce",9)) |
| 438 | { |
| 439 | memset((char *)(&head)+0x7,0,0x9); |
| 440 | } |
| 441 | |
| 442 | if(!memcmp((char *)(&head)+0xA,"Ni03",4)) |
| 443 | { |
| 444 | if(!memcmp((char *)(&head)+0x7,"Dis",3)) |
| 445 | memset((char *)(&head)+0x7,0,0x9); |
| 446 | else |
| 447 | memset((char *)(&head)+0xA,0,0x6); |
| 448 | } |
| 449 | |
| 450 | if(!head.ROM_size) |
| 451 | head.ROM_size++; |
| 452 | |
| 453 | ROM_size = head.ROM_size; |
| 454 | VROM_size = head.VROM_size; |
| 455 | ROM_size=uppow2(ROM_size); |
| 456 | |
| 457 | if(VROM_size) |
| 458 | VROM_size=uppow2(VROM_size); |
| 459 | |
| 460 | MapperNo = (head.ROM_type>>4); |
| 461 | MapperNo|=(head.ROM_type2&0xF0); |
| 462 | |
| 463 | Mirroring = (head.ROM_type&1); |
| 464 | if(head.ROM_type&8) Mirroring=2; |
| 465 | |
| 466 | if(!(ROM=(uint8 *)FCEU_malloc(ROM_size<<14))) |
| 467 | return 0; |
| 468 | |
| 469 | if (VROM_size) |
| 470 | if(!(VROM=(uint8 *)FCEU_malloc(VROM_size<<13))) |
| 471 | { |
| 472 | free(ROM); |
| 473 | return 0; |
| 474 | } |
| 475 | |
| 476 | memset(ROM,0xFF,ROM_size<<14); |
| 477 | if(VROM_size) memset(VROM,0xFF,VROM_size<<13); |
| 478 | if(head.ROM_type&4) /* Trainer */ |
| 479 | { |
| 480 | if(!(trainerpoo=FCEU_malloc(512))) |
| 481 | return(0); |
| 482 | FCEU_fread(trainerpoo,512,1,fp); |
| 483 | } |
| 484 | ResetCartMapping(); |
| 485 | SetupCartPRGMapping(0,ROM,ROM_size*0x4000,0); |
| 486 | SetupCartPRGMapping(1,WRAM,8192,1); |
| 487 | |
| 488 | FCEU_fread(ROM,0x4000,head.ROM_size,fp); |
| 489 | |
| 490 | if(VROM_size) |
| 491 | FCEU_fread(VROM,0x2000,head.VROM_size,fp); |
| 492 | |
| 493 | printf("File %s loaded.\n",name); |
| 494 | |
| 495 | iNESGameCRC32=CalcCRC32(0,ROM,ROM_size<<14); |
| 496 | if(VROM_size) |
| 497 | iNESGameCRC32=CalcCRC32(iNESGameCRC32,VROM,VROM_size<<13); |
| 498 | printf("\n PRG ROM: %3d x 16k\n CHR ROM: %3d x 8k\n ROM CRC32: %08lx\n Mapper: %d\n Mirroring: %s\n",head.ROM_size,head.VROM_size,iNESGameCRC32,MapperNo,Mirroring==2?"None(Four-screen)":Mirroring?"Vertical":"Horizontal"); |
| 499 | if(head.ROM_type&2) puts(" Battery-backed."); |
| 500 | if(head.ROM_type&4) puts(" Trained."); |
| 501 | puts(""); |
| 502 | |
| 503 | CheckBad(); |
| 504 | SetInput(); |
| 505 | CheckHInfo(); |
| 506 | CheckVSUni(); |
| 507 | //if(MapperNo!=4 && MapperNo!=118 && MapperNo!=119) exit(1); // Testing |
| 508 | |
| 509 | /* Must remain here because above functions might change value of |
| 510 | VROM_size and free(VROM). |
| 511 | */ |
| 512 | if(VROM_size) |
| 513 | SetupCartCHRMapping(0,VROM,VROM_size*0x2000,0); |
| 514 | |
| 515 | if(Mirroring==2) |
| 516 | SetupCartMirroring(4,1,ExtraNTARAM); |
| 517 | else if(Mirroring>=0x10) |
| 518 | SetupCartMirroring(2+(Mirroring&1),1,0); |
| 519 | else |
| 520 | SetupCartMirroring(Mirroring&1,(Mirroring&4)>>2,0); |
| 521 | |
| 522 | if(MapperNo==5) DetectMMC5WRAMSize(); |
| 523 | else if(MapperNo==1) DetectMMC1WRAMSize(); |
| 524 | |
| 525 | if(head.ROM_type&2) |
| 526 | { |
| 527 | char *soot; |
| 528 | |
| 529 | SaveGame=1; |
| 530 | soot=FCEU_MakeFName(FCEUMKF_SAV,0,"sav"); |
| 531 | sp=fopen(soot,"rb"); |
| 532 | if(sp!=NULL) |
| 533 | { |
| 534 | void *ptr; |
| 535 | uint32 amount; |
| 536 | |
| 537 | ptr=WRAM; |
| 538 | amount=8192; |
| 539 | if(MapperNo==1) |
| 540 | { |
| 541 | extern uint8 MMC1WRAMsize; |
| 542 | if(MMC1WRAMsize==2) ptr=WRAM+8192; |
| 543 | } |
| 544 | else if(MapperNo==5) |
| 545 | { |
| 546 | extern uint8 MMC5WRAMsize; |
| 547 | if(MMC5WRAMsize==4) |
| 548 | amount=32768; |
| 549 | } |
| 550 | if(fread(ptr,1,amount,sp)==amount) |
| 551 | printf(" WRAM Save File \"%s\" loaded...\n",soot); |
| 552 | fclose(sp); |
| 553 | } |
| 554 | |
| 555 | } |
| 556 | GameInterface=iNESGI; |
| 557 | return 1; |
| 558 | } |
| 559 | |
| 560 | #include "banksw.h" |
| 561 | |
| 562 | |
| 563 | void FASTAPASS(1) onemir(uint8 V) |
| 564 | { |
| 565 | if(Mirroring==2) return; |
| 566 | if(V>1) |
| 567 | V=1; |
| 568 | Mirroring=0x10|V; |
| 569 | setmirror(MI_0+V); |
| 570 | } |
| 571 | |
| 572 | void FASTAPASS(1) MIRROR_SET2(uint8 V) |
| 573 | { |
| 574 | if(Mirroring==2) return; |
| 575 | Mirroring=V; |
| 576 | setmirror(V); |
| 577 | } |
| 578 | |
| 579 | void FASTAPASS(1) MIRROR_SET(uint8 V) |
| 580 | { |
| 581 | if(Mirroring==2) return; |
| 582 | V^=1; |
| 583 | Mirroring=V; |
| 584 | setmirror(V); |
| 585 | } |
| 586 | |
| 587 | static void NONE_init(void) |
| 588 | { |
| 589 | ROM_BANK16(0x8000,0); |
| 590 | ROM_BANK16(0xC000,~0); |
| 591 | |
| 592 | if(VROM_size) |
| 593 | VROM_BANK8(0); |
| 594 | else |
| 595 | setvram8(CHRRAM); |
| 596 | } |
| 597 | |
| 598 | static uint8 secdata[2][32]= |
| 599 | { |
| 600 | { |
| 601 | 0xff, 0xbf, 0xb7, 0x97, 0x97, 0x17, 0x57, 0x4f, |
| 602 | 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, 0x94, 0x14, |
| 603 | 0x56, 0x4e, 0x6f, 0x6b, 0xeb, 0xa9, 0xb1, 0x90, |
| 604 | 0xd4, 0x5c, 0x3e, 0x26, 0x87, 0x83, 0x13, 0x00 |
| 605 | }, |
| 606 | { |
| 607 | 0x00, 0x00, 0x00, 0x00, 0xb4, 0x00, 0x00, 0x00, |
| 608 | 0x00, 0x6F, 0x00, 0x00, 0x00, 0x00, 0x94, 0x00, |
| 609 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, |
| 610 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 |
| 611 | } |
| 612 | }; |
| 613 | |
| 614 | static uint8 *secptr; |
| 615 | |
| 616 | static void CheckVSUni(void) |
| 617 | { |
| 618 | FCEUGameInfo.type=GIT_VSUNI; |
| 619 | |
| 620 | /* FCE Ultra doesn't complain when headers for these games are bad. */ |
| 621 | secptr=0; |
| 622 | switch(iNESGameCRC32) |
| 623 | { |
| 624 | default:FCEUGameInfo.type=0;break; |
| 625 | |
| 626 | case 0xffbef374: pale=1;break; /* Castlevania */ |
| 627 | |
| 628 | case 0x98e3c75a: |
| 629 | case 0x7cff0f84: pale=2;break; /* SMB */ |
| 630 | |
| 631 | case 0xef7af338: pale=2;break; /* Ice Climber */ |
| 632 | case 0xabe1a0c2: FCEUGameInfo.input[0]=SI_ZAPPER;break; /*Duck hunt */ |
| 633 | case 0x16aa4e2d: pale=7;FCEUGameInfo.input[0]=SI_ZAPPER;break; /* hoganal ^_^ */ |
| 634 | case 0x2b85420e: pale=3;break; /* Dr Mario */ |
| 635 | |
| 636 | case 0xfb0ddde7: pale=2;break; |
| 637 | case 0xc95321a8: pale=6;break; /* Excitebike */ |
| 638 | case 0xb1c4c508: pale=1;break; /* Golf */ |
| 639 | |
| 640 | case 0x66471efe: break; |
| 641 | case 0xca3e9b1a: pale=7;break; /* Pinball*/ |
| 642 | |
| 643 | case 0xf735d926: pale=7; break; /* Gradius */ |
| 644 | |
| 645 | case 0x2019fe65: |
| 646 | case 0x31678411:MapperNo=68;pale=7;break; /* Platoon */ |
| 647 | |
| 648 | case 0x74f713b4:pale=4;break; /* Goonies */ |
| 649 | case 0x9ae2baa0:pale=5;break; /* Slalom */ |
| 650 | case 0xe45485a5:secptr=secdata[1];vsdip=0x20;MapperNo=4;break; /* RBI Baseball */ |
| 651 | case 0x21a653c7:vsdip=0x20;MapperNo=4;break; /* Super Sky Kid */ |
| 652 | case 0xe9a6f17d:vsdip=0x20;break; /* Tetris */ |
| 653 | |
| 654 | case 0x159ef3c1:break; /* Star Luster */ |
| 655 | case 0x9768e5e0:break; /* Stroke and Match Golf */ |
| 656 | |
| 657 | /* FCE Ultra doesn't have correct palettes for the following games. */ |
| 658 | case 0x0fa322c2:pale=2;break; /* Clu Clu Land */ |
| 659 | |
| 660 | case 0x766c2cac: /* Soccer */ |
| 661 | case 0x01357944: /* Battle City */ |
| 662 | case 0xb2816bf9:break; /* Battle City (Bootleg) */ |
| 663 | |
| 664 | case 0x832cf592:FCEUGameInfo.input[0]=SI_ZAPPER;break; /* Freedom Force */ |
| 665 | case 0x63abf889:break; /* Ladies Golf */ |
| 666 | case 0x8c0c2df5:pale=2;MapperNo=1;Mirroring=1;break; /* Top Gun */ |
| 667 | case 0x52c501d0:vsdip=0x80;MapperNo=4;secptr=secdata[0];break; |
| 668 | /* TKO Boxing */ |
| 669 | } |
| 670 | |
| 671 | if(MapperNo==99) |
| 672 | Mirroring=2; |
| 673 | } |
| 674 | |
| 675 | |
| 676 | static int VSindex; |
| 677 | |
| 678 | static DECLFR(VSRead) |
| 679 | { |
| 680 | //printf("$%04x, $%04x\n",A,X.PC); |
| 681 | switch(A) |
| 682 | { |
| 683 | default: |
| 684 | case 0x5e00: VSindex=0;return 0xFF; |
| 685 | case 0x5e01: return(secptr[(VSindex++)&0x1F]); |
| 686 | } |
| 687 | } |
| 688 | |
| 689 | static void DoVSHooks(void) |
| 690 | { |
| 691 | if(secptr) |
| 692 | SetReadHandler(0x5e00,0x5e01,VSRead); |
| 693 | } |
| 694 | |
| 695 | void (*MapInitTab[256])(void)= |
| 696 | { |
| 697 | 0, |
| 698 | Mapper1_init,Mapper2_init,Mapper3_init,Mapper4_init, |
| 699 | Mapper5_init,Mapper6_init,Mapper7_init,Mapper8_init, |
| 700 | Mapper9_init,Mapper10_init,Mapper11_init,0, |
| 701 | Mapper13_init,0,Mapper15_init,Mapper16_init, |
| 702 | Mapper17_init,Mapper18_init,Mapper19_init,0, |
| 703 | Mapper21_init,Mapper22_init,Mapper23_init,Mapper24_init, |
| 704 | Mapper25_init,Mapper26_init,0,0, |
| 705 | 0,0,0,Mapper32_init, |
| 706 | Mapper33_init,Mapper34_init,0,0, |
| 707 | 0,0,0,Mapper40_init, |
| 708 | Mapper41_init,Mapper42_init,Mapper43_init,Mapper44_init, |
| 709 | Mapper45_init,Mapper46_init,Mapper47_init,Mapper33_init,Mapper49_init,0,Mapper51_init,Mapper52_init, |
| 710 | 0,0,0,0,0,0,0,0, |
| 711 | 0,0,0,Mapper64_init, |
| 712 | Mapper65_init,Mapper66_init,Mapper67_init,Mapper68_init, |
| 713 | Mapper69_init,Mapper70_init,Mapper71_init,Mapper72_init, |
| 714 | Mapper73_init,0,Mapper75_init,Mapper76_init, |
| 715 | Mapper77_init,Mapper78_init,Mapper79_init,Mapper80_init, |
| 716 | 0,Mapper82_init,Mapper83_init,0, |
| 717 | Mapper85_init,Mapper86_init,Mapper87_init,Mapper88_init, |
| 718 | Mapper89_init,Mapper90_init,0,Mapper92_init, |
| 719 | Mapper93_init,Mapper94_init,Mapper95_init,Mapper96_init, |
| 720 | Mapper97_init,0,Mapper99_init,0, |
| 721 | 0,0,0,0,Mapper105_init,0,0,0, |
| 722 | 0,0,0,Mapper112_init,Mapper113_init,0,0,0, |
| 723 | Mapper117_init,Mapper118_init,Mapper119_init,0,0,0,0,0, |
| 724 | 0,0,0,0,0,0,0,0, |
| 725 | 0,0,0,0,0,0,0,Mapper140_init, |
| 726 | 0,0,0,0,0,0,0,0, |
| 727 | 0,0,Mapper151_init,Mapper152_init,Mapper153_init,0,0,0, |
| 728 | 0,0,0,0,0,0,0,0, |
| 729 | 0,0,0,0,0,0,0,0, |
| 730 | 0,0,0,0,0,0,0,Mapper180_init, |
| 731 | 0,Mapper182_init,0,Mapper184_init,Mapper185_init,0,0,0, |
| 732 | Mapper189_init,0,0,0,0,0,0,0, |
| 733 | 0,0,0,0,0,0,0,0, |
| 734 | 0,0,0,0,0,0,0,0, |
| 735 | 0,0,0,0,0,0,0,0, |
| 736 | 0,0,0,0,Mapper225_init,Mapper226_init,Mapper227_init,Mapper228_init, |
| 737 | Mapper229_init,0,0,Mapper232_init,0,Mapper234_init,Mapper43_init,0, |
| 738 | 0,0,0,Mapper240_init,0,Mapper242_init,0,0, |
| 739 | Mapper245_init,Mapper246_init,0,Mapper248_init,Mapper249_init,Mapper250_init,0,0,0,0,0 |
| 740 | }; |
| 741 | |
| 742 | static DECLFW(BWRAM) |
| 743 | { |
| 744 | WRAM[A-0x6000]=V; |
| 745 | } |
| 746 | |
| 747 | static DECLFR(AWRAM) |
| 748 | { |
| 749 | return WRAM[A-0x6000]; |
| 750 | } |
| 751 | |
| 752 | void (*MapStateRestore)(int version); |
| 753 | void iNESStateRestore(int version) |
| 754 | { |
| 755 | int x; |
| 756 | |
| 757 | if(!MapperNo) return; |
| 758 | |
| 759 | for(x=0;x<4;x++) |
| 760 | setprg8(0x8000+x*8192,PRGBankList[x]); |
| 761 | |
| 762 | if(VROM_size) |
| 763 | for(x=0;x<8;x++) |
| 764 | setchr1(0x400*x,CHRBankList[x]); |
| 765 | |
| 766 | switch(Mirroring) |
| 767 | { |
| 768 | case 0:setmirror(MI_H);break; |
| 769 | case 1:setmirror(MI_V);break; |
| 770 | case 0x12: |
| 771 | case 0x10:setmirror(MI_0);break; |
| 772 | case 0x13: |
| 773 | case 0x11:setmirror(MI_1);break; |
| 774 | } |
| 775 | if(MapStateRestore) MapStateRestore(version); |
| 776 | } |
| 777 | |
| 778 | static int MMC_init(int type) |
| 779 | { |
| 780 | int x; |
| 781 | |
| 782 | GameStateRestore=iNESStateRestore; |
| 783 | MapClose=0; |
| 784 | MapperReset=0; |
| 785 | MapStateRestore=0; |
| 786 | |
| 787 | setprg8r(1,0x6000,0); |
| 788 | |
| 789 | SetReadHandler(0x6000,0x7FFF,AWRAM); |
| 790 | SetWriteHandler(0x6000,0x7FFF,BWRAM); |
| 791 | FCEU_CheatAddRAM(8,0x6000,WRAM); |
| 792 | |
| 793 | /* This statement represents atrocious code. I need to rewrite |
| 794 | all of the iNES mapper code... */ |
| 795 | IRQCount=IRQLatch=IRQa=0; |
| 796 | if(head.ROM_type&2) |
| 797 | { |
| 798 | extern uint8 MMC5WRAMsize,MMC1WRAMsize; |
| 799 | if(type==5 && MMC5WRAMsize==4) |
| 800 | memset(GameMemBlock+32768,0,sizeof(GameMemBlock)-32768); |
| 801 | else if(type==1 && MMC1WRAMsize==2) |
| 802 | { |
| 803 | memset(GameMemBlock,0,8192); |
| 804 | memset(GameMemBlock+16384,0,sizeof(GameMemBlock)-16384); |
| 805 | } |
| 806 | else |
| 807 | memset(GameMemBlock+8192,0,sizeof(GameMemBlock)-8192); |
| 808 | } |
| 809 | else |
| 810 | memset(GameMemBlock,0,sizeof(GameMemBlock)); |
| 811 | if(head.ROM_type&4) |
| 812 | memcpy(WRAM+4096,trainerpoo,512); |
| 813 | |
| 814 | NONE_init(); |
| 815 | |
| 816 | if(FCEUGameInfo.type==GIT_VSUNI) |
| 817 | DoVSHooks(); |
| 818 | if(type==5) |
| 819 | { |
| 820 | MMC5HackVROMMask=CHRmask4[0]; |
| 821 | MMC5HackExNTARAMPtr=MapperExRAM+0x6000; |
| 822 | MMC5Hack=1; |
| 823 | MMC5HackVROMPTR=VROM; |
| 824 | MMC5HackCHRMode=0; |
| 825 | } |
| 826 | ResetExState(); |
| 827 | AddExState(WRAM, 8192, 0, "WRAM"); |
| 828 | if(type==19 || type==5 || type==6 || type==69 || type==85) |
| 829 | AddExState(MapperExRAM, 32768, 0, "MEXR"); |
| 830 | if((!VROM_size || type==6 || type==19 || type==119) && |
| 831 | (type!=13 && type!=96)) |
| 832 | AddExState(CHRRAM, 8192, 0, "CHRR"); |
| 833 | if(head.ROM_type&8) |
| 834 | AddExState(ExtraNTARAM, 2048, 0, "EXNR"); |
| 835 | |
| 836 | /* Exclude some mappers whose emulation code handle save state stuff |
| 837 | themselves. */ |
| 838 | if(type && type!=13 && type!=96) |
| 839 | { |
| 840 | AddExState(mapbyte1, 32, 0, "MPBY"); |
| 841 | AddExState(&Mirroring, 1, 0, "MIRR"); |
| 842 | AddExState(&IRQCount, 4, 1, "IRQC"); |
| 843 | AddExState(&IRQLatch, 4, 1, "IQL1"); |
| 844 | AddExState(&IRQa, 1, 0, "IRQA"); |
| 845 | AddExState(PRGBankList, 4, 0, "PBL"); |
| 846 | for(x=0;x<8;x++) |
| 847 | { |
| 848 | char tak[8]; |
| 849 | sprintf(tak,"CBL%d",x); |
| 850 | AddExState(&CHRBankList[x], 2, 1,tak); |
| 851 | } |
| 852 | } |
| 853 | |
| 854 | if(MapInitTab[type]) MapInitTab[type](); |
| 855 | else if(type) |
| 856 | { |
| 857 | FCEU_PrintError("iNES mapper #%d is not supported at all.",type); |
| 858 | } |
| 859 | return 1; |
| 860 | } |