wpc_95: Make it testable [O. Galibert]

This commit is contained in:
Olivier Galibert 2014-05-17 20:25:12 +00:00
parent 72f44639ef
commit 2f867f94f8
14 changed files with 3659 additions and 739 deletions

View File

@ -304,24 +304,24 @@ static ADDRESS_MAP_START( dcs_8k_data_map, AS_DATA, 16, dcs_audio_device )
AM_RANGE(0x0000, 0x07ff) AM_RAM AM_RANGE(0x0000, 0x07ff) AM_RAM
AM_RANGE(0x0800, 0x1fff) AM_READWRITE(dcs_dataram_r, dcs_dataram_w) AM_RANGE(0x0800, 0x1fff) AM_READWRITE(dcs_dataram_r, dcs_dataram_w)
AM_RANGE(0x2000, 0x2fff) AM_ROMBANK("databank") AM_RANGE(0x2000, 0x2fff) AM_ROMBANK("databank")
AM_RANGE(0x3000, 0x33ff) AM_WRITE(dcs_data_bank_select_w) AM_RANGE(0x3000, 0x3000) AM_WRITE(dcs_data_bank_select_w)
AM_RANGE(0x3400, 0x37ff) AM_READWRITE(input_latch_r, output_latch_w) AM_RANGE(0x3400, 0x3403) AM_READWRITE(input_latch_r, output_latch_w)
AM_RANGE(0x3800, 0x39ff) AM_RAM AM_RANGE(0x3800, 0x39ff) AM_RAM
AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w)
ADDRESS_MAP_END ADDRESS_MAP_END
/* Williams WPC DCS/Security Pinball */ /* Williams WPC DCS/Security Pinball */
static ADDRESS_MAP_START( dcs_wpc_program_map, AS_PROGRAM, 32, dcs_audio_device ) static ADDRESS_MAP_START( dcs_wpc_program_map, AS_PROGRAM, 32, dcs_audio_device )
AM_RANGE(0x0000, 0x07ff) AM_RAM AM_SHARE("dcsint") AM_RANGE(0x0000, 0x03ff) AM_RAM AM_SHARE("dcsint")
AM_RANGE(0x0800, 0x2fff) AM_RAM AM_SHARE("dcsext") AM_RANGE(0x1000, 0x3fff) AM_RAM AM_SHARE("dcsext")
AM_RANGE(0x3000, 0x3001) AM_READWRITE16(input_latch_r, output_latch_w,0xffff)
ADDRESS_MAP_END ADDRESS_MAP_END
static ADDRESS_MAP_START( dcs_wpc_data_map, AS_DATA, 16, dcs_audio_device ) static ADDRESS_MAP_START( dcs_wpc_data_map, AS_DATA, 16, dcs_audio_wpc_device )
AM_RANGE(0x0000, 0x1fff) AM_READWRITE(dcs_dataram_r, dcs_dataram_w) AM_RANGE(0x0000, 0x07ff) AM_ROMBANK("databank")
AM_RANGE(0x2000, 0x2fff) AM_ROMBANK("databank") AM_RANGE(0x1000, 0x2fff) AM_READWRITE(dcs_dataram_r, dcs_dataram_w)
AM_RANGE(0x3000, 0x33ff) AM_WRITE(dcs_data_bank_select_w) AM_RANGE(0x3000, 0x3000) AM_WRITE(dcs_data_bank_select_w)
AM_RANGE(0x3100, 0x3100) AM_WRITE(dcs_data_bank_select2_w)
AM_RANGE(0x3300, 0x3303) AM_READWRITE(input_latch_r, output_latch_w)
AM_RANGE(0x3800, 0x39ff) AM_RAM AM_RANGE(0x3800, 0x39ff) AM_RAM
AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w) AM_RANGE(0x3fe0, 0x3fff) AM_READWRITE(adsp_control_r, adsp_control_w)
ADDRESS_MAP_END ADDRESS_MAP_END
@ -600,8 +600,9 @@ void dcs_audio_device::dcs_boot()
switch (m_rev) switch (m_rev)
{ {
/* rev 1: use the last set data bank to boot from */ /* rev 1/1.5: use the last set data bank to boot from */
case 1: case 1:
case 15:
/* determine the base */ /* determine the base */
// max_banks = m_bootrom_words / 0x1000; // max_banks = m_bootrom_words / 0x1000;
@ -659,8 +660,9 @@ TIMER_CALLBACK_MEMBER( dcs_audio_device::dcs_reset )
/* reset the memory banking */ /* reset the memory banking */
switch (m_rev) switch (m_rev)
{ {
/* rev 1: just reset the bank to 0 */ /* rev 1/1.5: just reset the bank to 0 */
case 1: case 1:
case 15:
m_sounddata_bank = 0; m_sounddata_bank = 0;
membank("databank")->set_entry(0); membank("databank")->set_entry(0);
break; break;
@ -782,12 +784,12 @@ void dcs_audio_device::dcs_register_state()
// dcs_audio_device - constructor // dcs_audio_device - constructor
//------------------------------------------------- //-------------------------------------------------
dcs_audio_device::dcs_audio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) : dcs_audio_device::dcs_audio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source, int rev) :
device_t(mconfig, type, name, tag, owner, clock, shortname, source), device_t(mconfig, type, name, tag, owner, clock, shortname, source),
m_cpu(NULL), m_cpu(NULL),
m_program(NULL), m_program(NULL),
m_data(NULL), m_data(NULL),
m_rev(0), m_rev(rev),
m_polling_offset(0), m_polling_offset(0),
m_polling_count(0), m_polling_count(0),
m_channels(0), m_channels(0),
@ -833,6 +835,10 @@ dcs_audio_device::dcs_audio_device(const machine_config &mconfig, device_type ty
memset(&m_transfer, 0, sizeof(m_transfer)); memset(&m_transfer, 0, sizeof(m_transfer));
} }
void dcs_audio_device::device_reset()
{
dcs_reset(NULL, 0);
}
void dcs_audio_device::device_start() void dcs_audio_device::device_start()
{ {
@ -848,7 +854,6 @@ void dcs_audio_device::device_start()
m_program = &m_cpu->space(AS_PROGRAM); m_program = &m_cpu->space(AS_PROGRAM);
m_data = &m_cpu->space(AS_DATA); m_data = &m_cpu->space(AS_DATA);
m_rev = 1;
m_channels = 1; m_channels = 1;
m_dmadac[0] = subdevice<dmadac_sound_device>("dac"); m_dmadac[0] = subdevice<dmadac_sound_device>("dac");
@ -857,8 +862,16 @@ void dcs_audio_device::device_start()
m_bootrom_words = machine().root_device().memregion("dcs")->bytes() / 2; m_bootrom_words = machine().root_device().memregion("dcs")->bytes() / 2;
m_sounddata = m_bootrom; m_sounddata = m_bootrom;
m_sounddata_words = m_bootrom_words; m_sounddata_words = m_bootrom_words;
if (m_rev == 1)
{
m_sounddata_banks = m_sounddata_words / 0x1000; m_sounddata_banks = m_sounddata_words / 0x1000;
membank("databank")->configure_entries(0, m_sounddata_banks, m_sounddata, 0x1000*2); membank("databank")->configure_entries(0, m_sounddata_banks, m_sounddata, 0x1000*2);
}
else
{
m_sounddata_banks = m_sounddata_words / 0x800;
membank("databank")->configure_entries(0, m_sounddata_banks, m_sounddata, 0x800*2);
}
/* create the timers */ /* create the timers */
m_internal_timer = subdevice<timer_device>("dcs_int_timer"); m_internal_timer = subdevice<timer_device>("dcs_int_timer");
@ -973,24 +986,34 @@ READ16_MEMBER( dcs_audio_device::dcs_dataram_r )
WRITE16_MEMBER( dcs_audio_device::dcs_dataram_w ) WRITE16_MEMBER( dcs_audio_device::dcs_dataram_w )
{ {
UINT16 newdata = m_external_program_ram[offset] >> 8; UINT16 val = m_external_program_ram[offset] >> 8;
COMBINE_DATA(&newdata); COMBINE_DATA(&val);
m_external_program_ram[offset] = (newdata << 8) | (m_external_program_ram[offset] & 0xff); m_external_program_ram[offset] = (val << 8) | (m_external_program_ram[offset] & 0x0000ff);
} }
WRITE16_MEMBER( dcs_audio_device::dcs_data_bank_select_w ) WRITE16_MEMBER( dcs_audio_device::dcs_data_bank_select_w )
{ {
if (m_rev != 15)
m_sounddata_bank = data & 0x7ff; m_sounddata_bank = data & 0x7ff;
else
m_sounddata_bank = (m_sounddata_bank & 0xff00) | (data & 0xff);
membank("databank")->set_entry(m_sounddata_bank % m_sounddata_banks); membank("databank")->set_entry(m_sounddata_bank % m_sounddata_banks);
/* bit 11 = sound board led */ /* bit 11 = sound board led */
#if 0 #if 0
if (m_rev != 15)
set_led_status(machine(), 2, data & 0x800); set_led_status(machine(), 2, data & 0x800);
#endif #endif
} }
WRITE16_MEMBER( dcs_audio_device::dcs_data_bank_select2_w )
{
m_sounddata_bank = (m_sounddata_bank & 0x00ff) | ((data & 0x01) << 8) | ((data & 0xfc) << 7);
membank("databank")->set_entry(m_sounddata_bank % m_sounddata_banks);
}
/************************************* /*************************************
* *
@ -1416,6 +1439,8 @@ int dcs_audio_device::control_r()
/* only boost for DCS2 boards */ /* only boost for DCS2 boards */
if (!m_auto_ack && !m_transfer.hle_enabled) if (!m_auto_ack && !m_transfer.hle_enabled)
machine().scheduler().boost_interleave(attotime::from_nsec(500), attotime::from_usec(5)); machine().scheduler().boost_interleave(attotime::from_nsec(500), attotime::from_usec(5));
if (m_rev == 15)
return IS_OUTPUT_FULL() ? 0x80 : 0x00;
return m_latch_control; return m_latch_control;
} }
@ -1467,7 +1492,7 @@ READ16_MEMBER( dcs_audio_device::fifo_input_r )
INPUT LATCH (data from host to DCS) INPUT LATCH (data from host to DCS)
****************************************************************************/ ****************************************************************************/
void dcs_audio_device::dcs_delayed_data_w(int data) void dcs_audio_device::dcs_delayed_data_w(UINT8 data)
{ {
if (LOG_DCS_IO) if (LOG_DCS_IO)
logerror("%s:dcs_data_w(%04X)\n", machine().describe_context(), data); logerror("%s:dcs_data_w(%04X)\n", machine().describe_context(), data);
@ -1494,7 +1519,7 @@ TIMER_CALLBACK_MEMBER( dcs_audio_device::dcs_delayed_data_w_callback )
} }
void dcs_audio_device::data_w(int data) void dcs_audio_device::data_w(UINT8 data)
{ {
/* preprocess the write */ /* preprocess the write */
if (preprocess_write(data)) if (preprocess_write(data))
@ -1526,8 +1551,6 @@ READ16_MEMBER( dcs_audio_device::input_latch_r )
return m_input_data; return m_input_data;
} }
/*************************************************************************** /***************************************************************************
OUTPUT LATCH (data from DCS to host) OUTPUT LATCH (data from DCS to host)
****************************************************************************/ ****************************************************************************/
@ -1537,15 +1560,16 @@ TIMER_CALLBACK_MEMBER( dcs_audio_device::latch_delayed_w )
if (!m_last_output_full && !m_output_full_cb.isnull()) if (!m_last_output_full && !m_output_full_cb.isnull())
m_output_full_cb(m_last_output_full = 1); m_output_full_cb(m_last_output_full = 1);
SET_OUTPUT_FULL(); SET_OUTPUT_FULL();
m_output_data = param; m_output_data = m_pre_output_data;
} }
WRITE16_MEMBER( dcs_audio_device::output_latch_w ) WRITE16_MEMBER( dcs_audio_device::output_latch_w )
{ {
m_pre_output_data = data;
if (LOG_DCS_IO) if (LOG_DCS_IO)
logerror("%08X:output_latch_w(%04X) (empty=%d)\n", space.device().safe_pc(), data, IS_OUTPUT_EMPTY()); logerror("%08X:output_latch_w(%04X) (empty=%d)\n", space.device().safe_pc(), data, IS_OUTPUT_EMPTY());
machine().scheduler().synchronize(timer_expired_delegate(FUNC(dcs_audio_device::latch_delayed_w),this), data); machine().scheduler().synchronize(timer_expired_delegate(FUNC(dcs_audio_device::latch_delayed_w),this), data>>8);
} }
@ -1567,9 +1591,9 @@ void dcs_audio_device::ack_w()
} }
int dcs_audio_device::data_r() UINT8 dcs_audio_device::data_r()
{ {
/* data is actually only 8 bit (read from d8-d15) */ /* data is actually only 8 bit (read from d8-d15, which is d0-d7 from the data access instructions POV) */
if (m_last_output_full && !m_output_full_cb.isnull()) if (m_last_output_full && !m_output_full_cb.isnull())
m_output_full_cb(m_last_output_full = 0); m_output_full_cb(m_last_output_full = 0);
if (m_auto_ack) if (m_auto_ack)
@ -1758,6 +1782,7 @@ READ16_MEMBER( dcs_audio_device::adsp_control_r )
break; break;
case IDMA_CONTROL_REG: case IDMA_CONTROL_REG:
if (m_rev == 3 || m_rev == 4)
result = downcast<adsp2181_device *>(m_cpu)->idma_addr_r(); result = downcast<adsp2181_device *>(m_cpu)->idma_addr_r();
break; break;
@ -1839,6 +1864,7 @@ WRITE16_MEMBER(dcs_audio_device:: adsp_control_w )
break; break;
case IDMA_CONTROL_REG: case IDMA_CONTROL_REG:
if (m_rev == 3 || m_rev == 4)
downcast<adsp2181_device *>(m_cpu)->idma_addr_w(data); downcast<adsp2181_device *>(m_cpu)->idma_addr_w(data);
break; break;
} }
@ -1856,7 +1882,7 @@ TIMER_DEVICE_CALLBACK_MEMBER( dcs_audio_device::dcs_irq )
/* copy the current data into the buffer */ /* copy the current data into the buffer */
{ {
int count = m_size / 2; int count = m_size / (2*(m_incs ? m_incs : 1));
INT16 buffer[0x400]; INT16 buffer[0x400];
int i; int i;
@ -1867,7 +1893,7 @@ TIMER_DEVICE_CALLBACK_MEMBER( dcs_audio_device::dcs_irq )
} }
if (m_channels) if (m_channels)
dmadac_transfer(&m_dmadac[0], m_channels, 1, m_channels, (m_size / 2) / m_channels, buffer); dmadac_transfer(&m_dmadac[0], m_channels, 1, m_channels, count / m_channels, buffer);
} }
/* check for wrapping */ /* check for wrapping */
@ -1911,7 +1937,7 @@ void dcs_audio_device::recompute_sample_rate()
dmadac_set_frequency(&m_dmadac[0], m_channels, ATTOSECONDS_TO_HZ(sample_period.attoseconds)); dmadac_set_frequency(&m_dmadac[0], m_channels, ATTOSECONDS_TO_HZ(sample_period.attoseconds));
dmadac_enable(&m_dmadac[0], m_channels, 1); dmadac_enable(&m_dmadac[0], m_channels, 1);
/* fire off a timer wich will hit every half-buffer */ /* fire off a timer which will hit every half-buffer */
if (m_incs) if (m_incs)
{ {
attotime period = (sample_period * m_size) / (2 * m_channels * m_incs); attotime period = (sample_period * m_size) / (2 * m_channels * m_incs);
@ -1919,7 +1945,6 @@ void dcs_audio_device::recompute_sample_rate()
} }
} }
WRITE32_MEMBER(dcs_audio_device::sound_tx_callback) WRITE32_MEMBER(dcs_audio_device::sound_tx_callback)
{ {
/* check if it's for SPORT1 */ /* check if it's for SPORT1 */
@ -1942,7 +1967,7 @@ WRITE32_MEMBER(dcs_audio_device::sound_tx_callback)
lreg = m_ireg; lreg = m_ireg;
/* now get the register contents in a more legible format */ /* now get the register contents in a more legible format */
/* we depend on register indexes to be continuous (wich is the case in our core) */ /* we depend on register indexes to be continuous (which is the case in our core) */
source = m_cpu->state_int(ADSP2100_I0 + m_ireg); source = m_cpu->state_int(ADSP2100_I0 + m_ireg);
m_incs = m_cpu->state_int(ADSP2100_M0 + mreg); m_incs = m_cpu->state_int(ADSP2100_M0 + mreg);
m_size = m_cpu->state_int(ADSP2100_L0 + lreg); m_size = m_cpu->state_int(ADSP2100_L0 + lreg);
@ -2381,7 +2406,7 @@ const device_type DCS_AUDIO_WPC = &device_creator<dcs_audio_wpc_device>;
//------------------------------------------------- //-------------------------------------------------
dcs_audio_wpc_device::dcs_audio_wpc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) : dcs_audio_wpc_device::dcs_audio_wpc_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
dcs_audio_device(mconfig, DCS_AUDIO_WPC, "DCS Audio WPC", tag, owner, clock, "dcs_audio_wpc", __FILE__) dcs_audio_device(mconfig, DCS_AUDIO_WPC, "DCS Audio WPC", tag, owner, clock, "dcs_audio_wpc", __FILE__, 15)
{ {
} }

View File

@ -23,7 +23,7 @@ class dcs_audio_device : public device_t
{ {
public: public:
// construction/destruction // construction/destruction
dcs_audio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); dcs_audio_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source, int rev = 1);
// for dcs2 (int dram_in_mb, offs_t polling_offset) // for dcs2 (int dram_in_mb, offs_t polling_offset)
static void static_set_dram_in_mb(device_t &device, int dram_in_mb) { downcast<dcs_audio_device &>(device).m_dram_in_mb = dram_in_mb; } static void static_set_dram_in_mb(device_t &device, int dram_in_mb) { downcast<dcs_audio_device &>(device).m_dram_in_mb = dram_in_mb; }
@ -34,12 +34,12 @@ public:
void set_fifo_callbacks(read16_delegate fifo_data_r, read16_delegate fifo_status_r, write_line_delegate fifo_reset_w); void set_fifo_callbacks(read16_delegate fifo_data_r, read16_delegate fifo_status_r, write_line_delegate fifo_reset_w);
void set_io_callbacks(write_line_delegate output_full_cb, write_line_delegate input_empty_cb); void set_io_callbacks(write_line_delegate output_full_cb, write_line_delegate input_empty_cb);
int data_r(); UINT8 data_r();
void ack_w(); void ack_w();
int data2_r(); int data2_r();
int control_r(); int control_r();
void data_w(int data); void data_w(UINT8 data);
void reset_w(int state); void reset_w(int state);
void fifo_notify(int count, int max); void fifo_notify(int count, int max);
@ -48,6 +48,9 @@ public:
DECLARE_WRITE32_MEMBER( dsio_idma_data_w ); DECLARE_WRITE32_MEMBER( dsio_idma_data_w );
DECLARE_READ32_MEMBER( dsio_idma_data_r ); DECLARE_READ32_MEMBER( dsio_idma_data_r );
DECLARE_READ32_MEMBER(de_r);
DECLARE_WRITE32_MEMBER(de_w);
// non public // non public
void dcs_boot(); void dcs_boot();
TIMER_CALLBACK_MEMBER( dcs_reset ); TIMER_CALLBACK_MEMBER( dcs_reset );
@ -55,6 +58,9 @@ public:
DECLARE_READ16_MEMBER( dcs_dataram_r ); DECLARE_READ16_MEMBER( dcs_dataram_r );
DECLARE_WRITE16_MEMBER( dcs_dataram_w ); DECLARE_WRITE16_MEMBER( dcs_dataram_w );
DECLARE_WRITE16_MEMBER( dcs_data_bank_select_w ); DECLARE_WRITE16_MEMBER( dcs_data_bank_select_w );
DECLARE_WRITE16_MEMBER( dcs_data_bank_select2_w );
DECLARE_READ16_MEMBER( dcs_dataram_bank_select_r );
DECLARE_WRITE16_MEMBER( dcs_dataram_bank_select_w );
inline void sdrc_update_bank_pointers(); inline void sdrc_update_bank_pointers();
void sdrc_remap_memory(); void sdrc_remap_memory();
void sdrc_reset(); void sdrc_reset();
@ -68,7 +74,7 @@ public:
DECLARE_WRITE16_MEMBER( denver_w ); DECLARE_WRITE16_MEMBER( denver_w );
DECLARE_READ16_MEMBER( latch_status_r ); DECLARE_READ16_MEMBER( latch_status_r );
DECLARE_READ16_MEMBER( fifo_input_r ); DECLARE_READ16_MEMBER( fifo_input_r );
void dcs_delayed_data_w(int data); void dcs_delayed_data_w(UINT8 data);
TIMER_CALLBACK_MEMBER( dcs_delayed_data_w_callback ); TIMER_CALLBACK_MEMBER( dcs_delayed_data_w_callback );
DECLARE_WRITE16_MEMBER( input_latch_ack_w ); DECLARE_WRITE16_MEMBER( input_latch_ack_w );
DECLARE_READ16_MEMBER( input_latch_r ); DECLARE_READ16_MEMBER( input_latch_r );
@ -102,6 +108,7 @@ public:
protected: protected:
// device-level overrides // device-level overrides
virtual void device_start(); virtual void device_start();
virtual void device_reset();
protected: protected:
struct sdrc_state struct sdrc_state
@ -164,8 +171,9 @@ protected:
/* I/O with the host */ /* I/O with the host */
UINT8 m_auto_ack; UINT8 m_auto_ack;
UINT16 m_latch_control; UINT16 m_latch_control;
UINT16 m_input_data; UINT8 m_input_data;
UINT16 m_output_data; UINT8 m_output_data;
UINT8 m_pre_output_data;
UINT16 m_output_control; UINT16 m_output_control;
UINT64 m_output_control_cycles; UINT64 m_output_control_cycles;
UINT8 m_last_output_full; UINT8 m_last_output_full;
@ -259,7 +267,6 @@ public:
// optional information overrides // optional information overrides
virtual machine_config_constructor device_mconfig_additions() const; virtual machine_config_constructor device_mconfig_additions() const;
}; };
// device type definition // device type definition

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
#include "emu.h"
#include "wpc_lamp.h"
const device_type WPC_LAMP = &device_creator<wpc_lamp_device>;
wpc_lamp_device::wpc_lamp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, WPC_LAMP, "Williams Pinball Controller lamp control", tag, owner, clock, "wpc_lamp", __FILE__)
{
names = NULL;
}
wpc_lamp_device::~wpc_lamp_device()
{
}
void wpc_lamp_device::set_names(const char *const *_names)
{
names = _names;
}
void wpc_lamp_device::update()
{
for(int i=0; i<8; i++)
if(row & (1 << i))
for(int j=0; j<8; j++)
if(col & (1 << j))
state[(j<<3)|i] |= 0x80;
}
WRITE8_MEMBER(wpc_lamp_device::row_w)
{
row = data;
update();
}
WRITE8_MEMBER(wpc_lamp_device::col_w)
{
col = data;
update();
}
void wpc_lamp_device::device_start()
{
save_item(NAME(state));
save_item(NAME(col));
save_item(NAME(row));
timer = timer_alloc(0);
}
void wpc_lamp_device::device_reset()
{
memset(state, 0x00, 64);
timer->adjust(attotime::from_hz(60), 0, attotime::from_hz(60));
}
void wpc_lamp_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
for(int i=0; i<64; i++) {
UINT8 s = state[i];
state[i] = s >> 1;
if((s & 0xc0) == 0x40 || (s & 0xc0) == 0x80) {
char buffer[256];
if(names && names[i])
sprintf(buffer, "l:%s", names[i]);
else
sprintf(buffer, "l:%d%d", 1+(i >> 3), 1 + (i & 7));
output_set_value(buffer, (s & 0xc0) == 0x80);
}
}
}

