358 lines
No EOL
12 KiB
Rust
358 lines
No EOL
12 KiB
Rust
#![no_std]
|
|
#![no_main]
|
|
|
|
use core::sync::atomic::{AtomicBool, AtomicPtr, AtomicUsize, Ordering};
|
|
use liblbos::characters;
|
|
use crate::terminal::{print, println};
|
|
use liblbos::fs::{DirectoryReader, FileSystem};
|
|
|
|
static NO_EXIT: AtomicBool = AtomicBool::new(false);
|
|
static CURRENT_DIR_PATH: AtomicPtr<u8> = AtomicPtr::new(0 as *mut _);
|
|
static CURRENT_DIR_PATH_LEN: AtomicUsize = AtomicUsize::new(0);
|
|
|
|
struct Environment<'a> {
|
|
pub current_directory_path: &'a [u8],
|
|
}
|
|
|
|
#[panic_handler]
|
|
fn panic(info: &core::panic::PanicInfo) -> ! {
|
|
liblbos::syscalls::write_terminal(b"abort\n");
|
|
loop {}
|
|
}
|
|
|
|
fn cd(new_path: &str, environment: &mut Environment<'_>) {
|
|
let old_path = environment.current_directory_path;
|
|
let abs_new_path = if new_path.starts_with('/') {
|
|
// absolute path, replaces old path
|
|
let new_path_buf = liblbos::syscalls::alloc_blocks(new_path.len().div_ceil(512));
|
|
let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path.len()) };
|
|
// copy
|
|
{
|
|
for (i, &x) in new_path.as_bytes().iter().enumerate() {
|
|
new_path_buf[i] = x;
|
|
}
|
|
}
|
|
new_path_buf
|
|
} else {
|
|
// todo: ".." is currently handled on the ktask level, however...
|
|
// todo: we should evaluate this and instead modify the absolute path to remove the ".."
|
|
// todo: otherwise, we're bound to end up with a path like "/a/b/../../c" which is not nice
|
|
// relative path, append
|
|
let new_path_len = old_path.len() + new_path.len() + 1;
|
|
let new_path_buf = liblbos::syscalls::alloc_blocks(new_path_len.div_ceil(512));
|
|
let new_path_buf = unsafe { core::slice::from_raw_parts_mut(new_path_buf as *mut _, new_path_len) };
|
|
// copy old path
|
|
{
|
|
for (i, &x) in old_path.iter().enumerate() {
|
|
new_path_buf[i] = x;
|
|
}
|
|
}
|
|
// copy new path
|
|
{
|
|
for (i, &x) in new_path.as_bytes().iter().enumerate() {
|
|
new_path_buf[old_path.len() + i] = x;
|
|
}
|
|
}
|
|
// add separator
|
|
new_path_buf[old_path.len() + new_path.len()] = b'/';
|
|
|
|
new_path_buf
|
|
};
|
|
|
|
// check to make sure the new path is valid
|
|
{
|
|
let mut fs = FileSystem::empty();
|
|
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
|
println("failed to open fs");
|
|
// free
|
|
liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512));
|
|
return;
|
|
}
|
|
let mut dir = DirectoryReader::empty();
|
|
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, abs_new_path) != 0 {
|
|
println("invalid dir");
|
|
// free
|
|
liblbos::syscalls::free_blocks(abs_new_path.as_ptr() as usize, abs_new_path.len().div_ceil(512));
|
|
return;
|
|
}
|
|
}
|
|
|
|
// it is valid, update everything
|
|
{
|
|
// free old path
|
|
liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512));
|
|
}
|
|
CURRENT_DIR_PATH.store(abs_new_path.as_ptr() as *mut _, Ordering::Relaxed);
|
|
CURRENT_DIR_PATH_LEN.store(abs_new_path.len(), Ordering::Relaxed);
|
|
environment.current_directory_path = unsafe {
|
|
core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed))
|
|
};
|
|
}
|
|
|
|
fn lsdir(env: &Environment<'_>) {
|
|
let mut fs = FileSystem::empty();
|
|
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
|
println("failed to open fs");
|
|
return;
|
|
}
|
|
let mut dir = DirectoryReader::empty();
|
|
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, env.current_directory_path) != 0 {
|
|
println("failed to open dir");
|
|
return;
|
|
}
|
|
let mut record = liblbos::fs::Record {
|
|
name: [0; 12],
|
|
record_type: 0,
|
|
target: 0,
|
|
total_size_bytes: 0,
|
|
};
|
|
while liblbos::ktask::ktask_fsreaddir(&fs, &mut dir, &mut record) == 0 {
|
|
print("\n");
|
|
if record.record_type == 0 {
|
|
println("unexpected eod");
|
|
break;
|
|
}
|
|
print("- ");
|
|
let name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
|
|
liblbos::syscalls::write_terminal(
|
|
name
|
|
);
|
|
if record.record_type == liblbos::fs::RecordType::Directory as u8 {
|
|
liblbos::syscalls::write_terminal(&[b' ', characters::C_FOLDER as u8]);
|
|
} else if &name[name.len() - 3..] == b"DDI" {
|
|
liblbos::syscalls::write_terminal(&[b' ', characters::C_BLOOD as u8]);
|
|
} else {
|
|
liblbos::syscalls::write_terminal(&[b' ', characters::C_FILE as u8]);
|
|
}
|
|
}
|
|
print("\n");
|
|
}
|
|
|
|
fn attempt_run_file(env: &Environment<'_>, name: &str) {
|
|
print("\n");
|
|
if name.len() > 11 {
|
|
println("filename too long");
|
|
return;
|
|
}
|
|
|
|
let mut fs = FileSystem::empty();
|
|
if liblbos::ktask::ktask_fsopen(&mut fs) != 0 {
|
|
println("failed to open fs");
|
|
return;
|
|
}
|
|
let mut dir = DirectoryReader::empty();
|
|
if liblbos::ktask::ktask_fsopendir(&fs, &mut dir, env.current_directory_path) != 0 {
|
|
println("failed to open dir");
|
|
return;
|
|
}
|
|
let mut record = liblbos::fs::Record {
|
|
name: [0; 12],
|
|
record_type: 0,
|
|
target: 0,
|
|
total_size_bytes: 0,
|
|
};
|
|
fn namecmp(record: &liblbos::fs::Record, name: &[u8]) -> bool {
|
|
let record_name = &record.name[..record.name.iter().position(|&x| x == 0).unwrap_or(11)];
|
|
record_name == name
|
|
}
|
|
while !namecmp(&record, name.as_bytes()) {
|
|
if liblbos::ktask::ktask_fsreaddir(&fs, &mut dir, &mut record) != 0 {
|
|
println("file not found");
|
|
return;
|
|
}
|
|
if record.record_type == 0 {
|
|
println("unexpected eod");
|
|
return;
|
|
}
|
|
if record.record_type == liblbos::fs::RecordType::Directory as u8
|
|
&& namecmp(&record, name.as_bytes())
|
|
{
|
|
println("not a file");
|
|
return;
|
|
}
|
|
}
|
|
// found, load the file
|
|
let mut reader = liblbos::fs::FileReader::empty();
|
|
if liblbos::ktask::ktask_fsopenfile(&fs, &record, &mut reader) != 0 {
|
|
println("failed to open file");
|
|
return;
|
|
}
|
|
let size = record.total_size_bytes as usize;
|
|
let buf = liblbos::syscalls::alloc_blocks(size.div_ceil(512));
|
|
if liblbos::ktask::ktask_fsreadfile(&fs, &mut reader, unsafe { core::slice::from_raw_parts_mut(buf as *mut _, size) }) != 0 {
|
|
println("failed to read file");
|
|
}
|
|
|
|
let e = liblbos::Environment {
|
|
current_directory_path: CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize,
|
|
current_directory_path_len: CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed),
|
|
};
|
|
// fixme: turntable task needs to exit, but it owns the environment memory.
|
|
// fixme: we need to somehow pass the environment to the task, and have it free the memory
|
|
|
|
let mut task_setup = liblbos::TaskSetup {
|
|
epc: 0,
|
|
stack_block_count: 8,
|
|
ddi_first_addr: 0,
|
|
ddi_size: 0,
|
|
environment: 0,
|
|
wait_for_task_exit: false,
|
|
};
|
|
|
|
if liblbos::ktask::ktask_loadddifile(buf, size, &mut task_setup as *mut _ as usize) != 0 {
|
|
println("failed to load ddi");
|
|
liblbos::syscalls::free_blocks(buf, size.div_ceil(512));
|
|
return;
|
|
}
|
|
|
|
liblbos::syscalls::free_blocks(buf, size.div_ceil(512));
|
|
|
|
liblbos::syscalls::create_task(task_setup);
|
|
|
|
if CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize != 0 {
|
|
// free
|
|
liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512));
|
|
}
|
|
liblbos::syscalls::exit();
|
|
}
|
|
|
|
fn execute(cmd: &str, environment: &mut Environment<'_>) {
|
|
print("\n");
|
|
let mut split = cmd.split_ascii_whitespace();
|
|
|
|
if let Some(cmd) = split.next() {
|
|
match cmd {
|
|
"ls" => {
|
|
lsdir(environment);
|
|
}
|
|
"cd" => {
|
|
if let Some(path) = split.next() {
|
|
cd(path, environment);
|
|
} else {
|
|
println("cd requires a path");
|
|
}
|
|
}
|
|
"exit" => {
|
|
if NO_EXIT.load(Ordering::Relaxed) {
|
|
println("cannot exit as task 1");
|
|
} else {
|
|
if CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize != 0 {
|
|
// free
|
|
liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512));
|
|
}
|
|
liblbos::syscalls::exit();
|
|
}
|
|
}
|
|
_ => {
|
|
attempt_run_file(environment, cmd);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
fn hex(mut n: u8) -> [u8; 2] {
|
|
let mut buf = [0u8; 2];
|
|
for i in (0..2).rev() {
|
|
let num = n & 0b1111;
|
|
n >>= 4;
|
|
if num < 10 {
|
|
buf[i] = b'0' + num;
|
|
} else {
|
|
buf[i] = b'a' + (num - 10);
|
|
}
|
|
}
|
|
buf
|
|
}
|
|
|
|
mod arch;
|
|
mod terminal;
|
|
|
|
fn main_wrapper() {
|
|
let tid = liblbos::syscalls::current_task();
|
|
NO_EXIT.store(tid == 1, Ordering::Relaxed);
|
|
|
|
let environment = liblbos::syscalls::environment_pointer();
|
|
let mut environment = if environment == 0 {
|
|
// no environment, construct it ourself
|
|
static DEFAULT_CURRENT_DIRECTORY: &[u8] = b"/";
|
|
let environment = Environment {
|
|
current_directory_path: DEFAULT_CURRENT_DIRECTORY,
|
|
};
|
|
environment
|
|
} else {
|
|
let env = unsafe { &mut *(environment as *mut liblbos::Environment) };
|
|
let current_directory_path = unsafe { core::slice::from_raw_parts(env.current_directory_path as *const u8, env.current_directory_path_len) };
|
|
let environment = Environment {
|
|
current_directory_path,
|
|
};
|
|
environment
|
|
};
|
|
// replace the current path with one that we allocate ourself, so that we can modify it later
|
|
{
|
|
let path = environment.current_directory_path;
|
|
let len = environment.current_directory_path.len();
|
|
CURRENT_DIR_PATH.store(liblbos::syscalls::alloc_blocks(len.div_ceil(512)) as *mut u8, Ordering::Relaxed);
|
|
CURRENT_DIR_PATH_LEN.store(len, Ordering::Relaxed);
|
|
{
|
|
// copy
|
|
let dest = unsafe {
|
|
core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut _, len)
|
|
};
|
|
for (i, &x) in path.iter().enumerate() {
|
|
dest[i] = x;
|
|
}
|
|
}
|
|
environment.current_directory_path = unsafe { core::slice::from_raw_parts_mut(CURRENT_DIR_PATH.load(Ordering::Relaxed) as *mut u8, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed)) };
|
|
}
|
|
|
|
print("\n\n");
|
|
print("turntable v");
|
|
const VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
print(VERSION);
|
|
print("\n");
|
|
|
|
println("(c) 2025\nReal Microsoft, LLC");
|
|
|
|
print("> ");
|
|
|
|
let mut cmdbuf = [0u8; 256];
|
|
let mut cmdbuf_len = 0;
|
|
let mut inbuf = [0u8; 8];
|
|
loop {
|
|
let read = liblbos::syscalls::read_inbuf(&mut inbuf);
|
|
if read > 0 {
|
|
for c in &inbuf[0..read] {
|
|
if cmdbuf_len >= cmdbuf.len() {
|
|
print("\x07");
|
|
} else {
|
|
// backspace
|
|
if *c == 0x7f && cmdbuf_len > 0 {
|
|
cmdbuf_len -= 1;
|
|
print("\x08 \x08");
|
|
} else if *c == b'\r' {
|
|
execute(unsafe {
|
|
core::str::from_utf8_unchecked(&cmdbuf[0..cmdbuf_len])
|
|
},
|
|
&mut environment);
|
|
cmdbuf_len = 0;
|
|
print("> ");
|
|
break;
|
|
} else if !c.is_ascii_control() {
|
|
cmdbuf[cmdbuf_len] = *c;
|
|
cmdbuf_len += 1;
|
|
liblbos::syscalls::write_terminal(&[*c]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
#[unsafe(no_mangle)]
|
|
extern "C" fn main() {
|
|
main_wrapper();
|
|
if CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize != 0 {
|
|
// free
|
|
liblbos::syscalls::free_blocks(CURRENT_DIR_PATH.load(Ordering::Relaxed) as usize, CURRENT_DIR_PATH_LEN.load(Ordering::Relaxed).div_ceil(512));
|
|
}
|
|
} |