Expanding PICAXE named-variables via RAM bank-switching

mrburnette

Senior Member
From my blog: http://www.picaxeforum.co.uk/entry.php?36-Extending-PICAXE-named-variables-by-300-or-more! Re-posted here for easy search identification.

This technique MAY be unsuitable for time-critical code. The sample code is for a 08M2 since it is the most limited shipping chip, but is applicable across the product line.

The (perceived) problem: Not enough named variables, B0-B27 / W0-W13 on an 08M2.

A solution: Use the un-named RAM area to bank-switch the lower variables B0-B27.

DEMO Implementation for 08M2:

Code:
' DEMO of saving named variables and restoring... 3 levels deep
' M. Ray Burnette, 09 Nov 2011
' Refer to Manual 2, page 11 and other parts as appropriate
'
#picaxe 08m2
'
' Fill lower RAM addresses: B0 - B27 (W0 - W13) ... For this DEMO this will be RAM Bank 0
for bptr = 0 to 27 : @bptr = bptr : next

' Copy Bank 0, named variables B0 thru B27 (main memory) into a higher partition
' Select one of the following RAM OFFSET INDEXES: 28, 56, or 84
'
bptr = 0
' BANK 1 _____________________________________>
Poke 28, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc

bptr = 0
' BANK 2 _____________________________________>
Poke 56, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc

bptr = 0
' BANK 3 _____________________________________>
Poke 84, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc
'
'
' For this DEMO, let's clear Bank 0 before each restore
for bptr = 0 to 27 : @bptr = 0 : next
'
' Now restore Bank 1, 2, 3 into Bank 0 (named variables)
bptr = 0
' BANK 1 _____________________________________>
Peek 28, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc
'
for bptr = 0 to 27 : @bptr = 0 : next
'
bptr = 0
' BANK 2 _____________________________________>
Peek 56, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc
'
for bptr = 0 to 27 : @bptr = 0 : next
'
bptr = 0
' BANK 3 _____________________________________>
Peek 84, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc, @bptrinc

END
Observe: under the Simulator that RAM in the 08M2 can be thought of as 4 banks: 0, 1, 2, 3 with BANK0 being the named variables B0-B27. Using a single PEEK or POKE in extended format, RAM areas can be swapped at will to provide a subroutine with a completely fresh or previously used RAM map.

Suggestions:

  • Write Modular code

  • Push (copy) RAM0 into RAM1, RAM2, or RAM3 at the start of a subroutine, 1st executable line.

  • Pull (copy) RAM1, RAM2, or RAM3 back into RAM0 before returning from the subroutine, last line before RETURN

  • Some subroutines may not need private variables, for example, sertxd and serout

  • If you need GLOBAL PUBLIC variables, individual PEEK/POKE statements in the 2nd line of the called subroutine (after the RAM swap) can be utilized to synchronize a few variables. In general, public variables should be limited... you may consider using a single subroutine to do this for BANK-swapping subroutines... to ensure consistency and avoid errors. REMEMBER, unless you take action to save a results as PUBLIC, the entire named-RAM area will be rewritten immediately before the RETURN and RAM0 bank will revert to the values before the subroutine executed.
 
Last edited:

hwholmes

Member
Good work - kind of like, in another system with limited resources, I used to hide global variable values on the stack and then use the variable locations as scratch pad and then pop the values back when I was done. Here we have 3 stacks - more if the need is smaller (see below).

A simple extension of your idea would be to keep the GLOBAL values at the top or bottom of the bank (depending on whether you needed bits or bytes/words) and shorten the peek and poke strings to only those in the middle that need to be shared. If short enough, you could have more than 3 banks. Using only as much as you need would, of course, reduce the time penalty.
 

mrburnette

Senior Member
<...>

A simple extension of your idea would be to keep the GLOBAL values at the top or bottom of the bank (depending on whether you needed bits or bytes/words) and shorten the peek and poke strings to only those in the middle that need to be shared. <...>
I mentioned that approach in the Blog, but I think for most folks, bank switching everything is easier to get their hands around at first. ON an 20X2, the scratchpad makes a great "RAM stack", too. I always use "byte" variables from the bottom and "word" variables from the top in my SYMBOL defs, so the concept of two (2) GLOBAL (ranges of variables) could also be easily implemented in the concept... just adjust the swap variables accordingly such that "swapping" only occurs in the middle of the address ranges.

Probably worth mentioning here that I am aware that the last "@bptrinc" in the series really should be a "@bptr" but in my use, it does not matter other than an academic issue on which to be scolded :)

This is such an old trick but it is usually done by mucking around with pointer offsets to blocks of RAM, something that I wish the multi-tasking versions of the PICAXE did automatically... as an option... so that each of the up-to-4 multitasking programs could have private variables without any swapping penalties. Unfortunately, some of that user RAM is used during pseudo-multitasking... nothing comes free! However, since the PICAXE is used widely for education, PUBLIC/PRIVATE variable scope is a capability that needs to be implemented into the compiler/simulator to advance the paradigm of modular programming (IMHO.) Please... no fire and brimstone... no heresy suggested.

- Ray
 
Last edited:
Top