Quote:
The Keil compiler for the 8051 does a reasonably good job at producing code for a device that was never meant for C.
Any serious C compiler targetting the 6502 (by "serious" I mean who aims to produce reasonable code, which is less than 2x worse than hand written assembly) should OF COURSE do this. I didn't mention it because I thought it would be obvious. There is no way a "stack" based thing can be implemented efficiently on the 6502, no matter if it's software or hardware stack. However, it you have to choose between the real stack and a software one, the hardware one is a
little more efficient, because you can use PHA/PLA to acess the top level, and "lda $100,X" style adressing is only required to adress subsequent bytes. Adressing a software stack with lda (),Y is an asbsolutely TERRIBLE solution (performence wise I mean).
Unfortunately there is obstacles for such an analysis. Function pointers, and separate compilation.
Imagine a table call with 32 different functions.
Theoretically they should not be mutually exclusive, so all 32 functions should use different locations for temp storage and so on. This would waste a LOT of RAM.
In practice, they are mutually exclusive, and you'd like the compiler to use the same locations for the temporary storage.
Quote:
When writing C programs, are you careful to use 8-bit variable sizes as much as possible? Most I've seen use ints more than anything else, plus many passed variables tend to be pointers. So effectively anything above 1 parameter is going to be using the stack anyway, and that's most functions.
The size of arguments is the programer's responsability. A compiler could detect cases when an "int" can obviously be transformed in an unsigned char, but still it wouldn't be able to do it in all cases (and in some cases it would actually introduce dangerous bugs if the data was actually meant to be int).
This could also be solved by making "int" 8 bit, breaking all standard for C, but making the code more intuitive. "int" should typically be the size of the processor's native registers, in our case, 8-bit.
About the part where most passed variables tends to be pointers, it's up to programming style. The question to be asked is : If you wanted to pass 3 pointers in assembly langage, how would you do it ? Then the answer should be introduced int the compiler as much as possible. In my case, I think I would do it the optimisation 4 way, getting rid of the stack passing. If the pointers are results of a calculation, then it doesn't work any longer sadly.
Quote:
Really, the primary reason cc65's code is so terrible is that the C way of doing this is not the 6502 way of doing things.
Honnestly I don't know. C was designed in the 70s, so was the 6502.
Because nobody really made a (free) really optimising 6502 compiler yet doesn't mean it's impossible. It just means it's not a trivial thing to do.
Chances are that commercial compilers were made during the 80s were of decent quality. However, they are probably lost, or is there a cracked version of one of them available, that could still run somehow today ?
I am all ears if you can suggest another high level language that is more suited for the 6502. I did not really mention C, but since it's the most used language ever, especially for embedded platforms, it would be hard to think of something else.
EDIT : What I'd have in mind is something close to David A. Wheeler's "3rd solution".
However, the optimisations mentionned would only work between calls that doesn't use function pointers, and that are within the same .c file. When calling a function by a pointer or an "external" function, some kind of global convention is required, and in thise case I think the stack usage is "required" if you want to keep C compatible. Think of the case if you call a function in another .c file, which calls back the same function. There is recursivity, but no way to detect it by compiling a single file at a time.