I recently saw someone elses (very small) example code of C for cc65 for NES... they did things in a way that the user, basically, doesn't need to understand anything under the hood. Hiding 2006/2007 writes in hidden function definitions, for example.
Personally, I like to understand everything that is going on, but I suppose a casual user, who just wants to make a simple game, might prefer to know nothing about the inner workings of the PPU.
This seems to me, to be a more modern approach. The library takes care of technical details, the programmer can focus on the game.
Thoughts?
Personally I'm with you that I don't want to include anything I don't fully understand, so I think it's good if both types of tutorials are out there. I prefer to at least know how the code I'm using works, so no libraries, but I don't need to know every little hardware detail under the hood (although I try to learn that too to a degree).
However, although a tutorial that explains everything, doesn't need explain everything the moment they are introduced, as long as it tells the reader that it is something necessary that will be explained later.
For example the Nerdy Nights doesn't explain the init code, leaving the reader wondering why there's suddenly extra code in the source file that the tutorial didn't mention and even seems to contradict what it said.
I stick to higher level languages because it allows me to focus on game logic and prototyping ideas easily.
I kinda failed to even get a basic program working in NESICIDE. So, hardware details are nice but a distant priority/motivator.
Thanks for the replies.
This was the example code I saw, that made me think I could have made it MUCH easier...
https://github.com/algofoogle/nes-gamed ... ello-nes.cEspecially lines like this...
Code:
/* Write a byte to a given address: */
#define poke(addr, data) (*((unsigned char*)addr) = data)
/* Write a pair of bytes to the PPU VRAM Address 2. */
#define ppu_2(a, b) { poke(PPU_VRAM_ADDR2, a); poke(PPU_VRAM_ADDR2, b); }
I wouldn't use this 'poke' stuff, but it's interesting. I was thinking of something like set_ppu_address (x,y) where the definition somehow converts the x,y coordinates AT COMPILE TIME into writes to $2006.
The user would then only have to use set_ppu_address() and never worry about PPU addresses.
Or, perhaps, everything should be buffered, and written during V-blank. So people won't have to worry about timing things, just an automated system, that buffers all writes, and auto-writes it during NMI routine.
Hmm.
Edit, actually, the very next line of code above...
#define ppu_set_cursor(x, y) ppu_2(0x20+((y)>>3), ((y)<<5)+(x))
Would do what I said.
dougeff wrote:
I wouldn't use this 'poke' stuff, but it's interesting. I was thinking of something like set_ppu_address (x,y) where the definition somehow converts the x,y coordinates AT COMPILE TIME into writes to $2006.
Every neslib I've seen has used NTADR to do this:
https://github.com/jmk/cc65-nes-example ... mple1.c#L9I like to use PPU_DATA and PPU_ADDR in my C projects to simplify memory-mapped registers:
https://github.com/dustmop/annotatecc65 ... e/common.hIn general, macros are dangerous, but can be super useful for readability if used carefully.
It's cool that you can highlight specific lines on github with a #L
I'm learning lots today
If you click on the line-numbers on the left-side of the code display, it sends your browser to the line-highlighted link. No need to memorize the anchor or anything!
I was very reluctant to use Shiru's neslib in my example code. For one thing, I'm not sure I understand it 100% fully. Another thing. I'm pretty sure Shiru hates me. A lot.
Neslib is pretty straightforward, is well built, simple, barebones, yet it manages to conceal all the fiddly stuff. But it's there anyways and easily modifiable if you know what you are doing. So it was great for my beginnings and also great now that I have a proper understanding of the system. So easily tweakable. I like it.
I think it's great for beginners 'cause it solves all the fiddly stuff, and still great for intermediates and advanced coders, 'cause the fiddly stuff is easily modifiable. It was pretty straightforward to make it work in UNROM, MMC1 and MMC3, for example.
dustmop wrote:
dougeff wrote:
I wouldn't use this 'poke' stuff, but it's interesting. I was thinking of something like set_ppu_address (x,y) where the definition somehow converts the x,y coordinates AT COMPILE TIME into writes to $2006.
Every neslib I've seen has used NTADR to do this:
https://github.com/jmk/cc65-nes-example ... mple1.c#L9I like to use PPU_DATA and PPU_ADDR in my C projects to simplify memory-mapped registers:
https://github.com/dustmop/annotatecc65 ... e/common.hIn general, macros are dangerous, but can be super useful for readability if used carefully.
The 'cc65-nes-examples' github you linked to is an outdated version. The newer version is from 2015. It has more example code and a completely different neslib.s
Interestingly...the neslib.s from the 2015 examples is different from
the example in Chase and different from other Shiru source code (Zooming secretary)
EDIT - I think the newest examples uses the same as the newest Chase source neslib.s, and I think that is what dustmop and na_th_an are using. Nevermind.
Na_th_an / dustmop, which version of neslib.s do you use? It seems that Chase is the most recent version.
My base version comes from
this revised version repackaged for the "new" cc65 (current active branch) by lidnariq.
Calima maintains an
enhaced version with some fixes.
My own UNROM version is
here. I have created simple MMC1 and MMC3 versions but haven't released them yet.
I'm definitely a fan of having the little details in there. I like what I've seen of your tutorials, (disclaimer: I haven't read all that much) because of those nitty-gritty details. I may be biased due to already using 6502 assembly a lot, however.
At the same time, neslib is awesome, and makes working in C a bit more enjoyable. Neslib made it much easier to bootstrap some examples of my most recent project. It also made those examples more readable - at least as far as using my library goes, as I was able to abstract the system details away.
It seems to me like there's room on the internet and a use for both. I guess it's a question of which experience you want to provide - quickly bootstrapping a working NES game, or giving the user the tools to understand and tinker with everything going on in their game.
This is admittedly not a fully-formed thought - could you do some sort of a hybrid approach, where you use neslib, but also have info boxes that explain the macros/etc for anyone that's inclined to read them? Or maybe even just links to the source for where to see things?
Unrelated note, I didn't realize how many people already had neslib-based bootstrap repos. I'm partway through making an mmc1 example for myself. (Mainly want to use it for ludum dare in a couple weeks) I still probably will, but nice to see more examples coming out. It gives people options for getting started. For example, my typical setup involves using makefiles, a lot of integration with vs code, and automatic circleci builds/releases of every pushed commit. (I only recently figured that last one out; check out my nesnet project if you want an example!) I can see people wanting something simpler or just different.
This gentleman has the newer example code (Shiru) on his github
https://github.com/peterhellberg/nesdev ... s_examplesAlso found source code for SplatooD, which uses neslib / cc65.
https://github.com/SplatooD/splatoodQuote:
hybrid approach, where you use neslib
I might try to use a modified (expanded) version of neslib. Now that I've looked into it, I do think I understand it 99%. Some of the functions could be changed a bit. I'm not a fan of the current controller read functions, for example.
As for the main topic, I appreciate that you get low in the mud in the tutorials from your site. They have been quite helpful for me, specially undestanding how scrolling works in a lower level (neslib hides this) or understanding MMC3's IRQs.
dougeff wrote:
This gentleman has the newer example code (Shiru) on his github
https://github.com/peterhellberg/nesdev ... s_examplesAlso found source code for SplatooD, which uses neslib / cc65.
https://github.com/SplatooD/splatoodJust promise us that you won't put global data in .h files like these examples do
It's common practice. You'll find lots of dirty and ugly stuff in C code for 8 bits
But there's some dirty and ugly that's that way for a good reason (structs of arrays instead of arrays of structs, global variables instead of lots of function parameters), but I'm not sure there's any good reason to put data in an .h file, is there? I mean, if you really want to #include it instead of compiling it separately, at least name it something else so it's clear that it's not actually a header file.
(Sorry to hijack your thread, Doug)
Globals in an .h file are the least of my concerns.
There will be globals. Lots of globals. Where they are defined doesn't interest me. It takes the user 1 extra click to open an included file, if he's interested.
I appreciate your input, na_th_an. It's hard to tell if anyone actually uses my tutorial. The traffic on there is practically nil. I think I had over 90,000 unique visitors in the first month, and last month was 169 visitors. 90% of which see the front page and don't actually click on any real pages.
I actually just used your post on mmc3 bankswitching to figure out how to put code/constants/etc into a separate bank for cc65 today. In my case I'm using MMC1 (and the older cc65... I should consider updating) so I adapted it a bit. It definitely helped.
I'm certain I've gotten there from google a few times to remind myself how to do something. I can't recall what for the life of me, but I know I've seen that layout at least a few times. At any rate, it's been helpful before. It's never bad to have more development resources!
Last note, I see you added makefiles for stuff last month. That's cool, I've been getting into using them pretty recently. For some reason I had a crazy time wrapping my head around it, but I think I'm finally getting the idea. Done right, it's really powerful. (Especially for projects with many source files)
You mentioned in your most recent update that linux users had to uncomment things in the makefiles to make things work. This might be obvious, but if you're so inclined, you could remove that step. You could set that as a variable, like REMOVE="rm" then call it like you call $(cc65). It's pretty easy to detect someone's OS - especially if you need to treat Windows specially. You could do something like this:
Code:
ifeq ($(OS),Windows_NT)
REMOVE="del"
else
REMOVE="rm"
endif
# ...
clean:
$(REMOVE) *.nes
I've been tweaking a makefile for something I'm working on super regularly so I thought I'd provide the example in case it's useful. Sorry if that's super obvious, or you just aren't doing it because updating ~20 separate zip files is annoying. At any rate, there it is.
gauauu wrote:
I'm not sure there's any good reason to put data in an .h file, is there? I mean, if you really want to #include it instead of compiling it separately, at least name it something else so it's clear that it's not actually a header file.
It's an extremely common practice and well used convention. I'm sure I've seen it in at least a hundred programs. Here's
1 2 3 examples from the top results of a
google search for "binary to C".
Headers like this tend to be included in only one place, though, so there's really no practical problem with it being compiled more than once. (They're certainly not "global" data, in this respect. I'm not sure why you called them that.)
The only objection I can think of is that I guess you consider .h to be semantically reserved for
shared code only? I mean, sure that's a convention you can insist upon if that's your preference, but a whole lot of people have been using it this way for a very long time.
If you want to compare the alternatives, the code itself would merely exchange an
#include statement for an
extern, so that difference seems lateral to me? It's also adding one more compile operation to the build, as opposed to just an include into the file that consumes it. Overall, I'd expect the extra compile to have a little more overhead but it's probably not a significant issue either way except in some extreme cases... but if you'd like a "good" reason for preferring to #include that might be one.
On forum.gbadev.org, some of the more vocal regulars were sticklers about .h being semantically reserved for header-y things, such as type definitions, function prototypes,
extern, and the like.
In addition, at one time, GCC would bloat large
const arrays in RAM by orders of magnitude. This means, for example, it'd use 60 MB of RAM at build time to compile an array with 300,000 elements. (Source:
DJGPP FAQ) That could become taxing on some of the PCs in use during the early Game Boy Advance homebrew eraa, which were built for Windows 98. Converting the files to assembly language instead of C caused the build process to use less memory. Before the GNU assembler had
.incbin, one would use
bin2s to save time and RAM compared to
bin2c.
When coding in C for such limited platforms, most if not all your variables will be globals - even reusable variables (I, for example, have a bunch of variables defined in the ZP I reuse for common tasks). Of course, having several modules which import the globals they use would be cleaner, but once you get used to be monolithic, it's not such a big deal. cc65 is pretty fast in modern computers and having it compile your game only takes a couple of seconds. I usually have one main.c which includes everything (with things which include things). As long as I do it in the right order, no probs. 99% of my functions take no parameters but read from globals the data they need, even in cases where they are semantically taking parameters. That means that most globals are needed in almost any group of related functions (which I usually keep in the same source file), so the monolithic, just one .c, everything defined in .h, approach is quite fitting.
tepples wrote:
In addition, at one time, GCC would bloat large
const arrays in RAM by orders of magnitude. This means, for example, it'd use 60 MB of RAM at build time to compile an array with 300,000 elements. (Source:
DJGPP FAQ) That could become taxing on some of the PCs in use during the early Game Boy Advance homebrew eraa, which were built for Windows 98. Converting the files to assembly language instead of C caused the build process to use less memory. Before the GNU assembler had
.incbin, one would use
bin2s to save time and RAM compared to
bin2c.
If you're only targeting GCC, there's the
objcopy utility that can just turn a binary file directly into an object file, and you can bypass any intermediate text format that needs to be compiled/assembled. Not sure if other compiler toolchains have an equivalent.
Of course the ideal thing would be to get some sort of
#incbin into the C++ standard so we don't have to worry about which compiler we're using, but it doesn't appear to be on the roadmap as of C++17. So... we continue to convert binary to text and back again, I suppose. The endless toil.
Whether or not you want to put the data in a H or a C file, it's also prudent not to have a large amount of text in a file you need to recompile frequently, of course. (As always, relevant only in specific situations-- in the case of Splatood neither data size nor compile times are an issue.)
Quote:
(They're certainly not "global" data, in this respect. I'm not sure why you called them that.)
You're right, I'm not sure why I called it that. :-/
rainwarrior wrote:
The only objection I can think of is that I guess you consider .h to be semantically reserved for shared code only? I mean, sure that's a convention you can insist upon if that's your preference,
Well, I'm the new guy around here, so I'll submit to maybe being wrong since most of you seem to disagree with me
But yeah, it's all about semantics. .h means a header file, which is for declaration of symbols, not the definition of symbols. Keeping that consistent makes it easier for humans (and tools) to understand your code. When I'm looking at a codebase, I expect a header file to give me an overview of the function call api. Having a bunch of data confuses that.
Which is why I'm not arguing against #include'ing data. I don't care if you #include everything into one big file. I just think calling your data a .h file is horribly confusing, and semantically incorrect.
Quote:
but a whole lot of people have been using it this way for a very long time.
I would contend that they've been doing it wrong for a long time. But whatever, like I said, I'm willing to be wrong
And Doug, to get back at your original question: I like the level that your example code is at, and don't think you need to simplify it. I find your examples helpful
Oh, I'm not going to overwrite the current pages, I'm going to expand (maybe) with some kind of neslib-like examples.
Hi Nesdoug, just a quick comment - I don't know about anyone else, but I certainly used your tutorials and found them extremely helpful. Thanks for writing them!
From what I remember the first two or three just set everything up in terms of basic graphics, and I think your style is not to explain everything, but to explain key parts and then provide a complete working example. I think this works well already and is not too overwhelming.
I discovered them in the middle to late to last year, but from the structure I presume they arose very organically? If you were to redo any of them, it might be an idea to more cleanly separate the early tutorials, which build post on post in terms of skills and understanding, from the spacy mcshooty stuff, which is more like a devblog and less something someone could follow along with.
It's a great wealth of information and I often find myself digging in it for something I half remember reading, so thanks!
On the other theme of this thread, I've been using header files as more or less complete libraries - so all my tile routines are in one, for example, with their own global variables that are used exclusively for them. Is this considered poor practice? It's a little wasteful (in that I'm using 20 odd variables that could be reused elsewhere in the code) but it does mean everything is self contained and makes testing a lot easier.
gauauu wrote:
Well, I'm the new guy around here, so I'll submit to maybe being wrong since most of you seem to disagree with me
But yeah, it's all about semantics. .h means a header file, which is for declaration of symbols, not the definition of symbols. Keeping that consistent makes it easier for humans (and tools) to understand your code. When I'm looking at a codebase, I expect a header file to give me an overview of the function call api. Having a bunch of data confuses that.
Which is why I'm not arguing against #include'ing data. I don't care if you #include everything into one big file. I just think calling your data a .h file is horribly confusing, and semantically incorrect.
I don't think of your idea as wrong: it's a correct consequence of that semantic choice. I just think it's a choice, though. It's very common practice to do otherwise, and not normally harmful in any obvious way. There are plenty of people who share your preference, too.
Inline definitions have always had to be included with a header in C, and C++ doubled down on that with templates. (Open any STL header for an example.) I've seen some projects with a convention that puts inline definitions in yet another file (with a custom extension) included from the header instead.
I've even seen others try to do away with header files, and write some sort of processor program that automatically converts an intermediary ".cpp with annotations" file into cpp + headers as part of the build process. Kind of a simulated Java style, I guess?
I think gauauu is 100% right on what he says, but once I started coding for 8 bits platforms in C, I had to forget about readability, proper use of semantics, good practice and readability if I wanted to get proper results.
na_th_an wrote:
I think gauauu is 100% right on what he says, but once I started coding for 8 bits platforms in C, I had to forget about readability, proper use of semantics, good practice and readability if I wanted to get proper results.
Well, everything I've done on 6502 is in assembly so far, so maybe once I start doing some C, I'll find out that my strict ideas won't work
na_th_an wrote:
once I started coding for 8 bits platforms in C, I had to forget about readability, proper use of semantics, good practice and readability if I wanted to get proper results.
If you get to the point where tou have to sacrifice even readability, wouldn't it be better to just write well structured assembly code?
I never wrote C code for 8-bit CPUs, but from reading about it here in the forums it sounds like many of the benefits of using a high level language have to be thrown out the window if you want to get passable results, so I end up wondering if it wouldn't be better to not sacrifice anything and write proper ASM code instead...
tokumaru wrote:
na_th_an wrote:
once I started coding for 8 bits platforms in C, I had to forget about readability, proper use of semantics, good practice and readability if I wanted to get proper results.
If you get to the point where tou have to sacrifice even readability, wouldn't it be better to just write well structured assembly code?
Not if you want the same game logic code in the NES and PC versions of the same game. Or would it be better to
statically transpile assembly language to C?
Comparisons are easier to understand in C than assembly.
If (X > 50 && Y <= 80 + B)
tepples wrote:
tokumaru wrote:
na_th_an wrote:
once I started coding for 8 bits platforms in C, I had to forget about readability, proper use of semantics, good practice and readability if I wanted to get proper results.
If you get to the point where tou have to sacrifice even readability, wouldn't it be better to just write well structured assembly code?
Not if you want the same game logic code in the NES and PC versions of the same game.
If the "NES-appropriate C" is nasty enough, I think (might?) prefer having a readable and idiomatic "reference" C implementation on the PC, regardless of whether the NES version is written in unreadable C or unreadable 6502.
I don't find using global variables and NES-specific pragmas to impede readability much. Writing C is still 10-100x faster than writing the same thing in asm.
dougeff wrote:
Comparisons are easier to understand in C than assembly.
If (X > 50 && Y <= 80 + B)
I came here to say this, but Doug beat me to it. You can read and write this kind of thing it A LOT faster in C. There's quite a few bits of my games that were just as easy in assembly, but big sections of comparisons mixed with simple math were really tedious and error-prone.
That said, it would be nice to be able to "jump up" into C syntax from assembly for a few of these expressions. I see lots of information about dropping to assembly from a C program, but I think the reverse would be just as useful, even though it's not often discussed. (although I can think of quite a few hurdles that would make it complicated)
Quote:
be able to "jump up" into C syntax from assembly
I bet you could write a macro in ca65 to automate comparisons. Right
tepples?
dougeff wrote:
I bet you could write a macro in ca65 to automate comparisons. Right tepples?
I think you mean "right Movax12", who spent a few months working on a "high level" macro pack for ca65 a while back:
https://forums.nesdev.com/viewtopic.php?f=2&t=9272As a proof of concept, it was then applied as a "high level disassembly" of Super Mario Bros:
http://www.romhacking.net/documents/635/
tokumaru wrote:
na_th_an wrote:
once I started coding for 8 bits platforms in C, I had to forget about readability, proper use of semantics, good practice and readability if I wanted to get proper results.
If you get to the point where tou have to sacrifice even readability, wouldn't it be better to just write well structured assembly code?
I sacrifice *some* readability. To my untrained eye (although I understand and can produce assembly code), even unreadable C code is easier to grasp, follow and understand. What I mean with unreadable code, of my own, could be something like where I have to add a comment to help me remember this si not a typo, or strange reordering of cases in a switch so I can share code and save bytes, using a less intuitive approach to an expression 'cause I know cc65 will generate better code, etc. And, as mentioned, the resulting mess is still easy to port to different systems.
Code:
case 3:
// Gravity
envy = enems_lut_falling [enmy [enit]];
if (enmy [enit] < ENEMS_LUT_FALLING_MAXIDX) enmy [enit] ++;
// Correct! no break here. Move left/right
case 2:
case 7:
if (enx1 [enit] || half_life ) envx = enmx [enit];
break;
...
Code:
if (en_ac [gpit] == 8) {
// Killed baddie
if (0 == --en_x2 [gpit]) en_ac [gpit] = 0;
oam_meta_spr (en_x [gpit] >> FIXBITS, (en_y [gpit] >> FIXBITS) - camera_position, gpit << 4, spr_explosion);
} else if (gpjt = en_ac [gpit]) switch (gpjt) {
// Yeah, this is an assignment. Don't panic
case 1:
if (rand8 () < SHOOT_TE...
They joy of cc65 is that when it doesn't behave you can always inline a better solution, as in:
Code:
// cc65 could do better, so:
/*
rda = c_enems_t [enidx];
rdb = c_enems_yx1 [enidx];
rdc = c_enems_yx2 [enidx];
rdd = c_enems_mn [enidx];
*/
__asm__ ("ldy %v", enidx);
__asm__ ("lda (%v), y", c_enems_t);
__asm__ ("sta %v", rda);
__asm__ ("lda (%v), y", c_enems_yx1);
__asm__ ("sta %v", rdb);
__asm__ ("lda (%v), y", c_enems_yx2);
__asm__ ("sta %v", rdc);
__asm__ ("lda (%v), y", c_enems_mn);
__asm__ ("sta %v", rdd);
The code produced for the commented section was five times as long and fiddly, as it did recalculate the offset for each access when you'd rather use indirect addressing sharing the smae index register as enidx is unsigned char.
Case fallthroughs are standard practice, common in places like the linux kernel. The comment is usually just // fallthrough.
This is not exactly what I had in mind, but I just ended up using a slightly modified neslib code.
Here are my detailed neslib notes.
https://nesdoug.com/2017/04/13/my-neslib-notes/And 2 simple example source codes.
https://nesdoug.com/2017/04/13/neslib-example-code/Let me know if this is easy to follow. Or, if anything needs more explanation. Thanks.
Also, I changed the 'theme' to a darker background, because the white background kills my eyes at night.
The dark mostly looks nice, although the contrast is a little low on my monitor. Slightly darker background or slightly brighter text would be a little easier.
Changed themes again. This one has a little more contrast.
dougeff wrote:
Let me know if this is easy to follow. Or, if anything needs more explanation. Thanks.
I don't know if I'm the right one to judge this, as I know neslib in and out, but I think it's easy to follow and I will be linking your articles when I want to introduce the library to other people.
EDIT: about the ppu_wait_frame funcion, I don't think it's buggy, just not suited for games with splits as it will skip every 6th frame in NTSC. It works great in other situations.
Added an example of making a BG by importing BMP to NES Screen Tool, and then compressing as RLE (and using neslib to compile NES file)
https://nesdoug.com/2017/04/22/nes-scre ... mp-import/https://nesdoug.com/2017/04/22/import-f ... nd-as-rle/Here's a screenshot of the example file.