C function for copying data doesn't work in CC65

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

Moderator: Moderators

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

Re: C function for copying data doesn't work in CC65

Post by DRW »

rainwarrior wrote:Could you attach a ROM? (A zip file of the source would be more useful to build than stuff copy pasted into the forum, too.)
I added the file to this post here. I simplified the code even further. And I also added ROMs.
rainwarrior wrote:Also: please specify the cc65 version.
thefox wrote:Looks like you're using a fairly old version of cc65, judging from SYMBOLS { __STACKSIZE__ = $0300; } (the syntax was changed slightly in later versions).
I was using the version that can be downloaded at http://www.cc65.org, ftp://ftp.musoftware.de/pub/uz/cc65/: Version 2.13.3.

Where can I find the latest stable version?
I've downloaded the snapshot version from http://sourceforge.net/projects/cc65/ now. Is this the correct one?

However, regarding my problem, this makes no difference: The error appears in both builds as can be seen with the ROMs in the zip file.
Attachments
TestCodeAndROMs.zip
(3 KiB) Downloaded 129 times
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: C function for copying data doesn't work in CC65

Post by thefox »

Well, I checked it out. Your initialization code fails to initialize the variable "sp" (software stack pointer) used by the cc65 runtime library. See for example: https://github.com/cc65/cc65/blob/maste ... e/pushax.s

So, what ends up happening is that pushax fetches the uninitialized stack pointer (in case of FCEUX, value is $0000), subtracts 2 to get $FFFE, then tries to push the value there. Naturally this doesn't work because that memory area points to ROM.

NDX is nice enough to give this warning:

Code: Select all

Warning: Uninitialized memory accessed: $0 (PC = $80AA)
Warning: Uninitialized memory accessed: $1 (PC = $80B8)
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
DRW
Posts: 2146
Joined: Sat Sep 07, 2013 2:59 pm

Re: C function for copying data doesn't work in CC65

Post by DRW »

Wow! I would have never found out that. I wasn't aware that I manually have to initialize any values that the compiler/nes.lib/runtime uses by itself.

Thanks a lot!

Two (or three) questions though:

1: Am I right that the sp variable is supposed to be set with that value from the config file:

Code: Select all

SYMBOLS
{
	__STACKSIZE__ = $0300;
}
2a: Is there any initialization function in the compiler's library that is supposed to be called by the end user that does this stuff?
(No, I'm not talking about the specific initialization code that sets up the whole NES (nes/crt0.s). I do that myself. I'm talking just about a generic function InitRuntimeVars or something like that which sets all the variables that the compiler needs.)

2b: If there's no such function: Is sp the only thing that is required to be set by the user? Or are there a bunch of other variables needed by the compiler that I have to set?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: C function for copying data doesn't work in CC65

Post by thefox »

I can't remember whether there's something else to initialize besides sp, it has been a too long time since I had to look at that stuff. I'd advise you to look at the default crt0 shipped with ca65 and remove what you don't need: https://github.com/cc65/cc65/blob/maste ... nes/crt0.s. Of course you have to understand what the code does to know what you can safely remove.

This was also what I was trying to say here: viewtopic.php?p=152911#p152911
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
DRW
Posts: 2146
Joined: Sat Sep 07, 2013 2:59 pm

Re: C function for copying data doesn't work in CC65

Post by DRW »

O.k., I'll check it out.

It works now. Thanks.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
dougeff
Posts: 3035
Joined: Fri May 08, 2015 7:17 pm
Location: DIGDUG
Contact:

Re: C function for copying data doesn't work in CC65

Post by dougeff »

PalettesData[1] = (PalettesData[0] == PalettesData[0] ? 0xA : 0xE);
I'm no expert, but I've noticed that if you code a conditional that is always true, cc65 will optimize it down to the "if true" statements and get rid of the rest. However that should =0A.
nesdoug.com -- blog/tutorial on programming for the NES
User avatar
rainwarrior
Posts: 8578
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: C function for copying data doesn't work in CC65

Post by rainwarrior »

As long as you don't use anything with constructors/destructors, this was sufficient for me: (run after "standard" NES initialization, PPU wait, zero RAM, etc.)

Code: Select all

