Add GUI module using egui and update project structure

Introduces a new `gui` module with initial implementation and tests using egui, updates dependencies in Cargo.toml and Cargo.lock to include egui and related crates, and ui is renamed from ui to gui. Updates .gitignore to exclude oboromi.log. Refactors main.rs and lib.rs to integrate the new GUI, and removes the obsolete ui module.
This commit is contained in:
Nikilite
2025-07-14 19:17:57 +02:00
parent 5c891afc37
commit 4fd6dfe537
14 changed files with 4077 additions and 183 deletions

1
.gitignore vendored
View File

@@ -1 +1,2 @@
/target
/oboromi.log

3794
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,6 +9,14 @@ repository = "https://github.com/0xNikilite/oboromi"
[dependencies]
bitflags = "2.9"
log = "0.4"
fern = { version = "0.6", features = ["colored"] }
parking_lot = "0.12"
crossbeam-channel = "0.5"
egui = { version = "0.32", optional = true }
eframe = { version = "0.32", optional = true }
[features]
trace = []
trace = []
default = ["gui"]
gui = ["dep:egui", "dep:eframe"]

View File

@@ -3,14 +3,31 @@ use bitflags::bitflags;
bitflags! {
/// Processor State Flags: Negative, Zero, Carry, Overflow
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Flags: u32 {
const NEGATIVE = 1 << 31; // N flag: result is negative
const ZERO = 1 << 30; // Z flag: result is zero
const CARRY = 1 << 29; // C flag: carry/borrow
const OVERFLOW = 1 << 28; // V flag: signed overflow
// ARMv8.3-A Pointer Authentication
const PAC_FAIL = 1 << 27; // Pointer authentication failed
}
}
/// ARM Processor State
#[derive(Debug, Clone)]
pub struct ProcessorState {
pub el: u8, // Exception Level (0-3)
pub spsel: bool, // Stack Pointer Selection
pub nzcv: Flags, // Condition flags
pub daif: u8, // Debug, SError, IRQ, FIQ masks
pub pan: bool, // Privileged Access Never
pub uao: bool, // User Access Override
pub dit: bool, // Data Independent Timing
pub tco: bool, // Tag Check Override
pub pstate: u32, // Complete PSTATE
}
/// ARM64 registers X0X30, SP, PC and processor flags.
pub struct Registers {
pub x: [u64; 31],
@@ -263,9 +280,9 @@ impl CPU {
if (opcode & LS_MASK) == LDR || (opcode & LS_MASK) == STR {
let rt = (opcode & 0x1F) as usize;
let rn = ((opcode >> 5) & 0x1F) as usize;
let imm = ((opcode & 0x3FFC00) >> 10) as usize * 8;
let imm = ((opcode & 0x3FFC00) >> 10) as usize; // Immediate is in units of 8 bytes
let base= self.regs.x.get(rn).copied().unwrap_or(0) as usize;
let addr= base.wrapping_add(imm);
let addr= base.wrapping_add(imm * 8); // Scale by 8
if (opcode & LS_MASK) == LDR {
let v = self.memory.read_u64(addr);
if rt < 31 {
@@ -305,4 +322,4 @@ impl Registers {
self.flags.set(Flags::NEGATIVE, (res >> 63) != 0);
self.flags.set(Flags::ZERO, res == 0);
}
}
}

33
src/gui/gui.rs Normal file
View File

@@ -0,0 +1,33 @@
use eframe::egui::{ScrollArea, CentralPanel};
use crate::gui::run_tests;
/// GUI with a button to run tests and display results
pub struct GUI {
pub logs: Vec<String>,
}
impl Default for GUI {
fn default() -> Self {
Self { logs: vec![] }
}
}
impl eframe::App for GUI {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
CentralPanel::default().show(ctx, |ui| {
ui.heading("oboromi");
if ui.button("Run Tests").clicked() {
self.logs = run_tests();
}
ui.separator();
ui.label("Test Results:");
ScrollArea::vertical().show(ui, |ui| {
for line in &self.logs {
ui.label(line);
}
});
});
}
}

5
src/gui/mod.rs Normal file
View File

@@ -0,0 +1,5 @@
mod gui;
mod tests;
pub use gui::GUI;
pub use tests::run_tests;

153
src/gui/tests.rs Normal file
View File

@@ -0,0 +1,153 @@
use crate::cpu::{CPU, Flags};
use crate::memory::Memory;
pub fn run_tests() -> Vec<String> {
let mut results = Vec::new();
// 1) Memory subsystem test
let mut mem = Memory::new(64 * 1024 * 1024);
mem.write_byte(10, 0xAB);
assert_eq!(mem.read_byte(10), 0xAB);
mem.write_byte(100, 0x11);
mem.write_byte(101, 0x22);
mem.write_byte(102, 0x33);
mem.write_byte(103, 0x44);
assert_eq!(mem.read_u32(100), 0x4433_2211);
println!("✅ Memory OK");
// 2) NOP instruction test
let mut cpu = CPU::new(1024);
cpu.regs.pc = 0;
let nop = 0xD503201F_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i, nop[i]); }
cpu.step();
assert_eq!(cpu.regs.pc, 4);
println!("✅ NOP OK");
// 3) ADD immediate test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[1] = 5;
let addi = 0x9100_0821_u32.to_le_bytes(); // ADD X1, X1, #0x2
for i in 0..4 { cpu.memory.write_byte(i, addi[i]); }
cpu.step();
assert_eq!(cpu.regs.x[1], 7);
println!("✅ ADDI OK");
// 4) SUB immediate test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[2] = 10;
let subi = 0xD100_0442_u32.to_le_bytes(); // SUB X2, X2, #0x1
for i in 0..4 { cpu.memory.write_byte(i, subi[i]); }
cpu.step();
assert_eq!(cpu.regs.x[2], 9);
println!("✅ SUBI OK");
// 5) ADD register test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[0] = 7;
cpu.regs.x[1] = 3;
let addr = 0x8B01_0000_u32.to_le_bytes(); // ADD X0, X0, X1
for i in 0..4 { cpu.memory.write_byte(i, addr[i]); }
cpu.step();
assert_eq!(cpu.regs.x[0], 10);
println!("✅ ADDR OK");
// 6) SUB register test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[3] = 8;
cpu.regs.x[4] = 2;
let subr = 0xCB04_0063_u32.to_le_bytes(); // SUB X3, X3, X4
for i in 0..4 { cpu.memory.write_byte(i, subr[i]); }
cpu.step();
assert_eq!(cpu.regs.x[3], 6);
println!("✅ SUBR OK");
// 7) AND register test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[5] = 0b1010;
cpu.regs.x[6] = 0b1100;
let andr = 0x8A06_00A5_u32.to_le_bytes(); // AND X5, X5, X6
for i in 0..4 { cpu.memory.write_byte(i, andr[i]); }
cpu.step();
assert_eq!(cpu.regs.x[5], 0b1000);
println!("✅ AND OK");
// 8) CMP (SUBS XZR, X7, X8) test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[7] = 3;
cpu.regs.x[8] = 3;
let cmp = 0xEB08_00FF_u32.to_le_bytes(); // CMP X7, X8
for i in 0..4 { cpu.memory.write_byte(i, cmp[i]); }
cpu.step();
assert!(cpu.regs.flags.contains(Flags::ZERO));
println!("✅ CMP OK");
// 9) LDR/STR immediate (64-bit) test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[9] = 100;
cpu.regs.x[10] = 0x1234_5678;
// STR X10, [X9, #16]
let stri = 0xF900092A_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i, stri[i]); }
cpu.step();
// LDR X11, [X9, #16]
let ldri = 0xF940092B_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i + 4, ldri[i]); }
cpu.step();
assert_eq!(cpu.regs.x[11], 0x1234_5678);
println!("✅ LDR/STR OK");
// 10) Branch test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
let b = 0x1400_0002_u32.to_le_bytes(); // B +2
for i in 0..4 { cpu.memory.write_byte(i, b[i]); }
cpu.step();
assert_eq!(cpu.regs.pc, 8);
println!("✅ B OK");
// 11) RET test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[30] = 16;
let ret = 0xD65F_03C0_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i, ret[i]); }
cpu.step();
assert_eq!(cpu.regs.pc, 16);
println!("✅ RET OK");
// 12) MMU Basic Functionality Test
println!("\nTesting MMU Basic Functionality...");
let mut cpu = CPU::new(4096); // 4KB memory for MMU test
let vaddr = 0x10;
let paddr = cpu.memory.mmu.translate(vaddr).expect("Translation failed");
println!("MMU: 0x{:x} → 0x{:x}", vaddr, paddr);
assert_eq!(vaddr, paddr);
// Test: write through MMU
cpu.memory.write_byte(vaddr as usize, 0xAB);
let value = cpu.memory.ram[paddr as usize];
println!("Wrote 0xAB to vaddr 0x{:x} (paddr 0x{:x}), read back: 0x{:x}",
vaddr, paddr, value);
assert_eq!(value, 0xAB);
// Test: read through MMU
let read_value = cpu.memory.read_byte(vaddr as usize);
println!("Read from vaddr 0x{:x}: 0x{:x}", vaddr, read_value);
assert_eq!(read_value, 0xAB);
println!("✅ MMU Basic Functionality OK");
results.push("All tests passed".to_string());
results
}

