Thank you very much for your input! You have given me a lot to think about.
Your points reinforce the insight this language is trying to express: that low-level language design patterns should be expressed in a higher-level language. I think it is inappropriate for the design patterns of established languages (such as C) to be back-ported to this architecture. For instance, I don't know of anyone (not to say that there aren't any) that use a stack-based expression parser in hand-written assembly.
This will be a cross-compiler. No use in trying to write a self-hosting compiler on an NES now is there?
Expression parsing is stack based, but only at compile-time. The expression processing will be reduced to standard 6502 mathematics.
Example:
Code: Select all
r = a - b + 4Code: Select all
clc
lda b
adc #4
sta ___temp0
sec
lda a
sbc ___temp0
sta rHowever this static assignment of temporary variable space means that the entire scope of the expression must be constant, knowable at compile-time and consistent. This is why function calls within expressions are not possible.
Here's a more detailed example:
Code: Select all
byte someFunc(a, b) {
byte tempVal = a + b;
tempVal |= %00100101;
// This expression allocates temporary values on the static expression stack
return tempVal - a + b;
}
// This expression must also allocate temporary values
byte myVal = c - someFunc(9, 11) + 9;As for larger arrays, I had thought about using two different addressing modes for small and large arrays, but I gave up on this idea as there were things that would get inefficient. That's why I have the notion of "memory files" like you were asking about. Here's a quick example:
Code: Select all
word myPtr = mapDataArray + (mapYPos - 1 << 5/* x32 */) + mapXPos - 1;
// Read the tile we're standing on
// Pointer Y Index Value
byte tile = read(mapDataArray, 32 + 1); // 32 + 1 is optomized down to 33 at compile time
// Read the tiles around us
tile = read(mapDataArray, 0); // North-West
tile = read(mapDataArray, 1); // North
tile = read(mapDataArray, 2); // North-East
tile = read(mapDataArray, 32 + 0); // West
tile = read(mapDataArray, 32 + 2); // East
tile = read(mapDataArray, 64); // South-West
tile = read(mapDataArray, 64 + 1); // South
tile = read(mapDataArray, 64 + 2); // South-EastCode: Select all
ldy #0
lda (mapDataArray),y
sta tile
ldy #1
lda (mapDataArray),y
sta tileNote that the call graph-based variable space reuse would be a great optimization here to conserve zero-page space.
I had never thought of doing objects that way. That's a great idea! I'll have to give that some thought.
As for multiplication operations as functions, that's something I gave a lot of thought to. In the end I decided it was best to force the user to use a function so they understood the performance impact of the operation. I've seen plenty of timing-critical C code with division inside of tight loops on platforms that do not have hardware division, then the programmer trying in vain to understand why it's taking so long. I took the Phythonic approach of "explicit is better than implicit".
As for memory reuse, I've never used that in my demos and never had an issue with running out of RAM, even on NROM boards. Then again I use a set of sixteen zero-page variables for all of this temporary stuff, so I guess that is a limited form of variable reuse. Anyhow, that's an optimization I am saving for later.
What's in a name? But if you're curious, here's the project page. It's called NICHE, for Niche Instruction Code for Homebrew Enthusiasts. I've always wanted to name something with a recursive acronym