CC65, FamiTone: Constant values unknown at compile-time

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
CC65, FamiTone: Constant values unknown at compile-time
by on (#169752)
I wanted to include sampled sounds into my game and while I managed to do so, I encountered a little stylistic issue:

Since FamiTone requires the samples to be at ROM address $C000..$FFC0, in 64-byte steps, I originally intended to do a separate segment for it, with an alignment to ensure the correct location:
Code:
RODATA:   load = PRG_ROM, type = ro;
SAMPLES:  load = PRG_ROM, type = ro,  align = $0040, define = yes;
VECTORS:  load = PRG_ROM, type = ro,  start = $FFFA;

And then I would have done this in Assembly:
Code:
.import __SAMPLES_LOAD__

FT_DPCM_OFF = __SAMPLES_LOAD__
; That's the constant that tells FamiTone where to look for the samples

.segment "SAMPLES"
    .incbin "Music.dmc"

But the problem is: When FamiTone creates the Assembly file and the DMC file out of the FamiTracker text export, then the code for the samples includes this:
Code:
@samples:
    .byte $00+FT_DPCM_PTR,$00,$00   ;1
    .byte $00+FT_DPCM_PTR,$00,$00   ;2
    ; ...
    .byte $00+FT_DPCM_PTR,$58,$08   ;25
    ; ...

FT_DPCM_PTR is a value that is calculated out of FT_DPCM_OFF. But if FT_DPCM_OFF is set to a value that isn't known at compile time, but only gets set at link time (in our case __SAMPLES_LOAD__), then the compiler gives a range error.

If I use a fixed value, then everything works file:
Code:
SAMPLES:  load = PRG_ROM, type = ro,  start = $F800;
Code:
FT_DPCM_OFF = $F800


But there are two issues with it:

Firstly, the address value is written in two different locations, i.e. we have a redundancy.

Secondly:
If I still want to be able to use all of my ROM space, I would have to put the samples at the end. And whenever a sample changes in my FamiTracker file, I would have to manually check its size, then subtract the size from $FFFA (the VECTORS address), then align it with $40 and put this value into my config file and my code manually.

Which is not very elegant.
Due to the config file, I never had to worry about absolute addresses. Changing details in songs or sound effects never had any influence on my code. But now, whenever I change a sample, I would have to alter values in my source code.

Is there any solution to this?
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169754)
What's the range error? If it's just on the .byte lines you can force them into byte range with "<". (You can also use ".feature force_range" to disable range checking, if you like.) Though I would expect range checking to work at link time, so there might be more to look at there; maybe put a redundant range check using .assert just to make sure? If there is some weird issue with linker generated symbols like __SAMPLE_LOAD__ (not sure if these are "special" in some way) you could just use a regular label (e.g. if you took that @ off @samples:) which I'm sure is usable for this purpose if __SAMPLE_LOAD__ isn't for some reason.

As for putting samples at the end, ld65 doesn't have any kind of elegant "right align" feature. If you put your vectors in their own segment with "start=$FFFA" and put your samples in their own segment that is the last segment before vectors. If it's the last thing in the list, they'll always end up between there and the vectors. The only thing that's a problem is if everything else currently does not go past $C000, in which case you would need "start=$C000" (instead of align) in your DPCM segment until this space is taken up, or partition your ROM into two 16k memory regions, or some other solution.
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169755)
The range error doesn't have a further explanation.

It isn't actually a range error in the way that there's an incorrect value. It's simply the fact that the .byte command cannot handle a value that isn't known in the same translation unit.

You can easily recreate this phenomenon:
Put the following code in any random Assembly file:
Code:
CONSTANT_EXPRESSION = 42
.byte CONSTANT_EXPRESSION

Everything is fine.

But now write this in one code file:
Code:
CONSTANT_EXPRESSION = 42
.export CONSTANT_EXPRESSION
and this in the other:
Code:
.import CONSTANT_EXPRESSION
.byte CONSTANT_EXPRESSION

The compiler will produce the range error because .byte CONSTANT_EXPRESSION reserves ROM space whose value isn't known yet.


And that's my issue:
In my case, the value at .byte is part of the address to the sample data. But the address to the sample data is declared as a segment in the NES.cfg Which means its actual address is only set at link time, unless I go the dirty way and simply declare an absolute value for it (which goes against the purpose of a cfg file in the first place).
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169756)
rainwarrior wrote:
As for putting samples at the end, ld65 doesn't have any kind of elegant "right align" feature.

That's not my problem. My problem is that the start address of the samples is set by the linker. But the way FamiTone works, it is already needed by the compiler. So, in the moment, I set a definite, constant value. But that's not good because I would have to manually recalculate the value whenever I change something in the samples, so that the value is always as right as possible.

