/*
JPC: An x86 PC Hardware Emulator for a pure Java Virtual Machine
Release Version 2.4
A project from the Physics Dept, The University of Oxford
Copyright (C) 2007-2010 The University of Oxford
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License version 2 as published by
the Free Software Foundation.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
Details (including contact information) can be found at:
jpc.sourceforge.net
or the developer website
sourceforge.net/projects/jpc/
Conceived and Developed by:
Rhys Newman, Ian Preston, Chris Dennis
End of licence header
*/
package org.jpc.emulator.pci.peripheral;
import org.jpc.emulator.pci.*;
import org.jpc.emulator.motherboard.IOPortHandler;
import org.jpc.emulator.AbstractHardwareComponent;
import java.io.*;
import java.net.*;
import java.util.Random;
import java.util.logging.*;
import org.jpc.support.*;
/** Realtek 8029 (AS) Emulation
*
* based on the Bochs ne2000 emulation
* @author Chris Dennis
* @author Ian Preston
*/
public class EthernetCard extends AbstractPCIDevice
{
private static final Logger LOGGING = Logger.getLogger(EthernetCard.class.getName());
private static final int IRQ = 9;
//Static Device Constants
private static final int MAX_ETH_FRAME_SIZE = 1514;
private static final int E8390_CMD = 0x00; // The command register (for all pages) */
/* Page 0 register offsets. */
private static final int EN0_CLDALO = 0x01; // Low byte of current local dma addr RD */
private static final int EN0_STARTPG = 0x01; // Starting page of ring bfr WR */
private static final int EN0_CLDAHI = 0x02; // High byte of current local dma addr RD */
private static final int EN0_STOPPG = 0x02; // Ending page +1 of ring bfr WR */
private static final int EN0_BOUNDARY = 0x03; // Boundary page of ring bfr RD WR */
private static final int EN0_TSR = 0x04; // Transmit status reg RD */
private static final int EN0_TPSR = 0x04; // Transmit starting page WR */
private static final int EN0_NCR = 0x05; // Number of collision reg RD */
private static final int EN0_TCNTLO = 0x05; // Low byte of tx byte count WR */
private static final int EN0_FIFO = 0x06; // FIFO RD */
private static final int EN0_TCNTHI = 0x06; // High byte of tx byte count WR */
private static final int EN0_ISR = 0x07; // Interrupt status reg RD WR */
private static final int EN0_CRDALO = 0x08; // low byte of current remote dma address RD */
private static final int EN0_RSARLO = 0x08; // Remote start address reg 0 */
private static final int EN0_CRDAHI = 0x09; // high byte, current remote dma address RD */
private static final int EN0_RSARHI = 0x09; // Remote start address reg 1 */
private static final int EN0_ID0 = 0x0a; //RTL 8029 ID0
private static final int EN0_ID1 = 0x0b; //RTL 8029 ID1
private static final int EN0_RCNTLO = 0x0a; // Remote byte count reg WR */
private static final int EN0_RCNTHI = 0x0b; // Remote byte count reg WR */
private static final int EN0_RSR = 0x0c; // rx status reg RD */
private static final int EN0_RXCR = 0x0c; // RX configuration reg WR */
private static final int EN0_TXCR = 0x0d; // TX configuration reg WR */
private static final int EN0_COUNTER0 = 0x0d; // Rcv alignment error counter RD */
private static final int EN0_DCFG = 0x0e; // Data configuration reg WR */
private static final int EN0_COUNTER1 = 0x0e; // Rcv CRC error counter RD */
private static final int EN0_IMR = 0x0f; // Interrupt mask reg WR */
private static final int EN0_COUNTER2 = 0x0f; // Rcv missed frame error counter RD */
private static final int EN1_PHYS = 0x11;
private static final int EN1_CURPAG = 0x17;
private static final int EN1_MULT = 0x18;
/* Register accessed at EN_CMD, the 8390 base addr. */
private static final byte E8390_STOP = (byte) 0x01; // Stop and reset the chip */
private static final byte E8390_START = (byte) 0x02; // Start the chip, clear reset */
private static final byte E8390_TRANS = (byte) 0x04; // Transmit a frame */
private static final byte E8390_RREAD = (byte) 0x08; // Remote read */
private static final byte E8390_RWRITE = (byte) 0x10; // Remote write */
private static final byte E8390_NODMA = (byte) 0x20; // Remote DMA */
private static final byte E8390_PAGE0 = (byte) 0x00; // Select page chip registers */
private static final byte E8390_PAGE1 = (byte) 0x40; // using the two high-order bits */
private static final byte E8390_PAGE2 = (byte) 0x80; // Page 3 is invalid. */
/* Bits in EN0_ISR - Interrupt status register */
private static final byte ENISR_RX = (byte) 0x01; // Receiver, no error */
private static final byte ENISR_TX = (byte) 0x02; // Transmitter, no error */
private static final byte ENISR_RX_ERR = (byte) 0x04; // Receiver, with error */
private static final byte ENISR_TX_ERR = (byte) 0x08; // Transmitter, with error */
private static final byte ENISR_OVER = (byte) 0x10; // Receiver overwrote the ring */
private static final byte ENISR_COUNTERS = (byte) 0x20; // Counters need emptying */
private static final byte ENISR_RDC = (byte) 0x40; // remote dma complete */
private static final byte ENISR_RESET = (byte) 0x80; // Reset completed */
private static final byte ENISR_ALL = (byte) 0x3f; // Interrupts we will enable */
/* Bits in received packet status byte and EN0_RSR*/
private static final byte ENRSR_RXOK = (byte) 0x01; // Received a good packet */
private static final byte ENRSR_CRC = (byte) 0x02; // CRC error */
private static final byte ENRSR_FAE = (byte) 0x04; // frame alignment error */
private static final byte ENRSR_FO = (byte) 0x08; // FIFO overrun */
private static final byte ENRSR_MPA = (byte) 0x10; // missed pkt */
private static final byte ENRSR_PHY = (byte) 0x20; // physical/multicast address */
private static final byte ENRSR_DIS = (byte) 0x40; // receiver disable. set in monitor mode */
private static final byte ENRSR_DEF = (byte) 0x80; // deferring */
/* Transmitted packet status, EN0_TSR. */
private static final byte ENTSR_PTX = (byte) 0x01; // Packet transmitted without error */
private static final byte ENTSR_ND = (byte) 0x02; // The transmit wasn't deferred. */
private static final byte ENTSR_COL = (byte) 0x04; // The transmit collided at least once. */
private static final byte ENTSR_ABT = (byte) 0x08; // The transmit collided 16 times, and was deferred. */
private static final byte ENTSR_CRS = (byte) 0x10; // The carrier sense was lost. */
private static final byte ENTSR_FU = (byte) 0x20; // A "FIFO underrun" occurred during transmit. */
private static final byte ENTSR_CDH = (byte) 0x40; // The collision detect "heartbeat" signal was lost. */
private static final byte ENTSR_OWC = (byte) 0x80; // There was an out-of-window collision. */
private static final int NE2000_PMEM_SIZE = (32 * 1024);
private static final int NE2000_PMEM_START = (16 * 1024);
private static final int NE2000_PMEM_END = (NE2000_PMEM_SIZE + NE2000_PMEM_START);
private static final int NE2000_MEM_SIZE = NE2000_PMEM_END;
//Instance (State) Properties
private byte command;
private int start;
private int stop;
private byte boundary;
private byte tsr;
private byte tpsr;
private byte txcr;
private short tcnt;
private short rcnt;
private int rsar;
private volatile int rxcr;
private byte rsr;
private volatile byte isr;
private byte dcfg;
private volatile byte imr;
private byte phys[]; /* mac address */
private byte curpag;
private byte mult[]; /* multicast mask array */
//public volatile int ethPacketsWaiting = 0;
EthernetOutput outputDevice;
private byte[] memory;
private EthernetIORegion ioRegion;
public EthernetCard()
{
this(null);
}
public EthernetCard(EthernetOutput output)
{
setIRQIndex(IRQ);
putConfigWord(PCI_CONFIG_VENDOR_ID, (short) 0x10ec); // Realtek
putConfigWord(PCI_CONFIG_DEVICE_ID, (short) 0x8029); // 8029
putConfigWord(PCI_CONFIG_CLASS_DEVICE, (short) 0x0200); // ethernet network controller
putConfigByte(PCI_CONFIG_HEADER, (byte) 0x00); // header_type
putConfigByte(PCI_CONFIG_INTERRUPT_PIN, (byte) 0x01); // interrupt pin 0
ioRegion = new EthernetIORegion();
outputDevice = output;
if (outputDevice == null)
outputDevice = new EthernetProxy();
memory = new byte[NE2000_MEM_SIZE];
phys = new byte[6];
mult = new byte[8];
//generate random MAC address
Random random = new Random();
random.nextBytes(phys);
phys[0] = (byte) 0x4a;
phys[1] = (byte) 0x50;
phys[2] = (byte) 0x43;
System.arraycopy(phys, 0, memory, 0, phys.length);
internalReset();
}
public void saveState(DataOutput output) throws IOException
{
output.writeByte(command);
output.writeInt(start);
output.writeInt(stop);
output.writeByte(boundary);
output.writeByte(tsr);
output.writeByte(tpsr);
output.writeShort(tcnt);
output.writeShort(rcnt);
output.writeInt(rsar);
output.writeByte(rsr);
output.writeByte(isr);
output.writeByte(dcfg);
output.writeByte(imr);
output.writeInt(phys.length);
output.write(phys);
output.writeByte(curpag);
output.writeInt(mult.length);
output.write(mult);
output.writeInt(memory.length);
output.write(memory);
ioRegion.saveState(output);
//dump output device
//let's ignore it for now
}
public void loadState(DataInput input) throws IOException
{
command = input.readByte();
start = input.readInt();
stop = input.readInt();
boundary = input.readByte();
tsr = input.readByte();
tpsr = input.readByte();
tcnt = input.readShort();
rcnt = input.readShort();
rsar = input.readInt();
rsr = input.readByte();
isr = input.readByte();
dcfg = input.readByte();
imr = input.readByte();
int len = input.readInt();
phys = new byte[len];
input.readFully(phys, 0, len);
curpag = input.readByte();
len = input.readInt();
mult = new byte[len];
input.readFully(mult, 0, len);
len = input.readInt();
memory = new byte[len];
input.readFully(memory, 0, len);
ioRegion.loadState(input);
//load output device
//apparently this is another whole kettle of fish... so let's ignore it
}
public void checkForPackets() {
receivePacket(outputDevice.getPacket());
}
public void setOutputDevice(EthernetOutput out) {
this.outputDevice = out;
}
public void loadIOPorts(IOPortHandler ioportHandler, DataInput input) throws IOException
{
loadState(input);
ioportHandler.registerIOPortCapable(ioRegion);
}
public void reset()
{
putConfigWord(PCI_CONFIG_VENDOR_ID, (short) 0x10ec); // Realtek
putConfigWord(PCI_CONFIG_DEVICE_ID, (short) 0x8029); // 8029
putConfigWord(PCI_CONFIG_CLASS_DEVICE, (short) 0x0200); // ethernet network controller
putConfigByte(PCI_CONFIG_HEADER, (byte) 0x00); // header_type
putConfigByte(PCI_CONFIG_INTERRUPT_PIN, (byte) 0x01); // interrupt pin 0
memory = new byte[NE2000_MEM_SIZE];
// phys = new byte[6];
// mult = new byte[8];
internalReset();
super.reset();
}
private void internalReset()
{
isr = ENISR_RESET;
memory[0x0e] = (byte) 0x57;
memory[0x0f] = (byte) 0x57;
System.arraycopy(phys, 0, memory, 0, phys.length);
for (int i = 15; i >= 0; i--)
{
memory[2 * i] = memory[i];
memory[2 * i + 1] = memory[i];
}
}
private void updateIRQ()
{
int interruptService = isr & imr;
if (interruptService != 0) {
this.getIRQBouncer().setIRQ(this, IRQ, 1);
}
else
this.getIRQBouncer().setIRQ(this, IRQ, 0);
}
private int canReceive()
{
if (command == E8390_STOP)
return 1;
return 0;
}
//PCIDevice Methods
//IOPort Registration Aids
public IORegion[] getIORegions()
{
return new IORegion[]
{
ioRegion
};
}
public IORegion getIORegion(int index)
{
if (index == 0)
return ioRegion;
else
return null;
}
class EthernetIORegion extends AbstractHardwareComponent implements IOPortIORegion
{
private int address;
public EthernetIORegion()
{
address = -1;
}
public void saveState(DataOutput output) throws IOException
{
output.writeInt(address);
}
public void loadState(DataInput input) throws IOException
{
address = input.readInt();
}
//IORegion Methods
public int getAddress()
{
return address;
}
public long getSize()
{
return 0x100;
}
public int getType()
{
return PCI_ADDRESS_SPACE_IO;
}
public int getRegionNumber()
{
return 0;
}
public void setAddress(int address)
{
this.address = address;
LOGGING.log(Level.FINE, "Ethernet IO address is "+Integer.toHexString(address));
}
//IODevice Methods
public void ioPortWrite8(int address, int data)
{
switch (address - this.getAddress())
{
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
EthernetCard.this.ioPortWrite(address, (byte) data);
break;
case 0x10:
// May do a 16 bit write, so must only narrow to short
EthernetCard.this.asicIOPortWriteByte(address, (short) data);
break;
case 0x1f:
//this.resetIOPortWrite(address); //end of reset pulse
break;
default: //this is invalid, but happens under win 95 device detection
break;
}
}
public void ioPortWrite16(int address, int data)
{
switch (address - this.getAddress())
{
case 0x10:
case 0x11:
EthernetCard.this.asicIOPortWriteWord(address, (short) data);
break;
default:
// should do two byte access
break;
}
}
public void ioPortWrite32(int address, int data)
{
switch (address - this.getAddress())
{
case 0x10:
case 0x11:
case 0x12:
case 0x13:
EthernetCard.this.asicIOPortWriteLong(address, data);
break;
default:
break;
}
}
public int ioPortRead8(int address)
{
switch (address - this.getAddress())
{
case 0x00:
case 0x01:
case 0x02:
case 0x03:
case 0x04:
case 0x05:
case 0x06:
case 0x07:
case 0x08:
case 0x09:
case 0x0a:
case 0x0b:
case 0x0c:
case 0x0d:
case 0x0e:
case 0x0f:
return EthernetCard.this.ioPortRead(address);
case 0x10:
return 0xffff & EthernetCard.this.asicIOPortReadByte(address);
case 0x1f:
return EthernetCard.this.resetIOPortRead(address);
default:
return (byte) 0xff;
}
}
public int ioPortRead16(int address)
{
switch (address - this.getAddress())
{
case 0x10:
case 0x11:
return EthernetCard.this.asicIOPortReadWord(address);
default:
return (short) 0xffff; //should do two byte access
}
}
public int ioPortRead32(int address)
{
switch (address - this.getAddress())
{
case 0x10:
case 0x11:
case 0x12:
case 0x13:
return EthernetCard.this.asicIOPortReadLong(address);
default:
return 0xffffffff;
}
}
public int[] ioPortsRequested()
{
int addr = this.getAddress();
int[] temp = new int[32];
for (int i = 0; i < 32; i++)
temp[i] = addr + i;
return temp;
}
}
private void ioPortWrite(int address, byte data)
{
address &= 0xf;
if (address == E8390_CMD)
{
/* control register */
if ((data & 0x38) == 0) {
System.out.println("Invalid DMA command");
data |= 0x20;
}
//test for s/w reset
if ((data & 0x1) != 0) {
isr |= ENISR_RESET;
command |= E8390_STOP;
} else {
command &= ~E8390_STOP;
}
//update remote DMA command
command = (byte) ((data & 0x38) | (command & ~0x38));
if (((command & E8390_START) == 0) && (0 != (data & E8390_START)))
{
isr = (byte) (isr & ~ENISR_RESET);
}
//set start, and page select
command = (byte) ((command & ~0xc2) | (data & 0xc2));
//check for send packet command
if ((command & 0x38) == 0x18) {
//setup dma read from receive ring
rsar = ((short) boundary) << 8;
rcnt = (short) (2);
System.out.println("After send packet command, setting rcnt to " + 2);
}
//check for start tx
if ((0 != (data & E8390_TRANS)) && ((txcr & 0x6) != 0)) {
int loop_control = (txcr & 0x6);
if (loop_control != 2) {
System.out.println("ETH: Loop mode " + (loop_control >> 1) + " not supported.");
} else {
byte[] packet = new byte[tcnt];
System.arraycopy(memory, (tpsr & 0xff ) << 8, packet, 0, tcnt);
receivePacket(packet);
}
} else if (0 != (data & E8390_TRANS)) {
if ((0 != (command & E8390_STOP)) || ((0 == (command & E8390_START)) && (!initialised()))) {
if (tcnt == 0) {
return;
}
throw new IllegalStateException("ETH0: command write, tx start, device in reset");
}
if (tcnt == 0) {
throw new IllegalStateException("ETH0: command - tx start, tx bytes == 0");
}
//now send the packet
command |= 0x4;
int index = ((tpsr & 0xFF) << 8);
outputDevice.sendPacket(memory, index, tcnt);
/* signal end of transfer */
tsr = ENTSR_PTX;
isr = (byte) (isr | ENISR_TX);
this.updateIRQ();
//linux probes for an interrupt by setting up a remote dma read of 0 bytes
//with remote dma completion interrupts enabled
if ((rcnt == 0) && (0 != (command & E8390_START)) && ((command & 0x38) == 0x8)) {
isr = (byte) (isr |ENISR_RDC);
this.updateIRQ();
}
}
// /* test specific case: zero length transfer */
// if ((0 != (data & (E8390_RREAD | E8390_RWRITE))) && (rcnt == 0))
// { // check operators
// isr = (byte) (isr | ENISR_RDC);
// this.updateIRQ();
// }
// if (0 != (data & E8390_TRANS))
// {
// int index = ((tpsr & 0xFF) << 8);
// outputDevice.sendPacket(memory, index, tcnt);
// /* signal end of transfer */
// tsr = ENTSR_PTX;
// isr = (byte) (isr | ENISR_TX);
// this.updateIRQ();
// }
}
else
{
int page = command >> 6;
int offset = address | (page << 4);
switch (offset)
{
case EN0_STARTPG:
start = data & 0xFF;
break;
case EN0_STOPPG:
stop = data & 0xFF;
break;
case EN0_BOUNDARY:
boundary = data;
break;
case EN0_TPSR:
tpsr = data;
break;
case EN0_TCNTLO:
tcnt = (short) ((tcnt & 0xff00) | data);
break;
case EN0_TCNTHI:
tcnt = (short) ((tcnt & 0x00ff) | (((short) data) << 8));
break;
case EN0_ISR:
isr = (byte) (isr & ~(data & 0x7f));
this.updateIRQ();
break;
case EN0_RSARLO:
rsar = ((rsar & 0xff00) | data);
break;
case EN0_RSARHI:
rsar = ((rsar & 0x00ff) | (data << 8));
break;
case EN0_RCNTLO:
rcnt = (short) ((rcnt & 0xff00) | data);
break;
case EN0_RCNTHI:
rcnt = (short) ((rcnt & 0x00ff) | (((short) data) << 8));
break;
case EN0_RXCR:
if ((data & 0xc0) != 0)
System.out.println("ETH: Reserved bits of rxcr set");
rxcr = data;
break;
case EN0_TXCR:
if ((data & 0xe0) != 0)
System.out.println("ETH: Reserved bits of txcr set");
//test loop mode (not supported)
if ((data & 0x6) != 0) {
System.out.println("ETH: Loop mode " + ((data & 0x6) >> 1) + " not supported.");
txcr |= (data & 0x6);
} else {
txcr &= ~0x6;
}
//stop CRC
if ((data & 0x1) != 0)
throw new IllegalStateException("ETH: TCR write - CRC not supported");
//stop auto-transmit disable
if ((data & 0x8) != 0)
throw new IllegalStateException("ETH: TCR write - auto transmit disable not supported");
// should set txcr bit 4 here, but we don't use it
break;
case EN0_DCFG:
dcfg = data;
break;
case EN0_IMR:
imr = data;
this.updateIRQ();
break;
case EN1_PHYS:
case EN1_PHYS + 1:
case EN1_PHYS + 2:
case EN1_PHYS + 3:
case EN1_PHYS + 4:
case EN1_PHYS + 5:
phys[offset - EN1_PHYS] = data;
if (offset == EN1_PHYS + 5)
System.out.println("ETH: MAC address set to: " + Integer.toHexString(phys[0] & 0xFF)
+ Integer.toHexString(phys[1] & 0xFF)
+ Integer.toHexString(phys[2] & 0xFF)
+ Integer.toHexString(phys[3] & 0xFF)
+ Integer.toHexString(phys[4] & 0xFF)
+ Integer.toHexString(phys[5] & 0xFF));
break;
case EN1_CURPAG:
curpag = data;
break;
case EN1_MULT:
case EN1_MULT + 1:
case EN1_MULT + 2:
case EN1_MULT + 3:
case EN1_MULT + 4:
case EN1_MULT + 5:
case EN1_MULT + 6:
case EN1_MULT + 7:
mult[offset - EN1_MULT] = data;
break;
default:
throw new IllegalStateException("ETH: invalid write address: " + Integer.toHexString(address) + "page: " + page);
}
}
}
private void asicIOPortWriteByte(int address, short data)
{
if (rcnt == 0)
return;
if (0 != (dcfg & 0x01))
{
/* 16 bit access */
this.memoryWriteWord(rsar, data);
this.dmaUpdate(2);
}
else
{
/* 8 bit access */
this.memoryWriteByte(rsar, (byte) data);
this.dmaUpdate(1);
}
}
private void asicIOPortWriteWord(int address, short data)
{
if (rcnt == 0)
return;
if (0 != (dcfg & 0x01))
{
/* 16 bit access */
this.memoryWriteWord(rsar, data);
this.dmaUpdate(2);
}
else
{
/* 8 bit access */
this.memoryWriteByte(rsar, (byte) data);
this.dmaUpdate(1);
}
}
private void asicIOPortWriteLong(int address, int data)
{
if (rcnt == 0)
return;
this.memoryWriteLong(rsar, data);
this.dmaUpdate(4);
}
private byte ioPortRead(int address)
{
address &= 0xf;
if (address == E8390_CMD)
return command;
int page = command >> 6;
int offset = address | (page << 4);
switch (offset)
{
case EN0_BOUNDARY:
return boundary;
case EN0_TSR:
return tsr;
case EN0_ISR:
return isr;
case EN0_RSARLO:
return (byte) (rsar & 0x00ff);
case EN0_RSARHI:
return (byte) (rsar >> 8);
case EN0_ID0:
if (initialised())
return (byte) 0x50;
else
return (byte) 0xFF;
case EN0_ID1:
if (initialised())
return (byte) 0x43;
else
return (byte) 0xFF;
case EN0_RSR:
return rsr;
case EN1_PHYS:
case EN1_PHYS + 1:
case EN1_PHYS + 2:
case EN1_PHYS + 3:
case EN1_PHYS + 4:
case EN1_PHYS + 5:
return phys[offset - EN1_PHYS];
case EN1_CURPAG:
return curpag;
case EN1_MULT:
case EN1_MULT + 1:
case EN1_MULT + 2:
case EN1_MULT + 3:
case EN1_MULT + 4:
case EN1_MULT + 5:
case EN1_MULT + 6:
case EN1_MULT + 7:
return mult[offset - EN1_MULT];
default:
return 0x00;
}
}
// this is the high 16 bytes of IO space - the low 16 are for the DS8390
private short asicIOPortReadByte(int address)
{
short ret;
if (0 != (dcfg & 0x01))
{
/* 16 bit access */
ret = this.memoryReadWord(rsar);
this.dmaUpdate(2);
}
else
{
/* 8 bit access */
ret = (short) this.memoryReadByte(rsar);
ret &= 0xff;
this.dmaUpdate(1);
}
return ret;
}
private short asicIOPortReadWord(int address)
{
short ret;
if (0 != (dcfg & 0x01))
{
/* 16 bit access */
ret = this.memoryReadWord(rsar);
this.dmaUpdate(2);
}
else
{
/* 8 bit access */
ret = (short) this.memoryReadByte(rsar);
ret &= 0xff;
this.dmaUpdate(1);
}
return ret;
}
private int asicIOPortReadLong(int address)
{
int ret = this.memoryReadLong(rsar);
this.dmaUpdate(4);
return ret;
}
private byte resetIOPortRead(int address)
{
this.internalReset();
return 0x00;
}
private void dmaUpdate(int length)
{
rsar += length;
if (rsar == stop)
rsar = start;
if (rcnt <= length)
{
rcnt = (short) 0;
/* signal end of transfer */
isr = (byte) (isr | ENISR_RDC);
this.updateIRQ();
}
else
rcnt = (short) (rcnt - length);
}
private void memoryWriteByte(int address, byte data)
{
if (address >= NE2000_PMEM_START && address < NE2000_MEM_SIZE)
memory[address] = data;
else {
System.out.println("Out of bounds ETH chip memory write: " + Integer.toHexString(address));
}
}
private void memoryWriteWord(int address, short data)
{
address &= ~1;
if (address >= NE2000_PMEM_START && address < NE2000_MEM_SIZE)
{
memory[address] = (byte) data;
memory[address + 1] = (byte) (data >> 8);
} else {
System.out.println("Out of bounds ETH chip memory write: " + Integer.toHexString(address));
}
}
private void memoryWriteLong(int address, int data)
{
address &= ~1;
if (address >= NE2000_PMEM_START && address < NE2000_MEM_SIZE)
{
memory[address] = (byte) data;
memory[address + 1] = (byte) (data >> 8);
memory[address + 2] = (byte) (data >> 16);
memory[address + 3] = (byte) (data >> 24);
} else {
System.out.println("Out of bounds ETH chip memory write: " + Integer.toHexString(address));
}
}
private byte memoryReadByte(int address)
{
if (address < 32 || (address >= NE2000_PMEM_START && address < NE2000_MEM_SIZE)) {
return memory[address];
} else {
System.out.println("Out of bounds ETH chip memory read: " + Integer.toHexString(address));
return (byte) 0xff;
}
}
private short memoryReadWord(int address)
{
address &= ~1;
if (address < 32 || (address >= NE2000_PMEM_START && address < NE2000_MEM_SIZE))
{
short val = (short) (0xff & memory[address]);
val |= memory[address + 1] << 8;
return val;
}
else {
System.out.println("Out of bounds ETH chip memory read: " + Integer.toHexString(address));
return (short) 0xffff;
}
}
private int memoryReadLong(int address)
{
address &= ~1;
if (address < 32 || (address >= NE2000_PMEM_START && address < NE2000_MEM_SIZE))
{
int val = (0xff & memory[address]);
val |= (0xff & memory[address + 1]) << 8;
val |= (0xff & memory[address + 2]) << 16;
val |= (0xff & memory[address + 3]) << 24;
return val;
}
else {
System.out.println("Out of bounds ETH chip memory read: " + Integer.toHexString(address));
return 0xffffffff;
}
}
public void testPacket()
{
imr = (byte) 0xff;
isr = (byte) (isr | ENISR_RX);
this.updateIRQ();
}
private int computeCRC(byte[] buf)
{
//based on FreeBSD
int crc, carry, index = 0;
byte b;
int POLYNOMIAL = 0x04c11db6;
crc = 0xFFFFFFFF;
for (int i = 0; i < 6; i++)
{
b = buf[index++];
for (int j = 0; j < 8; j++)
{
carry = (((crc & 0x80000000L) != 0) ? 1 : 0) ^ (b & 0x01);
crc = crc << 1;
b = (byte) (b >>> 1);
if (carry > 0)
crc = ((crc ^ POLYNOMIAL) | carry);
}
}
return crc >>> 26;
}
public static void printPacket(byte[] oldpacket, int offset, int length) {
byte[] packet =new byte[length];
System.arraycopy(oldpacket, offset, packet, 0, length);
for (int j = 0; j< packet.length / 16; j++) {
for (int i=0; i< 16; i++)
System.out.print(Integer.toHexString(packet[16*j + i] & 0xFF));
System.out.println();
}
int remainder = packet.length % 16;
if (remainder != 0) {
for (int i=packet.length-remainder; i< packet.length; i++)
System.out.print(Integer.toHexString(packet[i] & 0xFF));
System.out.println();
}
}
public void receivePacket(byte[] packet)
{
if (packet == null)
return;
int totalLen, index, mcastIdx;
if ((command & E8390_STOP) == 1)
{
System.out.print("ETH told to stop");
return;
}
//check this
if ((rxcr & 0x10) != 0)
{
System.out.println("Receiving packet in prom mode");
//promiscuous: receive all
}
else if ((packet[0] == 0xFF) && (packet[1] == 0xFF) && (packet[2] == 0xFF) && (packet[3] == 0xFF) && (packet[4] == 0xFF) && (packet[5] == 0xFF))
{
//broadcast address
if ((rxcr & 0x04) == 0)
return;
}
else if ((packet[0] & 1) != 0)
{
//multicast
//if ((rxcr & 0x08) == 0)
// return;
//mcastIdx = computeCRC(packet);
//if ((mult[mcastIdx >>> 3] & (1 << (mcastIdx & 7))) == 0)
// return;
}
else if ((memory[0] == packet[0]) && (memory[2] == packet[1]) && (memory[4] == packet[2]) && (memory[6] == packet[3]) && (memory[8] == packet[4]) && (memory[10] == packet[5]))
{
//this is us!
} else {
System.out.println("Weird ETH packet recieved");
printPacket(packet, 0, packet.length);
return;
}
//if buffer is too small expand it!!!!!!!!!!!!
// System.out.println("Packet got through");
index = (curpag & 0xFF) << 8;
//4 bytes for header
totalLen = packet.length + 4;
/* address for next packet (4 bytes for CRC) */
int pages = (totalLen + 4 + 255)/256;
int next = curpag + pages;
// int avail;
//don't emulate partial receives
// if (avail < pages)
// return;
if (next >= stop)
next -= (stop - start);
//prepare packet header
rsr = ENRSR_RXOK; // receive status
//check this
if ((packet[0] & 1) != 0)
rsr |= ENRSR_PHY;
memory[index] = 1;// (was rsr)
if ((packet[0] & 1) != 0)
memory[index] |= 0x20;
memory[index + 1] = (byte) (next);
memory[index + 2] = (byte) totalLen;
memory[index + 3] = (byte) (totalLen >>> 8);
index += 4;
//write packet data
if ((next > curpag) || (curpag + pages == stop))
{
System.arraycopy(packet, 0, memory, index, packet.length);
System.arraycopy(phys, 0, memory, index, 6);
} else
{
int endSize = (stop - curpag) << 8;
System.arraycopy(packet, 0, memory, index, endSize - 4);
int startIndex = start * 256;
System.arraycopy(packet, endSize -4, memory, startIndex, packet.length + 4 - (endSize - 4));
}
curpag = (byte) next;
//signal that we have a packet
isr |= ENISR_RX;
updateIRQ();
}
private class DefaultOutput extends EthernetOutput
{
DataOutputStream dos;
public void sendPacket(byte[] data, int offset, int length)
{
LOGGING.log(Level.FINE, "Sent packet on default output");
try
{
if (length <= 0)
return;
File file = new File("ethernetout.bin");
FileOutputStream fos = new FileOutputStream(file);
dos = new DataOutputStream(fos);
dos.write(data, offset, length);
dos.close();
}
catch (IOException e)
{
LOGGING.log(Level.INFO, "Error sending packet", e);
}
}
public byte[] getPacket() {
return null;
}
}
}