(See also: Memory Organization )
Before you can use a variable in a PBASIC program you must declare it. "Declare" means letting the BASIC Stamp know that you plan to use a variable, what you want to call it, and how big it is. Although PBASIC does have predefined variables that you can use without declaring them first (see Memory Organization ), the preferred way to set up variables is to use the directive SYMBOL (for the BS1) or VAR (for all other BASIC Stamp models). Here is the syntax for a variable declaration:
SYMBOL name = RegisterName
name VAR VarType
...where name is the name by which you will refer to the variable, RegisterName is the "fixed" name for the register and VarType indicates the type (Bit, Nib, Byte, or Word) of storage for the variable. NOTE: The top example is for the BS1 and the bottom example is for all BASIC Stamp 2 models.
There are certain rules regarding symbol names. Symbols must start with a letter, can contain a mixture of letters, numbers, and underscore (_) characters, and must not be the same as PBASIC keywords or labels used in your program. Additionally, symbols can be up to 32 characters long. See the PBASIC Command Reference for a list of PBASIC keywords. PBASIC does not distinguish between upper and lower case, so the names MYVARIABLE, myVariable, and MyVaRiAbLe are all equivalent.
Refer to the Elements of PBASIC Style for suggested guidelines on naming variables.
For the BS1, the RegisterName is one of the predefined "fixed" variable names, such as W0, W1, B0, B1, etc. Here are a few examples of variable declarations on the BS1:
SYMBOL temp = W0 ' value can be 0 to 65535 SYMBOL cntr = B1 ' value can be 0 to 255 SYMBOL result = B2 ' value can be 0 to 255
The above example will create a variable called temp whose contents will be stored in the RAM location called W0. Also, the variable cntrwill be located at RAM location B1 and result at location B2. Note that temp is a word-sized variable (because that's what size W0 is) while the other two are both byte-sized variables. Throughout the rest of the program, we can use the names temp, cntr, and result instead of W0, B1 and B2, respectively. This makes the code much more readable; it's easier to determine what cntr is used for than it would be to figure out what the name B1 means. Please note, that cntr resides at location B1, and B1 happens to be the high byte of W0. This means than changing cntr will also change temp since they overlap. A situation like this usually is a mistake and results in strange behavior, but is also a powerful feature if used carefully.
For the BS2-family, the Size argument has four choices: 1) Bit (1 bit), 2) Nib (nibble; 4 bits), 3) Byte (8 bits), and 4) Word (16 bits). Here are some examples of variable declarations on the BS2-family:
mouse VAR Bit ' Value can be 0 or 1 cat VAR Nib ' Value can be 0 to 15 dog VAR Byte ' Value can be 0 to 255 rhino VAR Word ' Value can be 0 to 65535
The example above will create a bit-sized variable called mouse, and nibble-sized variable called cat, a byte-size variable called dog and a word-sized variable called rhino. Unlike in the BS1, these variable declarations don't point to a specific location in RAM. Instead, we only specified the desired size for each variable; the BASIC Stamp will arrange them in RAM as it sees fit. Throughout the rest of the program, we can use the names mouse, cat, dog and rhino to set or retrieve the contents of these variables.
A variable should be given the smallest size that will hold the largest value that will ever be stored in it. If you need a variable to hold the on/off status (1 or 0) of switch, use a bit. If you need a counter for a FOR...NEXT loop that will count from 1 to 100, use a byte. And so on.
If you assign a value to a variable that exceeds its size, the excess bits will be lost. For example, suppose you use the nibble variable cat, from the example above, and write cat = 260 (%100000100 binary). What will catcontain? It will hold only the lowest 4 bits of 260: %0100 (4 decimal).
On the BS2-family, you can also define multi-part variables called arrays. An array is a group of variables of the same size, and sharing a single name, but broken up into numbered cells, called elements. You can define an array using the following syntax:
name VAR Size(n)
where name and Size are the same as described earlier. The new argument, (n), tells PBASIC how many elements you want the array to have. For example:
myList VAR Byte(10) ' Create a 10-byte array
Once an array is defined, you can access its elements by number. Numbering starts at 0 and ends at n–1. For example:
myList(3) = 57 DEBUG ? myList(3)
This code will display "myList(3) = 57" on the PC screen. The real power of arrays is that the index value can be a variable itself. For example:
myBytes VAR Byte(10) ' Define 10-byte array idx VAR Nib ' Define nibble variable Main: FOR idx = 0 TO 9 ' Repeat with idx= 0, 1...9 myBytes(idx) = idx * 13 ' Write idx*13 to array NEXT FOR idx = 0 TO 9 ' Repeat with idx= 0, 1...9 DEBUG "myBytes(", DEC idx, ")= ", DEC myBytes(idx), CR ' Show contents of each cell NEXT STOP
If you run this program, DEBUG will display each of the 10 values stored in the elements of the array: myBytes(0) = 0 * 13 = 0, myBytes(1) = 1 * 13 = 13, myBytes(2) = 2 * 13 = 26 ... myBytes(9) = 9 * 13 = 117.
A word of caution about arrays: If you're familiar with other BASICs and have used their arrays, you have probably run into the "subscript out of range" error. Subscript is another term for the index value. It is out-of-range when it exceeds the maximum value for the size of the array. For instance, in the example above, myBytes is a 10-cell array. Allowable index numbers are 0 through 9. If your program exceeds this range, PBASIC will not respond with an error message. Instead, it will access the next RAM location past the end of the array. If you are not careful about this, it can cause all sorts of bugs.
If accessing an out-of-range location is bad, why does PBASIC allow it? Unlike a desktop computer, the BASIC Stamp doesn't always have a display device connected to it for displaying error messages. So it just continues the best way it knows how. It's up to the programmer (you!) to prevent bugs.
Another unique property of PBASIC arrays is this: You can refer to the 0th (first) cell of the array by using just the array's name without an index value. For example:
myBytes VAR Byte(10) ' Define 10-byte array Main: myBytes(0) = 17 ' Store 17 to 0th (first) cell DEBUG ? myBytes(0) ' Display contents of 0th cell DEBUG ? myBytes ' Also displays contents of 0th cell
This feature is how the "string" capabilities of the DEBUG and SEROUT command expect to work, that is, referencing the variable name only and not a specific element. A string is simply a byte array used to store text. See the "Displaying Strings (Byte Arrays)" section in the DEBUG command description for more information.
An alias is an alternative name for an existing variable. For example:
SYMBOL cat = B0 ' Create a byte-sized variable SYMBOL tabby = cat ' Create alias for cat
cat VAR Byte ' Create a byte-sized variable tabby VAR cat ' Create alias for cat
In this example, tabby is an alias to the variable cat. Anything stored in cat shows up in tabby and vice versa. Both names refer to the same physical piece of RAM. This kind of alias can be useful when you want to reuse a temporary variable in different places in your program, but also want the variable's name to reflect its function in each place. Use caution, because it is easy to forget about the aliases; during debugging, you might end up asking 'How did that value get here?!' The answer is that it was stored in the variable's alias.
On the BS2-family an alias can also serve as a window into a portion of another variable. This is done using "modifiers." Here the alias is assigned with a modifier that specifies what part:
rhino VAR Word ' A 16-bit variable head VAR rhino.HIGHBYTE ' Highest 8 bits of rhino tail VAR rhino.LOWBYTE ' Lowest 8 bits of rhino
Given that example, if you write the value %1011000011111101 to rhino, then head would contain %10110000 and tail would contain %11111101.
The table below lists all the variable modifiers. PBASIC2 lets you apply these modifiers to any variable name and to combine them in any fashion that makes sense. For example, it will allow:
rhino VAR Word ' A 16-bit variable eye VAR rhino.HIGHBYTE.LOWNIB.BIT1 ' A bit
Symbol | Definitions |
LOWBYTE | Low byte of a word |
HIGHBYTE | High byte of a word |
BYTE0 | Low byte of a word |
BYTE1 | High byte of a word |
LOWNIB | Low nibble of a word or byte |
HIGHNIB | High nibble of a word or byte |
NIB0 | Nibble 0 of a word or byte |
NIB1 | Nibble 1 of a word or byte |
NIB2 | Nibble 2 of a word |
NIB3 | Nibble 3 of a word |
LOWBIT | Low bit (LSB) of a word, byte, or nibble |
HIGHBIT | High bit (MSB) of a word, byte, or nibble |
BIT0 | Bit 0 (LSB) of a word, byte, or nibble |
BIT1 | Bit 1 of a word, byte, or nibble |
BIT2 | Bit 2 of a word, byte, or nibble |
BIT3 | Bit 3 of a word, byte, or nibble |
BIT4 ... BIT7 | Bits 4 through 7 of a word or byte |
BIT8 ... BIT15 | Bits 8 through 15 of a word |
The commonsense rule for combining modifiers is that they must get progressively smaller from left to right. It would make no sense to specify, for instance, the low byte of a nibble, because a nibble is smaller than a byte! And just because you can stack up modifiers doesn't mean that you should unless it is the clearest way to express the location of the part you want get at. The example above might be improved:
rhino VAR Word ' A 16-bit variable eye VAR rhino.BIT9 ' A bit
Although we've only discussed variable modifiers in terms of creating alias variables, you can also use them within program instructions:
rhino VAR Word ' A 16-bit variable head VAR rhino.HIGHBYTE ' Highest 8 bits of rhino Main: rhino = 13567 DEBUG ? head ' Show the value of alias variable head DEBUG ? rhino.HIGHBYTE ' rhino.HIGHBYTE works too STOP
Modifiers also work with arrays. For example:
myBytes VAR Byte(10) ' Define 10-byte array Main: myBytes(0) = $AB ' Hex $AB into 0th byte DEBUG HEX ? myBytes.LOWNIB(0) ' Show low nib ($B) DEBUG HEX ? myBytes.LOWNIB(1) ' Show high nib ($A)
If you looked closely at that example, you probably thought it was a misprint. Shouldn't myBytes.LowNib(1) give you the low nibble of byte 1 of the array rather than the high nibble of byte 0? Well, it doesn't. The modifier changes the meaning of the index value to match its own size. In the example above, when myBytes() is addressed as a byte array, it has 10 byte-sized cells numbered 0 through 9. When it is addressed as a nibble array, using myBytes.LowNib(), it has 20 nibble-sized cells numbered 0 through 19. You could also address it as individual bits using myBytes.LowBit(), in which case it would have 80 bit-sized cells numbered 0 through 79.
What if you use something other than a “low” modifier, say myBytes.HighNib()? That will work, but its effect will be to start the nibble array with the high nibble of myBytes(0). The nibbles you address with this nib array will all be contiguous, one right after the other, as in the previous example.
myBytes VAR Byte(10) ' Define 10-byte array Main: myBytes(0) = $AB ' Hex $AB into 0th byte myBytes(1) = $CD ' Hex $CD into next byte DEBUG HEX ? myBytes.HIGHNIB(0) ' Show high nib of cell 0 ($A) DEBUG HEX ? myBytes.HIGHNIB(1) ' Show next nib ($D)
This property of modified arrays makes the names a little confusing. If you prefer, you can use the less-descriptive versions of the modifier names; Bit0 instead of LowBit, Nib0 instead of LowNib, and Byte0 instead of LowByte. These have exactly the same effect, but may be less likely to be misconstrued.
You may also use modifiers with the 0th cell of an array by referring to just the array name without the index value in parentheses. It's fair game for aliases and modifiers, both in VAR directives and in instructions.
BASIC Stamp Help Version 2.5.4
Copyright © Parallax Inc.
8/8/2012