Variable stack on 28X1

Jeremy Leach

Senior Member
Just wondering, now we've got a scratchpad, if others have considered this, and what people's ideas are?

I'm thinking that could keep the upper half of the 28 byte variables as 'global variables' and the other half put on a stack. So every time you call a subroutine you 'Put' the current 14 variables onto the stack, then have the b0 to b13 variables free to use in your subroutine.

The 28X1 has an 8 level stack, and the memory required is 8 * 14 = 112 bytes which fits into the scratchpad nicely.

All you need is an entry and exit routine in each subroutine - on entry you save the current b0-b13 variables to the stack, on exit you retrieve them. The exit routine can be a 'goto exit routine' rather than a return, where the exit routine includes the return statement. The entry routine, if called within the subroutine, would reduce the possible stack level to 7, so it might be best to save variables before calling subroutines.

This idea will of course introduce some overhead, but might make coding a LOT simpler, where each subroutine has a free range of 14 variables and can have it's own set of symbol declarations. Could also dedicate a set of these 14 variables to be 'parameters' passed to and from the subroutine.
 

womai

Senior Member
Hi Jeremy,

your basic idea is solid, and I am having similar plans for the scratchpad. The problem I see is that the way the Picaxe Basic works there is really not much possibility to truly encapsulate portions of the code.

E.g. what if you want to use background serial receive for longer data strings - that will use the scratchpad and so would collide with your proposed scheme. Or you may sorely need that additional scratchpad RAM for general data storage or to simulate a data array. Also, some application will need more variables to be saved and some less - and with a relatively slow device like the Picaxe I'd think twice about wasting time on saving superfluous data.

That reminds me of the recent thread about a coding standard for general purpose routines to be shared between different users. Personally I think such an endeavor makes little sense on a resource-constrained platform like a microcontroller. (It does make a lot of sense on "big-iron" PCs etc. where clear code standards are important to allow reliable development, but run time and memory usage are usually secondary because the PC has more than enough horsepower and RAM). Programs for such small platforms tend to be highly optimized in terms of code size, execution speed, usage of available capabilities like timers etc., and overhead in any of those is largely discouraged.

That's not to say using the scratchpad as a stack does not have its merits, but I think I'd rather optimize that to my particular application as well.

And for sharing some clever code, it's often more beneficial to explain what the idea behind it is and then let everybody adapt it to his liking.

Just my 2 cents, and I'm curious about other people's take in this!

Wolfgang
 

inglewoodpete

Senior Member
I agree with everybody so far! It's certainly good to have standards. However, that's often difficult where there are a diverse range of constraints in the design of a microcontroller project.
Eg.
* Execution speed
* RAM usage
* programme storage
* EEPROM usage
* Pin usage

I guess I try to have standards (and even standard routines) when other constraints allow. The key to standard code is to make it as efficient and flexible as possible.

Did I say "efficient and flexible"? - there's a potential conflict already ;o)
 

womai

Senior Member
Well, "efficient" can have many meanings - "fast & small code" is only one of them. But depending on the application it could also be "easily allows to create/add/modify the program code without introducing errors". And good, consistent clean, coding style certainly helps to achieve this goal, so on a "big PC" I'd rate readable, straightforward code as almost my highest goal (I rareley try to pull any dirty programming tricks when coding in VB or C). The point I wanted to make was more that on the Picaxe (and any other plain 8-bit microcontroller for that matter) there just aren't enough resources to allow for such "luxury". On the other hand, a typical Picaxe program is a few hundred lines of code as best, so relatively easy to navigate, while PC software may comprise millions of lines of code.

Wolfgang
 

Jeremy Leach

Senior Member
Hi Wolfgang

I've got a real application for this - a very complex piece of code > 1000 lines that I'm working on - and I fully understand the points you are making.

I DO need to receive fairly long strings of data into the scratchpad, so I'm planning to restrict the levels of the stack so I can keep a portion of the scratchpad for other things like this. I tend to try to 'flatten' my code out anyway and would rarely need a stack of more than 4.

I've thought about the issue where some subroutines need just a few variables and others a lot - I agree it's not fantastic to always be swapping a large number of variables when only a couple are being used. A way round this might be 'push' and 'pop' only the number of variables each subroutine needs. If each subroutine always uses variables from b0 downwards then it's clear how many variables you need to push before calling the subroutine.

I've also thought about the overhead here, and think on balance it might introduce execution SAVINGS rather than overhead. In big, complex code I currently poke/peek variables to/from RAM because the number of variables required is greater than b0-b13 can provide, even with giving the b0-b13 variables multiple roles. So there are many peek and poke commands throughout my code. With this stack idea I think the use of peek/poke would be GREATLY reduced which could offset the overhead of pushing and popping variables - perhaps both in terms of execution speed and code space.

