Grobots - Documentation - Language

Grobots are programmed in a simple stack-based language derived from Forth. This imitates RoboWar, but the language itself is much closer to Forth. These stack-based languages are good for this sort of application because they are very easy to implement, and easier to learn and use than other similarly low-level languages.

The Grobots language doesn't have a name yet; I will call it Grobocode here for lack of a better name. If you are used to RoboTalk, you will find it pleasantly powerful. If you are used to Forth, you will find it annoyingly underpowered. If you don't know any stack-based languages, you will just find it weird.

Introduction

Like all Forth dialects, keeps intermediate results of calculations on a stack: operands are pushed onto the stack, and operators take their arguments from the stack. Expressions are written in postfix order - the operator comes after all its arguments. This may be unfamiliar, but fortunately it doesn't take long to get used to. What in a conventional language would be:

gamma = 1 / sqrt(1 - (v / c)^2)
is in Grobots:
1 v c / square - sqrt reciprocal gamma!

Here's how it works. Start with an empty stack, and execute the first instruction, 1, which pushes a value on the stack.

stack: 1
  Then v, which pushes the value of that variable:
stack: 1 201285
  Then c:
stack: 1 201285 299780
  / takes the top two arguments and leaves their quotient:
stack: 1 0.6714
  square takes one argument
stack: 1 0.45
  -
stack: 0.55
  sqrt
stack: 0.7416
  reciprocal
stack: 1.35
  gamma! stores the value into the variable of that name.
(empty stack)

Try clicking on a robot and opening the Debugger window. Then pause the simulation and give the Step Brain command to slowly advance the brain. Watch the stack change.

There are instructions to operate on the stack, so you can do fancier things like using a result more than once, or shuffling things to be in the right order:

fibonacci: ;n -- f_n
  dup 1 <= if drop 1 return then
  dup 1 - fibonacci swap 2 - fibonacci +
return

See if you can understand how that works. (You'll need to look up a lot of the operators, and this may be confusing to trace since it's recursive.) n -- f_n is a stack diagram, saying the newly defined fibonacci operator takes one argument (n) and returns one result (f_n).

The most confusing errors are stack underflow and overflow. Underflow means means you tried to remove more values from the stack than were actually there. For example, 2 + will probably underflow, because + needs two arguments. Overflow means you kept leaving values on the stack, so they piled higher and higher until the interpreter gave up.

Model details

Grobocode runs on a two-stack machine, like traditional Forth. There is a return stack, which is only used for return addresses, and a data stack, used for arguments and return values of operators.

The only data type is a 32-bit fixed-point number, with 12 bits of fraction (and 1 of sign and 19 of integer). The range is about +/- 524,288, and the precision 1/4096 (.00024). Integer values may be used as addresses.

Syntax

Grobocode syntax is a sequence of words separated by white space. There are several kinds of words; suffixes are sometimes used to indicate which kind. Each word (except compile-time words and label declarations) compiles to one instruction.

Kind of wordSuffix
Primitive callnone
Read variable (including constants, vector variables, and hardware variables)none
Write variable!
Read label (put its address on the stack)&
Call label^ (optional)
Declare label:
Immediate numbernone

Semicolon makes a comment to the end of the line. This works everywhere, not just in code.

Variables and constants are defined somewhat clumsily by leaving the language and using reader tags. #var name initial-value defines a variable, #vector name initial-x initial-y defines a vector variable, and #const name value defines a constant.

Primitive Operators

Hardware access words are not included here, but are listed on the hardware page.

