diff options
Diffstat (limited to 'src/devices/bus/intv/slot.cpp')
-rw-r--r-- | src/devices/bus/intv/slot.cpp | 571 |
1 files changed, 571 insertions, 0 deletions
diff --git a/src/devices/bus/intv/slot.cpp b/src/devices/bus/intv/slot.cpp new file mode 100644 index 00000000000..6b48f976e7f --- /dev/null +++ b/src/devices/bus/intv/slot.cpp @@ -0,0 +1,571 @@ +// license:BSD-3-Clause +// copyright-holders:Fabio Priuli +/*********************************************************************************************************** + + Mattel Intellivision cart emulation + (through slot devices) + + + This is a strange beast, because INTV carts had potentially access to + a *LOT* of memory ranges! + Quoting Joe Zbiciak's documentation for his emu (jzIntv): + + + The Intellivision leaves many addresses available to cartridges. However, + several address ranges come with caveats, such as interactions with other + devices in the system, or incompatibilities with various peripherals. + + Below is a summary. + + ADDRESSES NOTES + -------------- -------------------------------------------------------------- + $0400 - $04FF RAM/ROM ok on all but Intellivision 2. + $0500 - $06FF RAM/ROM ok. + $0700 - $0CFF RAM/ROM ok if no Intellivoice. + $0D00 - $0FFF RAM/ROM ok. + $2000 - $2FFF RAM/ROM ok if no ECS. + $4000 - $47FF RAM/ROM ok if no ECS. + $4800 ROM ok. RAM ok only if boot ROM at $7000. + $4801 - $4FFF RAM/ROM ok. + $5000 - $5014 ROM ok. RAM ok only if boot ROM at $7000 or $4800. + $5015 - $6FFF RAM/ROM ok. + $7000 ROM ok if no ECS. RAM at $7000 confuses EXEC boot sequence. + $7001 - $77FF RAM/ROM ok if no ECS. + $7800 - $7FFF ROM ok if no ECS. Do not map RAM here due to GRAM alias. + $8000 - $8FFF RAM/ROM ok. Avoid STIC alias at $8000 - $803F. + $9000 - $B7FF RAM/ROM ok. + $B800 - $BFFF ROM ok. Do not map RAM here due to GRAM alias. + $C000 - $CFFF RAM/ROM ok. Avoid STIC alias at $C000 - $C03F. + $D000 - $DFFF RAM/ROM ok. + $E000 - $EFFF RAM/ROM ok if no ECS. + $F000 - $F7FF RAM/ROM ok. + $F800 - $FFFF ROM ok. Do not map RAM here due to GRAM alias. + + + We handle this, by always creating a 0x10000 wide ROM region to load the + cart image and exposing the following (long list of) read handlers: + read_rom04 + read_rom20 + read_rom40 + read_rom48 + read_rom50 + read_rom60 + read_rom70 + read_rom80 + read_rom90 + read_roma0 + read_romb0 + read_romc0 + read_romd0 + read_rome0 + read_romf0 + Each pcb types will then use the correct ones for its wiring setup. + + The BIN+CFG format introduced by INTVPC emulator includes metadata about where to + load ROM into memory in the CFG file, but we don't support it (because we don't parse + the CFG at all) and we rely instead on the intv.hsi metadata for fullpath loading of + these. + Alternatively, we support the .ROM format used by jzIntv. + + + TODO: + - Convert also the keyboard component to be a passthru slot device + - Merge some of the ROM accessor above, once it is clear which ones can be merged + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "slot.h" +#include "hashfile.h" + +#define INTELLIVOICE_MASK 0x02 +#define ECS_MASK 0x01 + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type INTV_CART_SLOT = &device_creator<intv_cart_slot_device>; + +//************************************************************************** +// Intellivision Cartridges Interface +//************************************************************************** + +//------------------------------------------------- +// device_intv_cart_interface - constructor +//------------------------------------------------- + +device_intv_cart_interface::device_intv_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL), + m_rom_size(0) +{ +} + + +//------------------------------------------------- +// ~device_intv_cart_interface - destructor +//------------------------------------------------- + +device_intv_cart_interface::~device_intv_cart_interface() +{ +} + +//------------------------------------------------- +// rom_alloc - alloc the space for the cart +//------------------------------------------------- + +void device_intv_cart_interface::rom_alloc(UINT32 size, const char *tag) +{ + if (m_rom == NULL) + { + m_rom = device().machine().memory().region_alloc(std::string(tag).append(INTVSLOT_ROM_REGION_TAG).c_str(), size, 1, ENDIANNESS_LITTLE)->base(); + memset(m_rom, 0xff, size); + m_rom_size = size; + } +} + + +//------------------------------------------------- +// ram_alloc - alloc the space for the ram +//------------------------------------------------- + +void device_intv_cart_interface::ram_alloc(UINT32 size) +{ + m_ram.resize(size); +} + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// intv_cart_slot_device - constructor +//------------------------------------------------- +intv_cart_slot_device::intv_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, INTV_CART_SLOT, "Intellivision Cartridge Slot", tag, owner, clock, "intv_cart_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this), + m_type(INTV_STD) +{ +} + + +//------------------------------------------------- +// intv_cart_slot_device - destructor +//------------------------------------------------- + +intv_cart_slot_device::~intv_cart_slot_device() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void intv_cart_slot_device::device_start() +{ + m_cart = dynamic_cast<device_intv_cart_interface *>(get_card_device()); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void intv_cart_slot_device::device_config_complete() +{ + // set brief and instance name + update_names(); +} + + +//------------------------------------------------- +// INTV PCB +//------------------------------------------------- + +struct intv_slot +{ + int pcb_id; + const char *slot_option; +}; + +// Here, we take the feature attribute from .xml (i.e. the PCB name) and we assign a unique ID to it +static const intv_slot slot_list[] = +{ + { INTV_STD, "intv_rom" }, + { INTV_RAM, "intv_ram" }, + { INTV_GFACT, "intv_gfact" }, + { INTV_WSMLB, "intv_wsmlb" }, + { INTV_VOICE, "intv_voice" }, + { INTV_ECS, "intv_ecs" }, + { INTV_KEYCOMP, "intv_keycomp" } +}; + +static int intv_get_pcb_id(const char *slot) +{ + for (int i = 0; i < ARRAY_LENGTH(slot_list); i++) + { + if (!core_stricmp(slot_list[i].slot_option, slot)) + return slot_list[i].pcb_id; + } + + return 0; +} + +#if 1 +static const char *intv_get_slot(int type) +{ + for (int i = 0; i < ARRAY_LENGTH(slot_list); i++) + { + if (slot_list[i].pcb_id == type) + return slot_list[i].slot_option; + } + + return "intv_rom"; +} +#endif + +/*------------------------------------------------- + call load + -------------------------------------------------*/ + +int intv_cart_slot_device::load_fullpath() +{ + UINT8 temp; + UINT8 num_segments; + UINT8 start_seg; + UINT8 end_seg; + + UINT32 current_address; + UINT32 end_address; + + UINT8 high_byte; + UINT8 low_byte; + + UINT8 *ROM; + const char *file_type = filetype(); + + /* if it is in .rom format, we enter here */ + if (!core_stricmp (file_type, "rom")) + { + // header + fread(&temp, 1); + if (temp != 0xa8) + return IMAGE_INIT_FAIL; + + fread(&num_segments, 1); + + fread(&temp, 1); + if (temp != (num_segments ^ 0xff)) + return IMAGE_INIT_FAIL; + + m_cart->rom_alloc(0x20000, tag()); + ROM = (UINT8 *)m_cart->get_rom_base(); + + for (int i = 0; i < num_segments; i++) + { + fread(&start_seg, 1); + current_address = start_seg * 0x100; + + fread(&end_seg, 1); + end_address = end_seg * 0x100 + 0xff; + + while (current_address <= end_address) + { + fread(&low_byte, 1); + ROM[(current_address << 1) + 1] = low_byte; + fread(&high_byte, 1); + ROM[current_address << 1] = high_byte; + current_address++; + } + + // Here we should calculate and compare the CRC16... + fread(&temp, 1); + fread(&temp, 1); + } + + // Access tables and fine address restriction tables are not supported ATM + for (int i = 0; i < (16 + 32 + 2); i++) + { + fread(&temp, 1); + } + return IMAGE_INIT_PASS; + } + /* otherwise, we load it as a .bin file, using extrainfo from intv.hsi in place of .cfg */ + else + { + // This code is a blatant hack, due to impossibility to load a separate .cfg file in MESS. + // It shall be eventually replaced by the .xml loading + + // extrainfo format + // 1. mapper number (to deal with bankswitch). no bankswitch is mapper 0 (most games). + // 2.->5. current images have at most 4 chunks of data. we store here block size and location to load + // (value & 0xf0) >> 4 is the location / 0x1000 + // (value & 0x0f) is the size / 0x800 + // 6. some images have a ram chunk. as above we store location and size in 8 bits + // 7. extra = 1 ECS, 2 Intellivoice + int start, size; + int mapper, rom[5], ram, extra; + std::string extrainfo; + + m_cart->rom_alloc(0x20000, tag()); + ROM = (UINT8 *)m_cart->get_rom_base(); + + if (!hashfile_extrainfo(*this, extrainfo)) + { + // If no extrainfo, we assume a single 0x2000 chunk at 0x5000 + for (int i = 0; i < 0x2000; i++ ) + { + fread(&low_byte, 1); + ROM[((0x5000 + i) << 1) + 1] = low_byte; + fread(&high_byte, 1); + ROM[(0x5000 + i) << 1] = high_byte; + } + } + else + { + sscanf(extrainfo.c_str() ,"%d %d %d %d %d %d %d", &mapper, &rom[0], &rom[1], &rom[2], + &rom[3], &ram, &extra); + //printf("extrainfo: %d %d %d %d %d %d %d \n", mapper, rom[0], rom[1], rom[2], rom[3], ram, extra); + + if (mapper) + logerror("Bankswitch not yet implemented!\n"); + + if (ram) + { + start = ((ram & 0xf0) >> 4) * 0x1000; + size = (ram & 0x0f) * 0x800; + + if (start == 0xd000 && size == 0x800) + { + m_type = INTV_RAM; + m_cart->ram_alloc(0x800); + } + else if (start == 0x8800 && size == 0x800) + { + m_type = INTV_GFACT; + m_cart->ram_alloc(0x800); + } + else + printf("Unrecognized RAM setup [Start 0x%X - End 0x%X]. Please contact MESSdevs.\n", start, start + size); + } + if (extra & INTELLIVOICE_MASK) + { + printf("WARNING: This game requires emulation of the IntelliVoice module.\n"); + } + + if (extra & ECS_MASK) + { + printf("WARNING: This game requires emulation of the ECS module.\n"); + } + + for (int j = 0; j < 4; j++) + { + start = ((rom[j] & 0xf0) >> 4) * 0x1000; + size = (rom[j] & 0x0f) * 0x800; + + // some cart has to be loaded to 0x4800, but none of the available ones goes to 0x4000. + // Hence, we use 0x04 << 4 in extrainfo (to reduce the stored values) and fix the value here. + if (start == 0x4000) start += 0x800; + +// logerror("step %d: %d %d \n", j, start / 0x1000, size / 0x1000); + + for (int i = 0; i < size; i++) + { + fread(&low_byte, 1); + ROM[((start + i) << 1) + 1] = low_byte; + fread(&high_byte, 1); + ROM[(start + i) << 1] = high_byte; + } + } + } + + return IMAGE_INIT_PASS; + } +} + +bool intv_cart_slot_device::call_load() +{ + if (m_cart) + { + if (software_entry() == NULL) + return load_fullpath(); + else + { + UINT16 offset[] = { 0x400, 0x2000, 0x4000, 0x4800, 0x5000, 0x6000, 0x7000, 0x8000, 0x9000, 0xa000, 0xb000, 0xc000, 0xd000, 0xe000, 0xf000}; + const char* region_name[] = {"0400", "2000", "4000", "4800", "5000", "6000", "7000", "8000", "9000", "A000", "B000", "C000", "D000", "E000", "F000"}; + const char *pcb_name = get_feature("slot"); + bool extra_bank = false; + + if (pcb_name) + m_type = intv_get_pcb_id(pcb_name); + + // these two carts have paged roms, which does not work well with our 0x10000 rom region + // so if we are loading one of these, we allocate additional 0x2000 bytes for the paged bank + if (m_type == INTV_WSMLB) + extra_bank = true; + + UINT32 size = 0; + UINT16 address = 0; + UINT8 *ROM, *region; + + m_cart->rom_alloc(extra_bank ? 0x22000 : 0x20000, tag()); + ROM = m_cart->get_rom_base(); + + for (int i = 0; i < 15; i++) + { + address = offset[i]; + size = get_software_region_length(region_name[i]); + if (size) + { + region = get_software_region(region_name[i]); + + for (int j = 0; j < size / 2; j++) + { + ROM[((address + j) << 1) + 1] = region[2 * j]; + ROM[(address + j) << 1] = region[2 * j + 1]; + } + } + } + + if (m_type == INTV_RAM || m_type == INTV_GFACT || m_type == INTV_ECS) + m_cart->ram_alloc(get_software_region_length("ram")); + + //printf("Type: %s\n", intv_get_slot(m_type)); + return IMAGE_INIT_PASS; + } + } + + return IMAGE_INIT_PASS; +} + + +/*------------------------------------------------- + call softlist load + -------------------------------------------------*/ + +bool intv_cart_slot_device::call_softlist_load(software_list_device &swlist, const char *swname, const rom_entry *start_entry) +{ + load_software_part_region(*this, swlist, swname, start_entry); + return TRUE; +} + + +/*------------------------------------------------- + get default card software + -------------------------------------------------*/ + +void intv_cart_slot_device::get_default_card_software(std::string &result) +{ + if (open_image_file(mconfig().options())) + { + const char *slot_string = "intv_rom"; + UINT32 len = core_fsize(m_file); + dynamic_buffer rom(len); + int type = INTV_STD; + + core_fread(m_file, &rom[0], len); + + if (rom[0] == 0xa8 && (rom[1] == (rom[2] ^ 0xff))) + { + // it's .ROM file, so that we don't have currently any way to distinguish RAM-equipped carts + } + else + { + // assume it's .BIN and try to use .hsi file to determine type (just RAM) + int start; + int mapper, rom[5], ram, extra; + std::string extrainfo; + + if (hashfile_extrainfo(*this, extrainfo)) + { + sscanf(extrainfo.c_str() ,"%d %d %d %d %d %d %d", &mapper, &rom[0], &rom[1], &rom[2], + &rom[3], &ram, &extra); + + if (ram) + { + start = ((ram & 0xf0) >> 4) * 0x1000; + if (start == 0xd000) + type = INTV_RAM; + if (start == 0x8800) + type = INTV_GFACT; + } + } + + } + + slot_string = intv_get_slot(type); + + //printf("type: %s\n", slot_string); + clear(); + + result.assign(slot_string); + return; + } + software_get_default_slot(result, "intv_rom"); +} + +/*------------------------------------------------- + read_ay + -------------------------------------------------*/ + +READ16_MEMBER(intv_cart_slot_device::read_ay) +{ + if (m_cart) + return m_cart->read_ay(space, offset, mem_mask); + else + return 0xffff; +} + +/*------------------------------------------------- + write_ay + -------------------------------------------------*/ + +WRITE16_MEMBER(intv_cart_slot_device::write_ay) +{ + if (m_cart) + m_cart->write_ay(space, offset, data, mem_mask); +} + +/*------------------------------------------------- + read_speech + -------------------------------------------------*/ + +READ16_MEMBER(intv_cart_slot_device::read_speech) +{ + if (m_cart) + return m_cart->read_speech(space, offset, mem_mask); + else + return 0xffff; +} + +/*------------------------------------------------- + write_speech + -------------------------------------------------*/ + +WRITE16_MEMBER(intv_cart_slot_device::write_speech) +{ + if (m_cart) + m_cart->write_speech(space, offset, data, mem_mask); +} + + + +#include "bus/intv/rom.h" +#include "bus/intv/ecs.h" +//#include "bus/intv/keycomp.h" +#include "bus/intv/voice.h" + +SLOT_INTERFACE_START(intv_cart) + SLOT_INTERFACE_INTERNAL("intv_rom", INTV_ROM_STD) + SLOT_INTERFACE_INTERNAL("intv_ram", INTV_ROM_RAM) + SLOT_INTERFACE_INTERNAL("intv_gfact", INTV_ROM_GFACT) + SLOT_INTERFACE_INTERNAL("intv_wsmlb", INTV_ROM_WSMLB) + SLOT_INTERFACE_INTERNAL("intv_voice", INTV_ROM_VOICE) + SLOT_INTERFACE_INTERNAL("intv_ecs", INTV_ROM_ECS) +// SLOT_INTERFACE_INTERNAL("intv_keycomp", INTV_ROM_KEYCOMP) +SLOT_INTERFACE_END |