feat(ps2): work in progressin this commit ps2 driver is in irq module (makes no sense), but ps2 was already a file
this will be part of cleanup
@@ -13,6 +13,7 @@ reboot:
jmp word 0xffff:0x0000
; void clear_screen()
+global clear_screen
clear_screen:
.clear:
mov ax, 0x0700 ; Entire screen
@@ -31,6 +32,7 @@ clear_screen:
ret
; void print_string(ds:si string)
+global print_string
print_string:
.print_char:
; Gets a character and compares it with NULL
@@ -51,6 +53,7 @@ print_string:
ret
; void read_disk(cx sector, al number-to-read, es:bx into)
+global read_disk
read_disk:
.read:
push ax
@@ -72,6 +75,7 @@ read_disk:
call reboot
; (ch cylinder, cl sector, dh head) calculate_chs(cx LBA-sector)
+global calculate_chs
calculate_chs:
sectorsPerTrack: equ 36
headsPerCylinder: equ 2
@@ -3,7 +3,7 @@ def say_hello
puts "Hello from Crystal!"
end
-
+# @deprecated: use KernelShim.outb
# def outb(port : UInt16, value : UInt8)
# asm (%(outb %al, %dx) : : "a"(value), "Nd"(port))
# end
@@ -1,12 +1,19 @@
bits 32
+extern kearly
extern kmain
-global set_cursor_pos
-global hide_cursor
-global show_cursor
+extern irq_dispatcher_c
+extern pic_send_eoi
+
+; void outb(uint16, uint8)
+global outb
+outb:
+ out dx, al
+ ret
-; Inputs: rdi = row, rsi = col
+; void set_cursor_pos(int32, int32)
+global set_cursor_pos
set_cursor_pos:
push ebp
mov ebp, esp
@@ -42,6 +49,7 @@ set_cursor_pos:
ret
; void hide_cursor(void)
+global hide_cursor
hide_cursor:
; Read: write index 0x0A, then write data with bit 5 set
; For simplicity we don't read current value; we set start = 0x20 (disabled)
@@ -56,6 +64,7 @@ hide_cursor:
ret
; void show_cursor(void)
+global show_cursor
show_cursor:
; Restore a typical cursor shape: start = 0 (scanline start)
mov dx, 0x3D4
@@ -77,12 +86,34 @@ show_cursor:
ret
+section .text
+
+global irq_common_entry
+irq_common_entry:
+ pushad
+ push eax
+ call irq_dispatcher_c
+ add esp, 4
+ popad
+ ret
+
+global irq1_stub
+irq1_stub:
+ cli
+ push byte 1
+ call irq_common_entry
+ push byte 1
+ call pic_send_eoi
+ add esp, 4
+ iret
+
section .entry
; void start()
global _start
_start:
mov esp, kernelStackStart
+ call kearly
call kmain
.hang:
@@ -1,55 +1,53 @@
require "./vga.cr"
-# IDT module
-module IDT
- # require "crystal/kernel" # core types
+ @[Packed]
+struct Entry
+ offset_low : UInt16
+ selector : UInt16
+ ist : UInt8
+ type_attr : UInt8
+ offset_mid : UInt16
+ offset_high : UInt32
+ zero : UInt32
+end
- IDT_ENTRIES = 256
+@[Packed]
+struct Ptr
+ limit : UInt16
+ base : UInt64
+end
- # Storage for the IDT entries
- @idt = StaticArray(Entry, IDT_ENTRIES)
-
- @[Packed]
- struct Entry
- offset_low : UInt16
- selector : UInt16
- ist : UInt8
- type_attr : UInt8
- offset_mid : UInt16
- offset_high : UInt32
- zero : UInt32
- end
+lib KernelShim
+ # External functions from assembly
+ # fun lidt(ptr : Ptr): Nil
+ # fun isr_handler_common(): Nil
+end
- @[Packed]
- struct Ptr
- limit : UInt16
- base : UInt64
- end
+# IDT module
+module IDT
- # External functions from assembly
- extern "C" def lidt(ptr : Ptr*): Nil
- {% if flag?(:linux) %}
- {% else %}
- {% end %}
- end
+ IDT_ENTRIES = 256
- # IRQ handlers storage
- @irq_handlers = StaticArray(Void*, IDT_ENTRIES)
+ # # Storage for the IDT entries
+ # @@idt : StaticArray(Entry, IDT_ENTRIES) = StaticArray(Entry, IDT_ENTRIES).new()
+ # property idt
- extern "C" def isr_handler_common: Nil
+ # # IRQ handlers storage
+ # @@irq_handlers : StaticArray(Proc(Nil, Nil), IDT_ENTRIES) = StaticArray(Proc(Nil, Nil), IDT_ENTRIES).new()
+ # property irq_handlers
- def self.register_irq(irq : Int32, handler : Void*) : Nil
- if irq >= 0 && irq < IDT_ENTRIES
- @irq_handlers[irq] = handler
- end
- end
+ # def self.register_irq(irq : Int32, handler : Proc(Nil, Nil)) : Nil
+ # if irq >= 0 && irq < IDT_ENTRIES
+ # @irq_handlers[irq] = handler
+ # end
+ # end
def self.init : Nil
- ptr = Ptr.new
- ptr.limit = (IDT_ENTRIES * Entry.size) - 1
- ptr.base = getaddr(&@idt).to_u64
- lidt(&ptr)
+ #ptr = Ptr.new
+ #ptr.limit = (IDT_ENTRIES * Entry.size) - 1
+ #ptr.base = getaddr(&@idt).to_u64
+ #lidt(&ptr)
- VGA.putd("IDT initialized\n")
+ # VGA.puts("IDT initialized\n")
end
end
@@ -0,0 +1,51 @@
+lib KernelShim
+ fun outb(port : UInt16, value : UInt8) : Void
+end
+
+module IRQ
+ IRQ_COUNT = 16
+ HANDLERS = StaticArray(Proc(UInt8, Void)?, IRQ_COUNT).new()
+
+ def self.init
+ self.register_keyboard
+ puts "IRQ initialized!\n"
+ end
+
+ private def self.register_keyboard
+ # todo: handle keyboard events
+ register(1, # IRQ 1 = keyboard
+ ->(scan_code : UInt8) {
+ if scan_code == 0x1C
+ puts "Enter\n"
+ end
+ # do smth
+ puts "keyboard event\n"
+ })
+ end
+
+ def self.register(irq : Int32, handler : Proc(UInt8, Void)) : Proc(UInt8, Void)?
+ return nil if irq < 0 || irq >= IRQ_COUNT
+ prev = HANDLERS[irq]
+ HANDLERS[irq] = handler
+ prev
+ end
+
+ def self.dispatcher(irq : UInt32)
+ i = irq.to_u8
+ h = HANDLERS[i]
+ h.call(i) if h
+ end
+end
+
+fun irq_dispatcher_c(irq : UInt32)
+ puts "irq_dispatcher_c\n"
+ IRQ.dispatcher(irq)
+end
+
+fun pic_send_eoi(irq : UInt32)
+ puts "pic_send_eoi\n"
+ if irq >= 8
+ KernelShim.outb(0xA0_u16, 0x20_u8)
+ end
+ KernelShim.outb(0x20_u16, 0x20_u8)
+end
@@ -1,7 +1,8 @@
-require "./tiny_alloc_i386.cr"
require "./vga.cr"
+require "./tiny_alloc_i386.cr"
+require "./idt.cr"
+require "./irq.cr"
# require "./pmm.cr"
-# require "./ps2.cr"
lib LibBootstrap
@[Packed]
@@ -17,18 +18,19 @@ fun kearly(info_ptr: LibBootstrap::StartInfo*)
end
fun kmain
- # PMM.init # Physical Memory Init
- # TinyAllocI386.init # Tiny Bump Allocator
-
VGA.init
-
VGA.puts(
"[CrystalOS Kernel] Hello from Crystal.\n",
color: VGA::Colors::BLACK_ON_CYAN,
)
+
+ IDT.init
+ IRQ.init # Should be PS2 ?
+ TinyAllocI386.init
+ # PMM.init # Physical Memory Init
+
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("\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"
@@ -44,6 +46,8 @@ fun kmain
# VGA.set_cursor(0, 5)
# VGA.hide_cursor
+
+ # asm (%(sti))
while true
asm (%(hlt))
@@ -1,12 +1,11 @@
-# PMM module - Physical Memory Manager
module PMM
PAGE_SIZE = 4096
KERNEL_HEAP_START = 0x200000_u64
KERNEL_HEAP_SIZE = 16 * 1024 * 1024
- TOTAL_PAGES = 40322 # KERNEL_HEAP_SIZE / PAGE_SIZE
+ TOTAL_PAGES = 4096 # KERNEL_HEAP_SIZE / PAGE_SIZE
- @@page_bitmap = StaticArray(UInt64, 40322).new()
+ @@page_bitmap = StaticArray(UInt64, TOTAL_PAGES).new()
@@heap_ptr = Pointer(UInt8).new(KERNEL_HEAP_START)
@@heap_end = Pointer(UInt8).new(KERNEL_HEAP_START + KERNEL_HEAP_SIZE)
@@ -5,7 +5,7 @@ module PS2
def self.init : Nil
# Initialize PS/2 controller
- VGA.puts("PS/2 initialized\n")
+ VGA.puts("PS/2 initialized!\n")
end
end
@@ -1,6 +1,4 @@
-# Minimal bump allocator + memset/memcpy for i386 bare-metal Crystal
-
-# Exports C symbols: malloc, free, memset, memcpy, calloc
+require "./vga.cr"
# 32-bit addresses/sizes (see linker.ld)
HEAP_START = 0x00108000_u32
@@ -44,11 +42,14 @@ end
# top-level var binding (becomes a global/static)
HEAP_STATE = HeapState.new
+# Minimal bump allocator for i386 (freestand)
module TinyAllocI386
def self.init
return if HEAP_STATE.inited
HEAP_STATE.heap_ptr = HEAP_STATE.heap_base
HEAP_STATE.inited = true
+
+ VGA.puts("TinyAllocI386 initialized!\n")
end
def self.align_up32(v : UInt32, align : UInt32) : UInt32
@@ -98,39 +99,3 @@ module TinyAllocI386
p
end
end
-
-# Expose C symbols via a lib block (no @[Export] needed)
-lib CExports
- fun malloc(size : UInt32) : Pointer(Void)
- fun free(ptr : Pointer(Void))
- fun memset(ptr : Pointer(Void), c : Int32, n : UInt32) : Pointer(Void)
- fun memcpy(dest : Pointer(Void), src : Pointer(Void), n : UInt32) : Pointer(Void)
- fun calloc(nmemb : UInt32, size : UInt32) : Pointer(Void)
-end
-
-# Providd implementations
-def malloc(size : UInt32) : Pointer(Void)
- TinyAllocI386.malloc32(size).as(Pointer(Void))
-end
-
-def free(ptr : Pointer(Void))
- # no-op
-end
-
-def memset(ptr : Pointer(Void), c : Int32, n : UInt32) : Pointer(Void)
- TinyAllocI386.memset_bytes(Pointer(UInt8).new(ptr.to_i), c.to_u8, n)
- ptr
-end
-
-def memcpy(dest : Pointer(Void), src : Pointer(Void), n : UInt32) : Pointer(Void)
- TinyAllocI386.memcpy_bytes(
- Pointer(UInt8).new(dest.to_i),
- Pointer(UInt8).new(src.to_i),
- n
- )
- dest
-end
-
-def calloc(nmemb : UInt32, size : UInt32) : Pointer(Void)
- TinyAllocI386.calloc32(nmemb, size).as(Pointer(Void))
-end
@@ -1,3 +1,4 @@
+require "./prelude/crystal_core/mem"
require "./prelude/atomic"
require "./prelude/primitives"
@@ -0,0 +1,47 @@
+fun memset(dst : UInt8*, c : USize, n : USize) : Void*
+ r0 = r1 = r2 = 0
+ asm(
+ "cld\nrep stosb"
+ : "={al}"(r0), "={Di}"(r1), "={cx}"(r2)
+ : "{al}"(c.to_u8), "{Di}"(dst), "{cx}"(n)
+ : "volatile", "memory"
+ )
+ dst.as(Void*)
+end
+
+fun memcpy(dst : UInt8*, src : UInt8*, n : USize) : Void*
+ r0 = r1 = r2 = 0
+ asm(
+ "cld\nrep movsb"
+ : "={Di}"(r0), "={Si}"(r1), "={cx}"(r2)
+ : "{Di}"(dst), "{Si}"(src), "{cx}"(n)
+ : "volatile", "memory"
+ )
+ dst.as(Void*)
+end
+
+fun memcmp(s1 : UInt8*, s2 : UInt8*, n : Int32) : Int32
+ while n > 0 && (s1.value == s2.value)
+ s1 += 1
+ s2 += 1
+ n -= 1
+ end
+ return 0 if n == 0
+ (s1.value - s2.value).to_i32
+end
+
+fun memmove(dst : UInt8*, src : UInt8*, n : USize) : Void*
+ if src.address < dst.address
+ src += n.to_i64
+ dst += n.to_i64
+ until n == 0
+ dst -= 1
+ src -= 1
+ dst.value = src.value
+ n -= 1
+ end
+ else
+ memcpy dst, src, n
+ end
+ dst.as(Void*)
+end
@@ -269,6 +269,34 @@ class String
def hash(hasher)
hasher.hash self
end
+
+ def interpolation(str)
+ parts = str.split(/\#\{({|})/)
+ result = ""
+ i = 0
+ while i < parts.size
+ if parts[i] == "{"
+ i += 1
+ expr = ""
+ depth = 1
+ while depth > 0
+ if parts[i] == "{"
+ depth += 1
+ elsif parts[i] == "}"
+ depth -= 1
+ end
+ expr += parts[i]
+ i += 1
+ end
+ expr = expr[1...-1] # Remove { and }
+ result += eval(expr).to_s
+ else
+ result += parts[i]
+ i += 1
+ end
+ end
+ result
+ end
def to_s
self
@@ -6,3 +6,10 @@ end
def p!(str : String)
VGA.puts str
end
+
+# STDIN = IO::FileDescriptor.new 0
+# STDOUT = IO::FileDescriptor.new 1
+# STDERR = IO::FileDescriptor.new 2
+# STDERR.buffer_size = 0
+
+# STDOUT.puts "hello from crystal\n"
@@ -65,7 +65,7 @@ echo -e "\nRunning..."
use_vnc=${USE_VNC:-false}
-if [ "$use_VNC" = true ]
+if [ "$use_vnc" = true ]
then
qemu-system-i386 -drive if=floppy,index=0,format=raw,file=crystalos.img || exit 1
else