barrym95838 wrote:
In a different thread at comp.sys.arch, someone was mentioning that the 65816's direct page register was utilized as a stack frame pointer; it was pushed then pointed to the beginning of the local function's variables and parameters, allowing even more flexibility with addressing, since there are tons more direct-page addressing modes than stack-relative. Have you heard of this or seen any examples of this usage?
I'm aware of it and have actually played around with the concept. While the greater number of DP addressing modes seems to be an advantage, in practice I've not found that to be all that useful over and above the available stack relative modes. This is especially true when one considers code such as the following:
Code:
PER NPTR ;number of chars pointer
PER IPTR ;index pointer
PER S2PTR ;STRING2's pointer
PER S1PTR ;STRING1's pointer
JSR STRSUB ;execute function
The only practical way to access the function parameters that were pushed before
JSRing to
STRSUB is via stack pointer relative addressing. For that, the function needs to be able to work with local stack frame definitions:
Code:
strsub ;this line intentionally has no code
;
;—————————————————————————————————————————————————————————
;EPHEMERAL DEFINITIONS
;
.s_byte =1 ;size of a byte
.s_word =.s_byte*2 ;size of a word
;
;
; 65C816 register sizes...
;
.s_mpudb =.s_byte ;data bank
.s_mpudp =.s_word ;direct page
.s_mpupb =.s_byte ;program bank
.s_mpupc =.s_word ;program counter
.s_mpusp =.s_word ;stack pointer
.s_mpusr =.s_byte ;status
;
;
; status register bits...
;
.sr_car =@00000001 ;C — carry
.sr_bdm =@00001000 ;D — decimal
.sr_irq =@00000100 ;I — IRQ
.sr_neg =@10000000 ;N — result negative
.sr_ovl =@01000000 ;V — sign overflow
.sr_zer =@00000010 ;Z — result zero
.sr_amw =@00100000 ;m — accumulator/memory size
.sr_ixw =@00010000 ;x — index sizes
;
;
; stack definitions...
;
.sfbase .= 1 ;base stack index
.sfidx .= .sfbase ;workspace index
;
;—————————> workspace stack frame start <—————————
.nchar =.sfidx ;chars to shift
.sfidx .= .sfidx+.s_word
.s1len =.sfidx ;STRING1's length
.sfidx .= .sfidx+.s_word
;—————————> workspace stack frame end <—————————
;
.s_wsf =.sfidx-.sfbase ;stack workspace size
.sfbase .= .sfidx
;
;—————————> register stack frame start <—————————
.reg_c =.sfidx ;.C
.sfidx .= .sfidx+.s_word
.reg_x =.sfidx ;.X
.sfidx .= .sfidx+.s_word
.reg_y =.sfidx ;.Y
.sfidx .= .sfidx+.s_word
.reg_db =.sfidx ;DB
.sfidx .= .sfidx+.s_mpudb
.reg_sr =.sfidx ;SR
.sfidx .= .sfidx+.s_mpusr
.reg_pc =.sfidx ;PC
.sfidx .= .sfidx+.s_mpupc
;—————————> register stack frame end <—————————
;
.s_rsf =.sfidx-.sfbase ;register stack frame size
.sfbase .= .sfidx
;
;—————————> parameter stack frame start <—————————
.s1ptr =.sfidx ;STRING1's pointer
.sfidx .= .sfidx+.s_word
.s2ptr =.sfidx ;STRING2's pointer
.sfidx .= .sfidx+.s_word
.iptr =.sfidx ;index pointer
.sfidx .= .sfidx+.s_word
.nptr =.sfidx ;number of chars pointer
.sfidx .= .sfidx+.s_word
;—————————> parameter stack frame end <—————————
;
.s_psf =.sfidx-.sfbase ;parameter stack frame size
;
;
; error flags...
;
.er_bol =.sr_zer ;bank span
.er_idx =.sr_ovl ;index range
.er_stl =.sr_neg ;string length
.er_bits =.er_bol|.er_idx|.er_stl ;mask
;—————————————————————————————————————————————————————————
Within
STRSUB, any parameter is accessible by using its positional definition (e.g.,
.S2PTR) as an argument to a stack relative instruction, e.g.,
LDA (.S2PTR,S),Y. How would DP addressing help in this instance? I have indexed indirect addressing available in stack relative, as I do with DP.
An alternative to stack relative addressing would be to capture the entry stack pointer, which would be pointing to the stack location immediately below the return address, add
.S_WSF + .S_RSF bytes to it and copy that value to the MPU's
DP register—
DP would also have to be pushed so it can be restored upon exit. That would cause DP to point to the start of the function parameter stack frame. Subsequently, access to any parameter could be made via DP indexed indirect addressing, e.g.,
LDA $00,X. However, it really doesn't gain all that much over using stack pointer relative addressing, since the same addressing modes that would be most useful (and most used) are available either way. After all, the function parameters are pointers, so indirect addressing is required, requiring that pointer arithmetic be used if addressing through an ephemeral DP.
During development of my
65C816 string library, I found it easiest to define local workspace on the stack, as shown above. This approach eliminated the use of DP entirely and all that that entails.
Something else to consider is that unless the ephemeral DP location exactly falls on a page boundary (i.e.,
$xx00), the speed advantage of DP addressing is lost and indexed indirect addressing would actually be slower than stack pointer relative.