CC65, C const data ends up in data, not rodata

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

Post Reply
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

CC65, C const data ends up in data, not rodata

Post by Banshaku »

I have been playing with C code in cc65 these days and I have a small issue that right now doesn't hinder my testing but would like to know what I'm doing wrong. I didn't write C code for ages so it may be something that I don't know anymore about.

I will explain with some example so it will be easier to understand. First, what works.

For example, the smallest element, a list of sprite that forms a meta-sprite, ends up in rodata:

Code: Select all

const unsigned char standing1[] = {
	0x00, 0x00, 0x0B, 0x40,	// Head top 1  
...
128};
My list of meta-sprite that makes an animation list ends up in rodata:

Code: Select all

const unsigned char* const standingAnim[2] = {
	standing1,
	standing2
};
But my list of animation ends up in data:

Code: Select all

const unsigned char* const *heroAnimList[5] = { standingAnim, ...,  runningAnim };
The goal is to able to retrieve the proper animation list based on the current state of the actor:

Code: Select all

// List of animation
typedef enum { 
	STANDING = 0,
	WALKING,
	WALKING_GUN,
	JUMPING,
	JUMPING_GUN
} animState_t;

....
hero.anim.frameList = heroAnimList[STANDING];
My guess is on how I define my list with const but I didn't work in C for ages that I cannot see what is the subtle error I have. At the least for now the logic work, it just it takes ram for nothing.

I'm sure right now my code is quite complex too but for now I do not mind much. If some code does cause issues I will change the structure after examinating the resulting .s files.
Last edited by Banshaku on Fri Jun 22, 2018 8:25 am, edited 1 time in total.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: CC65, C const data ends up in data, not rodata

Post by tepples »

Banshaku wrote: But my list of animation ends up in data:

Code: Select all

const unsigned char* const *heroAnimList[5] = { standingAnim, ...,  runningAnim };
If the individual cels, sequences, and list of sequences are all const, you need three const in the declaration of the list of sequences. Does the following also end up in `DATA`?

Code: Select all

const unsigned char* const *const heroAnimList[5] = { standingAnim, ...,  runningAnim };
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: CC65, C const data ends up in data, not rodata

Post by thefox »

tepples wrote:
Banshaku wrote: But my list of animation ends up in data:

Code: Select all

const unsigned char* const *heroAnimList[5] = { standingAnim, ...,  runningAnim };
If the individual cels, sequences, and list of sequences are all const, you need three const in the declaration of the list of sequences. Does the following also end up in `DATA`?

Code: Select all

const unsigned char* const *const heroAnimList[5] = { standingAnim, ...,  runningAnim };
I find it easiest to read these from right to left:

The 1st one: heroAnimList is a pointer to a const pointer to unsigned char that is const
The 2nd one: heroAnimList is a const pointer to a const pointer to unsigned char that is const

So in this case the 2nd one should get placed in RODATA.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: CC65, C const data ends up in data, not rodata

Post by Banshaku »

tepples wrote: If the individual cels, sequences, and list of sequences are all const, you need three const in the declaration of the list of sequences. Does the following also end up in `DATA`?

Code: Select all

const unsigned char* const *const heroAnimList[5] = { standingAnim, ...,  runningAnim };
Just tested it and it is now in rodata. I had a feeling that it was something simple but I never used that many const before and looked like it was too many of them. I was wrong.

Thank you Tepples!

Seems like the Zxibit meme where we heard that you like const, so we put const in your const so you can const :lol:
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: CC65, C const data ends up in data, not rodata

Post by Banshaku »

thefox wrote: I find it easiest to read these from right to left:

The 1st one: heroAnimList is a pointer to a const pointer to unsigned char that is const
The 2nd one: heroAnimList is a const pointer to a const pointer to unsigned char that is const

So in this case the 2nd one should get placed in RODATA.
Read that way it makes it quite easy to understand. I didn't use much C in more than 10 years so I'm either rusty or didn't understand much at the time about const (I was more active around 93 during college but I don't remember much about talking about const with Turbo C at the time).
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: CC65, C const data ends up in data, not rodata

Post by lidnariq »

You might find the "cdecl" program useful.

Code: Select all

cdecl> explain const unsigned char* const *const heroAnimList[5]
declare heroAnimList as array 5 of const pointer to const pointer to const unsigned char
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: CC65, C const data ends up in data, not rodata

Post by Banshaku »

@lidnariq

What a coincidence, I did found that software yesterday when I was trying to find information about arrays of arrays of pointers. I don't know how to use it yet and at first tried to test with "declare", which is like writing English. I will try it with "explain" next time. Thank you for the tip!
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: CC65, C const data ends up in data, not rodata

Post by DRW »

Banshaku wrote:I had a feeling that it was something simple but I never used that many const before and looked like it was too many of them.
Well, it's only logical:
A constant array needs one const.
An array of an array needs two consts.
And an array of an array of an array needs three consts.

One const for each * or [].
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
Banshaku
Posts: 2417
Joined: Tue Jun 24, 2008 8:38 pm
Location: Japan
Contact:

Re: CC65, C const data ends up in data, not rodata

Post by Banshaku »

Yes, put that way it is logical. I guess I didn't visualize it as 3 arrays at first. I'm so used to language with their own memory management at work that pointers are not something common for me to use. I think I will need to review a little bit on the subject and try to work on that when I'm not exhausted (easier to find bugs this way ^^;;).
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: CC65, C const data ends up in data, not rodata

Post by rainwarrior »

DRW wrote:One const for each * or [].
[] is a bit different than * in this respect.


A * takes whatever type was on the left, and replaces it with a pointer to that type. That pointer itself is a 2 byte (or 4/8 byte) variable that stores an address, and that stored address can be modified if it's not const. The syntax allows you to put const immediately after the * to specify that it should not be modified.


A [] builds an array of whatever type was on the left. There's no place in the syntax to specify const like there is with *, because there is no pointer here. The array type is like all other variables; it can't be moved in the same way that "int a" can't be moved.

The difference might make some sense with an appeal to the 6502: we can always use absolute addressing to access variables or arrays (with indexing), but pointers always require indirect addressing.

You'll find if you try to assign a [] to another [] it will tell you that it's an invalid lvalue which is what the language spec calls variables, sort of. Array types are a non-modifiable lvalue, so you can't assign directly to them, except as part of the initial definition. (It was historically decided in C that arrays should not be assigned to, because that would really mean to copy the whole array, which is usually inefficient. So, they made arrays a special case, and the spec has to mention that over and over. When structs were added later they decided it was okay to assign to structs, though.. the language might seem inconsistent in this way. The implicit conversion of arrays to pointers makes this slightly more confusing too.)


There's an exception here, though. If the [] type is a parameter to a function, it implicitly behaves like a non-const pointer instead (modifiable lvalue). The language is actually missing a way to specify this as const. This conversion to a pointer type is forced by the location of the passed array being unknown at compile time. If you need const here, though, you can simply use a const * type instead, as arrays can always implicitly convert to pointers anyway. (So: a parameter [] is really a pointer with a syntax that unfortunately overrides the true array.)

(The importance of const on a parameter is minor, though. The consequences of reassigning it are local to that function; it won't make a difference in DATA vs RODATA etc. like it would with a variable's definition.)


Edit: I found this, which might explain it better: http://c-faq.com/aryptr/index.html


† I think C99 might have added the option to put const inside the []? Not sure.
Post Reply