Fixed func calls with string literal arguments and $-replacements in #DEFINE. memlib.c65 is still borked.
This commit is contained in:
parent
312f724971
commit
8f947f7149
8 changed files with 286 additions and 20 deletions
20
examples/memlib_demo/cm.sh
Executable file
20
examples/memlib_demo/cm.sh
Executable 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
|
||||
146
examples/memlib_demo/memlib_demo.c65
Normal file
146
examples/memlib_demo/memlib_demo.c65
Normal 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
|
||||
1
examples/memlib_demo/start_in_vice.sh
Normal file
1
examples/memlib_demo/start_in_vice.sh
Normal file
|
|
@ -0,0 +1 @@
|
|||
x64 -autostartprgmode 1 main.prg
|
||||
|
|
@ -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()),
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in a new issue