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
+9
View File
@@ -0,0 +1,9 @@
[unstable]
build-std-features = ["compiler-builtins-mem"]
build-std = ["core", "compiler_builtins", "alloc"]
[build]
target = "x86_64-CrystalOS.json"
[target.'cfg(target_os = "none")']
runner = "bootimage runner"
+1
View File
@@ -0,0 +1 @@
/target
+43
View File
@@ -0,0 +1,43 @@
[?25l[?7l -`
.o+`
`ooo/
`+oooo:
`+oooooo:
-+oooooo+:
`/:-:++oooo+:
`/++++/+++++++:
`/++++++++++++++:
`/+++ooooooooooooo/`
 ./ooosssso++osssssso+`
 .oossssso-````/ossssss+`
-osssssso. :ssssssso.
:osssssss/ osssso+++.
/ossssssss/ +ssssooo/-
`/ossssso+/:- -:/+osssso+-
`+sso+:-` `.-/+oso:
`++:. `-/+/
.` `/
fantasypvp@arch-x240
--------------------
OS: Arch Linux x86_64
Host: 20AL007YUK ThinkPad X240
Kernel: 6.1.6-zen1-2-zen
Uptime: 19 days, 22 hours, 25 mins
Packages: 1064 (pacman)
Shell: bash 5.1.16
Resolution: 1366x768
DE: Plasma 5.26.5
WM: KWin
WM Theme: Bismuth
Theme: Breeze Light [Plasma], Materia-dark [GTK2/3]
Icons: PlasmaXDark [Plasma], PlasmaXDark [GTK2/3]
Terminal: vscode
CPU: Intel i5-4200U (4) @ 2.600GHz
GPU: Intel Haswell-ULT
Memory: 5401MiB / 7637MiB
        
        
[?25h[?7h
+20
View File
@@ -0,0 +1,20 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "cargo run",
"type": "shell",
"command": "~/.cargo/bin/cargo", // note: full path to the cargo
"args": [
"run",
// "--release",
// "--",
// "arg1"
],
"group": {
"kind": "build",
"isDefault": true
}
}
]
}
Generated
+323
View File
@@ -0,0 +1,323 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "CrystalOS"
version = "0.2.1"
dependencies = [
"ansi_rgb",
"async-trait",
"bootloader",
"conquer-once",
"crossbeam-queue",
"futures-util",
"lazy_static",
"linked_list_allocator",
"pc-keyboard",
"pic8259",
"rand",
"rgb",
"spin",
"uart_16550",
"volatile 0.2.7",
"x86_64",
]
[[package]]
name = "ansi_rgb"
version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a730095eb14ee842a0f1e68504b85c8d4a19b1ef2ac2a9b4debf0ed982f9b08a"
dependencies = [
"rgb",
]
[[package]]
name = "async-trait"
version = "0.1.62"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "689894c2db1ea643a50834b999abf1c110887402542955ff5451dab8f861f9ed"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "autocfg"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
[[package]]
name = "bit_field"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
[[package]]
name = "bootloader"
version = "0.9.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b6e02311b16c9819e7c72866d379cdd3026c3b7b25c1edf161f548f8e887e7ff"
[[package]]
name = "bytemuck"
version = "1.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c041d3eab048880cb0b86b256447da3f18859a163c3b8d8893f4e6368abe6393"
[[package]]
name = "cfg-if"
version = "0.1.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822"
[[package]]
name = "conquer-once"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c6d3a9775a69f6d1fe2cc888999b67ed30257d3da4d2af91984e722f2ec918a"
dependencies = [
"conquer-util",
]
[[package]]
name = "conquer-util"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e763eef8846b13b380f37dfecda401770b0ca4e56e95170237bd7c25c7db3582"
[[package]]
name = "crossbeam-queue"
version = "0.2.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "774ba60a54c213d409d5353bda12d49cd68d14e45036a285234c8d6f91f92570"
dependencies = [
"cfg-if",
"crossbeam-utils",
"maybe-uninit",
]
[[package]]
name = "crossbeam-utils"
version = "0.7.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3c7c73a2d1e9fc0886a08b93e98eb643461230d5f1925e4036204d5f2e261a8"
dependencies = [
"autocfg",
"cfg-if",
]
[[package]]
name = "futures-core"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
[[package]]
name = "futures-task"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
[[package]]
name = "futures-util"
version = "0.3.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
dependencies = [
"futures-core",
"futures-task",
"pin-project-lite",
"pin-utils",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
dependencies = [
"spin",
]
[[package]]
name = "linked_list_allocator"
version = "0.10.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e322f259d225fbae43a1b053b2dc6a5968a6bdf8b205f5de684dab485b95030e"
dependencies = [
"spinning_top",
]
[[package]]
name = "lock_api"
version = "0.4.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]]
name = "maybe-uninit"
version = "2.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "60302e4db3a61da70c0cb7991976248362f30319e88850c487b9b95bbf059e00"
[[package]]
name = "pc-keyboard"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c6f2d937e3b8d63449b01401e2bae4041bc9dd1129c2e3e0d239407cf6635ac"
[[package]]
name = "pic8259"
version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ec21f514e2e16e94649f1d041ca4a7069b512c037ac156360652a775e6229d"
dependencies = [
"x86_64",
]
[[package]]
name = "pin-project-lite"
version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e0a7ae3ac2f1173085d398531c705756c94a4c56843785df85a60c1a0afac116"
[[package]]
name = "pin-utils"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184"
[[package]]
name = "proc-macro2"
version = "1.0.50"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ef7d57beacfaf2d8aee5937dab7b7f28de3cb8b1828479bb5de2a7106f2bae2"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.23"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8856d8364d252a14d474036ea1358d63c9e6965c8e5c1885c18f73d70bff9c7b"
dependencies = [
"proc-macro2",
]
[[package]]
name = "rand"
version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"rand_core",
]
[[package]]
name = "rand_core"
version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "rgb"
version = "0.8.34"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3603b7d71ca82644f79b5a06d1220e9a58ede60bd32255f698cb1af8838b8db3"
dependencies = [
"bytemuck",
]
[[package]]
name = "rustversion"
version = "1.0.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5583e89e108996506031660fe09baa5011b9dd0341b89029313006d1fb508d70"
[[package]]
name = "scopeguard"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "spin"
version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e63cff320ae2c57904679ba7cb63280a3dc4613885beafb148ee7bf9aa9042d"
[[package]]
name = "spinning_top"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75adad84ee84b521fb2cca2d4fd0f1dab1d8d026bda3c5bea4ca63b5f9f9293c"
dependencies = [
"lock_api",
]
[[package]]
name = "syn"
version = "1.0.107"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1f4064b5b16e03ae50984a5a8ed5d4f8803e6bc1fd170a3cda91a1be4b18e3f5"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "uart_16550"
version = "0.2.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b074eb9300ad949edd74c529c0e8d451625af71bb948e6b65fe69f72dc1363d9"
dependencies = [
"bitflags",
"rustversion",
"x86_64",
]
[[package]]
name = "unicode-ident"
version = "1.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84a22b9f218b40614adcb3f4ff08b703773ad44fa9423e4e0d346d5db86e4ebc"
[[package]]
name = "volatile"
version = "0.2.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f6b06ad3ed06fef1713569d547cdbdb439eafed76341820fb0e0344f29a41945"
[[package]]
name = "volatile"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
[[package]]
name = "x86_64"
version = "0.14.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "100555a863c0092238c2e0e814c1096c1e5cf066a309c696a87e907b5f8c5d69"
dependencies = [
"bit_field",
"bitflags",
"rustversion",
"volatile 0.4.6",
]
+55
View File
@@ -0,0 +1,55 @@
[package]
name = "CrystalOS"
version = "0.2.1"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[package.metadata.bootimage]
test-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio", "-display", "none"]
test-success-exit-code = 33
test-timeout = 30
run-args = ["-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-serial", "stdio"]
[dependencies]
bootloader= { version = "0.9.23", features = ["map_physical_memory"] }
volatile="0.2.6"
spin = "0.5.2"
x86_64 = "0.14.2"
uart_16550 = "0.2.0"
pic8259 = "0.10.1"
pc-keyboard = "0.5.0"
linked_list_allocator = "0.10.2"
async-trait = "0.1.62"
# fatfs = { version = "0.3", default-features = false, features = ["core_io"] }
ansi_rgb = "0.2.0"
rgb = "0.8"
rand = { version = "0.8.5", default-features = false, features = ["small_rng"]}
[dependencies.lazy_static]
version = "1.0"
features = ["spin_no_std"]
[dependencies.crossbeam-queue]
version = "0.2.1"
default-features = false
features = ["alloc"]
[dependencies.conquer-once]
version = "0.3.2"
default-features = false
[dependencies.futures-util]
version = "0.3.4"
default-features = false
features = ["alloc"]
[[test]]
name = "should_panic"
harness = false
[[test]]
name = "stack_overflow"
harness = false
+206
View File
@@ -0,0 +1,206 @@
# CrystalOS
## Phase 1: The kernel.
the initial aim of this project was to follow a blog series on how to make a custom operating system found here:
https://os.phil-opp.com/
with the github repo for his project here:
https://github.com/phil-opp/blog_os
After reading and implementing the features from the final chapter, (async/await) I could find
no further instruction on how to continue with the project from there despite the author of the
series saying over a year previously that there would be more posts coming soon.
i guess im gonna just have to improvise :)
the blog got me through the memory management side of the process so i believe that I should
have a lot more breathing room to implement the features that i want. As of completing the
tutorial, i obviously still dont have access to a standard library, however i can at least
use Vectors and Strings now which are important types, as well as the fact that i have access
to async and heap allocation
### my aims going forwards:
- whenever i have the chance to work on this project, i want to try and implement a new utility
which could be useful or cool for anyone using the operating system.
- this could be anything from a cool neofetch style ascii fetcher (if you dont know what im
talking about, its just a cool ascii logo of the operating system that appears when you open
a terminal sometimes)
- improve the text rendering system to create a set of globally accessible functions and/or macros
in order to render the text in a more visually appealing way to the user (as the default yellow text
does look extremely ugly lmao)
- implement a basic text editor (this will be difficult)
- i would need a way to move the cursor around the screen and print text at that location
- this would mean rewriting the majority of the code for the vga buffer module to create a more
flexible system which allows for applications (modules / commands) to take more direct control of
the text rendering whenever they are active
# Implementation
## Phase 2: the shell.
### shell.rs
diverging from the original blog series, i have made some significant changes to keyboard.rs
- i have moved the source code that handles the keyboard input from keyboard.rs to shell.rs
- this means that instead of the operating system running a task on startup that continually
awaits a the next keystroke and works from there, the new layout works very differently
- firstly, i use a lazy_static creating a static called CMD which houses the shell itself
- this allows me to reference it from anywhere in the code and initialise it as soon as the program
runs
- this may be changed later as i could just make an init function in shell.rs if i needed to
- the shell contains a get_input function that awaits a keystroke from the user before continuing
- this is looped inside the main shell function and added to a buffer
- when the \n character is inputted, the buffer is copied to the command history vector and then cleared
- additionally the buffer is run through a match statement that will start any app that matches the command
or alias.
## Phase 3: CrystalAPI
### the basics:
the crystal api will essentially be a standard library for any programs that are run by the shell
- it provides basic functions such as waiting for a keystroke or string to be entered by the user
- it will eventually support coloured text output once ive had a chance to modify the code for the vga
buffer to support coloured text output through a public function.
### example:
here is a template that could be used to program using the crystal API
```rust
// ignore everything from this point up until the App struct
// --------------OS-INTERFACE-------------------------------------------------------------------------------------------------------
use std::io;
use std::io::Write; // ignore these, i have my own implementations that i will replace them with
struct CommandHandler {} // a struct used in my code (just ignore)
impl CommandHandler { // dont modify anything here
fn new() -> Self {
Self {}
}
fn input(&mut self) -> String { // this function will get replaced by the custom input function
let mut string = String::new();
io::stdin().read_line(&mut string).expect("error getting input");
string
}
}
fn main() { // the entry point to your code, it calls the code for the application
// will be removed when integrated into the os and replaced by the shell command
println!("");
print!("enter arguments to run command with > ");
io::stdout().flush();
let mut args = String::new();
io::stdin().read_line(&mut args).expect("failed to get input");
let mut app = App::new(CommandHandler::new());
app.run(args);
}
// --------------IMPLEMENTATION-----------------------------------------------------------------------------------------------------
struct App { // change name to whatever you want
handler: CommandHandler,
// any global variables for the application should be put here
// in the form: varname: VarType,
}
impl App { // name must be the same as the name of the struct
fn new(handler: CommandHandler) -> Self {
Self { // this should add any variables that are needed while the application is running
handler: handler,
// status: String, (example)
}
}
fn input(&mut self) -> String { // this function gives command line input
self.handler.input()
}
fn run(&mut self, args: String) -> Result<(), String> { /*
this represents your actual main function
write all the code for your program starting here
use println!() to print to the screen
use self.input() to get input from terminal
*/
println!("app running {}", args); // do stuff here
// example of how you can use the input function
println!("type something");
println!("input: {}", self.input());
// if you want to return an error, write: return Err("error message")
// the error message tells the operating system what went wrong with the code or user input.
// if you want to return ok, write: return Ok(()) (make sure to have the 2 sets of brackets)
Ok(())
}
}
```
## future plans (as of initial kernel build jan 2023):
eventually i want to try rewriting the majority of the code for the VGA buffer.
this is so that i can implement what i'll call a 'sandbox mode' for the screen.
this mode will support:
- moving the cursor around with arrow keys
- writing text at the cursor
- writing coloured text anywhere
- reading the entire output of the vga buffer or just a line into a string
eventually, this could theoretically lead to a library that was able to support things like a basic text editor
for writing out messages and the capability to theoretically program basic 2d games in an ascii art style
(something like space invaders, tetris, etc.)
## UPDATE: 21/02/23
- created a standard library of functions that any application can use
- implemented a random function to the standard library that can generate random numbers in a range
- added the print and println macros to be directly accessible from the standard library
- added global functions for getting input as a keystroke or from the command line
```rust
pub async fn stdin() -> String // returns the string inputted by the user
pub async fn stdchar() -> char // returns the next keystroke
pub fn crate::std::random::Random::int(min: usize, max: usize) -> usize // returns random integer in range (min <= x <= max)
pub fn crate::std::random::Random::selection<T: Clone>(list: Vec<T>) -> T // returns random element of vector argument
```
## UPDATE: 23/02/23
### changes
since the last update i did a few things.
- refactored the entire codebase, moving the standard library, kernel and applications to
separate folders in the source code in order to better organise them
- this should make my next goal much more seamless - this goal will be to further abstract the applications
from the kernel, essentially there will be a lib crate containing the kernel and standard library
in addition to a binary crate which actually has the bootloader / init system / applications etc
- wrote a basic ASCII rendering engine that can create and place 2d element anywhere on the screen
- the elements are placed using a coordinate system where the top left character of the element is
placed at that coordinate on the screen
```rust
pub struct crate::std::io::Element
```
+28
View File
@@ -0,0 +1,28 @@
# CrystalAPI
## Concept:
the Crystal API will be a set of functions and objects that make it relatively easy for anyone to develop an application
for the crystal operating system.
this means that anyone with the API documentation and source code can easily make a program that follows the
### current features:
as of 24/01/23 the API allows for the following:
- standard input (String)
- standard output with regular or coloured text
- detecting individual keystrokes
- access to some basic system information
### short term planned features:
- a greater set of OS information that the program can access
- this will also include a module that the OS and other programs can read from to change the active state of the OS
- this means that different applications can communicate with one another to share information.
- better support for coloured / formatted text
- applications should be able to control when the user can enter keystrokes
- this will have to be implemented in shell.rs with the text input so that the user cannot backspace text
that has been written by the system or the application.
- text sandbox mode
- this would essentially give the program more direct access to the vga buffer through some kind of wrapper function
/ class that would grant the ability to make a much more flexible interface.
- the main benefit of this would be the ability for a developer to make simple 2d games by using characters on the vga
buffer as pixels
+167
View File
@@ -0,0 +1,167 @@
# CrystalOS
## Phase 1: The kernel.
the initial aim of this project was to follow a blog series on how to make a custom operating system found here:
https://os.phil-opp.com/
with the github repo for his project here:
https://github.com/phil-opp/blog_os
After reading and implementing the features from the final chapter, (async/await) I could find
no further instruction on how to continue with the project from there despite the author of the
series saying over a year previously that there would be more posts coming soon.
i guess im gonna just have to improvise :)
the blog got me through the memory management side of the process so i believe that I should
have a lot more breathing room to implement the features that i want. As of completing the
tutorial, i obviously still dont have access to a standard library, however i can at least
use Vectors and Strings now which are important types, as well as the fact that i have access
to async and heap allocation
### my aims going forwards:
- whenever i have the chance to work on this project, i want to try and implement a new utility
which could be useful or cool for anyone using the operating system.
- this could be anything from a cool neofetch style ascii fetcher (if you dont know what im
talking about, its just a cool ascii logo of the operating system that appears when you open
a terminal sometimes)
- improve the text rendering system to create a set of globally accessible functions and/or macros
in order to render the text in a more visually appealing way to the user (as the default yellow text
does look extremely ugly lmao)
- implement a basic text editor (this will be difficult)
- i would need a way to move the cursor around the screen and print text at that location
- this would mean rewriting the majority of the code for the vga buffer module to create a more
flexible system which allows for applications (modules / commands) to take more direct control of
the text rendering whenever they are active
# Implementation
## Phase 2: the shell.
### shell.rs
diverging from the original blog series, i have made some significant changes to keyboard.rs
- i have moved the source code that handles the keyboard input from keyboard.rs to shell.rs
- this means that instead of the operating system running a task on startup that continually
awaits a the next keystroke and works from there, the new layout works very differently
- firstly, i use a lazy_static creating a static called CMD which houses the shell itself
- this allows me to reference it from anywhere in the code and initialise it as soon as the program
runs
- this may be changed later as i could just make an init function in shell.rs if i needed to
- the shell contains a get_input function that awaits a keystroke from the user before continuing
- this is looped inside the main shell function and added to a buffer
- when the \n character is inputted, the buffer is copied to the command history vector and then cleared
- additionally the buffer is run through a match statement that will start any app that matches the command
or alias.
## Phase 3: CrystalAPI
### the basics:
the crystal api will essentially be a standard library for any programs that are run by the shell
- it provides basic functions such as waiting for a keystroke or string to be entered by the user
- it will eventually support coloured text output once ive had a chance to modify the code for the vga
buffer to support coloured text output through a public function.
### example:
here is a template that could be used to program using the crystal API
```rust
// ignore everything from this point up until the App struct
// --------------OS-INTERFACE-------------------------------------------------------------------------------------------------------
use std::io;
use std::io::Write; // ignore these, i have my own implementations that i will replace them with
struct CommandHandler {} // a struct used in my code (just ignore)
impl CommandHandler { // dont modify anything here
fn new() -> Self {
Self {}
}
fn input(&mut self) -> String { // this function will get replaced by the custom input function
let mut string = String::new();
io::stdin().read_line(&mut string).expect("error getting input");
string
}
}
fn main() { // the entry point to your code, it calls the code for the application
// will be removed when integrated into the os and replaced by the shell command
println!("");
print!("enter arguments to run command with > ");
io::stdout().flush();
let mut args = String::new();
io::stdin().read_line(&mut args).expect("failed to get input");
let mut app = App::new(CommandHandler::new());
app.run(args);
}
// --------------IMPLEMENTATION-----------------------------------------------------------------------------------------------------
struct App { // change name to whatever you want
handler: CommandHandler,
// any global variables for the application should be put here
// in the form: varname: VarType,
}
impl App { // name must be the same as the name of the struct
fn new(handler: CommandHandler) -> Self {
Self { // this should add any variables that are needed while the application is running
handler: handler,
// status: String, (example)
}
}
fn input(&mut self) -> String { // this function gives command line input
self.handler.input()
}
fn run(&mut self, args: String) -> Result<(), String> { /*
this represents your actual main function
write all the code for your program starting here
use println!() to print to the screen
use self.input() to get input from terminal
*/
println!("app running {}", args); // do stuff here
// example of how you can use the input function
println!("type something");
println!("input: {}", self.input());
// if you want to return an error, write: return Err("error message")
// the error message tells the operating system what went wrong with the code or user input.
// if you want to return ok, write: return Ok(()) (make sure to have the 2 sets of brackets)
Ok(())
}
}
```
## future plans:
eventually i want to try rewriting the majority of the code for the VGA buffer.
this is so that i can implement what i'll call a 'sandbox mode' for the screen.
this mode will support:
- moving the cursor around with arrow keys
- writing text at the cursor
- writing coloured text anywhere
- reading the entire output of the vga buffer or just a line into a string
eventually, this could theoretically lead to a library that was able to support things like a basic text editor
for writing out messages and the capability to theoretically program basic 2d games in an ascii art style
(something like space invaders, tetris, etc.)
Executable
+2
View File
@@ -0,0 +1,2 @@
cargo bootimage --release
sudo dd if=target/x86_64-CrystalOS/release/bootimage-CrystalOS.bin of=/dev/sdc
+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;
@@ -0,0 +1,87 @@
// ignore everything from this point up until the App struct
// --------------OS-INTERFACE-------------------------------------------------------------------------------------------------------
use std::io;
use std::io::Write; // ignore these, i have my own implementations that i will replace them with
struct CommandHandler {} // a struct used in my code (just ignore)
impl CommandHandler { // dont modify anything here
fn new() -> Self {
Self {}
}
fn input(&mut self) -> String { // this function will get replaced by the custom input function
let mut string = String::new();
io::stdin().read_line(&mut string).expect("error getting input");
string
}
}
fn main() { // the entry point to your code, it calls the code for the application
// will be removed when integrated into the os and replaced by the shell command
println!("");
print!("enter arguments to run command with > ");
io::stdout().flush();
let mut args = String::new();
io::stdin().read_line(&mut args).expect("failed to get input");
let mut app = App::new(CommandHandler::new());
app.run(args);
}
// --------------IMPLEMENTATION-----------------------------------------------------------------------------------------------------
struct App { // change name to whatever you want
handler: CommandHandler,
// any global variables for the application should be put here
// in the form: varname: VarType,
}
impl App { // name must be the same as the name of the struct
fn new(handler: CommandHandler) -> Self {
Self { // this should add any variables that are needed while the application is running
handler: handler,
// status: String, (example)
}
}
fn input(&mut self) -> String { // this function gives command line input
self.handler.input()
}
fn keystroke(&mut self) -> String {
self.handler.input()
}
fn run(&mut self, args: String) -> Result<(), String> { /*
this represents your actual main function
write all the code for your program starting here
use println!() to print to the screen
use self.input() to get input from terminal
*/
println!("app running {}", args); // do stuff here
// example of how you can use the input function
println!("type something");
println!("input: {}", self.input());
// if you want to return an error, write: return Err("error message")
// the error message tells the operating system what went wrong with the code or user input.
// if you want to return ok, write: return Ok(()) (make sure to have the 2 sets of brackets)
Ok(())
}
}
+69
View File
@@ -0,0 +1,69 @@
#![no_std]
#![no_main]
#![feature(custom_test_frameworks)]
#![test_runner(CrystalOS::test_runner)]
#![reexport_test_harness_main = "test_main"]
extern crate alloc;
use bootloader::{entry_point, BootInfo};
use core::panic::PanicInfo;
use alloc::{ boxed::Box, vec::Vec };
entry_point!(main);
fn main(boot_info: &'static BootInfo) -> ! {
use CrystalOS::kernel::allocator;
use CrystalOS::kernel::memory::{self, BootInfoFrameAllocator};
use x86_64::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("failed to initialise heap");
test_main();
loop {}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
CrystalOS::test_panic_handler(info)
}
#[test_case]
fn box_allocation() {
let heap_1 = Box::new(69);
let heap_2 = Box::new(420);
assert_eq!(*heap_1, 69);
assert_eq!(*heap_2, 420);
}
#[test_case]
fn vec_allocation() {
let x = 1000;
let mut vector = Vec::new();
for i in 0..x {
vector.push(i);
}
assert_eq!(vector.iter().sum::<u64>(), (x-1) * x/2);
}
#[test_case]
fn reallocation() {
use CrystalOS::kernel::allocator::HEAP_SIZE;
for i in 0..HEAP_SIZE {
let x = Box::new(i);
assert_eq!(*x, i);
}
}
+29
View File
@@ -0,0 +1,29 @@
#![no_std]
#![no_main]
use core::panic::PanicInfo;
use CrystalOS::{QemuExitCode, exit, serial_println, serial_print};
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {
serial_println!("OK");
exit(QemuExitCode::Ok);
loop {}
}
#[no_mangle]
pub extern "C" fn _start() -> ! {
shouldpanic();
serial_println!("Err: Test did not panic");
exit(QemuExitCode::Err);
loop {}
}
fn shouldpanic() {
serial_print!("{}...\t", "should_panic::should_panic");
assert_eq!(1,2);
}
+57
View File
@@ -0,0 +1,57 @@
#![no_std]
#![no_main]
#![feature(abi_x86_interrupt)]
use core::panic::PanicInfo;
use lazy_static::lazy_static;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame};
use CrystalOS::{exit, QemuExitCode, serial_println, serial_print};
#[no_mangle]
pub extern "C" fn _start() -> ! {
serial_print!("stack_overflow::stack_overflow...\t");
CrystalOS::kernel::gdt::init();
init_test_idt();
stack_overflow();
panic!("stack overflow did not stop execution!");
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
CrystalOS::test_panic_handler(info);
}
#[allow(unconditional_recursion)]
fn stack_overflow() {
stack_overflow();
volatile::Volatile::new(0).read();
}
lazy_static! {
static ref TEST_IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
unsafe {
idt.double_fault.set_handler_fn(test_double_fault_handler).set_stack_index(CrystalOS::kernel::gdt::DOUBLE_FAULT_IST_INDEX);
}
idt
};
}
extern "x86-interrupt" fn test_double_fault_handler(
_stack_frame: InterruptStackFrame,
_error_code: u64,
) -> ! {
serial_println!("OK");
exit(QemuExitCode::Ok);
loop {}
}
pub fn init_test_idt() {
TEST_IDT.load();
}
+26
View File
@@ -0,0 +1,26 @@
#![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;
#[no_mangle]
pub extern "C" fn _start() -> ! {
test_main();
loop {}
}
#[panic_handler]
fn panic(info: &PanicInfo) -> ! {
CrystalOS::test_panic_handler(info)
}
#[test_case]
fn test_println() {
println!("testing println output");
}
+16
View File
@@ -0,0 +1,16 @@
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-i64:64-f80:128-n8:16:32:64-S128",
"arch": "x86_64",
"target-endian": "little",
"target-pointer-width": "64",
"target-c-int-width": "32",
"os": "none",
"executables": true,
"linker-flavor": "ld.lld",
"linker": "rust-lld",
"panic-strategy": "abort",
"disable-redzone": true,
"features": "-mmx,-sse,+soft-float"
}