From 8f947f714923c3975ad3b4bcfe559b26719ab31d Mon Sep 17 00:00:00 2001 From: Mattias Hansson Date: Fri, 21 Nov 2025 16:27:57 +0100 Subject: [PATCH] Fixed func calls with string literal arguments and $-replacements in #DEFINE. memlib.c65 is still borked. --- examples/memlib_demo/cm.sh | 20 ++++ examples/memlib_demo/memlib_demo.c65 | 146 ++++++++++++++++++++++++++ examples/memlib_demo/start_in_vice.sh | 1 + internal/compiler/funchandler.go | 6 +- internal/preproc/definelist.go | 48 ++++++++- internal/preproc/preproc_test.go | 44 +++++++- lib/cbmiolib.c65 | 8 ++ lib/memlib.c65 | 33 +++--- 8 files changed, 286 insertions(+), 20 deletions(-) create mode 100755 examples/memlib_demo/cm.sh create mode 100644 examples/memlib_demo/memlib_demo.c65 create mode 100644 examples/memlib_demo/start_in_vice.sh diff --git a/examples/memlib_demo/cm.sh b/examples/memlib_demo/cm.sh new file mode 100755 index 0000000..b054420 --- /dev/null +++ b/examples/memlib_demo/cm.sh @@ -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 diff --git a/examples/memlib_demo/memlib_demo.c65 b/examples/memlib_demo/memlib_demo.c65 new file mode 100644 index 0000000..1ad34e3 --- /dev/null +++ b/examples/memlib_demo/memlib_demo.c65 @@ -0,0 +1,146 @@ +//----------------------------------------------------------- +// Memory Library Demo +// Tests malloc and free functionality +//----------------------------------------------------------- + +#INCLUDE +#INCLUDE +#INCLUDE +#INCLUDE + +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 diff --git a/examples/memlib_demo/start_in_vice.sh b/examples/memlib_demo/start_in_vice.sh new file mode 100644 index 0000000..865c48e --- /dev/null +++ b/examples/memlib_demo/start_in_vice.sh @@ -0,0 +1 @@ +x64 -autostartprgmode 1 main.prg diff --git a/internal/compiler/funchandler.go b/internal/compiler/funchandler.go index 40b935a..6d0b218 100644 --- a/internal/compiler/funchandler.go +++ b/internal/compiler/funchandler.go @@ -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()), ) diff --git a/internal/preproc/definelist.go b/internal/preproc/definelist.go index d374cf4..4978fc7 100644 --- a/internal/preproc/definelist.go +++ b/internal/preproc/definelist.go @@ -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() +} diff --git a/internal/preproc/preproc_test.go b/internal/preproc/preproc_test.go index 8fb17e6..e94a908 100644 --- a/internal/preproc/preproc_test.go +++ b/internal/preproc/preproc_test.go @@ -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) + } +} diff --git a/lib/cbmiolib.c65 b/lib/cbmiolib.c65 index 3a3c710..8aa4379 100644 --- a/lib/cbmiolib.c65 +++ b/lib/cbmiolib.c65 @@ -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 diff --git a/lib/memlib.c65 b/lib/memlib.c65 index 8f58cae..d2de080 100644 --- a/lib/memlib.c65 +++ b/lib/memlib.c65 @@ -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