ok
This commit is contained in:
FantasyPvP
2023-03-10 00:25:08 +00:00
commit e491435dea
60 changed files with 4542 additions and 0 deletions
+126
View File
@@ -0,0 +1,126 @@
use async_trait::async_trait;
use rand::prelude::*;
use super::{
engine::{eventcheck, Choice, Event},
entity::{Entity, Enemy, EntityObject},
player::Player,
};
use alloc::{boxed::Box, string::{String, ToString}, vec::Vec, format, borrow::ToOwned};
use crate::{
std::application::{
Application,
Error,
},
std::{
io::{self, println, serial_println, FRAMEGEN, Element},
random,
},
};
pub struct GameLoop;
#[async_trait]
impl Application for GameLoop {
fn new() -> Self {
Self {}
}
async fn run(&mut self, _args: Vec<String>) -> Result<(), Error> {
let mut username: String = io::stdin().await;
username = username.trim().to_string();
let mut player = Player::new(username);
let mut enemy = Enemy::new();
for _ in 0..30 {
match (eventcheck(player.attack_entity(&mut EntityObject::Enemy(&mut enemy)))) {
Choice::A(result) => {
println!("{}", result);
},
Choice::B(event) => {
println!("{}", event);
match event {
Event::PlayerKilled => {
println!(" [!] {} was slain by Enemy\n\n[ You lost! ]", player.username);
break;
}
Event::EntityKilled(entity) => {
println!("\n [!] Enemy was slain by {}\n\n [ You won! ]", player.username);
break;
}
}
}
}
println!("{}", eventcheck(enemy.attack_entity(&mut EntityObject::Player(&mut player))));
println!("[{}\n[{}", player, enemy);
}
FRAMEGEN.lock().render_frame();
let string = String::from(format!(
"┌────────────────────────────┐
{}
{} / {}
└────────────────────────────┘"
, player.username, player.health_points, player.max_health_points));
let mut healthbar = Element::from_str(string);
healthbar.render((1, 1));
let new2 = String::from("slushy stfu");
let mut new = Element::from_str(new2);
new.render((10, 20));
FRAMEGEN.lock().render_frame();
let fr = FRAMEGEN.lock().get_frame().to_owned();
serial_println!("{}", {
let mut string = String::new();
for row in fr {
let mut r = String::new();
for col in row {
r.push(col);
}
string.push_str(&r);
string.push('\n')
};
string
});
loop {
println!("{}", io::stdchar().await)
}
Ok(())
}
}
fn random() -> u64 {
let mut r = random::Random::int(0, 125) as u64;
r
}
+63
View File
@@ -0,0 +1,63 @@
use async_trait::async_trait;
use alloc::{boxed::Box, string::String, vec::Vec};
use crate::{
kernel::{
os::OS,
render::{Color, write}
},
println,
std::application::{
Application,
Error,
},
};
pub struct CrystalFetch {}
#[async_trait]
impl Application for CrystalFetch {
fn new() -> Self {
Self {}
}
async fn run(&mut self, _args: Vec<String>) -> Result<(), Error> {
let os = OS.lock().os.clone();
let version = OS.lock().version.clone();
write(format_args!("
────────────────────────────────────────────────────────
_____ _ _ ____ _____
/ ____| | | | |/ __ \\ / ____|
| | _ __ _ _ ___| |_ __ _| | | | | (___
| | | '__| | | / __| __/ _` | | | | |\\___ \\
| |____| | | |_| \\__ \\ || (_| | | |__| |____) |
\\_____|_| \\__, |___/\\__\\__,_|_|\\____/|_____/
__/ |
|___/
"), (Color::Magenta, Color::Black));
println!("
╔═══════════════════════════════
║ OS » {}
║ BUILD » {}
║ RAM » idk
║ Shell » CrystalSH
║ API » CrystalAPI
║ Pkgs » 4
║ Fetch » CrystalFetch
╚═══════════════════════════════
────────────────────────────────────────────────────────
", os, version);
Ok(())
}
}
+33
View File
@@ -0,0 +1,33 @@
/*
[ Cry-SH ]
CrystalOS shell rewrite to replace the original shell implementation
this shell should support:
- running basic commands
- a prompt that displays the status of the last command
- customised error messages returned from applications
- invoking any application with arguments
- cycling through previous commands with arrow keys
- parsing of basic mathematical expressions using the calc module
- chained commands using the '|' or pipe operator which sends the output
of one command to the next
*/
// import necessary modules
use async_trait::async_trait;
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::instructions::interrupts;
use alloc::{string::{String, ToString}, vec::Vec, boxed::Box};
use crate::{
kernel::tasks::keyboard::KEYBOARD,
std::application::{Error, Application}
std::io::{println, print};
};
use super::*
+109
View File
@@ -0,0 +1,109 @@
#![no_std]
#![cfg_attr(test, no_main)]
#![feature(custom_test_frameworks)]
#![test_runner(crate::test_runner)]
#![reexport_test_harness_main = "test_main"]
#![feature(abi_x86_interrupt)]
#![feature(alloc_error_handler)]
#![feature(async_fn_in_trait)]
#![feature(global_asm)]
use core::panic::PanicInfo;
use spin::Mutex;
pub mod system;
pub mod user;
pub use system::kernel as kernel;
pub use system::std as std;
pub use user::bin::*;
extern crate alloc;
//extern crate fatfs;
#[cfg(test)]
use bootloader::{entry_point, BootInfo};
#[cfg(test)]
entry_point!(test_kernel_main);
#[alloc_error_handler]
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
panic!("error while allocating: {:?}", layout)
}
pub fn init() {
system::init();
}
pub fn hlt() -> ! {
loop {
x86_64::instructions::hlt();
}
}
pub trait Testable {
fn run(&self) -> ();
}
impl<T> Testable for T where T: Fn(), {
fn run(&self) {
serial_print!("{}...\t", core::any::type_name::<T>());
self();
serial_println!("OK");
}
}
pub fn test_runner(tests: &[&dyn Testable]) {
serial_println!("Running {} tests", tests.len());
for test in tests {
test.run();
}
exit(QemuExitCode::Ok);
}
pub fn test_panic_handler(info: &PanicInfo) -> ! {
serial_println!("ERR");
serial_println!("Error: {}\n", info);
exit(QemuExitCode::Err);
hlt();
}
#[cfg(test)]
fn test_kernel_main(_boot_info: &'static BootInfo) -> ! {
init();
test_main();
hlt();
}
#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
test_panic_handler(info)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {
Ok = 0x10,
Err = 0x11,
}
pub fn poweroff() {
exit(QemuExitCode::Ok);
}
pub fn exit(code: QemuExitCode) {
use x86_64::instructions::port::Port;
unsafe {
let mut port = Port::new(0xf4);
port.write(code as u32);
}
println!("e");
}
+59
View File
@@ -0,0 +1,59 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(CrystalOS::test_runner)]
#![reexport_test_harness_main = "test_main"]
use core::panic::PanicInfo;
use CrystalOS::{println, print, println_log, print_log};
use CrystalOS::kernel::tasks::{Task, executor::Executor, keyboard};
use bootloader::{BootInfo, entry_point};
extern crate alloc;
use alloc::{boxed::Box, vec, vec::Vec, rc::Rc, string, string::String};
use CrystalOS::user::bin::shell;
#[cfg(not(test))]
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
println!("{}", _info);
CrystalOS::hlt();
}
#[cfg(test)]
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
CrystalOS::test_panic_handler(info)
}
entry_point!(main);
fn main(boot_info: &'static BootInfo) -> ! {
use CrystalOS::kernel::allocator;
use CrystalOS::kernel::memory;
use CrystalOS::kernel::memory::BootInfoFrameAllocator;
use x86_64::{structures::paging::{Page, Translate}, VirtAddr};
CrystalOS::init();
let physical_memory_offset = VirtAddr::new(boot_info.physical_memory_offset);
let mut mapper = unsafe { memory::init(physical_memory_offset) };
let mut frame_allocator = unsafe {
BootInfoFrameAllocator::init(&boot_info.memory_map)
};
allocator::init_heap(&mut mapper, &mut frame_allocator).expect("heap initialisation failed");
let mut executor = Executor::new();
executor.spawn(Task::new(shell::command_handler()));
executor.run();
#[cfg(test)]
test_main();
loop {}
}
+63
View File
@@ -0,0 +1,63 @@
use alloc::alloc::{GlobalAlloc, Layout};
use core::ptr::null_mut;
use linked_list_allocator::LockedHeap;
use x86_64::{
structures::paging::{
mapper::MapToError, FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB,
},
VirtAddr,
};
pub fn init_heap(
mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>
) -> Result<(), MapToError<Size4KiB>> {
let page_range = {
let heap_start = VirtAddr::new(HEAP_START as u64);
let heap_end = heap_start + HEAP_SIZE - 1u64;
let heap_start_page = Page::containing_address(heap_start);
let heap_end_page = Page::containing_address(heap_end);
Page::range_inclusive(heap_start_page, heap_end_page)
};
for page in page_range {
let frame = frame_allocator.allocate_frame()
.ok_or(MapToError::FrameAllocationFailed)?;
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
unsafe {
mapper.map_to(page, frame, flags, frame_allocator)?.flush()
};
unsafe { ALLOCATOR.lock().init(HEAP_START as *mut u8, HEAP_SIZE); }
}
Ok(())
}
pub struct Dummy;
unsafe impl GlobalAlloc for Dummy {
unsafe fn alloc(&self, _layout: Layout) -> *mut u8 {
null_mut()
}
unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {
panic!("this method should not be used")
}
}
#[global_allocator]
static ALLOCATOR: LockedHeap = LockedHeap::empty();
pub const HEAP_START: usize = 0x_4444_4444_0000;
pub const HEAP_SIZE: usize = 100 * 1024;
+160
View File
@@ -0,0 +1,160 @@
use lazy_static::lazy_static;
use async_trait::async_trait;
use spin::Mutex;
use alloc::{vec::Vec, string::String, boxed::Box};
use crate::std::{self, application::{Error, Application}};
lazy_static! {
pub static ref FILESYSTEM: Mutex<Box<File>> = Mutex::new(Box::new(File::new(
String::from(""), // root
FileType::Dir(Directory::new()),
)));
}
pub type Directory = Vec<Box<File>>;
#[derive(Debug)]
pub struct File {
pub name: String,
pub data: FileType,
}
impl File {
pub fn new(name: String, data: FileType) -> Self {
Self { name, data }
}
}
#[derive(Debug)]
pub enum FileType {
Dir(Directory),
Txt(String),
Exe(Apppp),
}
pub fn mkfs() {
let mut fs = FILESYSTEM.lock();
match fs.data {
FileType::Dir(ref mut dir) => {
dir.push(Box::new(
File::new(
String::from("hello there"),
FileType::Txt(String::from("this is a basic text file")),
)
));
dir.push(Box::new(
File::new(
String::from("function that prints out an integer"),
FileType::Exe(Apppp::new()),
)
));
}
_ => {
()
}
}
}
#[derive(Debug)]
pub struct Apppp {}
#[async_trait]
impl std::application::Application for Apppp {
fn new() -> Self {
Self {}
}
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
Ok(())
}
}
/*
lazy_static! {
pub static ref FILESYSTEM: Mutex<Filesystem> = Mutex::new(Filesystem::new());
}
enum FsError {
FileNotFound,
AlreadyExists,
InvalidPath,
}
pub type Path = String;
impl Path {
pub fn new(path: &str) -> Result<Path, FsError> {
if path.is_empty() {
return Err(FsError::FileNotFound);
}
Ok(path.to_string())
}
pub fn dirs(&self) -> Vec<String> {
self.split('/').map(|s| s.to_string()).collect()
}
}
pub struct Filesystem {
pub root: Vec<File>
}
pub type Directory = Vec<File>;
impl Directory {
pub fn new(name: String, files: Vec<File>) -> Self {
Self { name, files }
}
pub fn size(&self) -> usize {
self.files.len()
}
pub fn mkdir(&mut self, name: &str, location: Path) -> Result<(), FsError> {
if self.exists(location.as_str()) {
return Err(FsError::AlreadyExists);
}
self.files.push(File::new(name, FileType::Directory(Box::new(Directory::new()))))
}
}
pub enum FileType {
Directory(Box<Directory>),
TextFile(Box<TextFile>),
BinaryFile(Box<BinaryFile>),
Executable(fn()),
}
pub struct File {
pub name: String,
pub file_type: FileType,
}
impl File {
pub fn new(name: String, file_type: FileType) -> Self {
Self { name, file_type }
}
}
pub struct TextFile {
pub name: String,
pub data: String,
}
impl TextFile {
pub fn new(name: String, data: String) -> Self {
Self { name, data }
}
pub fn size(&self) -> usize {
self.data.len()
}
}
*/
+50
View File
@@ -0,0 +1,50 @@
use x86_64::VirtAddr;
use x86_64::structures::tss::TaskStateSegment;
use x86_64::structures::gdt::{GlobalDescriptorTable, Descriptor, SegmentSelector};
use lazy_static::lazy_static;
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
lazy_static! {
static ref TSS: TaskStateSegment = {
let mut tss = TaskStateSegment::new();
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
const STACK_SIZE: usize = 4096 * 5;
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
let stack_start = VirtAddr::from_ptr(unsafe{ &STACK });
let stack_end = stack_start + STACK_SIZE;
stack_end
};
tss
};
}
lazy_static! {
static ref GDT: (GlobalDescriptorTable, Selectors) = {
let mut gdt = GlobalDescriptorTable::new();
let code_selector = gdt.add_entry(Descriptor::kernel_code_segment());
let tss_selector = gdt.add_entry(Descriptor::tss_segment(&TSS));
(gdt, Selectors { code_selector, tss_selector })
};
}
pub fn init() {
use x86_64::instructions::tables::load_tss;
use x86_64::instructions::segmentation::{CS, Segment};
GDT.0.load();
unsafe {
CS::set_reg(GDT.1.code_selector);
load_tss(GDT.1.tss_selector);
}
}
struct Selectors {
code_selector: SegmentSelector,
tss_selector: SegmentSelector,
}
+110
View File
@@ -0,0 +1,110 @@
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use crate::{print, println};
use crate::kernel::gdt;
use lazy_static::lazy_static;
use spin;
use pic8259::ChainedPics;
pub fn init_idt() {
IDT.load();
}
extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
println!("EXCEPTION: breakpoint\n{:#?}", stack_frame);
}
extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, _error_code: u64) -> ! {
panic!("EXCEPTION: double fault\n{:#?}", stack_frame)
}
extern "x86-interrupt" fn timer_interrupt_handler(stack_frame: InterruptStackFrame) {
unsafe {
GLOBALTIMER.lock().inc();
PICS.lock().notify_end_of_interrupt(InterruptIndex::Timer.as_u8());
}
}
extern "x86-interrupt" fn keyboard_interrupt_handler(_stack_frame: InterruptStackFrame) {
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use spin::Mutex;
use x86_64::instructions::port::Port;
lazy_static! {
static ref KEYBOARD: Mutex<Keyboard<layouts::Uk105Key, ScancodeSet1>> = {
Mutex::new(Keyboard::new(layouts::Uk105Key, ScancodeSet1, HandleControl::Ignore))
};
}
let mut keyboard = KEYBOARD.lock();
let mut port = Port::new(0x60);
let scancode: u8 = unsafe { port.read() };
crate::kernel::tasks::keyboard::add_scancode(scancode);
unsafe {
PICS.lock().notify_end_of_interrupt(InterruptIndex::Keyboard.as_u8());
}
}
lazy_static! {
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
idt.breakpoint.set_handler_fn(breakpoint_handler);
unsafe {
idt.double_fault.set_handler_fn(double_fault_handler)
.set_stack_index(gdt::DOUBLE_FAULT_IST_INDEX);
}
idt[InterruptIndex::Timer.as_usize()].set_handler_fn(timer_interrupt_handler);
idt[InterruptIndex::Keyboard.as_usize()].set_handler_fn(keyboard_interrupt_handler);
idt
};
}
lazy_static! {
pub static ref GLOBALTIMER: spin::Mutex<Timer> = spin::Mutex::new(Timer::new());
}
pub struct Timer {
pub val: i64
}
impl Timer {
pub fn new() -> Self {
Self { val: 0 }
}
pub fn inc(&mut self) {
self.val += 1
}
pub fn clear(&mut self) {
self.val = 0
}
}
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
pub static PICS: spin::Mutex<ChainedPics> = spin::Mutex::new( unsafe {
ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET)
});
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum InterruptIndex {
Timer = PIC_1_OFFSET,
Keyboard,
}
impl InterruptIndex {
fn as_u8(self) -> u8 {
self as u8
}
fn as_usize(self) -> usize {
usize::from(self.as_u8())
}
}
+140
View File
@@ -0,0 +1,140 @@
use x86_64::{
structures::paging::{Page, PhysFrame, Mapper, Size4KiB, FrameAllocator, PageTable},
VirtAddr,
PhysAddr
};
use bootloader::bootinfo::{MemoryMap, MemoryRegionType};
use x86_64::structures::paging::OffsetPageTable;
unsafe fn active_level_4_table(physical_memory_offset: VirtAddr) -> &'static mut PageTable {
use x86_64::registers::control::Cr3;
let (level_4_table_frame, _) = Cr3::read();
let phys = level_4_table_frame.start_address();
let virt = physical_memory_offset + phys.as_u64();
let page_table_ptr: *mut PageTable = virt.as_mut_ptr();
&mut *page_table_ptr
}
pub unsafe fn init(physical_memory_offset: VirtAddr) -> OffsetPageTable<'static> {
let level_4_table = active_level_4_table(physical_memory_offset);
OffsetPageTable::new(level_4_table, physical_memory_offset)
}
pub struct BootInfoFrameAllocator {
memory_map: &'static MemoryMap,
next: usize,
}
impl BootInfoFrameAllocator {
pub unsafe fn init(memory_map: &'static MemoryMap) -> Self {
BootInfoFrameAllocator {
memory_map,
next: 0,
}
}
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
let regions = self.memory_map.iter();
let usable_regions = regions.filter(|r|
r.region_type == MemoryRegionType::Usable
);
let address_ranges = usable_regions.map(|r|
r.range.start_addr()..r.range.end_addr()
);
let frame_addresses = address_ranges.flat_map(|r| r.step_by(4096));
frame_addresses.map(|a| PhysFrame::containing_address(PhysAddr::new(a)))
}
}
pub struct EmptyFrameAllocator;
unsafe impl FrameAllocator<Size4KiB> for EmptyFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame> {
None
}
}
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
fn allocate_frame(&mut self) -> Option<PhysFrame> {
let frame = self.usable_frames().nth(self.next);
self.next += 1;
frame
}
}
/*
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StackBounds {
start: VirtAddr,
end: VirtAddr,
}
impl StackBounds {
pub fn start(&self) -> VirtAddr {
self.start
}
pub fn end(&self) -> VirtAddr {
self.end
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub struct ThreadId(u64);
impl ThreadId {
pub fn as_u64(&self) -> u64 {
self.0
}
fn new() -> Self {
use core::sync::atomic::{AtomicU64, Ordering};
static NEXT_THREAD_ID: AtomicU64 = AtomicU64::new(1);
ThreadId(NEXT_THREAD_ID.fetch_add(1, Ordering::Relaxed))
}
}
fn reserve_stack_memory(size_in_pages: u64) -> Page {
use core::sync::atomic::{AtomicU64, Ordering};
static STACK_ALLOC_NEXT: AtomicU64 = AtomicU64::new(0x_5555_5555_0000);
let start_addr = VirtAddr::new(STACK_ALLOC_NEXT.fetch_add(
size_in_pages * Page::<Size4KiB>::SIZE,
Ordering::Relaxed,
));
Page::from_start_address(start_addr).expect("STACK_ALLOC_NEXT: not page aligned")
}
pub fn alloc_stack(
size_in_pages: u64, mapper: &mut impl Mapper<Size4KiB>,
frame_allocator: &mut impl FrameAllocator<Size4KiB>
) -> Result<StackBounds, mapper::MapToError> {
use x86_64::structures::paging::PageTableFlags as Flags;
let guard_page = reserve_stack_memory(size_in_pages + 1);
let stack_start = guard_page + 1;
let stack_end = stack_start + size_in_pages;
for page in Page::range(stack_start, stack_end) {
let frame = frame_allocator.allocate_frame().ok_or(mapper.MapToError::FrameAllocatorFailed)?;
let flags = Flags::PRESENT | Flags::WRITABLE;
mapper.map_to(page, frame, flags, frame_allocator)?.flush();
}
Ok(StackBounds {
start: stack_start.start_address(),
end: stack_end.start_address(),
})
}
*/
+8
View File
@@ -0,0 +1,8 @@
pub mod allocator;
pub mod fs;
pub mod gdt;
pub mod interrupts;
pub mod memory;
pub mod render;
pub mod serial;
pub mod tasks;
+16
View File
@@ -0,0 +1,16 @@
use lazy_static::lazy_static;
use spin::Mutex;
use alloc::{string::String};
lazy_static! {
pub static ref OS: Mutex<SysInfo> = Mutex::new(SysInfo {
os: String::from("CrystalOS Alpha"),
version: String::from("0.2.1"),
});
}
pub struct SysInfo {
pub os: String,
pub version: String,
}
+286
View File
@@ -0,0 +1,286 @@
use volatile::Volatile;
use lazy_static::lazy_static;
use core::fmt;
use spin::Mutex;
use alloc::vec::Vec;
use alloc::vec;
use alloc::borrow::ToOwned;
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(transparent)]
pub struct ColorCode(u8);
impl ColorCode {
pub fn new(foreground: Color, background: Color) -> ColorCode {
ColorCode((background as u8) << 5 | (foreground as u8))
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(C)]
struct ScreenChar {
character: u8,
colour: ColorCode,
}
pub const BUFFER_HEIGHT: usize = 25;
pub const BUFFER_WIDTH: usize = 80;
#[repr(transparent)]
struct Buffer {
chars: [[Volatile<ScreenChar>; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
struct BufferSwap {
chars: [[ScreenChar; BUFFER_WIDTH]; BUFFER_HEIGHT],
}
struct CharGrid {
chars: Vec<[ScreenChar; BUFFER_WIDTH]>
}
pub struct Renderer {
col_pos: usize,
pub col_code: ColorCode,
buffer: &'static mut Buffer,
userspace: BufferSwap,
upwards: CharGrid,
downwards: CharGrid,
pub sandbox: bool,
}
lazy_static! {
pub static ref RENDERER: Mutex<Renderer> = Mutex::new(Renderer {
col_pos: 0,
col_code: ColorCode::new(Color::White, Color::Black),
buffer: unsafe {
&mut *(0xb8000 as *mut Buffer)
},
userspace: BufferSwap {
chars: [[ScreenChar {
character: 179u8,
colour: ColorCode::new(Color::White, Color::Black),
}; BUFFER_WIDTH]; BUFFER_HEIGHT]
},
upwards: CharGrid {
chars: vec![
[ScreenChar {
character: 32u8,
colour: ColorCode::new(Color::White, Color::Black),
}; 80]
]
},
downwards: CharGrid {
chars: vec![
[ScreenChar {
character: 32u8,
colour: ColorCode::new(Color::White, Color::Black),
}; 80]
]
},
sandbox: false,
});
}
impl Renderer {
pub fn text_mode(&mut self) -> Result<(), ()> {
if !self.sandbox { return Err(()) };
self.buffer_swap().unwrap();
self.sandbox = false;
Ok(())
}
pub fn sandbox_mode(&mut self) -> Result<(), ()> {
if self.sandbox { return Err(()) };
self.buffer_swap().unwrap();
self.sandbox = true;
Ok(())
}
fn buffer_swap(&mut self) -> Result<(), ()> {
for (i, _) in self.userspace.chars.clone().iter().enumerate() {
let tmp = self.buffer.chars[i].clone();
for (j, col) in self.userspace.chars[i].clone().iter().enumerate() {
self.buffer.chars[i][j].write(col.to_owned())
}
for (j, _) in tmp.iter().enumerate() {
self.userspace.chars[i][j] = tmp[j].read().to_owned()
}
}
Ok(())
}
pub fn render_frame(&mut self, frame: [ [ char; BUFFER_WIDTH ]; BUFFER_HEIGHT]) {
for (i, row) in frame.iter().enumerate() {
for (j, col) in row.iter().enumerate() {
if let Some(c) = self.fancy_char(*col) {
self.buffer.chars[i][j].write(ScreenChar { character: c, colour: self.col_code});
} else {
self.buffer.chars[i][j].write(ScreenChar { character: *col as u8, colour: self.col_code});
}
}
}
}
pub fn write_string(&mut self, string: &str) {
for ch in string.chars() {
if let Some(x) = self.fancy_char(ch) {
self.write_byte(x)
} else {
match ch as u8 {
0x20..=0xff | b'\n' => self.write_byte(ch as u8),
_ => self.write_byte(0xfe),
}
}
}
}
fn fancy_char(&self, ch: char) -> Option<u8> {
let res: u8 = match ch {
'│' => 179,
'─' => 196,
'┴' => 193,
'┤' => 180,
'═' => 205,
'║' => 186,
'╗' => 187,
'╝' => 188,
'╚' => 200,
'╔' => 201,
'»' => 175,
'┐' => 191,
'└' => 192,
'┘' => 217,
'┌' => 218,
_ => { return None; }
};
Some(res)
}
pub fn backspace(&mut self) -> Result<(), ()> {
if self.col_pos == 0 {
self.undonewline();
}
self.col_pos -= 1;
let row = BUFFER_HEIGHT -1;
let col = self.col_pos;
let blank = ScreenChar {
character: b' ',
colour: self.col_code,
};
self.buffer.chars[row][col].write(blank);
Ok(())
}
pub fn write_byte(&mut self, byte: u8) {
match byte {
b'\n' => {
self.newline()
},
byte => {
if self.col_pos >= BUFFER_WIDTH {
self.newline();
}
let row = BUFFER_HEIGHT -1;
let col = self.col_pos;
let col_code = self.col_code;
self.buffer.chars[row][col].write(ScreenChar {
character: byte,
colour: col_code,
});
self.col_pos += 1
}
}
}
fn newline(&mut self) {
for row in 1..BUFFER_HEIGHT {
for col in 0..BUFFER_WIDTH {
let character = self.buffer.chars[row][col].read();
self.buffer.chars[row - 1][col].write(character);
}
}
self.clear_row(BUFFER_HEIGHT -1);
self.col_pos = 0;
}
pub fn undonewline(&mut self) {
for row in (0..BUFFER_HEIGHT-1).rev() {
for col in 0..BUFFER_WIDTH {
let character = self.buffer.chars[row][col].read();
self.buffer.chars[row + 1][col].write(character);
}
}
self.clear_row(0);
self.col_pos = BUFFER_WIDTH;
}
pub fn clear(&mut self) {
for row in (0..BUFFER_HEIGHT-1).rev() {
self.clear_row(row);
}
}
fn clear_row(&mut self, row: usize) {
let blank = ScreenChar {
character: b' ',
colour: self.col_code,
};
for col in 0..BUFFER_WIDTH {
self.buffer.chars[row][col].write(blank);
}
}
}
impl fmt::Write for Renderer {
fn write_str(&mut self, string:&str) -> fmt::Result {
self.write_string(string);
Ok(())
}
}
pub fn write(args: fmt::Arguments, cols: (Color, Color)) {
use core::fmt::Write;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
let mut writer = RENDERER.lock();
writer.col_code = ColorCode::new(cols.0, cols.1);
writer.write_fmt(args).unwrap()
})
}
+46
View File
@@ -0,0 +1,46 @@
use uart_16550::SerialPort;
use spin::Mutex;
use lazy_static::lazy_static;
lazy_static! {
pub static ref SERIAL1: Mutex<SerialPort> = {
let mut serial_port = unsafe {
SerialPort::new(0x3F8)
};
serial_port.init();
Mutex::new(serial_port)
};
}
#[doc(hidden)]
pub fn _print(args: core::fmt::Arguments) {
use core::fmt::Write;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
SERIAL1.lock().write_fmt(args).expect("unable to print to serial!")
})
}
#[macro_export]
macro_rules! serial_print {
($($arg:tt)*) => {
$crate::kernel::serial::_print(format_args!($($arg)*));
};
}
#[macro_export]
macro_rules! serial_println {
() => (serial_print!("\n"));
($fmt:expr) => ($crate::serial_print!(concat!($fmt, "\n")));
($fmt:expr, $($arg:tt)*) => (
$crate::serial_print!(
concat!($fmt, "\n"), $($arg)*
)
);
}
+3
View File
@@ -0,0 +1,3 @@
fn init() -> Result<(), ()> {
Ok(())
}
+106
View File
@@ -0,0 +1,106 @@
use super::{Task, TaskId};
use alloc::{collections::BTreeMap, sync::Arc, task::Wake};
use core::task::{Waker, Context, Poll};
use crossbeam_queue::ArrayQueue;
pub struct Executor {
tasks: BTreeMap<TaskId, Task>,
task_queue: Arc<ArrayQueue<TaskId>>,
waker_cache: BTreeMap<TaskId, Waker>,
}
impl Executor {
pub fn new() -> Self {
Executor {
tasks: BTreeMap::new(),
task_queue: Arc::new(ArrayQueue::new(100)),
waker_cache: BTreeMap::new(),
}
}
pub fn spawn(&mut self, task: Task) {
let task_id = task.id;
if self.tasks.insert(task.id, task).is_some() {
panic!("a task with this id has already been allocated");
}
self.task_queue.push(task_id).expect("task queue is full");
}
fn run_ready_tasks(&mut self) {
let Self {
tasks, task_queue, waker_cache
} = self;
while let Ok(task_id) = task_queue.pop() {
let task = match tasks.get_mut(&task_id) {
Some(task) => task,
None => continue,
};
let waker = waker_cache.entry(task_id)
.or_insert_with(|| TaskWaker::new(task_id, task_queue.clone()));
let mut context = Context::from_waker(waker);
match task.poll(&mut context) {
Poll::Ready(()) => {
tasks.remove(&task_id);
waker_cache.remove(&task_id);
}
Poll::Pending => {}
}
}
}
pub fn run(&mut self) -> ! {
loop {
self.run_ready_tasks();
self.sleep_if_idle();
}
}
fn sleep_if_idle(&self) {
use x86_64::instructions::interrupts::{self, enable_and_hlt};
interrupts::disable();
if self.task_queue.is_empty() {
enable_and_hlt();
} else {
interrupts::enable();
}
}
}
struct TaskWaker {
task_id: TaskId,
task_queue: Arc<ArrayQueue<TaskId>>,
}
impl TaskWaker {
fn new(task_id: TaskId, task_queue: Arc<ArrayQueue<TaskId>>) -> Waker {
Waker::from(Arc::new(TaskWaker {
task_id,
task_queue,
}))
}
fn wake_task(&self) {
self.task_queue.push(self.task_id).expect("task queue is full")
}
}
impl Wake for TaskWaker {
fn wake(self: Arc<Self>) {
self.wake_task();
}
fn wake_by_ref(self: &Arc<Self>) {
self.wake_task();
}
}
+161
View File
@@ -0,0 +1,161 @@
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::instructions::interrupts;
use conquer_once::spin::OnceCell;
use crossbeam_queue::ArrayQueue;
use crate::println;
use core::{pin::Pin, task::{Poll, Context}};
use futures_util::stream::Stream;
use futures_util::task::AtomicWaker;
use futures_util::stream::StreamExt;
use pc_keyboard::{layouts, DecodedKey, HandleControl, Keyboard, ScancodeSet1};
use crate::print;
use crate::kernel::render::RENDERER;
use alloc::{string::String};
static WAKER: AtomicWaker = AtomicWaker::new();
static SCANCODE_QUEUE: OnceCell<ArrayQueue<u8>> = OnceCell::uninit();
/*
pub async fn print_keypresses() {
let mut scancodes = ScanCodeStream::new();
let mut keyboard = Keyboard::new(layouts::Uk105Key, ScancodeSet1, HandleControl::Ignore);
while let Some(scancode) = scancodes.next().await {
if let Ok(Some(key_event)) = keyboard.add_byte(scancode) {
if let Some(key) = keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => {
let mut cmd = CMD.lock();
cmd.input(character).await;
}
DecodedKey::RawKey(key) => print!("{:?}", key),
}
}
}
}
}
*/
lazy_static! {
pub static ref KEYBOARD: Mutex<KeyboardHandler> = Mutex::new(KeyboardHandler::new());
}
pub struct KeyboardHandler {
scancodes: ScanCodeStream,
keyboard: Keyboard<layouts::Uk105Key, ScancodeSet1>,
}
impl KeyboardHandler {
pub fn new() -> KeyboardHandler {
KeyboardHandler {
scancodes: ScanCodeStream::new(),
keyboard: Keyboard::new(layouts::Uk105Key, ScancodeSet1, HandleControl::Ignore),
}
}
pub async fn get_keystroke_inner(&mut self) -> Option<char> {
loop {
if let Some(scancode) = self.scancodes.next().await {
if let Ok(Some(key_event)) = self.keyboard.add_byte(scancode) {
if let Some(key) = self.keyboard.process_keyevent(key_event) {
match key {
DecodedKey::Unicode(character) => {
if character == b'\x08' as char { // checks if the character is a backspace
interrupts::without_interrupts(|| {
RENDERER.lock().backspace(); // runs the backspace function of the vga buffer to remove the last character
});
return None;
} else {
return Some(character);
}
},
DecodedKey::RawKey(key) => { print!("{:?}", key) },
}
}
}
}
}
}
pub async fn get_keystroke(&mut self) -> char {
loop {
match self.get_keystroke_inner().await {
Some(c) => return c,
None => ()
}
}
}
pub async fn get_string(&mut self) -> String {
let mut val = String::new();
loop {
let character = match self.get_keystroke_inner().await {
Some(c) => { c },
None => { val.pop(); continue; },
};
print!("{}", character);
let (character, execute): (char, bool) = match character {
'\n' => (character, true),
_ => (character, false),
};
val.push(character);
if execute {
return val;
}
}
}
}
pub(crate) fn add_scancode(scancode: u8) {
if let Ok(queue) = SCANCODE_QUEUE.try_get() {
if let Err(_) = queue.push(scancode) {
println!("WARNING: queue is full - ignoring input");
} else {
WAKER.wake();
}
} else {
println!("WARNING: scancode queue has not been initialised");
}
}
pub struct ScanCodeStream {
_private: (),
}
impl ScanCodeStream {
pub fn new() -> Self {
SCANCODE_QUEUE.try_init_once(|| ArrayQueue::new(100))
.expect("ScanCodeStream::new has already been called once");
ScanCodeStream { _private: () }
}
}
impl Stream for ScanCodeStream {
type Item = u8;
fn poll_next(self: Pin<&mut Self>, ctx: &mut Context) -> Poll<Option<u8>> {
let queue = SCANCODE_QUEUE.try_get().expect("not initialised");
if let Ok(scancode) = queue.pop() {
return Poll::Ready(Some(scancode));
}
WAKER.register(&ctx.waker());
match queue.pop() {
Ok(scancode) => {
WAKER.take();
Poll::Ready(Some(scancode))
},
Err(crossbeam_queue::PopError) => Poll::Pending,
}
}
}
+36
View File
@@ -0,0 +1,36 @@
use core::{future::Future, pin::Pin};
use alloc::boxed::Box;
use core::task::{Context, Poll};
pub mod executor;
pub mod keyboard;
use core::sync::atomic::{AtomicU64, Ordering};
pub struct Task {
id: TaskId,
future: Pin<Box<dyn Future<Output = ()>>>,
}
impl Task {
pub fn new(future: impl Future<Output = ()> + 'static) -> Self {
Self {
id: TaskId::new(),
future: Box::pin(future),
}
}
fn poll(&mut self, context: &mut Context) -> Poll<()> {
self.future.as_mut().poll(context)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
struct TaskId(u64);
impl TaskId {
fn new() -> Self {
static NEXT_ID: AtomicU64 = AtomicU64::new(0);
TaskId(NEXT_ID.fetch_add(1, Ordering::Relaxed))
}
}
+15
View File
@@ -0,0 +1,15 @@
/*
pub mod thread_switch;
use x86_64::VirtAddr;
use crate::memory::{ThreadId, StackBounds};
#[derive(Debug)]
pub struct Thread {
id: ThreadId,
stack_pointer: Option<VirtAddr>,
stack_bounds: Option<StackBounds>,
}
*/
@@ -0,0 +1,3 @@
use core::arch::global_asm;
global_asm!(include_str!("thread_switch.s"));
@@ -0,0 +1,13 @@
asm_thread_switch:
pushfq
mov rax, rsp
mov rsp, rdi
mov rdi, rax
call add_paused_thread
popfq
ret
+9
View File
@@ -0,0 +1,9 @@
pub mod std;
pub mod kernel;
pub fn init() {
kernel::gdt::init();
kernel::interrupts::init_idt();
unsafe { kernel::interrupts::PICS.lock().initialize() };
x86_64::instructions::interrupts::enable();
}
+19
View File
@@ -0,0 +1,19 @@
use async_trait::async_trait;
use alloc::{string::String, vec::Vec, boxed::Box};
#[async_trait]
pub trait Application {
fn new() -> Self;
async fn run(&mut self, _: Vec<String>) -> Result<(), Error> {
Ok(())
}
}
#[derive(Debug)]
pub enum Error {
UnknownCommand(String),
CommandFailed(String),
EmptyCommand,
}
+210
View File
@@ -0,0 +1,210 @@
use crate::{
kernel::render::{RENDERER, BUFFER_WIDTH, BUFFER_HEIGHT, ColorCode},
kernel::tasks::keyboard::KEYBOARD,
};
use alloc::{boxed::Box, string::{String, ToString}, vec::Vec};
pub use crate::{print, println, serial_print, serial_println};
use lazy_static::lazy_static;
use spin::Mutex;
pub async fn stdin() -> String {
let string = KEYBOARD.lock().get_string().await;
string
}
pub async fn stdchar() -> char {
let chr = KEYBOARD.lock().get_keystroke().await;
chr
}
pub fn text_mode() {
RENDERER.lock().text_mode().unwrap();
}
pub fn sandbox_mode() {
RENDERER.lock().sandbox_mode().unwrap();
}
pub fn switch_mode() {
if RENDERER.lock().sandbox == true {
RENDERER.lock().text_mode().unwrap();
} else {
RENDERER.lock().sandbox_mode().unwrap();
}
}
pub fn clear() {
RENDERER.lock().clear();
}
pub type Frame = [ [ char; BUFFER_WIDTH ]; BUFFER_HEIGHT];
#[derive(Clone)]
pub struct Element {
frame: Vec<Vec<char>>,
dimensions: (u8, u8)
}
/// elements can be created using their from_str() method
/// you can then render the element to the current frame using the render() method
/// the position of the element by passing a tuple (x,y) to render()
///
/// nothing will appear on the screen until the frame is actually
impl Element {
pub fn from_str(elemstr: String) -> Self {
let mut element = Element { frame: Vec::<Vec<char>>::new(), dimensions: (0, 0) };
for line in elemstr.split("\n") {
let mut ln = Vec::<char>::new();
for col in line.chars() {
ln.push(col)
};
element.frame.push(ln);
}
for row in element.clone().frame {
let n = row.len();
if n > element.dimensions.0 as usize {
element.dimensions.0 = n as u8;
}
}
element
}
pub fn render(&mut self, pos: (u8, u8)) { // x,y
for (i, row) in self.frame.iter().enumerate() {
for (j, col) in row.iter().enumerate() {
println!("{} {} {}", i, j, col);
FRAMEGEN.lock().frame[i + pos.1 as usize][j + pos.0 as usize] = *col;
};
}
}
}
lazy_static! {
pub static ref FRAMEGEN: Mutex<FrameGen> = Mutex::new(FrameGen::new() );
}
#[derive(Clone, Copy)]
pub struct FrameGen {
frame: Frame,
}
impl FrameGen {
pub fn render_frame(&self) {
RENDERER.lock().render_frame(self.frame)
}
fn new() -> Self {
let mut frame: [[char; BUFFER_WIDTH]; BUFFER_HEIGHT] = [[' '; BUFFER_WIDTH]; BUFFER_HEIGHT];
for i in 0..BUFFER_WIDTH {
frame[0][i] = "┌──────────────────────────────────────────────────────────────────────────────┐".chars().collect::<Vec<char>>()[i];
frame[BUFFER_HEIGHT -1][i] = "└──────────────────────────────────────────────────────────────────────────────┘".chars().collect::<Vec<char>>()[i];
}
for j in 1..BUFFER_HEIGHT -1 {
for i in 0..BUFFER_WIDTH {
frame[j][i] = "│ │".chars().collect::<Vec<char>>()[i];
}
}
Self { frame: Frame::from(frame) }
}
pub fn get_frame(&self) -> &[ [ char; BUFFER_WIDTH ]; BUFFER_HEIGHT] {
&self.frame
}
}
impl core::fmt::Display for FrameGen {
fn fmt(&self, _: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
println!(" ");
for row in &self.frame {
println!("{}", row.iter().collect::<String>());
};
Ok(())
}
}
#[macro_export]
macro_rules! println_log {
() => ($crate::print_log!("/n"));
($($arg:tt)*) => ($crate::print_log!("{}\n", format_args!($($arg)*)));
}
#[macro_export]
macro_rules! print_log {
($($arg:tt)*) => ($crate::std::io::_log(format_args!($($arg)*)));
}
#[macro_export]
macro_rules! println {
() => ($crate::print!("/n"));
($($arg:tt)*) => ($crate::print!("{}\n", format_args!($($arg)*)));
}
#[macro_export]
macro_rules! print {
($($arg:tt)*) => ($crate::std::io::_print(format_args!($($arg)*)));
}
pub use crate::kernel::render::Color;
#[doc(hidden)]
pub fn _print(args: core::fmt::Arguments) {
use core::fmt::Write;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
let mut writer = RENDERER.lock();
writer.col_code = ColorCode::new(Color::White, Color::Black);
writer.write_fmt(args).unwrap();
//WRITER.lock().write_fmt(args).unwrap();
});
}
#[doc(hidden)]
pub fn _log(args: core::fmt::Arguments) {
use core::fmt::Write;
use x86_64::instructions::interrupts;
interrupts::without_interrupts(|| {
let mut writer = RENDERER.lock();
writer.col_code = ColorCode::new(Color::Yellow, Color::Black);
writer.write_fmt(args).unwrap();
//WRITER.lock().write_fmt(args).unwrap();
});
}
pub fn write(args: core::fmt::Arguments, cols: (Color, Color)) {
crate::kernel::render::write(args, cols);
}
pub fn mkfs() {
use crate::kernel::fs;
fs::mkfs();
println!("{:?}", *(fs::FILESYSTEM.lock()));
}
+13
View File
@@ -0,0 +1,13 @@
pub mod io;
pub mod random;
pub mod application;
pub mod tasks;
pub mod os;
// this is where the standard library for the operating system will be defined
// my aim is to completely separate this from the shell.
// these functions should all be asynchronous.
+16
View File
@@ -0,0 +1,16 @@
use lazy_static::lazy_static;
use spin::Mutex;
use alloc::{string::String};
lazy_static! {
pub static ref OS: Mutex<SysInfo> = Mutex::new(SysInfo {
os: String::from("CrystalOS Alpha"),
version: String::from("0.2.1"),
});
}
pub struct SysInfo {
pub os: String,
pub version: String,
}
+35
View File
@@ -0,0 +1,35 @@
use alloc::{boxed::Box, string::{String, ToString}, vec::Vec};
use rand::{Rng, SeedableRng, rngs::SmallRng, RngCore};
use spin::Mutex;
use lazy_static::lazy_static;
lazy_static! {
pub static ref RANDOM: Mutex<SmallRng> = Mutex::new(SmallRng::seed_from_u64(1));
}
pub struct Random;
impl Random {
pub fn int(lower: usize, upper: usize) -> usize {
loop {
let integer: u64 = RANDOM.lock().next_u64();
let mut integer: String = integer.to_string();
integer = "0".repeat(20 - integer.len()) + &integer;
let integer: usize = integer[1..upper.to_string().len() + 1].parse().unwrap();
if integer <= upper && integer >= lower {
return integer;
} else {
continue;
}
}
}
pub fn selection<T: Clone>(ls: Vec<T>) -> T {
let range = Random::int(0, ls.len() - 1);
ls[range as usize].clone()
}
}
+7
View File
@@ -0,0 +1,7 @@
pub use crate::kernel::tasks::{Task, executor::Executor};
pub fn stop() -> ! {
loop {
x86_64::instructions::hlt();
}
}
+582
View File
@@ -0,0 +1,582 @@
use core::fmt;
use alloc::{boxed::Box, string::String, vec::Vec};
use alloc::string::ToString;
use alloc::borrow::ToOwned;
use crate::{println, print, mknode, std};
use async_trait::async_trait;
use crate::std::application::{
Application,
Error as ShellError
};
struct Parser {
tokens: Vec<Token>,
idx: i32,
current: Token
}
struct Interpreter {}
impl Interpreter {
fn new() -> Result<Self, Error>{
return Ok(Self {})
}
fn visit(&mut self, node: Node) -> Result<Value, Error> {
match node {
Node::BinaryOperation(_) => return self.visit_binary_operation(node),
Node::UnaryOperation(_) => return self.visit_unary_operation(node),
Node::Number(_) => return self.visit_number(node),
Node::Operator(_) => return self.visit_operator(node),
}
}
fn visit_number(&mut self, node: Node) -> Result<Value, Error> {
if let Node::Number(x) = node {
Ok(Value::Number(x))
} else {
Err(Error::Other(String::from("value accessed was not an number")))
}
}
fn visit_operator(&mut self, node: Node) -> Result<Value, Error> {
if let Node::Operator(x) = node {
Ok(Value::Operator(x))
} else {
Err(Error::Other(String::from("value is not an operator")))
}
}
fn visit_binary_operation(&mut self, node: Node) -> Result<Value, Error> {
let left = match self.visit(self.get_node(node.clone(), "left")?.expect("returned none").to_owned())? {
Value::Number(x) => x,
_ => return Err(Error::Other(String::from("value is not a number"))),
};
let right = match self.visit(self.get_node(node.clone(), "right")?.expect("returned none").to_owned())? {
Value::Number(x) => x,
_ => return Err(Error::Other(String::from("value is not a number"))),
};
let operator = match self.visit(self.get_node(node.clone(), "operator")?.expect("returned none").to_owned())? {
Value::Operator(x) => x,
_ => return Err(Error::Other(String::from("value is not a binary operator"))),
};
match operator {
Operator::Add => {
return Ok(Value::Number(left + right))
},
Operator::Sub => {
return Ok(Value::Number(left - right))
},
Operator::Div => {
if right != 0.0 {
return Ok(Value::Number(left / right))
} else {
return Err(Error::LogicalError(String::from("division by 0")))
}
},
Operator::Mod => {
return Ok(Value::Number(left % right))
},
Operator::Qot => {
if right != 0.0 {
return Ok(Value::Number(
((left / right) as i64) as f64
))
} else {
return Err(Error::LogicalError(String::from("division by 0")))
}
},
Operator::Mul => {
return Ok(Value::Number(left * right))
},
Operator::Exp => {
return Ok(Value::Number({
let mut val = 1.0;
for _ in 0..(right as i64) {
val *= left
};
val
}))
}
}
}
fn visit_unary_operation(&mut self, node: Node) -> Result<Value, Error> {
let other: f64 = match self.visit(self.get_node(node.clone(), "other")?.expect("returned none").to_owned())? {
Value::Number(x) => x,
_ => return Err(Error::LogicalError("value is not a number".to_string()))
};
if let Node::UnaryOperation(x) = node {
match x.operator {
Node::Operator(Operator::Sub) => {
return Ok(Value::Number(other * -1f64));
},
_ => return Err(Error::LogicalError("value is not an operator".to_string()))
}
} else {
return Err(Error::LogicalError("node is not a binary operator".to_string()))
}
}
fn get_node(&self, node: Node, position: &str) -> Result<Option<Node>, Error> {
let result = match position {
"left" => {
if let Node::BinaryOperation(x) = node {
Some(x.left)
} else {
None
}
}
"right" => {
if let Node::BinaryOperation(x) = node {
Some(x.right)
} else {
None
}
}
"other" => {
if let Node::UnaryOperation(x) = node {
Some(x.other)
} else {
None
}
}
"operator" => {
if let Node::BinaryOperation(x) = node {
Some(x.operator)
} else {
None
}
},
_ => return Err(Error::Other(String::from("invalid param for get_Node")))
};
Ok(result)
}
}
impl Parser {
fn new(tokens: Vec<Token>) -> Result<Self, Error> {
let mut parser = Self { tokens, idx: -1, current: Token::Null };
parser.advance()?;
Ok(parser)
}
fn parse(&mut self) -> Result<Node, Error> {
let result = self.expr();
result
}
fn advance(&mut self) -> Result<Option<Token>, Error> {
self.idx += 1;
if self.idx < self.tokens.len() as i32 {
self.current = self.tokens[self.idx as usize].clone();
Ok(Some(self.current.clone()))
} else if self.idx == self.tokens.len() as i32 {
Ok(None)
} else {
Err(Error::Eof)
}
}
fn atom(&mut self) -> Result<Node, Error> {
let token = self.current.clone();
match token {
Token::Number(x) => {
self.advance()?;
return Ok(Node::Number(x))
},
Token::Bracket('(') => {
self.advance()?;
let expr = self.expr()?;
if let Token::Bracket(')') = self.current {
self.advance()?;
return Ok(expr)
} else {
return Err(Error::InvalidSyntax(0))
}
},
_ => return Err(Error::InvalidSyntax(0))
}
}
fn power(&mut self) -> Result<Node, Error> {
let mut left = self.atom()?;
while let Token::Operator(Operator::Exp) = self.current {
let current = self.current.clone();
self.advance()?;
let operator = mknode!(current).expect("mknode function returned None");
let right = self.factor()?;
left = Node::BinaryOperation(Box::new(BinaryOperation { left: left.clone(), operator, right }));
}
Ok(left)
}
fn factor(&mut self) -> Result<Node, Error> {
let token = self.current.clone();
match token {
Token::Operator(Operator::Add) | Token::Operator(Operator::Sub) => {
self.advance()?;
let operator = mknode!(token).expect("mknode returned none");
let other = self.factor().expect("holup");
return Ok(Node::UnaryOperation(Box::new(UnaryOperation { operator, other})))
},
_ => {
return Ok(self.power()?)
}
}
}
fn term(&mut self) -> Result<Node, Error> {
let mut left = self.factor()?;
while let Token::Operator(Operator::Div)
| Token::Operator(Operator::Mul)
| Token::Operator(Operator::Mod)
| Token::Operator(Operator::Qot) = self.current {
let current = self.current.clone();
self.advance()?;
let operator = mknode!(current).expect("mknode function returned None");
let right = self.factor()?;
left = Node::BinaryOperation(Box::new(BinaryOperation { left: left.clone(), operator, right }));
}
Ok(left)
}
fn expr(&mut self) -> Result<Node, Error> {
let mut left = self.term()?;
while let Token::Operator(Operator::Sub) | Token::Operator(Operator::Add) = self.current {
let current = self.current.clone();
self.advance()?;
let operator = mknode!(current).expect("mknode returned None");
let right = self.term()?;
left = Node::BinaryOperation(Box::new(BinaryOperation { left: left.clone(), operator, right }));
}
Ok(left)
}
}
#[macro_export]
macro_rules! mknode {
($token:expr) => {
match $token {
Token::Operator(x) => Some(Node::Operator(x)),
Token::Number(x) => Some(Node::Number(x)),
_ => None
}
};
}
pub struct Calculator {}
#[async_trait]
impl Application for Calculator {
fn new() -> Self {
Self {}
}
async fn run(&mut self, args: Vec<String>) -> Result<(), ShellError> {
if args.len() == 0 {
loop {
print!("enter equation > ");
let inp = std::io::stdin().await;
println!("{}", inp);
if inp == String::from("exit\n") {
return Ok(());
}
match calculate_inner(inp) {
Ok(_) => (),
Err(_) => { println!("your input must be a valid mathematical expression contaning only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]"); return Err(ShellError::CommandFailed(String::from("failed"))) },
};
}
} else {
match calculate_inner(args.into_iter().collect()) {
Ok(x) => x,
Err(_) => { println!("your input must be a valid mathematical expression contaning only numbers (including floats) and the operators: [ +, -, *, **, /, //, % ]"); return Err(ShellError::CommandFailed(String::from("failed"))) },
};
Ok(())
}
}
}
fn calculate_inner(mut equation: String) -> Result<f64, Error> {
equation.push('\n');
let mut neweq = equation.clone();
neweq.pop();
let tokens = tokenise(&equation)?;
let mut parser = Parser::new(tokens)?;
let ast = parser.parse()?;
let mut interpreter = Interpreter::new()?;
let result = interpreter.visit(ast)?;
let return_res = {
if let Value::Number(x) = result {
x
} else {
panic!("did not return a float!");
}
};
println!("\n\n
_____ _ _
/ ____| | | | |
| | _ __ _ _ ___| |_ __ _| |
| | | '__| | | / __| __/ _` | |
| |____| | | |_| \\__ \\ || (_| | |
\\_____|_| \\__, |___/\\__\\__,_|_|
_____ __/ |
/ ____||___/ |
| | __ _| | ___
| | / _` | |/ __|
| |___| (_| | | (__
\\_____\\__,_|_|\\___|
┌────────────────────────────────────────────┐
│ │
│ Expression -> [ {} ]
│ │
│ Calculated Solution -> [ {} ]
│ │
└────────────────────────────────────────────┘
", neweq, return_res);
Ok(return_res)
}
fn tokenise(equation: &str) -> Result<Vec<Token>, Error> {
let mut tokens = Vec::new();
let mut current_num = "".to_string();
let current_string: String = "".to_string();
'mainloop: for (x, character) in equation.chars().enumerate() {
match character {
'0'..='9' => current_num.push(character),
'.' => current_num.push(character),
_ => {
if current_num.len() != 0 {
tokens.push(Token::Number(current_num.parse::<f64>().unwrap()));
current_num = "".to_string();
} else if current_string.len() != 0 {
} match character {
'+' => tokens.push(Token::Operator(Operator::Add)),
'-' => tokens.push(Token::Operator(Operator::Sub)),
'%' => tokens.push(Token::Operator(Operator::Mod)),
'*' => {
if &equation.chars().nth(x-1).unwrap() == &'*' {
tokens.push(Token::Operator(Operator::Exp));
} else if &equation.chars().nth(x+1).unwrap() == &'*' {
()
} else {
tokens.push(Token::Operator(Operator::Mul));
}
},
'/' => {
if &equation.chars().nth(x-1).unwrap() == &'/' {
tokens.push(Token::Operator(Operator::Qot));
} else if &equation.chars().nth(x+1).unwrap() == &'/' {
()
} else {
tokens.push(Token::Operator(Operator::Div));
}
},
'(' | ')' | '[' | ']' | '{' | '}' | '<' | '>' => tokens.push(Token::Bracket(character)),
'\n' => break 'mainloop,
' ' => (),
_ => {
return Err(Error::InvalidCharacter(x))
},
}
}
}
}
Ok(tokens)
}
#[derive(Debug)]
enum Value {
Number(f64),
Operator(Operator)
}
#[derive(Debug, Clone, PartialEq)]
enum Token {
Number(f64),
Operator(Operator),
Bracket(char),
Null,
}
#[derive(Copy, Debug, Clone, PartialEq)]
enum Operator {
Add,
Sub,
Mul,
Div,
Qot,
Mod,
Exp,
}
#[derive(Debug)]
enum Error {
InvalidSyntax(usize),
InvalidCharacter(usize),
LogicalError(String),
Eof,
Other(String),
}
#[derive(Debug, Clone, PartialEq)]
enum Node {
Number(f64),
Operator(Operator),
BinaryOperation(Box<BinaryOperation>),
UnaryOperation(Box<UnaryOperation>)
}
#[derive(Debug, Clone, PartialEq)]
struct BinaryOperation {
left: Node,
operator: Node,
right: Node,
}
#[derive(Debug, Clone, PartialEq)]
struct UnaryOperation {
operator: Node,
other: Node,
}
impl fmt::Display for BinaryOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f, "(\n{} \n{} \n{}\n)
", self.left, self.operator, self.right
)
}
}
impl fmt::Display for UnaryOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f, "(\n{} \n{} \n)
", self.operator, self.other,
)
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Node::Number(x) => write!(f, "{}", x),
Node::Operator(x) => write!(f, "{}", x),
Node::BinaryOperation(x) => {
let inner = *x.clone();
write!(f, "{}", inner)
}
Node::UnaryOperation(x) => {
let inner = *x.clone();
write!(f, "{}", inner)
}
}
}
}
impl fmt::Display for Operator {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Operator::Add => write!(f, "+"),
Operator::Sub => write!(f, "-"),
Operator::Mul => write!(f, "*"),
Operator::Div => write!(f, "/"),
Operator::Mod => write!(f, "%"),
Operator::Qot => write!(f, "//"),
Operator::Exp => write!(f, "**"),
}
}
}
+53
View File
@@ -0,0 +1,53 @@
use super::entity::Enemy;
use alloc::vec::Vec;
pub enum Event {
PlayerKilled,
EntityKilled(Enemy),
}
pub enum Choice<A, B> {
A(A),
B(B),
}
impl core::fmt::Display for Event {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Event::PlayerKilled => write!(f, "Player killed!"),
Event::EntityKilled(x) => write!(f, "Entity killed! {}", x),
}
}
}
impl<A, B> core::fmt::Display for Choice<A, B> where
A: core::fmt::Display,
B: core::fmt::Display
{
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
Choice::A(a) => write!(f, "{}", a),
Choice::B(b) => write!(f, "{}", b),
}
}
}
pub fn eventcheck<A>(e: (A, Option<Vec<Event>>)) -> Choice<A, Event> {
match e.1 {
Some(events) => {
for event in events {
match event {
Event::PlayerKilled => {
return Choice::B(event)
}
Event::EntityKilled(entity) => {
return Choice::B(event)
}
}
}
},
None => (),
};
Choice::A(e.0)
}
+102
View File
@@ -0,0 +1,102 @@
use super::player::Player;
use super::engine::Event;
use alloc::{string::String, vec::Vec, vec};
use crate::std::random;
pub trait Entity {
fn attack_entity(&mut self, _: &mut EntityObject) -> (AttackResult, Option<Vec<Event>>) {
(AttackResult::Miss, None)
}
}
pub enum EntityObject<'a> {
Player(&'a mut Player),
Enemy(&'a mut Enemy),
}
#[derive(Debug, Clone, Copy)]
pub enum AttackResult {
Miss,
GlancingBlow(f64),
Hit(f64),
CriticalHit(f64),
FriendlyFire,
}
impl core::fmt::Display for AttackResult {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
match self {
AttackResult::Miss => write!(f, "Missed!"),
AttackResult::GlancingBlow(damage) => write!(f, "Glancing Blow: {}", damage),
AttackResult::Hit(damage) => write!(f, "Hit: {}", damage),
AttackResult::CriticalHit(damage) => write!(f, "Critical Hit: {}", damage),
AttackResult::FriendlyFire => write!(f, "Friendly Fire (no damage dealt)!"),
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct Enemy {
pub health_points: f64,
pub max_health_points: f64,
pub base_attack_damage: f64,
pub speed: f64,
}
impl Enemy {
pub fn new() -> Self {
Self {
health_points: 200.0,
max_health_points: 200.0,
base_attack_damage: 5.0,
speed: 100.0,
}
}
}
impl core::fmt::Display for Enemy {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Enemy: {}/{}", self.health_points, self.max_health_points)
}
}
impl Entity for Enemy {
fn attack_entity(&mut self, target: &mut EntityObject) -> (AttackResult, Option<Vec<Event>>) {
let mut entity = if let EntityObject::Player(player) = target {
player
} else {
return (AttackResult::FriendlyFire, None);
};
// combat implementation
let dmg: f64;
let r = random::Random::int(0, 125) as f64;
let rs = self.speed / entity.speed * 100 as f64;
let attack = if r < rs * 0.2 {
dmg = self.base_attack_damage * 1.5;
entity.health_points -= dmg;
AttackResult::CriticalHit(dmg)
} else if r < rs * 0.8 {
dmg = self.base_attack_damage;
entity.health_points -= dmg;
AttackResult::Hit(dmg)
} else if r < rs {
dmg = self.base_attack_damage * 0.5;
entity.health_points -= dmg;
AttackResult::GlancingBlow(dmg)
} else {
AttackResult::Miss
};
if entity.health_points <= 0.0 {
return (attack, Some(vec![Event::PlayerKilled]));
} else {
return (attack, None)
}
}
}
+129
View File
@@ -0,0 +1,129 @@
use async_trait::async_trait;
use rand::prelude::*;
use super::{
engine::{eventcheck, Choice, Event},
entity::{Entity, Enemy, EntityObject},
player::Player,
};
use alloc::{boxed::Box, string::{String, ToString}, vec::Vec, format, borrow::ToOwned};
use crate::{
std::application::{
Application,
Error,
},
std::{
io::{self, println, serial_println, FRAMEGEN, Element},
random,
},
};
pub struct GameLoop;
#[async_trait]
impl Application for GameLoop {
fn new() -> Self {
Self {}
}
async fn run(&mut self, _args: Vec<String>) -> Result<(), Error> {
let mut username: String = io::stdin().await;
username = username.trim().to_string();
let mut player = Player::new(username);
let mut enemy = Enemy::new();
for _ in 0..30 {
match (eventcheck(player.attack_entity(&mut EntityObject::Enemy(&mut enemy)))) {
Choice::A(result) => {
println!("{}", result);
},
Choice::B(event) => {
println!("{}", event);
match event {
Event::PlayerKilled => {
println!(" [!] {} was slain by Enemy\n\n[ You lost! ]", player.username);
break;
}
Event::EntityKilled(entity) => {
println!("\n [!] Enemy was slain by {}\n\n [ You won! ]", player.username);
break;
}
}
}
}
println!("{}", eventcheck(enemy.attack_entity(&mut EntityObject::Player(&mut player))));
println!("[{}\n[{}", player, enemy);
}
FRAMEGEN.lock().render_frame();
let string = String::from(format!(
"┌────────────────────────────┐
{}
{} / {}
└────────────────────────────┘"
, player.username, player.health_points, player.max_health_points));
let mut healthbar = Element::from_str(string);
healthbar.render((1, 1));
let new2 = String::from("[an element]");
let mut new = Element::from_str(new2);
new.render((10, 10));
new.render((10, 15));
new.render((5, 20));
new.render((34, 16));
FRAMEGEN.lock().render_frame();
let fr = FRAMEGEN.lock().get_frame().to_owned();
serial_println!("{}", {
let mut string = String::new();
for row in fr {
let mut r = String::new();
for col in row {
r.push(col);
}
string.push_str(&r);
string.push('\n')
};
string
});
loop {
println!("{}", io::stdchar().await)
}
Ok(())
}
}
fn random() -> u64 {
let mut r = random::Random::int(0, 125) as u64;
r
}
+19
View File
@@ -0,0 +1,19 @@
#[derive(Debug, Clone, Copy)]
pub struct Armour;
#[derive(Debug, Clone, Copy)]
pub struct Weapon;
#[derive(Debug, Clone, Copy)]
pub struct Charm;
#[derive(Debug, Clone, Copy)]
pub struct OtherItem;
#[derive(Debug, Clone, Copy)]
pub enum Item {
Armour(Armour),
Weapon(Weapon),
Charm(Charm),
Other(OtherItem),
}
+6
View File
@@ -0,0 +1,6 @@
pub mod player;
pub mod items;
pub mod entity;
pub mod engine;
pub mod renderer;
pub mod init;
+82
View File
@@ -0,0 +1,82 @@
use super::{
items::{Item, Armour},
entity::{Entity, EntityObject, AttackResult},
engine::Event,
};
use alloc::{string::String, vec::Vec, vec};
use crate::std::random;
pub struct Player {
pub username: String,
pub health_points: f64,
pub max_health_points: f64,
pub base_attack_damage: f64,
pub speed: f64,
pub inventory: [ Item ; 15 ],
pub equipped: [ Item ; 7 ], // helmet, chestplate, leggings, boots, mainhand, offhand, charm
}
impl Player {
pub fn new(username: String) -> Self {
Self {
username,
health_points: 100.0,
max_health_points: 100.0,
base_attack_damage: 10.0,
speed: 100.0,
inventory: [ Item::Armour(Armour {}); 15 ],
equipped: [ Item::Armour(Armour {}); 7 ],
}
}
}
impl core::fmt::Display for Player {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{}: {}/{}", self.username, self.health_points, self.max_health_points)
}
}
impl Entity for Player {
fn attack_entity(&mut self, target: &mut EntityObject) -> (AttackResult, Option<Vec<Event>>) {
let mut entity = if let EntityObject::Enemy(enemy) = target {
enemy
} else {
return (AttackResult::FriendlyFire, None);
};
// combat implementation
let dmg: f64;
let r = random::Random::int(0, 125) as f64;
let rs = self.speed / entity.speed * 100 as f64;
let attack = if r < rs * 0.2 {
dmg = self.base_attack_damage * 1.5;
entity.health_points -= dmg;
AttackResult::CriticalHit(dmg)
} else if r < rs * 0.8 {
dmg = self.base_attack_damage;
entity.health_points -= dmg;
AttackResult::Hit(dmg)
} else if r < rs {
dmg = self.base_attack_damage * 0.5;
entity.health_points -= dmg;
AttackResult::GlancingBlow(dmg)
} else {
AttackResult::Miss
};
if entity.health_points <= 0.0 {
return (attack, Some(vec![Event::EntityKilled(entity.clone())]));
} else {
return (attack, None)
}
}
}
+60
View File
@@ -0,0 +1,60 @@
use async_trait::async_trait;
use alloc::{boxed::Box, string::String, vec::Vec};
use crate::{
std::os::OS,
std::io::{Color, write},
println,
std::application::{
Application,
Error,
},
};
pub struct CrystalFetch {}
#[async_trait]
impl Application for CrystalFetch {
fn new() -> Self {
Self {}
}
async fn run(&mut self, _args: Vec<String>) -> Result<(), Error> {
let os = OS.lock().os.clone();
let version = OS.lock().version.clone();
write(format_args!("
────────────────────────────────────────────────────────
_____ _ _ ____ _____
/ ____| | | | |/ __ \\ / ____|
| | _ __ _ _ ___| |_ __ _| | | | | (___
| | | '__| | | / __| __/ _` | | | | |\\___ \\
| |____| | | |_| \\__ \\ || (_| | | |__| |____) |
\\_____|_| \\__, |___/\\__\\__,_|_|\\____/|_____/
__/ |
|___/
"), (Color::Magenta, Color::Black));
println!("
╔═══════════════════════════════
║ OS » {}
║ BUILD » {}
║ RAM » idk
║ Shell » CrystalSH
║ API » CrystalAPI
║ Pkgs » 4
║ Fetch » CrystalFetch
╚═══════════════════════════════
────────────────────────────────────────────────────────
", os, version);
Ok(())
}
}
+6
View File
@@ -0,0 +1,6 @@
pub mod calc;
pub mod rickroll;
pub mod crystalfetch;
pub mod tasks;
pub mod crystal_rpg;
pub mod shell;
+41
View File
@@ -0,0 +1,41 @@
use async_trait::async_trait;
use crate::std::application::{
Application,
Error,
};
use crate::{println};
use alloc::{string::String, boxed::Box, vec::Vec};
pub struct Rickroll {}
#[async_trait]
impl Application for Rickroll {
fn new() -> Self {
Self {}
}
async fn run(&mut self, _args: Vec<String>) -> Result<(), Error> {
println!("
_ _ _____
| \\ | | / ____|
| \\| | _____ _____ _ __ | | __ ___ _ __ _ __ __ _
| . ` |/ _ \\ \\ / / _ \\ '__| | | |_ |/ _ \\| '_ \\| '_ \\ / _` |
| |\\ | __/\\ V / __/ | | |__| | (_) | | | | | | | (_| |
|_| \\_|\\___| \\_/ \\___|_| \\_____|\\___/|_| |_|_| |_|\\__,_|
_______ __ __ ___ ___
/ ____(_) \\ \\ / / | | | |
| | __ ___ _____ \\ \\_/ /__ _ _ | | | |_ __
| | |_ | \\ \\ / / _ \\ \\ / _ \\| | | | | | | | '_ \\
| |__| | |\\ V / __/ | | (_) | |_| | | |__| | |_) |
\\_____|_| \\_/ \\___| |_|\\___/ \\__,_| \\____/| .__/
| |
|_|
");
Ok(())
}
}
+197
View File
@@ -0,0 +1,197 @@
use async_trait::async_trait;
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::instructions::interrupts;
use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use crate::{
print, println,
std::application::{Application, Error},
user::bin::*,
};
lazy_static! {
pub static ref CMD: Mutex<CommandHandler> = Mutex::new(CommandHandler::new());
}
/// boilerplate function
/// may provide other interfacing options later on idk.
pub async fn command_handler() {
eventloop().await;
}
/// this function starts the shell running, the function will loop repeatedly until the command to shutdown
/// TODO: implement shutdown command
pub async fn eventloop() {
println!("running!");
let mut fetch = crystalfetch::CrystalFetch::new();
let string = String::from(" ");
let mut vec: Vec<String> = Vec::new();
vec.push(string);
fetch.run(vec).await.unwrap();
CMD.lock().prompt();
loop {
let string = crate::std::io::stdin().await;
CMD.lock().current.push_str(&string);
match exec().await {
Ok(_) => {
();
}
Err(e) => {
handle_error(e);
}
};
CMD.lock().prompt();
}
}
fn handle_error(e: Error) {}
async fn exec() -> Result<(), Error> {
let mut current = CMD.lock().current.clone();
CMD.lock().history.history.push(current.clone());
current.pop();
CMD.lock().current = String::new();
let (cmd, args) = match CommandHandler::parse_args(current) {
Ok((cmd, args)) => (cmd, args),
Err(_) => {
return Err(Error::EmptyCommand);
}
};
match cmd.as_str() {
"calculate" | "calc" | "solve" => {
let mut cmd = calc::Calculator::new();
cmd.run(args).await?;
}
"rickroll" => {
let mut cmd = rickroll::Rickroll::new();
cmd.run(args).await?;
}
"crystalfetch" => {
let mut cmd = crystalfetch::CrystalFetch::new();
cmd.run(args).await?;
}
"tasks" => {
let mut cmd = tasks::Tasks::new();
cmd.run(args).await?;
}
"play" => {
let mut gameloop = crystal_rpg::init::GameLoop::new();
gameloop.run(args).await?;
}
// direct OS functions (not applications)
"echo" => {
println!(
"Crystal: '{}'",
args.into_iter()
.map(|mut s| {
s.push_str(" ");
s
})
.collect::<String>()
)
}
"clear" => {
interrupts::without_interrupts(|| {
crate::std::io::clear();
});
}
"print" => {
use crate::std::os::OS;
let x: String = OS.lock().version.clone();
println!("{}", x);
}
"switch" => {
crate::std::io::switch_mode();
}
"random" => {
use crate::std::random::Random;
let vec = Vec::from(["a", "b", "c", "d", "e", "f"]);
let sel = Random::selection(vec);
println!("{}", sel);
}
"filesystem" => {
use crate::std::io;
io::mkfs();
}
_ => {
return Err(Error::UnknownCommand(
"command not yet implemented".to_string(),
))
}
};
Ok(())
}
pub struct CommandHandler {
current: String,
history: CmdHistory,
}
impl CommandHandler {
pub fn new() -> Self {
let handler = Self {
current: String::new(),
history: CmdHistory {
history: Vec::new(),
},
};
handler
}
pub fn parse_args(command: String) -> Result<(String, Vec<String>), String> {
let temp = command.split(" ").collect::<Vec<&str>>();
let mut args: Vec<String> = Vec::new();
for arg in temp {
match arg {
"" => {}
x => args.push(x.to_string()),
}
}
let cmd: String;
if args.len() > 0 {
cmd = args[0].clone();
args.remove(0);
} else {
return Err("command was empty.".to_string());
};
Ok((cmd, args))
}
// this function is activated every time the user presses a key on the keyboard
// it accesses the queue of keys (a static ref in src/tasks/keyboard.rs)
// displays a text prompt for the user to type into.
// this is a separate function so that it can be developed as necessary later on
// TODO: coloured prompt
pub fn prompt(&self) {
print!("\n [ Crystal ] >> ");
}
// this function is run every time the enter key is pressed in the command line mode.
// it detects the command that is being run and then executes it, passing the arguments to it.
}
struct CmdHistory {
history: Vec<String>,
}
+42
View File
@@ -0,0 +1,42 @@
use async_trait::async_trait;
use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::instructions::interrupts;
use alloc::{
boxed::Box,
string::{String, ToString},
vec::Vec,
};
use crate::{
applications::*,
std::application::{Application, Error},
std::io::{print, println},
user::bin::*,
};
lazy_static! {
pub static ref CMD: Mutex<Cmd> = Mutex::new(Cmd::new());
}
struct Cmd {}
impl Cmd {
fn new() -> Self {
Self {}
}
}
// [ CRYSTAL SHELL ]
// the purpose of this module is to provide a basic unix shell like experience for the user
// to interact with the OS
// this is a rewrite of my original shell.
// this shell should support:
// - browsing the virtual filesystem
// - executing programs
// - basic arithmetic
// - chained execution ( multiple commands linked together) eg: '5 + 5 | echo' which calculates
// the result of 5 + 5 and then sends the result to an echo command which prints it to console
pub fn init_sh() -> Result<(), String> {
Ok(())
}
+168
View File
@@ -0,0 +1,168 @@
use alloc::{string::String, vec::Vec, boxed::Box};
use crate::std::application::{
Application,
Error
};
use crate::{print, println};
use lazy_static::lazy_static;
use spin::Mutex;
use async_trait::async_trait;
use alloc::{
string::ToString,
borrow::ToOwned,
};
use crate::std::random;
lazy_static! {
static ref TASKS: Mutex<TaskList> = Mutex::new(TaskList::new());
}
pub struct Tasks;
#[async_trait]
impl Application for Tasks {
fn new() -> Self { Self {} }
async fn run(&mut self, args: Vec<String>) -> Result<(), Error> {
if args[0].clone() == String::from("add") {
let content = args[1..].to_owned().into_iter().map(|mut s| {s.push_str(" "); s} ).collect::<String>();
self.add_task(content);
}
if args[0].clone() == String::from("remove") {
let idx = match args[1].to_owned().parse::<usize>() {
Ok(x) => x,
Err(_) => { return Err(Error::CommandFailed(String::from("number must be an integer"))) },
};
self.remove_task(idx);
}
if args[0].clone() == String::from("select") {
let arg2 = args[1].clone();
if arg2 == String::from("random") {
let len = TASKS.lock().tasks.len();
self.select_task(random::Random::int(0, len -1) as i32);
} else if arg2.parse::<u64>().is_ok() {
()
}
}
if args[0].clone() == String::from("priority") {
let idx = TASKS.lock().current;
if idx < 0 {
println!(
"-------------------------------------
no task currently set as priority
-------------------------------------\n"
);
return Ok(())
}
let task = TASKS.lock().tasks[idx as usize].clone();
let content = task.content.clone();
println!(
"-------------------------------------
PRIORITY TASK: {} : {}
-------------------------------------\n",
idx, content
)
}
if args[0].as_str() == "list" {
println!(
"-------------------------------------
Your TODO List:
-------------------------------------\n");
for task in TASKS.lock().tasks.iter() {
let idx = task.taskid;
let content = task.content.clone();
println!(" | Task -> {} \n | {}\n", idx, content);
}
println!("\n-------------------------------------");
}
Ok(())
}
}
impl Tasks {
fn add_task(&mut self, content: String) {
TASKS.lock().add(content);
}
fn remove_task(&self, idx: usize) {
TASKS.lock().remove(idx);
}
fn select_task(&self, idx: i32) {
TASKS.lock().select(idx);
}
}
pub struct TaskList {
current: i32,
tasks: Vec<Task>,
next_idx: usize,
}
impl TaskList {
pub fn new() -> Self {
Self {
current: -1,
tasks: Vec::new(),
next_idx: 1
}
}
pub fn next(&mut self) -> usize {
self.next_idx += 1;
self.next_idx -1
}
pub fn add(&mut self, content: String) -> Result<(), Error> {
let task = Task::new(self.next(), content);
let id = task.taskid.clone();
self.tasks.push(task);
Ok(())
}
pub fn remove(&mut self, id: usize) -> Result<(), Error> {
for (i, task) in self.tasks.clone().iter().enumerate() {
match task.taskid {
id => { self.tasks.remove(i); },
_ => { return Err(Error::CommandFailed(String::from("this task does not exist"))); },
}
};
Ok(())
}
pub fn select(&mut self, idx: i32) -> Result<(), Error> {
self.current = idx;
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct Task {
taskid: usize,
content: String,
}
impl Task {
fn new(id: usize, content: String) -> Self {
Self {
taskid: id,
content,
}
}
}
View File
+2
View File
@@ -0,0 +1,2 @@
pub mod lib;
pub mod bin;