Advanced Undergraduate Project
Justin Kent
Fall 99
Fred Martin, Advisor
Epistemology & Learning, MIT Media Lab

 

For years, little and big kids alike have used Lego sets to realize castles, cars, cityscapes, alien ships, battle scenes, and anything else their imaginations could muster. Legos have evolved substantially over that time, from the early sets of a few different interconnecting blocks to sets involving simple tools like wheels and axles, and on into more complicated realms such as hydraulics and electronics. Today’s most advanced inception of the Lego block is the Lego MindStorm set, which includes motors, sensors, and a PC software-interfacing programmable brick, allowing kids to create autonomous, interactive entities, such as intelligent vehicles, robots, or machines. This system is currently under further development, and it was my job to redesign the software for the Nintendo Gameboy platform.

The Lego MindStorm technology was developed by the MIT Media Lab’s Epistemology & Learning group. One of the ideas that the E&L group is based around is that by giving children the proper toys, one can stimulate them to learn more effectively; much of E&L’s research focuses on trying to develop such toys. The MindStorms are surely targeted at a new generation of youth, one that will have to digest and eventually apply volumes more information than kids who were born even twenty years ago. Legos have always provoked a child’s imagination and design instinct within a very rigid, analytical framework of rules. As the rules of our world have changed, Lego blocks have evolved to reflect the new system of thinking and doing. Today’s brands of creativity and technology have mutated and blended into new forms; MindStorms are both a proclamation of this and a drive behind it.

At the core of the MindStorm system is an RCX Programmable Brick; it has the appearance of a large, foreign Lego block, with areas for stacking on block sensors and motors, as well as an LCD status screen and an infrared port. Users upload programs to the block from their pc via iR, over which they can also download data that they program the RCX brick to collect. A budding engineer writes programs for MindStorms on their PC in Logo Blocks, a graphical derivative of the Logo programming language. Logo is often used to teach young children in a classroom setting; Logo Blocks is simple and intuitive enough for even a 6 year old to use, while being powerful enough for an adult to produce interesting, non-trivial systems. Computers, however, can be intimidating to youngsters and are usually not portable – these two problems are what the project aims to address.

Logo Blocks is a visual representation of Lego Logo, a superset of Logo also developed by the Epistemology & Learning group. Lego Logo is a full-featured 2nd generation programming language, which includes conditionals, loops, variables, and functions. It also has pointers to the Lego hardware, such as MotorA or Sensor2. Internally, a program in this language is converted to byte code before being transferred to the RCX brick, where it is in turn stored in and interpreted by a PIC. The goal of this project was to emulate all parts of this system up to and including the infrared transfer, on a more portable and child friendly platform. Due to its compact size and widespread popularity among children, the Nintendo Gameboy was chosen. More specifically, the new Gameboy Color was selected, as it has a built in infrared port, and a clock doubling mode which would later help us meet the infrared timing constraints of the brick.

Programming for the Gameboy took a bit of initial research and preparation. Since we were not a licensed Nintendo developer, we could not obtain any of the legitimate programming tools, such as the Gameboy development platform, cartridges, or a cartridge programmer. Instead, we surfed abroad to Hong Kong, where we found a company called Bung that made unauthorized cartridges and programmers. For less than $100, we ordered enough to get started, saving us possibly months that it would have taken to manufacture our own. With hardware no longer a problem, we began searching for a software solution. After examining many different packages, the Gameboy Development Kit (GBDK, not authorized by or associated with Nintendo) and No$gmb (No Cash Gameboy) emulator were chosen. Both were freeware, and together they provided everything needed to write and test custom gameboy applications. A game could be written using C, assembly, or both, then compiled using GBDK. The resulting image file could be tested on the computer using Nocash, or downloaded to a cartridge for actual Gameboy play using Bung’s parallel port GB Xchanger and the accompanying GBX software. This last step was necessary for testing any transfer operations, as Nocash had no way of emulating the infrared port. For developing tiles, a program called Gameboy Tile Designer was used, which allowed for the design and export of tiles and palettes. With all the development tools in place, it was time to move on to the actual development.

To more fully understand the capabilities and constraints of the gameboy, a few simple demo programs were developed while consulting the sparse reference material available on the web. Gameboy Colors sport a modified Z80 processor, similar to the type found in many graphing calculators; it has a clock speed of 1.05 MHz, which can be doubled at the cost of increased power consumption. The graphics system is based on 8x8 pixel tiles filling up the Gameboy’s 160x184 pixel screen, with each pixel capable of displaying 32 gradations of color for red, green, and blue. There is a 256x256 pixel background image, which can be scrolled to produce the illusion of movement. A single resizable window can be layered on top of this, as well as up to 40 sprites, which can be one tile wide and either one or two tiles high. Each tile also has an associated 4 color palette.

The design of the actual program borrows a general layout feel from Logo Blocks: pieces are arranged and linked on screen to form a program. The main screen consists of a 16x16 scrollable square grid, of which maximum 10x11.5 are visible. A palette containing program blocks and status information slides in and out on demand, covering the rightmost portion of the screen. Each grid square accommodates a program block. At the upper left hand corner is a start block, which is the beginning of program flow; a program begins execution here and continues downward, line by line. At the top middle is a watch block, which allows for one additional program thread to await execution until a specific condition, often involving sensor values, is met; the conditional sits to the right of the watch block and the thread is stacked underneath it. On the palette are the program blocks, roughly separated into motors and their descriptors, sensors and their descriptors, program flow, and user defined procedures. If a program block requires a numerical value when dropped, this is prompted for and accepted in the palette status area. The grid is represented internally by a 2-dimensional array with a very strong rep invariant, dictating which blocks can be placed next to each other. This results in a user only being able to place blocks in configurations that make sense and represent a well-formed program; if a block is selected then dropped in an area where it shouldn’t fit, the application beeps and will not drop the block. The syntax for a command line is very loose, and allows for many different permutations. This is handled by the candrop() function by examining what is around each individual block. Here is a typical piece of one of its switch statements:

case MOTOR:

if ((top==START|top==WATCH|top==WAIT|top==MOTOR|top==IFHOLDER|top==ELSEHOLDER|
top==LOOPHOLDER|top==NOTE|topleft==IF|left==ELSE|left==LOOP|topleft==LOOP)&!(top==IF&topleft==LOOP)&under==0)

ok=1;
break;

case DARK:

if (left==LIGHTSENSOR&under==0)

ok=1;
break;

A motor can be placed almost anywhere in a program, but it can’t be placed in the middle of the screen detached from everything else, and it can’t be placed on top of another block. A dark block, on the other hand, can only be placed to the right of a lightsensor.

To create a program, the user selects a block from the palette using button A, then drops it in its place also using A. The B button erases blocks, and the Start button downloads a program to the brick. For larger programs, the user must use the grid cursor to scroll around the grid and see all parts of it. Great efforts were taken to make the design as intuitive and easy to use as possible, in order to make the application accessible to the widest range of people. The artwork is targeted at young children, and when possible, is extremely literal. All application code was developed in C.

The application code is split into different modules, as cartridge memory is separated into 8k banks. Bank 0, the home of the main program loop, must therefore always be in memory, while other banks are swapped in and out depending on what needs to be done at that moment. This is accomplished by having all functions reside in bank 0, but within many functions having the commands to switch banks, call a helper function, then switch back. This also gives the result of making the system much more easily expandable – an arbitrary number of additional memory banks can be added to a cartridge. As an example of bank switching, examine the programs initialization functions. They are only needed on power up, so they are located in bank 2, which is not loaded again after these functions are called. Here are their function definitions:

void Init()
{
SWITCH_ROM_MBC1(2);//switch to bank 2
Init_bank2();
SWITCH_ROM_MBC1(1);//switch back to bank 1
}

void InitScreen()
{
SWITCH_ROM_MBC1(2);
InitScreen_bank2();
SWITCH_ROM_MBC1(1);
}

The definitions of Init_bank2()and InitScreen_bank2()can be found in Appendix A, INIT.C, and are placed in bank 2 at compile time by using the –bo flag.

lcc -Wa-l -Wf-bo2 -c -o init.o init.c

Functions not located in bank 0 should not attempt to switch banks, as they will be switched out of memory resulting in a fatal error.

Turning to the hardware side of things, we realized that before assessing the infrared capabilities of the Gameboy, we had to first ascertain the timing requirements of the RCX brick. By looking at assembly code and using an oscilloscope hooked up to an iR receiver, we were able to grab a picture of what a transmitted byte was supposed to look like, then match our Gameboy transmitted bytes to that template. A transmitted byte consists of a pre-start bit followed by a 40 ms pause, a start bit, the 8 bit byte, and a stop bit. The Gameboy clock speed was not quite fast enough to enable iR transfers to the brick, but by doubling the processor speed at times when information was being sent we gained more than enough speed to get the signal right.

SetDoubleSpeed:
ldh a,(0x00) ;get joypad state
ld e,a
ld a, #0x30
ldh (0x00),a ;don't let joypad interfere
ld a, #0x01
ldh (0x4d),a ;setting speed bit to 1, then
stop ;issuing the stop command doubles speed

ld a,e

ldh (0x00),a ;reset joypad Because of the time sensitive nature of the transfers, the send routines were coded in assembly, then linked into the rest of the program as procedures.

SendPreStartBit:
ld a, #0x01
ldh (0x56),a ;loading a 1 into iR bit
ld a, #0x00 ;followed by loading a 0 sends a 1 bit
ldh (0x56),a