Otherwise, if I just declare any random value, my sample would be right in the middle of the free space, blocking the usage of the space that comes after it:
Attachment:
Space.PNG
Space.PNG [ 8.05 KiB | Viewed 2297 times ]
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169758)
If range checking is assemble-time only, then there's three easy ways to deal with this, two of which I already mentioned:

1. .importzp can be used to import byte-sized symbols. (Applicable to your contrived example, not the original problem you posted.)

2. ".byte <thing" automatically takes the low byte of "thing", forcing it into range. (Again I'd suggest an .assert to manually recreate the range check here.)

3. ".feature force_range" gets rid of range checking altogether, in case you think it's a stupid or badly implemented feature. (I sometimes do.)


If the range error is solved, do you even have the segment problem anymore?
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169762)
rainwarrior wrote:
If range checking is assemble-time only, then there's three easy ways to deal with this, two of which I already mentioned but you don't acknowledge I guess

You just don't understand my problem.

rainwarrior wrote:
1. .importzp can be used to import byte-sized symbols. (Applicable to your contrived example, not the original problem you posted.)

That's not the issue. The size of the data has nothing to do with it.

To put it bluntly: When I write this:
Code:
.import CONSTANT_EXPRESSION
.byte CONSTANT_EXPRESSION

then the compiler basically tells me:
"You want me to reserve a byte in ROM and put the value CONSTANT_EXPRESSION there. But I don't know what the value CONSTANT_EXPRESSION is. It doesn't exist in my translation unit. You can ask me to use addresses that are not known yet because the linker will take care of them. But if you want me to put actual fixed data into the ROM, then I need to know the exact value".

rainwarrior wrote:
2. ".byte <thing" automatically takes the low byte of "thing", forcing it into range. (Again I'd suggest an .assert to manually recreate the range check here.)

The .byte code is not written by me. It's the automatically created code by FamiTone. And it doesn't really have anything to do with address checking (one bytes/two bytes) in the first place. It has to do with the fact that reserving a piece of ROM data requires the compiler to know what value it shall write in this place.
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169763)
rainwarrior wrote:
As for putting samples at the end, ld65 doesn't have any kind of elegant "right align" feature. If you put your vectors in their own segment with "start=$FFFA" and put your samples in their own segment that is the last segment before vectors.


Do this. It's what I did in a recent project, works fine.

Code:
    DMC:                start = $f900, size = $06fa, file = %O, fill = yes;
    VECTORS:            start = $fffa, size = $0006, file = %O, fill = yes;


Here my samples were a bit less than 0x6fa bytes. It starts at a correct pos, and I then set 0xf900 as the offset var value.
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169764)
DRW wrote:
It isn't actually a range error in the way that there's an incorrect value. It's simply the fact that the .byte command cannot handle a value that isn't known in the same translation unit.

That's because whether it would be a range error or not is not known at assembly time. You can give it clues using .importzp CONSTANT_EXPRESSION, and then changing the file that defines the value of CONSTANT_EXPRESSION to use .exportzp instead of .export. In the case of FamiTone, you could edit the converter's source code and recompile the converter, or you could write some sort of post-processor that converts select .export lines to .exportzp lines.
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169766)
DRW wrote:
That's not the issue. The size of the data has nothing to do with it.

ca65 range errors are entirely about the size of the value.

All values are 16-bit by default, and considered unsigned. Unless they are declared explicitly as 8-bit (.importzp, or <) they will be considered 16-bit. In cases where the value is known, it may truncate implicitly to 8-bit.

The forcing of unsigned is why I think the range checking feature is stupid, sometimes. I can't write ".byte -1" because it considers it to be the same thing as 65535. This is why I am often willing to just disable the feature.

DRW wrote:
The .byte code is not written by me. It's the automatically created code by FamiTone. And it doesn't really have anything to do with address checking (one bytes/two bytes) in the first place. It has to do with the fact that reserving a piece of ROM data requires the compiler to know what value it shall write in this place.

So I told you how you can fix that code, and you're unwilling to do so because it was automatically generated.

My third suggestion (disabling the force_range feature) would actually fix this problem for you without changing the code generator, but you'd rather complain to me that I don't understand your problem and that my suggestion didn't fix the code generator you're using. So... good luck, I guess. :beer:
Re: CC65, FamiTone: Constant values unknown at compile-time
by on (#169767)
Alright, I understand the problem now. Sorry for thinking that you misunderstood it. I didn't see the bigger picture.

I'll ask Shiru whether he changes the text2data program, otherwise I'll use a workaround like force_range.

Thanks.