mirror of
https://github.com/holub/mame
synced 2025-10-04 16:34:53 +03:00
interpro: notworking -> networking (#3815)
* interpro: notworking -> networking These changes combine to make InterPro networking work on Windows with the TAP-Windows6 driver. * osdnet: add a receive delay (1 frame) after transmit to avoid a time-travel problem * taptun: pad short Ethernet frames and append FCS (Windows-only until Linux taptun behaviour is verified) * clipper: fix bugs in carry flag handling, prefer sign bit for tests * i82586: fix transmit bug, handle reset * networking: delayed transmit/receive A second attempt to fix networking on InterPro systems, by introducing somewhat realistic delays into network transmit and receive paths. This version works by adding functions to device_network_interface which enable a device to be informed when the transmit or receive completes. The delay is only crudely approximated based on the specified bandwidth and the number of bytes being transmitted, but it should be good enough in practice. Existing drivers should not be impacted by these changes; overriding the new functions (and no longer overriding recv_cb) is necessary to obtain the new behaviour. Changes from the previous commit: * i82586: improve interrupt handling, implement delayed transmit/receive behaviour * dinetwork: add transmit/receive delay timers, handlers and logic * osdnet: remove receive delay, add the ability to start the receive timer
This commit is contained in:
parent
21fa0dfd4f
commit
8919ce5645
@ -42,7 +42,7 @@
|
||||
|
||||
#include "logmacro.h"
|
||||
|
||||
// disable FCS insertion (on transmit) and checking (on receive) because pcap doesn't expose them
|
||||
// disable FCS insertion on transmit
|
||||
#define I82586_FCS 0
|
||||
|
||||
ALLOW_SAVE_TYPE(i82586_base_device::cu_state);
|
||||
@ -112,46 +112,53 @@ CFG_PARAMS[] =
|
||||
};
|
||||
|
||||
i82586_base_device::i82586_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endian, u8 datawidth, u8 addrwidth)
|
||||
: device_t(mconfig, type, tag, owner, clock),
|
||||
device_memory_interface(mconfig, *this),
|
||||
device_network_interface(mconfig, *this, 10.0f),
|
||||
m_space_config("shared", endian, datawidth, addrwidth),
|
||||
m_out_irq(*this),
|
||||
m_cx(false),
|
||||
m_fr(false),
|
||||
m_cna(false),
|
||||
m_rnr(false),
|
||||
m_irq_state(false),
|
||||
m_initialised(false),
|
||||
m_cu_state(CU_IDLE),
|
||||
m_ru_state(RU_IDLE),
|
||||
m_scp_address(SCP_ADDRESS),
|
||||
m_lb_length(0)
|
||||
{}
|
||||
: device_t(mconfig, type, tag, owner, clock)
|
||||
, device_memory_interface(mconfig, *this)
|
||||
, device_network_interface(mconfig, *this, 10.0f)
|
||||
, m_space_config("shared", endian, datawidth, addrwidth)
|
||||
, m_out_irq(*this)
|
||||
, m_cx(false)
|
||||
, m_fr(false)
|
||||
, m_cna(false)
|
||||
, m_rnr(false)
|
||||
, m_initialised(false)
|
||||
, m_irq_assert(1)
|
||||
, m_cu_state(CU_IDLE)
|
||||
, m_ru_state(RU_IDLE)
|
||||
, m_scp_address(SCP_ADDRESS)
|
||||
, m_lb_length(0)
|
||||
{
|
||||
}
|
||||
|
||||
i82586_device::i82586_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: i82586_base_device(mconfig, I82586, tag, owner, clock, ENDIANNESS_LITTLE, 16, 24)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
i82596_device::i82596_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endian, u8 datawidth)
|
||||
: i82586_base_device(mconfig, type, tag, owner, clock, endian, datawidth, 32)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
i82596_le16_device::i82596_le16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: i82596_device(mconfig, I82596_LE16, tag, owner, clock, ENDIANNESS_LITTLE, 16)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
i82596_be16_device::i82596_be16_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: i82596_device(mconfig, I82596_BE16, tag, owner, clock, ENDIANNESS_BIG, 16)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
i82596_le32_device::i82596_le32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: i82596_device(mconfig, I82596_LE32, tag, owner, clock, ENDIANNESS_LITTLE, 32)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
i82596_be32_device::i82596_be32_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock)
|
||||
: i82596_device(mconfig, I82596_BE32, tag, owner, clock, ENDIANNESS_BIG, 32)
|
||||
{}
|
||||
{
|
||||
}
|
||||
|
||||
// shared implementation
|
||||
void i82586_base_device::device_start()
|
||||
@ -169,7 +176,6 @@ void i82586_base_device::device_start()
|
||||
save_item(NAME(m_fr));
|
||||
save_item(NAME(m_cna));
|
||||
save_item(NAME(m_rnr));
|
||||
save_item(NAME(m_irq_state));
|
||||
save_item(NAME(m_initialised));
|
||||
|
||||
save_item(NAME(m_cu_state));
|
||||
@ -197,7 +203,6 @@ void i82586_base_device::device_reset()
|
||||
m_fr = false;
|
||||
m_cna = false;
|
||||
m_rnr = false;
|
||||
m_irq_state = false;
|
||||
m_initialised = false;
|
||||
|
||||
m_cu_state = CU_IDLE;
|
||||
@ -213,7 +218,6 @@ void i82586_base_device::device_timer(emu_timer &timer, device_timer_id id, int
|
||||
{
|
||||
case CU_TIMER:
|
||||
cu_execute();
|
||||
update_scb();
|
||||
break;
|
||||
|
||||
case RU_TIMER:
|
||||
@ -237,7 +241,7 @@ device_memory_interface::space_config_vector i82586_base_device::memory_space_co
|
||||
|
||||
WRITE_LINE_MEMBER(i82586_base_device::ca)
|
||||
{
|
||||
LOG("channel attention %s (%s)\n", state ? "asserted" : "deasserted", machine().describe_context());
|
||||
LOG("channel attention %s (%s)\n", state ? "asserted" : "cleared", machine().describe_context());
|
||||
|
||||
if (state)
|
||||
{
|
||||
@ -249,7 +253,7 @@ WRITE_LINE_MEMBER(i82586_base_device::ca)
|
||||
}
|
||||
}
|
||||
|
||||
void i82586_base_device::recv_cb(u8 *buf, int length)
|
||||
int i82586_base_device::recv_start_cb(u8 *buf, int length)
|
||||
{
|
||||
switch (m_ru_state)
|
||||
{
|
||||
@ -264,9 +268,7 @@ void i82586_base_device::recv_cb(u8 *buf, int length)
|
||||
LOG("recv_cb receiving frame length %d\n", length);
|
||||
dump_bytes(buf, length);
|
||||
|
||||
ru_execute(buf, length);
|
||||
|
||||
update_scb();
|
||||
return ru_execute(buf, length);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -275,6 +277,15 @@ void i82586_base_device::recv_cb(u8 *buf, int length)
|
||||
// TODO: accumulate statistics
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void i82586_base_device::recv_complete_cb(int result)
|
||||
{
|
||||
ru_complete(result);
|
||||
|
||||
update_scb();
|
||||
}
|
||||
|
||||
void i82586_base_device::process_scb()
|
||||
@ -282,12 +293,20 @@ void i82586_base_device::process_scb()
|
||||
// fetch current command and status
|
||||
m_scb_cs = m_space->read_dword(m_scb_address);
|
||||
|
||||
// handle reset
|
||||
if (m_scb_cs & RESET)
|
||||
{
|
||||
LOG("process_scb reset\n");
|
||||
|
||||
device_reset();
|
||||
return;
|
||||
}
|
||||
|
||||
static const char *const CUC_NAME[] = { "NOP", "START", "RESUME", "SUSPEND", "ABORT", "THROTTLE_D", "THROTTLE_I", "reserved" };
|
||||
static const char *const RUC_NAME[] = { "NOP", "START", "RESUME", "SUSPEND", "ABORT", "reserved", "reserved", "reserved" };
|
||||
LOG("process_scb command/status 0x%08x (cuc %s, ruc %s%s)\n", m_scb_cs,
|
||||
LOG("process_scb command/status 0x%08x (cuc %s, ruc %s)\n", m_scb_cs,
|
||||
CUC_NAME[(m_scb_cs & CUC) >> 24],
|
||||
RUC_NAME[(m_scb_cs & RUC) >> 20],
|
||||
m_scb_cs & RESET ? ", reset" : "");
|
||||
RUC_NAME[(m_scb_cs & RUC) >> 20]);
|
||||
|
||||
// clear interrupt flags when acknowledged
|
||||
if (m_scb_cs & ACK_CX)
|
||||
@ -379,6 +398,8 @@ void i82586_base_device::update_scb()
|
||||
(m_cu_state << 8) |
|
||||
(m_ru_state << 4));
|
||||
|
||||
LOG("update_scb%s%s%s%s\n", m_cx ? " CX" : "", m_fr ? " FR" : "", m_cna ? " CNA" : "", m_rnr ? " RNR" : "");
|
||||
|
||||
// update interrupt status
|
||||
set_irq(m_cx || m_fr || m_cna || m_rnr);
|
||||
}
|
||||
@ -386,7 +407,8 @@ void i82586_base_device::update_scb()
|
||||
void i82586_base_device::cu_execute()
|
||||
{
|
||||
// fetch the command block command/status
|
||||
u32 cb_cs = m_space->read_dword(m_cba);
|
||||
const u32 cb_cs = m_space->read_dword(m_cba);
|
||||
u16 status = 0;
|
||||
|
||||
// set busy status
|
||||
m_space->write_dword(m_cba, cb_cs | CB_B);
|
||||
@ -400,52 +422,57 @@ void i82586_base_device::cu_execute()
|
||||
switch (cb_cs & CB_CMD)
|
||||
{
|
||||
case CB_NOP:
|
||||
cb_cs |= CB_OK;
|
||||
status |= CB_OK;
|
||||
break;
|
||||
|
||||
case CB_IASETUP:
|
||||
if (cu_iasetup())
|
||||
cb_cs |= CB_OK;
|
||||
status |= CB_OK;
|
||||
break;
|
||||
|
||||
case CB_CONFIGURE:
|
||||
if (cu_configure())
|
||||
cb_cs |= CB_OK;
|
||||
status |= CB_OK;
|
||||
break;
|
||||
|
||||
case CB_MCSETUP:
|
||||
if (cu_mcsetup())
|
||||
cb_cs |= CB_OK;
|
||||
status |= CB_OK;
|
||||
break;
|
||||
|
||||
case CB_TRANSMIT:
|
||||
// always turn on the heartbeat indicator status after a successful transmission; not
|
||||
// strictly correct, but allows one InterPro 2000 diagnostic to pass
|
||||
if (cu_transmit(cb_cs))
|
||||
cb_cs |= CB_OK | CB_S6;
|
||||
return;
|
||||
break;
|
||||
|
||||
case CB_TDREFLECT:
|
||||
if (cu_tdreflect())
|
||||
cb_cs |= CB_OK;
|
||||
status |= CB_OK;
|
||||
break;
|
||||
|
||||
case CB_DUMP:
|
||||
if (cu_dump())
|
||||
cb_cs |= CB_OK;
|
||||
status |= CB_OK;
|
||||
break;
|
||||
|
||||
case CB_DIAGNOSE:
|
||||
cb_cs |= CB_OK;
|
||||
status |= CB_OK;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
// abort status
|
||||
cb_cs |= CB_A;
|
||||
status |= CB_A;
|
||||
|
||||
// complete command
|
||||
cu_complete(status);
|
||||
}
|
||||
|
||||
void i82586_base_device::cu_complete(const u16 status)
|
||||
{
|
||||
// clear busy status and set completion status
|
||||
m_space->write_dword(m_cba, cb_cs | CB_C);
|
||||
const u32 cb_cs = m_space->read_dword(m_cba);
|
||||
m_space->write_dword(m_cba, (cb_cs & ~0xffffU) | CB_C | status);
|
||||
|
||||
// chain to next command
|
||||
if (!(cb_cs & CB_EL))
|
||||
@ -479,7 +506,9 @@ void i82586_base_device::cu_execute()
|
||||
LOG("cu_execute complete state %s\n", CU_STATE_NAME[m_cu_state]);
|
||||
|
||||
// set command executed status
|
||||
m_cx = (cb_cs & CB_I) && (cb_cs & CB_OK);
|
||||
m_cx = (cb_cs & CB_I) && (status & CB_OK);
|
||||
|
||||
update_scb();
|
||||
}
|
||||
|
||||
bool i82586_base_device::address_filter(u8 *mac)
|
||||
@ -531,15 +560,12 @@ bool i82586_base_device::address_filter(u8 *mac)
|
||||
// shared helpers
|
||||
void i82586_base_device::set_irq(bool irq)
|
||||
{
|
||||
if (m_irq_state != irq)
|
||||
if (irq)
|
||||
{
|
||||
m_irq_state = irq;
|
||||
m_out_irq(m_irq_state ? ASSERT_LINE : CLEAR_LINE);
|
||||
}
|
||||
else if (m_irq_state && irq)
|
||||
{
|
||||
m_out_irq(CLEAR_LINE);
|
||||
m_out_irq(ASSERT_LINE);
|
||||
LOG("irq asserted\n");
|
||||
|
||||
m_out_irq(m_irq_assert);
|
||||
m_out_irq(!m_irq_assert);
|
||||
}
|
||||
}
|
||||
|
||||
@ -747,6 +773,7 @@ void i82586_device::device_start()
|
||||
{
|
||||
i82586_base_device::device_start();
|
||||
|
||||
save_item(NAME(m_rbd_offset));
|
||||
save_item(NAME(m_cfg_bytes));
|
||||
}
|
||||
|
||||
@ -987,10 +1014,17 @@ bool i82586_device::cu_transmit(u32 command)
|
||||
LOG("cu_transmit sending frame length %d\n", length);
|
||||
dump_bytes(buf, length);
|
||||
|
||||
return send(buf, length) == 0;
|
||||
return send(buf, length) == length;
|
||||
}
|
||||
}
|
||||
|
||||
void i82586_base_device::send_complete_cb(int result)
|
||||
{
|
||||
// always turn on the heartbeat indicator status after a successful transmit; not
|
||||
// strictly correct, but allows one InterPro 2000 diagnostic to pass
|
||||
cu_complete(result ? (CB_OK | CB_S6) : CB_S9);
|
||||
}
|
||||
|
||||
bool i82586_device::cu_tdreflect()
|
||||
{
|
||||
m_space->write_word(m_cba + 6, TDR_LNK_OK | TDR_TIME);
|
||||
@ -1036,10 +1070,11 @@ bool i82586_device::address_filter(u8 *mac)
|
||||
return false;
|
||||
}
|
||||
|
||||
void i82586_device::ru_execute(u8 *buf, int length)
|
||||
u16 i82586_device::ru_execute(u8 *buf, int length)
|
||||
{
|
||||
// fetch receive frame descriptor command/status
|
||||
u32 rfd_cs = m_space->read_dword(m_rfd);
|
||||
u16 status = 0;
|
||||
|
||||
// current buffer position and bytes remaining
|
||||
int position = 0, remaining = length;
|
||||
@ -1051,9 +1086,8 @@ void i82586_device::ru_execute(u8 *buf, int length)
|
||||
|
||||
// set short frame status
|
||||
if (length < cfg_min_frame_length())
|
||||
rfd_cs |= RFD_S_SHORT;
|
||||
status |= RFD_S_SHORT;
|
||||
|
||||
#if I82586_FCS
|
||||
// set crc status
|
||||
if (~compute_crc(buf, length, cfg_crc16()) != FCS_RESIDUE)
|
||||
{
|
||||
@ -1063,15 +1097,14 @@ void i82586_device::ru_execute(u8 *buf, int length)
|
||||
// increment crc error count
|
||||
m_space->write_word(m_scb_address + 8, m_space->read_word(m_scb_address + 8) + 1);
|
||||
|
||||
rfd_cs |= RFD_S_CRC;
|
||||
status |= RFD_S_CRC;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: alignment error (crc in misaligned frame), status bit 10
|
||||
// TODO: increment alignment error counter
|
||||
|
||||
// fetch initial rbd offset from rfd
|
||||
u16 rbd_offset = m_space->read_word(m_rfd + 6);
|
||||
m_rbd_offset = m_space->read_word(m_rfd + 6);
|
||||
|
||||
if (!cfg_no_src_add_ins())
|
||||
{
|
||||
@ -1087,11 +1120,11 @@ void i82586_device::ru_execute(u8 *buf, int length)
|
||||
}
|
||||
|
||||
// store remaining bytes in receive buffers
|
||||
while (remaining && rbd_offset != RBD_EMPTY)
|
||||
while (remaining && m_rbd_offset != RBD_EMPTY)
|
||||
{
|
||||
// fetch the count and address for this buffer
|
||||
u32 rb_address = m_space->read_dword(m_scb_base + rbd_offset + 4);
|
||||
u16 rbd_size = m_space->read_word(m_scb_base + rbd_offset + 8);
|
||||
u32 rb_address = m_space->read_dword(m_scb_base + m_rbd_offset + 4);
|
||||
u16 rbd_size = m_space->read_word(m_scb_base + m_rbd_offset + 8);
|
||||
|
||||
// compute number of bytes to store in buffer
|
||||
int actual = remaining > (rbd_size & RB_SIZE) ? (rbd_size & RB_SIZE) : remaining;
|
||||
@ -1104,17 +1137,17 @@ void i82586_device::ru_execute(u8 *buf, int length)
|
||||
remaining -= actual;
|
||||
|
||||
// store actual count
|
||||
m_space->write_word(m_scb_base + rbd_offset + 0, actual | RB_F | (remaining ? 0 : RB_EOF));
|
||||
m_space->write_word(m_scb_base + m_rbd_offset + 0, actual | RB_F | (remaining ? 0 : RB_EOF));
|
||||
|
||||
// check if buffers exhausted
|
||||
if ((rbd_size & RB_EL))
|
||||
{
|
||||
rbd_offset = RBD_EMPTY;
|
||||
m_rbd_offset = RBD_EMPTY;
|
||||
|
||||
if (remaining)
|
||||
{
|
||||
// set buffers exhausted status
|
||||
rfd_cs |= RFD_S_BUFFER;
|
||||
status |= RFD_S_BUFFER;
|
||||
|
||||
m_ru_state = RU_NR;
|
||||
m_rnr = true;
|
||||
@ -1122,28 +1155,33 @@ void i82586_device::ru_execute(u8 *buf, int length)
|
||||
}
|
||||
else
|
||||
// fetch next rbd offset
|
||||
rbd_offset = m_space->read_word(m_scb_base + rbd_offset + 2);
|
||||
m_rbd_offset = m_space->read_word(m_scb_base + m_rbd_offset + 2);
|
||||
}
|
||||
|
||||
if (remaining == 0 || cfg_save_bad_frames())
|
||||
// set frame received status
|
||||
rfd_cs |= RFD_C;
|
||||
status |= RFD_C;
|
||||
|
||||
// frame received without errors
|
||||
if (!(rfd_cs & RFD_ERROR_82586))
|
||||
{
|
||||
LOG("ru_execute frame received without error\n");
|
||||
if (!(status & RFD_ERROR_82586))
|
||||
status |= RFD_OK;
|
||||
|
||||
rfd_cs |= RFD_OK;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void i82586_device::ru_complete(const u16 status)
|
||||
{
|
||||
if (status & RFD_OK)
|
||||
LOG("ru_complete frame received without error\n");
|
||||
else
|
||||
LOG("ru_execute frame received with errors status 0x%04x\n", rfd_cs);
|
||||
LOG("ru_complete frame received with errors status 0x%04x\n", status);
|
||||
|
||||
// store status
|
||||
m_space->write_dword(m_rfd, rfd_cs);
|
||||
// update receive frame descriptor status
|
||||
u32 rfd_cs = m_space->read_dword(m_rfd);
|
||||
m_space->write_dword(m_rfd, (rfd_cs & ~0xffffU) | status);
|
||||
|
||||
// if we received without error, or we're saving bad frames, advance to the next rfd
|
||||
if ((rfd_cs & RFD_OK) || cfg_save_bad_frames())
|
||||
if ((status & RFD_OK) || cfg_save_bad_frames())
|
||||
{
|
||||
if (!(rfd_cs & RFD_EL))
|
||||
{
|
||||
@ -1151,8 +1189,8 @@ void i82586_device::ru_execute(u8 *buf, int length)
|
||||
m_rfd = m_scb_base + m_space->read_word(m_rfd + 4);
|
||||
|
||||
// store next free rbd address into rfd
|
||||
if (rbd_offset != RBD_EMPTY)
|
||||
m_space->write_word(m_rfd + 6, rbd_offset);
|
||||
if (m_rbd_offset != RBD_EMPTY)
|
||||
m_space->write_word(m_rfd + 6, m_rbd_offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1172,7 +1210,7 @@ void i82586_device::ru_execute(u8 *buf, int length)
|
||||
}
|
||||
|
||||
static const char *const RU_STATE_NAME[] = { "IDLE", "SUSPENDED", "NO RESOURCES", nullptr, "READY" };
|
||||
LOG("ru_execute complete state %s\n", RU_STATE_NAME[m_ru_state]);
|
||||
LOG("ru_complete complete state %s\n", RU_STATE_NAME[m_ru_state]);
|
||||
}
|
||||
|
||||
u32 i82586_device::address(u32 base, int offset, int address, u16 empty)
|
||||
@ -1191,6 +1229,8 @@ void i82596_device::device_start()
|
||||
|
||||
save_item(NAME(m_sysbus));
|
||||
|
||||
save_item(NAME(m_irq_assert));
|
||||
save_item(NAME(m_rbd_address));
|
||||
save_item(NAME(m_mac_multi_ia));
|
||||
}
|
||||
|
||||
@ -1267,6 +1307,9 @@ void i82596_device::initialise()
|
||||
break;
|
||||
}
|
||||
|
||||
// configure interrupt polarity
|
||||
m_irq_assert = (m_sysbus & SYSBUS_INT) ? 0 : 1;
|
||||
|
||||
// clear iscp busy byte
|
||||
m_space->write_byte(iscp_address, 0);
|
||||
|
||||
@ -1652,7 +1695,7 @@ bool i82596_device::cu_transmit(u32 command)
|
||||
LOG("cu_transmit sending frame length %d\n", length);
|
||||
dump_bytes(buf, length);
|
||||
|
||||
return send(buf, length) == 0;
|
||||
return send(buf, length) == length;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1743,10 +1786,11 @@ bool i82596_device::address_filter(u8 *mac)
|
||||
return false;
|
||||
}
|
||||
|
||||
void i82596_device::ru_execute(u8 *buf, int length)
|
||||
u16 i82596_device::ru_execute(u8 *buf, int length)
|
||||
{
|
||||
// fetch receive frame descriptor command/status
|
||||
u32 rfd_cs = m_space->read_dword(m_rfd);
|
||||
const u32 rfd_cs = m_space->read_dword(m_rfd);
|
||||
u16 status = 0;
|
||||
|
||||
// offset into rfd/rbd for linear mode
|
||||
int linear_offset = mode() == MODE_LINEAR ? 4 : 0;
|
||||
@ -1782,10 +1826,9 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
if (mode() != MODE_82586)
|
||||
m_space->write_dword(m_scb_address + 28 + linear_offset, m_space->read_dword(m_scb_address + 28 + linear_offset) + 1);
|
||||
|
||||
rfd_cs |= RFD_S_SHORT;
|
||||
status |= RFD_S_SHORT;
|
||||
}
|
||||
|
||||
#if I82586_FCS
|
||||
// set crc status
|
||||
if (~compute_crc(buf, length, cfg_crc16()) != FCS_RESIDUE)
|
||||
{
|
||||
@ -1798,19 +1841,18 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
else
|
||||
m_space->write_dword(m_scb_address + 8 + linear_offset, m_space->read_dword(m_scb_address + 8 + linear_offset) + 1);
|
||||
|
||||
rfd_cs |= RFD_S_CRC;
|
||||
status |= RFD_S_CRC;
|
||||
}
|
||||
#endif
|
||||
|
||||
// TODO: alignment error (crc in misaligned frame), status bit 10
|
||||
// TODO: increment alignment error counter
|
||||
|
||||
// set multicast status
|
||||
if (mode() != MODE_82586 && memcmp(buf, get_mac(), cfg_address_length()))
|
||||
rfd_cs |= RFD_S_MULTICAST;
|
||||
status |= RFD_S_MULTICAST;
|
||||
|
||||
// fetch initial rbd address from rfd
|
||||
u32 rbd_address = address(m_rfd, 6, 8, RBD_EMPTY);
|
||||
m_rbd_address = address(m_rfd, 6, 8, RBD_EMPTY);
|
||||
|
||||
// check for simplified mode
|
||||
if (mode() != MODE_82586 && !(rfd_cs & RFD_SF))
|
||||
@ -1839,7 +1881,7 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
m_space->write_word(m_rfd + 8 + linear_offset, actual | RB_F | RB_EOF);
|
||||
|
||||
// set frame received and truncated frame status
|
||||
rfd_cs |= RFD_C | (actual < length ? RFD_S_TRUNCATED : 0);
|
||||
status |= RFD_C | (actual < length ? RFD_S_TRUNCATED : 0);
|
||||
}
|
||||
else
|
||||
LOG("ru_execute discarding %d byte frame exceeding rfd size %d\n", length, rfd_size);
|
||||
@ -1879,11 +1921,11 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
}
|
||||
|
||||
// store remaining bytes in receive buffers
|
||||
while (remaining && rbd_address != RBD_EMPTY)
|
||||
while (remaining && m_rbd_address != RBD_EMPTY)
|
||||
{
|
||||
// fetch the count and address for this buffer
|
||||
u32 rb_address = m_space->read_dword(rbd_address + 4 + linear_offset);
|
||||
u16 rbd_size = m_space->read_word(rbd_address + 8 + linear_offset);
|
||||
u32 rb_address = m_space->read_dword(m_rbd_address + 4 + linear_offset);
|
||||
u16 rbd_size = m_space->read_word(m_rbd_address + 8 + linear_offset);
|
||||
|
||||
// compute number of bytes to store in buffer
|
||||
int actual = remaining > (rbd_size & RB_SIZE) ? (rbd_size & RB_SIZE) : remaining;
|
||||
@ -1896,17 +1938,17 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
remaining -= actual;
|
||||
|
||||
// store actual count
|
||||
m_space->write_word(rbd_address + 0, actual | RB_F | (remaining ? 0 : RB_EOF));
|
||||
m_space->write_word(m_rbd_address + 0, actual | RB_F | (remaining ? 0 : RB_EOF));
|
||||
|
||||
// check if buffers exhausted
|
||||
if ((rbd_size & RB_EL))
|
||||
{
|
||||
rbd_address = RBD_EMPTY;
|
||||
m_rbd_address = RBD_EMPTY;
|
||||
|
||||
if (remaining)
|
||||
{
|
||||
// set buffers exhausted status
|
||||
rfd_cs |= RFD_S_BUFFER;
|
||||
status |= RFD_S_BUFFER;
|
||||
|
||||
m_ru_state = mode() == MODE_82586 ? RU_NR : RU_NR_RBD;
|
||||
m_rnr = true;
|
||||
@ -1914,26 +1956,30 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
}
|
||||
else
|
||||
// fetch next rbd address
|
||||
rbd_address = address(rbd_address, 2, 4);
|
||||
m_rbd_address = address(m_rbd_address, 2, 4);
|
||||
}
|
||||
|
||||
if (remaining == 0 || cfg_save_bad_frames())
|
||||
// set frame received status
|
||||
rfd_cs |= RFD_C;
|
||||
status |= RFD_C;
|
||||
}
|
||||
|
||||
// frame received without errors
|
||||
if (!(rfd_cs & (mode() == MODE_82586 ? RFD_ERROR_82586 : RFD_ERROR)))
|
||||
{
|
||||
LOG("ru_execute frame received without error\n");
|
||||
if (!(status & (mode() == MODE_82586 ? RFD_ERROR_82586 : RFD_ERROR)))
|
||||
status |= RFD_OK;
|
||||
|
||||
rfd_cs |= RFD_OK;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
void i82596_device::ru_complete(const u16 status)
|
||||
{
|
||||
if (status & RFD_OK)
|
||||
LOG("ru_complete frame received without error\n");
|
||||
else
|
||||
LOG("ru_execute frame received with errors status 0x%04x\n", rfd_cs);
|
||||
LOG("ru_complete frame received with errors status 0x%04x\n", status);
|
||||
|
||||
// store status
|
||||
m_space->write_dword(m_rfd, rfd_cs);
|
||||
const u32 rfd_cs = m_space->read_dword(m_rfd);
|
||||
m_space->write_dword(m_rfd, (rfd_cs & ~0xffffU) | status);
|
||||
|
||||
// if we received without error, or we're saving bad frames, advance to the next rfd
|
||||
if ((rfd_cs & RFD_OK) || cfg_save_bad_frames())
|
||||
@ -1944,12 +1990,12 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
m_rfd = address(m_rfd, 4, 4);
|
||||
|
||||
// store next free rbd address into rfd
|
||||
if (rbd_address != RBD_EMPTY)
|
||||
if (m_rbd_address != RBD_EMPTY)
|
||||
{
|
||||
if (mode() == MODE_LINEAR)
|
||||
m_space->write_dword(m_rfd + 8, rbd_address);
|
||||
m_space->write_dword(m_rfd + 8, m_rbd_address);
|
||||
else
|
||||
m_space->write_word(m_rfd + 6, rbd_address - m_scb_base);
|
||||
m_space->write_word(m_rfd + 6, m_rbd_address - m_scb_base);
|
||||
}
|
||||
}
|
||||
else
|
||||
@ -1970,7 +2016,7 @@ void i82596_device::ru_execute(u8 *buf, int length)
|
||||
}
|
||||
|
||||
static const char *const RU_STATE_NAME[] = { "IDLE", "SUSPENDED", "NO RESOURCES", nullptr, "READY", nullptr, nullptr, nullptr, nullptr, nullptr, "NO RESOURCES (RFD)", nullptr, "NO RESOURCES (RBD)" };
|
||||
LOG("ru_execute complete state %s\n", RU_STATE_NAME[m_ru_state]);
|
||||
LOG("ru_complete complete state %s\n", RU_STATE_NAME[m_ru_state]);
|
||||
}
|
||||
|
||||
u32 i82596_device::address(u32 base, int offset, int address, u16 empty)
|
||||
|
@ -158,7 +158,6 @@ public:
|
||||
auto out_irq_cb() { return m_out_irq.bind(); }
|
||||
|
||||
DECLARE_WRITE_LINE_MEMBER(ca);
|
||||
virtual void recv_cb(u8 *buf, int length) override;
|
||||
|
||||
protected:
|
||||
i82586_base_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, endianness_t endian, u8 datawidth, u8 addrwidth);
|
||||
@ -170,6 +169,11 @@ protected:
|
||||
virtual void device_timer(emu_timer &timer, device_timer_id id, int param, void *ptr) override;
|
||||
virtual space_config_vector memory_space_config() const override;
|
||||
|
||||
// device_network_interface overrides
|
||||
virtual void send_complete_cb(int result) override;
|
||||
virtual int recv_start_cb(u8 *buf, int length) override;
|
||||
virtual void recv_complete_cb(int result) override;
|
||||
|
||||
// setup and scb
|
||||
virtual void initialise() = 0;
|
||||
virtual void process_scb();
|
||||
@ -177,6 +181,7 @@ protected:
|
||||
|
||||
// command unit
|
||||
virtual void cu_execute();
|
||||
virtual void cu_complete(const u16 status);
|
||||
virtual bool cu_iasetup() = 0;
|
||||
virtual bool cu_configure() = 0;
|
||||
virtual bool cu_mcsetup() = 0;
|
||||
@ -186,7 +191,8 @@ protected:
|
||||
|
||||
// receive unit
|
||||
virtual bool address_filter(u8 *mac);
|
||||
virtual void ru_execute(u8 *buf, int length) = 0;
|
||||
virtual u16 ru_execute(u8 *buf, int length) = 0;
|
||||
virtual void ru_complete(const u16 status) = 0;
|
||||
|
||||
// helpers
|
||||
void set_irq(bool irq);
|
||||
@ -213,8 +219,8 @@ protected:
|
||||
bool m_fr; // frame received
|
||||
bool m_cna; // command unit became inactive
|
||||
bool m_rnr; // receive unit became not ready
|
||||
bool m_irq_state;
|
||||
bool m_initialised;
|
||||
int m_irq_assert; // configurable interrupt polarity
|
||||
|
||||
// receive/command unit state
|
||||
cu_state m_cu_state; // command unit state
|
||||
@ -279,7 +285,8 @@ protected:
|
||||
|
||||
// receive unit
|
||||
virtual bool address_filter(u8 *mac) override;
|
||||
virtual void ru_execute(u8 *buf, int length) override;
|
||||
virtual u16 ru_execute(u8 *buf, int length) override;
|
||||
virtual void ru_complete(const u16 status) override;
|
||||
|
||||
// helpers
|
||||
virtual u32 address(u32 base, int offset, int address) override { return m_scb_base + m_space->read_word(base + offset); }
|
||||
@ -291,6 +298,7 @@ private:
|
||||
virtual void cfg_set(int offset, u8 data) override { m_cfg_bytes[offset] = data; }
|
||||
|
||||
u8 m_cfg_bytes[CFG_SIZE];
|
||||
u16 m_rbd_offset; // next available receive buffer descriptor
|
||||
};
|
||||
|
||||
class i82596_device : public i82586_base_device
|
||||
@ -336,7 +344,8 @@ protected:
|
||||
|
||||
// receive unit
|
||||
virtual bool address_filter(u8 *mac) override;
|
||||
virtual void ru_execute(u8 *buf, int length) override;
|
||||
virtual u16 ru_execute(u8 *buf, int length) override;
|
||||
virtual void ru_complete(const u16 status) override;
|
||||
|
||||
// helpers
|
||||
virtual u32 address(u32 base, int offset, int address) override { return (mode() == MODE_LINEAR) ? m_space->read_dword(base + address) : m_scb_base + m_space->read_word(base + offset); }
|
||||
@ -357,6 +366,7 @@ private:
|
||||
u8 m_cfg_bytes[CFG_SIZE];
|
||||
u8 m_sysbus;
|
||||
|
||||
u32 m_rbd_address; // next available receive buffer descriptor
|
||||
u64 m_mac_multi_ia; // multi-ia address hash table
|
||||
};
|
||||
|
||||
|
@ -16,14 +16,56 @@ device_network_interface::~device_network_interface()
|
||||
{
|
||||
}
|
||||
|
||||
void device_network_interface::interface_pre_start()
|
||||
{
|
||||
m_send_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::send_complete), this));
|
||||
m_recv_timer = device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(device_network_interface::recv_complete), this));
|
||||
}
|
||||
|
||||
int device_network_interface::send(u8 *buf, int len) const
|
||||
{
|
||||
if(!m_dev) return 0;
|
||||
return m_dev->send(buf, len);
|
||||
|
||||
// TODO: enable this check when other devices implement delayed transmit
|
||||
//assert_always(!m_send_timer->enabled(), "attempted to transmit while transmit already in progress");
|
||||
|
||||
// send the data
|
||||
int result = m_dev->send(buf, len);
|
||||
|
||||
// schedule transmit complete callback
|
||||
m_send_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TIMER_CALLBACK_MEMBER(device_network_interface::send_complete)
|
||||
{
|
||||
send_complete_cb(param);
|
||||
}
|
||||
|
||||
void device_network_interface::recv_cb(u8 *buf, int len)
|
||||
{
|
||||
assert_always(!m_recv_timer->enabled(), "attempted to receive while receive already in progress");
|
||||
|
||||
// process the received data
|
||||
int result = recv_start_cb(buf, len);
|
||||
|
||||
if (result)
|
||||
{
|
||||
// stop receiving more data from the network
|
||||
m_dev->stop();
|
||||
|
||||
// schedule receive complete callback
|
||||
m_recv_timer->adjust(attotime::from_ticks(len, m_bandwidth * 1'000'000 / 8), result);
|
||||
}
|
||||
}
|
||||
|
||||
TIMER_CALLBACK_MEMBER(device_network_interface::recv_complete)
|
||||
{
|
||||
recv_complete_cb(param);
|
||||
|
||||
// start receiving data from the network again
|
||||
m_dev->start();
|
||||
}
|
||||
|
||||
void device_network_interface::set_promisc(bool promisc)
|
||||
|
@ -11,6 +11,8 @@ public:
|
||||
device_network_interface(const machine_config &mconfig, device_t &device, float bandwidth);
|
||||
virtual ~device_network_interface();
|
||||
|
||||
void interface_pre_start() override;
|
||||
|
||||
void set_interface(int id);
|
||||
void set_promisc(bool promisc);
|
||||
void set_mac(const char *mac);
|
||||
@ -20,14 +22,27 @@ public:
|
||||
int get_interface() const { return m_intf; }
|
||||
|
||||
int send(u8 *buf, int len) const;
|
||||
|
||||
// TODO: de-virtualise this when existing devices implement delayed receive
|
||||
virtual void recv_cb(u8 *buf, int len);
|
||||
|
||||
// delayed transmit/receive handlers
|
||||
virtual void send_complete_cb(int result) {}
|
||||
virtual int recv_start_cb(u8 *buf, int len) { return 0; }
|
||||
virtual void recv_complete_cb(int result) {}
|
||||
|
||||
protected:
|
||||
TIMER_CALLBACK_MEMBER(send_complete);
|
||||
TIMER_CALLBACK_MEMBER(recv_complete);
|
||||
|
||||
bool m_promisc;
|
||||
char m_mac[6];
|
||||
float m_bandwidth;
|
||||
std::unique_ptr<osd_netdev> m_dev;
|
||||
int m_intf;
|
||||
|
||||
emu_timer *m_send_timer;
|
||||
emu_timer *m_recv_timer;
|
||||
};
|
||||
|
||||
|
||||
|
@ -28,6 +28,9 @@
|
||||
// for some reason this isn't defined in the header, and presumably it changes
|
||||
// with major? versions of the driver - perhaps it should be configurable?
|
||||
#define PRODUCT_TAP_WIN_COMPONENT_ID "tap0901"
|
||||
|
||||
// Ethernet minimum frame length
|
||||
static constexpr int ETHERNET_MIN_FRAME = 64;
|
||||
#endif
|
||||
|
||||
class taptun_module : public osd_module, public netdev_module
|
||||
@ -53,10 +56,10 @@ public:
|
||||
netdev_tap(const char *name, class device_network_interface *ifdev, int rate);
|
||||
~netdev_tap();
|
||||
|
||||
int send(uint8_t *buf, int len);
|
||||
int send(uint8_t *buf, int len) override;
|
||||
void set_mac(const char *mac);
|
||||
protected:
|
||||
int recv_dev(uint8_t **buf);
|
||||
int recv_dev(uint8_t **buf) override;
|
||||
private:
|
||||
#if defined(WIN32)
|
||||
HANDLE m_handle = INVALID_HANDLE_VALUE;
|
||||
@ -136,6 +139,35 @@ void netdev_tap::set_mac(const char *mac)
|
||||
}
|
||||
|
||||
#if defined(WIN32)
|
||||
static u32 finalise_frame(u8 buf[], u32 length)
|
||||
{
|
||||
/*
|
||||
* On Windows, the taptun driver receives frames which are shorter
|
||||
* than the Ethernet minimum. Partly this is because it can't see
|
||||
* the frame check sequence bytes, but mainly it's because NDIS
|
||||
* expects the lower level device to add the required padding.
|
||||
* We do the equivalent padding here (i.e. pad with zeroes to the
|
||||
* minimum Ethernet length minus FCS), so that devices which check
|
||||
* for this will not reject these packets.
|
||||
*/
|
||||
if (length < ETHERNET_MIN_FRAME - 4)
|
||||
{
|
||||
std::fill_n(&buf[length], ETHERNET_MIN_FRAME - length - 4, 0);
|
||||
|
||||
length = ETHERNET_MIN_FRAME - 4;
|
||||
}
|
||||
|
||||
// compute and append the frame check sequence
|
||||
const u32 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;
|
||||
|
||||
return length;
|
||||
}
|
||||
|
||||
int netdev_tap::send(uint8_t *buf, int len)
|
||||
{
|
||||
OVERLAPPED overlapped = {};
|
||||
@ -171,7 +203,7 @@ int netdev_tap::recv_dev(uint8_t **buf)
|
||||
// handle unexpected synchronous completion
|
||||
*buf = m_buf;
|
||||
|
||||
return bytes_transferred;
|
||||
return finalise_frame(m_buf, bytes_transferred);
|
||||
}
|
||||
else if (GetLastError() == ERROR_IO_PENDING)
|
||||
m_receive_pending = true;
|
||||
@ -184,7 +216,7 @@ int netdev_tap::recv_dev(uint8_t **buf)
|
||||
m_receive_pending = false;
|
||||
*buf = m_buf;
|
||||
|
||||
return bytes_transferred;
|
||||
return finalise_frame(m_buf, bytes_transferred);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -38,7 +38,6 @@ class osd_netdev *open_netdev(int id, class device_network_interface *ifdev, int
|
||||
osd_netdev::osd_netdev(class device_network_interface *ifdev, int rate)
|
||||
{
|
||||
m_dev = ifdev;
|
||||
m_stop = false;
|
||||
m_timer = ifdev->device().machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(osd_netdev::recv), this));
|
||||
m_timer->adjust(attotime::from_hz(rate), 0, attotime::from_hz(rate));
|
||||
}
|
||||
@ -47,10 +46,14 @@ osd_netdev::~osd_netdev()
|
||||
{
|
||||
}
|
||||
|
||||
void osd_netdev::start()
|
||||
{
|
||||
m_timer->enable(true);
|
||||
}
|
||||
|
||||
void osd_netdev::stop()
|
||||
{
|
||||
m_stop = true;
|
||||
m_timer->reset();
|
||||
m_timer->enable(false);
|
||||
}
|
||||
|
||||
int osd_netdev::send(uint8_t *buf, int len)
|
||||
@ -63,7 +66,7 @@ void osd_netdev::recv(void *ptr, int param)
|
||||
uint8_t *buf;
|
||||
int len;
|
||||
//const char atalkmac[] = { 0x09, 0x00, 0x07, 0xff, 0xff, 0xff };
|
||||
while((!m_stop) && (len = recv_dev(&buf)))
|
||||
while(m_timer->enabled() && (len = recv_dev(&buf)))
|
||||
{
|
||||
#if 0
|
||||
if(buf[0] & 1)
|
||||
|
@ -22,6 +22,7 @@ public:
|
||||
};
|
||||
osd_netdev(class device_network_interface *ifdev, int rate);
|
||||
virtual ~osd_netdev();
|
||||
void start();
|
||||
void stop();
|
||||
|
||||
virtual int send(uint8_t *buf, int len);
|
||||
@ -39,7 +40,6 @@ private:
|
||||
|
||||
class device_network_interface *m_dev;
|
||||
emu_timer *m_timer;
|
||||
bool m_stop;
|
||||
};
|
||||
|
||||
class osd_netdev *open_netdev(int id, class device_network_interface *ifdev, int rate);
|
||||
|
Loading…
Reference in New Issue
Block a user