Pause:
dec b ;b is defined elsewhere, and was tweaked
jp nz,Pause ;to make the pause 40ms

SendStartBit:
ld a, #0x01 ;the start bit is sent
ldh (0x56),a
ld a, #0x00
ldh (0x56),a
ld a, #0x00 ;timing correction
ldh (0x56),a

Send1:
dec d ;for each bit in the byte
jp z,SendStopBit
rrc c
nop ;nops are inserted to tweak the timing
jp nc,Send0
ld a, #0x01
ldh (0x56),a
ld a, #0x00
ldh (0x56),a
nop
jp Send1

Send0:
ld a, #0x00
ldh (0x56),a
ld a, #0x00
ldh (0x56),a
jp Send1

SendStopBit:
ld a, #0x00 ;finish off with the stop bit
ldh (0x56),a
nop
ld a, #0x01
ldh (0x56),a
ld a, #0x00
ldh (0x56),a

One problem arose from each on bit’s pulse occasionally not being wide enough to register on the receiving end; this had to do with an omission in the Nintendo spec regarding the reset timing of the iR bit, and was resolved by changing the loop order and therefore the bit toggling timing.

When a program is about to be sent to the RCX brick, the main application first goes through and compiles the program into byte code. This is currently done by going through the program iteratively as specified by the program flow model, and examining each block’s context. A simpler and more elegant method would be to do this recursively, a definite must for a newer version. Next the RCX’s memory pointer is set to a specific place in memory, and the list of bytes is sent. Another special memory location has to be filled with pointers to the starting address of the uploaded program; when the brick’s execute button is pressed, the location pointer is set to this address.

The system that Lego MindStorms was based on has been evolving since its development. The cricket, a smaller, less powerful controller, has replaced the RCX brick. When multiple crickets are grouped together, however, they have the ability to communicate with each other, making for some potentially very sophisticated systems. Swarms of independent crickets can exhibit group behavior, or can work together as part of one entity. All Gameboy development for this project was actually targeted at and tested with crickets, rather than bricks. MindStorms come with motors, touch sensors, and light sensors, but more devices are currently under development: servos, temperature sensors, led displays, even a digital camera. Someday a resourceful youth could create a lego face recognition system or a mobile reconnaissance camera. But right now, MindStorm and its relatives are already providing eager children with a playground of today’s most powerful technology.

 

Bibliography:

Epistemology & Learning Group, MIT Media Lab

http://el.www.media.mit.edu/groups/el/

The MIT Programmable Brick Project

http://el.www.media.mit.edu/groups/el/projects/programmable-brick/

Crickets: Tiny Computers for Big Ideas

http://fredm.www.media.mit.edu/people/fredm/projects/cricket/

Gameboy Development Kit

http://gbdk.sourceforge.net/

Gameboy Tile Designer
http://www.casema.net/~hpmulder/

Gameboy Development FAQs

http://pc1-archbo.bot.unibas.ch/~lukas/GBprojects/gbFAQ.html

Jeff Frohwein’s GameBoy Tech Page

http://fly.hiwaay.net/~jfrohwei/gameboy/home.html

NoCash

http://www.work.de/nocash/

Bung Enterprises

http://www.bung.com.hk/

 

 

 

Appendix A

Program Flow

(GBDK and sound function calls excluded for clarity)




















Appendix B

GBBlocks Code

GBBLOCKS.C

unsigned int currenttile3;

unsigned int currenttile4;

long grid[16][16];

int palpos;

int palcursorpos;

unsigned int palcursorypos;

int currentrunrow;

int currentwatchrow;

long numnum;

int pownum;

int xmit_buf_x=20;

int xmit_buf_y=64;

unsigned int xmit_buf[20][64]; //1280, compiler won't allow arrays with index>128,

//so using multidimensional array as array

void Init()
/initializes globals, etc.
{
SWITCH_ROM_MBC1(2);
Init_bank2();
SWITCH_ROM_MBC1(1);

}

void InitScreen()
/initializes screen
{
SWITCH_ROM_MBC1(2);
InitScreen_bank2();
SWITCH_ROM_MBC1(1);
}

void change_object(int num, unsigned int tile)

//abstraction for changing sprites

{

unsigned int tile2;

if (tile==LIGHTSENSOR1)

{

tile = LIGHTSENSOR;

tile2 = L1;

}

else if (tile==LIGHTSENSOR2)

{

tile = LIGHTSENSOR;

tile2 = L2;

}

else if (tile==LIGHTSENSOR3)

{

tile = LIGHTSENSOR;

tile2 = L3;

}

else if (tile==TOUCHSENSOR1)

{

tile = TOUCHSENSOR;

tile2 = L1;

}

else if (tile==TOUCHSENSOR2)

{

tile = TOUCHSENSOR;

tile2 = L2;

}

else if (tile==TOUCHSENSOR3)

{

tile = TOUCHSENSOR;

tile2 = L3;

}

else if (tile==PROC1)

{

tile = PROC;

tile2 = L1;

}

else if (tile==PROC2)

{

tile = PROC;

tile2 = L2;

}

else if (tile==PROC3)

{

tile = PROC;

tile2 = L3;

}

else

{

tile2 = tile + 2;

}

set_sprite_tile(num,tile);

set_sprite_tile(num+1,tile2);

set_sprite_prop(num,tilepalmap[tile/4]);

set_sprite_prop(num+1,tilepalmap[tile/4]);

}

void move_object(int num, unsigned int x, unsigned int y)

//abstraction for moving blocks

{

move_sprite(num,x,y);

move_sprite(num+1,x+8,y);

}

 

 

void flipmode(int newmode)

//handles mode switching for dropping, selecting, etc.

{

if ((gmode==BLOCKMODE & newmode==SELMODE)|(gmode==DROPMODE & newmode==SELMODE))

{

change_object(GRIDCURSOR,BLANK);

change_object(PALCURSOR,SELARROW);

}

else if (gmode==SELMODE & newmode==BLOCKMODE)

{

change_object(PALCURSOR,BLANK);

change_object(GRIDCURSOR,CROSSHAIR);

}

else if (gmode==DROPMODE & newmode==BLOCKMODE)

{

change_object(GRIDCURSOR,CROSSHAIR);

}

else if (gmode==SELMODE & newmode==DROPMODE)

{

change_object(PALCURSOR,BLANK);

change_object(GRIDCURSOR,currenttile);

}

else if (gmode==DROPMODE & newmode==NUMMODE)

{

move_object(PALCURSOR,PALCURSORXPOS1,PALCURSORYPOS2);

change_object(PALCURSOR,SELARROW);

numnum = 10;

display_num(numnum);

}

else if ((gmode==NUMMODE|gmode==POWMODE) & newmode==SELMODE)

{

set_win_tiles(1,PALTITLEPOS,4,4,&frameblank[0]);

move_object(PALCURSOR,PALCURSORXPOS1,PALCURSORYPOS1);

palcursorpos=ACTIONTILES;

palcursorypos = PALCURSORYPOS1;

}

else if (gmode==DROPMODE & newmode==POWMODE)

{

move_object(PALCURSOR,PALCURSORXPOS1,PALCURSORYPOS2);

change_object(PALCURSOR,SELARROW);

pownum = 5;

display_pow(pownum);

}

gmode=newmode;

wait_vbl_done();

wait_vbl_done();

wait_vbl_done();

wait_vbl_done();

}

unsigned int top_byte(long n)

//grab top byte of number for iR transmission

{

unsigned int top;

top = n/0x100U;

return(top);

}

unsigned int bot_byte(long n)

//grab bottom byte of number for iR transmission

{

unsigned int top;

top = n%0x100U;

return(top);

}

void output_short_num(unsigned int numa,unsigned int xp, unsigned int yp)

//output short number

{

int tens,ones;

tens = numa/0x0aU;

ones = numa%0x0aU;

set_win_tiles(xp+0,yp,1,1,&framenummap[tens]);

set_win_tiles(xp+1,yp,1,1,&framenummap[ones]);

}

void display_num(unsigned int dnum)

//number entry prompt

{

SWITCH_ROM_MBC1(2);

output_num(dnum,1,PALNUMPOS);

SWITCH_ROM_MBC1(1);

RClick_Sound();

set_win_tiles(1,PALTITLEPOS,1,1,&frameletmap[LETN]);

set_win_tiles(2,PALTITLEPOS,1,1,&frameletmap[LETU]);

set_win_tiles(3,PALTITLEPOS,1,1,&frameletmap[LETM]);

}

void display_pow(unsigned int dnum)

//power level entry prompt

{

output_short_num(dnum,1,PALNUMPOS);

set_win_tiles(1,PALTITLEPOS,1,1,&frameletmap[LETP]);

set_win_tiles(2,PALTITLEPOS,1,1,&frameletmap[LETO]);

set_win_tiles(3,PALTITLEPOS,1,1,&frameletmap[LETW]);

}

unsigned int furthest()

//returns occupied grid spaces for scrolling purposes

{

int i,j,maxi,maxj;

maxi=0;

maxj=0;

for (i=0;i!=16;i++)

{

for (j=0;j!=16;j++)

{

if (grid[i][j]!=0)

{

if (i>maxi)

maxi=i;

if (j>maxj)

maxj=j;

}

}

}

return(16*maxi+maxj);

}

unsigned int sensorconv(long sensor)

//converts specific sensor to sensor type

