ca65 Segments

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
ca65 Segments
by on (#242982)
While trying to compile a piece of code someone else had written I received an error, Segment 'ZEROPAGE' overflows memory area 'ZP' by 19 bytes. This was odd, since the code was only using up 45 bytes in the ZEROPAGE segment, and I believed the Zero Page to range from $0000 to $00FF. I looked into the NES config file (cc65\cfg\nes.cfg), and found that the ZEROPAGE segment only had a size of 26 ($001A) bytes. I tried changing the size to $00FF, and everything compiled just fine.
Here are some pieces of the file:
Code:
MEMORY {
    ZP:     file = "", start = $0002, size = $001A, type = rw, define = yes;
}
SEGMENTS {
    ZEROPAGE: load = ZP,              type = zp;
}


Curious, I looked into this file some more an messed around with it a bit. There seem to be many overlapping segments, and many seemed to be out of place for what I knew of the NES. In particular I looked at ROM2, which is what the CHARS segment loads. I would think that this segment would start someplace later than $0000, so I messed with it. No matter what I changed the start value to, everything compiled fine, and there were no changes in the game when I tested it. I tried setting the value to $FFFFFFFF, and there were no problems.
Code:
MEMORY {
    ROM2:   file = %O, start = $0000, size = $2000, fill = yes;
}
SEGMENTS {
    CHARS:    load = ROM2,            type = rw;
}




I have two questions:

1. [ANSWERED] Why was the ZEROPAGE segment so small? I have learned that it ranges from $0000 to $00FF, yet in this file it only ranges from $0002 to $001C.

2. [ANSWERED] Why do these segments start in the places they do? How are these segments put together into the ROM when I compile? (This is a broad question, so a more general answer makes sense if a more detailed answer is too troublesome.)
Re: ca65 Segments
by on (#242983)
"start" specifies the logical address where the data can be found, not the physical address. (This is also why "start" and "load" parameters exist: for code that exists at one location ("load"), is copied somewhere else, and then used)

The CHR ROM in an NROM game starts at PPU address $0000, and is $2000 bytes long. If one specified the entire CHR data using macros to draw them, instead of manually including a tile, you could literally have something like
Code:
tile34: NESTILE(00000000,01111110,01222210,01233210,01222210,01111110,00000000)

and then could refer to "tile34" in code elsewhere to get the address of that tile within CHR.

One might use this to refer to where to get the non-tile data such as in SMB's title screen, or Galaxian's easter egg music – but only if "start" is correct!
Re: ca65 Segments
by on (#242984)
PixelMagic wrote:
I looked into the NES config file (cc65\cfg\nes.cfg)...

1. Why was the ZEROPAGE segment so small? I have learned that it ranges from $0000 to $00FF, yet in this file it only ranges from $0002 to $001C.

2. Why do these segments start in the places they do? How are these segments put together into the ROM when I compile? (This is a broad question, so a more general answer makes sense if a more detailed answer is too troublesome.)

1. That CFG is for CC65's generic C framework. It's not a good starting point for doing assembly programming on the NES. The ZP is small because that's all the space the C compiler internals need on the ZP, and otherwise the C compiler doesn't have much outward concept of zero page. There's a #pragma zpsym, or maybe #pragma bss-name might do, but either way you'll need your own CFG to make use of this.

The bottom line is that CFG is not for what you're trying to do. I think the size is as small as it is for consistency across the 30 or so other target platforms. The intent isn't to make it as big as possible, but rather as consistent as possible if you're compiling the same C code to several targets.

2. Each SEGMENT goes into the assigned MEMORY load region. They get placed into that region in the same order they appear in the SEGMENTS list, and they take up only as much space as they need. You can give them a "start" attribute to make it start at a specific memory address, though this will skip over any unused space that came earlier in the MEMORY region, so it's usually only useful for putting the last segment at the end of a memory block (e.g. vector table).


If you'd like a simpler example CFG here's one I created as part of a small example project a while ago:
https://github.com/bbbradsmith/NES-ca65-example/blob/master/example.cfg
Re: ca65 Segments
by on (#243035)
rainwarrior wrote:
PixelMagic wrote:
I looked into the NES config file (cc65\cfg\nes.cfg)...

1. Why was the ZEROPAGE segment so small? I have learned that it ranges from $0000 to $00FF, yet in this file it only ranges from $0002 to $001C.

2. Why do these segments start in the places they do? How are these segments put together into the ROM when I compile? (This is a broad question, so a more general answer makes sense if a more detailed answer is too troublesome.)

1. That CFG is for CC65's generic C framework. It's not a good starting point for doing assembly programming on the NES. The ZP is small because that's all the space the C compiler internals need on the ZP, and otherwise the C compiler doesn't have much outward concept of zero page. There's a #pragma zpsym, or maybe #pragma bss-name might do, but either way you'll need your own CFG to make use of this.

The bottom line is that CFG is not for what you're trying to do. I think the size is as small as it is for consistency across the 30 or so other target platforms. The intent isn't to make it as big as possible, but rather as consistent as possible if you're compiling the same C code to several targets.

2. Each SEGMENT goes into the assigned MEMORY load region. They get placed into that region in the same order they appear in the SEGMENTS list, and they take up only as much space as they need. You can give them a "start" attribute to make it start at a specific memory address, though this will skip over any unused space that came earlier in the MEMORY region, so it's usually only useful for putting the last segment at the end of a memory block (e.g. vector table).


If you'd like a simpler example CFG here's one I created as part of a small example project a while ago:
https://github.com/bbbradsmith/NES-ca65-example/blob/master/example.cfg

I see, that makes sense. Thanks for the explanation.

Do you know where I might be able to learn more about how to write my cfg file? I can see there's more stuff than just the start and size parameters, and I can't seem to find anything online.
Re: ca65 Segments
by on (#243036)
What kind/size of ROM are you targeting? NROM256 (like Super Mario Bros) ?
Re: ca65 Segments
by on (#243039)
PixelMagic wrote:
Do you know where I might be able to learn more about how to write my cfg file? I can see there's more stuff than just the start and size parameters, and I can't seem to find anything online.

The official reference for it is here:
https://cc65.github.io/doc/ld65.html

One folder up is the official reference collection for the entire CC65 toolset:
https://cc65.github.io/doc/
Re: ca65 Segments
by on (#243040)
dougeff wrote:
What kind/size of ROM are you targeting? NROM256 (like Super Mario Bros) ?

I don't really know. I'm not making anything substantial right now. I'm just trying to mess around to figure out what's what, and I'd like to know exactly what I'm doing to the ROM to make things happen.
I suppose I'm not looking for any special sort of ROM yet (I know there's stuff about bank switching and such that set ROMS apart). Super Mario Bros. would be on a pretty generic type of ROM, right?

I looked into NROM-256 on Nesdev wiki, and it looks like it has some stuff to do with things stated in the header, so if it's useful, here's the header I'm trying to build stuff off of at the moment (pretty generic):
Code:
.segment "HEADER" ; Header - 16 bytes
    .byte "NES"     ; 0-2  $4e $45 $53
    .byte $1a       ; 3    $1a (iNES header)
    .byte $02       ; 4    PRG ROM *16kb
    .byte $01       ; 5    CHR ROM *8kb  (0=CHR RAM)
    .byte %00000000 ; 6    mapper, mirroring, battery, trainer
    .byte $00       ; 7    other stuff :P
    .byte $00       ; 8    :P
    .byte $00       ; 9    :P
    .byte $00       ; 10   :P
    .byte $00,$00,$00,$00,$00 ; 11-15   Unused
Re: ca65 Segments
by on (#243041)
rainwarrior wrote:
PixelMagic wrote:
Do you know where I might be able to learn more about how to write my cfg file? I can see there's more stuff than just the start and size parameters, and I can't seem to find anything online.

The official reference for it is here:
https://cc65.github.io/doc/ld65.html

One folder up is the official reference collection for the entire CC65 toolset:
https://cc65.github.io/doc/

Ah, I should have known. Thanks for the reply.
Re: ca65 Segments
by on (#243042)
I couldn't find a generic .cfg file for ca65, NROM256, but it would be something like this...

Code:
MEMORY {
#RAM Addresses:
    # Zero page
    ZP: start = $00, size = $100, type = rw, define = yes;
   
    RAM: start = $0300, size = $0500, define = yes;

#INES Header:
    HEADER: start = $0, size = $10, file = %O ,fill = yes;

#ROM Addresses:
    PRG: start = $8000, size = $8000, file = %O ,fill = yes, define = yes;

#1 Bank of 8K CHR ROM
    CHR: start = $0000, size = $2000, file = %O, fill = yes;
}

SEGMENTS {
    HEADER:   load = HEADER,         type = ro;

    CODE:     load = PRG,            type = ro,  define = yes;
    RODATA:   load = PRG,            type = ro,  define = yes;
    VECTORS:  load = PRG, start = $fffa, type = ro;

    ZEROPAGE: load = ZP,             type = zp;
    BSS:      load = RAM,            type = bss, define = yes;

    CHARS:    load = CHR,            type = rw;
}




With the sprite buffer going at $200-2ff not defined, but you could optionally put that in.
Re: ca65 Segments
by on (#243044)
dougeff wrote:
I couldn't find a generic .cfg file for ca65, NROM256, but it would be something like this...

Code:
MEMORY {
#RAM Addresses:
    # Zero page
    ZP: start = $00, size = $100, type = rw, define = yes;
   
    RAM: start = $0300, size = $0500, define = yes;

#INES Header:
    HEADER: start = $0, size = $10, file = %O ,fill = yes;

#ROM Addresses:
    PRG: start = $8000, size = $8000, file = %O ,fill = yes, define = yes;

#1 Bank of 8K CHR ROM
    CHR: start = $0000, size = $2000, file = %O, fill = yes;
}

SEGMENTS {
    HEADER:   load = HEADER,         type = ro;

    CODE:     load = PRG,            type = ro,  define = yes;
    RODATA:   load = PRG,            type = ro,  define = yes;
    VECTORS:  load = PRG, start = $fffa, type = ro;

    ZEROPAGE: load = ZP,             type = zp;
    BSS:      load = RAM,            type = bss, define = yes;

    CHARS:    load = CHR,            type = rw;
}




With the sprite buffer going at $200-2ff not defined, but you could optionally put that in.

I think I'm starting to get the hang of things.

That makes three .cfg files to compare with each other. Thanks for the help.