Page 1 of 2

"Incomplete expression." in ASM6 macro

Posted: Sat Mar 30, 2019 6:35 pm
by tepples
I'm building an audio driver in ASM6, and I'm up to where I build a table of sound effect addresses and lengths. But when I try to express lookup tables using macros, I get "Incomplete expression" errors.

When I assemble this source file and provide -L to expand macros in the listing file:

Code: Select all

macro sfxdef name, baseaddr, length, period, channel
name = <((* - sfx_table) / 4)
dw baseaddr
db (channel<<2)|((period - 1)<<4)
db length
endm

base $C000
sfx_table:
sfxdef SFX_foo, foo_data, 1, 1, 0
sfxdef SFX_bar, bar_data, 1, 1, 0

foo_data: db $89,$10
bar_data: db $84,$20
This listing is produced:

Code: Select all

                                macro sfxdef name, baseaddr, length, period, channel
                                name = <((* - sfx_table) / 4)
                                dw baseaddr
                                db (channel<<2)|((period - 1)<<4)
                                db length
                                endm
                                
                                base $C000
0C000                           sfx_table:
0C000                           sfxdef SFX_foo, foo_data, 1, 1, 0
0C000                           SFX_foo = <((* - sfx_table) / 4)
*** Incomplete expression.
0C000 08 C0                     dw foo_data
0C002 00                        db (0<<2)|((1 - 1)<<4)
0C003 01                        db 1
0C004                           sfxdef SFX_bar, bar_data, 1, 1, 0
0C004                           SFX_bar = <((* - sfx_table) / 4)
*** Incomplete expression.
0C004 0A C0                     dw bar_data
0C006 00                        db (0<<2)|((1 - 1)<<4)
0C007 01                        db 1
0C008                           
0C008 89 10                     foo_data: db $89,$10
0C00A 84 20                     bar_data: db $84,$20
What is causing these errors, and what should I understand in order to figure out how to fix them?

Re: "Incomplete expression." in ASM6 macro

Posted: Sat Mar 30, 2019 6:42 pm
by tokumaru
Isn't this because the * is being interpreted as the multiplication operator, since ASM6 uses $ for the PC?

Re: "Incomplete expression." in ASM6 macro

Posted: Sat Mar 30, 2019 6:48 pm
by tepples
Thanks for help. Now I have to figure out how to programmatically distinguish the two in the ca65 code I'm converting without having to hand-edit both copies every time I change something.

Re: "Incomplete expression." in ASM6 macro

Posted: Sat Mar 30, 2019 6:53 pm
by tokumaru
Well, you could use ca65's dollar_is_pc feature and start using $ all around...

Re: "Incomplete expression." in ASM6 macro

Posted: Sat Mar 30, 2019 7:09 pm
by tepples
A new question

Translating * to $ at the start or end of an expression or parenthesized subexpression fixes one layer of the above MCVE but changes the error message I get when I assemble the whole project. If I try to translate the ca65 idiom for an include guard to ASM6, it ends up not recognizing the macro at all, instead producing "Illegal instruction" but only for the first use of each such macro.

Source:

Code: Select all

ifndef INCLUDE_GUARD
INCLUDE_GUARD = 1

macro sfxdef name, baseaddr, length, period, channel
name = <(($ - sfx_table) / 4)
dw baseaddr
db (channel<<2)|((period - 1)<<4)
db length
endm

endif

base $C000
sfx_table:
sfxdef SFX_foo, foo_data, 1, 1, 0
sfxdef SFX_bar, bar_data, 1, 1, 0

foo_data: db $89,$10
bar_data: db $84,$20
Listing:

Code: Select all

                                ifndef INCLUDE_GUARD
                                INCLUDE_GUARD = 1
                                macro sfxdef name, baseaddr, length, period, channel
                                name = <(($ - sfx_table) / 4)
                                dw baseaddr
                                db (channel<<2)|((period - 1)<<4)
                                db length
                                endm
                                endif
                                
                                base $C000
0C000                           sfx_table:
0C000                           sfxdef SFX_foo, foo_data, 1, 1, 0
*** Illegal instruction.
0C000                           sfxdef SFX_bar, bar_data, 1, 1, 0
0C000                           SFX_bar = <(($ - sfx_table) / 4)
0C000 0A C0                     dw bar_data
0C002 00                        db (0<<2)|((1 - 1)<<4)
0C003 01                        db 1
0C004                           
0C004 89 10                     foo_data: db $89,$10
0C006 84 20                     bar_data: db $84,$20

