Access Extra M2 Memory

erco

Senior Member
IIRC one or more of the M2 chips had an extra bank of program memory which could be used in place of the default memory. Either/or.

How to access?

Or have I gone mad?
 

westaust55

Moderator
To have access to program the second memory slot In M2 parts also requires the use of PE6.
 
Last edited:

OLDmarty

Senior Member
Is there really any 'extra' memory to access? I thought both (M) or 4 (X2) slots simply share the base amount of eeprom memory that is naturally available in slot0.
OR, am i misreading/misunderstanding the SLOT command info?? ;-(
 

hippy

Senior Member
Probably the simplest example is code which just bounces from slot to slot.

Download this first -
Code:
#Slot 1
SerTxd( "In slot 1", CR, LF )
Pause 1000
SerTxd( "Pass control to slot 0 ...", CR, LF )
Run 0
At this point your PICAXE will have rebooted after downloading and will still be running whatever was previously downloaded - which goes into slot 0 by default.

We need to change that to something which can pass control to slot 1. So we download this -

Code:
#Slot 0
SerTxd( "In slot 0", CR, LF )
Pause 1000
SerTxd( "Pass control to slot 1 ...", CR, LF )
Run 1
The main problem with slots is that you cannot use slots like subroutines using GOSUB's. You can't start a slot and automatically have it return to after the RUN command which started it. You can only run a program which is in a slot from the beginning, and it keeps running until another RUN command, when it in turn starts the slot it invokes from the beginning.

I haven't really found much use for slots myself. The main use I can think of is where one had a program such as a datalogger and you could have the program fill EEPROM with data, and have another slot which contained a program which reported what had been captured. Which to run could depend on a how an input was set when powered up.

But there's not a lot to be gained from that over having both capabilities in the same program if it fits into memory.

IMO slots are one of those things which is there if one wants it or needs it but most people wouldn't need to use them.
 

OLDmarty

Senior Member
Hmmm, OK, so besides the memory advantage of using slots, does this mean the other advantage is that very LONG code could be restructured in a way that we can break the code up into 4 slots (40X2) instead of running out of code space in slot 0.
 

lbenson

Senior Member
Hmmm, OK, so besides the memory advantage of using slots, does this mean the other advantage is that very LONG code could be restructured in a way that we can break the code up into 4 slots (40X2) instead of running out of code space in slot 0.
Yes--this is to my mind exactly the most useful power of slots. It does take some organizing to work with the "always start from the beginning" aspect--especially with regard to slot 0. For additional slots, I begin (in slot 0) by setting a variable (variables are preserved between slots), and then (at the beginning of SLOT n) use a SELECT to jump to the desired routine:
Code:
select slot1Routine
  case slot1_displaySlotNo gosub displaySlotNo
  case slot1_printTemplate gosub printTemplate
  case slot1_printData gosub printData
  case slot1_printDataAddresses gosub printDataAddresses
  case slot1_PrintDiagnostics       ' print diagnostics
  case slot1_updateData
    gosub setOutputs
    gosub updateData
  case slot1_getChartAddress gosub getChartAddress
  else gosub displaySlotNo
endselect
Variable definitions which are shared between slots are best put into an "include" file, and then included in all slots:
Code:
#include "c:\dl\picaxe\20htmlServer_includes.bas"
#slot 1
You may need to include some routines in both (all) slots.

In slot 0, in the initialization code, I set a flag; upon entering or re-entering slot 0, I check that flag (and perhaps other flags) to determine where to jump to to resume execution.
 
Last edited:

mikeyBoo

Senior Member
Slots can actually be very useful. In my former life in the industrial world,
we sometimes used PLC "BASIC modules" or little single-board computers to do tasks
that didn't need big-iron controllers.

For example, suppose you have 10 process lines each with 3 winders. A little
BASIC board sends a character string to a barcode printer when a winder turns out
to a new roll. Very simple task. However, there are small differences between the
lines & winders that must be reflected in the barcodes.

Now, you could maintain a spare board for each line with its own program. But
wouldn't it be easier to have a separate "ROM" program for each line & simply set
an EEPROM location to tell the board which "ROM" to run on reset? After all, each
program is very small.

Well, the same thing will work on the Picaxe. Lets suppose we have 16 little
"apps". Let's put 9 of them in slot 2 & 7 of them in slot 1 (along with a little
"boot manager" in slot 1).
When the Picaxe powers up, the boot manager looks in 2 EEPROM locations to find
out what "app" to run:
SlotNum (the slot to run, 1 2 3 etc.)
AppNum (the app to run 1 2 3 4 5 etc.) Of course you could do it in one byte.
So, we can run any one of 16 apps on reset by simply telling the boot manager what
to put in the SlotNum & AppNum EEPROM locations.
Forgive me if the following isn't exactly apples & oranges, put hopefully it conveys
the BASIC idea & (if you are clever) you can see how it could be adapted to the Picaxe
world. I hope this turns on some light bulbs.

