Compare commits

...

120 commits

Author SHA1 Message Date
Able 9aa84a0f40 👍 checkpoint 2024-11-24 23:51:37 -06:00
Able bdb762c986 Magic function style guide 2024-11-24 23:50:46 -06:00
koniifer 0b57c2e9bb simple foldhash implementation 2024-11-24 20:58:14 +00:00
Able 8c88c0b5ae defer in alloc 2024-11-24 12:43:15 -06:00
Able 8b2b50e433 Style guide update, vscode config to make tab indent follow the style guide 2024-11-24 12:22:06 -06:00
Able d7e0d573e2 Style guide commit. Feel free to bikeshed about it. What should be added etcetc 2024-11-24 11:57:06 -06:00
Able 0ee8b7a4e4 janky error handling 2024-11-24 11:08:22 -06:00
Able 2261f36101 simply just dont inline 2024-11-24 11:04:12 -06:00
Able 6f82c92c30 sorta alloc support. Compiler error checkpoint 2024-11-24 11:03:06 -06:00
koniifer 5ea3ee9fe1 able sanity check 2024-11-24 16:39:24 +00:00
Able 241139f5af changes 2024-11-24 10:18:09 -06:00
Able 5c4056bc5c commit checkpoint 2024-11-24 10:00:24 -06:00
able b505622601 Merge pull request 'Ktest + Nix Flake' (#20) from FunkyEgg/ableos:master into master
Reviewed-on: https://git.ablecorp.us/AbleOS/ableos/pulls/20
2024-11-23 21:13:23 -06:00
funky 80ae717dd9 Introduced kernel testing system ktest
This commit also fixed a small issue with panic handler formatting
2024-11-24 11:21:39 +11:00
funky ab8b2309ae Nix Flake Support
nix-shell is broken on both my systems, so better be safe than sorry.
2024-11-24 11:12:24 +11:00
koniifer 7b7e02976d random change 2024-11-23 18:06:48 +00:00
koniifer c1c02ffefd silly string 2024-11-23 16:51:39 +00:00
able 8602ceb0d3 nightly revert 2024-11-23 04:59:32 -06:00
Able 6b673bc7e6 labels now have bg/fg color 2024-11-18 09:19:04 -06:00
koniifer 0af4d142a4 bad process spawning, compiler updates 2024-11-17 20:29:32 +00:00
koniifer 36f0d357cf arm cpuid fix 2024-11-17 10:52:13 +00:00
koniifer ccddf36050 fix 2024-11-17 10:11:14 +00:00
koniifer da5f1a7a19 compiler update 2024-11-16 20:42:16 +00:00
koniifer 2808bc9e7c various fixes, update to latest rust nightly 2024-11-16 16:34:45 +00:00
koniifer d958a103fa fix 2024-11-16 09:34:17 +00:00
koniifer ee82bc5705 minor changes 2024-11-16 09:30:53 +00:00
Able 3d53b641bf interrupt work 2024-11-16 02:51:58 -06:00
koniifer cea7f1fa5c use new register allocator by default 2024-11-15 19:46:36 +00:00
Able 614bef7ec5 ok now there is colors 2024-11-14 18:40:44 -06:00
Able b0f6aa53bd Mouse updates 2024-11-14 18:33:29 -06:00
Able d5f4ce18a3 push changes 2024-11-14 18:09:54 -06:00
koniifer 907c0d0dd4 multi-client support and demo 2024-11-14 21:28:21 +00:00
koniifer 95b4a921dc improve & optimise window decoration code 2024-11-14 16:43:23 +00:00
koniifer b6261d014d bad frame synchronisation implementation 2024-11-13 23:08:20 +00:00
Able 56ee0fe851 misc 2024-11-13 14:17:15 -06:00
koniifer b07bac1f1f broken 2024-11-13 17:03:31 +00:00
koniifer dc9273b3c1 various opts and keeping on top of compiler changes 2024-11-12 20:14:37 +00:00
Able e6d8f5c822 todo changes 2024-11-11 17:49:11 -06:00
koniifer 8ad7542b9c sunset work, stn changes, and fiddling with kernel logging clutter 2024-11-11 21:48:43 +00:00
Able e40a22fccd Commit 2024-11-11 01:23:14 -06:00
koniifer cf917927a5 incomplete sunset redesign 2024-11-11 01:03:01 +00:00
koniifer 97ceb12d6e double skill issue 2024-11-10 20:07:03 +00:00
koniifer bae58ade61 skill issue fix 2024-11-10 20:04:49 +00:00
koniifer b21c05e924 compiler bugfix 2024-11-10 19:37:37 +00:00
Able 2f5bc73665 The List 2024-11-10 13:13:38 -06:00
Able 0003464099 sunsetting sunset 2024-11-10 13:00:41 -06:00
koniifer 77a708d41e sunrising sunset 2024-11-10 18:57:48 +00:00
peony 3409f5051a Circle rendering support (janky) (#18)
circle rendering
Reviewed-on: https://git.ablecorp.us/AbleOS/ableos/pulls/18
Co-authored-by: peony <peony@email.com>
Co-committed-by: peony <peony@email.com>
2024-11-10 09:03:14 -06:00
Able 8c7b95277d reorg 2024-11-10 06:18:23 -06:00
koniifer 7da32b263f make the cursor more visible 2024-11-10 11:58:42 +00:00
Able 2439722c88 misc 2024-11-10 05:52:51 -06:00
koniifer 3333b1706f minor optimisations 2024-11-10 11:48:55 +00:00
Able 8808ed5bfe misc 2024-11-10 04:33:50 -06:00
koniifer 68d3236cc0 misc 2024-11-10 10:11:40 +00:00
Able eea23d967b mouse cursor functional 2024-11-10 02:36:37 -06:00
Able d2152537ad clean up 2024-11-09 22:34:24 -06:00
Able 7723799e76 changes or something idk 2024-11-09 20:44:10 -06:00
koniifer 8b04b275f3 text editor is actually a text editor 2024-11-09 01:54:33 +00:00
Able 1345f294b8 changes 2024-11-08 14:42:58 -06:00
Able 818bcb458f widgets 2024-11-08 10:29:06 -06:00
Able d2b5f09511 changes + lui examples 2024-11-08 10:16:24 -06:00
Jakub Doka 2676bd7b62
fixing error messages 2024-11-08 15:04:10 +01:00
koniifer 444fcdb0c4 more text editor work 2024-11-08 13:47:24 +00:00
koniifer adbf32d970 text editor work and misc changes 2024-11-08 01:48:17 +00:00
Able 7f01b0e0f8 👍 2024-11-07 09:57:50 -06:00
Able e8edee4ccc broken 👍 2024-11-07 09:57:39 -06:00
koniifer ba59233ce7 untested string.contains(haystack, needle) 2024-11-07 02:22:58 +00:00
Able f13c682171 refactor and widgeting magic 2024-11-06 20:11:36 -06:00
Able b6b097a872 widgeting work 2024-11-06 19:57:47 -06:00
koniifer 6a8d92d2c2 mostly qoi image support
refactor image loader for multiple filetypes
port some broken math stuff
make logger more readable
2024-11-07 01:55:29 +00:00
Able ab8522fbe1 widget UI 2024-11-06 11:43:40 -06:00
Able a49ac7bca0 Label Widget inside horizon 2024-11-06 09:36:17 -06:00
Able 12883ac926 Remove busy loop 2024-11-05 23:04:29 -06:00
Able 6a319c55b0 Proper Y direction 2024-11-05 23:01:15 -06:00
Able 879bbfa173 rework logic 2024-11-05 23:01:06 -06:00
Able 795f10986f Add partial support for sending mouse position updates 2024-11-05 22:58:52 -06:00
Able 85e63eb51c Add backspace 2024-11-05 22:16:08 -06:00
koniifer aafe047b51 forgor 2024-11-06 01:50:28 +00:00
koniifer 404ea22c46 rebase 2024-11-06 01:49:57 +00:00
koniifer 664334fd00 unicode stuff for psf rendering
make silly keyboard app
2024-11-06 01:47:33 +00:00
Able 086948d47a rename 2024-11-03 19:53:09 -06:00
Able b795215b62 enable horizon and ps2 driver 2024-11-03 19:38:40 -06:00
Able 16135ae536 mouse render 2024-11-03 18:37:39 -06:00
Able 941eed0ac9 patch 2024-11-03 18:34:48 -06:00
Able f4ad4b6856 mouse driver 2024-11-03 16:48:36 -06:00
koniifer 9e83707a28 misc changes 2024-11-03 22:31:53 +00:00
Able 6894ef2360 keycode nonsense 2024-11-02 11:09:31 -05:00
Able 69c95c35b5 fix serial argument 2024-11-01 17:37:47 -05:00
Able 80d363bc59 panic func in stn 2024-11-01 15:11:33 -05:00
Able 3c3c156eb0 misc changes + mouse driver work 2024-11-01 13:09:12 -05:00
koniifer eaace7d9c1 unoptimised text load & render 2024-10-26 09:23:28 +01:00
koniifer 5af5631755 minor changes 2024-10-25 16:37:38 +01:00
Able fac573837f Work 2024-10-25 09:17:37 -05:00
koniifer 6ff65eee41 kaboom 2024-10-23 21:49:40 +01:00
koniifer b35b430047 display_int radices 2024-10-20 13:31:44 +01:00
koniifer bcfaf89ed0 fix buffer mutating message 2024-10-20 12:36:34 +01:00
koniifer 2ba2dcb464 change logger message format so it doesn't mutate the string 2024-10-20 12:11:29 +01:00
koniifer 41d9c0b82a miscellaneous changes, free performance tweaks, and fiddle with enabling avx finally 2024-10-19 19:45:41 +01:00
koniifer a94332370a various render changes, implement sine & cosine for stn.math 2024-10-19 15:54:19 +01:00
koniifer 7caa47b9fb make surface example cooler 2024-10-17 15:31:42 +01:00
Able 34101d2e8c Windowing changes 2024-10-15 20:30:24 -05:00
Able 2bc13cd7d8 fix 👍 2024-10-15 18:06:37 -05:00
koniifer 086cc4aef0 patch minor logging bug 2024-10-16 00:03:23 +01:00
Able 84f86488c7 buggy code 2024-10-15 17:25:11 -05:00
Able ed7c8f20cb animal crossing new Horizon beginnings 2024-10-15 17:06:38 -05:00
koniifer 34cbd9a5e6 fiddling 2024-10-15 21:43:23 +01:00
koniifer 0ebb1f200e make surface example more interesting, optimise memory filling 2024-10-15 21:11:06 +01:00
Able fc06820332 fix visibility 2024-10-15 03:44:12 -05:00
koniifer 2a082d8283 rudimentary render surfaces 2024-10-15 01:24:29 +01:00
koniifer 1eee33ce8b fix icky pointer misalignment
move dt_api to stn for ease of use
do away with now redundant strobe example (colour example is basically strobe now)
2024-10-14 18:54:53 +01:00
Able 6fa1c829fd Horizon work 2024-10-13 22:34:33 -05:00
koniifer 820c3e459b optimisations 2024-10-14 01:31:23 +01:00
koniifer 3af28f1666 use able image 2024-10-13 23:41:17 +01:00
koniifer 96c2bd5cd5 bitmap image support & better mem opts
restructure render lib
add peony's render api additions and example
add image example
fix some dubious bugs i had made in unused code in mem_serve.rs
remove tetris stub (now in peony's fork)
update hblang
2024-10-13 23:38:43 +01:00
koniifer ea8eca1089 various stn & render-api changes 2024-10-12 21:39:09 +01:00
koniifer 7f4a040505 WIP help doc 2024-10-12 15:53:32 +01:00
koniifer 82f5cdbe21 add --noaccel flag to disable vm acceleration, update repbuild help 2024-10-12 15:02:55 +01:00
koniifer aac6d61dc6 stupid fix 2024-10-12 13:39:23 +01:00
koniifer 3df6f18c85 fix buffers
update hblang
auto cpu feature detection for repbuild
less verbose buffer api
add peony's tetris stub
2024-10-12 13:35:49 +01:00
koniifer 07ee8de9f1 push it to prod 😄
experimental avx stuff (enable it yourself, coward)
update hblang & fiddle with stuff
2024-09-30 21:45:57 +01:00
158 changed files with 4793 additions and 1972 deletions

View file

@ -1,3 +1,3 @@
[alias] [alias]
repbuild = "run --manifest-path ./repbuild/Cargo.toml -r --" repbuild = "run --manifest-path ./repbuild/Cargo.toml -- "
dev = "run --manifest-path ./dev/Cargo.toml -r --" dev = "run --manifest-path ./dev/Cargo.toml -r --"

View file

@ -1,4 +1,6 @@
{ {
"editor.insertSpaces": false,
"editor.detectIndentation": false,
"rust-analyzer.checkOnSave.allTargets": false, "rust-analyzer.checkOnSave.allTargets": false,
"rust-analyzer.showUnlinkedFileNotification": false, "rust-analyzer.showUnlinkedFileNotification": false,
"C_Cpp.errorSquiggles": "disabled" "C_Cpp.errorSquiggles": "disabled"

1207
Cargo.lock generated

File diff suppressed because it is too large Load diff

69
HELP.md Normal file
View file

@ -0,0 +1,69 @@
### What are the requirements?
- A machine with [Rustc Tier 1 platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-1-with-host-tools)
- Rustup
- QEMU (for executing)
- GIT CLI
### How do I run ableos?
- It is recommended to run ableos under QEMU. Here is how:
- Install QEMU
- Clone ableos
- Go to ableos directory
- Pull the limine submodule with `git submodule update --init`
- Run `cargo repbuild help`
### How can I contribute?
- [Contribute code](#how-do-i-contribute-code)
- [Run ableos on your machine](#how-do-i-run-ableos)
- Find bugs
- Create media showing ableos
### How do I contribute code?
- Start by forking ableos
- Write something that runs in the userspace, for example:
- System drivers
- Programs
- Libraries
- Patch bugs and improve code in the kernel
- Ensure that the code is OK to be maintained by asking in the [discord](https://discord.gg/t5Wt3K4YNA)
- When you have finished your changes, you can submit a pull request for review [here](https://git.ablecorp.us/ableos/ableos)
### repbuild and kernel compile, but QEMU isn't starting
- Ensure you have the `qemu-desktop-{arch}` for your OS and target architecture installed
- Try running again with `--noaccel` if you have QEMU already
### I have run using repbuild but it's slow
- Ensure release mode is enabled with the `-r` flag
- Remove the `--noaccel` flag if you can
- If both of these are already done, there may be a problem with thee VM, kernel, your program, or the hblang compiler
### Compiler is complaining about "reg id leaked"
- [Submit](#how-do-i-report-a-compiler-bug) an issue, reg id leaked is a bug
### My program isn't running
- Refer to [here](#i-have-run-using-repbuild-but-its-slow), it may be that your program is simply starting slowly
- Ensure that your program has a properly written meta.toml file
- Ensure that your program is enabled in [system_config.toml](sysdata/system_config.toml)
- Try running again with `--noaccel`, there is a known bug with some systems that prevents programs from starting.
### Kernel panic??? Huh???
- Kernel panics can be caused by improperly using memory (e.g, writing out of bounds)
- Kernel panics are most likely to be caused when accessing memory or using `@eca` for kernel ecalls
- [Report](#how-do-i-report-an-ableos-bug) a kernel panic
### I am running in release mode but I have no debug info
- Add the `-d` flag for debug info
### What is `@eca`? How do I use it?
- Eca is an ecall. They are similar to syscalls
- The `@eca` directive takes the following arguments:
- `@eca(ecall_number, reg_1, ..., reg_n)`
- The various ecalls have different arguments that are given by register values
- Most ecalls are wrapped by `stn`, for example, `random`, `buffer`, and `memory` all make use of ecalls
- All ecalls can be found [ecah.rs](kernel/src/holeybytes/ecah.rs)
### How do I report an ableos bug?
- Submit an issue [here](https://git.ablecorp.us/ableos/ableos/issues) or report it in the [discord](https://discord.gg/t5Wt3K4YNA)
### How do I report a compiler bug?
- Submit an issue [here](https://git.ablecorp.us/ableos/holey-bytes/issues) or report it in the [discord](https://discord.gg/t5Wt3K4YNA)

View file

@ -10,15 +10,7 @@ Donations can be made [here on Liberapay](https://liberapay.com/AbleTheAbove) or
<img src="https://img.shields.io/liberapay/patrons/AbleTheAbove.svg?logo=liberapay"> <img src="https://img.shields.io/liberapay/patrons/AbleTheAbove.svg?logo=liberapay">
# Compiling # Compiling
AbleOS should be able to be built on any platform which is supported by See [HELP.md](HELP.md)
[Rustc Tier 1 platform support](https://doc.rust-lang.org/nightly/rustc/platform-support.html#tier-1-with-host-tools).
For running AbleOS, `repbuild` uses QEMU.
## Steps
1. Ensure you have qemu installed
2. `git submodule update --init`
3. `cargo repbuild run`
# Developing # Developing
There is a new work in progress developer tool for hblang. There is a new work in progress developer tool for hblang. (see: dev folder)

77
STYLE_GUIDE.md Normal file
View file

@ -0,0 +1,77 @@
# Style Guide
This style guide has two modes that a guideline may be.
`strict` means that prs will be rejected if they do not follow the guideline.
`loose` means that a pr would be accepted but should later be fixed.
## Empty Functions | loose
Empty functions are typically a sign of an unfinished program or driver.
In cases where there is a clear reason to have an empty function it will be allowed.
For example FakeAlloc is only empty functions because it is a example of an the allocator api.
### Allowed
```rust
/// in example.hb
a := fn(): void {}
```
### Not Allowed
```rust
/// in fat32.hb
a := fn(): void {}
```
## Magic Functions | loose
'Magic functions' are what I am calling small helper functions that do one or two things.
### Example
```rust
a := null
magic_a := fn(){
a = 10
}
```
The exact policy I want to have here is a bit fuzzy. I think that functions like this are nice in certain situations and not in others.
Regardless of if you use them or not, put a comment above the function explaining rational.
## Magic Numbers | loose
The policy on magic numbers is make them const and have a comment above them. Typically linking to a source of information about the magic number.
This helps cut down on magic numbers while making acceptable names and atleast half assed documentation.
Constants are inlined anyways, so its the same thing in the binary.
```rust
// The standard vga port is mapped at 0xB8000
$VGA_PTR := 0xB8000
```
## Tabs Vs Spaces | strict
I prefer for hblang code to use hard tabs.
The rational behind this is that a tab is `1 Indent` which some developers might want to be various different sizes when displayed
Soft tabs do not allow this user/editor specific as soft tabs always become spaces.
Bottom line is this is an accessibility feature.
There are some samples below.
```
\t means hard tab
\n means new line
\0x20 means space
```
### Allowed
```rust
if x == y {\n
\tlog(z)\n
}\n
```
### Not Allowed
```rust
if x == y {\n
\0x20\0x20\0x20\0x20log(z)\n
}\n
```

View file

@ -138,8 +138,8 @@ fn build(name: String) {
} }
} }
pub fn build_program(name: String) {} pub fn build_program(_name: String) {}
pub fn build_library(name: String) {} pub fn build_library(_name: String) {}
fn help() { fn help() {
println!( println!(

96
flake.lock Normal file
View file

@ -0,0 +1,96 @@
{
"nodes": {
"flake-utils": {
"inputs": {
"systems": "systems"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1732014248,
"narHash": "sha256-y/MEyuJ5oBWrWAic/14LaIr/u5E0wRVzyYsouYY3W6w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "23e89b7da85c3640bbc2173fe04f4bd114342367",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixos-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"nixpkgs_2": {
"locked": {
"lastModified": 1728538411,
"narHash": "sha256-f0SBJz1eZ2yOuKUr5CA9BHULGXVSn6miBuUWdTyhUhU=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "b69de56fac8c2b6f8fd27f2eca01dcda8e0a4221",
"type": "github"
},
"original": {
"owner": "NixOS",
"ref": "nixpkgs-unstable",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs",
"rust-overlay": "rust-overlay"
}
},
"rust-overlay": {
"inputs": {
"nixpkgs": "nixpkgs_2"
},
"locked": {
"lastModified": 1732328983,
"narHash": "sha256-RHt12f/slrzDpSL7SSkydh8wUE4Nr4r23HlpWywed9E=",
"owner": "oxalica",
"repo": "rust-overlay",
"rev": "ed8aa5b64f7d36d9338eb1d0a3bb60cf52069a72",
"type": "github"
},
"original": {
"owner": "oxalica",
"repo": "rust-overlay",
"type": "github"
}
},
"systems": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
}
},
"root": "root",
"version": 7
}

30
flake.nix Normal file
View file

@ -0,0 +1,30 @@
{
description = "A devShell example";
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
rust-overlay.url = "github:oxalica/rust-overlay";
flake-utils.url = "github:numtide/flake-utils";
};
outputs = { self, nixpkgs, rust-overlay, flake-utils }:
flake-utils.lib.eachDefaultSystem (system:
let
overlays = [ (import rust-overlay) ];
pkgs = import nixpkgs {
inherit system overlays;
};
rustToolchain = pkgs.pkgsBuildHost.rust-bin.fromRustupToolchainFile ./rust-toolchain.toml;
in
with pkgs;
{
devShells.default = mkShell {
buildInputs = [
rustToolchain
qemu_full
];
};
}
);
}

View file

@ -4,6 +4,3 @@ build-std-features = ["compiler-builtins-mem"]
[build] [build]
target = "./targets/x86_64-ableos.json" target = "./targets/x86_64-ableos.json"
[target.'cfg(target_arch = "x86_64")']
rustflags = ["-C", "target-feature=+rdrand"]

View file

@ -3,10 +3,15 @@ edition = "2021"
name = "kernel" name = "kernel"
version = "0.2.0" version = "0.2.0"
[features]
ktest = []
[dependencies] [dependencies]
embedded-graphics = "0.8" # embedded-graphics = "0.8"
hbvm.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git" hbvm = { git = "https://git.ablecorp.us/AbleOS/holey-bytes.git", features = [
"nightly",
] }
ktest_macro = { path = "ktest_macro" }
log = "0.4" log = "0.4"
spin = "0.9" spin = "0.9"
slab = { version = "0.4", default-features = false } slab = { version = "0.4", default-features = false }
@ -14,21 +19,14 @@ uart_16550 = { version = "0.3", features = ["nightly"] }
xml.git = "https://git.ablecorp.us/ableos/ableos_userland" xml.git = "https://git.ablecorp.us/ableos/ableos_userland"
versioning.git = "https://git.ablecorp.us/ableos/ableos_userland" versioning.git = "https://git.ablecorp.us/ableos/ableos_userland"
# able_graphics_library.git = "https://git.ablecorp.us/ableos/ableos_userland" # able_graphics_library.git = "https://git.ablecorp.us/ableos/ableos_userland"
hashbrown = { version = "0.14", features = ["nightly"] } hashbrown = { version = "0.15", features = ["nightly"] }
limine = "0.1"
[dependencies.limine]
version = "0.1"
#git = "https://github.com/limine-bootloader/limine-rs"
[dependencies.crossbeam-queue] [dependencies.crossbeam-queue]
version = "0.3" version = "0.3"
default-features = false default-features = false
features = ["alloc", "nightly"] features = ["alloc", "nightly"]
# [dependencies.clparse]
# git = "https://git.ablecorp.us/ableos/ableos_userland"
# default-features = false
[dependencies.derive_more] [dependencies.derive_more]
version = "1" version = "1"
default-features = false default-features = false
@ -49,7 +47,7 @@ features = [
[target.'cfg(target_arch = "x86_64")'.dependencies] [target.'cfg(target_arch = "x86_64")'.dependencies]
x86_64 = "0.15" x86_64 = "0.15"
x2apic = "0.4" x2apic = "0.4"
virtio-drivers = "0.7" # virtio-drivers = "0.7"
[target.'cfg(target_arch = "riscv64")'.dependencies] [target.'cfg(target_arch = "riscv64")'.dependencies]
sbi = "0.2.0" sbi = "0.2.0"

View file

@ -0,0 +1,11 @@
[package]
edition = "2021"
name = "ktest_macro"
version = "0.1.0"
[lib]
proc-macro = true
[dependencies]
quote = "1.0.37"
syn = { version = "2.0.89", features = ["full"] }

View file

@ -0,0 +1,29 @@
extern crate proc_macro;
extern crate quote;
extern crate syn;
use {
proc_macro::TokenStream,
quote::quote,
syn::{parse_macro_input, ItemFn}
};
#[proc_macro_attribute]
pub fn ktest(_attr: TokenStream, item: TokenStream) -> TokenStream {
let input = parse_macro_input!(item as ItemFn);
let test_name = &input.sig.ident;
let static_var_name = syn::Ident::new(
&format!("__ktest_{}", test_name),
test_name.span(),
);
let out = quote! {
// #[cfg(feature = "ktest")]
#input
// #[cfg(feature = "ktest")]
#[unsafe(link_section = ".note.ktest")]
#[used]
pub static #static_var_name: fn() = #test_name;
};
TokenStream::from(out)
}

View file

@ -38,8 +38,16 @@ SECTIONS
.data : { .data : {
*(.data .data.*) *(.data .data.*)
*(.got .got.*)
} :data } :data
/* Add the .ktest section for test functions */
.note.ktest : {
__ktest_start = .; /* Mark the beginning of the section */
*(.note.ktest) /* Include all items in the .ktest section */
__ktest_end = .; /* Mark the end of the section */
}
.bss : { .bss : {
*(COMMON) *(COMMON)
*(.bss .bss.*) *(.bss .bss.*)

View file

@ -1,6 +1,5 @@
use { use {
crate::{alloc::string::ToString, device_tree::DeviceTree, kmain::DEVICE_TREE}, crate::{device_tree::DeviceTree, kmain::DEVICE_TREE},
alloc::string::String,
core::arch::asm, core::arch::asm,
xml::XMLElement, xml::XMLElement,
}; };
@ -28,7 +27,7 @@ fn collect_cpu_info(device_tree: &mut DeviceTree) {
cpus.push(cpu); cpus.push(cpu);
} }
fn cpu_id() -> (String, u64) { fn cpu_id<'a>() -> (&'a str, u64) {
let mut cpu_id: u64; let mut cpu_id: u64;
unsafe { unsafe {
asm!("mrs {cpu_id}, MIDR_EL1", asm!("mrs {cpu_id}, MIDR_EL1",
@ -39,11 +38,11 @@ fn cpu_id() -> (String, u64) {
let cpu_name = match cpu_id { let cpu_name = match cpu_id {
// the source of these two was a stackoverflow question // the source of these two was a stackoverflow question
// https://raspberrypi.stackexchange.com/questions/117175/how-do-i-read-the-cpuid-in-aarch64-asm // https://raspberrypi.stackexchange.com/questions/117175/how-do-i-read-the-cpuid-in-aarch64-asm
0x410FD034 => "Cortex-A53".to_string(), 0x410FD034 => "Cortex-A53",
0x410FD083 => "Cortex-A72".to_string(), 0x410FD083 => "Cortex-A72",
// the source of this one was checking the cpu id :thinking: // the source of this one was checking the cpu id :thinking:
0x410FD493 => "Neoverse N2".to_string(), 0x410FD493 => "Neoverse N2",
_ => "Unknown".to_string(), _ => "Unknown",
}; };
log::trace!("CPU Name: {cpu_name} - CPU ID: 0x{:X}", cpu_id); log::trace!("CPU Name: {cpu_name} - CPU ID: 0x{:X}", cpu_id);

View file

@ -3,7 +3,7 @@ pub static SERIAL_CONSOLE: Mutex<SerialConsole> = Mutex::new(SerialConsole {
uart: 0x09000000 as *mut u8, uart: 0x09000000 as *mut u8,
}); });
struct SerialConsole { pub struct SerialConsole {
uart: *mut u8, uart: *mut u8,
} }

View file

@ -14,3 +14,29 @@ arch_cond!(
riscv64: "riscv64", riscv64: "riscv64",
x86_64: "x86_64", x86_64: "x86_64",
); );
#[cfg(target_arch = "x86_64")]
use {crate::arch::interrupts::Interrupt, alloc::string::String};
#[cfg(target_arch = "x86_64")]
pub struct InterruptList {
list: HashMap<Interrupt, String>,
}
#[cfg(target_arch = "x86_64")]
use hashbrown::HashMap;
#[cfg(target_arch = "x86_64")]
impl InterruptList {
pub fn new() -> Self {
Self {
list: HashMap::new(),
}
}
}
#[cfg(target_arch = "x86_64")]
use spin::{Lazy, Mutex};
#[cfg(target_arch = "x86_64")]
pub static INTERRUPT_LIST: Lazy<Mutex<InterruptList>> = Lazy::new(|| {
let mut il = InterruptList::new();
use crate::alloc::string::ToString;
il.list.insert(Interrupt::Timer, "PS/2 Mouse".to_string());
Mutex::new(il)
});

View file

@ -11,7 +11,9 @@ static mut LAPIC: LocalApic = unsafe { MaybeUninit::zeroed().assume_init() };
static mut IDT: InterruptDescriptorTable = unsafe { MaybeUninit::zeroed().assume_init() }; static mut IDT: InterruptDescriptorTable = unsafe { MaybeUninit::zeroed().assume_init() };
#[repr(u8)] #[repr(u8)]
enum Interrupt { #[derive(Debug, Eq, Hash, PartialEq)]
pub enum Interrupt {
Timer = 32, Timer = 32,
ApicErr = u8::MAX - 1, ApicErr = u8::MAX - 1,
Spurious = u8::MAX, Spurious = u8::MAX,
@ -60,17 +62,49 @@ extern "x86-interrupt" fn page_fault(
} }
extern "x86-interrupt" fn timer(_isf: InterruptStackFrame) { extern "x86-interrupt" fn timer(_isf: InterruptStackFrame) {
// interrupt(Interrupt::Timer);
unsafe { unsafe {
LAPIC.end_of_interrupt(); LAPIC.end_of_interrupt();
} }
} }
extern "x86-interrupt" fn apic_err(_: InterruptStackFrame) { extern "x86-interrupt" fn apic_err(_: InterruptStackFrame) {
interrupt(Interrupt::ApicErr);
panic!("Internal APIC error"); panic!("Internal APIC error");
} }
extern "x86-interrupt" fn spurious(_: InterruptStackFrame) { extern "x86-interrupt" fn spurious(_: InterruptStackFrame) {
interrupt(Interrupt::Spurious);
unsafe { unsafe {
LAPIC.end_of_interrupt(); LAPIC.end_of_interrupt();
} }
} }
fn interrupt(interrupt_type: Interrupt) {
use crate::arch::INTERRUPT_LIST;
let il = INTERRUPT_LIST.lock();
let val = il.list.get(&interrupt_type).unwrap();
use crate::holeybytes::kernel_services::service_definition_service::sds_search_service;
let buffer = sds_search_service(val);
if buffer != 0 {
use {crate::kmain::IPC_BUFFERS, alloc::vec::Vec};
let mut buffs = IPC_BUFFERS.lock();
match buffs.get_mut(&buffer) {
Some(buff) => {
let mut msg_vec = Vec::new();
msg_vec.push(0xFF);
buff.push(msg_vec.to_vec());
log::debug!("Sent Message {:?} to Buffer({})", msg_vec, buffer);
}
None => {
log::error!("Access of non-existent buffer {}", buffer)
}
}
// log::info!("{}", buffer);
}
}

View file

@ -10,7 +10,7 @@ pub mod graphics;
pub(crate) mod interrupts; pub(crate) mod interrupts;
pub mod logging; pub mod logging;
pub mod pci; pub mod pci;
pub mod virtio; // pub mod virtio;
pub use {logging::log, memory::PAGE_SIZE}; pub use {logging::log, memory::PAGE_SIZE};
@ -30,9 +30,11 @@ const INITIAL_KERNEL_HEAP_SIZE: *const () = _initial_kernel_heap_size as _;
#[no_mangle] #[no_mangle]
#[naked] #[naked]
#[cfg(not(target_feature = "avx2"))]
unsafe extern "C" fn _kernel_start() -> ! { unsafe extern "C" fn _kernel_start() -> ! {
// Initialise SSE and jump to kernel entrypoint // Initialise SSE, then jump to kernel entrypoint
core::arch::asm!( core::arch::naked_asm!(
// Initialise SSE
"mov rax, cr0", "mov rax, cr0",
"and ax, 0xfffb", "and ax, 0xfffb",
"or ax, 0x2", "or ax, 0x2",
@ -40,16 +42,74 @@ unsafe extern "C" fn _kernel_start() -> ! {
"mov rax, cr4", "mov rax, cr4",
"or ax, 3 << 9", "or ax, 3 << 9",
"mov cr4, rax", "mov cr4, rax",
// Jump to the kernel entry point
"jmp {}", "jmp {}",
sym start, sym start,
options(noreturn),
) )
} }
#[no_mangle]
#[naked]
#[cfg(target_feature = "avx2")]
unsafe extern "C" fn _kernel_start() -> ! {
core::arch::naked_asm!(
// Enable protected mode and configure control registers
"mov rax, cr0",
"and ax, 0xFFFB", // Clear CR0.EM (bit 2) for coprocessor emulation
"or ax, 0x2", // Set CR0.MP (bit 1) for coprocessor monitoring
"mov cr0, rax",
"mov rax, cr4",
"or ax, (1 << 9) | (1 << 10)", // Set CR4.OSFXSR (bit 9) and CR4.OSXMMEXCPT (bit 10)
"mov cr4, rax",
// Enable OSXSAVE (required for AVX, AVX2, and XSAVE)
"mov rax, cr4",
"or eax, 1 << 18", // Set CR4.OSXSAVE (bit 18)
"mov cr4, rax",
// Enable AVX and AVX2 state saving
"xor rcx, rcx",
"xgetbv",
"or eax, 7", // Enable SSE, AVX, and AVX2 state saving
"xsetbv",
// Check for AVX and XSAVE support
"mov eax, 1",
"cpuid",
"and ecx, 0x18000000",
"cmp ecx, 0x18000000",
"jne {1}", // Jump if AVX/OSXSAVE is not supported
// Check for BMI2 and AVX2 support
"mov eax, 7",
"xor ecx, ecx",
"cpuid",
"and ebx, (1 << 8) | (1 << 5)", // Check BMI2 (bit 8) and AVX2 (bit 5)
"cmp ebx, (1 << 8) | (1 << 5)", // Compare to ensure both are supported
// Check for LZCNT and POPCNT support
"mov eax, 1",
"cpuid",
"and ecx, (1 << 5) | (1 << 23)", // Check LZCNT (bit 5) and POPCNT (bit 23)
"cmp ecx, (1 << 5) | (1 << 23)", // Compare to ensure both are supported
// Jump to the kernel entry point
"jmp {0}",
sym start,
sym oops,
)
}
unsafe extern "C" fn oops() -> ! {
panic!("your cpu is ancient >:(")
}
unsafe extern "C" fn start() -> ! { unsafe extern "C" fn start() -> ! {
logging::init(); logging::init();
crate::logger::init().expect("failed to set logger"); crate::logger::init().expect("failed to set logger");
log::info!("Initialising AKern {}", crate::VERSION); log::debug!("Initialising AKern {}", crate::VERSION);
static HDHM_REQ: HhdmRequest = HhdmRequest::new(0); static HDHM_REQ: HhdmRequest = HhdmRequest::new(0);
memory::init_pt(VirtAddr::new( memory::init_pt(VirtAddr::new(
@ -128,7 +188,7 @@ unsafe extern "C" fn start() -> ! {
// TODO: Add in rdseed and rdrand as sources for randomness // TODO: Add in rdseed and rdrand as sources for randomness
let _rand = xml::XMLElement::new("Random"); let _rand = xml::XMLElement::new("Random");
log::trace!("Getting boot modules"); log::debug!("Getting boot modules");
let bm = MOD_REQ.get_response().get(); let bm = MOD_REQ.get_response().get();
let mut bootmodules = alloc::vec::Vec::new(); let mut bootmodules = alloc::vec::Vec::new();
@ -166,7 +226,7 @@ unsafe extern "C" fn start() -> ! {
break; break;
} }
} }
log::info!("Boot module count: {:?}", bootmodules.len()); log::debug!("Boot module count: {:?}", bootmodules.len());
assert_eq!(bm.module_count, bootmodules.len() as u64); assert_eq!(bm.module_count, bootmodules.len() as u64);
} }
@ -189,6 +249,7 @@ unsafe extern "C" fn start() -> ! {
/// Spin loop /// Spin loop
pub fn spin_loop() -> ! { pub fn spin_loop() -> ! {
loop { loop {
core::hint::spin_loop();
x86_64::instructions::hlt() x86_64::instructions::hlt()
} }
} }

View file

@ -8,13 +8,12 @@ pub struct PciDeviceInfo {
pub full_class: PciFullClass, pub full_class: PciFullClass,
pub rev_id: u8, pub rev_id: u8,
} }
use crate::alloc::string::ToString;
/// Enumerate PCI devices and run initialisation routines on ones we support /// Enumerate PCI devices and run initialisation routines on ones we support
pub fn init(device_tree: &mut DeviceTree) { pub fn init(device_tree: &mut DeviceTree) {
device_tree device_tree
.devices .devices
.insert("Unidentified PCI".to_string(), alloc::vec![]); .insert("Unidentified PCI", alloc::vec![]);
let mut devices = alloc::vec![]; let mut devices = alloc::vec![];
for bus in 0..=255 { for bus in 0..=255 {
@ -48,7 +47,7 @@ pub fn init(device_tree: &mut DeviceTree) {
pci_info.set_attribute("device", device_info.device); pci_info.set_attribute("device", device_info.device);
pci_info.set_attribute("vendor", vendor); pci_info.set_attribute("vendor", vendor);
pci_info.set_attribute("bus", bus); pci_info.set_attribute("bus", bus);
pci_info.set_attribute("class", device_info.full_class.to_string()); pci_info.set_attribute("class", device_info.full_class);
dev.set_child(pci_info); dev.set_child(pci_info);
devices.push((dev_type, dev)); devices.push((dev_type, dev));
} }
@ -70,7 +69,7 @@ pub fn check_device(bus: u8, device: u8) -> Option<PciDeviceInfo> {
} }
let (reg2, addr) = unsafe { pci_config_read_2(bus, device, 0, 0x8) }; let (reg2, addr) = unsafe { pci_config_read_2(bus, device, 0, 0x8) };
log::info!("pci device-({}) addr {} is {}", device, addr, reg2); log::debug!("pci device-({}) addr {} is {}", device, addr, reg2);
let class = ((reg2 >> 16) & 0x0000_FFFF) as u16; let class = ((reg2 >> 16) & 0x0000_FFFF) as u16;
let pci_class = PciFullClass::from_u16(class); let pci_class = PciFullClass::from_u16(class);
let header_type = get_header_type(bus, device, 0); let header_type = get_header_type(bus, device, 0);

View file

@ -1,5 +1,5 @@
use { use {
core::{ptr::NonNull}, core::ptr::NonNull,
virtio_drivers::{BufferDirection, Hal, PhysAddr}, virtio_drivers::{BufferDirection, Hal, PhysAddr},
}; };

View file

@ -1,11 +1,6 @@
//! A tree of hardware devices //! A tree of hardware devices
use { use {alloc::vec::Vec, core::fmt, hashbrown::HashMap};
crate::alloc::string::ToString,
alloc::{string::String, vec::Vec},
core::fmt,
hashbrown::HashMap,
};
/// A device object. /// A device object.
/// TODO define device /// TODO define device
@ -14,11 +9,11 @@ pub type Device = xml::XMLElement;
/// A tree of devices /// A tree of devices
// TODO: alphabetize this list // TODO: alphabetize this list
#[derive(Debug)] #[derive(Debug)]
pub struct DeviceTree { pub struct DeviceTree<'a> {
/// The device tree /// The device tree
pub devices: HashMap<String, Vec<Device>>, pub devices: HashMap<&'a str, Vec<Device>>,
} }
impl DeviceTree { impl<'a> DeviceTree<'a> {
/// Build the device tree. Does not populate the device tree /// Build the device tree. Does not populate the device tree
pub fn new() -> Self { pub fn new() -> Self {
let mut dt = Self { let mut dt = Self {
@ -47,7 +42,7 @@ impl DeviceTree {
} }
} }
use crate::{device_tree, tab, utils::TAB}; use crate::{device_tree, tab, utils::TAB};
impl fmt::Display for DeviceTree { impl<'a> fmt::Display for DeviceTree<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
writeln!(f)?; writeln!(f)?;
for (device_type, devices) in &self.devices { for (device_type, devices) in &self.devices {

View file

@ -1,21 +1,40 @@
//! Environment call handling routines //! Environment call handling routines
use crate::holeybytes::kernel_services::{ use {alloc::boxed::Box, core::cell::LazyCell, hbvm::mem::Address};
use crate::{
holeybytes::{
kernel_services::{
block_read, dt_msg_handler::dt_msg_handler, logging_service::log_msg_handler, block_read, dt_msg_handler::dt_msg_handler, logging_service::log_msg_handler,
service_definition_service::sds_msg_handler, service_definition_service::sds_msg_handler,
},
ExecThread,
},
kmain::EXECUTOR,
task::Executor,
}; };
use { use {
super::Vm, super::Vm,
crate::{arch, ipc::buffer::IpcBuffer, kmain::IPC_BUFFERS}, crate::{arch, ipc::buffer::IpcBuffer, kmain::IPC_BUFFERS},
hbvm::value::Value,
log::{debug, error, info, trace}, log::{debug, error, info, trace},
}; };
#[cfg(target_arch = "x86_64")]
#[inline(always)]
unsafe fn x86_out<T: x86_64::instructions::port::PortWrite>(address: u16, value: T) {
x86_64::instructions::port::Port::new(address).write(value);
}
#[cfg(target_arch = "x86_64")]
#[inline(always)]
unsafe fn x86_in<T: x86_64::instructions::port::PortRead>(address: u16) -> T {
x86_64::instructions::port::Port::new(address).read()
}
#[inline(always)]
pub fn handler(vm: &mut Vm) { pub fn handler(vm: &mut Vm) {
let ecall_number = vm.registers[2].cast::<u64>(); let ecall_number = vm.registers[2].cast::<u64>();
// log::info!("eca called :pensive:");
// debug!("Ecall number {:?}", ecall_number);
//info!("Register dump: {:?}", vm.registers);
match ecall_number { match ecall_number {
0 => { 0 => {
@ -28,9 +47,9 @@ pub fn handler(vm: &mut Vm) {
1 => { 1 => {
// Make buffer // Make buffer
let bounded = match vm.registers[3].cast::<u64>() { let bounded = match vm.registers[3] {
0 => false, Value(0) => false,
1 => true, Value(1) => true,
_ => { _ => {
panic!("Bad"); panic!("Bad");
} }
@ -39,22 +58,19 @@ pub fn handler(vm: &mut Vm) {
let length = vm.registers[4].cast::<u64>(); let length = vm.registers[4].cast::<u64>();
let mut buffs = IPC_BUFFERS.lock(); let mut buffs = IPC_BUFFERS.lock();
let abc;
match bounded {
false => {
abc = IpcBuffer::new(false, 0);
}
true => {
abc = IpcBuffer::new(true, length);
}
};
let buff_id = arch::hardware_random_u64(); let buff_id = arch::hardware_random_u64();
buffs.insert(buff_id, abc); buffs.insert(
info!("Buffer ID: {}", buff_id); buff_id,
match bounded {
false => IpcBuffer::new(false, 0),
true => IpcBuffer::new(true, length),
},
);
vm.registers[1] = hbvm::value::Value(buff_id); vm.registers[1] = hbvm::value::Value(buff_id);
} }
2 => { 2 => {
log::error!("Oops, deleting buffers is not implemented.")
// Delete buffer // Delete buffer
} }
3 => { 3 => {
@ -63,7 +79,6 @@ pub fn handler(vm: &mut Vm) {
let mem_addr = vm.registers[4].cast::<u64>(); let mem_addr = vm.registers[4].cast::<u64>();
let length = vm.registers[5].cast::<u64>() as usize; let length = vm.registers[5].cast::<u64>() as usize;
trace!("IPC address: {:?}", mem_addr); trace!("IPC address: {:?}", mem_addr);
use alloc::vec::Vec;
match buffer_id { match buffer_id {
0 => match sds_msg_handler(vm, mem_addr, length) { 0 => match sds_msg_handler(vm, mem_addr, length) {
@ -81,25 +96,15 @@ pub fn handler(vm: &mut Vm) {
Err(_) => {} Err(_) => {}
} }
} }
#[cfg(not(target_arch = "x86_64"))]
3 => info!("TODO: implement whatever buffer 3 does for no x86_64"),
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
3 => { 3 => {
unsafe fn x86_out<T: x86_64::instructions::port::PortWrite>(
address: u16,
value: T,
) {
x86_64::instructions::port::Port::new(address).write(value);
}
unsafe fn x86_in<T: x86_64::instructions::port::PortRead>(address: u16) -> T {
x86_64::instructions::port::Port::new(address).read()
}
let msg_vec = block_read(mem_addr, length); let msg_vec = block_read(mem_addr, length);
let msg_type = msg_vec[0]; let msg_type = msg_vec[0];
match msg_type { match msg_type {
0 => unsafe { 0 => unsafe {
let size = msg_vec[1]; let size = msg_vec[1];
let addr = u16::from_le_bytes(msg_vec[2..4].try_into().unwrap()); let addr =
u16::from_le_bytes(msg_vec[2..4].try_into().unwrap_unchecked());
let value = match size { let value = match size {
0 => x86_in::<u8>(addr) as u64, 0 => x86_in::<u8>(addr) as u64,
1 => x86_in::<u16>(addr) as u64, 1 => x86_in::<u16>(addr) as u64,
@ -111,7 +116,8 @@ pub fn handler(vm: &mut Vm) {
}, },
1 => unsafe { 1 => unsafe {
let size = msg_vec[1]; let size = msg_vec[1];
let addr = u16::from_le_bytes(msg_vec[2..4].try_into().unwrap()); let addr =
u16::from_le_bytes(msg_vec[2..4].try_into().unwrap_unchecked());
// info!("Setting address {}", addr); // info!("Setting address {}", addr);
match size { match size {
@ -134,68 +140,95 @@ pub fn handler(vm: &mut Vm) {
3 => unimplemented!("TODO: implement whatever buffer 3 does for no x86_64"), 3 => unimplemented!("TODO: implement whatever buffer 3 does for no x86_64"),
// source of rng // source of rng
4 => { 4 => {
// limit to last 32 bits let block = block_read(mem_addr, length);
vm.registers[1] = block.chunks_mut(8.min(length)).for_each(|chunk| {
hbvm::value::Value(crate::arch::hardware_random_u64() & 0xFFFFFFFF); chunk.clone_from_slice(
&crate::arch::hardware_random_u64().to_le_bytes()[..chunk.len()],
);
});
vm.registers[1] = hbvm::value::Value(mem_addr);
} }
5 => match dt_msg_handler(vm, mem_addr, length) { 5 => match dt_msg_handler(vm, mem_addr, length) {
Ok(()) => {} Ok(()) => {}
Err(_) => log::error!("Improper dt query"), Err(_) => log::error!("Improper dt query"),
}, },
6 => unsafe {
let program = block_read(mem_addr, length);
// decode AbleOS Executable format
let header = &program[0..46];
let magic_slice = &header[0..3];
if magic_slice != [0x15, 0x91, 0xD2] {
log::error!("Invalid magic number at the start of executable.");
return;
}
let executable_format_version =
u32::from_le_bytes(header[3..7].try_into().unwrap());
let offset = if executable_format_version == 0 {
47
} else {
error!("Invalid executable format.");
return;
};
let code_length = u64::from_le_bytes(header[7..15].try_into().unwrap());
let data_length = u64::from_le_bytes(header[15..23].try_into().unwrap());
let end = (code_length + data_length) as usize;
log::debug!("{code_length} + {data_length} = {end}");
let thr = ExecThread::new(&program[offset..end], Address::new(0));
vm.registers[1] = Value(
LazyCell::<Executor>::get_mut(&mut EXECUTOR)
.unwrap()
.spawn(Box::pin(async move {
if let Err(e) = thr.await {
log::error!("{e:?}");
}
})) as u64,
);
log::debug!("spawned a process");
},
buffer_id => { buffer_id => {
let mut buffs = IPC_BUFFERS.lock(); let mut buffs = IPC_BUFFERS.lock();
match buffs.get_mut(&buffer_id) { match buffs.get_mut(&buffer_id) {
Some(buff) => { Some(buff) => {
let mut msg_vec = Vec::with_capacity(length); let msg_vec = block_read(mem_addr, length);
buff.push(msg_vec.to_vec());
for x in 0..(length as isize) { debug!("Sent Message {:?} to Buffer({})", msg_vec, buffer_id);
let xyz = mem_addr as *const u8;
let value = unsafe { xyz.offset(x).read() };
msg_vec.push(value);
}
debug!(
"Message {:?} has been sent to Buffer({})",
msg_vec, buffer_id
);
buff.push(msg_vec);
} }
None => { None => {
log::error!("Access of non-existent buffer {}", buffer_id) log::error!("Access of non-existent buffer {}", buffer_id)
} }
} }
drop(buffs);
} }
} }
} }
4 => { 4 => {
let buffer_id = vm.registers[3].cast::<u64>(); let buffer_id = vm.registers[3].cast::<u64>();
let mut map_ptr = vm.registers[4].cast::<u64>(); let map_ptr = vm.registers[4].cast::<u64>();
let max_length = vm.registers[5].cast::<u64>(); let max_length = vm.registers[5].cast::<u64>();
let mut buffs = IPC_BUFFERS.lock(); let mut buffs = IPC_BUFFERS.lock();
let buff: &mut IpcBuffer; let buff: &mut IpcBuffer = match buffs.get_mut(&buffer_id) {
Some(buff) => buff,
None => panic!(
"Failed to get buffer: id={buffer_id}, ptr={map_ptr}, length={max_length}"
),
};
if buffs.get_mut(&buffer_id).is_some() { let msg = match buff.pop() {
buff = buffs.get_mut(&buffer_id).unwrap(); Ok(msg) => msg,
} else { Err(_) => return,
// info!("AHHH"); };
vm.registers[1] = hbvm::value::Value(0); if msg.len() > unsafe { max_length.try_into().unwrap_unchecked() } {
return;
}
let pop = buff.pop();
if pop.is_err() {
return;
}
let msg = pop.unwrap();
if msg.len() > max_length.try_into().unwrap() {
info!("{}", max_length); info!("{}", max_length);
error!("Message is too long to map in."); error!("Message is too long to map in.");
} else { } else {
unsafe { unsafe {
let ptr: *mut u64 = &mut map_ptr; let ptr = map_ptr as *mut u8;
for (index, byte) in msg.iter().enumerate() { ptr.copy_from_nonoverlapping(msg.as_ptr(), msg.len());
ptr.offset(index.try_into().unwrap()).write_bytes(*byte, 1);
}
} }
debug!("Recieve {:?} from Buffer({})", msg, buffer_id); debug!("Recieve {:?} from Buffer({})", msg, buffer_id);
@ -205,13 +238,7 @@ pub fn handler(vm: &mut Vm) {
#[cfg(target_arch = "x86_64")] #[cfg(target_arch = "x86_64")]
{ {
let r2 = vm.registers[2].cast::<u64>(); let r2 = vm.registers[2].cast::<u64>();
unsafe fn x86_in(address: u16) -> u32 { let x = hbvm::value::Value(unsafe { x86_in::<u8>(r2 as u16) } as u64);
x86_64::instructions::port::Port::new(address).read()
}
unsafe fn x86_out(address: u16, value: u32) {
x86_64::instructions::port::Port::new(address).write(value);
}
let x = hbvm::value::Value(unsafe { x86_in(r2 as u16) } as u64);
// info!("Read {:?} from Port {:?}", x, r2); // info!("Read {:?} from Port {:?}", x, r2);
vm.registers[3] = x vm.registers[3] = x
} }
@ -227,15 +254,3 @@ pub enum LogError {
NoMessages, NoMessages,
InvalidLogFormat, InvalidLogFormat,
} }
// use {alloc::vec, log::Record};
// fn memory_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), LogError> {
// let mut val = alloc::vec::Vec::new();
// for _ in 0..4096 {
// val.push(0);
// }
// info!("Block address: {:?}", val.as_ptr());
// vm.registers[1] = hbvm::value::Value(val.as_ptr() as u64);
// vm.registers[2] = hbvm::value::Value(4096);
// Ok(())
// }

View file

@ -1,9 +1,6 @@
use { use {
crate::holeybytes::{kernel_services::block_read, Vm}, crate::holeybytes::{kernel_services::block_read, Vm},
alloc::{ alloc::vec::Vec,
string::{String, ToString},
vec::Vec,
},
}; };
pub enum DtError { pub enum DtError {
QueryFailure, QueryFailure,
@ -11,14 +8,13 @@ pub enum DtError {
pub fn dt_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), DtError> { pub fn dt_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), DtError> {
let msg_vec = block_read(mem_addr, length); let msg_vec = block_read(mem_addr, length);
let mut bytes: Vec<u8> = Vec::new(); let query_string = core::str::from_utf8(
for byte in msg_vec { msg_vec
if *byte == 0 { .split_once(|&byte| byte == 0)
break; .unwrap_or((msg_vec, &[]))
} .0,
bytes.push(*byte) )
} .unwrap();
let query_string = String::from_utf8(bytes).unwrap();
log::trace!("Query {}", query_string); log::trace!("Query {}", query_string);
let ret = query_parse(query_string); let ret = query_parse(query_string);
@ -29,17 +25,13 @@ pub fn dt_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), D
Ok(()) Ok(())
} }
fn query_parse(query_string: String) -> u64 { fn query_parse(query_string: &str) -> u64 {
let qt_parse_step_one = query_string.split("/"); let query = query_string.split('/').collect::<Vec<&str>>();
let mut qt_parse_step_two: Vec<String> = Vec::new();
for a in qt_parse_step_one {
qt_parse_step_two.push(a.to_string());
}
let first_fragment: &str = &qt_parse_step_two[0]; let first_fragment: &str = &query[0];
let ret = match first_fragment { let ret = match first_fragment {
"framebuffer" => framebuffer_parse(qt_parse_step_two), "framebuffer" => framebuffer_parse(query),
"cpu" => cpu_parse(qt_parse_step_two), "cpu" => cpu_parse(query),
_ => 0, _ => 0,
}; };
@ -47,8 +39,8 @@ fn query_parse(query_string: String) -> u64 {
return ret; return ret;
} }
fn cpu_parse(qt_parse_step_two: Vec<String>) -> u64 { fn cpu_parse(qt_parse_step_two: Vec<&str>) -> u64 {
let second_fragment: &str = &qt_parse_step_two[1]; let second_fragment: &str = qt_parse_step_two[1];
match second_fragment { match second_fragment {
// "architecture" => { // "architecture" => {
// return 0; // return 0;
@ -59,15 +51,14 @@ fn cpu_parse(qt_parse_step_two: Vec<String>) -> u64 {
}; };
} }
fn framebuffer_parse(qt_parse_step_two: Vec<String>) -> u64 { fn framebuffer_parse(qt_parse_step_two: Vec<&str>) -> u64 {
use crate::kmain::FB_REQ; use crate::kmain::FB_REQ;
let fbs = &FB_REQ.get_response().get().unwrap().framebuffers(); let fbs = &mut FB_REQ.get_response().get().unwrap().framebuffers();
let second_fragment: &str = qt_parse_step_two[1];
let second_fragment: &str = &qt_parse_step_two[1];
match second_fragment { match second_fragment {
"fb0" => { "fb0" => {
let fb_front = &fbs[0]; let fb_front = &fbs[0];
let third_fragment: &str = &qt_parse_step_two[2]; let third_fragment: &str = qt_parse_step_two[2];
let ret = match third_fragment { let ret = match third_fragment {
"ptr" => { "ptr" => {
let ptr = fb_front.address.as_ptr().unwrap(); let ptr = fb_front.address.as_ptr().unwrap();

View file

@ -9,15 +9,8 @@ use log::Record;
pub fn log_msg_handler(_vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), LogError> { pub fn log_msg_handler(_vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), LogError> {
let msg_vec = block_read(mem_addr, length); let msg_vec = block_read(mem_addr, length);
let log_level = msg_vec.last().unwrap();
let file_name = "None";
let line_number = 0;
match core::str::from_utf8(&msg_vec[..msg_vec.len()]) {
Ok(strr) => {
use log::Level::*; use log::Level::*;
let log_level = match log_level { let log_level = match msg_vec[0] {
0 | 48 => Error, 0 | 48 => Error,
1 | 49 => Warn, 1 | 49 => Warn,
2 | 50 => Info, 2 | 50 => Info,
@ -27,7 +20,19 @@ pub fn log_msg_handler(_vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(),
return Err(LogError::InvalidLogFormat); return Err(LogError::InvalidLogFormat);
} }
}; };
if log_level > log::max_level() {
return Ok(());
}
let strptr = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap());
let strlen = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap()) as usize;
let str = block_read(strptr, strlen);
let file_name = "None";
let line_number = 0;
match core::str::from_utf8(&str) {
Ok(strr) => {
log::logger().log( log::logger().log(
&Record::builder() &Record::builder()
.args(format_args!("{}", strr)) .args(format_args!("{}", strr))

View file

@ -17,13 +17,28 @@ pub enum MemoryQuotaType {
} }
fn alloc_page(vm: &mut Vm, _mem_addr: u64, _length: usize) -> Result<(), MemoryServiceError> { fn alloc_page(vm: &mut Vm, _mem_addr: u64, _length: usize) -> Result<(), MemoryServiceError> {
let ptr = unsafe { alloc(Layout::from_size_align_unchecked(4096, 4096)) }; let ptr = unsafe { alloc(Layout::from_size_align_unchecked(4096, 8)) };
info!("Block address: {:?}", ptr); info!("Block address: {:?}", ptr);
vm.registers[1] = hbvm::value::Value(ptr as u64); vm.registers[1] = hbvm::value::Value(ptr as u64);
vm.registers[2] = hbvm::value::Value(4096); vm.registers[2] = hbvm::value::Value(4096);
Ok(()) Ok(())
} }
#[inline(always)]
unsafe fn memset(dest: *mut u8, src: *const u8, count: usize, size: usize) {
let total_size = count * size;
src.copy_to_nonoverlapping(dest, size);
let mut copied = size;
while copied < total_size {
let copy_size = copied.min(total_size - copied);
dest.add(copied).copy_from_nonoverlapping(dest, copy_size);
copied += copy_size;
}
}
#[inline(always)]
pub fn memory_msg_handler( pub fn memory_msg_handler(
vm: &mut Vm, vm: &mut Vm,
mem_addr: u64, mem_addr: u64,
@ -32,40 +47,35 @@ pub fn memory_msg_handler(
let msg_vec = block_read(mem_addr, length); let msg_vec = block_read(mem_addr, length);
let msg_type = msg_vec[0]; let msg_type = msg_vec[0];
match msg_type { match msg_type {
0 => { 0 => unsafe {
let page_count = msg_vec[1]; let page_count = msg_vec[1];
let mptr_raw: [u8; 8] = msg_vec[2..10].try_into().unwrap();
let mptr: u64 = u64::from_le_bytes(mptr_raw);
log::debug!("Allocating {} pages @ {:x}", page_count, mptr); let ptr = alloc(Layout::from_size_align_unchecked(
let ptr = unsafe {
alloc(Layout::from_size_align_unchecked(
page_count as usize * 4096, page_count as usize * 4096,
4096, 8,
)) ));
};
log::debug!("Allocating {} pages @ {:x}", page_count, ptr as u64);
vm.registers[1] = hbvm::value::Value(ptr as u64); vm.registers[1] = hbvm::value::Value(ptr as u64);
log::debug!("Kernel ptr: {:x}", ptr as u64); log::debug!("Kernel ptr: {:x}", ptr as u64);
} },
1 => { 1 => unsafe {
let page_count = msg_vec[1]; let page_count = msg_vec[1];
let mptr_raw: [u8; 8] = msg_vec[2..10].try_into().unwrap(); let mptr_raw: [u8; 8] = msg_vec[2..10].try_into().unwrap();
let mptr: u64 = u64::from_le_bytes(mptr_raw); let mptr: u64 = u64::from_le_bytes(mptr_raw);
log::debug!("Deallocating {} pages @ {:x}", page_count, mptr); log::debug!("Deallocating {} pages @ {:x}", page_count, mptr);
unsafe {
dealloc( dealloc(
mptr as *mut u8, mptr as *mut u8,
Layout::from_size_align_unchecked(page_count as usize * 4096, 4096), Layout::from_size_align_unchecked(page_count as usize * 4096, 8),
) )
} },
}
2 => { 2 => {
use MemoryQuotaType::*; use MemoryQuotaType::*;
let quota_type = match msg_vec[0] { let quota_type = match msg_vec[1] {
0 => NoQuota, 0 => NoQuota,
1 => SoftQuota, 1 => SoftQuota,
2 => HardQuota, 2 => HardQuota,
@ -82,10 +92,24 @@ pub fn memory_msg_handler(
) )
} }
3 => { 3 => {
let page_count = msg_vec[0]; let page_count = msg_vec[1];
log::debug!(" {} pages", page_count); log::debug!(" {} pages", page_count);
} }
4 => unsafe {
let count = u32::from_le_bytes(msg_vec[1..5].try_into().unwrap_unchecked()) as usize;
let src = u64::from_le_bytes(msg_vec[5..13].try_into().unwrap_unchecked()) as *const u8;
let dest = u64::from_le_bytes(msg_vec[13..21].try_into().unwrap_unchecked()) as *mut u8;
src.copy_to_nonoverlapping(dest, count);
},
5 => unsafe {
let count = u32::from_le_bytes(msg_vec[1..5].try_into().unwrap_unchecked()) as usize;
let size = u32::from_le_bytes(msg_vec[5..9].try_into().unwrap_unchecked()) as usize;
let src = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap_unchecked()) as *const u8;
let dest = u64::from_le_bytes(msg_vec[17..25].try_into().unwrap_unchecked()) as *mut u8;
memset(dest, src, count, size);
},
_ => { _ => {
log::debug!("Unknown memory service message type: {}", msg_type); log::debug!("Unknown memory service message type: {}", msg_type);
} }

View file

@ -22,21 +22,19 @@ pub enum ServiceError {
pub fn sds_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), ServiceError> { pub fn sds_msg_handler(vm: &mut Vm, mem_addr: u64, length: usize) -> Result<(), ServiceError> {
let msg_vec = block_read(mem_addr, length); let msg_vec = block_read(mem_addr, length);
let sds_event_type: ServiceEventType = msg_vec[0].into(); let sds_event_type: ServiceEventType = msg_vec[0].into();
let strptr = u64::from_le_bytes(msg_vec[1..9].try_into().unwrap());
// info!("Length {}", msg_vec.len()); let strlen = u64::from_le_bytes(msg_vec[9..17].try_into().unwrap()) as usize;
let string_vec = block_read(strptr, strlen);
let string = core::str::from_utf8(string_vec).expect("Our bytes should be valid utf8");
use ServiceEventType::*; use ServiceEventType::*;
match sds_event_type { match sds_event_type {
CreateService => { CreateService => {
let string =
core::str::from_utf8(&msg_vec[1..]).expect("Our bytes should be valid utf8");
let ret = sds_create_service(string); let ret = sds_create_service(string);
vm.registers[1] = hbvm::value::Value(ret as u64); vm.registers[1] = hbvm::value::Value(ret as u64);
} }
DeleteService => todo!(), DeleteService => todo!(),
SearchServices => { SearchServices => {
let string =
core::str::from_utf8(&msg_vec[1..]).expect("Our bytes should be valid utf8");
let ret = sds_search_service(string); let ret = sds_search_service(string);
vm.registers[1] = hbvm::value::Value(ret as u64); vm.registers[1] = hbvm::value::Value(ret as u64);
} }
@ -94,8 +92,7 @@ fn sds_create_service(protocol: &'static str) -> u64 {
// let a: protocol::Protocol = protocol.into(); // let a: protocol::Protocol = protocol.into();
buff_id buff_id
} }
pub fn sds_search_service(protocol: &str) -> u64 {
fn sds_search_service(protocol: &str) -> u64 {
let services = SERVICES.lock(); let services = SERVICES.lock();
let compare = Protocol::from(protocol); let compare = Protocol::from(protocol);
for (bid, protocol_canidate) in &services.0 { for (bid, protocol_canidate) in &services.0 {

View file

@ -30,30 +30,29 @@ impl Memory {
} }
impl hbvm::mem::Memory for Memory { impl hbvm::mem::Memory for Memory {
#[inline] #[inline(always)]
unsafe fn load( unsafe fn load(
&mut self, &mut self,
addr: Address, addr: Address,
target: *mut u8, target: *mut u8,
count: usize, count: usize,
) -> Result<(), hbvm::mem::LoadError> { ) -> Result<(), hbvm::mem::LoadError> {
if addr.get() % 4096 == 0 {} core::ptr::copy_nonoverlapping(addr.get() as *const u8, target, count);
core::ptr::copy(addr.get() as *const u8, target, count);
Ok(()) Ok(())
} }
#[inline] #[inline(always)]
unsafe fn store( unsafe fn store(
&mut self, &mut self,
addr: Address, addr: Address,
source: *const u8, source: *const u8,
count: usize, count: usize,
) -> Result<(), hbvm::mem::StoreError> { ) -> Result<(), hbvm::mem::StoreError> {
core::ptr::copy(source, addr.get() as *mut u8, count); core::ptr::copy_nonoverlapping(source, addr.get() as *mut u8, count);
Ok(()) Ok(())
} }
#[inline] #[inline(always)]
unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T { unsafe fn prog_read<T: Copy>(&mut self, addr: Address) -> T {
(addr.get() as *const T).read() (addr.get() as *const T).read()
} }

View file

@ -1,5 +1,5 @@
mod ecah; mod ecah;
mod kernel_services; pub mod kernel_services;
mod mem; mod mem;
use { use {
@ -97,7 +97,7 @@ impl HandlePageFault for PageFaultHandler {
#[inline(always)] #[inline(always)]
const fn stack_layout() -> Layout { const fn stack_layout() -> Layout {
unsafe { Layout::from_size_align_unchecked(STACK_SIZE, 4096) } unsafe { Layout::from_size_align_unchecked(STACK_SIZE, 8) }
} }
#[inline(always)] #[inline(always)]

View file

@ -49,9 +49,7 @@ impl<'a> IpcBuffer<'a> {
pub fn push(&mut self, msg: Message) { pub fn push(&mut self, msg: Message) {
match &self.buffer { match &self.buffer {
BufferTypes::Unbound(buff) => buff.push(msg), BufferTypes::Unbound(buff) => buff.push(msg),
BufferTypes::Bound(buff) => { BufferTypes::Bound(buff) => buff.push(msg).unwrap(),
let _ = buff.push(msg);
}
}; };
} }
pub fn pop(&mut self) -> Result<Message, IpcError> { pub fn pop(&mut self) -> Result<Message, IpcError> {

View file

@ -1,18 +1,15 @@
use { use {alloc::vec::Vec, hashbrown::HashMap};
alloc::{string::String, vec::Vec},
hashbrown::HashMap,
};
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Type {} pub struct Type {}
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Funct { pub struct Funct<'a> {
takes: Vec<String>, takes: Vec<&'a str>,
gives: Vec<String>, gives: Vec<&'a str>,
} }
#[derive(Debug, PartialEq, Clone)] #[derive(Debug, PartialEq, Clone)]
pub struct Protocol<'a> { pub struct Protocol<'a> {
types: HashMap<&'a str, Type>, types: HashMap<&'a str, Type>,
fns: HashMap<&'a str, Funct>, fns: HashMap<&'a str, Funct<'a>>,
} }
impl<'a> Protocol<'a> { impl<'a> Protocol<'a> {
pub fn void() -> Self { pub fn void() -> Self {

View file

@ -8,7 +8,10 @@ use {
device_tree::DeviceTree, device_tree::DeviceTree,
holeybytes::ExecThread, holeybytes::ExecThread,
ipc::buffer::IpcBuffer, ipc::buffer::IpcBuffer,
task::Executor,
}, },
alloc::boxed::Box,
core::cell::LazyCell,
hashbrown::HashMap, hashbrown::HashMap,
hbvm::mem::Address, hbvm::mem::Address,
limine::{Framebuffer, FramebufferRequest, NonNullPtr}, limine::{Framebuffer, FramebufferRequest, NonNullPtr},
@ -19,6 +22,15 @@ use {
pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! { pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! {
debug!("Entered kmain"); debug!("Entered kmain");
#[cfg(feature = "ktest")]
{
use crate::ktest;
debug!("TESTING");
ktest::test_main();
loop {}
}
// let kcmd = build_cmd("Kernel Command Line", cmdline); // let kcmd = build_cmd("Kernel Command Line", cmdline);
// trace!("Cmdline: {kcmd:?}"); // trace!("Cmdline: {kcmd:?}");
@ -50,7 +62,6 @@ pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! {
let fb1: &NonNullPtr<Framebuffer> = &FB_REQ.get_response().get().unwrap().framebuffers()[0]; let fb1: &NonNullPtr<Framebuffer> = &FB_REQ.get_response().get().unwrap().framebuffers()[0];
{ {
use crate::alloc::string::ToString;
let mut dt = DEVICE_TREE.lock(); let mut dt = DEVICE_TREE.lock();
let mut disp = xml::XMLElement::new("display_0"); let mut disp = xml::XMLElement::new("display_0");
@ -58,21 +69,33 @@ pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! {
disp.set_attribute("height", fb1.height); disp.set_attribute("height", fb1.height);
disp.set_attribute("bpp", fb1.bpp); disp.set_attribute("bpp", fb1.bpp);
disp.set_attribute("pitch", fb1.pitch); disp.set_attribute("pitch", fb1.pitch);
dt.devices.insert("Displays".to_string(), alloc::vec![disp]); dt.devices.insert("Displays", alloc::vec![disp]);
} }
debug!("Graphics initialised"); debug!("Graphics initialised");
debug!( debug!(
"Graphics front ptr {:?}", "Graphics front ptr {:?}",
fb1.address.as_ptr().unwrap() as *const u8 fb1.address.as_ptr().unwrap() as *const u8
); );
log::info!("Started AbleOS");
let mut executor = crate::task::Executor::new(256);
unsafe { unsafe {
let executor = LazyCell::<Executor>::force_mut(&mut EXECUTOR);
for module in boot_modules.iter() { for module in boot_modules.iter() {
let cmd = module.cmd.trim_matches('"'); let cmd = module.cmd.trim_matches('"');
let cmd_len = cmd.len() as u64; let cmd_len = cmd.len() as u64;
log::info!("Spawning {} with arguments \"{}\"", module.path, cmd); log::info!(
"Starting {}",
module
.path
.split('/')
.last()
.unwrap()
.split('.')
.next()
.unwrap()
);
log::debug!("Spawning {} with arguments \"{}\"", module.path, cmd);
// decode AbleOS Executable format // decode AbleOS Executable format
let header = &module.bytes[0..46]; let header = &module.bytes[0..46];
@ -93,27 +116,30 @@ pub fn kmain(_cmdline: &str, boot_modules: BootModules) -> ! {
let code_length = u64::from_le_bytes(header[7..15].try_into().unwrap()); let code_length = u64::from_le_bytes(header[7..15].try_into().unwrap());
let data_length = u64::from_le_bytes(header[15..23].try_into().unwrap()); let data_length = u64::from_le_bytes(header[15..23].try_into().unwrap());
let end = (code_length + data_length) as usize; let end = (code_length + data_length) as usize;
log::info!("{code_length} + {data_length} = {end}"); log::debug!("{code_length} + {data_length} = {end}");
let mut thr = ExecThread::new(&module.bytes[offset..end], Address::new(0)); let mut thr = ExecThread::new(&module.bytes[offset..end], Address::new(0));
if cmd_len > 0 { if cmd_len > 0 {
thr.set_arguments(cmd.as_ptr() as u64, cmd_len); thr.set_arguments(cmd.as_ptr() as u64, cmd_len);
} }
executor.spawn(async move { executor.spawn(Box::pin(async move {
if let Err(e) = thr.await { if let Err(e) = thr.await {
log::error!("{e:?}"); log::error!("{e:?}");
} }
}) }));
} }
debug!("Random number: {}", hardware_random_u64()); debug!("Random number: {}", hardware_random_u64());
executor.run(); executor.run();
}; };
crate::arch::spin_loop() crate::arch::spin_loop()
} }
// ! SAFETY: this is not threadsafe at all, like even a little bit.
// ! SERIOUSLY
pub static mut EXECUTOR: LazyCell<Executor> = LazyCell::new(|| Executor::new());
pub static DEVICE_TREE: Lazy<Mutex<DeviceTree>> = Lazy::new(|| { pub static DEVICE_TREE: Lazy<Mutex<DeviceTree>> = Lazy::new(|| {
let dt = DeviceTree::new(); let dt = DeviceTree::new();
Mutex::new(dt) Mutex::new(dt)
@ -131,10 +157,3 @@ pub static IPC_BUFFERS: Lazy<Mutex<IpcBuffers>> = Lazy::new(|| {
Mutex::new(bufs) Mutex::new(bufs)
}); });
#[test_case]
fn trivial_assertion() {
trace!("trivial assertion... ");
assert_eq!(1, 1);
info!("[ok]");
}

38
kernel/src/ktest.rs Normal file
View file

@ -0,0 +1,38 @@
pub use ktest_macro::ktest;
use log::debug;
extern "C" {
static __ktest_start: fn();
static __ktest_end: fn();
}
// TODO: Get test_fn linker name (may require no_mangle in macro)
// More info on tests (run the rest even if panic)
// Implement ktest for arm and riscv (Later problems, see below)
// Allow for arch specific tests (Leave for now)
// Allow for ktest test name attr
// Usefull message at the end of testing
pub fn test_main() {
unsafe {
let mut current_test = &__ktest_start as *const fn();
let mut current = 1;
let test_end = &__ktest_end as *const fn();
while current_test < test_end {
let test_fn = *current_test;
debug!("Running test {}", current);
test_fn();
debug!("Test {} passed", current);
current_test = current_test.add(1);
current += 1;
}
}
}
#[ktest]
pub fn trivial_assertion() {
assert_eq!(1, 1);
}

View file

@ -2,19 +2,19 @@
//! Named akern. //! Named akern.
//! Akern is woefully undersupported at the moment but we are looking to add support improve hardware discovery and make our lives as kernel and operating system developers easier and better //! Akern is woefully undersupported at the moment but we are looking to add support improve hardware discovery and make our lives as kernel and operating system developers easier and better
#![no_std] #![no_std]
#![no_main]
#![feature( #![feature(
slice_split_once,
exclusive_wrapper, exclusive_wrapper,
new_uninit, core_intrinsics,
abi_x86_interrupt, abi_x86_interrupt,
lazy_get,
alloc_error_handler, alloc_error_handler,
ptr_sub_ptr, ptr_sub_ptr,
custom_test_frameworks,
naked_functions, naked_functions,
pointer_is_aligned_to pointer_is_aligned_to,
)] )]
#![test_runner(crate::test_runner)] #![allow(dead_code, internal_features, static_mut_refs)]
#![cfg_attr(not(debug_assertions), allow(unused, deprecated))]
#![allow(dead_code)]
extern crate alloc; extern crate alloc;
mod allocator; mod allocator;
@ -32,6 +32,10 @@ mod memory;
mod task; mod task;
mod utils; mod utils;
// #[cfg(feature = "tests")]
mod ktest;
use alloc::string::ToString;
use versioning::Version; use versioning::Version;
/// Kernel's version /// Kernel's version
@ -55,15 +59,7 @@ fn panic(info: &core::panic::PanicInfo) -> ! {
)); ));
} }
let msg = info.message(); let msg = info.message().to_string().replace("\n", "\r\n");
let _ = crate::arch::log(format_args!("{msg}\r\n")); let _ = crate::arch::log(format_args!("{msg}\r\n"));
loop {} loop {}
} }
#[cfg(test)]
fn test_runner(tests: &[&dyn Fn()]) {
println!("Running {} tests", tests.len());
for test in tests {
test();
}
}

View file

@ -36,7 +36,19 @@ impl log::Log for Logger {
Level::Debug => "25", Level::Debug => "25",
Level::Trace => "103", Level::Trace => "103",
}; };
let module = record.module_path().unwrap_or_default(); let module = record
.module_path()
.unwrap_or_default()
.rsplit_once(':')
.unwrap_or_default()
.1;
if module == "" {
crate::arch::log(format_args!(
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m: {}\r\n",
record.args(),
))
.expect("write to serial console");
} else {
let line = record.line().unwrap_or_default(); let line = record.line().unwrap_or_default();
crate::arch::log(format_args!( crate::arch::log(format_args!(
"\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n", "\x1b[38;5;{lvl_color}m{lvl}\x1b[0m [{module}:{line}]: {}\r\n",
@ -44,6 +56,7 @@ impl log::Log for Logger {
)) ))
.expect("write to serial console"); .expect("write to serial console");
} }
}
fn flush(&self) {} fn flush(&self) {}
} }

View file

@ -29,24 +29,24 @@ pub fn yield_now() -> impl Future<Output = ()> {
YieldNow(false) YieldNow(false)
} }
pub struct Executor<F: Future<Output = ()> + Send> { pub struct Executor {
tasks: Slab<Task<F>>, tasks: Slab<Task>,
task_queue: Arc<TaskQueue>, task_queue: Arc<TaskQueue>,
} }
impl<F: Future<Output = ()> + Send> Executor<F> { impl Executor {
pub fn new(size: usize) -> Self { pub fn new() -> Self {
Self { Self {
tasks: Slab::with_capacity(size), tasks: Slab::new(),
task_queue: Arc::new(TaskQueue::new()), task_queue: Arc::new(TaskQueue::new()),
} }
} }
#[inline] #[inline]
pub fn spawn(&mut self, future: F) { pub fn spawn(&mut self, future: Pin<Box<dyn Future<Output = ()> + Send>>) -> usize {
self.task_queue let id = self.tasks.insert(Task::new(future));
.queue self.task_queue.queue.push(id);
.push(self.tasks.insert(Task::new(future))); id
} }
pub fn run(&mut self) { pub fn run(&mut self) {
@ -83,16 +83,16 @@ impl<F: Future<Output = ()> + Send> Executor<F> {
} }
} }
struct Task<F: Future<Output = ()> + Send> { struct Task {
future: Pin<Box<F>>, future: Pin<Box<dyn Future<Output = ()> + Send>>,
waker: Option<TaskWaker>, waker: Option<TaskWaker>,
} }
impl<F: Future<Output = ()> + Send> Task<F> { impl Task {
#[inline(always)] #[inline(always)]
pub fn new(future: F) -> Self { pub fn new(future: Pin<Box<dyn Future<Output = ()> + Send>>) -> Self {
Self { Self {
future: Box::pin(future), future,
waker: None, waker: None,
} }
} }

View file

@ -4,7 +4,6 @@
/// Used when tab `\t` in hardware is not known and we will default to two spaces /// Used when tab `\t` in hardware is not known and we will default to two spaces
pub const TAB: &str = " "; pub const TAB: &str = " ";
// NOTE: this only reduces the code duplication in source code not in generated code! // NOTE: this only reduces the code duplication in source code not in generated code!
// Written by Yours Truly: Munir // Written by Yours Truly: Munir
/// A simple macro to reduce code duplication when we use TAB internally /// A simple macro to reduce code duplication when we use TAB internally
@ -12,7 +11,7 @@ pub const TAB: &str = " ";
macro_rules! tab { macro_rules! tab {
($num:expr) => { ($num:expr) => {
TAB.repeat($num) TAB.repeat($num)
} };
} }
// NOTE: this only reduces the code duplication in source code not in generated code! // NOTE: this only reduces the code duplication in source code not in generated code!
@ -22,7 +21,7 @@ macro_rules! tab {
macro_rules! device_tree { macro_rules! device_tree {
($devtree:expr, $dev_type_vec:expr) => { ($devtree:expr, $dev_type_vec:expr) => {
for each_device_type in $dev_type_vec { for each_device_type in $dev_type_vec {
$devtree.devices.insert(each_device_type.to_string(), Vec::new()); $devtree.devices.insert(each_device_type, Vec::new());
} }
}; };
} }

View file

@ -1,6 +1,6 @@
{ {
"arch": "aarch64", "arch": "aarch64",
"data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128", "data-layout": "e-m:e-i8:8:32-i16:16:32-i64:64-i128:128-n32:64-S128-Fn32",
"disable-redzone": true, "disable-redzone": true,
"env": "", "env": "",
"executables": true, "executables": true,

View file

@ -0,0 +1,22 @@
{
"llvm-target": "x86_64-unknown-none",
"data-layout": "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-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": "+sse4.1,+avx,+aes,+fma,+popcnt,+bmi2,+avx2,+lzcnt,+xsave",
"code-model": "kernel",
"pre-link-args": {
"ld.lld": [
"--gc-sections",
"--script=kernel/lds/x86_64.ld"
]
}
}

2
known_bugs.md Normal file
View file

@ -0,0 +1,2 @@
# i did not know where to put this
- memcpy / memset cause crash on debug builds due to ptr misalignment that is not present on release builds

View file

@ -6,24 +6,15 @@ edition = "2021"
[dependencies] [dependencies]
str-reader = "0.1" str-reader = "0.1"
derive_more = { version = "1", default-features = false, features = [ derive_more = { version = "1", default-features = false, features = [
"add",
"add_assign",
"constructor",
"display", "display",
"from",
"into",
"mul",
"mul_assign",
"not",
"sum",
] } ] }
error-stack = "0.5" error-stack = "0.5"
fatfs = "0.3" fatfs = { version = "0.3", default-features = false, features = [
"std",
"alloc",
] }
toml = "0.8" toml = "0.8"
# hbasm.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git"
hblang.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git" hblang.git = "https://git.ablecorp.us/AbleOS/holey-bytes.git"
log = "0.4"
[dependencies.reqwest] raw-cpuid = "11"
version = "0.12" ureq = { version = "2", default-features = false, features = ["tls"] }
default-features = false
features = ["rustls-tls", "blocking"]

View file

@ -1,5 +1,6 @@
#![allow(unused)] #![allow(unused)]
use std::{ use std::{
collections::HashMap,
fmt::format, fmt::format,
fs::{read_to_string, File}, fs::{read_to_string, File},
io::{BufWriter, Write}, io::{BufWriter, Write},
@ -13,6 +14,7 @@ pub struct Package {
name: String, name: String,
binaries: Vec<String>, binaries: Vec<String>,
build_cmd: String, build_cmd: String,
args: HashMap<String, String>,
} }
impl Package { impl Package {
@ -46,57 +48,79 @@ impl Package {
let mut binaries = vec![]; let mut binaries = vec![];
for (count, (name, table)) in bin_table.into_iter().enumerate() { for (count, (name, table)) in bin_table.into_iter().enumerate() {
// if count != 0 {
println!("{}", name);
binaries.push(name.clone()); binaries.push(name.clone());
// }
} }
let build_table = data.get("build").unwrap(); let build_table = data.get("build").unwrap();
let mut build_cmd: String = build_table.get("command").unwrap().as_str().unwrap().into(); let mut build_cmd: String = build_table.get("command").unwrap().as_str().unwrap().into();
build_cmd.remove(0); build_cmd.remove(0);
// build_cmd.pop(); let mut args: HashMap<String, String> = match build_table.get("args") {
None => HashMap::new(),
Some(v) => v
.as_table()
.unwrap()
.into_iter()
.map(|(k, v)| (k.clone(), v.to_string()))
.collect::<HashMap<String, String>>(),
};
Self { Self {
name, name,
binaries, binaries,
build_cmd, build_cmd,
args,
} }
} }
pub fn build(&self) { pub fn build(&self, out: &mut Vec<u8>) -> std::io::Result<()> {
if self.binaries.contains(&"hblang".to_string()) { if self.binaries.contains(&"hblang".to_string()) {
let file = self.build_cmd.split_ascii_whitespace().last().unwrap(); let file = self.build_cmd.split_ascii_whitespace().last().unwrap();
let path = format!("sysdata/programs/{}/{}", self.name, file); let path = format!("sysdata/programs/{}/{}", self.name, file);
let mut bytes = Vec::new();
// compile here // compile here
let _ = hblang::run_compiler( let mut warnings = String::new();
hblang::run_compiler(
&path, &path,
Options { Options {
fmt: true, fmt: true,
in_house_regalloc: true,
..Default::default() ..Default::default()
}, },
&mut bytes, out,
); &mut warnings,
let _ = hblang::run_compiler(&path, Default::default(), &mut bytes); )?;
match std::fs::create_dir("target/programs") { match std::fs::create_dir("target/programs") {
Ok(_) => (), Ok(_) => (),
Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => (), Err(e) if e.kind() == std::io::ErrorKind::AlreadyExists => (),
Err(e) => panic!("{}", e), Err(e) => panic!("{}", e),
} }
std::fs::write(format!("target/programs/{}.hbf", self.name), &bytes).unwrap();
bytes.clear(); hblang::run_compiler(
let _ = hblang::run_compiler( &path,
Options {
in_house_regalloc: true,
..Default::default()
},
out,
&mut warnings,
)?;
std::fs::write(format!("target/programs/{}.hbf", self.name), &out)?;
out.clear();
hblang::run_compiler(
&path, &path,
Options { Options {
dump_asm: true, dump_asm: true,
in_house_regalloc: true,
..Default::default() ..Default::default()
}, },
&mut bytes, out,
); &mut warnings,
std::fs::write(format!("target/programs/{}.hba", self.name), &bytes).unwrap(); )?;
} std::fs::write(format!("target/programs/{}.hba", self.name), &out)?;
out.clear();
}
Ok(())
} }
} }

View file

@ -1,18 +1,16 @@
// #![allow(unused)]
mod dev; mod dev;
use { use {
core::fmt::Write as _,
derive_more::Display, derive_more::Display,
dev::Package, dev::Package,
error_stack::{bail, report, Context, Report, Result, ResultExt}, error_stack::{bail, report, Context, Report, Result, ResultExt},
fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek}, fatfs::{FileSystem, FormatVolumeOptions, FsOptions, ReadWriteSeek},
std::{ std::{
// fmt::Display,
fs::{self, File}, fs::{self, File},
io::{self, Write}, io::{self, Write},
path::Path, path::Path,
process::{exit, Command}, process::{exit, Command, Stdio},
}, },
toml::Value, toml::Value,
}; };
@ -21,11 +19,15 @@ fn main() -> Result<(), Error> {
let mut args = std::env::args(); let mut args = std::env::args();
args.next(); args.next();
log::set_logger(&hblang::Logger).unwrap();
log::set_max_level(log::LevelFilter::Error);
match args.next().as_deref() { match args.next().as_deref() {
Some("build" | "b") => { Some("build" | "b") => {
let mut release = false; let mut release = false;
let mut debuginfo = false; let mut debuginfo = false;
let mut target = Target::X86_64; let mut target = Target::X86_64;
let mut tests = false;
for arg in args { for arg in args {
if arg == "-r" || arg == "--release" { if arg == "-r" || arg == "--release" {
release = true; release = true;
@ -35,17 +37,45 @@ fn main() -> Result<(), Error> {
target = Target::Riscv64Virt; target = Target::Riscv64Virt;
} else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" { } else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" {
target = Target::Aarch64; target = Target::Aarch64;
} else if arg == "avx2" {
target = Target::X86_64Avx2;
} else if arg == "--ktest" {
tests = true;
} else { } else {
return Err(report!(Error::InvalidSubCom)); return Err(report!(Error::InvalidSubCom));
} }
} }
build(release, target, debuginfo).change_context(Error::Build) build(release, target, debuginfo, tests).change_context(Error::Build)
} }
// Some("test" | "t") => {
// let mut release = false;
// let mut debuginfo = false;
// let mut target = Target::X86_64;
// for arg in args {
// if arg == "-r" || arg == "--release" {
// release = true;
// } else if arg == "-d" || arg == "--debuginfo" {
// debuginfo = true;
// } else if arg == "rv64" || arg == "riscv64" || arg == "riscv64-virt" {
// target = Target::Riscv64Virt;
// } else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" {
// target = Target::Aarch64;
// } else if arg == "avx2" {
// target = Target::X86_64Avx2;
// } else {
// return Err(report!(Error::InvalidSubCom));
// }
// }
// test(release, target, debuginfo).change_context(Error::Build)
// }
Some("run" | "r") => { Some("run" | "r") => {
let mut release = false; let mut release = false;
let mut debuginfo = false; let mut debuginfo = false;
let mut target = Target::X86_64; let mut target = Target::X86_64;
let mut tests = false;
let mut do_accel = true;
for arg in args { for arg in args {
if arg == "-r" || arg == "--release" { if arg == "-r" || arg == "--release" {
release = true; release = true;
@ -55,13 +85,19 @@ fn main() -> Result<(), Error> {
target = Target::Riscv64Virt; target = Target::Riscv64Virt;
} else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" { } else if arg == "arm64" || arg == "aarch64" || arg == "aarch64-virt" {
target = Target::Aarch64; target = Target::Aarch64;
} else if arg == "--noaccel" {
do_accel = false;
} else if arg == "avx2" {
target = Target::X86_64Avx2;
} else if arg == "--ktest" {
tests = true;
} else { } else {
return Err(report!(Error::InvalidSubCom)); return Err(report!(Error::InvalidSubCom));
} }
} }
build(release, target, debuginfo)?; build(release, target, debuginfo, tests)?;
run(release, target) run(release, target, do_accel)
} }
Some("help" | "h") => { Some("help" | "h") => {
println!(concat!( println!(concat!(
@ -71,8 +107,11 @@ fn main() -> Result<(), Error> {
" help (h): Print this message\n", " help (h): Print this message\n",
" run (r): Build and run AbleOS in QEMU\n\n", " run (r): Build and run AbleOS in QEMU\n\n",
"Options for build and run:\n", "Options for build and run:\n",
" -r: build in release mode", " -r / --release: build in release mode\n",
" [target]: sets target" " -d / --debuginfo: build with debug info\n",
" --noaccel: run without acceleration (e.g, no kvm)\n",
" --ktest: Enables tests via ktest\n",
"[ rv64 / riscv64 / riscv64-virt / aarch64 / arm64 / aarch64-virt / avx2 ]: sets target"
),); ),);
Ok(()) Ok(())
} }
@ -195,7 +234,12 @@ TERM_BACKDROP={}
let modules = value.get_mut("modules").unwrap().as_table_mut().unwrap(); let modules = value.get_mut("modules").unwrap().as_table_mut().unwrap();
// let mut real_modules = modules.clone(); // let mut real_modules = modules.clone();
modules.into_iter().for_each(|(_, value)| { let mut errors = String::new();
let mut out = Vec::new();
modules
.into_iter()
.map(|(_, value)| -> Result<(), io::Error> {
if value.is_table() { if value.is_table() {
let path = get_path_without_boot_prefix( let path = get_path_without_boot_prefix(
value.get("path").expect("You must have `path` as a value"), value.get("path").expect("You must have `path` as a value"),
@ -207,9 +251,26 @@ TERM_BACKDROP={}
let p = Package::load_from_file( let p = Package::load_from_file(
format!("sysdata/programs/{}/meta.toml", path).to_owned(), format!("sysdata/programs/{}/meta.toml", path).to_owned(),
); );
p.build(); match p.build(&mut out) {
Ok(()) => {}
Err(_) => {
writeln!(errors, "========= while compiling {} =========", path)
.unwrap();
errors.push_str(core::str::from_utf8(&out).expect("no"));
out.clear();
} }
}); }
}
Ok(())
})
.for_each(drop);
if !errors.is_empty() {
let _ = writeln!(errors, "!!! STOPPING DUE TO PREVIOUS ERRORS !!!");
std::eprint!("{errors}");
continue;
}
modules.into_iter().for_each(|(_key, value)| { modules.into_iter().for_each(|(_key, value)| {
if value.is_table() { if value.is_table() {
let path = value.get("path").expect("You must have `path` as a value"); let path = value.get("path").expect("You must have `path` as a value");
@ -278,7 +339,7 @@ fn copy_file_to_img(fpath: &str, fs: &FileSystem<File>) {
.expect("Copy failed"); .expect("Copy failed");
} }
fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> { fn build(release: bool, target: Target, debuginfo: bool, tests: bool) -> Result<(), Error> {
let fs = get_fs().change_context(Error::Io)?; let fs = get_fs().change_context(Error::Io)?;
let mut com = Command::new("cargo"); let mut com = Command::new("cargo");
com.current_dir("kernel"); com.current_dir("kernel");
@ -290,12 +351,19 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> {
com.env("RUSTFLAGS", "-Cdebug-assertions=true"); com.env("RUSTFLAGS", "-Cdebug-assertions=true");
} }
if tests {
com.args(["--features", "ktest"]);
}
if target == Target::Riscv64Virt { if target == Target::Riscv64Virt {
com.args(["--target", "targets/riscv64-virt-ableos.json"]); com.args(["--target", "targets/riscv64-virt-ableos.json"]);
} }
if target == Target::Aarch64 { if target == Target::Aarch64 {
com.args(["--target", "targets/aarch64-virt-ableos.json"]); com.args(["--target", "targets/aarch64-virt-ableos.json"]);
} }
if target == Target::X86_64Avx2 {
com.args(["--target", "targets/x86_64_v3-ableos.json"]);
}
match com.status() { match com.status() {
Ok(s) if s.code() != Some(0) => bail!(Error::Build), Ok(s) if s.code() != Some(0) => bail!(Error::Build),
@ -309,6 +377,10 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> {
path.push_str("_x86-64"); path.push_str("_x86-64");
"target/x86_64-ableos" "target/x86_64-ableos"
} }
Target::X86_64Avx2 => {
path.push_str("_x86-64");
"target/x86_64_v3-ableos"
}
Target::Riscv64Virt => "target/riscv64-virt-ableos", Target::Riscv64Virt => "target/riscv64-virt-ableos",
Target::Aarch64 => { Target::Aarch64 => {
path.push_str("_aarch64"); path.push_str("_aarch64");
@ -331,25 +403,67 @@ fn build(release: bool, target: Target, debuginfo: bool) -> Result<(), Error> {
.change_context(Error::Io) .change_context(Error::Io)
} }
fn run(release: bool, target: Target) -> Result<(), Error> { fn run(release: bool, target: Target, do_accel: bool) -> Result<(), Error> {
let mut com = match target { let target_str = match target {
Target::X86_64 => Command::new("qemu-system-x86_64"), Target::X86_64 | Target::X86_64Avx2 => "qemu-system-x86_64",
Target::Riscv64Virt => Command::new("qemu-system-riscv64"), Target::Riscv64Virt => "qemu-system-riscv64",
Target::Aarch64 => Command::new("qemu-system-aarch64"), Target::Aarch64 => "qemu-system-aarch64",
}; };
let (mut com, mut com2) = (Command::new(target_str), Command::new(target_str));
let ovmf_path = fetch_ovmf(target); let ovmf_path = fetch_ovmf(target);
#[cfg(target_arch = "x86_64")]
let accel = if do_accel {
let supported = String::from_utf8(
com2.args(["--accel", "help"])
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.unwrap()
.wait_with_output()
.unwrap()
.stdout,
)
.unwrap();
let cpuid = raw_cpuid::CpuId::new();
let vmx = cpuid.get_feature_info().unwrap().has_vmx();
let svm = cpuid.get_svm_info().is_some();
if supported.contains("kvm") && (vmx || svm) {
"accel=kvm"
} else if cpuid
.get_processor_brand_string()
.filter(|a| a.as_str() == "GenuineIntel")
.is_some()
&& supported.contains("hax")
&& vmx
{
"accel=hax"
} else if supported.contains("whpx") {
"accel=whpx"
} else {
"accel=tcg"
}
} else {
"accel=tcg"
};
#[cfg(not(target_arch = "x86_64"))]
let accel = "accel=tcg";
match target { match target {
Target::X86_64 => { Target::X86_64 | Target::X86_64Avx2 => {
#[rustfmt::skip] #[rustfmt::skip]
com.args([ com.args([
"-bios", &ovmf_path.change_context(Error::OvmfFetch)?, "-bios", &ovmf_path.change_context(Error::OvmfFetch)?,
"-drive", "file=target/disk.img,format=raw", "-drive", "file=target/disk.img,format=raw",
"-device", "vmware-svga", "-device", "vmware-svga",
// "-serial", "stdio",
"-m", "2G", "-m", "2G",
"-smp", "1", "-smp", "1",
"-machine", "accel=kvm", "-parallel", "none",
"-cpu", "host", "-monitor", "none",
"-machine", accel,
"-cpu", "max",
"-device", "isa-debug-exit,iobase=0xf4,iosize=0x04", "-device", "isa-debug-exit,iobase=0xf4,iosize=0x04",
]); ]);
} }
@ -371,7 +485,7 @@ fn run(release: bool, target: Target) -> Result<(), Error> {
#[rustfmt::skip] #[rustfmt::skip]
com.args([ com.args([
"-M", "virt", "-M", "virt",
"-cpu", "neoverse-n2", "-cpu", "max",
"-device", "ramfb", "-device", "ramfb",
"-device", "qemu-xhci", "-device", "qemu-xhci",
"-device", "usb-kbd", "-device", "usb-kbd",
@ -392,9 +506,10 @@ fn run(release: bool, target: Target) -> Result<(), Error> {
} }
} }
fn fetch_ovmf(target: Target) -> Result<String, OvmfFetchError> { fn fetch_ovmf(target: Target) -> Result<String, OvmfFetchError> {
let (ovmf_url, ovmf_path) = match target { let (ovmf_url, ovmf_path) = match target {
Target::X86_64 => ( Target::X86_64 | Target::X86_64Avx2 => (
"https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd", "https://retrage.github.io/edk2-nightly/bin/RELEASEX64_OVMF.fd",
"target/RELEASEX64_OVMF.fd", "target/RELEASEX64_OVMF.fd",
), ),
@ -416,12 +531,12 @@ fn fetch_ovmf(target: Target) -> Result<String, OvmfFetchError> {
Ok(_) => return Ok(ovmf_path.to_owned()), Ok(_) => return Ok(ovmf_path.to_owned()),
Err(e) => return Err(report!(e).change_context(OvmfFetchError::Io)), Err(e) => return Err(report!(e).change_context(OvmfFetchError::Io)),
}; };
let mut bytes = reqwest::blocking::get(ovmf_url) let req = ureq::get(ovmf_url)
.call()
.map_err(Report::from) .map_err(Report::from)
.change_context(OvmfFetchError::Fetch)?; .change_context(OvmfFetchError::Fetch)?;
bytes std::io::copy(&mut req.into_reader(), &mut file)
.copy_to(&mut file)
.map_err(Report::from) .map_err(Report::from)
.change_context(OvmfFetchError::Io)?; .change_context(OvmfFetchError::Io)?;
@ -443,6 +558,7 @@ impl Context for OvmfFetchError {}
#[derive(Clone, Copy, PartialEq, Eq)] #[derive(Clone, Copy, PartialEq, Eq)]
enum Target { enum Target {
X86_64, X86_64,
X86_64Avx2,
Riscv64Virt, Riscv64Virt,
Aarch64, Aarch64,
} }

View file

@ -1,3 +1,5 @@
[toolchain] [toolchain]
channel = "nightly-2024-07-27" # old toolchain
# channel = "nightly-2024-07-27"
channel = "nightly-2024-11-20"
components = ["rust-src", "llvm-tools"] components = ["rust-src", "llvm-tools"]

BIN
sysdata/assets/able.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,10 @@
Tamsyn font is free. You are hereby granted permission to use, copy, modify,
and distribute it as you see fit.
Tamsyn font is provided "as is" without any express or implied warranty.
The author makes no representations about the suitability of this font for
a particular purpose.
In no event will the author be held liable for damages arising from the use
of this font.

BIN
sysdata/assets/mini.bmp Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 100 KiB

BIN
sysdata/assets/mini.qoi Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 B

View file

@ -1 +0,0 @@
# dt_api

View file

@ -1,8 +0,0 @@
stn := @use("../../stn/src/lib.hb");
.{string, memory, buffer} := stn
dt_get := fn(query: ^u8): int {
message_length := string.length(query)
return @eca(3, 5, query, message_length)
}

View file

@ -0,0 +1,4 @@
(horizontal
spacing : 10
(label "hi")
(label "goodbye"))

View file

@ -0,0 +1 @@
(label "hello")

View file

@ -0,0 +1,3 @@
(vertical
(label "hello")
(label "hello" color:red))

View file

@ -1,25 +1,49 @@
stn := @use("../../stn/src/lib.hb"); stn := @use("../../stn/src/lib.hb");
.{string, memory, buffer} := stn .{string, memory, buffer, log} := stn
render := @use("../../../libraries/render/src/lib.hb")
input := @use("../../intouch/src/lib.hb") input := @use("../../intouch/src/lib.hb")
widgets := @use("widgets/widgets.hb")
ui := @use("ui.hb")
WindowID := struct { WindowID := struct {
host_id: int, host_id: int,
window_id: int, window_id: int,
} }
create_window := fn(channel: int): void { VoidWindowID := WindowID.(0, 0)
create_window := fn(channel: int): ^render.Surface {
// get the horizon buffer // get the horizon buffer
// request a new window and provide the callback buffer // request a new window and provide the callback buffer
// wait to recieve a message // wait to recieve a message
windowing_system_buffer := buffer.search("XHorizon\0") windowing_system_buffer := buffer.search("XHorizon\0")
mem_buf := memory.request_page(1)
if windowing_system_buffer == 0 { if windowing_system_buffer == 0 {
return return @as(^render.Surface, idk)
} else { } else {
msg := "\{01}\0" // ! bad able, stop using string messages :ragey:
msg_length := 2 // msg := "\{01}\0"
return @eca(3, windowing_system_buffer, msg, msg_length) // msg_length := 2
// @as(void, @eca(3, windowing_system_buffer, msg, msg_length))
x := 0
loop if x > 1000 break else x += 1
ret := buffer.recv([u8; 4096], windowing_system_buffer, mem_buf)
if ret == null {
log.info("No messages\0")
}
if *mem_buf == 0 {
log.info("No messages\0")
}
return @as(^render.Surface, idk)
} }
} }

View file

@ -0,0 +1,47 @@
stn := @use("../../../libraries/stn/src/lib.hb");
.{string, log} := stn;
.{Vec2} := stn.math
render := @use("../../../libraries/render/src/lib.hb");
.{Surface} := render;
.{Font} := render.text
UI := struct {raw: ^u8, raw_length: uint, is_dirty: bool, surface: Surface, // Each child has their WidgetType as their first byte
// children: ^^u8,
}
render_ui := fn(surface: Surface, ui: UI): void {
if ui.is_dirty {
render.clear(ui.surface, render.black)
ui.is_dirty = false
}
pos := Vec2(uint).(0, 0)
render.put_surface(surface, ui.surface, pos, false)
}
sexpr_parser := fn(sexpr: ^u8): UI {
cursor := sexpr
paren_balance := 0
loop {
if *cursor == 0 {
if paren_balance != 0 {
log.error("Unbalanced Parens\0")
}
break
} else if *cursor == 40 {
log.info("Open paren\0")
paren_balance += 1
} else if *cursor == 41 {
log.info("Closed paren\0")
paren_balance -= 1
}
cursor += 1
}
length := string.length(sexpr)
ui_surface := render.new_surface(100, 100)
return UI.(sexpr, length, true, ui_surface)
}

View file

@ -0,0 +1,13 @@
render := @use("../../../../libraries/render/src/lib.hb");
.{Surface} := render
Image := struct {
magic: uint,
is_dirty: bool,
surface: Surface,
}
image_from_surface := fn(surface: Surface): Image {
img := Image.(4, true, surface)
return img
}

View file

@ -0,0 +1,46 @@
stn := @use("../../../../libraries/stn/src/lib.hb");
.{string, log} := stn;
.{Vec2} := stn.math
render := @use("../../../../libraries/render/src/lib.hb");
.{Surface, Color} := render;
.{Font} := render.text
Label := struct {
magic: uint,
is_dirty: bool,
surface: Surface,
text: ^u8,
text_length: uint,
bg: Color,
fg: Color,
}
set_label_text := fn(label: Label, text: ^u8): void {
text_length := string.length(text)
label.is_dirty = true
label.text = text
label.text_length = text_length
}
render_label_to_surface := fn(surface: Surface, label: Label, font: Font, pos: Vec2(uint)): void {
if label.is_dirty {
render.clear(label.surface, label.bg)
render.put_text(label.surface, font, .(0, 0), label.fg, label.text)
}
render.put_surface(surface, label.surface, pos, false)
}
new_label := fn(text: ^u8): Label {
text_surface := render.new_surface(1024, 20)
text_length := string.length(text)
label := Label.(3, true, text_surface, text, text_length, render.black, render.white)
return label
}
$set_color := fn(label: Label, bg: Color, fg: Color): void {
label.bg = bg
label.fg = fg
label.is_dirty = true
}

View file

@ -0,0 +1,7 @@
NoWidget := 0
VerticalWidgetType := 1
HorizontalWidgetType := 2
LabelWidgetType := 3
ImageWidgetType := 4

View file

@ -0,0 +1,36 @@
// Widget types
// End types
stn := @use("../../../../libraries/stn/src/lib.hb");
.{string, log} := stn;
.{Vec2} := stn.math
render := @use("../../../../libraries/render/src/lib.hb");
.{Surface} := render;
.{Font} := render.text
widget_types := @use("widget_types.hb")
label := @use("label.hb")
image := @use("image.hb")
Size := struct {
min_width: int,
max_width: int,
min_height: int,
max_height: int,
}
Vertical := packed struct {
magic: uint,
// array of children, idk
// use a vec or linked list or whatever
children: ^^u8,
}
Horizontal := packed struct {
magic: uint,
// array of children, idk
// use a vec or linked list or whatever
children: ^^u8,
}

View file

@ -7,7 +7,7 @@ ApplicationInfo := struct {
pNext: ^int, pNext: ^int,
application_name: ^u8, application_name: ^u8,
application_version: int, application_version: int,
engine_name: int, engine_name: ^u8,
engine_version: int, engine_version: int,
api_version: int, api_version: int,
} }

View file

@ -0,0 +1,18 @@
keycodes := @use("keycodes.hb");
.{KeyCode} := keycodes
KeyEvent := packed struct {
up: bool,
just_triggered: bool,
key: KeyCode,
}
MouseEvent := packed struct {
x_change: i8,
y_change: i8,
left: bool,
middle: bool,
right: bool,
}
GamepadEvent := struct {}

View file

@ -1,67 +1,121 @@
/*
Originally I was modelling this after the following(1). I have since changed my mind.
I am now modelling it as I see fit. This is likely not the final version.
1) https://www.libsdl.org/release/SDL-1.2.15/include/SDL_keysym.h
*/
KeyCode := u32 KeyCode := u32
// https://www.libsdl.org/release/SDL-1.2.15/include/SDL_keysym.h // Typically this is not a keycode you will ever recieve.
Backspace := KeyCode.(8) None := KeyCode.(0)
Tab := KeyCode.(9)
Clear := KeyCode.(12)
Return := KeyCode.(13)
Pause := KeyCode.(19)
Escape := KeyCode.(27)
Space := KeyCode.(32)
A := KeyCode.(97) Escape := KeyCode.(1)
/* /* Alphabet keycodes */
ETC
*/
Z := KeyCode.(122)
Delete := KeyCode.(127) A := KeyCode.(2)
B := KeyCode.(3)
C := KeyCode.(4)
D := KeyCode.(5)
E := KeyCode.(6)
F := KeyCode.(7)
G := KeyCode.(8)
H := KeyCode.(9)
I := KeyCode.(10)
J := KeyCode.(11)
K := KeyCode.(12)
L := KeyCode.(13)
M := KeyCode.(14)
N := KeyCode.(15)
O := KeyCode.(16)
P := KeyCode.(17)
Q := KeyCode.(18)
R := KeyCode.(19)
S := KeyCode.(20)
T := KeyCode.(21)
U := KeyCode.(22)
V := KeyCode.(23)
W := KeyCode.(24)
X := KeyCode.(25)
Y := KeyCode.(26)
Z := KeyCode.(27)
/* /* Numeric keycodes*/
ETC
*/
KeypadNumber0 := KeyCode.(256) Number0 := KeyCode.(28)
KeypadNumber1 := KeyCode.(257) Number1 := KeyCode.(29)
KeypadNumber2 := KeyCode.(258) Number2 := KeyCode.(30)
KeypadNumber3 := KeyCode.(259) Number3 := KeyCode.(31)
KeypadNumber4 := KeyCode.(260) Number4 := KeyCode.(32)
KeypadNumber5 := KeyCode.(261) Number5 := KeyCode.(33)
KeypadNumber6 := KeyCode.(262) Number6 := KeyCode.(34)
KeypadNumber7 := KeyCode.(263) Number7 := KeyCode.(35)
KeypadNumber8 := KeyCode.(264) Number8 := KeyCode.(36)
KeypadNumber9 := KeyCode.(265) Number9 := KeyCode.(37)
KeypadPeriod := KeyCode.(266) KeypadNumber0 := KeyCode.(38)
KeypadDivide := KeyCode.(267) KeypadNumber1 := KeyCode.(39)
KeypadMultiply := KeyCode.(268) KeypadNumber2 := KeyCode.(40)
KeypadMinus := KeyCode.(269) KeypadNumber3 := KeyCode.(41)
KeypadPlus := KeyCode.(270) KeypadNumber4 := KeyCode.(42)
KeypadEnter := KeyCode.(271) KeypadNumber5 := KeyCode.(43)
KeypadEquals := KeyCode.(272) KeypadNumber6 := KeyCode.(44)
KeypadNumber7 := KeyCode.(45)
KeypadNumber8 := KeyCode.(46)
KeypadNumber9 := KeyCode.(47)
NumLock := KeyCode.(300) KeypadPeriod := KeyCode.(48)
CapsLock := KeyCode.(301) KeypadDivide := KeyCode.(49)
ScrollLock := KeyCode.(302) KeypadMultiply := KeyCode.(50)
KeypadMinus := KeyCode.(51)
KeypadPlus := KeyCode.(52)
KeypadEnter := KeyCode.(53)
KeypadEquals := KeyCode.(54)
RightShift := KeyCode.(303) Delete := KeyCode.(55)
LeftShift := KeyCode.(304) /* Locking Keys */
NumLock := KeyCode.(56)
RightControl := KeyCode.(305) CapsLock := KeyCode.(57)
LeftControl := KeyCode.(306) ScrollLock := KeyCode.(58)
RightAlt := KeyCode.(307)
LeftAlt := KeyCode.(308)
RightMeta := KeyCode.(309)
LeftMeta := KeyCode.(310)
/* Left "Windows" key */
LeftSuper := KeyCode.(311)
/* Right "Windows" key */
RightSuper := KeyCode.(312)
/* "Alt Gr" key */ /* "Alt Gr" key */
Mode := KeyCode.(313) Mode := KeyCode.(59)
/* Multi-key compose key */ /* Multi-key compose key */
Compose := KeyCode.(314) Compose := KeyCode.(60)
LeftAlt := KeyCode.(61)
LeftControl := KeyCode.(62)
LeftMeta := KeyCode.(63)
LeftShift := KeyCode.(64)
/* Left "Windows" key */
LeftSuper := KeyCode.(65)
RightAlt := KeyCode.(66)
RightControl := KeyCode.(67)
RightMeta := KeyCode.(68)
RightShift := KeyCode.(69)
/* Right "Windows" key */
RightSuper := KeyCode.(70)
/*
This block of any triggers on any press of any of the keys.
Typically this is the event to care about.
*/
AnyNumber0 := KeyCode.(71)
AnyNumber1 := KeyCode.(72)
AnyNumber2 := KeyCode.(73)
AnyNumber3 := KeyCode.(74)
AnyNumber4 := KeyCode.(75)
AnyNumber5 := KeyCode.(76)
AnyNumber6 := KeyCode.(77)
AnyNumber7 := KeyCode.(78)
AnyNumber8 := KeyCode.(79)
AnyNumber9 := KeyCode.(80)
AnyAlt := KeyCode.(81)
AnyControl := KeyCode.(82)
AnyMeta := KeyCode.(83)
AnyShift := KeyCode.(84)
/* Any "Windows" key */
AnySuper := KeyCode.(85)

View file

@ -1,22 +1,36 @@
keycodes := @use("keycodes.hb"); stn := @use("../../stn/src/lib.hb");
.{KeyCode} := keycodes .{log, buffer, memory} := stn
keycodes := @use("keycodes.hb")
MouseEvent := struct { events := @use("events.hb");
x_change: u8, .{KeyEvent, MouseEvent} := events
y_change: u8,
left: u8, recieve_key_event := fn(): ?KeyEvent {
middle: u8, kevent := KeyEvent.(false, false, 0)
right: u8,
buf_id := buffer.search("PS/2 Keyboard\0")
// Read out of the Keyboard buffer here
buffer.recv(KeyEvent, buf_id, &kevent)
if kevent.just_triggered {
return kevent
} }
KeyEvent := struct { return null
// 0 if down
// 1 if up
up: u8,
// 0 if not just triggered
// 1 if just triggered
just_triggered: u8,
key: KeyCode,
} }
GamepadEvent := struct {} recieve_mouse_event := fn(): ?MouseEvent {
mevent := MouseEvent.(0, 0, false, false, false)
buf_id := buffer.search("PS/2 Mouse\0")
// Read out of the Mouse buffer here
buffer.recv(MouseEvent, buf_id, &mevent)
if mevent.x_change != 0 | mevent.y_change != 0 | mevent.left | mevent.middle | mevent.right {
return mevent
}
return null
}

View file

@ -0,0 +1,27 @@
# Images
- Animation
# API
- Colour operations:
- Alpha Composite
- Invert
- Surface Operations:
- FlipH
- Resize
- Wrap the colour operations
- Tile
- Gradient overlay
- Draw operations:
- Curve raster algorithm
- VGA font fast blit
- Polygon
- Rounded rects
# Backend
- SVGA Driver
- Support whatever vulkan stuff able is cooking
# Bits and bobs on the table
- Funny 3D Renderer
- stn.memory.swap & kernel message
- Make memory.{copy, set} smart

View file

@ -0,0 +1,49 @@
.{Color, Surface, new_surface, put_surface} := @use("../lib.hb");
.{log} := @use("../../../stn/src/lib.hb")
BitmapFileHeader := packed struct {
magic: u16,
size: u32,
reserved_1: u16,
reserved_2: u16,
offset: u32,
}
BitmapInfoHeader := packed struct {
size: u32,
width: i32,
height: i32,
planes: u16,
bits: u16,
compression: u32,
image_size: u32,
x_resolution: i32,
y_resolution: i32,
n_colours: u32,
important_colours: u32,
}
BitmapColorHeader := packed struct {
red_mask: u32,
green_mask: u32,
blue_mask: u32,
alpha_mask: u32,
color_space_type: u32,
unused: u32,
}
from := fn(bmp: ^u8): ?Surface {
file_header := @as(^BitmapFileHeader, @bitcast(bmp))
info_header := @as(^BitmapInfoHeader, @bitcast(bmp + @sizeof(BitmapFileHeader)))
if file_header.magic != 0x4D42 | info_header.width == 0 | info_header.height == 0 {
log.error("Invalid BMP image.\0")
return null
}
lhs := Surface.(@bitcast(bmp + file_header.offset), info_header.width, info_header.height, info_header.width * info_header.height)
rhs := new_surface(info_header.width, info_header.height)
put_surface(rhs, lhs, .(0, 0), true)
return rhs
}

View file

@ -0,0 +1,31 @@
.{log} := @use("../../../stn/src/lib.hb");
.{Surface} := @use("../lib.hb")
bmp := @use("bmp.hb")
qoi := @use("qoi.hb")
$BMP := 0x4D42
$QOI := 0x66696F71
get_format := fn(file: ^u8): ?uint {
if *@as(^u16, @bitcast(file)) == BMP {
return BMP
} else if *@as(^u32, @bitcast(file)) == QOI {
return QOI
} else {
return null
}
}
from := fn(file: ^u8): ?Surface {
format := get_format(file)
if format == null {
log.error("Could not detect image format.\0")
return null
} else if format == BMP {
return bmp.from(file)
} else if format == QOI {
return qoi.from(file)
}
return null
}

View file

@ -0,0 +1,101 @@
.{Color, Surface, new_surface} := @use("../lib.hb");
.{log} := @use("../../../stn/src/lib.hb")
/* source:
https://github.com/phoboslab/qoi/blob/master/qoi.h */
$QOI_SRGB := 0
$QOI_LINEAR := 1
$QOI_OP_INDEX := 0x0
$QOI_OP_DIFF := 0x40
$QOI_OP_LUMA := 0x80
$QOI_OP_RUN := 0xC0
$QOI_OP_RGB := 0xFE
$QOI_OP_RGBA := 0xFF
$QOI_MASK_2 := 0xC0
$QOI_COLOR_HASH := fn(c: Color): u8 {
return (c.r * 3 + c.g * 5 + c.b * 7 + c.a * 11) % 64
}
$QOI_MAGIC := 0x716F6966
$QOI_PIXELS_MAX := 400000000
QuiteOkayHeader := packed struct {
magic: u32,
width: u32,
height: u32,
channels: u8,
colorspace: u8,
}
be_to_le := fn(big: u32): u32 {
return big >> 24 | big >> 8 & 0xFF00 | big << 8 & 0xFF0000 | big << 24
}
from := fn(qoi: ^u8): ?Surface {
header := @as(^QuiteOkayHeader, @bitcast(qoi))
qoi += @sizeof(QuiteOkayHeader)
width := be_to_le(header.width)
height := be_to_le(header.height)
if be_to_le(header.magic) != QOI_MAGIC | width == 0 | height == 0 | header.channels < 3 | header.channels > 4 {
log.error("Invalid QOI image.\0")
return null
}
surface := new_surface(width, height)
index := @as([Color; 64], idk)
run := 0
px := Color.(0, 0, 0, 255)
px_pos := 0
total_pixels := width * height
loop if px_pos >= total_pixels break else {
if run > 0 {
run -= 1
} else {
b1 := *qoi
qoi += 1
if b1 == QOI_OP_RGB {
px.r = *qoi
px.g = *(qoi + 1)
px.b = *(qoi + 2)
qoi += 3
} else if b1 == QOI_OP_RGBA {
px.r = *qoi
px.g = *(qoi + 1)
px.b = *(qoi + 2)
px.a = *(qoi + 3)
qoi += 4
} else if (b1 & QOI_MASK_2) == QOI_OP_INDEX {
px = index[b1 & 0x3F]
} else if (b1 & QOI_MASK_2) == QOI_OP_DIFF {
px.r = px.r + (b1 >> 4 & 0x3) - 2 & 0xFF
px.g = px.g + (b1 >> 2 & 0x3) - 2 & 0xFF
px.b = px.b + (b1 & 0x3) - 2 & 0xFF
} else if (b1 & QOI_MASK_2) == QOI_OP_LUMA {
b2 := *qoi
vg := (b1 & 0x3F) - 32
px.r = px.r + vg - 8 + (b2 >> 4 & 0xF) & 0xFF
px.g = px.g + vg & 0xFF
px.b = px.b + vg - 8 + (b2 & 0xF) & 0xFF
qoi += 1
} else if (b1 & QOI_MASK_2) == QOI_OP_RUN {
run = b1 & 0x3F
}
index[QOI_COLOR_HASH(px)] = px
};
*(surface.buf + px_pos) = px
px_pos += 1
}
return surface
}

View file

@ -1,47 +1,57 @@
svga := @use("svga.hb")
software := @use("software.hb") software := @use("software.hb")
image := @use("image/lib.hb")
text := @use("text.hb")
// default mode // default mode
mode := software mode := software
init := mode.init init := mode.init
doublebuffer := mode.doublebuffer doublebuffer := mode.doublebuffer
Surface := mode.Surface
new_surface := mode.new_surface
surface_from_ptr := mode.surface_from_ptr
clone_surface := mode.clone_surface
free_surface := mode.free_surface
index := mode.index
indexptr := mode.indexptr
// Colours // Colours
Color := mode.Color Color := packed struct {b: u8, g: u8, r: u8, a: u8}
white := mode.white $white := Color.(255, 255, 255, 255)
black := mode.black $black := Color.(0, 0, 0, 255)
gray := mode.gray $gray := Color.(127, 127, 127, 255)
red := mode.red $red := Color.(0, 0, 205, 255)
green := mode.green $green := Color.(0, 205, 0, 255)
yellow := mode.yellow $yellow := Color.(0, 205, 205, 255)
blue := mode.blue $blue := Color.(205, 0, 0, 255)
magenta := mode.magenta $magenta := Color.(205, 0, 205, 255)
cyan := mode.cyan $cyan := Color.(205, 205, 0, 255)
light_gray := mode.light_gray $light_gray := Color.(229, 229, 229, 255)
light_red := mode.light_red $light_red := Color.(0, 0, 255, 255)
light_green := mode.light_green $light_green := Color.(0, 255, 0, 255)
light_yellow := mode.light_yellow $light_yellow := Color.(0, 255, 255, 255)
light_blue := mode.light_blue $light_blue := Color.(255, 0, 0, 255)
light_magenta := mode.light_magenta $light_magenta := Color.(255, 0, 255, 255)
light_cyan := mode.light_cyan $light_cyan := Color.(255, 255, 0, 255)
// Drawing // Drawing
put_pixel := mode.put_pixel put_pixel := mode.put_pixel
put_rect := mode.put_rect put_rect := mode.put_rect
put_filled_rect := mode.put_filled_rect put_filled_rect := mode.put_filled_rect
put_trirect := mode.put_trirect
put_circle := mode.put_circle
put_filled_circle := mode.put_filled_circle
put_textured_circle := mode.put_textured_circle
put_line := mode.put_line put_line := mode.put_line
put_vline := mode.put_vline
put_hline := mode.put_hline
clear := mode.clear clear := mode.clear
put_surface := mode.put_surface
put_text := mode.put_text
// thanks peony for these three!
//put_trirect := mode.put_trirect
//put_vline := mode.put_vline
//put_hline := mode.put_hline
// Display // Display
width := mode.width
height := mode.height
dimensions := mode.dimensions
set_height := mode.set_height
set_width := mode.set_width
set_dimensions := mode.set_dimensions
sync := mode.sync sync := mode.sync
// Math
UVec2 := struct {x: uint, y: uint}
IVec2 := struct {x: int, y: int}

View file

@ -1,197 +1,116 @@
.{math, memory} := @use("../../stn/src/lib.hb"); .{math, memory, dt} := @use("../../stn/src/lib.hb");
.{dt_get} := @use("../../dt_api/src/lib.hb"); .{Color, text} := @use("lib.hb");
.{IVec2} := @use("lib.hb") .{get_glyph, get_glyph_unicode, Font, UNC_TABLE_SIZE} := text;
.{Vec2} := math
Color := struct {b: u8, g: u8, r: u8, a: u8} // safety: don't use before init() or you will get a memory access violation
white := Color.(255, 255, 255, 255) framebuffer := memory.dangling(Color)
black := Color.(0, 0, 0, 255)
gray := Color.(127, 127, 127, 255)
red := Color.(0, 0, 205, 255)
green := Color.(0, 205, 0, 255)
yellow := Color.(0, 205, 205, 255)
blue := Color.(205, 0, 0, 255)
magenta := Color.(205, 0, 205, 255)
cyan := Color.(205, 205, 0, 255)
light_gray := Color.(229, 229, 229, 255)
light_red := Color.(0, 0, 255, 255)
light_green := Color.(0, 255, 0, 255)
light_yellow := Color.(0, 255, 255, 255)
light_blue := Color.(255, 0, 0, 255)
light_magenta := Color.(255, 0, 255, 255)
light_cyan := Color.(255, 255, 0, 255)
// might not work for some resolutions, but needs to be comptime because... Surface := struct {
copy_pixels := 0xC000 >> 2
ctx := @as(Context, idk)
// some of these are redudant holdovers from fb_driver
// will keep them for future work if necessary
Context := struct {
fb: ^Color,
bb: ^Color,
buf: ^Color, buf: ^Color,
width: int, width: uint,
height: int, height: uint,
partitions: int, size: uint,
pixels: int,
bb_pages: int,
double_buffer: bool,
} }
init := fn(): void { new_surface := fn(width: uint, height: uint): Surface {
width := dt_get("framebuffer/fb0/width\0") return .(
height := dt_get("framebuffer/fb0/height\0") memory.alloc(Color, width * height),
// width := 1024
// height := 768
pixels := width * height
bytes := pixels << 2
partitions := pixels / copy_pixels
pages := 1 + bytes >> 12
back_buffer := create_back_buffer(pages)
ctx = Context.{
fb: dt_get("framebuffer/fb0/ptr\0"),
bb: back_buffer,
buf: back_buffer,
width, width,
height, height,
partitions, width * height,
pixels, )
bb_pages: pages,
double_buffer: true,
}
return
} }
doublebuffer := fn(enable: bool): void { clone_surface := fn(surface: ^Surface): Surface {
if enable { new := new_surface(surface.width, surface.height)
ctx.buf = ctx.bb memory.copy(Color, surface.buf, new.buf, @intcast(surface.size))
return new
}
init := fn(doublebuffer: bool): Surface {
framebuffer = dt.get(^Color, "framebuffer/fb0/ptr\0")
width := dt.get(uint, "framebuffer/fb0/width\0")
height := dt.get(uint, "framebuffer/fb0/height\0")
if doublebuffer {
return new_surface(width, height)
} else { } else {
ctx.buf = ctx.fb return .(framebuffer, width, height, width * height)
} }
ctx.double_buffer = enable }
$clear := fn(surface: Surface, color: Color): void {
memory.set(Color, &color, surface.buf, surface.width * surface.height)
}
$sync := fn(surface: Surface): void {
memory.copy(Color, surface.buf, framebuffer, @bitcast(surface.width * surface.height))
}
$index := fn(surface: Surface, x: uint, y: uint): uint {
return x + surface.width * y
}
$indexptr := fn(surface: Surface, x: uint, y: uint): ^Color {
return surface.buf + index(surface, x, y)
}
$put_pixel := fn(surface: Surface, pos: Vec2(uint), color: Color): void {
return *indexptr(surface, pos.x, pos.y) = color
}
put_filled_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void {
top_start_idx := indexptr(surface, pos.x, pos.y)
bottom_start_idx := indexptr(surface, pos.x, pos.y + tr.y - 1)
rows_to_fill := tr.y
loop if rows_to_fill <= 1 break else {
memory.set(Color, &color, top_start_idx, tr.x)
memory.set(Color, &color, bottom_start_idx, tr.x)
top_start_idx += surface.width
bottom_start_idx -= surface.width
rows_to_fill -= 2
}
if rows_to_fill == 1 {
memory.set(Color, &color, top_start_idx, tr.x)
}
return return
} }
create_back_buffer := fn(pages: int): ^Color { put_rect := fn(surface: Surface, pos: Vec2(uint), tr: Vec2(uint), color: Color): void {
if pages <= 0xFF { start_idx := indexptr(surface, pos.x, pos.y)
return @bitcast(@inline(memory.request_page, pages)) end_idx := indexptr(surface, pos.x, pos.y + tr.y)
} right_start_idx := indexptr(surface, pos.x + tr.x, pos.y)
ptr := @inline(memory.request_page, 255)
remaining := pages - 0xFF loop if start_idx > end_idx break else {
loop if remaining <= 0 break else { *start_idx = color;
if remaining < 0xFF { *right_start_idx = color
memory.request_page(remaining) start_idx += surface.width
} else { right_start_idx += surface.width
memory.request_page(0xFF)
}
remaining -= 0xFF
}
return @bitcast(ptr)
} }
clear := fn(color: Color): void { memory.set(Color, &color, indexptr(surface, pos.x, pos.y), @bitcast(tr.x + 1))
cursor := ctx.buf memory.set(Color, &color, indexptr(surface, pos.x, pos.y + tr.y), @bitcast(tr.x + 1))
boundary := cursor + 512
loop if cursor == boundary break else {
*cursor = color
cursor += 1
}
boundary += 512 * 7
loop if cursor == boundary break else {
*@as(^[Color; 512], @bitcast(cursor)) = *@as(^[Color; 512], @bitcast(ctx.buf))
cursor += 512
}
boundary += copy_pixels - 4096
loop if cursor == boundary break else {
*@as(^[Color; 4096], @bitcast(cursor)) = *@as(^[Color; 4096], @bitcast(ctx.buf))
cursor += 4096
}
boundary += (ctx.partitions - 1) * copy_pixels
loop if cursor == boundary break else {
*@as(^[Color; copy_pixels], @bitcast(cursor)) = *@as(^[Color; copy_pixels], @bitcast(ctx.buf))
cursor += @sizeof([u8; copy_pixels])
}
return return
} }
sync := fn(): void { put_line_low := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void {
if ctx.double_buffer { dx := @as(int, @bitcast(p1.x - p0.x))
bb := ctx.buf dy := @as(int, @bitcast(p1.y - p0.y))
fb := ctx.fb
boundary := bb + ctx.pixels
loop if bb == boundary break else {
*@as(^[Color; copy_pixels], @bitcast(fb)) = *@as(^[Color; copy_pixels], @bitcast(bb))
bb += copy_pixels
fb += copy_pixels
}
}
return
}
width := fn(): int {
return ctx.width
}
height := fn(): int {
return ctx.height
}
screenidx := fn(x: int, y: int): int {
return x + ctx.width * y
}
put_pixel := fn(pos: IVec2, color: Color): void {
*(ctx.buf + @inline(screenidx, pos.x, pos.y)) = color
return
}
put_filled_rect := fn(pos: IVec2, tr: IVec2, color: Color): void {
x := pos.x
y := pos.y
end := pos + tr
loop if x == end.x break else {
loop if y == end.y break else {
*(ctx.buf + @inline(screenidx, x, y)) = color
y += 1
}
x += 1
y = pos.y
}
return
}
put_rect := fn(pos: IVec2, tr: IVec2, color: Color): void {
x := pos.x
y := pos.y
end := pos + tr
loop if y == end.y break else {
*(ctx.buf + @inline(screenidx, x, y)) = color;
*(ctx.buf + @inline(screenidx, x + tr.x, y)) = color
y += 1
}
y = pos.y
loop if x == end.x break else {
*(ctx.buf + @inline(screenidx, x, y)) = color;
*(ctx.buf + @inline(screenidx, x, y + tr.y)) = color
x += 1
}
return
}
put_line_low := fn(p0: IVec2, p1: IVec2, color: Color): void {
dx := p1.x - p0.x
dy := p1.y - p0.y
yi := 1 yi := 1
if dy < 0 { if dy < 0 {
yi = -1 yi = -1
dy = -dy dy = -dy
} }
D := 2 * dy - dx D := @as(int, 2) * dy - dx
y := p0.y y := p0.y
x := p0.x x := p0.x
loop if x == p1.x break else { loop if x == p1.x break else {
*(ctx.buf + @inline(screenidx, x, y)) = color *indexptr(surface, x, y) = color
if D > 0 { if D > 0 {
y += yi y += yi
D += 2 * (dy - dx) D += 2 * (dy - dx)
@ -203,19 +122,19 @@ put_line_low := fn(p0: IVec2, p1: IVec2, color: Color): void {
return return
} }
put_line_high := fn(p0: IVec2, p1: IVec2, color: Color): void { put_line_high := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void {
dx := p1.x - p0.x dx := @as(int, @bitcast(p1.x - p0.x))
dy := p1.y - p0.y dy := @as(int, @bitcast(p1.y - p0.y))
xi := 1 xi := 1
if dy < 0 { if dy < 0 {
xi = -1 xi = -1
dx = -dx dx = -dx
} }
D := 2 * dx - dy D := @as(int, 2) * dx - dy
x := p0.x x := p0.x
y := p0.y y := p0.y
loop if y == p1.y break else { loop if y == p1.y break else {
*(ctx.buf + @inline(screenidx, x, y)) = color *indexptr(surface, x, y) = color
if D > 0 { if D > 0 {
x += xi x += xi
D += 2 * (dx - dy) D += 2 * (dx - dy)
@ -227,35 +146,327 @@ put_line_high := fn(p0: IVec2, p1: IVec2, color: Color): void {
return return
} }
put_line := fn(p0: IVec2, p1: IVec2, color: Color): void { put_line := fn(surface: Surface, p0: Vec2(uint), p1: Vec2(uint), color: Color): void {
if @inline(math.abs, p1.y - p0.y) < @inline(math.abs, p1.x - p0.x) { if math.abs(uint, p1.y - p0.y) < math.abs(uint, p1.x - p0.x) {
if p0.x > p1.x { if p0.x > p1.x {
@inline(put_line_low, p1, p0, color) @inline(put_line_low, surface, p1, p0, color)
} else { } else {
@inline(put_line_low, p0, p1, color) @inline(put_line_low, surface, p0, p1, color)
} }
} else { } else {
if p0.y > p1.y { if p0.y > p1.y {
@inline(put_line_high, p1, p0, color) @inline(put_line_high, surface, p1, p0, color)
} else { } else {
@inline(put_line_high, p0, p1, color) @inline(put_line_high, surface, p0, p1, color)
} }
} }
return return
} }
set_height := fn(new: int): void { put_surface := fn(surface: Surface, top: Surface, pos: Vec2(uint), flip_v: bool): void {
src_top_cursor := top.buf
src_bottom_cursor := top.buf + top.width * (top.height - 1)
dst_top_idx := indexptr(surface, pos.x, pos.y)
dst_bottom_idx := indexptr(surface, pos.x, pos.y + top.height - 1)
dst_increment := surface.width
if flip_v {
dst_increment = -dst_increment
tmp := dst_top_idx
dst_top_idx = dst_bottom_idx
dst_bottom_idx = tmp
}
rows_to_copy := top.height
loop if rows_to_copy <= 1 break else {
memory.copy(Color, src_top_cursor, dst_top_idx, top.width)
memory.copy(Color, src_bottom_cursor, dst_bottom_idx, top.width)
dst_top_idx += dst_increment
dst_bottom_idx -= dst_increment
src_top_cursor += top.width
src_bottom_cursor -= top.width
rows_to_copy -= 2
}
if rows_to_copy == 1 {
memory.copy(Color, src_top_cursor, dst_top_idx, top.width)
}
return return
} }
set_width := fn(new: int): void { // peony-made
put_trirect := fn(surface: Surface, pos: Vec2(uint), size: Vec2(int), color0: Color, color1: Color): void {
step := Vec2(int).(1, 1)
if size.x < 0 {
step.x = -1
}
if size.y < 0 {
step.y /= @bitcast(size.x)
}
start_y := pos.y
target := pos + @bitcast(size)
loop if pos.x == target.x break else {
@inline(put_vline, surface, pos.x, pos.y, target.y, color0)
@inline(put_vline, surface, pos.x, pos.y, start_y, color1)
pos += @bitcast(step)
}
return return
} }
dimensions := fn(): IVec2 { // peony-made
return .(ctx.width, ctx.height) put_vline := fn(surface: Surface, x: uint, y0: uint, y1: uint, color: Color): void {
if y1 < y0 {
tmp := y0
y0 = y1
y1 = tmp
}
y := y0
loop if y == y1 break else {
*indexptr(surface, x, y) = color
y += 1
}
return
}
// peony-made
put_hline := fn(surface: Surface, y: uint, x0: uint, x1: uint, color: Color): void {
if x1 < x0 {
tmp := x0
x0 = x1
x1 = tmp
}
// x0 = math.min(x0, x1)
memory.set(Color, &color, indexptr(surface, x0, y), @bitcast(x1 - x0 - 1))
return
}
put_circle := fn(surface: Surface, pos: Vec2(uint), radius: uint, color: Color): void {
x := 0
y := radius
error := @as(int, 3) - @intcast(2 * radius);
*indexptr(surface, pos.x + radius, pos.y) = color;
*indexptr(surface, pos.x - radius, pos.y) = color;
*indexptr(surface, pos.x, pos.y + radius) = color;
*indexptr(surface, pos.x, pos.y - radius) = color
loop if y < x break else {
x += 1
if error > 0 {
y -= 1
error += 4 * (@intcast(x) - @intcast(y)) + 10
} else {
error += 4 * @intcast(x) + 6
};
*indexptr(surface, pos.x + x, pos.y + y) = color;
*indexptr(surface, pos.x + y, pos.y + x) = color;
*indexptr(surface, pos.x - x, pos.y + y) = color;
*indexptr(surface, pos.x - y, pos.y + x) = color;
*indexptr(surface, pos.x + x, pos.y - y) = color;
*indexptr(surface, pos.x + y, pos.y - x) = color;
*indexptr(surface, pos.x - x, pos.y - y) = color;
*indexptr(surface, pos.x - y, pos.y - x) = color
}
return
}
put_filled_circle := fn(surface: Surface, pos: Vec2(uint), radius: uint, color: Color): void {
x := 0
y := radius
error := @as(int, 3) - @intcast(2 * radius)
put_hline(surface, pos.y - x, pos.x - radius, pos.x + radius, color);
*indexptr(surface, pos.x, pos.y + radius) = color;
*indexptr(surface, pos.x, pos.y - radius) = color
loop if y < x break else {
x += 1
if error > 0 {
put_hline(surface, pos.y + y, pos.x - x, pos.x + x, color)
put_hline(surface, pos.y - y, pos.x - x, pos.x + x, color)
y -= 1
error += 4 * (@intcast(x) - @intcast(y)) + 10
} else {
error += 4 * @intcast(x) + 6
}
put_hline(surface, pos.y + x, pos.x - y, pos.x + y, color)
put_hline(surface, pos.y - x, pos.x - y, pos.x + y, color)
}
return
}
put_textured_circle := fn(surface: Surface, source: Surface, source_pos: Vec2(uint), pos: Vec2(uint), radius: uint): void {
x := 0
y := radius
error := @as(int, 3) - @intcast(2 * radius)
memory.copy(Color, indexptr(source, source_pos.x - y, source_pos.y), indexptr(surface, pos.x - y, pos.y), 2 * y);
*indexptr(surface, pos.x, pos.y + y) = *indexptr(source, source_pos.x, source_pos.y + y);
*indexptr(surface, pos.x, pos.y - y) = *indexptr(source, source_pos.x, source_pos.y - y)
loop if y < x break else {
x += 1
if error > 0 {
memory.copy(Color, indexptr(source, source_pos.x - x, source_pos.y + y), indexptr(surface, pos.x - x, pos.y + y), 2 * x)
memory.copy(Color, indexptr(source, source_pos.x - x, source_pos.y - y), indexptr(surface, pos.x - x, pos.y - y), 2 * x)
y -= 1
error += 4 * (@intcast(x) - @intcast(y)) + 10
} else {
error += 4 * @intcast(x) + 6
}
memory.copy(Color, indexptr(source, source_pos.x - y, source_pos.y + x), indexptr(surface, pos.x - y, pos.y + x), 2 * y)
memory.copy(Color, indexptr(source, source_pos.x - y, source_pos.y - x), indexptr(surface, pos.x - y, pos.y - x), 2 * y)
}
return
}
utf8_len_table := [u8].(0, 0, 2, 3)
put_text := fn(surface: Surface, font: Font, pos: Vec2(uint), color: Color, str: ^u8): void {
cursor := Vec2(uint).(pos.x, pos.y)
max_y := surface.height - font.height
next_line_y := font.height + font.line_gap
char_advance := font.width + font.char_gap
surface_width := surface.width
loop if *str == 0 break else {
if cursor.y > max_y break
glyph_data := @as(^u8, idk)
code_point := @as(uint, 0)
if (*str & 0x80) == 0 {
if *str == 10 {
cursor.x = pos.x
cursor.y += next_line_y
str += 1
continue
}
if font.unicode == null {
if *str > font.num_glyphs {
str += 1
continue
}
glyph_data = get_glyph(font, *str)
} else {
if *str < UNC_TABLE_SIZE {
glyph_index := *(font.unicode + *str)
if glyph_index == 0xFFFF {
str += 1
continue
}
glyph_data = font.data + glyph_index * font.bytes_per_glyph
} else {
str += 1
continue
}
}
str += 1
} else if font.unicode != null {
first_byte := *str
num_bytes := @as(uint, 0)
num_bytes = utf8_len_table[first_byte >> 5 & 0x3]
if num_bytes == 0 {
str += 1
continue
}
code_point = first_byte & (0x7F >> num_bytes | 0x1F)
valid_sequence := true
bytes_processed := 1
loop if bytes_processed >= num_bytes break else {
str += 1
if *str == 0 | (*str & 0xC0) != 0x80 {
valid_sequence = false
}
if valid_sequence == false {
break
}
code_point = code_point << 6 | *str & 0x3F
bytes_processed += 1
}
if valid_sequence == false {
str += 1
continue
}
str += 1
if code_point == 10 {
cursor.x = pos.x
cursor.y += next_line_y
continue
}
if code_point >= UNC_TABLE_SIZE {
continue
}
glyph_index := *(font.unicode + code_point)
if glyph_index == 0xFFFF {
continue
}
glyph_data = font.data + glyph_index * font.bytes_per_glyph
}
if cursor.x + font.width >= surface_width {
cursor.x = pos.x
cursor.y += next_line_y
}
dest := indexptr(surface, cursor.x, cursor.y)
rows := font.height
loop if rows == 0 break else {
byte := *glyph_data
pixel_dest := dest
mask := @as(u8, 0x80)
bits := font.width
loop if bits == 0 break else {
if (byte & mask) != 0 {
*pixel_dest = color
}
pixel_dest += 1
mask >>= 1
if mask == 0 & bits > 0 {
glyph_data += 1
byte = *glyph_data
mask = 0x80
}
bits -= 1
}
if mask != 0x80 {
glyph_data += 1
}
dest += surface_width
rows -= 1
}
cursor.x += char_advance
} }
set_dimensions := fn(new: IVec2): void {
return return
} }

View file

@ -1,80 +0,0 @@
.{IVec2} := @use("lib.hb")
// .{pci, memory, string, log} := @use("../../stn/src/lib.hb");
Color := struct {b: u8, g: u8, r: u8, a: u8}
white := Color.(255, 255, 255, 255)
black := Color.(0, 0, 0, 255)
gray := Color.(127, 127, 127, 255)
red := Color.(0, 0, 205, 255)
green := Color.(0, 205, 0, 255)
yellow := Color.(0, 205, 205, 255)
blue := Color.(205, 0, 0, 255)
magenta := Color.(205, 0, 205, 255)
cyan := Color.(205, 205, 0, 255)
light_gray := Color.(229, 229, 229, 255)
light_red := Color.(0, 0, 255, 255)
light_green := Color.(0, 255, 0, 255)
light_yellow := Color.(0, 255, 255, 255)
light_blue := Color.(255, 0, 0, 255)
light_magenta := Color.(255, 0, 255, 255)
light_cyan := Color.(255, 255, 0, 255)
clear := fn(color: Color): void {
return
}
width := fn(): int {
return 0
}
height := fn(): int {
return 0
}
dimensions := fn(): IVec2 {
return .(0, 0)
}
put_pixel := fn(position: IVec2, color: Color): void {
return
}
put_filled_rect := fn(pos: IVec2, tr: IVec2, color: Color): void {
return
}
put_rect := fn(pos: IVec2, tr: IVec2, color: Color): void {
return
}
put_line_low := fn(p0: IVec2, p1: IVec2, color: Color): void {
return
}
// do not use, use line() instead
put_line_high := fn(p0: IVec2, p1: IVec2, color: Color): void {
return
}
put_line := fn(p0: IVec2, p1: IVec2, color: Color): void {
return
}
set_height := fn(new: int): void {
return
}
set_width := fn(new: int): void {
return
}
set_dimensions := fn(new: IVec2): void {
return
}
sync := fn(): void {
return
}
init := fn(): void {
return
}

View file

@ -0,0 +1,160 @@
.{log, memory} := @use("../../stn/src/lib.hb")
PSF1Header := packed struct {
magic: u16,
font_mode: u8,
character_size: u8,
}
PSF2Header := packed struct {
magic: u32,
version: u32,
header_size: u32,
flags: u32,
num_glyph: u32,
bytes_per_glyph: u32,
height: u32,
width: u32,
}
Font := struct {
data: ^u8,
width: uint,
height: uint,
num_glyphs: uint,
bytes_per_glyph: uint,
line_gap: uint,
char_gap: uint,
unicode: ?^u16,
}
font_from_psf1 := fn(psf: ^u8): ?Font {
header := @as(^PSF1Header, @bitcast(psf))
if header.magic != 0x436 {
log.error("failed to load psf font: not a psf1 font, idiot\0")
return null
}
psf += @sizeof(PSF1Header)
return .(
psf,
8,
header.character_size,
256,
header.character_size,
0,
0,
null,
)
}
font_from_psf2 := fn(psf: ^u8, unicode: bool): ?Font {
header := @as(^PSF2Header, @bitcast(psf))
if header.magic != 0x864AB572 {
log.error("failed to load psf font: not a psf2 font, idiot\0")
return null
}
psf += header.header_size
font := Font.(
psf,
header.width,
header.height,
header.num_glyph,
header.bytes_per_glyph,
0,
0,
null,
)
if (header.flags & 1) != 0 & unicode {
init_unicode(&font)
}
return font
}
$get_glyph := fn(font: Font, index: u8): ^u8 {
return font.data + @as(uint, index) * font.bytes_per_glyph
}
$UNC_TABLE_SIZE := 1 << 16
init_unicode := fn(font: ^Font): void {
font.unicode = memory.alloc(u16, UNC_TABLE_SIZE)
memory.set(u16, &0xFFFF, font.unicode, UNC_TABLE_SIZE)
table := font.data + font.num_glyphs * font.bytes_per_glyph
curr_glyph := @as(u16, 0)
loop if curr_glyph >= font.num_glyphs break else {
loop {
byte := *table
table += 1
if byte == 0xFF break
if byte == 0xFE {
continue
}
unicode := @as(uint, 0)
bytes_to_read := @as(uint, 1)
if (byte & 0x80) == 0 {
unicode = byte
} else if (byte & 0xE0) == 0xC0 {
unicode = byte & 0x1F
bytes_to_read = 2
} else if (byte & 0xF0) == 0xE0 {
unicode = byte & 0xF
bytes_to_read = 3
} else if (byte & 0xF8) == 0xF0 {
unicode = byte & 0x7
bytes_to_read = 4
} else {
continue
}
valid := true
loop if bytes_to_read <= 1 break else {
next_byte := *table
if (next_byte & 0xC0) != 0x80 {
valid = false
break
}
unicode = unicode << 6 | next_byte & 0x3F
table += 1
bytes_to_read -= 1
}
if valid == false continue
if bytes_to_read == 4 {
if unicode < 0x10000 | unicode > 0x10FFFF continue
if unicode <= 0xFFFF {
if unicode < UNC_TABLE_SIZE {
*(@unwrap(font.unicode) + unicode) = curr_glyph
}
} else {
unicode -= 0x10000
high_surrogate := 0xD800 | unicode >> 10 & 0x3FF
low_surrogate := 0xDC00 | unicode & 0x3FF
if high_surrogate < UNC_TABLE_SIZE {
*(@unwrap(font.unicode) + high_surrogate) = curr_glyph
}
if low_surrogate < UNC_TABLE_SIZE {
*(@unwrap(font.unicode) + low_surrogate) = curr_glyph
}
}
} else {
if unicode < UNC_TABLE_SIZE {
*(@unwrap(font.unicode) + unicode) = curr_glyph
}
}
}
curr_glyph += 1
}
}

View file

@ -0,0 +1,4 @@
AllocReturn := struct {
byte_count: uint,
ptr: ?^u8,
}

View file

@ -0,0 +1,90 @@
.{log, panic, memory} := @use("../lib.hb")
alloc_return := @use("alloc_return.hb")
/* the block size is 64 bytes, 64 blocks of 64 bytes.
this will very quickly lead to exhaustion of free blocks.
*/
BlockAlloc := struct {
// hi
state: uint,
ptr: ?^u8,
$init := fn(): Self {
alloc_page_ptr := memory.request_page(1)
state := 0xFFFFFFFFFFFFFFFF
return .(state, alloc_page_ptr)
}
alloc := fn(self: Self, alloc_type: type, count: uint): alloc_return.AllocReturn {
offset := 0
state_2 := 0
loop {
xyz := self.state & 1
abc := if xyz == 1 {
true
} else {
false
}
// check if the `offset` bit is 1, if it is move to the next offset
if abc {
offset += 1
return .(0, null)
} else {
log.info("Already Allocated\0")
}
// else {
// // self it to 1 and return the ptr to the allocation
// self.state |= a
// // return ptr + offset * 64
// if self.ptr != null {
// return .(64, self.ptr + offset * 64)
// } else {
// // panic.panic("Allocator is not inited.\0")
// // return .(0, null)
// }
// }
// there are only 64 blocks
if offset >= 64 {
return .(0, null)
}
}
}
dealloc := fn(self: Self, ptr: ^u8, alloc_type: type, count: uint): void {
// size := size_of(alloc_type)*count
size := 64
// get the size alligned to the nearest block
// rounded_size := nearest_block_size_rounded_up(size)
rounded_size := 64
state_bit_start := {
// Do math here to figure out what starting ptr corresponds to what bit
3
}
offset := 0
loop {
if rounded_size > 0 {
// set state_bit_start+offset to 0
// at the end move to the next one
offset += 1
} else {
break
}
rounded_size -= 64
}
return void
}
$deinit := fn(self: Self): void {
self.state = 0
self.ptr = null
}
}
// request a kernel page
// ptr := memory.alloc(1)

View file

@ -0,0 +1,19 @@
alloc_return := @use("alloc_return.hb")
FakeAlloc := struct {
$init := fn(): Self {
return .()
}
$alloc := fn(self: Self, alloc_type: type, count: uint): alloc_return.AllocReturn {
return .(0, null)
}
$dealloc := fn(self: Self, ptr: ^u8, alloc_type: type, count: uint): void {
return void
}
// Nothing to clean up here
$deinit := fn(self: Self): void {
return void
}
}

View file

@ -0,0 +1,2 @@
.{BlockAlloc} := @use("block_alloc.hb");
.{FakeAlloc} := @use("fake_alloc.hb")

View file

@ -0,0 +1,25 @@
allocators := @use("alloc/alloc.hb")
AStruct := struct {
a_field: u8,
}
main := fn():void{
alloc := allocators.FakeAlloc.init()
astruct := alloc.alloc(AStruct, 2)
if astruct.ptr != null{
panic("FakeAlloc actually allocated.")
}
alloc.dealloc(astruct_ptr, AStruct, 2)
alloc.deinit()
balloc := allocators.BlockAlloc.init()
bstruct_ptr := balloc.alloc(AStruct, 2)
if bstruct_ptr == null {
panic("BlockAlloc actually didn't allocate.")
}
balloc.dealloc(bstruct_ptr, AStruct, 2)
balloc.deinit()
}

View file

@ -1,22 +1,35 @@
string := @use("string.hb") string := @use("string.hb")
receive_message := fn(buffer_id: int, memory_map_location: ^u8, length: int): ^u8 { $recv := fn($Expr: type, buffer_id: uint, memory_map_location: ^Expr): void {
return @eca(4, buffer_id, memory_map_location, @sizeof(Expr))
}
$write := fn($Expr: type, buffer_id: uint, msg: ^Expr): void {
return @eca(3, buffer_id, msg, @sizeof(Expr))
}
$recv_length := fn(length: uint, memory_map_location: ^u8, buffer_id: uint): void {
return @eca(4, buffer_id, memory_map_location, length) return @eca(4, buffer_id, memory_map_location, length)
} }
send_message := fn(msg: ^u8, buffer_id: int, length: int): void { $write_length := fn(length: uint, msg: ^u8, buffer_id: uint): void {
return @eca(3, buffer_id, msg, length) return @eca(3, buffer_id, msg, length)
} }
create := fn(msg: ^u8): int { BufferMsg := packed struct {operation: u8, msg: ^u8, msg_len: uint}
msg_length := @inline(string.length, msg);
*msg = 0 create := fn(msg: ^u8): uint {
return @eca(3, 0, msg, msg_length) return @eca(3, 0, BufferMsg.(0, msg, @inline(string.length, msg)), @sizeof(BufferMsg))
} }
search := fn(msg: ^u8): int { $create_nameless := fn(): uint {
msg_length := @inline(string.length, msg); return @eca(1, 0)
*msg = 3 }
return @eca(3, 0, msg, msg_length) $delete_buffer := fn(buffer_id: uint): void {
return @eca(2, buffer_id)
}
search := fn(msg: ^u8): uint {
return @eca(3, 0, BufferMsg.(3, msg, @inline(string.length, msg)), @sizeof(BufferMsg))
} }

View file

@ -0,0 +1,5 @@
.{string} := @use("../../stn/src/lib.hb")
get := fn($Expr: type, query: ^u8): Expr {
return @eca(3, 5, query, @inline(string.length, query))
}

View file

@ -0,0 +1,189 @@
/*
* This code is an implementation of the FoldHash algorithm from https://github.com/orlp/foldhash,
* originally written by Orson Peters under the zlib license.
*
* Changes to the original code were made to meet the simplicity requirements of this implementation.
* Behaviour aims to be equivalent but not identical to the original code.
*
* Copyright (c) 2024 Orson Peters
*
* This software is provided 'as-is', without any express or implied warranty. In
* no event will the authors be held liable for any damages arising from the use of
* this software.
*
* Permission is granted to anyone to use this software for any purpose, including
* commercial applications, and to alter it and redistribute it freely, subject to
* the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must not claim
* that you wrote the original software. If you use this software in a product,
* an acknowledgment in the product documentation would be appreciated but is
* not required.
*
* 2. Altered source versions must be plainly marked as such, and must not be
* misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/;
.{math, random} := @use("../lib.hb")
$ARBITRARY0 := 0x243F6A8885A308D3
$ARBITRARY1 := 0x13198A2E03707344
$ARBITRARY2 := 0xA4093822299F31D0
$ARBITRARY3 := 0x82EFA98EC4E6C89
$ARBITRARY4 := 0x452821E638D01377
$ARBITRARY5 := 0xBE5466CF34E90C6C
$ARBITRARY6 := 0xC0AC29B7C97C50DD
$ARBITRARY7 := 0x3F84D5B5B5470917
$ARBITRARY8 := 0x9216D5D98979FB1B
$ARBITRARY9 := 0xD1310BA698DFB5AC
$FIXED_GLOBAL_SEED := [uint].(ARBITRARY4, ARBITRARY5, ARBITRARY6, ARBITRARY7)
global_seed := 0
u128 := packed struct {a: uint, b: uint}
$folded_multiply := fn(x: uint, y: uint): uint {
lx := @as(u32, @intcast(x))
ly := @as(u32, @intcast(y))
hx := x >> 32
hy := y >> 32
afull := lx * hy
bfull := hx * ly
return afull ^ (bfull << 32 | bfull >> 32)
}
hash_bytes_medium := fn(bytes: ^u8, len: uint, s0: uint, s1: uint, fold_seed: uint): uint {
lo := bytes
end := bytes + len
hi := end - 16
loop if lo >= hi break else {
a := *@as(^uint, @bitcast(lo))
b := *@as(^uint, @bitcast(lo + 8))
c := *@as(^uint, @bitcast(hi))
d := *@as(^uint, @bitcast(hi + 8))
s0 = folded_multiply(a ^ s0, c ^ fold_seed)
s1 = folded_multiply(b ^ s1, d ^ fold_seed)
hi -= 16
lo += 16
}
return s0 ^ s1
}
hash_bytes_long := fn(bytes: ^u8, len: uint, s0: uint, s1: uint, s2: uint, s3: uint, fold_seed: uint): uint {
$chunk_size := 64
chunks := len / chunk_size
remainder := len % chunk_size
ptr := bytes
i := 0
loop if i >= chunks break else {
a := *@as(^uint, @bitcast(ptr))
b := *@as(^uint, @bitcast(ptr + 8))
c := *@as(^uint, @bitcast(ptr + 16))
d := *@as(^uint, @bitcast(ptr + 24))
e := *@as(^uint, @bitcast(ptr + 32))
f := *@as(^uint, @bitcast(ptr + 40))
g := *@as(^uint, @bitcast(ptr + 48))
h := *@as(^uint, @bitcast(ptr + 56))
s0 = folded_multiply(a ^ s0, e ^ fold_seed)
s1 = folded_multiply(b ^ s1, f ^ fold_seed)
s2 = folded_multiply(c ^ s2, g ^ fold_seed)
s3 = folded_multiply(d ^ s3, h ^ fold_seed)
ptr += chunk_size
i += 1
}
s0 ^= s2
s1 ^= s3
if remainder > 0 {
remainder_start := bytes + len - math.max(uint, remainder, 16)
return hash_bytes_medium(remainder_start, math.max(uint, remainder, 16), s0, s1, fold_seed)
}
return s0 ^ s1
}
FoldHasher := struct {
accumulator: uint,
original_seed: uint,
sponge: u128,
sponge_len: u8,
fold_seed: uint,
expand_seed: uint,
expand_seed2: uint,
expand_seed3: uint,
$new := fn(seed: uint): Self {
return .(
seed,
seed,
.(0, 0),
0,
FIXED_GLOBAL_SEED[0],
FIXED_GLOBAL_SEED[1],
FIXED_GLOBAL_SEED[2],
FIXED_GLOBAL_SEED[3],
)
}
default := fn(): Self {
if global_seed == 0 {
// ! consider this "secure enough" for now
global_seed = random.any(uint)
}
return Self.new(global_seed)
}
write := fn(self: ^Self, bytes: ^u8, len: uint): void {
s0 := self.accumulator
s1 := self.expand_seed
if len <= 16 {
if len >= 8 {
s0 ^= *@bitcast(bytes)
s1 ^= *@bitcast(bytes + len - 8)
} else if len >= 4 {
s0 ^= *@as(^u32, @bitcast(bytes))
s1 ^= *@as(^u32, @bitcast(bytes + len - 4))
} else if len > 0 {
lo := *bytes
mid := *(bytes + len / 2)
hi := *(bytes + len - 1)
s0 ^= lo
s1 ^= @as(uint, hi) << 8 | mid
}
self.accumulator = folded_multiply(s0, s1)
} else if len < 256 {
self.accumulator = hash_bytes_medium(bytes, len, s0, s1, self.fold_seed)
} else {
self.accumulator = hash_bytes_long(
bytes,
len,
s0,
s1,
self.expand_seed2,
self.expand_seed3,
self.fold_seed,
)
}
}
finish := fn(self: ^Self): uint {
if self.sponge_len > 0 {
return folded_multiply(self.sponge.b ^ self.accumulator, self.sponge.a ^ self.fold_seed)
} else {
return self.accumulator
}
}
reset := fn(self: ^Self): void {
self.accumulator = self.original_seed
self.sponge = .(0, 0)
self.sponge_len = 0
}
}

View file

@ -0,0 +1,2 @@
//! NON CRYPTOGRAPHIC HASHER
foldhash := @use("foldhash.hb")

View file

@ -1,5 +1,6 @@
acs := @use("acs.hb") acs := @use("acs.hb")
allocators := @use("alloc/lib.hb")
hashers := @use("hash/lib.hb")
string := @use("string.hb") string := @use("string.hb")
log := @use("log.hb") log := @use("log.hb")
memory := @use("memory.hb") memory := @use("memory.hb")
@ -7,3 +8,15 @@ buffer := @use("buffer.hb")
math := @use("math.hb") math := @use("math.hb")
random := @use("random.hb") random := @use("random.hb")
file := @use("file_io.hb") file := @use("file_io.hb")
dt := @use("dt.hb")
process := @use("process.hb")
panic := fn(message: ?^u8): never {
log.error("Error: Panic Called, Message:\0")
if message == null {
log.error("None\0")
} else {
log.error(message)
}
die
}

View file

@ -1,15 +1,13 @@
string := @use("string.hb") string := @use("string.hb")
buffer := @use("buffer.hb")
log := fn(message: ^u8, level: u8): void { LogMsg := packed struct {level: u8, string: ^u8, strlen: uint}
message_length := @inline(string.length, message);
*(message + message_length) = level
return @eca(3, 1, message, message_length + 1) log := fn(level: u8, message: ^u8): void {
return @eca(3, 1, LogMsg.(level, message, @inline(string.length, message)), @sizeof(LogMsg))
} }
error := fn(message: ^u8): void return log(message, 0) error := fn(message: ^u8): void return @inline(log, 0, message)
warn := fn(message: ^u8): void return log(message, 1) warn := fn(message: ^u8): void return @inline(log, 1, message)
info := fn(message: ^u8): void return log(message, 2) info := fn(message: ^u8): void return @inline(log, 2, message)
debug := fn(message: ^u8): void return log(message, 3) debug := fn(message: ^u8): void return @inline(log, 3, message)
trace := fn(message: ^u8): void return log(message, 4) trace := fn(message: ^u8): void return @inline(log, 4, message)

File diff suppressed because one or more lines are too long

View file

@ -1,39 +1,76 @@
request_page := fn(page_count: u8): ^u8 { $PAGE_SIZE := 4096
msg := "\{00}\{01}xxxxxxxx\0" $MAX_ALLOC := 0xFF
msg_page_count := msg + 1; $MAX_FREE := 0xFF
*msg_page_count = page_count
return @eca(3, 2, msg, 12) $uninit := fn($Expr: type): ?Expr {
return null
} }
release_page := fn(ptr: ^u8, page_count: u8): void { $dangling := fn($Expr: type): ^Expr {
msg := "\{01}\{00}xxxxxxxx\0" return @bitcast(@alignof(Expr))
msg_page_count := msg + 1;
*msg_page_count = page_count
msg_ptr := @as(^^u8, @bitcast(msg + 2));
*msg_ptr = ptr
return @eca(3, 2, msg, 12)
} }
OutbMsg := struct {a: u8, b: u8, addr: u16, value: u8} $calc_pages := fn($Expr: type, num: uint): uint {
InbMsg := struct {a: u8, b: u8, addr: u16} return 1 + @sizeof(Expr) * num / PAGE_SIZE
OutlMsg := struct {a: u8, b: u8, addr: u16, value: u32} }
InlMsg := struct {a: u8, b: u8, addr: u16}
outb := fn(addr: u16, value: u8): void { // ! will be replaced, don't get attached
alloc := fn($Expr: type, num: uint): ^Expr {
pages := calc_pages(Expr, num)
if pages <= MAX_ALLOC {
return @bitcast(request_page(@intcast(pages)))
}
ptr := request_page(MAX_ALLOC)
remaining := pages - MAX_ALLOC
loop if remaining < MAX_ALLOC break else {
_ = request_page(MAX_ALLOC)
remaining -= MAX_ALLOC
}
_ = request_page(@intcast(remaining))
return @bitcast(ptr)
}
// ! stub
$free := fn($Expr: type, ptr: ^Expr, num: uint, nullify: bool): void {
return
}
RqPageMsg := packed struct {a: u8, count: u8}
$request_page := fn(count: u8): ^u8 {
return @eca(3, 2, &RqPageMsg.(0, count), @sizeof(RqPageMsg))
}
RlPageMsg := packed struct {a: u8, count: u8, ptr: ^u8}
$release_page := fn(ptr: ^u8, count: u8): void {
return @eca(3, 2, &RlPageMsg.(1, count, ptr), @sizeof(RlPageMsg))
}
OutbMsg := packed struct {a: u8, b: u8, addr: u16, value: u8}
$outb := fn(addr: u16, value: u8): void {
return @eca(3, 3, &OutbMsg.(1, 0, addr, value), @sizeof(OutbMsg)) return @eca(3, 3, &OutbMsg.(1, 0, addr, value), @sizeof(OutbMsg))
} }
inb := fn(addr: u16): u8 { InbMsg := packed struct {a: u8, b: u8, addr: u16}
$inb := fn(addr: u16): u8 {
return @eca(3, 3, &InbMsg.(0, 0, addr), @sizeof(InbMsg)) return @eca(3, 3, &InbMsg.(0, 0, addr), @sizeof(InbMsg))
} }
outl := fn(addr: u16, value: u32): void { OutlMsg := packed struct {a: u8, b: u8, addr: u16, value: u32}
$outl := fn(addr: u16, value: u32): void {
return @eca(3, 3, &OutlMsg.(1, 2, addr, value), @sizeof(OutlMsg)) return @eca(3, 3, &OutlMsg.(1, 2, addr, value), @sizeof(OutlMsg))
} }
inl := fn(addr: u16): u32 { InlMsg := packed struct {a: u8, b: u8, addr: u16}
$inl := fn(addr: u16): u32 {
return @eca(3, 3, &InlMsg.(0, 2, addr), @sizeof(InlMsg)) return @eca(3, 3, &InlMsg.(0, 2, addr), @sizeof(InlMsg))
} }
CopyMsg := packed struct {a: u8, count: u32, src: ^u8, dest: ^u8}
$copy := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void {
return @eca(3, 2, &CopyMsg.(4, @intcast(count * @sizeof(Expr)), @bitcast(src), @bitcast(dest)), @sizeof(CopyMsg))
}
SetMsg := packed struct {a: u8, count: u32, size: u32, src: ^u8, dest: ^u8}
$set := fn($Expr: type, src: ^Expr, dest: ^Expr, count: uint): void {
return @eca(3, 2, &SetMsg.(5, @intcast(count), @intcast(@sizeof(Expr)), @bitcast(src), @bitcast(dest)), @sizeof(SetMsg))
}

Some files were not shown because too many files have changed in this diff Show more