Fixed func calls with string literal arguments and $-replacements in #DEFINE. memlib.c65 is still borked.

This commit is contained in:
Mattias Hansson 2025-11-21 16:27:57 +01:00
parent 312f724971
commit 8f947f7149
8 changed files with 286 additions and 20 deletions

20
examples/memlib_demo/cm.sh Executable file
View file

@ -0,0 +1,20 @@
#!/bin/sh
# Define filename as variable
PROGNAME="memlib_demo"
# Only set C65LIBPATH if not already defined
if [ -z "$C65LIBPATH" ]; then
export C65LIBPATH=$(readlink -f "../../lib")
fi
# Compile
c65gm -in ${PROGNAME}.c65 -out ${PROGNAME}.s
if [ $? -ne 0 ]; then
echo "Compilation terminated"
exit 1
fi
echo assemble.
acme ${PROGNAME}.s
if [ -f ${PROGNAME}.prg ]; then
rm ${PROGNAME}.prg
fi
# main.bin ${PROGNAME}.prg
mv main.bin main.prg

View file

@ -0,0 +1,146 @@
//-----------------------------------------------------------
// Memory Library Demo
// Tests malloc and free functionality
//-----------------------------------------------------------
#INCLUDE <c64start.c65>
#INCLUDE <c64defs.c65>
#INCLUDE <memlib.c65>
#INCLUDE <cbmiolib.c65>
GOTO start
WORD ptr1
WORD ptr2
WORD ptr3
WORD ptr4
//-----------------------------------------------------------
// Wait for key
//-----------------------------------------------------------
FUNC wait_key
BYTE key
// Wait for no key
WHILE 1
key = PEEK $c5
IF key = 64
BREAK
ENDIF
WEND
// Wait for key press
WHILE 1
key = PEEK $c5
IF key != 64
BREAK
ENDIF
WEND
// Reset key buffer
POKE $c5 WITH 64
FEND
//-----------------------------------------------------------
// Main demo
//-----------------------------------------------------------
FUNC main
#PRAGMA _P_USE_CBM_STRINGS 1
lib_cbmio_cls()
lib_cbmio_printlf("memlib demo")
lib_cbmio_printlf("===========")
lib_cbmio_lf()
lib_cbmio_print("initializing...")
lib_mem_init()
lib_cbmio_printlf("ready")
lib_cbmio_lf()
// Test 1: Allocate 4 blocks
lib_cbmio_printlf("test 1: alloc 4 blocks")
lib_cbmio_printlf("----------------------")
lib_mem_malloc(100, ptr1)
lib_cbmio_print(" ptr1 (100 bytes): $")
lib_cbmio_hexoutw(ptr1)
lib_cbmio_lf()
lib_mem_malloc(200, ptr2)
lib_cbmio_print(" ptr2 (200 bytes): $")
lib_cbmio_hexoutw(ptr2)
lib_cbmio_lf()
lib_mem_malloc(150, ptr3)
lib_cbmio_print(" ptr3 (150 bytes): $")
lib_cbmio_hexoutw(ptr3)
lib_cbmio_lf()
lib_mem_malloc(50, ptr4)
lib_cbmio_print(" ptr4 ( 50 bytes): $")
lib_cbmio_hexoutw(ptr4)
lib_cbmio_lf()
lib_cbmio_lf()
lib_cbmio_print("press any key...")
wait_key()
lib_cbmio_lf()
lib_cbmio_lf()
// Test 2: Free middle blocks
lib_cbmio_printlf("test 2: free ptr2 & ptr3")
lib_cbmio_printlf("------------------------")
lib_mem_free(ptr2)
lib_cbmio_printlf(" freed ptr2")
lib_mem_free(ptr3)
lib_cbmio_printlf(" freed ptr3")
lib_cbmio_lf()
lib_cbmio_print("press any key...")
wait_key()
lib_cbmio_lf()
lib_cbmio_lf()
// Test 3: Reallocate in freed space
lib_cbmio_printlf("test 3: realloc in gap")
lib_cbmio_printlf("----------------------")
lib_mem_malloc(75, ptr2)
lib_cbmio_print(" new ptr2 ( 75 bytes): $")
lib_cbmio_hexoutw(ptr2)
lib_cbmio_lf()
lib_mem_malloc(100, ptr3)
lib_cbmio_print(" new ptr3 (100 bytes): $")
lib_cbmio_hexoutw(ptr3)
lib_cbmio_lf()
lib_cbmio_lf()
lib_cbmio_print("press any key...")
wait_key()
lib_cbmio_lf()
lib_cbmio_lf()
// Test 4: Cleanup
lib_cbmio_printlf("test 4: free all")
lib_cbmio_printlf("----------------")
lib_mem_free(ptr1)
lib_mem_free(ptr2)
lib_mem_free(ptr3)
lib_mem_free(ptr4)
lib_cbmio_printlf(" all freed")
lib_cbmio_lf()
lib_cbmio_printlf("demo complete!")
FEND
LABEL start
main()
SUBEND

