Refactor everything to stay below 80 chars per line (#567)

* Refactor everything to stay below 80 chars per line

* Refactor hash command

* Refactor code changed by cargo fmt

* Refactor code

* Realign syscall numbers

* Update readme
This commit is contained in:
Vincent Ollivier 2024-01-17 20:27:06 +01:00 committed by GitHub
parent a9eb6f9c21
commit aada07a3ef
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
107 changed files with 3811 additions and 1969 deletions

View File

@ -2,7 +2,7 @@
![screenshot](doc/images/moros.png)
MOROS is a hobby operating system written in Rust by [Vincent Ollivier](https://vinc.cc).
MOROS is a hobby operating system written in Rust by [Vincent Ollivier][0].
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).
@ -11,28 +11,27 @@ 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.
[![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)
[![GitHub Actions][s1]](https://github.com/vinc/moros)
[![Crates.io][s2]](https://crates.io/crates/moros)
## Features
- External bootloader (using [bootloader](https://github.com/rust-osdev/bootloader))
- x86 CPU support (using [x86_64](https://crates.io/crates/x86_64))
- Hardware interrupts (using [pic8259](https://crates.io/crates/pic8259))
- PS/2 Keyboard with customizable layout (using [pc-keyboard](https://crates.io/crates/pc-keyboard))
- External bootloader (using [bootloader][4])
- x86 CPU support (using [x86_64][5])
- Hardware interrupts (using [pic8259][6])
- PS/2 Keyboard with customizable layout (using [pc-keyboard][7])
- VGA Text mode with customizable font and color palette
- Serial output (using [uart_16550](https://crates.io/crates/uart_16550))
- Serial output (using [uart_16550][8])
- Paging
- Heap allocation (using [linked_list_allocator](https://crates.io/crates/linked_list_allocator))
- ACPI shutdown (using [acpi](https://crates.io/crates/acpi) and [aml](https://crates.io/crates/aml))
- Heap allocation (using [linked_list_allocator][9])
- ACPI shutdown (using [acpi][10]) and [aml][11])
- RTC clock
- PCI devices
- ATA PIO mode
- Random number generator (using [rand_hc](https://crates.io/crates/rand_hc))
- Random number generator (using [rand_hc][12])
- RTL8139 network card
- AMD PCNET network card
- DHCP/IP/TCP/UDP/DNS/HTTP network protocols (using [smoltcp](https://crates.io/crates/smoltcp))
- DHCP/IP/TCP/UDP/DNS/HTTP network protocols (using [smoltcp][13])
- Basic [filesystem](doc/filesystem.md)
- Basic [shell](doc/shell.md)
- Basic [text editor](doc/editor.md)
@ -41,16 +40,14 @@ This project started from the [seventh post][1] of the second edition of
- Some file and [network](doc/network.md) commands
- Some [games](doc/games.md)
## Documentation
Documentation is available [here](doc/index.md)
## Setup
You will need `git`, `gcc`, `make`, `curl`, `qemu-img`, and
`qemu-system-x86_64` on the host system.
You will need `git`, `gcc`, `make`, `curl`, `qemu-img`,
and `qemu-system-x86_64` on the host system.
Clone the repo:
@ -63,7 +60,6 @@ Install the required tools with `make setup` or the following commands:
$ rustup show
$ cargo install bootimage
## Usage
Build the image to `disk.img`:
@ -78,7 +74,7 @@ Run natively on a x86 computer by copying the bootloader and the kernel to a
hard drive or USB stick (but there is currently no USB driver so the filesystem
will not be available in that case):
$ sudo dd if=target/x86_64-moros/release/bootimage-moros.bin of=/dev/sdx && sync
$ sudo dd if=target/x86_64-moros/release/bootimage-moros.bin of=/dev/sdx
MOROS will open a console in diskless mode after boot if no filesystem is
detected. The following command will setup the filesystem on a hard drive,
@ -90,18 +86,30 @@ allowing you to exit the diskless mode and log in as a normal user:
your OS, and `install` or `disk format` inside MOROS if you don't use an
emulator.**
## Tests
Run the test suite in QEMU:
$ make test
## License
MOROS is released under MIT.
[0]: https://vinc.cc
[1]: https://github.com/phil-opp/blog_os/tree/post-07
[2]: https://os.phil-opp.com
[3]: https://wiki.osdev.org
[4]: https://github.com/rust-osdev/bootloader
[5]: https://crates.io/crates/x86_64
[6]: https://crates.io/crates/pic8259
[7]: https://crates.io/crates/pc-keyboard
[8]: https://crates.io/crates/uart_16550
[9]: https://crates.io/crates/linked_list_allocator
[10]: https://crates.io/crates/acpi
[11]: https://crates.io/crates/aml
[12]: https://crates.io/crates/rand_hc
[13]: https://crates.io/crates/smoltcp
[s1]: https://img.shields.io/github/actions/workflow/status/vinc/moros/rust.yml
[s2]: https://img.shields.io/crates/v/moros.svg

View File

@ -4,7 +4,7 @@ use core::alloc::{GlobalAlloc, Layout};
pub struct UserspaceAllocator;
unsafe impl GlobalAlloc for UserspaceAllocator{
unsafe impl GlobalAlloc for UserspaceAllocator {
unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
syscall::alloc(layout.size(), layout.align())
}

View File

@ -1,7 +1,7 @@
use crate::sys;
use core::fmt;
pub use crate::sys::console::{ETX_KEY, EOT_KEY};
pub use crate::sys::console::{EOT_KEY, ETX_KEY};
#[derive(Clone, Copy)]
pub struct Style {
@ -11,23 +11,38 @@ pub struct Style {
impl Style {
pub fn reset() -> Self {
Self { foreground: None, background: None }
Self {
foreground: None,
background: None,
}
}
pub fn foreground(name: &str) -> Self {
Self { foreground: color_to_fg(name), background: None }
Self {
foreground: color_to_fg(name),
background: None,
}
}
pub fn with_foreground(self, name: &str) -> Self {
Self { foreground: color_to_fg(name), background: self.background }
Self {
foreground: color_to_fg(name),
background: self.background,
}
}
pub fn background(name: &str) -> Self {
Self { foreground: None, background: color_to_bg(name) }
Self {
foreground: None,
background: color_to_bg(name),
}
}
pub fn with_background(self, name: &str) -> Self {
Self { foreground: self.foreground, background: color_to_bg(name) }
Self {
foreground: self.foreground,
background: color_to_bg(name),
}
}
pub fn color(name: &str) -> Self {

View File

@ -37,7 +37,9 @@ pub fn from_bytes(buf: &[u8]) -> Result<Font, ()> {
fn parse_psf_font() {
assert!(from_bytes(include_bytes!("../../dsk/ini/boot.sh")).is_err());
let font = from_bytes(include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf")).unwrap();
let font = from_bytes(
include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf")
).unwrap();
assert_eq!(font.height, 16);
assert_eq!(font.size, 256);
assert_eq!(font.data.len(), 256 * 16);

View File

@ -1,16 +1,19 @@
use crate::api::syscall;
use crate::sys;
use crate::sys::fs::OpenFlag;
use crate::api::syscall;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use alloc::vec;
use alloc::vec::Vec;
pub use crate::sys::fs::{FileInfo, DeviceType};
pub use crate::sys::fs::{DeviceType, FileInfo};
#[derive(Clone, Copy)]
pub enum IO { Read, Write }
pub enum IO {
Read,
Write,
}
pub trait FileIO {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()>;
@ -106,7 +109,11 @@ pub fn create_device(path: &str, kind: DeviceType) -> Option<usize> {
pub fn read(path: &str, buf: &mut [u8]) -> Result<usize, ()> {
if let Some(info) = syscall::info(path) {
let res = if info.is_device() { open_device(path) } else { open_file(path) };
let res = if info.is_device() {
open_device(path)
} else {
open_file(path)
};
if let Some(handle) = res {
if let Some(bytes) = syscall::read(handle, buf) {
syscall::close(handle);
@ -173,11 +180,11 @@ pub fn append(path: &str, buf: &[u8]) -> Result<usize, ()> {
Err(())
}
pub fn reopen(path: &str, handle: usize, append_mode: bool) -> Result<usize, ()> {
pub fn reopen(path: &str, handle: usize, append: bool) -> Result<usize, ()> {
let res = if let Some(info) = syscall::info(path) {
if info.is_device() {
open_device(path)
} else if append_mode {
} else if append {
append_file(path)
} else {
open_file(path)
@ -218,7 +225,7 @@ pub fn read_dir(path: &str) -> Result<Vec<FileInfo>, ()> {
#[test_case]
fn test_file() {
use crate::sys::fs::{mount_mem, format_mem, dismount};
use crate::sys::fs::{dismount, format_mem, mount_mem};
mount_mem();
format_mem();

View File

@ -1,7 +1,7 @@
use crate::api::syscall;
use alloc::vec;
use alloc::string::{String, ToString};
use alloc::vec;
pub struct Stdin;
pub struct Stdout;
@ -17,7 +17,8 @@ impl Stdin {
if let Some(bytes) = syscall::read(0, &mut buf) {
if bytes > 0 {
buf.resize(bytes, 0);
return Some(String::from_utf8_lossy(&buf).to_string().remove(0));
let s = String::from_utf8_lossy(&buf).to_string().remove(0);
return Some(s);
}
}
None

View File

@ -5,16 +5,18 @@ macro_rules! entry_point {
#[panic_handler]
fn panic(_info: &core::panic::PanicInfo) -> ! {
$crate::api::syscall::write(1, b"An exception occured!\n");
$crate::api::syscall::exit($crate::api::process::ExitCode::ExecError);
let code = $crate::api::process::ExitCode::ExecError;
$crate::api::syscall::exit(code);
loop {}
}
#[export_name = "_start"]
pub unsafe extern "sysv64" fn __impl_start(args_ptr: u64, args_len: usize) {
let args = core::slice::from_raw_parts(args_ptr as *const _, args_len);
pub unsafe extern "sysv64" fn __impl_start(ptr: u64, len: usize) {
let args = core::slice::from_raw_parts(ptr as *const _, len);
let f: fn(&[&str]) = $path;
f(args);
$crate::api::syscall::exit($crate::api::process::ExitCode::Success);
let code = $crate::api::process::ExitCode::Success;
$crate::api::syscall::exit(code);
}
};
}
@ -62,7 +64,9 @@ macro_rules! error {
($($arg:tt)*) => ({
let csi_color = $crate::api::console::Style::color("LightRed");
let csi_reset = $crate::api::console::Style::reset();
eprintln!("{}Error:{} {}", csi_color, csi_reset, format_args!($($arg)*));
eprintln!(
"{}Error:{} {}", csi_color, csi_reset, format_args!($($arg)*)
);
});
}
@ -71,7 +75,9 @@ macro_rules! warning {
($($arg:tt)*) => ({
let csi_color = $crate::api::console::Style::color("Yellow");
let csi_reset = $crate::api::console::Style::reset();
eprintln!("{}Warning:{} {}", csi_color, csi_reset, format_args!($($arg)*));
eprintln!(
"{}Warning:{} {}", csi_color, csi_reset, format_args!($($arg)*)
);
});
}

View File

@ -5,10 +5,10 @@ use core::convert::From;
#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(u8)]
pub enum ExitCode {
Success = 0,
Failure = 1,
UsageError = 64,
DataError = 65,
Success = 0,
Failure = 1,
UsageError = 64,
DataError = 65,
OpenError = 128,
ReadError = 129,
ExecError = 130,
@ -19,15 +19,15 @@ pub enum ExitCode {
impl From<usize> for ExitCode {
fn from(code: usize) -> Self {
match code {
0 => ExitCode::Success,
64 => ExitCode::UsageError,
65 => ExitCode::DataError,
0 => ExitCode::Success,
64 => ExitCode::UsageError,
65 => ExitCode::DataError,
128 => ExitCode::OpenError,
129 => ExitCode::ReadError,
130 => ExitCode::ExecError,
200 => ExitCode::PageFaultError,
255 => ExitCode::ShellExit,
_ => ExitCode::Failure,
_ => ExitCode::Failure,
}
}
}

View File

@ -2,7 +2,6 @@ use crate::api::{console, fs, io};
use alloc::boxed::Box;
use alloc::string::{String, ToString};
use alloc::vec::Vec;
use core::cmp;
use vte::{Params, Parser, Perform};
pub struct Prompt {
@ -36,20 +35,20 @@ impl Prompt {
self.update_completion();
println!();
return Some(String::new());
},
}
console::EOT_KEY => { // End of Transmission (^D)
self.update_completion();
println!();
return None;
},
}
'\n' => { // New Line
self.update_completion();
self.update_history();
println!();
return Some(self.line.iter().collect());
},
}
c => {
for b in c.to_string().as_bytes() {
for b in c.to_string().as_bytes() {
parser.advance(self, *b);
}
}
@ -91,16 +90,16 @@ impl Prompt {
} else {
(bs, 0)
}
},
}
None => {
let line: String = self.line.iter().collect();
self.completion.entries = (self.completion.completer)(&line);
if !self.completion.entries.is_empty() {
(0, 0)
} else {
return
return;
}
},
}
};
let erase = "\x08".repeat(bs);
let complete = &self.completion.entries[pos];
@ -123,16 +122,16 @@ impl Prompt {
} else {
(bs, pos - 1)
}
},
}
None => {
let line: String = self.line.iter().collect();
self.completion.entries = (self.completion.completer)(&line);
if !self.completion.entries.is_empty() {
(0, 0)
} else {
return
return;
}
},
}
};
let erase = "\x08".repeat(bs);
let complete = &self.completion.entries[pos];
@ -147,8 +146,12 @@ impl Prompt {
return;
}
let (bs, i) = match self.history.pos {
Some(i) => (self.history.entries[i].chars().count(), cmp::max(i, 1) - 1),
None => (self.line.len(), n - 1),
Some(i) => {
(self.history.entries[i].chars().count(), i.saturating_sub(1))
}
None => {
(self.line.len(), n - 1)
}
};
let line = &self.history.entries[i];
let blank = ' '.to_string().repeat((self.offset + bs) - self.cursor);
@ -245,7 +248,7 @@ impl Perform for Prompt {
match c {
'\x08' => self.handle_backspace_key(),
'\t' => self.handle_tab_key(),
_ => {},
_ => {}
}
}
@ -256,7 +259,7 @@ impl Perform for Prompt {
}
}
fn csi_dispatch(&mut self, params: &Params, _intermediates: &[u8], _ignore: bool, c: char) {
fn csi_dispatch(&mut self, params: &Params, _: &[u8], _: bool, c: char) {
match c {
'A' => self.handle_up_key(),
'B' => self.handle_down_key(),
@ -269,8 +272,8 @@ impl Perform for Prompt {
self.handle_delete_key();
}
}
},
_ => {},
}
_ => {}
}
}
}

View File

@ -1,5 +1,5 @@
use crate::api::syscall;
use crate::api::fs;
use crate::api::syscall;
pub fn get_u64() -> u64 {
let mut buf = [0; 8];

View File

@ -21,7 +21,7 @@ impl From<char> for MetaChar {
fn from(c: char) -> Self {
match c {
'.' => MetaChar::Any,
_ => MetaChar::Literal(c),
_ => MetaChar::Literal(c),
}
}
}
@ -40,7 +40,7 @@ impl MetaCharExt for MetaChar {
'D' => MetaChar::NonNumeric,
'S' => MetaChar::NonWhitespace,
'W' => MetaChar::NonAlphanumeric,
_ => MetaChar::Literal(c),
_ => MetaChar::Literal(c),
}
}
fn contains(&self, c: char) -> bool {
@ -82,7 +82,12 @@ impl Regex {
}
}
fn is_match(re: &[char], text: &[char], start: &mut usize, end: &mut usize) -> bool {
fn is_match(
re: &[char],
text: &[char],
start: &mut usize,
end: &mut usize,
) -> bool {
if re.is_empty() {
return true;
}
@ -105,7 +110,11 @@ fn is_match(re: &[char], text: &[char], start: &mut usize, end: &mut usize) -> b
}
}
fn is_match_here(re: &[char], text: &[char], end: &mut usize) -> bool {
fn is_match_here(
re: &[char],
text: &[char],
end: &mut usize,
) -> bool {
if re.is_empty() {
return true;
}
@ -136,19 +145,44 @@ fn is_match_here(re: &[char], text: &[char], end: &mut usize) -> bool {
false
}
fn is_match_star(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
fn is_match_star(
lazy: bool,
mc: MetaChar,
re: &[char],
text: &[char],
end: &mut usize,
) -> bool {
is_match_char(lazy, mc, re, text, .., end)
}
fn is_match_plus(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
fn is_match_plus(
lazy: bool,
mc: MetaChar,
re: &[char],
text: &[char],
end: &mut usize,
) -> bool {
is_match_char(lazy, mc, re, text, 1.., end)
}
fn is_match_ques(lazy: bool, mc: MetaChar, re: &[char], text: &[char], end: &mut usize) -> bool {
fn is_match_ques(
lazy: bool,
mc: MetaChar,
re: &[char],
text: &[char],
end: &mut usize,
) -> bool {
is_match_char(lazy, mc, re, text, ..2, end)
}
fn is_match_char<T: RangeBounds<usize>>(lazy: bool, mc: MetaChar, re: &[char], text: &[char], range: T, end: &mut usize) -> bool {
fn is_match_char<T: RangeBounds<usize>>(
lazy: bool,
mc: MetaChar,
re: &[char],
text: &[char],
range: T,
end: &mut usize,
) -> bool {
let mut i = 0;
let n = text.len();
@ -183,82 +217,81 @@ fn is_match_char<T: RangeBounds<usize>>(lazy: bool, mc: MetaChar, re: &[char], t
#[test_case]
fn test_regex() {
let tests = [
("", "aaa", true),
("", "", true),
("aaa", "aaa", true),
("aaa", "bbb", false),
("a.a", "aaa", true),
("a.a", "aba", true),
("a.a", "abb", false),
("a*", "aaa", true),
("a*b", "aab", true),
("a*b*", "aabb", true),
("a*b*", "bb", true),
("a.*", "abb", true),
(".*", "aaa", true),
("a.*", "a", true),
("a.+", "ab", true),
("a.+", "abb", true),
("a.+", "a", false),
("a.+b", "ab", false),
("a.+b", "abb", true),
(".+", "abb", true),
(".+", "b", true),
("a?b", "abb", true),
("a?b", "bb", true),
("a?b", "aabb", true),
("^a.*a$", "aaa", true),
("^#.*", "#aaa", true),
("^#.*", "a#aaa", false),
(".*;$", "aaa;", true),
(".*;$", "aaa;a", false),
("^.*$", "aaa", true),
("a.b", "abb", true),
("a.b", "a.b", true),
("a\\.b", "abb", false),
("a\\.b", "a.b", true),
("a\\\\.b", "abb", false),
("a\\\\.b", "a.b", false),
("a\\\\.b", "a\\bb", true),
("a\\\\.b", "a\\.b", true),
("a\\\\\\.b", "a\\bb", false),
("a\\\\\\.b", "a\\.b", true),
("a\\\\\\.b", "a\\\\bb", false),
("a\\\\\\.b", "a\\\\.b", false),
("a\\\\\\\\.b", "a\\bb", false),
("a\\\\\\\\.b", "a\\.b", false),
("", "aaa", true),
("", "", true),
("aaa", "aaa", true),
("aaa", "bbb", false),
("a.a", "aaa", true),
("a.a", "aba", true),
("a.a", "abb", false),
("a*", "aaa", true),
("a*b", "aab", true),
("a*b*", "aabb", true),
("a*b*", "bb", true),
("a.*", "abb", true),
(".*", "aaa", true),
("a.*", "a", true),
("a.+", "ab", true),
("a.+", "abb", true),
("a.+", "a", false),
("a.+b", "ab", false),
("a.+b", "abb", true),
(".+", "abb", true),
(".+", "b", true),
("a?b", "abb", true),
("a?b", "bb", true),
("a?b", "aabb", true),
("^a.*a$", "aaa", true),
("^#.*", "#aaa", true),
("^#.*", "a#aaa", false),
(".*;$", "aaa;", true),
(".*;$", "aaa;a", false),
("^.*$", "aaa", true),
("a.b", "abb", true),
("a.b", "a.b", true),
("a\\.b", "abb", false),
("a\\.b", "a.b", true),
("a\\\\.b", "abb", false),
("a\\\\.b", "a.b", false),
("a\\\\.b", "a\\bb", true),
("a\\\\.b", "a\\.b", true),
("a\\\\\\.b", "a\\bb", false),
("a\\\\\\.b", "a\\.b", true),
("a\\\\\\.b", "a\\\\bb", false),
("a\\\\\\.b", "a\\\\.b", false),
("a\\\\\\\\.b", "a\\bb", false),
("a\\\\\\\\.b", "a\\.b", false),
("a\\\\\\\\.b", "a\\\\bb", true),
("a\\\\\\\\.b", "a\\\\.b", true),
("a\\wb", "aéb", true),
("a\\wb", "awb", true),
("a\\wb", "abb", true),
("a\\wb", "a1b", true),
("a\\wb", "a.b", false),
("a\\Wb", "aWb", false),
("a\\Wb", "abb", false),
("a\\Wb", "a1b", false),
("a\\Wb", "a.b", true),
("a\\db", "abb", false),
("a\\db", "a1b", true),
("a\\Db", "abb", true),
("a\\Db", "a1b", false),
("a\\sb", "abb", false),
("a\\sb", "a b", true),
("a\\Sb", "abb", true),
("a\\Sb", "a b", false),
("a\\.*d", "a..d", true),
("a\\.*d", "a.cd", false),
("a\\w*d", "abcd", true),
("a\\wb", "aéb", true),
("a\\wb", "awb", true),
("a\\wb", "abb", true),
("a\\wb", "a1b", true),
("a\\wb", "a.b", false),
("a\\Wb", "aWb", false),
("a\\Wb", "abb", false),
("a\\Wb", "a1b", false),
("a\\Wb", "a.b", true),
("a\\db", "abb", false),
("a\\db", "a1b", true),
("a\\Db", "abb", true),
("a\\Db", "a1b", false),
("a\\sb", "abb", false),
("a\\sb", "a b", true),
("a\\Sb", "abb", true),
("a\\Sb", "a b", false),
("a\\.*d", "a..d", true),
("a\\.*d", "a.cd", false),
("a\\w*d", "abcd", true),
];
for (re, text, is_match) in tests {
assert!(Regex::new(re).is_match(text) == is_match, "Regex::new(\"{}\").is_match(\"{}\") == {}", re, text, is_match);
assert!(
Regex::new(re).is_match(text) == is_match,
"Regex::new(\"{}\").is_match(\"{}\") == {}",
re,
text,
is_match
);
}
assert_eq!(Regex::new(".*").find("abcd"), Some((0, 4)));

View File

@ -1,8 +1,8 @@
use crate::api::fs::IO;
use crate::api::process::ExitCode;
use crate::syscall;
use crate::sys::syscall::number::*;
use crate::sys::fs::FileInfo;
use crate::sys::syscall::number::*;
use crate::syscall;
use smoltcp::wire::IpAddress;
use smoltcp::wire::Ipv4Address;
@ -90,7 +90,9 @@ pub fn spawn(path: &str, args: &[&str]) -> Result<(), ExitCode> {
let args_ptr = args.as_ptr() as usize;
let path_len = path.len();
let args_len = args.len();
let res = unsafe { syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len) };
let res = unsafe {
syscall!(SPAWN, path_ptr, path_len, args_ptr, args_len)
};
if res == 0 {
Ok(())
} else {
@ -156,9 +158,7 @@ pub fn accept(handle: usize) -> Result<IpAddress, ()> {
}
pub fn alloc(size: usize, align: usize) -> *mut u8 {
unsafe {
syscall!(ALLOC, size, align) as *mut u8
}
unsafe { syscall!(ALLOC, size, align) as *mut u8 }
}
pub fn free(ptr: *mut u8, size: usize, align: usize) {
@ -169,7 +169,7 @@ pub fn free(ptr: *mut u8, size: usize, align: usize) {
#[test_case]
fn test_file() {
use crate::sys::fs::{mount_mem, format_mem, dismount, OpenFlag};
use crate::sys::fs::{dismount, format_mem, mount_mem, OpenFlag};
use alloc::vec;
mount_mem();
format_mem();

View File

@ -1,7 +1,7 @@
use crate::sys;
use crate::api::clock;
use crate::sys;
use time::{OffsetDateTime, Duration, UtcOffset};
use time::{Duration, OffsetDateTime, UtcOffset};
pub fn now() -> OffsetDateTime {
now_utc().to_offset(offset())
@ -9,7 +9,9 @@ pub fn now() -> OffsetDateTime {
pub fn now_utc() -> OffsetDateTime {
let s = clock::realtime(); // Since Unix Epoch
let ns = Duration::nanoseconds(libm::floor(1e9 * (s - libm::floor(s))) as i64);
let ns = Duration::nanoseconds(
libm::floor(1e9 * (s - libm::floor(s))) as i64
);
OffsetDateTime::from_unix_timestamp(s as i64) + ns
}

View File

@ -3,22 +3,22 @@
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u8)]
pub enum Color {
Black = 0,
Blue = 1,
Green = 2,
Cyan = 3,
Red = 4,
Magenta = 5,
Brown = 6,
LightGray = 7,
DarkGray = 8,
LightBlue = 9,
LightGreen = 10,
LightCyan = 11,
LightRed = 12,
Pink = 13,
Yellow = 14,
White = 15,
Black = 0x0,
Blue = 0x1,
Green = 0x2,
Cyan = 0x3,
Red = 0x4,
Magenta = 0x5,
Brown = 0x6,
LightGray = 0x7,
DarkGray = 0x8,
LightBlue = 0x9,
LightGreen = 0xA,
LightCyan = 0xB,
LightRed = 0xC,
Pink = 0xD,
Yellow = 0xE,
White = 0xF,
}
const COLORS: [Color; 16] = [
@ -66,7 +66,7 @@ pub fn from_ansi(code: u8) -> Color {
95 => Color::Pink,
96 => Color::LightCyan,
97 => Color::White,
_ => Color::Black, // Error
_ => Color::Black, // TODO: Error
}
}

View File

@ -3,7 +3,7 @@ use core::convert::TryInto;
// TODO: Move this to kernel after removing the `vga set palette` command
pub struct Palette {
pub colors: [(u8, u8, u8); 16]
pub colors: [(u8, u8, u8); 16],
}
impl Palette {
@ -26,7 +26,7 @@ impl Palette {
(0xFF, 0x00, 0xFF), // Pink (Light Magenta)
(0xFF, 0xFF, 0x00), // Yellow (Light Yellow)
(0xFF, 0xFF, 0xFF), // White
]
],
}
}
}

View File

@ -7,5 +7,5 @@ use moros::entry_point;
entry_point!(main);
fn main(_args: &[&str]) {
syscall::write(1, b"\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
syscall::write(1, b"\x1b[2J\x1b[1;1H"); // Clear screen and move to top
}

View File

@ -6,8 +6,8 @@ extern crate alloc;
use alloc::string::String;
use alloc::vec::Vec;
use moros::api::io;
use moros::api::syscall;
use moros::api::process;
use moros::api::syscall;
use moros::entry_point;
entry_point!(main);

View File

@ -8,10 +8,13 @@ entry_point!(main);
fn main(_args: &[&str]) {
syscall::write(1, b"\x1b[93m"); // Yellow
syscall::write(1, b"MOROS has reached its fate, the system is now halting.");
syscall::write(1, b"MOROS has reached its fate, ");
syscall::write(1, b"the system is now halting.");
syscall::write(1, b"\x1b[0m"); // Reset
syscall::write(1, b"\n");
syscall::sleep(0.5);
syscall::halt();
loop { syscall::sleep(1.0) }
loop {
syscall::sleep(1.0)
}
}

View File

@ -11,17 +11,17 @@ entry_point!(main);
fn main(args: &[&str]) {
if args.len() > 1 {
syscall::write(1, args[1].as_bytes()); // FIXME: this is needed
syscall::write(1, args[1].as_bytes()); // FIXME: this is needed
syscall::write(1, "\n".as_bytes());
let mut hello = "Hello, ".to_string();
hello.push_str(args[1]); // FIXME: for that to work
hello.push_str(args[1]); // FIXME: for that to work
hello.push_str("!\n");
syscall::write(1, hello.as_bytes());
if args.len() > 2 {
let mut hello = "Hello, ".to_string();
hello.push_str(args[2]); // FIXME: not working
hello.push_str(args[2]); // FIXME: not working
hello.push_str("!\n");
syscall::write(1, hello.as_bytes());
}

View File

@ -1,8 +1,8 @@
#![no_std]
#![no_main]
use moros::entry_point;
use moros::api::syscall;
use moros::entry_point;
entry_point!(main);

View File

@ -8,9 +8,14 @@ entry_point!(main);
fn main(_args: &[&str]) {
syscall::write(1, b"\x1b[93m"); // Yellow
syscall::write(1, b"MOROS has reached its fate, the system is now rebooting.\n");
syscall::write(
1,
b"MOROS has reached its fate, the system is now rebooting.\n",
);
syscall::write(1, b"\x1b[0m"); // Reset
syscall::sleep(0.5);
syscall::reboot();
loop { syscall::sleep(1.0) }
loop {
syscall::sleep(1.0)
}
}

View File

@ -1,8 +1,8 @@
#![no_std]
#![no_main]
use moros::api::syscall;
use moros::api::process::ExitCode;
use moros::api::syscall;
use moros::entry_point;
entry_point!(main);

View File

@ -30,7 +30,9 @@ pub fn init(boot_info: &'static BootInfo) {
sys::keyboard::init();
sys::time::init();
log!("MOROS v{}\n", option_env!("MOROS_VERSION").unwrap_or(env!("CARGO_PKG_VERSION")));
let v = option_env!("MOROS_VERSION").unwrap_or(env!("CARGO_PKG_VERSION"));
log!("MOROS v{}\n", v);
sys::mem::init(boot_info);
sys::cpu::init();
sys::pci::init(); // Require MEM
@ -44,7 +46,12 @@ pub fn init(boot_info: &'static BootInfo) {
fn alloc_error_handler(layout: alloc::alloc::Layout) -> ! {
let csi_color = api::console::Style::color("LightRed");
let csi_reset = api::console::Style::reset();
printk!("{}Error:{} Could not allocate {} bytes\n", csi_color, csi_reset, layout.size());
printk!(
"{}Error:{} Could not allocate {} bytes\n",
csi_color,
csi_reset,
layout.size()
);
hlt_loop();
}
@ -71,7 +78,6 @@ pub fn test_runner(tests: &[&dyn Testable]) {
exit_qemu(QemuExitCode::Success);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[repr(u32)]
pub enum QemuExitCode {

View File

@ -5,7 +5,7 @@ extern crate alloc;
use bootloader::{entry_point, BootInfo};
use core::panic::PanicInfo;
use moros::{sys, usr, print, println, debug, hlt_loop};
use moros::{debug, hlt_loop, print, println, sys, usr};
entry_point!(main);

View File

@ -1,15 +1,15 @@
use crate::sys;
use acpi::{AcpiHandler, PhysicalMapping, AcpiTables};
use acpi::{AcpiHandler, AcpiTables, PhysicalMapping};
use alloc::boxed::Box;
use aml::value::AmlValue;
use aml::{AmlContext, AmlName, DebugVerbosity, Handler};
use core::ptr::NonNull;
use x86_64::PhysAddr;
use x86_64::instructions::port::Port;
use x86_64::PhysAddr;
fn read_addr<T>(physical_address: usize) -> T where T: Copy {
let virtual_address = sys::mem::phys_to_virt(PhysAddr::new(physical_address as u64));
fn read_addr<T>(addr: usize) -> T where T: Copy {
let virtual_address = sys::mem::phys_to_virt(PhysAddr::new(addr as u64));
unsafe { *virtual_address.as_ptr::<T>() }
}
@ -19,27 +19,29 @@ pub fn shutdown() {
let slp_len = 1 << 13;
log!("ACPI Shutdown\n");
let mut aml = AmlContext::new(Box::new(MorosAmlHandler), DebugVerbosity::None);
let handler = Box::new(MorosAmlHandler);
let mut aml = AmlContext::new(handler, DebugVerbosity::None);
let res = unsafe { AcpiTables::search_for_rsdp_bios(MorosAcpiHandler) };
match res {
Ok(acpi) => {
if let Ok(fadt) = acpi.find_table::<acpi::fadt::Fadt>() {
if let Ok(block) = fadt.pm1a_control_block() {
pm1a_control_block = block.address as u32;
//debug!("ACPI Found PM1a Control Block: {:#X}", pm1a_control_block);
}
}
if let Ok(dsdt) = &acpi.dsdt() {
let address = sys::mem::phys_to_virt(PhysAddr::new(dsdt.address as u64));
//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) };
let phys_addr = PhysAddr::new(dsdt.address as u64);
let virt_addr = sys::mem::phys_to_virt(phys_addr);
let ptr = virt_addr.as_ptr();
let table = unsafe {
core::slice::from_raw_parts(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) {
//debug!("ACPI Found \\_S5 in DSDT");
let res = aml.namespace.get_by_path(&name);
if let Ok(AmlValue::Package(s5)) = res {
if let AmlValue::Integer(value) = s5[0] {
slp_typa = value as u16;
//debug!("ACPI Found SLP_TYPa in \\_S5: {}", slp_typa);
}
}
} else {
@ -67,10 +69,15 @@ pub fn shutdown() {
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)
unsafe fn map_physical_region<T>(
&self,
addr: usize,
size: usize,
) -> PhysicalMapping<Self, T> {
let phys_addr = PhysAddr::new(addr as u64);
let virt_addr = sys::mem::phys_to_virt(phys_addr);
let ptr = NonNull::new(virt_addr.as_mut_ptr()).unwrap();
PhysicalMapping::new(addr, ptr, size, size, Self)
}
fn unmap_physical_region<T>(_region: &PhysicalMapping<Self, T>) {}
@ -79,24 +86,65 @@ impl AcpiHandler for MorosAcpiHandler {
struct MorosAmlHandler;
impl Handler for MorosAmlHandler {
fn read_u8(&self, address: usize) -> u8 { read_addr::<u8>(address) }
fn read_u16(&self, address: usize) -> u16 { read_addr::<u16>(address) }
fn read_u32(&self, address: usize) -> u32 { read_addr::<u32>(address) }
fn read_u64(&self, address: usize) -> u64 { read_addr::<u64>(address) }
fn write_u8(&mut self, _address: usize, _value: u8) { unimplemented!() }
fn write_u16(&mut self, _address: usize, _value: u16) { unimplemented!() }
fn write_u32(&mut self, _address: usize, _value: u32) { unimplemented!() }
fn write_u64(&mut self, _address: usize, _value: u64) { unimplemented!() }
fn read_io_u8(&self, _port: u16) -> u8 { unimplemented!() }
fn read_io_u16(&self, _port: u16) -> u16 { unimplemented!() }
fn read_io_u32(&self, _port: u16) -> u32 { unimplemented!() }
fn write_io_u8(&self, _port: u16, _value: u8) { unimplemented!() }
fn write_io_u16(&self, _port: u16, _value: u16) { unimplemented!() }
fn write_io_u32(&self, _port: u16, _value: u32) { unimplemented!() }
fn read_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u8 { unimplemented!() }
fn read_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u16 { unimplemented!() }
fn read_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16) -> u32 { unimplemented!() }
fn write_pci_u8(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u8) { unimplemented!() }
fn write_pci_u16(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u16) { unimplemented!() }
fn write_pci_u32(&self, _segment: u16, _bus: u8, _device: u8, _function: u8, _offset: u16, _value: u32) { unimplemented!() }
fn read_u8(&self, address: usize) -> u8 {
read_addr::<u8>(address)
}
fn read_u16(&self, address: usize) -> u16 {
read_addr::<u16>(address)
}
fn read_u32(&self, address: usize) -> u32 {
read_addr::<u32>(address)
}
fn read_u64(&self, address: usize) -> u64 {
read_addr::<u64>(address)
}
fn write_u8(&mut self, _: usize, _: u8) {
unimplemented!()
}
fn write_u16(&mut self, _: usize, _: u16) {
unimplemented!()
}
fn write_u32(&mut self, _: usize, _: u32) {
unimplemented!()
}
fn write_u64(&mut self, _: usize, _: u64) {
unimplemented!()
}
fn read_io_u8(&self, _: u16) -> u8 {
unimplemented!()
}
fn read_io_u16(&self, _: u16) -> u16 {
unimplemented!()
}
fn read_io_u32(&self, _: u16) -> u32 {
unimplemented!()
}
fn write_io_u8(&self, _: u16, _: u8) {
unimplemented!()
}
fn write_io_u16(&self, _: u16, _: u16) {
unimplemented!()
}
fn write_io_u32(&self, _: u16, _: u32) {
unimplemented!()
}
fn read_pci_u8(&self, _: u16, _: u8, _: u8, _: u8, _: u16) -> u8 {
unimplemented!()
}
fn read_pci_u16(&self, _: u16, _: u8, _: u8, _: u8, _: u16) -> u16 {
unimplemented!()
}
fn read_pci_u32(&self, _: u16, _: u8, _: u8, _: u8, _: u16) -> u32 {
unimplemented!()
}
fn write_pci_u8(&self, _: u16, _: u8, _: u8, _: u8, _: u16, _: u8) {
unimplemented!()
}
fn write_pci_u16(&self, _: u16, _: u8, _: u8, _: u8, _: u16, _: u16) {
unimplemented!()
}
fn write_pci_u32(&self, _: u16, _: u8, _: u8, _: u8, _: u16, _: u32) {
unimplemented!()
}
}

View File

@ -8,9 +8,10 @@ use core::cmp;
use core::ops::{Index, IndexMut};
use linked_list_allocator::LockedHeap;
use spin::Mutex;
use x86_64::structures::paging::OffsetPageTable;
use x86_64::structures::paging::mapper::MapToError;
use x86_64::structures::paging::{FrameAllocator, Mapper, Page, PageTableFlags, Size4KiB};
use x86_64::structures::paging::{
mapper::MapToError, page::PageRangeInclusive,
FrameAllocator, Mapper, OffsetPageTable, Page, PageTableFlags, Size4KiB,
};
use x86_64::VirtAddr;
#[cfg_attr(not(feature = "userspace"), global_allocator)]
@ -19,15 +20,16 @@ static ALLOCATOR: LockedHeap = LockedHeap::empty();
pub const HEAP_START: u64 = 0x4444_4444_0000;
fn max_memory() -> u64 {
option_env!("MOROS_MEMORY").unwrap_or("32").parse::<u64>().unwrap() << 20 // MB
// Default to 32 MB
option_env!("MOROS_MEMORY").unwrap_or("32").parse::<u64>().unwrap() << 20
}
pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
let mapper = sys::mem::mapper();
let mut frame_allocator = sys::mem::frame_allocator();
// Use half of the memory for the heap caped to 16MB by default because the
// allocator is slow.
// Use half of the memory for the heap caped to 16 MB by default
// because the allocator is slow.
let heap_size = cmp::min(sys::mem::memory_size(), max_memory()) / 2;
let heap_start = VirtAddr::new(HEAP_START);
sys::process::init_process_addr(HEAP_START + heap_size);
@ -40,8 +42,10 @@ pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
};
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE;
for page in pages {
let frame = frame_allocator.allocate_frame().ok_or(MapToError::FrameAllocationFailed)?;
let err = MapToError::FrameAllocationFailed;
let frame = frame_allocator.allocate_frame().ok_or(err)?;
unsafe {
mapper.map_to(page, frame, flags, &mut frame_allocator)?.flush();
}
@ -54,47 +58,54 @@ pub fn init_heap() -> Result<(), MapToError<Size4KiB>> {
Ok(())
}
pub fn alloc_pages(mapper: &mut OffsetPageTable, addr: u64, size: usize) -> Result<(), ()> {
//debug!("Alloc pages (addr={:#X}, size={})", addr, size);
pub fn alloc_pages(
mapper: &mut OffsetPageTable, addr: u64, size: usize
) -> Result<(), ()> {
let size = size.saturating_sub(1) as u64;
let mut frame_allocator = sys::mem::frame_allocator();
let flags = PageTableFlags::PRESENT | PageTableFlags::WRITABLE | PageTableFlags::USER_ACCESSIBLE;
let pages = {
let start_page = Page::containing_address(VirtAddr::new(addr));
let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1));
let end_page = Page::containing_address(VirtAddr::new(addr + size));
Page::range_inclusive(start_page, end_page)
};
let flags = PageTableFlags::PRESENT
| PageTableFlags::WRITABLE
| PageTableFlags::USER_ACCESSIBLE;
for page in pages {
//debug!("Alloc page {:?}", page);
if let Some(frame) = frame_allocator.allocate_frame() {
//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 {:?} to {:?}", page, frame);
return Err(());
}
let res = unsafe {
mapper.map_to(page, frame, flags, &mut frame_allocator)
};
if let Ok(mapping) = res {
mapping.flush();
} else {
debug!("Could not map {:?} to {:?}", page, frame);
return Err(());
}
} else {
debug!("Could not allocate frame for {:?}", page);
return Err(());
}
}
Ok(())
}
use x86_64::structures::paging::page::PageRangeInclusive;
// TODO: Replace `free` by `dealloc`
pub fn free_pages(mapper: &mut OffsetPageTable, addr: u64, size: usize) {
let size = size.saturating_sub(1) as u64;
let pages: PageRangeInclusive<Size4KiB> = {
let start_page = Page::containing_address(VirtAddr::new(addr));
let end_page = Page::containing_address(VirtAddr::new(addr + (size as u64) - 1));
let end_page = Page::containing_address(VirtAddr::new(addr + size));
Page::range_inclusive(start_page, end_page)
};
for page in pages {
if let Ok((_frame, mapping)) = mapper.unmap(page) {
if let Ok((_, mapping)) = mapper.unmap(page) {
mapping.flush();
} else {
//debug!("Could not unmap {:?}", page);
@ -117,7 +128,9 @@ impl PhysBuf {
let buffer_len = vec.len() - 1;
let memory_len = phys_addr(&vec[buffer_len]) - phys_addr(&vec[0]);
if buffer_len == memory_len as usize {
Self { buf: Arc::new(Mutex::new(vec)) }
Self {
buf: Arc::new(Mutex::new(vec)),
}
} else {
Self::from(vec.clone()) // Clone vec and try again
}
@ -162,7 +175,9 @@ impl core::ops::Deref for PhysBuf {
impl core::ops::DerefMut for PhysBuf {
fn deref_mut(&mut self) -> &mut [u8] {
let mut vec = self.buf.lock();
unsafe { alloc::slice::from_raw_parts_mut(vec.as_mut_ptr(), vec.len()) }
unsafe {
alloc::slice::from_raw_parts_mut(vec.as_mut_ptr(), vec.len())
}
}
}

View File

@ -9,15 +9,17 @@ use lazy_static::lazy_static;
use spin::Mutex;
use x86_64::instructions::port::{Port, PortReadOnly, PortWriteOnly};
// See "Information Technology - AT Attachment with Packet Interface Extension (ATA/ATAPI-4)" (1998)
// Information Technology
// AT Attachment with Packet Interface Extension (ATA/ATAPI-4)
// (1998)
pub const BLOCK_SIZE: usize = 512;
#[repr(u16)]
#[derive(Debug, Clone, Copy)]
enum Command {
Read = 0x20,
Write = 0x30,
Read = 0x20,
Write = 0x30,
Identify = 0xEC,
}
@ -67,8 +69,8 @@ pub struct Bus {
impl Bus {
pub fn new(id: u8, io_base: u16, ctrl_base: u16, irq: u8) -> Self {
Self {
id, irq,
id,
irq,
data_register: Port::new(io_base + 0),
error_register: PortReadOnly::new(io_base + 1),
features_register: PortWriteOnly::new(io_base + 1),
@ -79,7 +81,6 @@ impl Bus {
drive_register: Port::new(io_base + 6),
status_register: PortReadOnly::new(io_base + 7),
command_register: PortWriteOnly::new(io_base + 7),
alternate_status_register: PortReadOnly::new(ctrl_base + 0),
control_register: PortWriteOnly::new(ctrl_base + 0),
drive_blockess_register: PortReadOnly::new(ctrl_base + 1),
@ -129,7 +130,10 @@ impl Bus {
let start = sys::clock::uptime();
while self.status().get_bit(bit as usize) != val {
if sys::clock::uptime() - start > 1.0 {
debug!("ATA hanged while polling {:?} bit in status register", bit);
debug!(
"ATA hanged while polling {:?} bit in status register",
bit
);
self.debug();
return Err(());
}
@ -153,7 +157,11 @@ impl Bus {
Ok(())
}
fn write_command_params(&mut self, drive: u8, block: u32) -> Result<(), ()> {
fn write_command_params(
&mut self,
drive: u8,
block: u32
) -> Result<(), ()> {
let lba = true;
let mut bytes = block.to_le_bytes();
bytes[3].set_bit(4, drive > 0);
@ -194,7 +202,12 @@ impl Bus {
Ok(())
}
fn read(&mut self, drive: u8, block: u32, buf: &mut [u8]) -> Result<(), ()> {
fn read(
&mut self,
drive: u8,
block: u32,
buf: &mut [u8]
) -> Result<(), ()> {
debug_assert!(buf.len() == BLOCK_SIZE);
self.setup_pio(drive, block)?;
self.write_command(Command::Read)?;
@ -242,7 +255,9 @@ impl Bus {
}
}
match (self.lba1(), self.lba2()) {
(0x00, 0x00) => Ok(IdentifyResponse::Ata([(); 256].map(|_| { self.read_data() }))),
(0x00, 0x00) => {
Ok(IdentifyResponse::Ata([(); 256].map(|_| self.read_data())))
}
(0x14, 0xEB) => Ok(IdentifyResponse::Atapi),
(0x3C, 0xC3) => Ok(IdentifyResponse::Sata),
(_, _) => Err(()),
@ -253,17 +268,23 @@ impl Bus {
fn reset(&mut self) {
unsafe {
self.control_register.write(4); // Set SRST bit
self.wait(5); // Wait at least 5 ns
self.wait(5); // Wait at least 5 ns
self.control_register.write(0); // Then clear it
self.wait(2000); // Wait at least 2 ms
self.wait(2000); // Wait at least 2 ms
}
}
#[allow(dead_code)]
fn debug(&mut self) {
unsafe {
debug!("ATA status register: 0b{:08b} <BSY|DRDY|#|#|DRQ|#|#|ERR>", self.alternate_status_register.read());
debug!("ATA error register: 0b{:08b} <#|#|#|#|#|ABRT|#|#>", self.error_register.read());
debug!(
"ATA status register: 0b{:08b} <BSY|DRDY|#|#|DRQ|#|#|ERR>",
self.alternate_status_register.read()
);
debug!(
"ATA error register: 0b{:08b} <#|#|#|#|#|ABRT|#|#>",
self.error_register.read()
);
}
}
}
@ -296,12 +317,22 @@ pub struct Drive {
impl Drive {
pub fn open(bus: u8, dsk: u8) -> Option<Self> {
let mut buses = BUSES.lock();
if let Ok(IdentifyResponse::Ata(res)) = buses[bus as usize].identify_drive(dsk) {
let res = buses[bus as usize].identify_drive(dsk);
if let Ok(IdentifyResponse::Ata(res)) = res {
let buf = res.map(u16::to_be_bytes).concat();
let serial = String::from_utf8_lossy(&buf[20..40]).trim().into();
let model = String::from_utf8_lossy(&buf[54..94]).trim().into();
let blocks = u32::from_be_bytes(buf[120..124].try_into().unwrap()).rotate_left(16);
Some(Self { bus, dsk, model, serial, blocks })
let blocks = u32::from_be_bytes(buf[120..124].try_into().unwrap()).
rotate_left(16);
Some(Self {
bus,
dsk,
model,
serial,
blocks,
})
} else {
None
}

View File

@ -3,9 +3,11 @@ use crate::api::fs::{FileIO, IO};
use crate::sys;
use crate::sys::cmos::CMOS;
use time::{OffsetDateTime, Duration};
use time::{Duration, OffsetDateTime};
const DAYS_BEFORE_MONTH: [u64; 13] = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365];
const DAYS_BEFORE_MONTH: [u64; 13] = [
0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365
];
#[derive(Debug, Clone)]
pub struct Uptime;
@ -36,8 +38,7 @@ impl FileIO for Uptime {
unimplemented!();
}
fn close(&mut self) {
}
fn close(&mut self) {}
fn poll(&mut self, event: IO) -> bool {
match event {
@ -81,8 +82,7 @@ impl FileIO for Realtime {
unimplemented!();
}
fn close(&mut self) {
}
fn close(&mut self) {}
fn poll(&mut self, event: IO) -> bool {
match event {
@ -96,23 +96,23 @@ impl FileIO for Realtime {
pub fn realtime() -> f64 {
let rtc = CMOS::new().rtc(); // Assuming GMT
let timestamp = 86400 * days_before_year(rtc.year as u64)
+ 86400 * days_before_month(rtc.year as u64, rtc.month as u64)
+ 86400 * (rtc.day - 1) as u64
+ 3600 * rtc.hour as u64
+ 60 * rtc.minute as u64
+ rtc.second as u64;
let ts = 86400 * days_before_year(rtc.year as u64)
+ 86400 * days_before_month(rtc.year as u64, rtc.month as u64)
+ 86400 * (rtc.day - 1) as u64
+ 3600 * rtc.hour as u64
+ 60 * rtc.minute as u64
+ rtc.second as u64;
let fract = sys::time::time_between_ticks()
* (sys::time::ticks() - sys::time::last_rtc_update()) as f64;
(timestamp as f64) + fract
(ts as f64) + fract
}
fn days_before_year(year: u64) -> u64 {
(1970..year).fold(0, |days, y| {
(1970..year).fold(0, |days, y|
days + if is_leap_year(y) { 366 } else { 365 }
})
)
}
fn days_before_month(year: u64, month: u64) -> u64 {
@ -134,7 +134,9 @@ fn is_leap_year(year: u64) -> bool {
pub fn init() {
let s = realtime();
let ns = Duration::nanoseconds(libm::floor(1e9 * (s - libm::floor(s))) as i64);
let ns = Duration::nanoseconds(
libm::floor(1e9 * (s - libm::floor(s))) as i64
);
let dt = OffsetDateTime::from_unix_timestamp(s as i64) + ns;
let rtc = dt.format(DATE_TIME_ZONE);
log!("RTC {}\n", rtc);

View File

@ -57,8 +57,10 @@ impl RTC {
impl FileIO for RTC {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
self.sync();
let date = Date::try_from_ymd(self.year.into(), self.month, self.day).map_err(|_| ())?;
let date_time = date.try_with_hms(self.hour, self.minute, self.second).map_err(|_| ())?;
let date = Date::try_from_ymd(self.year.into(), self.month, self.day).
map_err(|_| ())?;
let date_time = date.try_with_hms(self.hour, self.minute, self.second).
map_err(|_| ())?;
let out = date_time.format(DATE_TIME);
buf.copy_from_slice(out.as_bytes());
Ok(out.len())
@ -84,8 +86,7 @@ impl FileIO for RTC {
Ok(buf.len())
}
fn close(&mut self) {
}
fn close(&mut self) {}
fn poll(&mut self, event: IO) -> bool {
match event {
@ -134,16 +135,19 @@ impl CMOS {
let b = self.read_register(Register::B);
if b & 0x04 == 0 { // BCD Mode
// BCD Mode
if b & 0x04 == 0 {
rtc.second = (rtc.second & 0x0F) + ((rtc.second / 16) * 10);
rtc.minute = (rtc.minute & 0x0F) + ((rtc.minute / 16) * 10);
rtc.hour = ((rtc.hour & 0x0F) + (((rtc.hour & 0x70) / 16) * 10)) | (rtc.hour & 0x80);
rtc.hour = ((rtc.hour & 0x0F) + (((rtc.hour & 0x70) / 16) * 10))
| (rtc.hour & 0x80);
rtc.day = (rtc.day & 0x0F) + ((rtc.day / 16) * 10);
rtc.month = (rtc.month & 0x0F) + ((rtc.month / 16) * 10);
rtc.year = (rtc.year & 0x0F) + ((rtc.year / 16) * 10);
}
if (b & 0x02 == 0) && (rtc.hour & 0x80 == 0) { // 12 hour format
// 12 hour format
if (b & 0x02 == 0) && (rtc.hour & 0x80 == 0) {
rtc.hour = ((rtc.hour & 0x7F) + 12) % 24;
}
@ -165,7 +169,8 @@ impl CMOS {
let b = self.read_register(Register::B);
if b & 0x02 == 0 { // 12 hour format
// 12 hour format
if b & 0x02 == 0 {
if hour == 0 {
hour = 24;
}
@ -175,7 +180,8 @@ impl CMOS {
}
}
if b & 0x04 == 0 { // BCD Mode
// BCD Mode
if b & 0x04 == 0 {
second = 16 * (second / 10) + (second % 10);
minute = 16 * (minute / 10) + (minute % 10);
hour = 16 * (hour / 10) + (hour % 10);

View File

@ -11,6 +11,11 @@ pub static STDIN: Mutex<String> = Mutex::new(String::new());
pub static ECHO: AtomicBool = AtomicBool::new(true);
pub static RAW: AtomicBool = AtomicBool::new(false);
pub const BS_KEY: char = '\x08'; // Backspace
pub const EOT_KEY: char = '\x04'; // End of Transmission
pub const ESC_KEY: char = '\x1B'; // Escape
pub const ETX_KEY: char = '\x03'; // End of Text
#[derive(Debug, Clone)]
pub struct Console;
@ -44,8 +49,7 @@ impl FileIO for Console {
Ok(n)
}
fn close(&mut self) {
}
fn close(&mut self) {}
fn poll(&mut self, event: IO) -> bool {
match event {
@ -99,11 +103,6 @@ pub fn is_raw_enabled() -> bool {
RAW.load(Ordering::SeqCst)
}
pub const ETX_KEY: char = '\x03'; // End of Text
pub const EOT_KEY: char = '\x04'; // End of Transmission
pub const BS_KEY: char = '\x08'; // Backspace
pub const ESC_KEY: char = '\x1B'; // Escape
pub fn key_handle(key: char) {
let mut stdin = STDIN.lock();
@ -113,41 +112,45 @@ pub fn key_handle(key: char) {
if is_echo_enabled() {
let n = match c {
ETX_KEY | EOT_KEY | ESC_KEY => 2,
_ => if (c as u32) < 0xFF { 1 } else { c.len_utf8() },
_ => {
if (c as u32) < 0xFF {
1
} else {
c.len_utf8()
}
}
};
print_fmt(format_args!("{}", BS_KEY.to_string().repeat(n)));
}
}
} else {
let key = if (key as u32) < 0xFF { (key as u8) as char } else { key };
let key = if (key as u32) < 0xFF {
(key as u8) as char
} else {
key
};
stdin.push(key);
if is_echo_enabled() {
match key {
ETX_KEY => print_fmt(format_args!("^C")),
EOT_KEY => print_fmt(format_args!("^D")),
ESC_KEY => print_fmt(format_args!("^[")),
_ => print_fmt(format_args!("{}", key)),
_ => print_fmt(format_args!("{}", key)),
};
}
}
}
pub fn end_of_text() -> bool {
interrupts::without_interrupts(|| {
STDIN.lock().contains(ETX_KEY)
})
interrupts::without_interrupts(|| STDIN.lock().contains(ETX_KEY))
}
pub fn end_of_transmission() -> bool {
interrupts::without_interrupts(|| {
STDIN.lock().contains(EOT_KEY)
})
interrupts::without_interrupts(|| STDIN.lock().contains(EOT_KEY))
}
pub fn drain() {
interrupts::without_interrupts(|| {
STDIN.lock().clear();
})
interrupts::without_interrupts(|| STDIN.lock().clear())
}
pub fn read_char() -> char {
@ -182,9 +185,7 @@ pub fn read_line() -> String {
stdin.clear();
Some(line)
}
_ => {
None
}
_ => None,
}
});
if let Some(line) = res {

View File

@ -11,8 +11,8 @@ pub fn init() {
log!("CPU {}\n", processor_brand_string.as_str().trim());
}
if let Some(processor_frequency_info) = cpuid.get_processor_frequency_info() {
let processor_base_frequency = processor_frequency_info.processor_base_frequency();
log!("CPU {} MHz\n", processor_base_frequency);
if let Some(info) = cpuid.get_processor_frequency_info() {
let frequency = info.processor_base_frequency();
log!("CPU {} MHz\n", frequency);
}
}

View File

@ -1,5 +1,6 @@
use super::bitmap_block::BitmapBlock;
use super::block_device::BlockDeviceIO;
use super::block_device::BLOCK_DEVICE;
use core::convert::TryInto;
@ -22,9 +23,7 @@ impl Block {
pub fn alloc() -> Option<Self> {
match BitmapBlock::next_free_addr() {
None => {
None
}
None => None,
Some(addr) => {
BitmapBlock::alloc(addr);
@ -42,7 +41,7 @@ impl Block {
pub fn read(addr: u32) -> Self {
let mut buf = [0; super::BLOCK_SIZE];
if let Some(ref mut block_device) = *super::block_device::BLOCK_DEVICE.lock() {
if let Some(ref mut block_device) = *BLOCK_DEVICE.lock() {
if block_device.read(addr, &mut buf).is_err() {
debug!("MFS: could not read block {:#X}", addr);
}
@ -51,7 +50,7 @@ impl Block {
}
pub fn write(&self) {
if let Some(ref mut block_device) = *super::block_device::BLOCK_DEVICE.lock() {
if let Some(ref mut block_device) = *BLOCK_DEVICE.lock() {
if block_device.write(self.addr, &self.buf).is_err() {
debug!("MFS: could not write block {:#X}", self.addr);
}
@ -78,12 +77,14 @@ impl Block {
}
pub struct LinkedBlock {
block: Block
block: Block,
}
impl LinkedBlock {
pub fn new(addr: u32) -> Self {
Self { block: Block::new(addr) }
Self {
block: Block::new(addr),
}
}
pub fn alloc() -> Option<Self> {
@ -91,7 +92,9 @@ impl LinkedBlock {
}
pub fn read(addr: u32) -> Self {
Self { block: Block::read(addr) }
Self {
block: Block::read(addr),
}
}
pub fn write(&self) {

View File

@ -61,12 +61,6 @@ impl MemBlockDevice {
let dev = vec![[0; super::BLOCK_SIZE]; len];
Self { dev }
}
/*
pub fn len(&self) -> usize {
self.dev.len()
}
*/
}
impl BlockDeviceIO for MemBlockDevice {
@ -112,7 +106,7 @@ const ATA_CACHE_SIZE: usize = 1024;
#[derive(Clone)]
pub struct AtaBlockDevice {
cache: [Option<(u32, Vec<u8>)>; ATA_CACHE_SIZE],
dev: sys::ata::Drive
dev: sys::ata::Drive,
}
impl AtaBlockDevice {

View File

@ -1,14 +1,14 @@
use super::{dirname, filename, realpath, FileIO, IO};
use super::block::LinkedBlock;
use super::dir::Dir;
use super::file::File;
use super::block::LinkedBlock;
use super::{dirname, filename, realpath, FileIO, IO};
use crate::sys::clock::{Realtime, Uptime};
use crate::sys::cmos::RTC;
use crate::sys::console::Console;
use crate::sys::random::Random;
use crate::sys::clock::{Uptime, Realtime};
use crate::sys::net::socket::tcp::TcpSocket;
use crate::sys::net::socket::udp::UdpSocket;
use crate::sys::random::Random;
use alloc::vec;
use alloc::vec::Vec;
@ -37,7 +37,7 @@ impl DeviceType {
DeviceType::Console => Console::size(),
DeviceType::TcpSocket => TcpSocket::size(),
DeviceType::UdpSocket => UdpSocket::size(),
_ => 1,
_ => 1,
};
let mut res = vec![0; len];
res[0] = self as u8;
@ -61,15 +61,33 @@ pub enum Device {
impl From<u8> for Device {
fn from(i: u8) -> Self {
match i {
i if i == DeviceType::Null as u8 => Device::Null,
i if i == DeviceType::File as u8 => Device::File(File::new()),
i if i == DeviceType::Console as u8 => Device::Console(Console::new()),
i if i == DeviceType::Random as u8 => Device::Random(Random::new()),
i if i == DeviceType::Uptime as u8 => Device::Uptime(Uptime::new()),
i if i == DeviceType::Realtime as u8 => Device::Realtime(Realtime::new()),
i if i == DeviceType::RTC as u8 => Device::RTC(RTC::new()),
i if i == DeviceType::TcpSocket as u8 => Device::TcpSocket(TcpSocket::new()),
i if i == DeviceType::UdpSocket as u8 => Device::UdpSocket(UdpSocket::new()),
i if i == DeviceType::Null as u8 => {
Device::Null
}
i if i == DeviceType::File as u8 => {
Device::File(File::new())
}
i if i == DeviceType::Console as u8 => {
Device::Console(Console::new())
}
i if i == DeviceType::Random as u8 => {
Device::Random(Random::new())
}
i if i == DeviceType::Uptime as u8 => {
Device::Uptime(Uptime::new())
}
i if i == DeviceType::Realtime as u8 => {
Device::Realtime(Realtime::new())
}
i if i == DeviceType::RTC as u8 => {
Device::RTC(RTC::new())
}
i if i == DeviceType::TcpSocket as u8 => {
Device::TcpSocket(TcpSocket::new())
}
i if i == DeviceType::UdpSocket as u8 => {
Device::UdpSocket(UdpSocket::new())
}
_ => unimplemented!(),
}
}
@ -82,7 +100,7 @@ impl Device {
let filename = filename(&pathname);
if let Some(mut dir) = Dir::open(dirname) {
if let Some(dir_entry) = dir.create_device(filename) {
return Some(Device::File(dir_entry.into()))
return Some(Device::File(dir_entry.into()));
}
}
None
@ -138,7 +156,7 @@ impl FileIO for Device {
fn close(&mut self) {
match self {
Device::Null => {},
Device::Null => {}
Device::File(io) => io.close(),
Device::Console(io) => io.close(),
Device::Random(io) => io.close(),

View File

@ -1,10 +1,10 @@
use super::{dirname, filename, realpath, FileIO, IO};
use super::super_block::SuperBlock;
use super::bitmap_block::BitmapBlock;
use super::block::LinkedBlock;
use super::dir_entry::DirEntry;
use super::read_dir::ReadDir;
use super::bitmap_block::BitmapBlock;
use super::super_block::SuperBlock;
use super::FileType;
use super::block::LinkedBlock;
use super::{dirname, filename, realpath, FileIO, IO};
use crate::sys;
use alloc::boxed::Box;
@ -22,7 +22,13 @@ pub struct Dir {
impl From<DirEntry> for Dir {
fn from(entry: DirEntry) -> Self {
Self { parent: Some(Box::new(entry.dir())), name: entry.name(), addr: entry.addr(), size: entry.size(), entry_index: 0 }
Self {
parent: Some(Box::new(entry.dir())),
name: entry.name(),
addr: entry.addr(),
size: entry.size(),
entry_index: 0,
}
}
}
@ -30,7 +36,13 @@ impl Dir {
pub fn root() -> Self {
let name = String::new();
let addr = SuperBlock::read().data_area();
let mut root = Self { parent: None, name, addr, size: 0, entry_index: 0 };
let mut root = Self {
parent: None,
name,
addr,
size: 0,
entry_index: 0,
};
root.update_size();
root
}
@ -71,10 +83,8 @@ impl Dir {
} else {
return None;
}
},
None => {
return None
},
}
None => return None,
}
}
Some(dir)
@ -110,7 +120,8 @@ impl Dir {
let mut entries = self.entries();
while entries.next().is_some() {}
// Allocate a new block for the dir if no space left for adding the new entry
// Allocate a new block for the dir if no space left for adding
// the new entry.
let space_left = entries.block.data().len() - entries.block_offset();
let entry_len = DirEntry::empty_len() + name.len();
if entry_len > space_left {
@ -119,7 +130,7 @@ impl Dir {
Some(new_block) => {
entries.block = new_block;
entries.block_offset = 0;
},
}
}
}
@ -144,7 +155,14 @@ impl Dir {
entries.block.write();
self.update_size();
Some(DirEntry::new(self.clone(), kind, entry_addr, entry_size, entry_time, &entry_name))
Some(DirEntry::new(
self.clone(),
kind,
entry_addr,
entry_size,
entry_time,
&entry_name,
))
}
// Deleting an entry is done by setting the entry address to 0
@ -244,8 +262,7 @@ impl FileIO for Dir {
Err(())
}
fn close(&mut self) {
}
fn close(&mut self) {}
fn poll(&mut self, event: IO) -> bool {
match event {
@ -255,7 +272,8 @@ impl FileIO for Dir {
}
}
// Truncate to the given number of bytes at most while respecting char boundaries
// Truncate to the given number of bytes at most
// while respecting char boundaries.
fn truncate(s: &str, max: usize) -> String {
s.char_indices().take_while(|(i, _)| *i <= max).map(|(_, c)| c).collect()
}

View File

@ -1,5 +1,5 @@
use super::{dirname, filename, realpath, FileType};
use super::dir::Dir;
use super::{dirname, filename, realpath, FileType};
use alloc::string::String;
use alloc::vec::Vec;
@ -26,9 +26,23 @@ impl DirEntry {
None
}
pub fn new(dir: Dir, kind: FileType, addr: u32, size: u32, time: u64, name: &str) -> Self {
pub fn new(
dir: Dir,
kind: FileType,
addr: u32,
size: u32,
time: u64,
name: &str
) -> Self {
let name = String::from(name);
Self { dir, kind, addr, size, time, name }
Self {
dir,
kind,
addr,
size,
time,
name,
}
}
pub fn empty_len() -> usize {
@ -80,7 +94,12 @@ impl DirEntry {
}
pub fn info(&self) -> FileInfo {
FileInfo { kind: self.kind, name: self.name(), size: self.size(), time: self.time }
FileInfo {
kind: self.kind,
name: self.name(),
size: self.size(),
time: self.time,
}
}
}
@ -94,7 +113,12 @@ pub struct FileInfo {
impl FileInfo {
pub fn new() -> Self {
Self { kind: FileType::File, name: String::new(), size: 0, time: 0 }
Self {
kind: FileType::File,
name: String::new(),
size: 0,
time: 0,
}
}
pub fn root() -> Self {
@ -102,7 +126,12 @@ impl FileInfo {
let name = String::new();
let size = Dir::root().size() as u32;
let time = 0;
Self { kind, name, size, time }
Self {
kind,
name,
size,
time,
}
}
pub fn size(&self) -> u32 {
@ -147,11 +176,12 @@ impl FileInfo {
}
}
use core::convert::TryInto;
use core::convert::From;
use core::convert::TryInto;
impl From<&[u8]> for FileInfo {
fn from(buf: &[u8]) -> Self {
let kind = match buf[0] { // TODO: Add FileType::from(u8)
let kind = match buf[0] {
// TODO: Add FileType::from(u8)
0 => FileType::Dir,
1 => FileType::File,
2 => FileType::Device,
@ -161,6 +191,11 @@ impl From<&[u8]> for FileInfo {
let time = u64::from_be_bytes(buf[5..13].try_into().unwrap());
let i = 14 + buf[13] as usize;
let name = String::from_utf8_lossy(&buf[14..i]).into();
Self { kind, name, size, time }
Self {
kind,
name,
size,
time,
}
}
}

View File

@ -1,7 +1,7 @@
use super::{dirname, filename, realpath, FileIO, IO};
use super::dir::Dir;
use super::block::LinkedBlock;
use super::dir::Dir;
use super::dir_entry::DirEntry;
use super::{dirname, filename, realpath, FileIO, IO};
use alloc::boxed::Box;
use alloc::string::{String, ToString};
@ -42,7 +42,7 @@ impl File {
name: String::new(),
addr: 0,
size: 0,
offset:0,
offset: 0,
}
}
@ -87,13 +87,13 @@ impl File {
SeekFrom::End(i) => i + self.size as i32,
};
if offset < 0 || offset > self.size as i32 { // TODO: offset > size?
return Err(())
return Err(());
}
self.offset = offset as u32;
Ok(self.offset)
}
// TODO: add `read_to_end(&self, buf: &mut Vec<u8>) -> Result<u32>`
// TODO: Add `read_to_end(&self, buf: &mut Vec<u8>) -> Result<u32>`
// TODO: `return Result<String>`
pub fn read_to_string(&mut self) -> String {
@ -200,8 +200,7 @@ impl FileIO for File {
Ok(bytes)
}
fn close(&mut self) {
}
fn close(&mut self) {}
fn poll(&mut self, event: IO) -> bool {
match event {

View File

@ -1,5 +1,5 @@
mod block;
mod bitmap_block;
mod block;
mod block_device;
mod device;
mod dir;
@ -10,14 +10,16 @@ mod super_block;
use crate::sys;
pub use crate::api::fs::{dirname, filename, realpath, FileIO, IO};
pub use crate::sys::ata::BLOCK_SIZE;
pub use bitmap_block::BITMAP_SIZE;
pub use block_device::{
dismount, format_ata, format_mem, is_mounted, mount_ata, mount_mem
};
pub use device::{Device, DeviceType};
pub use dir::Dir;
pub use dir_entry::FileInfo;
pub use file::{File, SeekFrom};
pub use block_device::{format_ata, format_mem, is_mounted, mount_ata, mount_mem, dismount};
pub use crate::api::fs::{dirname, filename, realpath, FileIO, IO};
pub use crate::sys::ata::BLOCK_SIZE;
use dir_entry::DirEntry;
use super_block::SuperBlock;
@ -149,10 +151,8 @@ pub fn canonicalize(path: &str) -> Result<String, ()> {
} else {
Ok(path.to_string())
}
},
None => {
Ok(path.to_string())
}
None => Ok(path.to_string()),
}
}

View File

@ -1,6 +1,6 @@
use super::dir_entry::DirEntry;
use super::block::LinkedBlock;
use super::dir::Dir;
use super::dir_entry::DirEntry;
use super::FileType;
use alloc::string::String;
@ -87,7 +87,7 @@ impl Iterator for ReadDir {
_ => {
self.block_offset = offset; // Rewind the cursor
break;
},
}
};
let entry_addr = self.read_u32();
@ -109,7 +109,14 @@ impl Iterator for ReadDir {
}
let dir = self.dir.clone();
return Some(DirEntry::new(dir, entry_kind, entry_addr, entry_size, entry_time, &entry_name));
return Some(DirEntry::new(
dir,
entry_kind,
entry_addr,
entry_size,
entry_time,
&entry_name,
));
}
match self.block.next() {

View File

@ -1,7 +1,7 @@
use crate::sys;
use crate::KERNEL_SIZE;
use super::block::Block;
use super::block_device::BlockDeviceIO;
use crate::sys;
use crate::KERNEL_SIZE;
use core::convert::TryInto;
const SUPERBLOCK_ADDR: u32 = (KERNEL_SIZE / super::BLOCK_SIZE) as u32;
@ -9,7 +9,7 @@ const SIGNATURE: &[u8; 8] = b"MOROS FS";
#[derive(Debug)]
pub struct SuperBlock {
signature: &'static[u8; 8],
signature: &'static [u8; 8],
version: u8,
block_size: u32,
pub block_count: u32,

View File

@ -1,14 +1,16 @@
use lazy_static::lazy_static;
use x86_64::VirtAddr;
use x86_64::instructions::segmentation::{CS, DS, Segment};
use x86_64::instructions::segmentation::{Segment, CS, DS};
use x86_64::instructions::tables::load_tss;
use x86_64::structures::gdt::{Descriptor, GlobalDescriptorTable, SegmentSelector};
use x86_64::structures::gdt::{
Descriptor, GlobalDescriptorTable, SegmentSelector
};
use x86_64::structures::tss::TaskStateSegment;
use x86_64::VirtAddr;
const STACK_SIZE: usize = 1024 * 8;
pub const DOUBLE_FAULT_IST_INDEX: u16 = 0;
pub const PAGE_FAULT_IST_INDEX: u16 = 1;
pub const GENERAL_PROTECTION_FAULT_IST_INDEX: u16 = 2;
pub const DOUBLE_FAULT_IST: u16 = 0;
pub const PAGE_FAULT_IST: u16 = 1;
pub const GENERAL_PROTECTION_FAULT_IST: u16 = 2;
lazy_static! {
static ref TSS: TaskStateSegment = {
@ -17,15 +19,15 @@ lazy_static! {
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
};
tss.interrupt_stack_table[DOUBLE_FAULT_IST_INDEX as usize] = {
tss.interrupt_stack_table[DOUBLE_FAULT_IST as usize] = {
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
};
tss.interrupt_stack_table[PAGE_FAULT_IST_INDEX as usize] = {
tss.interrupt_stack_table[PAGE_FAULT_IST as usize] = {
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
};
tss.interrupt_stack_table[GENERAL_PROTECTION_FAULT_IST_INDEX as usize] = {
tss.interrupt_stack_table[GENERAL_PROTECTION_FAULT_IST as usize] = {
static mut STACK: [u8; STACK_SIZE] = [0; STACK_SIZE];
VirtAddr::from_ptr(unsafe { &STACK }) + STACK_SIZE
};
@ -43,7 +45,16 @@ lazy_static! {
let user_code = gdt.add_entry(Descriptor::user_code_segment());
let user_data = gdt.add_entry(Descriptor::user_data_segment());
(gdt, Selectors { tss, code, data, user_code, user_data })
(
gdt,
Selectors {
tss,
code,
data,
user_code,
user_data,
},
)
};
}

View File

@ -1,6 +1,6 @@
use crate::{api, sys, hlt_loop};
use crate::api::process::ExitCode;
use crate::sys::process::Registers;
use crate::{api, hlt_loop, sys};
use core::arch::asm;
use lazy_static::lazy_static;
@ -8,7 +8,10 @@ use spin::Mutex;
use x86_64::instructions::interrupts;
use x86_64::instructions::port::Port;
use x86_64::registers::control::Cr2;
use x86_64::structures::idt::{InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue, PageFaultErrorCode};
use x86_64::structures::idt::{
InterruptDescriptorTable, InterruptStackFrame, InterruptStackFrameValue,
PageFaultErrorCode,
};
use x86_64::structures::paging::OffsetPageTable;
use x86_64::VirtAddr;
@ -24,10 +27,12 @@ fn interrupt_index(irq: u8) -> u8 {
sys::pic::PIC_1_OFFSET + irq
}
fn default_irq_handler() {}
fn default_handler() {}
lazy_static! {
pub static ref IRQ_HANDLERS: Mutex<[fn(); 16]> = Mutex::new([default_irq_handler; 16]);
static ref IRQ_HANDLERS: Mutex<[fn(); 16]> = {
Mutex::new([default_handler; 16])
};
static ref IDT: InterruptDescriptorTable = {
let mut idt = InterruptDescriptorTable::new();
@ -37,16 +42,17 @@ lazy_static! {
unsafe {
idt.double_fault.
set_handler_fn(double_fault_handler).
set_stack_index(sys::gdt::DOUBLE_FAULT_IST_INDEX);
set_stack_index(sys::gdt::DOUBLE_FAULT_IST);
idt.page_fault.
set_handler_fn(page_fault_handler).
set_stack_index(sys::gdt::PAGE_FAULT_IST_INDEX);
set_stack_index(sys::gdt::PAGE_FAULT_IST);
idt.general_protection_fault.
set_handler_fn(general_protection_fault_handler).
set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST_INDEX);
set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST);
let f = wrapped_syscall_handler as *mut fn();
idt[0x80].
set_handler_fn(core::mem::transmute(wrapped_syscall_handler as *mut fn())).
//set_stack_index(sys::gdt::GENERAL_PROTECTION_FAULT_IST_INDEX).
set_handler_fn(core::mem::transmute(f)).
set_privilege_level(x86_64::PrivilegeLevel::Ring3);
}
idt[interrupt_index(0) as usize].set_handler_fn(irq0_handler);
@ -71,10 +77,14 @@ lazy_static! {
macro_rules! irq_handler {
($handler:ident, $irq:expr) => {
pub extern "x86-interrupt" fn $handler(_stack_frame: InterruptStackFrame) {
pub extern "x86-interrupt" fn $handler(_: InterruptStackFrame) {
let handlers = IRQ_HANDLERS.lock();
handlers[$irq]();
unsafe { sys::pic::PICS.lock().notify_end_of_interrupt(interrupt_index($irq)); }
unsafe {
sys::pic::PICS.lock().notify_end_of_interrupt(
interrupt_index($irq)
);
}
}
};
}
@ -102,25 +112,36 @@ extern "x86-interrupt" fn breakpoint_handler(stack_frame: InterruptStackFrame) {
panic!();
}
extern "x86-interrupt" fn double_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) -> ! {
extern "x86-interrupt" fn double_fault_handler(
stack_frame: InterruptStackFrame,
error_code: u64,
) -> ! {
debug!("EXCEPTION: DOUBLE FAULT");
debug!("Stack Frame: {:#?}", stack_frame);
debug!("Error: {:?}", error_code);
panic!();
}
extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame, error_code: PageFaultErrorCode) {
extern "x86-interrupt" fn page_fault_handler(
_stack_frame: InterruptStackFrame,
error_code: PageFaultErrorCode,
) {
//debug!("EXCEPTION: PAGE FAULT ({:?})", error_code);
let addr = Cr2::read().as_u64();
let page_table = unsafe { sys::process::page_table() };
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
let mut mapper = unsafe {
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
};
if sys::allocator::alloc_pages(&mut mapper, addr, 1).is_err() {
let csi_color = api::console::Style::color("LightRed");
let csi_reset = api::console::Style::reset();
printk!("{}Error:{} Could not allocate address {:#X}\n", csi_color, csi_reset, addr);
printk!(
"{}Error:{} Could not allocate address {:#X}\n",
csi_color, csi_reset, addr
);
if error_code.contains(PageFaultErrorCode::USER_MODE) {
api::syscall::exit(ExitCode::PageFaultError);
} else {
@ -129,21 +150,30 @@ extern "x86-interrupt" fn page_fault_handler(_stack_frame: InterruptStackFrame,
}
}
extern "x86-interrupt" fn general_protection_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) {
extern "x86-interrupt" fn general_protection_fault_handler(
stack_frame: InterruptStackFrame,
error_code: u64,
) {
debug!("EXCEPTION: GENERAL PROTECTION FAULT");
debug!("Stack Frame: {:#?}", stack_frame);
debug!("Error: {:?}", error_code);
panic!();
}
extern "x86-interrupt" fn stack_segment_fault_handler(stack_frame: InterruptStackFrame, error_code: u64) {
extern "x86-interrupt" fn stack_segment_fault_handler(
stack_frame: InterruptStackFrame,
error_code: u64,
) {
debug!("EXCEPTION: STACK SEGMENT FAULT");
debug!("Stack Frame: {:#?}", stack_frame);
debug!("Error: {:?}", error_code);
panic!();
}
extern "x86-interrupt" fn segment_not_present_handler(stack_frame: InterruptStackFrame, error_code: u64) {
extern "x86-interrupt" fn segment_not_present_handler(
stack_frame: InterruptStackFrame,
error_code: u64,
) {
debug!("EXCEPTION: SEGMENT NOT PRESENT");
debug!("Stack Frame: {:#?}", stack_frame);
debug!("Error: {:?}", error_code);
@ -151,7 +181,7 @@ extern "x86-interrupt" fn segment_not_present_handler(stack_frame: InterruptStac
}
// Naked function wrapper saving all scratch registers to the stack
// See: https://os.phil-opp.com/returning-from-exceptions/#a-naked-wrapper-function
// See: https://os.phil-opp.com/returning-from-exceptions/
macro_rules! wrap {
($fn: ident => $w:ident) => {
#[naked]
@ -192,26 +222,36 @@ wrap!(syscall_handler => wrapped_syscall_handler);
// NOTE: We can't use "x86-interrupt" for syscall_handler because we need to
// return a result in the RAX register and it will be overwritten when the
// context of the caller is restored.
extern "sysv64" fn syscall_handler(stack_frame: &mut InterruptStackFrame, regs: &mut Registers) {
extern "sysv64" fn syscall_handler(
stack_frame: &mut InterruptStackFrame,
regs: &mut Registers
) {
let n = regs.rax;
// The registers order follow the System V ABI convention
let n = regs.rax;
let arg1 = regs.rdi;
let arg2 = regs.rsi;
let arg3 = regs.rdx;
let arg4 = regs.r8;
if n == sys::syscall::number::SPAWN { // Backup CPU context
// Backup CPU context before spawning a process
if n == sys::syscall::number::SPAWN {
sys::process::set_stack_frame(**stack_frame);
sys::process::set_registers(*regs);
}
let res = sys::syscall::dispatcher(n, arg1, arg2, arg3, arg4);
if n == sys::syscall::number::EXIT { // Restore CPU context
// Restore CPU context before exiting a process
if n == sys::syscall::number::EXIT {
let sf = sys::process::stack_frame();
unsafe {
// FIXME: the following line should replace the next ones
//stack_frame.as_mut().write(sf);
core::ptr::write_volatile(stack_frame.as_mut().extract_inner() as *mut InterruptStackFrameValue, sf); // FIXME
let inner = stack_frame.as_mut().extract_inner();
let ptr = inner as *mut InterruptStackFrameValue;
core::ptr::write_volatile(ptr, sf);
core::ptr::write_volatile(regs, sys::process::registers());
}
}

View File

@ -1,8 +1,11 @@
use crate::sys;
use crate::api::syscall;
use crate::sys;
use core::sync::atomic::{AtomicBool, Ordering};
use pc_keyboard::{layouts, DecodedKey, Error, HandleControl, KeyState, KeyCode, KeyEvent, Keyboard, ScancodeSet1};
use pc_keyboard::{
layouts, DecodedKey, Error, HandleControl, KeyCode, KeyEvent, KeyState,
Keyboard, ScancodeSet1,
};
use spin::Mutex;
use x86_64::instructions::port::Port;
@ -21,25 +24,31 @@ pub enum KeyboardLayout {
impl KeyboardLayout {
fn add_byte(&mut self, scancode: u8) -> Result<Option<KeyEvent>, Error> {
match self {
KeyboardLayout::Azerty(keyboard) => keyboard.add_byte(scancode),
KeyboardLayout::Dvorak(keyboard) => keyboard.add_byte(scancode),
KeyboardLayout::Qwerty(keyboard) => keyboard.add_byte(scancode),
KeyboardLayout::Azerty(kb) => kb.add_byte(scancode),
KeyboardLayout::Dvorak(kb) => kb.add_byte(scancode),
KeyboardLayout::Qwerty(kb) => kb.add_byte(scancode),
}
}
fn process_keyevent(&mut self, event: KeyEvent) -> Option<DecodedKey> {
match self {
KeyboardLayout::Azerty(keyboard) => keyboard.process_keyevent(event),
KeyboardLayout::Dvorak(keyboard) => keyboard.process_keyevent(event),
KeyboardLayout::Qwerty(keyboard) => keyboard.process_keyevent(event),
KeyboardLayout::Azerty(kb) => kb.process_keyevent(event),
KeyboardLayout::Dvorak(kb) => kb.process_keyevent(event),
KeyboardLayout::Qwerty(kb) => kb.process_keyevent(event),
}
}
fn from(name: &str) -> Option<Self> {
match name {
"azerty" => Some(KeyboardLayout::Azerty(Keyboard::new(HandleControl::MapLettersToUnicode))),
"dvorak" => Some(KeyboardLayout::Dvorak(Keyboard::new(HandleControl::MapLettersToUnicode))),
"qwerty" => Some(KeyboardLayout::Qwerty(Keyboard::new(HandleControl::MapLettersToUnicode))),
"azerty" => Some(KeyboardLayout::Azerty(Keyboard::new(
HandleControl::MapLettersToUnicode,
))),
"dvorak" => Some(KeyboardLayout::Dvorak(Keyboard::new(
HandleControl::MapLettersToUnicode,
))),
"qwerty" => Some(KeyboardLayout::Qwerty(Keyboard::new(
HandleControl::MapLettersToUnicode,
))),
_ => None,
}
}
@ -82,9 +91,15 @@ fn interrupt_handler() {
if let Ok(Some(event)) = keyboard.add_byte(scancode) {
let ord = Ordering::Relaxed;
match event.code {
KeyCode::AltLeft | KeyCode::AltRight => ALT.store(event.state == KeyState::Down, ord),
KeyCode::ShiftLeft | KeyCode::ShiftRight => SHIFT.store(event.state == KeyState::Down, ord),
KeyCode::ControlLeft | KeyCode::ControlRight => CTRL.store(event.state == KeyState::Down, ord),
KeyCode::AltLeft | KeyCode::AltRight => {
ALT.store(event.state == KeyState::Down, ord)
}
KeyCode::ShiftLeft | KeyCode::ShiftRight => {
SHIFT.store(event.state == KeyState::Down, ord)
}
KeyCode::ControlLeft | KeyCode::ControlRight => {
CTRL.store(event.state == KeyState::Down, ord)
}
_ => {}
}
let is_alt = ALT.load(ord);
@ -92,16 +107,24 @@ fn interrupt_handler() {
let is_shift = SHIFT.load(ord);
if let Some(key) = keyboard.process_keyevent(event) {
match key {
DecodedKey::Unicode('\u{7f}') if is_alt && is_ctrl => syscall::reboot(), // Ctrl-Alt-Del
DecodedKey::RawKey(KeyCode::PageUp) => send_csi("5~"),
DecodedKey::RawKey(KeyCode::PageDown) => send_csi("6~"),
DecodedKey::RawKey(KeyCode::ArrowUp) => send_csi("A"),
DecodedKey::RawKey(KeyCode::ArrowDown) => send_csi("B"),
// Ctrl-Alt-Del
DecodedKey::Unicode('\u{7f}') if is_alt && is_ctrl => {
syscall::reboot()
}
DecodedKey::RawKey(KeyCode::PageUp) => send_csi("5~"),
DecodedKey::RawKey(KeyCode::PageDown) => send_csi("6~"),
DecodedKey::RawKey(KeyCode::ArrowUp) => send_csi("A"),
DecodedKey::RawKey(KeyCode::ArrowDown) => send_csi("B"),
DecodedKey::RawKey(KeyCode::ArrowRight) => send_csi("C"),
DecodedKey::RawKey(KeyCode::ArrowLeft) => send_csi("D"),
DecodedKey::Unicode('\t') if is_shift => send_csi("Z"), // Convert Shift-Tab into Backtab
DecodedKey::Unicode(c) => send_key(c),
_ => {},
DecodedKey::RawKey(KeyCode::ArrowLeft) => send_csi("D"),
// Convert Shift-Tab into Backtab
DecodedKey::Unicode('\t') if is_shift => send_csi("Z"),
DecodedKey::Unicode(c) => send_key(c),
_ => {}
};
}
}

View File

@ -3,7 +3,9 @@ use bootloader::bootinfo::{BootInfo, MemoryMap, MemoryRegionType};
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use x86_64::instructions::interrupts;
use x86_64::registers::control::Cr3;
use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, Translate};
use x86_64::structures::paging::{
FrameAllocator, OffsetPageTable, PageTable, PhysFrame, Size4KiB, Translate,
};
use x86_64::{PhysAddr, VirtAddr};
pub static mut PHYS_MEM_OFFSET: Option<u64> = None;
@ -21,7 +23,12 @@ pub fn init(boot_info: &'static BootInfo) {
let start_addr = region.range.start_addr();
let end_addr = region.range.end_addr();
memory_size += end_addr - start_addr;
log!("MEM [{:#016X}-{:#016X}] {:?}\n", start_addr, end_addr - 1, region.region_type);
log!(
"MEM [{:#016X}-{:#016X}] {:?}\n",
start_addr,
end_addr - 1,
region.region_type
);
}
log!("MEM {} KB\n", memory_size >> 10);
MEMORY_SIZE.store(memory_size, Ordering::Relaxed);
@ -30,7 +37,12 @@ pub fn init(boot_info: &'static BootInfo) {
unsafe { PHYS_MEM_OFFSET.replace(phys_mem_offset) };
unsafe { MEMORY_MAP.replace(&boot_info.memory_map) };
unsafe { MAPPER.replace(OffsetPageTable::new(active_page_table(), VirtAddr::new(phys_mem_offset))) };
unsafe {
MAPPER.replace(OffsetPageTable::new(
active_page_table(),
VirtAddr::new(phys_mem_offset),
))
};
sys::allocator::init_heap().expect("heap initialization failed");
});
@ -69,7 +81,7 @@ pub unsafe fn create_page_table(frame: PhysFrame) -> &'static mut PageTable {
}
pub struct BootInfoFrameAllocator {
memory_map: &'static MemoryMap
memory_map: &'static MemoryMap,
}
impl BootInfoFrameAllocator {
@ -79,20 +91,26 @@ impl BootInfoFrameAllocator {
fn usable_frames(&self) -> impl Iterator<Item = PhysFrame> {
let regions = self.memory_map.iter();
let usable_regions = regions.filter(|r| r.region_type == MemoryRegionType::Usable);
let addr_ranges = usable_regions.map(|r| r.range.start_addr()..r.range.end_addr());
let frame_addresses = addr_ranges.flat_map(|r| r.step_by(4096));
frame_addresses.map(|addr| PhysFrame::containing_address(PhysAddr::new(addr)))
let usable_regions = regions.filter(|r|
r.region_type == MemoryRegionType::Usable
);
let addr_ranges = usable_regions.map(|r|
r.range.start_addr()..r.range.end_addr()
);
let frame_addresses = addr_ranges.flat_map(|r|
r.step_by(4096)
);
frame_addresses.map(|addr|
PhysFrame::containing_address(PhysAddr::new(addr))
)
}
}
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.
// FIXME: When the heap is larger than a few megabytes,
// creating an iterator for each allocation become very slow.
self.usable_frames().nth(next)
}
}

View File

@ -23,7 +23,9 @@ macro_rules! log {
let uptime = $crate::sys::clock::uptime();
let csi_color = $crate::api::console::Style::color("LightGreen");
let csi_reset = $crate::api::console::Style::reset();
$crate::sys::console::print_fmt(format_args!("{}[{:.6}]{} ", csi_color, uptime, csi_reset));
$crate::sys::console::print_fmt(
format_args!("{}[{:.6}]{} ", csi_color, uptime, csi_reset)
);
$crate::sys::console::print_fmt(format_args!($($arg)*));
// TODO: Add newline
}

View File

@ -93,7 +93,10 @@ impl<'a> smoltcp::phy::Device for EthernetDevice {
caps
}
fn receive(&mut self, _instant: smoltcp::time::Instant) -> Option<(Self::RxToken<'a>, Self::TxToken<'a>)> {
fn receive(
&mut self,
_instant: smoltcp::time::Instant,
) -> Option<(Self::RxToken<'a>, Self::TxToken<'a>)> {
if let Some(buffer) = self.receive_packet() {
if self.config().is_debug_enabled() {
debug!("NET Packet Received");
@ -101,15 +104,22 @@ impl<'a> smoltcp::phy::Device for EthernetDevice {
}
self.stats().rx_add(buffer.len() as u64);
let rx = RxToken { buffer };
let tx = TxToken { device: self.clone() };
let tx = TxToken {
device: self.clone(),
};
Some((rx, tx))
} else {
None
}
}
fn transmit(&mut self, _instant: smoltcp::time::Instant) -> Option<Self::TxToken<'a>> {
let tx = TxToken { device: self.clone() };
fn transmit(
&mut self,
_instant: smoltcp::time::Instant
) -> Option<Self::TxToken<'a>> {
let tx = TxToken {
device: self.clone(),
};
Some(tx)
}
}
@ -120,7 +130,10 @@ pub struct RxToken {
}
impl smoltcp::phy::RxToken for RxToken {
fn consume<R, F>(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R {
fn consume<R, F>(mut self, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
f(&mut self.buffer)
}
}
@ -130,7 +143,10 @@ pub struct TxToken {
device: EthernetDevice,
}
impl smoltcp::phy::TxToken for TxToken {
fn consume<R, F>(mut self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R {
fn consume<R, F>(mut self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
let config = self.device.config();
let buf = self.device.next_tx_buffer(len);
if config.is_debug_enabled() {
@ -244,9 +260,15 @@ pub fn init() {
}
};
if let Some(io_base) = find_pci_io_base(0x10EC, 0x8139) {
add(EthernetDevice::RTL8139(nic::rtl8139::Device::new(io_base)), "RTL8139");
add(
EthernetDevice::RTL8139(nic::rtl8139::Device::new(io_base)),
"RTL8139",
);
}
if let Some(io_base) = find_pci_io_base(0x1022, 0x2000) {
add(EthernetDevice::PCNET(nic::pcnet::Device::new(io_base)), "PCNET");
add(
EthernetDevice::PCNET(nic::pcnet::Device::new(io_base)),
"PCNET",
);
}
}

View File

@ -1,2 +1,2 @@
pub mod rtl8139;
pub mod pcnet;
pub mod rtl8139;

View File

@ -1,6 +1,6 @@
use crate::sys;
use crate::sys::allocator::PhysBuf;
use crate::sys::net::{EthernetDeviceIO, Config, Stats};
use crate::sys::net::{Config, EthernetDeviceIO, Stats};
use alloc::sync::Arc;
use alloc::vec::Vec;
@ -26,14 +26,14 @@ const CSR0_IDON: usize = 8;
//const CSR0_BABL: usize = 14;
//const CSR0_ERR: usize = 0;
const DE_ENP: usize = 0;
const DE_STP: usize = 1;
const DE_ENP: usize = 0;
const DE_STP: usize = 1;
//const DE_BUFF: usize = 2;
//const DE_CRC: usize = 3;
//const DE_OFLO: usize = 4;
//const DE_FRAM: usize = 5;
//const DE_ERR: usize = 6;
const DE_OWN: usize = 7;
const DE_OWN: usize = 7;
#[derive(Clone)]
pub struct Ports {
@ -218,7 +218,9 @@ impl Device {
let addr = init_struct.addr();
self.ports.write_csr_32(1, addr.get_bits(0..16) as u32);
self.ports.write_csr_32(2, addr.get_bits(16..32) as u32);
assert!(self.ports.read_csr_32(0) == 0b000000100); // STOP
// STOP
debug_assert!(self.ports.read_csr_32(0) == 0b000000100);
// self.ports.write_csr_32(4, 1 << 11); // Pad short ethernet packets
@ -229,15 +231,23 @@ impl Device {
while !self.ports.read_csr_32(0).get_bit(CSR0_IDON) {
sys::time::halt();
}
assert!(self.ports.read_csr_32(0) == 0b110000001); // IDON + INTR + INIT
// IDON + INTR + INIT
debug_assert!(self.ports.read_csr_32(0) == 0b110000001);
// Start the card
self.ports.write_csr_32(0, 1 << CSR0_STRT);
assert!(self.ports.read_csr_32(0) == 0b110110011); // IDON + INTR + RXON + TXON + STRT + INIT
// IDON + INTR + RXON + TXON + STRT + INIT
debug_assert!(self.ports.read_csr_32(0) == 0b110110011);
}
fn init_descriptor_entry(&mut self, i: usize, is_rx: bool) {
let des = if is_rx { &mut self.rx_des } else { &mut self.tx_des };
let des = if is_rx {
&mut self.rx_des
} else {
&mut self.tx_des
};
// Set buffer address
let addr = if is_rx {
@ -277,47 +287,22 @@ impl EthernetDeviceIO for Device {
let rmd1 = self.rx_des[rx_id * DE_LEN + 7];
let end_of_packet = rmd1.get_bit(DE_ENP);
/*
let start_of_packet = rmd1.get_bit(DE_STP);
let error = rmd1.get_bit(DE_ERR);
let buffer_error = rmd1.get_bit(DE_BUFF);
let overflow_error = rmd1.get_bit(DE_OFLO) && !rmd1.get_bit(DE_ENP);
let crc_error = rmd1.get_bit(DE_CRC) && !rmd1.get_bit(DE_OFLO) && rmd1.get_bit(DE_ENP);
let framing_error = rmd1.get_bit(DE_FRAM) && !rmd1.get_bit(DE_OFLO) && rmd1.get_bit(DE_ENP);
if self.config.is_debug_enabled() {
printk!("Flags: ");
if start_of_packet {
printk!("start_of_packet ");
}
if end_of_packet {
printk!("end_of_packet ");
}
if error {
if overflow_error {
printk!("overflow_error ");
}
if framing_error {
printk!("framing_error ");
}
if crc_error {
printk!("crc_error ");
}
}
printk!("\n");
}
*/
// Read packet size
let packet_size = u16::from_le_bytes([
self.rx_des[rx_id * DE_LEN + 8],
self.rx_des[rx_id * DE_LEN + 9]
self.rx_des[rx_id * DE_LEN + 9],
]) as usize;
let n = if end_of_packet { packet_size } else { self.rx_buffers[rx_id].len() };
let n = if end_of_packet {
packet_size
} else {
self.rx_buffers[rx_id].len()
};
packet.extend(&self.rx_buffers[rx_id][0..n]);
self.rx_des[rx_id * DE_LEN + 7].set_bit(DE_OWN, true); // Give back ownership
// Give back ownership
self.rx_des[rx_id * DE_LEN + 7].set_bit(DE_OWN, true);
rx_id = (rx_id + 1) % RX_BUFFERS_COUNT;
self.rx_id.store(rx_id, Ordering::Relaxed);
@ -336,14 +321,18 @@ impl EthernetDeviceIO for Device {
fn transmit_packet(&mut self, len: usize) {
let tx_id = self.tx_id.load(Ordering::SeqCst);
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_STP, true); // Set start of packet
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_ENP, true); // Set end of packet
// Set start and end of packet
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_STP, true);
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_ENP, true);
// Set buffer byte count (0..12 BCNT + 12..16 ONES)
let bcnt = (0xF000 | (0x0FFF & (1 + !(len as u16)))).to_le_bytes();
self.tx_des[tx_id * DE_LEN + 4] = bcnt[0];
self.tx_des[tx_id * DE_LEN + 5] = bcnt[1];
// Give back ownership to the card
self.tx_des[tx_id * DE_LEN + 7].set_bit(DE_OWN, true);
self.tx_id.store((tx_id + 1) % TX_BUFFERS_COUNT, Ordering::Relaxed);
if !is_buffer_owner(&self.tx_des, tx_id) {

View File

@ -1,5 +1,5 @@
use crate::sys::allocator::PhysBuf;
use crate::sys::net::{EthernetDeviceIO, Config, Stats};
use crate::sys::net::{Config, EthernetDeviceIO, Stats};
use alloc::sync::Arc;
use alloc::vec::Vec;
@ -24,9 +24,9 @@ const TX_BUFFER_LEN: usize = 2048;
const TX_BUFFERS_COUNT: usize = 4;
const ROK: u16 = 0x01;
const CR_RST: u8 = 1 << 4; // Reset
const CR_RE: u8 = 1 << 3; // Receiver Enable
const CR_TE: u8 = 1 << 2; // Transmitter Enable
const CR_RST: u8 = 1 << 4; // Reset
const CR_RE: u8 = 1 << 3; // Receiver Enable
const CR_TE: u8 = 1 << 2; // Transmitter Enable
const CR_BUFE: u8 = 1 << 0; // Buffer Empty
// Rx Buffer Length
@ -38,10 +38,10 @@ const RCR_RBLEN: u32 = (RX_BUFFER_IDX << 11) as u32;
// of the buffer. So the buffer must have an additionnal 1500 bytes.
const RCR_WRAP: u32 = 1 << 7;
const RCR_AB: u32 = 1 << 3; // Accept Broadcast packets
const RCR_AM: u32 = 1 << 2; // Accept Multicast packets
const RCR_APM: u32 = 1 << 1; // Accept Physical Match packets
const RCR_AAP: u32 = 1 << 0; // Accept All Packets
const RCR_AB: u32 = 1 << 3; // Accept Broadcast packets
const RCR_AM: u32 = 1 << 2; // Accept Multicast packets
const RCR_APM: u32 = 1 << 1; // Accept Physical Match packets
const RCR_AAP: u32 = 1 << 0; // Accept All Packets
// Interframe Gap Time
const TCR_IFG: u32 = 3 << 24;
@ -68,23 +68,46 @@ const TCR_MXDMA2: u32 = 1 << 10;
//const OWC: u32 = 1 << 29; // Out of Window Collision
//const CDH: u32 = 1 << 28; // CD Heart Beat
const TOK: u32 = 1 << 15; // Transmit OK
//const TUN: u32 = 1 << 14; // Transmit FIFO Underrun
//const TUN: u32 = 1 << 14; // Transmit FIFO Underrun
const OWN: u32 = 1 << 13; // DMA operation completed
#[derive(Clone)]
pub struct Ports {
pub mac: [Port<u8>; 6], // ID Registers (IDR0 ... IDR5)
pub tx_cmds: [Port<u32>; TX_BUFFERS_COUNT], // Transmit Status of Descriptors (TSD0 .. TSD3)
pub tx_addrs: [Port<u32>; TX_BUFFERS_COUNT], // Transmit Start Address of Descriptor0 (TSAD0 .. TSAD3)
pub config1: Port<u8>, // Configuration Register 1 (CONFIG1)
pub rx_addr: Port<u32>, // Receive (Rx) Buffer Start Address (RBSTART)
pub capr: Port<u16>, // Current Address of Packet Read (CAPR)
pub cba: Port<u16>, // Current Buffer Address (CBA)
pub cmd: Port<u8>, // Command Register (CR)
pub imr: Port<u16>, // Interrupt Mask Register (IMR)
pub isr: Port<u16>, // Interrupt Status Register (ISR)
pub tx_config: Port<u32>, // Transmit (Tx) Configuration Register (TCR)
pub rx_config: Port<u32>, // Receive (Rx) Configuration Register (RCR)
// ID Registers (IDR0 ... IDR5)
pub mac: [Port<u8>; 6],
// Transmit Status of Descriptors (TSD0 .. TSD3)
pub tx_cmds: [Port<u32>; TX_BUFFERS_COUNT],
// Transmit Start Address of Descriptor0 (TSAD0 .. TSAD3)
pub tx_addrs: [Port<u32>; TX_BUFFERS_COUNT],
// Configuration Register 1 (CONFIG1)
pub config1: Port<u8>,
// Receive (Rx) Buffer Start Address (RBSTART)
pub rx_addr: Port<u32>,
// Current Address of Packet Read (CAPR)
pub capr: Port<u16>,
// Current Buffer Address (CBA)
pub cba: Port<u16>,
// Command Register (CR)
pub cmd: Port<u8>,
// Interrupt Mask Register (IMR)
pub imr: Port<u16>,
// Interrupt Status Register (ISR)
pub isr: Port<u16>,
// Transmit (Tx) Configuration Register (TCR)
pub tx_config: Port<u32>,
// Receive (Rx) Configuration Register (RCR)
pub rx_config: Port<u32>,
}
impl Ports {
@ -159,7 +182,9 @@ impl Device {
rx_buffer: PhysBuf::new(RX_BUFFER_LEN + RX_BUFFER_PAD + MTU),
rx_offset: 0,
tx_buffers: [(); TX_BUFFERS_COUNT].map(|_| PhysBuf::new(TX_BUFFER_LEN)),
tx_buffers: [(); TX_BUFFERS_COUNT].map(|_|
PhysBuf::new(TX_BUFFER_LEN)
),
// Before a transmission begin the id is incremented,
// so the first transimission will start at 0.
@ -206,10 +231,12 @@ impl Device {
}
// Configure receive buffer (RCR)
unsafe { self.ports.rx_config.write(RCR_RBLEN | RCR_WRAP | RCR_AB | RCR_AM | RCR_APM | RCR_AAP) }
let flags = RCR_RBLEN | RCR_WRAP | RCR_AB | RCR_AM | RCR_APM | RCR_AAP;
unsafe { self.ports.rx_config.write(flags) }
// Configure transmit buffer (TCR)
unsafe { self.ports.tx_config.write(TCR_IFG | TCR_MXDMA1 | TCR_MXDMA2); }
let flags = TCR_IFG | TCR_MXDMA1 | TCR_MXDMA2;
unsafe { self.ports.tx_config.write(flags) }
}
}
@ -233,27 +260,31 @@ impl EthernetDeviceIO for Device {
return None;
}
//let isr = unsafe { self.ports.isr.read() };
let cba = unsafe { self.ports.cba.read() };
// CAPR starts at 65520 and with the pad it overflows to 0
let capr = unsafe { self.ports.capr.read() };
let offset = ((capr as usize) + RX_BUFFER_PAD) % (1 << 16);
let header = u16::from_le_bytes(self.rx_buffer[(offset + 0)..(offset + 2)].try_into().unwrap());
let header = u16::from_le_bytes(
self.rx_buffer[(offset + 0)..(offset + 2)].try_into().unwrap(),
);
if header & ROK != ROK {
unsafe { self.ports.capr.write((((cba as usize) % RX_BUFFER_LEN) - RX_BUFFER_PAD) as u16) };
let capr = ((cba as usize) % RX_BUFFER_LEN) - RX_BUFFER_PAD;
unsafe { self.ports.capr.write(capr as u16) }
return None;
}
let n = u16::from_le_bytes(self.rx_buffer[(offset + 2)..(offset + 4)].try_into().unwrap()) as usize;
//let crc = u32::from_le_bytes(self.rx_buffer[(offset + n)..(offset + n + 4)].try_into().unwrap());
let n = u16::from_le_bytes(
self.rx_buffer[(offset + 2)..(offset + 4)].try_into().unwrap()
) as usize;
// Update buffer read pointer
self.rx_offset = (offset + n + 4 + 3) & !3;
unsafe {
self.ports.capr.write(((self.rx_offset % RX_BUFFER_LEN) - RX_BUFFER_PAD) as u16);
}
let capr = (self.rx_offset % RX_BUFFER_LEN) - RX_BUFFER_PAD;
unsafe { self.ports.capr.write(capr as u16) }
//unsafe { self.ports.isr.write(0x1); }
Some(self.rx_buffer[(offset + 4)..(offset + n)].to_vec())
}
@ -296,7 +327,8 @@ pub fn interrupt_handler() {
printk!("RTL8139 interrupt!\n");
if let Some(mut guard) = sys::net::IFACE.try_lock() {
if let Some(ref mut iface) = *guard {
unsafe { iface.device_mut().ports.isr.write(0xFFFF) } // Clear the interrupt
// Clear the interrupt
unsafe { iface.device_mut().ports.isr.write(0xFFFF) }
}
}
}

View File

@ -10,7 +10,9 @@ use smoltcp::time::Duration;
use spin::Mutex;
lazy_static! {
pub static ref SOCKETS: Mutex<SocketSet<'static>> = Mutex::new(SocketSet::new(vec![]));
pub static ref SOCKETS: Mutex<SocketSet<'static>> = {
Mutex::new(SocketSet::new(vec![]))
};
}
fn random_port() -> u16 {

View File

@ -78,23 +78,18 @@ impl TcpSocket {
}
connecting = true;
}
tcp::State::SynSent => {
}
tcp::State::SynSent => {}
tcp::State::Established => {
break;
}
_ => {
// Did something get sent before the connection closed?
return if socket.can_recv() {
Ok(())
} else {
Err(())
};
return if socket.can_recv() { Ok(()) } else { Err(()) };
}
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}
@ -112,8 +107,8 @@ impl TcpSocket {
return Err(());
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
Ok(())
@ -133,8 +128,8 @@ impl TcpSocket {
return Ok(endpoint.addr);
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}
@ -158,7 +153,8 @@ impl FileIO for TcpSocket {
iface.poll(sys::net::time(), device, &mut sockets);
let socket = sockets.get_mut::<tcp::Socket>(self.handle);
if buf.len() == 1 { // 1 byte status read
if buf.len() == 1 {
// 1 byte status read
buf[0] = tcp_socket_status(socket);
return Ok(1);
}
@ -170,8 +166,8 @@ impl FileIO for TcpSocket {
if !socket.may_recv() {
break;
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}
@ -204,8 +200,8 @@ impl FileIO for TcpSocket {
sent = true; // Break after next poll
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}
@ -229,8 +225,8 @@ impl FileIO for TcpSocket {
socket.close();
closed = true;
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}

View File

@ -42,13 +42,20 @@ impl UdpSocket {
pub fn new() -> Self {
let mut sockets = SOCKETS.lock();
let udp_rx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024]);
let udp_tx_buffer = udp::PacketBuffer::new(vec![udp::PacketMetadata::EMPTY], vec![0; 1024]);
let udp_rx_buffer = udp::PacketBuffer::new(
vec![udp::PacketMetadata::EMPTY], vec![0; 1024]
);
let udp_tx_buffer = udp::PacketBuffer::new(
vec![udp::PacketMetadata::EMPTY], vec![0; 1024]
);
let udp_socket = udp::Socket::new(udp_rx_buffer, udp_tx_buffer);
let handle = sockets.add(udp_socket);
let remote_endpoint = None;
Self { handle, remote_endpoint }
Self {
handle,
remote_endpoint,
}
}
pub fn connect(&mut self, addr: IpAddress, port: u16) -> Result<(), ()> {
@ -62,15 +69,15 @@ impl UdpSocket {
let mut sockets = SOCKETS.lock();
iface.poll(sys::net::time(), device, &mut sockets);
let socket = sockets.get_mut::<udp::Socket>(self.handle);
if !socket.is_open() {
let local_endpoint = IpListenEndpoint::from(random_port());
socket.bind(local_endpoint).unwrap();
break;
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}
@ -102,7 +109,8 @@ impl FileIO for UdpSocket {
iface.poll(sys::net::time(), device, &mut sockets);
let socket = sockets.get_mut::<udp::Socket>(self.handle);
if buf.len() == 1 { // 1 byte status read
if buf.len() == 1 {
// 1 byte status read
buf[0] = udp_socket_status(socket);
return Ok(1);
}
@ -111,8 +119,8 @@ impl FileIO for UdpSocket {
(bytes, _) = socket.recv_slice(buf).map_err(|_| ())?;
break;
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}
@ -139,8 +147,8 @@ impl FileIO for UdpSocket {
break;
}
if socket.can_send() {
if let Some(remote_endpoint) = self.remote_endpoint {
if socket.send_slice(buf.as_ref(), remote_endpoint).is_err() {
if let Some(endpoint) = self.remote_endpoint {
if socket.send_slice(buf.as_ref(), endpoint).is_err() {
return Err(());
}
} else {
@ -149,8 +157,8 @@ impl FileIO for UdpSocket {
sent = true; // Break after next poll
}
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}
@ -174,8 +182,8 @@ impl FileIO for UdpSocket {
socket.close();
closed = true;
if let Some(duration) = iface.poll_delay(sys::net::time(), &sockets) {
wait(duration);
if let Some(d) = iface.poll_delay(sys::net::time(), &sockets) {
wait(d);
}
sys::time::halt();
}

View File

@ -1,3 +1,4 @@
use alloc::vec;
use alloc::vec::Vec;
use bit_field::BitField;
use lazy_static::lazy_static;
@ -47,7 +48,9 @@ impl DeviceConfig {
let mut base_addresses: [u32; 6] = [0; 6];
for i in 0..6 {
let offset = 0x10 + ((i as u8) << 2);
let mut register = ConfigRegister::new(bus, device, function, offset);
let mut register = ConfigRegister::new(
bus, device, function, offset
);
base_addresses[i] = register.read();
}
@ -72,7 +75,9 @@ impl DeviceConfig {
}
pub fn enable_bus_mastering(&mut self) {
let mut register = ConfigRegister::new(self.bus, self.device, self.function, 0x04);
let mut register = ConfigRegister::new(
self.bus, self.device, self.function, 0x04
);
let mut data = register.read();
data.set_bit(2, true);
register.write(data);
@ -80,7 +85,7 @@ impl DeviceConfig {
}
lazy_static! {
pub static ref PCI_DEVICES: Mutex<Vec<DeviceConfig>> = Mutex::new(Vec::new());
pub static ref PCI_DEVICES: Mutex<Vec<DeviceConfig>> = Mutex::new(vec![]);
}
pub fn find_device(vendor_id: u16, device_id: u16) -> Option<DeviceConfig> {
@ -122,7 +127,14 @@ fn check_device(bus: u8, device: u8) {
fn add_device(bus: u8, device: u8, function: u8) {
let config = DeviceConfig::new(bus, device, function);
PCI_DEVICES.lock().push(config);
log!("PCI {:04X}:{:02X}:{:02X} [{:04X}:{:04X}]\n", bus, device, function, config.vendor_id, config.device_id);
log!(
"PCI {:04X}:{:02X}:{:02X} [{:04X}:{:04X}]\n",
bus,
device,
function,
config.vendor_id,
config.device_id
);
}
fn get_vendor_id(bus: u8, device: u8, function: u8) -> u16 {
@ -151,10 +163,11 @@ impl ConfigRegister {
Self {
data_port: Port::new(0xCFC),
addr_port: Port::new(0xCF8),
addr: 0x8000_0000 | ((bus as u32) << 16)
| ((device as u32) << 11)
| ((function as u32) << 8)
| ((offset as u32) & 0xFC),
addr: 0x8000_0000
| ((bus as u32) << 16)
| ((device as u32) << 11)
| ((function as u32) << 8)
| ((offset as u32) & 0xFC),
}
}
@ -182,22 +195,29 @@ pub fn init() {
for dev in devs.iter() {
// NOTE: There's not yet an AHCI driver for SATA disks so we must
// switch to IDE legacy mode for the ATA driver.
if dev.class == 0x01 && dev.subclass == 0x01 { // IDE Controller
let mut register = ConfigRegister::new(dev.bus, dev.device, dev.function, 0x08);
if dev.class == 0x01 && dev.subclass == 0x01 {
// IDE Controller
let mut register = ConfigRegister::new(
dev.bus, dev.device, dev.function, 0x08
);
let mut data = register.read();
let prog_offset = 8; // The programing interface start at bit 8
// Switching primary channel to compatibility mode
if dev.prog.get_bit(0) { // PCI native mode
if dev.prog.get_bit(1) { // Modifiable
if dev.prog.get_bit(0) {
// PCI native mode
if dev.prog.get_bit(1) {
// Modifiable
data.set_bit(prog_offset, false);
register.write(data);
}
}
// Switching secondary channel to compatibility mode
if dev.prog.get_bit(2) { // PCI native mode
if dev.prog.get_bit(3) { // Modifiable
if dev.prog.get_bit(2) {
// PCI native mode
if dev.prog.get_bit(3) {
// Modifiable
data.set_bit(prog_offset + 2, false);
register.write(data);
}

View File

@ -4,7 +4,9 @@ use spin::Mutex;
pub const PIC_1_OFFSET: u8 = 32;
pub const PIC_2_OFFSET: u8 = PIC_1_OFFSET + 8;
pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe { ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET) });
pub static PICS: Mutex<ChainedPics> = Mutex::new(unsafe {
ChainedPics::new(PIC_1_OFFSET, PIC_2_OFFSET)
});
pub fn init() {
unsafe {

View File

@ -1,6 +1,8 @@
use crate::api::process::ExitCode;
use crate::sys::fs::{Resource, Device};
use crate::sys::console::Console;
use crate::sys::fs::{Device, Resource};
use crate::sys;
use crate::sys::gdt::GDT;
use alloc::boxed::Box;
use alloc::collections::btree_map::BTreeMap;
@ -9,24 +11,53 @@ use alloc::sync::Arc;
use alloc::vec::Vec;
use core::alloc::{GlobalAlloc, Layout};
use core::arch::asm;
use core::sync::atomic::{AtomicUsize, Ordering};
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use lazy_static::lazy_static;
use linked_list_allocator::LockedHeap;
use object::{Object, ObjectSegment};
use spin::RwLock;
use x86_64::registers::control::Cr3;
use x86_64::structures::idt::InterruptStackFrameValue;
use x86_64::structures::paging::{FrameAllocator, OffsetPageTable, PageTable, PhysFrame};
use x86_64::structures::paging::{
FrameAllocator, OffsetPageTable, PageTable, PhysFrame
};
use x86_64::VirtAddr;
const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
const BIN_MAGIC: [u8; 4] = [0x7F, b'B', b'I', b'N'];
const MAX_HANDLES: usize = 64;
const MAX_PROCS: usize = 4; // TODO: Update this when more than one process can run at once
const MAX_PROCS: usize = 4; // TODO: Increase this
const MAX_PROC_SIZE: usize = 10 << 20; // 10 MB
static CODE_ADDR: AtomicU64 = AtomicU64::new(0);
pub static PID: AtomicUsize = AtomicUsize::new(0);
pub static MAX_PID: AtomicUsize = AtomicUsize::new(1);
lazy_static! {
pub static ref PROCESS_TABLE: RwLock<[Box<Process>; MAX_PROCS]> = RwLock::new([(); MAX_PROCS].map(|_| Box::new(Process::new())));
pub static ref PROCESS_TABLE: RwLock<[Box<Process>; MAX_PROCS]> = {
RwLock::new([(); MAX_PROCS].map(|_| Box::new(Process::new())))
};
}
// Called during kernel heap initialization
pub fn init_process_addr(addr: u64) {
sys::process::CODE_ADDR.store(addr, Ordering::SeqCst);
}
#[repr(align(8), C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct Registers {
// Saved scratch registers
pub r11: usize,
pub r10: usize,
pub r9: usize,
pub r8: usize,
pub rdi: usize,
pub rsi: usize,
pub rdx: usize,
pub rcx: usize,
pub rax: usize,
}
#[derive(Clone, Debug)]
@ -42,11 +73,17 @@ impl ProcessData {
let env = BTreeMap::new();
let dir = dir.to_string();
let user = user.map(String::from);
let mut handles = [(); MAX_HANDLES].map(|_| None);
handles[0] = Some(Box::new(Resource::Device(Device::Console(Console::new())))); // stdin
handles[1] = Some(Box::new(Resource::Device(Device::Console(Console::new())))); // stdout
handles[2] = Some(Box::new(Resource::Device(Device::Console(Console::new())))); // stderr
handles[3] = Some(Box::new(Resource::Device(Device::Null))); // stdnull
let stdin = Resource::Device(Device::Console(Console::new()));
let stdout = Resource::Device(Device::Console(Console::new()));
let stderr = Resource::Device(Device::Console(Console::new()));
let stdnull = Resource::Device(Device::Null);
handles[0] = Some(Box::new(stdin));
handles[1] = Some(Box::new(stdout));
handles[2] = Some(Box::new(stderr));
handles[3] = Some(Box::new(stdnull));
Self { env, dir, user, handles }
}
}
@ -189,9 +226,15 @@ pub fn exit() {
let table = PROCESS_TABLE.read();
let proc = &table[id()];
let page_table = unsafe { sys::mem::create_page_table(proc.page_table_frame) };
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
let page_table = unsafe {
sys::mem::create_page_table(proc.page_table_frame)
};
let phys_mem_offset = unsafe {
sys::mem::PHYS_MEM_OFFSET.unwrap()
};
let mut mapper = unsafe {
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
};
sys::allocator::free_pages(&mut mapper, proc.code_addr, MAX_PROC_SIZE);
MAX_PID.fetch_sub(1, Ordering::SeqCst);
@ -235,42 +278,6 @@ pub unsafe fn free(ptr: *mut u8, _layout: Layout) {
}
}
/************************
* Userspace experiment *
************************/
// See https://nfil.dev/kernel/rust/coding/rust-kernel-to-userspace-and-back/
// And https://github.com/WartaPoirier-corp/ananos/blob/dev/docs/notes/context-switch.md
use crate::sys;
use crate::sys::gdt::GDT;
use core::sync::atomic::AtomicU64;
use x86_64::VirtAddr;
static CODE_ADDR: AtomicU64 = AtomicU64::new(0);
// Called during kernel heap initialization
pub fn init_process_addr(addr: u64) {
sys::process::CODE_ADDR.store(addr, Ordering::SeqCst);
}
#[repr(align(8), C)]
#[derive(Debug, Clone, Copy, Default)]
pub struct Registers { // Saved scratch registers
pub r11: usize,
pub r10: usize,
pub r9: usize,
pub r8: usize,
pub rdi: usize,
pub rsi: usize,
pub rdx: usize,
pub rcx: usize,
pub rax: usize,
}
const ELF_MAGIC: [u8; 4] = [0x7F, b'E', b'L', b'F'];
const BIN_MAGIC: [u8; 4] = [0x7F, b'B', b'I', b'N'];
#[derive(Clone)]
pub struct Process {
id: usize,
@ -308,7 +315,11 @@ impl Process {
}
}
pub fn spawn(bin: &[u8], args_ptr: usize, args_len: usize) -> Result<(), ExitCode> {
pub fn spawn(
bin: &[u8],
args_ptr: usize,
args_len: usize
) -> Result<(), ExitCode> {
if let Ok(id) = Self::create(bin) {
let proc = {
let table = PROCESS_TABLE.read();
@ -326,44 +337,59 @@ impl Process {
return Err(());
}
let page_table_frame = sys::mem::frame_allocator().allocate_frame().expect("frame allocation failed");
let page_table = unsafe { sys::mem::create_page_table(page_table_frame) };
let kernel_page_table = unsafe { sys::mem::active_page_table() };
let page_table_frame = sys::mem::frame_allocator().allocate_frame().
expect("frame allocation failed");
let page_table = unsafe {
sys::mem::create_page_table(page_table_frame)
};
let kernel_page_table = unsafe {
sys::mem::active_page_table()
};
// FIXME: for now we just copy everything
for (user_page, kernel_page) in page_table.iter_mut().zip(kernel_page_table.iter()) {
let pages = page_table.iter_mut().zip(kernel_page_table.iter());
for (user_page, kernel_page) in pages {
*user_page = kernel_page.clone();
}
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
let mut mapper = unsafe {
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
};
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 - 4096;
//debug!("code_addr: {:#X}", code_addr);
//debug!("stack_addr: {:#X}", stack_addr);
let mut entry_point_addr = 0;
let code_ptr = code_addr as *mut u8;
let code_size = bin.len();
sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).expect("proc mem alloc");
sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).
expect("proc mem alloc");
if bin[0..4] == ELF_MAGIC { // ELF binary
if let Ok(obj) = object::File::parse(bin) {
entry_point_addr = obj.entry();
sys::allocator::alloc_pages(&mut mapper, code_addr + entry_point_addr, code_size).expect("proc mem alloc");
let addr = code_addr + entry_point_addr;
sys::allocator::alloc_pages(&mut mapper, addr, code_size).
expect("proc mem alloc");
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) };
unsafe {
core::ptr::write(code_ptr.add(addr + i), *b)
};
}
}
}
}
} else if bin[0..4] == BIN_MAGIC { // Flat binary
//sys::allocator::alloc_pages(&mut mapper, code_addr, code_size).expect("proc mem alloc");
for (i, b) in bin.iter().skip(4).enumerate() {
unsafe { core::ptr::write(code_ptr.add(i), *b) };
}
@ -385,7 +411,16 @@ impl Process {
let id = MAX_PID.fetch_add(1, Ordering::SeqCst);
let parent_id = parent.id;
let proc = Process {
id, parent_id, code_addr, stack_addr, entry_point_addr, page_table_frame, data, stack_frame, registers, allocator
id,
parent_id,
code_addr,
stack_addr,
entry_point_addr,
page_table_frame,
data,
stack_frame,
registers,
allocator,
};
let mut process_table = PROCESS_TABLE.write();
@ -398,14 +433,19 @@ impl Process {
fn exec(&self, args_ptr: usize, args_len: usize) {
let page_table = unsafe { sys::process::page_table() };
let phys_mem_offset = unsafe { sys::mem::PHYS_MEM_OFFSET.unwrap() };
let mut mapper = unsafe { OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset)) };
let mut mapper = unsafe {
OffsetPageTable::new(page_table, VirtAddr::new(phys_mem_offset))
};
let heap_addr = self.code_addr + (self.stack_addr - self.code_addr) / 2;
//debug!("user-args: {:#016X}", heap_addr);
sys::allocator::alloc_pages(&mut mapper, heap_addr, 1).expect("proc heap alloc");
sys::allocator::alloc_pages(&mut mapper, 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) };
let args: &[&str] = unsafe {
core::slice::from_raw_parts(args_ptr as *const &str, args_len)
};
let mut addr = heap_addr;
let vec: Vec<&str> = args.iter().map(|arg| {
let ptr = addr as *mut u8;
@ -428,9 +468,10 @@ impl Process {
let args_ptr = args.as_ptr() as u64;
let heap_addr = addr;
let heap_size = (self.stack_addr - heap_addr) / 2;
//debug!("user-heap: {:#016X}..{:#016X}", heap_addr, heap_addr + heap_size);
unsafe { self.allocator.lock().init(heap_addr as *mut u8, heap_size as usize) };
let heap_size = ((self.stack_addr - heap_addr) / 2) as usize;
unsafe {
self.allocator.lock().init(heap_addr as *mut u8, heap_size);
}
set_id(self.id); // Change PID

View File

@ -1,5 +1,5 @@
use crate::sys;
use crate::api::fs::{FileIO, IO};
use crate::sys;
use rand::{RngCore, SeedableRng};
use rand_hc::Hc128Rng;
@ -27,8 +27,7 @@ impl FileIO for Random {
unimplemented!();
}
fn close(&mut self) {
}
fn close(&mut self) {}
fn poll(&mut self, event: IO) -> bool {
match event {

View File

@ -14,13 +14,13 @@ lazy_static! {
}
pub struct Serial {
port: SerialPort
port: SerialPort,
}
impl Serial {
fn new(addr: u16) -> Self {
Self {
port: unsafe { SerialPort::new(addr) }
port: unsafe { SerialPort::new(addr) },
}
}
@ -59,7 +59,7 @@ impl Perform for Serial {
_ => return,
}
}
},
}
'l' => { // Disable
for param in params.iter() {
match param[0] {
@ -67,17 +67,17 @@ impl Perform for Serial {
_ => return,
}
}
},
_ => {},
}
_ => {}
}
}
}
#[doc(hidden)]
pub fn print_fmt(args: fmt::Arguments) {
interrupts::without_interrupts(|| {
SERIAL.lock().write_fmt(args).expect("Could not print to serial");
})
interrupts::without_interrupts(||
SERIAL.lock().write_fmt(args).expect("Could not print to serial")
)
}
pub fn init() {

View File

@ -3,21 +3,28 @@ pub mod service;
use crate::api::process::ExitCode;
use crate::sys;
use crate::sys::fs::{FileInfo, IO};
use crate::sys::fs::FileInfo;
use core::arch::asm;
use smoltcp::wire::IpAddress;
use smoltcp::wire::Ipv4Address;
/*
* Dispatching system calls
*/
fn utf8_from_raw_parts(ptr: *mut u8, len: usize) -> &'static str {
unsafe {
let slice = core::slice::from_raw_parts(ptr, len);
core::str::from_utf8_unchecked(slice)
}
}
pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize {
pub fn dispatcher(
n: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize
) -> usize {
match n {
number::EXIT => {
service::exit(ExitCode::from(arg1)) as usize
}
number::EXIT => service::exit(ExitCode::from(arg1)) as usize,
number::SLEEP => {
service::sleep(f64::from_bits(arg1 as u64));
0
@ -25,35 +32,39 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
number::DELETE => {
let ptr = sys::process::ptr_from_addr(arg1 as u64);
let len = arg2;
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) };
let path = utf8_from_raw_parts(ptr, len);
service::delete(path) as usize
}
number::INFO => {
let ptr = sys::process::ptr_from_addr(arg1 as u64);
let len = arg2;
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) };
let path = utf8_from_raw_parts(ptr, len);
let info = unsafe { &mut *(arg3 as *mut FileInfo) };
service::info(path, info) as usize
}
number::OPEN => {
let ptr = sys::process::ptr_from_addr(arg1 as u64);
let len = arg2;
let path = utf8_from_raw_parts(ptr, len);
let flags = arg3;
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(ptr, len)) };
service::open(path, flags) as usize
}
number::READ => {
let handle = arg1;
let ptr = sys::process::ptr_from_addr(arg2 as u64);
let len = arg3;
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
let buf = unsafe {
core::slice::from_raw_parts_mut(ptr, len)
};
service::read(handle, buf) as usize
}
number::WRITE => {
let handle = arg1;
let ptr = sys::process::ptr_from_addr(arg2 as u64);
let len = arg3;
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) }; // TODO: Remove mut
let buf = unsafe {
core::slice::from_raw_parts_mut(ptr, len) // TODO: Remove mut
};
service::write(handle, buf) as usize
}
number::CLOSE => {
@ -69,7 +80,7 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
number::SPAWN => {
let path_ptr = sys::process::ptr_from_addr(arg1 as u64);
let path_len = arg2;
let path = unsafe { core::str::from_utf8_unchecked(core::slice::from_raw_parts(path_ptr, path_len)) };
let path = utf8_from_raw_parts(path_ptr, path_len);
let args_ptr = arg3;
let args_len = arg4;
service::spawn(path, args_ptr, args_len) as usize
@ -79,16 +90,16 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
service::stop(code)
}
number::POLL => {
let ptr = sys::process::ptr_from_addr(arg1 as u64) as *const (usize, IO);
let ptr = sys::process::ptr_from_addr(arg1 as u64) as *const _;
let len = arg2;
let list = unsafe { core::slice::from_raw_parts(ptr, len) };
service::poll(list) as usize
}
number::CONNECT => {
let handle = arg1;
let buf_ptr = sys::process::ptr_from_addr(arg2 as u64);
let buf_len = arg3;
let buf = unsafe { core::slice::from_raw_parts(buf_ptr, buf_len) };
let ptr = sys::process::ptr_from_addr(arg2 as u64);
let len = arg3;
let buf = unsafe { core::slice::from_raw_parts(ptr, len) };
let addr = IpAddress::from(Ipv4Address::from_bytes(buf));
let port = arg4 as u16;
service::connect(handle, addr, port) as usize
@ -100,11 +111,11 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
}
number::ACCEPT => {
let handle = arg1;
let buf_ptr = sys::process::ptr_from_addr(arg2 as u64);
let buf_len = arg3;
let buf = unsafe { core::slice::from_raw_parts_mut(buf_ptr, buf_len) };
let ptr = sys::process::ptr_from_addr(arg2 as u64);
let len = arg3;
let buf = unsafe { core::slice::from_raw_parts_mut(ptr, len) };
if let Ok(addr) = service::accept(handle) {
buf[0..buf_len].clone_from_slice(addr.as_bytes());
buf[0..len].clone_from_slice(addr.as_bytes());
0
} else {
-1 as isize as usize
@ -128,10 +139,6 @@ pub fn dispatcher(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize)
}
}
/*
* Sending system calls
*/
#[doc(hidden)]
pub unsafe fn syscall0(n: usize) -> usize {
let res: usize;
@ -165,7 +172,12 @@ pub unsafe fn syscall2(n: usize, arg1: usize, arg2: usize) -> usize {
}
#[doc(hidden)]
pub unsafe fn syscall3(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize {
pub unsafe fn syscall3(
n: usize,
arg1: usize,
arg2: usize,
arg3: usize
) -> usize {
let res: usize;
asm!(
"int 0x80", in("rax") n,
@ -176,7 +188,13 @@ pub unsafe fn syscall3(n: usize, arg1: usize, arg2: usize, arg3: usize) -> usize
}
#[doc(hidden)]
pub unsafe fn syscall4(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: usize) -> usize {
pub unsafe fn syscall4(
n: usize,
arg1: usize,
arg2: usize,
arg3: usize,
arg4: usize
) -> usize {
let res: usize;
asm!(
"int 0x80", in("rax") n,
@ -188,19 +206,30 @@ pub unsafe fn syscall4(n: usize, arg1: usize, arg2: usize, arg3: usize, arg4: us
#[macro_export]
macro_rules! syscall {
($n:expr) => (
$crate::sys::syscall::syscall0(
$n as usize));
($n:expr, $a1:expr) => (
$crate::sys::syscall::syscall1(
$n as usize, $a1 as usize));
($n:expr, $a1:expr, $a2:expr) => (
$crate::sys::syscall::syscall2(
$n as usize, $a1 as usize, $a2 as usize));
($n:expr, $a1:expr, $a2:expr, $a3:expr) => (
($n:expr) => {
$crate::sys::syscall::syscall0($n as usize)
};
($n:expr, $a1:expr) => {
$crate::sys::syscall::syscall1($n as usize, $a1 as usize)
};
($n:expr, $a1:expr, $a2:expr) => {
$crate::sys::syscall::syscall2($n as usize, $a1 as usize, $a2 as usize)
};
($n:expr, $a1:expr, $a2:expr, $a3:expr) => {
$crate::sys::syscall::syscall3(
$n as usize, $a1 as usize, $a2 as usize, $a3 as usize));
($n:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => (
$n as usize,
$a1 as usize,
$a2 as usize,
$a3 as usize,
)
};
($n:expr, $a1:expr, $a2:expr, $a3:expr, $a4:expr) => {
$crate::sys::syscall::syscall4(
$n as usize, $a1 as usize, $a2 as usize, $a3 as usize, $a4 as usize));
$n as usize,
$a1 as usize,
$a2 as usize,
$a3 as usize,
$a4 as usize,
)
};
}

View File

@ -1,17 +1,17 @@
pub const EXIT: usize = 0x1;
pub const SPAWN: usize = 0x2;
pub const READ: usize = 0x3;
pub const WRITE: usize = 0x4;
pub const OPEN: usize = 0x5;
pub const CLOSE: usize = 0x6;
pub const INFO: usize = 0x7;
pub const DUP: usize = 0x8;
pub const DELETE: usize = 0x9;
pub const STOP: usize = 0xA;
pub const SLEEP: usize = 0xB;
pub const POLL: usize = 0xC;
pub const CONNECT: usize = 0xD;
pub const LISTEN: usize = 0xE;
pub const ACCEPT: usize = 0xF;
pub const ALLOC: usize = 0x10;
pub const FREE: usize = 0x11;
pub const EXIT: usize = 0x1;
pub const SPAWN: usize = 0x2;
pub const READ: usize = 0x3;
pub const WRITE: usize = 0x4;
pub const OPEN: usize = 0x5;
pub const CLOSE: usize = 0x6;
pub const INFO: usize = 0x7;
pub const DUP: usize = 0x8;
pub const DELETE: usize = 0x9;
pub const STOP: usize = 0xA;
pub const SLEEP: usize = 0xB;
pub const POLL: usize = 0xC;
pub const CONNECT: usize = 0xD;
pub const LISTEN: usize = 0xE;
pub const ACCEPT: usize = 0xF;
pub const ALLOC: usize = 0x10;
pub const FREE: usize = 0x11;

View File

@ -1,17 +1,17 @@
use crate::sys;
use crate::api::process::ExitCode;
use crate::api::fs::{FileIO, IO};
use crate::api::process::ExitCode;
use crate::sys;
use crate::sys::fs::Device;
use crate::sys::fs::FileInfo;
use crate::sys::fs::Resource;
use crate::sys::fs::Device;
use crate::sys::process::Process;
use alloc::vec;
use core::alloc::Layout;
use core::arch::asm;
use smoltcp::wire::IpAddress;
pub fn exit(code: ExitCode) -> ExitCode {
//debug!("syscall::exit(code={})", code as usize);
sys::process::exit();
code
}
@ -90,7 +90,6 @@ pub fn close(handle: usize) {
}
pub fn spawn(path: &str, args_ptr: usize, args_len: usize) -> ExitCode {
//debug!("syscall::spawn(path={}, args_ptr={:#X}, args_len={})", path, args_ptr, args_len);
let path = match sys::fs::canonicalize(path) {
Ok(path) => path,
Err(_) => return ExitCode::OpenError,
@ -116,10 +115,7 @@ pub fn stop(code: usize) -> usize {
match code {
0xCAFE => { // Reboot
unsafe {
asm!(
"xor rax, rax",
"mov cr3, rax"
);
asm!("xor rax, rax", "mov cr3, rax");
}
}
0xDEAD => { // Halt
@ -147,8 +143,12 @@ pub fn poll(list: &[(usize, IO)]) -> isize {
pub fn connect(handle: usize, addr: IpAddress, port: u16) -> isize {
if let Some(mut file) = sys::process::handle(handle) {
let res = match *file {
Resource::Device(Device::TcpSocket(ref mut dev)) => dev.connect(addr, port),
Resource::Device(Device::UdpSocket(ref mut dev)) => dev.connect(addr, port),
Resource::Device(Device::TcpSocket(ref mut dev)) => {
dev.connect(addr, port)
}
Resource::Device(Device::UdpSocket(ref mut dev)) => {
dev.connect(addr, port)
}
_ => Err(()),
};
if res.is_ok() {
@ -184,13 +184,9 @@ pub fn accept(handle: usize) -> Result<IpAddress, ()> {
Err(())
}
use core::alloc::Layout;
pub fn alloc(size: usize, align: usize) -> *mut u8 {
if let Ok(layout) = Layout::from_size_align(size, align) {
let ptr = unsafe { sys::process::alloc(layout) };
//debug!("syscall::alloc(size={}, align={}) -> ptr={:?}", size, align, ptr);
ptr
unsafe { sys::process::alloc(layout) }
} else {
core::ptr::null_mut()
}
@ -198,7 +194,6 @@ pub fn alloc(size: usize, align: usize) -> *mut u8 {
pub fn free(ptr: *mut u8, size: usize, align: usize) {
if let Ok(layout) = Layout::from_size_align(size, align) {
//debug!("syscall::free(ptr={:?}, size={}, align={})", ptr, size, align);
unsafe { sys::process::free(ptr, layout) };
}
}

View File

@ -1,7 +1,7 @@
use crate::sys;
use crate::sys::cmos::CMOS;
use core::hint::spin_loop;
use core::sync::atomic::{AtomicUsize, AtomicU64, Ordering};
use core::sync::atomic::{AtomicU64, AtomicUsize, Ordering};
use x86_64::instructions::interrupts;
use x86_64::instructions::port::Port;

View File

@ -1,6 +1,6 @@
use crate::api::font::Font;
use crate::api::vga::{Color, Palette};
use crate::api::vga::color;
use crate::api::vga::{Color, Palette};
use crate::sys;
use alloc::string::String;
@ -14,18 +14,15 @@ use vte::{Params, Parser, Perform};
use x86_64::instructions::interrupts;
use x86_64::instructions::port::Port;
// See https://web.stanford.edu/class/cs140/projects/pintos/specs/freevga/vga/vga.htm
// And https://01.org/sites/default/files/documentation/snb_ihd_os_vol3_part1_0.pdf
const ATTR_ADDR_DATA_REG: u16 = 0x3C0;
const ATTR_DATA_READ_REG: u16 = 0x3C1;
const SEQUENCER_ADDR_REG: u16 = 0x3C4;
const ATTR_ADDR_DATA_REG: u16 = 0x3C0;
const ATTR_DATA_READ_REG: u16 = 0x3C1;
const SEQUENCER_ADDR_REG: u16 = 0x3C4;
const DAC_ADDR_WRITE_MODE_REG: u16 = 0x3C8;
const DAC_DATA_REG: u16 = 0x3C9;
const GRAPHICS_ADDR_REG: u16 = 0x3CE;
const CRTC_ADDR_REG: u16 = 0x3D4;
const CRTC_DATA_REG: u16 = 0x3D5;
const INPUT_STATUS_REG: u16 = 0x3DA;
const DAC_DATA_REG: u16 = 0x3C9;
const GRAPHICS_ADDR_REG: u16 = 0x3CE;
const CRTC_ADDR_REG: u16 = 0x3D4;
const CRTC_DATA_REG: u16 = 0x3D5;
const INPUT_STATUS_REG: u16 = 0x3DA;
const FG: Color = Color::LightGray;
const BG: Color = Color::Black;
@ -139,12 +136,14 @@ impl Writer {
fn write_byte(&mut self, byte: u8) {
match byte {
0x0A => { // Newline
0x0A => {
// Newline
self.new_line();
},
}
0x0D => { // Carriage Return
},
0x08 => { // Backspace
}
0x08 => {
// Backspace
if self.writer[0] > 0 {
self.writer[0] -= 1;
let c = ScreenChar {
@ -153,11 +152,10 @@ impl Writer {
};
let x = self.writer[0];
let y = self.writer[1];
unsafe {
core::ptr::write_volatile(&mut self.buffer.chars[y][x], c);
}
let ptr = &mut self.buffer.chars[y][x];
unsafe { core::ptr::write_volatile(ptr, c); }
}
},
}
byte => {
if self.writer[0] >= BUFFER_WIDTH {
self.new_line();
@ -165,12 +163,18 @@ impl Writer {
let x = self.writer[0];
let y = self.writer[1];
let ascii_code = if is_printable(byte) { byte } else { UNPRINTABLE };
let ascii_code = if is_printable(byte) {
byte
} else {
UNPRINTABLE
};
let color_code = self.color_code;
let c = ScreenChar { ascii_code, color_code };
unsafe {
core::ptr::write_volatile(&mut self.buffer.chars[y][x], c);
}
let c = ScreenChar {
ascii_code,
color_code,
};
let ptr = &mut self.buffer.chars[y][x];
unsafe { core::ptr::write_volatile(ptr, c); }
self.writer[0] += 1;
}
}
@ -232,7 +236,8 @@ impl Writer {
for j in 0..font.height as usize {
let vga_offset = j + i * 32 as usize;
let fnt_offset = j + i * font.height as usize;
buffer.add(vga_offset).write_volatile(font.data[fnt_offset]);
let ptr = buffer.add(vga_offset);
ptr.write_volatile(font.data[fnt_offset]);
}
}
@ -286,18 +291,18 @@ impl Perform for Writer {
0 => {
fg = FG;
bg = BG;
},
}
30..=37 | 90..=97 => {
fg = color::from_ansi(param[0] as u8);
},
}
40..=47 | 100..=107 => {
bg = color::from_ansi((param[0] as u8) - 10);
},
_ => {},
}
_ => {}
}
}
self.set_color(fg, bg);
},
}
'A' => { // Cursor Up
let mut n = 1;
for param in params.iter() {
@ -305,23 +310,25 @@ impl Perform for Writer {
}
self.writer[1] = self.writer[1].saturating_sub(n);
self.cursor[1] = self.cursor[1].saturating_sub(n);
},
}
'B' => { // Cursor Down
let mut n = 1;
for param in params.iter() {
n = param[0] as usize;
}
self.writer[1] = cmp::min(self.writer[1] + n, BUFFER_HEIGHT - 1);
self.cursor[1] = cmp::min(self.cursor[1] + n, BUFFER_HEIGHT - 1);
},
let height = BUFFER_HEIGHT - 1;
self.writer[1] = cmp::min(self.writer[1] + n, height);
self.cursor[1] = cmp::min(self.cursor[1] + n, height);
}
'C' => { // Cursor Forward
let mut n = 1;
for param in params.iter() {
n = param[0] as usize;
}
self.writer[0] = cmp::min(self.writer[0] + n, BUFFER_WIDTH - 1);
self.cursor[0] = cmp::min(self.cursor[0] + n, BUFFER_WIDTH - 1);
},
let width = BUFFER_WIDTH - 1;
self.writer[0] = cmp::min(self.writer[0] + n, width);
self.cursor[0] = cmp::min(self.cursor[0] + n, width);
}
'D' => { // Cursor Backward
let mut n = 1;
for param in params.iter() {
@ -329,7 +336,7 @@ impl Perform for Writer {
}
self.writer[0] = self.writer[0].saturating_sub(n);
self.cursor[0] = self.cursor[0].saturating_sub(n);
},
}
'G' => { // Cursor Horizontal Absolute
let (_, y) = self.cursor_position();
let mut x = 1;
@ -341,7 +348,7 @@ impl Perform for Writer {
}
self.set_writer_position(x - 1, y);
self.set_cursor_position(x - 1, y);
},
}
'H' => { // Move cursor
let mut x = 1;
let mut y = 1;
@ -357,20 +364,20 @@ impl Perform for Writer {
}
self.set_writer_position(x - 1, y - 1);
self.set_cursor_position(x - 1, y - 1);
},
}
'J' => { // Erase in Display
let mut n = 0;
for param in params.iter() {
n = param[0] as usize;
}
match n {
// TODO: 0 and 1, from cursor to begining or to end of screen
// TODO: 0 and 1, cursor to begining or to end of screen
2 => self.clear_screen(),
_ => return,
}
self.set_writer_position(0, 0);
self.set_cursor_position(0, 0);
},
}
'K' => { // Erase in Line
let (x, y) = self.cursor_position();
let mut n = 0;
@ -385,7 +392,7 @@ impl Perform for Writer {
}
self.set_writer_position(x, y);
self.set_cursor_position(x, y);
},
}
'h' => { // Enable
for param in params.iter() {
match param[0] {
@ -394,7 +401,7 @@ impl Perform for Writer {
_ => return,
}
}
},
}
'l' => { // Disable
for param in params.iter() {
match param[0] {
@ -403,8 +410,8 @@ impl Perform for Writer {
_ => return,
}
}
},
_ => {},
}
_ => {}
}
}
@ -445,9 +452,9 @@ impl fmt::Write for Writer {
#[doc(hidden)]
pub fn print_fmt(args: fmt::Arguments) {
interrupts::without_interrupts(|| {
WRITER.lock().write_fmt(args).expect("Could not print to VGA");
});
interrupts::without_interrupts(||
WRITER.lock().write_fmt(args).expect("Could not print to VGA")
)
}
pub fn cols() -> usize {
@ -459,15 +466,15 @@ pub fn rows() -> usize {
}
pub fn color() -> (Color, Color) {
interrupts::without_interrupts(|| {
interrupts::without_interrupts(||
WRITER.lock().color()
})
)
}
pub fn set_color(foreground: Color, background: Color) {
interrupts::without_interrupts(|| {
interrupts::without_interrupts(||
WRITER.lock().set_color(foreground, background)
})
)
}
// ASCII Printable
@ -480,18 +487,18 @@ pub fn is_printable(c: u8) -> bool {
}
pub fn set_font(font: &Font) {
interrupts::without_interrupts(|| {
WRITER.lock().set_font(font);
})
interrupts::without_interrupts(||
WRITER.lock().set_font(font)
)
}
// TODO: Remove this
pub fn set_palette(palette: Palette) {
interrupts::without_interrupts(|| {
interrupts::without_interrupts(||
for (i, (r, g, b)) in palette.colors.iter().enumerate() {
WRITER.lock().set_palette(i, *r, *g, *b);
WRITER.lock().set_palette(i, *r, *g, *b)
}
})
)
}
// 0x00 -> top

View File

@ -17,7 +17,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
pub fn encode(s: &[u8]) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.resize(s.len() * 4 / 3 + 4, 0); // Resize to base64 + padding
let bytes_written = base64::encode_config_slice(s, base64::STANDARD_NO_PAD, &mut buf);
let bytes_written = base64::encode_config_slice(
s, base64::STANDARD_NO_PAD, &mut buf
);
buf.resize(bytes_written, 0); // Resize back to actual size
buf
}
@ -25,7 +27,9 @@ pub fn encode(s: &[u8]) -> Vec<u8> {
pub fn decode(s: &[u8]) -> Vec<u8> {
let mut buf: Vec<u8> = Vec::new();
buf.resize(s.len(), 0);
let bytes_written = base64::decode_config_slice(s, base64::STANDARD_NO_PAD, &mut buf).unwrap();
let bytes_written = base64::decode_config_slice(
s, base64::STANDARD_NO_PAD, &mut buf
).unwrap();
buf.resize(bytes_written, 0);
buf
}

View File

@ -1,6 +1,6 @@
use crate::{api, sys};
use crate::api::process::ExitCode;
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::{api, sys};
use x86_64::instructions::port::Port;
@ -55,7 +55,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
error!("Missing freq");
return Err(ExitCode::UsageError);
}
},
}
"-l" | "--len" => {
if i + 1 < n {
if let Ok(value) = args[i + 1].parse() {
@ -69,8 +69,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
error!("Missing len");
return Err(ExitCode::UsageError);
}
},
_ => {},
}
_ => {}
}
i += 1;
}
@ -83,10 +83,19 @@ fn help() -> Result<(), ExitCode> {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} beep {}<options>{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} beep {}<options>{1}",
csi_title, csi_reset, csi_option
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-f{1}, {0}--freq <hertz>{1} Tone frequency", csi_option, csi_reset);
println!(" {0}-l{1}, {0}--len <milliseconds>{1} Tone length", csi_option, csi_reset);
println!(
" {0}-f{1}, {0}--freq <hertz>{1} Tone frequency",
csi_option, csi_reset
);
println!(
" {0}-l{1}, {0}--len <milliseconds>{1} Tone length",
csi_option, csi_reset
);
Ok(())
}

View File

@ -1,6 +1,6 @@
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::api::prompt::Prompt;
use crate::api::console::Style;
use alloc::boxed::Box;
use alloc::format;
@ -9,9 +9,9 @@ use alloc::vec::Vec;
use nom::branch::alt;
use nom::character::complete::{char, space0};
use nom::number::complete::double;
use nom::combinator::map;
use nom::multi::many0;
use nom::number::complete::double;
use nom::sequence::{delimited, tuple};
use nom::IResult;
@ -34,13 +34,18 @@ pub enum Exp {
fn parse(input: &str) -> IResult<&str, Exp> {
let (input, num1) = parse_term(input)?;
let (input, exps) = many0(tuple((alt((char('+'), char('-'))), parse_term)))(input)?;
let (input, exps) = many0(
tuple((alt((char('+'), char('-'))), parse_term))
)(input)?;
Ok((input, parse_exp(num1, exps)))
}
fn parse_term(input: &str) -> IResult<&str, Exp> {
let (input, num1) = parse_factor(input)?;
let (input, exps) = many0(tuple((alt((char('%'), char('/'), char('*'))), parse_factor)))(input)?;
let (input, exps) = many0(tuple((
alt((char('%'), char('/'), char('*'))),
parse_factor,
)))(input)?;
Ok((input, parse_exp(num1, exps)))
}
@ -99,10 +104,8 @@ fn parse_eval(line: &str) -> Result<f64, String> {
} else {
Err(format!("Could not parse '{}'", line))
}
},
Err(_) => {
Err(format!("Could not parse '{}'", line))
},
}
Err(_) => Err(format!("Could not parse '{}'", line)),
}
}
@ -145,7 +148,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
for &arg in args {
match arg {
"-h" | "--help" => return help(),
_ => {},
_ => {}
}
}
if args.len() == 1 {
@ -168,7 +171,10 @@ pub fn help() -> Result<(), ExitCode> {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} calc {}[<exp>]{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} calc {}[<exp>]{}",
csi_title, csi_reset, csi_option, csi_reset
);
Ok(())
}
@ -180,28 +186,28 @@ fn test_calc() {
};
}
assert_eq!(eval!("1"), "1");
assert_eq!(eval!("1.5"), "1.5");
assert_eq!(eval!("1"), "1");
assert_eq!(eval!("1.5"), "1.5");
assert_eq!(eval!("+1"), "1");
assert_eq!(eval!("-1"), "-1");
assert_eq!(eval!("+1"), "1");
assert_eq!(eval!("-1"), "-1");
assert_eq!(eval!("1 + 2"), "3");
assert_eq!(eval!("1 + 2 + 3"), "6");
assert_eq!(eval!("1 + 2.5"), "3.5");
assert_eq!(eval!("1 + 2.5"), "3.5");
assert_eq!(eval!("2 - 1"), "1");
assert_eq!(eval!("1 - 2"), "-1");
assert_eq!(eval!("2 * 3"), "6");
assert_eq!(eval!("2 * 3.5"), "7");
assert_eq!(eval!("6 / 2"), "3");
assert_eq!(eval!("6 / 4"), "1.5");
assert_eq!(eval!("2 ^ 4"), "16");
assert_eq!(eval!("3 % 2"), "1");
assert_eq!(eval!("1 + 2"), "3");
assert_eq!(eval!("1 + 2 + 3"), "6");
assert_eq!(eval!("1 + 2.5"), "3.5");
assert_eq!(eval!("1 + 2.5"), "3.5");
assert_eq!(eval!("2 - 1"), "1");
assert_eq!(eval!("1 - 2"), "-1");
assert_eq!(eval!("2 * 3"), "6");
assert_eq!(eval!("2 * 3.5"), "7");
assert_eq!(eval!("6 / 2"), "3");
assert_eq!(eval!("6 / 4"), "1.5");
assert_eq!(eval!("2 ^ 4"), "16");
assert_eq!(eval!("3 % 2"), "1");
assert_eq!(eval!("2 * 3 + 4"), "10");
assert_eq!(eval!("2 * (3 + 4)"), "14");
assert_eq!(eval!("2 ^ 4 + 1"), "17");
assert_eq!(eval!("1 + 2 ^ 4"), "17");
assert_eq!(eval!("2 * 3 + 4"), "10");
assert_eq!(eval!("2 * (3 + 4)"), "14");
assert_eq!(eval!("2 ^ 4 + 1"), "17");
assert_eq!(eval!("1 + 2 ^ 4"), "17");
assert_eq!(eval!("1 + 3 * 2 ^ 4 * 2 + 3"), "100");
}

View File

@ -1,18 +1,18 @@
use crate::{api, sys};
use crate::api::console::Style;
use crate::api::fs;
use crate::api::process::ExitCode;
use crate::api::prompt::Prompt;
use crate::{api, sys};
use alloc::format;
use alloc::string::String;
use alloc::sync::Arc;
use alloc::vec::Vec;
use littlewing::color::*;
use littlewing::chess::*;
use littlewing::fen::FEN;
use lazy_static::lazy_static;
use littlewing::chess::*;
use littlewing::color::*;
use littlewing::fen::FEN;
use spin::Mutex;
lazy_static! {
@ -20,7 +20,9 @@ lazy_static! {
}
const FEN: &str = "rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1";
const COMMANDS: [&str; 8] = ["quit", "help", "init", "time", "move", "undo", "show", "perf"];
const COMMANDS: [&str; 8] = [
"quit", "help", "init", "time", "move", "undo", "show", "perf",
];
fn update_autocomplete(prompt: &mut Prompt, game: &mut Game) {
*MOVES.lock() = game.get_moves().into_iter().map(|m| m.to_lan()).collect();
@ -29,13 +31,15 @@ fn update_autocomplete(prompt: &mut Prompt, game: &mut Game) {
let mut entries = Vec::new();
let args: Vec<&str> = line.split(' ').collect();
let i = args.len() - 1;
if i == 0 { // Autocomplete command
if i == 0 {
// Autocomplete command
for &cmd in &COMMANDS {
if let Some(entry) = cmd.strip_prefix(args[i]) {
entries.push(entry.into());
}
}
} else if i == 1 && (args[0] == "move" || args[0] == "m") { // Autocomplete moves
} else if i == 1 && (args[0] == "move" || args[0] == "m") {
// Autocomplete moves
for m in &*MOVES.lock() {
if let Some(entry) = m.strip_prefix(args[1]) {
entries.push(entry.into());
@ -78,9 +82,10 @@ impl Chess {
let history_file = "~/.chess-history";
prompt.history.load(history_file);
self.game.show_coordinates = true;
self.game.clock = Clock::new(40, 5 * 60 * 1000); // 40 moves in 5 minutes
// 40 moves in 5 minutes
self.game.clock = Clock::new(40, 5 * 60 * 1000);
self.game.clock.system_time = Arc::new(system_time);
self.game.show_coordinates = true;
let size = 1 << 20; // 1 MB
self.game.tt_resize(size);
self.game.load_fen(FEN).unwrap();
@ -99,7 +104,7 @@ impl Chess {
"u" | "undo" => self.cmd_undo(args),
"l" | "load" => self.cmd_load(args),
"s" | "save" => self.cmd_save(args),
"perf" => self.cmd_perf(args),
"perf" => self.cmd_perf(args),
cmd => {
if cmd.is_empty() {
println!();
@ -117,21 +122,28 @@ impl Chess {
fn cmd_help(&mut self, _args: Vec<&str>) {
println!("{}Commands:{}", self.csi_notif, self.csi_reset);
let cmds = [
("q", "uit", "Exit this program\n"),
("h", "elp", "Display this screen\n"),
("i", "nit", "Initialize a new game\n"),
("t", "ime <moves> <time>", "Set clock to <moves> in <time> (in seconds)\n"),
("p", "lay [<side>]", "Play <side> on the board\n"),
("m", "ove <move>", "Play <move> on the board\n"),
("u", "ndo", "Undo the last move\n"),
("l", "oad <file>", "Load game from <file>\n"),
("s", "ave <file>", "Save game to <file>\n"),
("", "perf [<depth>] ", "Count the nodes at each depth\n"),
("q", "uit", "Exit this program\n"),
("h", "elp", "Display this screen\n"),
("i", "nit", "Initialize a new game\n"),
(
"t",
"ime <moves> <time>",
"Set clock to <moves> in <time> (in seconds)\n",
),
("p", "lay [<side>]", "Play <side> on the board\n"),
("m", "ove <move>", "Play <move> on the board\n"),
("u", "ndo", "Undo the last move\n"),
("l", "oad <file>", "Load game from <file>\n"),
("s", "ave <file>", "Save game to <file>\n"),
("", "perf [<depth>] ", "Count the nodes at each depth\n"),
];
for (alias, command, usage) in &cmds {
let csi_col1 = Style::color("LightGreen");
let csi_col2 = Style::color("LightCyan");
print!(" {}{}{}{:20}{}{}", csi_col1, alias, csi_col2, command, self.csi_reset, usage);
print!(
" {}{}{}{:20}{}{}",
csi_col1, alias, csi_col2, command, self.csi_reset, usage
);
}
println!();
}
@ -152,9 +164,15 @@ impl Chess {
if let Ok(contents) = fs::read_to_string(path) {
if self.game.load_fen(&contents).is_ok() {
self.side = self.game.side() ^ 1;
let color = if self.game.side() == WHITE { "white" } else { "black" };
let color = if self.game.side() == WHITE {
"white"
} else {
"black"
};
println!();
println!("{}<{} play {}", self.csi_color, self.csi_reset, color);
println!(
"{}<{} play {}", self.csi_color, self.csi_reset, color
);
println!();
println!("{}", self.game);
} else {
@ -171,7 +189,8 @@ impl Chess {
return;
}
let path = args[1];
if fs::write(path, format!("{}\n", self.game.to_fen()).as_bytes()).is_ok() {
let contents = format!("{}\n", self.game.to_fen());
if fs::write(path, contents.as_bytes()).is_ok() {
println!();
} else {
error!("Could not write to '{}'\n", path);
@ -183,12 +202,12 @@ impl Chess {
1 => {
error!("No <moves> and <time> given\n");
return;
},
}
2 => {
error!("No <time> given\n");
return;
},
_ => {},
}
_ => {}
}
if let Ok(moves) = args[1].parse::<u16>() {
if let Ok(time) = args[2].parse::<f64>() {
@ -286,9 +305,13 @@ impl Chess {
loop {
let started_at = (self.game.clock.system_time)();
let n = self.game.perft(depth);
let s = (((self.game.clock.system_time)() - started_at) as f64) / 1000.0;
let t = (self.game.clock.system_time)();
let s = ((t - started_at) as f64) / 1000.0;
let nps = (n as f64) / s;
println!("{}{}:{} {}{} ({:.2} s, {:.2e} nps)", csi_depth, depth, csi_count, n, csi_reset, s, nps);
println!(
"{}{}:{} {}{} ({:.2} s, {:.2e} nps)",
csi_depth, depth, csi_count, n, csi_reset, s, nps
);
if args.len() > 1 || sys::console::end_of_text() {
break;
@ -301,11 +324,15 @@ impl Chess {
fn play(&mut self) {
let time = (self.game.clock.allocated_time() as f64) / 1000.0;
print!("{}<{} wait {:.2} seconds{}", self.csi_color, self.csi_notif, time, self.csi_reset);
print!(
"{}<{} wait {:.2} seconds{}",
self.csi_color, self.csi_notif, time, self.csi_reset
);
let r = self.game.search(1..99);
print!("\x1b[2K\x1b[1G");
if let Some(m) = r {
println!("{}<{} move {}", self.csi_color, self.csi_reset, m.to_lan());
let s = m.to_lan();
println!("{}<{} move {}", self.csi_color, self.csi_reset, s);
println!();
self.game.make_move(m);
self.game.history.push(m);
@ -345,7 +372,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
for &arg in args {
match arg {
"-h" | "--help" => return help(),
_ => {},
_ => {}
}
}
let mut chess = Chess::new();
@ -357,6 +384,9 @@ pub fn help() -> Result<(), ExitCode> {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} chess {}{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} chess {}{}",
csi_title, csi_reset, csi_option, csi_reset
);
Ok(())
}

View File

@ -14,7 +14,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
help();
return Ok(());
}
_ => continue
_ => continue,
}
}
@ -38,5 +38,8 @@ 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);
println!(
"{}Usage:{} copy {}<src> <dst>{}",
csi_title, csi_reset, csi_option, csi_reset
);
}

View File

@ -8,7 +8,11 @@ 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 };
let format = if args.len() > 1 {
args[1]
} else {
DATE_TIME_ZONE
};
if format == "-h" || format == "--help" {
return help();
}
@ -28,6 +32,9 @@ 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);
println!(
"{}Usage:{} date {}[<format>]{}",
csi_title, csi_reset, csi_option, csi_reset
);
Ok(())
}

View File

@ -15,15 +15,15 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
help();
return Ok(());
}
_ => continue
_ => continue,
}
}
for arg in &args[1..] {
let mut pathname = *arg;
// The commands `delete /usr/alice/` and `delete /usr/alice` are equivalent,
// but `delete /` should not be modified.
// The commands `delete /usr/alice/` and `delete /usr/alice`
// are equivalent, but `delete /` should not be modified.
if pathname.len() > 1 {
pathname = pathname.trim_end_matches('/');
}
@ -52,7 +52,10 @@ 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!(
"{}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);

View File

@ -1,13 +1,16 @@
use crate::{sys, usr, debug};
use crate::api::clock;
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::api::syscall;
use crate::debug;
use crate::sys::console;
use crate::sys::net;
use crate::usr::shell;
use alloc::format;
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::vec;
use alloc::vec::Vec;
use smoltcp::iface::SocketSet;
use smoltcp::socket::dhcpv4;
use smoltcp::time::Instant;
@ -24,7 +27,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
}
if let Some((ref mut iface, ref mut device)) = *sys::net::NET.lock() {
if let Some((ref mut iface, ref mut device)) = *net::NET.lock() {
let dhcp_socket = dhcpv4::Socket::new();
let mut sockets = SocketSet::new(vec![]);
let dhcp_handle = sockets.add(dhcp_socket);
@ -38,30 +41,32 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
error!("Timeout reached");
return Err(ExitCode::Failure);
}
if sys::console::end_of_text() || sys::console::end_of_transmission() {
if console::end_of_text() || console::end_of_transmission() {
eprintln!();
return Err(ExitCode::Failure);
}
let time = Instant::from_micros((clock::realtime() * 1000000.0) as i64);
let ms = (clock::realtime() * 1000000.0) as i64;
let time = Instant::from_micros(ms);
iface.poll(time, device, &mut sockets);
let event = sockets.get_mut::<dhcpv4::Socket>(dhcp_handle).poll();
match event {
None => {}
Some(dhcpv4::Event::Configured(config)) => {
dhcp_config = Some((config.address, config.router, config.dns_servers));
dhcp_config = Some(
(config.address, config.router, config.dns_servers)
);
if verbose {
debug!("DHCP Offer received");
}
break;
}
Some(dhcpv4::Event::Deconfigured) => {
}
Some(dhcpv4::Event::Deconfigured) => {}
}
if let Some(wait_duration) = iface.poll_delay(time, &sockets) {
syscall::sleep((wait_duration.total_micros() as f64) / 1000000.0);
if let Some(d) = iface.poll_delay(time, &sockets) {
syscall::sleep((d.total_micros() as f64) / 1000000.0);
}
}
} else {
@ -70,21 +75,21 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
if let Some((address, router, dns_servers)) = dhcp_config {
usr::shell::exec(&format!("net config ip {}", address)).ok();
usr::shell::exec("net config ip").ok();
shell::exec(&format!("net config ip {}", address)).ok();
shell::exec("net config ip").ok();
if let Some(router) = router {
usr::shell::exec(&format!("net config gw {}", router)).ok();
shell::exec(&format!("net config gw {}", router)).ok();
} else {
usr::shell::exec("net config gw 0.0.0.0").ok();
shell::exec("net config gw 0.0.0.0").ok();
}
usr::shell::exec("net config gw").ok();
shell::exec("net config gw").ok();
let dns: Vec<_> = dns_servers.iter().map(|s| s.to_string()).collect();
if !dns.is_empty() {
usr::shell::exec(&format!("net config dns {}", dns.join(","))).ok();
shell::exec(&format!("net config dns {}", dns.join(","))).ok();
}
usr::shell::exec("net config dns").ok();
shell::exec("net config dns").ok();
return Ok(());
}
@ -96,9 +101,15 @@ 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!(
"{}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);
println!(
" {0}-v{1}, {0}--verbose{1} Increase verbosity",
csi_option, csi_reset
);
Ok(())
}

View File

@ -4,27 +4,20 @@ use crate::api::process::ExitCode;
use crate::api::unit::SizeUnit;
use crate::sys;
use crate::sys::ata::Drive;
use crate::sys::console;
use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::vec;
use alloc::vec::Vec;
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
match *args.get(1).unwrap_or(&"") {
"f" | "format" if args.len() == 3 => {
format(args[2])
}
"e" | "erase" if args.len() == 3 => {
erase(args[2])
}
"u" | "usage" => {
usage(&args[2..])
}
"l" | "list" => {
list()
}
"f" | "format" if args.len() == 3 => format(args[2]),
"e" | "erase" if args.len() == 3 => erase(args[2]),
"u" | "usage" => usage(&args[2..]),
"l" | "list" => list(),
"-h" | "--help" => {
help();
Ok(())
@ -62,6 +55,10 @@ fn format(pathname: &str) -> Result<(), ExitCode> {
}
}
fn is_canceled() -> bool {
console::end_of_text() || console::end_of_transmission()
}
fn erase(pathname: &str) -> Result<(), ExitCode> {
match parse_disk_path(pathname) {
Ok((bus, dsk)) => {
@ -74,7 +71,7 @@ fn erase(pathname: &str) -> Result<(), ExitCode> {
let buf = vec![0; drive.block_size() as usize];
print!("\x1b[?25l"); // Disable cursor
for i in 0..n {
if sys::console::end_of_text() || sys::console::end_of_transmission() {
if is_canceled() {
println!();
print!("\x1b[?25h"); // Enable cursor
return Err(ExitCode::Failure);
@ -128,14 +125,32 @@ fn usage(args: &[&str]) -> Result<(), ExitCode> {
let size = sys::fs::disk_size();
let used = sys::fs::disk_used();
let free = size - used;
let width = [size, used, free].iter().fold(0, |acc, num| {
let width = [size, used, free].iter().fold(0, |acc, num|
core::cmp::max(acc, unit.format(*num).len())
});
);
let color = Style::color("LightCyan");
let reset = Style::reset();
println!("{}size:{} {:>width$}", color, reset, unit.format(size), width = width);
println!("{}used:{} {:>width$}", color, reset, unit.format(used), width = width);
println!("{}free:{} {:>width$}", color, reset, unit.format(free), width = width);
println!(
"{}size:{} {:>width$}",
color,
reset,
unit.format(size),
width = width
);
println!(
"{}used:{} {:>width$}",
color,
reset,
unit.format(used),
width = width
);
println!(
"{}free:{} {:>width$}",
color,
reset,
unit.format(free),
width = width
);
Ok(())
}
@ -143,21 +158,36 @@ fn help_usage() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} disk usage {}<options>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} disk usage {}<options>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-b{1}, {0}--binary-size{1} Use binary size", csi_option, csi_reset);
println!(
" {0}-b{1}, {0}--binary-size{1} Use binary size",
csi_option, csi_reset
);
}
fn help() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} disk {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} disk {}<command>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Commands:{}", csi_title, csi_reset);
println!(" {}list{} List detected disks", csi_option, csi_reset);
println!(" {}usage{} List disk usage", csi_option, csi_reset);
println!(
" {}list{} List detected disks",
csi_option, csi_reset
);
println!(
" {}usage{} List disk usage",
csi_option, csi_reset
);
println!(" {}format <path>{} Format disk", csi_option, csi_reset);
println!(" {}erase <path>{} Erase disk", csi_option, csi_reset);
}

View File

@ -1,7 +1,7 @@
use crate::sys;
use crate::api::{console, fs, io};
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::api::{console, fs, io};
use crate::sys;
use alloc::format;
use alloc::string::{String, ToString};
@ -56,7 +56,7 @@ impl Editor {
for line in contents.split('\n') {
lines.push(line.into());
}
},
}
Err(_) => {
lines.push(String::new());
}
@ -64,7 +64,15 @@ impl Editor {
let pathname = pathname.into();
Self { pathname, clipboard, lines, cursor, offset, highlighted, config }
Self {
pathname,
clipboard,
lines,
cursor,
offset,
highlighted,
config,
}
}
pub fn save(&mut self) -> Result<(), ExitCode> {
@ -89,11 +97,15 @@ impl Editor {
}
fn print_status(&mut self, status: &str, background: &str) {
// Move cursor to the bottom of the screen
print!("\x1b[{};1H", self.rows() + 1);
let color = Style::color("Black").with_background(background);
let reset = Style::reset();
print!("\x1b[{};1H", self.rows() + 1); // Move cursor to the bottom of the screen
print!("{}{:cols$}{}", color, status, reset, cols = self.cols());
print!("\x1b[{};{}H", self.cursor.y + 1, self.cursor.x + 1); // Move cursor back
// Move cursor back
print!("\x1b[{};{}H", self.cursor.y + 1, self.cursor.x + 1);
}
fn print_editing_status(&mut self) {
@ -128,9 +140,14 @@ impl Editor {
fn render_line(&self, y: usize) -> String {
// Render line into a row of the screen, or an empty row when past eof
let line = if y < self.lines.len() { &self.lines[y] } else { "" };
let line = if y < self.lines.len() {
&self.lines[y]
} else {
""
};
let mut row: Vec<char> = format!("{:cols$}", line, cols = self.offset.x).chars().collect();
let s = format!("{:cols$}", line, cols = self.offset.x);
let mut row: Vec<char> = s.chars().collect();
let n = self.offset.x + self.cols();
let after = if row.len() > n {
row.truncate(n - 1);
@ -144,9 +161,9 @@ impl Editor {
fn render_char(&self, c: char) -> Option<String> {
match c {
'\t' => Some(" ".repeat(self.config.tab_size)),
'\t' => Some(" ".repeat(self.config.tab_size)),
c if console::is_printable(c) => Some(c.to_string()),
_ => None,
_ => None,
}
}
@ -160,13 +177,16 @@ impl Editor {
if cursor == closing {
for (y, line) in self.lines.iter().enumerate() {
for (x, c) in line.chars().enumerate() {
if oy + cy == y && ox + cx == x { // Cursor position
if oy + cy == y && ox + cx == x {
// Cursor position
if let Some((x, y)) = stack.pop() {
self.highlighted.push((cx, cy, closing));
let is_col = ox <= x && x < ox + self.cols();
let is_row = oy <= y && y < oy + self.rows();
if is_col && is_row {
self.highlighted.push((x - ox, y - oy, opening));
self.highlighted.push(
(x - ox, y - oy, opening)
);
}
}
return;
@ -186,7 +206,8 @@ impl Editor {
if cursor == opening {
for (y, line) in self.lines.iter().enumerate().skip(oy + cy) {
for (x, c) in line.chars().enumerate() {
if y == oy + cy && x <= ox + cx { // Skip chars before cursor
if y == oy + cy && x <= ox + cx {
// Skip chars before cursor
continue;
}
if c == opening {
@ -198,7 +219,9 @@ impl Editor {
let is_col = ox <= x && x < ox + self.cols();
let is_row = oy <= y && y < oy + self.rows();
if is_col && is_row {
self.highlighted.push((x - ox, y - oy, closing));
self.highlighted.push(
(x - ox, y - oy, closing)
);
}
return;
}
@ -262,7 +285,7 @@ impl Editor {
}
pub fn run(&mut self) -> Result<(), ExitCode> {
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
self.print_screen();
self.print_editing_status();
self.print_highlighted();
@ -281,37 +304,38 @@ impl Editor {
'\x1B' => { // ESC
escape = true;
continue;
},
}
'[' if escape => {
csi = true;
csi_params.clear();
continue;
},
}
'\0' => {
continue;
}
'\x11' | '\x03' => { // Ctrl Q or Ctrl C
// TODO: Warn if modifications have not been saved
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
print!("\x1b[?25h"); // Enable cursor
break;
},
}
'\x17' => { // Ctrl W
self.save().ok();
print!("\x1b[?25h"); // Enable cursor
continue;
},
}
'\x18' => { // Ctrl X
let res = self.save();
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
print!("\x1b[?25h"); // Enable cursor
return res;
},
}
'\n' => { // Newline
let y = self.offset.y + self.cursor.y;
let old_line = self.lines[y].clone();
let mut row: Vec<char> = old_line.chars().collect();
let new_line = row.split_off(self.offset.x + self.cursor.x).into_iter().collect();
let new_line = row.
split_off(self.offset.x + self.cursor.x).
into_iter().collect();
self.lines[y] = row.into_iter().collect();
self.lines.insert(y + 1, new_line);
if self.cursor.y == self.rows() - 1 {
@ -322,21 +346,22 @@ impl Editor {
self.cursor.x = 0;
self.offset.x = 0;
self.print_screen();
},
}
'~' if csi && csi_params == "5" => { // Page Up
let scroll = self.rows() - 1; // Keep one previous line on screen
let scroll = self.rows() - 1; // Keep one line on screen
self.offset.y -= cmp::min(scroll, self.offset.y);
self.print_screen();
},
}
'~' if csi && csi_params == "6" => { // Page Down
let scroll = self.rows() - 1; // Keep one previous line on screen
let remaining = cmp::max(self.lines.len(), 1) - self.offset.y - 1;
let scroll = self.rows() - 1; // Keep one line on screen
let n = cmp::max(self.lines.len(), 1);
let remaining = n - self.offset.y - 1;
self.offset.y += cmp::min(scroll, remaining);
if self.cursor.y + scroll > remaining {
self.cursor.y = 0;
}
self.print_screen();
},
}
'A' if csi => { // Arrow Up
if self.cursor.y > 0 {
self.cursor.y -= 1
@ -345,11 +370,12 @@ impl Editor {
}
self.align_cursor();
self.print_screen();
},
}
'B' if csi => { // Arrow Down
let is_eof = self.offset.y + self.cursor.y == self.lines.len() - 1;
let n = self.lines.len() - 1;
let is_eof = n == (self.offset.y + self.cursor.y);
let is_bottom = self.cursor.y == self.rows() - 1;
if self.cursor.y < cmp::min(self.rows(), self.lines.len() - 1) {
if self.cursor.y < cmp::min(self.rows(), n) {
if is_bottom || is_eof {
if !is_eof {
self.offset.y += 1;
@ -360,14 +386,16 @@ impl Editor {
self.align_cursor();
self.print_screen();
}
},
}
'C' if csi => { // Arrow Right
let line = &self.lines[self.offset.y + self.cursor.y];
if line.is_empty() || self.cursor.x + self.offset.x >= line.chars().count() {
let x = self.cursor.x + self.offset.x;
let n = line.chars().count();
if line.is_empty() || x >= n {
print!("\x1b[?25h"); // Enable cursor
escape = false;
csi = false;
continue
continue;
} else if self.cursor.x == self.cols() - 1 {
self.offset.x += self.cols();
self.cursor.x -= self.cols() - 1;
@ -375,7 +403,7 @@ impl Editor {
} else {
self.cursor.x += 1;
}
},
}
'D' if csi => { // Arrow Left
if self.cursor.x + self.offset.x == 0 {
print!("\x1b[?25h"); // Enable cursor
@ -390,36 +418,37 @@ impl Editor {
} else {
self.cursor.x -= 1;
}
},
}
'Z' if csi => { // Backtab (Shift + Tab)
// Do nothing
},
// Do nothing
}
'\x14' => { // Ctrl T -> Go to top of file
self.cursor.x = 0;
self.cursor.y = 0;
self.offset.x = 0;
self.offset.y = 0;
self.print_screen();
},
}
'\x02' => { // Ctrl B -> Go to bottom of file
self.cursor.x = 0;
self.cursor.y = cmp::min(self.rows(), self.lines.len()) - 1;
self.offset.x = 0;
self.offset.y = self.lines.len() - 1 - self.cursor.y;
self.print_screen();
},
}
'\x01' => { // Ctrl A -> Go to beginning of line
self.cursor.x = 0;
self.offset.x = 0;
self.print_screen();
},
}
'\x05' => { // Ctrl E -> Go to end of line
let n = self.lines[self.offset.y + self.cursor.y].chars().count();
let line = &self.lines[self.offset.y + self.cursor.y];
let n = line.chars().count();
let w = self.cols();
self.cursor.x = n % w;
self.offset.x = w * (n / w);
self.print_screen();
},
}
'\x04' => { // Ctrl D -> Delete (cut) line
let i = self.offset.y + self.cursor.y;
self.clipboard.push(self.lines.remove(i));
@ -427,8 +456,8 @@ impl Editor {
self.lines.push(String::new());
}
// Move cursor up to the previous line
if i >= self.lines.len() {
// Move cursor up to the previous line
if self.cursor.y > 0 {
self.cursor.y -= 1;
} else if self.offset.y > 0 {
@ -439,11 +468,11 @@ impl Editor {
self.offset.x = 0;
self.print_screen();
},
}
'\x19' => { // Ctrl Y -> Yank (copy) line
let i = self.offset.y + self.cursor.y;
self.clipboard.push(self.lines[i].clone());
},
}
'\x10' => { // Ctrl P -> Put (paste) line
let i = self.offset.y + self.cursor.y;
if let Some(line) = self.clipboard.pop() {
@ -452,11 +481,12 @@ impl Editor {
self.cursor.x = 0;
self.offset.x = 0;
self.print_screen();
},
}
'\x08' => { // Backspace
let y = self.offset.y + self.cursor.y;
if self.offset.x + self.cursor.x > 0 { // Remove char from line
let mut row: Vec<char> = self.lines[y].chars().collect();
if self.offset.x + self.cursor.x > 0 {
// Remove char from line
let mut row: Vec<_> = self.lines[y].chars().collect();
row.remove(self.offset.x + self.cursor.x - 1);
self.lines[y] = row.into_iter().collect();
@ -469,7 +499,8 @@ impl Editor {
let line = self.render_line(y);
print!("\x1b[2K\x1b[1G{}", line);
}
} else { // Remove newline from previous line
} else {
// Remove newline from previous line
if self.cursor.y == 0 && self.offset.y == 0 {
print!("\x1b[?25h"); // Enable cursor
escape = false;
@ -496,30 +527,33 @@ impl Editor {
self.print_screen();
}
},
'\x7f' => { // Delete
}
'\x7f' => {
// Delete
let y = self.offset.y + self.cursor.y;
let n = self.lines[y].chars().count();
if self.offset.x + self.cursor.x >= n { // Remove newline from line
if self.offset.x + self.cursor.x >= n {
// Remove newline from line
if y + 1 < self.lines.len() {
let line = self.lines.remove(y + 1);
self.lines[y].push_str(&line);
self.print_screen();
}
} else { // Remove char from line
} else {
// Remove char from line
self.lines[y].remove(self.offset.x + self.cursor.x);
let line = self.render_line(y);
print!("\x1b[2K\x1b[1G{}", line);
}
},
}
c if csi => {
csi_params.push(c);
continue;
},
}
c => {
if let Some(s) = self.render_char(c) {
let y = self.offset.y + self.cursor.y;
let mut row: Vec<char> = self.lines[y].chars().collect();
let mut row: Vec<_> = self.lines[y].chars().collect();
for c in s.chars() {
row.insert(self.offset.x + self.cursor.x, c);
self.cursor.x += 1;
@ -530,11 +564,11 @@ impl Editor {
self.cursor.x -= self.cols();
self.print_screen();
} else {
let line = self.render_line(self.offset.y + self.cursor.y);
let line = self.render_line(y);
print!("\x1b[2K\x1b[1G{}", line);
}
}
},
}
}
self.print_editing_status();
self.print_highlighted();
@ -565,5 +599,8 @@ 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);
println!(
"{}Usage:{} edit {}<file>{}",
csi_title, csi_reset, csi_option, csi_reset
);
}

View File

@ -31,7 +31,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
let size = section.size();
let align = section.align();
println!();
println!("{}{}{} (addr: {:#X}, size: {}, align: {})", color, name, reset, addr, size, align);
println!(
"{}{}{} (addr: {:#X}, size: {}, align: {})",
color, name, reset, addr, size, align
);
if let Ok(data) = section.data() {
usr::hex::print_hex_at(data, addr);
}
@ -52,5 +55,8 @@ 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);
println!(
"{}Usage:{} elf {}<binary>{}",
csi_title, csi_reset, csi_option, csi_reset
);
}

View File

@ -10,12 +10,14 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
help();
return Ok(());
}
_ => continue
_ => continue,
}
}
match n {
1 => {
let width = sys::process::envs().keys().map(|k| k.len()).max().unwrap_or(0);
let width = sys::process::envs().keys().map(|k|
k.len()
).max().unwrap_or(0);
for (key, val) in sys::process::envs() {
println!("{:width$} \"{}\"", key, val, width = width);
}
@ -46,5 +48,8 @@ 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);
println!(
"{}Usage:{} env {}[<key> [<value>]]{}",
csi_title, csi_reset, csi_option, csi_reset
);
}

View File

@ -1,8 +1,8 @@
use crate::sys;
use crate::api::console::Style;
use crate::api::fs;
use crate::api::process::ExitCode;
use crate::api::regex::Regex;
use crate::api::console::Style;
use crate::sys;
use alloc::format;
use alloc::string::{String, ToString};
@ -68,7 +68,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
return Err(ExitCode::UsageError);
}
if name.is_some() { // TODO
if name.is_some() {
// TODO
error!("`--name` is not implemented");
return Err(ExitCode::Failure);
}
@ -103,7 +104,11 @@ fn print_matching_lines(path: &str, pattern: &str, state: &mut PrintingState) {
}
}
fn print_matching_lines_in_file(path: &str, pattern: &str, state: &mut PrintingState) {
fn print_matching_lines_in_file(
path: &str,
pattern: &str,
state: &mut PrintingState
) {
let name_color = Style::color("Yellow");
let line_color = Style::color("LightCyan");
let match_color = Style::color("LightRed");
@ -119,9 +124,9 @@ fn print_matching_lines_in_file(path: &str, pattern: &str, state: &mut PrintingS
while let Some((a, b)) = re.find(&String::from_iter(&line[j..])) {
let m = j + a;
let n = j + b;
let before = String::from_iter(&line[j..m]);
let b = String::from_iter(&line[j..m]);
let matched = String::from_iter(&line[m..n]);
l = format!("{}{}{}{}{}", l, before, match_color, matched, reset);
l = format!("{}{}{}{}{}", l, b, match_color, matched, reset);
j = n;
if m == n || n >= line.len() {
// Some patterns like "" or ".*?" would never move the
@ -148,7 +153,14 @@ fn print_matching_lines_in_file(path: &str, pattern: &str, state: &mut PrintingS
}
let width = matches[matches.len() - 1].0.to_string().len();
for (i, line) in matches {
println!("{}{:>width$}:{} {}", line_color, i, reset, line, width = width);
println!(
"{}{:>width$}:{} {}",
line_color,
i,
reset,
line,
width = width
);
}
}
}
@ -158,9 +170,20 @@ fn usage() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} find {}<options> <path>{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} find {}<options> <path>{1}",
csi_title, csi_reset, csi_option
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-n{1}, {0}--name \"<pattern>\"{1} Find file name matching {0}<pattern>{1}", csi_option, csi_reset);
println!(" {0}-l{1}, {0}--line \"<pattern>\"{1} Find lines matching {0}<pattern>{1}", csi_option, csi_reset);
println!(
" {0}-n{1}, {0}--name \"<pattern>\"{1} \
Find file name matching {0}<pattern>{1}",
csi_option, csi_reset
);
println!(
" {0}-l{1}, {0}--line \"<pattern>\"{1} \
Find lines matching {0}<pattern>{1}",
csi_option, csi_reset
);
}

View File

@ -36,9 +36,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
paths.sort();
for path in paths {
if let Err(code) = print_hash(path, full) {
return Err(code);
}
print_hash(path, full)?;
}
Ok(())
}
@ -76,8 +74,14 @@ fn help() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} hash {}<file>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} hash {}<file>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-f{1}, {0}--full{1} Show full hash", csi_option, csi_reset);
println!(
" {0}-f{1}, {0}--full{1} Show full hash",
csi_option, csi_reset
);
}

View File

@ -14,10 +14,13 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
fn help_command(cmd: &str) -> Result<(), ExitCode> {
match cmd {
"-h" | "--help" => { help(); Ok(()) },
"-h" | "--help" => {
help();
Ok(())
}
"date" => help_date(),
"edit" => help_edit(),
_ => help_unknown(cmd),
_ => help_unknown(cmd),
}
}
@ -30,7 +33,10 @@ fn print_usage(alias: &str, command: &str, usage: &str) {
let csi_col1 = Style::color("LightGreen");
let csi_col2 = Style::color("LightCyan");
let csi_reset = Style::reset();
println!(" {}{}{}{:21}{}{}", csi_col1, alias, csi_col2, command, csi_reset, usage);
println!(
" {}{}{}{:21}{}{}",
csi_col1, alias, csi_col2, command, csi_reset, usage
);
}
fn help_summary() -> Result<(), ExitCode> {
@ -38,22 +44,30 @@ fn help_summary() -> Result<(), ExitCode> {
let csi_reset = Style::reset();
println!("{}Usage:{}", csi_color, csi_reset);
print_usage("", "<dir>", " Change directory");
print_usage("", "<cmd>", " Execute command");
print_usage("", "<dir>", " Change directory");
print_usage("", "<cmd>", " Execute command");
println!();
println!("{}Commands:{}", csi_color, csi_reset);
print_usage("c", "opy <file> <file>", "Copy file from source to destination");
print_usage("d", "elete <file>", "Delete file or empty directory");
print_usage("e", "dit <file>", "Edit existing or new file");
print_usage("f", "ind <str> <path>", "Find pattern in path");
print_usage("h", "elp <cmd>", "Display help about a command");
print_usage("l", "ist <dir>", "List entries in directory");
print_usage("m", "ove <file> <file>", "Move file from source to destination");
print_usage("p", "rint <str>", "Print string to screen");
print_usage("q", "uit", "Quit the console");
print_usage("r", "ead <file>", "Read file to screen");
print_usage("w", "rite <file>", "Write file or directory");
print_usage(
"c",
"opy <file> <file>",
"Copy file from source to destination",
);
print_usage("d", "elete <file>", "Delete file or empty directory");
print_usage("e", "dit <file>", "Edit existing or new file");
print_usage("f", "ind <str> <path>", "Find pattern in path");
print_usage("h", "elp <cmd>", "Display help about a command");
print_usage("l", "ist <dir>", "List entries in directory");
print_usage(
"m",
"ove <file> <file>",
"Move file from source to destination",
);
print_usage("p", "rint <str>", "Print string to screen");
print_usage("q", "uit", "Quit the console");
print_usage("r", "ead <file>", "Read file to screen");
print_usage("w", "rite <file>", "Write file or directory");
println!();
println!("{}Credits:{}", csi_color, csi_reset);
@ -64,7 +78,8 @@ 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 a very simple editor inspired by Pico, Nano, and Micro.");
println!(
"MOROS text editor is a very simple editor inspired by Pico.");
println!();
println!("{}Commands:{}", csi_color, csi_reset);
let commands = [
@ -90,42 +105,166 @@ 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 from C.");
println!("The date command's formatting behavior is based on strftime.");
println!();
println!("{}Specifiers:{}", csi_color, csi_reset);
let specifiers = [
("%a", "Abbreviated weekday name", "Thu"),
("%A", "Full weekday name", "Thursday"),
("%b", "Abbreviated month name", "Aug"),
("%B", "Full month name", "August"),
("%c", "Date and time representation, equivalent to %a %b %-d %-H:%M:%S %-Y", "Thu Aug 23 14:55:02 2001"),
("%C", "Year divided by 100 and truncated to integer (00-99)", "20"),
("%d", "Day of the month, zero-padded (01-31)", "23"),
("%D", "Short MM/DD/YY date, equivalent to %-m/%d/%y", "8/23/01"),
("%F", "Short YYYY-MM-DD date, equivalent to %-Y-%m-%d", "2001-08-23"),
("%g", "Week-based year, last two digits (00-99)", "01"),
("%G", "Week-based year", "2001"),
("%H", "Hour in 24h format (00-23)", "14"),
("%I", "Hour in 12h format (01-12)", "02"),
("%j", "Day of the year (001-366)", "235"),
("%m", "Month as a decimal number (01-12)", "08"),
("%M", "Minute (00-59)", "55"),
("%N", "Subsecond nanoseconds. Always 9 digits", "012345678"),
("%p", "am or pm designation", "pm"),
("%P", "AM or PM designation", "PM"),
("%r", "12-hour clock time, equivalent to %-I:%M:%S %p", "2:55:02 pm"),
("%R", "24-hour HH:MM time, equivalent to %-H:%M", "14:55"),
("%S", "Second (00-59)", "02"),
("%T", "24-hour clock time with seconds, equivalent to %-H:%M:%S", "14:55:02"),
("%u", "ISO 8601 weekday as number with Monday as 1 (1-7)", "4"),
("%U", "Week number with the first Sunday as the start of week one (00-53)", "33"),
("%V", "ISO 8601 week number (01-53)", "34"),
("%w", "Weekday as a decimal number with Sunday as 0 (0-6)", "4"),
("%W", "Week number with the first Monday as the start of week one (00-53)", "34"),
("%y", "Year, last two digits (00-99)", "01"),
("%Y", "Full year, including + if ≥10,000", "2001"),
("%z", "ISO 8601 offset from UTC in timezone (+HHMM)", "+0100"),
("%%", "Literal %", "%"),
(
"%a",
"Abbreviated weekday name",
"Thu",
),
(
"%A",
"Full weekday name",
"Thursday",
),
(
"%b",
"Abbreviated month name",
"Aug",
),
(
"%B",
"Full month name",
"August",
),
(
"%c",
"Date and time, equivalent to %a %b %-d %-H:%M:%S %-Y",
"Thu Aug 23 14:55:02 2001",
),
(
"%C",
"Year divided by 100 and truncated to integer (00-99)",
"20",
),
(
"%d",
"Day of the month, zero-padded (01-31)",
"23",
),
(
"%D",
"Short MM/DD/YY date, equivalent to %-m/%d/%y",
"8/23/01",
),
(
"%F",
"Short YYYY-MM-DD date, equivalent to %-Y-%m-%d",
"2001-08-23",
),
(
"%g",
"Week-based year, last two digits (00-99)",
"01",
),
(
"%G",
"Week-based year",
"2001",
),
(
"%H",
"Hour in 24h format (00-23)",
"14",
),
(
"%I",
"Hour in 12h format (01-12)",
"02",
),
(
"%j",
"Day of the year (001-366)",
"235",
),
(
"%m",
"Month as a decimal number (01-12)",
"08",
),
(
"%M",
"Minute (00-59)",
"55",
),
(
"%N",
"Subsecond nanoseconds. Always 9 digits",
"012345678",
),
(
"%p",
"am or pm designation", "pm"),
(
"%P",
"AM or PM designation", "PM"),
(
"%r",
"12-hour clock time, equivalent to %-I:%M:%S %p",
"2:55:02 pm",
),
(
"%R",
"24-hour HH:MM time, equivalent to %-H:%M",
"14:55"
),
(
"%S",
"Second (00-59)",
"02"
),
(
"%T",
"24-hour clock time with seconds, equivalent to %-H:%M:%S",
"14:55:02",
),
(
"%u",
"ISO 8601 weekday as number with Monday as 1 (1-7)",
"4",
),
(
"%U",
"Week number with Sunday as first day of the week (00-53)",
"33",
),
(
"%V",
"ISO 8601 week number (01-53)",
"34",
),
(
"%w",
"Weekday as a decimal number with Sunday as 0 (0-6)",
"4",
),
(
"%W",
"Week number with Monday as first day of the week (00-53)",
"34",
),
(
"%y",
"Year, last two digits (00-99)",
"01",
),
(
"%Y",
"Full year, including + if ≥10,000",
"2001",
),
(
"%z",
"ISO 8601 offset from UTC in timezone (+HHMM)",
"+0100",
),
(
"%%",
"Literal %",
"%"
),
];
for (specifier, usage, _exemple) in &specifiers {
let csi_color = Style::color("LightCyan");
@ -139,5 +278,8 @@ 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);
println!(
"{}Usage:{} help {}[<command>]{}",
csi_title, csi_reset, csi_option, csi_reset
);
}

View File

@ -1,5 +1,5 @@
use crate::api::fs;
use crate::api::console::Style;
use crate::api::fs;
use crate::api::process::ExitCode;
use alloc::format;
@ -17,7 +17,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
return Ok(());
}
let pathname = args[1];
if let Ok(buf) = fs::read_to_bytes(pathname) { // TODO: read chunks
if let Ok(buf) = fs::read_to_bytes(pathname) {
// TODO: read chunks
print_hex(&buf);
Ok(())
} else {
@ -61,5 +62,8 @@ 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);
println!(
"{}Usage:{} hex {}<file>{}",
csi_title, csi_reset, csi_option, csi_reset
);
}

View File

@ -1,9 +1,9 @@
use crate::usr;
use crate::api::console::Style;
use crate::sys::fs::OpenFlag;
use crate::api::process::ExitCode;
use crate::api::random;
use crate::api::syscall;
use crate::sys::fs::OpenFlag;
use crate::usr;
use alloc::vec;
use alloc::vec::Vec;
use bit_field::BitField;
@ -143,11 +143,11 @@ pub fn resolve(name: &str) -> Result<IpAddress, ResponseCode> {
if let Some(handle) = syscall::open(socket_path, flags) {
if syscall::connect(handle, addr, port).is_err() {
syscall::close(handle);
return Err(ResponseCode::NetworkError)
return Err(ResponseCode::NetworkError);
}
if syscall::write(handle, &query.datagram).is_none() {
syscall::close(handle);
return Err(ResponseCode::NetworkError)
return Err(ResponseCode::NetworkError);
}
loop {
let mut data = vec![0; buf_len];
@ -174,10 +174,8 @@ pub fn resolve(name: &str) -> Result<IpAddress, ResponseCode> {
Ok(IpAddress::from(ipv4))
}
}
code => {
Err(code)
}
}
code => Err(code),
};
}
} else {
break;
@ -211,5 +209,8 @@ fn help() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} host {}<domain>{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} host {}<domain>{1}",
csi_title, csi_reset, csi_option
);
}

View File

@ -1,8 +1,9 @@
use crate::{sys, usr};
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::api::syscall;
use crate::sys::console;
use crate::sys::fs::OpenFlag;
use crate::usr;
use alloc::format;
use alloc::string::{String, ToString};
@ -17,7 +18,10 @@ struct URL {
pub path: String,
}
enum ResponseState { Headers, Body }
enum ResponseState {
Headers,
Body,
}
impl URL {
pub fn parse(url: &str) -> Option<Self> {
@ -75,7 +79,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
error!("Invalid option '{}'", args[i]);
return Err(ExitCode::UsageError);
} else if host.is_empty() {
host = args[i].trim_start_matches("http://").trim_start_matches("https://");
host = args[i].
trim_start_matches("http://").
trim_start_matches("https://");
} else if path.is_empty() {
path = args[i];
} else {
@ -103,9 +109,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
let port = url.port;
let addr = if url.host.ends_with(char::is_numeric) {
match IpAddress::from_str(&url.host) {
Ok(ip_addr) => {
ip_addr
}
Ok(ip_addr) => ip_addr,
Err(_) => {
error!("Invalid address format");
return Err(ExitCode::UsageError);
@ -113,9 +117,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
} else {
match usr::host::resolve(&url.host) {
Ok(ip_addr) => {
ip_addr
}
Ok(ip_addr) => ip_addr,
Err(e) => {
error!("Could not resolve host: {:?}", e);
return Err(ExitCode::Failure);
@ -157,7 +159,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
let mut response_state = ResponseState::Headers;
loop {
if sys::console::end_of_text() || sys::console::end_of_transmission() {
if console::end_of_text() || console::end_of_transmission() {
eprintln!();
syscall::close(handle);
return Err(ExitCode::Failure);
@ -179,7 +181,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
j += 1;
}
let line = String::from_utf8_lossy(&data[i..j]); // TODO: check i == j
// TODO: check i == j
let line = String::from_utf8_lossy(&data[i..j]);
if is_verbose {
if i == 0 {
print!("{}", csi_verbose);
@ -195,8 +198,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
i = j + 1;
}
ResponseState::Body => {
// NOTE: The buffer may not be convertible to a UTF-8 string
// so we write it to STDOUT directly instead of using print.
// NOTE: The buffer may not be convertible to a
// UTF-8 string so we write it to STDOUT directly
// instead of using print.
syscall::write(1, &data[i..n]);
break;
}
@ -219,10 +223,19 @@ fn help() -> Result<(), ExitCode> {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} http {}<options> <url>{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} http {}<options> <url>{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);
println!(" {0}-t{1}, {0}--timeout <seconds>{1} Request timeout", csi_option, csi_reset);
println!(
" {0}-v{1}, {0}--verbose{1} Increase verbosity",
csi_option, csi_reset
);
println!(
" {0}-t{1}, {0}--timeout <seconds>{1} Request timeout",
csi_option, csi_reset
);
Ok(())
}

View File

@ -1,4 +1,3 @@
use crate::sys;
use crate::api::clock;
use crate::api::clock::DATE_TIME_ZONE;
use crate::api::console::Style;
@ -6,6 +5,8 @@ use crate::api::fs;
use crate::api::process::ExitCode;
use crate::api::syscall;
use crate::api::time;
use crate::sys;
use crate::sys::console;
use alloc::collections::btree_map::BTreeMap;
use alloc::collections::vec_deque::VecDeque;
@ -15,9 +16,9 @@ use alloc::vec;
use alloc::vec::Vec;
use core::fmt;
use smoltcp::iface::SocketSet;
use smoltcp::phy::Device;
use smoltcp::socket::tcp;
use smoltcp::time::Instant;
use smoltcp::phy::Device;
use smoltcp::wire::IpAddress;
const MAX_CONNECTIONS: usize = 32;
@ -49,20 +50,26 @@ impl Request {
let mut req = Request::new(addr);
let mut is_header = true;
for (i, line) in msg.lines().enumerate() {
if i == 0 { // Request line
if i == 0 {
// Request line
let fields: Vec<_> = line.split(' ').collect();
if fields.len() >= 2 {
req.verb = fields[0].to_string();
req.path = fields[1].to_string();
}
} else if is_header { // Message header
} else if is_header {
// Message header
if let Some((key, val)) = line.split_once(':') {
req.headers.insert(key.trim().to_string(), val.trim().to_string());
let k = key.trim().to_string();
let v = val.trim().to_string();
req.headers.insert(k, v);
} else if line.is_empty() {
is_header = false;
}
} else if !is_header { // Message body
req.body.extend_from_slice(format!("{}\n", line).as_bytes());
} else if !is_header {
// Message body
let s = format!("{}\n", line);
req.body.extend_from_slice(s.as_bytes());
}
}
Some(req)
@ -82,13 +89,20 @@ struct Response {
size: usize,
body: Vec<u8>,
headers: BTreeMap<String, String>,
real_path: String,
}
impl Response {
pub fn new(req: Request) -> Self {
let mut headers = BTreeMap::new();
headers.insert("Date".to_string(), time::now_utc().format("%a, %d %b %Y %H:%M:%S GMT"));
headers.insert("Server".to_string(), format!("MOROS/{}", env!("CARGO_PKG_VERSION")));
headers.insert(
"Date".to_string(),
time::now_utc().format("%a, %d %b %Y %H:%M:%S GMT"),
);
headers.insert(
"Server".to_string(),
format!("MOROS/{}", env!("CARGO_PKG_VERSION")),
);
Self {
req,
buf: Vec::new(),
@ -98,30 +112,44 @@ impl Response {
size: 0,
body: Vec::new(),
headers,
real_path: String::new(),
}
}
pub fn end(&mut self) {
self.size = self.body.len();
self.headers.insert("Content-Length".to_string(), self.size.to_string());
self.headers.insert("Connection".to_string(), if self.is_persistent() {
"keep-alive".to_string()
} else {
"close".to_string()
});
self.headers.insert("Content-Type".to_string(), if self.mime.starts_with("text/") {
format!("{}; charset=utf-8", self.mime)
} else {
format!("{}", self.mime)
});
self.headers.insert(
"Content-Length".to_string(),
self.size.to_string()
);
self.headers.insert(
"Connection".to_string(),
if self.is_persistent() {
"keep-alive".to_string()
} else {
"close".to_string()
}
);
self.headers.insert(
"Content-Type".to_string(),
if self.mime.starts_with("text/") {
format!("{}; charset=utf-8", self.mime)
} else {
format!("{}", self.mime)
}
);
self.write();
}
fn write(&mut self) {
self.buf.clear();
self.buf.extend_from_slice(format!("{}\r\n", self.status()).as_bytes());
self.buf.extend_from_slice(
format!("{}\r\n", self.status()).as_bytes()
);
for (key, val) in &self.headers {
self.buf.extend_from_slice(format!("{}: {}\r\n", key, val).as_bytes());
self.buf.extend_from_slice(
format!("{}: {}\r\n", key, val).as_bytes()
);
}
self.buf.extend_from_slice(b"\r\n");
self.buf.extend_from_slice(&self.body);
@ -135,7 +163,7 @@ impl Response {
403 => "Forbidden",
404 => "Not Found",
500 => "Internal Server Error",
_ => "Unknown Error",
_ => "Unknown Error",
};
format!("HTTP/1.1 {} {}", self.code, msg)
}
@ -157,15 +185,119 @@ impl fmt::Display for Response {
let csi_pink = Style::color("Pink");
let csi_reset = Style::reset();
write!(
f, "{}{} - -{} [{}] {}\"{} {}\"{} {} {}",
csi_cyan, self.req.addr,
csi_pink, self.time,
csi_blue, self.req.verb, self.req.path,
csi_reset, self.code, self.size
f,
"{}{} - -{} [{}] {}\"{} {}\"{} {} {}",
csi_cyan,
self.req.addr,
csi_pink,
self.time,
csi_blue,
self.req.verb,
self.req.path,
csi_reset,
self.code,
self.size
)
}
}
fn get(req: &Request, res: &mut Response) {
if fs::is_dir(&res.real_path) && !req.path.ends_with('/') {
res.code = 301;
res.mime = "text/html".to_string();
res.headers.insert(
"Location".to_string(),
format!("{}/", req.path),
);
res.body.extend_from_slice(b"<h1>Moved Permanently</h1>\r\n");
} else {
let mut not_found = true;
for autocomplete in
&["", "/index.html", "/index.htm", "/index.txt"]
{
let real_path = format!("{}{}", res.real_path, autocomplete);
if fs::is_dir(&real_path) {
continue;
}
if let Ok(buf) = fs::read_to_bytes(&real_path) {
res.code = 200;
res.mime = content_type(&res.real_path);
let tmp;
res.body.extend_from_slice(
if res.mime.starts_with("text/") {
tmp = String::from_utf8_lossy(&buf).to_string().
replace("\n", "\r\n");
tmp.as_bytes()
} else {
&buf
},
);
not_found = false;
break;
}
}
if not_found {
if let Ok(mut files) = fs::read_dir(&res.real_path) {
res.code = 200;
res.mime = "text/html".to_string();
res.body.extend_from_slice(
format!("<h1>Index of {}</h1>\r\n", req.path).as_bytes()
);
files.sort_by_key(|f| f.name());
for file in files {
let path = format!("{}{}", req.path, file.name());
let link = format!(
"<li><a href=\"{}\">{}</a></li>\n",
path,
file.name()
);
res.body.extend_from_slice(link.as_bytes());
}
} else {
res.code = 404;
res.mime = "text/html".to_string();
res.body.extend_from_slice(b"<h1>Not Found</h1>\r\n");
}
}
}
}
fn put(req: &Request, res: &mut Response) {
if res.real_path.ends_with('/') {
// Write directory
let real_path = res.real_path.trim_end_matches('/');
if fs::exists(real_path) {
res.code = 403;
} else if let Some(handle) = fs::create_dir(real_path) {
syscall::close(handle);
res.code = 200;
} else {
res.code = 500;
}
} else {
// Write file
if fs::write(&res.real_path, &req.body).is_ok() {
res.code = 200;
} else {
res.code = 500;
}
}
res.mime = "text/plain".to_string();
}
fn delete(_req: &Request, res: &mut Response) {
if fs::exists(&res.real_path) {
if fs::delete(&res.real_path).is_ok() {
res.code = 200;
} else {
res.code = 500;
}
} else {
res.code = 404;
}
res.mime = "text/plain".to_string();
}
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
let csi_color = Style::color("Yellow");
let csi_reset = Style::reset();
@ -223,15 +355,19 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
connections.push((tcp_handle, send_queue, keep_alive));
}
println!("{}HTTP Server listening on 0.0.0.0:{}{}", csi_color, port, csi_reset);
println!(
"{}HTTP Server listening on 0.0.0.0:{}{}",
csi_color, port, csi_reset
);
loop {
if sys::console::end_of_text() || sys::console::end_of_transmission() {
if console::end_of_text() || console::end_of_transmission() {
println!();
return Ok(());
}
let time = Instant::from_micros((clock::realtime() * 1000000.0) as i64);
let ms = (clock::realtime() * 1000000.0) as i64;
let time = Instant::from_micros(ms);
iface.poll(time, device, &mut sockets);
for (tcp_handle, send_queue, keep_alive) in &mut connections {
@ -245,100 +381,37 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
None => continue,
};
if socket.may_recv() {
let res = socket.recv(|buffer| {
if let Some(req) = Request::from(endpoint.addr, buffer) {
let res = socket.recv(|buf| {
if let Some(req) = Request::from(endpoint.addr, buf) {
let mut res = Response::new(req.clone());
let sep = if req.path == "/" { "" } else { "/" };
let real_path = format!("{}{}{}", dir, sep, req.path.strip_suffix('/').unwrap_or(&req.path)).replace("//", "/");
res.real_path = format!(
"{}{}{}",
dir,
sep,
req.path.strip_suffix('/').unwrap_or(&req.path)
).replace("//", "/");
match req.verb.as_str() {
"GET" => {
if fs::is_dir(&real_path) && !req.path.ends_with('/') {
res.code = 301;
res.mime = "text/html".to_string();
res.headers.insert("Location".to_string(), format!("{}/", req.path));
res.body.extend_from_slice(b"<h1>Moved Permanently</h1>\r\n");
} else {
let mut not_found = true;
for autocomplete in &["", "/index.html", "/index.htm", "/index.txt"] {
let real_path = format!("{}{}", real_path, autocomplete);
if fs::is_dir(&real_path) {
continue;
}
if let Ok(buf) = fs::read_to_bytes(&real_path) {
res.code = 200;
res.mime = content_type(&real_path);
let tmp;
res.body.extend_from_slice(if res.mime.starts_with("text/") {
tmp = String::from_utf8_lossy(&buf).to_string().replace("\n", "\r\n");
tmp.as_bytes()
} else {
&buf
});
not_found = false;
break;
}
}
if not_found {
if let Ok(mut files) = fs::read_dir(&real_path) {
res.code = 200;
res.mime = "text/html".to_string();
res.body.extend_from_slice(format!("<h1>Index of {}</h1>\r\n", req.path).as_bytes());
files.sort_by_key(|f| f.name());
for file in files {
let path = format!("{}{}", req.path, file.name());
let link = format!("<li><a href=\"{}\">{}</a></li>\n", path, file.name());
res.body.extend_from_slice(link.as_bytes());
}
} else {
res.code = 404;
res.mime = "text/html".to_string();
res.body.extend_from_slice(b"<h1>Not Found</h1>\r\n");
}
}
}
},
get(&req, &mut res)
}
"PUT" if !read_only => {
if real_path.ends_with('/') { // Write directory
let real_path = real_path.trim_end_matches('/');
if fs::exists(real_path) {
res.code = 403;
} else if let Some(handle) = fs::create_dir(real_path) {
syscall::close(handle);
res.code = 200;
} else {
res.code = 500;
}
} else { // Write file
if fs::write(&real_path, &req.body).is_ok() {
res.code = 200;
} else {
res.code = 500;
}
}
res.mime = "text/plain".to_string();
},
put(&req, &mut res)
}
"DELETE" if !read_only => {
if fs::exists(&real_path) {
if fs::delete(&real_path).is_ok() {
res.code = 200;
} else {
res.code = 500;
}
} else {
res.code = 404;
}
res.mime = "text/plain".to_string();
},
delete(&req, &mut res)
}
_ => {
let s = b"<h1>Bad Request</h1>\r\n";
res.body.extend_from_slice(s);
res.code = 400;
res.mime = "text/html".to_string();
res.body.extend_from_slice(b"<h1>Bad Request</h1>\r\n");
},
}
}
res.end();
println!("{}", res);
(buffer.len(), Some(res))
(buf.len(), Some(res))
} else {
(0, None)
}
@ -351,7 +424,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
if socket.can_send() {
if let Some(chunk) = send_queue.pop_front() {
let sent = socket.send_slice(&chunk).expect("Could not send chunk");
let sent = socket.send_slice(&chunk).
expect("Could not send chunk");
debug_assert!(sent == chunk.len());
}
}
@ -398,10 +472,22 @@ fn usage() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} httpd {}<options>{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} httpd {}<options>{1}",
csi_title, csi_reset, csi_option
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-d{1}, {0}--dir <path>{1} Set directory to {0}<path>{1}", csi_option, csi_reset);
println!(" {0}-p{1}, {0}--port <number>{1} Listen to port {0}<number>{1}", csi_option, csi_reset);
println!(" {0}-r{1}, {0}--read-only{1} Set read-only mode", csi_option, csi_reset);
println!(
" {0}-d{1}, {0}--dir <path>{1} Set directory to {0}<path>{1}",
csi_option, csi_reset
);
println!(
" {0}-p{1}, {0}--port <number>{1} Listen to port {0}<number>{1}",
csi_option, csi_reset
);
println!(
" {0}-r{1}, {0}--read-only{1} Set read-only mode",
csi_option, csi_reset
);
}

View File

@ -1,10 +1,10 @@
use crate::{api, sys, usr};
use crate::api::console::Style;
use crate::api::fs;
use crate::api::fs::DeviceType;
use crate::api::io;
use crate::api::process::ExitCode;
use crate::api::syscall;
use crate::{api, sys, usr};
use alloc::format;
use alloc::string::String;
@ -25,7 +25,11 @@ pub fn copy_files(verbose: bool) {
copy_file("/bin/halt", include_bytes!("../../dsk/bin/halt"), verbose);
//copy_file("/bin/hello", include_bytes!("../../dsk/bin/hello"), verbose);
copy_file("/bin/print", include_bytes!("../../dsk/bin/print"), verbose);
copy_file("/bin/reboot", include_bytes!("../../dsk/bin/reboot"), verbose);
copy_file(
"/bin/reboot",
include_bytes!("../../dsk/bin/reboot"),
verbose,
);
copy_file("/bin/sleep", include_bytes!("../../dsk/bin/sleep"), verbose);
create_dir("/dev/clk", verbose); // Clock
@ -39,74 +43,245 @@ pub fn copy_files(verbose: bool) {
create_dev("/dev/net/tcp", DeviceType::TcpSocket, verbose);
create_dev("/dev/net/udp", DeviceType::UdpSocket, verbose);
copy_file("/ini/banner.txt", include_bytes!("../../dsk/ini/banner.txt"), verbose);
copy_file("/ini/boot.sh", include_bytes!("../../dsk/ini/boot.sh"), verbose);
copy_file("/ini/lisp.lsp", include_bytes!("../../dsk/ini/lisp.lsp"), verbose);
copy_file("/ini/shell.sh", include_bytes!("../../dsk/ini/shell.sh"), verbose);
copy_file("/ini/version.txt", include_bytes!("../../dsk/ini/version.txt"), verbose);
copy_file(
"/ini/banner.txt",
include_bytes!("../../dsk/ini/banner.txt"),
verbose,
);
copy_file(
"/ini/boot.sh",
include_bytes!("../../dsk/ini/boot.sh"),
verbose,
);
copy_file(
"/ini/lisp.lsp",
include_bytes!("../../dsk/ini/lisp.lsp"),
verbose,
);
copy_file(
"/ini/shell.sh",
include_bytes!("../../dsk/ini/shell.sh"),
verbose,
);
copy_file(
"/ini/version.txt",
include_bytes!("../../dsk/ini/version.txt"),
verbose,
);
create_dir("/ini/palettes", verbose);
copy_file("/ini/palettes/gruvbox-dark.sh", include_bytes!("../../dsk/ini/palettes/gruvbox-dark.sh"), verbose);
copy_file("/ini/palettes/gruvbox-light.sh", include_bytes!("../../dsk/ini/palettes/gruvbox-light.sh"), verbose);
copy_file(
"/ini/palettes/gruvbox-dark.sh",
include_bytes!("../../dsk/ini/palettes/gruvbox-dark.sh"),
verbose,
);
copy_file(
"/ini/palettes/gruvbox-light.sh",
include_bytes!("../../dsk/ini/palettes/gruvbox-light.sh"),
verbose,
);
create_dir("/ini/fonts", verbose);
//copy_file("/ini/fonts/lat15-terminus-8x16.psf", include_bytes!("../../dsk/ini/fonts/lat15-terminus-8x16.psf"), verbose);
copy_file("/ini/fonts/zap-light-8x16.psf", include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf"), verbose);
copy_file("/ini/fonts/zap-vga-8x16.psf", include_bytes!("../../dsk/ini/fonts/zap-vga-8x16.psf"), verbose);
/*
copy_file(
"/ini/fonts/lat15-terminus-8x16.psf",
include_bytes!("../../dsk/ini/fonts/lat15-terminus-8x16.psf"),
verbose
);
*/
copy_file(
"/ini/fonts/zap-light-8x16.psf",
include_bytes!("../../dsk/ini/fonts/zap-light-8x16.psf"),
verbose,
);
copy_file(
"/ini/fonts/zap-vga-8x16.psf",
include_bytes!("../../dsk/ini/fonts/zap-vga-8x16.psf"),
verbose,
);
create_dir("/lib/lisp", verbose);
copy_file("/lib/lisp/alias.lsp", include_bytes!("../../dsk/lib/lisp/alias.lsp"), verbose);
copy_file("/lib/lisp/core.lsp", include_bytes!("../../dsk/lib/lisp/core.lsp"), verbose);
copy_file("/lib/lisp/file.lsp", include_bytes!("../../dsk/lib/lisp/file.lsp"), verbose);
//copy_file("/lib/lisp/legacy.lsp", include_bytes!("../../dsk/lib/lisp/legacy.lsp"), verbose);
copy_file(
"/lib/lisp/alias.lsp",
include_bytes!("../../dsk/lib/lisp/alias.lsp"),
verbose,
);
copy_file(
"/lib/lisp/core.lsp",
include_bytes!("../../dsk/lib/lisp/core.lsp"),
verbose,
);
copy_file(
"/lib/lisp/file.lsp",
include_bytes!("../../dsk/lib/lisp/file.lsp"),
verbose,
);
/*
copy_file(
"/lib/lisp/legacy.lsp",
include_bytes!("../../dsk/lib/lisp/legacy.lsp"),
verbose
);
*/
copy_file("/tmp/alice.txt", include_bytes!("../../dsk/tmp/alice.txt"), verbose);
copy_file("/tmp/machines.txt", include_bytes!("../../dsk/tmp/machines.txt"), verbose);
copy_file(
"/tmp/alice.txt",
include_bytes!("../../dsk/tmp/alice.txt"),
verbose,
);
copy_file(
"/tmp/machines.txt",
include_bytes!("../../dsk/tmp/machines.txt"),
verbose,
);
create_dir("/tmp/lisp", verbose);
copy_file("/tmp/lisp/colors.lsp", include_bytes!("../../dsk/tmp/lisp/colors.lsp"), verbose);
copy_file("/tmp/lisp/doc.lsp", include_bytes!("../../dsk/tmp/lisp/doc.lsp"), verbose);
copy_file("/tmp/lisp/factorial.lsp", include_bytes!("../../dsk/tmp/lisp/factorial.lsp"), verbose);
//copy_file("/tmp/lisp/fetch.lsp", include_bytes!("../../dsk/tmp/lisp/fetch.lsp"), verbose);
copy_file("/tmp/lisp/fibonacci.lsp", include_bytes!("../../dsk/tmp/lisp/fibonacci.lsp"), verbose);
copy_file("/tmp/lisp/geotime.lsp", include_bytes!("../../dsk/tmp/lisp/geotime.lsp"), verbose);
copy_file("/tmp/lisp/ntp.lsp", include_bytes!("../../dsk/tmp/lisp/ntp.lsp"), verbose);
copy_file("/tmp/lisp/pi.lsp", include_bytes!("../../dsk/tmp/lisp/pi.lsp"), verbose);
copy_file("/tmp/lisp/sum.lsp", include_bytes!("../../dsk/tmp/lisp/sum.lsp"), verbose);
copy_file(
"/tmp/lisp/colors.lsp",
include_bytes!("../../dsk/tmp/lisp/colors.lsp"),
verbose,
);
copy_file(
"/tmp/lisp/doc.lsp",
include_bytes!("../../dsk/tmp/lisp/doc.lsp"),
verbose,
);
copy_file(
"/tmp/lisp/factorial.lsp",
include_bytes!("../../dsk/tmp/lisp/factorial.lsp"),
verbose,
);
/*
copy_file(
"/tmp/lisp/fetch.lsp",
include_bytes!("../../dsk/tmp/lisp/fetch.lsp"),
verbose
);
*/
copy_file(
"/tmp/lisp/fibonacci.lsp",
include_bytes!("../../dsk/tmp/lisp/fibonacci.lsp"),
verbose,
);
copy_file(
"/tmp/lisp/geotime.lsp",
include_bytes!("../../dsk/tmp/lisp/geotime.lsp"),
verbose,
);
copy_file(
"/tmp/lisp/ntp.lsp",
include_bytes!("../../dsk/tmp/lisp/ntp.lsp"),
verbose,
);
copy_file(
"/tmp/lisp/pi.lsp",
include_bytes!("../../dsk/tmp/lisp/pi.lsp"),
verbose,
);
copy_file(
"/tmp/lisp/sum.lsp",
include_bytes!("../../dsk/tmp/lisp/sum.lsp"),
verbose,
);
create_dir("/tmp/life", verbose);
copy_file("/tmp/life/centinal.cells", include_bytes!("../../dsk/tmp/life/centinal.cells"), verbose);
copy_file("/tmp/life/flower-of-eden.cells", include_bytes!("../../dsk/tmp/life/flower-of-eden.cells"), verbose);
copy_file("/tmp/life/garden-of-eden.cells", include_bytes!("../../dsk/tmp/life/garden-of-eden.cells"), verbose);
copy_file("/tmp/life/glider-gun.cells", include_bytes!("../../dsk/tmp/life/glider-gun.cells"), verbose);
copy_file("/tmp/life/pentadecathlon.cells", include_bytes!("../../dsk/tmp/life/pentadecathlon.cells"), verbose);
copy_file("/tmp/life/queen-bee-shuttle.cells", include_bytes!("../../dsk/tmp/life/queen-bee-shuttle.cells"), verbose);
copy_file("/tmp/life/ship-in-a-bottle.cells", include_bytes!("../../dsk/tmp/life/ship-in-a-bottle.cells"), verbose);
copy_file("/tmp/life/thunderbird.cells", include_bytes!("../../dsk/tmp/life/thunderbird.cells"), verbose);
copy_file("/tmp/life/wing.cells", include_bytes!("../../dsk/tmp/life/wing.cells"), verbose);
copy_file(
"/tmp/life/centinal.cells",
include_bytes!("../../dsk/tmp/life/centinal.cells"),
verbose,
);
copy_file(
"/tmp/life/flower-of-eden.cells",
include_bytes!("../../dsk/tmp/life/flower-of-eden.cells"),
verbose,
);
copy_file(
"/tmp/life/garden-of-eden.cells",
include_bytes!("../../dsk/tmp/life/garden-of-eden.cells"),
verbose,
);
copy_file(
"/tmp/life/glider-gun.cells",
include_bytes!("../../dsk/tmp/life/glider-gun.cells"),
verbose,
);
copy_file(
"/tmp/life/pentadecathlon.cells",
include_bytes!("../../dsk/tmp/life/pentadecathlon.cells"),
verbose,
);
copy_file(
"/tmp/life/queen-bee-shuttle.cells",
include_bytes!("../../dsk/tmp/life/queen-bee-shuttle.cells"),
verbose,
);
copy_file(
"/tmp/life/ship-in-a-bottle.cells",
include_bytes!("../../dsk/tmp/life/ship-in-a-bottle.cells"),
verbose,
);
copy_file(
"/tmp/life/thunderbird.cells",
include_bytes!("../../dsk/tmp/life/thunderbird.cells"),
verbose,
);
copy_file(
"/tmp/life/wing.cells",
include_bytes!("../../dsk/tmp/life/wing.cells"),
verbose,
);
create_dir("/tmp/beep", verbose);
copy_file("/tmp/beep/tetris.sh", include_bytes!("../../dsk/tmp/beep/tetris.sh"), verbose);
copy_file("/tmp/beep/starwars.sh", include_bytes!("../../dsk/tmp/beep/starwars.sh"), verbose);
copy_file("/tmp/beep/mario.sh", include_bytes!("../../dsk/tmp/beep/mario.sh"), verbose);
copy_file(
"/tmp/beep/tetris.sh",
include_bytes!("../../dsk/tmp/beep/tetris.sh"),
verbose,
);
copy_file(
"/tmp/beep/starwars.sh",
include_bytes!("../../dsk/tmp/beep/starwars.sh"),
verbose,
);
copy_file(
"/tmp/beep/mario.sh",
include_bytes!("../../dsk/tmp/beep/mario.sh"),
verbose,
);
create_dir("/var/www", verbose);
copy_file("/var/www/index.html", include_bytes!("../../dsk/var/www/index.html"), verbose);
copy_file("/var/www/moros.css", include_bytes!("../../dsk/var/www/moros.css"), verbose);
copy_file("/var/www/moros.png", include_bytes!("../../dsk/var/www/moros.png"), verbose);
copy_file(
"/var/www/index.html",
include_bytes!("../../dsk/var/www/index.html"),
verbose,
);
copy_file(
"/var/www/moros.css",
include_bytes!("../../dsk/var/www/moros.css"),
verbose,
);
copy_file(
"/var/www/moros.png",
include_bytes!("../../dsk/var/www/moros.png"),
verbose,
);
}
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
let csi_color = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Welcome to MOROS v{} installation program!{}", csi_color, env!("CARGO_PKG_VERSION"), csi_reset);
println!(
"{}Welcome to MOROS v{} installation program!{}",
csi_color,
env!("CARGO_PKG_VERSION"),
csi_reset
);
println!();
let mut has_confirmed = false;
for &arg in args {
match arg {
"-y" | "--yes" => has_confirmed = true,
_ => continue
_ => continue,
}
}
if !has_confirmed {

View File

@ -1,6 +1,6 @@
use crate::sys;
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::sys;
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
if args.len() == 1 {
@ -37,8 +37,14 @@ fn help() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} keyboard {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} keyboard {}<command>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Commands:{}", csi_title, csi_reset);
println!(" {0}set <layout>{1} Set keyboard layout", csi_option, csi_reset);
println!(
" {0}set <layout>{1} Set keyboard layout",
csi_option, csi_reset
);
}

View File

@ -3,6 +3,7 @@ use crate::api::fs;
use crate::api::process::ExitCode;
use crate::api::random;
use crate::sys;
use crate::sys::console;
use alloc::collections::BTreeSet;
use alloc::format;
@ -43,7 +44,7 @@ impl Game {
let cell = (x as i64, y as i64);
match c {
' ' | '.' | '0' => self.grid.remove(&cell),
_ => self.grid.insert(cell),
_ => self.grid.insert(cell),
};
}
}
@ -55,8 +56,8 @@ impl Game {
if self.seed_interval > 0 && self.step % self.seed_interval == 0 {
self.seed();
}
if sys::console::end_of_text() || (self.is_game_over() && self.quiet) {
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move cursor to top
if console::end_of_text() || (self.is_game_over() && self.quiet) {
print!("\x1b[2J\x1b[1;1H"); // Clear screen and move to top
return;
}
print!("{}", self);
@ -74,7 +75,10 @@ impl Game {
for x in 0..self.cols {
for y in 0..self.rows {
let cell = (x, y);
match neighboors(&cell).iter().fold(0, |s, c| s + self.grid.contains(c) as u8) {
let n = neighboors(&cell).iter().fold(0, |s, c|
s + self.grid.contains(c) as u8
);
match n {
2 => continue,
3 => cells_to_insert.push(cell),
_ => cells_to_remove.push(cell),
@ -118,15 +122,20 @@ impl Game {
let reset = Style::reset();
let stats = format!("GEN: {:04} | POP: {:04}", gen, pop);
let size = (self.cols as usize) - stats.len();
format!("\n{}{:n$}{}{}", color, title, stats, reset, n=size)
format!("\n{}{:n$}{}{}", color, title, stats, reset, n = size)
}
}
fn neighboors(&(x, y): &(i64, i64)) -> Vec<(i64, i64)> {
vec![
(x - 1, y - 1), (x, y - 1), (x + 1, y - 1),
(x - 1, y), (x + 1, y),
(x - 1, y + 1), (x, y + 1), (x + 1, y + 1),
(x - 1, y - 1),
(x, y - 1),
(x + 1, y - 1),
(x - 1, y),
(x + 1, y),
(x - 1, y + 1),
(x, y + 1),
(x + 1, y + 1),
]
}
@ -135,7 +144,11 @@ impl fmt::Display for Game {
let mut out = String::new();
for y in 0..self.rows {
for x in 0..self.cols {
out.push(if self.grid.contains(&(x, y)) { '#' } else { ' ' });
out.push(if self.grid.contains(&(x, y)) {
'#'
} else {
' '
});
}
if y < self.rows - 1 {
out.push('\n');
@ -168,7 +181,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
"-p" | "--population" => {
if i + 1 < n {
game.seed_population = args[i + 1].parse().unwrap_or(game.seed_population);
game.seed_population = args[i + 1].parse().
unwrap_or(game.seed_population);
i += 1;
} else {
error!("Missing --population <num>");
@ -177,7 +191,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
"-i" | "--interval" => {
if i + 1 < n {
game.seed_interval = args[i + 1].parse().unwrap_or(game.seed_interval);
game.seed_interval = args[i + 1].parse().
unwrap_or(game.seed_interval);
i += 1;
} else {
error!("Missing --interval <num>");
@ -229,11 +244,30 @@ fn usage() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} life {}<options> [<path>]{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} life {}<options> [<path>]{1}",
csi_title, csi_reset, csi_option
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-p{1}, {0}--population <num>{1} Set the seed population to {0}<num>{1}", csi_option, csi_reset);
println!(" {0}-i{1}, {0}--interval <num>{1} Set the seed interval to {0}<num>{1}", csi_option, csi_reset);
println!(" {0}-s{1}, {0}--speed <num>{1} Set the simulation speed to {0}<num>{1}", csi_option, csi_reset);
println!(" {0}-q{1}, {0}--quiet{1} Enable quiet mode", csi_option, csi_reset);
println!(
" {0}-p{1}, {0}--population <num>{1} \
Set the seed population to {0}<num>{1}",
csi_option, csi_reset
);
println!(
" {0}-i{1}, {0}--interval <num>{1} \
Set the seed interval to {0}<num>{1}",
csi_option, csi_reset
);
println!(
" {0}-s{1}, {0}--speed <num>{1} \
Set the simulation speed to {0}<num>{1}",
csi_option, csi_reset
);
println!(
" {0}-q{1}, {0}--quiet{1} \
Enable quiet mode",
csi_option, csi_reset
);
}

View File

@ -1,7 +1,7 @@
use super::FUNCTIONS;
use super::primitive;
use super::eval::BUILT_INS;
use super::eval::eval_args;
use super::eval::BUILT_INS;
use super::primitive;
use super::FUNCTIONS;
use super::{Err, Exp, Number};
use crate::{could_not, expected};
@ -23,69 +23,230 @@ pub struct Env {
pub fn default_env() -> Rc<RefCell<Env>> {
let mut data: BTreeMap<String, Exp> = BTreeMap::new();
data.insert("pi".to_string(), Exp::Num(Number::from(PI)));
data.insert("=".to_string(), Exp::Primitive(primitive::lisp_eq));
data.insert(">".to_string(), Exp::Primitive(primitive::lisp_gt));
data.insert(">=".to_string(), Exp::Primitive(primitive::lisp_gte));
data.insert("<".to_string(), Exp::Primitive(primitive::lisp_lt));
data.insert("<=".to_string(), Exp::Primitive(primitive::lisp_lte));
data.insert("*".to_string(), Exp::Primitive(primitive::lisp_mul));
data.insert("+".to_string(), Exp::Primitive(primitive::lisp_add));
data.insert("-".to_string(), Exp::Primitive(primitive::lisp_sub));
data.insert("/".to_string(), Exp::Primitive(primitive::lisp_div));
data.insert("^".to_string(), Exp::Primitive(primitive::lisp_exp));
data.insert("<<".to_string(), Exp::Primitive(primitive::lisp_shl));
data.insert(">>".to_string(), Exp::Primitive(primitive::lisp_shr));
data.insert("rem".to_string(), Exp::Primitive(primitive::lisp_rem));
data.insert("cos".to_string(), Exp::Primitive(primitive::lisp_cos));
data.insert("acos".to_string(), Exp::Primitive(primitive::lisp_acos));
data.insert("asin".to_string(), Exp::Primitive(primitive::lisp_asin));
data.insert("atan".to_string(), Exp::Primitive(primitive::lisp_atan));
data.insert("sin".to_string(), Exp::Primitive(primitive::lisp_sin));
data.insert("tan".to_string(), Exp::Primitive(primitive::lisp_tan));
data.insert("trunc".to_string(), Exp::Primitive(primitive::lisp_trunc));
data.insert("shell".to_string(), Exp::Primitive(primitive::lisp_shell));
data.insert("string".to_string(), Exp::Primitive(primitive::lisp_string));
data.insert("string->binary".to_string(), Exp::Primitive(primitive::lisp_string_binary));
data.insert("binary->string".to_string(), Exp::Primitive(primitive::lisp_binary_string));
data.insert("binary->number".to_string(), Exp::Primitive(primitive::lisp_binary_number));
data.insert("number->binary".to_string(), Exp::Primitive(primitive::lisp_number_binary));
data.insert("number->string".to_string(), Exp::Primitive(primitive::lisp_number_string));
data.insert("string->number".to_string(), Exp::Primitive(primitive::lisp_string_number));
data.insert("type".to_string(), Exp::Primitive(primitive::lisp_type));
data.insert("parse".to_string(), Exp::Primitive(primitive::lisp_parse));
data.insert("list".to_string(), Exp::Primitive(primitive::lisp_list));
data.insert("sort".to_string(), Exp::Primitive(primitive::lisp_sort));
data.insert("unique".to_string(), Exp::Primitive(primitive::lisp_unique));
data.insert("contains?".to_string(), Exp::Primitive(primitive::lisp_contains));
data.insert("slice".to_string(), Exp::Primitive(primitive::lisp_slice));
data.insert("chunks".to_string(), Exp::Primitive(primitive::lisp_chunks));
data.insert("length".to_string(), Exp::Primitive(primitive::lisp_length));
data.insert("concat".to_string(), Exp::Primitive(primitive::lisp_concat));
data.insert("number/type".to_string(), Exp::Primitive(primitive::lisp_number_type));
data.insert("regex/find".to_string(), Exp::Primitive(primitive::lisp_regex_find));
data.insert("string/split".to_string(), Exp::Primitive(primitive::lisp_string_split));
data.insert("string/trim".to_string(), Exp::Primitive(primitive::lisp_string_trim));
data.insert("file/size".to_string(), Exp::Primitive(primitive::lisp_file_size));
data.insert("file/open".to_string(), Exp::Primitive(primitive::lisp_file_open));
data.insert("file/read".to_string(), Exp::Primitive(primitive::lisp_file_read));
data.insert("file/write".to_string(), Exp::Primitive(primitive::lisp_file_write));
data.insert("file/close".to_string(), Exp::Primitive(primitive::lisp_file_close));
data.insert("socket/connect".to_string(), Exp::Primitive(primitive::lisp_socket_connect));
data.insert("socket/listen".to_string(), Exp::Primitive(primitive::lisp_socket_listen));
data.insert("socket/accept".to_string(), Exp::Primitive(primitive::lisp_socket_accept));
data.insert("host".to_string(), Exp::Primitive(primitive::lisp_host));
data.insert("dict".to_string(), Exp::Primitive(primitive::lisp_dict));
data.insert("get".to_string(), Exp::Primitive(primitive::lisp_get));
data.insert("put".to_string(), Exp::Primitive(primitive::lisp_put));
data.insert(
"pi".to_string(),
Exp::Num(Number::from(PI)),
);
data.insert(
"=".to_string(),
Exp::Primitive(primitive::lisp_eq),
);
data.insert(
">".to_string(),
Exp::Primitive(primitive::lisp_gt),
);
data.insert(
">=".to_string(),
Exp::Primitive(primitive::lisp_gte),
);
data.insert(
"<".to_string(),
Exp::Primitive(primitive::lisp_lt),
);
data.insert(
"<=".to_string(),
Exp::Primitive(primitive::lisp_lte),
);
data.insert(
"*".to_string(),
Exp::Primitive(primitive::lisp_mul),
);
data.insert(
"+".to_string(),
Exp::Primitive(primitive::lisp_add),
);
data.insert(
"-".to_string(),
Exp::Primitive(primitive::lisp_sub),
);
data.insert(
"/".to_string(),
Exp::Primitive(primitive::lisp_div),
);
data.insert(
"^".to_string(),
Exp::Primitive(primitive::lisp_exp),
);
data.insert(
"<<".to_string(),
Exp::Primitive(primitive::lisp_shl),
);
data.insert(
">>".to_string(),
Exp::Primitive(primitive::lisp_shr),
);
data.insert(
"rem".to_string(),
Exp::Primitive(primitive::lisp_rem),
);
data.insert(
"cos".to_string(),
Exp::Primitive(primitive::lisp_cos),
);
data.insert(
"acos".to_string(),
Exp::Primitive(primitive::lisp_acos),
);
data.insert(
"asin".to_string(),
Exp::Primitive(primitive::lisp_asin),
);
data.insert(
"atan".to_string(),
Exp::Primitive(primitive::lisp_atan),
);
data.insert(
"sin".to_string(),
Exp::Primitive(primitive::lisp_sin),
);
data.insert(
"tan".to_string(),
Exp::Primitive(primitive::lisp_tan),
);
data.insert(
"trunc".to_string(),
Exp::Primitive(primitive::lisp_trunc),
);
data.insert(
"shell".to_string(),
Exp::Primitive(primitive::lisp_shell),
);
data.insert(
"string".to_string(),
Exp::Primitive(primitive::lisp_string),
);
data.insert(
"string->binary".to_string(),
Exp::Primitive(primitive::lisp_string_binary),
);
data.insert(
"binary->string".to_string(),
Exp::Primitive(primitive::lisp_binary_string),
);
data.insert(
"binary->number".to_string(),
Exp::Primitive(primitive::lisp_binary_number),
);
data.insert(
"number->binary".to_string(),
Exp::Primitive(primitive::lisp_number_binary),
);
data.insert(
"number->string".to_string(),
Exp::Primitive(primitive::lisp_number_string),
);
data.insert(
"string->number".to_string(),
Exp::Primitive(primitive::lisp_string_number),
);
data.insert(
"type".to_string(),
Exp::Primitive(primitive::lisp_type),
);
data.insert(
"parse".to_string(),
Exp::Primitive(primitive::lisp_parse),
);
data.insert(
"list".to_string(),
Exp::Primitive(primitive::lisp_list),
);
data.insert(
"sort".to_string(),
Exp::Primitive(primitive::lisp_sort),
);
data.insert(
"unique".to_string(),
Exp::Primitive(primitive::lisp_unique),
);
data.insert(
"contains?".to_string(),
Exp::Primitive(primitive::lisp_contains),
);
data.insert(
"slice".to_string(),
Exp::Primitive(primitive::lisp_slice),
);
data.insert(
"chunks".to_string(),
Exp::Primitive(primitive::lisp_chunks),
);
data.insert(
"length".to_string(),
Exp::Primitive(primitive::lisp_length),
);
data.insert(
"concat".to_string(),
Exp::Primitive(primitive::lisp_concat),
);
data.insert(
"number/type".to_string(),
Exp::Primitive(primitive::lisp_number_type),
);
data.insert(
"regex/find".to_string(),
Exp::Primitive(primitive::lisp_regex_find),
);
data.insert(
"string/split".to_string(),
Exp::Primitive(primitive::lisp_string_split),
);
data.insert(
"string/trim".to_string(),
Exp::Primitive(primitive::lisp_string_trim),
);
data.insert(
"file/size".to_string(),
Exp::Primitive(primitive::lisp_file_size),
);
data.insert(
"file/open".to_string(),
Exp::Primitive(primitive::lisp_file_open),
);
data.insert(
"file/read".to_string(),
Exp::Primitive(primitive::lisp_file_read),
);
data.insert(
"file/write".to_string(),
Exp::Primitive(primitive::lisp_file_write),
);
data.insert(
"file/close".to_string(),
Exp::Primitive(primitive::lisp_file_close),
);
data.insert(
"socket/connect".to_string(),
Exp::Primitive(primitive::lisp_socket_connect),
);
data.insert(
"socket/listen".to_string(),
Exp::Primitive(primitive::lisp_socket_listen),
);
data.insert(
"socket/accept".to_string(),
Exp::Primitive(primitive::lisp_socket_accept),
);
data.insert(
"host".to_string(),
Exp::Primitive(primitive::lisp_host),
);
data.insert(
"dict".to_string(),
Exp::Primitive(primitive::lisp_dict),
);
data.insert(
"get".to_string(),
Exp::Primitive(primitive::lisp_get),
);
data.insert(
"put".to_string(),
Exp::Primitive(primitive::lisp_put),
);
// Setup autocompletion
*FUNCTIONS.lock() = data.keys().cloned().chain(BUILT_INS.map(String::from)).collect();
*FUNCTIONS.lock() = data.keys().cloned().
chain(BUILT_INS.map(String::from)).collect();
Rc::new(RefCell::new(Env { data, outer: None }))
}
@ -103,34 +264,42 @@ pub fn env_get(key: &str, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
let env = env.borrow_mut();
match env.data.get(key) {
Some(exp) => Ok(exp.clone()),
None => {
match &env.outer {
Some(outer_env) => env_get(key, outer_env),
None => could_not!("find symbol '{}'", key),
}
}
None => match &env.outer {
Some(outer_env) => env_get(key, outer_env),
None => could_not!("find symbol '{}'", key),
},
}
}
pub fn env_set(key: &str, val: Exp, env: &Rc<RefCell<Env>>) -> Result<Exp, Err> {
pub fn env_set(
key: &str,
val: Exp,
env: &Rc<RefCell<Env>>
) -> Result<Exp, Err> {
let mut env = env.borrow_mut();
match env.data.get(key) {
Some(_) => {
env.data.insert(key.to_string(), val.clone());
Ok(val)
}
None => {
match &env.outer {
Some(outer_env) => env_set(key, val, outer_env),
None => could_not!("find symbol '{}'", key),
}
}
None => match &env.outer {
Some(outer_env) => env_set(key, val, outer_env),
None => could_not!("find symbol '{}'", key),
},
}
}
enum InnerEnv { Function, Macro }
enum InnerEnv {
Function,
Macro,
}
fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
fn inner_env(
kind: InnerEnv,
params: &Exp,
args: &[Exp],
outer: &mut Rc<RefCell<Env>>,
) -> Result<Rc<RefCell<Env>>, Err> {
let mut args = match kind {
InnerEnv::Function => eval_args(args, outer)?,
InnerEnv::Macro => args.to_vec(),
@ -177,13 +346,24 @@ fn inner_env(kind: InnerEnv, params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<
}
_ => return expected!("params to be a list"),
}
Ok(Rc::new(RefCell::new(Env { data, outer: Some(Rc::new(RefCell::new(outer.borrow_mut().clone()))) })))
Ok(Rc::new(RefCell::new(Env {
data,
outer: Some(Rc::new(RefCell::new(outer.borrow_mut().clone()))),
})))
}
pub fn function_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
pub fn function_env(
params: &Exp,
args: &[Exp],
outer: &mut Rc<RefCell<Env>>,
) -> Result<Rc<RefCell<Env>>, Err> {
inner_env(InnerEnv::Function, params, args, outer)
}
pub fn macro_env(params: &Exp, args: &[Exp], outer: &mut Rc<RefCell<Env>>) -> Result<Rc<RefCell<Env>>, Err> {
pub fn macro_env(
params: &Exp,
args: &[Exp],
outer: &mut Rc<RefCell<Env>>,
) -> Result<Rc<RefCell<Env>>, Err> {
inner_env(InnerEnv::Macro, params, args, outer)
}

View File

@ -1,18 +1,18 @@
use super::{Err, Exp, Env, Function, parse_eval};
use super::env::{env_keys, env_get, env_set, function_env};
use super::env::{env_get, env_keys, env_set, function_env};
use super::expand::expand;
use crate::could_not;
use super::string;
use super::{parse_eval, Env, Err, Exp, Function};
use crate::could_not;
use crate::{ensure_length_eq, ensure_length_gt, expected};
use crate::api::fs;
use crate::{ensure_length_eq, ensure_length_gt, expected};
use alloc::boxed::Box;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::vec;
use alloc::vec::Vec;
use core::cell::RefCell;
fn eval_quote_args(args: &[Exp]) -> Result<Exp, Err> {
@ -20,55 +20,73 @@ fn eval_quote_args(args: &[Exp]) -> Result<Exp, Err> {
Ok(args[0].clone())
}
fn eval_atom_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_atom_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
match eval(&args[0], env)? {
Exp::List(_) => Ok(Exp::Bool(false)),
_ => Ok(Exp::Bool(true)),
_ => Ok(Exp::Bool(true)),
}
}
fn eval_equal_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_equal_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
let a = eval(&args[0], env)?;
let b = eval(&args[1], env)?;
Ok(Exp::Bool(a == b))
}
fn eval_head_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_head_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
match eval(&args[0], env)? {
Exp::List(list) => {
ensure_length_gt!(list, 0);
Ok(list[0].clone())
},
}
_ => expected!("first argument to be a list"),
}
}
fn eval_tail_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_tail_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
match eval(&args[0], env)? {
Exp::List(list) => {
ensure_length_gt!(list, 0);
Ok(Exp::List(list[1..].to_vec()))
},
}
_ => expected!("first argument to be a list"),
}
}
fn eval_cons_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_cons_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match eval(&args[1], env)? {
Exp::List(mut list) => {
list.insert(0, eval(&args[0], env)?);
Ok(Exp::List(list))
},
}
_ => expected!("first argument to be a list"),
}
}
pub fn eval_variable_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
pub fn eval_variable_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match &args[0] {
Exp::Sym(name) => {
@ -76,28 +94,37 @@ pub fn eval_variable_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Ex
env.borrow_mut().data.insert(name.clone(), exp);
Ok(Exp::Sym(name.clone()))
}
_ => expected!("first argument to be a symbol")
_ => expected!("first argument to be a symbol"),
}
}
fn eval_set_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_set_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match &args[0] {
Exp::Sym(name) => {
let exp = eval(&args[1], env)?;
Ok(env_set(name, exp, env)?)
}
_ => expected!("first argument to be a symbol")
_ => expected!("first argument to be a symbol"),
}
}
fn eval_env_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_env_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 0);
let keys = env_keys(env)?.iter().map(|k| Exp::Sym(k.clone())).collect();
Ok(Exp::List(keys))
}
fn eval_while_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_while_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_gt!(args, 1);
let cond = &args[0];
let mut res = Exp::List(vec![]);
@ -109,7 +136,10 @@ fn eval_while_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
Ok(res)
}
fn eval_apply_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_apply_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_gt!(args, 1);
let mut args = args.to_vec();
match eval(&args.pop().unwrap(), env) {
@ -119,13 +149,19 @@ fn eval_apply_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
eval(&Exp::List(args.to_vec()), env)
}
fn eval_eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_eval_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let exp = eval(&args[0], env)?;
eval(&exp, env)
}
fn eval_do_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_do_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
let mut res = Ok(Exp::List(vec![]));
for arg in args {
res = Ok(eval(arg, env)?);
@ -133,10 +169,14 @@ fn eval_do_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
res
}
fn eval_load_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_load_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let path = string(&eval(&args[0], env)?)?;
let mut input = fs::read_to_string(&path).or(could_not!("find file '{}'", path))?;
let mut input = fs::read_to_string(&path).
or(could_not!("find file '{}'", path))?;
loop {
let (rest, _) = parse_eval(&input, env)?;
if rest.is_empty() {
@ -147,7 +187,10 @@ fn eval_load_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err>
Ok(Exp::Bool(true))
}
fn eval_doc_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
fn eval_doc_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
match eval(&args[0], env)? {
Exp::Primitive(_) => Ok(Exp::Str("".to_string())),
@ -157,23 +200,40 @@ fn eval_doc_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
}
}
pub fn eval_args(args: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Vec<Exp>, Err> {
pub fn eval_args(
args: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Vec<Exp>, Err> {
args.iter().map(|x| eval(x, env)).collect()
}
pub const BUILT_INS: [&str; 26] = [
"quote", "quasiquote", "unquote", "unquote-splicing",
"atom?", "equal?", "head", "tail", "cons",
"if", "cond", "while",
"variable", "function", "macro",
"define-function", "define",
"quote",
"quasiquote",
"unquote",
"unquote-splicing",
"atom?",
"equal?",
"head",
"tail",
"cons",
"if",
"cond",
"while",
"variable",
"function",
"macro",
"define-function",
"define",
"define-macro",
"set",
"apply", "eval", "expand",
"apply",
"eval",
"expand",
"do",
"load",
"doc",
"env"
"env",
];
pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
@ -191,62 +251,104 @@ pub fn eval(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_gt!(list, 0);
let args = &list[1..];
match &list[0] {
Exp::Sym(s) if s == "quote" => return eval_quote_args(args),
Exp::Sym(s) if s == "atom?" => return eval_atom_args(args, env),
Exp::Sym(s) if s == "equal?" => return eval_equal_args(args, env),
Exp::Sym(s) if s == "head" => return eval_head_args(args, env),
Exp::Sym(s) if s == "tail" => return eval_tail_args(args, env),
Exp::Sym(s) if s == "cons" => return eval_cons_args(args, env),
Exp::Sym(s) if s == "set" => return eval_set_args(args, env),
Exp::Sym(s) if s == "while" => return eval_while_args(args, env),
Exp::Sym(s) if s == "apply" => return eval_apply_args(args, env),
Exp::Sym(s) if s == "eval" => return eval_eval_args(args, env),
Exp::Sym(s) if s == "do" => return eval_do_args(args, env),
Exp::Sym(s) if s == "load" => return eval_load_args(args, env),
Exp::Sym(s) if s == "doc" => return eval_doc_args(args, env),
Exp::Sym(s) if s == "variable" => return eval_variable_args(args, env),
Exp::Sym(s) if s == "env" => return eval_env_args(args, env),
Exp::Sym(s) if s == "expand" => {
Exp::Sym(s) if s == "quote" => {
return eval_quote_args(args);
}
Exp::Sym(s) if s == "atom?" => {
return eval_atom_args(args, env);
}
Exp::Sym(s) if s == "equal?" => {
return eval_equal_args(args, env);
}
Exp::Sym(s) if s == "head" => {
return eval_head_args(args, env);
}
Exp::Sym(s) if s == "tail" => {
return eval_tail_args(args, env);
}
Exp::Sym(s) if s == "cons" => {
return eval_cons_args(args, env);
}
Exp::Sym(s) if s == "set" => {
return eval_set_args(args, env);
}
Exp::Sym(s) if s == "while" => {
return eval_while_args(args, env);
}
Exp::Sym(s) if s == "apply" => {
return eval_apply_args(args, env);
}
Exp::Sym(s) if s == "eval" => {
return eval_eval_args(args, env);
}
Exp::Sym(s) if s == "do" => {
return eval_do_args(args, env);
}
Exp::Sym(s) if s == "load" => {
return eval_load_args(args, env);
}
Exp::Sym(s) if s == "doc" => {
return eval_doc_args(args, env);
}
Exp::Sym(s) if s == "variable" => {
return eval_variable_args(args, env);
}
Exp::Sym(s) if s == "env" => {
return eval_env_args(args, env);
}
Exp::Sym(s) if s == "expand" => {
ensure_length_eq!(args, 1);
return expand(&args[0], env);
}
Exp::Sym(s) if s == "if" => {
ensure_length_gt!(args, 1);
if eval(&args[0], env)?.is_truthy() { // consequent
if eval(&args[0], env)?.is_truthy() { // Consequent
exp_tmp = args[1].clone();
} else if args.len() > 2 { // alternate
} else if args.len() > 2 { // Alternate
exp_tmp = args[2].clone();
} else { // '()
exp_tmp = Exp::List(vec![Exp::Sym("quote".to_string()), Exp::List(vec![])]);
exp_tmp = Exp::List(vec![
Exp::Sym("quote".to_string()),
Exp::List(vec![]),
]);
}
exp = &exp_tmp;
}
Exp::Sym(s) if s == "function" || s == "macro" => {
let (params, body, doc) = match args.len() {
2 => (args[0].clone(), args[1].clone(), None),
3 => (args[0].clone(), args[2].clone(), Some(string(&args[1])?)),
2 => {
(args[0].clone(), args[1].clone(), None)
}
3 => {
let doc = Some(string(&args[1])?);
(args[0].clone(), args[2].clone(), doc)
}
_ => return expected!("3 or 4 arguments"),
};
let f = Box::new(Function { params, body, doc });
let exp = if s == "function" { Exp::Function(f) } else { Exp::Macro(f) };
let exp = if s == "function" {
Exp::Function(f)
} else {
Exp::Macro(f)
};
return Ok(exp);
}
_ => {
match eval(&list[0], env)? {
Exp::Function(f) => {
env_tmp = function_env(&f.params, args, env)?;
exp_tmp = f.body;
env = &mut env_tmp;
exp = &exp_tmp;
},
Exp::Primitive(f) => {
return f(&eval_args(args, env)?)
},
_ => return expected!("first argument to be a function"),
_ => match eval(&list[0], env)? {
Exp::Function(f) => {
env_tmp = function_env(&f.params, args, env)?;
exp_tmp = f.body;
env = &mut env_tmp;
exp = &exp_tmp;
}
}
Exp::Primitive(f) => {
return f(&eval_args(args, env)?);
}
_ => {
return expected!("first argument to be a function");
}
},
}
},
}
_ => return Err(Err::Reason("Unexpected argument".to_string())),
}
}

View File

@ -1,45 +1,50 @@
use super::{Err, Exp, Env};
use super::env::{env_get, macro_env};
use super::eval::eval;
use super::{Env, Err, Exp};
use crate::{ensure_length_eq, ensure_length_gt, ensure_list, ensure_string, expected};
use crate::{
ensure_length_eq, ensure_length_gt, ensure_list, ensure_string, expected
};
use alloc::format;
use alloc::rc::Rc;
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::vec;
use alloc::vec::Vec;
use core::cell::RefCell;
fn is_sym(e: &Exp, name: &str) -> bool {
*e == Exp::Sym(name.to_string())
}
pub fn expand_quasiquote(exp: &Exp) -> Result<Exp, Err> {
match exp {
Exp::List(list) if list.len() > 0 => {
match &list[0] {
Exp::Sym(s) if s == "unquote" => {
Ok(list[1].clone())
}
Exp::List(l) if l.len() == 2 && l[0] == Exp::Sym("unquote-splice".to_string()) => {
Ok(Exp::List(vec![
Exp::Sym("concat".to_string()),
l[1].clone(),
expand_quasiquote(&Exp::List(list[1..].to_vec()))?
]))
}
_ => {
Ok(Exp::List(vec![
Exp::Sym("cons".to_string()),
expand_quasiquote(&list[0])?,
expand_quasiquote(&Exp::List(list[1..].to_vec()))?,
]))
}
Exp::List(list) if list.len() > 0 => match &list[0] {
Exp::Sym(s) if s == "unquote" => Ok(list[1].clone()),
Exp::List(l) if l.len() == 2 && is_sym(&l[0], "unquote-splice") => {
Ok(Exp::List(vec![
Exp::Sym("concat".to_string()),
l[1].clone(),
expand_quasiquote(&Exp::List(list[1..].to_vec()))?,
]))
}
}
_ => Ok(Exp::List(vec![
Exp::Sym("cons".to_string()),
expand_quasiquote(&list[0])?,
expand_quasiquote(&Exp::List(list[1..].to_vec()))?,
])),
},
_ => Ok(Exp::List(vec![Exp::Sym("quote".to_string()), exp.clone()])),
}
}
pub fn expand_list(list: &[Exp], env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
let expanded: Result<Vec<Exp>, Err> = list.iter().map(|item| expand(item, env)).collect();
pub fn expand_list(
list: &[Exp],
env: &mut Rc<RefCell<Env>>
) -> Result<Exp, Err> {
let expanded: Result<Vec<Exp>, Err> = list.iter().map(|item|
expand(item, env)
).collect();
Ok(Exp::List(expanded?))
}
@ -74,16 +79,22 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
let name = args[0].clone();
let args = Exp::List(args[1..].to_vec());
let body = expand(body, env)?;
let mut function = vec![Exp::Sym("function".to_string()), args, body];
let mut function = vec![
Exp::Sym("function".to_string()),
args,
body,
];
if list.len() == 4 {
function.insert(2, list[2].clone());
}
Ok(Exp::List(vec![
Exp::Sym("variable".to_string()), name, Exp::List(function)
Exp::Sym("variable".to_string()),
name,
Exp::List(function),
]))
}
Exp::Sym(_) => expand_list(list, env),
_ => expected!("first argument to be a symbol or a list")
_ => expected!("first argument to be a symbol or a list"),
}
}
Exp::Sym(s) if s == "define-macro" => {
@ -95,13 +106,17 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
let args = Exp::List(args[1..].to_vec());
let body = expand(&list[2], env)?;
Ok(Exp::List(vec![
Exp::Sym("variable".to_string()), name, Exp::List(vec![
Exp::Sym("macro".to_string()), args, body
])
Exp::Sym("variable".to_string()),
name,
Exp::List(vec![
Exp::Sym("macro".to_string()),
args,
body
]),
]))
}
(Exp::Sym(_), _) => expand_list(list, env),
_ => expected!("first argument to be a symbol or a list")
_ => expected!("first argument to be a symbol or a list"),
}
}
Exp::Sym(s) if s == "cond" => {
@ -110,7 +125,11 @@ pub fn expand(exp: &Exp, env: &mut Rc<RefCell<Env>>) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
let test_exp = expand(&args[0], env)?;
let then_exp = expand(&args[1], env)?;
let mut res = vec![Exp::Sym("if".to_string()), test_exp, then_exp];
let mut res = vec![
Exp::Sym("if".to_string()),
test_exp,
then_exp,
];
if list.len() > 2 {
let mut acc = vec![Exp::Sym("cond".to_string())];
acc.extend_from_slice(&list[2..]);

View File

@ -5,34 +5,36 @@ mod number;
mod parse;
mod primitive;
pub use number::Number;
pub use env::Env;
pub use number::Number;
use env::default_env;
use eval::{eval, eval_variable_args};
use expand::expand;
use parse::parse;
use crate::api::fs;
use crate::api::console::Style;
use crate::api::fs;
use crate::api::process::ExitCode;
use crate::api::prompt::Prompt;
use alloc::boxed::Box;
use alloc::collections::btree_map::BTreeMap;
use alloc::format;
use alloc::rc::Rc;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::collections::btree_map::BTreeMap;
use alloc::vec;
use alloc::vec::Vec;
use core::cell::RefCell;
use core::convert::TryInto;
use core::cmp;
use core::convert::TryInto;
use core::fmt;
use lazy_static::lazy_static;
use spin::Mutex;
// MOROS Lisp is a lisp-1 like Scheme and Clojure
//
// Eval & Env adapted from Risp
// Copyright 2019 Stepan Parunashvili
// https://github.com/stopachka/risp
@ -40,11 +42,16 @@ use spin::Mutex;
// Parser rewritten from scratch using Nom
// https://github.com/geal/nom
//
// See "Recursive Functions of Symic Expressions and Their Computation by Machine" by John McCarthy (1960)
// And "The Roots of Lisp" by Paul Graham (2002)
// References:
//
// MOROS Lisp is a lisp-1 like Scheme and Clojure
// See "Technical Issues of Separation in Function Cells and Value Cells" by Richard P. Gabriel (1982)
// "Recursive Functions of Symic Expressions and Their Computation by Machine"
// by John McCarthy (1960)
//
// "The Roots of Lisp"
// by Paul Graham (2002)
//
// "Technical Issues of Separation in Function Cells and Value Cells"
// by Richard P. Gabriel (1982)
// Types
@ -75,13 +82,13 @@ impl PartialEq for Exp {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Exp::Function(a), Exp::Function(b)) => a == b,
(Exp::Macro(a), Exp::Macro(b)) => a == b,
(Exp::List(a), Exp::List(b)) => a == b,
(Exp::Dict(a), Exp::Dict(b)) => a == b,
(Exp::Bool(a), Exp::Bool(b)) => a == b,
(Exp::Num(a), Exp::Num(b)) => a == b,
(Exp::Str(a), Exp::Str(b)) => a == b,
(Exp::Sym(a), Exp::Sym(b)) => a == b,
(Exp::Macro(a), Exp::Macro(b)) => a == b,
(Exp::List(a), Exp::List(b)) => a == b,
(Exp::Dict(a), Exp::Dict(b)) => a == b,
(Exp::Bool(a), Exp::Bool(b)) => a == b,
(Exp::Num(a), Exp::Num(b)) => a == b,
(Exp::Str(a), Exp::Str(b)) => a == b,
(Exp::Sym(a), Exp::Sym(b)) => a == b,
_ => false,
}
}
@ -91,13 +98,13 @@ impl PartialOrd for Exp {
fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
match (self, other) {
(Exp::Function(a), Exp::Function(b)) => a.partial_cmp(b),
(Exp::Macro(a), Exp::Macro(b)) => a.partial_cmp(b),
(Exp::List(a), Exp::List(b)) => a.partial_cmp(b),
(Exp::Dict(a), Exp::Dict(b)) => a.partial_cmp(b),
(Exp::Bool(a), Exp::Bool(b)) => a.partial_cmp(b),
(Exp::Num(a), Exp::Num(b)) => a.partial_cmp(b),
(Exp::Str(a), Exp::Str(b)) => a.partial_cmp(b),
(Exp::Sym(a), Exp::Sym(b)) => a.partial_cmp(b),
(Exp::Macro(a), Exp::Macro(b)) => a.partial_cmp(b),
(Exp::List(a), Exp::List(b)) => a.partial_cmp(b),
(Exp::Dict(a), Exp::Dict(b)) => a.partial_cmp(b),
(Exp::Bool(a), Exp::Bool(b)) => a.partial_cmp(b),
(Exp::Num(a), Exp::Num(b)) => a.partial_cmp(b),
(Exp::Str(a), Exp::Str(b)) => a.partial_cmp(b),
(Exp::Sym(a), Exp::Sym(b)) => a.partial_cmp(b),
_ => None,
}
}
@ -107,20 +114,25 @@ impl fmt::Display for Exp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let out = match self {
Exp::Primitive(_) => format!("(function args)"),
Exp::Function(f) => format!("(function {})", f.params),
Exp::Macro(m) => format!("(macro {})", m.params),
Exp::Bool(a) => a.to_string(),
Exp::Num(n) => n.to_string(),
Exp::Sym(s) => s.clone(),
Exp::Str(s) => format!("{:?}", s).replace("\\u{8}", "\\b").replace("\\u{1b}", "\\e"),
Exp::List(list) => {
let xs: Vec<String> = list.iter().map(|x| x.to_string()).collect();
Exp::Function(f) => format!("(function {})", f.params),
Exp::Macro(m) => format!("(macro {})", m.params),
Exp::Bool(a) => a.to_string(),
Exp::Num(n) => n.to_string(),
Exp::Sym(s) => s.clone(),
Exp::Str(s) => {
format!("{:?}", s).
replace("\\u{8}", "\\b").replace("\\u{1b}", "\\e")
}
Exp::List(list) => {
let xs: Vec<_> = list.iter().map(|x| x.to_string()).collect();
format!("({})", xs.join(" "))
},
Exp::Dict(dict) => {
let xs: Vec<String> = dict.iter().map(|(k, v)| format!("{} {}", k, v)).collect();
}
Exp::Dict(dict) => {
let xs: Vec<_> = dict.iter().map(|(k, v)|
format!("{} {}", k, v)
).collect();
format!("(dict {})", xs.join(" "))
},
}
};
write!(f, "{}", out)
}
@ -166,7 +178,7 @@ macro_rules! ensure_length_gt {
macro_rules! ensure_string {
($exp:expr) => {
match $exp {
Exp::Str(_) => {},
Exp::Str(_) => {}
_ => return expected!("a string"),
}
};
@ -176,7 +188,7 @@ macro_rules! ensure_string {
macro_rules! ensure_list {
($exp:expr) => {
match $exp {
Exp::List(_) => {},
Exp::List(_) => {}
_ => return expected!("a list"),
}
};
@ -237,7 +249,10 @@ pub fn byte(exp: &Exp) -> Result<u8, Err> {
// REPL
fn parse_eval(input: &str, env: &mut Rc<RefCell<Env>>) -> Result<(String, Exp), Err> {
fn parse_eval(
input: &str,
env: &mut Rc<RefCell<Env>>
) -> Result<(String, Exp), Err> {
let (rest, exp) = parse(input)?;
let exp = expand(&exp, env)?;
let exp = eval(&exp, env)?;
@ -349,7 +364,10 @@ 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);
println!(
"{}Usage:{} lisp {}[<file> [<args>]]{}",
csi_title, csi_reset, csi_option, csi_reset
);
Ok(())
}
@ -431,7 +449,10 @@ fn test_lisp() {
// cons
assert_eq!(eval!("(cons (quote 1) (quote (2 3)))"), "(1 2 3)");
assert_eq!(eval!("(cons (quote 1) (cons (quote 2) (cons (quote 3) (quote ()))))"), "(1 2 3)");
assert_eq!(
eval!("(cons (quote 1) (cons (quote 2) (cons (quote 3) (quote ()))))"),
"(1 2 3)"
);
// cond
assert_eq!(eval!("(cond ((< 2 4) 1))"), "1");
@ -452,14 +473,18 @@ fn test_lisp() {
assert_eq!(eval!("(if \"\" 1 2)"), "1");
// while
assert_eq!(eval!("(do (variable i 0) (while (< i 5) (set i (+ i 1))) i)"), "5");
assert_eq!(
eval!("(do (variable i 0) (while (< i 5) (set i (+ i 1))) i)"),
"5"
);
// variable
eval!("(variable a 2)");
assert_eq!(eval!("(+ a 1)"), "3");
eval!("(variable add-one (function (b) (+ b 1)))");
assert_eq!(eval!("(add-one 2)"), "3");
eval!("(variable fibonacci (function (n) (if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))");
eval!("(variable fibonacci (function (n) \
(if (< n 2) n (+ (fibonacci (- n 1)) (fibonacci (- n 2))))))");
assert_eq!(eval!("(fibonacci 6)"), "8");
// function
@ -525,7 +550,10 @@ fn test_lisp() {
// number
assert_eq!(eval!("(binary->number (number->binary 42) \"int\")"), "42");
assert_eq!(eval!("(binary->number (number->binary 42.0) \"float\")"), "42.0");
assert_eq!(
eval!("(binary->number (number->binary 42.0) \"float\")"),
"42.0"
);
// string
assert_eq!(eval!("(parse \"9.75\")"), "9.75");
@ -535,7 +563,10 @@ fn test_lisp() {
assert_eq!(eval!("(equal? \"foo\" \"foo\")"), "true");
assert_eq!(eval!("(equal? \"foo\" \"bar\")"), "false");
assert_eq!(eval!("(string/trim \"abc\n\")"), "\"abc\"");
assert_eq!(eval!("(string/split \"a\nb\nc\" \"\n\")"), "(\"a\" \"b\" \"c\")");
assert_eq!(
eval!("(string/split \"a\nb\nc\" \"\n\")"),
"(\"a\" \"b\" \"c\")"
);
// apply
assert_eq!(eval!("(apply + '(1 2 3))"), "6");
@ -559,24 +590,66 @@ fn test_lisp() {
assert_eq!(eval!("(list 1 2 (+ 1 2))"), "(1 2 3)");
// bigint
assert_eq!(eval!("9223372036854775807"), "9223372036854775807"); // -> int
assert_eq!(eval!("9223372036854775808"), "9223372036854775808"); // -> bigint
assert_eq!(eval!("0x7fffffffffffffff"), "9223372036854775807"); // -> int
assert_eq!(eval!("0x8000000000000000"), "9223372036854775808"); // -> bigint
assert_eq!(eval!("0x800000000000000f"), "9223372036854775823"); // -> bigint
assert_eq!(eval!("(+ 9223372036854775807 0)"), "9223372036854775807"); // -> int
assert_eq!(eval!("(- 9223372036854775808 1)"), "9223372036854775807"); // -> bigint
assert_eq!(eval!("(+ 9223372036854775807 1)"), "9223372036854775808"); // -> bigint
assert_eq!(eval!("(+ 9223372036854775807 1.0)"), "9223372036854776000.0"); // -> float
assert_eq!(eval!("(+ 9223372036854775807 10)"), "9223372036854775817"); // -> bigint
assert_eq!(eval!("(* 9223372036854775807 10)"), "92233720368547758070"); // -> bigint
assert_eq!(
eval!("9223372036854775807"),
"9223372036854775807" // -> int
);
assert_eq!(
eval!("9223372036854775808"),
"9223372036854775808" // -> bigint
);
assert_eq!(
eval!("0x7fffffffffffffff"),
"9223372036854775807" // -> int
);
assert_eq!(
eval!("0x8000000000000000"),
"9223372036854775808" // -> bigint
);
assert_eq!(
eval!("0x800000000000000f"),
"9223372036854775823" // -> bigint
);
assert_eq!(
eval!("(+ 9223372036854775807 0)"),
"9223372036854775807" // -> int
);
assert_eq!(
eval!("(- 9223372036854775808 1)"),
"9223372036854775807" // -> bigint
);
assert_eq!(
eval!("(+ 9223372036854775807 1)"),
"9223372036854775808" // -> bigint
);
assert_eq!(
eval!("(+ 9223372036854775807 1.0)"),
"9223372036854776000.0" // -> float
);
assert_eq!(
eval!("(+ 9223372036854775807 10)"),
"9223372036854775817" // -> bigint
);
assert_eq!(
eval!("(* 9223372036854775807 10)"),
"92233720368547758070" // -> bigint
);
assert_eq!(eval!("(^ 2 16)"), "65536"); // -> int
assert_eq!(eval!("(^ 2 128)"), "340282366920938463463374607431768211456"); // -> bigint
assert_eq!(eval!("(^ 2.0 128)"), "340282366920938500000000000000000000000.0"); // -> float
assert_eq!(
eval!("(^ 2 16)"),
"65536" // -> int
);
assert_eq!(
eval!("(^ 2 128)"),
"340282366920938463463374607431768211456" // -> bigint
);
assert_eq!(
eval!("(^ 2.0 128)"),
"340282366920938500000000000000000000000.0" // -> float
);
assert_eq!(eval!("(number/type 9223372036854775807)"), "\"int\"");
assert_eq!(eval!("(number/type 9223372036854775808)"), "\"bigint\"");
assert_eq!(eval!("(number/type 9223372036854775807)"), "\"int\"");
assert_eq!(eval!("(number/type 9223372036854775808)"), "\"bigint\"");
assert_eq!(eval!("(number/type 9223372036854776000.0)"), "\"float\"");
// quasiquote
@ -614,7 +687,10 @@ fn test_lisp() {
assert_eq!(eval!("(list 1 2 3)"), "(1 2 3)");
// dict
assert_eq!(eval!("(dict \"a\" 1 \"b\" 2 \"c\" 3)"), "(dict \"a\" 1 \"b\" 2 \"c\" 3)");
assert_eq!(
eval!("(dict \"a\" 1 \"b\" 2 \"c\" 3)"),
"(dict \"a\" 1 \"b\" 2 \"c\" 3)"
);
// get
assert_eq!(eval!("(get \"Hello\" 0)"), "\"H\"");
@ -625,7 +701,10 @@ fn test_lisp() {
assert_eq!(eval!("(get (dict \"a\" 1 \"b\" 2 \"c\" 3) \"d\")"), "()");
// put
assert_eq!(eval!("(put (dict \"a\" 1 \"b\" 2) \"c\" 3)"), "(dict \"a\" 1 \"b\" 2 \"c\" 3)");
assert_eq!(
eval!("(put (dict \"a\" 1 \"b\" 2) \"c\" 3)"),
"(dict \"a\" 1 \"b\" 2 \"c\" 3)"
);
assert_eq!(eval!("(put (list 1 3) 1 2)"), "(1 2 3)");
assert_eq!(eval!("(put \"Heo\" 2 \"ll\")"), "\"Hello\"");
}

View File

@ -6,13 +6,13 @@ use alloc::vec::Vec;
use core::convert::TryFrom;
use core::fmt;
use core::num::ParseIntError;
use core::ops::{Neg, Add, Div, Mul, Sub, Rem, Shl, Shr};
use core::ops::{Add, Div, Mul, Neg, Rem, Shl, Shr, Sub};
use core::str::FromStr;
use num_bigint::BigInt;
use num_bigint::ParseBigIntError;
use num_traits::cast::ToPrimitive;
use num_traits::Num;
use num_traits::Zero;
use num_traits::cast::ToPrimitive;
#[derive(Clone, PartialEq, PartialOrd)]
pub enum Number {
@ -26,16 +26,22 @@ macro_rules! trigonometric_method {
pub fn $op(&self) -> Number {
Number::Float(libm::$op(self.into()))
}
}
};
}
macro_rules! arithmetic_method {
($op:ident, $checked_op:ident) => {
pub fn $op(self, other: Number) -> Number {
match (self, other) {
(Number::BigInt(a), Number::BigInt(b)) => Number::BigInt(a.$op(b)),
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.$op(b)),
(Number::Int(a), Number::BigInt(b)) => Number::BigInt(a.$op(b)),
(Number::BigInt(a), Number::BigInt(b)) => {
Number::BigInt(a.$op(b))
}
(Number::BigInt(a), Number::Int(b)) => {
Number::BigInt(a.$op(b))
}
(Number::Int(a), Number::BigInt(b)) => {
Number::BigInt(a.$op(b))
}
(Number::Int(a), Number::Int(b)) => {
if let Some(r) = a.$checked_op(b) {
Number::Int(r)
@ -43,13 +49,21 @@ macro_rules! arithmetic_method {
Number::BigInt(BigInt::from(a).$op(BigInt::from(b)))
}
}
(Number::Int(a), Number::Float(b)) => Number::Float((a as f64).$op(b)),
(Number::Float(a), Number::Int(b)) => Number::Float(a.$op(b as f64)),
(Number::Float(a), Number::Float(b)) => Number::Float(a.$op(b)),
_ => Number::Float(f64::NAN), // TODO
(Number::Int(a), Number::Float(b)) => {
Number::Float((a as f64).$op(b))
}
(Number::Float(a), Number::Int(b)) => {
Number::Float(a.$op(b as f64))
}
(Number::Float(a), Number::Float(b)) => {
Number::Float(a.$op(b))
}
_ => {
Number::Float(f64::NAN) // TODO
}
}
}
}
};
}
impl Number {
@ -68,9 +82,15 @@ impl Number {
// NOTE: Rem use `libm::fmod` for `f64` instead of `rem`
pub fn rem(self, other: Number) -> Number {
match (self, other) {
(Number::BigInt(a), Number::BigInt(b)) => Number::BigInt(a.rem(b)),
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.rem(b)),
(Number::Int(a), Number::BigInt(b)) => Number::BigInt(a.rem(b)),
(Number::BigInt(a), Number::BigInt(b)) => {
Number::BigInt(a.rem(b))
}
(Number::BigInt(a), Number::Int(b)) => {
Number::BigInt(a.rem(b))
}
(Number::Int(a), Number::BigInt(b)) => {
Number::BigInt(a.rem(b))
}
(Number::Int(a), Number::Int(b)) => {
if let Some(r) = a.checked_rem(b) {
Number::Int(r)
@ -78,10 +98,18 @@ impl Number {
Number::BigInt(BigInt::from(a).rem(BigInt::from(b)))
}
}
(Number::Int(a), Number::Float(b)) => Number::Float(libm::fmod(a as f64, b)),
(Number::Float(a), Number::Int(b)) => Number::Float(libm::fmod(a, b as f64)),
(Number::Float(a), Number::Float(b)) => Number::Float(libm::fmod(a, b)),
_ => Number::Float(f64::NAN), // TODO
(Number::Int(a), Number::Float(b)) => {
Number::Float(libm::fmod(a as f64, b))
}
(Number::Float(a), Number::Int(b)) => {
Number::Float(libm::fmod(a, b as f64))
}
(Number::Float(a), Number::Float(b)) => {
Number::Float(libm::fmod(a, b))
}
_ => {
Number::Float(f64::NAN) // TODO
}
}
}
@ -89,9 +117,15 @@ impl Number {
let bmax = BigInt::from(u32::MAX);
let imax = u32::MAX as i64;
match (self, other) {
(_, Number::BigInt(b)) if *b > bmax => Number::Float(f64::INFINITY),
(_, Number::Int(b)) if *b > imax => Number::Float(f64::INFINITY),
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.pow(*b as u32)),
(_, Number::BigInt(b)) if *b > bmax => {
Number::Float(f64::INFINITY)
}
(_, Number::Int(b)) if *b > imax => {
Number::Float(f64::INFINITY)
}
(Number::BigInt(a), Number::Int(b)) => {
Number::BigInt(a.pow(*b as u32))
}
(Number::Int(a), Number::Int(b)) => {
if let Some(r) = a.checked_pow(*b as u32) {
Number::Int(r)
@ -99,16 +133,26 @@ impl Number {
Number::BigInt(BigInt::from(*a)).pow(other)
}
}
(Number::Int(a), Number::Float(b)) => Number::Float(libm::pow(*a as f64, *b)),
(Number::Float(a), Number::Int(b)) => Number::Float(libm::pow(*a, *b as f64)),
(Number::Float(a), Number::Float(b)) => Number::Float(libm::pow(*a, *b)),
_ => Number::Float(f64::NAN), // TODO
(Number::Int(a), Number::Float(b)) => {
Number::Float(libm::pow(*a as f64, *b))
}
(Number::Float(a), Number::Int(b)) => {
Number::Float(libm::pow(*a, *b as f64))
}
(Number::Float(a), Number::Float(b)) => {
Number::Float(libm::pow(*a, *b))
}
_ => {
Number::Float(f64::NAN) // TODO
}
}
}
pub fn neg(self) -> Number {
match self {
Number::BigInt(a) => Number::BigInt(-a),
Number::BigInt(a) => {
Number::BigInt(-a)
}
Number::Int(a) => {
if let Some(r) = a.checked_neg() {
Number::Int(r)
@ -116,7 +160,9 @@ impl Number {
Number::BigInt(-BigInt::from(a))
}
}
Number::Float(a) => Number::Float(-a),
Number::Float(a) => {
Number::Float(-a)
}
}
}
@ -130,7 +176,9 @@ impl Number {
pub fn shl(self, other: Number) -> Number {
match (self, other) {
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.shl(b)),
(Number::BigInt(a), Number::Int(b)) => {
Number::BigInt(a.shl(b))
}
(Number::Int(a), Number::Int(b)) => {
if let Some(r) = a.checked_shl(b as u32) {
Number::Int(r)
@ -138,13 +186,17 @@ impl Number {
Number::BigInt(BigInt::from(a).shl(b))
}
}
_ => Number::Float(f64::NAN), // TODO
_ => {
Number::Float(f64::NAN) // TODO
}
}
}
pub fn shr(self, other: Number) -> Number {
match (self, other) {
(Number::BigInt(a), Number::Int(b)) => Number::BigInt(a.shr(b)),
(Number::BigInt(a), Number::Int(b)) => {
Number::BigInt(a.shr(b))
}
(Number::Int(a), Number::Int(b)) => {
if let Some(r) = a.checked_shr(b as u32) {
Number::Int(r)
@ -152,7 +204,9 @@ impl Number {
Number::BigInt(BigInt::from(a).shr(b))
}
}
_ => Number::Float(f64::NAN), // TODO
_ => {
Number::Float(f64::NAN) // TODO
}
}
}
@ -188,7 +242,7 @@ macro_rules! operator {
self.$op(other)
}
}
}
};
}
operator!(Add, add);
@ -285,8 +339,8 @@ impl From<usize> for Number {
impl From<&Number> for f64 {
fn from(num: &Number) -> f64 {
match num {
Number::Float(n) => *n,
Number::Int(n) => *n as f64,
Number::Float(n) => *n,
Number::Int(n) => *n as f64,
Number::BigInt(n) => n.to_f64().unwrap_or(f64::NAN),
}
}
@ -298,15 +352,17 @@ macro_rules! try_from_number {
type Error = Err;
fn try_from(num: Number) -> Result<Self, Self::Error> {
let err = Err::Reason(format!("Expected an integer between 0 and {}", $int::MAX));
let err = Err::Reason(
format!("Expected an integer between 0 and {}", $int::MAX)
);
match num {
Number::Float(n) => $int::try_from(n as i64).or(Err(err)),
Number::Int(n) => $int::try_from(n).or(Err(err)),
Number::Float(n) => $int::try_from(n as i64).or(Err(err)),
Number::Int(n) => $int::try_from(n).or(Err(err)),
Number::BigInt(n) => n.$to_int().ok_or(err),
}
}
}
}
};
}
try_from_number!(usize, to_usize);
@ -315,13 +371,15 @@ try_from_number!(u8, to_u8);
impl fmt::Display for Number {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
//write!(f, "{}", self), // FIXME: alloc error
// FIXME: alloc error
// write!(f, "{}", self)
match self {
Number::Int(n) => {
write!(f, "{}", n)
}
Number::BigInt(n) => {
//write!(f, "{}", n), // FIXME: rust-lld: error: undefined symbol: fmod
// FIXME: rust-lld: error: undefined symbol: fmod
// write!(f, "{}", n)
let mut v = Vec::new();
let mut n = n.clone();
if n < BigInt::from(0) {

View File

@ -5,8 +5,6 @@ use alloc::string::String;
use alloc::string::ToString;
use alloc::vec;
use nom::Err::Error;
use nom::IResult;
use nom::branch::alt;
use nom::bytes::complete::escaped_transform;
use nom::bytes::complete::is_not;
@ -14,84 +12,69 @@ use nom::bytes::complete::tag;
use nom::bytes::complete::take_while1;
use nom::character::complete::char;
use nom::character::complete::multispace0;
use nom::character::complete::one_of;
use nom::combinator::map;
use nom::combinator::opt;
use nom::combinator::recognize;
use nom::combinator::value;
use nom::multi::many0;
use nom::multi::many1;
use nom::sequence::delimited;
use nom::sequence::preceded;
use nom::sequence::tuple;
use nom::character::complete::one_of;
use nom::multi::many1;
use nom::sequence::terminated;
use nom::sequence::tuple;
use nom::Err::Error;
use nom::IResult;
// https://docs.rs/nom/latest/nom/recipes/index.html#hexadecimal
fn hexadecimal(input: &str) -> IResult<&str, &str> {
preceded(
tag("0x"),
recognize(
many1(
terminated(one_of("0123456789abcdefABCDEF"), many0(char('_')))
)
)
recognize(many1(terminated(
one_of("0123456789abcdefABCDEF"),
many0(char('_')),
))),
)(input)
}
// https://docs.rs/nom/latest/nom/recipes/index.html#decimal
fn decimal(input: &str) -> IResult<&str, &str> {
recognize(
many1(
terminated(one_of("0123456789"), many0(char('_')))
)
)(input)
recognize(many1(terminated(one_of("0123456789"), many0(char('_')))))(input)
}
// https://docs.rs/nom/latest/nom/recipes/index.html#binary
fn binary(input: &str) -> IResult<&str, &str> {
preceded(
tag("0b"),
recognize(
many1(
terminated(one_of("01"), many0(char('_')))
)
)
recognize(many1(terminated(one_of("01"), many0(char('_'))))),
)(input)
}
// https://docs.rs/nom/latest/nom/recipes/index.html#floating-point-numbers
fn float(input: &str) -> IResult<&str, &str> {
alt((
recognize( // .42
recognize(
// .42
tuple((
char('.'),
decimal,
opt(tuple((
one_of("eE"),
opt(one_of("+-")),
decimal
)))
))
opt(tuple((one_of("eE"), opt(one_of("+-")), decimal))),
)),
),
recognize( // 42e42 and 42.42e42
recognize(
// 42e42 and 42.42e42
tuple((
decimal,
opt(preceded(
char('.'),
decimal,
)),
opt(preceded(char('.'), decimal)),
one_of("eE"),
opt(one_of("+-")),
decimal
))
),
recognize( // 42. and 42.42
tuple((
decimal,
char('.'),
opt(decimal)
))
)
)),
),
recognize(
// 42. and 42.42
tuple((decimal, char('.'), opt(decimal))),
),
))(input)
}
@ -101,15 +84,22 @@ fn is_symbol_letter(c: char) -> bool {
}
fn parse_str(input: &str) -> IResult<&str, Exp> {
let escaped = map(opt(escaped_transform(is_not("\\\""), '\\', alt((
value("\\", tag("\\")),
value("\"", tag("\"")),
value("\n", tag("n")),
value("\r", tag("r")),
value("\t", tag("t")),
value("\x08", tag("b")),
value("\x1B", tag("e")),
)))), |inner| inner.unwrap_or("".to_string()));
let escaped = map(
opt(escaped_transform(
is_not("\\\""),
'\\',
alt((
value("\\", tag("\\")),
value("\"", tag("\"")),
value("\n", tag("n")),
value("\r", tag("r")),
value("\t", tag("t")),
value("\x08", tag("b")),
value("\x1B", tag("e")),
)),
)),
|inner| inner.unwrap_or("".to_string()),
);
let (input, s) = delimited(char('"'), escaped, char('"'))(input)?;
Ok((input, Exp::Str(s)))
}
@ -122,7 +112,7 @@ fn parse_sym(input: &str) -> IResult<&str, Exp> {
fn parse_num(input: &str) -> IResult<&str, Exp> {
let (input, num) = recognize(tuple((
opt(alt((char('+'), char('-')))),
alt((float, hexadecimal, binary, decimal))
alt((float, hexadecimal, binary, decimal)),
)))(input)?;
Ok((input, Exp::Num(Number::from(num))))
}
@ -133,7 +123,9 @@ fn parse_bool(input: &str) -> IResult<&str, Exp> {
}
fn parse_list(input: &str) -> IResult<&str, Exp> {
let (input, list) = delimited(char('('), many0(parse_exp), char(')'))(input)?;
let (input, list) = delimited(
char('('), many0(parse_exp), char(')')
)(input)?;
Ok((input, Exp::List(list)))
}
@ -173,17 +165,33 @@ fn parse_comment(input: &str) -> IResult<&str, &str> {
fn parse_exp(input: &str) -> IResult<&str, Exp> {
let (input, _) = opt(many0(parse_comment))(input)?;
delimited(multispace0, alt((
parse_num, parse_bool, parse_str, parse_list, parse_quote, parse_quasiquote, parse_unquote_splice, parse_unquote, parse_splice, parse_sym
)), alt((parse_comment, multispace0)))(input)
delimited(
multispace0,
alt((
parse_num,
parse_bool,
parse_str,
parse_list,
parse_quote,
parse_quasiquote,
parse_unquote_splice,
parse_unquote,
parse_splice,
parse_sym,
)),
alt((parse_comment, multispace0)),
)(input)
}
pub fn parse(input: &str)-> Result<(String, Exp), Err> {
pub fn parse(input: &str) -> Result<(String, Exp), Err> {
match parse_exp(input) {
Ok((input, exp)) => Ok((input.to_string(), exp)),
Err(Error(err)) => {
if err.input.is_empty() {
Ok(("".to_string(), Exp::List(vec![Exp::Sym("quote".to_string()), Exp::List(vec![])])))
Ok(("".to_string(), Exp::List(vec![
Exp::Sym("quote".to_string()),
Exp::List(vec![])
])))
} else {
let line = err.input.lines().next().unwrap();
could_not!("parse '{}'", line)

View File

@ -1,21 +1,21 @@
use super::parse::parse;
use super::{Err, Exp, Number};
use super::{float, number, string};
use super::{bytes, numbers, strings};
use super::{float, number, string};
use super::{Err, Exp, Number};
use crate::{ensure_length_eq, ensure_length_gt, expected, could_not};
use crate::api::regex::Regex;
use crate::api::syscall;
use crate::sys::fs::OpenFlag;
use crate::usr::shell;
use crate::usr::host;
use crate::usr::shell;
use crate::{could_not, ensure_length_eq, ensure_length_gt, expected};
use alloc::collections::btree_map::BTreeMap;
use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use alloc::vec;
use alloc::vec::Vec;
use core::cmp::Ordering::Equal;
use core::convert::TryFrom;
use core::convert::TryInto;
@ -24,32 +24,46 @@ use num_bigint::BigInt;
use smoltcp::wire::IpAddress;
pub fn lisp_eq(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] == nums[1])))
Ok(Exp::Bool(
numbers(args)?.windows(2).all(|nums| nums[0] == nums[1])
))
}
pub fn lisp_gt(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] > nums[1])))
Ok(Exp::Bool(
numbers(args)?.windows(2).all(|nums| nums[0] > nums[1])
))
}
pub fn lisp_gte(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] >= nums[1])))
Ok(Exp::Bool(
numbers(args)?.windows(2).all(|nums| nums[0] >= nums[1])
))
}
pub fn lisp_lt(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] < nums[1])))
Ok(Exp::Bool(
numbers(args)?.windows(2).all(|nums| nums[0] < nums[1])
))
}
pub fn lisp_lte(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Bool(numbers(args)?.windows(2).all(|nums| nums[0] <= nums[1])))
Ok(Exp::Bool(
numbers(args)?.windows(2).all(|nums| nums[0] <= nums[1])
))
}
pub fn lisp_mul(args: &[Exp]) -> Result<Exp, Err> {
let res = numbers(args)?.iter().fold(Number::Int(1), |acc, a| acc * a.clone());
let res = numbers(args)?.iter().fold(Number::Int(1), |acc, a|
acc * a.clone()
);
Ok(Exp::Num(res))
}
pub fn lisp_add(args: &[Exp]) -> Result<Exp, Err> {
let res = numbers(args)?.iter().fold(Number::Int(0), |acc, a| acc + a.clone());
let res = numbers(args)?.iter().fold(Number::Int(0), |acc, a|
acc + a.clone()
);
Ok(Exp::Num(res))
}
@ -60,7 +74,9 @@ pub fn lisp_sub(args: &[Exp]) -> Result<Exp, Err> {
if args.len() == 1 {
Ok(Exp::Num(-head))
} else {
let res = args[1..].iter().fold(Number::Int(0), |acc, a| acc + a.clone());
let res = args[1..].iter().fold(Number::Int(0), |acc, a|
acc + a.clone()
);
Ok(Exp::Num(head - res))
}
}
@ -180,7 +196,9 @@ pub fn lisp_string_binary(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let s = string(&args[0])?;
let buf = s.as_bytes();
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
Ok(Exp::List(
buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()
))
}
pub fn lisp_binary_string(args: &[Exp]) -> Result<Exp, Err> {
@ -188,33 +206,46 @@ pub fn lisp_binary_string(args: &[Exp]) -> Result<Exp, Err> {
match &args[0] {
Exp::List(list) => {
let buf = bytes(list)?;
let s = String::from_utf8(buf).or(expected!("a valid UTF-8 string"))?;
let s = String::from_utf8(buf).
or(expected!("a valid UTF-8 string"))?;
Ok(Exp::Str(s))
}
_ => expected!("argument to be a list")
_ => expected!("argument to be a list"),
}
}
pub fn lisp_binary_number(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match (&args[0], &args[1]) { // TODO: default type to "int" and make it optional
match (&args[0], &args[1]) {
// TODO: default type to "int" and make it optional
(Exp::List(list), Exp::Str(kind)) => {
let buf = bytes(list)?;
ensure_length_eq!(buf, 8);
match kind.as_str() { // TODO: bigint
"int" => Ok(Exp::Num(Number::Int(i64::from_be_bytes(buf[0..8].try_into().unwrap())))),
"float" => Ok(Exp::Num(Number::Float(f64::from_be_bytes(buf[0..8].try_into().unwrap())))),
match kind.as_str() {
// TODO: bigint
"int" => Ok(Exp::Num(Number::Int(i64::from_be_bytes(
buf[0..8].try_into().unwrap()
)))),
"float" => Ok(Exp::Num(Number::Float(f64::from_be_bytes(
buf[0..8].try_into().unwrap()
)))),
_ => expected!("valid number type"),
}
}
_ => expected!("arguments to be the type of number and a list of bytes")
_ => {
expected!("arguments to be the type of number and a list of bytes")
}
}
}
pub fn lisp_number_binary(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let n = number(&args[0])?;
Ok(Exp::List(n.to_be_bytes().iter().map(|b| Exp::Num(Number::from(*b))).collect()))
Ok(Exp::List(
n.to_be_bytes().iter().map(|b|
Exp::Num(Number::from(*b))
).collect()
))
}
pub fn lisp_number_string(args: &[Exp]) -> Result<Exp, Err> {
@ -226,7 +257,7 @@ pub fn lisp_number_string(args: &[Exp]) -> Result<Exp, Err> {
}
r
}
_ => 10
_ => 10,
};
let s = match number(&args[0])? {
Number::Int(n) if args.len() == 2 => {
@ -254,14 +285,14 @@ pub fn lisp_type(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let exp = match args[0] {
Exp::Primitive(_) => "function",
Exp::Function(_) => "function",
Exp::Macro(_) => "macro",
Exp::List(_) => "list",
Exp::Dict(_) => "dict",
Exp::Bool(_) => "boolean",
Exp::Str(_) => "string",
Exp::Sym(_) => "symbol",
Exp::Num(_) => "number",
Exp::Function(_) => "function",
Exp::Macro(_) => "macro",
Exp::List(_) => "list",
Exp::Dict(_) => "dict",
Exp::Bool(_) => "boolean",
Exp::Str(_) => "string",
Exp::Sym(_) => "symbol",
Exp::Num(_) => "number",
};
Ok(Exp::Str(exp.to_string()))
}
@ -310,8 +341,14 @@ pub fn lisp_contains(args: &[Exp]) -> Result<Exp, Err> {
pub fn lisp_slice(args: &[Exp]) -> Result<Exp, Err> {
let (a, b) = match args.len() {
2 => (usize::try_from(number(&args[1])?)?, 1),
3 => (usize::try_from(number(&args[1])?)?, usize::try_from(number(&args[2])?)?),
2 => (
usize::try_from(number(&args[1])?)?,
1,
),
3 => (
usize::try_from(number(&args[1])?)?,
usize::try_from(number(&args[2])?)?,
),
_ => return expected!("2 or 3 arguments"),
};
match &args[0] {
@ -323,7 +360,7 @@ pub fn lisp_slice(args: &[Exp]) -> Result<Exp, Err> {
let s: String = s.chars().skip(a).take(b).collect();
Ok(Exp::Str(s))
}
_ => expected!("first argument to be a list or a string")
_ => expected!("first argument to be a list or a string"),
}
}
@ -332,9 +369,11 @@ pub fn lisp_chunks(args: &[Exp]) -> Result<Exp, Err> {
match (&args[0], &args[1]) {
(Exp::List(list), Exp::Num(num)) => {
let n = usize::try_from(num.clone())?;
Ok(Exp::List(list.chunks(n).map(|a| Exp::List(a.to_vec())).collect()))
Ok(Exp::List(
list.chunks(n).map(|a| Exp::List(a.to_vec())).collect()
))
}
_ => expected!("a list and a number")
_ => expected!("a list and a number"),
}
}
@ -343,7 +382,7 @@ pub fn lisp_length(args: &[Exp]) -> Result<Exp, Err> {
match &args[0] {
Exp::List(list) => Ok(Exp::Num(Number::from(list.len()))),
Exp::Str(string) => Ok(Exp::Num(Number::from(string.chars().count()))),
_ => expected!("a list or a string")
_ => expected!("a list or a string"),
}
}
@ -354,7 +393,7 @@ pub fn lisp_concat(args: &[Exp]) -> Result<Exp, Err> {
if let Exp::List(list) = arg {
res.extend_from_slice(list);
} else {
return expected!("a list")
return expected!("a list");
}
}
Ok(Exp::List(res))
@ -368,7 +407,7 @@ pub fn lisp_number_type(args: &[Exp]) -> Result<Exp, Err> {
Exp::Num(Number::Int(_)) => Ok(Exp::Str("int".to_string())),
Exp::Num(Number::BigInt(_)) => Ok(Exp::Str("bigint".to_string())),
Exp::Num(Number::Float(_)) => Ok(Exp::Str("float".to_string())),
_ => expected!("argument to be a number")
_ => expected!("argument to be a number"),
}
}
@ -378,12 +417,12 @@ pub fn lisp_regex_find(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 2);
match (&args[0], &args[1]) {
(Exp::Str(regex), Exp::Str(s)) => {
let res = Regex::new(regex).find(s).map(|(a, b)| {
let res = Regex::new(regex).find(s).map(|(a, b)|
vec![Exp::Num(Number::from(a)), Exp::Num(Number::from(b))]
}).unwrap_or(vec![]);
).unwrap_or(vec![]);
Ok(Exp::List(res))
}
_ => expected!("arguments to be a regex and a string")
_ => expected!("arguments to be a regex and a string"),
}
}
@ -395,13 +434,17 @@ pub fn lisp_string_split(args: &[Exp]) -> Result<Exp, Err> {
(Exp::Str(string), Exp::Str(pattern)) => {
let list = if pattern.is_empty() {
// NOTE: "abc".split("") => ["", "b", "c", ""]
string.chars().map(|s| Exp::Str(s.to_string())).collect()
string.chars().map(|s|
Exp::Str(s.to_string())
).collect()
} else {
string.split(pattern).map(|s| Exp::Str(s.to_string())).collect()
string.split(pattern).map(|s|
Exp::Str(s.to_string())
).collect()
};
Ok(Exp::List(list))
}
_ => expected!("a string and a pattern")
_ => expected!("a string and a pattern"),
}
}
@ -434,14 +477,14 @@ pub fn lisp_file_open(args: &[Exp]) -> Result<Exp, Err> {
"a" => OpenFlag::Append as usize,
"r" => OpenFlag::Read as usize,
"w" => OpenFlag::Write as usize,
_ => return expected!("valid mode"),
_ => return expected!("valid mode"),
};
flags |= match syscall::info(&path) {
Some(info) if info.is_device() => OpenFlag::Device as usize,
Some(info) if info.is_dir() => OpenFlag::Dir as usize,
None if &mode == "r" => return could_not!("open file"),
None => OpenFlag::Create as usize,
_ => 0
_ => 0,
};
match syscall::open(&path, flags) {
@ -466,7 +509,9 @@ pub fn lisp_file_read(args: &[Exp]) -> Result<Exp, Err> {
match syscall::read(handle, &mut buf) {
Some(n) => {
buf.resize(n, 0);
Ok(Exp::List(buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()))
Ok(Exp::List(
buf.iter().map(|b| Exp::Num(Number::from(*b))).collect()
))
}
None => could_not!("read file"),
}
@ -484,7 +529,7 @@ pub fn lisp_file_write(args: &[Exp]) -> Result<Exp, Err> {
None => could_not!("write file"),
}
}
_ => expected!("second argument to be a list")
_ => expected!("second argument to be a list"),
}
}
@ -547,9 +592,9 @@ pub fn lisp_get(args: &[Exp]) -> Result<Exp, Err> {
Exp::Dict(dict) => {
let key = format!("{}", args[1]);
if let Some(val) = dict.get(&key) {
Ok(val.clone())
Ok(val.clone())
} else {
Ok(Exp::List(vec![]))
Ok(Exp::List(vec![]))
}
}
Exp::List(l) => {
@ -568,7 +613,7 @@ pub fn lisp_get(args: &[Exp]) -> Result<Exp, Err> {
Ok(Exp::Str("".to_string()))
}
}
_ => expected!("first argument to be a dict, a list, or a string")
_ => expected!("first argument to be a dict, a list, or a string"),
}
}
@ -597,7 +642,7 @@ pub fn lisp_put(args: &[Exp]) -> Result<Exp, Err> {
let s: String = s.into_iter().collect();
Ok(Exp::Str(s))
}
_ => expected!("first argument to be a dict, a list, or a string")
_ => expected!("first argument to be a dict, a list, or a string"),
}
}
@ -605,11 +650,7 @@ pub fn lisp_host(args: &[Exp]) -> Result<Exp, Err> {
ensure_length_eq!(args, 1);
let hostname = string(&args[0])?;
match host::resolve(&hostname) {
Ok(addr) => {
Ok(Exp::Str(format!("{}", addr)))
}
Err(_) => {
Ok(Exp::List(vec![]))
}
Ok(addr) => Ok(Exp::Str(format!("{}", addr))),
Err(_) => Ok(Exp::List(vec![])),
}
}

View File

@ -1,12 +1,12 @@
use crate::sys;
use crate::api::clock::DATE_TIME;
use crate::api::console::Style;
use crate::api::time;
use crate::api::fs;
use crate::api::fs::FileInfo;
use crate::api::process::ExitCode;
use crate::api::syscall;
use crate::api::time;
use crate::api::unit::SizeUnit;
use crate::sys;
use alloc::string::ToString;
use alloc::vec::Vec;
@ -21,7 +21,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
for i in 1..n {
match args[i] {
"-h" | "--help" => return help(),
"-a" | "--all" => hide_dot_files = false,
"-a" | "--all" => hide_dot_files = false,
"-n" | "--name" => sort = "name",
"-s" | "--size" => sort = "size",
"-t" | "--time" => sort = "time",
@ -40,9 +40,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
if let Some(info) = syscall::info(path) {
if info.is_dir() {
if let Ok(entries) = fs::read_dir(path) {
let mut files: Vec<_> = entries.iter().filter(|entry| {
let mut files: Vec<_> = entries.iter().filter(|entry|
!(entry.name().starts_with('.') && hide_dot_files)
}).collect();
).collect();
match sort {
"name" => files.sort_by_key(|f| f.name()),
@ -91,19 +91,42 @@ fn print_file(file: &FileInfo, width: usize, unit: SizeUnit) {
} else {
csi_reset
};
println!("{:>width$} {} {}{}{}", size, time, color, file.name(), csi_reset, width = width);
println!(
"{:>width$} {} {}{}{}",
size,
time,
color,
file.name(),
csi_reset,
width = width
);
}
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> [<dir>]{}", 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);
println!(" {0}-n{1}, {0}--name{1} Sort by name", csi_option, csi_reset);
println!(" {0}-s{1}, {0}--size{1} Sort by size", csi_option, csi_reset);
println!(" {0}-t{1}, {0}--time{1} Sort by time", csi_option, csi_reset);
println!(
" {0}-a{1}, {0}--all{1} Show dot files",
csi_option, csi_reset
);
println!(
" {0}-n{1}, {0}--name{1} Sort by name",
csi_option, csi_reset
);
println!(
" {0}-s{1}, {0}--size{1} Sort by size",
csi_option, csi_reset
);
println!(
" {0}-t{1}, {0}--time{1} Sort by time",
csi_option, csi_reset
);
Ok(())
}

View File

@ -1,13 +1,11 @@
use crate::sys;
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::api::unit::SizeUnit;
use crate::sys;
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
match *args.get(1).unwrap_or(&"") {
"u" | "usage" => {
usage(&args[2..])
}
"u" | "usage" => usage(&args[2..]),
"f" | "format" => {
sys::fs::mount_mem();
sys::fs::format_mem();
@ -49,14 +47,32 @@ fn usage(args: &[&str]) -> Result<(), ExitCode> {
let size = sys::allocator::memory_size();
let used = sys::allocator::memory_used();
let free = size - used;
let width = [size, used, free].iter().fold(0, |acc, num| {
let width = [size, used, free].iter().fold(0, |acc, num|
core::cmp::max(acc, unit.format(*num).len())
});
);
let color = Style::color("LightCyan");
let reset = Style::reset();
println!("{}size:{} {:>width$}", color, reset, unit.format(size), width = width);
println!("{}used:{} {:>width$}", color, reset, unit.format(used), width = width);
println!("{}free:{} {:>width$}", color, reset, unit.format(free), width = width);
println!(
"{}size:{} {:>width$}",
color,
reset,
unit.format(size),
width = width
);
println!(
"{}used:{} {:>width$}",
color,
reset,
unit.format(used),
width = width
);
println!(
"{}free:{} {:>width$}",
color,
reset,
unit.format(free),
width = width
);
Ok(())
}
@ -64,17 +80,26 @@ fn help_usage() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} memory usage {}<options>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} memory usage {}<options>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-b{1}, {0}--binary-size{1} Use binary size", csi_option, csi_reset);
println!(
" {0}-b{1}, {0}--binary-size{1} Use binary size",
csi_option, csi_reset
);
}
fn help() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} memory {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} memory {}<command>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Commands:{}", csi_title, csi_reset);
println!(" {}usage{} List memory usage", csi_option, csi_reset);

View File

@ -23,11 +23,11 @@ pub mod life;
pub mod lisp;
pub mod list;
pub mod memory;
pub mod r#move;
pub mod net;
pub mod pci;
pub mod pi;
pub mod pow;
pub mod r#move;
pub mod read;
pub mod shell;
pub mod socket;

View File

@ -14,7 +14,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
help();
return Ok(());
}
_ => continue
_ => continue,
}
}
@ -29,5 +29,8 @@ 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);
println!(
"{}Usage:{} move {}<src> <dst>{}",
csi_title, csi_reset, csi_option, csi_reset
);
}

View File

@ -1,22 +1,24 @@
use crate::sys;
use alloc::format;
use crate::api::clock;
use crate::api::console::Style;
use crate::api::fs;
use crate::api::syscall;
use crate::api::process::ExitCode;
use crate::api::syscall;
use crate::sys;
use crate::sys::console;
use crate::sys::net;
use crate::sys::net::EthernetDeviceIO;
use alloc::format;
use alloc::borrow::ToOwned;
use alloc::string::ToString;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec;
use core::str::FromStr;
use smoltcp::wire::{EthernetFrame, PrettyPrinter, IpCidr, Ipv4Address};
use smoltcp::iface::SocketSet;
use smoltcp::phy::Device;
use smoltcp::socket::tcp;
use smoltcp::time::Instant;
use smoltcp::phy::Device;
use smoltcp::iface::SocketSet;
use smoltcp::wire::{EthernetFrame, IpCidr, Ipv4Address, PrettyPrinter};
pub fn main(args: &[&str]) -> Result<(), ExitCode> {
match *args.get(1).unwrap_or(&"") {
@ -40,49 +42,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
}
"s" | "stat" => {
if let Some((_, ref mut device)) = *sys::net::NET.lock() {
let stats = device.stats();
let csi_color = Style::color("LightCyan");
let csi_reset = Style::reset();
println!("{}rx:{} {} packets ({} bytes)", csi_color, csi_reset, stats.rx_packets_count(), stats.rx_bytes_count());
println!("{}tx:{} {} packets ({} bytes)", csi_color, csi_reset, stats.tx_packets_count(), stats.tx_bytes_count());
} else {
error!("Network error");
}
stat();
}
"m" | "monitor" => {
if let Some((ref mut iface, ref mut device)) = *sys::net::NET.lock() {
device.config().enable_debug();
let mtu = device.capabilities().max_transmission_unit;
let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer);
let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);
loop {
if sys::console::end_of_text() || sys::console::end_of_transmission() {
println!();
return Ok(());
}
syscall::sleep(0.1);
let time = Instant::from_micros((clock::realtime() * 1000000.0) as i64);
iface.poll(time, device, &mut sockets);
let socket = sockets.get_mut::<tcp::Socket>(tcp_handle);
if socket.may_recv() {
socket.recv(|buffer| {
let recvd_len = buffer.len();
let data = buffer.to_owned();
debug!("{}", PrettyPrinter::<EthernetFrame<&[u8]>>::new("", &buffer));
(recvd_len, data)
}).unwrap();
}
}
} else {
error!("Network error");
}
monitor();
}
_ => {
error!("Invalid command");
@ -96,19 +59,28 @@ fn help() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} net {}<command>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} net {}<command>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Commands:{}", csi_title, csi_reset);
println!(" {}config{} Configure network", csi_option, csi_reset);
println!(" {}monitor{} Monitor network", csi_option, csi_reset);
println!(" {}stat{} Display network status", csi_option, csi_reset);
println!(
" {}stat{} Display network status",
csi_option, csi_reset
);
}
fn help_config() {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} net config {}<attribute> <value>{}", csi_title, csi_reset, csi_option, csi_reset);
println!(
"{}Usage:{} net config {}<attribute> <value>{}",
csi_title, csi_reset, csi_option, csi_reset
);
println!();
println!("{}Attributes:{}", csi_title, csi_reset);
println!(" {}mac{} MAC Address", csi_option, csi_reset);
@ -122,59 +94,77 @@ fn print_config(attribute: &str) {
let csi_reset = Style::reset();
if let Some(value) = get_config(attribute) {
let width = 4 - attribute.len();
println!("{}{}:{}{:width$}{}", csi_color, attribute, csi_reset, "", value, width = width);
println!(
"{}{}:{}{:width$}{}",
csi_color,
attribute,
csi_reset,
"",
value,
width = width
);
}
}
const DNS_FILE: &str = "/ini/dns";
fn dns_config() -> Option<String> {
if let Ok(value) = fs::read_to_string(DNS_FILE) {
let servers = value.trim();
if servers.split(',').all(|s| Ipv4Address::from_str(s).is_ok()) {
Some(servers.to_string())
} else {
error!("Could not parse '{}'", servers);
None
}
} else {
error!("Could not read '{}'", DNS_FILE);
None
}
}
fn gw_config() -> Option<String> {
let mut res = None;
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
iface.routes_mut().update(|storage| {
if let Some(route) = storage.iter().next() {
res = Some(route.via_router.to_string());
}
});
} else {
error!("Network error");
}
res
}
fn ip_config() -> Option<String> {
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
if let Some(ip_cidr) = iface.ip_addrs().iter().next() {
return Some(format!(
"{}/{}", ip_cidr.address(), ip_cidr.prefix_len()
));
}
} else {
error!("Network error");
}
None
}
fn mac_config() -> Option<String> {
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
return Some(iface.hardware_addr().to_string());
} else {
error!("Network error");
}
None
}
pub fn get_config(attribute: &str) -> Option<String> {
match attribute {
"mac" => {
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
return Some(iface.hardware_addr().to_string());
} else {
error!("Network error");
}
None
}
"ip" => {
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
if let Some(ip_cidr) = iface.ip_addrs().iter().next() {
return Some(format!("{}/{}", ip_cidr.address(), ip_cidr.prefix_len()));
}
} else {
error!("Network error");
}
None
}
"gw" => {
let mut res = None;
if let Some((ref mut iface, _)) = *sys::net::NET.lock() {
iface.routes_mut().update(|storage| {
if let Some(route) = storage.iter().next() {
res = Some(route.via_router.to_string());
}
});
} else {
error!("Network error");
}
res
}
"dns" => {
if let Ok(value) = fs::read_to_string(DNS_FILE) {
let servers = value.trim();
if servers.split(',').all(|s| Ipv4Address::from_str(s).is_ok()) {
Some(servers.to_string())
} else {
error!("Could not parse '{}'", servers);
None
}
} else {
error!("Could not read '{}'", DNS_FILE);
None
}
}
"dns" => dns_config(),
"gw" => gw_config(),
"ip" => ip_config(),
"mac" => mac_config(),
_ => {
error!("Invalid config attribute");
None
@ -225,7 +215,8 @@ pub fn set_config(attribute: &str, value: &str) {
"dns" => {
let servers = value.trim();
if servers.split(',').all(|s| Ipv4Address::from_str(s).is_ok()) {
if fs::write(DNS_FILE, format!("{}\n", servers).as_bytes()).is_err() {
let s = format!("{}\n", servers);
if fs::write(DNS_FILE, s.as_bytes()).is_err() {
error!("Could not write to '{}'", DNS_FILE);
}
} else {
@ -237,3 +228,66 @@ pub fn set_config(attribute: &str, value: &str) {
}
}
}
pub fn stat() {
if let Some((_, ref mut device)) = *sys::net::NET.lock() {
let stats = device.stats();
let csi_color = Style::color("LightCyan");
let csi_reset = Style::reset();
println!(
"{}rx:{} {} packets ({} bytes)",
csi_color,
csi_reset,
stats.rx_packets_count(),
stats.rx_bytes_count()
);
println!(
"{}tx:{} {} packets ({} bytes)",
csi_color,
csi_reset,
stats.tx_packets_count(),
stats.tx_bytes_count()
);
} else {
error!("Network error");
}
}
fn monitor() {
if let Some((ref mut iface, ref mut device)) = *net::NET.lock() {
device.config().enable_debug();
let mtu = device.capabilities().max_transmission_unit;
let tcp_rx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
let tcp_tx_buffer = tcp::SocketBuffer::new(vec![0; mtu]);
let tcp_socket = tcp::Socket::new(tcp_rx_buffer, tcp_tx_buffer);
let mut sockets = SocketSet::new(vec![]);
let tcp_handle = sockets.add(tcp_socket);
loop {
if console::end_of_text() || console::end_of_transmission() {
println!();
return;
}
syscall::sleep(0.1);
let ms = (clock::realtime() * 1000000.0) as i64;
let time = Instant::from_micros(ms);
iface.poll(time, device, &mut sockets);
let socket = sockets.get_mut::<tcp::Socket>(tcp_handle);
if socket.may_recv() {
socket.recv(|buffer| {
let recvd_len = buffer.len();
let data = buffer.to_owned();
let pp = PrettyPrinter::<EthernetFrame<&[u8]>>::new(
"", &buffer
);
debug!("{}", pp);
(recvd_len, data)
}).unwrap();
}
}
} else {
error!("Network error");
}
}

View File

@ -11,10 +11,8 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
"list" => {
let verbose = args.contains(&"-v") || args.contains(&"--verbose");
list(verbose)
},
_ => {
help()
}
_ => help(),
}
}
@ -23,17 +21,38 @@ fn list(verbose: bool) -> Result<(), ExitCode> {
let color2 = Style::color("LightBlue");
let reset = Style::reset();
if verbose {
println!("{}+-------------------->{} bus num{}", color1, color2, reset);
println!("{}| +--------------->{} device num{}", color1, color2, reset);
println!("{}| | +------------>{} function num{}", color1, color2, reset);
println!("{}| | | +-------->{} vendor id{}", color1, color2, reset);
println!("{}| | | | +--->{} device id{}", color1, color2, reset);
println!(
"{}+-------------------->{} bus num{}",
color1, color2, reset
);
println!(
"{}| +--------------->{} device num{}",
color1, color2, reset
);
println!(
"{}| | +------------>{} function num{}",
color1, color2, reset
);
println!(
"{}| | | +-------->{} vendor id{}",
color1, color2, reset
);
println!(
"{}| | | | +--->{} device id{}",
color1, color2, reset
);
println!("{}| | | | |{}", color1, reset);
}
for d in sys::pci::list() {
print!("{:04X}:{:02X}:{:02X} [{:04X}:{:04X}]", d.bus, d.device, d.function, d.vendor_id, d.device_id);
print!(
"{:04X}:{:02X}:{:02X} [{:04X}:{:04X}]",
d.bus, d.device, d.function, d.vendor_id, d.device_id
);
if verbose {
println!(" {}rev={:#04X} class={:#04X},{:#04X} prog={:#04X}{}", color2, d.rev, d.class, d.subclass, d.prog, reset);
println!(
" {}rev={:#04X} class={:#04X},{:#04X} prog={:#04X}{}",
color2, d.rev, d.class, d.subclass, d.prog, reset
);
} else {
println!();
}
@ -45,12 +64,21 @@ fn help() -> Result<(), ExitCode> {
let csi_option = Style::color("LightCyan");
let csi_title = Style::color("Yellow");
let csi_reset = Style::reset();
println!("{}Usage:{} pci {}<command> <options>{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} pci {}<command> <options>{1}",
csi_title, csi_reset, csi_option
);
println!();
println!("{}Commands:{}", csi_title, csi_reset);
println!(" {}list{} List PCI devices", csi_option, csi_reset);
println!(
" {}list{} List PCI devices",
csi_option, csi_reset
);
println!();
println!("{}Options:{}", csi_title, csi_reset);
println!(" {0}-v{1}, {0}--verbose{1} Increase verbosity", csi_option, csi_reset);
println!(
" {0}-v{1}, {0}--verbose{1} Increase verbosity",
csi_option, csi_reset
);
Ok(())
}

View File

@ -1,5 +1,5 @@
use crate::{usr, sys};
use crate::api::process::ExitCode;
use crate::{sys, usr};
use alloc::format;
use num_bigint::BigInt;
@ -33,7 +33,7 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
match digits {
Some(0) => break,
Some(i) => digits = Some(i - 1),
None => {},
None => {}
}
let nr = (&r - &n * &t) * 10;

View File

@ -1,11 +1,11 @@
use crate::api::console::Style;
use crate::api::process::ExitCode;
use crate::api::{io, random, console};
use crate::api::{console, io, random};
use core::fmt;
use alloc::format;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::fmt;
use vte::{Params, Parser, Perform};
struct Game {
@ -37,7 +37,7 @@ impl Game {
match c {
'q' | console::ETX_KEY | console::EOT_KEY => {
return;
},
}
c => {
for b in c.to_string().as_bytes() {
parser.advance(self, *b);
@ -131,18 +131,18 @@ impl fmt::Display for Game {
write!(f, " |")?;
} else {
let color = match v {
2 => Style::color("LightGray"),
4 => Style::color("LightBlue"),
8 => Style::color("LightCyan"),
16 => Style::color("LightGreen"),
32 => Style::color("Yellow"),
64 => Style::color("LightRed"),
128 => Style::color("Pink"),
256 => Style::color("Magenta"),
512 => Style::color("Pink"),
2 => Style::color("LightGray"),
4 => Style::color("LightBlue"),
8 => Style::color("LightCyan"),
16 => Style::color("LightGreen"),
32 => Style::color("Yellow"),
64 => Style::color("LightRed"),
128 => Style::color("Pink"),
256 => Style::color("Magenta"),
512 => Style::color("Pink"),
1024 => Style::color("Red"),
2048 => Style::color("Brown"),
_ => Style::color("White"),
_ => Style::color("White"),
};
write!(f, " {}{:^5}{}|", color, v, reset)?;
}
@ -154,13 +154,13 @@ impl fmt::Display for Game {
}
impl Perform for Game {
fn csi_dispatch(&mut self, _params: &Params, _intermediates: &[u8], _ignore: bool, c: char) {
fn csi_dispatch(&mut self, _: &Params, _: &[u8], _: bool, c: char) {
match c {
'A' => self.handle_up_key(),
'B' => self.handle_down_key(),
'C' => self.handle_forward_key(),
'D' => self.handle_backward_key(),
_ => {},
_ => {}
}
}
}

View File

@ -1,9 +1,9 @@
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;
use crate::api::syscall;
use crate::sys::console;
use crate::{api, usr};
use alloc::borrow::ToOwned;
use alloc::format;
@ -39,7 +39,10 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
// > read /net/tcp/time.nist.gov:13
let parts: Vec<_> = path.split('/').collect();
if parts.len() < 4 {
println!("{}Usage:{} read {}/net/<proto>/<host>[:<port>]/<path>{1}", csi_title, csi_reset, csi_option);
println!(
"{}Usage:{} read {}/net/<proto>/<host>[:<port>]/<path>{1}",
csi_title, csi_reset, csi_option
);
Err(ExitCode::Failure)
} else {
let host = parts[3];
@ -87,18 +90,18 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
let is_float_device = info.size() == 8;
let is_eof_device = info.size() > 8;
loop {
if sys::console::end_of_text() || sys::console::end_of_transmission() {
if console::end_of_text() || console::end_of_transmission() {
println!();
return Ok(());
}
if let Ok(bytes) = fs::read_to_bytes(path) {
if is_char_device && bytes.len() == 1 {
match bytes[0] as char {
console::ETX_KEY => {
api::console::ETX_KEY => {
println!("^C");
return Ok(());
}
console::EOT_KEY => {
api::console::EOT_KEY => {
println!("^D");
return Ok(());
}
@ -106,7 +109,9 @@ pub fn main(args: &[&str]) -> Result<(), ExitCode> {
}
}
if is_float_device && bytes.len() == 8 {
println!("{:.6}", f64::from_be_bytes(bytes[0..8].try_into().unwrap()));
let f = f64::from_be_bytes(bytes[0..8].try_into().
unwrap());
println!("{:.6}", f);
return Ok(());
}
for b in bytes {
@ -135,7 +140,10 @@ 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!(
"{}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);

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