My CC65 config file

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

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

My CC65 config file

Post by DRW »

Alright, I created the nes.cfg. I used the default one from the compiler as the basis and rainwarrior's from this thread to correct the values.

I removed all the stuff that didn't have an influence on the output file.
Stuff like file = %O, fill = yes, define = yes. Or type = ro and type = rw in the MEMORY section, since these values are set in SEGMENTS anyway.

Curiously, fill = yes was necessary for HEADER. But whether it was set or not made no difference for PRG_ROM.

So, can you please tell me if I've got everything or if I'm missing something?

For example, stuff that I omitted, but that's still important because it will make a difference later when my program is a bit bigger.

Also, I'm going to program some stuff of my game in C. So, if there's something necessary for that, please tell me as well.

My game is supposed to be a standard-sized mapper 0 program with non-switchable pattern tables. So, whatever is only necessary for games with another mapper, you don't need to list it.

Code: Select all

MEMORY
{
	HEADER:  start = $0000, size = $0010, fill = yes;
	PRG_ROM: start = $8000, size = $8000;
	CHR_ROM: start = $0000, size = $2000;
	ZP:      start = $0000, size = $0100;
	RAM:     start = $0300, size = $0500;
}

SEGMENTS
{
	HEADER:   load = HEADER,  type = ro;
	CODE:     load = PRG_ROM, type = ro;
	RODATA:   load = PRG_ROM, type = ro;
	CHARS:    load = CHR_ROM, type = ro;
	VECTORS:  load = PRG_ROM, type = ro, start = $FFFA;
	ZEROPAGE: load = ZP,      type = zp;
	BSS:      load = RAM,     type = bss;
}
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: 8763
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: My CC65 config file

Post by rainwarrior »

"fill = yes" is an important specification for both the PRG and CHR memory blocks. This is part of the iNES format specification; the PRG needs to a full 32k chunk (or some multiple of 16k anyway) or the CHR block won't be output in the right position. The CHR needs to be a full 8k chunk (or some multiple of 8k) too.

The only reason you don't notice a difference is because you've filled PRG to the end (i.e. because of the vectors placed there) and you've filled CHR to the end too (because you're including 8k of CHR). If either of these weren't completely filled, your ROM would be invalid. (Of course, without the vectors your ROM would be invalid too, but for a different reason.)

If you don't have "fill = yes" it means that block of output data in the ROM will have variable size (as big as the data you put in). This could be useful for an NSF, for example. For an NES ROM though, you need complete chunks or it's an invalid file.

To clarify, the MEMORY chunks define the layout of your output ROM, as well as RAM and where the system is expected to load them in memory (see: CPU memory map, and PPU memory map). The SEGMENT chunks only define the layout within the MEMORY chunks; they are one layer removed from directly controlling the output file.
User avatar
DRW
Posts: 2304
Joined: Sat Sep 07, 2013 2:59 pm

Re: My CC65 config file

Post by DRW »

Alright, I'll add the fill command. Thanks for the explanation.


What about that other stuff? Can file = %O have any influence on an NES ROM where there's only one file to begin with? And can it have a difference towards file = "" on the NES?

Or this whole define = yes stuff. I don't really understand what they are talking about:
http://www.cc65.org/doc/ld65-5.html wrote:But how does your code know, where the segment starts, and how big it is? The linker is able to give that information, but you must request it. This is, what we're doing with the "define = yes" attribute in the BSS definitions. For each segment, where this attribute is true, the linker will export three symbols.

__NAME_LOAD__ This is set to the address where the
segment is loaded.
__NAME_RUN__ This is set to the run address of the
segment. We will cover run addresses
later.
__NAME_SIZE__ This is set to the segment size.

Replace NAME by the name of the segment, in the example above, this would be BSS. These symbols may be accessed by your code.
What kind of strange stuff is this? "How does your code know where the segment starts and how big it is"? Isn't this what the properties start and size are used for? And even if there are two segments for one memory item, well, in this case the define property doesn't say anything about the size of that one particular segment either.

And why should you specify ro and rw in the memory section? Sure, from a physical point of view, that's where the difference takes place. But you cannot omit it in the segments anyway. When you omit it there, the compiler complains. So, is the additional redundant information in the memory section of any use if we have to declare the access rights in each segment anyway?

And finally: The default config file created this:

Code: Select all

FEATURES {
CONDES: segment = INIT,
type = constructor,
label = __CONSTRUCTOR_TABLE__,
count = __CONSTRUCTOR_COUNT__;
CONDES: segment = RODATA,
type = destructor,
label = __DESTRUCTOR_TABLE__,
count = __DESTRUCTOR_COUNT__;
CONDES: type = interruptor,
segment = RODATA,
label = __INTERRUPTOR_TABLE__,
count = __INTERRUPTOR_COUNT__;
}

