Interuptions

This commit is contained in:
Mwa
2026-03-15 23:32:13 +01:00
parent aead858727
commit c38bb22e88
6 changed files with 575 additions and 195 deletions

26
Cargo.lock generated
View File

@@ -117,6 +117,16 @@ dependencies = [
"regex",
]
[[package]]
name = "atomic-wait"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a55b94919229f2c42292fd71ffa4b75e83193bffdd77b1e858cd55fd2d0b0ea8"
dependencies = [
"libc",
"windows-sys 0.42.0",
]
[[package]]
name = "atomic-waker"
version = "1.1.2"
@@ -1497,6 +1507,7 @@ checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
name = "simu"
version = "0.1.0"
dependencies = [
"atomic-wait",
"pixels",
"winit",
"winit_input_helper",
@@ -2131,6 +2142,21 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
[[package]]
name = "windows-sys"
version = "0.42.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7"
dependencies = [
"windows_aarch64_gnullvm 0.42.2",
"windows_aarch64_msvc 0.42.2",
"windows_i686_gnu 0.42.2",
"windows_i686_msvc 0.42.2",
"windows_x86_64_gnu 0.42.2",
"windows_x86_64_gnullvm 0.42.2",
"windows_x86_64_msvc 0.42.2",
]
[[package]]
name = "windows-sys"
version = "0.45.0"

View File

@@ -126,6 +126,10 @@ enum Instruction {
Jump(Labeli32), //address / 4
Call(Labeli32), //address / 4
Ret(),
Reti(),
Swi(),
Dint(),
Eint(),
GetStack(Reg),
SetStack(Op2),
Data(Labeli32),
@@ -173,13 +177,9 @@ impl Instruction {
Labeli17::LabelLow(l) => {
encode_imm(1, 1, reg.into(), reg1.into(), (prgm[l] & 0xFFFF) as i32)
}
Labeli17::LabelHigh(l) => encode_imm(
1,
1,
reg.into(),
reg1.into(),
(prgm[l] as u32 >> 16) as i32,
),
Labeli17::LabelHigh(l) => {
encode_imm(1, 1, reg.into(), reg1.into(), (prgm[l] as u32 >> 16) as i32)
}
},
Instruction::Sub(reg, reg1, op2) => encode_op2(01, 2, reg.into(), reg1.into(), *op2),
Instruction::Or(reg, reg1, op2) => encode_op2(01, 3, reg.into(), reg1.into(), *op2),
@@ -233,6 +233,10 @@ impl Instruction {
(dest & 0x1FFF_FFFF) | (1 << 29)
}
Instruction::Ret() => encode_reg(2, 0b1000, 0, 0, 0),
Instruction::Reti() => encode_reg(2, 0b1000, 0, 0, 0) | 1 << 29,
Instruction::Swi() => encode_reg(2, 0b1001, 0, 0, 0),
Instruction::Dint() => encode_reg(2, 0b1011, 0, 0, 0),
Instruction::Eint() => encode_reg(2, 0b1100, 0, 0, 0),
Instruction::GetStack(reg) => encode_reg(2, 0b1101, reg.into(), 0, 0),
Instruction::SetStack(op2) => encode_op2(2, 0b1110, 0, 0, *op2),
Instruction::Data(labeli32) => match labeli32 {
@@ -361,6 +365,7 @@ struct RegexCollection {
}
fn process_line(prgm: &mut Program, (linenum, line): (usize, String), rgx: &RegexCollection) {
let linenum = linenum + 1;
let mut no_comment = line.split(';').next().unwrap_or("");
let binding = no_comment.to_ascii_lowercase();
no_comment = binding.as_str();
@@ -550,6 +555,18 @@ fn process_line(prgm: &mut Program, (linenum, line): (usize, String), rgx: &Rege
chk(0);
Instruction::Ret()
}
"reti" => {
chk(0);
Instruction::Reti()
}
"eint" => {
chk(0);
Instruction::Eint()
}
"dint" => {
chk(0);
Instruction::Dint()
}
"halt" => {
chk(0);
Instruction::Jump(Labeli32::Value(0))

BIN
perf.data Normal file

Binary file not shown.

View File

@@ -4,6 +4,10 @@ version = "0.1.0"
edition = "2024"
[dependencies]
atomic-wait = "1.1.0"
pixels = "0.15.0"
winit = { version = "0.30.13", features = ["x11", "x11-dl", "x11rb", "ahash", "bytemuck", "memmap2", "rwh_06", "sctk", "sctk-adwaita"] }
winit_input_helper = "0.17.0"
[features]
div_mul = []

View File

@@ -1,6 +1,7 @@
use std::{
hint::{likely, unlikely},
hint::{likely, unlikely, unreachable_unchecked},
io::Read,
mem::transmute,
ops::{Index, IndexMut},
process::exit,
sync::atomic::AtomicU32,
@@ -11,11 +12,17 @@ use std::{
pub(crate) struct SharedState {
pub(crate) keyboard: AtomicU32,
pub(crate) screen_buf: [AtomicU32; 480 * 640],
pub(crate) external_interupts: AtomicU32,
pub(crate) external_enabled_interupts: AtomicU32,
pub(crate) mouse: [AtomicU32; 3],
}
pub(crate) static SHARED: SharedState = SharedState {
keyboard: AtomicU32::new(0),
screen_buf: [const { AtomicU32::new(0) }; 480 * 640],
external_interupts: AtomicU32::new(0),
external_enabled_interupts: AtomicU32::new(0),
mouse: [const { AtomicU32::new(0) }; 3],
};
enum Op2 {
@@ -37,6 +44,31 @@ enum Cond {
Ifule,
}
enum InteruptState {
Disabled,
Enabled,
Serving(InteruptKind, u8),
}
#[derive(PartialEq, PartialOrd, Clone, Copy, Debug)]
#[repr(u32)]
enum InteruptKind {
MMIO = 1,
Swi = 2,
DivByZero = 3,
IllegalLoadStore = 4,
UnsupportedOpcode = 5,
IllegalOpcode = 6,
}
#[repr(u32)]
pub enum MMIOInterupt {
Keyboard = 1,
MouseClick = 2,
MouseMove = 4,
VSync = 8,
}
impl From<u8> for Cond {
fn from(value: u8) -> Self {
match value {
@@ -50,13 +82,28 @@ impl From<u8> for Cond {
0b1101 => Cond::Ifuge,
0b1110 => Cond::Ifugt,
0b1111 => Cond::Ifule,
_ => {
unreachable!()
}
_ => iot(),
}
}
}
impl Into<u32> for MMIOInterupt {
fn into(self) -> u32 {
unsafe { transmute(self) }
}
}
impl Into<u32> for InteruptKind {
fn into(self) -> u32 {
unsafe { transmute(self) }
}
}
impl From<u32> for InteruptKind {
fn from(v: u32) -> Self {
unsafe { transmute(v as u32) }
}
}
impl Cond {
fn eval(self, a: u32, b: u32) -> bool {
match self {
@@ -98,13 +145,18 @@ enum Instruction {
Jump(u32), //address / 4
Call(u32), //address / 4
Ret(),
Reti(),
Swi(),
Eint(),
Dint(),
GetStack(Reg),
SetStack(Op2),
}
impl From<u32> for Instruction {
fn from(value: u32) -> Self {
match value >> 30 {
impl TryFrom<u32> for Instruction {
#[inline(always)]
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(match value >> 30 {
0b00 => {
let t = value & (1 << 29); // 3rd bit set
let value = value - t;
@@ -144,25 +196,49 @@ impl From<u32> for Instruction {
(1, 0b0110) => Self::Lsl(rd, rx, op2),
(1, 0b0111) => Self::Lsr(rd, rx, op2),
(1, 0b1000) => Self::Asr(rd, rx, op2),
#[cfg(feature = "div_mul")]
(1, 0b1001) => Self::Smull(rd, rx, op2),
#[cfg(feature = "div_mul")]
(1, 0b1010) => Self::Smulh(rd, rx, op2),
#[cfg(feature = "div_mul")]
(1, 0b1011) => Self::Umull(rd, rx, op2),
#[cfg(feature = "div_mul")]
(1, 0b1100) => Self::Umulh(rd, rx, op2),
#[cfg(feature = "div_mul")]
(1, 0b1101) => Self::Div(rd, rx, op2),
#[cfg(feature = "div_mul")]
(1, 0b1110) => Self::Mod(rd, rx, op2),
#[cfg(not(feature = "div_mul"))]
(1, 0b1001)
| (1, 0b1010)
| (1, 0b1011)
| (1, 0b1100)
| (1, 0b1101)
| (1, 0b1110) => return Err((InteruptKind::UnsupportedOpcode, rd, rx, op2)),
(2, 0b0000) => Self::Store(rx, op2, rd),
(2, 0b0001) => Self::Load(rd, rx, op2),
(2, 0b0010) => Self::Push(op2),
(2, 0b0011) => Self::Pop(rd),
(2, 0b1000) => Self::Ret(),
(2, 0b1000) => {
if s {
Self::Reti()
} else {
Self::Ret()
}
}
(2, 0b1001) => Self::Swi(),
(2, 0b1011) => Self::Dint(),
(2, 0b1100) => Self::Eint(),
(2, 0b1101) => Self::GetStack(rd),
(2, 0b1110) => Self::SetStack(op2),
(3, skip) => Self::Skip(rd.0, (skip as u8).into(), rx, op2),
_ => unreachable!(),
_ => return Err((InteruptKind::IllegalOpcode, rd, rx, op2)),
}
}
}
})
}
type Error = (InteruptKind, Reg, Reg, Op2);
}
pub struct Computer {
@@ -171,6 +247,7 @@ pub struct Computer {
regs: [u32; 16],
pc: usize,
sp: usize,
interupts: InteruptState,
}
impl Index<Reg> for Computer {
@@ -199,6 +276,7 @@ impl Computer {
regs: [0; 16],
pc: 0,
sp: 0,
interupts: InteruptState::Disabled,
};
let mut buf = String::new();
std::fs::File::open(filename)
@@ -213,159 +291,299 @@ impl Computer {
}
new
}
#[inline(always)]
pub fn step(&mut self) {
let next_opcode = self.ram[self.pc];
match Instruction::from(next_opcode) {
Instruction::Copy(reg, op2) => {
self[reg] = self.resolve(op2);
self.pc += 1;
}
Instruction::Add(reg, reg1, op2) => {
self[reg] = self[reg1] + self.resolve(op2);
self.pc += 1;
}
Instruction::Sub(reg, reg1, op2) => {
self[reg] = self[reg1] - self.resolve(op2);
self.pc += 1;
}
Instruction::Or(reg, reg1, op2) => {
self[reg] = self[reg1] | self.resolve(op2);
self.pc += 1;
}
Instruction::And(reg, reg1, op2) => {
self[reg] = self[reg1] & self.resolve(op2);
self.pc += 1;
}
Instruction::Xor(reg, reg1, op2) => {
self[reg] = self[reg1] ^ self.resolve(op2);
self.pc += 1;
}
Instruction::Lsl(reg, reg1, op2) => {
self[reg] = self[reg1] << self.resolve(op2);
self.pc += 1;
}
Instruction::Lsr(reg, reg1, op2) => {
self[reg] = self[reg1] >> self.resolve(op2);
self.pc += 1;
}
Instruction::Asr(reg, reg1, op2) => {
self[reg] = (self[reg1] as i32 >> self.resolve(op2)) as u32;
self.pc += 1;
}
Instruction::Umull(reg, reg1, op2) => {
self[reg] = self[reg1].wrapping_mul(self.resolve(op2));
self.pc += 1;
}
Instruction::Smull(reg, reg1, op2) => {
self[reg] = (self[reg1] as i32).wrapping_mul(self.resolve(op2) as i32) as u32;
self.pc += 1;
}
Instruction::Umulh(reg, reg1, op2) => {
self[reg] = self[reg1].widening_mul(self.resolve(op2)).1;
self.pc += 1;
}
Instruction::Smulh(reg, reg1, op2) => {
self[reg] = (self[reg1] as i32).widening_mul(self.resolve(op2) as i32).1 as u32;
self.pc += 1;
}
Instruction::Div(reg, reg1, op2) => {
self[reg] = self[reg1] / self.resolve(op2);
self.pc += 1
}
Instruction::Mod(reg, reg1, op2) => {
self[reg] = self[reg1] % self.resolve(op2);
self.pc += 1
}
Instruction::Store(reg, op2, reg1) => {
let addr = (self[reg].wrapping_add(self.resolve(op2))) as usize;
if !addr.is_multiple_of(4) {
iot();
}
if addr <= 0x00ffffff {
self.ram[addr / 4] = self[reg1];
} else if addr <= 0x00ff_ffff + 480 * 640 * 4 {
let buf_addr = (addr - 0x0100_0000) / 4;
let dat = self[reg1] & 0x00FF_FFFF;
(&SHARED.screen_buf[buf_addr]).store(dat, std::sync::atomic::Ordering::Relaxed);
} else {
iot();
}
self.pc += 1;
}
Instruction::Load(reg, reg1, op2) => {
let addr = (self[reg1].wrapping_add(self.resolve(op2))) as usize;
if !addr.is_multiple_of(4) {
iot();
}
self[reg] = if addr <= 0x00ffffff {
self.ram[addr / 4]
} else if addr <= 0x00ffffff + 480 * 640 * 4 {
let buf_addr = (addr - 0x0100_0000) / 4;
(&SHARED.screen_buf[buf_addr]).load(std::sync::atomic::Ordering::Relaxed)
} else if addr == 0x01200000 {
SHARED.keyboard.load(std::sync::atomic::Ordering::Relaxed)
} else if addr == 0x01200004 {
time::Instant::now()
.duration_since(self.creation)
.as_millis() as u32
} else {
iot();
};
self.pc += 1;
}
Instruction::Push(op2) => {
self.sp -= 1;
self.ram[self.sp] = self.resolve(op2);
self.pc += 1;
}
Instruction::Pop(reg) => {
self[reg] = self.ram[self.sp];
self.sp += 1;
self.pc += 1;
}
Instruction::Skip(d, cond, reg, op2) => {
self.pc += 1;
if cond.eval(self[reg], self.resolve(op2)) {
self.pc += d as usize
match self.interupts {
InteruptState::Disabled => {}
InteruptState::Enabled => {
let mmio_interupts =
(&SHARED.external_interupts).load(std::sync::atomic::Ordering::Acquire);
if unlikely(mmio_interupts != 0) {
let enabled = (&SHARED.external_enabled_interupts)
.load(std::sync::atomic::Ordering::Relaxed);
if unlikely(mmio_interupts & !(enabled) != 0) {
(&SHARED.external_interupts).store(0, std::sync::atomic::Ordering::Relaxed);
} else {
let kind = mmio_interupts.highest_one().unwrap();
self.serve_interupt(InteruptKind::MMIO, [kind]);
}
}
}
Instruction::Jump(mut addr) => {
if addr & (1 << 28) != 0 {
addr += 7 << 29;
} else if unlikely(addr == 0) {
sleep(Duration::from_hours(1));
}
self.pc = (addr + self.pc as u32) as usize;
}
Instruction::Call(mut addr) => {
self.sp -= 1;
self.ram[self.sp] = ((self.pc << 2) + 4) as u32;
InteruptState::Serving(..) => {}
}
if addr & (1 << 28) != 0 {
addr += 7 << 29;
} else if unlikely(addr == 0) {
sleep(Duration::from_hours(1));
}
self.pc = (addr + self.pc as u32) as usize;
//potentially just changed by interupt.
let next_opcode = self.ram[self.pc];
let instruction = Instruction::try_from(next_opcode);
match instruction {
Ok(instruction) => {
match instruction {
Instruction::Copy(reg, op2) => {
self[reg] = self.resolve(op2);
self.pc += 1;
}
Instruction::Add(reg, reg1, op2) => {
self[reg] = self[reg1] + self.resolve(op2);
self.pc += 1;
}
Instruction::Sub(reg, reg1, op2) => {
self[reg] = self[reg1] - self.resolve(op2);
self.pc += 1;
}
Instruction::Or(reg, reg1, op2) => {
self[reg] = self[reg1] | self.resolve(op2);
self.pc += 1;
}
Instruction::And(reg, reg1, op2) => {
self[reg] = self[reg1] & self.resolve(op2);
self.pc += 1;
}
Instruction::Xor(reg, reg1, op2) => {
self[reg] = self[reg1] ^ self.resolve(op2);
self.pc += 1;
}
Instruction::Lsl(reg, reg1, op2) => {
self[reg] = self[reg1] << self.resolve(op2);
self.pc += 1;
}
Instruction::Lsr(reg, reg1, op2) => {
self[reg] = self[reg1] >> self.resolve(op2);
self.pc += 1;
}
Instruction::Asr(reg, reg1, op2) => {
self[reg] = (self[reg1] as i32 >> self.resolve(op2)) as u32;
self.pc += 1;
}
Instruction::Umull(reg, reg1, op2) => {
self[reg] = self[reg1].wrapping_mul(self.resolve(op2));
self.pc += 1;
}
Instruction::Smull(reg, reg1, op2) => {
self[reg] =
(self[reg1] as i32).wrapping_mul(self.resolve(op2) as i32) as u32;
self.pc += 1;
}
Instruction::Umulh(reg, reg1, op2) => {
self[reg] = self[reg1].widening_mul(self.resolve(op2)).1;
self.pc += 1;
}
Instruction::Smulh(reg, reg1, op2) => {
self[reg] =
(self[reg1] as i32).widening_mul(self.resolve(op2) as i32).1 as u32;
self.pc += 1;
}
Instruction::Div(reg, reg1, op2) => {
self.pc += 1;
let d = self.resolve(op2);
if unlikely(d == 0) {
self.serve_interupt(
InteruptKind::DivByZero,
[reg.0.into(), self[reg1]],
);
return;
}
self[reg] = self[reg1] / d;
}
Instruction::Mod(reg, reg1, op2) => {
self[reg] = self[reg1] % self.resolve(op2);
self.pc += 1
}
Instruction::Store(reg, op2, reg1) => {
self.pc += 1;
let addr = (self[reg].wrapping_add(self.resolve(op2))) as usize;
if !addr.is_multiple_of(4) {
self.serve_interupt(
InteruptKind::IllegalLoadStore,
[0, addr as u32, self[reg1]],
);
return;
}
if addr <= 0x00ffffff {
self.ram[addr / 4] = self[reg1];
} else if addr <= 0x00ff_ffff + 480 * 640 * 4 {
let buf_addr = (addr - 0x0100_0000) / 4;
let dat = self[reg1] & 0x00FF_FFFF;
(&SHARED.screen_buf[buf_addr])
.store(dat, std::sync::atomic::Ordering::Relaxed);
} else if addr == 0x0120_1000 {
(&SHARED.external_enabled_interupts)
.store(self[reg1], std::sync::atomic::Ordering::Relaxed);
} else {
self.serve_interupt(
InteruptKind::IllegalLoadStore,
[0, addr as u32, self[reg1]],
);
}
}
Instruction::Load(reg, reg1, op2) => {
self.pc += 1;
let addr = (self[reg1].wrapping_add(self.resolve(op2))) as usize;
if !addr.is_multiple_of(4) {
self.serve_interupt(
InteruptKind::IllegalLoadStore,
[0, addr as u32, reg.0 as u32],
);
return;
}
self[reg] = if addr <= 0x00ffffff {
self.ram[addr / 4]
} else if addr <= 0x00ffffff + 480 * 640 * 4 {
let buf_addr = (addr - 0x0100_0000) / 4;
(&SHARED.screen_buf[buf_addr])
.load(std::sync::atomic::Ordering::Relaxed)
} else if 0x0120_0000 <= addr && addr <= 0x0120_0010 {
match addr - 0x0120_0000 {
0 => SHARED.keyboard.load(std::sync::atomic::Ordering::Relaxed),
4 => time::Instant::now()
.duration_since(self.creation)
.as_millis() as u32,
8 => SHARED.mouse[0].load(std::sync::atomic::Ordering::Relaxed),
12 => 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
_ => unsafe { unreachable_unchecked() },
}
} else {
self.serve_interupt(
InteruptKind::IllegalLoadStore,
[1, addr as u32, reg.0 as u32],
);
return;
};
}
Instruction::Push(op2) => {
self.sp -= 1;
self.ram[self.sp] = self.resolve(op2);
self.pc += 1;
}
Instruction::Pop(reg) => {
self[reg] = self.ram[self.sp];
self.sp += 1;
self.pc += 1;
}
Instruction::Skip(d, cond, reg, op2) => {
self.pc += 1;
if cond.eval(self[reg], self.resolve(op2)) {
self.pc += d as usize
}
}
Instruction::Jump(mut addr) => {
if addr & (1 << 28) != 0 {
addr += 7 << 29;
} else if addr == 0 {
println!("awaiting interupt...");
atomic_wait::wait(&SHARED.external_interupts, 0);
println!("waking!")
}
self.pc = (addr + self.pc as u32) as usize;
}
Instruction::Call(mut addr) => {
self.sp -= 1;
self.ram[self.sp] = ((self.pc << 2) + 4) as u32;
if addr & (1 << 28) != 0 {
addr += 7 << 29;
} else if unlikely(addr == 0) {
exit(0);
}
self.pc = (addr + self.pc as u32) as usize;
}
Instruction::Ret() => {
self.pc = (self.ram[self.sp] >> 2) as usize;
self.sp += 1;
}
Instruction::Reti() => {
let mut ret_index = None;
let mut ret_value = 0;
match self.interupts {
InteruptState::Serving(kind, prev) => {
match prev.highest_one() {
None => self.interupts = InteruptState::Enabled,
Some(i) => {
self.interupts =
InteruptState::Serving(i.into(), prev ^ (1 << i))
}
}
match kind {
InteruptKind::MMIO => {
(&SHARED.external_interupts)
.store(0, std::sync::atomic::Ordering::Relaxed);
atomic_wait::wake_all(&SHARED.external_interupts);
//no need to check prev because MMIO is the lowest priority
self.interupts = InteruptState::Enabled
}
InteruptKind::Swi => {}
InteruptKind::DivByZero | InteruptKind::UnsupportedOpcode => {
ret_index = Some(self.regs[0]);
ret_value = self.regs[1];
}
InteruptKind::IllegalLoadStore => {}
InteruptKind::IllegalOpcode => {}
}
}
_ => { /* This is a troubling case but ... well it's ok */ }
}
println!("returning from interupt");
let ret = self.ram[self.sp];
self.pc = (ret & 0x0FFF_FFFF) as usize;
self.sp += 1 as usize;
for i in (0..(ret >> 28) as usize).rev() {
self.regs[i] = self.ram[self.sp];
self.sp += 1
}
if let Some(idx) = ret_index {
self.regs[idx as usize] = ret_value;
}
}
Instruction::Eint() => {
match self.interupts {
InteruptState::Disabled => self.interupts = InteruptState::Enabled,
_ => {}
}
self.pc += 1;
}
Instruction::Dint() => {
self.interupts = InteruptState::Disabled;
self.pc += 1;
(&SHARED.external_enabled_interupts)
.store(0, std::sync::atomic::Ordering::Relaxed);
(&SHARED.external_interupts).store(0, std::sync::atomic::Ordering::Relaxed);
atomic_wait::wake_all(&SHARED.external_interupts);
}
Instruction::Swi() => {
self.pc += 1;
self.serve_interupt(InteruptKind::Swi, []);
}
Instruction::GetStack(reg) => {
self[reg] = (self.sp << 2) as u32;
self.pc += 1;
}
Instruction::SetStack(op2) => {
let v = self.resolve(op2);
if likely(v.is_multiple_of(4)) {
self.sp = (v >> 2) as usize;
} else {
self.sp = usize::MAX //Yes, that means that clever program using sp to store information wont work on my emulator. Deal with it
}
self.pc += 1;
}
};
}
Instruction::Ret() => {
self.pc = (self.ram[self.sp] >> 2) as usize;
self.sp += 1;
}
Instruction::GetStack(reg) => {
self[reg] = (self.sp << 2) as u32;
Err((kind, rx, ry, op2)) => {
self.pc += 1;
}
Instruction::SetStack(op2) => {
let v = self.resolve(op2);
if likely(v.is_multiple_of(4)) {
self.sp = (v >> 2) as usize;
} else {
self.sp = usize::MAX //Yes, that means that clever program using sp to store information wont work on my emulator. Deal with it
match kind {
InteruptKind::UnsupportedOpcode => {
self.serve_interupt(kind, [rx.0.into(), self[ry], self.resolve(op2)])
}
InteruptKind::IllegalOpcode => self.serve_interupt(kind, [next_opcode]),
_ => unsafe { unreachable_unchecked() },
}
self.pc += 1;
}
};
}
}
fn resolve(&self, op2: Op2) -> u32 {
match op2 {
@@ -373,4 +591,36 @@ impl Computer {
Op2::Register(r) => self.regs[r as usize],
}
}
fn serve_interupt<const N: usize>(&mut self, kind: InteruptKind, arg: [u32; N]) {
if N >= 16 {
panic!();
}
println!("serving interupt {kind:?} {arg:?}");
match self.interupts {
InteruptState::Disabled => {
println!("Illegal Instruction whith interupt disabled {kind:?} {arg:?}");
exit(1);
}
InteruptState::Enabled => self.interupts = InteruptState::Serving(kind, 0),
InteruptState::Serving(interupt_kind, previous) => {
if kind > interupt_kind {
self.interupts = InteruptState::Serving(
kind,
previous | (1_u8 << Into::<u32>::into(interupt_kind)),
);
} else {
return;
}
}
}
for (i, a) in arg.iter().enumerate() {
self.sp -= 1;
self.ram[self.sp] = self.regs[i];
self.regs[i] = *a
}
self.sp -= 1;
self.ram[self.sp] = (self.pc | N << 28) as u32;
self.pc = Into::<u32>::into(kind) as usize
}
}

View File

@@ -1,10 +1,19 @@
#![feature(likely_unlikely, widening_mul, sync_unsafe_cell)]
#![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;
use std::thread::{scope, sleep};
use std::sync::{
Arc,
atomic::Ordering::{Relaxed, Release},
};
use std::thread::scope;
use std::time::{Duration, Instant};
use pixels::{Error, Pixels, SurfaceTexture};
@@ -16,11 +25,20 @@ use winit::platform::scancode::PhysicalKeyExtScancode;
use winit::window::Window;
// use winit_input_helper::WinitInputHelper;
use crate::cpu::Computer;
use crate::cpu::{Computer, MMIOInterupt};
mod cpu;
use cpu::SHARED;
fn wait_int() {
let mut v = (&SHARED.external_interupts).load(Relaxed);
while unlikely(v != 0) {
println!("wating for interupt clear {v}");
atomic_wait::wait(&SHARED.external_interupts, v);
v = (&SHARED.external_interupts).load(std::sync::atomic::Ordering::Relaxed);
}
}
const WIDTH: u32 = 640;
const HEIGHT: u32 = 480;
@@ -58,42 +76,107 @@ impl<'a> ApplicationHandler for App<'a> {
) {
// Draw the current frame
match event {
WindowEvent::KeyboardInput { event, .. } => match event.state {
winit::event::ElementState::Pressed => {
if let Some(val) = event.physical_key.to_scancode() {
cpu::SHARED
.keyboard
.store(val + 8, std::sync::atomic::Ordering::Relaxed);
WindowEvent::KeyboardInput { event, .. } => {
let enabled = (&SHARED.external_enabled_interupts).load(Relaxed)
& Into::<u32>::into(MMIOInterupt::Keyboard)
!= 0;
if enabled {
wait_int();
}
match event.state {
winit::event::ElementState::Pressed => {
if let Some(val) = event.physical_key.to_scancode() {
cpu::SHARED
.keyboard
.store(val + 8, std::sync::atomic::Ordering::Relaxed);
}
}
winit::event::ElementState::Released => SHARED
.keyboard
.store(0, std::sync::atomic::Ordering::Relaxed),
}
if enabled {
(&SHARED.external_interupts).store(MMIOInterupt::Keyboard.into(), Release);
atomic_wait::wake_all(&SHARED.external_interupts);
}
}
WindowEvent::CursorMoved { position, .. } => {
let enabled = (&SHARED.external_enabled_interupts).load(Relaxed)
& Into::<u32>::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);
}
}
winit::event::ElementState::Released => SHARED
.keyboard
.store(0, std::sync::atomic::Ordering::Relaxed),
},
WindowEvent::CursorMoved {
device_id,
position,
} => {}
WindowEvent::MouseWheel {
device_id,
delta,
phase,
} => {}
WindowEvent::MouseInput {
device_id,
state,
button,
} => {}
WindowEvent::ScaleFactorChanged {
scale_factor,
inner_size_writer,
} => {}
if enabled {
(&SHARED.external_interupts).store(MMIOInterupt::MouseMove.into(), Release);
println!("wake mouse move");
atomic_wait::wake_all(&SHARED.external_interupts);
}
}
// WindowEvent::MouseWheel {
// delta,
// ..
// } => {}
WindowEvent::MouseInput { state, button, .. } => {
let enabled = (&SHARED.external_enabled_interupts).load(Relaxed)
& Into::<u32>::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);
println!("wake mouse click");
atomic_wait::wake_all(&SHARED.external_interupts);
}
}
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::<u32>::into(MMIOInterupt::VSync)
!= 0;
if enabled {
(&SHARED.external_interupts).store(MMIOInterupt::VSync.into(), Relaxed);
atomic_wait::wake_all(&SHARED.external_interupts);
wait_int();
}
let pix = self.pixels.as_mut().unwrap();
let screen = pix.frame_mut();
for (addr, ubgr) in { cpu::SHARED.screen_buf.iter().enumerate() } {
for (addr, ubgr) in cpu::SHARED.screen_buf.iter().enumerate() {
let ubgr = ubgr.load(std::sync::atomic::Ordering::Relaxed);
let rgba = [ubgr as u8, (ubgr >> 8) as u8, (ubgr >> 16) as u8, 0xff];
for i in 0..4 {