@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
@@ -0,0 +1,3415 @@
1
+ declare let DEBUG: boolean
2
+
3
+ import { V86 } from './starter.js'
4
+ import { LOG_NAMES } from '../const.js'
5
+ import { SyncFileBuffer } from '../buffer.js'
6
+ import {
7
+ h,
8
+ pad0,
9
+ pads,
10
+ hex_dump,
11
+ dump_file,
12
+ download,
13
+ round_up_to_next_power_of_2,
14
+ } from '../lib.js'
15
+ import { log_data, LOG_LEVEL, set_log_level } from '../log.js'
16
+ import * as iso9660 from '../iso9660.js'
17
+
18
+ const ON_LOCALHOST = !location.hostname.endsWith('copy.sh')
19
+
20
+ const DEFAULT_NETWORKING_PROXIES = [
21
+ 'wss://relay.widgetry.org/',
22
+ 'ws://localhost:8080/',
23
+ ]
24
+ const DEFAULT_MEMORY_SIZE = 128
25
+ const DEFAULT_VGA_MEMORY_SIZE = 8
26
+ const DEFAULT_BOOT_ORDER = 0
27
+ const DEFAULT_MTU = 1500
28
+ const DEFAULT_NIC_TYPE = 'ne2k'
29
+
30
+ const MAX_ARRAY_BUFFER_SIZE_MB = 2000
31
+
32
+ function query_append(): string {
33
+ const version = $('version')
34
+ return version ? '?' + version.textContent : ''
35
+ }
36
+
37
+ function set_title(text: string): void {
38
+ document.title = text + ' - v86' + (DEBUG ? ' - debug' : '')
39
+ const description = document.querySelector('meta[name=description]')
40
+ if (description) {
41
+ ;(description as HTMLMetaElement).content = 'Running ' + text
42
+ }
43
+ }
44
+
45
+ function bool_arg(x: string | null): boolean {
46
+ return !!x && x !== '0'
47
+ }
48
+
49
+ function format_timestamp(time: number): string {
50
+ if (time < 60) {
51
+ return time + 's'
52
+ } else if (time < 3600) {
53
+ return ((time / 60) | 0) + 'm ' + pad0(time % 60, 2) + 's'
54
+ } else {
55
+ return (
56
+ ((time / 3600) | 0) +
57
+ 'h ' +
58
+ pad0(((time / 60) | 0) % 60, 2) +
59
+ 'm ' +
60
+ pad0(time % 60, 2) +
61
+ 's'
62
+ )
63
+ }
64
+ }
65
+
66
+ function read_file(file: File): Promise<ArrayBuffer | string | null> {
67
+ return new Promise((resolve, reject) => {
68
+ const fr = new FileReader()
69
+ fr.onload = () => resolve(fr.result)
70
+ fr.onerror = (e) => reject(e)
71
+ fr.readAsArrayBuffer(file)
72
+ })
73
+ }
74
+
75
+ let progress_ticks = 0
76
+
77
+ function show_progress(e: any): void {
78
+ const el = $('loading')!
79
+ ;(el as HTMLElement).style.display = 'block'
80
+
81
+ const file_name = e.file_name.split('?', 1)[0]
82
+
83
+ if (file_name.endsWith('.wasm')) {
84
+ const parts = file_name.split('/')
85
+ el.textContent = 'Fetching ' + parts[parts.length - 1] + ' ...'
86
+ return
87
+ }
88
+
89
+ if (e.file_index === e.file_count - 1 && e.loaded >= e.total - 2048) {
90
+ // last file is (almost) loaded
91
+ el.textContent = 'Done downloading. Starting now ...'
92
+ return
93
+ }
94
+
95
+ let line = 'Downloading images '
96
+
97
+ if (typeof e.file_index === 'number' && e.file_count) {
98
+ line += '[' + (e.file_index + 1) + '/' + e.file_count + '] '
99
+ }
100
+
101
+ if (e.total && typeof e.loaded === 'number') {
102
+ let per100 = Math.floor((e.loaded / e.total) * 100)
103
+ per100 = Math.min(100, Math.max(0, per100))
104
+
105
+ const per50 = Math.floor(per100 / 2)
106
+
107
+ line += per100 + '% ['
108
+ line += '#'.repeat(per50)
109
+ line += ' '.repeat(50 - per50) + ']'
110
+ } else {
111
+ line += '.'.repeat(progress_ticks++ % 50)
112
+ }
113
+
114
+ el.textContent = line
115
+ }
116
+
117
+ function $(id: string): HTMLElement | null {
118
+ return document.getElementById(id)
119
+ }
120
+
121
+ // These values were previously stored in localStorage
122
+ const elements_to_restore = [
123
+ 'memory_size',
124
+ 'video_memory_size',
125
+ 'networking_proxy',
126
+ 'disable_audio',
127
+ 'enable_acpi',
128
+ 'boot_order',
129
+ ]
130
+ for (const item of elements_to_restore) {
131
+ try {
132
+ window.localStorage.removeItem(item)
133
+ } catch {
134
+ // intentionally empty
135
+ }
136
+ }
137
+
138
+ interface OSProfile {
139
+ id: string
140
+ name: string
141
+
142
+ [key: string]: any
143
+ }
144
+
145
+ function onload(): void {
146
+ if (!window.WebAssembly) {
147
+ alert(
148
+ "Your browser is not supported because it doesn't support WebAssembly",
149
+ )
150
+ return
151
+ }
152
+
153
+ $('start_emulation')!.onclick = function (e) {
154
+ start_emulation(null, null)
155
+ ;($('start_emulation') as HTMLElement).blur()
156
+ e.preventDefault()
157
+ }
158
+
159
+ if (DEBUG) {
160
+ debug_onload()
161
+ }
162
+
163
+ if (DEBUG && ON_LOCALHOST) {
164
+ // don't use online relay in debug mode
165
+ ;($('relay_url') as HTMLInputElement).value = 'ws://localhost:8080/'
166
+ }
167
+
168
+ const query_args = new URLSearchParams(location.search)
169
+ const host =
170
+ query_args.get('cdn') || (ON_LOCALHOST ? 'images/' : '//i.copy.sh/')
171
+
172
+ // Abandonware OS images are from https://winworldpc.com/library/operating-systems
173
+ const oses: OSProfile[] = [
174
+ {
175
+ id: 'archlinux',
176
+ name: 'Arch Linux',
177
+ memory_size: 512 * 1024 * 1024,
178
+ vga_memory_size: 8 * 1024 * 1024,
179
+ state: { url: host + 'arch_state-v3.bin.zst' },
180
+ filesystem: {
181
+ baseurl: host + 'arch/',
182
+ },
183
+ net_device_type: 'virtio',
184
+ },
185
+ {
186
+ id: 'archlinux-boot',
187
+ name: 'Arch Linux',
188
+ memory_size: 512 * 1024 * 1024,
189
+ vga_memory_size: 8 * 1024 * 1024,
190
+ filesystem: {
191
+ baseurl: host + 'arch/',
192
+ basefs: { url: host + 'fs.json' },
193
+ },
194
+ cmdline: [
195
+ 'rw apm=off vga=0x344 video=vesafb:ypan,vremap:8',
196
+ 'root=host9p rootfstype=9p rootflags=trans=virtio,cache=loose',
197
+ 'mitigations=off audit=0',
198
+ 'init_on_free=on',
199
+ 'tsc=reliable',
200
+ 'random.trust_cpu=on',
201
+ 'nowatchdog',
202
+ 'init=/usr/bin/init-openrc net.ifnames=0 biosdevname=0',
203
+ ].join(' '),
204
+ bzimage_initrd_from_filesystem: true,
205
+ net_device_type: 'virtio',
206
+ },
207
+ {
208
+ id: 'copy/skiffos',
209
+ name: 'SkiffOS',
210
+ cdrom: {
211
+ url: host + 'skiffos/.iso',
212
+ size: 124672000,
213
+ async: true,
214
+ fixed_chunk_size: 1024 * 1024,
215
+ use_parts: true,
216
+ },
217
+ memory_size: 512 * 1024 * 1024,
218
+ },
219
+ {
220
+ id: 'serenity',
221
+ name: 'SerenityOS',
222
+ hda: {
223
+ url: host + 'serenity-v3/.img.zst',
224
+ size: 734003200,
225
+ async: true,
226
+ fixed_chunk_size: 1024 * 1024,
227
+ use_parts: true,
228
+ },
229
+ memory_size: 512 * 1024 * 1024,
230
+ state: { url: host + 'serenity_state-v4.bin.zst' },
231
+ homepage: 'https://serenityos.org/',
232
+ mac_address_translation: true,
233
+ },
234
+ {
235
+ id: 'serenity-boot',
236
+ name: 'SerenityOS',
237
+ hda: {
238
+ url: host + 'serenity-v3/.img.zst',
239
+ size: 734003200,
240
+ async: true,
241
+ fixed_chunk_size: 1024 * 1024,
242
+ use_parts: true,
243
+ },
244
+ memory_size: 512 * 1024 * 1024,
245
+ homepage: 'https://serenityos.org/',
246
+ },
247
+ {
248
+ id: 'redox',
249
+ name: 'Redox',
250
+ hda: {
251
+ url: host + 'redox_demo_i686_2024-09-07_1225_harddrive/.img',
252
+ size: 671088640,
253
+ async: true,
254
+ fixed_chunk_size: 1024 * 1024,
255
+ use_parts: true,
256
+ },
257
+ memory_size: 1024 * 1024 * 1024,
258
+ state: { url: host + 'redox_state-v2.bin.zst' },
259
+ homepage: 'https://www.redox-os.org/',
260
+ acpi: true,
261
+ },
262
+ {
263
+ id: 'redox-boot',
264
+ name: 'Redox',
265
+ hda: {
266
+ url: host + 'redox_demo_i686_2024-09-07_1225_harddrive/.img',
267
+ size: 671088640,
268
+ async: true,
269
+ fixed_chunk_size: 1024 * 1024,
270
+ use_parts: true,
271
+ },
272
+ memory_size: 1024 * 1024 * 1024,
273
+ homepage: 'https://www.redox-os.org/',
274
+ acpi: true,
275
+ },
276
+ {
277
+ id: 'helenos',
278
+ memory_size: 256 * 1024 * 1024,
279
+ cdrom: {
280
+ //url: host + "HelenOS-0.11.2-ia32.iso",
281
+ //size: 25765888,
282
+ url: host + 'HelenOS-0.14.1-ia32.iso',
283
+ size: 25792512,
284
+ async: false,
285
+ },
286
+ name: 'HelenOS',
287
+ homepage: 'http://www.helenos.org/',
288
+ },
289
+ {
290
+ id: 'fiwix',
291
+ memory_size: 256 * 1024 * 1024,
292
+ hda: {
293
+ url: host + 'FiwixOS-3.4-i386/.img',
294
+ size: 1024 * 1024 * 1024,
295
+ async: true,
296
+ fixed_chunk_size: 1024 * 1024,
297
+ use_parts: true,
298
+ },
299
+ name: 'FiwixOS',
300
+ homepage: 'https://www.fiwix.org/',
301
+ },
302
+ {
303
+ id: 'haiku',
304
+ memory_size: 512 * 1024 * 1024,
305
+ hda: {
306
+ url: host + 'haiku-v5/.img',
307
+ size: 1342177280,
308
+ async: true,
309
+ fixed_chunk_size: 1024 * 1024,
310
+ use_parts: true,
311
+ },
312
+ state: { url: host + 'haiku_state-v5.bin.zst' },
313
+ name: 'Haiku',
314
+ homepage: 'https://www.haiku-os.org/',
315
+ acpi: true,
316
+ },
317
+ {
318
+ id: 'haiku-boot',
319
+ memory_size: 512 * 1024 * 1024,
320
+ hda: {
321
+ url: host + 'haiku-v5/.img',
322
+ size: 1342177280,
323
+ async: true,
324
+ fixed_chunk_size: 1024 * 1024,
325
+ use_parts: true,
326
+ },
327
+ name: 'Haiku',
328
+ homepage: 'https://www.haiku-os.org/',
329
+ acpi: true,
330
+ },
331
+ {
332
+ id: 'beos',
333
+ memory_size: 512 * 1024 * 1024,
334
+ hda: {
335
+ url: host + 'beos5/.img',
336
+ size: 536870912,
337
+ async: true,
338
+ fixed_chunk_size: 1024 * 1024,
339
+ use_parts: true,
340
+ },
341
+ name: 'BeOS 5',
342
+ // NOTE: segfaults if 256k bios is used
343
+ },
344
+ {
345
+ id: 'msdos',
346
+ hda: {
347
+ url: host + 'msdos622/.img',
348
+ size: 64 * 1024 * 1024,
349
+ async: true,
350
+ fixed_chunk_size: 256 * 1024,
351
+ use_parts: true,
352
+ },
353
+ name: 'MS-DOS 6.22',
354
+ },
355
+ {
356
+ id: 'msdos4',
357
+ fda: {
358
+ url: host + 'msdos4.img',
359
+ size: 1474560,
360
+ },
361
+ name: 'MS-DOS 4',
362
+ },
363
+ {
364
+ id: 'freedos',
365
+ fda: {
366
+ url: host + 'freedos722.img',
367
+ size: 737280,
368
+ },
369
+ name: 'FreeDOS',
370
+ },
371
+ {
372
+ id: 'doof',
373
+ fda: {
374
+ url: host + 'doof-1440.img',
375
+ size: 1474560,
376
+ },
377
+ name: 'Doom On One Floppy',
378
+ homepage: 'https://github.com/fragglet/squashware',
379
+ },
380
+ {
381
+ id: 'quantixos',
382
+ cdrom: {
383
+ url: host + 'quantixos.iso',
384
+ size: 11784192,
385
+ async: false,
386
+ },
387
+ name: 'QuantixOS',
388
+ homepage: 'https://github.com/MrGilli/QuantixOS',
389
+ },
390
+ {
391
+ id: 'chip4504',
392
+ fda: {
393
+ url: host + 'chip4504.img',
394
+ size: 1474560,
395
+ },
396
+ name: 'Chip4504',
397
+ homepage: 'https://github.com/RelativisticMechanic/chip4504',
398
+ },
399
+ {
400
+ id: 'forthos',
401
+ hda: {
402
+ url: host + 'forthos20.img.zst',
403
+ size: 95420416,
404
+ async: false,
405
+ },
406
+ memory_size: 128 * 1024 * 1024,
407
+ name: 'ForthOS',
408
+ homepage: 'http://sources.vsta.org/forthos/',
409
+ },
410
+ {
411
+ id: 'chimaeraos',
412
+ hda: {
413
+ url: host + 'chimaeraos.img',
414
+ size: 34120704,
415
+ async: false,
416
+ },
417
+ name: 'Chimaera OS',
418
+ homepage: 'https://chimaeraos.org/',
419
+ },
420
+ {
421
+ id: 'freegem',
422
+ hda: {
423
+ url: host + 'freegem/.bin',
424
+ size: 209715200,
425
+ async: true,
426
+ fixed_chunk_size: 256 * 1024,
427
+ use_parts: true,
428
+ },
429
+ name: 'Freedos with FreeGEM',
430
+ },
431
+ {
432
+ id: 'xcom',
433
+ fda: {
434
+ url: host + 'xcom144.img',
435
+ size: 1440 * 1024,
436
+ },
437
+ name: 'Freedos with Xcom',
438
+ homepage: 'http://xcom.infora.hu/index.html',
439
+ },
440
+ {
441
+ id: 'psychdos',
442
+ hda: {
443
+ url: host + 'psychdos/.img',
444
+ size: 549453824,
445
+ async: true,
446
+ fixed_chunk_size: 256 * 1024,
447
+ use_parts: true,
448
+ },
449
+ name: 'PsychDOS',
450
+ homepage: 'https://psychoslinux.gitlab.io/DOS/INDEX.HTM',
451
+ },
452
+ {
453
+ id: '86dos',
454
+ fda: {
455
+ url: host + 'pc86dos.img',
456
+ size: 163840,
457
+ },
458
+ name: '86-DOS',
459
+ homepage: 'https://www.os2museum.com/wp/pc-86-dos/',
460
+ },
461
+ {
462
+ id: 'oberon',
463
+ hda: {
464
+ url: host + 'oberon.img',
465
+ size: 24 * 1024 * 1024,
466
+ async: false,
467
+ },
468
+ name: 'Oberon',
469
+ },
470
+ {
471
+ id: 'windows1',
472
+ fda: {
473
+ url: host + 'windows101.img',
474
+ size: 1474560,
475
+ },
476
+ name: 'Windows 1.01',
477
+ },
478
+ {
479
+ id: 'windows2',
480
+ hda: {
481
+ url: host + 'windows2.img',
482
+ size: 4177920,
483
+ async: false,
484
+ },
485
+ name: 'Windows 2.03',
486
+ },
487
+ {
488
+ id: 'linux26',
489
+ cdrom: {
490
+ url: host + 'linux.iso',
491
+ size: 6547456,
492
+ async: false,
493
+ },
494
+ name: 'Linux',
495
+ },
496
+ {
497
+ id: 'linux3',
498
+ cdrom: {
499
+ url: host + 'linux3.iso',
500
+ size: 8638464,
501
+ async: false,
502
+ },
503
+ name: 'Linux',
504
+ },
505
+ {
506
+ id: 'linux4',
507
+ cdrom: {
508
+ url: host + 'linux4.iso',
509
+ size: 7731200,
510
+ async: false,
511
+ },
512
+ name: 'Linux',
513
+ filesystem: {},
514
+ },
515
+ {
516
+ id: 'buildroot',
517
+ bzimage: {
518
+ url: host + 'buildroot-bzimage.bin',
519
+ size: 5166352,
520
+ async: false,
521
+ },
522
+ name: 'Buildroot Linux',
523
+ filesystem: {},
524
+ cmdline: 'tsc=reliable mitigations=off random.trust_cpu=on',
525
+ mouse_disabled_default: true,
526
+ },
527
+ {
528
+ id: 'buildroot6',
529
+ bzimage: {
530
+ url: host + 'buildroot-bzimage68.bin',
531
+ size: 10068480,
532
+ async: false,
533
+ },
534
+ name: 'Buildroot Linux 6.8',
535
+ filesystem: {},
536
+ cmdline: 'tsc=reliable mitigations=off random.trust_cpu=on',
537
+ },
538
+ {
539
+ id: 'basiclinux',
540
+ hda: {
541
+ url: host + 'bl3-5.img',
542
+ size: 104857600,
543
+ async: false,
544
+ },
545
+ name: 'BasicLinux',
546
+ },
547
+ {
548
+ id: 'xpud',
549
+ cdrom: {
550
+ url: host + 'xpud-0.9.2.iso',
551
+ size: 67108864,
552
+ async: false,
553
+ },
554
+ name: 'xPUD',
555
+ memory_size: 256 * 1024 * 1024,
556
+ },
557
+ {
558
+ id: 'elks',
559
+ hda: {
560
+ url: host + 'elks-hd32-fat.img',
561
+ size: 32514048,
562
+ async: false,
563
+ },
564
+ name: 'ELKS',
565
+ homepage: 'https://github.com/ghaerr/elks',
566
+ },
567
+ {
568
+ id: 'nodeos',
569
+ bzimage: {
570
+ url: host + 'nodeos-kernel.bin',
571
+ size: 14452000,
572
+ async: false,
573
+ },
574
+ name: 'NodeOS',
575
+ cmdline: 'tsc=reliable mitigations=off random.trust_cpu=on',
576
+ },
577
+ {
578
+ id: 'dsl',
579
+ memory_size: 256 * 1024 * 1024,
580
+ cdrom: {
581
+ url: host + 'dsl-4.11.rc2.iso',
582
+ size: 52824064,
583
+ async: false,
584
+ },
585
+ name: 'Damn Small Linux',
586
+ homepage: 'http://www.damnsmalllinux.org/',
587
+ },
588
+ {
589
+ id: 'xwoaf',
590
+ memory_size: 256 * 1024 * 1024,
591
+ cdrom: {
592
+ url: host + 'xwoaf_rebuild4.iso',
593
+ size: 2205696,
594
+ async: false,
595
+ },
596
+ name: 'xwoaf',
597
+ homepage: 'https://pupngo.dk/xwinflpy/xwoaf_rebuild.html',
598
+ },
599
+ {
600
+ id: 'minix',
601
+ name: 'Minix',
602
+ memory_size: 256 * 1024 * 1024,
603
+ cdrom: {
604
+ url: host + 'minix-3.3.0/.iso',
605
+ size: 605581312,
606
+ async: true,
607
+ fixed_chunk_size: 1024 * 1024,
608
+ use_parts: true,
609
+ },
610
+ homepage: 'https://www.minix3.org/',
611
+ },
612
+ {
613
+ id: 'unix-v7',
614
+ name: 'Unix V7',
615
+ hda: {
616
+ url: host + 'unix-v7x86-0.8a/.img',
617
+ size: 152764416,
618
+ async: true,
619
+ fixed_chunk_size: 256 * 1024,
620
+ use_parts: true,
621
+ },
622
+ },
623
+ {
624
+ id: 'kolibrios',
625
+ fda: {
626
+ url: ON_LOCALHOST
627
+ ? host + 'kolibri.img'
628
+ : '//builds.kolibrios.org/en_US/data/data/kolibri.img',
629
+ size: 1474560,
630
+ },
631
+ name: 'KolibriOS',
632
+ homepage: 'https://kolibrios.org/en/',
633
+ },
634
+ {
635
+ id: 'kolibrios-fallback',
636
+ fda: {
637
+ url: host + 'kolibri.img',
638
+ size: 1474560,
639
+ },
640
+ name: 'KolibriOS',
641
+ },
642
+ {
643
+ id: 'mu',
644
+ hda: {
645
+ url: host + 'mu-shell.img',
646
+ size: 10321920,
647
+ async: false,
648
+ },
649
+ memory_size: 256 * 1024 * 1024,
650
+ name: 'Mu',
651
+ homepage: 'https://github.com/akkartik/mu',
652
+ mouse_disabled_default: true, // https://github.com/akkartik/mu/issues/52
653
+ },
654
+ {
655
+ id: 'openbsd',
656
+ hda: {
657
+ url: host + 'openbsd/.img',
658
+ size: 1073741824,
659
+ async: true,
660
+ fixed_chunk_size: 1024 * 1024,
661
+ use_parts: true,
662
+ },
663
+ state: { url: host + 'openbsd_state-v2.bin.zst' },
664
+ memory_size: 256 * 1024 * 1024,
665
+ name: 'OpenBSD',
666
+ },
667
+ {
668
+ id: 'sortix',
669
+ cdrom: {
670
+ url: host + 'sortix-1.0-i686.iso',
671
+ size: 71075840,
672
+ async: false,
673
+ },
674
+ memory_size: 512 * 1024 * 1024,
675
+ name: 'Sortix',
676
+ },
677
+ {
678
+ id: 'openbsd-boot',
679
+ hda: {
680
+ url: host + 'openbsd/.img',
681
+ size: 1073741824,
682
+ async: true,
683
+ fixed_chunk_size: 1024 * 1024,
684
+ use_parts: true,
685
+ },
686
+ memory_size: 256 * 1024 * 1024,
687
+ name: 'OpenBSD',
688
+ //acpi: true, // doesn't seem to work
689
+ },
690
+ {
691
+ id: 'netbsd',
692
+ hda: {
693
+ url: host + 'netbsd/.img',
694
+ size: 511000064,
695
+ async: true,
696
+ fixed_chunk_size: 1024 * 1024,
697
+ use_parts: true,
698
+ },
699
+ memory_size: 256 * 1024 * 1024,
700
+ name: 'NetBSD',
701
+ },
702
+ {
703
+ id: 'crazierl',
704
+ multiboot: {
705
+ url: host + 'crazierl-elf.img',
706
+ size: 896592,
707
+ async: false,
708
+ },
709
+ initrd: {
710
+ url: host + 'crazierl-initrd.img',
711
+ size: 18448316,
712
+ async: false,
713
+ },
714
+ acpi: true,
715
+ cmdline: 'kernel /libexec/ld-elf32.so.1',
716
+ memory_size: 128 * 1024 * 1024,
717
+ name: 'Crazierl',
718
+ },
719
+ {
720
+ id: 'solos',
721
+ fda: {
722
+ url: host + 'os8.img',
723
+ size: 1474560,
724
+ },
725
+ name: 'Sol OS',
726
+ homepage: 'http://oby.ro/os/',
727
+ },
728
+ {
729
+ id: 'bootchess',
730
+ fda: {
731
+ url: host + 'bootchess.img',
732
+ size: 1474560,
733
+ },
734
+ name: 'BootChess',
735
+ homepage: 'http://www.pouet.net/prod.php?which=64962',
736
+ },
737
+ {
738
+ id: 'bootbasic',
739
+ fda: {
740
+ url: host + 'bootbasic.img',
741
+ size: 512,
742
+ },
743
+ name: 'bootBASIC',
744
+ homepage: 'https://github.com/nanochess/bootBASIC',
745
+ },
746
+ {
747
+ id: 'bootlogo',
748
+ fda: {
749
+ url: host + 'bootlogo.img',
750
+ size: 512,
751
+ },
752
+ name: 'bootLogo',
753
+ homepage: 'https://github.com/nanochess/bootLogo',
754
+ },
755
+ {
756
+ id: 'pillman',
757
+ fda: {
758
+ url: host + 'pillman.img',
759
+ size: 512,
760
+ },
761
+ name: 'Pillman',
762
+ homepage: 'https://github.com/nanochess/Pillman',
763
+ },
764
+ {
765
+ id: 'invaders',
766
+ fda: {
767
+ url: host + 'invaders.img',
768
+ size: 512,
769
+ },
770
+ name: 'Invaders',
771
+ homepage: 'https://github.com/nanochess/Invaders',
772
+ },
773
+ {
774
+ id: 'bootos',
775
+ fda: {
776
+ url: host + 'bootos-all.img',
777
+ size: 368640,
778
+ },
779
+ name: 'bootOS',
780
+ homepage: 'https://github.com/nanochess/bootOS',
781
+ },
782
+ {
783
+ id: 'sectorlisp',
784
+ fda: {
785
+ url: host + 'sectorlisp-friendly.bin',
786
+ size: 512,
787
+ },
788
+ name: 'SectorLISP',
789
+ homepage: 'https://justine.lol/sectorlisp2/',
790
+ },
791
+ {
792
+ id: 'sectorforth',
793
+ fda: {
794
+ url: host + 'sectorforth.img',
795
+ size: 512,
796
+ },
797
+ name: 'sectorforth',
798
+ homepage: 'https://github.com/cesarblum/sectorforth',
799
+ },
800
+ {
801
+ id: 'floppybird',
802
+ fda: {
803
+ url: host + 'floppybird.img',
804
+ size: 1474560,
805
+ },
806
+ name: 'Floppy Bird',
807
+ homepage: 'http://mihail.co/floppybird',
808
+ },
809
+ {
810
+ id: 'stillalive',
811
+ fda: {
812
+ url: host + 'stillalive-os.img',
813
+ size: 368640,
814
+ },
815
+ name: 'Still Alive',
816
+ homepage: 'https://github.com/maniekx86/stillalive-os',
817
+ },
818
+ {
819
+ id: 'hello-v86',
820
+ fda: {
821
+ url: host + 'hello-v86.img',
822
+ size: 512,
823
+ },
824
+ name: 'Hello v86',
825
+ },
826
+ {
827
+ id: 'tetros',
828
+ fda: {
829
+ url: host + 'tetros.img',
830
+ size: 512,
831
+ },
832
+ name: 'TetrOS',
833
+ homepage: 'https://github.com/daniel-e/tetros',
834
+ },
835
+ {
836
+ id: 'dino',
837
+ fda: {
838
+ url: host + 'bootdino.img',
839
+ size: 512,
840
+ },
841
+ name: 'dino',
842
+ homepage: 'https://github.com/franeklubi/dino',
843
+ },
844
+ {
845
+ id: 'bootrogue',
846
+ fda: {
847
+ url: host + 'bootrogue.img',
848
+ size: 512,
849
+ },
850
+ name: 'bootRogue',
851
+ homepage: 'https://github.com/nanochess/bootRogue',
852
+ },
853
+ {
854
+ id: 'duskos',
855
+ hda: {
856
+ url: host + 'duskos.img',
857
+ async: false,
858
+ size: 8388608,
859
+ },
860
+ name: 'Dusk OS',
861
+ homepage: 'http://duskos.org/',
862
+ },
863
+ {
864
+ id: 'windows2000',
865
+ memory_size: 512 * 1024 * 1024,
866
+ hda: {
867
+ url: host + 'windows2k-v2/.img',
868
+ size: 2 * 1024 * 1024 * 1024,
869
+ async: true,
870
+ fixed_chunk_size: 256 * 1024,
871
+ use_parts: true,
872
+ },
873
+ name: 'Windows 2000',
874
+ state: { url: host + 'windows2k_state-v4.bin.zst' },
875
+ mac_address_translation: true,
876
+ },
877
+ {
878
+ id: 'windows2000-boot',
879
+ memory_size: 512 * 1024 * 1024,
880
+ hda: {
881
+ url: host + 'windows2k-v2/.img',
882
+ size: 2 * 1024 * 1024 * 1024,
883
+ async: true,
884
+ fixed_chunk_size: 256 * 1024,
885
+ use_parts: true,
886
+ },
887
+ name: 'Windows 2000',
888
+ },
889
+ {
890
+ id: 'windows-me',
891
+ memory_size: 256 * 1024 * 1024,
892
+ hda: {
893
+ url: host + 'windowsme-v3/.img',
894
+ size: 1073741824,
895
+ async: true,
896
+ fixed_chunk_size: 256 * 1024,
897
+ use_parts: true,
898
+ },
899
+ state: { url: host + 'windows-me_state-v3.bin.zst' },
900
+ name: 'Windows ME',
901
+ },
902
+ {
903
+ id: 'windowsnt4',
904
+ memory_size: 512 * 1024 * 1024,
905
+ hda: {
906
+ url: host + 'winnt4_noacpi/.img',
907
+ size: 523837440,
908
+ async: true,
909
+ fixed_chunk_size: 256 * 1024,
910
+ use_parts: true,
911
+ },
912
+ name: 'Windows NT 4.0',
913
+ cpuid_level: 2,
914
+ },
915
+ {
916
+ id: 'windowsnt35',
917
+ memory_size: 256 * 1024 * 1024,
918
+ hda: {
919
+ url: host + 'windowsnt351-v2/.img',
920
+ size: 163577856,
921
+ async: true,
922
+ fixed_chunk_size: 256 * 1024,
923
+ use_parts: true,
924
+ },
925
+ name: 'Windows NT 3.51',
926
+ },
927
+ {
928
+ id: 'windowsnt3',
929
+ memory_size: 256 * 1024 * 1024,
930
+ hda: {
931
+ url: host + 'winnt31/.img',
932
+ size: 87 * 1024 * 1024,
933
+ async: true,
934
+ fixed_chunk_size: 256 * 1024,
935
+ use_parts: true,
936
+ },
937
+ name: 'Windows NT 3.1',
938
+ },
939
+ {
940
+ id: 'windows98',
941
+ memory_size: 128 * 1024 * 1024,
942
+ hda: {
943
+ url: host + 'windows98/.img',
944
+ size: 300 * 1024 * 1024,
945
+ async: true,
946
+ fixed_chunk_size: 256 * 1024,
947
+ use_parts: true,
948
+ },
949
+ name: 'Windows 98',
950
+ state: { url: host + 'windows98_state-v2.bin.zst' },
951
+ mac_address_translation: true,
952
+ },
953
+ {
954
+ id: 'windows98-boot',
955
+ memory_size: 128 * 1024 * 1024,
956
+ hda: {
957
+ url: host + 'windows98/.img',
958
+ size: 300 * 1024 * 1024,
959
+ async: true,
960
+ fixed_chunk_size: 256 * 1024,
961
+ use_parts: true,
962
+ },
963
+ name: 'Windows 98',
964
+ },
965
+ {
966
+ id: 'windows95',
967
+ memory_size: 64 * 1024 * 1024,
968
+ // old image:
969
+ //memory_size: 32 * 1024 * 1024,
970
+ //hda: {
971
+ // url: host + "w95/.img",
972
+ // size: 242049024,
973
+ // async: true,
974
+ // fixed_chunk_size: 256 * 1024,
975
+ // use_parts: true,
976
+ //},
977
+ //state: { url: host + "windows95_state.bin.zst" },
978
+ hda: {
979
+ url: host + 'windows95-v2/.img',
980
+ size: 471859200,
981
+ async: true,
982
+ fixed_chunk_size: 256 * 1024,
983
+ use_parts: true,
984
+ },
985
+ name: 'Windows 95',
986
+ },
987
+ {
988
+ id: 'windows95-boot',
989
+ memory_size: 64 * 1024 * 1024,
990
+ hda: {
991
+ url: host + 'windows95-v2/.img',
992
+ size: 471859200,
993
+ async: true,
994
+ fixed_chunk_size: 256 * 1024,
995
+ use_parts: true,
996
+ },
997
+ name: 'Windows 95',
998
+ },
999
+ {
1000
+ id: 'windows30-old',
1001
+ memory_size: 64 * 1024 * 1024,
1002
+ cdrom: {
1003
+ url: host + 'Win30.iso',
1004
+ size: 7774208,
1005
+ async: false,
1006
+ },
1007
+ name: 'Windows 3.0',
1008
+ },
1009
+ {
1010
+ id: 'windows30',
1011
+ memory_size: 128 * 1024 * 1024,
1012
+ hda: {
1013
+ url: host + 'windows30.img',
1014
+ size: 25165824,
1015
+ async: false,
1016
+ },
1017
+ name: 'Windows 3.0',
1018
+ },
1019
+ {
1020
+ id: 'windows31',
1021
+ memory_size: 64 * 1024 * 1024,
1022
+ hda: {
1023
+ url: host + 'win31.img',
1024
+ async: false,
1025
+ size: 34463744,
1026
+ },
1027
+ name: 'Windows 3.1',
1028
+ },
1029
+ {
1030
+ id: 'tilck',
1031
+ memory_size: 128 * 1024 * 1024,
1032
+ hda: {
1033
+ url: host + 'tilck.img',
1034
+ async: false,
1035
+ size: 37748736,
1036
+ },
1037
+ name: 'Tilck',
1038
+ homepage: 'https://github.com/vvaltchev/tilck',
1039
+ },
1040
+ {
1041
+ id: 'littlekernel',
1042
+ multiboot: {
1043
+ url: host + 'littlekernel-multiboot.img',
1044
+ async: false,
1045
+ size: 969580,
1046
+ },
1047
+ name: 'Little Kernel',
1048
+ homepage: 'https://github.com/littlekernel/lk',
1049
+ },
1050
+ {
1051
+ id: 'sanos',
1052
+ memory_size: 128 * 1024 * 1024,
1053
+ hda: {
1054
+ url: host + 'sanos-flp.img',
1055
+ async: false,
1056
+ size: 1474560,
1057
+ },
1058
+ name: 'Sanos',
1059
+ homepage: 'http://www.jbox.dk/sanos/',
1060
+ },
1061
+ {
1062
+ id: 'freebsd',
1063
+ memory_size: 256 * 1024 * 1024,
1064
+ hda: {
1065
+ url: host + 'freebsd/.img',
1066
+ size: 2147483648,
1067
+ async: true,
1068
+ fixed_chunk_size: 1024 * 1024,
1069
+ use_parts: true,
1070
+ },
1071
+ state: { url: host + 'freebsd_state-v2.bin.zst' },
1072
+ name: 'FreeBSD',
1073
+ },
1074
+ {
1075
+ id: 'freebsd-boot',
1076
+ memory_size: 256 * 1024 * 1024,
1077
+ hda: {
1078
+ url: host + 'freebsd/.img',
1079
+ size: 2147483648,
1080
+ async: true,
1081
+ fixed_chunk_size: 1024 * 1024,
1082
+ use_parts: true,
1083
+ },
1084
+ name: 'FreeBSD',
1085
+ },
1086
+ {
1087
+ id: 'reactos',
1088
+ memory_size: 512 * 1024 * 1024,
1089
+ hda: {
1090
+ url: host + 'reactos-v3/.img',
1091
+ size: 734003200,
1092
+ async: true,
1093
+ fixed_chunk_size: 1024 * 1024,
1094
+ use_parts: true,
1095
+ },
1096
+ state: { url: host + 'reactos_state-v3.bin.zst' },
1097
+ mac_address_translation: true,
1098
+ name: 'ReactOS',
1099
+ acpi: true,
1100
+ net_device_type: 'virtio',
1101
+ homepage: 'https://reactos.org/',
1102
+ },
1103
+ {
1104
+ id: 'reactos-boot',
1105
+ memory_size: 512 * 1024 * 1024,
1106
+ hda: {
1107
+ url: host + 'reactos-v2/.img',
1108
+ size: 681574400,
1109
+ async: true,
1110
+ fixed_chunk_size: 1024 * 1024,
1111
+ use_parts: true,
1112
+ },
1113
+ name: 'ReactOS',
1114
+ acpi: true,
1115
+ homepage: 'https://reactos.org/',
1116
+ },
1117
+ {
1118
+ id: 'skift',
1119
+ cdrom: {
1120
+ url: host + 'skift-20200910.iso',
1121
+ size: 64452608,
1122
+ async: false,
1123
+ },
1124
+ name: 'Skift',
1125
+ homepage: 'https://skiftos.org/',
1126
+ },
1127
+ {
1128
+ id: 'snowdrop',
1129
+ fda: {
1130
+ url: host + 'snowdrop.img',
1131
+ size: 1440 * 1024,
1132
+ },
1133
+ name: 'Snowdrop',
1134
+ homepage: 'http://www.sebastianmihai.com/snowdrop/',
1135
+ },
1136
+ {
1137
+ id: 'openwrt',
1138
+ hda: {
1139
+ url: host + 'openwrt-18.06.1-x86-legacy-combined-squashfs.img',
1140
+ size: 19846474,
1141
+ async: false,
1142
+ },
1143
+ name: 'OpenWrt',
1144
+ },
1145
+ {
1146
+ id: 'qnx',
1147
+ fda: {
1148
+ url: host + 'qnx-demo-network-4.05.img',
1149
+ size: 1474560,
1150
+ },
1151
+ name: 'QNX 4.05',
1152
+ },
1153
+ {
1154
+ id: '9front',
1155
+ memory_size: 128 * 1024 * 1024,
1156
+ hda: {
1157
+ url: host + '9front-10931.386/.iso',
1158
+ size: 489453568,
1159
+ async: true,
1160
+ fixed_chunk_size: 1024 * 1024,
1161
+ use_parts: true,
1162
+ },
1163
+ state: { url: host + '9front_state-v3.bin.zst' },
1164
+ acpi: true,
1165
+ name: '9front',
1166
+ homepage: 'https://9front.org/',
1167
+ },
1168
+ {
1169
+ id: '9front-boot',
1170
+ memory_size: 128 * 1024 * 1024,
1171
+ hda: {
1172
+ url: host + '9front-10931.386/.iso',
1173
+ size: 489453568,
1174
+ async: true,
1175
+ fixed_chunk_size: 1024 * 1024,
1176
+ use_parts: true,
1177
+ },
1178
+ acpi: true,
1179
+ name: '9front',
1180
+ homepage: 'https://9front.org/',
1181
+ },
1182
+ {
1183
+ id: '9legacy',
1184
+ memory_size: 512 * 1024 * 1024,
1185
+ hda: {
1186
+ url: host + '9legacy.img',
1187
+ async: false,
1188
+ size: 16000000,
1189
+ },
1190
+ name: '9legacy',
1191
+ homepage: 'http://www.9legacy.org/',
1192
+ //net_device_type: "none",
1193
+ },
1194
+ {
1195
+ id: 'mobius',
1196
+ fda: {
1197
+ url: host + 'mobius-fd-release5.img',
1198
+ size: 1474560,
1199
+ },
1200
+ name: 'Mobius',
1201
+ },
1202
+ {
1203
+ id: 'android',
1204
+ memory_size: 512 * 1024 * 1024,
1205
+ cdrom: {
1206
+ url: host + 'android-x86-1.6-r2/.iso',
1207
+ size: 54661120,
1208
+ async: true,
1209
+ fixed_chunk_size: 1024 * 1024,
1210
+ use_parts: true,
1211
+ },
1212
+ name: 'Android',
1213
+ },
1214
+ {
1215
+ id: 'android4',
1216
+ memory_size: 512 * 1024 * 1024,
1217
+ cdrom: {
1218
+ url: host + 'android_x86_nonsse3_4.4r1_20140904/.iso',
1219
+ size: 247463936,
1220
+ async: true,
1221
+ fixed_chunk_size: 1024 * 1024,
1222
+ use_parts: true,
1223
+ },
1224
+ name: 'Android 4',
1225
+ },
1226
+ {
1227
+ id: 'tinycore',
1228
+ memory_size: 256 * 1024 * 1024,
1229
+ hda: {
1230
+ url: host + 'TinyCore-11.0.iso',
1231
+ size: 19922944,
1232
+ async: false,
1233
+ },
1234
+ name: 'Tinycore',
1235
+ homepage: 'http://www.tinycorelinux.net/',
1236
+ },
1237
+ {
1238
+ id: 'slitaz',
1239
+ memory_size: 512 * 1024 * 1024,
1240
+ hda: {
1241
+ url: host + 'slitaz-rolling-2024.iso',
1242
+ size: 56573952,
1243
+ async: false,
1244
+ },
1245
+ name: 'SliTaz',
1246
+ homepage: 'https://slitaz.org/',
1247
+ },
1248
+ {
1249
+ id: 'freenos',
1250
+ memory_size: 256 * 1024 * 1024,
1251
+ cdrom: {
1252
+ url: host + 'FreeNOS-1.0.3.iso',
1253
+ async: false,
1254
+ size: 11014144,
1255
+ },
1256
+ name: 'FreeNOS',
1257
+ acpi: true,
1258
+ homepage: 'http://www.freenos.org/',
1259
+ },
1260
+ {
1261
+ id: 'syllable',
1262
+ memory_size: 512 * 1024 * 1024,
1263
+ hda: {
1264
+ url: host + 'syllable-destop-0.6.7/.img',
1265
+ async: true,
1266
+ size: 500 * 1024 * 1024,
1267
+ fixed_chunk_size: 512 * 1024,
1268
+ use_parts: true,
1269
+ },
1270
+ name: 'Syllable',
1271
+ homepage: 'http://syllable.metaproject.frl/',
1272
+ },
1273
+ {
1274
+ id: 'toaruos',
1275
+ memory_size: 512 * 1024 * 1024,
1276
+ cdrom: {
1277
+ url: host + 'toaruos-1.6.1-core.iso',
1278
+ size: 67567616,
1279
+ async: false,
1280
+ },
1281
+ name: 'ToaruOS',
1282
+ acpi: true,
1283
+ homepage: 'https://toaruos.org/',
1284
+ },
1285
+ {
1286
+ id: 'nopeos',
1287
+ cdrom: {
1288
+ url: host + 'nopeos-0.1.iso',
1289
+ size: 532480,
1290
+ async: false,
1291
+ },
1292
+ name: 'Nope OS',
1293
+ homepage: 'https://github.com/d99kris/nopeos',
1294
+ },
1295
+ {
1296
+ id: 'soso',
1297
+ cdrom: {
1298
+ url: host + 'soso.iso',
1299
+ size: 22546432,
1300
+ async: false,
1301
+ },
1302
+ name: 'Soso',
1303
+ homepage: 'https://github.com/ozkl/soso',
1304
+ },
1305
+ {
1306
+ id: 'pcmos',
1307
+ fda: {
1308
+ url: host + 'PCMOS386-9-user-patched.img',
1309
+ size: 1440 * 1024,
1310
+ },
1311
+ name: 'PC-MOS/386',
1312
+ homepage: 'https://github.com/roelandjansen/pcmos386v501',
1313
+ },
1314
+ {
1315
+ id: 'jx',
1316
+ fda: {
1317
+ url: host + 'jx-demo.img',
1318
+ size: 1440 * 1024,
1319
+ },
1320
+ name: 'JX',
1321
+ homepage: 'https://www4.cs.fau.de/Projects/JX/index.html',
1322
+ },
1323
+ {
1324
+ id: 'house',
1325
+ fda: {
1326
+ url: host + 'hOp-0.8.img',
1327
+ size: 1440 * 1024,
1328
+ },
1329
+ name: 'House',
1330
+ homepage: 'https://programatica.cs.pdx.edu/House/',
1331
+ },
1332
+ {
1333
+ id: 'bleskos',
1334
+ name: 'BleskOS',
1335
+ cdrom: {
1336
+ url: host + 'bleskos_2024u32.iso',
1337
+ size: 1835008,
1338
+ async: false,
1339
+ },
1340
+ homepage: 'https://github.com/VendelinSlezak/BleskOS',
1341
+ },
1342
+ {
1343
+ id: 'boneos',
1344
+ name: 'BoneOS',
1345
+ cdrom: {
1346
+ url: host + 'BoneOS.iso',
1347
+ size: 11429888,
1348
+ async: false,
1349
+ },
1350
+ homepage: 'https://amanuel.io/projects/BoneOS/',
1351
+ },
1352
+ {
1353
+ id: 'mikeos',
1354
+ name: 'MikeOS',
1355
+ cdrom: {
1356
+ url: host + 'mikeos.iso',
1357
+ size: 3311616,
1358
+ async: false,
1359
+ },
1360
+ homepage: 'https://mikeos.sourceforge.net/',
1361
+ },
1362
+ {
1363
+ id: 'bluejay',
1364
+ name: 'Blue Jay',
1365
+ fda: {
1366
+ url: host + 'bj050.img',
1367
+ size: 1474560,
1368
+ },
1369
+ homepage: 'https://archiveos.org/blue-jay/',
1370
+ },
1371
+ {
1372
+ id: 't3xforth',
1373
+ name: 'T3XFORTH',
1374
+ fda: {
1375
+ url: host + 't3xforth.img',
1376
+ size: 1474560,
1377
+ },
1378
+ homepage: 'https://t3x.org/t3xforth/',
1379
+ },
1380
+ {
1381
+ id: 'nanoshell',
1382
+ name: 'NanoShell',
1383
+ cdrom: {
1384
+ url: host + 'nanoshell.iso',
1385
+ size: 6785024,
1386
+ async: false,
1387
+ },
1388
+ homepage: 'https://github.com/iProgramMC/NanoShellOS',
1389
+ },
1390
+ {
1391
+ id: 'catk',
1392
+ name: 'CatK',
1393
+ cdrom: {
1394
+ url: host + 'catkernel.iso',
1395
+ size: 11968512,
1396
+ async: false,
1397
+ },
1398
+ homepage: 'https://catk.neocities.org/',
1399
+ },
1400
+ {
1401
+ id: 'mcp',
1402
+ name: 'M/CP',
1403
+ fda: {
1404
+ url: host + 'mcp2.img',
1405
+ size: 512,
1406
+ },
1407
+ homepage: 'https://github.com/ybuzoku/MCP',
1408
+ },
1409
+ {
1410
+ id: 'ibm-exploring',
1411
+ name: 'Exploring The IBM Personal Computer',
1412
+ fda: {
1413
+ url: host + 'ibm-exploring.img',
1414
+ size: 368640,
1415
+ },
1416
+ },
1417
+ {
1418
+ id: 'leetos',
1419
+ name: 'lEEt/OS',
1420
+ fda: {
1421
+ url: host + 'leetos.img',
1422
+ size: 1474560,
1423
+ },
1424
+ homepage: 'http://sininenankka.dy.fi/leetos/index.php',
1425
+ },
1426
+ {
1427
+ id: 'newos',
1428
+ name: 'NewOS',
1429
+ fda: {
1430
+ url: host + 'newos-flp.img',
1431
+ size: 1474560,
1432
+ async: false,
1433
+ },
1434
+ homepage: 'https://newos.org/',
1435
+ },
1436
+ {
1437
+ id: 'newos-notion',
1438
+ hda: {
1439
+ url: host + 'newos-notion.img',
1440
+ size: 4128768,
1441
+ async: false,
1442
+ },
1443
+ memory_size: 128 * 1024 * 1024,
1444
+ name: 'NewOS Notion',
1445
+ homepage: 'http://notion.muelln-kommune.net/newos.html',
1446
+ },
1447
+ {
1448
+ id: 'aros-broadway',
1449
+ name: 'AROS Broadway',
1450
+ memory_size: 512 * 1024 * 1024,
1451
+ cdrom: {
1452
+ url: host + 'broadway10/.iso',
1453
+ size: 742051840,
1454
+ async: true,
1455
+ fixed_chunk_size: 512 * 1024,
1456
+ use_parts: true,
1457
+ },
1458
+ homepage:
1459
+ 'https://web.archive.org/web/20231109224346/http://www.aros-broadway.de/',
1460
+ },
1461
+ {
1462
+ id: 'icaros',
1463
+ name: 'Icaros Desktop',
1464
+ memory_size: 512 * 1024 * 1024,
1465
+ cdrom: {
1466
+ url: host + 'icaros-pc-i386-2.3/.iso',
1467
+ size: 726511616,
1468
+ async: true,
1469
+ // NOTE: needs 136MB/287 requests to boot, maybe state image or zst parts?
1470
+ fixed_chunk_size: 512 * 1024,
1471
+ use_parts: true,
1472
+ },
1473
+ homepage: 'http://vmwaros.blogspot.com/',
1474
+ },
1475
+ {
1476
+ id: 'tinyaros',
1477
+ name: 'Tiny Aros',
1478
+ memory_size: 512 * 1024 * 1024,
1479
+ cdrom: {
1480
+ url: host + 'tinyaros-pc-i386/.iso',
1481
+ size: 111175680,
1482
+ async: true,
1483
+ fixed_chunk_size: 512 * 1024,
1484
+ use_parts: true,
1485
+ },
1486
+ homepage: 'https://www.tinyaros.it/',
1487
+ },
1488
+ {
1489
+ id: 'dancy',
1490
+ name: 'Dancy',
1491
+ cdrom: {
1492
+ url: host + 'dancy.iso',
1493
+ size: 10485760,
1494
+ async: false,
1495
+ },
1496
+ homepage: 'https://github.com/Tiihala/Dancy',
1497
+ },
1498
+ {
1499
+ id: 'curios',
1500
+ name: 'CuriOS',
1501
+ hda: {
1502
+ url: host + 'curios.img',
1503
+ size: 83886080,
1504
+ async: false,
1505
+ },
1506
+ homepage: 'https://github.com/h5n1xp/CuriOS',
1507
+ },
1508
+ {
1509
+ id: 'os64',
1510
+ name: 'OS64',
1511
+ cdrom: {
1512
+ url: host + 'os64boot.iso',
1513
+ size: 5580800,
1514
+ async: false,
1515
+ },
1516
+ homepage: 'https://os64.blogspot.com/',
1517
+ },
1518
+ {
1519
+ id: 'ipxe',
1520
+ name: 'iPXE',
1521
+ cdrom: {
1522
+ url: host + 'ipxe.iso',
1523
+ size: 4194304,
1524
+ async: false,
1525
+ },
1526
+ homepage: 'https://ipxe.org/',
1527
+ },
1528
+ {
1529
+ id: 'netboot.xyz',
1530
+ name: 'netboot.xyz',
1531
+ cdrom: {
1532
+ url: host + 'netboot.xyz.iso',
1533
+ size: 2398208,
1534
+ async: false,
1535
+ },
1536
+ homepage: 'https://netboot.xyz/',
1537
+ net_device_type: 'virtio',
1538
+ },
1539
+ {
1540
+ id: 'squeaknos',
1541
+ name: 'SqueakNOS',
1542
+ cdrom: {
1543
+ url: host + 'SqueakNOS.iso',
1544
+ size: 61171712,
1545
+ async: false,
1546
+ },
1547
+ memory_size: 512 * 1024 * 1024,
1548
+ homepage: 'https://squeaknos.blogspot.com/',
1549
+ },
1550
+ {
1551
+ id: 'chokanji4',
1552
+ name: 'Chokanji 4',
1553
+ hda: {
1554
+ url: host + 'chokanji4/.img.zst',
1555
+ size: 10737418240,
1556
+ async: true,
1557
+ fixed_chunk_size: 256 * 1024,
1558
+ use_parts: true,
1559
+ },
1560
+ memory_size: 512 * 1024 * 1024,
1561
+ homepage: 'https://archive.org/details/brightv4000',
1562
+ },
1563
+ {
1564
+ id: 'archhurd',
1565
+ name: 'Arch Hurd',
1566
+ hda: {
1567
+ url: host + 'archhurd-2018.09.28/.img.zst',
1568
+ size: 4294967296,
1569
+ async: true,
1570
+ fixed_chunk_size: 1024 * 1024,
1571
+ use_parts: true,
1572
+ },
1573
+ memory_size: 512 * 1024 * 1024,
1574
+ homepage: 'https://archhurd.org/',
1575
+ },
1576
+ {
1577
+ id: 'prettyos',
1578
+ name: 'PrettyOS',
1579
+ fda: {
1580
+ url: host + 'prettyos.img',
1581
+ size: 1474560,
1582
+ async: false,
1583
+ },
1584
+ homepage: 'https://www.prettyos.de/Image.html',
1585
+ },
1586
+ {
1587
+ id: 'vanadium',
1588
+ name: 'Vanadium OS',
1589
+ cdrom: {
1590
+ url: host + 'vanadiumos.iso',
1591
+ size: 8388608,
1592
+ async: false,
1593
+ },
1594
+ homepage: 'https://www.durlej.net/software.html',
1595
+ },
1596
+ {
1597
+ id: 'xenus',
1598
+ name: 'XENUS',
1599
+ hda: {
1600
+ url: host + 'xenushdd.img',
1601
+ size: 52428800,
1602
+ async: false,
1603
+ },
1604
+ homepage: 'https://www.durlej.net/xenus/',
1605
+ },
1606
+ {
1607
+ id: 'mojo',
1608
+ name: 'Mojo OS',
1609
+ cdrom: {
1610
+ url: host + 'mojo-0.2.2.iso',
1611
+ size: 4048896,
1612
+ async: false,
1613
+ },
1614
+ homepage: 'https://archiveos.org/mojoos/',
1615
+ },
1616
+ {
1617
+ id: 'bsdos',
1618
+ memory_size: 128 * 1024 * 1024,
1619
+ name: 'BSD/OS',
1620
+ hda: {
1621
+ url: host + 'bsdos43/.img.zst',
1622
+ size: 1024 * 1024 * 1024,
1623
+ async: true,
1624
+ fixed_chunk_size: 1024 * 1024,
1625
+ use_parts: true,
1626
+ },
1627
+ state: { url: host + 'bsdos43_state.bin' },
1628
+ homepage: 'https://en.wikipedia.org/wiki/BSD/OS',
1629
+ },
1630
+ {
1631
+ id: 'bsdos-boot',
1632
+ memory_size: 128 * 1024 * 1024,
1633
+ name: 'BSD/OS',
1634
+ hda: {
1635
+ url: host + 'bsdos43/.img.zst',
1636
+ size: 1024 * 1024 * 1024,
1637
+ async: true,
1638
+ fixed_chunk_size: 1024 * 1024,
1639
+ use_parts: true,
1640
+ },
1641
+ homepage: 'https://en.wikipedia.org/wiki/BSD/OS',
1642
+ },
1643
+ {
1644
+ id: 'asuro',
1645
+ name: 'Asuro',
1646
+ cdrom: {
1647
+ url: host + 'asuro.iso',
1648
+ size: 5361664,
1649
+ async: false,
1650
+ },
1651
+ homepage: 'https://asuro.xyz/',
1652
+ },
1653
+ ]
1654
+
1655
+ if (DEBUG) {
1656
+ // see tests/kvm-unit-tests/x86/
1657
+ const tests = [
1658
+ 'realmode',
1659
+ // All tests below require an APIC
1660
+ 'cmpxchg8b',
1661
+ 'port80',
1662
+ 'setjmp',
1663
+ 'sieve',
1664
+ 'hypercall', // crashes
1665
+ 'init', // stops execution
1666
+ 'msr', // TODO: Expects 64 bit msrs
1667
+ 'smap', // test stops, SMAP not enabled
1668
+ 'tsc_adjust', // TODO: IA32_TSC_ADJUST
1669
+ 'tsc', // TODO: rdtscp
1670
+ 'rmap_chain', // crashes
1671
+ 'memory', // missing mfence (uninteresting)
1672
+ 'taskswitch', // TODO: Jump
1673
+ 'taskswitch2', // TODO: Call TSS
1674
+ 'eventinj', // Missing #nt
1675
+ 'ioapic',
1676
+ 'apic',
1677
+ ]
1678
+
1679
+ for (const test of tests) {
1680
+ oses.push({
1681
+ name: 'Test case: ' + test,
1682
+ id: 'test-' + test,
1683
+ memory_size: 128 * 1024 * 1024,
1684
+ multiboot: {
1685
+ url: 'tests/kvm-unit-tests/x86/' + test + '.flat',
1686
+ },
1687
+ })
1688
+ }
1689
+ }
1690
+
1691
+ const profile = query_args.get('profile')
1692
+
1693
+ if (!profile && !DEBUG) {
1694
+ const link = document.createElement('link')
1695
+ link.rel = 'prefetch'
1696
+ link.href = 'build/v86.wasm' + query_append()
1697
+ document.head.appendChild(link)
1698
+ }
1699
+
1700
+ const link = document.createElement('link')
1701
+ link.rel = 'prefetch'
1702
+ link.href = 'build/xterm.js'
1703
+ document.head.appendChild(link)
1704
+
1705
+ for (const os of oses) {
1706
+ if (profile === os.id) {
1707
+ start_emulation(os, query_args)
1708
+ return
1709
+ }
1710
+
1711
+ const element = $('start_' + os.id)
1712
+
1713
+ if (element) {
1714
+ element.onclick = (e) => {
1715
+ if (!e.ctrlKey) {
1716
+ e.preventDefault()
1717
+ ;(element as HTMLElement).blur()
1718
+ start_emulation(os, null)
1719
+ }
1720
+ }
1721
+ }
1722
+ }
1723
+
1724
+ if (profile === 'custom') {
1725
+ // TODO: if one of the file form fields has a value (firefox), start here?
1726
+
1727
+ if (
1728
+ query_args.has('hda.url') ||
1729
+ query_args.has('cdrom.url') ||
1730
+ query_args.has('fda.url')
1731
+ ) {
1732
+ start_emulation(null, query_args)
1733
+ return
1734
+ }
1735
+ } else if (/^[a-zA-Z0-9\-_]+\/[a-zA-Z0-9\-_]+$/g.test(profile || '')) {
1736
+ // experimental: server that allows user-uploaded images
1737
+
1738
+ const base = 'https://v86-user-images.b-cdn.net/' + profile
1739
+
1740
+ fetch(base + '/profile.json')
1741
+ .catch((_e) => alert('Profile not found: ' + profile))
1742
+ .then((response) => (response as Response).json())
1743
+
1744
+ .then((p: any) => {
1745
+ function handle_image(o: any) {
1746
+ return (
1747
+ o && {
1748
+ url: base + '/' + o['url'],
1749
+ async: o['async'],
1750
+ size: o['size'],
1751
+ }
1752
+ )
1753
+ }
1754
+
1755
+ const fetched_profile: OSProfile = {
1756
+ id: p['id'],
1757
+ name: p['name'],
1758
+ memory_size: p['memory_size'],
1759
+ vga_memory_size: p['vga_memory_size'],
1760
+ acpi: p['acpi'],
1761
+ boot_order: p['boot_order'],
1762
+ hda: handle_image(p['hda']),
1763
+ cdrom: handle_image(p['cdrom']),
1764
+ fda: handle_image(p['fda']),
1765
+ multiboot: handle_image(p['multiboot']),
1766
+ bzimage: handle_image(p['bzimage']),
1767
+ initrd: handle_image(p['initrd']),
1768
+ }
1769
+
1770
+ start_emulation(fetched_profile, query_args)
1771
+ })
1772
+ }
1773
+
1774
+ if (query_args.has('m'))
1775
+ ($('memory_size') as HTMLInputElement).value = query_args.get('m')!
1776
+ if (query_args.has('vram'))
1777
+ ($('vga_memory_size') as HTMLInputElement).value =
1778
+ query_args.get('vram')!
1779
+ if (query_args.has('relay_url'))
1780
+ ($('relay_url') as HTMLInputElement).value =
1781
+ query_args.get('relay_url')!
1782
+ if (query_args.has('mute'))
1783
+ ($('disable_audio') as HTMLInputElement).checked = bool_arg(
1784
+ query_args.get('mute'),
1785
+ )
1786
+ if (query_args.has('acpi'))
1787
+ ($('acpi') as HTMLInputElement).checked = bool_arg(
1788
+ query_args.get('acpi'),
1789
+ )
1790
+ if (query_args.has('boot_order'))
1791
+ ($('boot_order') as HTMLInputElement).value =
1792
+ query_args.get('boot_order')!
1793
+ if (query_args.has('net_device_type'))
1794
+ ($('net_device_type') as HTMLInputElement).value =
1795
+ query_args.get('net_device_type')!
1796
+ if (query_args.has('mtu'))
1797
+ ($('mtu') as HTMLInputElement).value = query_args.get('mtu')!
1798
+
1799
+ for (const dev of ['fda', 'fdb']) {
1800
+ const toggle = $(dev + '_toggle_empty_disk')
1801
+ if (!toggle) continue
1802
+
1803
+ toggle.onclick = function (e) {
1804
+ e.preventDefault()
1805
+ const select = document.createElement('select')
1806
+ select.id = dev + '_empty_size'
1807
+ for (const n_sect of [
1808
+ 320, 360, 400, 640, 720, 800, 1440, 2400, 2880, 3444, 5760,
1809
+ 7680,
1810
+ ]) {
1811
+ const n_bytes = n_sect * 512,
1812
+ kB = 1024,
1813
+ MB = kB * 1000
1814
+ const option = document.createElement('option')
1815
+ if (n_bytes < MB) {
1816
+ option.textContent = n_bytes / kB + ' kB'
1817
+ } else {
1818
+ option.textContent = (n_bytes / MB).toFixed(2) + ' MB'
1819
+ }
1820
+ if (n_sect === 2880) {
1821
+ option.selected = true
1822
+ }
1823
+
1824
+ ;(option as any).value = n_bytes
1825
+ select.appendChild(option)
1826
+ }
1827
+ // TODO (when closure compiler supports it): parent.parentNode.replaceChildren(...);
1828
+ const parent = toggle.parentNode as HTMLElement
1829
+ parent.innerHTML = ''
1830
+ parent.append('Empty disk of ', select)
1831
+ }
1832
+ }
1833
+
1834
+ for (const dev of ['hda', 'hdb']) {
1835
+ const toggle = $(dev + '_toggle_empty_disk')
1836
+ if (!toggle) continue
1837
+
1838
+ toggle.onclick = function (e) {
1839
+ e.preventDefault()
1840
+ const input = document.createElement('input')
1841
+ input.id = dev + '_empty_size'
1842
+ input.type = 'number'
1843
+ input.min = '0'
1844
+ input.max = String(MAX_ARRAY_BUFFER_SIZE_MB)
1845
+ input.step = '100'
1846
+ input.value = '100'
1847
+ // TODO (when closure compiler supports it): parent.parentNode.replaceChildren(...);
1848
+ const parent = toggle.parentNode as HTMLElement
1849
+ parent.innerHTML = ''
1850
+ parent.append('Empty disk of ', input, ' MB')
1851
+ }
1852
+ }
1853
+
1854
+ const os_info = Array.from(document.querySelectorAll('#oses a.tr')).map(
1855
+ (element) => {
1856
+ const match =
1857
+ element.children[1].textContent!.match(/([\d.]+)\+? (\w+)/)!
1858
+ const [_, size_raw, unit] = match
1859
+ let size = +size_raw
1860
+ if (unit === 'MB') size *= 1024 * 1024
1861
+ else if (unit === 'KB') size *= 1024
1862
+ return {
1863
+ element: element as HTMLElement,
1864
+ size,
1865
+ graphical:
1866
+ (element.children[2].firstChild as HTMLElement)
1867
+ .className === 'gui_icon',
1868
+ family: element.children[3].textContent!.replace(/-like/, ''),
1869
+ arch: element.children[4].textContent,
1870
+ status: element.children[5].textContent,
1871
+ source: element.children[6].textContent,
1872
+ languages: new Set(
1873
+ element.children[7].textContent!.split(', '),
1874
+ ),
1875
+ medium: element.children[8].textContent,
1876
+ }
1877
+ },
1878
+ )
1879
+
1880
+ interface FilterDef {
1881
+ id: string
1882
+ condition: (os: (typeof os_info)[number]) => boolean
1883
+ element?: HTMLInputElement
1884
+ }
1885
+
1886
+ const known_filter: FilterDef[][] = [
1887
+ [
1888
+ // Family:
1889
+ { id: 'linux', condition: (os) => os.family === 'Linux' },
1890
+ { id: 'bsd', condition: (os) => os.family === 'BSD' },
1891
+ { id: 'windows', condition: (os) => os.family === 'Windows' },
1892
+ { id: 'unix', condition: (os) => os.family === 'Unix' },
1893
+ { id: 'dos', condition: (os) => os.family === 'DOS' },
1894
+ { id: 'custom', condition: (os) => os.family === 'Custom' },
1895
+ ],
1896
+ [
1897
+ // UI:
1898
+ { id: 'graphical', condition: (os) => os.graphical },
1899
+ { id: 'text', condition: (os) => !os.graphical },
1900
+ ],
1901
+ [
1902
+ // Medium:
1903
+ { id: 'floppy', condition: (os) => os.medium === 'Floppy' },
1904
+ { id: 'cd', condition: (os) => os.medium === 'CD' },
1905
+ { id: 'hd', condition: (os) => os.medium === 'HD' },
1906
+ ],
1907
+ [
1908
+ // Size:
1909
+ { id: 'bootsector', condition: (os) => os.size <= 512 },
1910
+ { id: 'lt5mb', condition: (os) => os.size <= 5 * 1024 * 1024 },
1911
+ { id: 'gt5mb', condition: (os) => os.size > 5 * 1024 * 1024 },
1912
+ ],
1913
+ [
1914
+ // Status:
1915
+ { id: 'modern', condition: (os) => os.status === 'Modern' },
1916
+ { id: 'historic', condition: (os) => os.status === 'Historic' },
1917
+ ],
1918
+ [
1919
+ // License:
1920
+ {
1921
+ id: 'opensource',
1922
+ condition: (os) => os.source === 'Open-source',
1923
+ },
1924
+ {
1925
+ id: 'proprietary',
1926
+ condition: (os) => os.source === 'Proprietary',
1927
+ },
1928
+ ],
1929
+ [
1930
+ // Arch:
1931
+ { id: '16bit', condition: (os) => os.arch === '16-bit' },
1932
+ { id: '32bit', condition: (os) => os.arch === '32-bit' },
1933
+ ],
1934
+ [
1935
+ // Lang:
1936
+ { id: 'asm', condition: (os) => os.languages.has('ASM') },
1937
+ { id: 'c', condition: (os) => os.languages.has('C') },
1938
+ { id: 'cpp', condition: (os) => os.languages.has('C++') },
1939
+ {
1940
+ id: 'other_lang',
1941
+ condition: (os) =>
1942
+ ['ASM', 'C', 'C++'].every(
1943
+ (lang) => !os.languages.has(lang),
1944
+ ),
1945
+ },
1946
+ ],
1947
+ ]
1948
+
1949
+ const defined_filter: FilterDef[][] = []
1950
+ for (const known_category of known_filter) {
1951
+ const category = known_category.filter((filter) => {
1952
+ const element = document.getElementById(
1953
+ `filter_${filter.id}`,
1954
+ ) as HTMLInputElement | null
1955
+ if (element) {
1956
+ element.onchange = update_filters
1957
+ filter.element = element
1958
+ }
1959
+ return element
1960
+ })
1961
+ if (category.length) {
1962
+ defined_filter.push(category)
1963
+ }
1964
+ }
1965
+
1966
+ function update_filters(): void {
1967
+ const conjunction: FilterDef[][] = []
1968
+ for (const category of defined_filter) {
1969
+ const disjunction = category.filter(
1970
+ (filter) => filter.element!.checked,
1971
+ )
1972
+ if (disjunction.length) {
1973
+ conjunction.push(disjunction)
1974
+ }
1975
+ }
1976
+ for (const os of os_info) {
1977
+ os.element.style.display = conjunction.every((disjunction) =>
1978
+ disjunction.some((filter) => filter.condition(os)),
1979
+ )
1980
+ ? ''
1981
+ : 'none'
1982
+ }
1983
+ }
1984
+
1985
+ if ($('reset_filters')) {
1986
+ $('reset_filters')!.onclick = function () {
1987
+ for (const element of document.querySelectorAll(
1988
+ '#filter input[type=checkbox]',
1989
+ )) {
1990
+ ;(element as HTMLInputElement).checked = false
1991
+ }
1992
+ update_filters()
1993
+ }
1994
+ }
1995
+
1996
+ function set_proxy_value(id: string, value: string): void {
1997
+ const elem = $(id)
1998
+ if (elem) {
1999
+ elem.onclick = () =>
2000
+ (($('relay_url') as HTMLInputElement).value = value)
2001
+ }
2002
+ }
2003
+ set_proxy_value('network_none', '')
2004
+ set_proxy_value('network_inbrowser', 'inbrowser')
2005
+ set_proxy_value('network_fetch', 'fetch')
2006
+ set_proxy_value('network_relay', 'wss://relay.widgetry.org/')
2007
+ set_proxy_value('network_wisp', 'wisps://wisp.mercurywork.shop/v86/')
2008
+ }
2009
+
2010
+ function debug_onload(): void {
2011
+ // called on window.onload, in debug mode
2012
+
2013
+ const log_levels = $('log_levels')
2014
+
2015
+ if (!log_levels) {
2016
+ return
2017
+ }
2018
+
2019
+ for (let i = 0; i < LOG_NAMES.length; i++) {
2020
+ const mask = LOG_NAMES[i][0]
2021
+
2022
+ if (mask === 1) continue
2023
+
2024
+ const name = (LOG_NAMES[i][1] as string).toLowerCase()
2025
+ const input = document.createElement('input')
2026
+ const label = document.createElement('label')
2027
+
2028
+ input.type = 'checkbox'
2029
+
2030
+ label.htmlFor = input.id = 'log_' + name
2031
+
2032
+ if (LOG_LEVEL & (mask as number)) {
2033
+ input.checked = true
2034
+ }
2035
+
2036
+ ;(input as any).mask = mask
2037
+
2038
+ label.append(input, pads(name, 4) + ' ')
2039
+ log_levels.appendChild(label)
2040
+
2041
+ if (i === Math.floor(LOG_NAMES.length / 2)) {
2042
+ log_levels.append('\n')
2043
+ }
2044
+ }
2045
+
2046
+ log_levels.onchange = function (e) {
2047
+ const target = e.target as HTMLInputElement
2048
+
2049
+ const mask = (target as any).mask as number
2050
+
2051
+ if (target.checked) {
2052
+ set_log_level(LOG_LEVEL | mask)
2053
+ } else {
2054
+ set_log_level(LOG_LEVEL & ~mask)
2055
+ }
2056
+
2057
+ target.blur()
2058
+ }
2059
+ }
2060
+
2061
+ window.addEventListener('load', onload, false)
2062
+
2063
+ // old webkit fires popstate on every load, fuck webkit
2064
+ // https://code.google.com/p/chromium/issues/detail?id=63040
2065
+ window.addEventListener('load', function () {
2066
+ setTimeout(function () {
2067
+ window.addEventListener('popstate', onpopstate)
2068
+ }, 0)
2069
+ })
2070
+
2071
+ // works in firefox and chromium
2072
+ if (document.readyState === 'complete') {
2073
+ onload()
2074
+ }
2075
+
2076
+ // we can get here in various ways:
2077
+ // - the user clicked on the "start emulation" button
2078
+ // - the user clicked on a profile
2079
+ // - the ?profile= query parameter specified a valid profile
2080
+ // - the ?profile= query parameter was set to "custom" and at least one disk image was given
2081
+
2082
+ function start_emulation(
2083
+ profile: OSProfile | null,
2084
+ query_args: URLSearchParams | null,
2085
+ ): void {
2086
+ $('boot_options')!.style.display = 'none'
2087
+
2088
+ const new_query_args = new Map<string, string>()
2089
+ new_query_args.set('profile', profile?.id || 'custom')
2090
+
2091
+ const settings: any = {}
2092
+
2093
+ if (profile) {
2094
+ if (profile.state) {
2095
+ $('reset')!.style.display = 'none'
2096
+ }
2097
+
2098
+ set_title(profile.name)
2099
+
2100
+ settings.initial_state = profile.state
2101
+ settings.filesystem = profile.filesystem
2102
+ settings.fda = profile.fda
2103
+ settings.fdb = profile.fdb
2104
+ settings.cdrom = profile.cdrom
2105
+ settings.hda = profile.hda
2106
+ settings.hdb = profile.hdb
2107
+ settings.multiboot = profile.multiboot
2108
+ settings.bzimage = profile.bzimage
2109
+ settings.initrd = profile.initrd
2110
+ settings.cmdline = profile.cmdline
2111
+ settings.bzimage_initrd_from_filesystem =
2112
+ profile.bzimage_initrd_from_filesystem
2113
+ settings.mac_address_translation = profile.mac_address_translation
2114
+ settings.cpuid_level = profile.cpuid_level
2115
+ settings.acpi = profile.acpi
2116
+ settings.memory_size = profile.memory_size
2117
+ settings.vga_memory_size = profile.vga_memory_size
2118
+ settings.boot_order = profile.boot_order
2119
+ settings.net_device_type = profile.net_device_type
2120
+
2121
+ if (!DEBUG && profile.homepage) {
2122
+ $('description')!.style.display = 'block'
2123
+ const link = document.createElement('a')
2124
+ link.href = profile.homepage
2125
+ link.textContent = profile.name
2126
+ link.target = '_blank'
2127
+ $('description')!.append(document.createTextNode('Running '), link)
2128
+ }
2129
+ }
2130
+
2131
+ if (query_args) {
2132
+ // ignore certain settings when using a state image
2133
+ if (!settings.initial_state) {
2134
+ let chunk_size = parseInt(query_args.get('chunk_size')!, 10)
2135
+ if (chunk_size >= 0) {
2136
+ chunk_size = Math.min(
2137
+ 4 * 1024 * 1024,
2138
+ Math.max(512, chunk_size),
2139
+ )
2140
+ chunk_size = round_up_to_next_power_of_2(chunk_size)
2141
+ } else {
2142
+ chunk_size = 256 * 1024
2143
+ }
2144
+
2145
+ if (query_args.has('hda.url')) {
2146
+ settings.hda = {
2147
+ size:
2148
+ parseInt(query_args.get('hda.size')!, 10) || undefined,
2149
+ // TODO: synchronous if small?
2150
+ url: query_args.get('hda.url'),
2151
+ fixed_chunk_size: chunk_size,
2152
+ async: true,
2153
+ }
2154
+ } else if (query_args.has('hda.empty')) {
2155
+ const empty_size = parseInt(query_args.get('hda.empty')!, 10)
2156
+ if (empty_size > 0) {
2157
+ settings.hda = { buffer: new ArrayBuffer(empty_size) }
2158
+ }
2159
+ }
2160
+
2161
+ if (query_args.has('hdb.url')) {
2162
+ settings.hdb = {
2163
+ size:
2164
+ parseInt(query_args.get('hdb.size')!, 10) || undefined,
2165
+ // TODO: synchronous if small?
2166
+ url: query_args.get('hdb.url'),
2167
+ fixed_chunk_size: chunk_size,
2168
+ async: true,
2169
+ }
2170
+ } else if (query_args.has('hdb.empty')) {
2171
+ const empty_size = parseInt(query_args.get('hdb.empty')!, 10)
2172
+ if (empty_size > 0) {
2173
+ settings.hdb = { buffer: new ArrayBuffer(empty_size) }
2174
+ }
2175
+ }
2176
+
2177
+ if (query_args.has('cdrom.url')) {
2178
+ settings.cdrom = {
2179
+ size:
2180
+ parseInt(query_args.get('cdrom.size')!, 10) ||
2181
+ undefined,
2182
+ url: query_args.get('cdrom.url'),
2183
+ fixed_chunk_size: chunk_size,
2184
+ async: true,
2185
+ }
2186
+ }
2187
+
2188
+ if (query_args.has('fda.url')) {
2189
+ settings.fda = {
2190
+ size:
2191
+ parseInt(query_args.get('fda.size')!, 10) || undefined,
2192
+ url: query_args.get('fda.url'),
2193
+ async: false,
2194
+ }
2195
+ }
2196
+
2197
+ const m = parseInt(query_args.get('m')!, 10)
2198
+ if (m > 0) {
2199
+ settings.memory_size = Math.max(16, m) * 1024 * 1024
2200
+ }
2201
+
2202
+ const vram = parseInt(query_args.get('vram')!, 10)
2203
+ if (vram > 0) {
2204
+ settings.vga_memory_size = vram * 1024 * 1024
2205
+ }
2206
+
2207
+ settings.acpi = query_args.has('acpi')
2208
+ ? bool_arg(query_args.get('acpi'))
2209
+ : settings.acpi
2210
+ settings.use_bochs_bios = query_args.get('bios') === 'bochs'
2211
+ settings.net_device_type =
2212
+ query_args.get('net_device_type') || settings.net_device_type
2213
+ settings.mtu = parseInt(query_args.get('mtu')!, 10) || undefined
2214
+ }
2215
+
2216
+ settings.relay_url = query_args.get('relay_url')
2217
+ settings.disable_jit = bool_arg(query_args.get('disable_jit'))
2218
+ settings.disable_audio = bool_arg(query_args.get('mute'))
2219
+ }
2220
+
2221
+ if (!settings.relay_url) {
2222
+ settings.relay_url = ($('relay_url') as HTMLInputElement).value
2223
+ if (!DEFAULT_NETWORKING_PROXIES.includes(settings.relay_url))
2224
+ new_query_args.set('relay_url', settings.relay_url)
2225
+ }
2226
+ if (settings.relay_url.startsWith('fetch:')) {
2227
+ settings.cors_proxy = settings.relay_url.slice(6)
2228
+ settings.relay_url = 'fetch'
2229
+ }
2230
+ settings.disable_audio =
2231
+ ($('disable_audio') as HTMLInputElement).checked ||
2232
+ settings.disable_audio
2233
+ if (settings.disable_audio) new_query_args.set('mute', '1')
2234
+
2235
+ // some settings cannot be overridden when a state image is used
2236
+ if (!settings.initial_state) {
2237
+ const bios = ($('bios') as HTMLInputElement).files![0]
2238
+ if (bios) {
2239
+ settings.bios = { buffer: bios }
2240
+ }
2241
+ const vga_bios = ($('vga_bios') as HTMLInputElement).files![0]
2242
+ if (vga_bios) {
2243
+ settings.vga_bios = { buffer: vga_bios }
2244
+ }
2245
+ const fda = ($('fda_image') as HTMLInputElement | null)?.files?.[0]
2246
+ if (fda) {
2247
+ settings.fda = { buffer: fda }
2248
+ }
2249
+ const fda_empty_size = +(
2250
+ ($('fda_empty_size') as HTMLInputElement | null)?.value ?? ''
2251
+ )
2252
+ if (fda_empty_size) {
2253
+ settings.fda = { buffer: new ArrayBuffer(fda_empty_size) }
2254
+ }
2255
+ const fdb = ($('fdb_image') as HTMLInputElement | null)?.files?.[0]
2256
+ if (fdb) {
2257
+ settings.fdb = { buffer: fdb }
2258
+ }
2259
+ const fdb_empty_size = +(
2260
+ ($('fdb_empty_size') as HTMLInputElement | null)?.value ?? ''
2261
+ )
2262
+ if (fdb_empty_size) {
2263
+ settings.fdb = { buffer: new ArrayBuffer(fdb_empty_size) }
2264
+ }
2265
+ const cdrom = ($('cdrom_image') as HTMLInputElement).files![0]
2266
+ if (cdrom) {
2267
+ settings.cdrom = { buffer: cdrom }
2268
+ }
2269
+ const hda = ($('hda_image') as HTMLInputElement | null)?.files?.[0]
2270
+ if (hda) {
2271
+ settings.hda = { buffer: hda }
2272
+ }
2273
+ const hda_empty_size = +(
2274
+ ($('hda_empty_size') as HTMLInputElement | null)?.value ?? ''
2275
+ )
2276
+ if (hda_empty_size) {
2277
+ const size =
2278
+ Math.max(
2279
+ 1,
2280
+ Math.min(MAX_ARRAY_BUFFER_SIZE_MB, hda_empty_size),
2281
+ ) *
2282
+ 1024 *
2283
+ 1024
2284
+ settings.hda = { buffer: new ArrayBuffer(size) }
2285
+ new_query_args.set('hda.empty', String(size))
2286
+ }
2287
+ const hdb = ($('hdb_image') as HTMLInputElement | null)?.files?.[0]
2288
+ if (hdb) {
2289
+ settings.hdb = { buffer: hdb }
2290
+ }
2291
+ const hdb_empty_size = +(
2292
+ ($('hdb_empty_size') as HTMLInputElement | null)?.value ?? ''
2293
+ )
2294
+ if (hdb_empty_size) {
2295
+ const size =
2296
+ Math.max(
2297
+ 1,
2298
+ Math.min(MAX_ARRAY_BUFFER_SIZE_MB, hdb_empty_size),
2299
+ ) *
2300
+ 1024 *
2301
+ 1024
2302
+ settings.hdb = { buffer: new ArrayBuffer(size) }
2303
+ new_query_args.set('hdb.empty', String(size))
2304
+ }
2305
+ const multiboot = ($('multiboot_image') as HTMLInputElement | null)
2306
+ ?.files?.[0]
2307
+ if (multiboot) {
2308
+ settings.multiboot = { buffer: multiboot }
2309
+ }
2310
+ const bzimage = ($('bzimage') as HTMLInputElement).files![0]
2311
+ if (bzimage) {
2312
+ settings.bzimage = { buffer: bzimage }
2313
+ }
2314
+ const initrd = ($('initrd') as HTMLInputElement).files![0]
2315
+ if (initrd) {
2316
+ settings.initrd = { buffer: initrd }
2317
+ }
2318
+
2319
+ const title =
2320
+ multiboot?.name ||
2321
+ hda?.name ||
2322
+ cdrom?.name ||
2323
+ hdb?.name ||
2324
+ fda?.name ||
2325
+ bios?.name
2326
+ if (title) {
2327
+ set_title(title)
2328
+ }
2329
+
2330
+ const MB = 1024 * 1024
2331
+
2332
+ const memory_size =
2333
+ parseInt(($('memory_size') as HTMLInputElement).value, 10) ||
2334
+ DEFAULT_MEMORY_SIZE
2335
+ if (!settings.memory_size || memory_size !== DEFAULT_MEMORY_SIZE) {
2336
+ settings.memory_size = memory_size * MB
2337
+ }
2338
+ if (memory_size !== DEFAULT_MEMORY_SIZE)
2339
+ new_query_args.set('m', String(memory_size))
2340
+
2341
+ const vga_memory_size =
2342
+ parseInt(($('vga_memory_size') as HTMLInputElement).value, 10) ||
2343
+ DEFAULT_VGA_MEMORY_SIZE
2344
+ if (
2345
+ !settings.vga_memory_size ||
2346
+ vga_memory_size !== DEFAULT_VGA_MEMORY_SIZE
2347
+ ) {
2348
+ settings.vga_memory_size = vga_memory_size * MB
2349
+ }
2350
+ if (vga_memory_size !== DEFAULT_VGA_MEMORY_SIZE)
2351
+ new_query_args.set('vram', String(vga_memory_size))
2352
+
2353
+ const boot_order =
2354
+ parseInt(($('boot_order') as HTMLInputElement).value, 16) ||
2355
+ DEFAULT_BOOT_ORDER
2356
+ if (!settings.boot_order || boot_order !== DEFAULT_BOOT_ORDER) {
2357
+ settings.boot_order = boot_order
2358
+ }
2359
+ if (settings.boot_order !== DEFAULT_BOOT_ORDER)
2360
+ new_query_args.set('boot_order', settings.boot_order.toString(16))
2361
+
2362
+ if (settings.acpi === undefined) {
2363
+ settings.acpi = ($('acpi') as HTMLInputElement).checked
2364
+ if (settings.acpi) new_query_args.set('acpi', '1')
2365
+ }
2366
+
2367
+ const BIOSPATH = 'bios/'
2368
+
2369
+ if (!settings.bios) {
2370
+ settings.bios = {
2371
+ url: BIOSPATH + (DEBUG ? 'seabios-debug.bin' : 'seabios.bin'),
2372
+ }
2373
+ }
2374
+ if (!settings.vga_bios) {
2375
+ settings.vga_bios = {
2376
+ url: BIOSPATH + (DEBUG ? 'vgabios-debug.bin' : 'vgabios.bin'),
2377
+ }
2378
+ }
2379
+ if (settings.use_bochs_bios) {
2380
+ settings.bios = { url: BIOSPATH + 'bochs-bios.bin' }
2381
+ settings.vga_bios = { url: BIOSPATH + 'bochs-vgabios.bin' }
2382
+ }
2383
+
2384
+ const nic_type =
2385
+ ($('net_device_type') as HTMLInputElement).value || DEFAULT_NIC_TYPE
2386
+ if (!settings.net_device_type || nic_type !== DEFAULT_NIC_TYPE) {
2387
+ settings.net_device_type = nic_type
2388
+ }
2389
+ if (settings.net_device_type !== DEFAULT_NIC_TYPE)
2390
+ new_query_args.set('net_device_type', settings.net_device_type)
2391
+
2392
+ const mtu =
2393
+ parseInt(($('mtu') as HTMLInputElement).value, 10) || DEFAULT_MTU
2394
+ if (!settings.mtu || mtu !== DEFAULT_MTU) {
2395
+ settings.mtu = mtu
2396
+ }
2397
+ if (settings.mtu !== DEFAULT_MTU)
2398
+ new_query_args.set('mtu', settings.mtu.toString())
2399
+ }
2400
+
2401
+ if (!query_args) {
2402
+ push_state(new_query_args)
2403
+ }
2404
+
2405
+ const emulator = new V86({
2406
+ wasm_path:
2407
+ 'build/' + (DEBUG ? 'v86-debug.wasm' : 'v86.wasm') + query_append(),
2408
+ screen: {
2409
+ container: $('screen_container'),
2410
+ use_graphical_text: false,
2411
+ },
2412
+ net_device: {
2413
+ type: settings.net_device_type || DEFAULT_NIC_TYPE,
2414
+ relay_url: settings.relay_url,
2415
+ cors_proxy: settings.cors_proxy,
2416
+ mtu: settings.mtu,
2417
+ },
2418
+ autostart: true,
2419
+
2420
+ memory_size: settings.memory_size,
2421
+ vga_memory_size: settings.vga_memory_size,
2422
+ boot_order: settings.boot_order,
2423
+
2424
+ bios: settings.bios,
2425
+ vga_bios: settings.vga_bios,
2426
+ fda: settings.fda,
2427
+ fdb: settings.fdb,
2428
+ hda: settings.hda,
2429
+ hdb: settings.hdb,
2430
+ cdrom: settings.cdrom,
2431
+ multiboot: settings.multiboot,
2432
+ bzimage: settings.bzimage,
2433
+ initrd: settings.initrd,
2434
+
2435
+ cmdline: settings.cmdline,
2436
+ bzimage_initrd_from_filesystem: settings.bzimage_initrd_from_filesystem,
2437
+ acpi: settings.acpi,
2438
+ disable_jit: settings.disable_jit,
2439
+ initial_state: settings.initial_state,
2440
+ filesystem: settings.filesystem || {},
2441
+ disable_speaker: settings.disable_audio,
2442
+ mac_address_translation: settings.mac_address_translation,
2443
+ cpuid_level: settings.cpuid_level,
2444
+ })
2445
+
2446
+ if (DEBUG) (window as any).emulator = emulator
2447
+
2448
+ emulator.add_listener('emulator-ready', function () {
2449
+ if (DEBUG) {
2450
+ debug_start(emulator)
2451
+ }
2452
+
2453
+ if (emulator.v86.cpu.wm.exports['profiler_is_enabled']()) {
2454
+ const CLEAR_STATS = false
2455
+
2456
+ const panel = document.createElement('pre')
2457
+ document.body.appendChild(panel)
2458
+
2459
+ setInterval(
2460
+ function () {
2461
+ if (!emulator.is_running()) {
2462
+ return
2463
+ }
2464
+
2465
+ panel.textContent = emulator.get_instruction_stats()
2466
+
2467
+ if (CLEAR_STATS) {
2468
+ emulator.v86.cpu.clear_opstats()
2469
+ }
2470
+ },
2471
+ CLEAR_STATS ? 5000 : 1000,
2472
+ )
2473
+ }
2474
+
2475
+ if (
2476
+ [
2477
+ 'dsl',
2478
+ 'helenos',
2479
+ 'android',
2480
+ 'android4',
2481
+ 'redox',
2482
+ 'beos',
2483
+ '9legacy',
2484
+ ].includes(profile?.id ?? '')
2485
+ ) {
2486
+ setTimeout(() => {
2487
+ // hack: Start automatically
2488
+ emulator.keyboard_send_text(
2489
+ profile!.id === '9legacy' ? '1\n' : '\n',
2490
+ )
2491
+ }, 3000)
2492
+ }
2493
+
2494
+ init_ui(profile, settings, emulator)
2495
+
2496
+ if (query_args?.has('c')) {
2497
+ setTimeout(function () {
2498
+ emulator.keyboard_send_text(query_args!.get('c') + '\n')
2499
+ }, 25)
2500
+ }
2501
+
2502
+ if (query_args?.has('s')) {
2503
+ setTimeout(function () {
2504
+ emulator.serial0_send(query_args!.get('s') + '\n')
2505
+ }, 25)
2506
+ }
2507
+
2508
+ if (
2509
+ query_args?.has('theatre') &&
2510
+ bool_arg(query_args?.get('theatre'))
2511
+ ) {
2512
+ ;($('toggle_theatre') as HTMLElement).click()
2513
+ }
2514
+ })
2515
+
2516
+ emulator.add_listener('emulator-loaded', function () {
2517
+ if (!emulator.v86.cpu.devices.cdrom) {
2518
+ $('change_cdrom_image')!.style.display = 'none'
2519
+ }
2520
+ })
2521
+
2522
+ emulator.add_listener('download-progress', function (e: any) {
2523
+ show_progress(e)
2524
+ })
2525
+
2526
+ emulator.add_listener('download-error', function (e: any) {
2527
+ const el = $('loading')!
2528
+ el.style.display = 'block'
2529
+ el.textContent = `Loading ${e.file_name} failed. Check your connection and reload the page to try again.`
2530
+ })
2531
+ }
2532
+
2533
+ function init_ui(
2534
+ profile: OSProfile | null,
2535
+ settings: any,
2536
+ emulator: V86,
2537
+ ): void {
2538
+ $('loading')!.style.display = 'none'
2539
+ $('runtime_options')!.style.display = 'block'
2540
+ $('runtime_infos')!.style.display = 'block'
2541
+ $('screen_container')!.style.display = 'block'
2542
+
2543
+ let filesystem_is_enabled = false
2544
+
2545
+ if (settings.filesystem) {
2546
+ filesystem_is_enabled = true
2547
+ init_filesystem_panel(emulator)
2548
+ } else {
2549
+ emulator.add_listener('9p-attach', function () {
2550
+ filesystem_is_enabled = true
2551
+ init_filesystem_panel(emulator)
2552
+ })
2553
+ }
2554
+
2555
+ $('run')!.onclick = function () {
2556
+ if (emulator.is_running()) {
2557
+ $('run')!.textContent = 'Run'
2558
+ emulator.stop()
2559
+ } else {
2560
+ $('run')!.textContent = 'Pause'
2561
+ emulator.run()
2562
+ }
2563
+
2564
+ ;($('run') as HTMLElement).blur()
2565
+ }
2566
+
2567
+ $('exit')!.onclick = function () {
2568
+ emulator.destroy()
2569
+ const url = new URL(location.href)
2570
+ url.searchParams.delete('profile')
2571
+ location.href = url.pathname + url.search
2572
+ }
2573
+
2574
+ $('lock_mouse')!.onclick = function () {
2575
+ if (!mouse_is_enabled) {
2576
+ ;($('toggle_mouse') as HTMLElement).click()
2577
+ }
2578
+
2579
+ emulator.lock_mouse()
2580
+ ;($('lock_mouse') as HTMLElement).blur()
2581
+ }
2582
+
2583
+ let mouse_is_enabled = true
2584
+
2585
+ $('toggle_mouse')!.onclick = function () {
2586
+ mouse_is_enabled = !mouse_is_enabled
2587
+
2588
+ emulator.mouse_set_enabled(mouse_is_enabled)
2589
+ $('toggle_mouse')!.textContent =
2590
+ (mouse_is_enabled ? 'Dis' : 'En') + 'able mouse'
2591
+ ;($('toggle_mouse') as HTMLElement).blur()
2592
+ }
2593
+
2594
+ if (profile?.mouse_disabled_default) {
2595
+ ;($('toggle_mouse') as HTMLElement).click()
2596
+ }
2597
+
2598
+ let theatre_mode = false
2599
+ let theatre_ui = true
2600
+ let theatre_zoom_to_fit = false
2601
+
2602
+ function zoom_to_fit(): void {
2603
+ // reset size
2604
+ emulator.screen_set_scale(1, 1)
2605
+
2606
+ const emulator_screen = $('screen_container')!.getBoundingClientRect()
2607
+ const emulator_screen_width = emulator_screen.width
2608
+ const emulator_screen_height = emulator_screen.height
2609
+
2610
+ const viewport_screen_width = window.innerWidth
2611
+ const viewport_screen_height = window.innerHeight
2612
+
2613
+ const n = Math.min(
2614
+ viewport_screen_width / emulator_screen_width,
2615
+ viewport_screen_height / emulator_screen_height,
2616
+ )
2617
+ emulator.screen_set_scale(n, n)
2618
+ }
2619
+
2620
+ function enable_theatre_ui(enabled: boolean): void {
2621
+ theatre_ui = enabled
2622
+
2623
+ $('runtime_options')!.style.display = theatre_ui ? 'block' : 'none'
2624
+ $('runtime_infos')!.style.display = theatre_ui ? 'block' : 'none'
2625
+ $('filesystem_panel')!.style.display =
2626
+ filesystem_is_enabled && theatre_ui ? 'block' : 'none'
2627
+
2628
+ $('toggle_ui')!.textContent = (theatre_ui ? 'Hide' : 'Show') + ' UI'
2629
+ }
2630
+
2631
+ function enable_zoom_to_fit(enabled: boolean): void {
2632
+ theatre_zoom_to_fit = enabled
2633
+ ;($('scale') as HTMLInputElement).disabled = theatre_zoom_to_fit
2634
+
2635
+ if (theatre_zoom_to_fit) {
2636
+ window.addEventListener('resize', zoom_to_fit, true)
2637
+ emulator.add_listener('screen-set-size', zoom_to_fit)
2638
+
2639
+ zoom_to_fit()
2640
+ } else {
2641
+ window.removeEventListener('resize', zoom_to_fit, true)
2642
+ emulator.remove_listener('screen-set-size', zoom_to_fit)
2643
+
2644
+ const n = parseFloat(($('scale') as HTMLInputElement).value) || 1
2645
+ emulator.screen_set_scale(n, n)
2646
+ }
2647
+
2648
+ $('toggle_zoom_to_fit')!.textContent =
2649
+ (theatre_zoom_to_fit ? 'Dis' : 'En') + 'able zoom to fit'
2650
+ }
2651
+
2652
+ function enable_theatre_mode(enabled: boolean): void {
2653
+ theatre_mode = enabled
2654
+
2655
+ if (!theatre_ui) {
2656
+ enable_theatre_ui(true)
2657
+ }
2658
+
2659
+ if (!theatre_mode && theatre_zoom_to_fit) {
2660
+ enable_zoom_to_fit(false)
2661
+ }
2662
+
2663
+ for (const el of [
2664
+ 'screen_container',
2665
+ 'runtime_options',
2666
+ 'runtime_infos',
2667
+ 'filesystem_panel',
2668
+ ]) {
2669
+ $(el)!.classList.toggle('theatre_' + el)
2670
+ }
2671
+
2672
+ $('theatre_background')!.style.display = theatre_mode ? 'block' : 'none'
2673
+ $('toggle_zoom_to_fit')!.style.display = theatre_mode
2674
+ ? 'inline'
2675
+ : 'none'
2676
+ $('toggle_ui')!.style.display = theatre_mode ? 'block' : 'none'
2677
+
2678
+ // hide scrolling
2679
+ document.body.style.overflow = theatre_mode ? 'hidden' : 'visible'
2680
+
2681
+ $('toggle_theatre')!.textContent =
2682
+ (theatre_mode ? 'Dis' : 'En') + 'able theatre mode'
2683
+ }
2684
+
2685
+ $('toggle_ui')!.onclick = function () {
2686
+ enable_theatre_ui(!theatre_ui)
2687
+ ;($('toggle_ui') as HTMLElement).blur()
2688
+ }
2689
+
2690
+ $('toggle_theatre')!.onclick = function () {
2691
+ enable_theatre_mode(!theatre_mode)
2692
+ ;($('toggle_theatre') as HTMLElement).blur()
2693
+ }
2694
+
2695
+ $('toggle_zoom_to_fit')!.onclick = function () {
2696
+ enable_zoom_to_fit(!theatre_zoom_to_fit)
2697
+ ;($('toggle_zoom_to_fit') as HTMLElement).blur()
2698
+ }
2699
+
2700
+ let last_tick = 0
2701
+ let running_time = 0
2702
+ let last_instr_counter = 0
2703
+ let interval: ReturnType<typeof setInterval> | null = null
2704
+ let os_uses_mouse = false
2705
+ let total_instructions = 0
2706
+
2707
+ function update_info(): void {
2708
+ const now = Date.now()
2709
+
2710
+ const instruction_counter = emulator.get_instruction_counter()
2711
+
2712
+ if (instruction_counter < last_instr_counter) {
2713
+ // 32-bit wrap-around
2714
+ last_instr_counter -= 0x100000000
2715
+ }
2716
+
2717
+ const last_ips = instruction_counter - last_instr_counter
2718
+ last_instr_counter = instruction_counter
2719
+ total_instructions += last_ips
2720
+
2721
+ const delta_time = now - last_tick
2722
+
2723
+ if (delta_time) {
2724
+ running_time += delta_time
2725
+ last_tick = now
2726
+
2727
+ $('speed')!.textContent = (last_ips / 1000 / delta_time).toFixed(1)
2728
+ $('avg_speed')!.textContent = (
2729
+ total_instructions /
2730
+ 1000 /
2731
+ running_time
2732
+ ).toFixed(1)
2733
+ $('running_time')!.textContent = format_timestamp(
2734
+ (running_time / 1000) | 0,
2735
+ )
2736
+ }
2737
+ }
2738
+
2739
+ emulator.add_listener('emulator-started', function () {
2740
+ last_tick = Date.now()
2741
+ interval = setInterval(update_info, 1000)
2742
+ })
2743
+
2744
+ emulator.add_listener('emulator-stopped', function () {
2745
+ update_info()
2746
+ if (interval !== null) {
2747
+ clearInterval(interval)
2748
+ }
2749
+ })
2750
+
2751
+ const stats_9p = {
2752
+ read: 0,
2753
+ write: 0,
2754
+ files: [] as string[],
2755
+ }
2756
+
2757
+ emulator.add_listener('9p-read-start', function (args: any) {
2758
+ const file = args[0]
2759
+ stats_9p.files.push(file)
2760
+ $('info_filesystem')!.style.display = 'block'
2761
+ $('info_filesystem_status')!.textContent = 'Loading ...'
2762
+ $('info_filesystem_last_file')!.textContent = file
2763
+ })
2764
+
2765
+ emulator.add_listener('9p-read-end', function (args: any) {
2766
+ stats_9p.read += args[1]
2767
+ $('info_filesystem_bytes_read')!.textContent = String(stats_9p.read)
2768
+
2769
+ const file = args[0]
2770
+ stats_9p.files = stats_9p.files.filter((f) => f !== file)
2771
+
2772
+ if (stats_9p.files[0]) {
2773
+ $('info_filesystem_last_file')!.textContent = stats_9p.files[0]
2774
+ } else {
2775
+ $('info_filesystem_status')!.textContent = 'Idle'
2776
+ }
2777
+ })
2778
+
2779
+ emulator.add_listener('9p-write-end', function (args: any) {
2780
+ stats_9p.write += args[1]
2781
+ $('info_filesystem_bytes_written')!.textContent = String(stats_9p.write)
2782
+
2783
+ if (!stats_9p.files[0]) {
2784
+ $('info_filesystem_last_file')!.textContent = args[0]
2785
+ }
2786
+ })
2787
+
2788
+ const stats_storage = {
2789
+ read: 0,
2790
+ read_sectors: 0,
2791
+ write: 0,
2792
+ write_sectors: 0,
2793
+ }
2794
+
2795
+ $('ide_type')!.textContent = settings.cdrom ? ' (CD-ROM)' : ' (hard disk)'
2796
+
2797
+ emulator.add_listener('ide-read-start', function () {
2798
+ $('info_storage')!.style.display = 'block'
2799
+ $('info_storage_status')!.textContent = 'Loading ...'
2800
+ })
2801
+
2802
+ emulator.add_listener('ide-read-end', function (args: any) {
2803
+ stats_storage.read += args[1]
2804
+ stats_storage.read_sectors += args[2]
2805
+
2806
+ $('info_storage_status')!.textContent = 'Idle'
2807
+ $('info_storage_bytes_read')!.textContent = String(stats_storage.read)
2808
+ $('info_storage_sectors_read')!.textContent = String(
2809
+ stats_storage.read_sectors,
2810
+ )
2811
+ })
2812
+
2813
+ emulator.add_listener('ide-write-end', function (args: any) {
2814
+ stats_storage.write += args[1]
2815
+ stats_storage.write_sectors += args[2]
2816
+
2817
+ $('info_storage_bytes_written')!.textContent = String(
2818
+ stats_storage.write,
2819
+ )
2820
+ $('info_storage_sectors_written')!.textContent = String(
2821
+ stats_storage.write_sectors,
2822
+ )
2823
+ })
2824
+
2825
+ const stats_net = {
2826
+ bytes_transmitted: 0,
2827
+ bytes_received: 0,
2828
+ }
2829
+
2830
+ emulator.add_listener('eth-receive-end', function (args: any) {
2831
+ stats_net.bytes_received += args[0]
2832
+
2833
+ $('info_network')!.style.display = 'block'
2834
+ $('info_network_bytes_received')!.textContent = String(
2835
+ stats_net.bytes_received,
2836
+ )
2837
+ })
2838
+
2839
+ emulator.add_listener('eth-transmit-end', function (args: any) {
2840
+ stats_net.bytes_transmitted += args[0]
2841
+
2842
+ $('info_network')!.style.display = 'block'
2843
+ $('info_network_bytes_transmitted')!.textContent = String(
2844
+ stats_net.bytes_transmitted,
2845
+ )
2846
+ })
2847
+
2848
+ emulator.add_listener('mouse-enable', function (is_enabled: boolean) {
2849
+ os_uses_mouse = is_enabled
2850
+ $('info_mouse_enabled')!.textContent = is_enabled ? 'Yes' : 'No'
2851
+ })
2852
+
2853
+ emulator.add_listener('screen-set-size', function (args: any) {
2854
+ const [w, h_val, bpp] = args
2855
+ $('info_res')!.textContent = w + 'x' + h_val + (bpp ? 'x' + bpp : '')
2856
+ $('info_vga_mode')!.textContent = bpp ? 'Graphical' : 'Text'
2857
+ })
2858
+
2859
+ $('reset')!.onclick = function () {
2860
+ emulator.restart()
2861
+ ;($('reset') as HTMLElement).blur()
2862
+ }
2863
+
2864
+ add_image_download_button(
2865
+ settings.hda,
2866
+ () => emulator.v86.cpu.devices.ide.primary.master.buffer,
2867
+ 'hda',
2868
+ )
2869
+ add_image_download_button(
2870
+ settings.hdb,
2871
+ () => emulator.v86.cpu.devices.ide.primary.slave.buffer,
2872
+ 'hdb',
2873
+ )
2874
+ add_image_download_button(
2875
+ settings.fda,
2876
+ () => emulator.v86.cpu.devices.fdc.drives[0].buffer,
2877
+ 'fda',
2878
+ )
2879
+ add_image_download_button(
2880
+ settings.fdb,
2881
+ () => emulator.v86.cpu.devices.fdc.drives[1].buffer,
2882
+ 'fdb',
2883
+ )
2884
+ add_image_download_button(
2885
+ settings.cdrom,
2886
+ () => emulator.v86.cpu.devices.cdrom.buffer,
2887
+ 'cdrom',
2888
+ )
2889
+
2890
+ function add_image_download_button(
2891
+ obj: any,
2892
+ get_buffer: () => any,
2893
+ type: string,
2894
+ ): void {
2895
+ const elem = $('get_' + type + '_image')!
2896
+
2897
+ if (!obj || obj.async) {
2898
+ elem.style.display = 'none'
2899
+ }
2900
+
2901
+ elem.onclick = function (_e) {
2902
+ const buffer = get_buffer()
2903
+ const filename =
2904
+ (buffer.file && buffer.file.name) ||
2905
+ (profile?.id || 'v86') +
2906
+ '-' +
2907
+ type +
2908
+ (type === 'cdrom' ? '.iso' : '.img')
2909
+
2910
+ if (buffer.get_as_file) {
2911
+ const file = buffer.get_as_file(filename)
2912
+ download(file, filename)
2913
+ } else {
2914
+ buffer.get_buffer(function (b: any) {
2915
+ if (b) {
2916
+ dump_file(b, filename)
2917
+ } else {
2918
+ alert(
2919
+ "The file could not be loaded. Maybe it's too big?",
2920
+ )
2921
+ }
2922
+ })
2923
+ }
2924
+
2925
+ ;(elem as HTMLElement).blur()
2926
+ }
2927
+ }
2928
+
2929
+ function pick_file(multiple: boolean): Promise<FileList> {
2930
+ return new Promise((resolve) => {
2931
+ const file_input = document.createElement('input')
2932
+ file_input.type = 'file'
2933
+ file_input.multiple = multiple
2934
+ file_input.onchange = function () {
2935
+ resolve(file_input.files!)
2936
+ }
2937
+ file_input.oncancel = function () {
2938
+ resolve([] as any)
2939
+ }
2940
+ file_input.click()
2941
+ })
2942
+ }
2943
+
2944
+ $('change_fda_image')!.textContent = settings.fda
2945
+ ? 'Eject floppy image'
2946
+ : 'Insert floppy image'
2947
+ $('change_fda_image')!.ondragover = function (e) {
2948
+ e.preventDefault()
2949
+ }
2950
+ async function insert_fda(files: FileList | File[]): Promise<void> {
2951
+ const file = files[0]
2952
+ if (file) {
2953
+ await emulator.set_fda({ buffer: file })
2954
+ $('change_fda_image')!.textContent = 'Eject floppy image'
2955
+ $('get_fda_image')!.style.display = 'block'
2956
+ }
2957
+ }
2958
+ $('change_fda_image')!.ondrop = function (e) {
2959
+ e.preventDefault()
2960
+ if (emulator.get_disk_fda()) {
2961
+ emulator.eject_fda()
2962
+ }
2963
+ insert_fda(e.dataTransfer!.files)
2964
+ }
2965
+ $('change_fda_image')!.onclick = async function () {
2966
+ if (emulator.get_disk_fda()) {
2967
+ emulator.eject_fda()
2968
+ $('change_fda_image')!.textContent = 'Insert floppy image'
2969
+ $('get_fda_image')!.style.display = 'none'
2970
+ } else {
2971
+ const files = await pick_file(false)
2972
+ insert_fda(files)
2973
+ }
2974
+ ;($('change_fda_image') as HTMLElement).blur()
2975
+ }
2976
+
2977
+ $('change_fdb_image')!.textContent = settings.fdb
2978
+ ? 'Eject second floppy image'
2979
+ : 'Insert second floppy image'
2980
+ $('change_fdb_image')!.ondragover = function (e) {
2981
+ e.preventDefault()
2982
+ }
2983
+ async function insert_fdb(files: FileList | File[]): Promise<void> {
2984
+ const file = files[0]
2985
+ if (file) {
2986
+ await emulator.set_fdb({ buffer: file })
2987
+ $('change_fdb_image')!.textContent = 'Eject second floppy image'
2988
+ $('get_fdb_image')!.style.display = 'block'
2989
+ }
2990
+ }
2991
+ $('change_fdb_image')!.ondrop = function (e) {
2992
+ e.preventDefault()
2993
+ if (emulator.get_disk_fdb()) {
2994
+ emulator.eject_fdb()
2995
+ }
2996
+ insert_fdb(e.dataTransfer!.files)
2997
+ }
2998
+ $('change_fdb_image')!.onclick = async function () {
2999
+ if (emulator.get_disk_fdb()) {
3000
+ emulator.eject_fdb()
3001
+ $('change_fdb_image')!.textContent = 'Insert second floppy image'
3002
+ $('get_fdb_image')!.style.display = 'none'
3003
+ } else {
3004
+ const files = await pick_file(false)
3005
+ insert_fdb(files)
3006
+ }
3007
+ ;($('change_fdb_image') as HTMLElement).blur()
3008
+ }
3009
+
3010
+ $('change_cdrom_image')!.textContent = settings.cdrom
3011
+ ? 'Eject CD image'
3012
+ : 'Insert CD image'
3013
+ $('change_cdrom_image')!.ondragover = function (e) {
3014
+ e.preventDefault()
3015
+ }
3016
+ async function insert_cdrom(files: FileList | File[]): Promise<void> {
3017
+ let buffer
3018
+
3019
+ if (
3020
+ files.length === 1 &&
3021
+ /\.(iso(9660|img)?|cdr)$/i.test(files[0].name)
3022
+ ) {
3023
+ buffer = files[0]
3024
+ } else if (files.length) {
3025
+ const files2 = []
3026
+ for (const file of files) {
3027
+ files2.push({
3028
+ name: file.name,
3029
+ contents: new Uint8Array(
3030
+ (await read_file(file)) as ArrayBuffer,
3031
+ ),
3032
+ })
3033
+ }
3034
+ buffer = iso9660.generate(files2).buffer
3035
+ }
3036
+
3037
+ if (buffer) {
3038
+ await emulator.set_cdrom({ buffer })
3039
+ $('change_cdrom_image')!.textContent = 'Eject CD image'
3040
+ $('get_cdrom_image')!.style.display = 'block'
3041
+ }
3042
+ }
3043
+ $('change_cdrom_image')!.ondrop = function (e) {
3044
+ e.preventDefault()
3045
+ if (emulator.v86.cpu.devices.cdrom.has_disk()) {
3046
+ emulator.eject_cdrom()
3047
+ }
3048
+ insert_cdrom(e.dataTransfer!.files)
3049
+ }
3050
+ $('change_cdrom_image')!.onclick = async function () {
3051
+ if (emulator.v86.cpu.devices.cdrom.has_disk()) {
3052
+ emulator.eject_cdrom()
3053
+ $('change_cdrom_image')!.textContent = 'Insert CD image'
3054
+ $('get_cdrom_image')!.style.display = 'none'
3055
+ } else {
3056
+ const files = await pick_file(true)
3057
+ insert_cdrom(files)
3058
+ }
3059
+ ;($('change_cdrom_image') as HTMLElement).blur()
3060
+ }
3061
+
3062
+ $('memory_dump')!.onclick = function () {
3063
+ const mem8 = emulator.v86.cpu.mem8
3064
+ dump_file(
3065
+ new Uint8Array(mem8.buffer, mem8.byteOffset, mem8.length),
3066
+ 'v86memory.bin',
3067
+ )
3068
+ ;($('memory_dump') as HTMLElement).blur()
3069
+ }
3070
+
3071
+ //$("memory_dump_dmp").onclick = function()
3072
+ //{
3073
+ // var memory = emulator.v86.cpu.mem8;
3074
+ // var memory_size = memory.length;
3075
+ // var page_size = 4096;
3076
+ // var header = new Uint8Array(4096);
3077
+ // var header32 = new Int32Array(header.buffer);
3078
+
3079
+ // header32[0] = 0x45474150; // 'PAGE'
3080
+ // header32[1] = 0x504D5544; // 'DUMP'
3081
+
3082
+ // header32[0x10 >> 2] = emulator.v86.cpu.cr[3]; // DirectoryTableBase
3083
+ // header32[0x24 >> 2] = 1; // NumberProcessors
3084
+ // header32[0xf88 >> 2] = 1; // DumpType: full dump
3085
+ // header32[0xfa0 >> 2] = header.length + memory_size; // RequiredDumpSpace
3086
+
3087
+ // header32[0x064 + 0 >> 2] = 1; // NumberOfRuns
3088
+ // header32[0x064 + 4 >> 2] = memory_size / page_size; // NumberOfPages
3089
+ // header32[0x064 + 8 >> 2] = 0; // BasePage
3090
+ // header32[0x064 + 12 >> 2] = memory_size / page_size; // PageCount
3091
+
3092
+ // dump_file([header, memory], "v86memory.dmp");
3093
+
3094
+ // $("memory_dump_dmp").blur();
3095
+ //};
3096
+
3097
+ const capture_traffic_el = $('capture_network_traffic')!
3098
+ capture_traffic_el.onclick = function () {
3099
+ capture_traffic_el.textContent = '0 packets'
3100
+
3101
+ let capture: any[] = []
3102
+
3103
+ function do_capture(direction: string, data: any): void {
3104
+ capture.push({
3105
+ direction,
3106
+ time: performance.now() / 1000,
3107
+ hex_dump: hex_dump(data),
3108
+ })
3109
+ capture_traffic_el.textContent = capture.length + ' packets'
3110
+ }
3111
+
3112
+ emulator.emulator_bus.register(
3113
+ 'net0-receive',
3114
+ do_capture.bind(null, 'I'),
3115
+ )
3116
+ emulator.add_listener('net0-send', do_capture.bind(null, 'O'))
3117
+
3118
+ capture_traffic_el.onclick = function () {
3119
+ const capture_raw = capture
3120
+
3121
+ .map(({ direction, time, hex_dump: hd }: any) => {
3122
+ // https://www.wireshark.org/docs/wsug_html_chunked/ChIOImportSection.html
3123
+ // In wireshark: file -> import from hex -> tick direction indication, timestamp %s.%f
3124
+ return direction + ' ' + time.toFixed(6) + hd + '\n'
3125
+ })
3126
+ .join('')
3127
+ dump_file(capture_raw, 'traffic.hex')
3128
+ capture = []
3129
+ capture_traffic_el.textContent = '0 packets'
3130
+ }
3131
+ }
3132
+
3133
+ $('save_state')!.onclick = async function () {
3134
+ const result = await emulator.save_state()
3135
+ dump_file(result, 'v86state.bin')
3136
+ ;($('save_state') as HTMLElement).blur()
3137
+ }
3138
+
3139
+ $('load_state')!.onclick = async function () {
3140
+ ;($('load_state') as HTMLElement).blur()
3141
+
3142
+ const files = await pick_file(false)
3143
+ const file = files[0]
3144
+
3145
+ if (!file) {
3146
+ return
3147
+ }
3148
+
3149
+ const was_running = emulator.is_running()
3150
+
3151
+ if (was_running) {
3152
+ await emulator.stop()
3153
+ }
3154
+
3155
+ const filereader = new FileReader()
3156
+ filereader.onload = async function (e) {
3157
+ try {
3158
+ await emulator.restore_state(e.target!.result as ArrayBuffer)
3159
+ } catch (err) {
3160
+ alert(
3161
+ 'Something bad happened while restoring the state:\n' +
3162
+ err +
3163
+ '\n\n' +
3164
+ 'Note that the current configuration must be the same as the original',
3165
+ )
3166
+ throw err
3167
+ }
3168
+
3169
+ if (was_running) {
3170
+ emulator.run()
3171
+ }
3172
+ }
3173
+ filereader.readAsArrayBuffer(file)
3174
+ }
3175
+
3176
+ $('ctrlaltdel')!.onclick = function () {
3177
+ emulator.keyboard_send_scancodes([
3178
+ 0x1d, // ctrl
3179
+ 0x38, // alt
3180
+ 0x53, // delete
3181
+
3182
+ // break codes
3183
+ 0x1d | 0x80,
3184
+ 0x38 | 0x80,
3185
+ 0x53 | 0x80,
3186
+ ])
3187
+ ;($('ctrlaltdel') as HTMLElement).blur()
3188
+ }
3189
+
3190
+ $('alttab')!.onclick = function () {
3191
+ emulator.keyboard_send_scancodes([
3192
+ 0x38, // alt
3193
+ 0x0f, // tab
3194
+ ])
3195
+
3196
+ setTimeout(function () {
3197
+ emulator.keyboard_send_scancodes([0x38 | 0x80, 0x0f | 0x80])
3198
+ }, 100)
3199
+ ;($('alttab') as HTMLElement).blur()
3200
+ }
3201
+
3202
+ $('scale')!.onchange = function () {
3203
+ const n = parseFloat(($('scale') as HTMLInputElement).value)
3204
+
3205
+ if (n || n > 0) {
3206
+ emulator.screen_set_scale(n, n)
3207
+ }
3208
+ }
3209
+
3210
+ $('fullscreen')!.onclick = function () {
3211
+ emulator.screen_go_fullscreen()
3212
+ }
3213
+
3214
+ $('screen_container')!.onclick = function (e) {
3215
+ if (
3216
+ emulator.is_running() &&
3217
+ emulator.speaker_adapter?.audio_context?.state === 'suspended'
3218
+ ) {
3219
+ emulator.speaker_adapter.audio_context.resume()
3220
+ }
3221
+
3222
+ if (mouse_is_enabled && os_uses_mouse) {
3223
+ emulator.lock_mouse()
3224
+ }
3225
+
3226
+ // allow text selection
3227
+ if (window.getSelection()!.isCollapsed) {
3228
+ const phone_keyboard = document.getElementsByClassName(
3229
+ 'phone_keyboard',
3230
+ )[0] as HTMLInputElement
3231
+
3232
+ phone_keyboard.style.top = window.scrollY + e.clientY + 20 + 'px'
3233
+ phone_keyboard.style.left = window.scrollX + e.clientX + 'px'
3234
+
3235
+ // clean after previous input
3236
+ phone_keyboard.value = ''
3237
+ phone_keyboard.focus()
3238
+ }
3239
+ }
3240
+
3241
+ const phone_keyboard = document.getElementsByClassName(
3242
+ 'phone_keyboard',
3243
+ )[0] as HTMLElement
3244
+
3245
+ phone_keyboard.setAttribute('autocorrect', 'off')
3246
+ phone_keyboard.setAttribute('autocapitalize', 'off')
3247
+ phone_keyboard.setAttribute('spellcheck', 'false')
3248
+ phone_keyboard.tabIndex = 0
3249
+
3250
+ $('take_screenshot')!.onclick = function () {
3251
+ const image = emulator.screen_make_screenshot()
3252
+ try {
3253
+ const w = window.open('')!
3254
+ w.document.write(image!.outerHTML)
3255
+ } catch {
3256
+ // intentionally empty
3257
+ }
3258
+ ;($('take_screenshot') as HTMLElement).blur()
3259
+ }
3260
+
3261
+ if (emulator.speaker_adapter) {
3262
+ let is_muted = false
3263
+
3264
+ $('mute')!.onclick = function () {
3265
+ if (is_muted) {
3266
+ emulator.speaker_adapter.mixer.set_volume(1, undefined)
3267
+ is_muted = false
3268
+ $('mute')!.textContent = 'Mute'
3269
+ } else {
3270
+ emulator.speaker_adapter.mixer.set_volume(0, undefined)
3271
+ is_muted = true
3272
+ $('mute')!.textContent = 'Unmute'
3273
+ }
3274
+
3275
+ ;($('mute') as HTMLElement).blur()
3276
+ }
3277
+ } else {
3278
+ $('mute')!.remove()
3279
+ }
3280
+
3281
+ window.addEventListener('keydown', ctrl_w_rescue, false)
3282
+ window.addEventListener('keyup', ctrl_w_rescue, false)
3283
+ window.addEventListener('blur', ctrl_w_rescue, false)
3284
+
3285
+ function ctrl_w_rescue(e: Event): void {
3286
+ if ((e as KeyboardEvent).ctrlKey) {
3287
+ window.onbeforeunload = function () {
3288
+ window.onbeforeunload = null
3289
+ return 'CTRL-W cannot be sent to the emulator.'
3290
+ }
3291
+ } else {
3292
+ window.onbeforeunload = null
3293
+ }
3294
+ }
3295
+
3296
+ const script = document.createElement('script')
3297
+ script.src = 'build/xterm.js'
3298
+ script.async = true
3299
+ script.onload = function () {
3300
+ emulator.set_serial_container_xtermjs($('terminal')!)
3301
+ emulator.serial_adapter.term.write(
3302
+ 'This is the serial console. Whatever you type or paste here will be sent to COM1',
3303
+ )
3304
+ }
3305
+ document.body.appendChild(script)
3306
+ }
3307
+
3308
+ function init_filesystem_panel(emulator: V86): void {
3309
+ $('filesystem_panel')!.style.display = 'block'
3310
+
3311
+ const fs_send_el = $('filesystem_send_file') as HTMLInputElement
3312
+ fs_send_el.onchange = function () {
3313
+ Array.prototype.forEach.call(fs_send_el.files, function (file: File) {
3314
+ const loader = new SyncFileBuffer(file)
3315
+ loader.onload = function () {
3316
+ loader.get_buffer(async function (buffer: any) {
3317
+ await emulator.create_file(
3318
+ '/' + file.name,
3319
+ new Uint8Array(buffer),
3320
+ )
3321
+ })
3322
+ }
3323
+ loader.load()
3324
+ })
3325
+
3326
+ fs_send_el.value = ''
3327
+ fs_send_el.blur()
3328
+ }
3329
+
3330
+ const fs_get_el = $('filesystem_get_file') as HTMLInputElement
3331
+ fs_get_el.onkeypress = async function (e: KeyboardEvent) {
3332
+ if (e.which !== 13) {
3333
+ return
3334
+ }
3335
+
3336
+ fs_get_el.disabled = true
3337
+
3338
+ let result
3339
+ try {
3340
+ result = await emulator.read_file(fs_get_el.value)
3341
+ } catch (err) {
3342
+ console.log(err)
3343
+ }
3344
+
3345
+ fs_get_el.disabled = false
3346
+
3347
+ if (result) {
3348
+ let filename: string | string[] = fs_get_el.value
3349
+ .replace(/\/$/, '')
3350
+ .split('/')
3351
+ filename = filename[filename.length - 1] || 'root'
3352
+
3353
+ dump_file(result as any, filename)
3354
+ fs_get_el.value = ''
3355
+ } else {
3356
+ alert("Can't read file")
3357
+ }
3358
+ }
3359
+ }
3360
+
3361
+ function debug_start(emulator: V86): void {
3362
+ if (!emulator.v86) {
3363
+ return
3364
+ }
3365
+
3366
+ // called as soon as soon as emulation is started, in debug mode
3367
+ const cpu = emulator.v86.cpu
3368
+
3369
+ $('dump_gdt')!.onclick = cpu.dump_gdt_ldt.bind(cpu)
3370
+ $('dump_idt')!.onclick = cpu.dump_idt.bind(cpu)
3371
+ $('dump_regs')!.onclick = () => {
3372
+ cpu.dump_regs_short()
3373
+ cpu.dump_state()
3374
+ }
3375
+ $('dump_pt')!.onclick = cpu.dump_page_structures.bind(cpu)
3376
+
3377
+ $('dump_log')!.onclick = function () {
3378
+ dump_file(log_data.join(''), 'v86.log')
3379
+ }
3380
+
3381
+ $('debug_panel')!.style.display = 'block'
3382
+ setInterval(function () {
3383
+ $('debug_panel')!.textContent =
3384
+ cpu.get_regs_short().join('\n') + '\n' + cpu.debug_get_state()
3385
+
3386
+ $('dump_log')!.textContent =
3387
+ 'Dump log' +
3388
+ (log_data.length ? ' (' + log_data.length + ' lines)' : '')
3389
+ }, 1000)
3390
+
3391
+ // helps debugging
3392
+ ;(window as any).cpu = cpu
3393
+ ;(window as any).h = h
3394
+ ;(window as any).dump_file = dump_file
3395
+ }
3396
+
3397
+ function onpopstate(_e: PopStateEvent): void {
3398
+ location.reload()
3399
+ }
3400
+
3401
+ function push_state(params: Map<string, string>): void {
3402
+ if (window.history.pushState) {
3403
+ const search =
3404
+ '?' +
3405
+ Array.from(params.entries())
3406
+ .map(
3407
+ ([key, value]) =>
3408
+ key +
3409
+ '=' +
3410
+ value.replace(/[?&=#+]/g, encodeURIComponent),
3411
+ )
3412
+ .join('&')
3413
+ window.history.pushState({ search }, '', search)
3414
+ }
3415
+ }