Compare commits

..

18 Commits

Author SHA1 Message Date
Mwa
128a53bd3b improved debug 2026-03-27 22:40:28 +01:00
Mwa
689f4b77fd slightly improved asm debug message 2026-03-26 21:24:14 +01:00
Mwa
96c3a514f2 fixed up debug PC/SP addr 2026-03-26 20:26:51 +01:00
Mwa
e97b4c4ee4 fixed up debug command 2026-03-26 20:11:32 +01:00
Mwa
a51b6589f0 removed subdir 2026-03-26 17:07:00 +01:00
Mwa
cebc8fcc5b bitmap to asm moved to the other repo 2026-03-26 13:18:51 +01:00
Mwa
b664538923 added color to img 2026-03-25 09:42:23 +01:00
Mwa
3525db5299 added a warn when frame lag 2026-03-24 23:16:33 +01:00
Mwa
8d7cb5e6f3 fixed import on non-debug build 2026-03-24 15:36:52 +01:00
Mwa
ced0e13f6b improved debug interface 2026-03-23 19:19:38 +01:00
Mwa
c6b58dbc21 again, some minor perf improvement, probably be the last 2026-03-21 00:08:45 +01:00
Mwa
af111c5992 minor interupt on halt responsivity improvement and final Cargo.toml args 2026-03-20 19:30:00 +01:00
Mwa
b5962c6b50 minor performance improvement for jump/call 2026-03-20 11:18:48 +01:00
Mwa
ff6427b020 performance improvement 2026-03-20 11:08:18 +01:00
Mwa
c72e133cde Implemented correct behavior for lsl, asr, lsr (mask and correct for input = 32) 2026-03-19 21:35:49 +01:00
Mwa
c844f8d806 added nosize arg 2026-03-18 15:07:05 +01:00
Mwa
5c5d8471fa minor graphic modification 2026-03-17 23:10:58 +01:00
Mwa
94120273bb better rgba 2026-03-17 22:04:21 +01:00
10 changed files with 1083 additions and 1235 deletions

2
.cargo/config.toml Normal file
View File

@@ -0,0 +1,2 @@
[build]
rustflags = ["-Ctarget-cpu=native"]

1196
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,7 @@
[workspace] [workspace]
resolver = "3" resolver = "3"
members = ["simu","asm","bitmap_to_asm"] members = ["simu","asm"]
[profile.release] [profile.release]
debug = "line-tables-only" opt-level = 2
# panic = "abort"

View File

@@ -31,7 +31,7 @@ syntaxe supplémentaire supportée:
## Features ## Features
rajouter --features=[liste séparé par des virgules] rajouter --features=[liste séparé par des virgules]
- div_mul: Support des instruction de multiplication / division - div_mul: Support des instruction de multiplication / division
- rgba: Écran au format RGBA plutot que 0BGR - rgba: Écran au format RGBA plutot que 0BGR (alpha ignoré mais stocké)
- rich_keyboard: rajoute trois champs de mmio pour le clavier. Voir MMIO - rich_keyboard: rajoute trois champs de mmio pour le clavier. Voir MMIO
- debug: repl similaire a la version python du simulateur - debug: repl similaire a la version python du simulateur
- futex: accélère le programme. ne marche pas sur les macs. actif par default, utiliser --no-default-features pour le desactiver - futex: accélère le programme. ne marche pas sur les macs. actif par default, utiliser --no-default-features pour le desactiver
@@ -44,7 +44,7 @@ call 0 termine l'exécution du simulateur proprement
## mmio: ## mmio:
- 0x01000000 à 0x0112c000 : Écran en lecture/écriture. un pixel par 32bits, de gauche a droite puis de bas en haut. - 0x01000000 à 0x0112c000 : Écran en lecture/écriture. un pixel par 32bits, de gauche a droite puis de bas en haut.
Format de pixel en 0BGR, 4 bits par couleur. Format de pixel en 0BGR, 4 bits par couleur.
Passage au format RGBA (a pour alpha) avec la feature rgba Passage au format RGBA (alpha ignoré, mais stocké) avec la feature rgba
- 0x01200000 : Clavier (scancode) (lecture seule) - 0x01200000 : Clavier (scancode) (lecture seule)
- 0x01200004 : Horloge (millisecondes écoulé depuis le début de la simulation, wrappe tout les 49 jours) (lecture seule) - 0x01200004 : Horloge (millisecondes écoulé depuis le début de la simulation, wrappe tout les 49 jours) (lecture seule)
- 0x01200008 : Boutons de la souris (OR des boutons préssés) Gauche = 1, Droit=2, Clic Molette = 3, Autres non testé (lecture seule) - 0x01200008 : Boutons de la souris (OR des boutons préssés) Gauche = 1, Droit=2, Clic Molette = 3, Autres non testé (lecture seule)

