I am creating a simulator for an 8 bit processor in Java. It’s architecture is really simple, just an 4 byte register and 256 byte main memory. I have already implemented a “hardware” stack, so the processor supports PUSH, POP and GET. The stack is filled backward from the last memory cell, so “normal” memory usage should start at cell 0. You don’t have to reserve memory, the program is able to use the full 256 byte by default.
I am also creating a compiler for this processor which compiles from a simple language I invented. At the moment, every variable defined (I’m just supporting one 8-bit integer type) is assigned to one memory cell, starting with 0 and increasing. So I have a maximum of 256 (if the stack is empty) variables. Currently, I don’t want to change this.
My next goal is to add the ability to use parameterless procedures without return type. Variables declared in the function should be freed before returning automatically. So where should I store the variables? I would create a “software” stack between my variables at the beginning and the “hardware” stack at the end of the memory. I first had the idea to use the hardware stack for this, but I want to use it for the calls and returns of methods itself. Is there a better solution than creating a second “software” stack?
Each function, when you call it, should create an activation frame/call stack on the stack, which includes space for the return address, any parameters, and any local variables that are created within the function. At the very least, an activation frame in your situation should contain the return address of the calling code. The extra data that comes into play is based on whether your function accepts parameters, and/or if it creates any variables that are local to the function. Your stack might look like this:
Since you already are using main memory for the stack, this is where the activation frame will also live. I’m assuming that your processor has a stack pointer which points to the top of the stack?
You could create two stacks but then you would need to decide where to put this stack and how much memory it should consume. Do you want to use half of main memory for the hardware stack and the other half for the software stack? This also means that you are limiting the number of nested calls (or even recursive) calls that you can make. Instead there is another method you can use to conserve memory usage. The way to do this is to include the parameters to your function after the call to the function (your assembler will have to do this):
This means that you will have to do a little more work inside the function to load up the parameters. If you recall, the return address is the first value on the stack. You can load this value and then index off that value to load up your parameters. Once you’re done loading your parameters, you will need to adjust the value of your return address so that it points to code that starts after the parameters that you have defined.