pub mod core; use crate::core::*; use elf::ElfBytes; use elf::endian::AnyEndian; use std::collections::HashSet; use std::io; use std::io::Write; use std::process::exit; use std::sync::{LazyLock, Mutex}; const MEMORY_SIZE: usize = 64 * 1024 * 1024; const REGISTER_NAME_WIDTH: u32 = 0b11111; static MEMORY: LazyLock>> = LazyLock::new(|| Mutex::new(vec![0u8; MEMORY_SIZE].into_boxed_slice())); struct CpuState<'a> { memory: &'a mut Box<[u8]>, registers: &'a mut [u32; 32], f_registers: &'a mut [f32; 32], pc: &'a mut u32, reservations: &'a mut HashSet, } fn main() { let path = std::path::PathBuf::from("hi"); let f_dat = std::fs::read(path).expect("Unable to read file"); let elf = ElfBytes::::minimal_parse(f_dat.as_slice()).expect("Unable to parse elf"); let (sh, stab) = elf .section_headers_with_strtab() .expect("Unable to get section headers"); let dup = f_dat.clone(); // write memory { let mut memory = MEMORY.lock().expect("Unable to get memory"); for i in 0..dup.len() { memory[i] = dup[i]; } } let mut registers: [u32; 32] = [0; 32]; let mut f_registers: [f32; 32] = [0f32; 32]; let mut pc: u32 = 0; let mut reservations: HashSet = HashSet::new(); let str_tab = stab.expect("Cannot read strtab"); if let Some(headers) = sh { for header in headers { // Search for .text if let Ok(sh_name) = str_tab.get(header.sh_name as usize) && sh_name == ".text" { pc = header.sh_addr as u32; } } } loop { { let mut memory = MEMORY.lock().expect("Unable to get memory"); handle_instruction(CpuState { memory: &mut *memory, registers: &mut registers, f_registers: &mut f_registers, pc: &mut pc, reservations: &mut reservations, }); } } } fn handle_instruction( CpuState { memory, registers, f_registers, pc, reservations, }: CpuState, ) { let word = u32::from_le_bytes([ memory[*pc as usize], memory[*pc as usize + 1], memory[*pc as usize + 2], memory[*pc as usize + 3], ]); 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(registers, rd, rs1, rs2), 256 => sub(registers, rd, rs1, rs2), 4 => xor(registers, rd, rs1, rs2), 6 => or(registers, rd, rs1, rs2), 7 => and(registers, rd, rs1, rs2), 1 => sll(registers, rd, rs1, rs2), 5 => srl(registers, rd, rs1, rs2), 261 => sra(registers, rd, rs1, rs2), 2 => slt(registers, rd, rs1, rs2), 3 => sltu(registers, rd, rs1, rs2), 8 => mul(registers, rd, rs1, rs2), 9 => mulh(registers, rd, rs1, rs2), 10 => mulhsu(registers, rd, rs1, rs2), 11 => mulhu(registers, rd, rs1, rs2), 12 => div(registers, rd, rs1, rs2), 13 => divu(registers, rd, rs1, rs2), 14 => rem(registers, rd, rs1, rs2), 15 => remu(registers, 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(registers, rd, rs1, imm), 4 => xori(registers, rd, rs1, imm), 6 => ori(registers, rd, rs1, imm), 7 => andi(registers, rd, rs1, imm), 1 => { if (imm >> 5) != 0 { panic!("Illegal instruction: {:x}", word); } slli(registers, rd, rs1, imm); } 5 => { if (imm >> 5) != 0 { panic!("Illegal instruction: {:x}", word); } if (imm >> 5) & 0b1111111 == 0 { srli(registers, rd, rs1, imm); } else { srai(registers, rd, rs1, imm); } } 2 => slti(registers, rd, rs1, imm), 3 => sltiu(registers, 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(registers, memory, rd, rs1, imm), 1 => lh(registers, memory, rd, rs1, imm), 2 => lw(registers, memory, rd, rs1, imm), 4 => lbu(registers, memory, rd, rs1, imm), 5 => lhu(registers, memory, 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; let addr = (registers[rs1] + imm) as usize; match f3 { 0 => { sb(registers, memory, rs1, rs2, imm); release_reservations(reservations, &[addr]); } 1 => { sh(registers, memory, rs1, rs2, imm); release_reservations(reservations, &[addr, addr + 1]); } 2 => { sw(registers, memory, rs1, rs2, imm); release_reservations(reservations, &[addr, addr + 1, addr + 2, addr + 3]); } _ => 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(registers, pc, rs1, rs2, imm), 1 => bne(registers, pc, rs1, rs2, imm), 4 => blt(registers, pc, rs1, rs2, imm), 5 => bge(registers, pc, rs1, rs2, imm), 6 => bltu(registers, pc, rs1, rs2, imm), 7 => bgeu(registers, pc, 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(registers, pc, 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(registers, pc, rd as usize, rs1 as usize, imm); return; } 0b0110111 => { let imm = word >> 12; let rd = ((word >> 7) & 0b11111) as usize; lui(registers, rd, imm); } 0b0010111 => { let imm = word >> 12; let rd = (word >> 7) & REGISTER_NAME_WIDTH; auipc(registers, *pc, rd as usize, imm); } 0b1110011 => { // ecall eprintln!( "TRAP! syscall {} with gp registers {:?} calling instruction {:x}", registers[17], ®isters[10..17], *pc ); if registers[17] == 0 { // sys_write: just a debug let buf = registers[11] as usize; let len = registers[12] as usize; let total_bytes = &memory[buf..buf + len]; io::stdout().write(&total_bytes).unwrap(); } else if registers[17] == 60 { exit(registers[10] as i32); } } // ecall (ebreak not implemented) 0b0101111 => { let f5 = word >> 27; let f3 = (word >> 12) & 0b111; let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; if f3 != 2 { panic!("Illegal instruction: {:x}", word); } match f5 { 2 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = number; acquire_reservations(reservations, &[addr, addr + 1, addr + 2, addr + 3]); } // lr.w 3 => { let addr = registers[rs1] as usize; if (*reservations).contains(&addr) { memory[addr..addr + 4].copy_from_slice(®isters[rs2].to_le_bytes()); registers[rd] = 0; } else { registers[rd] = 1; } } // sc.w 1 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = number; store_word(memory, addr, registers[rs2]); } // amoswap.w 0 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = (number as i32 + registers[rs2] as i32) as u32; store_word(memory, addr, registers[rd]); } // amoadd.w 12 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = number & registers[rs2]; store_word(memory, addr, registers[rd]); } // amoand.w 8 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = number | registers[rs2]; store_word(memory, addr, registers[rd]); } // amoor.w 4 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = number ^ registers[rs2]; store_word(memory, addr, registers[rd]); } // amoxor.w 20 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = std::cmp::max(number as i32, registers[rs2] as i32) as u32; store_word(memory, addr, registers[rd]); } // amomax.w 16 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = std::cmp::min(number as i32, registers[rs2] as i32) as u32; store_word(memory, addr, registers[rd]); } // amomin.w 28 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = std::cmp::max(number, registers[rs2]); store_word(memory, addr, registers[rd]); } // amomaxu.w 24 => { let addr = registers[rs1] as usize; let number: u32 = load_word(&memory, addr); registers[rd] = std::cmp::min(number, registers[rs2]); store_word(memory, addr, registers[rd]); } // amomin.w _ => panic!("Illegal instruction: {:x}", word), } } 0b1000011 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fmadd_s(f_registers, rd, rs1, rs2, rs3); } 0b1000111 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fmsub_s(f_registers, rd, rs1, rs2, rs3); } 0b1001011 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fnmsub_s(f_registers, rd, rs1, rs2, rs3); } 0b1001111 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let rs3 = ((word >> 27) & REGISTER_NAME_WIDTH) as usize; fnmadd_s(f_registers, rd, rs1, rs2, rs3); } 0b1010011 => { // todo: handle rm bit let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; let rs2 = ((word >> 20) & REGISTER_NAME_WIDTH) as usize; let f5 = word >> 27; let rm = (word >> 12) & 0b111; match f5 { 0 => fadd_s(f_registers, rd, rs1, rs2), 1 => fsub_s(f_registers, rd, rs1, rs2), 2 => fmul_s(f_registers, rd, rs1, rs2), 3 => fdiv_s(f_registers, rd, rs1, rs2), 11 => fsqrt_s(f_registers, rd, rs1), 4 => match rm { 0 => fsgnj_s(f_registers, rd, rs1, rs2), 1 => fsgnjn_s(f_registers, rd, rs1, rs2), 2 => fsgnjx_s(f_registers, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", rm), }, 5 => match rm { 0 => fmin_s(f_registers, rd, rs1, rs2), 1 => fmax_s(f_registers, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", rm), }, 24 => { fcvt_wu_s(registers, f_registers, rd, rs1); } 28 => { match rm { 0 => fmv_x_w(registers, f_registers, rd, rs1), 1 => fclass_s(registers, f_registers, rd, rs1), _ => panic!("Illegal instruction: {:x}", rm), }; } 20 => match rm { 0 => feq_s(registers, f_registers, rd, rs1, rs2), 1 => flt_s(registers, f_registers, rd, rs1, rs2), 2 => fle_s(registers, f_registers, rd, rs1, rs2), _ => panic!("Illegal instruction: {:x}", rm), }, _ => panic!("Illegal instruction: {:x}", word), } } 0b1010101 => { let rd = ((word >> 7) & REGISTER_NAME_WIDTH) as usize; let rs1 = ((word >> 15) & REGISTER_NAME_WIDTH) as usize; fcvt_w_s(registers, f_registers, rd, rs1); } _ => panic!("Illegal instruction or todo: {:x}", word), } *pc += 4; } fn release_reservations(reservations: &mut HashSet, indices: &[usize]) { for index in indices { reservations.remove(&index); } } fn acquire_reservations(reservations: &mut HashSet, indices: &[usize]) { for index in indices { reservations.insert(*index); } } fn load_word(memory: &&mut Box<[u8]>, addr: usize) -> u32 { let slice = memory.get(addr..addr + 4).unwrap(); u32::from_le_bytes(slice.try_into().unwrap()) } fn store_word(memory: &mut Box<[u8]>, addr: usize, value: u32) { memory[addr..addr + 4].copy_from_slice(&value.to_le_bytes()); }