crt0_start:
; initialize data
        jsr     copydata
; setup the C stack
        lda     #<(cstack + STACK_SIZE)
        sta     sp
        lda     #>(cstack + STACK_SIZE)
        sta     sp+1            ; Set argument stack ptr
        rts
User avatar
rainwarrior
Posts: 8578
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: C function for copying data doesn't work in CC65

Post by rainwarrior »

Also, you may want to use the -O option for "optimize". It will generate smaller and faster code.

Not sure if you turned it off just for this example, but I don't think there's much advantage to not optimizing in CC65. Turning off optimizations usually helps in debugging for regular C compilers, but for CC65 I find it makes it worse, since it generates a lot more (useless) code; the optimzed output is cleaner and easier to read! Also it doesn't reorder operations or do anything else that a normal compiler would do that normally makes no optimizations a debugging advantage.
User avatar
DRW
Posts: 2146
Joined: Sat Sep 07, 2013 2:59 pm

Re: C function for copying data doesn't work in CC65

Post by DRW »

dougeff wrote:
PalettesData[1] = (PalettesData[0] == PalettesData[0] ? 0xA : 0xE);
I'm no expert, but I've noticed that if you code a conditional that is always true, cc65 will optimize it down to the "if true" statements and get rid of the rest. However that should =0A.
I haven't tried it out again since my code works now, but I'm pretty sure that behavior also had to do with the missing sp initialization.
rainwarrior wrote:As long as you don't use anything with constructors/destructors
I only know constructors from object oriented languages like C++. So, I don't think I will use them in the program.
rainwarrior wrote:this was sufficient for me: (run after "standard" NES initialization, PPU wait, zero RAM, etc.)

Code: Select all

crt0_start:
; initialize data
        jsr     copydata
; setup the C stack
        lda     #<(cstack + STACK_SIZE)
        sta     sp
        lda     #>(cstack + STACK_SIZE)
        sta     sp+1            ; Set argument stack ptr
        rts
Is the call to copydata necessary? The comment in the code says:
"Copy the data segment from the LOAD to the RUN location"

I know when you declare a non-constant global variable and immediately initialize it, it is put into DATA instead of BSS. I never really understood why. So, I assume you cannot use the variables from the DATA section without calling copydata first?

However, I haven't needed a DATA section until now. I either declare global variables for general usage or I declare constants. I haven't really found any use for a global variable that can change the value later, but that has to have a definite initialization value. Do you know any?

What is cstack in your code?
I simply wrote this for sp:

Code: Select all

	LDA #<(__RAM_START__ + __RAM_SIZE__)
	STA sp
	LDA #>(__RAM_START__ + __RAM_SIZE__)
	STA sp + 1
with the RAM in the config file being:

Code: Select all

RAM: start = $0300, size = $0500, define = yes;
This is the way it was done in "Zooming Secretary".
rainwarrior wrote:Also, you may want to use the -O option for "optimize". It will generate smaller and faster code.
I have this in my actual code. I just left it out from the example because I wanted the smallest bare-bones example.

But thanks for mentioning it.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
rainwarrior
Posts: 8578
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: C function for copying data doesn't work in CC65

Post by rainwarrior »

DRW wrote:I only know constructors from object oriented languages like C++. So, I don't think I will use them in the program.
In this case "constructors" is CC65 specific jargon for a piece of code that runs at startup. More info here. Destructors are for when the program shuts down, but that's not relevant on the NES, since NES programs don't do that.

I'm not really sure why they added this feature to CC65. Probably for initializing zeropage variables used by the CRT or something? Only a few parts of the CRT use them. In particular, I think the heap allocators (i.e. "malloc" and "free") need them, but you probably wouldn't be using those in an NES game.

