From 23a721217dd7732a17c25f5aba23c8b1d22b2f01 Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Sat, 22 Nov 2025 11:26:19 +0100 Subject: [PATCH] Fixed up memlib.c65 and added errors to POKEW and PEEKW if using the same variable for pointer and value. --- internal/commands/peekw.go | 5 + internal/commands/pokew.go | 4 + lib/memlib.c65 | 360 ++++++++++++------------------------- 3 files changed, 121 insertions(+), 248 deletions(-) diff --git a/internal/commands/peekw.go b/internal/commands/peekw.go index 228fdcc..5cd48c1 100644 --- a/internal/commands/peekw.go +++ b/internal/commands/peekw.go @@ -197,6 +197,11 @@ func (c *PeekWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex } c.destVarName = destSym.FullName() + // Check for self-referential PEEKW (reading through a pointer into itself) + if c.isZPPointer && c.addrVarName == c.destVarName { + return fmt.Errorf("PEEKW: cannot read through pointer %q into itself (would corrupt pointer during read)", c.destVarName) + } + return nil } diff --git a/internal/commands/pokew.go b/internal/commands/pokew.go index f21a0b5..04d9fc5 100644 --- a/internal/commands/pokew.go +++ b/internal/commands/pokew.go @@ -174,6 +174,10 @@ func (c *PokeWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex c.isValueVar = false } + // Check for self-referential POKEW (writing pointer through itself) + if c.isZPPointer && c.isValueVar && c.addrVarName == c.valueVarName { + return fmt.Errorf("POKEW: writing pointer %q through itself - pointer is both address and value", c.valueVarName) + } return nil } diff --git a/lib/memlib.c65 b/lib/memlib.c65 index 0dbbbe0..e608dad 100644 --- a/lib/memlib.c65 +++ b/lib/memlib.c65 @@ -4,48 +4,34 @@ // // // Author: Mattias Hansson -// Copyright (c) : 2000-2005 Mattias Hansson +// Copyright (c) : 2000-2025 Mattias Hansson // License: GNU LGPL 2 // Language: 65CM v0.4+ // Dependencies: // Target: generic 6502 // -// Purpose: Provide some functions the help to create, update and access -// a linked list of strings. +// Purpose: Dynamic memory allocation using free list +// +// Modernized with: +// - Free list algorithm +// - ZP pointer for array-style access //----------------------------------------------------------- - //----------------------------------------------------------- // Internals: // -// Makes heap-handling in a defined part of the memory. +// Free list memory allocator. // -// Requested memory is placed "from bottom upwards". -// Allocation entries are written from the top downwards. +// Block structure: +// - Free blocks: [size:WORD][next:WORD][...free space...] +// - Allocated blocks: [size:WORD][...user data...] // -// layout: +// User receives pointer to data area (block + 2). +// Size field includes 2-byte header. // -// Bottom ( e.g. $2000-$2fff ) -// -------------- -// $2000 -// . -// . area 1 allocated by application -// . -// $2020 -// $2021 -// . -// . area 2 allocated by application -// . -// $2025 -// -// . -// . -// . -// -// $2ff8 + $2ff9 = $2025 end of area 2 -// $2ffa + $2ffb = $2021 start of area 2 -// $2ffc + $2ffd = $2020 end of area 1 -// $2ffe + $2fff = $2000 start of area 1 +// ZP usage (can be overridden): +// - $fa-$fb: current block pointer +//----------------------------------------------------------- #IFNDEF __LIB_MEM #DEFINE __LIB_MEM = 1 @@ -60,246 +46,124 @@ #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 CONST lib_memstart = __LIB_MEM_START -WORD CONST lib_memend = __LIB_MEM_END -WORD lib_alloctable_end +WORD lib_memlib_current +WORD lib_memlib_free_head //----------------------------------------------------------- -// Init: sets upp things for the lib in mem before start +// 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 -//set allocation table end start value - LET lib_alloctable_end GET lib_memend - INC lib_alloctable_end - + POKEW current[0] , total_size + POKEW current[2] , NULL FEND //----------------------------------------------------------- -// Malloc: alloc memory of size size_t -// Address to allocated space is returned in result +// 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 maxsize //max possible size now in heap - WORD newspaceb - WORD newspacee - WORD newtableend - - DEC size_t // 1 = e.g. $2001-$2001 ( $2001 - $2001 = 0 ) - - LET newtableend GET lib_alloctable_end - SUBT newtableend - 4 -> newtableend - - IF lib_alloctable_end > lib_memend THEN // only when first item! - - SUBT newtableend - lib_memstart -> maxsize - IF maxsize < size_t THEN - //error handling: return NULL pointer! - LET result GET NULL - GOTO lib_malloc_exit - ENDIF - - LET newspaceb GET lib_memstart - ADD newspaceb + size_t -> newspacee - - //insert first record and recalc lib_alloctable_end - DEC lib_alloctable_end - DEC lib_alloctable_end - POKEW lib_alloctable_end , newspaceb - DEC lib_alloctable_end - DEC lib_alloctable_end - POKEW lib_alloctable_end , newspacee - - LET result GET newspaceb - - GOTO lib_malloc_exit; - +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 - //else if this is not the first item then: - - WORD lib_malloc_ptr - WORD lib_malloc_spaceend - WORD lib_malloc_spacestart - - //LET lib_malloc_ptr GET lib_memend - //SUBT lib_malloc_ptr - 3 -> lib_malloc_ptr - LET lib_malloc_ptr GET lib_alloctable_end - - lib_malloc_spaceend = PEEKW lib_malloc_ptr - - LET newspaceb GET NULL //if not found afterwards - - WHILE 1 - - //if we reached the last record in the alloc-table - IF lib_malloc_ptr = lib_alloctable_end THEN - //is there sufficient space left @ top of heap? - INC lib_malloc_spaceend - SUBT newtableend - lib_malloc_spaceend -> maxsize - - IF maxsize > size_t THEN - LET newspaceb GET lib_malloc_spaceend - ADD size_t + newspaceb -> newspacee - ENDIF - - BREAK - ENDIF - - DEC lib_malloc_ptr - DEC lib_malloc_ptr - lib_malloc_spacestart = PEEKW lib_malloc_ptr - SUBT lib_malloc_spacestart - lib_malloc_spaceend -> maxsize - DEC maxsize - //if we found a space big enough for the allocation - IF maxsize > size_t THEN - //Save the pointer to the new space end the pointer in the alloctable - LET newspaceb GET lib_malloc_spaceend - INC newspaceb // must start @ the next byte - ADD size_t + newspaceb -> newspacee - - INC lib_malloc_ptr // set it to start of record! ( for move ) -// INC lib_malloc_ptr - - BREAK - ENDIF - - DEC lib_malloc_ptr - DEC lib_malloc_ptr - lib_malloc_spaceend = PEEKW lib_malloc_ptr - - WEND - - IF newspaceb <> NULL THEN // allocate @ found space - IF lib_malloc_ptr = lib_alloctable_end THEN //allocate @ end of table - - //insert record and recalc lib_alloctable_end - DEC lib_alloctable_end - DEC lib_alloctable_end - POKEW lib_alloctable_end , newspaceb - DEC lib_alloctable_end - DEC lib_alloctable_end - POKEW lib_alloctable_end , newspacee - - //return new pointer - LET result GET newspaceb - - GOTO lib_malloc_exit - ENDIF - //else = insert into alloctable - WORD lib_malloc_from - WORD lib_malloc_to - BYTE lib_malloc_byte - - // move the rest of the stack - LET lib_malloc_from GET lib_alloctable_end - LET lib_malloc_to GET newtableend - - WHILE 1 - PEEK lib_malloc_from -> lib_malloc_byte - POKE lib_malloc_to WITH lib_malloc_byte - IF lib_malloc_from = lib_malloc_ptr THEN - BREAK - ENDIF - INC lib_malloc_from - INC lib_malloc_to - WEND - - //insert record and recalc lib_alloctable_end - DEC lib_malloc_ptr - POKEW lib_malloc_ptr , newspaceb - DEC lib_malloc_ptr - DEC lib_malloc_ptr - POKEW lib_malloc_ptr , newspacee - LET lib_alloctable_end GET newtableend - - //return new pointer - LET result GET newspaceb - - ENDIF - - IF newspaceb = NULL THEN // return null pointer - LET result GET NULL - GOTO lib_malloc_exit - ENDIF - -LABEL lib_malloc_exit - - INC size_t - -FEND - -//----------------------------------------------------------- -// Free: free alloced memory at m_ptr -//----------------------------------------------------------- - -FUNC lib_mem_free ( {WORD m_ptr} ) - WORD ptr - WORD startaddr - WORD lastrecfirst - WORD from - WORD to - BYTE tempb - - IF lib_alloctable_end > lib_memend THEN // no entries - GOTO lib_free_exit - ENDIF - - LET ptr GET lib_memend - DEC ptr - - ADD 2 + lib_alloctable_end -> lastrecfirst - - WHILE 1 - startaddr = PEEKW ptr - - IF startaddr = m_ptr THEN //found allocation! - //remove it! - IF ptr = lastrecfirst THEN - ADD 4 + lib_alloctable_end -> lib_alloctable_end - BREAK - ENDIF - - LET to GET ptr - INC to - SUBT to - 4 -> from - - WHILE 1 - PEEK from -> tempb - POKE to WITH tempb - IF from = lib_alloctable_end THEN - BREAK + + 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 - DEC from - DEC to - WEND - - ADD 4 + lib_alloctable_end -> lib_alloctable_end - - BREAK + 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 - - DEC ptr - DEC ptr - - IF ptr = lib_alloctable_end THEN - BREAK - ENDIF - - DEC ptr - DEC ptr - + + prev = current + WORD temp + temp = PEEKW current[2] + current = temp WEND + +FEND -LABEL lib_free_exit - +//----------------------------------------------------------- +// 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