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)
-
Could you add a link to that Wiki article? The only mention of "facilitated the creation of reentrant code" that a search turned up is from a Google book Early Home Computers (that doesn't seem to explain why it's useful).– TripeHoundCommented Apr 27, 2018 at 9:45
-
the book you mention looks like a paper form of the wikipedia entires– Tommylee2kCommented Apr 27, 2018 at 11:42
4 Answers
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:
Point 11:
Tell me again about the stack pointers: why two stack pointers?
Answer 11:
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
U
register as a stack mark pointer. All lower level modules will then be able to refer to the same data by identical offsets from theU
register.
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 X
and Y
, as well as for "blasting" memory copies.
It's exactly analogous to the x86 BP
/EBP
/RBP
registers supplementing the SP
/ESP
/RSP
registers.
-
4There was lore around Austin TX at the time that strongly suggested that Terry Ritter was a closet FORTHer, and he put the second stack pointer in there specifically to make it easy to implement FORTH on the 6809. I've long since lost touch with anyone who might have been able to confirm this. Commented Aug 20, 2018 at 23:22
-
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
pointer (for 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.
-
Are you maybe misinterpreting the U stack ptr of the 6809 for a frame pointer? It could certainly be used as one, just like any other register in that specific CPU. Its main feature, however, is that it can maintain a completely separate stack, independent from the system stack– tofroCommented Apr 27, 2018 at 19:23
-
@tofro: I've never actually used a 6809 C compiler, but given that other compilers for micros of that era practically always used frame pointers whenever it was remotely practical (e.g. IX for the Z80, or Y for the 68HC11, and of course BP for 8088 and A6 for the 68000) I'd be rather surprised if C compilers for the 6809 didn't use U as a frame pointer. Something like a Forth interpreter might benefit from having an additional stack, but would still be able to call C code. That code might need a little pointer magic to retrieve the saved U value and use it to find arguments pushed by...– supercatCommented Apr 27, 2018 at 19:42
-
...the Forth engine, but that shouldn't be too hard. The 6809 might not have hard-wired limitations on the usage of S and U that would make it necessary to use U as a frame pointer (as Intel did with BP on the 8088) but I think that was most likely at least one of the anticipated ways code could use it.– supercatCommented Apr 27, 2018 at 19:47
-
What I'm trying to say is that the indexed addressing mode (effective address formed from a register and an 8- or 16-bit offset) that is the main feature that makes up a frame pointer, is not a specific feature of the U stack pointer. Any other register in the 6809 can do that. The specific feature that distinguishes the U register from all the other registers except S is that it is a stack pointer.– tofroCommented Apr 27, 2018 at 20:19
-
@tofro: The ability to read and write a sequence of registers from/to sequential storage indexed by U would probably be more helpful in a Forth engine than in a compiler that uses U as a frame pointer, but I don't recall there being anything else magic about U vs X and Y.– supercatCommented Apr 27, 2018 at 21:53
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...
-
I'm going to edit this because it sort of... goes in a completely different direction to what it should. As it stands this isn't a great answer.– wizzwizz4 ♦Commented Sep 27, 2018 at 15:48