Beginner with BS2SX
Archiver
Posts: 46,084
I wrote a program for a BS2, but there is not enough space. So I got
a BS2SX because it has more EEPROM space. When I tried to download
the program to the BS2SX (using the same carrier board), the editor
still said, "EEPROM Full." I changed the setting in the Preferences,
Editor Operation menu to BS2SX, and it still won't work. Does anyone
know how to fix this problem? Thanks.
a BS2SX because it has more EEPROM space. When I tried to download
the program to the BS2SX (using the same carrier board), the editor
still said, "EEPROM Full." I changed the setting in the Preferences,
Editor Operation menu to BS2SX, and it still won't work. Does anyone
know how to fix this problem? Thanks.
Comments
You have 16K bytes (program and data in 8 x 2K program spaces) on the SX.
You need to organize your program to fit within 2k pages.
Another possiblity is your variable storage space exceeded the maximum
available.
Original Message
From: sethjaredjeromiejohnson [noparse]/noparse]mailto:[url=http://forums.parallaxinc.com/group/basicstamps/post?postID=gm8PgM5dlY85Xv6YoASg-8qy1JRlkMkjVFJbE8JW2osN9pW-oC4BKFPiw_8PgASmBFGulOi5mKernrO99mc]sjohns10@h...[/url
Sent: Tuesday, December 25, 2001 8:29 AM
To: basicstamps@yahoogroups.com
Subject: [noparse][[/noparse]basicstamps] Beginner with BS2SX
I wrote a program for a BS2, but there is not enough space. So I got
a BS2SX because it has more EEPROM space. When I tried to download
the program to the BS2SX (using the same carrier board), the editor
still said, "EEPROM Full." I changed the setting in the Preferences,
Editor Operation menu to BS2SX, and it still won't work. Does anyone
know how to fix this problem? Thanks.
To UNSUBSCRIBE, just send mail to:
basicstamps-unsubscribe@yahoogroups.com
from the same email address that you subscribed. Text in the Subject and
Body of the message will be ignored.
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
sjohns10@h... writes:
> I wrote a program for a BS2, but there is not enough space. So I got
> a BS2SX because it has more EEPROM space. When I tried to download
> the program to the BS2SX (using the same carrier board), the editor
> still said, "EEPROM Full." I changed the setting in the Preferences,
> Editor Operation menu to BS2SX, and it still won't work. Does anyone
> know how to fix this problem? Thanks.
The BS2sx has eight, 2 kByte program slots. What you need to do is pull
portions out of your main program and put them into another program slot. If
your variable definitions are the same in both programs, they will be
preserved when you use the RUN command. Otherwise, you can use PUT to store
values in the SPRAM and GET to retrieve them when you've changed program
slots.
-- Jon Williams
-- Parallax
[noparse][[/noparse]Non-text portions of this message have been removed]
compile it, it comes up with an "Undefined Label" error in the
second slot where there is a gosub instruction. The gosub
instruction in the second slot tells the program to go back to the
first slot, then return. How would I fix this? Thanks.
compile it, it comes up with an "Undefined Label" error in the
second slot where there is a gosub instruction. The gosub
instruction in the second slot tells the program to go back to the
first slot, then return. How would I fix this? Thanks.
> Hi,
>
> The easiest way is to copy the subroutine in the first slot
> to the second slot. Place a remark by both the subroutines
> that there is a copy of this routine in an other slot.
> This will remind you later to change both subroutines
> iIf they need to be modified.
> There are pitfalls though, for example, variables used in the
> subroutine must also be available in the slot you
> copy the routine to.
>
> Regards peter
Almost every section of my program is dependant on another section in
the program. For instance, I have the program going back to the
beginning often to check the state of the power button. There are
several instances similar to that one, and if I copied them all to
where they were needed (in the same slot), then I'm back to start
because the program is too large to fit in one slot. Is there some
way to get the program to recognize that the other parts of the
program are in different slots? Thank you very much for your help.
to switch between program slots, and then it always begins at the first
line.
Original Message
> I got the program into three seperate slots, but whenever I try to
> compile it, it comes up with an "Undefined Label" error in the
> second slot where there is a gosub instruction. The gosub
> instruction in the second slot tells the program to go back to the
> first slot, then return. How would I fix this? Thanks.
Al Williams
AWC
* Floating point math for the Stamp, PIC, SX, or any microcontroller
http://www.al-williams.com/awce/pak1.htm
>
Original Message
> From: peter verkaik [noparse]/noparse]mailto:[url=http://forums.parallaxinc.com/group/basicstamps/post?postID=xSQvJ3Y5HOmzI7tnBHBulBsuxwW-RB6dSPiv6mQuIyYNShOJf0gmYDt_Z72OPgPeMkuIGSFqYWE5ujU0ttAlO2t5shc]peterverkaik@b...[/url
> Sent: Wednesday, December 26, 2001 7:33 AM
> To: basicstamps@yahoogroups.com
> Subject: RE: [noparse][[/noparse]basicstamps] Re: Beginner with BS2SX
>
>
> Hi,
>
> I noticed Al's faq page has a disturbed pagelayout concerning
> the example. So here is the original from my archive.
>
> Multilevel stack design and usage for Basic Stamp BS2SX, BS2E
> and BS2P using the scratchpad.
>
> Many thanks to Tracy Allen for all his input and positive suggestions.
>
> This design uses the available scratchpad to hold a
> multilevel stack that can be used to call functions (with
> parameters if required) in other programs and to create named
> local variables for use within gosubs. It shows a general
> program setup that can be used as a template.
>
> Features:
> up to 32 entry/return points per program
> nested program calls
> parameter passing via stack
> named local variables for subroutines
>
> Limitations:
> program calls can only execute at main level code outside
> for-next loops. names for local variables must be unique as
> they are seen as aliases no stackpointer check
>
> 'scratchpad usage (TOS stands for Top Of Stack)
> 'TOS-0 = entryID/runID 1
> 'TOS-1 = entryID/runID 2
> 'TOS-2 = entryID/runID 3
> 'etc.
>
> 'program 0
>
> TopOfStack1 con 62 'top of stack
> fot BS2SX, BS2E
> TopOfStack2 con 126 'top of stack for BS2P
>
> TOS con TopOfStack2 'pick the right
> top of stack
> STACKSIZE con 30 'define stacksize
> BOS con TOS-STACKSIZE 'bottom of stack
>
> EN1P0 con (1<<3)+0 'entry01 in program 0
> EN2P0 con (2<<3)+0 'entry02 in program 0
> EN3P0 con (3<<3)+0 'entry03 in program 0
> EN4P0 con (4<<3)+0 'entry04 in program 0
> EN5P0 con (5<<3)+0 'entry05 in program 0
> EN4P3 con (4<<3)+4 'entry 4 in program 3
> EN14P4 con (14<<3)+4 'entry
> 14 in program 4
> EN6P7 con (6<<3)+7 'entry 6 in program 7
>
> 'system variables
> TargetID var byte 'entry index
> and program to run
> runID var TargetID.lownib 'entry index
> b0, program to run
> entryID var TargetID.highnib 'entry
> index b4-b1
> StackPointer var byte 'stackpointer
>
> 'application variables
> x var byte
> y var byte
> z var byte
>
> ProgramID con 0
>
> begin0:
> branch
> (TargetID>>3),[noparse][[/noparse]main0,entry01,netry02,...,entry1F]'first entry
> for direct run
>
> main0:
> StackPointer=TOS
> 'insert init code here
>
> main0loop:
> 'insert mainloop code here
>
> z=0
> call_loop_example:
> '1st program call example, stackpointer check could be
> placed here
> put StackPointer,EN1P0
> StackPointer=StackPointer-1
> TargetID=EN14P4
> run TargetID 'execute entry 14 in program 4, then
> return to entry01
> entry01:
> 'return point for call
> z=z+1
> if z<10 then call_loop_example
>
> 'insert mainloop code here
>
> '2nd call example, stackpointer check could be placed here
> put StackPointer,x 'first put parameters onto stack
> put StackPointer-1,y
> put StackPointer-2,EN2P0 'then put return point
> StackPointer=StackPointer-3
> TargetID=EN6P7
> run TargetID 'execute entry 6 in program 7, then
> return to entry01
> entry02:
> 'return point for call
> get StackPointer+2,x 'retrieve results
> get StackPointer+1,y
> StackPointer=StackPointer+2 'remove results from stack
>
> 'insert mainloop code here
>
> 'direct run example
> TargetID=$03
> run TargetID 'run program 3 directly (no call but jump)
>
> 'insert mainloop code here
>
> main0end:
> goto main0loop
>
> 'function entries for program calls
>
> entry03:
> gosub function_03
> put StackPointer,EN4P3
> StackPointer=StackPointer-1
> TargetID=EN4P0
> run TargetID 'execute entry 14 in program 4, then
> return to entry01
> entry04:
> 'return point for call
> goto ReturnFromProgramCall_0
>
> entry05:
> get StackPointer+3,x 'get parameters from stack
> get StackPointer+2,y
> gosub function_04 'perform some math on x and y,
> results in x and y
> put StackPointer+3,x 'place results on stack
> put StackPointer+2,y
> goto ReturnFromProgramCall_0
>
> ReturnFromProgramCall_0:
> StackPointer=StackPointer+1 'adjust stack here
> get StackPointer,TargetID 'get TargetID
> run TargetID 'return to parent program
>
> 'subroutines
>
> function_03:
> gosub alloc4 'allocate 4 bytes for local variables
> x1 var W12 'local word variable
> y1 var B23 'local byte variable
> 'code here
> goto free4 'free local variables and return from gosub
>
> function_04:
> 'code here
> return
>
> alloc8: put StackPointer,B18 'space is
> allocated from B25 upwards
> put StackPointer-1,B19
> put StackPointer-2,B20
> put StackPointer-3,B21
> StackPointer=StackPointer-4
> alloc4: put StackPointer,B22
> put StackPointer-1,B23
> put StackPointer-2,B24
> put StackPointer-3,B25
> StackPointer=StackPointer-4
> return
>
> free8: get StackPointer+8,B18
> get StackPointer+7,B19
> get StackPointer+6,B20
> get StackPointer+5,B21
> get StackPointer+4,B22
> get StackPointer+3,B23
> get StackPointer+2,B24
> get StackPointer+1,B25
> StackPointer=StackPointer+8
> return
>
> free4: get StackPointer+4,B22
> get StackPointer+3,B23
> get StackPointer+2,B24
> get StackPointer+1,B25
> StackPointer=StackPointer+4
> return
>
> end
>
>
> When we use the stack for program calls, each program should
> start with a branch statement as shown in program 0. The
> first entry for all these branch statements must point to the
> program's main entry. The reamaining entries (max. 31) point
> to either function entries or call return points. The global
> variable TargetID holds the index for the branch statements
> in its upper five bits. The lower three bits of TargetID
> determine the program to run.
>
> At powerup/rest all variables are cleared to 0, so TargetID
> holds 0, and therefore the brach statement takes us to main0.
> At that point the stackpointer is initialized. This stack
> initialization is only required at this point in program 0.
> The other programs do not require such an initialization.
>
> Now take a look at the call_loop_example. It demonstrates how
> to do program calls inside a loop. Remember you can not use
> for-next loops, hence the if-then construction that simulates
> a for-next loop without using the Stamp internal stack which
> is reset when switching programs. To do a program call, you
> must first save the return point onto the stack. The statements
>
> put StackPointer,EN1P0
> StackPointer=StackPointer-1
>
> handle this. The constant EN1P0 is calculated at compile-time
> and is determined by the index of entry01 in the branch
> statement and the program the call is made from. The statement
>
> TargetID=EN14P4
>
> is used to set the target, i.e the function entry point
> inside the program we want to call. The statement
>
> run TargetID 'execute entry 14 in program 4, then
> return to entry01
>
> will then switch to the program determined by the three lower
> bits of TargetID. The first line of code for this new program
> holds a simular branch statement as shown above. Given
> TargetID, the branch statement takes us to the desired
> function entry, which is simular to entry03 in the above
> example. The function itself is programmed as a subroutine
> that is called from this entry. Upon return from this
> subroutine the function first performs another program call
> (we now have two nested program calls) and then exits by
> going to the ReturnFromProgrammCall entry. The number of
> nested program calls allowed is determined by the available
> stackspace. The statements
>
> StackPointer=StackPointer+1 'adjust stack here
> get StackPointer,TargetID 'get TargetID
>
> get the stored return point from the stack. The statement
>
> run TargetID 'return to parent program
>
> then switches back to the parent program that made the call,
> and via the brach statement the program continues at label
> entry01, the entry we had to return to. A special note on the
> loop variable z in the above example: for the loop to work
> this variable must not be changed by the called program, so
> be aware of any aliases. Note the absence of any stack
> overflow or underflow. You could place a test statement like
>
> if StackPointer-'required bytes' < BOS then 'do no call'
>
> before each call but this gives much overhead and interferes
> with the program flow. It is best to keep track of the
> necessary stack space during programming and selecting a
> STACKSIZE value that is slightly larger. Also note that the
> BOS constant determines the maximum number of bytes that can
> be read into the scratchpad using the SERIN command, without
> conflicting with the stack. At any time, the StackPointer
> variable determines the absolute maximum number of bytes to
> read in with SERIN, without corrupting the stack.
>
> To demonstrate passing variables via the stack take a look at
> the second call example. Here any parameters are placed onto
> the stack prior to the return point. Also note how the
> stackpointer is adjusted. The function to call is simular to
> entry05. This function retrieves the parameters into named
> variables to allow math calculations before calling the
> actual function, which again is programmed as a subroutine.
> Upon return from this subroutine, any results (if any, or
> maybe in other variables) are placed back onto the stack to
> the locations occupied by the parameters. If the total bytes
> of any result is more than the total bytes of the parameters,
> you should have reserved this space by decreasing the
> stackpointer more, prior to calling. At all times however,
> the return point is the last byte on the stack. Once we are
> back at the return point, any result is retrieved from the
> stack and the stackpointer is adjusted.
>
> Why passing variables via the stack if we can use variables?
>
> Whenever possible, the use of variables is the way to go.
> This scheme of 'call by reference' takes less time and less
> code. It has a major drawback: the function cannot call
> itself. By using the stack for parameter passing, we use a
> scheme known as 'call by value' which does allow a function
> to call itself (recursive programming). Why would we want this?
>
> Lets take the example of the faculty calculation y=x! (for
> x=6 then 6!=1*2*3*4*5*6), which is not the best example as it
> can easily be program with a for-next loop, but it
> demonstrates recursive programming.
>
> unsigned integer faculty(unsigned integer x)
> {
> if x=0 then return 1 else return x*faculty(x-1)
> }
>
> For x=6 the function calls itself six times. The final result
> is 1*1*2*3*4*5*6. Note that the factor 1 occurs twice, once
> for faculty(0) and once for faculty(1). Also note there
> always must be a limiting factor, in this case the function
> stops calling itself when x=0. Whenever possible you should
> try to avoid recursive programming as it requires lots of
> stackspace which may not be available, resulting in program
> errors. It's nice to have the option though. In the function
> above no additional variables were required. If they are,
> they have to created onto the stack (local variables), and
> any results for these variables have to be writtrn to the
> stack prior to the function calling itself. Each time the
> function calls itself, a new set of local variables is
> created onto the stack. This makes the function re-entrant,
> which is imperative for recursive programming.
>
> Unfortunately, variables on the stack are not easy to use.
> You can not directly use them for calculations or testing, so
> a great number of put and get commands would be required
> within each function. To reduce the overhead we work the
> other way around. We save the last piece of variable ram onto
> the stack and define new variables occupying this ram space.
> Take a look at function_03 for an example.
>
> function_03:
> gosub alloc4 'allocate 4 bytes for local variables
> x1 var W12 'local word variable
> y1 var B23 'local byte variable
> 'code here
> goto free4 'free local variables and return from gosub
>
> The alloc subroutines copy the contents of the last 4 or 8
> bytes of variable ram to the stack. To reduce code only 4 or
> 8 bytes can be freed, which is enough for most cases. See how
> the local variables x1 and y1 are defined to known locations
> inside the freed variable ram. To keep things clear, you
> should use these variables only within the subroutines in
> which they are defined (for for-next loops, counters,
> intermediate results etc.), because for the stamp compiler
> they are just aliases. Upon return from the subroutine you
> must go to either free4 (if you used alloc4) or free8 (if you
> used alloc8) to restore the contents of the variable ram. In
> this way we only have two statements overhead and direct
> access to the local variables. The same technique is
> applicable to program calls. The main advantages of these
> local variables is that you can avoid running out of variable
> space (using variables for multiple purposes which may lead
> to hard to debug errors) and making functions re-entrant.
>
> This concludes this article about stack use for the stamp.
> I hope it demonstrates the program power that a multilevel
> stack can provide.
>
> Peter Verkaik, May 14, 2001
> Any more suggestions are welcome.
> Please send errors, enhancements and suggestions to
> peterverkaik@e...
>
> SUPPLEMENT:
>
> I ran into the problem of retaining variable values during
> program switching. Thought you might be interested in my
> solution. As the stack mechanism does provide a means to
> retain variable values for the calling program (i.e parent),
> it does not for the called program (i.e. child), as the ram
> locations are restored upon return. To overcome this I found
> an elegant way. I reserve some scratchpad locations above the
> stack to hold program variables that have to be retained
> during a program call. Upon entry of a program these values
> are restored, while when doing a program call these values
> are saved. This reduces overhead to just 2 gosubs and 2 small
> routines.
>
> Retain1 var word 'retainable vars (known to
> program only)
> start at ramlocation 0
> '
> Global1 var word 'globals (known to all programs)
> '
> Local1 var word 'locals stop at ramlocation
> 25 (known to
> program only)
>
> 'program 0
>
> gosub Restore0Global 'first line of code: restore
> context upon entry
> branch TargetID,[noparse][[/noparse]...]
> '
> gosub PushLocal
> put StackPointer,EN1P0
> StackPointer=StackPointer-1
> TargetID=EN6P4
> goto CallProgram
> entry01:
> 'return point
> gosub PopLocal
> '
> CallProgram: 'single entry used for program calling
> gosub Save0Globsl 'save context before calling
> run TargetID
> '
> '
> Save0Global:
> for X=0 to RETAIN0SIZE-1
> put RETAIN0START-X,B0(X) 'implied array
> next 'put B0 first,
> B3 last (for RETAIN0SIZE=4)
> return
>
> Restore0Global:
> for X=0 to RETAIN0SIZE-1
> get RETAIN0START-X,B0(X) 'implied array
> next 'get B0 first,
> B3 last (for RETAIN0SIZE=4)
> return
>
> 'Program 1
> Save1Global:
> for X=0 to RETAIN1SIZE-1
> put RETAIN1START-X,B0(X) 'implied array
> next 'put B0 first,
> B3 last (for RETAIN1SIZE=4)
> return
>
> Restore1Global:
> for X=0 to RETAIN1SIZE-1
> get RETAIN1START-X,B0(X) 'implied array
> next 'get B0 first,
> B3 last (for RETAIN1SIZE=4)
> return
>
> The constants RETAIN?START and RETAIN?SIZE are defined at the
> program start.
> TopOfScratchPad con 126 '126 or 62
> retain01 var word 'start
> at ramlocation 0 (program 0)
> retain02 var byte
> RETAIN0SIZE con 3
> RETAIN0START con TopOfScratchPad '126 or 62
> retain11 var byte 'start
> at ramlocation 0 (program 1)
> retain12 var byte
> RETAIN1SIZE con 2
> RETAIN1START con RETAIN0START-RETAIN0SIZE
> etc. until
> RETAIN7SIZE con 0
> 'program 7 without retainable variables
> RETAIN7START con RETAIN6START-RETAIN6SIZE
> TOS con RETAIN7START-RETAIN7SIZE
> 'Top Of Stack
> For programs with a RETAINSIZE=0 the gosub in the first line
> must be removed, so must the gosub in CallProgram and both
> routines Save?Global and Restore?Global.
>
> Any comments on this procedure for a (stripped down) context
> switch is welcomed.
>
> Regards peter.
>
>
>
> To UNSUBSCRIBE, just send mail to:
> basicstamps-unsubscribe@yahoogroups.com
> from the same email address that you subscribed. Text in the
> Subject and Body of the message will be ignored.
>
>
> Your use of Yahoo! Groups is subject to
http://docs.yahoo.com/info/terms/
The easiest way is to copy the subroutine in the first slot
to the second slot. Place a remark by both the subroutines
that there is a copy of this routine in an other slot.
This will remind you later to change both subroutines
iIf they need to be modified.
There are pitfalls though, for example, variables used in the
subroutine must also be available in the slot you
copy the routine to.
Regards peter
Oorspronkelijk bericht
Van: sethjaredjeromiejohnson [noparse]/noparse]mailto:[url=http://forums.parallaxinc.com/group/basicstamps/post?postID=s8ShSfuAP4EVbbzywxWTAmPqYCI9t_jUBU1ZPGtYB8HagsK5wWuONGB94BZoNFh19MblpgOxcvcPLvMOtzk]sjohns10@h...[/url
Verzonden: dinsdag 25 december 2001 15:57
Aan: basicstamps@yahoogroups.com
Onderwerp: [noparse][[/noparse]basicstamps] Re: Beginner with BS2SX
I got the program into three seperate slots, but whenever I try to
compile it, it comes up with an "Undefined Label" error in the
second slot where there is a gosub instruction. The gosub
instruction in the second slot tells the program to go back to the
first slot, then return. How would I fix this? Thanks.
To UNSUBSCRIBE, just send mail to:
basicstamps-unsubscribe@yahoogroups.com
from the same email address that you subscribed. Text in the Subject and
Body of the message will be ignored.
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
Yes there is a better but more complicated way.
I once had a discussion with Tracy Allen on this list
and finally came to a general call/return scheme by
implenting a stack mechanism using the scratchpad.
Al Williams has posted the outcome of that discussion at
http://www.al-williams.com/wd5gnr/stampfaq.htm#s3.20
(3.20 How can I simulate call/return across banks?)
Regards peter
Oorspronkelijk bericht
Van: sethjaredjeromiejohnson [noparse]/noparse]mailto:[url=http://forums.parallaxinc.com/group/basicstamps/post?postID=5B233YXQ4zSAUslx0gz6hR2GPB_lmrwC732eSEAAzd9PoQ0WZ-OZ0VabpqucekujJrUi8vsjtMX7IgzyQQ]sjohns10@h...[/url
Verzonden: dinsdag 25 december 2001 18:00
Aan: basicstamps@yahoogroups.com
Onderwerp: [noparse][[/noparse]basicstamps] Re: Beginner with BS2SX
--- In basicstamps@y..., "peter verkaik" <peterverkaik@b...> wrote:
> Hi,
>
> The easiest way is to copy the subroutine in the first slot
> to the second slot. Place a remark by both the subroutines
> that there is a copy of this routine in an other slot.
> This will remind you later to change both subroutines
> iIf they need to be modified.
> There are pitfalls though, for example, variables used in the
> subroutine must also be available in the slot you
> copy the routine to.
>
> Regards peter
Almost every section of my program is dependant on another section in
the program. For instance, I have the program going back to the
beginning often to check the state of the power button. There are
several instances similar to that one, and if I copied them all to
where they were needed (in the same slot), then I'm back to start
because the program is too large to fit in one slot. Is there some
way to get the program to recognize that the other parts of the
program are in different slots? Thank you very much for your help.
To UNSUBSCRIBE, just send mail to:
basicstamps-unsubscribe@yahoogroups.com
from the same email address that you subscribed. Text in the Subject and
Body of the message will be ignored.
Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
I noticed Al's faq page has a disturbed pagelayout concerning the example.
So here is the original from my archive.
Multilevel stack design and usage for Basic Stamp BS2SX, BS2E and BS2P using
the scratchpad.
Many thanks to Tracy Allen for all his input and positive suggestions.
This design uses the available scratchpad to hold a multilevel stack that
can be used
to call functions (with parameters if required) in other programs and to
create named
local variables for use within gosubs. It shows a general program setup that
can be
used as a template.
Features:
up to 32 entry/return points per program
nested program calls
parameter passing via stack
named local variables for subroutines
Limitations:
program calls can only execute at main level code outside for-next loops.
names for local variables must be unique as they are seen as aliases
no stackpointer check
'scratchpad usage (TOS stands for Top Of Stack)
'TOS-0 = entryID/runID 1
'TOS-1 = entryID/runID 2
'TOS-2 = entryID/runID 3
'etc.
'program 0
TopOfStack1 con 62 'top of stack fot BS2SX, BS2E
TopOfStack2 con 126 'top of stack for BS2P
TOS con TopOfStack2 'pick the right top of stack
STACKSIZE con 30 'define stacksize
BOS con TOS-STACKSIZE 'bottom of stack
EN1P0 con (1<<3)+0 'entry01 in program 0
EN2P0 con (2<<3)+0 'entry02 in program 0
EN3P0 con (3<<3)+0 'entry03 in program 0
EN4P0 con (4<<3)+0 'entry04 in program 0
EN5P0 con (5<<3)+0 'entry05 in program 0
EN4P3 con (4<<3)+4 'entry 4 in program 3
EN14P4 con (14<<3)+4 'entry 14 in program 4
EN6P7 con (6<<3)+7 'entry 6 in program 7
'system variables
TargetID var byte 'entry index and program to run
runID var TargetID.lownib 'entry index b0, program to run
entryID var TargetID.highnib 'entry index b4-b1
StackPointer var byte 'stackpointer
'application variables
x var byte
y var byte
z var byte
ProgramID con 0
begin0:
branch (TargetID>>3),[noparse][[/noparse]main0,entry01,netry02,...,entry1F]'first entry for
direct run
main0:
StackPointer=TOS
'insert init code here
main0loop:
'insert mainloop code here
z=0
call_loop_example:
'1st program call example, stackpointer check could be placed here
put StackPointer,EN1P0
StackPointer=StackPointer-1
TargetID=EN14P4
run TargetID 'execute entry 14 in program 4, then return to entry01
entry01:
'return point for call
z=z+1
if z<10 then call_loop_example
'insert mainloop code here
'2nd call example, stackpointer check could be placed here
put StackPointer,x 'first put parameters onto stack
put StackPointer-1,y
put StackPointer-2,EN2P0 'then put return point
StackPointer=StackPointer-3
TargetID=EN6P7
run TargetID 'execute entry 6 in program 7, then return to entry01
entry02:
'return point for call
get StackPointer+2,x 'retrieve results
get StackPointer+1,y
StackPointer=StackPointer+2 'remove results from stack
'insert mainloop code here
'direct run example
TargetID=$03
run TargetID 'run program 3 directly (no call but jump)
'insert mainloop code here
main0end:
goto main0loop
'function entries for program calls
entry03:
gosub function_03
put StackPointer,EN4P3
StackPointer=StackPointer-1
TargetID=EN4P0
run TargetID 'execute entry 14 in program 4, then return to entry01
entry04:
'return point for call
goto ReturnFromProgramCall_0
entry05:
get StackPointer+3,x 'get parameters from stack
get StackPointer+2,y
gosub function_04 'perform some math on x and y, results in x and y
put StackPointer+3,x 'place results on stack
put StackPointer+2,y
goto ReturnFromProgramCall_0
ReturnFromProgramCall_0:
StackPointer=StackPointer+1 'adjust stack here
get StackPointer,TargetID 'get TargetID
run TargetID 'return to parent program
'subroutines
function_03:
gosub alloc4 'allocate 4 bytes for local variables
x1 var W12 'local word variable
y1 var B23 'local byte variable
'code here
goto free4 'free local variables and return from gosub
function_04:
'code here
return
alloc8: put StackPointer,B18 'space is allocated from B25 upwards
put StackPointer-1,B19
put StackPointer-2,B20
put StackPointer-3,B21
StackPointer=StackPointer-4
alloc4: put StackPointer,B22
put StackPointer-1,B23
put StackPointer-2,B24
put StackPointer-3,B25
StackPointer=StackPointer-4
return
free8: get StackPointer+8,B18
get StackPointer+7,B19
get StackPointer+6,B20
get StackPointer+5,B21
get StackPointer+4,B22
get StackPointer+3,B23
get StackPointer+2,B24
get StackPointer+1,B25
StackPointer=StackPointer+8
return
free4: get StackPointer+4,B22
get StackPointer+3,B23
get StackPointer+2,B24
get StackPointer+1,B25
StackPointer=StackPointer+4
return
end
When we use the stack for program calls, each program should start with a
branch
statement as shown in program 0. The first entry for all these branch
statements
must point to the program's main entry. The reamaining entries (max. 31)
point
to either function entries or call return points. The global variable
TargetID
holds the index for the branch statements in its upper five bits. The lower
three
bits of TargetID determine the program to run.
At powerup/rest all variables are cleared to 0, so TargetID holds 0, and
therefore
the brach statement takes us to main0. At that point the stackpointer is
initialized.
This stack initialization is only required at this point in program 0. The
other programs
do not require such an initialization.
Now take a look at the call_loop_example. It demonstrates how to do program
calls inside
a loop. Remember you can not use for-next loops, hence the if-then
construction that simulates
a for-next loop without using the Stamp internal stack which is reset when
switching programs.
To do a program call, you must first save the return point onto the stack.
The statements
put StackPointer,EN1P0
StackPointer=StackPointer-1
handle this. The constant EN1P0 is calculated at compile-time and is
determined by the
index of entry01 in the branch statement and the program the call is made
from. The statement
TargetID=EN14P4
is used to set the target, i.e the function entry point inside the program
we want to call.
The statement
run TargetID 'execute entry 14 in program 4, then return to entry01
will then switch to the program determined by the three lower bits of
TargetID.
The first line of code for this new program holds a simular branch statement
as shown above.
Given TargetID, the branch statement takes us to the desired function entry,
which is
simular to entry03 in the above example. The function itself is programmed
as a subroutine
that is called from this entry. Upon return from this subroutine the
function first performs
another program call (we now have two nested program calls) and then exits
by going to the
ReturnFromProgrammCall entry. The number of nested program calls allowed is
determined by
the available stackspace. The statements
StackPointer=StackPointer+1 'adjust stack here
get StackPointer,TargetID 'get TargetID
get the stored return point from the stack. The statement
run TargetID 'return to parent program
then switches back to the parent program that made the call, and via the
brach statement
the program continues at label entry01, the entry we had to return to.
A special note on the loop variable z in the above example: for the loop to
work this
variable must not be changed by the called program, so be aware of any
aliases.
Note the absence of any stack overflow or underflow. You could place a test
statement like
if StackPointer-'required bytes' < BOS then 'do no call'
before each call but this gives much overhead and interferes with the
program flow. It is best
to keep track of the necessary stack space during programming and selecting
a STACKSIZE value
that is slightly larger. Also note that the BOS constant determines the
maximum number
of bytes that can be read into the scratchpad using the SERIN command,
without conflicting
with the stack. At any time, the StackPointer variable determines the
absolute maximum number
of bytes to read in with SERIN, without corrupting the stack.
To demonstrate passing variables via the stack take a look at the second
call example.
Here any parameters are placed onto the stack prior to the return point.
Also note
how the stackpointer is adjusted. The function to call is simular to
entry05. This
function retrieves the parameters into named variables to allow math
calculations before
calling the actual function, which again is programmed as a subroutine. Upon
return
from this subroutine, any results (if any, or maybe in other variables) are
placed back
onto the stack to the locations occupied by the parameters. If the total
bytes of any
result is more than the total bytes of the parameters, you should have
reserved this
space by decreasing the stackpointer more, prior to calling. At all times
however, the
return point is the last byte on the stack.
Once we are back at the return point, any result is retrieved from the stack
and the
stackpointer is adjusted.
Why passing variables via the stack if we can use variables?
Whenever possible, the use of variables is the way to go. This scheme of
'call by reference'
takes less time and less code. It has a major drawback: the function cannot
call itself.
By using the stack for parameter passing, we use a scheme known as 'call by
value' which does
allow a function to call itself (recursive programming). Why would we want
this?
Lets take the example of the faculty calculation y=x! (for x=6 then
6!=1*2*3*4*5*6), which is
not the best example as it can easily be program with a for-next loop, but
it demonstrates
recursive programming.
unsigned integer faculty(unsigned integer x)
{
if x=0 then return 1 else return x*faculty(x-1)
}
For x=6 the function calls itself six times. The final result is
1*1*2*3*4*5*6. Note that
the factor 1 occurs twice, once for faculty(0) and once for faculty(1). Also
note there
always must be a limiting factor, in this case the function stops calling
itself when x=0.
Whenever possible you should try to avoid recursive programming as it
requires lots of
stackspace which may not be available, resulting in program errors. It's
nice to have
the option though. In the function above no additional variables were
required. If they
are, they have to created onto the stack (local variables), and any results
for these
variables have to be writtrn to the stack prior to the function calling
itself. Each time
the function calls itself, a new set of local variables is created onto the
stack.
This makes the function re-entrant, which is imperative for recursive
programming.
Unfortunately, variables on the stack are not easy to use. You can not
directly use
them for calculations or testing, so a great number of put and get commands
would be
required within each function. To reduce the overhead we work the other way
around.
We save the last piece of variable ram onto the stack and define new
variables occupying
this ram space. Take a look at function_03 for an example.
function_03:
gosub alloc4 'allocate 4 bytes for local variables
x1 var W12 'local word variable
y1 var B23 'local byte variable
'code here
goto free4 'free local variables and return from gosub
The alloc subroutines copy the contents of the last 4 or 8 bytes of variable
ram to the
stack. To reduce code only 4 or 8 bytes can be freed, which is enough for
most cases.
See how the local variables x1 and y1 are defined to known locations inside
the freed
variable ram. To keep things clear, you should use these variables only
within the subroutines
in which they are defined (for for-next loops, counters, intermediate
results etc.), because
for the stamp compiler they are just aliases. Upon return from the
subroutine you must go to
either free4 (if you used alloc4) or free8 (if you used alloc8) to restore
the contents of
the variable ram. In this way we only have two statements overhead and
direct access to the
local variables. The same technique is applicable to program calls.
The main advantages of these local variables is that you can avoid running
out of
variable space (using variables for multiple purposes which may lead to hard
to debug errors)
and making functions re-entrant.
This concludes this article about stack use for the stamp.
I hope it demonstrates the program power that a multilevel stack can
provide.
Peter Verkaik, May 14, 2001
Any more suggestions are welcome.
Please send errors, enhancements and suggestions to
peterverkaik@e...
SUPPLEMENT:
I ran into the problem of retaining variable values during program
switching.
Thought you might be interested in my solution.
As the stack mechanism does provide a means to retain variable values for
the calling
program (i.e parent), it does not for the called program (i.e. child), as
the ram locations
are restored upon return.
To overcome this I found an elegant way.
I reserve some scratchpad locations above the stack to hold program
variables that
have to be retained during a program call.
Upon entry of a program these values are restored, while when doing a
program call
these values are saved. This reduces overhead to just 2 gosubs and 2 small
routines.
Retain1 var word 'retainable vars (known to program only)
start at ramlocation 0
'
Global1 var word 'globals (known to all programs)
'
Local1 var word 'locals stop at ramlocation 25 (known to
program only)
'program 0
gosub Restore0Global 'first line of code: restore
context upon entry
branch TargetID,[noparse][[/noparse]...]
'
gosub PushLocal
put StackPointer,EN1P0
StackPointer=StackPointer-1
TargetID=EN6P4
goto CallProgram
entry01:
'return point
gosub PopLocal
'
CallProgram: 'single entry used for program calling
gosub Save0Globsl 'save context before calling
run TargetID
'
'
Save0Global:
for X=0 to RETAIN0SIZE-1
put RETAIN0START-X,B0(X) 'implied array
next 'put B0 first, B3 last (for RETAIN0SIZE=4)
return
Restore0Global:
for X=0 to RETAIN0SIZE-1
get RETAIN0START-X,B0(X) 'implied array
next 'get B0 first, B3 last (for RETAIN0SIZE=4)
return
'Program 1
Save1Global:
for X=0 to RETAIN1SIZE-1
put RETAIN1START-X,B0(X) 'implied array
next 'put B0 first, B3 last (for RETAIN1SIZE=4)
return
Restore1Global:
for X=0 to RETAIN1SIZE-1
get RETAIN1START-X,B0(X) 'implied array
next 'get B0 first, B3 last (for RETAIN1SIZE=4)
return
The constants RETAIN?START and RETAIN?SIZE are defined at the program start.
TopOfScratchPad con 126 '126 or 62
retain01 var word 'start at ramlocation 0 (program 0)
retain02 var byte
RETAIN0SIZE con 3
RETAIN0START con TopOfScratchPad '126 or 62
retain11 var byte 'start at ramlocation 0 (program 1)
retain12 var byte
RETAIN1SIZE con 2
RETAIN1START con RETAIN0START-RETAIN0SIZE
etc. until
RETAIN7SIZE con 0 'program 7 without retainable variables
RETAIN7START con RETAIN6START-RETAIN6SIZE
TOS con RETAIN7START-RETAIN7SIZE 'Top Of Stack
For programs with a RETAINSIZE=0 the gosub in the first line must be
removed,
so must the gosub in CallProgram and both routines Save?Global and
Restore?Global.
Any comments on this procedure for a (stripped down) context switch is
welcomed.
Regards peter.
Have a look at http://www.emesystems.com/BS2SX.htm
There is a lot of information about local and global variables, and
cross-bank calls.
This should solve your problem, without the need of copying subroutines in
every bank.
I use this system, it's working great !
Regards,
Phil.
Original Message
From: "sethjaredjeromiejohnson" <sjohns10@h...>
To: <basicstamps@yahoogroups.com>
Sent: Wednesday, December 26, 2001 3:00 AM
Subject: [noparse][[/noparse]basicstamps] Re: Beginner with BS2SX
>
> --- In basicstamps@y..., "peter verkaik" <peterverkaik@b...> wrote:
> > Hi,
> >
> > The easiest way is to copy the subroutine in the first slot
> > to the second slot. Place a remark by both the subroutines
> > that there is a copy of this routine in an other slot.
> > This will remind you later to change both subroutines
> > iIf they need to be modified.
> > There are pitfalls though, for example, variables used in the
> > subroutine must also be available in the slot you
> > copy the routine to.
> >
> > Regards peter
>
> Almost every section of my program is dependant on another section in
> the program. For instance, I have the program going back to the
> beginning often to check the state of the power button. There are
> several instances similar to that one, and if I copied them all to
> where they were needed (in the same slot), then I'm back to start
> because the program is too large to fit in one slot. Is there some
> way to get the program to recognize that the other parts of the
> program are in different slots? Thank you very much for your help.
>
>
> To UNSUBSCRIBE, just send mail to:
> basicstamps-unsubscribe@yahoogroups.com
> from the same email address that you subscribed. Text in the Subject and
Body of the message will be ignored.
>
>
> Your use of Yahoo! Groups is subject to http://docs.yahoo.com/info/terms/
>
>
>