It's been a while since I've read about this, and I'm far from a 6502 expert, but I think the biggest issues are:
1) There's not a cheap, straightforward way to get a conventional frame pointer (i.e. indexable pointer to local variables on the stack). I imagine one could do something with the indexed zero-page modes, but I'm not sure how well that works in practice.
2) None of the registers are large enough to hold a full memory address, which considerably complicates any pointer arithmetic beyond increment/decrement.
I'm even less familiar with Z80 than I am with 6502, but I gather that the IX and IY index registers were added to it to address similar limitations in the 8080 architecture.
but I gather that the IX and IY index registers were added to it to address similar limitations in the 8080 architecture
The 8080 was much better than the 6502 in terms of 16-bit registers, even without IX and IY.
The architecture was:
A an 8 bit accumulator
B C two 8 bit regs or combined 16 bit
D E two 8 bit regs or combined 16 bit
H L two 8 bit regs or combined 16 bit
I did a lot of Z80 programming and I usually ignored IX and IY. They were useful for certain things. But they were slower than the 8080's registers, because IIRC they needed an extra opcode byte.
The Z80 did, however, add lots of neat stuff that used the existing 8080 architecture registers.
E.g., here's a single instruction equivalent of today's memmove() Unix library function. Do this:
BC <-- stuff a 16 bit count into it
DE <-- stuff a 16 bit destination address into it
HL <-- stuff a 16 bit source address into it
then do the LDIR instruction and you get a memory copy in a single instruction. Up to 64 K bytes, which was the entire address space.
IIRC the instruction was interruptible on a byte-by-byte basis, so it's not like you ruined your interrupt response time.
> IIRC the instruction was interruptible on a byte-by-byte basis, so it's not like you ruined your interrupt response time.
Yes, LDIR is basically the same as the LDI instruction with the little difference that the PC is reset to the start of the LDIR instruction until BC is 0. It's basically a microcoded LDI+conditional jump, and interrupt handling, DRAM refresh etc... happens at each step, since from the CPU's point of view, it's just executing a lot of single instructions while LDIR is running.
On 6502 you are usually better off with bytecode (SWEET16, p-code) or threaded code (FORTH) to improve code density. There are even people making new languages (Cowgol, PLASMA) that are self-hosted on the 6502!
Original 16bit x86 was 8080/8085 assembly upgrade to 16bit (source code compatible to the 8080), and the Z80 was a upgraded clone for the 8080. You can get a assembly code for a 8080 and just compile it for the 8086 and it would work.
So, it's logic that compilers that target Z80/8080/8085, generate something similar to what any compiler would do on x86.
> I imagine one could do something with the indexed zero-page modes, but I'm not sure how well that works in practice.
That's it exactly. Page zero essentially becomes 128 16-bit registers. But of course a lot of things are hungry for page zero resources. And it isn't exactly speedy. So you could put a pointer in page zero, and reach 256 bytes off of it.
1) There's not a cheap, straightforward way to get a conventional frame pointer (i.e. indexable pointer to local variables on the stack). I imagine one could do something with the indexed zero-page modes, but I'm not sure how well that works in practice.
2) None of the registers are large enough to hold a full memory address, which considerably complicates any pointer arithmetic beyond increment/decrement.
I'm even less familiar with Z80 than I am with 6502, but I gather that the IX and IY index registers were added to it to address similar limitations in the 8080 architecture.