View File

@@ -1,3 +1,4 @@
pub mod cpu;
pub mod memory;
pub mod mmu;
pub mod gui;

View File

@@ -1,5 +1,5 @@
use oboromi::cpu::{CPU, Flags};
use oboromi::memory::Memory;
#[cfg(feature = "gui")]
use oboromi::gui::GUI;
#[allow(dead_code)]
fn decode_arm64_fields(opcode: u32) -> (u8, u8, u8, u8) {
@@ -10,152 +10,47 @@ fn decode_arm64_fields(opcode: u32) -> (u8, u8, u8, u8) {
(sf, opc, rn, rd)
}
fn setup_logger() -> Result<(), fern::InitError> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"[{}][{}] {}",
record.level(),
record.target(),
message
))
})
.level(log::LevelFilter::Debug)
.chain(std::io::stdout())
.chain(fern::log_file("oboromi.log")?)
.apply()?;
Ok(())
}
#[cfg(feature = "gui")]
fn run_gui() {
let options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default()
.with_inner_size([1200.0, 800.0])
.with_title("oboromi"),
..Default::default()
};
eframe::run_native(
"oboromi",
options,
Box::new(|_cc| Ok(Box::new(GUI::default()))),
).expect("Failed to run GUI");
}
fn main() {
// Initialize logging
setup_logger().expect("Failed to initialize logger");
#[cfg(feature = "trace")]
println!("-- TRACING ENABLED --");
// 1) Memory subsystem test
let mut mem = Memory::new(64 * 1024 * 1024);
mem.write_byte(10, 0xAB);
assert_eq!(mem.read_byte(10), 0xAB);
mem.write_byte(100, 0x11);
mem.write_byte(101, 0x22);
mem.write_byte(102, 0x33);
mem.write_byte(103, 0x44);
assert_eq!(mem.read_u32(100), 0x4433_2211);
println!("✅ Memory OK");
log::info!("-- TRACING ENABLED --");
// 2) NOP instruction test
let mut cpu = CPU::new(1024);
cpu.regs.pc = 0;
let nop = 0xD503201F_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i, nop[i]); }
cpu.step();
assert_eq!(cpu.regs.pc, 4);
println!("✅ NOP OK");
// 3) ADD immediate test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[1] = 5;
let addi = 0x9100_0821_u32.to_le_bytes(); // ADD X1, X1, #0x2
for i in 0..4 { cpu.memory.write_byte(i, addi[i]); }
cpu.step();
assert_eq!(cpu.regs.x[1], 7);
println!("✅ ADDI OK");
// 4) SUB immediate test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[2] = 10;
let subi = 0xD100_0442_u32.to_le_bytes(); // SUB X2, X2, #0x1
for i in 0..4 { cpu.memory.write_byte(i, subi[i]); }
cpu.step();
assert_eq!(cpu.regs.x[2], 9);
println!("✅ SUBI OK");
// 5) ADD register test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[0] = 7;
cpu.regs.x[1] = 3;
let addr = 0x8B01_0000_u32.to_le_bytes(); // ADD X0, X0, X1
for i in 0..4 { cpu.memory.write_byte(i, addr[i]); }
cpu.step();
assert_eq!(cpu.regs.x[0], 10);
println!("✅ ADDR OK");
// 6) SUB register test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[3] = 8;
cpu.regs.x[4] = 2;
let subr = 0xCB04_0063_u32.to_le_bytes(); // SUB X3, X3, X4
for i in 0..4 { cpu.memory.write_byte(i, subr[i]); }
cpu.step();
assert_eq!(cpu.regs.x[3], 6);
println!("✅ SUBR OK");
// 7) AND register test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[5] = 0b1010;
cpu.regs.x[6] = 0b1100;
let andr = 0x8A06_00A5_u32.to_le_bytes(); // AND X5, X5, X6
for i in 0..4 { cpu.memory.write_byte(i, andr[i]); }
cpu.step();
assert_eq!(cpu.regs.x[5], 0b1000);
println!("✅ AND OK");
// 8) CMP (SUBS XZR, X7, X8) test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[7] = 3;
cpu.regs.x[8] = 3;
let cmp = 0xEB08_00FF_u32.to_le_bytes(); // CMP X7, X8
for i in 0..4 { cpu.memory.write_byte(i, cmp[i]); }
cpu.step();
assert!(cpu.regs.flags.contains(Flags::ZERO));
println!("✅ CMP OK");
// 9) LDR/STR immediate (64-bit) test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[9] = 100;
cpu.regs.x[10] = 0x1234_5678;
// STR X10, [X9, #16]
let stri = 0xF900092A_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i, stri[i]); }
cpu.step();
// LDR X11, [X9, #16]
let ldri = 0xF940092B_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i + 4, ldri[i]); }
cpu.step();
assert_eq!(cpu.regs.x[11], 0x1234_5678);
println!("✅ LDR/STR OK");
// 10) Branch test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
let b = 0x1400_0002_u32.to_le_bytes(); // B +2
for i in 0..4 { cpu.memory.write_byte(i, b[i]); }
cpu.step();
assert_eq!(cpu.regs.pc, 8);
println!("✅ B OK");
// 11) RET test
cpu = CPU::new(1024);
cpu.regs.pc = 0;
cpu.regs.x[30] = 16;
let ret = 0xD65F_03C0_u32.to_le_bytes();
for i in 0..4 { cpu.memory.write_byte(i, ret[i]); }
cpu.step();
assert_eq!(cpu.regs.pc, 16);
println!("✅ RET OK");
// 12) MMU Basic Functionality Test
println!("\nTesting MMU Basic Functionality...");
let mut cpu = CPU::new(4096); // 4KB memory for MMU test
let vaddr = 0x10;
let paddr = cpu.memory.mmu.translate(vaddr).expect("Translation failed");
println!("MMU: 0x{:x} → 0x{:x}", vaddr, paddr);
assert_eq!(vaddr, paddr);
// Test: write through MMU
cpu.memory.write_byte(vaddr as usize, 0xAB);
let value = cpu.memory.ram[paddr as usize];
println!("Wrote 0xAB to vaddr 0x{:x} (paddr 0x{:x}), read back: 0x{:x}",
vaddr, paddr, value);
assert_eq!(value, 0xAB);
// Test: read through MMU
let read_value = cpu.memory.read_byte(vaddr as usize);
println!("Read from vaddr 0x{:x}: 0x{:x}", vaddr, read_value);
assert_eq!(read_value, 0xAB);
println!("✅ MMU Basic Functionality OK");
}
#[cfg(feature = "gui")] {
run_gui();
}
}

