feat: scheduling, usermode, shell
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
# Nuke built-in rules and variables.
|
||||
MAKEFLAGS += -rR
|
||||
.SUFFIXES:
|
||||
|
||||
# Target architecture.
|
||||
ARCH := x86_64
|
||||
|
||||
# Auto-detect cross compiler from toolchain/local/.
|
||||
TOOLCHAIN_PREFIX := $(shell cd .. && pwd)/toolchain/local/bin/x86_64-elf-
|
||||
ifneq ($(wildcard $(TOOLCHAIN_PREFIX)gcc),)
|
||||
CXX := $(TOOLCHAIN_PREFIX)g++
|
||||
else
|
||||
CXX := g++
|
||||
endif
|
||||
|
||||
# Compiler flags: freestanding, no stdlib, kernel-mode compatible.
|
||||
override CXXFLAGS := \
|
||||
-std=gnu++20 \
|
||||
-g -O2 -pipe \
|
||||
-Wall \
|
||||
-Wextra \
|
||||
-nostdinc \
|
||||
-ffreestanding \
|
||||
-fno-stack-protector \
|
||||
-fno-stack-check \
|
||||
-fno-PIC \
|
||||
-fno-rtti \
|
||||
-fno-exceptions \
|
||||
-ffunction-sections \
|
||||
-fdata-sections \
|
||||
-m64 \
|
||||
-march=x86-64 \
|
||||
-mno-80387 \
|
||||
-mno-mmx \
|
||||
-mno-sse \
|
||||
-mno-sse2 \
|
||||
-mno-red-zone \
|
||||
-mcmodel=small \
|
||||
-I include \
|
||||
-isystem ../kernel/freestnd-c-hdrs/x86_64/include \
|
||||
-isystem ../kernel/freestnd-cxx-hdrs/x86_64/include
|
||||
|
||||
# Linker flags: freestanding static ELF.
|
||||
override LDFLAGS := \
|
||||
-nostdlib \
|
||||
-static \
|
||||
-Wl,--build-id=none \
|
||||
-Wl,--gc-sections \
|
||||
-Wl,-m,elf_x86_64 \
|
||||
-z max-page-size=0x1000 \
|
||||
-T link.ld
|
||||
|
||||
# Output directory.
|
||||
BINDIR := bin
|
||||
|
||||
# Discover all programs (each subdirectory under src/ is a program).
|
||||
PROGRAMS := $(notdir $(wildcard src/*))
|
||||
|
||||
# Build targets: one ELF per program.
|
||||
TARGETS := $(addprefix $(BINDIR)/,$(addsuffix .elf,$(PROGRAMS)))
|
||||
|
||||
.PHONY: all clean
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
# Build each program from its source files.
|
||||
# For now each program is a single .cpp file compiled and linked directly.
|
||||
$(BINDIR)/%.elf: src/%/main.cpp link.ld GNUmakefile
|
||||
mkdir -p $(BINDIR) obj/$*
|
||||
$(CXX) $(CXXFLAGS) -c src/$*/main.cpp -o obj/$*/main.o
|
||||
$(CXX) $(CXXFLAGS) $(LDFLAGS) obj/$*/main.o -o $@
|
||||
|
||||
clean:
|
||||
rm -rf $(BINDIR) obj
|
||||
Executable
BIN
Binary file not shown.
Executable
BIN
Binary file not shown.
@@ -0,0 +1,51 @@
|
||||
/*
|
||||
* Syscall.hpp
|
||||
* ZenithOS syscall definitions for userspace programs
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Zenith {
|
||||
|
||||
// Syscall numbers
|
||||
static constexpr uint64_t SYS_EXIT = 0;
|
||||
static constexpr uint64_t SYS_YIELD = 1;
|
||||
static constexpr uint64_t SYS_SLEEP_MS = 2;
|
||||
static constexpr uint64_t SYS_GETPID = 3;
|
||||
static constexpr uint64_t SYS_PRINT = 4;
|
||||
static constexpr uint64_t SYS_PUTCHAR = 5;
|
||||
static constexpr uint64_t SYS_OPEN = 6;
|
||||
static constexpr uint64_t SYS_READ = 7;
|
||||
static constexpr uint64_t SYS_GETSIZE = 8;
|
||||
static constexpr uint64_t SYS_CLOSE = 9;
|
||||
static constexpr uint64_t SYS_READDIR = 10;
|
||||
static constexpr uint64_t SYS_ALLOC = 11;
|
||||
static constexpr uint64_t SYS_FREE = 12;
|
||||
static constexpr uint64_t SYS_GETTICKS = 13;
|
||||
static constexpr uint64_t SYS_GETMILLISECONDS = 14;
|
||||
static constexpr uint64_t SYS_GETINFO = 15;
|
||||
static constexpr uint64_t SYS_ISKEYAVAILABLE = 16;
|
||||
static constexpr uint64_t SYS_GETKEY = 17;
|
||||
static constexpr uint64_t SYS_GETCHAR = 18;
|
||||
static constexpr uint64_t SYS_PING = 19;
|
||||
|
||||
struct SysInfo {
|
||||
char osName[32];
|
||||
char osVersion[32];
|
||||
uint32_t apiVersion;
|
||||
uint32_t maxProcesses;
|
||||
};
|
||||
|
||||
struct KeyEvent {
|
||||
uint8_t scancode;
|
||||
char ascii;
|
||||
bool pressed;
|
||||
bool shift;
|
||||
bool ctrl;
|
||||
bool alt;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
* syscall.h
|
||||
* ZenithOS program-side syscall wrappers using SYSCALL instruction
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <Api/Syscall.hpp>
|
||||
|
||||
namespace zenith {
|
||||
|
||||
// ---- Raw SYSCALL wrappers ----
|
||||
|
||||
// The SYSCALL handler does not restore RDI, RSI, RDX, R10, R8, R9
|
||||
// (they are skipped on the return path). We move arguments into the
|
||||
// correct registers inside the asm block and list ALL argument
|
||||
// registers in the clobber list. This guarantees the compiler
|
||||
// reloads every argument on each call — GCC cannot optimise away
|
||||
// clobbers, unlike "+r" outputs whose dead values it may discard.
|
||||
|
||||
inline int64_t syscall0(uint64_t nr) {
|
||||
int64_t ret;
|
||||
asm volatile("syscall" : "=a"(ret) : "a"(nr)
|
||||
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline int64_t syscall1(uint64_t nr, uint64_t a1) {
|
||||
int64_t ret;
|
||||
asm volatile(
|
||||
"mov %[a1], %%rdi\n\t"
|
||||
"syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(nr), [a1] "r"(a1)
|
||||
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline int64_t syscall2(uint64_t nr, uint64_t a1, uint64_t a2) {
|
||||
int64_t ret;
|
||||
asm volatile(
|
||||
"mov %[a1], %%rdi\n\t"
|
||||
"mov %[a2], %%rsi\n\t"
|
||||
"syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2)
|
||||
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline int64_t syscall3(uint64_t nr, uint64_t a1, uint64_t a2, uint64_t a3) {
|
||||
int64_t ret;
|
||||
asm volatile(
|
||||
"mov %[a1], %%rdi\n\t"
|
||||
"mov %[a2], %%rsi\n\t"
|
||||
"mov %[a3], %%rdx\n\t"
|
||||
"syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3)
|
||||
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline int64_t syscall4(uint64_t nr, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4) {
|
||||
int64_t ret;
|
||||
asm volatile(
|
||||
"mov %[a1], %%rdi\n\t"
|
||||
"mov %[a2], %%rsi\n\t"
|
||||
"mov %[a3], %%rdx\n\t"
|
||||
"mov %[a4], %%r10\n\t"
|
||||
"syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3), [a4] "r"(a4)
|
||||
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline int64_t syscall5(uint64_t nr, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5) {
|
||||
int64_t ret;
|
||||
asm volatile(
|
||||
"mov %[a1], %%rdi\n\t"
|
||||
"mov %[a2], %%rsi\n\t"
|
||||
"mov %[a3], %%rdx\n\t"
|
||||
"mov %[a4], %%r10\n\t"
|
||||
"mov %[a5], %%r8\n\t"
|
||||
"syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3), [a4] "r"(a4), [a5] "r"(a5)
|
||||
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
inline int64_t syscall6(uint64_t nr, uint64_t a1, uint64_t a2, uint64_t a3, uint64_t a4, uint64_t a5, uint64_t a6) {
|
||||
int64_t ret;
|
||||
asm volatile(
|
||||
"mov %[a1], %%rdi\n\t"
|
||||
"mov %[a2], %%rsi\n\t"
|
||||
"mov %[a3], %%rdx\n\t"
|
||||
"mov %[a4], %%r10\n\t"
|
||||
"mov %[a5], %%r8\n\t"
|
||||
"mov %[a6], %%r9\n\t"
|
||||
"syscall"
|
||||
: "=a"(ret)
|
||||
: "a"(nr), [a1] "r"(a1), [a2] "r"(a2), [a3] "r"(a3), [a4] "r"(a4), [a5] "r"(a5), [a6] "r"(a6)
|
||||
: "rcx", "r11", "rdi", "rsi", "rdx", "r8", "r9", "r10", "memory");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ---- Typed wrappers ----
|
||||
|
||||
// Process
|
||||
[[noreturn]] inline void exit(int code = 0) {
|
||||
syscall1(Zenith::SYS_EXIT, (uint64_t)code);
|
||||
__builtin_unreachable();
|
||||
}
|
||||
|
||||
inline void yield() { syscall0(Zenith::SYS_YIELD); }
|
||||
inline void sleep_ms(uint64_t ms) { syscall1(Zenith::SYS_SLEEP_MS, ms); }
|
||||
inline int getpid() { return (int)syscall0(Zenith::SYS_GETPID); }
|
||||
|
||||
// Console
|
||||
inline void print(const char* text) { syscall1(Zenith::SYS_PRINT, (uint64_t)text); }
|
||||
inline void putchar(char c) { syscall1(Zenith::SYS_PUTCHAR, (uint64_t)c); }
|
||||
|
||||
// File I/O
|
||||
inline int open(const char* path) { return (int)syscall1(Zenith::SYS_OPEN, (uint64_t)path); }
|
||||
inline int read(int handle, uint8_t* buf, uint64_t off, uint64_t size) {
|
||||
return (int)syscall4(Zenith::SYS_READ, (uint64_t)handle, (uint64_t)buf, off, size);
|
||||
}
|
||||
inline uint64_t getsize(int handle) { return (uint64_t)syscall1(Zenith::SYS_GETSIZE, (uint64_t)handle); }
|
||||
inline void close(int handle) { syscall1(Zenith::SYS_CLOSE, (uint64_t)handle); }
|
||||
inline int readdir(const char* path, const char** names, int max) {
|
||||
return (int)syscall3(Zenith::SYS_READDIR, (uint64_t)path, (uint64_t)names, (uint64_t)max);
|
||||
}
|
||||
|
||||
// Memory
|
||||
inline void* alloc(uint64_t size) { return (void*)syscall1(Zenith::SYS_ALLOC, size); }
|
||||
inline void free(void* ptr) { syscall1(Zenith::SYS_FREE, (uint64_t)ptr); }
|
||||
|
||||
// Timekeeping
|
||||
inline uint64_t get_ticks() { return (uint64_t)syscall0(Zenith::SYS_GETTICKS); }
|
||||
inline uint64_t get_milliseconds() { return (uint64_t)syscall0(Zenith::SYS_GETMILLISECONDS); }
|
||||
|
||||
// System
|
||||
inline void get_info(Zenith::SysInfo* info) { syscall1(Zenith::SYS_GETINFO, (uint64_t)info); }
|
||||
|
||||
// Keyboard
|
||||
inline bool is_key_available() { return (bool)syscall0(Zenith::SYS_ISKEYAVAILABLE); }
|
||||
inline void getkey(Zenith::KeyEvent* out) { syscall1(Zenith::SYS_GETKEY, (uint64_t)out); }
|
||||
inline char getchar() { return (char)syscall0(Zenith::SYS_GETCHAR); }
|
||||
|
||||
// Networking
|
||||
inline int32_t ping(uint32_t ip, uint32_t timeoutMs = 3000) {
|
||||
return (int32_t)syscall2(Zenith::SYS_PING, (uint64_t)ip, (uint64_t)timeoutMs);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* link.ld
|
||||
* Linker script for ZenithOS userspace programs
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*
|
||||
* Programs are loaded at a standard user-space address.
|
||||
*/
|
||||
|
||||
OUTPUT_FORMAT(elf64-x86-64)
|
||||
|
||||
ENTRY(_start)
|
||||
|
||||
SECTIONS
|
||||
{
|
||||
. = 0x400000;
|
||||
|
||||
.text : {
|
||||
*(.text .text.*)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
|
||||
.rodata : {
|
||||
*(.rodata .rodata.*)
|
||||
}
|
||||
|
||||
. = ALIGN(4096);
|
||||
|
||||
.data : {
|
||||
*(.data .data.*)
|
||||
}
|
||||
|
||||
.bss : {
|
||||
*(.bss .bss.*)
|
||||
*(COMMON)
|
||||
}
|
||||
|
||||
/DISCARD/ : {
|
||||
*(.eh_frame*)
|
||||
*(.note .note.*)
|
||||
*(.comment*)
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* main.cpp
|
||||
* Hello world program for ZenithOS
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <zenith/syscall.h>
|
||||
|
||||
extern "C" void _start() {
|
||||
|
||||
|
||||
zenith::print("Hello from userspace!\n");
|
||||
|
||||
// while(true) {
|
||||
// zenith::print("ab");
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
/*
|
||||
* main.cpp
|
||||
* Interactive shell for ZenithOS
|
||||
* Copyright (c) 2025 Daniel Hammer
|
||||
*/
|
||||
|
||||
#include <zenith/syscall.h>
|
||||
|
||||
static bool streq(const char* a, const char* b) {
|
||||
while (*a && *b) {
|
||||
if (*a != *b) return false;
|
||||
a++; b++;
|
||||
}
|
||||
return *a == *b;
|
||||
}
|
||||
|
||||
static bool starts_with(const char* str, const char* prefix) {
|
||||
while (*prefix) {
|
||||
if (*str != *prefix) return false;
|
||||
str++; prefix++;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static const char* skip_spaces(const char* s) {
|
||||
while (*s == ' ') s++;
|
||||
return s;
|
||||
}
|
||||
|
||||
static void print_int(uint64_t n) {
|
||||
if (n == 0) {
|
||||
zenith::putchar('0');
|
||||
return;
|
||||
}
|
||||
char buf[20];
|
||||
int i = 0;
|
||||
while (n > 0) {
|
||||
buf[i++] = '0' + (n % 10);
|
||||
n /= 10;
|
||||
}
|
||||
for (int j = i - 1; j >= 0; j--) {
|
||||
zenith::putchar(buf[j]);
|
||||
}
|
||||
}
|
||||
|
||||
static void prompt() {
|
||||
zenith::print("zenith> ");
|
||||
}
|
||||
|
||||
static void cmd_help() {
|
||||
zenith::print("Available commands:\n");
|
||||
zenith::print(" help Show this help message\n");
|
||||
zenith::print(" info Show system information\n");
|
||||
zenith::print(" ls List ramdisk files\n");
|
||||
zenith::print(" cat <file> Display file contents\n");
|
||||
zenith::print(" ping <ip> Send ICMP echo requests\n");
|
||||
zenith::print(" uptime Show uptime in milliseconds\n");
|
||||
zenith::print(" clear Clear the screen\n");
|
||||
zenith::print(" exit Exit the shell\n");
|
||||
}
|
||||
|
||||
static void cmd_info() {
|
||||
Zenith::SysInfo info;
|
||||
zenith::get_info(&info);
|
||||
zenith::print(info.osName);
|
||||
zenith::print(" v");
|
||||
zenith::print(info.osVersion);
|
||||
zenith::print("\n");
|
||||
zenith::print("Syscall API version: ");
|
||||
print_int(info.apiVersion);
|
||||
zenith::putchar('\n');
|
||||
}
|
||||
|
||||
static void cmd_ls() {
|
||||
const char* entries[64];
|
||||
int count = zenith::readdir("0:/", entries, 64);
|
||||
if (count <= 0) {
|
||||
zenith::print("(empty)\n");
|
||||
return;
|
||||
}
|
||||
for (int i = 0; i < count; i++) {
|
||||
zenith::print(" ");
|
||||
zenith::print(entries[i]);
|
||||
zenith::putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_cat(const char* arg) {
|
||||
arg = skip_spaces(arg);
|
||||
if (*arg == '\0') {
|
||||
zenith::print("Usage: cat <filename>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// Build path "0:/<filename>"
|
||||
char path[128];
|
||||
const char* prefix = "0:/";
|
||||
int i = 0;
|
||||
while (prefix[i]) { path[i] = prefix[i]; i++; }
|
||||
int j = 0;
|
||||
while (arg[j] && i < 126) { path[i++] = arg[j++]; }
|
||||
path[i] = '\0';
|
||||
|
||||
int handle = zenith::open(path);
|
||||
if (handle < 0) {
|
||||
zenith::print("Error: cannot open '");
|
||||
zenith::print(arg);
|
||||
zenith::print("'\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint64_t size = zenith::getsize(handle);
|
||||
if (size == 0) {
|
||||
zenith::close(handle);
|
||||
return;
|
||||
}
|
||||
|
||||
// Read in chunks
|
||||
uint8_t buf[512];
|
||||
uint64_t offset = 0;
|
||||
while (offset < size) {
|
||||
uint64_t chunk = size - offset;
|
||||
if (chunk > sizeof(buf) - 1) chunk = sizeof(buf) - 1;
|
||||
int bytesRead = zenith::read(handle, buf, offset, chunk);
|
||||
if (bytesRead <= 0) break;
|
||||
buf[bytesRead] = '\0';
|
||||
zenith::print((const char*)buf);
|
||||
offset += bytesRead;
|
||||
}
|
||||
|
||||
zenith::close(handle);
|
||||
zenith::putchar('\n');
|
||||
}
|
||||
|
||||
static void cmd_uptime() {
|
||||
uint64_t ms = zenith::get_milliseconds();
|
||||
uint64_t secs = ms / 1000;
|
||||
uint64_t mins = secs / 60;
|
||||
secs %= 60;
|
||||
ms %= 1000;
|
||||
|
||||
zenith::print("Uptime: ");
|
||||
print_int(mins);
|
||||
zenith::print("m ");
|
||||
print_int(secs);
|
||||
zenith::print("s ");
|
||||
print_int(ms);
|
||||
zenith::print("ms\n");
|
||||
}
|
||||
|
||||
static bool parse_ip(const char* s, uint32_t* out) {
|
||||
// Parse "a.b.c.d" into a uint32_t in network byte order (little-endian stored)
|
||||
uint32_t octets[4];
|
||||
int idx = 0;
|
||||
uint32_t val = 0;
|
||||
bool hasDigit = false;
|
||||
|
||||
for (int i = 0; ; i++) {
|
||||
char c = s[i];
|
||||
if (c >= '0' && c <= '9') {
|
||||
val = val * 10 + (c - '0');
|
||||
if (val > 255) return false;
|
||||
hasDigit = true;
|
||||
} else if (c == '.' || c == '\0') {
|
||||
if (!hasDigit || idx >= 4) return false;
|
||||
octets[idx++] = val;
|
||||
val = 0;
|
||||
hasDigit = false;
|
||||
if (c == '\0') break;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (idx != 4) return false;
|
||||
*out = octets[0] | (octets[1] << 8) | (octets[2] << 16) | (octets[3] << 24);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void print_ip(uint32_t ip) {
|
||||
print_int(ip & 0xFF);
|
||||
zenith::putchar('.');
|
||||
print_int((ip >> 8) & 0xFF);
|
||||
zenith::putchar('.');
|
||||
print_int((ip >> 16) & 0xFF);
|
||||
zenith::putchar('.');
|
||||
print_int((ip >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
static void cmd_ping(const char* arg) {
|
||||
arg = skip_spaces(arg);
|
||||
if (*arg == '\0') {
|
||||
zenith::print("Usage: ping <ip address>\n");
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t ip;
|
||||
if (!parse_ip(arg, &ip)) {
|
||||
zenith::print("Invalid IP address: ");
|
||||
zenith::print(arg);
|
||||
zenith::putchar('\n');
|
||||
return;
|
||||
}
|
||||
|
||||
zenith::print("PING ");
|
||||
print_ip(ip);
|
||||
zenith::putchar('\n');
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
int32_t rtt = zenith::ping(ip, 3000);
|
||||
if (rtt < 0) {
|
||||
zenith::print(" Request timed out\n");
|
||||
} else {
|
||||
zenith::print(" Reply from ");
|
||||
print_ip(ip);
|
||||
zenith::print(": time=");
|
||||
print_int((uint64_t)rtt);
|
||||
zenith::print("ms\n");
|
||||
}
|
||||
if (i < 3) {
|
||||
zenith::sleep_ms(1000);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void cmd_clear() {
|
||||
// Print enough newlines to scroll past visible content
|
||||
for (int i = 0; i < 50; i++) {
|
||||
zenith::putchar('\n');
|
||||
}
|
||||
}
|
||||
|
||||
static void process_command(const char* line) {
|
||||
// Skip leading spaces
|
||||
line = skip_spaces(line);
|
||||
if (*line == '\0') return;
|
||||
|
||||
if (streq(line, "help")) {
|
||||
cmd_help();
|
||||
} else if (streq(line, "info")) {
|
||||
cmd_info();
|
||||
} else if (streq(line, "ls")) {
|
||||
cmd_ls();
|
||||
} else if (starts_with(line, "cat ")) {
|
||||
cmd_cat(line + 4);
|
||||
} else if (streq(line, "cat")) {
|
||||
cmd_cat("");
|
||||
} else if (starts_with(line, "ping ")) {
|
||||
cmd_ping(line + 5);
|
||||
} else if (streq(line, "ping")) {
|
||||
cmd_ping("");
|
||||
} else if (streq(line, "uptime")) {
|
||||
cmd_uptime();
|
||||
} else if (streq(line, "clear")) {
|
||||
cmd_clear();
|
||||
} else if (streq(line, "exit")) {
|
||||
zenith::print("Goodbye.\n");
|
||||
zenith::exit(0);
|
||||
} else {
|
||||
zenith::print("Unknown command: ");
|
||||
zenith::print(line);
|
||||
zenith::print("\nType 'help' for available commands.\n");
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" void _start() {
|
||||
zenith::print("\n");
|
||||
zenith::print(" ZenithOS Shell v0.1\n");
|
||||
zenith::print(" Type 'help' for available commands.\n");
|
||||
zenith::print("\n");
|
||||
|
||||
char line[256];
|
||||
int pos = 0;
|
||||
|
||||
prompt();
|
||||
|
||||
while (true) {
|
||||
char c = zenith::getchar();
|
||||
|
||||
if (c == '\n') {
|
||||
zenith::putchar('\n');
|
||||
line[pos] = '\0';
|
||||
process_command(line);
|
||||
pos = 0;
|
||||
prompt();
|
||||
} else if (c == '\b') {
|
||||
if (pos > 0) {
|
||||
pos--;
|
||||
zenith::putchar('\b');
|
||||
zenith::putchar(' ');
|
||||
zenith::putchar('\b');
|
||||
}
|
||||
} else if (c >= ' ' && pos < 255) {
|
||||
line[pos++] = c;
|
||||
zenith::putchar(c);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user