Variadic macro in ca65 possible?

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Variadic macro in ca65 possible?

Post by tepples »

In [url=https://forums.nesdev.com/viewtopic.php?p=187010#p187010]this post[/url], tokumaru wrote:
Garth wrote:one feature I liked was that it allowed macros to have varying numbers of input parameters.
You can create macros like this in ca65 too, I do it all the time.
How does this work? I thought each macro's declaration specified the maximum number of arguments that it takes. Do you just declare a macro with, say, 32 arguments and say "too bad" to a process that wants to generate more?

For example: By default, the .word command asserts that each argument's value is in the range 0 to 65535. Let's say I want to make a counterpart that instead asserts that each value is in the range -32768 to 32767. The force_range option only switches between no range checking at all and unsigned range checking, not signed range checking. The issue about lack of signed range checking cites a mailing list conversation started by rainwarrior, in which Marc Rintsch recommended that one create a macro that performs the signed range checking.

So here's what I came up with (in small text because it's so wide):

Code: Select all

.macro signedword r1,r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20,r21,r22,r23,r24,r25,r26,r27,r28,r29,r30,r31,r32
.local r1value
  .ifblank r1
    .exitmacro
  .else
    r1value = r1
    .assert -32768 <= r1value && r1value <= 32767, error, "Range error (value not in [-32768..32767])"
    .word r1value & $FFFF
    signedword r2,r3,r4,r5,r6,r7,r8,r9,r10,r11,r12,r13,r14,r15,r16,r17,r18,r19,r20,r21,r22,r23,r24,r25,r26,r27,r28,r29,r30,r31,r32
  .endif
.endmacro
[/size]
But calling this with more than 32 arguments produces Error: Too many macro parameters. Though source code line length conventions make it unlikely that I'll use more than a dozen or so arguments, a preprocessor for data tables might use that many.

For comparison, C has variadic functions and variadic macros, and Python has def function(arg1, arg2, *restofargs).

If nobody can figure out a better way to make a ca65 macro that takes an arbitrary number of arguments, I plan to report this on cc65's issue tracker on GitHub.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Variadic macro in ca65 possible?

Post by tokumaru »

Garth wrote:Does it allow making macros that don't require a given number of input parameters? (just to confirm.)
You have to name all the parameters the macro can possibly take, but you're not required to supply them all when calling the macro. Iterating over a variable number of parameters requires recursion, and to avoid writing the lda #0 multiple times you might need an auxiliary macro (or some sort of flag indicating that the first iteration has already happened).

Here's a simple implementation using an auxiliary macro:

Code: Select all

.macro CLR_FLAG var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15
	.ifnblank var0
		lda #0
		CLR_FLAG2 var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15
	.endif
.endmacro

.macro CLR_FLAG2 var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15
	.ifnblank var0
		sta var0
		CLR_FLAG2 var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15
	.endif
.endmacro
And here's it with a flag:

Code: Select all

.macro CLR_FLAG var0, var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15, flag
	.ifnblank var0
		.ifblank flag
			lda #0
		.endif
		sta var0
		CLR_FLAG var1, var2, var3, var4, var5, var6, var7, var8, var9, var10, var11, var12, var13, var14, var15, , 1
	.endif
.endmacro
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Variadic macro in ca65 possible?

Post by tepples »

Calling your macro with 17 arguments will produce Error: Too many macro parameters.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Variadic macro in ca65 possible?

Post by tokumaru »

Well, there is a limit... I often don't bother writing extremely robust macros that can deal with every little error because they end up looking pretty bloated. I normally only write macros for myself, so I prefer not to waste much time making them foolproof.
User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 569
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Re: Variadic macro in ca65 possible?

Post by Jarhmander »

Movax12 has a technique that involve putting your arguments insides curly braces, and parsing that to extract your arguments (the curly braces around the parameters make exactly one argument for the macro). But, I have a feeling this is not what you're after.
((λ (x) (x x)) (λ (x) (x x)))
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Variadic macro in ca65 possible?

Post by koitsu »

What actual (i.e. non-anecdotal) need is there for this in ca65, or any assembler for that matter? Demonstrate this to me. My initial opinion as of this writing is that this is for the most part pointless. And while I obviously have utmost respect for Garth (esp. given his stature on the 6502.org forum), he has what I've concluded to e a very unhealthy fixation on macros (almost all responses from him involve macro advocacy).

