@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.
Files changed (111) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.MIT +22 -0
  3. package/Readme.md +237 -0
  4. package/dist/v86.browser.js +26666 -0
  5. package/dist/v86.browser.js.map +7 -0
  6. package/dist/v86.js +26632 -0
  7. package/dist/v86.js.map +7 -0
  8. package/gen/generate_analyzer.ts +512 -0
  9. package/gen/generate_interpreter.ts +522 -0
  10. package/gen/generate_jit.ts +624 -0
  11. package/gen/rust_ast.ts +107 -0
  12. package/gen/util.ts +35 -0
  13. package/gen/x86_table.ts +1836 -0
  14. package/lib/9p.ts +1547 -0
  15. package/lib/filesystem.ts +1879 -0
  16. package/lib/marshall.ts +168 -0
  17. package/lib/softfloat/softfloat.c +32501 -0
  18. package/lib/zstd/zstddeclib.c +13520 -0
  19. package/package.json +75 -0
  20. package/src/acpi.ts +267 -0
  21. package/src/browser/dummy_screen.ts +106 -0
  22. package/src/browser/fake_network.ts +1771 -0
  23. package/src/browser/fetch_network.ts +361 -0
  24. package/src/browser/filestorage.ts +124 -0
  25. package/src/browser/inbrowser_network.ts +57 -0
  26. package/src/browser/keyboard.ts +564 -0
  27. package/src/browser/main.ts +3415 -0
  28. package/src/browser/mouse.ts +255 -0
  29. package/src/browser/network.ts +142 -0
  30. package/src/browser/print_stats.ts +336 -0
  31. package/src/browser/screen.ts +978 -0
  32. package/src/browser/serial.ts +316 -0
  33. package/src/browser/speaker.ts +1223 -0
  34. package/src/browser/starter.ts +1688 -0
  35. package/src/browser/wisp_network.ts +332 -0
  36. package/src/browser/worker_bus.ts +64 -0
  37. package/src/buffer.ts +652 -0
  38. package/src/bus.ts +78 -0
  39. package/src/const.ts +128 -0
  40. package/src/cpu.ts +2891 -0
  41. package/src/dma.ts +474 -0
  42. package/src/elf.ts +251 -0
  43. package/src/floppy.ts +1778 -0
  44. package/src/ide.ts +3455 -0
  45. package/src/io.ts +504 -0
  46. package/src/iso9660.ts +317 -0
  47. package/src/kernel.ts +250 -0
  48. package/src/lib.ts +645 -0
  49. package/src/log.ts +149 -0
  50. package/src/main.ts +199 -0
  51. package/src/ne2k.ts +1589 -0
  52. package/src/pci.ts +815 -0
  53. package/src/pit.ts +406 -0
  54. package/src/ps2.ts +820 -0
  55. package/src/rtc.ts +537 -0
  56. package/src/rust/analysis.rs +101 -0
  57. package/src/rust/codegen.rs +2660 -0
  58. package/src/rust/config.rs +3 -0
  59. package/src/rust/control_flow.rs +425 -0
  60. package/src/rust/cpu/apic.rs +658 -0
  61. package/src/rust/cpu/arith.rs +1207 -0
  62. package/src/rust/cpu/call_indirect.rs +2 -0
  63. package/src/rust/cpu/cpu.rs +4501 -0
  64. package/src/rust/cpu/fpu.rs +923 -0
  65. package/src/rust/cpu/global_pointers.rs +112 -0
  66. package/src/rust/cpu/instructions.rs +2486 -0
  67. package/src/rust/cpu/instructions_0f.rs +5261 -0
  68. package/src/rust/cpu/ioapic.rs +316 -0
  69. package/src/rust/cpu/memory.rs +351 -0
  70. package/src/rust/cpu/misc_instr.rs +613 -0
  71. package/src/rust/cpu/mod.rs +16 -0
  72. package/src/rust/cpu/modrm.rs +133 -0
  73. package/src/rust/cpu/pic.rs +402 -0
  74. package/src/rust/cpu/sse_instr.rs +361 -0
  75. package/src/rust/cpu/string.rs +701 -0
  76. package/src/rust/cpu/vga.rs +175 -0
  77. package/src/rust/cpu_context.rs +69 -0
  78. package/src/rust/dbg.rs +98 -0
  79. package/src/rust/gen/analyzer.rs +3807 -0
  80. package/src/rust/gen/analyzer0f.rs +3992 -0
  81. package/src/rust/gen/interpreter.rs +4447 -0
  82. package/src/rust/gen/interpreter0f.rs +5404 -0
  83. package/src/rust/gen/jit.rs +5080 -0
  84. package/src/rust/gen/jit0f.rs +5547 -0
  85. package/src/rust/gen/mod.rs +14 -0
  86. package/src/rust/jit.rs +2443 -0
  87. package/src/rust/jit_instructions.rs +7881 -0
  88. package/src/rust/js_api.rs +6 -0
  89. package/src/rust/leb.rs +46 -0
  90. package/src/rust/lib.rs +29 -0
  91. package/src/rust/modrm.rs +330 -0
  92. package/src/rust/opstats.rs +249 -0
  93. package/src/rust/page.rs +15 -0
  94. package/src/rust/paging.rs +25 -0
  95. package/src/rust/prefix.rs +15 -0
  96. package/src/rust/profiler.rs +155 -0
  97. package/src/rust/regs.rs +38 -0
  98. package/src/rust/softfloat.rs +286 -0
  99. package/src/rust/state_flags.rs +27 -0
  100. package/src/rust/wasmgen/mod.rs +2 -0
  101. package/src/rust/wasmgen/wasm_builder.rs +1047 -0
  102. package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
  103. package/src/rust/zstd.rs +105 -0
  104. package/src/sb16.ts +1928 -0
  105. package/src/state.ts +359 -0
  106. package/src/uart.ts +472 -0
  107. package/src/vga.ts +2791 -0
  108. package/src/virtio.ts +1756 -0
  109. package/src/virtio_balloon.ts +273 -0
  110. package/src/virtio_console.ts +372 -0
  111. 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
+ }