barrym95838 wrote:
BruceRMcF wrote:
I push the bottom of the hardware stack to $01AF, and use $01B0-$01B7 for the four system variables (>IN, LATEST, HERE and STATE), and the $01B8-$01FF is the space for the X or Y indexed return stack.
Interesting twist. I am far from an expert, but all of the 65xx implementations with which I'm familiar just use the S register for the Forth return stack pointer, influenced by the paucity of registers and the favorable trade-offs that come from fusing the hardware and interpreter return stacks. Do you have some sample primitives you can share (possibly in a fresh thread) which utilize RSP?
It doesn't need to be a separate thread, because I did it to address part of the SectorForth/MilliForth style model, where RP@ has to return an address and act "as if" it is running the return stack with a pointer. You can have all of the R-stack users dutifully update the return stack, but assembly language routines and calling routines from interrupt vectors are not necessarily going to respect that.
So I wouldn't do it in general, it's for cramming the square peg of 65C02 X/Y indexed stacked into the round hole of this Forth model which, AFAIU, wants something that works like an actual top-of-stack pointer. For the general CamelForth model which has RP@ RP! SP@ SP!, I just use the X index into my data stack and the S index into my Return stack and the SP@ and RP@ just clear the high byte while the SP! and RP! ignore it.
It's early days yet, and if I think the SectorForth approach is too clunky, I could well return to the CamelForth style words, though at the cost of having to rewrite the SectorForth HelloWorld script. So at the very least, I want to implement the SectorForth model -- with some extensions, but not with modifications ... so if I run the SectorForth script and it doesn't work, I know it is the s4th.prg binary that is wrong rather than the script. Whether or not I decide to "de-clunkify it" would then be a matter of available time and interest.
It's at home but this is office hour, and I think I can recall the DOLIST.
The compiled forth word is:
Code:
JSR DOLIST
[address of 1st word]
[address of 2nd word]
...
[address of EXIT]
So 1 minus the start of the list is on the top of the system stack.
Code:
DOLIST:
LDX RP ; the RP@ side requires it be maintained "as if" it is a return stack pointer
DEX
DEX
STX RP
CLC
LDA #2
ADC IP ; the model requires that the EXIT address on R: points to a list entry to start executing
STA RL,X
LDA #0
ADC IP+1
STA RH,X
PLA
PLX
INC
BNE +
INX
+ STA IP
STX IP+1
JMP DONEXT
So the JSR DOLIST that extracts the address of the list of (direct threaded) executable words can push onto the R stack without interfering with the list subroutine return address on the stack.
The other aspect is when SectorForth is used to intermix conventional assembly language and forth ... since you can define a wordset to move HERE to where you want an assembly language routine, and then restore it when you are done:
$0400 STARTCODE
CODE< $DE , $AD , $BE , $ EF , >CODE
CODEHERE @ .
$0404 OK
... to put arbitrary data into an arbitrary location, so a script can inject a set of assembly language routines.
So this simplifies writing a helper routine for assembler language routines calling compiled Forth words. The helper routine injects the address of the return routine onto the Return stack, then returns to the caller, which can then call the Forth word as a subroutine ... and the helper can just return to the caller because the R stack and the hardware stack are separated:
Code:
; User
; ...
JSR CALL4TH
JSR SETUP_IO
; ...
CALL4TH: ; untested
LDX RP
DEX
DEX
STX RP
LDA #<(CALL4TH1)
STA RL,X
LDA #>(CALL4TH1)
STA RH,X
RST ; return from JSR CALL4TH
CALL4TH1: ; EXIT has called here
LDX RP
INX
INX
STX RP
RTS ; return from JSR SETUP_IO