NOTE: I'm talking about ca65, not cc65 where in C you can have variable arguments (ex. void foo(int blah, ...) then access said bits using va_{start,arg,copy,end}() functions -- these are at run-time, which I feel is important to note here).
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: Variadic macro in ca65 possible?

Post by tepples »

It was for a lookup table containing signed 16-bit starting velocities for particular projectiles in The Curse of Possum Hollow.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Variadic macro in ca65 possible?

Post by tokumaru »

I use macros to structure my programs better. You can obviously code in assembly the barebones way, using little more than labels and a couple of directives (org, for example), but assembly is so devoid of rules that sometimes it's hard to manage all the variables, the interaction between modules and so on. Even if you come up with strategies to handle this sort of thing, repeating the patterns manually all the time can be very error prone. This is where macros come in, you can automate all that functionality so it doesn't clutter your source code and you'll know it'll work consistently every time you use it.

I made moderate use of macros back when I used ASM6, but after switching to ca65 I couldn't help taking advantage of some of that macro goodness. I coded a set of assembler extensions to help me with declaring variables, encapsulating functions, aligning code/data to the end of the addressing space (for simulating a fixed bank in a mapper with 32KB switching), and now my code looks pretty clean, as it's composed almost exclusively by actual game code, and all the ROM and RAM organization is handled through simple macro calls.

Lots of my tasks need to work with a variable amount of parameters. For example, my functions are all isolated from the global scope, so that I can easily reuse labels or include the same function over and over (due to the need of having it present in several banks) without the risk of running into "label already defined" errors. But since I might need to address something inside a function's scope from the outside, I have to "export" labels to the global scope. To do that, I simply created a macro called "promote" (didn't want to create confusion with the existing .export and .global directives) that takes the names of labels from inside the scope and creates aliases for them in the global scope, prefixed by the function's name to leave no doubts as to where that label is.

That's just one example, I probably have 10 or so macros handling variable amounts of parameters.
tomaitheous
Posts: 592
Joined: Thu Aug 28, 2008 1:17 am
Contact:

Re: Variadic macro in ca65 possible?

Post by tomaitheous »

koitsu wrote:what I've concluded to e a very unhealthy fixation on macros (almost all responses from him involve macro advocacy).
I'd say your lack of consistent work in assembly has gained you an ignorant perception of macros. Anyone who's coded in enough assembly over the years, especially on something like the 65x, usually comes to the realization that macros are the next level. If you have the years of experience, but haven't reached this level.. then I don't know what to say. But to call someone else's preference for macros unhealthy, seems pretty ignorant to me. Especially someone who probably has spent waaayy more time in 65x assembly than you.
__________________________
http://pcedev.wordpress.com
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Variadic macro in ca65 possible?

Post by tokumaru »

I think koitsu had his fair share of ASM coding in the past, but doesn't do much of it these days. I assume tools back then weren't as versatile as they are today, and from the little I've heard (feel free to correct me if I'm wrong, I wasn't around back then) he made mostly hacks, not programs he architectured from the ground up.

If my assumptions are correct, I don't find his opinions on these topics surprising. Hacking is... well, for lack of a better term, a hacky activity. You have to work with the space you can get, dancing around someone else's code, without necessarily appreciating all the aspects of the architecture that was envisioned by the original creators. Sure it will make more sense to define variables with EQU than reserving bytes from a base address. Macros will definitely seem silly if you're working on top of binary code or disassembled code that don't use any.

Maybe if he started developing something from the ground up he'd appreciate the help that all these newer tools provide. Macros and assembler directives are definitely a step up from straight up ASM code, maybe not as much as straight up ASM is to coding directly in hex, but still. It boosts your productivity and the robustness of your code, once you take the time to study how everything works. If you simply try to use that stuff just for the heck of it without realizing WHY you need it, then it definitely isn't gonna work out.

Note that I'm not looking down on the work of hackers, they can do really amazing and creative stuff, often under sub-optimal conditions. I doubt I'd make a good hacker myself. I'm just saying that it's a different craft, even though most areas of knowledge overlap with those of game developers.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: Variadic macro in ca65 possible?

Post by rainwarrior »

