diff --git a/Cargo.lock b/Cargo.lock index 02064b5..7b71a93 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -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" diff --git a/asm/src/main.rs b/asm/src/main.rs index 8845b0e..3847f25 100644 --- a/asm/src/main.rs +++ b/asm/src/main.rs @@ -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)) diff --git a/perf.data b/perf.data new file mode 100644 index 0000000..5259178 Binary files /dev/null and b/perf.data differ diff --git a/simu/Cargo.toml b/simu/Cargo.toml index 56a1815..f69878c 100644 --- a/simu/Cargo.toml +++ b/simu/Cargo.toml @@ -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 = [] diff --git a/simu/src/cpu.rs b/simu/src/cpu.rs index 5fe9c63..7e21d37 100644 --- a/simu/src/cpu.rs +++ b/simu/src/cpu.rs @@ -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 for Cond { fn from(value: u8) -> Self { match value { @@ -50,13 +82,28 @@ impl From for Cond { 0b1101 => Cond::Ifuge, 0b1110 => Cond::Ifugt, 0b1111 => Cond::Ifule, - _ => { - unreachable!() - } + _ => iot(), } } } +impl Into for MMIOInterupt { + fn into(self) -> u32 { + unsafe { transmute(self) } + } +} +impl Into for InteruptKind { + fn into(self) -> u32 { + unsafe { transmute(self) } + } +} + +impl From 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 for Instruction { - fn from(value: u32) -> Self { - match value >> 30 { +impl TryFrom for Instruction { + #[inline(always)] + fn try_from(value: u32) -> Result { + Ok(match value >> 30 { 0b00 => { let t = value & (1 << 29); // 3rd bit set let value = value - t; @@ -144,25 +196,49 @@ impl From 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 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(&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::::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::::into(kind) as usize + } } diff --git a/simu/src/main.rs b/simu/src/main.rs index 2efa722..c59cba8 100644 --- a/simu/src/main.rs +++ b/simu/src/main.rs @@ -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::::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::::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::::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::::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 {