NES 2.0: Add preliminary support for iNES 2.0 format

Currently reads and sets the following:
- mapper number up to 4095, prg rom size, chr rom size
- chr ram (volatile + non-volatile), region (ntsc, pal, dendy or multi-region)

todo: submapper handling, w-ram and prg nvram at least...
This commit is contained in:
retro-wertz
2019-07-05 22:46:22 +08:00
parent bb8aef82c1
commit 51199e59d7
5 changed files with 176 additions and 90 deletions

View File

@@ -217,9 +217,9 @@ void UNROM512_Init(CartInfo *info) {
flash_bank = 0; flash_bank = 0;
flash_save = info->battery; flash_save = info->battery;
if (info->vram_size == 8192) if (info->chrRam == 8192)
chrram_mask = 0; chrram_mask = 0;
else if (info->vram_size == 16384) else if (info->chrRam == 16384)
chrram_mask = 0x20; chrram_mask = 0x20;
else else
chrram_mask = 0x60; chrram_mask = 0x60;
@@ -236,7 +236,7 @@ void UNROM512_Init(CartInfo *info) {
SetupCartMirroring(MI_0, 0, NULL); SetupCartMirroring(MI_0, 0, NULL);
break; break;
case 3: /* hard four screen, last 8k of 32k RAM (flags: 4-screen + vertical) */ case 3: /* hard four screen, last 8k of 32k RAM (flags: 4-screen + vertical) */
SetupCartMirroring(4, 1, VROM + (info->vram_size - 8192)); SetupCartMirroring(4, 1, VROM + (info->chrRam - 8192));
break; break;
} }
bus_conflict = !info->battery; bus_conflict = !info->battery;

View File

