- internal CPU registers are 8 bits, no more, no less and you only have 3 of them (5 if you count the stack pointer and processor status register).
- fixed 8-bit stack pointer so things like automatic variables and pass-by-value can't take up a lot of space.
- things like "access memory location Z + contents of register A + this offset" aren't possible without a lot of instructions.
- no hardware divide or multiply.
Many CPUs have instructions that map neatly to C operations, but not 6502. With enough instructions C is hostable by any CPU (e.g. ZPU) but a lot of work is needed to do that on the 6502 and the real question is - will it fit in 16K, 32K, as most 6502 CPUs only have 16 address lines - meaning they only see 64K of addresses at once. Mechanisms exist to extend that but they are platform specific.
IMHO Z80 is better in this regard with it's 16-bit stack pointer and combinable index registers.
> - fixed 8-bit stack pointer so things like automatic variables and pass-by-value can't take up a lot of space.
Also, the stack isn't addressable. The only stack operations the CPU natively supports are push and pop. If you want to access values anywhere else on the stack (say, if you're trying to spill a couple of local variables to the stack), you're going to have a bad time.
No, the stack is in the next page up ($0100 - $01ff). While it's accessible as memory, the 6502 doesn't provide any addressing modes specific to this page; accessing it involves multiple instructions, and requires the use of the X register as a temporary.
6502 was nice only in comparison with Intel 8080/8085, but it was very limited in comparison with better architectures.
The great quality of 6502 was that it allowed a very cheap implementation, resulting in a very low price.
A very large number of computers used 6502 because it was the cheapest, not because it was better than the alternatives.
For a very large number of programmers, 6502 was the 1st CPU whose assembly language they have learned and possibly the only one, as later they have used only high-level languages, so it is fondly remembered. That does not mean that it was really good.
I also have nostalgic happy memories about my first programs, which I have entered on punched cards. That does not mean that using punched cards is preferable to modern computers.
Programming a 6502 was tedious, because you had only 8-bit operations (even Intel 8080 had 16-bit additions, which simplified a lot multiply routines and many other computations) and you had only a small number of 8-bit registers with dedicated functions.
So most or all variables of normal sizes had to be stored in memory and almost everything that would require a single instruction in modern microcontrollers, e.g. ARM, required a long sequence of instructions on 6502. (e.g. a single 32-bit integer addition would have required a dozen instructions in the best case, for statically allocated variables, but up to 20 instructions or even more when run-time address computations were also required, for dynamically-allocated variables.)
A good macroassemmbler could simplify a lot the programming on a 6502, by writing all programs with a set of macroinstructions designed to simulate a more powerful CPU.
I do not know whether good macro-assemblers existed for 6502, as in those days I have used mostly CP/M computers, which had a good macro-assembler from Microsoft, or the much more powerful Motorola 6809. I have programmed 6502 only a couple of times, at some friends who had Commodore computers, and it was weak in comparison with more recent CPUs, e.g. Zilog Z80 (which appeared one year later than 6502).
> I do not know whether good macro-assemblers existed for 6502
They certainly did. I don't know about the communities that grew around Commodore, Atari, or other 6502-based computers, but in the Apple II world, there were multiple macro assemblers available. Possibly the most famous was Merlin. As a pre-teen, I used the Mindcraft Assembler. Mindcraft even sold another product called Macrosoft, which was a macro library for their assembler that tried to combine the speed of assembly language with a BASIC-like syntax. The major downside, compared to both hand-coded assembly and Applesoft BASIC (which was stored in a pre-tokenized binary format), was the size of the executable.
Edit: Speaking of simulating a more powerful CPU, Steve Wozniak implemented the SWEET16 [1] bytecode interpreter as part of his original Apple II ROM. Apple Pascal used p-code. And a more recent bytecode interpreter for the Apple II is PLASMA [2].
6502 is nice to program in assembler. For a compiler it is an atrocious platform to support. The limited stack, the 8 bit limit, the zero page idiosyncrasies, the
page crossing bugs (some corrected in 65C02), etc.
Z80, while also full of adhoc-isms is a much more pleasant target for a C compiler, only just for its 16 bit register and stack.
I only ever hear glowing/nostalgic reviews of 6502 programming, I guess from the retro/8bit gaming scene, curious what you find so atrocious.