tokumaru wrote:I don't know about C, but in assembly I have a chunk of ZP dedicated to local variables and function parameters, and I use a macro to allocate the space needed for each function. The macro can start allocating space from the very first byte of the ZP chunk, or you can optionally specify an offset into the chunk. These offsets can be specified using what I call "memory counters", which are symbols that are incremented automatically when you allocate memory using them, so if I have a bunch of nested functions, I just have to use the same memory counter for all of them and their locals will never overlap.
So, am I understanding this correctly? You're basically using a stack.
That's actually what I wanted to avoid, so that the usage of the temporary variables uses the least possible ROM space and CPU time.
Using counters before and after every function and accessing the temporary variables in the
LDA Temp, X manner takes away from the performance.
In this case, I think, I would be better off by simply using non-zeropage global variables.
Using a non-zeropage global variable should be faster and smaller than setting X every time and then accessing the variable with the offset.
And, at least in my case, the non-zeropage variables should be more than enough to give each function in the whole program its own distinct variables for local usage, so that reusing isn't even necessary.
calima wrote:I'd probably make a program that calculates every possible execution tree, then passes that to a register allocation algorithm.
This would be the optimal solution, but this would require to write your own source code parser, wouldn't it?
rainwarrior wrote:Your code will naturally have some structured relationship between functions, and you can group your temporary usage correspondingly as well as you can fit it to the existing relationships. i.e. this group of functions will mostly use temporaries i,j,k,l and this other one will mostly use m,n,o,p...
I think this is a sub-optimal solution because of the following reason:
Within the same group, you need to make sure that variables aren't reused between functions since they call each other.
However, if two functions belong to different groups, they can share the same variable.
So, to me it doesn't make sense that the functions to process the game's characters belong to the group that use i, j, k, l while the screen buildup functions use m, n, o, p.
Because MoveCharacter and CheckCollision should not both use i since MoveCharacter calls CheckCollision.
But there's no reason why MoveCharacter and DrawBackgroundTiles should not both use the variable i since those functions never overlap each other.
So, the grouping of functions and assigning variables to each group is a little backwards because reusability comes into play specifically when two functions are
not related.
Use variable i for screen buildup, character processing and hardware sprite calculation since they are unrelated to each other.
But never share variable i between the functions MoveCharacter, CheckCollision and StartMercyInvincibility because these functions call each other.
(Unless you're talking about variables that need to be consistent between function calls like the mercy invincibility counter after the player was hit. But that's something completely different anyway since we're talking about variables that are used in a purely local way, not about variables that are intended as actual global variables.)
Rahsennor wrote:Would anyone here be interested in such an "optimizing" assembler?
I would rather be interested in something that can turn local variables into shared global variables on the source code level, so that I can keep using the same compiler and assembler for my projects as before (cc65) and so that I can easily see every instance of the optimization process myself.
However, this would require that this is also done on C code, with each local variable and function parameter.