diff options
Diffstat (limited to 'src/mame/sega/magistr16.cpp')
| -rw-r--r-- | src/mame/sega/magistr16.cpp | 430 |
1 files changed, 430 insertions, 0 deletions
diff --git a/src/mame/sega/magistr16.cpp b/src/mame/sega/magistr16.cpp new file mode 100644 index 00000000000..2ec30528190 --- /dev/null +++ b/src/mame/sega/magistr16.cpp @@ -0,0 +1,430 @@ +// license: BSD-3-Clause +// copyright-holders: Angelo Salese +/************************************************************************************************** + +https://segaretro.org/Magistr_16 + +a.k.a. Super Kombat KO16 + +MegaDrive SECAM clone with ISA bus glued together thru expansion bus + +TODO: +- accomodate YM7101 to use PAL semantics and V30 modes +- Fix error #1004 (at PC=FC07DA), should be wrong type for flash device; + +**************************************************************************************************/ + +#include "emu.h" + +#include "bus/isa/isa.h" +#include "bus/megadrive/cart/options.h" +#include "bus/megadrive/cart/slot.h" +#include "bus/pc_kbd/keyboards.h" +#include "bus/pc_kbd/pc_kbdc.h" +#include "bus/sms_ctrl/controllers.h" +#include "bus/sms_ctrl/smsctrl.h" +#include "cpu/m68000/m68000.h" +#include "cpu/z80/z80.h" +#include "machine/input_merger.h" +#include "machine/intelfsh.h" +#include "machine/ram.h" +#include "machine/sega_md_ioport.h" +#include "machine/w83977tf.h" +#include "sound/spkrdev.h" +#include "sound/ymopn.h" +#include "video/ym7101.h" + +#include "screen.h" +#include "softlist_dev.h" +#include "speaker.h" + +namespace { + +class magistr16_state : public driver_device +{ +public: + magistr16_state(const machine_config &mconfig, device_type type, const char *tag) + : driver_device(mconfig, type, tag) + , m_md68kcpu(*this, "md68kcpu") + , m_mdz80cpu(*this, "mdz80cpu") + , m_mdscreen(*this, "mdscreen") + , m_md_cart(*this, "md_cart") + , m_md_vdp(*this, "md_vdp") + , m_opn(*this, "opn") + , m_md_68k_sound_view(*this, "md_68k_sound_view") + , m_md_ctrl_ports(*this, { "md_ctrl1", "md_ctrl2", "md_exp" }) + , m_md_ioports(*this, "md_ioport%u", 1U) + , m_isabus(*this, "isabus") + , m_flash(*this, "flash") + { } + + void magistr16(machine_config &config); + +protected: + void machine_start() override ATTR_COLD; + void machine_reset() override ATTR_COLD; + + void md_68k_map(address_map &map) ATTR_COLD; + void md_cpu_space_map(address_map &map); + void md_68k_z80_map(address_map &map) ATTR_COLD; + void md_z80_map(address_map &map) ATTR_COLD; + void md_z80_io(address_map &map) ATTR_COLD; + void md_ioctrl_map(address_map &map) ATTR_COLD; +private: + required_device<m68000_device> m_md68kcpu; + required_device<z80_device> m_mdz80cpu; + required_device<screen_device> m_mdscreen; + required_device<megadrive_cart_slot_device> m_md_cart; + required_device<ym7101_device> m_md_vdp; + required_device<ym_generic_device> m_opn; + memory_view m_md_68k_sound_view; + required_device_array<sms_control_port_device, 3> m_md_ctrl_ports; + required_device_array<megadrive_io_port_device, 3> m_md_ioports; + required_device<isa16_device> m_isabus; + required_device<fujitsu_29f016a_device> m_flash; + + std::unique_ptr<u8[]> m_sound_program; + + bool m_z80_reset = false; + bool m_z80_busrq = false; + u32 m_z80_main_address = 0; + + void flush_z80_state(); + + static void winbond_superio_config(device_t *device); +}; + + +void magistr16_state::md_68k_map(address_map &map) +{ + map.unmap_value_high(); + // assume it can't access expansion bus area + map(0x000000, 0x3fffff).rw(m_md_cart, FUNC(megadrive_cart_slot_device::base_r), FUNC(megadrive_cart_slot_device::base_w)); + map(0x400000, 0x5fffff).rw(m_flash, FUNC(fujitsu_29f016a_device::read), FUNC(fujitsu_29f016a_device::write)); + + map(0x7e0000, 0x7e07ff).rw(m_isabus, FUNC(isa16_device::io_r), FUNC(isa16_device::io_w)).umask16(0x00ff); + +// map(0x800000, 0x9fffff) unmapped or 32X + map(0xa00000, 0xa07fff).view(m_md_68k_sound_view); + m_md_68k_sound_view[0](0xa00000, 0xa07fff).before_delay(NAME([](offs_t) { return 1; })).m(*this, FUNC(magistr16_state::md_68k_z80_map)); +// map(0xa07f00, 0xa07fff) Z80 VDP space (freezes machine if accessed from 68k) +// map(0xa08000, 0xa0ffff) Z80 68k window (assume no DTACK), or just mirror of above according to TD HW notes? +// map(0xa10000, 0xa100ff) I/O + map(0xa10000, 0xa100ff).m(*this, FUNC(magistr16_state::md_ioctrl_map)); + +// map(0xa11000, 0xa110ff) memory mode register +// map(0xa11100, 0xa111ff) Z80 BUSREQ/BUSACK + map(0xa11100, 0xa11101).lrw16( + NAME([this] (offs_t offset, u16 mem_mask) { + address_space &space = m_md68kcpu->space(AS_PROGRAM); + // TODO: enough for all edge cases but timekill + u16 open_bus = space.read_word(m_md68kcpu->pc() - 2) & 0xfefe; + // printf("%06x -> %04x\n", m_md68kcpu->pc() - 2, open_bus); + u16 res = (!m_z80_busrq || m_z80_reset) ^ 1; + return (res << 8) | (res) | open_bus; + }), + NAME([this] (offs_t offset, u16 data, u16 mem_mask) { + //printf("%04x %04x\n", data, mem_mask); + if (!ACCESSING_BITS_0_7) + { + m_z80_busrq = !!BIT(~data, 8); + } + else if (!ACCESSING_BITS_8_15) + { + m_z80_busrq = !!BIT(~data, 0); + } + else // word access + { + m_z80_busrq = !!BIT(~data, 8); + } + flush_z80_state(); + }) + ); +// map(0xa11200, 0xa112ff) Z80 RESET + map(0xa11200, 0xa11201).lw16( + NAME([this] (offs_t offset, u16 data, u16 mem_mask) { + if (!ACCESSING_BITS_0_7) + { + m_z80_reset = !!BIT(~data, 8); + } + else if (!ACCESSING_BITS_8_15) + { + m_z80_reset = !!BIT(~data, 0); + } + else // word access + { + m_z80_reset = !!BIT(~data, 8); + } + flush_z80_state(); + }) + ); +// map(0xa11300, 0xa113ff) <open bus> + +// map(0xa11400, 0xa1dfff) <unmapped> (no DTACK generation, freezes machine without additional HW) +// map(0xa12000, 0xa120ff).m(m_exp, FUNC(...::fdc_map)); + map(0xa13000, 0xa130ff).rw(m_md_cart, FUNC(megadrive_cart_slot_device::time_r), FUNC(megadrive_cart_slot_device::time_w)); +// map(0xa14000, 0xa14003) TMSS lock +// map(0xa15100, 0xa153ff) 32X registers if present, <unmapped> otherwise +// map(0xc00000, 0xdfffff) VDP and PSG (with mirrors and holes) +// $d00000 alias required by earthdef + map(0xc00000, 0xc0001f).mirror(0x100000).m(m_md_vdp, FUNC(ym7101_device::if16_map)); + map(0xe00000, 0xe3ffff).mirror(0x1c0000).ram(); // more work RAM compared to stock MD +} + +// $a10000 base +void magistr16_state::md_ioctrl_map(address_map &map) +{ + // version unknown, expansion bus should be on. + // Assume overseas (bit 7) and PAL set (bit 6) + map(0x00, 0x01).lr8(NAME([] () { return 1 << 7 | 1 << 6 | 0 << 5; })); + map(0x02, 0x03).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::data_r), FUNC(megadrive_io_port_device::data_w)); + map(0x04, 0x05).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::data_r), FUNC(megadrive_io_port_device::data_w)); + map(0x06, 0x07).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::data_r), FUNC(megadrive_io_port_device::data_w)); + map(0x08, 0x09).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::ctrl_r), FUNC(megadrive_io_port_device::ctrl_w)); + map(0x0a, 0x0b).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::ctrl_r), FUNC(megadrive_io_port_device::ctrl_w)); + map(0x0c, 0x0d).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::ctrl_r), FUNC(megadrive_io_port_device::ctrl_w)); + map(0x0e, 0x0f).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::txdata_r), FUNC(megadrive_io_port_device::txdata_w)); + map(0x10, 0x11).r(m_md_ioports[0], FUNC(megadrive_io_port_device::rxdata_r)); + map(0x12, 0x13).rw(m_md_ioports[0], FUNC(megadrive_io_port_device::s_ctrl_r), FUNC(megadrive_io_port_device::s_ctrl_w)); + map(0x14, 0x15).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::txdata_r), FUNC(megadrive_io_port_device::txdata_w)); + map(0x16, 0x17).r(m_md_ioports[1], FUNC(megadrive_io_port_device::rxdata_r)); + map(0x18, 0x19).rw(m_md_ioports[1], FUNC(megadrive_io_port_device::s_ctrl_r), FUNC(megadrive_io_port_device::s_ctrl_w)); + map(0x1a, 0x1b).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::txdata_r), FUNC(megadrive_io_port_device::txdata_w)); + map(0x1c, 0x1d).r(m_md_ioports[2], FUNC(megadrive_io_port_device::rxdata_r)); + map(0x1e, 0x1f).rw(m_md_ioports[2], FUNC(megadrive_io_port_device::s_ctrl_r), FUNC(megadrive_io_port_device::s_ctrl_w)); +} + +void magistr16_state::md_cpu_space_map(address_map &map) +{ + map(0xfffff3, 0xfffff3).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 25; })); + // TODO: IPL0 (external irq tied to VDP IE2) + map(0xfffff5, 0xfffff5).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 26; })); + map(0xfffff7, 0xfffff7).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 27; })); + map(0xfffff9, 0xfffff9).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([this] () -> u8 { + m_md_vdp->irq_ack(); + return 28; + })); + map(0xfffffb, 0xfffffb).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 29; })); + map(0xfffffd, 0xfffffd).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([this] () -> u8 { + m_md_vdp->irq_ack(); + return 30; + })); + map(0xffffff, 0xffffff).before_time(m_md68kcpu, FUNC(m68000_device::vpa_sync)).after_delay(m_md68kcpu, FUNC(m68000_device::vpa_after)).lr8(NAME([] () -> u8 { return 31; })); +} + + +void magistr16_state::flush_z80_state() +{ + m_mdz80cpu->set_input_line(INPUT_LINE_RESET, m_z80_reset ? ASSERT_LINE : CLEAR_LINE); + m_mdz80cpu->set_input_line(Z80_INPUT_LINE_BUSRQ, m_z80_busrq ? CLEAR_LINE : ASSERT_LINE); + if (m_z80_reset) + m_opn->reset(); + if (m_z80_reset || !m_z80_busrq) + m_md_68k_sound_view.select(0); + else + m_md_68k_sound_view.disable(); +} + +void magistr16_state::md_68k_z80_map(address_map &map) +{ + map(0x0000, 0x3fff).lrw16( + NAME([this] (offs_t offset, u16 mem_mask) { + u16 res = m_sound_program[(offset << 1) ^ 1] | (m_sound_program[offset << 1] << 8); + return res; + }), + NAME([this] (offs_t offset, u16 data, u16 mem_mask) { + if (!ACCESSING_BITS_0_7) + { + m_sound_program[(offset<<1)] = (data & 0xff00) >> 8; + } + else if (!ACCESSING_BITS_8_15) + { + m_sound_program[(offset<<1) ^ 1] = (data & 0x00ff); + } + else + { + m_sound_program[(offset<<1)] = (data & 0xff00) >> 8; + } + }) + ); + map(0x4000, 0x4003).mirror(0x1ffc).rw(m_opn, FUNC(ym_generic_device::read), FUNC(ym_generic_device::write)); +} + +void magistr16_state::md_z80_map(address_map &map) +{ + map(0x0000, 0x1fff).lrw8( + NAME([this] (offs_t offset) { + return m_sound_program[offset]; + }), + NAME([this] (offs_t offset, u8 data) { + m_sound_program[offset] = data; + }) + ); + map(0x4000, 0x4003).mirror(0x1ffc).rw(m_opn, FUNC(ym_generic_device::read), FUNC(ym_generic_device::write)); + map(0x6000, 0x60ff).lw8(NAME([this] (offs_t offset, u8 data) { + m_z80_main_address = ((m_z80_main_address >> 1) | ((data & 1) << 23)) & 0xff8000; + })); + map(0x7f00, 0x7f1f).m(m_md_vdp, FUNC(ym7101_device::if8_map)); + map(0x8000, 0xffff).lrw8( + NAME([this] (offs_t offset) { + address_space &space68k = m_md68kcpu->space(); + u8 ret = space68k.read_byte(m_z80_main_address + offset); + return ret; + + }), + NAME([this] (offs_t offset, u8 data) { + address_space &space68k = m_md68kcpu->space(); + space68k.write_byte(m_z80_main_address + offset, data); + }) + ); +} + +void magistr16_state::md_z80_io(address_map &map) +{ + map.global_mask(0xff); + map.unmap_value_high(); + // juuouki reads $bf in irq service (SMS VDP left-over?) + map(0x00, 0xff).nopr(); + // TODO: SMS mode thru cart +} + +static INPUT_PORTS_START( magistr16 ) +INPUT_PORTS_END + +void magistr16_state::machine_start() +{ + m_sound_program = std::make_unique<u8[]>(0x2000); + + save_pointer(NAME(m_sound_program), 0x2000); + + save_item(NAME(m_z80_reset)); + save_item(NAME(m_z80_busrq)); + save_item(NAME(m_z80_main_address)); +} + +void magistr16_state::machine_reset() +{ + m_z80_reset = true; + flush_z80_state(); +} + +static void isa_internal_devices(device_slot_interface &device) +{ + device.option_add("w83977tf", W83977TF); +} + +void magistr16_state::winbond_superio_config(device_t *device) +{ + // TODO: Winbond w83977f +// w83977tf_device &fdc = *downcast<w83977tf_device *>(device); +// fdc.set_sysopt_pin(1); +// fdc.gp20_reset().set_inputline(":maincpu", INPUT_LINE_RESET); +// fdc.gp25_gatea20().set_inputline(":maincpu", INPUT_LINE_A20); +// fdc.irq1().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq1_w)); +// fdc.irq8().set(":pci:07.0", FUNC(i82371eb_isa_device::pc_irq8n_w)); +// fdc.txd1().set(":serport0", FUNC(rs232_port_device::write_txd)); +// fdc.ndtr1().set(":serport0", FUNC(rs232_port_device::write_dtr)); +// fdc.nrts1().set(":serport0", FUNC(rs232_port_device::write_rts)); +// fdc.txd2().set(":serport1", FUNC(rs232_port_device::write_txd)); +// fdc.ndtr2().set(":serport1", FUNC(rs232_port_device::write_dtr)); +// fdc.nrts2().set(":serport1", FUNC(rs232_port_device::write_rts)); +} + + +void magistr16_state::magistr16(machine_config &config) +{ + // PAL clock + constexpr XTAL md_master_xtal(53.203424_MHz_XTAL); + + M68000(config, m_md68kcpu, md_master_xtal / 7); + m_md68kcpu->set_addrmap(AS_PROGRAM, &magistr16_state::md_68k_map); + m_md68kcpu->set_addrmap(m68000_base_device::AS_CPU_SPACE, &magistr16_state::md_cpu_space_map); + // disallow TAS (gargoyle, cliffh, exmutant) + m_md68kcpu->set_tas_write_callback(NAME([] (offs_t offset, u8 data) { })); + + Z80(config, m_mdz80cpu, md_master_xtal / 15); + m_mdz80cpu->set_addrmap(AS_PROGRAM, &magistr16_state::md_z80_map); + m_mdz80cpu->set_addrmap(AS_IO, &magistr16_state::md_z80_io); + + SCREEN(config, m_mdscreen, SCREEN_TYPE_RASTER); + m_mdscreen->set_raw(md_master_xtal / 8, 423, 0, 320, 312, 0, 240); + m_mdscreen->set_screen_update(m_md_vdp, FUNC(ym7101_device::screen_update)); + + YM7101(config, m_md_vdp, md_master_xtal / 4); + m_md_vdp->set_mclk(md_master_xtal); + m_md_vdp->set_screen(m_mdscreen); + // TODO: actual DTACK + // TODO: accessing 68k bus from x86, defers access? + m_md_vdp->dtack_cb().set_inputline(m_md68kcpu, INPUT_LINE_HALT); + m_md_vdp->mreq_cb().set([this] (offs_t offset) { + // TODO: can't read outside of base cart and RAM + //printf("%06x\n", offset); + address_space &space68k = m_md68kcpu->space(); + u16 ret = space68k.read_word(offset, 0xffff); + + return ret; + }); + m_md_vdp->vint_cb().set_inputline(m_md68kcpu, 6); + m_md_vdp->hint_cb().set_inputline(m_md68kcpu, 4); + m_md_vdp->sint_cb().set_inputline(m_mdz80cpu, INPUT_LINE_IRQ0); + m_md_vdp->add_route(ALL_OUTPUTS, "md_speaker", 0.50, 0); + m_md_vdp->add_route(ALL_OUTPUTS, "md_speaker", 0.50, 1); + + auto &hl(INPUT_MERGER_ANY_HIGH(config, "hl")); + // TODO: gated thru VDP + hl.output_handler().set_inputline(m_md68kcpu, 2); + + MEGADRIVE_IO_PORT(config, m_md_ioports[0], 0); + m_md_ioports[0]->hl_handler().set("hl", FUNC(input_merger_device::in_w<0>)); + + MEGADRIVE_IO_PORT(config, m_md_ioports[1], 0); + m_md_ioports[1]->hl_handler().set("hl", FUNC(input_merger_device::in_w<1>)); + + MEGADRIVE_IO_PORT(config, m_md_ioports[2], 0); + m_md_ioports[2]->hl_handler().set("hl", FUNC(input_merger_device::in_w<2>)); + + for (int N = 0; N < 3; N++) + { + SMS_CONTROL_PORT(config, m_md_ctrl_ports[N], sms_control_port_devices, N != 2 ? SMS_CTRL_OPTION_MD_PAD : nullptr); + m_md_ctrl_ports[N]->th_handler().set(m_md_ioports[N], FUNC(megadrive_io_port_device::th_w)); + m_md_ctrl_ports[N]->set_screen(m_mdscreen); + + m_md_ioports[N]->set_in_handler(m_md_ctrl_ports[N], FUNC(sms_control_port_device::in_r)); + m_md_ioports[N]->set_out_handler(m_md_ctrl_ports[N], FUNC(sms_control_port_device::out_w)); + } + + MEGADRIVE_CART_SLOT(config, m_md_cart, md_master_xtal / 7, megadrive_cart_options, nullptr).set_must_be_loaded(true); + + SPEAKER(config, "md_speaker", 2).front(); + + YM2612(config, m_opn, md_master_xtal / 7); + m_opn->add_route(0, "md_speaker", 0.50, 0); + m_opn->add_route(1, "md_speaker", 0.50, 1); + + SOFTWARE_LIST(config, "cart_list").set_original("megadriv").set_filter("PAL"); + + // expansion bus overlays + // TODO: Winbond W29C020C really + FUJITSU_29F016A(config, "flash"); + + // FIXME: determine ISA bus clock + ISA16(config, m_isabus, 0); + m_isabus->set_custom_spaces(); + ISA16_SLOT(config, "board1", 0, "isabus", isa_internal_devices, "w83977tf", true).set_option_machine_config("w83977tf", winbond_superio_config); +} + + +ROM_START( magistr16 ) + ROM_REGION( 0x200000, "flash", ROMREGION_ERASEFF ) + // both looks incomplete, to be checked if any vital data is missing here. + ROM_SYSTEM_BIOS(0, "212a", "2.12a") + ROMX_LOAD( "bios-dump_2.12a-core.bin", 0x00000, 0x4800, BAD_DUMP CRC(ed7b2d61) SHA1(89aa09023262e5daae7f62c2c46b0a6b2228113f), ROM_BIOS(0)) + ROM_SYSTEM_BIOS(1, "209i", "2.09i") + ROMX_LOAD( "bios-dump_2.09i-core.bin", 0x00000, 0x4c00, BAD_DUMP CRC(c63d54b4) SHA1(8dad327fd5464341c55a9c12225b46c440c46507), ROM_BIOS(1)) + +ROM_END + +} // anonymous namespace + +CONS( 2001, magistr16, 0, 0, magistr16, magistr16, magistr16_state, empty_init, "New Game", "Magistr16 (Russia)", MACHINE_NOT_WORKING ) + |
