Working example showing Slots with Return stack and Parm stack

TimCTimC Posts: 73
edited 2020-04-19 - 15:47:34 in BASIC Stamp
Hi All,
Been looking through some old code to try to understand how to use Slots. To make them easier and provide more local variables to work with.
Most of the work was started years ago by Tracy Allen and others. I think I improved on a bit but it does take time to get used to.
For example in normal basic you would write a line: Gosub xPrintEEDATA(char) which runs a routine with one parameter and returns.
In my example it looks like this:

GoToX=xPrintEEDATA : ReturnPointer=ReturnPointer-1 : PUT ReturnPointer,xDispMsg1
ParmPointer=ParmPointer-1 : PUT ParmPointer,char : RUN GoToSlot : DispMsg1:

You could read the two lines as: ' 1:where are we going, 2:How are we getting back, 3:What are we passing 4:Our RETURN label
Without a macro language or a pre-processor it's never going to look neat and tidy.

The Quick flow of the program is this:

Slot 0 EnterMessage
passes to
Slot 1 GetMsgNum
returns to
Slot 0 ShowMessage
passes to
Slot 1 DispMsg
passes to
Slot 2 Print
returns to
Slot 1 DispMsg
returns to
Slot 0 Final

There is more than that but if you run the program is self documents where it is and where it's going and how it is going to return.
Uncomment the '#DEFINE DebugMode lines to see more detail or debug.

The Console output should look like this:

Starting MultiSlot program. It's got slots and stacks oh my!
---> Hi there! Type in your new message now (CR finishes) <---
Just Testing.
What message # to Display (0-5)?
0
Our Message # is:0
Just Testing.
Return Stack is empty. ReturnPointer is Zer0. No Problems
**** FINI ****



Hope someone finds this usefull
Regards
Tim C.

****** LOOK FOR NEWER VERSION BELOW, EASIER TO USE, FIXED SOME BUGS *******


Here is the Main Program
' =========================================================================
'
'   File...... MsgMaster.bsp
'   Purpose... Program for slot demo showing stacks.
'
'   Author.... TimC. (based on code from Chris Anderson, Tracy Allen and Parallax)
'   E-mail....
'   Started... 30 Mar 2020
'   Updated...
'   Tim Comments...  Working Great!!!
'
'   {$STAMP BS2p, MsgSlot1, MsgSlot2}
'   {$PBASIC 2.5}
'
' =========================================================================


' --- scratch pad assignments
ReturnPointerTop    CON 62   ' will be initial value of stack pointer
ParmPointerTop      CON 52   ' will be initial value of Parm pointer

' --- variable assignments
DataPointer    VAR     Word        ' Used as an address pointer for DATA statements
ReturnPointer  VAR     Byte        ' stack pointer--do not use for anything else!
ParmPointer    VAR     Byte        ' Parm pointer--do not use for anything else!
GoToX          VAR     Byte        ' for bank switching
GoToSlot       VAR     GoToX.nib1  ' target bank for RUN
GoToLabel      VAR     GoToX.nib0  ' target routine from BRANCH within bank

cat            VAR     GoToX       ' this byte can be reused within bank
char           VAR     Byte        ' for demo, preserved across banks
                                   ' an index for the message to retrieve
temp           VAR     Byte
temp2          VAR     Byte

' --- crossbank labels targets ----
xInit         CON $00     ' bank 0 targets
xEnterMessage CON $01
xShowMessage  CON $02
xFinal        CON $03
xDispMsg      CON $10     ' bank 1 targets
xDispMsg1     CON $11
xGetMsgNum    CON $12
xPrintEEDATA  CON $21     ' bank 2 targets

'#DEFINE DebugMode      'Follow along on the console
' ----------------------------------------------------
' --- top of program
' sxGoTo is $00 at reset ---> branch to top

BRANCH GoToLabel,[Init,EnterMessage,showmessage,Final]

Init:
   ReturnPointer=ReturnPointerTop       ' initialize the return stack
   ParmPointer=ParmPointerTop       ' initialize the Parm stack
   PAUSE 700
   DEBUG 0,"Starting MultiSlot program. It's got slots and stacks oh my!",CR

