I'm an assembly newbie, but I'm curious about why the designers of the 6809 choose to use 2 stack in the microprocessor architecture. What is their original intent? The 6809 Wikipedia page says "a user stack pointer (U) facilitated the creation of reentrant code.". Is that the intention of the design? if so can someone point me to an example of this? I read about the "stack blasting" thing, but this is a consequence of the design, not an intent.(Motorola 6809 Stack Blasting)
Well, the intent behind the two-stack design we can only guess today. It was definitely not the same as the intent behind the later development of User and Supervisor Stack in the Motorola 68000. The 6809 has nothing comparable to a supervisor mode.
Also the implementation is different: The later 68000 uses both stacks as system stacks (i.e. stores return addresses for JSR/BSR/TRAP and interrupts there) in user and supervisor mode, while the 6809 leaves the "U" stack to exclusive use by the application (i.e. the only instructions that put something there are the equivalents of "push" and "pop" and explicit data move instructions, subroutine calls and software interrupts won't affect the "U" stack)
Most probably, the designers just thought a stack is a convenient data structure on assembler level (which is definitely true) and a separate stack, undisturbed by subroutine calls and (software) traps would be even more convenient (which is true in my opinion as well, working around data pushed by subroutine calls in assembler can be quite a nuisance and a source of subtle bugs at times). The various indexed and offset addressing modes of the 6809 make the U stack pointer even more convenient to use. Implementing a stack-based language like a Forth interpreter on the 6809 must have been pure joy over other 8-bit-CPUs.
Apropos Forth: Forth really became en vogue end of the 70ies, beginning of the eighties - Maybe it was intended to support that (and other stack-based languages) language in a more ideal form than other CPUs. But that's pure guessing and maybe a coincidence.
From Byte Magazine article on 6809, by Terry Ritter:
Tell me again about the stack pointers: why two stack pointers?
Good Point. The original reason for adding the user stack pointer was to facilitate the creation of a data stack in memory that is separate from the program stack. This avoids one of the serious problems of using a second generation processor in a modular programming environment -- that of returning parameters to a calling routine. We want to pass parameters in a position independent manner, of course, but the return from subroutine (
RTS) instruction uses the top element of the stack as a return address, and this address is placed on the stack before the subroutine is entered. On the 6800 there will be a lot of stack rearrangement going on to get around this problem. The user stack pointer was created as a new stack unencumbered with return addresses (or interrupt state information) to allow data to be passed between routines of different levels in a reasonable manner. And since the new stack works exactly like the old, there is a relatively small silicon cost involved.
We do suspect, however, that many programmers will elect to accept the overhead involved with passing parameters on the hardware stack (note that the overhead problem is greatly reduced with the 6809). These programmers will be concerned with the access of parameters placed on the stack by higher level routines. Notice that, as more elements are added to the stack, these same parameters are referred to by varying offsets with respect to the stack pointer itself: this is bad, since it becomes difficult to analyze exactly which value is being accessed by any given subroutine. Thus many programmers will use the
Uregister as a stack mark pointer. All lower level modules will then be able to refer to the same data by identical offsets from the
This was back in the days of Pascal, where you could have nested functions which can access their parents' local variables. The compiler does not know what call context the function is currently in, and thus how far up the stack its parents' local variables are; it must have a pointer to the previous stack frame, or some other way of accessing its parent's local variables. See Nested Function and Non-local Variable.
For languages like C which don't allow nested functions, the user stack is less necessary, because the compiler can easily track the hardware stack pointer as it changes, and generate the correct offsets, thus allowing the
U register to be used as another index register like
Y, as well as for "blasting" memory copies.
It's exactly analogous to the x86
RBP registers supplementing the
Many high-level language compilers store local variables (or, as C calls them, "objects with automatic duration"), on the stack. They also store temporary computation results and arguments of functions they're about to call there as well.
Suppose a compiler sees
foo(x,y); when two-byte value x is sitting at a spot
4 bytes above the stack pointer and y is at a spot 12 bytes above, and a
language calling convention pushes arguments right to left. In that case,
a compiler could start by pushing a word that's 12 bytes above the stack
y), but it would then have to push a word that's 6 bytes above
the stack pointer (rather than 4) for
x, since the act of pushing
y would have just changed the stack pointer.
Adding a second value that can be used as a "frame pointer" makes things a bit simpler. At the start of a function, a compiler pushes the frame pointer on the stack, copies the stack pointer to it, and then moves the stack pointer to reserve space for any local variables the function will need. At the end of the function, the stack pointer can be reloaded with the frame pointer and the old frame pointer can be popped off the stack. Since the frame pointer won't move during the execution of the function, all of its parameters and local variables can be accessed at a fixed displacement from the frame pointer.
Some processors (e.g. the 8088) have frame-pointer-relative addressing but not stack-pointer relative. On those which have both, I don't know how much real benefit the frame pointer would offer a compiler, versus having a compiler keep track of how much it has pushed on the stack since a function began. I suspect the biggest benefit of a frame pointer comes when a program mixes a high-level language with in-line assembly or machine code [as was much more common in the 6809's heyday than it is now]. If a compiler lays things out consistently in memory, a programmer may be able to write machine code to access a particular parameter or variable and a frame-relative address without having to know how many additional bytes of information might be sitting on the stack. Even if a compiler would know such things, it's much easier for a compiler to process a directive:
__raw_code(0x43, 0x68, 0x92, 0x95, ...etc...);
when the exact bytes to generate are all simple constants, than it would be to add ways of making the bytes vary depending upon other factors.
For a compiler, or in Assembler, a procedure usually needs the stack.
But also the environment it is in, system or application calling it. So the are always (at least) 2 states in action: The caller, and the present code being called.
For easy and safe access, both should be independent (separated). What the Motorola designers had in mind, expressed but not limited to that, was the relation between system and application.
In the case of FORTH, while it is both the system and the application, the reason is another: It's personality reflects those 2 states as 2 different usages, therefore demanding 2 stacks for each use: Ret-Stk (system) and Dat-Stk (application).
In FORTH, it's a parallel of what is really at work.
Not mattering if an OS, an application, Forth or ASM, the use of stacks suggest at least 2. Because the first CPUs used one, inertia kept it, though inadequate.
In such inadequacy, a single stack forces to tricks to deal with the limitations habit imposed...