View File

@ -0,0 +1,38 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
// Williams Pinball Controller lamp control
#ifndef WPC_LAMP_H
#define WPC_LAMP_H
#define MCFG_WPC_LAMP_ADD( _tag ) \
MCFG_DEVICE_ADD( _tag, WPC_LAMP, 0 )
class wpc_lamp_device : public device_t
{
public:
wpc_lamp_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual ~wpc_lamp_device();
DECLARE_WRITE8_MEMBER(row_w);
DECLARE_WRITE8_MEMBER(col_w);
void set_names(const char *const *lamp_names);
protected:
UINT8 state[64];
UINT8 col, row;
emu_timer *timer;
const char *const *names;
virtual void device_start();
virtual void device_reset();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
void update();
};
extern const device_type WPC_LAMP;
#endif

View File

@ -0,0 +1,144 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
#include "emu.h"
#include "wpc_out.h"
const device_type WPC_OUT = &device_creator<wpc_out_device>;
wpc_out_device::wpc_out_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, WPC_OUT, "Williams Pinball Controller output control", tag, owner, clock, "wpc_out", __FILE__)
{
names = NULL;
}
wpc_out_device::~wpc_out_device()
{
}
void wpc_out_device::set_names(const char *const *_names)
{
names = _names;
}
void wpc_out_device::set_handler(handler_t cb)
{
handler_cb = cb;
}
void wpc_out_device::set_gi_count(int _count)
{
gi_count = _count;
}
void wpc_out_device::gi_update()
{
attotime now = machine().time();
attotime delta = now - previous_gi_update;
UINT32 delta_us = delta.as_ticks(1e6);
for(int i=0; i<gi_count; i++)
if(gi & (1 <<i))
gi_time[i] += delta_us;
previous_gi_update = now;
}
void wpc_out_device::send_output(int sid, int state)
{
if(!handler_cb.isnull() && handler_cb(sid, state))
return;
char buffer[32];
const char *name;
if(names && names[sid-1])
name = names[sid-1];
else {
sprintf(buffer, "u:output %02d", sid);
name = buffer;
}
output_set_value(name, state);
if(sid == 41)
coin_counter_w(machine(), 0, state);
}
WRITE8_MEMBER(wpc_out_device::out_w)
{
logerror("%s: out_w %d, %02x (%04x)\n", tag(), offset, data, space.device().safe_pc());
first_after_led = false;
UINT8 diff = state[offset] ^ data;
state[offset] = data;
if(diff)
for(int i=0; i<8; i++)
if(diff & (1 << i)) {
int id = (offset << 3) | i;
int sid;
if(id <= 3)
sid = id + 25;
else if(id <= 7)
sid = id + 33;
else if(id <= 15)
sid = id-7;
else if(id <= 23)
sid = id+1;
else if(id <= 31)
sid = id-15;
else if(id <= 39)
sid = id-3;
else
sid = id+2;
send_output(sid, (data & (1<<i)) != 0);
}
}
WRITE8_MEMBER(wpc_out_device::out4_w)
{
// This is gross, probably wrong, but also the best I could find.
// Test case is No Good Gofers (ngg_13).
out_w(space, first_after_led ? 5 : 4, data, mem_mask);
}
WRITE8_MEMBER(wpc_out_device::gi_w)
{
gi_update();
if((gi^data) & 0x80)
send_output(41, data & 0x80 ? 1 : 0);
gi = data;
}
WRITE8_MEMBER(wpc_out_device::led_w)
{
first_after_led = true;
output_set_value("L:cpu led", data & 0x80 ? 1 : 0);
}
void wpc_out_device::device_start()
{
save_item(NAME(state));
save_item(NAME(gi));
save_item(NAME(first_after_led));
save_item(NAME(previous_gi_update));
save_item(NAME(gi_time));
timer = timer_alloc(0);
}
void wpc_out_device::device_reset()
{
memset(state, 0x00, 6);
first_after_led = false;
gi = 0x00;
previous_gi_update = attotime::zero;
memset(gi_time, 0, sizeof(gi_time));
timer->adjust(attotime::from_hz(10), 0, attotime::from_hz(10));
}
void wpc_out_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
{
gi_update();
for(int i=0; i<gi_count; i++) {
// fprintf(stderr, "gi[%d] = %d\n", i, gi_time[i]);
gi_time[i] = 0;
}
}