{

unsigned int retv;

retv=sensor;

if (sensor==LIGHTSENSOR1|sensor==LIGHTSENSOR2|sensor==LIGHTSENSOR3)

retv=LIGHTSENSOR;

else if (sensor==TOUCHSENSOR1|sensor==TOUCHSENSOR2|sensor==TOUCHSENSOR3)

retv=TOUCHSENSOR;

else if (sensor==PROC1|sensor==PROC2|sensor==PROC3)

retv=PROC;

else if (sensor>=NUMBERVALUE&sensor<POWERVALUE)

retv=NUMBER;

else if (sensor>=POWERVALUE)

retv=POWER;

output_num(retv,1,14);

return(retv);

}

void main()

//main program loop

{

UBYTE joytemp;

int i, at, bt, incbyte, index1, index2, index3, index4, maxsel1,maxsel2, maxsel3, maxsel4;

int flip = 0;

unsigned int sel1[] = {MOTOR,A,B,C,FORWARD,BACKWARD,REVERSE,POWER,STOP,NUMBER};

unsigned int sel2[] = {TOUCHSENSOR1,TOUCHSENSOR2,TOUCHSENSOR3, LIGHTSENSOR1, LIGHTSENSOR2,LIGHTSENSOR3,ON,OFF,DARK,LIGHT};

unsigned int sel3[] = {IF,ELSE,LOOP,WAIT,NUMBER,NOTE};

unsigned int sel4[] = {PROC1,PROC2,PROC3};

unsigned int xtile,ytile,tile;

maxsel1=9;

maxsel2=9;

maxsel3=5;

maxsel4=2;

at=0;

bt=2;

incbyte=0;

index1=0;

index2=0;

index3=0;

index4=0;

Init();

InitScreen();

Init_Sound();

set_sprite_prop(GRIDCURSOR,GRAY);

set_sprite_prop(GRIDCURSOR+1,GRAY);

set_sprite_prop(PALCURSOR,ORANGE);

set_sprite_prop(PALCURSOR+1,ORANGE);

set_sprite_prop(TILESEL1,RED);

set_sprite_prop(TILESEL1+1,RED);

set_sprite_prop(TILESEL2,RED);

set_sprite_prop(TILESEL2+1,RED);

set_sprite_prop(TILESEL3,RED);

set_sprite_prop(TILESEL3+1,RED);

set_sprite_prop(PROCSEL,RED);

set_sprite_prop(PROCSEL+1,RED);

change_object(GRIDCURSOR,CROSSHAIR);

change_object(TILESEL1,currenttile1);

change_object(TILESEL2,currenttile2);

change_object(TILESEL3,currenttile3);

change_object(PROCSEL,currenttile4);

change_object(PALCURSOR,SELARROW);

move_object(TILESEL1,TILESEL1XPOS,TILESEL1YPOS);

move_object(TILESEL2,TILESEL2XPOS,TILESEL2YPOS);

move_object(TILESEL3,TILESEL3XPOS,TILESEL3YPOS);

move_object(PROCSEL,PROCSELXPOS,PROCSELYPOS);

move_object(PALCURSOR,PALCURSORXPOS1,PALCURSORYPOS1);

SHOW_SPRITES;

while (1)

{

xtile = gridcursorxpos; //

xtile = xtile/8; //

xtile = xtile + 2*scrolledx; //

xtile = xtile-1; //

xtile = xtile/2; //neccessary because

ytile = gridcursorypos; //of stack overflows

ytile = ytile/8; //

ytile = ytile + 2*scrolledy; //

ytile = ytile-1; //

ytile = ytile/2; //

tile = sensorconv(grid[xtile][ytile]);

for(i = 0; i < 6; i++)

wait_vbl_done();

if (gmode==BLOCKMODE | gmode==DROPMODE)

{

if (gmode==BLOCKMODE)

{

if (flip!=3)

flip++;

else if(get_sprite_prop(GRIDCURSOR)==0)

{

set_sprite_prop(GRIDCURSOR,S_FLIPY);

set_sprite_prop(GRIDCURSOR+1,S_FLIPY);

flip=0;

}

else

{

set_sprite_prop(GRIDCURSOR,0);

set_sprite_prop(GRIDCURSOR+1,0);

flip=0;

}

set_win_tiles(1,PALTITLEPOS,4,4,&frameblank[0]);

if (tile==NUMBER)

display_num(grid[xtile][ytile]-NUMBERVALUE);

else if (tile==POWER)

display_pow(grid[xtile][ytile]-POWERVALUE);

}

joytemp = joypad();

if (joytemp & J_UP)

gridcursorypos -= 16;

if (joytemp & J_DOWN)

gridcursorypos += 16;

if (joytemp & J_RIGHT)

gridcursorxpos += 16;

if (joytemp & J_LEFT)

{

if (gridcursorxpos>MINWNDPOSX+8)

gridcursorxpos -= 16;

else

gridcursorxpos -= 3;

}

if (joytemp & J_A)

{

if (gmode==DROPMODE)

{

if (candrop())

{

dropblock();

RClick_Sound();

if (currenttile==NUMBER)

flipmode(NUMMODE);

else if (currenttile==POWER)

flipmode(POWMODE);

else

flipmode(BLOCKMODE);

}

}

else if (gmode==BLOCKMODE)

{

output_num(sensorconv(grid[xtile][ytile]),2,12);

}

}

if (joytemp & J_B)

{

if (gmode==BLOCKMODE)

{

eraseblock();

Clear_Sound(0);

}

else if (gmode==DROPMODE)

{

flipmode(BLOCKMODE);

}

}

if (joytemp & J_SELECT)

{

flipmode(SELMODE);

}

if (joytemp & J_START)

{

StartSend(incbyte++);

wait_vbl_done();

}

if (gridcursorxpos > MAXSPRITEPOSX - palpos*3*16)

{

gridcursorxpos = MAXSPRITEPOSX - palpos*3*16;

scroll(RIGHT);

}

if (gridcursorypos > MAXSPRITEPOSY)

{

gridcursorypos = MAXSPRITEPOSY;

scroll(DOWN);

}

if (gridcursorxpos < MINSPRITEPOSX)

{

gridcursorxpos = MINSPRITEPOSX;

scroll(LEFT);

}

if (gridcursorypos < MINSPRITEPOSY)

{

gridcursorypos = MINSPRITEPOSY;

scroll(UP);

}

move_object(GRIDCURSOR,gridcursorxpos,gridcursorypos);

}

else if (gmode==SELMODE)

{

joytemp = joypad();

if (joytemp & J_UP)

{

if (palcursorpos>ACTIONTILES)

{

palcursorpos--;

palcursorypos -= SELTILEDIST;

move_object(PALCURSOR,PALCURSORXPOS1, palcursorypos);

}

}

if (joytemp & J_DOWN)

{

if (palcursorpos<PROCTILES)

{

palcursorpos++;

palcursorypos += SELTILEDIST;

move_object(PALCURSOR,PALCURSORXPOS1, palcursorypos);

}

}

if (joytemp & J_RIGHT)

{

if (palcursorpos==ACTIONTILES&index1<maxsel1)

{

index1++;

currenttile1 = sel1[index1];

change_object(TILESEL1,currenttile1);

}

else if (palcursorpos==SENSORTILES&index2<maxsel2)

{

index2++;

currenttile2 = sel2[index2];

change_object(TILESEL2,currenttile2);

}

else if (palcursorpos==FLOWTILES&index3<maxsel3)

{

index3++;

currenttile3 = sel3[index3];

change_object(TILESEL3,currenttile3);

}

else if (palcursorpos==PROCTILES&index4<maxsel4)

{

index4++;

currenttile4 = sel4[index4];

change_object(PROCSEL,currenttile4);

}

}

if (joytemp & J_LEFT)

{

if (palcursorpos==ACTIONTILES&index1>0)

{

index1--;

currenttile1 = sel1[index1];

change_object(TILESEL1,currenttile1);

}

else if (palcursorpos==SENSORTILES&index2>0)

{

index2--;

currenttile2 = sel2[index2];

change_object(TILESEL2,currenttile2);

}

else if (palcursorpos==FLOWTILES&index3>0)

{

index3--;

currenttile3 = sel3[index3];

change_object(TILESEL3,currenttile3);

}

else if (palcursorpos==PROCTILES&index4>0)

{

index4--;

currenttile4 = sel4[index4];

change_object(PROCSEL,currenttile4);

}

}

if (joytemp & J_A)

{

if (palcursorpos==ACTIONTILES)

{

currenttile=currenttile1;

}

else if (palcursorpos==SENSORTILES)

{

currenttile=currenttile2;

}

else if (palcursorpos==FLOWTILES)

{

currenttile=currenttile3;

}

else if (palcursorpos==PROCTILES)

{

currenttile=currenttile4;

}

flipmode(DROPMODE);

}

if (joytemp & J_B)

{

uploadprogram();

RClick_Sound();

}

if (joytemp & J_SELECT)

{

flipmode(BLOCKMODE);

}

if (joytemp & J_START)

{

}

}

else if (gmode==NUMMODE)

{

joytemp = joypad();

if (joytemp & J_UP)

{

numnum++;

if (numnum==251)

numnum=250;

output_num(numnum,1,PALNUMPOS);

}

if (joytemp & J_DOWN)

{

numnum--;

if (numnum==-1)

numnum=0;

output_num(numnum,1,PALNUMPOS);

}

if (joytemp & J_A)

{

RClick_Sound();

grid[xtile][ytile]=numnum+NUMBERVALUE;

flipmode(SELMODE);

}

}

else if (gmode==POWMODE)

{

joytemp = joypad();

if (joytemp & J_UP)

{

pownum++;

if (pownum==11)

pownum=10;

output_short_num(pownum,1,PALNUMPOS);

}

if (joytemp & J_DOWN)

{

pownum--;

if (pownum==-1)

pownum=0;

output_short_num(pownum,1,PALNUMPOS);

}

if (joytemp & J_A)

{

RClick_Sound();

grid[xtile][ytile]=pownum+POWERVALUE;

flipmode(SELMODE);

}

}

}

}

 

