CC65 nes.cfg define = yes without occupying ROM space

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

CC65 nes.cfg define = yes without occupying ROM space

Post by DRW »

When I use define = yes in a CC65 config file, then the compiler defines the value of the segment as a constant.

This constant occupies actual ROM space. It is a value at a memory location that you can use with .import. When I want to use this value, I use the name (that the compiler turns into an address) and the program looks up the value at the corresponding location.

Now my question: Is there any way to let the compiler create the values as the kind of constants that are directly converted into values at compile time?

So, when I declare a segment SPRITES, I don't want the compiler to do this:

Code: Select all

__SPRITES_LOAD__: .byte $02, $00
__SPRITES_SIZE__: .byte $01, $00
Because this occupies actual space in the ROM.

I want something like this:

Code: Select all

__SPRITES_LOAD__ = $0200
__SPRITES_SIZE__ = $0100
Because this doesn't occupy ROM space. Instead, whenever I use these constants, the values are used directly.

I don't understand why anybody would want these values inside the ROM anyway. This is not actual game data stuff like a level map or something like that. It's just a constant for calculations. Therefore, I would never declare them in an actual memory location, but I would simply use the name = value version, so that whenever it is used, the actual values get used.
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by rainwarrior »

The compiler doesn't use a .cfg file, only the linker does. There is no possible way for the compiler generate the data you mentioned.

As you said, you can .import the symbol. A symbol is just a symbol, a constant value that you can then use in your code, which is exactly what you want.

The linker uses the .cfg file, and creates that symbol at link time which can only be imported. It will never appear as data in any object generated by the compiler or assembler.

What do you mean it creates space in the ROM? I really don't understand what you're saying here. Could you post some example that demonstrates what you mean?
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by DRW »

rainwarrior wrote:What do you mean it creates space in the ROM? I really don't understand what you're saying here. Could you post some example that demonstrates what you mean?
An general example: I mean the code that gets generated when you do something like this:

Code: Select all

const unsigned char Test = 5;
Which means, the value 5 occupies one byte in the ROM.

As far as I know (I don't have the compiler at hand right now), the code is Assembly then looks like this:

Code: Select all

.segment "RODATA"
    _Test: .byte $05
And in this case, you can .import the value and use it with its address:

Code: Select all

LDA _Test
Now, on the other hand, when I just write this in Assembly:

Code: Select all

Test = $05
then I would use it like this:

Code: Select all

LDA #Test
In this case, Test doesn't occupy any dedicated space in ROM. (Apart of course from the fact that the call to LDA with a parameter occupies space. But there is no specific section in RODATA where the value 5 is put.)

And since the config definitions can be imported with .import, I was under the impression that the values defined through the config file get stored into the ROM data.
After all, isn't __SPRITES_SIZE__ an address that points to a two byte value?

__SPRITES_SIZE__ is not the value $0100 itself. It is an address that points to a memory location where the value $0100 is stored.
So, you cannot write

Code: Select all

LDA #__SPRITES_SIZE__ >> 8
to put $01 into the accumulator while

Code: Select all

LDA #__SPRITES_SIZE__ & $FF
would put $00 into the accumulator, right?

Instead, you have to use it this way:

Code: Select all

LDA __SPRITES_SIZE__ ; Gets $01
LDA __SPRITES_SIZE__ + 1 ; Gets $00
Therefore, the value $0100 is stored somewhere in the ROM memory.

Isn't that correct? Or am I completely mistaken here?
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by rainwarrior »

It already does what you say you want it to, and it already doesn't do what you say you don't want it to.

Why do you think it would generate a table of bytes in RODATA?
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by DRW »

Because until now, I haven't seen an .import that doesn't refer to an actual memory location.

For example, when you have a bunch of constants like these:

Code: Select all

PPUMASK = $2001
these are always put into an ".inc" file if they are used in more than one code file.

I have never seen anybody do this:

Code: Select all

PPUMASK = $2001
.export PPUMASK
and then the second code file doing this:

Code: Select all

.import PPUMASK
(Is this even possible?)

So, I was under the impression that whenever you have an .import, that it is always a memory address to either a function or to a constant or a variable, but not that the name refers to the actual value itself.
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by thefox »

DRW wrote:I have never seen anybody do this:

Code: Select all

PPUMASK = $2001
.export PPUMASK
and then the second code file doing this:

Code: Select all

.import PPUMASK
(Is this even possible?)
It's possible, but quite pointless. And no, define=yes doesn't occupy ROM space.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by DRW »

Why is it pointless? Why is an include file that defines all constants and that gets included in all code files better than defining the constants in one code file and then importing them everywhere else?
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by rainwarrior »

He might have jut meant that exporting PPUMASK is kind of pointless. Myself I think defining PPUMASK is already pointless. ;P I find $2001 much easier to read.

On the subject of exporting constants, yes I think there is good reason to do this sometimes. It lets me keep those constants together with their implementation, instead of the C practice of having to expose it publicly with a header.
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by DRW »

rainwarrior wrote:He might have jut meant that exporting PPUMASK is kind of pointless. Myself I think defining PPUMASK is already pointless. ;P I find $2001 much easier to read.
That was of course just an example. (Although I do define these constants in my program since I never keep in mind which value is done for which stuff.)
rainwarrior wrote:On the subject of exporting constants, yes I think there is good reason to do this sometimes. It lets me keep those constants together with their implementation, instead of the C practice of having to expose it publicly with a header.
Well, I wasn't even aware that this is even possible.

By the way, when I asked for a way to use the same constant in Assembly and in C, someone suggested to do this:

Code: Select all

#ifdef ASSEMBLY_VERSION
#define CONSTANT(name, value) name = value
#else
#define CONSTANT(name, value) enum { name = value };
#endif

CONSTANT(MyTestConstant, 5)
and then to compile this file once with the macro ASSEMBLY_VERSION defined and once without it being defined and then to include these two files in the C and Assembly code respectively.

But if you can export constants in Assembly, shouldn't it simply be possible to import that constant with
extern const unsigned char MyTestConstant;?
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by tepples »

DRW wrote:I have never seen anybody do this:

Code: Select all

PPUMASK = $2001
.export PPUMASK
and then the second code file doing this:

Code: Select all

.import PPUMASK
(Is this even possible?)
I do exactly this to declare the location of the 256-byte page containing the RAM copy of the OAM display list. In one file I do

Code: Select all

OAM = $0200
And in the global.inc file that all files in the project include, I do this:

Code: Select all

.global OAM
The .global keyword acts like .export if a file defines a symbol or .import if it doesn't. Then I can do this:

Code: Select all

lda cursor_y
sta OAM,x
inx
; rest of code snipped
lda #>OAM
sta OAM_DMA
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by rainwarrior »

DRW wrote:But if you can export constants in Assembly, shouldn't it simply be possible to import that constant with
extern const unsigned char MyTestConstant;?
Ah, now I think I understand where the confusion comes from. Extern in C does not really work the same as .import in assembly.

In assembly there isn't really a difference between a pointer and any other number. When you .export a label, really you're just exporting the address that it eventually ends up at, which is just a number. You can .export a constant value too if you want. They both go through the same way. Just a number.

I kinda wish I could have an assembler where all address operands must be expressions involving labels, so I'd never make the mistake of forgetting #. (I made this mistake a lot more when I was learning, though, it doesn't happen so often these days, but still now and then.) I think there's too many edge cases where you might want to use a label or a literal the other way though. Fantasy assembler:

