//-----------------------------------------------------------
//
//                Memory library ( Malloc, Free )
//
//
// Author: Mattias Hansson
// Copyright (c) : 2000-2025 Mattias Hansson
// License: GNU LGPL 2
// Language: 65CM v0.4+
// Dependencies:
// Target: generic 6502
//
// Purpose: Dynamic memory allocation using free list
//
// Modernized with:
// - Free list algorithm
// - ZP pointer for array-style access
//-----------------------------------------------------------

//-----------------------------------------------------------
// Internals:
//
// Free list memory allocator.
//
// Block structure:
// - Free blocks: [size:WORD][next:WORD][...free space...]
// - Allocated blocks: [size:WORD][...user data...]
//
// User receives pointer to data area (block + 2).
// Size field includes 2-byte header.
//
// ZP usage (can be overridden):
// - $fa-$fb: current block pointer
//-----------------------------------------------------------

#IFNDEF __LIB_MEM
#DEFINE __LIB_MEM = 1

#PRAGMA _P_USE_LONG_JUMP 1

#IFNDEF __LIB_MEM_START
#DEFINE __LIB_MEM_START = $$5000
#IFEND

#IFNDEF __LIB_MEM_END
#DEFINE __LIB_MEM_END = $$9fff
#IFEND

#IFNDEF __LIB_MEM_ZP_CURRENT
#DEFINE __LIB_MEM_ZP_CURRENT = $$fa
#IFEND

#IFNDEF NULL
#DEFINE NULL = 0
#IFEND

GOTO lib_memlib_skip


WORD lib_memlib_current
WORD lib_memlib_free_head

//-----------------------------------------------------------
// Init: Initialize heap with one large free block
//-----------------------------------------------------------
FUNC lib_mem_init
  WORD current @ __LIB_MEM_ZP_CURRENT
  WORD CONST memstart = __LIB_MEM_START
  WORD CONST memend = __LIB_MEM_END
  
  lib_memlib_free_head = memstart
  lib_memlib_current = memstart
  current = lib_memlib_current 
  
  WORD CONST total_size = memend-memstart+1

  POKEW current[0] , total_size
  POKEW current[2] , NULL
FEND

//-----------------------------------------------------------
// Malloc: Allocate memory block
// Returns pointer to user data area or NULL if no space
//-----------------------------------------------------------
FUNC lib_mem_malloc({WORD size_t} out:{WORD result})
  WORD current @ __LIB_MEM_ZP_CURRENT
  WORD prev
  WORD block_size
  WORD needed_size
  WORD remaining
  WORD next_free
  WORD split_addr
  WORD alloc_addr
  
  result = NULL
  
  IF size_t = 0
    EXIT
  ENDIF
  
  needed_size = size_t + 2
  
  current = lib_memlib_free_head
  prev = NULL
  
  WHILE current != NULL
    block_size = PEEKW current[0]
    
    IF block_size >= needed_size
      next_free = PEEKW current[2]
      alloc_addr = current
      
      remaining = block_size - needed_size
      
      IF remaining >= 6
        POKEW current[0] , needed_size
        
        split_addr = current + needed_size
        current = split_addr
        POKEW current[0] , remaining
        POKEW current[2] , next_free
        
        IF prev = NULL
          lib_memlib_free_head = split_addr
        ELSE
          current = prev
          POKEW current[2] , split_addr
        ENDIF
      ELSE
        IF prev = NULL
          lib_memlib_free_head = next_free
        ELSE
          current = prev
          POKEW current[2] , next_free
        ENDIF
      ENDIF
      
      result = alloc_addr + 2
      EXIT
    ENDIF
    
    prev = current
    WORD temp
    temp = PEEKW current[2]
    current = temp
  WEND
  
FEND

//-----------------------------------------------------------
// Free: Release allocated memory block
//-----------------------------------------------------------
FUNC lib_mem_free({WORD m_ptr})
  WORD current @ __LIB_MEM_ZP_CURRENT
  WORD block_start
  
  IF m_ptr = NULL
    EXIT
  ENDIF
  
  block_start = m_ptr - 2
  
  current = block_start
  POKEW current[2] , lib_memlib_free_head
  lib_memlib_free_head = block_start
  
FEND

LABEL lib_memlib_skip

#PRAGMA _P_USE_LONG_JUMP 0

#IFEND
