Programming NES in C
Moderator: Moderators
Programming NES in C
After finally getting cc65 to work, I decided to test it out, so rewrote my "Paint or Draw" game almost entirely in C. While I assumed that C would run slower and take up more ROM space, ...after comparing the two, I discovered that the one programmed in C was actually slightly shorter than the one in ASM...(the original had some redundancy, that I turned into a reused functions)... and they run about the same speed. They both took me about a week. So, all in all I'd say that programming in C is roughly equivalent to programming in ASM.
Occasionally, there's a few things that can't be done in C, but you can write those bits in ASM and export the function to be called by the C code, which is nice.
Next, I'm going to rewrite the music in Famitracker and import them in.
Occasionally, there's a few things that can't be done in C, but you can write those bits in ASM and export the function to be called by the C code, which is nice.
Next, I'm going to rewrite the music in Famitracker and import them in.
nesdoug.com -- blog/tutorial on programming for the NES
Re: Programming NES in C
What makes setting up ca65/cc65 so involved?
Re: Programming NES in C
Is that the same perceived speed (as seen by the player), or have you actually measured CPU usage in some objective way? Perceived speed is easy to match in simple programs, because there's often a lot of free CPU time per frame either way.dougeff wrote:and they run about the same speed.
I think it's great that you see C as a viable alternative for coding NES programs, but saying "it's the same" might be a little precipitated after only one small scale project.They both took me about a week. So, all in all I'd say that programming in C is roughly equivalent to programming in ASM.
Re: Programming NES in C
That's 1094 cycles (ASM version) vs 1107 cycles (C version)...to complete all the logic between frames.speed
nesdoug.com -- blog/tutorial on programming for the NES
Re: Programming NES in C
Alot of my problems is just the learning curve, and figuring out syntax differences from what I'm used to...What makes setting up ca65/cc65 so involved?
Code: Select all
*((unsigned char*)0x2006) = updateHigh;
*((unsigned char*)0x2006) = updateLow;
*((unsigned char*)0x2007) = updateData;Code: Select all
#pragma rodata-name (push, "RODATA")nesdoug.com -- blog/tutorial on programming for the NES
Re: Programming NES in C
...and, I couldn't get it to work at first because I was trying to compile Shirus example files with the wrong version of cc65. And I was getting error messages from the linker. Etc.
nesdoug.com -- blog/tutorial on programming for the NES
Re: Programming NES in C
I am surprised that you could get something working at the same speed as ASM, however I suspect that this is because you don't use features I used when I tested CC64. I used bi dimensional arrays and this turned out to be implemented by a particularly catastrophic way in CC65.
- mikejmoffitt
- Posts: 1352
- Joined: Sun May 27, 2012 8:43 pm
Re: Programming NES in C
This isn't anything really particular to CC65, this is just totally normal C.dougeff wrote:Alot of my problems is just the learning curve, and figuring out syntax differences from what I'm used to...What makes setting up ca65/cc65 so involved?
Code: Select all
*((unsigned char*)0x2006) = updateHigh; *((unsigned char*)0x2006) = updateLow; *((unsigned char*)0x2007) = updateData;
I don't know much about CC65, but you should really be defining those as volatile unsigned char. Otherwise the compiler may optimize out certain reads/writes.
You should be able to do something like this:
Code: Select all
volatile unsigned char *h_ptr = (volatile unsigned char *)0x2006;
*h_ptr = updateHigh;
// Etc etc
Re: Programming NES in C
cc65 ignores volatile. I suppose it doesn't have the ability to optimize away stuff that would matter anyway.
- rainwarrior
- Posts: 8062
- Joined: Sun Jan 22, 2012 12:03 pm
- Location: Canada
- Contact:
Re: Programming NES in C
Volatile is the semantically correct thing to do. It basically just means its value can change outside this code (e.g. multi-threading, hardware interfaces, etc) so that an optimizer should always load/store it directly to memory every time it is used (don't reorder operations, or keep it around in a register to optimize calculations with it, etc.). I don't think cc65's optimizer is strong enough to even do the kinds of things that would make volatile matter, which is why it is currently meaningless for cc65.
There used to be an issue with code the looks like this:
At one time this would generate an indirect indexed write, which for the PPU causes a double increment of the PPU address. I don't know if this has since been fixed, but I don't recommend trying it, anyway.
I think it is a poor approach to try to update hardware registers in C. Much better to delegate this to assembly:
There used to be an issue with code the looks like this:
Code: Select all
((unsigned char*)0)[0x2007] = aI think it is a poor approach to try to update hardware registers in C. Much better to delegate this to assembly:
Code: Select all
; assembly file:
.proc _ppu_write:
sta $2007
rts
.endproc
// C header:
void __fastcall__ ppu_write(unsigned char data);
// C code:
ppu_write(a);Re: Programming NES in C
I reposted my Paint or Draw game to RHDN, and included the source code, in C for cc65.
I've only been using cc65 for 1 month, so it's not the best code, but there are so few example codes using cc65, I thought I should. Consider this one step above 'Hello World' for any C programmers out there who want to program an NES game. It builds from Shiru's Chase game, but I rewrote 90% of the code, so it's not entirely derivative. Also, it runs music with Famitone2 from Famitracker file.
http://www.romhacking.net/homebrew/69/
I've only been using cc65 for 1 month, so it's not the best code, but there are so few example codes using cc65, I thought I should. Consider this one step above 'Hello World' for any C programmers out there who want to program an NES game. It builds from Shiru's Chase game, but I rewrote 90% of the code, so it's not entirely derivative. Also, it runs music with Famitone2 from Famitracker file.
http://www.romhacking.net/homebrew/69/
nesdoug.com -- blog/tutorial on programming for the NES
Re: Programming NES in C
Hey, question for all the people who know anything about cc65.
I was writing some test code, and...previously my compile.bat file always said something like this...
...near the end.
But, I then removed 'runtime.lib' off that line...and it compiled perfectly into an .nes file. So, my question is, why do we need a runtime.lib? What is it supposed to be doing?
I was writing some test code, and...previously my compile.bat file always said something like this...
Code: Select all
ld65 -C nes.cfg -o blah.nes reset.o blah.o runtime.libBut, I then removed 'runtime.lib' off that line...and it compiled perfectly into an .nes file. So, my question is, why do we need a runtime.lib? What is it supposed to be doing?
nesdoug.com -- blog/tutorial on programming for the NES
- GradualGames
- Posts: 1106
- Joined: Sun Nov 09, 2008 9:18 pm
- Location: Pennsylvania, USA
- Contact:
Re: Programming NES in C
If I understand correctly, runtime.lib is Shiru's modified version of nes.lib (included with CC65). I'm not exactly sure what he's done with it, but in my own adventures with C, what I've done is to remove any object files I don't need from nes.lib but I still compile against it for any standard C stuff I might use, such as string routines for example. My understanding is that it has a partial implementation of the standard C library and some default code for interacting with the ppu. I don't think they have printf implemented, but you can use putchar and write your own string printing routine without adding any custom code. They have a buffered ppu system set up (by default in nes.lib) for just writing tiles to the screen. It's not very useful for game programming so what I did was to remove crt0.o from nes.lib, scarf crt0.s from cc65's source code and modify it with my own startup code and my own approach to nmi handlers. Breaks some of the C implementation, but you should still be able to use the rest of it. Basically I think it is harmless to keep compiling against it because, if I understand compilation correctly, if you don't link to a function it won't get built into your rom. So I just leave it there.
*edit* I'm pretty sure there's other stuff in there that's required for basic operations the compiler relies on for temporary register manipulation, too. I'm unable to compile without it, probably because I'm using crt0.s. I'd be wary of not including it all if you're coding in C. *edit* sure enough, just one simple C routine I wrote, which doesn't even use any C library stuff, won't compile without nes.lib. So, I anticipate if you write much C at all in your program, you'll run into issues pretty quickly without the runtime. *edit* note this is all assuming runtime.lib was indeed from shiru's C examples, and assuming that it is indeed a modified version of nes.lib. I'm pretty sure it is because, it seemed natural also for me to remove crt0.o from nes.lib and customize it, but leave the runtime in place. He just renamed it to runtime.lib. *edit* probably because the library he has provided is itself called neslib, haha! It all makes sense now
Not sure if that helps or has just increased the confusion!
*edit* I'm pretty sure there's other stuff in there that's required for basic operations the compiler relies on for temporary register manipulation, too. I'm unable to compile without it, probably because I'm using crt0.s. I'd be wary of not including it all if you're coding in C. *edit* sure enough, just one simple C routine I wrote, which doesn't even use any C library stuff, won't compile without nes.lib. So, I anticipate if you write much C at all in your program, you'll run into issues pretty quickly without the runtime. *edit* note this is all assuming runtime.lib was indeed from shiru's C examples, and assuming that it is indeed a modified version of nes.lib. I'm pretty sure it is because, it seemed natural also for me to remove crt0.o from nes.lib and customize it, but leave the runtime in place. He just renamed it to runtime.lib. *edit* probably because the library he has provided is itself called neslib, haha! It all makes sense now
Not sure if that helps or has just increased the confusion!
Re: Programming NES in C
Shiru's runtime.lib is just a subset of the runtime as-of when he made it. It's missing an awful lot, so rather than explicitly listing all 310 missing object files:GradualGames wrote:If I understand correctly, runtime.lib is Shiru's modified version of nes.lib (included with CC65). I'm not exactly sure what he's done with it
* No TGI
* No string operations
* No conio.h things
* No FILE * operations
* random other things
You can't do function calls with arguments without at least some of the cc65 runtime (push* and pop* functions)¹. You can't do multiplication or division without some of the cc65 runtime. Many bits of pointer math require the runtime.I'm pretty sure there's other stuff in there that's required for basic operations the compiler relies on for temporary register manipulation, too. I'm unable to compile without it, probably because I'm using crt0.s. I'd be wary of not including it all if you're coding in C. *edit* sure enough, just one simple C routine I wrote, which doesn't even use any C library stuff, won't compile without nes.lib. So, I anticipate if you write much C at all in your program, you'll run into issues pretty quickly without the runtime.
¹ You can call assembly functions that have a single 8- or 16- bit argument without the runtime if cc65 is run WITHOUT --all-cdecl or the function prototype is declared with __fastcall__. That argument is in A or X:A.
Re: Programming NES in C
So, for the sake of argument...
If all my functions look like this...
And I use no multiply/divide.
(And a few other things)
I can leave runtime.lib off without error?
If all my functions look like this...
Code: Select all
void foo(void)(And a few other things)
I can leave runtime.lib off without error?
nesdoug.com -- blog/tutorial on programming for the NES