EnterMessage:            ' get a new message from the serial port
   #IF DebugMode #THEN DEBUG "inside slot 0 get message 0,1",CR #ENDIF
   DEBUG "---> Hi there! Type in your new message now (CR finishes) <---",CR
   STORE 2             ' All my data is in Slot 2
   DataPointer = 0     ' reset data pointer
   char = " "  ' else first character would be a 0 and erase screen
   DO
       WRITE DataPointer,char      ' Write to the EEPROM
       DEBUGIN STR char\1          ' Read in one character from the console
       DataPointer=DataPointer+1   ' Inc to next EEPROM Location
   LOOP UNTIL (char = CR)  ' enter key finishes input
   WRITE DataPointer,0     ' write end of line char

   char=0  'points to the message we just typed in
   'This is our first slot style GoTo.  Read the line as: Where we Going and how are we getting back
   GoToX=xGetMsgNum : ReturnPointer=ReturnPointer-1 : PUT ReturnPointer,xShowMessage
   #IF DebugMode #THEN DEBUG "GoTo is Now=", HEX2 GoToX, " ReturnPointer =", SDEC ReturnPointer - ReturnPointerTop, ".",CR #ENDIF
   RUN GoToSlot      ' GoTo get the new message


ShowMessage:
   #IF DebugMode #THEN DEBUG "inside slot 0 show message number 0,2",CR #ENDIF
   DEBUG "Our Message # is:",DEC char, CR  'This is all we do in ShowMessage,  Just show the Message #

   'Slot style GoToSUB with a parm
   GoToX=xDispMsg : ReturnPointer=ReturnPointer-1 : PUT ReturnPointer,xFinal    ' stack the return target in this bank
   ParmPointer=ParmPointer-1 : PUT ParmPointer,char       ' put the parameter char on the stack too
   RUN GoToSlot       ' GoTo show the message #X

Final:
   IF char=6 THEN EnterMessage
   #IF DebugMode #THEN DEBUG "Inside slot 0 Label text 0,3",CR #ENDIF
   '-----
   GET ReturnPointer,char
   ReturnPointer = ReturnPointer - ReturnPointerTop
   IF ReturnPointer = 0 AND char=0 THEN
       DEBUG "Return Stack is empty. ReturnPointer is Zer0. No Problems",CR
   ELSE
       DEBUG "Return Stack = ", DEC ReturnPointer, " Return Address = ", HEX char, ". There is a problem."
   ENDIF
   DEBUG "****  FINI  ****", CR
   PAUSE 700

END

Here is the First Slot Program:
' =========================================================================
'
'   File...... MsgSlot1.bsp
'   Purpose... Program for slot demo showing stacks.
'
'   Author.... TimC. (based on code from Chris Anderson, Tracy Allen and Parallax)
'   E-mail....
'   Started... 30 Mar 2020
'   Updated...
'   Tim Comments...   Working Great!!!
'
'   {$STAMP BS2p}
'   {$PBASIC 2.5}
'
' =========================================================================

' DATA section,     messages in eeprom.

ReturnPointerTop    CON 62   ' will be initial value of stack pointer
ParmPointerTop      CON 52   ' will be initial value of Parm pointer


' variable assignments
DataPointer    VAR     Word        ' Used as an address pointer for DATA statements (if more than 256 bytes of DATA)
ReturnPointer  VAR     Byte        ' stack pointer--do not use for anything else!
ParmPointer    VAR     Byte        ' Parm pointer--do not use for anything else!
GoToX          VAR     Byte        ' for bank switching
GoToSlot       VAR     GoToX.nib1  ' target bank for RUN
GoToLabel      VAR     GoToX.nib0  ' target routine from BRANCH within bank

cat            VAR     GoToX       ' this byte can be reused within bank
char           VAR     Byte        ' for demo, preserved across banks
                                   ' an index for the message to retrieve
temp           VAR     Byte
temp2          VAR     Byte

' --- crossbank labels targets ----
xInit         CON $00     ' bank 0 targets
xEnterMessage CON $01
xShowMessage  CON $02
xFinal        CON $03
xDispMsg      CON $10     ' bank 1 targets
xDispMsg1     CON $11
xGetMsgNum    CON $12
xPrintEEDATA  CON $21     ' bank 2 targets

