.cr
Crystal
(text/x-crystal)
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