diff --git a/Makefile b/Makefile index bba402d2a..892b1fe07 100644 --- a/Makefile +++ b/Makefile @@ -79,7 +79,7 @@ CARGO_FLAGS_SAFE=\ CARGO_FLAGS=$(CARGO_FLAGS_SAFE) -C target-feature=+bulk-memory -C target-feature=+multivalue -C target-feature=+simd128 CORE_FILES=const.js config.js io.js main.js lib.js buffer.js ide.js pci.js floppy.js \ - memory.js dma.js pit.js vga.js vga_text.js ps2.js rtc.js uart.js \ + memory.js dma.js pit.js vga.js ps2.js rtc.js uart.js \ acpi.js apic.js ioapic.js \ state.js ne2k.js sb16.js virtio.js virtio_console.js virtio_net.js \ bus.js log.js cpu.js debug.js \ diff --git a/debug.html b/debug.html index 57594cb86..a4e2d20c1 100644 --- a/debug.html +++ b/debug.html @@ -20,7 +20,6 @@ - diff --git a/src/browser/main.js b/src/browser/main.js index bdabbb225..d48b9056b 100644 --- a/src/browser/main.js +++ b/src/browser/main.js @@ -1694,10 +1694,7 @@ } const emulator = new V86({ - screen: { - container: $("screen_container"), - use_graphical_text: false, - }, + screen_container: $("screen_container"), net_device: { type: settings.net_device_type || "ne2k", relay_url: settings.relay_url, diff --git a/src/browser/screen.js b/src/browser/screen.js index 176ae429e..df8945139 100644 --- a/src/browser/screen.js +++ b/src/browser/screen.js @@ -37,7 +37,7 @@ function ScreenAdapter(options, screen_fill_buffer) changed_rows, // are we in graphical mode now? - is_graphical = !!options.use_graphical_text, + is_graphical = false, // Index 0: ASCII code // Index 1: Blinking @@ -134,19 +134,9 @@ function ScreenAdapter(options, screen_fill_buffer) this.init = function() { - // initialize with mode and size presets as expected by the bios - // to avoid flickering during early startup - this.set_mode(is_graphical); - - if(is_graphical) - { - // assume 80x25 with 9x16 font - this.set_size_graphical(720, 400, 720, 400); - } - else - { - this.set_size_text(80, 25); - } + // not necessary, because this gets initialized by the bios early, + // but nicer to look at + this.set_size_text(80, 25); this.timer(); }; diff --git a/src/browser/starter.js b/src/browser/starter.js index 5813c1fa9..154ae3e4f 100644 --- a/src/browser/starter.js +++ b/src/browser/starter.js @@ -305,7 +305,6 @@ V86.prototype.continue_init = async function(emulator, options) settings.cpuid_level = options.cpuid_level; settings.virtio_console = options.virtio_console; settings.virtio_net = options.virtio_net; - settings.screen_options = options.screen_options; const relay_url = options.network_relay_url || options.net_device && options.net_device.relay_url; if(relay_url) @@ -346,14 +345,13 @@ V86.prototype.continue_init = async function(emulator, options) if(screen_options.container) { - this.screen_adapter = new ScreenAdapter(screen_options, () => this.v86.cpu.devices.vga && this.v86.cpu.devices.vga.screen_fill_buffer()); + this.screen_adapter = new ScreenAdapter(screen_options, () => this.v86.cpu.devices.vga.screen_fill_buffer()); } else { this.screen_adapter = new DummyScreenAdapter(); } settings.screen = this.screen_adapter; - settings.screen_options = screen_options; if(options.serial_container) { diff --git a/src/cpu.js b/src/cpu.js index c1b6210ed..c81759fd4 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -940,7 +940,7 @@ CPU.prototype.init = function(settings, device_bus) this.devices.dma = new DMA(this); - this.devices.vga = new VGAScreen(this, device_bus, settings.screen, settings.vga_memory_size || 8 * 1024 * 1024, settings.screen_options || {}); + this.devices.vga = new VGAScreen(this, device_bus, settings.screen, settings.vga_memory_size || 8 * 1024 * 1024); this.devices.ps2 = new PS2(this, device_bus); diff --git a/src/vga.js b/src/vga.js index 2f34ccb73..5b1e63656 100644 --- a/src/vga.js +++ b/src/vga.js @@ -50,9 +50,8 @@ const VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([ * @param {BusConnector} bus * @param {ScreenAdapter|DummyScreenAdapter} screen * @param {number} vga_memory_size - * @param {Object} options */ -function VGAScreen(cpu, bus, screen, vga_memory_size, options) +function VGAScreen(cpu, bus, screen, vga_memory_size) { this.cpu = cpu; @@ -167,6 +166,7 @@ function VGAScreen(cpu, bus, screen, vga_memory_size, options) /** @type {boolean} */ this.graphical_mode = false; + this.screen.set_mode(this.graphical_mode); /* * VGA palette containing 256 colors for video mode 13, svga 8bpp, etc. @@ -377,41 +377,9 @@ function VGAScreen(cpu, bus, screen, vga_memory_size, options) (addr, value) => this.vga_memory_write(addr, value), ); - if(options.use_graphical_text) - { - this.graphical_text = new GraphicalText(this); - } - cpu.devices.pci.register_device(this); } -VGAScreen.prototype.grab_text_content = function(keep_whitespace) -{ - var addr = this.start_address << 1; - const split_screen_row = this.scan_line_to_screen_row(this.line_compare); - const row_offset = Math.max(0, (this.offset_register * 2 - this.max_cols) * 2); - const text_rows = []; - - for(var row = 0; row < this.max_rows; row++) - { - if(row === split_screen_row) - { - addr = 0; - } - - let line = ""; - for(var col = 0; col < this.max_cols; col++, addr += 2) - { - line += String.fromCodePoint(this.vga_memory[addr]); - } - - text_rows.push(keep_whitespace ? line : line.trimEnd()); - addr += row_offset; - } - - return text_rows; -}; - VGAScreen.prototype.get_state = function() { var state = []; @@ -551,7 +519,7 @@ VGAScreen.prototype.set_state = function(state) this.dac_mask = state[62] === undefined ? 0xFF : state[62]; this.character_map_select = state[63] === undefined ? 0 : state[63]; - this.screen.set_mode(this.graphical_mode || !!this.graphical_text); + this.screen.set_mode(this.graphical_mode); if(this.graphical_mode) { @@ -855,11 +823,6 @@ VGAScreen.prototype.apply_bitmask = function(data_dword, bitmask_dword) VGAScreen.prototype.text_mode_redraw = function() { - if(this.graphical_text) - { - return; - } - const split_screen_row = this.scan_line_to_screen_row(this.line_compare); const row_offset = Math.max(0, (this.offset_register * 2 - this.max_cols) * 2); const blink_flag = this.attribute_mode & 1 << 3; @@ -935,23 +898,15 @@ VGAScreen.prototype.vga_memory_write_text_mode = function(addr, value) chr = value; color = this.vga_memory[addr | 1]; } - const blink_flag = this.attribute_mode & 1 << 3; const blinking = blink_flag && (color & 1 << 7); const bg_color_mask = blink_flag ? 7 : 0xF; this.bus.send("screen-put-char", [row, col, chr]); - if(this.graphical_text) - { - this.graphical_text.invalidate_row(row); - } - else - { - this.screen.put_char(row, col, chr, blinking, - this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & bg_color_mask]], - this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]); - } + this.screen.put_char(row, col, chr, blinking, + this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & bg_color_mask]], + this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]); }; VGAScreen.prototype.update_cursor = function() @@ -972,16 +927,9 @@ VGAScreen.prototype.update_cursor = function() } dbg_assert(row >= 0 && col >= 0); - // NOTE: is allowed to be out of bounds - if(this.graphical_text) - { - this.graphical_text.set_cursor_pos(row, col); - } - else - { - this.screen.update_cursor(row, col); - } + // NOTE: is allowed to be out of bounds + this.screen.update_cursor(row, col); }; VGAScreen.prototype.complete_redraw = function() @@ -1174,16 +1122,8 @@ VGAScreen.prototype.set_size_text = function(cols_count, rows_count) this.max_cols = cols_count; this.max_rows = rows_count; + this.screen.set_size_text(cols_count, rows_count); this.bus.send("screen-set-size", [cols_count, rows_count, 0]); - - if(this.graphical_text) - { - this.graphical_text.set_size(rows_count, cols_count); - } - else - { - this.screen.set_size_text(cols_count, rows_count); - } }; VGAScreen.prototype.set_size_graphical = function(width, height, virtual_width, virtual_height, bpp) @@ -1401,15 +1341,7 @@ VGAScreen.prototype.update_cursor_scanline = function() const start = Math.min(max, this.cursor_scanline_start & 0x1F); const end = Math.min(max, this.cursor_scanline_end & 0x1F); const visible = !disabled && start < end; - - if(this.graphical_text) - { - this.graphical_text.set_cursor_attr(start, end, visible); - } - else - { - this.screen.update_cursor_scanline(start, end, visible); - } + this.screen.update_cursor_scanline(start, end, visible); }; /** @@ -1456,11 +1388,11 @@ VGAScreen.prototype.port3C0_write = function(value) var previous_mode = this.attribute_mode; this.attribute_mode = value; - const is_graphical = (value & 0x1) !== 0; + var is_graphical = (value & 0x1) > 0; if(!this.svga_enabled && this.graphical_mode !== is_graphical) { this.graphical_mode = is_graphical; - this.screen.set_mode(this.graphical_mode || !!this.graphical_text); + this.screen.set_mode(this.graphical_mode); } if((previous_mode ^ value) & 0x40) @@ -1596,7 +1528,6 @@ VGAScreen.prototype.port3C5_write = function(value) if(this.graphical_text && previous_plane_write_bm !== 0xf && (previous_plane_write_bm & 0x4) && !(this.plane_write_bm & 0x4)) { // End of font plane 2 write access (initial value of plane_write_bm assumed to be 0xf) - this.graphical_text.invalidate_font_shape(); } break; case 0x03: @@ -1605,7 +1536,6 @@ VGAScreen.prototype.port3C5_write = function(value) this.character_map_select = value; if(this.graphical_text && previous_character_map_select !== this.character_map_select) { - this.graphical_text.set_character_map(this.character_map_select); } break; case 0x04: @@ -2495,19 +2425,9 @@ VGAScreen.prototype.screen_fill_buffer = function() if(!this.graphical_mode) { // text mode - if(this.graphical_text) - { - const image_data = this.graphical_text.render(); - this.screen.update_buffer([{ - image_data: image_data, - screen_x: 0, - screen_y: 0, - buffer_x: 0, - buffer_y: 0, - buffer_width: image_data.width, - buffer_height: image_data.height - }]); - } + // Update retrace behaviour anyway - programs waiting for signal before + // changing to graphical mode + this.update_vertical_retrace(); return; } diff --git a/src/vga_text.js b/src/vga_text.js deleted file mode 100644 index 5b5949b03..000000000 --- a/src/vga_text.js +++ /dev/null @@ -1,628 +0,0 @@ -/* -vga_text.js - -Renders text to image buffer using VGA fonts and attributes. -*/ -"use strict"; - -/** - * @constructor - * @param {VGAScreen} vga - */ -function GraphicalText(vga) -{ - this.vga = vga; - - /** - * Number of text columns - * @type {number} - */ - this.txt_width = 80; - - /** - * Number of text rows - * @type {number} - */ - this.txt_height = 25; - - /** - * If true then at least one row in txt_row_dirty is marked as modified - * @type{number} - */ - this.txt_dirty = 0; - - /** - * One bool per row, row was modified if its entry is != 0 - */ - this.txt_row_dirty = new Uint8Array(this.txt_height); - - /** - * Font bitmaps in VGA memory were changed if true - * @type{boolean} - */ - this.font_data_dirty = false; - - /** - * Font width in pixel (8, 9 or 16) - * @type {number} - */ - this.font_width = 9; - - /** - * Font height in pixel (0...32) - * @type {number} - */ - this.font_height = 16; - - /** - * Duplicate 8th to 9th column in horizontal line drawing characters if true (Line Graphics Enable) - * @type{boolean} - */ - this.font_lge = false; - - /** - * Flat bitmap of 8 fonts, array of size: 8 * 256 * font_width * font_height - * @type{Uint8ClampedArray} - */ - this.font_bitmap = new Uint8ClampedArray(8 * 256 * this.font_width * this.font_height); - - /** - * True: blink when msb (0x80) of text attribute is set (8 background colors) - * False: msb selects background intensity (16 background colors) - * @type{boolean} - */ - this.font_blink_enabled = false; - - /** - * Active index (0...7) of font A - * @type {number} - */ - this.font_index_A = 0; - - /** - * Active index (0...7) of font B (TODO) - * @type {number} - */ - this.font_index_B = 0; - - /** - * If true then cursor_enabled_latch, cursor_top_latch and cursor_bottom_latch were overwritten since last call to render(). - * @type{boolean} - */ - this.cursor_attr_dirty = false; - - /** - * Latest value for cursor_enabled if cursor_attr_dirty is true - * @type{boolean} - */ - this.cursor_enabled_latch = false; - - /** - * Latest value for cursor_top_latch if cursor_attr_dirty is true - * @type {number} - */ - this.cursor_top_latch = 0; - - /** - * Latest value for cursor_bottom_latch if cursor_attr_dirty is true - * @type {number} - */ - this.cursor_bottom_latch = 0; - - /** - * If true then cursor_row_latch and cursor_col_latch were overwritten since last call to render(). - * @type{boolean} - */ - this.cursor_pos_dirty = false; - - /** - * Latest value for cursor_row if cursor_pos_dirty is true - * @type {number} - */ - this.cursor_row_latch = 0; - - /** - * Latest value for cursor_col if cursor_pos_dirty is true - * @type {number} - */ - this.cursor_col_latch = 0; - - /** - * Emulate cursor if true, else disable cursor - * @type{boolean} - */ - this.cursor_enabled = false; - - /** - * Cursor position's row (0...txt_height-1) - * @type {number} - */ - this.cursor_row = 0; - - /** - * Cursor position's column (0...txt_width-1) - * @type {number} - */ - this.cursor_col = 0; - - /** - * Cursor box's top scanline (0...font_height) - * @type {number} - */ - this.cursor_top = 0; - - /** - * Cursor box's bottom scanline (0...font_height, inclusive) - * @type {number} - */ - this.cursor_bottom = 0; - - /** - * Tracked value of register vga.attribute_mode - * @type {number} - */ - this.vga_attribute_mode = 0; - - /** - * Tracked value of register vga.clocking_mode - * @type {number} - */ - this.vga_clocking_mode = 0; - - /** - * Tracked value of register vga.max_scan_line - * @type {number} - */ - this.vga_max_scan_line = 0; - - /** - * Width of graphics canvas in pixel (txt_width * font_width) - * @type {number} - */ - this.gfx_width = this.txt_width * this.font_width; - - /** - * Height of graphics canvas in pixel (txt_height * font_height) - * @type {number} - */ - this.gfx_height = this.txt_height * this.font_height; - - /** - * Local screen bitmap buffer, array of size: gfx_width * gfx_height * 4 - * @type{Uint8ClampedArray} - */ - this.gfx_data = new Uint8ClampedArray(this.gfx_width * this.gfx_height * 4); - - /** - * Image container of local screen bitmap buffer gfx_data - * @type{ImageData} - */ - this.image_data = new ImageData(this.gfx_data, this.gfx_width, this.gfx_height); - - /** - * Show cursor and blinking text now if true (controlled by framerate counter) - * @type{boolean} - */ - this.blink_visible = false; - - /** - * Frame counter to control blink rate of type Uint32 - * @type {number} - */ - this.frame_count = 0; -} - -GraphicalText.prototype.rebuild_font_bitmap = function(width_9px, width_double) -{ - const font_height = this.font_height; - const font_lge = this.font_lge; - const src_bitmap = this.vga.plane2; - const dst_bitmap = new Uint8ClampedArray(8 * 256 * this.font_width * font_height); - const vga_inc_chr = 32 - font_height; - - let i_dst = 0; - const copy_bit = width_double ? - function(value) - { - dst_bitmap[i_dst++] = value; - dst_bitmap[i_dst++] = value; - } : - function(value) - { - dst_bitmap[i_dst++] = value; - }; - - let i_src = 0; - for(let i_font = 0; i_font < 8; ++i_font) - { - for(let i_chr = 0; i_chr < 256; ++i_chr, i_src += vga_inc_chr) - { - for(let i_line = 0; i_line < font_height; ++i_line) - { - const line_bits = src_bitmap[i_src++]; - for(let i_bit = 0x80; i_bit > 0; i_bit >>= 1) - { - copy_bit(line_bits & i_bit ? 1 : 0); - } - if(width_9px) - { - copy_bit(font_lge && i_chr >= 0xC0 && i_chr <= 0xDF && line_bits & 1 ? 1 : 0); - } - } - } - } - - return dst_bitmap; -}; - -GraphicalText.prototype.resize_canvas = function() -{ - this.txt_dirty = 1; - this.txt_row_dirty.fill(1); -}; - -GraphicalText.prototype.rebuild_image_data = function() -{ - const gfx_size = this.gfx_width * this.gfx_height * 4; - const gfx_data = new Uint8ClampedArray(gfx_size); - for(let i = 3; i < gfx_size; i += 4) - { - gfx_data[i] = 0xff; - } - this.gfx_data = gfx_data; - this.image_data = new ImageData(this.gfx_data, this.gfx_width, this.gfx_height); - this.resize_canvas(); -}; - -GraphicalText.prototype.mark_blinking_rows_dirty = function() -{ - const vga_memory = this.vga.vga_memory; - const txt_row_dirty = this.txt_row_dirty; - const txt_width = this.txt_width; - const txt_height = this.txt_height; - const txt_row_size = txt_width * 2; - const txt_row_step = Math.max(0, (this.vga.offset_register * 2 - txt_width) * 2); - const split_screen_row = this.vga.scan_line_to_screen_row(this.vga.line_compare); - let row, col, txt_i = this.vga.start_address << 1; - - for(row = 0; row < txt_height; ++row, txt_i += txt_row_step) - { - if(row === split_screen_row) - { - txt_i = 0; - } - - if(txt_row_dirty[row]) - { - txt_i += txt_row_size; - continue; - } - - for(col = 0; col < txt_width; ++col, txt_i += 2) - { - if(vga_memory[txt_i | 1] & 0x80) - { - txt_row_dirty[row] = this.txt_dirty = 1; - txt_i += txt_row_size - col * 2; - break; - } - } - } -}; - -GraphicalText.prototype.render_dirty_rows = function() -{ - const vga = this.vga; - const vga_memory = vga.vga_memory; - const txt_width = this.txt_width; - const txt_height = this.txt_height; - const txt_row_dirty = this.txt_row_dirty; - const gfx_data = this.gfx_data; - const font_bitmap = this.font_bitmap; - const font_size = this.font_width * this.font_height; - const font_A_offset = this.font_index_A * 256; - const font_B_offset = this.font_index_B * 256; - const font_AB_enabled = font_A_offset !== font_B_offset; - const font_blink_enabled = this.font_blink_enabled; - //const blink_visible = this.blink_visible; - const blink_visible = true; - const cursor_visible = this.cursor_enabled && blink_visible; - const cursor_top = this.cursor_top; - const cursor_height = this.cursor_bottom - cursor_top + 1; - - const split_screen_row = vga.scan_line_to_screen_row(vga.line_compare); - const bg_color_mask = font_blink_enabled ? 0x7 : 0xF; - const palette = new Int32Array(16); - for(let i = 0; i < 16; ++i) - { - palette[i] = vga.vga256_palette[vga.dac_mask & vga.dac_map[i]]; - } - - const txt_row_size = txt_width * 2; - const txt_row_step = Math.max(0, (vga.offset_register * 2 - txt_width) * 2); - - const gfx_col_size = this.font_width * 4; // column size in gfx_data (tuple of 4 RGBA items) - const gfx_line_size = this.gfx_width * 4; // line size in gfx_data - const gfx_row_size = gfx_line_size * this.font_height; // row size in gfx_data - const gfx_col_step = (this.font_width - this.font_height * this.gfx_width) * 4; // move from end of current column to start of next in gfx_data - const gfx_line_step = (this.gfx_width - this.font_width) * 4; // move forward to start of column's next line in gfx_data - - // int, current cursor linear position in canvas coordinates (top left of row/col) - const cursor_gfx_i = (this.cursor_row * this.gfx_width * this.font_height + this.cursor_col * this.font_width) * 4; - - let txt_i, chr, chr_attr, chr_bg_rgba, chr_fg_rgba, chr_blinking, chr_font_ofs; - let fg, bg, fg_r=0, fg_g=0, fg_b=0, bg_r=0, bg_g=0, bg_b=0; - let gfx_i, gfx_end_y, gfx_end_x, glyph_i; - let draw_cursor, gfx_ic; - let row, col; - - txt_i = vga.start_address << 1; - - for(row = 0; row < txt_height; ++row, txt_i += txt_row_step) - { - if(row === split_screen_row) - { - txt_i = 0; - } - - if(! txt_row_dirty[row]) - { - txt_i += txt_row_size; - continue; - } - - gfx_i = row * gfx_row_size; - - for(col = 0; col < txt_width; ++col, txt_i += 2, gfx_i += gfx_col_step) - { - chr = vga_memory[txt_i]; - chr_attr = vga_memory[txt_i | 1]; - chr_blinking = font_blink_enabled && chr_attr & 0x80; - chr_font_ofs = font_AB_enabled ? (chr_attr & 0x8 ? font_A_offset : font_B_offset) : font_A_offset; - chr_bg_rgba = palette[chr_attr >> 4 & bg_color_mask]; - chr_fg_rgba = palette[chr_attr & 0xF]; - - if(bg !== chr_bg_rgba) - { - bg = chr_bg_rgba; - bg_r = bg >> 16; - bg_g = (bg >> 8) & 0xff; - bg_b = bg & 0xff; - } - - if(chr_blinking && ! blink_visible) - { - if(fg !== bg) { - fg = bg; - fg_r = bg_r; - fg_g = bg_g; - fg_b = bg_b; - } - } - else if(fg !== chr_fg_rgba) - { - fg = chr_fg_rgba; - fg_r = fg >> 16; - fg_g = (fg >> 8) & 0xff; - fg_b = fg & 0xff; - } - - draw_cursor = cursor_visible && cursor_gfx_i === gfx_i; - - glyph_i = (chr_font_ofs + chr) * font_size; - - gfx_end_y = gfx_i + gfx_row_size; - for(; gfx_i < gfx_end_y; gfx_i += gfx_line_step) - { - gfx_end_x = gfx_i + gfx_col_size; - for(; gfx_i < gfx_end_x; gfx_i += 4) - { - if(font_bitmap[glyph_i++]) - { - gfx_data[gfx_i] = fg_r; - gfx_data[gfx_i+1] = fg_g; - gfx_data[gfx_i+2] = fg_b; - } - else - { - gfx_data[gfx_i] = bg_r; - gfx_data[gfx_i+1] = bg_g; - gfx_data[gfx_i+2] = bg_b; - } - } - } - - if(draw_cursor) - { - gfx_ic = cursor_gfx_i + cursor_top * gfx_line_size; - gfx_end_y = gfx_ic + cursor_height * gfx_line_size; - for(; gfx_ic < gfx_end_y; gfx_ic += gfx_line_step) - { - gfx_end_x = gfx_ic + gfx_col_size; - for(; gfx_ic < gfx_end_x; gfx_ic += 4) - { - gfx_data[gfx_ic] = fg_r; - gfx_data[gfx_ic+1] = fg_g; - gfx_data[gfx_ic+2] = fg_b; - } - } - } - } - } -}; - -// -// Public methods -// - -GraphicalText.prototype.mark_dirty = function() -{ - this.txt_row_dirty.fill(1); - this.txt_dirty = 1; -}; - -GraphicalText.prototype.invalidate_row = function(row) -{ - if(row >= 0 && row < this.txt_height) - { - this.txt_row_dirty[row] = this.txt_dirty = 1; - } -}; - -GraphicalText.prototype.invalidate_font_shape = function() -{ - this.font_data_dirty = true; -}; - -GraphicalText.prototype.set_size = function(rows, cols) -{ - if(rows > 0 && rows < 256 && cols > 0 && cols < 256) - { - this.txt_width = cols; - this.txt_height = rows; - - this.gfx_width = this.txt_width * this.font_width; - this.gfx_height = this.txt_height * this.font_height; - - this.txt_row_dirty = new Uint8Array(this.txt_height); - this.vga.screen.set_size_graphical(this.gfx_width, this.gfx_height, this.gfx_width, this.gfx_height); - this.mark_dirty(); - this.rebuild_image_data(); - } -}; - -GraphicalText.prototype.set_character_map = function(char_map_select) -{ - // bits 2, 3 and 5 (LSB to MSB): VGA font page index of font A - // bits 0, 1 and 4: VGA font page index of font B - // linear_index_map[] maps VGA's non-liner font page index to linear index - const linear_index_map = [0, 2, 4, 6, 1, 3, 5, 7]; - const vga_index_A = ((char_map_select & 0b1100) >> 2) | ((char_map_select & 0b100000) >> 3); - const vga_index_B = (char_map_select & 0b11) | ((char_map_select & 0b10000) >> 2); - const font_index_A = linear_index_map[vga_index_A]; - const font_index_B = linear_index_map[vga_index_B]; - - if(this.font_index_A !== font_index_A || this.font_index_B !== font_index_B) - { - this.font_index_A = font_index_A; - this.font_index_B = font_index_B; - this.mark_dirty(); - } -}; - -GraphicalText.prototype.set_cursor_pos = function(row, col) -{ - this.cursor_pos_dirty = true; - this.cursor_row_latch = row; - this.cursor_col_latch = col; -}; - -GraphicalText.prototype.set_cursor_attr = function(start, end, visible) -{ - this.cursor_attr_dirty = true; - this.cursor_enabled_latch = !! visible; - this.cursor_top_latch = start; - this.cursor_bottom_latch = end; -}; - -GraphicalText.prototype.render = function() -{ - // increment Uint32 frame counter - this.frame_count = (this.frame_count + 1) >>> 0; - - // apply changes to font_width, font_height, font_lge, font_bitmap and font_blink_enabled - const curr_clocking_mode = this.vga.clocking_mode & 0b00001001; - const curr_attribute_mode = this.vga.attribute_mode & 0b00001100; - const curr_max_scan_line = this.vga.max_scan_line & 0b10011111; - if(this.font_data_dirty || - this.vga_clocking_mode !== curr_clocking_mode || - this.vga_attribute_mode !== curr_attribute_mode || - this.vga_max_scan_line !== curr_max_scan_line) - { - const width_9px = ! (curr_clocking_mode & 0x01); - const width_double = !! (curr_clocking_mode & 0x08); - const curr_font_width = (width_9px ? 9 : 8) * (width_double ? 2 : 1); - const curr_font_blink_enabled = !! (curr_attribute_mode & 0b00001000); - const curr_font_lge = !! (curr_attribute_mode & 0b00000100); - const curr_font_height = (curr_max_scan_line & 0b00011111) + 1; - - const font_data_changed = this.font_data_dirty || this.font_lge !== curr_font_lge; - const font_size_changed = this.font_width !== curr_font_width || this.font_height !== curr_font_height; - - this.font_data_dirty = false; - this.font_width = curr_font_width; - this.font_height = curr_font_height; - this.font_blink_enabled = curr_font_blink_enabled; - this.font_lge = curr_font_lge; - - this.vga_clocking_mode = curr_clocking_mode; - this.vga_attribute_mode = curr_attribute_mode; - this.vga_max_scan_line = curr_max_scan_line; - - if(font_data_changed || font_size_changed) - { - if(font_size_changed) - { - this.gfx_width = this.txt_width * this.font_width; - this.gfx_height = this.txt_height * this.font_height; - this.rebuild_image_data(); - } - this.font_bitmap = this.rebuild_font_bitmap(width_9px, width_double); - } - this.mark_dirty(); - } - - // apply changes to cursor position - if(this.cursor_pos_dirty) - { - this.cursor_pos_dirty = false; - this.cursor_row_latch = Math.min(this.cursor_row_latch, this.txt_height-1); - this.cursor_col_latch = Math.min(this.cursor_col_latch, this.txt_width-1); - if(this.cursor_row !== this.cursor_row_latch || this.cursor_col !== this.cursor_col_latch) - { - this.txt_row_dirty[this.cursor_row] = this.txt_row_dirty[this.cursor_row_latch] = this.txt_dirty = 1; - this.cursor_row = this.cursor_row_latch; - this.cursor_col = this.cursor_col_latch; - } - } - - // apply changes to cursor_enabled, cursor_top and cursor_bottom - if(this.cursor_attr_dirty) - { - this.cursor_attr_dirty = false; - if(this.cursor_enabled !== this.cursor_enabled_latch || - this.cursor_top !== this.cursor_top_latch || - this.cursor_bottom !== this.cursor_bottom_latch) - { - this.cursor_enabled = this.cursor_enabled_latch; - this.cursor_top = this.cursor_top_latch; - this.cursor_bottom = this.cursor_bottom_latch; - this.txt_row_dirty[this.cursor_row] = this.txt_dirty = 1; - } - } - - // toggle cursor and blinking character visibility at a frequency of ~3.75hz (every 16th frame at 60fps) - // TODO: make framerate independant - //if(this.frame_count % 16 === 0) - //{ - // this.blink_visible = ! this.blink_visible; - // if(this.font_blink_enabled) - // { - // this.mark_blinking_rows_dirty(); - // } - // if(this.cursor_enabled) - // { - // this.txt_row_dirty[this.cursor_row] = this.txt_dirty = 1; - // } - //} - - // render changed rows - if(this.txt_dirty) - { - this.render_dirty_rows(); - this.txt_dirty = 0; - this.txt_row_dirty.fill(0); - } - - return this.image_data; -};