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 ./...`
|
- Test with verbose output: `go test -v ./...`
|
||||||
|
|
||||||
#### Rebuilding c65gm:
|
#### 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
|
```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
|
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)
|
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 ./...`
|
4. Ask the user to run tests: `go test ./...`
|
||||||
5. Test the changes with example .c65 files
|
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
|
// Author: Mattias Hansson
|
||||||
|
|
@ -11,10 +11,13 @@
|
||||||
// Target: generic 6502
|
// Target: generic 6502
|
||||||
//
|
//
|
||||||
// Purpose: Dynamic memory allocation using free list
|
// Purpose: Dynamic memory allocation using free list
|
||||||
|
// with heap defragmentation support
|
||||||
//
|
//
|
||||||
// Modernized with:
|
// Modernized with:
|
||||||
// - Free list algorithm
|
// - Free list algorithm
|
||||||
// - ZP pointer for array-style access
|
// - ZP pointer for array-style access
|
||||||
|
// - Heap defragmentation (coalesces adjacent free blocks)
|
||||||
|
// - Free list statistics
|
||||||
//-----------------------------------------------------------
|
//-----------------------------------------------------------
|
||||||
|
|
||||||
//-----------------------------------------------------------
|
//-----------------------------------------------------------
|
||||||
|
|
@ -31,6 +34,13 @@
|
||||||
//
|
//
|
||||||
// ZP usage (can be overridden):
|
// ZP usage (can be overridden):
|
||||||
// - $fa-$fb: current block pointer
|
// - $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
|
#IFNDEF __LIB_MEM
|
||||||
|
|
@ -166,6 +176,128 @@ FUNC lib_mem_free({WORD m_ptr})
|
||||||
|
|
||||||
FEND
|
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
|
LABEL lib_memlib_skip
|
||||||
|
|
||||||
#PRAGMA _P_USE_LONG_JUMP 0
|
#PRAGMA _P_USE_LONG_JUMP 0
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue