summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/z180
diff options
context:
space:
mode:
author Miodrag Milanović <mmicko@gmail.com>2022-06-06 10:48:41 +0200
committer GitHub <noreply@github.com>2022-06-06 10:48:41 +0200
commitb26c4ed848d1f5b9b2f28a8fee162e086874bdd1 (patch)
tree48cacfb6f10833c7e76b99145400d17c3972d726 /src/devices/cpu/z180
parent4a587fd7c1c512649320929f5e1fc3d44cc9c979 (diff)
Z180 ASCI (#9762)
New working machines ----------------------------------- Micromint SB180 [Miodrag Milanovic] -z180: implemented ASCI serial communication [Miodrag Milanovic] -tim011: hookup serial keyboard [Miodrag Milanovic] -20pacgal: Add support for terminal
Diffstat (limited to 'src/devices/cpu/z180')
-rw-r--r--src/devices/cpu/z180/z180.cpp256
-rw-r--r--src/devices/cpu/z180/z180.h26
-rw-r--r--src/devices/cpu/z180/z180asci.cpp702
-rw-r--r--src/devices/cpu/z180/z180asci.h201
4 files changed, 965 insertions, 220 deletions
diff --git a/src/devices/cpu/z180/z180.cpp b/src/devices/cpu/z180/z180.cpp
index 6c68b877bca..db7b4a3026b 100644
--- a/src/devices/cpu/z180/z180.cpp
+++ b/src/devices/cpu/z180/z180.cpp
@@ -93,16 +93,12 @@ z180_device::z180_device(const machine_config &mconfig, device_type type, const
, m_program_config("program", ENDIANNESS_LITTLE, 8, 20, 0, 16, 12, internal_map)
, m_io_config("io", ENDIANNESS_LITTLE, 8, 16, 0)
, m_decrypted_opcodes_config("opcodes", ENDIANNESS_LITTLE, 8, 20, 0, 16, 12, internal_map)
+ , m_asci(*this, "asci_%u", 0U)
, m_extended_io(extended_io)
, m_tend0_cb(*this)
, m_tend1_cb(*this)
{
// some arbitrary initial values
- m_asci_cntla[0] = m_asci_cntla[1] = 0;
- m_asci_cntlb[0] = m_asci_cntlb[1] = 0;
- m_asci_stat[0] = 0;
- m_asci_tdr[0] = m_asci_tdr[1] = 0;
- m_asci_rdr[0] = m_asci_rdr[1] = 0;
m_csio_trdr = 0;
m_tmdr[0].w = m_tmdr[1].w = 0;
m_rldr[0].w = m_rldr[1].w = 0xffff;
@@ -133,8 +129,6 @@ hd64180rp_device::hd64180rp_device(const machine_config &mconfig, const char *ta
z8s180_device::z8s180_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock)
: z180_device(mconfig, type, tag, owner, clock, false, address_map_constructor())
{
- // some arbitrary initial values
- m_asci_tc[0].w = m_asci_tc[1].w = 0;
}
z8s180_device::z8s180_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
@@ -242,60 +236,6 @@ bool z180_device::get_tend1()
#define _LY m_IY.b.l
-/* 00 ASCI control register A ch 0 */
-#define Z180_CNTLA0_MPE 0x80
-#define Z180_CNTLA0_RE 0x40
-#define Z180_CNTLA0_TE 0x20
-#define Z180_CNTLA0_RTS0 0x10
-#define Z180_CNTLA0_MPBR_EFR 0x08
-#define Z180_CNTLA0_MODE_DATA 0x04
-#define Z180_CNTLA0_MODE_PARITY 0x02
-#define Z180_CNTLA0_MODE_STOPB 0x01
-
-/* 01 ASCI control register A ch 1 */
-#define Z180_CNTLA1_MPE 0x80
-#define Z180_CNTLA1_RE 0x40
-#define Z180_CNTLA1_TE 0x20
-#define Z180_CNTLA1_CKA1D 0x10
-#define Z180_CNTLA1_MPBR_EFR 0x08
-#define Z180_CNTLA1_MODE 0x07
-
-/* 02 ASCI control register B ch 0 */
-#define Z180_CNTLB0_MPBT 0x80
-#define Z180_CNTLB0_MP 0x40
-#define Z180_CNTLB0_CTS_PS 0x20
-#define Z180_CNTLB0_PEO 0x10
-#define Z180_CNTLB0_DR 0x08
-#define Z180_CNTLB0_SS 0x07
-
-/* 03 ASCI control register B ch 1 */
-#define Z180_CNTLB1_MPBT 0x80
-#define Z180_CNTLB1_MP 0x40
-#define Z180_CNTLB1_CTS_PS 0x20
-#define Z180_CNTLB1_PEO 0x10
-#define Z180_CNTLB1_DR 0x08
-#define Z180_CNTLB1_SS 0x07
-
-/* 04 ASCI status register 0 (all bits read-only except RIE and TIE) */
-#define Z180_STAT0_RDRF 0x80
-#define Z180_STAT0_OVRN 0x40
-#define Z180_STAT0_PE 0x20
-#define Z180_STAT0_FE 0x10
-#define Z180_STAT0_RIE 0x08
-#define Z180_STAT0_DCD0 0x04
-#define Z180_STAT0_TDRE 0x02
-#define Z180_STAT0_TIE 0x01
-
-/* 05 ASCI status register 1 (all bits read-only except RIE, CTS1E and TIE) */
-#define Z180_STAT1_RDRF 0x80
-#define Z180_STAT1_OVRN 0x40
-#define Z180_STAT1_PE 0x20
-#define Z180_STAT1_FE 0x10
-#define Z180_STAT1_RIE 0x08
-#define Z180_STAT1_CTS1E 0x04
-#define Z180_STAT1_TDRE 0x02
-#define Z180_STAT1_TIE 0x01
-
/* 0a CSI/O control/status register (EF is read-only) */
#define Z180_CNTR_EF 0x80
#define Z180_CNTR_EIE 0x40
@@ -315,26 +255,6 @@ bool z180_device::get_tend1()
#define Z180_TCR_TDE1 0x02
#define Z180_TCR_TDE0 0x01
-/* 12 (Z8S180/Z8L180) ASCI extension control register 0 (break detect is read-only) */
-#define Z180_ASEXT0_DCD0 0x40
-#define Z180_ASEXT0_CTS0 0x20
-#define Z180_ASEXT0_X1_BIT_CLK0 0x10
-#define Z180_ASEXT0_BRG0_MODE 0x08
-#define Z180_ASEXT0_BRK_EN 0x04
-#define Z180_ASEXT0_BRK_DET 0x02
-#define Z180_ASEXT0_BRK_SEND 0x01
-
-#define Z180_ASEXT0_MASK 0x7f
-
-/* 13 (Z8S180/Z8L180) ASCI extension control register 1 (break detect is read-only) */
-#define Z180_ASEXT1_X1_BIT_CLK1 0x10
-#define Z180_ASEXT1_BRG1_MODE 0x08
-#define Z180_ASEXT1_BRK_EN 0x04
-#define Z180_ASEXT1_BRK_DET 0x02
-#define Z180_ASEXT1_BRK_SEND 0x01
-
-#define Z180_ASEXT1_MASK 0x1f
-
/* 1e (Z8S180/Z8L180) clock multiplier */
#define Z180_CMR_X2 0x80
#define Z180_CMR_LOW_NOISE 0x40
@@ -505,54 +425,28 @@ uint8_t z180_device::z180_internal_port_read(uint8_t port)
switch (port)
{
case 0x00:
- data = m_asci_cntla[0];
- LOG("Z180 CNTLA0 rd $%02x\n", data);
- break;
-
case 0x01:
- data = m_asci_cntla[1];
- LOG("Z180 CNTLA1 rd $%02x\n", data);
+ data = m_asci[port & 1]->cntla_r();
break;
case 0x02:
- data = m_asci_cntlb[0];
- LOG("Z180 CNTLB0 rd $%02x\n", data);
- break;
-
case 0x03:
- data = m_asci_cntlb[1];
- LOG("Z180 CNTLB1 rd $%02x\n", data);
+ data = m_asci[port & 1]->cntlb_r();
break;
case 0x04:
- data = m_asci_stat[0];
- data |= 0x02; // kludge for 20pacgal
- LOG("Z180 STAT0 rd $%02x\n", data);
- break;
-
case 0x05:
- data = m_asci_stat[1];
- LOG("Z180 STAT1 rd $%02x\n", data);
+ data = m_asci[port & 1]->stat_r();
break;
case 0x06:
- data = m_asci_tdr[0];
- LOG("Z180 TDR0 rd $%02x\n", data);
- break;
-
case 0x07:
- data = m_asci_tdr[1];
- LOG("Z180 TDR1 rd $%02x\n", data);
+ data = m_asci[port & 1]->tdr_r();
break;
case 0x08:
- data = m_asci_rdr[0];
- LOG("Z180 RDR0 rd $%02x\n", data);
- break;
-
case 0x09:
- data = m_asci_rdr[1];
- LOG("Z180 RDR1 rd $%02x\n", data);
+ data = m_asci[port & 1]->rdr_r();
break;
case 0x0a:
@@ -869,53 +763,28 @@ void z180_device::z180_internal_port_write(uint8_t port, uint8_t data)
switch (port)
{
case 0x00:
- LOG("Z180 CNTLA0 wr $%02x\n", data);
- m_asci_cntla[0] = data;
- break;
-
case 0x01:
- LOG("Z180 CNTLA1 wr $%02x\n", data);
- m_asci_cntla[1] = data;
+ m_asci[port & 1]->cntla_w(data);
break;
case 0x02:
- LOG("Z180 CNTLB0 wr $%02x\n", data);
- m_asci_cntlb[0] = data;
- break;
-
case 0x03:
- LOG("Z180 CNTLB1 wr $%02x\n", data);
- m_asci_cntlb[1] = data;
+ m_asci[port & 1]->cntlb_w(data);
break;
case 0x04:
- LOG("Z180 STAT0 wr $%02x ($%02x)\n", data, data & (Z180_STAT0_RIE | Z180_STAT0_TIE));
- m_asci_stat[0] = (m_asci_stat[0] & ~(Z180_STAT0_RIE | Z180_STAT0_TIE)) | (data & (Z180_STAT0_RIE | Z180_STAT0_TIE));
- break;
-
case 0x05:
- LOG("Z180 STAT1 wr $%02x ($%02x)\n", data, data & (Z180_STAT1_RIE | Z180_STAT1_CTS1E | Z180_STAT1_TIE));
- m_asci_stat[1] = (m_asci_stat[1] & ~(Z180_STAT1_RIE | Z180_STAT1_CTS1E | Z180_STAT1_TIE)) | (data & (Z180_STAT1_RIE | Z180_STAT1_CTS1E | Z180_STAT1_TIE));
+ m_asci[port & 1]->stat_w(data);
break;
case 0x06:
- LOG("Z180 TDR0 wr $%02x\n", data);
- m_asci_tdr[0] = data;
- break;
-
case 0x07:
- LOG("Z180 TDR1 wr $%02x\n", data);
- m_asci_tdr[1] = data;
+ m_asci[port & 1]->tdr_w(data);
break;
case 0x08:
- LOG("Z180 RDR0 wr $%02x\n", data);
- m_asci_rdr[0] = data;
- break;
-
case 0x09:
- LOG("Z180 RDR1 wr $%02x\n", data);
- m_asci_rdr[1] = data;
+ m_asci[port & 1]->rdr_w(data);
break;
case 0x0a:
@@ -1152,33 +1021,18 @@ uint8_t z8s180_device::z180_internal_port_read(uint8_t port)
switch (port)
{
case 0x12:
- data = m_asci_ext[0];
- LOG("Z180 ASEXT0 rd $%02x ($%02x)\n", data, m_asci_ext[0]);
- break;
-
case 0x13:
- data = m_asci_ext[1];
- LOG("Z180 ASEXT1 rd $%02x ($%02x)\n", data, m_asci_ext[1]);
+ data = m_asci[port & 1]->asext_r();
break;
case 0x1a:
- LOG("Z180 ASTC0L wr $%02x\n", data);
- m_asci_tc[0].b.l = data;
- break;
-
case 0x1b:
- LOG("Z180 ASTC0H wr $%02x\n", data);
- m_asci_tc[0].b.h = data;
+ data = m_asci[port & 1]->astcl_r();
break;
case 0x1c:
- LOG("Z180 ASTC1L wr $%02x\n", data);
- m_asci_tc[1].b.l = data;
- break;
-
case 0x1d:
- LOG("Z180 ASTC1H wr $%02x\n", data);
- m_asci_tc[1].b.h = data;
+ data = m_asci[port & 1]->astcl_r();
break;
case 0x1e:
@@ -1204,33 +1058,18 @@ void z8s180_device::z180_internal_port_write(uint8_t port, uint8_t data)
switch (port)
{
case 0x12:
- LOG("Z180 ASEXT0 wr $%02x ($%02x)\n", data, data & Z180_ASEXT0_MASK & ~Z180_ASEXT0_BRK_DET);
- m_asci_ext[0] = (m_asci_ext[0] & Z180_ASEXT0_BRK_DET) | (data & Z180_ASEXT0_MASK & ~Z180_ASEXT0_BRK_DET);
- break;
-
case 0x13:
- LOG("Z180 ASEXT1 wr $%02x ($%02x)\n", data, data & Z180_ASEXT1_MASK & ~Z180_ASEXT1_BRK_DET);
- m_asci_ext[1] = (m_asci_ext[1] & Z180_ASEXT1_BRK_DET) | (data & Z180_ASEXT1_MASK & ~Z180_ASEXT1_BRK_DET);
+ m_asci[port & 1]->asext_w(data);
break;
case 0x1a:
- data = m_asci_tc[0].b.l;
- LOG("Z180 ASTC0L rd $%02x ($%04x)\n", data, m_asci_tc[0].w);
- break;
-
case 0x1b:
- data = m_asci_tc[0].b.h;
- LOG("Z180 ASTC0H rd $%02x ($%04x)\n", data, m_asci_tc[0].w);
+ m_asci[port & 1]->astcl_w(data);
break;
case 0x1c:
- data = m_asci_tc[1].b.l;
- LOG("Z180 ASTC1L rd $%02x ($%04x)\n", data, m_asci_tc[1].w);
- break;
-
case 0x1d:
- data = m_asci_tc[1].b.h;
- LOG("Z180 ASTC1H rd $%02x ($%04x)\n", data, m_asci_tc[1].w);
+ m_asci[port & 1]->astch_w(data);
break;
case 0x1e:
@@ -1725,18 +1564,8 @@ void z180_device::device_start()
state_add(Z180_IOLINES, "IOLINES", m_ioltemp).mask(0xffffff).callimport();
- state_add(Z180_CNTLA0, "CNTLA0", m_asci_cntla[0]);
- state_add(Z180_CNTLB0, "CNTLB0", m_asci_cntlb[0]);
- state_add(Z180_STAT0, "STAT0", m_asci_stat[0]);
- state_add(Z180_TDR0, "TDR0", m_asci_tdr[0]);
- state_add(Z180_RDR0, "RDR0", m_asci_rdr[0]);
-
- state_add(Z180_CNTLA1, "CNTLA1", m_asci_cntla[1]);
- state_add(Z180_CNTLB1, "CNTLB1", m_asci_cntlb[1]);
- state_add(Z180_STAT1, "STAT1", m_asci_stat[1]);
- state_add(Z180_TDR1, "TDR1", m_asci_tdr[1]);
- state_add(Z180_RDR1, "RDR1", m_asci_rdr[1]);
-
+ m_asci[0]->state_add(*this);
+ m_asci[1]->state_add(*this);
state_add(Z180_CNTR, "CNTR", m_csio_cntr).mask(Z180_CNTR_MASK);
state_add(Z180_TRDR, "TRDR", m_csio_trdr);
@@ -1798,11 +1627,6 @@ void z180_device::device_start()
save_item(NAME(m_tmdrh));
save_item(NAME(m_tmdr_latch));
- save_item(NAME(m_asci_cntla));
- save_item(NAME(m_asci_cntlb));
- save_item(NAME(m_asci_stat));
- save_item(NAME(m_asci_tdr));
- save_item(NAME(m_asci_rdr));
save_item(NAME(m_csio_cntr));
save_item(NAME(m_csio_trdr));
save_item(NAME(m_tmdr[0].w));
@@ -1841,18 +1665,9 @@ void z8s180_device::device_start()
{
z180_device::device_start();
- state_add(Z180_ASEXT0, "ASEXT0", m_asci_ext[0]).mask(Z180_ASEXT0_MASK);
- state_add(Z180_ASTC0, "ASTC0", m_asci_tc[0].w);
-
- state_add(Z180_ASEXT1, "ASEXT1", m_asci_ext[1]).mask(Z180_ASEXT1_MASK);
- state_add(Z180_ASTC1, "ASTC1", m_asci_tc[1].w);
-
state_add(Z180_CMR, "CMR", m_cmr).mask(Z180_CMR_MASK);
state_add(Z180_CCR, "CCR", m_ccr);
- save_item(NAME(m_asci_ext));
- save_item(NAME(m_asci_tc[0].w));
- save_item(NAME(m_asci_tc[1].w));
save_item(NAME(m_cmr));
save_item(NAME(m_ccr));
}
@@ -1912,12 +1727,6 @@ void z180_device::device_reset()
m_frc_prescale = 0;
/* reset io registers */
- m_asci_cntla[0] = (m_asci_cntla[0] & Z180_CNTLA0_MPBR_EFR) | Z180_CNTLA0_RTS0;
- m_asci_cntla[1] = (m_asci_cntla[1] & Z180_CNTLA1_MPBR_EFR) | Z180_CNTLA1_CKA1D;
- m_asci_cntlb[0] = (m_asci_cntlb[0] & (Z180_CNTLB0_MPBT | Z180_CNTLB0_CTS_PS)) | 0x07;
- m_asci_cntlb[1] = (m_asci_cntlb[1] & Z180_CNTLB1_MPBT) | 0x07;
- m_asci_stat[0] = m_asci_stat[0] & (Z180_STAT0_DCD0 | Z180_STAT0_TDRE);
- m_asci_stat[1] = Z180_STAT1_TDRE;
m_csio_cntr = 0x07;
m_tcr = 0x00;
m_dma_iar1.b.h2 = 0x00;
@@ -1936,17 +1745,35 @@ void z180_device::device_reset()
z180_mmu();
}
+void z180_device::device_add_mconfig(machine_config &config)
+{
+ Z180ASCI_CHANNEL_0(config, m_asci[0], DERIVED_CLOCK(1,2));
+
+ Z180ASCI_CHANNEL_1(config, m_asci[1], DERIVED_CLOCK(1,2));
+}
+
+void z8s180_device::device_add_mconfig(machine_config &config)
+{
+ Z180ASCI_EXT_CHANNEL_0(config, m_asci[0], DERIVED_CLOCK(1,2));
+
+ Z180ASCI_EXT_CHANNEL_1(config, m_asci[1], DERIVED_CLOCK(1,2));
+}
+
void z8s180_device::device_reset()
{
z180_device::device_reset();
- m_asci_ext[0] = 0x00;
- m_asci_ext[1] = 0x00;
m_cmr = 0x00;
m_ccr = 0x00;
notify_clock_changed();
}
+void z8s180_device::device_clock_changed()
+{
+ m_asci[0]->set_clock((m_cmr & 0x80) ? DERIVED_CLOCK(2,1) : (m_ccr & 0x80) ? DERIVED_CLOCK(1,1) : DERIVED_CLOCK(1,2));
+ m_asci[1]->set_clock((m_cmr & 0x80) ? DERIVED_CLOCK(2,1) : (m_ccr & 0x80) ? DERIVED_CLOCK(1,1) : DERIVED_CLOCK(1,2));
+}
+
/* Handle PRT timers, decreasing them after 20 clocks and returning the new icount base that needs to be used for the next check */
void z180_device::clock_timers()
{
@@ -2005,6 +1832,9 @@ int z180_device::check_interrupts()
if (m_irq_state[2] != CLEAR_LINE && (m_itc & Z180_ITC_ITE2) == Z180_ITC_ITE2)
m_int_pending[Z180_INT_IRQ2] = 1;
+
+ m_int_pending[Z180_INT_ASCI0] = m_asci[0]->check_interrupt();
+ m_int_pending[Z180_INT_ASCI1] = m_asci[1]->check_interrupt();
}
for (i = 0; i <= Z180_INT_MAX; i++)
@@ -2012,6 +1842,8 @@ int z180_device::check_interrupts()
{
cycles += take_interrupt(i);
m_int_pending[i] = 0;
+ if (i == Z180_INT_ASCI0) m_asci[0]->clear_interrupt();
+ if (i == Z180_INT_ASCI1) m_asci[1]->clear_interrupt();
break;
}
diff --git a/src/devices/cpu/z180/z180.h b/src/devices/cpu/z180/z180.h
index 94b95d1488e..62213845b05 100644
--- a/src/devices/cpu/z180/z180.h
+++ b/src/devices/cpu/z180/z180.h
@@ -6,7 +6,7 @@
#pragma once
#include "machine/z80daisy.h"
-
+#include "z180asci.h"
enum
{
@@ -107,10 +107,23 @@ class z180_device : public cpu_device, public z80_daisy_chain_interface
public:
auto tend0_wr_callback() { return m_tend0_cb.bind(); }
auto tend1_wr_callback() { return m_tend1_cb.bind(); }
+ auto txa0_wr_callback() { return subdevice<z180asci_channel_base>("asci_0")->txa_handler(); }
+ auto txa1_wr_callback() { return subdevice<z180asci_channel_base>("asci_1")->txa_handler(); }
+ auto rts0_wr_callback() { return subdevice<z180asci_channel_base>("asci_0")->rts_handler(); }
+ auto cka0_wr_callback() { return subdevice<z180asci_channel_base>("asci_0")->cka_handler(); }
+ auto cka1_wr_callback() { return subdevice<z180asci_channel_base>("asci_1")->cka_handler(); }
bool get_tend0();
bool get_tend1();
+ DECLARE_WRITE_LINE_MEMBER( rxa0_w ) { m_asci[0]->rxa_wr(state); }
+ DECLARE_WRITE_LINE_MEMBER( rxa1_w ) { m_asci[1]->rxa_wr(state); }
+ DECLARE_WRITE_LINE_MEMBER( cts0_w ) { m_asci[0]->cts_wr(state); }
+ DECLARE_WRITE_LINE_MEMBER( cts1_w ) { m_asci[1]->cts_wr(state); }
+ DECLARE_WRITE_LINE_MEMBER( dcd0_w ) { m_asci[0]->dcd_wr(state); }
+ DECLARE_WRITE_LINE_MEMBER( cka0_w ) { m_asci[0]->cka_wr(state); }
+ DECLARE_WRITE_LINE_MEMBER( cka1_w ) { m_asci[1]->cka_wr(state); }
+
protected:
// construction/destruction
z180_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, bool extended_io, address_map_constructor internal_map);
@@ -119,6 +132,7 @@ protected:
virtual void device_start() override;
virtual void device_reset() override;
virtual void device_resolve_objects() override;
+ virtual void device_add_mconfig(machine_config &config) override;
// device_execute_interface overrides
virtual uint32_t execute_min_cycles() const noexcept override { return 1; }
@@ -150,6 +164,7 @@ protected:
address_space_config m_program_config;
address_space_config m_io_config;
address_space_config m_decrypted_opcodes_config;
+ required_device_array<z180asci_channel_base, 2> m_asci;
void set_address_width(int bits);
@@ -169,11 +184,6 @@ private:
uint8_t m_tmdr_latch; // flag latched TMDR0H, TMDR1H values
uint8_t m_read_tcr_tmdr[2]; // flag to indicate that TCR or TMDR was read
uint32_t m_iol; // I/O line status bits
- uint8_t m_asci_cntla[2]; // ASCI control register A ch 0-1
- uint8_t m_asci_cntlb[2]; // ASCI control register B ch 0-1
- uint8_t m_asci_stat[2]; // ASCI status register 0-1
- uint8_t m_asci_tdr[2]; // ASCI transmit data register 0-1
- uint8_t m_asci_rdr[2]; // ASCI receive data register 0-1
uint8_t m_csio_cntr; // CSI/O control/status register
uint8_t m_csio_trdr; // CSI/O transmit/receive register
PAIR16 m_tmdr[2]; // PRT data register ch 0-1
@@ -1822,6 +1832,8 @@ protected:
// device-level overrides
virtual void device_start() override;
virtual void device_reset() override;
+ virtual void device_clock_changed() override;
+ virtual void device_add_mconfig(machine_config &config) override;
// device_execute_interface overrides
virtual uint64_t execute_clocks_to_cycles(uint64_t clocks) const noexcept override { return BIT(m_cmr, 7) ? (clocks * 2) : BIT(m_ccr, 7) ? clocks : (clocks + 2 - 1) / 2; }
@@ -1831,8 +1843,6 @@ protected:
virtual void z180_internal_port_write(uint8_t port, uint8_t data) override;
private:
- uint8_t m_asci_ext[2]; // ASCI extension control register 0-1
- PAIR16 m_asci_tc[2]; // ASCI time constant ch 0-1
uint8_t m_cmr; // clock multiplier
uint8_t m_ccr; // chip control register
};
diff --git a/src/devices/cpu/z180/z180asci.cpp b/src/devices/cpu/z180/z180asci.cpp
new file mode 100644
index 00000000000..69c225accaa
--- /dev/null
+++ b/src/devices/cpu/z180/z180asci.cpp
@@ -0,0 +1,702 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic
+/*********************************************************************
+
+ z180asci.cpp
+
+*********************************************************************/
+
+#include "emu.h"
+#include "z180.h"
+
+//#define VERBOSE 1
+
+#include "logmacro.h"
+
+/* 00 ASCI control register A ch 0 */
+static constexpr u8 Z180_CNTLA_MPE = 0x80;
+static constexpr u8 Z180_CNTLA_RE = 0x40;
+static constexpr u8 Z180_CNTLA_TE = 0x20;
+static constexpr u8 Z180_CNTLA_RTS0 = 0x10;
+static constexpr u8 Z180_CNTLA_MPBR_EFR = 0x08;
+static constexpr u8 Z180_CNTLA_MODE = 0x07;
+static constexpr u8 Z180_CNTLA_MODE_DATA = 0x04;
+static constexpr u8 Z180_CNTLA_MODE_PARITY = 0x02;
+static constexpr u8 Z180_CNTLA_MODE_STOPB = 0x01;
+
+/* 01 ASCI control register A ch 1 */
+static constexpr u8 Z180_CNTLA1_CKA1D = 0x10;
+
+/* 02/03 ASCI control register B ch 0 */
+static constexpr u8 Z180_CNTLB_MPBT = 0x80;
+static constexpr u8 Z180_CNTLB_MP = 0x40;
+static constexpr u8 Z180_CNTLB_CTS_PS = 0x20;
+static constexpr u8 Z180_CNTLB_PEO = 0x10;
+static constexpr u8 Z180_CNTLB_DR = 0x08;
+static constexpr u8 Z180_CNTLB_SS = 0x07;
+
+/* 04 ASCI status register 0 (all bits read-only except RIE and TIE) */
+static constexpr u8 Z180_STAT_RDRF = 0x80;
+static constexpr u8 Z180_STAT_OVRN = 0x40;
+static constexpr u8 Z180_STAT_PE = 0x20;
+static constexpr u8 Z180_STAT_FE = 0x10;
+static constexpr u8 Z180_STAT_RIE = 0x08;
+static constexpr u8 Z180_STAT_DCD0 = 0x04;
+static constexpr u8 Z180_STAT_TDRE = 0x02;
+static constexpr u8 Z180_STAT_TIE = 0x01;
+
+/* 05 ASCI status register 1 (all bits read-only except RIE, CTS1E and TIE) */
+static constexpr u8 Z180_STAT1_CTS1E = 0x04;
+
+/* 12/13 (Z8S180/Z8L180) ASCI extension control register 0 (break detect is read-only) */
+static constexpr u8 Z180_ASEXT_DCD0 = 0x40;
+static constexpr u8 Z180_ASEXT_CTS0 = 0x20;
+static constexpr u8 Z180_ASEXT_X1_BIT_CLK0 = 0x10;
+static constexpr u8 Z180_ASEXT_BRG0_MODE = 0x08;
+static constexpr u8 Z180_ASEXT_BRK_EN = 0x04;
+static constexpr u8 Z180_ASEXT_BRK_DET = 0x02;
+static constexpr u8 Z180_ASEXT_BRK_SEND = 0x01;
+
+static constexpr u8 Z180_ASEXT0_MASK = 0x7f;
+static constexpr u8 Z180_ASEXT1_MASK = 0x1f;
+
+
+//**************************************************************************
+// z180asci_channel_base
+//**************************************************************************
+
+z180asci_channel_base::z180asci_channel_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const int id, const bool ext)
+ : device_t(mconfig, type, tag, owner, clock)
+ , m_rcv_clock(nullptr)
+ , m_tra_clock(nullptr)
+ , m_bit_rate(attotime::never)
+ , m_sample_rate(attotime::never)
+ , m_txa_handler(*this)
+ , m_rts_handler(*this)
+ , m_cka_handler(*this)
+ , m_cts(0)
+ , m_dcd(0)
+ , m_irq(0)
+ , m_rts(0)
+ , m_divisor(0)
+ , m_id(id)
+ , m_ext(ext)
+{
+}
+
+
+void z180asci_channel_base::device_resolve_objects()
+{
+ // resolve callbacks
+ m_txa_handler.resolve_safe();
+ m_rts_handler.resolve_safe();
+ m_cka_handler.resolve_safe();
+}
+
+void z180asci_channel_base::device_start()
+{
+ save_item(NAME(m_asci_cntla));
+ save_item(NAME(m_asci_cntlb));
+ save_item(NAME(m_asci_stat));
+ save_item(NAME(m_asci_tdr));
+ save_item(NAME(m_asci_rdr));
+ if (m_ext)
+ {
+ save_item(NAME(m_asci_ext));
+ save_item(NAME(m_asci_tc.w));
+ }
+ save_item(NAME(m_tsr));
+ save_item(NAME(m_rsr));
+ save_item(NAME(m_data_fifo));
+ save_item(NAME(m_error_fifo));
+ save_item(NAME(m_fifo_wr));
+ save_item(NAME(m_fifo_rd));
+
+ save_item(NAME(m_cts));
+ save_item(NAME(m_dcd));
+ save_item(NAME(m_irq));
+ save_item(NAME(m_txa));
+ save_item(NAME(m_rxa));
+ save_item(NAME(m_rts));
+
+ save_item(NAME(m_divisor));
+
+ save_item(NAME(m_clock_state));
+
+ save_item(NAME(m_tx_counter));
+ save_item(NAME(m_tx_state));
+
+ save_item(NAME(m_rx_state));
+ save_item(NAME(m_rx_bits));
+ save_item(NAME(m_rx_counter));
+ save_item(NAME(m_rx_count_to));
+ save_item(NAME(m_rx_total_bits));
+ save_item(NAME(m_rx_enabled));
+
+ save_item(NAME(m_bit_rate));
+ save_item(NAME(m_sample_rate));
+
+
+ m_rcv_clock = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(z180asci_channel_base::rcv_clock), this));
+ m_tra_clock = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(z180asci_channel_base::tra_clock), this));
+
+}
+
+void z180asci_channel_base::device_reset()
+{
+ m_asci_ext = 0;
+ m_asci_tc.w = 0;
+
+ m_fifo_wr = 0;
+ m_fifo_rd = 0;
+
+ output_txa(1);
+ output_rts(1);
+
+ m_tsr = 0;
+ m_rxa = 1;
+ m_clock_state = 0;
+ m_tx_state = STATE_WAIT;
+ m_rx_state = STATE_START;
+ m_rx_bits = 0;
+ m_rx_enabled = true;
+
+ m_tx_counter = 0;
+ m_rx_counter = 0;
+ m_rx_count_to = 1;
+}
+
+void z180asci_channel_base::device_clock_changed()
+{
+ uint32_t brg_divisor;
+ // Divide ratio
+ m_divisor = (m_asci_ext & Z180_ASEXT_X1_BIT_CLK0) ? 1 : ((m_asci_cntlb & Z180_CNTLB_DR) ? 64 : 16);
+
+ if ((m_asci_cntlb & Z180_CNTLB_SS) == Z180_CNTLB_SS)
+ {
+ // External clock
+ brg_divisor = 0;
+ }
+ else
+ {
+ if (m_asci_ext & Z180_ASEXT_BRG0_MODE)
+ {
+ // Extended boud rate generator mode
+ brg_divisor = m_asci_tc.w + 2;
+ }
+ else
+ {
+ // Regular bitrate generator mode
+ brg_divisor = 1 << (m_asci_cntlb & Z180_CNTLB_SS);
+ brg_divisor *= ((m_asci_cntlb & Z180_CNTLB_CTS_PS) ? 30 : 10); // Prescale
+ }
+ }
+
+ if (brg_divisor)
+ {
+ LOG("Z180 ASCI%d set bitrate %d\n", m_id, uint32_t(clock() / brg_divisor / m_divisor));
+ m_bit_rate = attotime::from_hz(clock() / brg_divisor / m_divisor);
+ m_sample_rate = attotime::from_hz(clock() / brg_divisor);
+ }
+ else
+ {
+ LOG("Z180 ASCI%d set bitrate 0, using external\n", m_id);
+ m_bit_rate = attotime::never;
+ m_sample_rate = attotime::never;
+ }
+
+ m_tra_clock->adjust(attotime::never);
+ m_rx_state = STATE_START;
+ m_rx_count_to = 1;
+ if(!m_sample_rate.is_never())
+ m_rcv_clock->adjust(m_sample_rate, 0, m_sample_rate);
+}
+
+uint8_t z180asci_channel_base::cntla_r()
+{
+ LOG("Z180 CNTLA%d rd $%02x\n", m_id, m_asci_cntla);
+ return m_asci_cntla;
+}
+
+uint8_t z180asci_channel_base::cntlb_r()
+{
+ uint8_t data = (m_asci_cntlb & 0x0d) | (m_cts << 5);
+ LOG("Z180 CNTLB%d rd $%02x\n", m_id, data);
+ return data;
+}
+
+uint8_t z180asci_channel_base::stat_r()
+{
+ LOG("Z180 STAT%d rd $%02x\n", m_id, m_asci_stat);
+ return m_asci_stat;
+}
+
+uint8_t z180asci_channel_base::tdr_r()
+{
+ LOG("Z180 TDR%d rd $%02x\n", m_id, m_asci_tdr);
+ return m_asci_tdr;
+}
+
+uint8_t z180asci_channel_base::rdr_r()
+{
+ LOG("Z180 RDR%d rd $%02x\n", m_id, m_asci_rdr);
+ if (!machine().side_effects_disabled())
+ {
+ if (m_fifo_rd != m_fifo_wr)
+ {
+ m_asci_rdr = m_data_fifo[m_fifo_rd];
+ m_asci_stat &= ~(Z180_STAT_OVRN | Z180_STAT_PE | Z180_STAT_FE);
+ m_asci_stat |= m_error_fifo[m_fifo_rd];
+ if (m_asci_stat & (Z180_STAT_OVRN | Z180_STAT_PE | Z180_STAT_FE))
+ m_irq = 1;
+ m_fifo_rd = (m_fifo_rd + 1) & 3;
+ if (m_fifo_rd == m_fifo_wr) // empty
+ m_asci_stat &= ~Z180_STAT_RDRF;
+ }
+ }
+ return m_asci_rdr;
+}
+
+uint8_t z180asci_channel_base::asext_r()
+{
+ LOG("Z180 ASEXT%d rd $%02x\n", m_id, m_asci_ext);
+ return m_asci_ext;
+}
+
+uint8_t z180asci_channel_base::astcl_r()
+{
+ LOG("Z180 ASTC%dL rd $%02x ($%04x)\n", m_id, m_asci_tc.b.l, m_asci_tc.w);
+ return m_asci_tc.b.l;
+}
+
+uint8_t z180asci_channel_base::astch_r()
+{
+ LOG("Z180 ASTC%dH rd $%02x ($%04x)\n", m_id, m_asci_tc.b.h, m_asci_tc.w);
+ return m_asci_tc.b.h;
+}
+
+void z180asci_channel_base::update_total_bits()
+{
+ m_rx_total_bits = (m_asci_cntla & Z180_CNTLA_MODE_DATA) ? 8 : 7;
+ m_rx_total_bits += (m_asci_cntla & Z180_CNTLA_MODE_STOPB) ? 2 : 1;
+ m_rx_total_bits += (m_asci_cntlb & Z180_CNTLB_MP) ? 1 : ((m_asci_cntla & Z180_CNTLA_MODE_PARITY) ? 1 : 0);
+}
+
+void z180asci_channel_base::cntla_w(uint8_t data)
+{
+ LOG("Z180 CNTLA%d wr $%02x\n", m_id, data);
+ m_asci_cntla = data & ~(Z180_CNTLA_MPBR_EFR | Z180_CNTLA_RTS0);
+ output_rts(BIT(data,4)); // Z180_CNTLA_RTS0
+ if (data & Z180_CNTLA_MPBR_EFR) // Error Flag Reset
+ {
+ m_asci_stat &= ~(Z180_STAT_OVRN | Z180_STAT_PE | Z180_STAT_FE);
+ m_asci_ext &= ~(Z180_ASEXT_BRK_DET);
+ }
+ update_total_bits();
+}
+
+void z180asci_channel_base::cntlb_w(uint8_t data)
+{
+ LOG("Z180 CNTLB%d wr $%02x\n", m_id, data);
+ m_asci_cntlb = data;
+ device_clock_changed();
+ update_total_bits();
+}
+
+void z180asci_channel_base::tdr_w(uint8_t data)
+{
+ LOG("Z180 TDR%d wr $%02x\n", m_id, data);
+ m_asci_tdr = data;
+ m_asci_stat &= ~Z180_STAT_TDRE;
+
+ if (!m_bit_rate.is_never())
+ m_tra_clock->adjust(m_bit_rate, 0, m_bit_rate);
+}
+
+void z180asci_channel_base::rdr_w(uint8_t data)
+{
+ LOG("Z180 RDR%d wr $%02x\n", m_id, data);
+ if (!(m_asci_stat & Z180_STAT_RDRF))
+ set_fifo_data(data, 0);
+}
+
+void z180asci_channel_base::asext_w(uint8_t data)
+{
+ if (m_asci_ext & Z180_ASEXT_BRK_EN)
+ m_tx_state = (m_asci_ext & Z180_ASEXT_BRK_SEND) ? STATE_BREAK : STATE_WAIT;
+ device_clock_changed();
+}
+
+void z180asci_channel_base::astcl_w(uint8_t data)
+{
+ LOG("Z180 ASTC%dL wr $%02x\n", m_id, data);
+ m_asci_tc.b.l = data;
+ device_clock_changed();
+}
+
+void z180asci_channel_base::astch_w(uint8_t data)
+{
+ LOG("Z180 ASTC%dH wr $%02x\n", m_id, data);
+ m_asci_tc.b.h = data;
+ device_clock_changed();
+}
+
+DECLARE_WRITE_LINE_MEMBER( z180asci_channel_base::cts_wr )
+{
+ if (m_id)
+ {
+ // For channel 1, CTS can be disabled
+ if ((m_asci_stat && Z180_STAT1_CTS1E) == 0) return;
+ }
+ else
+ {
+ // For channel 0, high resets TDRE
+ if (m_ext && state && (m_asci_ext && Z180_ASEXT_CTS0) == 0)
+ m_asci_stat |= Z180_STAT_TDRE;
+ }
+ m_cts = state;
+}
+
+DECLARE_WRITE_LINE_MEMBER( z180asci_channel_base::dcd_wr )
+{
+ if (m_id)
+ return;
+
+ // In extended mode, DCD autoenables RX if configured
+ if (m_ext && (m_asci_ext && Z180_ASEXT_DCD0) == 0)
+ {
+ m_rx_enabled = state ? false : true;
+ if (state)
+ m_asci_ext &= ~(Z180_ASEXT_BRK_DET);
+ }
+
+ m_dcd = state;
+ if (m_dcd)
+ m_irq = 1;
+}
+
+DECLARE_WRITE_LINE_MEMBER( z180asci_channel_base::rxa_wr )
+{
+ m_rxa = state;
+}
+
+DECLARE_WRITE_LINE_MEMBER( z180asci_channel_base::cka_wr )
+{
+ // For channel 1, CKA can be disabled
+ if (m_id && (m_asci_cntla && Z180_CNTLA1_CKA1D)) return;
+
+ if(state != m_clock_state)
+ {
+ m_clock_state = state;
+ if(!state)
+ {
+ m_tx_counter++;
+ if (m_tx_counter != m_divisor) return;
+ m_tx_counter = 0;
+ if (m_asci_cntla & Z180_CNTLA_TE)
+ transmit_edge();
+ }
+ else
+ {
+ m_rx_counter++;
+ if (m_rx_counter != m_rx_count_to) return;
+ m_rx_counter = 0;
+ if (m_rx_enabled && (m_asci_cntla & Z180_CNTLA_RE))
+ receive_edge();
+ }
+ }
+}
+
+void z180asci_channel_base::prepare_tsr()
+{
+ int bits = (m_asci_cntla & Z180_CNTLA_MODE_DATA) ? 8 : 7;
+
+ m_tsr = (m_asci_cntla & Z180_CNTLA_MODE_STOPB) ? 3 : 1; // stop bit(s)
+
+ if ((m_asci_cntlb & Z180_CNTLB_MP) || (m_asci_cntla & Z180_CNTLA_MODE_PARITY))
+ {
+ m_tsr <<= 1;
+ if (m_asci_cntlb & Z180_CNTLB_MP)
+ {
+ m_tsr |= (m_asci_cntlb & Z180_CNTLB_MPBT) ? 1 : 0;
+ }
+ else
+ {
+ uint8_t parity = 0;
+ for (int i = 0; i < bits; i++)
+ parity ^= BIT(m_asci_tdr, i);
+ if (m_asci_cntlb & Z180_CNTLB_PEO) parity ^= 1; // odd parity
+ m_tsr |= parity;
+ }
+ }
+ m_tsr <<= bits;
+ m_tsr |= m_asci_tdr;
+ m_tsr <<= 1; // start bit
+}
+
+void z180asci_channel_base::transmit_edge()
+{
+ if (m_asci_cntla & Z180_CNTLA_TE)
+ {
+ switch (m_tx_state)
+ {
+ case STATE_DATA:
+ output_txa(BIT(m_tsr, 0));
+ m_tsr >>= 1;
+ if (m_tsr == 0)
+ {
+ m_asci_stat |= Z180_STAT_TDRE;
+ if (m_asci_stat & Z180_STAT_TIE)
+ {
+ m_irq = 1;
+ }
+ m_tx_state = STATE_WAIT;
+ m_tra_clock->adjust(attotime::never);
+ }
+ break;
+ case STATE_WAIT:
+ if ((m_asci_stat & Z180_STAT_TDRE) == 0)
+ {
+ prepare_tsr();
+ m_tx_state = STATE_DATA;
+ }
+ break;
+ case STATE_BREAK:
+ output_txa(0);
+ break;
+ }
+ }
+}
+
+void z180asci_channel_base::update_received()
+{
+ uint8_t rx_error = 0;
+ int stop_bits = (m_asci_cntla & Z180_CNTLA_MODE_STOPB) ? 2 : 1;
+ if (m_rsr == 0) // Break detect
+ {
+ m_asci_ext |= Z180_ASEXT_BRK_DET;
+ }
+ uint8_t stop_val = (m_asci_cntla & Z180_CNTLA_MODE_STOPB) ? 3: 1;
+ if ((m_rsr & stop_val) != stop_val)
+ rx_error |= Z180_STAT_FE;
+
+ if (m_asci_cntlb & Z180_CNTLB_MP)
+ {
+ m_asci_cntla |= ((m_rsr >> stop_bits) & 1) ? Z180_CNTLA_MPBR_EFR : 0;
+ }
+ else if (m_asci_cntla & Z180_CNTLA_MODE_PARITY)
+ {
+ uint8_t parity = 0;
+ for (int i = 0; i < ((m_asci_cntla & Z180_CNTLA_MODE_DATA) ? 8 : 7); i++)
+ parity ^= BIT(m_rsr, i);
+ if (m_asci_cntlb & Z180_CNTLB_PEO) parity ^= 1; // odd parity
+ if (((m_rsr >> stop_bits) & 1) != parity)
+ rx_error |= Z180_STAT_PE;
+ }
+ // Skip only if MPE mode active and MPB is 0
+ if (!((m_asci_cntla & Z180_CNTLA_MPE) && ((m_asci_cntla & Z180_CNTLA_MPBR_EFR) == 0)))
+ {
+ set_fifo_data(m_rsr, rx_error);
+ }
+}
+
+void z180asci_channel_base::receive_edge()
+{
+ if (!m_rx_enabled) return;
+
+ if (m_asci_cntla & Z180_CNTLA_RE)
+ {
+ switch (m_rx_state)
+ {
+ case STATE_START:
+ if(m_rxa == 0)
+ {
+ m_rx_state = STATE_DATA;
+ m_rx_bits = 0;
+ m_rsr = 0;
+ if(!(m_bit_rate.is_never()))
+ m_rcv_clock->adjust((m_divisor == 1) ? m_bit_rate : (m_bit_rate * 3) / 2, 0, m_bit_rate);
+ else
+ m_rx_count_to = (m_divisor == 1) ? m_divisor : (m_divisor * 3) / 2;
+ }
+ break;
+ case STATE_DATA:
+ m_rsr |= m_rxa << m_rx_bits;
+ m_rx_bits++;
+ m_rx_count_to = m_divisor;
+ if (m_rx_bits == m_rx_total_bits)
+ {
+ update_received();
+ m_rx_state = STATE_START;
+ if(!m_sample_rate.is_never())
+ m_rcv_clock->adjust(m_sample_rate, 0, m_sample_rate);
+ else
+ m_rx_count_to = 1;
+ }
+ break;
+ }
+ }
+}
+
+void z180asci_channel_base::set_fifo_data(uint8_t data, uint8_t error)
+{
+ m_data_fifo[m_fifo_wr] = data;
+ m_error_fifo[m_fifo_wr] = error;
+ if (((m_fifo_wr + 1) & 3) == m_fifo_rd) // overrun
+ {
+ m_error_fifo[m_fifo_wr] |= Z180_STAT_OVRN;
+ }
+ else
+ {
+ m_error_fifo[m_fifo_wr] &= Z180_STAT_OVRN;
+ m_fifo_wr = (m_fifo_wr + 1) & 3;
+ }
+ m_asci_stat |= Z180_STAT_RDRF;
+ if (m_asci_stat & Z180_STAT_RIE)
+ {
+ m_irq = 1;
+ }
+}
+
+void z180asci_channel_base::output_txa(int txa)
+{
+ if (m_txa != txa)
+ {
+ m_txa = txa;
+ m_txa_handler(m_txa);
+ }
+}
+
+void z180asci_channel_base::output_rts(int rts)
+{
+ if (m_rts != rts)
+ {
+ m_rts = rts;
+ m_rts_handler(m_rts);
+ }
+}
+
+//**************************************************************************
+// z180asci_channel_0
+//**************************************************************************
+
+z180asci_channel_0::z180asci_channel_0(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const bool ext)
+ : z180asci_channel_base(mconfig, type, tag, owner, clock, 0, ext)
+{
+}
+
+z180asci_channel_0::z180asci_channel_0(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : z180asci_channel_0(mconfig, Z180ASCI_CHANNEL_0, tag, owner, clock, false)
+{
+}
+
+void z180asci_channel_0::device_reset()
+{
+ z180asci_channel_base::device_reset();
+ cntla_w((m_asci_cntla & Z180_CNTLA_MPBR_EFR) | Z180_CNTLA_RTS0);
+ cntlb_w((m_asci_cntlb & (Z180_CNTLB_MPBT | Z180_CNTLB_CTS_PS)) | 0x07);
+ m_asci_stat = (m_asci_stat & Z180_STAT_DCD0) | Z180_STAT_TDRE;
+}
+
+void z180asci_channel_0::state_add(device_state_interface &parent)
+{
+ parent.state_add(Z180_CNTLA0, "CNTLA0", m_asci_cntla);
+ parent.state_add(Z180_CNTLB0, "CNTLB0", m_asci_cntlb);
+ parent.state_add(Z180_STAT0, "STAT0", m_asci_stat);
+ parent.state_add(Z180_TDR0, "TDR0", m_asci_tdr);
+ parent.state_add(Z180_RDR0, "RDR0", m_asci_rdr);
+ if (m_ext)
+ {
+ parent.state_add(Z180_ASEXT0, "ASEXT0", m_asci_ext).mask(Z180_ASEXT0_MASK);
+ parent.state_add(Z180_ASTC0, "ASTC0", m_asci_tc.w);
+ }
+}
+
+void z180asci_channel_0::stat_w(uint8_t data)
+{
+ LOG("Z180 STAT0 wr $%02x ($%02x)\n", data, data & (Z180_STAT_RIE | Z180_STAT_TIE));
+ m_asci_stat = (m_asci_stat & ~(Z180_STAT_RIE | Z180_STAT_TIE)) | (data & (Z180_STAT_RIE | Z180_STAT_TIE));
+}
+
+void z180asci_channel_0::asext_w(uint8_t data)
+{
+ LOG("Z180 ASEXT0 wr $%02x ($%02x)\n", data, data & Z180_ASEXT0_MASK & ~Z180_ASEXT_BRK_DET);
+ m_asci_ext = (m_asci_ext & Z180_ASEXT_BRK_DET) | (data & Z180_ASEXT0_MASK & ~Z180_ASEXT_BRK_DET);
+ z180asci_channel_base::asext_w(data);
+}
+
+//**************************************************************************
+// z180asci_channel_1
+//**************************************************************************
+
+z180asci_channel_1::z180asci_channel_1(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const bool ext)
+ : z180asci_channel_base(mconfig, type, tag, owner, clock, 1, ext)
+{
+}
+
+z180asci_channel_1::z180asci_channel_1(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : z180asci_channel_1(mconfig, Z180ASCI_CHANNEL_1, tag, owner, clock, false)
+{
+}
+void z180asci_channel_1::device_reset()
+{
+ z180asci_channel_base::device_reset();
+ cntla_w((m_asci_cntla & Z180_CNTLA_MPBR_EFR) | Z180_CNTLA1_CKA1D);
+ cntlb_w((m_asci_cntlb & Z180_CNTLB_MPBT) | 0x07);
+ m_asci_stat = Z180_STAT_TDRE;
+}
+
+void z180asci_channel_1::state_add(device_state_interface &parent)
+{
+ parent.state_add(Z180_CNTLA1, "CNTLA1", m_asci_cntla);
+ parent.state_add(Z180_CNTLB1, "CNTLB1", m_asci_cntlb);
+ parent.state_add(Z180_STAT1, "STAT1", m_asci_stat);
+ parent.state_add(Z180_TDR1, "TDR1", m_asci_tdr);
+ parent.state_add(Z180_RDR1, "RDR1", m_asci_rdr);
+ if (m_ext)
+ {
+ parent.state_add(Z180_ASEXT1, "ASEXT1", m_asci_ext).mask(Z180_ASEXT1_MASK);
+ parent.state_add(Z180_ASTC1, "ASTC1", m_asci_tc.w);
+ }
+}
+
+void z180asci_channel_1::stat_w(uint8_t data)
+{
+ LOG("Z180 STAT1 wr $%02x ($%02x)\n", data, data & (Z180_STAT_RIE | Z180_STAT1_CTS1E | Z180_STAT_TIE));
+ m_asci_stat = (m_asci_stat & ~(Z180_STAT_RIE | Z180_STAT1_CTS1E | Z180_STAT_TIE)) | (data & (Z180_STAT_RIE | Z180_STAT1_CTS1E | Z180_STAT_TIE));
+}
+
+void z180asci_channel_1::asext_w(uint8_t data)
+{
+ LOG("Z180 ASEXT1 wr $%02x ($%02x)\n", data, data & Z180_ASEXT1_MASK & ~Z180_ASEXT_BRK_DET);
+ m_asci_ext = (m_asci_ext & Z180_ASEXT_BRK_DET) | (data & Z180_ASEXT1_MASK & ~Z180_ASEXT_BRK_DET);
+ z180asci_channel_base::asext_w(data);
+}
+
+//**************************************************************************
+// z180asci_ext_channel_0
+//**************************************************************************
+
+z180asci_ext_channel_0::z180asci_ext_channel_0(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : z180asci_channel_0(mconfig, Z180ASCI_EXT_CHANNEL_0, tag, owner, clock, true)
+{
+}
+
+//**************************************************************************
+// z180asci_channel_1
+//**************************************************************************
+
+z180asci_ext_channel_1::z180asci_ext_channel_1(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
+ : z180asci_channel_1(mconfig, Z180ASCI_EXT_CHANNEL_1, tag, owner, clock, true)
+{
+}
+
+//**************************************************************************
+// DEVICE DEFINITIONS
+//**************************************************************************
+
+DEFINE_DEVICE_TYPE(Z180ASCI_CHANNEL_0, z180asci_channel_0, "z180asci_channel_0", "Z180 ASCI Channel 0")
+DEFINE_DEVICE_TYPE(Z180ASCI_CHANNEL_1, z180asci_channel_1, "z180asci_channel_1", "Z180 ASCI Channel 1")
+
+DEFINE_DEVICE_TYPE(Z180ASCI_EXT_CHANNEL_0, z180asci_ext_channel_0, "z180asci_ext_channel_0", "Z180 ASCI Extended Channel 0")
+DEFINE_DEVICE_TYPE(Z180ASCI_EXT_CHANNEL_1, z180asci_ext_channel_1, "z180asci_ext_channel_1", "Z180 ASCI Extended Channel 1")
diff --git a/src/devices/cpu/z180/z180asci.h b/src/devices/cpu/z180/z180asci.h
new file mode 100644
index 00000000000..3794fe635a8
--- /dev/null
+++ b/src/devices/cpu/z180/z180asci.h
@@ -0,0 +1,201 @@
+// license:BSD-3-Clause
+// copyright-holders:Miodrag Milanovic
+/*********************************************************************
+
+ z180asci.h
+
+*********************************************************************/
+
+#ifndef MAME_CPU_Z180_Z180ASCI_H
+#define MAME_CPU_Z180_Z180ASCI_H
+
+#pragma once
+
+//**************************************************************************
+// z180asci_channel_base
+//**************************************************************************
+
+class z180asci_channel_base : public device_t
+{
+public:
+ // inline configuration
+ auto txa_handler() { return m_txa_handler.bind(); }
+ auto rts_handler() { return m_rts_handler.bind(); }
+ auto cka_handler() { return m_cka_handler.bind(); }
+
+ uint8_t cntla_r();
+ uint8_t cntlb_r();
+ uint8_t stat_r();
+ uint8_t tdr_r();
+ uint8_t rdr_r();
+ uint8_t asext_r();
+ uint8_t astcl_r();
+ uint8_t astch_r();
+ void cntla_w(uint8_t data);
+ void cntlb_w(uint8_t data);
+ virtual void stat_w(uint8_t data) = 0;
+ void tdr_w(uint8_t data);
+ void rdr_w(uint8_t data);
+ virtual void asext_w(uint8_t data);
+ void astcl_w(uint8_t data);
+ void astch_w(uint8_t data);
+
+ DECLARE_WRITE_LINE_MEMBER( rxa_wr );
+ DECLARE_WRITE_LINE_MEMBER( cts_wr );
+ DECLARE_WRITE_LINE_MEMBER( dcd_wr );
+ DECLARE_WRITE_LINE_MEMBER( cka_wr );
+
+ virtual void state_add(device_state_interface &parent) = 0;
+
+ int check_interrupt() { return m_irq; }
+ void clear_interrupt() { m_irq = 0; }
+protected:
+ z180asci_channel_base(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const int id, const bool ext);
+
+ // device-level overrides
+ virtual void device_start() override;
+ virtual void device_reset() override;
+ virtual void device_resolve_objects() override;
+ virtual void device_clock_changed() override;
+
+ void transmit_edge();
+ void receive_edge();
+ void set_fifo_data(uint8_t data, uint8_t error);
+ void prepare_tsr();
+ void update_received();
+ void update_total_bits();
+
+ enum serial_state
+ {
+ STATE_START,
+ STATE_DATA,
+ STATE_WAIT,
+ STATE_BREAK
+ };
+
+
+ void output_txa(int txa);
+ void output_rts(int rts);
+ TIMER_CALLBACK_MEMBER(rcv_clock) { receive_edge(); }
+ TIMER_CALLBACK_MEMBER(tra_clock) { transmit_edge(); }
+
+ emu_timer *m_rcv_clock;
+ emu_timer *m_tra_clock;
+ attotime m_bit_rate;
+ attotime m_sample_rate;
+
+ devcb_write_line m_txa_handler;
+ devcb_write_line m_rts_handler;
+ devcb_write_line m_cka_handler;
+
+ uint8_t m_asci_cntla; // ASCI control register A
+ uint8_t m_asci_cntlb; // ASCI control register B
+ uint8_t m_asci_stat; // ASCI status register
+ uint8_t m_asci_tdr; // ASCI transmit data register
+ uint8_t m_asci_rdr; // ASCI receive data register
+ uint8_t m_asci_ext; // ASCI extension control register
+ PAIR16 m_asci_tc; // ASCI time constant
+
+ uint16_t m_tsr;
+ uint16_t m_rsr;
+ uint8_t m_data_fifo[4];
+ uint8_t m_error_fifo[4];
+ uint8_t m_fifo_wr;
+ uint8_t m_fifo_rd;
+
+ uint8_t m_cts;
+ uint8_t m_dcd;
+ uint8_t m_irq;
+ uint8_t m_txa;
+ uint8_t m_rxa;
+ uint8_t m_rts;
+
+ uint32_t m_divisor;
+
+ uint8_t m_clock_state;
+ uint8_t m_tx_state;
+ uint8_t m_rx_state;
+ uint8_t m_rx_bits;
+
+ uint8_t m_tx_counter;
+ uint8_t m_rx_counter;
+ uint8_t m_rx_count_to;
+ uint8_t m_rx_total_bits;
+
+ bool m_rx_enabled;
+
+ const int m_id;
+ const bool m_ext;
+};
+
+//**************************************************************************
+// z180asci_channel_0
+//**************************************************************************
+
+class z180asci_channel_0 : public z180asci_channel_base
+{
+public:
+ // construction/destruction
+ z180asci_channel_0(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void stat_w(uint8_t data) override;
+ void asext_w(uint8_t data) override;
+ void state_add(device_state_interface &parent) override;
+protected:
+ z180asci_channel_0(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const bool ext);
+ // device-level overrides
+ virtual void device_reset() override;
+};
+
+//**************************************************************************
+// z180asci_channel_1
+//**************************************************************************
+
+class z180asci_channel_1 : public z180asci_channel_base
+{
+public:
+ // construction/destruction
+ z180asci_channel_1(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+
+ void stat_w(uint8_t data) override;
+ void asext_w(uint8_t data) override;
+ void state_add(device_state_interface &parent) override;
+protected:
+ z180asci_channel_1(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, const bool ext);
+ // device-level overrides
+ virtual void device_reset() override;
+};
+
+//**************************************************************************
+// z180asci_ext_channel_0
+//**************************************************************************
+
+class z180asci_ext_channel_0 : public z180asci_channel_0
+{
+public:
+ // construction/destruction
+ z180asci_ext_channel_0(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+//**************************************************************************
+// z180asci_ext_channel_1
+//**************************************************************************
+
+class z180asci_ext_channel_1 : public z180asci_channel_1
+{
+public:
+ // construction/destruction
+ z180asci_ext_channel_1(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
+};
+
+//**************************************************************************
+// DEVICE TYPE DEFINITIONS
+//**************************************************************************
+
+DECLARE_DEVICE_TYPE(Z180ASCI_CHANNEL_0, z180asci_channel_0)
+DECLARE_DEVICE_TYPE(Z180ASCI_CHANNEL_1, z180asci_channel_1)
+
+DECLARE_DEVICE_TYPE(Z180ASCI_EXT_CHANNEL_0, z180asci_ext_channel_0)
+DECLARE_DEVICE_TYPE(Z180ASCI_EXT_CHANNEL_1, z180asci_ext_channel_1)
+
+#endif // MAME_CPU_Z180_Z180ASCI_H