1 //
2 // Copyright (c) 2010-2023 Antmicro
3 //
4 // This file is licensed under the MIT License.
5 // Full license text is available in 'licenses/MIT.txt'.
6 //
7 using System;
8 using System.Collections.Generic;
9 using System.Linq;
10 using Antmicro.Renode.Core;
11 using Antmicro.Renode.Core.Structure;
12 using Antmicro.Renode.Core.Structure.Registers;
13 using Antmicro.Renode.Logging;
14 using Antmicro.Renode.Network;
15 using Antmicro.Renode.Peripherals.SPI;
16 using Antmicro.Renode.Utilities;
17 
18 namespace Antmicro.Renode.Peripherals.Network
19 {
20     public class ENC28J60 : ISPIPeripheral, IMACInterface
21     {
ENC28J60()22         public ENC28J60()
23         {
24             sync = new object();
25             ResetPointers();
26 
27             var econ1 = new ByteRegister(this).WithValueField(0, 2, out currentBank, name: "BSEL")
28                                               .WithFlag(2, out ethernetReceiveEnabled, name: "RXEN")
29                                               .WithFlag(3, FieldMode.Read, writeCallback: (_, value) => { if(value) TransmitPacket(); }, name: "TXRTS")
30                                               .WithFlag(7, name: "TXRST");
31 
32             var econ2 = new ByteRegister(this, 0x80).WithFlag(6, FieldMode.Read, writeCallback: delegate {
33                                                         waitingPacketCount = Math.Max(0, waitingPacketCount - 1);
34                                                         RefreshInterruptStatus(); }, name: "PKTDEC")
35                                                     .WithFlag(7, out autoIncrement, name: "AUTOINC");
36 
37             var estat = new ByteRegister(this, 1).WithReadCallback(delegate { transmitPacketInterrupt.Value = false; RefreshInterruptStatus(); }) // not sure
38                                                  .WithFlag(0, FieldMode.Read, name: "CLKRDY"); // we're always ready, so the reset value is 1
39 
40             var eie = new ByteRegister(this).WithFlag(3, out transmitPacketInterruptEnabled, writeCallback: delegate { RefreshInterruptStatus(); }, name: "TXIE")
41                                             .WithFlag(6, out receivePacketInterruptEnabled, writeCallback: delegate { RefreshInterruptStatus(); }, name: "PKTIE")
42                                             .WithFlag(7, out interruptsEnabled, writeCallback: delegate { RefreshInterruptStatus(); }, name: "INTIE");
43 
44             var eir = new ByteRegister(this).WithFlag(0, name: "RXERIF")
45                                             .WithFlag(3, out transmitPacketInterrupt, writeCallback: delegate { RefreshInterruptStatus(); }, name: "TXIF")
46                                             .WithFlag(6, FieldMode.Read, valueProviderCallback: _ => IsReceiveInterruptActive(), name: "PKTIF");
47 
48             var bank0Map = new Dictionary<long, ByteRegister>
49             {
50                 // ERDPTL
51                 { 0x00, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(bufferReadPointer),
52                                                               writeCallback: (_, value) => SetLowByteOf(ref bufferReadPointer, (byte)value)) },
53 
54                 // ERDPTH
55                 { 0x01, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(bufferReadPointer),
56                                                               writeCallback: (_, value) => SetHighByteOf(ref bufferReadPointer, (byte)value)) },
57 
58                 // EWRPTL
59                 { 0x02, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(bufferWritePointer),
60                                                               writeCallback: (_, value) => SetLowByteOf(ref bufferWritePointer, (byte)value)) },
61 
62                 // EWRPTH
63                 { 0x03, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(bufferWritePointer),
64                                                               writeCallback: (_, value) => SetHighByteOf(ref bufferWritePointer, (byte)value)) },
65 
66                 // ETXSTL
67                 { 0x04, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(transmitBufferStart),
68                                                               writeCallback: (_, value) => SetLowByteOf(ref transmitBufferStart, (byte)value)) },
69 
70                 // ETXSTH
71                 { 0x05, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(transmitBufferStart),
72                                                               writeCallback: (_, value) => SetHighByteOf(ref transmitBufferStart, (byte)value)) },
73 
74                 // ETXNDL
75                 { 0x06, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(transmitBufferEnd),
76                                                               writeCallback: (_, value) => SetLowByteOf(ref transmitBufferEnd, (byte)value)) },
77 
78                 // ETXNDH
79                 { 0x07, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(transmitBufferEnd),
80                                                               writeCallback: (_, value) => SetHighByteOf(ref transmitBufferEnd, (byte)value)) },
81 
82                 // ERXSTL
83                 { 0x08, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(receiveBufferStart),
84                                                               writeCallback: (_, value) => { SetLowByteOf(ref receiveBufferStart, (byte)value); currentReceiveWritePointer = receiveBufferStart; } ) },
85 
86                 // ERXSTH
87                 { 0x09, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(receiveBufferStart),
88                                                               writeCallback: (_, value) => { SetHighByteOf(ref receiveBufferStart, (byte)value); currentReceiveWritePointer = receiveBufferStart; } ) },
89 
90                 // ERXNDL
91                 { 0x0A, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(receiveBufferEnd),
92                                                               writeCallback: (_, value) => SetLowByteOf(ref receiveBufferEnd, (byte)value)) },
93 
94                 // ERXNDH
95                 { 0x0B, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(receiveBufferEnd),
96                                                               writeCallback: (_, value) => SetHighByteOf(ref receiveBufferEnd, (byte)value)) },
97 
98                 // ERXRDPTL
99                 { 0x0C, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => GetLowByteOf(receiveReadPointer),
100                                                               writeCallback: (_, value) =>  bufferedLowByteOfReceiveReadPointer = (byte)value) },
101 
102                 // ERXRDPTH
103                 { 0x0D, new ByteRegister(this).WithValueField(0, 5, valueProviderCallback: _ => GetHighByteOf(receiveReadPointer),
104                                                               writeCallback: (_, value) => receiveReadPointer = (int)(bufferedLowByteOfReceiveReadPointer | ((value << 8)))) }
105             };
106 
107             var bank1Map = new Dictionary<long, ByteRegister>
108             {
109                 // ERXFCON
110                 { 0x18, new ByteRegister(this, 0xA1).WithFlag(5, out crcEnabled, name: "CRCEN") },
111 
112                 // EPKTCNT
113                 { 0x19, new ByteRegister(this).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (uint)waitingPacketCount) }
114             };
115 
116             var bank2Map = new Dictionary<long, ByteRegister>
117             {
118                 // MACON1
119                 // note that we currently ignore all the Pause Control Frame stuff
120                 { 0x00, new ByteRegister(this).WithFlag(0, out macReceiveEnabled, name: "MARXEN").WithFlag(2,  name: "RXPAUS").WithFlag(3, name: "TXPAUS")},
121 
122                 // MACON3
123                 { 0x02, new ByteRegister(this).WithFlag(0, name: "FULDPX") },
124 
125                 // MABBIPG (too low level parameter for emulation)
126                 { 0x04, new ByteRegister(this).WithValueField(0, 7) },
127 
128                 // MAIPGL (same as above)
129                 { 0x06, new ByteRegister(this).WithValueField(0, 7) },
130 
131                 // MAIPGH (same as above)
132                 { 0x07, new ByteRegister(this).WithValueField(0, 7) },
133 
134                 // MICMD
135                 { 0x12, new ByteRegister(this).WithFlag(0, writeCallback: (_, value) => { if(value) ReadPhyRegister(); }, name: "MIIRD") },
136 
137                 // MIREGADR
138                 { 0x14, new ByteRegister(this).WithValueField(0, 5, out miiRegisterAddress) },
139 
140                 // MIWRL
141                 { 0x16, new ByteRegister(this).WithValueField(0, 8, out phyWriteLow) },
142 
143                 // MIWRH
144                 { 0x17, new ByteRegister(this).WithValueField(0, 8, writeCallback: (_, value) => WritePhyRegister((ushort)(phyWriteLow.Value | (value << 8)))) },
145 
146                 // MIRDL
147                 { 0x18, new ByteRegister(this).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (byte)lastReadPhyRegisterValue) },
148 
149                 // MIRDH
150                 { 0x19, new ByteRegister(this).WithValueField(0, 8, FieldMode.Read, valueProviderCallback: _ => (byte)(lastReadPhyRegisterValue >> 8)) }
151             };
152 
153             var bank3Map = new Dictionary<long, ByteRegister>
154             {
155                 // MAADR5
156                 { 0x00, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.E, writeCallback: (_, value) => MAC = MAC.WithNewOctets(e: (byte)value)) },
157 
158                 // MADDR6
159                 { 0x01, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.F, writeCallback: (_, value) => MAC = MAC.WithNewOctets(f: (byte)value)) },
160 
161                 // MADDR3
162                 { 0x02, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.C, writeCallback: (_, value) => MAC = MAC.WithNewOctets(c: (byte)value)) },
163 
164                 // MADDR4
165                 { 0x03, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.D, writeCallback: (_, value) => MAC = MAC.WithNewOctets(d: (byte)value)) },
166 
167                 // MADDR1
168                 { 0x04, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.A, writeCallback: (_, value) => MAC = MAC.WithNewOctets(a: (byte)value)) },
169 
170                 // MADDR2
171                 { 0x05, new ByteRegister(this).WithValueField(0, 8, valueProviderCallback: _ => MAC.B, writeCallback: (_, value) => MAC = MAC.WithNewOctets(b: (byte)value)) },
172 
173                 // MISTAT
174                 { 0x0A, new ByteRegister(this).WithFlag(0, FieldMode.Read, name: "BUSY") } // we're never busy
175             };
176 
177             var maps = new[] { bank0Map, bank1Map, bank2Map, bank3Map };
178             // registers below are available in all banks
179             foreach(var map in maps)
180             {
181                 map.Add(0x1B, eie); // EIE
182                 map.Add(0x1C, eir); // EIR
183                 map.Add(0x1D, estat); // ESTAT
184                 map.Add(0x1E, econ2); // ECON2
185                 map.Add(0x1F, econ1); // ECON1
186             }
187             registers = maps.Select(x => new ByteRegisterCollection(this, x)).ToArray();
188 
189             ethernetBuffer = new byte[8.KB()];
190 
191             phyRegisters = new WordRegisterCollection(this, new Dictionary<long, WordRegister>
192             {
193                 // PHCON1
194                 { 0x00, new WordRegister(this).WithFlag(8, name: "PDPXMD") }, // full duplex stuff, ignored
195 
196                 // PHCON2
197                 { 0x10, new WordRegister(this) }
198             });
199             IRQ = new GPIO();
200             IRQ.Set(); // the interrupt output is negated
201         }
202 
203         public event Action<EthernetFrame> FrameReady;
204 
205         public MACAddress MAC { get; set; }
206 
ReceiveFrame(EthernetFrame frame)207         public void ReceiveFrame(EthernetFrame frame)
208         {
209             lock(sync)
210             {
211                 if(!macReceiveEnabled.Value || !ethernetReceiveEnabled.Value)
212                 {
213                     return;
214                 }
215                 if(!TryReceivePacket(frame.Bytes))
216                 {
217                     this.Log(LogLevel.Info, "Packet ignored.");
218                 }
219             }
220         }
221 
Reset()222         public void Reset()
223         {
224             lock(sync)
225             {
226                 ResetPointers();
227                 foreach(var registerCollection in registers)
228                 {
229                     registerCollection.Reset();
230                 }
231                 phyRegisters.Reset();
232                 waitingPacketCount = 0;
233                 currentMode = Mode.Normal;
234                 RefreshInterruptStatus();
235             }
236         }
237 
Transmit(byte data)238         public byte Transmit(byte data)
239         {
240             lock(sync)
241             {
242                 switch(currentMode)
243                 {
244                 case Mode.Normal:
245                     return HandleTransmissionInNormalMode(data);
246                 case Mode.ReadMacOrMiiRegister:
247                     currentMode = Mode.ReadControlRegister;
248                     return 0x00; // dummy byte
249                 case Mode.ReadControlRegister:
250                     return HandleReadRegister();
251                 case Mode.WriteControlRegister:
252                     HandleWriteRegister(data);
253                     return 0x00;
254                 case Mode.RegisterBitSet:
255                     HandleBitSetOrClear(data, true);
256                     return 0x00;
257                 case Mode.RegisterBitClear:
258                     HandleBitSetOrClear(data, false);
259                     return 0x00;
260                 case Mode.ReadBufferMemory:
261                     return HandleReadBufferMemory();
262                 case Mode.WriteBufferMemory:
263                     HandleWriteBufferMemory(data);
264                     return 0x00;
265                 default:
266                     throw new InvalidOperationException("Internal error: unexpected mode.");
267                 }
268             }
269         }
270 
FinishTransmission()271         public void FinishTransmission()
272         {
273             lock(sync)
274             {
275                 currentMode = Mode.Normal;
276             }
277         }
278 
279         public GPIO IRQ { get; private set; }
280 
ResetPointers()281         private void ResetPointers()
282         {
283             transmitBufferStart = 0;
284             transmitBufferEnd = 0;
285             receiveBufferStart = 0x5FA;
286             receiveBufferEnd = 0x1FFF;
287             bufferReadPointer = 0x5FA;
288             bufferWritePointer = 0;
289             receiveReadPointer = 0x5FA;
290         }
291 
HandleTransmissionInNormalMode(byte data)292         private byte HandleTransmissionInNormalMode(byte data)
293         {
294             if(data == 0xFF)
295             {
296                 // soft reset
297                 return 0x00;
298             }
299             var commandType = data >> 5;
300             selectedRegister = data & 0x1F;
301             switch(commandType)
302             {
303                 case 0:
304                     currentMode = MacOrMiiRegisters.Contains(Tuple.Create((int)currentBank.Value, selectedRegister))
305                         ? Mode.ReadMacOrMiiRegister : Mode.ReadControlRegister;
306                     break;
307                 case 1:
308                     currentMode = Mode.ReadBufferMemory;
309                     break;
310                 case 2:
311                     currentMode = Mode.WriteControlRegister;
312                     break;
313                 case 3:
314                     currentMode = Mode.WriteBufferMemory;
315                     break;
316                 case 4:
317                     currentMode = Mode.RegisterBitSet;
318                     break;
319                 case 5:
320                     currentMode = Mode.RegisterBitClear;
321                     break;
322                 default:
323                     this.Log(LogLevel.Error, "Unhandled command type: {0}.", commandType);
324                     break;
325             }
326             return 0x00;
327         }
328 
HandleReadRegister()329         private byte HandleReadRegister()
330         {
331             var registerDetails = string.Format("0x{0:X}, bank {1}", selectedRegister, currentBank.Value);
332             this.Log(LogLevel.Debug, "Read from {0}.", registerDetails);
333             var result = default(byte);
334             if(!GetCurrentRegistersBank().TryRead(selectedRegister, out result))
335             {
336                 this.Log(LogLevel.Warning, "Read from unhandled register {0}.", registerDetails);
337             }
338             currentMode = Mode.Normal;
339             return result;
340         }
341 
HandleWriteRegister(byte data)342         private void HandleWriteRegister(byte data)
343         {
344             var registerDetails = string.Format("0x{0:X}, bank {1}, value 0x{2:X}", selectedRegister, currentBank.Value, data);
345             this.Log(LogLevel.Debug, "Write to {0}.", registerDetails);
346             if(!GetCurrentRegistersBank().TryWrite(selectedRegister, data))
347             {
348                 this.Log(LogLevel.Warning, "Write to unhandled register {0}.", registerDetails);
349             }
350             currentMode = Mode.Normal;
351         }
352 
HandleReadBufferMemory()353         private byte HandleReadBufferMemory()
354         {
355             this.Log(LogLevel.Debug, "Reading buffer memory at 0x{0:X}.", bufferReadPointer);
356             var result = ethernetBuffer[bufferReadPointer];
357             if(autoIncrement.Value)
358             {
359                 bufferReadPointer++;
360                 if(bufferReadPointer > receiveBufferEnd)
361                 {
362                     bufferReadPointer = receiveBufferStart;
363                 }
364             }
365             return result;
366         }
367 
HandleWriteBufferMemory(byte value)368         private void HandleWriteBufferMemory(byte value)
369         {
370             this.Log(LogLevel.Debug, "Writing buffer memory at 0x{0:X}, value 0x{1:X}.", bufferWritePointer, value);
371             ethernetBuffer[bufferWritePointer] = value;
372             if(autoIncrement.Value)
373             {
374                 bufferWritePointer++;
375                 if(bufferWritePointer == ethernetBuffer.Length)
376                 {
377                     bufferWritePointer = 0;
378                 }
379             }
380         }
381 
HandleBitSetOrClear(byte data, bool set)382         private void HandleBitSetOrClear(byte data, bool set)
383         {
384             var registerDetails = string.Format("0x{0:X}, bank {1}, bits to {3}: {2}", selectedRegister, currentBank.Value,
385                                                 BitHelper.GetSetBitsPretty(data), set ? "set" : "clear");
386             this.Log(LogLevel.Debug, "Bit{1} to {0}.", registerDetails, set ? "Set" : "Clear");
387             currentMode = Mode.Normal;
388             if(MacOrMiiRegisters.Contains(Tuple.Create((int)currentBank.Value, selectedRegister)))
389             {
390                 this.Log(LogLevel.Warning, "Trying to set a bit on MAC or MII register {0}.", registerDetails);
391                 return;
392             }
393             byte value;
394             if(!GetCurrentRegistersBank().TryRead(selectedRegister, out value))
395             {
396                 this.Log(LogLevel.Warning, "BitSet to unimplemented register {0}.", registerDetails);
397                 return;
398             }
399             if(set)
400             {
401                 value |= data;
402             }
403             else
404             {
405                 value &= (byte)~data;
406             }
407             GetCurrentRegistersBank().Write(selectedRegister, value);
408         }
409 
GetCurrentRegistersBank()410         private ByteRegisterCollection GetCurrentRegistersBank()
411         {
412             return registers[(int)currentBank.Value];
413         }
414 
ReadPhyRegister()415         private void ReadPhyRegister()
416         {
417             ushort result = 0;
418             var address = miiRegisterAddress.Value;
419             this.DebugLog("Read from PHY register 0x{0:X}.", address);
420             if(!phyRegisters.TryRead((long)address, out result))
421             {
422                 this.Log(LogLevel.Warning, "Read from unimplemented PHY register 0x{0:X}.", address);
423             }
424             lastReadPhyRegisterValue = result;
425         }
426 
WritePhyRegister(ushort value)427         private void WritePhyRegister(ushort value)
428         {
429             var address = miiRegisterAddress.Value;
430             var registerFriendlyName = string.Format("PHY register 0x{0:X}, value 0x{1:X}", address, value);
431             this.DebugLog("Write to {0}.", registerFriendlyName);
432             if(!phyRegisters.TryWrite((long)address, value))
433             {
434                 this.Log(LogLevel.Warning, "Write to unimplemented {0}.", registerFriendlyName);
435             }
436         }
437 
SetInterrupt(bool value)438         private void SetInterrupt(bool value)
439         {
440             IRQ.Set(!value);
441         }
442 
RefreshInterruptStatus()443         private void RefreshInterruptStatus()
444         {
445             SetInterrupt(interruptsEnabled.Value &&
446                          (IsReceiveInterruptActive() || (transmitPacketInterrupt.Value && transmitPacketInterruptEnabled.Value)));
447         }
448 
IsReceiveInterruptActive()449         private bool IsReceiveInterruptActive()
450         {
451             return waitingPacketCount > 0 && receivePacketInterruptEnabled.Value;
452         }
453 
TryReceivePacket(byte[] data)454         private bool TryReceivePacket(byte[] data)
455         {
456             // first check whether the packet fits into buffer
457             var receiveBufferSize = receiveBufferEnd - receiveBufferStart + 1;
458             var freeSpace = receiveBufferSize - ((receiveBufferSize + currentReceiveWritePointer - receiveReadPointer) % receiveBufferSize);
459             var packetPlusHeadersLength = data.Length + 2 + 4; // next packet pointer and receive status vector
460             packetPlusHeadersLength += packetPlusHeadersLength % 2; // padding byte
461             if(freeSpace < packetPlusHeadersLength)
462             {
463                 this.Log(LogLevel.Warning, "No free space for packet. Packet length (+ headers): {0}B, free space: {1}B.",
464                          Misc.NormalizeBinary(packetPlusHeadersLength), Misc.NormalizeBinary(freeSpace));
465                 return false;
466             }
467 
468             if(!EthernetFrame.CheckCRC(data) && crcEnabled.Value)
469             {
470                 this.Log(LogLevel.Info, "Invalid CRC, packet discarded");
471                 return false;
472             }
473 
474             var nextReceiveWritePointer = currentReceiveWritePointer + packetPlusHeadersLength;
475             if(nextReceiveWritePointer > receiveBufferEnd)
476             {
477                 nextReceiveWritePointer -= receiveBufferSize;
478             }
479             var packetWithHeader = new byte[packetPlusHeadersLength];
480             BitConverter.GetBytes((ushort)nextReceiveWritePointer).CopyTo(packetWithHeader, 0);
481             BitConverter.GetBytes((ushort)data.Length).CopyTo(packetWithHeader, 2);
482             BitConverter.GetBytes((ushort)(1 << 7)).CopyTo(packetWithHeader, 4);
483             data.CopyTo(packetWithHeader, 6);
484 
485             var firstPartLength = Math.Min(packetPlusHeadersLength, receiveBufferEnd - currentReceiveWritePointer + 1);
486             Array.Copy(packetWithHeader, 0, ethernetBuffer, currentReceiveWritePointer, firstPartLength);
487             if(firstPartLength < packetPlusHeadersLength)
488             {
489                 // packet overlaps buffer
490                 Array.Copy(packetWithHeader, firstPartLength, ethernetBuffer, receiveBufferStart, packetWithHeader.Length - firstPartLength);
491             }
492             currentReceiveWritePointer = nextReceiveWritePointer;
493 
494             waitingPacketCount++;
495             RefreshInterruptStatus();
496             return true;
497         }
498 
TransmitPacket()499         private void TransmitPacket()
500         {
501             var packetSize = transmitBufferEnd - transmitBufferStart; // -1 for the per packet control byte, but transmitBufferEnd points to the last byte of the packet
502             var data = new byte[packetSize];
503             Array.Copy(ethernetBuffer, transmitBufferStart + 1, data, 0, packetSize);
504             if(!Misc.TryCreateFrameOrLogWarning(this, data, out var frame, addCrc: true))
505             {
506                 return;
507             }
508             // status vector is not implemented yet
509             this.Log(LogLevel.Debug, "Sending frame {0}.", frame);
510             FrameReady?.Invoke(frame);
511             transmitPacketInterrupt.Value = true;
512             RefreshInterruptStatus();
513         }
514 
SetLowByteOf(ref int ofWhat, byte with)515         private static void SetLowByteOf(ref int ofWhat, byte with)
516         {
517             ofWhat = (ofWhat & 0xFF00) | with;
518         }
519 
SetHighByteOf(ref int ofWhat, byte with)520         private static void SetHighByteOf(ref int ofWhat, byte with)
521         {
522             ofWhat = (with << 8) | (ofWhat & 0xFF);
523         }
524 
525         // methods below return uint because of the register infrastructure (it is more convenient)
GetLowByteOf(int ofWhat)526         private static uint GetLowByteOf(int ofWhat)
527         {
528             return (uint)(ofWhat & 0xFF);
529         }
530 
GetHighByteOf(int ofWhat)531         private static uint GetHighByteOf(int ofWhat)
532         {
533             return (uint)((ofWhat >> 8) & 0x1F);
534         }
535 
536         private Mode currentMode;
537         private IValueRegisterField currentBank;
538         private int selectedRegister;
539         private int receiveBufferStart;
540         private int receiveBufferEnd;
541         private int transmitBufferStart;
542         private int transmitBufferEnd;
543         private int bufferReadPointer;
544         private int bufferWritePointer;
545         private byte bufferedLowByteOfReceiveReadPointer;
546         private int receiveReadPointer;
547         private int currentReceiveWritePointer;
548         private IFlagRegisterField macReceiveEnabled;
549         private IValueRegisterField miiRegisterAddress;
550         private ushort lastReadPhyRegisterValue;
551         private IValueRegisterField phyWriteLow;
552         private IFlagRegisterField interruptsEnabled;
553         private IFlagRegisterField receivePacketInterruptEnabled;
554         private IFlagRegisterField transmitPacketInterruptEnabled;
555         private IFlagRegisterField transmitPacketInterrupt;
556         private IFlagRegisterField ethernetReceiveEnabled;
557         private IFlagRegisterField autoIncrement;
558         private int waitingPacketCount;
559         private IFlagRegisterField crcEnabled;
560 
561         private readonly WordRegisterCollection phyRegisters;
562         private readonly ByteRegisterCollection[] registers;
563         private readonly byte[] ethernetBuffer;
564         private readonly object sync;
565 
566         // here is the list of MAC and MII registers in the format (bank, register_number)
567         // they have to be read in a slightly different way than ethernet registers
568         private static readonly HashSet<Tuple<int, int>> MacOrMiiRegisters = new HashSet<Tuple<int, int>>
569         {
570             Tuple.Create(2, 0x00),
571             Tuple.Create(2, 0x02),
572             Tuple.Create(2, 0x04),
573             Tuple.Create(2, 0x06),
574             Tuple.Create(2, 0x07),
575             Tuple.Create(2, 0x12),
576             Tuple.Create(2, 0x14),
577             Tuple.Create(2, 0x16),
578             Tuple.Create(2, 0x17),
579             Tuple.Create(2, 0x18),
580             Tuple.Create(2, 0x19),
581             Tuple.Create(3, 0x00),
582             Tuple.Create(3, 0x01),
583             Tuple.Create(3, 0x02),
584             Tuple.Create(3, 0x03),
585             Tuple.Create(3, 0x04),
586             Tuple.Create(3, 0x05),
587             Tuple.Create(3, 0x0A)
588         };
589 
590         private enum Mode
591         {
592             Normal,
593             ReadBufferMemory,
594             WriteBufferMemory,
595             ReadControlRegister,
596             ReadMacOrMiiRegister,
597             WriteControlRegister,
598             RegisterBitSet,
599             RegisterBitClear
600         }
601     }
602 }
603