This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

Macros that define visible labels in ca65 and ASM6

Macros that define visible labels in ca65 and ASM6
by on (#236869)
In ASM6 README.txt, loopy wrote:
Labels defined inside macros are local (visible only to that macro).

Several of my ca65 projects have relied on the ability of macros to define global labels in order to allow a macro to assign a name to which other data tables can refer. For example:

Code:
; ca65 code
.macro entry nameofthing, part1, part2, part3
  nameofthing = (* - tablestart) / 4
  .word part1
  .byte part2, part3
.endmacro

tablestart:
hello THING_ONE, $1111,$01,$01
hello THING_TWO, $2222,$02,$02
hello THING_THREE, $3333,$03,$03

; becomes this:
tablestart:
THING_ONE = (* - tablestart) / 4  ; that is, 0
.word $1111
.byte $01,$01
THING_TWO = (* - tablestart) / 4  ; that is, 1
.word $2222
.byte $02,$02
THING_THREE = (* - tablestart) / 4  ; that is, 2
.word $3333
.byte $03,$03


But now I'm being asked to port one of my libraries to ASM6, where each macro definition behaves as if its body were wrapped in a ca65 .scope.
Code:
; ASM6 code
macro entry nameofthing, part1, part2, part3
  nameofthing = ($ - tablestart) / 4
  word part1
  byte part2, part3
endmacro

tablestart:
hello THING_ONE, $1111,$01,$01
hello THING_TWO, $2222,$02,$02
hello THING_THREE, $3333,$03,$03

; becomes this, where the rept 1 blocks make labels defined within
; invisible to the rest of the program
tablestart:
rept 1
THING_ONE = ($ - tablestart) / 4  ; that is, 0
word $1111
byte $01,$01
endr  ; symbol THING_ONE unusable outside
rept 1
THING_TWO = ($ - tablestart) / 4  ; that is, 1
word $2222
byte $02,$02
endr  ; symbol THING_TWO unusable outside
rept 1
THING_THREE = ($ - tablestart) / 4  ; that is, 2
word $3333
byte $03,$03
endr  ; symbol THING_THREE unusable outside


What's the corresponding idiom for generating symbols for indices into a data table in ASM6? In theory, I could require users of the library to include boilerplate before each invocation of the macro to move the symbol definition out of the macro, but I imagine that would increase the opportunity for mistakes when a particular invocation fails to get copied.
Code:
; ASM6 code
macro entry nameofthing, part1, part2, part3
  word part1
  byte part2, part3
endmacro

tablestart:
THING_ONE = ($ - tablestart) / 4
hello THING_ONE, $1111,$01,$01
THING_TWO = ($ - tablestart) / 4
hello THING_TWO, $2222,$02,$02
THING_THREE = ($ - tablestart) / 4
hello THING_THREE, $3333,$03,$03

Or do I need to implement my own preprocessor for this sort of thing?
Re: Macros that define visible labels in ca65 and ASM6
by on (#236874)
Just tested here, but if you define the symbols you wish to use in the macro before the macro is run, changes made to that symbol with = will apply to the outside definition of that symbol, rather than being locals.

According to the ASM6 readme documentation symbols created with '=' can be reused. and in their own examples, under REPT, they use a loop counter that is defined outside the REPT / ENDR block, by going i=i+1 inside the loop:
Code:
i=0
REPT 256
    DB i
    i=i+1
ENDR


Their scope rules are pretty weird heh. Apparently ASM6 will define local names only if it doesn't shadow a global of the same name that was defined before the macro was expanded. And if you absolutely want a local constant or label, you should prefix it with @ (in this case, it sounds like you want to use the macro to provide a value to an existing global though).

So I think what you want to do is define your symbols to some don't-care value like 0 before to force symbol existence, and then if the macro reuses that name, it will replace its value.

Here's an example that will error after the macro is expanded 3 times:

Code:
counter=0
MACRO TEST
    counter=counter+1
    IF counter>=3
        ERROR Got here!
    ENDIF
ENDM

TEST
TEST
TEST

Re: Macros that define visible labels in ca65 and ASM6
by on (#236878)
Overkill wrote:
So I think what you want to do is define your symbols to some don't-care value like 0 before to force symbol existence

Doing this for all relevant macros would require running a preprocessor not only on the code (to translate it once from ca65 to ASM6 syntax) but also on the data (to scan for symbols that need to be defined to a don't-care value beforehand). I'm just not certain that the sort of people who prefer ASM6 over ca65 would find the preprocessing requirement acceptable. They might accept it for the code in order to download an already-preprocessed ASM6 release, but requiring users of the library to install a programming language interpreter or compiler might be beyond them.

On the other hand, because the project includes a script that translates an MML-like dialect to bytecode for the audio driver, the user will probably already have the interpreter installed to run that script.
Re: Macros that define visible labels in ca65 and ASM6
by on (#236886)
If you did have a preprocessor solution, if you just distribute both the produced CC65 and ASM versions, nobody but you would have to run a preprocessor (unless they want to directly collaborate on the source code with you).

I feel like that might be a significantly easier (and less obfuscated) solution than trying to write dual-assembler code directly.
Re: Macros that define visible labels in ca65 and ASM6
by on (#236887)
I had already planned on preprocessing the driver code for release. But the user is still going to need to install some programming language interpreter for two things:

A. To preprocess the data files that the dev creates
B. To read the config file and optimally lay out RAM based on the features of the code that the project is and isn't using