The key to realizing efficient stack usage is to separate your concerns. You need a split data/return stack for maximum efficiency. The split is vital because it prevents you from ever having to duplicate parameters.
For example, suppose we have the following C code:
Code:
void fnA(int a, int b) {
...
fnB(a);
...
}
void fnB(int a) {
...
fnC(a);
...
}
void fnC(int a) {
/* do something with a here */
}
Your knee-jerk reaction is, "Duhh, don't do that." I would agree, except real-world code does not offer nearly the opportunities to optimize this kind of code as you'd like. As a result, I'd wager about 33% to 40% of space used on the C stack is devoted to "threading parameters" to the appropriate functions. That means, 33% or so of stack space is actually
wasted. This is due entirely to the fact that C uses a single stack for both data and return information.
Pascal is worse because it allows for lexically scoped nested functions and procedures. It uses the stack for not only return and parameter information, but also for extra pointers to nested lexical environments. Combine that with Pascal's support for passing non-trivial objects by value, something C doesn't provide usually, we find ourselves with excessively heavy stack utilization.
If you use the CPU's stack strictly for holding return information, you'll find that you can nest your subroutines 124 times before you run out of space. That is a
huge amount of nesting. I work on a professional Java servlet e-commerce application for a rediculously well-known company, handling thousands of hits per server per hour, and its method nesting levels, while they can get awfully deep, almost never reaches over 100. Therefore, the 6502's stack requirements
ought to be enough for really hard-core, heavy-duty applications even today.