Plus, is the execution overhead really that great?
If the b0-b13 variables start at address 50 in RAM (like they have on the other picaxes), then the pushing routine could be simply (I think):

<code><pre><font size=2 face='Courier'>
PushVariables:
ptr = StackLevel * 14
Peek 50,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc
Inc StackLevel
Return
</font></pre></code>

Where Stacklevel is a separate, reserved, byte variable. The popping routine would be ..

<code><pre><font size=2 face='Courier'>
PopVariables:
Dec StackLevel
ptr = StackLevel * 14
Poke 50,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc,@ptrinc
Return
</font></pre></code>

Unless I don't understand @ptrinc properly !

It all depends on how often subroutines are called, and for intensely called routines any overhead clearly isn't going to help.

I agree about it being difficult to set coding standards and said so in that previous topic.

I think I'm going to try this out. There's so much sheer effort trying to keep track of the multiple use of the b0-b13 variables in complex code and it's where most of my bugs result. Giving each subroutine it's own freedom in the use of variables is going to be very liberating <img src="wink.gif" width=15 height=15 align=middle>.


Edited by - jeremy leach on 08/05/2007 09:03:15
 

womai

Senior Member
Jeremy,

as far as trying those suggestions - you are lucky and I am green with envy :)

(because if I remember Technical's comment correctly you already have a beta-28X1 in your hand, while the US distributor just announced that he still did not receive the 28X1's that I ordered... can't wait to pop them into my latest design!

Wolfgang
 

Jeremy Leach

Senior Member
I'm ashamed to say that even though I have one, I've not powered it up yet ...still just coding up, and I think now I'll be re-coding to this new method !!

I've just corrected my push and pop routines above - I meant to use @ptrinc.
 

hippy

Ex-Staff (retired)
I have to agree with womai to a degree that the PICAXE is constrained and there isn't much room for such luxuries, but on the other hand, its constrained nature often makes such things necessary.

Any program which needs to use more variables than there are has to have some mechanism to save and reload variables as necessary, at a subroutine or a 'task'/module level. The extra variables on the X1's and X2's do help reduce that necessity.

If there were a @decptr variable as well as @ptrinc, this would really help with implementing stacks, and their use could even be hidden behind very understandable PUSH And POP commands.

I must say that Jeremy's PushVariables and PopVariables routines look to be very elegant solutions to saving blocks of variables which I hadn't considered, and the concept looks sound to me ...

[Added] ... except variables do not seem to be contiguous in the 28X1 and not all can be accessed by Peek and Poke because some are not in page zero of SFR. I'll write some 28X1 code which identifies where the variables are.

Edited by - hippy on 08/05/2007 12:35:34
 

hippy

Ex-Staff (retired)
Variable allocation in SFR for 28X1 Beta ...

b0 @ 34 / $22
b1 @ 35
b4 @ 36
b5 @ 37
b8 @ 38
b9 @ 39
b12 @ 40
b13 @ 41
b16 @ 42
b17 @ 43
b20 @ 44
b21 @ 45
b24 @ 46
b25 @ 47
 

Jeremy Leach

Senior Member
Excellent analysis Hippy ...but sugar, where are the rest?!

It's a pity they aren't b0 to b13, but I guess it's still a sequential block of 14, so it's still possible.

Plus they are grouped by Word, which is great.

Edited by - jeremy leach on 08/05/2007 15:27:13
 

hippy

Ex-Staff (retired)
The rest are presumably in a different SFR Page, and Peek/Poke only accesses Page zero :-(
 

Jeremy Leach

Senior Member
These are all the 'even' words (w0,w2,w4,w6,w8,w10,w12), which makes it a logical split and I think this stack idea is still a goer.

I'll use these even words to push/pop to/from the Variable Stack. The 'odd' words will be used as global variables.

Edited by - jeremy leach on 09/05/2007 13:33:33
 

Jeremy Leach

Senior Member
Just in case anyone's interested, the scheme I'm working on to utilise this push/pop is like this:

I've split my code into descrete 'modules'. Each module has symbol declarations at the top for the 7 'even' words
(w0,w2,w4,w6,w8,w10,w12). There are just two rules to remember when writing gosubs in the code:

1. If one module calls a routine in <i>another </i> module, then Push the current variables to the stack first, and Pop them after the gosub.
2. If code is calling a routine in the <i>same </i> module then no need to push and pop.

The 'odd' words are kept as global variables to be used by any routine and declared at the top of the code.

It's early days but it's already making the code so much easier to write, because each module is standalone and because the code is broken up there is far less multi-use of the variables and so far less potential of bugs in that way. The trick is to break the code into the right modules in the first place to try to reduce the number of push and pops.

(I'm also using some RAM locations as 'parameters' to pass data to/from modules.)


Edited by - jeremy leach on 10/05/2007 09:14:41
 
Top