Programming for the NES in C or C++

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems.

Moderator: Moderators

User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Post by cpow »

Disch wrote:...
Create first. Optimize later. If doing the reverse you'll never get to the create step. If you create something that sucks balls in performance you'll either feel bad enough about it that you'll learn how to fix your mistakes before making your mess public, or you'll seek out a generally helpful bunch of folk like those that usually hang out here, and they'll...usually...help.

Having said that, my comments apply to people that enjoy taking on the majority of the challenge themselves, unlike what appears to be going on in another 40+ page thread here. Oh...isn't he trying to do it in assembly? How many times have you guys said "you forgot a PLA" or "you forgot an RTS"?
tepples
Posts: 22345
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

cpow wrote:Create first. Optimize later.
And if you feel it makes creating easier, make your game mechanics in Pygame or XNA or another high-level environment before porting them to the NES.
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

cpow wrote:How many times have you guys said "you forgot a PLA" or "you forgot an RTS"?
Which would become "You forgot an ! (not)" or "You forgot an * (to make something a pointer)" or "You need to cast (uint8)", or "You used | not ||" etc., etc in C. I don't think that's a fair example. It's just the compiler will catch things like "you forgot a semicolon", so that a person might not have to post about it, but it's not as if a new person would not still have trouble or post about what the actual compiler errors MEAN. It's not much different.

In my experience assembly has been much easier to teach and learn, it's just harder to actually USE for something complex which I guess is the current topic. And I mean teach for understanding, not teach for the test. (Here's Hello World with no explanation. Copy paste it, and wow! Magic!)

There were lots of things about C I NEVER got or just copy pasted from tutorials (which is just plain awful) until I learned some assembly language.

Edit: Just to be more on topic for the original post: Since the topic creator already knows C, I don't believe he should develop his game entirely in assembly language. But he'll almost certainly need to learn it regardless for certain parts of the program, so he needs to be aware of that.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Post by rainwarrior »

tepples wrote:And if you feel it makes creating easier, make your game mechanics in Pygame or XNA or another high-level environment before porting them to the NES.
Yes, this is very good advice. I'm writing a game in Java, myself, intended for the NES eventually.


Kasumi, what's unfair about that example? Messing up your stack is an entire class of errors that are impossible in C because the stack is abstracted away for you. A missing semicolon in C isn't equivalent to missing an RTS or PLA in assembly. One is a compile time error, corrected very quickly, the other is undefined runtime behaviour, which if you're lucky just crashes outright, and if you're unlucky looks fine for now and becomes a nightmare to debug later on.
User avatar
Jarhmander
Formerly ~J-@D!~
Posts: 521
Joined: Sun Mar 12, 2006 12:36 am
Location: Rive nord de Montréal

Post by Jarhmander »

rainwarrior wrote:Kasumi, what's unfair about that example? Messing up your stack is an entire class of errors that are impossible in C because the stack is abstracted away for you. A missing semicolon in C isn't equivalent to missing an RTS or PLA in assembly. One is a compile time error, corrected very quickly, the other is undefined runtime behaviour, which if you're lucky just crashes outright, and if you're unlucky looks fine for now and becomes a nightmare to debug later on.
That. This was my point, just put out much nicely.

Seriously, I don't even understand the "assembly is easier than C" point. From a beginner's perspective, it's a lot of potential pitfalls your assembler will not even give a hint about it.

Let consider this sentence: " * is easier than assembly (on any target)", then the wildcard matches any high level programming language, except some esoteric programming languages of questionable use (brainf*ck, Malbolge, Piet etc).
User avatar
Kasumi
Posts: 1293
Joined: Wed Apr 02, 2008 2:09 pm

Post by Kasumi »

rainwarrior wrote: Kasumi, what's unfair about that example? Messing up your stack is an entire class of errors that are impossible in C because the stack is abstracted away for you.
How about messing up on braces in a way that will compile? I've had an error like that. I'm saying that hard-to-debug-because-you're-reading-what-you-think-is-there-rather-than-what's-there errors aren't unique to assembly. Even with a compiler, to a new person, the errors can occasionally be pretty unhelpful. An experienced programmer would never miss an RTS, not mess up their braces, or would catch these things immediately. But I honestly don't think it's so different for a new guy to C or assembly. I didn't think it was a fair example, because I imagine the same person would experience similar errors whether it was C, assembly, Java, or visual basic.

How about something like this:

Code: Select all

int function(int x){
     int mario[10];
   //Do some actions on the mario array. Maybe to count how many times
  //the numbers 0-9 are used in some global data.
   return mario[x]//Then return the count.
}
I thought it was safe to assume that all the values in mario[10] would be initialized to 0, which isn't true here. So this function would never work as expected, and the compiler I was using at the time didn't tell me anything, because (I'm guessing) at least some of mario was touched each time it was run. (Otherwise, it would warn me I was trying to return an uninitialized variable.) That's a close example to something I encountered when I started out. In fact, I believe it wasn't until my first use of malloc that something actually clicked and I understood WHY it would be stupid to expect all arrays to be initialized to 0 at runtime.

I can name quite a few similar things in C I did not understand because it was abstracted from me. Learning C was a battle for me, and 6502 assembly absolutely wasn't. I guess I'm the only one.
~J-@D!~ wrote: Seriously, I don't even understand the "assembly is easier than C" point. From a beginner's perspective, it's a lot of potential pitfalls your assembler will not even give a hint about it.
I didn't say assembly was easier to program in (in fact I said the opposite), or that it was easier. I said in my experience it's easier to teach and learn, and I think it's because (for 6502 at least) it does about a single thing at a time. Even if you never use the language to actually make anything other than examples, it gives you a really great understanding of programming.

I agree C(++) has a better workflow, and if we weren't programming for NES I'd recommend it every time. I'm sharing my personal experience of 6502 being easier, and saying C(++) still has lots of room for beginner mistakes that seem to be ignored whenever these discussions come up. I'm not even sure 6502 assembly has more pitfalls for a beginner honestly. It's definitely true that a compiler is more helpful than an assembler when such errors occur, though.
User avatar
rainwarrior
Posts: 8062
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Post by rainwarrior »

Making typographical errors or logical errors when programming happens to everyone. It gets better with experience but nobody's perfect. Errors will happen. My issue is not with how easy or hard it is to produce correct code, but with what happens when you fail to do so.

Assembly has very few compile time errors; most of them are syntax errors which are trivial to fix. There is almost nothing that will guard against incorrect logic at compile time; these errors have to be found exclusively at runtime, and fixing runtime errors takes much more development time than compile errors, by a wide margin.

C provides all sorts of things that prevent logical errors at compile time. For instance, in C literals and addresses are distinct types, not distinguished by a # prefix in the instruction they are used, but distinguished by their definition. There is no way to use one as the other without explicitly doing so (e.g. an array operator or a cast).

Similarly 16 bit and 8 bit variables are distinguished by their definition; the compiler won't forget which is which or cast away information unless you explicitly tell it to. It will never forget the carry bit.

etc.

I've probably said to much on this subject, sorry.


Anyhow, as I said, there's a lot you can do with very little (or perhaps no) assembly work, especially starting from Shiru's examples, and I see no reason to discourage someone new to this from using it. Yes, learning assembly is useful, and absolutely critical for doing certain kinds of things, but this doesn't invalidate C for the other things.

I'd rather see someone be able to make a simple game in C, than get discouraged because someone told them they have to learn assembly. If you're ready to learn it, learn it, if not, I'd rather see you finish something than be held back making a bunch of beginner's mistakes in a language you're just learning.

If you already know what you're doing, then my opinion doesn't matter; you've already got your own, and probably have a workflow that suits you.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

I'm with Kasumi on this one, you're being kinda unfair when comparing C to ASM. For someone who already knows C, like in this case, using it to get started with NES programming makes sense, but for people who know neither C nor ASM, I don't think it's simple to say which one is easier.

I've seen people who knew (eh... kinda knew, that was in college and not many people liked programming at all) Pascal and Visual Basic, who were completely lost when they started learning C, much like newbies here are lost with ASM. You can screw things up pretty badly with C, maybe not as much as with ASM, but I feel like the rules of ASM are easier for beginners to understand, since the building blocks are so small. So I think there's some sort of balance there, that doesn't give a clear advantage of one language over the other if you don't know either one.

EDIT: I do wonder if it's possible to complete a game for the NES in C without knowing ANY assembly though. Shiru succeeded because he knows the NES very well, and was using his own libraries. A person that is not familiar with the overall architecture of the NES and has no clue of what his C code is turning into probably wouldn't achieve the same success. I mean, you have to know what registers are there and when you're supposed to use them, you have to be aware of the limitations for accessing the PPU, things like that, and that's usually easier to see when using ASM.
Shiru
Posts: 1161
Joined: Sat Jan 23, 2010 11:41 pm

Post by Shiru »

There was a project by someone who don't know 6502 assembly and NES architecture and limitations. He managed to make a demo with large moving and jumping animated character and background with limited two-screen scroll using my library. Then he learned some limitations (8 sprites per scanline), they were a showstopper for him. So - basic NES architecture knowledge is important in this case, while 6502 assembly code is not.
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »


1) 6502 has a concept of a call stack, but only partially. JSR/RTS only record the PC, they do not push/pop local variables or function parameters. The 256 bytes on the 6502 is probably not enough space to handle such a call stack, so the compiled code will have to simulate its own stack.
No, no, no !!

