diff --git a/docs/source/techspecs/device_rom_interface.rst b/docs/source/techspecs/device_rom_interface.rst
index ab5eb18737b..3a40590fc01 100644
--- a/docs/source/techspecs/device_rom_interface.rst
+++ b/docs/source/techspecs/device_rom_interface.rst
@@ -15,7 +15,7 @@ as rom. In the region/block cases, banking is automatically handled.
2. Setup
--------
-| **device_rom_interface**\ (const machine_config &mconfig, device_t &device, UINT8 addrwidth, endianness_t endian = ENDIANNESS_LITTLE, UINT8 datawidth = 8)
+| **device_rom_interface**\ (const machine_config &mconfig, device_t &device, u8 addrwidth, endianness_t endian = ENDIANNESS_LITTLE, u8 datawidth = 8)
The constructor of the interface wants, in addition to the standard
parameters, the address bus width of the dedicated bus. In addition
@@ -41,7 +41,16 @@ rom description for the system, it will be automatically picked up as
the connected rom. An address map has priority over the region if
present in the machine config.
-| void **set_rom**\ (const void \*base, UINT32 size);
+| void **set_rom_endianness**\ (endianness_t endian)
+| void **set_rom_data_width**\ (u8 width)
+| void **set_rom_addr_width**\ (u8 width)
+
+These methods, intended for generic devices with indefinite hardware
+specifications, override the endianness, data bus width and address
+bus width assigned through the constructor. They must be called from
+within the device before **config_complete** time.
+
+| void **set_rom**\ (const void \*base, u32 size);
At any time post- **interface_pre_start**, a memory block can be
setup as the connected rom with that method. It overrides any
@@ -51,10 +60,10 @@ times.
3. Rom access
-------------
-| UINT8 **read_byte**\ (offs_t byteaddress)
-| UINT16 **read_word**\ (offs_t byteaddress)
-| UINT32 **read_dword**\ (offs_t byteaddress)
-| UINT64 **read_qword**\ (offs_t byteaddress)
+| u8 **read_byte**\ (offs_t byteaddress)
+| u16 **read_word**\ (offs_t byteaddress)
+| u32 **read_dword**\ (offs_t byteaddress)
+| u64 **read_qword**\ (offs_t byteaddress)
These methods provide read access to the connected rom. Out-of-bounds
access results in standard unmapped read logerror messages.
@@ -76,3 +85,8 @@ Using that interface makes the device derive from
**device_memory_interface**. If the device wants to actually use the
memory interface for itself, remember that AS_0/AS_PROGRAM is used by
the rom interface, and don't forget to upcall **memory_space_config**.
+
+For devices which have outputs that can be used to address ROMs but
+only to forward the data to another device for processing, it may be
+helpful to disable the interface when it is not required. This can be
+done by overriding **memory_space_config** to return an empty vector.
diff --git a/hash/psx.xml b/hash/psx.xml
index 05a84cbd878..4b482affe0e 100644
--- a/hash/psx.xml
+++ b/hash/psx.xml
@@ -42805,6 +42805,21 @@ The entries in this section are intended to replace the existing "low-grade" Jap
+
+
+ Fire Pro Wrestling G (Japan) (v1.1)
+ 2000
+ Spike
+
+
+
+
+
+
+
+ Gradius Gaiden (Japan) (v1.1)
+ 1997
+ Konami
+
+
+
+
+
+
+
+ Gunners Heaven (Japan)
+ 1995
+ Media Vision
+
+
+
+
+
+
+
+ Hokuto no Ken - Seikimatsu Kyuuseishu Densetsu (Japan)
+ 2000
+ Bandai
+
+
+
+
+
+
+
+ Jikkyou Oshaberi Parodius - Forever with Me (Japan)
+ 1996
+ Konami
+
+
+
+
+
+
+
+ Macross Digital Mission VF-X (Japan)
+ 1997
+ Bandai
+
+
+
+
+
+
+
+
+
+ Macross VF-X2 (Japan)
+ 1999
+ Bandai
+
+
+
+
+
+
+
+ Super Robot Taisen Alpha (Japan) (v1.1)
+ 2000
+ Banpresto
+
+
+
+
+
+
+
+
+
+ Super Robot Taisen Alpha Gaiden - Premium Edition (Japan)
+ 2001
+ Banpresto
+
+
+
+
+
+
+
+
+
+ Super Robot Taisen Alpha Gaiden - Shokai Genteiban (Japan)
+ 2001
+ Banpresto
+
+
+
+
+
+
+
+
+
+ Super Robot Taisen Complete Box (Japan)
+ 1999
+ Banpresto
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Super Robot Taisen F (Japan)
+ 1998
+ Banpresto
+
+
+
+
+
+
+
+
+
+ Super Robot Taisen F Kanketsuhen (Japan)
+ 1999
+ Banpresto
+
+
+
+
+
+
+
+ Zanac X Zanac (Japan)
+ 2001
+ Compile
+
+
+
+
+
+
+
diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua
index d254c010116..38017680f93 100644
--- a/scripts/src/machine.lua
+++ b/scripts/src/machine.lua
@@ -2196,6 +2196,18 @@ if (MACHINES["RF5C296"]~=null) then
}
end
+---------------------------------------------------
+--
+--@src/devices/machine/ripple_counter.h,MACHINES["RIPPLE_COUNTER"] = true
+---------------------------------------------------
+
+if (MACHINES["RIPPLE_COUNTER"]~=null) then
+ files {
+ MAME_DIR .. "src/devices/machine/ripple_counter.cpp",
+ MAME_DIR .. "src/devices/machine/ripple_counter.h",
+ }
+end
+
---------------------------------------------------
--
--@src/devices/machine/roc10937.h,MACHINES["ROC10937"] = true
diff --git a/scripts/target/mame/arcade.lua b/scripts/target/mame/arcade.lua
index 85342ebe188..f0af50fd870 100644
--- a/scripts/target/mame/arcade.lua
+++ b/scripts/target/mame/arcade.lua
@@ -535,6 +535,7 @@ MACHINES["RA17XX"] = true
--MACHINES["R64H156"] = true
MACHINES["RF5C296"] = true
--MACHINES["RIOT6532"] = true
+MACHINES["RIPPLE_COUNTER"] = true
MACHINES["ROC10937"] = true
MACHINES["RP5C01"] = true
MACHINES["RP5C15"] = true
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index ea2ee8ef9ec..03a80c4a6f0 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -524,6 +524,7 @@ MACHINES["PROM82S129"] = true
MACHINES["R64H156"] = true
MACHINES["RF5C296"] = true
MACHINES["RIOT6532"] = true
+MACHINES["RIPPLE_COUNTER"] = true
MACHINES["ROC10937"] = true
MACHINES["RP5C01"] = true
MACHINES["RP5C15"] = true
diff --git a/src/devices/bus/ss50/mpc.cpp b/src/devices/bus/ss50/mpc.cpp
index 7f587d6bb62..8a1ac577a5e 100644
--- a/src/devices/bus/ss50/mpc.cpp
+++ b/src/devices/bus/ss50/mpc.cpp
@@ -13,6 +13,7 @@
#include "bus/rs232/rs232.h"
#include "machine/6821pia.h"
#include "machine/input_merger.h"
+#include "machine/ripple_counter.h"
//**************************************************************************
// TYPE DEFINITIONS
@@ -29,10 +30,8 @@ public:
ss50_card_interface(mconfig, *this),
m_pia(*this, "pia"),
m_loopback(*this, "loopback"),
+ m_counter(*this, "counter"),
m_baud_jumper(*this, "BAUD"),
- m_cd4024ae_count(0),
- m_cd4024ae_clock(false),
- m_cd4024ae_reset(true),
m_count_select(false)
{
}
@@ -53,17 +52,13 @@ private:
DECLARE_WRITE_LINE_MEMBER(serial_input_w);
DECLARE_WRITE_LINE_MEMBER(reader_control_w);
DECLARE_READ_LINE_MEMBER(count_r);
- DECLARE_WRITE_LINE_MEMBER(count_reset_w);
DECLARE_WRITE_LINE_MEMBER(count_select_w);
- DECLARE_WRITE_LINE_MEMBER(clock_input_w);
required_device m_pia;
required_device m_loopback;
+ required_device m_counter;
required_ioport m_baud_jumper;
- u8 m_cd4024ae_count;
- bool m_cd4024ae_clock;
- bool m_cd4024ae_reset;
bool m_count_select;
};
@@ -113,7 +108,7 @@ MACHINE_CONFIG_MEMBER(ss50_mpc_device::device_add_mconfig)
MCFG_PIA_READPB_HANDLER(IOPORT("STOP")) MCFG_DEVCB_BIT(6)
MCFG_DEVCB_CHAIN_INPUT(READLINE(ss50_mpc_device, count_r)) MCFG_DEVCB_BIT(7)
MCFG_PIA_WRITEPB_HANDLER(WRITELINE(ss50_mpc_device, count_select_w)) MCFG_DEVCB_BIT(2)
- MCFG_DEVCB_CHAIN_OUTPUT(WRITELINE(ss50_mpc_device, count_reset_w)) MCFG_DEVCB_BIT(0)
+ MCFG_DEVCB_CHAIN_OUTPUT(DEVWRITELINE("counter", ripple_counter_device, reset_w)) MCFG_DEVCB_BIT(0)
//MCFG_PIA_IRQA_HANDLER(WRITELINE(ss50_mpc_device, pia_irq_w))
//MCFG_PIA_IRQB_HANDLER(WRITELINE(ss50_mpc_device, pia_irq_w))
@@ -126,6 +121,9 @@ MACHINE_CONFIG_MEMBER(ss50_mpc_device::device_add_mconfig)
MCFG_INPUT_MERGER_ANY_HIGH("loopback")
MCFG_INPUT_MERGER_OUTPUT_HANDLER(DEVWRITELINE("outgate", input_merger_device, in_w<1>))
+
+ MCFG_DEVICE_ADD("counter", RIPPLE_COUNTER, 0) // CD4024AE (IC3)
+ MCFG_RIPPLE_COUNTER_STAGES(7) // only Q5 (÷32) and Q4 (÷16) are actually used
MACHINE_CONFIG_END
@@ -135,9 +133,6 @@ MACHINE_CONFIG_END
void ss50_mpc_device::device_start()
{
- save_item(NAME(m_cd4024ae_count));
- save_item(NAME(m_cd4024ae_clock));
- save_item(NAME(m_cd4024ae_reset));
save_item(NAME(m_count_select));
}
@@ -155,14 +150,7 @@ WRITE_LINE_MEMBER(ss50_mpc_device::reader_control_w)
READ_LINE_MEMBER(ss50_mpc_device::count_r)
{
- return BIT(m_cd4024ae_count, m_count_select ? 4 : 3);
-}
-
-WRITE_LINE_MEMBER(ss50_mpc_device::count_reset_w)
-{
- m_cd4024ae_reset = bool(state);
- if (state)
- m_cd4024ae_count = 0;
+ return BIT(m_counter->count(), m_count_select ? 4 : 3);
}
WRITE_LINE_MEMBER(ss50_mpc_device::count_select_w)
@@ -170,16 +158,6 @@ WRITE_LINE_MEMBER(ss50_mpc_device::count_select_w)
m_count_select = bool(state);
}
-WRITE_LINE_MEMBER(ss50_mpc_device::clock_input_w)
-{
- if (m_cd4024ae_clock != bool(state))
- {
- m_cd4024ae_clock = bool(state);
- if (!state && !m_cd4024ae_reset)
- m_cd4024ae_count++;
- }
-}
-
//-------------------------------------------------
// register_read - read from a port register
@@ -202,13 +180,13 @@ WRITE8_MEMBER(ss50_mpc_device::register_write)
WRITE_LINE_MEMBER(ss50_mpc_device::f110_w)
{
if (m_baud_jumper->read())
- clock_input_w(state);
+ m_counter->clock_w(state);
}
WRITE_LINE_MEMBER(ss50_mpc_device::f300_w)
{
if (!m_baud_jumper->read())
- clock_input_w(state);
+ m_counter->clock_w(state);
}
diff --git a/src/devices/cpu/i386/i386.cpp b/src/devices/cpu/i386/i386.cpp
index 15379c18d7b..dd6d86928e2 100644
--- a/src/devices/cpu/i386/i386.cpp
+++ b/src/devices/cpu/i386/i386.cpp
@@ -3788,59 +3788,59 @@ void i386_device::pentium_smi()
m_smi_latched = false;
// save state
- WRITE32(m_cr[4], smram_state+SMRAM_IP5_CR4);
- WRITE32(m_sreg[ES].limit, smram_state+SMRAM_IP5_ESLIM);
- WRITE32(m_sreg[ES].base, smram_state+SMRAM_IP5_ESBASE);
- WRITE32(m_sreg[ES].flags, smram_state+SMRAM_IP5_ESACC);
- WRITE32(m_sreg[CS].limit, smram_state+SMRAM_IP5_CSLIM);
- WRITE32(m_sreg[CS].base, smram_state+SMRAM_IP5_CSBASE);
- WRITE32(m_sreg[CS].flags, smram_state+SMRAM_IP5_CSACC);
- WRITE32(m_sreg[SS].limit, smram_state+SMRAM_IP5_SSLIM);
- WRITE32(m_sreg[SS].base, smram_state+SMRAM_IP5_SSBASE);
- WRITE32(m_sreg[SS].flags, smram_state+SMRAM_IP5_SSACC);
- WRITE32(m_sreg[DS].limit, smram_state+SMRAM_IP5_DSLIM);
- WRITE32(m_sreg[DS].base, smram_state+SMRAM_IP5_DSBASE);
- WRITE32(m_sreg[DS].flags, smram_state+SMRAM_IP5_DSACC);
- WRITE32(m_sreg[FS].limit, smram_state+SMRAM_IP5_FSLIM);
- WRITE32(m_sreg[FS].base, smram_state+SMRAM_IP5_FSBASE);
- WRITE32(m_sreg[FS].flags, smram_state+SMRAM_IP5_FSACC);
- WRITE32(m_sreg[GS].limit, smram_state+SMRAM_IP5_GSLIM);
- WRITE32(m_sreg[GS].base, smram_state+SMRAM_IP5_GSBASE);
- WRITE32(m_sreg[GS].flags, smram_state+SMRAM_IP5_GSACC);
- WRITE32(m_ldtr.flags, smram_state+SMRAM_IP5_LDTACC);
- WRITE32(m_ldtr.limit, smram_state+SMRAM_IP5_LDTLIM);
- WRITE32(m_ldtr.base, smram_state+SMRAM_IP5_LDTBASE);
- WRITE32(m_gdtr.limit, smram_state+SMRAM_IP5_GDTLIM);
- WRITE32(m_gdtr.base, smram_state+SMRAM_IP5_GDTBASE);
- WRITE32(m_idtr.limit, smram_state+SMRAM_IP5_IDTLIM);
- WRITE32(m_idtr.base, smram_state+SMRAM_IP5_IDTBASE);
- WRITE32(m_task.limit, smram_state+SMRAM_IP5_TRLIM);
- WRITE32(m_task.base, smram_state+SMRAM_IP5_TRBASE);
- WRITE32(m_task.flags, smram_state+SMRAM_IP5_TRACC);
+ WRITE32(smram_state + SMRAM_IP5_CR4, m_cr[4]);
+ WRITE32(smram_state + SMRAM_IP5_ESLIM, m_sreg[ES].limit);
+ WRITE32(smram_state + SMRAM_IP5_ESBASE, m_sreg[ES].base);
+ WRITE32(smram_state + SMRAM_IP5_ESACC, m_sreg[ES].flags);
+ WRITE32(smram_state + SMRAM_IP5_CSLIM, m_sreg[CS].limit);
+ WRITE32(smram_state + SMRAM_IP5_CSBASE, m_sreg[CS].base);
+ WRITE32(smram_state + SMRAM_IP5_CSACC, m_sreg[CS].flags);
+ WRITE32(smram_state + SMRAM_IP5_SSLIM, m_sreg[SS].limit);
+ WRITE32(smram_state + SMRAM_IP5_SSBASE, m_sreg[SS].base);
+ WRITE32(smram_state + SMRAM_IP5_SSACC, m_sreg[SS].flags);
+ WRITE32(smram_state + SMRAM_IP5_DSLIM, m_sreg[DS].limit);
+ WRITE32(smram_state + SMRAM_IP5_DSBASE, m_sreg[DS].base);
+ WRITE32(smram_state + SMRAM_IP5_DSACC, m_sreg[DS].flags);
+ WRITE32(smram_state + SMRAM_IP5_FSLIM, m_sreg[FS].limit);
+ WRITE32(smram_state + SMRAM_IP5_FSBASE, m_sreg[FS].base);
+ WRITE32(smram_state + SMRAM_IP5_FSACC, m_sreg[FS].flags);
+ WRITE32(smram_state + SMRAM_IP5_GSLIM, m_sreg[GS].limit);
+ WRITE32(smram_state + SMRAM_IP5_GSBASE, m_sreg[GS].base);
+ WRITE32(smram_state + SMRAM_IP5_GSACC, m_sreg[GS].flags);
+ WRITE32(smram_state + SMRAM_IP5_LDTACC, m_ldtr.flags);
+ WRITE32(smram_state + SMRAM_IP5_LDTLIM, m_ldtr.limit);
+ WRITE32(smram_state + SMRAM_IP5_LDTBASE, m_ldtr.base);
+ WRITE32(smram_state + SMRAM_IP5_GDTLIM, m_gdtr.limit);
+ WRITE32(smram_state + SMRAM_IP5_GDTBASE, m_gdtr.base);
+ WRITE32(smram_state + SMRAM_IP5_IDTLIM, m_idtr.limit);
+ WRITE32(smram_state + SMRAM_IP5_IDTBASE, m_idtr.base);
+ WRITE32(smram_state + SMRAM_IP5_TRLIM, m_task.limit);
+ WRITE32(smram_state + SMRAM_IP5_TRBASE, m_task.base);
+ WRITE32(smram_state + SMRAM_IP5_TRACC, m_task.flags);
- WRITE32(m_sreg[ES].selector, smram_state+SMRAM_ES);
- WRITE32(m_sreg[CS].selector, smram_state+SMRAM_CS);
- WRITE32(m_sreg[SS].selector, smram_state+SMRAM_SS);
- WRITE32(m_sreg[DS].selector, smram_state+SMRAM_DS);
- WRITE32(m_sreg[FS].selector, smram_state+SMRAM_FS);
- WRITE32(m_sreg[GS].selector, smram_state+SMRAM_GS);
- WRITE32(m_ldtr.segment, smram_state+SMRAM_LDTR);
- WRITE32(m_task.segment, smram_state+SMRAM_TR);
+ WRITE32(smram_state + SMRAM_ES, m_sreg[ES].selector);
+ WRITE32(smram_state + SMRAM_CS, m_sreg[CS].selector);
+ WRITE32(smram_state + SMRAM_SS, m_sreg[SS].selector);
+ WRITE32(smram_state + SMRAM_DS, m_sreg[DS].selector);
+ WRITE32(smram_state + SMRAM_FS, m_sreg[FS].selector);
+ WRITE32(smram_state + SMRAM_GS, m_sreg[GS].selector);
+ WRITE32(smram_state + SMRAM_LDTR, m_ldtr.segment);
+ WRITE32(smram_state + SMRAM_TR, m_task.segment);
- WRITE32(m_dr[7], smram_state+SMRAM_DR7);
- WRITE32(m_dr[6], smram_state+SMRAM_DR6);
- WRITE32(REG32(EAX), smram_state+SMRAM_EAX);
- WRITE32(REG32(ECX), smram_state+SMRAM_ECX);
- WRITE32(REG32(EDX), smram_state+SMRAM_EDX);
- WRITE32(REG32(EBX), smram_state+SMRAM_EBX);
- WRITE32(REG32(ESP), smram_state+SMRAM_ESP);
- WRITE32(REG32(EBP), smram_state+SMRAM_EBP);
- WRITE32(REG32(ESI), smram_state+SMRAM_ESI);
- WRITE32(REG32(EDI), smram_state+SMRAM_EDI);
- WRITE32(m_eip, smram_state+SMRAM_EIP);
- WRITE32(old_flags, smram_state+SMRAM_EFLAGS);
- WRITE32(m_cr[3], smram_state+SMRAM_CR3);
- WRITE32(old_cr0, smram_state+SMRAM_CR0);
+ WRITE32(smram_state + SMRAM_DR7, m_dr[7]);
+ WRITE32(smram_state + SMRAM_DR6, m_dr[6]);
+ WRITE32(smram_state + SMRAM_EAX, REG32(EAX));
+ WRITE32(smram_state + SMRAM_ECX, REG32(ECX));
+ WRITE32(smram_state + SMRAM_EDX, REG32(EDX));
+ WRITE32(smram_state + SMRAM_EBX, REG32(EBX));
+ WRITE32(smram_state + SMRAM_ESP, REG32(ESP));
+ WRITE32(smram_state + SMRAM_EBP, REG32(EBP));
+ WRITE32(smram_state + SMRAM_ESI, REG32(ESI));
+ WRITE32(smram_state + SMRAM_EDI, REG32(EDI));
+ WRITE32(smram_state + SMRAM_EIP, m_eip);
+ WRITE32(smram_state + SMRAM_EFLAGS, old_flags);
+ WRITE32(smram_state + SMRAM_CR3, m_cr[3]);
+ WRITE32(smram_state + SMRAM_CR0, old_cr0);
m_sreg[DS].selector = m_sreg[ES].selector = m_sreg[FS].selector = m_sreg[GS].selector = m_sreg[SS].selector = 0;
m_sreg[DS].base = m_sreg[ES].base = m_sreg[FS].base = m_sreg[GS].base = m_sreg[SS].base = 0x00000000;
diff --git a/src/devices/cpu/m6809/m6809.cpp b/src/devices/cpu/m6809/m6809.cpp
index 521d4a113b7..0521b745414 100644
--- a/src/devices/cpu/m6809/m6809.cpp
+++ b/src/devices/cpu/m6809/m6809.cpp
@@ -92,6 +92,15 @@ March 2013 NPW:
divider to the MC6809E and not to the MC6809; the confusion resulting
from this error is in the process of being straightened out.
+ Maximum clock ratings:
+
+ Q & E EXTAL
+ MC6809(E) 1.0 MHz 4.0 MHz
+ MC68A09(E) 1.5 MHz 6.0 MHz
+ MC68B09(E) 2.0 MHz 8.0 MHz
+ HD63B09(E) 2.0 MHz 8.0 MHz
+ HD63C09(E) 3.0 MHz 12.0 MHz
+
*****************************************************************************/
#include "emu.h"
diff --git a/src/devices/machine/ripple_counter.cpp b/src/devices/machine/ripple_counter.cpp
new file mode 100644
index 00000000000..ae38d342d68
--- /dev/null
+++ b/src/devices/machine/ripple_counter.cpp
@@ -0,0 +1,202 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+
+ Generic binary ripple counter emulation
+
+ This device emulates basic ripple counter logic ICs with falling-
+ edge clocks and a synchronous reset inputs such as CD4040 and
+ 74LS393.
+
+ The optional 8-bit ROM interface is intended to help stream ROM
+ data to sound chips that lack memory interfaces of their own
+ (e.g. MSM5205, TMS5110).
+
+**********************************************************************/
+
+#include "emu.h"
+#include "machine/ripple_counter.h"
+
+//**************************************************************************
+// GLOBAL VARIABLES
+//**************************************************************************
+
+// device type definition
+DEFINE_DEVICE_TYPE(RIPPLE_COUNTER, ripple_counter_device, "ripple_counter", "Generic ripple counter")
+
+
+//**************************************************************************
+// RIPPLE COUNTER DEVICE
+//**************************************************************************
+
+//-------------------------------------------------
+// ripple_counter_device - constructor
+//-------------------------------------------------
+
+ripple_counter_device::ripple_counter_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, RIPPLE_COUNTER, tag, owner, clock),
+ device_rom_interface(mconfig, *this, 0, ENDIANNESS_LITTLE, 8),
+ m_count_out_cb(*this),
+ m_rom_out_cb(*this),
+ m_count_timer(nullptr),
+ m_count_mask(0),
+ m_count(1),
+ m_clk(false),
+ m_reset(false)
+{
+}
+
+
+//-------------------------------------------------
+// static_set_stages - configure the number of
+// stages used to count
+//-------------------------------------------------
+
+void ripple_counter_device::static_set_stages(device_t &device, u8 stages)
+{
+ auto &dev = downcast(device);
+ dev.m_count_mask = (1U << stages) - 1;
+ dev.set_rom_addr_width(stages);
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return a description of
+// any address spaces owned by this device
+//-------------------------------------------------
+
+device_memory_interface::space_config_vector ripple_counter_device::memory_space_config() const
+{
+ if (m_rom_out_cb.isnull())
+ return space_config_vector();
+ else
+ return device_rom_interface::memory_space_config();
+}
+
+
+//-------------------------------------------------
+// device_validity_check - validate a device after
+// the configuration has been constructed
+//-------------------------------------------------
+
+void ripple_counter_device::device_validity_check(validity_checker &valid) const
+{
+ if (m_count_mask == 0)
+ osd_printf_error("No counting stages configured\n");
+}
+
+
+//-------------------------------------------------
+// device_resolve_objects - resolve objects that
+// may be needed for other devices to set
+// initial conditions at start time
+//-------------------------------------------------
+
+void ripple_counter_device::device_resolve_objects()
+{
+ // resolve callbacks
+ m_count_out_cb.resolve_safe();
+ m_rom_out_cb.resolve();
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void ripple_counter_device::device_start()
+{
+ // initialize timers
+ m_count_timer = timer_alloc(TIMER_COUNT);
+
+ // register internal state
+ save_item(NAME(m_count));
+ save_item(NAME(m_clk));
+ save_item(NAME(m_reset));
+}
+
+
+//-------------------------------------------------
+// device_clock_changed - called when the
+// device clock is altered in any way
+//-------------------------------------------------
+
+void ripple_counter_device::device_clock_changed()
+{
+ attotime freq = m_reset ? attotime::never : clocks_to_attotime(1);
+ m_count_timer->adjust(freq, 0, freq);
+}
+
+
+//-------------------------------------------------
+// rom_bank_updated - called when the ROM bank
+// is changed
+//-------------------------------------------------
+
+void ripple_counter_device::rom_bank_updated()
+{
+ m_rom_out_cb(read_byte(m_count));
+}
+
+
+//-------------------------------------------------
+// set_count - update the count and associated
+// outputs
+//-------------------------------------------------
+
+void ripple_counter_device::set_count(u32 count)
+{
+ m_count = count;
+ m_count_out_cb(count);
+ if (!m_rom_out_cb.isnull())
+ m_rom_out_cb(read_byte(count));
+}
+
+
+//-------------------------------------------------
+// clock_w - handle falling-edge clock input
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER(ripple_counter_device::clock_w)
+{
+ if (m_clk != bool(state))
+ {
+ m_clk = bool(state);
+ if (!state && !m_reset)
+ set_count((m_count + 1) & m_count_mask);
+ }
+}
+
+
+//-------------------------------------------------
+// reset_w - handle active-high reset input
+//-------------------------------------------------
+
+WRITE_LINE_MEMBER(ripple_counter_device::reset_w)
+{
+ if (m_reset != bool(state))
+ {
+ m_reset = bool(state);
+ if (state && m_count != 0)
+ set_count(0);
+
+ // stop or start the count timer as required
+ notify_clock_changed();
+ }
+}
+
+
+//-------------------------------------------------
+// device_timer - called whenever a device timer
+// fires
+//-------------------------------------------------
+
+void ripple_counter_device::device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr)
+{
+ switch (id)
+ {
+ case TIMER_COUNT:
+ set_count((m_count + 1) & m_count_mask);
+ break;
+ }
+}
diff --git a/src/devices/machine/ripple_counter.h b/src/devices/machine/ripple_counter.h
new file mode 100644
index 00000000000..f9e997f3348
--- /dev/null
+++ b/src/devices/machine/ripple_counter.h
@@ -0,0 +1,92 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+
+ Generic binary ripple counter emulation
+
+**********************************************************************/
+
+#ifndef MAME_MACHINE_RIPPLE_COUNTER_H
+#define MAME_MACHINE_RIPPLE_COUNTER_H
+
+#pragma once
+
+//**************************************************************************
+// CONFIGURATION MACROS
+//**************************************************************************
+
+#define MCFG_RIPPLE_COUNTER_STAGES(_stages) \
+ ripple_counter_device::static_set_stages(*device, _stages);
+
+// output callbacks
+#define MCFG_RIPPLE_COUNTER_COUNT_OUT_CB(_devcb) \
+ devcb = &ripple_counter_device::static_set_count_out_cb(*device, DEVCB_##_devcb);
+#define MCFG_RIPPLE_COUNTER_ROM_OUT_CB(_devcb) \
+ devcb = &ripple_counter_device::static_set_rom_out_cb(*device, DEVCB_##_devcb);
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> ripple_counter_device
+
+class ripple_counter_device : public device_t, public device_rom_interface
+{
+public:
+ // construction/destruction
+ ripple_counter_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock);
+
+ // static configuration
+ static void static_set_stages(device_t &device, u8 stages);
+ template static devcb_base &static_set_count_out_cb(device_t &device, Object &&cb)
+ { return downcast(device).m_count_out_cb.set_callback(std::forward