From old MCS-BASIC board:
Code:
PSEUDO-ROM UTILITIES  Ver. 2.01a
Modified for 32K NVM
0_ Install a ROM
1_ Remove a ROM
2_ Insert a ROM
3_ Clear ROM
4_ Partition ROM.... F800H
5_ Reset Options
6_ Hex Loader
7_ Hex Dump
8_ Display Memory
9_ Quit
   Select 0..9     NVM Protect ON

Choosing option 5 allows selecting baud, Boot ROM, etc.

0_ Exit
1_ PROG1..(Save Baud Rate)
2_ PROG2..(Save Baud Rate  RUN ROM1 on Reset)
3_ PROG3..(Save Baud Rate/MTOP)
4_ PROG4..(Save Baud Rate/MTOP  RUN ROM1 on Reset)
5_ PROG5..(Save Baud Rate/MTOP)
           If (on Reset) XBY(5FH)=0A5H  None of Memory is Cleared.
           If (on Reset) XBY(5EH)=034H  RUN Prog. in RAM (Trap Mode)
6_ PROG6..(Same Options as PROG5) Plus Options Below
           MUST put Assembly Language Prog. at 4039H
                3 Options Exist on Return from 4039H
            1- If Carry = 0 Enter Auto-Baud Routine
            2- If Carry = 1 and A Reg.(bit 0) = 0 Use Saved Baud Rate
            3- If Carry = 1 and A Reg.(bit 0) = 1 then RUN ROM1
7_ Select Boot Rom
8_ Clear Reset Options
    Select 0...8

;# what boot manager does on startup...
10 ON X-2 GOTO 22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42

22 RROM2
23 RROM3
24 RROM4
25 RROM5
26 RROM6
27 RROM7
28 RROM8
29 RROM9

30 RROM10
31 RROM11
32 RROM12
33 RROM13
34 RROM14
35 RROM15
36 RROM16
37 RROM17
38 RROM18
39 RROM19

40 RROM20
41 RROM21
42 RROM22
etc...

# EEPROM Board Config Registers
#  8000h = after programming by PROG/FPROG   (PROG1 = 31h...PROG6 = 36h etc.)
#  8001h = baud rate hi-byte RCAP2H (used by PROG1/FPROG1...6)
#  8002h = baud rate lo-byte RCAP2L (used by PROG1/FPROG1...6)
#  8003h = mtop hi-byte (used by PROG3/FPROG3...6)
#  8004h = mtop lo-byte (used by PROG3/FPROG3...6)
#  Since 8005h..800Fh (reserved for MCS-BASIC although not used)
#            Use these locations for other stuff (e.g. 8005h = Remote #, 8006h = sware version, etc.)
#  8005h = Assigned Remote # (allows host to know what board it’s working with)
#          Remote 0 should always be the host, 255 means unassigned.
#  8006h = software version
#  8007h = hardware version
#  8008h = bindcode (hi-byte) \ i.e. a "password" that may be used
#  8009h = bindcode (lo-byte) /      to allow access 0...65535
#  800Ah = ?
#  800Bh = ?
#  800Ch = NVM partition (BASIC/Assembly) e.g. A0h = 0A000h, F8h = 0F800h, etc.
#  800Dh = Xtal Value in MHZ e.g. 242 would be 24.2 MHz etc. (if 0 or 255 use 11.0592 MHz)
#  800Eh = ROM to run on Reset (i.e. Boot Rom)
#  800Fh = Reserved for checking if NVM is Open or Closed
#  8010h = start of ROM1
 

