Hacker News new | ask | show | jobs
by cmrdporcupine 1733 days ago
I haven't looked at this closely, but 6502 really doesn't lend itself to C compilation. Three registers, only one of which works with the ALU, awkward immovable stack, etc.

The 65816 is a better target (moveable direct page and stack and some wider registers), but also awkward with its register mode switching.

5 comments

From what I understand, LLVM-MOS treats large parts of the zero page as virtual ("imaginary") registers, so you have no shortage of that (https://llvm-mos.org/wiki/Imaginary_registers). Then, sufficiently advanced compiler technology improves the stack situation (https://llvm-mos.org/wiki/C_calling_convention).
6502 assembly has the distinct advantage of having special page-0 instructions for reading/writing from memory, including, if I recall correctly, the ability to take a 2-byte sequence and treat it as a 16-bit value (or was that in the AppleSoft ROM?)
The main way to do pointer indirection (without self-modifying code) is to use the zeropage-specific indirect addressing modes, which use a 2-byte address stored in zero page as a pointer to a byte in memory. (And on the original 6502, the only available addressing modes for this forced you to use the X or Y register as an index, so you had to set it to 0 first!)
You can treat 2 bytes (not just in the zero page, though) as indirect jump addresses, yes.

Doing something like "JMP ($2345)" will jump to whatever $2345/$2346 is pointing to.

It's a little amazing how much 6502 assembler sticks with me 35 years later.

But only a little. I didn't have the money to buy an assembler or the skill to write one so I would write out my programs in long-hand on graph paper and hand-assemble them before entering hex codes manually. While not the most efficient process, it did do a good job of encoding things into long-term memory.

Haha, yes, I can relate. I didn't do any 6502 coding for ~25 years and it mostly just stuck around. Apparently it's like riding a bike.

In the meantime I've forgotten most of the 68000 and z80 instruction sets.

I remember being in high school, reading K&R and trying to figure out how I could get a C compiler running on an Apple ][. Never did, but it was a useful intellectual enterprise.

My second (and last) assembly language after 6502 was 370 which replaces the "awkward immovable stack" of the 6502 with no hardware stack at all. Applications are completely responsible for maintaining their own call stack.

Not only C, any language that thinks there’s other things than global state.

If all your functions are void foo(void) and you don’t use local variables (or your language doesn’t support recursion, in which case all locals can be given a fixed address), targeting 6502 is fine (it also helps if you avoid floating point, use 8-bit variables where possible, etc)

Not supporting recursion also means you can statically compute maximum stack depth. That way, you can avoid linking code that would overflow the stack.

The cool thing about LLVM-MOS specifically it that by using the zero page as virtual registers you sort-of get the same output with 'regular' code as opposed to this 'global variables' style of programming.

I recall a tutorial for 'cc65 optimizations'[0] which basically destroys a well-structured C program in order to do all of these optimizations (like making everything global) and it was absolutely terrible, code-wise. Well, the end result was probably fine, but it's just a shame these 'optimizations' were needed.

[0] I think it was this one: https://github.com/ilmenit/CC65-Advanced-Optimizations

Nice article, but it doesn’t mention the really gnarly stuff such as using the fact that a subroutine happens to return with some flags set, or with some fixed value in the X register to shave of some initialization instruction in the code calling it.

A main advantage of the 6502 is that it only has 64 kilobytes of memory ;-). That means sufficiently advanced and motivated programmers can keep the entire program in their head, and also nudges them to avoid bloat such as the use of 16-bit integers.

Zero page is great, but has limitations, for sure. Lots of moving stuff back and forth into the accumulator in order to do anything with it. And not relocatable like in the 6809 or 65816 "direct page".

Some nice simple extensions to the 02 architecture would be:

1) relocatable direct page and stack like in the 816. 2) some way of aliasing A to a direct page address to avoid doing it by hand.

I think you could use zero page as the data stack. Treat X is your frame pointer, and the zero page "address" is then the offset into the frame. Most instructions have a zp,X addressing mode. (This scheme works well with (zp,X) too.) LDY zp,X and STY zp,X are available, and the useful read instructions have abs,Y forms. So you can do lookups into global tables with an 8-bit local variable index without having to save X.

You'd need a little region for making use of (zp),Y, probably callee-saved, putting previous values on the return stack with PHA.

I wonder if the CSG-65CE02 wasn't an attempt to make C easier for the C6x/c128 line. Unfortunately it never saw the light of day except as a serial controller and isn't available today

https://en.wikipedia.org/wiki/CSG_65CE02

They actually address some of that on their project page, see: https://llvm-mos.org/wiki/Findings
It's a good read, but I still maintain that the immovable zero page and stack make the '02 sub-optimal. The 816 lets you move both around, and the WDC C compiler at least does some nice things with this to allow a proper stack frame.

I suspect that an LLVM backend for the 816 would have to be something quite a bit different from the 02.

The downside of the 65x816 compared to the 65x02 is the address/data line multiplexing. In order to add 8 more address lines without going above 40 pins,[0] they multiplexed them onto the data lines. So to decode the address, you need some support circuitry for latching and gating. The 65x816 datasheet (from WDC) gives a schematic for doing so, but it’s not as simple/clean as a 65x02.

I personally would choose the 65x816 over the other for a new design, but I can understand why it’s not as popular.

[0]: 40 was the de facto maximum. Although, the M68k had 64. That thing was a monster in size.