@@ -10,6 +10,7 @@ typedef struct {
uint32 SaveGameLen[4]; /* How much memory to save/load. */ uint32 SaveGameLen[4]; /* How much memory to save/load. */
/* Set by iNES/UNIF loading code. */ /* Set by iNES/UNIF loading code. */
int iNES2; /* iNES version */
int mapper; /* mapper used */ int mapper; /* mapper used */
int submapper; /* submapper used */ /* TODO: */ int submapper; /* submapper used */ /* TODO: */
int mirror; /* As set in the header or chunk. int mirror; /* As set in the header or chunk.
@@ -19,7 +20,12 @@ typedef struct {
* set to mapper 4. * set to mapper 4.
*/ */
int battery; /* Presence of an actual battery. */ int battery; /* Presence of an actual battery. */
int vram_size; int prgRam; /* prg ram size (volatile) */
int chrRam; /* chr ram size (volatile) */
int prgRam_battery; /* prg ram size (non-volatile or battery backed) */
int chrRam_battery; /* chr ram size (non-volatile or battery backed) */
int region; /* video system timing (ntsc, pal, dendy */
uint8 MD5[16]; uint8 MD5[16];
uint32 CRC32; /* Should be set by the iNES/UNIF loading uint32 CRC32; /* Should be set by the iNES/UNIF loading
* code, used by mapper/board code, maybe * code, used by mapper/board code, maybe

View File

@@ -57,7 +57,9 @@ uint32 VROM_size = 0;
static int CHRRAMSize = -1; static int CHRRAMSize = -1;
static int iNES_Init(int num); static int iNES_Init(int num);
static int iNES2 = 0;
static int MapperNo = 0; static int MapperNo = 0;
static int subMapper = 0;
static DECLFR(TrainerRead) { static DECLFR(TrainerRead) {
return(trainerpoo[A & 0x1FF]); return(trainerpoo[A & 0x1FF]);
@@ -376,7 +378,7 @@ static void CheckHInfo(void) {
if (tofix & 8) if (tofix & 8)
strcat(gigastr, "This game should not have any CHR ROM. "); strcat(gigastr, "This game should not have any CHR ROM. ");
strcat(gigastr, "\n"); strcat(gigastr, "\n");
FCEU_printf("%s", gigastr); FCEU_printf("%s\n", gigastr);
} }
} }
@@ -661,14 +663,19 @@ static BMAPPINGLocal bmap[] = {
int iNESLoad(const char *name, FCEUFILE *fp) { int iNESLoad(const char *name, FCEUFILE *fp) {
struct md5_context md5; struct md5_context md5;
char* mappername; char* mappername = NULL;
uint32 mappertest; uint32 filesize = fp->fp->size;
uint32 mappertest = 0;
uint32 prgRom = 0;
uint32 chrRom = 0;
if (FCEU_fread(&head, 1, 16, fp) != 16) if (FCEU_fread(&head, 1, 16, fp) != 16)
return 0; return 0;
filesize -= 16; /* remove header size from total size */
if (memcmp(&head, "NES\x1a", 4)) { if (memcmp(&head, "NES\x1a", 4)) {
FCEU_PrintError("Missing header or invalid iNES format file!\n"); FCEU_PrintError("Not an iNES file!\n");
return 0; return 0;
} }
@@ -689,20 +696,51 @@ int iNESLoad(const char *name, FCEUFILE *fp) {
memset((char*)(&head) + 0xA, 0, 0x6); memset((char*)(&head) + 0xA, 0, 0x6);
} }
MapperNo = (head.ROM_type >> 4); Mirroring = 0;
MapperNo |= (head.ROM_type2 & 0xF0); ROM_size = 0;
if (head.ROM_type & 8) { VROM_size = 0;
Mirroring = 2; iNES2 = 0;
} else MapperNo = 0;
Mirroring = (head.ROM_type & 1); subMapper = 0;
if (!head.ROM_size) MapperNo = (head.ROM_type >> 4) | (head.ROM_type2 & 0xF0);
ROM_size = 256; Mirroring = (head.ROM_type & 8) ? 2 : (head.ROM_type & 1);
else prgRom = head.ROM_size;
ROM_size = uppow2(head.ROM_size); chrRom = head.VROM_size;
VROM_size = uppow2(head.VROM_size); if ((head.ROM_type2 & 0x0C) == 0x08) {
iNES2 = 1;
MapperNo |= ((uint32)head.ROM_type3 << 8) & 0xF00;
prgRom |= ((uint32)head.upper_PRG_CHR_size << 8) & 0xF00;
chrRom |= ((uint32)head.upper_PRG_CHR_size << 4) & 0xF00;
subMapper = head.ROM_type3 >> 4 & 0x0F;
iNESCart.prgRam = (head.PRGRAM_size & 0x0F) ? (64 << (head.PRGRAM_size & 0x0F)) : 0;
iNESCart.chrRam = (head.CHRRAM_size & 0x0F) ? (64 << (head.CHRRAM_size & 0x0F)) : 0;
iNESCart.prgRam_battery = (head.PRGRAM_size & 0xF0) ? (64 << ((head.PRGRAM_size & 0xF0) >> 4)) : 0;
iNESCart.chrRam_battery = (head.CHRRAM_size & 0xF0) ? (64 << ((head.CHRRAM_size & 0xF0) >> 4)) : 0;
iNESCart.region = head.Region;
} else {
if (!prgRom)
prgRom = 256;
}
if (head.ROM_type & 4) { /* Trainer */
trainerpoo = (uint8*)FCEU_gmalloc(512);
FCEU_fread(trainerpoo, 512, 1, fp);
filesize -= 512;
}
if (((prgRom * 0x4000) + (chrRom * 0x2000)) > filesize) {
FCEU_PrintError(" File length is too short to contain all data reported from header by %llu\n", ((prgRom * 0x4000) + (chrRom * 0x2000)) - filesize);
return 0;
} else if (((prgRom * 0x4000) + (chrRom * 0x2000)) < filesize)
FCEU_PrintError(" File contains %llu bytes of unused data\n", filesize - ((prgRom * 0x4000) + (chrRom * 0x2000)));
ROM_size = uppow2(prgRom);
if (chrRom)
VROM_size = uppow2(chrRom);
if ((ROM = (uint8*)FCEU_malloc(ROM_size << 14)) == NULL) if ((ROM = (uint8*)FCEU_malloc(ROM_size << 14)) == NULL)
return 0; return 0;
@@ -717,36 +755,23 @@ int iNESLoad(const char *name, FCEUFILE *fp) {
memset(VROM, 0xFF, VROM_size << 13); memset(VROM, 0xFF, VROM_size << 13);
} }
if (head.ROM_type & 4) { /* Trainer */
trainerpoo = (uint8*)FCEU_gmalloc(512);
FCEU_fread(trainerpoo, 512, 1, fp);
}
ResetCartMapping(); ResetCartMapping();
ResetExState(0, 0); ResetExState(0, 0);
SetupCartPRGMapping(0, ROM, ROM_size << 14, 0); SetupCartPRGMapping(0, ROM, ROM_size << 14, 0);
if (head.ROM_size) FCEU_fread(ROM, 0x4000, prgRom, fp);
FCEU_fread(ROM, 0x4000, head.ROM_size, fp);
else
FCEU_fread(ROM, 0x4000, ROM_size, fp);
if (VROM_size) if (VROM_size)
FCEU_fread(VROM, 0x2000, VROM_size, fp); FCEU_fread(VROM, 0x2000, chrRom, fp);
md5_starts(&md5); md5_starts(&md5);
if (head.ROM_size) { md5_update(&md5, ROM, prgRom << 14);
md5_update(&md5, ROM, head.ROM_size << 14); iNESGameCRC32 = CalcCRC32(0, ROM, prgRom << 14);
iNESGameCRC32 = CalcCRC32(0, ROM, head.ROM_size << 14);
} else {
md5_update(&md5, ROM, ROM_size << 14);
iNESGameCRC32 = CalcCRC32(0, ROM, ROM_size << 14);
}
if (VROM_size) { if (VROM_size) {
iNESGameCRC32 = CalcCRC32(iNESGameCRC32, VROM, VROM_size << 13); iNESGameCRC32 = CalcCRC32(iNESGameCRC32, VROM, chrRom << 13);
md5_update(&md5, VROM, VROM_size << 13); md5_update(&md5, VROM, chrRom << 13);
} }
md5_finish(&md5, iNESCart.MD5); md5_finish(&md5, iNESCart.MD5);
memcpy(&GameInfo->MD5, &iNESCart.MD5, sizeof(iNESCart.MD5)); memcpy(&GameInfo->MD5, &iNESCart.MD5, sizeof(iNESCart.MD5));
@@ -755,14 +780,7 @@ int iNESLoad(const char *name, FCEUFILE *fp) {
SetInput(); SetInput();
CheckHInfo(); CheckHInfo();
mappername = "Not Listed";
for (mappertest = 0; mappertest < (sizeof bmap / sizeof bmap[0]) - 1; mappertest++) {
if (bmap[mappertest].number == MapperNo) {
mappername = (char*)bmap[mappertest].name;
break;
}
}
{ {
int x; int x;
uint64 partialmd5 = 0; uint64 partialmd5 = 0;
@@ -787,43 +805,92 @@ int iNESLoad(const char *name, FCEUFILE *fp) {
else else
SetupCartMirroring(Mirroring & 1, (Mirroring & 4) >> 2, 0); SetupCartMirroring(Mirroring & 1, (Mirroring & 4) >> 2, 0);
iNESCart.iNES2 = iNES2;
iNESCart.mapper = MapperNo;
iNESCart.submapper = subMapper;
iNESCart.battery = (head.ROM_type & 2) ? 1 : 0; iNESCart.battery = (head.ROM_type & 2) ? 1 : 0;
iNESCart.mirror = Mirroring; iNESCart.mirror = Mirroring;
mappername = "Not Listed";
for (mappertest = 0; mappertest < (sizeof bmap / sizeof bmap[0]) - 1; mappertest++) {
if (bmap[mappertest].number == MapperNo) {
mappername = (char*)bmap[mappertest].name;
break;
}
}
if (iNES2) FCEU_printf(" NES 2.0 extended iNES.\n");
FCEU_printf(" ROM CRC32: 0x%08lx\n", iNESGameCRC32); FCEU_printf(" ROM CRC32: 0x%08lx\n", iNESGameCRC32);
FCEU_printf(" ROM MD5: 0x%s\n", md5_asciistr(iNESCart.MD5)); FCEU_printf(" ROM MD5: 0x%s\n", md5_asciistr(iNESCart.MD5));
FCEU_printf(" PRG ROM: %3d x 16KiB\n", head.ROM_size ? head.ROM_size : 256); FCEU_printf(" PRG ROM: %3d x 16KiB\n", prgRom);
FCEU_printf(" CHR ROM: %3d x 8KiB\n", head.VROM_size); FCEU_printf(" CHR ROM: %3d x 8KiB\n", chrRom);
if (!iNES_Init(MapperNo)) if (iNES2) {
FCEU_PrintError("iNES mapper #%d is not supported at all.", MapperNo); const char *tv_region[] = { "NTSC", "PAL", "Multi-region", "Dendy" };
iNESCart.mapper = MapperNo; unsigned PRGRAM = iNESCart.prgRam + iNESCart.prgRam_battery;
unsigned CHRRAM = iNESCart.chrRam + iNESCart.chrRam_battery;
FCEU_printf(" \n"); FCEU_printf(" Mapper #: %3d\n", MapperNo);
FCEU_printf(" Mapper #: %3d\n", MapperNo); FCEU_printf(" Sub Mapper #: %3d\n", subMapper);
FCEU_printf(" Mapper name: %s\n", mappername); FCEU_printf(" Mapper name: %s\n", mappername);
FCEU_printf(" Mirroring: %s\n", Mirroring == 2 ? "None (Four-screen)" : Mirroring ? "Vertical" : "Horizontal"); if (PRGRAM || CHRRAM) {
FCEU_printf(" Battery-backed: %s\n", (head.ROM_type & 2) ? "Yes" : "No"); FCEU_printf(" PRG RAM: %d KiB\n", PRGRAM / 1024);
FCEU_printf(" Trained: %s\n", (head.ROM_type & 4) ? "Yes" : "No"); FCEU_printf(" CHR RAM: %d KiB\n", CHRRAM / 1024);
if (head.ROM_type & 0x02) {
FCEU_printf(" PRG RAM backed by battery: %d KiB\n", iNESCart.prgRam_battery / 1024);
FCEU_printf(" CHR RAM backed by battery: %d KiB\n", iNESCart.chrRam_battery / 1024);
}
}
FCEU_printf(" Mirroring: %s\n", Mirroring == 2 ? "None (Four-screen)" : Mirroring ? "Vertical" : "Horizontal");
FCEU_printf(" System: %s\n", tv_region[(iNESCart.region & 3)]);
FCEU_printf(" Trained: %s\n", (head.ROM_type & 4) ? "Yes" : "No");
} else {
FCEU_printf(" Mapper #: %3d\n", MapperNo);
FCEU_printf(" Mapper name: %s\n", mappername);
FCEU_printf(" Mirroring: %s\n", Mirroring == 2 ? "None (Four-screen)" : Mirroring ? "Vertical" : "Horizontal");
FCEU_printf(" Battery-backed: %s\n", (head.ROM_type & 2) ? "Yes" : "No");
FCEU_printf(" Trained: %s\n", (head.ROM_type & 4) ? "Yes" : "No");
}
if (!iNES_Init(MapperNo)) {
FCEU_printf("\n");
FCEU_PrintError(" iNES mapper #%d is not supported at all.\n", MapperNo);
return 0;
}
GameInterface = iNESGI; GameInterface = iNESGI;
FCEU_printf("\n");
if (strstr(name, "(E)") || strstr(name, "(e)") if (iNES2) {
|| strstr(name, "(Europe)") || strstr(name, "(PAL)") switch (iNESCart.region & 0x03) {
|| strstr(name, "(F)") || strstr(name, "(f)") /* 0: RP2C02 ("NTSC NES")
|| strstr(name, "(G)") || strstr(name, "(g)") * 1: RP2C07 ("Licensed PAL NES")
|| strstr(name, "(I)") || strstr(name, "(i)") * 2: Multiple-region
|| strstr(name, "(S)") || strstr(name, "(s)") * 3: UMC 6527P ("Dendy")
|| strstr(name, "(France)") || strstr(name, "(Germany)") */
|| strstr(name, "(Italy)") || strstr(name, "(Spain)") case 0: case 2: FCEUI_SetVidSystem(0); break;
|| strstr(name, "(Sweden)") || strstr(name, "(Sw)") case 1: FCEUI_SetVidSystem(1); break;
|| strstr(name, "(Australia)") || strstr(name, "(A)") case 3: dendy = 1; FCEUI_SetVidSystem(0); break;
|| strstr(name, "(a)")) { }
FCEUI_SetVidSystem(1); } else {
if (strstr(name, "(E)") || strstr(name, "(e)") ||
strstr(name, "(Europe)") || strstr(name, "(PAL)") ||
strstr(name, "(F)") || strstr(name, "(f)") ||
strstr(name, "(G)") || strstr(name, "(g)") ||
strstr(name, "(I)") || strstr(name, "(i)") ||
strstr(name, "(S)") || strstr(name, "(s)") ||
strstr(name, "(France)") || strstr(name, "(Germany)") ||
strstr(name, "(Italy)") || strstr(name, "(Spain)") ||
strstr(name, "(Sweden)") || strstr(name, "(Sw)") ||
strstr(name, "(Australia)") || strstr(name, "(A)") ||
strstr(name, "(a)")) {
iNESCart.region = 1;
FCEUI_SetVidSystem(1);
} else {
iNESCart.region = 0;
FCEUI_SetVidSystem(0);
}
} }
else
FCEUI_SetVidSystem(0);
return 1; return 1;
} }
@@ -840,24 +907,30 @@ static int iNES_Init(int num) {
if (num == tmp->number) { if (num == tmp->number) {
UNIFchrrama = 0; /* need here for compatibility with UNIF mapper code */ UNIFchrrama = 0; /* need here for compatibility with UNIF mapper code */
if (!VROM_size) { if (!VROM_size) {
switch (num) { /* FIXME, mapper or game data base with the board parameters and ROM/RAM sizes */ if (iNESCart.iNES2) {
case 13: CHRRAMSize = 16 * 1024; break; CHRRAMSize = iNESCart.chrRam + iNESCart.chrRam_battery;
case 6: } else {
case 28: switch (num) { /* FIXME, mapper or game data base with the board parameters and ROM/RAM sizes */
case 29: case 13: CHRRAMSize = 16 * 1024; break;
case 30: case 6:
case 45: case 28:
case 96: CHRRAMSize = 32 * 1024; break; case 29:
case 176: CHRRAMSize = 128 * 1024; break; case 30:
default: CHRRAMSize = 8 * 1024; break; case 45:
case 96: CHRRAMSize = 32 * 1024; break;
case 176: CHRRAMSize = 128 * 1024; break;
default: CHRRAMSize = 8 * 1024; break;
}
iNESCart.chrRam = CHRRAMSize;
FCEU_printf(" CHR-RAM: %3d KiB\n", CHRRAMSize / 1024);
}
if (CHRRAMSize) { /* TODO: CHR-RAM are sometimes handled in mappers e.g. MMC1 using submapper 1/2/4 and CHR-RAM can be zero here */
if ((VROM = (uint8*)malloc(CHRRAMSize)) == NULL) return 0;
FCEU_MemoryRand(VROM, CHRRAMSize);
UNIFchrrama = VROM;
SetupCartCHRMapping(0, VROM, CHRRAMSize, 1);
AddExState(VROM, CHRRAMSize, 0, "CHRR");
} }
iNESCart.vram_size = CHRRAMSize;
if ((VROM = (uint8*)malloc(CHRRAMSize)) == NULL) return 0;
FCEU_MemoryRand(VROM, CHRRAMSize);
UNIFchrrama = VROM;
SetupCartCHRMapping(0, VROM, CHRRAMSize, 1);
AddExState(VROM, CHRRAMSize, 0, "CHRR");
FCEU_printf(" CHR RAM: %3d x 1KiB\n", CHRRAMSize / 1024);
} }
if (head.ROM_type & 8) if (head.ROM_type & 8)
AddExState(ExtraNTARAM, 2048, 0, "EXNR"); AddExState(ExtraNTARAM, 2048, 0, "EXNR");

View File

@@ -28,7 +28,14 @@ typedef struct {
uint8 VROM_size; uint8 VROM_size;
uint8 ROM_type; uint8 ROM_type;
uint8 ROM_type2; uint8 ROM_type2;
uint8 reserve[8]; uint8 ROM_type3;
uint8 upper_PRG_CHR_size;
uint8 PRGRAM_size;
uint8 CHRRAM_size;
uint8 Region;
uint8 VS_hardware;
uint8 MiscRoms;
uint8 ExpDevice;
} iNES_HEADER; } iNES_HEADER;
extern uint8 *ROM; extern uint8 *ROM;

View File

@@ -353,7 +353,7 @@ static void CheckHashInfo(void) {
FCEU_PrintError(" For now, the information will be corrected in RAM.\n"); FCEU_PrintError(" For now, the information will be corrected in RAM.\n");
if (unif_db[x].boardname != NULL && strcmp((char*)unif_db[x].boardname, (char*)sboardname) != 0) { if (unif_db[x].boardname != NULL && strcmp((char*)unif_db[x].boardname, (char*)sboardname) != 0) {
FCEU_printf(" Boardname should be set to %s\n", unif_db[x].boardname); FCEU_printf(" Boardname should be set to %s\n", unif_db[x].boardname);
sboardname = unif_db[x].boardname; sboardname = (uint8*)unif_db[x].boardname;
} }
if (unif_db[x].submapper >= 0 && unif_db[x].submapper != submapper) { if (unif_db[x].submapper >= 0 && unif_db[x].submapper != submapper) {
FCEU_PrintError(" Submapper should be set to %d\n", unif_db[x].submapper); FCEU_PrintError(" Submapper should be set to %d\n", unif_db[x].submapper);