INIT.C

#include <gb.h>

#include "fgtiles.c"

#include "bgtiles.c"

#include "defines.c"

extern tilepalmap[];

extern unsigned char framenummap[];

extern unsigned long pals[];

extern unsigned char bgtiles[];

extern unsigned char bgpalmap[];

extern unsigned char bgtilemap[];

extern unsigned char frametiles[];

extern unsigned char frametilemap[];

extern unsigned char fgtiles[];

extern unsigned char palmap[];

extern unsigned char ctilemap[];

extern int scrolledx;

extern int scrolledy;

extern int gmode;

extern unsigned int gridcursorxpos;

extern unsigned int gridcursorypos;

extern unsigned int currenttile;

extern unsigned int currenttile1;

extern unsigned int currenttile2;

extern unsigned int currenttile3;

extern unsigned int currenttile4;

extern long grid[16][16];

extern int palpos;

extern int palcursorpos;

extern unsigned int palcursorypos;

extern int currentrunrow;

extern int currentwatchrow;

extern long numnum;

extern int pownum;

extern int xmit_buf_x;

extern int xmit_buf_y;

extern unsigned int xmit_buf[20][64];

void Init_bank2()

//initialize grid, modes, palette, etc.

{

int i,j;

long k;

scrolledx=0;

scrolledy=0;

gmode = SELMODE;

gridcursorxpos=40;

gridcursorypos=48;

currenttile1=MOTOR;

currenttile2=TOUCHSENSOR1;

currenttile3=IF;

currenttile4=PROC1;

currenttile=currenttile1;

palcursorpos=ACTIONTILES;

palcursorypos=PALCURSORYPOS1;

currentrunrow=0;

currentwatchrow=8;

palpos=OUT;

pownum=0;

for (i=0;i!=16;i++)

{

for (j=0;j!=16;j++)

{

grid[i][j]=0;

}

}

grid[0][0]=START;

grid[8][0]=WATCH;

for (k=0;k!=1280;k++)

{

xmit_buf[k/xmit_buf_y][k%xmit_buf_y] = 0;

}

}

void InitScreen_bank2()

//initialize screen

{

//this function sets up the screen:

//background, window, and sprites

int bgxstart = 0; //tiling start x loc

int bgystart = 0; //tiling start y loc

int bgwidth = 32; //bg width in tiles

int bgheight = 32; //bg height in tiles

unsigned int bgtilen = TILENUM; //number of bg tiles

int framewidth = FRAMEWIDTH; //frame width in tiles

int frameheight = 18; //frame height in tiles

unsigned int frametilen = 128; //number of frame tiles

unsigned int fgtilen = TILENUM; //number of fg tiles

set_bkg_palette(0,8,&pals[0]);

set_bkg_data(0,bgtilen,bgtiles);

VBK_REG = 1; set_bkg_tiles( bgxstart, bgystart, bgwidth, bgheight, bgpalmap);

VBK_REG = 0; set_bkg_tiles( bgxstart, bgystart, bgwidth, bgheight, bgtilemap);

DISPLAY_ON;

set_win_data(0x80, frametilen, frametiles);

set_win_tiles(0, 0, framewidth, frameheight, frametilemap);

move_win(MAXWNDPOSX-8*framewidth+1,MINWNDPOSY);

SHOW_WIN;

set_sprite_palette(0,8,&pals[0]);

SPRITES_8x16;

set_sprite_data(0,fgtilen,fgtiles);

}

 

 

GBPROCS.C

#include <gb.h>

#include "defines.c"

#include "sound.c"

extern tilepalmap[];

extern unsigned char framenummap[];

extern unsigned long pals[];

extern unsigned char bgtiles[];

extern unsigned char bgpalmap[];

extern unsigned char bgtilemap[];

extern unsigned char frametiles[];

extern unsigned char frametilemap[];

extern unsigned char fgtiles[];

extern unsigned char palmap[];

extern unsigned char ctilemap[];

extern int scrolledx;

extern int scrolledy;

extern int gmode;

extern unsigned int gridcursorxpos;

extern unsigned int gridcursorypos;

extern long currenttile;

extern unsigned int currenttile1;

extern unsigned int currenttile2;

extern unsigned int currenttile3;

extern unsigned int currenttile4;

extern long grid[16][16];

extern int palpos;

extern int palcursorpos;

extern unsigned int palcursorypos;

extern int currentrunrow;

extern int currentwatchrow;

extern long numnum;

extern int pownum;

extern int xmit_buf_x;

extern int xmit_buf_y;

extern unsigned int xmit_buf[20][64];

void output_num(unsigned int numa,unsigned int xp,unsigned int yp)

//output a number in the palette status area

{

unsigned int hundreds,tens,ones;

hundreds = numa/0x64U;

numa = numa - (100 * hundreds);

tens = numa/0x0aU;

ones = numa%0x0aU;

set_win_tiles(xp+0,yp,1,1,&framenummap[hundreds]);

set_win_tiles(xp+1,yp,1,1,&framenummap[tens]);

set_win_tiles(xp+2,yp,1,1,&framenummap[ones]);

}

void output_lnum(long numa,unsigned int xp,unsigned int yp)

//output a long number in the palette status area

{

unsigned int hundreds,tens,ones;

hundreds = numa/0x64U;

numa = numa - (100 * hundreds);

tens = numa/0x0aU;

ones = numa%0x0aU;

if (numa > 999)

{

hundreds = 9;

tens = 9;

ones = 9;

}

set_win_tiles(xp+0,yp,1,1,&framenummap[hundreds]);

set_win_tiles(xp+1,yp,1,1,&framenummap[tens]);

set_win_tiles(xp+2,yp,1,1,&framenummap[ones]);

}

void scroll(int dir)

//scroll the grid in dir

{

int x,y,f,fx,fy;

int bgwidth=16; //bg width in 16x16 blocks

int bgheight=16;

int viswidth=10; //visible width in 16x16 blocks

int visheight=9;

fx=1;

fy=1;

if (palpos==OUT)

viswidth -= 3;

if (dir==RIGHT)

{

x=4;

y=0;

scrolledx++;

}

else if (dir==LEFT)

{

x=-4;

y=0;

scrolledx--;

}

else if (dir==UP)

{

x=0;

y=-4;

scrolledy--;

}

else if (dir==DOWN)

{

x=0;

y=4;

scrolledy++;

}

f = furthest();

fx = f/0x10U;

fy = f%0x10U;

fx = fx+3-viswidth;

fy = fy+3-visheight;

if (fx<0)

fx=0;

if (fy<0)

fy=0;

if (scrolledx<0)

scrolledx=0;

else if (scrolledy<0)

scrolledy=0;

else if (scrolledx>fx)

scrolledx=fx;

else if (scrolledy>fy)

scrolledy=fy;

else if (scrolledx>bgwidth-viswidth)

scrolledx=bgwidth-viswidth;

else if (scrolledy>bgheight-visheight)

scrolledy=bgheight-visheight;

else

{

scroll_bkg(x,y);

wait_vbl_done();

scroll_bkg(x,y);

wait_vbl_done();

scroll_bkg(x,y);

wait_vbl_done();

scroll_bkg(x,y);

}

}

int candrop()

//returns true if the current block can be dropped in the current position

//enforces rep invariant

