From dc265f64381cb2dd2df6bee1cb02afb1c94c0a63 Mon Sep 17 00:00:00 2001 From: commonkestrel Date: Mon, 29 Jul 2024 15:41:51 -0700 Subject: [PATCH] better CPU usage and color showcase --- examples/screen.asm | 25 ++++++++++++-- misc/screen.png | Bin 24025 -> 41188 bytes src/emulator.rs | 2 +- src/emulator/display.rs | 72 +++++++++++++++++++++++----------------- 4 files changed, 64 insertions(+), 35 deletions(-) diff --git a/examples/screen.asm b/examples/screen.asm index 4fae336..4dc5cd5 100644 --- a/examples/screen.asm +++ b/examples/screen.asm @@ -1,12 +1,31 @@ @org 0x0000 -@define BUF_START 0xF800 +@define BUF_START 0xF000 +@define COLOR_START (BUF_START + 256*2) ; Start offset by 256 characters to not overwrite any of the char display _start: mv A, 0 -.loop: + mv B, 0x0F +.char_loop: lda [BUF_START] add16 H, L, 0x00, A + add16 H, L, 0x00, A ; double offset to account for modifier bytes st A + inc H, L ; increment to shift into modifier byte + st B + inc A - jnz A, [.loop] + jnz A, [.char_loop] + + mv A, 0 + mv B, 0x01 ; smiley face +.color_loop: + lda [COLOR_START] + add16 H, L, 0x00, A + add16 H, L, 0x00, A ; double offset to account for modifier bytes + st B + inc H, L ; increment to shift into modifier byte + st A + + inc A + jnz A, [.color_loop] halt diff --git a/misc/screen.png b/misc/screen.png index 4dc06e5091b60347b6fcf6a2dc4615a7fcc44754..3f74c02d679a4b24961484ea662ac74165a9a36a 100644 GIT binary patch literal 41188 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 literal 24025 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 Result<(), EmulatorError> { state.halt(); } } else { - async_std::task::sleep(Duration::from_millis(1)).await; + async_std::task::sleep(Duration::from_millis(10)).await; } } diff --git a/src/emulator/display.rs b/src/emulator/display.rs index 2562bf4..c24a877 100644 --- a/src/emulator/display.rs +++ b/src/emulator/display.rs @@ -1,4 +1,6 @@ -use std::pin::Pin; +use std::sync::atomic::Ordering; +use std::sync::Arc; +use std::{pin::Pin, sync::atomic::AtomicBool}; use std::str::FromStr; use async_std::task::JoinHandle; @@ -31,12 +33,14 @@ const COLORS: [u32; 16] = [ pub struct TextBuffer { chars: Pin>, modifiers: Pin>, + modified: Arc, handle: JoinHandle<()>, } -struct BufferPtr{ +struct BufferPtr { chars: *const [u8; 1 << 11], modifiers: *const [u8; 1 << 11], + modified: Arc, } unsafe impl Send for BufferPtr {} @@ -45,13 +49,15 @@ impl TextBuffer { pub fn spawn() -> TextBuffer { let chars = Box::pin([0; 1 << 11]); let modifiers = Box::pin([0; 1 << 11]); + let modified = Arc::new(AtomicBool::new(true)); let handle = async_std::task::spawn(run_handle(BufferPtr{ chars: &*chars, modifiers: &*modifiers, + modified: modified.clone(), })); - TextBuffer { chars, modifiers, handle } + TextBuffer { chars, modifiers, modified, handle } } pub fn get(&self, addr: u16) -> u8 { @@ -65,7 +71,7 @@ impl TextBuffer { } pub fn set(&mut self, addr: u16, data: u8) { - println!("setting {addr} to {data}"); + self.modified.store(true, Ordering::Relaxed); if addr % 2 == 0 { let sub_index = (addr >> 1) as usize; @@ -90,7 +96,7 @@ async fn run_handle(buffer: BufferPtr) { let mut window = minifb::Window::new("f8ful", WIDTH, HEIGHT, opts).expect("should be able to create window"); - // match the VGA standard + // the actual VGA standard is 75, but this function is very resource intensive and should not run constantly window.set_target_fps(75); if let Some(icon) = get_icon() { window.set_icon(icon); @@ -99,35 +105,39 @@ async fn run_handle(buffer: BufferPtr) { let mut fb = [0x00000000; WIDTH * HEIGHT]; while window.is_open() { - for y in 0..HEIGHT { - for x in 0..WIDTH { - let font_x = x % 8; - let font_y = y % 16; - - let char_x = x / 8; - let char_y = y / 16; - let char_idx = char_x + char_y * WIDTH / 8; - let (character, modifier) = unsafe {( - (*buffer.chars)[char_idx], - (*buffer.modifiers)[char_idx] - )}; - - let font_addr = ((character as usize) << 4) + font_y; - let lit = FONT[font_addr] & (1 << (7 - font_x)) > 0; - - // This part isn't part of the actual CPU, - // the real value will be transmitted via VGA instead of stored. - let fg = COLORS[(modifier & 0xf) as usize]; - let bg = COLORS[(modifier >> 4) as usize]; - fb[x + y * WIDTH] = if lit { fg } else { bg }; + if buffer.modified.load(Ordering::Relaxed) { + buffer.modified.store(false, Ordering::Relaxed); + for y in 0..HEIGHT { + for x in 0..WIDTH { + let font_x = x % 8; + let font_y = y % 16; + + let char_x = x / 8; + let char_y = y / 16; + let char_idx = char_x + char_y * WIDTH / 8; + let (character, modifier) = unsafe {( + (*buffer.chars)[char_idx], + (*buffer.modifiers)[char_idx] + )}; + + let font_addr = ((character as usize) << 4) + font_y; + let lit = FONT[font_addr] & (1 << (7 - font_x)) > 0; + + // This part isn't part of the actual CPU, + // the real value will be transmitted via VGA instead of stored. + let fg = COLORS[(modifier & 0xf) as usize]; + let bg = COLORS[(modifier >> 4) as usize]; + fb[x + y * WIDTH] = if lit { fg } else { bg }; + } } - } - window - .update_with_buffer(&fb, WIDTH, HEIGHT) - .expect("unable to write to window"); + window + .update_with_buffer(&fb, WIDTH, HEIGHT) + .expect("unable to write to window"); + } else { + window.update(); + } } - } fn get_icon() -> Option {