diff options
author | 2014-03-10 14:00:01 +0000 | |
---|---|---|
committer | 2014-03-10 14:00:01 +0000 | |
commit | d131f7fa0cd0255b1a33778d1b51d1bb967fb53a (patch) | |
tree | 3021094c998780a9c4b442173889a2dbc46a20e4 /src/emu/bus | |
parent | fe36f872390885a181844708e6459d4d85f1c2fc (diff) |
Moved rest of console slot devices to bus folder (nw)
Diffstat (limited to 'src/emu/bus')
58 files changed, 15146 insertions, 3 deletions
diff --git a/src/emu/bus/bus.mak b/src/emu/bus/bus.mak index 28ad39783ef..c52b987ad18 100644 --- a/src/emu/bus/bus.mak +++ b/src/emu/bus/bus.mak @@ -20,6 +20,7 @@ BUSOBJ = $(EMUOBJ)/bus #------------------------------------------------- ifneq ($(filter ABCBUS,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/abcbus BUSOBJS += $(BUSOBJ)/abcbus/abcbus.o BUSOBJS += $(BUSOBJ)/abcbus/abc890.o BUSOBJS += $(BUSOBJ)/abcbus/dos.o @@ -42,6 +43,7 @@ endif #------------------------------------------------- ifneq ($(filter ADAM,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/adam BUSOBJS += $(BUSOBJ)/adam/exp.o BUSOBJS += $(BUSOBJ)/adam/adamlink.o BUSOBJS += $(BUSOBJ)/adam/ide.o @@ -55,6 +57,7 @@ endif #------------------------------------------------- ifneq ($(filter ADAMNET,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/adamnet BUSOBJS += $(BUSOBJ)/adamnet/adamnet.o BUSOBJS += $(BUSOBJ)/adamnet/ddp.o BUSOBJS += $(BUSOBJ)/adamnet/fdc.o @@ -70,6 +73,7 @@ endif #------------------------------------------------- ifneq ($(filter BW2,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/bw2 BUSOBJS += $(BUSOBJ)/bw2/exp.o BUSOBJS += $(BUSOBJ)/bw2/ramcard.o endif @@ -82,6 +86,7 @@ endif #------------------------------------------------- ifneq ($(filter C64,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/c64 BUSOBJS += $(BUSOBJ)/c64/exp.o BUSOBJS += $(BUSOBJ)/c64/c128_comal80.o BUSOBJS += $(BUSOBJ)/c64/comal80.o @@ -163,6 +168,7 @@ endif #------------------------------------------------- ifneq ($(filter CBM2,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/cbm2 BUSOBJS += $(BUSOBJ)/cbm2/exp.o BUSOBJS += $(BUSOBJ)/cbm2/24k.o BUSOBJS += $(BUSOBJ)/cbm2/hrg.o @@ -177,6 +183,7 @@ endif #------------------------------------------------- ifneq ($(filter CBMIEC,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/cbmiec BUSOBJS += $(BUSOBJ)/cbmiec/cbmiec.o BUSOBJS += $(BUSOBJ)/cbmiec/c1541.o BUSOBJS += $(BUSOBJ)/cbmiec/c1571.o @@ -196,6 +203,7 @@ endif #------------------------------------------------- ifneq ($(filter COMX35,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/comx35 BUSOBJS += $(BUSOBJ)/comx35/exp.o BUSOBJS += $(BUSOBJ)/comx35/clm.o BUSOBJS += $(BUSOBJ)/comx35/expbox.o @@ -214,6 +222,7 @@ endif #------------------------------------------------- ifneq ($(filter ECBBUS,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/ecbbus BUSOBJS += $(BUSOBJ)/ecbbus/ecbbus.o BUSOBJS += $(BUSOBJ)/ecbbus/grip.o endif @@ -225,6 +234,7 @@ endif #------------------------------------------------- ifneq ($(filter ECONET,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/econet BUSOBJS += $(BUSOBJ)/econet/econet.o BUSOBJS += $(BUSOBJ)/econet/e01.o endif @@ -236,6 +246,7 @@ endif #------------------------------------------------- ifneq ($(filter EP64,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/ep64 BUSOBJS += $(BUSOBJ)/ep64/exp.o BUSOBJS += $(BUSOBJ)/ep64/exdos.o endif @@ -247,6 +258,7 @@ endif #------------------------------------------------- ifneq ($(filter IEEE488,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/ieee488 BUSOBJS += $(BUSOBJ)/ieee488/ieee488.o BUSOBJS += $(BUSOBJ)/ieee488/c2031.o BUSOBJS += $(BUSOBJ)/ieee488/c2040.o @@ -267,6 +279,7 @@ endif #------------------------------------------------- ifneq ($(filter IQ151,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/iq151 BUSOBJS += $(BUSOBJ)/iq151/iq151.o BUSOBJS += $(BUSOBJ)/iq151/disc2.o BUSOBJS += $(BUSOBJ)/iq151/grafik.o @@ -285,6 +298,7 @@ endif #------------------------------------------------- ifneq ($(filter IMI7000,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/imi7000 BUSOBJS += $(BUSOBJ)/imi7000/imi7000.o BUSOBJS += $(BUSOBJ)/imi7000/imi5000h.o endif @@ -296,6 +310,7 @@ endif #------------------------------------------------- ifneq ($(filter ISA,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/isa BUSOBJS += $(BUSOBJ)/isa/isa.o BUSOBJS += $(BUSOBJ)/isa/isa_cards.o BUSOBJS += $(BUSOBJ)/isa/mda.o @@ -350,6 +365,7 @@ endif #------------------------------------------------- ifneq ($(filter ISBX,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/isbx BUSOBJS += $(BUSOBJ)/isbx/isbx.o BUSOBJS += $(BUSOBJ)/isbx/compis_fdc.o BUSOBJS += $(BUSOBJ)/isbx/isbc_218a.o @@ -362,6 +378,7 @@ endif #------------------------------------------------- ifneq ($(filter KC,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/kc BUSOBJS += $(BUSOBJ)/kc/kc.o BUSOBJS += $(BUSOBJ)/kc/d002.o BUSOBJS += $(BUSOBJ)/kc/d004.o @@ -375,6 +392,7 @@ endif #------------------------------------------------- ifneq ($(filter PC_JOY,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/pc_joy BUSOBJS += $(BUSOBJ)/pc_joy/pc_joy.o BUSOBJS += $(BUSOBJ)/pc_joy/pc_joy_sw.o endif @@ -386,6 +404,7 @@ endif #------------------------------------------------- ifneq ($(filter PC_KBD,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/pc_kbd BUSOBJS += $(BUSOBJ)/pc_kbd/pc_kbdc.o BUSOBJS += $(BUSOBJ)/pc_kbd/keyboards.o BUSOBJS += $(BUSOBJ)/pc_kbd/ec1841.o @@ -406,6 +425,7 @@ endif #------------------------------------------------- ifneq ($(filter PET,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/pet BUSOBJS += $(BUSOBJ)/pet/cass.o BUSOBJS += $(BUSOBJ)/pet/c2n.o BUSOBJS += $(BUSOBJ)/pet/diag264_lb_tape.o @@ -424,6 +444,7 @@ endif #------------------------------------------------- ifneq ($(filter PLUS4,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/plus4 BUSOBJS += $(BUSOBJ)/plus4/exp.o BUSOBJS += $(BUSOBJ)/plus4/c1551.o BUSOBJS += $(BUSOBJ)/plus4/sid.o @@ -439,6 +460,7 @@ endif #------------------------------------------------- ifneq ($(filter S100,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/s100 BUSOBJS += $(BUSOBJ)/s100/s100.o BUSOBJS += $(BUSOBJ)/s100/dj2db.o BUSOBJS += $(BUSOBJ)/s100/djdma.o @@ -455,6 +477,7 @@ endif #------------------------------------------------- ifneq ($(filter TVC,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/tvc BUSOBJS += $(BUSOBJ)/tvc/tvc.o BUSOBJS += $(BUSOBJ)/tvc/hbf.o endif @@ -466,6 +489,7 @@ endif #------------------------------------------------- ifneq ($(filter VCS,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/vcs BUSOBJS += $(BUSOBJ)/vcs/ctrl.o BUSOBJS += $(BUSOBJ)/vcs/joystick.o BUSOBJS += $(BUSOBJ)/vcs/joybooster.o @@ -482,6 +506,7 @@ endif #------------------------------------------------- ifneq ($(filter VIC10,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/vic10 BUSOBJS += $(BUSOBJ)/vic10/exp.o BUSOBJS += $(BUSOBJ)/vic10/std.o endif @@ -494,6 +519,7 @@ endif #------------------------------------------------- ifneq ($(filter VIC20,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/vic20 BUSOBJS += $(BUSOBJ)/vic20/exp.o BUSOBJS += $(BUSOBJ)/vic20/megacart.o BUSOBJS += $(BUSOBJ)/vic20/std.o @@ -514,6 +540,7 @@ endif #------------------------------------------------- ifneq ($(filter VIDBRAIN,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/vidbrain BUSOBJS += $(BUSOBJ)/vidbrain/exp.o BUSOBJS += $(BUSOBJ)/vidbrain/std.o BUSOBJS += $(BUSOBJ)/vidbrain/money_minder.o @@ -528,6 +555,7 @@ endif #------------------------------------------------- ifneq ($(filter VIP,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/vip BUSOBJS += $(BUSOBJ)/vip/byteio.o BUSOBJS += $(BUSOBJ)/vip/vp620.o BUSOBJS += $(BUSOBJ)/vip/exp.o @@ -547,6 +575,7 @@ endif #------------------------------------------------- ifneq ($(filter WANGPC,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/wangpc BUSOBJS += $(BUSOBJ)/wangpc/wangpc.o BUSOBJS += $(BUSOBJ)/wangpc/emb.o BUSOBJS += $(BUSOBJ)/wangpc/lic.o @@ -565,6 +594,7 @@ endif #------------------------------------------------- ifneq ($(filter Z88,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/z88 BUSOBJS += $(BUSOBJ)/z88/z88.o BUSOBJS += $(BUSOBJ)/z88/flash.o BUSOBJS += $(BUSOBJ)/z88/ram.o @@ -577,6 +607,7 @@ endif #------------------------------------------------- ifneq ($(filter A2BUS,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/a2bus BUSOBJS += $(BUSOBJ)/a2bus/a2bus.o BUSOBJS += $(BUSOBJ)/a2bus/a2lang.o BUSOBJS += $(BUSOBJ)/a2bus/a2diskii.o @@ -614,6 +645,7 @@ endif #------------------------------------------------- ifneq ($(filter NUBUS,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/nubus BUSOBJS += $(BUSOBJ)/nubus/nubus.o BUSOBJS += $(BUSOBJ)/nubus/nubus_48gc.o BUSOBJS += $(BUSOBJ)/nubus/nubus_cb264.o @@ -639,6 +671,7 @@ endif #------------------------------------------------- ifneq ($(filter CENTRONICS,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/centronics BUSOBJS += $(BUSOBJ)/centronics/ctronics.o BUSOBJS += $(BUSOBJ)/centronics/comxpl80.o BUSOBJS += $(BUSOBJ)/centronics/covox.o @@ -652,6 +685,7 @@ endif #------------------------------------------------- ifneq ($(filter RS232,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/rs232 BUSOBJS += $(BUSOBJ)/rs232/keyboard.o BUSOBJS += $(BUSOBJ)/rs232/null_modem.o BUSOBJS += $(BUSOBJ)/rs232/rs232.o @@ -665,6 +699,7 @@ endif #------------------------------------------------- ifneq ($(filter MIDI,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/midi BUSOBJS += $(BUSOBJ)/midi/midi.o BUSOBJS += $(BUSOBJ)/midi/midiinport.o BUSOBJS += $(BUSOBJ)/midi/midioutport.o @@ -676,6 +711,7 @@ endif #------------------------------------------------- ifneq ($(filter PCI,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/pci BUSOBJS += $(BUSOBJ)/pci/pci.o BUSOBJS += $(BUSOBJ)/pci/i82371ab.o BUSOBJS += $(BUSOBJ)/pci/i82371sb.o @@ -691,6 +727,7 @@ endif #------------------------------------------------- ifneq ($(filter NES,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/nes BUSOBJS += $(BUSOBJ)/nes/nes_slot.o BUSOBJS += $(BUSOBJ)/nes/nes_carts.o BUSOBJS += $(BUSOBJ)/nes/act53.o @@ -747,6 +784,7 @@ endif #------------------------------------------------- ifneq ($(filter SNES,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/snes BUSOBJS += $(BUSOBJ)/snes/snes_slot.o BUSOBJS += $(BUSOBJ)/snes/snes_carts.o BUSOBJS += $(BUSOBJ)/snes/bsx.o @@ -761,3 +799,95 @@ BUSOBJS += $(BUSOBJ)/snes/spc7110.o BUSOBJS += $(BUSOBJ)/snes/sufami.o BUSOBJS += $(BUSOBJ)/snes/upd.o endif + +#------------------------------------------------- +# +#@src/emu/bus/megadrive/md_slot.h,BUSES += MEGADRIVE +#------------------------------------------------- + +ifneq ($(filter MEGADRIVE,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/megadrive +BUSOBJS += $(BUSOBJ)/megadrive/md_slot.o +BUSOBJS += $(BUSOBJ)/megadrive/md_carts.o +BUSOBJS += $(BUSOBJ)/megadrive/eeprom.o +BUSOBJS += $(BUSOBJ)/megadrive/jcart.o +BUSOBJS += $(BUSOBJ)/megadrive/rom.o +BUSOBJS += $(BUSOBJ)/megadrive/sk.o +BUSOBJS += $(BUSOBJ)/megadrive/stm95.o +BUSOBJS += $(BUSOBJ)/megadrive/svp.o +endif + +#------------------------------------------------- +# +#@src/emu/bus/saturn/sat_slot.h,BUSES += SATURN +#------------------------------------------------- + +ifneq ($(filter SATURN,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/saturn +BUSOBJS += $(BUSOBJ)/saturn/sat_slot.o +BUSOBJS += $(BUSOBJ)/saturn/bram.o +BUSOBJS += $(BUSOBJ)/saturn/dram.o +BUSOBJS += $(BUSOBJ)/saturn/rom.o +endif + +#------------------------------------------------- +# +#@src/emu/bus/sega8/sega8_slot.h,BUSES += SEGA8 +#------------------------------------------------- + +ifneq ($(filter SEGA8,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/sega8 +BUSOBJS += $(BUSOBJ)/sega8/sega8_slot.o +BUSOBJS += $(BUSOBJ)/sega8/rom.o +endif + +#------------------------------------------------- +# +#@src/emu/bus/sms_ctrl/smsctrl.h,BUSES += SMS_CTRL +#------------------------------------------------- + +ifneq ($(filter SMS_CTRL,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/sms_ctrl +BUSOBJS += $(BUSOBJ)/sms_ctrl/smsctrl.o +BUSOBJS += $(BUSOBJ)/sms_ctrl/joypad.o +BUSOBJS += $(BUSOBJ)/sms_ctrl/lphaser.o +BUSOBJS += $(BUSOBJ)/sms_ctrl/paddle.o +BUSOBJS += $(BUSOBJ)/sms_ctrl/rfu.o +BUSOBJS += $(BUSOBJ)/sms_ctrl/sports.o +BUSOBJS += $(BUSOBJ)/sms_ctrl/sportsjp.o +endif + +#------------------------------------------------- +# +#@src/emu/bus/sms_exp/smsexp.h,BUSES += SMS_EXP +#------------------------------------------------- + +ifneq ($(filter SMS_EXP,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/sms_exp +BUSOBJS += $(BUSOBJ)/sms_exp/smsexp.o +BUSOBJS += $(BUSOBJ)/sms_exp/gender.o +endif + +#------------------------------------------------- +# +#@src/emu/bus/gameboy/gb_slot.h,BUSES += GAMEBOY +#------------------------------------------------- + +ifneq ($(filter GAMEBOY,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/gameboy +BUSOBJS += $(BUSOBJ)/gameboy/gb_slot.o +BUSOBJS += $(BUSOBJ)/gameboy/rom.o +BUSOBJS += $(BUSOBJ)/gameboy/mbc.o +endif + +#------------------------------------------------- +# +#@src/emu/bus/gba/gba_slot.h,BUSES += GBA +#------------------------------------------------- + +ifneq ($(filter GBA,$(BUSES)),) +OBJDIRS += $(BUSOBJ)/gba +BUSOBJS += $(BUSOBJ)/gba/gba_slot.o +BUSOBJS += $(BUSOBJ)/gba/rom.o +endif + diff --git a/src/emu/bus/gameboy/gb_slot.c b/src/emu/bus/gameboy/gb_slot.c new file mode 100644 index 00000000000..8646440a0f3 --- /dev/null +++ b/src/emu/bus/gameboy/gb_slot.c @@ -0,0 +1,831 @@ +/*********************************************************************************************************** + + + Game Boy cart emulation + (through slot devices) + + + The driver exposes address ranges + 0x0000-0x7fff to read_rom/write_bank + 0xa000-0xbfff to read_ram/write_ram (typically RAM/NVRAM accesses, but megaduck uses the write for bankswitch) + + currently available slot devices: + gb_rom: standard carts + TAMA5 mapper + pirate carts with protection & bankswitch + gb_mbc: MBC1-MBC7 carts (more complex bankswitch + RAM + possibly RTC/Rumble/etc.) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "gb_slot.h" + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type GB_CART_SLOT = &device_creator<gb_cart_slot_device>; +const device_type MEGADUCK_CART_SLOT = &device_creator<megaduck_cart_slot_device>; + +//************************************************************************** +// GB cartridges Interface +//************************************************************************** + +//------------------------------------------------- +// device_gb_cart_interface - constructor +//------------------------------------------------- + +device_gb_cart_interface::device_gb_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL), + m_ram(NULL), + m_rom_size(0), + m_ram_size(0), + has_rumble(FALSE), + has_timer(FALSE), + has_battery(FALSE) +{ +} + + +//------------------------------------------------- +// ~device_gb_cart_interface - destructor +//------------------------------------------------- + +device_gb_cart_interface::~device_gb_cart_interface() +{ +} + +//------------------------------------------------- +// rom_alloc - alloc the space for the cart +//------------------------------------------------- + +void device_gb_cart_interface::rom_alloc(running_machine &machine, UINT32 size) +{ + if (m_rom == NULL) + { + m_rom = auto_alloc_array_clear(machine, UINT8, size); + m_rom_size = size; + } +} + + +//------------------------------------------------- +// ram_alloc - alloc the space for the ram +//------------------------------------------------- + +void device_gb_cart_interface::ram_alloc(running_machine &machine, UINT32 size) +{ + if (m_ram == NULL) + { + m_ram = auto_alloc_array_clear(machine, UINT8, size); + m_ram_size = size; + state_save_register_item_pointer(machine, "GB_CART", this->device().tag(), 0, m_ram, m_ram_size); + } +} + + +//------------------------------------------------- +// rom_map_setup - setup map of rom banks in 16K +// blocks, so to simplify ROM access +//------------------------------------------------- + +void device_gb_cart_interface::rom_map_setup(UINT32 size) +{ + int i; + // setup the rom_bank_map array to faster ROM read + for (i = 0; i < size / 0x4000; i++) + rom_bank_map[i] = i; + + // fill up remaining blocks with mirrors + while (i % 512) + { + int j = 0, repeat_banks; + while ((i % (512 >> j)) && j < 9) + j++; + repeat_banks = i % (512 >> (j - 1)); + for (int k = 0; k < repeat_banks; k++) + rom_bank_map[i + k] = rom_bank_map[i + k - repeat_banks]; + i += repeat_banks; + } + +// check bank map! +// for (i = 0; i < 256; i++) +// { +// printf("bank %3d = %3d\t", i, rom_bank_map[i]); +// if ((i%8) == 7) +// printf("\n"); +// } +} + +//------------------------------------------------- +// ram_map_setup - setup map of ram banks in 16K +// blocks, so to simplify ROM access +//------------------------------------------------- + +void device_gb_cart_interface::ram_map_setup(UINT8 banks) +{ + int mask = banks - 1; + + for (int i = 0; i < banks; i++) + ram_bank_map[i] = i; + + // Set up rest of the (mirrored) RAM pages + for (int i = banks; i < 256; i++) + ram_bank_map[i] = i & mask; +} + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// base_gb_cart_slot_device - constructor +//------------------------------------------------- +base_gb_cart_slot_device::base_gb_cart_slot_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : + device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this), + m_sgb_hack(0), + m_type(GB_MBC_UNKNOWN) +{ +} + +gb_cart_slot_device::gb_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + base_gb_cart_slot_device(mconfig, GB_CART_SLOT, "Game Boy Cartridge Slot", tag, owner, clock, "gb_cart_slot", __FILE__) +{ +} + +megaduck_cart_slot_device::megaduck_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + base_gb_cart_slot_device(mconfig, MEGADUCK_CART_SLOT, "Megaduck Cartridge Slot", tag, owner, clock, "megaduck_cart_slot", __FILE__) +{ +} + +//------------------------------------------------- +// base_gb_cart_slot_device - destructor +//------------------------------------------------- + +base_gb_cart_slot_device::~base_gb_cart_slot_device() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void base_gb_cart_slot_device::device_start() +{ + m_cart = dynamic_cast<device_gb_cart_interface *>(get_card_device()); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void base_gb_cart_slot_device::device_config_complete() +{ + // set brief and instance name + update_names(); +} + + +//------------------------------------------------- +// GB PCB +//------------------------------------------------- + + +struct gb_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 gb_slot slot_list[] = +{ + { GB_MBC_MBC1, "rom_mbc1" }, + { GB_MBC_MBC1_COL, "rom_mbc1col" }, + { GB_MBC_MBC2, "rom_mbc2" }, + { GB_MBC_MBC3, "rom_mbc3" }, + { GB_MBC_MBC5, "rom_mbc5" }, + { GB_MBC_MBC6, "rom_mbc6" }, + { GB_MBC_MBC7, "rom_mbc7" }, + { GB_MBC_TAMA5, "rom_tama5" }, + { GB_MBC_MMM01, "rom_mmm01" }, + { GB_MBC_MBC3, "rom_huc1" }, // for now treat this as alias for MBC3 + { GB_MBC_MBC3, "rom_huc3" }, // for now treat this as alias for MBC3 + { GB_MBC_WISDOM, "rom_wisdom" }, + { GB_MBC_YONGYONG, "rom_yong" }, + { GB_MBC_LASAMA, "rom_lasama" }, + { GB_MBC_ATVRACIN, "rom_atvrac" }, + { GB_MBC_SINTAX, "rom_sintax" }, + { GB_MBC_CHONGWU, "rom_chong" }, + { GB_MBC_DIGIMON, "rom_digimon" }, + { GB_MBC_ROCKMAN8, "rom_rock8" }, + { GB_MBC_SM3SP, "rom_sm3sp" }, + { GB_MBC_UNK01, "rom_unk01" }, + { GB_MBC_DKONG5, "rom_dkong5" }, + { GB_MBC_CAMERA, "rom_camera" } +}; + +static int gb_get_pcb_id(const char *slot) +{ + for (int i = 0; i < ARRAY_LENGTH(slot_list); i++) + { + if (!mame_stricmp(slot_list[i].slot_option, slot)) + return slot_list[i].pcb_id; + } + + return 0; +} + +static const char *gb_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 "rom"; +} + + +/*------------------------------------------------- + call load + -------------------------------------------------*/ + + +bool base_gb_cart_slot_device::call_load() +{ + if (m_cart) + { + UINT32 offset = 0; + UINT32 len = (software_entry() == NULL) ? length() : get_software_region_length("rom"); + UINT8 *ROM; + int rambanks = 0; + + // From fullpath, check for presence of a header and skip it + check filesize is valid + if (software_entry() == NULL) + { + if ((len % 0x4000) == 512) + { + logerror("Rom-header found, skipping\n"); + offset = 512; + len -= offset; + fseek(offset, SEEK_SET); + } + /* Verify that the file contains 16kb blocks */ + if ((len == 0) || ((len % 0x4000) != 0)) + { + seterror(IMAGE_ERROR_UNSPECIFIED, "Invalid rom file size\n"); + return IMAGE_INIT_FAIL; + } + } + + m_cart->rom_alloc(machine(), len); + ROM = m_cart->get_rom_base(); + + if (software_entry() == NULL) + fread(ROM, len); + else + memcpy(ROM, get_software_region("rom"), len); + + // determine cart type + offset = 0; + if (get_mmm01_candidate(ROM, len)) + offset = len - 0x8000; + int type; + + if (software_entry() != NULL) + type = gb_get_pcb_id(get_feature("slot") ? get_feature("slot") : "rom"); + else + type = get_cart_type(ROM + offset, len - offset); + + + // setup RAM/NVRAM/RTC/RUMBLE + if (software_entry() != NULL) + { + // from softlist we only rely on xml + if (get_software_region("ram")) + rambanks = get_software_region_length("ram") / 0x2000; + + if (get_software_region("nvram")) + { + m_cart->set_has_battery(TRUE); + rambanks = get_software_region_length("nvram") / 0x2000; + } + + if (get_feature("rumble")) + { + if (!mame_stricmp(get_feature("rumble"), "yes")) + m_cart->set_has_rumble(TRUE); + } + + if (get_feature("rtc")) + { + if (!mame_stricmp(get_feature("rtc"), "yes")) + m_cart->set_has_timer(TRUE); + } + } + else + { + // from fullpath we rely on header + switch (ROM[0x0147 + offset]) + { + case 0x03: case 0x06: case 0x09: case 0x0d: case 0x13: case 0x17: case 0x1b: case 0x22: + m_cart->set_has_battery(TRUE); + break; + + case 0x0f: case 0x10: + m_cart->set_has_battery(TRUE); + m_cart->set_has_timer(TRUE); + break; + + case 0x1c: case 0x1d: + m_cart->set_has_rumble(TRUE); + break; + + case 0x1e: + m_cart->set_has_battery(TRUE); + m_cart->set_has_rumble(TRUE); + break; + } + + switch (ROM[0x0149 + offset] & 0x07) + { + case 0x00: case 0x06: case 0x07: + rambanks = 0; + break; + case 0x01: case 0x02: + rambanks = 1; + break; + case 0x03: + rambanks = 4; + break; + case 0x04: + rambanks = 16; + break; + case 0x05: + default: + rambanks = 8; + break; + } + + if (type == GB_MBC_MBC2 || type == GB_MBC_MBC7) + rambanks = 1; + } + + // setup rom bank map based on real length, not header value + m_cart->rom_map_setup(len); + + if (rambanks) + setup_ram(rambanks); + + if (m_cart->get_ram_size() && m_cart->get_has_battery()) + battery_load(m_cart->get_ram_base(), m_cart->get_ram_size(), 0xff); + + //printf("Type: %s\n", gb_get_slot(type)); + + internal_header_logging(ROM + offset, len); + + // Hack to support Donkey Kong Land 2 + 3 in SGB + // For some reason, these store the tile data differently. Hacks will go once it's been figured out + if (strncmp((const char*)(ROM + 0x134), "DONKEYKONGLAND 2", 16) == 0 || + strncmp((const char*)(ROM + 0x134), "DONKEYKONGLAND 3", 16) == 0) + m_sgb_hack = 1; + + return IMAGE_INIT_PASS; + } + + return IMAGE_INIT_PASS; +} + +bool megaduck_cart_slot_device::call_load() +{ + if (m_cart) + { + UINT32 len = (software_entry() == NULL) ? length() : get_software_region_length("rom"); + UINT8 *ROM; + + m_cart->rom_alloc(machine(), len); + ROM = m_cart->get_rom_base(); + + if (software_entry() == NULL) + fread(ROM, len); + else + memcpy(ROM, get_software_region("rom"), len); + + // setup rom bank map based on real length, not header value + m_cart->rom_map_setup(len); + + return IMAGE_INIT_PASS; + } + + return IMAGE_INIT_PASS; +} + + +/*------------------------------------------------- + call_unload + -------------------------------------------------*/ + +void base_gb_cart_slot_device::call_unload() +{ + if (m_cart && m_cart->get_ram_size() && m_cart->get_has_battery()) + battery_save(m_cart->get_ram_base(), m_cart->get_ram_size()); +} + +void base_gb_cart_slot_device::setup_ram(UINT8 banks) +{ + m_cart->ram_alloc(machine(), banks * 0x2000); + memset(m_cart->get_ram_base(), 0xff, m_cart->get_ram_size()); + m_cart->ram_map_setup(banks); +} + + + +/*------------------------------------------------- + call softlist load + -------------------------------------------------*/ + +bool base_gb_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; +} + +// This fails to catch Mani 4-in-1 carts... even when they match this, then they have MBC1/3 in the internal header instead of MMM01... +bool base_gb_cart_slot_device::get_mmm01_candidate(UINT8 *ROM, UINT32 len) +{ + if (len < 0x8147) + return FALSE; + + static const UINT8 nintendo_logo[0x18] = { + 0xCE, 0xED, 0x66, 0x66, 0xCC, 0x0D, 0x00, 0x0B, + 0x03, 0x73, 0x00, 0x83, 0x00, 0x0C, 0x00, 0x0D, + 0x00, 0x08, 0x11, 0x1F, 0x88, 0x89, 0x00, 0x0E + }; + int bytes_matched = 0; + for (int i = 0; i < 0x18; i++) + { + if (ROM[(len - 0x8000) + 0x104 + i] == nintendo_logo[i]) + bytes_matched++; + } + + if (bytes_matched == 0x18 && ROM[(len - 0x8000) + 0x147] >= 0x0b && ROM[(len - 0x8000) + 0x147] <= 0x0d) + return TRUE; + else + return FALSE; +} + +int base_gb_cart_slot_device::get_cart_type(UINT8 *ROM, UINT32 len) +{ + int type = GB_MBC_NONE; + + if (len < 0x014c) + fatalerror("Checking header of a corrupted image!\n"); + + switch(ROM[0x0147]) + { + case 0x00: type = GB_MBC_NONE; break; + case 0x01: type = GB_MBC_MBC1; break; + case 0x02: type = GB_MBC_MBC1; break; + case 0x03: type = GB_MBC_MBC1; break; + case 0x05: type = GB_MBC_MBC2; break; + case 0x06: type = GB_MBC_MBC2; break; + case 0x08: type = GB_MBC_NONE; break; + case 0x09: type = GB_MBC_NONE; break; + case 0x0b: type = GB_MBC_MMM01; break; + case 0x0c: type = GB_MBC_MMM01; break; + case 0x0d: type = GB_MBC_MMM01; break; + case 0x0f: type = GB_MBC_MBC3; break; + case 0x10: type = GB_MBC_MBC3; break; + case 0x11: type = GB_MBC_MBC3; break; + case 0x12: type = GB_MBC_MBC3; break; + case 0x13: type = GB_MBC_MBC3; break; + case 0x15: type = GB_MBC_MBC4; break; + case 0x16: type = GB_MBC_MBC4; break; + case 0x17: type = GB_MBC_MBC4; break; + case 0x19: type = GB_MBC_MBC5; break; + case 0x1a: type = GB_MBC_MBC5; break; + case 0x1b: type = GB_MBC_MBC5; break; + case 0x1c: type = GB_MBC_MBC5; break; + case 0x1d: type = GB_MBC_MBC5; break; + case 0x1e: type = GB_MBC_MBC5; break; + case 0x20: type = GB_MBC_MBC6; break; + case 0x22: type = GB_MBC_MBC7; break; + case 0xbe: type = GB_MBC_NONE; break; /* used in Flash2Advance GB Bridge boot program */ + case 0xea: type = GB_MBC_YONGYONG; break; /* Found in Sonic 3D Blast 5 pirate */ + case 0xfc: type = GB_MBC_CAMERA; break; + case 0xfd: type = GB_MBC_TAMA5; break; + case 0xfe: type = GB_MBC_HUC3; break; + case 0xff: type = GB_MBC_HUC1; break; + } + + // Check for special mappers + if (type == GB_MBC_NONE) + { + int count = 0; + for (int i = 0x0134; i <= 0x014c; i++) + { + count += ROM[i]; + } + if (count == 0) + { + type = GB_MBC_WISDOM; + } + } + + // Check for some unlicensed games + if (type == GB_MBC_MBC5) + { + int count = 0; + for (int i = 0x0184; i < 0x0184 + 0x30; i++) + { + count += ROM[i]; + } + if (count == 4876) + { +// printf("Niutoude!\n"); +// type = GB_MBC_NIUTOUDE; + } + if (count == 4138 || count == 4125) + { + // Zhi Huan Wang uses 4138 + // most sintax use 4125 + printf("Sintax %d!\n", count); + type = GB_MBC_SINTAX; + } + } + + /* Check if we're dealing with the multigame variant of the MBC1 mapper */ + if (type == GB_MBC_MBC1) + { + if (ROM[0x13f] == 0x42 && ROM[0x140] == 0x32 && ROM[0x141] == 0x43 && ROM[0x142] == 0x4B) + type = GB_MBC_MBC1_COL; + } + + return type; +} +/*------------------------------------------------- + get default card software + -------------------------------------------------*/ + +const char * base_gb_cart_slot_device::get_default_card_software(const machine_config &config, emu_options &options) +{ + if (open_image_file(options)) + { + const char *slot_string = "rom"; + UINT32 len = core_fsize(m_file), offset = 0; + UINT8 *ROM = global_alloc_array(UINT8, len); + int type; + + core_fread(m_file, ROM, len); + + if ((len % 0x4000) == 512) + offset = 512; + + if (get_mmm01_candidate(ROM + offset, len - offset)) + offset += (len - 0x8000); + + type = get_cart_type(ROM + offset, len - offset); + slot_string = gb_get_slot(type); + + //printf("type: %s\n", slot_string); + global_free(ROM); + clear(); + + return slot_string; + } + + return software_get_default_slot(config, options, this, "rom"); +} + + +const char * megaduck_cart_slot_device::get_default_card_software(const machine_config &config, emu_options &options) +{ + if (open_image_file(options)) + return "rom"; + + return software_get_default_slot(config, options, this, "rom"); +} + + + +/*------------------------------------------------- + read + -------------------------------------------------*/ + +READ8_MEMBER(base_gb_cart_slot_device::read_rom) +{ + if (m_cart) + return m_cart->read_rom(space, offset); + else + return 0xff; +} + +READ8_MEMBER(base_gb_cart_slot_device::read_ram) +{ + if (m_cart) + return m_cart->read_ram(space, offset); + else + return 0xff; +} + + +/*------------------------------------------------- + write + -------------------------------------------------*/ + +WRITE8_MEMBER(base_gb_cart_slot_device::write_bank) +{ + if (m_cart) + m_cart->write_bank(space, offset, data); +} + +WRITE8_MEMBER(base_gb_cart_slot_device::write_ram) +{ + if (m_cart) + m_cart->write_ram(space, offset, data); +} + + +/*------------------------------------------------- + Internal header logging + -------------------------------------------------*/ + +void base_gb_cart_slot_device::internal_header_logging(UINT8 *ROM, UINT32 len) +{ + static const char *const cart_types[] = + { + "ROM ONLY", "ROM+MBC1", "ROM+MBC1+RAM", + "ROM+MBC1+RAM+BATTERY", "UNKNOWN", "ROM+MBC2", + "ROM+MBC2+BATTERY", "UNKNOWN", "ROM+RAM", + "ROM+RAM+BATTERY", "UNKNOWN", "ROM+MMM01", + "ROM+MMM01+SRAM", "ROM+MMM01+SRAM+BATTERY", "UNKNOWN", + "ROM+MBC3+TIMER+BATTERY", "ROM+MBC3+TIMER+RAM+BATTERY", "ROM+MBC3", + "ROM+MBC3+RAM", "ROM+MBC3+RAM+BATTERY", "UNKNOWN", + "UNKNOWN", "UNKNOWN", "UNKNOWN", + "UNKNOWN", "ROM+MBC5", "ROM+MBC5+RAM", + "ROM+MBC5+RAM+BATTERY", "ROM+MBC5+RUMBLE", "ROM+MBC5+RUMBLE+SRAM", + "ROM+MBC5+RUMBLE+SRAM+BATTERY", "Pocket Camera", "Bandai TAMA5", + /* Need heaps of unknowns here */ + "Hudson HuC-3", "Hudson HuC-1" + }; + + // some company codes + static const struct + { + UINT16 code; + const char *name; + } + companies[] = + { + {0x3301, "Nintendo"}, + {0x7901, "Accolade"}, + {0xA400, "Konami"}, + {0x6701, "Ocean"}, + {0x5601, "LJN"}, + {0x9900, "ARC?"}, + {0x0101, "Nintendo"}, + {0x0801, "Capcom"}, + {0x0100, "Nintendo"}, + {0xBB01, "SunSoft"}, + {0xA401, "Konami"}, + {0xAF01, "Namcot?"}, + {0x4901, "Irem"}, + {0x9C01, "Imagineer"}, + {0xA600, "Kawada?"}, + {0xB101, "Nexoft"}, + {0x5101, "Acclaim"}, + {0x6001, "Titus"}, + {0xB601, "HAL"}, + {0x3300, "Nintendo"}, + {0x0B00, "Coconuts?"}, + {0x5401, "Gametek"}, + {0x7F01, "Kemco?"}, + {0xC001, "Taito"}, + {0xEB01, "Atlus"}, + {0xE800, "Asmik?"}, + {0xDA00, "Tomy?"}, + {0xB100, "ASCII?"}, + {0xEB00, "Atlus"}, + {0xC000, "Taito"}, + {0x9C00, "Imagineer"}, + {0xC201, "Kemco?"}, + {0xD101, "Sofel?"}, + {0x6101, "Virgin"}, + {0xBB00, "SunSoft"}, + {0xCE01, "FCI?"}, + {0xB400, "Enix?"}, + {0xBD01, "Imagesoft"}, + {0x0A01, "Jaleco?"}, + {0xDF00, "Altron?"}, + {0xA700, "Takara?"}, + {0xEE00, "IGS?"}, + {0x8300, "Lozc?"}, + {0x5001, "Absolute?"}, + {0xDD00, "NCS?"}, + {0xE500, "Epoch?"}, + {0xCB00, "VAP?"}, + {0x8C00, "Vic Tokai"}, + {0xC200, "Kemco?"}, + {0xBF00, "Sammy?"}, + {0x1800, "Hudson Soft"}, + {0xCA01, "Palcom/Ultra"}, + {0xCA00, "Palcom/Ultra"}, + {0xC500, "Data East?"}, + {0xA900, "Technos Japan?"}, + {0xD900, "Banpresto?"}, + {0x7201, "Broderbund?"}, + {0x7A01, "Triffix Entertainment?"}, + {0xE100, "Towachiki?"}, + {0x9300, "Tsuburava?"}, + {0xC600, "Tonkin House?"}, + {0xCE00, "Pony Canyon"}, + {0x7001, "Infogrames?"}, + {0x8B01, "Bullet-Proof Software?"}, + {0x5501, "Park Place?"}, + {0xEA00, "King Records?"}, + {0x5D01, "Tradewest?"}, + {0x6F01, "ElectroBrain?"}, + {0xAA01, "Broderbund?"}, + {0xC301, "SquareSoft"}, + {0x5201, "Activision?"}, + {0x5A01, "Bitmap Brothers/Mindscape"}, + {0x5301, "American Sammy"}, + {0x4701, "Spectrum Holobyte"}, + {0x1801, "Hudson Soft"}, + {0x0000, NULL} + }; + static const int ramsize[8] = { 0, 2, 8, 32, 128, 64, 0, 0 }; + + char soft[17]; + UINT32 tmp = 0; + int csum = 0, i = 0; + int rom_banks; + + switch (ROM[0x0148]) + { + case 0x52: + rom_banks = 72; + break; + case 0x53: + rom_banks = 80; + break; + case 0x54: + rom_banks = 96; + break; + case 0x00: case 0x01: case 0x02: case 0x03: + case 0x04: case 0x05: case 0x06: case 0x07: + rom_banks = 2 << ROM[0x0148]; + break; + default: + rom_banks = 256; + break; + } + + strncpy(soft, (char *)&ROM[0x0134], 16); + soft[16] = '\0'; + logerror("Cart Information\n"); + logerror("\tName: %s\n", soft); + logerror("\tType: %s [0x%2X]\n", (ROM[0x0147] <= 32) ? cart_types[ROM[0x0147]] : "", ROM[0x0147] ); + logerror("\tGame Boy: %s\n", (ROM[0x0143] == 0xc0) ? "No" : "Yes" ); + logerror("\tSuper GB: %s [0x%2X]\n", (ROM[0x0146] == 0x03) ? "Yes" : "No", ROM[0x0146] ); + logerror("\tColor GB: %s [0x%2X]\n", (ROM[0x0143] == 0x80 || ROM[0x0143] == 0xc0) ? "Yes" : "No", ROM[0x0143] ); + logerror("\tROM Size: %d 16kB Banks [0x%2X]\n", rom_banks, ROM[0x0148]); + logerror("\tRAM Size: %d kB [0x%2X]\n", ramsize[ROM[0x0149] & 0x07], ROM[0x0149]); + logerror("\tLicense code: 0x%2X%2X\n", ROM[0x0145], ROM[0x0144] ); + tmp = (ROM[0x014b] << 8) + ROM[0x014a]; + for (i = 0; i < ARRAY_LENGTH(companies); i++) + if (tmp == companies[i].code) + break; + logerror("\tManufacturer ID: 0x%2X", tmp); + logerror(" [%s]\n", (i < ARRAY_LENGTH(companies)) ? companies[i].name : "?"); + logerror("\tVersion Number: 0x%2X\n", ROM[0x014c]); + logerror("\tComplement Check: 0x%2X\n", ROM[0x014d]); + logerror("\tChecksum: 0x%2X\n", ((ROM[0x014e] << 8) + ROM[0x014f])); + tmp = (ROM[0x0103] << 8) + ROM[0x0102]; + logerror("\tStart Address: 0x%2X\n", tmp); + + // Additional checks + if (rom_banks == 256) + logerror("\nWarning loading cartridge: Unknown ROM size in header [0x%x].\n", ROM[0x0148]); + + if ((len / 0x4000) != rom_banks) + logerror("\nWarning loading cartridge: Filesize (0x%x) and reported ROM banks (0x%x) don't match.\n", + len, rom_banks * 0x4000); + /* Calculate and check checksum */ + tmp = (ROM[0x014e] << 8) + ROM[0x014f]; + for (int i = 0; i < len; i++) + csum += ROM[i]; + csum -= (ROM[0x014e] + ROM[0x014f]); + csum &= 0xffff; + + if (csum != tmp) + logerror("\nWarning loading cartridge: Checksum is wrong (Actual %X vs Internal %X)\n", csum, tmp); + +} diff --git a/src/emu/bus/gameboy/gb_slot.h b/src/emu/bus/gameboy/gb_slot.h new file mode 100644 index 00000000000..8c9049f68d5 --- /dev/null +++ b/src/emu/bus/gameboy/gb_slot.h @@ -0,0 +1,207 @@ +#ifndef __GB_SLOT_H +#define __GB_SLOT_H + +/*************************************************************************** + TYPE DEFINITIONS + ***************************************************************************/ + + +/* PCB */ +enum +{ + GB_MBC_NONE = 0, /* 32KB ROM - No memory bank controller */ + GB_MBC_MBC1, /* ~2MB ROM, 8KB RAM -or- 512KB ROM, 32KB RAM */ + GB_MBC_MBC2, /* 256KB ROM, 32KB RAM */ + GB_MBC_MMM01, /* ?? ROM, ?? RAM */ + GB_MBC_MBC3, /* 2MB ROM, 32KB RAM, RTC */ + GB_MBC_MBC4, /* ?? ROM, ?? RAM */ + GB_MBC_MBC5, /* 8MB ROM, 128KB RAM (32KB w/ Rumble) */ + GB_MBC_TAMA5, /* ?? ROM ?? RAM - What is this? */ + GB_MBC_HUC1, /* ?? ROM, ?? RAM - Hudson Soft Controller */ + GB_MBC_HUC3, /* ?? ROM, ?? RAM - Hudson Soft Controller */ + GB_MBC_MBC6, /* ?? ROM, 32KB SRAM */ + GB_MBC_MBC7, /* ?? ROM, ?? RAM */ + GB_MBC_WISDOM, /* ?? ROM, ?? RAM - Wisdom tree controller */ + GB_MBC_MBC1_COL, /* 1MB ROM, ?? RAM - MBC1 variant for multigame carts */ + GB_MBC_YONGYONG, /* ?? ROM, ?? RAM - Appears in Sonic 3D Blast 5 pirate */ + GB_MBC_LASAMA, /* ?? ROM, ?? RAM - Appears in La Sa Ma */ + GB_MBC_ATVRACIN, + GB_MBC_CAMERA, + GB_MBC_SINTAX, + GB_MBC_CHONGWU, + GB_MBC_DIGIMON, + GB_MBC_ROCKMAN8, + GB_MBC_SM3SP, + GB_MBC_DKONG5, + GB_MBC_UNK01, + GB_MBC_MEGADUCK, /* MEGADUCK style banking */ + GB_MBC_UNKNOWN /* Unknown mapper */ +}; + + +// ======================> gb_cart_interface + +struct gb_cart_interface +{ +}; + + +// ======================> device_gb_cart_interface + +class device_gb_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_gb_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~device_gb_cart_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom) { return 0xff; } + virtual DECLARE_WRITE8_MEMBER(write_bank) {} + virtual DECLARE_READ8_MEMBER(read_ram) { return 0xff; } + virtual DECLARE_WRITE8_MEMBER(write_ram) {} + + void rom_alloc(running_machine &machine, UINT32 size); + void ram_alloc(running_machine &machine, UINT32 size); + UINT8* get_rom_base() { return m_rom; } + UINT8* get_ram_base() { return m_ram; } + UINT32 get_rom_size() { return m_rom_size; } + UINT32 get_ram_size() { return m_ram_size; } + + void rom_map_setup(UINT32 size); + void ram_map_setup(UINT8 banks); + + void set_has_timer(bool val) { has_timer = val; } + void set_has_rumble(bool val) { has_rumble = val; } + void set_has_battery(bool val) { has_battery = val; } + bool get_has_battery() { return has_battery; } + + // internal state + UINT8 *m_rom; + UINT8 *m_ram; + UINT32 m_rom_size; + UINT32 m_ram_size; + + // bankswitch variables + // we access ROM/RAM banks through these bank maps + // default accesses are: + // 0x0000-0x3fff = rom_bank_map[m_latch_bank] (generally defaults to m_latch_bank = 0) + // 0x4000-0x7fff = rom_bank_map[m_latch_bank2] (generally defaults to m_latch_bank2 = 1) + // 0xa000-0xbfff = ram_bank_map[m_ram_bank] (generally defaults to m_ram_bank = 0) + // suitable writes to 0x0000-0x7fff can then modify m_latch_bank/m_latch_bank2 + UINT8 rom_bank_map[512]; // 16K chunks of ROM + UINT8 ram_bank_map[256]; // 16K chunks of RAM + UINT8 m_ram_bank; + UINT16 m_latch_bank, m_latch_bank2; + + bool has_rumble, has_timer, has_battery; +}; + + +// ======================> base_gb_cart_slot_device + +class base_gb_cart_slot_device : public device_t, + public gb_cart_interface, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + base_gb_cart_slot_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + virtual ~base_gb_cart_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete(); + + // image-level overrides + virtual bool call_load(); + virtual void call_unload(); + virtual bool call_softlist_load(char *swlist, char *swname, rom_entry *start_entry); + + int get_type() { return m_type; } + int get_cart_type(UINT8 *ROM, UINT32 len); + bool get_mmm01_candidate(UINT8 *ROM, UINT32 len); + // remove me when SGB is properly emulated + int get_sgb_hack() { return m_sgb_hack; } + + void setup_ram(UINT8 banks); + void internal_header_logging(UINT8 *ROM, UINT32 len); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 1; } + virtual const option_guide *create_option_guide() const { return NULL; } + virtual const char *image_interface() const { return "gameboy_cart"; } + virtual const char *file_extensions() const { return "bin,gb,gbc"; } + + // slot interface overrides + virtual const char * get_default_card_software(const machine_config &config, emu_options &options); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + + +protected: + // Donkey Kong Land 2 + 3 store SGB border tiles differently... this will be hopefully be removed when SGB is properly emulated! + int m_sgb_hack; + + int m_type; + device_gb_cart_interface* m_cart; +}; + +// ======================> gb_cart_slot_device + +class gb_cart_slot_device : public base_gb_cart_slot_device +{ +public: + // construction/destruction + gb_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + + +// ======================> megaduck_cart_slot_device + +class megaduck_cart_slot_device : public base_gb_cart_slot_device +{ +public: + // construction/destruction + megaduck_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // image-level overrides + virtual bool call_load(); + virtual const char *image_interface() const { return "megaduck_cart"; } + virtual const char *file_extensions() const { return "bin"; } + + // slot interface overrides + virtual const char * get_default_card_software(const machine_config &config, emu_options &options); +}; + + + + +// device type definition +extern const device_type GB_CART_SLOT; +extern const device_type MEGADUCK_CART_SLOT; + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS + ***************************************************************************/ + +#define MCFG_GB_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, GB_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + +#define MCFG_MEGADUCK_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, MEGADUCK_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + + +#endif diff --git a/src/emu/bus/gameboy/mbc.c b/src/emu/bus/gameboy/mbc.c new file mode 100644 index 00000000000..fc823c1f991 --- /dev/null +++ b/src/emu/bus/gameboy/mbc.c @@ -0,0 +1,1041 @@ +/*********************************************************************************************************** + + Game Boy carts with MBC (Memory Bank Controller) + + + TODO: add proper RTC and Rumble support + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "mbc.h" + + +//------------------------------------------------- +// gb_rom_mbc*_device - constructor +//------------------------------------------------- + +const device_type GB_ROM_MBC1 = &device_creator<gb_rom_mbc1_device>; +const device_type GB_ROM_MBC1_COL = &device_creator<gb_rom_mbc1col_device>; +const device_type GB_ROM_MBC2 = &device_creator<gb_rom_mbc2_device>; +const device_type GB_ROM_MBC3 = &device_creator<gb_rom_mbc3_device>; +const device_type GB_ROM_MBC5 = &device_creator<gb_rom_mbc5_device>; +const device_type GB_ROM_MBC6 = &device_creator<gb_rom_mbc6_device>; +const device_type GB_ROM_MBC7 = &device_creator<gb_rom_mbc7_device>; +const device_type GB_ROM_MMM01 = &device_creator<gb_rom_mmm01_device>; +const device_type GB_ROM_SINTAX = &device_creator<gb_rom_sintax_device>; +const device_type GB_ROM_CHONGWU = &device_creator<gb_rom_chongwu_device>; +const device_type GB_ROM_DIGIMON = &device_creator<gb_rom_digimon_device>; +const device_type GB_ROM_ROCKMAN8 = &device_creator<gb_rom_rockman8_device>; +const device_type GB_ROM_SM3SP = &device_creator<gb_rom_sm3sp_device>; + + +gb_rom_mbc_device::gb_rom_mbc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_gb_cart_interface( mconfig, *this ) +{ +} + +gb_rom_mbc1_device::gb_rom_mbc1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MBC1, "GB MBC1 Carts", tag, owner, clock, "gb_rom_mbc1", __FILE__) +{ +} + +gb_rom_mbc1col_device::gb_rom_mbc1col_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MBC1_COL, "GB MBC1 Collection Carts", tag, owner, clock, "gb_rom_mbc1col", __FILE__) +{ +} + +gb_rom_mbc2_device::gb_rom_mbc2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MBC2, "GB MBC2 Carts", tag, owner, clock, "gb_rom_mbc2", __FILE__) +{ +} + +gb_rom_mbc3_device::gb_rom_mbc3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MBC3, "GB MBC3 Carts", tag, owner, clock, "gb_rom_mbc3", __FILE__) +{ +} + +gb_rom_mbc5_device::gb_rom_mbc5_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : gb_rom_mbc_device(mconfig, type, name, tag, owner, clock, shortname, source) +{ +} + +gb_rom_mbc5_device::gb_rom_mbc5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MBC5, "GB MBC5 Carts", tag, owner, clock, "gb_rom_mbc5", __FILE__) +{ +} + +gb_rom_mbc6_device::gb_rom_mbc6_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MBC6, "GB MBC6 Carts", tag, owner, clock, "gb_rom_mbc6", __FILE__) +{ +} + +gb_rom_mbc7_device::gb_rom_mbc7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MBC7, "GB MBC7 Carts", tag, owner, clock, "gb_rom_mbc7", __FILE__) +{ +} + +gb_rom_mmm01_device::gb_rom_mmm01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_MMM01, "GB MMM01 Carts", tag, owner, clock, "gb_rom_mmm01", __FILE__) +{ +} + +gb_rom_sintax_device::gb_rom_sintax_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_SINTAX, "GB MBC5 Sintax Carts", tag, owner, clock, "gb_rom_sintax", __FILE__) +{ +} + +gb_rom_chongwu_device::gb_rom_chongwu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc5_device(mconfig, GB_ROM_CHONGWU, "GB Chong Wu Xiao Jing Ling", tag, owner, clock, "gb_rom_chongwu", __FILE__) +{ +} + +gb_rom_digimon_device::gb_rom_digimon_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc5_device(mconfig, GB_ROM_DIGIMON, "GB Digimon", tag, owner, clock, "gb_rom_digimon", __FILE__) +{ +} + +gb_rom_rockman8_device::gb_rom_rockman8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_ROCKMAN8, "GB MBC1 Rockman 8", tag, owner, clock, "gb_rom_rockman8", __FILE__) +{ +} + +gb_rom_sm3sp_device::gb_rom_sm3sp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_mbc_device(mconfig, GB_ROM_SM3SP, "GB MBC1 Super Mario 3 Special", tag, owner, clock, "gb_rom_sm3sp", __FILE__) +{ +} + + +//------------------------------------------------- +// shared_start +//------------------------------------------------- + +void gb_rom_mbc_device::shared_start() +{ + save_item(NAME(m_latch_bank)); + save_item(NAME(m_latch_bank2)); + save_item(NAME(m_ram_bank)); + save_item(NAME(m_ram_enable)); + save_item(NAME(m_mode)); +} + +//------------------------------------------------- +// shared_reset +//------------------------------------------------- + +void gb_rom_mbc_device::shared_reset() +{ + m_latch_bank = 0; + m_latch_bank2 = 1; + m_ram_bank = 0; + m_ram_enable = 0; + m_mode = 0; +} + +//------------------------------------------------- +// mapper specific start/reset +//------------------------------------------------- + +void gb_rom_mbc3_device::device_start() +{ + shared_start(); + save_item(NAME(m_rtc_map)); +} + +void gb_rom_mbc3_device::device_reset() +{ + shared_reset(); + memset(m_rtc_map, 0, sizeof(m_rtc_map)); +} + +void gb_rom_mbc6_device::device_start() +{ + save_item(NAME(m_bank_4000)); + save_item(NAME(m_bank_6000)); + save_item(NAME(m_latch1)); + save_item(NAME(m_latch2)); + save_item(NAME(m_latch_bank)); + save_item(NAME(m_latch_bank2)); + save_item(NAME(m_ram_bank)); + save_item(NAME(m_ram_enable)); + save_item(NAME(m_mode)); +} + +void gb_rom_mbc6_device::device_reset() +{ + m_bank_4000 = 2; // correct default? + m_bank_6000 = 3; // correct default? + m_latch1 = 0; // correct default? + m_latch2 = 0; // correct default? + + m_latch_bank = 2; // correct default? + m_latch_bank2 = 3; // correct default? + m_ram_bank = 0; + m_ram_enable = 0; + m_mode = 0; +} + +void gb_rom_mmm01_device::device_start() +{ + shared_start(); + save_item(NAME(m_bank_mask)); + save_item(NAME(m_bank)); + save_item(NAME(m_reg)); +} + +void gb_rom_mmm01_device::device_reset() +{ + m_latch_bank = 0x200 - 2; + m_latch_bank2 = 0x200 - 1; + m_ram_bank = 0; + m_bank_mask = 0xff; + m_bank = 0; + m_reg = 0; +} + +void gb_rom_sintax_device::device_start() +{ + shared_start(); + save_item(NAME(m_sintax_mode)); + save_item(NAME(m_currentxor)); + save_item(NAME(m_xor2)); + save_item(NAME(m_xor3)); + save_item(NAME(m_xor4)); + save_item(NAME(m_xor5)); +} + +void gb_rom_sintax_device::device_reset() +{ + shared_reset(); + m_sintax_mode = 0; + m_currentxor = 0; + m_xor2 = 0; + m_xor3 = 0; + m_xor4 = 0; + m_xor5 = 0; +} + +void gb_rom_chongwu_device::device_start() +{ + shared_start(); + save_item(NAME(m_protection_checked)); +} + +void gb_rom_chongwu_device::device_reset() +{ + shared_reset(); + m_protection_checked = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +READ8_MEMBER(gb_rom_mbc_device::read_rom) +{ + return m_rom[rom_bank_map[m_latch_bank] + offset]; +} + +READ8_MEMBER(gb_rom_mbc_device::read_ram) +{ + if (m_ram) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc_device::write_ram) +{ + if (m_ram) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset] = data; +} + + +// MBC1 + + +READ8_MEMBER(gb_rom_mbc1_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_mbc1_device::write_bank) +{ + if (offset < 0x2000) + m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0; + else if (offset < 0x4000) + { + // 5bits only + data &= 0x1f; + // bank = 0 => bank = 1 + if (data == 0) + data = 1; + + m_latch_bank2 = (m_latch_bank2 & 0x01e0) | data; + } + else if (offset < 0x6000) + { + // 2bits only + data &= 0x3; + m_latch_bank2 = (m_latch_bank2 & 0x001f) | (data << 5); + } + else + m_mode = data & 0x1; +} + +READ8_MEMBER(gb_rom_mbc1_device::read_ram) +{ + if (m_ram && m_ram_enable) + { + m_ram_bank = m_mode ? (m_latch_bank2 >> 5) : 0; + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset]; + } + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc1_device::write_ram) +{ + if (m_ram && m_ram_enable) + { + m_ram_bank = m_mode ? (m_latch_bank2 >> 5) : 0; + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset] = data; + } +} + + +// MBC1 Korean variant (used by Bomberman Selection) + +READ8_MEMBER(gb_rom_mbc1col_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_mbc1col_device::write_bank) +{ + if (offset < 0x2000) + m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0; + else if (offset < 0x4000) + { + // 4bits only? + data &= 0x0f; + // bank = 0 => bank = 1 + if (data == 0) + data = 1; + + m_latch_bank2 = (m_latch_bank2 & 0x01f0) | data; + } + else if (offset < 0x6000) + { + // 2bits only + data &= 0x3; + m_latch_bank2 = (m_latch_bank2 & 0x000f) | (data << 4); + m_latch_bank = m_latch_bank2 & 0x30; + } + else + m_mode = data & 0x1; +} + +// RAM access is the same as usual MBC1 +READ8_MEMBER(gb_rom_mbc1col_device::read_ram) +{ + if (m_ram && m_ram_enable) + { + m_ram_bank = m_mode ? (m_latch_bank2 >> 5) : 0; + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset]; + } + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc1col_device::write_ram) +{ + if (m_ram && m_ram_enable) + { + m_ram_bank = m_mode ? (m_latch_bank2 >> 5) : 0; + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset] = data; + } +} + +// MBC2 + +READ8_MEMBER(gb_rom_mbc2_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_mbc2_device::write_bank) +{ + if (offset < 0x2000) + m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0; + else if (offset < 0x4000) + { + // 4bits only + data &= 0x0f; + // bank = 0 => bank = 1 + if (data == 0) + data = 1; + + // The least significant bit of the upper address byte must be 1 + if (offset & 0x0100) + m_latch_bank2 = (m_latch_bank2 & 0x100) | data; + } +} + +// 1 bank only?? +READ8_MEMBER(gb_rom_mbc2_device::read_ram) +{ + if (m_ram && m_ram_enable) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc2_device::write_ram) +{ + if (m_ram && m_ram_enable) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; +} + + +// MBC3 + +READ8_MEMBER(gb_rom_mbc3_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_mbc3_device::write_bank) +{ + if (offset < 0x2000) + m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0; + else if (offset < 0x4000) + { + // 7bits + data &= 0x7f; + /* Selecting bank 0 == selecting bank 1 */ + if (data == 0) + data = 1; + + m_latch_bank2 = data; + } + else if (offset < 0x6000) + { + m_ram_bank = data; + } + else + { + if (has_timer) + { + /* FIXME: RTC Latch goes here */ + m_rtc_map[0] = 50; /* Seconds */ + m_rtc_map[1] = 40; /* Minutes */ + m_rtc_map[2] = 15; /* Hours */ + m_rtc_map[3] = 25; /* Day counter lowest 8 bits */ + m_rtc_map[4] = 0x01; /* Day counter upper bit, timer off, no day overflow occurred (bit7) */ + } + } +} + +READ8_MEMBER(gb_rom_mbc3_device::read_ram) +{ + if (m_ram_bank < 4 && m_ram_enable) + { // RAM + if (m_ram) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; + } + if (m_ram_bank >= 0x8 && m_ram_bank <= 0xc) + { // RAM + if (has_timer) + return m_rtc_map[m_ram_bank - 8]; + } + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc3_device::write_ram) +{ + if (m_ram_bank < 4 && m_ram_enable) + { // RAM + if (m_ram) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; + } + if (m_ram_bank >= 0x8 && m_ram_bank <= 0xc) + { // RAM + if (has_timer) + { + // what to do here? + } + } +} + +// MBC5 + +READ8_MEMBER(gb_rom_mbc5_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_mbc5_device::write_bank) +{ + if (offset < 0x2000) + m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0; + else if (offset < 0x3000) + { + // MBC5 has a 9 bit bank select + // Writing into 2000-2fff sets the lower 8 bits + m_latch_bank2 = (m_latch_bank2 & 0x100) | data; + } + else if (offset < 0x4000) + { + // MBC5 has a 9 bit bank select + // Writing into 3000-3fff sets the 9th bit + m_latch_bank2 = (m_latch_bank2 & 0xff) | ((data & 0x01) << 8); + } + else if (offset < 0x6000) + { + data &= 0x0f; + if (has_rumble) + data &= 0x7; + m_ram_bank = data; + } +} + +READ8_MEMBER(gb_rom_mbc5_device::read_ram) +{ + if (m_ram && m_ram_enable) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc5_device::write_ram) +{ + if (m_ram && m_ram_enable) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; +} + +// MBC6 + +READ8_MEMBER(gb_rom_mbc6_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else if (offset < 0x6000) + return m_rom[rom_bank_map[m_bank_4000 >> 1] * 0x4000 + (m_bank_4000 & 0x01) * 0x2000 + (offset & 0x1fff)]; + else + return m_rom[rom_bank_map[m_bank_6000 >> 1] * 0x4000 + (m_bank_6000 & 0x01) * 0x2000 + (offset & 0x1fff)]; +} + +WRITE8_MEMBER(gb_rom_mbc6_device::write_bank) +{ + if (offset < 0x2000) + { + logerror( "0x%04X: write to mbc6 ram enable area: %04X <- 0x%02X\n", space.device().safe_pc(), offset, data ); + } + else if (offset < 0x3000) + { + if (!(offset & 0x0800)) + m_latch1 = data; + else if (data == 0x00) + m_bank_4000 = m_latch1; + } + else if (offset < 0x4000) + { + if (!(offset & 0x0800)) + m_latch2 = data; + else if (data == 0x00) + m_bank_6000 = m_latch2; + } +} + +READ8_MEMBER(gb_rom_mbc6_device::read_ram) +{ + if (m_ram) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc6_device::write_ram) +{ + if (m_ram) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; +} + +// MBC7 + +READ8_MEMBER(gb_rom_mbc7_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_mbc7_device::write_bank) +{ + if (offset < 0x2000) + { + // FIXME: Add RAM enable support + logerror("0x%04X: Write to ram enable register 0x%04X <- 0x%02X\n", space.device().safe_pc( ), offset, data); + } + else if (offset < 0x3000) + { + logerror( "0x%04X: write to mbc7 rom select register: 0x%04X <- 0x%02X\n", space.device() .safe_pc( ), 0x2000 + offset, data ); + /* Bit 12 must be set for writing to the mbc register */ + if (offset & 0x0100) + m_latch_bank2 = data; + } + else + { + logerror( "0x%04X: write to mbc7 rom area: 0x%04X <- 0x%02X\n", space.device() .safe_pc( ), 0x3000 + offset, data ); + /* Bit 12 must be set for writing to the mbc register */ + if (offset & 0x0100) + { + switch (offset & 0x7000) + { + case 0x3000: /* 0x3000-0x3fff */ + case 0x4000: /* 0x4000-0x4fff */ + case 0x5000: /* 0x5000-0x5fff */ + case 0x6000: /* 0x6000-0x6fff */ + case 0x7000: /* 0x7000-0x7fff */ + break; + } + } + } +} + +READ8_MEMBER(gb_rom_mbc7_device::read_ram) +{ + if (m_ram) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_mbc7_device::write_ram) +{ + if (m_ram) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; +} + + +// MMM01 +// This mmm01 implementation is mostly guess work, no clue how correct it all is + +READ8_MEMBER(gb_rom_mmm01_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + offset]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_mmm01_device::write_bank) +{ + if (offset < 0x2000) + { + if (data & 0x40) + { + m_latch_bank = m_reg; + m_latch_bank2 = m_latch_bank + m_bank; + } + } + else if (offset < 0x4000) + { + m_reg = data & ((m_rom_size / 0x4000) - 1); + m_bank = m_reg & m_bank_mask; + if (m_bank == 0) + m_bank = 1; + m_latch_bank2 = m_latch_bank + m_bank; + } + else if (offset < 0x6000) + logerror("0x%04X: write 0x%02X to 0x%04X\n", space.device().safe_pc(), data, offset); + else + { + logerror("0x%04X: write 0x%02X to 0x%04X\n", space.device().safe_pc(), data, offset); + /* Not sure if this is correct, Taito Variety Pack sets these values */ + /* Momotarou Collection 2 writes 01 and 21 here */ + switch (data) + { + case 0x30: m_bank_mask = 0x07; break; + case 0x38: m_bank_mask = 0x03; break; + default: m_bank_mask = 0xff; break; + } + } +} + +// MBC5 variant used by Chong Wu Xiao Jing Ling (this appears to be a re-release of a Li Cheng / Niutoude game, +// given that it contains the Niutoude logo, with most protection checks patched out) + +READ8_MEMBER(gb_rom_chongwu_device::read_rom) +{ + // protection check at the first read here... + if (offset == 0x41c3 && !m_protection_checked) + { + m_protection_checked = 1; + return 0x5d; + } + + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +// MBC5 variant used by Sintax games + +void gb_rom_sintax_device::set_xor_for_bank(UINT8 bank) +{ + switch (bank & 0x0f) + { + case 0x00: case 0x04: case 0x08: case 0x0c: + m_currentxor = m_xor2; + break; + case 0x01: case 0x05: case 0x09: case 0x0d: + m_currentxor = m_xor3; + break; + case 0x02: case 0x06: case 0x0a: case 0x0e: + m_currentxor = m_xor4; + break; + case 0x03: case 0x07: case 0x0b: case 0x0f: + m_currentxor = m_xor5; + break; + } +} + +READ8_MEMBER(gb_rom_sintax_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)] ^ m_currentxor; +} + +WRITE8_MEMBER(gb_rom_sintax_device::write_bank) +{ + if (offset < 0x2000) + m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0; + else if (offset < 0x3000) + { + set_xor_for_bank(data); + + switch (m_sintax_mode & 0x0f) + { + case 0x0d: + data = BITSWAP8(data, 1,0,7,6,5,4,3,2); + break; + case 0x09: + //data = BITSWAP8(data, 3,2,5,4,0,1,6,7); // Monkey..no + data = BITSWAP8(data, 4,5,2,3,0,1,6,7); + break; + case 0x00: // 0x10=lion 0x00 hmmmmm // 1 and 0 unconfirmed + data = BITSWAP8(data, 7,0,5,6,3,4,1,2); + break; + case 0x01: + data = BITSWAP8(data, 0,1,6,7,4,5,2,3); + break; + case 0x05: + data = BITSWAP8(data, 7,6,1,0,3,2,5,4); // Not 100% on this one + break; + case 0x07: + data = BITSWAP8(data, 2,0,3,1,5,4,7,6); // 5 and 7 unconfirmed + break; + case 0x0b: + data = BITSWAP8(data, 2,3,0,1,6,7,4,5); // 5 and 6 unconfirmed + break; + } + m_latch_bank2 = (m_latch_bank2 & 0x100) | data; + } + else if (offset < 0x4000) + { + m_latch_bank2 = (m_latch_bank2 & 0xff) | ((data & 0x01) << 8); + } + else if (offset < 0x5000) + { + data &= 0x0f; + if (has_rumble) + data &= 0x7; + m_ram_bank = data; + } + else if (offset < 0x6000) + { + if (!m_sintax_mode) + { + m_sintax_mode = data; + write_bank(space, 0x2000, 1); //force a fake bank switch + } +// printf("sintax mode %x\n", m_sintax_mode & 0xf); + } + else if (offset >= 0x7000) + { + switch ((offset & 0x00f0) >> 4) + { + case 2: + m_xor2 = data; + break; + case 3: + m_xor3 = data; + break; + case 4: + m_xor4 = data; + break; + case 5: + m_xor5 = data; + break; + } + + if (m_currentxor == 0) + set_xor_for_bank(4); + } + +} + +READ8_MEMBER(gb_rom_sintax_device::read_ram) +{ + if (m_ram && m_ram_enable) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_sintax_device::write_ram) +{ + if (m_ram && m_ram_enable) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; +} + +/* + + Further MBC5 variants to emulate: + + Digimon 2 & Digimon 4 (Yong Yong) + + Digimon 2 writes at $2000 to select latch2 (data must be divided by 2, and 0 becomes 1), + then writes to $2400 a series of values that the patched version does not write... + Digimon 4 seems to share part of the $2000 behavior, but does not write to $2400... + + */ + +// MBC5 variant used by Digimon 2 (and maybe 4?) + +READ8_MEMBER(gb_rom_digimon_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_digimon_device::write_bank) +{ + if (offset < 0x2000) + m_ram_enable = ((data & 0x0f) == 0x0a) ? 1 : 0; + else if (offset == 0x2000) + { +// printf("written $02 %X at %X\n", data, offset); + if (!data) + data++; + m_latch_bank2 = data/2; + } + else if (offset < 0x3000) + { +// printf("written $03 %X at %X\n", data, offset); + } + else if (offset < 0x4000) + { +// printf("written $04 %X at %X\n", data, offset); + } + else if (offset < 0x6000) + { +// printf("written $05-$06 %X at %X\n", data, offset); + data &= 0x0f; + if (has_rumble) + data &= 0x7; + m_ram_bank = data; + } +// else +// printf("written $07 %X at %X\n", data, offset); +} + +READ8_MEMBER(gb_rom_digimon_device::read_ram) +{ + if (m_ram && m_ram_enable) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_digimon_device::write_ram) +{ + if (m_ram && m_ram_enable) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + (offset & 0x1fff)] = data; +} + + +// MBC1 variant used by Yong Yong for Rockman 8 + +READ8_MEMBER(gb_rom_rockman8_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[m_latch_bank * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[m_latch_bank2 * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_rockman8_device::write_bank) +{ + if (offset < 0x2000) + return; + else if (offset < 0x4000) + { + // 5bits only + data &= 0x1f; + if (data == 0) + data = 1; + if (data > 0xf) + data -= 8; + + m_latch_bank2 = data; + } +} + +READ8_MEMBER(gb_rom_rockman8_device::read_ram) +{ + if (m_ram) + return m_ram[offset]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_rockman8_device::write_ram) +{ + if (m_ram) + m_ram[offset] = data; +} + +// MBC1 variant used by Yong Yong for Super Mario 3 Special + +// Mario special seems to be 512k image (mirrored up to 1m or 2m [redump needed to establish this]) +// it consists of 13 unique 16k chunks layed out as follows +// unique chunk --> bank in bin +// 1st to 7th --> 0x00 to 0x06 +// 8th --> 0x08 +// 9th --> 0x0b +// 10th --> 0x0c +// 11th --> 0x0d +// 12th --> 0x0f +// 13th --> 0x13 + +// writing data to 0x2000-0x2fff switches bank according to the table below +// (the value values corresponding to table[0x0f] is not confirmed, choices +// 0,1,2,3,8,c,f freeze the game, while 4,5,6,7,b,d,0x13 work with glitches) +static UINT8 smb3_table1[0x20] = +{ + 0x00,0x04,0x01,0x05, 0x02,0x06,0x03,0x05, 0x08,0x0c,0x03,0x0d, 0x03,0x0b,0x0b,0x08 /* original doc here put 0x0f (i.e. 11th unique bank) */, + 0x05,0x06,0x0b,0x0d, 0x08,0x06,0x13,0x0b, 0x08,0x05,0x05,0x08, 0x0b,0x0d,0x06,0x05 +}; + +// according to old doc from Brian Provinciano, writing bit5 in 0x5000-0x5fff should +// change the bank layout, in the sense that writing to bankswitch acts like if +// the original rom has a different layout (as if unique chunks were under permutations +// (24), (365) and (8a9) with 0,1,7,b,c fixed) and the same table above is used +// however, no such a write ever happen (only bit4 is written, but changing mode with +// bit4 breaks the gfx...) + +READ8_MEMBER(gb_rom_sm3sp_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[0] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[m_latch_bank2 * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_sm3sp_device::write_bank) +{ +// printf("write 0x%x at %x\n", data, offset); + if (offset < 0x2000) + return; + else if (offset < 0x3000) + { + // Table 1 confirmed... + // 0->0, 4->2, 6->3 + // 1e -> 6 (level 1 bg gfx) + // 19 -> 5 (level 2 bg gfx) + // 1b -> 8 (level 3 bg gfx) + // 1d -> D (level 4 bg gfx) + // 1c -> B (bonus house bg gfx) + // 1 (9 maybe, or 3)? f (5 maybe)? 2->1? + // 16 -> 4-8? b? + + // 5bits only + data &= 0x1f; + + m_latch_bank2 = smb3_table1[data]; + if (m_mode) + { + switch (m_latch_bank2) + { + case 0x02: m_latch_bank2 = 4; break; + case 0x03: m_latch_bank2 = 6; break; + case 0x04: m_latch_bank2 = 2; break; + case 0x05: m_latch_bank2 = 3; break; + case 0x06: m_latch_bank2 = 5; break; + case 0x0b: m_latch_bank2 = 0xd; break; + case 0x0c: m_latch_bank2 = 0xb; break; + case 0x0d: m_latch_bank2 = 0xc; break; + + case 0x00: + case 0x01: + case 0x08: + case 0x0f: + case 0x13: + default: + break; + } + } + } + else if (offset < 0x5000) + { +// printf("write $5 %X at %X\n", data, offset); + //maybe rumble?? + } + else if (offset < 0x6000) + { +// printf("write mode %x\n", data); + m_mode = BIT(data, 5); +// write_bank(space, 0x2000, 1); + } +} + +READ8_MEMBER(gb_rom_sm3sp_device::read_ram) +{ + if (m_ram) + return m_ram[offset]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_sm3sp_device::write_ram) +{ + if (m_ram) + m_ram[offset] = data; +} diff --git a/src/emu/bus/gameboy/mbc.h b/src/emu/bus/gameboy/mbc.h new file mode 100644 index 00000000000..adb74accefb --- /dev/null +++ b/src/emu/bus/gameboy/mbc.h @@ -0,0 +1,291 @@ +#ifndef __GB_MBC_H +#define __GB_MBC_H + +#include "gb_slot.h" + + +// ======================> gb_rom_mbc_device + +class gb_rom_mbc_device : public device_t, + public device_gb_cart_interface +{ +public: + // construction/destruction + gb_rom_mbc_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + void shared_start(); + void shared_reset(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + + UINT8 m_ram_enable; + UINT8 m_mode; +}; + +// ======================> gb_rom_mbc1_device + +class gb_rom_mbc1_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mbc1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// ======================> gb_rom_mbc1col_device + +class gb_rom_mbc1col_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mbc1col_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// ======================> gb_rom_mbc2_device + +class gb_rom_mbc2_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mbc2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// ======================> gb_rom_mbc3_device + +class gb_rom_mbc3_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mbc3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + UINT8 m_rtc_map[5]; +}; + +// ======================> gb_rom_mbc5_device + +class gb_rom_mbc5_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mbc5_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + gb_rom_mbc5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// ======================> gb_rom_mbc6_device + +class gb_rom_mbc6_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mbc6_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + UINT16 m_latch1, m_latch2; + UINT8 m_bank_4000, m_bank_6000; +}; + +// ======================> gb_rom_mbc7_device + +class gb_rom_mbc7_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mbc7_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// ======================> gb_rom_mmm01_device +class gb_rom_mmm01_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_mmm01_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + UINT8 m_bank_mask, m_bank, m_reg; +}; + +// ======================> gb_rom_sintax_device +class gb_rom_sintax_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_sintax_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + void set_xor_for_bank(UINT8 bank); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + UINT8 m_bank_mask, m_bank, m_reg; + + UINT8 m_currentxor, m_xor2, m_xor3, m_xor4, m_xor5, m_sintax_mode; +}; + +// ======================> gb_rom_chongwu_device + +class gb_rom_chongwu_device : public gb_rom_mbc5_device +{ +public: + // construction/destruction + gb_rom_chongwu_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + virtual DECLARE_READ8_MEMBER(read_rom); + UINT8 m_protection_checked; +}; + +// ======================> gb_rom_digimon_device + +class gb_rom_digimon_device : public gb_rom_mbc5_device +{ +public: + // construction/destruction + gb_rom_digimon_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// ======================> gb_rom_rockman8_device +class gb_rom_rockman8_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_rockman8_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + UINT8 m_bank_mask, m_bank, m_reg; +}; + +// ======================> gb_rom_sm3sp_device +class gb_rom_sm3sp_device : public gb_rom_mbc_device +{ +public: + // construction/destruction + gb_rom_sm3sp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + UINT8 m_bank_mask, m_bank, m_reg; +}; + + + +// device type definition +extern const device_type GB_ROM_MBC1; +extern const device_type GB_ROM_MBC1_COL; +extern const device_type GB_ROM_MBC2; +extern const device_type GB_ROM_MBC3; +extern const device_type GB_ROM_MBC4; +extern const device_type GB_ROM_MBC5; +extern const device_type GB_ROM_MBC6; +extern const device_type GB_ROM_MBC7; +extern const device_type GB_ROM_MMM01; +extern const device_type GB_ROM_SINTAX; +extern const device_type GB_ROM_CHONGWU; +extern const device_type GB_ROM_DIGIMON; +extern const device_type GB_ROM_ROCKMAN8; +extern const device_type GB_ROM_SM3SP; + +#endif diff --git a/src/emu/bus/gameboy/rom.c b/src/emu/bus/gameboy/rom.c new file mode 100644 index 00000000000..1ec6258710a --- /dev/null +++ b/src/emu/bus/gameboy/rom.c @@ -0,0 +1,362 @@ +/*********************************************************************************************************** + + Game Boy cart emulation + + + Here we emulate carts with no RAM and simple bankswitch + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "rom.h" + + +//------------------------------------------------- +// gb_rom_device - constructor +//------------------------------------------------- + +const device_type GB_STD_ROM = &device_creator<gb_rom_device>; +const device_type GB_ROM_TAMA5 = &device_creator<gb_rom_tama5_device>; +const device_type GB_ROM_WISDOM = &device_creator<gb_rom_wisdom_device>; +const device_type GB_ROM_YONG = &device_creator<gb_rom_yong_device>; +const device_type GB_ROM_ATVRAC = &device_creator<gb_rom_atvrac_device>; +const device_type GB_ROM_LASAMA = &device_creator<gb_rom_lasama_device>; + +const device_type MEGADUCK_ROM = &device_creator<megaduck_rom_device>; + + +gb_rom_device::gb_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_gb_cart_interface( mconfig, *this ) +{ +} + +gb_rom_device::gb_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, GB_STD_ROM, "GB Carts", tag, owner, clock, "gb_rom", __FILE__), + device_gb_cart_interface( mconfig, *this ) +{ +} + +gb_rom_tama5_device::gb_rom_tama5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_device(mconfig, GB_ROM_TAMA5, "GB Tamagotchi", tag, owner, clock, "gb_rom_tama5", __FILE__) +{ +} + +gb_rom_wisdom_device::gb_rom_wisdom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_device(mconfig, GB_ROM_WISDOM, "GB Wisdom Tree Carts", tag, owner, clock, "gb_rom_wisdom", __FILE__) +{ +} + +gb_rom_yong_device::gb_rom_yong_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_device(mconfig, GB_ROM_YONG, "GB Yong Yong Carts", tag, owner, clock, "gb_rom_yong", __FILE__) +{ +} + +gb_rom_atvrac_device::gb_rom_atvrac_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_device(mconfig, GB_ROM_ATVRAC, "GB ATV Racin'", tag, owner, clock, "gb_rom_atvrac", __FILE__) +{ +} + +gb_rom_lasama_device::gb_rom_lasama_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gb_rom_device(mconfig, GB_ROM_LASAMA, "GB LaSaMa", tag, owner, clock, "gb_rom_lasama", __FILE__) +{ +} + + +megaduck_rom_device::megaduck_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_gb_cart_interface( mconfig, *this ) +{ +} + +megaduck_rom_device::megaduck_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MEGADUCK_ROM, "MegaDuck Carts", tag, owner, clock, "megaduck_rom", __FILE__), + device_gb_cart_interface( mconfig, *this ) +{ +} + + +//------------------------------------------------- +// shared_start +//------------------------------------------------- + +void gb_rom_device::shared_start() +{ + save_item(NAME(m_latch_bank)); + save_item(NAME(m_latch_bank2)); + save_item(NAME(m_ram_bank)); +} + +//------------------------------------------------- +// shared_reset +//------------------------------------------------- + +void gb_rom_device::shared_reset() +{ + m_ram_bank = 0; + m_latch_bank = 0; + m_latch_bank2 = 1; +} + +//------------------------------------------------- +// mapper specific start/reset +//------------------------------------------------- + +void gb_rom_tama5_device::device_start() +{ + shared_start(); + save_item(NAME(m_tama5_data)); + save_item(NAME(m_tama5_addr)); + save_item(NAME(m_tama5_cmd)); + save_item(NAME(m_regs)); + save_item(NAME(m_rtc_reg)); +} + +void gb_rom_tama5_device::device_reset() +{ + shared_reset(); + m_tama5_data = 0; + m_tama5_addr= 0; + m_tama5_cmd = 0; + memset(m_regs, 0xff, sizeof(m_regs)); + m_rtc_reg = 0xff; +} + + +// these are identical to shared ones above, but megaduck cart class is not derived from gb cart class... +void megaduck_rom_device::device_start() +{ + save_item(NAME(m_latch_bank)); + save_item(NAME(m_latch_bank2)); + save_item(NAME(m_ram_bank)); +} + +void megaduck_rom_device::device_reset() +{ + m_ram_bank = 0; + m_latch_bank = 0; + m_latch_bank2 = 1; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +READ8_MEMBER(gb_rom_device::read_rom) +{ + m_latch_bank = offset / 0x4000; + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; +} + +READ8_MEMBER(gb_rom_device::read_ram) +{ + if (m_ram) + return m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset]; + else + return 0xff; +} + +WRITE8_MEMBER(gb_rom_device::write_ram) +{ + if (m_ram) + m_ram[ram_bank_map[m_ram_bank] * 0x2000 + offset] = data; +} + + +// Tamagotchi + +READ8_MEMBER(gb_rom_tama5_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +READ8_MEMBER(gb_rom_tama5_device::read_ram) +{ + return m_rtc_reg; +} + +WRITE8_MEMBER(gb_rom_tama5_device::write_ram) +{ + switch (offset & 0x0001) + { + case 0x0000: /* Write to data register */ + switch (m_tama5_cmd) + { + case 0x00: /* Bits 0-3 for rom bank selection */ + m_latch_bank2 = (m_latch_bank2 & 0xf0) | (data & 0x0f); + break; + case 0x01: /* Bit 4(-7?) for rom bank selection */ + m_latch_bank2 = (m_latch_bank2 & 0x0f) | ((data & 0x0f) << 4); + break; + case 0x04: /* Data to write lo */ + m_tama5_data = (m_tama5_data & 0xf0) | (data & 0x0f); + break; + case 0x05: /* Data to write hi */ + m_tama5_data = (m_tama5_data & 0x0f) | ((data & 0x0f) << 4); + break; + case 0x06: /* Address selection hi */ + m_tama5_addr = (m_tama5_addr & 0x0f) | ((data & 0x0f) << 4); + break; + case 0x07: /* Address selection lo */ + /* This address always seems to written last, so we'll just + execute the command here */ + m_tama5_addr = (m_tama5_addr & 0xf0) | (data & 0x0f); + switch (m_tama5_addr & 0xe0) + { + case 0x00: /* Write memory */ + //logerror( "Write tama5 memory 0x%02X <- 0x%02X\n", m_tama5_addr & 0x1f, m_tama5_data); + m_regs[m_tama5_addr & 0x1f] = m_tama5_data; + break; + case 0x20: /* Read memory */ + //logerror( "Read tama5 memory 0x%02X\n", m_tama5_addr & 0x1f); + m_tama5_data = m_regs[m_tama5_addr & 0x1f]; + break; + case 0x40: /* Unknown, some kind of read */ + if ((m_tama5_addr & 0x1f) == 0x12) + m_tama5_data = 0xff; + case 0x80: /* Unknown, some kind of read (when 07=01)/write (when 07=00/02) */ + default: + logerror( "0x%04X: Unknown addressing mode\n", space.device() .safe_pc( ) ); + break; + } + break; + } + break; + case 0x0001: /* Write to control register */ + switch (data) + { + case 0x00: /* Bits 0-3 for rom bank selection */ + case 0x01: /* Bits 4-7 for rom bank selection */ + case 0x04: /* Data write register lo */ + case 0x05: /* Data write register hi */ + case 0x06: /* Address register hi */ + case 0x07: /* Address register lo */ + break; + case 0x0a: /* Are we ready for the next command? */ + m_rtc_reg = 0x01; + break; + case 0x0c: /* Data read register lo */ + m_rtc_reg = m_tama5_data & 0x0f; + break; + case 0x0d: /* Data read register hi */ + m_rtc_reg = (m_tama5_data & 0xf0) >> 4; + break; + default: + logerror( "0x%04X: Unknown tama5 command 0x%02X\n", space.device() .safe_pc( ), data ); + break; + } + m_tama5_cmd = data; + break; + } +} + + +// Wisdom Tree + +READ8_MEMBER(gb_rom_wisdom_device::read_rom) +{ + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + offset]; +} + +WRITE8_MEMBER(gb_rom_wisdom_device::write_bank) +{ + if (offset < 0x4000) + m_latch_bank = (offset << 1) & 0x1ff; +} + + +// Yong Yong pirate + +READ8_MEMBER(gb_rom_yong_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_yong_device::write_bank) +{ + if (offset == 0x2000) + m_latch_bank2 = data; +} + + +// ATV Racin pirate (incomplete) + +READ8_MEMBER(gb_rom_atvrac_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_atvrac_device::write_bank) +{ + if (offset == 0x3f00) + { + if (data == 0) + data = 1; + m_latch_bank2 = m_latch_bank | data; + } + if (offset == 0x3fc0) + m_latch_bank = data * 16; +} + +// La Sa Ma pirate (incomplete) + +READ8_MEMBER(gb_rom_lasama_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(gb_rom_lasama_device::write_bank) +{ + if (offset == 0x2080) + { + // Actual banking? + m_latch_bank2 = m_latch_bank | (data & 0x03); + } + if (offset == 0x6000) + { + // On boot the following two get written right after each other: + // 02 + // BE + // Disable logo switching? + if (!(data & 0x80)) + m_latch_bank = (data & 0x02) << 1; + } +} + + +// MegaDuck carts + +READ8_MEMBER(megaduck_rom_device::read_rom) +{ + if (offset < 0x4000) + return m_rom[rom_bank_map[m_latch_bank] * 0x4000 + (offset & 0x3fff)]; + else + return m_rom[rom_bank_map[m_latch_bank2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(megaduck_rom_device::write_bank) +{ + if (offset == 0x0001) + m_latch_bank2 = data; +} + +WRITE8_MEMBER(megaduck_rom_device::write_ram) +{ + m_latch_bank = data * 2; + m_latch_bank2 = data * 2 + 1; +} diff --git a/src/emu/bus/gameboy/rom.h b/src/emu/bus/gameboy/rom.h new file mode 100644 index 00000000000..0e9f4a57974 --- /dev/null +++ b/src/emu/bus/gameboy/rom.h @@ -0,0 +1,144 @@ +#ifndef __GB_ROM_H +#define __GB_ROM_H + +#include "gb_slot.h" + + +// ======================> gb_rom_device + +class gb_rom_device : public device_t, + public device_gb_cart_interface +{ +public: + // construction/destruction + gb_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + gb_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + void shared_start(); + void shared_reset(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// ======================> gb_rom_tama5_device +class gb_rom_tama5_device : public gb_rom_device +{ +public: + // construction/destruction + gb_rom_tama5_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + UINT16 m_tama5_data, m_tama5_addr, m_tama5_cmd; + UINT8 m_regs[32]; + UINT8 m_rtc_reg; +}; + +// ======================> gb_rom_wisdom_device +class gb_rom_wisdom_device : public gb_rom_device +{ +public: + // construction/destruction + gb_rom_wisdom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); +}; + +// ======================> gb_rom_yong_device +class gb_rom_yong_device : public gb_rom_device +{ +public: + // construction/destruction + gb_rom_yong_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); +}; + +// ======================> gb_rom_atvrac_device +class gb_rom_atvrac_device : public gb_rom_device +{ +public: + // construction/destruction + gb_rom_atvrac_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); +}; + +// ======================> gb_rom_lasama_device +class gb_rom_lasama_device : public gb_rom_device +{ +public: + // construction/destruction + gb_rom_lasama_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { shared_start(); }; + virtual void device_reset() { shared_reset(); }; + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); +}; + + +// ======================> megaduck_rom_device +class megaduck_rom_device :public device_t, + public device_gb_cart_interface +{ +public: + // construction/destruction + megaduck_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + megaduck_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_rom); + virtual DECLARE_WRITE8_MEMBER(write_bank); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + +// device type definition +extern const device_type GB_STD_ROM; +extern const device_type GB_ROM_TAMA5; +extern const device_type GB_ROM_WISDOM; +extern const device_type GB_ROM_YONG; +extern const device_type GB_ROM_ATVRAC; +extern const device_type GB_ROM_LASAMA; + +extern const device_type MEGADUCK_ROM; + +#endif diff --git a/src/emu/bus/gba/gba_slot.c b/src/emu/bus/gba/gba_slot.c new file mode 100644 index 00000000000..05c64325686 --- /dev/null +++ b/src/emu/bus/gba/gba_slot.c @@ -0,0 +1,483 @@ +/*********************************************************************************************************** + + + Game Boy Advance cart emulation + (through slot devices) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "gba_slot.h" + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type GBA_CART_SLOT = &device_creator<gba_cart_slot_device>; + +//************************************************************************** +// GBA cartridges Interface +//************************************************************************** + +//------------------------------------------------- +// device_gba_cart_interface - constructor +//------------------------------------------------- + +device_gba_cart_interface::device_gba_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL), + m_nvram(NULL), + m_rom_size(0), + m_nvram_size(0) +{ +} + + +//------------------------------------------------- +// ~device_gba_cart_interface - destructor +//------------------------------------------------- + +device_gba_cart_interface::~device_gba_cart_interface() +{ +} + +//------------------------------------------------- +// nvram_alloc - alloc the space for the ram +//------------------------------------------------- + +void device_gba_cart_interface::nvram_alloc(running_machine &machine, UINT32 size) +{ + if (m_nvram == NULL) + { + m_nvram = auto_alloc_array_clear(machine, UINT32, size/sizeof(UINT32)); + m_nvram_size = size; + state_save_register_item_pointer(machine, "GBA_CART", this->device().tag(), 0, m_nvram, m_nvram_size/sizeof(UINT32)); + } +} + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// gba_cart_slot_device - constructor +//------------------------------------------------- +gba_cart_slot_device::gba_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, GBA_CART_SLOT, "Game Boy Advance Cartridge Slot", tag, owner, clock, "gba_cart_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this), + m_type(GBA_STD) +{ +} + + +//------------------------------------------------- +// gba_cart_slot_device - destructor +//------------------------------------------------- + +gba_cart_slot_device::~gba_cart_slot_device() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void gba_cart_slot_device::device_start() +{ + m_cart = dynamic_cast<device_gba_cart_interface *>(get_card_device()); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void gba_cart_slot_device::device_config_complete() +{ + // set brief and instance name + update_names(); +} + + +//------------------------------------------------- +// GBA PCB +//------------------------------------------------- + +struct gba_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 gba_slot slot_list[] = +{ + { GBA_STD, "gba_rom" }, + { GBA_SRAM, "gba_sram" }, + { GBA_EEPROM, "gba_eeprom" }, + { GBA_EEPROM4, "gba_eeprom_4k" }, + { GBA_EEPROM64, "gba_eeprom_64k" }, + { GBA_FLASH, "gba_flash" }, + { GBA_FLASH512, "gba_flash_512" }, + { GBA_FLASH1M, "gba_flash_1m" }, +}; + +static int gba_get_pcb_id(const char *slot) +{ + for (int i = 0; i < ARRAY_LENGTH(slot_list); i++) + { + if (!mame_stricmp(slot_list[i].slot_option, slot)) + return slot_list[i].pcb_id; + } + + return 0; +} + +static const char *gba_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 "gba_rom"; +} + + +/*------------------------------------------------- + call load + -------------------------------------------------*/ + +bool gba_cart_slot_device::call_load() +{ + if (m_cart) + { + UINT8 *ROM = (UINT8 *)m_cart->get_rom_base(); + UINT32 cart_size; + + if (software_entry() == NULL) + { + cart_size = length(); + if (cart_size > 0x2000000) + { + seterror(IMAGE_ERROR_UNSPECIFIED, "Attempted loading a cart larger than 32MB"); + return IMAGE_INIT_FAIL; + } + fread(ROM, cart_size); + m_cart->set_rom_size(cart_size); // we store the actual game size... + + m_type = get_cart_type(ROM, cart_size); + } + else + { + const char *pcb_name = get_feature("slot"); + + cart_size = get_software_region_length("rom"); + if (cart_size > 0x2000000) + { + seterror(IMAGE_ERROR_UNSPECIFIED, "Attempted loading a cart larger than 32MB"); + return IMAGE_INIT_FAIL; + } + memcpy(ROM, get_software_region("rom"), cart_size); + m_cart->set_rom_size(cart_size); // we store the actual game size... + + if (pcb_name) + m_type = gba_get_pcb_id(pcb_name); + + //printf("Type: %s\n", gba_get_slot(m_type)); + + mame_printf_info("GBA: Detected (XML) %s\n", pcb_name ? pcb_name : "NONE"); + } + + if (m_type == GBA_SRAM) + m_cart->nvram_alloc(machine(), 0x10000); + + // mirror the ROM + switch (cart_size) + { + case 2 * 1024 * 1024: + memcpy(ROM + 0x200000, ROM, 0x200000); + // intentional fall-through + case 4 * 1024 * 1024: + memcpy(ROM + 0x400000, ROM, 0x400000); + // intentional fall-through + case 8 * 1024 * 1024: + memcpy(ROM + 0x800000, ROM, 0x800000); + // intentional fall-through + case 16 * 1024 * 1024: + memcpy(ROM + 0x1000000, ROM, 0x1000000); + break; + } + + if (m_cart->get_nvram_size()) + battery_load(m_cart->get_nvram_base(), m_cart->get_nvram_size(), 0x00); + + return IMAGE_INIT_PASS; + } + + return IMAGE_INIT_PASS; +} + + +/*------------------------------------------------- + call_unload + -------------------------------------------------*/ + +void gba_cart_slot_device::call_unload() +{ + if (m_cart && m_cart->get_nvram_size()) + battery_save(m_cart->get_nvram_base(), m_cart->get_nvram_size()); +} + + + +/*------------------------------------------------- + call softlist load + -------------------------------------------------*/ + +bool gba_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_cart_type - code to detect NVRAM type from + fullpath + -------------------------------------------------*/ + +INLINE astring gba_chip_string( UINT32 chip ) +{ + astring str; + if (chip == 0) str += "NONE "; + if (chip & GBA_CHIP_EEPROM) str += "EEPROM "; + if (chip & GBA_CHIP_EEPROM_64K) str += "EEPROM_64K "; + if (chip & GBA_CHIP_EEPROM_4K) str += "EEPROM_4K "; + if (chip & GBA_CHIP_FLASH) str += "FLASH "; + if (chip & GBA_CHIP_FLASH_1M) str += "FLASH_1M "; + if (chip & GBA_CHIP_FLASH_512) str += "FLASH_512 "; + if (chip & GBA_CHIP_SRAM) str += "SRAM "; + if (chip & GBA_CHIP_RTC) str += "RTC "; + return str.trimspace(); +} + + +INLINE int gba_chip_has_conflict( UINT32 chip ) +{ + int count1 = 0, count2 = 0; + if (chip & GBA_CHIP_EEPROM) count1++; + if (chip & GBA_CHIP_EEPROM_4K) count1++; + if (chip & GBA_CHIP_EEPROM_64K) count1++; + if (chip & GBA_CHIP_FLASH) count2++; + if (chip & GBA_CHIP_FLASH_1M) count2++; + if (chip & GBA_CHIP_FLASH_512) count2++; + if (chip & GBA_CHIP_SRAM) count2++; + return (count1 + count2) > 1; // if EEPROM + FLASH or EEPROM + SRAM carts exist, change to "(count1 > 1) || (count2 > 1)" +} + + +int gba_cart_slot_device::get_cart_type(UINT8 *ROM, UINT32 len) +{ + UINT32 chip = 0; + int type = GBA_STD; + + // first detect nvram type based on strings inside the file + for (int i = 0; i < len; i++) + { + if (!memcmp(&ROM[i], "EEPROM_V", 8)) + chip |= GBA_CHIP_EEPROM; // should be either GBA_CHIP_EEPROM_4K or GBA_CHIP_EEPROM_64K, but it is not yet possible to automatically detect which one + else if ((!memcmp(&ROM[i], "SRAM_V", 6)) || (!memcmp(&ROM[i], "SRAM_F_V", 8))) // || (!memcmp(&data[i], "ADVANCEWARS", 11))) //advance wars 1 & 2 has SRAM, but no "SRAM_" string can be found inside the ROM space + chip |= GBA_CHIP_SRAM; + else if (!memcmp(&ROM[i], "FLASH1M_V", 9)) + chip |= GBA_CHIP_FLASH_1M; + else if (!memcmp(&ROM[i], "FLASH512_V", 10)) + chip |= GBA_CHIP_FLASH_512; + else if (!memcmp(&ROM[i], "FLASH_V", 7)) + chip |= GBA_CHIP_FLASH; + else if (!memcmp(&ROM[i], "SIIRTC_V", 8)) + chip |= GBA_CHIP_RTC; + } + mame_printf_info("GBA: Detected (ROM) %s\n", gba_chip_string(chip).cstr()); + + // fix for games which return more than one kind of chip: either it is one of the known titles, or we default to no battery + if (gba_chip_has_conflict(chip)) + { + char game_code[5] = { 0 }; + bool resolved = FALSE; + + if (len >= 0xac + 4) + memcpy(game_code, ROM + 0xac, 4); + + mame_printf_info("GBA: Game Code \"%s\"\n", game_code); + + chip &= ~(GBA_CHIP_EEPROM | GBA_CHIP_EEPROM_4K | GBA_CHIP_EEPROM_64K | GBA_CHIP_FLASH | GBA_CHIP_FLASH_1M | GBA_CHIP_FLASH_512 | GBA_CHIP_SRAM); + + // search if it is one of the known titles with NVRAM conflicts + for (int i = 0; i < sizeof(gba_chip_fix_conflict_list) / sizeof(gba_chip_fix_conflict_item); i++) + { + const gba_chip_fix_conflict_item *item = &gba_chip_fix_conflict_list[i]; + if (!strcmp(game_code, item->game_code)) + { + chip |= item->chip; + resolved = TRUE; + break; + } + } + if (!resolved) + mame_printf_warning("GBA: NVRAM is disabled because multiple NVRAM chips were detected!\n"); + } + + // fix for games which are known to require an eeprom with 14-bit addressing (64 kbit) + if (chip & GBA_CHIP_EEPROM) + { + char game_code[5] = { 0 }; + + if (len >= 0xac + 4) + memcpy(game_code, ROM + 0xac, 4); + + mame_printf_info("GBA: Game Code \"%s\"\n", game_code); + + for (int i = 0; i < sizeof(gba_chip_fix_eeprom_list) / sizeof(gba_chip_fix_eeprom_item); i++) + { + const gba_chip_fix_eeprom_item *item = &gba_chip_fix_eeprom_list[i]; + if (!strcmp(game_code, item->game_code)) + { + chip = (chip & ~GBA_CHIP_EEPROM) | GBA_CHIP_EEPROM_64K; + break; + } + } + } + + if (type & GBA_CHIP_RTC) + { + mame_printf_verbose("game has RTC - not emulated at the moment\n"); + chip &= ~GBA_CHIP_RTC; + } + + mame_printf_info("GBA: Emulate %s\n", gba_chip_string(chip).cstr()); + + switch (chip) + { + case GBA_CHIP_SRAM: + type = GBA_SRAM; + break; + case GBA_CHIP_EEPROM: + type = GBA_EEPROM; + break; + case GBA_CHIP_EEPROM_4K: + type = GBA_EEPROM4; + break; + case GBA_CHIP_EEPROM_64K: + type = GBA_EEPROM64; + break; + case GBA_CHIP_FLASH: + type = GBA_FLASH; + break; + case GBA_CHIP_FLASH_512: + type = GBA_FLASH512; + break; + case GBA_CHIP_FLASH_1M: + type = GBA_FLASH1M; + break; + default: + break; + } + + return type; +} +/*------------------------------------------------- + get default card software + -------------------------------------------------*/ + +const char * gba_cart_slot_device::get_default_card_software(const machine_config &config, emu_options &options) +{ + if (open_image_file(options)) + { + const char *slot_string = "gba_rom"; + UINT32 len = core_fsize(m_file); + UINT8 *ROM = global_alloc_array(UINT8, len); + int type; + + core_fread(m_file, ROM, len); + + type = get_cart_type(ROM, len); + slot_string = gba_get_slot(type); + + //printf("type: %s\n", slot_string); + global_free(ROM); + clear(); + + return slot_string; + } + + return software_get_default_slot(config, options, this, "gba_rom"); +} + +/*------------------------------------------------- + read + -------------------------------------------------*/ + +READ32_MEMBER(gba_cart_slot_device::read_rom) +{ + if (m_cart) + return m_cart->read_rom(space, offset, mem_mask); + else + return 0xffffffff; +} + +READ32_MEMBER(gba_cart_slot_device::read_ram) +{ + if (m_cart) + return m_cart->read_ram(space, offset, mem_mask); + else + return 0xffffffff; +} + + +/*------------------------------------------------- + write + -------------------------------------------------*/ + +WRITE32_MEMBER(gba_cart_slot_device::write_ram) +{ + if (m_cart) + m_cart->write_ram(space, offset, data, mem_mask); +} + + +/*------------------------------------------------- + Internal header logging + -------------------------------------------------*/ + +void gba_cart_slot_device::internal_header_logging(UINT8 *ROM, UINT32 len) +{ +} + + +/*------------------------------------------------- + Install ROM - directly point system address map + to the cart ROM region so to avoid the memory + system additional load + -------------------------------------------------*/ + +void gba_cart_slot_device::install_rom() +{ + if (m_cart) + { + astring tempstring; + address_space &space = machine().device<cpu_device>("maincpu")->space(AS_PROGRAM); + space.install_read_bank(0x08000000, 0x09ffffff, 0, 0, "rom1"); + space.install_read_bank(0x0a000000, 0x0bffffff, 0, 0, "rom2"); + space.install_read_bank(0x0c000000, 0x0cffffff, 0, 0, "rom3"); + machine().root_device().membank("rom1")->set_base(machine().root_device().memregion(m_cart->device().subtag(tempstring, "cartridge"))->base()); + machine().root_device().membank("rom2")->set_base(machine().root_device().memregion(m_cart->device().subtag(tempstring, "cartridge"))->base()); + machine().root_device().membank("rom3")->set_base(machine().root_device().memregion(m_cart->device().subtag(tempstring, "cartridge"))->base()); + } +} diff --git a/src/emu/bus/gba/gba_slot.h b/src/emu/bus/gba/gba_slot.h new file mode 100644 index 00000000000..efb3c9077a5 --- /dev/null +++ b/src/emu/bus/gba/gba_slot.h @@ -0,0 +1,577 @@ +#ifndef __GBA_SLOT_H +#define __GBA_SLOT_H + +/*************************************************************************** + TYPE DEFINITIONS + ***************************************************************************/ + + +/* PCB */ +enum +{ + GBA_STD = 0, + GBA_SRAM, + GBA_EEPROM, + GBA_EEPROM4, + GBA_EEPROM64, + GBA_FLASH, + GBA_FLASH512, + GBA_FLASH1M +}; + + +// ======================> device_gba_cart_interface + +class device_gba_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_gba_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~device_gba_cart_interface(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_rom) { return 0xffffffff; } + virtual DECLARE_READ32_MEMBER(read_ram) { return 0xffffffff; } + virtual DECLARE_WRITE32_MEMBER(write_ram) {}; + + void nvram_alloc(running_machine &machine, UINT32 size); + UINT32* get_rom_base() { return m_rom; } + UINT32* get_nvram_base() { return m_nvram; } + UINT32 get_rom_size() { return m_rom_size; } + UINT32 get_nvram_size() { return m_nvram_size; } + void set_rom_size(UINT32 val) { m_rom_size = val; } + + // internal state + UINT32 *m_rom; // this points to the cart rom region + UINT32 *m_nvram; + UINT32 m_rom_size; // this is the actual game size, not the rom region size! + UINT32 m_nvram_size; +}; + + +// ======================> gba_cart_slot_device + +class gba_cart_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + gba_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~gba_cart_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete(); + + // image-level overrides + virtual bool call_load(); + virtual void call_unload(); + virtual bool call_softlist_load(char *swlist, char *swname, rom_entry *start_entry); + + void install_rom(); + + int get_type() { return m_type; } + int get_cart_type(UINT8 *ROM, UINT32 len); + + void setup_ram(UINT8 banks); + void internal_header_logging(UINT8 *ROM, UINT32 len); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 1; } + virtual const option_guide *create_option_guide() const { return NULL; } + virtual const char *image_interface() const { return "gba_cart"; } + virtual const char *file_extensions() const { return "gba,bin"; } + + // slot interface overrides + virtual const char * get_default_card_software(const machine_config &config, emu_options &options); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_rom); + virtual DECLARE_READ32_MEMBER(read_ram); + virtual DECLARE_WRITE32_MEMBER(write_ram); + + +protected: + + int m_type; + device_gba_cart_interface* m_cart; +}; + + + +// device type definition +extern const device_type GBA_CART_SLOT; + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS + ***************************************************************************/ + +#define MCFG_GBA_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, GBA_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + + + +//------------------------------------------------------------------------ +// +// Misc structs to attempt NVRAM identification when loading from fullpath +// +//------------------------------------------------------------------------ + + +#define GBA_CHIP_EEPROM (1 << 0) +#define GBA_CHIP_SRAM (1 << 1) +#define GBA_CHIP_FLASH (1 << 2) +#define GBA_CHIP_FLASH_1M (1 << 3) +#define GBA_CHIP_RTC (1 << 4) +#define GBA_CHIP_FLASH_512 (1 << 5) +#define GBA_CHIP_EEPROM_64K (1 << 6) +#define GBA_CHIP_EEPROM_4K (1 << 7) + + +struct gba_chip_fix_conflict_item +{ + char game_code[5]; + UINT32 chip; +}; + +static const gba_chip_fix_conflict_item gba_chip_fix_conflict_list[] = +{ + { "ABFJ", GBA_CHIP_SRAM }, // 0059 - Breath of Fire - Ryuu no Senshi (JPN) + { "AHMJ", GBA_CHIP_EEPROM_4K }, // 0364 - Dai-Mahjong (JPN) + { "A2GJ", GBA_CHIP_EEPROM_64K }, // 0399 - Advance GT2 (JPN) + { "AK9E", GBA_CHIP_EEPROM_4K }, // 0479 - Medabots AX - Rokusho Version (USA) + { "AK8E", GBA_CHIP_EEPROM_4K }, // 0480 - Medabots AX - Metabee Version (USA) + { "AK9P", GBA_CHIP_EEPROM_4K }, // 0515 - Medabots AX - Rokusho Version (EUR) + { "AGIJ", GBA_CHIP_EEPROM_4K }, // 0548 - Medarot G - Kuwagata Version (JPN) + { "A3DJ", GBA_CHIP_EEPROM_4K }, // 0567 - Disney Sports - American Football (JPN) + { "AF7J", GBA_CHIP_EEPROM_64K }, // 0605 - Tokimeki Yume Series 1 - Ohanaya-san ni Narou! (JPN) + { "AH7J", GBA_CHIP_EEPROM_64K }, // 0617 - Nakayoshi Pet Advance Series 1 - Kawaii Hamster (JPN) + { "AGHJ", GBA_CHIP_EEPROM_4K }, // 0620 - Medarot G - Kabuto Version (JPN) + { "AR8E", GBA_CHIP_SRAM }, // 0727 - Rocky (USA) + { "ALUE", GBA_CHIP_EEPROM_4K }, // 0751 - Super Monkey Ball Jr. (USA) + { "A3DE", GBA_CHIP_EEPROM_4K }, // 0800 - Disney Sports - Football (USA) + { "A87J", GBA_CHIP_EEPROM_64K }, // 0817 - Ohanaya-San Monogatari GBA (JPN) + { "A56J", GBA_CHIP_EEPROM_64K }, // 0827 - DokiDoki Cooking Series 1 - Komugi-chan no Happy Cake (JPN) + { "AUSJ", GBA_CHIP_FLASH }, // 0906 - One Piece - Mezase! King of Berries (JPN) + { "ANTJ", GBA_CHIP_SRAM }, // 0950 - Nippon Pro Mahjong Renmei Kounin - Tetsuman Advance (JPN) + { "A8OJ", GBA_CHIP_EEPROM_64K }, // 0988 - DokiDoki Cooking Series 2 - Gourmet Kitchen - Suteki na Obentou (JPN) + { "AK8P", GBA_CHIP_EEPROM_4K }, // 1022 - Medabots AX - Metabee Version (EUR) + { "A6OJ", GBA_CHIP_EEPROM_64K }, // 1092 - Onimusha Tactics (JPN) + { "A6OE", GBA_CHIP_EEPROM_64K }, // 1241 - Onimusha Tactics (USA) + { "A6OP", GBA_CHIP_EEPROM_64K }, // 1288 - Onimusha Tactics (EUR) + { "BKME", GBA_CHIP_EEPROM_4K }, // 1545 - Kim Possible 2 - Drakken's Demise (USA) + { "BDKJ", GBA_CHIP_EEPROM_64K }, // 1555 - Digi Communication 2 in 1 Datou! Black Gemagema Dan (JPN) + { "BR4J", GBA_CHIP_FLASH }, // 1586 - Rockman EXE 4.5 - Real Operation (JPN) + { "BG8J", GBA_CHIP_EEPROM_64K }, // 1853 - Ganbare! Dodge Fighters (JPN) + { "AROP", GBA_CHIP_EEPROM_4K }, // 1862 - Rocky (EUR) + { "A2YE", GBA_CHIP_SRAM }, // 1906 - Top Gun - Combat Zones (USA) + { "BKMJ", GBA_CHIP_EEPROM_4K }, // 2039 - Kim Possible (JPN) + { "BKEJ", GBA_CHIP_EEPROM_64K }, // 2047 - Konjiki no Gashbell - The Card Battle for GBA (JPN) + { "BKMP", GBA_CHIP_EEPROM_4K }, // 2297 - Kim Possible 2 - Drakken's Demise (EUR) + { "BUHJ", GBA_CHIP_EEPROM_4K }, // 2311 - Ueki no Housoku Shinki Sakuretsu! Nouryokumono Battle (JPN) + { "BYUJ", GBA_CHIP_EEPROM_64K }, // 2322 - Yggdra Union (JPN) +}; + +struct gba_chip_fix_eeprom_item +{ + char game_code[5]; +}; + +static const gba_chip_fix_eeprom_item gba_chip_fix_eeprom_list[] = +{ + // gba scan no. 7 + { "AKTJ" }, // 0145 - Hello Kitty Collection - Miracle Fashion Maker (JPN) + { "AISP" }, // 0185 - International Superstar Soccer (EUR) + { "AKGP" }, // 0204 - Mech Platoon (EUR) + { "AX2E" }, // 0207 - Dave Mirra Freestyle BMX 2 (USA) + { "AASJ" }, // 0234 - World Advance Soccer - Shouri heno Michi (JPN) + { "AA2J" }, // 0237 - Super Mario World - Super Mario Advance 2 (JPN) + { "AJWJ" }, // 0242 - Jikkyou World Soccer Pocket (JPN) + { "AABE" }, // 0244 - American Bass Challenge (USA) + { "AWXJ" }, // 0254 - ESPN Winter X-Games Snowboarding 2002 (JPN) + { "AALJ" }, // 0259 - Kidou Tenshi Angelic Layer - Misaki to Yume no Tenshi-tachi (JPN) + { "AKGE" }, // 0263 - Mech Platoon (USA) + { "AGLJ" }, // 0273 - Tomato Adventure (JPN) + { "AWIJ" }, // 0274 - Hyper Sports 2002 Winter (JPN) + { "APNJ" }, // 0286 - Pinky Monkey Town (JPN) + { "AA2E" }, // 0288 - Super Mario World - Super Mario Advance 2 (USA) + { "AX2P" }, // 0293 - Dave Mirra Freestyle BMX 2 (EUR) + { "AMGP" }, // 0296 - ESPN Great Outdoor Games - Bass Tournament (EUR) + { "AMHJ" }, // 0308 - Bomberman Max 2 - Bomberman Version (JPN) + { "AGNJ" }, // 0311 - Goemon - New Age Shutsudou! (JPN) + { "AMYJ" }, // 0324 - Bomberman Max 2 - Max Version (JPN) + { "AT3E" }, // 0326 - Tony Hawk's Pro Skater 3 (USA) + { "AHHE" }, // 0327 - High Heat - Major League Baseball 2003 (USA) + { "ANLE" }, // 0328 - NHL 2002 (USA) + { "AAGJ" }, // 0345 - Angelique (JPN) + { "ABJP" }, // 0351 - Broken Sword - The Shadow of the Templars (EUR) + { "AKVJ" }, // 0357 - K-1 Pocket Grand Prix (JPN) + { "AKGJ" }, // 0361 - Kikaika Guntai - Mech Platoon (JPN) + { "ADDJ" }, // 0362 - Diadroids World - Evil Teikoku no Yabou (JPN) + { "ABJE" }, // 0365 - Broken Sword - The Shadow of the Templars (USA) + { "AABP" }, // 0380 - Super Black Bass Advance (EUR) + { "AA2P" }, // 0390 - Super Mario World - Super Mario Advance 2 (EUR) + { "A2GJ" }, // 0399 - Advance GT2 (JPN) + { "AEWJ" }, // 0400 - Ui-Ire - World Soccer Winning Eleven (JPN) + { "ADPJ" }, // 0417 - Doraemon - Dokodemo Walker (JPN) + { "AN5J" }, // 0420 - Kawa no Nushi Tsuri 5 - Fushigi no Mori Kara (JPN) + { "ACBJ" }, // 0421 - Gekitou! Car Battler Go!! (JPN) + { "AHIJ" }, // 0426 - Hitsuji no Kimochi (JPN) + { "ATFP" }, // 0429 - Alex Ferguson's Player Manager 2002 (EUR) + { "AFUJ" }, // 0431 - Youkaidou (JPN) + { "AEPP" }, // 0435 - Sheep (EUR) + { "AMHE" }, // 0442 - Bomberman Max 2 - Blue Advance (USA) + { "AMYE" }, // 0443 - Bomberman Max 2 - Red Advance (USA) + { "AT3F" }, // 0457 - Tony Hawk's Pro Skater 3 (FRA) + { "ARJJ" }, // 0497 - Custom Robo GX (JPN) + { "AFCJ" }, // 0521 - RockMan & Forte (JPN) + { "ANJE" }, // 0528 - Madden NFL 2003 (USA) + { "AN7J" }, // 0533 - Famista Advance (JPN) + { "ATYJ" }, // 0540 - Gambler Densetsu Tetsuya - Yomigaeru Densetsu (JPN) + { "AXBJ" }, // 0551 - Black Matrix Zero (JPN) + { "A3AE" }, // 0580 - Yoshi's Island - Super Mario Advance 3 (USA) + { "A3AJ" }, // 0582 - Super Mario Advance 3 (JPN) + { "AZUJ" }, // 0595 - Street Fighter Zero 3 - Upper (JPN) + { "ALOE" }, // 0600 - The Lord of the Rings - The Fellowship of the Ring (USA) + { "A2SE" }, // 0602 - Spyro 2 - Season of Flame (USA) + { "AF7J" }, // 0605 - Tokimeki Yume Series 1 - Ohanaya-san ni Narou! (JPN) + { "A3AP" }, // 0610 - Yoshi's Island - Super Mario Advance 3 (EUR) + { "AH7J" }, // 0617 - Nakayoshi Pet Advance Series 1 - Kawaii Hamster (JPN) + { "AI7J" }, // 0618 - Nakayoshi Pet Advance Series 2 - Kawaii Koinu (JPN) + { "AN3J" }, // 0619 - Nakayoshi Pet Advance Series 3 - Kawaii Koneko (JPN) + { "AAPJ" }, // 0632 - Metalgun Slinger (JPN) + { "A2JJ" }, // 0643 - J.League - Winning Eleven Advance 2002 (JPN) + { "AHXJ" }, // 0649 - High Heat - Major League Baseball 2003 (JPN) + { "AHAJ" }, // 0651 - Hamster Paradise Advance (JPN) + { "APUJ" }, // 0653 - PukuPuku Tennen Kairanban (JPN) + { "A2SP" }, // 0673 - Spyro 2 - Season of Flame (EUR) + { "AN9J" }, // 0675 - Tales of the World - Narikiri Dungeon 2 (JPN) + { "ACBE" }, // 0683 - Car Battler Joe (USA) + { "AT6E" }, // 0693 - Tony Hawk's Pro Skater 4 (USA) + { "ALOP" }, // 0702 - The Lord of the Rings - The Fellowship of the Ring (EUR) + { "A63J" }, // 0710 - Kawaii Pet Shop Monogatari 3 (JPN) + { "AAXJ" }, // 0748 - Fantastic Maerchen - Cake-yasan Monogatari (JPN) + { "AZLE" }, // 0763 - The Legend of Zelda - A Link to the Past & Four Swords (USA) + { "AZUP" }, // 0765 - Street Fighter Alpha 3 - Upper (EUR) + { "AJKJ" }, // 0769 - Jikkyou World Soccer Pocket 2 (JPN) + { "AB3E" }, // 0781 - Dave Mirra Freestyle BMX 3 (USA) + { "A2IJ" }, // 0791 - Magi Nation (JPN) + { "AK7J" }, // 0792 - Klonoa Heroes - Densetsu no Star Medal (JPN) + { "A2HJ" }, // 0794 - Hajime no Ippo - The Fighting! (JPN) + { "ALNE" }, // 0795 - Lunar Legend (USA) + { "AUCJ" }, // 0808 - Uchuu Daisakusen Choco Vader - Uchuu Kara no Shinryakusha (JPN) + { "A59J" }, // 0809 - Toukon Heat (JPN) + { "ALJE" }, // 0815 - Sea Trader - Rise of Taipan (USA) + { "A87J" }, // 0817 - Ohanaya-San Monogatari GBA (JPN) + { "A56J" }, // 0827 - DokiDoki Cooking Series 1 - Komugi-chan no Happy Cake (JPN) + { "AHZJ" }, // 0830 - Higanbana (JPN) + { "A8BP" }, // 0832 - Medabots - Metabee Version (EUR) + { "A2OJ" }, // 0833 - K-1 Pocket Grand Prix 2 (JPN) + { "AY2P" }, // 0843 - International Superstar Soccer Advance (EUR) + { "ANSJ" }, // 0845 - Marie, Elie & Anis no Atelier - Soyokaze Kara no Dengon (JPN) + { "ACOJ" }, // 0865 - Manga-ka Debut Monogatari (JPN) + { "AZLP" }, // 0870 - The Legend of Zelda - A Link to the Past & Four Swords (EUR) + { "AWKJ" }, // 0879 - Wagamama Fairy Mirumo de Pon! - Ougon Maracas no Densetsu (JPN) + { "AZUE" }, // 0886 - Street Fighter Alpha 3 - Upper (USA) + { "AZLJ" }, // 0887 - Zelda no Densetsu - Kamigami no Triforce & 4tsu no Tsurugi (JPN) + { "A6ME" }, // 0889 - MegaMan & Bass (USA) + { "A64J" }, // 0915 - Shimura Ken no Baka Tonosama (JPN) + { "A9HJ" }, // 0917 - Dragon Quest Monsters - Caravan Heart (JPN) + { "AMHP" }, // 0929 - Bomberman Max 2 - Blue Advance (EUR) + { "AMYP" }, // 0930 - Bomberman Max 2 - Red Advance (EUR) + { "AMGJ" }, // 0943 - Exciting Bass (JPN) + { "A5KJ" }, // 0946 - Medarot 2 Core - Kabuto Version (JPN) + { "A4LJ" }, // 0949 - Sylvania Family 4 - Meguru Kisetsu no Tapestry (JPN) + { "A2VJ" }, // 0955 - Kisekko Gurumi - Chesty to Nuigurumi-tachi no Mahou no Bouken (JPN) + { "A5QJ" }, // 0956 - Medarot 2 Core - Kuwagata Version (JPN) + { "AZBJ" }, // 0958 - Bass Tsuri Shiyouze! (JPN) + { "AO2J" }, // 0961 - Oshare Princess 2 (JPN) + { "AB4J" }, // 0965 - Summon Night - Craft Sword Monogatari (JPN) + { "AZAJ" }, // 0971 - Azumanga Daiou Advance (JPN) + { "AF3J" }, // 0974 - Zero One (JPN) + { "A8OJ" }, // 0988 - DokiDoki Cooking Series 2 - Gourmet Kitchen - Suteki na Obentou (JPN) + { "AT3D" }, // 1016 - Tony Hawk's Pro Skater 3 (GER) + { "A6MP" }, // 1031 - MegaMan & Bass (EUR) + { "ANNJ" }, // 1032 - Gekitou Densetsu Noah - Dream Management (JPN) + { "AFNJ" }, // 1036 - Angel Collection - Mezase! Gakuen no Fashion Leader (JPN) + { "ALFP" }, // 1041 - Dragon Ball Z - The Legacy of Goku II (EUR) + { "A9TJ" }, // 1055 - Metal Max 2 Kai (JPN) + { "ALFE" }, // 1070 - Dragon Ball Z - The Legacy of Goku II (USA) + { "BHCJ" }, // 1074 - Hamster Monogatari Collection (JPN) + { "BKKJ" }, // 1075 - Minna no Shiiku Series - Boku no Kabuto-Kuwagata (JPN) + { "BKIJ" }, // 1083 - Nakayoshi Pet Advance Series 4 - Kawaii Koinu Kogata Inu (JPN) + { "BGBJ" }, // 1084 - Get! - Boku no Mushi Tsukamaete (JPN) + { "A82J" }, // 1085 - Hamster Paradise - Pure Heart (JPN) + { "U3IJ" }, // 1087 - Bokura no Taiyou - Taiyou Action RPG (JPN) + { "A6OJ" }, // 1092 - Onimusha Tactics (JPN) + { "AN8J" }, // 1102 - Tales of Phantasia (JPN) + { "AC4J" }, // 1104 - Meitantei Conan - Nerawareta Tantei (JPN) + { "A8ZJ" }, // 1108 - Shin Megami Tensei Devil Children - Puzzle de Call! (JPN) + { "BGMJ" }, // 1113 - Gensou Maden Saiyuuki - Hangyaku no Toushin-taishi (JPN) + { "BMDE" }, // 1115 - Madden NFL 2004 (USA) + { "BO3J" }, // 1141 - Oshare Princess 3 (JPN) + { "U3IE" }, // 1145 - Boktai - The Sun is in Your Hand (USA) + { "BMRJ" }, // 1194 - Matantei Loki Ragnarok - Gensou no Labyrinth (JPN) + { "BLME" }, // 1204 - Lizzie McGuire (USA) + { "AOWE" }, // 1208 - Spyro - Attack of the Rhynocs (USA) + { "BTOE" }, // 1209 - Tony Hawk's Underground (USA) + { "BFJE" }, // 1212 - Frogger's Journey - The Forgotten Relic (USA) + { "A88P" }, // 1229 - Mario & Luigi - Superstar Saga (EUR) + { "BEYP" }, // 1236 - Beyblade VForce - Ultimate Blader Jam (EUR) + { "A85J" }, // 1239 - Sanrio Puroland All Characters (JPN) + { "BMZJ" }, // 1240 - Zooo (JPN) + { "A6OE" }, // 1241 - Onimusha Tactics (USA) + { "AOWP" }, // 1253 - Spyro Adventure (EUR) + { "A88E" }, // 1260 - Mario & Luigi - Superstar Saga (USA) + { "BEYE" }, // 1262 - Beyblade VForce - Ultimate Blader Jam (USA) + { "BCME" }, // 1264 - CIMA - The Enemy (USA) + { "A88J" }, // 1266 - Mario & Luigi RPG (JPN) + { "A5CP" }, // 1269 - Sim City 2000 (EUR) + { "BGAJ" }, // 1277 - SD Gundam G Generation (JPN) + { "A6OP" }, // 1288 - Onimusha Tactics (EUR) + { "BLMP" }, // 1289 - Lizzie McGuire (EUR) + { "ASIE" }, // 1295 - The Sims - Bustin' Out (USA) + { "BISJ" }, // 1299 - Koinu-Chan no Hajimete no Osanpo - Koinu no Kokoro Ikusei Game (JPN) + { "BK3J" }, // 1305 - Card Captor Sakura - Sakura Card de Mini Game (JPN) + { "A4GJ" }, // 1306 - Konjiki no Gashbell!! - Unare! Yuujou no Zakeru (JPN) + { "BTAJ" }, // 1315 - Astro Boy - Tetsuwan Atom (JPN) + { "BS5J" }, // 1322 - Sylvanian Family - Yousei no Stick to Fushigi no Ki (JPN) + { "A5CE" }, // 1326 - Sim City 2000 (USA) + { "B4PJ" }, // 1342 - The Sims (JPN) + { "BDTJ" }, // 1383 - Downtown - Nekketsu Monogatari EX (JPN) + { "B08J" }, // 1391 - One Piece - Going Baseball (JPN) + { "AWUP" }, // 1394 - Sabre Wulf (EUR) + { "BRPJ" }, // 1421 - Liliput Oukoku (JPN) + { "BPNJ" }, // 1435 - Pika Pika Nurse Monogatari - Nurse Ikusei Game (JPN) + { "BP3J" }, // 1446 - Pia Carrot he Youkoso!! 3.3 (JPN) + { "BKCJ" }, // 1461 - Crayon Shin-Chan - Arashi no Yobu Cinema-Land no Daibouken! (JPN) + { "BGNJ" }, // 1464 - Battle Suit Gundam Seed - Battle Assault (JPN) + { "U3IP" }, // 1465 - Boktai - The Sun is in Your Hand (EUR) + { "BDTE" }, // 1484 - River City Ransom EX (USA) + { "BHTE" }, // 1485 - Harry Potter and the Prisoner of Azkaban (USA) + { "FZLE" }, // 1494 - Classic NES Series - The Legend of Zelda (USA) + { "FEBE" }, // 1499 - Classic NES Series - ExciteBike (USA) + { "BUCE" }, // 1505 - Ultimate Card Games (USA) + { "AWUE" }, // 1511 - Sabre Wulf (USA) + { "B2DP" }, // 1522 - Donkey Kong Country 2 (EUR) + { "BHTJ" }, // 1528 - Harry Potter to Azkaban no Shuujin (JPN) + { "A5SJ" }, // 1534 - Oshare Wanko (JPN) + { "B2DJ" }, // 1541 - Super Donkey Kong 2 (JPN) + { "BTAE" }, // 1551 - Astro Boy - Omega Factor (USA) + { "BKOJ" }, // 1553 - Kaiketsu Zorori to Mahou no Yuuenchi (JPN) + { "BDKJ" }, // 1555 - Digi Communication 2 in 1 Datou! Black Gemagema Dan (JPN) + { "U32J" }, // 1567 - Zoku Bokura no Taiyou - Taiyou Shounen Django (JPN) + { "ALFJ" }, // 1573 - Dragon Ball Z - The Legacy of Goku II - International (JPN) + { "BGHJ" }, // 1575 - Gakkou no Kaidan - Hyakuyobako no Fuuin (JPN) + { "BZOJ" }, // 1576 - Zero One SP (JPN) + { "BDXJ" }, // 1587 - B-Densetsu! Battle B-Daman Moero! B-Kon (JPN) + { "BNBJ" }, // 1589 - Himawari Doubutsu Byouin Pet no Oishasan (JPN) + { "BMFE" }, // 1595 - Madden NFL 2005 (USA) + { "FMRJ" }, // 1599 - Famicom Mini Series 23 - Metroid (JPN) + { "FPTJ" }, // 1600 - Famicom Mini Series 24 - Hikari Shinwa - Palutena no Kagame (JPN) + { "FLBJ" }, // 1601 - Famicom Mini Series 25 - The Legend of Zelda 2 - Link no Bouken (JPN) + { "FSDJ" }, // 1606 - Famicom Mini Series 30 - SD Gundam World - Gachapon Senshi Scramble Wars (JPN) + { "BSKJ" }, // 1611 - Summon Night - Craft Sword Monogatari 2 (JPN) + { "BG3E" }, // 1628 - Dragon Ball Z - Buu's Fury (USA) + { "BECJ" }, // 1644 - Angel Collection 2 - Pichimo ni Narou (JPN) + { "B2TE" }, // 1672 - Tony Hawk's Underground 2 (USA) + { "BTYE" }, // 1689 - Ty the Tasmanian Tiger 2 - Bush Rescue (USA) + { "BT2E" }, // 1695 - Teenage Mutant Ninja Turtles 2 - Battlenexus (USA) + { "U32E" }, // 1697 - Boktai 2 - Solar Boy Django (USA) + { "BFDJ" }, // 1708 - Fruit Mura no Doubutsu Tachi (JPN) + { "BPQJ" }, // 1717 - PukuPuku Tennen Kairanban - Koi no Cupid Daisakusen (JPN) + { "BZMJ" }, // 1721 - The Legend of Zelda - Fushigi no Boushi (JPN) + { "FLBE" }, // 1723 - Classic NES Series - Zelda II - The Adventure of Link (USA) + { "BZMP" }, // 1736 - The Legend of Zelda - The Minish Cap (EUR) + { "B2DE" }, // 1754 - Donkey Kong Country 2 (USA) + { "BT2P" }, // 1758 - Teenage Mutant Ninja Turtles 2 - Battle Nexus (EUR) + { "BB2E" }, // 1759 - Beyblade G-Revolution (USA) + { "BRGE" }, // 1761 - Yu-Yu-Hakusho - Tournament Tactics (USA) + { "BFJJ" }, // 1766 - Frogger - Kodaibunmei no Nazo (JPN) + { "BB2P" }, // 1776 - Beyblade G-Revolution (EUR) + { "BSFJ" }, // 1791 - Sylvania Family - Fashion Designer ni Naritai (JPN) + { "BPIE" }, // 1798 - It's Mr Pants (USA) + { "B3PJ" }, // 1809 - Pukupuku Tennen Kairanban Youkoso Illusion Land (JPN) + { "BHDJ" }, // 1812 - Hello Idol Debut (JPN) + { "BKUJ" }, // 1823 - Shingata Medarot - Kuwagata Version (JPN) + { "BKVJ" }, // 1824 - Shingata Medarot - Kabuto Version (JPN) + { "BLIJ" }, // 1825 - Little Patissier Cake no Oshiro (JPN) + { "B3TJ" }, // 1833 - Tales of the World - Narikiri Dungeon 3 (JPN) + { "B2KJ" }, // 1836 - Kiss x Kiss - Seirei Gakuen (JPN) + { "A9BE" }, // 1837 - Medabots - Rokusho Version (USA) + { "BZME" }, // 1842 - The Legend of Zelda - The Minish Cap (USA) + { "B8MJ" }, // 1845 - Mario Party Advance (JPN) + { "A8BE" }, // 1871 - Medabots - Metabee Version (USA) + { "BTAP" }, // 1879 - Astro Boy - Omega Factor (EUR) + { "FSRJ" }, // 1916 - Famicom Mini Series - Dai 2 Ji Super Robot Taisen (JPN) + { "B8ME" }, // 1931 - Mario Party Advance (USA) + { "BO8K" }, // 1938 - One Piece - Going Baseball Haejeok Yaku (KOR) + { "B4ZJ" }, // 1941 - Rockman Zero 4 (JPN) + { "BQAJ" }, // 1953 - Meitantei Conan Akatsuki no Monument (JPN) + { "BIPJ" }, // 1956 - One Piece - Dragon Dream (JPN) + { "BQBJ" }, // 1970 - Konchu Monster Battle Master (JPN) + { "BQSJ" }, // 1971 - Konchu Monster Battle Stadium (JPN) + { "BWXJ" }, // 1982 - Wanko Mix Chiwanko World (JPN) + { "A9TJ" }, // 1984 - Metal Max 2 - Kai Version (JPN) + { "U32P" }, // 1992 - Boktai 2 - Solar Boy Django (EUR) + { "B4RJ" }, // 2005 - Shikakui Atama wo Marukusuru Advance - Kokugo Sansu Rika Shakai (JPN) + { "B4KJ" }, // 2007 - Shikakui Atama wo Marukusuru Advance - Kanji Keisan (JPN) + { "BFCJ" }, // 2019 - Fantasic Children (JPN) + { "BCSP" }, // 2020 - 2 in 1 - V-Rally 3 - Stuntman (EUR) + { "BM2J" }, // 2024 - Momotarou Densetsu G Gold Deck wo Tsukure! (JPN) + { "BEJJ" }, // 2026 - Erementar Gerad (JPN) + { "B5AP" }, // 2034 - Crash & Spyro - Super Pack Volume 1 (EUR) + { "B52P" }, // 2035 - Crash & Spyro - Super Pack Volume 2 (EUR) + { "BFMJ" }, // 2046 - Futari wa Precure Max Heart Maji! Maji! Fight de IN Janai (JPN) + { "BKEJ" }, // 2047 - Konjiki no Gashbell - The Card Battle for GBA (JPN) + { "U33J" }, // 2048 - Shin Bokura no Taiyou - Gyakushuu no Sabata (JPN) + { "BHFJ" }, // 2050 - Twin Series 4 - Ham Ham Monster EX + Fantasy Puzzle Hamster Monogatari (JPN) + { "BMWJ" }, // 2051 - Twin Series 5 - Wan Wan Meitantei EX + Mahou no Kuni no Keaki-Okusan Monogatari (JPN) + { "BMZP" }, // 2055 - Zooo (EUR) + { "B6ME" }, // 2057 - Madden NFL 06 (USA) + { "BT4E" }, // 2058 - Dragon Ball GT - Transformation (USA) + { "B2OJ" }, // 2071 - Pro Mahjong - Tsuwamono GBA (JPN) + { "BX4E" }, // 2079 - 2 in 1 - Tony Hawk's Underground + Kelly Slater's Pro Surfer (USA) + { "BRLE" }, // 2097 - Rebelstar - Tactical Command (USA) + { "BX5P" }, // 2100 - Rayman - 10th Anniversary (EUR) + { "B4ZP" }, // 2108 - MegaMan Zero 4 (EUR) + { "BX5E" }, // 2123 - Rayman - 10th Anniversary (USA) + { "BGXJ" }, // 2143 - Gunstar Super Heroes (JPN) + { "B4ZE" }, // 2144 - Megaman Zero 4 (USA) + { "B53P" }, // 2164 - Crash & Spyro - Super Pack Volume 3 (EUR) + { "B26E" }, // 2169 - World Poker Tour (USA) + { "BH9E" }, // 2172 - Tony Hawk's American Sk8land (USA) + { "BHGE" }, // 2177 - Gunstar Super Heroes (USA) + { "BCMJ" }, // 2178 - Frontier Stories (JPN) + { "BUZE" }, // 2182 - Ultimate Arcade Games (USA) + { "BTVE" }, // 2198 - Ty the Tasmanian Tiger 3 - Night of the Quinkan (USA) + { "BHGP" }, // 2199 - Gunstar Future Heroes (EUR) + { "BH9X" }, // 2214 - Tony Hawk's American Sk8land (EUR) + { "BWIP" }, // 2231 - Win X Club (EUR) + { "BQTP" }, // 2232 - My Pet Hotel (EUR) + { "B4LJ" }, // 2245 - Sugar Sugar Une - Heart Gaippai! Moegi Gakuen (JPN) + { "B3CJ" }, // 2249 - Summon Night Craft Sword Monogatari - Hajimari no Ishi (JPN) + { "A4GE" }, // 2260 - ZatchBell! - Electric Arena (USA) + { "BLFE" }, // 2264 - 2 in 1 - Dragon Ball Z 1 and 2 (USA) + { "BO2J" }, // 2272 - Ochainu no Bouken Jima (JPN) + { "BWIE" }, // 2276 - WinX Club (USA) + { "BGQE" }, // 2279 - Greg Hastings' Tournament Paintball Max'd (USA) + { "BURE" }, // 2298 - Paws & Claws - Pet Resort (USA) + { "A3AC" }, // 2303 - Yaoxi Dao (CHN) + { "AN8E" }, // 2305 - Tales of Phantasia (USA) + { "BZWJ" }, // 2309 - Akagi (JPN) + { "BT8P" }, // 2316 - Teenage Mutant Ninja Turtles Double Pack (EUR) + { "BYUJ" }, // 2322 - Yggdra Union (JPN) + { "AN3E" }, // 2324 - Catz (USA) + { "AN8P" }, // 2325 - Tales of Phantasia (EUR) + { "AA2C" }, // 2332 - Chaoji Maliou Shijie (CHN) + { "BWOP" }, // 2333 - World Poker Tour (EUR) + { "BKCS" }, // 2334 - Shinchan - Aventuras en Cineland (ESP) + { "BC2J" }, // 2341 - Crayon Shin chan - Densetsu wo Yobu Omake no Miyako Shockgaan (JPN) + { "BUOJ" }, // 2345 - Minna no Soft Series - Numpla Advance (JPN) + { "AN3J" }, // 2347 - Minna no Soft Series - Kawaii Koneko (JPN) + { "BUOE" }, // 2366 - Dr. Sudoku (USA) + { "B8SE" }, // 2368 - Spyro Superpack - Season of Ice + Season of Flame (USA) + { "U32J" }, // 2369 - Zoku Bokura no Taiyou - Taiyou Shounen Django (v01) (JPN) + { "BBMJ" }, // 2388 - B-Legend! Battle B-Daman - Moero! B-Damashi!! (JPN) + { "B53E" }, // 2395 - Crash & Spyro Superpack - Ripto's Rampage + The Cortex Conspiracy (USA) + { "BH9P" }, // 2399 - Tony Hawk's American Sk8land (EUR) + { "BAQP" }, // 2406 - Premier Action Soccer (EUR) + { "AB4E" }, // 2432 - Summon Night - Swordcraft Story (USA) + { "BBYE" }, // 2436 - Barnyard (USA) + { "BDXE" }, // 2438 - Battle B-Daman (USA) + { "B7ME" }, // 2446 - Madden NFL 07 (USA) + { "BUFE" }, // 2447 - 2 Games in 1 - Dragon Ball Z - Buu's Fury + Dragon Ball GT - Transformation (USA) + { "BUOP" }, // 2449 - Dr. Sudoku (EUR) + { "BBYX" }, // 2461 - Barnyard (EUR) + { "BFEE" }, // 2466 - Dogz - Fashion (USA) + { "AN3X" }, // 2468 - Catz (EUR) + { "BBME" }, // 2481 - Battle B-Daman - Fire Spirits (USA) + { "BQZE" }, // 2487 - Avatar - The Last Airbender (USA) + { "BXFE" }, // 2498 - Bratz - Forever Diamondz (USA) + { "BT8E" }, // 2500 - Teenage Mutant Ninja Turtles Double Pack (USA) + { "B3YE" }, // 2504 - The Legend of Spyro - A New Beginning (USA) + { "BSKE" }, // 2505 - Summon Night - Swordcraft Story 2 (USA) + { "BHBP" }, // 2513 - Best Friends - Hunde & Katzen (EUR) + { "B3YP" }, // 2519 - The Legend Of Spyro - A New Beginning (EUR) + { "BXFD" }, // 2520 - Bratz - Forever Diamondz (GER) + { "BRLP" }, // 2532 - Rebelstar - Tactical Command (EUR) + { "BENP" }, // 2560 - Eragon (EUR) + { "BENE" }, // 2561 - Eragon (USA) + { "BYUE" }, // 2573 - Yggdra Union - We'll Never Fight Alone (USA) + { "BFRP" }, // 2588 - My Animal Centre in Africa (EUR) + { "BFQE" }, // 2606 - Mazes of Fate (USA) + { "BQZP" }, // 2607 - Avatar - The Legend of Aang (EUR) + { "BFEP" }, // 2613 - Dogz Fashion (EUR) + { "BC2S" }, // 2631 - Shinchan contra los Munecos de Shock Gahn (ESP) + { "BXFP" }, // 2640 - Bratz - Forever Diamondz (EUR) + { "BEFP" }, // 2652 - Best Friends - My Horse (EUR) + { "BNBE" }, // 2695 - Petz Vet (USA) + { "BIME" }, // 2696 - Dogz 2 (USA) + { "BQTX" }, // 2710 - Mijn Dierenpension (EUR) + { "BIMP" }, // 2720 - Dogz 2 (EUR) + { "BIMX" }, // 2727 - Dogz 2 (EUR) + { "BHUE" }, // 2730 - Horsez (USA) + { "BQTF" }, // 2732 - Lea - Passion Veterinaire (FRA) + { "BJPP" }, // 2770 - Harry Potter Collection (EUR) + { "BEFE" }, // 2772 - Let's Ride - Friends Forever (USA) + { "BHBE" }, // 2774 - Best Friends - Dogs & Cats (USA) + { "BYUP" }, // 2781 - Yggdra Union - We'll Never Fight Alone (EUR) + { "ACOJ" }, // 2787 - Manga-ka Debut Monogatari (v01) (JPN) + { "BFDJ" }, // 2789 - Fruit Mura no Doubutsu Tachi (v02) (JPN) + // gba scan no. 8 + { "AYSJ" }, // 0229 - Gakkou wo Tsukurou!! Advance (JPN) + { "ASNJ" }, // 0260 - Sansara Naga 1x2 (JPN) + { "ACTX" }, // 0265 - Creatures (EUR) + { "ASFJ" }, // 0359 - Slot! Pro Advance - Takarabune & Ooedo Sakurafubuki 2 (JPN) + { "ABGJ" }, // 0404 - Sweet Cookie Pie (JPN) + { "ARNJ" }, // 0615 - Harukanaru Toki no Naka de (JPN) + { "AOPJ" }, // 0646 - Oshare Princess (JPN) + { "AHVJ" }, // 0664 - Nakayoshi Youchien - Sukoyaka Enji Ikusei Game (JPN) + { "AYCE" }, // 0758 - Phantasy Star Collection (USA) + { "AYCP" }, // 0877 - Phantasy Star Collection (EUR) + { "ATBJ" }, // 0948 - Slot! Pro 2 Advance - GoGo Juggler & New Tairyou (JPN) + { "A83J" }, // 1012 - Hamster Monogatari 3 GBA (JPN) + { "AEHJ" }, // 1014 - Puzzle & Tantei Collection (JPN) + { "BWDJ" }, // 1114 - Wan Nyan Doubutsu Byouin (JPN) + { "BKZE" }, // 1138 - Banjo-Kazooie - Grunty's Revenge (USA) + { "FZLJ" }, // 1369 - Famicom Mini Series 5 - Zelda no Denzetsu 1 (JPN) + { "BPVP" }, // 1738 - Pferd & Pony - Mein Pferdehof (EUR) + { "ACTY" }, // 1763 - Creatures (EUR) + { "BITJ" }, // 1811 - Onmyou Taisenki Zeroshik (JPN) + { "BOVJ" }, // 1848 - Bouken-Ou Beet - Busters Road (JPN) + { "BT3J" }, // 1852 - Tantei Jinguuji Saburou Shiroi Kage no Syoujyo (JPN) + { "BG8J" }, // 1853 - Ganbare! Dodge Fighters (JPN) + { "BLDS" }, // 1919 - 2 Games in 1 - Lizzie McGuire - Disney Princesas (ESP) + { "BLDP" }, // 1934 - 2 Games in 1 - Lizzie McGuire - Disney Princess (EUR) + { "B8MP" }, // 1993 - Mario Party Advance (EUR) + { "BYPP" }, // 2155 - Horse & Pony - Let`s Ride 2 (EUR) + { "B8AE" }, // 2174 - Crash Superpack - N-Tranced + Nitro Kart (USA) + { "BL9E" }, // 2329 - Let's Ride! Dreamer (USA) + { "B34E" }, // 2331 - Let's Ride! - Sunshine Stables (USA) + { "BQVP" }, // 2414 - Meine Tierarztpraxis (EUR) + { "BHUP" }, // 2480 - Horse and Pony - My Stud Farm (EUR) + { "BPVX" }, // 2645 - Pippa Funnell - Stable Adventures (EUR) + { "BYPX" }, // 2653 - Pippa Funell 2 (EUR) + { "BQVX" }, // 2711 - Mijn Dierenpraktijk (EUR) + { "BPVY" }, // 2712 - Paard & Pony - Mijn Manege (EUR) + { "BYPY" }, // 2713 - Paard & Pony - Paard in Galop (EUR) + { "B54E" }, // 2757 - Crash & Spyro Superpack - The Huge Adventure + Season of Ice (USA) + // gba scan no. 9 + { "BKZX" }, // 1199 - Banjo-Kazooie - Grunty's Revenge (EUR) + { "BKZI" }, // 1381 - Banjo Kazooie - La Vendetta di Grunty (ITA) + { "BAZJ" }, // 1710 - Akachan Doubutsu Sono (JPN) + { "BKZS" }, // 1883 - Banjo Kazooie - La Venganza de Grunty (ESP) + // gba scan no. 11 + { "A9BP" }, // 0925 - Medabots - Rokusho Version (EUR) + { "A3IJ" }, // bokura no taiyou - taiyou action rpg - kabunushi go-yuutai ban (japan) (demo) +}; + +#endif diff --git a/src/emu/bus/gba/rom.c b/src/emu/bus/gba/rom.c new file mode 100644 index 00000000000..bcddbe8b630 --- /dev/null +++ b/src/emu/bus/gba/rom.c @@ -0,0 +1,460 @@ +/*********************************************************************************************************** + + + Game Boy Advance cart emulation + + + We support carts with several kind of Save RAM (actual SRAM, Flash RAM or EEPROM) + + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "rom.h" + + +//------------------------------------------------- +// gba_rom_device - constructor +//------------------------------------------------- + +const device_type GBA_ROM_STD = &device_creator<gba_rom_device>; +const device_type GBA_ROM_SRAM = &device_creator<gba_rom_sram_device>; +const device_type GBA_ROM_EEPROM = &device_creator<gba_rom_eeprom_device>; +const device_type GBA_ROM_EEPROM64 = &device_creator<gba_rom_eeprom64_device>; +const device_type GBA_ROM_FLASH = &device_creator<gba_rom_flash_device>; +const device_type GBA_ROM_FLASH1M = &device_creator<gba_rom_flash1m_device>; + + +gba_rom_device::gba_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_gba_cart_interface( mconfig, *this ) +{ +} + +gba_rom_device::gba_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, GBA_ROM_STD, "GBA Carts", tag, owner, clock, "gba_rom", __FILE__), + device_gba_cart_interface( mconfig, *this ) +{ +} + +gba_rom_sram_device::gba_rom_sram_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gba_rom_device(mconfig, GBA_ROM_SRAM, "GBA Carts + SRAM", tag, owner, clock, "gba_sram", __FILE__) +{ +} + +gba_rom_eeprom_device::gba_rom_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gba_rom_device(mconfig, GBA_ROM_EEPROM, "GBA Carts + EEPROM", tag, owner, clock, "gba_eeprom", __FILE__) +{ +} + +gba_rom_eeprom64_device::gba_rom_eeprom64_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gba_rom_device(mconfig, GBA_ROM_EEPROM64, "GBA Carts + EEPROM 64K", tag, owner, clock, "gba_eeprom64", __FILE__) +{ +} + +gba_rom_flash_device::gba_rom_flash_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gba_rom_device(mconfig, GBA_ROM_FLASH, "GBA Carts + Panasonic Flash", tag, owner, clock, "gba_flash", __FILE__), + m_flash(*this, "flash") +{ +} + +gba_rom_flash1m_device::gba_rom_flash1m_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : gba_rom_device(mconfig, GBA_ROM_FLASH1M, "GBA Carts + Sanyo Flash", tag, owner, clock, "gba_flash1m", __FILE__), + m_flash(*this, "flash") +{ +} + + +//------------------------------------------------- +// ROM Region to allow faster access to cart data +//------------------------------------------------- + +ROM_START( gba_cart ) + // cartridge region - 32 MBytes (128 Mbit) + ROM_REGION( 0x2000000, "cartridge", ROMREGION_ERASEFF ) +ROM_END + +const rom_entry *gba_rom_device::device_rom_region() const +{ + return ROM_NAME( gba_cart ); +} + +//------------------------------------------------- +// mapper specific start/reset +//------------------------------------------------- + +void gba_rom_device::device_start() +{ + astring tempstring; + m_rom = (UINT32 *)memregion(this->subtag(tempstring, "cartridge"))->base(); +} + +void gba_rom_device::device_reset() +{ +} + +void gba_rom_flash_device::device_reset() +{ + m_flash_mask = 0xffff/4; +} + +void gba_rom_flash1m_device::device_reset() +{ + m_flash_mask = 0x1ffff/4; +} + + +void gba_rom_eeprom_device::device_start() +{ + astring tempstring; + m_rom = (UINT32 *)memregion(this->subtag(tempstring, "cartridge"))->base(); + + // for the moment we use a custom eeprom implementation, so we alloc/save it as nvram + nvram_alloc(machine(), 0x200); + m_eeprom = auto_alloc(machine(), gba_eeprom_device(machine(), (UINT8*)get_nvram_base(), get_nvram_size(), 6)); +} + +void gba_rom_eeprom64_device::device_start() +{ + astring tempstring; + m_rom = (UINT32 *)memregion(this->subtag(tempstring, "cartridge"))->base(); + + // for the moment we use a custom eeprom implementation, so we alloc/save it as nvram + nvram_alloc(machine(), 0x2000); + m_eeprom = auto_alloc(machine(), gba_eeprom_device(machine(), (UINT8*)get_nvram_base(), get_nvram_size(), 14)); +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + + +/*------------------------------------------------- + Carts with SRAM + -------------------------------------------------*/ + +READ32_MEMBER(gba_rom_sram_device::read_ram) +{ + if (m_nvram && offset < m_nvram_size/4) + return m_nvram[offset]; + else // this cannot actually happen... + return 0xffffffff; +} + +WRITE32_MEMBER(gba_rom_sram_device::write_ram) +{ + if (m_nvram && offset < m_nvram_size/4) + COMBINE_DATA(&m_nvram[offset]); +} + + +/*------------------------------------------------- + Carts with Flash RAM + -------------------------------------------------*/ + +static MACHINE_CONFIG_FRAGMENT( panasonic_flash ) + MCFG_PANASONIC_MN63F805MNP_ADD("flash") +MACHINE_CONFIG_END + +machine_config_constructor gba_rom_flash_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( panasonic_flash ); +} + + +READ32_MEMBER(gba_rom_flash_device::read_ram) +{ + UINT32 rv = 0; + + offset &= m_flash_mask; + + if (mem_mask & 0xff) + rv |= m_flash->read(offset * 4); + if (mem_mask & 0xff00) + rv |= m_flash->read((offset * 4) + 1) << 8; + if (mem_mask & 0xff0000) + rv |= m_flash->read((offset * 4) + 2) << 16; + if (mem_mask & 0xff000000) + rv |= m_flash->read((offset * 4) + 3) << 24; + + return rv; +} + +WRITE32_MEMBER(gba_rom_flash_device::write_ram) +{ + offset &= m_flash_mask; + + switch (mem_mask) + { + case 0xff: + m_flash->write(offset * 4, data & 0xff); + break; + case 0xff00: + m_flash->write((offset * 4) + 1, (data >> 8) & 0xff); + break; + case 0xff0000: + m_flash->write((offset * 4) + 2, (data >> 16) & 0xff); + break; + case 0xff000000: + m_flash->write((offset * 4) + 3, (data >> 24) & 0xff); + break; + default: + fatalerror("Unknown mem_mask for GBA flash write %x\n", mem_mask); + break; + } +} + +static MACHINE_CONFIG_FRAGMENT( sanyo_flash ) + MCFG_SANYO_LE26FV10N1TS_ADD("flash") +MACHINE_CONFIG_END + +machine_config_constructor gba_rom_flash1m_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sanyo_flash ); +} + + +READ32_MEMBER(gba_rom_flash1m_device::read_ram) +{ + UINT32 rv = 0; + + offset &= m_flash_mask; + + if (mem_mask & 0xff) + rv |= m_flash->read(offset * 4); + if (mem_mask & 0xff00) + rv |= m_flash->read((offset * 4) + 1) << 8; + if (mem_mask & 0xff0000) + rv |= m_flash->read((offset * 4) + 2) << 16; + if (mem_mask & 0xff000000) + rv |= m_flash->read((offset * 4) + 3) << 24; + + return rv; +} + +WRITE32_MEMBER(gba_rom_flash1m_device::write_ram) +{ + offset &= m_flash_mask; + + switch (mem_mask) + { + case 0xff: + m_flash->write(offset * 4, data & 0xff); + break; + case 0xff00: + m_flash->write((offset * 4) + 1, (data >> 8) & 0xff); + break; + case 0xff0000: + m_flash->write((offset * 4) + 2, (data >> 16) & 0xff); + break; + case 0xff000000: + m_flash->write((offset * 4) + 3, (data >> 24) & 0xff); + break; + default: + fatalerror("Unknown mem_mask for GBA flash write %x\n", mem_mask); + break; + } +} + + +/*------------------------------------------------- + Carts with EEPROM + + TODO: can this sketchy EEPROM device be merged + with the core implementation? + -------------------------------------------------*/ + +// GBA EEPROM Device + +gba_eeprom_device::gba_eeprom_device(running_machine &machine, UINT8 *eeprom, UINT32 size, int addr_bits) : + m_state(EEP_IDLE), + m_machine(machine) +{ + m_data = eeprom; + m_data_size = size; + m_addr_bits = addr_bits; + + state_save_register_item(machine, "GBA_EEPROM", NULL, 0, m_state); + state_save_register_item(machine, "GBA_EEPROM", NULL, 0, m_command); + state_save_register_item(machine, "GBA_EEPROM", NULL, 0, m_count); + state_save_register_item(machine, "GBA_EEPROM", NULL, 0, m_addr); + state_save_register_item(machine, "GBA_EEPROM", NULL, 0, m_bits); + state_save_register_item(machine, "GBA_EEPROM", NULL, 0, m_eep_data); +} + +UINT32 gba_eeprom_device::read() +{ + UINT32 out; + + switch (m_state) + { + case EEP_IDLE: +// printf("eeprom_r: @ %x, mask %08x (state %d) (PC=%x) = %d\n", offset, ~mem_mask, m_state, activecpu_get_pc(), 1); + return 0x00010001; // "ready" + + case EEP_READFIRST: + m_count--; + + if (!m_count) + { + m_count = 64; + m_bits = 0; + m_eep_data = 0; + m_state = EEP_READ; + } + break; + case EEP_READ: + if ((m_bits == 0) && (m_count)) + { + if (m_addr >= m_data_size) + { + fatalerror("eeprom: invalid address (%x)\n", m_addr); + } + m_eep_data = m_data[m_addr]; + //printf("EEPROM read @ %x = %x (%x)\n", m_addr, m_eep_data, (m_eep_data & 0x80) ? 1 : 0); + m_addr++; + m_bits = 8; + } + + out = (m_eep_data & 0x80) ? 1 : 0; + out |= (out<<16); + m_eep_data <<= 1; + + m_bits--; + m_count--; + + if (!m_count) + { + m_state = EEP_IDLE; + } + +// printf("out = %08x\n", out); +// printf("eeprom_r: @ %x, mask %08x (state %d) (PC=%x) = %08x\n", offset, ~mem_mask, m_state, activecpu_get_pc(), out); + return out; + } +// printf("eeprom_r: @ %x, mask %08x (state %d) (PC=%x) = %d\n", offset, ~mem_mask, m_state, space.device().safe_pc(), 0); + return 0; +} + +void gba_eeprom_device::write(UINT32 data) +{ +// printf("eeprom_w: %x @ %x (state %d) (PC=%x)\n", data, offset, m_state, space.device().safe_pc()); + switch (m_state) + { + case EEP_IDLE: + if (data == 1) + m_state++; + break; + + case EEP_COMMAND: + if (data == 1) + m_command = EEP_READFIRST; + else + m_command = EEP_WRITE; + m_state = EEP_ADDR; + m_count = m_addr_bits; + m_addr = 0; + break; + + case EEP_ADDR: + m_addr <<= 1; + m_addr |= (data & 1); + m_count--; + if (!m_count) + { + m_addr *= 8; // each address points to 8 bytes + if (m_command == EEP_READFIRST) + m_state = EEP_AFTERADDR; + else + { + m_count = 64; + m_bits = 8; + m_state = EEP_WRITE; + m_eep_data = 0; + } + } + break; + + case EEP_AFTERADDR: + m_state = m_command; + m_count = 64; + m_bits = 0; + m_eep_data = 0; + if (m_state == EEP_READFIRST) + m_count = 4; + break; + + case EEP_WRITE: + m_eep_data <<= 1; + m_eep_data |= (data & 1); + m_bits--; + m_count--; + + if (m_bits == 0) + { + mame_printf_verbose("%08x: EEPROM: %02x to %x\n", machine().device("maincpu")->safe_pc(), m_eep_data, m_addr); + if (m_addr >= m_data_size) + fatalerror("eeprom: invalid address (%x)\n", m_addr); + + m_data[m_addr] = m_eep_data; + m_addr++; + m_eep_data = 0; + m_bits = 8; + } + + if (!m_count) + m_state = EEP_AFTERWRITE; + break; + + case EEP_AFTERWRITE: + m_state = EEP_IDLE; + break; + } +} + + + +READ32_MEMBER(gba_rom_eeprom_device::read_ram) +{ + // Larger games have smaller access to EERPOM content + if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4) + return 0xffffffff; + + return m_eeprom->read(); +} + +WRITE32_MEMBER(gba_rom_eeprom_device::write_ram) +{ + // Larger games have smaller access to EEPROM content + if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4) + return; + + if (~mem_mask == 0x0000ffff) + data >>= 16; + + m_eeprom->write(data); +} + +READ32_MEMBER(gba_rom_eeprom64_device::read_ram) +{ + // Larger games have smaller access to EERPOM content + if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4) + return 0xffffffff; + + return m_eeprom->read(); +} + +WRITE32_MEMBER(gba_rom_eeprom64_device::write_ram) +{ + // Larger games have smaller access to EEPROM content + if (m_rom_size > (16 * 1024 * 1024) && offset < 0xffff00/4) + return; + + if (~mem_mask == 0x0000ffff) + data >>= 16; + + m_eeprom->write(data); +} diff --git a/src/emu/bus/gba/rom.h b/src/emu/bus/gba/rom.h new file mode 100644 index 00000000000..b6c90f742ce --- /dev/null +++ b/src/emu/bus/gba/rom.h @@ -0,0 +1,173 @@ +#ifndef __GBA_ROM_H +#define __GBA_ROM_H + +#include "gba_slot.h" +#include "machine/intelfsh.h" + + +// ======================> gba_rom_device + +class gba_rom_device : public device_t, + public device_gba_cart_interface +{ +public: + // construction/destruction + gba_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + gba_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual const rom_entry *device_rom_region() const; + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_rom) { return m_rom[offset]; } +}; + +// ======================> gba_rom_sram_device + +class gba_rom_sram_device : public gba_rom_device +{ +public: + // construction/destruction + gba_rom_sram_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_ram); + virtual DECLARE_WRITE32_MEMBER(write_ram); +}; + +// ======================> gba_rom_flash_device + +class gba_rom_flash_device : public gba_rom_device +{ +public: + // construction/destruction + gba_rom_flash_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_ram); + virtual DECLARE_WRITE32_MEMBER(write_ram); + +private: + //UINT32 m_flash_size; + UINT32 m_flash_mask; + required_device<intelfsh8_device> m_flash; +}; + +// ======================> gba_rom_flash1m_device + +class gba_rom_flash1m_device : public gba_rom_device +{ +public: + // construction/destruction + gba_rom_flash1m_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_ram); + virtual DECLARE_WRITE32_MEMBER(write_ram); + +private: + //UINT32 m_flash_size; + UINT32 m_flash_mask; + required_device<intelfsh8_device> m_flash; +}; + +// GBA EEPROM device +// TODO: is it possible to merge this with the standard EEPROM devices in the core? + +enum +{ + EEP_IDLE = 0, + EEP_COMMAND, + EEP_ADDR, + EEP_AFTERADDR, + EEP_READ, + EEP_WRITE, + EEP_AFTERWRITE, + EEP_READFIRST +}; + +class gba_eeprom_device +{ +public: + gba_eeprom_device(running_machine &machine, UINT8 *eeprom, UINT32 size, int addr_bits); + running_machine &machine() const { return m_machine; } + + UINT32 read(); + void write(UINT32 data); + +protected: + UINT8 *m_data; + UINT32 m_data_size; + int m_state; + int m_command; + int m_count; + int m_addr; + int m_bits; + int m_addr_bits; + UINT8 m_eep_data; + + running_machine& m_machine; +}; + + +// ======================> gba_rom_eeprom_device + +class gba_rom_eeprom_device : public gba_rom_device +{ +public: + // construction/destruction + gba_rom_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_ram); + virtual DECLARE_WRITE32_MEMBER(write_ram); + +private: + gba_eeprom_device *m_eeprom; +}; + + +// ======================> gba_rom_eeprom64_device + +class gba_rom_eeprom64_device : public gba_rom_device +{ +public: + // construction/destruction + gba_rom_eeprom64_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_ram); + virtual DECLARE_WRITE32_MEMBER(write_ram); + +private: + gba_eeprom_device *m_eeprom; +}; + + +// device type definition +extern const device_type GBA_ROM_STD; +extern const device_type GBA_ROM_SRAM; +extern const device_type GBA_ROM_EEPROM; +extern const device_type GBA_ROM_EEPROM64; +extern const device_type GBA_ROM_FLASH; +extern const device_type GBA_ROM_FLASH1M; + + + +#endif diff --git a/src/emu/bus/megadrive/eeprom.c b/src/emu/bus/megadrive/eeprom.c new file mode 100644 index 00000000000..95bc78c716b --- /dev/null +++ b/src/emu/bus/megadrive/eeprom.c @@ -0,0 +1,374 @@ +/*********************************************************************************************************** + + + MegaDrive / Genesis cart+EEPROM emulation + + + TODO: proper EEPROM emulation, still not worked on (just hooked up the I2C device) + + + i2c games mapping table: + + game name | SDA_IN | SDA_OUT | SCL | SIZE_MASK | PAGE_MASK | + ----------------------------------|------------|------------|------------|----------------|-----------| + NBA Jam | 0x200001-0 | 0x200001-0 | 0x200001-1 | 0x00ff (24C02) | 0x03 | xx + NBA Jam TE | 0x200001-0 | 0x200001-0 | 0x200000-0 | 0x00ff (24C02) | 0x03 | xx + NBA Jam TE (32x) | 0x200001-0 | 0x200001-0 | 0x200000-0 | 0x00ff (24C02) | 0x03 | + NFL Quarterback Club | 0x200001-0 | 0x200001-0 | 0x200000-0 | 0x00ff (24C02) | 0x03 | xx + NFL Quarterback Club 96 | 0x200001-0 | 0x200001-0 | 0x200000-0 | 0x07ff (24C16) | 0x07 | xx + College Slam | 0x200001-0 | 0x200001-0 | 0x200000-0 | 0x1fff (24C64) | 0x07 | xx + Frank Thomas Big Hurt Baseball | 0x200001-0 | 0x200001-0 | 0x200000-0 | 0x1fff (24C64) | 0x07 | xx + NHLPA Hockey 93 | 0x200001-7 | 0x200001-7 | 0x200001-6 | 0x007f (24C01) | 0x03 | xx + Rings of Power | 0x200001-7 | 0x200001-7 | 0x200001-6 | 0x007f (24C01) | 0x03 | xx + Evander Holyfield's Boxing | 0x200001-0 | 0x200001-0 | 0x200001-1 | 0x007f (24C01) | 0x03 | xx + Greatest Heavyweights of the Ring | 0x200001-0 | 0x200001-0 | 0x200001-1 | 0x007f (24C01) | 0x03 | xx + Wonder Boy V | 0x200001-0 | 0x200001-0 | 0x200001-1 | 0x007f (24C01) | 0x03 | xx + Sports Talk Baseball | 0x200001-0 | 0x200001-0 | 0x200001-1 | 0x007f (24C01) | 0x03 | xx + Megaman - the Wily Wars | 0x200001-0 | 0x200001-0 | 0x200001-1 | 0x007f (24C01) | 0x03 | xx ** + Micro Machines 2 | 0x380001-7 | 0x300000-0*| 0x300000-1*| 0x03ff (24C08) | 0x0f | + Micro Machines Military | 0x380001-7 | 0x300000-0*| 0x300000-1*| 0x03ff (24C08) | 0x0f | + Micro Machines 96 | 0x380001-7 | 0x300000-0*| 0x300000-1*| 0x07ff (24C16) | 0x0f | + Brian Lara Cricket 96 | 0x380001-7 | 0x300000-0*| 0x300000-1*| 0x1fff (24C64) | 0x??* | + ----------------------------------|------------|------------|------------|----------------|-----------| + + * Notes: check these + ** original Rockman Mega World (J) set uses normal backup RAM + + + Micro Machines 2, Micro Machines Military, Micro Machines 96 are emulated in md_jcart + +***********************************************************************************************************/ + + + +#include "emu.h" +#include "eeprom.h" + + +//------------------------------------------------- +// md_rom_device - constructor +//------------------------------------------------- + +const device_type MD_STD_EEPROM = &device_creator<md_std_eeprom_device>; +const device_type MD_EEPROM_NBAJAM = &device_creator<md_eeprom_nbajam_device>; +const device_type MD_EEPROM_NBAJAMTE = &device_creator<md_eeprom_nbajamte_device>; +const device_type MD_EEPROM_NFLQB = &device_creator<md_eeprom_nflqb_device>; +const device_type MD_EEPROM_CSLAM = &device_creator<md_eeprom_cslam_device>; +const device_type MD_EEPROM_NHLPA = &device_creator<md_eeprom_nhlpa_device>; +const device_type MD_EEPROM_BLARA = &device_creator<md_eeprom_blara_device>; + + +md_std_eeprom_device::md_std_eeprom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_md_cart_interface( mconfig, *this ), + m_i2cmem(*this, "i2cmem") +{ +} + +md_std_eeprom_device::md_std_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MD_STD_EEPROM, "MD Standard cart + EEPROM", tag, owner, clock, "md_std_eeprom", __FILE__), + device_md_cart_interface( mconfig, *this ), + m_i2cmem(*this, "i2cmem") +{ +} + +md_eeprom_nbajam_device::md_eeprom_nbajam_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_eeprom_device(mconfig, MD_EEPROM_NBAJAM, "MD NBA Jam", tag, owner, clock, "md_eeprom_nbajam", __FILE__) +{ +} + +md_eeprom_nbajamte_device::md_eeprom_nbajamte_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_eeprom_device(mconfig, MD_EEPROM_NBAJAMTE, "MD NBA Jam TE (and a few more)", tag, owner, clock, "md_eeprom_nbajamte", __FILE__) +{ +} + +md_eeprom_cslam_device::md_eeprom_cslam_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_eeprom_device(mconfig, MD_EEPROM_CSLAM, "MD College Slam", tag, owner, clock, "md_eeprom_cslam", __FILE__) +{ +} + +md_eeprom_nflqb_device::md_eeprom_nflqb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_eeprom_device(mconfig, MD_EEPROM_NFLQB, "MD NFL Quarterback 96", tag, owner, clock, "md_eeprom_nflqb", __FILE__) +{ +} + +md_eeprom_nhlpa_device::md_eeprom_nhlpa_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_eeprom_device(mconfig, MD_EEPROM_NHLPA, "MD NHLPA 93", tag, owner, clock, "md_eeprom_nhlpa", __FILE__) +{ +} + +md_eeprom_blara_device::md_eeprom_blara_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_eeprom_device(mconfig, MD_EEPROM_BLARA, "MD Brian Lara", tag, owner, clock, "md_eeprom_blara", __FILE__) +{ +} + + +//------------------------------------------------- +// SERIAL I2C DEVICE +//------------------------------------------------- + + +// MD_STD_EEPROM & MD_EEPROM_NHLPA +MACHINE_CONFIG_FRAGMENT( md_i2c_24c01 ) + MCFG_24C01_ADD("i2cmem") +MACHINE_CONFIG_END + +// MD_EEPROM_NBAJAM & MD_EEPROM_NBAJAMTE +MACHINE_CONFIG_FRAGMENT( md_i2c_24c02 ) + MCFG_24C02_ADD("i2cmem") +MACHINE_CONFIG_END + +// MD_EEPROM_NFLQB +MACHINE_CONFIG_FRAGMENT( md_i2c_24c16 ) + MCFG_24C16_ADD("i2cmem") +MACHINE_CONFIG_END + +// MD_EEPROM_CSLAM & MD_EEPROM_BLARA +MACHINE_CONFIG_FRAGMENT( md_i2c_24c64 ) + MCFG_24C64_ADD("i2cmem") +MACHINE_CONFIG_END + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor md_std_eeprom_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c01 ); +} + +machine_config_constructor md_eeprom_nbajam_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c02 ); +} + +machine_config_constructor md_eeprom_nbajamte_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c02 ); +} + +machine_config_constructor md_eeprom_cslam_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c64 ); +} + +machine_config_constructor md_eeprom_nflqb_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c16 ); +} + +machine_config_constructor md_eeprom_nhlpa_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c01 ); +} + +machine_config_constructor md_eeprom_blara_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c64 ); +} + + +void md_std_eeprom_device::device_start() +{ + save_item(NAME(m_i2c_mem)); + save_item(NAME(m_i2c_clk)); +} + +void md_std_eeprom_device::device_reset() +{ + m_i2c_mem = 0; + m_i2c_clk = 0; +} + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + CART + EEPROM + -------------------------------------------------*/ + +READ16_MEMBER(md_std_eeprom_device::read) +{ + if (offset == 0x200000/2) + { + return m_i2cmem->read_sda(); + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_std_eeprom_device::write) +{ + if (offset == 0x200000/2) + { + m_i2c_clk = BIT(data, 1); + m_i2c_mem = BIT(data, 0); + m_i2cmem->write_scl(m_i2c_clk); + m_i2cmem->write_sda(m_i2c_mem); + } +} + +READ16_MEMBER(md_eeprom_nbajam_device::read) +{ + if (offset == 0x200000/2) + { +// m_i2c_mem = m_i2cmem->read_sda(); + return m_i2cmem->read_sda(); + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_eeprom_nbajam_device::write) +{ + if (offset == 0x200000/2) + { + m_i2c_clk = BIT(data, 1); + m_i2c_mem = BIT(data, 0); + m_i2cmem->write_scl(m_i2c_clk); + m_i2cmem->write_sda(m_i2c_mem); + } +} + +READ16_MEMBER(md_eeprom_nbajamte_device::read) +{ + if (offset == 0x200000/2) + { +// m_i2c_mem = m_i2cmem->read_sda(); + return m_i2cmem->read_sda(); + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_eeprom_nbajamte_device::write) +{ + if (offset == 0x200000/2) + { + if(ACCESSING_BITS_8_15) + { + m_i2c_clk = BIT(data, 8); + m_i2cmem->write_scl(m_i2c_clk); + } + + if(ACCESSING_BITS_0_7) + { + m_i2c_mem = BIT(data, 0); + m_i2cmem->write_sda(m_i2c_mem); + } + } +} + +// same as NBAJAMTE above... derived class? +READ16_MEMBER(md_eeprom_cslam_device::read) +{ + if (offset == 0x200000/2) + { +// m_i2c_mem = m_i2cmem->read_sda(); + return m_i2cmem->read_sda(); + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_eeprom_cslam_device::write) +{ + if (offset == 0x200000/2) + { + if(ACCESSING_BITS_8_15) + { + m_i2c_clk = BIT(data, 8); + m_i2cmem->write_scl(m_i2c_clk); + } + + if(ACCESSING_BITS_0_7) + { + m_i2c_mem = BIT(data, 0); + m_i2cmem->write_sda(m_i2c_mem); + } + } +} + +// same as NBAJAMTE above... derived class? +READ16_MEMBER(md_eeprom_nflqb_device::read) +{ + if (offset == 0x200000/2) + { +// m_i2c_mem = i2cmem_sda_read(m_i2cmem); + return m_i2cmem->read_sda(); + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_eeprom_nflqb_device::write) +{ + if (offset == 0x200000/2) + { + m_i2c_clk = BIT(data, 8); + m_i2c_mem = BIT(data, 0); + m_i2cmem->write_scl(m_i2c_clk); + m_i2cmem->write_sda(m_i2c_mem); + } +} + +READ16_MEMBER(md_eeprom_nhlpa_device::read) +{ + if (offset == 0x200000/2) + { +// m_i2c_mem = m_i2cmem->read_sda(); + return (m_i2cmem->read_sda() & 1) << 7; + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_eeprom_nhlpa_device::write) +{ + if (offset == 0x200000/2) + { + m_i2c_clk = BIT(data, 6); + m_i2c_mem = BIT(data, 7); + m_i2cmem->write_scl(m_i2c_clk); + m_i2cmem->write_sda(m_i2c_mem); + } +} + +READ16_MEMBER(md_eeprom_blara_device::read) +{ + if (offset == 0x380000/2) + { +// m_i2c_mem = m_i2cmem->read_sda(); + return (m_i2cmem->read_sda() & 1) << 7; + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_eeprom_blara_device::write) +{ + if (offset == 0x380000/2) + { + m_i2c_clk = BIT(data, 9); + m_i2c_mem = BIT(data, 8); + m_i2cmem->write_scl(m_i2c_clk); + m_i2cmem->write_sda(m_i2c_mem); + } +} diff --git a/src/emu/bus/megadrive/eeprom.h b/src/emu/bus/megadrive/eeprom.h new file mode 100644 index 00000000000..c0c4266cc97 --- /dev/null +++ b/src/emu/bus/megadrive/eeprom.h @@ -0,0 +1,141 @@ +#ifndef __MD_EEPROM_H +#define __MD_EEPROM_H + +#include "md_slot.h" +#include "machine/i2cmem.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> md_std_eeprom_device + +class md_std_eeprom_device : public device_t, + public device_md_cart_interface +{ +public: + // construction/destruction + md_std_eeprom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + md_std_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + + required_device<i2cmem_device> m_i2cmem; + UINT8 m_i2c_mem, m_i2c_clk; +}; + +// ======================> md_eeprom_nbajam_device + +class md_eeprom_nbajam_device : public md_std_eeprom_device +{ +public: + // construction/destruction + md_eeprom_nbajam_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); +}; + +// ======================> md_eeprom_nbajamte_device + +class md_eeprom_nbajamte_device : public md_std_eeprom_device +{ +public: + // construction/destruction + md_eeprom_nbajamte_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); +}; + +// ======================> md_eeprom_cslam_device (same read/write as nbajamte, but different I2C type) + +class md_eeprom_cslam_device : public md_std_eeprom_device +{ +public: + // construction/destruction + md_eeprom_cslam_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); +}; + +// ======================> md_eeprom_nflqb_device (same read/write as nbajamte, but different I2C type) + +class md_eeprom_nflqb_device : public md_std_eeprom_device +{ +public: + // construction/destruction + md_eeprom_nflqb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); +}; + +// ======================> md_eeprom_nhlpa_device + +class md_eeprom_nhlpa_device : public md_std_eeprom_device +{ +public: + // construction/destruction + md_eeprom_nhlpa_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); +}; + +// ======================> md_eeprom_blara_device (same read/write as codemast, but different I2C type) + +class md_eeprom_blara_device : public md_std_eeprom_device +{ +public: + // construction/destruction + md_eeprom_blara_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); +}; + + +// device type definition +extern const device_type MD_STD_EEPROM; +extern const device_type MD_EEPROM_NBAJAM; +extern const device_type MD_EEPROM_NBAJAMTE; +extern const device_type MD_EEPROM_CSLAM; +extern const device_type MD_EEPROM_NFLQB; +extern const device_type MD_EEPROM_NHLPA; +extern const device_type MD_EEPROM_BLARA; + +#endif diff --git a/src/emu/bus/megadrive/jcart.c b/src/emu/bus/megadrive/jcart.c new file mode 100644 index 00000000000..3ae90264e7f --- /dev/null +++ b/src/emu/bus/megadrive/jcart.c @@ -0,0 +1,252 @@ +/*********************************************************************************************************** + + + MegaDrive / Genesis J-Cart (+SEPROM) emulation + + + i2c games mapping table: + + game name | SDA_IN | SDA_OUT | SCL | SIZE_MASK | PAGE_MASK | + ----------------------------------|------------|------------|------------|----------------|-----------| + Micro Machines 2 | 0x380001-7 | 0x300000-0*| 0x300000-1*| 0x03ff (24C08) | 0x0f | + Micro Machines Military | 0x380001-7 | 0x300000-0*| 0x300000-1*| 0x03ff (24C08) | 0x0f | + Micro Machines 96 | 0x380001-7 | 0x300000-0*| 0x300000-1*| 0x07ff (24C16) | 0x0f | + ----------------------------------|------------|------------|------------|----------------|-----------| + + * Notes: check these + + + TODO: proper SEPROM emulation, still not worked on (just hooked up the I2C device) + +***********************************************************************************************************/ + + + +#include "emu.h" +#include "jcart.h" + + +//------------------------------------------------- +// md_rom_device - constructor +//------------------------------------------------- + +const device_type MD_JCART = &device_creator<md_jcart_device>; +const device_type MD_SEPROM_CODEMAST = &device_creator<md_seprom_codemast_device>; +const device_type MD_SEPROM_MM96 = &device_creator<md_seprom_mm96_device>; + +// Sampras, Super Skidmarks? +md_jcart_device::md_jcart_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_md_cart_interface( mconfig, *this ), + m_jcart3(*this, "JCART3"), + m_jcart4(*this, "JCART4") +{ +} + +md_jcart_device::md_jcart_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MD_JCART, "MD J-Cart games", tag, owner, clock, "md_jcart", __FILE__), + device_md_cart_interface( mconfig, *this ), + m_jcart3(*this, "JCART3"), + m_jcart4(*this, "JCART4") +{ +} + +// Micro Machines 2, Micro Machines Military +md_seprom_codemast_device::md_seprom_codemast_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : md_jcart_device(mconfig, type, name, tag, owner, clock, shortname, source), + m_i2cmem(*this, "i2cmem") +{ +} + +md_seprom_codemast_device::md_seprom_codemast_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_jcart_device(mconfig, MD_SEPROM_CODEMAST, "MD J-Cart games + SEPROM", tag, owner, clock, "md_seprom_codemast", __FILE__), + m_i2cmem(*this, "i2cmem") +{ +} + +// Micro Machines 96 +md_seprom_mm96_device::md_seprom_mm96_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_seprom_codemast_device(mconfig, MD_SEPROM_MM96, "MD Micro Machine 96", tag, owner, clock, "md_seprom_mm96", __FILE__) +{ +} + + +//------------------------------------------------- +// SERIAL I2C DEVICE +//------------------------------------------------- + +// MD_SEPROM_CODEMAST +MACHINE_CONFIG_FRAGMENT( md_i2c_24c08 ) + MCFG_24C08_ADD("i2cmem") +MACHINE_CONFIG_END + +// MD_SEPROM_MM96 +MACHINE_CONFIG_FRAGMENT( md_i2c_24c16a ) + MCFG_24C16A_ADD("i2cmem") +MACHINE_CONFIG_END + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor md_seprom_codemast_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c08 ); +} + +machine_config_constructor md_seprom_mm96_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_i2c_24c16a ); +} + + +static INPUT_PORTS_START( jcart_ipt ) + + PORT_START("JCART3") /* Joypad 3 on J-Cart (3 button + start) */ + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(3) + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(3) + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(3) + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(3) + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(3) PORT_NAME("P3 B") + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(3) PORT_NAME("P3 C") + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3) PORT_NAME("P3 A") + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_START ) PORT_PLAYER(3) + + PORT_START("JCART4") /* Joypad 4 on J-Cart (3 button + start) */ + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY PORT_PLAYER(4) + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY PORT_PLAYER(4) + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY PORT_PLAYER(4) + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY PORT_PLAYER(4) + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_BUTTON2 ) PORT_PLAYER(4) PORT_NAME("P4 B") + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_PLAYER(4) PORT_NAME("P4 C") + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(4) PORT_NAME("P4 A") + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_START ) PORT_PLAYER(4) + +INPUT_PORTS_END + +ioport_constructor md_jcart_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( jcart_ipt ); +} + + +void md_jcart_device::device_start() +{ + save_item(NAME(m_jcart_io_data)); +} + +void md_jcart_device::device_reset() +{ + m_jcart_io_data[0] = 0; + m_jcart_io_data[1] = 0; +} + +void md_seprom_codemast_device::device_start() +{ + save_item(NAME(m_i2c_mem)); + save_item(NAME(m_i2c_clk)); + save_item(NAME(m_jcart_io_data)); +} + +void md_seprom_codemast_device::device_reset() +{ + m_i2c_mem = 0; + m_i2c_clk = 0; + m_jcart_io_data[0] = 0; + m_jcart_io_data[1] = 0; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + J-CART ONLY (Pete Sampras Tennis) + -------------------------------------------------*/ + +READ16_MEMBER(md_jcart_device::read) +{ + if (offset == 0x38fffe/2) + { + UINT8 joy[2]; + + if (m_jcart_io_data[0] & 0x40) + { + joy[0] = m_jcart3->read_safe(0); + joy[1] = m_jcart4->read_safe(0); + return (m_jcart_io_data[0] & 0x40) | joy[0] | (joy[1] << 8); + } + else + { + joy[0] = ((m_jcart3->read_safe(0) & 0xc0) >> 2) | (m_jcart3->read_safe(0) & 0x03); + joy[1] = ((m_jcart4->read_safe(0) & 0xc0) >> 2) | (m_jcart4->read_safe(0) & 0x03); + return (m_jcart_io_data[0] & 0x40) | joy[0] | (joy[1] << 8); + } + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_jcart_device::write) +{ + if (offset == 0x38fffe/2) + { + m_jcart_io_data[0] = (data & 1) << 6; + m_jcart_io_data[1] = (data & 1) << 6; + } +} + +/*------------------------------------------------- + J-CART + SEPROM + -------------------------------------------------*/ + +READ16_MEMBER(md_seprom_codemast_device::read) +{ + if (offset == 0x380000/2) + { + m_i2c_mem = m_i2cmem->read_sda(); + return (m_i2c_mem & 1) << 7; + } + if (offset == 0x38fffe/2) + { + UINT8 joy[2]; + + if (m_jcart_io_data[0] & 0x40) + { + joy[0] = m_jcart3->read_safe(0); + joy[1] = m_jcart4->read_safe(0); + return (m_jcart_io_data[0] & 0x40) | joy[0] | (joy[1] << 8); + } + else + { + joy[0] = ((m_jcart3->read_safe(0) & 0xc0) >> 2) | (m_jcart3->read_safe(0) & 0x03); + joy[1] = ((m_jcart4->read_safe(0) & 0xc0) >> 2) | (m_jcart4->read_safe(0) & 0x03); + return (m_jcart_io_data[0] & 0x40) | joy[0] | (joy[1] << 8); + } + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_seprom_codemast_device::write) +{ + if (offset == 0x380000/2) + { + m_i2c_clk = BIT(data, 9); + m_i2c_mem = BIT(data, 8); + m_i2cmem->write_scl(m_i2c_clk); + m_i2cmem->write_sda(m_i2c_mem); + } + if (offset == 0x38fffe/2) + { + m_jcart_io_data[0] = (data & 1) << 6; + m_jcart_io_data[1] = (data & 1) << 6; + } +} diff --git a/src/emu/bus/megadrive/jcart.h b/src/emu/bus/megadrive/jcart.h new file mode 100644 index 00000000000..a618f682d57 --- /dev/null +++ b/src/emu/bus/megadrive/jcart.h @@ -0,0 +1,82 @@ +#ifndef __MD_JCART_H +#define __MD_JCART_H + +#include "md_slot.h" +#include "machine/i2cmem.h" + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> md_jcart_device + +class md_jcart_device : public device_t, + public device_md_cart_interface +{ +public: + // construction/destruction + md_jcart_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + md_jcart_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual ioport_constructor device_input_ports() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + + required_ioport m_jcart3; + required_ioport m_jcart4; + +private: + UINT8 m_jcart_io_data[2]; +}; + +// ======================> md_seprom_codemast_device + +class md_seprom_codemast_device : public md_jcart_device +{ +public: + // construction/destruction + md_seprom_codemast_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + md_seprom_codemast_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + + required_device<i2cmem_device> m_i2cmem; + +private: + UINT8 m_jcart_io_data[2]; + UINT8 m_i2c_mem, m_i2c_clk; +}; + +// ======================> md_seprom_mm96_device (same read/write as codemast, but different I2C type) + +class md_seprom_mm96_device : public md_seprom_codemast_device +{ +public: + // construction/destruction + md_seprom_mm96_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual machine_config_constructor device_mconfig_additions() const; +}; + + + +// device type definition +extern const device_type MD_JCART; +extern const device_type MD_SEPROM_CODEMAST; +extern const device_type MD_SEPROM_MM96; + +#endif diff --git a/src/emu/bus/megadrive/md_carts.c b/src/emu/bus/megadrive/md_carts.c new file mode 100644 index 00000000000..191c939ad7e --- /dev/null +++ b/src/emu/bus/megadrive/md_carts.c @@ -0,0 +1,67 @@ + /********************************************************************** + + Megadrive carts + +**********************************************************************/ + +#include "md_carts.h" + +SLOT_INTERFACE_START(md_cart) + SLOT_INTERFACE_INTERNAL("rom", MD_STD_ROM) + SLOT_INTERFACE_INTERNAL("rom_svp", MD_ROM_SVP) + SLOT_INTERFACE_INTERNAL("rom_sk", MD_ROM_SK) +// NVRAM handling + SLOT_INTERFACE_INTERNAL("rom_sram", MD_ROM_SRAM) + SLOT_INTERFACE_INTERNAL("rom_sramsafe", MD_ROM_SRAM) + SLOT_INTERFACE_INTERNAL("rom_fram", MD_ROM_FRAM) + SLOT_INTERFACE_INTERNAL("rom_hardbl95", MD_ROM_SRAM) + SLOT_INTERFACE_INTERNAL("rom_xinqig", MD_ROM_SRAM) + SLOT_INTERFACE_INTERNAL("rom_beggarp", MD_ROM_BEGGARP) + SLOT_INTERFACE_INTERNAL("rom_wukong", MD_ROM_WUKONG) +// EEPROM handling (not supported fully yet) + SLOT_INTERFACE_INTERNAL("rom_eeprom", MD_STD_EEPROM) + SLOT_INTERFACE_INTERNAL("rom_nbajam", MD_EEPROM_NBAJAM) + SLOT_INTERFACE_INTERNAL("rom_nbajamte", MD_EEPROM_NBAJAMTE) + SLOT_INTERFACE_INTERNAL("rom_nflqb96", MD_EEPROM_NFLQB) + SLOT_INTERFACE_INTERNAL("rom_cslam", MD_EEPROM_CSLAM) + SLOT_INTERFACE_INTERNAL("rom_nhlpa", MD_EEPROM_NHLPA) + SLOT_INTERFACE_INTERNAL("rom_blara", MD_EEPROM_BLARA) +// J-Cart controller (Sampras Tennis) + SLOT_INTERFACE_INTERNAL("rom_jcart", MD_JCART) +// J-Cart controller + EEPROM handling (not supported fully yet) + SLOT_INTERFACE_INTERNAL("rom_codemast", MD_SEPROM_CODEMAST) + SLOT_INTERFACE_INTERNAL("rom_mm96", MD_SEPROM_MM96) +// STM95 EEPROM + SLOT_INTERFACE_INTERNAL("rom_stm95", MD_EEPROM_STM95) +// CodeMasters 2-in-1 (reset based) + SLOT_INTERFACE_INTERNAL("rom_cm2in1", MD_ROM_CM2IN1) +// unique bankswitch + SLOT_INTERFACE_INTERNAL("rom_ssf2", MD_ROM_SSF2) + SLOT_INTERFACE_INTERNAL("rom_radica", MD_ROM_RADICA) +// pirate mappers (protection and/or bankswitch) + SLOT_INTERFACE_INTERNAL("rom_16mj2", MD_ROM_16MJ2) + SLOT_INTERFACE_INTERNAL("rom_bugs", MD_ROM_BUGSLIFE) + SLOT_INTERFACE_INTERNAL("rom_chinf3", MD_ROM_CHINF3) + SLOT_INTERFACE_INTERNAL("rom_elfwor", MD_ROM_ELFWOR) + SLOT_INTERFACE_INTERNAL("rom_yasech", MD_ROM_YASECH) + SLOT_INTERFACE_INTERNAL("rom_kof98", MD_ROM_KOF98) + SLOT_INTERFACE_INTERNAL("rom_kof99", MD_ROM_KOF99) + SLOT_INTERFACE_INTERNAL("rom_lion2", MD_ROM_LION2) + SLOT_INTERFACE_INTERNAL("rom_lion3", MD_ROM_LION3) + SLOT_INTERFACE_INTERNAL("rom_mcpir", MD_ROM_MCPIR) + SLOT_INTERFACE_INTERNAL("rom_mjlov", MD_ROM_MJLOV) + SLOT_INTERFACE_INTERNAL("rom_pokea", MD_ROM_POKEA) + SLOT_INTERFACE_INTERNAL("rom_pokestad", MD_ROM_POKESTAD) + SLOT_INTERFACE_INTERNAL("rom_realtec", MD_ROM_REALTEC) + SLOT_INTERFACE_INTERNAL("rom_redcl", MD_ROM_REDCL) + SLOT_INTERFACE_INTERNAL("rom_rx3", MD_ROM_RX3) + SLOT_INTERFACE_INTERNAL("rom_sbubl", MD_ROM_SBUBL) + SLOT_INTERFACE_INTERNAL("rom_smb", MD_ROM_SMB) + SLOT_INTERFACE_INTERNAL("rom_smb2", MD_ROM_SMB2) + SLOT_INTERFACE_INTERNAL("rom_smw64", MD_ROM_SMW64) + SLOT_INTERFACE_INTERNAL("rom_smouse", MD_ROM_SMOUSE) + SLOT_INTERFACE_INTERNAL("rom_soulb", MD_ROM_SOULB) + SLOT_INTERFACE_INTERNAL("rom_squir", MD_ROM_SQUIR) + SLOT_INTERFACE_INTERNAL("rom_tekkensp", MD_ROM_TEKKENSP) + SLOT_INTERFACE_INTERNAL("rom_topf", MD_ROM_TOPF) +SLOT_INTERFACE_END diff --git a/src/emu/bus/megadrive/md_carts.h b/src/emu/bus/megadrive/md_carts.h new file mode 100644 index 00000000000..50290f33ce2 --- /dev/null +++ b/src/emu/bus/megadrive/md_carts.h @@ -0,0 +1,24 @@ +/********************************************************************** + + Megadrive carts + +**********************************************************************/ + +#pragma once + +#ifndef __MD_CARTS_H__ +#define __MD_CARTS_H__ + +#include "emu.h" + +#include "rom.h" +#include "svp.h" +#include "sk.h" +#include "eeprom.h" +#include "jcart.h" +#include "stm95.h" + +// supported devices +SLOT_INTERFACE_EXTERN(md_cart); + +#endif // __MD_CARTS_H__ diff --git a/src/emu/bus/megadrive/md_slot.c b/src/emu/bus/megadrive/md_slot.c new file mode 100644 index 00000000000..3dfb90a2aac --- /dev/null +++ b/src/emu/bus/megadrive/md_slot.c @@ -0,0 +1,1107 @@ +/*********************************************************************************************************** + + + MegaDrive cart emulation + (through slot devices) + + The driver exposes address ranges + 0x000000-0x7fffff to read/write (typically cart data, possibly banked, and some protection) + 0x130000-0x1300ff to read_a13/write_a13 (typically SRAM toggling or protection) + 0x150000-0x1500ff to read_a15/write_a15 (for SVP registers, not converted yet to slots) + + currently available slot devices: + md_rom: standard carts + carts with NVRAM (SRAM/FRAM) + pirate carts with protection & bankswitch + md_eeprom: carts + I2C EEPROM (EEPROM device hooked up only, support to be completed) + md_jcart: Codemasters J-Cart & J-Cart+SEPROM (again, EEPROM device hooked up only, support to be completed) + md_sk: Sonic & Knuckles pass-thorugh cart (enables a second slot to mount any other cart) + md_stm95: cart + STM95 EEPROM (e.g. Pier Solar) + + + Cart Mirroring (based Eke's research) + + MD Cartridge area is mapped to $000000-$3fffff: when accessing ROM, 68k address lines A1 to A21 can be + used by the internal cartridge hardware to decode full 4MB address range. + Depending on ROM total size and additional decoding hardware, some address lines might be ignored, + resulting in ROM mirroring. + + Cartridges typically use either 8-bits (x2) or 16-bits (x1, x2) Mask ROM chips, each chip size is a + factor of 2 bytes. + When one chip ROM1 of size 2^N is present, it is generally mirrored each 2^N bytes so that read access + to cart area sees the sequence ROM1,ROM1,ROM1,... (up to 4MB) + When two chips ROM1 & ROM2 are present and the whole size is 2^N, then the block ROM1+ROM2 is mirrored + in the cart area, and reads see the sequence ROM1+ROM2,ROM1+ROM2,... (up to 4MB) + When two chips ROM1 & ROM2 are present and the whole size is not 2^N (e.g. because ROM1 and ROM2 have + different sizes), then the area between the end of ROM2 and next power 2^N is generally ignored, and + reads see the sequence ROM1,ROM2,XXXX,ROM1,ROM2,XXXX... (up to 4MB) + + At loading time we first compute first power 2^N larger than cart size (see get_padded_size function), + we allocate such a size for ROM and we fill of 0xff the area between end of dump and 2^N. + Then we handle mirroring by creating a rom_bank_map[] (see rom_map_setup function) which points each + access in 0x000000-0x400000 to the correct 64K ROM bank. + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "md_slot.h" + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type MD_CART_SLOT = &device_creator<md_cart_slot_device>; +const device_type PICO_CART_SLOT = &device_creator<pico_cart_slot_device>; +const device_type COPERA_CART_SLOT = &device_creator<copera_cart_slot_device>; + +//************************************************************************** +// MD cartridges Interface +//************************************************************************** + +//------------------------------------------------- +// device_md_cart_interface - constructor +//------------------------------------------------- + +device_md_cart_interface::device_md_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL), + m_nvram(NULL), + m_rom_size(0), + m_nvram_size(0) +{ +} + + +//------------------------------------------------- +// ~device_md_cart_interface - destructor +//------------------------------------------------- + +device_md_cart_interface::~device_md_cart_interface() +{ +} + +//------------------------------------------------- +// rom_alloc - alloc the space for the cart +//------------------------------------------------- + +void device_md_cart_interface::rom_alloc(running_machine &machine, size_t size) +{ + if (m_rom == NULL) + { + m_rom = auto_alloc_array_clear(machine, UINT16, size/sizeof(UINT16)); + m_rom_size = size; + } +} + + +//------------------------------------------------- +// nvram_alloc - alloc the space for the nvram +//------------------------------------------------- + +void device_md_cart_interface::nvram_alloc(running_machine &machine, size_t size) +{ + if (m_nvram == NULL) + { + m_nvram = auto_alloc_array_clear(machine, UINT16, size/sizeof(UINT16)); + m_nvram_size = size; + state_save_register_item_pointer(machine, "MD_CART", this->device().tag(), 0, m_nvram, m_nvram_size/sizeof(UINT16)); + } +} + +//------------------------------------------------- +// rom_map_setup - setup map of rom banks in 64K +// blocks, so to simplify ROM mirroring +//------------------------------------------------- + +void device_md_cart_interface::rom_map_setup(UINT32 size) +{ + int i; + // setup the rom_bank_map array to faster ROM read + for (i = 0; i < size / 0x10000; i++) + rom_bank_map[i] = i; + + // fill up remaining blocks with mirrors + while (i % 64) + { + int j = 0, repeat_banks; + while ((i % (64 >> j)) && j < 7) + j++; + repeat_banks = i % (64 >> (j - 1)); + for (int k = 0; k < repeat_banks; k++) + rom_bank_map[i + k] = rom_bank_map[i + k - repeat_banks]; + i += repeat_banks; + } + +// check bank map! +// for (i = 0; i < 64; i++) +// { +// printf("bank %3d = %3d\t", i, rom_bank_map[i]); +// if ((i%8) == 7) +// printf("\n"); +// } +} + +//------------------------------------------------- +// get_padded_size +//------------------------------------------------- + +UINT32 device_md_cart_interface::get_padded_size(UINT32 size) +{ + UINT32 pad_size = 0x10000; + while (size > pad_size) + pad_size <<= 1; + + if (pad_size < 0x800000 && size < pad_size) + return pad_size; + else + return size; +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// base_md_cart_slot_device - constructor +//------------------------------------------------- +base_md_cart_slot_device::base_md_cart_slot_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : + device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this), + m_type(SEGA_STD), + m_must_be_loaded(1) +{ +} + +md_cart_slot_device::md_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + base_md_cart_slot_device(mconfig, MD_CART_SLOT, "MD Cartridge Slot", tag, owner, clock, "md_cart_slot", __FILE__) +{ +} + +pico_cart_slot_device::pico_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + base_md_cart_slot_device(mconfig, PICO_CART_SLOT, "Pico Cartridge Slot", tag, owner, clock, "pico_cart_slot", __FILE__) +{ +} + +copera_cart_slot_device::copera_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + base_md_cart_slot_device(mconfig, COPERA_CART_SLOT, "Copera Cartridge Slot", tag, owner, clock, "copera_cart_slot", __FILE__) +{ +} + +//------------------------------------------------- +// base_md_cart_slot_device - destructor +//------------------------------------------------- + +base_md_cart_slot_device::~base_md_cart_slot_device() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void base_md_cart_slot_device::device_start() +{ + m_cart = dynamic_cast<device_md_cart_interface *>(get_card_device()); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void base_md_cart_slot_device::device_config_complete() +{ + // set brief and instance name + update_names(); +} + + +//------------------------------------------------- +// MD PCB +//------------------------------------------------- + + +struct md_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 md_slot slot_list[] = +{ + { SEGA_SK, "rom_sk" }, + { SEGA_SVP, "rom_svp"}, + + { SEGA_SRAM, "rom_sram" }, + { SEGA_FRAM, "rom_fram" }, + { HARDBALL95, "rom_hardbl95" }, + { XINQIG, "rom_xinqig"}, + { BEGGARP, "rom_beggarp"}, + { WUKONG, "rom_wukong"}, + + { SEGA_EEPROM, "rom_eeprom" }, + { NBA_JAM, "rom_nbajam" }, + { NBA_JAM_TE, "rom_nbajamte" }, + { NFL_QB_96, "rom_nflqb" }, + { C_SLAM, "rom_cslam" }, + { EA_NHLPA, "rom_nhlpa" }, + { BRIAN_LARA, "rom_blara" }, + + { CM_JCART, "rom_jcart" }, + { CODE_MASTERS, "rom_codemast" }, + { CM_MM96, "rom_mm96" }, + + { PSOLAR, "rom_stm95"}, + + { SSF2, "rom_ssf2" }, + { CM_2IN1, "rom_cm2in1" }, + { RADICA, "rom_radica" }, +// { GAME_KANDUME, "rom_gkand" }, // what's needed by this? + + { TILESMJ2, "rom_16mj2" }, + { BUGSLIFE, "rom_bugs" }, + { CHINFIGHT3, "rom_chinf3" }, + { ELFWOR, "rom_elfwor" }, + { KAIJU, "rom_pokestad" }, + { KOF98, "rom_kof98" }, + { KOF99, "rom_kof99" }, + { LIONK2, "rom_lion2" }, + { LIONK3, "rom_lion3" }, + { MC_PIRATE, "rom_mcpir" }, + { MJLOVER, "rom_mjlov" }, + { POKEMONA, "rom_pokea" }, + { REALTEC, "rom_realtec" }, + { REDCL_EN, "rom_redcl" }, + { REDCLIFF, "rom_redcl" }, + { ROCKMANX3, "rom_rx3" }, + { SBUBBOB, "rom_sbubl" }, + { SMB, "rom_smb" }, + { SMB2, "rom_smb2" }, + { SMW64, "rom_smw64" }, + { SMOUSE, "rom_smouse" }, + { SOULBLAD, "rom_soulblad" }, + { SQUIRRELK, "rom_squir" }, + { TEKKENSP, "rom_tekkesp" }, + { TOPFIGHTER, "rom_topf" }, + + { SEGA_SRAM_FULLPATH, "rom_sram" }, + { SEGA_SRAM_FALLBACK, "rom_sramsafe" } +}; + +static int md_get_pcb_id(const char *slot) +{ + for (int i = 0; i < ARRAY_LENGTH(slot_list); i++) + { + if (!mame_stricmp(slot_list[i].slot_option, slot)) + return slot_list[i].pcb_id; + } + + return SEGA_STD; +} + +static const char *md_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 "rom"; +} + + +/*------------------------------------------------- + SRAM handling + -------------------------------------------------*/ + +/*------------------------------------------------- + call load + -------------------------------------------------*/ + + +bool base_md_cart_slot_device::call_load() +{ + if (m_cart) + { + m_type = SEGA_STD; + int res; + + // STEP 1: load the file image and keep a copy for later banking + // STEP 2: identify the cart type + // The two steps are carried out differently if we are loading from a list or not + if (software_entry() == NULL) + res = load_nonlist(); + else + res = load_list(); + + //printf("cart type: %d\n", m_type); + + if (res == IMAGE_INIT_PASS) + { + //speed-up rom access from SVP add-on, if present + if (m_type == SEGA_SVP) + m_cart->set_bank_to_rom("cart_svp", 0x800/2); + + // STEP 3: install memory handlers for this type of cart + setup_custom_mappers(); + + // STEP 4: take care of SRAM. + setup_nvram(); + + if (m_cart->get_nvram_size()) + battery_load(m_cart->get_nvram_base(), m_cart->get_nvram_size(), 0xff); + + file_logging((UINT8 *)m_cart->get_rom_base(), m_cart->get_rom_size(), m_cart->get_nvram_size()); + } + + return res; + } + + return IMAGE_INIT_PASS; +} + + +int base_md_cart_slot_device::load_list() +{ + UINT16 *ROM; + UINT32 length = get_software_region_length("rom"); + const char *slot_name; + + // if cart size is not (2^n * 64K), the system will see anyway that size so we need to alloc a bit more space + length = m_cart->get_padded_size(length); + + m_cart->rom_alloc(machine(), length); + ROM = m_cart->get_rom_base(); + memcpy(ROM, get_software_region("rom"), get_software_region_length("rom")); + + // if we allocated a ROM larger that the file (e.g. due to uneven cart size), set remaining space to 0xff + if (length > get_software_region_length("rom")) + memset(ROM + get_software_region_length("rom")/2, 0xffff, (length - get_software_region_length("rom"))/2); + + if ((slot_name = get_feature("slot")) == NULL) + m_type = SEGA_STD; + else + m_type = md_get_pcb_id(slot_name); + + // handle mirroring of ROM, unless it's SSF2 or Pier Solar + if (m_type != SSF2 && m_type != PSOLAR) + m_cart->rom_map_setup(length); + + return IMAGE_INIT_PASS; +} + + +/************************************* + * Helper function: Detect SMD file + *************************************/ + +/* code taken directly from GoodGEN by Cowering */ +static int genesis_is_SMD(unsigned char *buf, unsigned int len) +{ + if (len > 0x2081 && buf[0x2080] == 'S' && buf[0x80] == 'E' && buf[0x2081] == 'G' && buf[0x81] == 'A') + return 1; + + /* aq quiz */ + if (len > (0xf0 + 8) && !strncmp("UZ(-01 ", (const char *) &buf[0xf0], 8)) + return 1; + + /* Phelios USA redump */ + /* target earth */ + /* klax (namcot) */ + if (len > 0x2081 && buf[0x2080] == ' ' && buf[0x0080] == 'S' && buf[0x2081] == 'E' && buf[0x0081] == 'G') + return 1; + + /* jap baseball 94 */ + if (len > (0xf0 + 9) && !strncmp("OL R-AEAL", (const char *) &buf[0xf0], 9)) + return 1; + + /* devilish Mahjong Tower */ + if (len > (0xf3 + 11) && !strncmp("optrEtranet", (const char *) &buf[0xf3], 11)) + return 1; + + /* golden axe 2 beta */ + if (len > 0x0103 && buf[0x0100] == 0x3c && buf[0x0101] == 0 && buf[0x0102] == 0 && buf[0x0103] == 0x3c) + return 1; + + /* omega race */ + if (len > (0x90 + 8) && !strncmp("OEARC ", (const char *) &buf[0x90], 8)) + return 1; + + /* budokan beta */ + if ((len >= 0x6708 + 8) && !strncmp(" NTEBDKN", (const char *) &buf[0x6708], 8)) + return 1; + + /* cdx pro 1.8 bios */ + if (len > (0x2c0 + 7) && !strncmp("so fCXP", (const char *) &buf[0x2c0], 7)) + return 1; + + /* ishido (hacked) */ + if (len > (0x90 + 8) && !strncmp("sio-Wyo ", (const char *) &buf[0x0090], 8)) + return 1; + + /* onslaught */ + if (len > (0x88 + 8) && !strncmp("SS CAL ", (const char *) &buf[0x0088], 8)) + return 1; + + /* tram terror pirate */ + if ((len >= 0x3648 + 8) && !strncmp("SG NEPIE", (const char *) &buf[0x3648], 8)) + return 1; + + /* breath of fire 3 chinese */ + if (len > 0xa && buf[0x0007] == 0x1c && buf[0x0008] == 0x0a && buf[0x0009] == 0xb8 && buf[0x000a] == 0x0a) + return 1; + + /*tetris pirate */ + if ((len >= 0x1cbe + 5) && !strncmp("@TTI>", (const char *) &buf[0x1cbe], 5)) + return 1; + + return 0; +} + +/************************************* + * Loading a cart image *not* from + * softlist + *************************************/ + +int base_md_cart_slot_device::load_nonlist() +{ + unsigned char *ROM, *tmpROM; + bool is_smd, is_md; + UINT32 tmplen = length(), offset, len; + + // STEP 1: store a (possibly headered) copy of the file and determine its type (SMD? MD? BIN?) + tmpROM = global_alloc_array(unsigned char, tmplen); + fread(tmpROM, tmplen); + is_smd = genesis_is_SMD(&tmpROM[0x200], tmplen - 0x200); + is_md = (tmpROM[0x80] == 'E') && (tmpROM[0x81] == 'A') && (tmpROM[0x82] == 'M' || tmpROM[0x82] == 'G'); + + // take header into account, if any + offset = is_smd ? 0x200 : 0; + + // STEP 2: allocate space for the real copy of the game + // if cart size is not (2^n * 64K), the system will see anyway that size so we need to alloc a bit more space + len = m_cart->get_padded_size(tmplen - offset); + // this contains an hack for SSF2: its current bankswitch code needs larger rom space to work + m_cart->rom_alloc(machine(), (len == 0x500000) ? 0x900000 : len); + + + // STEP 3: copy the game data in the appropriate way + ROM = (unsigned char *)m_cart->get_rom_base(); + + if (is_smd) + { + mame_printf_debug("SMD!\n"); + + for (int ptr = 0; ptr < (tmplen - 0x200) / 0x2000; ptr += 2) + { + for (int x = 0; x < 0x2000; x++) + { + ROM[ptr * 0x2000 + x * 2 + 0] = tmpROM[0x200 + ((ptr + 1) * 0x2000) + x]; + ROM[ptr * 0x2000 + x * 2 + 1] = tmpROM[0x200 + ((ptr + 0) * 0x2000) + x]; + } + } + } + else if (is_md) + { + mame_printf_debug("MD!\n"); + + for (int ptr = 0; ptr < tmplen; ptr += 2) + { + ROM[ptr] = tmpROM[(tmplen >> 1) + (ptr >> 1)]; + ROM[ptr + 1] = tmpROM[(ptr >> 1)]; + } + } + else + { + mame_printf_debug("BIN!\n"); + + fseek(0, SEEK_SET); + fread(ROM, len); + } + + global_free(tmpROM); + + // if we allocated a ROM larger that the file (e.g. due to uneven cart size), set remaining space to 0xff + if (len > (tmplen - offset)) + memset(m_cart->get_rom_base() + (tmplen - offset)/2, 0xffff, (len - tmplen + offset)/2); + + + // STEP 4: determine the cart type (to deal with sram/eeprom & pirate mappers) + m_type = get_cart_type(ROM, len); + + // handle mirroring of ROM, unless it's SSF2 or Pier Solar + if (m_type != SSF2 && m_type != PSOLAR) + m_cart->rom_map_setup(len); + + +// CPU needs to access ROM as a ROM_REGION16_BE, so we need to compensate on LE machines +#ifdef LSB_FIRST + unsigned char fliptemp; + for (int ptr = 0; ptr < len; ptr += 2) + { + fliptemp = ROM[ptr]; + ROM[ptr] = ROM[ptr+1]; + ROM[ptr+1] = fliptemp; + } +#endif + + return IMAGE_INIT_PASS; +} + +/*------------------------------------------------- + call_unload + -------------------------------------------------*/ + +void base_md_cart_slot_device::call_unload() +{ + if (m_cart && m_cart->get_nvram_size()) + battery_save(m_cart->get_nvram_base(), m_cart->get_nvram_size()); +} + + +void base_md_cart_slot_device::setup_custom_mappers() +{ + UINT16 *ROM16 = m_cart->get_rom_base(); + + switch (m_type) + { + case SSF2: + // copy the image in 0x400000-0x900000 and keep the beginning for bankswitch + for (int x = 0x500000/2; x > 0; x--) + ROM16[x + 0x400000/2 - 1] = ROM16[x - 1]; + for (int x = 0; x < 0x400000/2; x++) + ROM16[x] = ROM16[x + 0x400000/2]; + break; + case REDCL_EN: + // decrypt + for (int x = 0; x < 0x200000/2; x++) + ROM16[x] = ROM16[x + 2] ^ 0x4040; + break; + } +} + +void base_md_cart_slot_device::setup_nvram() +{ + UINT8 *ROM = (UINT8 *)m_cart->get_rom_base(); + m_cart->m_nvram_readonly = 0; + m_cart->m_nvram_active = 0; + m_cart->m_nvram_handlers_installed = 0; + + /* install SRAM & i2c handlers for the specific type of cart */ + switch (m_type) + { + // These types only come from fullpath loading + case SEGA_SRAM_FULLPATH: + m_cart->m_nvram_start = (ROM[0x1b5] << 24 | ROM[0x1b4] << 16 | ROM[0x1b7] << 8 | ROM[0x1b6]); + m_cart->m_nvram_end = (ROM[0x1b9] << 24 | ROM[0x1b8] << 16 | ROM[0x1bb] << 8 | ROM[0x1ba]); + logerror("SRAM detected from header: starting location %X - SRAM Length %X\n", m_cart->m_nvram_start, m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + + // We assume at most 64k of SRAM. is this correct? + if ((m_cart->m_nvram_start > m_cart->m_nvram_end) || ((m_cart->m_nvram_end - m_cart->m_nvram_start) >= 0x10000)) + m_cart->m_nvram_end = m_cart->m_nvram_start + 0xffff; + + if (m_cart->m_nvram_start & 1) + m_cart->m_nvram_start -= 1; + + if (!(m_cart->m_nvram_end & 1)) + m_cart->m_nvram_end += 1; + + m_cart->nvram_alloc(machine(), m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + if (m_cart->m_rom_size <= m_cart->m_nvram_start) + m_cart->m_nvram_active = 1; + m_cart->m_nvram_handlers_installed = 1; + // don't trust too much header? + m_cart->m_nvram_start &= 0x3fffff; + m_cart->m_nvram_end &= 0x3fffff; + break; + case SEGA_SRAM_FALLBACK: + m_cart->m_nvram_start = 0x200000; + m_cart->m_nvram_end = m_cart->m_nvram_start + 0xffff; + logerror("No SRAM detected from header, using fallback SRAM in case this is a broken header\n"); + + m_cart->nvram_alloc(machine(), m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + if (m_cart->m_rom_size <= m_cart->m_nvram_start) + m_cart->m_nvram_active = 1; + break; + + // These types only come from softlist loading + case SEGA_SRAM: + m_cart->m_nvram_start = 0x200000; + m_cart->m_nvram_end = m_cart->m_nvram_start + get_software_region_length("sram") - 1; + m_cart->nvram_alloc(machine(), m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + if (m_cart->m_rom_size <= m_cart->m_nvram_start) + m_cart->m_nvram_active = 1; + m_cart->m_nvram_handlers_installed = 1; + break; + case SEGA_FRAM: + m_cart->m_nvram_start = 0x200000; + m_cart->m_nvram_end = m_cart->m_nvram_start + get_software_region_length("fram") - 1; + m_cart->nvram_alloc(machine(), m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + m_cart->m_nvram_active = 1; + m_cart->m_nvram_handlers_installed = 1; + break; + + // These types might come from both (pending proper id routines) + case HARDBALL95: + m_cart->m_nvram_start = 0x300000; + m_cart->m_nvram_end = m_cart->m_nvram_start + get_software_region_length("sram") - 1; + m_cart->nvram_alloc(machine(), m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + m_cart->m_nvram_active = 1; + m_cart->m_nvram_handlers_installed = 1; + break; + case XINQIG: + m_cart->m_nvram_start = 0x400000; + m_cart->m_nvram_end = m_cart->m_nvram_start + 0xffff; + m_cart->nvram_alloc(machine(), m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + m_cart->m_nvram_active = 1; + m_cart->m_nvram_handlers_installed = 1; + break; + case BEGGARP: + m_cart->m_nvram_start = 0x400000; + m_cart->m_nvram_end = m_cart->m_nvram_start + 0xffff; + m_cart->nvram_alloc(machine(), 0x8000); // 32K mirrored + m_cart->m_nvram_active = 1; + break; + case WUKONG: + m_cart->m_nvram_start = 0x3c0000; + m_cart->m_nvram_end = m_cart->m_nvram_start + 0x3fff; + m_cart->nvram_alloc(machine(), m_cart->m_nvram_end - m_cart->m_nvram_start + 1); + m_cart->m_nvram_active = 1; + break; + } +} + + + +/*------------------------------------------------- + call softlist load + -------------------------------------------------*/ + +bool base_md_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; +} + +int base_md_cart_slot_device::get_cart_type(UINT8 *ROM, UINT32 len) +{ + int type = SEGA_STD; + + /* Detect carts which need additional handlers */ + static const unsigned char smouse_sig[] = { 0x4d, 0xf9, 0x00, 0x40, 0x00, 0x02 }, + mjlover_sig[] = { 0x13, 0xf9, 0x00, 0x40, 0x00, 0x00 }, // move.b ($400000).l,($FFFF0C).l (partial) + squir_sig[] = { 0x26, 0x79, 0x00, 0xff, 0x00, 0xfa }, + bugsl_sig[] = { 0x20, 0x12, 0x13, 0xc0, 0x00, 0xff }, + sbub_sig[] = { 0x0c, 0x39, 0x00, 0x55, 0x00, 0x40 }, // cmpi.b #$55,($400000).l + lk3_sig[] = { 0x0c, 0x01, 0x00, 0x30, 0x66, 0xe4 }, + sdk_sig[] = { 0x48, 0xe7, 0xff, 0xfe, 0x52, 0x79 }, + redcliff_sig[] = { 0x10, 0x39, 0x00, 0x40, 0x00, 0x04 }, // move.b ($400004).l,d0 + redcl_en_sig[] = { 0x50, 0x79, 0x40, 0x00, 0x40, 0x44 }, // move.b ($400004).l,d0 + smb_sig[] = { 0x20, 0x4d, 0x41, 0x52, 0x49, 0x4f }, + smb2_sig[] = { 0x4e, 0xb9, 0x00, 0x0f, 0x25, 0x84 }, + kaiju_sig[] = { 0x19, 0x7c, 0x00, 0x01, 0x00, 0x00 }, + chifi3_sig[] = { 0xb6, 0x16, 0x66, 0x00, 0x00, 0x4a }, + lionk2_sig[] = { 0x26, 0x79, 0x00, 0xff, 0x00, 0xf4 }, + rx3_sig[] = { 0x66, 0x00, 0x00, 0x0e, 0x30, 0x3c }, + kof98_sig[] = { 0x9b, 0xfc, 0x00, 0x00, 0x4a, 0x00 }, + s15in1_sig[] = { 0x22, 0x3c, 0x00, 0xa1, 0x30, 0x00 }, + kof99_sig[] = { 0x20, 0x3c, 0x30, 0x00, 0x00, 0xa1 }, // move.l #$300000A1,d0 + radica_sig[] = { 0x4e, 0xd0, 0x30, 0x39, 0x00, 0xa1 }, // jmp (a0) move.w ($a130xx),d0 + soulb_sig[] = { 0x33, 0xfc, 0x00, 0x0c, 0x00, 0xff }, // move.w #$C,($FF020A).l (what happens if check fails) + s19in1_sig[] = { 0x13, 0xc0, 0x00, 0xa1, 0x30, 0x38 }, + rockman_sig[] = { 0xea, 0x80 }; + + switch (len) + { + case 0x80000: + if (!memcmp(&ROM[0x08c8], smouse_sig, sizeof(smouse_sig))) + type = SMOUSE; + + if (!memcmp((char *)&ROM[0x7e30e], "SEGA", 4) || + !memcmp((char *)&ROM[0x7e100], "SEGA", 4) || + !memcmp((char *)&ROM[0x7e1e6], "SEGA", 4)) + type = REALTEC; + + if (!memcmp((char *)&ROM[0x0180], "GM T-50396", 10)) // NHLPA Hockey 93 + type = EA_NHLPA; + + if (!memcmp((char *)&ROM[0x0180], "GM MK-1215", 10)) // Evander Holyfield + type = SEGA_EEPROM; + break; + + case 0xc0000: + + if (!memcmp((char *)&ROM[0x0180], "GM G-4060 ", 8)) // Wonder Boy V + type = SEGA_EEPROM; + break; + + case 0x100000: + if (!memcmp(&ROM[0x01b24], mjlover_sig, sizeof(mjlover_sig))) + type = MJLOVER; + + if (!memcmp(&ROM[0x03b4], squir_sig, sizeof(squir_sig))) + type = SQUIRRELK; + + if (!memcmp(&ROM[0xee0d0], bugsl_sig, sizeof(bugsl_sig))) + type = BUGSLIFE; + + if (!memcmp((char *)&ROM[0x0172], "GAME : ELF WOR", 14)) + type = ELFWOR; + + if (!memcmp(&ROM[0x123e4], sbub_sig, sizeof(sbub_sig))) + type = SBUBBOB; + + if (!memcmp((char *)&ROM[0x0180], "GM T-50176", 10)) // Rings of Power + type = EA_NHLPA; + + if (!memcmp((char *)&ROM[0x0180], "MK 00001211-00", 14)) // Sports Talk Baseball + type = SEGA_EEPROM; + + if (!memcmp((char *)&ROM[0x0180], "GM T-120096-", 12)) // Micro Machines 2 + type = CODE_MASTERS; + + if (!memcmp((char *)&ROM[0x0180], "GM T-120146-", 12)) // Brian Lara Cricket 96 / Shane Wayne Cricket 96 + type = BRIAN_LARA; + + if (!memcmp((char *)&ROM[0x0190], "OJKRPTBVFCA ", 0x10)) // Micro Machines '96 / Military TODO: better way to recognize these? + type = CODE_MASTERS; + break; + + case 0x200000: + if (!memcmp(&ROM[0x18c6], lk3_sig, sizeof(lk3_sig))) + type = LIONK3; + + if (!memcmp(&ROM[0x220], sdk_sig, sizeof(sdk_sig))) + type = LIONK3; + + if (!memcmp(&ROM[0xce560], redcliff_sig, sizeof(redcliff_sig))) + type = REDCLIFF; + + if (!memcmp(&ROM[0xc8cb0], smb_sig, sizeof(smb_sig))) + type = SMB; + + if (!memcmp(&ROM[0xf24d6], smb2_sig, sizeof(smb2_sig))) + type = SMB2; + + if (!memcmp(&ROM[0x674e], kaiju_sig, sizeof(kaiju_sig))) + type = KAIJU; + + if (!memcmp(&ROM[0x1780], chifi3_sig, sizeof(chifi3_sig))) + type = CHINFIGHT3; + + if (!memcmp(&ROM[0x03c2], lionk2_sig, sizeof(lionk2_sig))) + type = LIONK2; + + if (!memcmp(&ROM[0xc8b90], rx3_sig, sizeof(rx3_sig))) + type = ROCKMANX3; + + if (!memcmp(&ROM[0x56ae2], kof98_sig, sizeof(kof98_sig))) + type = KOF98; + + if (!memcmp(&ROM[0x17bb2], s15in1_sig, sizeof(s15in1_sig))) + type = MC_PIRATE; + + if (!memcmp((char *)&ROM[0x0180], "GM T-081326 ", 12)) // NBA Jam + type = NBA_JAM; + + if (!memcmp((char *)&ROM[0x0180], "GM MK-1228", 10)) // Greatest Heavyweight of the Ring + type = SEGA_EEPROM; + + if ((!memcmp((char *)&ROM[0x0180], "GM T-12046", 10)) || // Mega Man + (!memcmp((char *)&ROM[0x0180], "GM T-12053", 10) && !memcmp(&ROM[0x18e], rockman_sig, sizeof(rockman_sig)))) // / Rock Man (EEPROM version) + type = SEGA_EEPROM; + + if (!memcmp((char *)&ROM[0x0150], "Virtua Racing", 13)) + type = SEGA_SVP; + + break; + + case 0x200005: + if (!memcmp(&ROM[0xce564], redcl_en_sig, sizeof(redcliff_sig))) + type = REDCL_EN; + break; + + case 0x300000: + if (!memcmp(&ROM[0x220], sdk_sig, sizeof(sdk_sig))) + type = LIONK3; + + if (!memcmp(&ROM[0x1fd0d2], kof99_sig, sizeof(kof99_sig))) + type = KOF99; + + if (!memcmp((char *)&ROM[0x0180], "GM T-81406", 10)) // NBA Jam TE + type = NBA_JAM_TE; + + if (!memcmp((char *)&ROM[0x0180], "GM T-081276 ", 12)) // NFL Quarterback Club + type = NBA_JAM_TE; + + break; + + case 0x400000: + if (!memcmp(&ROM[0x3c031c], radica_sig, sizeof(radica_sig)) || + !memcmp(&ROM[0x3f031c], radica_sig, sizeof(radica_sig))) // ssf+gng + radica vol1 + type = RADICA; + + if (!memcmp(&ROM[0x028460], soulb_sig, sizeof(soulb_sig))) + type = SOULBLAD; + + if (!memcmp(&ROM[0x1e700], s19in1_sig, sizeof(s19in1_sig))) + type = MC_PIRATE; + + if (!memcmp((char *)&ROM[0x0180], "GM T-081586-", 12)) // NFL Quarterback Club 96 + type = NFL_QB_96; + + if (!memcmp((char *)&ROM[0x0180], "GM T-081576 ", 12)) // College Slam + type = C_SLAM; + + if (!memcmp((char *)&ROM[0x0180], "GM T-81476", 10)) // Big Hurt Baseball + type = C_SLAM; + + break; + + case 0x500000: + if (!memcmp((char *)&ROM[0x0120], "SUPER STREET FIGHTER2 ", 22)) + type = SSF2; + break; + + case 0x800000: + if (!memcmp((char *)&ROM[0x0180], "GM T-574023-", 12)) // Pier Solar + type = PSOLAR; + break; + + default: + break; + } + + //check for SRAM among the general carts + if (type == SEGA_STD) + { + // If the cart is not of a special type, we check the header for SRAM. + if (ROM[0x1b1] == 'A' && ROM[0x1b0] == 'R') + { + UINT32 start = (ROM[0x1b4] << 24 | ROM[0x1b5] << 16 | ROM[0x1b6] << 8 | ROM[0x1b7]); + UINT32 end = (ROM[0x1b8] << 24 | ROM[0x1b9] << 16 | ROM[0x1ba] << 8 | ROM[0x1bb]); + // For some games using serial EEPROM, difference between SRAM end to start is 0 or 1. + // Carts with EEPROM should have been already detected above, but better safe than sorry + if (end - start < 2) + type = SEGA_EEPROM; + else + type = SEGA_SRAM_FULLPATH; + } + else + { + // Unfortunately, there are ROMs without correct info in the header, + // Hence, when loading from fullpath we do the SRAM mapping anyway... + // but treat it in a custom way + type = SEGA_SRAM_FALLBACK; + } + } + + return type; +} +/*------------------------------------------------- + get default card software + -------------------------------------------------*/ + +const char * base_md_cart_slot_device::get_default_card_software(const machine_config &config, emu_options &options) +{ + if (open_image_file(options)) + { + const char *slot_string = "rom"; + UINT32 len = core_fsize(m_file), offset = 0; + UINT8 *ROM = global_alloc_array(UINT8, len); + int type; + + core_fread(m_file, ROM, len); + + if (genesis_is_SMD(&ROM[0x200], len - 0x200)) + offset = 0x200; + + type = get_cart_type(ROM + offset, len - offset); + slot_string = md_get_slot(type); + + global_free(ROM); + clear(); + + return slot_string; + } + else + return software_get_default_slot(config, options, this, "rom"); +} + + + +/*------------------------------------------------- + read + -------------------------------------------------*/ + +READ16_MEMBER(base_md_cart_slot_device::read) +{ + if (m_cart) + return m_cart->read(space, offset, mem_mask); + else + return 0xffff; +} + +READ16_MEMBER(base_md_cart_slot_device::read_a13) +{ + if (m_cart) + return m_cart->read_a13(space, offset, mem_mask); + else + return 0xffff; +} + +READ16_MEMBER(base_md_cart_slot_device::read_a15) +{ + if (m_cart) + return m_cart->read_a15(space, offset, mem_mask); + else + return 0xffff; +} + + +/*------------------------------------------------- + write + -------------------------------------------------*/ + +WRITE16_MEMBER(base_md_cart_slot_device::write) +{ + if (m_cart) + m_cart->write(space, offset, data, mem_mask); +} + +WRITE16_MEMBER(base_md_cart_slot_device::write_a13) +{ + if (m_cart) + m_cart->write_a13(space, offset, data, mem_mask); +} + +WRITE16_MEMBER(base_md_cart_slot_device::write_a15) +{ + if (m_cart) + m_cart->write_a15(space, offset, data, mem_mask); +} + +/*------------------------------------------------- + Image loading logging + -------------------------------------------------*/ + +void base_md_cart_slot_device::file_logging(UINT8 *ROM8, UINT32 rom_len, UINT32 nvram_len) +{ + char console[16], copyright[16], domestic_name[48], overseas_name[48]; + char serial[14], io[16], modem[12], memo[40], country[16]; + UINT32 rom_start, rom_end, ram_start, ram_end, sram_start = 0, sram_end = 0; + UINT16 checksum, csum = 0; + bool valid_sram = FALSE, is_pico = FALSE; + astring ctrl(""), reg(""); + + // LOG FILE DETAILS + logerror("FILE DETAILS\n" ); + logerror("============\n" ); + logerror("Name: %s\n", basename()); + logerror("File Size: 0x%" I64FMT "x\n", (software_entry() == NULL) ? length() : get_software_region_length("rom")); + logerror("Detected type: %s\n", md_get_slot(m_type)); + logerror("ROM (Allocated) Size: 0x%X\n", rom_len); + logerror("NVRAM: %s\n", nvram_len ? "Yes" : "No"); + if (nvram_len) + logerror("NVRAM (Allocated) Size: 0x%X\n", nvram_len); + logerror("\n" ); + + + // LOG HEADER DETAILS + if (rom_len < 0x200) + return; + + for (int i = 0; i < 16; i++) + console[i] = ROM8[0x100 + (i ^ 1)]; + if (!strncmp("SEGA PICO", console, 9)) + is_pico = TRUE; + for (int i = 0; i < 16; i++) + copyright[i] = ROM8[0x110 + (i ^ 1)]; + for (int i = 0; i < 48; i++) + domestic_name[i] = ROM8[0x120 + (i ^ 1)]; + for (int i = 0; i < 48; i++) + overseas_name[i] = ROM8[0x150 + (i ^ 1)]; + for (int i = 0; i < 14; i++) + serial[i] = ROM8[0x180 + (i ^ 1)]; + + checksum = ROM8[0x18e] | (ROM8[0x18f] << 8); + + for (int i = 0; i < 16; i++) + { + io[i] = ROM8[0x190 + (i ^ 1)]; + if (io[i] == 'J') + ctrl.cat(" - Joypad 3 buttons [J]\n"); + if (io[i] == '6') + ctrl.cat(" - Joypad 6 buttons [6]\n"); + } + + rom_start = (ROM8[0x1a1] << 24 | ROM8[0x1a0] << 16 | ROM8[0x1a3] << 8 | ROM8[0x1a2]); + rom_end = (ROM8[0x1a5] << 24 | ROM8[0x1a4] << 16 | ROM8[0x1a7] << 8 | ROM8[0x1a6]); + ram_start = (ROM8[0x1a9] << 24 | ROM8[0x1a8] << 16 | ROM8[0x1ab] << 8 | ROM8[0x1aa]);; + ram_end = (ROM8[0x1ad] << 24 | ROM8[0x1ac] << 16 | ROM8[0x1af] << 8 | ROM8[0x1ae]);; + if (ROM8[0x1b1] == 'R' && ROM8[0x1b0] == 'A') + { + valid_sram = TRUE; + sram_start = (ROM8[0x1b5] << 24 | ROM8[0x1b4] << 16 | ROM8[0x1b7] << 8 | ROM8[0x1b6]); + sram_end = (ROM8[0x1b9] << 24 | ROM8[0x1b8] << 16 | ROM8[0x1bb] << 8 | ROM8[0x1ba]); + } + + for (int i = 0; i < 12; i++) + modem[i] = ROM8[0x1bc + (i ^ 1)]; + for (int i = 0; i < 40; i++) + memo[i] = ROM8[0x1c8 + (i ^ 1)]; + for (int i = 0; i < 16; i++) + { + country[i] = ROM8[0x1f0 + (i ^ 1)]; + if (country[i] == 'J') + reg.cat(" - Japan [J]\n"); + if (country[i] == 'U') + reg.cat(" - USA [U]\n"); + if (country[i] == 'E') + reg.cat(" - Europe [E]\n"); + } + + // compute cart checksum to compare with expected one + for (int i = 0x200; i < (rom_end + 1) && i < rom_len; i += 2) + { + csum += (ROM8[i] | (ROM8[i + 1] << 8)); + csum &= 0xffff; + } + + logerror("INTERNAL HEADER\n" ); + logerror("===============\n" ); + logerror("Console: %.16s\n", console); + logerror("Copyright String: %.16s\n", copyright); + logerror(" - Manufacturer: %.4s\n", copyright + 3); // TODO: convert code to manufacturer name! + logerror(" - Date: %.8s\n", copyright + 8); + logerror("Name (domestic): %.48s\n", domestic_name); + logerror("Name (overseas): %.48s\n", overseas_name); + logerror("Serial String: %.14s\n", serial); + if (!is_pico) + { + logerror(" - Type: %.2s (%s)\n", serial, !strncmp("GM", serial, 2) ? "Game" : "Unknown"); + logerror(" - Serial Code: %.8s\n", serial + 3); + logerror(" - Revision: %.2s\n", serial + 12); + } + logerror("Checksum: %X\n", checksum); + logerror(" - Calculated Checksum: %X\n", csum); + logerror("Supported I/O Devices: %.16s\n%s", io, ctrl.cstr()); + logerror("Modem: %.12s\n", modem); + logerror("Memo: %.40s\n", memo); + logerror("Country: %.16s\n%s", country, reg.cstr()); + logerror("ROM Start: 0x%.8X\n", rom_start); + logerror("ROM End: 0x%.8X\n", rom_end); + logerror("RAM Start: 0x%.8X\n", ram_start); + logerror("RAM End: 0x%.8X\n", ram_end); + logerror("SRAM detected from header: %s\n", valid_sram ? "Yes" : "No"); + if (valid_sram) + { + logerror("SRAM Start: 0x%.8X\n", sram_start); + logerror("SRAM End: 0x%.8X\n", sram_end); + } +} diff --git a/src/emu/bus/megadrive/md_slot.h b/src/emu/bus/megadrive/md_slot.h new file mode 100644 index 00000000000..879b7dd862e --- /dev/null +++ b/src/emu/bus/megadrive/md_slot.h @@ -0,0 +1,263 @@ +#ifndef __MD_SLOT_H +#define __MD_SLOT_H + +/*************************************************************************** + TYPE DEFINITIONS + ***************************************************************************/ + +#define MD_ADDR(a) (rom_bank_map[((a << 1) / 0x10000) & 0x3f] * 0x10000 + ((a << 1) & 0xffff))/2 + +/* PCB */ +enum +{ + SEGA_STD = 0, + + // Cart + Slot Expansion + SEGA_SK, /* Sonic & Knuckles pass-through cart */ + + // Cart + SVP + SEGA_SVP, /* Virtua Racing */ + + // Cart + NVRAM + SEGA_SRAM, SEGA_FRAM, + HARDBALL95, /* Hardball 95 uses different sram start address */ + XINQIG, /* Xin Qigai Wangzi uses different sram start address and has no valid header */ + BEGGARP, /* Beggar Prince uses different sram start address + bankswitch tricks */ + WUKONG, /* Legend of Wukong uses different sram start address + bankswitch trick for last 128K of ROM */ + + // EEPROM + SEGA_EEPROM, /* Wonder Boy V / Evander Holyfield's Boxing / Greatest Heavyweights of the Ring / Sports Talk Baseball / Megaman */ + NBA_JAM, /* NBA Jam */ + NBA_JAM_TE, /* NBA Jam TE / NFL Quarterback Club */ + NFL_QB_96, /* NFL Quarterback Club '96 */ + C_SLAM, /* College Slam / Frank Thomas Big Hurt Baseball */ + EA_NHLPA, /* NHLPA Hockey 93 / Rings of Power */ + BRIAN_LARA, /* Brian Lara Cricket 96 */ + PSOLAR, /* Pier Solar (STM95 EEPROM) */ + + // J-Cart + CM_JCART, /* Pete Sampras Tennis */ + CODE_MASTERS, /* Micro Machines 2 / Military (J-Cart + SEPROM) */ + CM_MM96, /* Micro Machines 96 (J-Cart + SEPROM, diff I2C model) */ + + // Various + SSF2, /* Super Street Fighter 2 */ + CM_2IN1, /* CodeMasters 2in1 : Psycho Pinball + Micro Machines */ + GAME_KANDUME, /* Game no Kandume Otokuyou */ + RADICA, /* Radica TV games.. these probably should be a separate driver since they are a separate 'console' */ + + TILESMJ2, /* 16 Mahjong Tiles II */ + BUGSLIFE, /* A Bug's Life */ + CHINFIGHT3, /* Chinese Fighters 3 */ + ELFWOR, /* Linghuan Daoshi Super Magician */ + KAIJU, /* Pokemon Stadium */ + KOF98, /* King of Fighters '98 */ + KOF99, /* King of Fighters '99 */ + LIONK2, /* Lion King 2 */ + LIONK3, /* Lion King 3, Super Donkey Kong 99, Super King Kong 99 */ + MC_PIRATE, /* Super 19 in 1, Super 15 in 1, 12 in 1 and a few more multicarts */ + MJLOVER, /* Mahjong Lover */ + POKEMONA, /* Pocket Monster Alt Protection */ + REALTEC, /* Whac a Critter/Mallet legend, Defend the Earth, Funnyworld/Ballonboy */ + REDCLIFF, /* Romance of the Three Kingdoms - Battle of Red Cliffs, already decoded from .mdx format */ + REDCL_EN, /* The encoded version... */ + ROCKMANX3, /* Rockman X3 */ + SBUBBOB, /* Super Bubble Bobble */ + SMB, /* Super Mario Bros. */ + SMB2, /* Super Mario Bros. 2 */ + SMW64, /* Super Mario World 64 */ + SMOUSE, /* Smart Mouse */ + SOULBLAD, /* Soul Blade */ + SQUIRRELK, /* Squirrel King */ + TEKKENSP, /* Tekken Special */ + TOPFIGHTER, /* Top Fighter 2000 MK VIII */ + + // when loading from fullpath, we need to treat SRAM in custom way + SEGA_SRAM_FULLPATH, + SEGA_SRAM_FALLBACK +}; + + +// ======================> md_cart_interface + +struct md_cart_interface +{ +}; + + +// ======================> device_md_cart_interface + +class device_md_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_md_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~device_md_cart_interface(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read) { return 0xffff; } + virtual DECLARE_WRITE16_MEMBER(write) {} + virtual DECLARE_READ16_MEMBER(read_a13) { return 0xffff; } + virtual DECLARE_WRITE16_MEMBER(write_a13) {} + virtual DECLARE_READ16_MEMBER(read_a15) { return 0xffff; } + virtual DECLARE_WRITE16_MEMBER(write_a15) {} + + virtual int read_test() { return 0; } // used by Virtua Racing test + + /* this probably should do more, like make Genesis V2 'die' if the SEGA string is not written promptly */ + virtual DECLARE_WRITE16_MEMBER(write_tmss_bank) { logerror("Write to TMSS bank: offset %x data %x\n", 0xa14000 + (offset << 1), data); }; + + virtual void rom_alloc(running_machine &machine, size_t size); + virtual void nvram_alloc(running_machine &machine, size_t size); + virtual UINT16* get_rom_base() { return m_rom; }; + virtual UINT16* get_nvram_base() { return m_nvram; }; + virtual UINT32 get_rom_size() { return m_rom_size; }; + virtual UINT32 get_nvram_size() { return m_nvram_size; }; + virtual void set_bank_to_rom(const char *banktag, UINT32 offset) {}; + + void rom_map_setup(UINT32 size); + UINT32 get_padded_size(UINT32 size); + + int m_nvram_start, m_nvram_end; + int m_nvram_active, m_nvram_readonly; + + // when loading from fullpath, we create NVRAM even if not set in the header + // however in this case we access it only if the game turn it on + // the variable below is basically needed to track this... + int m_nvram_handlers_installed; + + // internal state + UINT16 *m_rom; + UINT16 *m_nvram; + UINT32 m_rom_size; + UINT32 m_nvram_size; + + UINT8 rom_bank_map[128]; // 64K chunks of rom +}; + + +// ======================> base_md_cart_slot_device + +class base_md_cart_slot_device : public device_t, + public md_cart_interface, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + base_md_cart_slot_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + virtual ~base_md_cart_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete(); + + // image-level overrides + virtual bool call_load(); + virtual void call_unload(); + virtual bool call_softlist_load(char *swlist, char *swname, rom_entry *start_entry); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return m_must_be_loaded; } + virtual bool is_reset_on_load() const { return 1; } + virtual const option_guide *create_option_guide() const { return NULL; } + + // slot interface overrides + virtual const char * get_default_card_software(const machine_config &config, emu_options &options); + + int get_type() { return m_type; } + + int load_list(); + int load_nonlist(); + int get_cart_type(UINT8 *ROM, UINT32 len); + + void setup_custom_mappers(); + void setup_nvram(); + void set_must_be_loaded(bool _must_be_loaded) { m_must_be_loaded = _must_be_loaded; } + void file_logging(UINT8 *ROM, UINT32 rom_len, UINT32 nvram_len); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + virtual DECLARE_READ16_MEMBER(read_a13); + virtual DECLARE_WRITE16_MEMBER(write_a13); + virtual DECLARE_READ16_MEMBER(read_a15); + virtual DECLARE_WRITE16_MEMBER(write_a15); + virtual DECLARE_WRITE16_MEMBER(write_tmss_bank) { if (m_cart) m_cart->write_tmss_bank(space, offset, data, mem_mask); }; + + virtual int read_test() { if (m_cart) return m_cart->read_test(); else return 0; } // used by Virtua Racing test + +// TODO: this only needs to be public because megasvp copies rom into memory region, so we need to rework that code... +//private: + + int m_type; + device_md_cart_interface* m_cart; + bool m_must_be_loaded; +}; + +// ======================> md_cart_slot_device + +class md_cart_slot_device : public base_md_cart_slot_device +{ +public: + // construction/destruction + md_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual const char *image_interface() const { return "megadriv_cart"; } + virtual const char *file_extensions() const { return "smd,bin,md,gen"; } +}; + +// ======================> pico_cart_slot_device + +class pico_cart_slot_device : public base_md_cart_slot_device +{ +public: + // construction/destruction + pico_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual const char *image_interface() const { return "pico_cart"; } + virtual const char *file_extensions() const { return "bin,md"; } +}; + +// ======================> copera_cart_slot_device + +class copera_cart_slot_device : public base_md_cart_slot_device +{ +public: + // construction/destruction + copera_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual const char *image_interface() const { return "copera_cart"; } + virtual const char *file_extensions() const { return "bin,md"; } +}; + + +// device type definition +extern const device_type MD_CART_SLOT; +extern const device_type PICO_CART_SLOT; +extern const device_type COPERA_CART_SLOT; + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS + ***************************************************************************/ + +#define MCFG_MD_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, MD_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + +#define MCFG_PICO_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, PICO_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + +#define MCFG_COPERA_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, COPERA_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + + + +#define MCFG_MD_CARTRIDGE_NOT_MANDATORY \ + static_cast<md_cart_slot_device *>(device)->set_must_be_loaded(FALSE); + + +#endif diff --git a/src/emu/bus/megadrive/rom.c b/src/emu/bus/megadrive/rom.c new file mode 100644 index 00000000000..c5c5b93c49f --- /dev/null +++ b/src/emu/bus/megadrive/rom.c @@ -0,0 +1,1417 @@ +/*********************************************************************************************************** + + + MegaDrive / Genesis cart emulation + + + Here we emulate bankswitch / protection / NVRAM found on generic carts with no additional hardware + + + ***********************************************************************************************************/ + +#include "emu.h" +#include "rom.h" +#include "cpu/m68000/m68000.h" + + +//------------------------------------------------- +// md_rom_device - constructor +//------------------------------------------------- + +// BASE CARTS + NVRAM +const device_type MD_STD_ROM = &device_creator<md_std_rom_device>; +const device_type MD_ROM_SRAM = &device_creator<md_rom_sram_device>; +const device_type MD_ROM_FRAM = &device_creator<md_rom_fram_device>; + +// BASE CARTS + BANKSWITCH AT RESET +const device_type MD_ROM_CM2IN1 = &device_creator<md_rom_cm2in1_device>; + +// BASE CARTS + PROTECTION / BANKSWITCH +const device_type MD_ROM_SSF2 = &device_creator<md_rom_ssf2_device>; +const device_type MD_ROM_BUGSLIFE = &device_creator<md_rom_bugslife_device>; +const device_type MD_ROM_SMOUSE = &device_creator<md_rom_smouse_device>; +const device_type MD_ROM_SMW64 = &device_creator<md_rom_smw64_device>; +const device_type MD_ROM_SMB = &device_creator<md_rom_smb_device>; +const device_type MD_ROM_SMB2 = &device_creator<md_rom_smb2_device>; +const device_type MD_ROM_SBUBL = &device_creator<md_rom_sbubl_device>; +const device_type MD_ROM_RX3 = &device_creator<md_rom_rx3_device>; +const device_type MD_ROM_MJLOV = &device_creator<md_rom_mjlov_device>; +const device_type MD_ROM_KOF98 = &device_creator<md_rom_kof98_device>; +const device_type MD_ROM_KOF99 = &device_creator<md_rom_kof99_device>; +const device_type MD_ROM_SOULB = &device_creator<md_rom_soulb_device>; +const device_type MD_ROM_CHINF3 = &device_creator<md_rom_chinf3_device>; +const device_type MD_ROM_16MJ2 = &device_creator<md_rom_16mj2_device>; +const device_type MD_ROM_ELFWOR = &device_creator<md_rom_elfwor_device>; +const device_type MD_ROM_YASECH = &device_creator<md_rom_yasech_device>; +const device_type MD_ROM_LION2 = &device_creator<md_rom_lion2_device>; +const device_type MD_ROM_LION3 = &device_creator<md_rom_lion3_device>; +const device_type MD_ROM_MCPIR = &device_creator<md_rom_mcpirate_device>; +const device_type MD_ROM_POKEA = &device_creator<md_rom_pokea_device>; +const device_type MD_ROM_POKESTAD = &device_creator<md_rom_pokestad_device>; +const device_type MD_ROM_REALTEC = &device_creator<md_rom_realtec_device>; +const device_type MD_ROM_REDCL = &device_creator<md_rom_redcl_device>; +const device_type MD_ROM_SQUIR = &device_creator<md_rom_squir_device>; +const device_type MD_ROM_TEKKENSP = &device_creator<md_rom_tekkensp_device>; +const device_type MD_ROM_TOPF = &device_creator<md_rom_topf_device>; +const device_type MD_ROM_RADICA = &device_creator<md_rom_radica_device>; +const device_type MD_ROM_BEGGARP = &device_creator<md_rom_beggarp_device>; +const device_type MD_ROM_WUKONG = &device_creator<md_rom_wukong_device>; + + +md_std_rom_device::md_std_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_md_cart_interface( mconfig, *this ) +{ +} + +md_std_rom_device::md_std_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MD_STD_ROM, "MD Standard cart", tag, owner, clock, "md_std_rom", __FILE__), + device_md_cart_interface( mconfig, *this ) +{ +} + +md_rom_sram_device::md_rom_sram_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SRAM, "MD Standard cart + SRAM", tag, owner, clock, "md_rom_sram", __FILE__) +{ +} + +md_rom_fram_device::md_rom_fram_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_FRAM, "MD Standard cart + FRAM", tag, owner, clock, "md_rom_fram", __FILE__) +{ +} + +md_rom_ssf2_device::md_rom_ssf2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SSF2, "MD Super SF2", tag, owner, clock, "md_rom_ssf2", __FILE__) +{ +} + +md_rom_cm2in1_device::md_rom_cm2in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_CM2IN1, "MD Codemasters 2in1", tag, owner, clock, "md_rom_cm2in1", __FILE__) +{ +} + +md_rom_mcpirate_device::md_rom_mcpirate_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_MCPIR, "MD Pirate Multicarts (Various)", tag, owner, clock, "md_rom_mcpirate", __FILE__) +{ +} + +md_rom_bugslife_device::md_rom_bugslife_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_BUGSLIFE, "MD A Bug's Life", tag, owner, clock, "md_rom_bugslife", __FILE__) +{ +} + +md_rom_smouse_device::md_rom_smouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SMOUSE, "MD Huan Le Tao Qi Shu / Smart Mouse", tag, owner, clock, "md_rom_smouse", __FILE__) +{ +} + +md_rom_smb_device::md_rom_smb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SMB, "MD Super Mario Bros.", tag, owner, clock, "md_rom_smb", __FILE__) +{ +} + +md_rom_smb2_device::md_rom_smb2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SMB2, "MD Super Mario Bros. 2", tag, owner, clock, "md_rom_smb2", __FILE__) +{ +} + +md_rom_smw64_device::md_rom_smw64_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SMW64, "MD Super Mario World 64", tag, owner, clock, "md_rom_smw64", __FILE__) +{ +} + +md_rom_sbubl_device::md_rom_sbubl_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SBUBL, "MD Super Bubble Bobble", tag, owner, clock, "md_rom_sbubl", __FILE__) +{ +} + +md_rom_rx3_device::md_rom_rx3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_RX3, "MD Rockman X3", tag, owner, clock, "md_rom_rx3", __FILE__) +{ +} + +md_rom_mjlov_device::md_rom_mjlov_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_MJLOV, "MD Ma Jiang Qing Ren / Mahjong Lover", tag, owner, clock, "md_rom_mjlov", __FILE__) +{ +} + +md_rom_kof98_device::md_rom_kof98_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_KOF98, "MD KOF 98", tag, owner, clock, "md_rom_kof98", __FILE__) +{ +} + +md_rom_kof99_device::md_rom_kof99_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_KOF99, "MD KOF 99 (and others)", tag, owner, clock, "md_rom_kof99", __FILE__) +{ +} + +md_rom_soulb_device::md_rom_soulb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SOULB, "MD Soul Blade", tag, owner, clock, "md_rom_soulb", __FILE__) +{ +} + +md_rom_chinf3_device::md_rom_chinf3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_CHINF3, "MD Chinese Fighter 3", tag, owner, clock, "md_rom_chinf3", __FILE__) +{ +} + +md_rom_16mj2_device::md_rom_16mj2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_16MJ2, "MD 16 Mahjong Tiles II", tag, owner, clock, "md_rom_16mj2", __FILE__) +{ +} + +md_rom_elfwor_device::md_rom_elfwor_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_ELFWOR, "MD Linghuan Daoshi Super Magician / Elf Wor", tag, owner, clock, "md_rom_elfwor", __FILE__) +{ +} + +md_rom_yasech_device::md_rom_yasech_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_YASECH, "MD Ya Se Chuan Shuo", tag, owner, clock, "md_rom_yasech", __FILE__) +{ +} + +md_rom_lion2_device::md_rom_lion2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_LION2, "MD Lion King 2", tag, owner, clock, "md_rom_lion2", __FILE__) +{ +} + +md_rom_lion3_device::md_rom_lion3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_LION3, "MD Lion King 3", tag, owner, clock, "md_rom_lion3", __FILE__) +{ +} + +md_rom_pokea_device::md_rom_pokea_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_POKEA, "MD Pokemon (Alt Protection)", tag, owner, clock, "md_rom_pokea", __FILE__) +{ +} + +md_rom_pokestad_device::md_rom_pokestad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_POKESTAD, "MD Pokemon Stadium", tag, owner, clock, "md_rom_pokestad", __FILE__) +{ +} + +md_rom_realtec_device::md_rom_realtec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_REALTEC, "MD Realtec", tag, owner, clock, "md_rom_realtec", __FILE__) +{ +} + +md_rom_redcl_device::md_rom_redcl_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_REDCL, "MD Redcliff", tag, owner, clock, "md_rom_redcl", __FILE__) +{ +} + +md_rom_squir_device::md_rom_squir_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_SQUIR, "MD Squirrel King", tag, owner, clock, "md_rom_squir", __FILE__) +{ +} + +md_rom_tekkensp_device::md_rom_tekkensp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_TEKKENSP, "MD Tekken Special", tag, owner, clock, "md_rom_tekkensp", __FILE__) +{ +} + +md_rom_topf_device::md_rom_topf_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_TOPF, "MD Top Fighter", tag, owner, clock, "md_rom_topf", __FILE__) +{ +} + +md_rom_radica_device::md_rom_radica_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_RADICA, "MD Radica TV games", tag, owner, clock, "md_rom_radica", __FILE__) +{ +} + +md_rom_beggarp_device::md_rom_beggarp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_BEGGARP, "MD Beggar Prince", tag, owner, clock, "md_rom_beggarp", __FILE__) +{ +} + +md_rom_wukong_device::md_rom_wukong_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : md_std_rom_device(mconfig, MD_ROM_WUKONG, "MD Legend of Wukong", tag, owner, clock, "md_rom_wukong", __FILE__) +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void md_rom_ssf2_device::device_start() +{ + save_item(NAME(m_bank)); + save_item(NAME(m_lastoff)); + save_item(NAME(m_lastdata)); +} + +void md_rom_ssf2_device::device_reset() +{ + for (int i = 0; i < 7; i++) + m_bank[i] = i; + m_lastoff = -1; + m_lastdata = -1; +} + +void md_rom_cm2in1_device::device_start() +{ + m_base = -1; + save_item(NAME(m_base)); +} + +void md_rom_cm2in1_device::device_reset() +{ + m_base++; + m_base &= 1; +} + +void md_rom_mcpirate_device::device_start() +{ + save_item(NAME(m_bank)); +} + +void md_rom_mcpirate_device::device_reset() +{ + m_bank = 0; +} + +void md_rom_chinf3_device::device_start() +{ + m_bank = 0; + save_item(NAME(m_bank)); +} + +void md_rom_chinf3_device::device_reset() +{ +} + +void md_rom_lion2_device::device_start() +{ + save_item(NAME(m_prot1_data)); + save_item(NAME(m_prot2_data)); +} + +void md_rom_lion2_device::device_reset() +{ + m_prot1_data = 0; + m_prot2_data = 0; +} + +void md_rom_lion3_device::device_start() +{ + save_item(NAME(m_reg)); + save_item(NAME(m_bank)); +} + +void md_rom_lion3_device::device_reset() +{ + m_reg[0] = 0; + m_reg[1] = 0; + m_reg[2] = 0; + m_bank = 0; +} + +void md_rom_pokestad_device::device_start() +{ + save_item(NAME(m_bank)); +} + +void md_rom_pokestad_device::device_reset() +{ + m_bank = 0; +} + +void md_rom_realtec_device::device_start() +{ + save_item(NAME(m_bank_addr)); + save_item(NAME(m_bank_size)); + save_item(NAME(m_old_bank_addr)); +} + +void md_rom_realtec_device::device_reset() +{ + m_bank_addr = 0; + m_bank_size = 0; + m_old_bank_addr = -1; +} + +void md_rom_squir_device::device_start() +{ + save_item(NAME(m_latch)); +} + +void md_rom_squir_device::device_reset() +{ + m_latch = 0; +} + +void md_rom_smw64_device::device_start() +{ + save_item(NAME(m_latch0)); + save_item(NAME(m_latch1)); + save_item(NAME(m_reg)); + save_item(NAME(m_ctrl)); +} + +void md_rom_smw64_device::device_reset() +{ + m_latch0 = 0xf; + m_latch1 = 0xf; + memset(m_reg, 0, sizeof(m_reg)); + memset(m_ctrl, 0, sizeof(m_ctrl)); +} + +void md_rom_tekkensp_device::device_start() +{ + save_item(NAME(m_reg)); +} + +void md_rom_tekkensp_device::device_reset() +{ + m_reg = 0; +} + +void md_rom_topf_device::device_start() +{ + save_item(NAME(m_latch)); + save_item(NAME(m_bank)); +} + +void md_rom_topf_device::device_reset() +{ + m_latch = 0; + m_bank[0] = m_bank[1] = m_bank[2] = 0; +} + +void md_rom_radica_device::device_start() +{ + save_item(NAME(m_bank)); +} + +void md_rom_radica_device::device_reset() +{ + m_bank = 0; +} + +void md_rom_beggarp_device::device_start() +{ + save_item(NAME(m_mode)); + save_item(NAME(m_lock)); +} + +void md_rom_beggarp_device::device_reset() +{ + m_mode = 0; + m_lock = 0; +} + +void md_rom_wukong_device::device_start() +{ + save_item(NAME(m_mode)); +} + +void md_rom_wukong_device::device_reset() +{ + m_mode = 0; +} + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + CART + SRAM + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_sram_device::read) +{ + // since a lot of generic carts ends up here if loaded from fullpath + // we access nvram only if m_nvram_handlers_installed has been turned on + if (m_nvram_handlers_installed) + { + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active) + return m_nvram[offset - m_nvram_start/2]; + } + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_sram_device::write) +{ + // since a lot of generic carts ends up here if loaded from fullpath + // we access nvram only if m_nvram_handlers_installed has been turned on + if (m_nvram_handlers_installed) + { + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active && !m_nvram_readonly) + m_nvram[offset - m_nvram_start/2] = data; + } +} + +WRITE16_MEMBER(md_rom_sram_device::write_a13) +{ + if (offset == 0xf0/2) + { + /* unsure if this is actually supposed to toggle or just switch on? yet to encounter game that uses this */ + m_nvram_active = BIT(data, 0); + m_nvram_readonly = BIT(data, 1); + + // since a lot of generic carts ends up here if loaded from fullpath + // we turn on nvram (with m_nvram_handlers_installed) only if they toggle it on by writing here! + if (m_nvram_active && !m_nvram_handlers_installed) + m_nvram_handlers_installed = 1; + } +} + +/*------------------------------------------------- + CART + FRAM [almost same as SRAM... merge common parts?] + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_fram_device::read) +{ + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active) + return m_nvram[offset - m_nvram_start/2]; + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_fram_device::write) +{ + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active) + m_nvram[offset - m_nvram_start/2] = data; +} + +WRITE16_MEMBER(md_rom_fram_device::write_a13) +{ + if (offset == 0xf0/2) + m_nvram_active = BIT(data, 0); +} + + +READ16_MEMBER(md_rom_fram_device::read_a13) +{ + if (offset == 0xf0/2) + return m_nvram_active; + else + return 0xffff; +} + +/*------------------------------------------------- + SUPER STREET FIGHTERS 2 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_ssf2_device::read) +{ + if (offset < 0x400000/2) + return m_rom[offset]; + else + return 0xffff; +} + +// I'm not very fond of the code below... +WRITE16_MEMBER(md_rom_ssf2_device::write_a13) +{ + if (offset >= 0xf0/2) + { + offset -= 0xf0/2; + if ((m_lastoff != offset) || (m_lastdata != data)) + { + m_lastoff = offset; + m_lastdata = data; + if (offset) // bank 0 is not modified + { + UINT16 *ROM = get_rom_base(); + m_bank[offset] = data & 0xf; + memcpy(ROM + offset * 0x080000/2, ROM + 0x400000/2 + (m_bank[offset] * 0x080000)/2, 0x080000); + } + } + } +} + +/*------------------------------------------------- + CODEMASTERS 2 IN 1 (RESET BASED) + -------------------------------------------------*/ + +#define MD_ADDR_CM2IN1(a) (m_base == 0 ? ((a << 1) & 0x1fffff)/2 : (((a << 1) & 0x7ffff) + 0x200000)/2) + +READ16_MEMBER(md_rom_cm2in1_device::read) +{ + if (offset < 0x400000/2) + return m_rom[MD_ADDR_CM2IN1(offset)]; + else + return 0xffff; +} + + +/*------------------------------------------------- + PIRATE MULTICARTS + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_mcpirate_device::read) +{ + if (offset < 0x400000/2) + return m_rom[(((m_bank * 0x10000) + (offset << 1)) & (m_rom_size - 1))/2]; + else + return read(space, offset - 0x400000/2, 0xffff); +} + +WRITE16_MEMBER(md_rom_mcpirate_device::write_a13) +{ + offset <<= 1; + if (offset < 0x40) + m_bank = offset; +} + +/*------------------------------------------------- + A BUG'S LIFE + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_bugslife_device::read_a13) +{ + if (offset == 0x00/2) return 0x28; + if (offset == 0x02/2) return 0x01; + if (offset == 0x3e/2) return 0x1f; + else return 0xffff; +} + +/*------------------------------------------------- + CHINESE FIGHTER 3 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_chinf3_device::read) +{ + if (offset < 0x100000/2) + { + if (!m_bank) + return m_rom[offset & 0xfffff/2]; + else + return m_rom[(offset & 0xffff/2) + (m_bank * 0x10000)/2]; + } + + // PROTECTION in 0x400000 - 0x4fffff + /* not 100% correct, there may be some relationship between the reads here + and the writes made at the start of the game.. */ + if (offset >= 0x400000/2 && offset < 0x500000/2) + { + UINT32 retdat = 0; + /* + 04dc10 chifi3, prot_r? 2800 + 04cefa chifi3, prot_r? 65262 + */ + if (space.machine().device("maincpu")->safe_pc() == 0x01782) // makes 'VS' screen appear + { + retdat = space.machine().device("maincpu")->state().state_int(M68K_D3) & 0xff; + retdat <<= 8; + return retdat; + } + else if (space.machine().device("maincpu")->safe_pc() == 0x1c24) // background gfx etc. + { + retdat = space.machine().device("maincpu")->state().state_int(M68K_D3) & 0xff; + retdat <<= 8; + return retdat; + } + else if (space.machine().device("maincpu")->safe_pc() == 0x10c4a) // unknown + { + return space.machine().rand(); + } + else if (space.machine().device("maincpu")->safe_pc() == 0x10c50) // unknown + { + return space.machine().rand(); + } + else if (space.machine().device("maincpu")->safe_pc() == 0x10c52) // relates to the game speed.. + { + retdat = space.machine().device("maincpu")->state().state_int(M68K_D4) & 0xff; + retdat <<= 8; + return retdat; + } + else if (space.machine().device("maincpu")->safe_pc() == 0x061ae) + { + retdat = space.machine().device("maincpu")->state().state_int(M68K_D3) & 0xff; + retdat <<= 8; + return retdat; + } + else if (space.machine().device("maincpu")->safe_pc() == 0x061b0) + { + retdat = space.machine().device("maincpu")->state().state_int(M68K_D3) & 0xff; + retdat <<= 8; + return retdat; + } + else + { + logerror("%06x chifi3, prot_r? %04x\n", space.machine().device("maincpu")->safe_pc(), offset); + } + return 0; + } + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_chinf3_device::write) +{ + if (offset >= 0x600000/2 && offset < 0x700000/2) + { + if (data == 0xf100) // *hit player + m_bank = 1; + else if (data == 0xd700) // title screen.. + m_bank = 7; + else if (data == 0xd300) // character hits floor + m_bank = 3; + else if (data == 0x0000) + m_bank = 0; + else + logerror("%06x chifi3, bankw? %04x %04x\n", space.device().safe_pc(), offset, data); + } +} + +/*------------------------------------------------- + 16 MAHJONG II + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_16mj2_device::read) +{ + if (offset == 0x400004/2) return 0xc900; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + LINGHUAN DAOSHI SUPER MAGICIAN / ELF WOR + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_elfwor_device::read) +{ + // It returns (0x55 @ 0x400000 OR 0xc9 @ 0x400004) AND (0x0f @ 0x400002 OR 0x18 @ 0x400006). + // It is probably best to add handlers for all 4 addresses + if (offset == 0x400000/2) return 0x5500; + if (offset == 0x400002/2) return 0x0f00; + if (offset == 0x400004/2) return 0xc900; + if (offset == 0x400006/2) return 0x1800; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + HUAN LE TAO QI SHU / SMART MOUSE + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_smouse_device::read) +{ + if (offset == 0x400000/2) return 0x5500; + if (offset == 0x400002/2) return 0x0f00; + if (offset == 0x400004/2) return 0xaa00; + if (offset == 0x400006/2) return 0xf000; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + YA SE CHUAN SHUO + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_yasech_device::read) +{ + if (offset == 0x400000/2) return 0x6300; + if (offset == 0x400002/2) return 0x9800; + if (offset == 0x400004/2) return 0xc900; + if (offset == 0x400006/2) return 0x1800; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + KOF98 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_kof98_device::read) +{ + if (offset == 0x480000/2) return 0xaa00; + if (offset == 0x4800e0/2) return 0xaa00; + if (offset == 0x4824a0/2) return 0xaa00; + if (offset == 0x488880/2) return 0xaa00; + if (offset == 0x4a8820/2) return 0x0a00; + if (offset == 0x4f8820/2) return 0x0000; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + KOF 99 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_kof99_device::read_a13) +{ + if (offset == 0x00/2) return 0x00; // startup protection check, chinese message if != 0 + if (offset == 0x02/2) return 0x01; // write 02 to a13002.. shift right 1? + if (offset == 0x3e/2) return 0x1f; // write 3e to a1303e.. shift right 1? + else return 0xffff; +} + +/*------------------------------------------------- + LION KING 2 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_lion2_device::read) +{ + if (offset == 0x400002/2) return m_prot1_data; + if (offset == 0x400006/2) return m_prot2_data; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_lion2_device::write) +{ + if (offset == 0x400000/2) m_prot1_data = data; + if (offset == 0x400004/2) m_prot2_data = data; +} + +/*------------------------------------------------- + LION KING 3 + -------------------------------------------------*/ + +#define MD_LION3_ADDR(a) (((offset << 1) | (m_bank << 15)) & (m_rom_size - 1))/2 + +READ16_MEMBER(md_rom_lion3_device::read) +{ + if (offset < 0x100000/2) + return m_rom[MD_LION3_ADDR(offset)]; + else if (offset >= 0x600000/2 && offset < 0x700000/2) + { + switch (offset & 0x7) + { + case 0: + return m_reg[0]; + case 1: + return m_reg[1]; + case 2: + return m_reg[2]; + default: + logerror("protection read, unknown offset %x\n", offset & 0x7); + break; + } + return 0; + } + + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_lion3_device::write) +{ + if (offset >= 0x600000/2 && offset < 0x700000/2) + { +// printf("protection write, offset %d data %d\n", offset & 0x7, data); + switch (offset & 0x7) + { + case 0x0: + m_reg[0] = data & 0xff; + break; + case 0x1: + m_reg[1] = data & 0xff; + break; + default: + logerror("protection write, unknown offset %d\n", offset & 0x7); + break; + } + + // update m_reg[2] + switch (m_reg[1] & 3) + { + case 0x0: + m_reg[2] = (m_reg[0] << 1); + break; + case 0x1: + m_reg[2] = (m_reg[0] >> 1); + break; + case 0x2: + m_reg[2] = (m_reg[0] >> 4) | ((m_reg[0] & 0x0f) << 4); + break; + case 0x3: + default: + m_reg[2] = (BIT(m_reg[0], 7) << 0) + | (BIT(m_reg[0], 6) << 1) + | (BIT(m_reg[0], 5) << 2) + | (BIT(m_reg[0], 4) << 3) + | (BIT(m_reg[0], 3) << 4) + | (BIT(m_reg[0], 2) << 5) + | (BIT(m_reg[0], 1) << 6) + | (BIT(m_reg[0], 0) << 7); + break; + } + + } + if (offset >= 0x700000/2) + m_bank = data & 0xff; +} + +/*------------------------------------------------- + MA JIANG QING REN / MAHJONG LOVER + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_mjlov_device::read) +{ + if (offset == 0x400000/2) return 0x9000; + if (offset == 0x401000/2) return 0xd300; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + + +/*------------------------------------------------- + SUPER BUBBLE BOBBLE MD + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_sbubl_device::read) +{ + if (offset == 0x400000/2) return 0x5500; + if (offset == 0x400002/2) return 0x0f00; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + SOUL BLADE + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_soulb_device::read) +{ + if (offset == 0x400002/2) return 0x9800; + if (offset == 0x400004/2) return 0xc900; + if (offset == 0x400006/2) return 0xf000; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + POKEMON STADIUM / KAIJU + -------------------------------------------------*/ + +#define MD_POKESTAD_ADDR(a) (((offset << 1) | (m_bank << 15)) & (m_rom_size - 1))/2 + +READ16_MEMBER(md_rom_pokestad_device::read) +{ + if (offset < 0x100000/2) + return m_rom[MD_POKESTAD_ADDR(offset)]; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_pokestad_device::write) +{ + if (offset >= 0x700000/2 && offset < 0x800000/2) + m_bank = data & 0x7f; +} + +/*------------------------------------------------- + POKEMON ALT + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_pokea_device::read_a13) +{ + if (offset == 0x00/2) return 0x14; + if (offset == 0x02/2) return 0x01; + if (offset == 0x3e/2) return 0x1f; + else return 0xffff; +} + +/*------------------------------------------------- + REALTEC + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_realtec_device::read) +{ + if (offset < (m_bank_size * 0x20000)) // two banks of same (variable) size at the bottom of the rom + return m_rom[MD_ADDR((offset + (m_bank_addr * 0x20000)/2))]; + return m_rom[MD_ADDR(((offset & 0x1fff/2) + 0x7e000/2))]; // otherwise it accesses the final 8k of the image +} + +WRITE16_MEMBER(md_rom_realtec_device::write) +{ + if (offset == 0x400000/2) + { + m_old_bank_addr = m_bank_addr; + m_bank_addr = (m_bank_addr & 0x7) | ((data >> 9) & 0x7) << 3; + } + if (offset == 0x402000/2) + { + m_bank_addr = 0; + m_bank_size = (data >> 8) & 0x1f; + } + if (offset == 0x404000/2) + { + m_old_bank_addr = m_bank_addr; + m_bank_addr = (m_bank_addr & 0xf8) | ((data >> 8) & 0x3); + } +} + +/*------------------------------------------------- + RED CLIFF + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_redcl_device::read) +{ + if (offset == 0x400000/2) return 0x55 << 8; + if (offset == 0x400004/2) return -0x56 << 8; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +/*------------------------------------------------- + ROCKMAN X3 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_rx3_device::read_a13) +{ + if (offset == 0) + return 0x0c; + else + return 0xffff; +} + +/*------------------------------------------------- + SQUIRREL KING + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_squir_device::read) +{ + if ((offset >= 0x400000/2) && (offset < 0x400008/2)) + return m_latch; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_squir_device::write) +{ + if (offset >= 0x400000/2 && offset < 0x400008/2) + m_latch = data; +} + +/*------------------------------------------------- + SUPER MARIO BROS + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_smb_device::read_a13) +{ + if (offset == 0) + return 0x0c; + else + return 0xffff; +} + +/*------------------------------------------------- + SUPER MARIO BROS 2 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_smb2_device::read_a13) +{ + if (offset == 0) + return 0x0a; + else + return 0xffff; +} + +/*------------------------------------------------- + SUPER MARIO WORLD 64 + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_smw64_device::read) +{ + // 0x000000-0x0fffff: lower 512KB ROM (up to 0x07ffff) + mirror + // 0x600000-0x6fffff: internal hardware (up to 0x67ffff) + mirror + // Namely, + // * 60xxx = bank1 of the upper 512KB ROM + // * 61xxx = bank2 of the upper 512KB ROM + // * 62xxx = alternate 4KB chunks of 0x0000 ~ 0xffff + // * 63xxx = same as 62xxx + // * 64xxx = returns 0x0000 + // * 65xxx = same as 64xxx + // * 66xxx = CTRL/DATA + // * 67xxx = CTRL/DATA + if (offset < 0x100000/2) + return m_rom[offset & 0x3ffff]; + + if ((offset >= 0x600000/2) && (offset < 0x610000/2)) + return m_rom[(m_latch0 * 0x10000)/2 + (offset & 0x7fff)]; + if ((offset >= 0x610000/2) && (offset < 0x620000/2)) + return m_rom[(m_latch1 * 0x10000)/2 + (offset & 0x7fff)]; + + if ((offset >= 0x620000/2) && (offset < 0x640000/2)) + return (offset & 0x1000/2) ? 0x0000 : 0xffff; + if ((offset >= 0x640000/2) && (offset < 0x660000/2)) + return 0x0000; + + if ((offset >= 0x660000/2) && (offset < 0x670000/2)) + { + offset &= 7; + switch (offset) + { + case 0x0: + case 0x2: + case 0x4: + return m_reg[offset/2]; // DATA1, DATA2, DATA3 + case 0x1: + case 0x3: + case 0x5: + return m_reg[offset/2] + 1; // DATA1+1, DATA2+1, DATA3+1 + case 0x6: + return m_reg[2] + 2; // DATA3+2 + case 0x7: + return m_reg[2] + 3; // DATA3+3 + } + } + if ((offset >= 0x670000/2) && (offset < 0x680000/2)) + { + UINT16 data = (m_ctrl[1] & 0x80) ? ((m_ctrl[2] & 0x40) ? (m_reg[4] & m_reg[5]) : (m_reg[4] ^ 0xff)) : 0x0000; + if (offset & 0x1) // odd offset, return lower 7 bits of the above + return data & 0x7f; + else // even offset, return whole data above, but also update the regs if CTRL3 has 0x80 set + { + if (m_ctrl[2] & 0x80) // update regs if CTRL3 has bit7 set + { + if (m_ctrl[2] & 0x20) + m_reg[2] = (m_reg[5] << 2) & 0xfc; // DATA3 + else + m_reg[0] = ((m_reg[4] << 1) ^ m_reg[3]) & 0xfe; // DATA1 + } + return data; + } + } + return 0xffff; +} + +WRITE16_MEMBER(md_rom_smw64_device::write) +{ + // 0x600000-0x6fffff: internal hardware (up to 0x67ffff) + mirror + // Namely, + // * 62xxx/63xxx = unknown/unmapped + // * 65xxx/66xxx = unknown/unmapped + // * remaining ranges = CTRL/DATA + if ((offset >= 0x600000/2) && (offset < 0x610000/2)) + { + if (offset & 1) + { + if ((m_ctrl[0] & 7) == 0) + m_reg[0] = ((m_reg[0] ^ m_reg[3]) ^ data) & 0xfe; // DATA1 + if ((m_ctrl[0] & 7) == 1) + m_reg[1] = data & 0xfe; // DATA2 + if ((m_ctrl[0] & 7) == 7) + m_latch1 = 8 + ((data & 0x1c) >> 2); // ROM BANKSWITCH $61 + m_reg[3] = data; // DATA4 + } + else + m_ctrl[0] = data; // CTRL1 + } + if ((offset >= 0x610000/2) && (offset < 0x620000/2)) + { + if (offset & 1) + m_ctrl[1] = data; // CTRL2 + } + if ((offset >= 0x640000/2) && (offset < 0x650000/2)) + { + if (offset & 1) + m_reg[5] = data; // DATA6 + else + m_reg[4] = data; // DATA5 + } + if ((offset >= 0x670000/2) && (offset < 0x680000/2)) + { + if (!(offset & 1)) + { + m_ctrl[2] = data; // CTRL3 + if (m_ctrl[1] & 0x80) + m_latch0 = 8 + ((data & 0x1c) >> 2); // ROM BANKSWITCH $60 + } + } +} + +/*------------------------------------------------- + TEKKEN SPECIAL + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_tekkensp_device::read) +{ + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else if ((offset & 0x07) == 1 && m_reg) + return (m_reg - 1) << 8; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_tekkensp_device::write) +{ + if (offset < 0x400000/2) + return; + + // thanks to EkeEke for the documentation + switch (offset & 0x07) + { + case 0x00: + // data output reset ? (game writes $FF before & after protection check) + m_reg = 0; + break; + case 0x01: + // read only ? + break; + case 0x06: + // data output mode bit 0 ? (game writes $01) + break; + case 0x07: + // data output mode bit 1 ? (never written by game) + break; + default: + if (data & 0x100) // data input (only connected to D0 ?) + { + // 4-bit hardware register ($400004 corresponds to bit0, $400006 to bit1, etc) + int shift = (offset - 2) & 3; + m_reg |= (1 << shift); + } + break; + } +} + +/*------------------------------------------------- + TOP FIGHTER + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_topf_device::read) +{ + //cpu #0 (PC=0004CBAE): unmapped program memory word read from 006A35D4 & 00FF -- wants regD7 + if (offset == 0x645b44/2) + { + //cpu #0 (PC=0004DE00): unmapped program memory word write to 00689B80 = 004A & 00FF + //cpu #0 (PC=0004DE08): unmapped program memory word write to 00 = 00B5 & 00FF + //cpu #0 (PC=0004DE0C): unmapped program memory word read from 00645B44 & 00FF + + return 0x9f;//0x25; + } + if (offset == 0x6bd294/2) + { + /* + cpu #0 (PC=00177192): unmapped program memory word write to 006BD240 = 00A8 & 00FF + cpu #0 (PC=0017719A): unmapped program memory word write to 006BD2D2 = 0098 & 00FF + cpu #0 (PC=001771A2): unmapped program memory word read from 006BD294 & 00FF + */ + + if (space.device().safe_pc()==0x1771a2) return 0x50; + else + { + m_latch++; + logerror("%06x topfig_6BD294_r %04x\n",space.device().safe_pc(), m_latch); + return m_latch; + } + } + if (offset == 0x6f5344/2) + { + if (space.device().safe_pc()==0x4C94E) + return space.machine().device("maincpu")->state().state_int((M68K_D0)) & 0xff; + else + { + m_latch++; + logerror("%06x topfig_6F5344_r %04x\n", space.device().safe_pc(), m_latch); + return m_latch; + } + } + + if (offset >= 0x20000/2 && offset < 0x28000/2) + return m_rom[offset + (m_bank[0] * 0x188000)/2]; + + if (offset >= 0x58000/2 && offset < 0x60000/2) + return m_rom[offset + (m_bank[1] * 0x20000)/2]; + + if (offset >= 0x60000/2 && offset < 0x68000/2) + return m_rom[offset + (m_bank[2] * 0x110000)/2]; + + // non-protection accesses + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_topf_device::write) +{ + if (offset >= 0x700000/2 && offset < 0x800000/2) + { + if (data == 0x002a) + m_bank[2] = 1; // == 0x2e*0x8000?! + else if (data==0x0035) // characters ingame + m_bank[0] = 1; // == 0x35*0x8000 + else if (data==0x000f) // special moves + m_bank[1] = 1; // == 0xf*0x8000 + else if (data==0x0000) + { + m_bank[0] = 0; + m_bank[1] = 0; + m_bank[2] = 0; + } + else + logerror("%06x offset %06x, data %04x\n", space.device().safe_pc(), offset, data); + } +} + +/*------------------------------------------------- + RADICA TV GAMES [to be split...] + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_radica_device::read) +{ + return m_rom[(((m_bank * 0x10000) + (offset << 1)) & (m_rom_size - 1))/2]; +} + +READ16_MEMBER(md_rom_radica_device::read_a13) +{ + if (offset < 0x80) + m_bank = offset & 0x3f; + return 0; +} + +/*------------------------------------------------- + BEGGAR PRINCE + This game uses cart which is the same as SEGA_SRAM + + bankswitch mechanism for first 256KB of the image: + depending on bit7 of the value written at 0xe00/2, + accesses to 0x00000-0x3ffff go to the first 256KB + of ROM, or to the second to last 256KB chunk (usually + mapped to 0x380000-0x3bffff). SRAM is mapped at + the end of ROM. + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_beggarp_device::read) +{ + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active) + return m_nvram[offset & 0x3fff]; + + if (offset < 0x040000/2) + return m_mode ? m_rom[offset + 0x380000/2] : m_rom[offset]; + else if (offset < 0x400000/2) + return m_rom[offset & 0x1fffff]; + + return 0xffff; +} + +WRITE16_MEMBER(md_rom_beggarp_device::write) +{ + if (offset >= 0x0e00/2 && offset < 0x0f00/2) + m_mode = BIT(data, 7); + + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active && !m_nvram_readonly) + m_nvram[offset & 0x3fff] = data; +} + +WRITE16_MEMBER(md_rom_beggarp_device::write_a13) +{ + if (offset == 0xf0/2) + { + /* unsure if this is actually supposed to toggle or just switch on? yet to encounter game that uses this */ + m_nvram_active = BIT(data, 0); + m_nvram_readonly = BIT(data, 1); + + // since a lot of generic carts ends up here if loaded from fullpath + // we turn on nvram (with m_nvram_handlers_installed) only if they toggle it on by writing here! + if (m_nvram_active) + m_nvram_handlers_installed = 1; + } +} + +/*------------------------------------------------- + LEGEND OF WUKONG + This game uses cart which is the same as SEGA_SRAM + + bankswitch mechanism for last 128KB of the image: + first 2MB of ROM is loaded in 0-0x200000 and + mirrored in 0x200000-0x400000, but depending on + bit7 of the value written at 0xe00/2 accesses to + 0x200000-0x21ffff go either to the "physical" address + (i.e. last 128K of ROM) or to the "memory" address + (i.e. mirror of first 128K) + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_wukong_device::read) +{ + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active) + return m_nvram[offset - m_nvram_start/2]; + + // here can access both last 128K of the ROM and the first 128K, depending of bit7 of m_mode + if (offset >= 0x200000/2 && offset < 0x220000/2) + return !m_mode ? m_rom[offset] : m_rom[offset & 0xffff]; + else if (offset < 0x400000/2) + return m_rom[offset & 0xfffff]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_wukong_device::write) +{ + if (offset < 0x100000/2) // it actually writes to 0xe00/2 + m_mode = BIT(data, 7); + + if (offset >= m_nvram_start/2 && offset <= m_nvram_end/2 && m_nvram_active && !m_nvram_readonly) + m_nvram[offset - m_nvram_start/2] = data; +} + +WRITE16_MEMBER(md_rom_wukong_device::write_a13) +{ + if (offset == 0xf0/2) + { + /* unsure if this is actually supposed to toggle or just switch on? yet to encounter game that uses this */ + m_nvram_active = BIT(data, 0); + m_nvram_readonly = BIT(data, 1); + + // since a lot of generic carts ends up here if loaded from fullpath + // we turn on nvram (with m_nvram_handlers_installed) only if they toggle it on by writing here! + if (m_nvram_active) + m_nvram_handlers_installed = 1; + } +} diff --git a/src/emu/bus/megadrive/rom.h b/src/emu/bus/megadrive/rom.h new file mode 100644 index 00000000000..53eae8fc39a --- /dev/null +++ b/src/emu/bus/megadrive/rom.h @@ -0,0 +1,583 @@ +#ifndef __MD_ROM_H +#define __MD_ROM_H + +#include "md_slot.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> md_std_rom_device + +class md_std_rom_device : public device_t, + public device_md_cart_interface +{ +public: + // construction/destruction + md_std_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + md_std_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() {}; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read) { if (offset < 0x400000/2) return m_rom[MD_ADDR(offset)]; else return 0xffff; }; + virtual DECLARE_WRITE16_MEMBER(write) { }; +}; + +// ======================> md_rom_sram_device + +class md_rom_sram_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_sram_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + virtual DECLARE_WRITE16_MEMBER(write_a13); +}; + +// ======================> md_rom_fram_device + +class md_rom_fram_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_fram_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + virtual DECLARE_READ16_MEMBER(read_a13); + virtual DECLARE_WRITE16_MEMBER(write_a13); +}; + +// ======================> md_rom_ssf2_device + +class md_rom_ssf2_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_ssf2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write_a13); + +private: + UINT8 m_bank[16]; + int m_lastoff, m_lastdata; +}; + +// ======================> md_rom_cm2in1_device + +class md_rom_cm2in1_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_cm2in1_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + +private: + int m_base; +}; + + +// ======================> md_rom_mcpirate_device + +class md_rom_mcpirate_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_mcpirate_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write_a13); + +private: + UINT8 m_bank; +}; + + +// ======================> md_rom_bugslife_device + +class md_rom_bugslife_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_bugslife_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read_a13); +}; + +// ======================> md_rom_chinf3_device + +class md_rom_chinf3_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_chinf3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + int m_bank; +}; + +// ======================> md_rom_16mj2_device + +class md_rom_16mj2_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_16mj2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_elfwor_device + +class md_rom_elfwor_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_elfwor_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_yasech_device + +class md_rom_yasech_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_yasech_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_kof98_device + +class md_rom_kof98_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_kof98_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_kof99_device + +class md_rom_kof99_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_kof99_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read_a13); +}; + +// ======================> md_rom_lion2_device + +class md_rom_lion2_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_lion2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT16 m_prot1_data, m_prot2_data; +}; + +// ======================> md_rom_lion3_device + +class md_rom_lion3_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_lion3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT8 m_reg[3]; + UINT16 m_bank; +}; + +// ======================> md_rom_mjlov_device + +class md_rom_mjlov_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_mjlov_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_pokea_device + +class md_rom_pokea_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_pokea_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read_a13); +}; + +// ======================> md_rom_pokestad_device + +class md_rom_pokestad_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_pokestad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT8 m_bank; +}; + +// ======================> md_rom_realtec_device + +class md_rom_realtec_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_realtec_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT16 m_bank_addr, m_bank_size, m_old_bank_addr; +}; + +// ======================> md_rom_redcl_device + +class md_rom_redcl_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_redcl_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_rx3_device + +class md_rom_rx3_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_rx3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read_a13); +}; + +// ======================> md_rom_sbubl_device + +class md_rom_sbubl_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_sbubl_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_smb_device + +class md_rom_smb_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_smb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read_a13); +}; + +// ======================> md_rom_smb2_device + +class md_rom_smb2_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_smb2_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read_a13); +}; + +// ======================> md_rom_smw64_device + +class md_rom_smw64_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_smw64_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT32 m_latch0, m_latch1; + UINT16 m_reg[6]; + UINT16 m_ctrl[3]; +}; + +// ======================> md_rom_smouse_device + +class md_rom_smouse_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_smouse_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + + +// ======================> md_rom_soulb_device + +class md_rom_soulb_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_soulb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); +}; + +// ======================> md_rom_squir_device + +class md_rom_squir_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_squir_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT16 m_latch; +}; + +// ======================> md_rom_tekkensp_device + +class md_rom_tekkensp_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_tekkensp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT16 m_reg; +}; + +// ======================> md_rom_topf_device + +class md_rom_topf_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_topf_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + UINT16 m_latch; + UINT8 m_bank[3]; +}; + +// ======================> md_rom_radica_device + +class md_rom_radica_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_radica_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_READ16_MEMBER(read_a13); + +private: + UINT8 m_bank; +}; + +// ======================> md_rom_beggarp_device + +class md_rom_beggarp_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_beggarp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + virtual DECLARE_WRITE16_MEMBER(write_a13); + +private: + UINT8 m_mode, m_lock; +}; + +// ======================> md_rom_wukong_device + +class md_rom_wukong_device : public md_std_rom_device +{ +public: + // construction/destruction + md_rom_wukong_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + virtual DECLARE_WRITE16_MEMBER(write_a13); + +private: + UINT8 m_mode; +}; + + + +// device type definition +extern const device_type MD_STD_ROM; +extern const device_type MD_ROM_SRAM; +extern const device_type MD_ROM_FRAM; +extern const device_type MD_ROM_CM2IN1; +extern const device_type MD_ROM_16MJ2; +extern const device_type MD_ROM_BUGSLIFE; +extern const device_type MD_ROM_CHINF3; +extern const device_type MD_ROM_ELFWOR; +extern const device_type MD_ROM_YASECH; +extern const device_type MD_ROM_KOF98; +extern const device_type MD_ROM_KOF99; +extern const device_type MD_ROM_LION2; +extern const device_type MD_ROM_LION3; +extern const device_type MD_ROM_MCPIR; +extern const device_type MD_ROM_MJLOV; +extern const device_type MD_ROM_POKEA; +extern const device_type MD_ROM_POKESTAD; +extern const device_type MD_ROM_REALTEC; +extern const device_type MD_ROM_REDCL; +extern const device_type MD_ROM_RX3; +extern const device_type MD_ROM_SBUBL; +extern const device_type MD_ROM_SMB; +extern const device_type MD_ROM_SMB2; +extern const device_type MD_ROM_SMW64; +extern const device_type MD_ROM_SMOUSE; +extern const device_type MD_ROM_SOULB; +extern const device_type MD_ROM_SSF2; +extern const device_type MD_ROM_SQUIR; +extern const device_type MD_ROM_TEKKENSP; +extern const device_type MD_ROM_TOPF; +extern const device_type MD_ROM_RADICA; +extern const device_type MD_ROM_BEGGARP; +extern const device_type MD_ROM_WUKONG; + +#endif diff --git a/src/emu/bus/megadrive/sk.c b/src/emu/bus/megadrive/sk.c new file mode 100644 index 00000000000..a86e055b6bb --- /dev/null +++ b/src/emu/bus/megadrive/sk.c @@ -0,0 +1,93 @@ +/*********************************************************************************************************** + + + Sonic & Knuckles pass-thorugh cart emulation + + + TODO: currently we only support loading of base carts with no bankswitch or protection... + shall we support other as well? + + + ***********************************************************************************************************/ + + + + +#include "emu.h" +#include "sk.h" +#include "rom.h" + + +//------------------------------------------------- +// md_rom_device - constructor +//------------------------------------------------- + +const device_type MD_ROM_SK = &device_creator<md_rom_sk_device>; + + +md_rom_sk_device::md_rom_sk_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_md_cart_interface( mconfig, *this ), + m_exp(*this, "subslot") +{ +} + +md_rom_sk_device::md_rom_sk_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MD_ROM_SK, "MD Sonic & Knuckles", tag, owner, clock, "md_rom_sk", __FILE__), + device_md_cart_interface( mconfig, *this ), + m_exp(*this, "subslot") +{ +} + + +void md_rom_sk_device::device_start() +{ +} + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +READ16_MEMBER(md_rom_sk_device::read) +{ + if (m_exp->m_cart != NULL && m_exp->m_cart->get_rom_base() != NULL && offset >= 0x200000/2 && offset < (0x200000 + m_exp->m_cart->get_rom_size())/2) + return m_exp->m_cart->m_rom[offset - 0x200000/2]; + if (offset < 0x400000/2) + return m_rom[MD_ADDR(offset)]; + else + return 0xffff; +} + +WRITE16_MEMBER(md_rom_sk_device::write) +{ +// should there be anything here? +} + +//------------------------------------------------- +// MACHINE_CONFIG_FRAGMENT( sk_slot ) +//------------------------------------------------- + +static SLOT_INTERFACE_START(sk_sub_cart) + SLOT_INTERFACE_INTERNAL("rom", MD_STD_ROM) + SLOT_INTERFACE_INTERNAL("rom_svp", MD_STD_ROM) + SLOT_INTERFACE_INTERNAL("rom_sram", MD_ROM_SRAM) + SLOT_INTERFACE_INTERNAL("rom_sramsafe", MD_ROM_SRAM) + SLOT_INTERFACE_INTERNAL("rom_fram", MD_ROM_FRAM) +// add all types?? +SLOT_INTERFACE_END + +static MACHINE_CONFIG_FRAGMENT( sk_slot ) + MCFG_MD_CARTRIDGE_ADD("subslot", sk_sub_cart, NULL) + MCFG_MD_CARTRIDGE_NOT_MANDATORY +MACHINE_CONFIG_END + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor md_rom_sk_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sk_slot ); +} diff --git a/src/emu/bus/megadrive/sk.h b/src/emu/bus/megadrive/sk.h new file mode 100644 index 00000000000..45671bdef34 --- /dev/null +++ b/src/emu/bus/megadrive/sk.h @@ -0,0 +1,33 @@ +#ifndef __MD_SK_H +#define __MD_SK_H + +#include "md_slot.h" + + +// ======================> md_rom_sk_device + +class md_rom_sk_device : public device_t, + public device_md_cart_interface +{ +public: + // construction/destruction + md_rom_sk_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + md_rom_sk_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + +private: + required_device<md_cart_slot_device> m_exp; +}; + + +// device type definition +extern const device_type MD_ROM_SK; + +#endif diff --git a/src/emu/bus/megadrive/stm95.c b/src/emu/bus/megadrive/stm95.c new file mode 100644 index 00000000000..734a0a10397 --- /dev/null +++ b/src/emu/bus/megadrive/stm95.c @@ -0,0 +1,266 @@ +/*************************************************************************** + + + MegaDrive / Genesis Cart + STM95 EEPROM device + + + Emulation by MetalliC, converted to slot by Fabio Priuli + + + TO DO: split STM95 to a separate device... + +***************************************************************************/ + + +#include "emu.h" +#include "stm95.h" + + +stm95_eeprom_device::stm95_eeprom_device(running_machine &machine, UINT8 *eeprom) : + stm_state(IDLE), + stream_pos(0), + m_machine(machine) +{ + eeprom_data = eeprom; + state_save_register_item(machine, "STM95", NULL, 0, latch); + state_save_register_item(machine, "STM95", NULL, 0, reset_line); + state_save_register_item(machine, "STM95", NULL, 0, sck_line); + state_save_register_item(machine, "STM95", NULL, 0, WEL); + state_save_register_item(machine, "STM95", NULL, 0, stream_pos); + state_save_register_item(machine, "STM95", NULL, 0, stream_data); + state_save_register_item(machine, "STM95", NULL, 0, eeprom_addr); +} + +void stm95_eeprom_device::set_cs_line(int state) +{ + reset_line = state; + if (reset_line != CLEAR_LINE) + { + stream_pos = 0; + stm_state = IDLE; + } +} + +void stm95_eeprom_device::set_si_line(int state) +{ + latch = state; +} + +int stm95_eeprom_device::get_so_line(void) +{ + if (stm_state == READING || stm_state == CMD_RDSR) + return (stream_data >> 8) & 1; + else + return 0; +} + +void stm95_eeprom_device::set_sck_line(int state) +{ + if (reset_line == CLEAR_LINE) + { + if (state == ASSERT_LINE && sck_line == CLEAR_LINE) + { + switch (stm_state) + { + case IDLE: + stream_data = (stream_data << 1) | (latch ? 1 : 0); + stream_pos++; + if (stream_pos == 8) + { + stream_pos = 0; + //printf("STM95 EEPROM: got cmd %02X\n", stream_data&0xff); + switch(stream_data & 0xff) + { + case 0x01: // write status register + if (WEL != 0) + stm_state = CMD_WRSR; + WEL = 0; + break; + case 0x02: // write + if (WEL != 0) + stm_state = CMD_WRITE; + stream_data = 0; + WEL = 0; + break; + case 0x03: // read + stm_state = M95320_CMD_READ; + stream_data = 0; + break; + case 0x04: // write disable + WEL = 0; + break; + case 0x05: // read status register + stm_state = CMD_RDSR; + stream_data = WEL<<1; + break; + case 0x06: // write enable + WEL = 1; + break; + default: + logerror("STM95 EEPROM: unknown cmd %02X\n", stream_data&0xff); + } + } + break; + case CMD_WRSR: + stream_pos++; // just skip, don't care block protection + if (stream_pos == 8) + { + stm_state = IDLE; + stream_pos = 0; + } + break; + case CMD_RDSR: + stream_data = stream_data<<1; + stream_pos++; + if (stream_pos == 8) + { + stm_state = IDLE; + stream_pos = 0; + } + break; + case M95320_CMD_READ: + stream_data = (stream_data << 1) | (latch ? 1 : 0); + stream_pos++; + if (stream_pos == 16) + { + eeprom_addr = stream_data & (M95320_SIZE - 1); + stream_data = eeprom_data[eeprom_addr]; + stm_state = READING; + stream_pos = 0; + } + break; + case READING: + stream_data = stream_data<<1; + stream_pos++; + if (stream_pos == 8) + { + if (++eeprom_addr == M95320_SIZE) + eeprom_addr = 0; + stream_data |= eeprom_data[eeprom_addr]; + stream_pos = 0; + } + break; + case CMD_WRITE: + stream_data = (stream_data << 1) | (latch ? 1 : 0); + stream_pos++; + if (stream_pos == 16) + { + eeprom_addr = stream_data & (M95320_SIZE - 1); + stm_state = WRITING; + stream_pos = 0; + } + break; + case WRITING: + stream_data = (stream_data << 1) | (latch ? 1 : 0); + stream_pos++; + if (stream_pos == 8) + { + eeprom_data[eeprom_addr] = stream_data; + if (++eeprom_addr == M95320_SIZE) + eeprom_addr = 0; + stream_pos = 0; + } + break; + } + } + } + sck_line = state; +} + + + +//------------------------------------------------- +// md_rom_device - constructor +//------------------------------------------------- + +const device_type MD_EEPROM_STM95 = &device_creator<md_eeprom_stm95_device>; + + +md_eeprom_stm95_device::md_eeprom_stm95_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_md_cart_interface( mconfig, *this ) +{ +} + +md_eeprom_stm95_device::md_eeprom_stm95_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MD_EEPROM_STM95, "MD Cart + EEPROM STM95", tag, owner, clock, "md_eeprom_stm95", __FILE__), + device_md_cart_interface( mconfig, *this ) +{ +} + + +void md_eeprom_stm95_device::device_start() +{ + nvram_alloc(machine(), M95320_SIZE); + m_stm95 = auto_alloc(machine(), stm95_eeprom_device(machine(), (UINT8*)get_nvram_base())); + + save_item(NAME(m_rdcnt)); + save_item(NAME(m_bank)); +} + +void md_eeprom_stm95_device::device_reset() +{ + m_rdcnt = 0; + m_bank[0] = 0; + m_bank[1] = 0; + m_bank[2] = 0; +} + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +READ16_MEMBER(md_eeprom_stm95_device::read) +{ + if (offset == 0x0015e6/2 || offset == 0x0015e8/2) + { + // ugly hack until we don't know much about game protection + // first 3 reads from 15e6 return 0x00000010, then normal 0x00018010 value for crc check + UINT16 res; + offset -= 0x0015e6/2; + logerror("read 0x15e6 %d\n", m_rdcnt); + if (m_rdcnt < 6) + { + m_rdcnt++; + res = offset ? 0x10 : 0; + } + else + res = offset ? 0x8010 : 0x0001; + return res; + } + if (offset < 0x280000/2) + return m_rom[offset]; + else // last 0x180000 are bankswitched + { + UINT8 bank = (offset - 0x280000/2) >> 18; + return m_rom[(offset & 0x7ffff/2) + (m_bank[bank] * 0x80000)/2]; + } +} + +READ16_MEMBER(md_eeprom_stm95_device::read_a13) +{ + if (offset == 0x0a/2) + { + return m_stm95->get_so_line() & 1; + } + return 0xffff; +} + +WRITE16_MEMBER(md_eeprom_stm95_device::write_a13) +{ + if (offset == 0x00/2) + { + logerror("A13001 write %02x\n", data); + } + else if (offset < 0x08/2) + { + m_bank[offset - 1] = data & 0x0f; + } + else if (offset < 0x0a/2) + { + m_stm95->set_si_line(BIT(data, 0)); + m_stm95->set_sck_line(BIT(data, 1)); + m_stm95->set_halt_line(BIT(data, 2)); + m_stm95->set_cs_line(BIT(data, 3)); + } +} diff --git a/src/emu/bus/megadrive/stm95.h b/src/emu/bus/megadrive/stm95.h new file mode 100644 index 00000000000..369f7bffd51 --- /dev/null +++ b/src/emu/bus/megadrive/stm95.h @@ -0,0 +1,84 @@ +#ifndef __MD_STM95_H +#define __MD_STM95_H + +#include "md_slot.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +/* ST M95320 32Kbit serial EEPROM implementation */ +// TO DO: STM95 should be made a separate EEPROM device and this should be merged with md_eeprom.c! + +#define M95320_SIZE 0x1000 + +enum STMSTATE +{ + IDLE = 0, + CMD_WRSR, + CMD_RDSR, + M95320_CMD_READ, + CMD_WRITE, + READING, + WRITING +}; + +class stm95_eeprom_device +{ +public: + stm95_eeprom_device(running_machine &machine, UINT8 *eeprom); + running_machine &machine() const { return m_machine; } + + UINT8 *eeprom_data; + void set_cs_line(int); + void set_halt_line(int state) {}; // not implemented + void set_si_line(int); + void set_sck_line(int state); + int get_so_line(void); + +protected: + int latch; + int reset_line; + int sck_line; + int WEL; + + STMSTATE stm_state; + int stream_pos; + int stream_data; + int eeprom_addr; + + running_machine& m_machine; +}; + + +// ======================> md_eeprom_stm95_device + +class md_eeprom_stm95_device : public device_t, + public device_md_cart_interface +{ +public: + // construction/destruction + md_eeprom_stm95_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + md_eeprom_stm95_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_READ16_MEMBER(read_a13); + virtual DECLARE_WRITE16_MEMBER(write_a13); + +private: + UINT8 m_bank[3]; + int m_rdcnt; + + stm95_eeprom_device *m_stm95; +}; + + +// device type definition +extern const device_type MD_EEPROM_STM95; + +#endif diff --git a/src/emu/bus/megadrive/svp.c b/src/emu/bus/megadrive/svp.c new file mode 100644 index 00000000000..5746720d58d --- /dev/null +++ b/src/emu/bus/megadrive/svp.c @@ -0,0 +1,474 @@ +/****************************************** SVP related *****************************************/ + +/* + * Emulator of memory controller in SVP chip + * + * Copyright 2008, Grazvydas Ignotas + * based on RE work by Tasco Deluxe + * + * SSP1601 EXT registers are mapped as I/O ports due to their function + * (they are interfaced through external bus), and are named as follows + * (these are unofficial names, official ones are unknown): + * EXT0: PM0 - programmable register 0 + * EXT1: PM1 - ... 1 + * EXT2: PM2 - ... 2 + * EXT3: XST - external status. Can also act as PM. + * EXT4: PM4 - ... 4 + * EXT5: (unused) + * EXT6: PMC - programmable memory register control (PMAC). + * EXT7: AL - although internal to SSP1601, it still causes bus access + * + * Depending on GPO bits in status register, PM0, PM1, PM2 and XST can act as + * external status registers, os as programmable memory registers. PM4 always + * acts as PM register (independent on GPO bits). + */ + + +#include "emu.h" +#include "svp.h" + + +//------------------------------------------------- +// md_rom_device - constructor +//------------------------------------------------- + +const device_type MD_ROM_SVP = &device_creator<md_rom_svp_device>; + +md_rom_svp_device::md_rom_svp_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_md_cart_interface( mconfig, *this ), + m_svp(*this, "svp"), + m_test_ipt(*this, "MEMORY_TEST") +{ +} + +md_rom_svp_device::md_rom_svp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, MD_ROM_SVP, "MD Virtua Racing", tag, owner, clock, "md_rom_svp", __FILE__), + device_md_cart_interface( mconfig, *this ), + m_svp(*this, "svp"), + m_test_ipt(*this, "MEMORY_TEST") +{ +} + + +#define SSP_PMC_HAVE_ADDR 1 // address written to PMAC, waiting for mode +#define SSP_PMC_SET 2 // PMAC is set, PMx can be programmed + +#define MASTER_CLOCK_NTSC 53693175 + +// HELPERS + +INLINE int get_inc(int mode) +{ + int inc = (mode >> 11) & 7; + if (inc != 0) { + if (inc != 7) inc--; + inc = 1 << inc; // 0 1 2 4 8 16 32 128 + if (mode & 0x8000) inc = -inc; // decrement mode + } + return inc; +} + +INLINE void overwrite_write(UINT16 *dst, UINT16 d) +{ + if (d & 0xf000) { *dst &= ~0xf000; *dst |= d & 0xf000; } + if (d & 0x0f00) { *dst &= ~0x0f00; *dst |= d & 0x0f00; } + if (d & 0x00f0) { *dst &= ~0x00f0; *dst |= d & 0x00f0; } + if (d & 0x000f) { *dst &= ~0x000f; *dst |= d & 0x000f; } +} + + + +UINT32 md_rom_svp_device::pm_io(int reg, int write, UINT32 d) +{ + if (m_emu_status & SSP_PMC_SET) + { + if (write) + m_pmac_write[reg] = m_pmc.d; + else + m_pmac_read[reg] = m_pmc.d; + + m_emu_status &= ~SSP_PMC_SET; + return 0; + } + + // just in case + if (m_emu_status & SSP_PMC_HAVE_ADDR) + m_emu_status &= ~SSP_PMC_HAVE_ADDR; + + if (reg == 4 || (m_svp->state().state_int(SSP_ST) & 0x60)) + { +#define CADDR ((((mode<<16)&0x7f0000)|addr)<<1) + UINT16 *dram = (UINT16 *)m_dram; + if (write) + { + int mode = m_pmac_write[reg] >> 16; + int addr = m_pmac_write[reg] & 0xffff; + + if ((mode & 0x43ff) == 0x0018) // DRAM + { + int inc = get_inc(mode); + if (mode & 0x0400) + overwrite_write(&dram[addr], d); + else + dram[addr] = d; + m_pmac_write[reg] += inc; + } + else if ((mode & 0xfbff) == 0x4018) // DRAM, cell inc + { + if (mode & 0x0400) + overwrite_write(&dram[addr], d); + else + dram[addr] = d; + m_pmac_write[reg] += (addr & 1) ? 31 : 1; + } + else if ((mode & 0x47ff) == 0x001c) // IRAM + { + int inc = get_inc(mode); + ((UINT16 *)m_iram)[addr & 0x3ff] = d; + m_pmac_write[reg] += inc; + } + else + { + logerror("ssp FIXME: PM%i unhandled write mode %04x, [%06x] %04x\n", + reg, mode, CADDR, d); + } + } + else + { + int mode = m_pmac_read[reg] >> 16; + int addr = m_pmac_read[reg] & 0xffff; + if ((mode & 0xfff0) == 0x0800) // ROM, inc 1, verified to be correct + { + UINT16 *ROM = (UINT16 *)get_rom_base(); + m_pmac_read[reg] += 1; + d = ROM[addr | ((mode & 0xf) << 16)]; + } + else if ((mode & 0x47ff) == 0x0018) // DRAM + { + int inc = get_inc(mode); + d = dram[addr]; + m_pmac_read[reg] += inc; + } + else + { + logerror("ssp FIXME: PM%i unhandled read mode %04x, [%06x]\n", + reg, mode, CADDR); + d = 0; + } + } + + // PMC value corresponds to last PMR accessed (not sure). + if (write) + m_pmc.d = m_pmac_write[reg]; + else + m_pmc.d = m_pmac_read[reg]; + + return d; + } + + return (UINT32)-1; +} + +READ16_MEMBER( md_rom_svp_device::read_pm0 ) +{ + UINT32 d = pm_io(0, 0, 0); + if (d != (UINT32)-1) + return d; + d = m_xst2; + m_xst2 &= ~2; // ? + return d; +} + +WRITE16_MEMBER( md_rom_svp_device::write_pm0 ) +{ + UINT32 r = pm_io(0, 1, data); + if (r != (UINT32)-1) + return; + m_xst2 = data; // ? +} + +READ16_MEMBER( md_rom_svp_device::read_pm1 ) +{ + UINT32 r = pm_io(1, 0, 0); + if (r != (UINT32)-1) + return r; + logerror("svp: PM1 acces in non PM mode?\n"); + return 0; +} + +WRITE16_MEMBER( md_rom_svp_device::write_pm1 ) +{ + UINT32 r = pm_io(1, 1, data); + if (r != (UINT32)-1) + return; + logerror("svp: PM1 acces in non PM mode?\n"); +} + +READ16_MEMBER( md_rom_svp_device::read_pm2 ) +{ + UINT32 r = pm_io(2, 0, 0); + if (r != (UINT32)-1) + return r; + logerror("svp: PM2 acces in non PM mode?\n"); + return 0; +} + +WRITE16_MEMBER( md_rom_svp_device::write_pm2 ) +{ + UINT32 r = pm_io(2, 1, data); + if (r != (UINT32)-1) + return; + logerror("svp: PM2 acces in non PM mode?\n"); +} + +READ16_MEMBER( md_rom_svp_device::read_xst ) +{ + UINT32 d = pm_io(3, 0, 0); + if (d != (UINT32)-1) + return d; + return m_xst; +} + +WRITE16_MEMBER( md_rom_svp_device::write_xst ) +{ + UINT32 r = pm_io(3, 1, data); + if (r != (UINT32)-1) + return; + m_xst2 |= 1; + m_xst = data; +} + +READ16_MEMBER( md_rom_svp_device::read_pm4 ) +{ + return pm_io(4, 0, 0); +} + +WRITE16_MEMBER( md_rom_svp_device::write_pm4 ) +{ + pm_io(4, 1, data); +} + +READ16_MEMBER( md_rom_svp_device::read_pmc ) +{ + if (m_emu_status & SSP_PMC_HAVE_ADDR) + { + m_emu_status |= SSP_PMC_SET; + m_emu_status &= ~SSP_PMC_HAVE_ADDR; + return ((m_pmc.w.l << 4) & 0xfff0) | ((m_pmc.w.l >> 4) & 0xf); + } + else + { + m_emu_status |= SSP_PMC_HAVE_ADDR; + return m_pmc.w.l; + } +} + +WRITE16_MEMBER( md_rom_svp_device::write_pmc ) +{ + if (m_emu_status & SSP_PMC_HAVE_ADDR) + { + m_emu_status |= SSP_PMC_SET; + m_emu_status &= ~SSP_PMC_HAVE_ADDR; + m_pmc.w.h = data; + } + else + { + m_emu_status |= SSP_PMC_HAVE_ADDR; + m_pmc.w.l = data; + } +} + +READ16_MEMBER( md_rom_svp_device::read_al ) +{ + m_emu_status &= ~(SSP_PMC_SET | SSP_PMC_HAVE_ADDR); + return 0; +} + +WRITE16_MEMBER( md_rom_svp_device::write_al ) +{ +} + + +READ16_MEMBER( md_rom_svp_device::rom_read1 ) +{ + UINT16 *IRAM = (UINT16 *)m_iram; + return IRAM[offset]; +} + +READ16_MEMBER( md_rom_svp_device::rom_read2 ) +{ + return m_rom[offset + 0x800/2]; +} + +int md_rom_svp_device::read_test() +{ + return m_test_ipt->read(); +} + + +static INPUT_PORTS_START( md_svp ) + PORT_START("MEMORY_TEST") /* special memtest mode */ + PORT_CONFNAME( 0x01, 0x00, "SVP Test" ) + PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) + PORT_CONFSETTING( 0x01, DEF_STR( On ) ) +INPUT_PORTS_END + +//------------------------------------------------- +// ADDRESS_MAP( svp_ssp_map ) +//------------------------------------------------- + +ADDRESS_MAP_START( md_svp_ssp_map, AS_PROGRAM, 16, md_rom_svp_device ) +// AM_RANGE(0x0000, 0x03ff) AM_READ(rom_read1) +// AM_RANGE(0x0400, 0xffff) AM_READ(rom_read2) + AM_RANGE(0x0000, 0x03ff) AM_ROMBANK("iram_svp") + AM_RANGE(0x0400, 0xffff) AM_ROMBANK("cart_svp") +ADDRESS_MAP_END + +//------------------------------------------------- +// ADDRESS_MAP( svp_ext_map ) +//------------------------------------------------- + +ADDRESS_MAP_START( md_svp_ext_map, AS_IO, 16, md_rom_svp_device ) + ADDRESS_MAP_GLOBAL_MASK(0xf) + AM_RANGE(0*2, 0*2+1) AM_READWRITE(read_pm0, write_pm0) + AM_RANGE(1*2, 1*2+1) AM_READWRITE(read_pm1, write_pm1) + AM_RANGE(2*2, 2*2+1) AM_READWRITE(read_pm2, write_pm2) + AM_RANGE(3*2, 3*2+1) AM_READWRITE(read_xst, write_xst) + AM_RANGE(4*2, 4*2+1) AM_READWRITE(read_pm4, write_pm4) + AM_RANGE(6*2, 6*2+1) AM_READWRITE(read_pmc, write_pmc) + AM_RANGE(7*2, 7*2+1) AM_READWRITE(read_al, write_al) +ADDRESS_MAP_END + +//------------------------------------------------- +// MACHINE_DRIVER( md_svp ) +//------------------------------------------------- + +static MACHINE_CONFIG_FRAGMENT( md_svp ) + MCFG_CPU_ADD("svp", SSP1601, MASTER_CLOCK_NTSC / 7 * 3) /* ~23 MHz (guessed) */ + MCFG_CPU_PROGRAM_MAP(md_svp_ssp_map) + MCFG_CPU_IO_MAP(md_svp_ext_map) +MACHINE_CONFIG_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +machine_config_constructor md_rom_svp_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( md_svp ); +} + +ioport_constructor md_rom_svp_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( md_svp ); +} + + +void md_rom_svp_device::set_bank_to_rom(const char *banktag, UINT32 offset) +{ + if (membank(banktag)) + membank(banktag)->set_base(m_rom + offset); +} + + +void md_rom_svp_device::device_start() +{ + memset(m_pmac_read, 0, ARRAY_LENGTH(m_pmac_read)); + memset(m_pmac_write, 0, ARRAY_LENGTH(m_pmac_write)); + m_pmc.d = 0; + m_pmc.w.l = 0; + m_pmc.w.h = 0; + m_emu_status = 0; + m_xst = 0; + m_xst2 = 0; + + // SVP stuff + // DRAM + m_dram = auto_alloc_array(machine(), UINT8, 0x20000); + // IRAM + m_iram = auto_alloc_array(machine(), UINT8, 0x800); + this->membank("iram_svp")->set_base(m_iram); + // the other bank, "cart_svp", is setup at call_load + + save_item(NAME(m_pmac_read)); + save_item(NAME(m_pmac_write)); + save_item(NAME(m_emu_status)); + save_item(NAME(m_xst)); + save_item(NAME(m_xst2)); + save_item(NAME(m_pmc.d)); + save_item(NAME(m_pmc.w.l)); + save_item(NAME(m_pmc.w.h)); + save_pointer(NAME(m_dram), 0x20000); + save_pointer(NAME(m_iram), 0x800); +} + +READ16_MEMBER(md_rom_svp_device::read) +{ + UINT16 *DRAM = (UINT16 *)m_dram; + + if (offset >= 0x300000/2 && offset < 0x320000/2) + { + return DRAM[offset - 0x300000/2]; + } + else if (offset >= 0x390000/2 && offset < 0x3a0000/2) + { + // this is rewritten 68k test code + UINT32 a1 = offset - 0x390000/2; + a1 = (a1 & 0x7001) | ((a1 & 0x3e) << 6) | ((a1 & 0xfc0) >> 5); + return DRAM[a1]; + } + else if (offset >= 0x3a0000/2 && offset < 0x3b0000/2) + { + // this is rewritten 68k test code + UINT32 a1 = offset - 0x3a0000/2; + a1 = (a1 & 0x7801) | ((a1 & 0x1e) << 6) | ((a1 & 0x7e0) >> 4); + return DRAM[a1]; + } + if (offset < 0x200000/2) + return m_rom[offset]; + else + { + printf("read out of bound\n"); + return 0xffff; + } +} + +WRITE16_MEMBER(md_rom_svp_device::write) +{ + if (offset >= 0x300000/2 && offset < 0x320000/2) + { + UINT32 a1 = offset - 0x300000/2; + UINT16 *DRAM = (UINT16 *)m_dram; + DRAM[a1] = data; + } +} + +READ16_MEMBER(md_rom_svp_device::read_a15) +{ + UINT32 d; + switch (offset) + { + // 0xa15000, 0xa15002 + case 0: + case 1: return m_xst; + // 0xa15004 + case 2: d = m_xst2; m_xst2 &= ~1; return d; + default: logerror("unhandled SVP reg read @ %x\n", offset << 1); + } + return 0; +} + +WRITE16_MEMBER(md_rom_svp_device::write_a15) +{ + switch (offset) + { + // 0xa15000, 0xa15002 + case 0: + case 1: m_xst = data; m_xst2 |= 2; break; + // 0xa15006 + case 3: break; // possibly halts SSP1601 + default: logerror("unhandled SVP reg write %04x @ %x\n", data, offset << 1); + } +} diff --git a/src/emu/bus/megadrive/svp.h b/src/emu/bus/megadrive/svp.h new file mode 100644 index 00000000000..3d107701603 --- /dev/null +++ b/src/emu/bus/megadrive/svp.h @@ -0,0 +1,73 @@ +#ifndef __MD_SVP_H +#define __MD_SVP_H + +#include "md_slot.h" +#include "cpu/ssp1601/ssp1601.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> md_rom_svp_device + +class md_rom_svp_device : public device_t, + public device_md_cart_interface +{ +public: + // construction/destruction + md_rom_svp_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + md_rom_svp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + +//protected: + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual ioport_constructor device_input_ports() const; + virtual void set_bank_to_rom(const char *banktag, UINT32 offset); + + required_device<device_t> m_svp; + required_ioport m_test_ipt; + + // reading and writing + virtual DECLARE_READ16_MEMBER(read); + virtual DECLARE_WRITE16_MEMBER(write); + virtual DECLARE_READ16_MEMBER(read_a15); + virtual DECLARE_WRITE16_MEMBER(write_a15); + + virtual int read_test(); + + virtual DECLARE_READ16_MEMBER(rom_read1); + virtual DECLARE_READ16_MEMBER(rom_read2); + + virtual DECLARE_READ16_MEMBER(read_pm0); + virtual DECLARE_READ16_MEMBER(read_pm1); + virtual DECLARE_READ16_MEMBER(read_pm2); + virtual DECLARE_READ16_MEMBER(read_pm4); + virtual DECLARE_READ16_MEMBER(read_xst); + virtual DECLARE_READ16_MEMBER(read_pmc); + virtual DECLARE_READ16_MEMBER(read_al); + virtual DECLARE_WRITE16_MEMBER(write_pm0); + virtual DECLARE_WRITE16_MEMBER(write_pm1); + virtual DECLARE_WRITE16_MEMBER(write_pm2); + virtual DECLARE_WRITE16_MEMBER(write_pm4); + virtual DECLARE_WRITE16_MEMBER(write_xst); + virtual DECLARE_WRITE16_MEMBER(write_pmc); + virtual DECLARE_WRITE16_MEMBER(write_al); + + UINT32 pm_io(int reg, int write, UINT32 d); + + UINT8 *m_iram; // IRAM (0-0x7ff) + UINT8 *m_dram; // [0x20000]; + UINT32 m_pmac_read[6]; // read modes/addrs for PM0-PM5 + UINT32 m_pmac_write[6]; // write ... + PAIR m_pmc; + UINT32 m_emu_status; + UINT16 m_xst; // external status, mapped at a15000 and a15002 on 68k side. + UINT16 m_xst2; // status of XST (bit1 set when 68k writes to XST) +}; + + +// device type definition +extern const device_type MD_ROM_SVP; + +#endif diff --git a/src/emu/bus/saturn/bram.c b/src/emu/bus/saturn/bram.c new file mode 100644 index 00000000000..9d11af18b9b --- /dev/null +++ b/src/emu/bus/saturn/bram.c @@ -0,0 +1,100 @@ +/*********************************************************************************************************** + + Saturn Battery RAM cart emulation + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "bram.h" + + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +const device_type SATURN_BRAM_4MB = &device_creator<saturn_bram4mb_device>; +const device_type SATURN_BRAM_8MB = &device_creator<saturn_bram8mb_device>; +const device_type SATURN_BRAM_16MB = &device_creator<saturn_bram16mb_device>; +const device_type SATURN_BRAM_32MB = &device_creator<saturn_bram32mb_device>; + + +saturn_bram_device::saturn_bram_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 size, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_sat_cart_interface( mconfig, *this ), + device_nvram_interface(mconfig, *this), + m_size(size) +{ +} + +saturn_bram4mb_device::saturn_bram4mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : saturn_bram_device(mconfig, SATURN_BRAM_4MB, "Saturn Battery RAM 4Mbit Cart", tag, owner, clock, 0x80000, "sat_bram_4mb", __FILE__) +{ + m_cart_type = 0x21; +} + +saturn_bram8mb_device::saturn_bram8mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : saturn_bram_device(mconfig, SATURN_BRAM_8MB, "Saturn Battery RAM 8Mbit Cart", tag, owner, clock, 0x100000, "sat_bram_8mb", __FILE__) +{ + m_cart_type = 0x22; +} + +saturn_bram16mb_device::saturn_bram16mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : saturn_bram_device(mconfig, SATURN_BRAM_16MB, "Saturn Battery RAM 16Mbit Cart", tag, owner, clock, 0x200000, "sat_bram_16mb", __FILE__) +{ + m_cart_type = 0x23; +} + +saturn_bram32mb_device::saturn_bram32mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : saturn_bram_device(mconfig, SATURN_BRAM_32MB, "Saturn Battery RAM 32Mbit Cart", tag, owner, clock, 0x400000, "sat_bram_32mb", __FILE__) +{ + m_cart_type = 0x24; +} + + +//------------------------------------------------- +// start/reset +//------------------------------------------------- + +void saturn_bram_device::device_start() +{ + // TODO: only allocate the real amount of RAM + m_ext_bram = auto_alloc_array_clear(machine(), UINT8, 0x400000); + m_ext_bram_size = 0x400000; + save_pointer(NAME(m_ext_bram), 0x400000); +} + +void saturn_bram_device::device_reset() +{ +} + + +/*------------------------------------------------- + IO handlers + -------------------------------------------------*/ + +// Battery RAM: single chip + +READ32_MEMBER(saturn_bram_device::read_ext_bram) +{ + if (offset < m_size/2) + return (m_ext_bram[offset * 2] << 16) | m_ext_bram[offset * 2 + 1]; + else + { + mame_printf_error("Battery RAM read beyond its boundary! offs: %X\n", offset); + return 0xffffffff; + } +} + +WRITE32_MEMBER(saturn_bram_device::write_ext_bram) +{ + if (offset < m_size/2) + { + if (ACCESSING_BITS_16_23) + m_ext_bram[offset * 2 + 0] = (data & 0x00ff0000) >> 16; + if (ACCESSING_BITS_0_7) + m_ext_bram[offset * 2 + 1] = (data & 0x000000ff) >> 0; + } + else + mame_printf_error("Battery RAM write beyond its boundary! offs: %X data: %X\n", offset, data); +} diff --git a/src/emu/bus/saturn/bram.h b/src/emu/bus/saturn/bram.h new file mode 100644 index 00000000000..861dd3db329 --- /dev/null +++ b/src/emu/bus/saturn/bram.h @@ -0,0 +1,69 @@ +#ifndef __SAT_BRAM_H +#define __SAT_BRAM_H + +#include "sat_slot.h" + + +// ======================> saturn_bram_device + +class saturn_bram_device : public device_t, + public device_sat_cart_interface, + public device_nvram_interface +{ +public: + // construction/destruction + saturn_bram_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 size, const char *shortname, const char *source); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // device_nvram_interface overrides + virtual void nvram_default() { } + virtual void nvram_read(emu_file &file) { if (m_ext_bram != NULL) { file.read(m_ext_bram, m_ext_bram_size); } } + virtual void nvram_write(emu_file &file) { if (m_ext_bram != NULL) { file.write(m_ext_bram, m_ext_bram_size); } } + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_ext_bram); + virtual DECLARE_WRITE32_MEMBER(write_ext_bram); + + UINT32 m_size; // this is the size of Battery RAM in bytes +}; + +class saturn_bram4mb_device : public saturn_bram_device +{ +public: + // construction/destruction + saturn_bram4mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class saturn_bram8mb_device : public saturn_bram_device +{ +public: + // construction/destruction + saturn_bram8mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class saturn_bram16mb_device : public saturn_bram_device +{ +public: + // construction/destruction + saturn_bram16mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class saturn_bram32mb_device : public saturn_bram_device +{ +public: + // construction/destruction + saturn_bram32mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + + + +// device type definition +extern const device_type SATURN_BRAM_4MB; +extern const device_type SATURN_BRAM_8MB; +extern const device_type SATURN_BRAM_16MB; +extern const device_type SATURN_BRAM_32MB; + +#endif diff --git a/src/emu/bus/saturn/dram.c b/src/emu/bus/saturn/dram.c new file mode 100644 index 00000000000..40af31cb29f --- /dev/null +++ b/src/emu/bus/saturn/dram.c @@ -0,0 +1,102 @@ +/*********************************************************************************************************** + + Saturn cart emulation + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "dram.h" + + +//------------------------------------------------- +// constructor +//------------------------------------------------- + +const device_type SATURN_DRAM_8MB = &device_creator<saturn_dram8mb_device>; +const device_type SATURN_DRAM_32MB = &device_creator<saturn_dram32mb_device>; + + +saturn_dram_device::saturn_dram_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 size, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_sat_cart_interface( mconfig, *this ), + m_size(size) +{ +} + +saturn_dram8mb_device::saturn_dram8mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : saturn_dram_device(mconfig, SATURN_DRAM_8MB, "Saturn Data RAM 8Mbit Cart", tag, owner, clock, 0x400000/4, "sat_dram_8mb", __FILE__) +{ + m_cart_type = 0x5a; +} + +saturn_dram32mb_device::saturn_dram32mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : saturn_dram_device(mconfig, SATURN_DRAM_32MB, "Saturn Data RAM 32Mbit Cart", tag, owner, clock, 0x800000/4, "sat_dram_32mb", __FILE__) +{ + m_cart_type = 0x5c; +} + + +//------------------------------------------------- +// mapper specific start/reset +//------------------------------------------------- + +void saturn_dram_device::device_start() +{ + // TODO: only allocate the real amount of RAM + m_ext_dram0 = auto_alloc_array_clear(machine(), UINT32, 0x400000/4); + m_ext_dram1 = auto_alloc_array_clear(machine(), UINT32, 0x400000/4); + m_ext_dram0_size = 0x400000; + m_ext_dram1_size = 0x400000; + save_pointer(NAME(m_ext_dram0), 0x400000/4); + save_pointer(NAME(m_ext_dram1), 0x400000/4); +} + +void saturn_dram_device::device_reset() +{ +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +// RAM: two DRAM chips are present in the cart + +READ32_MEMBER(saturn_dram_device::read_ext_dram0) +{ + if (offset < m_size/2) + return m_ext_dram0[offset]; + else + { + mame_printf_error("DRAM0 read beyond its boundary! offs: %X\n", offset); + return 0xffffffff; + } +} + +READ32_MEMBER(saturn_dram_device::read_ext_dram1) +{ + if (offset < m_size/2) + return m_ext_dram1[offset]; + else + { + mame_printf_error("DRAM1 read beyond its boundary! offs: %X\n", offset); + return 0xffffffff; + } +} + +WRITE32_MEMBER(saturn_dram_device::write_ext_dram0) +{ + if (offset < m_size/2) + COMBINE_DATA(&m_ext_dram0[offset]); + else + mame_printf_error("DRAM0 write beyond its boundary! offs: %X data: %X\n", offset, data); +} + +WRITE32_MEMBER(saturn_dram_device::write_ext_dram1) +{ + if (offset < m_size/2) + COMBINE_DATA(&m_ext_dram1[offset]); + else + mame_printf_error("DRAM1 write beyond its boundary! offs: %X data: %X\n", offset, data); +} diff --git a/src/emu/bus/saturn/dram.h b/src/emu/bus/saturn/dram.h new file mode 100644 index 00000000000..3a7e27a3e52 --- /dev/null +++ b/src/emu/bus/saturn/dram.h @@ -0,0 +1,49 @@ +#ifndef __SAT_DRAM_H +#define __SAT_DRAM_H + +#include "sat_slot.h" + + +// ======================> saturn_dram_device + +class saturn_dram_device : public device_t, + public device_sat_cart_interface +{ +public: + // construction/destruction + saturn_dram_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, UINT32 size, const char *shortname, const char *source); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_ext_dram0); + virtual DECLARE_READ32_MEMBER(read_ext_dram1); + virtual DECLARE_WRITE32_MEMBER(write_ext_dram0); + virtual DECLARE_WRITE32_MEMBER(write_ext_dram1); + + UINT32 m_size; // this is the size of DRAM0 + DRAM1 in dword units, so accesses to each bank go up to (m_size/2)-1 +}; + +class saturn_dram8mb_device : public saturn_dram_device +{ +public: + // construction/destruction + saturn_dram8mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + +class saturn_dram32mb_device : public saturn_dram_device +{ +public: + // construction/destruction + saturn_dram32mb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); +}; + + + +// device type definition +extern const device_type SATURN_DRAM_8MB; +extern const device_type SATURN_DRAM_32MB; + +#endif diff --git a/src/emu/bus/saturn/rom.c b/src/emu/bus/saturn/rom.c new file mode 100644 index 00000000000..c7fc84cbd81 --- /dev/null +++ b/src/emu/bus/saturn/rom.c @@ -0,0 +1,53 @@ +/*********************************************************************************************************** + + Saturn ROM cart emulation + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "rom.h" + + +//------------------------------------------------- +// saturn_rom_device - constructor +//------------------------------------------------- + +const device_type SATURN_ROM = &device_creator<saturn_rom_device>; + + +saturn_rom_device::saturn_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_sat_cart_interface( mconfig, *this ) +{ +} + +saturn_rom_device::saturn_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SATURN_ROM, "Saturn ROM Carts", tag, owner, clock, "sat_rom", __FILE__), + device_sat_cart_interface( mconfig, *this ) +{ + m_cart_type = 0xff; // actually not clear if ROM carts have a type ID like DRAM/BRAM carts +} + + +//------------------------------------------------- +// mapper specific start/reset +//------------------------------------------------- + +void saturn_rom_device::device_start() +{ +} + +void saturn_rom_device::device_reset() +{ +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +READ32_MEMBER(saturn_rom_device::read_rom) +{ + return m_rom[offset & (m_rom_size - 1)]; +} diff --git a/src/emu/bus/saturn/rom.h b/src/emu/bus/saturn/rom.h new file mode 100644 index 00000000000..3cb8dd1bf73 --- /dev/null +++ b/src/emu/bus/saturn/rom.h @@ -0,0 +1,31 @@ +#ifndef __SAT_ROM_H +#define __SAT_ROM_H + +#include "sat_slot.h" + + +// ======================> saturn_rom_device + +class saturn_rom_device : public device_t, + public device_sat_cart_interface +{ +public: + // construction/destruction + saturn_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + saturn_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_rom); +}; + + + +// device type definition +extern const device_type SATURN_ROM; + + +#endif diff --git a/src/emu/bus/saturn/sat_slot.c b/src/emu/bus/saturn/sat_slot.c new file mode 100644 index 00000000000..cc6d66786e0 --- /dev/null +++ b/src/emu/bus/saturn/sat_slot.c @@ -0,0 +1,260 @@ +/*********************************************************************************************************** + + + Saturn cart emulation + (through slot devices) + + Despite the system having a single cart slot, 3 different kinds of cart can be inserted and + different memory areas are exposed to each of them + * ROM carts are accessed in range 0x02000000-0x023fffff and 0x22000000-0x24ffffff of both CPUs + * Data RAM carts are accessed in range 0x02400000-0x027fffff of both CPUs (each DRAM chip is + mapped independently, the 1st at 0x2400000, the second at 0x2600000) + * Battery RAM carts are accessed in range 0x04000000-0x047fffff of both CPUs + + It is not clear what happens to accesses beyond the cart size (open bus? mirror of cart data?), + e.g. if you have a 16Mbit battery cart inserted and the system tries to read/write above 0x04400000, + so for the moment the whole range is mapped and an error message is printed for out-of-bounds + accesses + + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "sat_slot.h" + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type SATURN_CART_SLOT = &device_creator<sat_cart_slot_device>; + + +//------------------------------------------------- +// device_sat_cart_interface - constructor +//------------------------------------------------- + +device_sat_cart_interface::device_sat_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL), + m_ext_dram0(NULL), + m_ext_dram1(NULL), + m_ext_bram(NULL), + m_rom_size(0), + m_ext_dram0_size(0), + m_ext_dram1_size(0), + m_ext_bram_size(0) +{ +} + + +//------------------------------------------------- +// ~device_sat_cart_interface - destructor +//------------------------------------------------- + +device_sat_cart_interface::~device_sat_cart_interface() +{ +} + +//------------------------------------------------- +// rom_alloc - alloc the space for the cart +//------------------------------------------------- + +void device_sat_cart_interface::rom_alloc(running_machine &machine, UINT32 size) +{ + if (m_rom == NULL) + { + m_rom = auto_alloc_array_clear(machine, UINT32, size/4); + m_rom_size = size; + } +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sat_cart_slot_device - constructor +//------------------------------------------------- +sat_cart_slot_device::sat_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SATURN_CART_SLOT, "Saturn Cartridge Slot", tag, owner, clock, "sat_cart_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this) +{ +} + + +//------------------------------------------------- +// sat_cart_slot_device - destructor +//------------------------------------------------- + +sat_cart_slot_device::~sat_cart_slot_device() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sat_cart_slot_device::device_start() +{ + m_cart = dynamic_cast<device_sat_cart_interface *>(get_card_device()); +} + +//------------------------------------------------- +// device_config_complete - perform any +// operations now that the configuration is +// complete +//------------------------------------------------- + +void sat_cart_slot_device::device_config_complete() +{ + // set brief and instance name + update_names(); +} + + + +/*------------------------------------------------- + call load + -------------------------------------------------*/ + + +bool sat_cart_slot_device::call_load() +{ + if (m_cart) + { + UINT32 *ROM; + UINT32 len; + + if (software_entry() != NULL) + len = get_software_region_length("cart"); + else + len = length(); + + m_cart->rom_alloc(machine(), len); + ROM = m_cart->get_rom_base(); + + if (software_entry() != NULL) + memcpy(ROM, get_software_region("cart"), len); + else + fread(ROM, len); + + // fix endianness.... + for (int i = 0; i < len/4; i ++) + ROM[i] = BITSWAP32(ROM[i],7,6,5,4,3,2,1,0,15,14,13,12,11,10,9,8,23,22,21,20,19,18,17,16,31,30,29,28,27,26,25,24); +// UINT8 tempa = ROM[i+0]; +// UINT8 tempb = ROM[i+1]; +// ROM[i+1] = ROM[i+2]; +// ROM[i+0] = ROM[i+3]; +// ROM[i+3] = tempa; +// ROM[i+2] = tempb; +// } + return IMAGE_INIT_PASS; + } + + return IMAGE_INIT_PASS; +} + + +/*------------------------------------------------- + call_unload + -------------------------------------------------*/ + +void sat_cart_slot_device::call_unload() +{ +} + + + +/*------------------------------------------------- + call softlist load + -------------------------------------------------*/ + +bool sat_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 * sat_cart_slot_device::get_default_card_software(const machine_config &config, emu_options &options) +{ + return software_get_default_slot(config, options, this, "rom"); +} + + + +int sat_cart_slot_device::get_cart_type() +{ + if (m_cart) + return m_cart->get_cart_type(); + + return 0xff; +} + + + +/*------------------------------------------------- + read + -------------------------------------------------*/ + +READ32_MEMBER(sat_cart_slot_device::read_rom) +{ + if (m_cart) + return m_cart->read_rom(space, offset, mem_mask); + else + return 0xffffffff; +} + +READ32_MEMBER(sat_cart_slot_device::read_ext_dram0) +{ + if (m_cart) + return m_cart->read_ext_dram0(space, offset, mem_mask); + else + return 0xffffffff; +} + +READ32_MEMBER(sat_cart_slot_device::read_ext_dram1) +{ + if (m_cart) + return m_cart->read_ext_dram1(space, offset, mem_mask); + else + return 0xffffffff; +} + +READ32_MEMBER(sat_cart_slot_device::read_ext_bram) +{ + if (m_cart) + return m_cart->read_ext_bram(space, offset, mem_mask); + else + return 0xffffffff; +} + +/*------------------------------------------------- + write + -------------------------------------------------*/ + +WRITE32_MEMBER(sat_cart_slot_device::write_ext_dram0) +{ + if (m_cart) + m_cart->write_ext_dram0(space, offset, data, mem_mask); +} + +WRITE32_MEMBER(sat_cart_slot_device::write_ext_dram1) +{ + if (m_cart) + m_cart->write_ext_dram1(space, offset, data, mem_mask); +} + +WRITE32_MEMBER(sat_cart_slot_device::write_ext_bram) +{ + if (m_cart) + m_cart->write_ext_bram(space, offset, data, mem_mask); +} diff --git a/src/emu/bus/saturn/sat_slot.h b/src/emu/bus/saturn/sat_slot.h new file mode 100644 index 00000000000..d610494e65c --- /dev/null +++ b/src/emu/bus/saturn/sat_slot.h @@ -0,0 +1,119 @@ +#ifndef __SAT_SLOT_H +#define __SAT_SLOT_H + +/*************************************************************************** + TYPE DEFINITIONS + ***************************************************************************/ + + +// ======================> device_sat_cart_interface + +class device_sat_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_sat_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~device_sat_cart_interface(); + + // reading from ROM + virtual DECLARE_READ32_MEMBER(read_rom) { return 0xffffffff; } + // reading and writing to Extended DRAM chips + virtual DECLARE_READ32_MEMBER(read_ext_dram0) { return 0xffffffff; } + virtual DECLARE_WRITE32_MEMBER(write_ext_dram0) {} + virtual DECLARE_READ32_MEMBER(read_ext_dram1) { return 0xffffffff; } + virtual DECLARE_WRITE32_MEMBER(write_ext_dram1) {} + // reading and writing to Extended BRAM chip + virtual DECLARE_READ32_MEMBER(read_ext_bram) { return 0xffffffff; } + virtual DECLARE_WRITE32_MEMBER(write_ext_bram) {} + + virtual int get_cart_type() { return m_cart_type; }; + + + void rom_alloc(running_machine &machine, UINT32 size); + UINT32* get_rom_base() { return m_rom; } + UINT32* get_ext_dram0_base() { return m_ext_dram0; } + UINT32* get_ext_dram1_base() { return m_ext_dram1; } + UINT8* get_ext_bram_base() { return m_ext_bram; } + UINT32 get_rom_size() { return m_rom_size; } + UINT32 get_ext_dram0_size() { return m_ext_dram0_size; } + UINT32 get_ext_dram1_size() { return m_ext_dram1_size; } + UINT32 get_ext_bram_size() { return m_ext_bram_size; } + +protected: + int m_cart_type; + + // internal state + UINT32 *m_rom; + UINT32 *m_ext_dram0; + UINT32 *m_ext_dram1; + UINT8 *m_ext_bram; + UINT32 m_rom_size; + UINT32 m_ext_dram0_size; + UINT32 m_ext_dram1_size; + UINT32 m_ext_bram_size; +}; + + +// ======================> sat_cart_slot_device + +class sat_cart_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + sat_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~sat_cart_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete(); + + // image-level overrides + virtual bool call_load(); + virtual void call_unload(); + virtual bool call_softlist_load(char *swlist, char *swname, rom_entry *start_entry); + + int get_cart_type(); + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return 0; } + virtual bool is_reset_on_load() const { return 1; } + virtual const option_guide *create_option_guide() const { return NULL; } + virtual const char *image_interface() const { return "sat_cart"; } + virtual const char *file_extensions() const { return "bin"; } + + // slot interface overrides + virtual const char * get_default_card_software(const machine_config &config, emu_options &options); + + // reading and writing + virtual DECLARE_READ32_MEMBER(read_rom); + virtual DECLARE_READ32_MEMBER(read_ext_dram0); + virtual DECLARE_WRITE32_MEMBER(write_ext_dram0); + virtual DECLARE_READ32_MEMBER(read_ext_dram1); + virtual DECLARE_WRITE32_MEMBER(write_ext_dram1); + virtual DECLARE_READ32_MEMBER(read_ext_bram); + virtual DECLARE_WRITE32_MEMBER(write_ext_bram); + +private: + device_sat_cart_interface* m_cart; +}; + + +// device type definition +extern const device_type SATURN_CART_SLOT; + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS + ***************************************************************************/ + +#define MCFG_SATURN_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SATURN_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + + +#endif diff --git a/src/emu/bus/sega8/rom.c b/src/emu/bus/sega8/rom.c new file mode 100644 index 00000000000..4f0047555ac --- /dev/null +++ b/src/emu/bus/sega8/rom.c @@ -0,0 +1,941 @@ +/*********************************************************************************************************** + + Sega 8-bit cart emulation (for Master System, GameGear and SG-1000) + + TODO: implement proper ROM & RAM mirroring when the cart size is not a power of 2K (e.g. 24K or 48K) + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "rom.h" + + +//------------------------------------------------- +// constructors +//------------------------------------------------- + +// Base cart type shared across SG-1000, SG-1000 Mark II, SG-1000 Mark III, SMS, GG +// even if in sg1000 rom banks are never changed and ram is never enabled +const device_type SEGA8_ROM_STD = &device_creator<sega8_rom_device>; + +// Specific SG-1000 MkI - MkII cart types +const device_type SEGA8_ROM_CARDCATCH = &device_creator<sega8_cardcatch_device>; +const device_type SEGA8_ROM_OTHELLO = &device_creator<sega8_othello_device>; +const device_type SEGA8_ROM_CASTLE = &device_creator<sega8_castle_device>; +const device_type SEGA8_ROM_BASIC_L3 = &device_creator<sega8_basic_l3_device>; +const device_type SEGA8_ROM_MUSIC_EDITOR = &device_creator<sega8_music_editor_device>; +const device_type SEGA8_ROM_TEREBI = &device_creator<sega8_terebi_device>; +const device_type SEGA8_ROM_DAHJEE_TYPEA = &device_creator<sega8_dahjee_typea_device>; +const device_type SEGA8_ROM_DAHJEE_TYPEB = &device_creator<sega8_dahjee_typeb_device>; + +// Specific SG-1000 MkIII - SMS - GG cart types +const device_type SEGA8_ROM_EEPROM = &device_creator<sega8_eeprom_device>; +const device_type SEGA8_ROM_CODEMASTERS = &device_creator<sega8_codemasters_device>; +const device_type SEGA8_ROM_4PAK = &device_creator<sega8_4pak_device>; +const device_type SEGA8_ROM_ZEMINA = &device_creator<sega8_zemina_device>; +const device_type SEGA8_ROM_NEMESIS = &device_creator<sega8_nemesis_device>; +const device_type SEGA8_ROM_JANGGUN = &device_creator<sega8_janggun_device>; +const device_type SEGA8_ROM_KOREAN = &device_creator<sega8_korean_device>; +const device_type SEGA8_ROM_KOREAN_NB = &device_creator<sega8_korean_nb_device>; + + + +sega8_rom_device::sega8_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_sega8_cart_interface( mconfig, *this ) +{ +} + +sega8_rom_device::sega8_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SEGA8_ROM_STD, "Mark III, SMS & GG Carts", tag, owner, clock, "sega8_rom", __FILE__), + device_sega8_cart_interface( mconfig, *this ) +{ +} + + + + +sega8_cardcatch_device::sega8_cardcatch_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_CARDCATCH, "SG-1000 Card Catcher Cart", tag, owner, clock, "sega8_ccatch", __FILE__), + m_card(*this, "cardslot") +{ +} + + +sega8_othello_device::sega8_othello_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_OTHELLO, "SG-1000 Othello Cart", tag, owner, clock, "sega8_othello", __FILE__) +{ +} + + +sega8_castle_device::sega8_castle_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_CASTLE, "SG-1000 The Castle Cart", tag, owner, clock, "sega8_castle", __FILE__) +{ +} + + +sega8_basic_l3_device::sega8_basic_l3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_BASIC_L3, "SC-3000 BASIC Level III Cart", tag, owner, clock, "sega8_basicl3", __FILE__) +{ +} + + +sega8_music_editor_device::sega8_music_editor_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_MUSIC_EDITOR, "SC-3000 Music Editor Cart", tag, owner, clock, "sega8_music", __FILE__) +{ +} + + +sega8_terebi_device::sega8_terebi_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_TEREBI, "SG-1000 Terebi Oekaki Cart", tag, owner, clock, "sega8_terebi", __FILE__), + m_tvdraw_x(*this, "TVDRAW_X"), + m_tvdraw_y(*this, "TVDRAW_Y"), + m_tvdraw_pen(*this, "TVDRAW_PEN") +{ +} + + +sega8_dahjee_typea_device::sega8_dahjee_typea_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_DAHJEE_TYPEA, "SG-1000 Dahjee RAM expansion + Cart (Type A)", tag, owner, clock, "sega8_dahjeea", __FILE__) +{ +} + + +sega8_dahjee_typeb_device::sega8_dahjee_typeb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_DAHJEE_TYPEB, "SG-1000 Dahjee RAM expansion + Cart (Type B)", tag, owner, clock, "sega8_dahjeeb", __FILE__) +{ +} + + + + +sega8_eeprom_device::sega8_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SEGA8_ROM_EEPROM, "GG Carts + EEPROM", tag, owner, clock, "sega8_eeprom", __FILE__), + device_sega8_cart_interface( mconfig, *this ), + m_eeprom(*this, "eeprom") +{ +} + + +sega8_codemasters_device::sega8_codemasters_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SEGA8_ROM_CODEMASTERS, "Mark III, SMS & GG Codemasters Carts", tag, owner, clock, "sega8_codemasters", __FILE__), + device_sega8_cart_interface( mconfig, *this ) +{ +} + + +sega8_4pak_device::sega8_4pak_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_4PAK, "SMS 4-Pak Cart", tag, owner, clock, "sega8_4pak", __FILE__) +{ +} + + +sega8_zemina_device::sega8_zemina_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) + : device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_sega8_cart_interface( mconfig, *this ) +{ +} + +sega8_zemina_device::sega8_zemina_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SEGA8_ROM_ZEMINA, "SMS Zemina Carts", tag, owner, clock, "sega8_zemina", __FILE__), + device_sega8_cart_interface( mconfig, *this ) +{ +} + + +sega8_nemesis_device::sega8_nemesis_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_zemina_device(mconfig, SEGA8_ROM_NEMESIS, "SMS Nemesis Cart", tag, owner, clock, "sega8_nemesis", __FILE__) +{ +} + + +sega8_janggun_device::sega8_janggun_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : device_t(mconfig, SEGA8_ROM_JANGGUN, "SMS Janggun Cart", tag, owner, clock, "sega8_janggun", __FILE__), + device_sega8_cart_interface( mconfig, *this ) +{ +} + + +sega8_korean_device::sega8_korean_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_KOREAN, "SMS Korean Carts", tag, owner, clock, "sega8_korean", __FILE__) +{ +} + + +sega8_korean_nb_device::sega8_korean_nb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : sega8_rom_device(mconfig, SEGA8_ROM_KOREAN_NB, "SMS Korean No-Bank Mapper Carts", tag, owner, clock, "sega8_korean_nb", __FILE__) +{ +} + + +void sega8_rom_device::device_start() +{ + save_item(NAME(m_rom_bank_base)); + save_item(NAME(m_ram_base)); + save_item(NAME(m_ram_enabled)); +} + +void sega8_rom_device::device_reset() +{ + m_ram_base = 0; + m_ram_enabled = 0; +} + + + +void sega8_terebi_device::device_start() +{ + save_item(NAME(m_rom_bank_base)); + save_item(NAME(m_tvdraw_data)); +} + +void sega8_terebi_device::device_reset() +{ + m_tvdraw_data = 0; +} + + + + +void sega8_eeprom_device::device_start() +{ + save_item(NAME(m_rom_bank_base)); + save_item(NAME(m_93c46_enabled)); + save_item(NAME(m_93c46_lines)); +} + +void sega8_eeprom_device::device_reset() +{ + m_93c46_lines = 0; + m_93c46_enabled = 0; +} + + +void sega8_codemasters_device::device_start() +{ + save_item(NAME(m_rom_bank_base)); + save_item(NAME(m_ram_base)); + save_item(NAME(m_ram_enabled)); +} + +void sega8_codemasters_device::device_reset() +{ + m_ram_base = 0; + m_ram_enabled = 0; +} + +void sega8_4pak_device::device_start() +{ + save_item(NAME(m_rom_bank_base)); + save_item(NAME(m_reg)); +} + +void sega8_4pak_device::device_reset() +{ + memset(m_reg, 0, sizeof(m_reg)); +} + + +void sega8_zemina_device::device_start() +{ + save_item(NAME(m_rom_bank_base)); + save_item(NAME(m_ram_base)); + save_item(NAME(m_ram_enabled)); +} + +void sega8_zemina_device::device_reset() +{ + m_ram_base = 0; + m_ram_enabled = 0; +} + + +// initial bank setup needs to know how many 16K banks are available, so it needs to be called during cart loading... + +void sega8_rom_device::late_bank_setup() +{ + m_rom_bank_base[0] = 0; + m_rom_bank_base[1] = 1 % m_rom_page_count; + m_rom_bank_base[2] = 2 % m_rom_page_count; +} + +void sega8_eeprom_device::late_bank_setup() +{ + m_rom_bank_base[0] = 0; + m_rom_bank_base[1] = 1 % m_rom_page_count; + m_rom_bank_base[2] = 2 % m_rom_page_count; +} + +void sega8_codemasters_device::late_bank_setup() +{ + m_rom_bank_base[0] = 0; + m_rom_bank_base[1] = 1 % m_rom_page_count; + m_rom_bank_base[2] = 0; +} + +void sega8_zemina_device::late_bank_setup() +{ + m_rom_bank_base[0] = 0; + m_rom_bank_base[1] = 1 % (m_rom_page_count * 2); + m_rom_bank_base[2] = 2 % (m_rom_page_count * 2); + m_rom_bank_base[3] = 3 % (m_rom_page_count * 2); + m_rom_bank_base[4] = 4 % (m_rom_page_count * 2); + m_rom_bank_base[5] = 5 % (m_rom_page_count * 2); +} + +void sega8_nemesis_device::late_bank_setup() +{ + // Nemesis starts with last 8kb bank in page 0 (m_rom_page_count is for 16kb pages) + m_rom_bank_base[0] = m_rom_page_count * 2 - 1; + m_rom_bank_base[1] = 1 % (m_rom_page_count * 2); + m_rom_bank_base[2] = 2 % (m_rom_page_count * 2); + m_rom_bank_base[3] = 3 % (m_rom_page_count * 2); + m_rom_bank_base[4] = 4 % (m_rom_page_count * 2); + m_rom_bank_base[5] = 5 % (m_rom_page_count * 2); +} + +void sega8_janggun_device::late_bank_setup() +{ + m_rom_bank_base[0] = 0; + m_rom_bank_base[1] = 1; + m_rom_bank_base[2] = 2; + m_rom_bank_base[3] = 3; + m_rom_bank_base[4] = 4; + m_rom_bank_base[5] = 5; +} + +void sega8_korean_device::late_bank_setup() +{ + m_rom_bank_base[0] = 0; + m_rom_bank_base[1] = 1; + m_rom_bank_base[2] = 2; +} + + +/*------------------------------------------------- + mapper specific handlers + -------------------------------------------------*/ + +/*------------------------------------------------- + + Base Sega 8bit carts, possibly with bankswitch + (only used by Mark III, SMS and GG games) + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_rom_device::read_cart) +{ + int bank = offset / 0x4000; + + if (bank == 2 && m_ram && m_ram_enabled) + return m_ram[(m_ram_base * 0x4000 + (offset & 0x3fff)) % m_ram_size]; + + if (offset < 0x400) // first 1k is hardcoded + return m_rom[offset]; + + return m_rom[m_rom_bank_base[bank] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(sega8_rom_device::write_cart) +{ + int bank = offset / 0x4000; + + if (bank == 2 && m_ram && m_ram_enabled) + m_ram[(m_ram_base * 0x4000 + (offset & 0x3fff)) % m_ram_size] = data; +} + +WRITE8_MEMBER(sega8_rom_device::write_mapper) +{ + switch (offset) + { + case 0: + if (data & 0x08) + { + // hack: if we were loading from fullpath (hence allowing m_late_battery_enable to be true) + // and the RAM is ever enabled by the game, then we save it at exit... + if (m_late_battery_enable) + has_battery = 1; + m_ram_enabled = 1; + m_ram_base = (data & 0x04) >> 2; + } + else + m_ram_enabled = 0; + break; + + case 1: // Select 16k ROM bank for 0000-3fff + case 2: // Select 16k ROM bank for 4000-7fff + case 3: // Select 16k ROM bank for 8000-bfff + m_rom_bank_base[offset - 1] = data % m_rom_page_count; + break; + } +} + + +/*------------------------------------------------- + + Sega Card Catcher is a passthrough adapter for + SG-1000 to load games in MyCard format into the + main cartslot + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_cardcatch_device::read_cart) +{ + if (offset < 0x8000) + return m_card->read_cart(space, offset); + + return 0xff; +} + +WRITE8_MEMBER(sega8_cardcatch_device::write_cart) +{ + // this should never happen, because there is no RAM on cards + if (offset < 0x8000) + logerror("Attempt to write to MyCard\n"); +} + +static SLOT_INTERFACE_START(sg1000_card) + SLOT_INTERFACE_INTERNAL("rom", SEGA8_ROM_STD) +SLOT_INTERFACE_END + +static MACHINE_CONFIG_FRAGMENT( sub_slot ) + MCFG_SG1000_CARD_ADD("cardslot", sg1000_card, NULL) +MACHINE_CONFIG_END + +machine_config_constructor sega8_cardcatch_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( sub_slot ); +} + +/*------------------------------------------------- + + Othello is a SG-1000 game featuring 2K of + oncart RAM, mapped at 0x8000-0x9fff. + Is RAM mirrored? For now we assume so... + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_othello_device::read_cart) +{ + // 8K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000 && offset < 0xa000) + return m_ram[offset & 0x7ff]; + + return m_rom[offset % m_rom_size]; +} + +WRITE8_MEMBER(sega8_othello_device::write_cart) +{ + // 2K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000 && offset < 0xa000) + m_ram[offset & 0x7ff] = data; +} + + +/*------------------------------------------------- + + The Castle is a SG-1000 game featuring 8K of + oncart RAM, mapped at 0x8000-0x9fff + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_castle_device::read_cart) +{ + // 8K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000 && offset < 0xa000) + return m_ram[offset & 0x1fff]; + + return m_rom[offset % m_rom_size]; +} + +WRITE8_MEMBER(sega8_castle_device::write_cart) +{ + // 8K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000 && offset < 0xa000) + m_ram[offset & 0x1fff] = data; +} + + +/*------------------------------------------------- + + BASIC Level III cart featured 32K of + oncart RAM, mapped at 0x8000-0xffff? + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_basic_l3_device::read_cart) +{ + // 8K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000) + return m_ram[offset & 0x3fff]; + + return m_rom[offset % m_rom_size]; +} + +WRITE8_MEMBER(sega8_basic_l3_device::write_cart) +{ + // 8K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000) + m_ram[offset & 0x3fff] = data; +} + +READ8_MEMBER(sega8_basic_l3_device::read_ram) +{ + return m_ram[0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(sega8_basic_l3_device::write_ram) +{ + m_ram[0x4000 + (offset & 0x3fff)] = data; +} + + +/*------------------------------------------------- + + Music Editor cart featured 10K of oncart RAM, mapped + in 0x8000-0x9fff and 0xc000-0xffff + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_music_editor_device::read_cart) +{ + // 8K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000 && offset < 0xa000) + return m_ram[offset & 0x1fff]; + + return m_rom[offset % m_rom_size]; +} + +WRITE8_MEMBER(sega8_music_editor_device::write_cart) +{ + // 8K of RAM sits in 0x8000-0x9fff + if (offset >= 0x8000 && offset < 0xa000) + m_ram[offset & 0x1fff] = data; +} + +READ8_MEMBER(sega8_music_editor_device::read_ram) +{ + // 2K more of RAM sits in 0xc000-0xc3ff (and mirrored up to 0xffff) + // or should it simply go to the 2K of SC3000 RAM??? + return m_ram[0x2000 + (offset & 0x7ff)]; +} + +WRITE8_MEMBER(sega8_music_editor_device::write_ram) +{ + // 2K more of RAM sits in 0xc000-0xc3ff (and mirrored up to 0xffff) + // or should it simply go to the 2K of SC3000 RAM??? + m_ram[0x2000 + (offset & 0x7ff)] = data; +} + + +/*------------------------------------------------- + + SG-1000 Terebi Oekaki using a Tablet input device + + -------------------------------------------------*/ + +/* + + Terebi Oekaki (TV Draw) + + Address Access Bits + 7 6 5 4 3 2 1 0 + $6000 W - - - - - - - AXIS + $8000 R BUSY - - - - - - PRESS + $A000 R/W DATA + + AXIS: write 0 to select X axis, 1 to select Y axis. + BUSY: reads 1 when graphic board is busy sampling position, else 0. + PRESS: reads 0 when pen is touching graphic board, else 1. + DATA: when pen is touching graphic board, return 8-bit sample position for currently selected axis (X is in the 0-255 range, Y in the 0-191 range). Else, return 0. + + */ + + +READ8_MEMBER(sega8_terebi_device::read_cart) +{ + int bank = offset / 0x4000; + + if (offset == 0x8000) + return m_tvdraw_pen->read(); + if (offset == 0xa000) + return m_tvdraw_data; + + return m_rom[m_rom_bank_base[bank] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(sega8_terebi_device::write_cart) +{ + switch (offset) + { + case 0x6000: + if (data & 0x01) + { + m_tvdraw_data = m_tvdraw_x->read(); + + if (m_tvdraw_data < 4) m_tvdraw_data = 4; + if (m_tvdraw_data > 251) m_tvdraw_data = 251; + } + else + m_tvdraw_data = m_tvdraw_y->read() + 0x20; + break; + case 0xa000: + // effect unknown + break; + } +} + +static INPUT_PORTS_START( tvdraw ) + PORT_START("TVDRAW_X") + PORT_BIT( 0xff, 0x80, IPT_LIGHTGUN_X ) PORT_NAME("Tablet - X Axis") PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(1) + + PORT_START("TVDRAW_Y") + PORT_BIT( 0xff, 0x60, IPT_LIGHTGUN_Y ) PORT_NAME("Tablet - Y Axis") PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_MINMAX(0, 191) PORT_SENSITIVITY(50) PORT_KEYDELTA(10) PORT_PLAYER(1) + + PORT_START("TVDRAW_PEN") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON3 ) PORT_NAME("Tablet - Pen") +INPUT_PORTS_END + +ioport_constructor sega8_terebi_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( tvdraw ); +} + + +/*------------------------------------------------- + + Dahjee carts were sold with a RAM expansion pass-through + cart (which we don't emulate separately for the + moment) which allowed to play on old SG1000 machines + some MSX conversions requiring more RAM than available + + Two kind of expansion existed (for different games), + one with 9K of RAM (Type A) and one with 8K of + RAM (Type B). + + -------------------------------------------------*/ + +// TYPE A +READ8_MEMBER(sega8_dahjee_typea_device::read_cart) +{ + // 8K of RAM sits in 0x2000-0x3fff + if (offset >= 0x2000 && offset < 0x4000) + return m_ram[offset & 0x1fff]; + + return m_rom[offset % m_rom_size]; +} + +WRITE8_MEMBER(sega8_dahjee_typea_device::write_cart) +{ + // 8K of RAM sits in 0x2000-0x3fff + if (offset >= 0x2000 && offset < 0x4000) + m_ram[offset & 0x1fff] = data; +} + +READ8_MEMBER(sega8_dahjee_typea_device::read_ram) +{ + // 1K more of RAM sits in 0xc000-0xc3ff (and mirrored up to 0xffff + // or should it simply go to the 1K of SG1000 RAM??? + return m_ram[0x2000 + (offset & 0x3ff)]; +} + +WRITE8_MEMBER(sega8_dahjee_typea_device::write_ram) +{ + // 1K more of RAM sits in 0xc000-0xc3ff (and mirrored up to 0xffff + // or should it simply go to the 1K of SG1000 RAM??? + m_ram[0x2000 + (offset & 0x3ff)] = data; +} + + +// TYPE B +READ8_MEMBER(sega8_dahjee_typeb_device::read_cart) +{ + return m_rom[offset % m_rom_size]; +} + +READ8_MEMBER(sega8_dahjee_typeb_device::read_ram) +{ + // 8K more of RAM sits in 0xc000-0xffff + return m_ram[offset & 0x1fff]; +} + +WRITE8_MEMBER(sega8_dahjee_typeb_device::write_ram) +{ + // 8K more of RAM sits in 0xc000-0xffff + m_ram[offset & 0x1fff] = data; +} + + + + +/*------------------------------------------------- + + Sega carts + EEPROM, used for some GameGear baseball + games + + -------------------------------------------------*/ + + +READ8_MEMBER(sega8_eeprom_device::read_cart) +{ + int bank = offset / 0x4000; + + if (offset == 0x8000 && m_93c46_enabled) + { + UINT8 value = (m_93c46_lines & 0xfc) | 0x02; + value |= m_eeprom->do_read() ? 1 : 0; + return value; + } + + if (offset < 0x400) // first 1k is hardcoded + return m_rom[offset]; + + return m_rom[m_rom_bank_base[bank] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(sega8_eeprom_device::write_cart) +{ + if (offset == 0x8000 && m_93c46_enabled) + { + m_93c46_lines = data; + m_eeprom->di_write((data & 0x01) ? ASSERT_LINE : CLEAR_LINE); + m_eeprom->cs_write((data & 0x04) ? ASSERT_LINE : CLEAR_LINE); + m_eeprom->clk_write((data & 0x02) ? ASSERT_LINE : CLEAR_LINE); + } +} + +WRITE8_MEMBER(sega8_eeprom_device::write_mapper) +{ + switch (offset) + { + case 0: + if (data & 0x80) + { + m_eeprom->reset(); + logerror("eeprom CS = 1\n"); + m_eeprom->cs_write(CLEAR_LINE); + } + m_93c46_enabled = BIT(data, 3); + logerror("eeprom %s\n", m_93c46_enabled ? "enabled" : "disabled"); + break; + + case 1: // Select 16k ROM bank for 0000-3fff + case 2: // Select 16k ROM bank for 4000-7fff + case 3: // Select 16k ROM bank for 8000-bfff + m_rom_bank_base[offset - 1] = data % m_rom_page_count; + break; + } +} + +MACHINE_CONFIG_FRAGMENT( gg_eeprom ) + MCFG_EEPROM_SERIAL_93C46_ADD("eeprom") +MACHINE_CONFIG_END + +machine_config_constructor sega8_eeprom_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( gg_eeprom ); +} + + +/*------------------------------------------------- + + Codemasters carts, possibly having on cart RAM + (Ernie Els Golf) + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_codemasters_device::read_cart) +{ + int bank = offset / 0x2000; + + if (bank == 5 && m_ram && m_ram_enabled) + return m_ram[(m_ram_base * 0x2000 + (offset & 0x1fff)) % m_ram_size]; + + return m_rom[m_rom_bank_base[bank/2] * 0x4000 + (offset & 0x3fff)]; +} + +WRITE8_MEMBER(sega8_codemasters_device::write_cart) +{ + int bank = offset / 0x2000; + + switch (offset) + { + case 0x0000: + m_rom_bank_base[0] = data % m_rom_page_count; + break; + case 0x4000: + if (data & 0x80) + { + m_ram_enabled = 1; + m_ram_base = data & 0x07; + } + else + { + m_ram_enabled = 0; + m_rom_bank_base[1] = data % m_rom_page_count; + } + break; + case 0x8000: + m_rom_bank_base[2] = data % m_rom_page_count; + break; + } + + if (bank == 5 && m_ram && m_ram_enabled) + m_ram[(m_ram_base * 0x2000 + (offset & 0x1fff)) % m_ram_size] = data; +} + +/*------------------------------------------------- + + HES 4 PAK All Action cart + + -------------------------------------------------*/ + + +WRITE8_MEMBER(sega8_4pak_device::write_cart) +{ + switch (offset) + { + case 0x3ffe: + m_reg[0] = data; + m_rom_bank_base[0] = data % m_rom_page_count; + m_rom_bank_base[2] = ((m_reg[0] & 0x30) + m_reg[2]) % m_rom_page_count; + break; + case 0x7fff: + m_reg[1] = data; + m_rom_bank_base[1] = data % m_rom_page_count; + break; + case 0xbfff: + m_reg[2] = data; + m_rom_bank_base[2] = ((m_reg[0] & 0x30) + m_reg[2]) % m_rom_page_count; + break; + } +} + + +/*------------------------------------------------- + + base Zemina carts, allowing for 8K bankswitch + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_zemina_device::read_cart) +{ + int bank = offset / 0x2000; + + if (bank >= 4 && m_ram && m_ram_enabled) + return m_ram[(m_ram_base * 0x2000 + (offset & 0x1fff)) % m_ram_size]; + + return m_rom[m_rom_bank_base[bank] * 0x2000 + (offset & 0x1fff)]; +} + +WRITE8_MEMBER(sega8_zemina_device::write_cart) +{ + int bank = offset / 0x2000; + + if (bank >= 4 && m_ram && m_ram_enabled) + m_ram[(m_ram_base * 0x2000 + (offset & 0x1fff)) % m_ram_size] = data; + + if (offset < 4) + { + switch (offset & 3) + { + case 0: + m_rom_bank_base[4] = data % (m_rom_page_count * 2); + break; + case 1: + m_rom_bank_base[5] = data % (m_rom_page_count * 2); + break; + case 2: + m_rom_bank_base[2] = data % (m_rom_page_count * 2); + break; + case 3: + m_rom_bank_base[3] = data % (m_rom_page_count * 2); + break; + } + } +} + +/*------------------------------------------------- + + Zemina cart used for Nemesis: same as above, but + different bank layout at start (see late_bank_setup) + + -------------------------------------------------*/ + +/*------------------------------------------------- + + Daiou cart used for SMS Janggun-ui Adeul + + the game expects to access 256 x 8K banks: + first 64 are just the game, second 64 are a mirror of the first ones + upper 128 are the same as the previous but with bytes in reverse order + probably as a shortcut to get sprite flipping for free from hw + so if bit7 of current bank page is set, we swap the result + + -------------------------------------------------*/ + +READ8_MEMBER(sega8_janggun_device::read_cart) +{ + int bank = offset / 0x2000; + + if (m_rom_bank_base[bank] < 0x80) + return m_rom[(m_rom_bank_base[bank] & 0x3f) * 0x2000 + (offset & 0x1fff)]; + else + return BITSWAP8(m_rom[(m_rom_bank_base[bank] & 0x3f) * 0x2000 + (offset & 0x1fff)], 0, 1, 2, 3, 4, 5, 6, 7); +} + + +WRITE8_MEMBER(sega8_janggun_device::write_cart) +{ + switch (offset) + { + case 0x4000: + m_rom_bank_base[2] = data; + break; + case 0x6000: + m_rom_bank_base[3] = data; + break; + case 0x8000: + m_rom_bank_base[4] = data; + break; + case 0xa000: + m_rom_bank_base[5] = data; + break; + } +} + +WRITE8_MEMBER(sega8_janggun_device::write_mapper) +{ + switch (offset) + { + case 0: + break; + + case 1: // Select 16k ROM bank for 0000-3fff + case 2: // Select 16k ROM bank for 4000-7fff + case 3: // Select 16k ROM bank for 8000-bfff + m_rom_bank_base[(offset - 1) * 2] = (data % m_rom_page_count) * 2; + m_rom_bank_base[(offset - 1) * 2 + 1] = (data % m_rom_page_count) * 2 + 1; + break; + } +} + + +/*------------------------------------------------- + + Korean cart, used e.g. in Dodgeball King, + uses writes to 0xa000 for bankswitch + + -------------------------------------------------*/ + +WRITE8_MEMBER(sega8_korean_device::write_cart) +{ + int bank = offset / 0x4000; + + if (bank == 2 && m_ram && m_ram_enabled) + m_ram[m_ram_base * 0x4000 + (offset & 0x3fff)] = data; + + if (offset == 0xa000) + m_rom_bank_base[2] = data % m_rom_page_count; +} diff --git a/src/emu/bus/sega8/rom.h b/src/emu/bus/sega8/rom.h new file mode 100644 index 00000000000..efc49547e88 --- /dev/null +++ b/src/emu/bus/sega8/rom.h @@ -0,0 +1,382 @@ +#ifndef __SEGA8_ROM_H +#define __SEGA8_ROM_H + +#include "sega8_slot.h" +#include "machine/eepromser.h" + +// ======================> sega8_rom_device + +class sega8_rom_device : public device_t, + public device_sega8_cart_interface +{ +public: + // construction/destruction + sega8_rom_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + sega8_rom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + virtual void late_bank_setup(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper); + +protected: + UINT8 m_rom_bank_base[3]; + UINT8 m_ram_base; + int m_ram_enabled; +}; + + + + +// ======================> sega8_cardcatch_device + +class sega8_cardcatch_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_cardcatch_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} + + virtual machine_config_constructor device_mconfig_additions() const; + +protected: + required_device<sega8_card_slot_device> m_card; +}; + + +// ======================> sega8_othello_device + +class sega8_othello_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_othello_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} +}; + + +// ======================> sega8_castle_device + +class sega8_castle_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_castle_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} +}; + + +// ======================> sega8_basic_l3_device + +class sega8_basic_l3_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_basic_l3_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} + + // has internal RAM which overwrites the system one! + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + + +// ======================> sega8_music_editor_device + +class sega8_music_editor_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_music_editor_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} + + // has internal RAM which overwrites the system one! + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + + +// ======================> sega8_terebi_device + +class sega8_terebi_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_terebi_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual ioport_constructor device_input_ports() const; + virtual void device_reset(); + + required_ioport m_tvdraw_x; + required_ioport m_tvdraw_y; + required_ioport m_tvdraw_pen; + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} + +protected: + UINT8 m_tvdraw_data; +}; + + +// ======================> sega8_dahjee_typea_device + +class sega8_dahjee_typea_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_dahjee_typea_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} + + // has internal RAM which overwrites the system one! + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + + +// ======================> sega8_dahjee_typeb_device + +class sega8_dahjee_typeb_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_dahjee_typeb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart) {} + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} + + // has internal RAM which overwrites the system one! + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); +}; + + + + +// ======================> sega8_eeprom_device + +class sega8_eeprom_device : public device_t, + public device_sega8_cart_interface +{ +public: + // construction/destruction + sega8_eeprom_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + virtual void device_reset(); + + virtual void late_bank_setup(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper); + +protected: + UINT8 m_rom_bank_base[3]; + + required_device<eeprom_serial_93cxx_device> m_eeprom; + int m_93c46_enabled; + UINT8 m_93c46_lines; +}; + + +// ======================> sega8_codemasters_device + +class sega8_codemasters_device : public device_t, + public device_sega8_cart_interface +{ +public: + // construction/destruction + sega8_codemasters_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + virtual void late_bank_setup(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + // no mapper write for this! + +protected: + UINT8 m_rom_bank_base[3]; + UINT8 m_ram_base; + int m_ram_enabled; +}; + + +// ======================> sega8_4pak_device + +class sega8_4pak_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_4pak_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // reading and writing + virtual DECLARE_WRITE8_MEMBER(write_cart); + +private: + UINT8 m_reg[3]; +}; + + +// ======================> sega8_zemina_device + +class sega8_zemina_device : public device_t, + public device_sega8_cart_interface +{ +public: + // construction/destruction + sega8_zemina_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); + sega8_zemina_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + virtual void late_bank_setup(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + // no mapper write for this! + +protected: + UINT8 m_rom_bank_base[6]; + UINT8 m_ram_base; + int m_ram_enabled; +}; + + +// ======================> sega8_nemesis_device + +class sega8_nemesis_device : public sega8_zemina_device +{ +public: + // construction/destruction + sega8_nemesis_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void late_bank_setup(); +}; + + +// ======================> sega8_janggun_device + +class sega8_janggun_device : public device_t, + public device_sega8_cart_interface +{ +public: + // construction/destruction + sega8_janggun_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device-level overrides + virtual void device_start() { save_item(NAME(m_rom_bank_base)); } + + virtual void late_bank_setup(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper); + +protected: + UINT8 m_rom_bank_base[6]; +}; + + +// ======================> sega8_korean_device + +class sega8_korean_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_korean_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void late_bank_setup(); + + // reading and writing + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} +}; + + +// ======================> sega8_korean_nb_device + +class sega8_korean_nb_device : public sega8_rom_device +{ +public: + // construction/destruction + sega8_korean_nb_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // reading and writing + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} +}; + + + +// device type definition +extern const device_type SEGA8_ROM_STD; +extern const device_type SEGA8_ROM_CARDCATCH; +extern const device_type SEGA8_ROM_OTHELLO; +extern const device_type SEGA8_ROM_CASTLE; +extern const device_type SEGA8_ROM_BASIC_L3; +extern const device_type SEGA8_ROM_MUSIC_EDITOR; +extern const device_type SEGA8_ROM_DAHJEE_TYPEA; +extern const device_type SEGA8_ROM_DAHJEE_TYPEB; +extern const device_type SEGA8_ROM_EEPROM; +extern const device_type SEGA8_ROM_TEREBI; +extern const device_type SEGA8_ROM_CODEMASTERS; +extern const device_type SEGA8_ROM_4PAK; +extern const device_type SEGA8_ROM_ZEMINA; +extern const device_type SEGA8_ROM_NEMESIS; +extern const device_type SEGA8_ROM_JANGGUN; +extern const device_type SEGA8_ROM_KOREAN; +extern const device_type SEGA8_ROM_KOREAN_NB; + +#endif diff --git a/src/emu/bus/sega8/sega8_slot.c b/src/emu/bus/sega8/sega8_slot.c new file mode 100644 index 00000000000..c8ebf5297cb --- /dev/null +++ b/src/emu/bus/sega8/sega8_slot.c @@ -0,0 +1,823 @@ +/*********************************************************************************************************** + + + Sega 8-bit cart emulation + (through slot devices) + + Master System (Mark III) and Game Gear memory map can access 3 x 16K banks of ROM in + 0x0000-0xbfff memory range. These banks can however point to different ROM or RAM area + of the cart (or to BIOS banks, but these are handled directly in SMS emulation). + + Hence, carts can interface with the main system through the following handlers + * read_cart : to read from ROM/RAM in memory range [0000-bfff] + * write_cart : to write to ROM/RAM in memory range [0000-bfff] + * write_mapper : to write to range [fffc-ffff] (called by the handler accessing those + same addresses in sms.c) + + Note about Sega Card / MyCard: the data contained in these matches the data in carts, it's only + the connector to be different. We emulate this with a variant of the slot having different media + switch and different interface (the latter not implemented yet) + + TODO: + - investigate SG-1000 carts so to reduce duplicated code and to add full .sg support to sg1000m3 + + ***********************************************************************************************************/ + + +#include "emu.h" +#include "sega8_slot.h" + +#define VERBOSE 0 +#define LOG(x) do { if (VERBOSE) logerror x; } while (0) + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type SEGA8_CART_SLOT = &device_creator<sega8_cart_slot_device>; +const device_type SEGA8_CARD_SLOT = &device_creator<sega8_card_slot_device>; + + +//************************************************************************** +// SMS cartridges Interface +//************************************************************************** + +//------------------------------------------------- +// device_sega8_cart_interface - constructor +//------------------------------------------------- + +device_sega8_cart_interface::device_sega8_cart_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig, device), + m_rom(NULL), + m_ram(NULL), + m_rom_size(0), + m_ram_size(0), + m_rom_page_count(0), + has_battery(FALSE), + m_late_battery_enable(FALSE), + m_lphaser_xoffs(0), + m_sms_mode(0) +{ +} + + +//------------------------------------------------- +// ~device_sega8_cart_interface - destructor +//------------------------------------------------- + +device_sega8_cart_interface::~device_sega8_cart_interface() +{ +} + +//------------------------------------------------- +// rom_alloc - alloc the space for the cart +//------------------------------------------------- + +void device_sega8_cart_interface::rom_alloc(running_machine &machine, UINT32 size) +{ + if (m_rom == NULL) + { + m_rom = auto_alloc_array_clear(machine, UINT8, size); + m_rom_size = size; + m_rom_page_count = size / 0x4000; + if (!m_rom_page_count) + m_rom_page_count = 1; // we compute rom pages through (XXX % m_rom_page_count)! + late_bank_setup(); + } +} + + +//------------------------------------------------- +// ram_alloc - alloc the space for the ram +//------------------------------------------------- + +void device_sega8_cart_interface::ram_alloc(running_machine &machine, UINT32 size) +{ + if (m_ram == NULL) + { + m_ram = auto_alloc_array_clear(machine, UINT8, size); + m_ram_size = size; + state_save_register_item_pointer(machine, "SEGA8_CART", this->device().tag(), 0, m_ram, m_ram_size); + } +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sega8_cart_slot_device - constructor +//------------------------------------------------- + +sega8_cart_slot_device::sega8_cart_slot_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, bool is_card, const char *shortname, const char *source) : + device_t(mconfig, type, name, tag, owner, clock, shortname, source), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this), + m_type(SEGA8_BASE_ROM), + m_must_be_loaded(FALSE), + m_interface("sms_cart"), + m_extensions("bin") +{ + m_is_card = is_card; +} + +sega8_cart_slot_device::sega8_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SEGA8_CART_SLOT, "Sega Master System / Game Gear / SG1000 Cartridge Slot", tag, owner, clock, "sega8_cart_slot", __FILE__), + device_image_interface(mconfig, *this), + device_slot_interface(mconfig, *this), + m_type(SEGA8_BASE_ROM), + m_must_be_loaded(FALSE), + m_is_card(FALSE), + m_interface("sms_cart"), + m_extensions("bin") +{ +} + +sega8_card_slot_device::sega8_card_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + sega8_cart_slot_device(mconfig, SEGA8_CARD_SLOT, "Sega Master System / Game Gear / SG1000 Card Slot", tag, owner, clock, TRUE, "sega8_card_slot", __FILE__) +{ +} + + +//------------------------------------------------- +// sega8_cart_slot_device - destructor +//------------------------------------------------- + +sega8_cart_slot_device::~sega8_cart_slot_device() +{ +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sega8_cart_slot_device::device_start() +{ + m_cart = dynamic_cast<device_sega8_cart_interface *>(get_card_device()); +} + +//------------------------------------------------- +// SMS PCB +//------------------------------------------------- + + +struct sega8_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 sega8_slot slot_list[] = +{ + { SEGA8_BASE_ROM, "rom" }, + { SEGA8_EEPROM, "eeprom" }, + { SEGA8_TEREBIOEKAKI, "terebi" }, + { SEGA8_4PAK, "4pak" }, + { SEGA8_CODEMASTERS, "codemasters" }, + { SEGA8_ZEMINA, "zemina" }, + { SEGA8_NEMESIS, "nemesis" }, + { SEGA8_JANGGUN, "janggun" }, + { SEGA8_KOREAN, "korean" }, + { SEGA8_KOREAN_NOBANK, "korean_nb" }, + { SEGA8_OTHELLO, "othello" }, + { SEGA8_CASTLE, "castle" }, + { SEGA8_BASIC_L3, "level3" }, + { SEGA8_MUSIC_EDITOR, "music_editor" }, + { SEGA8_DAHJEE_TYPEA, "dahjee_typea" }, + { SEGA8_DAHJEE_TYPEB, "dahjee_typeb" } +}; + +static int sega8_get_pcb_id(const char *slot) +{ + for (int i = 0; i < ARRAY_LENGTH(slot_list); i++) + { + if (!mame_stricmp(slot_list[i].slot_option, slot)) + return slot_list[i].pcb_id; + } + + return 0; +} + +static const char *sega8_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 "rom"; +} + + +/*------------------------------------------------- + call load + -------------------------------------------------*/ + +int sega8_cart_slot_device::verify_cart( UINT8 *magic, int size ) +{ + int retval = IMAGE_VERIFY_FAIL; + + // Verify the file is a valid image - check $7ff0 for "TMR SEGA" + if (size >= 0x8000) + { + if (!strncmp((char*)&magic[0x7ff0], "TMR SEGA", 8)) + retval = IMAGE_VERIFY_PASS; + } + + return retval; +} + +void sega8_cart_slot_device::set_lphaser_xoffset( UINT8 *rom, int size ) +{ + static const UINT8 signatures[7][16] = + { + /* Spacegun */ + { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0xff, 0xff, 0x9d, 0x99, 0x10, 0x90, 0x00, 0x40 }, + /* Gangster Town */ + { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x19, 0x87, 0x1b, 0xc9, 0x74, 0x50, 0x00, 0x4f }, + /* Shooting Gallery */ + { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x20, 0x20, 0x8a, 0x3a, 0x72, 0x50, 0x00, 0x4f }, + /* Rescue Mission */ + { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x20, 0x20, 0xfb, 0xd3, 0x06, 0x51, 0x00, 0x4f }, + /* Laser Ghost */ + { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x00, 0x00, 0xb7, 0x55, 0x74, 0x70, 0x00, 0x40 }, + /* Assault City */ + { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0xff, 0xff, 0x9f, 0x74, 0x34, 0x70, 0x00, 0x40 }, + /* Missile Defense 3-D */ + { 0x54, 0x4d, 0x52, 0x20, 0x53, 0x45, 0x47, 0x41, 0x41, 0x4c, 0x15, 0x4a, 0x01, 0x80, 0x00, 0x4f } + }; + + int xoff = 44; + + if (size >= 0x8000) + { + if (!memcmp(&rom[0x7ff0], signatures[0], 16) || !memcmp(&rom[0x7ff0], signatures[1], 16)) + xoff = 34; + + if (!memcmp(&rom[0x7ff0], signatures[2], 16)) + xoff = 44; + + if (!memcmp(&rom[0x7ff0], signatures[3], 16)) + xoff = 40; + + if (!memcmp(&rom[0x7ff0], signatures[4], 16)) + xoff = 38; + + if (!memcmp(&rom[0x7ff0], signatures[5], 16)) + xoff = 47; + + if (!memcmp(&rom[0x7ff0], signatures[6], 16)) + xoff = 46; + } + + m_cart->set_lphaser_xoffs(xoff); +} + +void sega8_cart_slot_device::setup_ram() +{ + if (software_entry() == NULL) + { + if (m_type == SEGA8_CASTLE) + { + m_cart->ram_alloc(machine(), 0x2000); + m_cart->set_has_battery(FALSE); + } + else if (m_type == SEGA8_OTHELLO) + { + m_cart->ram_alloc(machine(), 0x800); + m_cart->set_has_battery(FALSE); + } + else if (m_type == SEGA8_BASIC_L3) + { + m_cart->ram_alloc(machine(), 0x8000); + m_cart->set_has_battery(FALSE); + } + else if (m_type == SEGA8_MUSIC_EDITOR) + { + m_cart->ram_alloc(machine(), 0x2800); + m_cart->set_has_battery(FALSE); + } + else if (m_type == SEGA8_DAHJEE_TYPEA) + { + m_cart->ram_alloc(machine(), 0x2400); + m_cart->set_has_battery(FALSE); + } + else if (m_type == SEGA8_DAHJEE_TYPEB) + { + m_cart->ram_alloc(machine(), 0x2000); + m_cart->set_has_battery(FALSE); + } + else if (m_type == SEGA8_CODEMASTERS) + { + // Codemasters cart can have 64KB of RAM (Ernie Els Golf? or 8KB?) and no battery + m_cart->ram_alloc(machine(), 0x10000); + m_cart->set_has_battery(FALSE); + } + else + { + // for generic carts loaded from fullpath we have no way to know exactly if there was RAM, + // how much RAM was in the cart and if there was a battery so we always alloc 32KB and + // we save its content only if the game enable the RAM + m_cart->set_late_battery(TRUE); + m_cart->ram_alloc(machine(), 0x08000); + } + } + else + { + // from softlist we rely on the xml to only allocate the correct amount of RAM and to save it only if a battery was present + const char *battery = get_feature("battery"); + m_cart->set_late_battery(FALSE); + + if (get_software_region_length("ram")) + m_cart->ram_alloc(machine(), get_software_region_length("ram")); + + if (battery && !strcmp(battery, "yes")) + m_cart->set_has_battery(TRUE); + } +} + +bool sega8_cart_slot_device::call_load() +{ + if (m_cart) + { + UINT32 len = (software_entry() == NULL) ? length() : get_software_region_length("rom"); + UINT32 offset = 0; + UINT8 *ROM; + + if (m_is_card && len > 0x8000) + { + seterror(IMAGE_ERROR_UNSPECIFIED, "Attempted loading a card larger than 32KB"); + return IMAGE_INIT_FAIL; + } + + // check for header + if ((len % 0x4000) == 512) + { + offset = 512; + len -= 512; + } + + // make sure that we only get complete (0x4000) rom banks + if (len & 0x3fff) + len = ((len >> 14) + 1) << 14; + + m_cart->rom_alloc(machine(), len); + ROM = m_cart->get_rom_base(); + + if (software_entry() == NULL) + { + fseek(offset, SEEK_SET); + fread(ROM, len); + } + else + memcpy(ROM, get_software_region("rom") + offset, len); + + /* check the image */ + if (verify_cart(ROM, len) == IMAGE_VERIFY_FAIL) + logerror("Warning loading image: verify_cart failed\n"); + + if (software_entry() != NULL) + m_type = sega8_get_pcb_id(get_feature("slot") ? get_feature("slot") : "rom"); + else + m_type = get_cart_type(ROM, len); + + set_lphaser_xoffset(ROM, len); + + setup_ram(); + + // Check for gamegear cartridges with PIN 42 set to SMS mode + if (software_entry() != NULL) + { + const char *pin_42 = get_feature("pin_42"); + if (pin_42 && !strcmp(pin_42, "sms_mode")) + m_cart->set_sms_mode(1); + } + + // when loading from fullpath m_late_battery_enable can be TRUE and in that case + // we attempt to load a battery because the game might have it! + if (m_cart->get_ram_size() && (m_cart->get_has_battery() || m_cart->get_late_battery())) + battery_load(m_cart->get_ram_base(), m_cart->get_ram_size(), 0x00); + + //printf("Type: %s\n", sega8_get_slot(type)); + + internal_header_logging(ROM + offset, len, m_cart->get_ram_size()); + + return IMAGE_INIT_PASS; + } + + return IMAGE_INIT_PASS; +} + + +/*------------------------------------------------- + call_unload + -------------------------------------------------*/ + +void sega8_cart_slot_device::call_unload() +{ + if (m_cart && m_cart->get_ram_size() && m_cart->get_has_battery()) + battery_save(m_cart->get_ram_base(), m_cart->get_ram_size()); +} + + +/*------------------------------------------------- + call softlist load + -------------------------------------------------*/ + +bool sega8_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; +} + + +#ifdef UNUSED_FUNCTION +// For the moment we switch to a different detection routine which allows to detect +// in a single run Codemasters mapper, Korean mapper (including Jang Pung 3 which +// uses a diff signature then the one below here) and Zemina mapper (used by Wonsiin, etc.). +// I leave these here to document alt. detection routines and in the case these functions +// can be updated + +/* Check for Codemasters mapper + 0x7FE3 - 93 - sms Cosmis Spacehead + - sms Dinobasher + - sms The Excellent Dizzy Collection + - sms Fantastic Dizzy + - sms Micro Machines + - gamegear Cosmic Spacehead + - gamegear Micro Machines + - 94 - gamegear Dropzone + - gamegear Ernie Els Golf (also has 64KB additional RAM on the cartridge) + - gamegear Pete Sampras Tennis + - gamegear S.S. Lucifer + - 95 - gamegear Micro Machines 2 - Turbo Tournament + + The Korean game Jang Pung II also seems to use a codemasters style mapper. + */ +int sms_state::detect_codemasters_mapper( UINT8 *rom ) +{ + static const UINT8 jang_pung2[16] = { 0x00, 0xba, 0x38, 0x0d, 0x00, 0xb8, 0x38, 0x0c, 0x00, 0xb6, 0x38, 0x0b, 0x00, 0xb4, 0x38, 0x0a }; + + if (((rom[0x7fe0] & 0x0f ) <= 9) && (rom[0x7fe3] == 0x93 || rom[0x7fe3] == 0x94 || rom[0x7fe3] == 0x95) && rom[0x7fef] == 0x00) + return 1; + + if (!memcmp(&rom[0x7ff0], jang_pung2, 16)) + return 1; + + return 0; +} + + +int sms_state::detect_korean_mapper( UINT8 *rom ) +{ + static const UINT8 signatures[2][16] = + { + { 0x3e, 0x11, 0x32, 0x00, 0xa0, 0x78, 0xcd, 0x84, 0x85, 0x3e, 0x02, 0x32, 0x00, 0xa0, 0xc9, 0xff }, /* Dodgeball King */ + { 0x41, 0x48, 0x37, 0x37, 0x44, 0x37, 0x4e, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20 }, /* Sangokushi 3 */ + }; + int i; + + for (i = 0; i < 2; i++) + { + if (!memcmp(&rom[0x7ff0], signatures[i], 16)) + { + return 1; + } + } + return 0; +} +#endif + +int sega8_cart_slot_device::get_cart_type(UINT8 *ROM, UINT32 len) +{ + int type = SEGA8_BASE_ROM; + + // Check for special cartridge features (new routine, courtesy of Omar Cornut, from MEKA) + if (len >= 0x8000) + { + int _0002 = 0, _8000 = 0, _a000 = 0, _ffff = 0, _3ffe = 0, _4000 = 0, _6000 = 0; + for (int i = 0; i < 0x8000; i++) + { + if (ROM[i] == 0x32) // Z80 opcode for: LD (xxxx), A + { + UINT16 addr = (ROM[i + 2] << 8) | ROM[i + 1]; + if (addr == 0xffff) + { i += 2; _ffff++; continue; } + if (addr == 0x0002 || addr == 0x0003 || addr == 0x0004) + { i += 2; _0002++; continue; } + if (addr == 0x8000) + { i += 2; _8000++; continue; } + if (addr == 0xa000) + { i += 2; _a000++; continue; } + if ( addr == 0x3ffe) + { i += 2; _3ffe++; continue; } + if ( addr == 0x4000 ) + { i += 2; _4000++; continue; } + if ( addr == 0x6000 ) + { i += 2; _6000++; continue; } + } + } + + LOG(("Mapper test: _0002 = %d, _8000 = %d, _a000 = %d, _ffff = %d\n", _0002, _8000, _a000, _ffff)); + + // 2 is a security measure, although tests on existing ROM showed it was not needed + if (_0002 > _ffff + 2 || (_0002 > 0 && _ffff == 0)) + { + type = SEGA8_ZEMINA; + + // Check for special bank 0 signature + if (len == 0x20000 && ROM[0] == 0x00 && ROM[1] == 0x00 && ROM[2] == 0x00 && + ROM[0x1e000] == 0xF3 && ROM[0x1e001] == 0xed && ROM[0x1e002] == 0x56) + type = SEGA8_NEMESIS; + } + else if (_8000 > _ffff + 2 || (_8000 > 0 && _ffff == 0)) + type = SEGA8_CODEMASTERS; + else if (_a000 > _ffff + 2 || (_a000 > 0 && _ffff == 0)) + type = SEGA8_KOREAN; + else if (_3ffe > _ffff + 2 || _3ffe > 0) + type = SEGA8_4PAK; + else if (_4000 > 0 && _6000 > 0 && _8000 > 0 && _a000 > 0) + type = SEGA8_JANGGUN; + } + + // Try to detect Dahjee RAM Expansions + if (len >= 0x8000) + { + int x2000_3000 = 0, xd000_e000_f000 = 0, x2000_ff = 0; + + for (int i = 0; i < 0x8000; i++) + { + if (ROM[i] == 0x32) + { + UINT16 addr = ROM[i + 1] | (ROM[i + 2] << 8); + + switch (addr & 0xf000) + { + case 0x2000: + case 0x3000: + i += 2; + x2000_3000++; + break; + + case 0xd000: + case 0xe000: + case 0xf000: + i += 2; + xd000_e000_f000++; + break; + } + } + } + for (int i = 0x2000; i < 0x4000; i++) + { + if (ROM[i] == 0xff) + x2000_ff++; + } + if (x2000_ff == 0x2000 && (xd000_e000_f000 > 10 || x2000_3000 > 10)) + { + if (xd000_e000_f000 > x2000_3000) + type = SEGA8_DAHJEE_TYPEB; + else + type = SEGA8_DAHJEE_TYPEA; + } + } + + // Terebi Oekaki (TV Draw) + if (!strncmp((const char *)&ROM[0x13b3], "annakmn", 7)) + type = SEGA8_TEREBIOEKAKI; + + // The Castle (ROM+RAM) + if (!strncmp((const char *)&ROM[0x1cc3], "ASCII 1986", 10)) + type = SEGA8_CASTLE; + + // BASIC Level 3 + if (!strncmp((const char *)&ROM[0x6a20], "SC-3000 BASIC Level 3 ver 1.0", 29)) + type = SEGA8_BASIC_L3; + + // Music Editor + if (!strncmp((const char *)&ROM[0x0841], "PIANO", 5) || !strncmp((const char *)&ROM[0x0841], "music", 5)) + type = SEGA8_MUSIC_EDITOR; + + + return type; +} +/*------------------------------------------------- + get default card software + -------------------------------------------------*/ + +const char * sega8_cart_slot_device::get_default_card_software(const machine_config &config, emu_options &options) +{ + if (open_image_file(options)) + { + const char *slot_string = "rom"; + UINT32 len = core_fsize(m_file), offset = 0; + UINT8 *ROM = global_alloc_array(UINT8, len); + int type; + + core_fread(m_file, ROM, len); + + if ((len % 0x4000) == 512) + offset = 512; + + type = get_cart_type(ROM + offset, len - offset); + slot_string = sega8_get_slot(type); + + //printf("type: %s\n", slot_string); + global_free(ROM); + clear(); + + return slot_string; + } + + return software_get_default_slot(config, options, this, "rom"); +} + + + +/*------------------------------------------------- + read + -------------------------------------------------*/ + +READ8_MEMBER(sega8_cart_slot_device::read_cart) +{ + if (m_cart) + return m_cart->read_cart(space, offset); + else + return 0xff; +} + +READ8_MEMBER(sega8_cart_slot_device::read_ram) +{ + if (m_cart) + return m_cart->read_ram(space, offset); + else + return 0xff; +} + + +/*------------------------------------------------- + write + -------------------------------------------------*/ + +WRITE8_MEMBER(sega8_cart_slot_device::write_mapper) +{ + if (m_cart) + m_cart->write_mapper(space, offset, data); +} + +WRITE8_MEMBER(sega8_cart_slot_device::write_cart) +{ + if (m_cart) + m_cart->write_cart(space, offset, data); +} + +WRITE8_MEMBER(sega8_cart_slot_device::write_ram) +{ + if (m_cart) + m_cart->write_ram(space, offset, data); +} + + +/*------------------------------------------------- + Internal header logging + -------------------------------------------------*/ + +void sega8_cart_slot_device::internal_header_logging(UINT8 *ROM, UINT32 len, UINT32 nvram_len) +{ + static const char *const system_region[] = + { + "", + "", + "", + "Master System Japan", + "Master System Export", + "Game Gear Japan", + "Game Gear Export", + "Game Gear International", + "", + "", + "", + "", + "", + "", + "", + "" + }; + + static int csum_length[] = + { + 0x40000, + 0x80000, + 0x100000, + 0, + 0, + 0, + 0, + 0, + 0, + 0, + 0x1ff0, + 0x3ff0, + 0x7ff0, + 0xcff0, + 0x10000, + 0x20000, + }; + + char reserved[10]; + UINT8 version, csum_size, region, serial[3]; + UINT16 checksum, csum = 0; + UINT32 csum_end = 0; + + // LOG FILE DETAILS + logerror("FILE DETAILS\n" ); + logerror("============\n" ); + logerror("Name: %s\n", basename()); + logerror("File Size: 0x%" I64FMT "x\n", (software_entry() == NULL) ? length() : get_software_region_length("rom")); + logerror("Detected type: %s\n", sega8_get_slot(m_type)); + logerror("ROM (Allocated) Size: 0x%X\n", len); + logerror("RAM: %s\n", nvram_len ? "Yes" : "No"); + if (nvram_len) + logerror("RAM (Allocated) Size: 0x%X - Battery: %s\n", nvram_len, m_cart->get_has_battery() ? "Yes" : "No"); + logerror("\n" ); + + + // LOG HEADER DETAILS + if (len < 0x8000) + return; + + for (int i = 0; i < 10; i++) + reserved[i] = ROM[0x7ff0 + i]; + + checksum = ROM[0x7ffa] | (ROM[0x7ffb] << 8); + + for (int i = 0; i < 3; i++) + serial[i] = ROM[0x7ffc + i]; + serial[2] &= 0x0f; + + version = (ROM[0x7ffe] & 0xf0) >> 4; + + csum_size = ROM[0x7fff] & 0x0f; + csum_end = csum_length[csum_size]; + if (!csum_end || csum_end > len) + csum_end = len; + + region = (ROM[0x7fff] & 0xf0) >> 4; + + // compute cart checksum to compare with expected one + for (int i = 0; i < csum_end; i++) + { + if (i < 0x7ff0 || i >= 0x8000) + { + csum += ROM[i]; + csum &= 0xffff; + } + } + + logerror("INTERNAL HEADER\n" ); + logerror("===============\n" ); + logerror("Reserved String: %.10s\n", reserved); + logerror("Region: %s\n", system_region[region]); + logerror("Checksum: (Expected) 0x%x - (Computed) 0x%x\n", checksum, csum); + logerror(" [checksum over 0x%X bytes]\n", csum_length[csum_size]); + logerror("Serial String: %X\n", serial[0] | (serial[1] << 8) | (serial[2] << 16)); + logerror("Software Revision: %x\n", version); + logerror("\n" ); + + + if (m_type == SEGA8_CODEMASTERS) + { + UINT8 day, month, year, hour, minute; + csum = 0; + + day = ROM[0x7fe1]; + month = ROM[0x7fe2]; + year = ROM[0x7fe3]; + hour = ROM[0x7fe4]; + minute = ROM[0x7fe5]; + checksum = ROM[0x7fe6] | (ROM[0x7fe7] << 8); + csum_size = ROM[0x7fe0]; + + // compute cart checksum to compare with expected one + for (int i = 0; i < len; i += 2) + { + if (i < 0x7ff0 || i >= 0x8000) + { + csum += (ROM[i] | (ROM[i + 1] << 8)); + csum &= 0xffff; + } + } + + logerror("CODEMASTERS HEADER\n" ); + logerror("==================\n" ); + logerror("Build date & time: %x/%x/%x %.2x:%.2x\n", day, month, year, hour, minute); + logerror("Checksum: (Expected) 0x%x - (Computed) 0x%x\n", checksum, csum); + logerror(" [checksum over 0x%X bytes]\n", csum_size * 0x4000); + logerror("\n" ); + } +} diff --git a/src/emu/bus/sega8/sega8_slot.h b/src/emu/bus/sega8/sega8_slot.h new file mode 100644 index 00000000000..5f11457cc5a --- /dev/null +++ b/src/emu/bus/sega8/sega8_slot.h @@ -0,0 +1,229 @@ +#ifndef __SEGA8_SLOT_H +#define __SEGA8_SLOT_H + +/*************************************************************************** + TYPE DEFINITIONS + ***************************************************************************/ + + +/* PCB */ +enum +{ + SEGA8_BASE_ROM = 0, + SEGA8_EEPROM, + SEGA8_TEREBIOEKAKI, + SEGA8_4PAK, + SEGA8_CODEMASTERS, + SEGA8_ZEMINA, + SEGA8_NEMESIS, + SEGA8_JANGGUN, + SEGA8_KOREAN, + SEGA8_KOREAN_NOBANK, + SEGA8_OTHELLO, + SEGA8_CASTLE, + SEGA8_BASIC_L3, + SEGA8_MUSIC_EDITOR, + SEGA8_DAHJEE_TYPEA, + SEGA8_DAHJEE_TYPEB +}; + + +extern const device_type SEGA8_CART_SLOT; +extern const device_type SEGA8_CARD_SLOT; + + +// ======================> device_sega8_cart_interface + +class device_sega8_cart_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_sega8_cart_interface(const machine_config &mconfig, device_t &device); + virtual ~device_sega8_cart_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart) { return 0xff; } + virtual DECLARE_WRITE8_MEMBER(write_cart) {} + virtual DECLARE_WRITE8_MEMBER(write_mapper) {} + // a few carts (for SG1000) acts as a RAM expansion, taking control of the system RAM in 0xc000-0xffff + virtual DECLARE_READ8_MEMBER(read_ram) { return 0xff; } + virtual DECLARE_WRITE8_MEMBER(write_ram) {} + + void rom_alloc(running_machine &machine, UINT32 size); + void ram_alloc(running_machine &machine, UINT32 size); + + virtual void late_bank_setup() {} + + void set_has_battery(bool val) { has_battery = val; } + bool get_has_battery() { return has_battery; } + void set_late_battery(bool val) { m_late_battery_enable = val; } + bool get_late_battery() { return m_late_battery_enable; } + void set_lphaser_xoffs(int val) { m_lphaser_xoffs = val; } + int get_lphaser_xoffs() { return m_lphaser_xoffs; } + void set_sms_mode(int val) { m_sms_mode = val; } + int get_sms_mode() { return m_sms_mode; } + +//protected: + UINT8* get_rom_base() { return m_rom; } + UINT8* get_ram_base() { return m_ram; } + UINT32 get_rom_size() { return m_rom_size; } + UINT32 get_ram_size() { return m_ram_size; } + + void rom_map_setup(UINT32 size); + void ram_map_setup(UINT8 banks); + +//private: + // internal state + UINT8 *m_rom; + UINT8 *m_ram; + UINT32 m_rom_size; + UINT32 m_ram_size; + int m_rom_page_count; + + bool has_battery; + + // we use this variable for fullpath loading only: in this case, RAM is always allocated, + // but we set has_battery only if RAM is actually enabled during game... + bool m_late_battery_enable; + + int m_lphaser_xoffs; + int m_sms_mode; +}; + + +// ======================> sega8_cart_slot_device + +class sega8_cart_slot_device : public device_t, + public device_image_interface, + public device_slot_interface +{ +public: + // construction/destruction + sega8_cart_slot_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, bool is_card, const char *shortname, const char *source); + sega8_cart_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~sega8_cart_slot_device(); + + // device-level overrides + virtual void device_start(); + virtual void device_config_complete() { update_names(SEGA8_CART_SLOT, "cartridge", "cart"); } + + // image-level overrides + virtual bool call_load(); + virtual void call_unload(); + virtual bool call_softlist_load(char *swlist, char *swname, rom_entry *start_entry); + + int get_type() { return m_type; } + int get_cart_type(UINT8 *ROM, UINT32 len); + + void setup_ram(); + void internal_header_logging(UINT8 *ROM, UINT32 len, UINT32 nvram_len); + int verify_cart(UINT8 *magic, int size); + void set_lphaser_xoffset(UINT8 *rom, int size); + + void set_mandatory(bool val) { m_must_be_loaded = val; } + void set_intf(const char * interface) { m_interface = interface; } + void set_ext(const char * extensions) { m_extensions = extensions; } + + virtual iodevice_t image_type() const { return IO_CARTSLOT; } + virtual bool is_readable() const { return 1; } + virtual bool is_writeable() const { return 0; } + virtual bool is_creatable() const { return 0; } + virtual bool must_be_loaded() const { return m_must_be_loaded; } + virtual bool is_reset_on_load() const { return 1; } + virtual const option_guide *create_option_guide() const { return NULL; } + virtual const char *image_interface() const { return m_interface; } + virtual const char *file_extensions() const { return m_extensions; } + + // slot interface overrides + virtual const char * get_default_card_software(const machine_config &config, emu_options &options); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read_cart); + virtual DECLARE_WRITE8_MEMBER(write_cart); + virtual DECLARE_WRITE8_MEMBER(write_mapper); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + + +//protected: + int m_type; + bool m_must_be_loaded, m_is_card; + const char *m_interface; + const char *m_extensions; + device_sega8_cart_interface* m_cart; +}; + +// ======================> sega8_card_slot_device + +class sega8_card_slot_device : public sega8_cart_slot_device +{ +public: + // construction/destruction + sega8_card_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + virtual void device_config_complete() { update_names(SEGA8_CARD_SLOT, "card", "card"); } +}; + + +/*************************************************************************** + DEVICE CONFIGURATION MACROS + ***************************************************************************/ + +#define MCFG_SG1000_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_cart_slot_device *>(device)->set_mandatory(TRUE); \ + static_cast<sega8_cart_slot_device *>(device)->set_intf("sg1000_cart"); \ + static_cast<sega8_cart_slot_device *>(device)->set_ext("bin,sg"); + +#define MCFG_OMV_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_cart_slot_device *>(device)->set_mandatory(FALSE); \ + static_cast<sega8_cart_slot_device *>(device)->set_intf("sg1000_cart"); \ + static_cast<sega8_cart_slot_device *>(device)->set_ext("bin,sg"); + +#define MCFG_SC3000_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_cart_slot_device *>(device)->set_mandatory(TRUE); \ + static_cast<sega8_cart_slot_device *>(device)->set_intf("sg1000_cart"); \ + static_cast<sega8_cart_slot_device *>(device)->set_ext("bin,sg,sc"); + +#define MCFG_SG1000MK3_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_cart_slot_device *>(device)->set_mandatory(FALSE); \ + static_cast<sega8_cart_slot_device *>(device)->set_intf("sms_cart"); \ + static_cast<sega8_cart_slot_device *>(device)->set_ext("bin,sms,sg"); + +#define MCFG_SMS_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_cart_slot_device *>(device)->set_mandatory(FALSE); \ + static_cast<sega8_cart_slot_device *>(device)->set_intf("sms_cart"); \ + static_cast<sega8_cart_slot_device *>(device)->set_ext("bin,sms"); + +#define MCFG_GG_CARTRIDGE_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CART_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_cart_slot_device *>(device)->set_mandatory(TRUE); \ + static_cast<sega8_cart_slot_device *>(device)->set_intf("gamegear_cart"); \ + static_cast<sega8_cart_slot_device *>(device)->set_ext("bin,gg"); + + + +#define MCFG_SMS_CARD_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CARD_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_card_slot_device *>(device)->set_mandatory(FALSE); \ + static_cast<sega8_card_slot_device *>(device)->set_intf("sms_card"); \ + static_cast<sega8_card_slot_device *>(device)->set_ext("bin"); +#define MCFG_SG1000_CARD_ADD(_tag,_slot_intf,_def_slot) \ + MCFG_DEVICE_ADD(_tag, SEGA8_CARD_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) \ + static_cast<sega8_card_slot_device *>(device)->set_intf("sg1000_cart"); \ + static_cast<sega8_card_slot_device *>(device)->set_ext("bin,sg"); + + +#endif diff --git a/src/emu/bus/sms_ctrl/joypad.c b/src/emu/bus/sms_ctrl/joypad.c new file mode 100644 index 00000000000..578fd2054ae --- /dev/null +++ b/src/emu/bus/sms_ctrl/joypad.c @@ -0,0 +1,77 @@ +/********************************************************************** + + Sega Master System "Control Pad"/generic joystick emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "joypad.h" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type SMS_JOYPAD = &device_creator<sms_joypad_device>; + + +static INPUT_PORTS_START( sms_joypad ) + PORT_START("JOYPAD") + PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_8WAY + PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_8WAY + PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_8WAY + PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_8WAY + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) // TH + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) // TR +INPUT_PORTS_END + + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor sms_joypad_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( sms_joypad ); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_joypad_device - constructor +//------------------------------------------------- + +sms_joypad_device::sms_joypad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_JOYPAD, "Control Pad", tag, owner, clock, "sms_joypad", __FILE__), + device_sms_control_port_interface(mconfig, *this), + m_joypad(*this, "JOYPAD") +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_joypad_device::device_start() +{ +} + + +//------------------------------------------------- +// sms_peripheral_r - joypad read +//------------------------------------------------- + +UINT8 sms_joypad_device::peripheral_r() +{ + return m_joypad->read(); +} diff --git a/src/emu/bus/sms_ctrl/joypad.h b/src/emu/bus/sms_ctrl/joypad.h new file mode 100644 index 00000000000..f102ca6d596 --- /dev/null +++ b/src/emu/bus/sms_ctrl/joypad.h @@ -0,0 +1,53 @@ +/********************************************************************** + + Sega Master System "Control Pad"/generic joystick emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_JOYPAD__ +#define __SMS_JOYPAD__ + + +#include "emu.h" +#include "smsctrl.h" + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_joypad_device + +class sms_joypad_device : public device_t, + public device_sms_control_port_interface +{ +public: + // construction/destruction + sms_joypad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual ioport_constructor device_input_ports() const; + +protected: + // device-level overrides + virtual void device_start(); + + // device_sms_control_port_interface overrides + virtual UINT8 peripheral_r(); + +private: + required_ioport m_joypad; +}; + + +// device type definition +extern const device_type SMS_JOYPAD; + + +#endif diff --git a/src/emu/bus/sms_ctrl/lphaser.c b/src/emu/bus/sms_ctrl/lphaser.c new file mode 100644 index 00000000000..8736657cae6 --- /dev/null +++ b/src/emu/bus/sms_ctrl/lphaser.c @@ -0,0 +1,284 @@ +/********************************************************************** + + Sega Master System "Light Phaser" (light gun) emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "lphaser.h" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type SMS_LIGHT_PHASER = &device_creator<sms_light_phaser_device>; + + +#define LGUN_RADIUS 6 +#define LGUN_X_INTERVAL 4 + + +CUSTOM_INPUT_MEMBER( sms_light_phaser_device::th_pin_r ) +{ + // The returned value is inverted due to IP_ACTIVE_LOW mapping. + return ~m_sensor_last_state; +} + + +INPUT_CHANGED_MEMBER( sms_light_phaser_device::position_changed ) +{ + if (newval != oldval) + sensor_check(); +} + + +static INPUT_PORTS_START( sms_light_phaser ) + PORT_START("CTRL_PORT") + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL (trigger) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_light_phaser_device, th_pin_r, NULL) + PORT_BIT( 0x9f, IP_ACTIVE_LOW, IPT_UNUSED ) + + PORT_START("LPHASER_X") + PORT_BIT( 0xff, 0x00, IPT_LIGHTGUN_X) PORT_CROSSHAIR(X, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(15) PORT_CHANGED_MEMBER(DEVICE_SELF, sms_light_phaser_device, position_changed, 0) + + PORT_START("LPHASER_Y") + PORT_BIT( 0xff, 0x00, IPT_LIGHTGUN_Y) PORT_CROSSHAIR(Y, 1.0, 0.0, 0) PORT_SENSITIVITY(50) PORT_KEYDELTA(15) PORT_CHANGED_MEMBER(DEVICE_SELF, sms_light_phaser_device, position_changed, 0) +INPUT_PORTS_END + + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor sms_light_phaser_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( sms_light_phaser ); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_light_phaser_device - constructor +//------------------------------------------------- + +sms_light_phaser_device::sms_light_phaser_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_LIGHT_PHASER, "Light Phaser", tag, owner, clock, "sms_light_phaser", __FILE__), + device_video_interface(mconfig, *this), + device_sms_control_port_interface(mconfig, *this), + m_lphaser_pins(*this, "CTRL_PORT"), + m_lphaser_x(*this, "LPHASER_X"), + m_lphaser_y(*this, "LPHASER_Y") +{ + // Workaround for failed validation that occurs when running on a driver + // with Sega Scope emulation, which adds 2 screens (left/right lenses). + m_screen_tag = ":screen"; +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_light_phaser_device::device_start() +{ + save_item(NAME(m_sensor_last_state)); + m_lphaser_timer = timer_alloc(TIMER_LPHASER); +} + + +void sms_light_phaser_device::device_reset() +{ + m_sensor_last_state = 1; // off (1) +} + + +//------------------------------------------------- +// sms_peripheral_r - light phaser read +//------------------------------------------------- + +UINT8 sms_light_phaser_device::peripheral_r() +{ + return m_lphaser_pins->read(); +} + + +/* + Light Phaser (light gun) emulation notes: + - The sensor is activated based on color brightness of some individual + pixels being drawn by the beam, at circular area where the gun is aiming. + - Currently, brightness is calculated based only on single pixels. + - In general, after the trigger is pressed, games draw the next frame using + a light color pattern, to make sure sensor will be activated. If emulation + skips that frame, sensor may stay deactivated. Frameskip set to 0 (no skip) + is recommended to avoid problems. + - When sensor switches from on (0) to off (1), a value is latched for the + HCount register. + - When sensor switches from off to on, a flag is set. The emulation uses the + flag to signal that TH line is activated when the status of the input port + is read. After read, the flag is cleared, or else it is cleared later when + the Pause status is read (end of a frame). This is necessary because the + "Color & Switch Test" ROM only reads the TH state after VINT occurs. + - The gun test of "Color & Switch Test" is an example that requires checks + of sensor status independent of other events, like trigger press or TH bit + reads. Another example is the title screen of "Hang-On & Safari Hunt", where + the game only reads HCount register in a loop, expecting a latch by the gun. + - The whole procedure is managed by a timer callback, that always reschedule + itself to run in some intervals when the beam is at the circular area. +*/ +int sms_light_phaser_device::bright_aim_area( emu_timer *timer, int lgun_x, int lgun_y ) +{ + const int r_x_r = LGUN_RADIUS * LGUN_RADIUS; + const rectangle &visarea = m_screen->visible_area(); + int beam_x = m_screen->hpos(); + int beam_y = m_screen->vpos(); + int dx, dy; + int result = 1; + int pos_changed = 0; + double dx_radius; + + while (1) + { + /* If beam's y isn't at a line where the aim area is, change it + the next line it enters that area. */ + dy = abs(beam_y - lgun_y); + if (dy > LGUN_RADIUS || beam_y < visarea.min_y || beam_y > visarea.max_y) + { + beam_y = lgun_y - LGUN_RADIUS; + if (beam_y < visarea.min_y) + beam_y = visarea.min_y; + dy = abs(beam_y - lgun_y); + pos_changed = 1; + } + + /* Caculate distance in x of the radius, relative to beam's y distance. + First try some shortcuts. */ + switch (dy) + { + case LGUN_RADIUS: + dx_radius = 0; + break; + case 0: + dx_radius = LGUN_RADIUS; + break; + default: + /* step 1: r^2 = dx^2 + dy^2 */ + /* step 2: dx^2 = r^2 - dy^2 */ + /* step 3: dx = sqrt(r^2 - dy^2) */ + dx_radius = ceil((float) sqrt((float) (r_x_r - (dy * dy)))); + } + + /* If beam's x isn't in the circular aim area, change it + to the next point it enters that area. */ + dx = abs(beam_x - lgun_x); + if (dx > dx_radius || beam_x < visarea.min_x || beam_x > visarea.max_x) + { + /* If beam's x has passed the aim area, advance to + next line and recheck y/x coordinates. */ + if (beam_x > lgun_x) + { + beam_x = 0; + beam_y++; + continue; + } + beam_x = lgun_x - dx_radius; + if (beam_x < visarea.min_x) + beam_x = visarea.min_x; + pos_changed = 1; + } + + if (pos_changed) + break; + + if (m_sensor_last_state == 0) /* sensor is on */ + { + /* keep sensor on until out of the aim area */ + result = 0; + } + else + { + rgb_t color; + UINT8 brightness; + /* brightness of the lightgray color in the frame drawn by Light Phaser games */ + const UINT8 sensor_min_brightness = 0x7f; + + color = m_port->pixel_r(); + + /* reference: http://www.w3.org/TR/AERT#color-contrast */ + brightness = (color.r() * 0.299) + (color.g() * 0.587) + (color.b() * 0.114); + //printf ("color brightness: %2X for x %d y %d\n", brightness, beam_x, beam_y); + + result = (brightness >= sensor_min_brightness) ? 0 : 1; + } + + if (result == 0) + { + /* Set next check for when sensor will be off */ + beam_x = lgun_x + dx_radius + 1; + if (beam_x > visarea.max_x) + beam_x = visarea.max_x + 1; + break; + } + else + { + /* Next check after the minimum interval */ + beam_x += LGUN_X_INTERVAL; + pos_changed = 1; + } + } + timer->adjust(m_screen->time_until_pos(beam_y, beam_x)); + + return result; +} + + +UINT16 sms_light_phaser_device::screen_hpos_nonscaled(int scaled_hpos) +{ + const rectangle &visarea = m_screen->visible_area(); + int offset_x = (scaled_hpos * (visarea.max_x - visarea.min_x)) / 255; + return visarea.min_x + offset_x; +} + + +UINT16 sms_light_phaser_device::screen_vpos_nonscaled(int scaled_vpos) +{ + const rectangle &visarea = m_screen->visible_area(); + int offset_y = (scaled_vpos * (visarea.max_y - visarea.min_y)) / 255; + return visarea.min_y + offset_y; +} + + +void sms_light_phaser_device::sensor_check() +{ + int sensor_new_state; + + const int x = screen_hpos_nonscaled(m_lphaser_x->read()); + const int y = screen_vpos_nonscaled(m_lphaser_y->read()); + + sensor_new_state = bright_aim_area(m_lphaser_timer, x, y); + if (sensor_new_state != m_sensor_last_state) + { + m_port->th_pin_w(sensor_new_state); + m_sensor_last_state = sensor_new_state; + } +} + + +void sms_light_phaser_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_LPHASER: + sensor_check(); + break; + default: + assert_always(FALSE, "Unknown id in sms_light_phaser_device::device_timer"); + } +} diff --git a/src/emu/bus/sms_ctrl/lphaser.h b/src/emu/bus/sms_ctrl/lphaser.h new file mode 100644 index 00000000000..8afaa47fcd7 --- /dev/null +++ b/src/emu/bus/sms_ctrl/lphaser.h @@ -0,0 +1,70 @@ +/********************************************************************** + + Sega Master System "Light Phaser" (light gun) emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_LIGHT_PHASER__ +#define __SMS_LIGHT_PHASER__ + + +#include "emu.h" +#include "smsctrl.h" + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_light_phaser_device + +class sms_light_phaser_device : public device_t, + public device_video_interface, + public device_sms_control_port_interface +{ +public: + // construction/destruction + sms_light_phaser_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual ioport_constructor device_input_ports() const; + + DECLARE_CUSTOM_INPUT_MEMBER( th_pin_r ); + DECLARE_INPUT_CHANGED_MEMBER( position_changed ); + +protected: + // device-level overrides + virtual void device_start(); + virtual void device_reset(); + + // device_sms_control_port_interface overrides + virtual UINT8 peripheral_r(); + +private: + required_ioport m_lphaser_pins; + required_ioport m_lphaser_x; + required_ioport m_lphaser_y; + + int m_sensor_last_state; + emu_timer *m_lphaser_timer; + static const device_timer_id TIMER_LPHASER = 0; + + void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr); + void sensor_check(); + int bright_aim_area( emu_timer *timer, int lgun_x, int lgun_y ); + UINT16 screen_hpos_nonscaled(int scaled_hpos); + UINT16 screen_vpos_nonscaled(int scaled_vpos); +}; + + +// device type definition +extern const device_type SMS_LIGHT_PHASER; + + +#endif diff --git a/src/emu/bus/sms_ctrl/paddle.c b/src/emu/bus/sms_ctrl/paddle.c new file mode 100644 index 00000000000..7258578d6da --- /dev/null +++ b/src/emu/bus/sms_ctrl/paddle.c @@ -0,0 +1,109 @@ +/********************************************************************** + + Sega Master System "Paddle Control" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "paddle.h" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type SMS_PADDLE = &device_creator<sms_paddle_device>; + + +#define PADDLE_INTERVAL attotime::from_hz(XTAL_53_693175MHz/15/256) + + +CUSTOM_INPUT_MEMBER( sms_paddle_device::dir_pins_r ) +{ + UINT8 data = m_paddle_x->read(); + + if (m_read_state) + data >>= 4; + + // The returned value is inverted due to IP_ACTIVE_LOW mapping. + return ~data; +} + + +CUSTOM_INPUT_MEMBER( sms_paddle_device::tr_pin_r ) +{ + // The returned value is inverted due to IP_ACTIVE_LOW mapping. + return ~m_read_state; +} + + +static INPUT_PORTS_START( sms_paddle ) + PORT_START("CTRL_PORT") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_paddle_device, dir_pins_r, NULL) // Directional pins + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) // TH + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_paddle_device, tr_pin_r, NULL) + + PORT_START("PADDLE_X") // Paddle knob + PORT_BIT( 0xff, 0x80, IPT_PADDLE) PORT_SENSITIVITY(40) PORT_KEYDELTA(20) PORT_CENTERDELTA(0) PORT_MINMAX(0,255) +INPUT_PORTS_END + + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor sms_paddle_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( sms_paddle ); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_paddle_device - constructor +//------------------------------------------------- + +sms_paddle_device::sms_paddle_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_PADDLE, "Paddle", tag, owner, clock, "sms_paddle", __FILE__), + device_sms_control_port_interface(mconfig, *this), + m_paddle_pins(*this, "CTRL_PORT"), + m_paddle_x(*this, "PADDLE_X"), + m_interval(PADDLE_INTERVAL) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_paddle_device::device_start() +{ + m_start_time = machine().time(); + m_read_state = 0; + + save_item(NAME(m_start_time)); + save_item(NAME(m_read_state)); +} + + +//------------------------------------------------- +// sms_peripheral_r - paddle read +//------------------------------------------------- + +UINT8 sms_paddle_device::peripheral_r() +{ + int num_intervals = (machine().time() - m_start_time).as_double() / m_interval.as_double(); + m_read_state = num_intervals & 1; + + return m_paddle_pins->read(); +} diff --git a/src/emu/bus/sms_ctrl/paddle.h b/src/emu/bus/sms_ctrl/paddle.h new file mode 100644 index 00000000000..59d41ec645d --- /dev/null +++ b/src/emu/bus/sms_ctrl/paddle.h @@ -0,0 +1,61 @@ +/********************************************************************** + + Sega Master System "Paddle Control" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_PADDLE__ +#define __SMS_PADDLE__ + + +#include "emu.h" +#include "smsctrl.h" + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_paddle_device + +class sms_paddle_device : public device_t, + public device_sms_control_port_interface +{ +public: + // construction/destruction + sms_paddle_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual ioport_constructor device_input_ports() const; + + DECLARE_CUSTOM_INPUT_MEMBER( dir_pins_r ); + DECLARE_CUSTOM_INPUT_MEMBER( tr_pin_r ); + +protected: + // device-level overrides + virtual void device_start(); + + // device_sms_control_port_interface overrides + virtual UINT8 peripheral_r(); + +private: + required_ioport m_paddle_pins; + required_ioport m_paddle_x; + + UINT8 m_read_state; + attotime m_start_time; + const attotime m_interval; +}; + + +// device type definition +extern const device_type SMS_PADDLE; + + +#endif diff --git a/src/emu/bus/sms_ctrl/rfu.c b/src/emu/bus/sms_ctrl/rfu.c new file mode 100644 index 00000000000..8b92979e7e6 --- /dev/null +++ b/src/emu/bus/sms_ctrl/rfu.c @@ -0,0 +1,141 @@ +/********************************************************************** + + Sega Master System "Rapid Fire Unit" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "rfu.h" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type SMS_RAPID_FIRE = &device_creator<sms_rapid_fire_device>; + + +#define RAPID_FIRE_INTERVAL attotime::from_hz(10) + + +static INPUT_PORTS_START( sms_rapid_fire ) + PORT_START("rfu_sw") // Rapid Fire Unit switches + PORT_CONFNAME( 0x03, 0x00, "Rapid Fire Unit" ) + PORT_CONFSETTING( 0x00, DEF_STR( Off ) ) + PORT_CONFSETTING( 0x01, "Button 1" ) + PORT_CONFSETTING( 0x02, "Button 2" ) + PORT_CONFSETTING( 0x03, "Button 1 + 2" ) +INPUT_PORTS_END + + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor sms_rapid_fire_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( sms_rapid_fire ); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_rapid_fire_device - constructor +//------------------------------------------------- + +sms_rapid_fire_device::sms_rapid_fire_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_RAPID_FIRE, "Rapid Fire", tag, owner, clock, "sms_rapid_fire", __FILE__), + device_sms_control_port_interface(mconfig, *this), + m_rfire_sw(*this, "rfu_sw"), + m_subctrl_port(*this, "ctrl"), + m_interval(RAPID_FIRE_INTERVAL) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_rapid_fire_device::device_start() +{ + m_start_time = machine().time(); + m_read_state = 0; + + save_item(NAME(m_start_time)); + save_item(NAME(m_read_state)); + + m_subctrl_port->device_start(); +} + + +//------------------------------------------------- +// sms_peripheral_r - rapid fire read +//------------------------------------------------- + +UINT8 sms_rapid_fire_device::peripheral_r() +{ + UINT8 data = 0xff; + + int num_intervals = (machine().time() - m_start_time).as_double() / m_interval.as_double(); + m_read_state = num_intervals & 1; + + data = m_subctrl_port->port_r(); + + /* Check Rapid Fire switch for Button 1 (TL) */ + if (!(data & 0x20) && (m_rfire_sw->read() & 0x01)) + data |= m_read_state << 5; + + /* Check Rapid Fire switch for Button 2 (TR) */ + if (!(data & 0x80) && (m_rfire_sw->read() & 0x02)) + data |= m_read_state << 7; + + return data; +} + + +//------------------------------------------------- +// sms_peripheral_w - rapid fire write +//------------------------------------------------- + +void sms_rapid_fire_device::peripheral_w(UINT8 data) +{ + m_subctrl_port->port_w(data); +} + + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +WRITE_LINE_MEMBER( sms_rapid_fire_device::th_pin_w ) +{ + m_port->th_pin_w(state); +} + + +READ32_MEMBER( sms_rapid_fire_device::pixel_r ) +{ + return m_port->pixel_r(); +} + + +static MACHINE_CONFIG_FRAGMENT( rfire_slot ) + MCFG_SMS_CONTROL_PORT_ADD("ctrl", sms_control_port_devices, "joypad") + MCFG_SMS_CONTROL_PORT_TH_INPUT_HANDLER(WRITELINE(sms_rapid_fire_device, th_pin_w)) + MCFG_SMS_CONTROL_PORT_PIXEL_HANDLER(READ32(sms_rapid_fire_device, pixel_r)) +MACHINE_CONFIG_END + + +machine_config_constructor sms_rapid_fire_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( rfire_slot ); +} diff --git a/src/emu/bus/sms_ctrl/rfu.h b/src/emu/bus/sms_ctrl/rfu.h new file mode 100644 index 00000000000..4edefaf2517 --- /dev/null +++ b/src/emu/bus/sms_ctrl/rfu.h @@ -0,0 +1,63 @@ +/********************************************************************** + + Sega Master System "Rapid Fire Unit" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_RAPID_FIRE__ +#define __SMS_RAPID_FIRE__ + + +#include "emu.h" +#include "smsctrl.h" + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_rapid_fire_device + +class sms_rapid_fire_device : public device_t, + public device_sms_control_port_interface +{ +public: + // construction/destruction + sms_rapid_fire_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual ioport_constructor device_input_ports() const; + + DECLARE_WRITE_LINE_MEMBER(th_pin_w); + DECLARE_READ32_MEMBER(pixel_r); + +protected: + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + + // device_sms_control_port_interface overrides + virtual UINT8 peripheral_r(); + virtual void peripheral_w(UINT8 data); + +private: + required_ioport m_rfire_sw; + required_device<sms_control_port_device> m_subctrl_port; + + UINT8 m_read_state; + attotime m_start_time; + const attotime m_interval; +}; + + +// device type definition +extern const device_type SMS_RAPID_FIRE; + + +#endif diff --git a/src/emu/bus/sms_ctrl/smsctrl.c b/src/emu/bus/sms_ctrl/smsctrl.c new file mode 100644 index 00000000000..41662470b28 --- /dev/null +++ b/src/emu/bus/sms_ctrl/smsctrl.c @@ -0,0 +1,123 @@ +/********************************************************************** + + Sega Master System controller port emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "smsctrl.h" + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type SMS_CONTROL_PORT = &device_creator<sms_control_port_device>; + + + +//************************************************************************** +// CARD INTERFACE +//************************************************************************** + +//------------------------------------------------- +// device_sms_control_port_interface - constructor +//------------------------------------------------- + +device_sms_control_port_interface::device_sms_control_port_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig,device) +{ + m_port = dynamic_cast<sms_control_port_device *>(device.owner()); +} + + +//------------------------------------------------- +// ~device_sms_control_port_interface - destructor +//------------------------------------------------- + +device_sms_control_port_interface::~device_sms_control_port_interface() +{ +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_control_port_device - constructor +//------------------------------------------------- + +sms_control_port_device::sms_control_port_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_CONTROL_PORT, "Sega SMS control port", tag, owner, clock, "sms_control_port", __FILE__), + device_slot_interface(mconfig, *this), + m_th_pin_handler(*this), + m_pixel_handler(*this) +{ +} + + +//------------------------------------------------- +// sms_control_port_device - destructor +//------------------------------------------------- + +sms_control_port_device::~sms_control_port_device() +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_control_port_device::device_start() +{ + m_device = dynamic_cast<device_sms_control_port_interface *>(get_card_device()); + + m_th_pin_handler.resolve_safe(); + m_pixel_handler.resolve_safe(0); +} + + +UINT8 sms_control_port_device::port_r() +{ + UINT8 data = 0xff; + if (m_device) + data = m_device->peripheral_r(); + return data; +} + +void sms_control_port_device::port_w( UINT8 data ) +{ + if (m_device) + m_device->peripheral_w(data); +} + + +void sms_control_port_device::th_pin_w(int state) +{ + m_th_pin_handler(state); +} + +UINT32 sms_control_port_device::pixel_r() +{ + return m_pixel_handler(); +} + + +//------------------------------------------------- +// SLOT_INTERFACE( sms_control_port_devices ) +//------------------------------------------------- + +SLOT_INTERFACE_START( sms_control_port_devices ) + SLOT_INTERFACE("joypad", SMS_JOYPAD) + SLOT_INTERFACE("lphaser", SMS_LIGHT_PHASER) + SLOT_INTERFACE("paddle", SMS_PADDLE) + SLOT_INTERFACE("sportspad", SMS_SPORTS_PAD) + SLOT_INTERFACE("sportspadjp", SMS_SPORTS_PAD_JP) + SLOT_INTERFACE("rapidfire", SMS_RAPID_FIRE) +SLOT_INTERFACE_END diff --git a/src/emu/bus/sms_ctrl/smsctrl.h b/src/emu/bus/sms_ctrl/smsctrl.h new file mode 100644 index 00000000000..f9581d86c8f --- /dev/null +++ b/src/emu/bus/sms_ctrl/smsctrl.h @@ -0,0 +1,127 @@ +/********************************************************************** + + Sega Master System controller port emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +********************************************************************** + + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_CONTROL_PORT__ +#define __SMS_CONTROL_PORT__ + +#include "emu.h" + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_SMS_CONTROL_PORT_ADD(_tag, _slot_intf, _def_slot) \ + MCFG_DEVICE_ADD(_tag, SMS_CONTROL_PORT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) +#define MCFG_SMS_CONTROL_PORT_MODIFY(_tag) \ + MCFG_DEVICE_MODIFY(_tag) + + +#define MCFG_SMS_CONTROL_PORT_TH_INPUT_HANDLER(_devcb) \ + devcb = &sms_control_port_device::set_th_input_handler(*device, DEVCB2_##_devcb); + + +#define MCFG_SMS_CONTROL_PORT_PIXEL_HANDLER(_devcb) \ + devcb = &sms_control_port_device::set_pixel_handler(*device, DEVCB2_##_devcb); + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_control_port_device + +class device_sms_control_port_interface; + +class sms_control_port_device : public device_t, + public device_slot_interface +{ +public: + // construction/destruction + sms_control_port_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~sms_control_port_device(); + + // static configuration helpers + template<class _Object> static devcb2_base &set_th_input_handler(device_t &device, _Object object) { return downcast<sms_control_port_device &>(device).m_th_pin_handler.set_callback(object); } + + template<class _Object> static devcb2_base &set_pixel_handler(device_t &device, _Object object) { return downcast<sms_control_port_device &>(device).m_pixel_handler.set_callback(object); } + + // Physical DE-9 connector interface + + // Data returned by the port_r methods: + // bit 0 - pin 1 - Up + // bit 1 - pin 2 - Down + // bit 2 - pin 3 - Left + // bit 3 - pin 4 - Right + // bit 4 - pin 5 - Vcc (no data) + // bit 5 - pin 6 - TL (Button 1/Light Phaser Trigger) + // bit 6 - pin 7 - TH (Light Phaser sensor) + // pin 8 - GND + // bit 7 - pin 9 - TR (Button 2) + // + UINT8 port_r(); + void port_w( UINT8 data ); + + void th_pin_w(int state); + UINT32 pixel_r(); + +//protected: + // device-level overrides + virtual void device_start(); + + device_sms_control_port_interface *m_device; + +private: + devcb2_write_line m_th_pin_handler; + devcb2_read32 m_pixel_handler; +}; + + +// ======================> device_sms_control_port_interface + +// class representing interface-specific live sms_expansion card +class device_sms_control_port_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_sms_control_port_interface(const machine_config &mconfig, device_t &device); + virtual ~device_sms_control_port_interface(); + + virtual UINT8 peripheral_r() { return 0xff; }; + virtual void peripheral_w(UINT8 data) { }; + +protected: + sms_control_port_device *m_port; +}; + + +// device type definition +extern const device_type SMS_CONTROL_PORT; + + +// slot devices +#include "joypad.h" +#include "lphaser.h" +#include "paddle.h" +#include "sports.h" +#include "sportsjp.h" +#include "rfu.h" + +SLOT_INTERFACE_EXTERN( sms_control_port_devices ); + + +#endif diff --git a/src/emu/bus/sms_ctrl/sports.c b/src/emu/bus/sms_ctrl/sports.c new file mode 100644 index 00000000000..4b0e184a4d0 --- /dev/null +++ b/src/emu/bus/sms_ctrl/sports.c @@ -0,0 +1,159 @@ +/********************************************************************** + + Sega Master System "Sports Pad" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "sports.h" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type SMS_SPORTS_PAD = &device_creator<sms_sports_pad_device>; + + +#define SPORTS_PAD_INTERVAL attotime::from_hz(XTAL_53_693175MHz/15/512) + + +CUSTOM_INPUT_MEMBER( sms_sports_pad_device::dir_pins_r ) +{ + UINT8 data = 0; + + switch (m_read_state) + { + case 0: + data = m_sports_x->read() >> 4; + break; + case 1: + data = m_sports_x->read(); + break; + case 2: + data = m_sports_y->read() >> 4; + break; + case 3: + data = m_sports_y->read(); + break; + } + + // The returned value is inverted due to IP_ACTIVE_LOW mapping. + return ~(data & 0x0f); +} + + +CUSTOM_INPUT_MEMBER( sms_sports_pad_device::th_pin_r ) +{ + return m_last_data; +} + + +INPUT_CHANGED_MEMBER( sms_sports_pad_device::th_pin_w ) +{ + attotime cur_time = machine().time(); + + if (cur_time - m_last_time > m_interval) + { + m_read_state = 0; + } + else + { + m_read_state = (m_read_state + 1) & 3; + } + m_last_time = cur_time; + m_last_data = newval; +} + + +static INPUT_PORTS_START( sms_sports_pad ) + PORT_START("SPORTS_IN") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_device, dir_pins_r, NULL) // Directional pins + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL (Button 1) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_device, th_pin_r, NULL) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) // TR (Button 2) + + PORT_START("SPORTS_OUT") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_UNUSED ) // Directional pins + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNUSED ) // TL (Button 1) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_OUTPUT ) PORT_CHANGED_MEMBER(DEVICE_SELF, sms_sports_pad_device, th_pin_w, NULL) + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNUSED ) // TR (Button 2) + + PORT_START("SPORTS_X") /* Sports Pad X axis */ + PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_RESET PORT_REVERSE + + PORT_START("SPORTS_Y") /* Sports Pad Y axis */ + PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) PORT_RESET PORT_REVERSE +INPUT_PORTS_END + + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor sms_sports_pad_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( sms_sports_pad ); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_sports_pad_device - constructor +//------------------------------------------------- + +sms_sports_pad_device::sms_sports_pad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_SPORTS_PAD, "Sports Pad", tag, owner, clock, "sms_sports_pad", __FILE__), + device_sms_control_port_interface(mconfig, *this), + m_sports_in(*this, "SPORTS_IN"), + m_sports_out(*this, "SPORTS_OUT"), + m_sports_x(*this, "SPORTS_X"), + m_sports_y(*this, "SPORTS_Y"), + m_interval(SPORTS_PAD_INTERVAL) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_sports_pad_device::device_start() +{ + m_read_state = 0; + m_last_data = 0; + m_last_time = machine().time(); + + save_item(NAME(m_read_state)); + save_item(NAME(m_last_data)); + save_item(NAME(m_last_time)); +} + + +//------------------------------------------------- +// sms_peripheral_r - sports pad read +//------------------------------------------------- + +UINT8 sms_sports_pad_device::peripheral_r() +{ + return m_sports_in->read(); +} + + +//------------------------------------------------- +// sms_peripheral_w - sports pad write +//------------------------------------------------- + +void sms_sports_pad_device::peripheral_w(UINT8 data) +{ + m_sports_out->write(data); +} diff --git a/src/emu/bus/sms_ctrl/sports.h b/src/emu/bus/sms_ctrl/sports.h new file mode 100644 index 00000000000..0ce5cc96b09 --- /dev/null +++ b/src/emu/bus/sms_ctrl/sports.h @@ -0,0 +1,66 @@ +/********************************************************************** + + Sega Master System "Sports Pad" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_SPORTS_PAD__ +#define __SMS_SPORTS_PAD__ + + +#include "emu.h" +#include "smsctrl.h" + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_sports_pad_device + +class sms_sports_pad_device : public device_t, + public device_sms_control_port_interface +{ +public: + // construction/destruction + sms_sports_pad_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual ioport_constructor device_input_ports() const; + + DECLARE_CUSTOM_INPUT_MEMBER( dir_pins_r ); + DECLARE_CUSTOM_INPUT_MEMBER( th_pin_r ); + DECLARE_INPUT_CHANGED_MEMBER( th_pin_w ); + +protected: + // device-level overrides + virtual void device_start(); + + // device_sms_control_port_interface overrides + virtual UINT8 peripheral_r(); + virtual void peripheral_w(UINT8 data); + +private: + required_ioport m_sports_in; + required_ioport m_sports_out; + required_ioport m_sports_x; + required_ioport m_sports_y; + + UINT8 m_read_state; + UINT8 m_last_data; + const attotime m_interval; + attotime m_last_time; +}; + + +// device type definition +extern const device_type SMS_SPORTS_PAD; + + +#endif diff --git a/src/emu/bus/sms_ctrl/sportsjp.c b/src/emu/bus/sms_ctrl/sportsjp.c new file mode 100644 index 00000000000..a9b181c1ee6 --- /dev/null +++ b/src/emu/bus/sms_ctrl/sportsjp.c @@ -0,0 +1,157 @@ +/********************************************************************** + + Sega Master System "Sports Pad" (japanese model) emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +// The japanese Sports Pad controller is only required to play the cartridge +// Sports Pad Soccer, released in Japan. It uses a different mode than the +// used by the US model, due to missing output lines on Sega Mark III +// controller ports. + +#include "sportsjp.h" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type SMS_SPORTS_PAD_JP = &device_creator<sms_sports_pad_jp_device>; + + +#define SPORTS_PAD_JP_INTERVAL attotime::from_hz(30000) // 30Hz (not measured) + + +DECLARE_CUSTOM_INPUT_MEMBER( sms_sports_pad_jp_device::dir_pins_r ) +{ + UINT8 data = 0; + + switch (m_read_state) + { + case 0: + data = m_sports_jp_x->read() >> 4; + break; + case 1: + data = m_sports_jp_x->read(); + break; + case 2: + data = m_sports_jp_y->read() >> 4; + break; + case 3: + data = m_sports_jp_y->read(); + break; + } + + // The returned value is inverted due to IP_ACTIVE_LOW mapping. + return ~(data & 0x0f); +} + + +static INPUT_PORTS_START( sms_sports_pad_jp ) + PORT_START("SPORTS_JP_IN") + PORT_BIT( 0x0f, IP_ACTIVE_LOW, IPT_SPECIAL ) PORT_CUSTOM_MEMBER(DEVICE_SELF, sms_sports_pad_jp_device, dir_pins_r, NULL) // Directional pins + PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNUSED ) // Vcc + PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON1 ) // TL (Button 1) + PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNUSED ) // TH + PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_BUTTON2 ) // TR (Button 2) + + PORT_START("SPORTS_JP_X") /* Sports Pad X axis */ + PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_X ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) + + PORT_START("SPORTS_JP_Y") /* Sports Pad Y axis */ + PORT_BIT( 0xff, 0x00, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(50) PORT_KEYDELTA(40) +INPUT_PORTS_END + + +//------------------------------------------------- +// input_ports - device-specific input ports +//------------------------------------------------- + +ioport_constructor sms_sports_pad_jp_device::device_input_ports() const +{ + return INPUT_PORTS_NAME( sms_sports_pad_jp ); +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_sports_pad_jp_device - constructor +//------------------------------------------------- + +sms_sports_pad_jp_device::sms_sports_pad_jp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_SPORTS_PAD_JP, "Sports Pad JP", tag, owner, clock, "sms_sports_pad_jp", __FILE__), + device_sms_control_port_interface(mconfig, *this), + m_sports_jp_in(*this, "SPORTS_JP_IN"), + m_sports_jp_x(*this, "SPORTS_JP_X"), + m_sports_jp_y(*this, "SPORTS_JP_Y"), + m_interval(SPORTS_PAD_JP_INTERVAL) +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_sports_pad_jp_device::device_start() +{ + m_start_time = machine().time(); + m_read_state = 0; + + save_item(NAME(m_start_time)); + save_item(NAME(m_read_state)); +} + + +//------------------------------------------------- +// sms_peripheral_r - sports pad read +//------------------------------------------------- + +UINT8 sms_sports_pad_jp_device::peripheral_r() +{ + UINT8 data; + int num_intervals = (machine().time() - m_start_time).as_double() / m_interval.as_double(); + m_read_state = num_intervals % 5; + + data = m_sports_jp_in->read(); + + switch (m_read_state) + { + case 0: + // X high nibble + data &= ~0x20; // TL 0 + data &= ~0x80; // TR 0 + break; + case 1: + // X low nibble + data |= 0x20; // TL 1 + data &= ~0x80; // TR 0 + break; + case 2: + // Y high nibble + data &= ~0x20; // TL 0 + data &= ~0x80; // TR 0 + break; + case 3: + // Y low nibble + data |= 0x20; // TL 1 + data &= ~0x80; // TR 0 + break; + case 4: + // buttons 1 and 2 + data = (data & 0x20) >> 5 | (data & 0x80) >> 6 | 0xfc; + data |= 0x20; // TL 1 + data |= 0x80; // TR 1 + break; + } + + return data; +} diff --git a/src/emu/bus/sms_ctrl/sportsjp.h b/src/emu/bus/sms_ctrl/sportsjp.h new file mode 100644 index 00000000000..065f8293a97 --- /dev/null +++ b/src/emu/bus/sms_ctrl/sportsjp.h @@ -0,0 +1,66 @@ +/********************************************************************** + + Sega Master System "Sports Pad" (japanese model) emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +// The japanese Sports Pad controller is only required to play the cartridge +// Sports Pad Soccer, released in Japan. It uses a different mode than the +// used by the US model, due to missing output lines on Sega Mark III +// controller ports. + +#pragma once + +#ifndef __SMS_SPORTS_PAD_JP__ +#define __SMS_SPORTS_PAD_JP__ + + +#include "emu.h" +#include "smsctrl.h" + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_sports_pad_jp_device + +class sms_sports_pad_jp_device : public device_t, + public device_sms_control_port_interface +{ +public: + // construction/destruction + sms_sports_pad_jp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // optional information overrides + virtual ioport_constructor device_input_ports() const; + + CUSTOM_INPUT_MEMBER( dir_pins_r ); + +protected: + // device-level overrides + virtual void device_start(); + + // device_sms_control_port_interface overrides + virtual UINT8 peripheral_r(); + +private: + required_ioport m_sports_jp_in; + required_ioport m_sports_jp_x; + required_ioport m_sports_jp_y; + + UINT8 m_read_state; + attotime m_start_time; + const attotime m_interval; +}; + + +// device type definition +extern const device_type SMS_SPORTS_PAD_JP; + + +#endif diff --git a/src/emu/bus/sms_exp/gender.c b/src/emu/bus/sms_exp/gender.c new file mode 100644 index 00000000000..0b80deb7c68 --- /dev/null +++ b/src/emu/bus/sms_exp/gender.c @@ -0,0 +1,105 @@ +/********************************************************************** + + Sega Master System "Gender Adapter" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "gender.h" + + + +//************************************************************************** +// DEVICE DEFINITIONS +//************************************************************************** + +const device_type SMS_GENDER_ADAPTER = &device_creator<sms_gender_adapter_device>; + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_gender_adapter_device - constructor +//------------------------------------------------- + +sms_gender_adapter_device::sms_gender_adapter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_GENDER_ADAPTER, "Gender Adapter", tag, owner, clock, "sms_gender_adapter", __FILE__), + device_sms_expansion_slot_interface(mconfig, *this), + m_subslot(*this, "subslot") +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_gender_adapter_device::device_start() +{ +} + + +//------------------------------------------------- +// read +//------------------------------------------------- + +READ8_MEMBER(sms_gender_adapter_device::read) +{ + return m_subslot->read_cart(space, offset); +} + +READ8_MEMBER(sms_gender_adapter_device::read_ram) +{ + return m_subslot->read_ram(space, offset); +} + + +//------------------------------------------------- +// write +//------------------------------------------------- + +WRITE8_MEMBER(sms_gender_adapter_device::write_mapper) +{ + m_subslot->write_mapper(space, offset, data); +} + +WRITE8_MEMBER(sms_gender_adapter_device::write) +{ + m_subslot->write_cart(space, offset, data); +} + +WRITE8_MEMBER(sms_gender_adapter_device::write_ram) +{ + m_subslot->write_ram(space, offset, data); +} + +static SLOT_INTERFACE_START(sms_cart) + SLOT_INTERFACE_INTERNAL("rom", SEGA8_ROM_STD) + SLOT_INTERFACE_INTERNAL("codemasters", SEGA8_ROM_CODEMASTERS) + SLOT_INTERFACE_INTERNAL("4pak", SEGA8_ROM_4PAK) + SLOT_INTERFACE_INTERNAL("zemina", SEGA8_ROM_ZEMINA) + SLOT_INTERFACE_INTERNAL("nemesis", SEGA8_ROM_NEMESIS) + SLOT_INTERFACE_INTERNAL("janggun", SEGA8_ROM_JANGGUN) + SLOT_INTERFACE_INTERNAL("korean", SEGA8_ROM_KOREAN) + SLOT_INTERFACE_INTERNAL("korean_nb", SEGA8_ROM_KOREAN_NB) +SLOT_INTERFACE_END + +//------------------------------------------------- +// machine_config_additions - device-specific +// machine configurations +//------------------------------------------------- + +static MACHINE_CONFIG_FRAGMENT( genderadp_slot ) + MCFG_SMS_CARTRIDGE_ADD("subslot", sms_cart, NULL) +MACHINE_CONFIG_END + + +machine_config_constructor sms_gender_adapter_device::device_mconfig_additions() const +{ + return MACHINE_CONFIG_NAME( genderadp_slot ); +} diff --git a/src/emu/bus/sms_exp/gender.h b/src/emu/bus/sms_exp/gender.h new file mode 100644 index 00000000000..4458e880688 --- /dev/null +++ b/src/emu/bus/sms_exp/gender.h @@ -0,0 +1,57 @@ +/********************************************************************** + + Sega Master System "Gender Adapter" emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_GENDER_ADAPTER__ +#define __SMS_GENDER_ADAPTER__ + + +#include "emu.h" +#include "smsexp.h" +#include "bus/sega8/sega8_slot.h" +#include "bus/sega8/rom.h" + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_gender_adapter_device + +class sms_gender_adapter_device : public device_t, + public device_sms_expansion_slot_interface +{ +public: + // construction/destruction + sms_gender_adapter_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + + // device_sms_expansion_slot_interface overrides + virtual DECLARE_READ8_MEMBER(read); + virtual DECLARE_WRITE8_MEMBER(write); + virtual DECLARE_WRITE8_MEMBER(write_mapper); + virtual DECLARE_READ8_MEMBER(read_ram); + virtual DECLARE_WRITE8_MEMBER(write_ram); + +protected: + // device-level overrides + virtual void device_start(); + virtual machine_config_constructor device_mconfig_additions() const; + +private: + required_device<sega8_cart_slot_device> m_subslot; +}; + + +// device type definition +extern const device_type SMS_GENDER_ADAPTER; + + +#endif diff --git a/src/emu/bus/sms_exp/smsexp.c b/src/emu/bus/sms_exp/smsexp.c new file mode 100644 index 00000000000..e1ddb4969f0 --- /dev/null +++ b/src/emu/bus/sms_exp/smsexp.c @@ -0,0 +1,139 @@ +/********************************************************************** + + Sega Master System expansion slot emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +**********************************************************************/ + +#include "smsexp.h" + + + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +const device_type SMS_EXPANSION_SLOT = &device_creator<sms_expansion_slot_device>; + + + +//************************************************************************** +// CARD INTERFACE +//************************************************************************** + +//------------------------------------------------- +// device_sms_expansion_slot_interface - constructor +//------------------------------------------------- + +device_sms_expansion_slot_interface::device_sms_expansion_slot_interface(const machine_config &mconfig, device_t &device) + : device_slot_card_interface(mconfig,device) +{ +} + + +//------------------------------------------------- +// ~device_sms_expansion_slot_interface - destructor +//------------------------------------------------- + +device_sms_expansion_slot_interface::~device_sms_expansion_slot_interface() +{ +} + + + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +//------------------------------------------------- +// sms_expansion_slot_device - constructor +//------------------------------------------------- + +sms_expansion_slot_device::sms_expansion_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : + device_t(mconfig, SMS_EXPANSION_SLOT, "Sega SMS expansion slot", tag, owner, clock, "sms_expansion_slot", __FILE__), + device_slot_interface(mconfig, *this) +{ +} + + +//------------------------------------------------- +// sms_expansion_slot_device - destructor +//------------------------------------------------- + +sms_expansion_slot_device::~sms_expansion_slot_device() +{ +} + + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void sms_expansion_slot_device::device_start() +{ + m_device = dynamic_cast<device_sms_expansion_slot_interface *>(get_card_device()); +} + + +//------------------------------------------------- +// read +//------------------------------------------------- + +READ8_MEMBER(sms_expansion_slot_device::read) +{ + if (m_device) + return m_device->read(space, offset); + else + return 0xff; +} + +READ8_MEMBER(sms_expansion_slot_device::read_ram) +{ + if (m_device) + return m_device->read_ram(space, offset); + else + return 0xff; +} + + +//------------------------------------------------- +// write +//------------------------------------------------- + +WRITE8_MEMBER(sms_expansion_slot_device::write_mapper) +{ + if (m_device) + m_device->write_mapper(space, offset, data); +} + +WRITE8_MEMBER(sms_expansion_slot_device::write) +{ + if (m_device) + m_device->write(space, offset, data); +} + +WRITE8_MEMBER(sms_expansion_slot_device::write_ram) +{ + if (m_device) + m_device->write_ram(space, offset, data); +} + +int sms_expansion_slot_device::get_lphaser_xoffs() +{ + if (m_device) + return m_device->get_lphaser_xoffs(); + else + return 0; +} + + + +//------------------------------------------------- +// SLOT_INTERFACE( sms_expansion_devices ) +//------------------------------------------------- + +SLOT_INTERFACE_START( sms_expansion_devices ) + SLOT_INTERFACE("genderadp", SMS_GENDER_ADAPTER) +SLOT_INTERFACE_END diff --git a/src/emu/bus/sms_exp/smsexp.h b/src/emu/bus/sms_exp/smsexp.h new file mode 100644 index 00000000000..871424aa856 --- /dev/null +++ b/src/emu/bus/sms_exp/smsexp.h @@ -0,0 +1,96 @@ +/********************************************************************** + + Sega Master System expansion slot emulation + + Copyright MESS Team. + Visit http://mamedev.org for licensing and usage restrictions. + +********************************************************************** + + +**********************************************************************/ + +#pragma once + +#ifndef __SMS_EXPANSION_SLOT__ +#define __SMS_EXPANSION_SLOT__ + +#include "emu.h" + + + +//************************************************************************** +// INTERFACE CONFIGURATION MACROS +//************************************************************************** + +#define MCFG_SMS_EXPANSION_ADD(_tag, _slot_intf, _def_slot) \ + MCFG_DEVICE_ADD(_tag, SMS_EXPANSION_SLOT, 0) \ + MCFG_DEVICE_SLOT_INTERFACE(_slot_intf, _def_slot, false) + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// ======================> sms_expansion_slot_device + +class device_sms_expansion_slot_interface; + +class sms_expansion_slot_device : public device_t, + public device_slot_interface +{ +public: + // construction/destruction + sms_expansion_slot_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); + virtual ~sms_expansion_slot_device(); + + // reading and writing + DECLARE_READ8_MEMBER(read); + DECLARE_WRITE8_MEMBER(write); + DECLARE_WRITE8_MEMBER(write_mapper); + DECLARE_READ8_MEMBER(read_ram); + DECLARE_WRITE8_MEMBER(write_ram); + + int get_lphaser_xoffs(); + + device_sms_expansion_slot_interface *m_device; + +protected: + // device-level overrides + virtual void device_start(); +}; + + +// ======================> device_sms_expansion_slot_interface + +// class representing interface-specific live sms_expansion card +class device_sms_expansion_slot_interface : public device_slot_card_interface +{ +public: + // construction/destruction + device_sms_expansion_slot_interface(const machine_config &mconfig, device_t &device); + virtual ~device_sms_expansion_slot_interface(); + + // reading and writing + virtual DECLARE_READ8_MEMBER(read) { return 0xff; }; + virtual DECLARE_WRITE8_MEMBER(write) { }; + virtual DECLARE_WRITE8_MEMBER(write_mapper) {}; + virtual DECLARE_READ8_MEMBER(read_ram) { return 0xff; }; + virtual DECLARE_WRITE8_MEMBER(write_ram) { }; + + virtual int get_lphaser_xoffs() { return 0; }; +}; + + +// device type definition +extern const device_type SMS_EXPANSION_SLOT; + + +// slot devices +#include "gender.h" + +SLOT_INTERFACE_EXTERN( sms_expansion_devices ); + + +#endif diff --git a/src/emu/bus/snes/sgb.h b/src/emu/bus/snes/sgb.h index fc7c940083e..549a7152da2 100644 --- a/src/emu/bus/snes/sgb.h +++ b/src/emu/bus/snes/sgb.h @@ -5,9 +5,9 @@ #include "rom.h" #include "cpu/lr35902/lr35902.h" -#include "machine/gb_slot.h" -#include "machine/gb_rom.h" -#include "machine/gb_mbc.h" +#include "bus/gameboy/gb_slot.h" +#include "bus/gameboy/rom.h" +#include "bus/gameboy/mbc.h" #include "video/gb_lcd.h" #include "audio/gb.h" |