{

unsigned int ct, xtile, ytile, top, left, topleft, twoleft, right, bottom, under, ok, row, prev;

xtile = gridcursorxpos; //

xtile = xtile/8; //

xtile = xtile + 2*scrolledx; //

xtile = xtile-1; //

xtile = xtile/2; //neccessary because

ytile = gridcursorypos; //of stack overflow

ytile = ytile/8; //

ytile = ytile + 2*scrolledy; //

ytile = ytile-1; //

ytile = ytile/2; //

if (ytile==0)

top = BLANK;

else

top = sensorconv(grid[xtile][ytile-1]);

if (xtile==0)

left=BLANK;

else

left = sensorconv(grid[xtile-1][ytile]);

if (xtile==15)

right=BLANK;

else

right = sensorconv(grid[xtile+1][ytile]);

if (ytile==16)

bottom = BLANK;

else

bottom = sensorconv(grid[xtile][ytile+1]);

row = sensorconv(grid[0][ytile]);

under = sensorconv(grid[xtile][ytile]);

ok=0;

if (xtile==0|ytile==0)

topleft = BLANK;

else

topleft = sensorconv(grid[xtile-1][ytile-1]);

if (xtile<2)

twoleft = BLANK;

else

twoleft = sensorconv(grid[xtile-2][ytile]);

ct = sensorconv(currenttile);

switch (ct)

{

case MOTOR:

if ((top==START|top==WATCH|top==WAIT|top==MOTOR|top==IFHOLDER|top==ELSEHOLDER|top==LOOPHOLDER|top==NOTE|topleft==IF|left==ELSE|left==LOOP|topleft==LOOP)&!(top==IF&topleft==LOOP)&under==0)

ok=1;

break;

case TOUCHSENSOR:

if ((left==WATCH|left==IF|LEFT==WAIT)&under==0)

ok=1;

break;

case LIGHTSENSOR:

if ((left==WATCH|left==IF|LEFT==WAIT)&under==0)

ok=1;

break;

case DARK:

if ((left==LIGHTSENSOR|(twoleft==LIGHTSENSOR&(left!=LIGHT&left!=DARK)))&under==0)

ok=1;

break;

case LIGHT:

if ((left==LIGHTSENSOR|twoleft==LIGHTSENSOR)&under==0)

ok=1;

break;

case WAIT:

if ((top==START|top==WATCH|top==WAIT|top==MOTOR|top==IFHOLDER|top==ELSEHOLDER|top==LOOPHOLDER|top==NOTE|topleft==IF|left==ELSE|left==LOOP|(twoleft==LOOP&left==NUMBER))&under==0)

ok=1;

break;

case NOTE:

if ((top==START|top==WATCH|top==WAIT|top==MOTOR|top==IFHOLDER|top==ELSEHOLDER|top==LOOPHOLDER|top==NOTE|topleft==IF|left==ELSE|left==LOOP|(twoleft==LOOP&left==NUMBER))&under==0)

ok=1;

break;

case NUMBER:

if ((left==FORWARD|left==BACKWARD|left==WAIT|left==NOTE|left==MOTOR|left==A|left==B|left==C|left==LOOP|left==POWER)&under==0)

ok=1;

break;

case A:

if ((left==MOTOR|left==B|left==C|left==LIGHTSENSOR|left==TOUCHSENSOR)&under==0)

ok=1;

break;

case B:

if ((left==MOTOR|left==A|left==C|left==LIGHTSENSOR|left==TOUCHSENSOR)&under==0)

ok=1;

break;

case C:

if ((left==MOTOR|left==A|left==B|left==LIGHTSENSOR|left==TOUCHSENSOR)&under==0)

ok=1;

break;

case FORWARD:

if ((left==A|left==B|left==C|left==MOTOR)&under==0)

ok=1;

break;

case BACKWARD:

if ((left==A|left==B|left==C|left==MOTOR)&under==0)

ok=1;

break;

case REVERSE:

if ((left==A|left==B|left==C|left==MOTOR)&under==0)

ok=1;

break;

case STOP:

if ((left==MOTOR|left==A|left==B|left==C)&under==0)

ok=1;

break;

case IF:

if ((top==START|top==WATCH|top==WAIT|top==MOTOR|top==IFHOLDER|top==ELSEHOLDER|top==LOOPHOLDER|top==NOTE|topleft==IF|left==ELSE|left==LOOP|topleft==LOOP)&under==0)

ok=1;

break;

case ELSE:

if ((top==IFHOLDER)&under==0)

ok=1;

break;

case LOOP:

if ((top==START|top==WATCH|top==WAIT|top==MOTOR|top==IFHOLDER|top==ELSEHOLDER|top==LOOPHOLDER|top==NOTE|topleft==IF|left==ELSE|left==LOOP|topleft==LOOP)&under==0)

ok=1;

break;

case POWER:

if ((left==MOTOR|left==A|left==B|left==C)&under==0)

ok=1;

break;

}

if (!ok)

Move_Sound();

return(ok);

}

void dropblock()

//drops block in current position

{

int xtile,ytile,tilepal,i;

unsigned int ct,ct2;

xtile = gridcursorxpos/8+scrolledx*2-1;

ytile = gridcursorypos/8+scrolledy*2-2;

if (currenttile==LIGHTSENSOR1)

{

Bad_Sound();

ct = LIGHTSENSOR;

ct2 = L1;

}

else if (currenttile==LIGHTSENSOR2)

{

ct = LIGHTSENSOR;

ct2 = L2;

}

else if (currenttile==LIGHTSENSOR3)

{

ct = LIGHTSENSOR;

ct2 = L3;

}

else if (currenttile==TOUCHSENSOR1)

{

ct = TOUCHSENSOR;

ct2 = L1;

}

else if (currenttile==TOUCHSENSOR2)

{

ct = TOUCHSENSOR;

ct2 = L2;

}

else if (currenttile==TOUCHSENSOR3)

{

ct = TOUCHSENSOR;

ct2 = L3;

}

else if (currenttile==PROC1)

{

ct = PROC;

ct2 = L1;

}

else if (currenttile==PROC2)

{

ct = PROC;

ct2 = L2;

}

else if (currenttile==PROC3)

{

ct = PROC;

ct2 = L3;

}

else

{

ct = currenttile;

ct2 = ct + 2;

}

tilepal = tilepalmap[ct/4];

VBK_REG = 1;

set_bkg_tiles(xtile,ytile, 1, 1, &palmap[tilepal]);

set_bkg_tiles(xtile,ytile+1, 1, 1, &palmap[tilepal]);

set_bkg_tiles(xtile+1,ytile, 1, 1, &palmap[tilepal]);

set_bkg_tiles(xtile+1,ytile+1, 1, 1, &palmap[tilepal]);

VBK_REG = 0;

set_bkg_tiles(xtile,ytile, 1, 1, &ctilemap[ct]);

set_bkg_tiles(xtile,ytile+1, 1, 1, &ctilemap[ct+1]);

set_bkg_tiles(xtile+1,ytile, 1, 1, &ctilemap[ct2]);

set_bkg_tiles(xtile+1,ytile+1, 1, 1, &ctilemap[ct2+1]);

xtile=xtile/2;

ytile=ytile/2;

grid[xtile][ytile]=currenttile;

for (i=xtile-1;i>-1;i--)

{

if (grid[i][ytile-1]==IFHOLDER|grid[i][ytile-1]==IF)

grid[i][ytile]=IFHOLDER;

if (grid[i][ytile-1]==ELSEHOLDER|grid[i][ytile-1]==ELSE)

grid[i][ytile]=ELSEHOLDER;

if (grid[i][ytile-1]==LOOPHOLDER|grid[i][ytile-1]==LOOP)

grid[i][ytile]=LOOPHOLDER;

}

}

void eraseblock()

//erases block in current position

{

int xtile,ytile,tilepal;

xtile = gridcursorxpos/8+scrolledx*2-1;

ytile = gridcursorypos/8+scrolledy*2-2;

tilepal = tilepalmap[0];

if (grid[xtile/2][ytile/2]!=START&grid[xtile/2][ytile/2]!=WATCH)

{

VBK_REG = 1;

set_bkg_tiles(xtile,ytile, 1, 1, &palmap[tilepal]);

set_bkg_tiles(xtile,ytile+1, 1, 1, &palmap[tilepal]);

set_bkg_tiles(xtile+1,ytile, 1, 1, &palmap[tilepal]);

set_bkg_tiles(xtile+1,ytile+1, 1, 1, &palmap[tilepal]);

VBK_REG = 0;

set_bkg_tiles(xtile,ytile, 1, 1, &ctilemap[GRID]);

set_bkg_tiles(xtile,ytile+1, 1, 1, &ctilemap[GRID+1]);

set_bkg_tiles(xtile+1,ytile, 1, 1, &ctilemap[GRID+2]);

set_bkg_tiles(xtile+1,ytile+1, 1, 1, &ctilemap[GRID+3]);

grid[xtile/2][ytile/2]=0;

}

}

void slide_frame(int dir)

//slides palette in or out{

int framewidth = FRAMEWIDTH;

int i;

if (dir==IN)

{

for (i=6;i!=-2;i-=2)

{

move_win(MAXWNDPOSX-i*framewidth+1,MINWNDPOSY);

move_object(TILESEL1,TILESEL1XPOS+48-6*i,TILESEL1YPOS);

move_object(TILESEL2,TILESEL2XPOS+48-6*i,TILESEL2YPOS);

move_object(TILESEL3,TILESEL3XPOS+48-6*i,TILESEL3YPOS);

move_object(PROCSEL,PROCSELXPOS+48-6*i,PROCSELYPOS);

move_object(PALCURSOR,PALCURSORXPOS1+48-6*i,palcursorypos);

wait_vbl_done();

}

palpos=IN;

}

else if (dir==OUT)

{

for (i=2;i!=10;i+=2)

{

move_win(MAXWNDPOSX-i*framewidth+1,MINWNDPOSY);

move_object(TILESEL1,TILESEL1XPOS+48-6*i,TILESEL1YPOS);

move_object(TILESEL2,TILESEL2XPOS+48-6*i,TILESEL2YPOS);

move_object(TILESEL3,TILESEL3XPOS+48-6*i,TILESEL3YPOS);

move_object(PROCSEL,PROCSELXPOS+48-6*i,PROCSELYPOS);

move_object(PALCURSOR,PALCURSORXPOS1+48-6*i,palcursorypos);

wait_vbl_done();

}

palpos=OUT;

}

}

 

UPLOAD.C

#include "cbytecodes.txt"

#include "defines.c"

extern xmit_buf[20][64];

extern grid[16][16];

extern xmit_buf_y;

void uploadprogram()

//compiles program into byte code,

//then uploads it to the cricket

//

//this is not the most current version!!!

