This page is a mirror of Tepples' nesdev forum mirror (URL TBD).
Last updated on Oct-18-2019 Download

C function for copying data doesn't work in CC65

C function for copying data doesn't work in CC65
by on (#154925)
In my NES program, I have an array for palette values and a "boolean" variable:
Code:
.segment "ZEROPAGE"
   _PalettesDataIsFilled: .res 1
   .exportzp _PalettesDataIsFilled
.segment "BSS"
   _PalettesData: .res 32
   .export _PalettesData


Whenever _PalettesDataIsFilled is set to 1, the NMI will update the palette by reading _PalettesData and copying it into the PPU and then it will set _PalettesDataIsFilled back to 0.

This works totally fine.


My problem is: When I try to copy another array to _PalettesDataIsFilled, it works only when I access the data directly, but not when I use a function for it.

So, let's say I have this constant:
Code:
const unsigned char TitleScreenPalettesData[] =
{
   0x0F, 0x16, 0x16, 0x16, 0x0F, 0x1A, 0x1A, 0x1A, 0x0F, 0x12, 0x12, 0x12, 0x0F, 0x28, 0x28, 0x28,
   0x0F, 0x30, 0x30, 0x30, 0x0F, 0x30, 0x30, 0x30, 0x0F, 0x30, 0x30, 0x30, 0x0F, 0x30, 0x30, 0x30,
};


Now, when I do this:
Code:
extern byte PalettesData[];
extern byte PalettesDataIsFilled;
#pragma zpsym("PalettesDataIsFilled")

static unsigned char Index;

void InitializeTitleScreen(void)
{
   for (Index = 0; Index < 32; ++Index)
      PalettesData[Index] = TitleScreenPalettesData[Index];
   
   PalettesDataIsFilled = 1;
}

then everything is alright. _PalettesData gets properly filled and I can see the colors in the game because the NMI reads from _PalettesData.

But now my problem: If I try to use a copy function, it doesn't work:
Code:
extern byte PalettesData[];
extern byte PalettesDataIsFilled;
#pragma zpsym("PalettesDataIsFilled")

static unsigned char Index;

void CopyPalettesData(unsigned char *destinationData, const unsigned char *sourceData)
{
   for (Index = 0; Index < 32; ++Index)
      destinationData[Index] = sourceData[Index];
}

void InitializeTitleScreen(void)
{
   CopyPalettesData(PalettesData, TitleScreenPalettesData);

   PalettesDataIsFilled = 1;
}

The NMI will just fill the palette with a bunch of zeroes, i.e. the values that _PalettesData has when it is declared.

So, why doesn't the copy function work?


When I use memcpy from the <string.h>, it works again:
Code:
void InitializeTitleScreen(void)
{
   memcpy(PalettesData, TitleScreenPalettesData, 32);

   PalettesDataIsFilled = 1;
}


What am I doing wrong with my CopyData function? I debugged it in Visual Studio and the copy process worked fine there.


Besides, in case you wonder why I don't just use memcpy: I'm planning to do more custom copy stuff like "read everything until the value 0xFF appears" etc. The above function is just the most basic one to explain my problem.
Re: C function for copying data doesn't work in CC65
by on (#154927)
I can't spot an error from just what you've given. Perhaps share the generated assembly files? (use -T flag with cc65 so that the source file is added as a comment) Or share the ROM, or explain how you are evaluating the results of the code you've written?

Also consider that the possibility that the error is outside what you've posted, and the change you are seeing might not be as directly related to what you've changed in the code as you think? (e.g. Are your variables like Index threadsafe?)
Re: C function for copying data doesn't work in CC65
by on (#154929)
I'm sending you the code of the file with the copy function.

I guess posting the whole code would be a bit too much. It's not some simple sample program, but the framework for my game, so I try to keep it short.

What other reasons could there be for this problem? I fill an array manually and it works as expected. I fill it by using memcpy and it works as expected. I fill it with my own copy function and it doesn't work. I don't see how a completely different part of the code, other than the copy function, could be responsible.

And why do variables in an NES game have to be thread-safe?

Code:
;
; File generated by cc65 v 2.13.3
;
   .fopt      compiler,"cc65 v 2.13.3"
   .setcpu      "6502"
   .smart      on
   .autoimport   on
   .case      on
   .debuginfo   off
   .importzp   sp, sreg, regsave, regbank, tmp1, ptr1, ptr2
   .macpack   longbranch
   .import      _PalettesData
   .importzp   _PalettesDataIsFilled
   .export      _InitializeTitleScreen
   .export      _ProcessTitleScreen
   .export      _TitleScreenPalettesData
   .export      _CopyPalettesData

.segment   "RODATA"

_TitleScreenPalettesData:
   .byte   $0F
   .byte   $16
   .byte   $16
   .byte   $16
   .byte   $0F
   .byte   $1A
   .byte   $1A
   .byte   $1A
   .byte   $0F
   .byte   $12
   .byte   $12
   .byte   $12
   .byte   $0F
   .byte   $28
   .byte   $28
   .byte   $28
   .byte   $0F
   .byte   $30
   .byte   $30
   .byte   $30
   .byte   $0F
   .byte   $30
   .byte   $30
   .byte   $30
   .byte   $0F
   .byte   $30
   .byte   $30
   .byte   $30
   .byte   $0F
   .byte   $30
   .byte   $30
   .byte   $30

.segment   "BSS"

_Index:
   .res   1,$00

; ---------------------------------------------------------------
; void __near__ InitializeTitleScreen (void)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _InitializeTitleScreen: near

.segment   "CODE"

;
; CopyPalettesData(PalettesData, TitleScreenPalettesData);
;
   lda     #<(_PalettesData)
   ldx     #>(_PalettesData)
   jsr     pushax
   lda     #<(_TitleScreenPalettesData)
   ldx     #>(_TitleScreenPalettesData)
   jsr     pushax
   jsr     _CopyPalettesData
;
; PalettesDataIsFilled = 1;
;
   lda     #$01
   sta     _PalettesDataIsFilled
;
; }
;
   rts

.endproc

; ---------------------------------------------------------------
; void __near__ CopyPalettesData (__near__ unsigned char*, __near__ const unsigned char*)
; ---------------------------------------------------------------

.segment   "CODE"

.proc   _CopyPalettesData: near

.segment   "CODE"

;
; for (Index = 0; Index < 32; ++Index)
;
   lda     #$00
   sta     _Index
L002D:   lda     _Index
   cmp     #$20
   jcs     incsp4
;
; destinationData[Index] = sourceData[Index];
;
   ldy     #$03
   jsr     ldaxysp
   clc
   adc     _Index
   bcc     L0040
   inx
L0040:   jsr     pushax
   ldy     #$03
   jsr     ldaxysp
   ldy     _Index
   sta     ptr1
   stx     ptr1+1
   lda     (ptr1),y
   ldy     #$00
   jsr     staspidx
;
; for (Index = 0; Index < 32; ++Index)
;
   inc     _Index
   jmp     L002D

.endproc
Re: C function for copying data doesn't work in CC65
by on (#154930)
DRW wrote:
And why do variables in an NES game have to be thread-safe?

NES code is often multi-threaded, with an NMI thread (and possibly an IRQ thread) interrupting the main thread. If, for example, you tried to use Index in your NMI thread and main thread, it very well could cause a conflict. For example, in this case if your code was slow enough to spill into the next frame, and you had a thread-safety issue, that could easily cause different behavior even if the code is "supposed" to do the same thing local to the main thread.

DRW wrote:
What other reasons could there be for this problem? I fill an array manually and it works as expected. I fill it by using memcpy and it works as expected. I fill it with my own copy function and it doesn't work. I don't see how a completely different part of the code, other than the copy function, could be responsible.

There are a million other possible reasons for the problem. Where is Index stored? Are you certain it is not conflicting with a cc65 temporary value, perhaps?

Well, let's start by verifying that it is indeed responsible. Put a breakpoint on writes to PalettesData, and watch and see if 32 writes go through. The assembly actually looks fine to me, so I expect that it is correct, but being corrupted by something else going on in your ROM. A breakpoint will verify this easily. If something else is writing to that array, you'll catch it very easily too.

As an aside, some performance tips for cc65:
  • declare functions with fastcall to avoid having to pass every parameter on the C stack
  • try to use as few parameters as possible to functions to avoid having to pass more parameters on the C stack
  • avoid using pointer parameters when the arrays are static; the compiler can make huge optimizations if it can work with an absolute address of an array instead of a pointer parameter to it

This:
Code:
for (Index = 0; Index < 32; ++Index) PalettesData[Index] = TitleScreenPalettesData[Index];

Will be a megaton faster than:
Code:
CopyPalettesData(PalettesData, TitleScreenPalettesData);
Re: C function for copying data doesn't work in CC65
by on (#154954)
Oh, you're talking about NMI when it comes to threads.
Yes, my variables are thread-safe: Apart from the fact that I would never use a variable in Assembly that I declared in C, my program is built in a way that when the game logic is still running and NMI hits, then NMI is left again. (Including saving and restoring the registers. I've actually asked about this here on the forum.)

rainwarrior wrote:
Where is Index stored? Are you certain it is not conflicting with a cc65 temporary value, perhaps?

Well, I hope that CC65 temporary values don't use an underscore and a capital letter in the beginning. (The variable would be transformed to _Index in Assembly instead of Index in C.) But when I get home, I'll try out a more cryptic name.

rainwarrior wrote:
Put a breakpoint on writes to PalettesData, and watch and see if 32 writes go through.

Yeah, that's the thing: How do I put a breakpoint there? I'm working with Notepad++ and call the compiler in a batch file. If you know a way to actually debug the program like in Visual Studio, I would be really grateful. This would help me a ton whenever something like this comes up.

rainwarrior wrote:
declare functions with fastcall to avoid having to pass every parameter on the C stack

The CC65 documentation said that this is only useful if you write the function in Assembly and that it doesn't change anything if your function is written in C.

rainwarrior wrote:
avoid using pointer parameters when the arrays are static; the compiler can make huge optimizations if it can work with an absolute address of an array instead of a pointer parameter to it

This:
Code:
for (Index = 0; Index < 32; ++Index) PalettesData[Index] = TitleScreenPalettesData[Index];

Will be a megaton faster than:
Code:
CopyPalettesData(PalettesData, TitleScreenPalettesData);

Sure, but you're missing a very important detail here: The above code is just an example. I won't have just one palette array to copy. I will have a ton of arrays. One palette for each level, just to begin with. Then I'll have a whole background for each level. Column updates when the game scrolls. Etc.

It might be faster. But using the above code 20 times in my program will fill the ROM pretty vastly.

Furthermore, speed isn't really an issue here: Since this is graphics initialization, these calls are all done when rendering is turned off anyway. The only "live update" that uses the function will be the write of 20 tiles every few frames and that's about it.
Re: C function for copying data doesn't work in CC65
by on (#154955)
DRW wrote:
Well, I hope that CC65 temporary values don't use an underscore and a capital letter in the beginning. (The variable would be transformed to _Index in Assembly instead of Index in C.) But when I get home, I'll try out a more cryptic name.

It's not a variable naming conflict, you'd get a linker error in that case. The only way that you'd be getting conflict with the runtime library's temporary variables is if you have somehow allocated the same memory area for them.

Quote:
Yeah, that's the thing: How do I put a breakpoint there? I'm working with Notepad++ and call the compiler in a batch file. If you know a way to actually debug the program like in Visual Studio, I would be really grateful. This would help me a ton whenever something like this comes up.

Find out the RAM address of the variable (e.g. from a label file -Ln) and set the breakpoint in an emulator like FCEUX.

Quote:
rainwarrior wrote:
declare functions with fastcall to avoid having to pass every parameter on the C stack

The CC65 documentation said that this is only useful if you write the function in Assembly and that it doesn't change anything if your function is written in C.

I believe that the default calling convention was recently changed to fastcall.

Quote:
It might be faster. But using the above code 20 times in my program will fill the ROM pretty vastly.

Depends on the function. If the function body is sufficiently small, inlining it might take less space than the parameter passing needed for the non-inlined version. Anyway, no point in optimizing prematurely.
Re: C function for copying data doesn't work in CC65
by on (#154957)
thefox wrote:
Find out the RAM address of the variable (e.g. from a label file -Ln) and set the breakpoint in an emulator like FCEUX.

The palettes array itself, that's the one that is used by the NMI later. So, I already see that it was not filled. Because when I call the algorithm manually with the concrete variables, it works.

What I need to debug: Does the copy function actually fill the array and something else goes wrong? Or is it the function that is wrong because it doesn't do the filling since the pointer shows to something else?

Therefore, I'd need to be able to pause the program exactly in the moment when that copy function is called to inspect the values. (That copying has nothing to do with PPU yet. It's just passing values to a general array.)

How do I do this in an emulator?
And if I find out that the copy function doesn't fill the array, how do I find out where the function parameters point to?


Another idea: Might there be an issue with the fact that the compiler treats the pointers as __near__? At least that's what the comment in the Assembly code says:
Code:
void __near__ CopyPalettesData (__near__ unsigned char*, __near__ const unsigned char*)

Would I have to add anything here?
Re: C function for copying data doesn't work in CC65
by on (#154958)
How about just set the write breakpoint on the array? If the copy function does its job, you will land directly within it, and you can step over the function to see which is the next place that also accesses the array (possibly overwriting its contents). If the copy function doesn't do it's job, then you can start to investigate WHY.
Re: C function for copying data doesn't work in CC65
by on (#154959)
O.k., I'll try it out when I'm at home again.
Re: C function for copying data doesn't work in CC65
by on (#154960)
Alternatively, you know the values of the array. You can search the compiled file for those values (0f 16 16 16 etc.). That will give you the address of the data, then set a breakpoint for reads from that starting address.

FCEUX can also add conditions to breakpoints if that's giving you problems. For palette writes, if you know the first value of the palette (0f). You could set a breakpoint to writes -- with the condition that (A == #0f)....this is just an example of setting breakpoints, and this might give you the RAM address to look for, if you don't know it.

I've been adding specific "false" writes into my code, right at the point that I want it to stop, so I can set a breakpoint for any writes to that address, and I know exactly where in the code I'm at. Mine looks like this...

Code:
inc $0100


Or in C it would look like this...

Code:
++*((unsigned char*)0x0100);
Re: C function for copying data doesn't work in CC65
by on (#154963)
DRW wrote:
Well, I hope that CC65 temporary values don't use an underscore and a capital letter in the beginning. (The variable would be transformed to _Index in Assembly instead of Index in C.) But when I get home, I'll try out a more cryptic name.

It wasn't a question of what the name is, but rather how it's being linked. If you accidentally had overlapping segments in your .cfg file, it could cause a conflict that the linker wouldn't be able to warn you about. (The linker would warn you about a name conflict.)

DRW wrote:
Yeah, that's the thing: How do I put a breakpoint there? I'm working with Notepad++ and call the compiler in a batch file. If you know a way to actually debug the program like in Visual Studio, I would be really grateful. This would help me a ton whenever something like this comes up.

Link with the -Ln flag, open the generated file and search for the symbol _PalettesData. This will tell you its address. Open your ROM in FCUEX (or other suitable debugging emulator), open the debugger, set a CPU write breakpoint on the relevant address, and reset your ROM. Documentation on FCUEX's debugger here: link

Additionally, I'd recommend writing some sort of utility/script to automatically process the -Ln result into debug symbols for FCEUX, so that the names and symbols will appear directly in the debugger as you're using it. This example contains a python script that demonstrates that, the debug symbol file format is documented here: link

DRW wrote:
The CC65 documentation said that fastcall is only useful if you write the function in Assembly and that it doesn't change anything if your function is written in C.

The documentation is a little bit muddled in this respect, as it does often result in smaller/faster code in pure C, though this depends on the contents of the C function itself.

DRW wrote:
Sure, but you're missing a very important detail here: The above code is just an example. I won't have just one palette array to copy. I will have a ton of arrays. One palette for each level, just to begin with. Then I'll have a whole background for each level. Column updates when the game scrolls. Etc.

I'm not missing that, I was suggesting it as an "aside", because sooner or later you will probably run into performance problem somewhere it matters. When that happens, I thought it might help to know a few tips on how to get more efficiency out of the C compiler just by writing your code a little different. When I was using cc65, every once in a while I would write some loop that caused horrible slowdown, but with some investigation and a tiny bit of rearrangement to be more cc65 compiler friendly it became efficient.

I definitely think you should learn why your current code doesn't work (and how to solve the problem), but I figured some other ideas might be useful for the future.

DRW wrote:
It might be faster. But using the above code 20 times in my program will fill the ROM pretty vastly.

In some cases it could compile to a similar amount of code as the function call when optimizations are on. All the parameter wrangling in the function call preamble eats up a lot of space on its own, so inline code might not be that bad by comparison. Not sure about this specific case (would have to test and review with the same compiler as you), so I could easily be wrong about it too. It's best to check up on it when it matters. My main point was that absolute addressing instead of pointers is usually a big win for efficiency in cc65.
Re: C function for copying data doesn't work in CC65
by on (#155022)
Alright, I haven't checked out the debugging features in fceux yet. But I created a really small sample program where the error can be seen.
I have removed every unnecessary stuff.

I will post the code below. The relevant stuff is explained with comments.
Maybe you see where I did something wrong and can tell me.

In the C code, change the #ifdef and #elif from 0 to 1 to execute another of the three variants.

The command line call is:
cl65 -o Test.nes -t nes -C NES.cfg Main.s Init.c


NES.cfg:
Code:
Removed. Updated in my next post.


Main.s:
Code:
Removed. Updated in my next post.


Init.c:
Code:
Removed. Updated in my next post.
Re: C function for copying data doesn't work in CC65
by on (#155024)
I've discovered another curiosity:

When I write this line as the last command in the InitializePalettesData function:
PalettesData[1] = (PalettesData[0] == PalettesData[0] ? 0xA : 0xE);
guess what the corresponding color will be? It will be $0E.

How is this possible? How can PalettesData[0] not be equal to itself?
Re: C function for copying data doesn't work in CC65
by on (#155062)
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. Also: please specify the cc65 version.)
Re: C function for copying data doesn't work in CC65
by on (#155068)
Looks like you're using a fairly old version of cc65, judging from SYMBOLS { __STACKSIZE__ = $0300; } (the syntax was changed slightly in later versions).
Re: C function for copying data doesn't work in CC65
by on (#155069)
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.
Re: C function for copying data doesn't work in CC65
by on (#155070)
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:
Warning: Uninitialized memory accessed: $0 (PC = $80AA)
Warning: Uninitialized memory accessed: $1 (PC = $80B8)
Re: C function for copying data doesn't work in CC65
by on (#155073)
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:
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?
Re: C function for copying data doesn't work in CC65
by on (#155074)
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
Re: C function for copying data doesn't work in CC65
by on (#155075)
O.k., I'll check it out.

It works now. Thanks.
Re: C function for copying data doesn't work in CC65
by on (#155077)
Quote:
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.
Re: C function for copying data doesn't work in CC65
by on (#155079)
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:
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
Re: C function for copying data doesn't work in CC65
by on (#155080)
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.
Re: C function for copying data doesn't work in CC65
by on (#155092)
dougeff wrote:
Quote:
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:
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:
   LDA #<(__RAM_START__ + __RAM_SIZE__)
   STA sp
   LDA #>(__RAM_START__ + __RAM_SIZE__)
   STA sp + 1

with the RAM in the config file being:
Code:
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.
Re: C function for copying data doesn't work in CC65
by on (#155146)
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.

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

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

Quote:
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.
Re: C function for copying data doesn't work in CC65
by on (#155152)
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?
Re: C function for copying data doesn't work in CC65
by on (#155162)
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).
Re: C function for copying data doesn't work in CC65
by on (#155184)
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.
Re: C function for copying data doesn't work in CC65
by on (#155235)
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.