snes-sdk, tcc816, and large arrays

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
snes-sdk, tcc816, and large arrays
by on (#81529)
Just thought this information could be useful to someone who want to use snes-sdk and will have the same problem I had, and solved with help of mic and Chilly Willy.

The tcc816 from the snes-sdk is a nice C compiler, but currently it has few problems that could be a real showstopper for any large project (say, just a full screen picture and music).

First problem, rather obvious, is that one piece of data can't be larger than 32K. So if you have a data array that is larger than 32K, you need to split it to few smaller arrays.

Second problem. Compiler puts data into .data section regardless of const. When this section overflows, linker tell you there is no room in .data. You can fix this with a custom tool that will move all the const data to .rodata section. mic made such a tool (source code only), but you may need to change it a bit if you have different data organization in your program.

Third problem. Even with the tool, when you have total amount of const data >32K, you'll get a linker error that there is no room in .rodata now. The problem is neither the compiler nor linker are allocate data into ROM banks automatically. You have do to it by yourself, creating .rodata1, .rodata2 etc sections and putting no more than 32K of data in every .rodata section. You can do it either by creating a custom tool, or by declaring additional .rodata sections in a separate assembly file, including the data with incbin and referencing to the data from C code through extern.

by on (#81530)
For big data files, like graphics, music, level data, etc. the best thing is probably to incbin them in assembly files rather than converting the data files to C arrays. You don't need to split the files yourself, since the assembler can do that for you:

Code:
.bank 1
.incbin "big_file.bin" read $8000

.bank 2
.incbin "big_file.bin" skip $8000 read $8000

; and so on...



Some other tips:

* The C compiler generates "superfree" sections for the compiled code. This means that the linker is free to put these sections anywhere it sees fit (i.e. where there's room for it). So if you use HiROM you might want to fill up the even banks (0, 2, 4, ...) with garbage to avoid having the linker placing any code or data in those banks, as they won't be directly addressable (you'd have to access them through one of the mirrors, e.g. $4xyyyy).

* There's no way to tell the compiler to place code in the .data section. The assembler/linker won't even let you do it manually IIRC. So what I did was to write all code I wanted to execute from RAM in assembly and putting them in the ROM bank at $018000 (making sure to use word addressing instead of long addressing when these functions call eachother). At startup I would copy all that code from $018000 to $7E8000 and create function pointers to call the copies in RAM.