fst_ptr <- 0
slot1 <- 4
header <- 8
off_oref <- -8
off_osize <- -4
off_next <- 0
off_size <- 4

// memlib - simple memory allocator for ILOC
//
// This library maintains a single-linked list (of monotonically increasing addresses) of free
// slots in memory and a reference count for occupied slots. Each slot contains a tuple `(next
// free slot address,size)` followed by `size` free bytes. The memory is initiliased with a
// pointer to the first free slot at `#fst_ptr` and one free slot at `#slot1` of size
// `brk-slot1-header`. When a piece of memory is requested for allocation, a tuple
// `(reference count, size)` followed by `size` bytes for the object is formed. The calling
// convention of the functions in this library consists of pushing the return address followed by
// any arguments (in order) to the stack. Any return values will be pushed to the stack likewise.
//
// Requires a register `brk` for the highest address available for heap allocation.
// Requires an instruction `halt` when memory runs out
//
// @author Frank Wibbelink

// initialise
memlib_: loadI    0 => m_0
         loadI    @slot1 => m_1
         subI     brk,@slot1 => m_2
         subI     m_2,@header => m_2
         storeAI  m_1 => m_0,@fst_ptr            // pointer to first slot
         storeAI  m_0 => m_1,@off_next           // next slot is null
         storeAI  m_2 => m_1,@off_size           // first slot size
         jumpI -> ememlib_


// allocate memory
// searches through the free slots for either a slot size of ~~exactly #4+8 or a size of~~ at
// least #4+16
// memory: [return address, object size] -> [object address]
memalloc: loadI    0 => m_0
          pop      => m_s                        // load size
          loadI    @fst_ptr => m_p               // load previous address (base pointer)
          loadAI   m_p,@off_next => m_c          // load current address (first slot)

ma_loop:  cmp_EQ   m_0,m_c => m_1                // check if address pointer is null
          cbr      m_1 -> ma_null,ma_cont        // if null, go to end and return null pointer
ma_cont:  loadAI   m_c,@off_size => m_1          // load slot size
          cmp_EQ   m_1,m_s => m_2                // check if request fits exactly
          cbr      m_2 -> ma_yxct,ma_nxct
ma_nxct:  subI     m_1,@header => m_1            // subtract free slot size
          cmp_GE   m_1,m_s => m_1                // check if request fits
          cbr      m_1 -> ma_found,ma_next
ma_next:  i2i      m_c => m_p
          loadAI   m_p,@off_next => m_c
          jumpI -> ma_loop

ma_yxct:  loadAI   m_c,@off_next => m_n          // location of next free slot
          jumpI -> ma_final

ma_found: addI     m_s,@header => m_1
          add      m_1,m_c => m_n                // location of new free slot
          loadAI   m_c,@off_size => m_1
          subI     m_1,@header => m_1
          sub      m_1,m_s => m_1                // size of new free slot
          storeAI  m_1 => m_n,@off_size
          loadAI   m_c,@off_next => m_1          // location of next free slot
          storeAI  m_1 => m_n,@off_next
ma_final: storeAI  m_n => m_p,@off_next          // link previous free slot to new
          addI     m_c,@header => m_c            // move to object location
          loadI    1 => m_1
          storeAI  m_1 => m_c,@off_oref          // set reference count to 1
          storeAI  m_s => m_c,@off_osize         // set object size
ma_null:  pop      => m_1                        // load return address
          push     m_c                           // store object address
          jump -> m_1



// increase reference count of object
// memory: [return address, object address] -> []
memaddref: loadI    0 => m_0
           pop      => m_n                       // load object address
           cmp_EQ   m_0,m_n => m_1
           cbr      m_1 -> mr_ynul,mr_nnul       // check if null pointer
mr_ynul:   haltI    1865445997
mr_nnul:   loadAI   m_n,@off_oref => m_1
           addI     m_1,1 => m_1
           storeAI  m_1 => m_n,@off_oref
           pop      => m_1                        // load return address
           jump -> m_1


// decrease reference count of object
// frees memory if count goes to zero
// memory: [return address, object address] -> []
memfree: loadI    0 => m_0
         pop      => m_n                         // load object address
         cmp_EQ   m_0,m_n => m_1
         cbr      m_1 -> mf_ynul,mf_nnul         // check if null pointer
mf_ynul: haltI    1865442925
mf_nnul: loadAI   m_n,@off_oref => m_1
         subI     m_1,1 => m_1
         cmp_GT   m_1,m_0 => m_1
         cbr      m_1 -> mf_exit,mf_free

mf_exit: storeAI  m_1 => m_n,@off_oref
         pop      => m_1                         // load return address
         jump -> m_1

mf_free: subI     m_n,@header => m_n
         loadI    @fst_ptr => m_p
         loadAI   m_p,@off_next => m_c

mf_loop: cmp_EQ   m_0,m_c => m_1                 // check if address pointer is null
         cbr      m_1 -> mf_halt,mf_cont
mf_halt: haltI    1882220141                     // halt program; object beyond last free slot (or memory corrupted)
mf_cont: cmp_EQ   m_c,m_n => m_1
         cbr      m_1 -> mf_hal2,mf_con2
mf_hal2: haltI    1717855853                     // halt program; object is free slot
mf_con2: cmp_LE   m_c,m_n => m_1
         cbr      m_1 -> mf_next, mf_done
mf_next: i2i      m_c => m_p
         loadAI   m_p,@off_next => m_c
         jumpI -> mf_loop

mf_done: loadAI   m_p,@off_size => m_1
         addI     m_1,@header => m_1
         add      m_1,m_p => m_2
         cmp_EQ   m_2,m_n => m_2
         cbr      m_2 -> mf_yprv,mf_nprv
mf_yprv: loadAI   m_n,@off_size => m_2           // merge with previous free slot
         add      m_1,m_2 => m_1                 // new size of previous free slot
         storeAI  m_1 => m_p,@off_size
         i2i      m_p => m_n
         jumpI -> mf_dprv
mf_nprv: storeAI  m_n => m_p,@off_next           // link previous free slot with new
mf_dprv: loadAI   m_n,@off_size => m_1
         addI     m_1,@header => m_1
         add      m_1,m_n => m_2
         cmp_EQ   m_2,m_c => m_2
         cbr      m_2 -> mf_ynxt,mf_nnxt
mf_ynxt: loadAI   m_c,@off_size => m_2           // merge with next free slot
         add      m_1,m_2 => m_1                 // new size of next free slot
         storeAI  m_1 => m_n,@off_size
         loadAI   m_c,@off_next => m_1
         storeAI  m_1 => m_n,@off_next           // move link of next's next to new free slot
         pop      => m_1                         // load return address
         jump -> m_1
mf_nnxt: storeAI  m_c => m_n,@off_next           // link new free slot with next
         pop      => m_1                         // load return address
         jump -> m_1


// copy object to location
// memory: [return address, object address, destination] -> []
memcopy: loadI    0 => m_0
         haltI    1835626101                     // unimplemented
         pop      => m_1                         // load return address
         jump -> m_1


ememlib_: nop
// end of memlib

