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;
-};