diff options
| author | 2025-11-02 12:52:27 -0500 | |
|---|---|---|
| committer | 2025-11-02 12:52:27 -0500 | |
| commit | 8777b5dc49b968fc873f9f2b0b544e539ccd3c85 (patch) | |
| tree | dd9670148b98e17dedf5959387cc541bc907c98c | |
| parent | ead8f36931f6741724962684002f848219021e16 (diff) | |
machine/mb89371.cpp: Rewrite as a fully functional device. [R. Belmont]
| -rw-r--r-- | src/devices/machine/mb89371.cpp | 246 | ||||
| -rw-r--r-- | src/devices/machine/mb89371.h | 70 | ||||
| -rw-r--r-- | src/mame/konami/konamigq.cpp | 11 | ||||
| -rw-r--r-- | src/mame/konami/konamigv.cpp | 9 | ||||
| -rw-r--r-- | src/mame/konami/ksys573.cpp | 9 |
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); } |
