#![feature( likely_unlikely, widening_mul, sync_unsafe_cell, int_lowest_highest_one )] #![deny(clippy::all)] use std::env::args; use std::hint::unlikely; use std::process::exit; use std::sync::{ Arc, atomic::Ordering::{Acquire, Relaxed, Release}, }; use std::thread::scope; use std::time::{Duration, Instant}; use pixels::wgpu::{BlendState, Color}; use pixels::{Error, Pixels, PixelsBuilder, SurfaceTexture}; use winit::application::ApplicationHandler; use winit::dpi::LogicalSize; use winit::event::WindowEvent; use winit::event_loop::EventLoop; use winit::platform::scancode::PhysicalKeyExtScancode; use winit::window::Window; use crate::cpu::{Computer, MMIOInterupt}; mod wait; use wait::WaitOnAtomic; mod cpu; use cpu::SHARED; fn wait_int() { let mut v = (&SHARED.external_interupts).load(Acquire); while unlikely(v != 0) { #[cfg(feature = "debug")] println!("wating for interupt clear {v}"); SHARED.external_interupts.wait(v); v = (&SHARED.external_interupts).load(Acquire); } } const WIDTH: u32 = 640; const HEIGHT: u32 = 480; struct App<'a> { w: Option>, pixels: Option>, } impl<'a> ApplicationHandler for App<'a> { fn resumed(&mut self, event_loop: &winit::event_loop::ActiveEventLoop) { let window = { let size = LogicalSize::new(WIDTH as f64, HEIGHT as f64); Arc::new( event_loop .create_window( Window::default_attributes() .with_title("bisare screen") .with_min_inner_size(size) .with_maximized(true), ) .unwrap(), ) }; self.w = Some(window.clone()); let size = window.inner_size(); let surface_texture = SurfaceTexture::new(size.width, size.height, window); let pix = PixelsBuilder::new(WIDTH, HEIGHT, surface_texture) .clear_color(Color::BLACK) .blend_state(BlendState::REPLACE).build() ; self.pixels = Some(pix.unwrap()); } fn window_event( &mut self, elwt: &winit::event_loop::ActiveEventLoop, _: winit::window::WindowId, event: WindowEvent, ) { // Draw the current frame match event { WindowEvent::KeyboardInput { event: key_event, .. } => { let enabled = (&SHARED.external_enabled_interupts).load(Relaxed) & Into::::into(MMIOInterupt::Keyboard) != 0; if enabled { wait_int(); } #[cfg(feature = "debug")] print!("Keyboard event: "); #[cfg(feature = "rich_keyboard")] { use winit::platform::modifier_supplement::KeyEventExtModifierSupplement; let kb0 = key_event.text_with_all_modifiers().map_or(u32::MAX, |s| { s.as_bytes() .into_iter() .fold(0, |a, e| a << 8 | (*e as u32)) }); SHARED.keyboard[0].store(kb0, Relaxed); let kb1 = key_event .key_without_modifiers() .to_text() .map_or(u32::MAX, |s| { s.as_bytes() .into_iter() .fold(0, |a, e| a << 8 | (*e as u32)) }); SHARED.keyboard[1].store(kb1, Relaxed); let kb2 = key_event.state.is_pressed() as u32 | ((key_event.repeat as u32) << 1); SHARED.keyboard[2].store(kb2, Relaxed); #[cfg(feature = "debug")] print!("{kb0} {kb1} {kb2}") } let kb3 = key_event.physical_key.to_scancode().unwrap_or(0); #[cfg(feature = "debug")] println!(" {kb3}"); SHARED.keyboard[3].store(kb3, Relaxed); if enabled { (&SHARED.external_interupts).store(MMIOInterupt::Keyboard.into(), Release); #[cfg(feature = "debug")] println!("wake due to keyboard event"); SHARED.external_interupts.signal(); } } WindowEvent::CursorMoved { position, .. } => { let enabled = (&SHARED.external_enabled_interupts).load(Relaxed) & Into::::into(MMIOInterupt::MouseMove) != 0; if enabled { wait_int(); } match self .pixels .as_ref() .unwrap() .window_pos_to_pixel((position.x as f32, position.y as f32)) { Ok((x, y)) => { (&cpu::SHARED.mouse[1]).store(x as u32, Relaxed); (&cpu::SHARED.mouse[2]).store(y as u32, Relaxed); } Err(_) => { (&SHARED.mouse[1]).store(u32::MAX, Relaxed); (&SHARED.mouse[2]).store(u32::MAX, Relaxed); } } if enabled { (&SHARED.external_interupts).store(MMIOInterupt::MouseMove.into(), Release); #[cfg(feature = "debug")] println!("wake due mouse move"); SHARED.external_interupts.signal(); } } // WindowEvent::MouseWheel { // delta, // .. // } => {} WindowEvent::MouseInput { state, button, .. } => { let enabled = (&SHARED.external_enabled_interupts).load(Relaxed) & Into::::into(MMIOInterupt::MouseClick) != 0; if enabled { wait_int(); } let but = 1 << match button { winit::event::MouseButton::Left => 0, winit::event::MouseButton::Right => 1, winit::event::MouseButton::Middle => 2, winit::event::MouseButton::Back => 3, winit::event::MouseButton::Forward => 4, winit::event::MouseButton::Other(i) => i & 31, }; match state { winit::event::ElementState::Pressed => { (&cpu::SHARED.mouse[0]).fetch_or(but, std::sync::atomic::Ordering::Relaxed) } winit::event::ElementState::Released => (&cpu::SHARED.mouse[0]) .fetch_and(!but, std::sync::atomic::Ordering::Relaxed), }; if enabled { (&SHARED.external_interupts).store(MMIOInterupt::MouseClick.into(), Release); #[cfg(feature = "debug")] println!("wake mouse click"); SHARED.external_interupts.signal(); } } WindowEvent::ScaleFactorChanged { .. } => { self.w.as_ref().unwrap().request_redraw(); } //handling redraws and other graphical events WindowEvent::RedrawRequested => { let enabled = (&SHARED.external_enabled_interupts).load(Relaxed) & Into::::into(MMIOInterupt::VSync) != 0; if enabled { (&SHARED.external_interupts).store(MMIOInterupt::VSync.into(), Relaxed); SHARED.external_interupts.signal(); wait_int(); } let pix = self.pixels.as_mut().unwrap(); let screen = pix.frame_mut(); for (addr, ubgr) in cpu::SHARED.screen_buf.iter().enumerate() { let raw = ubgr.load(std::sync::atomic::Ordering::Relaxed); #[cfg(not(feature = "rgba"))] let rgba: [u8;4] = raw.to_le_bytes(); #[cfg(feature = "rgba")] let rgba = raw.to_be_bytes(); for i in 0..4 { screen[addr * 4 + i] = rgba[i]; } } if let Err(_) = pix.render() { elwt.exit(); return; } } WindowEvent::Resized(size) => { if self .pixels .as_mut() .unwrap() .resize_surface(size.width, size.height) .is_err() { println!("Error while resising pixels, exiting!"); elwt.exit(); return; } self.w.as_ref().unwrap().request_redraw(); } WindowEvent::CloseRequested => { elwt.exit(); return; } WindowEvent::Destroyed => { println!("Windows destroyed, exiting!"); elwt.exit(); return; } //do nothing for the other events _ => {} } } fn new_events( &mut self, event_loop: &winit::event_loop::ActiveEventLoop, cause: winit::event::StartCause, ) { match cause { winit::event::StartCause::ResumeTimeReached { requested_resume, .. } => { let mut next = requested_resume + Duration::from_secs_f64(1. / 60.); let now = Instant::now(); if next < now { next = now + Duration::from_secs_f64(1. / 30.); } event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next)); if let Some(w) = self.w.as_ref() { w.request_redraw(); } } winit::event::StartCause::WaitCancelled { .. } => {} winit::event::StartCause::Poll => { let next = Instant::now() + Duration::from_secs_f64(1. / 60.); event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next)); } winit::event::StartCause::Init => { let next = Instant::now() + Duration::from_secs_f64(1. / 60.); event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next)); } } } } fn main() -> Result<(), Error> { let event_loop = EventLoop::new().unwrap(); // let mut input = WinitInputHelper::new(); let mut app = App { w: None, pixels: None, }; let program = args() .nth(1) .expect("you must supply the exec name as the first argument"); scope(|sc| { sc.spawn(|| { let mut simulation = Computer::new(program); #[cfg(not(feature = "debug"))] loop { simulation.step(64); } #[cfg(feature = "debug")] { let mut input = std::io::stdin().lines(); loop { { println!( "interrupts are {:?}, with mmio interupts flags {}", simulation.interupts, SHARED.external_enabled_interupts.load(Relaxed) ); for i in 0..8 { println!( "r{i} 0x{:08x} r{} 0x{:08x}", simulation.regs[i], i + 8, simulation.regs[i + 8] ); } println!( "SP: {:08x} PC: {:08x}", simulation.sp * 4, simulation.pc * 4 ); println!("RAM near SP"); let min_pc = (simulation.pc).min(0x0100_0000 / 4 - 8); let min_sp = (simulation.sp).min(0x0100_0000 / 4 - 8); for i in 0..8 { println!( "{:8x}: 0x{:08x}", (min_sp + i) * 4, simulation.ram[min_sp + i], ); } println!("Ram near PC"); for i in 0..8 { let idx = min_pc + i; let istr = simulation.ram[idx]; if let Some(s) = simulation.book.get(&(idx as u32 * 4)) { println!("{s}:") }; println!( "{:8x}: 0x{:08x} {}", idx * 4, istr, cpu::instr_to_text(istr, idx as u32 * 4, &simulation.book) ) } } while { let next = input.next().unwrap().unwrap(); let next: Vec<_> = next.split_ascii_whitespace().collect(); if next.len() > 0 { match next[0] { "s" | "step" => { let n: usize = { if next.len() >= 2 { parse_int::parse(next[1]).unwrap_or(1) } else { 1 } }; for _ in 0..n { if simulation.error { println!("cannot step, cpu killed"); break; } simulation.step(1); } false } "r" | "run" => { while !simulation.error { simulation.step(64); } false } "p" | "print" => { if next.len() >= 2 { match parse_int::parse::(next[1]) { Ok(i) => { let v = simulation.ram[i as usize / 4]; println!( "0x{:8x} -- {}", v, cpu::instr_to_text(v, i, &simulation.book) ); true } Err(e) => { println!("{e}"); true } } } else { println!("{HELP_MSG}"); true } } "c" | "context" => false, "u" | "up" => { while !simulation.error && simulation.ram[simulation.pc] != 0x8800_0000 { simulation.step(1); } false } "t" | "to" => { if next.len() >= 2 { match parse_int::parse::(next[1]) { Ok(v) => { while !simulation.error && simulation.pc != (v as usize / 4) { simulation.step(1); } false } Err(e) => { println!("{e}"); true } } } else { println!("{HELP_MSG}"); true } } _ => { println!("{HELP_MSG}"); true } } } else { println!("{HELP_MSG}"); true } } {} } } }); #[allow(deprecated)] let res = event_loop.run_app(&mut app); match res { Ok(_) => exit(0), Err(e) => { println!("{e}"); exit(1); } } }) } #[cfg(feature = "debug")] const HELP_MSG: &str = " step n - step trough n instructions (alias s) run - run program until exit / error (alias r) context - print context (alias c) print n - print ram content at address n and next 8 (alias p) up - run until the nex ret is reached (alias u) to n - tun until PC = n (alias t) ";