OLDmarty

Senior Member
Code:
#include "c:\dl\picaxe\20htmlServer_includes.bas"
#slot 1
You may need to include some routines in both (all) slots.

In slot 0, in the initialization code, I set a flag; upon entering or re-entering slot 0, I check that flag (and perhaps other flags) to determine where to jump to to resume execution.
Hi @lbenson,
Is there anything 'different' we need to know about the contents of your "20htmlServer_includes.bas"?
Is the included file (whatever we choose to name it, e.g. "included.bas") simply just full of all the symbol definitions and/or anything else that is needed to be shared across all 2 (or 4) slots???

Do we absolutely have to define (for example) "symbol dog = b0" into our "included.bas" file, instead of just defining "symbol dog = b0" into each of our slot codes?
Are you saying that the slots may not have variables that are transparent to each other unless the "included.bas" exists with all the variables defined in that 1 file?

I hope i make sense lol ;-)
 

techElder

Well-known member
Is the included file (whatever we choose to name it, e.g. "included.bas") simply just full of all the symbol definitions and/or anything else that is needed to be shared across all 2 (or 4) slots???

Do we absolutely have to define (for example) "symbol dog = b0" into our "included.bas" file, instead of just defining "symbol dog = b0" into each of our slot codes?
OLDmarty, an include file is just a way to move code out of the programming environment to make writing code easier. You don't have to scroll through it all the time. When you are writing code for slots, the advantage is that the code for each slot has to have the same exact symbol definitions because your variables are shared between all slots. It is very convenient not to have to go to each slot code to adjust/add a variable name.

NOTE: Wherever you place the "includes.bas" link in your code is where the editor will insert the included code.

PS. I learned all of this from hippy. :D
 

lbenson

Senior Member
Hi @@lbenson,
Is there anything 'different' we need to know about the contents of your "20htmlServer_includes.bas"?
It is as Tex says--an include file is useful when you have slots so that all your symbol and constant definitions and other shared code is consistent between slots. There's no rule that says you have to do this, but if your program is big enough to require slots to fit in the memory, then it is probable that using an include file will save you some hair-pulling.
OLDmarty said:
Is the included file (whatever we choose to name it, e.g. "included.bas") simply just full of all the symbol definitions and/or anything else that is needed to be shared across all 2 (or 4) slots???
Basically, yes.
OLDmarty said:
Do we absolutely have to define (for example) "symbol dog = b0" into our "included.bas" file, instead of just defining "symbol dog = b0" into each of our slot codes?
You can define symbols identically (or differently) in each slot separately without an include file.
OLDmarty said:
Are you saying that the slots may not have variables that are transparent to each other unless the "included.bas" exists with all the variables defined in that 1 file?
Byte and word variables (e.g., b0, w7) are transparent (available in all slots) and refer to the same values. Symbols referring to those variables must be defined in each slot so the PE editor/interpreter knows which byte and word variable tokens to substitute for the symbols when the tokenized code is downloaded to the picaxe.
 

premelec

