summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author arbee <rb6502@users.noreply.github.com>2025-11-02 12:52:27 -0500
committer arbee <rb6502@users.noreply.github.com>2025-11-02 12:52:27 -0500
commit8777b5dc49b968fc873f9f2b0b544e539ccd3c85 (patch)
treedd9670148b98e17dedf5959387cc541bc907c98c
parentead8f36931f6741724962684002f848219021e16 (diff)
machine/mb89371.cpp: Rewrite as a fully functional device. [R. Belmont]
-rw-r--r--src/devices/machine/mb89371.cpp246
-rw-r--r--src/devices/machine/mb89371.h70
-rw-r--r--src/mame/konami/konamigq.cpp11
-rw-r--r--src/mame/konami/konamigv.cpp9
-rw-r--r--src/mame/konami/ksys573.cpp9
5 files changed, 309 insertions, 36 deletions
diff --git a/src/devices/machine/mb89371.cpp b/src/devices/machine/mb89371.cpp
index 7393bd7d529..b3561ffec6b 100644
--- a/src/devices/machine/mb89371.cpp
+++ b/src/devices/machine/mb89371.cpp
@@ -1,52 +1,258 @@
// license:BSD-3-Clause
-// copyright-holders:smf
+// copyright-holders:R. Belmont, smf
/*
- * MB89371
- *
- * Fujitsu
- * Dual Serial UART
+ * Fujitsu MB89371 Dual Serial UART
+ * Emulation by R. Belmont, based on a skeleton by smf
*
+ * Basically two 8251s plus a rudimentary interrupt controller and
+ * a baud rate generator for each 8251. You can use it just as a
+ * plain dual 8251 though, and the MPC60 does. The MPC2000 and some
+ * PS1-based Konami games use it more fully.
*/
#include "emu.h"
+
#include "mb89371.h"
+#define LOG_BAUD_RATE_GENERATOR (1U << 1)
+
+#define VERBOSE (0)
+
+//#define LOG_OUTPUT_FUNC osd_printf_info
+#include "logmacro.h"
+
DEFINE_DEVICE_TYPE(MB89371, mb89371_device, "mb89371", "MB89371 Dual Serial UART")
-mb89371_device::mb89371_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock )
+void mb89371_device::device_add_mconfig(machine_config &config)
+{
+ I8251(config, m_sio[0], clock());
+ m_sio[0]->txd_handler().set(FUNC(mb89371_device::tx_data_w<0>));
+ m_sio[0]->rxrdy_handler().set(FUNC(mb89371_device::rx_ready_w<0>));
+ m_sio[0]->txrdy_handler().set(FUNC(mb89371_device::tx_ready_w<0>));
+ m_sio[0]->txempty_handler().set(FUNC(mb89371_device::tx_empty_w<0>));
+ m_sio[0]->syndet_handler().set(FUNC(mb89371_device::syndet_w<0>));
+
+ I8251(config, m_sio[1], clock());
+ m_sio[1]->txd_handler().set(FUNC(mb89371_device::tx_data_w<1>));
+ m_sio[1]->rxrdy_handler().set(FUNC(mb89371_device::rx_ready_w<1>));
+ m_sio[1]->txrdy_handler().set(FUNC(mb89371_device::tx_ready_w<1>));
+ m_sio[1]->txempty_handler().set(FUNC(mb89371_device::tx_empty_w<1>));
+ m_sio[1]->syndet_handler().set(FUNC(mb89371_device::syndet_w<1>));
+
+ CLOCK(config, m_brg[0]);
+ m_brg[0]->signal_handler().set(FUNC(mb89371_device::brg_tick<0>));
+
+ CLOCK(config, m_brg[1]);
+ m_brg[1]->signal_handler().set(FUNC(mb89371_device::brg_tick<1>));
+}
+
+mb89371_device::mb89371_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
: device_t(mconfig, MB89371, tag, owner, clock)
+ , m_sio(*this, "i8251_%u", 0)
+ , m_brg(*this, "brg_%u", 0)
+ , m_txd_w { *this, *this }
+ , m_rxready_w { *this, *this }
+ , m_txready_w { *this, *this }
+ , m_txempty_w { *this, *this }
+ , m_syndet_w { *this, *this }
+ , m_mode { 0xf0, 0xf0 }
+ , m_baud { 0, 0 }
{
}
void mb89371_device::device_start()
{
+ save_item(NAME(m_mode));
+ save_item(NAME(m_baud));
}
-void mb89371_device::write(offs_t offset, uint8_t data, uint8_t mem_mask)
+void mb89371_device::device_post_load()
+{
+ recalc_brg(0);
+ recalc_brg(1);
+}
+
+template<int ch> void mb89371_device::map(address_map &map)
+{
+ map(0x0, 0x0).rw(m_sio[ch], FUNC(i8251_device::data_r), FUNC(i8251_device::data_w));
+ map(0x1, 0x1).rw(m_sio[ch], FUNC(i8251_device::status_r), FUNC(i8251_device::control_w));
+ map(0x2, 0x2).rw(FUNC(mb89371_device::baud_r<ch>), FUNC(mb89371_device::baud_w<ch>));
+ map(0x3, 0x3).rw(FUNC(mb89371_device::mode_r<ch>), FUNC(mb89371_device::mode_w<ch>));
+}
+
+template void mb89371_device::map<0>(address_map &map);
+template void mb89371_device::map<1>(address_map &map);
+
+template<int ch> void mb89371_device::write(offs_t offset, uint8_t data)
{
switch (offset)
{
- case 0: // data
- //printf("%c", data);
+ case 0:
+ m_sio[ch]->data_w(data);
break;
- case 1: // control (0x40 = error reset)
- case 2: // baud (9600 = 2)
- case 3: // mode (8251 compatible?)
+
+ case 1:
+ m_sio[ch]->control_w(data);
+ break;
+
+ case 2:
+ baud_w<ch>(data);
+ break;
+
+ case 3:
+ mode_w<ch>(data);
break;
}
- logerror("MB89371 unimplemented write @%X = %02X & %02X\n", offset, data, mem_mask);
}
-uint8_t mb89371_device::read(offs_t offset, uint8_t mem_mask)
+template void mb89371_device::write<0>(offs_t offset, uint8_t data);
+template void mb89371_device::write<1>(offs_t offset, uint8_t data);
+
+template<int ch> uint8_t mb89371_device::read(offs_t offset)
{
switch (offset)
{
- case 0x00: // data
- break;
- case 0x01: // control
- // bit 0 = txrdy, bit 1 = rxrdy
- break;
+ case 0x00:
+ return m_sio[ch]->data_r();
+
+ case 0x01:
+ return m_sio[ch]->status_r();
+
+ case 0x02:
+ return baud_r<ch>();
+
+ case 0x03:
+ return mode_r<ch>();
}
- logerror("MB89371 unimplemented read @%X & %02X\n", offset, mem_mask);
return 0xff;
}
+
+template uint8_t mb89371_device::read<0>(offs_t offset);
+template uint8_t mb89371_device::read<1>(offs_t offset);
+
+template<int ch> void mb89371_device::brg_tick(int state)
+{
+ m_sio[ch]->write_txc(state);
+ m_sio[ch]->write_rxc(state);
+}
+
+template void mb89371_device::brg_tick<0>(int state);
+template void mb89371_device::brg_tick<1>(int state);
+
+template<int ch> uint8_t mb89371_device::baud_r()
+{
+ return m_baud[ch];
+}
+
+template uint8_t mb89371_device::baud_r<0>();
+template uint8_t mb89371_device::baud_r<1>();
+
+template<int ch> void mb89371_device::baud_w(uint8_t data)
+{
+ uint32_t divider = (2 << data);
+ m_baud[ch] = data;
+ LOG("baud %d = %02x, divider is %d\n", ch, data, divider);
+ recalc_brg(ch);
+}
+
+template void mb89371_device::baud_w<0>(uint8_t data);
+template void mb89371_device::baud_w<1>(uint8_t data);
+
+template<int ch> uint8_t mb89371_device::mode_r()
+{
+ return m_mode[ch];
+}
+
+template uint8_t mb89371_device::mode_r<0>();
+template uint8_t mb89371_device::mode_r<1>();
+
+template <int ch> void mb89371_device::mode_w(uint8_t data)
+{
+ LOG("%02x to mode\n", data);
+ LOG("loopback %d modem %d clock source %s trnemp/st1 %s\n",
+ BIT(data, MODE_LOOPBACK),
+ BIT(data, MODE_MODEM),
+ BIT(data, MODE_USE_BRG) ? "Internal BRG" : "External TRNCLK/RVCLK",
+ BIT(data, MODE_ST1_SEL) ? "ST1" : "TRNEMP");
+
+ m_mode[ch] = data;
+ recalc_brg(ch);
+}
+
+template void mb89371_device::mode_w<0>(uint8_t data);
+template void mb89371_device::mode_w<1>(uint8_t data);
+
+void mb89371_device::recalc_brg(int channel)
+{
+ if (BIT(m_mode[channel], MODE_USE_BRG))
+ {
+ uint32_t rate = clock() / (2 << m_baud[channel]);
+ LOGMASKED(LOG_BAUD_RATE_GENERATOR, "BRG %d enabled at %d Hz\n", channel, rate);
+ m_brg[channel]->set_period(attotime::from_hz(rate));
+ }
+ else
+ {
+ LOGMASKED(LOG_BAUD_RATE_GENERATOR, "BRG %d off\n", channel);
+ m_brg[channel]->set_period(attotime::never);
+ }
+}
+
+template <int ch> void mb89371_device::tx_data_w(int state)
+{
+ // handle loopback
+ if (BIT(m_mode[ch], MODE_LOOPBACK))
+ {
+ m_sio[ch]->write_rxd(state);
+ }
+ else
+ {
+ m_txd_w[ch](state);
+ }
+}
+
+template void mb89371_device::tx_data_w<0>(int state);
+template void mb89371_device::tx_data_w<1>(int state);
+
+template <int ch> void mb89371_device::rx_ready_w(int state)
+{
+ if (BIT(m_mode[ch], MODE_IRQMASK_RXREADY))
+ {
+ m_rxready_w[ch](state);
+ }
+}
+
+template void mb89371_device::rx_ready_w<0>(int state);
+template void mb89371_device::rx_ready_w<1>(int state);
+
+template <int ch> void mb89371_device::tx_ready_w(int state)
+{
+ if (BIT(m_mode[ch], MODE_IRQMASK_TXREADY))
+ {
+ m_txready_w[ch](state);
+ }
+}
+
+template void mb89371_device::tx_ready_w<0>(int state);
+template void mb89371_device::tx_ready_w<1>(int state);
+
+template <int ch> void mb89371_device::tx_empty_w(int state)
+{
+ if (BIT(m_mode[ch], MODE_IRQMASK_TXREADY))
+ {
+ m_txempty_w[ch](state);
+ }
+}
+
+template void mb89371_device::tx_empty_w<0>(int state);
+template void mb89371_device::tx_empty_w<1>(int state);
+
+template <int ch> void mb89371_device::syndet_w(int state)
+{
+ if (BIT(m_mode[ch], MODE_IRQMASK_TXREADY))
+ {
+ m_syndet_w[ch](state);
+ }
+}
+
+template void mb89371_device::syndet_w<0>(int state);
+template void mb89371_device::syndet_w<1>(int state);
diff --git a/src/devices/machine/mb89371.h b/src/devices/machine/mb89371.h
index 10f0180d65c..2f19386dc49 100644
--- a/src/devices/machine/mb89371.h
+++ b/src/devices/machine/mb89371.h
@@ -1,11 +1,7 @@
// license:BSD-3-Clause
-// copyright-holders:smf
+// copyright-holders:R. Belmont, smf
/*
- * MB89371
- *
- * Fujitsu
- * Dual Serial UART
- *
+ * Fujitsu MB89371 Dual Serial UART
*/
#ifndef MAME_MACHINE_MB89371_H
@@ -13,6 +9,9 @@
#pragma once
+#include "i8251.h"
+
+#include "machine/clock.h"
class mb89371_device : public device_t
{
@@ -20,14 +19,67 @@ public:
// construction/destruction
mb89371_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
- void write(offs_t offset, uint8_t data, uint8_t mem_mask = ~0);
- uint8_t read(offs_t offset, uint8_t mem_mask = ~0);
+ template<int ch> void map(address_map &map) ATTR_COLD;
+
+ template<int ch> auto txd_handler() { return m_txd_w[ch].bind(); }
+ template<int ch> auto dtr_handler() { return m_sio[ch].lookup()->dtr_handler(); }
+ template<int ch> auto rts_handler() { return m_sio[ch].lookup()->rts_handler(); }
+ template<int ch> auto rxrdy_handler() { return m_rxready_w[ch].bind(); }
+ template<int ch> auto txrdy_handler() { return m_txready_w[ch].bind(); }
+ template<int ch> auto txempty_handler() { return m_txempty_w[ch].bind(); }
+ template<int ch> auto syndet_handler() { return m_syndet_w[ch].bind(); }
+
+ template<int ch> void write_rxd(int state) { if (!BIT(m_mode[ch], MODE_LOOPBACK)) m_sio[ch]->write_rxd(state); }
+ template<int ch> void write_cts(int state) { m_sio[ch]->write_cts(state); }
+ template<int ch> void write_dsr(int state) { m_sio[ch]->write_dsr(state); }
+
+ template<int ch> void write_txc(int state) { if (!BIT(m_mode[ch], MODE_USE_BRG)) m_sio[ch]->write_txc(state); }
+ template<int ch> void write_rxc(int state) { if (!BIT(m_mode[ch], MODE_USE_BRG)) m_sio[ch]->write_rxc(state); }
+
+ template<int ch> int txrdy_r() { return m_sio[ch]->txrdy_r(); }
+ template<int ch> int rxrdy_r() { return m_sio[ch]->rxrdy_r(); }
+
+ template<int ch> void write(offs_t offset, uint8_t data);
+ template<int ch> uint8_t read(offs_t offset);
protected:
// device-level overrides
virtual void device_start() override ATTR_COLD;
-};
+ virtual void device_add_mconfig(machine_config &config) override ATTR_COLD;
+ virtual void device_post_load() override ATTR_COLD;
+
+private:
+ enum
+ {
+ MODE_ST1_SEL = 0,
+ MODE_USE_BRG,
+ MODE_MODEM,
+ MODE_LOOPBACK,
+ MODE_IRQMASK_TXEMPTY,
+ MODE_IRQMASK_TXREADY,
+ MODE_IRQMASK_RXREADY,
+ MODE_IRQMASK_SYNDET
+ };
+
+ required_device_array<i8251_device, 2> m_sio;
+ required_device_array<clock_device, 2> m_brg;
+ devcb_write_line m_txd_w[2], m_rxready_w[2], m_txready_w[2], m_txempty_w[2], m_syndet_w[2];
+
+ uint8_t m_mode[2], m_baud[2];
+
+ template<int ch> void brg_tick(int state);
+ void recalc_brg(int channel);
+ template<int ch> uint8_t baud_r();
+ template<int ch> void baud_w(uint8_t data);
+ template<int ch> uint8_t mode_r();
+ template<int ch> void mode_w(uint8_t data);
+ template<int ch> void tx_data_w(int state);
+ template<int ch> void rx_ready_w(int state);
+ template<int ch> void tx_ready_w(int state);
+ template<int ch> void tx_empty_w(int state);
+ template<int ch> void syndet_w(int state);
+};
// device type definition
DECLARE_DEVICE_TYPE(MB89371, mb89371_device)
diff --git a/src/mame/konami/konamigq.cpp b/src/mame/konami/konamigq.cpp
index ace96237d60..9b49d9c1716 100644
--- a/src/mame/konami/konamigq.cpp
+++ b/src/mame/konami/konamigq.cpp
@@ -101,7 +101,8 @@ public:
m_dasp(*this, "dasp"),
m_ncr53cf96(*this, "scsi:7:ncr53cf96"),
m_k056800(*this, "k056800"),
- m_pcmram(*this, "pcmram")
+ m_pcmram(*this, "pcmram"),
+ m_duart(*this, "duart")
{
}
@@ -118,6 +119,7 @@ private:
required_device<ncr53cf96_device> m_ncr53cf96;
required_device<k056800_device> m_k056800;
required_shared_ptr<uint8_t> m_pcmram;
+ required_device<mb89371_device> m_duart;
uint8_t m_sound_ctrl = 0;
uint8_t m_sound_intck = 0;
@@ -201,7 +203,8 @@ void konamigq_state::konamigq_map(address_map &map)
map(0x1f230004, 0x1f230007).portr("P3_SERVICE");
map(0x1f238000, 0x1f238003).portr("DSW");
map(0x1f300000, 0x1f5fffff).rw(FUNC(konamigq_state::pcmram_r), FUNC(konamigq_state::pcmram_w)).umask32(0x00ff00ff);
- map(0x1f680000, 0x1f68001f).rw("mb89371", FUNC(mb89371_device::read), FUNC(mb89371_device::write)).umask32(0x00ff00ff);
+ map(0x1f680000, 0x1f680007).rw(m_duart, FUNC(mb89371_device::read<0>), FUNC(mb89371_device::write<0>)).umask32(0x00ff00ff);
+ map(0x1f680008, 0x1f68000f).rw(m_duart, FUNC(mb89371_device::read<1>), FUNC(mb89371_device::write<1>)).umask32(0x00ff00ff);
map(0x1f780000, 0x1f780003).nopw(); /* watchdog? */
}
@@ -339,6 +342,8 @@ void konamigq_state::machine_reset()
m_dma_offset = 0;
m_dma_size = 0;
m_dma_requested = m_dma_is_write = false;
+
+ m_duart->write_cts<0>(CLEAR_LINE);
}
void konamigq_state::konamigq(machine_config &config)
@@ -357,7 +362,7 @@ void konamigq_state::konamigq(machine_config &config)
m_dasp->set_addrmap(AS_DATA, &konamigq_state::konamigq_dasp_map);
m_dasp->set_periodic_int(FUNC(konamigq_state::tms_sync), attotime::from_hz(48000));
- MB89371(config, "mb89371", 0);
+ MB89371(config, m_duart, 4_MHz_XTAL);
EEPROM_93C46_16BIT(config, "eeprom").default_data(konamigq_def_eeprom, 128);
diff --git a/src/mame/konami/konamigv.cpp b/src/mame/konami/konamigv.cpp
index 4b3b2ae77c0..7d99c56fdea 100644
--- a/src/mame/konami/konamigv.cpp
+++ b/src/mame/konami/konamigv.cpp
@@ -256,6 +256,7 @@ public:
, m_ncr53cf96(*this, "scsi:7:ncr53cf96")
, m_btc_trackball(*this, "upd%u", 1)
, m_maincpu(*this, "maincpu")
+ , m_duart(*this, "duart")
{
}
@@ -286,6 +287,7 @@ protected:
optional_device_array<upd4701_device, 2> m_btc_trackball;
required_device<cpu_device> m_maincpu;
+ required_device<mb89371_device> m_duart;
uint32_t *m_dma_data_ptr;
uint32_t m_dma_offset;
@@ -402,7 +404,8 @@ void konamigv_state::konamigv_map(address_map &map)
map(0x1f100004, 0x1f100007).portr("P2");
map(0x1f100008, 0x1f10000b).portr("P3_P4");
map(0x1f180000, 0x1f180003).portw("EEPROMOUT");
- map(0x1f680000, 0x1f68001f).rw("mb89371", FUNC(mb89371_device::read), FUNC(mb89371_device::write)).umask32(0x00ff00ff);
+ map(0x1f680000, 0x1f680007).rw(m_duart, FUNC(mb89371_device::read<0>), FUNC(mb89371_device::write<0>)).umask32(0x00ff00ff);
+ map(0x1f680008, 0x1f68000f).rw(m_duart, FUNC(mb89371_device::read<1>), FUNC(mb89371_device::write<1>)).umask32(0x00ff00ff);
map(0x1f780000, 0x1f780003).nopw(); // watchdog?
}
@@ -502,6 +505,8 @@ void konamigv_state::machine_reset()
m_dma_offset = 0;
m_dma_size = 0;
m_dma_requested = m_dma_is_write = false;
+
+ m_duart->write_cts<0>(CLEAR_LINE);
}
void simpbowl_state::machine_start()
@@ -573,7 +578,7 @@ void konamigv_state::konamigv(machine_config &config)
m_maincpu->subdevice<psxdma_device>("dma")->install_write_handler(5, psxdma_device::write_delegate(&konamigv_state::scsi_dma_write, this));
m_maincpu->subdevice<ram_device>("ram")->set_default_size("2M");
- MB89371(config, "mb89371", 0);
+ MB89371(config, m_duart, 4_MHz_XTAL);
EEPROM_93C46_16BIT(config, "eeprom");
NSCSI_BUS(config, "scsi");
diff --git a/src/mame/konami/ksys573.cpp b/src/mame/konami/ksys573.cpp
index dc75cf57679..706bc18cd81 100644
--- a/src/mame/konami/ksys573.cpp
+++ b/src/mame/konami/ksys573.cpp
@@ -898,7 +898,7 @@ void ksys573_state::gbbchmp_map(address_map &map)
{
konami573_map(map);
// The game waits until transmit is ready, but the chip may not actually be present.
- map(0x1f640000, 0x1f640007).rw(m_duart, FUNC(mb89371_device::read), FUNC(mb89371_device::write)).umask32(0x00ff00ff);
+ map(0x1f640000, 0x1f640007).m(m_duart, FUNC(mb89371_device::map<0>)).umask32(0x00ff00ff);
}
bool ksys573_state::jvs_is_valid_packet()
@@ -1120,6 +1120,11 @@ void ksys573_state::machine_reset()
std::fill_n( m_jvs_input_buffer, sizeof( m_jvs_input_buffer ), 0 );
std::fill_n( m_jvs_output_buffer, sizeof( m_jvs_output_buffer ), 0 );
+
+ if (m_duart.found())
+ {
+ m_duart->write_cts<0>(CLEAR_LINE);
+ }
}
// H8 check at startup (JVS related)
@@ -2930,7 +2935,7 @@ void ksys573_state::salarymc(machine_config &config)
void ksys573_state::gbbchmp(machine_config &config)
{
animechmp(config);
- MB89371(config, m_duart, 0);
+ MB89371(config, m_duart, 4_MHz_XTAL);
m_maincpu->set_addrmap(AS_PROGRAM, &ksys573_state::gbbchmp_map);
}