View File

@ -0,0 +1,51 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
// Williams Pinball Controller outputs control (solenoids, flashers, generic logic, global illumination, coin counter, cpu led)
#ifndef WPC_OUT_H
#define WPC_OUT_H
#define MCFG_WPC_OUT_ADD( _tag, _count ) \
MCFG_DEVICE_ADD( _tag, WPC_OUT, 0 ) \
downcast<wpc_out_device *>(device)->set_gi_count(_count);
class wpc_out_device : public device_t
{
public:
typedef delegate<bool (int, bool)> handler_t;
wpc_out_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual ~wpc_out_device();
DECLARE_WRITE8_MEMBER(out_w);
DECLARE_WRITE8_MEMBER(out4_w); // fixed offset 4
DECLARE_WRITE8_MEMBER(gi_w);
DECLARE_WRITE8_MEMBER(led_w);
void set_names(const char *const *names);
void set_handler(handler_t cb);
void set_gi_count(int _count);
protected:
UINT8 state[6], gi;
bool first_after_led;
attotime previous_gi_update;
int gi_count;
UINT32 gi_time[5];
emu_timer *timer;
const char *const *names;
handler_t handler_cb;
void send_output(int sid, int state);
virtual void device_start();
virtual void device_reset();
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr);
void gi_update();
};
extern const device_type WPC_OUT;
#endif

