Add lib_mem_defrag and lib_mem_stats to memlib.c65 with demo
This commit is contained in:
parent
9720d00dcb
commit
a15d45a130
5 changed files with 340 additions and 5 deletions
10
AGENTS.md
10
AGENTS.md
|
|
@ -127,13 +127,15 @@ All file operations must be restricted to the project's source code and document
|
|||
- Test with verbose output: `go test -v ./...`
|
||||
|
||||
#### Rebuilding c65gm:
|
||||
When making changes to the compiler, ask the user to rebuild c65gm:
|
||||
When making changes to the compiler or library files, ask the user to rebuild c65gm using the build script:
|
||||
```bash
|
||||
go build -o c65gm
|
||||
./build_c65gm.sh
|
||||
```
|
||||
This creates a fresh binary with your changes. The agent should:
|
||||
The build script copies `lib/` into `internal/preproc/lib/` (for embedding into the binary) and then runs `go build -o c65gm`.
|
||||
|
||||
**IMPORTANT**: Library files (`lib/*.c65`) are embedded into the binary at build time. After modifying any `.c65` file in `lib/`, you MUST use `build_c65gm.sh` (not `go build` directly) to ensure the updated library is embedded. The agent should:
|
||||
1. Make code changes
|
||||
2. Ask the user to rebuild c65gm: `go build -o c65gm`
|
||||
2. Ask the user to rebuild c65gm: `./build_c65gm.sh`
|
||||
3. **Check file datetime**: Verify the c65gm binary was recently updated (use `ls -la c65gm` or similar)
|
||||
4. Ask the user to run tests: `go test ./...`
|
||||
5. Test the changes with example .c65 files
|
||||
|
|
|
|||
3
examples/memlib_demo2/cm.sh
Normal file
3
examples/memlib_demo2/cm.sh
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
#!/bin/sh
|
||||
PROGNAME="memlib_demo2"
|
||||
c65gm ${PROGNAME}.c65
|
||||
197
examples/memlib_demo2/memlib_demo2.c65
Normal file
197
examples/memlib_demo2/memlib_demo2.c65
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
//-----------------------------------------------------------
|
||||
// Memory Library Demo 2: Defrag & Stats
|
||||
// Demonstrates heap defragmentation and statistics
|
||||
//-----------------------------------------------------------
|
||||
|
||||
#PRAGMA _P_REMOVE_UNUSED 1
|
||||
|
||||
#INCLUDE <c64start.c65>
|
||||
#INCLUDE <c64defs.c65>
|
||||
#INCLUDE <memlib.c65>
|
||||
#INCLUDE <cbmiolib.c65>
|
||||
|
||||
#PRAGMA _P_USE_CBM_STRINGS 1
|
||||
|
||||
GOTO start
|
||||
|
||||
WORD keep1
|
||||
WORD keep2
|
||||
WORD ptr_a
|
||||
WORD ptr_b
|
||||
WORD ptr_c
|
||||
WORD ptr_d
|
||||
|
||||
WORD free_blocks
|
||||
WORD total_free
|
||||
WORD largest_free
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Wait for key
|
||||
//-----------------------------------------------------------
|
||||
FUNC wait_key
|
||||
BYTE key
|
||||
|
||||
WHILE 1
|
||||
key = PEEK $c5
|
||||
IF key = 64
|
||||
BREAK
|
||||
ENDIF
|
||||
WEND
|
||||
|
||||
WHILE 1
|
||||
key = PEEK $c5
|
||||
IF key != 64
|
||||
BREAK
|
||||
ENDIF
|
||||
WEND
|
||||
|
||||
POKE $c6 WITH 0
|
||||
FEND
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Print stats with label (hex output)
|
||||
//-----------------------------------------------------------
|
||||
FUNC print_stats
|
||||
lib_cbmio_printlf("stats:")
|
||||
|
||||
lib_mem_stats(free_blocks, total_free, largest_free)
|
||||
|
||||
lib_cbmio_print(" blocks: $")
|
||||
lib_cbmio_hexoutw(free_blocks)
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print(" free: $")
|
||||
lib_cbmio_hexoutw(total_free)
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print(" biggest:$")
|
||||
lib_cbmio_hexoutw(largest_free)
|
||||
lib_cbmio_lf()
|
||||
FEND
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Main demo
|
||||
//-----------------------------------------------------------
|
||||
FUNC main
|
||||
|
||||
lib_cbmio_cls()
|
||||
|
||||
lib_cbmio_printlf("memlib demo 2: defrag")
|
||||
lib_cbmio_printlf("======================")
|
||||
lib_cbmio_lf()
|
||||
|
||||
lib_cbmio_print("initializing...")
|
||||
lib_mem_init()
|
||||
lib_cbmio_printlf("ready")
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Show initial state
|
||||
lib_cbmio_printlf("after init:")
|
||||
print_stats()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print("press any key...")
|
||||
wait_key()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Allocate 6 blocks.
|
||||
// Keep keep1 and keep2 allocated to create three gaps:
|
||||
// heap: [keep1][free][keep2][free][free]
|
||||
lib_cbmio_printlf("allocating 6 blocks")
|
||||
lib_mem_malloc(20, keep1)
|
||||
lib_mem_malloc(30, ptr_a)
|
||||
lib_mem_malloc(40, keep2)
|
||||
lib_mem_malloc(50, ptr_b)
|
||||
lib_mem_malloc(20, ptr_c)
|
||||
lib_mem_malloc(60, ptr_d)
|
||||
|
||||
lib_cbmio_print("keep1: $")
|
||||
lib_cbmio_hexoutw(keep1)
|
||||
lib_cbmio_print(" ptr_a: $")
|
||||
lib_cbmio_hexoutw(ptr_a)
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print("keep2: $")
|
||||
lib_cbmio_hexoutw(keep2)
|
||||
lib_cbmio_print(" ptr_b: $")
|
||||
lib_cbmio_hexoutw(ptr_b)
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print("ptr_c: $")
|
||||
lib_cbmio_hexoutw(ptr_c)
|
||||
lib_cbmio_print(" ptr_d: $")
|
||||
lib_cbmio_hexoutw(ptr_d)
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_lf()
|
||||
|
||||
print_stats()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print("press any key...")
|
||||
wait_key()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Free ptr_a and ptr_b (non-adjacent, between keep1 and keep2, and after keep2)
|
||||
lib_cbmio_printlf("freeing ptr_a and ptr_b")
|
||||
lib_mem_free(ptr_a)
|
||||
lib_mem_free(ptr_b)
|
||||
lib_cbmio_printlf("freed")
|
||||
lib_cbmio_lf()
|
||||
|
||||
print_stats()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print("press any key...")
|
||||
wait_key()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Free ptr_c and ptr_d (adjacent to each other, after ptr_b's free block)
|
||||
// Now gaps are: [keep1][gap_a][keep2][gap_b][gap_c]
|
||||
// gap_a and gap_b are separated by keep2
|
||||
// gap_b and gap_c are adjacent -> should merge after freeing gap_c
|
||||
lib_cbmio_printlf("freeing ptr_c and ptr_d")
|
||||
lib_mem_free(ptr_c)
|
||||
lib_mem_free(ptr_d)
|
||||
lib_cbmio_printlf("freed")
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Show fragmented state (keep1 and keep2 still allocated)
|
||||
lib_cbmio_printlf("before defrag (keep1,keep2 allocated):")
|
||||
print_stats()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_print("press any key...")
|
||||
wait_key()
|
||||
lib_cbmio_lf()
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Defrag!
|
||||
lib_cbmio_print("running defrag...")
|
||||
lib_mem_defrag()
|
||||
lib_cbmio_printlf("done")
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Show defragged state:
|
||||
// After defrag: [keep1][merged_gap_a][keep2][merged_gap_b+gap_c]
|
||||
// Should be 2 free blocks (gap_a and gap_b+gap_c are separate, keep2 sits between)
|
||||
lib_cbmio_printlf("after defrag:")
|
||||
print_stats()
|
||||
lib_cbmio_lf()
|
||||
|
||||
// Now free keep1 and keep2 and defrag again to show full coalesce
|
||||
lib_cbmio_printlf("freeing keep1 and keep2")
|
||||
lib_mem_free(keep1)
|
||||
lib_mem_free(keep2)
|
||||
lib_cbmio_printlf("all freed")
|
||||
lib_cbmio_lf()
|
||||
|
||||
lib_cbmio_print("running defrag...")
|
||||
lib_mem_defrag()
|
||||
lib_cbmio_printlf("done")
|
||||
lib_cbmio_lf()
|
||||
|
||||
lib_cbmio_printlf("after full defrag:")
|
||||
print_stats()
|
||||
lib_cbmio_lf()
|
||||
|
||||
lib_cbmio_printlf("done!")
|
||||
FEND
|
||||
|
||||
LABEL start
|
||||
main()
|
||||
SUBEND
|
||||
1
examples/memlib_demo2/start_in_vice.sh
Normal file
1
examples/memlib_demo2/start_in_vice.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
x64 -autostartprgmode 1 memlib_demo2.prg
|
||||
134
lib/memlib.c65
134
lib/memlib.c65
|
|
@ -1,6 +1,6 @@
|
|||
//-----------------------------------------------------------
|
||||
//
|
||||
// Memory library ( Malloc, Free )
|
||||
// Memory library ( Malloc, Free, Defrag )
|
||||
//
|
||||
//
|
||||
// Author: Mattias Hansson
|
||||
|
|
@ -11,10 +11,13 @@
|
|||
// Target: generic 6502
|
||||
//
|
||||
// Purpose: Dynamic memory allocation using free list
|
||||
// with heap defragmentation support
|
||||
//
|
||||
// Modernized with:
|
||||
// - Free list algorithm
|
||||
// - ZP pointer for array-style access
|
||||
// - Heap defragmentation (coalesces adjacent free blocks)
|
||||
// - Free list statistics
|
||||
//-----------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------
|
||||
|
|
@ -31,6 +34,13 @@
|
|||
//
|
||||
// ZP usage (can be overridden):
|
||||
// - $fa-$fb: current block pointer
|
||||
//
|
||||
// Functions:
|
||||
// lib_mem_init - Initialize heap with one large free block
|
||||
// lib_mem_malloc - Allocate memory block
|
||||
// lib_mem_free - Release allocated memory block
|
||||
// lib_mem_defrag - Coalesce adjacent free blocks (maintenance)
|
||||
// lib_mem_stats - Return free list statistics
|
||||
//-----------------------------------------------------------
|
||||
|
||||
#IFNDEF __LIB_MEM
|
||||
|
|
@ -166,6 +176,128 @@ FUNC lib_mem_free({WORD m_ptr})
|
|||
|
||||
FEND
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Defrag: Walk heap and merge adjacent free blocks
|
||||
//-----------------------------------------------------------
|
||||
FUNC lib_mem_defrag
|
||||
WORD current @ __LIB_MEM_ZP_CURRENT
|
||||
WORD scan_addr
|
||||
WORD next_addr
|
||||
WORD block_size
|
||||
WORD next_size
|
||||
WORD prev_free
|
||||
WORD free_node
|
||||
WORD next_next
|
||||
|
||||
WORD CONST memstart = __LIB_MEM_START
|
||||
WORD CONST memend = __LIB_MEM_END
|
||||
|
||||
scan_addr = memstart
|
||||
|
||||
WHILE scan_addr < memend
|
||||
current = scan_addr
|
||||
block_size = PEEKW current[0]
|
||||
IF block_size = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
current = lib_memlib_free_head
|
||||
prev_free = NULL
|
||||
free_node = NULL
|
||||
|
||||
WHILE current != NULL
|
||||
IF current = scan_addr
|
||||
free_node = current
|
||||
BREAK
|
||||
ENDIF
|
||||
prev_free = current
|
||||
WORD _tmpx
|
||||
_tmpx = PEEKW current[2]
|
||||
current = _tmpx
|
||||
WEND
|
||||
|
||||
IF free_node != NULL
|
||||
next_addr = scan_addr + block_size
|
||||
|
||||
WHILE next_addr < memend
|
||||
current = next_addr
|
||||
next_size = PEEKW current[0]
|
||||
IF next_size = 0
|
||||
EXIT
|
||||
ENDIF
|
||||
|
||||
current = lib_memlib_free_head
|
||||
prev_free = NULL
|
||||
free_node = NULL
|
||||
|
||||
WHILE current != NULL
|
||||
IF current = next_addr
|
||||
free_node = current
|
||||
BREAK
|
||||
ENDIF
|
||||
prev_free = current
|
||||
_tmpx = PEEKW current[2]
|
||||
current = _tmpx
|
||||
WEND
|
||||
|
||||
IF free_node = NULL
|
||||
BREAK
|
||||
ENDIF
|
||||
|
||||
current = next_addr
|
||||
next_next = PEEKW current[2]
|
||||
|
||||
IF prev_free = NULL
|
||||
lib_memlib_free_head = next_next
|
||||
ELSE
|
||||
current = prev_free
|
||||
POKEW current[2] , next_next
|
||||
ENDIF
|
||||
|
||||
block_size = block_size + next_size
|
||||
current = scan_addr
|
||||
POKEW current[0] , block_size
|
||||
next_addr = scan_addr + block_size
|
||||
WEND
|
||||
ENDIF
|
||||
|
||||
current = scan_addr
|
||||
block_size = PEEKW current[0]
|
||||
scan_addr = scan_addr + block_size
|
||||
WEND
|
||||
FEND
|
||||
|
||||
//-----------------------------------------------------------
|
||||
// Stats: Return free list statistics
|
||||
// free_blocks - Number of entries in the free list
|
||||
// total_free - Total free bytes
|
||||
// largest_free - Size of largest free block
|
||||
//-----------------------------------------------------------
|
||||
FUNC lib_mem_stats(out:{WORD free_blocks} out:{WORD total_free} out:{WORD largest_free})
|
||||
WORD current @ __LIB_MEM_ZP_CURRENT
|
||||
WORD block_size
|
||||
|
||||
free_blocks = 0
|
||||
total_free = 0
|
||||
largest_free = 0
|
||||
|
||||
current = lib_memlib_free_head
|
||||
|
||||
WHILE current != NULL
|
||||
block_size = PEEKW current[0]
|
||||
free_blocks = free_blocks + 1
|
||||
total_free = total_free + block_size
|
||||
|
||||
IF block_size > largest_free
|
||||
largest_free = block_size
|
||||
ENDIF
|
||||
|
||||
WORD _tmpx
|
||||
_tmpx = PEEKW current[2]
|
||||
current = _tmpx
|
||||
WEND
|
||||
FEND
|
||||
|
||||
LABEL lib_memlib_skip
|
||||
|
||||
#PRAGMA _P_USE_LONG_JUMP 0
|
||||
|
|
|
|||
Loading…
Reference in a new issue