This is the very reason CC65 performs so poorly.
However, SDCC does not have a call stack, at the expanse of not allowing re-entrant functions (functions that calls themselves).
This is something that is typically never, ever used. Why waste so much performance for something that is never used ?
After all C is just instructions to automate assembly code - and those instructions are free to be interpreted in the way the compiler likes it. A C compiler that does not follow exactly the standards is way better than writing everything in assembly.
This means that local variables and function parameters are all going to have to be accessed indirectly. This poses several problems on the 6502:

-) LDA (n),Y is 5-6 cycles. LDA abs is 4 cycles. This means for memory accesses alone, you're already running 25-50% slower.
I'm sure everything can be accessed in RAM and Zero-Page for fast aces if the compiler does it this way.
2) Common C practices would murder performance. Things like using 'int' for general variables, which every C programmer does. If you are adhering to C standards, an int has to be at least 16 bits. Can you imagine what kind of slowdown (and memory consumption!) you'd get in an NES program if every variable is 16 bits wide?
It's very common to program micro-controller in C and use "u8" for all variables that can fit in 8-bits.

Now for the reason I insist so much about C is the cross-platform compatibility.

Let's assume you want to do a game for both the NES and GameBoy.
If you code them all in assembly, you'll have to do all the work twice, because both use completely different processors.

If you do it in C, only the hardware-related parts will have to be rewritten, all the game logic can be kept identical to both versions with very minimal work.
Not only that but a C code typically have between 5 to 10 times less lines of codes than it's assembly counterpart.

That means between 10 and 20 times less work to code a game for two platforms. Worth it isn't it ?
Useless, lumbering half-wits don't scare us.
User avatar
cpow
NESICIDE developer
Posts: 1097
Joined: Mon Oct 13, 2008 7:55 pm
Location: Minneapolis, MN
Contact:

Post by cpow »

rainwarrior wrote:I'd rather see someone be able to make a simple game in C, than get discouraged because someone told them they have to learn assembly. If you're ready to learn it, learn it, if not, I'd rather see you finish something than be held back making a bunch of beginner's mistakes in a language you're just learning.
My point exactly, stated much more eloquently.
strat
Posts: 396
Joined: Mon Apr 07, 2008 6:08 pm
Location: Missouri

Post by strat »

Bregalad wrote:
Let's assume you want to do a game for both the NES and GameBoy.
If you code them all in assembly, you'll have to do all the work twice, because both use completely different processors.
It might be best to write a good chunk of the game in high-level pseudocode and compile it by hand, given that a C-compiler is better suited to GB than NES (Although I tried using one for GB several years back and remember it behaving quirky with 16-bit math).
3gengames
Formerly 65024U
Posts: 2281
Joined: Sat Mar 27, 2010 12:57 pm

Post by 3gengames »

What happens if your program barely cuts it on the NES on CPU and then can't run at the same speed because of the microprocessor needing more clocks that aren't there?
User avatar
Bregalad
Posts: 8036
Joined: Fri Nov 12, 2004 2:49 pm
Location: Caen, France

Post by Bregalad »

3gengames wrote:What happens if your program barely cuts it on the NES on CPU and then can't run at the same speed because of the microprocessor needing more clocks that aren't there?
Don't tell me you haven't ever seen a game lagging, have you ?
Useless, lumbering half-wits don't scare us.
3gengames
Formerly 65024U
Posts: 2281
Joined: Sat Mar 27, 2010 12:57 pm

Post by 3gengames »

I've never seen a C game with more than a couple objects and a couple background tiles to write, either. I mean, games should run at 100% speed 100% of the time, C or no C.
Post Reply