summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2017-07-12 19:58:33 +1000
committer Vas Crabb <vas@vastheman.com>2017-07-12 19:58:33 +1000
commit95906048abf77103d831014ce5a2ef214d9e8971 (patch)
tree99451208e1eec0be3dd45de2edc6fbc580389cdc
parent4b6e1f2a0c68fa106b4959423265146d7984e35a (diff)
INTELLEC 4: implement front-panel PROM programmer
-rw-r--r--scripts/target/mame/mess.lua2
-rw-r--r--src/devices/bus/intellec4/intellec4.h6
-rw-r--r--src/mame/drivers/intellec4.cpp109
-rw-r--r--src/mame/layout/intlc44.lay30
-rw-r--r--src/mame/layout/intlc440.lay30
-rw-r--r--src/mame/machine/imm6_76.cpp204
-rw-r--r--src/mame/machine/imm6_76.h203
7 files changed, 581 insertions, 3 deletions
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 60aa06fa3c3..395662a41da 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -2130,6 +2130,8 @@ files {
MAME_DIR .. "src/mame/drivers/isbc.cpp",
MAME_DIR .. "src/mame/drivers/isbc8010.cpp",
MAME_DIR .. "src/mame/drivers/isbc8030.cpp",
+ MAME_DIR .. "src/mame/machine/imm6_76.cpp",
+ MAME_DIR .. "src/mame/machine/imm6_76.h",
MAME_DIR .. "src/mame/machine/isbc_215g.cpp",
MAME_DIR .. "src/mame/machine/isbc_215g.h",
MAME_DIR .. "src/mame/machine/isbc_208.cpp",
diff --git a/src/devices/bus/intellec4/intellec4.h b/src/devices/bus/intellec4/intellec4.h
index 58900760300..61a77873d54 100644
--- a/src/devices/bus/intellec4/intellec4.h
+++ b/src/devices/bus/intellec4/intellec4.h
@@ -254,6 +254,12 @@ protected:
// device_interface implementation
void interface_pre_start() override;
+ address_space &rom_space() { return m_bus->m_rom_device->space(m_bus->m_rom_space); }
+ address_space &rom_ports_space() { return m_bus->m_rom_ports_device->space(m_bus->m_rom_ports_space); }
+ address_space &memory_space() { return m_bus->m_memory_device->space(m_bus->m_memory_space); }
+ address_space &status_space() { return m_bus->m_status_device->space(m_bus->m_status_space); }
+ address_space &ram_ports_space() { return m_bus->m_ram_ports_device->space(m_bus->m_ram_ports_space); }
+
private:
void set_bus(univ_bus_device &bus);
diff --git a/src/mame/drivers/intellec4.cpp b/src/mame/drivers/intellec4.cpp
index affdac7d5c6..f4c0eee7b51 100644
--- a/src/mame/drivers/intellec4.cpp
+++ b/src/mame/drivers/intellec4.cpp
@@ -23,19 +23,29 @@
MOD 40 jumpered to provide TEST line control rather than 4040 STOP
and SINGLE STEP features.
+ The MOD 40 repurposes the pins originally used for ROM 1 input port
+ lines to expose stop/interrupt request/acknowledge. It's pretty
+ obvious that the MOD 40 was designed as a minimal modification. A
+ set of X1 display LEDs would have been fantastic, as it would show the
+ contents of the accumulator with the 4040 CPU, while the X3 LEDs really
+ aren't very useful. However, since the 4004 just echoes M2 during X1,
+ the control board has no provision for latching data during this cycle
+ and would have required significant layout changes to cater for this.
+
Set the terminal for 110 1/8/N/2 to talk to the monitor.
It only accepts uppercase letters, digits, comma, and carriage return.
Paper tape reader run/stop is sent to RTS on the serial port.
TODO:
* Default terminal serial settings
- * 4702A programmer
* Universal slot cards
* Image device for paper tape reader?
* Expose general-purpose I/O?
*/
#include "emu.h"
+#include "machine/imm6_76.h"
+
#include "bus/intellec4/intellec4.h"
#include "bus/rs232/rs232.h"
#include "cpu/mcs40/mcs40.h"
@@ -60,9 +70,15 @@ public:
DECLARE_WRITE8_MEMBER(pm_write);
DECLARE_READ8_MEMBER(rom0_in);
+ DECLARE_READ8_MEMBER(rom2_in);
+ DECLARE_READ8_MEMBER(rom3_in);
DECLARE_READ8_MEMBER(rome_in);
DECLARE_READ8_MEMBER(romf_in);
+ DECLARE_WRITE8_MEMBER(rom0_out);
+ DECLARE_WRITE8_MEMBER(rom1_out);
+ DECLARE_WRITE8_MEMBER(rom2_out);
+ DECLARE_WRITE8_MEMBER(rom3_out);
DECLARE_WRITE8_MEMBER(rome_out);
DECLARE_WRITE8_MEMBER(romf_out);
@@ -84,6 +100,8 @@ public:
DECLARE_INPUT_CHANGED_MEMBER(sw_load);
DECLARE_INPUT_CHANGED_MEMBER(sw_cma_enable);
DECLARE_INPUT_CHANGED_MEMBER(sw_cma_write);
+ DECLARE_INPUT_CHANGED_MEMBER(sw_prgm_pwr);
+ DECLARE_INPUT_CHANGED_MEMBER(sw_do_enable);
protected:
intellec4_state(machine_config const &mconfig, device_type type, char const *tag)
@@ -94,11 +112,13 @@ protected:
, m_sw_mode(*this, "MODE")
, m_program_banks(*this, "prgbank")
, m_rom_port_banks(*this, "rpbank")
+ , m_prom_programmer(*this, "promprg")
, m_tty(*this, "tty")
, m_memory(*this, "memory"), m_status(*this, "status")
, m_sw_control(*this, "CONTROL")
, m_sw_addr_data(*this, "ADDRDAT")
, m_sw_passes(*this, "PASSES")
+ , m_sw_prom_prgm(*this, "PROM")
{
}
@@ -134,7 +154,10 @@ private:
BIT_SW_INCR,
BIT_SW_LOAD,
BIT_SW_CMA_ENABLE,
- BIT_SW_CMA_WRITE
+ BIT_SW_CMA_WRITE,
+
+ BIT_SW_PRGM_PWR = 0,
+ BIT_SW_DATA_OUT_ENABLE
};
TIMER_CALLBACK_MEMBER(reset_expired);
@@ -151,10 +174,12 @@ private:
void reset_panel();
required_device<address_map_bank_device> m_program_banks, m_rom_port_banks;
+ required_device<intel_imm6_76_device> m_prom_programmer;
required_device<rs232_port_device> m_tty;
required_shared_ptr<u8> m_memory, m_status;
required_ioport m_sw_control, m_sw_addr_data, m_sw_passes;
+ required_ioport m_sw_prom_prgm;
emu_timer *m_reset_timer = nullptr;
@@ -162,6 +187,9 @@ private:
u8 m_ram_page = 0U, m_ram_data = 0U;
bool m_ram_write = false;
+ // PROM programmer
+ u8 m_prom_addr = 0U, m_prom_data = 0U;
+
// control board state
bool m_cpu_reset = false;
bool m_ff_prg_mode[3] = { false, false, false };
@@ -207,7 +235,10 @@ ADDRESS_MAP_START(intellec4_rom_port_banks, mcs40_cpu_device_base::AS_ROM_PORTS,
ADDRESS_MAP_UNMAP_LOW
// 0x0000...0x07ff MON
- AM_RANGE(0x0000, 0x000f) AM_MIRROR(0x1f00) AM_READ(rom0_in)
+ AM_RANGE(0x0000, 0x000f) AM_MIRROR(0x1f00) AM_READWRITE(rom0_in, rom0_out)
+ AM_RANGE(0x0010, 0x001f) AM_MIRROR(0x1f00) AM_WRITE(rom1_out)
+ AM_RANGE(0x0020, 0x002f) AM_MIRROR(0x1f00) AM_READWRITE(rom2_in, rom2_out)
+ AM_RANGE(0x0030, 0x003f) AM_MIRROR(0x1f00) AM_READWRITE(rom3_in, rom3_out)
AM_RANGE(0x00e0, 0x00ef) AM_MIRROR(0x1f00) AM_READWRITE(rome_in, rome_out)
AM_RANGE(0x00f0, 0x00ff) AM_MIRROR(0x1f00) AM_READWRITE(romf_in, romf_out)
@@ -273,6 +304,8 @@ MACHINE_CONFIG_START(intellec4)
MCFG_ADDRESS_MAP_BANK_ADDRBUS_WIDTH(14)
MCFG_ADDRESS_MAP_BANK_STRIDE(0x1000)
+ MCFG_DEVICE_ADD("promprg", INTEL_IMM6_76, 0)
+
MCFG_RS232_PORT_ADD("tty", default_rs232_devices, "terminal")
MCFG_DEVICE_ADD("bus", INTELLEC4_UNIV_BUS, 518000. / 7)
@@ -339,6 +372,12 @@ INPUT_PORTS_START(intellec4)
PORT_BIT( 0x0002, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 1")
PORT_BIT( 0x0004, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 2")
PORT_BIT( 0x0008, IP_ACTIVE_HIGH, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PASSES 3")
+
+ PORT_START("PROM")
+ PORT_BIT( 0x0001, IP_ACTIVE_LOW, IPT_KEYPAD ) PORT_TOGGLE PORT_NAME("PRGM PROM PWR") PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_prgm_pwr, nullptr)
+ PORT_CONFNAME( 0x0002, 0x0002, "PROM PROGRAMMER DATA OUT ENABLE" ) PORT_CHANGED_MEMBER(DEVICE_SELF, intellec4_state, sw_do_enable, nullptr)
+ PORT_CONFSETTING( 0x0000, DEF_STR(Off) )
+ PORT_CONFSETTING( 0x0002, DEF_STR(On) )
INPUT_PORTS_END
@@ -464,6 +503,18 @@ READ8_MEMBER(intellec4_state::rom0_in)
return m_tty->rxd_r() ? 0x00U : 0x01U;
}
+READ8_MEMBER(intellec4_state::rom2_in)
+{
+ // lower nybble of PROM programmer data
+ return m_prom_programmer->do_r() & 0x0fU;
+}
+
+READ8_MEMBER(intellec4_state::rom3_in)
+{
+ // upper nybble of PROM programmer data
+ return (m_prom_programmer->do_r() >> 4) & 0x0fU;
+}
+
READ8_MEMBER(intellec4_state::rome_in)
{
// upper nybble of RAM data latch
@@ -476,6 +527,34 @@ READ8_MEMBER(intellec4_state::romf_in)
return m_ram_data & 0x0fU;
}
+WRITE8_MEMBER(intellec4_state::rom0_out)
+{
+ // lower nybble of PROM programmer address
+ m_prom_addr = (m_prom_addr & 0xf0U) | (data & 0x0fU);
+ m_prom_programmer->a_w(m_prom_addr);
+}
+
+WRITE8_MEMBER(intellec4_state::rom1_out)
+{
+ // upper nybble of PROM programmer address
+ m_prom_addr = (m_prom_addr & 0x0fU) | ((data << 4) & 0xf0U);
+ m_prom_programmer->a_w(m_prom_addr);
+}
+
+WRITE8_MEMBER(intellec4_state::rom2_out)
+{
+ // lower nybble of PROM programmer data
+ m_prom_data = (m_prom_data & 0xf0U) | (data & 0x0fU);
+ m_prom_programmer->di_w(m_prom_data);
+}
+
+WRITE8_MEMBER(intellec4_state::rom3_out)
+{
+ // upper nybble of PROM programmer data
+ m_prom_data = (m_prom_data & 0x0fU) | ((data << 4) & 0xf0U);
+ m_prom_programmer->di_w(m_prom_data);
+}
+
WRITE8_MEMBER(intellec4_state::rome_out)
{
// bit 0 of this port enables program memory write
@@ -498,6 +577,10 @@ WRITE8_MEMBER(intellec4_state::ram1_out)
{
// bit 0 of this port controls the paper tape motor (0 = stop, 1 = run)
m_tty->write_rts(BIT(~data, 0));
+
+ // bits 1 and 2 of this port enable PROM write
+ m_prom_programmer->r_w_a(BIT(~data, 1));
+ m_prom_programmer->r_w(BIT(~data, 2));
}
@@ -628,6 +711,16 @@ INPUT_CHANGED_MEMBER(intellec4_state::sw_cma_write)
m_cma_write = m_cma_enable;
}
+INPUT_CHANGED_MEMBER(intellec4_state::sw_prgm_pwr)
+{
+ m_prom_programmer->prgm_prom_pwr(newval);
+}
+
+INPUT_CHANGED_MEMBER(intellec4_state::sw_do_enable)
+{
+ m_prom_programmer->data_out_enable(newval);
+}
+
/*----------------------------------
driver_device implementation
@@ -641,6 +734,9 @@ void intellec4_state::driver_start()
save_item(NAME(m_ram_data));
save_item(NAME(m_ram_write));
+ save_item(NAME(m_prom_addr));
+ save_item(NAME(m_prom_data));
+
save_item(NAME(m_cpu_reset));
save_item(NAME(m_ff_prg_mode));
@@ -705,6 +801,13 @@ void intellec4_state::driver_reset()
m_bus->cpu_reset_in(m_cpu_reset ? 0 : 1);
m_bus->reset_4002_in((m_cpu_reset && m_sw_reset_mode) ? 0 : 1);
+ // set up the PROM programmer
+ ioport_value const sw_prom_prgm(m_sw_prom_prgm->read());
+ m_prom_programmer->data_out_enable(BIT(sw_prom_prgm, BIT_SW_DATA_OUT_ENABLE));
+ m_prom_programmer->data_in_positive(1); // not jumpered in, onboard pullup
+ m_prom_programmer->data_out_positive(0); // jumpered to ground
+ m_prom_programmer->prgm_prom_pwr(BIT(sw_prom_prgm, BIT_SW_PRGM_PWR));
+
// set front panel LEDs
machine().output().set_value("led_status_ptr_valid", m_pointer_valid);
machine().output().set_value("led_status_search", m_search_complete);
diff --git a/src/mame/layout/intlc44.lay b/src/mame/layout/intlc44.lay
index 1bc1f7d3853..ffbf6e5f005 100644
--- a/src/mame/layout/intlc44.lay
+++ b/src/mame/layout/intlc44.lay
@@ -74,6 +74,9 @@ Intel INTELLEC® 4/MOD 40 layout
<element name="label_ram"><text string="RAM"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_prom"><text string="PROM"><color red="1.0" green="1.0" blue="1.0" /></text></element>
+ <element name="label_prgm"><text string="PRGM"><color red="1.0" green="1.0" blue="1.0" /></text></element>
+ <element name="label_pwr"><text string="PWR"><color red="1.0" green="1.0" blue="1.0" /></text></element>
+
<element name="label_passes"><text string="PASSES"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_run"><text string="RUN"><color red="1.0" green="1.0" blue="1.0" /></text></element>
@@ -114,6 +117,13 @@ Intel INTELLEC® 4/MOD 40 layout
<text string="LAST RAM/ROM POINTER" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
+ <element name="label_on">
+ <text string="ON" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
+ </element>
+ <element name="label_off">
+ <text string="OFF" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
+ </element>
+
<element name="label_enable">
<text string="ENABLE" align="2"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
@@ -244,6 +254,12 @@ Intel INTELLEC® 4/MOD 40 layout
<cpanel element="label_ram"><bounds left="604" top="230" right="629" bottom="237" /></cpanel>
<cpanel element="label_prom"><bounds left="635" top="230" right="660" bottom="237" /></cpanel>
+ <cpanel element="label_prgm"><bounds left="728" top="214" right="753" bottom="221" /></cpanel>
+ <cpanel element="label_prom"><bounds left="728" top="222" right="753" bottom="229" /></cpanel>
+ <cpanel element="label_pwr"><bounds left="728" top="230" right="753" bottom="237" /></cpanel>
+ <cpanel element="label_on"><bounds left="758" top="239" right="814" bottom="246" /></cpanel>
+ <cpanel element="label_off"><bounds left="758" top="279" right="814" bottom="287" /></cpanel>
+
<cpanel element="label_pass_counter"><bounds left="72" top="296" right="194" bottom="304" /></cpanel>
<cpanel element="label_passes"><bounds left="74" top="304" right="192" bottom="311" /></cpanel>
<cpanel element="label_3"><bounds left="74" top="312" right="99" bottom="319" /></cpanel>
@@ -467,6 +483,10 @@ Intel INTELLEC® 4/MOD 40 layout
<bounds left="635" top="240" right="660" bottom="286" />
</cpanel>
+ <cpanel element="switch" inputtag="PROM" inputmask="0x0001">
+ <bounds left="728" top="240" right="753" bottom="286" />
+ </cpanel>
+
<cpanel element="switch" inputtag="PASSES" inputmask="0x08">
<bounds left="74" top="322" right="99" bottom="368" />
</cpanel>
@@ -614,6 +634,12 @@ Intel INTELLEC® 4/MOD 40 layout
<cpanel element="label_ram"><bounds left="604" top="230" right="629" bottom="237" /></cpanel>
<cpanel element="label_prom"><bounds left="635" top="230" right="660" bottom="237" /></cpanel>
+ <cpanel element="label_prgm"><bounds left="728" top="214" right="753" bottom="221" /></cpanel>
+ <cpanel element="label_prom"><bounds left="728" top="222" right="753" bottom="229" /></cpanel>
+ <cpanel element="label_pwr"><bounds left="728" top="230" right="753" bottom="237" /></cpanel>
+ <cpanel element="label_on"><bounds left="758" top="239" right="814" bottom="246" /></cpanel>
+ <cpanel element="label_off"><bounds left="758" top="279" right="814" bottom="287" /></cpanel>
+
<cpanel element="label_pass_counter"><bounds left="72" top="296" right="194" bottom="304" /></cpanel>
<cpanel element="label_passes"><bounds left="74" top="304" right="192" bottom="311" /></cpanel>
<cpanel element="label_3"><bounds left="74" top="312" right="99" bottom="319" /></cpanel>
@@ -837,6 +863,10 @@ Intel INTELLEC® 4/MOD 40 layout
<bounds left="635" top="240" right="660" bottom="286" />
</cpanel>
+ <cpanel element="switch" inputtag="PROM" inputmask="0x0001">
+ <bounds left="728" top="240" right="753" bottom="286" />
+ </cpanel>
+
<cpanel element="switch" inputtag="PASSES" inputmask="0x08">
<bounds left="74" top="322" right="99" bottom="368" />
</cpanel>
diff --git a/src/mame/layout/intlc440.lay b/src/mame/layout/intlc440.lay
index 27627e335d0..bf11a718128 100644
--- a/src/mame/layout/intlc440.lay
+++ b/src/mame/layout/intlc440.lay
@@ -73,6 +73,9 @@ Intel INTELLEC® 4/MOD 40 layout
<element name="label_ram"><text string="RAM"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_prom"><text string="PROM"><color red="1.0" green="1.0" blue="1.0" /></text></element>
+ <element name="label_prgm"><text string="PRGM"><color red="1.0" green="1.0" blue="1.0" /></text></element>
+ <element name="label_pwr"><text string="PWR"><color red="1.0" green="1.0" blue="1.0" /></text></element>
+
<element name="label_passes"><text string="PASSES"><color red="1.0" green="1.0" blue="1.0" /></text></element>
<element name="label_run"><text string="RUN"><color red="1.0" green="1.0" blue="1.0" /></text></element>
@@ -113,6 +116,13 @@ Intel INTELLEC® 4/MOD 40 layout
<text string="LAST RAM/ROM POINTER" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
+ <element name="label_on">
+ <text string="ON" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
+ </element>
+ <element name="label_off">
+ <text string="OFF" align="1"><color red="1.0" green="1.0" blue="1.0" /></text>
+ </element>
+
<element name="label_enable">
<text string="ENABLE" align="2"><color red="1.0" green="1.0" blue="1.0" /></text>
</element>
@@ -240,6 +250,12 @@ Intel INTELLEC® 4/MOD 40 layout
<cpanel element="label_ram"><bounds left="604" top="230" right="629" bottom="237" /></cpanel>
<cpanel element="label_prom"><bounds left="635" top="230" right="660" bottom="237" /></cpanel>
+ <cpanel element="label_prgm"><bounds left="728" top="214" right="753" bottom="221" /></cpanel>
+ <cpanel element="label_prom"><bounds left="728" top="222" right="753" bottom="229" /></cpanel>
+ <cpanel element="label_pwr"><bounds left="728" top="230" right="753" bottom="237" /></cpanel>
+ <cpanel element="label_on"><bounds left="758" top="239" right="814" bottom="246" /></cpanel>
+ <cpanel element="label_off"><bounds left="758" top="279" right="814" bottom="287" /></cpanel>
+
<cpanel element="label_pass_counter"><bounds left="72" top="296" right="194" bottom="304" /></cpanel>
<cpanel element="label_passes"><bounds left="74" top="304" right="192" bottom="311" /></cpanel>
<cpanel element="label_3"><bounds left="74" top="312" right="99" bottom="319" /></cpanel>
@@ -462,6 +478,10 @@ Intel INTELLEC® 4/MOD 40 layout
<bounds left="635" top="240" right="660" bottom="286" />
</cpanel>
+ <cpanel element="switch" inputtag="PROM" inputmask="0x0001">
+ <bounds left="728" top="240" right="753" bottom="286" />
+ </cpanel>
+
<cpanel element="switch" inputtag="PASSES" inputmask="0x08">
<bounds left="74" top="322" right="99" bottom="368" />
</cpanel>
@@ -609,6 +629,12 @@ Intel INTELLEC® 4/MOD 40 layout
<cpanel element="label_ram"><bounds left="604" top="230" right="629" bottom="237" /></cpanel>
<cpanel element="label_prom"><bounds left="635" top="230" right="660" bottom="237" /></cpanel>
+ <cpanel element="label_prgm"><bounds left="728" top="214" right="753" bottom="221" /></cpanel>
+ <cpanel element="label_prom"><bounds left="728" top="222" right="753" bottom="229" /></cpanel>
+ <cpanel element="label_pwr"><bounds left="728" top="230" right="753" bottom="237" /></cpanel>
+ <cpanel element="label_on"><bounds left="758" top="239" right="814" bottom="246" /></cpanel>
+ <cpanel element="label_off"><bounds left="758" top="279" right="814" bottom="287" /></cpanel>
+
<cpanel element="label_pass_counter"><bounds left="72" top="296" right="194" bottom="304" /></cpanel>
<cpanel element="label_passes"><bounds left="74" top="304" right="192" bottom="311" /></cpanel>
<cpanel element="label_3"><bounds left="74" top="312" right="99" bottom="319" /></cpanel>
@@ -831,6 +857,10 @@ Intel INTELLEC® 4/MOD 40 layout
<bounds left="635" top="240" right="660" bottom="286" />
</cpanel>
+ <cpanel element="switch" inputtag="PROM" inputmask="0x0001">
+ <bounds left="728" top="240" right="753" bottom="286" />
+ </cpanel>
+
<cpanel element="switch" inputtag="PASSES" inputmask="0x08">
<bounds left="74" top="322" right="99" bottom="368" />
</cpanel>
diff --git a/src/mame/machine/imm6_76.cpp b/src/mame/machine/imm6_76.cpp
new file mode 100644
index 00000000000..b30a1c5e9da
--- /dev/null
+++ b/src/mame/machine/imm6_76.cpp
@@ -0,0 +1,204 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+#include "emu.h"
+#include "imm6_76.h"
+
+#include <algorithm>
+
+
+DEFINE_DEVICE_TYPE(INTEL_IMM6_76, intel_imm6_76_device, "imm6_76", "Intel imm6-76 PROM programmer")
+
+
+intel_imm6_76_device::intel_imm6_76_device(
+ machine_config const &mconfig,
+ char const *tag,
+ device_t *owner,
+ uint32_t clock)
+ : device_t(mconfig, INTEL_IMM6_76, tag, owner, clock)
+ , device_image_interface(mconfig, *this)
+ , m_cycle_tmr(nullptr), m_cycle_a_tmr(nullptr), m_prg_tmr(nullptr)
+ , m_di(0U), m_a(0U)
+ , m_do_enable(true), m_di_pos(true), m_do_pos(true)
+ , m_r_w(false), m_r_w_a(false)
+ , m_prgm_pwr(false)
+ , m_cycle(false), m_cycle_a(false), m_prg(false)
+{
+ std::fill(std::begin(m_data), std::end(m_data), 0U);
+}
+
+
+void intel_imm6_76_device::device_start()
+{
+ m_cycle_tmr = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(intel_imm6_76_device::cycle_expired), this));
+ m_cycle_a_tmr = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(intel_imm6_76_device::cycle_a_expired), this));
+ m_prg_tmr = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(intel_imm6_76_device::prg_expired), this));
+
+ save_item(NAME(m_data));
+ save_item(NAME(m_di));
+ save_item(NAME(m_a));
+ save_item(NAME(m_do_enable));
+ save_item(NAME(m_di_pos));
+ save_item(NAME(m_do_pos));
+ save_item(NAME(m_r_w));
+ save_item(NAME(m_r_w_a));
+ save_item(NAME(m_prgm_pwr));
+ save_item(NAME(m_cycle));
+ save_item(NAME(m_cycle_a));
+ save_item(NAME(m_prg));
+}
+
+
+image_init_result intel_imm6_76_device::call_load()
+{
+ if (length() != ARRAY_LENGTH(m_data))
+ return image_init_result::FAIL;
+ else if (fread(m_data, ARRAY_LENGTH(m_data)) != ARRAY_LENGTH(m_data))
+ return image_init_result::FAIL;
+ else
+ return image_init_result::PASS;
+}
+
+image_init_result intel_imm6_76_device::call_create(int format_type, util::option_resolution *format_options)
+{
+ std::fill(std::begin(m_data), std::end(m_data), 0U);
+ if (fwrite(m_data, ARRAY_LENGTH(m_data)) != ARRAY_LENGTH(m_data))
+ return image_init_result::FAIL;
+ else
+ return image_init_result::PASS;
+}
+
+void intel_imm6_76_device::call_unload()
+{
+ if (!is_readonly())
+ {
+ fseek(0, SEEK_SET);
+ fwrite(m_data, ARRAY_LENGTH(m_data));
+ }
+ std::fill(std::begin(m_data), std::end(m_data), 0U);
+}
+
+
+void intel_imm6_76_device::di_w(u8 data)
+{
+ if (data != m_di)
+ {
+ if (m_prg)
+ logerror("data inputs changed during write - PROM damage likely (%02X -> %02X)\n", m_di, data);
+ m_di = data;
+ }
+}
+
+void intel_imm6_76_device::a_w(u8 data)
+{
+ if (data != m_a)
+ {
+ if (m_prg)
+ logerror("address changed during write - PROM damage likely (%02X -> %02X)\n", m_a, data);
+ m_a = data;
+ }
+}
+
+u8 intel_imm6_76_device::do_r() const
+{
+ if (!m_do_enable)
+ {
+ return 0xffU;
+ }
+ else
+ {
+ u8 const data(!m_prg ? m_data[m_a] : m_di_pos ? ~m_di : m_di);
+ return m_do_pos ? ~data : data;
+ }
+}
+
+DECLARE_WRITE_LINE_MEMBER(intel_imm6_76_device::data_out_enable)
+{
+ m_do_enable = bool(state);
+}
+
+DECLARE_WRITE_LINE_MEMBER(intel_imm6_76_device::data_in_positive)
+{
+ if (bool(state) != m_di_pos)
+ {
+ if (m_prg)
+ logerror("input polarity changed during write - PROM damage likely (%c -> %c)\n", m_di_pos ? '+' : '-', bool(state) ? '+' : '-');
+ m_di_pos = bool(state);
+ }
+}
+
+DECLARE_WRITE_LINE_MEMBER(intel_imm6_76_device::data_out_positive)
+{
+ m_do_pos = !bool(state);
+}
+
+WRITE_LINE_MEMBER(intel_imm6_76_device::r_w)
+{
+ if (!m_r_w && !bool(state) && !m_cycle)
+ {
+ // half 9602 with Rx = 20kΩ, Cx = 22µF, 1N914 across capacitor
+ // wired to prevent re-triggering
+ m_cycle_tmr->adjust(attotime::from_msec(150));
+ trigger_prg();
+ m_cycle = true;
+ }
+ m_r_w = !bool(state);
+}
+
+WRITE_LINE_MEMBER(intel_imm6_76_device::r_w_a)
+{
+ if (!m_r_w_a && !bool(state) && !m_cycle_a)
+ {
+ // half 9602 with Rx = 20kΩ, Cx = 2.2µF, 1N914 across capacitor
+ // wired to prevent re-triggering
+ m_cycle_a_tmr->adjust(attotime::from_msec(15));
+ trigger_prg();
+ m_cycle_a = true;
+ }
+ m_r_w_a = !bool(state);
+}
+
+WRITE_LINE_MEMBER(intel_imm6_76_device::prgm_prom_pwr)
+{
+ if (!bool(state) != m_prgm_pwr)
+ {
+ if (m_prg)
+ logerror("programming power %s during write - PROM damage likely\n", bool(state) ? "disabled" : "enabled");
+ m_prgm_pwr = !bool(state);
+ }
+}
+
+
+TIMER_CALLBACK_MEMBER(intel_imm6_76_device::cycle_expired)
+{
+ m_cycle = false;
+}
+
+TIMER_CALLBACK_MEMBER(intel_imm6_76_device::cycle_a_expired)
+{
+ m_cycle_a = false;
+}
+
+TIMER_CALLBACK_MEMBER(intel_imm6_76_device::prg_expired)
+{
+ m_prg = false;
+}
+
+
+void intel_imm6_76_device::trigger_prg()
+{
+ if (!m_cycle && !m_cycle_a)
+ {
+ // half 9602 with Rx = 4.7kΩ + 20kΩ trimpot, Cx = 1.0µF
+ m_prg_tmr->adjust(attotime::from_usec(3250));
+ if (!m_prg)
+ {
+ if (m_prgm_pwr && is_loaded())
+ m_data[m_a] |= m_di_pos ? ~m_di : m_di; // PROM can't be erased electronically
+ m_prg = true;
+ }
+ else
+ {
+ logerror("write re-triggered - PROM damage likely\n");
+ }
+ }
+}
diff --git a/src/mame/machine/imm6_76.h b/src/mame/machine/imm6_76.h
new file mode 100644
index 00000000000..cd21f72c024
--- /dev/null
+++ b/src/mame/machine/imm6_76.h
@@ -0,0 +1,203 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/*
+Intel imm6-76 PROM programmer
+
+Simple programmer for 1602/1702 and 1602A/1702A 256x8 static PMOS
+UVEPROMs (1602/1602A have a metal lid preventing erasure but are
+otherwise identical to 1702/1702A). Used in the INTELLEC® 4 and
+INTELLEC® 8 development systems.
+
+P1 universal edge connector (only used for power)
+
+ 1 2
+ GND 3 4 GND
+ 5 6
+ 7 8
+ 9 10
+ 11 12
+ 13 14
+ 15 16
+ 17 18
+ 19 20
+ 21 22
+ 23 24
+ 25 26
+ 27 28
+ 29 30
+ 31 32
+ 33 34
+ 35 36
+ 37 38
+ 39 40
+ 41 42
+ -10V 43 44 -10V
+ 45 46
+ 47 48
+ 49 50
+ 51 52
+ 53 54
+ 55 56
+ 57 58
+ 59 60
+ 61 62
+ 63 64
+ 65 66
+ 67 68
+ 69 70
+ 71 72
+ 73 74
+ 75 76
+ 77 78
+ 79 80
+ 81 82
+ 83 84
+ 85 86
+ 87 88
+ 89 90
+ 91 92
+ 93 94
+ 95 96
+ 97 98
+ +5V 99 100 +5V
+
+
+J1 40-pin IDC connector (data/control)
+
+ DI1 1 2 A0
+ DI2 3 4 A1
+ DI3 5 6 A2
+ DI4 7 8 A3
+ DI5 9 10 A4
+ DI6 11 12 A5
+ DI7 13 14 A6
+ DI8 15 16 A7
+ DO1 17 18
+ DO2 19 20
+ DO3 21 22
+ DO4 23 24 DATA OUT ENABLE
+ DO5 25 26 DATA IN + TRUE
+ DO6 27 28 /DATA OUT + TRUE
+ DO7 29 30 R/W (1702)
+ DO8 31 32 R/W A (1702A)
+ GND 33 34 GND
+ GND 35 36 GND
+ GND 37 38 GND
+ GND 39 40 GND
+
+
+J2 40-pin IDC connector (to PROM socket)
+
+ D1 1 2 A0
+ D2 3 4 A1
+ D3 5 6 A2
+ D4 7 8 A3
+ D5 9 10 A4
+ D6 11 11 A5
+ D7 13 12 A6
+ D8 15 16 A7
+ GND 17 18 GND
+ Vccs 19 20 Vccs
+ Vdd 21 22 Vdd
+ Vgg 23 24 /CS
+ Vbb 25 26 PRGM
+ 27 28
+ 29 30
+ 31 32
+ GND 33 34 GND
+ 35 36
+ 37 38
+ 39 40
+
+
+J3 Six-pin connector on flying leads
+
+ 50V AC 1 2
+ 50V AC 3 4 +80V DC
+/PRGM PROM PWR 5 6 GND
+
+
+DO are open-collector TTL outputs with 5.6kΩ pullups.
+
+DATA OUT ENABLE has an onboard 5.6kΩ pullup. Driving it low disables DO
+outputs.
+
+DATA IN + TRUE (if jumpered in) has a 5.6kΩ pullup and is XORed with the
+DI inputs. It should be pulled low in systems that use negative logic.
+It is not jumpered in when used with INTELLEC 4 or INTELLEC 8 systems.
+
+/DATA OUT + TRUE (if jumpered in) has a 5.6kΩ and is XORed with the PROM
+data outputs. It should be pulled low in systems that use positive
+logic. It is not jumpered in when used with INTELLEC 4 or INTELLEC 8
+systems, the line is tied low with an onboard jumper.
+
+/PRGM PROM PWR has a 27kΩ pullup to +80V DC and must be pulled low (to
+GND) for programming voltage to be applied to the PROM.
+
+When pulled low, R/W initiates a 2% programming duty cycle for writing
+to a 1602 or 1702 PROM (150 millisecond cycle time).
+
+When pulled low, RW A initiates a 20% programming duty cycle for writing
+to a 1602A or 1702A PROM (15 millisecond cycle time).
+*/
+#ifndef MAME_MACHINE_IMM6_76_H
+#define MAME_MACHINE_IMM6_76_H
+
+#pragma once
+
+
+class intel_imm6_76_device : public device_t, public device_image_interface
+{
+public:
+ intel_imm6_76_device(machine_config const &mconfig, char const *tag, device_t *owner, uint32_t clock);
+
+ // device_image_interface implementation
+ virtual image_init_result call_load() override;
+ virtual image_init_result call_create(int format_type, util::option_resolution *format_options) override;
+ virtual void call_unload() override;
+
+ // device_image_interface static info
+ virtual iodevice_t image_type() const override { return IO_ROM; }
+ virtual bool is_readable() const override { return true; }
+ virtual bool is_writeable() const override { return true; }
+ virtual bool is_creatable() const override { return true; }
+ virtual bool must_be_loaded() const override { return false; }
+ virtual bool is_reset_on_load() const override { return false; }
+ virtual char const *file_extensions() const override { return "rom,bin"; }
+ virtual char const *custom_instance_name() const override { return "promimage"; }
+ virtual char const *custom_brief_instance_name() const override { return "prom"; }
+
+ void di_w(u8 data);
+ void a_w(u8 data);
+ u8 do_r() const;
+ DECLARE_WRITE_LINE_MEMBER(data_out_enable); // 1 = asserted
+ DECLARE_WRITE_LINE_MEMBER(data_in_positive); // 1 = asserted
+ DECLARE_WRITE_LINE_MEMBER(data_out_positive); // 0 = asserted
+ DECLARE_WRITE_LINE_MEMBER(r_w); // 1 = read, 0 = write
+ DECLARE_WRITE_LINE_MEMBER(r_w_a); // 1 = read, 0 = write
+ DECLARE_WRITE_LINE_MEMBER(prgm_prom_pwr); // 0 = asserted
+
+protected:
+ // device_t implementation
+ virtual void device_start() override;
+
+private:
+ TIMER_CALLBACK_MEMBER(cycle_expired);
+ TIMER_CALLBACK_MEMBER(cycle_a_expired);
+ TIMER_CALLBACK_MEMBER(prg_expired);
+
+ void trigger_prg();
+
+ emu_timer *m_cycle_tmr, *m_cycle_a_tmr, *m_prg_tmr;
+
+ u8 m_data[256];
+ u8 m_di, m_a;
+ bool m_do_enable, m_di_pos, m_do_pos;
+ bool m_r_w, m_r_w_a;
+ bool m_prgm_pwr;
+ bool m_cycle, m_cycle_a, m_prg;
+};
+
+DECLARE_DEVICE_TYPE(INTEL_IMM6_76, intel_imm6_76_device)
+
+#endif // MAME_MACHINE_IMM6_76_H