@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/log.ts ADDED
@@ -0,0 +1,149 @@
1
+ declare let DEBUG: boolean
2
+
3
+ if (typeof DEBUG === 'undefined') {
4
+ Object.defineProperty(globalThis, 'DEBUG', {
5
+ value: true,
6
+ writable: true,
7
+ configurable: true,
8
+ })
9
+ }
10
+
11
+ import { LOG_NAMES } from './const.js'
12
+ import { pad0, pads } from './lib.js'
13
+ import {
14
+ LOG_ALL,
15
+ LOG_PS2,
16
+ LOG_PIT,
17
+ LOG_9P,
18
+ LOG_PIC,
19
+ LOG_DMA,
20
+ LOG_NET,
21
+ LOG_FLOPPY,
22
+ LOG_DISK,
23
+ LOG_SERIAL,
24
+ LOG_VGA,
25
+ LOG_SB16,
26
+ LOG_VIRTIO,
27
+ } from './const.js'
28
+
29
+ export const LOG_TO_FILE = false
30
+
31
+ export let LOG_LEVEL: number =
32
+ LOG_ALL &
33
+ ~LOG_PS2 &
34
+ ~LOG_PIT &
35
+ ~LOG_VIRTIO &
36
+ ~LOG_9P &
37
+ ~LOG_PIC &
38
+ ~LOG_DMA &
39
+ ~LOG_SERIAL &
40
+ ~LOG_NET &
41
+ ~LOG_FLOPPY &
42
+ ~LOG_DISK &
43
+ ~LOG_VGA &
44
+ ~LOG_SB16
45
+
46
+ export function set_log_level(level: number): void {
47
+ LOG_LEVEL = level
48
+ }
49
+
50
+ export const log_data: string[] = []
51
+
52
+ function do_the_log(message: string): void {
53
+ if (LOG_TO_FILE) {
54
+ log_data.push(message, '\n')
55
+ } else {
56
+ console.log(message)
57
+ }
58
+ }
59
+
60
+ export const dbg_log: (stuff: string | number, level?: number) => void =
61
+ (function () {
62
+ if (!DEBUG) {
63
+ return function () {}
64
+ }
65
+
66
+ const init: Record<number, string> = {}
67
+ const dbg_names = LOG_NAMES.reduce(function (a, x) {
68
+ a[x[0]] = x[1]
69
+ return a
70
+ }, init)
71
+
72
+ let log_last_message = ''
73
+ let log_message_repetitions = 0
74
+
75
+ function dbg_log_(stuff: string | number, level?: number): void {
76
+ if (!DEBUG) return
77
+
78
+ level = level || 1
79
+
80
+ if (level & LOG_LEVEL) {
81
+ const level_name = dbg_names[level] || '',
82
+ message = '[' + pads(level_name, 4) + '] ' + stuff
83
+
84
+ if (message === log_last_message) {
85
+ log_message_repetitions++
86
+
87
+ if (log_message_repetitions < 2048) {
88
+ return
89
+ }
90
+ }
91
+
92
+ const now = new Date()
93
+ const time_str =
94
+ pad0(now.getHours(), 2) +
95
+ ':' +
96
+ pad0(now.getMinutes(), 2) +
97
+ ':' +
98
+ pad0(now.getSeconds(), 2) +
99
+ '+' +
100
+ pad0(now.getMilliseconds(), 3) +
101
+ ' '
102
+
103
+ if (log_message_repetitions) {
104
+ if (log_message_repetitions === 1) {
105
+ do_the_log(time_str + log_last_message)
106
+ } else {
107
+ do_the_log(
108
+ 'Previous message repeated ' +
109
+ log_message_repetitions +
110
+ ' times',
111
+ )
112
+ }
113
+
114
+ log_message_repetitions = 0
115
+ }
116
+
117
+ do_the_log(time_str + message)
118
+ log_last_message = message
119
+ }
120
+ }
121
+
122
+ return dbg_log_
123
+ })()
124
+
125
+ export function dbg_trace(level?: number): void {
126
+ if (!DEBUG) return
127
+
128
+ dbg_log(Error().stack!, level)
129
+ }
130
+
131
+ export function dbg_assert(cond: boolean, msg?: string, _level?: number): void {
132
+ if (!DEBUG) return
133
+
134
+ if (!cond) {
135
+ dbg_assert_failed(msg)
136
+ }
137
+ }
138
+
139
+ export function dbg_assert_failed(msg?: string): never {
140
+ // eslint-disable-next-line no-debugger
141
+ debugger
142
+ console.trace()
143
+
144
+ if (msg) {
145
+ throw 'Assert failed: ' + msg
146
+ } else {
147
+ throw 'Assert failed'
148
+ }
149
+ }
package/src/main.ts ADDED
@@ -0,0 +1,199 @@
1
+ import { CPU } from './cpu.js'
2
+ import { save_state, restore_state } from './state.js'
3
+ export { V86 } from './browser/starter.js'
4
+
5
+ import { BusConnector } from './bus.js'
6
+
7
+ // CPU is a constructor function (not a TS class), so it lacks a construct
8
+ // signature. We alias it through an untyped binding to call with `new`.
9
+
10
+ const CPUConstructor: any = CPU
11
+
12
+ type CPUInstance = any
13
+
14
+ function the_worker() {
15
+ let timeout: ReturnType<typeof setTimeout> | undefined
16
+ globalThis.onmessage = function (e: MessageEvent) {
17
+ const t = e.data.t
18
+ if (timeout) {
19
+ clearTimeout(timeout)
20
+ timeout = undefined
21
+ }
22
+ if (t < 1) postMessage(e.data.tick)
23
+ else timeout = setTimeout(() => postMessage(e.data.tick), t)
24
+ }
25
+ }
26
+
27
+ export class v86 {
28
+ running: boolean = false
29
+ stopping: boolean = false
30
+ idle: boolean = true
31
+ tick_counter: number = 0
32
+ worker: Worker | null = null
33
+ cpu: CPUInstance
34
+ bus: BusConnector
35
+
36
+ static microtick: () => number
37
+
38
+ constructor(bus: BusConnector, wasm: any) {
39
+ this.cpu = new CPUConstructor(bus, wasm, () => {
40
+ if (this.idle) {
41
+ this.next_tick(0)
42
+ }
43
+ })
44
+ this.bus = bus
45
+ this.register_yield()
46
+ }
47
+
48
+ run(): void {
49
+ this.stopping = false
50
+
51
+ if (!this.running) {
52
+ this.running = true
53
+ this.bus.send('emulator-started')
54
+ }
55
+
56
+ this.next_tick(0)
57
+ }
58
+
59
+ do_tick(): void {
60
+ if (this.stopping || !this.running) {
61
+ this.stopping = this.running = false
62
+ this.bus.send('emulator-stopped')
63
+ return
64
+ }
65
+
66
+ this.idle = false
67
+ const t = this.cpu.main_loop()
68
+
69
+ this.next_tick(t)
70
+ }
71
+
72
+ next_tick(t: number): void {
73
+ const tick = ++this.tick_counter
74
+ this.idle = true
75
+ this.yield(t, tick)
76
+ }
77
+
78
+ yield_callback(tick: number): void {
79
+ if (tick === this.tick_counter) {
80
+ this.do_tick()
81
+ }
82
+ }
83
+
84
+ stop(): void {
85
+ if (this.running) {
86
+ this.stopping = true
87
+ }
88
+ }
89
+
90
+ destroy(): void {
91
+ this.unregister_yield()
92
+ }
93
+
94
+ restart(): void {
95
+ this.cpu.reset_cpu()
96
+ this.cpu.load_bios()
97
+ }
98
+
99
+ init(settings: any): void {
100
+ this.cpu.init(settings, this.bus)
101
+ this.bus.send('emulator-ready')
102
+ }
103
+
104
+ save_state(): ArrayBuffer {
105
+ return save_state(this.cpu)
106
+ }
107
+
108
+ restore_state(state: any): void {
109
+ return restore_state(this.cpu, state)
110
+ }
111
+
112
+ yield(t: number, _tick: number): void {
113
+ // Default fallback: setTimeout
114
+ setTimeout(() => {
115
+ this.do_tick()
116
+ }, t)
117
+ }
118
+
119
+ register_yield(): void {
120
+ // no-op by default
121
+ }
122
+
123
+ unregister_yield(): void {
124
+ // no-op by default
125
+ }
126
+ }
127
+
128
+ const _global: any = globalThis
129
+
130
+ // Environment-specific yield strategy overrides
131
+ if (typeof process !== 'undefined') {
132
+ v86.prototype.yield = function (t: number, tick: number): void {
133
+ if (t < 1) {
134
+ _global.setImmediate(
135
+ (tick: number) => this.yield_callback(tick),
136
+ tick,
137
+ )
138
+ } else {
139
+ setTimeout((tick: number) => this.yield_callback(tick), t, tick)
140
+ }
141
+ }
142
+
143
+ v86.prototype.register_yield = function (): void {}
144
+ v86.prototype.unregister_yield = function (): void {}
145
+ } else if (
146
+ _global['scheduler'] &&
147
+ typeof _global['scheduler']['postTask'] === 'function' &&
148
+ location.href.includes('use-scheduling-api')
149
+ ) {
150
+ v86.prototype.yield = function (t: number, tick: number): void {
151
+ t = Math.max(0, t)
152
+ _global['scheduler']['postTask'](() => this.yield_callback(tick), {
153
+ delay: t,
154
+ })
155
+ }
156
+
157
+ v86.prototype.register_yield = function (): void {}
158
+ v86.prototype.unregister_yield = function (): void {}
159
+ } else if (typeof Worker !== 'undefined') {
160
+ // XXX: This has a slightly lower throughput compared to window.postMessage
161
+
162
+ v86.prototype.register_yield = function (): void {
163
+ const url = URL.createObjectURL(
164
+ new Blob(['(' + the_worker.toString() + ')()'], {
165
+ type: 'text/javascript',
166
+ }),
167
+ )
168
+ this.worker = new Worker(url)
169
+ this.worker.onmessage = (e: MessageEvent) => this.yield_callback(e.data)
170
+ URL.revokeObjectURL(url)
171
+ }
172
+
173
+ v86.prototype.yield = function (t: number, tick: number): void {
174
+ this.worker!.postMessage({ t, tick })
175
+ }
176
+
177
+ v86.prototype.unregister_yield = function (): void {
178
+ if (this.worker) {
179
+ this.worker.terminate()
180
+ }
181
+ this.worker = null
182
+ }
183
+ }
184
+
185
+ // Static microtick initialization
186
+ if (typeof performance === 'object' && performance.now) {
187
+ v86.microtick = performance.now.bind(performance)
188
+ } else if (typeof require === 'function') {
189
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
190
+ const { performance } = require('perf_hooks')
191
+ v86.microtick = performance.now.bind(performance)
192
+ } else if (typeof process === 'object' && process.hrtime) {
193
+ v86.microtick = function () {
194
+ const t = process.hrtime()
195
+ return t[0] * 1000 + t[1] / 1e6
196
+ }
197
+ } else {
198
+ v86.microtick = Date.now
199
+ }