If it ever turns out you need them, you can just add jsr initlib after the other stuff. I think if you don't have an INIT segment in your CFG it will give you an error if you ever accidentally invoke a CRT module that has a constructor, so you should be safe as long as your CFG has no INIT segment.
DRW wrote:Is the call to copydata necessary?
Yes, unless you aren't using the DATA segment at all.
DRW wrote:I know when you declare a non-constant global variable and immediately initialize it, it is put into DATA instead of BSS. I never really understood why. So, I assume you cannot use the variables from the DATA section without calling copydata first?
Yes, exactly that. In C, any variable with an initializer gets its initialization value stored in the DATA segment, which is copied to RAM at startup by the copydata routine.
However, I haven't needed a DATA section until now.
If your CFG file has no DATA segment, then at least you'll get a error if you ever accidentally need it. If you're trying to avoid calling copydata, don't put a DATA segment in your CFG.
I either declare global variables for general usage or I declare constants. I haven't really found any use for a global variable that can change the value later, but that has to have a definite initialization value. Do you know any?
Like a lot of programming language features, there are always a thousand other ways to get something done; it isn't essential that you be able to initialize variables. It is just one of many convenient features of C. If you want to never use C initializers, that's fine. If you want to use them, CC65 will put them in the DATA segment and it needs to get copied to RAM at startup.
What is cstack in your code?
I must have explicitly allocated an area for the C stack with a .res directive somewhere, rather than just assuming that it goes at the end of the BSS segment or something.
User avatar
DRW
Posts: 2146
Joined: Sat Sep 07, 2013 2:59 pm

Re: C function for copying data doesn't work in CC65

Post by DRW »

Thanks for the information.

About the DATA segment: Can you think of anything in an NES game where initializing a global variable might be done at all?

I'm aware that there's always a way to prevent it.
But my situation is not: "I want to initialize my global variable, but I prefer to do this inside a function."
My situation is: "Where would I ever even consider to initialize a global variable?"

The thing is: In a PC program, I wouldn't use global variables at all. Now, here, with the NES, I do because they're cheaper than allocating memory on the stack or even the heap.

But my global variables are either things like counter variables for loops.

Or they are variables for the game status, like the current level or the player's energy.
But these get initialized when a game session starts, i.e. when you press Start on the title screen. Not when the program starts.

So, it wouldn't make sense to declare a global variable with unsigned char CurrentLevel = 1; when you start in the first level because you have to write CurrentLevel = 1; whenever you switch from the title screen to the game scenery. Therefore, you would of course declare the variable with unsigned char CurrentLevel; since it will be set before usage anyway.

So, it's not about finding ways to avoid a DATA segment. It's about not knowing of any situation where an explicit start value for a global variable would be even slightly useful.

Even if I wanted to use the DATA segment really badly (but only when it has a practical use, so explicitly setting the variables to 0 just for the sake of using DATA of course doesn't count), I wouldn't find an example. Can you think of any?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
tepples
Posts: 22426
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: C function for copying data doesn't work in CC65

Post by tepples »

DRW wrote:About the DATA segment: Can you think of anything in an NES game where initializing a global variable might be done at all?
Usually global variables are initialized not at power-on but at the start of a title screen, the start of a new game, the start of a new level, etc. If I'm copying anything from ROM into RAM at the start of a program, it's more than likely a trampoline for a 32K bank switching mapper, and the LOWCODE section is for that. There are a few variables that get initialized at power-on, such as the NMI counter, the result of detecting PAL vs. NTSC, the (disabled) state of the music engine and all channels, and the high score table (in games that use one).
User avatar
rainwarrior
Posts: 8578
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: C function for copying data doesn't work in CC65

Post by rainwarrior »

I don't think it's a big deal if you can't think of a use for it that particular C language feature. I can't think of any strong use cases for it off the top of my head.

You know what it does and how to use it; if you're doing something and it seems like a good/convenient thing to use, you can do it. If you never find yourself wanting it, that's perfectly fine too.

As long as there is no DATA segment in your CFG you are fine to leave that C language feature broken by not calling copydata, because any accidental use will produce a link error for you, stopping you from making that mistake.

It's perfectly normal not to use every single language feature in a project. Every professional project I've worked on has had some "forbidden" language features as part of its coding standard. In C++ projects I've had rules like no RTTI, no exceptions, no STL, no use of malloc/free, etc.
User avatar
DRW
Posts: 2146
Joined: Sat Sep 07, 2013 2:59 pm

Re: C function for copying data doesn't work in CC65

Post by DRW »

A highscore table is actually a good example for a DATA segment: It has meaningful default values (assuming it is not empty). But they can be overwritten.
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
Post Reply