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.

Moderator: Moderators

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

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

Post by Banshaku » Fri Jun 22, 2018 8:18 am

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: 21843
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 » Fri Jun 22, 2018 8:24 am

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: 3141
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

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

Post by thefox » Fri Jun 22, 2018 8:29 am

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: 2329
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

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

Post by Banshaku » Fri Jun 22, 2018 8:34 am

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: 2329
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

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

Post by Banshaku » Fri Jun 22, 2018 8:40 am

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: 9036
Joined: Sun Apr 13, 2008 11:12 am
Location: Seattle

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

Post by lidnariq » Fri Jun 22, 2018 10:14 am

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: 2329
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

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

Post by Banshaku » Fri Jun 22, 2018 7:20 pm

@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: 1914
Joined: Sat Sep 07, 2013 2:59 pm

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

Post by DRW » Sat Jun 23, 2018 4:06 am

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 [].
Available now: My game "City Trouble".
Sales website: https://megacatstudios.com/products/city-trouble
Gameplay: https://youtu.be/Eee0yurkIW4
Download website: http://www.denny-r-walter.de/city.htm

User avatar
Banshaku
Posts: 2329
Joined: Tue Jun 24, 2008 8:38 pm
Location: Fukuoka, Japan
Contact:

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

Post by Banshaku » Sat Jun 23, 2018 9:41 am

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: 7719
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 » Sat Jun 23, 2018 10:50 am

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