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
This commit is contained in:
Vincent Ollivier 2023-04-27 19:34:05 +02:00 committed by GitHub
parent 4d50eae287
commit 2f7f33b950
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
74 changed files with 2287 additions and 207 deletions

78
Cargo.lock generated
View File

@ -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"

View File

@ -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

View File

@ -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

BIN
doc/2048.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
doc/boot.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
doc/dark.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 7.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

After

Width:  |  Height:  |  Size: 7.1 KiB

BIN
doc/hex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

View File

@ -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

BIN
doc/life.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

BIN
doc/light.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@ -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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 2.2 KiB

349
doc/manual.md Normal file
View File

@ -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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
doc/usage.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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)
}

View File

@ -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(());
}
}

View File

@ -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.

View File

@ -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) };

View File

@ -115,6 +115,7 @@ pub fn stop(code: usize) -> usize {
}
}
0xdead => { // Halt
sys::process::exit();
sys::acpi::shutdown();
}
_ => {

View File

@ -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);
}

View File

@ -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(())
}

View File

@ -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);
}

View File

@ -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(())
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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);
}

View File

@ -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);
}

1
www/2048.png Symbolic link
View File

@ -0,0 +1 @@
../doc/2048.png

1
www/boot.png Symbolic link
View File

@ -0,0 +1 @@
../doc/boot.png

28
www/build.sh Normal file
View File

@ -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

50
www/calculator.html Normal file
View File

@ -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>&gt; calc 2 + 2
4
</code></pre>
<p>And it will open a REPL if no arguments are provided:</p>
<pre><code>&gt; calc
MOROS Calc v0.1.0
&gt; 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>&gt; 2 + 3 * 4
14
&gt; (2 + 3) * 4
20
</code></pre>
</body>
</html>

1
www/chess.png Symbolic link
View File

@ -0,0 +1 @@
../doc/chess.png

1
www/dark.png Symbolic link
View File

@ -0,0 +1 @@
../doc/dark.png

1
www/edit.png Symbolic link
View File

@ -0,0 +1 @@
../doc/edit.png

27
www/editor.html Normal file
View File

@ -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>

205
www/filesystem.html Normal file
View File

@ -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 &#39;disk.img&#39;, 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>&gt; 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>&gt; write /bin/ # Binaries
&gt; write /dev/ # Devices
&gt; write /ini/ # Initialisation files
&gt; write /lib/ # Libraries
&gt; write /net/ # Network
&gt; write /src/ # Sources
&gt; write /tmp/ # Temporary files
&gt; write /usr/ # User directories
&gt; 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>&gt; 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 = &quot;MOROS FS&quot;
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>

1
www/find.png Symbolic link
View File

@ -0,0 +1 @@
../doc/find.png

1
www/hex.png Symbolic link
View File

@ -0,0 +1 @@
../doc/hex.png

View File

@ -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>

1
www/life.png Symbolic link
View File

@ -0,0 +1 @@
../doc/life.png

1
www/light.png Symbolic link
View File

@ -0,0 +1 @@
../doc/light.png

225
www/lisp.html Normal file
View File

