summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/bus
diff options
context:
space:
mode:
author Miodrag Milanovic <mmicko@gmail.com>2014-03-10 14:00:01 +0000
committer Miodrag Milanovic <mmicko@gmail.com>2014-03-10 14:00:01 +0000
commitd131f7fa0cd0255b1a33778d1b51d1bb967fb53a (patch)
tree3021094c998780a9c4b442173889a2dbc46a20e4 /src/emu/bus
parentfe36f872390885a181844708e6459d4d85f1c2fc (diff)
Moved rest of console slot devices to bus folder (nw)
Diffstat (limited to 'src/emu/bus')
-rw-r--r--src/emu/bus/bus.mak130
-rw-r--r--src/emu/bus/gameboy/gb_slot.c831
-rw-r--r--src/emu/bus/gameboy/gb_slot.h207
-rw-r--r--src/emu/bus/gameboy/mbc.c1041
-rw-r--r--src/emu/bus/gameboy/mbc.h291
-rw-r--r--src/emu/bus/gameboy/rom.c362
-rw-r--r--src/emu/bus/gameboy/rom.h144
-rw-r--r--src/emu/bus/gba/gba_slot.c483
-rw-r--r--src/emu/bus/gba/gba_slot.h577
-rw-r--r--src/emu/bus/gba/rom.c460
-rw-r--r--src/emu/bus/gba/rom.h173
-rw-r--r--src/emu/bus/megadrive/eeprom.c374
-rw-r--r--src/emu/bus/megadrive/eeprom.h141
-rw-r--r--src/emu/bus/megadrive/jcart.c252
-rw-r--r--src/emu/bus/megadrive/jcart.h82
-rw-r--r--src/emu/bus/megadrive/md_carts.c67
-rw-r--r--src/emu/bus/megadrive/md_carts.h24
-rw-r--r--src/emu/bus/megadrive/md_slot.c1107
-rw-r--r--src/emu/bus/megadrive/md_slot.h263
-rw-r--r--src/emu/bus/megadrive/rom.c1417
-rw-r--r--src/emu/bus/megadrive/rom.h583
-rw-r--r--src/emu/bus/megadrive/sk.c93
-rw-r--r--src/emu/bus/megadrive/sk.h33
-rw-r--r--src/emu/bus/megadrive/stm95.c266
-rw-r--r--src/emu/bus/megadrive/stm95.h84
-rw-r--r--src/emu/bus/megadrive/svp.c474
-rw-r--r--src/emu/bus/megadrive/svp.h73
-rw-r--r--src/emu/bus/saturn/bram.c100
-rw-r--r--src/emu/bus/saturn/bram.h69
-rw-r--r--src/emu/bus/saturn/dram.c102
-rw-r--r--src/emu/bus/saturn/dram.h49
-rw-r--r--src/emu/bus/saturn/rom.c53
-rw-r--r--src/emu/bus/saturn/rom.h31
-rw-r--r--src/emu/bus/saturn/sat_slot.c260
-rw-r--r--src/emu/bus/saturn/sat_slot.h119
-rw-r--r--src/emu/bus/sega8/rom.c941
-rw-r--r--src/emu/bus/sega8/rom.h382
-rw-r--r--src/emu/bus/sega8/sega8_slot.c823
-rw-r--r--src/emu/bus/sega8/sega8_slot.h229
-rw-r--r--src/emu/bus/sms_ctrl/joypad.c77
-rw-r--r--src/emu/bus/sms_ctrl/joypad.h53
-rw-r--r--src/emu/bus/sms_ctrl/lphaser.c284
-rw-r--r--src/emu/bus/sms_ctrl/lphaser.h70
-rw-r--r--src/emu/bus/sms_ctrl/paddle.c109
-rw-r--r--src/emu/bus/sms_ctrl/paddle.h61
-rw-r--r--src/emu/bus/sms_ctrl/rfu.c141
-rw-r--r--src/emu/bus/sms_ctrl/rfu.h63
-rw-r--r--src/emu/bus/sms_ctrl/smsctrl.c123
-rw-r--r--src/emu/bus/sms_ctrl/smsctrl.h127
-rw-r--r--src/emu/bus/sms_ctrl/sports.c159
-rw-r--r--src/emu/bus/sms_ctrl/sports.h66
-rw-r--r--src/emu/bus/sms_ctrl/sportsjp.c157
-rw-r--r--src/emu/bus/sms_ctrl/sportsjp.h66
-rw-r--r--src/emu/bus/sms_exp/gender.c105
-rw-r--r--src/emu/bus/sms_exp/gender.h57
-rw-r--r--src/emu/bus/sms_exp/smsexp.c139
-rw-r--r--src/emu/bus/sms_exp/smsexp.h96
-rw-r--r--src/emu/bus/snes/sgb.h6
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"