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 global wd = { path = "/" } global prompt = fn() "revel:#{wd.path}$ " # runtime font bs fn embolden(byte, row) do return (byte bor (byte shl 1)) band 0xFF end fn slant(byte, row) do return (byte shl ((FONT_HEIGHT - 1 - row) shr 2)) band 0xFF end fn remap_glyphs(transform) do let out = {} for glyph_index in 0..FONT:len() do const glyph = FONT[glyph_index] let rows = {} for row_index in 0..FONT_HEIGHT do rows:push(transform(glyph[row_index], row_index)) end out:push(rows) end return out end global reverse8 = fn(byte) do let out = 0 for bit in 0..8 do if (byte shr bit) band 1 == 1 do out = out bor (1 shl (7 - bit)) end end return out end global fonts = {} global add_font = fn(name, glyphs, width, height, kind, path, size) do for index in 0..fonts:len() do if fonts[index].name == name do fonts[index].glyphs = glyphs fonts[index].width = width fonts[index].height = height fonts[index].kind = kind fonts[index].path = path fonts[index].size = size return :nil end end fonts:push({ name = name, glyphs = glyphs, width = width, height = height, kind = kind, path = path, size = size }) end global find_font = fn(name) do for index in 0..fonts:len() do if fonts[index].name == name return fonts[index] end return :nil end add_font("vga", FONT, FONT_WIDTH, FONT_HEIGHT, :bitmap, :nil, 0) add_font("bold", remap_glyphs(embolden), FONT_WIDTH, FONT_HEIGHT, :bitmap, :nil, 0) add_font("italic", remap_glyphs(slant), FONT_WIDTH, FONT_HEIGHT, :bitmap, :nil, 0) 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"), # this is now font dependent cell_width = fn(self) self.font_width * self.scale, cell_height = fn(self) self.font_height * self.scale, columns = fn(self) fb_width() / self:cell_width(), rows = fn(self) fb_height() / self:cell_height(), set_font = fn(self, entry) do self.font = entry.glyphs self.font_width = entry.width self.font_height = entry.height self.font_entry = entry # a ttf renders at its true pixel size so we js bitmap zoom scale if entry.kind == :ttf do self.scale = 1 end self:clear() end, draw_glyph = fn(self, column, row, character) do const origin_x = column * self:cell_width() const origin_y = row * self:cell_height() fill_rect(origin_x, origin_y, self:cell_width(), self:cell_height(), BG_COLOR) # clear the cell const code = character:ascii() if code < 32 return :nil if code > 127 return :nil const glyph = self.font[code - 32] 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) end end end, newline = fn(self) do self.column = 0 self.row += 1 if self.row >= self:rows() do fb_scroll(self:cell_height(), BG_COLOR) 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 putc = fn(self, character) do const code = character:ascii() 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 if self.column > 0 self.column -= 1 return :nil end if code < 32 return :nil self:draw_glyph(self.column, self.row, character) self.column += 1 if self.column >= self:columns() self:newline() end, clear = fn(self) do fill_rect(0, 0, fb_width(), fb_height(), BG_COLOR) self.column = 0 self.row = 0 end, cursor_clear = fn(self) do if self.shown do fill_rect(self.cursor_x * self:cell_width(), self.cursor_y * self:cell_height(), self:cell_width(), self:cell_height(), BG_COLOR) self.shown = :false end end, cursor_show = fn(self) do self.cursor_x = self.column self.cursor_y = self.row fill_rect(self.cursor_x * self:cell_width(), self.cursor_y * self:cell_height(), self:cell_width(), self:cell_height(), CURSOR_COLOR) 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, } global puts = fn(text) serial_puts(text) global flush_console = fn() do console:cursor_clear() console:drain() console.blink = 0 console:cursor_show() end global on_tick = fn(ticks) do let byte = serial_next() if byte >= 0 do console:cursor_clear() console:putc(string.of_ascii(byte)) console:drain() console.blink = 0 console:cursor_show() return :nil end if proc_exited() do console:cursor_clear() puts(prompt()) console:drain() console.blink = 0 console:cursor_show() return :nil end console.blink += 1 if console.blink >= 50 do console.blink = 0 if console.shown console:cursor_clear() else console:cursor_show() end end