mirror of
https://github.com/holub/mame
synced 2025-04-22 16:31:49 +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",
|
||||
}
|
||||
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["I82357"] = true
|
||||
MACHINES["XC1700E"] = true
|
||||
MACHINES["EDLC"] = true
|
||||
|
||||
--------------------------------------------------
|
||||
-- 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