feat: initial working bootloader/kernelthis includes a kernel.cr not working, currently kernel is in c++, needs x86_64 to have crystal kernel
@@ -1,4 +0,0 @@
-bin/
-build/
-iso/boot/kernel.bin
-crystalos.img
@@ -0,0 +1,40 @@
+ASMCC = nasm
+CC = gcc
+LD = ld
+CFLAGS = -m64 -ffreestanding -fno-stack-protector -nostdlib -nostartfiles -O2
+LDFLAGS = -T kernel/linker.ld -nostdlib
+
+SRCS = kernel/main.c kernel/vga.c kernel/pmm.c kernel/idt.c kernel/ps2.c kernel/timer.c
+OBJS = $(SRCS:.c=.o) kernel/idt_asm.o kernel/multiboot.o kernel/entry.o modules/example_mod.o
+
+all: kernel.bin
+
+kernel/entry.o: kernel/entry.asm
+ $(ASMCC) -f elf64 kernel/entry.asm -o kernel/entry.o
+
+kernel/multiboot.o: kernel/multiboot.asm
+ $(ASMCC) -f elf64 kernel/multiboot.asm -o kernel/multiboot.o
+
+kernel/idt_asm.o: kernel/idt.S
+ $(CC) -c kernel/idt.S -o kernel/idt_asm.o
+
+kernel/%.o: kernel/%.c
+ $(CC) $(CFLAGS) -c $< -o $@
+
+modules/example_mod.o: modules/example_mod.c
+ $(CC) $(CFLAGS) -c modules/example_mod.c -o modules/example_mod.o
+
+kernel.bin: $(OBJS)
+ $(LD) $(LDFLAGS) -o kernel.bin $(OBJS)
+
+iso:
+ mkdir -p iso/boot/grub
+ cp kernel.bin iso/boot/kernel.bin
+ printf 'set timeout=0\nset default=0\nmenuentry "CrystalOS" { multiboot2 /boot/kernel.bin }\n' > iso/boot/grub/grub.cfg
+ grub-mkrescue -o crystalos.iso iso
+
+start:
+ qemu-system-x86_64 -cdrom crystalos.iso -m 512 -serial stdio -display sdl
+
+clean:
+ rm -f $(OBJS) kernel.bin crystalos.iso
@@ -1,5 +1,4 @@
; void reboot()
-global reboot
reboot:
.notify:
mov si, rebootMessage
@@ -13,7 +12,6 @@ reboot:
jmp word 0xffff:0x0000
; void clear_screen()
-global clear_screen
clear_screen:
.clear:
mov ax, 0x0700 ; Entire screen
@@ -32,7 +30,6 @@ clear_screen:
ret
; void print_string(ds:si string)
-global print_string
print_string:
.print_char:
; Gets a character and compares it with NULL
@@ -53,7 +50,6 @@ print_string:
ret
; void read_disk(cx sector, al number-to-read, es:bx into)
-global read_disk
read_disk:
.read:
push ax
@@ -75,7 +71,6 @@ 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
@@ -0,0 +1,57 @@
+#!/bin/sh
+set -e
+
+# build.sh - builds the TinyCrystalOS prototype (kernel + ISO)
+# Place this at the project root (same dir as Makefile). Run: chmod +x build.sh && ./build.sh
+
+# Ensure required tools
+echo "Checking toolchain..."
+for cmd in gcc ld objcopy grub-mkrescue qemu-system-x86_64; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ echo "Warning: $cmd not found."
+ fi
+done
+
+# Build kernel and modules via Makefile
+echo "Building project using Makefile..."
+if [ ! -f Makefile ]; then
+ echo "Makefile not found in current directory."
+ exit 1
+fi
+
+make clean || true
+if ! make; then
+ echo "Build failed."
+ exit 1
+fi
+
+# Optionally create ISO if grub-mkrescue exists
+if command -v grub-mkrescue >/dev/null 2>&1; then
+ echo "Creating ISO with grub-mkrescue..."
+ # Ensure iso/boot/grub/grub.cfg exists (Makefile should have created it)
+ if [ ! -f iso/boot/grub/grub.cfg ]; then
+ echo "GRUB config not found, creating default grub.cfg..."
+ mkdir -p iso/boot/grub
+ cat > iso/boot/grub/grub.cfg <<'EOF'
+set timeout=0
+set default=0
+menuentry "CrystalOS" {
+ multiboot2 /boot/kernel.bin
+}
+EOF
+ fi
+ grub-mkrescue -o tinyos.iso iso || { echo "grub-mkrescue failed"; exit 1; }
+ echo "ISO created: tinyos.iso"
+else
+ echo "grub-mkrescue not available; skip ISO creation. kernel.bin produced."
+fi
+
+# Optionally create a compressed archive of the build
+ARCHIVE="tinycrystalos-build.tar.gz"
+echo "Packaging build artifacts into $ARCHIVE..."
+tar -czf "$ARCHIVE" kernel.bin kernel/*.o modules/*.o modules/*.c kernel/*.c kernel/*.S kernel/linker.ld || true
+echo "Packaged build artifacts."
+
+echo "Done."
+echo "To run in QEMU (if tinyos.iso was created):"
+echo " qemu-system-x86_64 -cdrom tinyos.iso -m 512"
@@ -0,0 +1,41 @@
+#!/bin/bash
+
+# Flags
+
+kernelCompileFlags="-ffreestanding -nostdinc -nostdinc++ \
+ -Wall -Wextra \
+ -o ./bin/kernel.bin -target i386-pc-none-elf \
+ -I ./kernel/"
+kernelLinkFlags="-nostdlib -Wl,--oformat=binary,-T./kernel/linker.ld"
+kernelFiles="./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=true
+
+if [ "$use_crystal_kernel" = true ]
+then
+ CC=clang LD=ld crystal build --no-debug -Dkernel --cross-compile --target i386-unknown-linux-elf --prelude=empty --link-flags="-nostdlib -T./kernel/linker.ld" -o ./build/kernel ./kernel/kernel.cr
+ # The object file should be at ./build/kernel.o, now link it with the entry point
+ ld -nostdlib -T ./kernel/linker.ld -o ./bin/kernel.bin ./build/kernelEntryPoint.o ./build/kernel.o || exit 1
+else
+ clang++ $kernelCompileFlags $kernelLinkFlags $kernelFiles || exit 1
+fi
+
+# touch ./bin/kernel.bin
+./tools/genVDisk --output "crystalos.img" --floppy \
+ --bootloader ./bin/bootloader.bin --kernel ./bin/kernel.bin
+
+echo -e "\nRunning..."
+qemu-system-i386 -drive if=floppy,index=0,format=raw,file=crystalos.img || exit 1
+
+exit 0
@@ -0,0 +1,4 @@
+
+def say_hello
+ puts "Hello from Crystal!"
+end
@@ -0,0 +1,21 @@
+bits 32
+
+extern kmain
+
+section .entry
+
+; void start()
+global _start
+_start:
+ mov esp, kernelStackStart
+ call kmain
+
+ .hang:
+ cli
+ hlt
+ jmp .hang
+
+align 16
+kernelStackEnd:
+ times 16384 db 0
+kernelStackStart:
@@ -1,13 +1,10 @@
-/* todo: port to intel .asm format */
-
.section .text
.global isr_handler_common
.global irq_handler_common
/* Minimal ISR handlers for linking */
isr_handler_common:
- ret
+ ret
irq_handler_common:
- jmp irq_common_entry
- ret
+ ret
@@ -0,0 +1,43 @@
+# require "../minrt/src/prelude"
+
+lib LibBootstrap
+ @[Packed]
+ struct StartInfo
+ multiboot_ptr : UInt32
+ end_of_kernel : UInt32
+ end
+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
+
+fun kmain
+ # IDT.enable_interrupts
+ # kprint "Hello from Fluorite."
+
+ address = 0xb800 # Pointer(UInt8).reinterpret(0xb8000)
+
+ string = "[CrystalOS Kernel] Hello from Crystal"
+ string_size = 33 # string.size
+
+ # Loop over each character in the string
+ # [0...string_size];each do |i|
+ # # Write the character
+ # address.value = string[i].to_u8
+ # address += 1
+ # # Write the attribute byte
+ # address.value = 0xA0.to_u8
+ # address += 1
+ # end
+end
+
@@ -20,19 +20,12 @@ SECTIONS
{
*(.data .data.*)
}
-
- .magic ALIGN(4k) : AT(ADDR(.magic))
- {
- *(.magic .magic.*)
- }
/* Not actually *in* the image. */
.bss ALIGN(4k) : AT(ADDR(.bss))
{
- PROVIDE(kernelBssBlockStart = .);
*(COMMON)
*(.bss .bss.*)
- PROVIDE(kernelBssBlockEnd = .);
}
.modules ALIGN(4k) : AT(ADDR(.modules))
@@ -101,6 +101,6 @@ void kmain(void) {
/* Idle loop */
while (1) {
- asm volatile ("hlt");
+ asm volatile ("hlt");
}
}
@@ -1,82 +0,0 @@
-#!/bin/bash
-
-# Setup/cleanup
-mkdir {bin,build} 2> /dev/null
-
-echo -e "Cleaning artifacts..."
-rm *.bin *.img *.iso *.o *.vmdk 2> /dev/null
-
-# Build
-echo -e "Building bootloader (asm)..."
-nasm ./src/boot/bootloader.asm -I ./src/boot/ -o ./bin/bootloader.bin || exit 1
-
-echo -e "Building kernel (asm)..."
-nasm -felf32 ./src/kernel/asm/entry.asm -I ./src/kernel/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
- echo -e "Building kernel (crystal)..."
- crystal build \
- --no-debug \
- --cross-compile \
- --target i386-unknown-linux-elf \
- -Dkernel \
- --prelude=../prelude.cr \
- -o ./build/kernel ./src/kernel/kernel.cr || exit 1
-
- # Link the kernel into an ELF executable, then convert it to a flat binary for the bootloader.
- echo -e "Linking kernel.elf (ld)..."
- ld \
- -m elf_i386 \
- -nostdlib \
- -T ./src/linker.ld \
- -o ./bin/kernel.elf ./build/kernelEntryPoint.o ./build/kernel.o || exit 1
-
- # Copy kernel.elf to kernel.bin properly.
- echo -e "Building kernel.bin (objcopy)..."
- objcopy \
- -O binary ./bin/kernel.elf ./bin/kernel.bin || exit 1
-else
- # Flags
- CC_FLAGS="-ffreestanding -nostdinc -nostdinc++ -Wall -Wextra -o ./bin/kernel.bin -target i386-pc-none-elf -I ./src/kernel/archive/cpp/"
- LD_FLAGS="-nostdlib -Wl,--oformat=binary,-T./src/linker.ld"
- CPP_FILES="./src/kernel/archive/cpp/unityBuild.cpp ./build/kernelEntryPoint.o"
-
- # Build c++ kernel without stdlib, using the custom prelude, and targeting i386-unknown-linux-elf
- echo -e "Building kernel (cpp)..."
- clang++ -v \
- $CC_FLAGS \
- $LD_FLAGS \
- $CPP_FILES || exit 1
-fi
-
-echo -e "Copy kernel.bin into iso/boot/"
-cp ./bin/kernel.bin ./iso/boot/kernel.bin || exit 1
-
-echo -e "Make bootable crystalos.img (genVDisk)..."
-./tools/genVDisk \
- --output "crystalos" \
- --floppy \
- --bootloader ./bin/bootloader.bin \
- --kernel ./bin/kernel.bin || exit 1
-
-if [ -f "crystalos" ]
-then
- # some platforms produce a no ext file, so we rename it to .img, eventually.
- mv crystalos crystalos.img || exit 1
-fi
-
-# Run
-echo -e "Booting into crystalos.img (qemu)..."
-
-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
@@ -1,50 +0,0 @@
-
-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
-
-# 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
-
@@ -1,140 +0,0 @@
-bits 32
-
-extern kearly
-extern kmain
-
-extern irq_dispatcher_c
-extern pic_send_eoi
-
-; void outb(uint16, uint8)
-global outb
-outb:
- out dx, al
- ret
-
-; void set_cursor_pos(int32, int32)
-global set_cursor_pos
-set_cursor_pos:
- push ebp
- mov ebp, esp
-
- mov eax, dword [ebp + 8] ; eax = row
- mov ebx, 80
- imul eax, ebx ; eax = row * 80
- add eax, dword [ebp + 12] ; eax += col
- mov bx, ax ; bx = position (16-bit)
-
- ; outb(0x3D4, 0x0E);
- mov dx, 0x3D4
- mov al, 0x0E
- out dx, al
-
- ; outb(0x3D5, (position >> 8) & 0xFF);
- mov dx, 0x3D5
- mov al, bh
- out dx, al
-
- ; outb(0x3D4, 0x0F);
- mov dx, 0x3D4
- mov al, 0x0F
- out dx, al
-
- ; outb(0x3D5, position & 0xFF);
- mov dx, 0x3D5
- mov al, bl
- out dx, al
-
- mov esp, ebp
- pop ebp
- 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)
- mov dx, 0x3D4
- mov al, 0x0A
- out dx, al
-
- mov dx, 0x3D5
- mov al, 0x20 ; set bit 5 to disable cursor
- out dx, al
-
- ret
-
-; void show_cursor(void)
-global show_cursor
-show_cursor:
- ; Restore a typical cursor shape: start = 0 (scanline start)
- mov dx, 0x3D4
- mov al, 0x0A
- out dx, al
-
- mov dx, 0x3D5
- mov al, 0x00 ; cursor start = 0
- out dx, al
-
- ; set cursor end (e.g., 15)
- mov dx, 0x3D4
- mov al, 0x0B
- out dx, al
-
- mov dx, 0x3D5
- mov al, 0x0F ; cursor end = 15 (typical)
- out dx, al
-
- 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
-
- push eax ; video mode
- push ebx ; memory map
- push ecx ; number of lines printed
-
- call kearly
- call kmain
-
- .hang:
- cli
- hlt
- jmp .hang
-
-align 16
-kernelStackEnd:
- times 16384 db 0
-kernelStackStart:
-
-section .magic
-
-global crystalOSMagic
-crystalOSMagic: db "123113371338"
-
-global crystalOSVersion
-crystalOSVersion: db "0.1.0"
@@ -1,124 +0,0 @@
-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
@@ -1,18 +0,0 @@
-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
@@ -1,53 +0,0 @@
-require "./vga.cr"
-
- @[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
-
-lib KernelShim
- # External functions from assembly
- # fun lidt(ptr : Ptr): Nil
- # fun isr_handler_common(): Nil
-end
-
-# IDT module
-module IDT
-
- IDT_ENTRIES = 256
-
- # # Storage for the IDT entries
- # @@idt : StaticArray(Entry, IDT_ENTRIES) = StaticArray(Entry, IDT_ENTRIES).new()
- # property idt
-
- # # 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 : 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)
-
- # VGA.puts("IDT initialized\n")
- end
-end
@@ -1,270 +0,0 @@
-lib KernelShim
- fun outb(port : UInt16, value : UInt8)
- fun wait()
-end
-
-module IDT
- # Constants
- IDT_ADDRESS = 0xc0007100_u32
- KERNEL_CODE_SEGMENT_SELECTOR = 0x18_u16
- INTERRUPT_GATE = 0x8e_u8
-
- # Structs
- struct IDTEntry
- offset_low : UInt16
- selector : UInt16
- unused : UInt8
- type : UInt8
- offset_high : UInt16
- end
-
- struct IDTPointer
- size : UInt16
- address : UInt32
- end
-
- module Handlers
- def self.handle_division_by_zero_exception
- # handler code
- end
-
- def self.handle_debug_exception
- # handler code
- end
-
- def self.handle_non_maskable_exception
- # handler code
- end
-
- def self.handle_breakpoint_exception
- # handler code
- end
-
- def self.handle_overflow_exception
- # handler code
- end
-
- def self.handle_bound_range_exceeded_exception
- # handler code
- end
-
- def self.handle_invalid_opcode_exception
- # handler code
- end
-
- def self.handle_device_not_available_exception
- # handler code
- end
-
- def self.handle_double_fault_exception
- # handler code
- end
-
- def self.handle_invalid_tss_exception
- # handler code
- end
-
- def self.handle_segment_not_present_exception
- # handler code
- end
-
- def self.handle_stack_segment_fault_exception
- # handler code
- end
-
- def self.handle_general_protection_fault_exception
- # handler code
- end
-
- def self.handle_page_fault_exception
- # handler code
- end
-
- def self.handle_x87_floating_point_exception
- # handler code
- end
-
- def self.handle_alignment_check_exception
- # handler code
- end
-
- def self.handle_machine_check_exception
- # handler code
- end
-
- def self.handle_simd_floating_point_exception
- # handler code
- end
-
- def self.handle_virtualisation_exception
- # handler code
- end
-
- def self.handle_security_exception
- # handler code
- end
-
- def self.handle_interrupt_request_0
- # handler code
- end
-
- def self.handle_interrupt_request_1
- # handler code
- end
-
- def self.handle_interrupt_request_2
- # handler code
- end
-
- def self.handle_interrupt_request_3
- # handler code
- end
-
- def self.handle_interrupt_request_4
- # handler code
- end
-
- def self.handle_interrupt_request_5
- # handler code
- end
-
- def self.handle_interrupt_request_6
- # handler code
- end
-
- def self.handle_interrupt_request_7
- # handler code
- end
-
- def self.handle_interrupt_request_8
- # handler code
- end
-
- def self.handle_interrupt_request_9
- # handler code
- end
-
- def self.handle_interrupt_request_10
- # handler code
- end
-
- def self.handle_interrupt_request_11
- # handler code
- end
-
- def self.handle_interrupt_request_12
- # handler code
- end
-
- def self.handle_interrupt_request_13
- # handler code
- end
-
- def self.handle_interrupt_request_14
- # handler code
- end
-
- def self.handle_interrupt_request_15
- # handler code
- end
- end
-
- # Functions
- def self.remap_pic
- MASTER_PIC_COMMAND = 0x20_u8
- MASTER_PIC_DATA = 0x21_u8
- SLAVE_PIC_COMMAND = 0xa0_u8
- SLAVE_PIC_DATA = 0xa1_u8
-
- INIT_SEQUENCE_START = 0x11_u8
- MASTER_PIC_OFFSET = 0x20_u8
- SLAVE_PIC_OFFSET = 0x28_u8
- MODE_8086 = 0x01_u8
-
- KernelShim.outb(MASTER_PIC_COMMAND, INIT_SEQUENCE_START)
- KernelShim.wait()
- KernelShim.outb(SLAVE_PIC_COMMAND, INIT_SEQUENCE_START)
- KernelShim.wait()
- KernelShim.outb(MASTER_PIC_DATA, MASTER_PIC_OFFSET)
- KernelShim.wait()
- KernelShim.outb(SLAVE_PIC_DATA, SLAVE_PIC_OFFSET)
- KernelShim.wait()
- KernelShim.outb(MASTER_PIC_DATA, 0x04_u8) # Tells master there's a slave on IRQ2
- KernelShim.wait()
- KernelShim.outb(SLAVE_PIC_DATA, 0x02_u8) # Tells slave its cascade identity
- KernelShim.wait()
- KernelShim.outb(MASTER_PIC_DATA, MODE_8086)
- KernelShim.wait()
- KernelShim.outb(SLAVE_PIC_DATA, MODE_8086)
- KernelShim.wait()
- KernelShim.outb(MASTER_PIC_DATA, 0x00_u8) # Masks all interrupts (enable)
- KernelShim.wait()
- KernelShim.outb(SLAVE_PIC_DATA, 0x00_u8) # Masks all interrupts (enable)
- KernelShim.wait()
- end
-
- def self.set_up_idt_entries(idt_entries : IDTEntry*)
- handlers = [
- Handlers.handle_division_by_zero_exception,
- Handlers.handle_debug_exception,
- Handlers.handle_non_maskable_exception,
- Handlers.handle_breakpoint_exception,
- Handlers.handle_overflow_exception,
- Handlers.handle_bound_range_exceeded_exception,
- Handlers.handle_invalid_opcode_exception,
- Handlers.handle_device_not_available_exception,
- Handlers.handle_double_fault_exception,
- Handlers.handle_invalid_tss_exception,
- Handlers.handle_segment_not_present_exception,
- Handlers.handle_stack_segment_fault_exception,
- Handlers.handle_general_protection_fault_exception,
- Handlers.handle_page_fault_exception,
- Handlers.handle_x87_floating_point_exception,
- Handlers.handle_alignment_check_exception,
- Handlers.handle_machine_check_exception,
- Handlers.handle_simd_floating_point_exception,
- Handlers.handle_virtualisation_exception,
- Handlers.handle_security_exception,
- Handlers.handle_interrupt_request_0,
- Handlers.handle_interrupt_request_1,
- Handlers.handle_interrupt_request_2,
- Handlers.handle_interrupt_request_3,
- Handlers.handle_interrupt_request_4,
- Handlers.handle_interrupt_request_5,
- Handlers.handle_interrupt_request_6,
- Handlers.handle_interrupt_request_7,
- Handlers.handle_interrupt_request_8,
- Handlers.handle_interrupt_request_9,
- Handlers.handle_interrupt_request_10,
- Handlers.handle_interrupt_request_11,
- Handlers.handle_interrupt_request_12,
- Handlers.handle_interrupt_request_13,
- Handlers.handle_interrupt_request_14,
- Handlers.handle_interrupt_request_15,
- ]
-
- handlers.each_with_index do |handler, index|
- idt_entries[index].offset_low = (handler.address & 0x0000ffff).to_u16
- idt_entries[index].selector = KERNEL_CODE_SEGMENT_SELECTOR
- idt_entries[index].unused = 0_u8
- idt_entries[index].type = INTERRUPT_GATE
- idt_entries[index].offset_high = ((handler.address & 0xffff0000) >> 16).to_u16
- end
- end
-
- def self.load_idt(idt_entries : IDTEntry*)
- idt_pointer = IDTPointer.new
- idt_pointer.size = sizeof(IDTEntry) * 256
- idt_pointer.address = IDT_ADDRESS
-
- asm "lidt %{idt_pointer}" : : "m"(idt_pointer)
- asm "sti" ::
- end
-
- def self.init
- remap_pic
- idt_entries = Pointer(IDTEntry).new(IDT_ADDRESS)
- set_up_idt_entries(idt_entries)
- load_idt(idt_entries)
- end
-end
@@ -1,41 +0,0 @@
-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
- puts "IRQ initialized!\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
- puts "irq handler registered\n"
- prev
- end
-
- def self.dispatcher(irq : UInt32)
- puts "irq dispatcher called ...\n"
- i = irq.to_u8
- if handler = HANDLERS[i]
- handler.call(i)
- end
- 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,104 +0,0 @@
-require "./vga.cr"
-require "./tiny_alloc_i386.cr"
-require "./irq.cr"
-require "./timer.cr"
-# require "./pmm.cr"
-
-CRYSTAL_OS_MAGIC = "123113371338".to_unsafe
-
-lib KernelShim
- @[Packed]
- struct StartInfo
- multiboot_ptr : UInt32
- end_of_kernel : UInt32
- end
-
- fun crystalOSMagic : UInt8
- fun crystalOSVersion : UInt8
-
- fun kernelBssBlockStart : UInt64
- fun kernelBssBlockEnd : UInt64
-end
-
-fun kearly(info_ptr: KernelShim::StartInfo*)
- VGA.init
-
- # Get the startup info
- info = info_ptr.value
-
- # VGA.debug_print info
-end
-
-fun kmain
- # todo: troubelshoot Pointer compare
- # magic = KernelShim.crystalOSMagic.value
- # if magic != CRYSTAL_OS_MAGIC.value
- # while (1)
- # end
- # return
- # end
-
- # Zeroes the BSS block
- # kernel_bss_block_size = (KernelShim.kernelBssBlockEnd.to_u64 - KernelShim.kernelBssBlockStart.to_u64).to_u32
- # ptr = Pointer(UInt8).new(KernelShim.kernelBssBlockStart)
- # i = 0
- # while i < kernel_bss_block_size
- # ptr[i] = 0_u8
- # i += 1
- # end
-
- # Init VGA (+ module demo)
- VGA.puts(
- "[CrystalOS Kernel] Hello from Crystal.\n",
- color: VGA::Colors::BLACK_ON_LIME,
- )
- # puts KernelShim.crystalOSVersion
- # PMM.init # Physical Memory Init
- TinyAllocI386.init
- IRQ.init
- Timer.init
- # Net.init
- # PS2.init
- # Audio.init
- # Display.init
- # WM.init
-
- puts("weeeeeeee\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"
- 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)
-
- puts "\nPress [enter] to boot into graphical mode\n\n"
-
- IRQ.register(0, ->(data : UInt8) {
- puts "timer handler called ...\n"
- Timer.irq_handler(data)
- return
- })
-
- IRQ.register(1, ->(scan_code : UInt8) {
- puts "keyboard handler called ...\n"
- if scan_code == 0x1C
- puts "Enter\n"
- end
- return
- })
-
- # VGA.set_cursor(0, 5)
- # VGA.hide_cursor
-
- # asm (%(sti))
-
- while true
- asm (%(hlt))
- end
-end
@@ -1,8 +0,0 @@
-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
@@ -1,56 +0,0 @@
-module PMM
- PAGE_SIZE = 4096
- KERNEL_HEAP_START = 0x200000_u64
- KERNEL_HEAP_SIZE = 16 * 1024 * 1024
-
- TOTAL_PAGES = 4096 # KERNEL_HEAP_SIZE / PAGE_SIZE
-
- @@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)
-
- 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
@@ -1,32 +0,0 @@
-require "./vga.cr"
-
-lib KernelShim
- # External assembly ISR for timer
- fun timer_isr_common : Void
- fun outb(port : UInt16, value: UInt8) : Void
-end
-
-# Timer module - System timer
-module Timer
- TIMER_FREQ = 1000_u32
-
- @@tick_count : UInt64 = 0
- class_property tick_count
-
- def self.init : Nil
- # PIT channel 0, mode 3 (square wave), lobyte/hibyte access
- KernelShim.outb(0x43, 0x36)
- # Frequency: 1193182 Hz / divisor = desired frequency (e.g., 100 Hz)
- divisor = 11931 # 1193182 / 100 ≈ 11931
- KernelShim.outb(0x40, divisor & 0xFF)
- KernelShim.outb(0x40, (divisor >> 8) & 0xFF)
-
- # Set up PIT or HPET here (placeholder)
- VGA.puts("Timer initialized!\n")
- end
-
- def self.irq_handler(data : UInt8)
- @@tick_count += 1
- # Could print a dot or update something; for now just increment.
- end
-end
@@ -1,101 +0,0 @@
-require "./vga.cr"
-
-# 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
-
-# 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
- 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
@@ -1,207 +0,0 @@
-# Definitions from kernel/entry.asm
-lib KernelShim
- fun hide_cursor()
- fun show_cursor()
- fun set_cursor_pos(
- row : UInt32,
- col : UInt32
- )
-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
-
- # 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
- 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)
- 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
- col = 0
- row = 0
- end
-
- # Hide cursor
- def self.hide_cursor
- KernelShim.hide_cursor
- end
-
- # Show cursor
- def self.show_cursor
- KernelShim.show_cursor
- end
-
- # Set typing cursor position
- def self.set_cursor(
- 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
- property puts_count
-
- @@col = 0
- class_property col
-
- @@row = 0
- class_property row
-
- # 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
- @@col ||= i || 0
- @@row ||= line || 0
-
- offset = (@@row * VGA_WIDTH + @@col) * 2
-
- (VGA_BUFFER + offset).value = ch
- (VGA_BUFFER + offset + 1).value = color
-
- @@col += 1
-
- # Wrap around if past screen
- if @@col >= VGA_WIDTH
- @@col = 0
- @@row += 1
- if @@row >= VGA_HEIGHT
- @@row = VGA_HEIGHT - 1
- end
- end
- end
-
- # Write a string to VGA
- def self.puts(
- str : String,
- color : UInt8? = Colors::DEFAULT,
- line : Int32? = nil,
- ) : Nil
- @@col ||= 0
- @@row ||= (line || 0)
- i = 0
- while i < str.size
- ch = (str.to_unsafe + i).value
-
- # 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, @@col, @@row, color)
- i += 1
- end
-
- VGA.set_cursor(@@col, @@row)
- end
-
-end
@@ -1,29 +0,0 @@
-require "./prelude/crystal_core/mem"
-require "./prelude/atomic"
-require "./prelude/primitives"
-
-# order matters
-require "./prelude/crystal_core/object"
-require "./prelude/crystal_core/pointer"
-require "intrinsics"
-require "comparable"
-require "./prelude/crystal_core/panic"
-require "./prelude/crystal_core/slice"
-require "./prelude/crystal_core/int"
-
-# order not important
-require "annotations"
-require "nil"
-require "./prelude/crystal_core/char"
-require "./prelude/crystal_core/proc"
-require "./prelude/crystal_core/enum"
-require "./prelude/crystal_core/bool"
-require "./prelude/crystal_core/tuple"
-require "./prelude/crystal_core/range"
-require "./prelude/crystal_core/string"
-require "./prelude/crystal_core/reference"
-require "./prelude/crystal_core/static_array"
-require "./prelude/crystal_core/array"
-
-# crystal api
-require "./prelude/puts"
@@ -1,248 +0,0 @@
-# :nodoc:
-struct Atomic(T)
- # Creates an Atomic with the given initial value.
- def initialize(@value : T)
- {% if !T.union? && (T == Char || T < Int::Primitive || T < Enum) %}
- # Support integer types, enum types, or char (because it's represented as an integer)
- {% elsif T < Reference || (T.union? && T.union_types.all? { |t| t == Nil || t < Reference }) %}
- # Support reference types, or union types with only nil or reference types
- {% else %}
- {{ raise "Can only create Atomic with primitive integer types, reference types or nilable reference types, not #{T}" }}
- {% end %}
- end
-
- # Compares this atomic's value with *cmp*:
- #
- # * if they are equal, sets the value to *new*, and returns `{old_value, true}`
- # * if they are not equal the value remains the same, and returns `{old_value, false}`
- #
- # ```
- # atomic = Atomic.new(1)
- # atomic.compare_and_set(2, 3) # => {1, false}
- # atomic.get # => 1
- #
- # atomic.compare_and_set(1, 3) # => {1, true}
- # atomic.get # => 3
- # ```
- def compare_and_set(cmp : T, new : T) : {T, Bool}
- # Check if it's a nilable reference type
- {% if T.union? && T.union_types.all? { |t| t == Nil || t < Reference } %}
- # If so, use addresses because LLVM < 3.9 doesn't support cmpxchg with pointers
- address, success = Ops.cmpxchg(pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new(cmp.as(T).object_id), LibC::SizeT.new(new.as(T).object_id), :sequentially_consistent, :sequentially_consistent)
- {address == 0 ? nil : Pointer(T).new(address).as(T), success}
- # Check if it's a reference type
- {% elsif T < Reference %}
- # Use addresses again (but this can't return nil)
- address, success = Ops.cmpxchg(pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new(cmp.as(T).object_id), LibC::SizeT.new(new.as(T).object_id), :sequentially_consistent, :sequentially_consistent)
- {Pointer(T).new(address).as(T), success}
- {% else %}
- # Otherwise, this is an integer type
- Ops.cmpxchg(pointerof(@value), cmp, new, :sequentially_consistent, :sequentially_consistent)
- {% end %}
- end
-
- # Performs `atomic_value += value`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(1)
- # atomic.add(2) # => 1
- # atomic.get # => 3
- # ```
- def add(value : T)
- Ops.atomicrmw(:add, pointerof(@value), value, :sequentially_consistent, false)
- end
-
- # Performs `atomic_value -= value`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(9)
- # atomic.sub(2) # => 9
- # atomic.get # => 7
- # ```
- def sub(value : T)
- Ops.atomicrmw(:sub, pointerof(@value), value, :sequentially_consistent, false)
- end
-
- # Performs `atomic_value &= value`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.and(3) # => 5
- # atomic.get # => 1
- # ```
- def and(value : T)
- Ops.atomicrmw(:and, pointerof(@value), value, :sequentially_consistent, false)
- end
-
- # Performs `atomic_value = ~(atomic_value & value)`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.nand(3) # => 5
- # atomic.get # => -2
- # ```
- def nand(value : T)
- Ops.atomicrmw(:nand, pointerof(@value), value, :sequentially_consistent, false)
- end
-
- # Performs `atomic_value |= value`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.or(2) # => 5
- # atomic.get # => 7
- # ```
- def or(value : T)
- Ops.atomicrmw(:or, pointerof(@value), value, :sequentially_consistent, false)
- end
-
- # Performs `atomic_value ^= value`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.xor(3) # => 5
- # atomic.get # => 6
- # ```
- def xor(value : T)
- Ops.atomicrmw(:xor, pointerof(@value), value, :sequentially_consistent, false)
- end
-
- # Performs `atomic_value = max(atomic_value, value)`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.max(3) # => 5
- # atomic.get # => 5
- #
- # atomic.max(10) # => 5
- # atomic.get # => 10
- # ```
- def max(value : T)
- {% if T < Int::Signed %}
- Ops.atomicrmw(:max, pointerof(@value), value, :sequentially_consistent, false)
- {% else %}
- Ops.atomicrmw(:umax, pointerof(@value), value, :sequentially_consistent, false)
- {% end %}
- end
-
- # Performs `atomic_value = min(atomic_value, value)`. Returns the old value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.min(10) # => 5
- # atomic.get # => 5
- #
- # atomic.min(3) # => 5
- # atomic.get # => 3
- # ```
- def min(value : T)
- {% if T < Int::Signed %}
- Ops.atomicrmw(:min, pointerof(@value), value, :sequentially_consistent, false)
- {% else %}
- Ops.atomicrmw(:umin, pointerof(@value), value, :sequentially_consistent, false)
- {% end %}
- end
-
- # Atomically sets this atomic's value to *value*. Returns the **old** value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.swap(10) # => 5
- # atomic.get # => 10
- # ```
- def swap(value : T)
- {% if T.union? && T.union_types.all? { |t| t == Nil || t < Reference } || T < Reference %}
- address = Ops.atomicrmw(:xchg, pointerof(@value).as(LibC::SizeT*), LibC::SizeT.new(value.as(T).object_id), :sequentially_consistent, false)
- Pointer(T).new(address).as(T)
- {% else %}
- Ops.atomicrmw(:xchg, pointerof(@value), value, :sequentially_consistent, false)
- {% end %}
- end
-
- # Atomically sets this atomic's value to *value*. Returns the **new** value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.set(10) # => 10
- # atomic.get # => 10
- # ```
- def set(value : T)
- Ops.store(pointerof(@value), value.as(T), :sequentially_consistent, true)
- value
- end
-
- # **Non-atomically** sets this atomic's value to *value*. Returns the **new** value.
- #
- # ```
- # atomic = Atomic.new(5)
- # atomic.lazy_set(10) # => 10
- # atomic.get # => 10
- # ```
- def lazy_set(@value : T)
- end
-
- # Atomically returns this atomic's value.
- def get
- Ops.load(pointerof(@value), :sequentially_consistent, true)
- end
-
- # **Non-atomically** returns this atomic's value.
- def lazy_get
- @value
- end
-
- # :nodoc:
- module Ops
- # Defines methods that directly map to LLVM instructions related to atomic operations.
-
- @[Primitive(:cmpxchg)]
- def self.cmpxchg(ptr : T*, cmp : T, new : T, success_ordering : Symbol, failure_ordering : Symbol) : {T, Bool} forall T
- end
-
- @[Primitive(:atomicrmw)]
- def self.atomicrmw(op : Symbol, ptr : T*, val : T, ordering : Symbol, singlethread : Bool) : T forall T
- end
-
- @[Primitive(:fence)]
- def self.fence(ordering : Symbol, singlethread : Bool) : Nil
- end
-
- @[Primitive(:load_atomic)]
- def self.load(ptr : T*, ordering : Symbol, volatile : Bool) : T forall T
- end
-
- @[Primitive(:store_atomic)]
- def self.store(ptr : T*, value : T, ordering : Symbol, volatile : Bool) : Nil forall T
- end
- end
-end
-
-# An atomic flag, that can be set or not.
-#
-# Concurrency safe. If many fibers try to set the atomic in parallel, only one
-# will succeed.
-#
-# Example:
-# ```
-# flag = Atomic::Flag.new
-# flag.test_and_set # => true
-# flag.test_and_set # => false
-# flag.clear
-# flag.test_and_set # => true
-# ```
-struct Atomic::Flag
- def initialize
- @value = 0_u8
- end
-
- # Atomically tries to set the flag. Only succeeds and returns `true` if the
- # flag wasn't previously set; returns `false` otherwise.
- def test_and_set : Bool
- Atomic::Ops.atomicrmw(:xchg, pointerof(@value), 1_u8, :sequentially_consistent, false) == 0_u8
- end
-
- # Atomically clears the flag.
- def clear : Nil
- Atomic::Ops.store(pointerof(@value), 0_u8, :sequentially_consistent, true)
- end
-end
@@ -1,150 +0,0 @@
-require "./markable.cr"
-
-# A simple dynamic array, see [Crystal's documentation](https://crystal-lang.org/api/0.32.1/Array.html) for more detail.
-class Array(T)
- @size = 0
- @capacity = 0
- # getter size, capacity
- # protected setter size
-
- def size
- @size
- end
-
- def size=(new_size)
- abort "size must be smaller than capacity" if new_size > @capacity
- @size = new_size
- end
-
- def capacity
- @capacity
- end
-
- @buffer : T* = Pointer(T).null
-
- def to_unsafe
- @buffer
- end
-
- private def recalculate_capacity
- @capacity = Allocator.block_size_for_ptr(@buffer) // sizeof(T)
- end
-
- private def expand(new_capacity)
- if @size > new_capacity
- abort "size must be smaller than capacity"
- end
- if @buffer.null?
- @buffer = Pointer(T).malloc_atomic(new_capacity)
- else
- @buffer = @buffer.realloc(new_capacity.to_u64)
- end
- recalculate_capacity
- end
-
- def initialize(initial_capacity : Int = 0)
- if initial_capacity > 0
- @buffer = Pointer(T).new(initial_capacity.to_u64)
- recalculate_capacity
- end
- end
-
- def self.build(capacity : Int) : self
- ary = Array(T).new(capacity)
- ary.write_barrier do
- ary.size = (yield ary.to_unsafe).to_i
- end
- ary
- end
-
- def self.new(size : Int, &block : Int32 -> T)
- Array(T).build(size) do |buffer|
- size.to_i.times do |i|
- buffer[i] = yield i
- end
- size
- end
- end
-
- def each(&block)
- @size.times do |i|
- yield @buffer[i]
- end
- end
-
- def each_with_index(&block)
- @size.times do |i|
- yield @buffer[i], i
- end
- end
-
- def reverse_each
- i = size - 1
- while i >= 0
- yield @buffer[i]
- i -= 1
- end
- end
-
- def clone
- Array(T).build(size) do |buffer|
- size.times do |i|
- buffer[i] = to_unsafe[i]
- end
- size
- end
- end
-
- def [](idx : Int)
- abort "accessing out of bounds!" unless 0 <= idx < @size
- @buffer[idx]
- end
-
- def []?(idx : Int) : T?
- return nil unless 0 <= idx < @size
- @buffer[idx]
- end
-
- def []=(idx : Int, value : T)
- abort "accessing out of bounds!" unless 0 <= idx < @size
- @buffer[idx] = value
- end
-
- def push(value : T)
- write_barrier do
- if @size < @capacity
- @buffer[@size] = value
- else
- expand(@size + 1)
- @buffer[@size] = value
- end
- @size += 1
- end
- end
-
- def clear
- write_barrier do
- @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* ->)
- return if @buffer.null?
- yield @buffer.as(Void*)
- {% unless T < Int || T < Struct %}
- each do |obj|
- yield obj.as(Void*)
- end
- {% end %}
- end
-end
@@ -1,14 +0,0 @@
-# :nodoc:
-struct Bool
- def to_unsafe
- self ? 1 : 0
- end
-
- def to_s(io)
- if self
- io.print "true"
- else
- io.print "false"
- end
- end
-end
@@ -1,29 +0,0 @@
-struct Char
- def self.new(codepoint : Int)
- codepoint.unsafe_chr
- end
-
- def ord
- # Intrinsics.convert self, Int32
- func = self
- pointerof(func).unsafe_as(Int32)
- end
-
- def ==(other : Char)
- ord == other.ord
- end
-
- def to_s(io)
- io.putc ord.to_u8
- end
-
- def to_s
- String.build(1) do |str|
- str << self
- end
- end
-
- def to_u8
- ord.to_u8
- end
-end
@@ -1,104 +0,0 @@
-# :nodoc:
-struct Enum
- def ==(other)
- value == other.value
- end
-
- def !=(other)
- value != other.value
- end
-
- def ===(other)
- value == other.value
- end
-
- def |(other : self)
- self.class.new(value | other.value)
- end
-
- def &(other : self)
- self.class.new(value & other.value)
- end
-
- def ~
- self.class.new(~value)
- end
-
- def includes?(other : self)
- (value & other.value) != 0
- end
-
- # Returns the enum member that has the given value, or `nil` if
- # no such member exists.
- #
- # ```
- # Color.from_value?(0) # => Color::Red
- # Color.from_value?(1) # => Color::Green
- # Color.from_value?(2) # => Color::Blue
- # Color.from_value?(3) # => nil
- # ```
- def self.from_value?(value : Int) : self?
- {% if @type.annotation(Flags) %}
- all_mask = {{@type}}::All.value
- return if all_mask & value != value
- return new(all_mask.class.new(value))
- {% else %}
- {% for member in @type.constants %}
- return new({{@type.constant(member)}}) if {{@type.constant(member)}} == value
- {% end %}
- {% end %}
- nil
- end
-
- # Returns the enum member that has the given value, or raises
- # if no such member exists.
- #
- # ```
- # Color.from_value(0) # => Color::Red
- # Color.from_value(1) # => Color::Green
- # Color.from_value(2) # => Color::Blue
- # Color.from_value(3) # raises Exception
- # ```
- def self.from_value(value : Int) : self
- from_value?(value) || raise "Unknown enum value"
- end
-
- def to_s(io) : Nil
- {% if @type.annotation(Flags) %}
- if value == 0
- io.print "None"
- else
- found = false
- {% for member in @type.constants %}
- {% if member.stringify != "All" %}
- if {{@type.constant(member)}} != 0 && value.bits_set? {{@type.constant(member)}}
- io.print " | " if found
- io.print {{member.stringify}}
- found = true
- end
- {% end %}
- {% end %}
- io.print value unless found
- end
- {% else %}
- io.print to_s
- {% end %}
- nil
- end
-
- def to_s
- {% if @type.annotation(Flags) %}
- value
- {% else %}
- # Can't use `case` here because case with duplicate values do
- # not compile, but enums can have duplicates (such as `enum Foo; FOO = 1; BAR = 1; end`).
- {% for member, i in @type.constants %}
- if value == {{@type.constant(member)}}
- return {{member.stringify}}
- end
- {% end %}
-
- value
- {% end %}
- end
-end
@@ -1,226 +0,0 @@
-# :nodoc:
-struct Int
- alias Signed = Int8 | Int16 | Int32 | Int64 | Int128
- alias Unsigned = UInt8 | UInt16 | UInt32 | UInt64 | UInt128
- alias Primitive = Signed | Unsigned
-
- def times
- x = 0
- while x < self
- yield x
- x += 1
- end
- end
-
- def //(other)
- self.unsafe_div other
- end
-
- def %(other)
- self.unsafe_mod other
- end
-
- def <<(other)
- self.unsafe_shl other
- end
-
- def >>(other)
- self.unsafe_shr other
- end
-
- def ~
- self ^ -1
- end
-
- def ===(other)
- self == other
- end
-
- def abs
- self >= 0 ? self : self * -1
- end
-
- def div_ceil(other : Int)
- (self + (other - 1)) // other
- end
-
- # bit manips
- def find_first_zero : Int
- Intrinsics.counttrailing32(~self.to_i32, true)
- end
-
- def nearest_power_of_2
- n = self - 1
- while (n & (n - 1)) != 0
- n = n & (n - 1)
- end
- n << 1
- end
-
- def lowest_power_of_2
- x = self
- x = x | (x >> 1)
- x = x | (x >> 2)
- x = x | (x >> 4)
- x = x | (x >> 8)
- x = x | (x >> 16)
- x - (x >> 1)
- end
-
- def hash(hasher)
- hasher.hash self
- end
-
- # format
- private BASE = "0123456789abcdefghijklmnopqrstuvwxyz"
-
- def each_digit(base = 10, &block)
- s = uninitialized UInt8[128]
- sign = self < 0
- n = self.abs
- i = 0
- while i < 128
- s[i] = BASE.to_unsafe[n % base]
- i += 1
- break if (n //= base) == 0
- end
- if sign
- yield '-'.ord.to_u8
- end
- i -= 1
- while true
- yield s[i]
- break if i == 0
- i -= 1
- end
- end
-
- def to_s(base : Int = 10)
- s = uninitialized UInt8[128]
- sign = self < 0
- n = self.abs
- i = 0
- while i < 128
- s[i] = BASE.to_unsafe[n % base]
- i += 1
- break if (n //= base) == 0
- end
- if sign
- s[i] = '-'.ord.to_u8
- else
- i -= 1
- end
- builder = String::Builder.new(i + 1)
- while true
- builder.write_byte s[i]
- break if i == 0
- i -= 1
- end
- builder.to_s
- end
-
- def to_s(io, base : Int = 10)
- each_digit(base) do |ch|
- io.putc ch
- end
- end
-
- def to_usize
- self.to_u64
- end
-
- def to_isize
- self.to_i64
- end
-end
-
-# :nodoc:
-struct Int8
- def popcount
- Intrinsics.popcount8 self
- end
-end
-
-# :nodoc:
-struct UInt8
- def popcount
- Intrinsics.popcount8 self
- end
-end
-
-# :nodoc:
-struct Int16
- def popcount
- Intrinsics.popcount16 self
- end
-end
-
-# :nodoc:
-struct UInt16
- def popcount
- Intrinsics.popcount16 self
- end
-end
-
-# :nodoc:
-struct Int32
- def popcount
- Intrinsics.popcount32 self
- end
-end
-
-# :nodoc:
-struct UInt32
- def popcount
- Intrinsics.popcount32 self
- end
-end
-
-# :nodoc:
-struct Int64
- def popcount
- Intrinsics.popcount64 self
- end
-end
-
-# :nodoc:
-struct UInt64
- def popcount
- Intrinsics.popcount64 self
- end
-end
-
-# :nodoc:
-alias ISize = Int64
-
-# :nodoc:
-alias USize = UInt64
-
-# :nodoc:
-struct Int128
-end
-
-# :nodoc:
-struct UInt128
-end
-
-module Math
- extend self
-
- # Returns the minimum between `a` and `b`.
- def min(a, b)
- a < b ? a : b
- end
-
- # Returns the maximum between `a` and `b`.
- def max(a, b)
- a > b ? a : b
- end
-
- # Clamps `x` to `min` and `max`.
- def clamp(x, min, max)
- return min if x < min
- return max if x > max
- x
- end
-end
@@ -1,9 +0,0 @@
-# :nodoc:
-class Markable
- def write_barrier(&block)
- yield
- end
-
- def mark(&block : Void* ->)
- end
-end
@@ -1,47 +0,0 @@
-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
@@ -1,90 +0,0 @@
-class Object
- def not_nil!
- self
- end
-
- {% for prefixes in { {"", "", "@", "#"}, {"class_", "self.", "@@", "."} } %}
- {%
- macro_prefix = prefixes[0].id
- method_prefix = prefixes[1].id
- var_prefix = prefixes[2].id
- doc_prefix = prefixes[3].id
- %}
- macro {{macro_prefix}}getter(*names)
- \{% for name in names %}
- \{% if name.is_a?(TypeDeclaration) %}
- def {{method_prefix}}\{{ name.var.id }} : \{{name.type}}
- {{var_prefix}}\{{ name.var.id }}
- end
- \{% else %}
- def {{method_prefix}}\{{ name.id }}
- {{var_prefix}}\{{ name.id }}
- end
- \{% end %}
- \{% end %}
- end
-
- macro {{macro_prefix}}getter!(*names)
- \{% for name in names %}
- \{% if name.is_a?(TypeDeclaration) %}
- def {{method_prefix}}\{{ name.var.id }} : \{{name.type}}
- {{var_prefix}}\{{ name.var.id }}.not_nil!
- end
- \{% else %}
- def {{method_prefix}}\{{ name.id }}
- {{var_prefix}}\{{ name.id }}.not_nil!
- end
- \{% end %}
- \{% end %}
- end
-
- macro {{macro_prefix}}setter(*names)
- \{% for name in names %}
- def {{method_prefix}}\{{ name.id }}=({{var_prefix}}\{{ name.id }})
- end
- \{% end %}
- end
-
- macro {{macro_prefix}}property(*names)
- \{% for name in names %}
- def {{method_prefix}}\{{ name.id }}
- {{var_prefix}}\{{ name.id }}
- end
- def {{method_prefix}}\{{ name.id }}=({{var_prefix}}\{{ name.id }})
- end
- \{% end %}
- end
- {% end %}
-
- def unsafe_as(type : T.class) forall T
- x = self
- pointerof(x).as(T*).value
- end
-
- def as!(type : T.class) forall T
- if self.is_a?(T)
- self.unsafe_as type
- else
- abort "invalid type cast!"
- end
- end
-end
-
-# :nodoc:
-struct Nil
- def not_nil!
- abort "casting nil to not-nil!"
- end
-
- def to_s(io)
- io.print "nil"
- end
-
- def ==(other)
- false
- end
-
- def object_id
- 0u64
- end
-end
@@ -1,33 +0,0 @@
-def abort(message : String)
- raise message
-end
-
-def panic(message : String)
- raise message
-end
-
-def raise(message : String) #: NoReturn
- # Console.print "\n\n-- fatal exception: ", message, " --\n"
- # Console.print "-- halting --\n"
- # Architecture.halt_processor
- # return NoReturn
-end
-
-@[Raises]
-fun __crystal_raise(unwind_ex : Void*) #: NoReturn
- # Console.print "\n\n-- fatal exception: raise called --\n"
- # Console.print "-- halting --\n"
- # Architecture.halt_processor
- # return NoReturn
-end
-
-fun __crystal_raise_overflow #: NoReturn
- # Console.print "\n\n-- fatal exception: overflow occured --\n"
- # Console.print "-- halting --\n"
- # Architecture.halt_processor
- # return NoReturn
-end
-
-fun __crystal_get_exception(unwind_ex : Void*) : UInt64
- 0u64
-end
@@ -1,61 +0,0 @@
-struct Pointer(T)
- def self.null
- new 0u64
- end
-
- def null?
- address == 0u64
- end
-
- def +(other : Int)
- Pointer(T).new(address + other * sizeof(T))
- end
-
- def -(other : Int)
- Pointer(T).new(address - other * sizeof(T))
- end
-
- def -(other : self)
- (address - other.address) // sizeof(T)
- end
-
- def [](offset : Int)
- (self + offset).value
- end
-
- def []=(offset : Int, value : T)
- (self + offset).value = value
- end
-
- def ==(other : self)
- address == other.address
- end
-
- def realloc(new_size : UInt64)
- Pointer(T).new Intrinsics.realloc(address, new_size * sizeof(T))
- end
-
- def memcpy(dest : Pointer, src : Pointer, size : USize)
- Intrinsics.memcpy dest.address, src.address, size
- end
-
- def memcmp(a : Pointer, b : Pointer, size : USize)
- Intrinsics.memcmp a.address, b.address, size
- end
-
- def memset(dest : Pointer, value : UInt8, size : USize)
- Intrinsics.memset dest.address, value, size
- end
-
- def malloc_atomic(size : UInt32)
- ptr = realloc(size)
- if ptr.null?
- ptr = realloc(size)
- raise "Out of memory" if ptr.null?
- else
- ptr = realloc(size)
- end
- ptr
- end
-
-end
@@ -1,26 +0,0 @@
-# :nodoc:
-struct Proc
- def self.new(pointer : Void*, closure_data : Void*)
- func = {pointer, closure_data}
- ptr = pointerof(func).as(self*)
- ptr.value
- end
-
- def pointer
- internal_representation[0]
- end
-
- def closure_data
- internal_representation[1]
- end
-
- def closure?
- !closure_data.null?
- end
-
- private def internal_representation
- func = self
- ptr = pointerof(func).as({Void*, Void*}*)
- ptr.value
- end
-end
@@ -1,19 +0,0 @@
-# :nodoc:
-struct Range(B, E)
- def begin
- @begin
- end
-
- def end
- @end
- end
-
- getter exclusive
-
- def size
- @end - @begin
- end
-
- def initialize(@begin : B, @end : E, @exclusive : Bool = false)
- end
-end
@@ -1,14 +0,0 @@
-# :nodoc:
-class Reference
- def ==(other : self)
- same?(other)
- end
-
- def ==(other)
- false
- end
-
- def same?(other : Reference)
- object_id == other.object_id
- end
-end
@@ -1,68 +0,0 @@
-struct Slice(T)
- getter size : UInt64
- @size : UInt64
-
- def initialize(@buffer : Pointer(T), size : Int)
- @size = size.to_u64
- end
-
- def self.null
- new Pointer(T).null, 0
- end
-
- def null?
- @buffer.null?
- end
-
- def self.malloc(sz : Int)
- new Pointer(T).malloc(sz.to_u64), sz
- end
-
- def self.malloc_atomic(sz : Int)
- new Pointer(T).malloc_atomic(sz.to_u64), sz
- end
-
- def self.mmalloc_a(sz, allocator)
- new allocator.malloc(sz * sizeof(T)).as(T*), sz
- end
-
- def [](idx : Int)
- return nil if idx >= @size || idx < 0
- @buffer[idx]
- end
-
- @[AlwaysInline]
- def []=(idx : Int, value : T)
- return value if idx >= @size || idx < 0
- @buffer[idx] = value
- end
-
- def [](range : Range(Int, Int))
- raise "Slice: out of range" if range.begin > range.end
- Slice(T).new(@buffer + range.begin, range.size)
- end
-
- def to_unsafe
- @buffer
- end
-
- def each(&block)
- i = 0
- while i < @size
- yield @buffer[i]
- i += 1
- end
- end
-
- def hash(hasher)
- hasher.hash self
- end
-
- def ==(other : String)
- other == self
- end
-
- def to_s(io)
- io.print "Slice(", @buffer, " ", @size, ")"
- end
-end
@@ -1,41 +0,0 @@
-class SliceWriter
- getter slice, offset
-
- def initialize(@slice : Slice(UInt8), @skip = -1)
- @offset = 0
- end
-
- def putc(ch)
- if @skip > 0
- @skip -= 1
- return
- end
- @slice[@offset] = ch
- @offset += 1
- end
-
- def print(ch : Char)
- putc(ch.ord.to_u8)
- end
-
- def print(ch : Int32)
- putc(ch.to_u8)
- end
-
- def print(str : String)
- str.each_byte do |ch|
- putc(ch)
- end
- end
-
- def print(*args)
- args.each do |arg|
- arg.to_s self
- end
- end
-
- def <<(other)
- other.to_s self
- @offset != @slice.size
- end
-end
@@ -1,49 +0,0 @@
-# :nodoc:
-struct StaticArray(T, N)
- macro [](*args)
- %array = uninitialized StaticArray(typeof({{*args}}), {{args.size}})
- {% for arg, i in args %}
- %array.to_unsafe[{{i}}] = {{arg}}
- {% end %}
- %array
- end
-
- def to_unsafe : Pointer(T)
- pointerof(@buffer)
- end
-
- def []=(index : Int, value : T)
- abort "setting out of bounds!" unless 0 <= index < N
- to_unsafe[index] = value
- end
-
- def [](index : Int)
- abort "setting out of bounds!" unless 0 <= index < N
- to_unsafe[index]
- end
-
- def []?(index : Int)
- return nil if index > N
- to_unsafe[index]
- end
-
- def size
- N
- end
-
- def each : Nil
- {% for i in 0...N %}
- yield self[{{i}}]
- {% end %}
- end
-
- def each_with_index : Nil
- {% for i in 0...N %}
- yield self[{{i}}], {{i}}
- {% end %}
- end
-
- def to_s(io)
- io.print "StaticArray(", to_unsafe, " ", N, ")"
- end
-end
@@ -1,318 +0,0 @@
-require "./int.cr"
-require "./pointer.cr"
-
-class String
- TYPE_ID = "".crystal_type_id
- HEADER_SIZE = sizeof({Int32, Int32, Int32})
-
- class Builder
- @capacity : Int32 = 0
- @bytesize : Int32 = 0
- # getter capacity, bytesize
-
- def capacity
- @capacity
- end
-
- def bytesize
- @bytesize
- end
-
- def initialize(capacity : Int32)
- @buffer = Pointer(UInt8).malloc_atomic(capacity.to_u32 + 1 + HEADER_SIZE)
- @capacity = capacity_for_ptr @buffer
- @bytesize = 0
- @finished = false
- end
-
- def initialize
- @capacity = 0
- @buffer = Pointer(UInt8).null
- @bytesize = 0
- @finished = false
- end
-
- private def capacity_for_ptr(ptr)
- (Allocator.block_size_for_ptr(ptr) - HEADER_SIZE).to_i32
- end
-
- def buffer
- @buffer + String::HEADER_SIZE
- end
-
- def empty?
- @bytesize == 0
- end
-
- def back(amount : Int)
- abort "overflow" if amount > @bytesize
- @bytesize -= amount
- end
-
- def write_byte(other : UInt8)
- if @bytesize == @capacity
- if @buffer.null?
- @buffer = Pointer(UInt8).malloc_atomic(5 + HEADER_SIZE)
- @capacity = capacity_for_ptr @buffer
- else
- old_buffer = @buffer
- old_size = @capacity.to_usize + 1 + HEADER_SIZE
- @buffer = Pointer(UInt8).malloc_atomic(@bytesize.to_u32 + 1 + HEADER_SIZE)
- @capacity = capacity_for_ptr @buffer
- memcpy(@buffer, old_buffer, old_size)
- end
- end
- buffer[@bytesize] = other
- @bytesize += 1
- end
-
- def <<(other : String)
- other.each_byte do |byte|
- write_byte byte
- end
- end
-
- def <<(other : Slice(UInt8))
- other.each do |byte|
- write_byte byte
- end
- end
-
- def <<(other : Char)
- if other.ord <= 0xFF
- write_byte other.ord.to_u8
- else
- abort "TODO: support utf-8 for builder"
- end
- end
-
- def putc(other : UInt8)
- write_byte other
- end
-
- def <<(other)
- other.to_s self
- end
-
- def to_s : String
- abort "Can only invoke 'to_s' once on String::Builder" if @finished
- @finished = true
-
- write_byte 0u8
-
- header = @buffer.as({Int32, Int32, Int32}*)
- bytesize, length = String.calculate_length(buffer)
- header.value = {String::TYPE_ID, bytesize, length}
- @buffer.as(String)
- end
-
- def reset
- @buffer = Pointer(UInt8).null
- @capacity = 0
- @bytesize = 0
- @finished = false
- end
-
- def reset(capacity : Int32)
- @buffer = Pointer(UInt8).malloc_atomic(capacity.to_u32 + 1 + HEADER_SIZE)
- @capacity = capacity_for_ptr @buffer
- @bytesize = 0
- @finished = false
- end
- end
-
- def self.new(capacity : Int)
- str = Pointer(UInt8).malloc_atomic(capacity.to_u32 + HEADER_SIZE + 1)
- buffer = str.as(String).to_unsafe
- bytesize, size = yield buffer
-
- unless 0 <= bytesize <= capacity
- return nil
- end
-
- buffer[bytesize] = 0_u8
-
- # TODO: Try to reclaim some memory if capacity is bigger than what was requested
-
- str_header = str.as({Int32, Int32, Int32}*)
- str_header.value = {TYPE_ID, bytesize.to_i, size.to_i}
- str.as(String)
- end
-
- def self.new(byte : UInt8)
- (new(1) { |buffer|
- buffer[0] = byte
- buffer[1] = 0u8
- {1, 1}
- }).not_nil!
- end
-
- def self.new(bytes : Slice(UInt8))
- (new(bytes.size + 1) { |buffer|
- memcpy(buffer, bytes.to_unsafe, bytes.size.to_usize)
- buffer[bytes.size] = 0u8
- String.calculate_length(buffer)
- }).not_nil!
- end
-
- def self.new(bytes : StaticArray)
- (new(bytes.size + 1) { |buffer|
- memcpy(buffer, bytes.to_unsafe, bytes.size.to_usize)
- buffer[bytes.size] = 0u8
- String.calculate_length(buffer)
- }).not_nil!
- end
-
- def self.new(bytes : NullTerminatedSlice)
- String.new Slice(UInt8).new(bytes.to_unsafe, bytes.size)
- end
-
- protected def self.calculate_length(buffer : UInt8*)
- i = 0
- length = 0
- until (ch = buffer[i]) == 0u8
- if ch >= 0b110_00000u8
- i += 2
- elsif ch >= 0b1110_0000u8
- i += 3
- elsif ch >= 0b11110_000u8
- i += 4
- else
- i += 1
- end
- length += 1
- end
- {i, length}
- end
-
- private def mask_tail_char(ch : UInt8) : UInt32
- (ch & 0b111111u8).to_u32
- end
-
- private def each_unicode_point
- i = 0
- points = 0
- until (ch = to_unsafe[i]) == 0u8
- point = 0u32
- if ch >= 0b110_00000u8
- point = (ch & 0b11111u8).to_u32 << 6 | mask_tail_char(to_unsafe[i + 1])
- i += 2
- elsif ch >= 0b1110_0000u8
- point = (ch & 0b1111u8).to_u32 << 12 |
- (mask_tail_char(to_unsafe[i + 1]) << 6) |
- (mask_tail_char(to_unsafe[i + 2]))
- i += 3
- elsif ch >= 0b11110_000u8
- point = (ch & 0b1111u8).to_u32 << 18 |
- (mask_tail_char(to_unsafe[i + 1]) << 12) |
- (mask_tail_char(to_unsafe[i + 2]) << 6) |
- (mask_tail_char(to_unsafe[i + 3]))
- i += 4
- else
- point = ch.to_u32
- i += 1
- end
- yield point.unsafe_chr, points, i
- points += 1
- end
- end
-
- def size
- @length
- end
-
- def bytesize
- @bytesize
- end
-
- def to_unsafe : UInt8*
- pointerof(@c)
- end
-
- def byte_slice
- Slice(UInt8).new(to_unsafe, bytesize)
- end
-
- def each_char(&block)
- each_unicode_point do |char|
- yield char
- end
- end
-
- def each_byte(&block)
- @bytesize.times do |i|
- yield to_unsafe[i]
- end
- end
-
- def [](index : Int)
- each_unicode_point do |char, i|
- return char if i == index
- end
- 0.unsafe_chr
- end
-
- def ==(other : self)
- return true if same?(other)
- return false unless bytesize == other.bytesize
- memcmp(to_unsafe, other.to_unsafe, bytesize) == 0
- end
-
- def ==(other : Slice(UInt8))
- return false unless bytesize == other.size
- memcmp(to_unsafe, other.to_unsafe, bytesize) == 0
- end
-
- def ===(other)
- self == other
- end
-
- def index(search)
- each_unicode_point do |char, i|
- return i if search == char
- end
- nil
- end
-
- 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
- end
-
- def to_s(io)
- each_byte do |char|
- io.putc char
- end
- end
-end
@@ -1,44 +0,0 @@
-# :nodoc:
-struct Tuple
- def self.new(*args : *T)
- args
- end
-
- def each : Nil
- {% for i in 0...T.size %}
- yield self[{{i}}]
- {% end %}
- end
-
- def to_s(io)
- io.print "Tuple("
- each do |x|
- io.print x, ","
- end
- io.print ")"
- end
-
- def at(index : Int)
- at(index) { nil }
- end
-
- def at(index : Int)
- index += size if index < 0
- {% for i in 0...T.size %}
- return self[{{i}}] if {{i}} == index
- {% end %}
- yield
- end
-
- def [](index : Int)
- at(index)
- end
-
- def []?(index : Int)
- at(index) { nil }
- end
-
- def size
- {{T.size}}
- end
-end
@@ -1,423 +0,0 @@
-# Methods defined here are primitives because they either:
-# * can't be expressed in Crystal (need to be expressed in LLVM). For example unary
-# and binary math operators fall into this category.
-# * should always be inlined with an LLVM instruction for performance reasons, even
-# in non-release builds. An example of this is `Char#ord`, which could be implemented
-# in Crystal by assigning `self` to a variable and casting a pointer to it to `Int32`,
-# and then reading back the value.
-
-# :nodoc:
-class Object
- # Returns the **runtime** `Class` of an object.
- #
- # ```
- # 1.class # => Int32
- # "hello".class # => String
- # ```
- #
- # Compare it with `typeof`, which returns the **compile-time** type of an object:
- #
- # ```
- # random_value = rand # => 0.627423
- # value = random_value < 0.5 ? 1 : "hello"
- # value # => "hello"
- # value.class # => String
- # typeof(value) # => Int32 | String
- # ```
- @[Primitive(:class)]
- def class
- end
-
- # :nodoc:
- @[Primitive(:object_crystal_type_id)]
- def crystal_type_id : Int32
- end
-end
-
-class Reference
- # Returns a `UInt64` that uniquely identifies this object.
- #
- # The returned value is the memory address of this object.
- #
- # ```
- # string = "hello"
- # string.object_id # => 4460249568
- #
- # pointer = Pointer(String).new(string.object_id)
- # string2 = pointer.as(String)
- # string2.object_id == string.object_id # => true
- # ```
- @[Primitive(:object_id)]
- def object_id : UInt64
- end
-end
-
-# :nodoc:
-class Class
- # :nodoc:
- @[Primitive(:class_crystal_instance_type_id)]
- def crystal_instance_type_id : Int32
- end
-end
-
-struct Bool
- # Returns `true` if `self` is equal to *other*.
- @[Primitive(:binary)]
- def ==(other : Bool) : Bool
- end
-
- # Returns `true` if `self` is not equal to *other*.
- @[Primitive(:binary)]
- def !=(other : Bool) : Bool
- end
-end
-
-struct Char
- # Returns the codepoint of this char.
- #
- # The codepoint is the integer representation.
- # The Universal Coded Character Set (UCS) standard, commonly known as Unicode,
- # assigns names and meanings to numbers, these numbers are called codepoints.
- #
- # For values below and including 127 this matches the ASCII codes
- # and thus its byte representation.
- #
- # ```
- # 'a'.ord # => 97
- # '\0'.ord # => 0
- # '\u007f'.ord # => 127
- # '☃'.ord # => 9731
- # ```
- @[Primitive(:cast)]
- def ord : Int32
- end
-
- {% for op, desc in {
- "==" => "equal to",
- "!=" => "not equal to",
- "<" => "less than",
- "<=" => "less than or equal to",
- ">" => "greater than",
- ">=" => "greater than or equal to",
- } %}
- # Returns `true` if `self`'s codepoint is {{desc.id}} *other*'s codepoint.
- @[Primitive(:binary)]
- def {{op.id}}(other : Char) : Bool
- end
- {% end %}
-end
-
-# :nodoc:
-struct Symbol
- # Returns `true` if `self` is equal to *other*.
- @[Primitive(:binary)]
- def ==(other : Symbol) : Bool
- end
-
- # Returns `true` if `self` is not equal to *other*.
- @[Primitive(:binary)]
- def !=(other : Symbol) : Bool
- end
-
- # Returns a unique number for this symbol.
- @[Primitive(:cast)]
- def to_i : Int32
- end
-
- # Returns the symbol's name as a String.
- #
- # ```
- # :foo.to_s # => "foo"
- # :"hello world".to_s # => "hello world"
- # ```
- @[Primitive(:symbol_to_s)]
- def to_s : String
- end
-end
-
-struct Pointer(T)
- # Allocates `size * sizeof(T)` bytes from the system's heap initialized
- # to zero and returns a pointer to the first byte from that memory.
- # The memory is allocated by the `GC`, so when there are
- # no pointers to this memory, it will be automatically freed.
- #
- # ```
- # # Allocate memory for an Int32: 4 bytes
- # ptr = Pointer(Int32).malloc(1_u64)
- # ptr.value # => 0
- #
- # # Allocate memory for 10 Int32: 40 bytes
- # ptr = Pointer(Int32).malloc(10_u64)
- # ptr[0] # => 0
- # # ...
- # ptr[9] # => 0
- # ```
- @[Primitive(:pointer_malloc)]
- def self.malloc(size : UInt64)
- end
-
- # Returns a pointer that points to the given memory address.
- # This doesn't allocate memory.
- #
- # ```
- # ptr = Pointer(Int32).new(5678_u64)
- # ptr.address # => 5678
- # ```
- @[Primitive(:pointer_new)]
- def self.new(address : UInt64)
- end
-
- # Gets the value pointed by this pointer.
- #
- # ```
- # ptr = Pointer(Int32).malloc(4)
- # ptr.value = 42
- # ptr.value # => 42
- # ```
- @[Primitive(:pointer_get)]
- def value : T
- end
-
- # Sets the value pointed by this pointer.
- #
- # ```
- # ptr = Pointer(Int32).malloc(4)
- # ptr.value = 42
- # ptr.value # => 42
- # ```
- @[Primitive(:pointer_set)]
- def value=(value : T)
- end
-
- # Returns the address of this pointer.
- #
- # ```
- # ptr = Pointer(Int32).new(1234)
- # ptr.address # => 1234
- # ```
- @[Primitive(:pointer_address)]
- def address : UInt64
- end
-
- # Tries to change the size of the allocation pointed to by this pointer to *size*,
- # and returns that pointer.
- #
- # Since the space after the end of the block may be in use, realloc may find it
- # necessary to copy the block to a new address where more free space is available.
- # The value of realloc is the new address of the block.
- # If the block needs to be moved, realloc copies the old contents.
- #
- # Remember to always assign the value of realloc.
- #
- # ```
- # ptr = Pointer.malloc(4) { |i| i + 1 } # [1, 2, 3, 4]
- # ptr = ptr.realloc(8_u8)
- # ptr # [1, 2, 3, 4, 0, 0, 0, 0]
- # ```
- @[Primitive(:pointer_realloc)]
- def realloc(size : UInt64) : self
- end
-
- # Returns a new pointer whose address is this pointer's address
- # incremented by `other * sizeof(T)`.
- #
- # ```
- # ptr = Pointer(Int32).new(1234)
- # ptr.address # => 1234
- #
- # # An Int32 occupies four bytes
- # ptr2 = ptr + 1_u64
- # ptr2.address # => 1238
- # ```
- @[Primitive(:pointer_add)]
- def +(offset : Int64) : self
- end
-
- # Returns how many T elements are there between this pointer and *other*.
- # That is, this is `(self.address - other.address) / sizeof(T)`.
- #
- # ```
- # ptr1 = Pointer(Int32).malloc(4)
- # ptr2 = ptr1 + 2
- # ptr2 - ptr1 # => 2
- # ```
- @[Primitive(:pointer_diff)]
- def -(other : self) : Int64
- end
-end
-
-struct Proc
- # Invokes this `Proc` and returns the result.
- #
- # ```
- # add = ->(x : Int32, y : Int32) { x + y }
- # add.call(1, 2) # => 3
- # ```
- @[Primitive(:proc_call)]
- @[Raises]
- def call(*args : *T) : R
- end
-end
-
-# All `Number` methods are defined on concrete structs (for example `Int32`, `UInt8`, etc.),
-# never on `Number`, `Int` or `Float` because we don't want to handle a primitive for
-# other types that could extend these types (for example `BigInt`): if we do that
-# a compiler crash will happen.
-#
-# A similar logic is applied to method arguments: they are always concrete, to avoid
-# unintentionally handling a `BigInt` and have a crash. We also can't have an argument
-# be a union, because the codegen primitives always consider primitive types,
-# never unions.
-
-{% begin %}
- {% ints = %w(Int8 Int16 Int32 Int64 Int128 UInt8 UInt16 UInt32 UInt64 UInt128) %}
- {% floats = %w(Float32 Float64) %}
- {% nums = %w(Int8 Int16 Int32 Int64 Int128 UInt8 UInt16 UInt32 UInt64 UInt128 Float32 Float64) %}
- {% binaries = {"+" => "adding", "-" => "subtracting", "*" => "multiplying"} %}
-
- {% for num in nums %}
- struct {{num.id}}
- {% for name, type in {
- to_i: Int32, to_u: UInt32, to_f: Float64,
- to_i8: Int8, to_i16: Int16, to_i32: Int32, to_i64: Int64, to_i128: Int128,
- to_u8: UInt8, to_u16: UInt16, to_u32: UInt32, to_u64: UInt64, to_u128: UInt128,
- to_f32: Float32, to_f64: Float64,
- } %}
- # Returns `self` converted to `{{type}}`.
- # Raises `OverflowError` in case of overflow.
- @[Primitive(:convert)]
- @[Raises]
- def {{name.id}} : {{type}}
- end
-
- # Returns `self` converted to `{{type}}`.
- # In case of overflow a wrapping is performed.
- # {% if ints.includes?(num) %} a wrapping is performed.
- # {% elsif type < Int %} the result is undefined.
- # {% else %} infinity is returned.
- # {% end %}
- @[Primitive(:unchecked_convert)]
- def {{name.id}}! : {{type}}
- end
- {% end %}
-
- {% for num2 in nums %}
- {% for op, desc in {
- "==" => "equal to",
- "!=" => "not equal to",
- "<" => "less than",
- "<=" => "less than or equal to",
- ">" => "greater than",
- ">=" => "greater than or equal to",
- } %}
- # Returns `true` if `self` is {{desc.id}} *other*.
- @[Primitive(:binary)]
- def {{op.id}}(other : {{num2.id}}) : Bool
- end
- {% end %}
- {% end %}
- end
- {% end %}
-
- {% for int in ints %}
- struct {{int.id}}
- # Returns a `Char` that has the unicode codepoint of `self`,
- # without checking if this integer is in the range valid for
- # chars (`0..0xd7ff` and `0xe000..0x10ffff`).
- #
- # You should never use this method unless `chr` turns out to
- # be a bottleneck.
- #
- # ```
- # 97.unsafe_chr # => 'a'
- # ```
- @[Primitive(:convert)]
- def unsafe_chr : Char
- end
-
- {% for int2 in ints %}
- {% for op, desc in binaries %}
- # Returns the result of {{desc.id}} `self` and *other*.
- # Raises `OverflowError` in case of overflow.
- @[Primitive(:binary)]
- @[Raises]
- def {{op.id}}(other : {{int2.id}}) : self
- end
-
- # Returns the result of {{desc.id}} `self` and *other*.
- # In case of overflow a wrapping is performed.
- @[Primitive(:binary)]
- def &{{op.id}}(other : {{int2.id}}) : self
- end
- {% end %}
-
- # Returns the result of performing a bitwise OR of `self`'s and *other*'s bits.
- @[Primitive(:binary)]
- def |(other : {{int2.id}}) : self
- end
-
- # Returns the result of performing a bitwise AND of `self`'s and *other*'s bits.
- @[Primitive(:binary)]
- def &(other : {{int2.id}}) : self
- end
-
- # Returns the result of performing a bitwise XOR of `self`'s and *other*'s bits.
- @[Primitive(:binary)]
- def ^(other : {{int2.id}}) : self
- end
-
- # :nodoc:
- @[Primitive(:binary)]
- def unsafe_shl(other : {{int2.id}}) : self
- end
-
- # :nodoc:
- @[Primitive(:binary)]
- def unsafe_shr(other : {{int2.id}}) : self
- end
-
- # :nodoc:
- @[Primitive(:binary)]
- def unsafe_div(other : {{int2.id}}) : self
- end
-
- # :nodoc:
- @[Primitive(:binary)]
- def unsafe_mod(other : {{int2.id}}) : self
- end
- {% end %}
-
- {% for float in floats %}
- {% for op, desc in binaries %}
- # Returns the result of {{desc.id}} `self` and *other*.
- @[Primitive(:binary)]
- def {{op.id}}(other : {{float.id}}) : {{float.id}}
- end
- {% end %}
- {% end %}
- end
- {% end %}
-
- {% for float in floats %}
- struct {{float.id}}
- {% for num in nums %}
- {% for op, desc in binaries %}
- # Returns the result of {{desc.id}} `self` and *other*.
- @[Primitive(:binary)]
- def {{op.id}}(other : {{num.id}}) : self
- end
- {% end %}
-
- # Returns the float division of `self` and *other*.
- @[Primitive(:binary)]
- def fdiv(other : {{num.id}}) : self
- end
- {% end %}
-
- # Returns the result of division `self` and *other*.
- @[Primitive(:binary)]
- def /(other : {{float.id}}) : {{float.id}}
- end
- end
- {% end %}
-{% end %}
@@ -1,15 +0,0 @@
-# Global `puts` method
-def puts(str : String)
- VGA.puts str
-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"
@@ -0,0 +1 @@
+int main() { return 0; }
@@ -0,0 +1,2 @@
+fun kmain
+end
@@ -1,7 +0,0 @@
-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.