THE HANDY CRICKET LOGO BYTE CODE INTERPRETER BYTE CODES Each byte code is an 8-bit number between 0 and 255. Most of them correspond directly to a Cricket Logo primitive like ON, OFF, RD etc. A few of them are special opcodes used to flag some of the subsequent bytes as something other than a usual byte code. In CK Direct, the special opcodes are flagged with a % sign. Here are some examples. Note that symbolic names like are used instead of the real code (which for is 49). (run ) ; evaluate this... is a command to turn the current motor on. (Cricket Logo defaults to selecting both motors, so both motor LEDs should turn on.) (run ) turns the motor off again. (run ) tells motor b to turn on and reverse direction (the light should alternate between green and red). Evaluate it several times; each time motor b should reverse its direction. The run macro doesn't do very much. Mostly it converts the opcodes (, , etc.) into numbers on a one-by-one basis, and adds a zero to indicate the end of the command. Check the code for more details. POSTFIX NOTATION The interpreter uses postfix for its inputs (like Forth, Postscript, and HP calculators). Postfix commands follow their inputs, so instead of saying "1 + 1" you'd say "1 1 +". Similarly, "2 + (3 * 4)" becomes "2 3 4 * +". You can interpret postfix notation by scanning a sequence from left to right. Numbers are pushed on a stack. Operations pop their inputs from the stack and push back their results (if any). The exception to this is the special "%" class of opcodes, whose inputs follow their opcodes. NUMBERS Handy Crickets support both 8- and 16-bit numbers. They are represented by either a <%num> special opcode followed by one byte for the number itself, or, for 16-bit numbers, a <%num16> special opcode followed by two bytes for the number itself. A one-byte number allows values from 0 to 255; a two-byte number allows values from 0 to 65535. To demonstrate numbers, we'll turn Motor A on for one second. Cricket Logo timing is done in tenths-of-a-second, so we need to say "onfor 10". Before running the example, if Motor A is already on, reset the cricket by power-cycling it. Please note that you must leave the cricket off for about 5 seconds before turning it back on again for the power-cycling to be effective. The cricket should beep when you turn it back on again; if it does not, turn it off, wait, and try again. (run <%num> 10 ) implements the ONFOR 10 command. LISTS Some primitives, like IF and REPEAT, need lists as inputs. Lists are represented by a <%list> opcode, followed by a length byte, followed by the contents of the list, followed by an <%eol> opcode. The length value includes the contents and the <%eol>, but not the <%list> opcode or the length byte itself. For example, the list [on off rd] would be represented as <%list> 4 <%eol>. Commands that take lists as input use postfix as well. For example, the REPEAT command takes two inputs: the number of times to repeat, and the list to be repeated. The command REPEAT 10 [ONFOR 7 RD] translates to: (run <%num> 10 <%list> 5 <%num> 7 <%eol> ) Before moving on, let's dissect that a little. First, the number 10 is pushed on the stack (<%num> 10); this will be the numeric argument to the REPEAT command. Then the <%list> opcode indicates the beginning of a code list. This is followed by the length of the list (5). Next, the number 7 is pushed on the stack; this becomes the input to the ONFOR command. Next, the RD command is inserted into the code list. The list is terminated by the <%eol> opcode, and then the REPEAT command is executed. (Internally, the code list is not actually pushed on the stack - instead, a pointer to the list is pushed. REPEAT pops this pointer and then the numeric argument.) Here is another example, using the IF statement. IF SWITCHA [ONFOR 5] becomes: (run <%list> 4 <%num> 5 <%eol> ) To test this, plug a switch into sensor A and evaluate the statement. If the switch is held down when the statement is evaluated, then a motor should turn on for half a second. EOLR-LISTS The WAITUNTIL and WHEN primitives take as input lists that are expected to output a boolean. Such lists must be terminated by <%eolr> rather than <%eol>, but are otherwise normal. For example, the following, when run, will cause the cricket to pause until it detects a pressed switch in sensor port A, then turn a motor on for half a second: (run <%list> 2 <%eolr> <%num> 5 ) BACKGROUND PROCESSES Handy Crickets support a single background process, initiated by the opcode and terminated by the opcode. The following, for example, will turn both motors on for five seconds, check for switch A every half second and beep if it's down, and terminate when done: (run <%list> 2 <%eolr> <%list> 2 <%eol> <%num> 10 <%list> 5 <%num> 5 <%eol> ) Alternatively, if the opcode is left out at the end, the cricket will begin checking for switch A continuously after the sequence of ten half-second ONFORs is done, and the red "activity" LED will remain on indefinitely. Press the start button or power-cycle the cricket to reset it in this event. PROCEDURES Cricket Logo includes a mechanism for defining procedures. Procedures take arguments off of the stack. There are no local variables. Procedures may or may not output a value. Procedures can be defined using the "defproc" macro. The macro takes two inputs: the destination address in the cricket's eeprom memory where the procedure is to begin, and the procedure body, which is a list of byte codes to be stored. In the resulting instruction stream, the first byte of the procedure body is the number of inputs to the procedure. This byte is followed by the procedure body code. For example, here is a procedure to select both of the motors and turn them on for one second. It takes no inputs. (defproc #x100 (0 <%num> 10 )) Evaluate this line; the procedure is then loaded into the cricket's memory at address #x100. Nothing visible happens, because the procedure has not actually been run. There is no opcode to run a procedure. Instead, you put the two-byte address of the procedure in the instruction stream, setting the high bit of the first byte (the most-significant-bit, or MSB, of the whole address). (Since the highest address available in the blue-dot eeprom is #xfff (it's #x7ff for red-dots), and this is less than 2^15, the highest of the 16 address bits is free for use in this manner. It turns out the second-highest bit is also free; see the section below on tail recursion for an explanation of its use.) To run the procedure defined above: (run #x81 #x00) When this line is evaluated, the two motor LEDs should light for one second. If you don't see how to get those two values, try converting 100 from hex to binary, change the 16th-highest bit to a one, and convert back to hexadecimal. The defproc macro returns the address at the end of the procedure, i.e. the next available location that you can use for subsequent procedures or code. You are responsible for making sure your procedures don't collide with one another or collide with memory used by the Cricket Logo runtime interpreter (see the memory map below for details). The defproc macro automatically adds the opcode to terminate the procedure without returning an output value. This is necessary to keep the interpreter from executing garbage code after the procedure has ended. The next example shows the translation of (in Logo syntax): to foo repeat 10 [onfor 1 rd] end Here it is: (defproc #x100 (0 <%num> 10 <%list> 5 <%num> 1 <%eol> )) Run the procedure as before: (run #x81 #x00) Procedures can call procedures; use the "set the high bit of the address" format inside the body of a procedure just as you would at the top level. PROCEDURE INPUTS To define a procedure that can take inputs, specify a nonzero number of inputs with the first byte of the procedure body. Within the body, the inputs can be accessed using the <%input> special opcode. A byte following <%input> indicates which input to reference; input 0 is the one that immediately preceded the procedure call, input 1 is the one before that, etc. For example, we will modify the previous "foo" procedure to take as input its argument to the REPEAT command: to foo :x repeat :x [onfor 1 rd] end Here is the translation: (defproc #x100 (1 <%input> 0 <%list> 5 <%num> 1 <%eol> )) After evaluating, run by pushing a number on the stack, followed by a call to invoke the procedure: (run <%num> 12 #x81 #x00) Again, the #x81 #x00 sequence is a procedure call in the "set high bit" format. PROCEDURE OUTPUT Support for procedure output is through the opcode, which takes a single input - the value to be returned - and replaces the opcode at the end of a procedure. You can simply include it at the end of your procedure body code, however, and the opcode that the defproc macro automatically appends will just be ignored. For example: (defproc #x000 (0 <%num> 10 <%num> 5 )) (run #x80 #x00 <%list> 2 <%eol> ) will turn a motor on for one second, then beep five times. TAIL RECURSION Tail recursion is supported by setting the second-highest-bit as well as the high bit when making a procedure call. It is to be used when, and only when, calling the currently running procedure. The stack frame of the currently running procedure - the inputs - is clobbered by the new inputs. IR TRANSMISSION DATA-TAKING Handy Crickets have space for 2500 16-bit numbers set aside in their eeprom memory for recording data over time. (Note: this space is free for the user to use normally if he doesn't plan on recording any data; see the memory map below for details.) The opcode takes no inputs and moves the "data pointer" to the beginning of this space. The opcode takes a single input, a number between 0 and 2500, and moves the pointer to that specific data location. The opcode takes a single input - the value to store, usually from one of the switches or sensors - and writes it to the location currently specified by the data pointer, advancing the pointer by one in the process. The opcode takes no inputs and returns the value at the current data location, also advancing the data pointer by one in the process. The opcode takes and input and erases that may data points by writing zeroes into the data buffer (in other words, is equivalent to TO ERASE :N RESETDP REPEAT :N [RECORD 0] RESETDP END). PRECISION TIMING Support for precision timing is through the and opcodes. The former resets the timer to 0, and the latter returns its current value, a 16-bit number specifying the number of milliseconds that have elapsed since the timer was last reset. It will rollover back to 0 after 65535 milliseconds, or one minute and 5.5 seconds. GLOBAL VARIABLES Handy Crickets support 16 global variables; they are 16-bit numbers and are stored in the cricket's RAM. To work with them, use the and byte codes; the former takes a single 8-bit number as input and returns the value of the corresponding global variable, while the latter writes its second input, a 16-bit number, to the global variable specified by its first input (an 8-bit number). For example: (run <%num> 0 <%num16> 100 20 ) writes the value 25620 into the first global variable. Note that, for all values of x between 0 and 255, "<%num> x" and "<%num16> 0 x" are equivalent as far as the crickets are concerned, so 8-bit numbers can be used when 16-bit ones are called for: (run <%num> 15 <%num> 200 ) writes the value 200 into the 16th (and last) global variable. START BUTTON & IR VECTORS A 16-bit pointer to the beginning of the code to be run when the start button is hit is stored in eeprom memory locations #xff0 and #xff1. The target address itself can be anywhere in memory. This code is also run when the first button on the Interface Cricket is pressed, or, equivalently, when sony ir code 128 (which is usually the 1 key on a remote control) is received. In addition, a second vector is stored in locations #xff2/#xff3, and corresponds to code that will be run when the second interface cricket button is pressed, or when sony code 129 - the 2 key - is received. OTHER TOPICS Support for the Handy Cricket bus port is through the , , , , , and opcodes. Please check the code for more details. User-defined arrays store 16-bit numbers and are built up from location #x000 in the cricket's eeprom memory. Support is through the and opcodes; please check the code for more details. BLUE-DOT EEPROM MEMORY MAP #x000-#x4ff (user code) 1280 bytes #x500-#xec3 ( data) 2500 bytes #xec4-#xfef (user code) 300 bytes #xff0/#xff1 (button #1 ptr) 2 bytes #xff2/#xff3 (button #2 ptr) 2 bytes #xff4-#xffe (cricket name) 11 bytes #xfff (autostart flag) 1 bytes KEY DIFFERENCES FROM RED-DOT CRICKETS * Support for the opcode and 16-bit numbers. * Support for the and opcodes and background processes. * Support for the opcode. * Support for 2500 16-bit numbers, rather than 250 8-bit numbers, for use with the data-taking primitives. * Support for the opcode. * The opcode returns a 16-bit number (rather than an 8-bit one whose value, multiplied by 4, gave a millisecond value between 0 and 1000). * There are 16 global variables instead of 224, they are 16-bit numbers instead of 8-bit ones, and they are stored in the cricket's RAM instead of its EEPROM (and hence will not retain their values between power-cyclings). * Support for the new bus port. * Support for user-defined arrays. ---- change log: Wed May 22 09:36:16 2002 fredm corrected definition of primitive. clarified procedure definition.