There's something magical about writing code in C for these old 8-bit and 16-bit CPUs. You really feel like you're in control. You can inspect the assembly output and actually understand it. You can do inline assembler to optimize critical sections. 32-bit CPUs lose some of that magic, because the instructions sets are usually much more complex. However, RISC ISAs like RISC-V are still plenty of fun...
> There's something magical about writing code in C for these old 8-bit and 16-bit CPUs.
Eh, I'm not sure I would agree, about the 6502 specifically. It's a pretty bad match to C since e.g. it doesn't have a relocatable stack and common pointer-based operations are quite clunky despite the provision for several address modes. FORTRAN-like languages really do match quite a bit better. In a different way, so does FORTH.
Also, plenty of people did have fun writing assembly code for the MC68k, which is a 32-bit CISC architecture. It's really only x86 (and maybe VAX) that tends to be a bit clunkier.
Having written 6502 assembly but not targeted 6502 with C, I tentatively agree, but if you can fit your program in a 256 byte stack, things should be... mostly fine? Except for the fact that you want to do pointer indirections through the zero page. Clunky!
Personally if I'm going to have fun writing assembly for a project like this, I'm not too picky about the particular architecture.
The dominant C compiler (CC65) has a separate stack for parameters and local variables, and keeps SP as a pointer in zero-page. This is a nice general approach that allows for deep stacks, recursion, and lots of locals, but isn't very efficient.
Newer experimental 6502 languages either forbid recursion (cowgol) or limit parameters passed to functions (C02), but I can see a language using a hybrid of stack and zero page to keep locals and parameters.
The drawback of high-level programming on the 6502 is you are always looking for ways to make it better... :)
They’re fast, but you’re not using them well if you’re writing C, because the compiler is not good at putting things like local variables, function arguments, or globals in the zero page. It’s only 256 bytes and you can’t easily share.
The MSP430 is a fun way to get back to that sort of simplicity with a fairly modern chip; it's a 16-bit processor with a focus on power efficiency. They've got a GCC toolchain and a port of the Processing IDE ('Arduino') called Energia:
... and it is a lot more fun when the toolchain runs on the target itself. With microcontrollers it's hardly possible (but see RetroBSD), but not needing a workstation to service an otherwise self-contained little machine somehow feels special.
I found MIPS (R3000), SPARC, and MC68k about the right size for an ISA. Things like PIC (and similar microcontrollers) and older 8bit CPU like the 6502 and Z80 always felt a little limiting to me. I think those (MIPS etc.) were also a great point for higher-level (C, etc.) languages to be useful/interesting.
> You can inspect the assembly output and actually understand it. You can do inline assembler to optimize critical sections. ...
You can really do this on any modern platform as well. It just takes a bit of effort. Understanding performance characteristics can be an order of magnitude harder, though.
What OP is attempting to do is actually not really something that was done 'in the day'. By the time games developers were shifting from writing in ASM the idea of development 'on the hardware' was largely disappearing, and most dev studios had low-end minicomputers or (16-bit, mostly) workstations running cross-compilers or cross-assemblers. Most indie devs, of that era, were sticking with ASM - it didn't help that the native compilers were often outrageously expensive for an individual to purchase.
That said, I understand the allure of trying to work 'native', and it's a mistake/trap I often fall into myself.
The 6502 only has 66 extremely simple instructions, and it's slow enough C is a disadvantage and you would need to learn 6502 assembly to know how to write good C code anyway, so I don't see the point in avoiding it. I started writing a Famicom game for the fun of it, and the ISA is very easy to pick up because of it's so tiny and uncomplicated.
Yes. But OP will learn (hopefully) and that's the fun of it.
Anyway, for the C64 many of your "APIs" -- especially for games -- are accessed by peeking and poking memory-mapped locations, careful usage of the zero-page, etc. C doesn't help you with that stuff but assembly loves it.
If you aren't in there counting cycles you are leaving so much on the table, and there just isn't that much table!
Another thing to look at and try for the C64 is KickC.
It's modern and being worked on. It's not true C and that's not it's intent. The intent is to produce efficient code for the 6502 and C64.