{

long xmit_length=0;

unsigned int watchtilex=8;

unsigned int watchtiley=0;

unsigned int starttilex=0;

unsigned int starttiley=0;

int whenloop=0;

long procmain,proc1,proc2,proc3;

long code_start_address=0;

long i;

int sensoraglobal=0;

int sensorbglobal=2;

long list1holder;

long list2holder;

int sensor;

procmain=0;

xmit_buf[0][xmit_length++] = C_BYTE;

xmit_buf[0][xmit_length++] = sensoraglobal;

xmit_buf[0][xmit_length++] = C_SENSORA;

xmit_buf[0][xmit_length++] = C_SETGLOBAL;

xmit_buf[0][xmit_length++] = C_BYTE;

xmit_buf[0][xmit_length++] = sensorbglobal;

xmit_buf[0][xmit_length++] = C_SENSORB;

xmit_buf[0][xmit_length++] = C_SETGLOBAL;

procmain = xmit_length + 2; //skips next 3 instructions to get to main

xmit_buf[0][xmit_length++] = top_byte(procmain) + C_PROC_CALL;

xmit_buf[0][xmit_length++] = bot_byte(procmain);

xmit_buf[0][xmit_length++] = C_CODE_END;

if (grid[watchtilex+1][watchtiley] != BLANK )

{

//put watch into a when

whenloop = 1;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_LIST;

list1holder = xmit_length;

xmit_length++; //leave space for list length

if (grid[watchtilex+1][watchtiley]==TOUCHSENSOR1|grid[watchtilex+1][watchtiley]==LIGHTSENSOR1)

{

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_SENSORA;

sensor = sensoraglobal;

}

else if (grid[watchtilex+1][watchtiley]==TOUCHSENSOR2|grid[watchtilex+1][watchtiley]==LIGHTSENSOR2)

{

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_SENSORB;

sensor = sensorbglobal;

}

if (grid[watchtilex+2][watchtiley]==DARK)

{

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BYTE;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = sensor;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_GLOBAL;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BYTE;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = 10;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_MINUS;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_LESS_THAN;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_EOLR;

xmit_buf[list1holder/xmit_buf_y][(list1holder)%xmit_buf_y] = xmit_length-(list1holder+1);

}

else if (grid[watchtilex+2][watchtiley]==LIGHT)

{

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BYTE;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = sensor;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_GLOBAL;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BYTE;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = 10;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_PLUS;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_GREATER_THAN;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_EOLR;

xmit_buf[list1holder/xmit_buf_y][(list1holder)%xmit_buf_y] = xmit_length-(list1holder+1);

}

else if (grid[watchtilex+2][watchtiley]==OFF)

{

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BYTE;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = sensor;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_GLOBAL;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BYTE;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = 0;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_EQUALS;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_EOLR;

xmit_buf[list1holder/xmit_buf_y][(list1holder)%xmit_buf_y] = xmit_length-(list1holder+1);

}

else

{

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_EOLR;

xmit_buf[list1holder/xmit_buf_y][(list1holder)%xmit_buf_y] = xmit_length-(list1holder+1);

}

//add in watch code

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_LIST;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = 2;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BEEP;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_EOL;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_WHEN;

}

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_LIST;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = 5;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_BYTE;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = 10;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_ONFOR;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_RD;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_EOL;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_LOOP;

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_STOP;

 

if (whenloop)

xmit_buf[xmit_length/xmit_buf_y][(xmit_length++)%xmit_buf_y] = C_WHENOFF;

//put in procedures here...

StartSend(C_CRICKET_CHECK);

StartSend(0);

StartSend(C_SET_POINTER);

StartSend(top_byte(code_start_address));

StartSend(bot_byte(code_start_address));

StartSend(C_WRITE_BYTES);

StartSend(top_byte(xmit_length));

StartSend(bot_byte(xmit_length));

for (i=0;i!=xmit_length;i++)

{

StartSend(xmit_buf[i/xmit_buf_y][i%xmit_buf_y]);

}

StartSend(C_SET_POINTER);

StartSend(top_byte(C_BUTTON_1_PTR));

StartSend(bot_byte(C_BUTTON_1_PTR));

StartSend(C_WRITE_BYTES);

StartSend(top_byte(2));

StartSend(bot_byte(2));

StartSend(top_byte(code_start_address));

StartSend(bot_byte(code_start_address));

StartSend(C_SET_POINTER);

StartSend(top_byte(code_start_address));

StartSend(bot_byte(code_start_address));

StartSend(C_RUN);

}

SOUND.C

void Init_Sound()

{

NR52_REG = 0x8BU; /* make sure sound is enabled */

NR51_REG = 0xFFU; /* send sound to left and rignt */

NR50_REG = 0x77U;

}

void Boom()

{

int length;

int envinit;

int envmode;

int envsteps;

int polyfreq;

int polystep;

int polydiv;

int consecutive;

int initialflag;

int so1;

int so2;

length = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envsteps = 5; /* 0 - 7 */

envmode = 0; /* 0 = decrease 1 = increase */

polyfreq = 4; /* 0 - 7 */

polystep = 0; /* 0 - 1 */

polydiv = 5; /* 0 - 7 */

consecutive = 0; /* 0 - 1 */

initialflag = 1; /* 0 - 1 */

NR41_REG = length;

NR42_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR43_REG = (polydiv + (polystep * 8) + (polyfreq * 16));

NR44_REG = (consecutive * 64) + 128;

}

void Move_Sound() /******* Control Sound 1 ***********/

{

int sweeptime;

int sweepmode;

int sweepshifts;

int patternduty;

int soundlength;

int envinit;

int envmode;

int envsteps;

unsigned int freql;

int freqh;

int consecutive;

int initialflag;

sweeptime = 2; /* 0-7 */

sweepmode = 1; /* 0-1 */

sweepshifts = 5; /* 0 - 7 */

patternduty = 3; /* 0-3 */

soundlength = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envmode = 0; /* 0-1 */

envsteps = 2; /* 0 - 7 */

freql = 165;

freqh = 3;

consecutive = 0;

initialflag = 1;

NR10_REG = (sweepshifts + (sweepmode * 8) + (sweeptime * 16));

NR11_REG = (soundlength + (patternduty * 32));

NR12_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR13_REG = freql;

NR14_REG = (freqh + (consecutive * 64) + (initialflag * 128));

}

void Clear_Sound(int start) /******* Control Sound 1 ***********/

{

int sweeptime;

int sweepmode;

int sweepshifts;

int patternduty;

int soundlength;

int envinit;

int envmode;

int envsteps;

unsigned int freql;

int freqh;

int consecutive;

int initialflag;

if(start)

{

sweeptime = 0; /* 0-7 */

sweepmode = 1; /* 0-1 */

sweepshifts = 0; /* 0 - 7 */

patternduty = 0; /* 0-3 */

soundlength = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envmode = 1; /* 0-1 */

envsteps = 2; /* 0 - 7 */

freql = 165;

freqh = 5;

consecutive = 0;

initialflag = 1;

NR10_REG = (sweepshifts + (sweepmode * 8) + (sweeptime * 16));

NR11_REG = (soundlength + (patternduty * 32));

NR12_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR13_REG = freql;

NR14_REG = (freqh + (consecutive * 64) + (initialflag * 128));

}//end if

else

{

sweeptime = 7; /* 0-7 */

sweepmode = 0; /* 0-1 */

sweepshifts = 7; /* 0 - 7 */

patternduty = 0; /* 0-3 */

soundlength = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envmode = 0; /* 0-1 */

envsteps = 2; /* 0 - 7 */

freql = 165;

freqh = 5;

consecutive = 0;

initialflag = 1;

NR10_REG = (sweepshifts + (sweepmode * 8) + (sweeptime * 16));

NR11_REG = (soundlength + (patternduty * 32));

NR12_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR13_REG = freql;

NR14_REG = (freqh + (consecutive * 64) + (initialflag * 128));

}

}

void Bad_Sound() /******* Control Sound 1 ***********/

{

int sweeptime;

int sweepmode;

int sweepshifts;

int patternduty;

int soundlength;

int envinit;

int envmode;

int envsteps;

unsigned int freql;

int freqh;

int consecutive;

int initialflag;

sweeptime = 7; /* 0-7 */

sweepmode = 1; /* 0-1 */

sweepshifts = 1; /* 0 - 7 */

patternduty = 1; /* 0-3 */

soundlength = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envmode = 0; /* 0-1 */

envsteps = 2; /* 0 - 7 */

freql = 000;

freqh = 5;

consecutive = 0;

initialflag = 1;

NR10_REG = (sweepshifts + (sweepmode * 8) + (sweeptime * 16));

NR11_REG = (soundlength + (patternduty * 32));

NR12_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR13_REG = freql;

NR14_REG = (freqh + (consecutive * 64) + (initialflag * 128));

delay(100);

sweeptime = 7; /* 0-7 */

sweepmode = 1; /* 0-1 */

sweepshifts = 2; /* 0 - 7 */

patternduty = 3; /* 0-3 */

soundlength = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envmode = 0; /* 0-1 */

envsteps = 2; /* 0 - 7 */

freql = 000;

freqh = 4;

consecutive = 0;

initialflag = 1;

NR10_REG = (sweepshifts + (sweepmode * 8) + (sweeptime * 16));

NR11_REG = (soundlength + (patternduty * 32));

NR12_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR13_REG = freql;

NR14_REG = (freqh + (consecutive * 64) + (initialflag * 128));

}

void RClick_Sound() /******* Control Sound 1 ***********/

