@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/package.json ADDED
@@ -0,0 +1,75 @@
1
+ {
2
+ "name": "@aptre/v86",
3
+ "version": "0.5.0",
4
+ "license": "BSD-2-Clause",
5
+ "description": "x86 PC emulator and x86-to-wasm JIT, running in the browser",
6
+ "homepage": "https://github.com/aperturerobotics/v86",
7
+ "repository": "github:aperturerobotics/v86",
8
+ "type": "module",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/v86.d.ts",
12
+ "import": "./dist/v86.js"
13
+ },
14
+ "./fs": {
15
+ "types": "./dist/fs.d.ts",
16
+ "import": "./dist/fs.js"
17
+ },
18
+ "./9p": {
19
+ "types": "./dist/9p.d.ts",
20
+ "import": "./dist/9p.js"
21
+ },
22
+ "./bus": {
23
+ "types": "./dist/bus.d.ts",
24
+ "import": "./dist/bus.js"
25
+ }
26
+ },
27
+ "files": [
28
+ "!**/*.tsbuildinfo",
29
+ "dist",
30
+ "src",
31
+ "lib",
32
+ "gen",
33
+ "Readme.md",
34
+ "LICENSE"
35
+ ],
36
+ "scripts": {
37
+ "clean": "rimraf ./dist",
38
+ "build": "bun build.mjs",
39
+ "build:debug": "bun build.mjs --debug",
40
+ "typecheck": "tsgo --noEmit",
41
+ "lint": "eslint ./",
42
+ "format": "prettier --write 'src/**/*.{js,ts}' 'lib/**/*.{js,ts}' 'gen/**/*.{js,ts}' 'tests/**/*.{js,ts}'",
43
+ "format:check": "prettier --check 'src/**/*.{js,ts}' 'lib/**/*.{js,ts}' 'gen/**/*.{js,ts}' 'tests/**/*.{js,ts}'",
44
+ "format:config": "prettier --write tsconfig.json package.json",
45
+ "test": "vitest run",
46
+ "test:watch": "vitest",
47
+ "prepare": "husky",
48
+ "precommit": "lint-staged",
49
+ "release": "npm version patch --no-git-tag-version && bun run release:commit",
50
+ "release:minor": "npm version minor --no-git-tag-version && bun run release:commit",
51
+ "release:commit": "git reset && git add package.json && git commit -s -m \"release: v$(bun -e \"console.log(require('./package.json').version)\")\" && git tag v$(bun -e \"console.log(require('./package.json').version)\")",
52
+ "release:publish": "git push && git push --tags"
53
+ },
54
+ "lint-staged": {
55
+ "package.json": "prettier --config .prettierrc.yaml --write",
56
+ "./{src,lib,gen,tests}/**/*.{ts,js}": "prettier --config .prettierrc.yaml --write"
57
+ },
58
+ "devDependencies": {
59
+ "@eslint/js": "^9.27.0",
60
+ "@types/node": "^25.5.0",
61
+ "@typescript/native-preview": "^7.0.0-dev.20250601",
62
+ "esbuild": "^0.25.4",
63
+ "eslint": "^9.27.0",
64
+ "eslint-config-prettier": "^10.1.5",
65
+ "eslint-plugin-unused-imports": "^4.4.1",
66
+ "globals": "^16.1.0",
67
+ "husky": "^9.1.7",
68
+ "lint-staged": "^16.0.0",
69
+ "prettier": "^3.5.3",
70
+ "rimraf": "^6.0.1",
71
+ "typescript": "^5.8.3",
72
+ "typescript-eslint": "^8.32.1",
73
+ "vitest": "^3.1.4"
74
+ }
75
+ }
package/src/acpi.ts ADDED
@@ -0,0 +1,267 @@
1
+ // http://www.uefi.org/sites/default/files/resources/ACPI_6_1.pdf
2
+
3
+ import { v86 } from './main.js'
4
+ import { LOG_ACPI } from '../src/const.js'
5
+ import { h } from './lib.js'
6
+ import { dbg_log, dbg_assert } from './log.js'
7
+ import { IO } from './io.js'
8
+
9
+ // Minimal interface for the CPU fields ACPI needs
10
+ interface ACPICpu {
11
+ io: IO
12
+ devices: {
13
+ pci: {
14
+ register_device(device: any): void
15
+ }
16
+ }
17
+ device_raise_irq(irq: number): void
18
+ device_lower_irq(irq: number): void
19
+ }
20
+
21
+ const PMTIMER_FREQ_SECONDS = 3579545
22
+
23
+ export class ACPI {
24
+ name: string
25
+ cpu: ACPICpu
26
+ timer_last_value: number
27
+ timer_imprecision_offset: number
28
+ status: number
29
+ pm1_status: number
30
+ pm1_enable: number
31
+ last_timer: number
32
+ gpe: Uint8Array
33
+
34
+ constructor(cpu: ACPICpu) {
35
+ this.name = 'acpi'
36
+ this.cpu = cpu
37
+
38
+ const io = cpu.io
39
+
40
+ const acpi = {
41
+ pci_id: 0x07 << 3,
42
+ pci_space: [
43
+ 0x86, 0x80, 0x13, 0x71, 0x07, 0x00, 0x80, 0x02, 0x08, 0x00,
44
+ 0x80, 0x06, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00,
45
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
46
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
47
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
48
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
49
+ 0x09, 0x01, 0x00, 0x00,
50
+ ],
51
+ pci_bars: [],
52
+ name: 'acpi',
53
+ }
54
+
55
+ // 00:07.0 Bridge: Intel Corporation 82371AB/EB/MB PIIX4 ACPI (rev 08)
56
+ cpu.devices.pci.register_device(acpi)
57
+
58
+ this.timer_last_value = 0
59
+ this.timer_imprecision_offset = 0
60
+
61
+ this.status = 1
62
+ this.pm1_status = 0
63
+ this.pm1_enable = 0
64
+ this.last_timer = this.get_timer(v86.microtick())
65
+
66
+ this.gpe = new Uint8Array(4)
67
+
68
+ io.register_read(
69
+ 0xb000,
70
+ this,
71
+ undefined,
72
+ function (this: ACPI): number {
73
+ dbg_log('ACPI pm1_status read', LOG_ACPI)
74
+ return this.pm1_status
75
+ },
76
+ )
77
+ io.register_write(
78
+ 0xb000,
79
+ this,
80
+ undefined,
81
+ function (this: ACPI, value: number): void {
82
+ dbg_log('ACPI pm1_status write: ' + h(value, 4), LOG_ACPI)
83
+ this.pm1_status &= ~value
84
+ },
85
+ )
86
+
87
+ io.register_read(
88
+ 0xb002,
89
+ this,
90
+ undefined,
91
+ function (this: ACPI): number {
92
+ dbg_log('ACPI pm1_enable read', LOG_ACPI)
93
+ return this.pm1_enable
94
+ },
95
+ )
96
+ io.register_write(
97
+ 0xb002,
98
+ this,
99
+ undefined,
100
+ function (this: ACPI, value: number): void {
101
+ dbg_log('ACPI pm1_enable write: ' + h(value), LOG_ACPI)
102
+ this.pm1_enable = value
103
+ },
104
+ )
105
+
106
+ // ACPI status
107
+ io.register_read(
108
+ 0xb004,
109
+ this,
110
+ function (this: ACPI): number {
111
+ dbg_log('ACPI status read8', LOG_ACPI)
112
+ return this.status & 0xff
113
+ },
114
+ function (this: ACPI): number {
115
+ dbg_log('ACPI status read', LOG_ACPI)
116
+ return this.status
117
+ },
118
+ )
119
+ io.register_write(
120
+ 0xb004,
121
+ this,
122
+ undefined,
123
+ function (this: ACPI, value: number): void {
124
+ dbg_log('ACPI status write: ' + h(value), LOG_ACPI)
125
+ this.status = value
126
+ },
127
+ )
128
+
129
+ // ACPI, pmtimer
130
+ io.register_read(
131
+ 0xb008,
132
+ this,
133
+ undefined,
134
+ undefined,
135
+ function (this: ACPI): number {
136
+ const value = this.get_timer(v86.microtick()) & 0xffffff
137
+ //dbg_log("pmtimer read: " + h(value >>> 0), LOG_ACPI);
138
+ return value
139
+ },
140
+ )
141
+
142
+ // ACPI, gpe
143
+ io.register_read(0xafe0, this, function (this: ACPI): number {
144
+ dbg_log('Read gpe#0', LOG_ACPI)
145
+ return this.gpe[0]
146
+ })
147
+ io.register_read(0xafe1, this, function (this: ACPI): number {
148
+ dbg_log('Read gpe#1', LOG_ACPI)
149
+ return this.gpe[1]
150
+ })
151
+ io.register_read(0xafe2, this, function (this: ACPI): number {
152
+ dbg_log('Read gpe#2', LOG_ACPI)
153
+ return this.gpe[2]
154
+ })
155
+ io.register_read(0xafe3, this, function (this: ACPI): number {
156
+ dbg_log('Read gpe#3', LOG_ACPI)
157
+ return this.gpe[3]
158
+ })
159
+
160
+ io.register_write(
161
+ 0xafe0,
162
+ this,
163
+ function (this: ACPI, value: number): void {
164
+ dbg_log('Write gpe#0: ' + h(value), LOG_ACPI)
165
+ this.gpe[0] = value
166
+ },
167
+ )
168
+ io.register_write(
169
+ 0xafe1,
170
+ this,
171
+ function (this: ACPI, value: number): void {
172
+ dbg_log('Write gpe#1: ' + h(value), LOG_ACPI)
173
+ this.gpe[1] = value
174
+ },
175
+ )
176
+ io.register_write(
177
+ 0xafe2,
178
+ this,
179
+ function (this: ACPI, value: number): void {
180
+ dbg_log('Write gpe#2: ' + h(value), LOG_ACPI)
181
+ this.gpe[2] = value
182
+ },
183
+ )
184
+ io.register_write(
185
+ 0xafe3,
186
+ this,
187
+ function (this: ACPI, value: number): void {
188
+ dbg_log('Write gpe#3: ' + h(value), LOG_ACPI)
189
+ this.gpe[3] = value
190
+ },
191
+ )
192
+ }
193
+
194
+ timer(now: number): number {
195
+ const timer = this.get_timer(now)
196
+ const highest_bit_changed =
197
+ ((timer ^ this.last_timer) & (1 << 23)) !== 0
198
+
199
+ if (this.pm1_enable & 1 && highest_bit_changed) {
200
+ dbg_log('ACPI raise irq', LOG_ACPI)
201
+ this.pm1_status |= 1
202
+ this.cpu.device_raise_irq(9)
203
+ } else {
204
+ this.cpu.device_lower_irq(9)
205
+ }
206
+
207
+ this.last_timer = timer
208
+ return 100 // TODO
209
+ }
210
+
211
+ get_timer(now: number): number {
212
+ const t = Math.round(now * (PMTIMER_FREQ_SECONDS / 1000))
213
+
214
+ // Due to the low precision of JavaScript's time functions we increment the
215
+ // returned timer value every time it is read
216
+
217
+ if (t === this.timer_last_value) {
218
+ // don't go past 1ms
219
+
220
+ if (this.timer_imprecision_offset < PMTIMER_FREQ_SECONDS / 1000) {
221
+ this.timer_imprecision_offset++
222
+ }
223
+ } else {
224
+ dbg_assert(t > this.timer_last_value)
225
+
226
+ const previous_timer =
227
+ this.timer_last_value + this.timer_imprecision_offset
228
+
229
+ // don't go back in time
230
+
231
+ if (previous_timer <= t) {
232
+ this.timer_imprecision_offset = 0
233
+ this.timer_last_value = t
234
+ } else {
235
+ dbg_log(
236
+ 'Warning: Overshot pmtimer, waiting;' +
237
+ ' current=' +
238
+ t +
239
+ ' last=' +
240
+ this.timer_last_value +
241
+ ' offset=' +
242
+ this.timer_imprecision_offset,
243
+ LOG_ACPI,
244
+ )
245
+ }
246
+ }
247
+
248
+ return this.timer_last_value + this.timer_imprecision_offset
249
+ }
250
+
251
+ get_state(): [number, number, number, Uint8Array] {
252
+ const state: [number, number, number, Uint8Array] = [
253
+ this.status,
254
+ this.pm1_status,
255
+ this.pm1_enable,
256
+ this.gpe,
257
+ ]
258
+ return state
259
+ }
260
+
261
+ set_state(state: [number, number, number, Uint8Array]): void {
262
+ this.status = state[0]
263
+ this.pm1_status = state[1]
264
+ this.pm1_enable = state[2]
265
+ this.gpe = state[3]
266
+ }
267
+ }
@@ -0,0 +1,106 @@
1
+ import { dbg_assert } from '../log.js'
2
+ import { get_charmap } from '../lib.js'
3
+
4
+ interface DummyScreenOptions {
5
+ encoding?: string
6
+ }
7
+
8
+ export class DummyScreenAdapter {
9
+ FLAG_BLINKING = 0x01
10
+ FLAG_FONT_PAGE_B = 0x02
11
+
12
+ private cursor_row = 0
13
+ private cursor_col = 0
14
+ private graphical_mode_width = 0
15
+ private graphical_mode_height = 0
16
+ private is_graphical = false
17
+ private text_mode_data: Uint8Array
18
+ private text_mode_width = 0
19
+ private text_mode_height = 0
20
+ private charmap: string
21
+
22
+ constructor(options?: DummyScreenOptions) {
23
+ this.charmap = get_charmap(options?.encoding || '')
24
+ this.text_mode_data = new Uint8Array(0)
25
+ this.set_size_text(80, 25)
26
+ }
27
+
28
+ put_char(
29
+ row: number,
30
+ col: number,
31
+ chr: number,
32
+ _blinking: number,
33
+ _bg_color: number,
34
+ _fg_color: number,
35
+ ): void {
36
+ dbg_assert(row >= 0 && row < this.text_mode_height)
37
+ dbg_assert(col >= 0 && col < this.text_mode_width)
38
+ this.text_mode_data[row * this.text_mode_width + col] = chr
39
+ }
40
+
41
+ destroy(): void {}
42
+ pause(): void {}
43
+ continue(): void {}
44
+
45
+ set_mode(graphical: boolean): void {
46
+ this.is_graphical = graphical
47
+ }
48
+
49
+ set_font_bitmap(
50
+ _height: number,
51
+ _width_9px: boolean,
52
+ _width_dbl: boolean,
53
+ _copy_8th_col: boolean,
54
+ _bitmap: Uint8Array,
55
+ _bitmap_changed: boolean,
56
+ ): void {}
57
+
58
+ set_font_page(_page_a: number, _page_b: number): void {}
59
+
60
+ clear_screen(): void {}
61
+
62
+ set_size_text(cols: number, rows: number): void {
63
+ if (cols === this.text_mode_width && rows === this.text_mode_height) {
64
+ return
65
+ }
66
+
67
+ this.text_mode_data = new Uint8Array(cols * rows)
68
+ this.text_mode_width = cols
69
+ this.text_mode_height = rows
70
+ }
71
+
72
+ set_size_graphical(width: number, height: number): void {
73
+ this.graphical_mode_width = width
74
+ this.graphical_mode_height = height
75
+ }
76
+
77
+ set_scale(_s_x: number, _s_y: number): void {}
78
+
79
+ update_cursor_scanline(_start: number, _end: number, _max: number): void {}
80
+
81
+ update_cursor(row: number, col: number): void {
82
+ this.cursor_row = row
83
+ this.cursor_col = col
84
+ }
85
+
86
+ update_buffer(_layers: any[]): void {}
87
+
88
+ get_text_screen(): string[] {
89
+ const screen: string[] = []
90
+
91
+ for (let i = 0; i < this.text_mode_height; i++) {
92
+ screen.push(this.get_text_row(i))
93
+ }
94
+
95
+ return screen
96
+ }
97
+
98
+ get_text_row(y: number): string {
99
+ const begin = y * this.text_mode_width
100
+ const end = begin + this.text_mode_width
101
+ return Array.from(
102
+ this.text_mode_data.subarray(begin, end),
103
+ (chr) => this.charmap[chr],
104
+ ).join('')
105
+ }
106
+ }