View File

@ -0,0 +1,141 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
#include "emu.h"
#include "wpc_pic.h"
const device_type WPC_PIC = &device_creator<wpc_pic_device>;
wpc_pic_device::wpc_pic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, WPC_PIC, "Williams Pinball Controller pic security", tag, owner, clock, "wpc_pic", __FILE__),
swarray(*this, ":SW")
{
serial = "000 000000 00000 000";
}
wpc_pic_device::~wpc_pic_device()
{
}
void wpc_pic_device::set_serial(const char *_serial)
{
serial = _serial;
}
READ8_MEMBER(wpc_pic_device::read)
{
UINT8 data = 0x00;
if(curcmd == 0x0d)
data = count;
else if((curcmd & 0xf0) == 0x70) {
data = mem[curcmd & 0xf];
scrambler = (scrambler >> 4) | (curcmd << 4);
mem[ 5]= (mem[ 5]^scrambler) + mem[13];
mem[13]= (mem[13]+scrambler) ^ mem[ 5];
} else if(curcmd >= 0x16 && curcmd < 0x1e)
data = swarray[curcmd - 0x16]->read();
else
logerror("%s: cmd=%02x (%04x)\n", tag(), curcmd, space.device().safe_pc());
return data;
}
void wpc_pic_device::check_game_id()
{
UINT32 cmp = (cmpchk[0] << 16) | (cmpchk[1] << 8) | cmpchk[2];
for(int i=0; i<1000; i++) {
UINT32 v = (i >> 8) * 0x3133 + (i & 0xff) * 0x3231;
v = v & 0xffffff;
if(v == cmp)
logerror("%s: Detected game id %03d\n", tag(), i);
}
}
WRITE8_MEMBER(wpc_pic_device::write)
{
if(chk_count) {
cmpchk[3-chk_count] = data;
if(data != cmpchk[3-chk_count]) {
logerror("%s: WARNING: validation error, checksum[%d] got %02x, expected %02x\n", tag(), 3-chk_count, data, chk[3-chk_count]);
if(chk_count == 1)
check_game_id();
}
chk_count--;
return;
}
if(data == 0x00) {
scrambler = 0xa5;
count = 0x20;
mem[ 5] = mem[0]^mem[15];
mem[13] = mem[2]^mem[12];
} else if(data == 0x0d)
count = (count - 1) & 0x1f;
else if(data == 0x20)
chk_count = 3;
else if((data < 0x16 || data >= 0x1e) && ((data & 0xf0) != 0x70))
logerror("%s: write %02x (%04x)\n", tag(), data, space.device().safe_pc());
curcmd = data;
}
void wpc_pic_device::serial_to_pic()
{
UINT32 no[20];
for(int i=0; i<20; i++)
no[i] = serial[i] - '0';
UINT32 v;
mem[10] = 0x12; // Random?
mem[ 2] = 0x34; // Random?
v = (100*no[1] + 10*no[8] + no[5] + mem[10]*5)*0x1bcd + 0x1f3f0;
mem[ 1] = v >> 16;
mem[11] = v >> 8;
mem[ 9] = v;
v = (10000*no[2] + 1000*no[18] + 100*no[0] + 10*no[9] + no[7] + mem[10]*2 + mem[2])*0x107f + 0x71e259;
mem[ 7] = v >> 24;
mem[12] = v >> 16;
mem[ 0] = v >> 8;
mem[ 8] = v;
v = (1000*no[19] + 100*no[4] + 10*no[6] + no[17] + mem[2])*0x245 + 0x3d74;
mem[ 3] = v >> 16;
mem[14] = v >> 8;
mem[ 6] = v;
v = 99999 - 10000*no[15] - 1000*no[14] - 100*no[13] - 10*no[12] - no[11];
mem[15] = v >> 8;
mem[ 4] = v;
v = 100*no[0] + 10*no[1] + no[2];
v = (v >> 8) * ((serial[17] << 8) | serial[19]) + (v & 0xff) * ((serial[18] << 8) | serial[17]);
chk[0] = v >> 16;
chk[1] = v >> 8;
chk[2] = v;
}
void wpc_pic_device::device_start()
{
save_item(NAME(mem));
save_item(NAME(chk));
save_item(NAME(curcmd));
save_item(NAME(scrambler));
save_item(NAME(count));
save_item(NAME(chk_count));
}
void wpc_pic_device::device_reset()
{
serial_to_pic();
curcmd = 0x00;
scrambler = 0x00;
count = 0x00;
chk_count = 0;
}

