For the last while (month?) I've been been working on making some high level language to interact with the NablaVM. I know in theory how things should work, but we all know that when theory meets practice there are always some headaches, and given the structure of the NVM, I got a lot of them.
Some of the headaches were caused by the segmentation that occurs from the functional units created to segment parts of code.
When creating a language that emits the NASM my first thought was to use each individual function unit as a 1-to-1 for the higher level language functions. Seems great in theory, but a serious issue arises in how to get data to another function when called. Obviously, at we would want to leverage the global stack. Easier said than done; Unfortunately for me this lead to a whole heap of management code that needed to be written to manage function frames in the global stack, which was a nightmare. Thinking that there had to be an easier way I decided to go for a conventional approach. One where a "module" of the program would encompass a NASM function, and any higher-order functions would be represented by labels and moved to with branches... As I worked through this idea I discovered that the segmentation of instructions and data was out to get me. I didn't have a convention to retrieve where in the execution cycle I was so it was impossible to return from a jump.
This seems like a huge oversight, but I would argue that it really isn't... mainly because I don't want to be wrong but also because my intended use of branches were for control flow, not for instruction segmentation (like with a function).
DEL - A learning experience.
Some other problems.. or inconveniences I came across were due to the change in the NVM codebase. For starters, the Assembler was build to take in files and hand back byte code. For Solace this is wonderful, for something that generates NASM with the intention of then generating byte code (at least during early development) this was irritating. Having to read a file, write a file, read that file, and then write byte code that would then be loaded as a file into a VM is just awful. Who designed this thing? Oh..
There were a few other points of irritation that cropped up due to the code base growing that I am choosing to omit for the sake of brevity, but bottom line is that I discovered things that I want to button up before moving on.
I'm still working on the updates, but the first (and easiest) that I implemented was a special @jmp and @ret instruction that utilize a new jump stack in the NVM Execution Context.
Since the system information (function pointer, etc) is intentionally removed from the scope of the instruction set we previously had no means to return from a jump. With the new syntax we now leverage a jump stack that is local to the current function. I decided to keep jump stacks local to each function as a means to ensure local jumping isn't affected by other function units either by misuse, malicious intent, or from use of the pcall instruction. The important thing to note here is that the original variants of ret (shown above on the highlighted line) and jmp haven't changed to ensure that we can decided to utilize the jump stack or not.
I still have a few ideas for things I want to update before taking a look at going back at a high level language, but DEL (all three variants) was a great way to find the shortcomings of the current NVM. I'm definitely happy with it as a learning experience.
Once I make the updates to the NVM I am going to look at all the issues that I had with DEL and probably do a fresh implementation taking lessons learned and hopefully getting something solid working.