summaryrefslogtreecommitdiff
path: root/website-emu/src/lib.rs
diff options
context:
space:
mode:
authorNathan Lee <me@nwlee.tech>2026-09-25 22:39:01 -0500
committerNathan Lee <me@nwlee.tech>2026-09-25 22:39:01 -0500
commit6858c9e7c97f772eb357e3eee0c1fcfb8adcb699 (patch)
tree691b3591fa0fc817b88f3a587fa0d613f9d475c1 /website-emu/src/lib.rs
scaffold emulator from wrv32em
Diffstat (limited to 'website-emu/src/lib.rs')
-rw-r--r--website-emu/src/lib.rs259
1 files changed, 259 insertions, 0 deletions
diff --git a/website-emu/src/lib.rs b/website-emu/src/lib.rs
new file mode 100644
index 0000000..7c1a285
--- /dev/null
+++ b/website-emu/src/lib.rs
@@ -0,0 +1,259 @@
+mod core;
+mod mem;
+mod state;
+
+use wasm_bindgen::prelude::wasm_bindgen;
+use crate::core::*;
+use crate::mem::*;
+use crate::state::CpuState;
+
+const REGISTER_NAME_WIDTH: u32 = 0b11111;
+
+const DRAM_LOAD: u32 = 0x20000000;
+const OS_LOAD: u32 = 0x80000000;
+const INITRD_LOAD: u32 = 0;
+
+#[wasm_bindgen]
+pub fn boot(image: &[u8], initrd: &[u8], memory: &[u8]) {
+ let mut cpu_memory = CpuMemory {
+ devices: Vec::new(),
+ };
+
+ let memory_len = memory.len();
+ let os_size = image.len();
+ let initrd_size = initrd.len();
+
+ let ram_device = LogicalMemory {
+ base: DRAM_LOAD,
+ size: memory_len as u32,
+ dram: memory.to_vec().into_boxed_slice(),
+ };
+
+ let os_image = LogicalMemory {
+ base: OS_LOAD,
+ size: os_size as u32,
+ dram: image.to_vec().into_boxed_slice(),
+ };
+
+ let initrd_image = LogicalMemory {
+ base: INITRD_LOAD,
+ size: initrd_size as u32,
+ dram: initrd.to_vec().into_boxed_slice(),
+ };
+
+ cpu_memory.register_device(
+ DRAM_LOAD..DRAM_LOAD + memory_len as u32,
+ Box::new(ram_device)
+ );
+
+ let mut state = CpuState::new(cpu_memory);
+
+ state.pc = 0x80000000;
+ state.registers[10] = 0;
+ state.registers[2] = DRAM_LOAD + memory_len as u32;
+
+ loop {
+ {
+ handle_instruction(&mut state);
+ state.registers[0] = 0;
+ }
+ }
+}
+
+fn handle_instruction(state: &mut CpuState) {
+ let pc = state.pc;
+ let word = state.memory.read_instruction(pc).expect(format!("pc {:x} does not map to valid memory", pc).as_str());
+ let opcode = word & 0x7f;
+ match opcode {
+ 0b0110011 => {
+ let f7 = word >> 25;
+ let f3 = (word >> 12) & 0b111;
+
+ let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize;
+ let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize;
+ let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize;
+
+ match (f7 << 3) | f3 {
+ 0 => add(state, rd, rs1, rs2),
+ 256 => sub(state, rd, rs1, rs2),
+ 4 => xor(state, rd, rs1, rs2),
+ 6 => or(state, rd, rs1, rs2),
+ 7 => and(state, rd, rs1, rs2),
+ 1 => sll(state, rd, rs1, rs2),
+ 5 => srl(state, rd, rs1, rs2),
+ 261 => sra(state, rd, rs1, rs2),
+ 2 => slt(state, rd, rs1, rs2),
+ 3 => sltu(state, rd, rs1, rs2),
+ 8 => mul(state, rd, rs1, rs2),
+ 9 => mulh(state, rd, rs1, rs2),
+ 10 => mulhsu(state, rd, rs1, rs2),
+ 11 => mulhu(state, rd, rs1, rs2),
+ 12 => div(state, rd, rs1, rs2),
+ 13 => divu(state, rd, rs1, rs2),
+ 14 => rem(state, rd, rs1, rs2),
+ 15 => remu(state, rd, rs1, rs2),
+ _ => panic!("Illegal instruction: {:x}", word),
+ }
+ }
+ 0b0010011 => {
+ let imm = ((word as i32) >> 20) as u32;
+ let f3 = (word >> 12) & 0b111;
+
+ let rs1 = ((word >> 15) & 0b11111) as usize;
+ let rd = ((word >> 7) & 0b11111) as usize;
+
+ match f3 {
+ 0 => addi(state, rd, rs1, imm),
+ 4 => xori(state, rd, rs1, imm),
+ 6 => ori(state, rd, rs1, imm),
+ 7 => andi(state, rd, rs1, imm),
+ 1 => {
+ if (imm >> 5) != 0 {
+ panic!("Illegal instruction: {:x}", word);
+ }
+ slli(state, rd, rs1, imm);
+ }
+ 5 => {
+ if (imm >> 5) != 0 {
+ panic!("Illegal instruction: {:x}", word);
+ }
+ if (imm >> 5) & 0b1111111 == 0 {
+ srli(state, rd, rs1, imm);
+ } else {
+ srai(state, rd, rs1, imm);
+ }
+ }
+ 2 => slti(state, rd, rs1, imm),
+ 3 => sltiu(state, rd, rs1, imm),
+ _ => panic!("Illegal instruction: {:x}", word),
+ }
+ }
+ 0b0000011 => {
+ let imm = ((word as i32) >> 20) as u32;
+ let f3 = (word >> 12) & 0b111;
+
+ let rs1 = ((word >> 15) & 0b11111) as usize;
+ let rd = ((word >> 7) & 0b11111) as usize;
+
+ match f3 {
+ 0 => lb(state, rd, rs1, imm),
+ 1 => lh(state, rd, rs1, imm),
+ 2 => lw(state, rd, rs1, imm),
+ 4 => lbu(state, rd, rs1, imm),
+ 5 => lhu(state, rd, rs1, imm),
+ _ => panic!("Illegal instruction: {:x}", word),
+ }
+ }
+ 0b0100011 => {
+ let imm_low = (word >> 7) & 0b11111;
+ let imm_high = (word >> 25) & 0b1111111;
+ let f3 = (word >> 12) & 0b111;
+ let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize;
+ let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize;
+
+ let imm = ((((imm_high << 5) | imm_low) as i32) << 20 >> 20) as u32;
+
+ match f3 {
+ 0 => sb(state, rs1, rs2, imm),
+ 1 => sh(state, rs1, rs2, imm),
+ 2 => sw(state, rs1, rs2, imm),
+ _ => panic!("Illegal instruction: {:x}", word),
+ };
+ }
+ 0b1100011 => {
+ let f3 = (word >> 12) & 0b111;
+ let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize;
+ let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize;
+
+ let imm = (((word >> 31) & 0x1) << 12)
+ | (((word >> 7) & 0x1) << 11)
+ | (((word >> 25) & 0x3f) << 5)
+ | (((word >> 8) & 0xf) << 1);
+
+ let imm = ((imm as i32) << 19 >> 19) as u32;
+
+ match f3 {
+ 0 => beq(state, rs1, rs2, imm),
+ 1 => bne(state, rs1, rs2, imm),
+ 4 => blt(state, rs1, rs2, imm),
+ 5 => bge(state, rs1, rs2, imm),
+ 6 => bltu(state, rs1, rs2, imm),
+ 7 => bgeu(state, rs1, rs2, imm),
+ _ => panic!("Illegal instruction: {:x}", word),
+ }
+ return;
+ }
+ 0b1101111 => {
+ let rd = (word >> 7) & REGISTER_NAME_WIDTH;
+ let imm = (((word >> 31) & 0x1) << 20)
+ | (((word >> 12) & 0xFF) << 12)
+ | (((word >> 20) & 0x1) << 11)
+ | (((word >> 21) & 0x3FF) << 1);
+ let imm = ((imm as i32) << 11 >> 11) as u32;
+ jal(state, rd as usize, imm);
+ return;
+ }
+ 0b1100111 => {
+ let rs1 = (word >> 15) & REGISTER_NAME_WIDTH;
+ let rd = (word >> 7) & REGISTER_NAME_WIDTH;
+ let imm = word >> 20;
+
+ let sentinel = (word >> 12) & 0b111;
+ if sentinel != 0 {
+ panic!("Illegal instruction: {:x}", word);
+ }
+
+ jalr(state, rd as usize, rs1 as usize, imm);
+ return;
+ }
+ 0b0110111 => {
+ let imm = word >> 12;
+ let rd = ((word >> 7) & 0b11111) as usize;
+ lui(state, rd, imm);
+ }
+ 0b0010111 => {
+ let imm = word >> 12;
+ let rd = (word >> 7) & REGISTER_NAME_WIDTH;
+ auipc(state, rd as usize, imm);
+ }
+ 0b1110011 => {
+ let imm = word >> 20;
+ let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize;
+ let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize;
+ let f3 = (word >> 12) & 0b111;
+
+ match f3 {
+ 0 => {
+ match imm {
+ 0 => trap_to_kernel(state), // ecall
+ 258 => return_to_userspace(state), // sret
+ 261 => {
+ println!("wfi");
+ }
+ _ => panic!("Illegal instruction: {:x}", word),
+ }
+ } // ecall
+ 1 => csrrw(state, rd, rs1, imm as usize),
+ 2 => csrrs(state, rd, rs1, imm as usize),
+ 3 => csrrc(state, rd, rs1, imm as usize),
+ 5 => csrrwi(state, rd, rs1 as u32, imm as usize),
+ 6 => csrrsi(state, rd, rs1 as u32, imm as usize),
+ 7 => csrrci(state, rd, rs1 as u32, imm as usize),
+
+ _ => panic!("Illegal instruction: {:x}", word),
+ }
+ } // ecall (ebreak not implemented)
+ _ => panic!("Illegal instruction or todo: {:032b}", word),
+ }
+
+ state.pc += 4;
+}
+
+fn trap_to_kernel(state: &mut CpuState) {
+ state.csr[0x341 /* mepc */] = state.pc;
+ state.pc = state.csr[0x305 /* mtvec */];
+}
+
+fn return_to_userspace(state: &mut CpuState) {
+ state.pc = state.csr[0x341 /* mepc */] + 4;
+}