@aptre/v86 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (111) hide show
  1. package/LICENSE +22 -0
  2. package/LICENSE.MIT +22 -0
  3. package/Readme.md +237 -0
  4. package/dist/v86.browser.js +26666 -0
  5. package/dist/v86.browser.js.map +7 -0
  6. package/dist/v86.js +26632 -0
  7. package/dist/v86.js.map +7 -0
  8. package/gen/generate_analyzer.ts +512 -0
  9. package/gen/generate_interpreter.ts +522 -0
  10. package/gen/generate_jit.ts +624 -0
  11. package/gen/rust_ast.ts +107 -0
  12. package/gen/util.ts +35 -0
  13. package/gen/x86_table.ts +1836 -0
  14. package/lib/9p.ts +1547 -0
  15. package/lib/filesystem.ts +1879 -0
  16. package/lib/marshall.ts +168 -0
  17. package/lib/softfloat/softfloat.c +32501 -0
  18. package/lib/zstd/zstddeclib.c +13520 -0
  19. package/package.json +75 -0
  20. package/src/acpi.ts +267 -0
  21. package/src/browser/dummy_screen.ts +106 -0
  22. package/src/browser/fake_network.ts +1771 -0
  23. package/src/browser/fetch_network.ts +361 -0
  24. package/src/browser/filestorage.ts +124 -0
  25. package/src/browser/inbrowser_network.ts +57 -0
  26. package/src/browser/keyboard.ts +564 -0
  27. package/src/browser/main.ts +3415 -0
  28. package/src/browser/mouse.ts +255 -0
  29. package/src/browser/network.ts +142 -0
  30. package/src/browser/print_stats.ts +336 -0
  31. package/src/browser/screen.ts +978 -0
  32. package/src/browser/serial.ts +316 -0
  33. package/src/browser/speaker.ts +1223 -0
  34. package/src/browser/starter.ts +1688 -0
  35. package/src/browser/wisp_network.ts +332 -0
  36. package/src/browser/worker_bus.ts +64 -0
  37. package/src/buffer.ts +652 -0
  38. package/src/bus.ts +78 -0
  39. package/src/const.ts +128 -0
  40. package/src/cpu.ts +2891 -0
  41. package/src/dma.ts +474 -0
  42. package/src/elf.ts +251 -0
  43. package/src/floppy.ts +1778 -0
  44. package/src/ide.ts +3455 -0
  45. package/src/io.ts +504 -0
  46. package/src/iso9660.ts +317 -0
  47. package/src/kernel.ts +250 -0
  48. package/src/lib.ts +645 -0
  49. package/src/log.ts +149 -0
  50. package/src/main.ts +199 -0
  51. package/src/ne2k.ts +1589 -0
  52. package/src/pci.ts +815 -0
  53. package/src/pit.ts +406 -0
  54. package/src/ps2.ts +820 -0
  55. package/src/rtc.ts +537 -0
  56. package/src/rust/analysis.rs +101 -0
  57. package/src/rust/codegen.rs +2660 -0
  58. package/src/rust/config.rs +3 -0
  59. package/src/rust/control_flow.rs +425 -0
  60. package/src/rust/cpu/apic.rs +658 -0
  61. package/src/rust/cpu/arith.rs +1207 -0
  62. package/src/rust/cpu/call_indirect.rs +2 -0
  63. package/src/rust/cpu/cpu.rs +4501 -0
  64. package/src/rust/cpu/fpu.rs +923 -0
  65. package/src/rust/cpu/global_pointers.rs +112 -0
  66. package/src/rust/cpu/instructions.rs +2486 -0
  67. package/src/rust/cpu/instructions_0f.rs +5261 -0
  68. package/src/rust/cpu/ioapic.rs +316 -0
  69. package/src/rust/cpu/memory.rs +351 -0
  70. package/src/rust/cpu/misc_instr.rs +613 -0
  71. package/src/rust/cpu/mod.rs +16 -0
  72. package/src/rust/cpu/modrm.rs +133 -0
  73. package/src/rust/cpu/pic.rs +402 -0
  74. package/src/rust/cpu/sse_instr.rs +361 -0
  75. package/src/rust/cpu/string.rs +701 -0
  76. package/src/rust/cpu/vga.rs +175 -0
  77. package/src/rust/cpu_context.rs +69 -0
  78. package/src/rust/dbg.rs +98 -0
  79. package/src/rust/gen/analyzer.rs +3807 -0
  80. package/src/rust/gen/analyzer0f.rs +3992 -0
  81. package/src/rust/gen/interpreter.rs +4447 -0
  82. package/src/rust/gen/interpreter0f.rs +5404 -0
  83. package/src/rust/gen/jit.rs +5080 -0
  84. package/src/rust/gen/jit0f.rs +5547 -0
  85. package/src/rust/gen/mod.rs +14 -0
  86. package/src/rust/jit.rs +2443 -0
  87. package/src/rust/jit_instructions.rs +7881 -0
  88. package/src/rust/js_api.rs +6 -0
  89. package/src/rust/leb.rs +46 -0
  90. package/src/rust/lib.rs +29 -0
  91. package/src/rust/modrm.rs +330 -0
  92. package/src/rust/opstats.rs +249 -0
  93. package/src/rust/page.rs +15 -0
  94. package/src/rust/paging.rs +25 -0
  95. package/src/rust/prefix.rs +15 -0
  96. package/src/rust/profiler.rs +155 -0
  97. package/src/rust/regs.rs +38 -0
  98. package/src/rust/softfloat.rs +286 -0
  99. package/src/rust/state_flags.rs +27 -0
  100. package/src/rust/wasmgen/mod.rs +2 -0
  101. package/src/rust/wasmgen/wasm_builder.rs +1047 -0
  102. package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
  103. package/src/rust/zstd.rs +105 -0
  104. package/src/sb16.ts +1928 -0
  105. package/src/state.ts +359 -0
  106. package/src/uart.ts +472 -0
  107. package/src/vga.ts +2791 -0
  108. package/src/virtio.ts +1756 -0
  109. package/src/virtio_balloon.ts +273 -0
  110. package/src/virtio_console.ts +372 -0
  111. package/src/virtio_net.ts +326 -0
