; In a main module, define: FUNCTION_RANGE_CHECKING_ON = 1 ;Turn off if functions are okay FUNCTION_LOCAL_SIZE = $0A ;# bytes for local zero page shared FUNCTION_PARAMS_SIZE = $06 ;bytes for paramaters ; these two must be the first thing reserved in zeropage .segment "ZEROPAGE" function_locals: .res FUNCTION_LOCAL_SIZE param: .res FUNCTION_PARAMS_SIZEAnywhere else or in an included file define your callable functions as:
.func get_nametableaddress .locals nametableaddress .byte .endlocals ; IN: reg x has nametable x, reg y has nametable y ; OUT : reg x has low address, reg y has high address ; LOCAL: Uses 1 byte tya asl asl asl asl asl stx local::nametableaddress ora local::nametableaddress tax tya lsr lsr lsr ora #$20 tay rts .endfuncIf you wish to define a function as above inside a scope and export/import it:
;export: .exportfunc get_nametableaddress ;import: .importfunc get_nametableaddressIn code, you can use call:
call get_nametableaddress, #10,#10The macro will generate a small amount of code that checks the local variable usage and parameter usage. In this example no paramater memory is used because the macros justs loads reg x and y. There is no stack! Only a small amount of code will be output that checks for overuse or nested use of paramaters and local memory use and outputs a warning, so you can look closer and see if there is a problem or not. If you do pass paramaters in paramater memory space use clear_param num, where num is the number of paramaters acknolwedged as read/no longer needed:
.func some_function ; IN reg X,Y (addressIN) and a, param+0 (addressout) .locals addressIN .word addressOUT .word .endlocals stx local::addressIN sty local::addressIN + 1 sta local::addressOUT lda param+0 clear_param 1 sta local::addressOUT + 1 ;.....If you look at the very first line of code:
FUNCTION_RANGE_CHECKING_ON = 1 ;Turn off if functions are okayIf you comment that line, no extra code will be generated and your assembled code will be the same as if you did not use any of these macros.
Code:
; Define this somewhere in your main module near the begining:
; FUNCTION_RANGE_CHECKING_ON = 1
; FUNCTION_LOCAL_SIZE = $08
; FUNCTION_PARAMS_SIZE = $08
;.segment "ZEROPAGE"
;function_locals: .res FUNCTION_LOCAL_SIZE
;param: .res FUNCTION_PARAMS_SIZE
.feature leading_dot_in_identifiers
.if ::FUNCTION_RANGE_CHECKING_ON
.pushseg
.segment "BSS"
locals_used: .res 1
params_used: .res 1
.popseg
.endif
.macro .exportfunc func1, func2, func3, func4, func5, func6, func7, func8, func9, func10
.export func1
.export .ident(.sprintf("%s%s",.string(func1),"___sizeof_locals" ))
.ifnblank func2
.exportfunc func2, func3, func4, func5, func6, func7, func8, func9, func10
.endif
.endmacro
.macro .importfunc func1, func2, func3, func4, func5, func6, func7, func8, func9, func10
.import func1
.import .ident(.sprintf("%s%s",.string(func1),"___sizeof_locals" ))
.ifnblank func2
.importfunc func2, func3, func4, func5, func6, func7, func8, func9, func10
.endif
.endmacro
.macro .func name
.ifdef _name_
.undefine _name_
.endif
.define _name_ name
.proc _name_
.endmacro
.macro .locals
_locals_ .set 1
.struct local
.endmacro
.macro .endlocals
.endstruct
.endmacro
.macro .endfunc
.endproc
.ifdef _name_::_locals_
.ident(.concat(.string(_name_),"___sizeof_locals" )) = .sizeof(_name_::local)
.else
.ident(.concat(.string(_name_),"___sizeof_locals" )) = 0
.endif
.endmacro
.macro call function, paramX, paramY, paramA, param0, param1, param2, param3
; use in order: reg.x, reg.y, reg.a, param zeropage, ....etc
.if .xmatch(paramA,a)
pha
.endif
.if ::FUNCTION_RANGE_CHECKING_ON
.ifdef ::.ident(.concat(.string(function),"___sizeof_locals" ))
lda locals_used
if not zero
debugOut { "WARNING: Nested local variable usage calling: ", .string(function) }
endif
lda # ::.ident(.concat(.string(function), "___sizeof_locals"))
clc
adc locals_used
sta locals_used
cmp #FUNCTION_LOCAL_SIZE
if greater
debugOut { "WARNING: Function: '", .string(function), "' local memory exceeded: ", fHex8 { locals_used } }
endif
.endif
.if .paramcount > 4
lda params_used
if not zero
debugOut { "WARNING: Nested paramater usage: ", .string(function) }
endif
lda #<( .paramcount - 4)
clc
adc params_used
sta params_used
cmp #FUNCTION_PARAMS_SIZE
if greater
debugOut { "WARNING: Function: '", .string(function), "' parameter memory exceeded: ", fHex8 { params_used } }
endif
.endif
.endif
.ifnblank param3
lda param3
sta param+3
.endif
.ifnblank param2
lda param2
sta param+2
.endif
.ifnblank param1
lda param1
sta param+1
.endif
.ifnblank param0
lda param0
sta param+0
.endif
.ifnblank paramA
.if .xmatch (paramA,a)
pla
.else
lda paramA
.endif
.endif
.ifnblank paramY
.if .not .xmatch (paramY,y)
ldy paramY
.endif
.endif
.ifnblank paramX ; reg.x
.if .not .xmatch(paramX,x)
ldx paramX
.endif
.endif
jsr function
.if ::FUNCTION_RANGE_CHECKING_ON .and .defined(::.ident(.concat(.string(function), "___sizeof_locals")))
lda locals_used
sec
sbc # ::.ident(.concat(.string(function), "___sizeof_locals"))
sta locals_used
.endif
.endmacro
.macro clear_param num ; clear a vlaue from the param count
.if ::FUNCTION_RANGE_CHECKING_ON
.repeat num
dec params_used ; if range checking on, dec params
.endrepeat
.endif
.endmacro