@aptre/v86 0.5.0 → 0.6.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/lib/9p.ts CHANGED
@@ -311,7 +311,7 @@ export class Virtio9p {
311
311
 
312
312
  const state = { offset: 0 }
313
313
  const header = marshall.Unmarshall(['w', 'b', 'h'], buffer, state)
314
- let size = header[0]
314
+ let size
315
315
  const id = header[1]
316
316
  const tag = header[2]
317
317
 
@@ -382,7 +382,7 @@ export class Virtio9p {
382
382
  )
383
383
 
384
384
  if (ret < 0) {
385
- let error_message = ''
385
+ let error_message: string
386
386
  if (ret === -EPERM)
387
387
  error_message = 'Operation not permitted'
388
388
  else {
@@ -984,7 +984,7 @@ export class Virtio9p {
984
984
  newname,
985
985
  )
986
986
  if (ret < 0) {
987
- let error_message = ''
987
+ let error_message: string
988
988
  if (ret === -ENOENT)
989
989
  error_message = 'No such file or directory'
990
990
  else if (ret === -EPERM)
@@ -1040,7 +1040,7 @@ export class Virtio9p {
1040
1040
  }
1041
1041
  const ret = this.fs.Unlink(this.fids[dirfd].inodeid, name)
1042
1042
  if (ret < 0) {
1043
- let error_message = ''
1043
+ let error_message: string
1044
1044
  if (ret === -ENOTEMPTY)
1045
1045
  error_message = 'Directory not empty'
1046
1046
  else if (ret === -EPERM)
package/lib/filesystem.ts CHANGED
@@ -1213,7 +1213,7 @@ export class FS {
1213
1213
  let parentid = -1
1214
1214
  let id = 0
1215
1215
  let forward_path: string | null = null
1216
- let i = 0
1216
+ let i
1217
1217
  for (i = 0; i < n; i++) {
1218
1218
  parentid = id
1219
1219
  id = this.Search(parentid, walk[i])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aptre/v86",
3
- "version": "0.5.0",
3
+ "version": "0.6.0",
4
4
  "license": "BSD-2-Clause",
5
5
  "description": "x86 PC emulator and x86-to-wasm JIT, running in the browser",
6
6
  "homepage": "https://github.com/aperturerobotics/v86",
@@ -56,20 +56,20 @@
56
56
  "./{src,lib,gen,tests}/**/*.{ts,js}": "prettier --config .prettierrc.yaml --write"
57
57
  },
58
58
  "devDependencies": {
59
- "@eslint/js": "^9.27.0",
59
+ "@eslint/js": "^10.0.0",
60
60
  "@types/node": "^25.5.0",
61
61
  "@typescript/native-preview": "^7.0.0-dev.20250601",
62
- "esbuild": "^0.25.4",
63
- "eslint": "^9.27.0",
62
+ "esbuild": "^0.27.0",
63
+ "eslint": "^10.0.0",
64
64
  "eslint-config-prettier": "^10.1.5",
65
65
  "eslint-plugin-unused-imports": "^4.4.1",
66
- "globals": "^16.1.0",
66
+ "globals": "^17.0.0",
67
67
  "husky": "^9.1.7",
68
68
  "lint-staged": "^16.0.0",
69
69
  "prettier": "^3.5.3",
70
70
  "rimraf": "^6.0.1",
71
- "typescript": "^5.8.3",
71
+ "typescript": "^6.0.0",
72
72
  "typescript-eslint": "^8.32.1",
73
- "vitest": "^3.1.4"
73
+ "vitest": "^4.0.0"
74
74
  }
75
75
  }
@@ -45,21 +45,11 @@ type FileDescriptor = any
45
45
 
46
46
  type AutoStep = any
47
47
 
48
- class FileExistsError {
49
- message: string
48
+ class FileNotFoundError extends Error {
50
49
  constructor(message?: string) {
51
- this.message = message || 'File already exists'
50
+ super(message || 'File not found')
52
51
  }
53
52
  }
54
- FileExistsError.prototype = Error.prototype
55
-
56
- class FileNotFoundError {
57
- message: string
58
- constructor(message?: string) {
59
- this.message = message || 'File not found'
60
- }
61
- }
62
- FileNotFoundError.prototype = Error.prototype
63
53
 
64
54
  /**
65
55
  * Constructor for emulator instances.
@@ -122,7 +112,7 @@ export class V86 {
122
112
  initial: WASM_TABLE_SIZE + WASM_TABLE_OFFSET,
123
113
  })
124
114
 
125
- const wasm_shared_funcs = {
115
+ const wasm_shared_funcs: Record<string, any> = {
126
116
  cpu_exception_hook: (n: number) => this.cpu_exception_hook(n),
127
117
 
128
118
  run_hardware_timers: function (a: any, t: any) {
@@ -231,8 +221,6 @@ export class V86 {
231
221
 
232
222
  if (!wasm_fn) {
233
223
  wasm_fn = (env: any) => {
234
- /* global __dirname */
235
-
236
224
  return new Promise((resolve) => {
237
225
  let v86_bin = DEBUG ? 'v86-debug.wasm' : 'v86.wasm'
238
226
  let v86_bin_fallback = 'v86-fallback.wasm'
@@ -243,12 +231,11 @@ export class V86 {
243
231
  'v86.wasm',
244
232
  'v86-fallback.wasm',
245
233
  )
246
- } else if (
247
- typeof window === 'undefined' &&
248
- typeof __dirname === 'string'
249
- ) {
250
- v86_bin = __dirname + '/' + v86_bin
251
- v86_bin_fallback = __dirname + '/' + v86_bin_fallback
234
+ } else if (typeof window === 'undefined') {
235
+ // Node/Bun: resolve WASM relative to project root build/
236
+ const root = new URL('../../', import.meta.url).pathname
237
+ v86_bin = root + 'build/' + v86_bin
238
+ v86_bin_fallback = root + 'build/' + v86_bin_fallback
252
239
  } else {
253
240
  v86_bin = 'build/' + v86_bin
254
241
  v86_bin_fallback = 'build/' + v86_bin_fallback
@@ -299,6 +286,7 @@ export class V86 {
299
286
  const emulator = (this.v86 = new v86(this.emulator_bus, {
300
287
  exports,
301
288
  wasm_table,
289
+ wasm_memory,
302
290
  }))
303
291
  cpu = emulator.cpu
304
292
 
@@ -356,7 +344,9 @@ export class V86 {
356
344
  settings.mac_address_translation = options.mac_address_translation
357
345
  settings.cpuid_level = options.cpuid_level
358
346
  settings.virtio_balloon = options.virtio_balloon
347
+ settings.virtio_mem = options.virtio_mem
359
348
  settings.virtio_console = !!options.virtio_console
349
+ settings.virtio_v86fs = !!options.virtio_v86fs
360
350
 
361
351
  const relay_url =
362
352
  options.network_relay_url ||
package/src/const.ts CHANGED
@@ -120,6 +120,8 @@ export const WASM_TABLE_SIZE = 900
120
120
 
121
121
  export const WASM_TABLE_OFFSET = 1024
122
122
 
123
+ export const WASM_PAGE_SIZE = 65536
124
+
123
125
  export const MIXER_CHANNEL_LEFT = 0
124
126
  export const MIXER_CHANNEL_RIGHT = 1
125
127
  export const MIXER_CHANNEL_BOTH = 2
package/src/cpu.ts CHANGED
@@ -46,6 +46,7 @@ import {
46
46
  FLAG_DIRECTION,
47
47
  FLAG_OVERFLOW,
48
48
  FLAG_PARITY,
49
+ WASM_PAGE_SIZE,
49
50
  } from './const.js'
50
51
  import { h, view, pads, Bitmap, dump_file } from './lib.js'
51
52
  import { dbg_assert, dbg_log } from './log.js'
@@ -67,6 +68,8 @@ import { IDEController } from './ide.js'
67
68
  import { VirtioNet } from './virtio_net.js'
68
69
  import { VGAScreen } from './vga.js'
69
70
  import { VirtioBalloon } from './virtio_balloon.js'
71
+ import { VirtioMem } from './virtio_mem.js'
72
+ import { VirtioV86FS } from './virtio_v86fs.js'
70
73
  import { Virtio9p, Virtio9pHandler, Virtio9pProxy } from '../lib/9p.js'
71
74
 
72
75
  import { load_kernel } from './kernel.js'
@@ -129,6 +132,8 @@ interface CPUDevices {
129
132
  virtio_console: VirtioConsole
130
133
  virtio_net: VirtioNet
131
134
  virtio_balloon: VirtioBalloon
135
+ virtio_mem: VirtioMem
136
+ virtio_v86fs: VirtioV86FS
132
137
  }
133
138
 
134
139
  interface OptionRom {
@@ -139,6 +144,7 @@ interface OptionRom {
139
144
  interface WasmModule {
140
145
  exports: Record<string, any>
141
146
  wasm_table: WebAssembly.Table
147
+ wasm_memory: WebAssembly.Memory
142
148
  }
143
149
 
144
150
  export class CPU {
@@ -268,7 +274,6 @@ export class CPU {
268
274
  get_eflags!: () => number
269
275
  handle_irqs!: () => void
270
276
  main_loop!: () => number
271
- reboot_internal!: () => void
272
277
  set_jit_config!: (a: number, b: number) => void
273
278
 
274
279
  read8!: (addr: number) => number
@@ -338,12 +343,11 @@ export class CPU {
338
343
  this.name = 'cpu'
339
344
  this.stop_idling = stop_idling
340
345
  this.wm = wm
346
+ this.wasm_memory = wm.wasm_memory
341
347
  this.wasm_patch()
342
348
  this.create_jit_imports()
343
349
 
344
- const memory = this.wm.exports['memory']
345
-
346
- this.wasm_memory = memory
350
+ const memory = this.wasm_memory
347
351
 
348
352
  this.memory_size = view(Uint32Array, memory, 812, 1)
349
353
 
@@ -576,7 +580,7 @@ export class CPU {
576
580
 
577
581
  const jit_imports = Object.create(null)
578
582
 
579
- jit_imports['m'] = this.wm.exports['memory']
583
+ jit_imports['m'] = this.wasm_memory
580
584
 
581
585
  for (const name of Object.keys(this.wm.exports)) {
582
586
  if (
@@ -611,8 +615,6 @@ export class CPU {
611
615
 
612
616
  this.main_loop = get_import('main_loop')
613
617
 
614
- this.reboot_internal = get_import('reboot_internal')
615
-
616
618
  this.set_jit_config = get_import('set_jit_config')
617
619
 
618
620
  this.read8 = get_import('read8')
@@ -812,6 +814,8 @@ export class CPU {
812
814
  state[86] = this.last_result
813
815
  state[87] = this.fpu_status_word
814
816
  state[88] = this.mxcsr
817
+ state[89] = this.devices.virtio_mem
818
+ state[90] = this.devices.virtio_v86fs
815
819
 
816
820
  return state
817
821
  }
@@ -882,6 +886,26 @@ export class CPU {
882
886
  )
883
887
  }
884
888
 
889
+ resize_memory(new_size: number): void {
890
+ const mem8_offset = this.mem8.byteOffset
891
+ const needed_total = mem8_offset + new_size
892
+ const current_buffer = this.wasm_memory.buffer.byteLength
893
+ if (needed_total > current_buffer) {
894
+ const grow_pages = Math.ceil(
895
+ (needed_total - current_buffer) / WASM_PAGE_SIZE,
896
+ )
897
+ this.wasm_memory.grow(grow_pages)
898
+ }
899
+ this.mem8 = view(Uint8Array, this.wasm_memory, mem8_offset, new_size)
900
+ this.mem32s = view(
901
+ Int32Array,
902
+ this.wasm_memory,
903
+ mem8_offset,
904
+ new_size >> 2,
905
+ )
906
+ this.memory_size[0] = new_size
907
+ }
908
+
885
909
  set_state(state: any[]): void {
886
910
  this.memory_size[0] = state[0]
887
911
 
@@ -1007,6 +1031,10 @@ export class CPU {
1007
1031
  this.devices.virtio_net.set_state(state[83])
1008
1032
  if (this.devices.virtio_balloon)
1009
1033
  this.devices.virtio_balloon.set_state(state[84])
1034
+ if (this.devices.virtio_mem && state[89])
1035
+ this.devices.virtio_mem.set_state(state[89])
1036
+ if (this.devices.virtio_v86fs && state[90])
1037
+ this.devices.virtio_v86fs.set_state(state[90])
1010
1038
 
1011
1039
  this.fw_value = state[62]
1012
1040
 
@@ -1521,6 +1549,19 @@ export class CPU {
1521
1549
  device_bus,
1522
1550
  )
1523
1551
  }
1552
+ if (settings.virtio_mem) {
1553
+ const mem_cfg = settings.virtio_mem
1554
+ this.devices.virtio_mem = new VirtioMem(
1555
+ this,
1556
+ device_bus,
1557
+ mem_cfg.region_addr,
1558
+ mem_cfg.region_size,
1559
+ mem_cfg.block_size,
1560
+ )
1561
+ }
1562
+ if (settings.virtio_v86fs) {
1563
+ this.devices.virtio_v86fs = new VirtioV86FS(this, device_bus)
1564
+ }
1524
1565
 
1525
1566
  this.devices.sb16 = new SB16(this, device_bus)
1526
1567
  }
@@ -1858,7 +1899,6 @@ export class CPU {
1858
1899
  cpu.write32(multiboot_data + 4, ramdisk_top) // mod_end
1859
1900
  cpu.write32(multiboot_data + 8, 0) // string
1860
1901
  cpu.write32(multiboot_data + 12, 0) // reserved
1861
- multiboot_data += 16
1862
1902
 
1863
1903
  dbg_assert(ramdisk_top < cpu.memory_size[0])
1864
1904
 
package/src/pci.ts CHANGED
@@ -13,7 +13,7 @@ interface PCICpu {
13
13
  io: IO
14
14
  device_raise_irq(irq: number): void
15
15
  device_lower_irq(irq: number): void
16
- reboot_internal(): void
16
+ reboot(): void
17
17
  }
18
18
 
19
19
  // Mirrors IOPortEntry from io.ts (not exported there).
@@ -177,7 +177,7 @@ export class PCI {
177
177
  (out_byte & 0x06) === 0x06
178
178
  ) {
179
179
  dbg_log('CPU reboot via PCI')
180
- cpu.reboot_internal()
180
+ cpu.reboot()
181
181
  return
182
182
  }
183
183
 
@@ -304,6 +304,7 @@ export class PCI {
304
304
  0x00,
305
305
  0x00,
306
306
  0x00,
307
+ 0x00,
307
308
  PAM0,
308
309
  0x00,
309
310
  0x00,
@@ -711,7 +712,11 @@ export class PCI {
711
712
 
712
713
  // convert bytewise notation from lspci to double words
713
714
  const space = new Int32Array(64)
714
- space.set(new Int32Array(new Uint8Array(device.pci_space).buffer))
715
+ const pci_bytes = new Uint8Array(device.pci_space)
716
+ const aligned_len = pci_bytes.length & ~3
717
+ if (aligned_len > 0) {
718
+ space.set(new Int32Array(pci_bytes.buffer, 0, aligned_len >> 2))
719
+ }
715
720
  this.device_spaces[device_id] = space
716
721
  this.devices[device_id] = device
717
722
 
package/src/ps2.ts CHANGED
@@ -11,7 +11,7 @@ interface PS2Cpu {
11
11
  io: IO
12
12
  device_raise_irq(irq: number): void
13
13
  device_lower_irq(irq: number): void
14
- reboot_internal(): void
14
+ reboot(): void
15
15
  }
16
16
 
17
17
  type PS2State = [
@@ -808,7 +808,7 @@ export class PS2 {
808
808
  break
809
809
  case 0xfe:
810
810
  dbg_log('CPU reboot via PS2')
811
- this.cpu.reboot_internal()
811
+ this.cpu.reboot()
812
812
  break
813
813
  default:
814
814
  dbg_log(
package/src/virtio.ts CHANGED
@@ -520,18 +520,6 @@ export class VirtIO {
520
520
  )
521
521
  }
522
522
 
523
- if (
524
- data &
525
- ~this.device_status &
526
- VIRTIO_STATUS_DRIVER_OK &&
527
- this.device_status &
528
- VIRTIO_STATUS_DEVICE_NEEDS_RESET
529
- ) {
530
- // We couldn't notify NEEDS_RESET earlier because DRIVER_OK was not set.
531
- // Now it has been set, notify now.
532
- this.notify_config_changes()
533
- }
534
-
535
523
  // Don't set FEATURES_OK if our device doesn't support requested features.
536
524
  if (!this.features_ok) {
537
525
  if (DEBUG && data & VIRTIO_STATUS_FEATURES_OK) {
@@ -540,13 +528,17 @@ export class VirtIO {
540
528
  data &= ~VIRTIO_STATUS_FEATURES_OK
541
529
  }
542
530
 
531
+ const prev_status = this.device_status
543
532
  this.device_status = data
544
533
 
545
- if (
546
- data &
547
- ~this.device_status &
548
- VIRTIO_STATUS_DRIVER_OK
549
- ) {
534
+ if (data & ~prev_status & VIRTIO_STATUS_DRIVER_OK) {
535
+ if (
536
+ prev_status & VIRTIO_STATUS_DEVICE_NEEDS_RESET
537
+ ) {
538
+ // We couldn't notify NEEDS_RESET earlier because DRIVER_OK was not set.
539
+ // Now it has been set, notify now.
540
+ this.notify_config_changes()
541
+ }
550
542
  options.on_driver_ok()
551
543
  }
552
544
  },
@@ -837,7 +829,7 @@ export class VirtIO {
837
829
  let cap_next = (this.pci_space[0x34] = 0x40)
838
830
 
839
831
  // Current offset.
840
- let cap_ptr = cap_next
832
+ let cap_ptr
841
833
 
842
834
  for (const cap of capabilities) {
843
835
  const cap_len = VIRTIO_PCI_CAP_LENGTH + cap.extra.length