hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/kernel
diff options
context:
space:
mode:
authorhachem <im@hachem.wtf>2026-10-04 03:05:36 +0200
committerhachem <im@hachem.wtf>2026-10-04 03:05:36 +0200
commit5cb9c6c03270487ee65a36828098f4f25f874bb6 (patch)
treefcd4cb432faad25fbc8152ba4ee680fbc1af8bb1 /kernel
parent7c1bd03a71cccfcf48e79d241787021b0de98e73 (diff)
chore: cleanup
Diffstat (limited to 'kernel')
-rw-r--r--kernel/console.rv162
-rw-r--r--kernel/fs.rv436
-rw-r--r--kernel/main.rv11
-rw-r--r--kernel/proc.rv4
-rw-r--r--kernel/seed.rv4
-rw-r--r--kernel/shell.rv110
6 files changed, 383 insertions, 344 deletions
diff --git a/kernel/console.rv b/kernel/console.rv
index 2083817..65fe03c 100644
--- a/kernel/console.rv
+++ b/kernel/console.rv
@@ -2,110 +2,118 @@ const FW = 8 # glyph width in pixels
const FH = 16 # glyph height in pixels (8x16 vga bitmap font)
const FG = 0xd0d0d0
const CON_BG = 0x101018
+const CUR = 0x50fa7b
-global con = { col = 0, row = 0, scale = 1, shown = :false, cur_x = 0, cur_y = 0, blink = 0 }
+global con = {
+ col = 0, row = 0, scale = 1, shown = :false, cur_x = 0, cur_y = 0, blink = 0,
-# cell size in pixels at the current zoom. named cell_* (not cw/ch) so they dont
-# collide with draw_glyphs `ch` character parameter
-fn cell_w() FW * con.scale
-fn cell_h() FH * con.scale
-fn con_cols() fb_width() / cell_w()
-fn con_rows() fb_height() / cell_h()
+ # cell size in pixels at the current zoom
+ cell_w = fn(self) FW * self.scale,
+ cell_h = fn(self) FH * self.scale,
+ cols = fn(self) fb_width() / self:cell_w(),
+ rows = fn(self) fb_height() / self:cell_h(),
-fn draw_glyph(col, row, ch) do
- const x0 = col * cell_w()
- const y0 = row * cell_h()
- fill_rect(x0, y0, cell_w(), cell_h(), CON_BG) # clear the cell
- const code = ch:ascii()
- if code < 32 return :nil
- if code > 127 return :nil
- const glyph = FONT[code - 32]
- for fy in 0..FH do
- const bits = glyph[fy]
- for fx in 0..FW do
- if (bits shr fx) band 1 == 1 fill_rect(x0 + fx * con.scale, y0 + fy * con.scale, con.scale, con.scale, FG)
+ draw_glyph = fn(self, col, row, ch) do
+ const x0 = col * self:cell_w()
+ const y0 = row * self:cell_h()
+ fill_rect(x0, y0, self:cell_w(), self:cell_h(), CON_BG) # clear the cell
+ const code = ch:ascii()
+ if code < 32 return :nil
+ if code > 127 return :nil
+ const glyph = FONT[code - 32]
+ for fy in 0..FH do
+ const bits = glyph[fy]
+ for fx in 0..FW do
+ if (bits shr fx) band 1 == 1 fill_rect(x0 + fx * self.scale, y0 + fy * self.scale, self.scale, self.scale, FG)
+ end
end
- end
-end
+ end,
-fn con_newline() do
- con.col = 0
- con.row += 1
- if con.row >= con_rows() do
- fb_scroll(cell_h(), CON_BG)
- con.row = con_rows() - 1
- end
-end
+ newline = fn(self) do
+ self.col = 0
+ self.row += 1
+ if self.row >= self:rows() do
+ fb_scroll(self:cell_h(), CON_BG)
+ self.row = self:rows() - 1
+ end
+ end,
-# we dont do shift/cntrl
-# thats a job for future me lmao
-# NOTE: future me kinda delt with it, i was mad drunk
-fn con_putc(ch) do
- const code = ch:ascii()
- if code == 10 do con_newline() return :nil end # \n
- if code == 13 do con.col = 0 return :nil end # \r
- if code == 8 do # backspace: move left
- if con.col > 0 con.col -= 1
- return :nil
- end
- if code < 32 return :nil
- draw_glyph(con.col, con.row, ch)
- con.col += 1
- if con.col >= con_cols() con_newline()
-end
+ # we dont do shift/cntrl, thats a job for future me lmao
+ # NOTE: future me kinda delt with it, i was mad drunk
+ putc = fn(self, ch) do
+ const code = ch:ascii()
+ if code == 10 do self:newline() return :nil end # \n
+ if code == 13 do self.col = 0 return :nil end # \r
+ if code == 8 do # backspace: move left
+ if self.col > 0 self.col -= 1
+ return :nil
+ end
+ if code < 32 return :nil
+ self:draw_glyph(self.col, self.row, ch)
+ self.col += 1
+ if self.col >= self:cols() self:newline()
+ end,
-fn puts(str) serial_puts(str)
+ clear = fn(self) do
+ fill_rect(0, 0, fb_width(), fb_height(), CON_BG)
+ self.col = 0
+ self.row = 0
+ end,
-const CUR = 0x50fa7b
-# the cursor is an overlay block. its cleared before any drawing and shown after,
-# so it never sits under a glyph, cur_x/cur_y remember where it was actually drawn
-# so we always clear the right cell even after the head moved (backspace and shit)
-fn cursor_clear() do
- if con.shown do
- fill_rect(con.cur_x * cell_w(), con.cur_y * cell_h(), cell_w(), cell_h(), CON_BG)
- con.shown = :false
- end
-end
-fn cursor_show() do
- con.cur_x = con.col
- con.cur_y = con.row
- fill_rect(con.cur_x * cell_w(), con.cur_y * cell_h(), cell_w(), cell_h(), CUR)
- con.shown = :true
-end
+ # the cursor is an overlay block. its cleared before any drawing and shown after,
+ # so it never sits under a glyph, cur_x/cur_y remember where it was actually drawn
+ # so we always clear the right cell even after the head moved (backspace and shit)
+ cursor_clear = fn(self) do
+ if self.shown do
+ fill_rect(self.cur_x * self:cell_w(), self.cur_y * self:cell_h(), self:cell_w(), self:cell_h(), CON_BG)
+ self.shown = :false
+ end
+ end,
-fn drain() do
- let byte = serial_next()
- while byte >= 0 do con_putc(string.of_ascii(byte)) byte = serial_next() end
-end
+ cursor_show = fn(self) do
+ self.cur_x = self.col
+ self.cur_y = self.row
+ fill_rect(self.cur_x * self:cell_w(), self.cur_y * self:cell_h(), self:cell_w(), self:cell_h(), CUR)
+ self.shown = :true
+ end,
+
+ # render every byte the serial mirror has buffered
+ drain = fn(self) do
+ let byte = serial_next()
+ while byte >= 0 do self:putc(string.of_ascii(byte)) byte = serial_next() end
+ end,
+}
+
+fn puts(str) serial_puts(str)
global flush_console = fn() do
- cursor_clear()
- drain()
+ con:cursor_clear()
+ con:drain()
con.blink = 0
- cursor_show()
+ con:cursor_show()
end
global on_tick = fn(ticks) do
let byte = serial_next()
if byte >= 0 do
- cursor_clear()
- con_putc(string.of_ascii(byte))
- drain()
+ con:cursor_clear()
+ con:putc(string.of_ascii(byte))
+ con:drain()
con.blink = 0
- cursor_show()
+ con:cursor_show()
return :nil
end
if proc_exited() do
- cursor_clear()
+ con:cursor_clear()
puts("% ")
- drain()
+ con:drain()
con.blink = 0
- cursor_show()
+ con:cursor_show()
return :nil
end
con.blink += 1
if con.blink >= 50 do
con.blink = 0
- if con.shown cursor_clear() else cursor_show()
+ if con.shown con:cursor_clear() else con:cursor_show()
end
end
diff --git a/kernel/fs.rv b/kernel/fs.rv
index 5d72bc1..0c98c36 100644
--- a/kernel/fs.rv
+++ b/kernel/fs.rv
@@ -1,4 +1,4 @@
-# revelfs, a tiny fat style filesystem
+# revelfs, a tiny fat style filesystem, now an object you talk to as fs:method()
#
# on disk layout for the 16 mib test disk (32768 sectors):
# sector 0 superblock (magic + total sectors)
@@ -19,242 +19,252 @@ const NAME_MAX = 24
const MAGIC = 0x53465652 # "RVFS" little endian
const END = 0xffff # end of chain marker in the fat
+# pure index math, no state, so theyre plain helpers not methods
+fn dir_lba(i) DIR_LBA + (i shr 4)
+fn dir_off(i) (i band 15) * 32
+
# buf is a scratch sector for the superblock/directory/data, fat is a separate
# scratch sector so fat lookups dont trample a directory sector were mid edit
-global fs = { buf = 0, fat = 0, ready = :false, total = 0 }
+global fs = {
+ buf = 0, fat = 0, ready = :false, total = 0,
-fn fat_get(block) do
- disk_read(FAT_LBA + (block shr 8), fs.fat)
- mem_read(fs.fat + (block band 255) * 2, 2)
-end
+ fat_get = fn(self, block) do
+ disk_read(FAT_LBA + (block shr 8), self.fat)
+ mem_read(self.fat + (block band 255) * 2, 2)
+ end,
-fn fat_set(block, val) do
- const lba = FAT_LBA + (block shr 8)
- disk_read(lba, fs.fat)
- mem_write(fs.fat + (block band 255) * 2, val, 2)
- disk_write(lba, fs.fat)
-end
+ fat_set = fn(self, block, val) do
+ const lba = FAT_LBA + (block shr 8)
+ disk_read(lba, self.fat)
+ mem_write(self.fat + (block band 255) * 2, val, 2)
+ disk_write(lba, self.fat)
+ end,
-# first free data block, or 0 if the disk is full. scans a whole fat sector per
-# read instead of one entry at a time so this isnt snails pace
-fn fat_alloc() do
- let block = DATA_LBA
- while block < fs.total do
- disk_read(FAT_LBA + (block shr 8), fs.fat)
- let off = block band 255
- while (off < 256 and block < fs.total) do
- if mem_read(fs.fat + off * 2, 2) == 0 return block
- off += 1
- block += 1
+ # first free data block, or 0 if full. scans a whole fat sector per read so this
+ # isnt snails pace
+ fat_alloc = fn(self) do
+ let block = DATA_LBA
+ while block < self.total do
+ disk_read(FAT_LBA + (block shr 8), self.fat)
+ let off = block band 255
+ while (off < 256 and block < self.total) do
+ if mem_read(self.fat + off * 2, 2) == 0 return block
+ off += 1
+ block += 1
+ end
end
- end
- 0
-end
-
-fn free_chain(first) do
- let block = first
- while (block != 0 and block != END) do
- const nxt = fat_get(block)
- fat_set(block, 0)
- block = nxt
- end
-end
+ 0
+ end,
-fn dir_lba(i) DIR_LBA + (i shr 4)
-fn dir_off(i) (i band 15) * 32
+ free_chain = fn(self, first) do
+ let block = first
+ while (block != 0 and block != END) do
+ const nxt = self:fat_get(block)
+ self:fat_set(block, 0)
+ block = nxt
+ end
+ end,
-# dir entry, 32 bytes: +0 used, +1 name len, +2 first block, +4 size, +8 name bytes
-fn name_at(base, len) do
- let out = ""
- for k in 0..len do out = out ~ string.of_ascii(mem_read(base + 8 + k, 1)) end
- out
-end
+ # dir entry is 32 bytes:
+ # +0 used
+ # +1 name len
+ # +2 first block
+ # +4 size
+ # +8 name
+ name_at = fn(self, base, len) do
+ let out = ""
+ for k in 0..len do out = out ~ string.of_ascii(mem_read(base + 8 + k, 1)) end
+ out
+ end,
-fn name_matches(base, target) do
- const len = mem_read(base + 1, 1)
- if len != target:len() return :false
- for k in 0..len do
- if mem_read(base + 8 + k, 1) != target[k]:ascii() return :false
- end
- :true
-end
+ name_matches = fn(self, base, target) do
+ const len = mem_read(base + 1, 1)
+ if len != target:len() return :false
+ return self:name_at(base, len) == target
+ end,
-fn fs_find(name) do
- for i in 0..DIR_ENTRIES do
- disk_read(dir_lba(i), fs.buf)
- const base = fs.buf + dir_off(i)
- if mem_read(base, 1) == 1 and name_matches(base, name) return i
- end
- return -1
-end
+ find = fn(self, name) do
+ for i in 0..DIR_ENTRIES do
+ disk_read(dir_lba(i), self.buf)
+ const base = self.buf + dir_off(i)
+ if mem_read(base, 1) == 1 and self:name_matches(base, name) return i
+ end
+ return -1
+ end,
-fn dir_alloc() do
- for i in 0..DIR_ENTRIES do
- disk_read(dir_lba(i), fs.buf)
- if mem_read(fs.buf + dir_off(i), 1) == 0 return i
- end
- return -1
-end
+ dir_alloc = fn(self) do
+ for i in 0..DIR_ENTRIES do
+ disk_read(dir_lba(i), self.buf)
+ if mem_read(self.buf + dir_off(i), 1) == 0 return i
+ end
+ return -1
+ end,
-fn fs_init() do
- fs.buf = frame_alloc()
- fs.fat = frame_alloc()
- fs.total = disk_sectors()
- if fs.total == 0 do fs.ready = :false return :nil end
- disk_read(SB_LBA, fs.buf)
- fs.ready = if mem_read(fs.buf, 4) == MAGIC :true else :false
-end
+ mount = fn(self) do
+ self.buf = frame_alloc()
+ self.fat = frame_alloc()
+ self.total = disk_sectors()
+ if self.total == 0 do self.ready = :false return :nil end
+ disk_read(SB_LBA, self.buf)
+ self.ready = if mem_read(self.buf, 4) == MAGIC :true else :false
+ end,
-fn fs_ready() do
- if fs.ready return :true
- puts("no filesystem, run mkfs first\n")
- :false
-end
+ # ready is a field so the gate cant be called `ready`, this complains if theres no fs
+ ensure = fn(self) do
+ if self.ready return :true
+ puts("no filesystem, run mkfs first\n")
+ :false
+ end,
-fn fs_format() do
- if disk_sectors() == 0 do puts("mkfs: no disk\n") return :nil end
- mem_zero(fs.buf, BLOCK)
- mem_write(fs.buf, MAGIC, 4)
- mem_write(fs.buf + 4, fs.total, 4)
- disk_write(SB_LBA, fs.buf)
- mem_zero(fs.buf, BLOCK)
- for s in 0..DIR_SECTORS do disk_write(DIR_LBA + s, fs.buf) end
- for s in 0..FAT_SECTORS do disk_write(FAT_LBA + s, fs.buf) end
- fs.ready = :true
- puts("formatted " ~ fs.total ~ " sectors, " ~ (fs.total shr 1) ~ " KiB\n")
-end
+ format = fn(self) do
+ if disk_sectors() == 0 do puts("mkfs: no disk\n") return :nil end
+ mem_zero(self.buf, BLOCK)
+ mem_write(self.buf, MAGIC, 4)
+ mem_write(self.buf + 4, self.total, 4)
+ disk_write(SB_LBA, self.buf)
+ mem_zero(self.buf, BLOCK)
+ for s in 0..DIR_SECTORS do disk_write(DIR_LBA + s, self.buf) end
+ for s in 0..FAT_SECTORS do disk_write(FAT_LBA + s, self.buf) end
+ self.ready = :true
+ puts("formatted #{self.total} sectors, #{(self.total shr 1)} KiB\n")
+ end,
-# silent core of fs_write, global so the bridge can reach it via getGlobal
-global fs_store = fn(name, text) do
- if fs_ready() == :false return 0
- if name:len() > NAME_MAX do puts("name too long (max " ~ NAME_MAX ~ ")\n") return 0 end
- let idx = fs_find(name)
- if idx >= 0 do
- disk_read(dir_lba(idx), fs.buf)
- free_chain(mem_read(fs.buf + dir_off(idx) + 2, 2))
- end
- else do
- idx = dir_alloc()
- if idx < 0 do puts("directory full\n") return 0 end
- end
+ # silent core of write, returns 1 ok / 0 fail
+ store = fn(self, name, text) do
+ if self:ensure() == :false return 0
+ if name:len() > NAME_MAX do puts("name too long (max #{NAME_MAX})\n") return 0 end
+ let idx = self:find(name)
+ if idx >= 0 do
+ disk_read(dir_lba(idx), self.buf)
+ self:free_chain(mem_read(self.buf + dir_off(idx) + 2, 2))
+ end
+ else do
+ idx = self:dir_alloc()
+ if idx < 0 do puts("directory full\n") return 0 end
+ end
- const size = text:len()
- let first = 0
- let prev = 0
- let written = 0
- while written < size do
- const block = fat_alloc()
- if block == 0 do puts("disk full\n") return 0 end
- fat_set(block, END) # reserve
- if first == 0 do first = block end
- else do fat_set(prev, block) end
- mem_zero(fs.buf, BLOCK)
- let k = 0
- while (k < BLOCK and written < size) do
- mem_write(fs.buf + k, text[written]:ascii(), 1)
- k += 1
- written += 1
+ const size = text:len()
+ let first = 0
+ let prev = 0
+ let written = 0
+ while written < size do
+ const block = self:fat_alloc()
+ if block == 0 do puts("disk full\n") return 0 end
+ self:fat_set(block, END) # reserve
+ if first == 0 do first = block end
+ else do self:fat_set(prev, block) end
+ mem_zero(self.buf, BLOCK)
+ let k = 0
+ while (k < BLOCK and written < size) do
+ mem_write(self.buf + k, text[written]:ascii(), 1)
+ k += 1
+ written += 1
+ end
+ disk_write(block, self.buf)
+ prev = block
end
- disk_write(block, fs.buf)
- prev = block
- end
- disk_read(dir_lba(idx), fs.buf)
- const base = fs.buf + dir_off(idx)
- mem_write(base, 1, 1)
- mem_write(base + 1, name:len(), 1)
- mem_write(base + 2, first, 2)
- mem_write(base + 4, size, 4)
- for k in 0..name:len() do mem_write(base + 8 + k, name[k]:ascii(), 1) end
- disk_write(dir_lba(idx), fs.buf)
- return 1
-end
+ disk_read(dir_lba(idx), self.buf)
+ const base = self.buf + dir_off(idx)
+ mem_write(base, 1, 1)
+ mem_write(base + 1, name:len(), 1)
+ mem_write(base + 2, first, 2)
+ mem_write(base + 4, size, 4)
+ for k in 0..name:len() do mem_write(base + 8 + k, name[k]:ascii(), 1) end
+ disk_write(dir_lba(idx), self.buf)
+ return 1
+ end,
-# write a file, gee i wonder what a function called `fs_write` does
-fn fs_write(name, text) do
- if fs_store(name, text) == 1 puts("wrote " ~ name ~ " (" ~ text:len() ~ " bytes)\n")
-end
+ # write a file, gee i wonder what a function called `write` does
+ write = fn(self, name, text) do
+ if self:store(name, text) == 1 puts("wrote #{name} (#{text:len()} bytes)\n")
+ end,
-global fs_size = fn(name) do
- const idx = fs_find(name)
- if idx < 0 return -1
- disk_read(dir_lba(idx), fs.buf)
- return mem_read(fs.buf + dir_off(idx) + 4, 4)
-end
+ size = fn(self, name) do
+ const idx = self:find(name)
+ if idx < 0 return -1
+ disk_read(dir_lba(idx), self.buf)
+ return mem_read(self.buf + dir_off(idx) + 4, 4)
+ end,
-# read up to len bytes from offset, returned as a string (shorter at eof)
-global fs_read_bytes = fn(name, offset, len) do
- const idx = fs_find(name)
- if idx < 0 return ""
- disk_read(dir_lba(idx), fs.buf)
- const base = fs.buf + dir_off(idx)
- let block = mem_read(base + 2, 2)
- const size = mem_read(base + 4, 4)
- let stop = offset + len
- if stop > size do stop = size end
- let pos = 0
- let out = ""
- while (block != 0 and block != END and pos < stop) do
- const nxt = fat_get(block)
- if (pos + BLOCK > offset and pos < stop) do
- disk_read(block, fs.buf)
- let k = 0
- while (k < BLOCK) do
- const abs = pos + k
- if (abs >= offset and abs < stop) do out = out ~ string.of_ascii(mem_read(fs.buf + k, 1)) end
- k += 1
+ # read up to len bytes from offset, returned as a string (shorter at eof)
+ read_bytes = fn(self, name, offset, len) do
+ const idx = self:find(name)
+ if idx < 0 return ""
+ disk_read(dir_lba(idx), self.buf)
+ const base = self.buf + dir_off(idx)
+ let block = mem_read(base + 2, 2)
+ const size = mem_read(base + 4, 4)
+ let stop = offset + len
+ if stop > size do stop = size end
+ let pos = 0
+ let out = ""
+ while (block != 0 and block != END and pos < stop) do
+ const nxt = self:fat_get(block)
+ if (pos + BLOCK > offset and pos < stop) do
+ disk_read(block, self.buf)
+ let k = 0
+ while (k < BLOCK) do
+ const abs = pos + k
+ if (abs >= offset and abs < stop) do out = out ~ string.of_ascii(mem_read(self.buf + k, 1)) end
+ k += 1
+ end
end
+ pos += BLOCK
+ block = nxt
end
- pos += BLOCK
- block = nxt
- end
- return out
-end
+ return out
+ end,
-fn fs_cat(name) do
- if fs_ready() == :false return :nil
- const idx = fs_find(name)
- if idx < 0 do puts("cat: " ~ name ~ ": no such file\n") return :nil end
- disk_read(dir_lba(idx), fs.buf)
- const base = fs.buf + dir_off(idx)
- let block = mem_read(base + 2, 2)
- let remaining = mem_read(base + 4, 4)
- let out = ""
- while (block != 0 and block != END and remaining > 0) do
- const nxt = fat_get(block) # grab next before disk_read reuses fs.buf
- disk_read(block, fs.buf)
- const chunk = if remaining > BLOCK BLOCK else remaining
- for k in 0..chunk do out = out ~ string.of_ascii(mem_read(fs.buf + k, 1)) end
- remaining -= chunk
- block = nxt
- end
- puts(out)
- puts("\n")
-end
+ cat = fn(self, name) do
+ if self:ensure() == :false return :nil
+ const idx = self:find(name)
+ if idx < 0 do puts("cat: #{name}: no such file\n") return :nil end
+ disk_read(dir_lba(idx), self.buf)
+ const base = self.buf + dir_off(idx)
+ let block = mem_read(base + 2, 2)
+ let remaining = mem_read(base + 4, 4)
+ let out = ""
+ while (block != 0 and block != END and remaining > 0) do
+ const nxt = self:fat_get(block) # grab next before disk_read reuses self.buf
+ disk_read(block, self.buf)
+ const chunk = if remaining > BLOCK BLOCK else remaining
+ for k in 0..chunk do out = out ~ string.of_ascii(mem_read(self.buf + k, 1)) end
+ remaining -= chunk
+ block = nxt
+ end
+ puts(out)
+ puts("\n")
+ end,
-fn fs_ls() do
- if fs_ready() == :false return :nil
- let count = 0
- for i in 0..DIR_ENTRIES do
- disk_read(dir_lba(i), fs.buf)
- const base = fs.buf + dir_off(i)
- if mem_read(base, 1) == 1 do
- puts(" " ~ name_at(base, mem_read(base + 1, 1)) ~ " " ~ mem_read(base + 4, 4) ~ " bytes\n")
- count += 1
+ ls = fn(self) do
+ if self:ensure() == :false return :nil
+ let count = 0
+ for i in 0..DIR_ENTRIES do
+ disk_read(dir_lba(i), self.buf)
+ const base = self.buf + dir_off(i)
+ if mem_read(base, 1) == 1 do
+ puts(" #{self:name_at(base, mem_read(base + 1, 1))} #{mem_read(base + 4, 4)} bytes\n")
+ count += 1
+ end
end
- end
- if count == 0 puts(" (empty)\n")
-end
+ if count == 0 puts(" (empty)\n")
+ end,
+
+ rm = fn(self, name) do
+ if self:ensure() == :false return :nil
+ const idx = self:find(name)
+ if idx < 0 do puts("rm: #{name}: no such file\n") return :nil end
+ disk_read(dir_lba(idx), self.buf)
+ const base = self.buf + dir_off(idx)
+ self:free_chain(mem_read(base + 2, 2))
+ disk_read(dir_lba(idx), self.buf)
+ mem_write(self.buf + dir_off(idx), 0, 1)
+ disk_write(dir_lba(idx), self.buf)
+ puts("removed #{name}\n")
+ end,
+}
-fn fs_rm(name) do
- if fs_ready() == :false return :nil
- const idx = fs_find(name)
- if idx < 0 do puts("rm: " ~ name ~ ": no such file\n") return :nil end
- disk_read(dir_lba(idx), fs.buf)
- const base = fs.buf + dir_off(idx)
- free_chain(mem_read(base + 2, 2))
- disk_read(dir_lba(idx), fs.buf)
- mem_write(fs.buf + dir_off(idx), 0, 1)
- disk_write(dir_lba(idx), fs.buf)
- puts("removed " ~ name ~ "\n")
-end
+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, len) fs:read_bytes(name, offset, len)
diff --git a/kernel/main.rv b/kernel/main.rv
index 24852fe..4a6fb58 100644
--- a/kernel/main.rv
+++ b/kernel/main.rv
@@ -8,15 +8,12 @@ pic_unmask(1) # keyboard (irq1)
cmd_clear()
dump_memmap()
-fs_init()
-if disk_sectors() == 0 puts("no disk attached\n")
-else if fs.ready puts("mounted revelfs on disk\n")
-else do
- # blank disk, first boot after a clone. format it and drop the default files on
+fs:mount()
+if (disk_sectors() != 0 and fs.ready == :false) do
puts("blank disk, formatting + seeding default files\n")
- fs_format()
+ fs:format()
seed_fs()
end
-puts("revel shell, live in revo. type `help`\n% ")
+welcome()
flush_console()
diff --git a/kernel/proc.rv b/kernel/proc.rv
index 3303bc1..f76c511 100644
--- a/kernel/proc.rv
+++ b/kernel/proc.rv
@@ -43,12 +43,12 @@ fn run_prog(name) do
for i in 0..prog_count() do
if prog_name(i) == name do load_elf(prog_phys(i)) return :true end
end
- puts("no such program: " ~ name ~ "\n")
+ puts("no such program: #{name}\n")
return :false
end
fn list_progs() do
puts("programs:")
- for i in 0..prog_count() do puts(" " ~ prog_name(i)) end
+ for i in 0..prog_count() do puts(" #{prog_name(i)}") end
puts("\n")
end
diff --git a/kernel/seed.rv b/kernel/seed.rv
index 6c0a73f..72f0efd 100644
--- a/kernel/seed.rv
+++ b/kernel/seed.rv
@@ -1,5 +1,5 @@
# js dw about this ok?
fn seed_fs() do
- fs_write("i have big balls.txt", "dude my balls are actually massive")
- fs_write("balls.txt", "its lowk insane how big my balls are")
+ fs:write("i have big balls.txt", "dude my balls are actually massive")
+ fs:write("balls.txt", "its lowk insane how big my balls are")
end
diff --git a/kernel/shell.rv b/kernel/shell.rv
index 35eac37..f72387a 100644
--- a/kernel/shell.rv
+++ b/kernel/shell.rv
@@ -1,42 +1,80 @@
# 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("commands: help, clear, bars, echo <text>, time, mem, uptime, ps, run <prog>, zoom in|out\n")
- puts("fs: mkfs, ls, cat <name>, write <name> <text>, rm <name>\n")
- puts(" names with spaces go in \"quotes\"\n")
+ 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 <text>", "print it back")
+ puts("\n files\n")
+ help_row("ls", "list files")
+ help_row("cat <name>", "print a file")
+ help_row("write <name> <text>", "create or overwrite")
+ help_row("rm <name>", "delete a file")
+ help_row("mkfs", "format the disk")
+ puts("\n programs\n")
+ help_row("run", "list programs")
+ help_row("run <name>", "")
+ puts("\n")
+end
+
+fn welcome() do
+ let fsline = "no filesystem"
+ if fs.ready do fsline = "revelfs mounted" end
+ puts("\n")
+ puts(" a tiny os whose brain lives in revo, zig only touches the metal\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 i = start
- let name = ""
- if (i < line:len() and line[i]:ascii() == 34) do
- i += 1
- while (i < line:len() and line[i]:ascii() != 34) do name = name ~ line[i] i += 1 end
- if i < line:len() do i += 1 end
+ let quoted = :false
+ let begin = start
+ if (begin < line:len() and line[begin] == "\"") do begin += 1 quoted = :true end
+ # scan to the end of the name, a closing quote or a space depending on how it opened
+ 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]:ascii() != 32) do name = name ~ line[i] i += 1 end
+ while (i < line:len() and line[i] != " ") do i += 1 end
end
- if (i < line:len() and line[i]:ascii() == 32) do i += 1 end
- { name = name, rest = i }
+ const name = line[begin..i]
+ # step past the closing quote and the separating space so rest points at the text
+ 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
fn cmd_write(line) do
const a = arg_name(line, 6)
if a.name:len() == 0 do puts("usage: write <name> <text>\n") return :nil end
- const text = if line:len() > a.rest line:sub(a.rest, line:len() - a.rest) else ""
- fs_write(a.name, text)
+ fs:write(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 <name>\n") return :nil end
- fs_cat(a.name)
+ fs:cat(a.name)
end
fn cmd_rm(line) do
const a = arg_name(line, 3)
if a.name:len() == 0 do puts("usage: rm <name>\n") return :nil end
- fs_rm(a.name)
+ fs:rm(a.name)
end
fn zoom_by(delta) do
@@ -45,7 +83,7 @@ fn zoom_by(delta) do
if scale > 6 do scale = 6 end
con.scale = scale
cmd_clear()
- puts("zoom " ~ scale ~ "x\n")
+ puts("zoom #{scale}x\n")
end
fn cmd_zoom(words) do
@@ -55,32 +93,21 @@ fn cmd_zoom(words) do
else do puts("usage: zoom in | zoom out\n") end
end
-fn cmd_clear() do
- fill_rect(0, 0, fb_width(), fb_height(), CON_BG)
- con.col = 0
- con.row = 0
-end
-
-fn cmd_bars() do
- const bars = { 0xff5555, 0xffaa00, 0xffff55, 0x55ff55, 0x55ffff, 0x5599ff, 0xaa55ff, 0xffffff }
- const bw = fb_width() / 8
- for i in 0..8 do fill_rect(i * bw, 0, bw, 40, bars[i]) end
- puts("painted 8 bars from revo\n")
-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")
+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
+ "0x#{out}"
end
fn mem_type_name(kind) do
@@ -99,21 +126,19 @@ end
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")
+ 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")
+ 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 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 line:trim():len() == 0 do puts("% ") return :nil end
const words = line:split(" ")
const cmd = words[0]
- # run launches a process, its prompt gets reprinted by on_tick when it exits so
- # it lands below the programs output. no arg just lists whats available
if cmd == "run" do
if words:len() <= 1 do list_progs() puts("% ") return :nil end
if run_prog(words[1]) == :true return :nil
@@ -123,18 +148,17 @@ fn run(line) do
match cmd
| "help" => cmd_help()
| "clear" => cmd_clear()
- | "bars" => cmd_bars()
| "time" => cmd_time()
| "mem" => cmd_mem()
| "uptime" => cmd_uptime()
| "ps" => cmd_ps()
| "zoom" => cmd_zoom(words)
- | "mkfs" => fs_format()
- | "ls" => fs_ls()
+ | "mkfs" => fs:format()
+ | "ls" => fs:ls()
| "cat" => cmd_cat(line)
| "write" => cmd_write(line)
| "rm" => cmd_rm(line)
- | "echo" => if line:len() > 5 puts(line:sub(5, line:len() - 5) ~ "\n") else puts("\n")
- | _ => puts("unknown command: " ~ cmd ~ "\n")
+ | "echo" => if line:len() > 5 puts("#{line[5..]}\n") else puts("\n")
+ | _ => puts("unknown command: #{cmd}\n")
puts("% ")
end