diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua index 530315717eb..d1cad02d63d 100644 --- a/scripts/target/mame/arcade.lua +++ b/scripts/target/mame/arcade.lua @@ -1296,6 +1296,7 @@ files { MAME_DIR .. "src/mame/drivers/liberatr.cpp", MAME_DIR .. "src/mame/includes/liberatr.h", MAME_DIR .. "src/mame/video/liberatr.cpp", + MAME_DIR .. "src/mame/drivers/marblmd2.cpp", MAME_DIR .. "src/mame/drivers/mediagx.cpp", MAME_DIR .. "src/mame/drivers/metalmx.cpp", MAME_DIR .. "src/mame/includes/metalmx.h", diff --git a/src/mame/arcade.flt b/src/mame/arcade.flt index f41a4f14f0a..ba7bb9134dc 100644 --- a/src/mame/arcade.flt +++ b/src/mame/arcade.flt @@ -747,6 +747,7 @@ majorpkr.cpp malzak.cpp manohman.cpp mappy.cpp +marblmd2.cpp marineb.cpp marinedt.cpp mario.cpp diff --git a/src/mame/drivers/marblmd2.cpp b/src/mame/drivers/marblmd2.cpp new file mode 100644 index 00000000000..05005fe2c64 --- /dev/null +++ b/src/mame/drivers/marblmd2.cpp @@ -0,0 +1,387 @@ +// license:BSD-3-Clause +// copyright-holders:David Haywood + +/* + Supported Games: + + Marble Madness II (prototype) + + Not Dumped: + + Marble Man: Marble Madness II (earlier version of the game before it was changed to use Joysticks) + + ------------------------------------------- + + TODO: + - Issues in service mode (eg. RAM check fails) could just be prototype issues + - EEPROM hookup + - verify XTALs, clocks, volume balance, dipswitches etc. + + Non-Bugs: + - objects appear over text boxes during scroll before levels, verified to happen on hardware + + +*/ + +#include "emu.h" + +#include "audio/atarijsa.h" +#include "video/atarimo.h" +#include "video/atarivad.h" + +#include "cpu/m68000/m68000.h" + +#include "emupal.h" +#include "screen.h" +#include "speaker.h" +#include "tilemap.h" + +class marblmd2_state : public driver_device +{ +public: + marblmd2_state(const machine_config &mconfig, device_type type, const char *tag) : + driver_device(mconfig, type, tag), + m_maincpu(*this, "maincpu"), + m_screen(*this, "screen"), + m_gfxdecode(*this, "gfxdecode"), + m_jsa(*this, "jsa"), + m_vad(*this, "vad"), + m_mob(*this, "vad:mob") + { } + + void marblmd2(machine_config &config); + +protected: + virtual void video_start() override; + virtual void machine_start() override; + +private: + required_device m_maincpu; + required_device m_screen; + required_device m_gfxdecode; + required_device m_jsa; + required_device m_vad; + required_device m_mob; + + void marblmd2_map(address_map &map); + + void latch_w(offs_t offset, uint16_t data, uint16_t mem_mask = ~0); + + TILE_GET_INFO_MEMBER(get_playfield_tile_info); + + uint32_t screen_update_marblmd2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + + bitmap_ind16 m_tempbitmap; + uint16_t m_latch_data = 0U; + + static const atari_motion_objects_config s_mob_config; +}; + +void marblmd2_state::machine_start() +{ + save_item(NAME(m_latch_data)); +} + +void marblmd2_state::video_start() +{ + m_screen->register_screen_bitmap(m_tempbitmap); + m_tempbitmap.fill(0); +} + +uint32_t marblmd2_state::screen_update_marblmd2(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +{ + // start drawing + m_vad->mob().draw_async(cliprect); + + // draw the playfield + bitmap_ind8 &priority_bitmap = screen.priority(); + priority_bitmap.fill(0, cliprect); + m_vad->playfield().draw(screen, m_tempbitmap, cliprect, 0, 0x00); + bitmap_ind16 &mobitmap = m_vad->mob().bitmap(); + + for (int y = cliprect.min_y; y <= cliprect.max_y; y++) + { + uint16_t const *const src = &m_tempbitmap.pix(y); + uint16_t *const dst = &bitmap.pix(y); + + // top bit of the gfxdata appears to be priority, so we don't want it in the render bitmap + for (int x = cliprect.min_x; x <= cliprect.max_x; x++) + { + dst[x] = src[x] & 0x007f; + } + } + + for (const sparse_dirty_rect *rect = m_vad->mob().first_dirty_rect(cliprect); rect != nullptr; rect = rect->next()) + { + for (int y = rect->top(); y <= rect->bottom(); y++) + { + uint16_t const *const pf2 = &m_tempbitmap.pix(y); + uint16_t const *const mo = &mobitmap.pix(y); + uint16_t *const pf = &bitmap.pix(y); + for (int x = rect->left(); x <= rect->right(); x++) + { + if (mo[x] != 0xffff) + { + if (pf2[x] & 0x80) // check against top bit of temp pf render + { + if (mo[x] & 0x80) + pf[x] = mo[x]; + } + else + { + pf[x] = mo[x] | 0x80; + } + + } + } + } + } + + return 0; +} + +// not verified +void marblmd2_state::latch_w(offs_t offset, uint16_t data, uint16_t mem_mask) +{ + COMBINE_DATA(&m_latch_data); + + if (m_latch_data & 0x0010) + m_jsa->soundcpu().set_input_line(INPUT_LINE_RESET, CLEAR_LINE); + else + m_jsa->soundcpu().set_input_line(INPUT_LINE_RESET, ASSERT_LINE); +} + + +void marblmd2_state::marblmd2_map(address_map& map) +{ + map(0x000000, 0x07ffff).rom(); + + map(0x600000, 0x600001).portr("600000"); + map(0x600002, 0x600003).portr("600002"); + map(0x600010, 0x600011).portr("600010"); + map(0x600012, 0x600013).portr("600012"); + map(0x600020, 0x600021).portr("600020"); + map(0x600030, 0x600031).r(m_jsa, FUNC(atari_jsa_iii_device::main_response_r)); + map(0x600040, 0x600041).w(m_jsa, FUNC(atari_jsa_iii_device::main_command_w)); + + map(0x600050, 0x600051).w(FUNC(marblmd2_state::latch_w)); + + map(0x601000, 0x6013ff).ram(); // some kind of NVRAM? + + map(0x607000, 0x607001).nopw(); + + map(0x7c0000, 0x7c03ff).rw("palette", FUNC(palette_device::read8), FUNC(palette_device::write8)).umask16(0xff00).share("palette"); + map(0x7cffc0, 0x7cffff).ram().rw(m_vad, FUNC(atari_vad_device::control_read), FUNC(atari_vad_device::control_write)); + + map(0x7d0000, 0x7d7fff).ram(); + map(0x7d8000, 0x7d9fff).ram().w(m_vad, FUNC(atari_vad_device::playfield_latched_lsb_w)).share("vad:playfield"); + map(0x7da000, 0x7dbeff).ram().share("vad:mob"); + map(0x7dbf00, 0x7dbf7f).ram().share("vad:eof"); + map(0x7dbf80, 0x7dbfff).ram().share("vad:mob:slip"); + map(0x7f8000, 0x7fbfff).ram(); +} + +static INPUT_PORTS_START( marblmd2 ) + + PORT_START("600000") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(3) // also acts as START3 + PORT_BIT( 0x00fe, IP_ACTIVE_LOW, IPT_UNKNOWN ) + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(1) // also acts as START1 + PORT_BIT( 0xfe00, IP_ACTIVE_LOW, IPT_UNKNOWN ) + + PORT_START("600002") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_UNKNOWN ) // acts as a 'freeze' input, probably not connected + PORT_BIT( 0x00fe, IP_ACTIVE_LOW, IPT_UNKNOWN ) + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_PLAYER(2) // also acts as START2 + PORT_BIT( 0xfe00, IP_ACTIVE_LOW, IPT_UNKNOWN ) + + PORT_START("600012") // assume at least one bank of 8 dips lives here, but could be something else + PORT_DIPNAME( 0x0001, 0x0001, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0x0001, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x0000, DEF_STR( On ) ) + PORT_DIPNAME( 0x0002, 0x0002, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0x0002, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x0000, DEF_STR( On ) ) + PORT_DIPNAME( 0x0004, 0x0004, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0x0004, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x0000, DEF_STR( On ) ) + PORT_DIPNAME( 0x0008, 0x0008, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0x0008, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x0000, DEF_STR( On ) ) + PORT_DIPNAME( 0x0010, 0x0010, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0x0010, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x0000, DEF_STR( On ) ) + PORT_DIPNAME( 0x0020, 0x0020, "Number of Players (Test Mode)" ) // this one controls 'number of players' in Control Test + PORT_DIPSETTING( 0x0000, "2" ) + PORT_DIPSETTING( 0x0020, "3" ) + PORT_DIPNAME( 0x0040, 0x0040, DEF_STR( Unknown ) ) + PORT_DIPSETTING( 0x0040, DEF_STR( Off ) ) + PORT_DIPSETTING( 0x0000, DEF_STR( On ) ) + PORT_DIPNAME( 0x0080, 0x0080, "Number of Players (Game)" ) + PORT_DIPSETTING( 0x0000, "2" ) + PORT_DIPSETTING( 0x0080, "3" ) + PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN ) + + + PORT_START("600020") + PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(2) + PORT_BIT( 0x0002, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(2) + PORT_BIT( 0x0004, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(2) + PORT_BIT( 0x0008, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(2) + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(1) + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(1) + PORT_BIT( 0x0040, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(1) + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(1) + PORT_BIT( 0x0100, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT ) PORT_PLAYER(3) + PORT_BIT( 0x0200, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT ) PORT_PLAYER(3) + PORT_BIT( 0x0400, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN ) PORT_PLAYER(3) + PORT_BIT( 0x0800, IP_ACTIVE_LOW, IPT_JOYSTICK_UP ) PORT_PLAYER(3) + PORT_BIT( 0xf000, IP_ACTIVE_LOW, IPT_UNKNOWN ) + + PORT_START("600010") + PORT_BIT( 0x000f, IP_ACTIVE_LOW, IPT_UNUSED ) + PORT_BIT( 0x0010, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_ATARI_JSA_SOUND_TO_MAIN_READY("jsa") // Input buffer full + PORT_BIT( 0x0020, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_ATARI_JSA_MAIN_TO_SOUND_READY("jsa") // Output buffer full + PORT_SERVICE( 0x0040, IP_ACTIVE_LOW ) + PORT_BIT( 0x0080, IP_ACTIVE_LOW, IPT_CUSTOM ) PORT_VBLANK("screen") + PORT_BIT( 0xff00, IP_ACTIVE_LOW, IPT_UNKNOWN ) + +INPUT_PORTS_END + + +static const gfx_layout pflayout = +{ + 8,8, + RGN_FRAC(1,2), + 8, + { 0,1,2,3,4,5,6,7 }, + { 0,RGN_FRAC(1,2) + 0, 8,RGN_FRAC(1,2) + 8, 16,RGN_FRAC(1,2) + 16,24,RGN_FRAC(1,2) + 24 }, + { 0 * 32, 1 * 32, 2 * 32, 3 * 32, 4 * 32, 5 * 32, 6 * 32, 7 * 32 }, + 8 * 32 +}; + +static const gfx_layout molayout = +{ + 8,8, + RGN_FRAC(1,2), + 4, + { 0, 1, 2, 3 }, + { RGN_FRAC(1,2) + 0, RGN_FRAC(1,2) + 4, 0, 4, RGN_FRAC(1,2) + 8, RGN_FRAC(1,2) + 12, 8, 12 }, + { 0 * 16, 1 * 16, 2 * 16, 3 * 16, 4 * 16, 5 * 16, 6 * 16, 7 * 16 }, + 16 * 8 +}; + + +static GFXDECODE_START( gfx_mm2 ) + GFXDECODE_ENTRY( "gfx1", 0, pflayout, 0x0, 1 ) + GFXDECODE_ENTRY( "gfx2", 0, molayout, 0x0, 0x10 ) +GFXDECODE_END + + +const atari_motion_objects_config marblmd2_state::s_mob_config = +{ + 1, // index to which gfx system + 1, // number of motion object banks + 1, // are the entries linked? + 0, // are the entries split? + 0, // render in reverse order? + 0, // render in swapped X/Y order? + 0, // does the neighbor bit affect the next object? + 8, // pixels per SLIP entry (0 for no-slip) + 0, // pixel offset for SLIPs + 0, // maximum number of links to visit/scanline (0=all) + + 0x000, // base palette entry + 0x100, // maximum number of colors + 0, // transparent pen index + + {{ 0x03ff,0,0,0 }}, // mask for the link + {{ 0,0x7fff,0,0 }}, // mask for the code index + {{ 0,0,0x000f,0 }}, // mask for the color + {{ 0,0,0xff80,0 }}, // mask for the X position + {{ 0,0,0,0xff80 }}, // mask for the Y position + {{ 0,0,0,0x0070 }}, // mask for the width, in tiles + {{ 0,0,0,0x0007 }}, // mask for the height, in tiles + {{ 0,0x8000,0,0 }}, // mask for the horizontal flip + {{ 0 }}, // mask for the vertical flip + {{ 0,0,0x0070,0 }}, // mask for the priority + {{ 0 }}, // mask for the neighbor + {{ 0 }}, // mask for absolute coordinates + + {{ 0 }}, // mask for the special value + 0, // resulting value to indicate "special" +}; + +TILE_GET_INFO_MEMBER(marblmd2_state::get_playfield_tile_info) +{ + uint16_t data1 = m_vad->playfield().basemem_read(tile_index); + int code = data1 & 0x3fff; + tileinfo.set(0, code, 0, (data1 >> 15) & 1); +} + +// XTALs are guessed based on other Atari titles of the same period +void marblmd2_state::marblmd2(machine_config &config) +{ + M68000(config, m_maincpu, XTAL(14'318'181)/2); + m_maincpu->set_addrmap(AS_PROGRAM, &marblmd2_state::marblmd2_map); + + GFXDECODE(config, m_gfxdecode, "palette", gfx_mm2); + + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_video_attributes(VIDEO_UPDATE_BEFORE_VBLANK); + m_screen->set_raw(14.318181_MHz_XTAL/2, 456, 0, 336, 262, 0, 240); + m_screen->set_screen_update(FUNC(marblmd2_state::screen_update_marblmd2)); + m_screen->set_palette("palette"); + + PALETTE(config, "palette").set_format(palette_device::IRGB_1555, 256).set_membits(8); + + ATARI_VAD(config, m_vad, 0, m_screen); + m_vad->scanline_int_cb().set_inputline(m_maincpu, M68K_IRQ_4); + m_vad->set_xoffsets(4, 4); + + ATARI_MOTION_OBJECTS(config, m_mob, 0, m_screen, marblmd2_state::s_mob_config).set_gfxdecode("gfxdecode"); + + TILEMAP(config, "vad:playfield", "gfxdecode", 2, 8, 8, TILEMAP_SCAN_COLS, 64, 64).set_info_callback(FUNC(marblmd2_state::get_playfield_tile_info)); + + // sound hardware + SPEAKER(config, "mono").front_center(); + + ATARI_JSA_III(config, m_jsa, 0); + m_jsa->main_int_cb().set_inputline(m_maincpu, M68K_IRQ_6); + m_jsa->test_read_cb().set_ioport("600010").bit(6); + m_jsa->add_route(ALL_OUTPUTS, "mono", 1.0); +} + +ROM_START( marblmd2 ) + ROM_REGION( 0x80000, "maincpu", 0 ) // 68000 Code + ROM_LOAD16_BYTE( "rom0l.18c", 0x00001, 0x20000, CRC(a4db40d9) SHA1(ae8313c9bb513143472347a7705bec33783bad7e) ) + ROM_LOAD16_BYTE( "rom0h.20c", 0x00000, 0x20000, CRC(d1a17d67) SHA1(7ac434858fa94e4bb4bb7d0603f699667494aa0d) ) + ROM_LOAD16_BYTE( "rom1l.18e", 0x40001, 0x20000, CRC(b6fb08b5) SHA1(0ce9c1a5d70133ffc879cfe548646c04de371e13) ) + ROM_LOAD16_BYTE( "rom1h.20e", 0x40000, 0x20000, CRC(b2a361a8) SHA1(b7c58404642a494532597cceb946463e3a6f56b3) ) + + ROM_REGION( 0x10000, "jsa:cpu", 0 ) // 6502 code + ROM_LOAD( "aud0.12c", 0x00000, 0x10000, CRC(89a8d90a) SHA1(cd73483d0bcfe2c8134d005c4417975f9a2cb658) ) + + ROM_REGION( 0x100000, "gfx1", 0 ) + ROM_LOAD( "pf0l.3p", 0x00000, 0x20000, CRC(a4fe377a) SHA1(a8a1a8027da778e5ad406a65814eec999b0f81af) ) + ROM_LOAD( "pf1l.3m", 0x20000, 0x20000, CRC(5dc7aaa8) SHA1(4fb815e9bcf6bcdf1b7976a3dea2b6d1dd6a8f6b) ) + ROM_LOAD( "pf2l.3k", 0x40000, 0x20000, CRC(0c7c5f74) SHA1(26f1d36f70f4e8354537d0d67764a1c9be35e8f7) ) + ROM_LOAD( "pf3l.3j", 0x60000, 0x20000, CRC(0a780429) SHA1(a9d7d564507c31dafc448726b04293d6a582cff5) ) + ROM_LOAD( "pf0h.1p", 0x80000, 0x20000, CRC(a6297a83) SHA1(ffe9ea41d1ba7bb3d0260f3fcf0e970112098d46) ) + ROM_LOAD( "pf1h.1m", 0xa0000, 0x20000, CRC(5b40f1bb) SHA1(cf0de8679ab0dd9460324ce72b4bfac029591506) ) + ROM_LOAD( "pf2h.1k", 0xc0000, 0x20000, CRC(18323df9) SHA1(9c4add4733bcfe7202b53d86f1bca4b9d207e22a) ) + ROM_LOAD( "pf3h.1j", 0xe0000, 0x20000, CRC(05d86ef8) SHA1(47eefd7112a3a3be16f0b4496cf034c8f7a69b1b) ) + + ROM_REGION( 0x80000, "gfx2", 0 ) + ROM_LOAD( "mo0l.7p", 0x00000, 0x20000, CRC(950d95a3) SHA1(3f38da7b6eeaa87cc84b98c9d535468b0c060f6d) ) + ROM_LOAD( "mo1l.10p", 0x20000, 0x20000, CRC(b62b6ebf) SHA1(3781cd81780c10cd245871bb8f7b6260f7bb53b7) ) + ROM_LOAD( "mo0h.12p", 0x40000, 0x20000, CRC(e47d92b0) SHA1(7953e8342450c02408e4d90f132144d55de2f491) ) + ROM_LOAD( "mo1h.14p", 0x60000, 0x20000, CRC(317a03fb) SHA1(23a7cfe7c5601c858e8b346de31441788c7a8e97) ) + + // loading based on batman, there are 2 unpopulated positions on the PCB + ROM_REGION( 0x80000, "jsa:oki1", 0 ) + ROM_LOAD( "sound.19e", 0x00000, 0x20000, CRC(e916bef7) SHA1(e07ddc8a3e1656d7307b767e692cf4a575ca47a3) ) + ROM_LOAD( "sound.12e", 0x60000, 0x20000, CRC(bab2f8e5) SHA1(bbe2d693d40e5eeba315fe7b6380a2030b66f23e) ) +ROM_END + +GAME( 1991, marblmd2, 0, marblmd2, marblmd2, marblmd2_state, empty_init, ROT0, "Atari Games", "Marble Madness II (prototype)", MACHINE_SUPPORTS_SAVE ) diff --git a/src/mame/mame.lst b/src/mame/mame.lst index 9df3c8f97dd..c5f891ff962 100644 --- a/src/mame/mame.lst +++ b/src/mame/mame.lst @@ -20864,6 +20864,9 @@ todruaga // (c) 1984 todruagao // (c) 1984 todruagas // (c) 1984 pcb manufactured by Sidam +@source:marblmd2.cpp +marblmd2 + @source:marineb.cpp bcruzm12 // (c) 1983 Sigma Ent. Inc. changes // (c) 1982 Orca