nesdev.com
https://forums.nesdev.com/

How do I do in CA65 things I do in ASM6?
https://forums.nesdev.com/viewtopic.php?f=2&t=13434
Page 3 of 6

Author:  thefox [ Tue Nov 03, 2015 7:56 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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).

Author:  tokumaru [ Tue Nov 03, 2015 8:29 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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.

Quote:
[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.

Author:  GradualGames [ Tue Nov 03, 2015 9:07 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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.

Author:  rainwarrior [ Tue Nov 03, 2015 10:26 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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:
; 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.

Author:  tokumaru [ Tue Nov 03, 2015 10:35 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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.

Author:  tepples [ Tue Nov 03, 2015 10:38 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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.

Author:  tokumaru [ Tue Nov 03, 2015 11:02 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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.

Author:  rainwarrior [ Tue Nov 03, 2015 11:17 am ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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:
.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.

Author:  tokumaru [ Tue Nov 03, 2015 2:15 pm ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

rainwarrior wrote:
Otherwise you have to manually do it:
Code:
.segment "ZP": zeropage

Wait, what's the point of having type = zp in the segment definitions then?

Quote:
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.

Author:  rainwarrior [ Tue Nov 03, 2015 3:25 pm ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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.

Author:  tepples [ Tue Nov 03, 2015 3:47 pm ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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:
.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:
.pushseg
.segment "ZP1": zeropage
.segment "ZP2": zeropage
.segment "ZP3": zeropage
.popseg

Author:  rainwarrior [ Tue Nov 03, 2015 4:26 pm ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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:
; 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

Author:  thefox [ Tue Nov 03, 2015 9:45 pm ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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:
.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:
# 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.

Author:  tokumaru [ Wed Nov 04, 2015 3:30 pm ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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?

Author:  tepples [ Wed Nov 04, 2015 3:51 pm ]
Post subject:  Re: How do I do in CA65 things I do in ASM6?

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:
.assert >function_end = >function, error, "I wanted Animal Crossing, not Page Crossing"

Page 3 of 6 All times are UTC - 7 hours
Powered by phpBB® Forum Software © phpBB Group
http://www.phpbb.com/