Compare commits
25 Commits
47efeef83d
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 128a53bd3b | |||
| 689f4b77fd | |||
| 96c3a514f2 | |||
| e97b4c4ee4 | |||
| a51b6589f0 | |||
| cebc8fcc5b | |||
| b664538923 | |||
| 3525db5299 | |||
| 8d7cb5e6f3 | |||
| ced0e13f6b | |||
| c6b58dbc21 | |||
| af111c5992 | |||
| b5962c6b50 | |||
| ff6427b020 | |||
| c72e133cde | |||
| c844f8d806 | |||
| 5c5d8471fa | |||
| 94120273bb | |||
| 978bb30fdb | |||
| 5dd9328008 | |||
| f6cc53075a | |||
| 8fa56c5b6f | |||
| 902ed046ca | |||
| f17b849da2 | |||
| d04b6f35c3 |
2
.cargo/config.toml
Normal file
2
.cargo/config.toml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
[build]
|
||||||
|
rustflags = ["-Ctarget-cpu=native"]
|
||||||
1196
Cargo.lock
generated
1196
Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||||
|
|||||||
@@ -31,9 +31,10 @@ 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
|
||||||
|
|
||||||
### instruction spéciale:
|
### instruction spéciale:
|
||||||
halt (jump 0) met le programme en pause, mais on peut se reveiller par des interuptions
|
halt (jump 0) met le programme en pause, mais on peut se reveiller par des interuptions
|
||||||
@@ -43,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)
|
||||||
@@ -78,6 +79,7 @@ de même, `reti` ne peut pas être appellé par une sous fonction appellée depu
|
|||||||
Les intéruptions et leurs arguments, par priorité croissante:
|
Les intéruptions et leurs arguments, par priorité croissante:
|
||||||
### 0: Point d'entrée
|
### 0: Point d'entrée
|
||||||
C'est ici que le programme commence. Seul cas d'utilisation
|
C'est ici que le programme commence. Seul cas d'utilisation
|
||||||
|
(ce n'est pas vraiment une interruption, juste la première entrée dans la jump table)
|
||||||
### 1: MMIO
|
### 1: MMIO
|
||||||
Doivent être activé par le MMIO a l'adresse 0x01201000, en y écrivant le OR des (1<<identifiant)
|
Doivent être activé par le MMIO a l'adresse 0x01201000, en y écrivant le OR des (1<<identifiant)
|
||||||
On toujours pour argument (r0) leur identifiant
|
On toujours pour argument (r0) leur identifiant
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "bitmap_to_asm"
|
|
||||||
version = "0.1.0"
|
|
||||||
edition = "2024"
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
image = "0.25.10"
|
|
||||||
regex = "1.12.3"
|
|
||||||
@@ -1,53 +0,0 @@
|
|||||||
use regex::Regex;
|
|
||||||
use std::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) -> String {
|
|
||||||
let re = Regex::new(r"[^a-zA-Z0-9_]+").unwrap();
|
|
||||||
re.replace_all(input, "").to_string()
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = remove_non_alphanumeric(&split[0..split.len() - 1].join("_")).to_lowercase();
|
|
||||||
(bytes, name, width, height)
|
|
||||||
}
|
|
||||||
@@ -8,11 +8,17 @@ pixels = "0.15.0"
|
|||||||
winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] }
|
winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] }
|
||||||
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 = "0.1.4"
|
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]
|
||||||
|
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"]
|
||||||
|
clap-repl = ["dep:clap-repl"]
|
||||||
|
clap = ["dep:clap"]
|
||||||
|
|||||||
181
simu/src/cpu.rs
181
simu/src/cpu.rs
@@ -1,15 +1,17 @@
|
|||||||
|
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},
|
||||||
};
|
};
|
||||||
use wait_on_address::AtomicWait;
|
|
||||||
|
#[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;
|
||||||
@@ -643,12 +664,13 @@ impl Computer {
|
|||||||
InteruptKind::MMIO => {
|
InteruptKind::MMIO => {
|
||||||
(&SHARED.external_interupts)
|
(&SHARED.external_interupts)
|
||||||
.store(0, std::sync::atomic::Ordering::Release);
|
.store(0, std::sync::atomic::Ordering::Release);
|
||||||
SHARED.external_interupts.notify_one();
|
SHARED.external_interupts.signal();
|
||||||
//no need to check prev because MMIO is the lowest priority
|
//no need to check prev because MMIO is the lowest priority
|
||||||
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,8 +709,9 @@ 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)
|
||||||
SHARED.external_interupts.notify_all();
|
.store(0, std::sync::atomic::Ordering::Relaxed);
|
||||||
|
SHARED.external_interupts.signal();
|
||||||
}
|
}
|
||||||
Instruction::Swi() => {
|
Instruction::Swi() => {
|
||||||
self.pc += 1;
|
self.pc += 1;
|
||||||
@@ -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,
|
||||||
|
|||||||
397
simu/src/main.rs
397
simu/src/main.rs
@@ -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,17 +11,18 @@ 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 wait_on_address::AtomicWait;
|
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::scancode::PhysicalKeyExtScancode;
|
use winit::platform::scancode::PhysicalKeyExtScancode;
|
||||||
use winit::window::Window;
|
use winit::window::Window;
|
||||||
// use winit_input_helper::WinitInputHelper;
|
|
||||||
|
|
||||||
use crate::cpu::{Computer, MMIOInterupt};
|
use crate::cpu::{Computer, MMIOInterupt};
|
||||||
|
mod wait;
|
||||||
|
use wait::WaitOnAtomic;
|
||||||
mod cpu;
|
mod cpu;
|
||||||
use cpu::SHARED;
|
use cpu::SHARED;
|
||||||
|
|
||||||
@@ -66,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(
|
||||||
@@ -91,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()
|
||||||
@@ -121,7 +123,7 @@ impl<'a> ApplicationHandler for App<'a> {
|
|||||||
(&SHARED.external_interupts).store(MMIOInterupt::Keyboard.into(), Release);
|
(&SHARED.external_interupts).store(MMIOInterupt::Keyboard.into(), Release);
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
println!("wake due to keyboard event");
|
println!("wake due to keyboard event");
|
||||||
SHARED.external_interupts.notify_one();
|
SHARED.external_interupts.signal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::CursorMoved { position, .. } => {
|
WindowEvent::CursorMoved { position, .. } => {
|
||||||
@@ -150,7 +152,7 @@ impl<'a> ApplicationHandler for App<'a> {
|
|||||||
(&SHARED.external_interupts).store(MMIOInterupt::MouseMove.into(), Release);
|
(&SHARED.external_interupts).store(MMIOInterupt::MouseMove.into(), Release);
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
println!("wake due mouse move");
|
println!("wake due mouse move");
|
||||||
SHARED.external_interupts.notify_one();
|
SHARED.external_interupts.signal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// WindowEvent::MouseWheel {
|
// WindowEvent::MouseWheel {
|
||||||
@@ -184,7 +186,7 @@ impl<'a> ApplicationHandler for App<'a> {
|
|||||||
(&SHARED.external_interupts).store(MMIOInterupt::MouseClick.into(), Release);
|
(&SHARED.external_interupts).store(MMIOInterupt::MouseClick.into(), Release);
|
||||||
#[cfg(feature = "debug")]
|
#[cfg(feature = "debug")]
|
||||||
println!("wake mouse click");
|
println!("wake mouse click");
|
||||||
SHARED.external_interupts.notify_one();
|
SHARED.external_interupts.signal();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
WindowEvent::ScaleFactorChanged { .. } => {
|
WindowEvent::ScaleFactorChanged { .. } => {
|
||||||
@@ -197,7 +199,7 @@ impl<'a> ApplicationHandler for App<'a> {
|
|||||||
!= 0;
|
!= 0;
|
||||||
if enabled {
|
if enabled {
|
||||||
(&SHARED.external_interupts).store(MMIOInterupt::VSync.into(), Relaxed);
|
(&SHARED.external_interupts).store(MMIOInterupt::VSync.into(), Relaxed);
|
||||||
SHARED.external_interupts.notify_one();
|
SHARED.external_interupts.signal();
|
||||||
wait_int();
|
wait_int();
|
||||||
}
|
}
|
||||||
let pix = self.pixels.as_mut().unwrap();
|
let pix = self.pixels.as_mut().unwrap();
|
||||||
@@ -206,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];
|
||||||
}
|
}
|
||||||
@@ -264,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() {
|
||||||
@@ -301,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 = 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
|
|
||||||
}
|
|
||||||
} {}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -458,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}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
35
simu/src/wait.rs
Normal file
35
simu/src/wait.rs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
use std::sync::atomic::AtomicU32;
|
||||||
|
|
||||||
|
#[cfg(feature = "futex")]
|
||||||
|
use wait_on_address::AtomicWait;
|
||||||
|
|
||||||
|
pub trait WaitOnAtomic {
|
||||||
|
fn wait(&self, v: u32);
|
||||||
|
fn signal(&self);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "futex")]
|
||||||
|
impl WaitOnAtomic for AtomicU32 {
|
||||||
|
fn wait(&self, v: u32) {
|
||||||
|
<AtomicU32 as AtomicWait>::wait(self, v);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signal(&self) {
|
||||||
|
self.notify_one();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "futex"))]
|
||||||
|
impl WaitOnAtomic for AtomicU32 {
|
||||||
|
fn wait(&self, v: u32) {
|
||||||
|
while self.load(std::sync::atomic::Ordering::Acquire) == v {
|
||||||
|
use std::{thread::sleep, time::Duration};
|
||||||
|
|
||||||
|
sleep(Duration::from_micros(500));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn signal(&self) {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user