package/src/vga.ts ADDED
@@ -0,0 +1,2791 @@
1
+ import { LOG_VGA } from './const.js'
2
+ import { h, round_up_to_next_power_of_2, view } from './lib.js'
3
+ import { dbg_assert, dbg_log } from './log.js'
4
+ import { IO } from './io.js'
5
+ import { BusConnector } from './bus.js'
6
+ import { PCI } from './pci.js'
7
+
8
+ // Always 64k
9
+ const VGA_BANK_SIZE = 64 * 1024
10
+
11
+ const MAX_XRES = 2560
12
+ const MAX_YRES = 1600
13
+ const MAX_BPP = 32
14
+
15
+ //const VGA_LFB_ADDRESS = 0xFE000000; // set by seabios
16
+ const VGA_LFB_ADDRESS = 0xe0000000
17
+
18
+ // Equals the maximum number of pixels for non svga.
19
+ // 8 pixels per byte.
20
+ const VGA_PIXEL_BUFFER_SIZE = 8 * VGA_BANK_SIZE
21
+
22
+ const VGA_MIN_MEMORY_SIZE = 4 * VGA_BANK_SIZE
23
+
24
+ // Avoid wrapping past VGA_LFB_ADDRESS
25
+ const VGA_MAX_MEMORY_SIZE = 256 * 1024 * 1024
26
+
27
+ // @see http://www.osdever.net/FreeVGA/vga/graphreg.htm#06
28
+ const VGA_HOST_MEMORY_SPACE_START = Uint32Array.from([
29
+ 0xa0000, 0xa0000, 0xb0000, 0xb8000,
30
+ ])
31
+
32
+ // @see http://www.osdever.net/FreeVGA/vga/graphreg.htm#06
33
+ const VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([
34
+ 0x20000, // 128K
35
+ 0x10000, // 64K
36
+ 0x8000, // 32K
37
+ 0x8000, // 32K
38
+ ])
39
+
40
+ // Minimal interface for the CPU fields VGA uses.
41
+ interface VGACpu {
42
+ io: IO
43
+ wasm_memory: WebAssembly.Memory
44
+ devices: { pci: PCI }
45
+ read8(addr: number): number
46
+ write8(addr: number, value: number): void
47
+ svga_allocate_memory(size: number): number
48
+ svga_allocate_dest_buffer(size: number): number
49
+ svga_mark_dirty(): void
50
+ svga_fill_pixel_buffer(bpp: number, offset: number): void
51
+ svga_dirty_bitmap_min_offset: Int32Array
52
+ svga_dirty_bitmap_max_offset: Int32Array
53
+ }
54
+
55
+ // Minimal interface for the screen adapter (ScreenAdapter or DummyScreenAdapter).
56
+ interface VGAScreenAdapter {
57
+ FLAG_BLINKING: number
58
+ FLAG_FONT_PAGE_B: number
59
+ set_mode(graphical: boolean): void
60
+ set_size_text(cols: number, rows: number): void
61
+ set_size_graphical(
62
+ width: number,
63
+ height: number,
64
+ virtual_width: number,
65
+ virtual_height: number,
66
+ ): void
67
+ put_char(
68
+ row: number,
69
+ col: number,
70
+ chr: number,
71
+ flags: number,
72
+ bg_color: number,
73
+ fg_color: number,
74
+ ): void
75
+ update_cursor(row: number, col: number): void
76
+ update_cursor_scanline(start: number, end: number, visible: boolean): void
77
+ update_buffer(layers: VGALayer[]): void
78
+ clear_screen(): void
79
+ set_font_bitmap(
80
+ height: number,
81
+ width_9px: boolean,
82
+ width_dbl: boolean,
83
+ copy_8th_col: boolean,
84
+ bitmap: Uint8Array,
85
+ bitmap_changed: boolean,
86
+ ): void
87
+ set_font_page(page_a: number, page_b: number): void
88
+ }
89
+
90
+ interface VGALayer {
91
+ image_data: ImageData | null
92
+ screen_x: number
93
+ screen_y: number
94
+ buffer_x: number
95
+ buffer_y: number
96
+ buffer_width: number
97
+ buffer_height: number
98
+ }
99
+
100
+ export class VGAScreen {
101
+ cpu: VGACpu
102
+ bus: BusConnector
103
+ screen: VGAScreenAdapter
104
+ vga_memory_size: number
105
+ cursor_address: number
106
+ cursor_scanline_start: number
107
+ cursor_scanline_end: number
108
+
109
+ // Number of columns in text mode
110
+ max_cols: number
111
+
112
+ // Number of rows in text mode
113
+ max_rows: number
114
+
115
+ // Width in pixels in graphical mode
116
+ screen_width: number
117
+
118
+ // Height in pixels in graphical mode
119
+ screen_height: number
120
+
121
+ // Logical width in pixels of virtual buffer available for panning
122
+ virtual_width: number
123
+
124
+ // Logical height in pixels of virtual buffer available for panning
125
+ virtual_height: number
126
+
127
+ // The rectangular fragments of the image buffer, and their destination
128
+ // locations, to be drawn every screen_fill_buffer during VGA modes.
129
+ layers: VGALayer[]
130
+
131
+ // video memory start address
132
+ start_address: number
133
+
134
+ // Start address - a copy of start_address that only gets updated
135
+ // during VSync, used for panning and page flipping
136
+ start_address_latched: number
137
+
138
+ // Unimplemented CRTC registers go here
139
+ crtc: Uint8Array
140
+
141
+ crtc_mode: number
142
+ horizontal_display_enable_end: number
143
+ horizontal_blank_start: number
144
+ vertical_display_enable_end: number
145
+ vertical_blank_start: number
146
+ underline_location_register: number
147
+ preset_row_scan: number
148
+ offset_register: number
149
+ line_compare: number
150
+ graphical_mode: boolean
151
+
152
+ // VGA palette containing 256 colors for video mode 13, svga 8bpp, etc.
153
+ // Needs to be initialised by the BIOS
154
+ vga256_palette: Int32Array
155
+
156
+ // VGA read latches
157
+ latch_dword: number
158
+ svga_version: number
159
+ svga_width: number
160
+ svga_height: number
161
+ svga_enabled: boolean
162
+ svga_bpp: number
163
+ svga_bank_offset: number
164
+
165
+ // The video buffer offset created by VBE_DISPI_INDEX_Y_OFFSET (in bytes)
166
+ svga_offset: number
167
+ svga_offset_x: number
168
+ svga_offset_y: number
169
+
170
+ pci_space: number[]
171
+ pci_id: number
172
+ pci_bars: { size: number }[]
173
+ pci_rom_size: number
174
+ pci_rom_address: number
175
+ name: string
176
+
177
+ index_crtc: number
178
+ dac_color_index_write: number
179
+ dac_color_index_read: number
180
+ dac_state: number
181
+ dac_mask: number
182
+ dac_map: Uint8Array
183
+
184
+ attribute_controller_index: number
185
+ palette_source: number
186
+ attribute_mode: number
187
+ color_plane_enable: number
188
+ horizontal_panning: number
189
+ color_select: number
190
+
191
+ sequencer_index: number
192
+ plane_write_bm: number
193
+ sequencer_memory_mode: number
194
+ clocking_mode: number
195
+ graphics_index: number
196
+ character_map_select: number
197
+
198
+ plane_read: number
199
+ planar_mode: number
200
+ planar_rotate_reg: number
201
+ planar_bitmap: number
202
+ planar_setreset: number
203
+ planar_setreset_enable: number
204
+ miscellaneous_graphics_register: number
205
+
206
+ color_compare: number
207
+ color_dont_care: number
208
+ max_scan_line: number
209
+ miscellaneous_output_register: number
210
+ port_3DA_value: number
211
+
212
+ font_page_ab_enabled: boolean
213
+
214
+ dispi_index: number
215
+ dispi_enable_value: number
216
+
217
+ svga_memory: Uint8Array
218
+ diff_addr_min: number
219
+ diff_addr_max: number
220
+ diff_plot_min: number
221
+ diff_plot_max: number
222
+
223
+ image_data: ImageData | null
224
+ dest_buffet_offset: number
225
+
226
+ vga_memory: Uint8Array
227
+ plane0: Uint8Array
228
+ plane1: Uint8Array
229
+ plane2: Uint8Array
230
+ plane3: Uint8Array
231
+ pixel_buffer: Uint8Array
232
+
233
+ constructor(
234
+ cpu: VGACpu,
235
+ bus: BusConnector,
236
+ screen: VGAScreenAdapter,
237
+ vga_memory_size: number,
238
+ ) {
239
+ this.cpu = cpu
240
+ this.bus = bus
241
+ this.screen = screen
242
+ this.vga_memory_size = vga_memory_size
243
+
244
+ this.cursor_address = 0
245
+ this.cursor_scanline_start = 0xe
246
+ this.cursor_scanline_end = 0xf
247
+ this.max_cols = 80
248
+ this.max_rows = 25
249
+ this.screen_width = 0
250
+ this.screen_height = 0
251
+ this.virtual_width = 0
252
+ this.virtual_height = 0
253
+ this.layers = []
254
+ this.start_address = 0
255
+ this.start_address_latched = 0
256
+ this.crtc = new Uint8Array(0x19)
257
+ this.crtc_mode = 0
258
+ this.horizontal_display_enable_end = 0
259
+ this.horizontal_blank_start = 0
260
+ this.vertical_display_enable_end = 0
261
+ this.vertical_blank_start = 0
262
+ this.underline_location_register = 0
263
+ this.preset_row_scan = 0
264
+ this.offset_register = 0
265
+ this.line_compare = 0
266
+ this.graphical_mode = false
267
+ this.vga256_palette = new Int32Array(256)
268
+ this.latch_dword = 0
269
+ this.svga_version = 0xb0c5
270
+ this.svga_width = 0
271
+ this.svga_height = 0
272
+ this.svga_enabled = false
273
+ this.svga_bpp = 32
274
+ this.svga_bank_offset = 0
275
+ this.svga_offset = 0
276
+ this.svga_offset_x = 0
277
+ this.svga_offset_y = 0
278
+
279
+ if (
280
+ this.vga_memory_size === undefined ||
281
+ this.vga_memory_size < VGA_MIN_MEMORY_SIZE
282
+ ) {
283
+ this.vga_memory_size = VGA_MIN_MEMORY_SIZE
284
+ } else if (this.vga_memory_size > VGA_MAX_MEMORY_SIZE) {
285
+ this.vga_memory_size = VGA_MAX_MEMORY_SIZE
286
+ } else {
287
+ // required for pci code
288
+ this.vga_memory_size = round_up_to_next_power_of_2(
289
+ this.vga_memory_size,
290
+ )
291
+ }
292
+ dbg_log('effective vga memory size: ' + this.vga_memory_size, LOG_VGA)
293
+
294
+ const pci_revision = 0 // set to 2 for qemu extended registers
295
+
296
+ // Experimental, could probably need some changes
297
+ // 01:00.0 VGA compatible controller: NVIDIA Corporation GT216 [GeForce GT 220] (rev a2)
298
+ this.pci_space = [
299
+ 0x34,
300
+ 0x12,
301
+ 0x11,
302
+ 0x11,
303
+ 0x03,
304
+ 0x01,
305
+ 0x00,
306
+ 0x00,
307
+ pci_revision,
308
+ 0x00,
309
+ 0x00,
310
+ 0x03,
311
+ 0x00,
312
+ 0x00,
313
+ 0x00,
314
+ 0x00,
315
+ 0x08,
316
+ VGA_LFB_ADDRESS >>> 8,
317
+ VGA_LFB_ADDRESS >>> 16,
318
+ VGA_LFB_ADDRESS >>> 24,
319
+ 0x00,
320
+ 0x00,
321
+ 0x00,
322
+ 0x00,
323
+ 0x00,
324
+ 0x00,
325
+ 0xbf,
326
+ 0xfe,
327
+ 0x00,
328
+ 0x00,
329
+ 0x00,
330
+ 0x00,
331
+ 0x00,
332
+ 0x00,
333
+ 0x00,
334
+ 0x00,
335
+ 0x00,
336
+ 0x00,
337
+ 0x00,
338
+ 0x00,
339
+ 0x00,
340
+ 0x00,
341
+ 0x00,
342
+ 0x00,
343
+ 0xf4,
344
+ 0x1a,
345
+ 0x00,
346
+ 0x11,
347
+ 0x00,
348
+ 0x00,
349
+ 0xbe,
350
+ 0xfe,
351
+ 0x00,
352
+ 0x00,
353
+ 0x00,
354
+ 0x00,
355
+ 0x00,
356
+ 0x00,
357
+ 0x00,
358
+ 0x00,
359
+ 0x00,
360
+ 0x00,
361
+ 0x00,
362
+ 0x00,
363
+ ]
364
+ this.pci_id = 0x12 << 3
365
+ this.pci_bars = [
366
+ {
367
+ size: this.vga_memory_size,
368
+ },
369
+ ]
370
+
371
+ // TODO: Should be matched with vga bios size and mapping address
372
+ // Seabios config for this device:
373
+ // CONFIG_VGA_PCI=y
374
+ // CONFIG_OVERRIDE_PCI_ID=y
375
+ // CONFIG_VGA_VID=0x10de
376
+ // CONFIG_VGA_DID=0x0a20
377
+
378
+ this.pci_rom_size = 0x10000
379
+ this.pci_rom_address = 0xfeb00000
380
+
381
+ this.name = 'vga'
382
+
383
+ this.index_crtc = 0
384
+
385
+ // index for setting colors through port 3C9h
386
+ this.dac_color_index_write = 0
387
+ this.dac_color_index_read = 0
388
+ this.dac_state = 0
389
+
390
+ this.dac_mask = 0xff
391
+
392
+ this.dac_map = new Uint8Array(0x10)
393
+
394
+ this.attribute_controller_index = -1
395
+ this.palette_source = 0x20
396
+ this.attribute_mode = 0
397
+ this.color_plane_enable = 0
398
+ this.horizontal_panning = 0
399
+ this.color_select = 0
400
+
401
+ this.sequencer_index = -1
402
+
403
+ // bitmap of planes 0-3
404
+ this.plane_write_bm = 0xf
405
+ this.sequencer_memory_mode = 0
406
+ this.clocking_mode = 0
407
+ this.graphics_index = -1
408
+ this.character_map_select = 0
409
+
410
+ this.plane_read = 0 // value 0-3, which plane to read
411
+ this.planar_mode = 0
412
+ this.planar_rotate_reg = 0
413
+ this.planar_bitmap = 0xff
414
+ this.planar_setreset = 0
415
+ this.planar_setreset_enable = 0
416
+ this.miscellaneous_graphics_register = 0
417
+
418
+ this.color_compare = 0
419
+ this.color_dont_care = 0
420
+
421
+ this.max_scan_line = 0
422
+
423
+ this.miscellaneous_output_register = 0xff
424
+ this.port_3DA_value = 0xff
425
+
426
+ this.font_page_ab_enabled = false
427
+
428
+ this.dest_buffet_offset = 0
429
+
430
+ const io = cpu.io
431
+
432
+ io.register_write(0x3c0, this, this.port3C0_write)
433
+ io.register_read(0x3c0, this, this.port3C0_read, this.port3C0_read16)
434
+
435
+ io.register_read(0x3c1, this, this.port3C1_read)
436
+ io.register_write(0x3c2, this, this.port3C2_write)
437
+
438
+ io.register_write_consecutive(
439
+ 0x3c4,
440
+ this,
441
+ this.port3C4_write,
442
+ this.port3C5_write,
443
+ )
444
+
445
+ io.register_read(0x3c4, this, this.port3C4_read)
446
+ io.register_read(0x3c5, this, this.port3C5_read)
447
+
448
+ io.register_write_consecutive(
449
+ 0x3ce,
450
+ this,
451
+ this.port3CE_write,
452
+ this.port3CF_write,
453
+ )
454
+
455
+ io.register_read(0x3ce, this, this.port3CE_read)
456
+ io.register_read(0x3cf, this, this.port3CF_read)
457
+
458
+ io.register_read(0x3c6, this, this.port3C6_read)
459
+ io.register_write(0x3c6, this, this.port3C6_write)
460
+ io.register_write(0x3c7, this, this.port3C7_write)
461
+ io.register_read(0x3c7, this, this.port3C7_read)
462
+ io.register_write(0x3c8, this, this.port3C8_write)
463
+ io.register_read(0x3c8, this, this.port3C8_read)
464
+ io.register_write(0x3c9, this, this.port3C9_write)
465
+ io.register_read(0x3c9, this, this.port3C9_read)
466
+
467
+ io.register_read(0x3cc, this, this.port3CC_read)
468
+
469
+ io.register_write(0x3d4, this, this.port3D4_write, this.port3D4_write16)
470
+ io.register_write(0x3d5, this, this.port3D5_write, this.port3D5_write16)
471
+
472
+ io.register_read(0x3d4, this, this.port3D4_read)
473
+ io.register_read(0x3d5, this, this.port3D5_read, this.port3D5_read16)
474
+
475
+ // use same handlers for monochrome text-mode's alternate port addresses 0x3B4/0x3B5 as for the regular addresses (0x3D4/0x3D5)
476
+ io.register_write(0x3b4, this, this.port3D4_write, this.port3D4_write16)
477
+ io.register_write(0x3b5, this, this.port3D5_write, this.port3D5_write16)
478
+
479
+ io.register_read(0x3b4, this, this.port3D4_read)
480
+ io.register_read(0x3b5, this, this.port3D5_read, this.port3D5_read16)
481
+
482
+ io.register_read(0x3ca, this, function () {
483
+ dbg_log('3CA read', LOG_VGA)
484
+ return 0
485
+ })
486
+
487
+ // use same handler for monochrome text-mode's alternate port address 0x3BA as for its regular address (0x3DA)
488
+ io.register_read(0x3da, this, this.port3DA_read)
489
+ io.register_read(0x3ba, this, this.port3DA_read)
490
+
491
+ // Bochs VBE Extensions
492
+ // http://wiki.osdev.org/Bochs_VBE_Extensions
493
+ this.dispi_index = -1
494
+ this.dispi_enable_value = 0
495
+
496
+ io.register_write(0x1ce, this, undefined, this.port1CE_write)
497
+
498
+ io.register_write(0x1cf, this, undefined, this.port1CF_write)
499
+ io.register_read(0x1cf, this, undefined, this.port1CF_read)
500
+
501
+ const vga_offset = cpu.svga_allocate_memory(this.vga_memory_size) >>> 0
502
+ this.svga_memory = view(
503
+ Uint8Array,
504
+ cpu.wasm_memory,
505
+ vga_offset,
506
+ this.vga_memory_size,
507
+ )
508
+
509
+ this.diff_addr_min = this.vga_memory_size
510
+ this.diff_addr_max = 0
511
+ this.diff_plot_min = this.vga_memory_size
512
+ this.diff_plot_max = 0
513
+
514
+ this.image_data = null
515
+
516
+ this.vga_memory = new Uint8Array(4 * VGA_BANK_SIZE)
517
+ this.plane0 = new Uint8Array(
518
+ this.vga_memory.buffer,
519
+ 0 * VGA_BANK_SIZE,
520
+ VGA_BANK_SIZE,
521
+ )
522
+ this.plane1 = new Uint8Array(
523
+ this.vga_memory.buffer,
524
+ 1 * VGA_BANK_SIZE,
525
+ VGA_BANK_SIZE,
526
+ )
527
+ this.plane2 = new Uint8Array(
528
+ this.vga_memory.buffer,
529
+ 2 * VGA_BANK_SIZE,
530
+ VGA_BANK_SIZE,
531
+ )
532
+ this.plane3 = new Uint8Array(
533
+ this.vga_memory.buffer,
534
+ 3 * VGA_BANK_SIZE,
535
+ VGA_BANK_SIZE,
536
+ )
537
+ this.pixel_buffer = new Uint8Array(VGA_PIXEL_BUFFER_SIZE)
538
+
539
+ io.mmap_register(
540
+ 0xa0000,
541
+ 0x20000,
542
+ (addr: number) => this.vga_memory_read(addr),
543
+ (addr: number, value: number) => this.vga_memory_write(addr, value),
544
+ )
545
+
546
+ cpu.devices.pci.register_device(this)
547
+ }
548
+
549
+ get_state(): any[] {
550
+ const state: any[] = []
551
+
552
+ state[0] = this.vga_memory_size
553
+ state[1] = this.cursor_address
554
+ state[2] = this.cursor_scanline_start
555
+ state[3] = this.cursor_scanline_end
556
+ state[4] = this.max_cols
557
+ state[5] = this.max_rows
558
+ state[6] = this.vga_memory
559
+ state[7] = this.dac_state
560
+ state[8] = this.start_address
561
+ state[9] = this.graphical_mode
562
+ state[10] = this.vga256_palette
563
+ state[11] = this.latch_dword
564
+ state[12] = this.color_compare
565
+ state[13] = this.color_dont_care
566
+ state[14] = this.miscellaneous_graphics_register
567
+ state[15] = this.svga_width
568
+ state[16] = this.svga_height
569
+ state[17] = this.crtc_mode
570
+ state[18] = this.svga_enabled
571
+ state[19] = this.svga_bpp
572
+ state[20] = this.svga_bank_offset
573
+ state[21] = this.svga_offset
574
+ state[22] = this.index_crtc
575
+ state[23] = this.dac_color_index_write
576
+ state[24] = this.dac_color_index_read
577
+ state[25] = this.dac_map
578
+ state[26] = this.sequencer_index
579
+ state[27] = this.plane_write_bm
580
+ state[28] = this.sequencer_memory_mode
581
+ state[29] = this.graphics_index
582
+ state[30] = this.plane_read
583
+ state[31] = this.planar_mode
584
+ state[32] = this.planar_rotate_reg
585
+ state[33] = this.planar_bitmap
586
+ state[34] = this.max_scan_line
587
+ state[35] = this.miscellaneous_output_register
588
+ state[36] = this.port_3DA_value
589
+ state[37] = this.dispi_index
590
+ state[38] = this.dispi_enable_value
591
+ state[39] = this.svga_memory
592
+ // this.graphical_mode_is_linear
593
+ state[41] = this.attribute_controller_index
594
+ state[42] = this.offset_register
595
+ state[43] = this.planar_setreset
596
+ state[44] = this.planar_setreset_enable
597
+ state[45] = this.start_address_latched
598
+ state[46] = this.crtc
599
+ state[47] = this.horizontal_display_enable_end
600
+ state[48] = this.horizontal_blank_start
601
+ state[49] = this.vertical_display_enable_end
602
+ state[50] = this.vertical_blank_start
603
+ state[51] = this.underline_location_register
604
+ state[52] = this.preset_row_scan
605
+ state[53] = this.offset_register
606
+ state[54] = this.palette_source
607
+ state[55] = this.attribute_mode
608
+ state[56] = this.color_plane_enable
609
+ state[57] = this.horizontal_panning
610
+ state[58] = this.color_select
611
+ state[59] = this.clocking_mode
612
+ state[60] = this.line_compare
613
+ state[61] = this.pixel_buffer
614
+ state[62] = this.dac_mask
615
+ state[63] = this.character_map_select
616
+ state[64] = this.font_page_ab_enabled
617
+
618
+ return state
619
+ }
620
+
621
+ set_state(state: any[]): void {
622
+ this.vga_memory_size = state[0]
623
+ this.cursor_address = state[1]
624
+ this.cursor_scanline_start = state[2]
625
+ this.cursor_scanline_end = state[3]
626
+ this.max_cols = state[4]
627
+ this.max_rows = state[5]
628
+ if (state[6]) {
629
+ this.vga_memory.set(state[6])
630
+ }
631
+ this.dac_state = state[7]
632
+ this.start_address = state[8]
633
+ this.graphical_mode = state[9]
634
+ this.vga256_palette = state[10]
635
+ this.latch_dword = state[11]
636
+ this.color_compare = state[12]
637
+ this.color_dont_care = state[13]
638
+ this.miscellaneous_graphics_register = state[14]
639
+ this.svga_width = state[15]
640
+ this.svga_height = state[16]
641
+ this.crtc_mode = state[17]
642
+ this.svga_enabled = state[18]
643
+ this.svga_bpp = state[19]
644
+ this.svga_bank_offset = state[20]
645
+ this.svga_offset = state[21]
646
+ this.index_crtc = state[22]
647
+ this.dac_color_index_write = state[23]
648
+ this.dac_color_index_read = state[24]
649
+ this.dac_map = state[25]
650
+ this.sequencer_index = state[26]
651
+ this.plane_write_bm = state[27]
652
+ this.sequencer_memory_mode = state[28]
653
+ this.graphics_index = state[29]
654
+ this.plane_read = state[30]
655
+ this.planar_mode = state[31]
656
+ this.planar_rotate_reg = state[32]
657
+ this.planar_bitmap = state[33]
658
+ this.max_scan_line = state[34]
659
+ this.miscellaneous_output_register = state[35]
660
+ this.port_3DA_value = state[36]
661
+ this.dispi_index = state[37]
662
+ this.dispi_enable_value = state[38]
663
+ this.svga_memory.set(state[39])
664
+ // state[40];
665
+ this.attribute_controller_index = state[41]
666
+ this.offset_register = state[42]
667
+ this.planar_setreset = state[43]
668
+ this.planar_setreset_enable = state[44]
669
+ this.start_address_latched = state[45]
670
+ this.crtc.set(state[46])
671
+ this.horizontal_display_enable_end = state[47]
672
+ this.horizontal_blank_start = state[48]
673
+ this.vertical_display_enable_end = state[49]
674
+ this.vertical_blank_start = state[50]
675
+ this.underline_location_register = state[51]
676
+ this.preset_row_scan = state[52]
677
+ this.offset_register = state[53]
678
+ this.palette_source = state[54]
679
+ this.attribute_mode = state[55]
680
+ this.color_plane_enable = state[56]
681
+ this.horizontal_panning = state[57]
682
+ this.color_select = state[58]
683
+ this.clocking_mode = state[59]
684
+ this.line_compare = state[60]
685
+ if (state[61]) {
686
+ this.pixel_buffer.set(state[61])
687
+ }
688
+ this.dac_mask = state[62] === undefined ? 0xff : state[62]
689
+ this.character_map_select = state[63] === undefined ? 0 : state[63]
690
+ this.font_page_ab_enabled = state[64] === undefined ? false : state[64]
691
+
692
+ this.screen.set_mode(this.graphical_mode)
693
+
694
+ if (this.graphical_mode) {
695
+ // Ensure set_size_graphical will update
696
+ this.screen_width = 0
697
+ this.screen_height = 0
698
+
699
+ if (this.svga_enabled) {
700
+ this.set_size_graphical(
701
+ this.svga_width,
702
+ this.svga_height,
703
+ this.svga_width,
704
+ this.svga_height,
705
+ this.svga_bpp,
706
+ )
707
+ this.update_layers()
708
+ } else {
709
+ this.update_vga_size()
710
+ this.update_layers()
711
+ this.complete_replot()
712
+ }
713
+ } else {
714
+ this.set_font_bitmap(true)
715
+ this.set_size_text(this.max_cols, this.max_rows)
716
+ this.set_font_page()
717
+ this.update_cursor_scanline()
718
+ this.update_cursor()
719
+ }
720
+ this.complete_redraw()
721
+ }
722
+
723
+ vga_memory_read(addr: number): number {
724
+ if (this.svga_enabled) {
725
+ // vbe banked mode (accessing svga memory through the regular vga memory range)
726
+ return this.cpu.read8(
727
+ (((addr - 0xa0000) | this.svga_bank_offset) + VGA_LFB_ADDRESS) |
728
+ 0,
729
+ )
730
+ }
731
+
732
+ const memory_space_select =
733
+ (this.miscellaneous_graphics_register >> 2) & 0x3
734
+ addr -= VGA_HOST_MEMORY_SPACE_START[memory_space_select]
735
+
736
+ // VGA chip only decodes addresses within the selected memory space.
737
+ if (
738
+ addr < 0 ||
739
+ addr >= VGA_HOST_MEMORY_SPACE_SIZE[memory_space_select]
740
+ ) {
741
+ dbg_log(
742
+ 'vga read outside memory space: addr:' + h(addr >>> 0),
743
+ LOG_VGA,
744
+ )
745
+ return 0
746
+ }
747
+
748
+ this.latch_dword = this.plane0[addr]
749
+ this.latch_dword |= this.plane1[addr] << 8
750
+ this.latch_dword |= this.plane2[addr] << 16
751
+ this.latch_dword |= this.plane3[addr] << 24
752
+
753
+ if (this.planar_mode & 0x08) {
754
+ // read mode 1
755
+ let reading = 0xff
756
+
757
+ if (this.color_dont_care & 0x1) {
758
+ reading &=
759
+ this.plane0[addr] ^
760
+ ~(this.color_compare & 0x1 ? 0xff : 0x00)
761
+ }
762
+ if (this.color_dont_care & 0x2) {
763
+ reading &=
764
+ this.plane1[addr] ^
765
+ ~(this.color_compare & 0x2 ? 0xff : 0x00)
766
+ }
767
+ if (this.color_dont_care & 0x4) {
768
+ reading &=
769
+ this.plane2[addr] ^
770
+ ~(this.color_compare & 0x4 ? 0xff : 0x00)
771
+ }
772
+ if (this.color_dont_care & 0x8) {
773
+ reading &=
774
+ this.plane3[addr] ^
775
+ ~(this.color_compare & 0x8 ? 0xff : 0x00)
776
+ }
777
+
778
+ return reading
779
+ } else {
780
+ // read mode 0
781
+
782
+ let plane = this.plane_read
783
+ if (!this.graphical_mode) {
784
+ // We store all text data linearly and font data in plane 2.
785
+ // TODO: works well for planes 0 and 2, but what about plane 1?
786
+ plane &= 0x3
787
+ } else if (this.sequencer_memory_mode & 0x8) {
788
+ // Chain 4
789
+ plane = addr & 0x3
790
+ addr &= ~0x3
791
+ } else if (this.planar_mode & 0x10) {
792
+ // Odd/Even host read
793
+ plane = addr & 0x1
794
+ addr &= ~0x1
795
+ }
796
+ return this.vga_memory[(plane << 16) | addr]
797
+ }
798
+ }
799
+
800
+ vga_memory_write(addr: number, value: number): void {
801
+ if (this.svga_enabled) {
802
+ // vbe banked mode (accessing svga memory through the regular vga memory range)
803
+ this.cpu.write8(
804
+ (((addr - 0xa0000) | this.svga_bank_offset) + VGA_LFB_ADDRESS) |
805
+ 0,
806
+ value,
807
+ )
808
+ return
809
+ }
810
+
811
+ const memory_space_select =
812
+ (this.miscellaneous_graphics_register >> 2) & 0x3
813
+ addr -= VGA_HOST_MEMORY_SPACE_START[memory_space_select]
814
+
815
+ if (
816
+ addr < 0 ||
817
+ addr >= VGA_HOST_MEMORY_SPACE_SIZE[memory_space_select]
818
+ ) {
819
+ dbg_log(
820
+ 'vga write outside memory space: addr:' +
821
+ h(addr >>> 0) +
822
+ ', value:' +
823
+ h(value),
824
+ LOG_VGA,
825
+ )
826
+ return
827
+ }
828
+
829
+ if (this.graphical_mode) {
830
+ this.vga_memory_write_graphical(addr, value)
831
+ } else if (!(this.plane_write_bm & 0x3)) {
832
+ if (this.plane_write_bm & 0x4) {
833
+ // write to plane 2 (font-bitmap)
834
+ this.plane2[addr] = value
835
+ }
836
+ } else {
837
+ this.vga_memory_write_text_mode(addr, value)
838
+ }
839
+ }
840
+
841
+ vga_memory_write_graphical(addr: number, value: number): void {
842
+ let plane_dword: number = 0
843
+ const write_mode = this.planar_mode & 3
844
+ let bitmask = this.apply_feed(this.planar_bitmap)
845
+ const setreset_dword = this.apply_expand(this.planar_setreset)
846
+ const setreset_enable_dword = this.apply_expand(
847
+ this.planar_setreset_enable,
848
+ )
849
+
850
+ // Write modes - see http://www.osdever.net/FreeVGA/vga/graphreg.htm#05
851
+ switch (write_mode) {
852
+ case 0:
853
+ value = this.apply_rotate(value)
854
+ plane_dword = this.apply_feed(value)
855
+ plane_dword = this.apply_setreset(
856
+ plane_dword,
857
+ setreset_enable_dword,
858
+ )
859
+ plane_dword = this.apply_logical(plane_dword, this.latch_dword)
860
+ plane_dword = this.apply_bitmask(plane_dword, bitmask)
861
+ break
862
+ case 1:
863
+ plane_dword = this.latch_dword
864
+ break
865
+ case 2:
866
+ plane_dword = this.apply_expand(value)
867
+ plane_dword = this.apply_logical(plane_dword, this.latch_dword)
868
+ plane_dword = this.apply_bitmask(plane_dword, bitmask)
869
+ break
870
+ case 3:
871
+ value = this.apply_rotate(value)
872
+ bitmask &= this.apply_feed(value)
873
+ plane_dword = setreset_dword
874
+ plane_dword = this.apply_bitmask(plane_dword, bitmask)
875
+ break
876
+ }
877
+
878
+ let plane_select = 0xf
879
+
880
+ switch (this.sequencer_memory_mode & 0xc) {
881
+ // Odd/Even (aka chain 2)
882
+ case 0x0:
883
+ plane_select = 0x5 << (addr & 0x1)
884
+ addr &= ~0x1
885
+ break
886
+
887
+ // Chain 4
888
+ // Note: FreeVGA may have mistakenly stated that this bit field is
889
+ // for system read only, yet the IBM Open Source Graphics Programmer's
890
+ // Reference Manual explicitly states "both read and write".
891
+ case 0x8:
892
+ case 0xc:
893
+ plane_select = 1 << (addr & 0x3)
894
+ addr &= ~0x3
895
+ break
896
+ }
897
+
898
+ // Plane masks take precedence
899
+ // See: http://www.osdever.net/FreeVGA/vga/seqreg.htm#02
900
+ plane_select &= this.plane_write_bm
901
+
902
+ if (plane_select & 0x1) this.plane0[addr] = (plane_dword >> 0) & 0xff
903
+ if (plane_select & 0x2) this.plane1[addr] = (plane_dword >> 8) & 0xff
904
+ if (plane_select & 0x4) this.plane2[addr] = (plane_dword >> 16) & 0xff
905
+ if (plane_select & 0x8) this.plane3[addr] = (plane_dword >> 24) & 0xff
906
+
907
+ const pixel_addr = this.vga_addr_to_pixel(addr)
908
+ this.partial_replot(pixel_addr, pixel_addr + 7)
909
+ }
910
+
911
+ // Copies data_byte into the four planes, with each plane
912
+ // represented by an 8-bit field inside the dword.
913
+ apply_feed(data_byte: number): number {
914
+ let dword = data_byte
915
+ dword |= data_byte << 8
916
+ dword |= data_byte << 16
917
+ dword |= data_byte << 24
918
+ return dword
919
+ }
920
+
921
+ // Expands bits 0 to 3 to ocupy bits 0 to 31. Each
922
+ // bit is expanded to 0xFF if set or 0x00 if clear.
923
+ apply_expand(data_byte: number): number {
924
+ let dword = data_byte & 0x1 ? 0xff : 0x00
925
+ dword |= (data_byte & 0x2 ? 0xff : 0x00) << 8
926
+ dword |= (data_byte & 0x4 ? 0xff : 0x00) << 16
927
+ dword |= (data_byte & 0x8 ? 0xff : 0x00) << 24
928
+ return dword
929
+ }
930
+
931
+ // Planar Write - Barrel Shifter
932
+ // @see http://www.phatcode.net/res/224/files/html/ch25/25-01.html#Heading3
933
+ // @see http://www.osdever.net/FreeVGA/vga/graphreg.htm#03
934
+ apply_rotate(data_byte: number): number {
935
+ const wrapped = data_byte | (data_byte << 8)
936
+ const count = this.planar_rotate_reg & 0x7
937
+ const shifted = wrapped >>> count
938
+ return shifted & 0xff
939
+ }
940
+
941
+ // Planar Write - Set / Reset Circuitry
942
+ // @see http://www.phatcode.net/res/224/files/html/ch25/25-03.html#Heading5
943
+ // @see http://www.osdever.net/FreeVGA/vga/graphreg.htm#00
944
+ apply_setreset(data_dword: number, enable_dword: number): number {
945
+ const setreset_dword = this.apply_expand(this.planar_setreset)
946
+ data_dword |= enable_dword & setreset_dword
947
+ data_dword &= ~enable_dword | setreset_dword
948
+ return data_dword
949
+ }
950
+
951
+ // Planar Write - ALU Unit
952
+ // @see http://www.phatcode.net/res/224/files/html/ch24/24-01.html#Heading3
953
+ // @see http://www.osdever.net/FreeVGA/vga/graphreg.htm#03
954
+ apply_logical(data_dword: number, latch_dword: number): number {
955
+ switch (this.planar_rotate_reg & 0x18) {
956
+ case 0x08:
957
+ return data_dword & latch_dword
958
+ case 0x10:
959
+ return data_dword | latch_dword
960
+ case 0x18:
961
+ return data_dword ^ latch_dword
962
+ }
963
+ return data_dword
964
+ }
965
+
966
+ // Planar Write - Bitmask Unit
967
+ // @see http://www.phatcode.net/res/224/files/html/ch25/25-01.html#Heading2
968
+ // @see http://www.osdever.net/FreeVGA/vga/graphreg.htm#08
969
+ apply_bitmask(data_dword: number, bitmask_dword: number): number {
970
+ let plane_dword = bitmask_dword & data_dword
971
+ plane_dword |= ~bitmask_dword & this.latch_dword
972
+ return plane_dword
973
+ }
974
+
975
+ text_mode_redraw(): void {
976
+ const split_screen_row = this.scan_line_to_screen_row(this.line_compare)
977
+ const row_offset = Math.max(
978
+ 0,
979
+ (this.offset_register * 2 - this.max_cols) * 2,
980
+ )
981
+ const blink_enabled = this.attribute_mode & (1 << 3)
982
+ const fg_color_mask = this.font_page_ab_enabled ? 7 : 0xf
983
+ const bg_color_mask = blink_enabled ? 7 : 0xf
984
+ const FLAG_BLINKING = this.screen.FLAG_BLINKING
985
+ const FLAG_FONT_PAGE_B = this.screen.FLAG_FONT_PAGE_B
986
+
987
+ let addr = this.start_address << 1
988
+
989
+ for (let row = 0; row < this.max_rows; row++) {
990
+ if (row === split_screen_row) {
991
+ addr = 0
992
+ }
993
+
994
+ for (let col = 0; col < this.max_cols; col++) {
995
+ const chr = this.vga_memory[addr]
996
+ const color = this.vga_memory[addr | 1]
997
+ const blinking = blink_enabled && color & (1 << 7)
998
+ const font_page_b =
999
+ this.font_page_ab_enabled && !(color & (1 << 3))
1000
+ const flags =
1001
+ (blinking ? FLAG_BLINKING : 0) |
1002
+ (font_page_b ? FLAG_FONT_PAGE_B : 0)
1003
+
1004
+ this.bus.send('screen-put-char', [row, col, chr])
1005
+
1006
+ this.screen.put_char(
1007
+ row,
1008
+ col,
1009
+ chr,
1010
+ flags,
1011
+ this.vga256_palette[
1012
+ this.dac_mask &
1013
+ this.dac_map[(color >> 4) & bg_color_mask]
1014
+ ],
1015
+ this.vga256_palette[
1016
+ this.dac_mask & this.dac_map[color & fg_color_mask]
1017
+ ],
1018
+ )
1019
+
1020
+ addr += 2
1021
+ }
1022
+
1023
+ addr += row_offset
1024
+ }
1025
+ }
1026
+
1027
+ vga_memory_write_text_mode(addr: number, value: number): void {
1028
+ this.vga_memory[addr] = value
1029
+
1030
+ const max_cols = Math.max(this.max_cols, this.offset_register * 2)
1031
+ let row: number
1032
+ let col: number
1033
+
1034
+ if (addr >> 1 >= this.start_address) {
1035
+ const memory_start = (addr >> 1) - this.start_address
1036
+ row = (memory_start / max_cols) | 0
1037
+ col = memory_start % max_cols
1038
+ } else {
1039
+ const memory_start = addr >> 1
1040
+ row =
1041
+ ((memory_start / max_cols) | 0) +
1042
+ this.scan_line_to_screen_row(this.line_compare)
1043
+ col = memory_start % max_cols
1044
+ }
1045
+
1046
+ dbg_assert(row >= 0 && col >= 0)
1047
+
1048
+ if (col >= this.max_cols || row >= this.max_rows) {
1049
+ return
1050
+ }
1051
+
1052
+ let chr: number
1053
+ let color: number
1054
+
1055
+ // XXX: Should handle 16 bit write if possible
1056
+ if (addr & 1) {
1057
+ color = value
1058
+ chr = this.vga_memory[addr & ~1]
1059
+ } else {
1060
+ chr = value
1061
+ color = this.vga_memory[addr | 1]
1062
+ }
1063
+ const blink_enabled = this.attribute_mode & (1 << 3)
1064
+ const blinking = blink_enabled && color & (1 << 7)
1065
+ const font_page_b = this.font_page_ab_enabled && !(color & (1 << 3))
1066
+ const flags =
1067
+ (blinking ? this.screen.FLAG_BLINKING : 0) |
1068
+ (font_page_b ? this.screen.FLAG_FONT_PAGE_B : 0)
1069
+ const fg_color_mask = this.font_page_ab_enabled ? 7 : 0xf
1070
+ const bg_color_mask = blink_enabled ? 7 : 0xf
1071
+
1072
+ this.bus.send('screen-put-char', [row, col, chr])
1073
+
1074
+ this.screen.put_char(
1075
+ row,
1076
+ col,
1077
+ chr,
1078
+ flags,
1079
+ this.vga256_palette[
1080
+ this.dac_mask & this.dac_map[(color >> 4) & bg_color_mask]
1081
+ ],
1082
+ this.vga256_palette[
1083
+ this.dac_mask & this.dac_map[color & fg_color_mask]
1084
+ ],
1085
+ )
1086
+ }
1087
+
1088
+ update_cursor(): void {
1089
+ const max_cols = Math.max(this.max_cols, this.offset_register * 2)
1090
+ let row: number
1091
+ let col: number
1092
+
1093
+ if (this.cursor_address >= this.start_address) {
1094
+ row = ((this.cursor_address - this.start_address) / max_cols) | 0
1095
+ col = (this.cursor_address - this.start_address) % max_cols
1096
+ } else {
1097
+ row =
1098
+ ((this.cursor_address / max_cols) | 0) +
1099
+ this.scan_line_to_screen_row(this.line_compare)
1100
+ col = this.cursor_address % max_cols
1101
+ }
1102
+
1103
+ dbg_assert(row >= 0 && col >= 0)
1104
+
1105
+ // NOTE: is allowed to be out of bounds
1106
+ this.screen.update_cursor(row, col)
1107
+ }
1108
+
1109
+ complete_redraw(): void {
1110
+ dbg_log('complete redraw', LOG_VGA)
1111
+
1112
+ if (this.graphical_mode) {
1113
+ if (this.svga_enabled) {
1114
+ this.cpu.svga_mark_dirty()
1115
+ } else {
1116
+ this.diff_addr_min = 0
1117
+ this.diff_addr_max = VGA_PIXEL_BUFFER_SIZE
1118
+ }
1119
+ } else {
1120
+ this.text_mode_redraw()
1121
+ }
1122
+ }
1123
+
1124
+ complete_replot(): void {
1125
+ dbg_log('complete replot', LOG_VGA)
1126
+
1127
+ if (!this.graphical_mode || this.svga_enabled) {
1128
+ return
1129
+ }
1130
+
1131
+ this.diff_plot_min = 0
1132
+ this.diff_plot_max = VGA_PIXEL_BUFFER_SIZE
1133
+
1134
+ this.complete_redraw()
1135
+ }
1136
+
1137
+ partial_redraw(min: number, max: number): void {
1138
+ if (min < this.diff_addr_min) this.diff_addr_min = min
1139
+ if (max > this.diff_addr_max) this.diff_addr_max = max
1140
+ }
1141
+
1142
+ partial_replot(min: number, max: number): void {
1143
+ if (min < this.diff_plot_min) this.diff_plot_min = min
1144
+ if (max > this.diff_plot_max) this.diff_plot_max = max
1145
+
1146
+ this.partial_redraw(min, max)
1147
+ }
1148
+
1149
+ reset_diffs(): void {
1150
+ this.diff_addr_min = this.vga_memory_size
1151
+ this.diff_addr_max = 0
1152
+ this.diff_plot_min = this.vga_memory_size
1153
+ this.diff_plot_max = 0
1154
+ }
1155
+
1156
+ destroy(): void {}
1157
+
1158
+ vga_bytes_per_line(): number {
1159
+ let bytes_per_line = this.offset_register << 2
1160
+ if (this.underline_location_register & 0x40) bytes_per_line <<= 1
1161
+ else if (this.crtc_mode & 0x40) bytes_per_line >>>= 1
1162
+ return bytes_per_line
1163
+ }
1164
+
1165
+ vga_addr_shift_count(): number {
1166
+ // Count in multiples of 0x40 for convenience
1167
+ // Left shift 2 for word mode - 2 bytes per dot clock
1168
+ let shift_count = 0x80
1169
+
1170
+ // Left shift 3 for byte mode - 1 byte per dot clock
1171
+ shift_count += ~this.underline_location_register & this.crtc_mode & 0x40
1172
+
1173
+ // Left shift 1 for doubleword mode - 4 bytes per dot clock
1174
+ shift_count -= this.underline_location_register & 0x40
1175
+
1176
+ // But shift one less if PEL width mode - 2 dot clocks per pixel
1177
+ shift_count -= this.attribute_mode & 0x40
1178
+
1179
+ return shift_count >>> 6
1180
+ }
1181
+
1182
+ vga_addr_to_pixel(addr: number): number {
1183
+ const shift_count = this.vga_addr_shift_count()
1184
+
1185
+ // Undo effects of substituted bits 13 and 14
1186
+ // Assumptions:
1187
+ // - max_scan_line register is set to the values shown below
1188
+ // - Each scan line stays within the offset alignment
1189
+ // - No panning and no page flipping after drawing
1190
+ if (~this.crtc_mode & 0x3) {
1191
+ let pixel_addr = addr - this.start_address
1192
+
1193
+ // Remove substituted bits
1194
+ pixel_addr &= (this.crtc_mode << 13) | ~0x6000
1195
+
1196
+ // Convert to 1 pixel per address
1197
+ pixel_addr <<= shift_count
1198
+
1199
+ // Decompose address
1200
+ let row = (pixel_addr / this.virtual_width) | 0
1201
+ const col = pixel_addr % this.virtual_width
1202
+
1203
+ switch (this.crtc_mode & 0x3) {
1204
+ case 0x2:
1205
+ // Alternating rows using bit 13
1206
+ // Assumes max scan line = 1
1207
+ row = (row << 1) | ((addr >> 13) & 0x1)
1208
+ break
1209
+ case 0x1:
1210
+ // Alternating rows using bit 14
1211
+ // Assumes max scan line = 3
1212
+ row = (row << 1) | ((addr >> 14) & 0x1)
1213
+ break
1214
+ case 0x0:
1215
+ // Cycling through rows using bit 13 and 14
1216
+ // Assumes max scan line = 3
1217
+ row = (row << 2) | ((addr >> 13) & 0x3)
1218
+ break
1219
+ }
1220
+
1221
+ // Reassemble address
1222
+ return (
1223
+ row * this.virtual_width +
1224
+ col +
1225
+ (this.start_address << shift_count)
1226
+ )
1227
+ } else {
1228
+ // Convert to 1 pixel per address
1229
+ return addr << shift_count
1230
+ }
1231
+ }
1232
+
1233
+ scan_line_to_screen_row(scan_line: number): number {
1234
+ // Double scanning. The clock to the row scan counter is halved
1235
+ // so it is not affected by the memory address bit substitutions below
1236
+ if (this.max_scan_line & 0x80) {
1237
+ scan_line >>>= 1
1238
+ }
1239
+
1240
+ // Maximum scan line, aka scan lines per character row
1241
+ // This is the number of repeats - 1 for graphic modes
1242
+ const repeat_factor = 1 + (this.max_scan_line & 0x1f)
1243
+ scan_line = Math.ceil(scan_line / repeat_factor)
1244
+
1245
+ // Odd and Even Row Scan Counter
1246
+ // Despite repeated address counter values, because bit 13 of the shifted
1247
+ // address is substituted with bit 0 of the row scan counter, a different
1248
+ // display buffer address is generated instead of repeated
1249
+ // Assumes maximum scan line register is set to 2 or 4.
1250
+ // Note: can't assert this as register values may not be fully programmed.
1251
+ if (!(this.crtc_mode & 0x1)) {
1252
+ scan_line <<= 1
1253
+ }
1254
+
1255
+ // Undo effects of substituted bit 14
1256
+ // Assumes maximum scan line register is set to 2 or 4
1257
+ // Note: can't assert this as register values may not be fully programmed.
1258
+ // Other maximum scan line register values would result in weird addressing
1259
+ // anyway
1260
+ if (!(this.crtc_mode & 0x2)) {
1261
+ scan_line <<= 1
1262
+ }
1263
+
1264
+ return scan_line
1265
+ }
1266
+
1267
+ set_size_text(cols_count: number, rows_count: number): void {
1268
+ dbg_assert(!this.graphical_mode)
1269
+ this.max_cols = cols_count
1270
+ this.max_rows = rows_count
1271
+
1272
+ this.screen.set_size_text(cols_count, rows_count)
1273
+ this.bus.send('screen-set-size', [cols_count, rows_count, 0])
1274
+ }
1275
+
1276
+ set_size_graphical(
1277
+ width: number,
1278
+ height: number,
1279
+ virtual_width: number,
1280
+ virtual_height: number,
1281
+ bpp: number,
1282
+ ): void {
1283
+ dbg_assert(this.graphical_mode)
1284
+
1285
+ virtual_width = Math.max(virtual_width, 1)
1286
+ virtual_height = Math.max(virtual_height, 1)
1287
+
1288
+ const needs_update =
1289
+ this.screen_width !== width ||
1290
+ this.screen_height !== height ||
1291
+ this.virtual_width !== virtual_width ||
1292
+ this.virtual_height !== virtual_height
1293
+
1294
+ if (needs_update) {
1295
+ this.screen_width = width
1296
+ this.screen_height = height
1297
+ this.virtual_width = virtual_width
1298
+ this.virtual_height = virtual_height
1299
+
1300
+ if (typeof ImageData !== 'undefined') {
1301
+ const size = virtual_width * virtual_height
1302
+ const offset = this.cpu.svga_allocate_dest_buffer(size) >>> 0
1303
+
1304
+ this.dest_buffet_offset = offset
1305
+ this.image_data = new ImageData(
1306
+ new Uint8ClampedArray(
1307
+ this.cpu.wasm_memory.buffer,
1308
+ offset,
1309
+ 4 * size,
1310
+ ),
1311
+ virtual_width,
1312
+ virtual_height,
1313
+ )
1314
+
1315
+ this.cpu.svga_mark_dirty()
1316
+ } else {
1317
+ // TODO: nodejs
1318
+ }
1319
+
1320
+ this.screen.set_size_graphical(
1321
+ width,
1322
+ height,
1323
+ virtual_width,
1324
+ virtual_height,
1325
+ )
1326
+ this.bus.send('screen-set-size', [width, height, bpp])
1327
+ }
1328
+ }
1329
+
1330
+ update_vga_size(): void {
1331
+ if (this.svga_enabled) {
1332
+ return
1333
+ }
1334
+
1335
+ const horizontal_characters = Math.min(
1336
+ 1 + this.horizontal_display_enable_end,
1337
+ this.horizontal_blank_start,
1338
+ )
1339
+ let vertical_scans = Math.min(
1340
+ 1 + this.vertical_display_enable_end,
1341
+ this.vertical_blank_start,
1342
+ )
1343
+
1344
+ if (!horizontal_characters || !vertical_scans) {
1345
+ // Don't update if width or height is zero.
1346
+ // These happen when registers are not fully configured yet.
1347
+ return
1348
+ }
1349
+
1350
+ if (this.graphical_mode) {
1351
+ let screen_width = horizontal_characters << 3
1352
+
1353
+ // Offset is half the number of bytes/words/dwords (depending on clocking mode)
1354
+ // of display memory that each logical line occupies.
1355
+ // However, the number of pixels latched, regardless of addressing mode,
1356
+ // should always 8 pixels per character clock (except for 8 bit PEL width, in which
1357
+ // case 4 pixels).
1358
+ let virtual_width = this.offset_register << 4
1359
+ let bpp = 4
1360
+
1361
+ // Pixel Width / PEL Width / Clock Select
1362
+ if (this.attribute_mode & 0x40) {
1363
+ screen_width >>>= 1
1364
+ virtual_width >>>= 1
1365
+ bpp = 8
1366
+ } else if (this.attribute_mode & 0x2) {
1367
+ bpp = 1
1368
+ }
1369
+
1370
+ const screen_height = this.scan_line_to_screen_row(vertical_scans)
1371
+
1372
+ // The virtual buffer height is however many rows of data that can fit.
1373
+ // Previously drawn graphics outside of current memory address space can
1374
+ // still be drawn by setting start_address. The address at
1375
+ // VGA_HOST_MEMORY_SPACE_START[memory_space_select] is mapped to the first
1376
+ // byte of the frame buffer. Verified on some hardware.
1377
+ // Depended on by: Windows 98 start screen
1378
+ const available_bytes = VGA_HOST_MEMORY_SPACE_SIZE[0]
1379
+
1380
+ const bytes_per_line = this.vga_bytes_per_line()
1381
+ const virtual_height = bytes_per_line
1382
+ ? Math.ceil(available_bytes / bytes_per_line)
1383
+ : screen_height
1384
+
1385
+ this.set_size_graphical(
1386
+ screen_width,
1387
+ screen_height,
1388
+ virtual_width,
1389
+ virtual_height,
1390
+ bpp,
1391
+ )
1392
+
1393
+ this.update_vertical_retrace()
1394
+ this.update_layers()
1395
+ } else {
1396
+ if (this.max_scan_line & 0x80) {
1397
+ // Double scanning means that half of those scan lines
1398
+ // are just repeats
1399
+ vertical_scans >>>= 1
1400
+ }
1401
+
1402
+ const height =
1403
+ (vertical_scans / (1 + (this.max_scan_line & 0x1f))) | 0
1404
+
1405
+ if (horizontal_characters && height) {
1406
+ this.set_size_text(horizontal_characters, height)
1407
+ }
1408
+ }
1409
+ }
1410
+
1411
+ update_layers(): void {
1412
+ if (!this.graphical_mode) {
1413
+ this.text_mode_redraw()
1414
+ }
1415
+
1416
+ if (this.svga_enabled) {
1417
+ this.layers = []
1418
+ return
1419
+ }
1420
+
1421
+ if (!this.virtual_width || !this.screen_width) {
1422
+ // Avoid division by zero
1423
+ return
1424
+ }
1425
+
1426
+ if (!this.palette_source || this.clocking_mode & 0x20) {
1427
+ // Palette source and screen disable bits = draw nothing
1428
+ // See http://www.phatcode.net/res/224/files/html/ch29/29-05.html#Heading6
1429
+ // and http://www.osdever.net/FreeVGA/vga/seqreg.htm#01
1430
+ this.layers = []
1431
+ this.screen.clear_screen()
1432
+ return
1433
+ }
1434
+
1435
+ const start_addr = this.start_address_latched
1436
+
1437
+ let pixel_panning = this.horizontal_panning
1438
+ if (this.attribute_mode & 0x40) {
1439
+ pixel_panning >>>= 1
1440
+ }
1441
+
1442
+ const byte_panning = (this.preset_row_scan >> 5) & 0x3
1443
+ const pixel_addr_start = this.vga_addr_to_pixel(
1444
+ start_addr + byte_panning,
1445
+ )
1446
+
1447
+ const start_buffer_row = (pixel_addr_start / this.virtual_width) | 0
1448
+ const start_buffer_col =
1449
+ (pixel_addr_start % this.virtual_width) + pixel_panning
1450
+
1451
+ let split_screen_row = this.scan_line_to_screen_row(
1452
+ 1 + this.line_compare,
1453
+ )
1454
+ split_screen_row = Math.min(split_screen_row, this.screen_height)
1455
+
1456
+ const split_buffer_height = this.screen_height - split_screen_row
1457
+
1458
+ this.layers = []
1459
+
1460
+ for (
1461
+ let x = -start_buffer_col, y = 0;
1462
+ x < this.screen_width;
1463
+ x += this.virtual_width, y++
1464
+ ) {
1465
+ this.layers.push({
1466
+ image_data: this.image_data,
1467
+ screen_x: x,
1468
+ screen_y: 0,
1469
+ buffer_x: 0,
1470
+ buffer_y: start_buffer_row + y,
1471
+ buffer_width: this.virtual_width,
1472
+ buffer_height: split_screen_row,
1473
+ })
1474
+ }
1475
+
1476
+ let start_split_col = 0
1477
+ if (!(this.attribute_mode & 0x20)) {
1478
+ // Pixel panning mode. Allow panning for the lower split screen
1479
+ start_split_col =
1480
+ this.vga_addr_to_pixel(byte_panning) + pixel_panning
1481
+ }
1482
+
1483
+ for (
1484
+ let x = -start_split_col, y = 0;
1485
+ x < this.screen_width;
1486
+ x += this.virtual_width, y++
1487
+ ) {
1488
+ this.layers.push({
1489
+ image_data: this.image_data,
1490
+ screen_x: x,
1491
+ screen_y: split_screen_row,
1492
+ buffer_x: 0,
1493
+ buffer_y: y,
1494
+ buffer_width: this.virtual_width,
1495
+ buffer_height: split_buffer_height,
1496
+ })
1497
+ }
1498
+ }
1499
+
1500
+ update_vertical_retrace(): void {
1501
+ // Emulate behaviour during VSync/VRetrace
1502
+ this.port_3DA_value |= 0x8
1503
+ if (this.start_address_latched !== this.start_address) {
1504
+ this.start_address_latched = this.start_address
1505
+ this.update_layers()
1506
+ }
1507
+ }
1508
+
1509
+ update_cursor_scanline(): void {
1510
+ const disabled = this.cursor_scanline_start & 0x20
1511
+ const max = this.max_scan_line & 0x1f
1512
+ const start = Math.min(max, this.cursor_scanline_start & 0x1f)
1513
+ const end = Math.min(max, this.cursor_scanline_end & 0x1f)
1514
+ const visible = !disabled && start < end
1515
+ this.screen.update_cursor_scanline(start, end, visible)
1516
+ }
1517
+
1518
+ // Attribute controller register / index write
1519
+ // @see http://www.osdever.net/FreeVGA/vga/attrreg.htm
1520
+ // @see http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf page 89
1521
+ // @see https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf page 48
1522
+ port3C0_write(value: number): void {
1523
+ if (this.attribute_controller_index === -1) {
1524
+ dbg_log('attribute controller index register: ' + h(value), LOG_VGA)
1525
+ this.attribute_controller_index = value & 0x1f
1526
+ dbg_log(
1527
+ 'attribute actual index: ' + h(this.attribute_controller_index),
1528
+ LOG_VGA,
1529
+ )
1530
+
1531
+ if (this.palette_source !== (value & 0x20)) {
1532
+ // A method of blanking the screen.
1533
+ // See http://www.phatcode.net/res/224/files/html/ch29/29-05.html#Heading6
1534
+ this.palette_source = value & 0x20
1535
+ this.update_layers()
1536
+ }
1537
+ } else {
1538
+ if (this.attribute_controller_index < 0x10) {
1539
+ dbg_log(
1540
+ 'internal palette: ' +
1541
+ h(this.attribute_controller_index) +
1542
+ ' -> ' +
1543
+ h(value),
1544
+ LOG_VGA,
1545
+ )
1546
+ this.dac_map[this.attribute_controller_index] = value
1547
+
1548
+ if (!(this.attribute_mode & 0x40)) {
1549
+ this.complete_redraw()
1550
+ }
1551
+ } else
1552
+ switch (this.attribute_controller_index) {
1553
+ case 0x10:
1554
+ dbg_log(
1555
+ '3C0 / attribute mode control: ' + h(value),
1556
+ LOG_VGA,
1557
+ )
1558
+ if (this.attribute_mode !== value) {
1559
+ const previous_mode = this.attribute_mode
1560
+ this.attribute_mode = value
1561
+
1562
+ const is_graphical = (value & 0x1) !== 0
1563
+ if (
1564
+ !this.svga_enabled &&
1565
+ this.graphical_mode !== is_graphical
1566
+ ) {
1567
+ this.graphical_mode = is_graphical
1568
+ this.screen.set_mode(this.graphical_mode)
1569
+ }
1570
+
1571
+ if ((previous_mode ^ value) & 0x40) {
1572
+ // PEL width changed. Pixel Buffer now invalidated
1573
+ this.complete_replot()
1574
+ }
1575
+
1576
+ this.update_vga_size()
1577
+
1578
+ // Data stored in image buffer are invalidated
1579
+ this.complete_redraw()
1580
+
1581
+ this.set_font_bitmap(false)
1582
+ }
1583
+ break
1584
+ case 0x12:
1585
+ dbg_log(
1586
+ '3C0 / color plane enable: ' + h(value),
1587
+ LOG_VGA,
1588
+ )
1589
+ if (this.color_plane_enable !== value) {
1590
+ this.color_plane_enable = value
1591
+
1592
+ // Data stored in image buffer are invalidated
1593
+ this.complete_redraw()
1594
+ }
1595
+ break
1596
+ case 0x13:
1597
+ dbg_log(
1598
+ '3C0 / horizontal panning: ' + h(value),
1599
+ LOG_VGA,
1600
+ )
1601
+ if (this.horizontal_panning !== value) {
1602
+ this.horizontal_panning = value & 0xf
1603
+ this.update_layers()
1604
+ }
1605
+ break
1606
+ case 0x14:
1607
+ dbg_log('3C0 / color select: ' + h(value), LOG_VGA)
1608
+ if (this.color_select !== value) {
1609
+ this.color_select = value
1610
+
1611
+ // Data stored in image buffer are invalidated
1612
+ this.complete_redraw()
1613
+ }
1614
+ break
1615
+ default:
1616
+ dbg_log(
1617
+ '3C0 / attribute controller write ' +
1618
+ h(this.attribute_controller_index) +
1619
+ ': ' +
1620
+ h(value),
1621
+ LOG_VGA,
1622
+ )
1623
+ }
1624
+
1625
+ this.attribute_controller_index = -1
1626
+ }
1627
+ }
1628
+
1629
+ port3C0_read(): number {
1630
+ dbg_log('3C0 read', LOG_VGA)
1631
+ return (this.attribute_controller_index | this.palette_source) & 0xff
1632
+ }
1633
+
1634
+ port3C0_read16(): number {
1635
+ dbg_log('3C0 read16', LOG_VGA)
1636
+ return this.port3C0_read() | ((this.port3C1_read() << 8) & 0xff00)
1637
+ }
1638
+
1639
+ port3C1_read(): number {
1640
+ if (this.attribute_controller_index < 0x10) {
1641
+ dbg_log(
1642
+ '3C1 / internal palette read: ' +
1643
+ h(this.attribute_controller_index) +
1644
+ ' -> ' +
1645
+ h(this.dac_map[this.attribute_controller_index]),
1646
+ LOG_VGA,
1647
+ )
1648
+ return this.dac_map[this.attribute_controller_index] & 0xff
1649
+ }
1650
+
1651
+ switch (this.attribute_controller_index) {
1652
+ case 0x10:
1653
+ dbg_log(
1654
+ '3C1 / attribute mode read: ' + h(this.attribute_mode),
1655
+ LOG_VGA,
1656
+ )
1657
+ return this.attribute_mode
1658
+ case 0x12:
1659
+ dbg_log(
1660
+ '3C1 / color plane enable read: ' +
1661
+ h(this.color_plane_enable),
1662
+ LOG_VGA,
1663
+ )
1664
+ return this.color_plane_enable
1665
+ case 0x13:
1666
+ dbg_log(
1667
+ '3C1 / horizontal panning read: ' +
1668
+ h(this.horizontal_panning),
1669
+ LOG_VGA,
1670
+ )
1671
+ return this.horizontal_panning
1672
+ case 0x14:
1673
+ dbg_log(
1674
+ '3C1 / color select read: ' + h(this.color_select),
1675
+ LOG_VGA,
1676
+ )
1677
+ return this.color_select
1678
+ default:
1679
+ dbg_log(
1680
+ '3C1 / attribute controller read ' +
1681
+ h(this.attribute_controller_index),
1682
+ LOG_VGA,
1683
+ )
1684
+ }
1685
+ return 0xff
1686
+ }
1687
+
1688
+ port3C2_write(value: number): void {
1689
+ dbg_log('3C2 / miscellaneous output register = ' + h(value), LOG_VGA)
1690
+ this.miscellaneous_output_register = value
1691
+ }
1692
+
1693
+ port3C4_write(value: number): void {
1694
+ this.sequencer_index = value
1695
+ }
1696
+
1697
+ port3C4_read(): number {
1698
+ return this.sequencer_index
1699
+ }
1700
+
1701
+ // Sequencer register writes
1702
+ // @see http://www.osdever.net/FreeVGA/vga/seqreg.htm
1703
+ // @see http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf page 47
1704
+ // @see https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf page 19
1705
+ port3C5_write(value: number): void {
1706
+ switch (this.sequencer_index) {
1707
+ case 0x01: {
1708
+ dbg_log('clocking mode: ' + h(value), LOG_VGA)
1709
+ const previous_clocking_mode = this.clocking_mode
1710
+ this.clocking_mode = value
1711
+ if ((previous_clocking_mode ^ value) & 0x20) {
1712
+ // Screen disable bit modified
1713
+ this.update_layers()
1714
+ }
1715
+ this.set_font_bitmap(false)
1716
+ break
1717
+ }
1718
+ case 0x02: {
1719
+ dbg_log('plane write mask: ' + h(value), LOG_VGA)
1720
+ const previous_plane_write_bm = this.plane_write_bm
1721
+ this.plane_write_bm = value
1722
+ if (
1723
+ !this.graphical_mode &&
1724
+ previous_plane_write_bm & 0x4 &&
1725
+ !(this.plane_write_bm & 0x4)
1726
+ ) {
1727
+ // End of font plane 2 write access
1728
+ this.set_font_bitmap(true)
1729
+ }
1730
+ break
1731
+ }
1732
+ case 0x03: {
1733
+ dbg_log('character map select: ' + h(value), LOG_VGA)
1734
+ const previous_character_map_select = this.character_map_select
1735
+ this.character_map_select = value
1736
+ if (
1737
+ !this.graphical_mode &&
1738
+ previous_character_map_select !== value
1739
+ ) {
1740
+ this.set_font_page()
1741
+ }
1742
+ break
1743
+ }
1744
+ case 0x04:
1745
+ dbg_log('sequencer memory mode: ' + h(value), LOG_VGA)
1746
+ this.sequencer_memory_mode = value
1747
+ break
1748
+ default:
1749
+ dbg_log(
1750
+ '3C5 / sequencer write ' +
1751
+ h(this.sequencer_index) +
1752
+ ': ' +
1753
+ h(value),
1754
+ LOG_VGA,
1755
+ )
1756
+ }
1757
+ }
1758
+
1759
+ port3C5_read(): number {
1760
+ dbg_log('3C5 / sequencer read ' + h(this.sequencer_index), LOG_VGA)
1761
+
1762
+ switch (this.sequencer_index) {
1763
+ case 0x01:
1764
+ return this.clocking_mode
1765
+ case 0x02:
1766
+ return this.plane_write_bm
1767
+ case 0x03:
1768
+ return this.character_map_select
1769
+ case 0x04:
1770
+ return this.sequencer_memory_mode
1771
+ case 0x06:
1772
+ return 0x12
1773
+ default:
1774
+ }
1775
+ return 0
1776
+ }
1777
+
1778
+ port3C6_write(data: number): void {
1779
+ if (this.dac_mask !== data) {
1780
+ this.dac_mask = data
1781
+ this.complete_redraw()
1782
+ }
1783
+ }
1784
+
1785
+ port3C6_read(): number {
1786
+ return this.dac_mask
1787
+ }
1788
+
1789
+ port3C7_write(index: number): void {
1790
+ // index for reading the DAC
1791
+ dbg_log('3C7 write: ' + h(index), LOG_VGA)
1792
+ this.dac_color_index_read = index * 3
1793
+ this.dac_state &= 0x0
1794
+ }
1795
+
1796
+ port3C7_read(): number {
1797
+ // prepared to accept reads or writes
1798
+ return this.dac_state
1799
+ }
1800
+
1801
+ port3C8_write(index: number): void {
1802
+ this.dac_color_index_write = index * 3
1803
+ this.dac_state |= 0x3
1804
+ }
1805
+
1806
+ port3C8_read(): number {
1807
+ return (this.dac_color_index_write / 3) & 0xff
1808
+ }
1809
+
1810
+ // DAC color palette register writes
1811
+ // @see http://www.osdever.net/FreeVGA/vga/colorreg.htm
1812
+ // @see http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf page 104
1813
+ // @see https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf page 57
1814
+ port3C9_write(color_byte: number): void {
1815
+ const index = (this.dac_color_index_write / 3) | 0,
1816
+ offset = this.dac_color_index_write % 3
1817
+ let color = this.vga256_palette[index]
1818
+
1819
+ if ((this.dispi_enable_value & 0x20) === 0) {
1820
+ color_byte &= 0x3f
1821
+ const b = color_byte & 1
1822
+ color_byte = (color_byte << 2) | (b << 1) | b
1823
+ }
1824
+
1825
+ if (offset === 0) {
1826
+ color = (color & ~0xff0000) | (color_byte << 16)
1827
+ } else if (offset === 1) {
1828
+ color = (color & ~0xff00) | (color_byte << 8)
1829
+ } else {
1830
+ color = (color & ~0xff) | color_byte
1831
+ dbg_log(
1832
+ 'dac set color, index=' + h(index) + ' value=' + h(color),
1833
+ LOG_VGA,
1834
+ )
1835
+ }
1836
+
1837
+ if (this.vga256_palette[index] !== color) {
1838
+ this.vga256_palette[index] = color
1839
+ this.complete_redraw()
1840
+ }
1841
+ this.dac_color_index_write++
1842
+ }
1843
+
1844
+ port3C9_read(): number {
1845
+ dbg_log('3C9 read', LOG_VGA)
1846
+
1847
+ const index = (this.dac_color_index_read / 3) | 0
1848
+ const offset = this.dac_color_index_read % 3
1849
+ const color = this.vga256_palette[index]
1850
+ const color8 = (color >> ((2 - offset) * 8)) & 0xff
1851
+
1852
+ this.dac_color_index_read++
1853
+
1854
+ if (this.dispi_enable_value & 0x20) {
1855
+ return color8
1856
+ } else {
1857
+ return color8 >> 2
1858
+ }
1859
+ }
1860
+
1861
+ port3CC_read(): number {
1862
+ dbg_log('3CC read', LOG_VGA)
1863
+ return this.miscellaneous_output_register
1864
+ }
1865
+
1866
+ port3CE_write(value: number): void {
1867
+ this.graphics_index = value
1868
+ }
1869
+
1870
+ port3CE_read(): number {
1871
+ return this.graphics_index
1872
+ }
1873
+
1874
+ // Graphics controller register writes
1875
+ // @see http://www.osdever.net/FreeVGA/vga/graphreg.htm
1876
+ // @see http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf page 78
1877
+ // @see https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf page 29
1878
+ port3CF_write(value: number): void {
1879
+ switch (this.graphics_index) {
1880
+ case 0:
1881
+ this.planar_setreset = value
1882
+ dbg_log('plane set/reset: ' + h(value), LOG_VGA)
1883
+ break
1884
+ case 1:
1885
+ this.planar_setreset_enable = value
1886
+ dbg_log('plane set/reset enable: ' + h(value), LOG_VGA)
1887
+ break
1888
+ case 2:
1889
+ this.color_compare = value
1890
+ dbg_log('color compare: ' + h(value), LOG_VGA)
1891
+ break
1892
+ case 3:
1893
+ this.planar_rotate_reg = value
1894
+ dbg_log('plane rotate: ' + h(value), LOG_VGA)
1895
+ break
1896
+ case 4:
1897
+ this.plane_read = value
1898
+ dbg_log('plane read: ' + h(value), LOG_VGA)
1899
+ break
1900
+ case 5: {
1901
+ const previous_planar_mode = this.planar_mode
1902
+ this.planar_mode = value
1903
+ dbg_log('planar mode: ' + h(value), LOG_VGA)
1904
+ if ((previous_planar_mode ^ value) & 0x60) {
1905
+ // Shift mode modified. Pixel buffer invalidated
1906
+ this.complete_replot()
1907
+ }
1908
+ break
1909
+ }
1910
+ case 6:
1911
+ dbg_log('miscellaneous graphics register: ' + h(value), LOG_VGA)
1912
+ if (this.miscellaneous_graphics_register !== value) {
1913
+ this.miscellaneous_graphics_register = value
1914
+ this.update_vga_size()
1915
+ }
1916
+ break
1917
+ case 7:
1918
+ this.color_dont_care = value
1919
+ dbg_log("color don't care: " + h(value), LOG_VGA)
1920
+ break
1921
+ case 8:
1922
+ this.planar_bitmap = value
1923
+ dbg_log('planar bitmap: ' + h(value), LOG_VGA)
1924
+ break
1925
+ default:
1926
+ dbg_log(
1927
+ '3CF / graphics write ' +
1928
+ h(this.graphics_index) +
1929
+ ': ' +
1930
+ h(value),
1931
+ LOG_VGA,
1932
+ )
1933
+ }
1934
+ }
1935
+
1936
+ port3CF_read(): number {
1937
+ dbg_log('3CF / graphics read ' + h(this.graphics_index), LOG_VGA)
1938
+
1939
+ switch (this.graphics_index) {
1940
+ case 0:
1941
+ return this.planar_setreset
1942
+ case 1:
1943
+ return this.planar_setreset_enable
1944
+ case 2:
1945
+ return this.color_compare
1946
+ case 3:
1947
+ return this.planar_rotate_reg
1948
+ case 4:
1949
+ return this.plane_read
1950
+ case 5:
1951
+ return this.planar_mode
1952
+ case 6:
1953
+ return this.miscellaneous_graphics_register
1954
+ case 7:
1955
+ return this.color_dont_care
1956
+ case 8:
1957
+ return this.planar_bitmap
1958
+ default:
1959
+ }
1960
+ return 0
1961
+ }
1962
+
1963
+ port3D4_write(register: number): void {
1964
+ dbg_log('3D4 / crtc index: ' + register, LOG_VGA)
1965
+ this.index_crtc = register
1966
+ }
1967
+
1968
+ port3D4_write16(register: number): void {
1969
+ this.port3D4_write(register & 0xff)
1970
+ this.port3D5_write((register >> 8) & 0xff)
1971
+ }
1972
+
1973
+ port3D4_read(): number {
1974
+ dbg_log('3D4 read / crtc index: ' + this.index_crtc, LOG_VGA)
1975
+ return this.index_crtc
1976
+ }
1977
+
1978
+ // CRT controller register writes
1979
+ // @see http://www.osdever.net/FreeVGA/vga/crtcreg.htm
1980
+ // @see http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf page 55
1981
+ // @see https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf page 63
1982
+ port3D5_write(value: number): void {
1983
+ switch (this.index_crtc) {
1984
+ case 0x1:
1985
+ dbg_log('3D5 / hdisp enable end write: ' + h(value), LOG_VGA)
1986
+ if (this.horizontal_display_enable_end !== value) {
1987
+ this.horizontal_display_enable_end = value
1988
+ this.update_vga_size()
1989
+ }
1990
+ break
1991
+ case 0x2:
1992
+ if (this.horizontal_blank_start !== value) {
1993
+ this.horizontal_blank_start = value
1994
+ this.update_vga_size()
1995
+ }
1996
+ break
1997
+ case 0x7: {
1998
+ dbg_log('3D5 / overflow register write: ' + h(value), LOG_VGA)
1999
+ const previous_vertical_display_enable_end =
2000
+ this.vertical_display_enable_end
2001
+ this.vertical_display_enable_end &= 0xff
2002
+ this.vertical_display_enable_end |=
2003
+ ((value << 3) & 0x200) | ((value << 7) & 0x100)
2004
+ if (
2005
+ previous_vertical_display_enable_end !==
2006
+ this.vertical_display_enable_end
2007
+ ) {
2008
+ this.update_vga_size()
2009
+ }
2010
+ this.line_compare =
2011
+ (this.line_compare & 0x2ff) | ((value << 4) & 0x100)
2012
+
2013
+ const previous_vertical_blank_start = this.vertical_blank_start
2014
+ this.vertical_blank_start =
2015
+ (this.vertical_blank_start & 0x2ff) | ((value << 5) & 0x100)
2016
+ if (
2017
+ previous_vertical_blank_start !== this.vertical_blank_start
2018
+ ) {
2019
+ this.update_vga_size()
2020
+ }
2021
+ this.update_layers()
2022
+ break
2023
+ }
2024
+ case 0x8:
2025
+ dbg_log('3D5 / preset row scan write: ' + h(value), LOG_VGA)
2026
+ this.preset_row_scan = value
2027
+ this.update_layers()
2028
+ break
2029
+ case 0x9: {
2030
+ dbg_log('3D5 / max scan line write: ' + h(value), LOG_VGA)
2031
+ const previous_max_scan_line = this.max_scan_line
2032
+ this.max_scan_line = value
2033
+ this.line_compare =
2034
+ (this.line_compare & 0x1ff) | ((value << 3) & 0x200)
2035
+
2036
+ const previous_vertical_blank_start2 = this.vertical_blank_start
2037
+ this.vertical_blank_start =
2038
+ (this.vertical_blank_start & 0x1ff) | ((value << 4) & 0x200)
2039
+ if (
2040
+ (previous_max_scan_line ^ this.max_scan_line) & 0x9f ||
2041
+ previous_vertical_blank_start2 !== this.vertical_blank_start
2042
+ ) {
2043
+ this.update_vga_size()
2044
+ }
2045
+
2046
+ this.update_cursor_scanline()
2047
+ this.update_layers()
2048
+
2049
+ this.set_font_bitmap(false)
2050
+ break
2051
+ }
2052
+ case 0xa:
2053
+ dbg_log(
2054
+ '3D5 / cursor scanline start write: ' + h(value),
2055
+ LOG_VGA,
2056
+ )
2057
+ this.cursor_scanline_start = value
2058
+ this.update_cursor_scanline()
2059
+ break
2060
+ case 0xb:
2061
+ dbg_log('3D5 / cursor scanline end write: ' + h(value), LOG_VGA)
2062
+ this.cursor_scanline_end = value
2063
+ this.update_cursor_scanline()
2064
+ break
2065
+ case 0xc:
2066
+ if (((this.start_address >> 8) & 0xff) !== value) {
2067
+ this.start_address =
2068
+ (this.start_address & 0xff) | (value << 8)
2069
+ this.update_layers()
2070
+ if (~this.crtc_mode & 0x3) {
2071
+ // Address substitution implementation depends on the
2072
+ // starting row and column, so the pixel buffer is invalidated.
2073
+ this.complete_replot()
2074
+ }
2075
+ }
2076
+ dbg_log(
2077
+ '3D5 / start addr hi write: ' +
2078
+ h(value) +
2079
+ ' -> ' +
2080
+ h(this.start_address, 4),
2081
+ LOG_VGA,
2082
+ )
2083
+ break
2084
+ case 0xd:
2085
+ if ((this.start_address & 0xff) !== value) {
2086
+ this.start_address = (this.start_address & 0xff00) | value
2087
+ this.update_layers()
2088
+ if (~this.crtc_mode & 0x3) {
2089
+ // Address substitution implementation depends on the
2090
+ // starting row and column, so the pixel buffer is invalidated.
2091
+ this.complete_replot()
2092
+ }
2093
+ }
2094
+ dbg_log(
2095
+ '3D5 / start addr lo write: ' +
2096
+ h(value) +
2097
+ ' -> ' +
2098
+ h(this.start_address, 4),
2099
+ LOG_VGA,
2100
+ )
2101
+ break
2102
+ case 0xe:
2103
+ dbg_log('3D5 / cursor address hi write: ' + h(value), LOG_VGA)
2104
+ this.cursor_address =
2105
+ (this.cursor_address & 0xff) | (value << 8)
2106
+ this.update_cursor()
2107
+ break
2108
+ case 0xf:
2109
+ dbg_log('3D5 / cursor address lo write: ' + h(value), LOG_VGA)
2110
+ this.cursor_address = (this.cursor_address & 0xff00) | value
2111
+ this.update_cursor()
2112
+ break
2113
+ case 0x12:
2114
+ dbg_log('3D5 / vdisp enable end write: ' + h(value), LOG_VGA)
2115
+ if ((this.vertical_display_enable_end & 0xff) !== value) {
2116
+ this.vertical_display_enable_end =
2117
+ (this.vertical_display_enable_end & 0x300) | value
2118
+ this.update_vga_size()
2119
+ }
2120
+ break
2121
+ case 0x13:
2122
+ dbg_log('3D5 / offset register write: ' + h(value), LOG_VGA)
2123
+ if (this.offset_register !== value) {
2124
+ this.offset_register = value
2125
+ this.update_vga_size()
2126
+
2127
+ if (~this.crtc_mode & 0x3) {
2128
+ // Address substitution implementation depends on the
2129
+ // virtual width, so the pixel buffer is invalidated.
2130
+ this.complete_replot()
2131
+ }
2132
+ }
2133
+ break
2134
+ case 0x14:
2135
+ dbg_log('3D5 / underline location write: ' + h(value), LOG_VGA)
2136
+ if (this.underline_location_register !== value) {
2137
+ const previous_underline = this.underline_location_register
2138
+
2139
+ this.underline_location_register = value
2140
+ this.update_vga_size()
2141
+
2142
+ if ((previous_underline ^ value) & 0x40) {
2143
+ // Doubleword addressing changed. Pixel buffer invalidated.
2144
+ this.complete_replot()
2145
+ }
2146
+ }
2147
+ break
2148
+ case 0x15:
2149
+ dbg_log(
2150
+ '3D5 / vertical blank start write: ' + h(value),
2151
+ LOG_VGA,
2152
+ )
2153
+ if ((this.vertical_blank_start & 0xff) !== value) {
2154
+ this.vertical_blank_start =
2155
+ (this.vertical_blank_start & 0x300) | value
2156
+ this.update_vga_size()
2157
+ }
2158
+ break
2159
+ case 0x17:
2160
+ dbg_log('3D5 / crtc mode write: ' + h(value), LOG_VGA)
2161
+ if (this.crtc_mode !== value) {
2162
+ const previous_mode = this.crtc_mode
2163
+
2164
+ this.crtc_mode = value
2165
+ this.update_vga_size()
2166
+
2167
+ if ((previous_mode ^ value) & 0x43) {
2168
+ // Word/byte addressing changed or address substitution changed.
2169
+ // Pixel buffer invalidated.
2170
+ this.complete_replot()
2171
+ }
2172
+ }
2173
+ break
2174
+ case 0x18:
2175
+ dbg_log('3D5 / line compare write: ' + h(value), LOG_VGA)
2176
+ this.line_compare = (this.line_compare & 0x300) | value
2177
+ this.update_layers()
2178
+ break
2179
+ default:
2180
+ if (this.index_crtc < this.crtc.length) {
2181
+ this.crtc[this.index_crtc] = value
2182
+ }
2183
+ dbg_log(
2184
+ '3D5 / CRTC write ' + h(this.index_crtc) + ': ' + h(value),
2185
+ LOG_VGA,
2186
+ )
2187
+ }
2188
+ }
2189
+
2190
+ port3D5_write16(register: number): void {
2191
+ dbg_log('16-bit write to 3D5: ' + h(register, 4), LOG_VGA)
2192
+ this.port3D5_write(register & 0xff)
2193
+ }
2194
+
2195
+ port3D5_read(): number {
2196
+ dbg_log('3D5 read ' + h(this.index_crtc), LOG_VGA)
2197
+
2198
+ switch (this.index_crtc) {
2199
+ case 0x1:
2200
+ return this.horizontal_display_enable_end
2201
+ case 0x2:
2202
+ return this.horizontal_blank_start
2203
+ case 0x7:
2204
+ return (
2205
+ ((this.vertical_display_enable_end >> 7) & 0x2) |
2206
+ ((this.vertical_blank_start >> 5) & 0x8) |
2207
+ ((this.line_compare >> 4) & 0x10) |
2208
+ ((this.vertical_display_enable_end >> 3) & 0x40)
2209
+ )
2210
+ case 0x8:
2211
+ return this.preset_row_scan
2212
+ case 0x9:
2213
+ return this.max_scan_line
2214
+ case 0xa:
2215
+ return this.cursor_scanline_start
2216
+ case 0xb:
2217
+ return this.cursor_scanline_end
2218
+ case 0xc:
2219
+ return this.start_address & 0xff
2220
+ case 0xd:
2221
+ return this.start_address >> 8
2222
+ case 0xe:
2223
+ return this.cursor_address >> 8
2224
+ case 0xf:
2225
+ return this.cursor_address & 0xff
2226
+ case 0x12:
2227
+ return this.vertical_display_enable_end & 0xff
2228
+ case 0x13:
2229
+ return this.offset_register
2230
+ case 0x14:
2231
+ return this.underline_location_register
2232
+ case 0x15:
2233
+ return this.vertical_blank_start & 0xff
2234
+ case 0x17:
2235
+ return this.crtc_mode
2236
+ case 0x18:
2237
+ return this.line_compare & 0xff
2238
+ }
2239
+
2240
+ if (this.index_crtc < this.crtc.length) {
2241
+ return this.crtc[this.index_crtc]
2242
+ } else {
2243
+ return 0
2244
+ }
2245
+ }
2246
+
2247
+ port3D5_read16(): number {
2248
+ dbg_log('Warning: 16-bit read from 3D5', LOG_VGA)
2249
+ return this.port3D5_read()
2250
+ }
2251
+
2252
+ port3DA_read(): number {
2253
+ dbg_log('3DA read - status 1 and clear attr index', LOG_VGA)
2254
+
2255
+ const value = this.port_3DA_value
2256
+
2257
+ // Status register, bit 3 set by update_vertical_retrace
2258
+ // during screen-fill-buffer
2259
+ if (!this.graphical_mode) {
2260
+ // But screen-fill-buffer may not get triggered in text mode
2261
+ // so toggle it manually here
2262
+ if (this.port_3DA_value & 1) {
2263
+ this.port_3DA_value ^= 8
2264
+ }
2265
+ this.port_3DA_value ^= 1
2266
+ } else {
2267
+ this.port_3DA_value ^= 1
2268
+ this.port_3DA_value &= 1
2269
+ }
2270
+ this.attribute_controller_index = -1
2271
+ return value
2272
+ }
2273
+
2274
+ port1CE_write(value: number): void {
2275
+ this.dispi_index = value
2276
+ }
2277
+
2278
+ port1CF_write(value: number): void {
2279
+ dbg_log(
2280
+ '1CF / dispi write ' + h(this.dispi_index) + ': ' + h(value),
2281
+ LOG_VGA,
2282
+ )
2283
+
2284
+ const was_enabled = this.svga_enabled
2285
+
2286
+ switch (this.dispi_index) {
2287
+ case 0:
2288
+ if (value >= 0xb0c0 && value <= 0xb0c5) {
2289
+ this.svga_version = value
2290
+ } else {
2291
+ dbg_log('Invalid version value: ' + h(value), LOG_VGA)
2292
+ }
2293
+ break
2294
+ case 1:
2295
+ this.svga_width = value
2296
+ if (this.svga_width > MAX_XRES) {
2297
+ dbg_log(
2298
+ 'svga_width reduced from ' +
2299
+ this.svga_width +
2300
+ ' to ' +
2301
+ MAX_XRES,
2302
+ LOG_VGA,
2303
+ )
2304
+ this.svga_width = MAX_XRES
2305
+ }
2306
+ break
2307
+ case 2:
2308
+ this.svga_height = value
2309
+ if (this.svga_height > MAX_YRES) {
2310
+ dbg_log(
2311
+ 'svga_height reduced from ' +
2312
+ this.svga_height +
2313
+ ' to ' +
2314
+ MAX_YRES,
2315
+ LOG_VGA,
2316
+ )
2317
+ this.svga_height = MAX_YRES
2318
+ }
2319
+ break
2320
+ case 3:
2321
+ this.svga_bpp = value
2322
+ break
2323
+ case 4:
2324
+ // enable, options
2325
+ this.svga_enabled = (value & 1) === 1
2326
+ if (this.svga_enabled && (value & 0x80) === 0) {
2327
+ this.svga_memory.fill(0)
2328
+ }
2329
+ this.dispi_enable_value = value
2330
+ break
2331
+ case 5:
2332
+ dbg_log('SVGA bank offset: ' + h(value << 16), LOG_VGA)
2333
+ this.svga_bank_offset = value << 16
2334
+ break
2335
+ case 8:
2336
+ // x offset
2337
+ dbg_log('SVGA X offset: ' + h(value), LOG_VGA)
2338
+ if (this.svga_offset_x !== value) {
2339
+ this.svga_offset_x = value
2340
+ this.svga_offset =
2341
+ this.svga_offset_y * this.svga_width +
2342
+ this.svga_offset_x
2343
+ this.complete_redraw()
2344
+ }
2345
+ break
2346
+ case 9:
2347
+ // y offset
2348
+ dbg_log(
2349
+ 'SVGA Y offset: ' +
2350
+ h(value * this.svga_width) +
2351
+ ' y=' +
2352
+ h(value),
2353
+ LOG_VGA,
2354
+ )
2355
+ if (this.svga_offset_y !== value) {
2356
+ this.svga_offset_y = value
2357
+ this.svga_offset =
2358
+ this.svga_offset_y * this.svga_width +
2359
+ this.svga_offset_x
2360
+ this.complete_redraw()
2361
+ }
2362
+ break
2363
+ default:
2364
+ dbg_log(
2365
+ 'Unimplemented dispi write index: ' + h(this.dispi_index),
2366
+ LOG_VGA,
2367
+ )
2368
+ }
2369
+
2370
+ if (this.svga_enabled && (!this.svga_width || !this.svga_height)) {
2371
+ dbg_log(
2372
+ 'SVGA: disabled because of invalid width/height: ' +
2373
+ this.svga_width +
2374
+ 'x' +
2375
+ this.svga_height,
2376
+ LOG_VGA,
2377
+ )
2378
+ this.svga_enabled = false
2379
+ }
2380
+
2381
+ dbg_assert(this.svga_bpp !== 4, 'unimplemented svga bpp: 4')
2382
+ dbg_assert(
2383
+ this.svga_bpp === 4 ||
2384
+ this.svga_bpp === 8 ||
2385
+ this.svga_bpp === 15 ||
2386
+ this.svga_bpp === 16 ||
2387
+ this.svga_bpp === 24 ||
2388
+ this.svga_bpp === 32,
2389
+ 'unexpected svga bpp: ' + this.svga_bpp,
2390
+ )
2391
+
2392
+ if (this.svga_enabled) {
2393
+ dbg_log(
2394
+ 'SVGA: enabled, ' +
2395
+ this.svga_width +
2396
+ 'x' +
2397
+ this.svga_height +
2398
+ 'x' +
2399
+ this.svga_bpp,
2400
+ LOG_VGA,
2401
+ )
2402
+ } else {
2403
+ dbg_log('SVGA: disabled', LOG_VGA)
2404
+ }
2405
+
2406
+ if (this.svga_enabled && !was_enabled) {
2407
+ this.svga_offset = 0
2408
+ this.svga_offset_x = 0
2409
+ this.svga_offset_y = 0
2410
+
2411
+ this.graphical_mode = true
2412
+ this.screen.set_mode(this.graphical_mode)
2413
+ this.set_size_graphical(
2414
+ this.svga_width,
2415
+ this.svga_height,
2416
+ this.svga_width,
2417
+ this.svga_height,
2418
+ this.svga_bpp,
2419
+ )
2420
+ }
2421
+
2422
+ if (was_enabled && !this.svga_enabled) {
2423
+ const is_graphical = (this.attribute_mode & 0x1) !== 0
2424
+ this.graphical_mode = is_graphical
2425
+ this.screen.set_mode(is_graphical)
2426
+ this.update_vga_size()
2427
+ this.set_font_bitmap(false)
2428
+ this.complete_redraw()
2429
+ }
2430
+
2431
+ if (!this.svga_enabled) {
2432
+ this.svga_bank_offset = 0
2433
+ }
2434
+
2435
+ this.update_layers()
2436
+ }
2437
+
2438
+ port1CF_read(): number {
2439
+ dbg_log('1CF / dispi read ' + h(this.dispi_index), LOG_VGA)
2440
+ return this.svga_register_read(this.dispi_index)
2441
+ }
2442
+
2443
+ svga_register_read(n: number): number {
2444
+ switch (n) {
2445
+ case 0:
2446
+ return this.svga_version
2447
+ case 1:
2448
+ return this.dispi_enable_value & 2 ? MAX_XRES : this.svga_width
2449
+ case 2:
2450
+ return this.dispi_enable_value & 2 ? MAX_YRES : this.svga_height
2451
+ case 3:
2452
+ return this.dispi_enable_value & 2 ? MAX_BPP : this.svga_bpp
2453
+ case 4:
2454
+ return this.dispi_enable_value
2455
+ case 5:
2456
+ return this.svga_bank_offset >>> 16
2457
+ case 6:
2458
+ // virtual width
2459
+ if (this.screen_width) {
2460
+ return this.screen_width
2461
+ } else {
2462
+ return 1 // seabios/windows98 divide exception
2463
+ }
2464
+
2465
+ case 8:
2466
+ // x offset
2467
+ return this.svga_offset_x
2468
+ case 9:
2469
+ return this.svga_offset_y
2470
+ case 0x0a:
2471
+ // memory size in 64 kilobyte banks
2472
+ return (this.vga_memory_size / VGA_BANK_SIZE) | 0
2473
+ default:
2474
+ dbg_log(
2475
+ 'Unimplemented dispi read index: ' + h(this.dispi_index),
2476
+ LOG_VGA,
2477
+ )
2478
+ }
2479
+
2480
+ return 0xff
2481
+ }
2482
+
2483
+ // Transfers graphics from VGA Planes to the Pixel Buffer
2484
+ // VGA Planes represent data stored on actual hardware.
2485
+ // Pixel Buffer caches the 4-bit or 8-bit color indices for each pixel.
2486
+ vga_replot(): void {
2487
+ // Round to multiple of 8 towards extreme
2488
+ const start = this.diff_plot_min & ~0xf
2489
+ const end = Math.min(
2490
+ this.diff_plot_max | 0xf,
2491
+ VGA_PIXEL_BUFFER_SIZE - 1,
2492
+ )
2493
+
2494
+ const addr_shift = this.vga_addr_shift_count()
2495
+ const addr_substitution = ~this.crtc_mode & 0x3
2496
+
2497
+ const shift_mode = this.planar_mode & 0x60
2498
+ const pel_width = this.attribute_mode & 0x40
2499
+
2500
+ for (let pixel_addr = start; pixel_addr <= end; ) {
2501
+ let addr = pixel_addr >>> addr_shift
2502
+ if (addr_substitution) {
2503
+ let row = (pixel_addr / this.virtual_width) | 0
2504
+ const col = pixel_addr - this.virtual_width * row
2505
+
2506
+ switch (addr_substitution) {
2507
+ case 0x1:
2508
+ // Alternating rows using bit 13
2509
+ // Assumes max scan line = 1
2510
+ addr = (row & 0x1) << 13
2511
+ row >>>= 1
2512
+ break
2513
+ case 0x2:
2514
+ // Alternating rows using bit 14
2515
+ // Assumes max scan line = 3
2516
+ addr = (row & 0x1) << 14
2517
+ row >>>= 1
2518
+ break
2519
+ case 0x3:
2520
+ // Cycling through rows using bit 13 and 14
2521
+ // Assumes max scan line = 3
2522
+ addr = (row & 0x3) << 13
2523
+ row >>>= 2
2524
+ break
2525
+ }
2526
+
2527
+ addr |=
2528
+ ((row * this.virtual_width + col) >>> addr_shift) +
2529
+ this.start_address
2530
+ }
2531
+
2532
+ let byte0 = this.plane0[addr]
2533
+ let byte1 = this.plane1[addr]
2534
+ let byte2 = this.plane2[addr]
2535
+ let byte3 = this.plane3[addr]
2536
+
2537
+ const shift_loads = new Uint8Array(8)
2538
+ switch (shift_mode) {
2539
+ // Planar Shift Mode
2540
+ // See http://www.osdever.net/FreeVGA/vga/vgaseq.htm
2541
+ case 0x00:
2542
+ // Shift these, so that the bits for the color are in
2543
+ // the correct position in the for loop
2544
+ byte0 <<= 0
2545
+ byte1 <<= 1
2546
+ byte2 <<= 2
2547
+ byte3 <<= 3
2548
+
2549
+ for (let i = 7; i >= 0; i--) {
2550
+ shift_loads[7 - i] =
2551
+ ((byte0 >> i) & 1) |
2552
+ ((byte1 >> i) & 2) |
2553
+ ((byte2 >> i) & 4) |
2554
+ ((byte3 >> i) & 8)
2555
+ }
2556
+ break
2557
+
2558
+ // Packed Shift Mode, aka Interleaved Shift Mode
2559
+ // Video Modes 4h and 5h
2560
+ case 0x20:
2561
+ shift_loads[0] = ((byte0 >> 6) & 0x3) | ((byte2 >> 4) & 0xc)
2562
+ shift_loads[1] = ((byte0 >> 4) & 0x3) | ((byte2 >> 2) & 0xc)
2563
+ shift_loads[2] = ((byte0 >> 2) & 0x3) | ((byte2 >> 0) & 0xc)
2564
+ shift_loads[3] = ((byte0 >> 0) & 0x3) | ((byte2 << 2) & 0xc)
2565
+
2566
+ shift_loads[4] = ((byte1 >> 6) & 0x3) | ((byte3 >> 4) & 0xc)
2567
+ shift_loads[5] = ((byte1 >> 4) & 0x3) | ((byte3 >> 2) & 0xc)
2568
+ shift_loads[6] = ((byte1 >> 2) & 0x3) | ((byte3 >> 0) & 0xc)
2569
+ shift_loads[7] = ((byte1 >> 0) & 0x3) | ((byte3 << 2) & 0xc)
2570
+ break
2571
+
2572
+ // 256-Color Shift Mode
2573
+ // Video Modes 13h and unchained 256 color
2574
+ case 0x40:
2575
+ case 0x60:
2576
+ shift_loads[0] = (byte0 >> 4) & 0xf
2577
+ shift_loads[1] = (byte0 >> 0) & 0xf
2578
+ shift_loads[2] = (byte1 >> 4) & 0xf
2579
+ shift_loads[3] = (byte1 >> 0) & 0xf
2580
+ shift_loads[4] = (byte2 >> 4) & 0xf
2581
+ shift_loads[5] = (byte2 >> 0) & 0xf
2582
+ shift_loads[6] = (byte3 >> 4) & 0xf
2583
+ shift_loads[7] = (byte3 >> 0) & 0xf
2584
+ break
2585
+ }
2586
+
2587
+ if (pel_width) {
2588
+ // Assemble from two sets of 4 bits.
2589
+ for (let i = 0, j = 0; i < 4; i++, pixel_addr++, j += 2) {
2590
+ this.pixel_buffer[pixel_addr] =
2591
+ (shift_loads[j] << 4) | shift_loads[j + 1]
2592
+ }
2593
+ } else {
2594
+ for (let i = 0; i < 8; i++, pixel_addr++) {
2595
+ this.pixel_buffer[pixel_addr] = shift_loads[i]
2596
+ }
2597
+ }
2598
+ }
2599
+ }
2600
+
2601
+ // Transfers graphics from Pixel Buffer to Destination Image Buffer.
2602
+ // The 4-bit/8-bit color indices in the Pixel Buffer are passed through
2603
+ // the internal palette (dac_map) and the DAC palette (vga256_palette) to
2604
+ // obtain the final 32 bit color that the Canvas API uses.
2605
+ vga_redraw(): void {
2606
+ const start = this.diff_addr_min
2607
+ const end = Math.min(this.diff_addr_max, VGA_PIXEL_BUFFER_SIZE - 1)
2608
+ const buffer = new Int32Array(
2609
+ this.cpu.wasm_memory.buffer,
2610
+ this.dest_buffet_offset,
2611
+ this.virtual_width * this.virtual_height,
2612
+ )
2613
+
2614
+ let mask = 0xff
2615
+ let colorset = 0x00
2616
+ if (this.attribute_mode & 0x80) {
2617
+ // Palette bits 5/4 select
2618
+ mask &= 0xcf
2619
+ colorset |= (this.color_select << 4) & 0x30
2620
+ }
2621
+
2622
+ if (this.attribute_mode & 0x40) {
2623
+ // 8 bit mode
2624
+
2625
+ for (let pixel_addr = start; pixel_addr <= end; pixel_addr++) {
2626
+ const color256 =
2627
+ (this.pixel_buffer[pixel_addr] & mask) | colorset
2628
+ const color = this.vga256_palette[color256]
2629
+
2630
+ buffer[pixel_addr] =
2631
+ (color & 0xff00) |
2632
+ (color << 16) |
2633
+ (color >> 16) |
2634
+ 0xff000000
2635
+ }
2636
+ } else {
2637
+ // 4 bit mode
2638
+
2639
+ // Palette bits 7/6 select
2640
+ mask &= 0x3f
2641
+ colorset |= (this.color_select << 4) & 0xc0
2642
+
2643
+ for (let pixel_addr = start; pixel_addr <= end; pixel_addr++) {
2644
+ const color16 =
2645
+ this.pixel_buffer[pixel_addr] & this.color_plane_enable
2646
+ const color256 = (this.dac_map[color16] & mask) | colorset
2647
+ const color = this.vga256_palette[color256]
2648
+
2649
+ buffer[pixel_addr] =
2650
+ (color & 0xff00) |
2651
+ (color << 16) |
2652
+ (color >> 16) |
2653
+ 0xff000000
2654
+ }
2655
+ }
2656
+ }
2657
+
2658
+ screen_fill_buffer(): void {
2659
+ if (!this.graphical_mode) {
2660
+ // text mode
2661
+ // Update retrace behaviour anyway - programs waiting for signal before
2662
+ // changing to graphical mode
2663
+ this.update_vertical_retrace()
2664
+ return
2665
+ }
2666
+
2667
+ if (this.image_data!.data.byteLength === 0) {
2668
+ // wasm memory resized
2669
+ const buffer = new Uint8ClampedArray(
2670
+ this.cpu.wasm_memory.buffer,
2671
+ this.dest_buffet_offset,
2672
+ 4 * this.virtual_width * this.virtual_height,
2673
+ )
2674
+ this.image_data = new ImageData(
2675
+ buffer,
2676
+ this.virtual_width,
2677
+ this.virtual_height,
2678
+ )
2679
+ this.update_layers()
2680
+ }
2681
+
2682
+ if (this.svga_enabled) {
2683
+ let min_y = 0
2684
+ let max_y = this.svga_height
2685
+
2686
+ if (this.svga_bpp === 8) {
2687
+ // XXX: Slow, should be ported to rust, but it doesn't have access to vga256_palette
2688
+ // XXX: Doesn't take svga_offset into account
2689
+ const buffer = new Int32Array(
2690
+ this.cpu.wasm_memory.buffer,
2691
+ this.dest_buffet_offset,
2692
+ this.screen_width * this.screen_height,
2693
+ )
2694
+ const svga_memory = new Uint8Array(
2695
+ this.cpu.wasm_memory.buffer,
2696
+ this.svga_memory.byteOffset,
2697
+ this.vga_memory_size,
2698
+ )
2699
+
2700
+ for (let i = 0; i < buffer.length; i++) {
2701
+ const color = this.vga256_palette[svga_memory[i]]
2702
+ buffer[i] =
2703
+ (color & 0xff00) |
2704
+ (color << 16) |
2705
+ (color >> 16) |
2706
+ 0xff000000
2707
+ }
2708
+ } else {
2709
+ this.cpu.svga_fill_pixel_buffer(this.svga_bpp, this.svga_offset)
2710
+
2711
+ const bytes_per_pixel =
2712
+ this.svga_bpp === 15 ? 2 : this.svga_bpp / 8
2713
+ min_y =
2714
+ ((((this.cpu.svga_dirty_bitmap_min_offset[0] /
2715
+ bytes_per_pixel) |
2716
+ 0) -
2717
+ this.svga_offset) /
2718
+ this.svga_width) |
2719
+ 0
2720
+ max_y =
2721
+ (((((this.cpu.svga_dirty_bitmap_max_offset[0] /
2722
+ bytes_per_pixel) |
2723
+ 0) -
2724
+ this.svga_offset) /
2725
+ this.svga_width) |
2726
+ 0) +
2727
+ 1
2728
+ }
2729
+
2730
+ if (min_y < max_y) {
2731
+ min_y = Math.max(min_y, 0)
2732
+ max_y = Math.min(max_y, this.svga_height)
2733
+
2734
+ this.screen.update_buffer([
2735
+ {
2736
+ image_data: this.image_data,
2737
+ screen_x: 0,
2738
+ screen_y: min_y,
2739
+ buffer_x: 0,
2740
+ buffer_y: min_y,
2741
+ buffer_width: this.svga_width,
2742
+ buffer_height: max_y - min_y,
2743
+ },
2744
+ ])
2745
+ }
2746
+ } else {
2747
+ this.vga_replot()
2748
+ this.vga_redraw()
2749
+ this.screen.update_buffer(this.layers)
2750
+ }
2751
+
2752
+ this.reset_diffs()
2753
+ this.update_vertical_retrace()
2754
+ }
2755
+
2756
+ set_font_bitmap(font_plane_dirty: boolean): void {
2757
+ const height = this.max_scan_line & 0x1f
2758
+ if (height && !this.graphical_mode) {
2759
+ const width_dbl = !!(this.clocking_mode & 0x08)
2760
+ const width_9px = !width_dbl && !(this.clocking_mode & 0x01)
2761
+ const copy_8th_col = !!(this.attribute_mode & 0x04)
2762
+ this.screen.set_font_bitmap(
2763
+ height + 1, // int height, font height 1..32px
2764
+ width_9px, // bool width_9px, True: font width 9px, else 8px
2765
+ width_dbl, // bool width_dbl, True: font width 16px (overrides width_9px)
2766
+ copy_8th_col, // bool copy_8th_col, True: duplicate 8th into 9th column in ASCII chars 0xC0-0xDF
2767
+ this.plane2, // Uint8Array font_bitmap[64k], static
2768
+ font_plane_dirty, // bool bitmap_changed, True: content of this.plane2 has changed
2769
+ )
2770
+ }
2771
+ }
2772
+
2773
+ set_font_page(): void {
2774
+ // bits 2, 3 and 5 (LSB to MSB): VGA font page index of font A
2775
+ // bits 0, 1 and 4: VGA font page index of font B
2776
+ // linear_index_map[] maps VGA's non-liner font page index to linear index
2777
+ const linear_index_map = [0, 2, 4, 6, 1, 3, 5, 7]
2778
+ const vga_index_A =
2779
+ ((this.character_map_select & 0b1100) >> 2) |
2780
+ ((this.character_map_select & 0b100000) >> 3)
2781
+ const vga_index_B =
2782
+ (this.character_map_select & 0b11) |
2783
+ ((this.character_map_select & 0b10000) >> 2)
2784
+ this.font_page_ab_enabled = vga_index_A !== vga_index_B
2785
+ this.screen.set_font_page(
2786
+ linear_index_map[vga_index_A],
2787
+ linear_index_map[vga_index_B],
2788
+ )
2789
+ this.complete_redraw()
2790
+ }
2791
+ }