@aptre/v86 0.6.0 → 0.6.1
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/dist/v86.browser.js +99 -35
- package/dist/v86.browser.js.map +3 -3
- package/dist/v86.js +99 -35
- package/dist/v86.js.map +3 -3
- package/package.json +1 -1
- package/src/browser/starter.ts +42 -2
- package/src/cpu.ts +83 -93
package/package.json
CHANGED
package/src/browser/starter.ts
CHANGED
|
@@ -105,7 +105,23 @@ export class V86 {
|
|
|
105
105
|
|
|
106
106
|
let cpu: any
|
|
107
107
|
|
|
108
|
-
|
|
108
|
+
// Reserve VA upfront so memory.grow() is fast (no realloc+copy).
|
|
109
|
+
// Without maximum, engines must reallocate on every grow which
|
|
110
|
+
// is expensive and can OOM for large guest memories.
|
|
111
|
+
const memory_size = options.memory_size || 64 * 1024 * 1024
|
|
112
|
+
const vga_memory_size = options.vga_memory_size || 8 * 1024 * 1024
|
|
113
|
+
const memory_max =
|
|
114
|
+
options.memory_max || (memory_size + vga_memory_size) * 4
|
|
115
|
+
const WASM_PAGE_SIZE = 65536
|
|
116
|
+
const wasm_initial_pages = 256
|
|
117
|
+
const wasm_max_pages = Math.max(
|
|
118
|
+
wasm_initial_pages,
|
|
119
|
+
Math.min(Math.ceil(memory_max / WASM_PAGE_SIZE), 65536),
|
|
120
|
+
)
|
|
121
|
+
const wasm_memory = new WebAssembly.Memory({
|
|
122
|
+
initial: wasm_initial_pages,
|
|
123
|
+
maximum: wasm_max_pages,
|
|
124
|
+
})
|
|
109
125
|
|
|
110
126
|
const wasm_table = new WebAssembly.Table({
|
|
111
127
|
element: 'anyfunc',
|
|
@@ -113,6 +129,7 @@ export class V86 {
|
|
|
113
129
|
})
|
|
114
130
|
|
|
115
131
|
const wasm_shared_funcs: Record<string, any> = {
|
|
132
|
+
memory: wasm_memory,
|
|
116
133
|
cpu_exception_hook: (n: number) => this.cpu_exception_hook(n),
|
|
117
134
|
|
|
118
135
|
run_hardware_timers: function (a: any, t: any) {
|
|
@@ -280,7 +297,6 @@ export class V86 {
|
|
|
280
297
|
}
|
|
281
298
|
|
|
282
299
|
wasm_fn({ env: wasm_shared_funcs }).then((exports: WasmExports) => {
|
|
283
|
-
wasm_memory = exports.memory
|
|
284
300
|
exports['rust_init']()
|
|
285
301
|
|
|
286
302
|
const emulator = (this.v86 = new v86(this.emulator_bus, {
|
|
@@ -957,6 +973,30 @@ export class V86 {
|
|
|
957
973
|
this.v86.run()
|
|
958
974
|
}
|
|
959
975
|
|
|
976
|
+
/**
|
|
977
|
+
* Grow guest memory to the specified size in bytes. Stops the VM,
|
|
978
|
+
* grows WASM linear memory, updates memory_size, clears the TLB,
|
|
979
|
+
* and resumes execution.
|
|
980
|
+
*/
|
|
981
|
+
async growMemory(newSizeBytes: number): Promise<void> {
|
|
982
|
+
const cpu = this.v86.cpu
|
|
983
|
+
if (newSizeBytes <= cpu.memory_size[0]) {
|
|
984
|
+
return
|
|
985
|
+
}
|
|
986
|
+
|
|
987
|
+
const wasRunning = this.cpu_is_running
|
|
988
|
+
if (wasRunning) {
|
|
989
|
+
await this.stop()
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
cpu.resize_memory(newSizeBytes)
|
|
993
|
+
cpu.full_clear_tlb()
|
|
994
|
+
|
|
995
|
+
if (wasRunning) {
|
|
996
|
+
await this.run()
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
|
|
960
1000
|
/**
|
|
961
1001
|
* Stop emulation. Do nothing if emulator is not running. Can be asynchronous.
|
|
962
1002
|
*/
|
package/src/cpu.ts
CHANGED
|
@@ -155,6 +155,7 @@ export class CPU {
|
|
|
155
155
|
memory_size!: Int32Array
|
|
156
156
|
|
|
157
157
|
mem8: Uint8Array
|
|
158
|
+
mem8_offset: number = 0
|
|
158
159
|
mem32s: Int32Array
|
|
159
160
|
|
|
160
161
|
segment_is_null!: Uint8Array
|
|
@@ -347,148 +348,111 @@ export class CPU {
|
|
|
347
348
|
this.wasm_patch()
|
|
348
349
|
this.create_jit_imports()
|
|
349
350
|
|
|
350
|
-
const memory = this.wasm_memory
|
|
351
|
-
|
|
352
|
-
this.memory_size = view(Uint32Array, memory, 812, 1)
|
|
353
|
-
|
|
354
351
|
this.mem8 = new Uint8Array(0)
|
|
355
352
|
this.mem32s = new Int32Array(this.mem8.buffer)
|
|
356
353
|
|
|
354
|
+
this.rebuild_wasm_views()
|
|
355
|
+
|
|
356
|
+
// @ts-expect-error Devices are populated during init()
|
|
357
|
+
this.devices = {}
|
|
358
|
+
|
|
359
|
+
// managed in io.js
|
|
360
|
+
this.memory_map_read8 = []
|
|
361
|
+
this.memory_map_write8 = []
|
|
362
|
+
this.memory_map_read32 = []
|
|
363
|
+
this.memory_map_write32 = []
|
|
364
|
+
|
|
365
|
+
this.bios = {
|
|
366
|
+
main: null,
|
|
367
|
+
vga: null,
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
this.fpu_stack_empty[0] = 0xff
|
|
371
|
+
this.fpu_stack_ptr[0] = 0
|
|
372
|
+
|
|
373
|
+
this.fpu_control_word[0] = 0x37f
|
|
374
|
+
this.fpu_status_word[0] = 0
|
|
375
|
+
this.fpu_ip[0] = 0
|
|
376
|
+
this.fpu_ip_selector[0] = 0
|
|
377
|
+
this.fpu_opcode[0] = 0
|
|
378
|
+
this.fpu_dp[0] = 0
|
|
379
|
+
this.fpu_dp_selector[0] = 0
|
|
380
|
+
|
|
381
|
+
this.fw_value = []
|
|
382
|
+
this.fw_pointer = 0
|
|
383
|
+
this.option_roms = []
|
|
384
|
+
|
|
385
|
+
this.io = undefined!
|
|
386
|
+
|
|
387
|
+
this.bus = bus
|
|
388
|
+
|
|
389
|
+
this.set_tsc(0, 0)
|
|
390
|
+
|
|
391
|
+
if (DEBUG) {
|
|
392
|
+
this.seen_code = {}
|
|
393
|
+
this.seen_code_uncompiled = {}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
/**
|
|
398
|
+
* Rebuild all TypedArray views into WASM linear memory.
|
|
399
|
+
* Must be called after any wasm_memory.grow() since growth
|
|
400
|
+
* detaches the old ArrayBuffer, invalidating all views.
|
|
401
|
+
*/
|
|
402
|
+
rebuild_wasm_views(): void {
|
|
403
|
+
const memory = this.wasm_memory
|
|
404
|
+
this.memory_size = view(Uint32Array, memory, 812, 1)
|
|
357
405
|
this.segment_is_null = view(Uint8Array, memory, 724, 8)
|
|
358
406
|
this.segment_offsets = view(Int32Array, memory, 736, 8)
|
|
359
407
|
this.segment_limits = view(Uint32Array, memory, 768, 8)
|
|
360
408
|
this.segment_access_bytes = view(Uint8Array, memory, 512, 8)
|
|
361
|
-
|
|
362
|
-
// Whether or not in protected mode
|
|
363
409
|
this.protected_mode = view(Int32Array, memory, 800, 1)
|
|
364
|
-
|
|
365
410
|
this.idtr_size = view(Int32Array, memory, 564, 1)
|
|
366
411
|
this.idtr_offset = view(Int32Array, memory, 568, 1)
|
|
367
|
-
|
|
368
|
-
// global descriptor table register
|
|
369
412
|
this.gdtr_size = view(Int32Array, memory, 572, 1)
|
|
370
413
|
this.gdtr_offset = view(Int32Array, memory, 576, 1)
|
|
371
|
-
|
|
372
414
|
this.tss_size_32 = view(Int32Array, memory, 1128, 1)
|
|
373
|
-
|
|
374
|
-
// whether or not a page fault occured
|
|
375
415
|
this.page_fault = view(Uint32Array, memory, 540, 8)
|
|
376
|
-
|
|
377
416
|
this.cr = view(Int32Array, memory, 580, 8)
|
|
378
|
-
|
|
379
|
-
// current privilege level
|
|
380
417
|
this.cpl = view(Uint8Array, memory, 612, 1)
|
|
381
|
-
|
|
382
|
-
// current operand/address size
|
|
383
418
|
this.is_32 = view(Int32Array, memory, 804, 1)
|
|
384
|
-
|
|
385
419
|
this.stack_size_32 = view(Int32Array, memory, 808, 1)
|
|
386
|
-
|
|
387
|
-
// Was the last instruction a hlt?
|
|
388
420
|
this.in_hlt = view(Uint8Array, memory, 616, 1)
|
|
389
|
-
|
|
390
421
|
this.last_virt_eip = view(Int32Array, memory, 620, 1)
|
|
391
422
|
this.eip_phys = view(Int32Array, memory, 624, 1)
|
|
392
|
-
|
|
393
423
|
this.sysenter_cs = view(Int32Array, memory, 636, 1)
|
|
394
|
-
|
|
395
424
|
this.sysenter_esp = view(Int32Array, memory, 640, 1)
|
|
396
|
-
|
|
397
425
|
this.sysenter_eip = view(Int32Array, memory, 644, 1)
|
|
398
|
-
|
|
399
426
|
this.prefixes = view(Int32Array, memory, 648, 1)
|
|
400
|
-
|
|
401
427
|
this.flags = view(Int32Array, memory, 120, 1)
|
|
402
|
-
|
|
403
|
-
// bitmap of flags which are not updated in the flags variable
|
|
404
|
-
// changed by arithmetic instructions, so only relevant to arithmetic flags
|
|
405
428
|
this.flags_changed = view(Int32Array, memory, 100, 1)
|
|
406
|
-
|
|
407
|
-
// enough infos about the last arithmetic operation to compute eflags
|
|
408
429
|
this.last_op_size = view(Int32Array, memory, 96, 1)
|
|
409
430
|
this.last_op1 = view(Int32Array, memory, 104, 1)
|
|
410
431
|
this.last_result = view(Int32Array, memory, 112, 1)
|
|
411
|
-
|
|
412
|
-
this.current_tsc = view(Uint32Array, memory, 960, 2) // 64 bit
|
|
413
|
-
|
|
414
|
-
// @ts-expect-error Devices are populated during init()
|
|
415
|
-
this.devices = {}
|
|
416
|
-
|
|
432
|
+
this.current_tsc = view(Uint32Array, memory, 960, 2)
|
|
417
433
|
this.instruction_pointer = view(Int32Array, memory, 556, 1)
|
|
418
434
|
this.previous_ip = view(Int32Array, memory, 560, 1)
|
|
419
|
-
|
|
420
|
-
// configured by guest
|
|
421
435
|
this.apic_enabled = view(Uint8Array, memory, 548, 1)
|
|
422
|
-
// configured when the emulator starts (changes bios initialisation)
|
|
423
436
|
this.acpi_enabled = view(Uint8Array, memory, 552, 1)
|
|
424
|
-
|
|
425
|
-
// managed in io.js
|
|
426
|
-
this.memory_map_read8 = []
|
|
427
|
-
this.memory_map_write8 = []
|
|
428
|
-
this.memory_map_read32 = []
|
|
429
|
-
this.memory_map_write32 = []
|
|
430
|
-
|
|
431
|
-
this.bios = {
|
|
432
|
-
main: null,
|
|
433
|
-
vga: null,
|
|
434
|
-
}
|
|
435
|
-
|
|
436
437
|
this.instruction_counter = view(Uint32Array, memory, 664, 1)
|
|
437
|
-
|
|
438
|
-
// registers
|
|
439
438
|
this.reg32 = view(Int32Array, memory, 64, 8)
|
|
440
|
-
|
|
441
439
|
this.fpu_st = view(Int32Array, memory, 1152, 4 * 8)
|
|
442
|
-
|
|
443
440
|
this.fpu_stack_empty = view(Uint8Array, memory, 816, 1)
|
|
444
|
-
this.fpu_stack_empty[0] = 0xff
|
|
445
441
|
this.fpu_stack_ptr = view(Uint8Array, memory, 1032, 1)
|
|
446
|
-
this.fpu_stack_ptr[0] = 0
|
|
447
|
-
|
|
448
442
|
this.fpu_control_word = view(Uint16Array, memory, 1036, 1)
|
|
449
|
-
this.fpu_control_word[0] = 0x37f
|
|
450
443
|
this.fpu_status_word = view(Uint16Array, memory, 1040, 1)
|
|
451
|
-
this.fpu_status_word[0] = 0
|
|
452
444
|
this.fpu_ip = view(Int32Array, memory, 1048, 1)
|
|
453
|
-
this.fpu_ip[0] = 0
|
|
454
445
|
this.fpu_ip_selector = view(Int32Array, memory, 1052, 1)
|
|
455
|
-
this.fpu_ip_selector[0] = 0
|
|
456
446
|
this.fpu_opcode = view(Int32Array, memory, 1044, 1)
|
|
457
|
-
this.fpu_opcode[0] = 0
|
|
458
447
|
this.fpu_dp = view(Int32Array, memory, 1056, 1)
|
|
459
|
-
this.fpu_dp[0] = 0
|
|
460
448
|
this.fpu_dp_selector = view(Int32Array, memory, 1060, 1)
|
|
461
|
-
this.fpu_dp_selector[0] = 0
|
|
462
|
-
|
|
463
449
|
this.reg_xmm32s = view(Int32Array, memory, 832, 8 * 4)
|
|
464
|
-
|
|
465
450
|
this.mxcsr = view(Int32Array, memory, 824, 1)
|
|
466
|
-
|
|
467
|
-
// segment registers, tr and ldtr
|
|
468
451
|
this.sreg = view(Uint16Array, memory, 668, 8)
|
|
469
|
-
|
|
470
|
-
// debug registers
|
|
471
452
|
this.dreg = view(Int32Array, memory, 684, 8)
|
|
472
|
-
|
|
473
453
|
this.reg_pdpte = view(Int32Array, memory, 968, 8)
|
|
474
|
-
|
|
475
454
|
this.svga_dirty_bitmap_min_offset = view(Uint32Array, memory, 716, 1)
|
|
476
455
|
this.svga_dirty_bitmap_max_offset = view(Uint32Array, memory, 720, 1)
|
|
477
|
-
|
|
478
|
-
this.fw_value = []
|
|
479
|
-
this.fw_pointer = 0
|
|
480
|
-
this.option_roms = []
|
|
481
|
-
|
|
482
|
-
this.io = undefined!
|
|
483
|
-
|
|
484
|
-
this.bus = bus
|
|
485
|
-
|
|
486
|
-
this.set_tsc(0, 0)
|
|
487
|
-
|
|
488
|
-
if (DEBUG) {
|
|
489
|
-
this.seen_code = {}
|
|
490
|
-
this.seen_code_uncompiled = {}
|
|
491
|
-
}
|
|
492
456
|
}
|
|
493
457
|
|
|
494
458
|
mmap_read8(addr: number): number {
|
|
@@ -887,7 +851,7 @@ export class CPU {
|
|
|
887
851
|
}
|
|
888
852
|
|
|
889
853
|
resize_memory(new_size: number): void {
|
|
890
|
-
const mem8_offset = this.
|
|
854
|
+
const mem8_offset = this.mem8_offset
|
|
891
855
|
const needed_total = mem8_offset + new_size
|
|
892
856
|
const current_buffer = this.wasm_memory.buffer.byteLength
|
|
893
857
|
if (needed_total > current_buffer) {
|
|
@@ -895,6 +859,7 @@ export class CPU {
|
|
|
895
859
|
(needed_total - current_buffer) / WASM_PAGE_SIZE,
|
|
896
860
|
)
|
|
897
861
|
this.wasm_memory.grow(grow_pages)
|
|
862
|
+
this.rebuild_wasm_views()
|
|
898
863
|
}
|
|
899
864
|
this.mem8 = view(Uint8Array, this.wasm_memory, mem8_offset, new_size)
|
|
900
865
|
this.mem32s = view(
|
|
@@ -907,7 +872,12 @@ export class CPU {
|
|
|
907
872
|
}
|
|
908
873
|
|
|
909
874
|
set_state(state: any[]): void {
|
|
910
|
-
|
|
875
|
+
const saved_memory_size = state[0]
|
|
876
|
+
if (saved_memory_size > this.memory_size[0]) {
|
|
877
|
+
this.resize_memory(saved_memory_size)
|
|
878
|
+
} else {
|
|
879
|
+
this.memory_size[0] = saved_memory_size
|
|
880
|
+
}
|
|
911
881
|
|
|
912
882
|
if (this.mem8.length !== this.memory_size[0]) {
|
|
913
883
|
console.warn(
|
|
@@ -1275,9 +1245,10 @@ export class CPU {
|
|
|
1275
1245
|
'Expected uninitialised memory',
|
|
1276
1246
|
)
|
|
1277
1247
|
|
|
1278
|
-
this.memory_size[0] = size
|
|
1279
|
-
|
|
1280
1248
|
const memory_offset = this.allocate_memory(size)
|
|
1249
|
+
this.rebuild_wasm_views()
|
|
1250
|
+
this.memory_size[0] = size
|
|
1251
|
+
this.mem8_offset = memory_offset
|
|
1281
1252
|
|
|
1282
1253
|
this.mem8 = view(Uint8Array, this.wasm_memory, memory_offset, size)
|
|
1283
1254
|
this.mem32s = view(
|
|
@@ -1586,6 +1557,25 @@ export class CPU {
|
|
|
1586
1557
|
}
|
|
1587
1558
|
|
|
1588
1559
|
this.debug_init()
|
|
1560
|
+
|
|
1561
|
+
// Rebuild all WASM memory views. During init, allocate_memory
|
|
1562
|
+
// and svga_allocate_memory may trigger memory.grow (from Rust),
|
|
1563
|
+
// which detaches the old ArrayBuffer and invalidates all views.
|
|
1564
|
+
this.rebuild_wasm_views()
|
|
1565
|
+
if (this.mem8_offset > 0) {
|
|
1566
|
+
this.mem8 = view(
|
|
1567
|
+
Uint8Array,
|
|
1568
|
+
this.wasm_memory,
|
|
1569
|
+
this.mem8_offset,
|
|
1570
|
+
this.memory_size[0],
|
|
1571
|
+
)
|
|
1572
|
+
this.mem32s = view(
|
|
1573
|
+
Uint32Array,
|
|
1574
|
+
this.wasm_memory,
|
|
1575
|
+
this.mem8_offset,
|
|
1576
|
+
this.memory_size[0] >> 2,
|
|
1577
|
+
)
|
|
1578
|
+
}
|
|
1589
1579
|
}
|
|
1590
1580
|
|
|
1591
1581
|
load_multiboot(buffer: ArrayBuffer): void {
|