Port svga rendering to rust
This commit is contained in:
parent
4567188324
commit
52ccc6a03c
|
@ -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");
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
154
src/rust/cpu/vga.rs
Normal 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
|
||||
}
|
||||
}
|
245
src/vga.js
245
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,
|
||||
|
|
Loading…
Reference in a new issue