View File

@ -0,0 +1,38 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
// Williams Pinball Controller Pic-based protection simulation
#ifndef WPC_PIC_H
#define WPC_PIC_H
#define MCFG_WPC_PIC_ADD( _tag ) \
MCFG_DEVICE_ADD( _tag, WPC_PIC, 0 )
class wpc_pic_device : public device_t
{
public:
wpc_pic_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual ~wpc_pic_device();
DECLARE_READ8_MEMBER(read);
DECLARE_WRITE8_MEMBER(write);
void set_serial(const char *serial);
protected:
required_ioport_array<8> swarray;
UINT8 mem[16], chk[3], curcmd, scrambler, count, chk_count, cmpchk[3];
const char *serial;
virtual void device_start();
virtual void device_reset();
void serial_to_pic();
void check_game_id();
};
extern const device_type WPC_PIC;
#endif

View File

@ -0,0 +1,78 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
#include "emu.h"
#include "wpc_shift.h"
const device_type WPC_SHIFT = &device_creator<wpc_shift_device>;
wpc_shift_device::wpc_shift_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, WPC_SHIFT, "Williams Pinball Controller shifter", tag, owner, clock, "wpc_shift", __FILE__)
{
}
wpc_shift_device::~wpc_shift_device()
{
}
DEVICE_ADDRESS_MAP_START( registers, 8, wpc_shift_device )
AM_RANGE(0, 0) AM_READWRITE(adrh_r, adrh_w)
AM_RANGE(1, 1) AM_READWRITE(adrl_r, adrl_w)
AM_RANGE(2, 2) AM_READWRITE(val1_r, val1_w)
AM_RANGE(3, 3) AM_READWRITE(val2_r, val2_w)
ADDRESS_MAP_END
READ8_MEMBER(wpc_shift_device::adrh_r)
{
return (adr + (val1 >> 3)) >> 8;
}
WRITE8_MEMBER(wpc_shift_device::adrh_w)
{
adr = (adr & 0xff) | (data << 8);
}
READ8_MEMBER(wpc_shift_device::adrl_r)
{
return adr + (val1 >> 3);
}
WRITE8_MEMBER(wpc_shift_device::adrl_w)
{
adr = (adr & 0xff00) | data;
}
READ8_MEMBER(wpc_shift_device::val1_r)
{
return 1 << (val1 & 7);
}
WRITE8_MEMBER(wpc_shift_device::val1_w)
{
val1 = data;
}
READ8_MEMBER(wpc_shift_device::val2_r)
{
return 1 << (val2 & 7);
}
WRITE8_MEMBER(wpc_shift_device::val2_w)
{
val2 = data;
}
void wpc_shift_device::device_start()
{
save_item(NAME(adr));
save_item(NAME(val1));
save_item(NAME(val2));
}
void wpc_shift_device::device_reset()
{
adr = 0x0000;
val1 = 0x00;
val2 = 0x00;
}

