diff options
| -rw-r--r-- | boot/elf.zig | 1 | ||||
| -rw-r--r-- | boot/sched.zig | 59 | ||||
| -rw-r--r-- | boot/user.zig | 172 | ||||
| -rw-r--r-- | bridge/bridge.zig | 12 | ||||
| -rw-r--r-- | build.zig | 5 | ||||
| -rw-r--r-- | gils/cat.rv | 6 | ||||
| -rw-r--r-- | gils/cp.rv | 4 | ||||
| -rw-r--r-- | gils/gils.rv | 2 | ||||
| -rw-r--r-- | gils/head.rv | 4 | ||||
| -rw-r--r-- | gils/mkdir.rv | 2 | ||||
| -rw-r--r-- | gils/mv.rv | 4 | ||||
| -rw-r--r-- | gils/rm.rv | 2 | ||||
| -rw-r--r-- | gils/tail.rv | 4 | ||||
| -rw-r--r-- | gils/touch.rv | 4 | ||||
| -rw-r--r-- | gils/tree.rv | 4 | ||||
| -rw-r--r-- | gils/wc.rv | 4 | ||||
| -rw-r--r-- | kernel/console.rv | 94 | ||||
| -rw-r--r-- | kernel/fs.rv | 49 | ||||
| -rw-r--r-- | kernel/io.rv | 37 | ||||
| -rw-r--r-- | kernel/main.rv | 4 | ||||
| -rw-r--r-- | kernel/proc.rv | 4 | ||||
| -rw-r--r-- | kernel/seed.rv | 10 | ||||
| -rw-r--r-- | kernel/shell.rv | 81 | ||||
| -rw-r--r-- | user/chat.c | 33 | ||||
| -rw-r--r-- | user/hexview.c | 16 | ||||
| -rw-r--r-- | user/save.c | 34 | ||||
| -rw-r--r-- | user/user.h | 58 |
27 files changed, 530 insertions, 179 deletions
diff --git a/boot/elf.zig b/boot/elf.zig index 2070db6..a6e621c 100644 --- a/boot/elf.zig +++ b/boot/elf.zig @@ -4,4 +4,5 @@ pub const programs = [_]Program{ .{ .name = "calc", .bytes = @embedFile("user_calc") }, .{ .name = "primes", .bytes = @embedFile("user_primes") }, .{ .name = "save", .bytes = @embedFile("user_save") }, + .{ .name = "chat", .bytes = @embedFile("user_chat") }, }; diff --git a/boot/sched.zig b/boot/sched.zig index 383b40d..30d5698 100644 --- a/boot/sched.zig +++ b/boot/sched.zig @@ -8,9 +8,20 @@ const bridge = @import("bridge"); const MAX_TASKS = 8; const KSTACK_SIZE = 16 * 1024; +const MAX_FDS = 8; +const MAX_PATH = 128; const State = enum { free, ready, dead, blocked }; +// file descriptor not "foid decimator" +pub const FdKind = enum { closed, keyboard, console, file }; +pub const Fd = struct { + kind: FdKind = .closed, + offset: u64 = 0, + path_len: usize = 0, + path: [MAX_PATH]u8 = @splat(0), +}; + const Task = struct { frame: Frame = std.mem.zeroes(Frame), cr3: u64 = 0, @@ -20,6 +31,7 @@ const Task = struct { var tasks: [MAX_TASKS]Task = @splat(.{}); var kstacks: [MAX_TASKS][KSTACK_SIZE]u8 align(16) = undefined; +var fdtabs: [MAX_TASKS][MAX_FDS]Fd = @splat(@splat(.{})); var current: usize = 0; var blocked_reader: ?usize = null; @@ -53,10 +65,10 @@ pub fn spawn(cr3: u64, entry: u64, ustack: u64, arg: u64, arglen: u64) void { var f = std.mem.zeroes(Frame); f.rip = entry; f.cs = 0x1b; // user code (0x18) | rpl 3 - f.rflags = 0x202; // reserved bit + if=1, so its preemptible + f.rflags = 0x202; // reserved bit + if=1 so its preemptible f.rsp = ustack; f.ss = 0x23; // user data (0x20) | rpl 3 - f.rdi = arg; // _start(arg_ptr, arg_len), the sysv first two args + f.rdi = arg; // _start(arg_ptr, arg_len) the sysv first two args f.rsi = arglen; tasks[i] = .{ .frame = f, @@ -64,10 +76,51 @@ pub fn spawn(cr3: u64, entry: u64, ustack: u64, arg: u64, arglen: u64) void { .kstack_top = @intFromPtr(&kstacks[i]) + KSTACK_SIZE, .state = .ready, }; + initFds(i); return; } } +// stdin at keyboard +fn initFds(i: usize) void { + for (&fdtabs[i]) |*f| f.* = .{}; + fdtabs[i][0].kind = .keyboard; + fdtabs[i][1].kind = .console; + fdtabs[i][2].kind = .console; +} + +// foid decimator open +pub fn fdOpen(path: []const u8) i64 { + var i: usize = 3; + while (i < MAX_FDS) : (i += 1) { + const f = &fdtabs[current][i]; + if (f.kind == .closed) { + f.kind = .file; + f.offset = 0; + const n = @min(path.len, MAX_PATH); + @memcpy(f.path[0..n], path[0..n]); + f.path_len = n; + return @intCast(i); + } + } + return -1; +} + +// foid decimator get +pub fn fdGet(fd: i64) ?*Fd { + if (fd < 0 or fd >= MAX_FDS) return null; + const f = &fdtabs[current][@intCast(fd)]; + if (f.kind == .closed) return null; + return f; +} + +// foid decimator close +pub fn fdClose(fd: i64) i64 { + const f = fdGet(fd) orelse return -1; + f.kind = .closed; + return 0; +} + pub fn procCount() u64 { var count: u64 = 0; var i: usize = 1; @@ -122,7 +175,7 @@ pub fn blockCurrentOnRead(resume_frame: Frame) void { pub fn deliverInput(byte: u8) bool { if (blocked_reader) |task| { - tasks[task].frame.rax = byte; + stdinPush(byte); tasks[task].state = .ready; blocked_reader = null; return true; diff --git a/boot/user.zig b/boot/user.zig index 9bce9e9..78d20ff 100644 --- a/boot/user.zig +++ b/boot/user.zig @@ -8,8 +8,17 @@ const regs = @import("regs.zig"); const bridge = @import("bridge"); const Frame = regs.Frame; -// scratch for a filename copied out of the caller legit one static buffer is fine -var name_buf: [64]u8 = undefined; +// what a syscall returns in rax on failure +const SYS_ERR: u64 = @bitCast(@as(i64, -1)); + +// open() flags (linux values lmao) +const O_CREAT: u64 = 0x40; +const O_TRUNC: u64 = 0x200; +const O_APPEND: u64 = 0x400; + +// scratch for a filename copied out of the caller +// legit one static buffer is fine +var name_buf: [128]u8 = undefined; fn userName(ptr: u64) []const u8 { const p: [*]const u8 = @ptrFromInt(ptr); var i: usize = 0; @@ -62,63 +71,144 @@ export fn syscallStub() callconv(.naked) void { } export fn syscallHandler(frame: *SyscallFrame) callconv(.c) void { + // i js reorganized half of these so if anyone wrote code in asm + // for this (me), your syscalled are fucked switch (frame.rax) { 0 => { // exit(code): code in rdi sched.exitCurrent(); asm volatile ("sti"); while (true) asm volatile ("hlt"); }, - 1 => { // write(ptr, len): rdi = user pointer, rsi = length - const len = frame.rsi; - if (len > 0 and len <= 4096) { - const bytes: [*]const u8 = @ptrFromInt(frame.rdi); - serial.write(bytes[0..len]); + 1 => { // write(fd, ptr, len) + const len = frame.rdx; + if (sched.fdGet(@bitCast(frame.rdi))) |f| { + if (f.kind == .console) { + if (len > 0 and len <= 4096) { + const bytes: [*]const u8 = @ptrFromInt(frame.rsi); + serial.write(bytes[0..len]); + } + frame.rax = len; + } else if (f.kind == .file) { + const n = @min(len, 4096); + const src: [*]const u8 = @ptrFromInt(frame.rsi); + if (bridge.fsWriteAt(f.path[0..f.path_len], f.offset, src[0..n])) { + f.offset += n; + frame.rax = n; + } else frame.rax = SYS_ERR; + } else { + frame.rax = SYS_ERR; // keyboard isnt writable + } + } else frame.rax = SYS_ERR; + }, + 2 => { // read(fd, buf, count) + const count = frame.rdx; + const f = sched.fdGet(@bitCast(frame.rdi)) orelse { + frame.rax = SYS_ERR; + return; + }; + switch (f.kind) { + .keyboard => { + if (count == 0) { + frame.rax = 0; + return; + } + if (sched.readReady()) |byte| { + const dst: [*]u8 = @ptrFromInt(frame.rsi); + dst[0] = byte; + frame.rax = 1; + return; + } + // nothn buffered + const resume_frame: Frame = .{ + .r15 = frame.r15, + .r14 = frame.r14, + .r13 = frame.r13, + .r12 = frame.r12, + .r11 = frame.r11, + .r10 = frame.r10, + .r9 = frame.r9, + .r8 = frame.r8, + .rbp = frame.rbp, + .rdi = frame.rdi, + .rsi = frame.rsi, + .rdx = frame.rdx, + .rcx = frame.rcx, + .rbx = frame.rbx, + .rax = frame.rax, + .vector = 0, + .error_code = 0, + .rip = frame.rip - 2, + .cs = frame.cs, + .rflags = frame.rflags, + .rsp = frame.rsp, + .ss = frame.ss, + }; + sched.blockCurrentOnRead(resume_frame); + asm volatile ("sti"); + while (true) asm volatile ("hlt"); + }, + .file => { + const n = @min(count, 4096); + const dst: [*]u8 = @ptrFromInt(frame.rsi); + const got = bridge.fsReadInto(f.path[0..f.path_len], f.offset, dst[0..n]); + f.offset += got; + frame.rax = got; + }, + else => frame.rax = SYS_ERR, } - frame.rax = len; // syscall return value }, - 2 => { // read(): return a buffered byte, else block for one - if (sched.readReady()) |byte| { - frame.rax = byte; + 7 => { // open(path_ptr, flags) + const name = userName(frame.rdi); + const flags = frame.rsi; + var size = bridge.fsSize(name); + if (size < 0) { + if (flags & O_CREAT != 0 and bridge.fsStore(name, "")) { + size = 0; + } else { + frame.rax = SYS_ERR; + return; + } + } else if (flags & O_TRUNC != 0) { + _ = bridge.fsStore(name, ""); + size = 0; + } + const fd = sched.fdOpen(name); + if (fd < 0) { + frame.rax = SYS_ERR; return; } - const resume_frame: Frame = .{ - .r15 = frame.r15, - .r14 = frame.r14, - .r13 = frame.r13, - .r12 = frame.r12, - .r11 = frame.r11, - .r10 = frame.r10, - .r9 = frame.r9, - .r8 = frame.r8, - .rbp = frame.rbp, - .rdi = frame.rdi, - .rsi = frame.rsi, - .rdx = frame.rdx, - .rcx = frame.rcx, - .rbx = frame.rbx, - .rax = frame.rax, - .vector = 0, - .error_code = 0, - .rip = frame.rip, - .cs = frame.cs, - .rflags = frame.rflags, - .rsp = frame.rsp, - .ss = frame.ss, - }; - sched.blockCurrentOnRead(resume_frame); - asm volatile ("sti"); - while (true) asm volatile ("hlt"); + if (flags & O_APPEND != 0) { + if (sched.fdGet(fd)) |f| f.offset = @intCast(size); + } + frame.rax = @bitCast(fd); + }, + 8 => { // close(fd) + frame.rax = @bitCast(sched.fdClose(@bitCast(frame.rdi))); + }, + 9 => { // lseek(fd, offset, whence) + if (sched.fdGet(@bitCast(frame.rdi))) |f| { + switch (frame.rdx) { + 0 => f.offset = frame.rsi, // SEEK_SET + 1 => f.offset += frame.rsi, // SEEK_CUR + 2 => { // SEEK_END + const sz = bridge.fsSize(f.path[0..f.path_len]); + if (sz >= 0) f.offset = @as(u64, @intCast(sz)) +% frame.rsi; + }, + else => {}, + } + frame.rax = f.offset; + } else frame.rax = SYS_ERR; }, - 4 => { // fs_size(name_ptr): returns size (like obv), or -1 + 4 => { // fs_size(name_ptr) frame.rax = @bitCast(bridge.fsSize(userName(frame.rdi))); }, - 5 => { // fs_read(name_ptr, offset, buf_ptr, len): returns bytes read + 5 => { // fs_read(name_ptr, offset, buf_ptr, len) const len = @min(frame.rcx, 4096); const name = userName(frame.rdi); const dst: [*]u8 = @ptrFromInt(frame.rdx); frame.rax = bridge.fsReadInto(name, frame.rsi, dst[0..len]); }, - 6 => { // fs_write(name_ptr, buf_ptr, len): returns 1 ok / 0 fail + 6 => { // fs_write(name_ptr, buf_ptr, len) const len = @min(frame.rdx, 64 * 1024); const name = userName(frame.rdi); const src: [*]const u8 = @ptrFromInt(frame.rsi); diff --git a/bridge/bridge.zig b/bridge/bridge.zig index 56cfc7f..4570e3d 100644 --- a/bridge/bridge.zig +++ b/bridge/bridge.zig @@ -21,6 +21,7 @@ const init_program = blk: { @embedFile("k_console") ++ "\n" ++ @embedFile("k_vmm") ++ "\n" ++ @embedFile("k_fs") ++ "\n" ++ + @embedFile("k_io") ++ "\n" ++ @embedFile("k_vfs") ++ "\n" ++ @embedFile("k_seed") ++ "\n"; @@ -179,3 +180,14 @@ pub fn fsStore(name: []const u8, data: []const u8) bool { const r = vm.callFunctionParts(func, null, &[_]revo.Value{ name_val, data_val }, null) catch return false; return (r.asNumOpt() orelse 0) == 1; } + +pub fn fsWriteAt(name: []const u8, offset: u64, data: []const u8) bool { + const vm = g_vm orelse return false; + const func = vm.getGlobal("fs_write_at") orelse return false; + const name_val = vm.ownValueString(name) catch return false; + const data_val = vm.ownValueString(data) catch return false; + g_vm_running = true; + defer g_vm_running = false; + const r = vm.callFunctionParts(func, null, &[_]revo.Value{ name_val, revo.Value.new.num(@as(f64, @floatFromInt(offset))), data_val }, null) catch return false; + return (r.asNumOpt() orelse 0) == 1; +} @@ -71,6 +71,7 @@ pub fn build(b: *std.Build) void { .{ "k_console", "kernel/console.rv" }, .{ "k_vmm", "kernel/vmm.rv" }, .{ "k_fs", "kernel/fs.rv" }, + .{ "k_io", "kernel/io.rv" }, .{ "k_vfs", "kernel/vfs.rv" }, .{ "k_seed", "kernel/seed.rv" }, .{ "k_proc", "kernel/proc.rv" }, @@ -102,7 +103,7 @@ pub fn build(b: *std.Build) void { kernel.root_module.addAnonymousImport(e[0], .{ .root_source_file = b.path(e[1]) }); } - inline for (.{ "hexview", "calc", "primes", "save" }) |prog| { + inline for (.{ "hexview", "calc", "primes", "save", "chat" }) |prog| { const cc = b.addSystemCommand(&.{ "zig", "cc", "-target", "x86_64-freestanding", @@ -114,6 +115,8 @@ pub fn build(b: *std.Build) void { }); const prog_elf = cc.addOutputFileArg(prog ++ ".elf"); cc.addFileArg(b.path("user/" ++ prog ++ ".c")); + cc.addFileInput(b.path("user/user.h")); + cc.addFileInput(b.path("user/link.ld")); kernel.root_module.addAnonymousImport("user_" ++ prog, .{ .root_source_file = prog_elf }); } diff --git a/gils/cat.rv b/gils/cat.rv index 05ab418..0666304 100644 --- a/gils/cat.rv +++ b/gils/cat.rv @@ -2,10 +2,8 @@ const context = env.ctx const parsed = arg_name(context.rest, 0) - -if parsed.name:len() == 0 do - puts("usage: cat <path>\n") -end else do +if parsed.name:len() == 0 do fail("usage: cat <path>\n") end +else do const path = expand_path(parsed.name) if vfs_owns(path) do vfs_cat(path) end else do fs:cat(path) end @@ -2,12 +2,12 @@ # HAAAAAANNNNKKKK const context = env.ctx -if context.argv:len() < 3 do puts("usage: cp <source> <destination>\n") end +if context.argv:len() < 3 do fail("usage: cp <source> <destination>\n") end else do const source = expand_path(context.argv[1]) const destination = expand_path(context.argv[2]) const size = fs:size(source) - if size < 0 do puts("cp: #{source}: no such file\n") end + if size < 0 do fail("cp: #{source}: no such file\n") end else do const contents = if size > 0 fs:read_bytes(source, 0, size) else "" if fs:store(destination, contents) == 1 do puts("#{source} -> #{destination}\n") end diff --git a/gils/gils.rv b/gils/gils.rv index 08c97cd..a500322 100644 --- a/gils/gils.rv +++ b/gils/gils.rv @@ -39,7 +39,7 @@ fn run_script_path(path, label, ctx) do if size < 0 return :false env.ctx = ctx const error_msg = eval_script(fs:read_bytes(path, 0, size)) - if error_msg != :nil do puts("#{label}: #{error_msg}\n") end + if error_msg != :nil do fail("#{label}: #{error_msg}\n") end return :true end diff --git a/gils/head.rv b/gils/head.rv index addd11a..2c2a82f 100644 --- a/gils/head.rv +++ b/gils/head.rv @@ -13,13 +13,13 @@ fn file_lines(path) do end const context = env.ctx -if context.argv:len() < 2 do puts("usage: head [n] <path>\n") end +if context.argv:len() < 2 do fail("usage: head [n] <path>\n") end else do let limit = 10 let path_arg = context.argv[1] if (context.argv:len() >= 3 and parse_pid(context.argv[1]) >= 0) do limit = parse_pid(context.argv[1]) path_arg = context.argv[2] end const lines = file_lines(expand_path(path_arg)) - if lines == :nil do puts("head: no such file\n") end + if lines == :nil do fail("head: no such file\n") end else do let index = 0 while (index < lines:len() and index < limit) do puts("#{lines[index]}\n") index += 1 end diff --git a/gils/mkdir.rv b/gils/mkdir.rv index 759544e..c7d12f7 100644 --- a/gils/mkdir.rv +++ b/gils/mkdir.rv @@ -1,5 +1,5 @@ # make a directory const context = env.ctx -if context.argv:len() < 2 do puts("usage: mkdir <path>\n") end +if context.argv:len() < 2 do fail("usage: mkdir <path>\n") end else do fs:mkdir(expand_path(context.argv[1])) end @@ -1,12 +1,12 @@ # move or rename a file const context = env.ctx -if context.argv:len() < 3 do puts("usage: mv <source> <destination>\n") end +if context.argv:len() < 3 do fail("usage: mv <source> <destination>\n") end else do const source = expand_path(context.argv[1]) const destination = expand_path(context.argv[2]) const size = fs:size(source) - if size < 0 do puts("mv: #{source}: no such file\n") end + if size < 0 do fail("mv: #{source}: no such file\n") end else do const contents = if size > 0 fs:read_bytes(source, 0, size) else "" if fs:store(destination, contents) == 1 do fs:unlink(source) puts("#{source} -> #{destination}\n") end @@ -2,5 +2,5 @@ const context = env.ctx const parsed = arg_name(context.rest, 0) -if parsed.name:len() == 0 do puts("usage: rm <path>\n") end +if parsed.name:len() == 0 do fail("usage: rm <path>\n") end else do fs:rm(expand_path(parsed.name)) end diff --git a/gils/tail.rv b/gils/tail.rv index 4a661b4..c1ca124 100644 --- a/gils/tail.rv +++ b/gils/tail.rv @@ -14,13 +14,13 @@ fn file_lines(path) do end const context = env.ctx -if context.argv:len() < 2 do puts("usage: tail [n] <path>\n") end +if context.argv:len() < 2 do fail("usage: tail [n] <path>\n") end else do let limit = 10 let path_arg = context.argv[1] if (context.argv:len() >= 3 and parse_pid(context.argv[1]) >= 0) do limit = parse_pid(context.argv[1]) path_arg = context.argv[2] end const lines = file_lines(expand_path(path_arg)) - if lines == :nil do puts("tail: no such file\n") end + if lines == :nil do fail("tail: no such file\n") end else do let start = lines:len() - limit if start < 0 do start = 0 end diff --git a/gils/touch.rv b/gils/touch.rv index 84ecca8..f8e3e18 100644 --- a/gils/touch.rv +++ b/gils/touch.rv @@ -1,9 +1,9 @@ # dont touch my nono square const context = env.ctx -if context.argv:len() < 2 do puts("usage: touch <path>\n") end +if context.argv:len() < 2 do fail("usage: touch <path>\n") end else do const path = expand_path(context.argv[1]) - if fs:is_dir(path) do puts("touch: #{path}: is a directory\n") end + if fs:is_dir(path) do fail("touch: #{path}: is a directory\n") end else if fs:size(path) < 0 do fs:store(path, "") end end diff --git a/gils/tree.rv b/gils/tree.rv index cc41601..d1a6af6 100644 --- a/gils/tree.rv +++ b/gils/tree.rv @@ -21,8 +21,8 @@ end const context = env.ctx const target = if context.argv:len() > 1 expand_path(context.argv[1]) else wd.path -if (vfs_owns(target) and target != "/") do puts("tree: #{target}: virtual dir\n") end -else if (target != "/" and fs:is_dir(target) == :false) do puts("tree: #{target}: not a directory\n") end +if (vfs_owns(target) and target != "/") do fail("tree: #{target}: virtual dir\n") end +else if (target != "/" and fs:is_dir(target) == :false) do fail("tree: #{target}: not a directory\n") end else do puts("#{target}\n") tree_walk(target, "") @@ -1,11 +1,11 @@ # word count const context = env.ctx -if context.argv:len() < 2 do puts("usage: wc <path>\n") end +if context.argv:len() < 2 do fail("usage: wc <path>\n") end else do const path = expand_path(context.argv[1]) const size = fs:size(path) - if size < 0 do puts("wc: #{path}: no such file\n") end + if size < 0 do fail("wc: #{path}: no such file\n") end else do const contents = if size > 0 fs:read_bytes(path, 0, size) else "" let line_count = 0 diff --git a/kernel/console.rv b/kernel/console.rv index 3bff6de..9447860 100644 --- a/kernel/console.rv +++ b/kernel/console.rv @@ -1,11 +1,54 @@ const FONT_WIDTH = 8 # glyph width in pixels const FONT_HEIGHT = 16 # glyph height in pixels (8x16 vga bitmap font) -const FG_COLOR = 0xd0d0d0 -const BG_COLOR = 0x101018 -const CURSOR_COLOR = 0x50fa7b + +# base16 from lung +const BASE00 = 0x101010 # background +const BASE03 = 0x505050 # bright black +const BASE05 = 0xd0d0d0 # default foreground +const BASE07 = 0xfafafa # bright white +const BASE08 = 0xab4642 # red +const BASE09 = 0xdc9656 # orange +const BASE0A = 0xf7ca88 # yellow +const BASE0B = 0xa1b56c # green +const BASE0C = 0x86c1b9 # cyan +const BASE0D = 0x7cafc2 # blue +const BASE0E = 0xba8baf # magenta +const FG_COLOR = BASE05 +const BG_COLOR = BASE00 +const CURSOR_COLOR = BASE0B global wd = { path = "/" } -global prompt = fn() "revel:#{wd.path}$ " +global shell_status = { code = 0 } + +global prompt = fn() do + let mid = "" + unless (shell_status.code == 0) do mid = "\x1b[31m#{shell_status.code} " end + return "\x1b[33m#{wd.path} #{mid}\x1b[97m$\x1b[0m " +end + +fn parse_int(text) do + let value = 0 + for index in 0..text:len() do + const digit = text[index]:ascii() + if (digit >= 48 and digit <= 57) do value = value * 10 + (digit - 48) end + end + return value +end + +fn sgr_color(code, current) do + if (code == 0 or code == 39) return BASE05 + if code == 30 return BASE00 + if code == 90 return BASE03 + if (code == 31 or code == 91) return BASE08 + if (code == 32 or code == 92) return BASE0B + if (code == 33 or code == 93) return BASE0A + if (code == 34 or code == 94) return BASE0D + if (code == 35 or code == 95) return BASE0E + if (code == 36 or code == 96) return BASE0C + if code == 37 return BASE05 + if code == 97 return BASE07 + return current +end # runtime font bs fn embolden(byte, row) do return (byte bor (byte shl 1)) band 0xFF end @@ -58,7 +101,23 @@ add_font("italic", remap_glyphs(slant), FONT_WIDTH, FONT_HEIGHT, :bitmap, :nil, add_font("bolditalic", remap_glyphs(fn(byte, row) do return slant(embolden(byte, row), row) end), FONT_WIDTH, FONT_HEIGHT, :bitmap, :nil, 0) global console = { - column = 0, row = 0, scale = 1, shown = :false, cursor_x = 0, cursor_y = 0, blink = 0, font = FONT, font_width = FONT_WIDTH, font_height = FONT_HEIGHT, font_entry = find_font("vga"), + column = 0, + row = 0, + scale = 1, + + shown = :false, + cursor_x = 0, + cursor_y = 0, + blink = 0, + + font = FONT, + font_width = FONT_WIDTH, + font_height = FONT_HEIGHT, + font_entry = find_font("vga"), + + fg = FG_COLOR, + ansi = 0, + ansi_param = "", # this is now font dependent cell_width = fn(self) self.font_width * self.scale, @@ -87,7 +146,7 @@ global console = { for glyph_row in 0..self.font_height do const bits = glyph[glyph_row] for glyph_col in 0..self.font_width do - if (bits shr glyph_col) band 1 == 1 fill_rect(origin_x + glyph_col * self.scale, origin_y + glyph_row * self.scale, self.scale, self.scale, FG_COLOR) + if (bits shr glyph_col) band 1 == 1 fill_rect(origin_x + glyph_col * self.scale, origin_y + glyph_row * self.scale, self.scale, self.scale, self.fg) end end end, @@ -103,8 +162,26 @@ global console = { # we dont do shift/cntrl, thats a job for future me lmao # NOTE: future me kinda delt with it, i was mad drunk + apply_sgr = fn(self, param) do + if param:len() == 0 do self.fg = BASE05 return :nil end + const parts = param:split(";") + for index in 0..parts:len() do self.fg = sgr_color(parse_int(parts[index]), self.fg) end + end, + putc = fn(self, character) do const code = character:ascii() + if self.ansi == 1 do + if code == 91 do self.ansi = 2 self.ansi_param = "" end + else do self.ansi = 0 end + return :nil + end + if self.ansi == 2 do + if (code >= 48 and code <= 57) or code == 59 do self.ansi_param = self.ansi_param ~ character return :nil end + if code == 109 do self:apply_sgr(self.ansi_param) end + self.ansi = 0 + return :nil + end + if code == 27 do self.ansi = 1 return :nil end # ESC, start a sequence if code == 10 do self:newline() return :nil end # \n if code == 13 do self.column = 0 return :nil end # \r if code == 8 do # backspace: move left @@ -146,6 +223,11 @@ global console = { global puts = fn(text) serial_puts(text) +global fail = fn(message) do + puts(message) + shell_status.code = 1 +end + global flush_console = fn() do console:cursor_clear() console:drain() diff --git a/kernel/fs.rv b/kernel/fs.rv index b23be7f..f732573 100644 --- a/kernel/fs.rv +++ b/kernel/fs.rv @@ -364,24 +364,24 @@ global fs = { const parent = self:resolve_parent(path) if parent == :nil do - puts("mkdir: #{path}: bad path\n") + fail("mkdir: #{path}: bad path\n") return :nil end if parent.name:len() > NAME_MAX do - puts("name too long (max #{NAME_MAX})\n") + fail("name too long (max #{NAME_MAX})\n") return :nil end if self:dir_lookup(parent.dir, parent.name) != :nil do - puts("mkdir: #{parent.name}: already exists\n") + fail("mkdir: #{parent.name}: already exists\n") return :nil end const new_block = self:fat_alloc() if new_block == 0 do - puts("disk full\n") + fail("disk full\n") return :nil end @@ -412,7 +412,7 @@ global fs = { if existing != :nil do if existing.type == T_DIR do - puts("#{parent.name}: is a directory\n") + fail("#{parent.name}: is a directory\n") return 0 end @@ -428,7 +428,7 @@ global fs = { const block = self:fat_alloc() if block == 0 do - puts("disk full\n") + fail("disk full\n") return 0 end @@ -534,6 +534,30 @@ global fs = { return entry.size end, + # TODO: ts needs a rewrite its not cheap and rewrites every file from scratch + fn write_at(self, path, offset, bytes) -> num do + const size = self:size(path) + let base = "" + + if size > 0 do base = self:read_bytes(path, 0, size) end + + let prefix = "" + + if offset <= base:len() do + prefix = base[0..offset] + end else do + prefix = base + while (prefix:len() < offset) do prefix = prefix ~ string.of_ascii(0) end + end + + const after = offset + bytes:len() + let tail = "" + + if after < base:len() do tail = base[after..] end + + return self:store(path, prefix ~ bytes ~ tail) + end, + fn cat(self, path) do if self:ensure() == :false do return :nil @@ -542,12 +566,12 @@ global fs = { const entry = self:resolve(path) if entry == :nil do - puts("cat: #{path}: no such file\n") + fail("cat: #{path}: no such file\n") return :nil end if entry.type == T_DIR do - puts("cat: #{path}: is a directory\n") + fail("cat: #{path}: is a directory\n") return :nil end @@ -582,7 +606,7 @@ global fs = { const entry = self:resolve(path) if entry == :nil do - puts("ls: #{path}: no such file\n") + fail("ls: #{path}: no such file\n") return -1 end @@ -630,19 +654,19 @@ global fs = { const parent = self:resolve_parent(path) if parent == :nil do - puts("rm: #{path}: no such file\n") + fail("rm: #{path}: no such file\n") return :nil end const entry = self:dir_lookup(parent.dir, parent.name) if entry == :nil do - puts("rm: #{path}: no such file\n") + fail("rm: #{path}: no such file\n") return :nil end if entry.type == T_DIR and self:dir_empty(entry.first) == :false do - puts("rm: #{parent.name}: directory not empty\n") + fail("rm: #{parent.name}: directory not empty\n") return :nil end @@ -718,3 +742,4 @@ global fs = { global fs_store = fn(name, text) fs:store(name, text) global fs_size = fn(name) fs:size(name) global fs_read_bytes = fn(name, offset, length) fs:read_bytes(name, offset, length) +global fs_write_at = fn(name, offset, bytes) fs:write_at(name, offset, bytes) diff --git a/kernel/io.rv b/kernel/io.rv new file mode 100644 index 0000000..68b7039 --- /dev/null +++ b/kernel/io.rv @@ -0,0 +1,37 @@ +# stdio stuff + +global io = { + open = fn(self, path, mode) do + if mode == "w" do fs:store(path, "") end + let offset = 0 + if mode == "a" do + const size = fs:size(path) + if size > 0 do offset = size end + end + return { path = path, offset = offset } + end, + + read = fn(self, handle, count) do + const data = fs:read_bytes(handle.path, handle.offset, count) + handle.offset += data:len() + return data + end, + + read_all = fn(self, handle) do + const size = fs:size(handle.path) + if size <= handle.offset return "" + const data = fs:read_bytes(handle.path, handle.offset, size - handle.offset) + handle.offset += data:len() + return data + end, + + write = fn(self, handle, data) do + fs:write_at(handle.path, handle.offset, data) + handle.offset += data:len() + return data:len() + end, + + seek = fn(self, handle, offset) do handle.offset = offset end, + tell = fn(self, handle) handle.offset, + close = fn(self, handle) :nil, +} diff --git a/kernel/main.rv b/kernel/main.rv index 67850d5..3547665 100644 --- a/kernel/main.rv +++ b/kernel/main.rv @@ -20,9 +20,9 @@ if fs:size("/etc/profile") >= 0 do end # lung will be happy -const default_face = load_ttf("/fonts/terminess.ttf", 32) +const default_face = load_ttf("/usr/share/terminess.ttf", 32) if default_face != :nil do - add_font("terminess", default_face.glyphs, default_face.width, default_face.height, :ttf, "/fonts/terminess.ttf", 32) + add_font("terminess", default_face.glyphs, default_face.width, default_face.height, :ttf, "/usr/share/terminess.ttf", 32) console:set_font(find_font("terminess")) end diff --git a/kernel/proc.rv b/kernel/proc.rv index 5019fa8..92d48f1 100644 --- a/kernel/proc.rv +++ b/kernel/proc.rv @@ -67,8 +67,8 @@ end fn run_prog(name, arg) do let bare_name = name - if bare_name:starts_with?("/bin/") bare_name = bare_name[5..] - const path = "/bin/#{bare_name}" + if bare_name:starts_with?("/usr/bin/") bare_name = bare_name[9..] + const path = "/usr/bin/#{bare_name}" if fs:size(path) < 0 return :false return load_file_elf(path, arg) end diff --git a/kernel/seed.rv b/kernel/seed.rv index cbb96e0..9dc2dc5 100644 --- a/kernel/seed.rv +++ b/kernel/seed.rv @@ -2,8 +2,9 @@ fn seed_bin() do fs:mkdir("/bin") + fs:mkdir("/usr/bin") for index in 0..prog_count() do - fs:store("/bin/#{prog_name(index)}", phys_to_str(prog_phys(index), prog_size(index))) + fs:store("/usr/bin/#{prog_name(index)}", phys_to_str(prog_phys(index), prog_size(index))) end for index in 0..script_count() do fs:store("/bin/#{script_name(index)}.rv", script_src(index)) @@ -11,9 +12,9 @@ fn seed_bin() do end fn seed_fonts() do - fs:mkdir("/fonts") + fs:mkdir("/usr/share") for index in 0..font_count() do - fs:store("/fonts/#{font_name(index)}", phys_to_str(font_phys(index), font_size(index))) + fs:store("/usr/share/#{font_name(index)}", phys_to_str(font_phys(index), font_size(index))) end end @@ -26,7 +27,8 @@ fn seed_fs() do fs:store("/etc/profile", """ # /etc/profile runs once at shell startup, keep it quick - if fs:size("/etc/motd") > 0 do + const motd_size = fs:size("/etc/motd") + if motd_size > 0 do puts(fs:read_bytes("/etc/motd", 0, motd_size)) puts("\n") end diff --git a/kernel/shell.rv b/kernel/shell.rv index 4940b34..99ead73 100644 --- a/kernel/shell.rv +++ b/kernel/shell.rv @@ -32,46 +32,27 @@ end fn cmd_help(context) do help_group("shell", "shell") help_group("gils (builtin)", "gils") - - # the rest of gils, and the programs, are real files in /bin now - const items = fs:entries("/bin") - - if items != :nil do + # gils scripts are real files in /bin, native programs in /usr/bin + const scripts = fs:entries("/bin") + if scripts != :nil do puts("\n gils (/bin scripts)\n") - - for index in 0..items:len() do - const name = items[index].name - - if name:len() > 3 and name[(name:len() - 3)..] == ".rv" do - help_row(name[0..(name:len() - 3)], "") - end - end - - puts("\n programs (/bin)\n") - - for index in 0..items:len() do - const name = items[index].name - - if name:len() <= 3 or name[(name:len() - 3)..] != ".rv" do - help_row(name, "") - end + for index in 0..scripts:len() do + const name = scripts[index].name + if (name:len() > 3 and name[(name:len() - 3)..] == ".rv") do help_row(name[0..(name:len() - 3)], "") end end end - - puts("\n fonts (font <name>)\n") - - for index in 0..fonts:len() do - help_row(fonts[index].name, "8x#{fonts[index].height}") + const progs = fs:entries("/usr/bin") + if progs != :nil do + puts("\n programs (/usr/bin)\n") + for index in 0..progs:len() do help_row(progs[index].name, "") end end - const font_files = fs:entries("/fonts") - + puts("\n fonts (font <name>)\n") + for index in 0..fonts:len() do help_row(fonts[index].name, "8x#{fonts[index].height}") end + const font_files = fs:entries("/usr/share") if font_files != :nil do - puts("\n fonts on disk (font load /fonts/x.psf)\n") - - for index in 0..font_files:len() do - help_row(font_files[index].name, "") - end + puts("\n fonts on disk (font load /usr/share/x.psf)\n") + for index in 0..font_files:len() do help_row(font_files[index].name, "") end end puts("\n") @@ -152,7 +133,7 @@ fn cmd_cd(context) do or fs:is_dir(target) do wd.path = target end else do - puts("cd: #{arg}: not a directory\n") + fail("cd: #{arg}: not a directory\n") end end @@ -334,8 +315,8 @@ fn font_file(name) -> { path: string, kind: ( :ttf | :psf | :none ) } do return { path = p, kind = :none } end - | fs:size("/fonts/#{name}.psf") >= 0 => { path = "/fonts/#{name}.psf", kind = :psf } - | fs:size("/fonts/#{name}.ttf") >= 0 => { path = "/fonts/#{name}.ttf", kind = :ttf } + | fs:size("/usr/share/#{name}.psf") >= 0 => { path = "/usr/share/#{name}.psf", kind = :psf } + | fs:size("/usr/share/#{name}.ttf") >= 0 => { path = "/usr/share/#{name}.ttf", kind = :ttf } | _ => { path = name, kind = :none } end @@ -348,9 +329,7 @@ fn cmd_font(context) do end puts("\n") - - const font_files = fs:entries("/fonts") - + const font_files = fs:entries("/usr/share") if font_files != :nil do puts("on disk:") @@ -519,17 +498,13 @@ fn exec_file(target) -> { path: string, kind: ( :elf | :script | :none ) } do let candidates = {} if has_slash(target) do - candidates:push( - expand_path(target), - expand_path(target) ~ ".rv", - ) + candidates:push(expand_path(target)) + candidates:push(expand_path(target) ~ ".rv") end else do - candidates:push( - expand_path(target), - expand_path(target) ~ ".rv", - "/bin/#{target}", - "/bin/#{target}.rv", - ) + candidates:push(expand_path(target)) + candidates:push(expand_path(target) ~ ".rv") + candidates:push("/usr/bin/#{target}") + candidates:push("/bin/#{target}.rv") end for index in 0..candidates:len() do @@ -563,7 +538,7 @@ fn cmd_run(context) do const info = exec_file(target) if info.kind == :none do - puts("run: #{target}: not found\n") + fail("run: #{target}: not found\n") return :nil end @@ -580,7 +555,7 @@ fn cmd_run(context) do return :hold end - puts("run: #{target}: not executable\n") + fail("run: #{target}: not executable\n") return :nil end @@ -601,6 +576,7 @@ fn run(line) -> :nil do const argv = line:split(" ") const context = make_ctx(line, argv) + shell_status.code = 0 const command = lookup(argv[0]) if command != :nil do @@ -624,6 +600,7 @@ fn run(line) -> :nil do return :nil end + shell_status.code = 127 puts("#{argv[0]}: command not found\n") puts(prompt()) end diff --git a/user/chat.c b/user/chat.c new file mode 100644 index 0000000..67bc727 --- /dev/null +++ b/user/chat.c @@ -0,0 +1,33 @@ +#include "user.h" + +void _start(const char* arg, unsigned long arglen) +{ + if (arglen == 0) + { + put("usage: run chat <path>\n"); + sys_exit(1); + } + + char path[128]; + unsigned long i = 0; + for (; i < arglen && i < sizeof(path) - 1; i++) + path[i] = arg[i]; + path[i] = 0; + + long fd = sys_open(path, 0); + if (fd < 0) + { + put("chat: "); + put(path); + put(": cannot open\n"); + sys_exit(1); + } + + char buffer[256]; + long n; + while ((n = sys_read(fd, buffer, sizeof(buffer))) > 0) + sys_write(1, buffer, (unsigned long)n); + + sys_close(fd); + sys_exit(0); +} diff --git a/user/hexview.c b/user/hexview.c index c211b44..5eac27c 100644 --- a/user/hexview.c +++ b/user/hexview.c @@ -5,7 +5,7 @@ static const char HEX[] = "0123456789abcdef"; static void put_hex8(unsigned char byte) { char digits[2] = { HEX[byte >> 4], HEX[byte & 15] }; - sys_write(digits, 2); + sys_write(1, digits, 2); } static void put_hex32(unsigned long value) @@ -13,7 +13,7 @@ static void put_hex32(unsigned long value) char digits[8]; for (int nibble = 0; nibble < 8; nibble++) digits[nibble] = HEX[(value >> ((7 - nibble) * 4)) & 15]; - sys_write(digits, 8); + sys_write(1, digits, 8); } #define PAGE 256 @@ -67,7 +67,7 @@ void _start(const char* arg, unsigned long arglen) if (row + column < count) { put_hex8((unsigned char)buffer[row + column]); - sys_write(" ", 1); + sys_write(1, " ", 1); } else put(" "); @@ -78,19 +78,19 @@ void _start(const char* arg, unsigned long arglen) { char character = buffer[row + column]; if (character >= 32 && character < 127) - sys_write(&character, 1); + sys_write(1, &character, 1); else - sys_write(".", 1); + sys_write(1, ".", 1); } put("\n"); } put("[n]ext [p]rev [q]uit (or up/down arrows) > "); - char key = (char)sys_read(); + char key = (char)get_char(); if (key == 27) // ESC { - sys_read(); - char final = (char)sys_read(); + get_char(); + char final = (char)get_char(); if (final == 'A') key = 'p'; else if (final == 'B') diff --git a/user/save.c b/user/save.c index b3664f4..48e1930 100644 --- a/user/save.c +++ b/user/save.c @@ -2,23 +2,30 @@ void _start(const char* arg, unsigned long arglen) { - if (arglen == 0 || arglen >= 64) + if (arglen == 0 || arglen >= 120) { put("usage: run save <path>\n"); sys_exit(1); } - char name[64]; + char name[128]; for (unsigned long i = 0; i < arglen; i++) name[i] = arg[i]; name[arglen] = 0; + long fd = sys_open(name, O_CREAT | O_TRUNC | O_WRONLY); + if (fd < 0) + { + put("save: "); + put(name); + put(": cannot open\n"); + sys_exit(1); + } + put("saving to "); put(name); put(" -- type lines, a single . saves\n"); - char buffer[4096]; - int length = 0; for (;;) { char line[256]; @@ -26,20 +33,13 @@ void _start(const char* arg, unsigned long arglen) put("\n"); if (n == 1 && line[0] == '.') break; - for (int i = 0; i < n && length < 4000; i++) - buffer[length++] = line[i]; - if (length < 4000) - buffer[length++] = '\n'; + sys_write(fd, line, (unsigned long)n); + sys_write(fd, "\n", 1); } - if (sys_fs_write(name, buffer, (unsigned long)length)) - { - put("saved "); - put(name); - put("\n"); - } - else - put("write failed\n"); - + sys_close(fd); + put("saved "); + put(name); + put("\n"); sys_exit(0); } diff --git a/user/user.h b/user/user.h index 635dc86..34710ac 100644 --- a/user/user.h +++ b/user/user.h @@ -1,16 +1,44 @@ -// shared syscall stubs +#define SEEK_SET 0 +#define SEEK_CUR 1 +#define SEEK_END 2 -static long sys_write(const char* buffer, unsigned long length) +#define O_WRONLY 1 +#define O_CREAT 0x40 +#define O_TRUNC 0x200 +#define O_APPEND 0x400 + +static long sys_write(long fd, const char* buffer, unsigned long length) +{ + long result; + __asm__ volatile("int $0x80" : "=a"(result) : "a"(1L), "D"(fd), "S"(buffer), "d"(length) : "memory"); + return result; +} + +static long sys_read(long fd, char* buffer, unsigned long length) +{ + long result; + __asm__ volatile("int $0x80" : "=a"(result) : "a"(2L), "D"(fd), "S"(buffer), "d"(length) : "memory"); + return result; +} + +static long sys_open(const char* path, long flags) +{ + long result; + __asm__ volatile("int $0x80" : "=a"(result) : "a"(7L), "D"(path), "S"(flags) : "memory"); + return result; +} + +static long sys_close(long fd) { long result; - __asm__ volatile("int $0x80" : "=a"(result) : "a"(1L), "D"(buffer), "S"(length) : "memory"); + __asm__ volatile("int $0x80" : "=a"(result) : "a"(8L), "D"(fd) : "memory"); return result; } -static long sys_read(void) +static long sys_lseek(long fd, long offset, long whence) { long result; - __asm__ volatile("int $0x80" : "=a"(result) : "a"(2L) : "memory"); + __asm__ volatile("int $0x80" : "=a"(result) : "a"(9L), "D"(fd), "S"(offset), "d"(whence) : "memory"); return result; } @@ -52,7 +80,15 @@ static void put(const char* string) unsigned long length = 0; while (string[length]) length++; - sys_write(string, length); + sys_write(1, string, length); +} + +static int get_char(void) +{ + char c; + if (sys_read(0, &c, 1) <= 0) + return -1; + return (unsigned char)c; } static int read_line(char* buffer, int max_length) @@ -60,7 +96,9 @@ static int read_line(char* buffer, int max_length) int length = 0; for (;;) { - char character = (char)sys_read(); + char character; + if (sys_read(0, &character, 1) <= 0) + break; if (character == '\n') break; if (character == '\b') @@ -68,14 +106,14 @@ static int read_line(char* buffer, int max_length) if (length > 0) { length--; - sys_write("\b \b", 3); + sys_write(1, "\b \b", 3); } continue; } if (length < max_length - 1) { buffer[length++] = character; - sys_write(&character, 1); + sys_write(1, &character, 1); } } buffer[length] = 0; @@ -108,5 +146,5 @@ static void put_int(long value) buffer[right] = swap; } - sys_write(buffer, length); + sys_write(1, buffer, length); } |