Code: Select all

lda 5 ; this generates a warning
lda #5 ; this does not
lda label ; this does not
lda label+25 ; this does not
lda #<label ; probably this should not warn?
lda .nowarning(5) ; maybe we could be explicit when we want to break the rule?
In C, however, there is a separation between pointers and other types of numbers, and constants work differently than other things. The compiler is actually free to store the value at a memory address to be fetched by the program if it wants or needs to, inlining it as an immediate value is merely a potential optimization it can do. C compilers won't do that optimization if the constant isn't defined within your translation unit, though, and linking of externs is done exclusively through addresses, so in C, extern does cause a constant to become a stored value rather than an inlined immediate, usually.
User avatar
DRW
Posts: 2070
Joined: Sat Sep 07, 2013 2:59 pm

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by DRW »

tepples wrote:I do exactly this to declare the location of the 256-byte page containing the RAM copy of the OAM display list. In one file I do

Code: Select all

OAM = $0200
And in the global.inc file that all files in the project include, I do this:

Code: Select all

.global OAM
Isn't this totally pointless? If you have an include file anyway, why not just declare the contant there?
My question whether it can be imported and exported was specifically so that you don't need an include file, but you declare one constant in one file and another file can import it.

@rainwarrior: Thanks for the additional information.

I'm still asking myself why you should ever use an ".inc" file for general purpose constants instead of just exporting them, so that every other file that needs it imports it.
Available now: My game "City Trouble".
Website: https://megacatstudios.com/products/city-trouble
Trailer: https://youtu.be/IYXpP59qSxA
Gameplay: https://youtu.be/Eee0yurkIW4
German Retro Gamer article: http://i67.tinypic.com/345o108.jpg
User avatar
Movax12
Posts: 529
Joined: Sun Jan 02, 2011 11:50 am

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by Movax12 »

DRW wrote:I'm still asking myself why you should ever use an ".inc" file for general purpose constants instead of just exporting them, so that every other file that needs it imports it.
In assembly, you can use your include file to define macros and symbols, without code. You could treat a file like that similar to a header file in C, and include it for each module that needed it when using make or something similar to build multiple modules at once. You can also use Include Guards with ca65. I prefer that over importing and exporting, though I don't see a big advantage either way.
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by thefox »

DRW wrote:Why is it pointless? Why is an include file that defines all constants and that gets included in all code files better than defining the constants in one code file and then importing them everywhere else?
I meant that it's pointless to import/export a known constant like that. I can't think of any benefit for doing so. PPUMASK is not going to move to a different memory location. Besides, by doing that you are only making the assembler's job harder by not letting it know the constant value (can be significant if need to use stuff like ".if" on your constants).

I think tepples' example is slightly different, because the shadow OAM address could potentially change (even if it's unlikely). At least using .import/.export/.global in that case makes sure that if the address changes, the project doesn't have to be recompiled, only linked. (Granted, that's also a minor concern given that compilation times in 65xx projects tend not to be very long anyways.)

DRW: What's the benefit of exporting them? Do you really want to type ".import PPUMASK" in every file where you need PPUMASK? And if you're going all of those .imports (or .globals) in an include file, why not just put the values there in the first place?
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
3gengames
Formerly 65024U
Posts: 2281
Joined: Sat Mar 27, 2010 12:57 pm

Re: CC65 nes.cfg define = yes without occupying ROM space

Post by 3gengames »

The only thing I can think of is that you have a more concise variable structure. I think it makes sense to import everything that isn't native to that file. One change is all you'll need to change everything, which is good in theory of modifying code.
Post Reply