2013-11-06 01:12:55 +01:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
var
|
|
|
|
/**
|
2014-09-27 04:54:36 +02:00
|
|
|
* Always 64k
|
2015-09-12 01:10:38 +02:00
|
|
|
* @const
|
2014-09-27 04:54:36 +02:00
|
|
|
*/
|
|
|
|
VGA_BANK_SIZE = 64 * 1024,
|
|
|
|
|
|
|
|
/** @const */
|
|
|
|
MAX_XRES = 2560,
|
|
|
|
|
|
|
|
/** @const */
|
|
|
|
MAX_YRES = 1600,
|
|
|
|
|
|
|
|
/** @const */
|
|
|
|
MAX_BPP = 32;
|
|
|
|
|
2017-06-07 17:19:15 +02:00
|
|
|
/** @const */
|
|
|
|
//var VGA_LFB_ADDRESS = 0xFE000000; // set by seabios
|
|
|
|
var VGA_LFB_ADDRESS = 0xE0000000;
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
/**
|
|
|
|
* @const
|
|
|
|
* Equals the maximum number of pixels for non svga.
|
|
|
|
* 8 pixels per byte.
|
|
|
|
*/
|
|
|
|
var VGA_PIXEL_BUFFER_SIZE = 8 * VGA_BANK_SIZE;
|
|
|
|
|
|
|
|
/** @const */
|
2022-02-16 00:58:31 +01:00
|
|
|
var VGA_MIN_MEMORY_SIZE = 4 * VGA_BANK_SIZE;
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @const
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#06}
|
|
|
|
*/
|
|
|
|
var VGA_HOST_MEMORY_SPACE_START = Uint32Array.from([
|
|
|
|
0xA0000,
|
|
|
|
0xA0000,
|
|
|
|
0xB0000,
|
|
|
|
0xB8000,
|
|
|
|
]);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @const
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#06}
|
|
|
|
*/
|
|
|
|
var VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([
|
|
|
|
0x20000, // 128K
|
|
|
|
0x10000, // 64K
|
|
|
|
0x8000, // 32K
|
|
|
|
0x8000, // 32K
|
|
|
|
]);
|
|
|
|
|
2013-11-06 01:12:55 +01:00
|
|
|
/**
|
|
|
|
* @constructor
|
2015-02-25 18:21:54 +01:00
|
|
|
* @param {CPU} cpu
|
2015-12-30 23:30:54 +01:00
|
|
|
* @param {BusConnector} bus
|
2015-02-25 18:21:54 +01:00
|
|
|
* @param {number} vga_memory_size
|
2013-11-06 01:12:55 +01:00
|
|
|
*/
|
2014-12-25 02:32:18 +01:00
|
|
|
function VGAScreen(cpu, bus, vga_memory_size)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2022-02-16 00:58:31 +01:00
|
|
|
this.cpu = cpu;
|
|
|
|
|
2015-12-30 23:30:54 +01:00
|
|
|
/** @const @type {BusConnector} */
|
2014-12-25 02:32:18 +01:00
|
|
|
this.bus = bus;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.vga_memory_size = vga_memory_size;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.cursor_address = 0;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.cursor_scanline_start = 0xE;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.cursor_scanline_end = 0xF;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/**
|
|
|
|
* Number of columns in text mode
|
2015-09-12 01:10:38 +02:00
|
|
|
* @type {number}
|
2014-09-27 04:54:36 +02:00
|
|
|
*/
|
2015-01-12 18:05:10 +01:00
|
|
|
this.max_cols = 80;
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
/**
|
2014-09-27 04:54:36 +02:00
|
|
|
* Number of rows in text mode
|
2015-09-12 01:10:38 +02:00
|
|
|
* @type {number}
|
2014-09-27 04:54:36 +02:00
|
|
|
*/
|
2015-01-12 18:05:10 +01:00
|
|
|
this.max_rows = 25;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/**
|
|
|
|
* Width in pixels in graphical mode
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-09-27 07:31:49 +02:00
|
|
|
this.screen_width = 0;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/**
|
|
|
|
* Height in pixels in graphical mode
|
|
|
|
* @type {number}
|
|
|
|
*/
|
2014-09-27 07:31:49 +02:00
|
|
|
this.screen_height = 0;
|
2013-11-28 01:31:33 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
/**
|
|
|
|
* Logical width in pixels of virtual buffer available for panning
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
this.virtual_width = 0;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Logical height in pixels of virtual buffer available for panning
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
this.virtual_height = 0;
|
|
|
|
|
2018-01-21 01:39:07 +01:00
|
|
|
/**
|
|
|
|
* The rectangular fragments of the image buffer, and their destination
|
|
|
|
* locations, to be drawn every screen_fill_buffer during VGA modes.
|
|
|
|
* @type {Array<Object<string, number>>}
|
|
|
|
*/
|
|
|
|
this.layers = [];
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/**
|
|
|
|
* video memory start address
|
|
|
|
* @type {number}
|
|
|
|
*/
|
|
|
|
this.start_address = 0;
|
|
|
|
|
2017-03-04 21:53:31 +01:00
|
|
|
/**
|
2018-01-14 03:59:27 +01:00
|
|
|
* Start address - a copy of start_address that only gets updated
|
|
|
|
* during VSync, used for panning and page flipping
|
2017-03-04 21:53:31 +01:00
|
|
|
* @type {number}
|
|
|
|
*/
|
2018-01-14 03:59:27 +01:00
|
|
|
this.start_address_latched = 0;
|
2017-03-04 21:53:31 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
/**
|
|
|
|
* Unimplemented CRTC registers go here
|
|
|
|
*/
|
|
|
|
this.crtc = new Uint8Array(0x19);
|
|
|
|
|
|
|
|
// Implemented CRTC registers:
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.crtc_mode = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.horizontal_display_enable_end = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.horizontal_blank_start = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.vertical_display_enable_end = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.vertical_blank_start = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.underline_location_register = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.preset_row_scan = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.offset_register = 0;
|
|
|
|
|
2018-01-15 09:47:43 +01:00
|
|
|
/** @type {number} */
|
|
|
|
this.line_compare = 0;
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
// End of CRTC registers
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Used for svga, e.g. banked modes
|
|
|
|
* @type{boolean}
|
|
|
|
*/
|
2014-09-27 04:54:36 +02:00
|
|
|
this.graphical_mode_is_linear = true;
|
|
|
|
|
|
|
|
/** @type {boolean} */
|
|
|
|
this.graphical_mode = false;
|
2018-01-23 01:56:54 +01:00
|
|
|
setTimeout(() => { bus.send("screen-set-mode", this.graphical_mode); }, 0);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
/*
|
2018-01-14 03:59:27 +01:00
|
|
|
* VGA palette containing 256 colors for video mode 13, svga 8bpp, etc.
|
2014-09-27 04:54:36 +02:00
|
|
|
* Needs to be initialised by the BIOS
|
|
|
|
*/
|
|
|
|
this.vga256_palette = new Int32Array(256);
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
/**
|
|
|
|
* VGA read latches
|
|
|
|
* @type{number}
|
|
|
|
*/
|
2017-11-26 00:17:47 +01:00
|
|
|
this.latch_dword = 0;
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2022-11-21 18:23:00 +01:00
|
|
|
/** @type {number} */
|
|
|
|
this.svga_version = 0xB0C5;
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.svga_width = 0;
|
|
|
|
|
|
|
|
/** @type {number} */
|
|
|
|
this.svga_height = 0;
|
|
|
|
|
2014-07-06 22:02:40 +02:00
|
|
|
this.svga_enabled = false;
|
2014-02-07 17:43:12 +01:00
|
|
|
|
|
|
|
/** @type {number} */
|
2015-04-12 21:40:25 +02:00
|
|
|
this.svga_bpp = 32;
|
2013-11-28 01:31:33 +01:00
|
|
|
|
2015-04-17 22:28:51 +02:00
|
|
|
/** @type {number} */
|
|
|
|
this.svga_bank_offset = 0;
|
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
/**
|
2014-07-14 22:19:59 +02:00
|
|
|
* The video buffer offset created by VBE_DISPI_INDEX_Y_OFFSET
|
|
|
|
* In bytes
|
2015-09-12 01:10:38 +02:00
|
|
|
* @type {number}
|
2014-07-14 22:19:59 +02:00
|
|
|
*/
|
|
|
|
this.svga_offset = 0;
|
|
|
|
|
2022-07-20 14:40:18 +02:00
|
|
|
const pci_revision = 0; // set to 2 for qemu extended registers
|
|
|
|
|
2013-11-25 12:43:39 +01:00
|
|
|
// Experimental, could probably need some changes
|
|
|
|
// 01:00.0 VGA compatible controller: NVIDIA Corporation GT216 [GeForce GT 220] (rev a2)
|
2013-12-20 22:04:58 +01:00
|
|
|
this.pci_space = [
|
2022-07-20 14:40:18 +02:00
|
|
|
0x34, 0x12, 0x11, 0x11, 0x03, 0x01, 0x00, 0x00, pci_revision, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00,
|
2017-06-07 17:19:15 +02:00
|
|
|
0x08, VGA_LFB_ADDRESS >>> 8, VGA_LFB_ADDRESS >>> 16, VGA_LFB_ADDRESS >>> 24,
|
2021-01-01 02:14:29 +01:00
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xbf, 0xfe, 0x00, 0x00, 0x00, 0x00,
|
|
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xf4, 0x1a, 0x00, 0x11,
|
|
|
|
0x00, 0x00, 0xbe, 0xfe, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
2013-12-20 22:04:58 +01:00
|
|
|
];
|
|
|
|
this.pci_id = 0x12 << 3;
|
2017-06-07 17:19:15 +02:00
|
|
|
this.pci_bars = [
|
|
|
|
{
|
|
|
|
size: vga_memory_size,
|
|
|
|
},
|
|
|
|
];
|
2017-03-11 00:08:35 +01:00
|
|
|
|
|
|
|
// TODO: Should be matched with vga bios size and mapping address
|
2017-06-07 17:19:15 +02:00
|
|
|
// Seabios config for this device:
|
|
|
|
// CONFIG_VGA_PCI=y
|
|
|
|
// CONFIG_OVERRIDE_PCI_ID=y
|
|
|
|
// CONFIG_VGA_VID=0x10de
|
|
|
|
// CONFIG_VGA_DID=0x0a20
|
|
|
|
|
2017-03-11 00:08:35 +01:00
|
|
|
this.pci_rom_size = 0x10000;
|
|
|
|
this.pci_rom_address = 0xFEB00000;
|
|
|
|
|
2016-01-01 17:49:21 +01:00
|
|
|
this.name = "vga";
|
2013-12-20 22:04:58 +01:00
|
|
|
|
2014-01-05 03:19:09 +01:00
|
|
|
this.stats = {
|
|
|
|
is_graphical: false,
|
|
|
|
res_x: 0,
|
|
|
|
res_y: 0,
|
|
|
|
bpp: 0,
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.index_crtc = 0;
|
2013-11-28 01:31:33 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
// index for setting colors through port 3C9h
|
2015-04-30 03:01:36 +02:00
|
|
|
this.dac_color_index_write = 0;
|
|
|
|
this.dac_color_index_read = 0;
|
2018-01-15 22:45:30 +01:00
|
|
|
this.dac_state = 0;
|
2013-11-28 01:31:33 +01:00
|
|
|
|
2022-05-17 13:42:53 +02:00
|
|
|
this.dac_mask = 0xFF;
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.dac_map = new Uint8Array(0x10);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
this.attribute_controller_index = -1;
|
2018-01-15 06:25:14 +01:00
|
|
|
this.palette_source = 0x20;
|
2018-01-14 03:59:27 +01:00
|
|
|
this.attribute_mode = 0;
|
|
|
|
this.color_plane_enable = 0;
|
|
|
|
this.horizontal_panning = 0;
|
|
|
|
this.color_select = 0;
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.sequencer_index = -1;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
// bitmap of planes 0-3
|
|
|
|
this.plane_write_bm = 0xF;
|
|
|
|
this.sequencer_memory_mode = 0;
|
2018-01-15 06:25:14 +01:00
|
|
|
this.clocking_mode = 0;
|
2014-09-27 04:54:36 +02:00
|
|
|
this.graphics_index = -1;
|
2014-07-14 22:19:59 +02:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.plane_read = 0, // value 0-3, which plane to read
|
|
|
|
this.planar_mode = 0;
|
|
|
|
this.planar_rotate_reg = 0;
|
|
|
|
this.planar_bitmap = 0xFF;
|
2017-11-26 00:17:47 +01:00
|
|
|
this.planar_setreset = 0;
|
|
|
|
this.planar_setreset_enable = 0;
|
2018-01-15 09:47:43 +01:00
|
|
|
this.miscellaneous_graphics_register = 0;
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2017-12-15 00:37:37 +01:00
|
|
|
this.color_compare = 0;
|
|
|
|
this.color_dont_care = 0;
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.max_scan_line = 0;
|
|
|
|
|
|
|
|
this.miscellaneous_output_register = 0xff;
|
|
|
|
this.port_3DA_value = 0xFF;
|
|
|
|
|
|
|
|
|
|
|
|
var io = cpu.io;
|
2015-09-12 01:10:38 +02:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x3C0, this, this.port3C0_write);
|
2015-02-16 01:58:40 +01:00
|
|
|
io.register_read(0x3C0, this, this.port3C0_read, this.port3C0_read16);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(0x3C1, this, this.port3C1_read);
|
|
|
|
io.register_write(0x3C2, this, this.port3C2_write);
|
2015-09-12 01:10:38 +02:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write_consecutive(0x3C4, this, this.port3C4_write, this.port3C5_write);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(0x3C4, this, this.port3C4_read);
|
|
|
|
io.register_read(0x3C5, this, this.port3C5_read);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write_consecutive(0x3CE, this, this.port3CE_write, this.port3CF_write);
|
2014-02-09 23:21:37 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(0x3CE, this, this.port3CE_read);
|
|
|
|
io.register_read(0x3CF, this, this.port3CF_read);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2022-05-17 13:42:53 +02:00
|
|
|
io.register_read(0x3C6, this, this.port3C6_read);
|
|
|
|
io.register_write(0x3C6, this, this.port3C6_write);
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x3C7, this, this.port3C7_write);
|
2018-01-15 22:45:30 +01:00
|
|
|
io.register_read(0x3C7, this, this.port3C7_read);
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x3C8, this, this.port3C8_write);
|
2018-01-15 22:45:30 +01:00
|
|
|
io.register_read(0x3C8, this, this.port3C8_read);
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x3C9, this, this.port3C9_write);
|
2015-04-30 03:01:36 +02:00
|
|
|
io.register_read(0x3C9, this, this.port3C9_read);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(0x3CC, this, this.port3CC_read);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write_consecutive(0x3D4, this, this.port3D4_write, this.port3D5_write);
|
2018-02-14 05:05:28 +01:00
|
|
|
io.register_read(0x3D4, this, this.port3D4_read);
|
2021-01-01 02:14:31 +01:00
|
|
|
io.register_read(0x3D5, this, this.port3D5_read, () => {
|
|
|
|
dbg_log("Warning: 16-bit read from 3D5", LOG_VGA);
|
|
|
|
return this.port3D5_read();
|
|
|
|
});
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2017-03-04 21:53:31 +01:00
|
|
|
io.register_read(0x3CA, this, function() { dbg_log("3CA read", LOG_VGA); return 0; });
|
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_read(0x3DA, this, this.port3DA_read);
|
2018-01-14 03:59:27 +01:00
|
|
|
io.register_read(0x3BA, this, this.port3DA_read);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
|
|
|
|
|
|
|
// Bochs VBE Extensions
|
|
|
|
// http://wiki.osdev.org/Bochs_VBE_Extensions
|
|
|
|
this.dispi_index = -1;
|
|
|
|
this.dispi_enable_value = 0;
|
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x1CE, this, undefined, this.port1CE_write);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
io.register_write(0x1CF, this, undefined, this.port1CF_write);
|
|
|
|
io.register_read(0x1CF, this, undefined, this.port1CF_read);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.vga_memory_size === undefined || this.vga_memory_size < VGA_MIN_MEMORY_SIZE)
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
this.vga_memory_size = VGA_MIN_MEMORY_SIZE;
|
2014-09-27 04:54:36 +02:00
|
|
|
dbg_log("vga memory size rounded up to " + this.vga_memory_size, LOG_VGA);
|
|
|
|
}
|
|
|
|
else if(this.vga_memory_size & (VGA_BANK_SIZE - 1))
|
|
|
|
{
|
|
|
|
// round up to next 64k
|
|
|
|
this.vga_memory_size |= VGA_BANK_SIZE - 1;
|
|
|
|
this.vga_memory_size++;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2022-02-16 00:58:31 +01:00
|
|
|
|
|
|
|
const vga_offset = cpu.svga_allocate_memory(this.vga_memory_size);
|
|
|
|
this.svga_memory = v86util.view(Uint8Array, cpu.wasm_memory, vga_offset, this.vga_memory_size);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2015-04-12 21:40:25 +02:00
|
|
|
this.diff_addr_min = this.vga_memory_size;
|
|
|
|
this.diff_addr_max = 0;
|
2018-01-14 03:59:27 +01:00
|
|
|
this.diff_plot_min = this.vga_memory_size;
|
|
|
|
this.diff_plot_max = 0;
|
2015-04-12 21:40:25 +02:00
|
|
|
|
2022-02-16 00:58:31 +01:00
|
|
|
this.image_data = null;
|
2015-04-12 21:40:25 +02:00
|
|
|
|
|
|
|
bus.register("screen-fill-buffer", function()
|
|
|
|
{
|
|
|
|
this.screen_fill_buffer();
|
|
|
|
}, this);
|
|
|
|
|
2022-02-16 00:58:31 +01:00
|
|
|
this.vga_memory = new Uint8Array(4 * VGA_BANK_SIZE);
|
|
|
|
this.plane0 = new Uint8Array(this.vga_memory.buffer, 0 * VGA_BANK_SIZE, VGA_BANK_SIZE);
|
|
|
|
this.plane1 = new Uint8Array(this.vga_memory.buffer, 1 * VGA_BANK_SIZE, VGA_BANK_SIZE);
|
|
|
|
this.plane2 = new Uint8Array(this.vga_memory.buffer, 2 * VGA_BANK_SIZE, VGA_BANK_SIZE);
|
|
|
|
this.plane3 = new Uint8Array(this.vga_memory.buffer, 3 * VGA_BANK_SIZE, VGA_BANK_SIZE);
|
|
|
|
this.pixel_buffer = new Uint8Array(VGA_PIXEL_BUFFER_SIZE);
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
var me = this;
|
2015-05-18 22:18:59 +02:00
|
|
|
io.mmap_register(0xA0000, 0x20000,
|
2014-10-21 21:51:42 +02:00
|
|
|
function(addr) { return me.vga_memory_read(addr); },
|
|
|
|
function(addr, value) { me.vga_memory_write(addr, value); }
|
2014-09-27 04:54:36 +02:00
|
|
|
);
|
2017-05-29 19:25:28 +02:00
|
|
|
|
|
|
|
cpu.devices.pci.register_device(this);
|
2017-05-29 22:38:01 +02:00
|
|
|
}
|
2014-09-29 07:10:47 +02:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
VGAScreen.prototype.get_state = function()
|
2014-09-29 07:10:47 +02:00
|
|
|
{
|
2015-05-18 22:18:59 +02:00
|
|
|
var state = [];
|
|
|
|
|
|
|
|
state[0] = this.vga_memory_size;
|
|
|
|
state[1] = this.cursor_address;
|
|
|
|
state[2] = this.cursor_scanline_start;
|
|
|
|
state[3] = this.cursor_scanline_end;
|
|
|
|
state[4] = this.max_cols;
|
|
|
|
state[5] = this.max_rows;
|
2022-02-16 00:58:31 +01:00
|
|
|
state[6] = this.vga_memory;
|
2018-01-23 01:42:05 +01:00
|
|
|
state[7] = this.dac_state;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[8] = this.start_address;
|
|
|
|
state[9] = this.graphical_mode;
|
|
|
|
state[10] = this.vga256_palette;
|
2017-12-15 00:37:37 +01:00
|
|
|
state[11] = this.latch_dword;
|
|
|
|
state[12] = this.color_compare;
|
|
|
|
state[13] = this.color_dont_care;
|
2018-01-23 01:42:05 +01:00
|
|
|
state[14] = this.miscellaneous_graphics_register;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[15] = this.svga_width;
|
|
|
|
state[16] = this.svga_height;
|
2018-01-23 01:42:05 +01:00
|
|
|
state[17] = this.crtc_mode;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[18] = this.svga_enabled;
|
|
|
|
state[19] = this.svga_bpp;
|
|
|
|
state[20] = this.svga_bank_offset;
|
|
|
|
state[21] = this.svga_offset;
|
|
|
|
state[22] = this.index_crtc;
|
|
|
|
state[23] = this.dac_color_index_write;
|
|
|
|
state[24] = this.dac_color_index_read;
|
|
|
|
state[25] = this.dac_map;
|
|
|
|
state[26] = this.sequencer_index;
|
|
|
|
state[27] = this.plane_write_bm;
|
|
|
|
state[28] = this.sequencer_memory_mode;
|
|
|
|
state[29] = this.graphics_index;
|
|
|
|
state[30] = this.plane_read;
|
|
|
|
state[31] = this.planar_mode;
|
|
|
|
state[32] = this.planar_rotate_reg;
|
|
|
|
state[33] = this.planar_bitmap;
|
|
|
|
state[34] = this.max_scan_line;
|
2017-05-29 22:38:01 +02:00
|
|
|
state[35] = this.miscellaneous_output_register;
|
2015-05-18 22:18:59 +02:00
|
|
|
state[36] = this.port_3DA_value;
|
|
|
|
state[37] = this.dispi_index;
|
|
|
|
state[38] = this.dispi_enable_value;
|
|
|
|
state[39] = this.svga_memory;
|
2016-02-14 13:07:10 +01:00
|
|
|
state[40] = this.graphical_mode_is_linear;
|
|
|
|
state[41] = this.attribute_controller_index;
|
2016-02-14 18:21:19 +01:00
|
|
|
state[42] = this.offset_register;
|
2017-12-12 23:46:30 +01:00
|
|
|
state[43] = this.planar_setreset;
|
|
|
|
state[44] = this.planar_setreset_enable;
|
2018-01-15 09:47:43 +01:00
|
|
|
state[45] = this.start_address_latched;
|
|
|
|
state[46] = this.crtc;
|
|
|
|
state[47] = this.horizontal_display_enable_end;
|
|
|
|
state[48] = this.horizontal_blank_start;
|
|
|
|
state[49] = this.vertical_display_enable_end;
|
|
|
|
state[50] = this.vertical_blank_start;
|
|
|
|
state[51] = this.underline_location_register;
|
|
|
|
state[52] = this.preset_row_scan;
|
|
|
|
state[53] = this.offset_register;
|
|
|
|
state[54] = this.palette_source;
|
|
|
|
state[55] = this.attribute_mode;
|
|
|
|
state[56] = this.color_plane_enable;
|
|
|
|
state[57] = this.horizontal_panning;
|
|
|
|
state[58] = this.color_select;
|
|
|
|
state[59] = this.clocking_mode;
|
|
|
|
state[60] = this.line_compare;
|
2022-02-16 00:58:31 +01:00
|
|
|
state[61] = this.pixel_buffer;
|
2022-05-17 13:42:53 +02:00
|
|
|
state[62] = this.dac_mask;
|
2015-05-18 22:18:59 +02:00
|
|
|
|
|
|
|
return state;
|
|
|
|
};
|
2014-09-29 07:10:47 +02:00
|
|
|
|
2015-05-18 22:18:59 +02:00
|
|
|
VGAScreen.prototype.set_state = function(state)
|
|
|
|
{
|
|
|
|
this.vga_memory_size = state[0];
|
|
|
|
this.cursor_address = state[1];
|
|
|
|
this.cursor_scanline_start = state[2];
|
|
|
|
this.cursor_scanline_end = state[3];
|
|
|
|
this.max_cols = state[4];
|
|
|
|
this.max_rows = state[5];
|
2022-02-16 00:58:31 +01:00
|
|
|
state[6] && this.vga_memory.set(state[6]);
|
2018-01-23 01:42:05 +01:00
|
|
|
this.dac_state = state[7];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.start_address = state[8];
|
|
|
|
this.graphical_mode = state[9];
|
|
|
|
this.vga256_palette = state[10];
|
2017-12-15 00:37:37 +01:00
|
|
|
this.latch_dword = state[11];
|
|
|
|
this.color_compare = state[12];
|
|
|
|
this.color_dont_care = state[13];
|
2018-01-23 01:42:05 +01:00
|
|
|
this.miscellaneous_graphics_register = state[14];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.svga_width = state[15];
|
|
|
|
this.svga_height = state[16];
|
2018-01-23 01:42:05 +01:00
|
|
|
this.crtc_mode = state[17];
|
2015-05-18 22:18:59 +02:00
|
|
|
this.svga_enabled = state[18];
|
|
|
|
this.svga_bpp = state[19];
|
|
|
|
this.svga_bank_offset = state[20];
|
|
|
|
this.svga_offset = state[21];
|
|
|
|
this.index_crtc = state[22];
|
|
|
|
this.dac_color_index_write = state[23];
|
|
|
|
this.dac_color_index_read = state[24];
|
|
|
|
this.dac_map = state[25];
|
|
|
|
this.sequencer_index = state[26];
|
|
|
|
this.plane_write_bm = state[27];
|
|
|
|
this.sequencer_memory_mode = state[28];
|
|
|
|
this.graphics_index = state[29];
|
|
|
|
this.plane_read = state[30];
|
|
|
|
this.planar_mode = state[31];
|
|
|
|
this.planar_rotate_reg = state[32];
|
|
|
|
this.planar_bitmap = state[33];
|
|
|
|
this.max_scan_line = state[34];
|
|
|
|
this.miscellaneous_output_register = state[35];
|
|
|
|
this.port_3DA_value = state[36];
|
|
|
|
this.dispi_index = state[37];
|
|
|
|
this.dispi_enable_value = state[38];
|
|
|
|
this.svga_memory.set(state[39]);
|
2016-02-14 13:07:10 +01:00
|
|
|
this.graphical_mode_is_linear = state[40];
|
|
|
|
this.attribute_controller_index = state[41];
|
2016-02-14 18:21:19 +01:00
|
|
|
this.offset_register = state[42];
|
2017-12-12 23:46:30 +01:00
|
|
|
this.planar_setreset = state[43];
|
|
|
|
this.planar_setreset_enable = state[44];
|
2018-01-15 09:47:43 +01:00
|
|
|
this.start_address_latched = state[45];
|
|
|
|
this.crtc.set(state[46]);
|
|
|
|
this.horizontal_display_enable_end = state[47];
|
|
|
|
this.horizontal_blank_start = state[48];
|
|
|
|
this.vertical_display_enable_end = state[49];
|
|
|
|
this.vertical_blank_start = state[50];
|
|
|
|
this.underline_location_register = state[51];
|
|
|
|
this.preset_row_scan = state[52];
|
|
|
|
this.offset_register = state[53];
|
|
|
|
this.palette_source = state[54];
|
|
|
|
this.attribute_mode = state[55];
|
|
|
|
this.color_plane_enable = state[56];
|
|
|
|
this.horizontal_panning = state[57];
|
|
|
|
this.color_select = state[58];
|
|
|
|
this.clocking_mode = state[59];
|
|
|
|
this.line_compare = state[60];
|
2022-02-16 00:58:31 +01:00
|
|
|
state[61] && this.pixel_buffer.set(state[61]);
|
2022-05-17 13:42:53 +02:00
|
|
|
this.dac_mask = state[62] === undefined ? 0xFF : state[62];
|
2017-12-12 23:46:30 +01:00
|
|
|
|
2015-04-17 22:28:51 +02:00
|
|
|
this.bus.send("screen-set-mode", this.graphical_mode);
|
2014-09-29 07:10:47 +02:00
|
|
|
|
2015-04-17 22:28:51 +02:00
|
|
|
if(this.graphical_mode)
|
2014-09-29 07:10:47 +02:00
|
|
|
{
|
2018-01-23 01:42:05 +01:00
|
|
|
// Ensure set_size_graphical will update
|
|
|
|
this.screen_width = 0;
|
|
|
|
this.screen_height = 0;
|
|
|
|
|
2018-01-15 09:47:43 +01:00
|
|
|
if(this.svga_enabled)
|
|
|
|
{
|
|
|
|
this.set_size_graphical(this.svga_width, this.svga_height,
|
2018-01-22 22:24:31 +01:00
|
|
|
this.svga_bpp, this.svga_width, this.svga_height);
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-15 09:47:43 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.update_vga_size();
|
2022-02-10 15:51:02 +01:00
|
|
|
this.update_layers();
|
2018-01-15 09:47:43 +01:00
|
|
|
this.complete_replot();
|
|
|
|
}
|
2014-09-29 07:10:47 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.set_size_text(this.max_cols, this.max_rows);
|
|
|
|
this.update_cursor_scanline();
|
|
|
|
this.update_cursor();
|
|
|
|
}
|
2015-04-12 21:40:25 +02:00
|
|
|
this.complete_redraw();
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.vga_memory_read = function(addr)
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.svga_enabled && this.graphical_mode_is_linear)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2022-02-16 00:58:31 +01:00
|
|
|
return this.cpu.read8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0);
|
2014-09-27 04:54:36 +02:00
|
|
|
}
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
var memory_space_select = this.miscellaneous_graphics_register >> 2 & 0x3;
|
|
|
|
addr -= VGA_HOST_MEMORY_SPACE_START[memory_space_select];
|
|
|
|
|
|
|
|
// VGA chip only decodes addresses within the selected memory space.
|
|
|
|
if(addr < 0 || addr >= VGA_HOST_MEMORY_SPACE_SIZE[memory_space_select])
|
|
|
|
{
|
2018-01-14 09:45:45 +01:00
|
|
|
dbg_log("vga read outside memory space: addr:" + h(addr), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
return 0;
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2017-12-17 21:13:11 +01:00
|
|
|
this.latch_dword = this.plane0[addr];
|
2017-11-26 00:17:47 +01:00
|
|
|
this.latch_dword |= this.plane1[addr] << 8;
|
|
|
|
this.latch_dword |= this.plane2[addr] << 16;
|
|
|
|
this.latch_dword |= this.plane3[addr] << 24;
|
2014-07-14 22:19:59 +02:00
|
|
|
|
2017-12-15 00:37:37 +01:00
|
|
|
if(this.planar_mode & 0x08)
|
|
|
|
{
|
|
|
|
// read mode 1
|
|
|
|
var reading = 0xFF;
|
|
|
|
|
2017-12-17 21:33:35 +01:00
|
|
|
if(this.color_dont_care & 0x1)
|
|
|
|
{
|
|
|
|
reading &= this.plane0[addr] ^ ~(this.color_compare & 0x1 ? 0xFF : 0x00);
|
|
|
|
}
|
|
|
|
if(this.color_dont_care & 0x2)
|
|
|
|
{
|
|
|
|
reading &= this.plane1[addr] ^ ~(this.color_compare & 0x2 ? 0xFF : 0x00);
|
|
|
|
}
|
|
|
|
if(this.color_dont_care & 0x4)
|
|
|
|
{
|
|
|
|
reading &= this.plane2[addr] ^ ~(this.color_compare & 0x4 ? 0xFF : 0x00);
|
|
|
|
}
|
|
|
|
if(this.color_dont_care & 0x8)
|
|
|
|
{
|
|
|
|
reading &= this.plane3[addr] ^ ~(this.color_compare & 0x8 ? 0xFF : 0x00);
|
|
|
|
}
|
2017-12-15 00:37:37 +01:00
|
|
|
|
|
|
|
return reading;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// read mode 0
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
var plane = this.plane_read;
|
|
|
|
if(!this.graphical_mode)
|
|
|
|
{
|
|
|
|
// We currently put all text data linearly
|
|
|
|
plane = 0;
|
|
|
|
}
|
|
|
|
else if(this.sequencer_memory_mode & 0x8)
|
|
|
|
{
|
|
|
|
// Chain 4
|
|
|
|
plane = addr & 0x3;
|
|
|
|
addr &= ~0x3;
|
|
|
|
}
|
|
|
|
else if(this.planar_mode & 0x10)
|
|
|
|
{
|
|
|
|
// Odd/Even host read
|
|
|
|
plane = addr & 0x1;
|
|
|
|
addr &= ~0x1;
|
|
|
|
}
|
|
|
|
return this.vga_memory[plane << 16 | addr];
|
2017-12-15 00:37:37 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.vga_memory_write = function(addr, value)
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.svga_enabled && this.graphical_mode && this.graphical_mode_is_linear)
|
|
|
|
{
|
|
|
|
// vbe banked mode
|
2022-02-16 00:58:31 +01:00
|
|
|
this.cpu.write8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0, value);
|
2018-01-14 03:59:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var memory_space_select = this.miscellaneous_graphics_register >> 2 & 0x3;
|
|
|
|
addr -= VGA_HOST_MEMORY_SPACE_START[memory_space_select];
|
|
|
|
|
|
|
|
if(addr < 0 || addr >= VGA_HOST_MEMORY_SPACE_SIZE[memory_space_select])
|
|
|
|
{
|
2018-01-14 09:45:45 +01:00
|
|
|
dbg_log("vga write outside memory space: addr:" + h(addr) + ", value:" + h(value), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
return;
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
|
|
|
|
if(this.graphical_mode)
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
this.vga_memory_write_graphical(addr, value);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
if(!(this.plane_write_bm & 0x3))
|
|
|
|
{
|
|
|
|
// Ignore writes to font planes.
|
|
|
|
return;
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
this.vga_memory_write_text_mode(addr, value);
|
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
VGAScreen.prototype.vga_memory_write_graphical = function(addr, value)
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2017-12-17 21:13:11 +01:00
|
|
|
var plane_dword;
|
|
|
|
var write_mode = this.planar_mode & 3;
|
2017-12-17 21:33:35 +01:00
|
|
|
var bitmask = this.apply_feed(this.planar_bitmap);
|
|
|
|
var setreset_dword = this.apply_expand(this.planar_setreset);
|
|
|
|
var setreset_enable_dword = this.apply_expand(this.planar_setreset_enable);
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2017-11-26 00:17:47 +01:00
|
|
|
// Write modes - see http://www.osdever.net/FreeVGA/vga/graphreg.htm#05
|
|
|
|
switch(write_mode)
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2017-11-26 00:17:47 +01:00
|
|
|
case 0:
|
|
|
|
value = this.apply_rotate(value);
|
|
|
|
plane_dword = this.apply_feed(value);
|
2017-12-17 21:33:35 +01:00
|
|
|
plane_dword = this.apply_setreset(plane_dword, setreset_enable_dword);
|
2017-11-26 00:17:47 +01:00
|
|
|
plane_dword = this.apply_logical(plane_dword, this.latch_dword);
|
|
|
|
plane_dword = this.apply_bitmask(plane_dword, bitmask);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
plane_dword = this.latch_dword;
|
|
|
|
break;
|
|
|
|
case 2:
|
|
|
|
plane_dword = this.apply_expand(value);
|
|
|
|
plane_dword = this.apply_logical(plane_dword, this.latch_dword);
|
|
|
|
plane_dword = this.apply_bitmask(plane_dword, bitmask);
|
|
|
|
break;
|
|
|
|
case 3:
|
|
|
|
value = this.apply_rotate(value);
|
2017-12-18 21:34:34 +01:00
|
|
|
bitmask &= this.apply_feed(value);
|
2017-12-17 21:33:35 +01:00
|
|
|
plane_dword = setreset_dword;
|
2017-11-26 00:17:47 +01:00
|
|
|
plane_dword = this.apply_bitmask(plane_dword, bitmask);
|
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
var plane_select = 0xF;
|
2015-04-12 21:40:25 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
switch(this.sequencer_memory_mode & 0xC)
|
|
|
|
{
|
|
|
|
// Odd/Even (aka chain 2)
|
|
|
|
case 0x0:
|
|
|
|
plane_select = 0x5 << (addr & 0x1);
|
|
|
|
addr &= ~0x1;
|
|
|
|
break;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
// Chain 4
|
|
|
|
// Note: FreeVGA may have mistakenly stated that this bit field is
|
|
|
|
// for system read only, yet the IBM Open Source Graphics Programmer's
|
|
|
|
// Reference Manual explicitly states "both read and write".
|
|
|
|
case 0x8:
|
|
|
|
case 0xC:
|
|
|
|
plane_select = 1 << (addr & 0x3);
|
|
|
|
addr &= ~0x3;
|
|
|
|
break;
|
|
|
|
}
|
2017-11-26 00:17:47 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
// Plane masks take precedence
|
|
|
|
// See: http://www.osdever.net/FreeVGA/vga/seqreg.htm#02
|
|
|
|
plane_select &= this.plane_write_bm;
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
if(plane_select & 0x1) this.plane0[addr] = (plane_dword >> 0) & 0xFF;
|
|
|
|
if(plane_select & 0x2) this.plane1[addr] = (plane_dword >> 8) & 0xFF;
|
|
|
|
if(plane_select & 0x4) this.plane2[addr] = (plane_dword >> 16) & 0xFF;
|
|
|
|
if(plane_select & 0x8) this.plane3[addr] = (plane_dword >> 24) & 0xFF;
|
2014-09-27 04:54:36 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
var pixel_addr = this.vga_addr_to_pixel(addr);
|
|
|
|
this.partial_replot(pixel_addr, pixel_addr + 7);
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2017-11-26 00:17:47 +01:00
|
|
|
/**
|
|
|
|
* Copies data_byte into the four planes, with each plane
|
|
|
|
* represented by an 8-bit field inside the dword.
|
|
|
|
* @param {number} data_byte
|
|
|
|
* @return {number} 32-bit number representing the bytes for each plane.
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.apply_feed = function(data_byte)
|
|
|
|
{
|
2017-12-17 21:13:11 +01:00
|
|
|
var dword = data_byte;
|
2017-11-26 00:17:47 +01:00
|
|
|
dword |= data_byte << 8;
|
|
|
|
dword |= data_byte << 16;
|
|
|
|
dword |= data_byte << 24;
|
|
|
|
return dword;
|
2017-12-17 08:10:34 +01:00
|
|
|
};
|
2017-11-26 00:17:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Expands bits 0 to 3 to ocupy bits 0 to 31. Each
|
|
|
|
* bit is expanded to 0xFF if set or 0x00 if clear.
|
|
|
|
* @param {number} data_byte
|
|
|
|
* @return {number} 32-bit number representing the bytes for each plane.
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.apply_expand = function(data_byte)
|
|
|
|
{
|
2017-12-17 21:13:11 +01:00
|
|
|
var dword = data_byte & 0x1 ? 0xFF : 0x00;
|
2017-11-26 00:17:47 +01:00
|
|
|
dword |= (data_byte & 0x2 ? 0xFF : 0x00) << 8;
|
|
|
|
dword |= (data_byte & 0x4 ? 0xFF : 0x00) << 16;
|
|
|
|
dword |= (data_byte & 0x8 ? 0xFF : 0x00) << 24;
|
|
|
|
return dword;
|
2017-12-17 08:10:34 +01:00
|
|
|
};
|
2017-11-26 00:17:47 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Planar Write - Barrel Shifter
|
|
|
|
* @param {number} data_byte
|
|
|
|
* @return {number}
|
|
|
|
* @see {@link http://www.phatcode.net/res/224/files/html/ch25/25-01.html#Heading3}
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#03}
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.apply_rotate = function(data_byte)
|
|
|
|
{
|
2017-12-17 21:13:11 +01:00
|
|
|
var wrapped = data_byte | (data_byte << 8);
|
|
|
|
var count = this.planar_rotate_reg & 0x7;
|
|
|
|
var shifted = wrapped >>> count;
|
2017-11-26 00:17:47 +01:00
|
|
|
return shifted & 0xFF;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Planar Write - Set / Reset Circuitry
|
|
|
|
* @param {number} data_dword
|
|
|
|
* @param {number} enable_dword
|
|
|
|
* @return {number}
|
|
|
|
* @see {@link http://www.phatcode.net/res/224/files/html/ch25/25-03.html#Heading5}
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#00}
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.apply_setreset = function(data_dword, enable_dword)
|
|
|
|
{
|
2017-12-17 21:33:35 +01:00
|
|
|
var setreset_dword = this.apply_expand(this.planar_setreset);
|
|
|
|
data_dword |= enable_dword & setreset_dword;
|
|
|
|
data_dword &= ~enable_dword | setreset_dword;
|
2017-11-26 00:17:47 +01:00
|
|
|
return data_dword;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Planar Write - ALU Unit
|
|
|
|
* @param {number} data_dword
|
|
|
|
* @param {number} latch_dword
|
|
|
|
* @return {number}
|
|
|
|
* @see {@link http://www.phatcode.net/res/224/files/html/ch24/24-01.html#Heading3}
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#03}
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.apply_logical = function(data_dword, latch_dword)
|
|
|
|
{
|
|
|
|
switch(this.planar_rotate_reg & 0x18)
|
|
|
|
{
|
|
|
|
case 0x08:
|
|
|
|
return data_dword & latch_dword;
|
|
|
|
case 0x10:
|
|
|
|
return data_dword | latch_dword;
|
|
|
|
case 0x18:
|
|
|
|
return data_dword ^ latch_dword;
|
|
|
|
}
|
|
|
|
return data_dword;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Planar Write - Bitmask Unit
|
|
|
|
* @param {number} data_dword
|
|
|
|
* @param {number} bitmask_dword
|
|
|
|
* @return {number}
|
|
|
|
* @see {@link http://www.phatcode.net/res/224/files/html/ch25/25-01.html#Heading2}
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm#08}
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.apply_bitmask = function(data_dword, bitmask_dword)
|
|
|
|
{
|
2017-12-17 21:13:11 +01:00
|
|
|
var plane_dword = bitmask_dword & data_dword;
|
2017-11-26 00:17:47 +01:00
|
|
|
plane_dword |= ~bitmask_dword & this.latch_dword;
|
|
|
|
return plane_dword;
|
|
|
|
};
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.text_mode_redraw = function()
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
var addr = this.start_address << 1,
|
2014-09-27 04:54:36 +02:00
|
|
|
chr,
|
|
|
|
color;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
for(var row = 0; row < this.max_rows; row++)
|
|
|
|
{
|
|
|
|
for(var col = 0; col < this.max_cols; col++)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-09-27 04:54:36 +02:00
|
|
|
chr = this.vga_memory[addr];
|
|
|
|
color = this.vga_memory[addr | 1];
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
this.bus.send("screen-put-char", [row, col, chr,
|
2022-05-17 13:42:53 +02:00
|
|
|
this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & 0xF]],
|
|
|
|
this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]]);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
addr += 2;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.vga_memory_write_text_mode = function(addr, value)
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
var memory_start = (addr >> 1) - this.start_address,
|
2014-09-27 04:54:36 +02:00
|
|
|
row = memory_start / this.max_cols | 0,
|
|
|
|
col = memory_start % this.max_cols,
|
|
|
|
chr,
|
|
|
|
color;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2017-03-04 21:53:31 +01:00
|
|
|
// XXX: Should handle 16 bit write if possible
|
2014-09-27 04:54:36 +02:00
|
|
|
if(addr & 1)
|
|
|
|
{
|
|
|
|
color = value;
|
|
|
|
chr = this.vga_memory[addr & ~1];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
chr = value;
|
|
|
|
color = this.vga_memory[addr | 1];
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2015-09-12 01:10:38 +02:00
|
|
|
this.bus.send("screen-put-char", [row, col, chr,
|
2022-05-17 13:42:53 +02:00
|
|
|
this.vga256_palette[this.dac_mask & this.dac_map[color >> 4 & 0xF]],
|
|
|
|
this.vga256_palette[this.dac_mask & this.dac_map[color & 0xF]]]);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.vga_memory[addr] = value;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.update_cursor = function()
|
|
|
|
{
|
|
|
|
var row = (this.cursor_address - this.start_address) / this.max_cols | 0,
|
|
|
|
col = (this.cursor_address - this.start_address) % this.max_cols;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
row = Math.min(this.max_rows - 1, row);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-12-25 02:32:18 +01:00
|
|
|
this.bus.send("screen-update-cursor", [row, col]);
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
2015-04-12 21:40:25 +02:00
|
|
|
VGAScreen.prototype.complete_redraw = function()
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2016-02-14 13:07:30 +01:00
|
|
|
dbg_log("complete redraw", LOG_VGA);
|
|
|
|
|
2015-04-12 21:40:25 +02:00
|
|
|
if(this.graphical_mode)
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.svga_enabled)
|
|
|
|
{
|
2022-02-16 00:58:31 +01:00
|
|
|
this.cpu.svga_mark_dirty();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-02-16 00:58:31 +01:00
|
|
|
this.diff_addr_min = 0;
|
2018-01-14 03:59:27 +01:00
|
|
|
this.diff_addr_max = VGA_PIXEL_BUFFER_SIZE;
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2015-04-12 21:40:25 +02:00
|
|
|
this.text_mode_redraw();
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
VGAScreen.prototype.complete_replot = function()
|
|
|
|
{
|
|
|
|
dbg_log("complete replot", LOG_VGA);
|
|
|
|
|
|
|
|
if(!this.graphical_mode || this.svga_enabled)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
this.diff_plot_min = 0;
|
|
|
|
this.diff_plot_max = VGA_PIXEL_BUFFER_SIZE;
|
|
|
|
|
|
|
|
this.complete_redraw();
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.partial_redraw = function(min, max)
|
|
|
|
{
|
|
|
|
if(min < this.diff_addr_min) this.diff_addr_min = min;
|
|
|
|
if(max > this.diff_addr_max) this.diff_addr_max = max;
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.partial_replot = function(min, max)
|
|
|
|
{
|
|
|
|
if(min < this.diff_plot_min) this.diff_plot_min = min;
|
|
|
|
if(max > this.diff_plot_max) this.diff_plot_max = max;
|
|
|
|
|
|
|
|
this.partial_redraw(min, max);
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.reset_diffs = function()
|
|
|
|
{
|
|
|
|
this.diff_addr_min = this.vga_memory_size;
|
|
|
|
this.diff_addr_max = 0;
|
|
|
|
this.diff_plot_min = this.vga_memory_size;
|
|
|
|
this.diff_plot_max = 0;
|
|
|
|
};
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.destroy = function()
|
|
|
|
{
|
2014-01-05 03:19:09 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
VGAScreen.prototype.vga_bytes_per_line = function()
|
2018-01-14 03:59:27 +01:00
|
|
|
{
|
2018-01-14 05:33:02 +01:00
|
|
|
var bytes_per_line = this.offset_register << 2;
|
|
|
|
if(this.underline_location_register & 0x40) bytes_per_line <<= 1;
|
|
|
|
else if(this.crtc_mode & 0x40) bytes_per_line >>>= 1;
|
|
|
|
return bytes_per_line;
|
2018-01-14 03:59:27 +01:00
|
|
|
};
|
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
VGAScreen.prototype.vga_addr_shift_count = function()
|
2018-01-14 03:59:27 +01:00
|
|
|
{
|
|
|
|
// Count in multiples of 0x40 for convenience
|
|
|
|
// Left shift 2 for word mode - 2 bytes per dot clock
|
|
|
|
var shift_count = 0x80;
|
|
|
|
|
|
|
|
// Left shift 3 for byte mode - 1 byte per dot clock
|
|
|
|
shift_count += ~this.underline_location_register & this.crtc_mode & 0x40;
|
|
|
|
|
|
|
|
// Left shift 1 for doubleword mode - 4 bytes per dot clock
|
|
|
|
shift_count -= this.underline_location_register & 0x40;
|
|
|
|
|
|
|
|
// But shift one less if PEL width mode - 2 dot clocks per pixel
|
|
|
|
shift_count -= this.attribute_mode & 0x40;
|
|
|
|
|
|
|
|
return shift_count >>> 6;
|
2018-01-14 08:18:17 +01:00
|
|
|
};
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
VGAScreen.prototype.vga_addr_to_pixel = function(addr)
|
|
|
|
{
|
2018-01-14 05:33:02 +01:00
|
|
|
var shift_count = this.vga_addr_shift_count();
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
// Undo effects of substituted bits 13 and 14
|
|
|
|
// Assumptions:
|
|
|
|
// - max_scan_line register is set to the values shown below
|
|
|
|
// - Each scan line stays within the offset alignment
|
|
|
|
// - No panning and no page flipping after drawing
|
|
|
|
if(~this.crtc_mode & 0x3)
|
|
|
|
{
|
|
|
|
var pixel_addr = addr - this.start_address;
|
|
|
|
|
|
|
|
// Remove substituted bits
|
|
|
|
pixel_addr &= this.crtc_mode << 13 | ~0x6000;
|
|
|
|
|
|
|
|
// Convert to 1 pixel per address
|
|
|
|
pixel_addr <<= shift_count;
|
|
|
|
|
|
|
|
// Decompose address
|
|
|
|
var row = pixel_addr / this.virtual_width | 0;
|
|
|
|
var col = pixel_addr % this.virtual_width;
|
|
|
|
|
|
|
|
switch(this.crtc_mode & 0x3)
|
|
|
|
{
|
|
|
|
case 0x2:
|
|
|
|
// Alternating rows using bit 13
|
|
|
|
// Assumes max scan line = 1
|
|
|
|
row = row << 1 | (addr >> 13 & 0x1);
|
|
|
|
break;
|
|
|
|
case 0x1:
|
|
|
|
// Alternating rows using bit 14
|
|
|
|
// Assumes max scan line = 3
|
|
|
|
row = row << 1 | (addr >> 14 & 0x1);
|
|
|
|
break;
|
|
|
|
case 0x0:
|
|
|
|
// Cycling through rows using bit 13 and 14
|
|
|
|
// Assumes max scan line = 3
|
|
|
|
row = row << 2 | (addr >> 13 & 0x3);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reassemble address
|
|
|
|
return row * this.virtual_width + col + (this.start_address << shift_count);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Convert to 1 pixel per address
|
|
|
|
return addr << shift_count;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.scan_line_to_screen_row = function(scan_line)
|
|
|
|
{
|
|
|
|
// Double scanning. The clock to the row scan counter is halved
|
|
|
|
// so it is not affected by the memory address bit substitutions below
|
|
|
|
if(this.max_scan_line & 0x80)
|
|
|
|
{
|
|
|
|
scan_line >>>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Maximum scan line, aka scan lines per character row
|
|
|
|
// This is the number of repeats - 1 for graphic modes
|
|
|
|
var repeat_factor = 1 + (this.max_scan_line & 0x1F);
|
|
|
|
scan_line = Math.ceil(scan_line / repeat_factor);
|
|
|
|
|
|
|
|
// Odd and Even Row Scan Counter
|
|
|
|
// Despite repeated address counter values, because bit 13 of the shifted
|
|
|
|
// address is substituted with bit 0 of the row scan counter, a different
|
|
|
|
// display buffer address is generated instead of repeated
|
|
|
|
// Assumes maximum scan line register is set to 2 or 4.
|
|
|
|
// Note: can't assert this as register values may not be fully programmed.
|
|
|
|
if(!(this.crtc_mode & 0x1))
|
|
|
|
{
|
|
|
|
scan_line <<= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Undo effects of substituted bit 14
|
|
|
|
// Assumes maximum scan line register is set to 2 or 4
|
|
|
|
// Note: can't assert this as register values may not be fully programmed.
|
|
|
|
// Other maximum scan line register values would result in weird addressing
|
|
|
|
// anyway
|
|
|
|
if(!(this.crtc_mode & 0x2))
|
|
|
|
{
|
|
|
|
scan_line <<= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
return scan_line;
|
|
|
|
};
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
/**
|
2015-09-12 01:10:38 +02:00
|
|
|
* @param {number} cols_count
|
|
|
|
* @param {number} rows_count
|
2014-09-27 04:54:36 +02:00
|
|
|
*/
|
|
|
|
VGAScreen.prototype.set_size_text = function(cols_count, rows_count)
|
|
|
|
{
|
|
|
|
this.max_cols = cols_count;
|
|
|
|
this.max_rows = rows_count;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-12-25 02:32:18 +01:00
|
|
|
this.bus.send("screen-set-size-text", [cols_count, rows_count]);
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
VGAScreen.prototype.set_size_graphical = function(width, height, bpp, virtual_width, virtual_height)
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2018-01-23 01:42:05 +01:00
|
|
|
var needs_update = !this.stats.is_graphical ||
|
2018-01-23 02:20:57 +01:00
|
|
|
this.stats.bpp !== bpp ||
|
2018-01-23 01:42:05 +01:00
|
|
|
this.screen_width !== width ||
|
|
|
|
this.screen_height !== height ||
|
|
|
|
this.virtual_width !== virtual_width ||
|
|
|
|
this.virtual_height !== virtual_height;
|
|
|
|
|
|
|
|
if(needs_update)
|
|
|
|
{
|
|
|
|
this.screen_width = width;
|
|
|
|
this.screen_height = height;
|
|
|
|
this.virtual_width = virtual_width;
|
|
|
|
this.virtual_height = virtual_height;
|
|
|
|
|
2018-01-23 02:20:57 +01:00
|
|
|
this.stats.bpp = bpp;
|
2018-01-23 01:42:05 +01:00
|
|
|
this.stats.is_graphical = true;
|
|
|
|
this.stats.res_x = width;
|
|
|
|
this.stats.res_y = height;
|
|
|
|
|
2022-02-10 15:51:02 +01:00
|
|
|
if (typeof ImageData !== "undefined")
|
|
|
|
{
|
2022-03-09 19:47:03 +01:00
|
|
|
const size = virtual_width * virtual_height;
|
2022-02-25 12:53:42 +01:00
|
|
|
const offset = this.cpu.svga_allocate_dest_buffer(size) >>> 0;
|
2022-02-16 00:58:31 +01:00
|
|
|
|
|
|
|
this.dest_buffet_offset = offset;
|
2022-03-09 19:47:03 +01:00
|
|
|
this.image_data = new ImageData(new Uint8ClampedArray(this.cpu.wasm_memory.buffer, offset, 4 * size), virtual_width, virtual_height);
|
2022-02-16 00:58:31 +01:00
|
|
|
|
|
|
|
this.cpu.svga_mark_dirty();
|
2022-02-10 15:51:02 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// TODO: nodejs
|
|
|
|
}
|
|
|
|
|
2018-01-23 02:20:57 +01:00
|
|
|
this.bus.send("screen-set-size-graphical", [width, height, virtual_width, virtual_height, bpp]);
|
2018-01-23 01:42:05 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-15 06:29:59 +01:00
|
|
|
VGAScreen.prototype.update_vga_size = function()
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.svga_enabled)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
var horizontal_characters = Math.min(1 + this.horizontal_display_enable_end,
|
|
|
|
this.horizontal_blank_start);
|
|
|
|
var vertical_scans = Math.min(1 + this.vertical_display_enable_end,
|
|
|
|
this.vertical_blank_start);
|
|
|
|
|
2018-01-16 05:49:09 +01:00
|
|
|
if(!horizontal_characters || !vertical_scans)
|
|
|
|
{
|
|
|
|
// Don't update if width or height is zero.
|
|
|
|
// These happen when registers are not fully configured yet.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.graphical_mode)
|
|
|
|
{
|
2018-01-23 01:42:05 +01:00
|
|
|
var screen_width = horizontal_characters << 3;
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2018-01-16 06:06:35 +01:00
|
|
|
// Offset is half the number of bytes/words/dwords (depending on clocking mode)
|
2018-01-14 03:59:27 +01:00
|
|
|
// of display memory that each logical line occupies.
|
|
|
|
// However, the number of pixels latched, regardless of addressing mode,
|
2018-01-16 06:06:35 +01:00
|
|
|
// should always 8 pixels per character clock (except for 8 bit PEL width, in which
|
2018-01-14 03:59:27 +01:00
|
|
|
// case 4 pixels).
|
2018-01-23 01:42:05 +01:00
|
|
|
var virtual_width = this.offset_register << 4;
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
// Pixel Width / PEL Width / Clock Select
|
|
|
|
if(this.attribute_mode & 0x40)
|
|
|
|
{
|
2018-01-23 01:42:05 +01:00
|
|
|
screen_width >>>= 1;
|
|
|
|
virtual_width >>>= 1;
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
|
2018-01-23 01:42:05 +01:00
|
|
|
var screen_height = this.scan_line_to_screen_row(vertical_scans);
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
// The virtual buffer height is however many rows of data that can fit.
|
2018-01-14 03:59:27 +01:00
|
|
|
// Previously drawn graphics outside of current memory address space can
|
|
|
|
// still be drawn by setting start_address. The address at
|
|
|
|
// VGA_HOST_MEMORY_SPACE_START[memory_space_select] is mapped to the first
|
|
|
|
// byte of the frame buffer. Verified on some hardware.
|
|
|
|
// Depended on by: Windows 98 start screen
|
|
|
|
var available_bytes = VGA_HOST_MEMORY_SPACE_SIZE[0];
|
|
|
|
|
2018-01-23 01:42:05 +01:00
|
|
|
var virtual_height = Math.ceil(available_bytes / this.vga_bytes_per_line());
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2018-01-23 01:42:05 +01:00
|
|
|
this.set_size_graphical(screen_width, screen_height, 8,
|
|
|
|
virtual_width, virtual_height);
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
this.update_vertical_retrace();
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(this.max_scan_line & 0x80)
|
|
|
|
{
|
|
|
|
// Double scanning means that half of those scan lines
|
|
|
|
// are just repeats
|
|
|
|
vertical_scans >>>= 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
var height = vertical_scans / (1 + (this.max_scan_line & 0x1F)) | 0;
|
|
|
|
|
|
|
|
if(horizontal_characters && height)
|
|
|
|
{
|
|
|
|
this.set_size_text(horizontal_characters, height);
|
|
|
|
}
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-19 03:58:34 +01:00
|
|
|
VGAScreen.prototype.update_layers = function()
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
if(!this.graphical_mode)
|
|
|
|
{
|
|
|
|
this.text_mode_redraw();
|
|
|
|
}
|
2018-01-15 06:25:14 +01:00
|
|
|
|
2018-01-21 01:39:07 +01:00
|
|
|
if(this.svga_enabled)
|
2018-01-14 03:59:27 +01:00
|
|
|
{
|
2018-01-21 01:39:07 +01:00
|
|
|
this.layers = [];
|
2018-01-19 03:58:34 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-23 01:42:05 +01:00
|
|
|
if(!this.virtual_width || !this.screen_width)
|
2018-01-19 03:58:34 +01:00
|
|
|
{
|
|
|
|
// Avoid division by zero
|
2018-01-14 03:59:27 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-15 06:25:14 +01:00
|
|
|
if(!this.palette_source || (this.clocking_mode & 0x20))
|
|
|
|
{
|
|
|
|
// Palette source and screen disable bits = draw nothing
|
|
|
|
// See http://www.phatcode.net/res/224/files/html/ch29/29-05.html#Heading6
|
|
|
|
// and http://www.osdever.net/FreeVGA/vga/seqreg.htm#01
|
2018-01-21 01:39:07 +01:00
|
|
|
this.layers = [];
|
|
|
|
this.bus.send("screen-clear");
|
2018-01-15 06:25:14 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
var start_addr = this.start_address_latched;
|
|
|
|
|
|
|
|
var pixel_panning = this.horizontal_panning;
|
2018-01-16 06:06:35 +01:00
|
|
|
if(this.attribute_mode & 0x40)
|
|
|
|
{
|
|
|
|
pixel_panning >>>= 1;
|
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
var byte_panning = this.preset_row_scan >> 5 & 0x3;
|
|
|
|
var pixel_addr_start = this.vga_addr_to_pixel(start_addr + byte_panning);
|
|
|
|
|
|
|
|
var start_buffer_row = pixel_addr_start / this.virtual_width | 0;
|
2018-01-21 02:15:58 +01:00
|
|
|
var start_buffer_col = pixel_addr_start % this.virtual_width + pixel_panning;
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
var split_screen_row = this.scan_line_to_screen_row(1 + this.line_compare);
|
2018-01-23 01:42:05 +01:00
|
|
|
split_screen_row = Math.min(split_screen_row, this.screen_height);
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2018-01-23 01:42:05 +01:00
|
|
|
var split_buffer_height = this.screen_height - split_screen_row;
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-21 01:39:07 +01:00
|
|
|
this.layers = [];
|
2015-04-12 21:40:25 +02:00
|
|
|
|
2018-01-23 01:42:05 +01:00
|
|
|
for(var x = -start_buffer_col, y = 0; x < this.screen_width; x += this.virtual_width, y++)
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2018-01-21 01:39:07 +01:00
|
|
|
this.layers.push({
|
2022-02-10 15:51:02 +01:00
|
|
|
image_data: this.image_data,
|
2018-01-14 03:59:27 +01:00
|
|
|
screen_x: x,
|
|
|
|
screen_y: 0,
|
|
|
|
buffer_x: 0,
|
2018-01-21 01:39:07 +01:00
|
|
|
buffer_y: start_buffer_row + y,
|
2018-01-14 03:59:27 +01:00
|
|
|
buffer_width: this.virtual_width,
|
2018-01-21 01:39:07 +01:00
|
|
|
buffer_height: split_screen_row,
|
2018-01-14 03:59:27 +01:00
|
|
|
});
|
2014-09-27 04:54:36 +02:00
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
var start_split_col = 0;
|
|
|
|
if(!(this.attribute_mode & 0x20))
|
|
|
|
{
|
2018-01-16 06:06:35 +01:00
|
|
|
// Pixel panning mode. Allow panning for the lower split screen
|
2018-01-14 03:59:27 +01:00
|
|
|
start_split_col = this.vga_addr_to_pixel(byte_panning) + pixel_panning;
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-23 01:42:05 +01:00
|
|
|
for(var x = -start_split_col, y = 0; x < this.screen_width; x += this.virtual_width, y++)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-21 01:39:07 +01:00
|
|
|
this.layers.push({
|
2022-02-10 15:51:02 +01:00
|
|
|
image_data: this.image_data,
|
2018-01-14 03:59:27 +01:00
|
|
|
screen_x: x,
|
|
|
|
screen_y: split_screen_row,
|
|
|
|
buffer_x: 0,
|
2018-01-21 01:39:07 +01:00
|
|
|
buffer_y: y,
|
2018-01-14 03:59:27 +01:00
|
|
|
buffer_width: this.virtual_width,
|
2018-01-21 01:39:07 +01:00
|
|
|
buffer_height: split_buffer_height,
|
2018-01-14 03:59:27 +01:00
|
|
|
});
|
2014-09-27 04:54:36 +02:00
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.update_vertical_retrace = function()
|
|
|
|
{
|
|
|
|
// Emulate behaviour during VSync/VRetrace
|
|
|
|
this.port_3DA_value |= 0x8;
|
|
|
|
if(this.start_address_latched !== this.start_address)
|
|
|
|
{
|
|
|
|
this.start_address_latched = this.start_address;
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
VGAScreen.prototype.update_cursor_scanline = function()
|
|
|
|
{
|
|
|
|
this.bus.send("screen-update-cursor-scanline", [this.cursor_scanline_start, this.cursor_scanline_end]);
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
/**
|
|
|
|
* Attribute controller register / index write
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/attrreg.htm}
|
|
|
|
* @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 89
|
|
|
|
* @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 48
|
|
|
|
*/
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C0_write = function(value)
|
|
|
|
{
|
|
|
|
if(this.attribute_controller_index === -1)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("attribute controller index register: " + h(value), LOG_VGA);
|
|
|
|
this.attribute_controller_index = value & 0x1F;
|
|
|
|
dbg_log("attribute actual index: " + h(this.attribute_controller_index), LOG_VGA);
|
2018-01-15 06:25:14 +01:00
|
|
|
|
|
|
|
if(this.palette_source !== (value & 0x20))
|
|
|
|
{
|
|
|
|
// A method of blanking the screen.
|
|
|
|
// See http://www.phatcode.net/res/224/files/html/ch29/29-05.html#Heading6
|
|
|
|
this.palette_source = value & 0x20;
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-15 06:25:14 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(this.attribute_controller_index < 0x10)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("internal palette: " + h(this.attribute_controller_index) + " -> " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
this.dac_map[this.attribute_controller_index] = value;
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
if(!(this.attribute_mode & 0x40))
|
|
|
|
{
|
2018-01-19 02:53:51 +01:00
|
|
|
this.complete_redraw();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
else
|
2014-09-27 04:54:36 +02:00
|
|
|
switch(this.attribute_controller_index)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
case 0x10:
|
|
|
|
dbg_log("3C0 / attribute mode control: " + h(value), LOG_VGA);
|
|
|
|
if(this.attribute_mode !== value)
|
|
|
|
{
|
|
|
|
var previous_mode = this.attribute_mode;
|
|
|
|
this.attribute_mode = value;
|
|
|
|
|
|
|
|
var is_graphical = (value & 0x1) > 0;
|
|
|
|
if(!this.svga_enabled && this.graphical_mode !== is_graphical)
|
|
|
|
{
|
|
|
|
this.graphical_mode = is_graphical;
|
|
|
|
this.bus.send("screen-set-mode", this.graphical_mode);
|
|
|
|
}
|
|
|
|
|
|
|
|
if((previous_mode ^ value) & 0x40)
|
|
|
|
{
|
2018-01-19 02:53:51 +01:00
|
|
|
// PEL width changed. Pixel Buffer now invalidated
|
2018-01-14 03:59:27 +01:00
|
|
|
this.complete_replot();
|
|
|
|
}
|
|
|
|
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
// Data stored in image buffer are invalidated
|
|
|
|
this.complete_redraw();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x12:
|
|
|
|
dbg_log("3C0 / color plane enable: " + h(value), LOG_VGA);
|
|
|
|
if(this.color_plane_enable !== value)
|
|
|
|
{
|
|
|
|
this.color_plane_enable = value;
|
2018-01-14 05:33:02 +01:00
|
|
|
|
|
|
|
// Data stored in image buffer are invalidated
|
2018-01-14 03:59:27 +01:00
|
|
|
this.complete_redraw();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x13:
|
|
|
|
dbg_log("3C0 / horizontal panning: " + h(value), LOG_VGA);
|
|
|
|
if(this.horizontal_panning !== value)
|
|
|
|
{
|
|
|
|
this.horizontal_panning = value & 0xF;
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x14:
|
|
|
|
dbg_log("3C0 / color select: " + h(value), LOG_VGA);
|
|
|
|
if(this.color_select !== value)
|
|
|
|
{
|
|
|
|
this.color_select = value;
|
2018-01-14 05:33:02 +01:00
|
|
|
|
|
|
|
// Data stored in image buffer are invalidated
|
2018-01-14 03:59:27 +01:00
|
|
|
this.complete_redraw();
|
|
|
|
}
|
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
default:
|
|
|
|
dbg_log("3C0 / attribute controller write " + h(this.attribute_controller_index) + ": " + h(value), LOG_VGA);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
this.attribute_controller_index = -1;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C0_read = function()
|
|
|
|
{
|
|
|
|
dbg_log("3C0 read", LOG_VGA);
|
2018-01-15 06:25:14 +01:00
|
|
|
var result = this.attribute_controller_index | this.palette_source;
|
2014-09-27 04:54:36 +02:00
|
|
|
return result;
|
|
|
|
};
|
2013-12-06 01:38:03 +01:00
|
|
|
|
2015-02-16 01:58:40 +01:00
|
|
|
VGAScreen.prototype.port3C0_read16 = function()
|
|
|
|
{
|
|
|
|
dbg_log("3C0 read16", LOG_VGA);
|
|
|
|
return this.port3C0_read() & 0xFF | this.port3C1_read() << 8 & 0xFF00;
|
|
|
|
};
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C1_read = function()
|
|
|
|
{
|
2018-01-16 04:58:24 +01:00
|
|
|
if(this.attribute_controller_index < 0x10)
|
2018-01-14 03:59:27 +01:00
|
|
|
{
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("3C1 / internal palette read: " + h(this.attribute_controller_index) +
|
|
|
|
" -> " + h(this.dac_map[this.attribute_controller_index]), LOG_VGA);
|
2021-01-03 10:22:22 +01:00
|
|
|
return this.dac_map[this.attribute_controller_index] & 0xFF;
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
switch(this.attribute_controller_index)
|
2018-01-14 03:59:27 +01:00
|
|
|
{
|
|
|
|
case 0x10:
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("3C1 / attribute mode read: " + h(this.attribute_mode), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
return this.attribute_mode;
|
|
|
|
case 0x12:
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("3C1 / color plane enable read: " + h(this.color_plane_enable), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
return this.color_plane_enable;
|
|
|
|
case 0x13:
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("3C1 / horizontal panning read: " + h(this.horizontal_panning), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
return this.horizontal_panning;
|
|
|
|
case 0x14:
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("3C1 / color select read: " + h(this.color_select), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
return this.color_select;
|
|
|
|
default:
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("3C1 / attribute controller read " + h(this.attribute_controller_index), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
2021-01-03 10:22:22 +01:00
|
|
|
return 0xFF;
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C2_write = function(value)
|
|
|
|
{
|
|
|
|
dbg_log("3C2 / miscellaneous output register = " + h(value), LOG_VGA);
|
|
|
|
this.miscellaneous_output_register = value;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C4_write = function(value)
|
|
|
|
{
|
|
|
|
this.sequencer_index = value;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C4_read = function()
|
|
|
|
{
|
|
|
|
return this.sequencer_index;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
/**
|
|
|
|
* Sequencer register writes
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/seqreg.htm}
|
|
|
|
* @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 47
|
|
|
|
* @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 19
|
|
|
|
*/
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C5_write = function(value)
|
|
|
|
{
|
|
|
|
switch(this.sequencer_index)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-15 06:25:14 +01:00
|
|
|
case 0x01:
|
|
|
|
dbg_log("clocking mode: " + h(value), LOG_VGA);
|
|
|
|
var previous_clocking_mode = this.clocking_mode;
|
|
|
|
this.clocking_mode = value;
|
|
|
|
if((previous_clocking_mode ^ value) & 0x20)
|
|
|
|
{
|
|
|
|
// Screen disable bit modified
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-15 06:25:14 +01:00
|
|
|
}
|
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 0x02:
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("plane write mask: " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
this.plane_write_bm = value;
|
|
|
|
break;
|
|
|
|
case 0x04:
|
|
|
|
dbg_log("sequencer memory mode: " + h(value), LOG_VGA);
|
|
|
|
this.sequencer_memory_mode = value;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
dbg_log("3C5 / sequencer write " + h(this.sequencer_index) + ": " + h(value), LOG_VGA);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C5_read = function()
|
|
|
|
{
|
2017-04-02 21:24:48 +02:00
|
|
|
dbg_log("3C5 / sequencer read " + h(this.sequencer_index), LOG_VGA);
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
switch(this.sequencer_index)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-15 06:25:14 +01:00
|
|
|
case 0x01:
|
|
|
|
return this.clocking_mode;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 0x02:
|
|
|
|
return this.plane_write_bm;
|
|
|
|
case 0x04:
|
|
|
|
return this.sequencer_memory_mode;
|
|
|
|
case 0x06:
|
|
|
|
return 0x12;
|
|
|
|
default:
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
return 0;
|
|
|
|
};
|
|
|
|
|
2022-05-17 13:42:53 +02:00
|
|
|
VGAScreen.prototype.port3C6_write = function(data)
|
|
|
|
{
|
|
|
|
this.dac_mask = data;
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.port3C6_read = function()
|
|
|
|
{
|
|
|
|
return this.dac_mask;
|
|
|
|
};
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C7_write = function(index)
|
|
|
|
{
|
|
|
|
// index for reading the DAC
|
|
|
|
dbg_log("3C7 write: " + h(index), LOG_VGA);
|
2015-04-30 03:01:36 +02:00
|
|
|
this.dac_color_index_read = index * 3;
|
2018-01-15 22:45:30 +01:00
|
|
|
this.dac_state &= 0x0;
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.port3C7_read = function()
|
|
|
|
{
|
|
|
|
// prepared to accept reads or writes
|
|
|
|
return this.dac_state;
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.port3C8_write = function(index)
|
|
|
|
{
|
2015-04-30 03:01:36 +02:00
|
|
|
this.dac_color_index_write = index * 3;
|
2018-01-15 22:45:30 +01:00
|
|
|
this.dac_state |= 0x3;
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.port3C8_read = function()
|
|
|
|
{
|
2021-04-11 00:19:40 +02:00
|
|
|
return this.dac_color_index_write / 3 & 0xFF;
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
/**
|
|
|
|
* DAC color palette register writes
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/colorreg.htm}
|
|
|
|
* @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 104
|
|
|
|
* @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 57
|
|
|
|
*/
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3C9_write = function(color_byte)
|
|
|
|
{
|
2015-04-30 03:01:36 +02:00
|
|
|
var index = this.dac_color_index_write / 3 | 0,
|
|
|
|
offset = this.dac_color_index_write % 3,
|
2014-09-27 04:54:36 +02:00
|
|
|
color = this.vga256_palette[index];
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2022-06-23 05:38:02 +02:00
|
|
|
if((this.dispi_enable_value & 0x20) === 0)
|
|
|
|
{
|
|
|
|
color_byte &= 0x3F;
|
|
|
|
const b = color_byte & 1;
|
|
|
|
color_byte = color_byte << 2 | b << 1 | b;
|
|
|
|
}
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
if(offset === 0)
|
|
|
|
{
|
|
|
|
color = color & ~0xFF0000 | color_byte << 16;
|
|
|
|
}
|
|
|
|
else if(offset === 1)
|
|
|
|
{
|
|
|
|
color = color & ~0xFF00 | color_byte << 8;
|
|
|
|
}
|
|
|
|
else
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-09-27 04:54:36 +02:00
|
|
|
color = color & ~0xFF | color_byte;
|
|
|
|
dbg_log("dac set color, index=" + h(index) + " value=" + h(color), LOG_VGA);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.vga256_palette[index] !== color)
|
|
|
|
{
|
|
|
|
this.vga256_palette[index] = color;
|
2018-01-19 02:53:51 +01:00
|
|
|
this.complete_redraw();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
this.dac_color_index_write++;
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
2015-04-30 03:01:36 +02:00
|
|
|
VGAScreen.prototype.port3C9_read = function()
|
|
|
|
{
|
2018-01-14 05:33:02 +01:00
|
|
|
dbg_log("3C9 read", LOG_VGA);
|
2017-04-02 21:24:48 +02:00
|
|
|
|
2015-04-30 03:01:36 +02:00
|
|
|
var index = this.dac_color_index_read / 3 | 0;
|
|
|
|
var offset = this.dac_color_index_read % 3;
|
|
|
|
var color = this.vga256_palette[index];
|
2022-06-23 05:38:02 +02:00
|
|
|
var color8 = color >> (2 - offset) * 8 & 0xFF;
|
2015-04-30 03:01:36 +02:00
|
|
|
|
|
|
|
this.dac_color_index_read++;
|
2022-06-23 05:38:02 +02:00
|
|
|
|
|
|
|
if(this.dispi_enable_value & 0x20)
|
|
|
|
{
|
|
|
|
return color8;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return color8 >> 2;
|
|
|
|
}
|
2017-05-29 22:38:01 +02:00
|
|
|
};
|
2015-04-30 03:01:36 +02:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3CC_read = function()
|
|
|
|
{
|
2017-04-02 21:24:48 +02:00
|
|
|
dbg_log("3CC read", LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
return this.miscellaneous_output_register;
|
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.port3CE_write = function(value)
|
|
|
|
{
|
|
|
|
this.graphics_index = value;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3CE_read = function()
|
|
|
|
{
|
|
|
|
return this.graphics_index;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
/**
|
|
|
|
* Graphics controller register writes
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/graphreg.htm}
|
|
|
|
* @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 78
|
|
|
|
* @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 29
|
|
|
|
*/
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3CF_write = function(value)
|
|
|
|
{
|
|
|
|
switch(this.graphics_index)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2017-11-26 00:17:47 +01:00
|
|
|
case 0:
|
|
|
|
this.planar_setreset = value;
|
|
|
|
dbg_log("plane set/reset: " + h(value), LOG_VGA);
|
|
|
|
break;
|
|
|
|
case 1:
|
|
|
|
this.planar_setreset_enable = value;
|
|
|
|
dbg_log("plane set/reset enable: " + h(value), LOG_VGA);
|
|
|
|
break;
|
2017-12-15 00:37:37 +01:00
|
|
|
case 2:
|
|
|
|
this.color_compare = value;
|
|
|
|
dbg_log("color compare: " + h(value), LOG_VGA);
|
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 3:
|
|
|
|
this.planar_rotate_reg = value;
|
|
|
|
dbg_log("plane rotate: " + h(value), LOG_VGA);
|
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
this.plane_read = value;
|
|
|
|
dbg_log("plane read: " + h(value), LOG_VGA);
|
|
|
|
break;
|
|
|
|
case 5:
|
2018-01-14 03:59:27 +01:00
|
|
|
var previous_planar_mode = this.planar_mode;
|
2014-09-27 04:54:36 +02:00
|
|
|
this.planar_mode = value;
|
2017-03-04 21:53:31 +01:00
|
|
|
dbg_log("planar mode: " + h(value), LOG_VGA);
|
2018-01-14 03:59:27 +01:00
|
|
|
if((previous_planar_mode ^ value) & 0x60)
|
|
|
|
{
|
|
|
|
// Shift mode modified. Pixel buffer invalidated
|
|
|
|
this.complete_replot();
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 6:
|
|
|
|
dbg_log("miscellaneous graphics register: " + h(value), LOG_VGA);
|
|
|
|
if(this.miscellaneous_graphics_register !== value)
|
|
|
|
{
|
|
|
|
this.miscellaneous_graphics_register = value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
2017-12-15 00:37:37 +01:00
|
|
|
case 7:
|
|
|
|
this.color_dont_care = value;
|
|
|
|
dbg_log("color don't care: " + h(value), LOG_VGA);
|
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 8:
|
|
|
|
this.planar_bitmap = value;
|
2017-03-04 21:53:31 +01:00
|
|
|
dbg_log("planar bitmap: " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
|
|
|
default:
|
2017-03-04 21:53:31 +01:00
|
|
|
dbg_log("3CF / graphics write " + h(this.graphics_index) + ": " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
}
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3CF_read = function()
|
|
|
|
{
|
2017-04-02 21:24:48 +02:00
|
|
|
dbg_log("3CF / graphics read " + h(this.graphics_index), LOG_VGA);
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
switch(this.graphics_index)
|
|
|
|
{
|
2017-11-26 00:17:47 +01:00
|
|
|
case 0:
|
|
|
|
return this.planar_setreset;
|
|
|
|
case 1:
|
|
|
|
return this.planar_setreset_enable;
|
2017-12-15 00:37:37 +01:00
|
|
|
case 2:
|
|
|
|
return this.color_compare;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 3:
|
|
|
|
return this.planar_rotate_reg;
|
|
|
|
case 4:
|
|
|
|
return this.plane_read;
|
|
|
|
case 5:
|
|
|
|
return this.planar_mode;
|
2018-01-14 03:59:27 +01:00
|
|
|
case 6:
|
|
|
|
return this.miscellaneous_graphics_register;
|
2017-12-15 00:37:37 +01:00
|
|
|
case 7:
|
|
|
|
return this.color_dont_care;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 8:
|
|
|
|
return this.planar_bitmap;
|
|
|
|
default:
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
return 0;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3D4_write = function(register)
|
|
|
|
{
|
2018-02-14 05:05:28 +01:00
|
|
|
dbg_log("3D4 / crtc index: " + register, LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
this.index_crtc = register;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-02-14 05:05:28 +01:00
|
|
|
VGAScreen.prototype.port3D4_read = function()
|
|
|
|
{
|
|
|
|
dbg_log("3D4 read / crtc index: " + this.index_crtc, LOG_VGA);
|
|
|
|
return this.index_crtc;
|
|
|
|
};
|
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
/**
|
|
|
|
* CRT controller register writes
|
|
|
|
* @see {@link http://www.osdever.net/FreeVGA/vga/crtcreg.htm}
|
|
|
|
* @see {@link http://www.mcamafia.de/pdf/ibm_vgaxga_trm2.pdf} page 55
|
|
|
|
* @see {@link https://01.org/sites/default/files/documentation/intel-gfx-prm-osrc-hsw-display_0.pdf} page 63
|
|
|
|
*/
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port3D5_write = function(value)
|
|
|
|
{
|
|
|
|
switch(this.index_crtc)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
case 0x1:
|
|
|
|
dbg_log("3D5 / hdisp enable end write: " + h(value), LOG_VGA);
|
|
|
|
if(this.horizontal_display_enable_end !== value)
|
|
|
|
{
|
|
|
|
this.horizontal_display_enable_end = value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
break;
|
2014-10-13 21:58:49 +02:00
|
|
|
case 0x2:
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.horizontal_blank_start !== value)
|
|
|
|
{
|
|
|
|
this.horizontal_blank_start = value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
2014-10-13 21:58:49 +02:00
|
|
|
break;
|
2018-01-14 03:59:27 +01:00
|
|
|
case 0x7:
|
|
|
|
dbg_log("3D5 / overflow register write: " + h(value), LOG_VGA);
|
|
|
|
var previous_vertical_display_enable_end = this.vertical_display_enable_end;
|
|
|
|
this.vertical_display_enable_end &= 0xFF;
|
|
|
|
this.vertical_display_enable_end |= (value << 3 & 0x200) | (value << 7 & 0x100);
|
|
|
|
if(previous_vertical_display_enable_end != this.vertical_display_enable_end)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
this.line_compare = (this.line_compare & 0x2FF) | (value << 4 & 0x100);
|
|
|
|
|
|
|
|
var previous_vertical_blank_start = this.vertical_blank_start;
|
|
|
|
this.vertical_blank_start = (this.vertical_blank_start & 0x2FF) | (value << 5 & 0x100);
|
|
|
|
if(previous_vertical_blank_start !== this.vertical_blank_start)
|
|
|
|
{
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
break;
|
|
|
|
case 0x8:
|
|
|
|
dbg_log("3D5 / preset row scan write: " + h(value), LOG_VGA);
|
|
|
|
this.preset_row_scan = value;
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
break;
|
|
|
|
case 0x9:
|
|
|
|
dbg_log("3D5 / max scan line write: " + h(value), LOG_VGA);
|
|
|
|
this.max_scan_line = value;
|
|
|
|
this.line_compare = (this.line_compare & 0x1FF) | (value << 3 & 0x200);
|
|
|
|
|
|
|
|
var previous_vertical_blank_start = this.vertical_blank_start;
|
|
|
|
this.vertical_blank_start = (this.vertical_blank_start & 0x1FF) | (value << 4 & 0x200);
|
|
|
|
if(previous_vertical_blank_start !== this.vertical_blank_start)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
|
|
|
case 0xA:
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("3D5 / cursor scanline start write: " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
this.cursor_scanline_start = value;
|
|
|
|
this.update_cursor_scanline();
|
|
|
|
break;
|
|
|
|
case 0xB:
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("3D5 / cursor scanline end write: " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
this.cursor_scanline_end = value;
|
|
|
|
this.update_cursor_scanline();
|
|
|
|
break;
|
|
|
|
case 0xC:
|
2018-01-14 03:59:27 +01:00
|
|
|
if((this.start_address >> 8 & 0xFF) !== value)
|
|
|
|
{
|
|
|
|
this.start_address = this.start_address & 0xff | value << 8;
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
if(~this.crtc_mode & 0x3)
|
|
|
|
{
|
|
|
|
// Address substitution implementation depends on the
|
|
|
|
// starting row and column, so the pixel buffer is invalidated.
|
|
|
|
this.complete_replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
dbg_log("3D5 / start addr hi write: " + h(value) + " -> " + h(this.start_address, 4), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
|
|
|
case 0xD:
|
2018-01-14 03:59:27 +01:00
|
|
|
if((this.start_address & 0xFF) !== value)
|
2017-03-04 21:53:31 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
this.start_address = this.start_address & 0xff00 | value;
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
if(~this.crtc_mode & 0x3)
|
2017-03-04 21:53:31 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
// Address substitution implementation depends on the
|
|
|
|
// starting row and column, so the pixel buffer is invalidated.
|
|
|
|
this.complete_replot();
|
2017-03-04 21:53:31 +01:00
|
|
|
}
|
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("3D5 / start addr lo write: " + h(value) + " -> " + h(this.start_address, 4), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
|
|
|
case 0xE:
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("3D5 / cursor address hi write: " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
this.cursor_address = this.cursor_address & 0xFF | value << 8;
|
|
|
|
this.update_cursor();
|
|
|
|
break;
|
|
|
|
case 0xF:
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("3D5 / cursor address lo write: " + h(value), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
this.cursor_address = this.cursor_address & 0xFF00 | value;
|
|
|
|
this.update_cursor();
|
|
|
|
break;
|
2018-01-14 03:59:27 +01:00
|
|
|
case 0x12:
|
|
|
|
dbg_log("3D5 / vdisp enable end write: " + h(value), LOG_VGA);
|
|
|
|
if((this.vertical_display_enable_end & 0xFF) !== value)
|
|
|
|
{
|
|
|
|
this.vertical_display_enable_end = (this.vertical_display_enable_end & 0x300) | value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
break;
|
2016-02-14 18:21:19 +01:00
|
|
|
case 0x13:
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("3D5 / offset register write: " + h(value), LOG_VGA);
|
|
|
|
if(this.offset_register !== value)
|
|
|
|
{
|
|
|
|
this.offset_register = value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
if(~this.crtc_mode & 0x3)
|
|
|
|
{
|
|
|
|
// Address substitution implementation depends on the
|
|
|
|
// virtual width, so the pixel buffer is invalidated.
|
|
|
|
this.complete_replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x14:
|
|
|
|
dbg_log("3D5 / underline location write: " + h(value), LOG_VGA);
|
|
|
|
if(this.underline_location_register !== value)
|
|
|
|
{
|
|
|
|
var previous_underline = this.underline_location_register;
|
|
|
|
|
|
|
|
this.underline_location_register = value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
if((previous_underline ^ value) & 0x40)
|
|
|
|
{
|
|
|
|
// Doubleword addressing changed. Pixel buffer invalidated.
|
|
|
|
this.complete_replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x15:
|
|
|
|
dbg_log("3D5 / vertical blank start write: " + h(value), LOG_VGA);
|
|
|
|
if((this.vertical_blank_start & 0xFF) !== value)
|
|
|
|
{
|
|
|
|
this.vertical_blank_start = (this.vertical_blank_start & 0x300) | value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x17:
|
|
|
|
dbg_log("3D5 / crtc mode write: " + h(value), LOG_VGA);
|
|
|
|
if(this.crtc_mode !== value)
|
|
|
|
{
|
|
|
|
var previous_mode = this.crtc_mode;
|
|
|
|
|
|
|
|
this.crtc_mode = value;
|
2018-01-15 06:29:59 +01:00
|
|
|
this.update_vga_size();
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
if((previous_mode ^ value) & 0x43)
|
|
|
|
{
|
|
|
|
// Word/byte addressing changed or address substitution changed.
|
|
|
|
// Pixel buffer invalidated.
|
|
|
|
this.complete_replot();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 0x18:
|
|
|
|
dbg_log("3D5 / line compare write: " + h(value), LOG_VGA);
|
|
|
|
this.line_compare = (this.line_compare & 0x300) | value;
|
2018-01-19 03:58:34 +01:00
|
|
|
this.update_layers();
|
2016-02-14 18:21:19 +01:00
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
default:
|
2017-03-04 21:53:31 +01:00
|
|
|
if(this.index_crtc < this.crtc.length)
|
|
|
|
{
|
|
|
|
this.crtc[this.index_crtc] = value;
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
dbg_log("3D5 / CRTC write " + h(this.index_crtc) + ": " + h(value), LOG_VGA);
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.port3D5_read = function()
|
|
|
|
{
|
2017-04-02 21:24:48 +02:00
|
|
|
dbg_log("3D5 read " + h(this.index_crtc), LOG_VGA);
|
|
|
|
|
2016-02-14 18:14:42 +01:00
|
|
|
switch(this.index_crtc)
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
case 0x1:
|
|
|
|
return this.horizontal_display_enable_end;
|
|
|
|
case 0x2:
|
|
|
|
return this.horizontal_blank_start;
|
|
|
|
case 0x7:
|
|
|
|
return (this.vertical_display_enable_end >> 7 & 0x2) |
|
|
|
|
(this.vertical_blank_start >> 5 & 0x8) |
|
|
|
|
(this.line_compare >> 4 & 0x10) |
|
|
|
|
(this.vertical_display_enable_end >> 3 & 0x40);
|
|
|
|
case 0x8:
|
|
|
|
return this.preset_row_scan;
|
2016-02-14 18:14:42 +01:00
|
|
|
case 0x9:
|
|
|
|
return this.max_scan_line;
|
|
|
|
case 0xA:
|
|
|
|
return this.cursor_scanline_start;
|
|
|
|
case 0xB:
|
|
|
|
return this.cursor_scanline_end;
|
2016-02-14 18:21:19 +01:00
|
|
|
case 0xC:
|
|
|
|
return this.start_address & 0xFF;
|
|
|
|
case 0xD:
|
|
|
|
return this.start_address >> 8;
|
2016-02-14 18:14:42 +01:00
|
|
|
case 0xE:
|
|
|
|
return this.cursor_address >> 8;
|
|
|
|
case 0xF:
|
|
|
|
return this.cursor_address & 0xFF;
|
2017-03-04 21:53:31 +01:00
|
|
|
case 0x12:
|
2018-01-14 03:59:27 +01:00
|
|
|
return this.vertical_display_enable_end & 0xFF;
|
2016-02-14 18:21:19 +01:00
|
|
|
case 0x13:
|
|
|
|
return this.offset_register;
|
2018-01-14 03:59:27 +01:00
|
|
|
case 0x14:
|
|
|
|
return this.underline_location_register;
|
|
|
|
case 0x15:
|
|
|
|
return this.vertical_blank_start & 0xFF;
|
|
|
|
case 0x17:
|
|
|
|
return this.crtc_mode;
|
|
|
|
case 0x18:
|
|
|
|
return this.line_compare & 0xFF;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2017-03-04 21:53:31 +01:00
|
|
|
if(this.index_crtc < this.crtc.length)
|
|
|
|
{
|
|
|
|
return this.crtc[this.index_crtc];
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 0;
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
VGAScreen.prototype.port3DA_read = function()
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
dbg_log("3DA read - status 1 and clear attr index", LOG_VGA);
|
2017-04-02 21:24:48 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
var value = this.port_3DA_value;
|
2018-01-14 07:10:04 +01:00
|
|
|
|
|
|
|
// Status register, bit 3 set by update_vertical_retrace
|
|
|
|
// during screen-fill-buffer
|
|
|
|
if(!this.graphical_mode)
|
|
|
|
{
|
|
|
|
// But screen-fill-buffer may not get triggered in text mode
|
|
|
|
// so toggle it manually here
|
|
|
|
if(this.port_3DA_value & 1)
|
|
|
|
{
|
|
|
|
this.port_3DA_value ^= 8;
|
|
|
|
}
|
|
|
|
this.port_3DA_value ^= 1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.port_3DA_value ^= 1;
|
|
|
|
this.port_3DA_value &= 1;
|
|
|
|
}
|
2017-05-29 22:38:01 +02:00
|
|
|
this.attribute_controller_index = -1;
|
2018-01-14 03:59:27 +01:00
|
|
|
return value;
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port1CE_write = function(value)
|
|
|
|
{
|
|
|
|
this.dispi_index = value;
|
|
|
|
};
|
2014-07-14 22:19:59 +02:00
|
|
|
|
2014-10-21 21:51:42 +02:00
|
|
|
VGAScreen.prototype.port1CF_write = function(value)
|
2014-09-27 04:54:36 +02:00
|
|
|
{
|
2014-10-21 21:51:42 +02:00
|
|
|
dbg_log("1CF / dispi write " + h(this.dispi_index) + ": " + h(value), LOG_VGA);
|
2013-11-25 20:02:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
switch(this.dispi_index)
|
|
|
|
{
|
2022-11-21 18:23:00 +01:00
|
|
|
case 0:
|
|
|
|
if(value >= 0xB0C0 && value <= 0xB0C5)
|
|
|
|
{
|
|
|
|
this.svga_version = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
dbg_log("Invalid version value: " + h(value), LOG_VGA);
|
|
|
|
}
|
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 1:
|
2014-10-21 21:51:42 +02:00
|
|
|
this.svga_width = value;
|
2014-09-27 04:54:36 +02:00
|
|
|
if(this.svga_width > MAX_XRES)
|
|
|
|
{
|
|
|
|
dbg_log("svga_width reduced from " + this.svga_width + " to " + MAX_XRES, LOG_VGA);
|
|
|
|
this.svga_width = MAX_XRES;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 2:
|
2014-10-21 21:51:42 +02:00
|
|
|
this.svga_height = value;
|
2014-09-27 04:54:36 +02:00
|
|
|
if(this.svga_height > MAX_YRES)
|
|
|
|
{
|
|
|
|
dbg_log("svga_height reduced from " + this.svga_height + " to " + MAX_YRES, LOG_VGA);
|
|
|
|
this.svga_height = MAX_YRES;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case 3:
|
2014-10-21 21:51:42 +02:00
|
|
|
this.svga_bpp = value;
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
|
|
|
case 4:
|
|
|
|
// enable, options
|
2014-10-21 21:51:42 +02:00
|
|
|
this.svga_enabled = (value & 1) === 1;
|
|
|
|
this.dispi_enable_value = value;
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
2015-04-17 22:28:51 +02:00
|
|
|
case 5:
|
2022-06-21 03:00:30 +02:00
|
|
|
dbg_log("SVGA bank offset: " + h(value << 16), LOG_VGA);
|
2015-04-17 22:28:51 +02:00
|
|
|
this.svga_bank_offset = value << 16;
|
|
|
|
break;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 9:
|
|
|
|
// y offset
|
2022-02-16 00:58:31 +01:00
|
|
|
const offset = value * this.svga_width;
|
|
|
|
dbg_log("SVGA offset: " + h(offset) + " y=" + h(value), LOG_VGA);
|
|
|
|
if(this.svga_offset !== offset)
|
|
|
|
{
|
|
|
|
this.svga_offset = offset;
|
|
|
|
this.complete_redraw();
|
|
|
|
}
|
2014-09-27 04:54:36 +02:00
|
|
|
break;
|
|
|
|
default:
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
if(this.svga_enabled && (!this.svga_width || !this.svga_height))
|
2013-11-06 01:12:55 +01:00
|
|
|
{
|
2014-09-27 04:54:36 +02:00
|
|
|
dbg_log("SVGA: disabled because of invalid width/height: " + this.svga_width + "x" + this.svga_height, LOG_VGA);
|
|
|
|
this.svga_enabled = false;
|
|
|
|
}
|
2013-12-06 01:38:03 +01:00
|
|
|
|
2015-04-12 21:40:25 +02:00
|
|
|
dbg_assert(this.svga_bpp !== 4, "unimplemented svga bpp: 4");
|
2015-09-12 01:10:38 +02:00
|
|
|
dbg_assert(this.svga_bpp === 4 || this.svga_bpp === 8 ||
|
|
|
|
this.svga_bpp === 15 || this.svga_bpp === 16 ||
|
2015-04-12 21:40:25 +02:00
|
|
|
this.svga_bpp === 24 || this.svga_bpp === 32,
|
|
|
|
"unexpected svga bpp: " + this.svga_bpp);
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
dbg_log("SVGA: enabled=" + this.svga_enabled + ", " + this.svga_width + "x" + this.svga_height + "x" + this.svga_bpp, LOG_VGA);
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
if(this.svga_enabled && this.dispi_index === 4)
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
this.set_size_graphical(this.svga_width, this.svga_height, this.svga_bpp, this.svga_width, this.svga_height);
|
2014-12-25 02:32:18 +01:00
|
|
|
this.bus.send("screen-set-mode", true);
|
2015-04-17 22:28:51 +02:00
|
|
|
this.graphical_mode = true;
|
|
|
|
this.graphical_mode_is_linear = true;
|
|
|
|
}
|
2014-01-05 03:19:09 +01:00
|
|
|
|
2015-04-17 22:28:51 +02:00
|
|
|
if(!this.svga_enabled)
|
|
|
|
{
|
|
|
|
this.svga_bank_offset = 0;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
2018-01-19 03:58:34 +01:00
|
|
|
|
|
|
|
this.update_layers();
|
2014-09-27 04:54:36 +02:00
|
|
|
};
|
2014-07-14 22:19:59 +02:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.port1CF_read = function()
|
|
|
|
{
|
2014-10-21 21:51:42 +02:00
|
|
|
dbg_log("1CF / dispi read " + h(this.dispi_index), LOG_VGA);
|
2014-09-27 04:54:36 +02:00
|
|
|
return this.svga_register_read(this.dispi_index);
|
|
|
|
};
|
2014-07-14 22:19:59 +02:00
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
VGAScreen.prototype.svga_register_read = function(n)
|
|
|
|
{
|
|
|
|
switch(n)
|
2014-07-14 22:19:59 +02:00
|
|
|
{
|
2014-09-27 04:54:36 +02:00
|
|
|
case 0:
|
2022-11-21 18:23:00 +01:00
|
|
|
return this.svga_version;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 1:
|
2014-10-21 21:51:42 +02:00
|
|
|
return this.dispi_enable_value & 2 ? MAX_XRES : this.svga_width;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 2:
|
2014-10-21 21:51:42 +02:00
|
|
|
return this.dispi_enable_value & 2 ? MAX_YRES : this.svga_height;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 3:
|
2014-10-21 21:51:42 +02:00
|
|
|
return this.dispi_enable_value & 2 ? MAX_BPP : this.svga_bpp;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 4:
|
|
|
|
return this.dispi_enable_value;
|
2015-04-17 22:28:51 +02:00
|
|
|
case 5:
|
|
|
|
return this.svga_bank_offset >>> 16;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 6:
|
|
|
|
// virtual width
|
2016-02-05 14:18:44 +01:00
|
|
|
if(this.screen_width)
|
|
|
|
{
|
|
|
|
return this.screen_width;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
return 1; // seabios/windows98 divide exception
|
|
|
|
}
|
2017-05-29 22:38:01 +02:00
|
|
|
break;
|
|
|
|
|
2015-04-17 22:49:49 +02:00
|
|
|
case 8:
|
|
|
|
// x offset
|
|
|
|
return 0;
|
2014-09-27 04:54:36 +02:00
|
|
|
case 0x0A:
|
|
|
|
// memory size in 64 kilobyte banks
|
|
|
|
return this.vga_memory_size / VGA_BANK_SIZE | 0;
|
2013-11-06 01:12:55 +01:00
|
|
|
}
|
|
|
|
|
2014-09-27 04:54:36 +02:00
|
|
|
return 0xFF;
|
|
|
|
};
|
2013-11-06 01:12:55 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
/**
|
|
|
|
* Transfers graphics from VGA Planes to the Pixel Buffer
|
|
|
|
* VGA Planes represent data stored on actual hardware.
|
|
|
|
* Pixel Buffer caches the 4-bit or 8-bit color indices for each pixel.
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.vga_replot = function()
|
2015-04-12 21:40:25 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
// Round to multiple of 8 towards extreme
|
|
|
|
var start = this.diff_plot_min & ~0xF;
|
2018-01-14 09:42:43 +01:00
|
|
|
var end = Math.min((this.diff_plot_max | 0xF), VGA_PIXEL_BUFFER_SIZE - 1);
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2018-01-14 05:33:02 +01:00
|
|
|
var addr_shift = this.vga_addr_shift_count();
|
2018-01-14 03:59:27 +01:00
|
|
|
var addr_substitution = ~this.crtc_mode & 0x3;
|
|
|
|
|
2021-01-03 07:51:29 +01:00
|
|
|
var shift_mode = this.planar_mode & 0x60;
|
2018-01-14 03:59:27 +01:00
|
|
|
var pel_width = this.attribute_mode & 0x40;
|
|
|
|
|
|
|
|
for(var pixel_addr = start; pixel_addr <= end;)
|
2015-04-12 21:40:25 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
var addr = pixel_addr >>> addr_shift;
|
|
|
|
if(addr_substitution)
|
|
|
|
{
|
|
|
|
var row = pixel_addr / this.virtual_width | 0;
|
|
|
|
var col = pixel_addr - this.virtual_width * row;
|
|
|
|
|
|
|
|
switch(addr_substitution)
|
|
|
|
{
|
|
|
|
case 0x1:
|
|
|
|
// Alternating rows using bit 13
|
|
|
|
// Assumes max scan line = 1
|
|
|
|
addr = (row & 0x1) << 13;
|
|
|
|
row >>>= 1;
|
|
|
|
break;
|
|
|
|
case 0x2:
|
|
|
|
// Alternating rows using bit 14
|
|
|
|
// Assumes max scan line = 3
|
|
|
|
addr = (row & 0x1) << 14;
|
|
|
|
row >>>= 1;
|
|
|
|
break;
|
|
|
|
case 0x3:
|
|
|
|
// Cycling through rows using bit 13 and 14
|
|
|
|
// Assumes max scan line = 3
|
|
|
|
addr = (row & 0x3) << 13;
|
|
|
|
row >>>= 2;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
addr |= (row * this.virtual_width + col >>> addr_shift) + this.start_address;
|
|
|
|
}
|
|
|
|
|
|
|
|
var byte0 = this.plane0[addr];
|
|
|
|
var byte1 = this.plane1[addr];
|
|
|
|
var byte2 = this.plane2[addr];
|
|
|
|
var byte3 = this.plane3[addr];
|
|
|
|
|
|
|
|
var shift_loads = new Uint8Array(8);
|
|
|
|
switch(shift_mode)
|
|
|
|
{
|
|
|
|
// Planar Shift Mode
|
|
|
|
// See http://www.osdever.net/FreeVGA/vga/vgaseq.htm
|
|
|
|
case 0x00:
|
|
|
|
// Shift these, so that the bits for the color are in
|
|
|
|
// the correct position in the for loop
|
|
|
|
byte0 <<= 0;
|
|
|
|
byte1 <<= 1;
|
|
|
|
byte2 <<= 2;
|
|
|
|
byte3 <<= 3;
|
|
|
|
|
|
|
|
for(var i = 7; i >= 0; i--)
|
|
|
|
{
|
|
|
|
shift_loads[7 - i] =
|
|
|
|
byte0 >> i & 1 |
|
|
|
|
byte1 >> i & 2 |
|
|
|
|
byte2 >> i & 4 |
|
|
|
|
byte3 >> i & 8;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
// Packed Shift Mode, aka Interleaved Shift Mode
|
|
|
|
// Video Modes 4h and 5h
|
|
|
|
case 0x20:
|
|
|
|
shift_loads[0] = (byte0 >> 6 & 0x3) | (byte2 >> 4 & 0xC);
|
|
|
|
shift_loads[1] = (byte0 >> 4 & 0x3) | (byte2 >> 2 & 0xC);
|
|
|
|
shift_loads[2] = (byte0 >> 2 & 0x3) | (byte2 >> 0 & 0xC);
|
|
|
|
shift_loads[3] = (byte0 >> 0 & 0x3) | (byte2 << 2 & 0xC);
|
|
|
|
|
|
|
|
shift_loads[4] = (byte1 >> 6 & 0x3) | (byte3 >> 4 & 0xC);
|
|
|
|
shift_loads[5] = (byte1 >> 4 & 0x3) | (byte3 >> 2 & 0xC);
|
|
|
|
shift_loads[6] = (byte1 >> 2 & 0x3) | (byte3 >> 0 & 0xC);
|
|
|
|
shift_loads[7] = (byte1 >> 0 & 0x3) | (byte3 << 2 & 0xC);
|
|
|
|
break;
|
|
|
|
|
|
|
|
// 256-Color Shift Mode
|
|
|
|
// Video Modes 13h and unchained 256 color
|
|
|
|
case 0x40:
|
|
|
|
case 0x60:
|
|
|
|
shift_loads[0] = byte0 >> 4 & 0xF;
|
|
|
|
shift_loads[1] = byte0 >> 0 & 0xF;
|
|
|
|
shift_loads[2] = byte1 >> 4 & 0xF;
|
|
|
|
shift_loads[3] = byte1 >> 0 & 0xF;
|
|
|
|
shift_loads[4] = byte2 >> 4 & 0xF;
|
|
|
|
shift_loads[5] = byte2 >> 0 & 0xF;
|
|
|
|
shift_loads[6] = byte3 >> 4 & 0xF;
|
|
|
|
shift_loads[7] = byte3 >> 0 & 0xF;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(pel_width)
|
|
|
|
{
|
|
|
|
// Assemble from two sets of 4 bits.
|
|
|
|
for(var i = 0, j = 0; i < 4; i++, pixel_addr++, j += 2)
|
|
|
|
{
|
2018-01-19 02:53:51 +01:00
|
|
|
this.pixel_buffer[pixel_addr] = (shift_loads[j] << 4) | shift_loads[j + 1];
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for(var i = 0; i < 8; i++, pixel_addr++)
|
|
|
|
{
|
|
|
|
this.pixel_buffer[pixel_addr] = shift_loads[i];
|
|
|
|
}
|
|
|
|
}
|
2015-04-12 21:40:25 +02:00
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
};
|
2015-04-12 21:40:25 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
/**
|
|
|
|
* Transfers graphics from Pixel Buffer to Destination Image Buffer.
|
|
|
|
* The 4-bit/8-bit color indices in the Pixel Buffer are passed through
|
|
|
|
* the internal palette (dac_map) and the DAC palette (vga256_palette) to
|
|
|
|
* obtain the final 32 bit color that the Canvas API uses.
|
|
|
|
*/
|
|
|
|
VGAScreen.prototype.vga_redraw = function()
|
|
|
|
{
|
|
|
|
var start = this.diff_addr_min;
|
2018-01-15 09:47:43 +01:00
|
|
|
var end = Math.min(this.diff_addr_max, VGA_PIXEL_BUFFER_SIZE - 1);
|
2022-03-09 19:47:03 +01:00
|
|
|
const buffer = new Int32Array(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, this.virtual_width * this.virtual_height);
|
2018-01-14 03:59:27 +01:00
|
|
|
|
|
|
|
var mask = 0xFF;
|
|
|
|
var colorset = 0x00;
|
|
|
|
if(this.attribute_mode & 0x80)
|
2015-04-12 21:40:25 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
// Palette bits 5/4 select
|
|
|
|
mask &= 0xCF;
|
|
|
|
colorset |= this.color_select << 4 & 0x30;
|
2015-04-12 21:40:25 +02:00
|
|
|
}
|
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.attribute_mode & 0x40)
|
2015-04-12 21:40:25 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
// 8 bit mode
|
|
|
|
|
|
|
|
for(var pixel_addr = start; pixel_addr <= end; pixel_addr++)
|
|
|
|
{
|
|
|
|
var color256 = (this.pixel_buffer[pixel_addr] & mask) | colorset;
|
|
|
|
var color = this.vga256_palette[color256];
|
|
|
|
|
|
|
|
buffer[pixel_addr] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
|
|
|
|
}
|
2015-04-12 21:40:25 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
// 4 bit mode
|
|
|
|
|
|
|
|
// Palette bits 7/6 select
|
|
|
|
mask &= 0x3F;
|
|
|
|
colorset |= this.color_select << 4 & 0xC0;
|
2018-01-19 02:53:51 +01:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
for(var pixel_addr = start; pixel_addr <= end; pixel_addr++)
|
2015-04-12 21:40:25 +02:00
|
|
|
{
|
2018-01-14 03:59:27 +01:00
|
|
|
var color16 = this.pixel_buffer[pixel_addr] & this.color_plane_enable;
|
|
|
|
var color256 = (this.dac_map[color16] & mask) | colorset;
|
|
|
|
var color = this.vga256_palette[color256];
|
|
|
|
|
|
|
|
buffer[pixel_addr] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
|
2015-04-12 21:40:25 +02:00
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
};
|
2015-04-12 21:40:25 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
VGAScreen.prototype.screen_fill_buffer = function()
|
|
|
|
{
|
|
|
|
if(!this.graphical_mode)
|
|
|
|
{
|
|
|
|
// text mode
|
2018-01-16 05:49:09 +01:00
|
|
|
// Update retrace behaviour anyway - programs waiting for signal before
|
2018-01-14 07:10:04 +01:00
|
|
|
// changing to graphical mode
|
|
|
|
this.update_vertical_retrace();
|
2018-01-14 03:59:27 +01:00
|
|
|
return;
|
|
|
|
}
|
2015-04-12 21:40:25 +02:00
|
|
|
|
2022-02-16 00:58:31 +01:00
|
|
|
if(this.image_data.data.byteLength === 0)
|
2015-04-12 21:40:25 +02:00
|
|
|
{
|
2022-02-16 00:58:31 +01:00
|
|
|
// wasm memory resized
|
2022-03-09 19:47:03 +01:00
|
|
|
const buffer = new Uint8ClampedArray(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, 4 * this.virtual_width * this.virtual_height);
|
|
|
|
this.image_data = new ImageData(buffer, this.virtual_width, this.virtual_height);
|
2022-02-16 00:58:31 +01:00
|
|
|
this.update_layers();
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
2015-04-12 21:40:25 +02:00
|
|
|
|
2018-01-14 03:59:27 +01:00
|
|
|
if(this.svga_enabled)
|
|
|
|
{
|
2022-02-22 00:18:13 +01:00
|
|
|
let min_y = 0;
|
2022-02-24 00:36:58 +01:00
|
|
|
let max_y = this.svga_height;
|
2022-02-22 00:18:13 +01:00
|
|
|
|
2022-02-16 00:58:31 +01:00
|
|
|
if(this.svga_bpp === 8)
|
2018-01-14 03:59:27 +01:00
|
|
|
{
|
2022-02-16 00:58:31 +01:00
|
|
|
// XXX: Slow, should be ported to rust, but it doesn't have access to vga256_palette
|
|
|
|
// XXX: Doesn't take svga_offset into account
|
|
|
|
const buffer = new Int32Array(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, this.screen_width * this.screen_height);
|
|
|
|
const svga_memory = new Uint8Array(this.cpu.wasm_memory.buffer, this.svga_memory.byteOffset, this.vga_memory_size);
|
2018-01-14 03:59:27 +01:00
|
|
|
|
2022-02-16 00:58:31 +01:00
|
|
|
for(var i = 0; i < buffer.length; i++)
|
|
|
|
{
|
|
|
|
var color = this.vga256_palette[svga_memory[i]];
|
|
|
|
buffer[i] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.cpu.svga_fill_pixel_buffer(this.svga_bpp, this.svga_offset);
|
2018-01-21 01:39:07 +01:00
|
|
|
|
2022-02-22 00:18:13 +01:00
|
|
|
const bytes_per_pixel = this.svga_bpp === 15 ? 2 : this.svga_bpp / 8;
|
2022-03-09 19:11:21 +01:00
|
|
|
min_y = (((this.cpu.svga_dirty_bitmap_min_offset[0] / bytes_per_pixel | 0) - this.svga_offset) / this.svga_width | 0);
|
|
|
|
max_y = (((this.cpu.svga_dirty_bitmap_max_offset[0] / bytes_per_pixel | 0) - this.svga_offset) / this.svga_width | 0) + 1;
|
2022-02-22 00:18:13 +01:00
|
|
|
}
|
2018-01-21 01:39:07 +01:00
|
|
|
|
2022-02-22 00:18:13 +01:00
|
|
|
if(min_y < max_y)
|
|
|
|
{
|
2022-02-24 00:36:58 +01:00
|
|
|
min_y = Math.max(min_y, 0);
|
|
|
|
max_y = Math.min(max_y, this.svga_height);
|
|
|
|
|
2022-02-22 00:18:13 +01:00
|
|
|
this.bus.send("screen-fill-buffer-end", [{
|
|
|
|
image_data: this.image_data,
|
|
|
|
screen_x: 0, screen_y: min_y,
|
|
|
|
buffer_x: 0, buffer_y: min_y,
|
|
|
|
buffer_width: this.svga_width,
|
2022-02-24 00:36:58 +01:00
|
|
|
buffer_height: max_y - min_y,
|
2022-02-22 00:18:13 +01:00
|
|
|
}]);
|
|
|
|
}
|
2018-01-14 03:59:27 +01:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
this.vga_replot();
|
|
|
|
this.vga_redraw();
|
2018-01-21 01:39:07 +01:00
|
|
|
this.bus.send("screen-fill-buffer-end", this.layers);
|
2015-04-12 21:40:25 +02:00
|
|
|
}
|
|
|
|
|
2018-01-21 01:39:07 +01:00
|
|
|
this.reset_diffs();
|
2018-01-14 03:59:27 +01:00
|
|
|
this.update_vertical_retrace();
|
2015-04-12 21:40:25 +02:00
|
|
|
};
|