// license:BSD-3-Clause // copyright-holders:hap, Roberto Fresca // thanks-to:Darrell Hal Smith, Kevin Mullins /**************************************************************** Meyco 8088 based hardware i8088 CPU @ 5MHz (15MHz XTAL + i8284A clock generator), 3 x 8KB EPROM (max 4), 3 x 8KB RAM (max 4), 2KB battery RAM, 2 x i8155, optional i8251A + RS232 for factory debug To initialize battery RAM, go into Meter Read mode (F1 -> 9), and then press the Meter Read + Reset buttons (9 + 0). If a game is not turned off properly, eg. exiting MAME in mid-game, it may run faulty on the next boot. Enable the Night Switch to prevent this. TODO: - coincounters/hopper - correct CPU speed (currently underclocked) ****************************************************************/ #include "emu.h" #include "cpu/i86/i86.h" #include "machine/i8155.h" #include "machine/nvram.h" #include "machine/timer.h" #include "sound/dac.h" #include "sound/volt_reg.h" #include "video/resnet.h" #include "screen.h" #include "speaker.h" #include "gldarrow.lh" class meyc8088_state : public driver_device { public: meyc8088_state(const machine_config &mconfig, device_type type, const char *tag) : driver_device(mconfig, type, tag), m_maincpu(*this,"maincpu"), m_vram(*this, "vram"), m_heartbeat(*this, "heartbeat"), m_switches(*this, {"C0", "C1", "C2", "C3"}), m_lamps(*this, "lamp%u", 0U) { } void meyc8088(machine_config &config); protected: virtual void machine_start() override; private: required_device m_maincpu; required_shared_ptr m_vram; required_device m_heartbeat; required_ioport_array<4> m_switches; output_finder<16> m_lamps; uint8_t m_status; uint8_t m_common; DECLARE_WRITE8_MEMBER(drive_w); DECLARE_WRITE8_MEMBER(video5_flip_w); DECLARE_READ8_MEMBER(video5_flip_r); DECLARE_WRITE8_MEMBER(screen_flip_w); DECLARE_READ8_MEMBER(screen_flip_r); DECLARE_READ8_MEMBER(input_r); DECLARE_READ8_MEMBER(status_r); DECLARE_WRITE8_MEMBER(lights1_w); DECLARE_WRITE8_MEMBER(lights2_w); DECLARE_WRITE8_MEMBER(common_w); DECLARE_PALETTE_INIT(meyc8088); uint32_t screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect); DECLARE_WRITE_LINE_MEMBER(screen_vblank); TIMER_DEVICE_CALLBACK_MEMBER(heartbeat_callback); void meyc8088_map(address_map &map); }; /*************************************************************************** Video ***************************************************************************/ /*************************************************************************** Convert the color PROMs into a more useable format. The palette PROM is connected to the RGB output this way: (even and uneven pins were switched around in the schematics) bit 7 -- N/C -- 820 ohm resistor -- GREEN -- -- GREEN -- 820 ohm resistor -- BLUE -- -- BLUE -- 820 ohm resistor -- RED -- -- RED bit 0 -- N/C plus 330 ohm pullup resistors on all lines ***************************************************************************/ static const res_net_decode_info meyc8088_decode_info = { 1, // there may be two proms needed to construct color 0, 31, // start/end // R, G, B, { 0, 0, 0, }, // offsets { 1, 5, 3, }, // shifts {0x03,0x03,0x03, } // masks }; static const res_net_info meyc8088_net_info = { RES_NET_VCC_5V | RES_NET_VBIAS_5V | RES_NET_VIN_OPEN_COL, { { RES_NET_AMP_NONE, 330, 0, 2, { 1, 820 } }, { RES_NET_AMP_NONE, 330, 0, 2, { 1, 820 } }, { RES_NET_AMP_NONE, 330, 0, 2, { 1, 820 } } } }; PALETTE_INIT_MEMBER(meyc8088_state, meyc8088) { const uint8_t *color_prom = memregion("proms")->base(); std::vector rgb; compute_res_net_all(rgb, color_prom, meyc8088_decode_info, meyc8088_net_info); palette.set_pen_colors(0, rgb); } uint32_t meyc8088_state::screen_update(screen_device &screen, bitmap_ind16 &bitmap, const rectangle &cliprect) { uint8_t v[5]; v[4] = m_status << 2 & 0x10; // video5: color prom d4 if (~m_status & 2) { // screen off bitmap.fill(v[4]); return 0; } for (offs_t offs = 0x800; offs < 0x4000; offs+=2) { uint8_t y = (offs-0x800) >> 6; uint8_t x = (offs-0x800) << 2; v[0] = m_vram[offs|0x0000]; // video1: color prom d0 v[1] = m_vram[offs|0x0001]; // video2: color prom d1 v[2] = m_vram[offs|0x4000]; // video3: color prom d2 v[3] = m_vram[offs|0x4001]; // video4: color prom d3 for (int i = 0; i < 8; i++) bitmap.pix16(y, x | i) = ((v[0] << i) >> 7 & 1) | ((v[1] << i) >> 6 & 2) | ((v[2] << i) >> 5 & 4) | ((v[3] << i) >> 4 & 8) | v[4]; } return 0; } WRITE_LINE_MEMBER(meyc8088_state::screen_vblank) { // LC255(200ns pulse) rising edge asserts INTR at start and end of vblank // INTA wired back to INTR to clear it, vector is hardwired to $20 m_maincpu->set_input_line_and_vector(0, HOLD_LINE, 0x20); } /*************************************************************************** I/O ***************************************************************************/ TIMER_DEVICE_CALLBACK_MEMBER(meyc8088_state::heartbeat_callback) { m_status |= 0x20; } WRITE8_MEMBER(meyc8088_state::drive_w) { // drivers go into high-impedance state ~100ms after write (LS374 /OC) m_status &= ~0x20; m_heartbeat->adjust(attotime::from_msec(100)); // d0-d3: DC counter drivers // d4-d7: AC motor drivers } // switch screen on/off on $b4000 access READ8_MEMBER(meyc8088_state::screen_flip_r) { m_status ^= 2; return 0; } WRITE8_MEMBER(meyc8088_state::screen_flip_w) { m_status ^= 2; } // switch video5 (color prom d4) on/off on $b5000 access READ8_MEMBER(meyc8088_state::video5_flip_r) { m_status ^= 4; return 0; } WRITE8_MEMBER(meyc8088_state::video5_flip_w) { m_status ^= 4; } void meyc8088_state::meyc8088_map(address_map &map) { map(0x00000, 0x007ff).ram().share("nvram"); map(0x70000, 0x77fff).ram().share("vram"); map(0xb0000, 0xb00ff).rw("i8155_2", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w)); map(0xb0800, 0xb0807).rw("i8155_2", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w)); map(0xb1000, 0xb10ff).rw("i8155_1", FUNC(i8155_device::memory_r), FUNC(i8155_device::memory_w)); map(0xb1800, 0xb1807).rw("i8155_1", FUNC(i8155_device::io_r), FUNC(i8155_device::io_w)); map(0xb2000, 0xb2000).w(this, FUNC(meyc8088_state::drive_w)); map(0xb3000, 0xb3000).noprw(); // i8251A data (debug related, unpopulated on sold boards) map(0xb3800, 0xb3800).noprw(); // " map(0xb4000, 0xb4000).rw(this, FUNC(meyc8088_state::screen_flip_r), FUNC(meyc8088_state::screen_flip_w)); map(0xb5000, 0xb5000).rw(this, FUNC(meyc8088_state::video5_flip_r), FUNC(meyc8088_state::video5_flip_w)); map(0xf8000, 0xfffff).rom(); } READ8_MEMBER(meyc8088_state::input_r) { uint8_t ret = 0xff; // multiplexed switch inputs if (~m_common & 1) ret &= m_switches[0].read_safe(0); // bit switches if (~m_common & 2) ret &= m_switches[1].read_safe(0); // control switches if (~m_common & 4) ret &= m_switches[2].read_safe(0); // light switches if (~m_common & 8) ret &= m_switches[3].read_safe(0); // light switches return ret; } READ8_MEMBER(meyc8088_state::status_r) { // d0: /CR2 // d1: screen on // d2: video5 // d3: N/C // d4: battery ok // d5: /drive on return (m_status & 0x27) | 0x18; } WRITE8_MEMBER(meyc8088_state::lights1_w) { // lite 1-8 for (int i = 0; i < 8; i++) m_lamps[i] = BIT(~data, i); } WRITE8_MEMBER(meyc8088_state::lights2_w) { // lite 9-16 for (int i = 0; i < 8; i++) m_lamps[i + 8] = BIT(~data, i); } WRITE8_MEMBER(meyc8088_state::common_w) { // d0: /CR2 m_status = (m_status & ~1) | (data & 1); // d1: battery on m_status = (m_status & ~0x10) | (data << 3 & 0x10); // d2-d5: /common m_common = data >> 2 & 0xf; } void meyc8088_state::machine_start() { m_lamps.resolve(); save_item(NAME(m_status)); save_item(NAME(m_common)); } /*************************************************************************** Inputs ***************************************************************************/ static INPUT_PORTS_START( gldarrow ) PORT_START("SW") PORT_BIT( 0x01, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_VBLANK("screen") PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN1 ) PORT_IMPULSE(1) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_COIN2 ) PORT_IMPULSE(1) // coin4 PORT_BIT( 0x78, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN ) // hopper coin switch? PORT_START("C0") PORT_DIPNAME( 0x03, 0x00, "Payout Percentage" ) PORT_DIPLOCATION("BSW:1,2") PORT_DIPSETTING( 0x03, "85%") PORT_DIPSETTING( 0x02, "88%") PORT_DIPSETTING( 0x01, "90%") PORT_DIPSETTING( 0x00, "93%") PORT_DIPNAME( 0x04, 0x00, "Bit Switch 3" ) PORT_DIPLOCATION("BSW:3") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x04, DEF_STR( On ) ) PORT_DIPNAME( 0x08, 0x00, "Bonus Award" ) PORT_DIPLOCATION("BSW:4") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x08, DEF_STR( On ) ) PORT_DIPNAME( 0x10, 0x00, DEF_STR( Coinage ) ) PORT_DIPLOCATION("BSW:5") PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ) ) PORT_DIPSETTING( 0x10, DEF_STR( 1C_5C ) ) PORT_DIPNAME( 0x20, 0x00, "Bit Switch 6" ) PORT_DIPLOCATION("BSW:6") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x20, DEF_STR( On ) ) PORT_DIPNAME( 0x40, 0x00, "Bit Switch 7" ) PORT_DIPLOCATION("BSW:7") PORT_DIPSETTING( 0x00, DEF_STR( Off ) ) PORT_DIPSETTING( 0x40, DEF_STR( On ) ) PORT_SERVICE_DIPLOC( 0x80, IP_ACTIVE_HIGH, "BSW:8" ) PORT_START("C1") PORT_BIT( 0x1f, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_SERVICE2 ) PORT_NAME("Meter Reset") PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SERVICE1 ) PORT_NAME("Meter Read") PORT_DIPNAME( 0x80, 0x80, "Night Switch" ) PORT_CODE(KEYCODE_F1) PORT_TOGGLE PORT_DIPSETTING( 0x80, DEF_STR( Off ) ) PORT_DIPSETTING( 0x00, DEF_STR( On ) ) PORT_START("C2") PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_GAMBLE_BET ) PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START1 ) PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_SLOT_STOP3 ) PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_SLOT_STOP2 ) PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_SLOT_STOP1 ) PORT_BIT( 0xe0, IP_ACTIVE_LOW, IPT_UNUSED ) PORT_START("C3") PORT_BIT( 0xff, IP_ACTIVE_LOW, IPT_UNUSED ) INPUT_PORTS_END /*************************************************************************** Machine Config ***************************************************************************/ MACHINE_CONFIG_START(meyc8088_state::meyc8088) /* basic machine hardware */ MCFG_DEVICE_ADD(m_maincpu, I8088, (XTAL(15'000'000) / 3) * 0.95) // NOTE: underclocked to prevent errors on diagnostics, MAME i8088 cycle timing is probably inaccurate MCFG_DEVICE_PROGRAM_MAP(meyc8088_map) MCFG_DEVICE_ADD("i8155_1", I8155, XTAL(15'000'000) / (3*1)) // all ports set to input MCFG_I8155_IN_PORTA_CB(READ8(*this, meyc8088_state, input_r)) MCFG_I8155_IN_PORTB_CB(IOPORT("SW")) MCFG_I8155_IN_PORTC_CB(READ8(*this, meyc8088_state, status_r)) // i8251A trigger txc/rxc (debug related, unpopulated on sold boards) MCFG_DEVICE_ADD("i8155_2", I8155, XTAL(15'000'000) / (3*32)) // all ports set to output MCFG_I8155_OUT_PORTA_CB(WRITE8(*this, meyc8088_state, lights2_w)) MCFG_I8155_OUT_PORTB_CB(WRITE8(*this, meyc8088_state, lights1_w)) MCFG_I8155_OUT_PORTC_CB(WRITE8(*this, meyc8088_state, common_w)) MCFG_I8155_OUT_TIMEROUT_CB(WRITELINE("dac", dac_bit_interface, write)) MCFG_NVRAM_ADD_0FILL("nvram") MCFG_TIMER_DRIVER_ADD("heartbeat", meyc8088_state, heartbeat_callback) /* video hardware */ MCFG_SCREEN_ADD("screen", RASTER) MCFG_SCREEN_RAW_PARAMS(XTAL(15'000'000)/3, 320, 0, 256, 261, 0, 224) MCFG_SCREEN_UPDATE_DRIVER(meyc8088_state, screen_update) MCFG_SCREEN_VBLANK_CALLBACK(WRITELINE(*this, meyc8088_state, screen_vblank)) MCFG_SCREEN_PALETTE("palette") MCFG_PALETTE_ADD("palette", 32) MCFG_PALETTE_INIT_OWNER(meyc8088_state, meyc8088) /* sound hardware */ SPEAKER(config, "speaker").front_center(); MCFG_DEVICE_ADD("dac", DAC_1BIT, 0) MCFG_SOUND_ROUTE(ALL_OUTPUTS, "speaker", 0.25) MCFG_DEVICE_ADD("vref", VOLTAGE_REGULATOR, 0) MCFG_VOLTAGE_REGULATOR_OUTPUT(5.0) MCFG_SOUND_ROUTE(0, "dac", 1.0, DAC_VREF_POS_INPUT) MACHINE_CONFIG_END ROM_START( gldarrow ) ROM_REGION( 0x100000, "maincpu", 0 ) ROM_LOAD( "3.14h", 0x0fa000, 0x002000, CRC(a4acc6df) SHA1(b25f2cf8154932834100615e2e9c44ef47a15fea) ) ROM_LOAD( "2.13h", 0x0fc000, 0x002000, CRC(595e380d) SHA1(6f8e58f646106d33cb651d97ca6a1133f7b05373) ) ROM_LOAD( "1.12h", 0x0fe000, 0x002000, CRC(71bd0e39) SHA1(15345f5726cd33ecb1b2da05f2852b6cc3ac7747) ) ROM_REGION( 0x20, "proms", 0 ) ROM_LOAD( "prom.2c", 0x00, 0x20, CRC(2839bb14) SHA1(c9acdb3ae00c2f9344aedaf77c0f4e860a3184fc) ) // M3-7602-5 color prom ROM_END GAMEL( 1984, gldarrow, 0, meyc8088, gldarrow, meyc8088_state, empty_init, ROT0, "Meyco Games, Inc.", "Golden Arrow (Standard G8-03)", MACHINE_SUPPORTS_SAVE, layout_gldarrow )