'#DEFINE DebugMode      'Follow along on the console
' ----------------------------------------------------
' --- top of program
BRANCH GoToLabel,[dispmsg,dispmsg1,GetMsgNum]

' --- display the stored message
' Calls a second routine to actually print the message
DispMsg:

   #IF DebugMode #THEN DEBUG "inside Display message slot 1,0",CR #ENDIF
   GET ParmPointer,char : ParmPointer=ParmPointer+1      ' retrieve (pop) the parm we need
   #IF DebugMode #THEN DEBUG "** Display Message #", DEC1 char, ". **",CR #ENDIF

   ' as close to a gosub parm subroutine: Gosub xPrintEEDATA(char)  as we can make it all compressed in 2 lines
   ' What's in these 2 lines:            Don't forget the branch edit above.
   ' 1:where are we going, 2:How are we getting back, 3:What are we passing 4:Our RETURN label
   GoToX=xPrintEEDATA : ReturnPointer=ReturnPointer-1 : PUT ReturnPointer,xDispMsg1
   ParmPointer=ParmPointer-1 : PUT ParmPointer,char  :  RUN GoToSlot : DispMsg1:


   GET ReturnPointer,GoToX : ReturnPointer=ReturnPointer+1      ' retrieve (pop) the return vector
   #IF DebugMode #THEN DEBUG "GoTo is Now=", HEX2 GoToX, " ReturnPointer=", SDEC ReturnPointer-ReturnPointerTop, ".",CR #ENDIF
   RUN GoToSlot         ' do it, return to bank 0


' --- get a user message and store it in eeprom in this bank
GetMsgNum:
   #IF DebugMode #THEN DEBUG "inside get message slot 1,1",CR #ENDIF
   DEBUG "What message # to Display (0-5)?",CR
   DEBUGIN  DEC1 char : DEBUG CR

   GET ReturnPointer,GoToX : ReturnPointer=ReturnPointer+1   ' get (pop) the return vector
   #IF DebugMode #THEN DEBUG "GoTo is Now=", HEX2 GoToX, " ReturnPointer=", SDEC ReturnPointer-ReturnPointerTop, ".",CR #ENDIF
   RUN GoToSlot          ' GoTo back to bank 0 target

Second and Last Slot
' =========================================================================
'
'   File...... MsgSlot2.bsp
'   Purpose... Program for slot demo showing stacks.
'
'   Author.... TimC. (based on code from Chris Anderson, Tracy Allen and Parallax)
'   E-mail....
'   Started... 30 Mar 2020
'   Updated...
'   Tim Comments...   Working Great!!!
'
'   {$STAMP BS2p}
'   {$PBASIC 2.5}
'
' =========================================================================

' data, messages in eeprom
msg0 DATA @0,(75)      ' reserve space for user message
msg1 DATA "The quick brown fox jumpted over the lazy dog.",0
msg2 DATA "This is a switch because we are reading sentences right out of EPROM!",0
msg3 DATA "Not even Houdini could escape If he were alive.",0
msg4 DATA "Back to the future is a great movie one the whole family will enjoy you must see it soon.",0
msg5 DATA "Now is the Time for all good men to come to the aid of the Party!",0

ReturnPointerTop    CON 62   ' will be initial value of stack pointer
ParmPointerTop      CON 52   ' will be initial value of Parm pointer


' variable assignments
DataPointer    VAR     Word        ' Used as an address pointer for DATA statements
ReturnPointer  VAR     Byte        ' stack pointer--do not use for anything else!
ParmPointer    VAR     Byte        ' Parm pointer--do not use for anything else!
GoToX          VAR     Byte        ' for bank switching
GoToSlot       VAR     GoToX.nib1  ' target bank for RUN
GoToLabel      VAR     GoToX.nib0  ' target routine from BRANCH within bank

cat            VAR     GoToX       ' this byte can be reused within bank
char           VAR     Byte        ' for demo, preserved across banks
                                   ' an index for the message to retrieve
temp           VAR     Byte
temp2          VAR     Byte

' --- crossbank labels targets ----
xInit         CON $00     ' bank 0 targets
xEnterMessage CON $01
xShowMessage  CON $02
xFinal        CON $03
xDispMsg      CON $10     ' bank 1 targets
xDispMsg1     CON $11
xGetMsgNum    CON $12
xPrintEEDATA  CON $21     ' bank 2 targets

