mirror of
https://github.com/holub/mame
synced 2025-07-01 08:18:59 +03:00
new device seeq8003
This commit is contained in:
parent
e78ac44ab0
commit
4b70016e83
@ -4079,3 +4079,15 @@ if (MACHINES["XC1700E"]~=null) then
|
|||||||
MAME_DIR .. "src/devices/machine/xc1700e.h",
|
MAME_DIR .. "src/devices/machine/xc1700e.h",
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
---------------------------------------------------
|
||||||
|
--
|
||||||
|
--@src/devices/machine/edlc.h,MACHINES["EDLC"] = true
|
||||||
|
---------------------------------------------------
|
||||||
|
|
||||||
|
if (MACHINES["EDLC"]~=null) then
|
||||||
|
files {
|
||||||
|
MAME_DIR .. "src/devices/machine/edlc.cpp",
|
||||||
|
MAME_DIR .. "src/devices/machine/edlc.h",
|
||||||
|
}
|
||||||
|
end
|
||||||
|
@ -710,6 +710,7 @@ MACHINES["AIC6250"] = true
|
|||||||
MACHINES["DC7085"] = true
|
MACHINES["DC7085"] = true
|
||||||
MACHINES["I82357"] = true
|
MACHINES["I82357"] = true
|
||||||
MACHINES["XC1700E"] = true
|
MACHINES["XC1700E"] = true
|
||||||
|
MACHINES["EDLC"] = true
|
||||||
|
|
||||||
--------------------------------------------------
|
--------------------------------------------------
|
||||||
-- specify available bus cores
|
-- specify available bus cores
|
||||||
|
318
src/devices/machine/edlc.cpp
Normal file
318
src/devices/machine/edlc.cpp
Normal file
@ -0,0 +1,318 @@
|
|||||||
|
// license:BSD-3-Clause
|
||||||
|
// copyright-holders:Patrick Mackinlay
|
||||||
|
|
||||||
|
/*
|
||||||
|
* An emulation of the SEEQ 8003 Ethernet Data Link Controller.
|
||||||
|
*
|
||||||
|
* This implementation uses transmit/receive fifos which hold entire frames,
|
||||||
|
* rather than the 16-byte fifos of the real device to simplify logic. Also,
|
||||||
|
* the bidirectional RxTxEOF line is split into separate read/write handlers
|
||||||
|
* which must be used strictly as follows:
|
||||||
|
*
|
||||||
|
* - rxeof_r() must be read before fifo_r()
|
||||||
|
* - txeof_w() must be written after fifo_w()
|
||||||
|
*
|
||||||
|
* Sources:
|
||||||
|
* - http://www.bitsavers.org/components/seeq/_dataBooks/1985_SEEQ_Data_Book.pdf
|
||||||
|
*
|
||||||
|
* TODO:
|
||||||
|
* - RxDC (discard) and TxRET (retransmit) logic
|
||||||
|
* - 80c03 support
|
||||||
|
* - testing
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "emu.h"
|
||||||
|
#include "edlc.h"
|
||||||
|
#include "hashing.h"
|
||||||
|
|
||||||
|
#define LOG_GENERAL (1U << 0)
|
||||||
|
#define LOG_FRAMES (1U << 1)
|
||||||
|
#define LOG_FILTER (1U << 2)
|
||||||
|
|
||||||
|
//#define VERBOSE (LOG_GENERAL|LOG_FRAMES|LOG_FILTER)
|
||||||
|
#include "logmacro.h"
|
||||||
|
|
||||||
|
DEFINE_DEVICE_TYPE(SEEQ8003, seeq8003_device, "seeq8003", "SEEQ 8003 EDLC")
|
||||||
|
|
||||||
|
static const u8 ETH_BROADCAST[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
|
||||||
|
|
||||||
|
seeq8003_device::seeq8003_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock)
|
||||||
|
: device_t(mconfig, SEEQ8003, tag, owner, clock)
|
||||||
|
, device_network_interface(mconfig, *this, 10.0f)
|
||||||
|
, m_out_int(*this)
|
||||||
|
, m_out_rxrdy(*this)
|
||||||
|
, m_out_txrdy(*this)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::device_start()
|
||||||
|
{
|
||||||
|
m_out_int.resolve_safe();
|
||||||
|
m_out_rxrdy.resolve_safe();
|
||||||
|
m_out_txrdy.resolve_safe();
|
||||||
|
|
||||||
|
save_item(NAME(m_int_state));
|
||||||
|
save_item(NAME(m_station_address));
|
||||||
|
save_item(NAME(m_rx_status));
|
||||||
|
save_item(NAME(m_tx_status));
|
||||||
|
save_item(NAME(m_rx_command));
|
||||||
|
save_item(NAME(m_tx_command));
|
||||||
|
|
||||||
|
m_tx_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(seeq8003_device::transmit), this));
|
||||||
|
m_int_timer = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(seeq8003_device::interrupt), this));
|
||||||
|
|
||||||
|
m_int_state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::device_reset()
|
||||||
|
{
|
||||||
|
m_rx_status = RXS_O;
|
||||||
|
m_tx_status = TXS_O;
|
||||||
|
m_rx_command = 0;
|
||||||
|
m_tx_command = 0;
|
||||||
|
|
||||||
|
m_rx_fifo.clear();
|
||||||
|
m_tx_fifo.clear();
|
||||||
|
m_out_rxrdy(0);
|
||||||
|
|
||||||
|
if (m_dev)
|
||||||
|
m_out_txrdy(1);
|
||||||
|
|
||||||
|
interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int seeq8003_device::recv_start_cb(u8 *buf, int length)
|
||||||
|
{
|
||||||
|
// check receiver disabled
|
||||||
|
if ((m_rx_command & RXC_M) == RXC_M0)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (address_filter(buf))
|
||||||
|
{
|
||||||
|
LOG("receiving frame length %d\n", length);
|
||||||
|
dump_bytes(buf, length);
|
||||||
|
|
||||||
|
return receive(buf, length);
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::map(address_map &map)
|
||||||
|
{
|
||||||
|
map.unmap_value_high();
|
||||||
|
|
||||||
|
map(0, 0).w(FUNC(seeq8003_device::station_address_w<0>));
|
||||||
|
map(1, 1).w(FUNC(seeq8003_device::station_address_w<1>));
|
||||||
|
map(2, 2).w(FUNC(seeq8003_device::station_address_w<2>));
|
||||||
|
map(3, 3).w(FUNC(seeq8003_device::station_address_w<3>));
|
||||||
|
map(4, 4).w(FUNC(seeq8003_device::station_address_w<4>));
|
||||||
|
map(5, 5).w(FUNC(seeq8003_device::station_address_w<5>));
|
||||||
|
map(6, 6).rw(FUNC(seeq8003_device::rx_status_r), FUNC(seeq8003_device::rx_command_w));
|
||||||
|
map(7, 7).rw(FUNC(seeq8003_device::tx_status_r), FUNC(seeq8003_device::tx_command_w));
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 seeq8003_device::fifo_r()
|
||||||
|
{
|
||||||
|
if (m_rx_fifo.empty())
|
||||||
|
fatalerror("seeq8003_device::fifo_r: fifo empty\n");
|
||||||
|
|
||||||
|
u8 const data = m_rx_fifo.dequeue();
|
||||||
|
|
||||||
|
if (m_rx_fifo.empty())
|
||||||
|
{
|
||||||
|
// disable rx fifo
|
||||||
|
m_out_rxrdy(0);
|
||||||
|
|
||||||
|
// schedule interrupt
|
||||||
|
m_int_timer->adjust(attotime::zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int seeq8003_device::rxeof_r()
|
||||||
|
{
|
||||||
|
return m_rx_fifo.queue_length() == 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::fifo_w(u8 data)
|
||||||
|
{
|
||||||
|
if (m_tx_fifo.full())
|
||||||
|
fatalerror("seeq8003_device::fifo_w: fifo full\n");
|
||||||
|
|
||||||
|
m_tx_fifo.enqueue(data);
|
||||||
|
|
||||||
|
if (m_tx_fifo.full())
|
||||||
|
m_out_txrdy(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::txeof_w(int state)
|
||||||
|
{
|
||||||
|
if (state)
|
||||||
|
{
|
||||||
|
// disable tx fifo
|
||||||
|
m_out_txrdy(0);
|
||||||
|
|
||||||
|
// schedule transmit
|
||||||
|
m_tx_timer->adjust(attotime::zero);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 seeq8003_device::rx_status_r()
|
||||||
|
{
|
||||||
|
u8 const data = m_rx_status;
|
||||||
|
|
||||||
|
// clear interrupt
|
||||||
|
m_rx_status |= RXS_O;
|
||||||
|
m_int_timer->adjust(attotime::zero);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 seeq8003_device::tx_status_r()
|
||||||
|
{
|
||||||
|
u8 const data = m_tx_status;
|
||||||
|
|
||||||
|
// clear interrupt
|
||||||
|
m_tx_status |= TXS_O;
|
||||||
|
m_int_timer->adjust(attotime::zero);
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::transmit(void *ptr, int param)
|
||||||
|
{
|
||||||
|
if (m_tx_fifo.queue_length())
|
||||||
|
{
|
||||||
|
u8 buf[MAX_FRAME_SIZE];
|
||||||
|
int length = 0;
|
||||||
|
|
||||||
|
// dequeue to buffer
|
||||||
|
while (!m_tx_fifo.empty())
|
||||||
|
buf[length++] = m_tx_fifo.dequeue();
|
||||||
|
|
||||||
|
// compute and append fcs
|
||||||
|
u32 const fcs = util::crc32_creator::simple(buf, length);
|
||||||
|
buf[length++] = (fcs >> 0) & 0xff;
|
||||||
|
buf[length++] = (fcs >> 8) & 0xff;
|
||||||
|
buf[length++] = (fcs >> 16) & 0xff;
|
||||||
|
buf[length++] = (fcs >> 24) & 0xff;
|
||||||
|
|
||||||
|
LOG("transmitting frame length %d\n", length);
|
||||||
|
dump_bytes(buf, length);
|
||||||
|
|
||||||
|
// transmit the frame
|
||||||
|
send(buf, length);
|
||||||
|
|
||||||
|
// TODO: transmit errors/TxRET
|
||||||
|
|
||||||
|
// update status
|
||||||
|
m_tx_status = TXS_S;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
m_tx_status = TXS_U;
|
||||||
|
|
||||||
|
// enable tx fifo
|
||||||
|
m_out_txrdy(1);
|
||||||
|
|
||||||
|
interrupt();
|
||||||
|
}
|
||||||
|
|
||||||
|
int seeq8003_device::receive(u8 *buf, int length)
|
||||||
|
{
|
||||||
|
// discard if rx status has not been read
|
||||||
|
// TODO: RxDC
|
||||||
|
if (!(m_rx_status & RXS_O))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
m_rx_status = RXS_E;
|
||||||
|
|
||||||
|
// check for errors
|
||||||
|
u32 const fcs = util::crc32_creator::simple(buf, length);
|
||||||
|
if (length < 64)
|
||||||
|
m_rx_status |= RXS_S;
|
||||||
|
else if (~fcs != FCS_RESIDUE)
|
||||||
|
m_rx_status |= RXS_C;
|
||||||
|
else
|
||||||
|
m_rx_status |= RXS_G;
|
||||||
|
|
||||||
|
// enqueue from buffer
|
||||||
|
for (unsigned i = 0; i < length - 4; i++)
|
||||||
|
m_rx_fifo.enqueue(buf[i]);
|
||||||
|
|
||||||
|
// enable rx fifo
|
||||||
|
m_out_rxrdy(1);
|
||||||
|
|
||||||
|
return length;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::interrupt(void *ptr, int param)
|
||||||
|
{
|
||||||
|
int const state =
|
||||||
|
(!(m_tx_status & TXS_O) && (m_tx_status & m_tx_command & TXS_M)) ||
|
||||||
|
(!(m_rx_status & RXS_O) && (m_rx_status & m_rx_command & RXS_M));
|
||||||
|
|
||||||
|
// TODO: assert RxDC for masked rx crc or short frame errors
|
||||||
|
|
||||||
|
if (state != m_int_state)
|
||||||
|
{
|
||||||
|
m_int_state = state;
|
||||||
|
m_out_int(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool seeq8003_device::address_filter(u8 *address)
|
||||||
|
{
|
||||||
|
LOGMASKED(LOG_FILTER, "address_filter testing destination address %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||||
|
address[0], address[1], address[2], address[3], address[4], address[5]);
|
||||||
|
|
||||||
|
if ((m_rx_command & RXC_M) == RXC_M1)
|
||||||
|
{
|
||||||
|
LOG("address_filter accepted: promiscuous mode enabled\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ethernet broadcast
|
||||||
|
if (!memcmp(address, ETH_BROADCAST, 6))
|
||||||
|
{
|
||||||
|
LOGMASKED(LOG_FILTER, "address_filter accepted: broadcast\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// station address
|
||||||
|
if (!memcmp(address, m_station_address, 6))
|
||||||
|
{
|
||||||
|
LOGMASKED(LOG_FILTER, "address_filter accepted: station address match\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ethernet multicast
|
||||||
|
if (((m_rx_command & RXC_M) == RXC_M3) && (address[0] & 0x1))
|
||||||
|
{
|
||||||
|
LOGMASKED(LOG_FILTER, "address_filter accepted: multicast address match\n");
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void seeq8003_device::dump_bytes(u8 *buf, int length)
|
||||||
|
{
|
||||||
|
if (VERBOSE & LOG_FRAMES)
|
||||||
|
{
|
||||||
|
// pad frame with zeros to 8-byte boundary
|
||||||
|
for (int i = 0; i < 8 - (length % 8); i++)
|
||||||
|
buf[length + i] = 0;
|
||||||
|
|
||||||
|
// dump length / 8 (rounded up) groups of 8 bytes
|
||||||
|
for (int i = 0; i < (length + 7) / 8; i++)
|
||||||
|
LOGMASKED(LOG_FRAMES, "%02x %02x %02x %02x %02x %02x %02x %02x\n",
|
||||||
|
buf[i * 8 + 0], buf[i * 8 + 1], buf[i * 8 + 2], buf[i * 8 + 3],
|
||||||
|
buf[i * 8 + 4], buf[i * 8 + 5], buf[i * 8 + 6], buf[i * 8 + 7]);
|
||||||
|
}
|
||||||
|
}
|
125
src/devices/machine/edlc.h
Normal file
125
src/devices/machine/edlc.h
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
// license:BSD-3-Clause
|
||||||
|
// copyright-holders:Patrick Mackinlay
|
||||||
|
|
||||||
|
#ifndef MAME_MACHINE_EDLC_H
|
||||||
|
#define MAME_MACHINE_EDLC_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
class seeq8003_device :
|
||||||
|
public device_t,
|
||||||
|
public device_network_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// callback configuration
|
||||||
|
auto out_int_cb() { return m_out_int.bind(); }
|
||||||
|
auto out_rxrdy_cb() { return m_out_rxrdy.bind(); }
|
||||||
|
auto out_txrdy_cb() { return m_out_txrdy.bind(); }
|
||||||
|
|
||||||
|
seeq8003_device(machine_config const &mconfig, char const *tag, device_t *owner, u32 clock = 0);
|
||||||
|
|
||||||
|
// host interface
|
||||||
|
void map(address_map &map);
|
||||||
|
u8 fifo_r();
|
||||||
|
int rxeof_r();
|
||||||
|
void fifo_w(u8 data);
|
||||||
|
void txeof_w(int state);
|
||||||
|
|
||||||
|
protected:
|
||||||
|
// device_t overrides
|
||||||
|
virtual void device_start() override;
|
||||||
|
virtual void device_reset() override;
|
||||||
|
|
||||||
|
// device_network_interface overrides
|
||||||
|
virtual int recv_start_cb(u8 *buf, int length) override;
|
||||||
|
|
||||||
|
// command/status interface
|
||||||
|
template <unsigned N> void station_address_w(u8 data) { m_station_address[N] = data; }
|
||||||
|
u8 rx_status_r();
|
||||||
|
u8 tx_status_r();
|
||||||
|
void rx_command_w(u8 data) { m_rx_command = data; }
|
||||||
|
void tx_command_w(u8 data) { m_tx_command = data & TXC_M; }
|
||||||
|
|
||||||
|
// helpers
|
||||||
|
void transmit(void *ptr, int param);
|
||||||
|
int receive(u8 *buf, int length);
|
||||||
|
void interrupt(void *ptr = nullptr, int param = 0);
|
||||||
|
bool address_filter(u8 *address);
|
||||||
|
void dump_bytes(u8 *buf, int length);
|
||||||
|
|
||||||
|
// constants and masks
|
||||||
|
static const unsigned MAX_FRAME_SIZE = 1518;
|
||||||
|
static const u32 FCS_RESIDUE = 0xdebb20e3;
|
||||||
|
|
||||||
|
enum rx_status_mask : u8
|
||||||
|
{
|
||||||
|
RXS_V = 0x01, // received frame with overflow error
|
||||||
|
RXS_C = 0x02, // received frame with crc error
|
||||||
|
RXS_D = 0x04, // received frame with dribble error
|
||||||
|
RXS_S = 0x08, // received short frame
|
||||||
|
RXS_E = 0x10, // received end of frame
|
||||||
|
RXS_G = 0x20, // received good frame
|
||||||
|
RXS_O = 0x80, // old/new status
|
||||||
|
|
||||||
|
RXS_M = 0x3f, // mask
|
||||||
|
};
|
||||||
|
enum rx_command_mask : u8
|
||||||
|
{
|
||||||
|
RXC_V = 0x01, // interrupt on receive overflow error
|
||||||
|
RXC_C = 0x02, // interrupt on receive crc error
|
||||||
|
RXC_D = 0x04, // interrupt on receive dribble error
|
||||||
|
RXC_S = 0x08, // interrupt on short frame
|
||||||
|
RXC_E = 0x10, // interrupt on end of frame
|
||||||
|
RXC_G = 0x20, // interrupt on good frame
|
||||||
|
RXC_M = 0xc0, // match mode
|
||||||
|
};
|
||||||
|
enum rx_match_mask : u8
|
||||||
|
{
|
||||||
|
RXC_M0 = 0x00, // receiver disable
|
||||||
|
RXC_M1 = 0x40, // receive all frames
|
||||||
|
RXC_M2 = 0x80, // receive station or broadcast frames
|
||||||
|
RXC_M3 = 0xc0, // receive station, broadcast/multicast frames
|
||||||
|
};
|
||||||
|
enum tx_status_mask : u8
|
||||||
|
{
|
||||||
|
TXS_U = 0x01, // transmit underflow
|
||||||
|
TXS_C = 0x02, // transmit collision
|
||||||
|
TXS_R = 0x04, // 16 transmission attempts
|
||||||
|
TXS_S = 0x08, // transmission successful
|
||||||
|
TXS_O = 0x80, // old/new status
|
||||||
|
|
||||||
|
TXS_M = 0x0f, // mask
|
||||||
|
};
|
||||||
|
enum tx_command_mask : u8
|
||||||
|
{
|
||||||
|
TXC_U = 0x01, // interrupt on transmit underflow
|
||||||
|
TXC_C = 0x02, // interrupt on transmit collision
|
||||||
|
TXC_R = 0x04, // interrupt on 16 transmission attempts
|
||||||
|
TXC_S = 0x08, // interrupt on transmit success
|
||||||
|
|
||||||
|
TXC_M = 0x0f, // write mask
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
emu_timer *m_tx_timer;
|
||||||
|
emu_timer *m_int_timer;
|
||||||
|
|
||||||
|
// device state
|
||||||
|
devcb_write_line m_out_int;
|
||||||
|
devcb_write_line m_out_rxrdy;
|
||||||
|
devcb_write_line m_out_txrdy;
|
||||||
|
|
||||||
|
int m_int_state;
|
||||||
|
u8 m_station_address[6];
|
||||||
|
u8 m_rx_status;
|
||||||
|
u8 m_tx_status;
|
||||||
|
u8 m_rx_command;
|
||||||
|
u8 m_tx_command;
|
||||||
|
|
||||||
|
util::fifo<u8, MAX_FRAME_SIZE> m_rx_fifo;
|
||||||
|
util::fifo<u8, MAX_FRAME_SIZE> m_tx_fifo;
|
||||||
|
};
|
||||||
|
|
||||||
|
DECLARE_DEVICE_TYPE(SEEQ8003, seeq8003_device)
|
||||||
|
|
||||||
|
#endif // MAME_MACHINE_EDLC_H
|
Loading…
Reference in New Issue
Block a user