From cf5985de0f952995861bf278ec72bf08e15f63d7 Mon Sep 17 00:00:00 2001 From: Vas Crabb Date: Mon, 19 Apr 2021 00:24:35 +1000 Subject: [PATCH] subhuntr.cpp: Start to flesh out the skeleton - still nowhere near close to working. --- src/mame/drivers/subhuntr.cpp | 358 +++++++++++++++++++++++++++------- 1 file changed, 284 insertions(+), 74 deletions(-) diff --git a/src/mame/drivers/subhuntr.cpp b/src/mame/drivers/subhuntr.cpp index efa2e43e19c..334a1604971 100644 --- a/src/mame/drivers/subhuntr.cpp +++ b/src/mame/drivers/subhuntr.cpp @@ -1,20 +1,30 @@ // license:BSD-3-Clause -// copyright-holders:David Haywood +// copyright-holders:Vas Crabb /* -QTY Type clock position function -2x 2636 Programmable Video Interface -1x 2650 OSC/2 = 1.7897725 MHz 8-bit Microprocessor - main -1x oscillator 3.579545 MHz +QTY Type clock position function +1x 2621 B8 Universal Sync Generator (PAL) +3x 2636 H5,L5,N5 Programmable Video Interface +1x 2650 OSC/2 = 1.7897725 MHz 8-bit Microprocessor - main +1x oscillator 3.579545 MHz ROMs QTY Type position status -4x 2708 6F,6H,6L,6N dumped -1x N82S115 2B dumped +4x 2708 F7,H7,L7,N7 dumped +1x N82S115 B2 dumped RAMs QTY Type position -2x 2101 +4x 2112-2 G8,G9,H8,H9,L8,L9,M8,M9 +2x 2101-1 B4,C4 + +Video uses approximate PAL timings derived from a 3.579MHz crystal +using a 2621 USG (227 clocks per line, 43-clock HBLANK, 312 lines, +43-line VBLANK). This gives a resolution of 184*269, which is used for +the 2636 PVIs. The clock is doubled to 7.15MHz for the text layer, +giving a resolution of 368*269. Screen raw parameters are derived by +considering pixel/line zero to be the first period after the end of the +horizontal/vertical sync pulse. */ @@ -24,55 +34,241 @@ QTY Type position #include "emupal.h" #include "screen.h" #include "speaker.h" +#include "tilemap.h" +namespace { + class subhuntr_state : public driver_device { public: - subhuntr_state(const machine_config &mconfig, device_type type, const char *tag) + subhuntr_state(machine_config const &mconfig, device_type type, char const *tag) : driver_device(mconfig, type, tag) , m_maincpu(*this, "maincpu") + , m_screen(*this, "screen") + , m_pvi1_h5(*this, "pvi1") + , m_pvi2_l5(*this, "pvi2") + , m_pvi3_n5(*this, "pvi3") + , m_gfx(*this, "gfxdecode") + , m_txtram(*this, "txtram") + , m_intreq_cnf(*this, "JMP_1_7") { } void subhuntr(machine_config &config); protected: + enum { TIMER_VIDEO }; + + virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override; + virtual void machine_start() override; virtual void machine_reset() override; - virtual void video_start() override; private: - INTERRUPT_GEN_MEMBER(subhuntr_interrupt); + void txtram_w(offs_t offset, u8 data); + u8 intack_r(); - void subhuntr_palette(palette_device &palette) const; - uint32_t screen_update_subhuntr(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); + DECLARE_WRITE_LINE_MEMBER(pvi1_intreq_w); + DECLARE_WRITE_LINE_MEMBER(pvi2_intreq_w); + DECLARE_WRITE_LINE_MEMBER(pvi3_intreq_w); + + void palette_init(palette_device &palette) const; + u32 screen_update(screen_device &screen, bitmap_ind16 &bitmap, rectangle const &cliprect); + TILE_GET_INFO_MEMBER(tile_info); + TIMER_CALLBACK_MEMBER(video_callback); + + void set_intreq(unsigned bit, u8 mask); + void clr_intreq(unsigned bit); - void subhuntr_data_map(address_map &map); - void subhuntr_io_map(address_map &map); void subhuntr_map(address_map &map); - required_device m_maincpu; + required_device m_maincpu; + required_device m_screen; + required_device m_pvi1_h5; + required_device m_pvi2_l5; + required_device m_pvi3_n5; + required_device m_gfx; + required_shared_ptr m_txtram; + + required_ioport m_intreq_cnf; + + u8 m_intreqs = 0; + + bitmap_ind16 m_bitmap; + emu_timer *m_video_timer = nullptr; + tilemap_t *m_tilemap = nullptr; }; /*************************************************************************** - Video + Machine implementation ***************************************************************************/ -void subhuntr_state::subhuntr_palette(palette_device &palette) const +void subhuntr_state::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) +{ + switch (id) + { + case TIMER_VIDEO: + video_callback(ptr, param); + break; + default: + throw emu_fatalerror("Unknown id in subhuntr_state::device_timer"); + } +} + +void subhuntr_state::machine_start() +{ + m_video_timer = timer_alloc(TIMER_VIDEO); + m_tilemap = &machine().tilemap().create(*m_gfx, tilemap_get_info_delegate(*this, FUNC(subhuntr_state::tile_info)), TILEMAP_SCAN_ROWS, 8, 8, 32, 8); + m_tilemap->set_transparent_pen(0); + + save_item(NAME(m_intreqs)); + + m_screen->register_screen_bitmap(m_bitmap); + + m_video_timer->adjust(m_screen->time_until_pos(1, 0)); +} + +void subhuntr_state::machine_reset() { } -uint32_t subhuntr_state::screen_update_subhuntr(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) +void subhuntr_state::txtram_w(offs_t offset, u8 data) { + if (m_txtram[offset] != data) + { + m_txtram[offset] = data; + m_tilemap->mark_tile_dirty(offset); + } +} + +u8 subhuntr_state::intack_r() +{ + unsigned const source = count_leading_zeros(m_intreqs) - 24; + u8 const vector = ((m_intreq_cnf->read() & 0x01) ? 0x91 : 0x11) | (source << 1); + switch (source) + { + case 1: + m_pvi1_h5->write_intack(ASSERT_LINE); + m_pvi1_h5->write_intack(CLEAR_LINE); + break; + case 2: + m_pvi2_l5->write_intack(ASSERT_LINE); + m_pvi2_l5->write_intack(CLEAR_LINE); + break; + case 4: + m_pvi3_n5->write_intack(ASSERT_LINE); + m_pvi3_n5->write_intack(CLEAR_LINE); + break; + case 3: + case 5: + case 6: + clr_intreq(7 - source); + break; + } + return vector; +} + +WRITE_LINE_MEMBER(subhuntr_state::pvi1_intreq_w) +{ + if (state) set_intreq(6, 0x02); + else clr_intreq(6); +} + +WRITE_LINE_MEMBER(subhuntr_state::pvi2_intreq_w) +{ + if (state) set_intreq(5, 0x04); + else clr_intreq(5); +} + +WRITE_LINE_MEMBER(subhuntr_state::pvi3_intreq_w) +{ + if (state) set_intreq(3, 0x08); + else clr_intreq(3); +} + +void subhuntr_state::palette_init(palette_device &palette) const +{ + palette.set_pen_color(0, 0x00, 0x00, 0x00); + palette.set_pen_color(1, 0x00, 0xff, 0x00); + palette.set_pen_color(2, 0x00, 0x00, 0x00); + palette.set_pen_color(3, 0x00, 0xff, 0xff); + palette.set_pen_color(4, 0x00, 0x00, 0x00); + palette.set_pen_color(5, 0xff, 0xff, 0x00); + palette.set_pen_color(6, 0x00, 0x00, 0x00); + palette.set_pen_color(7, 0xff, 0xff, 0xff); +} + +u32 subhuntr_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, rectangle const &cliprect) +{ + copybitmap(bitmap, m_bitmap, 0, 0, 0, 0, cliprect); return 0; } -void subhuntr_state::video_start() +TILE_GET_INFO_MEMBER(subhuntr_state::tile_info) { + u8 const tile = m_txtram[tile_index]; + u8 const code = tile & 0x3f; + u8 const colour = (tile >> 6) & 0x03; + tileinfo.set(0, code, colour, 0); +} + +TIMER_CALLBACK_MEMBER(subhuntr_state::video_callback) +{ + int const y = m_screen->vpos(); + + if (!y) + { + m_pvi1_h5->render_first_line(); + m_pvi2_l5->render_first_line(); + m_pvi3_n5->render_first_line(); + } + else + { + m_pvi1_h5->render_next_line(); + m_pvi2_l5->render_next_line(); + m_pvi3_n5->render_next_line(); + } + + u16 const *src1 = &m_pvi1_h5->bitmap().pix(y); + u16 const *src2 = &m_pvi2_l5->bitmap().pix(y); + u16 const *src3 = &m_pvi3_n5->bitmap().pix(y); + u16 *dst = &m_bitmap.pix(y); + for (unsigned x = 0; x < m_bitmap.width(); x++, src1++, src2++, src3++, dst++) + { + u16 const pvi_val = S2636_PIXEL_COLOR(*src1 | *src2 | *src3); + *dst = pvi_val; + if (S2636_IS_PIXEL_DRAWN(*src1) && S2636_IS_PIXEL_DRAWN(*src2)) set_intreq(4, 0x10); + if (S2636_IS_PIXEL_DRAWN(*src1) && S2636_IS_PIXEL_DRAWN(*src3)) set_intreq(2, 0x20); + if (S2636_IS_PIXEL_DRAWN(*src2) && S2636_IS_PIXEL_DRAWN(*src3)) set_intreq(1, 0x40); + } + + m_video_timer->adjust(m_screen->time_until_pos(y + 1, 0)); +} + +void subhuntr_state::set_intreq(unsigned bit, u8 mask) +{ + u8 const shifted = ((m_intreq_cnf->read() & mask) ? 1U : 0U) << bit; + if (shifted & ~m_intreqs) + { + if (!m_intreqs) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, ASSERT_LINE); + m_intreqs |= shifted; + } +} + +void subhuntr_state::clr_intreq(unsigned bit) +{ + u8 const shifted = 1U << bit; + if (shifted & m_intreqs) + { + m_intreqs &= ~shifted; + if (!m_intreqs) + m_maincpu->set_input_line(INPUT_LINE_IRQ0, CLEAR_LINE); + } } @@ -84,19 +280,21 @@ void subhuntr_state::video_start() void subhuntr_state::subhuntr_map(address_map &map) { + map.global_mask(0x1fff); + map.unmap_value_high(); + map(0x0000, 0x0fff).rom(); - map(0x1c00, 0x1fff).ram(); + map(0x1000, 0x10ff).mirror(0x0100).ram(); // G8/G9 + map(0x1200, 0x12ff).mirror(0x0100).ram(); // H8/H9 + map(0x1400, 0x14ff).mirror(0x0100).ram(); // L8/L9 + map(0x1600, 0x16ff).mirror(0x0100).ram(); // M8/M9 + map(0x1800, 0x18ff).mirror(0x0100).w(FUNC(subhuntr_state::txtram_w)).share(m_txtram); // B4/C4 + map(0x1b00, 0x1bff).rw(m_pvi3_n5, FUNC(s2636_device::read_data), FUNC(s2636_device::write_data)); + map(0x1d00, 0x1dff).rw(m_pvi2_l5, FUNC(s2636_device::read_data), FUNC(s2636_device::write_data)); + // Some kind of I/O at 1e00-1eff (enabled by PVI1 CE2) + map(0x1f00, 0x1fff).rw(m_pvi1_h5, FUNC(s2636_device::read_data), FUNC(s2636_device::write_data)); } -void subhuntr_state::subhuntr_io_map(address_map &map) -{ -} - -void subhuntr_state::subhuntr_data_map(address_map &map) -{ -// map(S2650_CTRL_PORT, S2650_CTRL_PORT).rw(FUNC(subhuntr_state::), FUNC(subhuntr_state::)); -// map(S2650_DATA_PORT, S2650_DATA_PORT).rw(FUNC(subhuntr_state::), FUNC(subhuntr_state::)); -} /*************************************************************************** @@ -105,6 +303,31 @@ void subhuntr_state::subhuntr_data_map(address_map &map) ***************************************************************************/ static INPUT_PORTS_START( subhuntr ) + + // Factory jumpers controlling interrupt masking + PORT_START("JMP_1_7") + PORT_CONFNAME(0x01, 0x00, "JMP1") // interrupt vector MSB select (indirect bit) + PORT_CONFSETTING(0x00, DEF_STR(Off)) + PORT_CONFSETTING(0x01, DEF_STR(On)) + PORT_CONFNAME(0x02, 0x02, "JMP2") // PVI1 VBLANK/completion interrupt enable + PORT_CONFSETTING(0x00, DEF_STR(Off)) + PORT_CONFSETTING(0x02, DEF_STR(On)) + PORT_CONFNAME(0x04, 0x04, "JMP3") // PVI2 VBLANK/completion interrupt enable + PORT_CONFSETTING(0x00, DEF_STR(Off)) + PORT_CONFSETTING(0x04, DEF_STR(On)) + PORT_CONFNAME(0x08, 0x08, "JMP4") // PVI3 VBLANK/completion interrupt enable + PORT_CONFSETTING(0x00, DEF_STR(Off)) + PORT_CONFSETTING(0x08, DEF_STR(On)) + PORT_CONFNAME(0x10, 0x10, "JMP5") // PVI1/PVI2 collision interrupt enable + PORT_CONFSETTING(0x00, DEF_STR(Off)) + PORT_CONFSETTING(0x10, DEF_STR(On)) + PORT_CONFNAME(0x20, 0x20, "JMP6") // PVI1/PVI3 collision interrupt enable + PORT_CONFSETTING(0x00, DEF_STR(Off)) + PORT_CONFSETTING(0x20, DEF_STR(On)) + PORT_CONFNAME(0x40, 0x40, "JMP7") // PVI2/PVI3 collision interrupt enable + PORT_CONFSETTING(0x00, DEF_STR(Off)); + PORT_CONFSETTING(0x40, DEF_STR(On)); + INPUT_PORTS_END @@ -114,67 +337,52 @@ INPUT_PORTS_END ***************************************************************************/ -void subhuntr_state::machine_start() -{ -} - -void subhuntr_state::machine_reset() -{ -} - -INTERRUPT_GEN_MEMBER(subhuntr_state::subhuntr_interrupt) -{ - m_maincpu->set_input_line(0, ASSERT_LINE); -} - static const gfx_layout tiles8x8_layout = { 8,8, RGN_FRAC(1,1), 1, { 0 }, - { 0, 1, 2, 3, 4, 5, 6, 7 }, + { 7, 6, 5, 4, 3, 2, 1, 0 }, { 0*8,1*8,2*8,3*8,4*8,5*8,6*8,7*8 }, 8*8 }; static GFXDECODE_START( gfx_subhuntr ) - GFXDECODE_ENTRY( "gfx1", 0, tiles8x8_layout, 0, 1 ) + GFXDECODE_ENTRY("gfx1", 0, tiles8x8_layout, 0, 4) GFXDECODE_END void subhuntr_state::subhuntr(machine_config &config) { - /* basic machine hardware */ - S2650(config, m_maincpu, 14318180/4/2); + S2650(config, m_maincpu, 3.579545_MHz_XTAL/2); m_maincpu->set_addrmap(AS_PROGRAM, &subhuntr_state::subhuntr_map); - m_maincpu->set_addrmap(AS_IO, &subhuntr_state::subhuntr_io_map); - m_maincpu->set_addrmap(AS_DATA, &subhuntr_state::subhuntr_data_map); - m_maincpu->set_vblank_int("screen", FUNC(subhuntr_state::subhuntr_interrupt)); - m_maincpu->sense_handler().set("screen", FUNC(screen_device::vblank)); - m_maincpu->intack_handler().set([this]() { m_maincpu->set_input_line(0, CLEAR_LINE); return 0x03; }); + m_maincpu->sense_handler().set(m_screen, FUNC(screen_device::vblank)); + m_maincpu->intack_handler().set(FUNC(subhuntr_state::intack_r)); - //s2636_device &s2636(S2636(config, "s2636", 0)); - //s2636.set_offsets(3, -21); - //s2636.add_route(ALL_OUTPUTS, "mono", 0.10); + S2636(config, m_pvi1_h5, 3.579545_MHz_XTAL); + m_pvi1_h5->set_divider(2); + m_pvi1_h5->intreq_cb().set(FUNC(subhuntr_state::pvi1_intreq_w)); - /* video hardware */ - screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER)); - screen.set_video_attributes(VIDEO_ALWAYS_UPDATE); - screen.set_refresh_hz(50); - screen.set_vblank_time(ATTOSECONDS_IN_USEC(2500)); /* not accurate */ - screen.set_size(256, 256); - screen.set_visarea(1*8, 29*8-1, 2*8, 32*8-1); - screen.set_screen_update(FUNC(subhuntr_state::screen_update_subhuntr)); - screen.set_palette("palette"); + S2636(config, m_pvi2_l5, 3.579545_MHz_XTAL); + m_pvi2_l5->set_divider(2); + m_pvi2_l5->intreq_cb().set(FUNC(subhuntr_state::pvi2_intreq_w)); - GFXDECODE(config, "gfxdecode", "palette", gfx_subhuntr); - PALETTE(config, "palette", FUNC(subhuntr_state::subhuntr_palette), 26); + S2636(config, m_pvi3_n5, 3.579545_MHz_XTAL); + m_pvi3_n5->set_divider(2); + m_pvi3_n5->intreq_cb().set(FUNC(subhuntr_state::pvi3_intreq_w)); + + SCREEN(config, m_screen, SCREEN_TYPE_RASTER); + m_screen->set_raw(3.579545_MHz_XTAL*2, 227*2, 21*2, 205*2, 312, 29, 298); + m_screen->set_palette("palette"); + m_screen->set_screen_update(FUNC(subhuntr_state::screen_update)); + + GFXDECODE(config, m_gfx, "txtpal", gfx_subhuntr); + + PALETTE(config, "palette", palette_device::RGB_3BIT); + PALETTE(config, "txtpal", FUNC(subhuntr_state::palette_init), 8); - /* sound hardware */ SPEAKER(config, "mono").front_center(); - - /* discrete sound */ } @@ -183,13 +391,15 @@ void subhuntr_state::subhuntr(machine_config &config) ROM_START( subhuntr ) ROM_REGION( 0x1000, "maincpu", 0 ) - ROM_LOAD( "mr21.6f", 0x0000, 0x0400, CRC(27847939) SHA1(e6b41b511fefac1e1e207eff2dac8c2963d47c5c) ) - ROM_LOAD( "mr22.6g", 0x0400, 0x0400, CRC(e9af1ee8) SHA1(451e88407a120444377a58b06b65152c57503533) ) - ROM_LOAD( "mr25.6l", 0x0800, 0x0400, CRC(8271c975) SHA1(c7192658b50d781ab1b94c2e8cb75c5be3539820) ) - ROM_LOAD( "mr24.6n", 0x0c00, 0x0400, CRC(385c4944) SHA1(84050b0356c9a3a36528dba768f2684e28c6c7c4) ) + ROM_LOAD( "mr21.f7", 0x0000, 0x0400, CRC(27847939) SHA1(e6b41b511fefac1e1e207eff2dac8c2963d47c5c) ) + ROM_LOAD( "mr22.g7", 0x0400, 0x0400, CRC(e9af1ee8) SHA1(451e88407a120444377a58b06b65152c57503533) ) + ROM_LOAD( "mr25.l7", 0x0800, 0x0400, CRC(8271c975) SHA1(c7192658b50d781ab1b94c2e8cb75c5be3539820) ) + ROM_LOAD( "mr24.n7", 0x0c00, 0x0400, CRC(385c4944) SHA1(84050b0356c9a3a36528dba768f2684e28c6c7c4) ) ROM_REGION( 0x0200, "gfx1", 0 ) - ROM_LOAD( "82s115.2b", 0x0000, 0x0200, CRC(6946c9de) SHA1(956b4bebe6960a73609deb75e1493c4127fd7f77) ) // ASCII, not much else + ROM_LOAD( "82s115.b2", 0x0000, 0x0200, CRC(6946c9de) SHA1(956b4bebe6960a73609deb75e1493c4127fd7f77) ) ROM_END +} // anonymous namespace + GAME(1979, subhuntr, 0, subhuntr, subhuntr, subhuntr_state, empty_init, ROT0, "Model Racing", "Sub Hunter (Model Racing)", MACHINE_NOT_WORKING | MACHINE_NO_SOUND )