When I write an NES program in CC65 and use some C code, why does the compiler give me error messages when I declare local variables?
Quote:
Unresolved external `decsp1' referenced in:
Functions.s(103)
Unresolved external `incsp1' referenced in:
Functions.s(126)
Unresolved external `sp' referenced in:
Functions.s(10)
(Besides, the file Functions.s is auto-generated from my Functions.c. It#s not written by myself.)
What do I have to do here?
You need to link the C runtime library. From my limited use of cc65, I used this:
cc65 test.c
ca65 test.s
ld65 -t nes nes.o test.o nes.lib
Meh. I was actually hoping to not having to link any code files. I only wanted to use the compiler and assembler who converts me my source files. But I wanted to write all the code myself. Linking a lib file means that I actually have a blackbox of functions. Isn't there some way around it?
The helper functions are for basic things like adjusting a secondary stack pointer. Inlining them would apparently bloat code too much. It uses a lot of small helper routines. Conceptually it's no different than the compiler outputting the same code inline, just smaller. Look at the runtime source to see what they do.
The C runtime library is not optional when you want to use CC65. Almost all of its generated code depends on it.
By the way, the linker is a "lazy linker", meaning that any modules within the CRT library that you do not use (i.e. any individual .s/.o files from it with no references linking to them) do not get linked into your executable, so the size increase is only as much as you actually use from the CRT.
I think DRW was hoping that a standalone C function would compile to a standalone asm routine, for easy examination/incorporation into other asm code.
Well, that's valid on some compilers, like with MSVC you can do a lot without the CRT (a good idea if trying to make a 4k demo, for instance).
It's not much of an option with CC65, as it relies on CRT routines for very basic things like any C-stack usage, multiplication, etc.
Some compilers split the runtime library into separate parts, one with said "very basic things" needed by even "freestanding" C and one needed only by code that includes a header from the standard library. For example, GCC has libgcc and libc respectively for C and libsupc++ and libstdc++ for C++. Does cc65 have a similar split?
Well, in my custom version of the CC65 CRT, this is what I use:
Code:
libsrc/runtime/*.*
- Makefile
- condes.s (removed to disable .constructor/.destructor functionality, which required self modifying code)
- stkchk.s (removed because it was the only crt module with a .constructor)
- callirq.s (IRQs should be handled manually in assembly)
- callmain.s (main is void(), arguments are irrelevant on NES)
libsrc/common/
+ copydata.s (needed to copy DATA segment to RAM)
I also have a custom crt0.s that I wrote. (Available here:
http://rainwarrior.ca/music/coltrane_src.zip)
This set contains the minimum needed to be able to compile C code, I believe (i.e. the runtime/ folder). You could omit copydata.s as well if you want to set up your DATA segment manually, but I didn't feel like rewriting that in my custom crt0. So, basically the "runtime" folder is the stuff that is essential, and everything else is optional library/helper stuff.
As I said, though, it doesn't link in anything you don't use anyway, so even if your .lib contains a lot more it still won't end up in the ROM unless you try to use it. I don't think the non-essential stuff is very relevant to NES development in general.
You can try giving the compiler the -Oi flag ("Optimize code, inline more code"), but note that this increases code size. Also regardless of the flag, eventually you're going to run into needing the runtime library.
Another way to avoid runtime calls is to avoid doing stuff that would require them, but this is not easy. For example, you could set all local variables as static (-Cl) so that stack isn't needed to access them. Obviously this is highly susceptible and may break when a new compiler version is released.
Thanks for the answers. I guess for a start, I'll just use the standard neslib until I have a working example that does all the things I want to do. Then I can see how much I would have to re-program if I omit the external lib.