Improve help system (#481)
* Rename shortcut to command in help * Add dir arg to list usage * Add help to read command * Add help to write command * Add help to date command * Add manual * Add help to copy * Add help to delete * Add help to dhcp * Add help to edit * Add elf command to autocomplete * Update boot in manual * Add help to elf * Add help to env * Add help to help * Add help to hex * Add help to lisp * Add help to move * Add help to shell * Refactor user help * Update crates * Improve debug * Update binaries * Increase user process memory space * Exit process before shutdown * Add more content to manual * Update manual * Update screenshots * Update index.md * Add link to the manual * Update shields in readme * Add links to the manual * Add games to README * Rewrite the first part of the index * Remove a word * Rewrite usage section * Add demo section * Rewrite part of the doc * Remove html files * Add make website command * Commit html files * Fix indentation * Fix code block * Add monitor option to qemu to take screenshots * Update CSS * Fix CSS * Add font-family sans-serif to website
|
@ -70,9 +70,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "block-buffer"
|
||||
version = "0.10.2"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bf7fe51849ea569fd452f37822f606a5cabb684dc918707a0193fd4664ff324"
|
||||
checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71"
|
||||
dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
@ -103,9 +103,9 @@ checksum = "fbdcdcb6d86f71c5e97409ad45898af11cbc995b4ee8112d59095a28d376c935"
|
|||
|
||||
[[package]]
|
||||
name = "cpufeatures"
|
||||
version = "0.2.2"
|
||||
version = "0.2.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59a6001667ab124aebae2a495118e11d30984c3a653e99d86d58971708cf5e4b"
|
||||
checksum = "3e4c1eaa2012c47becbbad2ab175484c2a84d1185b566fb2cc5b8707343dfe58"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
@ -139,9 +139,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
|
|||
|
||||
[[package]]
|
||||
name = "generic-array"
|
||||
version = "0.14.6"
|
||||
version = "0.14.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bff49e947297f3312447abdca79f45f4738097cc82b06e72054d2223f601f1b9"
|
||||
checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
|
||||
dependencies = [
|
||||
"typenum",
|
||||
"version_check",
|
||||
|
@ -167,9 +167,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.127"
|
||||
version = "0.2.142"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "505e71a4706fa491e9b1b55f51b95d4037d0821ee40131190475f692b35b009b"
|
||||
checksum = "6a987beff54b60ffa6d51982e1aa1146bc42f19bd26be28b0586f252fccf5317"
|
||||
|
||||
[[package]]
|
||||
name = "libm"
|
||||
|
@ -200,9 +200,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.7"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
|
@ -256,7 +256,7 @@ dependencies = [
|
|||
"pc-keyboard",
|
||||
"pic8259",
|
||||
"rand 0.8.5",
|
||||
"rand_hc 0.3.1",
|
||||
"rand_hc 0.3.2",
|
||||
"raw-cpuid",
|
||||
"sha2",
|
||||
"smoltcp",
|
||||
|
@ -340,39 +340,39 @@ checksum = "e0ad9805165d0672a3efa682cac952870d8b05d98e02e08063ca7db597005338"
|
|||
|
||||
[[package]]
|
||||
name = "pic8259"
|
||||
version = "0.10.3"
|
||||
version = "0.10.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "761e92187c0fc0c3db574a512609df5fd99b0ee237132858281d68a0cdc189c8"
|
||||
checksum = "cb844b5b01db1e0b17938685738f113bfc903846f18932b378bc0eabfa40e194"
|
||||
dependencies = [
|
||||
"x86_64",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.16"
|
||||
version = "0.2.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eb9f9e6e233e5c4a35559a617bf40a4ec447db2e84c20b55a6f83167b7e57872"
|
||||
checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro-hack"
|
||||
version = "0.5.19"
|
||||
version = "0.5.20+deprecated"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dbf0c48bc1d91375ae5c3cd81e3722dff1abcf81a30960240640d223f59fe0e5"
|
||||
checksum = "dc375e1527247fe1a97d8b7156678dfe7c1af2fc075c9a4db3690ecd2a148068"
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.43"
|
||||
version = "1.0.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0a2ca2c61bc9f3d74d2886294ab7b9853abd9c1ad903a3ac7815c58989bb7bab"
|
||||
checksum = "2b63bdb0cd06f1f4dedf69b254734f9b45af66e4a031e42a7480257d9898b435"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.21"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
@ -400,7 +400,7 @@ version = "0.8.5"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
|
||||
dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -421,9 +421,9 @@ checksum = "90bde5296fc891b0cef12a6d03ddccc162ce7b2aff54160af9338f8d40df6d19"
|
|||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.6.3"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
|
@ -436,11 +436,11 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.3.1"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d51e9f596de227fda2ea6c84607f5558e196eeaf43c986b724ba4fb8fdf497e7"
|
||||
checksum = "7b363d4f6370f88d62bf586c80405657bde0f0e1b8945d47d2ad59b906cb4f54"
|
||||
dependencies = [
|
||||
"rand_core 0.6.3",
|
||||
"rand_core 0.6.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -472,9 +472,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.9"
|
||||
version = "1.0.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97477e48b4cf8603ad5f7aaf897467cf42ab4218a38ef76fb14c2d6773a6d6a8"
|
||||
checksum = "4f3208ce4d8448b3f3e7d168a73f5e0c43a61e32930de3bceeccedb388b6bf06"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
|
@ -545,9 +545,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
|||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.99"
|
||||
version = "1.0.109"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58dbef6ec655055e20b86b15a8cc6d439cca19b667537ac6a1369572d151ab13"
|
||||
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
@ -597,9 +597,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.15.0"
|
||||
version = "1.16.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
|
||||
checksum = "497961ef93d974e23eb6f433eb5fe1b7930b659f06d12dec6fc44a8f554c0bba"
|
||||
|
||||
[[package]]
|
||||
name = "uart_16550"
|
||||
|
@ -614,15 +614,15 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.3"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c4f5b37a154999a8f3f98cc23a628d850e154479cd94decf3414696e12e31aaf"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "utf8parse"
|
||||
version = "0.2.0"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||
checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
|
@ -632,9 +632,9 @@ checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
|||
|
||||
[[package]]
|
||||
name = "volatile"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e3ca98349dda8a60ae74e04fd90c7fb4d6a4fbe01e6d3be095478aa0b76f6c0c"
|
||||
checksum = "442887c63f2c839b346c192d047a7c87e73d0689c9157b00b53dcc27dd5ea793"
|
||||
|
||||
[[package]]
|
||||
name = "vte"
|
||||
|
|
9
Makefile
|
@ -18,6 +18,7 @@ nic = rtl8139# rtl8139, pcnet
|
|||
audio = sdl# sdl, coreaudio
|
||||
kvm = false
|
||||
pcap = false
|
||||
monitor = false
|
||||
|
||||
export MOROS_VERSION = $(shell git describe --tags | sed "s/^v//")
|
||||
export MOROS_MEMORY = $(memory)
|
||||
|
@ -72,6 +73,10 @@ ifeq ($(pcap),true)
|
|||
qemu-opts += -object filter-dump,id=f1,netdev=e0,file=/tmp/qemu.pcap
|
||||
endif
|
||||
|
||||
ifeq ($(monitor),true)
|
||||
qemu-opts += -monitor telnet:127.0.0.1:7777,server,nowait
|
||||
endif
|
||||
|
||||
ifeq ($(output),serial)
|
||||
qemu-opts += -display none -chardev stdio,id=s0,signal=off -serial chardev:s0
|
||||
endif
|
||||
|
@ -91,5 +96,9 @@ test:
|
|||
cargo test --release --lib --no-default-features --features serial -- \
|
||||
-m $(memory) -display none -serial stdio -device isa-debug-exit,iobase=0xf4,iosize=0x04
|
||||
|
||||
website:
|
||||
cd www && sh build.sh
|
||||
|
||||
clean:
|
||||
cargo clean
|
||||
rm -f www/*.html www/*.png
|
||||
|
|
|
@ -11,7 +11,7 @@ This project started from the [seventh post][1] of the second edition of
|
|||
[Writing an OS in Rust][2] by Philipp Oppermann and by reading the
|
||||
[OSDev wiki][3] along with many open source kernels.
|
||||
|
||||
[![Travis](https://img.shields.io/travis/vinc/moros/master.svg)](https://travis-ci.org/vinc/moros/branches)
|
||||
[![GitHub Actions](https://img.shields.io/github/actions/workflow/status/vinc/moros/rust.yml)](https://github.com/vinc/moros)
|
||||
[![Crates.io](https://img.shields.io/crates/v/moros.svg)](https://crates.io/crates/moros)
|
||||
|
||||
|
||||
|
@ -37,8 +37,9 @@ This project started from the [seventh post][1] of the second edition of
|
|||
- [x] Basic [shell](doc/shell.md)
|
||||
- [x] Basic [text editor](doc/editor.md)
|
||||
- [x] Basic [lisp](doc/lisp.md) interpreter
|
||||
- [x] Basic file and [network](doc/network.md) commands
|
||||
- [x] Basic userspace for NASM and Rust programs without alloc
|
||||
- [x] Some file and [network](doc/network.md) commands
|
||||
- [x] Some games
|
||||
- [ ] Multitasking
|
||||
- [ ] C standard library port
|
||||
|
||||
|
|
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 4.9 KiB |
BIN
doc/chess.png
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 3.7 KiB |
BIN
doc/edit.png
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 7.2 KiB |
BIN
doc/find.png
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 7.1 KiB |
After Width: | Height: | Size: 5.9 KiB |
75
doc/index.md
|
@ -7,11 +7,16 @@ MOROS is a hobby operating system written in Rust by [Vincent Ollivier](https://
|
|||
It targets computers with a x86-64 architecture and a BIOS, so mostly from 2005
|
||||
to 2020, but it also runs well on most emulators (Bochs, QEMU, and VirtualBox).
|
||||
|
||||
* * *
|
||||
|
||||
The first task when running the OS is to install the
|
||||
[filesystem](filesystem.md) on a disk (or in RAM) using the `install` program,
|
||||
although it's possible to skip the installation and stay in read only mode.
|
||||
## Usage
|
||||
|
||||
MOROS is open source, you can [build it](https://github.com/vinc/moros)
|
||||
or [download an image](https://github.com/vinc/moros/releases). Consult the
|
||||
[manual](manual.md) to learn how to use the system.
|
||||
|
||||
|
||||
|
||||
## Features
|
||||
|
||||
Everything in MOROS is done from a command line interface and most programs are
|
||||
rather minimalist.
|
||||
|
@ -37,67 +42,27 @@ It has a [network stack](network.md) with two drivers for RTL81339 and PCNET car
|
|||
|
||||
![screenshot](network.png)
|
||||
|
||||
It even has a chess game:
|
||||
It has a chess game:
|
||||
|
||||
![chess](chess.png)
|
||||
|
||||
Finally here are a few commands to try it out:
|
||||
And the game of life:
|
||||
|
||||
> date
|
||||
2021-08-12T20:16:48
|
||||
![life](life.png)
|
||||
|
||||
> memory
|
||||
Size: 16777216
|
||||
Used: 15400
|
||||
Free: 16761816
|
||||
It even has 2048:
|
||||
|
||||
> disk
|
||||
Size: 8388608
|
||||
Used: 445440
|
||||
Free: 7943168
|
||||
![2048](2048.png)
|
||||
|
||||
> list /tmp
|
||||
5083 2021-08-07 15:10:09 alice.txt
|
||||
118 2021-08-07 15:10:09 fibonacci.lisp
|
||||
And finally it is quite customizable:
|
||||
|
||||
> goto /tmp
|
||||
![light](light.png)
|
||||
|
||||
> read fibonacci.lisp
|
||||
(label fib
|
||||
(lambda (n)
|
||||
(cond
|
||||
((< n 2) n)
|
||||
(true (+ (fib (- n 1)) (fib (- n 2)))))))
|
||||
|
||||
(print (fib 6))
|
||||
## Demo
|
||||
|
||||
> lisp fibonacci.lisp
|
||||
8
|
||||
You can log in to a demo with the following command using the name of the
|
||||
system as a password for the guest account:
|
||||
|
||||
> find /tmp --line "p.*nt"
|
||||
/tmp/alice.txt
|
||||
9: bank, and of having nothing to do: once or twice she had peeped into the
|
||||
36: dipped suddenly down, so suddenly that Alice had not a moment to think
|
||||
41: plenty of time as she went down to look about her and to wonder what was
|
||||
48: disappointment it was empty: she did not like to drop the jar for fear
|
||||
49: of killing somebody, so managed to put it into one of the cupboards as
|
||||
85: began to get rather sleepy, and went on saying to herself, in a dreamy
|
||||
$ ssh guest@try.moros.cc
|
||||
|
||||
/tmp/fibonacci.lisp
|
||||
7: (print (fib 6))
|
||||
|
||||
> dhcp
|
||||
DHCP Discover transmitted
|
||||
DHCP Offer received
|
||||
Leased: 10.0.2.15/24
|
||||
Router: 10.0.2.2
|
||||
DNS: 10.0.2.3
|
||||
|
||||
> tcp time.nist.gov 13
|
||||
Connecting to 132.163.97.4:13
|
||||
|
||||
59438 21-08-12 20:18:27 50 0 0 358.8 UTC(NIST) *
|
||||
|
||||
> halt
|
||||
MOROS has reached its fate, the system is now halting.
|
||||
[782.191890] ACPI Shutdown
|
||||
|
|
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 3.7 KiB |
71
doc/lisp.md
|
@ -5,40 +5,6 @@ of the Shell.
|
|||
|
||||
MOROS Lisp is a Lisp-1 dialect inspired by Scheme, Clojure, and Ruby!
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.1.0 (2021-07-21)
|
||||
MOROS Lisp started from [Risp](https://github.com/stopachka/risp) and was
|
||||
extended to include the seven primitive operators and the two special forms of
|
||||
John McCarthy's paper "Recursive Functions of Symbolic Expressions and Their
|
||||
Computation by Machine" (1960) and "The Roots of Lisp" (2002) by Paul Graham.
|
||||
|
||||
### 0.2.0 (2021-12-04)
|
||||
The whole implementation was refactored and the parser was rewritten to use
|
||||
[Nom](https://github.com/Geal/nom). This allowed the addition of strings to the
|
||||
language and reading from the filesystem.
|
||||
|
||||
### 0.3.0 (2022-12-12)
|
||||
Rewrite the evaluation code, add new functions and a core library.
|
||||
|
||||
### 0.3.1 (2022-06-06)
|
||||
Rewrite parts of the code and add new functions and examples.
|
||||
|
||||
### 0.3.2 (2022-07-02)
|
||||
- Add new functions
|
||||
|
||||
### 0.3.2 (2022-08-25)
|
||||
- Add new functions
|
||||
|
||||
### 0.4.0 (2022-08-25)
|
||||
- Rewrite a lot of the code
|
||||
- Add integer and big integer support
|
||||
- Add tail call optimization (TCO)
|
||||
- Add macro support
|
||||
|
||||
### 0.5.0 (unpublished)
|
||||
- Rename or add aliases to many functions
|
||||
|
||||
## Overview
|
||||
|
||||
### Types
|
||||
|
@ -81,7 +47,6 @@ Rewrite parts of the code and add new functions and examples.
|
|||
- `number->binary` and `binary->number` (aliased to `num->bin` and `bin->num`)
|
||||
- `regex-find`
|
||||
- `system`
|
||||
|
||||
- Arithmetic operations: `+`, `-`, `*`, `/`, `%`, `^`, `abs`
|
||||
- Trigonometric functions: `acos`, `asin`, `atan`, `cos`, `sin`, `tan`
|
||||
- Comparisons: `>`, `<`, `>=`, `<=`, `=`
|
||||
|
@ -186,5 +151,39 @@ Would produce the following output:
|
|||
|
||||
(str "Hello, " name) # => "Hello, Alice"
|
||||
|
||||
(^ 2 128) # => 340282366920938463463374607431768211456
|
||||
(^ 2 64) # => 18446744073709551616
|
||||
```
|
||||
|
||||
## Changelog
|
||||
|
||||
### 0.1.0 (2021-07-21)
|
||||
MOROS Lisp started from [Risp](https://github.com/stopachka/risp) and was
|
||||
extended to include the seven primitive operators and the two special forms of
|
||||
John McCarthy's paper "Recursive Functions of Symbolic Expressions and Their
|
||||
Computation by Machine" (1960) and "The Roots of Lisp" (2002) by Paul Graham.
|
||||
|
||||
### 0.2.0 (2021-12-04)
|
||||
The whole implementation was refactored and the parser was rewritten to use
|
||||
[Nom](https://github.com/Geal/nom). This allowed the addition of strings to the
|
||||
language and reading from the filesystem.
|
||||
|
||||
### 0.3.0 (2022-12-12)
|
||||
Rewrite the evaluation code, add new functions and a core library.
|
||||
|
||||
### 0.3.1 (2022-06-06)
|
||||
Rewrite parts of the code and add new functions and examples.
|
||||
|
||||
### 0.3.2 (2022-07-02)
|
||||
- Add new functions
|
||||
|
||||
### 0.3.2 (2022-08-25)
|
||||
- Add new functions
|
||||
|
||||
### 0.4.0 (2022-08-25)
|
||||
- Rewrite a lot of the code
|
||||
- Add integer and big integer support
|
||||
- Add tail call optimization (TCO)
|
||||
- Add macro support
|
||||
|
||||
### 0.5.0 (unpublished)
|
||||
- Rename or add aliases to many functions
|
||||
|
|
BIN
doc/lisp.png
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 2.2 KiB |
|
@ -0,0 +1,349 @@
|
|||
# MOROS Manual
|
||||
|
||||
## Boot
|
||||
|
||||
During boot MOROS will display its version followed by the memory layout,
|
||||
memory size, processor, devices, network cards, disks, and the real time clock.
|
||||
|
||||
[0.250962] MOROS v0.9.0
|
||||
[0.250962] MEM [0x00000000000000-0x00000000000FFF] FrameZero
|
||||
[0.250962] MEM [0x00000000001000-0x00000000004FFF] PageTable
|
||||
[0.250962] MEM [0x00000000005000-0x00000000015FFF] Bootloader
|
||||
[0.250962] MEM [0x00000000016000-0x00000000016FFF] BootInfo
|
||||
[0.250962] MEM [0x00000000017000-0x0000000002DFFF] Kernel
|
||||
[0.250962] MEM [0x0000000002E000-0x0000000009EFFF] KernelStack
|
||||
[0.250962] MEM [0x0000000009F000-0x0000000009FFFF] Reserved
|
||||
[0.250962] MEM [0x000000000F0000-0x000000000FFFFF] Reserved
|
||||
[0.250962] MEM [0x00000000100000-0x0000000028EFFF] KernelStack
|
||||
[0.250962] MEM [0x0000000028F000-0x000000003FFFFF] Usable
|
||||
[0.250962] MEM [0x00000000400000-0x000000005E1FFF] Kernel
|
||||
[0.250962] MEM [0x000000005E2000-0x000000005EFFFF] PageTable
|
||||
[0.250962] MEM [0x000000005F0000-0x00000001FDFFFF] Usable
|
||||
[0.250962] MEM [0x00000001FE0000-0x00000001FFFFFF] Reserved
|
||||
[0.250962] MEM [0x000000FEFFC000-0x000000FEFFFFFF] Reserved
|
||||
[0.250962] MEM [0x000000FFFC0000-0x000000FFFFFFFF] Reserved
|
||||
[0.250962] MEM 32720 KB
|
||||
[0.251962] CPU GenuineIntel
|
||||
[0.254961] CPU Intel(R) Core(TM) i5-4300M CPU @ 2.60GHz
|
||||
[0.284957] PCI 0000:00:00 [8086:1237]
|
||||
[0.312952] PCI 0000:01:00 [8086:7000]
|
||||
[0.321951] PCI 0000:01:01 [8086:7010]
|
||||
[0.321951] PCI 0000:01:03 [8086:7113]
|
||||
[0.322951] PCI 0000:02:00 [1234:1111]
|
||||
[0.323951] PCI 0000:03:00 [10EC:8139]
|
||||
[0.377942] NET RTL8139 MAC 52-54-00-12-34-56
|
||||
[0.382942] ATA 0:0 QEMU HARDDISK QM00001 (32 MB)
|
||||
[0.384941] MFS Superblock found in ATA 0:0
|
||||
[0.386941] RTC 2023-04-17 20:00:28 +0000
|
||||
|
||||
## Installation
|
||||
|
||||
The first time MOROS will boot in diskless mode where you can use the builtin
|
||||
commands to test the system or `install` to setup the
|
||||
[filesystem](filesystem.md) on a disk:
|
||||
|
||||
MFS is not mounted to '/'
|
||||
Running console in diskless mode
|
||||
|
||||
/
|
||||
> install
|
||||
Welcome to MOROS v0.9.0 installation program!
|
||||
|
||||
Proceed? [y/N] y
|
||||
|
||||
Listing disks ...
|
||||
Path Name (Size)
|
||||
/dev/ata/0/0 QEMU HARDDISK QM00001 (32 MB)
|
||||
|
||||
Formatting disk ...
|
||||
Enter path of disk to format: /dev/ata/0/0
|
||||
Disk successfully formatted
|
||||
MFS is now mounted to '/'
|
||||
|
||||
Populating filesystem...
|
||||
Created '/bin'
|
||||
Created '/dev'
|
||||
Created '/ini'
|
||||
Created '/lib'
|
||||
Created '/net'
|
||||
Created '/src'
|
||||
Created '/tmp'
|
||||
Created '/usr'
|
||||
Created '/var'
|
||||
Copied '/bin/clear'
|
||||
Copied '/bin/halt'
|
||||
Copied '/bin/hello'
|
||||
Copied '/bin/print'
|
||||
Copied '/bin/reboot'
|
||||
Copied '/bin/sleep'
|
||||
Created '/dev/clk'
|
||||
Created '/dev/clk/uptime'
|
||||
Created '/dev/clk/realtime'
|
||||
Created '/dev/rtc'
|
||||
Created '/dev/null'
|
||||
Created '/dev/random'
|
||||
Created '/dev/console'
|
||||
Copied '/ini/banner.txt'
|
||||
Copied '/ini/boot.sh'
|
||||
Copied '/ini/shell.sh'
|
||||
Copied '/ini/version.txt'
|
||||
Created '/ini/palettes'
|
||||
Copied '/ini/palettes/gruvbox-dark.csv'
|
||||
Copied '/ini/palettes/gruvbox-light.csv'
|
||||
Created '/ini/fonts'
|
||||
Copied '/ini/fonts/zap-light-8x16.psf'
|
||||
Copied '/ini/fonts/zap-vga-8x16.psf'
|
||||
Created '/lib/lisp'
|
||||
Copied '/lib/lisp/core.lsp'
|
||||
Copied '/lib/lisp/alias.lsp'
|
||||
Copied '/tmp/alice.txt'
|
||||
Copied '/tmp/machines.txt'
|
||||
Created '/tmp/lisp'
|
||||
Copied '/tmp/lisp/colors.lsp'
|
||||
Copied '/tmp/lisp/factorial.lsp'
|
||||
Copied '/tmp/lisp/fibonacci.lsp'
|
||||
Copied '/tmp/lisp/geotime.lsp'
|
||||
Copied '/tmp/lisp/pi.lsp'
|
||||
Copied '/tmp/lisp/sum.lsp'
|
||||
Created '/tmp/life'
|
||||
Copied '/tmp/life/centinal.cells'
|
||||
Copied '/tmp/life/flower-of-eden.cells'
|
||||
Copied '/tmp/life/garden-of-eden.cells'
|
||||
Copied '/tmp/life/glider-gun.cells'
|
||||
Copied '/tmp/life/pentadecathlon.cells'
|
||||
Copied '/tmp/life/queen-bee-shuttle.cells'
|
||||
Copied '/tmp/life/ship-in-a-bottle.cells'
|
||||
Copied '/tmp/life/thunderbird.cells'
|
||||
Copied '/tmp/life/wing.cells'
|
||||
Created '/tmp/beep'
|
||||
Copied '/tmp/beep/tetris.sh'
|
||||
Copied '/tmp/beep/starwars.sh'
|
||||
Copied '/tmp/beep/mario.sh'
|
||||
Created '/var/www'
|
||||
Copied '/var/www/index.html'
|
||||
Copied '/var/www/moros.png'
|
||||
|
||||
Creating user...
|
||||
Username: vinc
|
||||
Password:
|
||||
Confirm:
|
||||
|
||||
Installation successful!
|
||||
|
||||
Quit the console or reboot to apply changes
|
||||
|
||||
You can then use `^D` (a key combination of `CTRL` and `D`) to quit the
|
||||
diskless mode and let MOROS run the bootscript `/ini/boot.sh` to login and use
|
||||
the shell.
|
||||
|
||||
If no disks were detected or if you prefer not to use them you can mount the
|
||||
system in memory to use a virtual disk with `memory format` before `install`.
|
||||
|
||||
## Shell
|
||||
|
||||
The [shell](shell.md) is the primary command line interface to use MOROS.
|
||||
This is were you can type a command and its arguments to tell the system what
|
||||
to do:
|
||||
|
||||
~
|
||||
> print "Hello, World!"
|
||||
Hello, World!
|
||||
|
||||
The system has a `help` command to help you remember the basic commands.
|
||||
|
||||
Most commands also have a special `--help` argument to show all their options.
|
||||
|
||||
## Directories
|
||||
|
||||
The line above the command prompt tells you where you are in the disk. The
|
||||
tilde `~` means that you are in your home directory:
|
||||
|
||||
~
|
||||
> print $DIR
|
||||
/usr/vinc
|
||||
|
||||
You can change directory by typing it as if it was a command:
|
||||
|
||||
~
|
||||
> /tmp
|
||||
|
||||
/tmp
|
||||
> print $DIR
|
||||
/tmp
|
||||
|
||||
From now on we'll omit the directory line in most examples.
|
||||
|
||||
You can list the content of a directory with `list`:
|
||||
|
||||
> list /tmp
|
||||
5090 2023-04-17 06:25:54 alice.txt
|
||||
82 2023-04-17 06:25:55 beep
|
||||
324 2023-04-17 06:25:55 life
|
||||
168 2023-04-17 06:25:55 lisp
|
||||
649 2023-04-17 06:25:54 machines.txt
|
||||
|
||||
The command has some options to sort the results:
|
||||
|
||||
> list --help
|
||||
Usage: list <options> [<dir>]
|
||||
|
||||
Options:
|
||||
-a, --all Show dot files
|
||||
-n, --name Sort by name
|
||||
-s, --size Sort by size
|
||||
-t, --time Sort by time
|
||||
|
||||
You can write a directory in the disk with `write`:
|
||||
|
||||
> write test/
|
||||
|
||||
> list
|
||||
5090 2023-04-17 06:25:54 alice.txt
|
||||
82 2023-04-17 06:25:55 beep
|
||||
324 2023-04-17 06:25:55 life
|
||||
168 2023-04-17 06:25:55 lisp
|
||||
649 2023-04-17 06:25:54 machines.txt
|
||||
0 2023-04-17 07:06:18 test
|
||||
|
||||
The slash `/` at the end of `test/` is there to tell the `write` command to
|
||||
create a directory instead of a file.
|
||||
|
||||
## Files
|
||||
|
||||
You can create a file by redirecting the output of a command with an arrow `=>`
|
||||
to the file:
|
||||
|
||||
> print "Hello, World!" => hello.txt
|
||||
|
||||
The command `read` will read the content of the file:
|
||||
|
||||
> read hello.txt
|
||||
Hello, World!
|
||||
|
||||
You can edit a file with the `edit` command that will run the text editor.
|
||||
|
||||
Use `^W` (a key combination of `CTRL` and `W`) inside the editor to write the
|
||||
content to the file and `^Q` to quit the editor and go back to the shell.
|
||||
|
||||
The help command has a subcommand `help edit` to list the editor commands:
|
||||
|
||||
> help edit
|
||||
MOROS text editor is a very simple editor inspired by Pico, Nano, and Micro.
|
||||
|
||||
Commands:
|
||||
^Q Quit editor
|
||||
^W Write to file
|
||||
^X Write to file and quit
|
||||
^T Go to top of file
|
||||
^B Go to bottom of file
|
||||
^A Go to beginning of line
|
||||
^E Go to end of line
|
||||
^D Cut line
|
||||
^Y Copy line
|
||||
^P Paste line
|
||||
|
||||
## Time
|
||||
|
||||
You can print the date with `date`:
|
||||
|
||||
> date
|
||||
2001-01-01 00:00:00 +0000
|
||||
|
||||
You can update the real time clock by writing the correct time to its device
|
||||
file:
|
||||
|
||||
> print "2023-03-21 10:00:00" => /dev/rtc
|
||||
|
||||
> date
|
||||
2023-03-21 10:00:00 +0000
|
||||
|
||||
You can also set the `TZ` environment variable to use your preferred timezone:
|
||||
|
||||
> calc "2 * 60 * 60"
|
||||
7200
|
||||
|
||||
> env TZ 7200
|
||||
|
||||
> date
|
||||
2023-03-21 12:00:00 +0200
|
||||
|
||||
Add `env TZ 7200` to `/ini/boot.sh` before `shell` to save the timezone:
|
||||
|
||||
> read /ini/boot.sh
|
||||
vga set palette /ini/palettes/gruvbox-dark.csv
|
||||
vga set font /ini/fonts/zap-light-8x16.psf
|
||||
read /ini/banner.txt
|
||||
user login
|
||||
env TZ 7200
|
||||
shell
|
||||
|
||||
There's a device file to get the number of seconds elapsed since Unix Epoch:
|
||||
|
||||
> read /dev/clk/realtime
|
||||
1682105344.624905
|
||||
|
||||
And another one since boot:
|
||||
|
||||
> read /dev/clk/uptime
|
||||
1169.384929
|
||||
|
||||
## Aliases
|
||||
|
||||
You can add custom commands to the shell with the `alias` command.
|
||||
|
||||
For example you can define an `uptime` command that will read the device file
|
||||
described above:
|
||||
|
||||
> alias uptime "read /dev/clk/uptime"
|
||||
|
||||
> uptime
|
||||
1406.304852
|
||||
|
||||
You can add that command to `/ini/shell.sh` to save it.
|
||||
|
||||
Some shortcuts have been defined in that file for the most frequent commands,
|
||||
for example you can use `e` instead of `edit` to edit a file.
|
||||
|
||||
> read /ini/shell.sh
|
||||
# Command shortcuts
|
||||
alias c copy
|
||||
alias d delete
|
||||
alias e edit
|
||||
alias f find
|
||||
alias h help
|
||||
alias l list
|
||||
alias m move
|
||||
alias p print
|
||||
alias q quit
|
||||
alias r read
|
||||
alias w write
|
||||
|
||||
alias sh shell
|
||||
alias dsk disk
|
||||
alias mem memory
|
||||
alias kbd keyboard
|
||||
|
||||
## Network
|
||||
|
||||
You can setup the [network](network.md) manually with `net` or automatically
|
||||
with `dhcp`:
|
||||
|
||||
> dhcp
|
||||
ip: 10.0.2.15/24
|
||||
gw: 10.0.2.2
|
||||
dns: 10.0.2.3
|
||||
|
||||
A few tools are available like the `http` command:
|
||||
|
||||
> http moros.cc /test.html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS</h1>
|
||||
</body>
|
||||
</html>
|
BIN
doc/moros.png
Before Width: | Height: | Size: 4.0 KiB After Width: | Height: | Size: 4.0 KiB |
BIN
doc/network.png
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 2.6 KiB |
BIN
doc/shell.png
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 2.7 KiB |
After Width: | Height: | Size: 2.1 KiB |
BIN
dsk/bin/clear
BIN
dsk/bin/halt
BIN
dsk/bin/hello
BIN
dsk/bin/print
BIN
dsk/bin/reboot
BIN
dsk/bin/sleep
|
@ -41,10 +41,10 @@ pub fn shutdown() {
|
|||
let res = unsafe { AcpiTables::search_for_rsdp_bios(MorosAcpiHandler) };
|
||||
match res {
|
||||
Ok(acpi) => {
|
||||
//log!("ACPI Found RDSP in BIOS\n");
|
||||
//debug!("ACPI Found RDSP in BIOS");
|
||||
for (sign, sdt) in acpi.sdts {
|
||||
if sign.as_str() == "FACP" {
|
||||
//log!("ACPI Found FACP at {}\n", sdt.physical_address);
|
||||
//debug!("ACPI Found FACP at {:#x}", sdt.physical_address);
|
||||
|
||||
/*
|
||||
// Enable ACPI
|
||||
|
@ -56,25 +56,25 @@ pub fn shutdown() {
|
|||
*/
|
||||
|
||||
pm1a_control_block = read_fadt::<u32>(sdt.physical_address, FADT::Pm1aControlBlock);
|
||||
//log!("ACPI Found PM1a Control Block: {:#X}\n", pm1a_control_block);
|
||||
//debug!("ACPI Found PM1a Control Block: {:#x}", pm1a_control_block);
|
||||
}
|
||||
}
|
||||
match &acpi.dsdt {
|
||||
Some(dsdt) => {
|
||||
//log!("ACPI Found DSDT at {}\n", dsdt.address);
|
||||
let address = sys::mem::phys_to_virt(PhysAddr::new(dsdt.address as u64));
|
||||
let stream = unsafe { core::slice::from_raw_parts(address.as_ptr(), dsdt.length as usize) };
|
||||
if aml.parse_table(stream).is_ok() {
|
||||
//debug!("ACPI Found DSDT at {:#x} {:#x}", dsdt.address, address);
|
||||
let table = unsafe { core::slice::from_raw_parts(address.as_ptr(), dsdt.length as usize) };
|
||||
if aml.parse_table(table).is_ok() {
|
||||
let name = AmlName::from_str("\\_S5").unwrap();
|
||||
if let Ok(AmlValue::Package(s5)) = aml.namespace.get_by_path(&name) {
|
||||
//log!("ACPI Found \\_S5 in DSDT\n");
|
||||
//debug!("ACPI Found \\_S5 in DSDT");
|
||||
if let AmlValue::Integer(value) = s5[0] {
|
||||
slp_typa = value as u16;
|
||||
//log!("ACPI Found SLP_TYPa in \\_S5: {}\n", slp_typa);
|
||||
//debug!("ACPI Found SLP_TYPa in \\_S5: {}", slp_typa);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log!("ACPI Failed to parse AML in DSDT\n");
|
||||
debug!("ACPI Failed to parse AML in DSDT");
|
||||
// FIXME: AML parsing works on QEMU and Bochs but not
|
||||
// on VirtualBox at the moment, so we use the following
|
||||
// hardcoded value:
|
||||
|
@ -85,11 +85,12 @@ pub fn shutdown() {
|
|||
}
|
||||
}
|
||||
Err(_e) => {
|
||||
log!("ACPI Could not find RDSP in BIOS\n");
|
||||
debug!("ACPI Could not find RDSP in BIOS\n");
|
||||
}
|
||||
};
|
||||
|
||||
let mut port: Port<u16> = Port::new(pm1a_control_block as u16);
|
||||
//debug!("ACPI shutdown");
|
||||
unsafe {
|
||||
port.write(slp_typa | slp_len);
|
||||
}
|
||||
|
@ -101,6 +102,7 @@ pub struct MorosAcpiHandler;
|
|||
impl AcpiHandler for MorosAcpiHandler {
|
||||
unsafe fn map_physical_region<T>(&self, physical_address: usize, size: usize) -> PhysicalMapping<Self, T> {
|
||||
let virtual_address = sys::mem::phys_to_virt(PhysAddr::new(physical_address as u64));
|
||||
//debug!("ACPI mapping phys {:#x} -> virt {:#x}", physical_address, virtual_address);
|
||||
PhysicalMapping::new(physical_address, NonNull::new(virtual_address.as_mut_ptr()).unwrap(), size, size, Self)
|
||||
}
|
||||
|
||||
|
|
|
@ -66,14 +66,15 @@ pub fn alloc_pages(addr: u64, size: usize) -> Result<(), ()> {
|
|||
//debug!("Alloc frame {:?}", frame);
|
||||
unsafe {
|
||||
if let Ok(mapping) = mapper.map_to(page, frame, flags, &mut frame_allocator) {
|
||||
//debug!("Mapped {:?} to {:?}", page, frame);
|
||||
mapping.flush();
|
||||
} else {
|
||||
//debug!("Could not map {:?}", page);
|
||||
debug!("Could not map {:?} to {:?}", page, frame);
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
//debug!("Could not allocate frame for {:?}", page);
|
||||
debug!("Could not allocate frame for {:?}", page);
|
||||
return Err(());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,6 +84,7 @@ impl BootInfoFrameAllocator {
|
|||
unsafe impl FrameAllocator<Size4KiB> for BootInfoFrameAllocator {
|
||||
fn allocate_frame(&mut self) -> Option<PhysFrame> {
|
||||
let next = ALLOCATED_FRAMES.fetch_add(1, Ordering::SeqCst);
|
||||
//debug!("Allocate frame {} / {}", next, self.usable_frames().count());
|
||||
|
||||
// FIXME: creating an iterator for each allocation is very slow if
|
||||
// the heap is larger than a few megabytes.
|
||||
|
|
|
@ -15,7 +15,7 @@ use x86_64::structures::idt::InterruptStackFrameValue;
|
|||
|
||||
const MAX_FILE_HANDLES: usize = 64;
|
||||
const MAX_PROCS: usize = 2; // TODO: Update this when more than one process can run at once
|
||||
const MAX_PROC_SIZE: usize = 1 << 20; // 1 MB
|
||||
const MAX_PROC_SIZE: usize = 10 << 20; // 10 MB
|
||||
|
||||
pub static PID: AtomicUsize = AtomicUsize::new(0);
|
||||
pub static MAX_PID: AtomicUsize = AtomicUsize::new(1);
|
||||
|
@ -272,24 +272,27 @@ impl Process {
|
|||
let proc_size = MAX_PROC_SIZE as u64;
|
||||
let code_addr = CODE_ADDR.fetch_add(proc_size, Ordering::SeqCst);
|
||||
let stack_addr = code_addr + proc_size;
|
||||
//debug!("code_addr: {:#x}", code_addr);
|
||||
//debug!("stack_addr: {:#x}", stack_addr);
|
||||
|
||||
let mut entry_point = 0;
|
||||
let code_ptr = code_addr as *mut u8;
|
||||
if bin[0..4] == ELF_MAGIC { // ELF binary
|
||||
if let Ok(obj) = object::File::parse(bin) {
|
||||
//sys::allocator::alloc_pages(code_addr, code_size);
|
||||
//sys::allocator::alloc_pages(code_addr, proc_size as usize).expect("proc mem alloc");
|
||||
entry_point = obj.entry();
|
||||
for segment in obj.segments() {
|
||||
let addr = segment.address() as usize;
|
||||
if let Ok(data) = segment.data() {
|
||||
for (i, b) in data.iter().enumerate() {
|
||||
//debug!("code: {:#x}", unsafe { code_ptr.add(addr + i) as usize });
|
||||
unsafe { core::ptr::write(code_ptr.add(addr + i), *b) };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if bin[0..4] == BIN_MAGIC { // Flat binary
|
||||
//sys::allocator::alloc_pages(code_addr, code_size);
|
||||
//sys::allocator::alloc_pages(code_addr, proc_size as usize).expect("proc mem alloc");
|
||||
for (i, b) in bin.iter().skip(4).enumerate() {
|
||||
unsafe { core::ptr::write(code_ptr.add(i), *b) };
|
||||
}
|
||||
|
@ -318,7 +321,8 @@ impl Process {
|
|||
// Switch to user mode and execute the program
|
||||
fn exec(&self, args_ptr: usize, args_len: usize) {
|
||||
let heap_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2;
|
||||
sys::allocator::alloc_pages(heap_addr, 1).expect("Could not allocate");
|
||||
//debug!("heap_addr: {:#x}", heap_addr);
|
||||
sys::allocator::alloc_pages(heap_addr, 1).expect("proc heap alloc");
|
||||
|
||||
let args_ptr = ptr_from_addr(args_ptr as u64) as usize;
|
||||
let args: &[&str] = unsafe { core::slice::from_raw_parts(args_ptr as *const &str, args_len) };
|
||||
|
|
|
@ -115,6 +115,7 @@ pub fn stop(code: usize) -> usize {
|
|||
}
|
||||
}
|
||||
0xdead => { // Halt
|
||||
sys::process::exit();
|
||||
sys::acpi::shutdown();
|
||||
}
|
||||
_ => {
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() != 3 {
|
||||
eprintln!("Usage: copy <source> <dest>");
|
||||
let n = args.len();
|
||||
if n != 3 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
for i in 1..n {
|
||||
match args[i] {
|
||||
"-h" | "--help" => {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
|
||||
let source = args[1];
|
||||
let dest = args[2];
|
||||
|
@ -22,3 +33,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
Err(ExitCode::Failure)
|
||||
}
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} copy {}<src> <dst>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
use crate::api;
|
||||
use crate::api::clock::DATE_TIME_ZONE;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use time::validate_format_string;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() > 2 {
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
let format = if args.len() > 1 { args[1] } else { DATE_TIME_ZONE };
|
||||
if format == "-h" || format == "--help" {
|
||||
return help();
|
||||
}
|
||||
match validate_format_string(format) {
|
||||
Ok(()) => {
|
||||
println!("{}", api::time::now().format(format));
|
||||
|
@ -16,3 +23,11 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn help() -> Result<(), ExitCode> {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} date {}[<format>]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
use crate::api::fs;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() < 2 {
|
||||
let n = args.len();
|
||||
if n < 2 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
for i in 1..n {
|
||||
match args[i] {
|
||||
"-h" | "--help" => {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
|
||||
for arg in &args[1..] {
|
||||
let mut pathname = *arg;
|
||||
|
@ -35,3 +47,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} delete {}<path>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!();
|
||||
println!("{}Paths:{}", csi_title, csi_reset);
|
||||
println!(" {0}<dir>/{1} Delete directory", csi_option, csi_reset);
|
||||
println!(" {0}<file>{1} Delete file", csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{sys, usr, debug};
|
||||
use crate::api::clock;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
|
||||
|
@ -15,9 +16,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
|
||||
for arg in args {
|
||||
match *arg {
|
||||
"-v" | "--verbose" => {
|
||||
verbose = true;
|
||||
}
|
||||
"-h" | "--help" => return help(),
|
||||
"-v" | "--verbose" => verbose = true,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
@ -94,3 +94,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
|
||||
Err(ExitCode::Failure)
|
||||
}
|
||||
|
||||
fn help() -> Result<(), ExitCode> {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} dhcp {}<options>{1}", csi_title, csi_reset, csi_option);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-v{1}, {0}--verbose{1} Increase verbosity", csi_option, csi_reset);
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
@ -10,8 +10,13 @@ use core::cmp;
|
|||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() != 2 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
if args[1] == "-h" || args[1] == "--help" {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pathname = args[1];
|
||||
let mut editor = Editor::new(pathname);
|
||||
|
@ -425,3 +430,10 @@ fn truncated_line_indicator() -> String {
|
|||
let reset = Style::reset();
|
||||
format!("{}>{}", color, reset)
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} edit {}<file>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -7,12 +7,16 @@ use object::{Object, ObjectSection};
|
|||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() != 2 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
if args[1] == "-h" || args[1] == "--help" {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let color = Style::color("Yellow");
|
||||
let reset = Style::reset();
|
||||
|
||||
let pathname = args[1];
|
||||
if let Ok(buf) = fs::read_to_bytes(pathname) {
|
||||
let bin = buf.as_slice();
|
||||
|
@ -35,11 +39,18 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
Ok(())
|
||||
} else {
|
||||
println!("Could not parse ELF");
|
||||
error!("Could not parse ELF");
|
||||
Err(ExitCode::Failure)
|
||||
}
|
||||
} else {
|
||||
println!("File not found '{}'", pathname);
|
||||
error!("File not found '{}'", pathname);
|
||||
Err(ExitCode::Failure)
|
||||
}
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} elf {}<binary>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -1,11 +1,23 @@
|
|||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::sys;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
match args.len() {
|
||||
let n = args.len();
|
||||
for i in 1..n {
|
||||
match args[i] {
|
||||
"-h" | "--help" => {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
match n {
|
||||
1 => {
|
||||
let width = sys::process::envs().keys().map(|k| k.len()).max().unwrap_or(0);
|
||||
for (key, val) in sys::process::envs() {
|
||||
println!("{:10} \"{}\"", key, val);
|
||||
println!("{:width$} \"{}\"", key, val, width = width);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -24,8 +36,15 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
Ok(())
|
||||
}
|
||||
_ => {
|
||||
error!("Invalid number of arguments");
|
||||
help();
|
||||
Err(ExitCode::UsageError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} env {}[<key> [<value>]]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -2,15 +2,19 @@ use crate::api::console::Style;
|
|||
use crate::api::process::ExitCode;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() > 1 {
|
||||
help_command(args[1])
|
||||
} else {
|
||||
help_summary()
|
||||
match args.len() {
|
||||
1 => help_summary(),
|
||||
2 => help_command(args[1]),
|
||||
_ => {
|
||||
help();
|
||||
Err(ExitCode::UsageError)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn help_command(cmd: &str) -> Result<(), ExitCode> {
|
||||
match cmd {
|
||||
"-h" | "--help" => { help(); Ok(()) },
|
||||
"date" => help_date(),
|
||||
"edit" => help_edit(),
|
||||
_ => help_unknown(cmd),
|
||||
|
@ -60,11 +64,10 @@ fn help_summary() -> Result<(), ExitCode> {
|
|||
fn help_edit() -> Result<(), ExitCode> {
|
||||
let csi_color = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("MOROS text editor is somewhat inspired by Pico, but with an even smaller range");
|
||||
println!("of features.");
|
||||
println!("MOROS text editor is a very simple editor inspired by Pico, Nano, and Micro.");
|
||||
println!();
|
||||
println!("{}Shortcuts:{}", csi_color, csi_reset);
|
||||
let shortcuts = [
|
||||
println!("{}Commands:{}", csi_color, csi_reset);
|
||||
let commands = [
|
||||
("^Q", "Quit editor"),
|
||||
("^W", "Write to file"),
|
||||
("^X", "Write to file and quit"),
|
||||
|
@ -76,10 +79,10 @@ fn help_edit() -> Result<(), ExitCode> {
|
|||
("^Y", "Copy line"),
|
||||
("^P", "Paste line"),
|
||||
];
|
||||
for (shortcut, usage) in &shortcuts {
|
||||
for (command, usage) in &commands {
|
||||
let csi_color = Style::color("LightCyan");
|
||||
let csi_reset = Style::reset();
|
||||
println!(" {}{}{} {}", csi_color, shortcut, csi_reset, usage);
|
||||
println!(" {}{}{} {}", csi_color, command, csi_reset, usage);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -87,7 +90,7 @@ fn help_edit() -> Result<(), ExitCode> {
|
|||
fn help_date() -> Result<(), ExitCode> {
|
||||
let csi_color = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("The date command's formatting behavior is based on strftime in C");
|
||||
println!("The date command's formatting behavior is based on strftime from C.");
|
||||
println!();
|
||||
println!("{}Specifiers:{}", csi_color, csi_reset);
|
||||
let specifiers = [
|
||||
|
@ -131,3 +134,10 @@ fn help_date() -> Result<(), ExitCode> {
|
|||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} help {}[<command>]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -5,8 +5,13 @@ use crate::api::process::ExitCode;
|
|||
// TODO: add `--skip` and `--length` params
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() != 2 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
if args[1] == "-h" || args[1] == "--help" {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
let pathname = args[1];
|
||||
if let Ok(buf) = fs::read_to_bytes(pathname) { // TODO: read chunks
|
||||
print_hex(&buf);
|
||||
|
@ -47,3 +52,10 @@ pub fn print_hex(buf: &[u8]) {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} hex {}<file>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -266,6 +266,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
if args.len() < 2 {
|
||||
repl(env)
|
||||
} else {
|
||||
if args[1] == "-h" || args[1] == "--help" {
|
||||
return help();
|
||||
}
|
||||
let pathname = args[1];
|
||||
if let Ok(code) = api::fs::read_to_string(pathname) {
|
||||
let mut block = String::new();
|
||||
|
@ -302,6 +305,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
|
||||
fn help() -> Result<(), ExitCode> {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} lisp {}[<file> [<args>]]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_lisp() {
|
||||
use core::f64::consts::PI;
|
||||
|
|
|
@ -98,7 +98,7 @@ fn help() -> Result<(), ExitCode> {
|
|||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} list {}<options>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!("{}Usage:{} list {}<options> [<dir>]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!();
|
||||
println!("{}Options:{}", csi_title, csi_reset);
|
||||
println!(" {0}-a{1},{0} --all{1} Show dot files", csi_option, csi_reset);
|
||||
|
|
|
@ -1,10 +1,22 @@
|
|||
use crate::api::console::Style;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::usr;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() != 3 {
|
||||
let n = args.len();
|
||||
if n != 3 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
for i in 1..n {
|
||||
match args[i] {
|
||||
"-h" | "--help" => {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => continue
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Avoid doing copy+delete
|
||||
match usr::copy::main(args) {
|
||||
|
@ -12,3 +24,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
_ => Err(ExitCode::Failure),
|
||||
}
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} move {}<src> <dst>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
use crate::{api, sys, usr};
|
||||
use crate::api::console;
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::syscall;
|
||||
use crate::api::process::ExitCode;
|
||||
|
@ -10,9 +11,13 @@ use core::convert::TryInto;
|
|||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() != 2 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
|
||||
if args[1] == "-h" || args[1] == "--help" {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
let mut path = args[1];
|
||||
|
||||
// The commands `read /usr/alice/` and `read /usr/alice` are equivalent,
|
||||
|
@ -114,3 +119,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
Err(ExitCode::Failure)
|
||||
}
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} read {}<path>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!();
|
||||
println!("{}Paths:{}", csi_title, csi_reset);
|
||||
println!(" {0}<dir>/{1} Read directory", csi_option, csi_reset);
|
||||
println!(" {0}<file>{1} Read file", csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -13,8 +13,8 @@ use alloc::vec::Vec;
|
|||
use alloc::string::{String, ToString};
|
||||
|
||||
// TODO: Scan /bin
|
||||
const AUTOCOMPLETE_COMMANDS: [&str; 34] = [
|
||||
"2048", "base64", "calc", "copy", "date", "delete", "dhcp", "disk", "edit", "env",
|
||||
const AUTOCOMPLETE_COMMANDS: [&str; 35] = [
|
||||
"2048", "base64", "calc", "copy", "date", "delete", "dhcp", "disk", "edit", "elf", "env",
|
||||
"goto", "help", "hex", "host", "http", "httpd", "install", "keyboard", "life", "lisp",
|
||||
"list", "memory", "move", "net", "pci", "quit", "read", "shell", "socket", "tcp",
|
||||
"time", "user", "vga", "write"
|
||||
|
@ -610,6 +610,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
|
||||
repl(&mut config)
|
||||
} else {
|
||||
if args[1] == "-h" || args[1] == "--help" {
|
||||
return help();
|
||||
}
|
||||
config.env.insert(0.to_string(), args[1].to_string());
|
||||
|
||||
// Add script arguments to the environment as `$1`, `$2`, `$3`, ...
|
||||
|
@ -632,6 +635,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
}
|
||||
}
|
||||
|
||||
fn help() -> Result<(), ExitCode> {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} shell {}[<file> [<args>]]{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[test_case]
|
||||
fn test_shell() {
|
||||
use alloc::string::ToString;
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
use crate::{api, sys, usr};
|
||||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::io;
|
||||
use crate::api::random;
|
||||
|
@ -13,12 +14,20 @@ use core::str;
|
|||
use sha2::Sha256;
|
||||
|
||||
const USERS: &str = "/ini/users.csv";
|
||||
const COMMANDS: [&str; 2] = ["create", "login"];
|
||||
const DISABLE_EMPTY_PASSWORD: bool = false;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() == 1 || !COMMANDS.contains(&args[1]) {
|
||||
return usage();
|
||||
match *args.get(1).unwrap_or(&"invalid") {
|
||||
"create" => {},
|
||||
"login" => {},
|
||||
"-h" | "--help" => {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
_ => {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
}
|
||||
|
||||
let username: String = if args.len() == 2 {
|
||||
|
@ -31,15 +40,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
match args[1] {
|
||||
"create" => create(&username),
|
||||
"login" => login(&username),
|
||||
_ => usage(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn usage() -> Result<(), ExitCode> {
|
||||
eprintln!("Usage: user [{}] <username>", COMMANDS.join("|"));
|
||||
Err(ExitCode::UsageError)
|
||||
}
|
||||
|
||||
// TODO: Add max number of attempts
|
||||
pub fn login(username: &str) -> Result<(), ExitCode> {
|
||||
if !fs::exists(USERS) {
|
||||
|
@ -214,3 +218,14 @@ fn save_hashed_password(username: &str, hash: &str) -> Result<usize, ()> {
|
|||
|
||||
fs::write(USERS, csv.as_bytes())
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} user {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!();
|
||||
println!("{}Commands:{}", csi_title, csi_reset);
|
||||
println!(" {}create [<user>]{} Create user", csi_option, csi_reset);
|
||||
println!(" {}login [<user>]{} Login user", csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -1,12 +1,17 @@
|
|||
use crate::api::console::Style;
|
||||
use crate::api::fs;
|
||||
use crate::api::process::ExitCode;
|
||||
use crate::api::syscall;
|
||||
|
||||
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
||||
if args.len() != 2 {
|
||||
help();
|
||||
return Err(ExitCode::UsageError);
|
||||
}
|
||||
|
||||
if args[1] == "-h" || args[1] == "--help" {
|
||||
help();
|
||||
return Ok(());
|
||||
}
|
||||
let pathname = args[1];
|
||||
|
||||
// The command `write /usr/alice/` with a trailing slash will create
|
||||
|
@ -27,3 +32,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
|
|||
Err(ExitCode::Failure)
|
||||
}
|
||||
}
|
||||
|
||||
fn help() {
|
||||
let csi_option = Style::color("LightCyan");
|
||||
let csi_title = Style::color("Yellow");
|
||||
let csi_reset = Style::reset();
|
||||
println!("{}Usage:{} write {}<path>{}", csi_title, csi_reset, csi_option, csi_reset);
|
||||
println!();
|
||||
println!("{}Paths:{}", csi_title, csi_reset);
|
||||
println!(" {0}<dir>/{1} Write directory", csi_option, csi_reset);
|
||||
println!(" {0}<file>{1} Write file", csi_option, csi_reset);
|
||||
}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../doc/2048.png
|
|
@ -0,0 +1 @@
|
|||
../doc/boot.png
|
|
@ -0,0 +1,28 @@
|
|||
set -e
|
||||
|
||||
ln -fs ../doc/*.png .
|
||||
|
||||
echo "# MOROS" > ../doc/test.md
|
||||
|
||||
for md in ../doc/*.md; do
|
||||
title="$(head -n 1 $md | sed "s/^#* //")"
|
||||
html="$(basename ${md%.*}.html)"
|
||||
echo "$md => $html"
|
||||
cat << EOF > $html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>$title</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
EOF
|
||||
redcarpet --parse fenced-code-blocks ../doc/$md | sed "s/.md/.html/g" | sed "s/^</ </" | sed "s/ <\/code/<\/code/" >> $html
|
||||
cat << EOF >> $html
|
||||
</body>
|
||||
</html>
|
||||
EOF
|
||||
done
|
||||
|
||||
rm ../doc/test.md
|
|
@ -0,0 +1,50 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Calculator</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Calculator</h1>
|
||||
|
||||
<p>A calculator is available in MOROS using double-precision binary floating-point
|
||||
format (binary64).</p>
|
||||
|
||||
<p>It can be invocked from the shell through the command <code>calc</code> with an operation
|
||||
to perform in arguments:</p>
|
||||
|
||||
<pre><code>> calc 2 + 2
|
||||
4
|
||||
</code></pre>
|
||||
|
||||
<p>And it will open a REPL if no arguments are provided:</p>
|
||||
|
||||
<pre><code>> calc
|
||||
MOROS Calc v0.1.0
|
||||
|
||||
> 2 + 2
|
||||
4
|
||||
</code></pre>
|
||||
|
||||
<p>The following arithmetic operations are supported:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>+</code> addition</li>
|
||||
<li><code>-</code> subtraction</li>
|
||||
<li><code>*</code> multiplication</li>
|
||||
<li><code>/</code> division</li>
|
||||
<li><code>%</code> modulo</li>
|
||||
<li><code>^</code> exponential</li>
|
||||
</ul>
|
||||
|
||||
<p>Parentheses <code>()</code> can change the order of operations:</p>
|
||||
|
||||
<pre><code>> 2 + 3 * 4
|
||||
14
|
||||
|
||||
> (2 + 3) * 4
|
||||
20
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
../doc/chess.png
|
|
@ -0,0 +1 @@
|
|||
../doc/dark.png
|
|
@ -0,0 +1 @@
|
|||
../doc/edit.png
|
|
@ -0,0 +1,27 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Editor</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Editor</h1>
|
||||
|
||||
<h2>Commands</h2>
|
||||
|
||||
<ul>
|
||||
<li><code>CTRL</code> + <code>C</code> to quit without saving and without warnings</li>
|
||||
<li><code>CTRL</code> + <code>Q</code> to quit without saving</li>
|
||||
<li><code>CTRL</code> + <code>X</code> to quit after saving</li>
|
||||
<li><code>CTRL</code> + <code>W</code> to save</li>
|
||||
<li><code>CTRL</code> + <code>A</code> to move cursor to begining of line</li>
|
||||
<li><code>CTRL</code> + <code>E</code> to move cursor to enf of line</li>
|
||||
<li><code>CTRL</code> + <code>T</code> to move cursor to begining of file</li>
|
||||
<li><code>CTRL</code> + <code>B</code> to move cursor to enf of file</li>
|
||||
<li><code>CTRL</code> + <code>d</code> to cut (delete) a line</li>
|
||||
<li><code>CTRL</code> + <code>y</code> to copy (yank) a line</li>
|
||||
<li><code>CTRL</code> + <code>p</code> to paste (put) a line</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,205 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Filesystem</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Filesystem</h1>
|
||||
|
||||
<h2>Hard drive</h2>
|
||||
|
||||
<p>A hard drive is separated in blocks of 512 bytes, grouped into 4 areas:</p>
|
||||
|
||||
<pre><code>+------------+
|
||||
| Boot | (4096 blocks)
|
||||
+------------+
|
||||
| Superblock | (2 blocks)
|
||||
+------------+
|
||||
| Bitmap | (n / (8 * 512) blocks)
|
||||
+------------+
|
||||
| Data | (n blocks)
|
||||
+------------+
|
||||
</code></pre>
|
||||
|
||||
<p>The first area contains the bootloader and the kernel, the second is a
|
||||
superblock with a magic string to identify the file system, the third is a
|
||||
bitmap mapping the allocated data blocks of the last area.</p>
|
||||
|
||||
<p>A location on the tree of dirs and files is named a path:</p>
|
||||
|
||||
<ul>
|
||||
<li>The root dir is represented by a slash: <code>/</code></li>
|
||||
<li>A dir inside the root will have its name appended to the slash: <code>/usr</code></li>
|
||||
<li>Subsequent dirs will append a slash and their names: <code>/usr/admin</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Creation with QEMU</h3>
|
||||
|
||||
<pre><code>$ qemu-img create disk.img 128M
|
||||
Formatting 'disk.img', fmt=raw size=134217728
|
||||
</code></pre>
|
||||
|
||||
<h3>Setup in diskless console</h3>
|
||||
|
||||
<p>During boot MOROS will detect any hard drives present on the ATA buses, then
|
||||
look for a filesystem on those hard drives. If no filesystem is found, MOROS
|
||||
will open a console in diskless mode to allow the user to create one with
|
||||
the <code>disk format</code> command:</p>
|
||||
|
||||
<pre><code>> disk format /dev/ata/0/0
|
||||
</code></pre>
|
||||
|
||||
<p>This command will format the first disk on the first ATA bus by writing a magic
|
||||
string in a superblock, mounting the filesystem, and allocating the root
|
||||
directory.</p>
|
||||
|
||||
<p>The next step during setup is to create the directory structure:</p>
|
||||
|
||||
<pre><code>> write /bin/ # Binaries
|
||||
> write /dev/ # Devices
|
||||
> write /ini/ # Initialisation files
|
||||
> write /lib/ # Libraries
|
||||
> write /net/ # Network
|
||||
> write /src/ # Sources
|
||||
> write /tmp/ # Temporary files
|
||||
> write /usr/ # User directories
|
||||
> write /var/ # Variable files
|
||||
</code></pre>
|
||||
|
||||
<p>Then the following should be added to the boot script with the
|
||||
command <code>edit /ini/boot.sh</code> to allow MOROS to finish booting:</p>
|
||||
|
||||
<pre><code>user login
|
||||
shell
|
||||
</code></pre>
|
||||
|
||||
<p>Finally a user can be created with the following command:</p>
|
||||
|
||||
<pre><code>> user create
|
||||
</code></pre>
|
||||
|
||||
<p>All of this can be made more easily by running the <code>install</code> command instead.
|
||||
This installer will also add additional files contained in the <code>dsk</code>
|
||||
repository of the source code, like a nice login banner :)</p>
|
||||
|
||||
<h2>Data Structures</h2>
|
||||
|
||||
<h3>BlockBitmap</h3>
|
||||
|
||||
<p>Bitmap of allocated blocks in the data area.</p>
|
||||
|
||||
<h3>Block</h3>
|
||||
|
||||
<p>A block is small area of 512 bytes on a hard drive, and it is also part of
|
||||
linked list representing a file or a directory.</p>
|
||||
|
||||
<p>The first 4 bytes of a block is the address of the next block on the list and
|
||||
the rest of block is the data stored in the block.</p>
|
||||
|
||||
<p>Structure:</p>
|
||||
|
||||
<pre><code> 0
|
||||
0 1 2 3 4 5 6 n
|
||||
+-+-+-+-+-+-+-+ // +-+
|
||||
| addr | data |
|
||||
+-+-+-+-+-+-+-+ // +-+
|
||||
|
||||
n = 512
|
||||
</code></pre>
|
||||
|
||||
<h3>Superblock</h3>
|
||||
|
||||
<pre><code> 0 1 2
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 n
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
| signature |v|b| count | alloc | reserved |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
|
||||
signature = "MOROS FS"
|
||||
v = version number of the FS
|
||||
b = size of a block in 2 ^ (9 + b) bytes
|
||||
count = number of blocks
|
||||
alloc = number of allocated blocks
|
||||
</code></pre>
|
||||
|
||||
<h3>File</h3>
|
||||
|
||||
<p>The first block of a contains the address of the next block where its contents
|
||||
is stored and the beginning of its contents in the rest of the block.</p>
|
||||
|
||||
<p>If all contents can fit into one block the address of the next block will be
|
||||
empty.</p>
|
||||
|
||||
<p>Structure:</p>
|
||||
|
||||
<pre><code> 0
|
||||
0 1 2 3 4 5 6 7 8 n
|
||||
+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
| addr | contents |
|
||||
+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
|
||||
n = 512
|
||||
</code></pre>
|
||||
|
||||
<h3>Dir</h3>
|
||||
|
||||
<p>The first block of a directory contains the address of the next block where its
|
||||
directory entries are stored and the first entries in the rest of the block.</p>
|
||||
|
||||
<p>If all entries can fit into one block the address of the next block will be
|
||||
empty.</p>
|
||||
|
||||
<p>Structure:</p>
|
||||
|
||||
<pre><code> 0 1
|
||||
0 1 2 3 4 5 6 7 8 9 0 n
|
||||
+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+ // +-+
|
||||
| addr | dir entry 1 | dir entry 2 |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+ // +-+-+-+-+-+-+-+-+ // +-+
|
||||
|
||||
n = 512
|
||||
</code></pre>
|
||||
|
||||
<h3>DirEntry</h3>
|
||||
|
||||
<p>A directory entry represents a file or a directory contained inside a
|
||||
directory. Each entry use a variable number of bytes that must fit inside the
|
||||
data of one block. Those bytes represent the kind of entry (file or dir), the
|
||||
address of the first block, the filesize (max 4GB), the last modified time in
|
||||
seconds since Unix Epoch, the length of the filename, and the filename (max
|
||||
255 chars) of the entry.</p>
|
||||
|
||||
<p>Structure:</p>
|
||||
|
||||
<pre><code> 0 1 2
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 m
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
|k| addr | size | time |n| name buffer |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
|
||||
k = kind of entry
|
||||
n = length of name buffer
|
||||
m = 17 + n
|
||||
</code></pre>
|
||||
|
||||
<h3>FileInfo</h3>
|
||||
|
||||
<p>The <code>info</code> syscall on a file or directory and the <code>read</code> syscall on a directory
|
||||
return a subset of a directory entry for userspace programs.</p>
|
||||
|
||||
<p>Structure:</p>
|
||||
|
||||
<pre><code> 0 1 2
|
||||
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 m
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
|k| size | time |n| name buffer |
|
||||
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // +-+
|
||||
|
||||
k = kind of entry
|
||||
n = length of name buffer
|
||||
m = 13 + n
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
../doc/find.png
|
|
@ -0,0 +1 @@
|
|||
../doc/hex.png
|
|
@ -1,26 +1,74 @@
|
|||
<!doctype html>
|
||||
<html style="display: grid; place-content: center">
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS: Obscure Rust Operating System</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body style="max-width: 720px; margin: 1em">
|
||||
<body>
|
||||
<h1>MOROS: Obscure Rust Operating System</h1>
|
||||
<img src="moros.png" style="max-width: 100%">
|
||||
<p>
|
||||
MOROS is a hobby operating system written in Rust by
|
||||
<a href="https://vinc.cc">Vincent Ollivier</a>.
|
||||
</p>
|
||||
<p>
|
||||
It targets computers with a x86-64 architecture and a BIOS,
|
||||
so mostly from 2005 to 2020, but it also runs well on most
|
||||
emulators (Bochs, QEMU, and VirtualBox).
|
||||
</p>
|
||||
<p>
|
||||
MOROS is
|
||||
<a href="https://github.com/vinc/moros">free and open source</a>,
|
||||
you can compile it yourself or
|
||||
<a href="moros.bin">download an image</a>.
|
||||
</p>
|
||||
|
||||
<p><img src="moros.png" alt="screenshot"></p>
|
||||
|
||||
<p>MOROS is a hobby operating system written in Rust by <a href="https://vinc.cc">Vincent Ollivier</a>.</p>
|
||||
|
||||
<p>It targets computers with a x86-64 architecture and a BIOS, so mostly from 2005
|
||||
to 2020, but it also runs well on most emulators (Bochs, QEMU, and VirtualBox).</p>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<p>MOROS is open source, you can <a href="https://github.com/vinc/moros">build it</a>
|
||||
or <a href="https://github.com/vinc/moros/releases">download an image</a>. Consult the
|
||||
<a href="manual.html">manual</a> to learn how to use the system.</p>
|
||||
|
||||
<h2>Features</h2>
|
||||
|
||||
<p>Everything in MOROS is done from a command line interface and most programs are
|
||||
rather minimalist.</p>
|
||||
|
||||
<p>It has a <a href="shell.html">shell</a>:</p>
|
||||
|
||||
<p><img src="shell.png" alt="screenshot"></p>
|
||||
|
||||
<p>With a few programs like <code>find</code> that use a <a href="regex.html">regex engine</a> to find
|
||||
files or lines:</p>
|
||||
|
||||
<p><img src="find.png" alt="screenshot"></p>
|
||||
|
||||
<p>It has a <a href="calculator.html">calculator</a> and also a <a href="lisp.html">lisp</a> interpreter:</p>
|
||||
|
||||
<p><img src="lisp.png" alt="screenshot"></p>
|
||||
|
||||
<p>And a <a href="editor.html">text editor</a>:</p>
|
||||
|
||||
<p><img src="edit.png" alt="screenshot"></p>
|
||||
|
||||
<p>It has a <a href="network.html">network stack</a> with two drivers for RTL81339 and PCNET cards:</p>
|
||||
|
||||
<p><img src="network.png" alt="screenshot"></p>
|
||||
|
||||
<p>It has a chess game:</p>
|
||||
|
||||
<p><img src="chess.png" alt="chess"></p>
|
||||
|
||||
<p>And the game of life:</p>
|
||||
|
||||
<p><img src="life.png" alt="life"></p>
|
||||
|
||||
<p>It even has 2048:</p>
|
||||
|
||||
<p><img src="2048.png" alt="2048"></p>
|
||||
|
||||
<p>And finally it is quite customizable:</p>
|
||||
|
||||
<p><img src="light.png" alt="light"></p>
|
||||
|
||||
<h2>Demo</h2>
|
||||
|
||||
<p>You can log in to a demo with the following command using the name of the
|
||||
system as a password for the guest account:</p>
|
||||
|
||||
<pre><code>$ ssh guest@try.moros.cc
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../doc/life.png
|
|
@ -0,0 +1 @@
|
|||
../doc/light.png
|
|
@ -0,0 +1,225 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Lisp</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Lisp</h1>
|
||||
|
||||
<p>A minimalist Lisp interpreter is available in MOROS to extend the capabilities
|
||||
of the Shell.</p>
|
||||
|
||||
<p>MOROS Lisp is a Lisp-1 dialect inspired by Scheme, Clojure, and Ruby!</p>
|
||||
|
||||
<h2>Overview</h2>
|
||||
|
||||
<h3>Types</h3>
|
||||
|
||||
<ul>
|
||||
<li>Basics: <code>bool</code>, <code>list</code>, <code>symbol</code>, <code>string</code></li>
|
||||
<li>Numbers: <code>float</code>, <code>int</code>, <code>bigint</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Built-in Operators</h3>
|
||||
|
||||
<ul>
|
||||
<li><code>quote</code> (with the <code>'</code> syntax)</li>
|
||||
<li><code>quasiquote</code> (with the <code>`</code>)</li>
|
||||
<li><code>unquote</code> (with the <code>,</code> syntax)</li>
|
||||
<li><code>unquote-splice</code> (with the <code>,@</code> syntax)</li>
|
||||
<li><code>splice</code> (with the <code>@</code> syntax)</li>
|
||||
<li><code>atom?</code></li>
|
||||
<li><code>equal?</code> (aliased to <code>eq?</code>)</li>
|
||||
<li><code>head</code></li>
|
||||
<li><code>tail</code></li>
|
||||
<li><code>cons</code></li>
|
||||
<li><code>if</code></li>
|
||||
<li><code>cond</code></li>
|
||||
<li><code>while</code></li>
|
||||
<li><code>variable</code> (aliased to <code>var</code>)</li>
|
||||
<li><code>function</code> (aliased to <code>fun</code>)</li>
|
||||
<li><code>macro</code> (aliased to <code>mac</code>)</li>
|
||||
<li><code>set</code></li>
|
||||
<li><code>define</code> (aliased to <code>def</code> and equivalent to <code>define-function</code>)</li>
|
||||
<li><code>define-function</code> (aliased to <code>def-fun</code>)</li>
|
||||
<li><code>define-macro</code> (aliased to <code>def-mac</code>)</li>
|
||||
<li><code>apply</code></li>
|
||||
<li><code>eval</code></li>
|
||||
<li><code>expand</code></li>
|
||||
<li><code>do</code></li>
|
||||
<li><code>load</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Primitive Operators</h3>
|
||||
|
||||
<ul>
|
||||
<li><code>append</code></li>
|
||||
<li><code>type</code>, <code>number-type</code> (aliased to <code>num-type</code>)</li>
|
||||
<li><code>string</code> (aliased to <code>str</code>)</li>
|
||||
<li><code>string->number</code> (aliased to to <code>str->num</code>)</li>
|
||||
<li><code>string->binary</code> and <code>binary->string</code> (aliased to <code>str->bin</code> and <code>bin->str</code>)</li>
|
||||
<li><code>number->binary</code> and <code>binary->number</code> (aliased to <code>num->bin</code> and <code>bin->num</code>)</li>
|
||||
<li><code>regex-find</code></li>
|
||||
<li><code>system</code></li>
|
||||
<li>Arithmetic operations: <code>+</code>, <code>-</code>, <code>*</code>, <code>/</code>, <code>%</code>, <code>^</code>, <code>abs</code></li>
|
||||
<li>Trigonometric functions: <code>acos</code>, <code>asin</code>, <code>atan</code>, <code>cos</code>, <code>sin</code>, <code>tan</code></li>
|
||||
<li>Comparisons: <code>></code>, <code><</code>, <code>>=</code>, <code><=</code>, <code>=</code></li>
|
||||
<li>File IO: <code>read-file</code>, <code>read-file-binary</code>, <code>write-file-binary</code>, <code>append-file-binary</code></li>
|
||||
<li>List: <code>chunks</code>, <code>sort</code>, <code>unique</code> (aliased to <code>uniq</code>), <code>min</code>, <code>max</code></li>
|
||||
<li>String: <code>trim</code>, <code>split</code></li>
|
||||
<li>Enumerable: <code>length</code> (aliased to <code>len</code>), <code>nth</code>, <code>first</code>, <code>second</code>, <code>third</code>, <code>last</code>, <code>rest</code>, <code>slice</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Core Library</h3>
|
||||
|
||||
<ul>
|
||||
<li><code>nil</code>, <code>nil?</code>, <code>list?</code></li>
|
||||
<li><code>boolean?</code> (aliased to <code>bool?</code>), <code>string?</code> (aliased to <code>str?</code>), <code>symbol?</code> (aliased to <code>sym?</code>), <code>number?</code> (aliased to <code>num?</code>)</li>
|
||||
<li><code>function?</code> (aliased to <code>fun?</code>), <code>macro?</code> (aliased to <code>mac?</code>)</li>
|
||||
<li><code>first</code>, <code>second</code>, <code>third</code>, <code>rest</code></li>
|
||||
<li><code>map</code>, <code>reduce</code>, <code>reverse</code> (aliased to <code>rev</code>), <code>range</code>, <code>filter</code>, <code>intersection</code></li>
|
||||
<li><code>not</code>, <code>and</code>, <code>or</code></li>
|
||||
<li><code>let</code></li>
|
||||
<li><code>join-string</code> (aliased to <code>join-str</code>), <code>lines</code>, <code>words</code>, <code>chars</code></li>
|
||||
<li><code>read-line</code>, <code>read-char</code></li>
|
||||
<li><code>p</code>, <code>print</code></li>
|
||||
<li><code>write-file</code>, <code>append-file</code></li>
|
||||
<li><code>uptime</code>, <code>realtime</code></li>
|
||||
<li><code>regex-match?</code></li>
|
||||
</ul>
|
||||
|
||||
<h3>Compatibility Library</h3>
|
||||
|
||||
<ul>
|
||||
<li><code>atom</code>, <code>eq</code>, <code>label</code>, <code>lambda</code>, <code>progn</code>, <code>begin</code></li>
|
||||
<li><code>car</code>, <code>cdr</code>, <code>caar</code>, <code>cadr</code>, <code>cdar</code>, <code>cddr</code></li>
|
||||
</ul>
|
||||
|
||||
<h2>Usage</h2>
|
||||
|
||||
<p>The interpreter can be invoked from the shell:</p>
|
||||
|
||||
<pre><code>> lisp
|
||||
MOROS Lisp v0.4.0
|
||||
|
||||
> (+ 1 2 3)
|
||||
6
|
||||
|
||||
> (quit)
|
||||
</code></pre>
|
||||
|
||||
<p>And it can execute a file. For example a file located in <code>/tmp/lisp/fibonacci.lsp</code>
|
||||
with the following content:</p>
|
||||
|
||||
<pre><code class="lisp">(load "/lib/lisp/core.lsp")
|
||||
|
||||
(def (fibonacci n)
|
||||
(if (< n 2) n
|
||||
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
|
||||
|
||||
(print
|
||||
(if (nil? args) "Usage: fibonacci <num>"
|
||||
(fibonacci (str->num (head args)))))
|
||||
</code></pre>
|
||||
|
||||
<p>Would produce the following output:</p>
|
||||
|
||||
<pre><code>> lisp /tmp/lisp/fibonacci.lsp 20
|
||||
6755
|
||||
</code></pre>
|
||||
|
||||
<h2>Examples</h2>
|
||||
|
||||
<pre><code class="lisp">(load "/lib/lisp/core.lsp")
|
||||
|
||||
(print "Hello, World!")
|
||||
|
||||
(var foo 42) # Variable definition
|
||||
(set foo (+ 40 2)) # Variable assignement
|
||||
|
||||
(var double (fun (x) (* x 2))) # Function definition
|
||||
(def (double x) (* x 2)) # Shortcut
|
||||
|
||||
(double foo) # => 84
|
||||
|
||||
(def-mac (++ x) # Macro definition
|
||||
`(set ,x (+ ,x 1)))
|
||||
|
||||
(var i 0)
|
||||
(while (< i 10)
|
||||
(++ i))
|
||||
(= i 10) # => true
|
||||
|
||||
(def (map f ls)
|
||||
(if (nil? ls) nil
|
||||
(cons
|
||||
(f (first ls))
|
||||
(map f (rest ls)))))
|
||||
|
||||
(var bar (quote (1 2 3)))
|
||||
(var bar '(1 2 3)) # Shortcut
|
||||
|
||||
(map double bar) # => (2 4 6)
|
||||
|
||||
(map (fun (x) (+ x 1)) '(4 5 6)) # => (5 6 7)
|
||||
|
||||
(var name "Alice")
|
||||
|
||||
(str "Hello, " name) # => "Hello, Alice"
|
||||
|
||||
(^ 2 64) # => 18446744073709551616
|
||||
</code></pre>
|
||||
|
||||
<h2>Changelog</h2>
|
||||
|
||||
<h3>0.1.0 (2021-07-21)</h3>
|
||||
|
||||
<p>MOROS Lisp started from <a href="https://github.com/stopachka/risp">Risp</a> and was
|
||||
extended to include the seven primitive operators and the two special forms of
|
||||
John McCarthy's paper "Recursive Functions of Symbolic Expressions and Their
|
||||
Computation by Machine" (1960) and "The Roots of Lisp" (2002) by Paul Graham.</p>
|
||||
|
||||
<h3>0.2.0 (2021-12-04)</h3>
|
||||
|
||||
<p>The whole implementation was refactored and the parser was rewritten to use
|
||||
<a href="https://github.com/Geal/nom">Nom</a>. This allowed the addition of strings to the
|
||||
language and reading from the filesystem.</p>
|
||||
|
||||
<h3>0.3.0 (2022-12-12)</h3>
|
||||
|
||||
<p>Rewrite the evaluation code, add new functions and a core library.</p>
|
||||
|
||||
<h3>0.3.1 (2022-06-06)</h3>
|
||||
|
||||
<p>Rewrite parts of the code and add new functions and examples.</p>
|
||||
|
||||
<h3>0.3.2 (2022-07-02)</h3>
|
||||
|
||||
<ul>
|
||||
<li>Add new functions</li>
|
||||
</ul>
|
||||
|
||||
<h3>0.3.2 (2022-08-25)</h3>
|
||||
|
||||
<ul>
|
||||
<li>Add new functions</li>
|
||||
</ul>
|
||||
|
||||
<h3>0.4.0 (2022-08-25)</h3>
|
||||
|
||||
<ul>
|
||||
<li>Rewrite a lot of the code</li>
|
||||
<li>Add integer and big integer support</li>
|
||||
<li>Add tail call optimization (TCO)</li>
|
||||
<li>Add macro support</li>
|
||||
</ul>
|
||||
|
||||
<h3>0.5.0 (unpublished)</h3>
|
||||
|
||||
<ul>
|
||||
<li>Rename or add aliases to many functions</li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
../doc/lisp.png
|
|
@ -0,0 +1,380 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Manual</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Manual</h1>
|
||||
|
||||
<h2>Boot</h2>
|
||||
|
||||
<p>During boot MOROS will display its version followed by the memory layout,
|
||||
memory size, processor, devices, network cards, disks, and the real time clock.</p>
|
||||
|
||||
<pre><code>[0.250962] MOROS v0.9.0
|
||||
[0.250962] MEM [0x00000000000000-0x00000000000FFF] FrameZero
|
||||
[0.250962] MEM [0x00000000001000-0x00000000004FFF] PageTable
|
||||
[0.250962] MEM [0x00000000005000-0x00000000015FFF] Bootloader
|
||||
[0.250962] MEM [0x00000000016000-0x00000000016FFF] BootInfo
|
||||
[0.250962] MEM [0x00000000017000-0x0000000002DFFF] Kernel
|
||||
[0.250962] MEM [0x0000000002E000-0x0000000009EFFF] KernelStack
|
||||
[0.250962] MEM [0x0000000009F000-0x0000000009FFFF] Reserved
|
||||
[0.250962] MEM [0x000000000F0000-0x000000000FFFFF] Reserved
|
||||
[0.250962] MEM [0x00000000100000-0x0000000028EFFF] KernelStack
|
||||
[0.250962] MEM [0x0000000028F000-0x000000003FFFFF] Usable
|
||||
[0.250962] MEM [0x00000000400000-0x000000005E1FFF] Kernel
|
||||
[0.250962] MEM [0x000000005E2000-0x000000005EFFFF] PageTable
|
||||
[0.250962] MEM [0x000000005F0000-0x00000001FDFFFF] Usable
|
||||
[0.250962] MEM [0x00000001FE0000-0x00000001FFFFFF] Reserved
|
||||
[0.250962] MEM [0x000000FEFFC000-0x000000FEFFFFFF] Reserved
|
||||
[0.250962] MEM [0x000000FFFC0000-0x000000FFFFFFFF] Reserved
|
||||
[0.250962] MEM 32720 KB
|
||||
[0.251962] CPU GenuineIntel
|
||||
[0.254961] CPU Intel(R) Core(TM) i5-4300M CPU @ 2.60GHz
|
||||
[0.284957] PCI 0000:00:00 [8086:1237]
|
||||
[0.312952] PCI 0000:01:00 [8086:7000]
|
||||
[0.321951] PCI 0000:01:01 [8086:7010]
|
||||
[0.321951] PCI 0000:01:03 [8086:7113]
|
||||
[0.322951] PCI 0000:02:00 [1234:1111]
|
||||
[0.323951] PCI 0000:03:00 [10EC:8139]
|
||||
[0.377942] NET RTL8139 MAC 52-54-00-12-34-56
|
||||
[0.382942] ATA 0:0 QEMU HARDDISK QM00001 (32 MB)
|
||||
[0.384941] MFS Superblock found in ATA 0:0
|
||||
[0.386941] RTC 2023-04-17 20:00:28 +0000
|
||||
</code></pre>
|
||||
|
||||
<h2>Installation</h2>
|
||||
|
||||
<p>The first time MOROS will boot in diskless mode where you can use the builtin
|
||||
commands to test the system or <code>install</code> to setup the
|
||||
<a href="filesystem.html">filesystem</a> on a disk:</p>
|
||||
|
||||
<pre><code>MFS is not mounted to '/'
|
||||
Running console in diskless mode
|
||||
|
||||
/
|
||||
> install
|
||||
Welcome to MOROS v0.9.0 installation program!
|
||||
|
||||
Proceed? [y/N] y
|
||||
|
||||
Listing disks ...
|
||||
Path Name (Size)
|
||||
/dev/ata/0/0 QEMU HARDDISK QM00001 (32 MB)
|
||||
|
||||
Formatting disk ...
|
||||
Enter path of disk to format: /dev/ata/0/0
|
||||
Disk successfully formatted
|
||||
MFS is now mounted to '/'
|
||||
|
||||
Populating filesystem...
|
||||
Created '/bin'
|
||||
Created '/dev'
|
||||
Created '/ini'
|
||||
Created '/lib'
|
||||
Created '/net'
|
||||
Created '/src'
|
||||
Created '/tmp'
|
||||
Created '/usr'
|
||||
Created '/var'
|
||||
Copied '/bin/clear'
|
||||
Copied '/bin/halt'
|
||||
Copied '/bin/hello'
|
||||
Copied '/bin/print'
|
||||
Copied '/bin/reboot'
|
||||
Copied '/bin/sleep'
|
||||
Created '/dev/clk'
|
||||
Created '/dev/clk/uptime'
|
||||
Created '/dev/clk/realtime'
|
||||
Created '/dev/rtc'
|
||||
Created '/dev/null'
|
||||
Created '/dev/random'
|
||||
Created '/dev/console'
|
||||
Copied '/ini/banner.txt'
|
||||
Copied '/ini/boot.sh'
|
||||
Copied '/ini/shell.sh'
|
||||
Copied '/ini/version.txt'
|
||||
Created '/ini/palettes'
|
||||
Copied '/ini/palettes/gruvbox-dark.csv'
|
||||
Copied '/ini/palettes/gruvbox-light.csv'
|
||||
Created '/ini/fonts'
|
||||
Copied '/ini/fonts/zap-light-8x16.psf'
|
||||
Copied '/ini/fonts/zap-vga-8x16.psf'
|
||||
Created '/lib/lisp'
|
||||
Copied '/lib/lisp/core.lsp'
|
||||
Copied '/lib/lisp/alias.lsp'
|
||||
Copied '/tmp/alice.txt'
|
||||
Copied '/tmp/machines.txt'
|
||||
Created '/tmp/lisp'
|
||||
Copied '/tmp/lisp/colors.lsp'
|
||||
Copied '/tmp/lisp/factorial.lsp'
|
||||
Copied '/tmp/lisp/fibonacci.lsp'
|
||||
Copied '/tmp/lisp/geotime.lsp'
|
||||
Copied '/tmp/lisp/pi.lsp'
|
||||
Copied '/tmp/lisp/sum.lsp'
|
||||
Created '/tmp/life'
|
||||
Copied '/tmp/life/centinal.cells'
|
||||
Copied '/tmp/life/flower-of-eden.cells'
|
||||
Copied '/tmp/life/garden-of-eden.cells'
|
||||
Copied '/tmp/life/glider-gun.cells'
|
||||
Copied '/tmp/life/pentadecathlon.cells'
|
||||
Copied '/tmp/life/queen-bee-shuttle.cells'
|
||||
Copied '/tmp/life/ship-in-a-bottle.cells'
|
||||
Copied '/tmp/life/thunderbird.cells'
|
||||
Copied '/tmp/life/wing.cells'
|
||||
Created '/tmp/beep'
|
||||
Copied '/tmp/beep/tetris.sh'
|
||||
Copied '/tmp/beep/starwars.sh'
|
||||
Copied '/tmp/beep/mario.sh'
|
||||
Created '/var/www'
|
||||
Copied '/var/www/index.html'
|
||||
Copied '/var/www/moros.png'
|
||||
|
||||
Creating user...
|
||||
Username: vinc
|
||||
Password:
|
||||
Confirm:
|
||||
|
||||
Installation successful!
|
||||
|
||||
Quit the console or reboot to apply changes
|
||||
</code></pre>
|
||||
|
||||
<p>You can then use <code>^D</code> (a key combination of <code>CTRL</code> and <code>D</code>) to quit the
|
||||
diskless mode and let MOROS run the bootscript <code>/ini/boot.sh</code> to login and use
|
||||
the shell.</p>
|
||||
|
||||
<p>If no disks were detected or if you prefer not to use them you can mount the
|
||||
system in memory to use a virtual disk with <code>memory format</code> before <code>install</code>.</p>
|
||||
|
||||
<h2>Shell</h2>
|
||||
|
||||
<p>The <a href="shell.html">shell</a> is the primary command line interface to use MOROS.
|
||||
This is were you can type a command and its arguments to tell the system what
|
||||
to do:</p>
|
||||
|
||||
<pre><code>~
|
||||
> print "Hello, World!"
|
||||
Hello, World!
|
||||
</code></pre>
|
||||
|
||||
<p>The system has a <code>help</code> command to help you remember the basic commands.</p>
|
||||
|
||||
<p>Most commands also have a special <code>--help</code> argument to show all their options.</p>
|
||||
|
||||
<h2>Directories</h2>
|
||||
|
||||
<p>The line above the command prompt tells you where you are in the disk. The
|
||||
tilde <code>~</code> means that you are in your home directory:</p>
|
||||
|
||||
<pre><code>~
|
||||
> print $DIR
|
||||
/usr/vinc
|
||||
</code></pre>
|
||||
|
||||
<p>You can change directory by typing it as if it was a command:</p>
|
||||
|
||||
<pre><code>~
|
||||
> /tmp
|
||||
|
||||
/tmp
|
||||
> print $DIR
|
||||
/tmp
|
||||
</code></pre>
|
||||
|
||||
<p>From now on we'll omit the directory line in most examples.</p>
|
||||
|
||||
<p>You can list the content of a directory with <code>list</code>:</p>
|
||||
|
||||
<pre><code>> list /tmp
|
||||
5090 2023-04-17 06:25:54 alice.txt
|
||||
82 2023-04-17 06:25:55 beep
|
||||
324 2023-04-17 06:25:55 life
|
||||
168 2023-04-17 06:25:55 lisp
|
||||
649 2023-04-17 06:25:54 machines.txt
|
||||
</code></pre>
|
||||
|
||||
<p>The command has some options to sort the results:</p>
|
||||
|
||||
<pre><code>> list --help
|
||||
Usage: list <options> [<dir>]
|
||||
|
||||
Options:
|
||||
-a, --all Show dot files
|
||||
-n, --name Sort by name
|
||||
-s, --size Sort by size
|
||||
-t, --time Sort by time
|
||||
</code></pre>
|
||||
|
||||
<p>You can write a directory in the disk with <code>write</code>:</p>
|
||||
|
||||
<pre><code>> write test/
|
||||
|
||||
> list
|
||||
5090 2023-04-17 06:25:54 alice.txt
|
||||
82 2023-04-17 06:25:55 beep
|
||||
324 2023-04-17 06:25:55 life
|
||||
168 2023-04-17 06:25:55 lisp
|
||||
649 2023-04-17 06:25:54 machines.txt
|
||||
0 2023-04-17 07:06:18 test
|
||||
</code></pre>
|
||||
|
||||
<p>The slash <code>/</code> at the end of <code>test/</code> is there to tell the <code>write</code> command to
|
||||
create a directory instead of a file.</p>
|
||||
|
||||
<h2>Files</h2>
|
||||
|
||||
<p>You can create a file by redirecting the output of a command with an arrow <code>=></code>
|
||||
to the file:</p>
|
||||
|
||||
<pre><code>> print "Hello, World!" => hello.txt
|
||||
</code></pre>
|
||||
|
||||
<p>The command <code>read</code> will read the content of the file:</p>
|
||||
|
||||
<pre><code>> read hello.txt
|
||||
Hello, World!
|
||||
</code></pre>
|
||||
|
||||
<p>You can edit a file with the <code>edit</code> command that will run the text editor.</p>
|
||||
|
||||
<p>Use <code>^W</code> (a key combination of <code>CTRL</code> and <code>W</code>) inside the editor to write the
|
||||
content to the file and <code>^Q</code> to quit the editor and go back to the shell.</p>
|
||||
|
||||
<p>The help command has a subcommand <code>help edit</code> to list the editor commands:</p>
|
||||
|
||||
<pre><code>> help edit
|
||||
MOROS text editor is a very simple editor inspired by Pico, Nano, and Micro.
|
||||
|
||||
Commands:
|
||||
^Q Quit editor
|
||||
^W Write to file
|
||||
^X Write to file and quit
|
||||
^T Go to top of file
|
||||
^B Go to bottom of file
|
||||
^A Go to beginning of line
|
||||
^E Go to end of line
|
||||
^D Cut line
|
||||
^Y Copy line
|
||||
^P Paste line
|
||||
</code></pre>
|
||||
|
||||
<h2>Time</h2>
|
||||
|
||||
<p>You can print the date with <code>date</code>:</p>
|
||||
|
||||
<pre><code>> date
|
||||
2001-01-01 00:00:00 +0000
|
||||
</code></pre>
|
||||
|
||||
<p>You can update the real time clock by writing the correct time to its device
|
||||
file:</p>
|
||||
|
||||
<pre><code>> print "2023-03-21 10:00:00" => /dev/rtc
|
||||
|
||||
> date
|
||||
2023-03-21 10:00:00 +0000
|
||||
</code></pre>
|
||||
|
||||
<p>You can also set the <code>TZ</code> environment variable to use your preferred timezone:</p>
|
||||
|
||||
<pre><code>> calc "2 * 60 * 60"
|
||||
7200
|
||||
|
||||
> env TZ 7200
|
||||
|
||||
> date
|
||||
2023-03-21 12:00:00 +0200
|
||||
</code></pre>
|
||||
|
||||
<p>Add <code>env TZ 7200</code> to <code>/ini/boot.sh</code> before <code>shell</code> to save the timezone:</p>
|
||||
|
||||
<pre><code>> read /ini/boot.sh
|
||||
vga set palette /ini/palettes/gruvbox-dark.csv
|
||||
vga set font /ini/fonts/zap-light-8x16.psf
|
||||
read /ini/banner.txt
|
||||
user login
|
||||
env TZ 7200
|
||||
shell
|
||||
</code></pre>
|
||||
|
||||
<p>There's a device file to get the number of seconds elapsed since Unix Epoch:</p>
|
||||
|
||||
<pre><code>> read /dev/clk/realtime
|
||||
1682105344.624905
|
||||
</code></pre>
|
||||
|
||||
<p>And another one since boot:</p>
|
||||
|
||||
<pre><code>> read /dev/clk/uptime
|
||||
1169.384929
|
||||
</code></pre>
|
||||
|
||||
<h2>Aliases</h2>
|
||||
|
||||
<p>You can add custom commands to the shell with the <code>alias</code> command.</p>
|
||||
|
||||
<p>For example you can define an <code>uptime</code> command that will read the device file
|
||||
described above:</p>
|
||||
|
||||
<pre><code>> alias uptime "read /dev/clk/uptime"
|
||||
|
||||
> uptime
|
||||
1406.304852
|
||||
</code></pre>
|
||||
|
||||
<p>You can add that command to <code>/ini/shell.sh</code> to save it.</p>
|
||||
|
||||
<p>Some shortcuts have been defined in that file for the most frequent commands,
|
||||
for example you can use <code>e</code> instead of <code>edit</code> to edit a file.</p>
|
||||
|
||||
<pre><code>> read /ini/shell.sh
|
||||
# Command shortcuts
|
||||
alias c copy
|
||||
alias d delete
|
||||
alias e edit
|
||||
alias f find
|
||||
alias h help
|
||||
alias l list
|
||||
alias m move
|
||||
alias p print
|
||||
alias q quit
|
||||
alias r read
|
||||
alias w write
|
||||
|
||||
alias sh shell
|
||||
alias dsk disk
|
||||
alias mem memory
|
||||
alias kbd keyboard
|
||||
</code></pre>
|
||||
|
||||
<h2>Network</h2>
|
||||
|
||||
<p>You can setup the <a href="network.html">network</a> manually with <code>net</code> or automatically
|
||||
with <code>dhcp</code>:</p>
|
||||
|
||||
<pre><code>> dhcp
|
||||
ip: 10.0.2.15/24
|
||||
gw: 10.0.2.2
|
||||
dns: 10.0.2.3
|
||||
</code></pre>
|
||||
|
||||
<p>A few tools are available like the <code>http</code> command:</p>
|
||||
|
||||
<pre><code>> http moros.cc /test.html
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS</h1>
|
||||
</body>
|
||||
</html>
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -1 +0,0 @@
|
|||
../target/x86_64-moros/release/bootimage-moros.bin
|
|
@ -0,0 +1,22 @@
|
|||
html {
|
||||
display: grid;
|
||||
place-content: center;
|
||||
}
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
max-width: 720px;
|
||||
margin: 1rem;
|
||||
color: #111;
|
||||
font-size: 18px;
|
||||
}
|
||||
li code,
|
||||
p code {
|
||||
background: #eee;
|
||||
padding: 0.125rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
||||
pre {
|
||||
background: #eee;
|
||||
padding: 1rem;
|
||||
border-radius: 0.25rem;
|
||||
}
|
|
@ -0,0 +1,186 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Network</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Network</h1>
|
||||
|
||||
<h2>NET</h2>
|
||||
|
||||
<p>Display the network configuration:</p>
|
||||
|
||||
<pre><code>> net config
|
||||
mac: 52-54-00-12-34-56
|
||||
ip: 10.0.2.15/24
|
||||
gw: 10.0.2.2
|
||||
dns: 10.0.2.3
|
||||
</code></pre>
|
||||
|
||||
<p>Display one attribute of the network configuration:</p>
|
||||
|
||||
<pre><code>> net config dns
|
||||
dns: 10.0.2.3
|
||||
</code></pre>
|
||||
|
||||
<p>Set one attribute of the network configuration:</p>
|
||||
|
||||
<pre><code>> net config dns 10.0.2.3
|
||||
</code></pre>
|
||||
|
||||
<p>Display network statistics:</p>
|
||||
|
||||
<pre><code>> net stat
|
||||
rx: 13 packets (4052 bytes)
|
||||
tx: 15 packets (1518 bytes)
|
||||
</code></pre>
|
||||
|
||||
<p>Listen for packets transmitted on the network:</p>
|
||||
|
||||
<pre><code>> net monitor
|
||||
------------------------------------------------------------------
|
||||
[488.396667] NET RTL8139 Receiving:
|
||||
00000000: 3333 0000 0001 5256 0000 0002 86DD 6000 33....RV......`.
|
||||
00000010: 0000 0038 3AFF FE80 0000 0000 0000 0000 ...8:...........
|
||||
00000020: 0000 0000 0002 FF02 0000 0000 0000 0000 ................
|
||||
00000030: 0000 0000 0001 8600 155E 4000 0708 0000 .........^@.....
|
||||
00000040: 0000 0000 0000 0101 5256 0000 0002 0304 ........RV......
|
||||
00000050: 40C0 0001 5180 0000 3840 0000 0000 FEC0 @...Q...8@......
|
||||
00000060: 0000 0000 0000 0000 0000 0000 0000 ..............
|
||||
------------------------------------------------------------------
|
||||
[543.871322] NET RTL8139 Receiving:
|
||||
00000000: 5254 0012 3456 5255 0A00 0202 0800 4500 RT..4VRU .....E.
|
||||
00000010: 002C 0001 0000 4006 62BB 0A00 0202 0A00 .,....@.b. ... .
|
||||
00000020: 020F A2E8 0016 0412 F801 0000 0000 6002 ..............`.
|
||||
00000030: 2238 BECB 0000 0204 05B4 0000 "8..........
|
||||
------------------------------------------------------------------
|
||||
</code></pre>
|
||||
|
||||
<h2>DHCP</h2>
|
||||
|
||||
<p>The <code>dhcp</code> command configures the network automatically:</p>
|
||||
|
||||
<pre><code>> dhcp --verbose
|
||||
DEBUG: DHCP Discover transmitted
|
||||
DEBUG: DHCP Offer received
|
||||
ip: 10.0.2.15/24
|
||||
gw: 10.0.2.2
|
||||
dns: 10.0.2.3
|
||||
</code></pre>
|
||||
|
||||
<h2>HOST</h2>
|
||||
|
||||
<p>The <code>host</code> command performs DNS lookups:</p>
|
||||
|
||||
<pre><code>> host example.com
|
||||
example.com has address 93.184.216.34
|
||||
</code></pre>
|
||||
|
||||
<h2>TCP</h2>
|
||||
|
||||
<p>The <code>tcp</code> command connects to TCP sockets:</p>
|
||||
|
||||
<pre><code>> tcp time.nist.gov 13 --verbose
|
||||
DEBUG: Connecting to 129.6.15.30:13
|
||||
|
||||
58884 20-02-05 19:19:42 00 0 0 49.2 UTC(NIST) *
|
||||
</code></pre>
|
||||
|
||||
<p>This could also be done with the <code>read</code> command:</p>
|
||||
|
||||
<pre><code>> read /net/tcp/time.nist.gov:13
|
||||
|
||||
58884 20-02-05 19:19:55 00 0 0 49.2 UTC(NIST) *
|
||||
</code></pre>
|
||||
|
||||
<h2>HTTP</h2>
|
||||
|
||||
<p>Requesting a resource on a host:</p>
|
||||
|
||||
<pre><code>> http moros.cc /test.html
|
||||
</code></pre>
|
||||
|
||||
<p>Is equivalent to:</p>
|
||||
|
||||
<pre><code>> read /net/http/moros.cc/test.html
|
||||
</code></pre>
|
||||
|
||||
<p>And:</p>
|
||||
|
||||
<pre><code>> read /net/http/moros.cc:80/test.html
|
||||
</code></pre>
|
||||
|
||||
<h2>SOCKET</h2>
|
||||
|
||||
<p>The <code>socket</code> command is used to read and write to network connexions
|
||||
like the <code>netcat</code> command on Unix.</p>
|
||||
|
||||
<p>For example the request made with <code>tcp</code> above is equivalent to this:</p>
|
||||
|
||||
<pre><code>> socket time.nist.gov 13 --read-only
|
||||
|
||||
59710 22-05-11 21:44:52 50 0 0 359.3 UTC(NIST) *
|
||||
</code></pre>
|
||||
|
||||
<p>And the request made with <code>http</code> is equivalent to that:</p>
|
||||
|
||||
<pre><code>> socket moros.cc 80 --prompt
|
||||
MOROS Socket v0.1.0
|
||||
|
||||
> GET /test.html HTTP/1.0
|
||||
> Host: moros.cc
|
||||
>
|
||||
HTTP/1.1 200 OK
|
||||
Server: nginx
|
||||
Date: Wed, 11 May 2022 21:46:34 GMT
|
||||
Content-Type: text/html
|
||||
Content-Length: 866
|
||||
Connection: close
|
||||
Last-Modified: Fri, 29 Oct 2021 17:50:58 GMT
|
||||
ETag: "617c3482-362"
|
||||
Accept-Ranges: bytes
|
||||
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS: Obscure Rust Operating System</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS</h1>
|
||||
</body>
|
||||
</html>
|
||||
</code></pre>
|
||||
|
||||
<p>Here's a connexion to a SMTP server to send a mail:</p>
|
||||
|
||||
<pre><code>> socket 10.0.2.2 2500 --prompt
|
||||
MOROS Socket v0.1.0
|
||||
|
||||
220 EventMachine SMTP Server
|
||||
> EHLO moros.cc
|
||||
250-Ok EventMachine SMTP Server
|
||||
250-NO-SOLICITING
|
||||
250 SIZE 20000000
|
||||
> MAIL FROM:<v@moros.cc>
|
||||
> RCPT TO:<alice@example.com>
|
||||
250 Ok
|
||||
250 Ok
|
||||
> DATA
|
||||
354 Send it
|
||||
> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum nec
|
||||
> diam vitae ex blandit malesuada nec a turpis.
|
||||
> .
|
||||
> QUIT
|
||||
250 Message accepted
|
||||
221 Ok
|
||||
</code></pre>
|
||||
|
||||
<p>Sending a file to a server:</p>
|
||||
|
||||
<pre><code>> socket 10.0.2.2 1234 <= /tmp/alice.txt
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
../doc/network.png
|
|
@ -0,0 +1,33 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Regular Expression Engine</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Regular Expression Engine</h1>
|
||||
|
||||
<p>MOROS include a simplified regular expression engine with the following syntax:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>\</code> escape the following character to its literal meaning</li>
|
||||
<li><code>^</code> matches the starting position within the string</li>
|
||||
<li><code>$</code> matches the ending position within the string</li>
|
||||
<li><code>*</code> matches the preceding element zero or more times</li>
|
||||
<li><code>+</code> matches the preceding element one or more times</li>
|
||||
<li><code>?</code> matches the preceding element zero or one time</li>
|
||||
<li><code>.</code> matches any single character</li>
|
||||
<li><code>\w</code> matches any alphanumeric character</li>
|
||||
<li><code>\W</code> matches any non-alphanumeric character</li>
|
||||
<li><code>\d</code> matches any numeric character</li>
|
||||
<li><code>\D</code> matches any non-numeric character</li>
|
||||
<li><code>\w</code> matches any whitespace character</li>
|
||||
<li><code>\W</code> matches any whitespace character</li>
|
||||
</ul>
|
||||
|
||||
<p>The engine is UTF-8 aware, so for example the unicode character <code>é</code> will be
|
||||
matched by <code>\w</code> even if it's not present in the ASCII table and has a size
|
||||
of two bytes.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,242 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Shell</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Shell</h1>
|
||||
|
||||
<h2>Configuration</h2>
|
||||
|
||||
<p>The shell will read <code>/ini/shell.sh</code> during initialization to setup its
|
||||
configuration.</p>
|
||||
|
||||
<h2>Commands</h2>
|
||||
|
||||
<p>The main commands have a long name, a one-letter alias, and may have
|
||||
additional common aliases.</p>
|
||||
|
||||
<p><strong>Alias</strong> command:</p>
|
||||
|
||||
<pre><code>> alias d delete
|
||||
</code></pre>
|
||||
|
||||
<!--
|
||||
**Append** to file:
|
||||
|
||||
> a a.txt
|
||||
> append a.txt
|
||||
-->
|
||||
|
||||
<p><strong>Delete</strong> file:</p>
|
||||
|
||||
<pre><code>> d a.txt
|
||||
> del a.txt
|
||||
> delete a.txt
|
||||
</code></pre>
|
||||
|
||||
<p><strong>Copy</strong> file:</p>
|
||||
|
||||
<pre><code>> c a.txt b.txt
|
||||
> copy a.txt b.txt
|
||||
</code></pre>
|
||||
|
||||
<p><strong>Move</strong> file:</p>
|
||||
|
||||
<pre><code>> m a.txt b.txt
|
||||
> move a.txt b.txt
|
||||
</code></pre>
|
||||
|
||||
<p><strong>Print</strong> string:</p>
|
||||
|
||||
<pre><code>> p "Hi"
|
||||
> print "Hi"
|
||||
</code></pre>
|
||||
|
||||
<p><strong>Read</strong> file:</p>
|
||||
|
||||
<pre><code>> r a.txt
|
||||
> read a.txt
|
||||
</code></pre>
|
||||
|
||||
<p><strong>Write</strong> file:</p>
|
||||
|
||||
<pre><code>> w a.txt
|
||||
> write a.txt
|
||||
</code></pre>
|
||||
|
||||
<p><strong>Write</strong> dir:</p>
|
||||
|
||||
<pre><code>> write /usr/alice/ # with a trailing slash to create a dir instead of a file
|
||||
</code></pre>
|
||||
|
||||
<p><strong>List</strong> files in dir:</p>
|
||||
|
||||
<pre><code>> list /usr/alice
|
||||
</code></pre>
|
||||
|
||||
<p>When executed without arguments, this command will list the files of the
|
||||
current directory.</p>
|
||||
|
||||
<p><strong>Go to</strong> dir:</p>
|
||||
|
||||
<pre><code>> goto /usr/alice
|
||||
</code></pre>
|
||||
|
||||
<p>When executed without arguments, this command will print the current directory.</p>
|
||||
|
||||
<h2>Combiners (TODO)</h2>
|
||||
|
||||
<p><strong>And combiner:</strong></p>
|
||||
|
||||
<pre><code>> read foo.txt and read bar.txt
|
||||
</code></pre>
|
||||
|
||||
<p><strong>Or combiners:</strong></p>
|
||||
|
||||
<pre><code>> read foo.txt or read bar.txt
|
||||
</code></pre>
|
||||
|
||||
<h2>Pipes and redirections (WIP)</h2>
|
||||
|
||||
<p>A thin arrow <code>-></code> can be used for piping the output from one command to the
|
||||
input of another command (TODO):</p>
|
||||
|
||||
<pre><code>> read foo.txt -> write bar.txt
|
||||
</code></pre>
|
||||
|
||||
<p>A fat arrow <code>=></code> can be used for redirecting directly to a file:</p>
|
||||
|
||||
<pre><code>> read foo.txt => bar.txt
|
||||
</code></pre>
|
||||
|
||||
<p>In the following example the standard output is redirected to the null device
|
||||
file while the standard error is kept:</p>
|
||||
|
||||
<pre><code>> time read foo.txt => /dev/null
|
||||
</code></pre>
|
||||
|
||||
<p>The standard output is implied as the source of a redirection, but it is
|
||||
possible to explicitly redirect a file handle to another (TODO):</p>
|
||||
|
||||
<pre><code>> time read foo.txt [1]=>[3]
|
||||
</code></pre>
|
||||
|
||||
<p>Or to redirect a file handle to a file:</p>
|
||||
|
||||
<pre><code>> time read foo.txt [1]=> bar.txt
|
||||
</code></pre>
|
||||
|
||||
<p>Or to pipe a file handle to another command:</p>
|
||||
|
||||
<pre><code>> time read foo.txt [1]-> write bar.txt
|
||||
</code></pre>
|
||||
|
||||
<p>It is possible to chain multiple redirections:</p>
|
||||
|
||||
<pre><code>> time read foo.txt [1]=> bar.txt [2]=> time.txt
|
||||
</code></pre>
|
||||
|
||||
<p>When the arrow point to the other direction the source and destination are
|
||||
swapped and the standard input is implied (TODO):</p>
|
||||
|
||||
<pre><code>> write <= req.txt => /net/http/moros.cc
|
||||
</code></pre>
|
||||
|
||||
<p>Redirections should be declared before piping (TODO):</p>
|
||||
|
||||
<pre><code>> write <= req.txt => /net/http/moros.cc -> find --line href -> sort
|
||||
</code></pre>
|
||||
|
||||
<p>NOTE: The following file handles are available when a process is created:</p>
|
||||
|
||||
<ul>
|
||||
<li><code>stdin(0)</code></li>
|
||||
<li><code>stdout(1)</code></li>
|
||||
<li><code>stderr(2)</code></li>
|
||||
<li><code>stdnull(3)</code></li>
|
||||
</ul>
|
||||
|
||||
<p>A redirection with a single arrow head will truncate its destination while
|
||||
multiple heads like <code>=>></code> will append to it.</p>
|
||||
|
||||
<p>NOTE: Arrows can be longer, and also shorter in the case of fat arrows:</p>
|
||||
|
||||
<pre><code>> read foo.txt --> write bar.txt
|
||||
> read foo.txt -> write bar.txt
|
||||
</code></pre>
|
||||
|
||||
<!--
|
||||
> read foo.txt | write bar.txt
|
||||
-->
|
||||
|
||||
<pre><code>> read foo.txt ==> bar.txt
|
||||
> read foo.txt => bar.txt
|
||||
> read foo.txt > bar.txt
|
||||
|
||||
> write bar.txt <== foo.txt
|
||||
> write bar.txt <= foo.txt
|
||||
> write bar.txt < foo.txt
|
||||
|
||||
> read foo.txt ==>> bar.txt
|
||||
> read foo.txt =>> bar.txt
|
||||
> read foo.txt >> bar.txt
|
||||
</code></pre>
|
||||
|
||||
<h2>Variables</h2>
|
||||
|
||||
<ul>
|
||||
<li>Name of the shell or the script: <code>$0</code></li>
|
||||
<li>Script arguments: <code>$1</code>, <code>$2</code>, <code>$3</code>, <code>$4</code>, ...</li>
|
||||
<li>Exit code: <code>$?</code></li>
|
||||
<li>Process environment variable: <code>$HOME</code>, ...</li>
|
||||
<li>Shell environment variable: <code>$foo</code>, ...</li>
|
||||
</ul>
|
||||
|
||||
<p>Setting a variable in the shell environment is done with the following command:</p>
|
||||
|
||||
<pre><code>> set foo 42
|
||||
|
||||
> set bar "Alice and Bob"
|
||||
</code></pre>
|
||||
|
||||
<p>And accessing a variable is done with the <code>$</code> operator:</p>
|
||||
|
||||
<pre><code>> print $foo
|
||||
42
|
||||
|
||||
> print "Hello $bar"
|
||||
Hello Alice and Bob
|
||||
</code></pre>
|
||||
|
||||
<p>The process environment is copied to the shell environment when a session is
|
||||
started. By convention a process env var should be in uppercase and a shell
|
||||
env var should be lowercase.</p>
|
||||
|
||||
<p>Unsetting a variable is done like this:</p>
|
||||
|
||||
<pre><code>> unset foo
|
||||
</code></pre>
|
||||
|
||||
<h2>Globbing</h2>
|
||||
|
||||
<p>MOROS Shell support filename expansion or globbing for <code>*</code> and <code>?</code> wildcard
|
||||
characters, where a pattern given in an argument of a command will be replaced
|
||||
by files matching the pattern.</p>
|
||||
|
||||
<ul>
|
||||
<li><code>*</code> means zero or more chars except <code>/</code></li>
|
||||
<li><code>?</code> means any char except <code>/</code></li>
|
||||
</ul>
|
||||
|
||||
<p>For example <code>/tmp/*.txt</code> will match any files with the <code>txt</code> extension inside
|
||||
<code>/tmp</code>, and <code>a?c.txt</code> will match a file named <code>abc.txt</code>.</p>
|
||||
|
||||
<h2>Tilde Expansion</h2>
|
||||
|
||||
<p>The tilde character <code>~</code> is a shortcut to <code>$HOME</code> so <code>~/test</code> will be expanded
|
||||
to <code>$HOME/test</code> by the shell.</p>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1 @@
|
|||
../doc/shell.png
|
|
@ -0,0 +1,70 @@
|
|||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS Syscalls</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS Syscalls</h1>
|
||||
|
||||
<p>This list is unstable and subject to change between versions of MOROS.</p>
|
||||
|
||||
<h2>EXIT (0x1)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn exit(code: usize) -> usize
|
||||
</code></pre>
|
||||
|
||||
<h2>SPAWN (0x2)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn spawn(path: &str) -> isize
|
||||
</code></pre>
|
||||
|
||||
<h2>READ (0x3)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn read(handle: usize, buf: &mut [u8]) -> isize
|
||||
</code></pre>
|
||||
|
||||
<h2>WRITE (0x4)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn write(handle: usize, buf: &mut [u8]) -> isize
|
||||
</code></pre>
|
||||
|
||||
<h2>OPEN (0x5)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn open(path: &str, flags: usize) -> isize
|
||||
</code></pre>
|
||||
|
||||
<h2>CLOSE (0x6)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn close(handle: usize)
|
||||
</code></pre>
|
||||
|
||||
<h2>INFO (0x7)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn info(path: &str, info: &mut FileInfo) -> isize
|
||||
</code></pre>
|
||||
|
||||
<h2>DUP (0x8)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn dup(old_handle: usize, new_handle: usize) -> isize
|
||||
</code></pre>
|
||||
|
||||
<h2>DELETE (0x9)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn delete(path: &str) -> isize
|
||||
</code></pre>
|
||||
|
||||
<h2>STOP (0xA)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn stop(code: usize)
|
||||
</code></pre>
|
||||
|
||||
<p>The system will reboot with <code>0xcafe</code> and halt with <code>0xdead</code>.</p>
|
||||
|
||||
<h2>SLEEP (0xB)</h2>
|
||||
|
||||
<pre><code class="rust">pub fn sleep(seconds: f64)
|
||||
</code></pre>
|
||||
</body>
|
||||
</html>
|
|
@ -2,7 +2,8 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>MOROS: Obscure Rust Operating System</title>
|
||||
<title>MOROS</title>
|
||||
<link rel="stylesheet" type="text/css" href="/moros.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>MOROS</h1>
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
../doc/usage.png
|