@aptre/v86 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- package/src/virtio_net.ts +326 -0
package/src/ne2k.ts
ADDED
|
@@ -0,0 +1,1589 @@
|
|
|
1
|
+
import { LOG_NET } from './const.js'
|
|
2
|
+
import { h, hex_dump } from './lib.js'
|
|
3
|
+
import { dbg_assert, dbg_log } from './log.js'
|
|
4
|
+
|
|
5
|
+
import { PCI } from './pci.js'
|
|
6
|
+
import { BusConnector } from './bus.js'
|
|
7
|
+
import { IO } from './io.js'
|
|
8
|
+
|
|
9
|
+
// http://www.ethernut.de/pdf/8019asds.pdf
|
|
10
|
+
|
|
11
|
+
// Minimal interface for the CPU fields Ne2k uses.
|
|
12
|
+
interface Ne2kCpu {
|
|
13
|
+
io: IO
|
|
14
|
+
devices: {
|
|
15
|
+
pci: PCI
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const NE2K_LOG_VERBOSE = false
|
|
20
|
+
const NE2K_LOG_PACKETS = false
|
|
21
|
+
|
|
22
|
+
const E8390_CMD = 0x00 /* The command register (for all pages) */
|
|
23
|
+
|
|
24
|
+
/* Page 0 register offsets. */
|
|
25
|
+
const _EN0_CLDALO = 0x01 /* Low byte of current local dma addr RD */
|
|
26
|
+
const EN0_STARTPG = 0x01 /* Starting page of ring bfr WR */
|
|
27
|
+
const _EN0_CLDAHI = 0x02 /* High byte of current local dma addr RD */
|
|
28
|
+
const EN0_STOPPG = 0x02 /* Ending page +1 of ring bfr WR */
|
|
29
|
+
const EN0_BOUNDARY = 0x03 /* Boundary page of ring bfr RD WR */
|
|
30
|
+
const EN0_TSR = 0x04 /* Transmit status reg RD */
|
|
31
|
+
const EN0_TPSR = 0x04 /* Transmit starting page WR */
|
|
32
|
+
const _EN0_NCR = 0x05 /* Number of collision reg RD */
|
|
33
|
+
const EN0_TCNTLO = 0x05 /* Low byte of tx byte count WR */
|
|
34
|
+
const _EN0_FIFO = 0x06 /* FIFO RD */
|
|
35
|
+
const EN0_TCNTHI = 0x06 /* High byte of tx byte count WR */
|
|
36
|
+
const EN0_ISR = 0x07 /* Interrupt status reg RD WR */
|
|
37
|
+
const _EN0_CRDALO = 0x08 /* low byte of current remote dma address RD */
|
|
38
|
+
const EN0_RSARLO = 0x08 /* Remote start address reg 0 */
|
|
39
|
+
const _EN0_CRDAHI = 0x09 /* high byte, current remote dma address RD */
|
|
40
|
+
const EN0_RSARHI = 0x09 /* Remote start address reg 1 */
|
|
41
|
+
const EN0_RCNTLO = 0x0a /* Remote byte count reg WR */
|
|
42
|
+
const EN0_RCNTHI = 0x0b /* Remote byte count reg WR */
|
|
43
|
+
const EN0_RSR = 0x0c /* rx status reg RD */
|
|
44
|
+
const EN0_RXCR = 0x0c /* RX configuration reg WR */
|
|
45
|
+
const EN0_TXCR = 0x0d /* TX configuration reg WR */
|
|
46
|
+
const EN0_COUNTER0 = 0x0d /* Rcv alignment error counter RD */
|
|
47
|
+
const EN0_DCFG = 0x0e /* Data configuration reg WR */
|
|
48
|
+
const EN0_COUNTER1 = 0x0e /* Rcv CRC error counter RD */
|
|
49
|
+
const EN0_IMR = 0x0f /* Interrupt mask reg WR */
|
|
50
|
+
const EN0_COUNTER2 = 0x0f /* Rcv missed frame error counter RD */
|
|
51
|
+
|
|
52
|
+
const NE_DATAPORT = 0x10 /* NatSemi-defined port window offset. */
|
|
53
|
+
const NE_RESET = 0x1f /* Issue a read to reset, a write to clear. */
|
|
54
|
+
|
|
55
|
+
/* Bits in EN0_ISR - Interrupt status register */
|
|
56
|
+
const ENISR_RX = 0x01 /* Receiver, no error */
|
|
57
|
+
const ENISR_TX = 0x02 /* Transmitter, no error */
|
|
58
|
+
const _ENISR_RX_ERR = 0x04 /* Receiver, with error */
|
|
59
|
+
const _ENISR_TX_ERR = 0x08 /* Transmitter, with error */
|
|
60
|
+
const _ENISR_OVER = 0x10 /* Receiver overwrote the ring */
|
|
61
|
+
const _ENISR_COUNTERS = 0x20 /* Counters need emptying */
|
|
62
|
+
const ENISR_RDC = 0x40 /* remote dma complete */
|
|
63
|
+
const ENISR_RESET = 0x80 /* Reset completed */
|
|
64
|
+
const _ENISR_ALL = 0x3f /* Interrupts we will enable */
|
|
65
|
+
|
|
66
|
+
const ENRSR_RXOK = 0x01 /* Received a good packet */
|
|
67
|
+
|
|
68
|
+
const START_PAGE = 0x40
|
|
69
|
+
const START_RX_PAGE = 0x40 + 12
|
|
70
|
+
const STOP_PAGE = 0x80
|
|
71
|
+
|
|
72
|
+
// Search and replace MAC addresses in ethernet, arp and dhcp packets.
|
|
73
|
+
// Used after restoring an OS from memory dump, so that multiple instances of
|
|
74
|
+
// that OS can run at the same time with different external MAC addresses.
|
|
75
|
+
// Crude but seems to work.
|
|
76
|
+
function translate_mac_address(
|
|
77
|
+
packet: Uint8Array,
|
|
78
|
+
search_mac: Uint8Array,
|
|
79
|
+
replacement_mac: Uint8Array,
|
|
80
|
+
): void {
|
|
81
|
+
if (
|
|
82
|
+
packet[0] === search_mac[0] &&
|
|
83
|
+
packet[1] === search_mac[1] &&
|
|
84
|
+
packet[2] === search_mac[2] &&
|
|
85
|
+
packet[3] === search_mac[3] &&
|
|
86
|
+
packet[4] === search_mac[4] &&
|
|
87
|
+
packet[5] === search_mac[5]
|
|
88
|
+
) {
|
|
89
|
+
dbg_log('Replace mac in eth destination field', LOG_NET)
|
|
90
|
+
|
|
91
|
+
packet[0] = replacement_mac[0]
|
|
92
|
+
packet[1] = replacement_mac[1]
|
|
93
|
+
packet[2] = replacement_mac[2]
|
|
94
|
+
packet[3] = replacement_mac[3]
|
|
95
|
+
packet[4] = replacement_mac[4]
|
|
96
|
+
packet[5] = replacement_mac[5]
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (
|
|
100
|
+
packet[6 + 0] === search_mac[0] &&
|
|
101
|
+
packet[6 + 1] === search_mac[1] &&
|
|
102
|
+
packet[6 + 2] === search_mac[2] &&
|
|
103
|
+
packet[6 + 3] === search_mac[3] &&
|
|
104
|
+
packet[6 + 4] === search_mac[4] &&
|
|
105
|
+
packet[6 + 5] === search_mac[5]
|
|
106
|
+
) {
|
|
107
|
+
dbg_log('Replace mac in eth source field', LOG_NET)
|
|
108
|
+
|
|
109
|
+
packet[6 + 0] = replacement_mac[0]
|
|
110
|
+
packet[6 + 1] = replacement_mac[1]
|
|
111
|
+
packet[6 + 2] = replacement_mac[2]
|
|
112
|
+
packet[6 + 3] = replacement_mac[3]
|
|
113
|
+
packet[6 + 4] = replacement_mac[4]
|
|
114
|
+
packet[6 + 5] = replacement_mac[5]
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const ethertype = (packet[12] << 8) | packet[13]
|
|
118
|
+
|
|
119
|
+
if (ethertype === 0x0800) {
|
|
120
|
+
// ipv4
|
|
121
|
+
const ipv4_packet = packet.subarray(14)
|
|
122
|
+
const ipv4_version = ipv4_packet[0] >> 4
|
|
123
|
+
|
|
124
|
+
if (ipv4_version !== 4) {
|
|
125
|
+
dbg_log(
|
|
126
|
+
'Expected ipv4.version==4 but got: ' + ipv4_version,
|
|
127
|
+
LOG_NET,
|
|
128
|
+
)
|
|
129
|
+
return
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
const ipv4_ihl = ipv4_packet[0] & 0xf
|
|
133
|
+
dbg_assert(ipv4_ihl === 5, 'TODO: ihl!=5')
|
|
134
|
+
|
|
135
|
+
const ipv4_proto = ipv4_packet[9]
|
|
136
|
+
if (ipv4_proto === 0x11) {
|
|
137
|
+
// udp
|
|
138
|
+
const udp_packet = ipv4_packet.subarray(5 * 4)
|
|
139
|
+
const source_port = (udp_packet[0] << 8) | udp_packet[1]
|
|
140
|
+
const destination_port = (udp_packet[2] << 8) | udp_packet[3]
|
|
141
|
+
const checksum = (udp_packet[6] << 8) | udp_packet[7]
|
|
142
|
+
|
|
143
|
+
dbg_log(
|
|
144
|
+
'udp srcport=' +
|
|
145
|
+
source_port +
|
|
146
|
+
' dstport=' +
|
|
147
|
+
destination_port +
|
|
148
|
+
' checksum=' +
|
|
149
|
+
h(checksum, 4),
|
|
150
|
+
LOG_NET,
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
if (source_port === 67 || destination_port === 67) {
|
|
154
|
+
// dhcp
|
|
155
|
+
const dhcp_packet = udp_packet.subarray(8)
|
|
156
|
+
const dhcp_magic =
|
|
157
|
+
(dhcp_packet[0xec] << 24) |
|
|
158
|
+
(dhcp_packet[0xed] << 16) |
|
|
159
|
+
(dhcp_packet[0xee] << 8) |
|
|
160
|
+
dhcp_packet[0xef]
|
|
161
|
+
|
|
162
|
+
if (dhcp_magic !== 0x63825363) {
|
|
163
|
+
dbg_log(
|
|
164
|
+
"dhcp packet didn't match magic: " + h(dhcp_magic, 8),
|
|
165
|
+
)
|
|
166
|
+
return
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
if (
|
|
170
|
+
dhcp_packet[28 + 0] === search_mac[0] &&
|
|
171
|
+
dhcp_packet[28 + 1] === search_mac[1] &&
|
|
172
|
+
dhcp_packet[28 + 2] === search_mac[2] &&
|
|
173
|
+
dhcp_packet[28 + 3] === search_mac[3] &&
|
|
174
|
+
dhcp_packet[28 + 4] === search_mac[4] &&
|
|
175
|
+
dhcp_packet[28 + 5] === search_mac[5]
|
|
176
|
+
) {
|
|
177
|
+
dbg_log('Replace mac in dhcp.chaddr', LOG_NET)
|
|
178
|
+
|
|
179
|
+
dhcp_packet[28 + 0] = replacement_mac[0]
|
|
180
|
+
dhcp_packet[28 + 1] = replacement_mac[1]
|
|
181
|
+
dhcp_packet[28 + 2] = replacement_mac[2]
|
|
182
|
+
dhcp_packet[28 + 3] = replacement_mac[3]
|
|
183
|
+
dhcp_packet[28 + 4] = replacement_mac[4]
|
|
184
|
+
dhcp_packet[28 + 5] = replacement_mac[5]
|
|
185
|
+
|
|
186
|
+
udp_packet[6] = udp_packet[7] = 0 // zero udp checksum
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
let offset = 0xf0
|
|
190
|
+
while (offset < dhcp_packet.length) {
|
|
191
|
+
const dhcp_option_type = dhcp_packet[offset++]
|
|
192
|
+
|
|
193
|
+
if (dhcp_option_type === 0xff) {
|
|
194
|
+
break
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const length = dhcp_packet[offset++]
|
|
198
|
+
|
|
199
|
+
if (
|
|
200
|
+
dhcp_option_type === 0x3d && // client identifier
|
|
201
|
+
dhcp_packet[offset + 0] === 0x01 && // ethernet
|
|
202
|
+
dhcp_packet[offset + 1] === search_mac[0] &&
|
|
203
|
+
dhcp_packet[offset + 2] === search_mac[1] &&
|
|
204
|
+
dhcp_packet[offset + 3] === search_mac[2] &&
|
|
205
|
+
dhcp_packet[offset + 4] === search_mac[3] &&
|
|
206
|
+
dhcp_packet[offset + 5] === search_mac[4] &&
|
|
207
|
+
dhcp_packet[offset + 6] === search_mac[5]
|
|
208
|
+
) {
|
|
209
|
+
dbg_log('Replace mac in dhcp.clientidentifier', LOG_NET)
|
|
210
|
+
|
|
211
|
+
dhcp_packet[offset + 1] = replacement_mac[0]
|
|
212
|
+
dhcp_packet[offset + 2] = replacement_mac[1]
|
|
213
|
+
dhcp_packet[offset + 3] = replacement_mac[2]
|
|
214
|
+
dhcp_packet[offset + 4] = replacement_mac[3]
|
|
215
|
+
dhcp_packet[offset + 5] = replacement_mac[4]
|
|
216
|
+
dhcp_packet[offset + 6] = replacement_mac[5]
|
|
217
|
+
|
|
218
|
+
udp_packet[6] = udp_packet[7] = 0 // zero udp checksum
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
offset += length
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
} else {
|
|
225
|
+
// tcp, ...
|
|
226
|
+
}
|
|
227
|
+
} else if (ethertype === 0x0806) {
|
|
228
|
+
// arp
|
|
229
|
+
const arp_packet = packet.subarray(14)
|
|
230
|
+
dbg_log(
|
|
231
|
+
'arp oper=' +
|
|
232
|
+
arp_packet[7] +
|
|
233
|
+
' ' +
|
|
234
|
+
format_mac(arp_packet.subarray(8, 8 + 6)) +
|
|
235
|
+
' ' +
|
|
236
|
+
format_mac(arp_packet.subarray(18, 18 + 6)),
|
|
237
|
+
LOG_NET,
|
|
238
|
+
)
|
|
239
|
+
|
|
240
|
+
if (
|
|
241
|
+
arp_packet[8 + 0] === search_mac[0] &&
|
|
242
|
+
arp_packet[8 + 1] === search_mac[1] &&
|
|
243
|
+
arp_packet[8 + 2] === search_mac[2] &&
|
|
244
|
+
arp_packet[8 + 3] === search_mac[3] &&
|
|
245
|
+
arp_packet[8 + 4] === search_mac[4] &&
|
|
246
|
+
arp_packet[8 + 5] === search_mac[5]
|
|
247
|
+
) {
|
|
248
|
+
dbg_log('Replace mac in arp.sha', LOG_NET)
|
|
249
|
+
|
|
250
|
+
arp_packet[8 + 0] = replacement_mac[0]
|
|
251
|
+
arp_packet[8 + 1] = replacement_mac[1]
|
|
252
|
+
arp_packet[8 + 2] = replacement_mac[2]
|
|
253
|
+
arp_packet[8 + 3] = replacement_mac[3]
|
|
254
|
+
arp_packet[8 + 4] = replacement_mac[4]
|
|
255
|
+
arp_packet[8 + 5] = replacement_mac[5]
|
|
256
|
+
}
|
|
257
|
+
} else {
|
|
258
|
+
// TODO: ipv6, ...
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
export function format_mac(mac: Uint8Array): string {
|
|
263
|
+
return [
|
|
264
|
+
mac[0].toString(16).padStart(2, '0'),
|
|
265
|
+
mac[1].toString(16).padStart(2, '0'),
|
|
266
|
+
mac[2].toString(16).padStart(2, '0'),
|
|
267
|
+
mac[3].toString(16).padStart(2, '0'),
|
|
268
|
+
mac[4].toString(16).padStart(2, '0'),
|
|
269
|
+
mac[5].toString(16).padStart(2, '0'),
|
|
270
|
+
].join(':')
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function dump_packet(packet: Uint8Array, prefix: string): void {
|
|
274
|
+
const ethertype = (packet[12] << 8) | (packet[13] << 0)
|
|
275
|
+
if (ethertype === 0x0800) {
|
|
276
|
+
const ipv4_packet = packet.subarray(14)
|
|
277
|
+
const ipv4_len = (ipv4_packet[2] << 8) | ipv4_packet[3]
|
|
278
|
+
const ipv4_proto = ipv4_packet[9]
|
|
279
|
+
if (ipv4_proto === 0x11) {
|
|
280
|
+
const udp_packet = ipv4_packet.subarray(5 * 4)
|
|
281
|
+
const source_port = (udp_packet[0] << 8) | udp_packet[1]
|
|
282
|
+
const destination_port = (udp_packet[2] << 8) | udp_packet[3]
|
|
283
|
+
const checksum = (udp_packet[6] << 8) | udp_packet[7]
|
|
284
|
+
|
|
285
|
+
if (source_port === 67 || destination_port === 67) {
|
|
286
|
+
const dhcp_packet = udp_packet.subarray(8)
|
|
287
|
+
const dhcp_chaddr = dhcp_packet.subarray(28, 28 + 6)
|
|
288
|
+
dbg_log(
|
|
289
|
+
prefix +
|
|
290
|
+
' len=' +
|
|
291
|
+
packet.length +
|
|
292
|
+
' ethertype=' +
|
|
293
|
+
h(ethertype) +
|
|
294
|
+
' ipv4.len=' +
|
|
295
|
+
ipv4_len +
|
|
296
|
+
' ipv4.proto=' +
|
|
297
|
+
h(packet[14 + 9]) +
|
|
298
|
+
' udp.srcport=' +
|
|
299
|
+
source_port +
|
|
300
|
+
' udp.dstport=' +
|
|
301
|
+
destination_port +
|
|
302
|
+
' udp.chksum=' +
|
|
303
|
+
h(checksum, 4) +
|
|
304
|
+
' dhcp.chaddr=' +
|
|
305
|
+
format_mac(dhcp_chaddr),
|
|
306
|
+
)
|
|
307
|
+
} else {
|
|
308
|
+
dbg_log(
|
|
309
|
+
prefix +
|
|
310
|
+
' len=' +
|
|
311
|
+
packet.length +
|
|
312
|
+
' ethertype=' +
|
|
313
|
+
h(ethertype) +
|
|
314
|
+
' ipv4.len=' +
|
|
315
|
+
ipv4_len +
|
|
316
|
+
' ipv4.proto=' +
|
|
317
|
+
h(packet[14 + 9]) +
|
|
318
|
+
' udp.srcport=' +
|
|
319
|
+
source_port +
|
|
320
|
+
' udp.dstport=' +
|
|
321
|
+
destination_port +
|
|
322
|
+
' udp.chksum=' +
|
|
323
|
+
h(checksum, 4),
|
|
324
|
+
)
|
|
325
|
+
}
|
|
326
|
+
} else if (ipv4_proto === 0x01) {
|
|
327
|
+
// icmp, intentionally empty
|
|
328
|
+
} else {
|
|
329
|
+
dbg_log(
|
|
330
|
+
prefix +
|
|
331
|
+
' len=' +
|
|
332
|
+
packet.length +
|
|
333
|
+
' ethertype=' +
|
|
334
|
+
h(ethertype) +
|
|
335
|
+
' ipv4.len=' +
|
|
336
|
+
ipv4_len +
|
|
337
|
+
' ipv4.proto=' +
|
|
338
|
+
h(packet[14 + 9]),
|
|
339
|
+
)
|
|
340
|
+
}
|
|
341
|
+
} else {
|
|
342
|
+
const _arp_packet = packet.subarray(14)
|
|
343
|
+
dbg_log(
|
|
344
|
+
prefix +
|
|
345
|
+
' len=' +
|
|
346
|
+
packet.length +
|
|
347
|
+
' ethertype=' +
|
|
348
|
+
h(ethertype) +
|
|
349
|
+
' arp',
|
|
350
|
+
)
|
|
351
|
+
}
|
|
352
|
+
dbg_log(hex_dump(packet))
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
export class Ne2k {
|
|
356
|
+
name: string
|
|
357
|
+
cpu: Ne2kCpu
|
|
358
|
+
pci: PCI
|
|
359
|
+
id: number
|
|
360
|
+
preserve_mac_from_state_image: boolean
|
|
361
|
+
mac_address_translation: boolean
|
|
362
|
+
bus: BusConnector
|
|
363
|
+
port: number
|
|
364
|
+
|
|
365
|
+
pci_space: number[]
|
|
366
|
+
pci_id: number
|
|
367
|
+
pci_bars: { size: number }[]
|
|
368
|
+
|
|
369
|
+
isr: number
|
|
370
|
+
imr: number
|
|
371
|
+
cr: number
|
|
372
|
+
dcfg: number
|
|
373
|
+
rcnt: number
|
|
374
|
+
tcnt: number
|
|
375
|
+
tpsr: number
|
|
376
|
+
memory: Uint8Array
|
|
377
|
+
rxcr: number
|
|
378
|
+
txcr: number
|
|
379
|
+
tsr: number
|
|
380
|
+
mac: Uint8Array
|
|
381
|
+
mar: Uint8Array
|
|
382
|
+
mac_address_in_state: Uint8Array | null
|
|
383
|
+
rsar: number
|
|
384
|
+
pstart: number
|
|
385
|
+
pstop: number
|
|
386
|
+
curpg: number
|
|
387
|
+
boundary: number
|
|
388
|
+
|
|
389
|
+
constructor(
|
|
390
|
+
cpu: Ne2kCpu,
|
|
391
|
+
bus: BusConnector,
|
|
392
|
+
preserve_mac_from_state_image: boolean,
|
|
393
|
+
mac_address_translation: boolean,
|
|
394
|
+
id?: number,
|
|
395
|
+
) {
|
|
396
|
+
this.cpu = cpu
|
|
397
|
+
this.pci = cpu.devices.pci
|
|
398
|
+
this.id = id || 0
|
|
399
|
+
this.preserve_mac_from_state_image = preserve_mac_from_state_image
|
|
400
|
+
this.mac_address_translation = mac_address_translation
|
|
401
|
+
this.bus = bus
|
|
402
|
+
this.bus.register(
|
|
403
|
+
'net' + this.id + '-receive',
|
|
404
|
+
function (this: Ne2k, data: Uint8Array) {
|
|
405
|
+
this.receive(data)
|
|
406
|
+
},
|
|
407
|
+
this,
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
this.port = 0x300 + 0x100 * this.id
|
|
411
|
+
|
|
412
|
+
this.name = 'ne2k'
|
|
413
|
+
|
|
414
|
+
const use_pci = true
|
|
415
|
+
|
|
416
|
+
if (use_pci) {
|
|
417
|
+
this.pci_space = [
|
|
418
|
+
0xec,
|
|
419
|
+
0x10,
|
|
420
|
+
0x29,
|
|
421
|
+
0x80,
|
|
422
|
+
0x03,
|
|
423
|
+
0x01,
|
|
424
|
+
0x00,
|
|
425
|
+
0x00,
|
|
426
|
+
0x00,
|
|
427
|
+
0x00,
|
|
428
|
+
0x00,
|
|
429
|
+
0x02,
|
|
430
|
+
0x00,
|
|
431
|
+
0x00,
|
|
432
|
+
0x00,
|
|
433
|
+
0x00,
|
|
434
|
+
(this.port & 0xff) | 1,
|
|
435
|
+
this.port >> 8,
|
|
436
|
+
0x00,
|
|
437
|
+
0x00,
|
|
438
|
+
0x00,
|
|
439
|
+
0x00,
|
|
440
|
+
0x00,
|
|
441
|
+
0x00,
|
|
442
|
+
0x00,
|
|
443
|
+
0x00,
|
|
444
|
+
0x00,
|
|
445
|
+
0x00,
|
|
446
|
+
0x00,
|
|
447
|
+
0x00,
|
|
448
|
+
0x00,
|
|
449
|
+
0x00,
|
|
450
|
+
0x00,
|
|
451
|
+
0x00,
|
|
452
|
+
0x00,
|
|
453
|
+
0x00,
|
|
454
|
+
0x00,
|
|
455
|
+
0x00,
|
|
456
|
+
0x00,
|
|
457
|
+
0x00,
|
|
458
|
+
0x00,
|
|
459
|
+
0x00,
|
|
460
|
+
0x00,
|
|
461
|
+
0x00,
|
|
462
|
+
0xf4,
|
|
463
|
+
0x1a,
|
|
464
|
+
0x00,
|
|
465
|
+
0x11,
|
|
466
|
+
0x00,
|
|
467
|
+
0x00,
|
|
468
|
+
0xb8,
|
|
469
|
+
0xfe,
|
|
470
|
+
0x00,
|
|
471
|
+
0x00,
|
|
472
|
+
0x00,
|
|
473
|
+
0x00,
|
|
474
|
+
0x00,
|
|
475
|
+
0x00,
|
|
476
|
+
0x00,
|
|
477
|
+
0x00,
|
|
478
|
+
0x00,
|
|
479
|
+
0x01,
|
|
480
|
+
0x00,
|
|
481
|
+
0x00,
|
|
482
|
+
]
|
|
483
|
+
this.pci_id = (this.id === 0 ? 0x05 : 0x07 + this.id) << 3
|
|
484
|
+
this.pci_bars = [
|
|
485
|
+
{
|
|
486
|
+
size: 32,
|
|
487
|
+
},
|
|
488
|
+
]
|
|
489
|
+
} else {
|
|
490
|
+
this.pci_space = []
|
|
491
|
+
this.pci_id = 0
|
|
492
|
+
this.pci_bars = []
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
this.isr = 0
|
|
496
|
+
this.imr = 0 // interrupt mask register
|
|
497
|
+
|
|
498
|
+
this.cr = 1
|
|
499
|
+
|
|
500
|
+
this.dcfg = 0
|
|
501
|
+
|
|
502
|
+
this.rcnt = 0
|
|
503
|
+
|
|
504
|
+
this.tcnt = 0
|
|
505
|
+
this.tpsr = 0
|
|
506
|
+
this.memory = new Uint8Array(256 * 0x80)
|
|
507
|
+
|
|
508
|
+
this.rxcr = 0
|
|
509
|
+
this.txcr = 0
|
|
510
|
+
this.tsr = 1
|
|
511
|
+
|
|
512
|
+
// mac address
|
|
513
|
+
this.mac = new Uint8Array([
|
|
514
|
+
0x00,
|
|
515
|
+
0x22,
|
|
516
|
+
0x15,
|
|
517
|
+
(Math.random() * 255) | 0,
|
|
518
|
+
(Math.random() * 255) | 0,
|
|
519
|
+
(Math.random() * 255) | 0,
|
|
520
|
+
])
|
|
521
|
+
|
|
522
|
+
this.bus.send('net' + this.id + '-mac', format_mac(this.mac))
|
|
523
|
+
|
|
524
|
+
// multicast addresses
|
|
525
|
+
this.mar = Uint8Array.of(0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff)
|
|
526
|
+
|
|
527
|
+
// Used for mac address translation
|
|
528
|
+
// The mac the OS thinks it has
|
|
529
|
+
this.mac_address_in_state = null
|
|
530
|
+
|
|
531
|
+
for (let i = 0; i < 6; i++) {
|
|
532
|
+
this.memory[i << 1] = this.memory[(i << 1) | 1] = this.mac[i]
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
// the PROM signature of 0x57, 0x57 is also doubled
|
|
536
|
+
// resulting in setting the 4 bytes at the end, 28, 29, 30 and 31 to 0x57
|
|
537
|
+
this.memory[14 << 1] = this.memory[(14 << 1) | 1] = 0x57
|
|
538
|
+
this.memory[15 << 1] = this.memory[(15 << 1) | 1] = 0x57
|
|
539
|
+
|
|
540
|
+
dbg_log('Mac: ' + format_mac(this.mac), LOG_NET)
|
|
541
|
+
|
|
542
|
+
this.rsar = 0
|
|
543
|
+
|
|
544
|
+
this.pstart = START_PAGE
|
|
545
|
+
this.pstop = STOP_PAGE
|
|
546
|
+
|
|
547
|
+
this.curpg = START_RX_PAGE
|
|
548
|
+
this.boundary = START_RX_PAGE
|
|
549
|
+
|
|
550
|
+
const io = cpu.io
|
|
551
|
+
|
|
552
|
+
io.register_read(
|
|
553
|
+
this.port | E8390_CMD,
|
|
554
|
+
this,
|
|
555
|
+
function (this: Ne2k): number {
|
|
556
|
+
dbg_log('Read cmd', LOG_NET)
|
|
557
|
+
return this.cr
|
|
558
|
+
},
|
|
559
|
+
function (this: Ne2k): number {
|
|
560
|
+
dbg_log('Read16 cmd', LOG_NET)
|
|
561
|
+
return this.cr
|
|
562
|
+
},
|
|
563
|
+
)
|
|
564
|
+
|
|
565
|
+
io.register_write(
|
|
566
|
+
this.port | E8390_CMD,
|
|
567
|
+
this,
|
|
568
|
+
function (this: Ne2k, data_byte: number): void {
|
|
569
|
+
this.cr = data_byte
|
|
570
|
+
dbg_log(
|
|
571
|
+
'Write command: ' +
|
|
572
|
+
h(data_byte, 2) +
|
|
573
|
+
' newpg=' +
|
|
574
|
+
(this.cr >> 6) +
|
|
575
|
+
' txcr=' +
|
|
576
|
+
h(this.txcr, 2),
|
|
577
|
+
LOG_NET,
|
|
578
|
+
)
|
|
579
|
+
|
|
580
|
+
if (this.cr & 1) {
|
|
581
|
+
return
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
if (data_byte & 0x18 && this.rcnt === 0) {
|
|
585
|
+
this.do_interrupt(ENISR_RDC)
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
if (data_byte & 4) {
|
|
589
|
+
const start = this.tpsr << 8
|
|
590
|
+
let data: Uint8Array = this.memory.subarray(
|
|
591
|
+
start,
|
|
592
|
+
start + this.tcnt,
|
|
593
|
+
)
|
|
594
|
+
|
|
595
|
+
if (NE2K_LOG_PACKETS) {
|
|
596
|
+
dump_packet(data, 'send')
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
if (this.mac_address_in_state) {
|
|
600
|
+
data = new Uint8Array(data) // make a copy
|
|
601
|
+
translate_mac_address(
|
|
602
|
+
data,
|
|
603
|
+
this.mac_address_in_state,
|
|
604
|
+
this.mac,
|
|
605
|
+
)
|
|
606
|
+
}
|
|
607
|
+
|
|
608
|
+
this.bus.send('net' + this.id + '-send', data)
|
|
609
|
+
this.bus.send('eth-transmit-end', [data.length])
|
|
610
|
+
this.cr &= ~4
|
|
611
|
+
this.do_interrupt(ENISR_TX)
|
|
612
|
+
|
|
613
|
+
dbg_log(
|
|
614
|
+
'Command: Transfer. length=' + h(data.byteLength),
|
|
615
|
+
LOG_NET,
|
|
616
|
+
)
|
|
617
|
+
}
|
|
618
|
+
},
|
|
619
|
+
)
|
|
620
|
+
|
|
621
|
+
io.register_read(
|
|
622
|
+
this.port | EN0_COUNTER0,
|
|
623
|
+
this,
|
|
624
|
+
function (this: Ne2k): number {
|
|
625
|
+
const pg = this.get_page()
|
|
626
|
+
if (pg === 1) {
|
|
627
|
+
dbg_log('Read mar5', LOG_NET)
|
|
628
|
+
return this.mar[5]
|
|
629
|
+
} else {
|
|
630
|
+
dbg_log('Read counter0 pg=' + pg, LOG_NET)
|
|
631
|
+
return 0
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
)
|
|
635
|
+
|
|
636
|
+
io.register_read(
|
|
637
|
+
this.port | EN0_COUNTER1,
|
|
638
|
+
this,
|
|
639
|
+
function (this: Ne2k): number {
|
|
640
|
+
const pg = this.get_page()
|
|
641
|
+
if (pg === 1) {
|
|
642
|
+
dbg_log('Read mar6', LOG_NET)
|
|
643
|
+
return this.mar[6]
|
|
644
|
+
} else {
|
|
645
|
+
dbg_log('Read8 counter1 pg=' + pg, LOG_NET)
|
|
646
|
+
return 0
|
|
647
|
+
}
|
|
648
|
+
},
|
|
649
|
+
function (this: Ne2k): number {
|
|
650
|
+
dbg_log('Read16 counter1 pg=' + this.get_page(), LOG_NET)
|
|
651
|
+
// openbsd
|
|
652
|
+
return 0
|
|
653
|
+
},
|
|
654
|
+
)
|
|
655
|
+
|
|
656
|
+
io.register_read(
|
|
657
|
+
this.port | EN0_COUNTER2,
|
|
658
|
+
this,
|
|
659
|
+
function (this: Ne2k): number {
|
|
660
|
+
const pg = this.get_page()
|
|
661
|
+
if (pg === 1) {
|
|
662
|
+
dbg_log('Read mar7', LOG_NET)
|
|
663
|
+
return this.mar[7]
|
|
664
|
+
} else {
|
|
665
|
+
dbg_log('Read counter2 pg=' + pg, LOG_NET)
|
|
666
|
+
return 0
|
|
667
|
+
}
|
|
668
|
+
},
|
|
669
|
+
)
|
|
670
|
+
|
|
671
|
+
io.register_read(
|
|
672
|
+
this.port | NE_RESET,
|
|
673
|
+
this,
|
|
674
|
+
function (this: Ne2k): number {
|
|
675
|
+
dbg_log('Read reset', LOG_NET)
|
|
676
|
+
this.do_interrupt(ENISR_RESET)
|
|
677
|
+
return 0
|
|
678
|
+
},
|
|
679
|
+
)
|
|
680
|
+
|
|
681
|
+
io.register_write(
|
|
682
|
+
this.port | NE_RESET,
|
|
683
|
+
this,
|
|
684
|
+
function (this: Ne2k, data_byte: number): void {
|
|
685
|
+
dbg_log('Write reset: ' + h(data_byte, 2), LOG_NET)
|
|
686
|
+
//this.isr &= ~ENISR_RESET;
|
|
687
|
+
},
|
|
688
|
+
)
|
|
689
|
+
|
|
690
|
+
io.register_read(
|
|
691
|
+
this.port | EN0_STARTPG,
|
|
692
|
+
this,
|
|
693
|
+
function (this: Ne2k): number {
|
|
694
|
+
const pg = this.get_page()
|
|
695
|
+
if (pg === 0) {
|
|
696
|
+
return this.pstart
|
|
697
|
+
} else if (pg === 1) {
|
|
698
|
+
dbg_log('Read pg1/01 (mac[0])', LOG_NET)
|
|
699
|
+
return this.mac[0]
|
|
700
|
+
} else if (pg === 2) {
|
|
701
|
+
return this.pstart
|
|
702
|
+
} else {
|
|
703
|
+
dbg_log('Read pg' + pg + '/01')
|
|
704
|
+
dbg_assert(false)
|
|
705
|
+
return 0
|
|
706
|
+
}
|
|
707
|
+
},
|
|
708
|
+
)
|
|
709
|
+
|
|
710
|
+
io.register_write(
|
|
711
|
+
this.port | EN0_STARTPG,
|
|
712
|
+
this,
|
|
713
|
+
function (this: Ne2k, data_byte: number): void {
|
|
714
|
+
const pg = this.get_page()
|
|
715
|
+
if (pg === 0) {
|
|
716
|
+
dbg_log('start page: ' + h(data_byte, 2), LOG_NET)
|
|
717
|
+
this.pstart = data_byte
|
|
718
|
+
} else if (pg === 1) {
|
|
719
|
+
dbg_log('mac[0] = ' + h(data_byte), LOG_NET)
|
|
720
|
+
this.mac[0] = data_byte
|
|
721
|
+
} else if (pg === 3) {
|
|
722
|
+
dbg_log(
|
|
723
|
+
'Unimplemented: Write pg3/01 (9346CR): ' + h(data_byte),
|
|
724
|
+
LOG_NET,
|
|
725
|
+
)
|
|
726
|
+
} else {
|
|
727
|
+
dbg_log('Write pg' + pg + '/01: ' + h(data_byte), LOG_NET)
|
|
728
|
+
dbg_assert(false)
|
|
729
|
+
}
|
|
730
|
+
},
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
io.register_read(
|
|
734
|
+
this.port | EN0_STOPPG,
|
|
735
|
+
this,
|
|
736
|
+
function (this: Ne2k): number {
|
|
737
|
+
const pg = this.get_page()
|
|
738
|
+
if (pg === 0) {
|
|
739
|
+
return this.pstop
|
|
740
|
+
} else if (pg === 1) {
|
|
741
|
+
dbg_log('Read pg1/02 (mac[1])', LOG_NET)
|
|
742
|
+
return this.mac[1]
|
|
743
|
+
} else if (pg === 2) {
|
|
744
|
+
return this.pstop
|
|
745
|
+
} else {
|
|
746
|
+
dbg_log('Read pg' + pg + '/02', LOG_NET)
|
|
747
|
+
dbg_assert(false)
|
|
748
|
+
return 0
|
|
749
|
+
}
|
|
750
|
+
},
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
io.register_write(
|
|
754
|
+
this.port | EN0_STOPPG,
|
|
755
|
+
this,
|
|
756
|
+
function (this: Ne2k, data_byte: number): void {
|
|
757
|
+
const pg = this.get_page()
|
|
758
|
+
if (pg === 0) {
|
|
759
|
+
dbg_log('stop page: ' + h(data_byte, 2), LOG_NET)
|
|
760
|
+
if (data_byte > this.memory.length >> 8) {
|
|
761
|
+
data_byte = this.memory.length >> 8
|
|
762
|
+
dbg_log(
|
|
763
|
+
'XXX: Adjusting stop page to ' + h(data_byte),
|
|
764
|
+
LOG_NET,
|
|
765
|
+
)
|
|
766
|
+
}
|
|
767
|
+
this.pstop = data_byte
|
|
768
|
+
} else if (pg === 1) {
|
|
769
|
+
dbg_log('mac[1] = ' + h(data_byte), LOG_NET)
|
|
770
|
+
this.mac[1] = data_byte
|
|
771
|
+
} else {
|
|
772
|
+
dbg_log('Write pg' + pg + '/02: ' + h(data_byte), LOG_NET)
|
|
773
|
+
dbg_assert(false)
|
|
774
|
+
}
|
|
775
|
+
},
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
io.register_read(
|
|
779
|
+
this.port | EN0_ISR,
|
|
780
|
+
this,
|
|
781
|
+
function (this: Ne2k): number {
|
|
782
|
+
const pg = this.get_page()
|
|
783
|
+
if (pg === 0) {
|
|
784
|
+
dbg_log('Read isr: ' + h(this.isr, 2), LOG_NET)
|
|
785
|
+
return this.isr
|
|
786
|
+
} else if (pg === 1) {
|
|
787
|
+
dbg_log('Read curpg: ' + h(this.curpg, 2), LOG_NET)
|
|
788
|
+
return this.curpg
|
|
789
|
+
} else {
|
|
790
|
+
dbg_assert(false)
|
|
791
|
+
return 0
|
|
792
|
+
}
|
|
793
|
+
},
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
io.register_write(
|
|
797
|
+
this.port | EN0_ISR,
|
|
798
|
+
this,
|
|
799
|
+
function (this: Ne2k, data_byte: number): void {
|
|
800
|
+
const pg = this.get_page()
|
|
801
|
+
if (pg === 0) {
|
|
802
|
+
// acknowledge interrupts where bit is set
|
|
803
|
+
dbg_log('Write isr: ' + h(data_byte, 2), LOG_NET)
|
|
804
|
+
this.isr &= ~data_byte
|
|
805
|
+
this.update_irq()
|
|
806
|
+
} else if (pg === 1) {
|
|
807
|
+
dbg_log('Write curpg: ' + h(data_byte, 2), LOG_NET)
|
|
808
|
+
this.curpg = data_byte
|
|
809
|
+
} else {
|
|
810
|
+
dbg_assert(false)
|
|
811
|
+
}
|
|
812
|
+
},
|
|
813
|
+
)
|
|
814
|
+
|
|
815
|
+
io.register_write(
|
|
816
|
+
this.port | EN0_TXCR,
|
|
817
|
+
this,
|
|
818
|
+
function (this: Ne2k, data_byte: number): void {
|
|
819
|
+
const pg = this.get_page()
|
|
820
|
+
if (pg === 0) {
|
|
821
|
+
this.txcr = data_byte
|
|
822
|
+
dbg_log('Write tx config: ' + h(data_byte, 2), LOG_NET)
|
|
823
|
+
} else {
|
|
824
|
+
dbg_log(
|
|
825
|
+
'Unimplemented: Write pg' +
|
|
826
|
+
pg +
|
|
827
|
+
'/0d ' +
|
|
828
|
+
h(data_byte, 2),
|
|
829
|
+
LOG_NET,
|
|
830
|
+
)
|
|
831
|
+
}
|
|
832
|
+
},
|
|
833
|
+
)
|
|
834
|
+
|
|
835
|
+
io.register_write(
|
|
836
|
+
this.port | EN0_DCFG,
|
|
837
|
+
this,
|
|
838
|
+
function (this: Ne2k, data_byte: number): void {
|
|
839
|
+
const pg = this.get_page()
|
|
840
|
+
if (pg === 0) {
|
|
841
|
+
dbg_log(
|
|
842
|
+
'Write data configuration: ' + h(data_byte, 2),
|
|
843
|
+
LOG_NET,
|
|
844
|
+
)
|
|
845
|
+
this.dcfg = data_byte
|
|
846
|
+
} else {
|
|
847
|
+
dbg_log(
|
|
848
|
+
'Unimplemented: Write pg' +
|
|
849
|
+
pg +
|
|
850
|
+
'/0e ' +
|
|
851
|
+
h(data_byte, 2),
|
|
852
|
+
LOG_NET,
|
|
853
|
+
)
|
|
854
|
+
}
|
|
855
|
+
},
|
|
856
|
+
)
|
|
857
|
+
|
|
858
|
+
io.register_read(
|
|
859
|
+
this.port | EN0_RCNTLO,
|
|
860
|
+
this,
|
|
861
|
+
function (this: Ne2k): number {
|
|
862
|
+
const pg = this.get_page()
|
|
863
|
+
if (pg === 0) {
|
|
864
|
+
dbg_log('Read pg0/0a', LOG_NET)
|
|
865
|
+
return 0x50
|
|
866
|
+
} else if (pg === 1) {
|
|
867
|
+
dbg_log('Read mar2', LOG_NET)
|
|
868
|
+
return this.mar[2]
|
|
869
|
+
} else {
|
|
870
|
+
dbg_assert(false, 'TODO')
|
|
871
|
+
return 0
|
|
872
|
+
}
|
|
873
|
+
},
|
|
874
|
+
)
|
|
875
|
+
|
|
876
|
+
io.register_write(
|
|
877
|
+
this.port | EN0_RCNTLO,
|
|
878
|
+
this,
|
|
879
|
+
function (this: Ne2k, data_byte: number): void {
|
|
880
|
+
const pg = this.get_page()
|
|
881
|
+
if (pg === 0) {
|
|
882
|
+
dbg_log(
|
|
883
|
+
'Write remote byte count low: ' + h(data_byte, 2),
|
|
884
|
+
LOG_NET,
|
|
885
|
+
)
|
|
886
|
+
this.rcnt = (this.rcnt & 0xff00) | (data_byte & 0xff)
|
|
887
|
+
} else {
|
|
888
|
+
dbg_log(
|
|
889
|
+
'Unimplemented: Write pg' +
|
|
890
|
+
pg +
|
|
891
|
+
'/0a ' +
|
|
892
|
+
h(data_byte, 2),
|
|
893
|
+
LOG_NET,
|
|
894
|
+
)
|
|
895
|
+
}
|
|
896
|
+
},
|
|
897
|
+
)
|
|
898
|
+
|
|
899
|
+
io.register_read(
|
|
900
|
+
this.port | EN0_RCNTHI,
|
|
901
|
+
this,
|
|
902
|
+
function (this: Ne2k): number {
|
|
903
|
+
const pg = this.get_page()
|
|
904
|
+
if (pg === 0) {
|
|
905
|
+
dbg_log('Read pg0/0b', LOG_NET)
|
|
906
|
+
return 0x43
|
|
907
|
+
} else if (pg === 1) {
|
|
908
|
+
dbg_log('Read mar3', LOG_NET)
|
|
909
|
+
return this.mar[3]
|
|
910
|
+
} else {
|
|
911
|
+
dbg_assert(false, 'TODO')
|
|
912
|
+
return 0
|
|
913
|
+
}
|
|
914
|
+
},
|
|
915
|
+
)
|
|
916
|
+
|
|
917
|
+
io.register_write(
|
|
918
|
+
this.port | EN0_RCNTHI,
|
|
919
|
+
this,
|
|
920
|
+
function (this: Ne2k, data_byte: number): void {
|
|
921
|
+
const pg = this.get_page()
|
|
922
|
+
if (pg === 0) {
|
|
923
|
+
dbg_log(
|
|
924
|
+
'Write remote byte count high: ' + h(data_byte, 2),
|
|
925
|
+
LOG_NET,
|
|
926
|
+
)
|
|
927
|
+
this.rcnt = (this.rcnt & 0xff) | ((data_byte << 8) & 0xff00)
|
|
928
|
+
} else {
|
|
929
|
+
dbg_log(
|
|
930
|
+
'Unimplemented: Write pg' +
|
|
931
|
+
pg +
|
|
932
|
+
'/0b ' +
|
|
933
|
+
h(data_byte, 2),
|
|
934
|
+
LOG_NET,
|
|
935
|
+
)
|
|
936
|
+
}
|
|
937
|
+
},
|
|
938
|
+
)
|
|
939
|
+
|
|
940
|
+
io.register_read(
|
|
941
|
+
this.port | EN0_RSARLO,
|
|
942
|
+
this,
|
|
943
|
+
function (this: Ne2k): number {
|
|
944
|
+
const pg = this.get_page()
|
|
945
|
+
if (pg === 0) {
|
|
946
|
+
dbg_log('Read remote start address low', LOG_NET)
|
|
947
|
+
return this.rsar & 0xff
|
|
948
|
+
} else if (pg === 1) {
|
|
949
|
+
dbg_log('Read mar0', LOG_NET)
|
|
950
|
+
return this.mar[0]
|
|
951
|
+
} else {
|
|
952
|
+
dbg_log('Unimplemented: Read pg' + pg + '/08', LOG_NET)
|
|
953
|
+
dbg_assert(false)
|
|
954
|
+
return 0
|
|
955
|
+
}
|
|
956
|
+
},
|
|
957
|
+
)
|
|
958
|
+
|
|
959
|
+
io.register_write(
|
|
960
|
+
this.port | EN0_RSARLO,
|
|
961
|
+
this,
|
|
962
|
+
function (this: Ne2k, data_byte: number): void {
|
|
963
|
+
const pg = this.get_page()
|
|
964
|
+
if (pg === 0) {
|
|
965
|
+
dbg_log(
|
|
966
|
+
'Write remote start address low: ' + h(data_byte, 2),
|
|
967
|
+
LOG_NET,
|
|
968
|
+
)
|
|
969
|
+
this.rsar = (this.rsar & 0xff00) | (data_byte & 0xff)
|
|
970
|
+
} else {
|
|
971
|
+
dbg_log(
|
|
972
|
+
'Unimplemented: Write pg' +
|
|
973
|
+
pg +
|
|
974
|
+
'/08 ' +
|
|
975
|
+
h(data_byte, 2),
|
|
976
|
+
LOG_NET,
|
|
977
|
+
)
|
|
978
|
+
}
|
|
979
|
+
},
|
|
980
|
+
)
|
|
981
|
+
|
|
982
|
+
io.register_read(
|
|
983
|
+
this.port | EN0_RSARHI,
|
|
984
|
+
this,
|
|
985
|
+
function (this: Ne2k): number {
|
|
986
|
+
const pg = this.get_page()
|
|
987
|
+
if (pg === 0) {
|
|
988
|
+
dbg_log('Read remote start address high', LOG_NET)
|
|
989
|
+
return (this.rsar >> 8) & 0xff
|
|
990
|
+
} else if (pg === 1) {
|
|
991
|
+
dbg_log('Read mar1', LOG_NET)
|
|
992
|
+
return this.mar[1]
|
|
993
|
+
} else {
|
|
994
|
+
dbg_log('Unimplemented: Read pg' + pg + '/09', LOG_NET)
|
|
995
|
+
dbg_assert(false)
|
|
996
|
+
return 0
|
|
997
|
+
}
|
|
998
|
+
},
|
|
999
|
+
)
|
|
1000
|
+
|
|
1001
|
+
io.register_write(
|
|
1002
|
+
this.port | EN0_RSARHI,
|
|
1003
|
+
this,
|
|
1004
|
+
function (this: Ne2k, data_byte: number): void {
|
|
1005
|
+
const pg = this.get_page()
|
|
1006
|
+
if (pg === 0) {
|
|
1007
|
+
dbg_log(
|
|
1008
|
+
'Write remote start address low: ' + h(data_byte, 2),
|
|
1009
|
+
LOG_NET,
|
|
1010
|
+
)
|
|
1011
|
+
this.rsar = (this.rsar & 0xff) | ((data_byte << 8) & 0xff00)
|
|
1012
|
+
} else {
|
|
1013
|
+
dbg_log(
|
|
1014
|
+
'Unimplemented: Write pg' +
|
|
1015
|
+
pg +
|
|
1016
|
+
'/09 ' +
|
|
1017
|
+
h(data_byte, 2),
|
|
1018
|
+
LOG_NET,
|
|
1019
|
+
)
|
|
1020
|
+
}
|
|
1021
|
+
},
|
|
1022
|
+
)
|
|
1023
|
+
|
|
1024
|
+
io.register_write(
|
|
1025
|
+
this.port | EN0_IMR,
|
|
1026
|
+
this,
|
|
1027
|
+
function (this: Ne2k, data_byte: number): void {
|
|
1028
|
+
const pg = this.get_page()
|
|
1029
|
+
if (pg === 0) {
|
|
1030
|
+
dbg_log(
|
|
1031
|
+
'Write interrupt mask register: ' +
|
|
1032
|
+
h(data_byte, 2) +
|
|
1033
|
+
' isr=' +
|
|
1034
|
+
h(this.isr, 2),
|
|
1035
|
+
LOG_NET,
|
|
1036
|
+
)
|
|
1037
|
+
this.imr = data_byte
|
|
1038
|
+
this.update_irq()
|
|
1039
|
+
} else {
|
|
1040
|
+
dbg_log(
|
|
1041
|
+
'Unimplemented: Write pg' +
|
|
1042
|
+
pg +
|
|
1043
|
+
'/0f ' +
|
|
1044
|
+
h(data_byte, 2),
|
|
1045
|
+
LOG_NET,
|
|
1046
|
+
)
|
|
1047
|
+
}
|
|
1048
|
+
},
|
|
1049
|
+
)
|
|
1050
|
+
|
|
1051
|
+
io.register_read(
|
|
1052
|
+
this.port | EN0_BOUNDARY,
|
|
1053
|
+
this,
|
|
1054
|
+
function (this: Ne2k): number {
|
|
1055
|
+
const pg = this.get_page()
|
|
1056
|
+
if (pg === 0) {
|
|
1057
|
+
dbg_log('Read boundary: ' + h(this.boundary, 2), LOG_NET)
|
|
1058
|
+
return this.boundary
|
|
1059
|
+
} else if (pg === 1) {
|
|
1060
|
+
dbg_log('Read pg1/03 (mac[2])', LOG_NET)
|
|
1061
|
+
return this.mac[2]
|
|
1062
|
+
} else if (pg === 3) {
|
|
1063
|
+
dbg_log('Unimplemented: Read pg3/03 (CONFIG0)', LOG_NET)
|
|
1064
|
+
return 0
|
|
1065
|
+
} else {
|
|
1066
|
+
dbg_log('Read pg' + pg + '/03', LOG_NET)
|
|
1067
|
+
dbg_assert(false)
|
|
1068
|
+
return 0
|
|
1069
|
+
}
|
|
1070
|
+
},
|
|
1071
|
+
)
|
|
1072
|
+
|
|
1073
|
+
io.register_write(
|
|
1074
|
+
this.port | EN0_BOUNDARY,
|
|
1075
|
+
this,
|
|
1076
|
+
function (this: Ne2k, data_byte: number): void {
|
|
1077
|
+
const pg = this.get_page()
|
|
1078
|
+
if (pg === 0) {
|
|
1079
|
+
dbg_log('Write boundary: ' + h(data_byte, 2), LOG_NET)
|
|
1080
|
+
this.boundary = data_byte
|
|
1081
|
+
} else if (pg === 1) {
|
|
1082
|
+
dbg_log('mac[2] = ' + h(data_byte), LOG_NET)
|
|
1083
|
+
this.mac[2] = data_byte
|
|
1084
|
+
} else {
|
|
1085
|
+
dbg_log('Write pg' + pg + '/03: ' + h(data_byte), LOG_NET)
|
|
1086
|
+
dbg_assert(false)
|
|
1087
|
+
}
|
|
1088
|
+
},
|
|
1089
|
+
)
|
|
1090
|
+
|
|
1091
|
+
io.register_read(
|
|
1092
|
+
this.port | EN0_TSR,
|
|
1093
|
+
this,
|
|
1094
|
+
function (this: Ne2k): number {
|
|
1095
|
+
const pg = this.get_page()
|
|
1096
|
+
if (pg === 0) {
|
|
1097
|
+
return this.tsr
|
|
1098
|
+
} else if (pg === 1) {
|
|
1099
|
+
dbg_log('Read pg1/04 (mac[3])', LOG_NET)
|
|
1100
|
+
return this.mac[3]
|
|
1101
|
+
} else {
|
|
1102
|
+
dbg_log('Read pg' + pg + '/04', LOG_NET)
|
|
1103
|
+
dbg_assert(false)
|
|
1104
|
+
return 0
|
|
1105
|
+
}
|
|
1106
|
+
},
|
|
1107
|
+
)
|
|
1108
|
+
|
|
1109
|
+
io.register_write(
|
|
1110
|
+
this.port | EN0_TPSR,
|
|
1111
|
+
this,
|
|
1112
|
+
function (this: Ne2k, data_byte: number): void {
|
|
1113
|
+
const pg = this.get_page()
|
|
1114
|
+
if (pg === 0) {
|
|
1115
|
+
dbg_log('Write tpsr: ' + h(data_byte, 2), LOG_NET)
|
|
1116
|
+
this.tpsr = data_byte
|
|
1117
|
+
} else if (pg === 1) {
|
|
1118
|
+
dbg_log('mac[3] = ' + h(data_byte), LOG_NET)
|
|
1119
|
+
this.mac[3] = data_byte
|
|
1120
|
+
} else {
|
|
1121
|
+
dbg_log('Write pg' + pg + '/04: ' + h(data_byte), LOG_NET)
|
|
1122
|
+
dbg_assert(false)
|
|
1123
|
+
}
|
|
1124
|
+
},
|
|
1125
|
+
)
|
|
1126
|
+
|
|
1127
|
+
io.register_read(
|
|
1128
|
+
this.port | EN0_TCNTLO,
|
|
1129
|
+
this,
|
|
1130
|
+
function (this: Ne2k): number {
|
|
1131
|
+
const pg = this.get_page()
|
|
1132
|
+
if (pg === 0) {
|
|
1133
|
+
dbg_log(
|
|
1134
|
+
'Unimplemented: Read pg0/05 (NCR: Number of Collisions Register)',
|
|
1135
|
+
LOG_NET,
|
|
1136
|
+
)
|
|
1137
|
+
return 0
|
|
1138
|
+
} else if (pg === 1) {
|
|
1139
|
+
dbg_log('Read pg1/05 (mac[4])', LOG_NET)
|
|
1140
|
+
return this.mac[4]
|
|
1141
|
+
} else if (pg === 3) {
|
|
1142
|
+
dbg_log('Unimplemented: Read pg3/05 (CONFIG2)', LOG_NET)
|
|
1143
|
+
return 0
|
|
1144
|
+
} else {
|
|
1145
|
+
dbg_log('Read pg' + pg + '/05', LOG_NET)
|
|
1146
|
+
dbg_assert(false)
|
|
1147
|
+
return 0
|
|
1148
|
+
}
|
|
1149
|
+
},
|
|
1150
|
+
)
|
|
1151
|
+
|
|
1152
|
+
io.register_write(
|
|
1153
|
+
this.port | EN0_TCNTLO,
|
|
1154
|
+
this,
|
|
1155
|
+
function (this: Ne2k, data_byte: number): void {
|
|
1156
|
+
const pg = this.get_page()
|
|
1157
|
+
if (pg === 0) {
|
|
1158
|
+
dbg_log('Write tcnt low: ' + h(data_byte, 2), LOG_NET)
|
|
1159
|
+
this.tcnt = (this.tcnt & ~0xff) | data_byte
|
|
1160
|
+
} else if (pg === 1) {
|
|
1161
|
+
dbg_log('mac[4] = ' + h(data_byte), LOG_NET)
|
|
1162
|
+
this.mac[4] = data_byte
|
|
1163
|
+
} else if (pg === 3) {
|
|
1164
|
+
dbg_log(
|
|
1165
|
+
'Unimplemented: Write pg3/05 (CONFIG2): ' +
|
|
1166
|
+
h(data_byte),
|
|
1167
|
+
LOG_NET,
|
|
1168
|
+
)
|
|
1169
|
+
} else {
|
|
1170
|
+
dbg_log('Write pg' + pg + '/05: ' + h(data_byte), LOG_NET)
|
|
1171
|
+
dbg_assert(false)
|
|
1172
|
+
}
|
|
1173
|
+
},
|
|
1174
|
+
)
|
|
1175
|
+
|
|
1176
|
+
io.register_read(
|
|
1177
|
+
this.port | EN0_TCNTHI,
|
|
1178
|
+
this,
|
|
1179
|
+
function (this: Ne2k): number {
|
|
1180
|
+
const pg = this.get_page()
|
|
1181
|
+
if (pg === 0) {
|
|
1182
|
+
dbg_assert(false, 'TODO')
|
|
1183
|
+
return 0
|
|
1184
|
+
} else if (pg === 1) {
|
|
1185
|
+
dbg_log('Read pg1/06 (mac[5])', LOG_NET)
|
|
1186
|
+
return this.mac[5]
|
|
1187
|
+
} else if (pg === 3) {
|
|
1188
|
+
dbg_log('Unimplemented: Read pg3/06 (CONFIG3)', LOG_NET)
|
|
1189
|
+
return 0
|
|
1190
|
+
} else {
|
|
1191
|
+
dbg_log('Read pg' + pg + '/06', LOG_NET)
|
|
1192
|
+
dbg_assert(false)
|
|
1193
|
+
return 0
|
|
1194
|
+
}
|
|
1195
|
+
},
|
|
1196
|
+
)
|
|
1197
|
+
|
|
1198
|
+
io.register_write(
|
|
1199
|
+
this.port | EN0_TCNTHI,
|
|
1200
|
+
this,
|
|
1201
|
+
function (this: Ne2k, data_byte: number): void {
|
|
1202
|
+
const pg = this.get_page()
|
|
1203
|
+
if (pg === 0) {
|
|
1204
|
+
dbg_log('Write tcnt high: ' + h(data_byte, 2), LOG_NET)
|
|
1205
|
+
this.tcnt = (this.tcnt & 0xff) | (data_byte << 8)
|
|
1206
|
+
} else if (pg === 1) {
|
|
1207
|
+
dbg_log('mac[5] = ' + h(data_byte), LOG_NET)
|
|
1208
|
+
this.mac[5] = data_byte
|
|
1209
|
+
} else if (pg === 3) {
|
|
1210
|
+
dbg_log(
|
|
1211
|
+
'Unimplemented: Write pg3/06 (CONFIG3): ' +
|
|
1212
|
+
h(data_byte),
|
|
1213
|
+
LOG_NET,
|
|
1214
|
+
)
|
|
1215
|
+
} else {
|
|
1216
|
+
dbg_log('Write pg' + pg + '/06: ' + h(data_byte), LOG_NET)
|
|
1217
|
+
dbg_assert(false)
|
|
1218
|
+
}
|
|
1219
|
+
},
|
|
1220
|
+
)
|
|
1221
|
+
|
|
1222
|
+
io.register_read(
|
|
1223
|
+
this.port | EN0_RSR,
|
|
1224
|
+
this,
|
|
1225
|
+
function (this: Ne2k): number {
|
|
1226
|
+
const pg = this.get_page()
|
|
1227
|
+
if (pg === 0) {
|
|
1228
|
+
return 1 | (1 << 3) // receive status ok
|
|
1229
|
+
} else if (pg === 1) {
|
|
1230
|
+
dbg_log('Read mar4', LOG_NET)
|
|
1231
|
+
return this.mar[4]
|
|
1232
|
+
} else {
|
|
1233
|
+
dbg_log('Unimplemented: Read pg' + pg + '/0c', LOG_NET)
|
|
1234
|
+
dbg_assert(false)
|
|
1235
|
+
return 0
|
|
1236
|
+
}
|
|
1237
|
+
},
|
|
1238
|
+
)
|
|
1239
|
+
|
|
1240
|
+
io.register_write(
|
|
1241
|
+
this.port | EN0_RXCR,
|
|
1242
|
+
this,
|
|
1243
|
+
function (this: Ne2k, data_byte: number): void {
|
|
1244
|
+
const pg = this.get_page()
|
|
1245
|
+
if (pg === 0) {
|
|
1246
|
+
dbg_log(
|
|
1247
|
+
'RX configuration reg write: ' + h(data_byte, 2),
|
|
1248
|
+
LOG_NET,
|
|
1249
|
+
)
|
|
1250
|
+
this.rxcr = data_byte
|
|
1251
|
+
} else {
|
|
1252
|
+
dbg_log(
|
|
1253
|
+
'Unimplemented: Write pg' + pg + '/0c: ' + h(data_byte),
|
|
1254
|
+
LOG_NET,
|
|
1255
|
+
)
|
|
1256
|
+
}
|
|
1257
|
+
},
|
|
1258
|
+
)
|
|
1259
|
+
|
|
1260
|
+
io.register_read(
|
|
1261
|
+
this.port | NE_DATAPORT | 0,
|
|
1262
|
+
this,
|
|
1263
|
+
this.data_port_read8,
|
|
1264
|
+
this.data_port_read16,
|
|
1265
|
+
this.data_port_read32,
|
|
1266
|
+
)
|
|
1267
|
+
io.register_write(
|
|
1268
|
+
this.port | NE_DATAPORT | 0,
|
|
1269
|
+
this,
|
|
1270
|
+
this.data_port_write16,
|
|
1271
|
+
this.data_port_write16,
|
|
1272
|
+
this.data_port_write32,
|
|
1273
|
+
)
|
|
1274
|
+
|
|
1275
|
+
if (use_pci) {
|
|
1276
|
+
cpu.devices.pci.register_device(this)
|
|
1277
|
+
}
|
|
1278
|
+
}
|
|
1279
|
+
|
|
1280
|
+
get_state(): (number | Uint8Array)[] {
|
|
1281
|
+
const state: (number | Uint8Array)[] = []
|
|
1282
|
+
|
|
1283
|
+
state[0] = this.isr
|
|
1284
|
+
state[1] = this.imr
|
|
1285
|
+
state[2] = this.cr
|
|
1286
|
+
state[3] = this.dcfg
|
|
1287
|
+
state[4] = this.rcnt
|
|
1288
|
+
state[5] = this.tcnt
|
|
1289
|
+
state[6] = this.tpsr
|
|
1290
|
+
state[7] = this.rsar
|
|
1291
|
+
state[8] = this.pstart
|
|
1292
|
+
state[9] = this.curpg
|
|
1293
|
+
state[10] = this.boundary
|
|
1294
|
+
state[11] = this.pstop
|
|
1295
|
+
state[12] = this.rxcr
|
|
1296
|
+
state[13] = this.txcr
|
|
1297
|
+
state[14] = this.tsr
|
|
1298
|
+
state[15] = this.mac
|
|
1299
|
+
state[16] = this.memory
|
|
1300
|
+
|
|
1301
|
+
return state
|
|
1302
|
+
}
|
|
1303
|
+
|
|
1304
|
+
set_state(state: any[]): void {
|
|
1305
|
+
this.isr = state[0]
|
|
1306
|
+
this.imr = state[1]
|
|
1307
|
+
this.cr = state[2]
|
|
1308
|
+
this.dcfg = state[3]
|
|
1309
|
+
this.rcnt = state[4]
|
|
1310
|
+
this.tcnt = state[5]
|
|
1311
|
+
this.tpsr = state[6]
|
|
1312
|
+
this.rsar = state[7]
|
|
1313
|
+
this.pstart = state[8]
|
|
1314
|
+
this.curpg = state[9]
|
|
1315
|
+
this.boundary = state[10]
|
|
1316
|
+
this.pstop = state[11]
|
|
1317
|
+
this.rxcr = state[12]
|
|
1318
|
+
this.txcr = state[13]
|
|
1319
|
+
this.tsr = state[14]
|
|
1320
|
+
|
|
1321
|
+
if (this.preserve_mac_from_state_image) {
|
|
1322
|
+
this.mac = state[15]
|
|
1323
|
+
this.memory = state[16]
|
|
1324
|
+
} else if (this.mac_address_translation) {
|
|
1325
|
+
this.mac_address_in_state = state[15]
|
|
1326
|
+
this.memory = state[16]
|
|
1327
|
+
|
|
1328
|
+
dbg_log(
|
|
1329
|
+
'Using mac address translation' +
|
|
1330
|
+
' guest_os_mac=' +
|
|
1331
|
+
format_mac(this.mac_address_in_state!) +
|
|
1332
|
+
' real_mac=' +
|
|
1333
|
+
format_mac(this.mac),
|
|
1334
|
+
LOG_NET,
|
|
1335
|
+
)
|
|
1336
|
+
}
|
|
1337
|
+
this.bus.send('net' + this.id + '-mac', format_mac(this.mac))
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
do_interrupt(ir_mask: number): void {
|
|
1341
|
+
dbg_log('Do interrupt ' + h(ir_mask, 2), LOG_NET)
|
|
1342
|
+
this.isr |= ir_mask
|
|
1343
|
+
this.update_irq()
|
|
1344
|
+
}
|
|
1345
|
+
|
|
1346
|
+
update_irq(): void {
|
|
1347
|
+
if (this.imr & this.isr) {
|
|
1348
|
+
this.pci.raise_irq(this.pci_id)
|
|
1349
|
+
} else {
|
|
1350
|
+
this.pci.lower_irq(this.pci_id)
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
data_port_write(data_byte: number): void {
|
|
1355
|
+
if (NE2K_LOG_VERBOSE) {
|
|
1356
|
+
dbg_log(
|
|
1357
|
+
'Write data port: data=' +
|
|
1358
|
+
h(data_byte & 0xff, 2) +
|
|
1359
|
+
' rsar=' +
|
|
1360
|
+
h(this.rsar, 4) +
|
|
1361
|
+
' rcnt=' +
|
|
1362
|
+
h(this.rcnt, 4),
|
|
1363
|
+
LOG_NET,
|
|
1364
|
+
)
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
if (
|
|
1368
|
+
this.rsar <= 0x10 ||
|
|
1369
|
+
(this.rsar >= START_PAGE << 8 && this.rsar < STOP_PAGE << 8)
|
|
1370
|
+
) {
|
|
1371
|
+
this.memory[this.rsar] = data_byte
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
this.rsar++
|
|
1375
|
+
this.rcnt--
|
|
1376
|
+
|
|
1377
|
+
if (this.rsar >= this.pstop << 8) {
|
|
1378
|
+
this.rsar += (this.pstart - this.pstop) << 8
|
|
1379
|
+
}
|
|
1380
|
+
|
|
1381
|
+
if (this.rcnt === 0) {
|
|
1382
|
+
this.do_interrupt(ENISR_RDC)
|
|
1383
|
+
}
|
|
1384
|
+
}
|
|
1385
|
+
|
|
1386
|
+
data_port_write16(data: number): void {
|
|
1387
|
+
this.data_port_write(data)
|
|
1388
|
+
|
|
1389
|
+
if (this.dcfg & 1) {
|
|
1390
|
+
this.data_port_write(data >> 8)
|
|
1391
|
+
}
|
|
1392
|
+
}
|
|
1393
|
+
|
|
1394
|
+
data_port_write32(data: number): void {
|
|
1395
|
+
this.data_port_write(data)
|
|
1396
|
+
this.data_port_write(data >> 8)
|
|
1397
|
+
this.data_port_write(data >> 16)
|
|
1398
|
+
this.data_port_write(data >> 24)
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
data_port_read(): number {
|
|
1402
|
+
let data = 0
|
|
1403
|
+
|
|
1404
|
+
if (this.rsar < STOP_PAGE << 8) {
|
|
1405
|
+
data = this.memory[this.rsar]
|
|
1406
|
+
}
|
|
1407
|
+
|
|
1408
|
+
if (NE2K_LOG_VERBOSE) {
|
|
1409
|
+
dbg_log(
|
|
1410
|
+
'Read data port: data=' +
|
|
1411
|
+
h(data, 2) +
|
|
1412
|
+
' rsar=' +
|
|
1413
|
+
h(this.rsar, 4) +
|
|
1414
|
+
' rcnt=' +
|
|
1415
|
+
h(this.rcnt, 4),
|
|
1416
|
+
LOG_NET,
|
|
1417
|
+
)
|
|
1418
|
+
}
|
|
1419
|
+
|
|
1420
|
+
this.rsar++
|
|
1421
|
+
this.rcnt--
|
|
1422
|
+
|
|
1423
|
+
if (this.rsar >= this.pstop << 8) {
|
|
1424
|
+
this.rsar += (this.pstart - this.pstop) << 8
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
if (this.rcnt === 0) {
|
|
1428
|
+
this.do_interrupt(ENISR_RDC)
|
|
1429
|
+
}
|
|
1430
|
+
|
|
1431
|
+
return data
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
data_port_read8(): number {
|
|
1435
|
+
return this.data_port_read16() & 0xff
|
|
1436
|
+
}
|
|
1437
|
+
|
|
1438
|
+
data_port_read16(): number {
|
|
1439
|
+
if (this.dcfg & 1) {
|
|
1440
|
+
return this.data_port_read() | (this.data_port_read() << 8)
|
|
1441
|
+
} else {
|
|
1442
|
+
return this.data_port_read()
|
|
1443
|
+
}
|
|
1444
|
+
}
|
|
1445
|
+
|
|
1446
|
+
data_port_read32(): number {
|
|
1447
|
+
return (
|
|
1448
|
+
this.data_port_read() |
|
|
1449
|
+
(this.data_port_read() << 8) |
|
|
1450
|
+
(this.data_port_read() << 16) |
|
|
1451
|
+
(this.data_port_read() << 24)
|
|
1452
|
+
)
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
receive(data: Uint8Array): void {
|
|
1456
|
+
// called from the adapter when data is received over the network
|
|
1457
|
+
|
|
1458
|
+
if (this.cr & 1) {
|
|
1459
|
+
// stop bit set
|
|
1460
|
+
return
|
|
1461
|
+
}
|
|
1462
|
+
|
|
1463
|
+
if (NE2K_LOG_PACKETS) {
|
|
1464
|
+
dump_packet(data, 'receive')
|
|
1465
|
+
}
|
|
1466
|
+
|
|
1467
|
+
this.bus.send('eth-receive-end', [data.length])
|
|
1468
|
+
|
|
1469
|
+
if (this.rxcr & 0x10) {
|
|
1470
|
+
// promiscuous
|
|
1471
|
+
} else if (
|
|
1472
|
+
this.rxcr & 4 &&
|
|
1473
|
+
data[0] === 0xff &&
|
|
1474
|
+
data[1] === 0xff &&
|
|
1475
|
+
data[2] === 0xff &&
|
|
1476
|
+
data[3] === 0xff &&
|
|
1477
|
+
data[4] === 0xff &&
|
|
1478
|
+
data[5] === 0xff
|
|
1479
|
+
) {
|
|
1480
|
+
// broadcast
|
|
1481
|
+
} else if (this.rxcr & 8 && (data[0] & 1) === 1) {
|
|
1482
|
+
// multicast
|
|
1483
|
+
// XXX
|
|
1484
|
+
return
|
|
1485
|
+
} else if (
|
|
1486
|
+
data[0] === this.mac[0] &&
|
|
1487
|
+
data[1] === this.mac[1] &&
|
|
1488
|
+
data[2] === this.mac[2] &&
|
|
1489
|
+
data[3] === this.mac[3] &&
|
|
1490
|
+
data[4] === this.mac[4] &&
|
|
1491
|
+
data[5] === this.mac[5]
|
|
1492
|
+
) {
|
|
1493
|
+
// unicast match, intentionally empty
|
|
1494
|
+
} else {
|
|
1495
|
+
return
|
|
1496
|
+
}
|
|
1497
|
+
|
|
1498
|
+
if (this.mac_address_in_state) {
|
|
1499
|
+
data = new Uint8Array(data) // make a copy
|
|
1500
|
+
translate_mac_address(data, this.mac, this.mac_address_in_state)
|
|
1501
|
+
}
|
|
1502
|
+
|
|
1503
|
+
const packet_length = Math.max(60, data.length)
|
|
1504
|
+
|
|
1505
|
+
const offset = this.curpg << 8
|
|
1506
|
+
const total_length = packet_length + 4
|
|
1507
|
+
const data_start = offset + 4
|
|
1508
|
+
let next = this.curpg + 1 + (total_length >> 8)
|
|
1509
|
+
|
|
1510
|
+
const end = offset + total_length
|
|
1511
|
+
|
|
1512
|
+
const needed = 1 + (total_length >> 8)
|
|
1513
|
+
|
|
1514
|
+
// boundary == curpg interpreted as ringbuffer empty
|
|
1515
|
+
const available =
|
|
1516
|
+
this.boundary > this.curpg
|
|
1517
|
+
? this.boundary - this.curpg
|
|
1518
|
+
: this.pstop - this.curpg + this.boundary - this.pstart
|
|
1519
|
+
|
|
1520
|
+
if (
|
|
1521
|
+
available < needed &&
|
|
1522
|
+
this.boundary !== 0 // XXX: ReactOS sets this to 0 initially and never updates it unless it receives a packet
|
|
1523
|
+
) {
|
|
1524
|
+
dbg_log(
|
|
1525
|
+
'Buffer full, dropping packet pstart=' +
|
|
1526
|
+
h(this.pstart) +
|
|
1527
|
+
' pstop=' +
|
|
1528
|
+
h(this.pstop) +
|
|
1529
|
+
' curpg=' +
|
|
1530
|
+
h(this.curpg) +
|
|
1531
|
+
' needed=' +
|
|
1532
|
+
h(needed) +
|
|
1533
|
+
' boundary=' +
|
|
1534
|
+
h(this.boundary) +
|
|
1535
|
+
' available=' +
|
|
1536
|
+
h(available),
|
|
1537
|
+
LOG_NET,
|
|
1538
|
+
)
|
|
1539
|
+
return
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
if (end > this.pstop << 8) {
|
|
1543
|
+
// Shouldn't happen because at this size it can't cross a page,
|
|
1544
|
+
// so we can skip filling with zeroes
|
|
1545
|
+
dbg_assert(data.length >= 60)
|
|
1546
|
+
|
|
1547
|
+
const cut = (this.pstop << 8) - data_start
|
|
1548
|
+
dbg_assert(cut >= 0)
|
|
1549
|
+
|
|
1550
|
+
this.memory.set(data.subarray(0, cut), data_start)
|
|
1551
|
+
this.memory.set(data.subarray(cut), this.pstart << 8)
|
|
1552
|
+
dbg_log('rcv cut=' + h(cut), LOG_NET)
|
|
1553
|
+
} else {
|
|
1554
|
+
this.memory.set(data, data_start)
|
|
1555
|
+
|
|
1556
|
+
if (data.length < 60) {
|
|
1557
|
+
this.memory.fill(0, data_start + data.length, data_start + 60)
|
|
1558
|
+
}
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
if (next >= this.pstop) {
|
|
1562
|
+
next += this.pstart - this.pstop
|
|
1563
|
+
}
|
|
1564
|
+
|
|
1565
|
+
// write packet header
|
|
1566
|
+
this.memory[offset] = ENRSR_RXOK // status
|
|
1567
|
+
this.memory[offset + 1] = next
|
|
1568
|
+
this.memory[offset + 2] = total_length
|
|
1569
|
+
this.memory[offset + 3] = total_length >> 8
|
|
1570
|
+
|
|
1571
|
+
this.curpg = next
|
|
1572
|
+
|
|
1573
|
+
dbg_log(
|
|
1574
|
+
'rcv offset=' +
|
|
1575
|
+
h(offset) +
|
|
1576
|
+
' len=' +
|
|
1577
|
+
h(total_length) +
|
|
1578
|
+
' next=' +
|
|
1579
|
+
h(next),
|
|
1580
|
+
LOG_NET,
|
|
1581
|
+
)
|
|
1582
|
+
|
|
1583
|
+
this.do_interrupt(ENISR_RX)
|
|
1584
|
+
}
|
|
1585
|
+
|
|
1586
|
+
get_page(): number {
|
|
1587
|
+
return (this.cr >> 6) & 3
|
|
1588
|
+
}
|
|
1589
|
+
}
|