// license:BSD-3-Clause
// copyright-holders:Ariane Fugmann
/**
MB89374
Fujitsu
Data Link Controller
**/
#include "emu.h"
#include "mb89374.h"
#include "emuopts.h"
//**************************************************************************
// LIVE DEVICE
//**************************************************************************
// device type definition
DEFINE_DEVICE_TYPE(MB89374, mb89374_device, "mb89374", "MB89374 Data Link Controller")
enum
{
REGISTER_SMR0 = 0x00,
REGISTER_SMR1,
REGISTER_SMR2,
REGISTER_CHRR0,
REGISTER_CHRR1,
REGISTER_MSR = 0x06,
REGISTER_MCR,
REGISTER_RXSR0,
REGISTER_RXSR1,
REGISTER_RXCR,
REGISTER_RXIER,
REGISTER_TXSR,
REGISTER_TXCR,
REGISTER_TXIER,
REGISTER_SDR,
REGISTER_TXBCR0,
REGISTER_TXBCR1,
REGISTER_TXFR0,
REGISTER_TXFR1,
REGISTER_SMR3,
REGISTER_PORTR,
REGISTER_REQR,
REGISTER_MASKR,
REGISTER_B1PSR,
REGISTER_B1PCR,
REGISTER_BG1DR,
REGISTER_B2SR = 0x1c,
REGISTER_B2CR,
REGISTER_BG2DR
};
#define SMR0_MASK 0xf0
#define SMR1_MASK 0xe3
#define MSR_MASK 0x80
#define MCR_MASK 0x14
#define RXSR1_MASK 0x08
#define RXCR_MASK 0x7c
#define RXCR_HUNT BIT(m_rxcr, 1)
#define RXCR_RXE BIT(m_rxcr, 7)
#define RXIER_RXDI BIT(m_rxier, 7)
#define TXSR_MASK 0x60
#define TXSR_TXRDY BIT(m_txsr, 0)
#define TXSR_TXEND BIT(m_txsr, 4)
#define TXCR_TXRST BIT(m_txcr, 3)
#define TXCR_TXE BIT(m_txcr, 7)
#define TXIER_MASK 0x60
#define TXIER_TXDI BIT(m_txier, 7)
#define TXFR1_MASK 0x70
#define SMR3_MASK 0x80
#define REQR_MASK 0x38
#define MASKR_MASK 0x38
#define MASKR_MRXDRQ BIT(m_maskr, 6)
#define MASKR_MTXDRQ BIT(m_maskr, 7)
#define B1PSR_MASK 0xfc
#define B2SR_MASK 0xfe
//-------------------------------------------------
// mb89374_device - constructor
//-------------------------------------------------
mb89374_device::mb89374_device( const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock )
: device_t(mconfig, MB89374, tag, owner, clock),
device_execute_interface(mconfig, *this),
m_icount(0),
m_out_irq_cb(*this),
m_out_po_cb{ { *this }, { *this }, { *this }, { *this } }
{
// prepare localhost "filename"
m_localhost[0] = 0;
strcat(m_localhost, "socket.");
strcat(m_localhost, mconfig.options().comm_localhost());
strcat(m_localhost, ":");
strcat(m_localhost, mconfig.options().comm_localport());
// prepare remotehost "filename"
m_remotehost[0] = 0;
strcat(m_remotehost, "socket.");
strcat(m_remotehost, mconfig.options().comm_remotehost());
strcat(m_remotehost, ":");
strcat(m_remotehost, mconfig.options().comm_remoteport());
}
//-------------------------------------------------
// device_start - device-specific startup
//------------------------------------------------
void mb89374_device::device_start()
{
// set our instruction counter
set_icountptr(m_icount);
// resolve callbacks
m_out_irq_cb.resolve_safe();
for(auto &cb : m_out_po_cb)
cb.resolve_safe();
// state saving
save_item(NAME(m_irq));
save_item(NAME(m_po));
save_item(NAME(m_pi));
save_item(NAME(m_ci));
save_item(NAME(m_smr0));
save_item(NAME(m_smr1));
save_item(NAME(m_smr2));
save_item(NAME(m_chrr0));
save_item(NAME(m_chrr1));
save_item(NAME(m_msr));
save_item(NAME(m_mcr));
save_item(NAME(m_rxsr0));
save_item(NAME(m_rxsr1));
save_item(NAME(m_rxcr));
save_item(NAME(m_rxier));
save_item(NAME(m_txsr));
save_item(NAME(m_txcr));
save_item(NAME(m_txier));
save_item(NAME(m_sdr));
save_item(NAME(m_txbcr0));
save_item(NAME(m_txbcr1));
save_item(NAME(m_txfr0));
save_item(NAME(m_txfr1));
save_item(NAME(m_smr3));
save_item(NAME(m_portr));
save_item(NAME(m_reqr));
save_item(NAME(m_maskr));
save_item(NAME(m_b1psr));
save_item(NAME(m_b1pcr));
save_item(NAME(m_bg1dr));
save_item(NAME(m_b2sr));
save_item(NAME(m_b2cr));
save_item(NAME(m_bg2dr));
save_item(NAME(m_intr_delay));
save_item(NAME(m_sock_delay));
save_item(NAME(m_rx_buffer));
save_item(NAME(m_rx_offset));
save_item(NAME(m_rx_length));
save_item(NAME(m_tx_buffer));
save_item(NAME(m_tx_offset));
}
//-------------------------------------------------
// device_reset - device-specific reset
//-------------------------------------------------
void mb89374_device::device_reset()
{
m_smr0 = SMR0_MASK;
m_smr1 = SMR1_MASK;
m_smr2 = 0x00;
m_chrr0 = 0x00;
m_chrr1 = 0x00;
m_msr = MSR_MASK;
m_mcr = MCR_MASK;
m_rxsr0 = 0x00;
m_rxsr1 = RXSR1_MASK;
m_rxcr = RXCR_MASK;
m_rxier = 0x00;
m_txsr = TXSR_MASK;
m_txcr = 0x00;
m_txier = TXIER_MASK;
m_sdr = 0x00;
m_txbcr0 = 0x00;
m_txbcr1 = 0x00;
m_txfr0 = 0x00;
m_txfr1 = TXFR1_MASK;
m_smr3 = SMR3_MASK;
m_portr = 0x00;
m_reqr = REQR_MASK;
m_maskr = MASKR_MASK;
m_b1psr = B1PSR_MASK;
m_b1pcr = 0x00;
m_bg1dr = 0x00;
m_b2sr = B2SR_MASK;
m_b2cr = 0x00;
m_bg2dr = 0x00;
set_irq(0);
set_po0(0);
set_po1(0);
set_po2(0);
set_po3(0);
m_intr_delay = 0;
m_sock_delay = 0x20;
m_rx_length = 0x0000;
m_rx_offset = 0x0000;
m_tx_offset = 0x0000;
}
//-------------------------------------------------
// execute_run -
//-------------------------------------------------
void mb89374_device::execute_run()
{
while (m_icount > 0)
{
// TODO waste some cycles before triggering ints
if (m_intr_delay > 0)
{
m_intr_delay--;
if (m_intr_delay == 0)
{
}
}
if (m_sock_delay > 0)
{
m_sock_delay--;
if (m_sock_delay == 0)
{
m_sock_delay = 0x20;
checkSockets();
}
}
m_icount--;
}
}
//-------------------------------------------------
// read - handler for register reading
//-------------------------------------------------
READ8_MEMBER(mb89374_device::read)
{
uint8_t data = 0xff;
switch (offset & 0x1f)
{
case REGISTER_RXSR0:
data = m_rxsr0;
break;
case REGISTER_RXSR1:
data = m_rxsr1;
break;
case REGISTER_TXSR:
data = m_txsr;
break;
case REGISTER_SDR:
m_sdr = rxRead();
data = m_sdr;
break;
case REGISTER_PORTR:
data = m_portr;
break;
default:
logerror("MB89374 unimplemented register read @%02X\n", offset);
}
return data;
}
//-------------------------------------------------
// write - handler for register writing
//-------------------------------------------------
WRITE8_MEMBER(mb89374_device::write)
{
switch (offset & 0x1f)
{
case REGISTER_SMR2:
m_smr2 = data;
break;
case REGISTER_MCR:
m_mcr = data | MCR_MASK;
break;
case REGISTER_RXCR:
m_rxcr = data | RXCR_MASK;
if (RXCR_HUNT)
rxReset();
break;
case REGISTER_RXIER:
m_rxier = data;
case REGISTER_TXSR:
m_txsr = data | TXSR_MASK;
txReset();
break;
case REGISTER_TXCR:
m_txcr = data;
if (TXCR_TXRST)
txReset();
break;
case REGISTER_TXIER:
m_txier = data | TXIER_MASK;
break;
case REGISTER_SMR3:
m_smr3 = data | SMR3_MASK;
break;
case REGISTER_PORTR:
m_portr = data;
break;
case REGISTER_MASKR:
m_maskr = data | MASKR_MASK;
set_po2((!MASKR_MTXDRQ && TXCR_TXE && TXIER_TXDI) ? 1 : 0);
break;
default:
logerror("MB89374 unimplemented register write @%02X = %02X\n", offset, data);
}
}
//-------------------------------------------------
// pi0_w - handler for RxCI#/PI0
//-------------------------------------------------
WRITE_LINE_MEMBER(mb89374_device::pi0_w)
{
m_pi[0] = state;
}
//-------------------------------------------------
// pi1_w - handler for TxCI#/PI1
//-------------------------------------------------
WRITE_LINE_MEMBER(mb89374_device::pi1_w)
{
m_pi[1] = state;
}
//-------------------------------------------------
// pi2_w - handler for TxDACK#/PI2
//-------------------------------------------------
WRITE_LINE_MEMBER(mb89374_device::pi2_w)
{
m_pi[2] = state;
}
//-------------------------------------------------
// pi3_w - handler for RxDACK#/PI3
//-------------------------------------------------
WRITE_LINE_MEMBER(mb89374_device::pi3_w)
{
m_pi[3] = state;
}
//-------------------------------------------------
// ci_w - handler for TxLAST#/CI#
//-------------------------------------------------
WRITE_LINE_MEMBER(mb89374_device::ci_w)
{
m_ci = state;
if (m_ci == 1 && m_pi[2] == 0)
{
txComplete();
}
}
//-------------------------------------------------
// read - handler for dma reading (rx buffer)
//-------------------------------------------------
READ8_MEMBER(mb89374_device::dma_r)
{
uint8_t data = rxRead();
if (m_rx_offset == m_rx_length)
set_po3(0); // transfer finished; release dma
return data;
}
//-------------------------------------------------
// write - handler for dma writing (tx buffer)
//-------------------------------------------------
WRITE8_MEMBER(mb89374_device::dma_w)
{
txWrite(data);
}
//**************************************************************************
// buffer logic
//**************************************************************************
void mb89374_device::rxReset()
{
m_rx_length = 0;
m_rx_offset = 0;
m_rxsr0 = 0x06; // RXIDL | DIDL
}
uint8_t mb89374_device::rxRead()
{
uint8_t data = m_rx_buffer[m_rx_offset];
m_rx_offset++;
if (m_rx_offset == m_rx_length)
m_rxsr0 |= 0x40; // EOF
if (m_rx_offset >= m_rx_length)
rxReset();
return data;
}
void mb89374_device::txReset()
{
m_tx_offset = 0;
m_txsr |= 0x05;
}
void mb89374_device::txWrite(uint8_t data)
{
m_tx_buffer[m_tx_offset] = data;
m_tx_offset++;
m_txsr = 0x6b;
// prevent overflow
if (m_tx_offset >= 0x0f00)
m_tx_offset = 0x0eff;
}
void mb89374_device::txComplete()
{
if (m_tx_offset > 0)
{
if (m_line_rx && m_line_tx)
{
m_socket_buffer[0x00] = m_tx_offset & 0xff;
m_socket_buffer[0x01] = (m_tx_offset >> 8) & 0xff;
for (int i = 0x00 ; i < m_tx_offset ; i++)
{
m_socket_buffer[i + 2] = m_tx_buffer[i];
}
std::uint32_t dataSize = m_tx_offset + 2;
std::uint32_t written;
m_line_tx->write(&m_socket_buffer, 0, dataSize, written);
}
}
m_txsr = 0x6f;
txReset();
}
void mb89374_device::checkSockets()
{
// check rx socket
if (!m_line_rx)
{
osd_printf_verbose("MB89374 listen on %s\n", m_localhost);
uint64_t filesize; // unused
osd_file::open(m_localhost, OPEN_FLAG_CREATE, m_line_rx, filesize);
}
// check tx socket
if (!m_line_tx)
{
osd_printf_verbose("MB89374 connect to %s\n", m_remotehost);
uint64_t filesize; // unused
osd_file::open(m_remotehost, 0, m_line_tx, filesize);
}
if (m_line_rx && m_line_tx)
{
if (RXCR_RXE)
{
if (m_rx_length == 0)
{
std::uint32_t recv = 0;
m_line_rx->read(m_socket_buffer, 0, 2, recv);
if (recv > 0)
{
if (recv == 2)
m_rx_length = m_socket_buffer[0x01] << 8 | m_socket_buffer[0x00];
else
{
m_rx_length = m_socket_buffer[0x00];
m_line_rx->read(m_socket_buffer, 0, 1, recv);
while (recv == 0) {}
m_rx_length |= m_socket_buffer[0x00] << 8;
}
int offset = 0;
int togo = m_rx_length;
while (togo > 0)
{
m_line_rx->read(m_socket_buffer, 0, togo, recv);
for (int i = 0x00 ; i < recv ; i++)
{
m_rx_buffer[offset] = m_socket_buffer[i];
offset++;
}
togo -= recv;
}
m_rx_offset = 0;
m_rxsr0 = 0x01; // RXRDY
if (m_rx_offset + 1 == m_rx_length)
m_rxsr0 |= 0x40; // EOF
m_rxsr1 = 0xc8;
set_po3(!MASKR_MRXDRQ && RXIER_RXDI ? 1 : 0);
}
}
}
}
}
//**************************************************************************
// INLINE HELPERS
//**************************************************************************
inline void mb89374_device::set_irq(int state)
{
if (m_irq != state)
{
m_out_irq_cb(state);
m_irq = state;
}
}
inline void mb89374_device::set_po0(int state)
{
if (m_po[0] != state)
{
m_out_po_cb[0](state);
m_po[0] = state;
}
}
inline void mb89374_device::set_po1(int state)
{
if (m_po[1] != state)
{
m_out_po_cb[1](state);
m_po[1] = state;
}
}
inline void mb89374_device::set_po2(int state)
{
if (m_po[2] != state)
{
m_out_po_cb[2](state);
m_po[2] = state;
}
}
inline void mb89374_device::set_po3(int state)
{
if (m_po[3] != state)
{
m_out_po_cb[3](state);
m_po[3] = state;
}
}