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

My CC65 config file

My CC65 config file
by on (#152868)
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:
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;
}
Re: My CC65 config file
by on (#152884)
"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.
Re: My CC65 config file
by on (#152897)
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:
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:
   LDX #$FF
   TXS
Re: My CC65 config file
by on (#152899)
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.

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

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

Quote:
Code:
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:
   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.
Re: My CC65 config file
by on (#152900)
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.
Re: My CC65 config file
by on (#152901)
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.

Quote:
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).
Re: My CC65 config file
by on (#152903)
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.)
Re: My CC65 config file
by on (#152905)
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".)
Re: My CC65 config file
by on (#152906)
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?
Re: My CC65 config file
by on (#152909)
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.

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

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

Quote:
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.
Re: My CC65 config file
by on (#152910)
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.
Re: My CC65 config file
by on (#152911)
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.

Quote:
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".
Re: My CC65 config file
by on (#152915)
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.)

Quote:
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.
Re: My CC65 config file
by on (#152918)
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.
Re: My CC65 config file
by on (#152923)
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.
Re: My CC65 config file
by on (#152934)
There is something else that I noticed:

Code:
MEMORY {
    HDR:    start = $0000,  size = $0010, type = ro, file = %O, fill = yes, fillval = $00;
    CHR:    start = $0000,  size = $2000, type = ro, file = %O, fill = yes, fillval = $00;
}

SEGMENTS {
    HEADER:   load = HDR, type = ro;
    TILES:    load = CHR, type = ro;
}

How does the compiler distinguish between the fact that the header is something that only goes into the beginning of the file at the specified location while the tiles are something that belong into the actual cartridge at that place? Wouldn't one of them overwrite the other in the .nes file? Yet, everything is correct. How does that work?
Re: My CC65 config file
by on (#152938)
DRW wrote:
How does the compiler distinguish between the fact that the header is something that only goes into the beginning of the file at the specified location while the tiles are something that belong into the actual cartridge at that place? Wouldn't one of them overwrite the other in the .nes file?

For the same reason that it can tell the seven banks destined for $8000 apart in a game that uses UNROM. Each item in MEMORY is theoretically in a separate address space. They don't overlap in the file because they come one after another, in the order that they appear in MEMORY. ld65 has no concept of an "actual cartridge", just an output file.
Re: My CC65 config file
by on (#152943)
The MEMORY blocks state explicitly what goes into the file. They write out to the file in the same order they're listed. If you put the header first in the list, it appears first in the file.

The "start" field is only to tell the linker what addresses to assign in the generated code. It doesn't actually affect the output file layout.

The "size" field either directly specifies the size of the block to be output to the file (if "fill=yes"), or it specifies the maximum size it could have (if "fill=no").


So in the example you just gave, HDR will output to the first 16 bytes of the file, and CHR will be the next 8k.
Re: My CC65 config file
by on (#152947)
Some info on constructors (you don't need to ever use these, at least with assembly): viewtopic.php?f=2&t=9983