From 6858c9e7c97f772eb357e3eee0c1fcfb8adcb699 Mon Sep 17 00:00:00 2001 From: Nathan Lee Date: Fri, 25 Sep 2026 22:39:01 -0500 Subject: scaffold emulator from wrv32em --- website-emu/Cargo.toml | 7 ++ website-emu/src/core.rs | 291 +++++++++++++++++++++++++++++++++++++++++++++++ website-emu/src/lib.rs | 259 +++++++++++++++++++++++++++++++++++++++++ website-emu/src/mem.rs | 105 +++++++++++++++++ website-emu/src/state.rs | 19 ++++ 5 files changed, 681 insertions(+) create mode 100644 website-emu/Cargo.toml create mode 100644 website-emu/src/core.rs create mode 100644 website-emu/src/lib.rs create mode 100644 website-emu/src/mem.rs create mode 100644 website-emu/src/state.rs (limited to 'website-emu') diff --git a/website-emu/Cargo.toml b/website-emu/Cargo.toml new file mode 100644 index 0000000..575b12b --- /dev/null +++ b/website-emu/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "website-emu" +version = "0.1.0" +edition = "2024" + +[dependencies] +wasm-bindgen = "0.2" diff --git a/website-emu/src/core.rs b/website-emu/src/core.rs new file mode 100644 index 0000000..6817c94 --- /dev/null +++ b/website-emu/src/core.rs @@ -0,0 +1,291 @@ +use crate::state::CpuState; + +pub fn add(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1].wrapping_add(state.registers[rs2]); +} + +pub fn sub(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1].wrapping_sub(state.registers[rs2]); +} + +pub fn xor(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1] ^ state.registers[rs2]; +} + +pub fn or(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1] | state.registers[rs2]; +} + +pub fn and(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1] & state.registers[rs2]; +} + +pub fn sll(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1] << (state.registers[rs2] & 0b11111); +} + +pub fn srl(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1] >> (state.registers[rs2] & 0b11111); +} + +pub fn sra(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = + ((state.registers[rs1] as i32) >> (state.registers[rs2] & 0b11111)) as u32; +} + +pub fn slt(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + let rs1_signed = state.registers[rs1] as i32; + let rs2_signed = state.registers[rs2] as i32; + + state.registers[rd] = if rs1_signed < rs2_signed { 1 } else { 0 }; +} + +pub fn sltu(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = if state.registers[rs1] < state.registers[rs2] { + 1 + } else { + 0 + }; +} + +pub fn mul(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = state.registers[rs1].wrapping_mul(state.registers[rs2]); +} + +pub fn mulh(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = (((state.registers[rs1] as i32 as i64) + * (state.registers[rs2] as i32 as i64)) + >> 32) as u32; +} + +pub fn mulhsu(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + let rs1_signed = (state.registers[rs1] as i32) as i64; + let rs2_unsigned = state.registers[rs2] as i64; + + state.registers[rd] = ((rs1_signed * rs2_unsigned) >> 32) as u32; +} + +pub fn mulhu(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = + ((state.registers[rs1] as u64 * state.registers[rs2] as u64) >> 32) as u32; +} + +pub fn div(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + let a = state.registers[rs1] as i32; + let b = state.registers[rs2] as i32; + + state.registers[rd] = if b == 0 { + u32::MAX + } else if a == i32::MIN && b == -1 { + a as u32 + } else { + (a / b) as u32 + }; +} + +pub fn divu(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = if state.registers[rs2] == 0 { + u32::MAX + } else { + state.registers[rs1] / state.registers[rs2] + }; +} + +pub fn rem(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + let a = state.registers[rs1] as i32; + let b = state.registers[rs2] as i32; + + state.registers[rd] = if b == 0 { + a as u32 + } else if a == i32::MIN && b == -1 { + 0 + } else { + (a % b) as u32 + }; +} + +pub fn remu(state: &mut CpuState, rd: usize, rs1: usize, rs2: usize) { + state.registers[rd] = if state.registers[rs2] == 0 { + state.registers[rs1] + } else { + state.registers[rs1] % state.registers[rs2] + }; +} + +pub fn addi(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.registers[rs1].wrapping_add(imm); +} + +pub fn xori(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.registers[rs1] ^ imm; +} + +pub fn ori(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.registers[rs1] | imm; +} + +pub fn andi(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.registers[rs1] & imm; +} + +pub fn slli(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.registers[rs1] << (imm & 0b11111); +} +pub fn srli(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.registers[rs1] >> (imm & 0b11111); +} +pub fn srai(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = ((state.registers[rs1] as i32) >> (imm & 0b11111)) as u32; +} + +pub fn slti(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = if (state.registers[rs1] as i32) < (imm as i32) { + 1 + } else { + 0 + }; +} + +pub fn sltiu(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = if state.registers[rs1] < imm { 1 } else { 0 }; +} +pub fn lb(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + let byte = state.memory.read(state.registers[rs1] + imm).unwrap(); + state.registers[rd] = (byte as i8) as u32; +} + +pub fn lh(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + let halfword = state.memory.read_halfword(state.registers[rs1] + imm).unwrap(); + state.registers[rd] = (halfword as i16) as u32; +} + +pub fn lw(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + let addr = state.registers[rs1].wrapping_add(imm); + let word = state.memory.read_word(addr).unwrap(); + state.registers[rd] = word; +} + +pub fn lbu(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state + .memory + .read(state.registers[rs1].wrapping_add(imm)) + .unwrap() as u32; +} + +pub fn lhu(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.memory.read_halfword(state.registers[rs1] + imm).unwrap() as u32; +} +pub fn sb(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + let addr = state.registers[rs1] + imm; + state + .memory + .write(addr, state.registers[rs2] as u8) + .unwrap(); +} + +pub fn sh(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + let addr = (state.registers[rs1] + imm) as usize; + + let halfword= (state.registers[rs2] & 0xFFFF) as u16; + + state.memory.write_halfword(addr as u32, halfword).unwrap(); +} + +pub fn sw(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + let addr = (state.registers[rs1] + imm) as usize; + + state.memory.write_word(addr as u32, state.registers[rs2]).unwrap(); +} + +pub fn beq(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + if state.registers[rs1] == state.registers[rs2] { + state.pc = state.pc.wrapping_add(imm); + } else { + state.pc += 4; + } +} + +pub fn bne(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + if state.registers[rs1] != state.registers[rs2] { + state.pc = state.pc.wrapping_add(imm); + } else { + state.pc += 4; + } +} + +pub fn blt(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + if (state.registers[rs1] as i32) < (state.registers[rs2] as i32) { + state.pc = state.pc.wrapping_add(imm); + } else { + state.pc += 4; + } +} + +pub fn bge(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + if (state.registers[rs1] as i32) >= (state.registers[rs2] as i32) { + state.pc = state.pc.wrapping_add(imm); + } else { + state.pc += 4; + } +} + +pub fn bltu(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + if state.registers[rs1] < state.registers[rs2] { + state.pc = state.pc.wrapping_add(imm); + } else { + state.pc += 4; + } +} + +pub fn bgeu(state: &mut CpuState, rs1: usize, rs2: usize, imm: u32) { + if state.registers[rs1] >= state.registers[rs2] { + state.pc = state.pc.wrapping_add(imm); + } else { + state.pc += 4; + } +} +pub fn jal(state: &mut CpuState, rd: usize, imm: u32) { + state.registers[rd] = state.pc + 4; + state.pc = state.pc.wrapping_add(imm); +} + +pub fn jalr(state: &mut CpuState, rd: usize, rs1: usize, imm: u32) { + state.registers[rd] = state.pc + 4; + state.pc = state.registers[rs1] + imm; +} +pub fn lui(state: &mut CpuState, rd: usize, imm: u32) { + state.registers[rd] = imm << 12; +} +pub fn auipc(state: &mut CpuState, rd: usize, imm: u32) { + state.registers[rd] = state.pc + (imm << 12); +} + +pub fn csrrw(state: &mut CpuState, rd: usize, rs1: usize, csr: usize) { + state.registers[rd] = state.csr[csr]; + state.csr[csr] = state.registers[rs1]; +} + +pub fn csrrwi(state: &mut CpuState, rd: usize, imm: u32, csr: usize) { + state.registers[rd] = state.csr[csr]; + state.csr[csr] = imm; +} + +pub fn csrrc(state: &mut CpuState, rd: usize, rs1: usize, csr: usize) { + state.registers[rd] = state.csr[csr]; + state.csr[csr] &= !state.registers[rs1]; +} + +pub fn csrrci(state: &mut CpuState, rd: usize, imm: u32, csr: usize) { + state.registers[rd] = state.csr[csr]; + state.csr[csr] &= !imm; +} + +pub fn csrrs(state: &mut CpuState, rd: usize, rs1: usize, csr: usize) { + state.registers[rd] = state.csr[csr]; + state.csr[csr] |= state.registers[rs1]; +} + +pub fn csrrsi(state: &mut CpuState, rd: usize, imm: u32, csr: usize) { + state.registers[rd] = state.csr[csr]; + state.csr[csr] |= imm; +} \ No newline at end of file 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; +} diff --git a/website-emu/src/mem.rs b/website-emu/src/mem.rs new file mode 100644 index 0000000..c5f1b5c --- /dev/null +++ b/website-emu/src/mem.rs @@ -0,0 +1,105 @@ +use std::ops::Range; + +pub trait MemoryDevice { + fn read(&self, address: u32) -> Result; + fn write(&mut self, address: u32, value: u8) -> Result<(), ()>; +} + +pub struct CpuMemory { + pub devices: Vec<(Range, Box)>, +} + +impl CpuMemory { + fn get_device(&self, address: u32) -> Option<&Box> { + self.devices + .iter() + .find(|(range, _)| range.contains(&address)) + .map(|(_, device)| device) + } + fn get_device_mut(&mut self, address: u32) -> Option<&mut Box> { + self.devices + .iter_mut() + .find(|(range, _)| range.contains(&address)) + .map(|(_, device)| device) + } + + pub fn register_device(&mut self, range: Range, device: Box) { + self.devices.push((range, device)); + } + + pub fn read(&self, address: u32) -> Result { + if let Some(device) = self.get_device(address) { + device.read(address) + } else { + Err(()) + } + } + + pub fn write(&mut self, address: u32, value: u8) -> Result<(), ()> { + if let Some(device) = self.get_device_mut(address) { + device.write(address, value) + } else { + Err(()) + } + } + + pub fn read_halfword(&self, address: u32) -> Result { + let low = self.read(address)?; + let high = self.read(address + 1)?; + Ok((high as u16) << 8 | low as u16) + } + + pub fn write_halfword(&mut self, address: u32, value: u16) -> Result<(), ()> { + self.write(address + 1, (value >> 8) as u8)?; + self.write(address, (value & 0xFF) as u8)?; + Ok(()) + } + + pub fn read_word(&self, address: u32) -> Result { + let low = self.read_halfword(address)?; + let high = self.read_halfword(address + 2)?; + Ok((high as u32) << 16 | low as u32) + } + + pub fn write_word(&mut self, address: u32, value: u32) -> Result<(), ()> { + self.write_halfword(address + 2, (value >> 16) as u16)?; + self.write_halfword(address, (value & 0xFFFF) as u16) + } + + pub fn read_instruction(&self, address: u32) -> Result { + if let Some(device) = self.get_device(address) { + Ok(u32::from_le_bytes([ + device.read(address)?, + device.read(address + 1)?, + device.read(address + 2)?, + device.read(address + 3)?, + ])) + } else { + Err(()) + } + } +} + +pub struct LogicalMemory { + pub base: u32, + pub size: u32, + pub dram: Box<[u8]>, +} + +impl MemoryDevice for LogicalMemory { + fn read(&self, address: u32) -> Result { + if address - self.base > self.size { + Err(()) + } else { + Ok(self.dram[(address - self.base) as usize]) + } + } + fn write(&mut self, address: u32, value: u8) -> Result<(), ()> { + if address - self.base > self.size { + Err(()) + } else { + self.dram[(address - self.base) as usize] = value; + Ok(()) + } + } +} diff --git a/website-emu/src/state.rs b/website-emu/src/state.rs new file mode 100644 index 0000000..f26167f --- /dev/null +++ b/website-emu/src/state.rs @@ -0,0 +1,19 @@ +use crate::mem::CpuMemory; + +pub struct CpuState { + pub memory: CpuMemory, + pub registers: [u32; 32], + pub csr: [u32; 4096], + pub pc: u32, +} + +impl CpuState { + pub fn new(memory: CpuMemory) -> CpuState { + CpuState { + memory, + registers: [0u32; 32], + csr: [0u32; 4096], + pc: 0 + } + } +} -- cgit v1.2.3