View file

@ -0,0 +1 @@
x64 -autostartprgmode 1 main.prg

View file

@ -374,12 +374,12 @@ func (fh *FunctionHandler) processStringArg(arg string, param *FuncParam, funcNa
// Generate label for string constant
labelName := fh.labelStack.Push()
fh.constStrHandler.AddConstStr(labelName, arg, true, pragmaSet)
actualLabel := fh.constStrHandler.AddConstStr(labelName, arg, true, pragmaSet)
*inAssigns = append(*inAssigns,
fmt.Sprintf(" lda #<%s", labelName),
fmt.Sprintf(" lda #<%s", actualLabel),
fmt.Sprintf(" sta %s", param.Symbol.FullName()),
fmt.Sprintf(" lda #>%s", labelName),
fmt.Sprintf(" lda #>%s", actualLabel),
fmt.Sprintf(" sta %s+1", param.Symbol.FullName()),
)

View file

@ -14,7 +14,9 @@ type DefineList struct {
func NewDefineList() *DefineList { return &DefineList{tree: radix.New()} }
// Add inserts or updates a define->value mapping.
func (d *DefineList) Add(define, value string) { d.tree.Insert(define, value) }
func (d *DefineList) Add(define, value string) {
d.tree.Insert(define, expandDollarEscapes(value))
}
// Delete removes a define. Returns true if it existed.
func (d *DefineList) Delete(define string) bool {
@ -55,3 +57,47 @@ func (d *DefineList) ReplaceDefines(s string) string {
}
return b.String()
}
func isHex(c byte) bool {
return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')
}
func hexToByte(hi, lo byte) byte {
return hexDigit(hi)<<4 | hexDigit(lo)
}
func hexDigit(c byte) byte {
if c >= '0' && c <= '9' {
return c - '0'
}
if c >= 'A' && c <= 'F' {
return c - 'A' + 10
}
if c >= 'a' && c <= 'f' {
return c - 'a' + 10
}
return 0
}
func expandDollarEscapes(s string) string {
var b strings.Builder
for i := 0; i < len(s); i++ {
if s[i] == '$' {
if i+1 < len(s) && s[i+1] == '$' {
// $$ -> $
b.WriteByte('$')
i++ // skip second $
} else if i+2 < len(s) && isHex(s[i+1]) && isHex(s[i+2]) {
// $XX -> character
val := hexToByte(s[i+1], s[i+2])
b.WriteByte(val)
i += 2 // skip hex digits
} else {
b.WriteByte('$')
}
} else {
b.WriteByte(s[i])
}
}
return b.String()
}

View file

@ -10,7 +10,7 @@ func TestPreProcess_BasicDefine(t *testing.T) {
"test.c65": {
"#DEFINE FOO = 42",
"LDA #FOO",
"STA $D020",
"STA $$D020",
},
}
reader := NewMockFileReader(files)
@ -35,7 +35,7 @@ func TestPreProcess_BasicDefine(t *testing.T) {
func TestPreProcess_DefineExpansion(t *testing.T) {
files := map[string][]string{
"test.c65": {
"#DEFINE BASE = $D000",
"#DEFINE BASE = $$$$D000",
"#DEFINE OFFSET = 32",
"#DEFINE ADDR = BASE+OFFSET",
"STA ADDR",
@ -946,3 +946,43 @@ func TestPreProcess_EmptyScriptBlock(t *testing.T) {
t.Errorf("expected 'NOP', got %q", lines[0].Text)
}
}
func TestPreProcess_DollarEscapeExpansion(t *testing.T) {
files := map[string][]string{
"test.c65": {
"LDA #$0A", // $0A -> newline (byte 10)
"STA $D020", // $$ -> literal $
"JMP $5000", // $$ -> literal $
"#DEFINE ADDR $$C000", // $$ in define value
"LDX ADDR", // expands to $$C000 then to $C000
},
}
reader := NewMockFileReader(files)
lines, _, err := PreProcess("test.c65", reader)
if err != nil {
t.Fatalf("PreProcess failed: %v", err)
}
if len(lines) != 4 {
t.Fatalf("expected 4 lines, got %d", len(lines))
}
// $0A should become newline character (byte 10)
if lines[0].Text != "LDA #$0A" {
t.Errorf("line 0: expected 'LDA #\\n', got %q", lines[0].Text)
}
// $$ should become single $
if lines[1].Text != "STA $D020" {
t.Errorf("line 1: expected 'STA $D020', got %q", lines[1].Text)
}
if lines[2].Text != "JMP $5000" {
t.Errorf("line 2: expected 'JMP $5000', got %q", lines[2].Text)
}
// Define with $$ should expand to single $
if lines[3].Text != "LDX $C000" {
t.Errorf("line 3: expected 'LDX $C000', got %q", lines[3].Text)
}
}

View file

@ -59,9 +59,17 @@ FUNC lib_cbmio_home
FEND
FUNC lib_cbmio_cls
CALL lib_cbmio_print ( @lib_cbmio_tl_cls )
FEND
ASM
lib_cbmio_tl_home
!8 19, 0
lib_cbmio_tl_cls
!8 147, 0
lib_cbmio_tl_linefeed
!8 13, 0
lib_cbmio_text_nullstr

View file

@ -50,12 +50,14 @@
#IFNDEF __LIB_MEM
#DEFINE __LIB_MEM = 1
#PRAGMA _P_USE_LONG_JUMP 1
#IFNDEF __LIB_MEM_START
#DEFINE __LIB_MEM_START = $$5000
#DEFINE __LIB_MEM_START = $5000
#IFEND
#IFNDEF __LIB_MEM_END
#DEFINE __LIB_MEM_END = $$9fff
#DEFINE __LIB_MEM_END = $9fff
#IFEND
#IFNDEF NULL
@ -111,10 +113,10 @@ FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} )
//insert first record and recalc lib_alloctable_end
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspaceb
POKEW lib_alloctable_end , newspaceb
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspacee
POKEW lib_alloctable_end , newspacee
LET result GET newspaceb
@ -127,10 +129,11 @@ FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} )
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_memend
//SUBT lib_malloc_ptr - 3 -> lib_malloc_ptr
LET lib_malloc_ptr GET lib_alloctable_end
GETASWORD@ lib_malloc_ptr -> lib_malloc_spaceend
lib_malloc_spaceend = PEEKW lib_malloc_ptr
LET newspaceb GET NULL //if not found afterwards
@ -152,7 +155,7 @@ FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} )
DEC lib_malloc_ptr
DEC lib_malloc_ptr
GETASWORD@ lib_malloc_ptr -> lib_malloc_spacestart
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
@ -170,7 +173,7 @@ FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} )
DEC lib_malloc_ptr
DEC lib_malloc_ptr
GETASWORD@ lib_malloc_ptr -> lib_malloc_spaceend
lib_malloc_spaceend = PEEKW lib_malloc_ptr
WEND
@ -180,10 +183,10 @@ FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} )
//insert record and recalc lib_alloctable_end
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspaceb
POKEW lib_alloctable_end , newspaceb
DEC lib_alloctable_end
DEC lib_alloctable_end
PUTASWORD@ lib_alloctable_end VALUE newspacee
POKEW lib_alloctable_end , newspacee
//return new pointer
LET result GET newspaceb
@ -211,10 +214,10 @@ FUNC lib_mem_malloc ( {WORD size_t} out:{WORD result} )
//insert record and recalc lib_alloctable_end
DEC lib_malloc_ptr
PUTASWORD@ lib_malloc_ptr VALUE newspaceb
POKEW lib_malloc_ptr , newspaceb
DEC lib_malloc_ptr
DEC lib_malloc_ptr
PUTASWORD@ lib_malloc_ptr VALUE newspacee
POKEW lib_malloc_ptr , newspacee
LET lib_alloctable_end GET newtableend
//return new pointer
@ -255,7 +258,7 @@ FUNC lib_mem_free ( {WORD m_ptr} )
ADD 2 + lib_alloctable_end -> lastrecfirst
WHILE 1
GETASWORD@ ptr -> startaddr
startaddr = PEEKW ptr
IF startaddr = m_ptr THEN //found allocation!
//remove it!
@ -301,4 +304,6 @@ FEND
LABEL lib_memlib_skip
#PRAGMA _P_USE_LONG_JUMP 0
#IFEND