PR-09 - VirtualFS
This commit is contained in:
parent
a8119d71f0
commit
5e5b703a56
3
.gitignore
vendored
3
.gitignore
vendored
@ -46,3 +46,6 @@ ehthumbs.db
|
|||||||
.env
|
.env
|
||||||
.env.*
|
.env.*
|
||||||
|
|
||||||
|
mnt
|
||||||
|
mnt/**
|
||||||
|
|
||||||
|
|||||||
112
crates/host-desktop/src/fs_desktop_backend.rs
Normal file
112
crates/host-desktop/src/fs_desktop_backend.rs
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
use std::path::PathBuf;
|
||||||
|
use std::fs;
|
||||||
|
use prometeu_core::fs::{FsBackend, FsEntry, FsError};
|
||||||
|
|
||||||
|
pub struct HostDirBackend {
|
||||||
|
root: PathBuf,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl HostDirBackend {
|
||||||
|
pub fn new(root: impl Into<PathBuf>) -> Self {
|
||||||
|
Self { root: root.into() }
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve(&self, path: &str) -> PathBuf {
|
||||||
|
let path = path.trim_start_matches('/');
|
||||||
|
self.root.join(path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FsBackend for HostDirBackend {
|
||||||
|
fn mount(&mut self) -> Result<(), FsError> {
|
||||||
|
if !self.root.exists() {
|
||||||
|
return Err(FsError::NotFound);
|
||||||
|
}
|
||||||
|
|
||||||
|
for dir in &["system", "apps", "media", "user"] {
|
||||||
|
let path = self.root.join(dir);
|
||||||
|
if !path.exists() {
|
||||||
|
fs::create_dir_all(&path).map_err(|e| FsError::IOError(e.to_string()))?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unmount(&mut self) {}
|
||||||
|
|
||||||
|
fn list_dir(&self, path: &str) -> Result<Vec<FsEntry>, FsError> {
|
||||||
|
let full_path = self.resolve(path);
|
||||||
|
let entries = fs::read_dir(full_path).map_err(|e| FsError::IOError(e.to_string()))?;
|
||||||
|
|
||||||
|
let mut result = Vec::new();
|
||||||
|
for entry in entries {
|
||||||
|
let entry = entry.map_err(|e| FsError::IOError(e.to_string()))?;
|
||||||
|
let metadata = entry.metadata().map_err(|e| FsError::IOError(e.to_string()))?;
|
||||||
|
result.push(FsEntry {
|
||||||
|
name: entry.file_name().to_string_lossy().into_owned(),
|
||||||
|
is_dir: metadata.is_dir(),
|
||||||
|
size: metadata.len(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
Ok(result)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError> {
|
||||||
|
let full_path = self.resolve(path);
|
||||||
|
fs::read(full_path).map_err(|e| match e.kind() {
|
||||||
|
std::io::ErrorKind::NotFound => FsError::NotFound,
|
||||||
|
_ => FsError::IOError(e.to_string()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError> {
|
||||||
|
let full_path = self.resolve(path);
|
||||||
|
if let Some(parent) = full_path.parent() {
|
||||||
|
fs::create_dir_all(parent).map_err(|e| FsError::IOError(e.to_string()))?;
|
||||||
|
}
|
||||||
|
fs::write(full_path, data).map_err(|e| FsError::IOError(e.to_string()))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn delete(&mut self, path: &str) -> Result<(), FsError> {
|
||||||
|
let full_path = self.resolve(path);
|
||||||
|
if full_path.is_dir() {
|
||||||
|
fs::remove_dir_all(full_path).map_err(|e| FsError::IOError(e.to_string()))
|
||||||
|
} else {
|
||||||
|
fs::remove_file(full_path).map_err(|e| FsError::IOError(e.to_string()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exists(&self, path: &str) -> bool {
|
||||||
|
self.resolve(path).exists()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::fs;
|
||||||
|
use std::env;
|
||||||
|
|
||||||
|
fn get_temp_dir(name: &str) -> PathBuf {
|
||||||
|
let mut path = env::temp_dir();
|
||||||
|
path.push(format!("prometeu_host_test_{}_{}", name, std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH).unwrap().as_nanos()));
|
||||||
|
fs::create_dir_all(&path).unwrap();
|
||||||
|
path
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_host_dir_backend_mount_and_dirs() {
|
||||||
|
let root = get_temp_dir("mount");
|
||||||
|
let mut backend = HostDirBackend::new(root.clone());
|
||||||
|
|
||||||
|
backend.mount().unwrap();
|
||||||
|
|
||||||
|
assert!(root.join("system").is_dir());
|
||||||
|
assert!(root.join("apps").is_dir());
|
||||||
|
assert!(root.join("media").is_dir());
|
||||||
|
assert!(root.join("user").is_dir());
|
||||||
|
|
||||||
|
let _ = fs::remove_dir_all(root);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,13 +1,25 @@
|
|||||||
mod audio_mixer;
|
mod audio_mixer;
|
||||||
mod prometeu_runner;
|
mod prometeu_runner;
|
||||||
|
mod fs_desktop_backend;
|
||||||
|
|
||||||
use crate::prometeu_runner::PrometeuRunner;
|
use crate::prometeu_runner::PrometeuRunner;
|
||||||
use winit::event_loop::EventLoop;
|
use winit::event_loop::EventLoop;
|
||||||
|
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
let mut fs_root = None;
|
||||||
|
let mut i = 0;
|
||||||
|
while i < args.len() {
|
||||||
|
if args[i] == "--fs-root" && i + 1 < args.len() {
|
||||||
|
fs_root = Some(args[i + 1].clone());
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
i += 1;
|
||||||
|
}
|
||||||
|
|
||||||
let event_loop = EventLoop::new()?;
|
let event_loop = EventLoop::new()?;
|
||||||
|
|
||||||
let mut runner = PrometeuRunner::new();
|
let mut runner = PrometeuRunner::new(fs_root);
|
||||||
event_loop.run_app(&mut runner)?;
|
event_loop.run_app(&mut runner)?;
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
use crate::audio_mixer::AudioMixer;
|
use crate::audio_mixer::AudioMixer;
|
||||||
|
use crate::fs_desktop_backend::HostDirBackend;
|
||||||
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
use cpal::traits::{DeviceTrait, HostTrait, StreamTrait};
|
||||||
use pixels::{Pixels, SurfaceTexture};
|
use pixels::{Pixels, SurfaceTexture};
|
||||||
use prometeu_core::firmware::Firmware;
|
use prometeu_core::firmware::Firmware;
|
||||||
@ -23,6 +24,7 @@ pub struct PrometeuRunner {
|
|||||||
firmware: Firmware,
|
firmware: Firmware,
|
||||||
|
|
||||||
input_signals: InputSignals,
|
input_signals: InputSignals,
|
||||||
|
fs_root: Option<String>,
|
||||||
|
|
||||||
frame_target_dt: Duration,
|
frame_target_dt: Duration,
|
||||||
last_frame_time: Instant,
|
last_frame_time: Instant,
|
||||||
@ -39,15 +41,22 @@ pub struct PrometeuRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl PrometeuRunner {
|
impl PrometeuRunner {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new(fs_root: Option<String>) -> Self {
|
||||||
let target_fps = 60;
|
let target_fps = 60;
|
||||||
|
|
||||||
|
let mut firmware = Firmware::new();
|
||||||
|
if let Some(root) = &fs_root {
|
||||||
|
let backend = HostDirBackend::new(root);
|
||||||
|
firmware.os.mount_fs(Box::new(backend));
|
||||||
|
}
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
window: None,
|
window: None,
|
||||||
pixels: None,
|
pixels: None,
|
||||||
hardware: Hardware::new(),
|
hardware: Hardware::new(),
|
||||||
firmware: Firmware::new(),
|
firmware,
|
||||||
input_signals: InputSignals::default(),
|
input_signals: InputSignals::default(),
|
||||||
|
fs_root,
|
||||||
frame_target_dt: Duration::from_nanos(1_000_000_000 / target_fps),
|
frame_target_dt: Duration::from_nanos(1_000_000_000 / target_fps),
|
||||||
last_frame_time: Instant::now(),
|
last_frame_time: Instant::now(),
|
||||||
accumulator: Duration::ZERO,
|
accumulator: Duration::ZERO,
|
||||||
@ -243,6 +252,17 @@ impl ApplicationHandler for PrometeuRunner {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
fn about_to_wait(&mut self, _event_loop: &ActiveEventLoop) {
|
||||||
|
// Atualiza estado do Filesystem no OS (específico do host-desktop)
|
||||||
|
if let Some(root) = &self.fs_root {
|
||||||
|
use prometeu_core::fs::FsState;
|
||||||
|
if matches!(self.firmware.os.fs_state, FsState::Unmounted | FsState::Error(_)) {
|
||||||
|
if std::path::Path::new(root).exists() {
|
||||||
|
let backend = HostDirBackend::new(root);
|
||||||
|
self.firmware.os.mount_fs(Box::new(backend));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let now = Instant::now();
|
let now = Instant::now();
|
||||||
let mut frame_delta = now.duration_since(self.last_frame_time);
|
let mut frame_delta = now.duration_since(self.last_frame_time);
|
||||||
|
|
||||||
|
|||||||
12
crates/prometeu-core/src/fs/fs_backend.rs
Normal file
12
crates/prometeu-core/src/fs/fs_backend.rs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
use crate::fs::{FsEntry, FsError};
|
||||||
|
|
||||||
|
pub trait FsBackend: Send + Sync {
|
||||||
|
fn mount(&mut self) -> Result<(), FsError>;
|
||||||
|
fn unmount(&mut self);
|
||||||
|
fn list_dir(&self, path: &str) -> Result<Vec<FsEntry>, FsError>;
|
||||||
|
fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError>;
|
||||||
|
fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError>;
|
||||||
|
fn delete(&mut self, path: &str) -> Result<(), FsError>;
|
||||||
|
fn exists(&self, path: &str) -> bool;
|
||||||
|
fn is_healthy(&self) -> bool { true }
|
||||||
|
}
|
||||||
5
crates/prometeu-core/src/fs/fs_entry.rs
Normal file
5
crates/prometeu-core/src/fs/fs_entry.rs
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
pub struct FsEntry {
|
||||||
|
pub name: String,
|
||||||
|
pub is_dir: bool,
|
||||||
|
pub size: u64,
|
||||||
|
}
|
||||||
24
crates/prometeu-core/src/fs/fs_error.rs
Normal file
24
crates/prometeu-core/src/fs/fs_error.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
use std::fmt;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum FsError {
|
||||||
|
NotFound,
|
||||||
|
AlreadyExists,
|
||||||
|
PermissionDenied,
|
||||||
|
NotMounted,
|
||||||
|
IOError(String),
|
||||||
|
Other(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FsError {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
match self {
|
||||||
|
FsError::NotFound => write!(f, "File or directory not found"),
|
||||||
|
FsError::AlreadyExists => write!(f, "Already exists"),
|
||||||
|
FsError::PermissionDenied => write!(f, "Permission denied"),
|
||||||
|
FsError::NotMounted => write!(f, "Filesystem not mounted"),
|
||||||
|
FsError::IOError(s) => write!(f, "IO Error: {}", s),
|
||||||
|
FsError::Other(s) => write!(f, "Error: {}", s),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
crates/prometeu-core/src/fs/fs_state.rs
Normal file
8
crates/prometeu-core/src/fs/fs_state.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use crate::fs::FsError;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum FsState {
|
||||||
|
Unmounted,
|
||||||
|
Mounted,
|
||||||
|
Error(FsError),
|
||||||
|
}
|
||||||
11
crates/prometeu-core/src/fs/mod.rs
Normal file
11
crates/prometeu-core/src/fs/mod.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
mod fs_error;
|
||||||
|
mod fs_state;
|
||||||
|
mod fs_entry;
|
||||||
|
mod fs_backend;
|
||||||
|
mod virtual_fs;
|
||||||
|
|
||||||
|
pub use fs_error::FsError;
|
||||||
|
pub use fs_state::FsState;
|
||||||
|
pub use fs_entry::FsEntry;
|
||||||
|
pub use fs_backend::FsBackend;
|
||||||
|
pub use virtual_fs::VirtualFS;
|
||||||
147
crates/prometeu-core/src/fs/virtual_fs.rs
Normal file
147
crates/prometeu-core/src/fs/virtual_fs.rs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
use crate::fs::{FsBackend, FsEntry, FsError};
|
||||||
|
|
||||||
|
pub struct VirtualFS {
|
||||||
|
backend: Option<Box<dyn FsBackend>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl VirtualFS {
|
||||||
|
pub fn new() -> Self {
|
||||||
|
Self { backend: None }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn mount(&mut self, mut backend: Box<dyn FsBackend>) -> Result<(), FsError> {
|
||||||
|
backend.mount()?;
|
||||||
|
self.backend = Some(backend);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmount(&mut self) {
|
||||||
|
if let Some(mut backend) = self.backend.take() {
|
||||||
|
backend.unmount();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_mounted(&self) -> bool {
|
||||||
|
self.backend.is_some()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn normalize_path(&self, path: &str) -> String {
|
||||||
|
let mut normalized = path.replace('\\', "/");
|
||||||
|
if !normalized.starts_with('/') {
|
||||||
|
normalized = format!("/{}", normalized);
|
||||||
|
}
|
||||||
|
normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn list_dir(&self, path: &str) -> Result<Vec<FsEntry>, FsError> {
|
||||||
|
let normalized = self.normalize_path(path);
|
||||||
|
let backend = self.backend.as_ref().ok_or(FsError::NotMounted)?;
|
||||||
|
backend.list_dir(&normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError> {
|
||||||
|
let normalized = self.normalize_path(path);
|
||||||
|
let backend = self.backend.as_ref().ok_or(FsError::NotMounted)?;
|
||||||
|
backend.read_file(&normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError> {
|
||||||
|
let normalized = self.normalize_path(path);
|
||||||
|
let backend = self.backend.as_mut().ok_or(FsError::NotMounted)?;
|
||||||
|
backend.write_file(&normalized, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn delete(&mut self, path: &str) -> Result<(), FsError> {
|
||||||
|
let normalized = self.normalize_path(path);
|
||||||
|
let backend = self.backend.as_mut().ok_or(FsError::NotMounted)?;
|
||||||
|
backend.delete(&normalized)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn exists(&self, path: &str) -> bool {
|
||||||
|
let normalized = self.normalize_path(path);
|
||||||
|
self.backend.as_ref().map(|b| b.exists(&normalized)).unwrap_or(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn is_healthy(&self) -> bool {
|
||||||
|
self.backend.as_ref().map(|b| b.is_healthy()).unwrap_or(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
struct MockBackend {
|
||||||
|
files: HashMap<String, Vec<u8>>,
|
||||||
|
healthy: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MockBackend {
|
||||||
|
fn new() -> Self {
|
||||||
|
Self {
|
||||||
|
files: HashMap::new(),
|
||||||
|
healthy: true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FsBackend for MockBackend {
|
||||||
|
fn mount(&mut self) -> Result<(), FsError> { Ok(()) }
|
||||||
|
fn unmount(&mut self) {}
|
||||||
|
fn list_dir(&self, _path: &str) -> Result<Vec<FsEntry>, FsError> {
|
||||||
|
Ok(self.files.keys().map(|name| FsEntry {
|
||||||
|
name: name.clone(),
|
||||||
|
is_dir: false,
|
||||||
|
size: 0,
|
||||||
|
}).collect())
|
||||||
|
}
|
||||||
|
fn read_file(&self, path: &str) -> Result<Vec<u8>, FsError> {
|
||||||
|
self.files.get(path).cloned().ok_or(FsError::NotFound)
|
||||||
|
}
|
||||||
|
fn write_file(&mut self, path: &str, data: &[u8]) -> Result<(), FsError> {
|
||||||
|
self.files.insert(path.to_string(), data.to_vec());
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn delete(&mut self, path: &str) -> Result<(), FsError> {
|
||||||
|
self.files.remove(path);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
fn exists(&self, path: &str) -> bool {
|
||||||
|
self.files.contains_key(path)
|
||||||
|
}
|
||||||
|
fn is_healthy(&self) -> bool {
|
||||||
|
self.healthy
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_virtual_fs_operations() {
|
||||||
|
let mut vfs = VirtualFS::new();
|
||||||
|
let backend = MockBackend::new();
|
||||||
|
|
||||||
|
vfs.mount(Box::new(backend)).unwrap();
|
||||||
|
|
||||||
|
let test_file = "/user/test.txt";
|
||||||
|
let content = b"hello world";
|
||||||
|
|
||||||
|
vfs.write_file(test_file, content).unwrap();
|
||||||
|
assert!(vfs.exists(test_file));
|
||||||
|
|
||||||
|
let read_content = vfs.read_file(test_file).unwrap();
|
||||||
|
assert_eq!(read_content, content);
|
||||||
|
|
||||||
|
vfs.delete(test_file).unwrap();
|
||||||
|
assert!(!vfs.exists(test_file));
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_virtual_fs_health() {
|
||||||
|
let mut vfs = VirtualFS::new();
|
||||||
|
let mut backend = MockBackend::new();
|
||||||
|
backend.healthy = false;
|
||||||
|
|
||||||
|
vfs.mount(Box::new(backend)).unwrap();
|
||||||
|
assert!(!vfs.is_healthy());
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -2,6 +2,7 @@ pub mod hardware;
|
|||||||
pub mod virtual_machine;
|
pub mod virtual_machine;
|
||||||
mod model;
|
mod model;
|
||||||
pub mod firmware;
|
pub mod firmware;
|
||||||
|
pub mod fs;
|
||||||
mod prometeu_os;
|
mod prometeu_os;
|
||||||
mod prometeu_hub;
|
mod prometeu_hub;
|
||||||
|
|
||||||
|
|||||||
@ -2,7 +2,9 @@ use crate::hardware::{HardwareBridge, InputSignals};
|
|||||||
use crate::model::{Cartridge, Color, Sample};
|
use crate::model::{Cartridge, Color, Sample};
|
||||||
use crate::prometeu_os::NativeInterface;
|
use crate::prometeu_os::NativeInterface;
|
||||||
use crate::virtual_machine::{Value, VirtualMachine};
|
use crate::virtual_machine::{Value, VirtualMachine};
|
||||||
|
use crate::fs::{VirtualFS, FsBackend, FsState};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
/// PrometeuOS (POS): O firmware/base do sistema.
|
/// PrometeuOS (POS): O firmware/base do sistema.
|
||||||
/// Autoridade máxima do boot, periféricos, execução da PVM e tratamento de falhas.
|
/// Autoridade máxima do boot, periféricos, execução da PVM e tratamento de falhas.
|
||||||
@ -17,6 +19,12 @@ pub struct PrometeuOS {
|
|||||||
pub sample_square: Option<Arc<Sample>>,
|
pub sample_square: Option<Arc<Sample>>,
|
||||||
pub sample_kick: Option<Arc<Sample>>,
|
pub sample_kick: Option<Arc<Sample>>,
|
||||||
pub sample_snare: Option<Arc<Sample>>,
|
pub sample_snare: Option<Arc<Sample>>,
|
||||||
|
|
||||||
|
// Filesystem
|
||||||
|
pub fs: VirtualFS,
|
||||||
|
pub fs_state: FsState,
|
||||||
|
pub open_files: HashMap<u32, String>,
|
||||||
|
pub next_handle: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PrometeuOS {
|
impl PrometeuOS {
|
||||||
@ -33,6 +41,10 @@ impl PrometeuOS {
|
|||||||
sample_square: None,
|
sample_square: None,
|
||||||
sample_kick: None,
|
sample_kick: None,
|
||||||
sample_snare: None,
|
sample_snare: None,
|
||||||
|
fs: VirtualFS::new(),
|
||||||
|
fs_state: FsState::Unmounted,
|
||||||
|
open_files: HashMap::new(),
|
||||||
|
next_handle: 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Inicializa samples básicos (mesma lógica do LogicalHardware)
|
// Inicializa samples básicos (mesma lógica do LogicalHardware)
|
||||||
@ -41,6 +53,30 @@ impl PrometeuOS {
|
|||||||
os
|
os
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn mount_fs(&mut self, backend: Box<dyn FsBackend>) {
|
||||||
|
match self.fs.mount(backend) {
|
||||||
|
Ok(_) => {
|
||||||
|
self.fs_state = FsState::Mounted;
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
self.fs_state = FsState::Error(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn unmount_fs(&mut self) {
|
||||||
|
self.fs.unmount();
|
||||||
|
self.fs_state = FsState::Unmounted;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn update_fs(&mut self) {
|
||||||
|
if self.fs_state == FsState::Mounted {
|
||||||
|
if !self.fs.is_healthy() {
|
||||||
|
self.unmount_fs();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn reset(&mut self, vm: &mut VirtualMachine) {
|
pub fn reset(&mut self, vm: &mut VirtualMachine) {
|
||||||
*vm = VirtualMachine::default();
|
*vm = VirtualMachine::default();
|
||||||
self.tick_index = 0;
|
self.tick_index = 0;
|
||||||
@ -60,6 +96,8 @@ impl PrometeuOS {
|
|||||||
let start = std::time::Instant::now();
|
let start = std::time::Instant::now();
|
||||||
self.tick_index += 1;
|
self.tick_index += 1;
|
||||||
|
|
||||||
|
self.update_fs();
|
||||||
|
|
||||||
if !self.logical_frame_active {
|
if !self.logical_frame_active {
|
||||||
self.logical_frame_active = true;
|
self.logical_frame_active = true;
|
||||||
self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME;
|
self.logical_frame_remaining_cycles = Self::CYCLES_PER_LOGICAL_FRAME;
|
||||||
@ -311,6 +349,107 @@ impl NativeInterface for PrometeuOS {
|
|||||||
}
|
}
|
||||||
Ok(300)
|
Ok(300)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// --- Filesystem Syscalls (0x4000) ---
|
||||||
|
|
||||||
|
// FS_OPEN(path) -> handle
|
||||||
|
0x4001 => {
|
||||||
|
let path = match vm.pop()? {
|
||||||
|
Value::String(s) => s,
|
||||||
|
_ => return Err("Expected string path".into()),
|
||||||
|
};
|
||||||
|
if self.fs_state != FsState::Mounted {
|
||||||
|
vm.push(Value::Integer(-1));
|
||||||
|
return Ok(100);
|
||||||
|
}
|
||||||
|
let handle = self.next_handle;
|
||||||
|
self.open_files.insert(handle, path);
|
||||||
|
self.next_handle += 1;
|
||||||
|
vm.push(Value::Integer(handle as i64));
|
||||||
|
Ok(200)
|
||||||
|
}
|
||||||
|
// FS_READ(handle) -> content
|
||||||
|
0x4002 => {
|
||||||
|
let handle = vm.pop_integer()? as u32;
|
||||||
|
let path = self.open_files.get(&handle).ok_or("Invalid handle")?;
|
||||||
|
match self.fs.read_file(path) {
|
||||||
|
Ok(data) => {
|
||||||
|
let s = String::from_utf8_lossy(&data).into_owned();
|
||||||
|
vm.push(Value::String(s));
|
||||||
|
Ok(1000)
|
||||||
|
}
|
||||||
|
Err(_e) => {
|
||||||
|
vm.push(Value::Null);
|
||||||
|
Ok(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FS_WRITE(handle, content)
|
||||||
|
0x4003 => {
|
||||||
|
let content = match vm.pop()? {
|
||||||
|
Value::String(s) => s,
|
||||||
|
_ => return Err("Expected string content".into()),
|
||||||
|
};
|
||||||
|
let handle = vm.pop_integer()? as u32;
|
||||||
|
let path = self.open_files.get(&handle).ok_or("Invalid handle")?;
|
||||||
|
match self.fs.write_file(path, content.as_bytes()) {
|
||||||
|
Ok(_) => {
|
||||||
|
vm.push(Value::Boolean(true));
|
||||||
|
Ok(1000)
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
vm.push(Value::Boolean(false));
|
||||||
|
Ok(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FS_CLOSE(handle)
|
||||||
|
0x4004 => {
|
||||||
|
let handle = vm.pop_integer()? as u32;
|
||||||
|
self.open_files.remove(&handle);
|
||||||
|
Ok(100)
|
||||||
|
}
|
||||||
|
// FS_LISTDIR(path)
|
||||||
|
0x4005 => {
|
||||||
|
let path = match vm.pop()? {
|
||||||
|
Value::String(s) => s,
|
||||||
|
_ => return Err("Expected string path".into()),
|
||||||
|
};
|
||||||
|
match self.fs.list_dir(&path) {
|
||||||
|
Ok(entries) => {
|
||||||
|
// Por enquanto, retorna uma string separada por ';'
|
||||||
|
let names: Vec<String> = entries.into_iter().map(|e| e.name).collect();
|
||||||
|
vm.push(Value::String(names.join(";")));
|
||||||
|
Ok(500)
|
||||||
|
}
|
||||||
|
Err(_) => {
|
||||||
|
vm.push(Value::Null);
|
||||||
|
Ok(100)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// FS_EXISTS(path) -> bool
|
||||||
|
0x4006 => {
|
||||||
|
let path = match vm.pop()? {
|
||||||
|
Value::String(s) => s,
|
||||||
|
_ => return Err("Expected string path".into()),
|
||||||
|
};
|
||||||
|
vm.push(Value::Boolean(self.fs.exists(&path)));
|
||||||
|
Ok(100)
|
||||||
|
}
|
||||||
|
// FS_DELETE(path)
|
||||||
|
0x4007 => {
|
||||||
|
let path = match vm.pop()? {
|
||||||
|
Value::String(s) => s,
|
||||||
|
_ => return Err("Expected string path".into()),
|
||||||
|
};
|
||||||
|
match self.fs.delete(&path) {
|
||||||
|
Ok(_) => vm.push(Value::Boolean(true)),
|
||||||
|
Err(_) => vm.push(Value::Boolean(false)),
|
||||||
|
}
|
||||||
|
Ok(500)
|
||||||
|
}
|
||||||
|
|
||||||
_ => Err(format!("Unknown syscall: 0x{:08X}", id)),
|
_ => Err(format!("Unknown syscall: 0x{:08X}", id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user