View File

@ -0,0 +1,39 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
// Williams Pinball Controller Shift-based protection simulation
#ifndef WPC_SHIFT_H
#define WPC_SHIFT_H
#define MCFG_WPC_SHIFT_ADD( _tag ) \
MCFG_DEVICE_ADD( _tag, WPC_SHIFT, 0 )
class wpc_shift_device : public device_t
{
public:
wpc_shift_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual ~wpc_shift_device();
DECLARE_ADDRESS_MAP(registers, 8);
DECLARE_READ8_MEMBER(adrh_r);
DECLARE_WRITE8_MEMBER(adrh_w);
DECLARE_READ8_MEMBER(adrl_r);
DECLARE_WRITE8_MEMBER(adrl_w);
DECLARE_READ8_MEMBER(val1_r);
DECLARE_WRITE8_MEMBER(val1_w);
DECLARE_READ8_MEMBER(val2_r);
DECLARE_WRITE8_MEMBER(val2_w);
protected:
UINT16 adr;
UINT8 val1, val2;
virtual void device_start();
virtual void device_reset();
};
extern const device_type WPC_SHIFT;
#endif

View File

@ -2157,6 +2157,11 @@ $(MAMEOBJ)/pinball.a: \
$(DRIVERS)/wpc_s.o \ $(DRIVERS)/wpc_s.o \
$(MACHINE)/wpc.o \ $(MACHINE)/wpc.o \
$(AUDIO)/wpcsnd.o \ $(AUDIO)/wpcsnd.o \
$(VIDEO)/wpc_dmd.o \
$(MACHINE)/wpc_pic.o \
$(MACHINE)/wpc_lamp.o \
$(MACHINE)/wpc_out.o \
$(MACHINE)/wpc_shift.o \
$(DRIVERS)/zac_1.o \ $(DRIVERS)/zac_1.o \
$(DRIVERS)/zac_2.o \ $(DRIVERS)/zac_2.o \
$(DRIVERS)/zac_proto.o \ $(DRIVERS)/zac_proto.o \