View File

@@ -207,7 +207,9 @@ impl Instruction {
} }
}; };
if jump_distance > 15 { if jump_distance > 15 {
println!("Error, cannot skip more than 15 instructions"); println!(
"Error, cannot skip {jump_distance} which is more than 15 instructions"
);
return Err(()); return Err(());
} }
encode_op2(11, cond.into(), jump_distance, reg.into(), *op2) encode_op2(11, cond.into(), jump_distance, reg.into(), *op2)

View File

@@ -1,8 +0,0 @@
[package]
name = "bitmap_to_asm"
version = "0.1.0"
edition = "2024"
[dependencies]
image = "0.25.10"
regex = "1.12.3"

View File

@@ -1,56 +0,0 @@
use regex::Regex;
use std::{borrow::Cow, env::args};
fn main() {
let path = args().nth(1).expect("usage: 1 image file argument");
let (data, name, width, height) = path_to_img(path.as_str());
println!("{name}:");
println!(" D {width}");
println!(" D {height}");
for d in data {
println!(" D 0x{d:08x}");
}
}
fn remove_non_alphanumeric(input: &str) -> Cow<'_, str> {
let re = Regex::new(r"[^a-zA-Z0-9_]+").unwrap();
re.replace_all(input, "")
}
fn path_to_img(path: &str) -> (Vec<u32>, String, u32, u32) {
let img = match image::open(path) {
Ok(img) => img.to_luma8(),
Err(e) => panic!("failed to open image {path}: {e}"),
};
let width = img.width();
let height = img.height();
let mut bytes = Vec::new();
let mut bit = 0;
let mut byte = 0;
for y in 0..height {
for x in 0..width {
let pix = img.get_pixel(x, y)[0];
if pix >= 127 {
byte |= 1 << bit
}
bit += 1;
if bit == 32 {
bytes.push(byte);
byte = 0;
bit = 0;
}
}
}
if bit != 0 {
bytes.push(byte);
}
let path = path.split('/').next_back().unwrap();
let split: Vec<_> = path.split('.').collect();
let name = format!(
"picture_{}",
remove_non_alphanumeric(&split[0..split.len() - 1].join("_")).to_lowercase()
);
(bytes, name, width, height)
}

View File

@@ -9,6 +9,8 @@ winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "b
winit_input_helper = "0.17.0" winit_input_helper = "0.17.0"
parse_int = { version = "0.9.0", optional = true } parse_int = { version = "0.9.0", optional = true }
wait_on_address = { version = "0.1.4", optional = true } wait_on_address = { version = "0.1.4", optional = true }
clap-repl = { version = "0.3.2", optional = true }
clap = { version = "4.6.0", optional = true }
[features] [features]
@@ -16,5 +18,7 @@ default = ["futex"]
div_mul = [] div_mul = []
rgba = [] rgba = []
rich_keyboard = [] rich_keyboard = []
debug = ["dep:parse_int"] debug = ["dep:parse_int","dep:clap-repl","dep:clap"]
futex = ["dep:wait_on_address"] futex = ["dep:wait_on_address"]
clap-repl = ["dep:clap-repl"]
clap = ["dep:clap"]

View File

