Interuptions
This commit is contained in:
26
Cargo.lock
generated
26
Cargo.lock
generated
@@ -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"
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
560
simu/src/cpu.rs
560
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<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
|
||||
}
|
||||
}
|
||||
|
||||
149
simu/src/main.rs
149
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::<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 {
|
||||
|
||||
Reference in New Issue
Block a user