SYMBOLS {
__STACKSIZE__ = $0300;
}
Is this necessary when I program in C? For example, isn't the code from the Reset interrupt already the code to establish the stack size?

Code: Select all

   LDX #$FF
   TXS
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: the universe
Contact:

Re: My CC65 config file

Post by thefox »

DRW wrote:What about that other stuff? Can file = %O have any influence on an NES ROM where there's only one file to begin with?
It's unnecessary. You only need to use file if you plan to output data to multiple files.
What kind of strange stuff is this? "How does your code know where the segment starts and how big it is"? Isn't this what the properties start and size are used for? And even if there are two segments for one memory item, well, in this case the define property doesn't say anything about the size of that one particular segment either.
The purpose of those symbols is for the code to be able to know the sizes/addresses of the segments/memory areas. You can import the symbols from your source files and get access to that information. The symbols can also tell the exact amount of data that was produced into a segment (only knowable at link time). This way e.g. the C initialization code doesn't have to be tightly coupled to a particular linker configuration.
And why should you specify ro and rw in the memory section? Sure, from a physical point of view, that's where the difference takes place. But you cannot omit it in the segments anyway. When you omit it there, the compiler complains. So, is the additional redundant information in the memory section of any use if we have to declare the access rights in each segment anyway?
Those attributes are only used to check for mistakes in the segment assignments. That is to say, you can't assign segments to memory areas if their attributes aren't compatible. Same is true for segment attributes like "bss", the linker will warn you if you try to put initialized data in such a segment. It's just error checking, doesn't affect the produced code.

Code: Select all

SYMBOLS {
__STACKSIZE__ = $0300;
}
Is this necessary when I program in C? For example, isn't the code from the Reset interrupt already the code to establish the stack size?

Code: Select all

   LDX #$FF
   TXS
There are two different stacks. There's the hardware stack of 6502 (256 bytes), and there's a software stack used by cc65. __STACKSIZE__ specifies the size for the software stack. As for the constructor/destructor/interruptor stuff, I don't think any of the default C stuff should require them, but I can't remember for sure.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
DRW
Posts: 2304
Joined: Sat Sep 07, 2013 2:59 pm

Re: My CC65 config file

Post by DRW »

Alright, thanks for the information.
thefox wrote:The purpose of those symbols is for the code to be able to know the sizes/addresses of the segments/memory areas.
O.k., I don't think I'll ever need this.
thefox wrote:__STACKSIZE__ specifies the size for the software stack.
So, if I omit it, does that mean the CC65 stack is zero or does CC65 use a default value?

The stack is for local variables and function parameters, right? If yes, I'll have to see in how far I even use them in an NES program.
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: the universe
Contact:

Re: My CC65 config file

Post by thefox »

DRW wrote:
thefox wrote:__STACKSIZE__ specifies the size for the software stack.
So, if I omit it, does that mean the CC65 stack is zero or does CC65 use a default value?
Dunno. My guess is that if you omit it, it will give you a linker error since that symbol is probably used somewhere in the startup code.
The stack is for local variables and function parameters, right? If yes, I'll have to see in how far I even use them in an NES program.
Yes. FYI, there's also a command line switch that will tell the compiler to make all local variables static, in which case the stack won't be used for those (with some consequences).
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
rainwarrior
Posts: 8763
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: My CC65 config file

Post by rainwarrior »

thefox wrote:
DRW wrote:What about that other stuff? Can file = %O have any influence on an NES ROM where there's only one file to begin with?
It's unnecessary. You only need to use file if you plan to output data to multiple files.
The file = "" was important for the ZP and RAM memory blocks. Otherwise they'll be output as part of the file, won't they? (I don't think most emulators would complain about extra bytes hanging off the end of the file, but it's technically invalid.)
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: the universe
Contact:

Re: My CC65 config file

Post by thefox »

thefox wrote:
DRW wrote:So, if I omit it, does that mean the CC65 stack is zero or does CC65 use a default value?
Dunno. My guess is that if you omit it, it will give you a linker error since that symbol is probably used somewhere in the startup code.
Double-checked this. It's not used by the startup code, but is used by the heap module (and maybe by the optional stack overflow check code?). Whatever may be the case, you should specify it.
rainwarrior wrote:
thefox wrote:It's unnecessary. You only need to use file if you plan to output data to multiple files.
The file = "" was important for the ZP and RAM memory blocks. Otherwise they'll be output as part of the file, won't they? (I don't think most emulators would complain about extra bytes hanging off the end of the file, but it's technically invalid.)
Hmm, I've never specified it for ZP/RAM and haven't had any problems, but now that you asked I'm not entirely sure why it works. I know BSS segments don't output any data to the output file, but not sure why a "zp" segment would behave in the same way.

