@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.
- package/LICENSE +22 -0
- package/LICENSE.MIT +22 -0
- package/Readme.md +237 -0
- package/dist/v86.browser.js +26666 -0
- package/dist/v86.browser.js.map +7 -0
- package/dist/v86.js +26632 -0
- package/dist/v86.js.map +7 -0
- package/gen/generate_analyzer.ts +512 -0
- package/gen/generate_interpreter.ts +522 -0
- package/gen/generate_jit.ts +624 -0
- package/gen/rust_ast.ts +107 -0
- package/gen/util.ts +35 -0
- package/gen/x86_table.ts +1836 -0
- package/lib/9p.ts +1547 -0
- package/lib/filesystem.ts +1879 -0
- package/lib/marshall.ts +168 -0
- package/lib/softfloat/softfloat.c +32501 -0
- package/lib/zstd/zstddeclib.c +13520 -0
- package/package.json +75 -0
- package/src/acpi.ts +267 -0
- package/src/browser/dummy_screen.ts +106 -0
- package/src/browser/fake_network.ts +1771 -0
- package/src/browser/fetch_network.ts +361 -0
- package/src/browser/filestorage.ts +124 -0
- package/src/browser/inbrowser_network.ts +57 -0
- package/src/browser/keyboard.ts +564 -0
- package/src/browser/main.ts +3415 -0
- package/src/browser/mouse.ts +255 -0
- package/src/browser/network.ts +142 -0
- package/src/browser/print_stats.ts +336 -0
- package/src/browser/screen.ts +978 -0
- package/src/browser/serial.ts +316 -0
- package/src/browser/speaker.ts +1223 -0
- package/src/browser/starter.ts +1688 -0
- package/src/browser/wisp_network.ts +332 -0
- package/src/browser/worker_bus.ts +64 -0
- package/src/buffer.ts +652 -0
- package/src/bus.ts +78 -0
- package/src/const.ts +128 -0
- package/src/cpu.ts +2891 -0
- package/src/dma.ts +474 -0
- package/src/elf.ts +251 -0
- package/src/floppy.ts +1778 -0
- package/src/ide.ts +3455 -0
- package/src/io.ts +504 -0
- package/src/iso9660.ts +317 -0
- package/src/kernel.ts +250 -0
- package/src/lib.ts +645 -0
- package/src/log.ts +149 -0
- package/src/main.ts +199 -0
- package/src/ne2k.ts +1589 -0
- package/src/pci.ts +815 -0
- package/src/pit.ts +406 -0
- package/src/ps2.ts +820 -0
- package/src/rtc.ts +537 -0
- package/src/rust/analysis.rs +101 -0
- package/src/rust/codegen.rs +2660 -0
- package/src/rust/config.rs +3 -0
- package/src/rust/control_flow.rs +425 -0
- package/src/rust/cpu/apic.rs +658 -0
- package/src/rust/cpu/arith.rs +1207 -0
- package/src/rust/cpu/call_indirect.rs +2 -0
- package/src/rust/cpu/cpu.rs +4501 -0
- package/src/rust/cpu/fpu.rs +923 -0
- package/src/rust/cpu/global_pointers.rs +112 -0
- package/src/rust/cpu/instructions.rs +2486 -0
- package/src/rust/cpu/instructions_0f.rs +5261 -0
- package/src/rust/cpu/ioapic.rs +316 -0
- package/src/rust/cpu/memory.rs +351 -0
- package/src/rust/cpu/misc_instr.rs +613 -0
- package/src/rust/cpu/mod.rs +16 -0
- package/src/rust/cpu/modrm.rs +133 -0
- package/src/rust/cpu/pic.rs +402 -0
- package/src/rust/cpu/sse_instr.rs +361 -0
- package/src/rust/cpu/string.rs +701 -0
- package/src/rust/cpu/vga.rs +175 -0
- package/src/rust/cpu_context.rs +69 -0
- package/src/rust/dbg.rs +98 -0
- package/src/rust/gen/analyzer.rs +3807 -0
- package/src/rust/gen/analyzer0f.rs +3992 -0
- package/src/rust/gen/interpreter.rs +4447 -0
- package/src/rust/gen/interpreter0f.rs +5404 -0
- package/src/rust/gen/jit.rs +5080 -0
- package/src/rust/gen/jit0f.rs +5547 -0
- package/src/rust/gen/mod.rs +14 -0
- package/src/rust/jit.rs +2443 -0
- package/src/rust/jit_instructions.rs +7881 -0
- package/src/rust/js_api.rs +6 -0
- package/src/rust/leb.rs +46 -0
- package/src/rust/lib.rs +29 -0
- package/src/rust/modrm.rs +330 -0
- package/src/rust/opstats.rs +249 -0
- package/src/rust/page.rs +15 -0
- package/src/rust/paging.rs +25 -0
- package/src/rust/prefix.rs +15 -0
- package/src/rust/profiler.rs +155 -0
- package/src/rust/regs.rs +38 -0
- package/src/rust/softfloat.rs +286 -0
- package/src/rust/state_flags.rs +27 -0
- package/src/rust/wasmgen/mod.rs +2 -0
- package/src/rust/wasmgen/wasm_builder.rs +1047 -0
- package/src/rust/wasmgen/wasm_opcodes.rs +221 -0
- package/src/rust/zstd.rs +105 -0
- package/src/sb16.ts +1928 -0
- package/src/state.ts +359 -0
- package/src/uart.ts +472 -0
- package/src/vga.ts +2791 -0
- package/src/virtio.ts +1756 -0
- package/src/virtio_balloon.ts +273 -0
- package/src/virtio_console.ts +372 -0
- 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
|
+
}
|