rainwarrior wrote:
dustmop wrote:I have implemented this technique in
co2, a scheme-based compiler that I took over for use in an upcoming project. See the file
casla.scm for the code. I can imagine an assembler doing something similar, allocating memory locations at link time, though it would need to be aware of jump tables and similar stack manipulating control structures. Possibly even cc65 could get support, assuming someone wanted to add it.
Have had some time to look at this example, and it's interesting to see.
With the very functional style scheme code you're writing, I can see how the call stack quickly builds and local temporaries are a rapidly mounting problem. It really does demonstrate why you had a need to write a system to manage them as statics.
A lot of the example co2 code in the repo was not written by me, but a previous maintainer. Though I generally enjoy a functional style, I personally don't think it's necessarily the best fit for NES work, because of the lack of GC or even convenient memory allocation. Side-effects all the way!
But anyway, yes, I've run into the question of how to handle temporaries / locals / parameters in every project I've made, and as they get more complex the issue continually gets worse. Especially as call graphs become taller and more criss-crossed. Though I believe you that you're genuinely curious, I can't explain why you haven't experienced this problem before. I guess tepples and I just have a different perspective.
For a better example of where such a tool would have come in handy, have a look at Filthy Kitchen's source.
sprite_space.asm
draw_picture.asm
player.asm
intro_outro.asm
I define a buffer of 16 bytes called
values, then inside each module, individual bytes are redefined with more readable names.
For example,
sprite_space is a leaf module that handles OAM cycling, and it defines
max_idx,
first_idx, and
second_idx to use values 4, 5, and 6, respectively. It gets called by
draw_picture, which only uses values 0 and 1. That in turn is called by
player which uses 1, 2, 7, and 8. Reusing 1 is safe because it's only clobbered during a tail call to DrawPicture, but it sure would be nice to have a tool prove that instead of relying on manual inspection that there's no bugs. Player is top level, but anything else that uses DrawPicture is in a similar situation.
The real headache comes when adding memory needs to lower-level calls. For example,
second_idx was added to SpriteSpace pretty late in development. I had to push a bunch of values around higher up the stack, it was very tricky and led to some bugs that were hard to track down. In fact, keeping track of bugs that I've run into on recent projects, about 60-70% were due to conflicts of this nature.
Finally,
intro_outro uses values 4 through 8. Why can't it use 0 to 3? I haven't the faintest idea anymore, and the only way to find out would be to look at every dependency and see what they each require. Personally I'd rather focus on other areas of development.
You may be over-estimating the difficulty of creating the tool I'm describing. The linked algorithm in scheme is only like 50 lines of code. While making Filthy Kitchen, I had started work on a python tool that would act like a "linter", to analyze the call graph and
values reservations to find any conflicts. I didn't finish it, as I felt I didn't understand the problem well enough yet, but writing this integrated scheme version has helped a lot in that area.