View File

@ -0,0 +1,199 @@
#include "emu.h"
#include "wpc_dmd.h"
const device_type WPC_DMD = &device_creator<wpc_dmd_device>;
DEVICE_ADDRESS_MAP_START( registers, 8, wpc_dmd_device )
AM_RANGE(0, 0) AM_WRITE(bank2_w)
AM_RANGE(1, 1) AM_WRITE(bank0_w)
AM_RANGE(2, 2) AM_WRITE(bank6_w)
AM_RANGE(3, 3) AM_WRITE(bank4_w)
AM_RANGE(4, 4) AM_WRITE(banka_w)
AM_RANGE(5, 5) AM_WRITE(firq_scanline_w)
AM_RANGE(6, 6) AM_WRITE(bank8_w)
AM_RANGE(7, 7) AM_WRITE(visible_page_w)
ADDRESS_MAP_END
static MACHINE_CONFIG_FRAGMENT( wpc_dmd )
MCFG_SCREEN_ADD("screen", LCD)
MCFG_SCREEN_REFRESH_RATE(60)
MCFG_SCREEN_VBLANK_TIME(ATTOSECONDS_IN_USEC(2500))
MCFG_SCREEN_UPDATE_DEVICE(DEVICE_SELF, wpc_dmd_device, screen_update)
MCFG_SCREEN_SIZE(128*4, 32*4)
MCFG_SCREEN_VISIBLE_AREA(0, 128*4-1, 0, 32*4-1)
MCFG_DEFAULT_LAYOUT(layout_lcd)
MCFG_TIMER_DRIVER_ADD_PERIODIC("scanline", wpc_dmd_device, scanline_timer, attotime::from_hz(60*4*32))
MACHINE_CONFIG_END
wpc_dmd_device::wpc_dmd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) :
device_t(mconfig, WPC_DMD, "Williams Pinball Controller Dot Matrix Display", tag, owner, clock, "wpc_dmd", __FILE__),
scanline_cb(*this),
dmd0(*this, ":dmd0"),
dmd2(*this, ":dmd2"),
dmd4(*this, ":dmd4"),
dmd6(*this, ":dmd6"),
dmd8(*this, ":dmd8"),
dmda(*this, ":dmda")
{
}
wpc_dmd_device::~wpc_dmd_device()
{
}
machine_config_constructor wpc_dmd_device::device_mconfig_additions() const
{
return MACHINE_CONFIG_NAME( wpc_dmd );
}
void wpc_dmd_device::device_start()
{
scanline_cb.resolve_safe();
ram.resize(0x2000);
screen_buffer.resize(128*32);
bitcounts.resize(256);
dmd0->configure_entries(0, 0x10, ram, 0x200);
dmd2->configure_entries(0, 0x10, ram, 0x200);
dmd4->configure_entries(0, 0x10, ram, 0x200);
dmd6->configure_entries(0, 0x10, ram, 0x200);
dmd8->configure_entries(0, 0x10, ram, 0x200);
dmda->configure_entries(0, 0x10, ram, 0x200);
memset(ram, 0x00, 0x2000);
for(int i=0; i<256; i++) {
int bc = i;
bc = ((bc & 0xaa) >> 1) + (bc & 0x55);
bc = ((bc & 0xcc) >> 2) + (bc & 0x33);
bc = ((bc & 0xf0) >> 4) + (bc & 0x0f);
bitcounts[i] = bc;
}
save_item(NAME(visible_page));
save_item(NAME(cur_scanline));
save_item(NAME(firq_scanline));
save_item(NAME(ram));
save_item(NAME(screen_buffer));
save_item(NAME(bitcounts));
}
void wpc_dmd_device::device_reset()
{
dmd0->set_entry(0);
dmd2->set_entry(1);
dmd4->set_entry(2);
dmd6->set_entry(3);
dmd8->set_entry(4);
dmda->set_entry(5);
memset(screen_buffer, 0x00, 128*32);
visible_page = 0;
firq_scanline = 0;
cur_scanline = 0;
}
UINT32 wpc_dmd_device::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
{
const UINT8 *src = screen_buffer;
for(int y=0; y<32; y++) {
UINT32 *pix0 = reinterpret_cast<UINT32 *>(bitmap.raw_pixptr(y*4));
UINT32 *pix1 = reinterpret_cast<UINT32 *>(bitmap.raw_pixptr(y*4+1));
UINT32 *pix2 = reinterpret_cast<UINT32 *>(bitmap.raw_pixptr(y*4+2));
UINT32 *pix3 = reinterpret_cast<UINT32 *>(bitmap.raw_pixptr(y*4+3));
for(int x=0; x<128; x++) {
UINT8 v = bitcounts[*src++ & 0x3f];
UINT8 v0 = v < 2 ? 0 : v-2;
UINT8 v1 = v < 1 ? 0 : v-1;
UINT8 v2 = v > 5 ? 5 : v;
v0 = 255*v0/5;
v1 = 255*v1/5;
v2 = 255*v2/5;
UINT32 xv0 = (v0 << 16) | (v0 << 8);
UINT32 xv1 = (v1 << 16) | (v1 << 8);
UINT32 xv2 = (v2 << 16) | (v2 << 8);
*pix0++ = xv0;
*pix0++ = xv1;
*pix0++ = xv1;
*pix0++ = xv0;
*pix1++ = xv1;
*pix1++ = xv2;
*pix1++ = xv2;
*pix1++ = xv1;
*pix2++ = xv1;
*pix2++ = xv2;
*pix2++ = xv2;
*pix2++ = xv1;
*pix3++ = xv0;
*pix3++ = xv1;
*pix3++ = xv1;
*pix3++ = xv0;
}
}
return 0;
}
TIMER_DEVICE_CALLBACK_MEMBER(wpc_dmd_device::scanline_timer)
{
const UINT8 *src = ram + 0x200*(visible_page & 0xf) + 16*cur_scanline;
UINT8 *base = &screen_buffer[128*cur_scanline];
for(int x1=0; x1<16; x1++) {
UINT8 v = *src++;
for(int x2=0; x2<8; x2++) {
*base = (*base << 1) | ((v & (0x01 << x2)) ? 1 : 0);
base++;
}
}
cur_scanline = (cur_scanline+1) & 0x1f;
scanline_cb(cur_scanline == (firq_scanline & 0x1f));
}
WRITE8_MEMBER(wpc_dmd_device::firq_scanline_w)
{
firq_scanline = data;
}
WRITE8_MEMBER(wpc_dmd_device::bank0_w)
{
dmd0->set_entry(data & 0xf);
}
WRITE8_MEMBER(wpc_dmd_device::bank2_w)
{
dmd2->set_entry(data & 0xf);
}
WRITE8_MEMBER(wpc_dmd_device::bank4_w)
{
dmd4->set_entry(data & 0xf);
}
WRITE8_MEMBER(wpc_dmd_device::bank6_w)
{
dmd6->set_entry(data & 0xf);
}
WRITE8_MEMBER(wpc_dmd_device::bank8_w)
{
dmd8->set_entry(data & 0xf);
}
WRITE8_MEMBER(wpc_dmd_device::banka_w)
{
dmda->set_entry(data & 0xf);
}
WRITE8_MEMBER(wpc_dmd_device::visible_page_w)
{
visible_page = data;
}

