1 // license:BSD-3-Clause
2 // copyright-holders:Aaron Giles
3 /*************************************************************************
4 
5     SMC91C9X ethernet controller implementation
6 
7     by Aaron Giles, Ted Green
8 
9 **************************************************************************/
10 
11 #ifndef MAME_MACHINE_SMC91C9X_H
12 #define MAME_MACHINE_SMC91C9X_H
13 
14 #pragma once
15 
16 /***************************************************************************
17     TYPE DEFINITIONS
18 ***************************************************************************/
19 
20 class smc91c9x_device : public device_t,public device_network_interface
21 {
22 public:
irq_handler()23 	auto irq_handler() { return m_irq_handler.bind(); }
24 
25 	u16 read(offs_t offset, u16 mem_mask = ~0);
26 	void write(offs_t offset, u16 data, u16 mem_mask = ~0);
27 
set_link_connected(bool connected)28 	void set_link_connected(bool connected) { m_link_unconnected = !connected; };
29 
30 protected:
31 	enum class dev_type {
32 		SMC91C94,
33 		SMC91C96
34 	};
35 
36 	smc91c9x_device(const machine_config &mconfig, device_type type, const char *tag, device_t *owner, uint32_t clock, dev_type device_type);
37 
38 	// device-level overrides
39 	virtual void device_start() override;
40 	virtual void device_reset() override;
41 
42 	// device_network_interface overrides
43 	virtual void send_complete_cb(int result) override;
44 	virtual int recv_start_cb(u8 *buf, int length) override;
45 	virtual void recv_complete_cb(int result) override;
46 
47 	void dump_bytes(u8 *buf, int length);
48 	int address_filter(u8 *buf);
49 	int receive(u8 *buf, int length);
50 
51 	TIMER_CALLBACK_MEMBER(tx_poll);
52 
53 	const dev_type m_device_type;
54 	unsigned m_num_ebuf;
55 
56 private:
57 	// Ethernet registers - bank 0
58 	enum bank0_addr : u8 {
59 		B0_TCR        = (0 * 8 + 0),
60 		B0_EPH_STATUS = (0 * 8 + 1),
61 		B0_RCR        = (0 * 8 + 2),
62 		B0_COUNTER    = (0 * 8 + 3),
63 		B0_MIR        = (0 * 8 + 4),
64 		B0_MCR        = (0 * 8 + 5),
65 		B0_BANK       = (0 * 8 + 7)
66 	};
67 
68 	// Ethernet registers - bank 1
69 	enum bank1_addr : u8 {
70 		B1_CONFIG       = (1 * 8 + 0),
71 		B1_BASE         = (1 * 8 + 1),
72 		B1_IA0_1        = (1 * 8 + 2),
73 		B1_IA2_3        = (1 * 8 + 3),
74 		B1_IA4_5        = (1 * 8 + 4),
75 		B1_GENERAL_PURP = (1 * 8 + 5),
76 		B1_CONTROL      = (1 * 8 + 6)
77 	};
78 
79 	// Ethernet registers - bank 2
80 	enum bank2_addr : u8 {
81 		B2_MMU_COMMAND  = (2 * 8 + 0),
82 		B2_PNR_ARR      = (2 * 8 + 1),
83 		B2_FIFO_PORTS   = (2 * 8 + 2),
84 		B2_POINTER      = (2 * 8 + 3),
85 		B2_DATA_0       = (2 * 8 + 4),
86 		B2_DATA_1       = (2 * 8 + 5),
87 		B2_INTERRUPT    = (2 * 8 + 6)
88 	};
89 
90 	// Ethernet registers - bank 3
91 	enum bank3_addr : u8 {
92 		B3_MT0_1    = (3 * 8 + 0),
93 		B3_MT2_3    = (3 * 8 + 1),
94 		B3_MT4_5    = (3 * 8 + 2),
95 		B3_MT6_7    = (3 * 8 + 3),
96 		B3_MGMT     = (3 * 8 + 4),
97 		B3_REVISION = (3 * 8 + 5),
98 		B3_ERCV     = (3 * 8 + 6)
99 	};
100 
101 	// Ethernet MMU commands
102 	enum mmu_cmd : u8 {
103 		ECMD_NOP                        = 0,
104 		ECMD_ALLOCATE                   = 2,
105 		ECMD_RESET_MMU                  = 4,
106 		ECMD_REMOVE_TOPFRAME_RX         = 6,
107 		ECMD_REMOVE_TOPFRAME_TX         = 7,
108 		ECMD_REMOVE_RELEASE_TOPFRAME_RX = 8,
109 		ECMD_RELEASE_PACKET             = 10,
110 		ECMD_ENQUEUE_PACKET             = 12,
111 		ECMD_RESET_FIFOS                = 14
112 	};
113 
114 	// Ethernet interrupt bits
115 	enum eint_def : u8 {
116 		EINT_RCV            = 0x01,
117 		EINT_TX             = 0x02,
118 		EINT_TX_EMPTY       = 0x04,
119 		EINT_ALLOC          = 0x08,
120 		EINT_RX_OVRN        = 0x10,
121 		EINT_EPH            = 0x20,
122 		EINT_ERCV           = 0x40,    // 91c92 only
123 		EINT_TX_IDLE        = 0x80     // 91c94 only
124 	};
125 
126 	// Address filter return codes
127 	enum addr_filter_def : int {
128 		ADDR_NOMATCH   = 0,
129 		ADDR_UNICAST   = 1,
130 		ADDR_BROADCAST = 2,
131 		ADDR_MULTICAST = 3
132 	};
133 
134 	// Rx/Tx control bits
135 	enum control_mask : u8 {
136 		EBUF_RX_ALWAYS = 0x40,   // Always set on receive buffer control byte
137 		EBUF_ODD       = 0x20,   // Odd number of data payload bytes
138 		EBUF_CRC       = 0x10       // Tx add CRC
139 	};
140 
141 	// Receive buffer status
142 	enum rx_status_mask : u16 {
143 		ALGNERR       = 0x8000,
144 		BRODCAST      = 0x4000,
145 		BADCRC        = 0x2000,
146 		ODDFRM        = 0x1000,
147 		TOOLNG        = 0x0800, // Received fram is longer than 1518 bytes on cable
148 		TOOSHORT      = 0x0400, // Received fram is shorter than 64 bytes on cable
149 		HASHVALUE     = 0x007e,
150 		MULTCAST      = 0x0001
151 	};
152 
153 	// EPH Status bits
154 	enum eph_mask : u16 {
155 		LINK_OK       = 0x4000, // State of link integrity test
156 		CTR_ROL       = 0x1000, // Counter roll Over
157 		EXC_DEF       = 0x0800, // Excessive deferral
158 		LOST_CARR     = 0x0400, // Lost carrier sense
159 		LATCOL        = 0x0200, // Late collisions detected
160 		WAKEUP        = 0x0100, // Magic packet received
161 		TX_DEFER      = 0x0080, // Transmit deferred
162 		LTX_BRD       = 0x0040, // Last transmit frame was a broadcast
163 		SQET          = 0x0020, // Signal Quality Error Test
164 		E16COL        = 0x0010, // 16 collisions reached
165 		LTX_MULT      = 0x0008, // Last transmit frame was a multicast
166 		MULCOL        = 0x0004, // Multiple collisions detected
167 		SNGLCOL       = 0x0002, // Single collision detected
168 		TX_SUC        = 0x0001  // Last transmit frame was successful
169 	};
170 
171 	// CTR register bits
172 	enum ctr_mask : u16 {
173 		RCV_BAD       = 0x4000, // Receive bad CRC packets
174 		PWRDN         = 0x2000, // Power down ethernet
175 		WAKEUP_EN     = 0x1000, // Enable magic packet wakeup
176 		AUTO_RELEASE  = 0x0800, // Release transmit packets on good transmission
177 		LE_ENABLE     = 0x0080, // Link Error enable
178 		CR_ENABLE     = 0x0040, // Counter Roll over enable
179 		TE_ENABLE     = 0x0020, // Transmit Error enable
180 		EEPROM_SEL    = 0x0004, // EEPROM address
181 		RELOAD        = 0x0002, // Reload config from EEPROM
182 		STORE         = 0x0001  // Store config to EEPROM
183 	};
184 
185 	// Transmit Control Register bits
186 	enum tcr_mask : u16 {
187 		FDSE        = 0x8000,
188 		EPH_LOOP    = 0x2000,
189 		STP_SQET    = 0x1000,
190 		FDUPLX      = 0x0800,
191 		MON_CSN     = 0x0400,
192 		NOCRC       = 0x0100,
193 		PAD_EN      = 0x0080,
194 		FORCOL      = 0x0004,
195 		LOOP        = 0x0002,
196 		TXENA       = 0x0001
197 	};
198 
199 	// Receive Control Register bits
200 	enum rcr_mask : u16 {
201 		SOFT_RST    = 0x8000,
202 		FILT_CAR    = 0x4000,
203 		STRIP_CRC   = 0x0200,
204 		RXEN        = 0x0100,
205 		ALMUL       = 0x0004,
206 		PRMS        = 0x0002,
207 		RX_ABORT    = 0x0001
208 	};
209 
210 	// Pointer Register bits
211 	enum pointer_mask : u16 {
212 		RCV         = 0x8000,
213 		AUTO_INCR   = 0x4000,
214 		READ        = 0x2000,
215 		PTR         = 0x07ff
216 	};
217 
218 	static constexpr unsigned ETHER_BUFFER_SIZE = 256 * 6;
219 	static const u8 ETH_BROADCAST[];
220 	static const u8 WMS_OUI[];
221 
222 	// mmu
223 
224 	// The bits in these vectors indicate a packet has been allocated
225 	u32 m_alloc_rx, m_alloc_tx;
226 
227 	// Requests a packet allocation and returns true
228 	// and sets the packet number if successful
229 	bool alloc_req(const int tx, int &packet_num);
230 	// Releases an allocation
231 	void alloc_release(const int packet_num);
232 	// Resets the MMU
233 	void mmu_reset();
234 
235 	// internal state
236 	devcb_write_line m_irq_handler;
237 
238 	// link unconnected
239 	bool m_link_unconnected;
240 
241 	/* raw register data and masks */
242 	uint16_t          m_reg[64];
243 	uint16_t          m_regmask[64];
244 
245 	/* IRQ information */
246 	uint8_t           m_irq_state;
247 
248 	// Main memory
249 	std::unique_ptr<u8[]> m_buffer;
250 
251 	/* counters */
252 	uint32_t          m_sent;
253 	uint32_t          m_recd;
254 
255 	emu_timer* m_tx_poll;
256 
257 	int m_tx_active;
258 	int m_rx_active;
259 	int m_tx_retry_count;
260 	u8 m_rx_hash;
261 	u8 m_loopback_result;
262 
263 	void update_ethernet_irq();
264 	void update_stats();
265 
266 	void process_command(uint16_t data);
267 	void reset_tx_fifos();
268 
269 	// TODO: Make circular fifo a separate device
270 	// Simple circular FIFO, power of 2 size, no over/under run checking
271 	static constexpr unsigned FIFO_SIZE = 1 << 5;
272 
273 	// FIFO for allocated (queued) transmit packets
274 	u8 m_queued_tx[FIFO_SIZE];
275 	int m_queued_tx_h, m_queued_tx_t;
reset_queued_tx()276 	void reset_queued_tx() { m_queued_tx_t = m_queued_tx_h = 0; };
push_queued_tx(const u8 & data)277 	void push_queued_tx(const u8 &data) { m_queued_tx[m_queued_tx_h++] = data; m_queued_tx_h &= FIFO_SIZE - 1; };
pop_queued_tx()278 	u8 pop_queued_tx() { u8 val = m_queued_tx[m_queued_tx_t++]; m_queued_tx_t &= FIFO_SIZE - 1; return val; };
empty_queued_tx()279 	bool empty_queued_tx() const { return m_queued_tx_h == m_queued_tx_t; };
curr_queued_tx()280 	u8 curr_queued_tx() const { return m_queued_tx[m_queued_tx_t]; };
281 
282 	// FIFO for completed transmit packets
283 	u8 m_completed_tx[FIFO_SIZE];
284 	int m_completed_tx_h, m_completed_tx_t;
reset_completed_tx()285 	void reset_completed_tx() { m_completed_tx_t = m_completed_tx_h = 0; };
push_completed_tx(const u8 & data)286 	void push_completed_tx(const u8 &data) { m_completed_tx[m_completed_tx_h++] = data; m_completed_tx_h &= FIFO_SIZE - 1; };
pop_completed_tx()287 	u8 pop_completed_tx() { u8 val = m_completed_tx[m_completed_tx_t++]; m_completed_tx_t &= FIFO_SIZE - 1; return val; };
empty_completed_tx()288 	bool empty_completed_tx() const { return m_completed_tx_h == m_completed_tx_t; };
curr_completed_tx()289 	u8 curr_completed_tx() const { return m_completed_tx[m_completed_tx_t]; };
290 
291 	// FIFO for completed receive packets
292 	u8 m_completed_rx[FIFO_SIZE];
293 	int m_completed_rx_h, m_completed_rx_t;
reset_completed_rx()294 	void reset_completed_rx() { m_completed_rx_t = m_completed_rx_h = 0; };
push_completed_rx(const u8 & data)295 	void push_completed_rx(const u8 &data) { m_completed_rx[m_completed_rx_h++] = data; m_completed_rx_h &= FIFO_SIZE - 1; };
pop_completed_rx()296 	u8 pop_completed_rx() { u8 val = m_completed_rx[m_completed_rx_t++]; m_completed_rx_t &= FIFO_SIZE - 1; return val; };
empty_completed_rx()297 	bool empty_completed_rx() const { return m_completed_rx_h == m_completed_rx_t; };
curr_completed_rx()298 	u8 curr_completed_rx() const { return m_completed_rx[m_completed_rx_t]; };
299 
300 };
301 
302 
303 class smc91c94_device : public smc91c9x_device
304 {
305 public:
306 	smc91c94_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
307 };
308 
309 class smc91c96_device : public smc91c9x_device
310 {
311 public:
312 	smc91c96_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock);
313 };
314 
315 
316 DECLARE_DEVICE_TYPE(SMC91C94, smc91c94_device)
317 DECLARE_DEVICE_TYPE(SMC91C96, smc91c96_device)
318 
319 
320 /***************************************************************************
321     DEVICE CONFIGURATION MACROS
322 ***************************************************************************/
323 
324 #endif // MAME_MACHINE_SMC91C9X_H
325