Fixed up memlib.c65 and added errors to POKEW and PEEKW if using the same variable for pointer and value.

This commit is contained in:
Mattias Hansson 2025-11-22 11:26:19 +01:00
parent 4971da9978
commit 23a721217d
3 changed files with 121 additions and 248 deletions

View file

@ -197,6 +197,11 @@ func (c *PeekWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
} }
c.destVarName = destSym.FullName() 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 return nil
} }

View file

@ -174,6 +174,10 @@ func (c *PokeWCommand) Interpret(line preproc.Line, ctx *compiler.CompilerContex
c.isValueVar = false 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 return nil
} }

View file

@ -4,48 +4,34 @@
// //
// //
// Author: Mattias Hansson // Author: Mattias Hansson
// Copyright (c) : 2000-2005 Mattias Hansson // Copyright (c) : 2000-2025 Mattias Hansson
// License: GNU LGPL 2 // License: GNU LGPL 2
// Language: 65CM v0.4+ // Language: 65CM v0.4+
// Dependencies: // Dependencies:
// Target: generic 6502 // Target: generic 6502
// //
// Purpose: Provide some functions the help to create, update and access // Purpose: Dynamic memory allocation using free list
// a linked list of strings. //
// Modernized with:
// - Free list algorithm
// - ZP pointer for array-style access
//----------------------------------------------------------- //-----------------------------------------------------------
//----------------------------------------------------------- //-----------------------------------------------------------
// Internals: // Internals:
// //
// Makes heap-handling in a defined part of the memory. // Free list memory allocator.
// //
// Requested memory is placed "from bottom upwards". // Block structure:
// Allocation entries are written from the top downwards. // - 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 ) // ZP usage (can be overridden):
// -------------- // - $fa-$fb: current block pointer
// $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
#IFNDEF __LIB_MEM #IFNDEF __LIB_MEM
#DEFINE __LIB_MEM = 1 #DEFINE __LIB_MEM = 1
@ -60,246 +46,124 @@
#DEFINE __LIB_MEM_END = $$9fff #DEFINE __LIB_MEM_END = $$9fff
#IFEND #IFEND
#IFNDEF __LIB_MEM_ZP_CURRENT
#DEFINE __LIB_MEM_ZP_CURRENT = $$fa
#IFEND
#IFNDEF NULL #IFNDEF NULL
#DEFINE NULL = 0 #DEFINE NULL = 0
#IFEND #IFEND
GOTO lib_memlib_skip 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 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 POKEW current[0] , total_size
LET lib_alloctable_end GET lib_memend POKEW current[2] , NULL
INC lib_alloctable_end
FEND FEND
//----------------------------------------------------------- //-----------------------------------------------------------
// Malloc: alloc memory of size size_t // Malloc: Allocate memory block
// Address to allocated space is returned in result // Returns pointer to user data area or NULL if no space
//----------------------------------------------------------- //-----------------------------------------------------------
FUNC lib_mem_malloc({WORD size_t} out:{WORD result})
FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} ) WORD current @ __LIB_MEM_ZP_CURRENT
WORD maxsize //max possible size now in heap WORD prev
WORD newspaceb WORD block_size
WORD newspacee WORD needed_size
WORD newtableend WORD remaining
WORD next_free
DEC size_t // 1 = e.g. $2001-$2001 ( $2001 - $2001 = 0 ) WORD split_addr
WORD alloc_addr
LET newtableend GET lib_alloctable_end
SUBT newtableend - 4 -> newtableend result = NULL
IF lib_alloctable_end > lib_memend THEN // only when first item! IF size_t = 0
EXIT
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;
ENDIF ENDIF
//else if this is not the first item then:
needed_size = size_t + 2
WORD lib_malloc_ptr
WORD lib_malloc_spaceend current = lib_memlib_free_head
WORD lib_malloc_spacestart prev = NULL
//LET lib_malloc_ptr GET lib_memend WHILE current != NULL
//SUBT lib_malloc_ptr - 3 -> lib_malloc_ptr block_size = PEEKW current[0]
LET lib_malloc_ptr GET lib_alloctable_end
IF block_size >= needed_size
lib_malloc_spaceend = PEEKW lib_malloc_ptr next_free = PEEKW current[2]
alloc_addr = current
LET newspaceb GET NULL //if not found afterwards
remaining = block_size - needed_size
WHILE 1
IF remaining >= 6
//if we reached the last record in the alloc-table POKEW current[0] , needed_size
IF lib_malloc_ptr = lib_alloctable_end THEN
//is there sufficient space left @ top of heap? split_addr = current + needed_size
INC lib_malloc_spaceend current = split_addr
SUBT newtableend - lib_malloc_spaceend -> maxsize POKEW current[0] , remaining
POKEW current[2] , next_free
IF maxsize > size_t THEN
LET newspaceb GET lib_malloc_spaceend IF prev = NULL
ADD size_t + newspaceb -> newspacee lib_memlib_free_head = split_addr
ENDIF ELSE
current = prev
BREAK POKEW current[2] , split_addr
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
ENDIF ENDIF
DEC from ELSE
DEC to IF prev = NULL
WEND lib_memlib_free_head = next_free
ELSE
ADD 4 + lib_alloctable_end -> lib_alloctable_end current = prev
POKEW current[2] , next_free
BREAK ENDIF
ENDIF
result = alloc_addr + 2
EXIT
ENDIF ENDIF
DEC ptr prev = current
DEC ptr WORD temp
temp = PEEKW current[2]
IF ptr = lib_alloctable_end THEN current = temp
BREAK
ENDIF
DEC ptr
DEC ptr
WEND 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 FEND
LABEL lib_memlib_skip LABEL lib_memlib_skip