Thursday, May 17, 2007


Redefining it

"The best way to solve a problem is often to redefine it." - Michael Rabin, via Paul Graham
"I don't like assembly language." - Richard Cook

Last August I got a Lego NXT robot, and of course I wanted to program it in Lisp. I got Frank Klassner's RCXLisp code and the NXT programming manual, and made a start, looking at opcodes and twiddling bits, and jump offsets, but it was, well, opcodes and bits, and I just wanted to make the robot do things, and it was a big hurdle, and so I procrastinated, and did other things. All that munging with assembly wasn't that interesting a problem to me, and when (if?) I got some output, and it didn't work, then I'd have to try to decipher the disassembled code, and act like the NXT VM, and "run" it...

The light bulb went off a couple of weeks ago. Other people have done most of the heavy lifting for me - there were compilers for other languages for the NXT. I didn't have to get from Lisp to assembly, I just had to get from Lisp to the intermediary language, which would catch syntax errors and I could look at my program's output without going crazy. After some looking around I decided to go with NXC, which has a C syntax. The advantage of a C syntax instead of, say, Basic or Lua is that I have a pretty good head start. Since Javascript has a syntax that's close to C, I started with parenscript and am modifying it to turn it from a Lisp->Javascript compiler into a Lisp->NXC compiler.

I've made a few changes, enough to let me compile a test program

(defconstant *MYNUM* 100)

(defun runout (offset multiplier)
(-num-out 0 offset (* *MYNUM* multiplier)))

(defthread (main :primary t) ()
(let ((x 0))
(setf x 1)
(runout *LCD_LINE1* x)
(setf x 2)
(runout *LCD_LINE2* x)
(-wait 10000)))


#include "NXCDefs.h"

#define MYNUM 100

sub runout(int
multiplier) {
NumOut(0, offset, MYNUM * multiplier);

task main() {
int x = 0;
x = 1;
runout(LCD_LINE1, x);
x = 2;
runout(LCD_LINE2, x);

which produces

I may pretty it up more when I understand parenscript more, but it compiles and runs and it isn't meant to be read by humans.

The next "architectual/philosophical" decision is how closely to follow the original RCXLisp syntax. That would give me some access to more example programs, but the NXT has more capabilities than the RCX, and there's a Google Summer of Code project "NXTLISP" working on a (I'm assuming) a Lisp->NXT VM compiler, so I may wait to see the syntax for that.

Or not. NXC has a rich set of C-style macros for things like turning on multiple sets of motors that's different than how RCXLisp does it, plus I'm thinking about keeping the language more generic so it can target multiple robot C compilers, such as the First VEX.

I'll post more as I go - the next thing to ponder is the best way to handle typing of NXC variables; int, short, long, byte, bool and string. Other robot C compilers may have other types. I may try Common Lisp type declares, or I may put the types in a list with the variable name, with just the variable name meaning "int".

Labels: , ,

This page is powered by Blogger. Isn't yours?