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

CC65, the CFG file and system variables

CC65, the CFG file and system variables
by on (#164176)
When you program in C with CC65, it needs some variables to do certain stuff: ptr1, ptr2, ptr3, sp and maybe some other things.

My question: How are these variables stored in memory and do I have to adjust my config file accordingly, so that my variables don't overlap with them?

To elaborate:

Are these variables stored in absolute memory locations in the zero page, so that I have to declare my zero page segment as going from address $0008 and being 248 bytes long instead of starting at $0000 and being 256 bytes long?

Or do these variables internally use the segment names as well, like this:
Code:
.segment "ZEROPAGE"
    prt1: .res 2
    prt2: .res 2
    prt3: .res 2
    sp: .res 2
so that they can never overlap with my own variables if I use the segment declaration?
Re: CC65, the CFG file and system variables
by on (#164177)
They are allocated at link time, just like your own ZP variables, so they will never overlap. You can move and resize the ZP segment if you want just fine.

I use a full ZP segment, ie start at 0 and 256 bytes.

If you want to see where they end up, see the linker's map file.
Re: CC65, the CFG file and system variables
by on (#164178)
O.k., that's good. Thanks for the information. I will also check the map file.
Re: CC65, the CFG file and system variables
by on (#164179)
See https://github.com/cc65/cc65/blob/maste ... zeropage.s (you can also find the rest of the runtime library implementation in that directory, if you're curious)
Re: CC65, the CFG file and system variables
by on (#164180)
What I don't understand: Why do they use .zeropage while I learned to use .segment "ZEROPAGE"? How can the first version even work if I need to name all segments in the config file?
Re: CC65, the CFG file and system variables
by on (#164182)
DRW wrote:
What I don't understand: Why do they use .zeropage while I learned to use .segment "ZEROPAGE"? How can the first version even work if I need to name all segments in the config file?

Both are equivalent (.zeropage is a convenience shorthand).
Re: CC65, the CFG file and system variables
by on (#164183)
So, what if I split the zero page into various segments? Do the variables from .zeropage go to the first possible location or do they go to .segment "ZEROPAGE"? In case the latter is true: What if I don't have a segment called "ZEROPAGE", but use my own names?
Re: CC65, the CFG file and system variables
by on (#164184)
DRW wrote:
So, what if I split the zero page into various segments? Do the variables from .zeropage go to the first possible location or do they go to .segment "ZEROPAGE"? In case the latter is true: What if I don't have a segment called "ZEROPAGE", but use my own names?

.zeropage is equivalent to .segment "ZEROPAGE", so they'd go in the "ZEROPAGE" segment. If you don't have that segment in your linker config, you'll get a linker error.
Re: CC65, the CFG file and system variables
by on (#164190)
When you make up your own .segment names, ca65 assumes that an unknown name is absolute (that is, that it uses a 16-bit address). You have to tell ca65 the first time you use a segment in a particular file that it is a zero page segment. The manual gives the example:
Code:
.segment "ZP2": zeropage

After it's used for the first time, you can switch to it normally:
Code:
.segment "ZP2"

So you might want to have an include file that switches to all the made-up zero page segments you'll be using>
Code:
.pushseg
.segment "ZP2": zeropage
.segment "ZP3": zeropage
.segment "ZP4": zeropage
.popseg  ; switch back
Re: CC65, the CFG file and system variables
by on (#164191)
Why should this be necessary? Isn't it enough to write type = zp into the corresponding segment in the config file?
Re: CC65, the CFG file and system variables
by on (#164194)
The assembler needs to know at assembly time, which precedes link time, whether addresses shall be 1 byte or 2 bytes.
Re: CC65, the CFG file and system variables
by on (#164195)
Then what is type = zp good for anyway? Also, I'm using only the configuration version, so does that mean that I'm doing something wrong?
Re: CC65, the CFG file and system variables
by on (#164221)
If you don't declare the segment properly as zeropage in the assembly source, it means that all your instructions that access ZP variables will contain an extra byte, and take extra cycles, because the assembler will be using the absolute address form instead. It's really worth typing those extra 10 characters in.
Re: CC65, the CFG file and system variables
by on (#164295)
O.k., I will try it out and see what the compiler does.

Explicitly declaring a segment as zeropage is not necessary for the segment that's actually called "ZEROPAGE", right? Only for custom segments, I assume.
Re: CC65, the CFG file and system variables
by on (#164298)
You can use the linker config to put as many segments as you want into the zeropage range.

The linker knows what to do from the config, but the assembler needs to know separately (it does not have a config to look at). The "ZEROPAGE" segment is the only segment that the assembler will know sits there by default. Any other segment that is intended for the zeropage requires the annotation in your assembly code as already mentioned (".segment "THING" : zeropage).
Re: CC65, the CFG file and system variables
by on (#164415)
I tried it out, declaring a separate zeropage segment, and it indeed makes a difference whether pushseg/popseg is used or not.


But I just noticed that my actual custom zeropage stuff in the program is a special case, so I'd need to know how to do that:

The custom zeropage variables are the ones for FamiTone. But FamiTone doesn't work with segments. Instead, you have to tell the library where it can find the zero page variables:
Code:
; FT_TEMP         = $00   ;3 bytes in zeropage used by the library as a scratchpad


And then it works like this:
Code:
FT_TEMP_PTR         = FT_TEMP      ;word
FT_TEMP_PTR_L      = FT_TEMP_PTR+0
FT_TEMP_PTR_H      = FT_TEMP_PTR+1
FT_TEMP_VAR1      = FT_TEMP+2


So, I did the following:

This is the zeropage part of my config file:
Code:
MEMORY
{
   ZP:      type = rw, start = $0000, size = $0100;
}

SEGMENTS
{
   ZEROPAGE:          load = ZP,      type = zp;
   ZEROPAGE_FAMITONE: load = ZP,      type = zp,  start = $00FD, define = yes;
}


And this is my corresponding code:
Code:
   .import __ZEROPAGE_FAMITONE_LOAD__

   FT_TEMP = <(__ZEROPAGE_FAMITONE_LOAD__)

.segment "ZEROPAGE_FAMITONE"
   ; Nothing here. Just declared so that no warning appears.

.segment "CODE"
   .include "famitone2.s"


Now, my question: Do I have to do something special to make sure that the compiler actually treats the FamiTone zeropage variables as zeropage?

When I completely remove ZEROPAGE_FAMITONE from the config file and simply declare FT_TEMP = $FD, then the output is still identical to my original code.
Likewise, when I use
Code:
.pushseg
.segment "ZEROPAGE_FAMITONE": zeropage
.popseg
then it's the same as well.

So, is there anything to do here? How does the compiler/assembler know that it is a zeropage variable in this case when no segments are used at all, but the variables point to absolute addresses in memory?
Re: CC65, the CFG file and system variables
by on (#164416)
The segments are there so the assembler can map everything to the appropriate memory locations, but when you do FT_TEMP = $FD you're selecting a memory location yourself, completely bypassing the assembler's logic. If you're gonna do that, there's no need to use segments.
Re: CC65, the CFG file and system variables
by on (#164417)
I still need to use segments because FamiTone is the only part in my program that bypasses the segments. This was not my decision, but is just how FamiTone works. For everything else, I of course use the regular segments.

So, I still have to declare this:
Code:
SEGMENTS
{
   ZEROPAGE:          load = ZP,      type = zp;
   ZEROPAGE_FAMITONE: load = ZP,      type = zp,  start = $00FD, define = yes;
}

to make sure that my regular zeropage variables and the three FamiTone zeropage variables don't overlap.

Also, I'm not using $FD directly. I'm using FT_TEMP = <(__ZEROPAGE_FAMITONE_LOAD__), a value which is itself unknown to the compiler and can only be resolved by the linker.

So, how does the compiler know that FT_TEMP is in the zero page?
Re: CC65, the CFG file and system variables
by on (#164425)
Quote:
So, how does the compiler know that FT_TEMP is in the zero page?



I do this...

Code:
.segment "ZEROPAGE"

NTSC_MODE:          .res 1
FRAME_CNT1:       .res 1
FRAME_CNT2:       .res 1
FT_TEMP:          .res 3
TEMP:             .res 11


and the compiler will assign it a value in the zeropage.



Also,

Quote:
__ZEROPAGE_FAMITONE_LOAD__, a value which is itself unknown to the compiler


have you tried...

.importzp __ZEROPAGE_FAMITONE_LOAD__

(actually, I'm kind of unfamiliar with what you're trying to do here, so you can ignore me.)
Re: CC65, the CFG file and system variables
by on (#164430)
DRW wrote:
Also, I'm not using $FD directly. I'm using FT_TEMP = <(__ZEROPAGE_FAMITONE_LOAD__), a value which is itself unknown to the compiler and can only be resolved by the linker.
So, how does the compiler know that FT_TEMP is in the zero page?

Sounds like the assembler won't, by default.

If you just declare FT_TEMP = $FD, this declaration isn't within a segment, it's just a constant value. It's not a place in memory, it's just a number (that you can use to access a place in memory). A constant less than $100 used to access memory is known to be on the ZP, so the assembly will do the correct thing with that.

If the value of FT_TEMP depends on __ZEROPAGE_FAMITONE_LOAD__, the assembler will presume it's a 16-bit value while assembling. I'm not sure if it would successfully assemble Famitone like this, since I presume it will use FT_TEMP for ZP-only instructions? I'd expect an error here. Edit: Oh, I see you put < around it. I think that might partially work, see next paragraph.

Since __ZEROPAGE_FAMITONE_LOAD__ is an import, you could use .importzp __ZEROPAGE_FAMITONE_LOAD__, which might make it work? There's two imports. .importzp is for 8-bit values, .import is for 16-bit values. I don't know what happens when you start assigning a ZP import to constants and doing math on them, though. Do assignments retain the "ZP" property? Do they promote to 16-bit if you put a +1 on them? Would have to investigate the produced code. Edit: the same applies to your constant assigned from <. The assembler shouldn't know at this point if FT_TEMP was $FF, so FT_TEMP+1 could potentially promote to 16-bit. If FT_TEMP is just a single 2-byte pointer, probably the fetch instructions (e.g. lda (FT_TEMP), Y) are correctly ZP, but the pointer assignments (e.g. sta FT_TEMP+1) might be incorrectly 16-bit.


Personally, what I'd do is probably:

1. Just declare FT_TEMP = $FD directly. That way the assembler knows it's ZP, and you're not changing the semantics of Famitone's existing code.

Then optionally:

2. Make a dummy allocation within ZEROPAGE_FAMITONE so that it has reserved space (.segment "ZEROPAGE_FAMITONE"; .res FAMITONE_ZP_SPACE).
3. Use a link-time assert that makes sure you don't accidentally move it without fixing the direct assignment. (.assert(FT_TEMP + FAMITONE_ZP_SPACE < $100, "Famitone ZP incorrectly allocated.")

This way you can avoid trying to alter the Famitone code much, but still have the linker police its assignments in case you set something up wrong.
Re: CC65, the CFG file and system variables
by on (#164431)
Huh, I had no idea Famitone2 had a hardcoded pointer overwriting ZP vars. Shiru needs a kick in the arse for hiding a surprise like that.

edit: false alarm, it was already a normal ZP .res.
Re: CC65, the CFG file and system variables
by on (#164432)
Looking at the Famitone source, it puts < on all the uses of FT_TEMP stuff so it's manually forcing ZP anyway. So... it should assemble correctly even without the assembler knowing if it's 8-bit at assemble time.

Since according to the Famitone source FT_TEMP is manually defined by you, what dougeff suggested is the simplest, I think.

Code:
.segment "ZEROPAGE"
FT_TEMP: .res 3


No need to create a second segment for it. No need to import linker defined symbols. You can just reserve it yourself. The assembler will know everything it need to from that.
Re: CC65, the CFG file and system variables
by on (#164568)
Thanks for the tip. Yeah, stylistically, this is probably the cleanest thing.

Unfortunately, the non-zeropage variables for FamiTone need to be put at an offset of $100, otherwise the sound doesn't work properly. Which means if I don't want to waste any variables, I have to create a segment for FamiTone at the start of the RAM and then I have to manually specify how much space is needed. So, with each new version of FamiTone, I have to check if the size is still correct.
If the whole offset $100 wasn't necessary, I would have just put the BSS_FAMITONE segment after the BSS segment, so I wouldn't need to bother about the needed size for FamiTone.