'#DEFINE DebugMode      'Follow along on the console
' ----------------------------------------------------
' --- top of program

BRANCH GoToLabel,[Init,printEEDATA]

Init:
   DEBUG "Stoping at Slot 2 Init.",CR

STOP

printEEDATA:
   GET ParmPointer,char : ParmPointer=ParmPointer+1     ' retrieve (pop) the Var we need
   #IF DebugMode #THEN DEBUG "We are in printEEData subroutine printing message# ", DEC1 char, ".",CR #ENDIF
   STORE 2     ' now look at data in the next slot
   LOOKUP char,[msg0,msg1,msg2,msg3,msg4,msg5],DataPointer   ' map to message start address
   char = " "  ' else first character would be a 0 and erase screen
   DO
       DEBUG char
       READ DataPointer,char
       DataPointer=DataPointer+1
   LOOP WHILE char
   DEBUG CR

   GET ReturnPointer,GoToX : ReturnPointer=ReturnPointer+1   ' get (pop) the return vector
   #IF DebugMode #THEN DEBUG CR,"GoTo is Now=", HEX2 GoToX, " ReturnPointer=", SDEC ReturnPointer - ReturnPointerTop, ".",CR #ENDIF
   RUN GoToSlot          ' GoTo back to target

Comments

  • I'm pleased to see this subject revived, techniques to tap the full power of the multislot BASIC Stamps. While the multiple slots do not run independently, I think they were a nice stepping stone on the Parallax journey to multiple cogs in the Propeller.

    I never got around to a full stack implementation, thinking it too cumbersome and unnecessary for my purposes. My stack was only one level deep, so its fixed return pointer location in scratchpad memory I named "back", not "stack". An early version of my implementation is still up on my website at http://emesystems.com/OLDSITE/BS2SX.htm#Crossbank.

    It is interesting to see how you have made a deeper stack. 10 deep, right? Why top at location 62?

    One comment. Since the Stamp assigns space in RAM in order of words, bytes, nibs, bits, I found it important to assign my cross-bank variable to the first word in all the linked programs. Same goes for shared flags and other template variables. Doing so allows new code to be added in any slot independently without worrying about bumping the position of the critical template variables.
    back     CON 126   ' single level stack, one fixed loc, the last in scratchpad ram
                        ' return target for crossbank calls in BS2pe, BS2P, BS2px
                        ' PUT the return target here, GET it from here to return or chain.
    
    wsx      VAR Word           ' always the first declared word in every bank
     lola      VAR wsx.BYTE0    ' alias inside wsx, this is the branch variable, has two components
      lolarun   VAR lola.NIB1   '  alias inside lola, which slot to run, RUN lolarun
      lolago    VAR lola.NIB0   '  alias inside lola, one of 16 possible routines to run in the target slot
     cat    VAR lola            ' sometimes I want to use the lola variable for something other than a cross-slot jump
     dog      VAR wsx.BYTE1     ' sometimes this passes data direct or as pointer, otherwise general purpose.
    
    
    The variable name lola is a tribute to the main character in the 1999 crime action drama, Run Lola Run.



  • I'm glad you took a look. I felt I never finished learning what the Stamp could do and there was still further to go. Your example was 99% there. Yea I just renamed it and gave it some room. I think you subtracted before and not after. The roadmap was there.

    62 was just random! and when you want to make changes with slots you have to do it in so many places. Just adding a slot label to jump to and answering a distracting phone call can create debugging problems later. 52 is the start of the data stack, thinking who would ever need a return stack that deep on a stamp.

    I agree with you on the assignment, but I have another plan, another slot version and perhaps you were the origin of this as well. When switching Slots save all the variables and restore when you land in the new slot. On a BS2p and up you can save all 26 word vars x4 with 24 scratchpad bytes to spare. So the stacks can not be so big. My goal is to combine the two Slot systems without making it an impossible monster to work with. Maybe the top 2-3 word vars would not have to be saved since they are the same anyway

    Here is the Part of the non-stack version showing the Save and restore vars at the bottom
    ' =========================================================================
    '
    '   File...... Master.bpx
    '   Purpose... Program for slot demo showing local and shared variables.
    '
    '   Author.... TimC. (based on code from Chris Anderson, Tracy Allen and Parallax)
    '   E-mail....
    '   Started... 30 Mar 2020
    '   Updated...
    '   Tim Comments...  Working
    '
    '   {$STAMP BS2p, slot1, slot2, slot3}
    '   {$PBASIC 2.5}
    '
    ' =========================================================================
    
    
    ' -----[ Program Description ]---------------------------------------------
    '
    'This Slot demo uses a BS2p to show how to use slots to add more code space and more ram space.
    'By using the scratchpad memory real local variables and shared variables can be created.
    'At a minimum each Slot uses about 256 bytes of program storage and 1 Word of Ram to operate this way
    'The ramaining space in each slot is available for your app. The BS2e and BS2sx have less
    'scratchpad memory so only 2 slots of local memory can be saved. Larger chips like the BS2p
    'are allowed 3 slots of local memory as seen in this example.
    
    'There is one example in Slot 1 Label 3 of calling another routine in slot 3 and then
    'returning to Slot 1 which then returns to the Master Slot.  Since there is no stack
    'Doing and more sub calling would get messy fast.
    
    'Now it will be easier to write you apps with 20 bytes of variables per slot and
    '1 3/4K per slot to work with.
    
    ' -----[ I/O Definitions ]-------------------------------------------------
    LCD       PIN         9      ' Simple serial LCD display
    E         PIN         0      ' connect LCD to OutL (Jumper JU3 must be installed)
    
    ' -----[ Constants ]-------------------------------------------------------
    '
    PgmSlotC      CON      0         ' Edit Here. With the Program Slot Number
    LastSlot      CON      0         ' Edit Here. Change to 1 if this is the last Slot Number
    
    #SELECT $STAMP
       #CASE BS2E, BS2SX
          BankVarsStart   CON      11    'Edit Here BS2SX and above Main=11, Slot1=37, Slot3=200 (error)
       #CASE BS2P, BS2PE, BS2PX
          BankVarsStart   CON      23    'Edit Here BS2p and above Main=23, Slot1=49, Slot2=75, Slot3=101, Slot4=200 (error)
       #CASE #ELSE
          #ERROR "Stamp Chip not defined for Slots programing."
    #ENDSELECT
    
    
    'Common LCD codes
    LCD_CMD        CON     254
    LCD_CLEAR      CON     1
    LCD_HOME       CON     2
    LCD_LINE1      CON     $80
    LCD_LINE2      CON     $C0
    LCD_DEG        CON     223      ' For Temperature Readings
    
    'Common Debug Window codes
    DBG_CLEAR      CON     0
    DBG_HOME       CON     1
    DBG_BELL       CON     7
    DBG_CURSORXY   CON     2
    DBG_TAB        CON     9
    DBG_CLEARDOWN  CON     12
    DBG_DEG        CON     223      ' For Temperature Readings
    
    'Handy On Off, Yes No Constants
    LedOn          CON      1      ' LED is driven High
    LedOff         CON      0      ' LED Pin is Low
    isOn           CON      1      ' LED is active high
    isOff          CON      0      ' LED Pin is low
    Ack            CON      0
    NoAck          CON      1
    No             CON      0
    Yes            CON      1
    
    ' Baud rate Constant
    #SELECT $STAMP
       #CASE BS2PX
          T38K4    CON      84   ' The smaller parallax display uses this
          N19K2    CON      188
          N9600    CON      396
       #CASE BS2P, BS2SX
          T115K2   CON      2   ' The 140x32 display uses this
          T38K4    CON      45   ' The smaller parallax display uses this
          N19K2    CON      110
          N9600    CON      240
       #CASE BS2, BS2E, BS2PE
          T38K4    CON      6
          N19K2    CON      32
          N9600    CON      84
       #CASE #ELSE
          #ERROR "Stamp Chip Baud speed not defined for this program."
    #ENDSELECT
    Inverted       CON      $4000            ' + This for the larger displays.  or True = 0
    Open           CON      $8000            ' or Driven = 0
    Even           CON      $2000            ' or No parity = 0
    ' Choose Speed Here for easier editing
    'Baud          CON      N9600            ' Use this for TTL
    Baud           CON      N9600 + Inverted   ' Use this when set for RS232
    'Baud          CON      N19K2 + Inverted   ' For Matrix orbital Graphics Display
    
    '
    ' -----[ Global Variables ]--------------------------------------------------
    '
    ' Global Variables are locations shared in the ScratchPad
    ' Max location is 10 for smaller chips and 22 for the larger
    ' use GET To move To a local variable and PUT after changing to save
    
    MainReturnLabelSCR      CON    0      ' Location in scratchPad Used by Task 0
    ReturnModuleSCR         CON    1
    ReturnLabelSCR          CON    2
    SlotLabelSCR            CON    3      ' Location in scratchPad Used by Slots to find which task
    ReturnCodeSCR           CON    4
    
    ' -----[ Local Variables ]--------------------------------------------------
    '
    '
    MainReturnLabel        VAR     Nib     ' used by task 0
    ReturnModule           VAR     Nib
    ReturnLabel            VAR     Nib
    SlotLabel              VAR     Nib     ' used by Slots to find which task
    ReturnCode             VAR     Nib     ' device check return ocde
    pgmSlot                VAR     Nib     ' what probram slot we are in loaded Via LoadSlotVars
    rwSlot                 VAR     Nib     ' what R/W slot the STORE command is pointing to for the Read/Write cmd
    
    zzzVar                 VAR     Byte    ' test var
    
    temp                   VAR     Byte    ' temporary working variables usefull in every slot
    looper                 VAR     Byte    ' temporary working variables usefull in every slot
    
    ' -----[ EEPROM Data ]-----------------------------------------------------
    '
    
    ' -----[ Initialization ]--------------------------------------------------
    '
       GOSUB RecallVars   'testing this mod
       PUT ReturnModuleSCR, 0             ' Main module is never a subroutine to a slot
       GET MainReturnLabelSCR, MainReturnLabel      ' casting a byte to a nib works without spilover :)
       GET SlotLabelSCR, SlotLabel         ' For Documentation to say where we have been
       BRANCH MainReturnLabel, [init, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14] 'Reentry index matches label #
    
    init:  'This only happends once! Last slot will INC MainReturnLabel
       DEBUG DBG_CLEAR,"Starting MultiSlot program.",CR, "Running Init: "
       GOTO InitAllSlots   'Yup a goto, not clean programming but necessary returns to _1: below
       DEBUG "At bottom of Init all slots in Main Module,  We should never be here!", CR
    
    ' -----[ Program Code ]----------------------------------------------------
    '
    _1:    ' Return here from Init of all slots.
    
    ' Slot 1, SlotLabel 1, Return here to _2: (MainReturnLabelSCR will match return label number)
       PUT SlotLabelSCR, 1 : PUT MainReturnLabelSCR, 2 : GOSUB SaveVars : RUN 1 : _2:
       DEBUG "*#1 RETURN FROM Run 1 SlotLabel ", DEC SlotLabel," *",CR
       'DEBUG "zzzVar=", SDEC zzzVar ,CR
    
    ' Slot 1, SlotLabel 1, Return here to _3: (MainReturnLabelSCR will match return label number)
       PUT SlotLabelSCR, 1 : PUT MainReturnLabelSCR, 3 : GOSUB SaveVars : RUN 1 : _3:
       DEBUG "*#2 RETURN FROM Run 1 SlotLabel ", DEC SlotLabel," *",CR
       'DEBUG "zzzVar=", SDEC zzzVar ,CR
    
    ' Slot 2, SlotLabel 1, Return here to _4: (MainReturnLabelSCR will match return label number)
       PUT SlotLabelSCR, 1 : PUT MainReturnLabelSCR, 4 : GOSUB SaveVars : RUN 2 : _4:
       DEBUG "*#3 RETURN FROM Run 2 SlotLabel ", DEC SlotLabel," *",CR
    
    ' Slot 2, SlotLabel 2, Return here to _5: (MainReturnLabelSCR will match return label number)
       PUT SlotLabelSCR, 2 : PUT MainReturnLabelSCR, 5 : GOSUB SaveVars : RUN 2 : _5:
       DEBUG "*#4 RETURN FROM Run 2 SlotLabel ", DEC SlotLabel," *",CR
    
    
    ' Slot 1, SlotLabel 2, Return here to _6: (MainReturnLabelSCR will match return label number)
       PUT SlotLabelSCR, 2 : PUT MainReturnLabelSCR, 6 : GOSUB SaveVars : RUN 1 : _6:
       DEBUG "*#5 RETURN FROM Run 1 SlotLabel ", DEC SlotLabel," *",CR
    
       LCDOUT E, LCD_Line1, ["SlotsAre"] ' easy banner
       LCDOUT E, LCD_Line2, [" Great! "]
    
    
    ' Slot 3, SlotLabel 1, Return here to _7: (MainReturnLabelSCR will match return label number)
       PUT SlotLabelSCR, 1 : PUT MainReturnLabelSCR, 7 : GOSUB SaveVars : RUN 3 : _7:
       DEBUG "*#6 RETURN FROM Run 3 SlotLabel ", DEC SlotLabel," *",CR
    
    
    ' Slot 1, SlotLabel 3, Return here to _8: (MainReturnLabelSCR will match return label number)
       PUT SlotLabelSCR, 3 : PUT MainReturnLabelSCR, 8 : GOSUB SaveVars : RUN 1 : _8:
       DEBUG "*#7 RETURN FROM Run 1 SlotLabel ", DEC SlotLabel," *",CR
    
    
    DEBUG "zzzVar=", SDEC zzzVar ,CR, "--Fini--", CR
    'PAUSE 60000
    'GOTO start
    
    ' Remove labels from this list after using in routines
    _9: _10: _11: _12: _13: _14:
    END
    ' -----[ Subroutine Section ]-----------------------------------------------------
    
    InitAllSlots:
       GOSUB LoadSlotVars    ' where are we variable
       zzzVar = pgmSlot+99   ' sample variable to confirm values are saved
       DEBUG DEC pgmSlot, ", "
       GOSUB SaveVars : RUN pgmSlot+1
       DEBUG "At bottom of Init all slots in Main Module,  We should never be here!", CR
    
    
    LoadSlotVars:
    ' We dont need to run this if we use a constant in each slot and dont need rwslot
    #SELECT $STAMP
       #CASE BS2
          pgmSlot = 0               ' everything in slot 0
          rwSlot = 0
          #ERROR "Stamp Chip not defined for Slots programing."
       #CASE BS2E, BS2SX
          GET 63, temp         ' read current slot
          rwSlot = temp.highnib         ' READ/WRITE slot is same
       #CASE BS2P, BS2PE, BS2PX
          GET 127, temp         ' get slot control byte
          rwSlot = temp.nib1     ' READ/WRITE in high nibble
          pgmSlot = temp.nib0   ' pgm slot in low nibble
    #ENDSELECT
    RETURN
    
    ' BS2sx  26x2 = 52 which leaves 12 scratchpad bytes 0-11
    ' BS2p and above 26x4 = 104 leaves 24 scratchpad bytes  0-23
    'Save All 26 Variables, can support main plus 3 slots
    SaveVars:
       'Increase the save pointer when we are in a higher bank
       'BankVarsStart = BankVarsStart + (26 * pgmSlot)
       'First save the one we will destroy
       PUT (BankVarsStart + 25), B0(25)
       FOR B25 = 0 TO 24
          PUT (BankVarsStart + B25), B0(B25)
       NEXT
    RETURN
    
    'Restore All 26 Variables, can support main plus 3 slots
    RecallVars:
       'Increase the restore pointer when we are in a higher bank
       'BankVarsStart = BankVarsStart + (26 * pgmSlot)
       'First save the one we will destroy
       GET (BankVarsStart + 25), B0(25)
       FOR B25 = 0 TO 24
          GET (BankVarsStart + B25), B0(B25)
       NEXT
    RETURN
    
  • Tracy AllenTracy Allen Posts: 6,512
    edited 2020-04-05 - 23:05:39
    Check out Jon William's article too,
    https://www.parallax.com/downloads/nuts-volts-stamp-applications-87-multi-bank-programming
    Toward the end of the article Jon suggests an approach where the main program would PUT all of the RAM variables into the scratchpad, saving them, like on a stack, for later retrieval with GETs. His example earlier in that article didn't do that; used a more ad-hoc approach instead.

    I found this example from Chris Anderson. I forget what sort of applications he had.
    https://forums.parallax.com/discussion/comment/504138

    My own apps needed something KISS, fast and systematic. I used my template for over 1000 programs, mostly variations on a Stamp2pe platform that had data storage with the option of eeprom, flash, or FTDI vDrive, also included a small amount of battery backed ram in the RTC chip to survive Stamp reset. There were options for lots of sensors and some effectors, communications by cable or wireless. Usually 6 to 8 slots in use, and almost all of the scratchpad memory was commandeered for data and computation buffers.

    About the number 62, I couldn't place why it looked so familiar. That is the top scratchpad memory address in the .bsx and .bse. The later Stamps, p, pe and px had more, top address 126.
  • Thanks for pointing these articles out!

    I have the merged code working.
    So now I get 20 free bytes of local storage per slot, 10 Bytes of stack space and 10 bytes of Scratch space left over.

    Going to any slot routine is easier:
    GoToX = x13SimpleMessage : PUT ReturnPointer,x05RecordData : GOTO SlotGo ' The simpleist example. No parms passed

    And with a parm:
    char = 55 'just something to pass
    PUT ParmPointer,char : ParmPointer=ParmPointer-1 ' put the parameter char on the stack then dec
    GoToX = x11ProcessData : PUT ReturnPointer,x02ReadTemperature : GOTO SlotGo ' Where to go, Where to return, GO!


    A slot subroutine starts with it's label and now only needs a goto
    _13SimpleMessage:
    DEBUG "In Slot ", DEC pgmSlot, " Label ", DEC GoToLabel, CR
    GOTO SlotReturn



    The idea that Jon William's had of saving RAM variables into the scratchpad is working as well:
    SaveVars:
    'Decrease the save pointer when we are in a higher bank
    'First save B5, the one we will destroy
    PUT BankVarsStart, B0(5)
    FOR B5 = 6 TO 25
    PUT (BankVarsStart + B5 - 5), B0(B5)
    NEXT
    'Finally restore B5
    GET BankVarsStart, B0(5)
    RETURN


    I still have a lot more work to do before it can be looked at so maybe a few more days.
    Regards
    Tim

  • Hi All
    Here is the new version I promised.
    Cleaned up some errors with edge cases saving and restoring local variables. The 2 stacks are better managed. I have left in place lots of debug statements just in case things go wrong. 2 bytes of global ram (Global X, GlobalY and GlobalbitQ) are available for your use as well. Added one more trick that might be useful:
    _2:    ' LCDInit
           ' This is a dual use Routine either called from another module OR as a standard subroutine
           ' within the module.  Remember to set Flag isSubroutine if within module.
       NAP 5 ' let LCD self-initialize
       DIRL = %11111110 ' setup pins for LCD
       LCDCMD E, %00110000 : PAUSE 5 ' 8-bit mode
       LCDCMD E, %00110000 : PAUSE 0
       LCDCMD E, %00110000 : PAUSE 0
       LCDCMD E, %00100000 : PAUSE 0 ' 4-bit mode
       LCDCMD E, %00101000 : PAUSE 0 ' multi-line mode
       LCDCMD E, %00001100 : PAUSE 0 ' no crsr, no blink
       LCDCMD E, %00000110 ' inc crsr, no disp shift
       LCDCMD E, LCD_CLEAR ' blank screen, home cursor
           'Remove next 3 lines after testing
       LCDOUT E, LCD_Line1, ["Hanging "] ' easy banner
       LCDOUT E, LCD_Line2, ["in Slot1"]
       PAUSE 9000   ' Need to pause to see LCD.
       IF isSubroutine THEN RETURN
       GOTO SlotReturn
    

    It's a subroutine that acts like any other subroutine when the isSubroutine bit set. When running from another slot it will return control back to the caller in any other slot. No Hard coding slot numbers. isSubroutine is cleared automatically when jumping to another slot.

    Have fun
    Tim

    The 3 files Below are: Master 0, Slot 1, Slot 2.
Sign In or Register to comment.