@ -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>&#39;</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-&gt;number</code> (aliased to to <code>str-&gt;num</code>)</li>
<li><code>string-&gt;binary</code> and <code>binary-&gt;string</code> (aliased to <code>str-&gt;bin</code> and <code>bin-&gt;str</code>)</li>
<li><code>number-&gt;binary</code> and <code>binary-&gt;number</code> (aliased to <code>num-&gt;bin</code> and <code>bin-&gt;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>&gt;</code>, <code>&lt;</code>, <code>&gt;=</code>, <code>&lt;=</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>&gt; lisp
MOROS Lisp v0.4.0
&gt; (+ 1 2 3)
6
&gt; (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 &quot;/lib/lisp/core.lsp&quot;)
(def (fibonacci n)
(if (&lt; n 2) n
(+ (fibonacci (- n 1)) (fibonacci (- n 2)))))
(print
(if (nil? args) &quot;Usage: fibonacci &lt;num&gt;&quot;
(fibonacci (str-&gt;num (head args)))))
</code></pre>
<p>Would produce the following output:</p>
<pre><code>&gt; lisp /tmp/lisp/fibonacci.lsp 20
6755
</code></pre>
<h2>Examples</h2>
<pre><code class="lisp">(load &quot;/lib/lisp/core.lsp&quot;)
(print &quot;Hello, World!&quot;)
(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) # =&gt; 84
(def-mac (++ x) # Macro definition
`(set ,x (+ ,x 1)))
(var i 0)
(while (&lt; i 10)
(++ i))
(= i 10) # =&gt; 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 &#39;(1 2 3)) # Shortcut
(map double bar) # =&gt; (2 4 6)
(map (fun (x) (+ x 1)) &#39;(4 5 6)) # =&gt; (5 6 7)
(var name &quot;Alice&quot;)
(str &quot;Hello, &quot; name) # =&gt; &quot;Hello, Alice&quot;
(^ 2 64) # =&gt; 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&#39;s paper &quot;Recursive Functions of Symbolic Expressions and Their
Computation by Machine&quot; (1960) and &quot;The Roots of Lisp&quot; (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>

1
www/lisp.png Symbolic link
View File

@ -0,0 +1 @@
../doc/lisp.png

380
www/manual.html Normal file
View File

@ -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 &#39;/&#39;
Running console in diskless mode
/
&gt; 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 &#39;/&#39;
Populating filesystem...
Created &#39;/bin&#39;
Created &#39;/dev&#39;
Created &#39;/ini&#39;
Created &#39;/lib&#39;
Created &#39;/net&#39;
Created &#39;/src&#39;
Created &#39;/tmp&#39;
Created &#39;/usr&#39;
Created &#39;/var&#39;
Copied &#39;/bin/clear&#39;
Copied &#39;/bin/halt&#39;
Copied &#39;/bin/hello&#39;
Copied &#39;/bin/print&#39;
Copied &#39;/bin/reboot&#39;
Copied &#39;/bin/sleep&#39;
Created &#39;/dev/clk&#39;
Created &#39;/dev/clk/uptime&#39;
Created &#39;/dev/clk/realtime&#39;
Created &#39;/dev/rtc&#39;
Created &#39;/dev/null&#39;
Created &#39;/dev/random&#39;
Created &#39;/dev/console&#39;
Copied &#39;/ini/banner.txt&#39;
Copied &#39;/ini/boot.sh&#39;
Copied &#39;/ini/shell.sh&#39;
Copied &#39;/ini/version.txt&#39;
Created &#39;/ini/palettes&#39;
Copied &#39;/ini/palettes/gruvbox-dark.csv&#39;
Copied &#39;/ini/palettes/gruvbox-light.csv&#39;
Created &#39;/ini/fonts&#39;
Copied &#39;/ini/fonts/zap-light-8x16.psf&#39;
Copied &#39;/ini/fonts/zap-vga-8x16.psf&#39;
Created &#39;/lib/lisp&#39;
Copied &#39;/lib/lisp/core.lsp&#39;
Copied &#39;/lib/lisp/alias.lsp&#39;
Copied &#39;/tmp/alice.txt&#39;
Copied &#39;/tmp/machines.txt&#39;
Created &#39;/tmp/lisp&#39;
Copied &#39;/tmp/lisp/colors.lsp&#39;
Copied &#39;/tmp/lisp/factorial.lsp&#39;
Copied &#39;/tmp/lisp/fibonacci.lsp&#39;
Copied &#39;/tmp/lisp/geotime.lsp&#39;
Copied &#39;/tmp/lisp/pi.lsp&#39;
Copied &#39;/tmp/lisp/sum.lsp&#39;
Created &#39;/tmp/life&#39;
Copied &#39;/tmp/life/centinal.cells&#39;
Copied &#39;/tmp/life/flower-of-eden.cells&#39;
Copied &#39;/tmp/life/garden-of-eden.cells&#39;
Copied &#39;/tmp/life/glider-gun.cells&#39;
Copied &#39;/tmp/life/pentadecathlon.cells&#39;
Copied &#39;/tmp/life/queen-bee-shuttle.cells&#39;
Copied &#39;/tmp/life/ship-in-a-bottle.cells&#39;
Copied &#39;/tmp/life/thunderbird.cells&#39;
Copied &#39;/tmp/life/wing.cells&#39;
Created &#39;/tmp/beep&#39;
Copied &#39;/tmp/beep/tetris.sh&#39;
Copied &#39;/tmp/beep/starwars.sh&#39;
Copied &#39;/tmp/beep/mario.sh&#39;
Created &#39;/var/www&#39;
Copied &#39;/var/www/index.html&#39;
Copied &#39;/var/www/moros.png&#39;
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>~
&gt; print &quot;Hello, World!&quot;
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>~
&gt; print $DIR
/usr/vinc
</code></pre>
<p>You can change directory by typing it as if it was a command:</p>
<pre><code>~
&gt; /tmp
/tmp
&gt; print $DIR
/tmp
</code></pre>
<p>From now on we&#39;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>&gt; 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>&gt; list --help
Usage: list &lt;options&gt; [&lt;dir&gt;]
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>&gt; write test/
&gt; 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>=&gt;</code>
to the file:</p>
<pre><code>&gt; print &quot;Hello, World!&quot; =&gt; hello.txt
</code></pre>
<p>The command <code>read</code> will read the content of the file:</p>
<pre><code>&gt; 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>&gt; 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>&gt; 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>&gt; print &quot;2023-03-21 10:00:00&quot; =&gt; /dev/rtc
&gt; 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>&gt; calc &quot;2 * 60 * 60&quot;
7200
&gt; env TZ 7200
&gt; 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>&gt; 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&#39;s a device file to get the number of seconds elapsed since Unix Epoch:</p>
<pre><code>&gt; read /dev/clk/realtime
1682105344.624905
</code></pre>
<p>And another one since boot:</p>
<pre><code>&gt; 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>&gt; alias uptime &quot;read /dev/clk/uptime&quot;
&gt; 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>&gt; 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>&gt; 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>&gt; http moros.cc /test.html
&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;utf-8&quot;&gt;
&lt;title&gt;MOROS&lt;/title&gt;
&lt;link rel=&quot;stylesheet&quot; type=&quot;text/css&quot; href=&quot;/moros.css&quot;&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;MOROS&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
</body>
</html>

View File

@ -1 +0,0 @@
../target/x86_64-moros/release/bootimage-moros.bin

22
www/moros.css Normal file
View File

@ -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;
}

186
www/network.html Normal file
View File

@ -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>&gt; 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>&gt; net config dns
dns: 10.0.2.3
</code></pre>
<p>Set one attribute of the network configuration:</p>
<pre><code>&gt; net config dns 10.0.2.3
</code></pre>
<p>Display network statistics:</p>
<pre><code>&gt; 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>&gt; 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 &quot;8..........
------------------------------------------------------------------
</code></pre>
<h2>DHCP</h2>
<p>The <code>dhcp</code> command configures the network automatically:</p>
<pre><code>&gt; 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>&gt; 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>&gt; 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>&gt; 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>&gt; http moros.cc /test.html
</code></pre>
<p>Is equivalent to:</p>
<pre><code>&gt; read /net/http/moros.cc/test.html
</code></pre>
<p>And:</p>
<pre><code>&gt; 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>&gt; 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>&gt; socket moros.cc 80 --prompt
MOROS Socket v0.1.0
&gt; GET /test.html HTTP/1.0
&gt; Host: moros.cc
&gt;
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: &quot;617c3482-362&quot;
Accept-Ranges: bytes
&lt;!doctype html&gt;
&lt;html&gt;
&lt;head&gt;
&lt;meta charset=&quot;utf-8&quot;&gt;
&lt;title&gt;MOROS: Obscure Rust Operating System&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
&lt;h1&gt;MOROS&lt;/h1&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>Here&#39;s a connexion to a SMTP server to send a mail:</p>
<pre><code>&gt; socket 10.0.2.2 2500 --prompt
MOROS Socket v0.1.0
220 EventMachine SMTP Server
&gt; EHLO moros.cc
250-Ok EventMachine SMTP Server
250-NO-SOLICITING
250 SIZE 20000000
&gt; MAIL FROM:&lt;v@moros.cc&gt;
&gt; RCPT TO:&lt;alice@example.com&gt;
250 Ok
250 Ok
&gt; DATA
354 Send it
&gt; Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum nec
&gt; diam vitae ex blandit malesuada nec a turpis.
&gt; .
&gt; QUIT
250 Message accepted
221 Ok
</code></pre>
<p>Sending a file to a server:</p>
<pre><code>&gt; socket 10.0.2.2 1234 &lt;= /tmp/alice.txt
</code></pre>
</body>
</html>

1
www/network.png Symbolic link
View File

@ -0,0 +1 @@
../doc/network.png

33
www/regex.html Normal file
View File

@ -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&#39;s not present in the ASCII table and has a size
of two bytes.</p>
</body>
</html>

242
www/shell.html Normal file
View File

@ -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>&gt; alias d delete
</code></pre>
<!--
**Append** to file:
> a a.txt
> append a.txt
-->
<p><strong>Delete</strong> file:</p>
<pre><code>&gt; d a.txt
&gt; del a.txt
&gt; delete a.txt
</code></pre>
<p><strong>Copy</strong> file:</p>
<pre><code>&gt; c a.txt b.txt
&gt; copy a.txt b.txt
</code></pre>
<p><strong>Move</strong> file:</p>
<pre><code>&gt; m a.txt b.txt
&gt; move a.txt b.txt
</code></pre>
<p><strong>Print</strong> string:</p>
<pre><code>&gt; p &quot;Hi&quot;
&gt; print &quot;Hi&quot;
</code></pre>
<p><strong>Read</strong> file:</p>
<pre><code>&gt; r a.txt
&gt; read a.txt
</code></pre>
<p><strong>Write</strong> file:</p>
<pre><code>&gt; w a.txt
&gt; write a.txt
</code></pre>
<p><strong>Write</strong> dir:</p>
<pre><code>&gt; 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>&gt; 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>&gt; 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>&gt; read foo.txt and read bar.txt
</code></pre>
<p><strong>Or combiners:</strong></p>
<pre><code>&gt; read foo.txt or read bar.txt
</code></pre>
<h2>Pipes and redirections (WIP)</h2>
<p>A thin arrow <code>-&gt;</code> can be used for piping the output from one command to the
input of another command (TODO):</p>
<pre><code>&gt; read foo.txt -&gt; write bar.txt
</code></pre>
<p>A fat arrow <code>=&gt;</code> can be used for redirecting directly to a file:</p>
<pre><code>&gt; read foo.txt =&gt; 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>&gt; time read foo.txt =&gt; /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>&gt; time read foo.txt [1]=&gt;[3]
</code></pre>
<p>Or to redirect a file handle to a file:</p>
<pre><code>&gt; time read foo.txt [1]=&gt; bar.txt
</code></pre>
<p>Or to pipe a file handle to another command:</p>
<pre><code>&gt; time read foo.txt [1]-&gt; write bar.txt
</code></pre>
<p>It is possible to chain multiple redirections:</p>
<pre><code>&gt; time read foo.txt [1]=&gt; bar.txt [2]=&gt; 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>&gt; write &lt;= req.txt =&gt; /net/http/moros.cc
</code></pre>
<p>Redirections should be declared before piping (TODO):</p>
<pre><code>&gt; write &lt;= req.txt =&gt; /net/http/moros.cc -&gt; find --line href -&gt; 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>=&gt;&gt;</code> will append to it.</p>
<p>NOTE: Arrows can be longer, and also shorter in the case of fat arrows:</p>
<pre><code>&gt; read foo.txt --&gt; write bar.txt
&gt; read foo.txt -&gt; write bar.txt
</code></pre>
<!--
> read foo.txt | write bar.txt
-->
<pre><code>&gt; read foo.txt ==&gt; bar.txt
&gt; read foo.txt =&gt; bar.txt
&gt; read foo.txt &gt; bar.txt
&gt; write bar.txt &lt;== foo.txt
&gt; write bar.txt &lt;= foo.txt
&gt; write bar.txt &lt; foo.txt
&gt; read foo.txt ==&gt;&gt; bar.txt
&gt; read foo.txt =&gt;&gt; bar.txt
&gt; read foo.txt &gt;&gt; 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>&gt; set foo 42
&gt; set bar &quot;Alice and Bob&quot;
</code></pre>
<p>And accessing a variable is done with the <code>$</code> operator:</p>
<pre><code>&gt; print $foo
42
&gt; print &quot;Hello $bar&quot;
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>&gt; 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>

1
www/shell.png Symbolic link
View File

@ -0,0 +1 @@
../doc/shell.png

70
www/syscalls.html Normal file
View File

@ -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) -&gt; usize
</code></pre>
<h2>SPAWN (0x2)</h2>
<pre><code class="rust">pub fn spawn(path: &amp;str) -&gt; isize
</code></pre>
<h2>READ (0x3)</h2>
<pre><code class="rust">pub fn read(handle: usize, buf: &amp;mut [u8]) -&gt; isize
</code></pre>
<h2>WRITE (0x4)</h2>
<pre><code class="rust">pub fn write(handle: usize, buf: &amp;mut [u8]) -&gt; isize
</code></pre>
<h2>OPEN (0x5)</h2>
<pre><code class="rust">pub fn open(path: &amp;str, flags: usize) -&gt; 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: &amp;str, info: &amp;mut FileInfo) -&gt; isize
</code></pre>
<h2>DUP (0x8)</h2>
<pre><code class="rust">pub fn dup(old_handle: usize, new_handle: usize) -&gt; isize
</code></pre>
<h2>DELETE (0x9)</h2>
<pre><code class="rust">pub fn delete(path: &amp;str) -&gt; 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>

View File

@ -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>

1
www/usage.png Symbolic link
View File

@ -0,0 +1 @@
../doc/usage.png