#include "emu.h" #include "hashfile.h" #include "machine/nes_slot.h" #define NES_BATTERY_SIZE 0x2000 //************************************************************************** // GLOBAL VARIABLES //************************************************************************** const device_type NES_CART_SLOT = &device_creator; //************************************************************************** // NES cartridges Interface //************************************************************************** //------------------------------------------------- // device_nes_cart_interface - constructor //------------------------------------------------- device_nes_cart_interface::device_nes_cart_interface(const machine_config &mconfig, device_t &device) : device_slot_card_interface(mconfig, device), m_prg(NULL), m_prgram(NULL), m_vrom(NULL), m_vram(NULL), m_battery(NULL), m_mapper_ram(NULL), m_mapper_bram(NULL), m_prg_size(0), m_prgram_size(0), m_vrom_size(0), m_vram_size(0), m_battery_size(0), m_mapper_ram_size(0), m_mapper_bram_size(0) { } //------------------------------------------------- // ~device_nes_cart_interface - destructor //------------------------------------------------- device_nes_cart_interface::~device_nes_cart_interface() { } //------------------------------------------------- // pointer allocators //------------------------------------------------- void device_nes_cart_interface::prg_alloc(running_machine &machine, size_t size) { if (m_prg == NULL) { m_prg = auto_alloc_array(machine, UINT8, size); m_prg_size = size; } } void device_nes_cart_interface::prgram_alloc(running_machine &machine, size_t size) { if (m_prgram == NULL) { m_prgram = auto_alloc_array(machine, UINT8, size); m_prgram_size = size; } } void device_nes_cart_interface::vrom_alloc(running_machine &machine, size_t size) { if (m_vrom == NULL) { m_vrom = auto_alloc_array(machine, UINT8, size); m_vrom_size = size; } } void device_nes_cart_interface::vram_alloc(running_machine &machine, size_t size) { if (m_vram == NULL) { m_vram = auto_alloc_array(machine, UINT8, size); m_vram_size = size; } } void device_nes_cart_interface::battery_alloc(running_machine &machine, size_t size) { if (m_battery == NULL) { m_battery = auto_alloc_array(machine, UINT8, size); m_battery_size = size; } } void device_nes_cart_interface::mapper_ram_alloc(running_machine &machine, size_t size) { if (m_mapper_ram == NULL) { m_mapper_ram = auto_alloc_array(machine, UINT8, size); m_mapper_ram_size = size; } } void device_nes_cart_interface::mapper_bram_alloc(running_machine &machine, size_t size) { if (m_mapper_bram == NULL) { m_mapper_bram = auto_alloc_array(machine, UINT8, size); m_mapper_bram_size = size; } } //************************************************************************** // LIVE DEVICE //************************************************************************** //------------------------------------------------- // nes_cart_slot_device - constructor //------------------------------------------------- nes_cart_slot_device::nes_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, NES_CART_SLOT, "NES Cartridge Slot", tag, owner, clock), device_image_interface(mconfig, *this), device_slot_interface(mconfig, *this), m_chr_open_bus(0), m_ce_mask(0), m_ce_state(0), m_vrc_ls_prg_a(0), m_vrc_ls_prg_b(0), m_vrc_ls_chr(0), m_crc_hack(0), m_must_be_loaded(1) { } //------------------------------------------------- // nes_cart_slot_device - destructor //------------------------------------------------- nes_cart_slot_device::~nes_cart_slot_device() { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void nes_cart_slot_device::device_start() { m_cart = dynamic_cast(get_card_device()); } //------------------------------------------------- // device_config_complete - perform any // operations now that the configuration is // complete //------------------------------------------------- void nes_cart_slot_device::device_config_complete() { // inherit a copy of the static data // const nes_cart_interface *intf = reinterpret_cast(static_config()); // if (intf != NULL) // { // *static_cast(this) = *intf; // } // set brief and instance name update_names(); } //------------------------------------------------- // device_timer - handler timer events //------------------------------------------------- /*------------------------------------------------- call load -------------------------------------------------*/ struct nes_cart_lines { const char *tag; int line; }; static const struct nes_cart_lines nes_cart_lines_table[] = { { "PRG A0", 0 }, { "PRG A1", 1 }, { "PRG A2", 2 }, { "PRG A3", 3 }, { "PRG A4", 4 }, { "PRG A5", 5 }, { "PRG A6", 6 }, { "PRG A7", 7 }, { "CHR A10", 10 }, { "CHR A11", 11 }, { "CHR A12", 12 }, { "CHR A13", 13 }, { "CHR A14", 14 }, { "CHR A15", 15 }, { "CHR A16", 16 }, { "CHR A17", 17 }, { "NC", 127 }, { 0 } }; static int nes_cart_get_line( const char *feature ) { const struct nes_cart_lines *nes_line = &nes_cart_lines_table[0]; if (feature == NULL) return 128; while (nes_line->tag) { if (strcmp(nes_line->tag, feature) == 0) break; nes_line++; } return nes_line->line; } /* Set to generate prg & chr files when the cart is loaded */ #define SPLIT_PRG 0 #define SPLIT_CHR 0 bool nes_cart_slot_device::call_load() { if (m_cart) { UINT32 vram_size = 0, prgram_size = 0, battery_size = 0, mapper_ram_size = 0, mapper_bram_size = 0; // helper for regions to alloc at the end if (software_entry() == NULL) { const char *mapinfo = NULL; int mapint1 = 0, mapint2 = 0, mapint3 = 0, mapint4 = 0; char magic[4]; /* Check first 4 bytes of the image to decide if it is UNIF or iNES */ /* Unfortunately, many .unf files have been released as .nes, so we cannot rely on extensions only */ memset(magic, '\0', sizeof(magic)); fread(magic, 4); if ((magic[0] == 'N') && (magic[1] == 'E') && (magic[2] == 'S')) /* If header starts with 'NES' it is iNES */ { UINT32 prg_size, vrom_size; UINT8 header[0x10]; UINT8 mapper, local_options; bool ines20 = FALSE, has_trainer = FALSE, prg16k; // check if the image is recognized by nes.hsi mapinfo = hashfile_extrainfo(*this); // image_extrainfo() resets the file position back to start. fseek(0, SEEK_SET); // read out the header fread(&header, 0x10); // SETUP step 1: getting PRG, VROM, VRAM sizes prg16k = (header[4] == 1); prg_size = prg16k ? 2 * 0x4000 : header[4] * 0x4000; vrom_size = header[5] * 0x2000; vram_size = 0x4000; // SETUP step 2: getting PCB and other settings mapper = (header[6] & 0xf0) >> 4; local_options = header[6] & 0x0f; switch (header[7] & 0xc) { case 0x4: case 0xc: // probably the header got corrupted: don't trust upper bits for mapper break; case 0x8: // it's iNES 2.0 format ines20 = TRUE; case 0x0: default: mapper |= header[7] & 0xf0; break; } // use info from nes.hsi if available! if (mapinfo) { if (4 == sscanf(mapinfo,"%d %d %d %d", &mapint1, &mapint2, &mapint3, &mapint4)) { /* image is present in nes.hsi: overwrite the header settings with these */ mapper = mapint1; local_options = mapint2 & 0x0f; m_crc_hack = (mapint2 & 0xf0) >> 4; // this is used to differentiate among variants of the same Mapper (see below) prg_size = mapint3 * 0x4000; vrom_size = mapint4 * 0x2000; logerror("NES.HSI info: %d %d %d %d\n", mapint1, mapint2, mapint3, mapint4); // mame_printf_error("NES.HSI info: %d %d %d %d\n", mapint1, mapint2, mapint3, mapint4); } else { logerror("NES: [%s], Invalid mapinfo found\n", mapinfo); } } else { logerror("NES: No extrainfo found\n"); } // use extended iNES2.0 info if available! if (ines20) { mapper |= (header[8] & 0x0f) << 8; // header[8] & 0xf0 is used for submappers, but I haven't found any specific image to implement this prg_size += ((header[9] & 0x0f) << 8) * 0x4000; vrom_size += ((header[9] & 0xf0) << 4) * 0x2000; } // SETUP step 3: storing the info needed for emulation m_pcb_id = nes_get_mmc_id(machine(), mapper); m_cart->set_mirroring(BIT(local_options, 0) ? PPU_MIRROR_VERT : PPU_MIRROR_HORZ); if (BIT(local_options, 1)) battery_size = NES_BATTERY_SIZE; // with original iNES format we can only support 8K WRAM battery has_trainer = BIT(local_options, 2) ? TRUE : FALSE; m_cart->set_four_screen_vram(BIT(local_options, 3)); if (ines20) { // PRGRAM/BWRAM (not fully supported, also due to lack of 2.0 files) if ((header[10] & 0x0f) > 0) prgram_size = 0x80 << ((header[10] & 0x0f) - 1); if ((header[10] & 0xf0) > 0) battery_size = 0x80 << ((header[10] & 0xf0) - 5); // VRAM vram_size = 0; if ((header[11] & 0x0f) > 0) vram_size = 0x80 << ((header[11] & 0x0f) - 1); // header[11] & 0xf0 is the size of battery backed VRAM, found so far in Racermate II only and not supported yet } else { // always map PRGRAM/WRAM in bank5 (eventually, this should be enabled only for some mappers) // and save it depending on has_battery // PRGRAM size is 8k for most games, but pirate carts often use different sizes, // so its size has been added recently to the iNES format spec, but almost no image uses it prgram_size = header[8] ? header[8] * 0x2000 : 0x2000; } // a few mappers correspond to multiple PCBs, so we need a few additional checks and tweaks switch (m_pcb_id) { case STD_CNROM: if (mapper == 185) { switch (m_crc_hack) { case 0x0: // pin26: CE, pin27: CE (B-Wings, Bird Week) m_ce_mask = 0x03; m_ce_state = 0x03; break; case 0x4: // pin26: CE, pin27: /CE (Mighty Bomb Jack, Spy Vs. Spy) m_ce_mask = 0x03; m_ce_state = 0x01; break; case 0x8: // pin26: /CE, pin27: CE (Sansu 1, 2, 3 Nen) m_ce_mask = 0x03; m_ce_state = 0x02; break; case 0xc: // pin26: /CE, pin27: /CE (Seicross v2.0) m_ce_mask = 0x03; m_ce_state = 0x00; break; } } break; case KONAMI_VRC2: if (mapper == 22) { m_vrc_ls_prg_a = 0; m_vrc_ls_prg_b = 1; m_vrc_ls_chr = 1; } if (mapper == 23 && !m_crc_hack) { m_vrc_ls_prg_a = 1; m_vrc_ls_prg_b = 0; m_vrc_ls_chr = 0; } if (mapper == 23 && m_crc_hack) { // here there are also Akumajou Special, Crisis Force, Parodius da!, Tiny Toons which are VRC-4 m_vrc_ls_prg_a = 3; m_vrc_ls_prg_b = 2; m_pcb_id = KONAMI_VRC4; // this allows for konami_irq to be installed at reset } break; case KONAMI_VRC4: if (mapper == 21) { // Wai Wai World 2 & Ganbare Goemon Gaiden 2 (the latter with crc_hack) m_vrc_ls_prg_a = m_crc_hack ? 7 : 2; m_vrc_ls_prg_b = m_crc_hack ? 6 : 1; } if (mapper == 25) // here there is also Ganbare Goemon Gaiden which is VRC-2 { m_vrc_ls_prg_a = m_crc_hack ? 2 : 0; m_vrc_ls_prg_b = m_crc_hack ? 3 : 1; } break; case KONAMI_VRC6: if (mapper == 24) { m_vrc_ls_prg_a = 1; m_vrc_ls_prg_b = 0; } if (mapper == 26) { m_vrc_ls_prg_a = 0; m_vrc_ls_prg_b = 1; } break; case IREM_G101: if (m_crc_hack) m_cart->set_mirroring(PPU_MIRROR_HIGH); // Major League has hardwired mirroring break; case DIS_74X161X161X32: if (mapper == 70) m_cart->set_mirroring(PPU_MIRROR_VERT); // only hardwired mirroring makes different mappers 70 & 152 break; case SUNSOFT_2: if (mapper == 93) m_cart->set_mirroring(PPU_MIRROR_VERT); // only hardwired mirroring makes different mappers 89 & 93 break; case STD_BXROM: if (m_crc_hack) m_pcb_id = AVE_NINA01; // Mapper 34 is used for 2 diff boards break; case BANDAI_LZ93: if (m_crc_hack) m_pcb_id = BANDAI_JUMP2; // Mapper 153 is used for 2 diff boards break; case IREM_HOLYDIV: if (m_crc_hack) m_pcb_id = JALECO_JF16; // Mapper 78 is used for 2 diff boards break; case CAMERICA_BF9093: if (m_crc_hack) m_pcb_id = CAMERICA_BF9097; // Mapper 71 is used for 2 diff boards break; case HES_BOARD: if (m_crc_hack) m_pcb_id = HES6IN1_BOARD; // Mapper 113 is used for 2 diff boards break; case WAIXING_ZS: if (m_crc_hack) m_pcb_id = WAIXING_DQ8; // Mapper 242 is used for 2 diff boards break; case BMC_GOLD_7IN1: if (m_crc_hack) m_pcb_id = BMC_MARIOPARTY_7IN1; // Mapper 52 is used for 2 diff boards break; case STD_EXROM: mapper_ram_size = 0x400; break; case TAITO_X1_017: mapper_ram_size = 0x1400; break; case TAITO_X1_005: case TAITO_X1_005_A: mapper_ram_size = 0x80; break; case NAMCOT_163: mapper_ram_size = 0x2000; break; case FUKUTAKE_BOARD: mapper_ram_size = 2816; break; //FIXME: we also have to fix Action 52 PRG loading somewhere... } // SETUP step 4: logging what we have found if (!ines20) { logerror("Loaded game in iNES format:\n"); logerror("-- Mapper %d\n", mapper); logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); if (battery_size) logerror("-- Battery found\n"); if (has_trainer) logerror("-- Trainer found\n"); if (m_cart->get_four_screen_vram()) logerror("-- 4-screen VRAM\n"); logerror("-- TV System: %s\n", ((header[10] & 3) == 0) ? "NTSC" : (header[10] & 1) ? "Both NTSC and PAL" : "PAL"); } else { logerror("Loaded game in Extended iNES format:\n"); logerror("-- Mapper: %d\n", mapper); logerror("-- Submapper: %d\n", (header[8] & 0xf0) >> 4); logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); logerror("-- PRG NVWRAM: %d\n", header[10] & 0x0f); logerror("-- PRG WRAM: %d\n", (header[10] & 0xf0) >> 4); logerror("-- CHR NVWRAM: %d\n", header[11] & 0x0f); logerror("-- CHR WRAM: %d\n", (header[11] & 0xf0) >> 4); logerror("-- TV System: %s\n", (header[12] & 2) ? "Both NTSC and PAL" : (header[12] & 1) ? "PAL" : "NTSC"); } // SETUP step 5: allocate pointers for PRG/VROM if (prg_size) m_cart->prg_alloc(machine(), prg_size); if (vrom_size) m_cart->vrom_alloc(machine(), vrom_size); if (has_trainer) fread(&m_cart->m_prgram[0x1000], 0x200); // SETUP step 6: at last load the data! // Read in the program chunks if (prg16k) { fread(m_cart->get_prg_base(), 0x4000); memcpy(m_cart->get_prg_base() + 0x4000, m_cart->get_prg_base(), 0x4000); } else fread(m_cart->get_prg_base(), m_cart->get_prg_size()); #if SPLIT_PRG { FILE *prgout; char outname[255]; sprintf(outname, "%s.prg", filename()); prgout = fopen(outname, "wb"); if (prgout) { fwrite(m_cart->get_prg_base(), 1, 0x4000 * m_cart->get_prg_size(), prgout); mame_printf_error("Created PRG chunk\n"); } fclose(prgout); } #endif // Read in any chr chunks if (m_cart->get_vrom_size()) fread(m_cart->get_vrom_base(), m_cart->get_vrom_size()); #if SPLIT_CHR if (state->m_chr_chunks > 0) { FILE *chrout; char outname[255]; sprintf(outname, "%s.chr", filename()); chrout= fopen(outname, "wb"); if (chrout) { fwrite(m_cart->get_vrom_base(), 1, m_cart->get_vrom_size(), chrout); mame_printf_error("Created CHR chunk\n"); } fclose(chrout); } #endif } else if ((magic[0] == 'U') && (magic[1] == 'N') && (magic[2] == 'I') && (magic[3] == 'F')) /* If header starts with 'UNIF' it is UNIF */ { // SETUP step 1: running through the file and getting PRG, VROM sizes UINT32 unif_ver = 0, chunk_length = 0, read_length = 0x20; UINT32 prg_start = 0, chr_start = 0; UINT32 size = length(), prg_size = 0, vrom_size = 0; UINT8 buffer[4], mirror = 0; char magic2[4]; char unif_mapr[32]; // here we should store MAPR chunks bool mapr_chunk_found = FALSE, small_prg = FALSE; // allocate space to temporarily store PRG & CHR banks UINT8 *temp_prg = auto_alloc_array(machine(), UINT8, 256 * 0x4000); UINT8 *temp_chr = auto_alloc_array(machine(), UINT8, 256 * 0x2000); UINT8 temp_byte = 0; fread(&buffer, 4); unif_ver = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); logerror("Loaded game in UNIF format, version %d\n", unif_ver); if (size <= 0x20) { logerror("%s only contains the UNIF header and no data.\n", filename()); return IMAGE_INIT_FAIL; } do { fseek(read_length, SEEK_SET); memset(magic2, '\0', sizeof(magic2)); fread(&magic2, 4); /* We first run through the whole image to find a [MAPR] chunk. This is needed because, unfortunately, the MAPR chunk is not always the first chunk (see Super 24-in-1). When such a chunk is found, we set mapr_chunk_found=1 and we go back to load other chunks! */ if (!mapr_chunk_found) { if ((magic2[0] == 'M') && (magic2[1] == 'A') && (magic2[2] == 'P') && (magic2[3] == 'R')) { mapr_chunk_found = TRUE; logerror("[MAPR] chunk found: "); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); if (chunk_length <= 0x20) fread(&unif_mapr, chunk_length); logerror("%s\n", unif_mapr); /* now that we found the MAPR chunk, we can go back to load other chunks */ fseek(0x20, SEEK_SET); read_length = 0x20; } else { logerror("Skip this chunk. We need a [MAPR] chunk before anything else.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } } else { /* What kind of chunk do we have here? */ if ((magic2[0] == 'M') && (magic2[1] == 'A') && (magic2[2] == 'P') && (magic2[3] == 'R')) { /* The [MAPR] chunk has already been read, so we skip it */ /* TO DO: it would be nice to check if more than one MAPR chunk is present */ logerror("[MAPR] chunk found (in the 2nd run). Already loaded.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'R') && (magic2[1] == 'E') && (magic2[2] == 'A') && (magic2[3] == 'D')) { logerror("[READ] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'N') && (magic2[1] == 'A') && (magic2[2] == 'M') && (magic2[3] == 'E')) { logerror("[NAME] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'W') && (magic2[1] == 'R') && (magic2[2] == 'T') && (magic2[3] == 'R')) { logerror("[WRTR] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'T') && (magic2[1] == 'V') && (magic2[2] == 'C') && (magic2[3] == 'I')) { logerror("[TVCI] chunk found.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); fread(&temp_byte, 1); logerror("Television Standard : %s\n", (temp_byte == 0) ? "NTSC" : (temp_byte == 1) ? "PAL" : "Does not matter"); read_length += (chunk_length + 8); } else if ((magic2[0] == 'T') && (magic2[1] == 'V') && (magic2[2] == 'S') && (magic2[3] == 'C')) // is this the same as TVCI?? { logerror("[TVSC] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'D') && (magic2[1] == 'I') && (magic2[2] == 'N') && (magic2[3] == 'F')) { logerror("[DINF] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'C') && (magic2[1] == 'T') && (magic2[2] == 'R') && (magic2[3] == 'L')) { logerror("[CTRL] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'B') && (magic2[1] == 'A') && (magic2[2] == 'T') && (magic2[3] == 'R')) { logerror("[BATR] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'V') && (magic2[1] == 'R') && (magic2[2] == 'O') && (magic2[3] == 'R')) { logerror("[VROR] chunk found. No support yet.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'M') && (magic2[1] == 'I') && (magic2[2] == 'R') && (magic2[3] == 'R')) { logerror("[MIRR] chunk found.\n"); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); fread(&mirror, 1); read_length += (chunk_length + 8); } else if ((magic2[0] == 'P') && (magic2[1] == 'C') && (magic2[2] == 'K')) { logerror("[PCK%c] chunk found. No support yet.\n", magic2[3]); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'C') && (magic2[1] == 'C') && (magic2[2] == 'K')) { logerror("[CCK%c] chunk found. No support yet.\n", magic2[3]); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); read_length += (chunk_length + 8); } else if ((magic2[0] == 'P') && (magic2[1] == 'R') && (magic2[2] == 'G')) { logerror("[PRG%c] chunk found. ", magic2[3]); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); prg_size += chunk_length; if (chunk_length / 0x4000) logerror("It consists of %d 16K-blocks.\n", chunk_length / 0x4000); else { small_prg = TRUE; logerror("This chunk is smaller than 16K: the emulation might have issues. Please report this file to the MESS forums.\n"); } /* Read in the program chunks */ fread(&temp_prg[prg_start], chunk_length); prg_start += chunk_length; read_length += (chunk_length + 8); } else if ((magic2[0] == 'C') && (magic2[1] == 'H') && (magic2[2] == 'R')) { logerror("[CHR%c] chunk found. ", magic2[3]); fread(&buffer, 4); chunk_length = buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24); vrom_size += chunk_length; logerror("It consists of %d 8K-blocks.\n", chunk_length / 0x2000); /* Read in the vrom chunks */ fread(&temp_chr[chr_start], chunk_length); chr_start += chunk_length; read_length += (chunk_length + 8); } else { logerror("Unsupported UNIF chunk or corrupted header. Please report the problem at MESS Board.\n"); read_length = size; } } } while (size > read_length); if (!mapr_chunk_found) { auto_free(machine(), temp_prg); auto_free(machine(), temp_chr); fatalerror("UNIF should have a [MAPR] chunk to work. Check if your image has been corrupted\n"); } if (!prg_start) { auto_free(machine(), temp_prg); auto_free(machine(), temp_chr); fatalerror("No PRG found. Please report the problem at MESS Board.\n"); } // SETUP step 2: getting PCB and other settings int pcb_id = 0, battery = 0, prgram = 0, vram_chunks = 0; unif_mapr_setup(unif_mapr, &pcb_id, &battery, &prgram, &vram_chunks); // SETUP step 3: storing the info needed for emulation m_pcb_id = pcb_id; if (battery) battery_size = NES_BATTERY_SIZE; // we should allow for smaller battery! prgram_size = prgram * 0x2000; vram_size = vram_chunks * 0x2000; m_cart->set_four_screen_vram(0); switch (mirror) { case 0: // Horizontal Mirroring (Hard Wired) m_cart->set_mirroring(PPU_MIRROR_HORZ); break; case 1: // Vertical Mirroring (Hard Wired) m_cart->set_mirroring(PPU_MIRROR_VERT); break; case 2: // Mirror All Pages From $2000 (Hard Wired) m_cart->set_mirroring(PPU_MIRROR_LOW); break; case 3: // Mirror All Pages From $2400 (Hard Wired) m_cart->set_mirroring(PPU_MIRROR_HIGH); break; case 4: // Four Screens of VRAM (Hard Wired) m_cart->set_four_screen_vram(1); break; case 5: // Mirroring Controlled By Mapper Hardware logerror("Mirroring handled by the board hardware.\n"); // default to horizontal at start m_cart->set_mirroring(PPU_MIRROR_HORZ); break; default: logerror("Undocumented mirroring value.\n"); // default to horizontal m_cart->set_mirroring(PPU_MIRROR_HORZ); break; } // SETUP step 4: logging what we have found logerror("-- Board %s\n", unif_mapr); logerror("-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); // SETUP steps 5/6: allocate pointers for PRG/VROM and load the data! if (prg_size == 0x4000) { m_cart->prg_alloc(machine(), 0x8000); memcpy(m_cart->get_prg_base(), temp_prg, 0x4000); memcpy(m_cart->get_prg_base() + 0x4000, m_cart->get_prg_base(), 0x4000); } else { m_cart->prg_alloc(machine(), prg_size); memcpy(m_cart->get_prg_base(), temp_prg, prg_size); } if (small_prg) // This is not supported yet, so warn users about this mame_printf_error("Loaded UNIF file with non-16k PRG chunk. This is not supported in MESS yet."); if (vrom_size) { m_cart->vrom_alloc(machine(), vrom_size); memcpy(m_cart->get_vrom_base(), temp_chr, vrom_size); } #if SPLIT_PRG { FILE *prgout; char outname[255]; sprintf(outname, "%s.prg", filename()); prgout = fopen(outname, "wb"); if (prgout) { fwrite(m_cart->get_prg_base(), 1, 0x4000 * m_cart->get_prg_size(), prgout); mame_printf_error("Created PRG chunk\n"); } fclose(prgout); } #endif #if SPLIT_CHR if (state->m_chr_chunks > 0) { FILE *chrout; char outname[255]; sprintf(outname, "%s.chr", filename()); chrout= fopen(outname, "wb"); if (chrout) { fwrite(m_cart->get_vrom_base(), 1, m_cart->get_vrom_size(), chrout); mame_printf_error("Created CHR chunk\n"); } fclose(chrout); } #endif // free the temporary copy of PRG/CHR auto_free(machine(), temp_prg); auto_free(machine(), temp_chr); logerror("UNIF support is only very preliminary.\n"); } else { logerror("%s is NOT a file in either iNES or UNIF format.\n", filename()); return IMAGE_INIT_FAIL; } } else { // SETUP step 1: getting PRG, VROM, VRAM sizes UINT32 prg_size = get_software_region_length("prg"); UINT32 vrom_size = get_software_region_length("chr"); UINT32 vram_size = get_software_region_length("vram"); vram_size += get_software_region_length("vram2"); // validate the xml fields if (!prg_size) fatalerror("No PRG entry for this software! Please check if the xml list got corrupted\n"); if (prg_size < 0x8000) fatalerror("PRG entry is too small! Please check if the xml list got corrupted\n"); // SETUP step 2: getting PCB and other settings if (get_feature("pcb")) m_pcb_id = nes_get_pcb_id(machine(), get_feature("pcb")); else m_pcb_id = NO_BOARD; // SETUP step 3: storing the info needed for emulation if (m_pcb_id == STD_TVROM || m_pcb_id == STD_DRROM || m_pcb_id == IREM_LROG017) m_cart->set_four_screen_vram(1); else m_cart->set_four_screen_vram(0); if (get_software_region("bwram") != NULL) battery_size = get_software_region_length("bwram"); if (m_pcb_id == BANDAI_LZ93EX) { // allocate the 24C01 or 24C02 EEPROM battery_size += 0x2000; } if (m_pcb_id == BANDAI_DATACH) { // allocate the 24C01 and 24C02 EEPROM battery_size += 0x4000; } if (get_software_region("wram") != NULL) prgram_size = get_software_region_length("wram"); if (get_software_region("mapper_ram") != NULL) mapper_ram_size = get_software_region_length("mapper_ram"); if (get_software_region("mapper_bram") != NULL) mapper_bram_size = get_software_region_length("mapper_bram"); if (get_feature("mirroring")) { const char *mirroring = get_feature("mirroring"); if (!strcmp(mirroring, "horizontal")) m_cart->set_mirroring(PPU_MIRROR_HORZ); if (!strcmp(mirroring, "vertical")) m_cart->set_mirroring(PPU_MIRROR_VERT); if (!strcmp(mirroring, "high")) m_cart->set_mirroring(PPU_MIRROR_HIGH); if (!strcmp(mirroring, "low")) m_cart->set_mirroring(PPU_MIRROR_LOW); } else m_cart->set_mirroring(PPU_MIRROR_NONE); /* Check for pins in specific boards which require them */ if (m_pcb_id == STD_CNROM) { if (get_feature("chr-pin26") != NULL) { m_ce_mask |= 0x01; m_ce_state |= !strcmp(get_feature("chr-pin26"), "CE") ? 0x01 : 0; } if (get_feature("chr-pin27") != NULL) { m_ce_mask |= 0x02; m_ce_state |= !strcmp(get_feature("chr-pin27"), "CE") ? 0x02 : 0; } } if (m_pcb_id == TAITO_X1_005 && get_feature("x1-pin17") != NULL && get_feature("x1-pin31") != NULL) { if (!strcmp(get_feature("x1-pin17"), "CIRAM A10") && !strcmp(get_feature("x1-pin31"), "NC")) m_pcb_id = TAITO_X1_005_A; } if (m_pcb_id == KONAMI_VRC2) { m_vrc_ls_prg_a = nes_cart_get_line(get_feature("vrc2-pin3")); m_vrc_ls_prg_b = nes_cart_get_line(get_feature("vrc2-pin4")); m_vrc_ls_chr = (nes_cart_get_line(get_feature("vrc2-pin21")) != 10) ? 1 : 0; // mame_printf_error("VRC-2, pin3: A%d, pin4: A%d, pin21: %s\n", state->m_vrc_ls_prg_a, state->m_vrc_ls_prg_b, state->m_vrc_ls_chr ? "NC" : "A10"); } if (m_pcb_id == KONAMI_VRC4) { m_vrc_ls_prg_a = nes_cart_get_line(get_feature("vrc4-pin3")); m_vrc_ls_prg_b = nes_cart_get_line(get_feature("vrc4-pin4")); // mame_printf_error("VRC-4, pin3: A%d, pin4: A%d\n", state->m_vrc_ls_prg_a, state->m_vrc_ls_prg_b); } if (m_pcb_id == KONAMI_VRC6) { m_vrc_ls_prg_a = nes_cart_get_line(get_feature("vrc6-pin9")); m_vrc_ls_prg_b = nes_cart_get_line(get_feature("vrc6-pin10")); // mame_printf_error("VRC-6, pin9: A%d, pin10: A%d\n", state->m_vrc_ls_prg_a, state->m_vrc_ls_prg_b); } /* Check for other misc board variants */ if (m_pcb_id == STD_SOROM) { if (get_feature("mmc1_type") != NULL && !strcmp(get_feature("mmc1_type"), "MMC1A")) m_pcb_id = STD_SOROM_A; // in MMC1-A PRG RAM is always enabled } if (m_pcb_id == STD_SXROM) { if (get_feature("mmc1_type") != NULL && !strcmp(get_feature("mmc1_type"), "MMC1A")) m_pcb_id = STD_SXROM_A; // in MMC1-A PRG RAM is always enabled } if (m_pcb_id == STD_NXROM || m_pcb_id == SUNSOFT_DCS) { if (get_software_region("minicart") != NULL) // check for dual minicart { m_pcb_id = SUNSOFT_DCS; // we shall load somewhere the minicart, but we still do not support this } } // SETUP step 4: logging what we have found logerror("Loaded game from softlist:\n"); logerror("-- PCB: %s", get_feature("pcb")); if (m_pcb_id == UNSUPPORTED_BOARD) logerror(" (currently not supported by MESS)"); logerror("\n-- PRG 0x%x (%d x 16k chunks)\n", prg_size, prg_size / 0x4000); logerror("-- VROM 0x%x (%d x 8k chunks)\n", vrom_size, vrom_size / 0x2000); logerror("-- VRAM 0x%x (%d x 8k chunks)\n", vram_size, vram_size / 0x2000); logerror("-- PRG NVWRAM: %d\n", m_cart->get_battery_size()); logerror("-- PRG WRAM: %d\n", m_cart->get_prgram_size()); // SETUP steps 5/6: allocate pointers for PRG/VROM and load the data! m_cart->prg_alloc(machine(), prg_size); memcpy(m_cart->get_prg_base(), get_software_region("prg"), prg_size); if (vrom_size) { m_cart->vrom_alloc(machine(), vrom_size); memcpy(m_cart->get_vrom_base(), get_software_region("chr"), vrom_size); } } // Allocate the remaining pointer, when needed if (vram_size) m_cart->vram_alloc(machine(), vram_size); if (prgram_size) m_cart->prgram_alloc(machine(), prgram_size); if (mapper_ram_size) m_cart->mapper_ram_alloc(machine(), mapper_ram_size); // Attempt to load a battery file for this ROM // A few boards have internal RAM with a battery (MMC6, Taito X1-005 & X1-017, etc.) if (battery_size || mapper_bram_size) { UINT32 tot_size = battery_size + mapper_bram_size; UINT8 *temp_nvram = auto_alloc_array(machine(), UINT8, tot_size); battery_load(temp_nvram, tot_size, 0x00); if (battery_size) { m_cart->battery_alloc(machine(), battery_size); memcpy(m_cart->get_battery_base(), temp_nvram, battery_size); } if (mapper_bram_size) { m_cart->mapper_bram_alloc(machine(), mapper_bram_size); memcpy(m_cart->get_mapper_bram_base(), temp_nvram + battery_size, mapper_bram_size); } if (temp_nvram) auto_free(machine(), temp_nvram); } } return IMAGE_INIT_PASS; } /*------------------------------------------------- call_unload -------------------------------------------------*/ void nes_cart_slot_device::call_unload() { if (m_cart) { if (m_cart->get_battery_size() || m_cart->get_mapper_bram_size()) { UINT32 tot_size = m_cart->get_battery_size() + m_cart->get_mapper_bram_size(); UINT8 *temp_nvram = auto_alloc_array(machine(), UINT8, tot_size); if (m_cart->get_battery_size()) memcpy(temp_nvram, m_cart->get_battery_base(), m_cart->get_battery_size()); if (m_cart->get_mapper_bram_size()) memcpy(temp_nvram + m_cart->get_battery_size(), m_cart->get_mapper_bram_base(), m_cart->get_mapper_bram_size()); battery_save(temp_nvram, tot_size); if (temp_nvram) auto_free(machine(), temp_nvram); } } } /*------------------------------------------------- call softlist load -------------------------------------------------*/ bool nes_cart_slot_device::call_softlist_load(char *swlist, char *swname, rom_entry *start_entry) { load_software_part_region(this, swlist, swname, start_entry ); return TRUE; } /*------------------------------------------------- get default card software -------------------------------------------------*/ const char * nes_cart_slot_device::get_default_card_software(const machine_config &config, emu_options &options) { return software_get_default_slot(config, options, this, "rom"); } /*------------------------------------------------- read -------------------------------------------------*/ READ8_MEMBER(nes_cart_slot_device::read_l) { if (m_cart) return m_cart->read_l(space, offset); else return 0xff; } READ8_MEMBER(nes_cart_slot_device::read_m) { if (m_cart) return m_cart->read_m(space, offset); else return 0xff; } READ8_MEMBER(nes_cart_slot_device::read_h) { if (m_cart) return m_cart->read_h(space, offset); else return 0xff; } /*------------------------------------------------- write -------------------------------------------------*/ WRITE8_MEMBER(nes_cart_slot_device::write_l) { if (m_cart) m_cart->write_l(space, offset, data); } WRITE8_MEMBER(nes_cart_slot_device::write_m) { if (m_cart) m_cart->write_m(space, offset, data); } WRITE8_MEMBER(nes_cart_slot_device::write_h) { if (m_cart) m_cart->write_h(space, offset, data); } // CART DEVICE [TO BE MOVED TO SEPARATE SOURCE LATER] //------------------------------------------------- // nes_rom_device - constructor //------------------------------------------------- const device_type NES_ROM = &device_creator; nes_rom_device::nes_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, NES_ROM, "NES ROM", tag, owner, clock), device_nes_cart_interface( mconfig, *this ) { } nes_rom_device::nes_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock) : device_t(mconfig, type, name, tag, owner, clock), device_nes_cart_interface( mconfig, *this ) { } //------------------------------------------------- // device_start - device-specific startup //------------------------------------------------- void nes_rom_device::device_start() { }