{

int sweeptime;

int sweepmode;

int sweepshifts;

int patternduty;

int soundlength;

int envinit;

int envmode;

int envsteps;

unsigned int freql;

int freqh;

int consecutive;

int initialflag;

sweeptime = 7; /* 0-7 */

sweepmode = 1; /* 0-1 */

sweepshifts = 2; /* 0 - 7 */

patternduty = 3; /* 0-3 */

soundlength = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envmode = 0; /* 0-1 */

envsteps = 2; /* 0 - 7 */

freql = 000;

freqh = 5;

consecutive = 0;

initialflag = 1;

NR10_REG = (sweepshifts + (sweepmode * 8) + (sweeptime * 16));

NR11_REG = (soundlength + (patternduty * 32));

NR12_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR13_REG = freql;

NR14_REG = (freqh + (consecutive * 64) + (initialflag * 128));

delay(100);

sweeptime = 7; /* 0-7 */

sweepmode = 1; /* 0-1 */

sweepshifts = 1; /* 0 - 7 */

patternduty = 1; /* 0-3 */

soundlength = 0; /* 0 - 63 */

envinit = 15; /* 0 - 15 */

envmode = 0; /* 0-1 */

envsteps = 2; /* 0 - 7 */

freql = 255;

freqh = 5;

consecutive = 0;

initialflag = 1;

NR10_REG = (sweepshifts + (sweepmode * 8) + (sweeptime * 16));

NR11_REG = (soundlength + (patternduty * 32));

NR12_REG = (envsteps + (envmode * 8) + (envinit * 16));

NR13_REG = freql;

NR14_REG = (freqh + (consecutive * 64) + (initialflag * 128));

}

 

CBYTECODES.TXT

#define C_CODE_END 0

#define C_BYTE 1

#define C_NUMBER 2

#define C_LIST 3

#define C_EOL 4

#define C_EOLR 5

#define C_LTHING 6

#define C_STOP 7

#define C_OUTPUT 8

#define C_REPEAT 9

#define C_IF 10

#define C_IFELSE 11

#define C_BEEP 12

#define C_NOTE 13

#define C_WAITUNTIL 14

#define C_LOOP 15

#define C_WAIT 16

#define C_TIMER 17

#define C_RESETT 18

#define C_SEND 19

#define C_IR 20

#define C_NEWIR? 21

#define C_RANDOM 22

#define C_PLUS 23

#define C_MINUS 24

#define C_TIMES 25

#define C_DIVIDE 26

#define C_MOD 27

#define C_EQUALS 28

#define C_GREATER_THAN 29

#define C_LESS_THAN 30

#define C_AND 31

#define C_OR 32

#define C_XOR 33

#define C_NOT 34

#define C_SETGLOBAL 35

#define C_GLOBAL 36

#define C_ASET 37

#define C_AGET 38

#define C_RECORD 39

#define C_RECALL 40

#define C_RESETDP 41

#define C_SETDP 42

#define C_ERASE 43

#define C_WHEN 44

#define C_WHENOFF 45

#define C_A, 46

#define C_B, 47

#define C_AB, 48

#define C_ON 49

#define C_ONFOR 50

#define C_OFF 51

#define C_THISWAY 52

#define C_THATWAY 53

#define C_RD 54

#define C_SENSORA 55

#define C_SENSORB 56

#define C_SWITCHA 57

#define C_SWITCHB 58

#define C_SETPOWER 59

#define C_BRAKE 60

#define C_BSEND 61

#define C_BSR 62

#define C_C, 63

#define C_D, 64

#define C_CD, 65

#define C_ABCD, 66

#define C_FASTSEND 67

#define C_STOPEX 68

#define C_EB 69

#define C_DB 70

#define C_PROC_CALL 128

#define C_SET_POINTER 131

#define C_READ_BYTES 132

#define C_WRITE_BYTES 133

#define C_RUN 134

#define C_CRICKET_CHECK 135

#define C_BUTTON_1_PTR 4080

#define C_BUTTON_2_PTR 4082

 

DEFINES.C

#define WINDOWWIDTH 48

#define MAXSPRITEPOSX MAXWNDPOSX-14

#define MINSPRITEPOSX MINWNDPOSX+1

#define MAXSPRITEPOSY MAXWNDPOSY+1

#define MINSPRITEPOSY MINWNDPOSY+16

#define FRAMEWIDTH 6

#define RIGHT 1

#define DOWN 2

#define LEFT 3

#define UP 4

#define BLOCKMODE 1

#define SELMODE 2

#define DROPMODE 3

#define NUMMODE 4

#define NOTEMODE 5

#define POWMODE 6

//block tile numbers

#define TILENUM 128

#define BLANK 0

#define CROSSHAIR 4

#define MOTOR 8

#define TOUCHSENSOR 12

#define L1 14

#define LIGHTSENSOR 16

#define L2 18

#define DARK 20

#define LIGHT 24

#define WAIT 28

#define NOTE 32

#define NUMBER 36

#define A 40

#define B 44

#define C 48

#define FORWARD 52

#define BACKWARD 56

#define REVERSE 60

#define STOP 64

#define START 68

#define WATCH 72

#define GRID 76

#define IF 80

#define ELSE 84

#define SPRING 88

#define LOOP 92

#define POWER 96

#define PROC 100

#define L3 102

#define ON 104

#define OFF 108

#define SELARROW 124

#define IFHOLDER 200

#define ELSEHOLDER 201

#define LOOPHOLDER 202

#define LIGHTSENSOR1 203

#define LIGHTSENSOR2 204

#define LIGHTSENSOR3 205

#define TOUCHSENSOR1 206

#define TOUCHSENSOR2 207

#define TOUCHSENSOR3 208

#define PROC1 209

#define PROC2 210

#define PROC3 211

#define NUMBERVALUE 300

#define POWERVALUE 600

//window tile numbers

#define NOTEA 13

#define NOTEB 14

#define NOTEC 15

#define NOTED 16

#define NOTEE 17

#define NOTEF 18

#define NOTEG 19

#define SHARP 20

#define FLAT 21

#define NUM0 22

#define NUM1 23

#define NUM2 24

#define NUM3 25

#define NUM4 26

#define NUM5 27

#define NUM6 28

#define NUM7 29

#define NUM8 30

#define NUM9 31

#define DASH 32

#define DOT 33

#define LETA 34

#define LETB 35

#define LETC 36

#define LETD 37

#define LETE 38

#define LETF 39

#define LETG 40

#define LETH 41

#define LETI 42

#define LETJ 43

#define LETK 44

#define LETL 45

#define LETM 46

#define LETN 47

#define LETO 48

#define LETP 49

#define LETQ 50

#define LETR 51

#define LETS 52

#define LETT 53

#define LETU 54

#define LETV 55

#define LETW 56

#define LETX 57

#define LETY 58

#define LETZ 59

#define COLON 60

#define PALBLANK 61

 

 

//palletes

#define GRAY 0

#define RED 1

#define ORANGE 2

#define YELLOW 3

#define GREEN 4

#define REDYELLOW 5

#define BLUE 6

#define VIOLET 7

//sprite numbers

#define GRIDCURSOR 0

#define PALCURSOR 2

#define TILESEL1 4

#define TILESEL2 6

#define TILESEL3 10

#define PROCSEL 8

#define TILESEL1XPOS 125

#define TILESEL1YPOS 23

#define TILESEL2XPOS 125

#define TILESEL2YPOS 43

#define TILESEL3XPOS 125

#define TILESEL3YPOS 63

#define PROCSELXPOS 125

#define PROCSELYPOS 83

#define SELTILEDIST 20 // TILESEL2YPOS-TILESEL1YPOS

#define PALCURSORXPOS1 145

#define PALCURSORYPOS1 23

#define PALCURSORYPOS2 120

#define PALNUMPOS 15

#define PALTITLEPOS 12

#define ACTIONTILES 0

#define SENSORTILES 1

#define FLOWTILES 2

#define PROCTILES 3

#define IN 0

#define OUT 1

 

MAKE.BAT

lcc -Wa-l -Wf-bo0 -c -o gbblocks.o gbblocks.c

lcc -Wa-l -Wf-bo2 -c -o irsend.o irsend.s

lcc -Wa-l -Wf-bo2 -c -o upload.o upload.c

lcc -Wa-l -Wf-bo1 -c -o gbprocs.o gbprocs.c

lcc -Wa-l -Wf-bo2 -c -o init.o init.c

lcc -Wl-m -Wl-yp0x143=0xc0 -Wl-yt1 -Wl-yo4 -Wl-ya1 -o gbblocks.gb gbblocks.o irsend.o upload.o gbprocs.o init.o

GBT15 GBBLOCKS.GB

@echo Done !

 

 

 

 

 

Appendix C

COMMUNICATIONS, BLUE DOT CRICKETS

http://fredm.www.media.mit.edu/people/fredm/projects/cricket/ckdirect-blue/comms.txt

 

THE CRICKET INTERFACE

The communications with the Cricket is via infrared through the Serial Interface Cricket. We use IRDa components with our own modulation scheme that produces an effective comms rate of about 50K baud; however, the baud rate between the Serial Interface Cricket and the host computer is 9600 N-8-1.

To keep the host from overflowing the bandwidth of the IR channel, there is a simple software handshake performed by the Interface Cricket. After transmitting the byte to the Cricket, the Interface Cricket echoes the unary complement of that byte back to the host.

Every time you send a byte to the Cricket, you must fetch the handshake byte before sending out the next byte. You may wish to check the handshake byte for correctness.

INTERACTION PROTOCOL

Crickets respond to a simple communications protocol. There is an address register that specifies where to read memory, write memory, or execute code from memory. Individual serial line transactions either set this register, perform a read command, write command, or execute command. There is no handshake on commands that do not specifically return a value (other than the handshaking implemented by the Interface Cricket as noted above).

Command ASCII Description

set-pointer 131 followed by address: high byte and low byte sets address register to address.

read-bytes 132 followed by two bytes of count (high byte first), after which the Cricket will

transmit "count" bytes of data. Increments the address register throughout, so afterward the register is pointing at the byte just beyond the last one read.

