How do I do in CA65 things I do in ASM6?

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

User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by thefox »

tokumaru wrote:Any way to achieve similar behavior in ca65, without having to create hundreds of overlapping segments, one for each subroutine?
In ca65 .enum has a different meaning, but .struct should be more or less equivalent. I would shy away from any solution that requires the linker configuration to be modified whenever a subroutine is added or removed.
tepples wrote:Is there a counterpart to GREATEST() or MAX() function in ld65 config language?
The expressions allowed in the linker scripts seem to be more limited than in the assembler. Apparently only addition, subtraction, multiplication and division (and parentheses) are allowed (I didn't check the source, though).
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I do in CA65 things I do in ASM6?

Post by tokumaru »

tepples wrote:I wonder whether scratchpad variables in $0000-$001F could be defined with a .struct.
Yeah, I was thinking of something along those lines.
[tokumaru]not automatic[/tokumaru]
I don't know if this is a criticism (don't worry, I won't be offended if it is), but I fail to see how wanting things to be automatic could be a bad thing. This is not about laziness (even though saving time is always a good thing!), but about making things more flexible and error-free. Flexibility means you can more easily reuse code across different projects without worrying about adjusting lots of tiny little details in your templates. At the same time, not having to type redundant pieces of information, that can be deduced from things you actually do have to type, completely eliminates the chances of you screwing up and creating inconsistencies. Sure, it may be more complicated to setup at first, but in the long run you have less things to worry about.
thefox wrote:I would shy away from any solution that requires the linker configuration to be modified whenever a subroutine is added or removed.
Precisely! I will look .struct up.
User avatar
GradualGames
Posts: 1106
Joined: Sun Nov 09, 2008 9:18 pm
Location: Pennsylvania, USA
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by GradualGames »

From my experience making two full size games with CA65, I find the most that I had to fiddle with linker configuration and rom layout came pretty early in the Nomolos project when I had little feeling for relative sizes of different types of code and data. So I basically lined everything up as I added them to the project. This resulted in having to juggle my rom layout fairly frequently, editing many files for .segment "ROMXX" and constants/luts for specifying which bank various objects reside in. After that first project I developed better habits for organizing data, dedicating certain roms exclusively to bg graphics, spr graphics, animations, music, engine extensions, and lookup tables. In short, once you get used to it I don't think that you'll feel it gets in your way. I can't say for sure how it would really differ from ASM6 for a large project, having never created a large project with ASM6. One thing that's nice about CA65 is it is pretty easy to stuff things in a few different banks all in one file just with the .segment directive. The linker config basically abstracts away all the .org, .pad that you would normally have to do in an ASM6 program. My feeling is it must be a big time saver.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by rainwarrior »

I don't think I'd bother trying to make the linker sort out unions on such a small scale. You can always just create alias labels. Maybe it's not as convenient as .res but for individual functions you probably don't have a lot of allocations to make. Doing it manually shouldn't be too onerous:

Code: Select all

; alllocate scratchpad
.segment "ZP": zeropage
scratchpad: .res 32

...

; alias new allocations from scratchpad for each function as needed

.proc myfunc
param_a = scratchpad + 0
param_b = scratchpad + 1
temp_c  = scartchpad + 2
    ; code goes here
    rts
.endproc

; bonus: using .proc puts these aliases in their own scope, so they don't pollute the global space, and you can also access them from outside the function:

    stx myfunc::param_a
    sty myfunc::param_b
    jsr myfunc
Edit: ZP segment must be identified as zeropage.
Last edited by rainwarrior on Tue Nov 03, 2015 11:18 am, edited 1 time in total.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I do in CA65 things I do in ASM6?

Post by tokumaru »

Yeah, that's the first solution that came to mind, and I do consider using it if I can't find a better one. Most subroutines use so few variables that this shouldn't be particularly annoying to manage.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by tepples »

I think the idea of the whole "union" thing is that the entire game needs certain variables, while other variables apply only to certain game phases, such as title screen, cut scenes, level intro and outro screens, and in-game. In RHDE, I had to use a ton of alias labels for memory maps that apply only to one of help screens, furnish phase, battle phase, and build phase.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I do in CA65 things I do in ASM6?

Post by tokumaru »

Another question: what are the rules for naming the MEMORY and SEGMENT entries in the config file? All examples I could find don't repeat names in those two sections, not even for the header, which is just 1 simple entry in each section. I don't want to use abbreviations (e.g. HDR) or make arbitrary modifications to the names before understanding what the best practices are.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by rainwarrior »

Ah, that's a good question actually. My above example has an error in it. If you have a segment called "ZEROPAGE" it will be automatically assumed by the assembler to be a zeropage segment.

Otherwise you have to manually do it:

Code: Select all

.segment "ZP": zeropage
If you forget, you get absolute addressing for everything. (Hopefully you'd catch this when getting an assembly error when trying to use a pointer, etc.)

Other than that, there's no special names for segments or memory blocks unless you want to use cc65 (it expects a bunch of specifically named segments by default, but they can all be changed). MEMORY regions can share the same names as SEGMENTS (i.e. they're different namespaces), but I always keep them unique, myself, for no particular reason.

See: ca65 docs: .segment

If you use .segment once with zeropage, it should generate an error if you ever forget to use zeropage anywhere else (this is checked globally by the linker), so there is a good amount of safety checks against doing it wrong. As long as you remember at least once, you're safe.

I tend to put all my allocations together in one file, so I forget about these rules; I don't have to think about them often. :P Usually I just name the zeropage segment "ZEROPAGE" so I don't have to remember this rule.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I do in CA65 things I do in ASM6?

Post by tokumaru »

rainwarrior wrote:Otherwise you have to manually do it:

Code: Select all

.segment "ZP": zeropage
Wait, what's the point of having type = zp in the segment definitions then?
Usually I just name the zeropage segment "ZEROPAGE" so I don't have to remember this rule.
I can't do that because ZP, like the rest of RAM, is divided into permanent and temporary sections, so I'll have several references to it. Do I need write : zeropage every time I switch to one of these segments? This isn't so bad, I just want to be sure it's necessary.
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by rainwarrior »

tokumaru wrote:Wait, what's the point of having type = zp in the segment definitions then?
Not really certain, but the types are ro/rw/bss/zp. Both bss and zp seem to have the same function (i.e. they enforce no initialized data in the segment). I wish it also made the linker enforce the missing "zeropage" on .segment but it does not appear to. Actually, I think I'll suggest this to the list...
tokumaru wrote:Do I need write : zeropage every time I switch to one of these segments? This isn't so bad, I just want to be sure it's necessary.
Yes, but as long as you remember to do it once, assembler/linker errors will enforce that you do it everywhere else that's needed.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by tepples »

That's not how I understand the docs. You only have to declare a segment as : zeropage the first time. The docs give this example:

Code: Select all

.segment "ROM2"                 ; Switch to ROM2 segment
.segment "ZP2": zeropage        ; New direct segment
.segment "ZP2"                  ; Ok, will use last attribute
.segment "ZP2": absolute        ; zp2.s(4): Error: Segment attribute mismatch
So you might define each zeropage segment by switching to it once in a global file that all your .s files .include:

Code: Select all

.pushseg
.segment "ZP1": zeropage
.segment "ZP2": zeropage
.segment "ZP3": zeropage
.popseg
User avatar
rainwarrior
Posts: 8731
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by rainwarrior »

Ah, I didn't realize subsequent uses of .segment with no qualifier will assume the previous state. I was just going by some quick tests I threw together, i.e.

Code: Select all

; this creates an error:

.segment "ZP" ; no specification: assumes absolute
.segment "ZP": zeropage ; error

; VS this apparently does not:

.segment "ZP": zeropage
.segment "ZP" ; OK: assumes previous usage
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by thefox »

thefox wrote:
tepples wrote:Is there a counterpart to GREATEST() or MAX() function in ld65 config language?
The expressions allowed in the linker scripts seem to be more limited than in the assembler. Apparently only addition, subtraction, multiplication and division (and parentheses) are allowed (I didn't check the source, though).
Possible solution for this one (this is getting a little bit hacky) is to export a symbol containing the necessary information from assembly code, and then import that in the linker config:

Code: Select all

.import __FOO_LAST__
; bogus expression, but full expression evaluation capabilities of the assembler available
; (can be used to implement stuff like MIN/MAX/...)
.export whatever = (__FOO_LAST__ < $8100)*$1234

Code: Select all

# linker.cfg
SYMBOLS { whatever: type=import; }
I tested this, and it seemed to work as expected, i.e. if the imported symbol was used before the "FOO" memory area had appeared in the config, it caused an error. And if it was used after (when the value of __FOO_LAST__ is known), it worked.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: How do I do in CA65 things I do in ASM6?

Post by tokumaru »

I have one more question: How do I go about making sure that timing-sensitive code doesn't cross page boundaries? So far I've used a macro to detect page crossing and generate an error in case it does happen, and then I'd manually shuffle subroutines around to prevent the situation. What is the recommended approach in ca65?
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How do I do in CA65 things I do in ASM6?

Post by tepples »

tokumaru wrote:How do I go about making sure that timing-sensitive code doesn't cross page boundaries?
.align 256 is one option. Another is

Code: Select all

.assert >function_end = >function, error, "I wanted Animal Crossing, not Page Crossing"
Post Reply