Senior Member
Please note that if you use different names for the same variable in the same or different slots it is VERY easy to get mixed up and have one part of your program mess up other parts inadvertently... :(
 

mikeyBoo

Senior Member
Please note that if you use different names for the same variable in the same or different slots it is VERY easy to get mixed up and have one part of your program mess up other parts inadvertently... :(
Yep, making multiple assignments to a "hard" register can lead to a world of confusion.
For example:
symbol disDoDis = b5
symbol datDoDat = b5
symbol whutDatDo = b5
symbol geeIDunno = b5 etc.
So it may be obvious that (ideally) there should be only one symbol assigned to each hard register.
Yes, there are always exceptions to every rule.

Using the following "alias register" scheme allows the building of Picaxe code libraries
so I don't have to start from ground zero for each project.
Since Picaxe BASIC doesn't have local variables (i.e. exist only within a proc (aka subroutine))
it's the only alternative besides using push/pop (which wastes valuable code space)

I use a little header at the start of each proc (aka subroutine, codeblock, etc.)
that lists the alias registers used in the proc (arguments, results, etc.)
Yes, it's not perfect & it requires a little extra typing, but it's the best way
I have found to make reusable code blocks for Picaxe projects.
Also, because there are no hard register assignments, it's easier to reuse the code
on other platforms.

Not trying to start a movement here, but it is a way to exchange code modules/libraries.

I know that with programmers a 100 guys will do it a 100 different ways.
I also get that some folks regard a specific language or method as religion.
Guess I'm just pragmatic, but the best method for me is the shortest route from point A to B.
Could it be that efficiency is eliminating the needless duplication of effort?
Of course, some folks are too busy to be organized! (chicken or the egg?)

Any thoughts on the following scheme?
Seem like a good idea? (or maybe you're thinking "crazy as a warehouse rat!")


Code:
; simple EXAMPLE of Reusable Code Module:

; word.toAscii_Convert --  (v1.00a by M. Ballew 04-06-2019)
;       convert XW to ascii characters
;
; Status: (works ok)
;
; Revisions:
;       none
;
; Arguments:
;       XW
;
; Results:
;       XW -> tenthousands thousands hundreds tens ones (ascii chars)
;             (XW not altered)
;
; Example:
;       gosub word.toAscii_Convert ; XW -> tenthousands thousands hundreds tens ones (ascii chars)
;
word.toAscii_Convert:
    bintoascii XW, tenthousands, thousands, hundreds, tens, ones
    return

That is why you will see the following at the beginning of code that I post:

;---------------------- VERY IMPORTANT ------------------------------------
; NEED TO ASSIGN variable usage as follows:
;  w0 = b0 (bit0...bit7)    general-purpose bit registers
;       b1 (bit8...bit15)   general-purpose bit registers
;  w1 = b2 (bit16...bit23)  general-purpose bit registers
;       b3 (bit24...bit31)  general-purpose bit registers
;
;  NOTE: always use the general-purpose alias registers inside procs (except linearConversion)
;  w2 = b4 b5     general-purpose word/byte registers (XW XL XH)
;  w3 = b6 b7     general-purpose word/byte registers (YW YL YH)
;  w4 = b8 b9     general-purpose word/byte registers (ZW ZL ZH)
;  w5 = b10 b11   general-purpose word/byte registers (UW UL UH)
;  w6 = b12 b13   general-purpose word/byte registers (VW VL VH)
;  w7 = b14 b15   general-purpose word/byte registers (WW WL WH)
; the U V W X Y Z alias registers are used to avoid hard register assignments
; since remembering the 2 bytes comprising a word can be confusing (e.g. w2 is b4b5),
; it is easier to know what byte registers are part of what word registers as follows:
; e.g. pseudo-variable (i.e. alias) X can be any of w0...w13
;      albeit w0 & w1 are usually reserved as both are bit addressable
; XW = variable X word value, XL = lo-byte of variable X, XH = hi-byte of variable X
symbol XW = w2           ; alias register for w2 word value
symbol XL = b4           ; alias register for w2 lo-byte value
symbol XH = b5           ; alias register for w2 hi-byte value

symbol YW = w3           ; alias register for w3 word value
symbol YL = b6           ; alias register for w3 lo-byte value
symbol YH = b7           ; alias register for w3 hi-byte value

symbol ZW = w4           ; alias register for w4 word value
symbol ZL = b8           ; alias register for w4 lo-byte value
symbol ZH = b9           ; alias register for w4 hi-byte value

symbol UW = w5           ; alias register for w5 word value
symbol UL = b10          ; alias register for w5 lo-byte value
symbol UH = b11          ; alias register for w5 hi-byte value

symbol VW = w6           ; alias register for w6 word value
symbol VL = b12          ; alias register for w6 lo-byte value
symbol VH = b13          ; alias register for w6 hi-byte value

symbol WW = w7           ; alias register for w7 word value
symbol WL = b14          ; alias register for w7 lo-byte value
symbol WH = b15          ; alias register for w7 hi-byte value

; symbol w8 = ??         ; unassigned (use as alias regs if needed)

; Following Reserved for Doing Scale Conversions :
symbol scale1Lo    = w9   ; place to put "from" scale bottom
symbol outValue    = w9   ; place to put output value (result of conversion)
symbol outValueLo  = b18  ; lo-byte of w9 output value (result of conversion)
symbol outValueHi  = b19  ; hi-byte of w9 output value (result of conversion)
symbol scale1Hi    = w10  ; place to put "from" scale top
symbol span1       = w10  ; place to put "from" scale span
symbol roundoff    = w10  ; place to put value to be rounded off
symbol scale2Lo    = w11  ; place to put "to" scale bottom
symbol remainder   = w11  ; place to put remainder of conversion
symbol scale2Hi    = w12  ; place to put "to" scale top
symbol span2       = w12  ; place to put "to" scale span
symbol scale1Val   = w13  ; place to put input value (value to be converted)

; Note: following regs (w10 w11 w12 w13) also used by linearConversion proc
symbol tenmillions  = b20   ; holds ascii 10000000s value (0..9) (w10 = b20 b21)
symbol millions     = b21   ; holds ascii 1000000s value (0..9)
symbol hunthousands = b22   ; holds ascii 100000s value (0..9)   (w11 = b22 b23)
symbol tenthousands = b23   ; holds ascii 10000s value (0..9)
symbol thousands    = b24   ; holds ascii 1000s value (0..9)     (w12 = b24 b25)
symbol hundreds     = b25   ; holds ascii 100s value (0..9)
symbol tens         = b26   ; holds ascii 10s value (0..9)       (w13 = b26 b27)
symbol ones         = b27   ; holds ascii 1s value (0..9)
 

hippy

Senior Member
Here's one trick which allows the global variables to be defined in just the main program, in Slot 0, while Slot 1 automatically gets those ...

Slot0.bas
Code:
#Picaxe 18M2
#Terminal 4800
#No_Data

Symbol counter = w5

#IfDef SLOT_1
  #Slot 1
#Else

  Main:
    Pause 1000
    counter = counter + 1
    Run 1

#EndIf
Slot1.bas
Code:
#Define SLOT_1
#Include "Slot0.bas"

SerTxd( "Counter = ", #counter, CR, LF )
Run 0
 

hippy

Senior Member
You can also extend that technique to include common subroutines which are defined in Slot 0 only, but can be called from Slot 1. The routines are at the end of Slot 0 code if they are not immediately obvious in the code window below -

Slot0.bas
Code:
#Picaxe 18M2
#Terminal 4800
#No_Data

Symbol counter = w5

#IfDef SLOT_1
  #Slot 1
#Else

  Main:
    Pause 1000
    counter = counter + 1
    b0 = 0 : Gosub DebugDump
    Run 1

#EndIf

CommonSubroutines:
  Goto EndofCommonSubroutines

DebugDump:
  SerTxd("In DebugDump. Called from Slot ", #b0, CR, LF )
  Return

EndofCommonSubroutines:
Slot1.bas
Code:
#Define SLOT_1
#Include "Slot0.bas"

b0 = 1 : Gosub DebugDump
SerTxd( "Counter = ", #counter, CR, LF )
Run 0
 

erco

Senior Member
This saved my bacon today. I delivered a project to a client last month using a maxed-out 20M2. They wanted to add another mode to it, so I just stored the new program in another slot, and you can jump between slots with a button press.

As I already mentioned, that's the best reason yet for me using PE6! I'll be using that more now.
 
Top