diff --git a/src/cpu.js b/src/cpu.js index 570237f3..3e7b0005 100644 --- a/src/cpu.js +++ b/src/cpu.js @@ -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"); diff --git a/src/rust/cpu/cpu.rs b/src/rust/cpu/cpu.rs index b14e04b9..8ab8a079 100644 --- a/src/rust/cpu/cpu.rs +++ b/src/rust/cpu/cpu.rs @@ -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 { diff --git a/src/rust/cpu/memory.rs b/src/rust/cpu/memory.rs index b2a93710..b09b0999 100644 --- a/src/rust/cpu/memory.rs +++ b/src/rust/cpu/memory.rs @@ -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, + ) + } +} diff --git a/src/rust/cpu/mod.rs b/src/rust/cpu/mod.rs index 38f65d87..346eed7f 100644 --- a/src/rust/cpu/mod.rs +++ b/src/rust/cpu/mod.rs @@ -10,3 +10,4 @@ pub mod misc_instr; pub mod modrm; pub mod sse_instr; pub mod string; +pub mod vga; diff --git a/src/rust/cpu/vga.rs b/src/rust/cpu/vga.rs new file mode 100644 index 00000000..a78fbeb7 --- /dev/null +++ b/src/rust/cpu/vga.rs @@ -0,0 +1,154 @@ +#![allow(non_upper_case_globals)] + +use cpu::memory; + +pub static mut dirty_bitmap: Vec = Vec::new(); +pub static mut dest_buffer: Vec = 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 + } +} diff --git a/src/vga.js b/src/vga.js index 362eaa3c..b70865a7 100644 --- a/src/vga.js +++ b/src/vga.js @@ -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,