Re: "Incomplete expression." in ASM6 macro

Posted: Mon Apr 01, 2019 1:50 pm
by tepples
The macro does different things (both wrong) on different invocations. The first time it says "Illegal instruction" and the second time it generates incorrect code. This makes me think I'm hitting an ASM6 bug. I decided to work around it in the translator by detecting whether a particular .ifndef is an include guard and changing it to a .if 1 if so.

Re: "Incomplete expression." in ASM6 macro

Posted: Mon Apr 01, 2019 6:04 pm
by unregistered
Maybe that "*** Illegal Instruction" is attempting to say that your dw baseaddr and db length are confusing for asm6 bc you haven't defined the values of baseaddr and length? :)

Re: "Incomplete expression." in ASM6 macro

Posted: Mon Apr 01, 2019 6:18 pm
by unregistered
unregistered wrote:Maybe that "*** Illegal Instruction" is attempting to say that your dw baseaddr and db length are confusing for asm6 bc you haven't defined the values of baseaddr and length? :)
Sorry tepples, I just noticed that you do define the values in your macro calls... maybe this error is created bc you db length before you call the macro? I think tokumaru taught me that the macro has to be defined before it can be called... maybe the same logic applies to whatever baseaddr and length are. :)

Re: "Incomplete expression." in ASM6 macro

Posted: Mon Apr 01, 2019 6:19 pm
by tokumaru
Sounds like a bug indeed.

Re: "Incomplete expression." in ASM6 macro

Posted: Tue Apr 02, 2019 5:48 am
by tepples
In any case, I've reduced another test case.

It seems like most actual bugs in ASM6 disappear once a forward reference is made no longer forward.

Re: "Incomplete expression." in ASM6 macro

Posted: Tue Apr 09, 2019 11:36 am
by doppelganger
Are you trying to use include guards because multiple source files include the same stuff?

Re: "Incomplete expression." in ASM6 macro

Posted: Tue Apr 09, 2019 1:16 pm
by tepples
doppelganger wrote:Are you trying to use include guards because multiple source files include the same stuff?
Yes. Both the driver and the instrument/sequence data include one of the header files, and I can't guarantee which gets included first because ASM6 requires them to be included in output file order.

Re: "Incomplete expression." in ASM6 macro

Posted: Tue Apr 09, 2019 2:37 pm
by doppelganger
So both the driver and instrument/sequence data are assembled together in one unit?

Re: "Incomplete expression." in ASM6 macro

Posted: Tue Apr 09, 2019 2:42 pm
by tepples
ASM6 normally assembles everything as one translation unit, so yes, driver and data are assembled together. It's possible though tricky to assemble something as multiple translation units with ASM6. If so, the separately assembled translation unit has to have entry points at fixed addresses, such as $8000, $8003, $8006, $8009, etc.

Re: "Incomplete expression." in ASM6 macro

Posted: Tue Apr 09, 2019 10:33 pm
by doppelganger
The problem with the traditional .ifndef type include guard is that ASM6 is a multiple-pass assembler, and anything inside an .ifndef with a following .define inside it will get processed on the first pass, but not on the second. The ASM6 only states that .ifdef and .ifndef operate depending on whether the operand symbol is defined/not defined, without any mention as to whether the definition is supposed to occur prior to the directive or not, so it's implied then that forward references are included as part of .ifdef/.ifndef's operation.

The fallout, of course, is that when ASM6 gets to the second pass, the included file's contents end up not getting output at all.

What you obviously wanted was the first include, and ONLY the first include, to occur. After thinking it over, I may have an ASM6-specific workaround.

Let's say you have your driver in driver.asm and instruments/sequence data in song.asm, and they both include a file called foo.asm for whatever reason. Now, since ASM6 is assembling these as one translation unit, it follows that there must be a top-level source file for the driver and data to go into, assuming one of them isn't the top-level file (I don't know enough about your code to make too many assumptions here).

Now, you obviously don't want foo.asm included twice. My idea is this: define a symbol in the top-level file that precedes the driver and data files that matches whatever symbol you'd have used for foo.asm's include guard:

Code: Select all

FOO_ASM = 1
Then, at the beginning of foo.asm, put this in:

Code: Select all

.if FOO_ASM
FOO_ASM = 0
;insert contents of foo.asm here
.endif
And hopefully that should cause only the very first .include of foo.asm to have any effect on the unit's output, since FOO_ASM will be set to nonzero on the first include, but zero on any subsequent includes, until the next pass.