Hopefully this won't be repeating things. If it is, maybe a different way of putting it will give further ideas and clarity.
MichaelM wrote:
It appears to me that re-entrant functions must use storage on the stack rather than in zero page.
As you know, Forth uses a virtual stack in ZP for data, to avoid certain overhead and problems that crop up with trying to mix data and return addresses on the hardware stack. It works super well; and being a stack, it automatically accommodates re-entrancy. Although it takes up precious ZP space, it also reduces the need to have so many ZP variables; so I think it pays for itself in that respect.
Quote:
With the paucity of registers in the 6502/65C02 architecture, I think that the stack pointer will not remain fixed as temporary storage is pushed and popped from the stack. If the routine uses zero page or other memory for this type of temporary storage, then implementing re-entrant functions would become more difficult.
A virtual
stack in ZP actually makes it easier. But if you want it all on the hardware stack, what you do, even on the '02, is to adjust S to accommodate all the needed temporary storage, either by pushing dummy bytes (like with the appropriate number of PHA's before the TSX) which will later get used for temporary variables, or if you need a lot of them, it may be more efficient to do TSX, TXA, SEC, SBC#nn, TAX, TXS. (BDD showed an example of the latter for the '816 further up.) Then the new S is already in X, ready for indexing. (If you want to index with Y instead, add a TAY.) Now interrupts won't interfere with the locals on the stack. A bonus is that subsequent changes in S will not affect the operand used in instructions that index into the stack, because the changes in S are not reflected in X unless you do TSX again. For example suppose you need to preserve both X and A as you get S into Y on the '02:
Code:
PHA
PHX
TSX
TXA
TAY
PLX
PLA
LDA 105,Y ; Access a particular parameter
<do_stuff>
<push n bytes onto the stack>
LDA 105,Y ; Access the same parameter again as 3 lines up.
<do_stuff>
<pull n bytes back off the stack>
STA 105,Y ; Access that same parameter again.
If you only do TSX first instead of the first seven lines above, the 105,Y becomes 103,X. The stack depth and the position of the variable are the same in both cases, but the index value difference will need to be compensated in the operand. There can be subsequent pushes and pulls in the subroutine without affecting that 105,Y to access the particular parameter in the stack. This BTW is on the '02, not even the '816 which has TSC and TXY instructions.
Quote:
Thus, I am interested in reactions to my proposed base pointer relative addressing mode where a single byte following the instruction is specified to be a signed offset. Positive offsets would be used to reference the functions parameters on the stack, and negative offsets would reference local variables.
I can see where that may have value, as long as the BP is not tied directly to S, being loaded with perhaps a TSB instruction. (Woops-- that mnemonic is taken for "Test and Set Bits." You'll need another one.) Without re-reading again, it seems related to my desire to have a second hardware stack, where instructions for stack operations would have a bit telling which hardware stack to operate on. The two hardware stack areas could overlap, as long as certain rules are observed. The only change needed to do the signed stack offset on the 816's stack-relative addressing modes is to have a 16-bit "Sr" operand so it wraps (which it would do since the stack is always in bank 0), but there would again be the problem with interrupts or even subroutines stepping on the local variables. Another way to implement the signed-offset idea with what's already in place is to do it in macros that would adjust the numbers and assemble all positive ones and do the same job.
Quote:
Use of the stack pointer to dynamically create temporary storage would not have an effect on the pre-calculated offsets to the parameters or the local variables.
It doesn't anyway, as shown (possibly poorly) above.
Bregalad wrote:
Note that it might be different on the '816. Personally I'm no '816 expert, but I'd say you should put good use the relocatable zero-page, and make it point to your stack-frame, keeping X register indexing the top of the stack frame. Whenever you push N bytes, you decrease the X register by N, and if X underflows you should make the ZP pointer point to a new page and adjust X accordingly.
Note that the 816's direct-page register is 16-bit, and the direct page does not have to start at a page boundary. If you want to increment or decrement it by some arbitrary number like 4 or 7, you can.