Stacking the stack

When going from emulation to simulation, new code will no longer use the emulated stack, but use its own stack somewhere in real memory. This retargeting project uses a transition phase in which we emulate most of the OS, and move functions one-by-one into simulation space and eventually realspace.

How many stacks will we need?

Taking a look at the ROM, we find special stack areas for every CPU mode. There is a supervior, a user, an iRQ stack, a FIQ stack, etc. . Whenever the CPU switches mode, the SP registers inside the CPU are switched as well, making this a native operation. But emulated stacks can only hold data from emulated space.

The return addresses to our functions in native space must be held in native space as well. A native return address may have a completely different format and may not fir at all into the emulated stack. Imagine running the emulated ARM 32 bit big endian CPU on a 64 bit little endian Intel CPU.

SO next to the emulated CPU mode stacks, we will need the same number of native stacks. The C language does not provide a standard way to switch stacks, but most operating systems do. On MSWindows, we can use FIbres, and Unix style operation systems commonly still offer ucontex, even though it is deprecated. If neither intrface is available, stacks can be created as a byproduct of threads. However, for NewtonOS, no two threads must run at the same time.

But wait, there is more...

We know that NewtonOS is a cooperative multitasking OS. There must be some task structure, and the CPU must switch between tasks at predefined points. This predefined point is at the end of every SWI instruction, where the OS decides if another task with higher priority is pending, and if so, switches to that task. The state of a task is savedin the TTask class which manages - you guesses it - its own stack.

The retargeted code needs to maintain a parallel native stack for every task stack in the OS. When the OS switches tasks, and the SP, the new code must switch stacks as well. Again, ucontext can do this work for us. As for TTask management, we will have to add code that manages the paralle stacks by creating and destroying ucontext structures as needed.

Well now, is that it?

I beleive so. It is important to keep in mind that NewtonOS does not allocate stacks at all. Instead it marks a certain area (usually 32k) in virtual space to trigger a data abort exception when the area is read or written. Only when the stack is actually used, RAM is mapped in 1k blocks as needed.

With our realstack, we will not have to worry about memory allocation. But until we have transcoded every possible path of execution for a task, we will need to keep an emulated stack around.