ok
ok
This commit is contained in:
@@ -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"
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/target
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
[?25l[?7l[0m[36m[1m -`
|
||||||
|
.o+`
|
||||||
|
`ooo/
|
||||||
|
`+oooo:
|
||||||
|
`+oooooo:
|
||||||
|
-+oooooo+:
|
||||||
|
`/:-:++oooo+:
|
||||||
|
`/++++/+++++++:
|
||||||
|
`/++++++++++++++:
|
||||||
|
`/+++o[0m[36m[1moooooooo[0m[36m[1moooo/`
|
||||||
|
[0m[36m[1m [0m[36m[1m./[0m[36m[1mooosssso++osssssso[0m[36m[1m+`
|
||||||
|
[0m[36m[1m .oossssso-````/ossssss+`
|
||||||
|
-osssssso. :ssssssso.
|
||||||
|
:osssssss/ osssso+++.
|
||||||
|
/ossssssss/ +ssssooo/-
|
||||||
|
`/ossssso+/:- -:/+osssso+-
|
||||||
|
`+sso+:-` `.-/+oso:
|
||||||
|
`++:. `-/+/
|
||||||
|
.` `/[0m
|
||||||
|
[19A[9999999D[41C[0m[1m[36m[1mfantasypvp[0m@[36m[1march-x240[0m
|
||||||
|
[41C[0m--------------------[0m
|
||||||
|
[41C[0m[36m[1mOS[0m[0m:[0m Arch Linux x86_64[0m
|
||||||
|
[41C[0m[36m[1mHost[0m[0m:[0m 20AL007YUK ThinkPad X240[0m
|
||||||
|
[41C[0m[36m[1mKernel[0m[0m:[0m 6.1.6-zen1-2-zen[0m
|
||||||
|
[41C[0m[36m[1mUptime[0m[0m:[0m 19 days, 22 hours, 25 mins[0m
|
||||||
|
[41C[0m[36m[1mPackages[0m[0m:[0m 1064 (pacman)[0m
|
||||||
|
[41C[0m[36m[1mShell[0m[0m:[0m bash 5.1.16[0m
|
||||||
|
[41C[0m[36m[1mResolution[0m[0m:[0m 1366x768[0m
|
||||||
|
[41C[0m[36m[1mDE[0m[0m:[0m Plasma 5.26.5[0m
|
||||||
|
[41C[0m[36m[1mWM[0m[0m:[0m KWin[0m
|
||||||
|
[41C[0m[36m[1mWM Theme[0m[0m:[0m Bismuth[0m
|
||||||
|
[41C[0m[36m[1mTheme[0m[0m:[0m Breeze Light [Plasma], Materia-dark [GTK2/3][0m
|
||||||
|
[41C[0m[36m[1mIcons[0m[0m:[0m PlasmaXDark [Plasma], PlasmaXDark [GTK2/3][0m
|
||||||
|
[41C[0m[36m[1mTerminal[0m[0m:[0m vscode[0m
|
||||||
|
[41C[0m[36m[1mCPU[0m[0m:[0m Intel i5-4200U (4) @ 2.600GHz[0m
|
||||||
|
[41C[0m[36m[1mGPU[0m[0m:[0m Intel Haswell-ULT[0m
|
||||||
|
[41C[0m[36m[1mMemory[0m[0m:[0m 5401MiB / 7637MiB[0m
|
||||||
|
|
||||||
|
[41C[30m[40m [31m[41m [32m[42m [33m[43m [34m[44m [35m[45m [36m[46m [37m[47m [m
|
||||||
|
[41C[38;5;8m[48;5;8m [38;5;9m[48;5;9m [38;5;10m[48;5;10m [38;5;11m[48;5;11m [38;5;12m[48;5;12m [38;5;13m[48;5;13m [38;5;14m[48;5;14m [38;5;15m[48;5;15m [m
|
||||||
|
|
||||||
|
|
||||||
|
[?25h[?7h
|
||||||
Vendored
+20
@@ -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
@@ -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
@@ -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
|
||||||
@@ -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
|
||||||
|
```
|
||||||
@@ -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
@@ -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
@@ -0,0 +1,2 @@
|
|||||||
|
cargo bootimage --release
|
||||||
|
sudo dd if=target/x86_64-CrystalOS/release/bootimage-CrystalOS.bin of=/dev/sdc
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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(())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -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
@@ -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
@@ -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 {}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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;
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
@@ -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())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
*/
|
||||||
@@ -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;
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -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)*
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
fn init() -> Result<(), ()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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()));
|
||||||
|
}
|
||||||
@@ -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.
|
||||||
|
|
||||||
|
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
|
||||||
@@ -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()
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub use crate::kernel::tasks::{Task, executor::Executor};
|
||||||
|
|
||||||
|
pub fn stop() -> ! {
|
||||||
|
loop {
|
||||||
|
x86_64::instructions::hlt();
|
||||||
|
}
|
||||||
|
}
|
||||||
Executable
+582
@@ -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, "**"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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),
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pub mod player;
|
||||||
|
pub mod items;
|
||||||
|
pub mod entity;
|
||||||
|
pub mod engine;
|
||||||
|
pub mod renderer;
|
||||||
|
pub mod init;
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pub mod calc;
|
||||||
|
pub mod rickroll;
|
||||||
|
pub mod crystalfetch;
|
||||||
|
pub mod tasks;
|
||||||
|
pub mod crystal_rpg;
|
||||||
|
pub mod shell;
|
||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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>,
|
||||||
|
}
|
||||||
@@ -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(())
|
||||||
|
}
|
||||||
@@ -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,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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);
|
||||||
|
}
|
||||||
@@ -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();
|
||||||
|
}
|
||||||
@@ -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");
|
||||||
|
}
|
||||||
@@ -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"
|
||||||
|
}
|
||||||
|
|
||||||
Reference in New Issue
Block a user