Several x86 assemblers from the '80s called themselves "macro assemblers" (example: MASM) to deliberately highlight their macro features.

I can't say what 6502 assemblers were like in the past, but the idea of macros in assembly is several decades old.

Whether or not they're being overused in NESdev might be more of a philosophical/ideological question though. ;P (I've got little opinion on this, except it's sometimes difficult to understand shared code when its in someone's personally constructed macro language. This problem is a thousand times worse in web development frameworks though.)
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Variadic macro in ca65 possible?

Post by koitsu »

As for my background: my personal (User:Koitsu) nesdev wiki page provides all of that. I did a lot more 65xxx assembly in my youth and teenage years (~1987-1993) than I do now, but I've "authored" enough (most was on the Apple IIGS) -- but I certainly did not author any of the demo bits or utility portions I did using a plethora of macros (table generation using BASIC, most certainly! But not macros). Macros were quite available (and capable!) in Merlin 16 and ORCA/M in my prime. But throughout my life, I didn't provide help on forums or public mediums that involved -- paraphrasing -- a kind of "constant focus" on macros. I've always preferred people understand how to do something (in the native language they're programming in) so that they then can then make macros for themselves, etc.. Same can be said of subroutines, frameworks, or anything else (if you want a good example of how and where it gets out of control, see this thread and this thread -- I put way too many hours into trying to decipher a gigantic mess of such on the SNES/65816 for Espozo. It's irrefutable).

Sorry, this sounds really judgemental of Garth. It's not quite what I intended (honest!). It's just a very common go-to I see in his replies, and it irks me because I feel very strongly that a person should know how something works. I don't like handing people "magic solutions" (particularly code); this kind of belief is horribly prevalent in today's programming world (see: Ruby). I'm cool with macros, as long as someone takes the time to explain to the person to which they're being recommended *how* it works. I know a **lot** of programmers, for sake of example, who don't like using other people's code or things -- they'd rather understand how something works and write the function/routine/macro themselves. Matter of opinion. It just irks me is all.

That said, he certainly has more 65xx experience than I do -- but then again, I'd guess most of the senior commonplace folks on this forum do. Whether or not that's relevant is up to the reader.

All said: I'm still waiting for a demonstration of why variable-length macro arguments are helpful/useful. This isn't a challenge for debate, it's a more of a "show me how this would be useful, because I just don't see it; educate me!" thing. I can see it in a higher level language, but not for native 65xxx. Just because I'll be 40 this month doesn't mean I'm not interested in being shown new things and their usefulness (though fully admit I am quite the reluctant soul ;-) ).
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: Variadic macro in ca65 possible?

Post by tokumaru »

koitsu wrote:I'm still waiting for a demonstration of why variable-length macro arguments are helpful/useful.
I don't know if this will help convince you (you might very well just say "you wouldn't need macros in the first place if everything was global"), but here's an example of how I could code a (very pointless) subroutine using my macro extensions:

Code: Select all

	;"Add" subroutine
	.block Add

	;local variables
	.memory System_Scratchpad
		Value: .res 1
	.endmemory

WithoutCarry:

	clc

WithCarry:

	adc Value
	rts

	;make some labels visible from the outside
	.promote Value, WithoutCarry, WithCarry ;this processes a variable number of arguments

	.endblock
And then I can use the function it like this:

Code: Select all

	lda #$40
	sta Add_Value ;might have to be sta z:Add_Value to force ZP addressing if this comes before the variable is declared
	lda #$10
	jsr Add_WithoutCarry
It may look stupid for such a trivial example, but when you consider the amount of labels a full-sized game has, it makes sense to encapsulate anything that doesn't need to be global, so you don't have to work with long labels inside the subroutines themselves or worry about labels being redefined. My macros also help with multiple inclusions of the same subroutine, since they don't put in the global space labels that have already been put there previously (e.g. that .promote command won't do anything if this same routine is included again in another bank). The entry point defaults to the top of the block (a global label named "Add" will point to the top of the block), but you can select an alternate entry point like this:

Code: Select all

	.block Whatever, EnterHere

Loop:

	txa
	
EnterHere: ;the global label "Whatever" will point here

	ldx #$10
	dex
	bne Loop

	rts

	.endblock
