More precise ne2k emulation (fixes networking in openbsd, freebsd and haiku)

This commit is contained in:
Fabian 2020-12-31 19:14:33 -06:00
parent 12a19b444f
commit c4f1965a3f

View file

@ -1,5 +1,7 @@
"use strict";
// http://www.ethernut.de/pdf/8019asds.pdf
const NE2K_LOG_VERBOSE = false;
/** @const */ var E8390_CMD = 0x00; /* The command register (for all pages) */
@ -114,26 +116,26 @@ function Ne2k(cpu, bus)
this.tsr = 1;
// mac address
var mac = [
this.mac = new Uint8Array([
0x00, 0x22, 0x15,
Math.random() * 255 | 0,
Math.random() * 255 | 0,
Math.random() * 255 | 0,
];
]);
for(var i = 0; i < 6; i++)
{
this.memory[i << 1] = this.memory[i << 1 | 1] = mac[i];
this.memory[i << 1] = this.memory[i << 1 | 1] = this.mac[i];
}
this.memory[14] = this.memory[15] = 0x57;
dbg_log("Mac: " + h(mac[0], 2) + ":" +
h(mac[1], 2) + ":" +
h(mac[2], 2) + ":" +
h(mac[3], 2) + ":" +
h(mac[4], 2) + ":" +
h(mac[5], 2), LOG_NET);
dbg_log("Mac: " + h(this.mac[0], 2) + ":" +
h(this.mac[1], 2) + ":" +
h(this.mac[2], 2) + ":" +
h(this.mac[3], 2) + ":" +
h(this.mac[4], 2) + ":" +
h(this.mac[5], 2), LOG_NET);
this.rsar = 0;
@ -206,7 +208,8 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Read pg1/1f", LOG_NET);
dbg_log("Read pg" + pg + "/1f", LOG_NET);
dbg_assert(false);
}
return 0;
});
@ -221,7 +224,32 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Write pg1/1f: " + h(data_byte), LOG_NET);
dbg_log("Write pg" + pg + "/1f: " + h(data_byte), LOG_NET);
dbg_assert(false);
}
});
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;
}
});
@ -233,9 +261,44 @@ function Ne2k(cpu, bus)
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("pg1/1: " + h(data_byte, 2), LOG_NET);
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;
}
});
@ -245,11 +308,22 @@ function Ne2k(cpu, bus)
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("pg1/2: " + h(data_byte, 2), LOG_NET);
dbg_log("Write pg" + pg + "/02: " + h(data_byte), LOG_NET);
dbg_assert(false);
}
});
@ -261,11 +335,15 @@ function Ne2k(cpu, bus)
dbg_log("Read isr: " + h(this.isr, 2), LOG_NET);
return this.isr;
}
else
else if(pg === 1)
{
dbg_log("Read curpg: " + h(this.curpg, 2), LOG_NET);
return this.curpg;
}
else
{
dbg_assert(false);
}
});
io.register_write(this.port | EN0_ISR, this, function(data_byte)
@ -278,11 +356,15 @@ function Ne2k(cpu, bus)
this.isr &= ~data_byte;
this.update_irq();
}
else
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)
@ -295,7 +377,7 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Write pg1/0x0d " + h(data_byte, 2), LOG_NET);
dbg_log("Unimplemented: Write pg" + pg + "/0d " + h(data_byte, 2), LOG_NET);
}
});
@ -309,7 +391,22 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Write pg1/0x0e " + h(data_byte, 2), LOG_NET);
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
{
dbg_assert(false, "TODO");
return 0;
}
});
@ -323,7 +420,22 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Write pg1/0x0a " + h(data_byte, 2), LOG_NET);
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
{
dbg_assert(false, "TODO");
return 0;
}
});
@ -337,7 +449,22 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Write pg1/0x0b " + h(data_byte, 2), LOG_NET);
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
{
dbg_log("Unimplemented: Read pg" + pg + "/08", LOG_NET);
dbg_assert(false);
}
});
@ -351,7 +478,22 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Write pg1/0x08 " + h(data_byte, 2), LOG_NET);
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
{
dbg_log("Unimplemented: Read pg" + pg + "/09", LOG_NET);
dbg_assert(false);
}
});
@ -360,12 +502,12 @@ function Ne2k(cpu, bus)
var pg = this.get_page();
if(pg === 0)
{
dbg_log("Write start addresse count high: " + h(data_byte, 2), LOG_NET);
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("Write pg1/0x09 " + h(data_byte, 2), LOG_NET);
dbg_log("Unimplemented: Write pg" + pg + "/09 " + h(data_byte, 2), LOG_NET);
}
});
@ -380,7 +522,7 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Write pg1/0x0f " + h(data_byte, 2), LOG_NET);
dbg_log("Unimplemented: Write pg" + pg + "/0f " + h(data_byte, 2), LOG_NET);
}
});
@ -392,9 +534,20 @@ function Ne2k(cpu, bus)
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 pg1/0x03", LOG_NET);
dbg_log("Read pg" + pg + "/03", LOG_NET);
dbg_assert(false);
return 0;
}
});
@ -407,9 +560,15 @@ function Ne2k(cpu, bus)
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 pg1/0x03 " + h(data_byte, 2), LOG_NET);
dbg_log("Write pg" + pg + "/03: " + h(data_byte), LOG_NET);
dbg_assert(false);
}
});
@ -420,9 +579,15 @@ function Ne2k(cpu, bus)
{
return this.tsr;
}
else if(pg === 1)
{
dbg_log("Read pg1/04 (mac[3])", LOG_NET);
return this.mac[3];
}
else
{
dbg_log("Read pg1/0x04", LOG_NET);
dbg_log("Read pg" + pg + "/04", LOG_NET);
dbg_assert(false);
return 0;
}
});
@ -435,9 +600,41 @@ function Ne2k(cpu, bus)
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 pg1/0x04 " + h(data_byte, 2), LOG_NET);
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;
}
});
@ -449,9 +646,45 @@ function Ne2k(cpu, bus)
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 pg1/0x05 " + h(data_byte, 2), LOG_NET);
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;
}
});
@ -463,9 +696,19 @@ function Ne2k(cpu, bus)
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 pg1/0x06 " + h(data_byte, 2), LOG_NET);
dbg_log("Write pg" + pg + "/06: " + h(data_byte), LOG_NET);
dbg_assert(false);
}
});
@ -478,15 +721,24 @@ function Ne2k(cpu, bus)
}
else
{
dbg_log("Read pg1/0x0c", LOG_NET);
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)
{
dbg_log("RX configuration reg write: " + h(data_byte, 2), LOG_NET);
this.rxcr = 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,
@ -519,6 +771,8 @@ Ne2k.prototype.get_state = function()
state[8] = this.pstart;
state[9] = this.curpg;
state[10] = this.boundary;
state[11] = this.mac;
state[12] = this.pstop;
return state;
};
@ -536,6 +790,8 @@ Ne2k.prototype.set_state = function(state)
this.pstart = state[8];
this.curpg = state[9];
this.boundary = state[10];
// state[11];
this.pstop = state[12];
};
Ne2k.prototype.do_interrupt = function(ir_mask)
@ -566,14 +822,13 @@ Ne2k.prototype.data_port_write = function(data_byte)
" rcnt=" + h(this.rcnt, 4), LOG_NET);
}
if(this.rsar > 0x10 && this.rsar < (START_PAGE << 8))
if(this.rsar <= 0x10 || this.rsar >= (START_PAGE << 8) && this.rsar < (STOP_PAGE << 8))
{
// unmapped
return;
this.memory[this.rsar] = data_byte;
}
this.rsar++;
this.rcnt--;
this.memory[this.rsar++] = data_byte;
if(this.rsar >= (this.pstop << 8))
{
@ -606,14 +861,21 @@ Ne2k.prototype.data_port_write32 = function(data)
Ne2k.prototype.data_port_read = function()
{
var data = this.memory[this.rsar++];
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 - 1, 4) +
" rsar=" + h(this.rsar, 4) +
" rcnt=" + h(this.rcnt, 4), LOG_NET);
}
this.rsar++;
this.rcnt--;
if(this.rsar >= (this.pstop << 8))
@ -680,9 +942,9 @@ Ne2k.prototype.receive = function(data)
// XXX
return;
}
else if(data[0] === this.memory[0] && data[1] === this.memory[2] &&
data[2] === this.memory[4] && data[3] === this.memory[6] &&
data[4] === this.memory[8] && data[5] === this.memory[10])
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
@ -699,12 +961,31 @@ Ne2k.prototype.receive = function(data)
var end = offset + total_length;
if(end > this.memory.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
)
{
// shouldn't happen because at this size it can't cross a page
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.memory.length - data_start;
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);
@ -739,5 +1020,5 @@ Ne2k.prototype.receive = function(data)
Ne2k.prototype.get_page = function()
{
return this.cr & 0xC0;
return this.cr >> 6 & 3;
};