Port svga rendering to rust

This commit is contained in:
Fabian 2022-02-16 00:58:31 +01:00
parent 4567188324
commit 52ccc6a03c
6 changed files with 375 additions and 234 deletions

View file

@ -239,6 +239,7 @@ CPU.prototype.wasm_patch = function()
this.read8 = get_import("read8");
this.read16 = get_import("read16");
this.read32s = get_import("read32s");
this.write8 = get_import("write8");
this.write16 = get_import("write16");
this.write32 = get_import("write32");
this.in_mapped_range = get_import("in_mapped_range");
@ -271,6 +272,11 @@ CPU.prototype.wasm_patch = function()
this.allocate_memory = get_import("allocate_memory");
this.zero_memory = get_import("zero_memory");
this.svga_allocate_memory = get_import("svga_allocate_memory");
this.svga_allocate_dest_buffer = get_import("svga_allocate_dest_buffer");
this.svga_fill_pixel_buffer = get_import("svga_fill_pixel_buffer");
this.svga_mark_dirty = get_import("svga_mark_dirty");
this.zstd_create_ctx = get_import("zstd_create_ctx");
this.zstd_get_src_ptr = get_import("zstd_get_src_ptr");
this.zstd_free_ctx = get_import("zstd_free_ctx");

View file

@ -3433,14 +3433,8 @@ pub unsafe fn safe_write_slow_jit(
}
else if in_mapped_range(addr_low) {
match bitsize {
128 => memory::mmap_write128(
addr_low,
value_low as i32,
(value_low >> 32) as i32,
value_high as i32,
(value_high >> 32) as i32,
),
64 => memory::mmap_write64(addr_low, value_low as i32, (value_low >> 32) as i32),
128 => memory::mmap_write128(addr_low, value_low, value_high),
64 => memory::mmap_write64(addr_low, value_low),
32 => memory::mmap_write32(addr_low, value_low as i32),
16 => memory::mmap_write16(addr_low, (value_low & 0xFFFF) as i32),
8 => memory::mmap_write8(addr_low, (value_low & 0xFF) as i32),
@ -3550,7 +3544,7 @@ pub unsafe fn safe_write64(addr: i32, value: u64) -> OrPageFault<()> {
else {
let (phys_addr, can_skip_dirty_page) = translate_address_write_and_can_skip_dirty(addr)?;
if in_mapped_range(phys_addr) {
memory::mmap_write64(phys_addr, value as i32, (value >> 32) as i32);
memory::mmap_write64(phys_addr, value);
}
else {
if !can_skip_dirty_page {
@ -3574,13 +3568,7 @@ pub unsafe fn safe_write128(addr: i32, value: reg128) -> OrPageFault<()> {
else {
let (phys_addr, can_skip_dirty_page) = translate_address_write_and_can_skip_dirty(addr)?;
if in_mapped_range(phys_addr) {
memory::mmap_write128(
phys_addr,
value.i32_0[0],
value.i32_0[1],
value.i32_0[2],
value.i32_0[3],
);
memory::mmap_write128(phys_addr, value.u64_0[0], value.u64_0[1]);
}
else {
if !can_skip_dirty_page {

View file

@ -1,18 +1,22 @@
extern "C" {
fn mmap_read8(addr: u32) -> i32;
fn mmap_read16(addr: u32) -> i32;
fn mmap_read32(addr: u32) -> i32;
mod ext {
extern "C" {
pub fn mmap_read8(addr: u32) -> i32;
pub fn mmap_read16(addr: u32) -> i32;
pub fn mmap_read32(addr: u32) -> i32;
pub fn mmap_write8(addr: u32, value: i32);
pub fn mmap_write16(addr: u32, value: i32);
pub fn mmap_write32(addr: u32, value: i32);
pub fn mmap_write64(addr: u32, v0: i32, v1: i32);
pub fn mmap_write128(addr: u32, v0: i32, v1: i32, v2: i32, v3: i32);
pub fn mmap_write8(addr: u32, value: i32);
pub fn mmap_write16(addr: u32, value: i32);
pub fn mmap_write32(addr: u32, value: i32);
pub fn mmap_write64(addr: u32, v0: i32, v1: i32);
pub fn mmap_write128(addr: u32, v0: i32, v1: i32, v2: i32, v3: i32);
}
}
use cpu::cpu::reg128;
use cpu::global_pointers::memory_size;
use cpu::vga;
use page::Page;
use std::alloc;
use std::ptr;
@ -36,30 +40,69 @@ pub fn allocate_memory(size: u32) -> u32 {
#[no_mangle]
pub unsafe fn zero_memory(size: u32) { ptr::write_bytes(mem8, 0, size as usize); }
#[allow(non_upper_case_globals)]
pub static mut vga_mem8: *mut u8 = ptr::null_mut();
#[allow(non_upper_case_globals)]
pub static mut vga_memory_size: u32 = 0;
#[no_mangle]
pub fn svga_allocate_memory(size: u32) -> u32 {
unsafe {
dbg_assert!(vga_mem8.is_null());
};
let layout = alloc::Layout::from_size_align(size as usize, 0x1000).unwrap();
let ptr = unsafe { alloc::alloc(layout) as u32 };
dbg_assert!(
size & (1 << 12 << 6) == 0,
"size not aligned to dirty_bitmap"
);
unsafe {
vga_mem8 = ptr as *mut u8;
vga_memory_size = size;
vga::dirty_bitmap.resize((size >> 12 >> 6) as usize, 0);
};
ptr
}
#[no_mangle]
pub fn in_mapped_range(addr: u32) -> bool {
return addr >= 0xA0000 && addr < 0xC0000 || addr >= unsafe { *memory_size };
}
pub const VGA_LFB_ADDRESS: u32 = 0xE0000000;
pub fn in_svga_lfb(addr: u32) -> bool {
addr >= VGA_LFB_ADDRESS && addr < unsafe { VGA_LFB_ADDRESS + vga_memory_size }
}
#[no_mangle]
pub fn read8(addr: u32) -> i32 {
if in_mapped_range(addr) {
return unsafe { mmap_read8(addr) };
if in_svga_lfb(addr) {
unsafe { *vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as i32 }
}
else {
unsafe { ext::mmap_read8(addr) }
}
}
else {
return read8_no_mmap_check(addr);
};
read8_no_mmap_check(addr)
}
}
pub fn read8_no_mmap_check(addr: u32) -> i32 { unsafe { *mem8.offset(addr as isize) as i32 } }
#[no_mangle]
pub fn read16(addr: u32) -> i32 {
if in_mapped_range(addr) {
return unsafe { mmap_read16(addr) };
if in_svga_lfb(addr) {
unsafe { *(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *const u16) as i32 }
}
else {
unsafe { ext::mmap_read16(addr) }
}
}
else {
return read16_no_mmap_check(addr);
};
read16_no_mmap_check(addr)
}
}
pub fn read16_no_mmap_check(addr: u32) -> i32 {
unsafe { *(mem8.offset(addr as isize) as *mut u16) as i32 }
@ -68,11 +111,16 @@ pub fn read16_no_mmap_check(addr: u32) -> i32 {
#[no_mangle]
pub fn read32s(addr: u32) -> i32 {
if in_mapped_range(addr) {
return unsafe { mmap_read32(addr) };
if in_svga_lfb(addr) {
unsafe { *(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *const i32) }
}
else {
unsafe { ext::mmap_read32(addr) }
}
}
else {
return read32_no_mmap_check(addr);
};
read32_no_mmap_check(addr)
}
}
pub fn read32_no_mmap_check(addr: u32) -> i32 {
unsafe { *(mem8.offset(addr as isize) as *mut i32) }
@ -80,30 +128,50 @@ pub fn read32_no_mmap_check(addr: u32) -> i32 {
pub unsafe fn read64s(addr: u32) -> i64 {
if in_mapped_range(addr) {
return mmap_read32(addr) as i64 | (mmap_read32(addr.wrapping_add(4 as u32)) as i64) << 32;
if in_svga_lfb(addr) {
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *const i64)
}
else {
ext::mmap_read32(addr) as i64 | (ext::mmap_read32(addr + 4) as i64) << 32
}
}
else {
return *(mem8.offset(addr as isize) as *mut i64);
};
*(mem8.offset(addr as isize) as *mut i64)
}
}
pub unsafe fn read128(addr: u32) -> reg128 {
let mut value: reg128 = reg128 {
i8_0: [0 as i8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
};
if in_mapped_range(addr) {
value.i32_0[0] = mmap_read32(addr);
value.i32_0[1] = mmap_read32(addr.wrapping_add(4 as u32));
value.i32_0[2] = mmap_read32(addr.wrapping_add(8 as u32));
value.i32_0[3] = mmap_read32(addr.wrapping_add(12 as u32))
if in_svga_lfb(addr) {
reg128 {
i64_0: [
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *const i64),
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS + 8) as isize) as *const i64),
],
}
}
else {
reg128 {
i32_0: [
ext::mmap_read32(addr + 0),
ext::mmap_read32(addr + 4),
ext::mmap_read32(addr + 8),
ext::mmap_read32(addr + 12),
],
}
}
}
else {
value.i64_0[0] = *(mem8.offset(addr as isize) as *mut i64);
value.i64_0[1] = *(mem8.offset(addr as isize).offset(8) as *mut i64)
reg128 {
i64_0: [
*(mem8.offset(addr as isize) as *mut i64),
*(mem8.offset(addr as isize).offset(8) as *mut i64),
],
}
}
return value;
}
#[no_mangle]
pub unsafe fn write8(addr: u32, value: i32) {
if in_mapped_range(addr) {
mmap_write8(addr, value & 0xFF);
@ -167,3 +235,56 @@ pub unsafe fn memcpy_no_mmap_or_dirty_check(src_addr: u32, dst_addr: u32, count:
count as usize,
)
}
pub unsafe fn mmap_write8(addr: u32, value: i32) {
if in_svga_lfb(addr) {
vga::mark_dirty(addr);
*vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) = value as u8
}
else {
ext::mmap_write8(addr, value)
}
}
pub unsafe fn mmap_write16(addr: u32, value: i32) {
if in_svga_lfb(addr) {
vga::mark_dirty(addr);
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *mut u16) = value as u16
}
else {
ext::mmap_write16(addr, value)
}
}
pub unsafe fn mmap_write32(addr: u32, value: i32) {
if in_svga_lfb(addr) {
vga::mark_dirty(addr);
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *mut i32) = value
}
else {
ext::mmap_write32(addr, value)
}
}
pub unsafe fn mmap_write64(addr: u32, value: u64) {
if in_svga_lfb(addr) {
vga::mark_dirty(addr);
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *mut u64) = value
}
else {
ext::mmap_write64(addr, value as i32, (value >> 32) as i32)
}
}
pub unsafe fn mmap_write128(addr: u32, v0: u64, v1: u64) {
if in_svga_lfb(addr) {
vga::mark_dirty(addr);
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS) as isize) as *mut u64) = v0;
*(vga_mem8.offset((addr - VGA_LFB_ADDRESS + 8) as isize) as *mut u64) = v1
}
else {
ext::mmap_write128(
addr,
v0 as i32,
(v0 >> 32) as i32,
v1 as i32,
(v1 >> 32) as i32,
)
}
}

View file

@ -10,3 +10,4 @@ pub mod misc_instr;
pub mod modrm;
pub mod sse_instr;
pub mod string;
pub mod vga;

154
src/rust/cpu/vga.rs Normal file
View file

@ -0,0 +1,154 @@
#![allow(non_upper_case_globals)]
use cpu::memory;
pub static mut dirty_bitmap: Vec<u64> = Vec::new();
pub static mut dest_buffer: Vec<u32> = Vec::new();
#[no_mangle]
pub unsafe fn svga_allocate_dest_buffer(size: u32) -> u32 {
dest_buffer.resize(size as usize, 0);
dest_buffer.as_mut_ptr() as u32
}
#[no_mangle]
pub unsafe fn mark_dirty(addr: u32) {
let page = (addr - memory::VGA_LFB_ADDRESS) >> 12;
dbg_assert!(((page >> 6) as usize) < dirty_bitmap.len());
*dirty_bitmap.get_unchecked_mut((page >> 6) as usize) |= 1 << (page & 63)
}
#[no_mangle]
pub unsafe fn svga_mark_dirty() {
for v in dirty_bitmap.iter_mut() {
*v = u64::MAX
}
}
fn iter_dirty_pages(f: &dyn Fn(isize)) {
for (i, &word) in unsafe { &dirty_bitmap }.iter().enumerate() {
if word == 0 {
continue;
}
for j in 0..64 {
if word & 1 << j == 0 {
continue;
}
let off = ((i << 6 | j) << 12) as isize;
dbg_assert!(off < unsafe { memory::vga_memory_size as isize });
f(off);
}
}
}
#[no_mangle]
pub unsafe fn svga_fill_pixel_buffer(bpp: i32, svga_dest_offset: i32) {
let debug_bounds = false;
match bpp {
32 => iter_dirty_pages(&|off| {
dbg_assert!(off >= 0);
let src = memory::vga_mem8.offset(off) as *const u32;
let dest_offset = off / 4 - svga_dest_offset as isize;
let dest = dest_buffer.as_mut_ptr().offset(dest_offset) as *mut u32;
let end = if dest_offset < 0 {
0
}
else {
isize::min(1024, dest_buffer.len() as isize - dest_offset)
};
for i in 0..end {
dbg_assert!(off + i < memory::vga_memory_size as isize);
let dword = *src.offset(i);
let dword = if debug_bounds && (i == 0 || i == end - 1) { 0xFFFFFF } else { dword };
dbg_assert!(dest_offset + i < dest_buffer.len() as isize);
*dest.offset(i) = dword << 16 | dword >> 16 & 0xFF | dword & 0xFF00 | 0xFF00_0000;
}
}),
24 => iter_dirty_pages(&|off| {
dbg_assert!(off >= 0 && off < memory::vga_memory_size as isize);
let off = off - off % 3;
let src = memory::vga_mem8.offset(off);
let dest_offset = off / 3 - svga_dest_offset as isize;
let dest = dest_buffer.as_mut_ptr().offset(dest_offset) as *mut u32;
let end = if dest_offset < 0 {
0
}
else {
isize::min(4096 / 3 + 1, dest_buffer.len() as isize - dest_offset)
};
for i in 0..end {
let dword = *(src.offset(3 * i) as *const u32);
let dword = if debug_bounds && (i == 0 || i == end - 1) { 0xFFFFFF } else { dword };
dbg_assert!(dest_offset + i < dest_buffer.len() as isize);
*dest.offset(i) = dword << 16 | dword >> 16 & 0xFF | dword & 0xFF00 | 0xFF00_0000;
}
}),
16 => iter_dirty_pages(&|off| {
dbg_assert!(off >= 0 && off + 2048 < memory::vga_memory_size as isize);
let src = memory::vga_mem8.offset(off) as *const u16;
let dest_offset = off / 2 - svga_dest_offset as isize;
let dest = dest_buffer.as_mut_ptr().offset(dest_offset) as *mut u32;
let end = if dest_offset < 0 {
0
}
else {
isize::min(2048, dest_buffer.len() as isize - dest_offset)
};
for i in 0..end {
dbg_assert!(off + i < memory::vga_memory_size as isize);
let word = *src.offset(i);
let word = if debug_bounds && (i == 0 || i == end - 1) { 0xFFFF } else { word };
let r = (word & 0x1F) * 0xFF / 0x1F;
let g = (word >> 5 & 0x3F) * 0xFF / 0x3F;
let b = (word >> 11) * 0xFF / 0x1F;
dbg_assert!(dest_offset + i < dest_buffer.len() as isize);
*dest.offset(i) = (r as u32) << 16 | (g as u32) << 8 | b as u32 | 0xFF00_0000;
}
}),
15 => iter_dirty_pages(&|off| {
dbg_assert!(off >= 0 && off + 2048 < memory::vga_memory_size as isize);
let src = memory::vga_mem8.offset(off) as *const u16;
let dest_offset = off / 2 - svga_dest_offset as isize;
let dest = dest_buffer.as_mut_ptr().offset(dest_offset) as *mut u32;
let end = if dest_offset < 0 {
0
}
else {
isize::min(2048, dest_buffer.len() as isize - dest_offset)
};
for i in 0..end {
dbg_assert!(off + i < memory::vga_memory_size as isize);
let word = *src.offset(i);
let word = if debug_bounds && (i == 0 || i == end - 1) { 0xFFFF } else { word };
let r = (word & 0x1F) * 0xFF / 0x1F;
let g = (word >> 5 & 0x1F) * 0xFF / 0x1F;
let b = (word >> 10 & 0x1F) * 0xFF / 0x1F;
dbg_assert!(dest_offset + i < dest_buffer.len() as isize);
*dest.offset(i) = (r as u32) << 16 | (g as u32) << 8 | b as u32 | 0xFF00_0000;
}
}),
_ => {
dbg_log!("{}", bpp);
dbg_assert!(false, "Unsupported bpp");
},
}
//if cfg!(debug_assertions) {
// let mut pages = 0;
// for &word in dirty_bitmap.iter() {
// pages += word.count_ones();
// }
// dbg_log!(
// "fill offset={:x} bpp={} pages={}",
// svga_dest_offset,
// bpp,
// pages,
// );
//}
for v in dirty_bitmap.iter_mut() {
*v = 0
}
}

View file

@ -21,9 +21,6 @@ var
//var VGA_LFB_ADDRESS = 0xFE000000; // set by seabios
var VGA_LFB_ADDRESS = 0xE0000000;
/** @const */
var VGA_PIXEL_BUFFER_START = 4 * VGA_BANK_SIZE;
/**
* @const
* Equals the maximum number of pixels for non svga.
@ -32,7 +29,7 @@ var VGA_PIXEL_BUFFER_START = 4 * VGA_BANK_SIZE;
var VGA_PIXEL_BUFFER_SIZE = 8 * VGA_BANK_SIZE;
/** @const */
var VGA_MIN_MEMORY_SIZE = VGA_PIXEL_BUFFER_START + VGA_PIXEL_BUFFER_SIZE;
var VGA_MIN_MEMORY_SIZE = 4 * VGA_BANK_SIZE;
/**
* @const
@ -64,6 +61,8 @@ var VGA_HOST_MEMORY_SPACE_SIZE = Uint32Array.from([
*/
function VGAScreen(cpu, bus, vga_memory_size)
{
this.cpu = cpu;
/** @const @type {BusConnector} */
this.bus = bus;
@ -351,42 +350,34 @@ function VGAScreen(cpu, bus, vga_memory_size)
this.vga_memory_size++;
}
this.svga_memory = new Uint8Array(this.vga_memory_size);
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);
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;
this.dest_buffer = null;
this.image_data = null;
bus.register("screen-fill-buffer", function()
{
this.screen_fill_buffer();
}, this);
this.svga_memory16 = new Uint16Array(this.svga_memory.buffer);
this.svga_memory32 = new Int32Array(this.svga_memory.buffer);
this.vga_memory = new Uint8Array(this.svga_memory.buffer, 0, 4 * VGA_BANK_SIZE);
this.plane0 = new Uint8Array(this.svga_memory.buffer, 0 * VGA_BANK_SIZE, VGA_BANK_SIZE);
this.plane1 = new Uint8Array(this.svga_memory.buffer, 1 * VGA_BANK_SIZE, VGA_BANK_SIZE);
this.plane2 = new Uint8Array(this.svga_memory.buffer, 2 * VGA_BANK_SIZE, VGA_BANK_SIZE);
this.plane3 = new Uint8Array(this.svga_memory.buffer, 3 * VGA_BANK_SIZE, VGA_BANK_SIZE);
this.pixel_buffer = new Uint8Array(this.svga_memory.buffer,
VGA_PIXEL_BUFFER_START, VGA_PIXEL_BUFFER_SIZE);
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);
var me = this;
io.mmap_register(0xA0000, 0x20000,
function(addr) { return me.vga_memory_read(addr); },
function(addr, value) { me.vga_memory_write(addr, value); }
);
io.mmap_register(VGA_LFB_ADDRESS, this.vga_memory_size,
function(addr) { return me.svga_memory_read8(addr); },
function(addr, value) { me.svga_memory_write8(addr, value); },
function(addr) { return me.svga_memory_read32(addr); },
function(addr, value) { me.svga_memory_write32(addr, value); }
);
cpu.devices.pci.register_device(this);
}
@ -401,7 +392,7 @@ VGAScreen.prototype.get_state = function()
state[3] = this.cursor_scanline_end;
state[4] = this.max_cols;
state[5] = this.max_rows;
state[6] = this.vga_memory;
state[7] = this.dac_state;
state[8] = this.start_address;
state[9] = this.graphical_mode;
@ -456,6 +447,7 @@ VGAScreen.prototype.get_state = function()
state[58] = this.color_select;
state[59] = this.clocking_mode;
state[60] = this.line_compare;
state[61] = this.pixel_buffer;
return state;
};
@ -468,7 +460,7 @@ VGAScreen.prototype.set_state = function(state)
this.cursor_scanline_end = state[3];
this.max_cols = state[4];
this.max_rows = state[5];
state[6] && this.vga_memory.set(state[6]);
this.dac_state = state[7];
this.start_address = state[8];
this.graphical_mode = state[9];
@ -523,6 +515,7 @@ VGAScreen.prototype.set_state = function(state)
this.color_select = state[58];
this.clocking_mode = state[59];
this.line_compare = state[60];
state[61] && this.pixel_buffer.set(state[61]);
this.bus.send("screen-set-mode", this.graphical_mode);
@ -558,10 +551,7 @@ VGAScreen.prototype.vga_memory_read = function(addr)
{
if(this.svga_enabled && this.graphical_mode_is_linear)
{
addr -= 0xA0000;
addr |= this.svga_bank_offset;
return this.svga_memory[addr];
return this.cpu.read8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0);
}
var memory_space_select = this.miscellaneous_graphics_register >> 2 & 0x3;
@ -634,8 +624,7 @@ VGAScreen.prototype.vga_memory_write = function(addr, value)
if(this.svga_enabled && this.graphical_mode && this.graphical_mode_is_linear)
{
// vbe banked mode
addr -= 0xA0000;
this.vga_memory_write_graphical_linear(addr, value);
this.cpu.write8((addr - 0xA0000 | this.svga_bank_offset) + VGA_LFB_ADDRESS | 0, value);
return;
}
@ -663,16 +652,6 @@ VGAScreen.prototype.vga_memory_write = function(addr, value)
}
};
VGAScreen.prototype.vga_memory_write_graphical_linear = function(addr, value)
{
addr |= this.svga_bank_offset;
this.diff_addr_min = addr < this.diff_addr_min ? addr : this.diff_addr_min;
this.diff_addr_max = addr > this.diff_addr_max ? addr : this.diff_addr_max;
this.svga_memory[addr] = value;
};
VGAScreen.prototype.vga_memory_write_graphical = function(addr, value)
{
var plane_dword;
@ -896,62 +875,19 @@ VGAScreen.prototype.update_cursor = function()
this.bus.send("screen-update-cursor", [row, col]);
};
VGAScreen.prototype.svga_memory_read8 = function(addr)
{
return this.svga_memory[addr & 0xFFFFFFF];
};
VGAScreen.prototype.svga_memory_read32 = function(addr)
{
addr &= 0xFFFFFFF;
if(addr & 3)
{
return this.svga_memory[addr] | this.svga_memory[addr + 1] << 8 |
this.svga_memory[addr + 2] << 16 | this.svga_memory[addr + 3] << 24;
}
else
{
return this.svga_memory32[addr >> 2];
}
};
VGAScreen.prototype.svga_memory_write8 = function(addr, value)
{
addr &= 0xFFFFFFF;
this.svga_memory[addr] = value;
this.diff_addr_min = addr < this.diff_addr_min ? addr : this.diff_addr_min;
this.diff_addr_max = addr > this.diff_addr_max ? addr : this.diff_addr_max;
};
VGAScreen.prototype.svga_memory_write32 = function(addr, value)
{
addr &= 0xFFFFFFF;
this.diff_addr_min = addr < this.diff_addr_min ? addr : this.diff_addr_min;
this.diff_addr_max = addr + 3 > this.diff_addr_max ? addr + 3 : this.diff_addr_max;
this.svga_memory[addr] = value;
this.svga_memory[addr + 1] = value >> 8;
this.svga_memory[addr + 2] = value >> 16;
this.svga_memory[addr + 3] = value >> 24;
};
VGAScreen.prototype.complete_redraw = function()
{
dbg_log("complete redraw", LOG_VGA);
if(this.graphical_mode)
{
this.diff_addr_min = 0;
if(this.svga_enabled)
{
this.diff_addr_max = this.vga_memory_size;
this.cpu.svga_mark_dirty();
}
else
{
this.diff_addr_min = 0;
this.diff_addr_max = VGA_PIXEL_BUFFER_SIZE;
}
}
@ -1154,8 +1090,13 @@ VGAScreen.prototype.set_size_graphical = function(width, height, bpp, virtual_wi
if (typeof ImageData !== "undefined")
{
this.image_data = new ImageData(width, height);
this.dest_buffer = new Int32Array(this.image_data.data.buffer);
const size = width * height;
const offset = this.cpu.svga_allocate_dest_buffer(size);
this.dest_buffet_offset = offset;
this.image_data = new ImageData(new Uint8ClampedArray(this.cpu.wasm_memory.buffer, offset, 4 * size), width, height);
this.cpu.svga_mark_dirty();
}
else
{
@ -2012,13 +1953,6 @@ VGAScreen.prototype.port3DA_read = function()
return value;
};
VGAScreen.prototype.svga_bytes_per_line = function()
{
var bits = this.svga_bpp === 15 ? 16 : this.svga_bpp;
return this.svga_width * bits / 8;
};
VGAScreen.prototype.port1CE_write = function(value)
{
this.dispi_index = value;
@ -2055,13 +1989,18 @@ VGAScreen.prototype.port1CF_write = function(value)
this.dispi_enable_value = value;
break;
case 5:
dbg_log("SVGA bank offset: " + h(value << 16));
this.svga_bank_offset = value << 16;
break;
case 9:
// y offset
this.svga_offset = value * this.svga_bytes_per_line();
dbg_log("SVGA offset: " + h(this.svga_offset) + " y=" + h(value), LOG_VGA);
this.complete_redraw();
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();
}
break;
default:
}
@ -2073,7 +2012,6 @@ VGAScreen.prototype.port1CF_write = function(value)
}
dbg_assert(this.svga_bpp !== 4, "unimplemented svga bpp: 4");
dbg_assert(this.svga_bpp !== 15, "unimplemented svga bpp: 15");
dbg_assert(this.svga_bpp === 4 || this.svga_bpp === 8 ||
this.svga_bpp === 15 || this.svga_bpp === 16 ||
this.svga_bpp === 24 || this.svga_bpp === 32,
@ -2278,10 +2216,7 @@ VGAScreen.prototype.vga_redraw = function()
{
var start = this.diff_addr_min;
var end = Math.min(this.diff_addr_max, VGA_PIXEL_BUFFER_SIZE - 1);
var buffer = this.dest_buffer;
// Closure compiler
if(!buffer) return;
const buffer = new Int32Array(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, this.screen_width * this.screen_height);
var mask = 0xFF;
var colorset = 0x00;
@ -2334,100 +2269,36 @@ VGAScreen.prototype.screen_fill_buffer = function()
return;
}
if(!this.dest_buffer)
if(this.image_data.data.byteLength === 0)
{
dbg_log("Cannot fill buffer: No destination buffer", LOG_VGA);
// Update retrace behaviour anyway
this.update_vertical_retrace();
return;
}
if(this.diff_addr_max < this.diff_addr_min && this.diff_plot_max < this.diff_plot_min)
{
// No pixels to update
this.bus.send("screen-fill-buffer-end", this.layers);
this.update_vertical_retrace();
return;
// wasm memory resized
const buffer = new Uint8ClampedArray(this.cpu.wasm_memory.buffer, this.dest_buffet_offset, 4 * this.screen_width * this.screen_height);
this.image_data = new ImageData(buffer, this.screen_width, this.screen_height);
this.update_layers();
}
if(this.svga_enabled)
{
var bpp = this.svga_bpp;
var buffer = this.dest_buffer;
var start = this.diff_addr_min;
var end = this.diff_addr_max;
switch(bpp)
if(this.svga_bpp === 8)
{
case 32:
var start_pixel = (start - this.svga_offset) >> 2;
var end_pixel = ((end - this.svga_offset) >> 2) + 1;
var addr = start >> 2;
// 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);
for(var i = start_pixel; i < end_pixel; i++)
{
var dword = this.svga_memory32[addr++];
buffer[i] = dword << 16 | dword >> 16 & 0xFF | dword & 0xFF00 | 0xFF000000;
}
break;
case 24:
start -= start % 3;
end += 3 - end % 3;
dbg_assert(this.svga_offset % 3 === 0);
var start_pixel = (start - this.svga_offset) / 3 | 0;
var end_pixel = ((end - this.svga_offset) / 3 | 0) + 1;
var addr = start;
for(var i = start_pixel; addr < end; i++)
{
var red = this.svga_memory[addr++];
var green = this.svga_memory[addr++];
var blue = this.svga_memory[addr++];
buffer[i] = red << 16 | green << 8 | blue | 0xFF000000;
}
break;
case 16:
var start_pixel = (start - this.svga_offset) >> 1;
var end_pixel = ((end - this.svga_offset) >> 1) + 1;
var addr = start >> 1;
for(var i = start_pixel; i < end_pixel; i++)
{
var word = this.svga_memory16[addr++];
var blue = (word >> 11) * 0xFF / 0x1F | 0;
var green = (word >> 5 & 0x3F) * 0xFF / 0x3F | 0;
var red = (word & 0x1F) * 0xFF / 0x1F | 0;
buffer[i] = red << 16 | green << 8 | blue | 0xFF000000;
}
break;
case 8:
var start_pixel = start - this.svga_offset;
var end_pixel = end - this.svga_offset + 1;
var addr = start;
for(var i = start; i <= end; i++)
{
var color = this.vga256_palette[this.svga_memory[addr++]];
buffer[i] = color & 0xFF00 | color << 16 | color >> 16 | 0xFF000000;
}
break;
default:
dbg_assert(false, "Unsupported BPP: " + bpp);
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);
}
var min_y = start_pixel / this.svga_width | 0;
var max_y = end_pixel / this.svga_width | 0;
const min_y = 0;
const max_y = this.svga_height;
this.bus.send("screen-fill-buffer-end", [{
image_data: this.image_data,