WordStack diagramComments
nop--Does nothing, of course. Used for padding in jump tables.
Stack manipulation
dropa --Discard the top item on the stack.
2dropa b --Discard the top two items.
nipa b -- bDiscard the second item on the stack.
rdropR: a --Discard the top item on the return stack.
dropn xn ... x2 x1 n -- remove n items from the stack.
swapa b -- b aExchange the top two items.
2swapa b c d -- c d a bExchange the top two pairs of items.
rota b c -- b c a
rrota b c -- c a b
dupa -- a a These all duplicate items that are on the stack.
2dupa b -- a b a b
tucka b -- b a b
overa b -- a b a
2overa b c d -- a b c d a b
stack-- heightReturn the number of items that were on the stack.
stack-limit-- limitReturn the maximum number of items that can be on the stack.
pick xn ... x2 x1 n -- xn ... x2 x1 xncopy the nth item to the top of the stack.
>ra -- R: -- a Move an item to or from the return stack. The item must be a valid address, so these instructions are not very useful.
r>-- a R: a --
Branches
jumpaddress --Continue execution from address.
calladdress -- R: -- pcPush the address of the next instruction on the return stack, and continue execution from address.
returnR: address --Remove address from the return stack and continue from it.
ifgflag address --Jump to address if flag is nonzero.
nifgflag address --Jump to address if flag is zero.
ifegflag addr1 addr2 --Jump to addr1 if flag is nonzero or to addr2 if it is zero.
ifcflag address -- [R: -- pc]Call address if flag is nonzero.
ifecflag addr1 addr2 -- R: -- pc
nifcflag address -- [R: -- pc]Call address if flag is zero.
ifrflag -- [R: address --]Returns (to the top address on the return stack) if flag is nonzero.
nifrflag -- [R: address --]Returns if flag is zero.
Arithmetic etc.
+a b -- a+b
-a b -- a-b
negatea -- -a
*a b -- a*b
/a b -- a/b
reciprocala -- 1/a
moda b -- r
rema b -- r
squarea -- a^2square: dup * return
sqrta -- ssquare root
exponentb x -- b^x
is-integera -- flag
floora -- nRounds down.
ceilinga -- nRounds up.
rounda -- nRounds to nearest.
mina b -- a|bReturns the lesser (closer to negative infinity) of the arguments.
maxa b -- a|bReturns the greater (closer to positive infinity) of the arguments.
absa -- bb = |a|
signuma -- bb = 1 if a positive, -1 if negative, 0 otherwise.
reorienta -- bEnsures an angle is in (-pi, pi].
sina -- sina
cosa -- cosa
tana -- tana
arcsinsina -- a
arccoscosa -- a
arctantana -- a
arctan2x y -- a
randommin max -- valueReturns a random real value evenly distributed between the two bounds (inclusive).
random-angle-- angleReturns a random value evenly distributed in (-pi, pi].
random-intmin max -- valueReturns a random integer evenly distributed between the two bounds (inclusive). The bounds need not be integers, but the result will be; 1.5 2.5 random-int returns 2.
random-boolprobability -- booleanReturns a boolean, which will be 1 with the given probability.
pi-- pi
2pi-- 2pi
pi/2-- pi/2
e-- e2.7182818284
epsilon-- epsilonReturns the smallest difference representable.
infinity-- infReturns the largest positive value representable. (Negative infinity is actually infinity negate epsilon -.)
Vector operations
rect-to-polarx y -- magnitude angleConvert a vector from rectangular to polar form.
polar-to-rectmagnitude angle -- x yConvert a vector from polar to rectangular form.
v+x1 y1 x2 y2 -- x1+x2 y1+y2Adds two vectors in rectangular form.
v-x1 y1 x2 y2 -- x1-x2 y1-y2Subtracts two vectors in rectangular form.
vnegatex y -- -x -yNegates a vector.
vs*x y s -- x*s y*sVector-scalar multiply.
vs/x y s -- x/s y/sVector-scalar divide.
normx y -- pReturns x square y square + sqrt, the Pythagorean sum.
anglex y -- angleReturns arctan2(y, x).
dotx1 y1 x2 y2 -- vDot product. v = x1 x2 * y1 y2 * +
projectx1 y1 x2 y2 -- x3 y3Returns the projection of the first vector onto the second.
crossx1 y1 x2 y2 -- vTwo-dimensional cross product, sort of. v = x1 y2 * y1 x2 * -. This is the z-component of the equivalent three-dimensional cross product.
unitizex y -- x' y'Returns a vector of magnitude 1 in the same direction.
distx1 y1 x2 y2 -- dDistance (norm of difference) operator.
in-rangex1 y1 x2 y2 r-- flagwhether the two vectors differ by less than r.
Comparisons and Boolean operations
=a b -- flag
<>a b -- flag
<a b -- flag
>a b -- flag
<=a b -- flag
>=a b -- flag
notf1 -- f2f2 is zero if f1 was nonzero, and one otherwise.
andf1 f2 -- f3f3 is one if both f1 and f2 were nonzero, and zero otherwise.
orf1 f2 -- f3f3 is zero if both f1 and f2 were zero, and one otherwise.
xorf1 f2 -- f3f3 is one if exactly one of f1 or f2 was non-zero, and zero otherwise.
nandf1 f2 -- f3f3 is zero if both f1 and f2 were nonzero, and one otherwise.
norf1 f2 -- f3f3 is one if both f1 and f2 were zero, and zero otherwise.
ifevflag a b -- a|bValue conditional: returns a if flag was nonzero, and b otherwise.
Miscellaneous
printvalue --Remove and display one item, for debugging purposes.
vprintx y --Remove and display a vector.
beep--Beep, for debugging purposes.
pause--Pauses the simulation. For debugging.
stop--Permanently stops execution of this brain.
sync--Waits until the next frame to continue execution. Useful for waiting for hardware to work, or for synchronizing to do something all in one frame.
storex addr --Writes a value to local memory.
loadaddr -- xReads local memory.
vstorex y addr --A convenience, to store two values.
vloadaddr -- x y

Compile-Time Words

Some words execute at compile time, for convenience in writing common control structures. There are conditionals and loops so far.

StructureMeaningExpansion
test if body then execute body if test is true test label& nifg body label:
test if body1 else body2 then execute body1 if test is true, body2 otherwise test skip& nifg body1 done& jump
skip: body2 done:
nif ... If, with sense reversed test label& ifg ...
test1 if test2 and-if body1 else body2 then if test1 is true, then if test2 is true, then execute body1... test skip& nifg test2 skip& nifg body1 done& jump
skip: body2 done:
test1 if body1 else test2 if body2 celse test3 if body3 celse body4 then Makes if-elsif-elsif-else constructs shorter (no need to write then then then). test1 skip1& nifg body1 done& jump
skip1: test2 skip2& nifg body2 done& jump
skip2: test3 skip3& nifg body3 done& jump
skip3: body4 done:
do code forever infinite loop repeat: code repeat& jump
do test while code loop Execute test and code until test is true (until) or false (while). Note that the end-test is in the middle of the loop. repeat: test done& nifg
code repeat& jump done:
do test until code loop repeat: test done& ifg
code repeat& jump done:
do code test while-loop optimized versions of while loop and until loop, saving two instructions repeat: code test repeat& ifg
do code test until-loop repeat: code test repeat& nifg

Grobots by Devon Schudy (dschudy@yahoo.com) and Warren Schudy (wschudy@wpi.edu)