refactor: make code more readable/maintainable@@ -1,4 +1,5 @@
; void reboot()
+global reboot
reboot:
.notify:
mov si, rebootMessage
@@ -1,51 +0,0 @@
-#!/bin/bash
-
-# Flags
-
-CC_FLAGS="-ffreestanding -nostdinc -nostdinc++ \
- -Wall -Wextra \
- -o ./bin/kernel.bin -target i386-pc-none-elf \
- -I ./kernel/"
-LD_FLAGS="-nostdlib -Wl,--oformat=binary,-T./kernel/linker.ld"
-CPP_FILES="./kernel/unityBuild.cpp ./build/kernelEntryPoint.o"
-
-# Build
-
-mkdir {bin,build} 2> /dev/null
-
-echo -e "Cleaning..."
-rm *.bin *.img *.iso *.o *.vmdk 2> /dev/null
-
-echo -e "\nBuilding..."
-nasm ./boot/bootloader.asm -I ./boot/ -o ./bin/bootloader.bin || exit 1
-nasm -felf32 ./kernel/entry.asm -o ./build/kernelEntryPoint.o || exit 1
-
-use_crystal_kernel=${USE_CRYSTAL_KERNEL:-true}
-
-if [ "$use_crystal_kernel" = true ]
-then
- # Build crystal kernel without stdlib, using the custom prelude, and targeting i386-unknown-linux-elf
- crystal build \
- --no-debug -Dkernel \
- --cross-compile --target i386-unknown-linux-elf \
- --prelude=../prelude.cr \
- -o ./build/kernel ./kernel/kernel.cr
-
- # Link the kernel into an ELF executable, then convert it to a flat binary for the bootloader.
- ld -m elf_i386 -nostdlib -T ./kernel/linker.ld -o ./bin/kernel.elf ./build/kernelEntryPoint.o ./build/kernel.o || exit 1
- objcopy -O binary ./bin/kernel.elf ./bin/kernel.bin || exit 1
-else
- clang++ $CC_FLAGS $LD_FLAGS $CPP_FILES || exit 1
-fi
-
-cp ./bin/kernel.bin ./iso/boot/kernel.bin || exit 1
-
-./tools/genVDisk --output "crystalos.img" --floppy \
- --bootloader ./bin/bootloader.bin --kernel ./bin/kernel.bin
-
-# Run
-
-echo -e "\nRunning..."
-qemu-system-i386 -drive if=floppy,index=0,format=raw,file=crystalos.img || exit 1
-
-exit 0
@@ -1,4 +1,50 @@
def say_hello
puts "Hello from Crystal!"
-end
+end
+
+
+# def outb(port : UInt16, value : UInt8)
+# asm (%(outb %al, %dx) : : "a"(value), "Nd"(port))
+# end
+
+# def set_cursor_position(row : Int32, col : Int32)
+# position = (80 * row + col).to_u16
+
+# # # Write the high byte
+# # outb(0x3D4_u16, 0x0E_u8);
+# # outb(0x3D5_u16, ((position >> 8) & 0xFF).to_u8);
+
+# # # Write the low byte
+# # outb(0x3D4_u16, 0x0F_u8);
+# # outb(0x3D5_u16, (position & 0xFF).to_u8);
+# end
+
+# def clear_screen
+# video_memory = Pointer(UInt8).new(0xB8000_u64)
+# screen_size = 80 * 25
+# i = 0
+# while i < screen_size
+# (video_memory + (i * 2)).value = " ".to_unsafe.value
+# (video_memory + (i * 2) + 1).value = 0x07_u8
+# i += 1
+# end
+# end
+
+# message = "[CrystalOS Kernel] Hello from Crystal"
+
+# clear_screen
+# set_cursor_position(0, 0)
+
+# _str : Pointer(UInt8) = message.to_unsafe
+# video_memory = Pointer(UInt8).new(0xB8000_u64)
+# 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 = 0xB0_u8
+# i += 1
+# end
+
@@ -0,0 +1,18 @@
+module Boot
+ # Kernel entry point (converted from main.c's kmain)
+ def self.kmain : Nil
+ # Initialize core kernel subsystems in order
+ IDT.init
+ Timer.init
+ PS2.init
+ VGA.init
+
+ # Enable hardware interrupts
+ asm("sti")
+
+ # Main kernel loop (halt until next interrupt)
+ loop do
+ asm("hlt")
+ end
+ end
+end
@@ -0,0 +1,55 @@
+require "./vga.cr"
+
+# IDT module
+module IDT
+ # require "crystal/kernel" # core types
+
+ IDT_ENTRIES = 256
+
+ # 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
+
+ @[Packed]
+ struct Ptr
+ limit : UInt16
+ base : UInt64
+ end
+
+ # External functions from assembly
+ extern "C" def lidt(ptr : Ptr*): Nil
+ {% if flag?(:linux) %}
+ {% else %}
+ {% end %}
+ end
+
+ # IRQ handlers storage
+ @irq_handlers = StaticArray(Void*, IDT_ENTRIES)
+
+ extern "C" def isr_handler_common: Nil
+
+ def self.register_irq(irq : Int32, handler : Void*) : 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)
+
+ VGA.putd("IDT initialized\n")
+ end
+end
@@ -1,3 +1,8 @@
+require "./tiny_alloc_i386.cr"
+require "./vga.cr"
+# require "./pmm.cr"
+# require "./ps2.cr"
+
lib LibBootstrap
@[Packed]
struct StartInfo
@@ -7,64 +12,36 @@ lib LibBootstrap
end
fun kearly(info_ptr: LibBootstrap::StartInfo*)
-
# Get the startup info
info = info_ptr.value
-
- # Initialize stuff
- # GDT.init
- # Heap.init info.end_of_kernel
- # PIC.remap
- # PIC.enable
- # IDT.init
-end
-
-def outb(port : UInt16, value : UInt8)
- asm (%(outb %al, %dx) : : "a"(value), "Nd"(port))
-end
-
-def set_cursor_position(row : Int32, col : Int32)
- position = (80 * row + col).to_u16
-
- # # Write the high byte
- # outb(0x3D4_u16, 0x0E_u8);
- # outb(0x3D5_u16, ((position >> 8) & 0xFF).to_u8);
-
- # # Write the low byte
- # outb(0x3D4_u16, 0x0F_u8);
- # outb(0x3D5_u16, (position & 0xFF).to_u8);
-end
-
-def clear_screen
- video_memory = Pointer(UInt8).new(0xB8000_u64)
- screen_size = 80 * 25
- i = 0
- while i < screen_size
- (video_memory + (i * 2)).value = " ".to_unsafe.value
- (video_memory + (i * 2) + 1).value = 0x07_u8
- i += 1
- end
end
fun kmain
- message = "[CrystalOS Kernel] Hello from Crystal"
+ # PMM.init # Physical Memory Init
+ # TinyAllocI386.init # Tiny Bump Allocator
+
+ VGA.init
+
+ VGA.puts(
+ "[CrystalOS Kernel] Hello from Crystal.",
+ VGA::Colors::BLACK_ON_CYAN,
+ )
+ VGA.puts("\n")
+ VGA.puts("Hello world!\nWorks maybe?\n", line: 1)
+ VGA.puts("Can print on multiple lines\n", line: 2)
+ VGA.puts("But because we don't have a Heap, we need to set lines number in puts call for now...\n", line: 3)
+ # VGA.puts(%(
+ # Works well
+ # multiline buffer?
+ # ),
+ # VGA::Colors::BLACK_ON_LIME,
+ # )
+ # VGA.puts("\n\n\n")
+ # VGA.puts "hello world\n"
- clear_screen
- set_cursor_position(0, 0)
-
- _str : Pointer(UInt8) = message.to_unsafe
- video_memory = Pointer(UInt8).new(0xB8000_u64)
- 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 = 0xB0_u8
- i += 1
- end
+ #VGA.dump_cursor_and_cells
while true
+ asm (%(hlt))
end
end
-
@@ -0,0 +1,8 @@
+module KernelAPI
+ # Kernel module structure (converted from module.h)
+ struct KernelModule
+ name : UInt8* # Module name (null-terminated string)
+ init : -> Nil # Module initialization function
+ uninit : -> Nil # Module cleanup function
+ end
+end
@@ -0,0 +1,57 @@
+# 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
+
+ @@page_bitmap = StaticArray(UInt64, 40322).new()
+ @@heap_ptr = Pointer(UInt8).new(KERNEL_HEAP_START)
+ @@heap_end = Pointer(UInt8).new(KERNEL_HEAP_START + KERNEL_HEAP_SIZE)
+
+ def self.init : Nil
+ TOTAL_PAGES.times do |i|
+ clear_page_used(i.to_u64)
+ end
+ end
+
+ private def self.set_page_used(page : UInt64) : Nil
+ @@page_bitmap[page // 64] |= (1_u64 << (page % 64))
+ end
+
+ private def self.clear_page_used(page : UInt64) : Nil
+ @@page_bitmap[page // 64] &= ~(1_u64 << (page % 64))
+ end
+
+ private def self.is_page_used(page : UInt64) : Bool
+ ((@@page_bitmap[page // 64] >> (page % 64)) & 1_u64) == 1_u64
+ end
+
+ def self.alloc_page : Pointer(Void)?
+ TOTAL_PAGES.times do |i|
+ unless is_page_used(i)
+ set_page_used(i)
+ return Pointer(Void).new(KERNEL_HEAP_START + i * PAGE_SIZE)
+ end
+ end
+ nil
+ end
+
+ def self.free_page(page : Pointer(Void)) : Nil
+ addr = page.address
+ return if addr < KERNEL_HEAP_START || addr >= KERNEL_HEAP_START + KERNEL_HEAP_SIZE
+ page_idx = (addr - KERNEL_HEAP_START) // PAGE_SIZE
+ clear_page_used(page_idx)
+ end
+
+ def self.kalloc(bytes : UInt64) : Pointer(Void)?
+ size = (bytes + 7) & ~7
+ ret = @@heap_ptr
+ if @@heap_ptr + size > @@heap_end
+ return nil
+ end
+ @@heap_ptr += size
+ ret
+ end
+end
@@ -0,0 +1,11 @@
+require "./vga.cr"
+
+# PS2 module - Keyboard/Mouse driver
+module PS2
+
+ def self.init : Nil
+ # Initialize PS/2 controller
+ VGA.puts("PS/2 initialized\n")
+ end
+
+end
@@ -0,0 +1,18 @@
+require "./vga.cr"
+
+# Timer module - System timer
+module Timer
+ TIMER_FREQ = 1000_u32
+
+ # External assembly ISR for timer
+ fun timer_isr_common : Void
+
+ def self.init : Nil
+ # Set up PIT or HPET here (placeholder)
+ VGA.puts("Timer initialized\n")
+ end
+
+ def self.enable : Nil
+ # Enable timer IRQ
+ end
+end
@@ -0,0 +1,136 @@
+# Minimal bump allocator + memset/memcpy for i386 bare-metal Crystal
+
+# Exports C symbols: malloc, free, memset, memcpy, calloc
+
+# 32-bit addresses/sizes (see linker.ld)
+HEAP_START = 0x00108000_u32
+HEAP_SIZE = 0x00008000_u32
+
+struct HeapState
+ @heap_base : UInt32 = HEAP_START
+ @heap_top : UInt32 = HEAP_START + HEAP_SIZE
+ @heap_ptr : UInt32 = HEAP_START
+ @inited : Bool = false
+
+ def heap_base
+ @heap_base
+ end
+ def heap_base=(v : UInt32)
+ @heap_base = v
+ end
+
+ def heap_top
+ @heap_top
+ end
+ def heap_top=(v : UInt32)
+ @heap_top = v
+ end
+
+ def heap_ptr
+ @heap_ptr
+ end
+ def heap_ptr=(v : UInt32)
+ @heap_ptr = v
+ end
+
+ def inited
+ @inited
+ end
+ def inited=(v : Bool)
+ @inited = v
+ end
+end
+
+# top-level var binding (becomes a global/static)
+HEAP_STATE = HeapState.new
+
+module TinyAllocI386
+ def self.init
+ return if HEAP_STATE.inited
+ HEAP_STATE.heap_ptr = HEAP_STATE.heap_base
+ HEAP_STATE.inited = true
+ end
+
+ def self.align_up32(v : UInt32, align : UInt32) : UInt32
+ mask = align - 1_u32
+ (v + mask) & ~mask
+ end
+
+ def self.malloc32(size : UInt32, align : UInt32 = 8_u32) : Pointer(UInt8)?
+ init_if_needed
+ return nil if size == 0_u32
+ a = align_up32(HEAP_STATE.heap_ptr, align)
+ new_ptr = a + size
+ if new_ptr > HEAP_STATE.heap_top
+ return nil
+ end
+ HEAP_STATE.heap_ptr = new_ptr
+ Pointer(UInt8).new(a.to_u64)
+ end
+
+ def self.reset_heap
+ HEAP_STATE.heap_ptr = HEAP_STATE.heap_base
+ end
+
+ def self.memset_bytes(ptr : Pointer(UInt8), c : UInt8, n : UInt32) : Pointer(UInt8)
+ i = 0_u32
+ while i < n
+ (ptr + i).value = c
+ i += 1
+ end
+ ptr
+ end
+
+ def self.memcpy_bytes(dst : Pointer(UInt8), src : Pointer(UInt8), n : UInt32) : Pointer(UInt8)
+ i = 0_u32
+ while i < n
+ (dst + i).value = (src + i).value
+ i += 1
+ end
+ dst
+ end
+
+ def self.calloc32(nmemb : UInt32, size : UInt32, align : UInt32 = 8_u32) : Pointer(UInt8)?
+ total = nmemb * size
+ p = malloc32(total, align)
+ return nil if p.nil?
+ memset_bytes(p, 0_u8, total)
+ 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
@@ -0,0 +1,124 @@
+bootDriveNumber: db 0
+
+diskErrorMessage: db "Error: Failed to read disk!", CR, LF, 0
+rebootMessage: db "Press any key to reboot...", CR, LF, 0
+
+; void reboot()
+global reboot
+reboot:
+ .notify:
+ mov si, rebootMessage
+ call print_string
+
+ .wait_for_key_press:
+ xor ax, ax
+ int 0x16
+
+ .restart:
+ jmp word 0xffff:0x0000
+
+; void clear_screen()
+clear_screen:
+ .clear:
+ mov ax, 0x0700 ; Entire screen
+ mov bx, 0x07 ; Colour (black background, white foreground)
+ xor cx, cx ; Top-left of screen is (0, 0)
+ mov dx, 0x184f ; Screen size: 24 rows x 79 columns
+ int 0x10
+
+ .move_cursor:
+ mov ax, 0x02
+ xor dx, dx ; Move to (0, 0)
+ xor bh, bh ; Page 0
+ int 0x10
+
+ .cleanup:
+ ret
+
+; void print_string(ds:si string)
+print_string:
+ .print_char:
+ ; Gets a character and compares it with NULL
+ mov al, [ds:si]
+ cmp al, 0
+ je .done
+
+ ; Calls the interuupt to print a character
+ mov ah, 0x0e
+ xor bx, bx
+ int 0x10
+
+ ; Move to the next character
+ inc si
+ jmp .print_char
+
+ .done:
+ ret
+
+; void read_disk(cx sector, al number-to-read, es:bx into)
+read_disk:
+ .read:
+ push ax
+ push bx
+ call calculate_chs
+ pop bx
+ pop ax
+
+ mov dl, [bootDriveNumber]
+ mov ah, 0x02
+ int 0x13
+
+ jc .read_failed
+ ret
+
+ .read_failed:
+ mov si, diskErrorMessage
+ call print_string
+ call reboot
+
+; (ch cylinder, cl sector, dh head) calculate_chs(cx LBA-sector)
+calculate_chs:
+ sectorsPerTrack: equ 36
+ headsPerCylinder: equ 2
+
+ .calculate_sector:
+ xor dx, dx
+ mov ax, cx
+ mov bx, sectorsPerTrack
+ div bx ; LBA div/mod SPT
+ inc dx
+ mov [tempSector], dl
+
+ .calculate_head:
+ ; ax already contains quotient of LBA / SPT
+ xor dx, dx
+ mov bx, headsPerCylinder
+ div bx
+ mov [tempHead], dl
+
+ .calculate_cylinder:
+ xor dx, dx
+ mov ax, cx
+ mov bx, sectorsPerTrack * headsPerCylinder
+ div bx
+ mov [tempCylinder], ax
+
+ .finish:
+ ; cx: -- CH -- -- CL --
+ ; Cylinder: 76543210 98
+ ; Sector: 543210
+ movzx cx, byte [tempSector]
+ mov ax, word [tempCylinder]
+ shl ax, 8
+ or cx, ax
+ mov ax, word [tempCylinder]
+ and ax, 0xc000
+ shr ax, 8
+ or cx, ax
+ mov dh, byte [tempHead]
+
+ ret
+
+tempCylinder: dw 0
+tempHead: db 0
+tempSector: db 0
@@ -0,0 +1,148 @@
+struct VGACursor
+ col : Int32 = 0
+ row : Int32 = 0
+
+ def initialize
+ @col = 0
+ @row = 0
+ end
+
+ def col : Int32
+ @col
+ end
+ def col=(c : Int32)
+ @col = c
+ end
+ def col_incr(c : Int32)
+ @col += c
+ end
+
+ def row : Int32
+ @row
+ end
+ def row=(r : Int32)
+ @row = r
+ end
+ def row_incr(r : Int32)
+ @row += r
+ end
+end
+
+module VGA
+ # VGA text mode buffer address (0xB8000)
+ VGA_ADDRESS = 0xB8000_u64
+ VGA_BUFFER = Pointer(UInt8).new(VGA_ADDRESS)
+
+ # Buffer size
+ VGA_WIDTH = 80
+ VGA_HEIGHT = 25
+ VGA_SIZE = VGA_WIDTH * VGA_HEIGHT
+
+ # Cursor state
+ VGA_CURSOR = VGACursor.new
+
+ # Default color: white text on black background
+ DEFAULT_COLOR = 0x07_u8
+
+ module Colors
+ DEFAULT = 0x07_u8
+ BLACK_ON_CYAN = 0xB0_u8
+ BLACK_ON_LIME = 0xA0_u8
+ end
+
+ # Initialize VGA subsystem (clear screen)
+ def self.init : Nil
+ clear
+ end
+
+ # Clear the entire VGA screen
+ def self.clear : Nil
+ i = 0
+ while i < VGA_SIZE
+ (VGA_BUFFER + (i * 2)).value = " ".to_unsafe.value
+ (VGA_BUFFER + (i * 2) + 1).value = Colors::DEFAULT
+ i += 1
+ end
+ VGA_CURSOR.col = 0
+ VGA_CURSOR.row = 0
+ end
+
+ # Write a single character to the current cursor position
+ def self.putchar(
+ ch : UInt8,
+ i : Int32? = 0,
+ line : Int32? = 0,
+ color : UInt8? = Colors::DEFAULT
+ ) : Nil
+ VGA_CURSOR.col = i
+ VGA_CURSOR.row = line
+
+ # Simplified cursor handling (replace with actual cursor logic later)
+ if ch == ("\n".to_unsafe).value # 0x0A_u8 # \n
+ VGA_CURSOR.col = 0
+ VGA_CURSOR.row += 1
+ if VGA_CURSOR.row >= VGA_HEIGHT
+ VGA_CURSOR.row = VGA_HEIGHT - 1
+ end
+ return
+ end
+
+ offset = (VGA_CURSOR.row * VGA_WIDTH + VGA_CURSOR.col) * 2
+
+ (VGA_BUFFER + offset).value = ch
+ (VGA_BUFFER + offset + 1).value = color
+
+ VGA_CURSOR.col += 1
+
+ if VGA_CURSOR.col >= VGA_WIDTH
+ VGA_CURSOR.col = 0
+ VGA_CURSOR.row += 1
+
+ if VGA_CURSOR.row >= VGA_HEIGHT
+ VGA_CURSOR.row = VGA_HEIGHT - 1
+ end
+ end
+
+ # Wrap around if past screen
+ # i %= VGA_SIZE
+ end
+
+ # Write a string to VGA
+ def self.puts(
+ str : String,
+ color : UInt8? = Colors::DEFAULT,
+ line : Int32? = 0,
+ ) : Nil
+ i = 0
+ while i < str.size - 1
+ ch = (str.to_unsafe + i).value
+ putchar(ch, i, line, color)
+ i += 1
+ end
+ end
+
+ def self.debug_write_at(r : Int32, c : Int32, s : String, color : UInt8 = Colors::DEFAULT)
+ off = ((r * VGA_WIDTH) + c) * 2
+ i = 0
+ ptr = s.to_unsafe
+ while i < s.bytesize
+ (VGA_BUFFER + off + (i * 2)).value = (ptr + i).value
+ (VGA_BUFFER + off + (i * 2) + 1).value = color
+ i += 1
+ end
+ end
+
+ def self.dump_cursor_and_cells
+ # Write cursor state at row 2 col 0
+ # debug_write_at(2, 0, ["CURSOR R:",VGA_CURSOR.row," C:",VGA_CURSOR.col].join(""), Colors::BLACK_ON_CYAN)
+ # Also copy first 32 bytes of VGA buffer to row 3 for inspection
+ i = 0
+ while i < 32
+ ch = (VGA_BUFFER + (i * 2)).value
+ (VGA_BUFFER + ((3 * VGA_WIDTH + i) * 2)).value = ch
+ (VGA_BUFFER + ((3 * VGA_WIDTH + i) * 2) + 1).value = Colors::BLACK_ON_LIME
+ i += 1
+ end
+ end
+
+end
@@ -22,4 +22,6 @@ require "./prelude/crystal_core/range"
require "./prelude/crystal_core/string"
require "./prelude/crystal_core/reference"
require "./prelude/crystal_core/static_array"
-require "./prelude/puts"
+
+# crystal api
+require "./prelude/puts"
@@ -1,9 +1,11 @@
fun print_string(str : UInt8*)
end
+VGA_ADDRESS = 0xb8000_u64
+
def puts(str : String)
_str : Pointer(UInt8) = str.to_unsafe
- video_memory = Pointer(UInt8).new(0xB8000_u64)
+ video_memory = Pointer(UInt8).new(VGA_ADDRESS)
i = 0
ch = _str.value
while ch > 0
@@ -0,0 +1,75 @@
+#!/bin/bash
+
+# Flags
+
+CC_FLAGS="-ffreestanding -nostdinc -nostdinc++ \
+ -Wall -Wextra \
+ -o ./bin/kernel.bin -target i386-pc-none-elf \
+ -I ./kernel/"
+LD_FLAGS="-nostdlib -Wl,--oformat=binary,-T./kernel/linker.ld"
+CPP_FILES="./kernel/unityBuild.cpp ./build/kernelEntryPoint.o"
+
+# Build
+
+mkdir {bin,build} 2> /dev/null
+
+echo -e "Cleaning..."
+rm *.bin *.img *.iso *.o *.vmdk 2> /dev/null
+
+echo -e "\nBuilding..."
+nasm ./boot/bootloader.asm -I ./boot/ -o ./bin/bootloader.bin || exit 1
+nasm -felf32 ./kernel/entry.asm -I ./kernel/ -o ./build/kernelEntryPoint.o || exit 1
+
+use_crystal_kernel=${USE_CRYSTAL_KERNEL:-true}
+
+if [ "$use_crystal_kernel" = true ]
+then
+ # Build crystal kernel without stdlib, using the custom prelude, and targeting i386-unknown-linux-elf
+ crystal build \
+ --no-debug \
+ --cross-compile \
+ --target i386-unknown-linux-elf \
+ -Dkernel \
+ --prelude=../prelude.cr \
+ -o ./build/kernel ./kernel/kernel.cr || exit 1
+
+ # Link the kernel into an ELF executable, then convert it to a flat binary for the bootloader.
+ ld \
+ -m elf_i386 \
+ -nostdlib \
+ -T ./kernel/linker.ld \
+ -o ./bin/kernel.elf ./build/kernelEntryPoint.o ./build/kernel.o || exit 1
+
+ # Copy kernel.elf to kernel.bin properly.
+ objcopy \
+ -O binary ./bin/kernel.elf ./bin/kernel.bin || exit 1
+else
+ # Build c++ kernel without stdlib, using the custom prelude, and targeting i386-unknown-linux-elf
+ clang++ -v \
+ $CC_FLAGS \
+ $LD_FLAGS \
+ $CPP_FILES || exit 1
+fi
+
+cp ./bin/kernel.bin ./iso/boot/kernel.bin || exit 1
+
+./tools/genVDisk \
+ --output "crystalos" \
+ --floppy \
+ --bootloader ./bin/bootloader.bin \
+ --kernel ./bin/kernel.bin || exit 1
+
+# Run
+
+echo -e "\nRunning..."
+
+use_vnc=${USE_VNC:-false}
+
+if [ "$use_VNC" = true ]
+then
+ qemu-system-i386 -drive if=floppy,index=0,format=raw,file=crystalos.img || exit 1
+else
+ qemu-system-i386 -drive if=floppy,index=0,format=raw,file=crystalos.img -display curses || exit 1
+fi
+
+exit 0
@@ -0,0 +1,7 @@
+genVDisk is from:
+- https://fancykillerpanda.github.io/OS-Tutorial/02_bootloader/genVDisk-utility/
+- https://github.com/FancyKillerPanda/PandaOS/tree/master/src/genVDisk
+
+can also run on Android/Termux but needs to be re-built from source (easy)
+
+its here because not packaged and should be taken care of somehow.