feat(vga): add and ESC (wip) support for VGA.puts@@ -26,11 +26,21 @@ fun kmain
"[CrystalOS Kernel] Hello from Crystal.\n",
color: VGA::Colors::BLACK_ON_CYAN,
)
- VGA.puts("weeeeeeee\n")
+ puts("weeeeeeee\n")
VGA.puts("Hello world!\n \nWorks maybe?\n")
VGA.puts("Can print on multiple lines\n", color: VGA::Colors::BLACK_ON_LIME)
- VGA.puts("\n\nWe still don't have a heap, but now the puts stores the last row and col so it can call the putchar method with the right col / row instead of overlaying subsequent calls on-top one of another (this is super long line to test word wrap).\n")
+ VGA.puts("\nWe still don't have a heap, but now the puts stores the last row and col so it can call the putchar method with the \x1b[32mright\x1b[0m col / row instead of overlaying subsequent calls on-top one of another (this is super long line to test word wrap).\n")
+ VGA.puts "\nFeatures:\n"
+ VGA.puts("\tIt supports \\t char at start\taswell as in text.\n\tIt supports \\n char at end aswell as in text.\n")
+ VGA.puts "\t"
+ VGA.puts "It support writing on same line"
+ VGA.puts " "
+ VGA.puts "(YAY)", VGA::Colors::GREEN_ON_WHITE
+ VGA.puts "\n"
+ VGA.puts("\tCan use custom colors", VGA::Colors::RED_ON_BLACK)
+ VGA.puts("\tCan use another custom color\n", VGA::Colors::GREEN_ON_BLACK)
+ VGA.puts("\tCan use lot of custom color\n", VGA::Colors::CYAN_ON_BLACK)
# VGA.set_cursor(0, 5)
# VGA.hide_cursor
@@ -8,14 +8,6 @@ lib KernelShim
)
end
-struct VGACursor
- @col : Int32 = 0
- property col
-
- @row : Int32 = 0
- property row
-end
-
module VGA
# VGA text mode buffer address (0xB8000)
VGA_ADDRESS = 0xB8000_u64
@@ -25,17 +17,49 @@ module VGA
VGA_WIDTH = 80
VGA_HEIGHT = 25
VGA_SIZE = VGA_WIDTH * VGA_HEIGHT
-
- # Cursor state
- VGA_CURSOR = VGACursor.new
+
+ # Misc
+ TAB_SIZE = 2
# Default color: white text on black background
DEFAULT_COLOR = 0x07_u8
+ def self.color_code(
+ fg : VGA::Color,
+ bg : VGA::Color,
+ #char : UInt8
+ ) : UInt16
+ (bg.value << 4) | fg.value
+ # (attrib << 8) | char.to_u8
+ end
+
+ enum Color : UInt16
+ Black = 0
+ Blue = 1
+ Green = 2
+ Cyan = 3
+ Red = 4
+ Magenta = 5
+ Brown = 6
+ LightGray = 7
+ DarkGray = 8
+ LightBlue = 9
+ LightGreen = 10
+ LightCyan = 11
+ LightRed = 12
+ Pink = 13
+ Yellow = 14
+ White = 15
+ end
+
module Colors
- DEFAULT = 0x07_u8
- BLACK_ON_CYAN = 0xB0_u8
- BLACK_ON_LIME = 0xA0_u8
+ DEFAULT = 0x07_u8
+ BLACK_ON_CYAN = 0xB0_u8
+ BLACK_ON_LIME = 0xA0_u8
+ RED_ON_BLACK = 0x04_u8
+ GREEN_ON_BLACK = 0x02_u8
+ CYAN_ON_BLACK = 0x03_u8
+ GREEN_ON_WHITE = 0xF2_u8
end
# Initialize VGA subsystem (clear screen)
@@ -67,12 +91,13 @@ module VGA
# Set typing cursor position
def self.set_cursor(
- col : Int32,
+ next_col : Int32,
next_row : Int32
) : Nil
KernelShim
.set_cursor_pos(next_row.to_u32, col.to_u32)
@@row = next_row
+ @@col = next_col
end
@@puts_count = 0
@@ -94,15 +119,6 @@ module VGA
@@col ||= i || 0
@@row ||= line || 0
- # somehow doesn't work...
- if ch == 0x0A_u8 # \n handler
- @@col = 0
- @@row += 1
- if @@row >= VGA_HEIGHT
- @@row = VGA_HEIGHT - 1
- end
- end
-
offset = (@@row * VGA_WIDTH + @@col) * 2
(VGA_BUFFER + offset).value = ch
@@ -126,21 +142,66 @@ module VGA
color : UInt8? = Colors::DEFAULT,
line : Int32? = nil,
) : Nil
- i = 0
+ @@col ||= 0
@@row ||= (line || 0)
+ i = 0
while i < str.size
ch = (str.to_unsafe + i).value
- if ch == 0x0A_u8
+
+ # todo: support more control chars
+ # - \b - 0x08 : Backspace (col - 1)
+ # - \a - 0x07 : Bell (ring sound) ?
+ # - \0 - 0x00 : NUL string terminator
+ # - \r - 0x0D : Cariage Return (col = 0)
+ # - \f - 0x0C : Form Feed (row += VGA_HEIGHT)
+ # - ESC - 0x1B : Escape (colors, etc)
+ if ch == 0x0A_u8 # \n
@@row += 1
@@col = 0
i += 1
next
+ elsif ch == 0x09_u8 # \t
+ @@col += TAB_SIZE
+ i += 1
+ next
+ elsif ch == 0x1B_u8 # \ESC
+ # i.e. reset all: \x1b[0m
+ # i.e. fg green: \x1b[32m
+ # i.e. bg green: \x1b[42m
+ # read next chars til' "m"
+ # convert args to color
+ # set cell color (as default)
+ # skip args (don't print)
+ j = 0
+ # args = [] of String
+ is_reset = false
+ while ((str.to_unsafe + i + j).value) != 0x6D_u8 # m letter
+ # do something with args
+ # i + 1 = [ start
+ # i + 2..; = fg color
+ # i + ;..; = bg color (optional)
+ # i + ;..; = style bit (optional)
+ # i + ;..m = end
+ if str[i+j+1] == '[' && str[i+j+2] == '0' && str[i+j+3] == 'm'
+ is_reset = true
+ else
+ is_reset = false
+ end
+ j += 1
+ end
+ #(str.to_unsafe + i + j).value = 0xB0_u8
+ i += j # skip ESC sequence length chars
+ puts "[" if is_reset == false
+ puts "]" if is_reset
+ i += 1
+ next
end
- putchar(ch, i, @@row, color)
+
+ putchar(ch, @@col, @@row, color)
i += 1
end
- VGA.set_cursor(0, @@row)
+ VGA.set_cursor(@@col, @@row)
end
end
@@ -23,5 +23,8 @@ require "./prelude/crystal_core/string"
require "./prelude/crystal_core/reference"
require "./prelude/crystal_core/static_array"
+# needs support for pmm first
+# require "./prelude/crystal_core/array"
+
# crystal api
require "./prelude/puts"
@@ -44,7 +44,7 @@ class Array(T)
def initialize(initial_capacity : Int = 0)
if initial_capacity > 0
- @buffer = Pointer(T).malloc_atomic(initial_capacity.to_u64)
+ @buffer = Pointer(T).new(initial_capacity.to_u64)
recalculate_capacity
end
end
@@ -127,6 +127,15 @@ class Array(T)
@size = 0
end
end
+
+ def join(sep : String? = "")
+ str = ""
+ @size.times do |i|
+ str += @buffer[i]
+ str += sep
+ end
+ str
+ end
@[NoInline]
def mark(&block : Void* ->)
@@ -1,19 +1,8 @@
-fun print_string(str : UInt8*)
-end
-
-VGA_ADDRESS = 0xb8000_u64
-
+# Global `puts` method
def puts(str : String)
- _str : Pointer(UInt8) = str.to_unsafe
- video_memory = Pointer(UInt8).new(VGA_ADDRESS)
- i = 0
- ch = _str.value
- while ch > 0
- ch = (_str + i).value
- break if ch == 0
- (video_memory + (i * 2)).value = ch
- (video_memory + (i * 2) + 1).value = 0x07_u8
- i += 1
- end
+ VGA.puts str
end
+def p!(str : String)
+ VGA.puts str
+end