Ever wonder what the actual "BASIC" program looked like that a Parallax BASIC Stamp runs? I did. My interest stemmed from two problems I was trying to solve, a "smart" robotics controller based on the PIC, and my inability to download programs into the stamp from my 486 based laptop. It was my belief that decoding the system used in the BASIC stamp would give me insight into how I might write a simiar system for a Robot Control language embedded in a PIC. I also believe that by knowing the program content I could decode a dump of the communication between a working PC and the stamp and derive the communications protocol.
For those of you reading this that are not familiar with the BASIC stamp I give you the following brief summary. A BASIC stamp is a preprogrammed Microchip 16C56 PIC controller and a serial EEPROM. The PIC is a loaded with a BASIC token interpreter, and the code to download BASIC tokens from the PC. BASIC programs are compiled into tokens on the PC and then serially downloaded into the EEPROM on the stamp board. The PIC then reads these tokens from the EEPROM and executes the program. The version of BASIC is "control oriented", meaning it doesn't support strings or complex I/O models, however it does have a fairly rich set of operations you can perform on the 8 I/O pins that are available for your use. For a complete description of what the stamp is and does I refer you to the reading at the end of the article.
Decoding the stamp tokens first required that I have a memory image of the contents of the EEPROM. There are three ways to get this information; Using a serial EEPROM programmer to read the contents of the EEPROM after it has been downloaded to, using the BASIC READ statement to read out the EEPROM, and finally using a little known command in the program, BSAVE, to store a copy of the EEPROM on disk. It was this latter command that made it straight forward to accomplish my mission.
The BSAVE command is documented in the READ.ME file for the BSLOADR.EXE command. Essentially, the BSLOADR program will down load a precompiled BASIC program into the stamp. To create this precompiled program you put the statement BSAVE anywhere in your BASIC program, when you download it from the STAMP.EXE program, the STAMP.EXE program will create a companion file named "CODE.OBJ" in the current directory that contains the BASIC program. This works in the 1.3 Version of the stamp software (available from the Parallax BBS) but I've not tried this with the v1.2 software.
After trying this option I discovered that the code.obj file on my disk was exactly 256 bytes long. This corresponds exactly to the number of bytes that are stored in the serial EEPROM, so I guessed this was simply a copy of the EEPROMs memory image. I verified this using a simple basic program :
FOR B0 = 0 to 255 ' read all of memory READ B0, B1 ' Read from (B0) -> B1 DEBUG B0,B1,cr ' Display B0=xx B1=xx <cr> NAP 7 ' time to copy it NEXT B0 END
The contents were the same except for one important difference, the address of the bytes was reversed. Specifically byte 0 in the code.obj file matched byte 255 in the EEPROM, byte 1 in the code.obj file matched byte 254 in the EEPROM, etc. After decoding the data in the file I've come to the conclusion that the READ command in BASIC actually complements the address value before using it. This allows a user program to use "EEPROM locations" 0 - 10 for example which gets translated to addresses 255 - 245 in the EEPROM.
From this point analysis consisted of looking at the data in as many ways as possible and trying to re-construct the original basic program. My first step was to write a program to dump out the 'core' file in several different formats, (I started using the unix od(1) command but quickly exhausted its abilities) I called this program 'dumper' and the first version was simply:
int i, j, fd; unsigned char buffer[256]; fd = open("core.obj", 0); read(fd, buffer, 256); for (i = 0; i < 256; i += 16) { printf("%02x: "); for (j = 0; j < 16; j++) printf("%02x ", buffer[i+j]); printf("\n"); }
I then wrote a very simple BASIC program :
BSAVE B0 = 1
And dumped it out and got:
00: fa 05 95 98 85 f0 00 00 00 00 00 00 00 00 00 00 ... and lots of zeros.
From this I noticed that the first two bytes were simply complements of each other and the second byte, 05 happened to correspond to the 'address' of the last byte of data.
Now the easiest way to analyze something like this code is to change one thing and analyze all of the dumps to see what changed, so I wrote three more programs:, B0 = 10, B0 = 100, b0 = 1000 and compared the results which were (including the first one):
B0=1 00: fa 05 95 98 85 f0 00 00 00 00 00 00 00 00 00 00 B0=10 00: fa 05 f5 b5 10 be 00 00 00 00 00 00 00 00 00 00 B0=100 00: f9 06 75 cc 91 0b e0 00 00 00 00 00 00 00 00 00 B0=1000 00: f8 07 75 e0 7d 11 0b e0 00 00 00 00 00 00 00 00
Which were clearly different, but how were they different? The one reasurring fact was that byte 1 continued to contain the address of the last real byte of data and byte 0 continued to hold the ones complement of that value.
I stared at this for quite a bit with no luck and decided to check to see just what _bits_ were different. Back to the dumper program and now modified to print out the data in binary. This resulted in these values...02: 10010101 10011000 10000101 11110000 00000000 00000000 02: 11110101 10110101 00010000 10111110 00000000 00000000 02: 01110101 11001100 10010001 00001011 11100000 00000000 02: 01110101 11100000 01111101 00010001 00001011 11100000
Now we were getting somewhere (I started at byte 2 since 0 and 1 seemed to be the complement of the length, and the length) So there were some obvious similarities, but some differences too, but what really lept out at me was they they were definitely NOT byte aligned. This meant that every bit was significant!
So removing the artificial byte alignments and lining up like bits, I got this:
1: 100 10101 1001 1000100001011111000000000000 00000000 2: 111 10101 1011010 1000100001011111000000000 00000000 3: 011 10101 11001100100 100010000101111100000 00000000 4: 011 10101 1110000001111101000 100010000101111100000
I did the alignment by starting at like bits at the end and moving forward, putting a space, and then doing the same from the front.
Well the 1001 in line 1 doesn't look all that much like '1' but the 1011010 has at least '1010' as its last four bits and this is 10. So what is 100 in binary? 0110 0100 (64h). And where does that appear in line 3? After the 110 sequence. This suggested the first three bits somehow described the constant and the rest were the constant. Note that splitting 1001 into three bits of prefix and the rest data is "100 1." So on line four the first three bits were 111 and then the binary code for 1000, in 4 nybbles, even though it could have been represented in three. After some head scratching I came to the conclusion that constants were represented as: 1 nn ddddd where '1' indicates it is a constant, 'nn' is the number of nybbles in the constant except 00 means only one bit (boolean) and 11 means 4 nybbles (word), and ddddd is the constant itself. I verified this by running a few more constants through this analysis and they all held up. Modifying the program slightly to be;
B0 = B0, then B0 = B1, then B0 = B2 etc.
And looking in the same bit positions that the constant showed up in, I was able to determine that a variable was represented as '0 vvvvvv' where 0 means, this is a variable, and vvvvvv is a 6 bit quantity representing the variable in question.
So what about the rest of the bits? Well by changing the program to B1= B0, B2=B0, B3=B0, etc I was able to figure out where the variable on the left half of the equals is in the bitstream, turns out it is the 6 bits following the variable/constant, and it doesn't have a leading '0' bit because it can only be a variable (LET 10 = B0 doesn't parse), determining the bitfields for the operators (+, -, *, //, etc) was simply a matter of cranking through the bits.
With that complete I needed to discover how a complete LET statment was constructed. To do that I put together several variations using variable names and operators that I knew the bit patterns for, except for one problem, the operator values were different. By analyzing this statement:
B0 = 1 + 1
I had separated the bits as follows:
101 10101 1001 100010 00011 1001 00110 11111000 | | | | | | | | | | | | | | | +-- trailer (unknown meaning) | | | | | | +------- Addition operator | | | | | +------------- Constant '1' | | | | +------------------- Equals operator | | | +------------------------- B0 identifier | | +------------------------------- Constant '1' | +------------------------------------- "LET" statment +------------------------------------------ prefix (unknown meaning)
But compiling and verifying B0 = 1 + 1 + 1 gave this (prefix and suffix removed for clarity):
10101 1001 100010 00011 1001 00111 1001 00110 | | | | | | | | | | | | | | | +-- Addition operator | | | | | | +------- Constant '1' | | | | | +------------- Addition oerator + 1? | | | | +------------------- Constant '1' | | | +------------------------- Equals operator | | +------------------------------- B0 identifier | +------------------------------------- constant 1 +------------------------------------------ "LET" statement
Then it dawned on me that the operators were really "oooo F" where oooo was the operator identifier (4 bits) and "F" was 1 if there was more operators to follow, and 0 if this was the last operator.
That completed, I could now decode any LET statement from the bits back into the original program text. This was used for the final breakthrough...
I had to move from decoding LET statments to decoding any statement. Given that any statement could start on any boundary, how could I tell where the statements started? I could put in a LET statement, and its end would be where the next statement started, but where did that statement end?
This was my dilemma until a solution suggested itself from the basic operation of the interpreter. How did *it* know where a statement started? Well parsing along it knew how to get around but what about jumps? The GOTO statement had to encode that address of the statement that the interpreter should jump to, so if I could figure out GOTO statements I could ask it where the next statement was, this will become clear in a moment, but first how to figure out a GOTO? The answer was, take what you know and wrap it around something you don't know, remove what you know and you are left with what is new. I took the program:
BSAVE B0 = 1 goto test b0 = 2 test: END
And dumped it out for analysis, the results were instructive to say the least. What I got was this (separated by bits I understood at the time.):
101 <- this strange prefix 10101 1001 100010 00010 <= let B0 = 1 11001 00001001011 10101 1001 100010 00010 <= let b0 = 1 11111 11111000 <- now a double dose of 1 bits at the end
Given the LET statements I postulated, what if the "statement" token was 5 bits? That would allow us to have 32 different statements. A quick tally of all the statements in the BASIC manual also came up, 32. Coincidence? I didn't think so. Ok so lets take the first 5 bits and call that the statement token, as you can see I already put a space in between them above. What does that imply? For one thing the last statement in our program was "END" and that would appear to be token 11111 (31 decimal) Hmmm, maybe the compiler always sticks an END statement into our code, that would explain why now we have two and we used to have 1. But for our GOTO, what does that give us? It means that 11001 is the GOTO token and 00001001011 is the address. Eleven bits? There are only 256 bytes in the EEPROM. But then it hit me, statements are not aligned on BYTE boundaries, they are aligned on BIT boundaries, how many bits are there in this EEPROM? 2048? Hmmm, 11 bits of address could address 2048 bits. So now back to our END statement (the target of the Goto) as it turns out it occurs in byte 9 of the dump (shown below with byte separations and bit numbers above the bytes.)
of: 01234567 01234567 01234567 01234567 01234567 01234567 --- -------- -------- -------- -------- -------- -------- 02: 10110101 10011000 10000101 10010000 10010111 01011001 08: 10001000 01011111 11111000 00000000 00000000 00000000 ^ | Start of the END statement.
The END statement starts in byte 9 at bit 3, now going back to our address:
00001001 011
Split this way it shows an 8 bit BYTE address and a 3 bit BIT address. I've chosen to represent these addresses as xx:n where xx is the hex byte address and n is the decimal bit number (could be hex it never goes above 7 :-)) Further, given this bit centricity of the interpreter the strange 3 bit prefix suggested itself to be a bit representation. And sure enough, the 11 bits that start at byte 1 in the dump define the address (in bit notation) of the first available bit in the EEPROM. The value 2048 - (byte[1] * 8) + prefix)) == total number of bits used in the EEPROM.
I had the key and the tools. To use them I rewrote my analyzer/dumper program to look for GOTO statements in the data first and save the addresses they pointed to, then when a non-goto was detected, print out all of the bits between the target and the next target. Then I could feed the analyzer programs of the form:
BSAVE GOTO label1 GOTO label2 GOTO label3 GOTO label4 label1: FOR B0 = 1 to 10 label2: PAUSE B0 label3: NEXT B0 label4: END
And it could track the GOTO targets to statements and then print out, as a bitstring, all of the bits in each statement. Running it on the above program yielded:
4 GOTOs found, statement dump is as follows: 1 0a:3 (010 bits) = 01010 1001100010 2 0c:2 (007 bits) = 01111 0100010 3 0d:6 (033 bits) = 10111 100110001011011010100100001100010 4 12:4 (005 bits) = 11111 11111
So now Line 1 is the FOR loop statment, line 2 is the PAUSE statement, line 3 the NEXT statement and line 4 the END statement. I separate the first 5 bits from the rest because I know that is the token for that statement. Knowing how to pick out variables and constants, and keeping in mind that a bit here or there is used as a flag to indicate something (like more parameters in a SOUND statement) I was able to completely decode the entire set of PIC BASIC tokens.
Well at this point I've put in a lot of work and what do I have to show for it? One benefit is that I can now write a STAMP compiler that can compile STAMP programs into correctly formatted memory images for the EEPROM. This brings me one large step closer to being able to use the BASIC stamp from my UNIX workstation. This also gives me the ability to decode the PIC protocol, because now that I know what part of the data being sent to the PIC is data to be stored in EEPROM, and I know the 11 bit address format, I can filter that out of the bits I see going back and forth which leaves me with just the protocol bits. Figuring those out so that I can write my own downloader has to wait for another couple of spare weekends.
Variables Operators Token Value Token Value ------------- --------------- ------- ------- PIN0 - PIN7 000000 - 000111 = 0001 DIR0 - DIR7 001000 - 001111 - 0010 BIT0 - BIT15 010000 - 011111 + 0011 PINS 100000 * 0100 DIRS 100001 ** 0101 B0-B13 100010 - 101111 // 0110 PORT 110000 / 0111 ? 110001 &/ 1000 W0 110010 & 1001 ? 110011 |/ 1010 W1 110100 | 1011 ? 110101 ^/ 1100 W2 110110 ^ 1101 ? 110111 MIN 1110 W3 111000 MAX 1111 ? 111001 W4 111010 ? 111011 W5 111100 ? 111101 W6 111110
In the notation for statements variables are shown as VVVVVV
Constants coded with a two bit 'size' and some number of databits they are as follows (M = MSB, L = LSB):
00 M Single bit (boolean) 01 MxxL One Nybble (short byte) 10 MxxxxxxL Two Nybbles (byte) 11 MxxxxxxxxxxxxxxL Four Nybbles (word)
In the notation below a constant is identified as:
CCCCCC
When either a constant or a variable can be used, the notation CCCVVV is used, this is represented in the bitstream as:
1 CCCCCC 1 followed by constant value 0 VVVVVV 0 followed by variable identifier
Addresses consist of 11 binary digits, the upper 8 are the byte address and the lower 3 are the bit offset in that byte. Bit 0 is the MSB of the byte. In the statement notation and address is represented as:
AAAAAAAAAAA 11 bit address of the form Byte:bit
Individual flag bits are represented in the statement notation as:
F Flag bit
Numbers that must be one or zero are represented as 1 or 0.
Braces, { and } enclose bit patterns that may or may not be present based on the state of a flag bit in the statement.
10101 CCCVVV VVVVVV 0001 F { CCCVVV OOOO F } | | | | | | | | | | | | | | | +-- More Operators Flag | | | | | | +------ Operator | | | | | +------------ Constant/Variable | | | | | | | | | +------------------- More operators Flag | | | +----------------------- "=" operator value | | +------------------------------ Destination variable | +------------------------------------- Source Variable +------------------------------------------- LET token
11001 AAAAAAAAAAA | | | +------ Address to jump to. +------------- GOTO token.
Note: All of the "smarts" in a FOR/NEXT loop are done by the NEXT token. The address it points to is the address of the statement just AFTER the FOR statement.
Note: The step constant is always a positive number, however if the Positive step flag is false, this number is subtracted from the counter variable.
10111 CCCVVV VVVVVV F CCCVVV CCCVVV AAAAAAAAAAA | | | | | | | | | | | | | +-------- Statement Address | | | | | +---------------- starting const/variable | | | | +------------------------ ending const/variable | | | +---------------------------- Positive Step value flag | | +--------------------------------- Counter variable | +--------------------------------------- Step increment (decrement) +---------------------------------------------- NEXT token
01010 CCCVVV VVVVVV | | | | | +- counter variable for the LOOP | +------- Constant/Variable initial value (assigned to variable) +-------------- FOR token
11010 CCCVVV AAAAAAAAAAA F { AAAAAAAAAAA F } | | | | | | | | | | | +-- Another Address Flag | | | | +--------- Branch address | | | +------------------ Another Address Flag | | +------------------------- Branch address (always present) | +----------------------------------- Constant/Variable Index +----------------------------------------- BRANCH token
10001 CCCVVV CCCVVV CCCVVV CCCVVV VVVVVV CCCVVV AAAAAAAAAAA | | | | | | | | | | | | | | | +-- GOTO Address | | | | | | +---------- C/V Target State | | | | | +----------------- Var Workspace | | | | +------------------------ C/V Repeat Rate | | | +------------------------------- C/V Delay Value | | +-------------------------------------- C/V Down state | +--------------------------------------------- C/V Pin to use +---------------------------------------------------- BUTTON token
Note: There is no bytecode encoding for EEPROM since this is taken care of by the STAMP.EXE program.
11111 | +---- END token
00000 CCCVVV | | | +--- Constant/variable containing pin number +--------- HIGH token
00001 CCCVVV | | | +--- Constant/variable containing pin number +--------- LOW token
00010 CCCVVV | | | +--- Constant/variable containing pin number +--------- INPUT token
00011 CCCVVV | | | +--- Constant/variable containing pin number +--------- OUTPUT token
00100 CCCVVV | | | +--- Constant/variable containing pin number +--------- TOGGLE token
00101 CCCVVV | | | +--- Constant/variable containing pin number +--------- REVERSE token
Table of logical operator values.
Token Value ----- ----- ??? 000 < 001 > 010 <> 011 = 100 <= 101 >= 110 ??? 111
11000 CCCVVV VVVVVV F F OOO {CCCVVV VVVVVV F F OOO} AAAAAAAAAAA | | | | | | | | | | | | | | | | | | | | | | | +- Address of GOTO | | | | | | | | | | +-------- Logical Operator | | | | | | | | | +----------- OR/AND (1/0) | | | | | | | | +------------- Another Clause Flag | | | | | | | +----------------- Comparison Variable | | | | | | +------------------------- C/V compared value. | | | | | +------------------------------- Logical Operator | | | | +---------------------------------- OR/AND (1/0) | | | +------------------------------------ Another Clause Flag | | +---------------------------------------- Comparison Variable | +------------------------------------------------ Value to compare to +------------------------------------------------------ IF token
01110 0000 AAAAAAAAAAA | | | | | +-- Address of the GOSUB | +---------- GOSUB "#" max of 16 (0 - 15) +--------------- GOSUB token
11011 | +------- RETURN token
Note that all of the string stuff is done in the STAMP.EXE program. When a debug statement is hit, it sends its address to the downloader and the stamp program uses that to figure out what to print.
11110 AAAAAAAAAAA | | | +-- Address of this debug token (identifies it) +--------- DEBUG token
10110 CCCVVV VVVVVV CCCVVV F {CCCVVV F} | | | | | | | | | | | | | +--- Another Constant Flag | | | | | +-------- constant/variable in list | | | | +------------- Another Constant Flag | | | +------------------ constant/variable in list | | +------------------------- destination variable | +-------------------------------- const/variable index into list +--------------------------------------- LOOKUP token
01101 CCCVVV VVVVVV CCCVVV F {CCCVVV F} | | | | | | | | | | | | | +--- Another Constant Flag | | | | | +-------- constant/variable in list | | | | +------------- Another Constant Flag | | | +------------------ constant/variable in list | | +------------------------- destination variable | +-------------------------------- const/variable index into list +--------------------------------------- LOOKDOWN token
00111 CCCVVV CCCVVV VVVVVV | | | | | | | +-- variable to receive input | | +--------- desired state | +---------------- pin # to monitor +----------------------- POT token
01000 CCCVVV CCCVVV | | | | | +--------- variable/constant length of pulse | +---------------- variable/constant identifying pin to pulse +---------------------- PULSOUT token
01001 CCCVVV CCCVVV VVVVVV | | | | | | | +-- Variable to get pulse width | | +--------- variable/constant identifying edge (1/0) | +---------------- variable/constant identifying pin to monitor +---------------------- PULSIN token
11100 AAAAAAAAAAA CCCVVV | | | | | +--- constant/variable nap 'units' (0-7 legal) | +------------ address of next statement to execute +--------------------- NAP token
00110 CCCVVV CCCVVV CCCVVV | | | | | | | +-- variable/constant holding number of cycles | | +--------- variable/constant analog level (0 - 255) | +---------------- variable/constant identifying pin (0 - 7) +---------------------- PWM token
10000 VVVVVV | | | +---- Word variable to store random number in +---------- RANDOM token
Note: I believe this command complements the value in the constant/variable so that low addresses in the EEPROM appear as high addresses.
01011 AAAAAAAAAAA CCCVVV VVVVVV | | | | | | | +-- Variable to read into (byte variable) | | +-------- variable/constant EEPROM Address to read from | +------------------ Address of next statement +--------------------------- READ token
Note: I believe this command complements the value in the constant/variable so that low addresses in the EEPROM appear as high addresses.
01100 AAAAAAAAAAA CCCVVV VVVVVV | | | | | | | +-- Variable to read from (byte variable) | | +-------- variable/constant EEPROM Address to write to | +------------------ Address of next statement +--------------------------- WRITE token
Note: Several statements encode the next statement to execute in their bits. I believe these are ones that use the Watchdog timer to wake them up. When woken up, the timer looks at the address that the interpreter has stored somewhere (probably in a register) and continues execution from there.
11101 AAAAAAAAAAA CCCVVV | | | | | +--- constant/variable time in seconds | +------------ address of next statement to execute +--------------------- SLEEP token
10010 CCCVVV CCCVVV CCCVVV F {CCCVVV CCCVVV F} | | | | | | | | | | | | | | | +-- Another Note Flag | | | | | | +------ Note duration | | | | | +-------------- Note number | | | | +------------------- Another Note Flag | | | +------------------------ Variable/constant note duration | | +------------------------------- Variable/constant note number | +-------------------------------------- Variable/constant pin number +-------------------------------------------- SOUND token
10011 CCCVVV CCCVVV {CCCVVV <F> <C>} | | | | | | | | | | | +-- Continuation flag (another const/var) | | | | +------ Format flag (1 = convert to number) | | | +------------ Constant/variable to send. | | +------------------- Constant/variable Serial mode to use. | +-------------------------- Constant/variable Pin number to use +-------------------------------- SEROUT token
This one is really weird, I've done it in three parts to make it more clear.
version 1 = Variables but No Qualifiers
10100 CCCVVV CCCVVV 0 {F vvvvvv V} | | | | | | | | | | | | | +---- More Variables flag (1 = More) | | | | | +--------- Variable | | | | +------------- Format flag, 1 = Number (#) input | | | +---------------- Qualifiers flag, 0 = No Qualifiers | | +--------------------- Constant/Variable Baud rate information | +---------------------------- Constant/Variable Pin to use +---------------------------------- SERIN token
version 2 = Qualifiers but No Variables
10100 CCCVVV CCCVVV 1 CCCVVV AAAAAAAAAAA Q {CCCVVV Q} 0 | | | | | | | | | | | | | | | | | | | +- Variables Present Flag | | | | | | | | +- More Qualifiers flag | | | | | | | +------ Const/Variable Qualifier | | | | | | +--- More Qualifiers Flag | | | | | +-------- Address of the following Flag ?!?! | | | | +------------------ Const/Variable First Qualifier | | | +---------------------- Qualifiers flag, 1 = Qualifiers | | +--------------------------- Const/Variable Baud Rate | +---------------------------------- Constant/Variable Pin to use +---------------------------------------- SERIN token
version 3 = Qualifiers and Variables
10100 CCCVVV CCCVVV 1 CCCVVV AAAAAAAAAAA Q {CCCVVV Q} 1 {F vvvvvv V} | | | | | | | | | | | | | | | | | | | | | | | | | +- More Vars | | | | | | | | | | | +------ Variable | | | | | | | | | | +---------- Var Format | | | | | | | | | +- Variables Present Flag | | | | | | | | +- More Qualifiers flag | | | | | | | +------ Const/Variable Qualifier | | | | | | +--- More Qualifiers Flag | | | | | +-------- Address of the following Flag ?!?! | | | | +------------------ Const/Variable First Qualifier | | | +---------------------- Qualifiers flag, 1 = Qualifiers | | +--------------------------- Const/Variable Baud Rate | +---------------------------------- Constant/Variable Pin to use +---------------------------------------- SERIN token
Token Keyword ===== ========= 00000 HIGH 00001 LOW 00010 INPUT 00011 OUTPUT 00100 TOGGLE 00101 REVERSE 00110 PWM 00111 POT 01000 PULSOUT 01001 PULSIN 01010 FOR 01011 READ 01100 WRITE 01101 LOOKDOWN 01110 GOSUB 01111 PAUSE 10000 RANDOM 10001 BUTTON 10010 SOUND 10011 SEROUT 10100 SERIN 10101 LET 10110 LOOKUP 10111 NEXT 11000 IF/THEN 11001 GOTO 11010 BRANCH 11011 RETURN 11100 NAP 11101 SLEEP 11110 DEBUG 11111 END
file: /Techref/microchip/language/basic/stamp-decode.htm, 34KB, , updated: 2012/7/12 07:35, local time: 2024/8/14 20:43,
3.138.69.110:LOG IN
|
©2024 These pages are served without commercial sponsorship. (No popup ads, etc...).Bandwidth abuse increases hosting cost forcing sponsorship or shutdown. This server aggressively defends against automated copying for any reason including offline viewing, duplication, etc... Please respect this requirement and DO NOT RIP THIS SITE. Questions? <A HREF="http://ecomorder.com/techref/microchip/language/basic/stamp-decode.htm"> Decoding the BASIC Stamp</A> |
Did you find what you needed? |
Welcome to ecomorder.com! |
Ashley Roll has put together a really nice little unit here. Leave off the MAX232 and keep these handy for the few times you need true RS232! |
.