Like I said, it may look dumb in this context, but macros can greatly increase robustness and productivity when you get the assembler to help you organize and manage things, instead of doing everything by hand.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Variadic macro in ca65 possible?

Post by koitsu »

To be fair, I took the time to go through Garth's posts to see if I was overreacting (because I certainly do tend to overreact). Of his 19 posts, there are great/direct answers, but there are some where "strange focuses" (and in many cases, focuses on macros) show up almost immediately. Just hear me out first, don't reply until you read past the itemised list:

* viewtopic.php?p=184968#p184968 -- doesn't mention macros directly, but said link quickly starts going over use of some magical StackOps.asm (what assembler is this?!) solution regarding "virtual stacks" (a thing I have never heard of in my life). The important point: the OP asked a pretty damn simple and direct question (see quoted material)
* viewtopic.php?p=184962#p184962 -- almost instantly starts going over use of macros for handling if/else in 6502, compounded by (again) an assembler we aren't familiar with around here (note: maybe we (the royal nesdev) should look into whatever assembler this is!)
* viewtopic.php?f=10&t=15199 -- not a post by Garth himself, but is actually a thread *about macros*. So, hey, this one's highly on-point, and sounds like some stuff was going on in PMs too (awesome, honest!)
* viewtopic.php?p=183751#p183751 -- more macro advocacy (gotta read it)
* viewtopic.php?p=183712#p183712 -- first post ever, instantly about macros (subject is about handling multiple loops)

The posts of his I *didn't* link tend to go over actual code or theories more clearly, e.g. I have this problem and how do I solve it in 6502, with good answers. So am I overreacting, given 19 posts where only 4 or 5 have a macro-centric focus? I am absolutely overreacting, and for for that I apologise.

I'm not back-patting when I say this, but: I think on some level it shows that I do read almost all the posts here on the forum and correlate certain "themes" or "focuses" with particular individuals (yeah Tepples I'm looking at you buddy ;-) Haha). When I start to see a kind of "advocacy pattern" that I feel doesn't necessarily help teach someone something, but is instead just "providing magic answers/solutions", it irks/concerns me. I jumped the gun on this for sure though.

Purely in contrast, I'll point to this thread where DRW asked a really good question and got a) good reference material including formula (which happens to be easily implemented in 6502 given /2 and *8 aspects), b) a clever answer from psychopathicteen (I'd forgotten about masking used in this particular manner), and c) a great answer from tokumaru about manipulating attribute table bytes this way being time consuming so instead just stick the AT in RAM and copy it to the actual AT when/as needed. I think that's probably one of my favourite posts in the past few months.

So, to Garth: sorry. I didn't mean to "call you out" or make you feel unwelcome. If I did, I truly deeply apologise. It's just that when I read your posts, I now expect macro advocacy, and had forgotten that the majority of what you've brought here isn't just that.

I also hope that tomaitheous can understand where I'm coming from in my concerns.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Variadic macro in ca65 possible?

Post by koitsu »

@tokumaru -- Thanks for the example. I'm now on my 4th time reading it through.

Ignoring our disagreement about globals vs. locals (I fully agree with you about label concerns and re-use!), I find what you've posted to be daunting (read: scary). I cannot imagine trying to debug this if something went awry with either an address the linker generated, or with "memory management" (the fact that the .res bytes pertain only to that individual .block/.endblock). I believe I understand how and why this works (it almost looks like a kind of half-ass implementation of a C++ class?) and I think it's neat. But man I would never want to see a listing file of anything you've assembled using that. ;-)

What I *don't* understand is how variable argument lengths for macros relates to your example. The only case I can work out is .promote being relevant to that subject? There is no .block/.endblock in ca65 (that I know of), so these too are macros, but they don't appear to have argument lists (re: jsr Add_WithoutCarry is not a macro call, it's a subroutine)? Iinstead it looks like {blockname}_{labelname} is the syntax you use... so is the point that you want to be able to do something like nested blocks, ex. end result of jsr Add_WithoutCarry_OtherBlock_LabelName_Block3_MoreStuff_HowLong_CanThisGet, or does it pertain to .promote specifically? If the latter: I now understand (and sympathise) with your concern/want. If not the latter: need more input!

Apologies if my question isn't clear enough. Ask and I can probably reword it.
Post Reply