# this is just a simple prompt to test that shit is working fn pad(s, width) do let out = s while out:len() < width do out = out ~ " " end return out end fn help_row(cmd, desc) puts(" #{pad(cmd, 20)}#{desc}\n") fn cmd_help() do puts("\n shell\n") help_row("help", "show this") help_row("clear", "wipe the screen") help_row("time", "utc clock") help_row("mem", "free / total ram") help_row("uptime", "seconds since boot") help_row("ps", "running processes") help_row("zoom in|out", "scale the font") help_row("echo ", "print it back") puts("\n files\n") help_row("ls [path]", "list a directory") help_row("cd ", "change directory") help_row("pwd", "print working dir") help_row("mkdir ", "make a directory") help_row("cat ", "print a file") help_row("edit ", "line editor, . to save") help_row("write ", "create or overwrite") help_row("rm ", "delete a file or empty dir") help_row("mkfs", "format the disk") puts("\n programs\n") help_row("run", "list programs") help_row("run ", "") puts("\n") end fn welcome() do let fsline = "no filesystem" if fs.ready do fsline = "revelfs mounted" end puts("\n") puts(" kernel written in revo - by an alcoholic\n") puts(" #{(mem_total() shr 20)} MiB ram | #{fsline} | #{prog_count()} programs\n") puts(" type help to see what it does\n\n% ") end fn arg_name(line, start) do let quoted = :false let begin = start if (begin < line:len() and line[begin] == "\"") do begin += 1 quoted = :true end let i = begin if quoted do while (i < line:len() and line[i] != "\"") do i += 1 end end else do while (i < line:len() and line[i] != " ") do i += 1 end end const name = line[begin..i] let rest = i if (quoted and rest < line:len()) do rest += 1 end if (rest < line:len() and line[rest] == " ") do rest += 1 end { name = name, rest = rest } end global wd = { path = "/" } global editor = { active = :false, path = "", buf = "" } fn editor_start(path) do editor.active = :true editor.path = path editor.buf = "" const sz = fs:size(path) if sz > 0 do editor.buf = fs:read_bytes(path, 0, sz) puts(editor.buf) end puts("-- editing #{path}, a single . on a line saves and exits --\n") end fn editor_line(line) do if line == "." do fs:store(editor.path, editor.buf) editor.active = :false puts("saved #{editor.path} (#{editor.buf:len()} bytes)\n% ") end else do editor.buf = editor.buf ~ line ~ "\n" end end fn cmd_edit(line) do const a = arg_name(line, 5) if a.name:len() == 0 do puts("usage: edit \n% ") return :nil end editor_start(abspath(a.name)) end fn abspath(arg) do if arg:len() == 0 return wd.path let p = arg if arg[0] != "/" do if wd.path == "/" do p = "/#{arg}" end else do p = "#{wd.path}/#{arg}" end end while (p:len() > 1 and p[p:len() - 1] == "/") do p = p[0..(p:len() - 1)] end return p end fn parent_of(path) do if path == "/" return "/" let i = path:len() - 1 while (i > 0 and path[i] != "/") do i -= 1 end if i == 0 return "/" return path[0..i] end fn cmd_cd(words) do const arg = if words:len() > 1 words[1] else "/" const target = if arg == ".." parent_of(wd.path) else abspath(arg) if target == "/proc" or target == "/bin" or fs:is_dir(target) do wd.path = target end else do puts("cd: #{arg}: not a directory\n") end end fn cmd_pwd() puts("#{wd.path}\n") fn cmd_ls(words) do const target = if words:len() > 1 abspath(words[1]) else wd.path if vfs_owns(target) do vfs_ls(target) return :nil end const n = fs:ls(target) # the virtual mounts arent real dir entries, overlay them on the root listing if target == "/" do puts(" bin/\n proc/\n") end else if n == 0 do puts(" (empty)\n") end end fn cmd_mkdir(words) do if words:len() < 2 do puts("usage: mkdir \n") return :nil end fs:mkdir(abspath(words[1])) end fn cmd_write(line) do const a = arg_name(line, 6) if a.name:len() == 0 do puts("usage: write \n") return :nil end fs:write(abspath(a.name), line[a.rest..]) end fn cmd_cat(line) do const a = arg_name(line, 4) if a.name:len() == 0 do puts("usage: cat \n") return :nil end const p = abspath(a.name) if vfs_owns(p) do vfs_cat(p) return :nil end fs:cat(p) end fn cmd_rm(line) do const a = arg_name(line, 3) if a.name:len() == 0 do puts("usage: rm \n") return :nil end fs:rm(abspath(a.name)) end fn zoom_by(delta) do let scale = con.scale + delta if scale < 1 do scale = 1 end if scale > 6 do scale = 6 end con.scale = scale cmd_clear() puts("zoom #{scale}x\n") end fn cmd_zoom(words) do const dir = if words:len() > 1 words[1] else "" if dir == "in" do zoom_by(1) end else if dir == "out" do zoom_by(-1) end else do puts("usage: zoom in | zoom out\n") end end fn cmd_clear() con:clear() fn bcd(byte) (byte shr 4) * 10 + (byte band 15) fn rtc(reg) do outb(0x70, reg) inb(0x71) end fn cmd_time() puts("utc #{bcd(rtc(4))}:#{bcd(rtc(2))}:#{bcd(rtc(0))}\n") # 0x prefixed 16 digit hex fn hex(n) do const digits = "0123456789abcdef" let out = "" for i in 0..16 do out = out ~ digits[(n shr ((15 - i) * 4)) band 15] end "0x#{out}" end fn mem_type_name(kind) do if kind == 0 return "usable" if kind == 1 return "reserved" if kind == 2 return "acpi-reclaimable" if kind == 3 return "acpi-nvs" if kind == 4 return "bad" if kind == 5 return "bootloader-reclaimable" if kind == 6 return "kernel+modules" if kind == 7 return "framebuffer" "unknown" end # the boot memory map dump fn dump_memmap() do puts("memory map:\n") for i in 0..memmap_count() do puts(" #{hex(memmap_base(i))} len=#{hex(memmap_len(i))} type=#{mem_type_name(memmap_kind(i))}\n") end puts("usable: #{(mem_total() shr 20)} MiB, free: #{(mem_free() shr 20)} MiB\n") end fn cmd_mem() puts("#{(mem_free() shr 20)} MiB free / #{(mem_total() shr 20)} MiB usable\n") fn cmd_uptime() puts("#{math.floor(uptime() / 100)}s (#{uptime()} ticks)\n") fn cmd_ps() puts("#{proc_count()} process(es) running\n") fn run(line) do if editor.active do editor_line(line) return :nil end if line:trim():len() == 0 do puts("% ") return :nil end const words = line:split(" ") const cmd = words[0] if cmd == "edit" do cmd_edit(line) return :nil end if cmd == "run" do if words:len() <= 1 do list_progs() puts("% ") return :nil end const prog = words[1] # everything after "run " is the programs argument string const start = 4 + prog:len() + 1 const arg = if line:len() > start line[start..] else "" if run_prog(prog, arg) == :true return :nil puts("% ") return :nil end match cmd | "help" => cmd_help() | "clear" => cmd_clear() | "time" => cmd_time() | "mem" => cmd_mem() | "uptime" => cmd_uptime() | "ps" => cmd_ps() | "zoom" => cmd_zoom(words) | "mkfs" => fs:format() | "ls" => cmd_ls(words) | "cat" => cmd_cat(line) | "write" => cmd_write(line) | "rm" => cmd_rm(line) | "mkdir" => cmd_mkdir(words) | "cd" => cmd_cd(words) | "pwd" => cmd_pwd() | "echo" => if line:len() > 5 puts("#{line[5..]}\n") else puts("\n") | _ => puts("unknown command: #{cmd}\n") puts("% ") end