summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author AJR <ajrhacker@users.noreply.github.com>2021-05-31 22:13:44 -0400
committer AJR <ajrhacker@users.noreply.github.com>2021-05-31 22:15:36 -0400
commit57d621860c8b3b53d41471d4fd10e5877bfddad6 (patch)
tree7afc2841e691c71c6ceaf1b061e3119501555eff
parentda24cd8d9b0aa6c6ce25f53f597472971ee4786b (diff)
tek410x: Add keyboard [AJR, Bitsavers]
* tek440x: Hook up 410X keyboard to pass boot test * mc68681: Wait until transmitter is actually empty to set TxEMT
-rw-r--r--scripts/target/mame/mess.lua2
-rw-r--r--src/devices/machine/mc68681.cpp22
-rw-r--r--src/mame/drivers/tek410x.cpp238
-rw-r--r--src/mame/drivers/tek440x.cpp87
-rw-r--r--src/mame/machine/tek410x_kbd.cpp385
-rw-r--r--src/mame/machine/tek410x_kbd.h69
6 files changed, 751 insertions, 52 deletions
diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua
index 84cd754592c..c034b9b403b 100644
--- a/scripts/target/mame/mess.lua
+++ b/scripts/target/mame/mess.lua
@@ -3908,6 +3908,8 @@ files {
MAME_DIR .. "src/mame/drivers/tek440x.cpp",
MAME_DIR .. "src/mame/drivers/tekigw.cpp",
MAME_DIR .. "src/mame/drivers/tekxp33x.cpp",
+ MAME_DIR .. "src/mame/machine/tek410x_kbd.cpp",
+ MAME_DIR .. "src/mame/machine/tek410x_kbd.h",
}
createMESSProjects(_target, _subtarget, "telenova")
diff --git a/src/devices/machine/mc68681.cpp b/src/devices/machine/mc68681.cpp
index 6ab7e2f69fe..645b7c9deba 100644
--- a/src/devices/machine/mc68681.cpp
+++ b/src/devices/machine/mc68681.cpp
@@ -1269,7 +1269,7 @@ void duart_channel::tra_complete()
{
//printf("%s ch %d Tx complete\n", tag(), m_ch);
tx_ready = 1;
- SR |= STATUS_TRANSMITTER_READY;
+ SR |= STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY;
if (m_ch == 0)
m_uart->set_ISR_bits(INT_TXRDYA);
@@ -1319,24 +1319,13 @@ void duart_channel::update_interrupts()
switch (MR2 & 0xc0) // what mode are we in?
{
case 0x00: // normal mode
- if (tx_enabled)
- SR |= STATUS_TRANSMITTER_EMPTY;
- else
- SR &= ~STATUS_TRANSMITTER_EMPTY;
break;
case 0x40: // automatic echo mode
- SR &= ~STATUS_TRANSMITTER_EMPTY;
SR &= ~STATUS_TRANSMITTER_READY;
break;
case 0x80: // local loopback mode
- if (tx_enabled)
- SR |= STATUS_TRANSMITTER_EMPTY;
- else
- SR &= ~STATUS_TRANSMITTER_EMPTY;
break;
case 0xc0: // remote loopback mode
- // write me, what the txrdy/txemt regs do for remote loopback mode is undocumented afaik, for now just clear both
- SR &= ~STATUS_TRANSMITTER_EMPTY;
SR &= ~STATUS_TRANSMITTER_READY;
break;
}
@@ -1571,7 +1560,8 @@ void duart_channel::write_CR(uint8_t data)
break;
case 3: /* Reset channel transmitter */
tx_enabled = 0;
- SR &= ~STATUS_TRANSMITTER_READY;
+ tx_ready = 0;
+ SR &= ~(STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY);
if (m_ch == 0)
m_uart->clear_ISR_bits(INT_TXRDYA);
else
@@ -1636,7 +1626,7 @@ void duart_channel::write_CR(uint8_t data)
{
tx_enabled = 1;
tx_ready = 1;
- SR |= STATUS_TRANSMITTER_READY;
+ SR |= STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY;
if (m_ch == 0)
m_uart->set_ISR_bits(INT_TXRDYA);
else
@@ -1646,7 +1636,7 @@ void duart_channel::write_CR(uint8_t data)
{
tx_enabled = 0;
tx_ready = 0;
- SR &= ~STATUS_TRANSMITTER_READY;
+ SR &= ~(STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY);
if (m_ch == 0)
m_uart->clear_ISR_bits(INT_TXRDYA);
else
@@ -1668,7 +1658,7 @@ void duart_channel::write_TX(uint8_t data)
//printf("%s ch %d Tx %c [%02x]\n", tag(), m_ch, isprint(data) ? data : ' ', data);
tx_ready = 0;
- SR &= ~STATUS_TRANSMITTER_READY;
+ SR &= ~(STATUS_TRANSMITTER_READY | STATUS_TRANSMITTER_EMPTY);
if (m_ch == 0)
m_uart->clear_ISR_bits(INT_TXRDYA);
diff --git a/src/mame/drivers/tek410x.cpp b/src/mame/drivers/tek410x.cpp
index d32f726d3ee..0008fc7a21e 100644
--- a/src/mame/drivers/tek410x.cpp
+++ b/src/mame/drivers/tek410x.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:Curt Coder
+// copyright-holders:Curt Coder, AJR
/***************************************************************************
Tektronix 4107A/4109A
@@ -8,19 +8,11 @@
****************************************************************************/
-/*
-
- TODO:
-
- - everything
-
-*/
-
-
#include "emu.h"
#include "cpu/i86/i186.h"
#include "machine/i8255.h"
#include "machine/mc68681.h"
+#include "machine/tek410x_kbd.h"
#include "video/crt9007.h"
#include "emupal.h"
#include "screen.h"
@@ -31,7 +23,19 @@ class tek4107a_state : public driver_device
public:
tek4107a_state(const machine_config &mconfig, device_type type, const char *tag)
: driver_device(mconfig, type, tag)
+ , m_duart(*this, "duart%u", 0U)
+ , m_keyboard(*this, "keyboard")
, m_vpac(*this, "vpac")
+ , m_ppi_pc(0)
+ , m_kb_rdata(true)
+ , m_kb_tdata(true)
+ , m_kb_rclamp(false)
+ , m_graphics_control(0)
+ , m_alpha_control(0)
+ , m_x_position(0)
+ , m_y_position(0)
+ , m_x_cursor(0)
+ , m_y_cursor(0)
{ }
void tek4109a(machine_config &config);
@@ -46,10 +50,45 @@ private:
u8 vpac_r(offs_t offset);
+ u16 nmi_enable_r();
+ u16 nmi_disable_r();
+ u16 system_reset_r();
+
+ void ppi_pc_w(u8 data);
+ DECLARE_WRITE_LINE_MEMBER(kb_rdata_w);
+ DECLARE_WRITE_LINE_MEMBER(kb_tdata_w);
+ DECLARE_WRITE_LINE_MEMBER(kb_rclamp_w);
+
+ void xpos_w(u16 data);
+ void ypos_w(u16 data);
+ void xcur_w(u16 data);
+ void ycur_w(u16 data);
+ void tbwin_w(u16 data);
+ u16 gcntl_r();
+ void gcntl_w(u16 data);
+ u16 acntl_r();
+ void acntl_w(u16 data);
+ u16 test_r();
+ u8 font_r();
+
void tek4107a_io(address_map &map);
void tek4107a_mem(address_map &map);
+ required_device_array<scn2681_device, 2> m_duart;
+ required_device<tek410x_keyboard_device> m_keyboard;
required_device<crt9007_device> m_vpac;
+
+ u8 m_ppi_pc;
+ bool m_kb_rdata;
+ bool m_kb_tdata;
+ bool m_kb_rclamp;
+
+ u16 m_graphics_control;
+ u16 m_alpha_control;
+ u16 m_x_position;
+ u16 m_y_position;
+ u16 m_x_cursor;
+ u16 m_y_cursor;
};
u8 tek4107a_state::vpac_r(offs_t offset)
@@ -57,22 +96,150 @@ u8 tek4107a_state::vpac_r(offs_t offset)
return m_vpac->read(offset + 0x20);
}
+u16 tek4107a_state::nmi_enable_r()
+{
+ // TODO
+ return 0;
+}
+
+u16 tek4107a_state::nmi_disable_r()
+{
+ // TODO
+ return 0;
+}
+
+u16 tek4107a_state::system_reset_r()
+{
+ // TODO
+ return 0;
+}
+
+void tek4107a_state::ppi_pc_w(u8 data)
+{
+ if (!m_kb_rclamp && BIT(m_ppi_pc, 2) != BIT(data, 2))
+ m_keyboard->kdo_w(!BIT(data, 2) || m_kb_tdata);
+
+ m_ppi_pc = data;
+}
+
+WRITE_LINE_MEMBER(tek4107a_state::kb_rdata_w)
+{
+ m_kb_rdata = state;
+ if (!m_kb_rclamp)
+ m_duart[0]->rx_a_w(state);
+}
+
+WRITE_LINE_MEMBER(tek4107a_state::kb_rclamp_w)
+{
+ if (m_kb_rclamp != !state)
+ {
+ m_kb_rclamp = !state;
+
+ // Clamp RXDA to 1 and KBRDATA to 0 when DUART asserts RxRDYA
+ if (m_kb_tdata || !BIT(m_ppi_pc, 2))
+ m_keyboard->kdo_w(state);
+ m_duart[0]->rx_a_w(state ? m_kb_rdata : 1);
+ }
+}
+
+WRITE_LINE_MEMBER(tek4107a_state::kb_tdata_w)
+{
+ if (m_kb_tdata != state)
+ {
+ m_kb_tdata = state;
+
+ m_duart[0]->ip4_w(!state);
+ if (BIT(m_ppi_pc, 2) && m_kb_rdata && !m_kb_rclamp)
+ m_keyboard->kdo_w(state);
+ }
+}
+
+void tek4107a_state::xpos_w(u16 data)
+{
+ m_x_position = data & 0x03ff;
+}
+
+void tek4107a_state::ypos_w(u16 data)
+{
+ m_y_position = data & 0x01ff;
+}
+
+void tek4107a_state::xcur_w(u16 data)
+{
+ m_x_cursor = data & 0x03ff;
+}
+
+void tek4107a_state::ycur_w(u16 data)
+{
+ m_y_cursor = data & 0x01ff;
+}
+
+void tek4107a_state::tbwin_w(u16 data)
+{
+ // TODO
+}
+
+u16 tek4107a_state::gcntl_r()
+{
+ return m_graphics_control;
+}
+
+void tek4107a_state::gcntl_w(u16 data)
+{
+ m_graphics_control = data;
+}
+
+u16 tek4107a_state::acntl_r()
+{
+ return m_alpha_control;
+}
+
+void tek4107a_state::acntl_w(u16 data)
+{
+ m_alpha_control = data;
+}
+
+u16 tek4107a_state::test_r()
+{
+ // TODO
+ return 0;
+}
+
+u8 tek4107a_state::font_r()
+{
+ // TODO
+ return 0;
+}
+
+
/* Memory Maps */
void tek4107a_state::tek4107a_mem(address_map &map)
{
- map(0x00000, 0x7ffff).ram();
+ map(0x00000, 0x3ffff).ram();
+ map(0x40000, 0x7ffff).ram().share("gfxram");
map(0x80000, 0xbffff).rom().region("firmware", 0);
map(0xf0000, 0xfffff).rom().region("firmware", 0x30000);
}
void tek4107a_state::tek4107a_io(address_map &map)
{
- map(0x0000, 0x001f).rw("duart0", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
- map(0x0000, 0x001f).rw("duart1", FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0xff00);
+ map(0x0000, 0x001f).rw(m_duart[0], FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0x00ff);
+ map(0x0000, 0x001f).rw(m_duart[1], FUNC(scn2681_device::read), FUNC(scn2681_device::write)).umask16(0xff00);
map(0x0080, 0x00bf).r(FUNC(tek4107a_state::vpac_r)).w(m_vpac, FUNC(crt9007_device::write)).umask16(0x00ff);
- map(0x00ce, 0x00cf).ram();
+ map(0x00c0, 0x00c1).w(FUNC(tek4107a_state::xpos_w));
+ map(0x00c2, 0x00c3).w(FUNC(tek4107a_state::ypos_w));
+ map(0x00c4, 0x00c5).w(FUNC(tek4107a_state::xcur_w));
+ map(0x00c6, 0x00c7).w(FUNC(tek4107a_state::ycur_w));
+ map(0x00c8, 0x00c9).r(FUNC(tek4107a_state::test_r));
+ map(0x00ca, 0x00ca).r(FUNC(tek4107a_state::font_r));
+ map(0x00ca, 0x00cb).w(FUNC(tek4107a_state::tbwin_w));
+ map(0x00cc, 0x00cd).rw(FUNC(tek4107a_state::gcntl_r), FUNC(tek4107a_state::gcntl_w));
+ map(0x00ce, 0x00cf).rw(FUNC(tek4107a_state::acntl_r), FUNC(tek4107a_state::acntl_w));
map(0x0100, 0x0107).rw("ppi", FUNC(i8255_device::read), FUNC(i8255_device::write)).umask16(0xff00);
+ map(0x0200, 0x0201).r(FUNC(tek4107a_state::nmi_enable_r));
+ map(0x0280, 0x0281).r(FUNC(tek4107a_state::system_reset_r));
+ map(0x0300, 0x0301).r(FUNC(tek4107a_state::nmi_disable_r));
}
/* Input Ports */
@@ -84,6 +251,12 @@ INPUT_PORTS_END
void tek4107a_state::video_start()
{
+ save_item(NAME(m_graphics_control));
+ save_item(NAME(m_alpha_control));
+ save_item(NAME(m_x_position));
+ save_item(NAME(m_y_position));
+ save_item(NAME(m_x_cursor));
+ save_item(NAME(m_y_cursor));
}
u32 tek4107a_state::screen_update(screen_device &screen, bitmap_rgb32 &bitmap, const rectangle &cliprect)
@@ -110,6 +283,10 @@ GFXDECODE_END
void tek4107a_state::machine_start()
{
+ save_item(NAME(m_ppi_pc));
+ save_item(NAME(m_kb_rdata));
+ save_item(NAME(m_kb_tdata));
+ save_item(NAME(m_kb_rclamp));
}
/* Machine Driver */
@@ -117,25 +294,34 @@ void tek4107a_state::machine_start()
void tek4107a_state::tek4107a(machine_config &config)
{
/* basic machine hardware */
- i80186_cpu_device &maincpu(I80186(config, "maincpu", 21000000));
+ i80186_cpu_device &maincpu(I80186(config, "maincpu", 14.7456_MHz_XTAL));
maincpu.set_addrmap(AS_PROGRAM, &tek4107a_state::tek4107a_mem);
maincpu.set_addrmap(AS_IO, &tek4107a_state::tek4107a_io);
- /* video hardware */
- screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
- screen.set_raw(25200000, 800, 0, 640, 525, 0, 480);
- screen.set_screen_update(FUNC(tek4107a_state::screen_update));
+ SCN2681(config, m_duart[0], 14.7456_MHz_XTAL / 4);
+ m_duart[0]->irq_cb().set("maincpu", FUNC(i80186_cpu_device::int0_w));
+ m_duart[0]->outport_cb().set_inputline("maincpu", INPUT_LINE_NMI).bit(5).invert(); // RxRDYB
+ m_duart[0]->outport_cb().append(FUNC(tek4107a_state::kb_rclamp_w)).bit(4);
+ m_duart[0]->outport_cb().append(m_keyboard, FUNC(tek410x_keyboard_device::reset_w)).bit(3);
+ m_duart[0]->a_tx_cb().set(m_keyboard, FUNC(tek410x_keyboard_device::kdi_w));
- scn2681_device &duart0(SCN2681(config, "duart0", 3686400));
- duart0.irq_cb().set("maincpu", FUNC(i80186_cpu_device::int0_w));
- duart0.outport_cb().set_inputline("maincpu", INPUT_LINE_NMI).bit(5).invert(); // RxRDYB
+ SCN2681(config, m_duart[1], 14.7456_MHz_XTAL / 4);
+ m_duart[1]->irq_cb().set("maincpu", FUNC(i80186_cpu_device::int2_w));
- scn2681_device &duart1(SCN2681(config, "duart1", 3686400));
- duart1.irq_cb().set("maincpu", FUNC(i80186_cpu_device::int2_w));
+ i8255_device &ppi(I8255(config, "ppi"));
+ ppi.in_pb_callback().set_constant(0x30);
+ ppi.out_pc_callback().set(FUNC(tek4107a_state::ppi_pc_w));
- I8255(config, "ppi").in_pb_callback().set_constant(0x30);
+ TEK410X_KEYBOARD(config, m_keyboard);
+ m_keyboard->tdata_callback().set(FUNC(tek4107a_state::kb_tdata_w));
+ m_keyboard->rdata_callback().set(FUNC(tek4107a_state::kb_rdata_w));
+
+ /* video hardware */
+ screen_device &screen(SCREEN(config, "screen", SCREEN_TYPE_RASTER));
+ screen.set_raw(25.2_MHz_XTAL, 800, 0, 640, 525, 0, 480);
+ screen.set_screen_update(FUNC(tek4107a_state::screen_update));
- CRT9007(config, m_vpac, 25200000 / 8);
+ CRT9007(config, m_vpac, 25.2_MHz_XTAL / 8);
m_vpac->set_screen("screen");
m_vpac->set_character_width(8);
m_vpac->int_callback().set("maincpu", FUNC(i80186_cpu_device::int1_w));
diff --git a/src/mame/drivers/tek440x.cpp b/src/mame/drivers/tek440x.cpp
index e04123889ee..a9a744e0963 100644
--- a/src/mame/drivers/tek440x.cpp
+++ b/src/mame/drivers/tek440x.cpp
@@ -1,5 +1,5 @@
// license:BSD-3-Clause
-// copyright-holders:R. Belmont
+// copyright-holders:R. Belmont, AJR
/***************************************************************************
Tektronix 440x "AI Workstations"
@@ -45,12 +45,12 @@
#include "bus/rs232/rs232.h"
#include "cpu/m68000/m68000.h"
#include "cpu/m6502/m6502.h"
-#include "cpu/mcs48/mcs48.h"
#include "machine/am9513.h"
#include "machine/bankdev.h"
#include "machine/mos6551.h" // debug tty
#include "machine/mc146818.h"
#include "machine/mc68681.h"
+#include "machine/tek410x_kbd.h"
#include "sound/sn76496.h"
#include "emupal.h"
#include "screen.h"
@@ -65,10 +65,17 @@ public:
m_maincpu(*this, "maincpu"),
m_fdccpu(*this, "fdccpu"),
m_vm(*this, "vm"),
+ m_duart(*this, "duart"),
+ m_keyboard(*this, "keyboard"),
m_snsnd(*this, "snsnd"),
m_prom(*this, "maincpu"),
m_mainram(*this, "mainram"),
- m_vram(*this, "vram")
+ m_vram(*this, "vram"),
+ m_boot(false),
+ m_kb_rdata(true),
+ m_kb_tdata(true),
+ m_kb_rclamp(false),
+ m_kb_loop(false)
{ }
void tek4404(machine_config &config);
@@ -81,6 +88,11 @@ private:
u16 memory_r(offs_t offset, u16 mem_mask);
void memory_w(offs_t offset, u16 data, u16 mem_mask);
void sound_w(u8 data);
+ void diag_w(u8 data);
+
+ DECLARE_WRITE_LINE_MEMBER(kb_rdata_w);
+ DECLARE_WRITE_LINE_MEMBER(kb_tdata_w);
+ DECLARE_WRITE_LINE_MEMBER(kb_rclamp_w);
void logical_map(address_map &map);
void physical_map(address_map &map);
@@ -89,12 +101,18 @@ private:
required_device<m68010_device> m_maincpu;
required_device<m6502_device> m_fdccpu;
required_device<address_map_bank_device> m_vm;
+ required_device<mc68681_device> m_duart;
+ required_device<tek410x_keyboard_device> m_keyboard;
required_device<sn76496_device> m_snsnd;
required_region_ptr<u16> m_prom;
required_shared_ptr<u16> m_mainram;
required_shared_ptr<u16> m_vram;
bool m_boot;
+ bool m_kb_rdata;
+ bool m_kb_tdata;
+ bool m_kb_rclamp;
+ bool m_kb_loop;
};
/*************************************
@@ -106,6 +124,10 @@ private:
void tek440x_state::machine_start()
{
save_item(NAME(m_boot));
+ save_item(NAME(m_kb_rdata));
+ save_item(NAME(m_kb_tdata));
+ save_item(NAME(m_kb_rclamp));
+ save_item(NAME(m_kb_loop));
}
@@ -119,6 +141,8 @@ void tek440x_state::machine_start()
void tek440x_state::machine_reset()
{
m_boot = true;
+ diag_w(0);
+ m_keyboard->kdo_w(1);
}
@@ -178,6 +202,46 @@ void tek440x_state::sound_w(u8 data)
m_boot = false;
}
+void tek440x_state::diag_w(u8 data)
+{
+ if (!m_kb_rclamp && m_kb_loop != BIT(data, 7))
+ m_keyboard->kdo_w(!BIT(data, 7) || m_kb_tdata);
+
+ m_kb_loop = BIT(data, 7);
+}
+
+WRITE_LINE_MEMBER(tek440x_state::kb_rdata_w)
+{
+ m_kb_rdata = state;
+ if (!m_kb_rclamp)
+ m_duart->rx_a_w(state);
+}
+
+WRITE_LINE_MEMBER(tek440x_state::kb_rclamp_w)
+{
+ if (m_kb_rclamp != !state)
+ {
+ m_kb_rclamp = !state;
+
+ // Clamp RXDA to 1 and KBRDATA to 0 when DUART asserts RxRDYA
+ if (m_kb_tdata || !m_kb_loop)
+ m_keyboard->kdo_w(state);
+ m_duart->rx_a_w(state ? m_kb_rdata : 1);
+ }
+}
+
+WRITE_LINE_MEMBER(tek440x_state::kb_tdata_w)
+{
+ if (m_kb_tdata != state)
+ {
+ m_kb_tdata = state;
+
+ m_duart->ip4_w(!state);
+ if (m_kb_loop && m_kb_rdata && !m_kb_rclamp)
+ m_keyboard->kdo_w(state);
+ }
+}
+
void tek440x_state::logical_map(address_map &map)
{
map(0x000000, 0x7fffff).rw(FUNC(tek440x_state::memory_r), FUNC(tek440x_state::memory_w));
@@ -195,9 +259,10 @@ void tek440x_state::physical_map(address_map &map)
map(0x788000, 0x788000).w(FUNC(tek440x_state::sound_w));
// 78a000-78bfff: NS32081 FPU
map(0x78c000, 0x78c007).rw("aica", FUNC(mos6551_device::read), FUNC(mos6551_device::write)).umask16(0xff00);
+ map(0x7b0000, 0x7b0000).w(FUNC(tek440x_state::diag_w));
// 7b1000-7b2fff: diagnostic registers
// 7b2000-7b3fff: Centronics printer data
- map(0x7b4000, 0x7b401f).rw("duart", FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0xff00);
+ map(0x7b4000, 0x7b401f).rw(m_duart, FUNC(mc68681_device::read), FUNC(mc68681_device::write)).umask16(0xff00);
// 7b6000-7b7fff: Mouse
map(0x7b8000, 0x7b8003).mirror(0x100).rw("timer", FUNC(am9513_device::read16), FUNC(am9513_device::write16));
// 7ba000-7bbfff: MC146818 RTC
@@ -254,10 +319,15 @@ void tek440x_state::tek4404(machine_config &config)
aica.txd_handler().set("rs232", FUNC(rs232_port_device::write_txd));
aica.irq_handler().set_inputline(m_maincpu, M68K_IRQ_7);
- mc68681_device &duart(MC68681(config, "duart", 3.6864_MHz_XTAL));
- duart.irq_cb().set_inputline(m_maincpu, M68K_IRQ_5);
+ MC68681(config, m_duart, 3.6864_MHz_XTAL);
+ m_duart->irq_cb().set_inputline(m_maincpu, M68K_IRQ_5); // auto-vectored
+ m_duart->outport_cb().set(FUNC(tek440x_state::kb_rclamp_w)).bit(4);
+ m_duart->outport_cb().append(m_keyboard, FUNC(tek410x_keyboard_device::reset_w)).bit(3);
+ m_duart->a_tx_cb().set(m_keyboard, FUNC(tek410x_keyboard_device::kdi_w));
- I8048(config, "kbdmcu", 4.608_MHz_XTAL).set_disable();
+ TEK410X_KEYBOARD(config, m_keyboard);
+ m_keyboard->tdata_callback().set(FUNC(tek440x_state::kb_tdata_w));
+ m_keyboard->rdata_callback().set(FUNC(tek440x_state::kb_rdata_w));
AM9513(config, "timer", 40_MHz_XTAL / 4 / 10); // from CPU E output
@@ -293,9 +363,6 @@ ROM_START( tek4404 )
ROM_REGION( 0x2000, "scsimfm", 0 )
ROM_LOAD( "scsi_mfm.bin", 0x000000, 0x002000, CRC(b4293435) SHA1(5e2b96c19c4f5c63a5afa2de504d29fe64a4c908) )
-
- ROM_REGION( 0x400, "kbdmcu", 0 )
- ROM_LOAD( "keytronic_8x48.bin", 0x000, 0x400, NO_DUMP )
ROM_END
/*************************************
diff --git a/src/mame/machine/tek410x_kbd.cpp b/src/mame/machine/tek410x_kbd.cpp
new file mode 100644
index 00000000000..4d0d1beea46
--- /dev/null
+++ b/src/mame/machine/tek410x_kbd.cpp
@@ -0,0 +1,385 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+/**********************************************************************
+
+ This emulates the standard serial keyboard used with Tektronix's
+ 4105, 4106, 4107 and 4109 terminals, including the trademark
+ octagonal "joy disk". The 4404 keyboard is also compatible.
+
+ The hardware was designed by the Keytronic Corporation, and uses
+ their 20-pin key strobe driver (22-0950-003) and input receiver
+ (22-0908-003) ASICs.
+
+ The 8048/8748's DB pins are connected to drivers for up to 5 LEDs,
+ but only one of these LEDs (CR9, on the Caps Lock key) is actually
+ populated.
+
+ A different but apparently backward-compatible keyboard was used
+ with the CX4107 and CX4109, based on a 8051 CPU with space for an
+ external program ROM and adding an onboard speaker as well as many
+ extra keys and an IBM 3279-style layout.
+
+ Hexadecimal table of press codes (release codes are the same + 80):
+
+ 00 Caps Lock
+ 01 Left Shift
+ 02 Right Shift
+ 03 Ctrl
+ 04 DEras/SEras
+ 05 Break
+ 06 Back Space
+ 07 Tab
+ 08 Line Feed
+ 09 Return
+ 0A Esc
+ 0B Space Bar
+ 0C " '
+ 0D < ,
+ 0E _ -
+ 0F > .
+ 10 ? /
+ 11 ) 0
+ 12 ! 1
+ 13 @ 2
+ 14 # 3
+ 15 $ 4
+ 16 % 5
+ 17 ^ 6
+ 18 & 7
+ 19 * 8
+ 1A ( 9
+ 1B : ;
+ 1C =
+ 1D A
+ 1E B
+ 1F C
+ 20 D
+ 21 E
+ 22 F
+ 23 G
+ 24 H
+ 25 I
+ 26 J
+ 27 K
+ 28 L
+ 29 M
+ 2A N
+ 2B O
+ 2C P
+ 2D Q
+ 2E R
+ 2F S
+ 30 T
+ 31 U
+ 32 V
+ 33 W
+ 34 X
+ 35 Y
+ 36 Z
+ 37 { [
+ 38 ` \
+ 39 } ]
+ 3A ~ |
+ 3B Rub Out
+ 3C Enter (keypad)
+ 3D , (keypad)
+ 3E - (keypad)
+ 3F . (keypad)
+ 40 0 (keypad)
+ 41 1 (keypad)
+ 42 2 (keypad)
+ 43 3 (keypad)
+ 44 4 (keypad)
+ 45 5 (keypad)
+ 46 6 (keypad)
+ 47 7 (keypad)
+ 48 8 (keypad)
+ 49 9 (keypad)
+ 4A F1
+ 4B F2
+ 4C F3
+ 4D F4
+ 4E F5
+ 4F F6
+ 50 F7
+ 51 F8
+ 52 GEras/Dialog
+ 53 Cancel/Setup
+ 54 DCopy/SCopy
+ 55 Menu
+ 56 Cursor Right (joystick)
+ 57 Cursor Up (joystick)
+ 58 Cursor Left (joystick)
+ 59 Cursor Down (joystick)
+
+ Additional press codes for CX keyboard only:
+
+ 5A Erase EOF
+ 5B Dup/PA1
+ 5C Reset/Dev Cncl
+ 5D Field Mark/PA2/_
+ 5E Attn Sys Req
+ 5F Cursr Sel/Clear
+ 60 < >
+ 61 Cursr Blink/Alt Cursr
+ 62 Ident
+ 63 Down Arrow
+ 64 Right Arrow/Right Double Arrow
+ 65 Left Arrow/Left Double Arrow
+ 66 Up Arrow
+ 67 Insert/Alpha
+ 68 Right Alt/Ctrl
+
+ The baud rate is 4800, with 8 data bits and no parity.
+
+**********************************************************************/
+
+#include "emu.h"
+#include "tek410x_kbd.h"
+
+// device type definition
+DEFINE_DEVICE_TYPE(TEK410X_KEYBOARD, tek410x_keyboard_device, "tek410x_kbd", "Tektronix 410X Standard Keyboard (119-1592-01)")
+
+tek410x_keyboard_device::tek410x_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock)
+ : device_t(mconfig, TEK410X_KEYBOARD, tag, owner, clock)
+ , m_mcu(*this, "mcu")
+ , m_key_matrix(*this, "X%u", 0U)
+ , m_config(*this, "CONFIG")
+ , m_tdata_callback(*this)
+ , m_rdata_callback(*this)
+ , m_select(0)
+ , m_p2_out(0)
+ , m_kdi(true)
+ , m_kdo(false)
+{
+}
+
+void tek410x_keyboard_device::device_resolve_objects()
+{
+ m_tdata_callback.resolve_safe();
+ m_rdata_callback.resolve_safe();
+}
+
+void tek410x_keyboard_device::device_start()
+{
+ save_item(NAME(m_select));
+ save_item(NAME(m_p2_out));
+ save_item(NAME(m_kdi));
+ save_item(NAME(m_kdo));
+}
+
+WRITE_LINE_MEMBER(tek410x_keyboard_device::kdi_w)
+{
+ m_kdi = state;
+ if (BIT(m_p2_out, 7))
+ {
+ m_mcu->set_input_line(MCS48_INPUT_IRQ, state ? CLEAR_LINE : ASSERT_LINE);
+ m_tdata_callback(state);
+ }
+}
+
+WRITE_LINE_MEMBER(tek410x_keyboard_device::kdo_w)
+{
+ m_kdo = state;
+ if (BIT(m_p2_out, 5))
+ m_rdata_callback(state);
+}
+
+WRITE_LINE_MEMBER(tek410x_keyboard_device::reset_w)
+{
+ m_mcu->set_input_line(INPUT_LINE_RESET, state ? CLEAR_LINE : ASSERT_LINE);
+ if (!state)
+ p2_w(0xff);
+}
+
+u8 tek410x_keyboard_device::p1_r()
+{
+ if (m_select < 12)
+ return m_key_matrix[m_select]->read();
+ else
+ return 0xff;
+}
+
+u8 tek410x_keyboard_device::p2_r()
+{
+ return 0xb0 | (m_kdo ? (m_p2_out & 0x20) << 1 : 0) | m_config->read();
+}
+
+void tek410x_keyboard_device::p2_w(u8 data)
+{
+ if (!BIT(data, 4))
+ m_select = m_mcu->p1_r() & 0x0f;
+
+ if (m_kdi && BIT(data, 7) != BIT(m_p2_out, 7))
+ {
+ m_tdata_callback(BIT(data, 7));
+ m_mcu->set_input_line(MCS48_INPUT_IRQ, BIT(data, 7) ? CLEAR_LINE : ASSERT_LINE);
+ }
+
+ if (m_kdo && BIT(data, 5) != BIT(m_p2_out, 5))
+ m_rdata_callback(BIT(data, 5));
+
+ m_p2_out = data;
+}
+
+void tek410x_keyboard_device::ext_map(address_map &map)
+{
+ map(0x00, 0xff).nopr();
+}
+
+static INPUT_PORTS_START(tek410x_keyboard)
+ PORT_START("X0")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(ENTER_PAD)) PORT_CODE(KEYCODE_ENTER_PAD)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(COMMA_PAD)) PORT_CODE(KEYCODE_PLUS_PAD)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(3_PAD)) PORT_CODE(KEYCODE_3_PAD)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(6_PAD)) PORT_CODE(KEYCODE_6_PAD)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(MINUS_PAD)) PORT_CODE(KEYCODE_MINUS_PAD)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(9_PAD)) PORT_CODE(KEYCODE_9_PAD)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DEL_PAD)) PORT_CODE(KEYCODE_DEL_PAD)
+
+ PORT_START("X1")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(4_PAD)) PORT_CODE(KEYCODE_4_PAD)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(2_PAD)) PORT_CODE(KEYCODE_2_PAD)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(5_PAD)) PORT_CODE(KEYCODE_5_PAD)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(7_PAD)) PORT_CODE(KEYCODE_7_PAD)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(1_PAD)) PORT_CODE(KEYCODE_1_PAD)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(8_PAD)) PORT_CODE(KEYCODE_8_PAD)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(0_PAD)) PORT_CODE(KEYCODE_0_PAD)
+
+ PORT_START("X2")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Break") //PORT_CODE(KEYCODE_XXX)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Line Feed") PORT_CHAR(0x0a) //PORT_CODE(KEYCODE_XXX)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Rub Out") PORT_CODE(KEYCODE_BACKSPACE)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_UNUSED)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(']') PORT_CHAR('}') PORT_CODE(KEYCODE_BACKSLASH)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_UNUSED)
+
+ PORT_START("X3")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Right Shift") PORT_CODE(KEYCODE_RSHIFT)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\\') PORT_CHAR('`') PORT_CODE(KEYCODE_OPENBRACE)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('\'') PORT_CHAR('"') PORT_CODE(KEYCODE_QUOTE)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Back Space") PORT_CHAR(0x08) PORT_CODE(KEYCODE_CLOSEBRACE)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('-') PORT_CHAR('_') PORT_CODE(KEYCODE_MINUS)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Return") PORT_CHAR(0x0d) PORT_CODE(KEYCODE_ENTER)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('=') PORT_CHAR('+') PORT_CODE(KEYCODE_EQUALS)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Space Bar") PORT_CHAR(' ') PORT_CODE(KEYCODE_SPACE)
+
+ PORT_START("X4")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(DOWN)) PORT_CODE(KEYCODE_DOWN)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(LEFT)) PORT_CODE(KEYCODE_LEFT)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F7)) PORT_CODE(KEYCODE_F11)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F2)) PORT_CODE(KEYCODE_F6)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F6)) PORT_CODE(KEYCODE_F10)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Setup Cancel") PORT_CODE(KEYCODE_F2)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F3)) PORT_CODE(KEYCODE_F7)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SCopy DCopy") PORT_CODE(KEYCODE_F3)
+
+ PORT_START("X5")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(RIGHT)) PORT_CODE(KEYCODE_RIGHT)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(UP)) PORT_CODE(KEYCODE_UP)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F8)) PORT_CODE(KEYCODE_F12)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F1)) PORT_CODE(KEYCODE_F5)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F5)) PORT_CODE(KEYCODE_F9)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Dialog GEras") PORT_CODE(KEYCODE_F1)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(UCHAR_MAMEKEY(F4)) PORT_CODE(KEYCODE_F8)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Menu") PORT_CODE(KEYCODE_F4)
+
+ PORT_START("X6")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("X") PORT_CHAR('x') PORT_CHAR('X') PORT_CODE(KEYCODE_X)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("W") PORT_CHAR('w') PORT_CHAR('W') PORT_CODE(KEYCODE_W)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("S") PORT_CHAR('s') PORT_CHAR('S') PORT_CODE(KEYCODE_S)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Q") PORT_CHAR('q') PORT_CHAR('Q') PORT_CODE(KEYCODE_Q)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('2') PORT_CHAR('@') PORT_CODE(KEYCODE_2)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("A") PORT_CHAR('a') PORT_CHAR('A') PORT_CODE(KEYCODE_A)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('1') PORT_CHAR('!') PORT_CODE(KEYCODE_1)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Z") PORT_CHAR('z') PORT_CHAR('Z') PORT_CODE(KEYCODE_Z)
+
+ PORT_START("X7")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Caps Lock") PORT_CHAR(UCHAR_MAMEKEY(CAPSLOCK)) PORT_CODE(KEYCODE_LCONTROL)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Esc") PORT_CHAR(0x1b) //PORT_CODE(KEYCODE_XXX)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Tab") PORT_CHAR(0x09) //PORT_CODE(KEYCODE_XXX)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('|') PORT_CHAR('~') PORT_CODE(KEYCODE_TAB)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("SEras DEras") //PORT_CODE(KEYCODE_XXX)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Ctrl") PORT_CHAR(UCHAR_SHIFT_2) PORT_CODE(KEYCODE_CAPSLOCK)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('[') PORT_CHAR('{') PORT_CODE(KEYCODE_TILDE)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Left Shift") PORT_CHAR(UCHAR_SHIFT_1) PORT_CODE(KEYCODE_LSHIFT)
+
+ PORT_START("X8")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("C") PORT_CHAR('c') PORT_CHAR('C') PORT_CODE(KEYCODE_C)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("E") PORT_CHAR('e') PORT_CHAR('E') PORT_CODE(KEYCODE_E)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("D") PORT_CHAR('d') PORT_CHAR('D') PORT_CODE(KEYCODE_D)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("R") PORT_CHAR('r') PORT_CHAR('R') PORT_CODE(KEYCODE_R)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('3') PORT_CHAR('#') PORT_CODE(KEYCODE_3)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("F") PORT_CHAR('f') PORT_CHAR('F') PORT_CODE(KEYCODE_F)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('4') PORT_CHAR('$') PORT_CODE(KEYCODE_4)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("V") PORT_CHAR('v') PORT_CHAR('V') PORT_CODE(KEYCODE_V)
+
+ PORT_START("X9")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("N") PORT_CHAR('n') PORT_CHAR('N') PORT_CODE(KEYCODE_N)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("Y") PORT_CHAR('y') PORT_CHAR('Y') PORT_CODE(KEYCODE_Y)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("H") PORT_CHAR('h') PORT_CHAR('H') PORT_CODE(KEYCODE_H)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("T") PORT_CHAR('t') PORT_CHAR('T') PORT_CODE(KEYCODE_T)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('6') PORT_CHAR('^') PORT_CODE(KEYCODE_6)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("G") PORT_CHAR('g') PORT_CHAR('G') PORT_CODE(KEYCODE_G)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('5') PORT_CHAR('%') PORT_CODE(KEYCODE_5)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("B") PORT_CHAR('b') PORT_CHAR('B') PORT_CODE(KEYCODE_B)
+
+ PORT_START("X10")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("M") PORT_CHAR('m') PORT_CHAR('M') PORT_CODE(KEYCODE_M)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("U") PORT_CHAR('u') PORT_CHAR('U') PORT_CODE(KEYCODE_U)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("J") PORT_CHAR('j') PORT_CHAR('J') PORT_CODE(KEYCODE_J)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("I") PORT_CHAR('i') PORT_CHAR('I') PORT_CODE(KEYCODE_I)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('7') PORT_CHAR('&') PORT_CODE(KEYCODE_7)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("K") PORT_CHAR('k') PORT_CHAR('K') PORT_CODE(KEYCODE_K)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('8') PORT_CHAR('*') PORT_CODE(KEYCODE_8)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(',') PORT_CHAR('>') PORT_CODE(KEYCODE_COMMA)
+
+ PORT_START("X11")
+ PORT_BIT(0x01, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('/') PORT_CHAR('?') PORT_CODE(KEYCODE_SLASH)
+ PORT_BIT(0x02, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("P") PORT_CHAR('p') PORT_CHAR('P') PORT_CODE(KEYCODE_P)
+ PORT_BIT(0x04, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR(';') PORT_CHAR(':') PORT_CODE(KEYCODE_COLON)
+ PORT_BIT(0x08, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("O") PORT_CHAR('o') PORT_CHAR('O') PORT_CODE(KEYCODE_O)
+ PORT_BIT(0x10, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('0') PORT_CHAR('(') PORT_CODE(KEYCODE_0)
+ PORT_BIT(0x20, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_NAME("L") PORT_CHAR('l') PORT_CHAR('L') PORT_CODE(KEYCODE_L)
+ PORT_BIT(0x40, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('9') PORT_CHAR(')') PORT_CODE(KEYCODE_9)
+ PORT_BIT(0x80, IP_ACTIVE_LOW, IPT_KEYBOARD) PORT_CHAR('.') PORT_CHAR('>') PORT_CODE(KEYCODE_STOP)
+
+ PORT_START("CONFIG")
+ PORT_DIPNAME(0x0f, 0x00, "Keyboard Type") PORT_DIPLOCATION("E:1,2,3,4")
+ PORT_DIPSETTING(0x00, "North America")
+ PORT_DIPSETTING(0x08, "United Kingdom (4A)")
+ PORT_DIPSETTING(0x04, "French (4B)")
+ PORT_DIPSETTING(0x02, "Swedish (4C)")
+ PORT_DIPSETTING(0x06, "Danish/Norwegian (4F)")
+ PORT_DIPSETTING(0x07, "German (4G)")
+ PORT_DIPSETTING(0x0a, "Katakana (4K)")
+INPUT_PORTS_END
+
+ioport_constructor tek410x_keyboard_device::device_input_ports() const
+{
+ return INPUT_PORTS_NAME(tek410x_keyboard);
+}
+
+void tek410x_keyboard_device::device_add_mconfig(machine_config &config)
+{
+ I8748(config, m_mcu, 4.608_MHz_XTAL);
+ m_mcu->set_addrmap(AS_IO, &tek410x_keyboard_device::ext_map);
+ m_mcu->bus_out_cb().set_output("led0").bit(0);
+ m_mcu->p1_in_cb().set(FUNC(tek410x_keyboard_device::p1_r));
+ m_mcu->p2_in_cb().set(FUNC(tek410x_keyboard_device::p2_r));
+ m_mcu->p2_out_cb().set(FUNC(tek410x_keyboard_device::p2_w));
+}
+
+ROM_START(tek410x_kbd)
+ ROM_REGION(0x400, "mcu", 0)
+ ROM_LOAD("473_8748.bin", 0x000, 0x400, CRC(371553a8) SHA1(165ffc2c0775c1a3c2cc3ec86fb05adc8e8bb3eb))
+ROM_END
+
+const tiny_rom_entry *tek410x_keyboard_device::device_rom_region() const
+{
+ return ROM_NAME(tek410x_kbd);
+}
diff --git a/src/mame/machine/tek410x_kbd.h b/src/mame/machine/tek410x_kbd.h
new file mode 100644
index 00000000000..095f7648094
--- /dev/null
+++ b/src/mame/machine/tek410x_kbd.h
@@ -0,0 +1,69 @@
+// license:BSD-3-Clause
+// copyright-holders:AJR
+
+#ifndef MAME_MACHINE_TEK410X_KEYBOARD_H
+#define MAME_MACHINE_TEK410X_KEYBOARD_H
+
+#pragma once
+
+#include "cpu/mcs48/mcs48.h"
+
+
+//**************************************************************************
+// TYPE DEFINITIONS
+//**************************************************************************
+
+// ======================> tek410x_keyboard_device
+
+class tek410x_keyboard_device : public device_t
+{
+public:
+ // device type constructor
+ tek410x_keyboard_device(const machine_config &mconfig, const char *tag, device_t *owner, u32 clock = 0);
+
+ // callback configuration
+ auto tdata_callback() { return m_tdata_callback.bind(); }
+ auto rdata_callback() { return m_rdata_callback.bind(); }
+
+ // line inputs
+ DECLARE_WRITE_LINE_MEMBER(kdi_w);
+ DECLARE_WRITE_LINE_MEMBER(kdo_w);
+ DECLARE_WRITE_LINE_MEMBER(reset_w);
+
+protected:
+ // device-level overrides
+ virtual void device_resolve_objects() override;
+ virtual void device_start() override;
+ virtual ioport_constructor device_input_ports() const override;
+ virtual void device_add_mconfig(machine_config &config) override;
+ virtual const tiny_rom_entry *device_rom_region() const override;
+
+private:
+ // MCU handlers
+ u8 p1_r();
+ u8 p2_r();
+ void p2_w(u8 data);
+
+ // address maps
+ void ext_map(address_map &map);
+
+ // object finders
+ required_device<mcs48_cpu_device> m_mcu;
+ required_ioport_array<12> m_key_matrix;
+ required_ioport m_config;
+
+ // output callbacks
+ devcb_write_line m_tdata_callback;
+ devcb_write_line m_rdata_callback;
+
+ // internal state
+ u8 m_select;
+ u8 m_p2_out;
+ bool m_kdi;
+ bool m_kdo;
+};
+
+// device type declaration
+DECLARE_DEVICE_TYPE(TEK410X_KEYBOARD, tek410x_keyboard_device)
+
+#endif // MAME_MACHINE_TEK410X_KEYBOARD_H