I am learning assembly language in my spare time to become a better developer.
I understand the difference between stack-based machines and register-based machines at a conceptual level, but I am wondering how stack-based machines are actually implemented. If a virtual machine, e.g. JVM or .NET, runs on a register-based architecture, e.g. x86 or x64, then it must use registers at the assembly level (as far as I am concerned). I am obviously missing something here. Therefore I am unsure of the distinction at assembly language.
I have read articles on here e.g. Stack-based machine depends on a register-based machine? and also on Wikipedia, but I don't believe they answer my question directly.
Stack based machines are rarely implemented in hardware - I've only every heard of one such implementation, and have never had the chance to work on one.
In reality Stack machines are implemented on real register based processors by native interpreters. In essence, the theoretical Stack machine is emulated by the real Register based machine.
So to answer your question: although the machine code of the stack machine doesn't have registers, the native interpreter that executes these instructions does have registers and will be using them.
Q: So why the indirection? A: Portability - the instruction set of the stack machine can be emulated on any number of different register based machines. This means that the same JVM application can be run on any machine that has an interpreter, hence the old Java slogan "Write once, Run anywhere"
For reference, Burroughs B5000 and Inmos Transputer were stack machines. DEC PDP11 had such flexible addressing modes that it could be used as a stack machine. I think Niklaus Wirth's Lilith may have been a stack machine (more than 20 years ago, my mind is slipping :-)
They really did not have any register name/number in instructions to find operands, because they were on the stack.
Instructions could load immediate (constant) values onto the stack, or load/store to memory.
So their was no add.w r0, r1, r5
or add.w eax, [#fe34]
. There was add.w
.
So an example (not at all accurate, it was more complex) of an assembler sequence might be
loadstack 0xfe34 -- got fe34 onto stack
loadstackindirect -- use address on the stack, to load the value at that address
add.w -- assumes we already have the other operand on the stack
-- result back onto the stack
To calculate and load a value in an array, the stack might be used because there might be no indexed addressing mode.
So instructions were small, and lots of work was done implicitly with the stack and stack pointer. IIRC Transputers actually had a stack of only three values, and the compiler (or developers) had to ensure that was maintained.
XMOS now sell a modern 'equivalent', and employ some of the same people.
It is over 20 years since I wrote Transputer code, so sorry for being a bit vague.
The UCSD Pascal system used a software defined virtual machine, which was a stack machine. The idea was to make something which was portable to new computers, but also easy to write, easy to compile to, and reasonable performance. It's virtual machine was defined in its own Pascal dialect. When it was ported to real computers, registers would be used to hold the stack pointer, and likely some ingenuity in how the top of stack was handled (by registers), in order to get reasonable performance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With