EDIT: Looks like segments of type "zp" are implicitly "bss". So there's the explanation. If I try to put initialized data into a "zp" segment, I get this warning: ld65: Warning: C:/dev/ngin/src/ngin/linker/nrom.cfg(173): Segment `ZEROPAGE' with type `bss' contains initialized data (It says "bss" even though the type is "zp".)
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
DRW
Posts: 2304
Joined: Sat Sep 07, 2013 2:59 pm

Re: My CC65 config file

Post by DRW »

thefox wrote:My guess is that if you omit it, it will give you a linker error since that symbol is probably used somewhere in the startup code.
I don't have any C startup code and I don't use any external #includes. I initialize my whole program in Assembler. The C compiler only converts simple general functions and variables.
thefox wrote:FYI, there's also a command line switch that will tell the compiler to make all local variables static, in which case the stack won't be used for those (with some consequences).
Yeah, I know, but I'm not so fond of these compiler switches that change the behavior of the code. If I want a static local variable, I declare it as such in the code.
rainwarrior wrote:The file = "" was important for the ZP and RAM memory blocks. Otherwise they'll be output as part of the file, won't they? (I don't think most emulators would complain about extra bytes hanging off the end of the file, but it's technically invalid.)
The option didn't seem to make any difference in the output file at all, not even some extra bytes. I'll have to check it again.
Also, shouldn't the fact that you declare these segments with type = zp and type = bss be enough for the compiler to know that this is not ROM data that goes into the file?
thefox wrote:Double-checked this. It's not used by the startup code, but is used by the heap module (and maybe by the optional stack overflow check code?). Whatever may be the case, you should specify it.
Is the heap module something that is included regardless or is it something that I have to use with #include?

Also, isn't the heap the location that is used when you create a variable with the new keyword in C++ and with malloc in C? I'm 100% sure that I will not use this in an NES program ever.

Also: Why is the stack size used in the heap module?
thefox wrote:Hmm, I've never specified it for ZP/RAM and haven't had any problems, but now that you asked I'm not entirely sure why it works. I know BSS segments don't output any data to the output file, but not sure why a "zp" segment would behave in the same way.
Because the type of the ZP segment is declared as type = zp and the compiler therefore knows that it is supposed to be RAM memory?
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: the universe
Contact:

Re: My CC65 config file

Post by thefox »

DRW wrote:
thefox wrote:My guess is that if you omit it, it will give you a linker error since that symbol is probably used somewhere in the startup code.
I don't have any C startup code and I don't use any external #includes. I initialize my whole program in Assembler. The C compiler only converts simple general functions and variables.
You may run into problems with that approach later if the system isn't initialized in a way that the compiler expects. BTW by "C startup code" I meant assembly code that sets up the environment for C.
thefox wrote:FYI, there's also a command line switch that will tell the compiler to make all local variables static, in which case the stack won't be used for those (with some consequences).
Yeah, I know, but I'm not so fond of these compiler switches that change the behavior of the code. If I want a static local variable, I declare it as such in the code.
It doesn't change behavior apart from disallowing reentrancy (including recursion). In particular the initialization behavior is different from a "normal" static local variable (normal ones are initialized at program startup, the ones produced with the switch are initialized on function entry).
thefox wrote:Double-checked this. It's not used by the startup code, but is used by the heap module (and maybe by the optional stack overflow check code?). Whatever may be the case, you should specify it.
Is the heap module something that is included regardless or is it something that I have to use with #include.

Also, isn't the heap the location that is used when you create a variable with the new keyword in C++ and with malloc in C? I'm 100% sure that I will not use this in an NES program ever.
No, it won't be included unless you use the heap-related functions. Feel free to leave __STACKSIZE__ undefined if the compiler allows you to, but you should at the very least be aware of the existence of the software stack and where it is placed.
thefox wrote:Hmm, I've never specified it for ZP/RAM and haven't had any problems, but now that you asked I'm not entirely sure why it works. I know BSS segments don't output any data to the output file, but not sure why a "zp" segment would behave in the same way.
Because the type of the ZP segment is declared as type = zp and the compiler therefore knows that it is supposed to be RAM memory?
Yeah, but it's not so obvious because for example the "rw" segment type can also be in RAM at runtime, and still output the initialization values of the segment to ROM.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
DRW
Posts: 2304
Joined: Sat Sep 07, 2013 2:59 pm

Re: My CC65 config file

Post by DRW »

thefox wrote:You may run into problems with that approach later if the system isn't initialized in a way that the compiler expects. BTW by "C startup code" I meant assembly code that sets up the environment for C.
I didn't know that I have to do anything specific here. Is there anything besides the stack size in the CFG file that I need to set up? Or is there maybe a document about it?
thefox wrote:It doesn't change behavior apart from disallowing reentrancy (including recursion). In particular the initialization behavior is different from a "normal" static local variable (normal ones are initialized at program startup, the ones produced with the switch are initialized on function entry).
I know. A local static variable is basically a global variable, only that the compiler prevents you from using it outside the function. But this is already changed behavior, as you already demonstrated: Calling function B in function A and then calling function A in function B has a different behavior if the variable is static vs. if it is non-static. And as I said: If I want that, I would never use a compiler switch. I would declare the variable as static in the code. Everything else is unsafe in my opionion.
thefox wrote:No, it won't be included unless you use the heap-related functions. Feel free to leave __STACKSIZE__ undefined if the compiler allows you to, but you should at the very least be aware of the existence of the software stack and where it is placed.
I'll comment it out and see if there's some problem in the future. Also, I guess I'll read about this topic a bit.
thefox wrote:Yeah, but it's not so obvious because for example the "rw" segment type can also be in RAM at runtime, and still output the initialization values of the segment to ROM.
Sure, "rw" is file-related. But I guess that's why they specified "bss" and "zp": Specifically to let the compiler know that this is never data that goes into the ROM. Therefore, file = "" is probably redundant here.
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: the universe
Contact:

Re: My CC65 config file

Post by thefox »

DRW wrote:I didn't know that I have to do anything specific here. Is there anything besides the stack size in the CFG file that I need to set up? Or is there maybe a document about it?
You can check what the default startup code (crt0.s) does.
Sure, "rw" is file-related. But I guess that's why they specified "bss" and "zp": Specifically to let the compiler know that this is never data that goes into the ROM. Therefore, file = "" is probably redundant here.
It's definitely redundant. But it's not like the zp initialization data couldn't be output to a file, which was why I said it wasn't obvious that it was implicitly "bss".
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
tepples
Posts: 22915
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: My CC65 config file

Post by tepples »

DRW wrote:And even if there are two segments for one memory item, well, in this case the define property doesn't say anything about the size of that one particular segment either.
In what way doesn't it? You put two segments with their load addresses in the same memory area and unspecified start and size, and the linker returns their start and size in a symbol. This is especially important if you're trying to put a small amount of code in RAM as a trampoline to get from one bank to another on a mapper that can switch the entire 32K, as it lets your init code know from where to copy the code, to where, and how much. (Such mappers include AOROM, BNROM, GNROM, Color Dreams, and multicart mappers.)
And finally: The default config file created this:
[stuff]
Is this necessary when I program in C?
Because C allows a function to call itself, whether directly or indirectly, automatically allocated variables need to be put on a bigger stack. This stack is accessed through a stack pointer on zero page using the (d),Y (zero page indirect indexed with Y) addressing mode.
User avatar
DRW
Posts: 2304
Joined: Sat Sep 07, 2013 2:59 pm

Re: My CC65 config file

Post by DRW »

tepples wrote:In what way doesn't it? You put two segments with their load addresses in the same memory area and unspecified start and size, and the linker returns their start and size in a symbol.
I understood it when thefox explained it. I was under the impression that you tell the compiler something with those things. But it turned out that this is the compiler declaring a constant for you that you can use in your code. And now I understand the concept.
tepples wrote:Because C allows a function to call itself, whether directly or indirectly, automatically allocated variables need to be put on a bigger stack. This stack is accessed through a stack pointer on zero page using the (d),Y (zero page indirect indexed with Y) addressing mode.
Alright, so it is indeed for local variables. That's also something that I didn't know until some posts ago: That there are is a hardware and a software stack. That's why I was a bit confused because I thought the stack is already in the Reset interrupt.
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: 8763
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: My CC65 config file

Post by rainwarrior »

To explain about file = "", it's not critical for a similar reason that fill = yes wasn't critical on PRG or CHR. Those MEMORY blocks will output data to the file if you ever accidentally put data in them. Segments of type bss or zp do not create data, so it's true that if they are the only SEGMENTs you put in those MEMORY regions, they won't contribute to the file, but there's no checks to prevent putting anything else in that MEMORY block. (Also, putting fill = yes on an otherwise empty MEMORY block would force it to be filled with the fillval.)

So, yeah, it's not a problem as long as your SEGMENTs are correct, but adding additional specifications like type and file to your MEMORY blocks can prevent errors when writing your SEGMENTs. Also type = ro on a MEMORY block will ensure that you never assign a bss/zp segment to it.

So... yes it's not minimally necessary as long as you do everything else correctly, but being more explicit about what each MEMORY is for can help catch errors. Though, file = "" wouldn't produce a linker error, it just ensures that block never writes to the file. (Maybe you'd prefer a corrupt ROM to demonstrate the error instead, though. Putting the RAM/ZP blocks first would do that.)

I actually find file = "" really useful in some other situations, like if I have a multi-bank ROM and I am linking each bank separately. This is a bit more complicated purpose though.
Post Reply