chore(arch): make it build/load kernel when using kernel.cr (crystal) too@@ -1,57 +0,0 @@
-#!/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"
@@ -2,12 +2,12 @@
# Flags
-kernelCompileFlags="-ffreestanding -nostdinc -nostdinc++ \
+CC_FLAGS="-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"
+LD_FLAGS="-nostdlib -Wl,--oformat=binary,-T./kernel/linker.ld"
+CPP_FILES="./kernel/unityBuild.cpp ./build/kernelEntryPoint.o"
# Build
@@ -24,11 +24,11 @@ use_crystal_kernel=true
if [ "$use_crystal_kernel" = true ]
then
- crystal build --no-debug -Dkernel --cross-compile --target i386-unknown-linux-elf --prelude=empty -o ./build/kernel ./kernel/kernel.cr
+ crystal build --no-debug -Dkernel --cross-compile --target i386-unknown-linux-elf --prelude=../prelude.cr -o ./build/kernel ./kernel/kernel.cr
# The object file should be at ./build/kernel.o, now link it with the entry point
ld -m elf_i386 -nostdlib -T ./kernel/linker.ld -o ./bin/kernel.bin ./build/kernelEntryPoint.o ./build/kernel.o || exit 1
else
- clang++ $kernelCompileFlags $kernelLinkFlags $kernelFiles || exit 1
+ clang++ $CC_FLAGS $LD_FLAGS $CPP_FILES || exit 1
fi
cp ./bin/kernel.bin ./iso/boot/kernel.bin || exit 1
@@ -1,5 +1,3 @@
-# require "../minrt/src/prelude"
-
lib LibBootstrap
@[Packed]
struct StartInfo
@@ -21,23 +19,45 @@ fun kearly(info_ptr: LibBootstrap::StartInfo*)
# IDT.init
end
+def clear_screen
+ video_memory = Pointer(UInt8).new(0xB8000)
+ screen_size = 80 * 25
+
+ i = 0
+ while i < screen_size
+ video_memory[2 * i] = ' '.ord.to_u8
+ video_memory[2 * i + 1] = 0x07_u8
+ i += 1
+ end
+end
+
fun kmain
# IDT.enable_interrupts
# kprint "Hello from Fluorite."
- address = 0xb800 # Pointer(UInt8).reinterpret(0xb8000)
+ address = Pointer(UInt8).new(0xb8000)
string = "[CrystalOS Kernel] Hello from Crystal"
- string_size = 33 # string.size
+ # string_size = 33 # string.size
+
+ # clear_screen
# Loop over each character in the string
- # [0...string_size];each do |i|
+ # i = 0
+ # while i < string.size
# # Write the character
# address.value = string[i].to_u8
# address += 1
# # Write the attribute byte
# address.value = 0xA0.to_u8
# address += 1
+ # i += 1
+ # end
+
+ # prevent bootloader to take back
+ # while true
+ # asm("cli")
+ # asm("hlt")
# end
end
@@ -0,0 +1,25 @@
+require "./prelude/atomic"
+require "./prelude/primitives"
+require "./prelude/puts"
+
+# 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"
@@ -0,0 +1,248 @@
+# :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
@@ -0,0 +1,141 @@
+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).malloc_atomic(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
+
+ @[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
@@ -0,0 +1,14 @@
+# :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
@@ -0,0 +1,29 @@
+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
@@ -0,0 +1,104 @@
+# :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
@@ -0,0 +1,226 @@
+# :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
@@ -0,0 +1,9 @@
+# :nodoc:
+class Markable
+ def write_barrier(&block)
+ yield
+ end
+
+ def mark(&block : Void* ->)
+ end
+end
@@ -0,0 +1,90 @@
+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
@@ -0,0 +1,33 @@
+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
@@ -0,0 +1,49 @@
+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
+end
@@ -0,0 +1,26 @@
+# :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
@@ -0,0 +1,19 @@
+# :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
@@ -0,0 +1,14 @@
+# :nodoc:
+class Reference
+ def ==(other : self)
+ same?(other)
+ end
+
+ def ==(other)
+ false
+ end
+
+ def same?(other : Reference)
+ object_id == other.object_id
+ end
+end
@@ -0,0 +1,68 @@
+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
@@ -0,0 +1,41 @@
+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
@@ -0,0 +1,49 @@
+# :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
@@ -0,0 +1,282 @@
+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(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 to_s
+ self
+ end
+
+ def to_s(io)
+ each_byte do |char|
+ io.putc char
+ end
+ end
+end
@@ -0,0 +1,44 @@
+# :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
@@ -0,0 +1,423 @@
+# 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 %}
@@ -0,0 +1,20 @@
+fun print_string(str : UInt8*)
+end
+
+def puts(str : String)
+ _str : Pointer(UInt8) = str.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 = 0x07_u8
+ i += 1
+ end
+end
+
+# Example usage with a static NUL-terminated string
+message = "[CrystalOS Kernel] Hello from C++\0"
+puts(message)
@@ -1 +0,0 @@
-int main() { return 0; }
@@ -1,2 +0,0 @@
-fun kmain
-end