|
|
|
@ -3,7 +3,9 @@ use std::time::{Duration, Instant}; |
|
|
|
|
|
|
|
|
|
use minifb::{Key, MouseButton, MouseMode, ScaleMode, Window, WindowOptions}; |
|
|
|
|
|
|
|
|
|
use crsn::asm::data::literal::Value; |
|
|
|
|
use crsn::asm::data::literal::{Value, is_negative}; |
|
|
|
|
use crsn::asm::error::CrsnError; |
|
|
|
|
use crsn::asm::instr::cond::Flag; |
|
|
|
|
use crsn::module::{EvalRes, OpTrait}; |
|
|
|
|
use crsn::runtime::fault::Fault; |
|
|
|
|
use crsn::runtime::run_thread::{state::RunState, ThreadInfo}; |
|
|
|
@ -12,18 +14,21 @@ use crsn::sexp::Sexp; |
|
|
|
|
use crsn::utils::A; |
|
|
|
|
|
|
|
|
|
use crate::defs::ScreenOp; |
|
|
|
|
use crsn::asm::instr::cond::Flag; |
|
|
|
|
use std::mem; |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
struct Opts { |
|
|
|
|
auto_blit: bool, |
|
|
|
|
frame_rate: Duration, |
|
|
|
|
upscale: Value, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[derive(Debug)] |
|
|
|
|
struct Backend { |
|
|
|
|
width: usize, |
|
|
|
|
height: usize, |
|
|
|
|
width: Value, |
|
|
|
|
height: Value, |
|
|
|
|
log_width: Value, |
|
|
|
|
log_height: Value, |
|
|
|
|
buffer: Vec<u32>, |
|
|
|
|
window: Option<Window>, |
|
|
|
|
last_render: Instant, |
|
|
|
@ -35,19 +40,33 @@ impl Default for Backend { |
|
|
|
|
Self { |
|
|
|
|
width: 0, |
|
|
|
|
height: 0, |
|
|
|
|
log_width: 0, |
|
|
|
|
log_height: 0, |
|
|
|
|
buffer: vec![], |
|
|
|
|
window: None, |
|
|
|
|
last_render: Instant::now().sub(Duration::from_secs(1)), |
|
|
|
|
opts: Opts { |
|
|
|
|
auto_blit: true, |
|
|
|
|
upscale: 1, |
|
|
|
|
frame_rate: Duration::from_micros(16600), |
|
|
|
|
}, |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl Backend { |
|
|
|
|
pub fn draw_rect_log(&mut self, mut x: Value, mut y: Value, w: Value, h: Value, color: u32) { |
|
|
|
|
for yy in (y * self.opts.upscale)..((y + h) * self.opts.upscale).min(self.height) { |
|
|
|
|
for xx in (x * self.opts.upscale)..((x + w) * self.opts.upscale).min(self.width) { |
|
|
|
|
self.buffer[(yy * self.width + xx) as usize] = color as u32; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
pub const OPT_AUTO_BLIT: u64 = 1; |
|
|
|
|
pub const OPT_FRAME_RATE: u64 = 2; |
|
|
|
|
pub const OPT_UPSCALE: u64 = 3; |
|
|
|
|
|
|
|
|
|
// Hack for Window
|
|
|
|
|
unsafe impl std::marker::Send for Backend {} |
|
|
|
@ -81,8 +100,22 @@ impl OpTrait for ScreenOp { |
|
|
|
|
debug!("Set auto blit to {:?}", backend.opts.auto_blit); |
|
|
|
|
} |
|
|
|
|
OPT_FRAME_RATE => { |
|
|
|
|
backend.opts.frame_rate = Duration::from_micros(1_000_000 as u64 / val); |
|
|
|
|
debug!("Set frame rate to {:?}", backend.opts.frame_rate); |
|
|
|
|
if val == 0 { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
} else { |
|
|
|
|
backend.opts.frame_rate = Duration::from_micros(1_000_000 as u64 / val); |
|
|
|
|
debug!("Set frame rate to {:?}", backend.opts.frame_rate); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
OPT_UPSCALE => { |
|
|
|
|
if val == 0 { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
} else { |
|
|
|
|
backend.opts.upscale = val; |
|
|
|
|
backend.log_width = backend.width / val; |
|
|
|
|
backend.log_height = backend.height / val; |
|
|
|
|
debug!("Set upscale to {:?}", val); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
_other => { |
|
|
|
|
unreachable!() |
|
|
|
@ -106,8 +139,9 @@ impl OpTrait for ScreenOp { |
|
|
|
|
match &mut backend.window { |
|
|
|
|
Some(w) => { |
|
|
|
|
if !w.is_open() { |
|
|
|
|
// TODO...
|
|
|
|
|
std::process::exit(0); |
|
|
|
|
debug!("Window is closed"); |
|
|
|
|
let _ = backend.window.take(); |
|
|
|
|
return Err(Fault::Halt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
w.update(); |
|
|
|
@ -126,23 +160,46 @@ impl OpTrait for ScreenOp { |
|
|
|
|
|
|
|
|
|
let backend: &mut Backend = state.ext_mut(); |
|
|
|
|
|
|
|
|
|
if x >= backend.width as u64 || y >= backend.height as u64 { |
|
|
|
|
if x >= backend.log_width as u64 || y >= backend.log_height as u64 { |
|
|
|
|
state.set_flag(Flag::Overflow, true); |
|
|
|
|
return Ok(eres); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match &mut backend.window { |
|
|
|
|
Some(_w) => { |
|
|
|
|
backend.draw_rect_log(x, y, 1, 1, color as u32); |
|
|
|
|
if backend.opts.auto_blit { |
|
|
|
|
blit_maybe(backend)?; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
None => { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
ScreenOp::GetPixel { color, x, y } => { |
|
|
|
|
state.clear_status(); |
|
|
|
|
let x = state.read(x)?; |
|
|
|
|
let y = state.read(y)?; |
|
|
|
|
|
|
|
|
|
let backend: &mut Backend = state.ext_mut(); |
|
|
|
|
|
|
|
|
|
if x >= backend.log_width as u64 || y >= backend.log_height as u64 { |
|
|
|
|
state.set_flag(Flag::Overflow, true); |
|
|
|
|
return Ok(eres); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match &mut backend.window { |
|
|
|
|
Some(_w) => { |
|
|
|
|
let index = y * backend.width as u64 + x; |
|
|
|
|
let index = y * backend.opts.upscale * backend.width as u64 + x * backend.opts.upscale; |
|
|
|
|
if index as usize > backend.buffer.len() { |
|
|
|
|
warn!("Screen set pixel out of bounds"); |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
} else { |
|
|
|
|
backend.buffer[index as usize] = color as u32; |
|
|
|
|
|
|
|
|
|
if backend.opts.auto_blit { |
|
|
|
|
blit_maybe(backend)?; |
|
|
|
|
} |
|
|
|
|
let c = backend.buffer[index as usize]; |
|
|
|
|
trace!("index {}, c {}", index, c); |
|
|
|
|
state.write(color, c as Value)?; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
None => { |
|
|
|
@ -163,8 +220,11 @@ impl OpTrait for ScreenOp { |
|
|
|
|
state.set_flag(Flag::Overflow, true); |
|
|
|
|
} |
|
|
|
|
Some((xf, yf)) => { |
|
|
|
|
let xval = xf.round() as u64; |
|
|
|
|
let yval = yf.round() as u64; |
|
|
|
|
let mut xval = xf.round() as u64; |
|
|
|
|
let mut yval = yf.round() as u64; |
|
|
|
|
|
|
|
|
|
xval /= backend.opts.upscale; |
|
|
|
|
yval /= backend.opts.upscale; |
|
|
|
|
|
|
|
|
|
state.write(x, xval)?; |
|
|
|
|
state.write(y, yval)?; |
|
|
|
@ -227,6 +287,51 @@ impl OpTrait for ScreenOp { |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
ScreenOp::FillRect { |
|
|
|
|
x, y, |
|
|
|
|
w, h, |
|
|
|
|
color |
|
|
|
|
} => { |
|
|
|
|
let mut x = state.read(x)?; |
|
|
|
|
let mut y = state.read(y)?; |
|
|
|
|
let mut w = state.read(w)?; |
|
|
|
|
let mut h = state.read(h)?; |
|
|
|
|
let c = state.read(color)?; |
|
|
|
|
let backend: &mut Backend = state.ext_mut(); |
|
|
|
|
|
|
|
|
|
if is_negative(w) || is_negative(h) { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
return Ok(eres); |
|
|
|
|
} |
|
|
|
|
if w > backend.log_width { |
|
|
|
|
w = backend.log_width; |
|
|
|
|
} |
|
|
|
|
if h > backend.log_height { |
|
|
|
|
h = backend.log_height; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if is_negative(x) { |
|
|
|
|
let xi : i64 = unsafe { mem::transmute(x) }; |
|
|
|
|
let minus = xi.abs() as u64; |
|
|
|
|
w -= minus; |
|
|
|
|
x = 0; |
|
|
|
|
} |
|
|
|
|
if is_negative(y) { |
|
|
|
|
let yi : i64 = unsafe { mem::transmute(y) }; |
|
|
|
|
let minus = yi.abs() as u64; |
|
|
|
|
h -= minus; |
|
|
|
|
y = 0; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
match &mut backend.window { |
|
|
|
|
Some(_w) => { |
|
|
|
|
backend.draw_rect_log(x, y, w, h, c as u32); |
|
|
|
|
} |
|
|
|
|
None => { |
|
|
|
|
state.set_flag(Flag::Invalid, true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Ok(eres) |
|
|
|
@ -236,13 +341,17 @@ impl OpTrait for ScreenOp { |
|
|
|
|
match self { |
|
|
|
|
ScreenOp::SetOpt { opt, val } => sexp::list(&[A("sc-opt"), A(opt), A(val)]), |
|
|
|
|
ScreenOp::ScreenInit { width, height } => sexp::list(&[A("sc-init"), A(width), A(height)]), |
|
|
|
|
ScreenOp::SetPixel { x, y, color } => sexp::list(&[A("sc-px"), A(x), A(y), A(color)]), |
|
|
|
|
ScreenOp::SetPixel { x, y, color } => sexp::list(&[A("sc-wr"), A(x), A(y), A(color)]), |
|
|
|
|
ScreenOp::GetPixel { color, x, y } => sexp::list(&[A("sc-rd"), A(color), A(x), A(y)]), |
|
|
|
|
ScreenOp::Blit { force } => sexp::list(&[A("sc-blit"), A(force)]), |
|
|
|
|
ScreenOp::Update => sexp::list(&[A("sc-poll")]), |
|
|
|
|
ScreenOp::GetMouse { x, y } => sexp::list(&[A("sc-mouse"), A(x), A(y)]), |
|
|
|
|
ScreenOp::TestKey { pressed, code } => sexp::list(&[A("sc-key"), A(pressed), A(code)]), |
|
|
|
|
ScreenOp::TestMouse { pressed, button } => sexp::list(&[A("sc-mbtn"), A(pressed), A(button)]), |
|
|
|
|
ScreenOp::Erase { color } => sexp::list(&[A("sc-erase"), A(color)]), |
|
|
|
|
ScreenOp::FillRect { x, y, w, h, color } => { |
|
|
|
|
sexp::list(&[A("sc-rect"), A(x), A(y), A(w), A(h), A(color)]) |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
@ -266,8 +375,10 @@ fn init(state: &mut RunState, width: Value, height: Value) -> Result<(), Fault> |
|
|
|
|
return Err(Fault::NotAllowed("Screen already initialized".into())); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
backend.width = width as usize; |
|
|
|
|
backend.height = height as usize; |
|
|
|
|
backend.width = width; |
|
|
|
|
backend.height = height; |
|
|
|
|
backend.log_width = width; |
|
|
|
|
backend.log_height = height; |
|
|
|
|
backend.buffer = vec![0; (width * height) as usize]; |
|
|
|
|
|
|
|
|
|
backend.window = Some(window); |
|
|
|
@ -289,10 +400,12 @@ fn blit(backend: &mut Backend) -> Result<(), Fault> { |
|
|
|
|
let w = backend.window.as_mut().unwrap(); |
|
|
|
|
|
|
|
|
|
if !w.is_open() { |
|
|
|
|
debug!("Window is closed"); |
|
|
|
|
let _ = backend.window.take(); |
|
|
|
|
return Err(Fault::Halt); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
w.update_with_buffer(&backend.buffer, backend.width, backend.height) |
|
|
|
|
w.update_with_buffer(&backend.buffer, backend.width as usize, backend.height as usize) |
|
|
|
|
.expect("Update screen"); // TODO fault
|
|
|
|
|
|
|
|
|
|
backend.last_render = Instant::now(); |
|
|
|
|