View File

@@ -28,9 +28,9 @@ impl Memory {
pub fn read_byte(&mut self, vaddr: usize) -> u8 {
match self.mmu.translate(vaddr as u64) {
Some(paddr) => {
let paddr_usize = paddr as usize;
if paddr_usize < self.ram.len() {
self.ram[paddr_usize]
let paddr = paddr as usize;
if paddr < self.ram.len() {
self.ram[paddr]
} else {
0
}
@@ -46,9 +46,9 @@ impl Memory {
pub fn write_byte(&mut self, vaddr: usize, val: u8) {
match self.mmu.translate(vaddr as u64) {
Some(paddr) => {
let paddr_usize = paddr as usize;
if paddr_usize < self.ram.len() {
self.ram[paddr_usize] = val;
let paddr = paddr as usize;
if paddr < self.ram.len() {
self.ram[paddr] = val;
}
}
None => println!("⚠️ Page fault writing at {:#x}", vaddr),
@@ -84,4 +84,4 @@ impl Memory {
self.write_u32(addr, value as u32);
self.write_u32(addr + 4, (value >> 32) as u32);
}
}
}

View File

@@ -35,10 +35,7 @@ impl MMU {
/// Set identity mapping for a memory range
pub fn set_identity_mapping(&mut self, start_page: usize, end_page: usize) {
for i in start_page..end_page {
if i < self.page_table.len() {
// Mappa la pagina virtuale i alla stessa pagina fisica
self.page_table.set_entry(i, (i * 4096) as u64);
}
self.page_table.set_entry(i, (i * 4096) as u64);
}
}
}
}

View File

@@ -1,24 +1,23 @@
use std::collections::HashMap;
#[derive(Debug)]
/// Page Table implementation
/// Page Table implementation with sparse entries
pub struct PageTable {
entries: Vec<Option<u64>>,
entries: HashMap<usize, u64>,
}
impl PageTable {
/// Create new page table with identity mapping disabled
/// Create new empty page table
pub fn new() -> Self {
PageTable {
entries: vec![None; 1 << 10],
entries: HashMap::new(),
}
}
/// Perform page table walk
pub fn walk(&self, vaddr: u64) -> Option<u64> {
let vpn = (vaddr >> 12) & 0x3FF; // Virtual Page Number (4KB pages)
let base = match self.entries.get(vpn as usize)? {
Some(addr) => *addr,
None => return None, // Page not mapped
};
let vpn = (vaddr >> 12) as usize; // Virtual Page Number (4KB pages)
let base = self.entries.get(&vpn)?;
// Physical address = page base + offset
Some(base | (vaddr & 0xFFF))
@@ -26,13 +25,6 @@ impl PageTable {
/// Set a page table entry at index
pub fn set_entry(&mut self, index: usize, value: u64) {
if index < self.entries.len() {
self.entries[index] = Some(value & !0xFFF);
}
self.entries.insert(index, value & !0xFFF);
}
/// Get the number of entries in the page table
pub fn len(&self) -> usize {
self.entries.len()
}
}
}

View File