View File

@ -0,0 +1,51 @@
// license:BSD-3-Clause
// copyright-holders:Olivier Galibert
// Williams Pinball Controller Dot Matrix Display
// A 128x32 plasma display with 16 pages and refreshed at 240Hz (for PWM luminosity control)
#ifndef WPC_DMD_H
#define WPC_DMD_H
#define MCFG_WPC_DMD_ADD( _tag, _scanline_cb ) \
MCFG_DEVICE_ADD( _tag, WPC_DMD, 0 ) \
devcb = &wpc_dmd_device::set_scanline_cb(*device, DEVCB_##_scanline_cb);
class wpc_dmd_device : public device_t
{
public:
wpc_dmd_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock);
virtual ~wpc_dmd_device();
DECLARE_ADDRESS_MAP(registers, 8);
DECLARE_WRITE8_MEMBER(bank0_w);
DECLARE_WRITE8_MEMBER(bank2_w);
DECLARE_WRITE8_MEMBER(bank4_w);
DECLARE_WRITE8_MEMBER(bank6_w);
DECLARE_WRITE8_MEMBER(bank8_w);
DECLARE_WRITE8_MEMBER(banka_w);
DECLARE_WRITE8_MEMBER(visible_page_w);
DECLARE_WRITE8_MEMBER(firq_scanline_w);
TIMER_DEVICE_CALLBACK_MEMBER(scanline_timer);
UINT32 screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect);
template<class _Object> static devcb_base &set_scanline_cb(device_t &device, _Object object) { return downcast<wpc_dmd_device &>(device).scanline_cb.set_callback(object); }
protected:
devcb_write_line scanline_cb;
required_memory_bank dmd0, dmd2, dmd4, dmd6, dmd8, dmda;
UINT8 cur_scanline, visible_page, firq_scanline;
dynamic_array<UINT8> ram, screen_buffer, bitcounts;
virtual void device_start();
virtual void device_reset();
virtual machine_config_constructor device_mconfig_additions() const;
};
extern const device_type WPC_DMD;
#endif