@@ -2,14 +2,16 @@ use crate::wait::WaitOnAtomic;
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
use std::collections::HashMap; use std::collections::HashMap;
use std::{ use std::{
hint::{likely, unlikely, unreachable_unchecked}, hint::{cold_path, likely, unlikely, unreachable_unchecked},
io::Read, io::Read,
mem::transmute, mem::transmute,
ops::{Index, IndexMut}, ops::{Index, IndexMut},
process::exit,
sync::atomic::AtomicU32, sync::atomic::AtomicU32,
time::{self, Instant}, time::{self, Instant},
}; };
#[cfg(not(feature = "debug"))]
use std::process::exit;
pub(crate) struct SharedState { pub(crate) struct SharedState {
pub(crate) keyboard: [AtomicU32; 4], pub(crate) keyboard: [AtomicU32; 4],
pub(crate) screen_buf: [AtomicU32; 480 * 640], pub(crate) screen_buf: [AtomicU32; 480 * 640],
@@ -32,17 +34,26 @@ enum Op2 {
} }
struct Reg(u8); struct Reg(u8);
#[allow(unused)] //constructed by transmute
#[repr(u8)]
enum Cond { enum Cond {
Ifeq, Ifeq = 0b000,
Ifne, Ifne = 0b0001,
Iflt, UK1 = 2,
Ifge, UK2 = 3,
Ifgt, UK3 = 4,
Ifle, UK4 = 5,
Ifult, UK5 = 6,
Ifuge, UK6 = 7,
Ifugt, Iflt = 0b1000,
Ifule, Ifge = 0b1001,
Ifgt = 0b1010,
Ifle = 0b1011,
Ifult = 0b1100,
Ifuge = 0b1101,
Ifugt = 0b1110,
Ifule = 0b1111,
} }
#[derive(Debug)] #[derive(Debug)]
@@ -73,20 +84,10 @@ pub enum MMIOInterupt {
} }
impl From<u8> for Cond { impl From<u8> for Cond {
#[inline(always)]
fn from(value: u8) -> Self { fn from(value: u8) -> Self {
match value { //unsafe if called with value >= 15 buuut only ever called on actually an u4 so it's ok
0b0000 => Cond::Ifeq, unsafe { transmute(value) }
0b0001 => Cond::Ifne,
0b1000 => Cond::Iflt,
0b1001 => Cond::Ifge,
0b1010 => Cond::Ifgt,
0b1011 => Cond::Ifle,
0b1100 => Cond::Ifult,
0b1101 => Cond::Ifuge,
0b1110 => Cond::Ifugt,
0b1111 => Cond::Ifule,
_ => iot(),
}
} }
} }
@@ -108,8 +109,9 @@ impl From<u32> for InteruptKind {
} }
impl Cond { impl Cond {
fn eval(self, a: u32, b: u32) -> bool { #[inline(always)]
match self { fn eval(self, a: u32, b: u32) -> Option<bool> {
Some(match self {
Cond::Ifeq => a == b, Cond::Ifeq => a == b,
Cond::Ifne => a != b, Cond::Ifne => a != b,
Cond::Iflt => (a as i32) < (b as i32), Cond::Iflt => (a as i32) < (b as i32),
@@ -120,7 +122,8 @@ impl Cond {
Cond::Ifuge => a >= b, Cond::Ifuge => a >= b,
Cond::Ifugt => a > b, Cond::Ifugt => a > b,
Cond::Ifule => a <= b, Cond::Ifule => a <= b,
} _ => return None,
})
} }
} }
@@ -163,7 +166,6 @@ impl TryFrom<u32> for Instruction {
Ok(match value >> 30 { Ok(match value >> 30 {
0b00 => { 0b00 => {
let t = value & (1 << 29); // 3rd bit set let t = value & (1 << 29); // 3rd bit set
let value = value - t;
let t = t != 0; let t = t != 0;
if t { if t {
Self::Call(value) Self::Call(value)
@@ -275,6 +277,7 @@ impl Display for Cond {
Cond::Ifuge => write!(f, "ifuge"), Cond::Ifuge => write!(f, "ifuge"),
Cond::Ifugt => write!(f, "ifugt"), Cond::Ifugt => write!(f, "ifugt"),
Cond::Ifule => write!(f, "ifule"), Cond::Ifule => write!(f, "ifule"),
_ => write!(f, "unknown"),
} }
} }
} }
@@ -313,7 +316,7 @@ pub(crate) fn instr_to_text(i: u32, a: u32, book: &HashMap<u32, String>) -> Stri
format!("skip {} {cond} {reg} {op2}", addr(d as u32)) format!("skip {} {cond} {reg} {op2}", addr(d as u32))
} }
Instruction::Jump(a) => format!("jump {}", addr(a)), Instruction::Jump(a) => format!("jump {}", addr(a)),
Instruction::Call(a) => format!("call {}", addr(a)), Instruction::Call(a) => format!("call {}", addr(a % 0x1000_0000)),
Instruction::Ret() => format!("ret"), Instruction::Ret() => format!("ret"),
Instruction::Reti() => format!("reti"), Instruction::Reti() => format!("reti"),
Instruction::Swi() => format!("swi"), Instruction::Swi() => format!("swi"),
@@ -338,7 +341,7 @@ pub struct Computer {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
pub(crate) error: bool, pub(crate) error: bool,
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
pub(crate) book: HashMap<u32, String>, pub(crate) book: (HashMap<u32, String>, HashMap<String, u32>),
} }
impl Index<Reg> for Computer { impl Index<Reg> for Computer {
@@ -355,10 +358,6 @@ impl IndexMut<Reg> for Computer {
} }
} }
fn iot() -> ! {
exit(1);
}
impl Computer { impl Computer {
pub fn new(filename: String) -> Self { pub fn new(filename: String) -> Self {
let mut new = Self { let mut new = Self {
@@ -371,7 +370,7 @@ impl Computer {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
error: false, error: false,
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
book: HashMap::new(), book: (HashMap::new(), HashMap::new()),
}; };
let mut buf = String::new(); let mut buf = String::new();
std::fs::File::open(filename) std::fs::File::open(filename)
@@ -389,14 +388,16 @@ impl Computer {
while let Some((_, line)) = lines.next() { while let Some((_, line)) = lines.next() {
if let Some([addr, s]) = line.split_ascii_whitespace().collect::<Vec<_>>().as_array() { if let Some([addr, s]) = line.split_ascii_whitespace().collect::<Vec<_>>().as_array() {
if let Ok(i) = u32::from_str_radix(addr, 16) { if let Ok(i) = u32::from_str_radix(addr, 16) {
new.book.insert(i, s.to_string()); new.book.0.insert(i, s.to_string());
new.book.1.insert(s.to_string(), i);
} }
} }
} }
new new
} }
#[inline(always)] #[inline(always)]
pub fn step(&mut self) { pub fn step(&mut self, s: usize) {
match self.interupts { match self.interupts {
InteruptState::Disabled => {} InteruptState::Disabled => {}
InteruptState::Enabled => { InteruptState::Enabled => {
@@ -415,7 +416,7 @@ impl Computer {
} }
InteruptState::Serving(..) => {} InteruptState::Serving(..) => {}
} }
for _ in 0..s {
//potentially just changed by interupt. //potentially just changed by interupt.
let next_opcode = self.ram[self.pc]; let next_opcode = self.ram[self.pc];
@@ -449,15 +450,15 @@ impl Computer {
self.pc += 1; self.pc += 1;
} }
Instruction::Lsl(reg, reg1, op2) => { Instruction::Lsl(reg, reg1, op2) => {
self[reg] = self[reg1] << self.resolve(op2); self[reg] = (self[reg1] as u64).wrapping_shl(self.resolve(op2)) as u32;
self.pc += 1; self.pc += 1;
} }
Instruction::Lsr(reg, reg1, op2) => { Instruction::Lsr(reg, reg1, op2) => {
self[reg] = self[reg1] >> self.resolve(op2); self[reg] = (self[reg1] as u64).wrapping_shr(self.resolve(op2)) as u32;
self.pc += 1; self.pc += 1;
} }
Instruction::Asr(reg, reg1, op2) => { Instruction::Asr(reg, reg1, op2) => {
self[reg] = (self[reg1] as i32 >> self.resolve(op2)) as u32; self[reg] = (self[reg1] as i64).wrapping_shr(self.resolve(op2)) as u32;
self.pc += 1; self.pc += 1;
} }
Instruction::Umull(reg, reg1, op2) => { Instruction::Umull(reg, reg1, op2) => {
@@ -508,13 +509,18 @@ impl Computer {
self.ram[addr / 4] = self[reg1]; self.ram[addr / 4] = self[reg1];
} else if addr <= 0x00ff_ffff + 480 * 640 * 4 { } else if addr <= 0x00ff_ffff + 480 * 640 * 4 {
let buf_addr = (addr - 0x0100_0000) / 4; let buf_addr = (addr - 0x0100_0000) / 4;
let dat = self[reg1] & 0x00FF_FFFF; let dat = if cfg!(feature = "rgba") {
self[reg1]
} else {
self[reg1] & 0x00FF_FFFF
};
(&SHARED.screen_buf[buf_addr]) (&SHARED.screen_buf[buf_addr])
.store(dat, std::sync::atomic::Ordering::Relaxed); .store(dat, std::sync::atomic::Ordering::Relaxed);
} else if addr == 0x0120_1000 { } else if addr == 0x0120_1000 {
(&SHARED.external_enabled_interupts) (&SHARED.external_enabled_interupts)
.store(self[reg1], std::sync::atomic::Ordering::Relaxed); .store(self[reg1], std::sync::atomic::Ordering::Relaxed);
} else { } else {
cold_path();
self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode]); self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode]);
} }
} }
@@ -537,23 +543,34 @@ impl Computer {
} else { } else {
match addr as isize - 0x0120_0000 { match addr as isize - 0x0120_0000 {
#[cfg(feature = "rich_keyboard")] #[cfg(feature = "rich_keyboard")]
-12 => { -12 => SHARED.keyboard[0]
SHARED.keyboard[0].load(std::sync::atomic::Ordering::Relaxed) .load(std::sync::atomic::Ordering::Relaxed),
}
#[cfg(feature = "rich_keyboard")] #[cfg(feature = "rich_keyboard")]
-8 => SHARED.keyboard[1].load(std::sync::atomic::Ordering::Relaxed), -8 => SHARED.keyboard[1]
.load(std::sync::atomic::Ordering::Relaxed),
#[cfg(feature = "rich_keyboard")] #[cfg(feature = "rich_keyboard")]
-4 => SHARED.keyboard[2].load(std::sync::atomic::Ordering::Relaxed), -4 => SHARED.keyboard[2]
0 => SHARED.keyboard[3].load(std::sync::atomic::Ordering::Relaxed), .load(std::sync::atomic::Ordering::Relaxed),
0 => SHARED.keyboard[3]
.load(std::sync::atomic::Ordering::Relaxed),
4 => time::Instant::now() 4 => time::Instant::now()
.duration_since(self.creation) .duration_since(self.creation)
.as_millis() as u32, .as_millis()
as u32,
8 => SHARED.mouse[0].load(std::sync::atomic::Ordering::Relaxed), 8 => SHARED.mouse[0].load(std::sync::atomic::Ordering::Relaxed),
12 => SHARED.mouse[1].load(std::sync::atomic::Ordering::Relaxed), 12 => {
16 => SHARED.mouse[2].load(std::sync::atomic::Ordering::Relaxed), SHARED.mouse[1].load(std::sync::atomic::Ordering::Relaxed)
}
16 => {
SHARED.mouse[2].load(std::sync::atomic::Ordering::Relaxed)
}
//guaranted by the inequality and is multiple of 4 //guaranted by the inequality and is multiple of 4
_ => { _ => {
self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode]); cold_path();
self.serve_interupt(
InteruptKind::IllegalOpcode,
[next_opcode],
);
return; return;
} }
} }
@@ -571,14 +588,17 @@ impl Computer {
} }
Instruction::Skip(d, cond, reg, op2) => { Instruction::Skip(d, cond, reg, op2) => {
self.pc += 1; self.pc += 1;
if cond.eval(self[reg], self.resolve(op2)) { match cond.eval(self[reg], self.resolve(op2)) {
self.pc += d as usize Some(false) => { /*Nothing*/ }
Some(true) => self.pc += d as usize,
None => {
cold_path();
self.serve_interupt(InteruptKind::IllegalOpcode, [next_opcode])
} }
} }
Instruction::Jump(mut addr) => { }
if addr & (1 << 28) != 0 { Instruction::Jump(addr) => {
addr += 7 << 29; if unlikely(addr == 0) {
} else if addr == 0 {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
{ {
match self.interupts { match self.interupts {
@@ -601,16 +621,16 @@ impl Computer {
println!("awaiting interupt..."); println!("awaiting interupt...");
} }
SHARED.external_interupts.wait(0); SHARED.external_interupts.wait(0);
return;
} }
self.pc = (addr + self.pc as u32) as usize; self.pc = ((addr + self.pc as u32) & 0x1FFF_FFFF) as usize;
} }
Instruction::Call(mut addr) => { Instruction::Call(addr) => {
//WARNING! addr still has the type bit set at this point!
self.sp -= 1; self.sp -= 1;
self.ram[self.sp] = ((self.pc << 2) + 4) as u32; self.ram[self.sp] = ((self.pc << 2) + 4) as u32;
if addr & (1 << 28) != 0 { if unlikely(addr == 0 + (1 << 29) /*t bit*/) {
addr += 7 << 29;
} else if unlikely(addr == 0) {
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
{ {
println!("program terminated"); println!("program terminated");
@@ -620,7 +640,8 @@ impl Computer {
#[cfg(not(feature = "debug"))] #[cfg(not(feature = "debug"))]
exit(0); exit(0);
} }
self.pc = (addr + self.pc as u32) as usize; //The mask take care of both wrapping and shedding the t bit
self.pc = ((addr + self.pc as u32) & 0x1FFF_FFFF) as usize;
} }
Instruction::Ret() => { Instruction::Ret() => {
self.pc = (self.ram[self.sp] >> 2) as usize; self.pc = (self.ram[self.sp] >> 2) as usize;
@@ -648,7 +669,8 @@ impl Computer {
self.interupts = InteruptState::Enabled self.interupts = InteruptState::Enabled
} }
InteruptKind::Swi => {} InteruptKind::Swi => {}
InteruptKind::DivByZero | InteruptKind::UnsupportedOpcode => { InteruptKind::DivByZero
| InteruptKind::UnsupportedOpcode => {
ret_index = Some(self.regs[0]); ret_index = Some(self.regs[0]);
ret_value = self.regs[1]; ret_value = self.regs[1];
} }
@@ -687,7 +709,8 @@ impl Computer {
(&SHARED.external_enabled_interupts) (&SHARED.external_enabled_interupts)
.store(0, std::sync::atomic::Ordering::Relaxed); .store(0, std::sync::atomic::Ordering::Relaxed);
(&SHARED.external_interupts).store(0, std::sync::atomic::Ordering::Relaxed); (&SHARED.external_interupts)
.store(0, std::sync::atomic::Ordering::Relaxed);
SHARED.external_interupts.signal(); SHARED.external_interupts.signal();
} }
Instruction::Swi() => { Instruction::Swi() => {
@@ -712,14 +735,26 @@ impl Computer {
Err((kind, rx, ry, op2, opcode)) => { Err((kind, rx, ry, op2, opcode)) => {
self.pc += 1; self.pc += 1;
match kind { match kind {
InteruptKind::UnsupportedOpcode => self InteruptKind::UnsupportedOpcode => self.serve_interupt(
.serve_interupt(kind, [rx.0.into(), self[ry], self.resolve(op2), opcode]), kind,
InteruptKind::IllegalOpcode => self.serve_interupt(kind, [next_opcode]), [rx.0.into(), self[ry], self.resolve(op2), opcode],
),
InteruptKind::IllegalOpcode => {
cold_path();
self.serve_interupt(kind, [next_opcode])
}
_ => unsafe { unreachable_unchecked() }, _ => unsafe { unreachable_unchecked() },
} }
} }
} }
} }
}
#[cfg(feature = "debug")]
pub fn debug_step(&mut self, s: usize) {
self.step(s);
}
#[inline(always)]
fn resolve(&self, op2: Op2) -> u32 { fn resolve(&self, op2: Op2) -> u32 {
match op2 { match op2 {
Op2::Direct(v) => v, Op2::Direct(v) => v,

View File

@@ -1,9 +1,4 @@
#![feature( #![feature(likely_unlikely, widening_mul, int_lowest_highest_one)]
likely_unlikely,
widening_mul,
sync_unsafe_cell,
int_lowest_highest_one
)]
#![deny(clippy::all)] #![deny(clippy::all)]
use std::env::args; use std::env::args;
@@ -16,12 +11,12 @@ use std::sync::{
use std::thread::scope; use std::thread::scope;
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use pixels::{Error, Pixels, SurfaceTexture}; use pixels::wgpu::{BlendState, Color};
use pixels::{Error, Pixels, PixelsBuilder, SurfaceTexture};
use winit::application::ApplicationHandler; use winit::application::ApplicationHandler;
use winit::dpi::LogicalSize; use winit::dpi::LogicalSize;
use winit::event::WindowEvent; use winit::event::WindowEvent;
use winit::event_loop::EventLoop; use winit::event_loop::EventLoop;
use winit::platform::modifier_supplement::KeyEventExtModifierSupplement;
use winit::platform::scancode::PhysicalKeyExtScancode; use winit::platform::scancode::PhysicalKeyExtScancode;
use winit::window::Window; use winit::window::Window;
@@ -67,7 +62,12 @@ impl<'a> ApplicationHandler for App<'a> {
self.w = Some(window.clone()); self.w = Some(window.clone());
let size = window.inner_size(); let size = window.inner_size();
let surface_texture = SurfaceTexture::new(size.width, size.height, window); let surface_texture = SurfaceTexture::new(size.width, size.height, window);
self.pixels = Some(Pixels::new(WIDTH, HEIGHT, surface_texture).unwrap()); 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( fn window_event(
@@ -92,6 +92,7 @@ impl<'a> ApplicationHandler for App<'a> {
print!("Keyboard event: "); print!("Keyboard event: ");
#[cfg(feature = "rich_keyboard")] #[cfg(feature = "rich_keyboard")]
{ {
use winit::platform::modifier_supplement::KeyEventExtModifierSupplement;
let kb0 = key_event.text_with_all_modifiers().map_or(u32::MAX, |s| { let kb0 = key_event.text_with_all_modifiers().map_or(u32::MAX, |s| {
s.as_bytes() s.as_bytes()
.into_iter() .into_iter()
@@ -207,14 +208,9 @@ impl<'a> ApplicationHandler for App<'a> {
for (addr, ubgr) in cpu::SHARED.screen_buf.iter().enumerate() { for (addr, ubgr) in cpu::SHARED.screen_buf.iter().enumerate() {
let raw = ubgr.load(std::sync::atomic::Ordering::Relaxed); let raw = ubgr.load(std::sync::atomic::Ordering::Relaxed);
#[cfg(not(feature = "rgba"))] #[cfg(not(feature = "rgba"))]
let rgba = [raw as u8, (raw >> 8) as u8, (raw >> 16) as u8, 0xff]; let rgba: [u8; 4] = raw.to_le_bytes();
#[cfg(feature = "rgba")] #[cfg(feature = "rgba")]
let rgba = [ let rgba = raw.to_be_bytes();
(raw >> 24) as u8,
(raw >> 16) as u8,
(raw >> 8) as u8,
raw as u8,
];
for i in 0..4 { for i in 0..4 {
screen[addr * 4 + i] = rgba[i]; screen[addr * 4 + i] = rgba[i];
} }
@@ -265,6 +261,7 @@ impl<'a> ApplicationHandler for App<'a> {
let now = Instant::now(); let now = Instant::now();
if next < now { if next < now {
next = now + Duration::from_secs_f64(1. / 30.); next = now + Duration::from_secs_f64(1. / 30.);
println!("Warning: rendering is lagging!")
} }
event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next)); event_loop.set_control_flow(winit::event_loop::ControlFlow::WaitUntil(next));
if let Some(w) = self.w.as_ref() { if let Some(w) = self.w.as_ref() {
@@ -302,147 +299,13 @@ fn main() -> Result<(), Error> {
let mut simulation = Computer::new(program); let mut simulation = Computer::new(program);
#[cfg(not(feature = "debug"))] #[cfg(not(feature = "debug"))]
loop { loop {
simulation.step(); simulation.step(64);
} }
//ugly debug code, I should improve that using a real TUI crate
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
{ {
let mut input = std::io::stdin().lines(); debug_loop(&mut simulation);
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();
}
false
}
"r" | "run" => {
while !simulation.error {
simulation.step();
}
false
}
"p" | "print" => {
if next.len() >= 2 {
match parse_int::parse::<u32>(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();
}
false
}
"t" | "to" => {
if next.len() >= 2 {
match parse_int::parse::<u32>(next[1]) {
Ok(v) => {
while !simulation.error
&& simulation.pc != (v as usize / 4)
{
simulation.step();
}
false
}
Err(e) => {
println!("{e}");
true
}
}
} else {
println!("{HELP_MSG}");
true
}
}
_ => {
println!("{HELP_MSG}");
true
}
}
} else {
println!("{HELP_MSG}");
true
}
} {}
}
} }
}); });
@@ -459,11 +322,210 @@ fn main() -> Result<(), Error> {
} }
#[cfg(feature = "debug")] #[cfg(feature = "debug")]
const HELP_MSG: &str = " fn debug_loop(com: &mut Computer) {
step n - step trough n instructions (alias s) struct Wrap(DefaultCompleter, DefaultCompleter);
run - run program until exit / error (alias r)
context - print context (alias c) use clap::Parser;
print n - print ram content at address n and next 8 (alias p) use clap_repl::ClapEditor;
up - run until the nex ret is reached (alias u) use clap_repl::reedline::{
to n - tun until PC = n (alias t) Completer, DefaultCompleter, DefaultPrompt, DefaultPromptSegment, FileBackedHistory,
"; };
use crate::cpu::instr_to_text;
#[derive(Debug, Parser)]
#[command(name = "")] // This name will show up in clap's error messages, so it is important to set it to "".
enum Commands {
/// Step by single instrcution
#[command(alias = "s")]
Step {
///number of instruction to step (default one)
num: Option<usize>,
},
/// Run until program halt, or specified instruction is reached
#[command(alias = "r")]
Run {
/// Can be either a label or a address (support hex format)
desigantor: Option<String>,
},
/// run until the current function return.
#[command(alias = "u")]
Up,
/// Print memory at address. Support hexa format
#[command(alias = "p")]
Print { address: String },
/// Print the address associated with a label
#[command(alias = "l")]
Label { label: String },
/// Print context
#[command(alias = "c")]
Context,
}
let prompt = DefaultPrompt {
left_prompt: DefaultPromptSegment::Basic(">>".to_owned()),
right_prompt: DefaultPromptSegment::Empty,
};
let commands_comp = DefaultCompleter::new_with_wordlen(
["step", "run", "up", "print", "label", "help", "context"]
.map(|s| s.to_string())
.to_vec(),
0,
);
let mut labels_comp =
DefaultCompleter::with_inclusions("_0123456789".chars().collect::<Vec<_>>().as_slice())
.set_min_word_len(0);
labels_comp.insert(com.book.0.values().cloned().collect());
let editor = ClapEditor::<Commands>::builder()
.with_prompt(Box::new(prompt))
.with_editor_hook(|reed| {
reed.with_history(Box::new(
FileBackedHistory::with_file(1000, "debug_cmd.hist".into()).unwrap(),
))
.with_completer(Box::new(Wrap(commands_comp, labels_comp)))
.with_quick_completions(true)
.with_partial_completions(true)
})
.build();
debug_context(com);
editor.repl(|command| match command {
Commands::Step { num } => {
let steps = num.unwrap_or(1);
com.debug_step(steps);
debug_context(com);
}
Commands::Run { desigantor } => match desigantor {
Some(s) => match parse_int::parse::<usize>(s.as_str()) {
Ok(addr) => {
while com.pc != (addr / 4) && !com.error {
com.debug_step(1);
}
debug_context(com);
}
Err(_) => match com.book.1.get(s.as_str()).cloned() {
Some(addr) => {
while com.pc != (addr as usize / 4) && !com.error {
com.debug_step(1);
}
debug_context(com);
}
None => {
println!("Error, {s} cannot be interpreted as addr nor label")
}
},
},
None => {
while !com.error {
com.debug_step(64);
}
debug_context(com);
}
},
Commands::Up => {
let curr_sp = com.sp;
while (com.sp < curr_sp
|| (com.ram[com.pc] != (0b10001000 << 24) && com.ram[com.pc] != (0b10101000 << 24)))
&& !com.error
{
com.debug_step(1);
}
debug_context(com);
}
Commands::Print { address } => match parse_int::parse::<usize>(address.as_str()) {
Ok(addr) => match com.ram.get(addr / 4) {
Some(i) => {
println!(
"RAM at {addr:8x}: {:8x} {}",
i,
instr_to_text(*i, u32::MAX, &com.book.0)
)
}
None => println!("Cannot index RAM at address {addr:8x}"),
},
Err(_) => match com.book.1.get(address.as_str()).cloned() {
Some(addr) => println!(
"RAM at {addr:8x}: {:8x} {}",
com.ram[addr as usize / 4],
instr_to_text(com.ram[addr as usize / 4], addr, &com.book.0)
),
None => {
println!("Error, {address} cannot be interpreted as addr nor label")
}
},
},
Commands::Label { label } => match com.book.1.get(label.as_str()) {
Some(addr) => println!("label is at addr {addr}"),
None => println!("error: label not found"),
},
Commands::Context => debug_context(com),
});
exit(0);
impl Completer for Wrap {
fn complete(&mut self, line: &str, pos: usize) -> Vec<clap_repl::reedline::Suggestion> {
let trimmed = line.trim_start();
let line_parts = trimmed.splitn(2, ' ').collect::<Vec<_>>();
if line_parts.len() <= 1 {
self.0.complete(line, pos)
} else {
match line_parts[0] {
"r" | "run" | "p" | "print" | "l" | "label" => {
let trimmed_2 = line_parts[1].trim_start();
let offset = line.len() - trimmed_2.len();
let mut sub = self.1.complete(trimmed_2, pos - offset);
for sug in sub.iter_mut() {
use clap_repl::reedline::Span;
sug.span = Span {
start: sug.span.start + offset,
end: sug.span.end + offset,
}
}
sub
}
_ => Vec::new(),
}
}
}
}
}
#[cfg(feature = "debug")]
fn debug_context(com: &Computer) {
use crate::cpu::instr_to_text;
println!("Interupt state: {:?}", com.interupts);
for i in 0..8 {
println!(
"r{i} = {:8x} r{:<2} = {:8x}",
com.regs[i],
i + 8,
com.regs[i + 8]
);
}
println!("SP={:08x} PC={:08x}", com.sp * 4, com.pc * 4);
println!("RAM at SP | Ram at PC:");
let mut pc_lines = Vec::new();
for i in 0..16 {
match com.book.0.get(&((com.pc + i) as u32 * 4)) {
Some(label) => pc_lines.push(format!(" {label}:")),
None => {}
};
pc_lines.push(format!(
"{:08x} {}",
com.ram[com.pc + i],
instr_to_text(com.ram[com.pc + i], (com.pc + i) as u32 * 4, &com.book.0)
));
}
for (i, pc_l) in pc_lines.iter().enumerate() {
if com.sp + i < com.ram.len() {
println!("{:08x} | {pc_l}", com.ram[com.sp + i])
} else {
println!(" -- | {pc_l}")
}
}
}