diff options
-rw-r--r-- | scripts/src/bus.lua | 2 | ||||
-rw-r--r-- | scripts/src/machine.lua | 13 | ||||
-rw-r--r-- | scripts/target/mame/mess.lua | 1 | ||||
-rw-r--r-- | src/devices/bus/a2bus/uthernet.cpp | 74 | ||||
-rw-r--r-- | src/devices/bus/a2bus/uthernet.h | 47 | ||||
-rw-r--r-- | src/devices/machine/cs8900a.cpp | 1513 | ||||
-rw-r--r-- | src/devices/machine/cs8900a.h | 106 | ||||
-rw-r--r-- | src/mame/drivers/apple2gs.cpp | 2 |
8 files changed, 1758 insertions, 0 deletions
diff --git a/scripts/src/bus.lua b/scripts/src/bus.lua index 4dc6609e9ed..88b73e56729 100644 --- a/scripts/src/bus.lua +++ b/scripts/src/bus.lua @@ -2374,6 +2374,8 @@ if (BUSES["A2BUS"]~=null) then MAME_DIR .. "src/devices/bus/a2bus/byte8251.h", MAME_DIR .. "src/devices/bus/a2bus/cmsscsi.cpp", MAME_DIR .. "src/devices/bus/a2bus/cmsscsi.h", + MAME_DIR .. "src/devices/bus/a2bus/uthernet.cpp", + MAME_DIR .. "src/devices/bus/a2bus/uthernet.h", } end diff --git a/scripts/src/machine.lua b/scripts/src/machine.lua index 87958503bef..a4751370c7d 100644 --- a/scripts/src/machine.lua +++ b/scripts/src/machine.lua @@ -1021,6 +1021,19 @@ end --------------------------------------------------- -- +--@src/devices/machine/cs8900a.h,MACHINES["CS8900A"] = true +--------------------------------------------------- + +if (MACHINES["CS8900A"]~=null) then + files { + MAME_DIR .. "src/devices/machine/cs8900a.cpp", + MAME_DIR .. "src/devices/machine/cs8900a.h", + } +end + + +--------------------------------------------------- +-- --@src/devices/machine/cxd1095.h,MACHINES["CXD1095"] = true --------------------------------------------------- diff --git a/scripts/target/mame/mess.lua b/scripts/target/mame/mess.lua index 14087cee686..4d8717bbe37 100644 --- a/scripts/target/mame/mess.lua +++ b/scripts/target/mame/mess.lua @@ -480,6 +480,7 @@ MACHINES["COP452"] = true MACHINES["CR589"] = true MACHINES["CS4031"] = true MACHINES["CS8221"] = true +MACHINES["CS8900A"] = true MACHINES["CXD1095"] = true MACHINES["DP8390"] = true MACHINES["DP83932C"] = true diff --git a/src/devices/bus/a2bus/uthernet.cpp b/src/devices/bus/a2bus/uthernet.cpp new file mode 100644 index 00000000000..734edadcf1a --- /dev/null +++ b/src/devices/bus/a2bus/uthernet.cpp @@ -0,0 +1,74 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/********************************************************************* + + uthernet.cpp + + Apple II Uthernet Card + +*********************************************************************/ + +#include "emu.h" +#include "uthernet.h" + +/*************************************************************************** + PARAMETERS +***************************************************************************/ + +//************************************************************************** +// GLOBAL VARIABLES +//************************************************************************** + +DEFINE_DEVICE_TYPE(A2BUS_UTHERNET, a2bus_uthernet_device, "a2uthernet", "Uthernet") + +//************************************************************************** +// LIVE DEVICE +//************************************************************************** + +a2bus_uthernet_device::a2bus_uthernet_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : + a2bus_uthernet_device(mconfig, A2BUS_UTHERNET, tag, owner, clock) +{ +} + +a2bus_uthernet_device::a2bus_uthernet_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) : + device_t(mconfig, type, tag, owner, clock), + device_a2bus_card_interface(mconfig, *this), + m_netinf(*this, "cs8900a") +{ +} + +void a2bus_uthernet_device::device_add_mconfig(machine_config &config) +{ + CS8900A(config, m_netinf, 20_MHz_XTAL); // see CS8900A data sheet sec 3.13 +} + +//------------------------------------------------- +// device_start - device-specific startup +//------------------------------------------------- + +void a2bus_uthernet_device::device_start() +{ +} + +void a2bus_uthernet_device::device_reset() +{ +} + +/*------------------------------------------------- + read_c0nx - called for reads from this card's c0nx space +-------------------------------------------------*/ + +uint8_t a2bus_uthernet_device::read_c0nx(uint8_t offset) +{ + return m_netinf->read(offset); +} + +/*------------------------------------------------- + write_c0nx - called for writes to this card's c0nx space +-------------------------------------------------*/ + +void a2bus_uthernet_device::write_c0nx(uint8_t offset, uint8_t data) +{ + m_netinf->write(offset,data); +} + diff --git a/src/devices/bus/a2bus/uthernet.h b/src/devices/bus/a2bus/uthernet.h new file mode 100644 index 00000000000..8e64e53ab38 --- /dev/null +++ b/src/devices/bus/a2bus/uthernet.h @@ -0,0 +1,47 @@ +// license:BSD-3-Clause +// copyright-holders:R. Belmont +/********************************************************************* + + uthernet.h + + Apple II Uthernet Card + +*********************************************************************/ + +#ifndef MAME_BUS_A2BUS_UTHERNET_H +#define MAME_BUS_A2BUS_UTHERNET_H + +#include "a2bus.h" +#include "machine/cs8900a.h" + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +class a2bus_uthernet_device: + public device_t, + public device_a2bus_card_interface +{ +public: + // construction/destruction + a2bus_uthernet_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock); + +protected: + a2bus_uthernet_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + virtual void device_add_mconfig(machine_config &config) override; + + virtual void device_start() override; + virtual void device_reset() override; + + virtual uint8_t read_c0nx(uint8_t offset) override; + virtual void write_c0nx(uint8_t offset, uint8_t data) override; + +private: + required_device<cs8900a_device> m_netinf; + +}; + +// device type definition +DECLARE_DEVICE_TYPE(A2BUS_UTHERNET, a2bus_uthernet_device) + +#endif // MAME_BUS_A2BUS_UTHERNET_H diff --git a/src/devices/machine/cs8900a.cpp b/src/devices/machine/cs8900a.cpp new file mode 100644 index 00000000000..8a33504a2d8 --- /dev/null +++ b/src/devices/machine/cs8900a.cpp @@ -0,0 +1,1513 @@ +// license:GPL-2.0+ + +// copyright-holders:Spiro Trikaliotis, Rhett Aultman + +/************************************************************************* + + CS8900A ethernet controller implementation + + by Rhett Aultman <roadriverrail@gmail.com> + ported to MAME from VICE Project (https://sourceforge.net/p/vice-emu/) + VICE CS8900 code by Spiro Trikaliotis <Spiro.Trikaliotis@gmx.de> + + **************************************************************************/ + +#include "emu.h" +#include "machine/cs8900a.h" + +#include <cstring> + +DEFINE_DEVICE_TYPE(CS8900A, cs8900a_device, "cs8900a", "CS8900A Crystal LAN 10Base-T Ethernet MAC") + +/* warn illegal behaviour */ +#define CS8900_DEBUG_WARN_REG (1 << 1U) /* warn about invalid register accesses */ +#define CS8900_DEBUG_WARN_RXTX (1 << 2U) /* warn about invalid rx or tx conditions */ + +#define CS8900_DEBUG (1 << 3U) /* enable to see port reads */ +#define CS8900_DEBUG_INIT (1 << 4U) +#define CS8900_DEBUG_LOAD (1 << 5U) /* enable to see port reads */ +#define CS8900_DEBUG_STORE (1 << 6U) /* enable to see port writes */ +#define CS8900_DEBUG_REGISTERS (1 << 7U) /* enable to see CS8900a register I/O */ +#define CS8900_DEBUG_RXTX_STATE (1 << 8U) /* enable to see tranceiver state changes */ +#define CS8900_DEBUG_RXTX_DATA (1 << 9U) /* enable to see data in/out flow */ +#define CS8900_DEBUG_FRAMES (1 << 10U) /* enable to see arch frame send/recv */ + +/** #define CS8900_DEBUG_IGNORE_RXEVENT 1 **/ /* enable to ignore RXEVENT in DEBUG_REGISTERS */ +#define VERBOSE 0 + +#include "logmacro.h" + +#define MAX_FRAME_QUEUE_ENTRIES 4096 + +/* CS8900 registers */ + +/* these are the 8 16-bit-ports for "I/O space configuration" + (see 4.10 on page 75 of cs8900a-4.pdf, the cs8900a data sheet) + + REMARK: The code operatoes the cs8900a in IO space configuration, as + it generates I/OW and I/OR signals. + */ + +/* + RW: RXTXDATA = DE00/DE01 + RW: RXTXDATA2 = DE02/DE03 (for 32-bit-operation) + -W: TXCMD = DE04/DE05 (TxCMD, Transmit Command) mapped to PP + 0144 (Reg. 9, Sec. 4.4, page 46) + -W: TXLENGTH = DE06/DE07 (TxLenght, Transmit Length) mapped to PP + 0146 + R-: INTSTQUEUE = DE08/DE09 (Interrupt Status Queue) mapped to PP + 0120 (ISQ, Sec. 5.1, page 78) + RW: PP_PTR = DE0A/DE0B (PacketPage Pointer) (see. page 75p: Read -011.---- ----.----) + RW: PP_DATA0 = DE0C/DE0D (PacketPage Data (Port 0)) + RW: PP_DATA1 = DE0E/DE0F (PacketPage Data (Port 1)) (for 32 bit only) + */ + +enum io_space_conf_regs_e : u8 { + CS8900_ADDR_RXTXDATA = 0x00, /* RW */ + CS8900_ADDR_RXTXDATA2 = 0x02, /* RW 32 bit only! */ + CS8900_ADDR_TXCMD = 0x04, /* -W Maps to PP+0144 */ + CS8900_ADDR_TXLENGTH = 0x06, /* -W Maps to PP+0146 */ + CS8900_ADDR_INTSTQUEUE = 0x08, /* R- Interrupt status queue, maps to PP + 0120 */ + CS8900_ADDR_PP_PTR = 0x0a, /* RW PacketPage Pointer */ + CS8900_ADDR_PP_DATA = 0x0c, /* RW PacketPage Data, Port 0 */ + CS8900_ADDR_PP_DATA2 = 0x0e /* RW PacketPage Data, Port 1 - 32 bit only */ +}; + +/* Makros for reading and writing the visible register: */ +#define GET_CS8900_8(_xxx_) \ + (assert(_xxx_ < CS8900_COUNT_IO_REGISTER), \ + cs8900_ioregs[_xxx_] \ + ) + +#define SET_CS8900_8(_xxx_, _val_) \ + do { \ + assert(_xxx_ < CS8900_COUNT_IO_REGISTER); \ + cs8900_ioregs[_xxx_] = (_val_) & 0xff; \ + } while (0) + +#define GET_CS8900_16(_xxx_) \ + (assert(_xxx_ < CS8900_COUNT_IO_REGISTER), \ + cs8900_ioregs[_xxx_] | (cs8900_ioregs[_xxx_ + 1] << 8) \ + ) + +#define SET_CS8900_16(_xxx_, _val_) \ + do { \ + assert(_xxx_ < CS8900_COUNT_IO_REGISTER); \ + cs8900_ioregs[_xxx_] = (_val_) & 0xff; \ + cs8900_ioregs[_xxx_ + 1] = (_val_ >> 8) & 0xff; \ + } while (0) + +/* The PacketPage register */ +/* note: The locations 0 to MAX_PACKETPAGE_ARRAY-1 are handled in this array. */ + +/* Macros for reading and writing the PacketPage register: */ + +#define GET_PP_8(_xxx_) \ + (assert(_xxx_ < MAX_PACKETPAGE_ARRAY), \ + cs8900_packetpage[_xxx_] \ + ) + +#define GET_PP_16(_xxx_) \ + (assert(_xxx_ < MAX_PACKETPAGE_ARRAY), \ + assert((_xxx_ & 1) == 0), \ + ((u16)cs8900_packetpage[_xxx_]) \ + |((u16)cs8900_packetpage[_xxx_ + 1] << 8) \ + ) + +#define GET_PP_32(_xxx_) \ + (assert(_xxx_ < MAX_PACKETPAGE_ARRAY), \ + assert((_xxx_ & 3) == 0), \ + (((u32)cs8900_packetpage[_xxx_])) \ + |(((u32)cs8900_packetpage[_xxx_ + 1]) << 8) \ + |(((u32)cs8900_packetpage[_xxx_ + 2]) << 16) \ + |(((u32)cs8900_packetpage[_xxx_ + 3]) << 24) \ + ) + +#define SET_PP_8(_xxx_, _val_) \ + do { \ + assert(_xxx_ < MAX_PACKETPAGE_ARRAY); \ + cs8900_packetpage[_xxx_] = (_val_) & 0xFF; \ + } while (0) + +#define SET_PP_16(_xxx_, _val_) \ + do { \ + assert(_xxx_ < MAX_PACKETPAGE_ARRAY); \ + assert((_xxx_ & 1) == 0), \ + cs8900_packetpage[_xxx_] = (_val_) & 0xFF; \ + cs8900_packetpage[_xxx_ + 1] = (_val_ >> 8) & 0xFF; \ + } while (0) + +#define SET_PP_32(_xxx_, _val_) \ + do { \ + assert(_xxx_ < MAX_PACKETPAGE_ARRAY); \ + assert((_xxx_ & 3) == 0), \ + cs8900_packetpage[_xxx_] = (_val_) & 0xFF; \ + cs8900_packetpage[_xxx_ + 1] = (_val_ >> 8) & 0xFF; \ + cs8900_packetpage[_xxx_ + 2] = (_val_ >> 16) & 0xFF; \ + cs8900_packetpage[_xxx_ + 3] = (_val_ >> 24) & 0xFF; \ + } while (0) + +enum packetpage_regs_e : u16 { + /* The packetpage register: see p. 39f */ + CS8900_PP_ADDR_PRODUCTID = 0x0000, /* R- - 4.3., p. 41 */ + CS8900_PP_ADDR_IOBASE = 0x0020, /* i RW - 4.3., p. 41 - 4.7., p. 72 */ + CS8900_PP_ADDR_INTNO = 0x0022, /* i RW - 3.2., p. 17 - 4.3., p. 41 */ + CS8900_PP_ADDR_DMA_CHAN = 0x0024, /* i RW - 3.2., p. 17 - 4.3., p. 41 */ + CS8900_PP_ADDR_DMA_SOF = 0x0026, /* ? R- - 4.3., p. 41 - 5.4., p. 89 */ + CS8900_PP_ADDR_DMA_FC = 0x0028, /* ? R- - 4.3., p. 41, "Receive DMA" */ + CS8900_PP_ADDR_RXDMA_BC = 0x002a, /* ? R- - 4.3., p. 41 - 5.4., p. 89 */ + CS8900_PP_ADDR_MEMBASE = 0x002c, /* i RW - 4.3., p. 41 - 4.9., p. 73 */ + CS8900_PP_ADDR_BPROM_BASE = 0x0030, /* i RW - 3.6., p. 24 - 4.3., p. 41 */ + CS8900_PP_ADDR_BPROM_MASK = 0x0034, /* i RW - 3.6., p. 24 - 4.3., p. 41 */ + /* 0x0038 - 0x003F: reserved */ + CS8900_PP_ADDR_EEPROM_CMD = 0x0040, /* i RW - 3.5., p. 23 - 4.3., p. 41 */ + CS8900_PP_ADDR_EEPROM_DATA = 0x0042, /* i RW - 3.5., p. 23 - 4.3., p. 41 */ + /* 0x0044 - 0x004F: reserved */ + CS8900_PP_ADDR_REC_FRAME_BC = 0x0050, /* RW - 4.3., p. 41 - 5.2.9., p. 86 */ + /* 0x0052 - 0x00FF: reserved */ + CS8900_PP_ADDR_CONF_CTRL = 0x0100, /* - RW - 4.4., p. 46; see below */ + CS8900_PP_ADDR_STATUS_EVENT = 0x0120, /* - R- - 4.4., p. 46; see below */ + /* 0x0140 - 0x0143: reserved */ + CS8900_PP_ADDR_TXCMD = 0x0144, /* # -W - 4.5., p. 70 - 5.7., p. 98 */ + CS8900_PP_ADDR_TXLENGTH = 0x0146, /* # -W - 4.5., p. 70 - 5.7., p. 98 */ + /* 0x0148 - 0x014F: reserved */ + CS8900_PP_ADDR_LOG_ADDR_FILTER = 0x0150, /* RW - 4.6., p. 71 - 5.3., p. 86 */ + CS8900_PP_ADDR_MAC_ADDR = 0x0158, /* # RW - 4.6., p. 71 - 5.3., p. 86 */ + /* 0x015E - 0x03FF: reserved */ + CS8900_PP_ADDR_RXSTATUS = 0x0400, /* R- - 4.7., p. 72 - 5.2., p. 78 */ + CS8900_PP_ADDR_RXLENGTH = 0x0402, /* R- - 4.7., p. 72 - 5.2., p. 78 */ + CS8900_PP_ADDR_RX_FRAMELOC = 0x0404, /* R- - 4.7., p. 72 - 5.2., p. 78 */ + /* here, the received frame is stored */ + CS8900_PP_ADDR_TX_FRAMELOC = 0x0A00, /* -W - 4.7., p. 72 - 5.7., p. 98 */ + /* here, the frame to transmit is stored */ + CS8900_PP_ADDR_END = 0x1000, /* memory to CS8900_PP_ADDR_END-1 is used */ + /* CS8900_PP_ADDR_CONF_CTRL is subdivided: */ + CS8900_PP_ADDR_CC_RXCFG = 0x0102, /* # RW - 4.4.6., p. 52 - 0003 */ + CS8900_PP_ADDR_CC_RXCTL = 0x0104, /* # RW - 4.4.8., p. 54 - 0005 */ + CS8900_PP_ADDR_CC_TXCFG = 0x0106, /* RW - 4.4.9., p. 55 - 0007 */ + CS8900_PP_ADDR_CC_TXCMD = 0x0108, /* R- - 4.4.11., p. 57 - 0009 */ + CS8900_PP_ADDR_CC_BUFCFG = 0x010A, /* RW - 4.4.12., p. 58 - 000B */ + CS8900_PP_ADDR_CC_LINECTL = 0x0112, /* # RW - 4.4.16., p. 62 - 0013 */ + CS8900_PP_ADDR_CC_SELFCTL = 0x0114, /* RW - 4.4.18., p. 64 - 0015 */ + CS8900_PP_ADDR_CC_BUSCTL = 0x0116, /* RW - 4.4.20., p. 66 - 0017 */ + CS8900_PP_ADDR_CC_TESTCTL = 0x0118, /* RW - 4.4.22., p. 68 - 0019 */ + /* CS8900_PP_ADDR_STATUS_EVENT is subdivided: */ + CS8900_PP_ADDR_SE_ISQ = 0x0120, /* R- - 4.4.5., p. 51 - 0000 */ + CS8900_PP_ADDR_SE_RXEVENT = 0x0124, /* # R- - 4.4.7., p. 53 - 0004 */ + CS8900_PP_ADDR_SE_TXEVENT = 0x0128, /* R- - 4.4.10., p. 56 - 0008 */ + CS8900_PP_ADDR_SE_BUFEVENT = 0x012C, /* R- - 4.4.13., p. 59 - 000C */ + CS8900_PP_ADDR_SE_RXMISS = 0x0130, /* R- - 4.4.14., p. 60 - 0010 */ + CS8900_PP_ADDR_SE_TXCOL = 0x0132, /* R- - 4.4.15., p. 61 - 0012 */ + CS8900_PP_ADDR_SE_LINEST = 0x0134, /* R- - 4.4.17., p. 63 - 0014 */ + CS8900_PP_ADDR_SE_SELFST = 0x0136, /* R- - 4.4.19., p. 65 - 0016 */ + CS8900_PP_ADDR_SE_BUSST = 0x0138, /* # R- - 4.4.21., p. 67 - 0018 */ + CS8900_PP_ADDR_SE_TDR = 0x013C /* R- - 4.4.23., p. 69 - 001C */ +}; + +enum tx_rx_min_max_e : u16 { + MAX_TXLENGTH = 1518, + MIN_TXLENGTH = 4, + MAX_RXLENGTH = 1518, + MIN_RXLENGTH = 64 +}; + +enum cs8900_tx_state_e : u8 { + CS8900_TX_IDLE = 0, + CS8900_TX_GOT_CMD = 1, + CS8900_TX_GOT_LEN = 2, + CS8900_TX_READ_BUSST = 3 +}; + +enum cs8900_rx_state_e : u8 { + CS8900_RX_IDLE = 0, + CS8900_RX_GOT_FRAME = 1 +}; + +enum pp_ptr_masks_e : u16 { + PP_PTR_AUTO_INCR_FLAG = 0x8000, /* auto increment flag in package pointer */ + PP_PTR_FLAG_MASK = 0xf000, /* is always : x y 1 1 (with x=auto incr) */ + PP_PTR_ADDR_MASK = 0x0fff /* address portion of packet page pointer */ +}; + +#define LO_BYTE(x) (u8)((x)& 0xff) +#define HI_BYTE(x) (u8)(((x) >> 8)& 0xff) +#define LOHI_WORD(x, y) ((u16)(x) | (((u16)(y)) << 8)) + +// ------------------- END #defines --------------------- + +// ---- debug logging support ---- +std::string cs8900a_device::debug_outbuffer(const int length, const unsigned char *const buffer) +{ + static constexpr u32 MAXLEN_DEBUG = 1600; + static constexpr u32 TFE_DEBUG_MAX_FRAME_DUMP = 150; + std::ostringstream outbuffer; + + static_assert(TFE_DEBUG_MAX_FRAME_DUMP <= MAXLEN_DEBUG, "Max frame dump must not be larger than max debug length"); + + for (int i = 0; (i < TFE_DEBUG_MAX_FRAME_DUMP) && (i < length); i++) + util::stream_format(outbuffer, "%02X%c", buffer[i], ((i + 1) % 16 == 0) ? '*' : (((i + 1) % 8 == 0) ? '-' : ' ')); + + return outbuffer.str(); +} + +void cs8900a_device::cs8900_set_tx_status(int ready, int error) +{ + u16 old_status = GET_PP_16(CS8900_PP_ADDR_SE_BUSST); + + /* mask out TxBidErr and Rdy4TxNOW */ + u16 new_status = old_status & ~0x180; + + if (ready) + new_status |= 0x100; /* set Rdy4TxNOW */ + + if (error) + new_status |= 0x080; /* set TxBidErr */ + + if (new_status != old_status) + { + SET_PP_16(CS8900_PP_ADDR_SE_BUSST, new_status); + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: set status Rdy4TxNOW=%d TxBidErr=%d", + ready, error); + } +} + +void cs8900a_device::cs8900_set_receiver(int enabled) +{ + rx_enabled = enabled; + rx_state = CS8900_RX_IDLE; + + rxevent_read_mask = 3; /* was L or H byte read in RXEVENT? */ +} + +void cs8900a_device::cs8900_set_transmitter(int enabled) +{ + tx_enabled = enabled; + tx_state = CS8900_TX_IDLE; + + cs8900_set_tx_status(0, 0); +} + +void cs8900a_device::device_reset() +{ + // Flush the inbound packet queue + std::queue<std::vector<u8> > empty_queue; + std::swap(m_frame_queue, empty_queue); + + /* initialize visible IO register and PacketPage registers */ + memset(cs8900_ioregs, 0, CS8900_COUNT_IO_REGISTER); + memset(cs8900_packetpage, 0, MAX_PACKETPAGE_ARRAY); + + /* according to page 19 unless stated otherwise */ + SET_PP_32(CS8900_PP_ADDR_PRODUCTID, 0x0900630E); /* p.41: 0E630009 for Rev. D; reversed order! */ + SET_PP_16(CS8900_PP_ADDR_IOBASE, 0x0300); + SET_PP_16(CS8900_PP_ADDR_INTNO, 0x0004); /* xxxx xxxx xxxx x100b */ + SET_PP_16(CS8900_PP_ADDR_DMA_CHAN, 0x0003); /* xxxx xxxx xxxx xx11b */ + + /* according to descriptions of the registers, see definitions of + CS8900_PP_ADDR_CC_... and CS8900_PP_ADDR_SE_... above! */ + + SET_PP_16(CS8900_PP_ADDR_CC_RXCFG, 0x0003); + SET_PP_16(CS8900_PP_ADDR_CC_RXCTL, 0x0005); + SET_PP_16(CS8900_PP_ADDR_CC_TXCFG, 0x0007); + SET_PP_16(CS8900_PP_ADDR_CC_TXCMD, 0x0009); + SET_PP_16(CS8900_PP_ADDR_CC_BUFCFG, 0x000B); + SET_PP_16(CS8900_PP_ADDR_CC_LINECTL, 0x0013); + SET_PP_16(CS8900_PP_ADDR_CC_SELFCTL, 0x0015); + SET_PP_16(CS8900_PP_ADDR_CC_BUSCTL, 0x0017); + SET_PP_16(CS8900_PP_ADDR_CC_TESTCTL, 0x0019); + + SET_PP_16(CS8900_PP_ADDR_SE_ISQ, 0x0000); + SET_PP_16(CS8900_PP_ADDR_SE_RXEVENT, 0x0004); + SET_PP_16(CS8900_PP_ADDR_SE_TXEVENT, 0x0008); + SET_PP_16(CS8900_PP_ADDR_SE_BUFEVENT, 0x000C); + SET_PP_16(CS8900_PP_ADDR_SE_RXMISS, 0x0010); + SET_PP_16(CS8900_PP_ADDR_SE_TXCOL, 0x0012); + SET_PP_16(CS8900_PP_ADDR_SE_LINEST, 0x0014); + SET_PP_16(CS8900_PP_ADDR_SE_SELFST, 0x0016); + SET_PP_16(CS8900_PP_ADDR_SE_BUSST, 0x0018); + SET_PP_16(CS8900_PP_ADDR_SE_TDR, 0x001C); + + SET_PP_16(CS8900_PP_ADDR_TXCMD, 0x0009); + + /* 4.4.19 Self Status Register, p. 65 + Important: set INITD (Bit 7) to signal device is ready */ + SET_PP_16(CS8900_PP_ADDR_SE_SELFST, 0x0896); + + cs8900_recv_control = GET_PP_16(CS8900_PP_ADDR_CC_RXCTL); + + /* spec: mac address is undefined after reset. + real HW: keeps the last set address. */ + for (int i = 0; i < 6; i++) + SET_PP_8(CS8900_PP_ADDR_MAC_ADDR + i, cs8900_ia_mac[i]); + + /* reset state */ + cs8900_set_transmitter(0); + cs8900_set_receiver(0); +} + +void cs8900a_device::device_start() +{ + LOGMASKED(CS8900_DEBUG, "device_start().\n"); + LOGMASKED(CS8900_DEBUG_INIT, "\tcs8900_ioregs at $%08X, cs8900_packetpage at $%08X", cs8900_ioregs, cs8900_packetpage); + + /* virtually reset the LAN chip */ + device_reset(); + + save_item(NAME(cs8900_ia_mac)); + save_item(NAME(cs8900_hash_mask)); + save_item(NAME(cs8900_ioregs)); + save_item(NAME(cs8900_packetpage)); + save_item(NAME(cs8900_packetpage_ptr)); + save_item(NAME(cs8900_recv_control)); + save_item(NAME(cs8900_recv_broadcast)); + save_item(NAME(cs8900_recv_mac)); + save_item(NAME(cs8900_recv_multicast)); + save_item(NAME(cs8900_recv_correct)); + save_item(NAME(cs8900_recv_promiscuous)); + save_item(NAME(cs8900_recv_hashfilter)); + save_item(NAME(tx_buffer)); + save_item(NAME(rx_buffer)); + save_item(NAME(tx_count)); + save_item(NAME(rx_count)); + save_item(NAME(tx_length)); + save_item(NAME(rx_length)); + save_item(NAME(tx_state)); + save_item(NAME(rx_state)); + save_item(NAME(tx_enabled)); + save_item(NAME(rx_enabled)); + save_item(NAME(rxevent_read_mask)); +} + +cs8900a_device::cs8900a_device(const machine_config& mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock) + : device_t(mconfig, type, tag, owner, clock) + , device_network_interface(mconfig, *this, 10.0f) + , cs8900_ia_mac{0, 0, 0, 0, 0, 0} + , cs8900_packetpage_ptr(0) + , cs8900_recv_control(0) /* copy of CC_RXCTL (contains all bits below) */ + , cs8900_recv_broadcast(false) /* broadcast */ + , cs8900_recv_mac(false) /* individual address (IA) */ + , cs8900_recv_multicast(false) /* multicast if address passes the hash filter */ + , cs8900_recv_correct(false) /* accept correct frames */ + , cs8900_recv_promiscuous(false) /* promiscuous mode */ + , cs8900_recv_hashfilter(false) /* accept if IA passes the hash filter */ + , tx_buffer(CS8900_PP_ADDR_TX_FRAMELOC) + , rx_buffer(CS8900_PP_ADDR_RXSTATUS) + , tx_count(0) + , rx_count(0) + , tx_length(0) + , rx_length(0) + , tx_state(CS8900_TX_IDLE) + , rx_state(CS8900_RX_IDLE) + , tx_enabled(0) + , rx_enabled(0) + , rxevent_read_mask(3) /* set if L and/or H u8 was read in RXEVENT? */ +{} + +cs8900a_device::cs8900a_device(machine_config const& mconfig, char const *tag, device_t *owner, u32 clock) + : cs8900a_device(mconfig, CS8900A, tag, owner, clock) +{} + +/* + This is a helper for cs8900_receive() to determine if the received frame should be accepted + according to the settings. + */ +bool cs8900a_device::cs8900_should_accept(unsigned char *buffer, int length, bool *phashed, int *phash_index, + bool *pcorrect_mac, bool *pbroadcast, bool *pmulticast) +{ + int hashreg; /* Hash Register (for hash computation) */ + + assert(length >= 6); /* we need at least 6 octets since the DA has this length */ + + /* first of all, delete any status */ + *phashed = false; + *phash_index = 0; + *pcorrect_mac = false; + *pbroadcast = false; + *pmulticast = false; + + LOGMASKED(CS8900_DEBUG_FRAMES, "cs8900_should_accept called with %02X:%02X:%02X:%02X:%02X:%02X, length=%4u", + cs8900_ia_mac[0], cs8900_ia_mac[1], cs8900_ia_mac[2], + cs8900_ia_mac[3], cs8900_ia_mac[4], cs8900_ia_mac[5], + length, + debug_outbuffer(length, buffer) + ); + + if ((buffer[0] == cs8900_ia_mac[0]) + && (buffer[1] == cs8900_ia_mac[1]) + && (buffer[2] == cs8900_ia_mac[2]) + && (buffer[3] == cs8900_ia_mac[3]) + && (buffer[4] == cs8900_ia_mac[4]) + && (buffer[5] == cs8900_ia_mac[5]) + ) + { + /* this is our individual address (IA) */ + *pcorrect_mac = true; + + /* if we don't want "correct MAC", we might have the chance + * that this address fits the hash index + */ + if (cs8900_recv_mac || cs8900_recv_promiscuous) + return true; + } + + if ((buffer[0] == 0xFF) + && (buffer[1] == 0xFF) + && (buffer[2] == 0xFF) + && (buffer[3] == 0xFF) + && (buffer[4] == 0xFF) + && (buffer[5] == 0xFF) + ) + { + /* this is a broadcast address */ + *pbroadcast = true; + + /* broadcasts cannot be accepted by the hash filter */ + return (cs8900_recv_broadcast || cs8900_recv_promiscuous); + } + + /* now check if DA passes the hash filter */ + hashreg = (~util::crc32_creator::simple((char *)buffer, 6) >> 26) & 0x3F; + + *phashed = (cs8900_hash_mask[(hashreg >= 32) ? 1 : 0] & (1 << (hashreg & 0x1F))); + + if (*phashed) + { + *phash_index = hashreg; + + if (buffer[0] & 0x80) + { + /* we have a multicast address */ + *pmulticast = true; + + /* if the multicast address fits into the hash filter, + * the hashed bit has to be clear + */ + *phashed = false; + + return (cs8900_recv_multicast || cs8900_recv_promiscuous); + } + return (cs8900_recv_hashfilter || cs8900_recv_promiscuous); + } + + return cs8900_recv_promiscuous; +} + +u16 cs8900a_device::cs8900_receive() +{ + u16 ret_val = 0x0004; + + u8 buffer[MAX_RXLENGTH]; + + int len; + bool hashed = false; + int hash_index = 0; + bool rx_ok = false; + bool correct_mac = false; + bool broadcast = false; + bool multicast = false; + bool crc_error = false; + + bool newframe = false; + + bool ready = false; + + do { + len = MAX_RXLENGTH; + + ready = true; /* assume we will find a good frame */ + std::vector<u8> frame; + + if (!m_frame_queue.empty()) + { + std::vector<u8> frame = m_frame_queue.front(); + m_frame_queue.pop(); + len = frame.size(); + std::memcpy(buffer, frame.data(), len); + newframe = true; + rx_ok = true; + } + else + { + newframe = false; + } + + assert((len & 1) == 0); /* length has to be even! */ + + if (newframe) + { + if (hashed || correct_mac || broadcast) + { + /* we already know the type of frame: Trust it! */ + LOGMASKED(CS8900_DEBUG_FRAMES, "+++ cs8900_receive(): *** hashed=%u, correct_mac=%u, " + "broadcast=%u", hashed, correct_mac, broadcast); + } + else + { + /* determine ourself the type of frame */ + if (!cs8900_should_accept(buffer, + len, &hashed, &hash_index, &correct_mac, &broadcast, &multicast)) + { + /* if we should not accept this frame, just do nothing + * now, look for another one */ + ready = false; /* try another frame */ + continue; + } + } + + /* we did receive a frame, return that status */ + ret_val |= rx_ok ? 0x0100 : 0; + ret_val |= multicast ? 0x0200 : 0; + + if (!multicast) + { + ret_val |= hashed ? 0x0040 : 0; + } + + if (hashed && rx_ok) + { + /* we have the 2nd, special format with hash index: */ + assert(hash_index < 64); + ret_val |= hash_index << 9; + } + else + { + /* we have the regular format */ + ret_val |= correct_mac ? 0x0400 : 0; + ret_val |= broadcast ? 0x0800 : 0; + ret_val |= crc_error ? 0x1000 : 0; + ret_val |= (len < MIN_RXLENGTH) ? 0x2000 : 0; + ret_val |= (len > MAX_RXLENGTH) ? 0x4000 : 0; + } + + /* discard any octets that are beyond the MAX_RXLEN */ + if (len > MAX_RXLENGTH) + { + len = MAX_RXLENGTH; + } + + if (rx_ok) + { + int i; + + /* set relevant parts of the PP area to correct values */ + SET_PP_16(CS8900_PP_ADDR_RXLENGTH, len); + + for (i = 0; i < len; i++) + { + SET_PP_8(CS8900_PP_ADDR_RX_FRAMELOC + i, buffer[i]); + } + + /* set rx_buffer to where start reading * + * According to 4.10.9 (pp. 76-77), we start with RxStatus and RxLength! + */ + rx_buffer = CS8900_PP_ADDR_RXSTATUS; + rx_length = len; + rx_count = 0; + + if (rx_state != CS8900_RX_IDLE) + { + LOGMASKED(CS8900_DEBUG_WARN_RXTX, "WARNING! New frame overwrites pending one!"); + } + rx_state = CS8900_RX_GOT_FRAME; + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "RX: recvd frame (length=%04x,status=%04x)", + rx_length, ret_val); + } + } + } while (!ready); + + if (ret_val != 0x0004) + LOGMASKED(CS8900_DEBUG_FRAMES, "+++ cs8900_receive(): ret_val=%04X", ret_val); + + return ret_val; +} + +/* ------------------------------------------------------------------------- */ +/* reading and writing IO register functions */ + +/* + These registers are currently fully or partially supported: + + CS8900_PP_ADDR_CC_RXCFG 0x0102 * # RW - 4.4.6., p. 52 - 0003 * + CS8900_PP_ADDR_CC_RXCTL 0x0104 * # RW - 4.4.8., p. 54 - 0005 * + CS8900_PP_ADDR_CC_LINECTL 0x0112 * # RW - 4.4.16., p. 62 - 0013 * + CS8900_PP_ADDR_SE_RXEVENT 0x0124 * # R- - 4.4.7., p. 53 - 0004 * + CS8900_PP_ADDR_SE_BUSST 0x0138 * # R- - 4.4.21., p. 67 - 0018 * + CS8900_PP_ADDR_TXCMD 0x0144 * # -W - 4.5., p. 70 - 5.7., p. 98 * + CS8900_PP_ADDR_TXLENGTH 0x0146 * # -W - 4.5., p. 70 - 5.7., p. 98 * + CS8900_PP_ADDR_MAC_ADDR 0x0158 * # RW - 4.6., p. 71 - 5.3., p. 86 * + 0x015a + 0x015c + */ + +/* ------------------------------------------------------------------------- */ +/* TX/RX buffer handling */ + +void cs8900a_device::cs8900_write_tx_buffer(u8 value, int odd_address) +{ + /* write tx data only if valid buffer is ready */ + if (tx_state != CS8900_TX_READ_BUSST) + { + LOGMASKED(CS8900_DEBUG_WARN_RXTX, "WARNING! Ignoring TX Write without correct Transmit Condition! (odd=%d,value=%02x)", + odd_address, value); + + /* ensure correct tx state (needed if transmit < 4 was started) */ + cs8900_set_tx_status(0, 0); + } + else + { + if (tx_count == 0) + { + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: write frame (length=%04x)", tx_length); + } + + /* always write LH, LH... to tx buffer */ + u16 addr = tx_buffer; + + if (odd_address) + { + addr++; + tx_buffer += 2; + } + tx_count++; + SET_PP_8(addr, value); + + LOGMASKED(CS8900_DEBUG_RXTX_DATA, "TX: %04x/%04x: %02x (buffer=%04x,odd=%d)", + tx_count, tx_length, value, addr, odd_address); + + /* full frame transmitted? */ + if (tx_count == tx_length) + { + LOGMASKED(CS8900_DEBUG_FRAMES, "cs8900_arch_transmit() called with: " + "length=%4u and buffer %s", tx_length, + debug_outbuffer(tx_length, &cs8900_packetpage[CS8900_PP_ADDR_TX_FRAMELOC]) + ); + + if (!tx_enabled) + { + LOGMASKED(CS8900_DEBUG_WARN_RXTX, "WARNING! Can't transmit frame (Transmitter is not enabled)!"); + } + else + { + /* send frame */ + LOGMASKED(CS8900_DEBUG, "SENDING from buf %p len %d\n", &cs8900_packetpage[CS8900_PP_ADDR_TX_FRAMELOC], tx_length); + send(&cs8900_packetpage[CS8900_PP_ADDR_TX_FRAMELOC], tx_length); + } + + /* reset transmitter state */ + tx_state = CS8900_TX_IDLE; + + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: sent frame (length=%04x)", tx_length); + + /* reset tx status */ + cs8900_set_tx_status(0, 0); + } + } +} + +u8 cs8900a_device::cs8900_read_rx_buffer(int odd_address) +{ + if (rx_state != CS8900_RX_GOT_FRAME) + { + LOGMASKED(CS8900_DEBUG_WARN_RXTX, "WARNING! RX Read without frame available! (odd=%d)", + odd_address); + + /* always reads zero on HW */ + return 0; + } + else + { + /* + According to the CS8900 spec, the handling is the following: + first read H, then L (RX_STATUS), then H, then L (RX_LENGTH). + Inside the RX frame data, we always get L then H, until the end is reached. + + even odd + CS8900_PP_ADDR_RXSTATUS: - proceed + CS8900_PP_ADDR_RXLENGTH: - proceed + CS8900_PP_ADDR_RX_FRAMELOC: - - + CS8900_PP_ADDR_RX_FRAMELOC+2: proceed - + CS8900_PP_ADDR_RX_FRAMELOC+4: proceed - + */ + u16 addr = odd_address ? 1 : 0; + u8 value; + + /* read RXSTATUS or RX_LENGTH */ + if (rx_count < 4) + { + addr += rx_buffer; + value = GET_PP_8(addr); + rx_count++; + + /* incr after RXSTATUS or RX_LENGTH even (L) read */ + if (!odd_address) + rx_buffer += 2; + } + /* read frame data */ + else + { + /* incr before frame read (but not in first word) */ + if ((rx_count >= 6) && (!odd_address)) + rx_buffer += 2; + + addr += rx_buffer; + value = GET_PP_8(addr); + rx_count++; + } + + LOGMASKED(CS8900_DEBUG_RXTX_DATA, "RX: %04x/%04x: %02x (buffer=%04x,odd=%d)", + rx_count, rx_length + 4, value, addr, odd_address); + + /* check frame end */ + if (rx_count >= rx_length + 4) + { + /* reset receiver state to idle */ + rx_state = CS8900_RX_IDLE; + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "RX: read frame (length=%04x)", rx_length); + } + return value; + } +} + +/* ------------------------------------------------------------------------- */ +/* handle side-effects of read and write operations */ + +/* + This is called *after* the relevant octets are written + */ +void cs8900a_device::cs8900_sideeffects_write_pp(u16 ppaddress, int odd_address) +{ + u16 content = GET_PP_16(ppaddress); + + assert((ppaddress & 1) == 0); + + switch (ppaddress) + { + case CS8900_PP_ADDR_CC_RXCFG: + + /* Skip_1 Flag: remove current (partial) tx frame and restore state */ + if (content & 0x40) + { + /* restore tx state */ + if (tx_state != CS8900_TX_IDLE) + { + tx_state = CS8900_TX_IDLE; + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: skipping current frame"); + } + + /* reset transmitter */ + cs8900_set_transmitter(tx_enabled); + + /* this is an "act once" bit, thus restore it to zero. */ + content &= ~0x40; + SET_PP_16(ppaddress, content); + } + break; + + case CS8900_PP_ADDR_CC_RXCTL: + + if (cs8900_recv_control != content) + { + cs8900_recv_broadcast = content & 0x0800; /* broadcast */ + cs8900_recv_mac = content & 0x0400; /* individual address (IA) */ + cs8900_recv_multicast = content & 0x0200; /* multicast if address passes the hash filter */ + cs8900_recv_correct = content & 0x0100; /* accept correct frames */ + cs8900_recv_promiscuous = content & 0x0080; /* promiscuous mode */ + cs8900_recv_hashfilter = content & 0x0040; /* accept if IA passes the hash filter */ + cs8900_recv_control = content; + } + break; + + case CS8900_PP_ADDR_CC_LINECTL: + { + int enable_tx = (content & 0x0080) == 0x0080; + int enable_rx = (content & 0x0040) == 0x0040; + + if ((enable_tx != tx_enabled) || (enable_rx != rx_enabled)) + { + cs8900_set_transmitter(enable_tx); + cs8900_set_receiver(enable_rx); + } + break; + } + + case CS8900_PP_ADDR_CC_SELFCTL: + { + /* reset chip? */ + if ((content & 0x40) == 0x40) + { + device_reset(); + } + break; + } + + case CS8900_PP_ADDR_TXCMD: + { + if (odd_address) + { + u16 txcommand = GET_PP_16(CS8900_PP_ADDR_TXCMD); + + /* already transmitting? */ + if (tx_state == CS8900_TX_READ_BUSST) + { + LOGMASKED(CS8900_DEBUG_WARN_RXTX, "WARNING! Early abort of transmitted frame"); + } + + /* The transmit status command gets the last transmit command */ + SET_PP_16(CS8900_PP_ADDR_CC_TXCMD, txcommand); + + /* set transmit state */ + tx_state = CS8900_TX_GOT_CMD; + cs8900_set_tx_status(0, 0); + + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: COMMAND accepted (%04x)", txcommand); + } + break; + } + + case CS8900_PP_ADDR_TXLENGTH: + { + if (odd_address && (tx_state == CS8900_TX_GOT_CMD)) + { + u16 txlength = GET_PP_16(CS8900_PP_ADDR_TXLENGTH); + u16 txcommand = GET_PP_16(CS8900_PP_ADDR_CC_TXCMD); + + if (txlength < 4) + { + /* frame to short */ + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: LENGTH rejected - too short! (%04x)", txlength); + + /* mask space available but do not commit */ + tx_state = CS8900_TX_IDLE; + cs8900_set_tx_status(1, 0); + } + else if ((txlength > MAX_TXLENGTH) + || ((txlength > MAX_TXLENGTH - 4) && (!(txcommand & 0x1000))) + ) + { + tx_state = CS8900_TX_IDLE; + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: LENGTH rejected - too long! (%04x)", txlength); + + /* txlength too big, mark an error */ + cs8900_set_tx_status(0, 1); + } + else + { + /* make sure we put the octets to transmit at the right place */ + tx_buffer = CS8900_PP_ADDR_TX_FRAMELOC; + tx_count = 0; + tx_length = txlength; + tx_state = CS8900_TX_GOT_LEN; + + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: LENGTH accepted (%04x)", txlength); + + /* all right, signal that we're ready for the next frame */ + cs8900_set_tx_status(1, 0); + } + } + break; + } + + case CS8900_PP_ADDR_LOG_ADDR_FILTER: + case CS8900_PP_ADDR_LOG_ADDR_FILTER + 2: + case CS8900_PP_ADDR_LOG_ADDR_FILTER + 4: + case CS8900_PP_ADDR_LOG_ADDR_FILTER + 6: + { + unsigned int pos = 8 * (ppaddress - CS8900_PP_ADDR_LOG_ADDR_FILTER + odd_address); + u32 *p = (pos < 32) ? &cs8900_hash_mask[0] : &cs8900_hash_mask[1]; + + *p &= ~(0xFF << pos); /* clear out relevant bits */ + *p |= GET_PP_8(ppaddress + odd_address) << pos; + break; + } + + case CS8900_PP_ADDR_MAC_ADDR: + case CS8900_PP_ADDR_MAC_ADDR + 2: + case CS8900_PP_ADDR_MAC_ADDR + 4: + + /* the MAC address has been changed */ + cs8900_ia_mac[ppaddress - CS8900_PP_ADDR_MAC_ADDR + odd_address] = GET_PP_8(ppaddress + odd_address); + set_mac((char *)cs8900_ia_mac); + + if (odd_address && (ppaddress == CS8900_PP_ADDR_MAC_ADDR + 4)) + LOGMASKED(CS8900_DEBUG, "set MAC address: %02x:%02x:%02x:%02x:%02x:%02x", + cs8900_ia_mac[0], cs8900_ia_mac[1], cs8900_ia_mac[2], + cs8900_ia_mac[3], cs8900_ia_mac[4], cs8900_ia_mac[5]); + break; + } +} + +/* + This is called *before* the relevant octets are read + */ +void cs8900a_device::cs8900_sideeffects_read_pp(u16 ppaddress, int odd_address) +{ + switch (ppaddress) + { + case CS8900_PP_ADDR_SE_RXEVENT: + + /* reading this before all octets of the frame are read performs an "implied skip" */ + { + int access_mask = (odd_address) ? 1 : 2; + + /* update the status register only if the full word of the last + status was read! unfortunately different access patterns are + possible: either the status is read LH, LH, LH... + or HL, HL, HL, or even L, L, L or H, H, H */ + if ((access_mask & rxevent_read_mask) != 0) + { + /* receiver is not enabled */ + if (!rx_enabled) + { + LOGMASKED(CS8900_DEBUG_WARN_RXTX, "WARNING! Can't receive any frame (Receiver is not enabled)!"); + } + else + { + /* perform frame reception */ + u16 ret_val = cs8900_receive(); + + /* RXSTATUS and RXEVENT are the same, except that RXSTATUS buffers + the old value while RXEVENT sets a new value whenever it is called + */ + SET_PP_16(CS8900_PP_ADDR_RXSTATUS, ret_val); + SET_PP_16(CS8900_PP_ADDR_SE_RXEVENT, ret_val); + } + + /* reset read mask of (possible) other access */ + rxevent_read_mask = access_mask; + } + else + { + /* add access bit to mask */ + rxevent_read_mask |= access_mask; + } + break; + } + + case CS8900_PP_ADDR_SE_BUSST: + + if (odd_address) + { + /* read busst before transmit condition is fullfilled */ + if (tx_state == CS8900_TX_GOT_LEN) + { + u16 bus_status = GET_PP_16(CS8900_PP_ADDR_SE_BUSST); + + /* check Rdy4TXNow flag */ + if ((bus_status & 0x100) == 0x100) + { + tx_state = CS8900_TX_READ_BUSST; + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: Ready4TXNow set! (%04x)", + bus_status); + } + else + { + LOGMASKED(CS8900_DEBUG_RXTX_STATE, "TX: waiting for Ready4TXNow! (%04x)", + bus_status); + } + } + } + break; + } +} + +/* ------------------------------------------------------------------------- */ +/* read/write from packet page register */ + +/* read a register from packet page */ +u16 cs8900a_device::cs8900_read_register(u16 ppaddress) +{ + u16 value = GET_PP_16(ppaddress); + + /* --- check the register address --- */ + if (ppaddress < 0x100) + { + /* reserved range reads 0x0300 on real HW */ + if ((ppaddress >= 0x0004) && (ppaddress < 0x0020)) + { + return 0x0300; + } + } + /* --- read control register range --- */ + else if (ppaddress < 0x120) + { + u16 regNum = ppaddress - 0x100; + regNum &= ~1; + regNum++; + LOGMASKED(CS8900_DEBUG_REGISTERS, "Read Control Register %04x: %04x (reg=%02x)", ppaddress, value, regNum); + /* reserved register? */ + if ((regNum == 0x01) || (regNum == 0x11) || (regNum > 0x19)) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Read reserved Control Register %04x (reg=%02x)", ppaddress, regNum); + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + /* make sure interal address is always valid */ + assert((value & 0x3f) == regNum); + } + /* --- read status register range --- */ + else if (ppaddress < 0x140) + { + u16 regNum = ppaddress - 0x120; + regNum &= ~1; +#ifdef CS8900_DEBUG_IGNORE_RXEVENT + if (regNum != 4) // do not show RXEVENT +#endif // ifdef CS8900_DEBUG_IGNORE_RXEVENT + LOGMASKED(CS8900_DEBUG_REGISTERS, "Read Status Register %04x: %04x (reg=%02x)", ppaddress, value, regNum); + /* reserved register? */ + if ((regNum == 0x02) || (regNum == 0x06) || (regNum == 0x0a) || + (regNum == 0x0e) || (regNum == 0x1a) || (regNum == 0x1e)) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, + "WARNING! Read reserved Status Register %04x (reg=%02x)", ppaddress, regNum); + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + /* make sure interal address is always valid */ + assert((value & 0x3f) == regNum); + } + /* --- read transmit register range --- */ + else if (ppaddress < 0x150) + { + if (ppaddress == 0x144) + { + /* make sure interal address is always valid */ + assert((value & 0x3f) == 0x09); + LOGMASKED(CS8900_DEBUG_REGISTERS, "Read TX Cmd Register %04x: %04x", ppaddress, value); + } + else if (ppaddress == 0x146) + { + LOGMASKED(CS8900_DEBUG_REGISTERS, "Read TX Len Register %04x: %04x", ppaddress, value); + } + /* reserved range */ + else + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Read reserved Initiate Transmit Register %04x", ppaddress); + + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + } + /* --- read address filter register range --- */ + else if (ppaddress < 0x160) + { + /* reserved range */ + if (ppaddress >= 0x15e) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Read reserved Address Filter Register %04x", ppaddress); + + /* real HW returns 0x0300 in reserved register range */ + return 0x0300; + } + } + /* --- reserved range below 0x400 (returns 0x300 on real HW) */ + else if (ppaddress < 0x400) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Read reserved Register %04x", ppaddress); + return 0x0300; + } + /* --- range from 0x400 .. 0x9ff --- RX Frame */ + else if (ppaddress < 0xa00) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Read from RX Buffer Range %04x", ppaddress); + return 0x0000; + } + /* --- range from 0xa00 .. 0xfff --- TX Frame */ + else + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Read from TX Buffer Range %04x", ppaddress); + return 0x0000; + } + /* actually read from pp memory */ + return value; +} + +void cs8900a_device::cs8900_write_register(u16 ppaddress, u16 value) +{ + /* --- write bus interface register range --- */ + if (ppaddress < 0x100) + { + int ignore = 0; + + if (ppaddress < 0x20) + { + ignore = 1; + } + else if ((ppaddress >= 0x26) && (ppaddress < 0x2c)) + { + ignore = 1; + } + else if (ppaddress == 0x38) + { + ignore = 1; + } + else if (ppaddress >= 0x44) + { + ignore = 1; + } + + if (ignore) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, + "WARNING! Ignoring write to read only/reserved Bus Interface Register %04x", + ppaddress); + return; + } + } + /* --- write to control register range --- */ + else if (ppaddress < 0x120) + { + u16 regNum = ppaddress - 0x100; + regNum &= ~1; + regNum += 1; + + /* validate internal address */ + if ((value & 0x3f) != regNum) + { + /* fix internal address */ + value &= ~0x3f; + value |= regNum; + } + LOGMASKED(CS8900_DEBUG_REGISTERS, "Write Control Register %04x: %04x (reg=%02x)", ppaddress, value, regNum); + + /* invalid register? -> ignore! */ + if ((regNum == 0x01) || (regNum == 0x11) || (regNum > 0x19)) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, + "WARNING! Ignoring write to reserved Control Register %04x (reg=%02x)", + ppaddress, regNum); + return; + } + } + /* --- write to status register range --- */ + else if (ppaddress < 0x140) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Ignoring write to read-only Status Register %04x", ppaddress); + return; + } + /* --- write to initiate transmit register range --- */ + else if (ppaddress < 0x150) + { + /* check tx_cmd register */ + if (ppaddress == 0x144) + { + /* validate internal address */ + if ((value & 0x3f) != 0x09) + { + /* fix internal address */ + value &= ~0x3f; + value |= 0x09; + } + + /* mask out reserved bits */ + value &= 0x33ff; + LOGMASKED(CS8900_DEBUG_REGISTERS, "Write TX Cmd Register %04x: %04x", ppaddress, value); + } + /* check tx_length register */ + else if (ppaddress == 0x146) + { + /* HW always masks 0x0fff */ + value &= 0x0fff; + LOGMASKED(CS8900_DEBUG_REGISTERS, "Write TX Len Register %04x: %04x", ppaddress, value); + } + /* reserved range */ + else if ((ppaddress < 0x144) || (ppaddress > 0x147)) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, + "WARNING! Ignoring write to reserved Initiate Transmit Register %04x", + ppaddress); + return; + } + } + /* --- write to address filter register range --- */ + else if (ppaddress < 0x160) + { + /* reserved range */ + if (ppaddress >= 0x15e) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, + "WARNING! Ingoring write to reserved Address Filter Register %04x", + ppaddress); + return; + } + } + /* --- ignore write outside --- */ + else if (ppaddress < 0x400) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Ingoring write to reserved Register %04x", ppaddress); + return; + } + else if (ppaddress < 0xa00) + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Ignoring write to RX Buffer Range %04x", ppaddress); + return; + } + else + { + LOGMASKED(CS8900_DEBUG_WARN_REG, "WARNING! Ignoring write to TX Buffer Range %04x", ppaddress); + return; + } + /* actually set value */ + SET_PP_16(ppaddress, value); +} + +void cs8900a_device::cs8900_auto_incr_pp_ptr() +{ + /* perform auto increment of packet page pointer */ + if ((cs8900_packetpage_ptr & PP_PTR_AUTO_INCR_FLAG) == PP_PTR_AUTO_INCR_FLAG) + { + /* pointer is always increment by one on real HW */ + u16 ptr = cs8900_packetpage_ptr & PP_PTR_ADDR_MASK; + u16 flags = cs8900_packetpage_ptr & PP_PTR_FLAG_MASK; + ptr++; + cs8900_packetpage_ptr = ptr | flags; + } +} + +/* ----- read byte from I/O range ----- */ +u8 cs8900a_device::cs8900_read(u16 io_address) +{ + u8 retval, lo, hi; + u16 word_value; + u16 reg_base; + + assert(cs8900_ioregs); + assert(cs8900_packetpage); + assert(io_address < 0x10); + + /* register base addr */ + reg_base = io_address & ~1; + + /* RX register is special as it reads from RX buffer directly */ + if ((reg_base == CS8900_ADDR_RXTXDATA) || (reg_base == CS8900_ADDR_RXTXDATA2)) + { + LOGMASKED(CS8900_DEBUG_LOAD, "reading RX Register.\n"); + return cs8900_read_rx_buffer(io_address & 0x01); + } + + /* read packet page pointer */ + if (reg_base == CS8900_ADDR_PP_PTR) + { + word_value = cs8900_packetpage_ptr; + } + /* read a register from packet page */ + else + { + u16 ppaddress = 0; + + /* determine read addr in packet page */ + switch (reg_base) + { + /* PP_DATA2 behaves like PP_DATA on real HW both show the contents at the page pointer */ + case CS8900_ADDR_PP_DATA: + case CS8900_ADDR_PP_DATA2: + + /* mask and align address of packet pointer */ + ppaddress = cs8900_packetpage_ptr & PP_PTR_ADDR_MASK; + ppaddress &= ~1; + + /* if flags match then auto incr pointer */ + if (!machine().side_effects_disabled()) + { + cs8900_auto_incr_pp_ptr(); + } + break; + + case CS8900_ADDR_INTSTQUEUE: + ppaddress = CS8900_PP_ADDR_SE_ISQ; + break; + + case CS8900_ADDR_TXCMD: + ppaddress = CS8900_PP_ADDR_TXCMD; + break; + + case CS8900_ADDR_TXLENGTH: + ppaddress = CS8900_PP_ADDR_TXLENGTH; + break; + + default: + /* invalid! */ + assert(0); + break; + } + + /* do side effects before access */ + if (!machine().side_effects_disabled()) + { + cs8900_sideeffects_read_pp(ppaddress, io_address & 1); + } + + /* read register value */ + word_value = cs8900_read_register(ppaddress); + + LOGMASKED(CS8900_DEBUG_LOAD, "reading PP Ptr: $%04X => $%04X.\n", ppaddress, word_value); + } + + /* extract return value from word_value */ + lo = LO_BYTE(word_value); + hi = HI_BYTE(word_value); + + if ((io_address & 1) == 0) + { + /* low byte on even address */ + retval = lo; + } + else + { + /* high byte on odd address */ + retval = hi; + } + + LOGMASKED(CS8900_DEBUG_LOAD, "read [$%02X] => $%02X.\n", io_address, retval); + + /* update _word_ value in register bank */ + cs8900_ioregs[reg_base] = lo; + cs8900_ioregs[reg_base + 1] = hi; + + return retval; +} + +/* ----- write byte to I/O range of VICE ----- */ +void cs8900a_device::cs8900_store(u16 io_address, u8 var) +{ + u16 reg_base; + u16 word_value; + + assert(cs8900_ioregs); + assert(cs8900_packetpage); + assert(io_address < 0x10); + + LOGMASKED(CS8900_DEBUG_STORE, "store [$%02X] <= $%02X.\n", io_address, (int)var); + + /* register base addr */ + reg_base = io_address & ~1; + + /* TX Register is special as it writes to TX buffer directly */ + if ((reg_base == CS8900_ADDR_RXTXDATA) || (reg_base == CS8900_ADDR_RXTXDATA2)) + { + cs8900_write_tx_buffer(var, io_address & 1); + return; + } + + /* combine stored value with new written byte */ + if ((io_address & 1) == 0) + { + /* overwrite low byte */ + word_value = LOHI_WORD(var, cs8900_ioregs[reg_base + 1]); + } + else + { + /* overwrite high byte */ + word_value = LOHI_WORD(cs8900_ioregs[reg_base], var); + } + + if (reg_base == CS8900_ADDR_PP_PTR) + { + /* cv: we store the full package pointer in cs8900_packetpage_ptr variable. + this includes the mask area (0xf000) and the addr range (0x0fff). + we ensure that the bits 0x3000 are always set (as in real HW). + odd values of the pointer are valid and supported. + only register read and write have to be mapped to word boundary. */ + word_value |= 0x3000; + cs8900_packetpage_ptr = word_value; + LOGMASKED(CS8900_DEBUG_STORE, "set PP Ptr to $%04X.\n", cs8900_packetpage_ptr); + } + else + { + /* write a register */ + + /*! \TODO: Find a reasonable default */ + u16 ppaddress = CS8900_PP_ADDR_PRODUCTID; + + /* now determine address of write in packet page */ + switch (reg_base) + { + case CS8900_ADDR_PP_DATA: + case CS8900_ADDR_PP_DATA2: + + /* mask and align ppaddress from page pointer */ + ppaddress = cs8900_packetpage_ptr & (MAX_PACKETPAGE_ARRAY - 1); + ppaddress &= ~1; + + /* auto increment pp ptr */ + cs8900_auto_incr_pp_ptr(); + break; + + case CS8900_ADDR_TXCMD: + ppaddress = CS8900_PP_ADDR_TXCMD; + break; + + case CS8900_ADDR_TXLENGTH: + ppaddress = CS8900_PP_ADDR_TXLENGTH; + break; + + case CS8900_ADDR_INTSTQUEUE: + ppaddress = CS8900_PP_ADDR_SE_ISQ; + break; + + case CS8900_ADDR_PP_PTR: + break; + + default: + /* invalid */ + assert(0); + break; + } + + LOGMASKED(CS8900_DEBUG_STORE, "before writing to PP Ptr: $%04X <= $%04X.\n", + ppaddress, word_value); + + /* perform the write */ + cs8900_write_register(ppaddress, word_value); + + /* handle sideeffects */ + cs8900_sideeffects_write_pp(ppaddress, io_address & 1); + + /* update word value if it was changed in write register or by side effect */ + word_value = GET_PP_16(ppaddress); + + LOGMASKED(CS8900_DEBUG_STORE, "after writing to PP Ptr: $%04X <= $%04X.\n", + ppaddress, word_value); + } + + /* update IO registers */ + cs8900_ioregs[reg_base] = LO_BYTE(word_value); + cs8900_ioregs[reg_base + 1] = HI_BYTE(word_value); +} + +u8 cs8900a_device::read(u16 address) +{ + return cs8900_read(address); +} + +void cs8900a_device::write(u16 address, u8 data) +{ + return cs8900_store(address, data); +} + +int cs8900a_device::recv_start_cb(u8 *buf, int length) +{ + LOGMASKED(CS8900_DEBUG, "recv_start_cb(), %p len %d\n", buf, length); + + // Make an extra call to cs8900_should_accept() on the receive + // callback to reduce the number of packets queueing up that + // will just get rejected anyway. + bool phashed = false; + int phash_index = 0; + bool pcorrect_mac = false; + bool pbroadcast = false; + bool pmulticast = false; + + if (cs8900_should_accept(buf, length, &phashed, &phash_index, &pcorrect_mac, &pbroadcast, &pmulticast)) + { + std::vector<u8> frame; + frame.assign(buf, buf + length); + m_frame_queue.push(frame); + + // In order to ensure the frame queue doesn't grow without bound, evict from the front + // when too many packets pile up. This could happen due to a DOS or to the card not being + // in use and stray packets enter. + + if (m_frame_queue.size() > MAX_FRAME_QUEUE_ENTRIES) + { + m_frame_queue.pop(); + } + } + return length; +} diff --git a/src/devices/machine/cs8900a.h b/src/devices/machine/cs8900a.h new file mode 100644 index 00000000000..69931c583e5 --- /dev/null +++ b/src/devices/machine/cs8900a.h @@ -0,0 +1,106 @@ +// license:GPL-2.0+ +// copyright-holders:Spiro Trikaliotis, Rhett Aultman + +/************************************************************************* + + CS8900A ethernet controller implementation + + by Rhett Aultman <roadriverrail@gmail.com> + ported to MAME from VICE Project (https://sourceforge.net/p/vice-emu/) + VICE CS8900 code by Spiro Trikaliotis <Spiro.Trikaliotis@gmx.de> + +**************************************************************************/ + +#ifndef MAME_MACHINE_CS8900A_H +#define MAME_MACHINE_CS8900A_H + +#pragma once + +#include <queue> + +/*************************************************************************** + TYPE DEFINITIONS +***************************************************************************/ + +class cs8900a_device : public device_t, public device_network_interface { +public: + + cs8900a_device(machine_config const& mconfig, char const *tag, device_t *owner, u32 clock); + u8 read(u16 address); + void write(u16 address, u8 data); + +protected: + + cs8900a_device(const machine_config& mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock); + + // device-level 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; + +private: + + static constexpr u8 CS8900_COUNT_IO_REGISTER = 0x10; /* we have 16 I/O register */ + static constexpr u16 MAX_PACKETPAGE_ARRAY = 0x1000; /* 4 KB */ + + u8 cs8900_ia_mac[6]; + + u32 cs8900_hash_mask[2]; + + // CS8900A IO Registers + u8 cs8900_ioregs[CS8900_COUNT_IO_REGISTER]; + + // CS8900A PacketPage + u8 cs8900_packetpage[MAX_PACKETPAGE_ARRAY]; + u16 cs8900_packetpage_ptr; + + /* reveiver setup */ + u16 cs8900_recv_control; /* copy of CC_RXCTL (contains all bits below) */ + bool cs8900_recv_broadcast; /* broadcast */ + bool cs8900_recv_mac; /* individual address (IA) */ + bool cs8900_recv_multicast; /* multicast if address passes the hash filter */ + bool cs8900_recv_correct; /* accept correct frames */ + bool cs8900_recv_promiscuous; /* promiscuous mode */ + bool cs8900_recv_hashfilter; /* accept if IA passes the hash filter */ + + u16 tx_buffer; + u16 rx_buffer; + + u16 tx_count; + u16 rx_count; + u16 tx_length; + u16 rx_length; + + /* tranceiver state */ + int tx_state; + int rx_state; + int tx_enabled; + int rx_enabled; + + int rxevent_read_mask; /* set if L and/or H u8 was read in RXEVENT? */ + + // The frame queue + std::queue<std::vector<u8> >m_frame_queue; + + std::string debug_outbuffer(const int length, const unsigned char *const buffer); + void cs8900_set_tx_status(int ready, int error); + void cs8900_set_receiver(int enabled); + void cs8900_set_transmitter(int enabled); + bool cs8900_should_accept(unsigned char *buffer, int length, bool *phashed, int *phash_index, bool *pcorrect_mac, bool *pbroadcast, bool *pmulticast); + u16 cs8900_receive(void); + void cs8900_write_tx_buffer(u8 value, int odd_address); + u8 cs8900_read_rx_buffer(int odd_address); + void cs8900_sideeffects_write_pp(u16 ppaddress, int odd_address); + void cs8900_sideeffects_read_pp(u16 ppaddress, int odd_address); + u16 cs8900_read_register(u16 ppaddress); + void cs8900_write_register(u16 ppaddress, u16 value); + void cs8900_auto_incr_pp_ptr(); + u8 cs8900_read(u16 io_address); + void cs8900_store(u16 io_address, u8 var); +}; + +DECLARE_DEVICE_TYPE(CS8900A, cs8900a_device) + +#endif // MAME_MACHINE_CS8900A_H diff --git a/src/mame/drivers/apple2gs.cpp b/src/mame/drivers/apple2gs.cpp index 40f990c743b..6f9e3e8402b 100644 --- a/src/mame/drivers/apple2gs.cpp +++ b/src/mame/drivers/apple2gs.cpp @@ -108,6 +108,7 @@ #include "bus/a2bus/byte8251.h" //#include "bus/a2bus/hostram.h" //#include "bus/a2bus/ramfast.h" +#include "bus/a2bus/uthernet.h" #include "bus/a2gameio/gameio.h" @@ -4576,6 +4577,7 @@ static void apple2_cards(device_slot_interface &device) // device.option_add("hostram", A2BUS_HOSTRAM); /* Slot 7 RAM for GS Plus host protocol */ // device.option_add("ramfast", A2BUS_RAMFAST); /* C.V. Technologies RAMFast SCSI card */ device.option_add("cmsscsi", A2BUS_CMSSCSI); /* CMS Apple II SCSI Card */ + device.option_add("uthernet", A2BUS_UTHERNET); /* CMS Apple II SCSI Card */ } void apple2gs_state::apple2gs(machine_config &config) |