Thus proving my point. mid-80s-era BASIC implementations (of which EhBASIC is an instance) simply lack activation frames, thus preventing effective use of structure and, in particular, modules when programming. Nobody needs local variables!!
Of course, you can fake locals with arrays:
Code: Select all
10 SP=0:REM My fake stack pointer
20 DIM IS(100), SS$(100):REM The stacks.
...
1000 REM Compute celcius from farhenheit the HARD way.
1010 SP=SP+1
1020 IS(SP)=(IS(SP-1)-32)/180
1030 R=IS(SP)*180
1040 SP=SP-1:RETURN
...
All you're doing here is hard-coding the activation frame (check it -- it even allows recursion, but I still won't recommend it) mechanisms by hand. It might even compile efficiently (it certainly won't interpret very fast!).
This reminds me of a VisualBasic 3 programming project I once had to do, where I sorely lacked various features provided by proper activation frames. I ended up not only re-implementing them as above, but I also implemented "virtual CPU registers" as global variables too, to facilitate parameter passing. This was for a dentistry management application. The code was ugly, but the code ran correctly. We shipped on schedule.
However, the above approach simply doesn't give you the benefit of modular programming, and moreover, heavens help you if you attempt object oriented or functional programming.
Languages like Lisp or Scheme have activation
records, not frames. That means, these things are actually
data structures, allocated on the heap just like any other object a program would manipulate (this is taken advantage of in Scheme by exposing
continuations, an entire chain of activation records). These provide all the same benefits of activation frames, but may be more suitable to the 6502 because they're normal data structures, and not based on a stack. As such, you can implement them much more freely (their semantics aren't so hard-set in concrete). Hence, I actually expect a language like Lisp/Scheme to outperform C on the 6502.
I should note that Forth on the 65816 relies heavily on stacks, and even in the best of cases (subroutine threaded with primitive inlining), you're looking at an average of 10 clock cycles per Forth primitive. Colon definitions or their equivalents are a
minimum of 12 cycles. So, as a general rule, if you have a 10MHz 65816, you're pulling about 1 Forth MIPS. It varies erratically, and deeply nested colon definitions will drop that to about 0.5 Forth MIPS, but still amazingly consistent. I would expect all MIPS measurements to drop by a factor of 2 for the 6502.
Although it relies heavily on stacks, it gets its performance from not having activation frames -- it
implicitly assumes the operands you're working with are always at the top of the stack, something even a 6502 can handle relatively easily.
Someday, I'll toy with the idea of making a Lisp-like language of some kind for the 65816, based on my experience with Haskell.