12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349 |
- "use strict";
- // http://www.ethernut.de/pdf/8019asds.pdf
- const NE2K_LOG_VERBOSE = false;
- const NE2K_LOG_PACKETS = false;
- /** @const */ var E8390_CMD = 0x00; /* The command register (for all pages) */
- /* Page 0 register offsets. */
- /** @const */ var EN0_CLDALO = 0x01; /* Low byte of current local dma addr RD */
- /** @const */ var EN0_STARTPG = 0x01; /* Starting page of ring bfr WR */
- /** @const */ var EN0_CLDAHI = 0x02; /* High byte of current local dma addr RD */
- /** @const */ var EN0_STOPPG = 0x02; /* Ending page +1 of ring bfr WR */
- /** @const */ var EN0_BOUNDARY = 0x03; /* Boundary page of ring bfr RD WR */
- /** @const */ var EN0_TSR = 0x04; /* Transmit status reg RD */
- /** @const */ var EN0_TPSR = 0x04; /* Transmit starting page WR */
- /** @const */ var EN0_NCR = 0x05; /* Number of collision reg RD */
- /** @const */ var EN0_TCNTLO = 0x05; /* Low byte of tx byte count WR */
- /** @const */ var EN0_FIFO = 0x06; /* FIFO RD */
- /** @const */ var EN0_TCNTHI = 0x06; /* High byte of tx byte count WR */
- /** @const */ var EN0_ISR = 0x07; /* Interrupt status reg RD WR */
- /** @const */ var EN0_CRDALO = 0x08; /* low byte of current remote dma address RD */
- /** @const */ var EN0_RSARLO = 0x08; /* Remote start address reg 0 */
- /** @const */ var EN0_CRDAHI = 0x09; /* high byte, current remote dma address RD */
- /** @const */ var EN0_RSARHI = 0x09; /* Remote start address reg 1 */
- /** @const */ var EN0_RCNTLO = 0x0a; /* Remote byte count reg WR */
- /** @const */ var EN0_RCNTHI = 0x0b; /* Remote byte count reg WR */
- /** @const */ var EN0_RSR = 0x0c; /* rx status reg RD */
- /** @const */ var EN0_RXCR = 0x0c; /* RX configuration reg WR */
- /** @const */ var EN0_TXCR = 0x0d; /* TX configuration reg WR */
- /** @const */ var EN0_COUNTER0 = 0x0d; /* Rcv alignment error counter RD */
- /** @const */ var EN0_DCFG = 0x0e; /* Data configuration reg WR */
- /** @const */ var EN0_COUNTER1 = 0x0e; /* Rcv CRC error counter RD */
- /** @const */ var EN0_IMR = 0x0f; /* Interrupt mask reg WR */
- /** @const */ var EN0_COUNTER2 = 0x0f; /* Rcv missed frame error counter RD */
- /** @const */ var NE_DATAPORT = 0x10; /* NatSemi-defined port window offset. */
- /** @const */ var NE_RESET = 0x1f; /* Issue a read to reset, a write to clear. */
- /* Bits in EN0_ISR - Interrupt status register */
- /** @const */ var ENISR_RX = 0x01; /* Receiver, no error */
- /** @const */ var ENISR_TX = 0x02; /* Transmitter, no error */
- /** @const */ var ENISR_RX_ERR = 0x04; /* Receiver, with error */
- /** @const */ var ENISR_TX_ERR = 0x08; /* Transmitter, with error */
- /** @const */ var ENISR_OVER = 0x10; /* Receiver overwrote the ring */
- /** @const */ var ENISR_COUNTERS = 0x20; /* Counters need emptying */
- /** @const */ var ENISR_RDC = 0x40; /* remote dma complete */
- /** @const */ var ENISR_RESET = 0x80; /* Reset completed */
- /** @const */ var ENISR_ALL = 0x3f; /* Interrupts we will enable */
- /** @const */ var ENRSR_RXOK = 0x01; /* Received a good packet */
- /** @const */ var START_PAGE = 0x40;
- /** @const */ var START_RX_PAGE = 0x40 + 12;
- /** @const */ var STOP_PAGE = 0x80;
- // Search and replace MAC addresses in ethernet, arp and dhcp packets.
- // Used after restoring an OS from memory dump, so that multiple instances of
- // that OS can run at the same time with different external MAC addresses.
- // Crude but seems to work.
- function translate_mac_address(packet, search_mac, replacement_mac)
- {
- if(packet[0] === search_mac[0] &&
- packet[1] === search_mac[1] &&
- packet[2] === search_mac[2] &&
- packet[3] === search_mac[3] &&
- packet[4] === search_mac[4] &&
- packet[5] === search_mac[5])
- {
- dbg_log("Replace mac in eth destination field", LOG_NET);
- packet[0] = replacement_mac[0];
- packet[1] = replacement_mac[1];
- packet[2] = replacement_mac[2];
- packet[3] = replacement_mac[3];
- packet[4] = replacement_mac[4];
- packet[5] = replacement_mac[5];
- }
- if(packet[6 + 0] === search_mac[0] &&
- packet[6 + 1] === search_mac[1] &&
- packet[6 + 2] === search_mac[2] &&
- packet[6 + 3] === search_mac[3] &&
- packet[6 + 4] === search_mac[4] &&
- packet[6 + 5] === search_mac[5])
- {
- dbg_log("Replace mac in eth source field", LOG_NET);
- packet[6 + 0] = replacement_mac[0];
- packet[6 + 1] = replacement_mac[1];
- packet[6 + 2] = replacement_mac[2];
- packet[6 + 3] = replacement_mac[3];
- packet[6 + 4] = replacement_mac[4];
- packet[6 + 5] = replacement_mac[5];
- }
- const ethertype = packet[12] << 8 | packet[13];
- if(ethertype === 0x0800)
- {
- // ipv4
- const ipv4_packet = packet.subarray(14);
- const ipv4_version = ipv4_packet[0] >> 4;
- if(ipv4_version !== 4)
- {
- dbg_log("Expected ipv4.version==4 but got: " + ipv4_version, LOG_NET);
- return;
- }
- const ipv4_ihl = ipv4_packet[0] & 0xF;
- dbg_assert(ipv4_ihl === 5, "TODO: ihl!=5");
- const ipv4_proto = ipv4_packet[9];
- if(ipv4_proto === 0x11)
- {
- // udp
- const udp_packet = ipv4_packet.subarray(5 * 4);
- const source_port = udp_packet[0] << 8 | udp_packet[1];
- const destination_port = udp_packet[2] << 8 | udp_packet[3];
- const checksum = udp_packet[6] << 8 | udp_packet[7];
- dbg_log("udp srcport=" + source_port + " dstport=" + destination_port + " checksum=" + h(checksum, 4), LOG_NET);
- if(source_port === 67 || destination_port === 67)
- {
- // dhcp
- const dhcp_packet = udp_packet.subarray(8);
- const dhcp_magic = dhcp_packet[0xEC] << 24 | dhcp_packet[0xED] << 16 | dhcp_packet[0xEE] << 8 | dhcp_packet[0xEF];
- if(dhcp_magic !== 0x63825363)
- {
- dbg_log("dhcp packet didn't match magic: " + h(dhcp_magic, 8));
- return;
- }
- if(dhcp_packet[28 + 0] === search_mac[0] &&
- dhcp_packet[28 + 1] === search_mac[1] &&
- dhcp_packet[28 + 2] === search_mac[2] &&
- dhcp_packet[28 + 3] === search_mac[3] &&
- dhcp_packet[28 + 4] === search_mac[4] &&
- dhcp_packet[28 + 5] === search_mac[5])
- {
- dbg_log("Replace mac in dhcp.chaddr", LOG_NET);
- dhcp_packet[28 + 0] = replacement_mac[0];
- dhcp_packet[28 + 1] = replacement_mac[1];
- dhcp_packet[28 + 2] = replacement_mac[2];
- dhcp_packet[28 + 3] = replacement_mac[3];
- dhcp_packet[28 + 4] = replacement_mac[4];
- dhcp_packet[28 + 5] = replacement_mac[5];
- udp_packet[6] = udp_packet[7] = 0; // zero udp checksum
- }
- let offset = 0xF0;
- while(offset < dhcp_packet.length)
- {
- const dhcp_option_type = dhcp_packet[offset++];
- if(dhcp_option_type === 0xFF)
- {
- break;
- }
- const length = dhcp_packet[offset++];
- if(dhcp_option_type === 0x3D && // client identifier
- dhcp_packet[offset + 0] === 0x01 && // ethernet
- dhcp_packet[offset + 1] === search_mac[0] &&
- dhcp_packet[offset + 2] === search_mac[1] &&
- dhcp_packet[offset + 3] === search_mac[2] &&
- dhcp_packet[offset + 4] === search_mac[3] &&
- dhcp_packet[offset + 5] === search_mac[4] &&
- dhcp_packet[offset + 6] === search_mac[5])
- {
- dbg_log("Replace mac in dhcp.clientidentifier", LOG_NET);
- dhcp_packet[offset + 1] = replacement_mac[0];
- dhcp_packet[offset + 2] = replacement_mac[1];
- dhcp_packet[offset + 3] = replacement_mac[2];
- dhcp_packet[offset + 4] = replacement_mac[3];
- dhcp_packet[offset + 5] = replacement_mac[4];
- dhcp_packet[offset + 6] = replacement_mac[5];
- udp_packet[6] = udp_packet[7] = 0; // zero udp checksum
- }
- offset += length;
- }
- }
- }
- else
- {
- // tcp, ...
- }
- }
- else if(ethertype === 0x0806)
- {
- // arp
- const arp_packet = packet.subarray(14);
- dbg_log("arp oper=" + arp_packet[7] + " " + format_mac(arp_packet.subarray(8, 8+6)) + " " + format_mac(arp_packet.subarray(18, 18+6)), LOG_NET);
- if(arp_packet[8 + 0] === search_mac[0] &&
- arp_packet[8 + 1] === search_mac[1] &&
- arp_packet[8 + 2] === search_mac[2] &&
- arp_packet[8 + 3] === search_mac[3] &&
- arp_packet[8 + 4] === search_mac[4] &&
- arp_packet[8 + 5] === search_mac[5])
- {
- dbg_log("Replace mac in arp.sha", LOG_NET);
- arp_packet[8 + 0] = replacement_mac[0];
- arp_packet[8 + 1] = replacement_mac[1];
- arp_packet[8 + 2] = replacement_mac[2];
- arp_packet[8 + 3] = replacement_mac[3];
- arp_packet[8 + 4] = replacement_mac[4];
- arp_packet[8 + 5] = replacement_mac[5];
- }
- }
- else
- {
- // TODO: ipv6, ...
- }
- }
- function format_mac(mac)
- {
- return [
- mac[0].toString(16).padStart(2, "0"),
- mac[1].toString(16).padStart(2, "0"),
- mac[2].toString(16).padStart(2, "0"),
- mac[3].toString(16).padStart(2, "0"),
- mac[4].toString(16).padStart(2, "0"),
- mac[5].toString(16).padStart(2, "0"),
- ].join(":");
- }
- function dump_packet(packet, prefix)
- {
- const ethertype = packet[12] << 8 | packet[13] << 0;
- if(ethertype === 0x0800)
- {
- const ipv4_packet = packet.subarray(14);
- const ipv4_len = ipv4_packet[2] << 8 | ipv4_packet[3];
- const ipv4_proto = ipv4_packet[9];
- if(ipv4_proto === 0x11)
- {
- const udp_packet = ipv4_packet.subarray(5 * 4);
- const source_port = udp_packet[0] << 8 | udp_packet[1];
- const destination_port = udp_packet[2] << 8 | udp_packet[3];
- const checksum = udp_packet[6] << 8 | udp_packet[7];
- if(source_port === 67 || destination_port === 67)
- {
- const dhcp_packet = udp_packet.subarray(8);
- const dhcp_chaddr = dhcp_packet.subarray(28, 28+6);
- dbg_log(prefix + " len=" + packet.length + " ethertype=" + h(ethertype) + " ipv4.len=" + ipv4_len + " ipv4.proto=" + h(packet[14 + 9]) + " udp.srcport=" + source_port + " udp.dstport=" + destination_port + " udp.chksum=" + h(checksum, 4) + " dhcp.chaddr=" + format_mac(dhcp_chaddr));
- }
- else
- {
- dbg_log(prefix + " len=" + packet.length + " ethertype=" + h(ethertype) + " ipv4.len=" + ipv4_len + " ipv4.proto=" + h(packet[14 + 9]) + " udp.srcport=" + source_port + " udp.dstport=" + destination_port + " udp.chksum=" + h(checksum, 4));
- }
- }
- else if(ipv4_proto === 0x01)
- {
- }
- else
- {
- dbg_log(prefix + " len=" + packet.length + " ethertype=" + h(ethertype) + " ipv4.len=" + ipv4_len + " ipv4.proto=" + h(packet[14 + 9]));
- }
- }
- else
- {
- const arp_packet = packet.subarray(14);
- dbg_log(prefix + " len=" + packet.length + " ethertype=" + h(ethertype) + " arp");
- }
- dbg_log(hex_dump(packet));
- }
- /**
- * @constructor
- * @param {CPU} cpu
- * @param {BusConnector} bus
- * @param {Boolean} preserve_mac_from_state_image
- * @param {Boolean} mac_address_translation
- */
- function Ne2k(cpu, bus, preserve_mac_from_state_image, mac_address_translation)
- {
- /** @const @type {CPU} */
- this.cpu = cpu;
- /** @const @type {PCI} */
- this.pci = cpu.devices.pci;
- this.preserve_mac_from_state_image = preserve_mac_from_state_image;
- this.mac_address_translation = mac_address_translation;
- /** @const @type {BusConnector} */
- this.bus = bus;
- this.bus.register("net0-receive", function(data)
- {
- this.receive(data);
- }, this);
- this.port = 0x300;
- this.name = "ne2k";
- /** @const */
- var use_pci = true;
- if(use_pci)
- {
- this.pci_space = [
- 0xec, 0x10, 0x29, 0x80, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
- this.port & 0xFF | 1, this.port >> 8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x1a, 0x00, 0x11,
- 0x00, 0x00, 0xb8, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
- ];
- this.pci_id = 0x05 << 3;
- this.pci_bars = [
- {
- size: 32,
- },
- ];
- }
- this.isr = 0;
- this.imr = 0; // interrupt mask register
- this.cr = 1;
- this.dcfg = 0;
- this.rcnt = 0;
- this.tcnt = 0;
- this.tpsr = 0;
- this.memory = new Uint8Array(256 * 0x80);
- this.rxcr = 0;
- this.txcr = 0;
- this.tsr = 1;
- // mac address
- this.mac = new Uint8Array([
- 0x00, 0x22, 0x15,
- Math.random() * 255 | 0,
- Math.random() * 255 | 0,
- Math.random() * 255 | 0,
- ]);
- // multicast addresses
- this.mar = Uint8Array.of(0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF);
- // Used for mac address translation
- // The mac the OS thinks it has
- this.mac_address_in_state = null;
- for(var i = 0; i < 6; i++)
- {
- this.memory[i << 1] = this.memory[i << 1 | 1] = this.mac[i];
- }
- // the PROM signature of 0x57, 0x57 is also doubled
- // resulting in setting the 4 bytes at the end, 28, 29, 30 and 31 to 0x57
- this.memory[14 << 1] = this.memory[14 << 1 | 1] = 0x57;
- this.memory[15 << 1] = this.memory[15 << 1 | 1] = 0x57;
- dbg_log("Mac: " + format_mac(this.mac), LOG_NET);
- this.rsar = 0;
- this.pstart = START_PAGE;
- this.pstop = STOP_PAGE;
- this.curpg = START_RX_PAGE;
- this.boundary = START_RX_PAGE;
- var io = cpu.io;
- io.register_read(this.port | E8390_CMD, this, function()
- {
- dbg_log("Read cmd", LOG_NET);
- return this.cr;
- });
- io.register_write(this.port | E8390_CMD, this, function(data_byte)
- {
- this.cr = data_byte;
- dbg_log("Write command: " + h(data_byte, 2) + " newpg=" + (this.cr >> 6) + " txcr=" + h(this.txcr, 2), LOG_NET);
- if(this.cr & 1)
- {
- return;
- }
- if((data_byte & 0x18) && this.rcnt === 0)
- {
- this.do_interrupt(ENISR_RDC);
- }
- if(data_byte & 4)
- {
- var start = this.tpsr << 8;
- var data = this.memory.subarray(start, start + this.tcnt);
- if(NE2K_LOG_PACKETS)
- {
- dump_packet(data, "send");
- }
- if(this.mac_address_in_state)
- {
- data = new Uint8Array(data); // make a copy
- translate_mac_address(data, this.mac_address_in_state, this.mac);
- }
- this.bus.send("net0-send", data);
- this.bus.send("eth-transmit-end", [data.length]);
- this.cr &= ~4;
- this.do_interrupt(ENISR_TX);
- dbg_log("Command: Transfer. length=" + h(data.byteLength), LOG_NET);
- }
- });
- io.register_read(this.port | EN0_COUNTER0, this, function()
- {
- var pg = this.get_page();
- if(pg === 1)
- {
- dbg_log("Read mar5", LOG_NET);
- return this.mar[5];
- }
- else
- {
- dbg_log("Read counter0 pg=" + pg, LOG_NET);
- return 0;
- }
- });
- io.register_read(this.port | EN0_COUNTER1, this, function()
- {
- var pg = this.get_page();
- if(pg === 1)
- {
- dbg_log("Read mar6", LOG_NET);
- return this.mar[6];
- }
- else
- {
- dbg_log("Read8 counter1 pg=" + pg, LOG_NET);
- return 0;
- }
- }, function()
- {
- dbg_log("Read16 counter1 pg=" + this.get_page(), LOG_NET);
- // openbsd
- return 0;
- }
- );
- io.register_read(this.port | EN0_COUNTER2, this, function()
- {
- var pg = this.get_page();
- if(pg === 1)
- {
- dbg_log("Read mar7", LOG_NET);
- return this.mar[7];
- }
- else
- {
- dbg_log("Read counter2 pg=" + pg, LOG_NET);
- return 0;
- }
- });
- io.register_read(this.port | NE_RESET, this, function()
- {
- var pg = this.get_page();
- dbg_log("Read reset", LOG_NET);
- this.do_interrupt(ENISR_RESET);
- return 0;
- });
- io.register_write(this.port | NE_RESET, this, function(data_byte)
- {
- var pg = this.get_page();
- dbg_log("Write reset: " + h(data_byte, 2), LOG_NET);
- //this.isr &= ~ENISR_RESET;
- });
- io.register_read(this.port | EN0_STARTPG, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- return this.pstart;
- }
- else if(pg === 1)
- {
- dbg_log("Read pg1/01 (mac[0])", LOG_NET);
- return this.mac[0];
- }
- else if(pg === 2)
- {
- return this.pstart;
- }
- else
- {
- dbg_log("Read pg" + pg + "/01");
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_STARTPG, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("start page: " + h(data_byte, 2), LOG_NET);
- this.pstart = data_byte;
- }
- else if(pg === 1)
- {
- dbg_log("mac[0] = " + h(data_byte), LOG_NET);
- this.mac[0] = data_byte;
- }
- else if(pg === 3)
- {
- dbg_log("Unimplemented: Write pg3/01 (9346CR): " + h(data_byte), LOG_NET);
- }
- else
- {
- dbg_log("Write pg" + pg + "/01: " + h(data_byte), LOG_NET);
- dbg_assert(false);
- }
- });
- io.register_read(this.port | EN0_STOPPG, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- return this.pstop;
- }
- else if(pg === 1)
- {
- dbg_log("Read pg1/02 (mac[1])", LOG_NET);
- return this.mac[1];
- }
- else if(pg === 2)
- {
- return this.pstop;
- }
- else
- {
- dbg_log("Read pg" + pg + "/02", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_STOPPG, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("stop page: " + h(data_byte, 2), LOG_NET);
- if(data_byte > (this.memory.length >> 8))
- {
- data_byte = this.memory.length >> 8;
- dbg_log("XXX: Adjusting stop page to " + h(data_byte), LOG_NET);
- }
- this.pstop = data_byte;
- }
- else if(pg === 1)
- {
- dbg_log("mac[1] = " + h(data_byte), LOG_NET);
- this.mac[1] = data_byte;
- }
- else
- {
- dbg_log("Write pg" + pg + "/02: " + h(data_byte), LOG_NET);
- dbg_assert(false);
- }
- });
- io.register_read(this.port | EN0_ISR, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Read isr: " + h(this.isr, 2), LOG_NET);
- return this.isr;
- }
- else if(pg === 1)
- {
- dbg_log("Read curpg: " + h(this.curpg, 2), LOG_NET);
- return this.curpg;
- }
- else
- {
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_ISR, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- // acknowledge interrupts where bit is set
- dbg_log("Write isr: " + h(data_byte, 2), LOG_NET);
- this.isr &= ~data_byte;
- this.update_irq();
- }
- else if(pg === 1)
- {
- dbg_log("Write curpg: " + h(data_byte, 2), LOG_NET);
- this.curpg = data_byte;
- }
- else
- {
- dbg_assert(false);
- }
- });
- io.register_write(this.port | EN0_TXCR, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- this.txcr = data_byte;
- dbg_log("Write tx config: " + h(data_byte, 2), LOG_NET);
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/0d " + h(data_byte, 2), LOG_NET);
- }
- });
- io.register_write(this.port | EN0_DCFG, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write data configuration: " + h(data_byte, 2), LOG_NET);
- this.dcfg = data_byte;
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/0e " + h(data_byte, 2), LOG_NET);
- }
- });
- io.register_read(this.port | EN0_RCNTLO, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Read pg0/0a", LOG_NET);
- return 0x50;
- }
- else if(pg === 1)
- {
- dbg_log("Read mar2", LOG_NET);
- return this.mar[2];
- }
- else
- {
- dbg_assert(false, "TODO");
- return 0;
- }
- });
- io.register_write(this.port | EN0_RCNTLO, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write remote byte count low: " + h(data_byte, 2), LOG_NET);
- this.rcnt = this.rcnt & 0xFF00 | data_byte & 0xFF;
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/0a " + h(data_byte, 2), LOG_NET);
- }
- });
- io.register_read(this.port | EN0_RCNTHI, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Read pg0/0b", LOG_NET);
- return 0x43;
- }
- else if(pg === 1)
- {
- dbg_log("Read mar3", LOG_NET);
- return this.mar[3];
- }
- else
- {
- dbg_assert(false, "TODO");
- return 0;
- }
- });
- io.register_write(this.port | EN0_RCNTHI, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write remote byte count high: " + h(data_byte, 2), LOG_NET);
- this.rcnt = this.rcnt & 0xFF | data_byte << 8 & 0xFF00;
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/0b " + h(data_byte, 2), LOG_NET);
- }
- });
- io.register_read(this.port | EN0_RSARLO, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Read remote start address low", LOG_NET);
- return this.rsar & 0xFF;
- }
- else if(pg === 1)
- {
- dbg_log("Read mar0", LOG_NET);
- return this.mar[0];
- }
- else
- {
- dbg_log("Unimplemented: Read pg" + pg + "/08", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_RSARLO, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write remote start address low: " + h(data_byte, 2), LOG_NET);
- this.rsar = this.rsar & 0xFF00 | data_byte & 0xFF;
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/08 " + h(data_byte, 2), LOG_NET);
- }
- });
- io.register_read(this.port | EN0_RSARHI, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Read remote start address high", LOG_NET);
- return this.rsar >> 8 & 0xFF;
- }
- else if(pg === 1)
- {
- dbg_log("Read mar1", LOG_NET);
- return this.mar[1];
- }
- else
- {
- dbg_log("Unimplemented: Read pg" + pg + "/09", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_RSARHI, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write remote start address low: " + h(data_byte, 2), LOG_NET);
- this.rsar = this.rsar & 0xFF | data_byte << 8 & 0xFF00;
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/09 " + h(data_byte, 2), LOG_NET);
- }
- });
- io.register_write(this.port | EN0_IMR, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write interrupt mask register: " + h(data_byte, 2) + " isr=" + h(this.isr, 2), LOG_NET);
- this.imr = data_byte;
- this.update_irq();
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/0f " + h(data_byte, 2), LOG_NET);
- }
- });
- io.register_read(this.port | EN0_BOUNDARY, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Read boundary: " + h(this.boundary, 2), LOG_NET);
- return this.boundary;
- }
- else if(pg === 1)
- {
- dbg_log("Read pg1/03 (mac[2])", LOG_NET);
- return this.mac[2];
- }
- else if(pg === 3)
- {
- dbg_log("Unimplemented: Read pg3/03 (CONFIG0)", LOG_NET);
- return 0;
- }
- else
- {
- dbg_log("Read pg" + pg + "/03", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_BOUNDARY, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write boundary: " + h(data_byte, 2), LOG_NET);
- this.boundary = data_byte;
- }
- else if(pg === 1)
- {
- dbg_log("mac[2] = " + h(data_byte), LOG_NET);
- this.mac[2] = data_byte;
- }
- else
- {
- dbg_log("Write pg" + pg + "/03: " + h(data_byte), LOG_NET);
- dbg_assert(false);
- }
- });
- io.register_read(this.port | EN0_TSR, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- return this.tsr;
- }
- else if(pg === 1)
- {
- dbg_log("Read pg1/04 (mac[3])", LOG_NET);
- return this.mac[3];
- }
- else
- {
- dbg_log("Read pg" + pg + "/04", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_TPSR, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write tpsr: " + h(data_byte, 2), LOG_NET);
- this.tpsr = data_byte;
- }
- else if(pg === 1)
- {
- dbg_log("mac[3] = " + h(data_byte), LOG_NET);
- this.mac[3] = data_byte;
- }
- else
- {
- dbg_log("Write pg" + pg + "/04: " + h(data_byte), LOG_NET);
- dbg_assert(false);
- }
- });
- io.register_read(this.port | EN0_TCNTLO, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Unimplemented: Read pg0/05 (NCR: Number of Collisions Register)", LOG_NET);
- return 0;
- }
- else if(pg === 1)
- {
- dbg_log("Read pg1/05 (mac[4])", LOG_NET);
- return this.mac[4];
- }
- else if(pg === 3)
- {
- dbg_log("Unimplemented: Read pg3/05 (CONFIG2)", LOG_NET);
- return 0;
- }
- else
- {
- dbg_log("Read pg" + pg + "/05", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_TCNTLO, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write tcnt low: " + h(data_byte, 2), LOG_NET);
- this.tcnt = this.tcnt & ~0xFF | data_byte;
- }
- else if(pg === 1)
- {
- dbg_log("mac[4] = " + h(data_byte), LOG_NET);
- this.mac[4] = data_byte;
- }
- else if(pg === 3)
- {
- dbg_log("Unimplemented: Write pg3/05 (CONFIG2): " + h(data_byte), LOG_NET);
- }
- else
- {
- dbg_log("Write pg" + pg + "/05: " + h(data_byte), LOG_NET);
- dbg_assert(false);
- }
- });
- io.register_read(this.port | EN0_TCNTHI, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_assert(false, "TODO");
- return 0;
- }
- else if(pg === 1)
- {
- dbg_log("Read pg1/06 (mac[5])", LOG_NET);
- return this.mac[5];
- }
- else if(pg === 3)
- {
- dbg_log("Unimplemented: Read pg3/06 (CONFIG3)", LOG_NET);
- return 0;
- }
- else
- {
- dbg_log("Read pg" + pg + "/06", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_TCNTHI, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("Write tcnt high: " + h(data_byte, 2), LOG_NET);
- this.tcnt = this.tcnt & 0xFF | data_byte << 8;
- }
- else if(pg === 1)
- {
- dbg_log("mac[5] = " + h(data_byte), LOG_NET);
- this.mac[5] = data_byte;
- }
- else if(pg === 3)
- {
- dbg_log("Unimplemented: Write pg3/06 (CONFIG3): " + h(data_byte), LOG_NET);
- }
- else
- {
- dbg_log("Write pg" + pg + "/06: " + h(data_byte), LOG_NET);
- dbg_assert(false);
- }
- });
- io.register_read(this.port | EN0_RSR, this, function()
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- return 1 | 1 << 3; // receive status ok
- }
- else if(pg === 1)
- {
- dbg_log("Read mar4", LOG_NET);
- return this.mar[4];
- }
- else
- {
- dbg_log("Unimplemented: Read pg" + pg + "/0c", LOG_NET);
- dbg_assert(false);
- return 0;
- }
- });
- io.register_write(this.port | EN0_RXCR, this, function(data_byte)
- {
- var pg = this.get_page();
- if(pg === 0)
- {
- dbg_log("RX configuration reg write: " + h(data_byte, 2), LOG_NET);
- this.rxcr = data_byte;
- }
- else
- {
- dbg_log("Unimplemented: Write pg" + pg + "/0c: " + h(data_byte), LOG_NET);
- }
- });
- io.register_read(this.port | NE_DATAPORT | 0, this,
- this.data_port_read8,
- this.data_port_read16,
- this.data_port_read32);
- io.register_write(this.port | NE_DATAPORT | 0, this,
- this.data_port_write16,
- this.data_port_write16,
- this.data_port_write32);
- if(use_pci)
- {
- cpu.devices.pci.register_device(this);
- }
- }
- Ne2k.prototype.get_state = function()
- {
- var state = [];
- state[0] = this.isr;
- state[1] = this.imr;
- state[2] = this.cr;
- state[3] = this.dcfg;
- state[4] = this.rcnt;
- state[5] = this.tcnt;
- state[6] = this.tpsr;
- state[7] = this.rsar;
- state[8] = this.pstart;
- state[9] = this.curpg;
- state[10] = this.boundary;
- state[11] = this.pstop;
- state[12] = this.rxcr;
- state[13] = this.txcr;
- state[14] = this.tsr;
- state[15] = this.mac;
- state[16] = this.memory;
- return state;
- };
- Ne2k.prototype.set_state = function(state)
- {
- this.isr = state[0];
- this.imr = state[1];
- this.cr = state[2];
- this.dcfg = state[3];
- this.rcnt = state[4];
- this.tcnt = state[5];
- this.tpsr = state[6];
- this.rsar = state[7];
- this.pstart = state[8];
- this.curpg = state[9];
- this.boundary = state[10];
- this.pstop = state[11];
- this.rxcr = state[12];
- this.txcr = state[13];
- this.tsr = state[14];
- if(this.preserve_mac_from_state_image)
- {
- this.mac = state[15];
- this.memory = state[16];
- }
- else if(this.mac_address_translation)
- {
- this.mac_address_in_state = state[15];
- this.memory = state[16];
- dbg_log("Using mac address translation" +
- " guest_os_mac=" + format_mac(this.mac_address_in_state) +
- " real_mac=" + format_mac(this.mac), LOG_NET);
- }
- };
- Ne2k.prototype.do_interrupt = function(ir_mask)
- {
- dbg_log("Do interrupt " + h(ir_mask, 2), LOG_NET);
- this.isr |= ir_mask;
- this.update_irq();
- };
- Ne2k.prototype.update_irq = function()
- {
- if(this.imr & this.isr)
- {
- this.pci.raise_irq(this.pci_id);
- }
- else
- {
- this.pci.lower_irq(this.pci_id);
- }
- };
- Ne2k.prototype.data_port_write = function(data_byte)
- {
- if(NE2K_LOG_VERBOSE)
- {
- dbg_log("Write data port: data=" + h(data_byte & 0xFF, 2) +
- " rsar=" + h(this.rsar, 4) +
- " rcnt=" + h(this.rcnt, 4), LOG_NET);
- }
- if(this.rsar <= 0x10 || this.rsar >= (START_PAGE << 8) && this.rsar < (STOP_PAGE << 8))
- {
- this.memory[this.rsar] = data_byte;
- }
- this.rsar++;
- this.rcnt--;
- if(this.rsar >= (this.pstop << 8))
- {
- this.rsar += (this.pstart - this.pstop) << 8;
- }
- if(this.rcnt === 0)
- {
- this.do_interrupt(ENISR_RDC);
- }
- };
- Ne2k.prototype.data_port_write16 = function(data)
- {
- this.data_port_write(data);
- if(this.dcfg & 1)
- {
- this.data_port_write(data >> 8);
- }
- };
- Ne2k.prototype.data_port_write32 = function(data)
- {
- this.data_port_write(data);
- this.data_port_write(data >> 8);
- this.data_port_write(data >> 16);
- this.data_port_write(data >> 24);
- };
- Ne2k.prototype.data_port_read = function()
- {
- let data = 0;
- if(this.rsar < (STOP_PAGE << 8))
- {
- data = this.memory[this.rsar];
- }
- if(NE2K_LOG_VERBOSE)
- {
- dbg_log("Read data port: data=" + h(data, 2) +
- " rsar=" + h(this.rsar, 4) +
- " rcnt=" + h(this.rcnt, 4), LOG_NET);
- }
- this.rsar++;
- this.rcnt--;
- if(this.rsar >= (this.pstop << 8))
- {
- this.rsar += (this.pstart - this.pstop) << 8;
- }
- if(this.rcnt === 0)
- {
- this.do_interrupt(ENISR_RDC);
- }
- return data;
- };
- Ne2k.prototype.data_port_read8 = function()
- {
- return this.data_port_read16() & 0xFF;
- };
- Ne2k.prototype.data_port_read16 = function()
- {
- if(this.dcfg & 1)
- {
- return this.data_port_read() | this.data_port_read() << 8;
- }
- else
- {
- return this.data_port_read();
- }
- };
- Ne2k.prototype.data_port_read32 = function()
- {
- return this.data_port_read() | this.data_port_read() << 8 |
- this.data_port_read() << 16 | this.data_port_read() << 24;
- };
- Ne2k.prototype.receive = function(data)
- {
- // called from the adapter when data is received over the network
- if(this.cr & 1)
- {
- // stop bit set
- return;
- }
- if(NE2K_LOG_PACKETS)
- {
- dump_packet(data, "receive");
- }
- this.bus.send("eth-receive-end", [data.length]);
- if(this.rxcr & 0x10)
- {
- // promiscuous
- }
- else if((this.rxcr & 4) &&
- data[0] === 0xFF && data[1] === 0xFF && data[2] === 0xFF &&
- data[3] === 0xFF && data[4] === 0xFF && data[5] === 0xFF)
- {
- // broadcast
- }
- else if((this.rxcr & 8) && (data[0] & 1) === 1)
- {
- // multicast
- // XXX
- return;
- }
- else if(data[0] === this.mac[0] && data[1] === this.mac[1] &&
- data[2] === this.mac[2] && data[3] === this.mac[3] &&
- data[4] === this.mac[4] && data[5] === this.mac[5])
- {
- }
- else
- {
- return;
- }
- if(this.mac_address_in_state)
- {
- data = new Uint8Array(data); // make a copy
- translate_mac_address(data, this.mac, this.mac_address_in_state);
- }
- var packet_length = Math.max(60, data.length);
- var offset = this.curpg << 8;
- var total_length = packet_length + 4;
- var data_start = offset + 4;
- var next = this.curpg + 1 + (total_length >> 8);
- var end = offset + total_length;
- const needed = 1 + (total_length >> 8);
- // boundary == curpg interpreted as ringbuffer empty
- const available = this.boundary > this.curpg ?
- this.boundary - this.curpg :
- this.pstop - this.curpg + this.boundary - this.pstart;
- if(available < needed &&
- this.boundary !== 0 // XXX: ReactOS sets this to 0 initially and never updates it unless it receives a packet
- )
- {
- dbg_log("Buffer full, dropping packet pstart=" + h(this.pstart) + " pstop=" + h(this.pstop) +
- " curpg=" + h(this.curpg) + " needed=" + h(needed) + " boundary=" + h(this.boundary) + " available=" + h(available), LOG_NET);
- return;
- }
- if(end > (this.pstop << 8))
- {
- // Shouldn't happen because at this size it can't cross a page,
- // so we can skip filling with zeroes
- dbg_assert(data.length >= 60);
- var cut = (this.pstop << 8) - data_start;
- dbg_assert(cut >= 0);
- this.memory.set(data.subarray(0, cut), data_start);
- this.memory.set(data.subarray(cut), this.pstart << 8);
- dbg_log("rcv cut=" + h(cut), LOG_NET);
- }
- else
- {
- this.memory.set(data, data_start);
- if(data.length < 60)
- {
- this.memory.fill(0, data_start + data.length, data_start + 60);
- }
- }
- if(next >= this.pstop)
- {
- next += this.pstart - this.pstop;
- }
- // write packet header
- this.memory[offset] = ENRSR_RXOK; // status
- this.memory[offset + 1] = next;
- this.memory[offset + 2] = total_length;
- this.memory[offset + 3] = total_length >> 8;
- this.curpg = next;
- dbg_log("rcv offset=" + h(offset) + " len=" + h(total_length) + " next=" + h(next), LOG_NET);
- this.do_interrupt(ENISR_RX);
- };
- Ne2k.prototype.get_page = function()
- {
- return this.cr >> 6 & 3;
- };
|