write-bytes 133 followed by two bytes of count (high byte first), then count bytes of data. Writes

the bytes starting at the address indicated by the address register. When finished, the address register will point to the memory location immediately after the last byte written.

run 134 starts execution of compiled Cricket Logo code beginning at address indicated

by the address register.

cricket-check 135 used to address one or multiple Crickets. See "Crickets Have Names," below.

CRICKETS HAVE NAMES

There is a protocol that lets you use several Crickets at the same time. You may check if a Cricket is present, addressing it using an ASCII name. All Crickets in the line of sight of the Interface Cricket, with that name, respond and activate themselves for subsequent commands. Other Crickets ignore commands until a subsequent "cricket check" may enable them. You can use a null name to address all Crickets in sight.

To address a cricket, send out the "cricket-check" byte, followed by the name of the cricket as a zero-terminated ASCII string. If that cricket is present, it will respond by sending back an ASCII 55. This cricket and any others with the same name will activate themselves for subsequent operations. (If multiple crickets are turned on, there will be a 55 handshake byte for each cricket that responded.) Any other cricket will "go to sleep," waiting for a subsequent cricket-check sequence that might activate it.

A zero-termined null string (i.e., the cricket-check byte followed by just a zero) will activate all crickets in sight. The cricket name is stored as a zero-terminated ASCII string from addresses 0xff4 to 0xffe (11 bytes) in the cricket's memory.

Appendix D

THE BLUE DOT CRICKET LOGO BYTE CODE INTERPRETER

http://fredm.www.media.mit.edu/people/fredm/projects/cricket/ckdirect-blue/ckdirect.txt

December 25, 1998

originally by Brian Silverman and Fred Martin for Red Dot Crickets;

revised and updated for Blue Dots by Kenneth Welch

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 <on> are used

instead of the real code (which for <on> is 49).

(run <on>) ; 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 <off>)

turns the motor off again.

(run <b> <on> <rd>)

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 (<on>,

<rd>, 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

Blue Dot 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 <a> <%num> 10 <onfor>)

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 <on> <off> <rd> <%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 <onfor> <rd> <%eol> <repeat>)

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 <switcha> <%list> 4 <%num> 5 <onfor> <%eol> <if>)

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 <switcha> <%eolr> <waituntil> <%num> 5 <onfor>)

 

BACKGROUND PROCESSES

Blue Dot Crickets support a single background process, initiated by the <when>

opcode and terminated by the <whenoff> 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 <switcha> <%eolr> <%list> 2 <beep> <%eol> <when>

<%num> 10 <%list> 5 <ab> <%num> 5 <onfor> <%eol> <repeat> <whenoff>)

Alternatively, if the <whenoff> 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.

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 <ab> <%num> 10 <onfor>))

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 <stop> 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 <onfor> <rd> <%eol> <repeat>))

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 <onfor> <rd> <%eol> <repeat>))

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 <output> opcode, which takes a

single input - the value to be returned - and replaces the <stop> opcode at

the end of a procedure. You can simply include it at the end of your procedure

body code, however, and the <stop> opcode that the defproc macro automatically

appends will just be ignored. For example:

(defproc #x000 (0 <%num> 10 <onfor> <%num> 5 <output>))

(run #x80 #x00 <%list> 2 <beep> <%eol> <repeat>)

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

<send> <ir> <newir?>

 

DATA-TAKING

Blue Dot 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 <resetdp> opcode takes no inputs and moves the "data pointer"

to the beginning of this space. The <setdp> opcode takes a single input, a number

between 0 and 2500, and moves the pointer to that specific data location. The

<record> 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 <recall>

opcode takes no inputs and returns the value at the current data location, also

advancing the data pointer by one in the process. And the <erase> opcode, a

macro for RESETDP REPEAT 2500 [RECORD 0] RESETDP, writes zeros to all 2500 data

locations and resets the data pointer.

 

PRECISION TIMING

Support for precision timing is through the <resett> and <timer> 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

Blue Dot Crickets support 16 global variables; they are 16-bit numbers and

are stored in the cricket's RAM. To work with them, use the <global> and

<setglobal> 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 <setglobal>)

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 <setglobal>)

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 Blue Dot Cricket bus port is through the <bsend>, <bsr>, <c>,

<d>, <cd>, and <abcd> 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 <aset> and <aget> opcodes;

please check the code for more details.

 

BLUE-DOT EEPROM MEMORY MAP

#x000-#x4ff (user code) 1280 bytes

#x500-#xec3 (<record> 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 <num16> opcode and 16-bit numbers.

* Support for the <when> and <whenoff> opcodes and background processes.

* Support for the <newir?> opcode.

* Support for 2500 16-bit numbers, rather than 250 8-bit numbers, for use

with the data-taking primitives.

* Support for the <erase> opcode.

* The <timer> 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.

Appendix E

Table of Cricket Op-Codes 

http://fredm.www.media.mit.edu/people/fredm/projects/cricket/ckdirect-blue/bytecodes.html

Mnemonic

Byte

Command or Reporter

Num of Args

Comment

code-end

0

c

0

terminates code.

byte

1

r

1 immediate byte

pushes 16-bit number on stack. takes one immediate byte as number in code stream (resulting number is 16-bit representation of 0 - 255).

number

2

r

2 immediate bytes

pushes 16-bit number on stack. takes two immediate bytes as number in code stream.

list

3

c

0

"start of list"; opens a code block

eol

4

c

0

"end of list"; closes a code block

eolr

5

r

0

"end of list reporter"; closes a code block that will return a value (e.g, for when, waituntil)

lthing

6

r

1

"local thing"; uses stack frame to retrieve proc args

stop

7

c

0

stops currently running procedure, returning control to caller

output

8

c

1

stops currently running procedure, returning value to caller

repeat

9

c

2

repeats block for specific number of times

if

10

c

2

if input expression is true, executes block

ifelse

11

c

3

if input expression is true, executes block1, else executes block2

beep

12

c

0

causes Cricket to beep

note

13

c

2

makes Cricket play note of specified pitch and duration

waituntil

14

c

1

repeatedly executes block until it evaluates to true

loop

15

c

1

indefinitely executes block

wait

16

c

1

waits for specified time period

timer

17

r

0

returns value of free-running timer

resett

18

c

0

resets free-running timer to zero

send

19

c

1

sends specified value out IR port, with 0.1 sec trailing delay

ir

20

r

0

reports value of most recently received IR byte

newir?

21

r

0

reports whether IR value has been received since last call to "ir"

random

22

r

0

reports pseudorandom 16 bit value

+

23

r

2

reports sum of two inputs

-

24

r

2

reports difference of two imputs

*

25

r

2

reports product of two inputs

/

26

r

2

reports division of two inputs

%

27

r

2

reports remainder of division of two inputs

=

28

r

2

reports boolean equality of two inputs

>

29

r

2

reports boolean "greater than" of two inputs

<

30

r

2

reports boolean "less than" of two inputs

and

31

r

2

reports bitwise/logical AND of two inputs

or

32

r

2

reports bitwise/logical OR of two inputs

xor

33

r

2

reports bitwise XOR of two inputs

not

34

r

1

reports bitwise/logical NOT of two inputs

setglobal

35

c

2

sets numbered global to value

global

36

r

1

reports value of numbered global

aset

37

c

3

sets numbered array with numbered element to value

aget

38

r

2

reports value of numbered element of numbered array

record

39

c

1

records 8-bit value into data buffer

recall

40

r

0

reports 8-bit value stored in data buffer

resetdp

41

c

0

resets data buffer pointer to zero

setdp

42

c

1

sets data buffer pointer to specified value

erase

43

c

1

erases data point

when

44

c

2

sets up daemon with condition block and action block

whenoff

45

c

0

terminates "when" daemon

a,

46

c

0

selects motor A for control

b,

47

c

0

selects motor B for control

ab,

48

c

0

selects motors A and B

on

49

c

0

turns on selected motor(s)

onfor

50

c

1

turns on selected motor(s) for specified period of time

off

51

c

0

turns off selectec motor(s)

thisway

52

c

0

sets selected motor(s) direction to green LED

thatway

53

c

0

sets selected motor(s) direction to red LED

rd

54

c

0

reverses direction of selected motor(s)

sensora

55

r

0

reports value of sensor A

sensorb

56

r

0

reports value of sensor B

switcha

57

r

0

reports boolean value of sensor A

switchb

58

r

0

reports boolean value of sensor B

setpower

59

c

1

sets power level of selected motor(s)

brake

60

c

0

sets selected motor(s) for active braking

bsend

61

c

1

sends value out Bus Port

bsr

62

r

1

sends value out Bus Port and reports reply

c,

63

c

0

selects bus motor C for control

d,

64

c

0

selects bus motor D for control

cd,

65

c

0

selects bus motors C and D for control

abcd,

66

c

0

selects all motors for control

fastsend

67

c

1

sends specified value out IR port without trailing delay

stop!

68

c

0

terminates execution completely

eb

69

r

1

"examine byte"; reports value of Cricket memory at specified address

db

70

c

2

"deposit byte"; pokes byte into Cricket memory

Notes

Compilation Examples

This code...

compiles to...

   

onfor 20 beep

1 20 50 12 0

repeat 10 [onfor 20 beep]

1 10 3 5 1 20 50 12 4 9 0

send 3 + 7

1 3 1 7 23 19 0

send 1000 / 12

2 3 232 1 12 26 19 0

if switcha [onfor 11]

57 3 4 1 11 50 4 10 0

 

Appendix F

Nintendo Color Gameboy Specifications