Videa/Sente stuff from the brief period before it was acquired by Bally is still in the bally project.
470 lines
15 KiB
C++
470 lines
15 KiB
C++
// license:BSD-3-Clause
|
|
// copyright-holders:Aaron Giles
|
|
/***************************************************************************
|
|
|
|
Videa (predecessor to Sente) Gridlee hardware
|
|
|
|
driver by Aaron Giles
|
|
|
|
special thanks to Howard Delman, Roger Hector and Ed Rotberg for
|
|
allowing distribution of the Gridlee ROMs
|
|
|
|
Based on the Bally/Sente SAC system
|
|
|
|
Games supported:
|
|
* Gridlee
|
|
|
|
Known bugs:
|
|
* analog sound hardware is unemulated
|
|
|
|
****************************************************************************
|
|
|
|
Memory map
|
|
|
|
****************************************************************************
|
|
|
|
========================================================================
|
|
CPU #1
|
|
========================================================================
|
|
0000-007F R/W xxxxxxxx Sprite RAM (32 entries x 4 bytes)
|
|
R/W xxxxxxxx (0: image number)
|
|
R/W -------- (1: unused?)
|
|
R/W xxxxxxxx (2: Y position, offset by 17 pixels)
|
|
R/W xxxxxxxx (3: X position)
|
|
0080-07FF R/W xxxxxxxx Program RAM
|
|
0800-7FFF R/W xxxxxxxx Video RAM (256x240 pixels)
|
|
R/W xxxx---- (left pixel)
|
|
R/W ----xxxx (right pixel)
|
|
9000 W -------x Player 1 LED
|
|
9010 W -------x Player 2 LED
|
|
9020 W -------x Coin counter
|
|
9060 W -------x Unknown (written once at startup)
|
|
9070 W -------x Cocktail flip
|
|
9200 W --xxxxxx Palette base select
|
|
9380 W -------- Watchdog reset
|
|
9500 R ---xxxxx Trackball Y position
|
|
R ---x---- Sign of delta
|
|
R ----xxxx Cumulative magnitude
|
|
9501 R ---xxxxx Trackball X position
|
|
R ---x---- Sign of delta
|
|
R ----xxxx Cumulative magnitude
|
|
9502 R ------x- Fire button 2
|
|
R -------x Fire button 1
|
|
9503 R --xx---- Coinage switches
|
|
R ----x--- 2 player start
|
|
R -----x-- 1 player start
|
|
R ------x- Right coin
|
|
R -------x Left coin
|
|
9600 R x------- Reset game data switch
|
|
R -x------ Reset hall of fame switch
|
|
R --x----- Cocktail/upright switch
|
|
R ---x---- Free play switch
|
|
R ----xx-- Lives switches
|
|
R ------xx Bonus lives switches
|
|
9700 R x------- VBLANK
|
|
R -x------ Service advance
|
|
R --x----- Service switch
|
|
9820 R xxxxxxxx Random number generator
|
|
9828-982C W ???????? Unknown
|
|
9830-983F W ???????? Unknown (sound-related)
|
|
9C00-9CFF R/W -------- NVRAM
|
|
A000-FFFF R xxxxxxxx Fixed program ROM
|
|
========================================================================
|
|
Interrupts:
|
|
NMI not connected
|
|
IRQ generated by 32L
|
|
FIRQ generated by ??? (but should be around scanline 92)
|
|
========================================================================
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
#include "emu.h"
|
|
#include "gridlee.h"
|
|
|
|
#include "cpu/m6809/m6809.h"
|
|
#include "sound/samples.h"
|
|
#include "machine/nvram.h"
|
|
#include "machine/watchdog.h"
|
|
#include "speaker.h"
|
|
|
|
|
|
/* constants */
|
|
#define FIRQ_SCANLINE 92
|
|
|
|
/*************************************
|
|
*
|
|
* Interrupt handling
|
|
*
|
|
*************************************/
|
|
|
|
TIMER_CALLBACK_MEMBER(gridlee_state::irq_off_tick)
|
|
{
|
|
m_maincpu->set_input_line(M6809_IRQ_LINE, CLEAR_LINE);
|
|
}
|
|
|
|
|
|
TIMER_CALLBACK_MEMBER(gridlee_state::irq_timer_tick)
|
|
{
|
|
/* next interrupt after scanline 256 is scanline 64 */
|
|
if (param == 256)
|
|
m_irq_timer->adjust(m_screen->time_until_pos(64), 64);
|
|
else
|
|
m_irq_timer->adjust(m_screen->time_until_pos(param + 64), param + 64);
|
|
|
|
/* IRQ starts on scanline 0, 64, 128, etc. */
|
|
m_maincpu->set_input_line(M6809_IRQ_LINE, ASSERT_LINE);
|
|
|
|
/* it will turn off on the next HBLANK */
|
|
m_irq_off->adjust(m_screen->time_until_pos(param, GRIDLEE_HBSTART));
|
|
}
|
|
|
|
|
|
TIMER_CALLBACK_MEMBER(gridlee_state::firq_off_tick)
|
|
{
|
|
m_maincpu->set_input_line(M6809_FIRQ_LINE, CLEAR_LINE);
|
|
}
|
|
|
|
|
|
TIMER_CALLBACK_MEMBER(gridlee_state::firq_timer_tick)
|
|
{
|
|
/* same time next frame */
|
|
m_firq_timer->adjust(m_screen->time_until_pos(FIRQ_SCANLINE));
|
|
|
|
/* IRQ starts on scanline FIRQ_SCANLINE? */
|
|
m_maincpu->set_input_line(M6809_FIRQ_LINE, ASSERT_LINE);
|
|
|
|
/* it will turn off on the next HBLANK */
|
|
m_firq_off->adjust(m_screen->time_until_pos(FIRQ_SCANLINE, GRIDLEE_HBSTART));
|
|
}
|
|
|
|
void gridlee_state::machine_start()
|
|
{
|
|
/* create the polynomial tables */
|
|
poly17_init();
|
|
|
|
save_item(NAME(m_last_analog_input));
|
|
save_item(NAME(m_last_analog_output));
|
|
|
|
m_irq_off = timer_alloc(FUNC(gridlee_state::irq_off_tick), this);
|
|
m_irq_timer = timer_alloc(FUNC(gridlee_state::irq_timer_tick), this);
|
|
m_firq_off = timer_alloc(FUNC(gridlee_state::firq_off_tick), this);
|
|
m_firq_timer = timer_alloc(FUNC(gridlee_state::firq_timer_tick), this);
|
|
}
|
|
|
|
|
|
void gridlee_state::machine_reset()
|
|
{
|
|
/* start timers to generate interrupts */
|
|
m_irq_timer->adjust(m_screen->time_until_pos(0));
|
|
m_firq_timer->adjust(m_screen->time_until_pos(FIRQ_SCANLINE));
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* ADC handlers
|
|
*
|
|
*************************************/
|
|
|
|
uint8_t gridlee_state::analog_port_r(offs_t offset)
|
|
{
|
|
int delta, sign, magnitude;
|
|
uint8_t newval;
|
|
static const char *const portnames[] = { "TRACK0_Y", "TRACK0_X", "TRACK1_Y", "TRACK1_X" };
|
|
|
|
/* first read the new trackball value and compute the signed delta */
|
|
newval = ioport(portnames[offset + 2 * m_cocktail_flip])->read();
|
|
delta = (int)newval - (int)m_last_analog_input[offset];
|
|
|
|
/* handle the case where we wrap around from 0x00 to 0xff, or vice versa */
|
|
if (delta >= 0x80)
|
|
delta -= 0x100;
|
|
if (delta <= -0x80)
|
|
delta += 0x100;
|
|
|
|
/* just return the previous value for deltas less than 2, which are ignored */
|
|
if (delta >= -1 && delta <= 1)
|
|
return m_last_analog_output[offset];
|
|
m_last_analog_input[offset] = newval;
|
|
|
|
/* compute the sign and the magnitude */
|
|
sign = (delta < 0) ? 0x10 : 0x00;
|
|
magnitude = (delta < 0) ? -delta : delta;
|
|
|
|
/* add the magnitude to the running total */
|
|
m_last_analog_output[offset] += magnitude;
|
|
|
|
/* or in the sign bit and return that */
|
|
return (m_last_analog_output[offset] & 15) | sign;
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* MM5837 noise generator
|
|
*
|
|
* NOTE: this is stolen straight from
|
|
* POKEY.c
|
|
*
|
|
* NOTE: this is assumed to be the
|
|
* same as balsente.c
|
|
*
|
|
*************************************/
|
|
|
|
#define POLY17_BITS 17
|
|
#define POLY17_SIZE ((1 << POLY17_BITS) - 1)
|
|
#define POLY17_SHL 7
|
|
#define POLY17_SHR 10
|
|
#define POLY17_ADD 0x18000
|
|
|
|
void gridlee_state::poly17_init()
|
|
{
|
|
uint32_t i, x = 0;
|
|
uint8_t *p, *r;
|
|
|
|
/* allocate memory */
|
|
m_poly17 = std::make_unique<uint8_t[]>(2 * (POLY17_SIZE + 1));
|
|
p = m_poly17.get();
|
|
r = m_rand17 = m_poly17.get() + POLY17_SIZE + 1;
|
|
|
|
/* generate the polynomial */
|
|
for (i = 0; i < POLY17_SIZE; i++)
|
|
{
|
|
/* store new values */
|
|
*p++ = x & 1;
|
|
*r++ = x >> 3;
|
|
|
|
/* calculate next bit */
|
|
x = ((x << POLY17_SHL) + (x >> POLY17_SHR) + POLY17_ADD) & POLY17_SIZE;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* Hardware random numbers
|
|
*
|
|
*************************************/
|
|
|
|
uint8_t gridlee_state::random_num_r()
|
|
{
|
|
uint32_t cc;
|
|
|
|
/* CPU runs at 1.25MHz, noise source at 100kHz --> multiply by 12.5 */
|
|
cc = m_maincpu->total_cycles();
|
|
|
|
/* 12.5 = 8 + 4 + 0.5 */
|
|
cc = (cc << 3) + (cc << 2) + (cc >> 1);
|
|
return m_rand17[cc & POLY17_SIZE];
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* Misc handlers
|
|
*
|
|
*************************************/
|
|
|
|
void gridlee_state::coin_counter_w(int state)
|
|
{
|
|
machine().bookkeeping().coin_counter_w(0, state);
|
|
logerror("coin counter %s\n", state ? "on" : "off");
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* Main CPU memory handlers
|
|
*
|
|
*************************************/
|
|
|
|
/* CPU 1 read addresses */
|
|
void gridlee_state::cpu1_map(address_map &map)
|
|
{
|
|
map(0x0000, 0x07ff).ram().share("spriteram");
|
|
map(0x0800, 0x7fff).ram().w(FUNC(gridlee_state::gridlee_videoram_w)).share("videoram");
|
|
map(0x9000, 0x9000).select(0x0070).lw8(NAME([this] (offs_t offset, u8 data) { m_latch->write_d0(offset >> 4, data); }));
|
|
map(0x9200, 0x9200).w(FUNC(gridlee_state::gridlee_palette_select_w));
|
|
map(0x9380, 0x9380).w("watchdog", FUNC(watchdog_timer_device::reset_w));
|
|
map(0x9500, 0x9501).r(FUNC(gridlee_state::analog_port_r));
|
|
map(0x9502, 0x9502).portr("IN0");
|
|
map(0x9503, 0x9503).portr("IN1");
|
|
map(0x9600, 0x9600).portr("DSW");
|
|
map(0x9700, 0x9700).portr("IN2").nopw();
|
|
map(0x9820, 0x9820).r(FUNC(gridlee_state::random_num_r));
|
|
map(0x9828, 0x993f).w("gridlee", FUNC(gridlee_sound_device::gridlee_sound_w));
|
|
map(0x9c00, 0x9cff).ram().share("nvram");
|
|
map(0xa000, 0xffff).rom();
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* Port definitions
|
|
*
|
|
*************************************/
|
|
|
|
static INPUT_PORTS_START( gridlee )
|
|
PORT_START("TRACK0_Y") /* 9500 (fake) */
|
|
PORT_BIT( 0xff, 0, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(20) PORT_KEYDELTA(8)
|
|
|
|
PORT_START("TRACK0_X") /* 9501 (fake) */
|
|
PORT_BIT( 0xff, 0, IPT_TRACKBALL_X ) PORT_SENSITIVITY(20) PORT_KEYDELTA(8) PORT_REVERSE
|
|
|
|
PORT_START("TRACK1_Y") /* 9500 (fake) */
|
|
PORT_BIT( 0xff, 0, IPT_TRACKBALL_Y ) PORT_SENSITIVITY(20) PORT_KEYDELTA(8) PORT_COCKTAIL
|
|
|
|
PORT_START("TRACK1_X") /* 9501 (fake) */
|
|
PORT_BIT( 0xff, 0, IPT_TRACKBALL_X ) PORT_SENSITIVITY(20) PORT_KEYDELTA(8) PORT_REVERSE PORT_COCKTAIL
|
|
|
|
PORT_START("IN0") /* 9502 */
|
|
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_BUTTON1 )
|
|
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_BUTTON1 ) PORT_COCKTAIL
|
|
PORT_BIT( 0xfc, IP_ACTIVE_LOW, IPT_UNKNOWN )
|
|
|
|
PORT_START("IN1") /* 9503 */
|
|
PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_COIN1 )
|
|
PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_COIN2 )
|
|
PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_START1 )
|
|
PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_START2 )
|
|
PORT_DIPNAME( 0x30, 0x00, DEF_STR( Coinage ))
|
|
PORT_DIPSETTING( 0x20, DEF_STR( 2C_1C ))
|
|
PORT_DIPSETTING( 0x00, DEF_STR( 1C_1C ))
|
|
PORT_DIPSETTING( 0x10, DEF_STR( 1C_2C ))
|
|
PORT_BIT( 0xc0, IP_ACTIVE_LOW, IPT_UNKNOWN )
|
|
|
|
PORT_START("DSW") /* 9600 */
|
|
PORT_DIPNAME( 0x03, 0x01, DEF_STR( Bonus_Life ))
|
|
PORT_DIPSETTING( 0x00, "8000 points" )
|
|
PORT_DIPSETTING( 0x01, "10000 points" )
|
|
PORT_DIPSETTING( 0x02, "12000 points" )
|
|
PORT_DIPSETTING( 0x03, "14000 points" )
|
|
PORT_DIPNAME( 0x0c, 0x04, DEF_STR( Lives ))
|
|
PORT_DIPSETTING( 0x00, "2" )
|
|
PORT_DIPSETTING( 0x04, "3" )
|
|
PORT_DIPSETTING( 0x08, "4" )
|
|
PORT_DIPSETTING( 0x0c, "5" )
|
|
PORT_DIPNAME( 0x10, 0x00, DEF_STR( Free_Play ))
|
|
PORT_DIPSETTING( 0x00, DEF_STR( Off ))
|
|
PORT_DIPSETTING( 0x10, DEF_STR( On ))
|
|
PORT_DIPNAME( 0x20, 0x00, DEF_STR( Cabinet ) )
|
|
PORT_DIPSETTING( 0x00, DEF_STR( Upright ) )
|
|
PORT_DIPSETTING( 0x20, DEF_STR( Cocktail ) )
|
|
PORT_DIPNAME( 0x40, 0x00, "Reset Hall of Fame" )
|
|
PORT_DIPSETTING( 0x00, DEF_STR( No ))
|
|
PORT_DIPSETTING( 0x40, DEF_STR( Yes ))
|
|
PORT_DIPNAME( 0x80, 0x00, "Reset Game Data" )
|
|
PORT_DIPSETTING( 0x00, DEF_STR( No ))
|
|
PORT_DIPSETTING( 0x80, DEF_STR( Yes ))
|
|
|
|
PORT_START("IN2") /* 9700 */
|
|
PORT_BIT( 0x1f, IP_ACTIVE_LOW, IPT_UNKNOWN )
|
|
PORT_SERVICE( 0x20, IP_ACTIVE_LOW )
|
|
PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_SERVICE1 )
|
|
PORT_BIT( 0x80, IP_ACTIVE_HIGH, IPT_CUSTOM ) PORT_READ_LINE_DEVICE_MEMBER("screen", FUNC(screen_device::vblank))
|
|
INPUT_PORTS_END
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* Sound definitions
|
|
*
|
|
*************************************/
|
|
|
|
static const char *const sample_names[] =
|
|
{
|
|
"*gridlee",
|
|
"bounce1",
|
|
"bounce2",
|
|
nullptr /* end of array */
|
|
};
|
|
|
|
/*************************************
|
|
*
|
|
* Machine driver
|
|
*
|
|
*************************************/
|
|
|
|
void gridlee_state::gridlee(machine_config &config)
|
|
{
|
|
/* basic machine hardware */
|
|
M6809(config, m_maincpu, GRIDLEE_CPU_CLOCK);
|
|
m_maincpu->set_addrmap(AS_PROGRAM, &gridlee_state::cpu1_map);
|
|
|
|
NVRAM(config, "nvram", nvram_device::DEFAULT_ALL_0);
|
|
|
|
WATCHDOG_TIMER(config, "watchdog");
|
|
|
|
LS259(config, m_latch); // type can only be guessed
|
|
m_latch->q_out_cb<0>().set_output("led0");
|
|
m_latch->q_out_cb<1>().set_output("led1");
|
|
m_latch->q_out_cb<2>().set(FUNC(gridlee_state::coin_counter_w));
|
|
// Q6 unknown - only written to at startup
|
|
m_latch->q_out_cb<7>().set(FUNC(gridlee_state::cocktail_flip_w));
|
|
|
|
/* video hardware */
|
|
SCREEN(config, m_screen, SCREEN_TYPE_RASTER);
|
|
m_screen->set_raw(GRIDLEE_PIXEL_CLOCK, GRIDLEE_HTOTAL, GRIDLEE_HBEND, GRIDLEE_HBSTART, GRIDLEE_VTOTAL, GRIDLEE_VBEND, GRIDLEE_VBSTART);
|
|
m_screen->set_screen_update(FUNC(gridlee_state::screen_update_gridlee));
|
|
m_screen->set_palette(m_palette);
|
|
|
|
PALETTE(config, m_palette, FUNC(gridlee_state::gridlee_palette), 2048);
|
|
|
|
/* sound hardware */
|
|
SPEAKER(config, "mono").front_center();
|
|
|
|
GRIDLEE(config, "gridlee", 0).add_route(ALL_OUTPUTS, "mono", 1.0);
|
|
|
|
SAMPLES(config, m_samples);
|
|
m_samples->set_channels(8);
|
|
m_samples->set_samples_names(sample_names);
|
|
m_samples->add_route(ALL_OUTPUTS, "mono", 0.40);
|
|
}
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* ROM definitions
|
|
*
|
|
*************************************/
|
|
|
|
ROM_START( gridlee )
|
|
ROM_REGION( 0x10000, "maincpu", 0 )
|
|
ROM_LOAD( "gridfnla.bin", 0xa000, 0x1000, CRC(1c43539e) SHA1(8b4a6f5c2c22bb021937157606d2129e2b01f718) )
|
|
ROM_LOAD( "gridfnlb.bin", 0xb000, 0x1000, CRC(c48b91b8) SHA1(651210470ddf7c14f16f6c3046a9b8e903824ab8) )
|
|
ROM_LOAD( "gridfnlc.bin", 0xc000, 0x1000, CRC(6ad436dd) SHA1(f393b63077f249d34a8e85649aea58b27a0425b1) )
|
|
ROM_LOAD( "gridfnld.bin", 0xd000, 0x1000, CRC(f7188ddb) SHA1(eeb3f7dd8c61689cdd9992280ee1b3b5dc79a54c) )
|
|
ROM_LOAD( "gridfnle.bin", 0xe000, 0x1000, CRC(d5330bee) SHA1(802bb5705d4cd22d556c1bcbcf730d688ca8e8ab) )
|
|
ROM_LOAD( "gridfnlf.bin", 0xf000, 0x1000, CRC(695d16a3) SHA1(53d22cbedbedad8c89a964b6a38b7075c43cf03b) )
|
|
|
|
ROM_REGION( 0x4000, "gfx1", 0 )
|
|
ROM_LOAD( "gridpix0.bin", 0x0000, 0x1000, CRC(e6ea15ae) SHA1(2c482e25ea44aafd63ca5533b5a2e2dd8bf89365) )
|
|
ROM_LOAD( "gridpix1.bin", 0x1000, 0x1000, CRC(d722f459) SHA1(8cad028eefbba387bdd57fb8bb3a855ae314fb32) )
|
|
ROM_LOAD( "gridpix2.bin", 0x2000, 0x1000, CRC(1e99143c) SHA1(89c2f772cd15f2c37c8167a03dc4c7d1c923e4c3) )
|
|
ROM_LOAD( "gridpix3.bin", 0x3000, 0x1000, CRC(274342a0) SHA1(818cfd4132183d922ff4585c73f2cd6e4546c75b) )
|
|
|
|
ROM_REGION( 0x1800, "proms", 0 )
|
|
ROM_LOAD( "grdrprom.bin", 0x0000, 0x800, CRC(f28f87ed) SHA1(736f38c3ec5455de1266aad348ba708d7201b21a) )
|
|
ROM_LOAD( "grdgprom.bin", 0x0800, 0x800, CRC(921b0328) SHA1(59d1a3d3a90bd680a75adca5dd1b4682236c673b) )
|
|
ROM_LOAD( "grdbprom.bin", 0x1000, 0x800, CRC(04350348) SHA1(098fec3073143e0b8746e728d7d321f2a353286f) )
|
|
ROM_END
|
|
|
|
|
|
|
|
/*************************************
|
|
*
|
|
* Game drivers
|
|
*
|
|
*************************************/
|
|
|
|
GAME( 1983, gridlee, 0, gridlee, gridlee, gridlee_state, empty_init, ROT0, "Videa", "Gridlee", MACHINE_SUPPORTS_SAVE | MACHINE_IMPERFECT_SOUND )
|