BOX-256 is a 8-bit fantasy computer, with 256 bytes of memory, 16 color 16x16 display. It is also a programming game, where the player tries to pass the graphics tests and optimize the code to perfection. The ultimate goal is to use as few CPU cycles or lines of code as possible, by employing multithreading and other optimization tricks.
Gameplay
Player writes the source code, executes the machine and tries to match the target output (bottom screen), with program output (top screen), with least amount of CPU cycles possible.
When the computer is not executing, the compiler tries actively to compile the source to machine code. This can be seen as memory changing, while the user is writing code. The execution can be started from the bottom using the PLAY button. It goes on forever, unless a) player stops it with STOP or STEP or b) the output screen matches the target and the level is passed.
If you want more challenge, cycle through the available levels with PREV and NEXT buttons.
Saving and Loading Your CodeUnfortunately WebGL doesn't directly support clipboard, but the game uses popup windows to give you ability to import & export code indirectly.
In the web version, clicking the SAVE button will open up a popup with your entire source code in it so you can copy it into your clipboard. In the standalone version there is no popup, but the source code is moved straight into clipboard.
Loading works similarly. In the web version you'll get a popup where you can paste your code and click load button to move it into the game. In the standalone version, clicking LOAD tries to load whatever text is in your clipboard currently.
NOTE: In the web version, drag-selecting text with mouse doesn't work. You have to use SHIFT+arrow keys to do it.
EditorSHIFT+arrow keys lets you select code and CTRL-C & CTRL-V for copy-pasting. CTRL-Z and CTRL-Y for undo and redo. Use insert/delete/backspace/enter to move stuff around, too. For the expert players and people with weird keyboard layouts, the '@' and '*' characters can be also typed with comma (,) and period (.) hotkeys.
Hovering over memory or output screen gives you the address of the currently hovered slot.
Fixed Width Instruction SetSource code is compiled to machine code in 4 byte chunks. In other words, the machine code is based on a fixed width instruction set. However, some instructions do not use all of the 4 bytes. Clever player may use this to her advantage. Note that even with the fixed width set, it is possible to force the program counter anywhere in memory, regardless of machine code alignment.
Program CounterWhen the execution starts, the last memory slot (FF) is used as a program counter (PC). If a second thread is started, the additional PC will be in FE, the third will be in FD and so on. Since there is no memory protection, it is possible to write into PC directly and cause execution to jump. The behaviour is identical to using JMP instruction.
MultithreadingIt is possible to start multiple threads with THR instruction. The threads are not really simultaneous and they are executed one after another. It is counted as single cycle for score keeping purposes. The memory reads for each thread are identical and based on the memory state cached at the beginning of the cycle. For conflicting writes to same memory address, the thread that writes last is the winner.
Because of cached reads, it is generally not possible for one thread to communicate with other thread simultaneously. HOWEVER two exceptions remains: An earlier thread can write to memory address later executed by later thread. So self-modifying code is possible. An earlier thread can also write to program counter (PC) owned by a later thread to cause an immediate jump.
Memory Addressing
There are three different prefixes in source code:
0 = Constant value
@ = Address in memory
* = Address pointed by another address (a.k.a pointer)
Example:
There is no memory protection whatsoever. All executable machine code, your own variables and program counters happily co-exist in the same 00-FF memory space. You can read and write everywhere and everytime, as long as you live with the consequences :).
Creating your own levels
Creating your own level is done by using the BOX-256 computer. You need to make a program that outputs the new level into output display, and then press the URL button.
Popup will open that has an URL to the new level which is based on your current output display.
NOTE: You can pause execution during playback pressing STEP!
You can share the URL to other players. Clicking the URL opens the game with only your custom level available, titled as "CUSTOM".
In the standalone version, there is an extra button OPEN. By first copying the URL of a custom level into clipboard, and then clicking OPEN, you can open a custom level.
The language consists of 11 possible instructions. These are compiled into 120 different opcodes by the compiler. Most of the different opcodes are just different permutations of the same instruction.
MOVMOV A B C
Sets the value A into memory address B.
The parameter C can be used as length for arrays, when multiple values are moved at once.
JMP A
Jumps the execution into A.
If A is a constant, it is an offset from current execution address, otherwise it is considered as an absolute address. It is normally a good idea to jump in multipliers of 4, because the instruction set is fixed width and the next instruction (usually) sits in the next 4 bytes.
Note that you can prefix the A with minus '-' to get negative values. They are not compiled negative in machine code, but JMP -1 compiles to JMP FF. This works, because the memory is exactly 256 bytes long and jumping FF means jumping through the entire memory and landing on to the previous memory address.
PIX A B
Outputs a pixel into index A with color B.
The screen is 16x16 consisting of 256 pixels. The index grows left to right, top to bottom. The top-left index is 0, and bottom-right is 255. The available colors are from 0 to F, but it is fine to output value bigger than that, since the palette keeps cycling through.
JEQ A B C
If A equals B, the execution jumps to C.
If C is a constant, it is an offset from current execution address, otherwise it is considered as an absolute address. It is normally a good idea to jump in multipliers of 4, because the instruction set is fixed width and the next instruction (usually) sits in the next 4 bytes.
Note that you can prefix the C with minus '-' to get negative values. They are not compiled negative in machine code, but JMP -1 compiles to JMP FF. This works, because the memory is exactly 256 bytes long and jumping FF means jumping through the entire memory and landing on to the previous memory address.
JNE A B C
If A doesn't equal B, the execution jumps to C.
If C is a constant, it is an offset from current execution address, otherwise it is considered as an absolute address. It is normally a good idea to jump in multipliers of 4, because the instruction set is fixed width and the next instruction (usually) sits in the next 4 bytes.
Note that you can prefix the C with minus '-' to get negative values. They are not compiled negative in machine code, but JMP -1 compiles to JMP FF. This works, because the memory is exactly 256 bytes long and jumping FF means jumping through the entire memory and landing on to the previous memory address.
JGR A B C
If A is greater than B, the execution jumps to C.
If C is a constant, it is an offset from current execution address, otherwise it is considered as an absolute address. It is normally a good idea to jump in multipliers of 4, because the instruction set is fixed width and the next instruction (usually) sits in the next 4 bytes.
Note that you can prefix the C with minus '-' to get negative values. They are not compiled negative in machine code, but JMP -1 compiles to JMP FF. This works, because the memory is exactly 256 bytes long and jumping FF means jumping through the entire memory and landing on to the previous memory address.
FLP A B C
Flips the values between memory address A and memory address B.
The parameter C can be used as length for arrays, when multiple values are flipped at once.
THR A
Starts a new thread from memory address A
This means that the executable machine code instruction you want the new thread to start executing is sitting in memory address A.
ADD A B C
Adds values A and B together and stores the result in C
SUB A B C
Subtracts B from A and stores the result in C
MUL A B C
Multiplies values A and B together and stores the result in C
DIV A B C
Divides A with B and stores the result in C
Division with zero (0) always evaluates to 0.
MOD A B C
Take modulo of A and B and place the result in C
Modulo with zero (0) always evaluates to 0.
The instructions in source code are compiled into machine code opcodes in the memory. These opcodes are the actual commands that the computer executes. An expert player can code programs directly by writing these values into the editor. It is also possible to change the executed memory at runtime, so everything is possible if you know what the different opcodes do.
Most of the opcodes are permutations of the same instruction. For example the ADD instruction has 8 different opcodes, depending on the 'memory depth' of the parameters.
There are three different 'depths' of parameter. Constant (0), Address (@) and pointer (*).
OPCODE | Instruction | P1 | P2 | P3 | Example |
00 | 000 (NOP) | 0 | 1 | 0 | 000 000 000 000 |
01 | MOV | 0 | @ | 0 | MOV 001 @02 003 |
02 | MOV | 0 | * | 0 | MOV 001 *02 003 |
03 | MOV | @ | @ | 0 | MOV @01 @02 003 |
04 | MOV | @ | * | 0 | MOV @01 *02 003 |
05 | MOV | * | @ | 0 | MOV *01 @02 003 |
06 | MOV | * | * | 0 | MOV *01 *02 003 |
07 | MOV | 0 | @ | @ | MOV 001 @02 @03 |
08 | MOV | 0 | * | @ | MOV 001 *02 @03 |
09 | MOV | @ | @ | @ | MOV @01 @02 @03 |
0A | MOV | @ | * | @ | MOV @01 *02 @03 |
0B | MOV | * | @ | @ | MOV *01 @02 @03 |
0C | MOV | * | * | @ | MOV *01 *02 @03 |
0D | MOV | 0 | @ | * | MOV 001 @02 *03 |
0E | MOV | 0 | * | * | MOV 001 *02 *03 |
0F | MOV | @ | @ | * | MOV @01 @02 *03 |
10 | MOV | @ | * | * | MOV @01 *02 *03 |
11 | MOV | * | @ | * | MOV *01 @02 *03 |
12 | MOV | * | * | * | MOV *01 *02 *03 |
13 | ADD | @ | 0 | @ | ADD @01 002 @03 |
14 | ADD | * | 0 | @ | ADD *01 002 @03 |
15 | ADD | @ | @ | @ | ADD @01 @02 @03 |
16 | ADD | * | @ | @ | ADD *01 @02 @03 |
17 | ADD | @ | * | @ | ADD @01 *02 @03 |
18 | ADD | * | 0 | * | ADD *01 002 *03 |
19 | ADD | @ | 0 | * | ADD @01 002 *03 |
1A | ADD | * | @ | * | ADD *01 @02 *03 |
1B | ADD | @ | @ | * | ADD @01 @02 *03 |
1C | ADD | * | * | * | ADD *01 *02 *03 |
1D | SUB | @ | 0 | @ | SUB @01 002 @03 |
1E | SUB | 0 | @ | @ | SUB 001 @02 @03 |
1F | SUB | @ | @ | @ | SUB @01 @02 @03 |
20 | SUB | * | 0 | @ | SUB *01 002 @03 |
21 | SUB | 0 | * | @ | SUB 001 *02 @03 |
22 | SUB | * | @ | @ | SUB *01 @02 @03 |
23 | SUB | @ | * | @ | SUB @01 *02 @03 |
24 | SUB | * | * | @ | SUB *01 *02 @03 |
25 | SUB | @ | 0 | * | SUB @01 002 *03 |
26 | SUB | 0 | @ | * | SUB 001 @02 *03 |
27 | SUB | @ | @ | * | SUB @01 @02 *03 |
28 | SUB | * | 0 | * | SUB *01 002 *03 |
29 | SUB | 0 | * | * | SUB 001 *02 *03 |
2A | SUB | * | @ | * | SUB *01 @02 *03 |
2B | SUB | @ | * | * | SUB @01 *02 *03 |
2C | SUB | * | * | * | SUB *01 *02 *03 |
2D | JEQ | @ | 0 | 0 | JEQ @01 002 003 |
2E | JEQ | @ | @ | 0 | JEQ @01 @02 003 |
2F | JEQ | * | 0 | 0 | JEQ *01 002 003 |
30 | JEQ | * | @ | 0 | JEQ *01 @02 003 |
31 | JEQ | * | * | 0 | JEQ *01 *02 003 |
32 | JEQ | @ | 0 | @ | JEQ @01 002 @03 |
33 | JEQ | @ | @ | @ | JEQ @01 @02 @03 |
34 | JEQ | * | 0 | @ | JEQ *01 002 @03 |
35 | JEQ | * | @ | @ | JEQ *01 @02 @03 |
36 | JEQ | * | * | @ | JEQ *01 *02 @03 |
37 | JEQ | @ | 0 | * | JEQ @01 002 *03 |
38 | JEQ | @ | @ | * | JEQ @01 @02 *03 |
39 | JEQ | * | 0 | * | JEQ *01 002 *03 |
3A | JEQ | * | @ | * | JEQ *01 @02 *03 |
3B | JEQ | * | * | * | JEQ *01 *02 *03 |
3C | MUL | @ | 0 | @ | MUL @01 002 @03 |
3D | MUL | @ | @ | @ | MUL @01 @02 @03 |
3E | MUL | * | 0 | @ | MUL *01 002 @03 |
3F | MUL | * | @ | @ | MUL *01 @02 @03 |
40 | MUL | * | * | @ | MUL *01 *02 @03 |
41 | MUL | @ | 0 | * | MUL @01 002 *03 |
42 | MUL | @ | @ | * | MUL @01 @02 *03 |
43 | MUL | * | 0 | * | MUL *01 002 *03 |
44 | MUL | * | @ | * | MUL *01 @02 *03 |
45 | MUL | * | * | * | MUL *01 *02 *03 |
46 | DIV | @ | 0 | @ | DIV @01 002 @03 |
47 | DIV | 0 | @ | @ | DIV 001 @02 @03 |
48 | DIV | @ | @ | @ | DIV @01 @02 @03 |
49 | DIV | * | 0 | @ | DIV *01 002 @03 |
4A | DIV | * | @ | @ | DIV *01 @02 @03 |
4B | DIV | @ | * | @ | DIV @01 *02 @03 |
4C | DIV | * | * | @ | DIV *01 *02 @03 |
4D | DIV | @ | 0 | * | DIV @01 002 *03 |
4E | DIV | 0 | @ | * | DIV 001 @02 *03 |
4F | DIV | @ | @ | * | DIV @01 @02 *03 |
50 | DIV | * | 0 | * | DIV *01 002 *03 |
51 | DIV | * | @ | * | DIV *01 @02 *03 |
52 | DIV | @ | * | * | DIV @01 *02 *03 |
53 | DIV | * | * | * | DIV *01 *02 *03 |
54 | JMP | 0 | JMP 001 000 000 | ||
55 | JMP | @ | JMP @01 000 000 | ||
56 | JMP | * | JMP *01 000 000 | ||
57 | JGR | 0 | @ | 0 | JGR 001 @02 003 |
58 | JGR | @ | 0 | 0 | JGR @01 002 003 |
59 | JGR | @ | @ | 0 | JGR @01 @02 003 |
5A | JGR | @ | * | 0 | JGR @01 *02 003 |
5B | JGR | * | 0 | 0 | JGR *01 002 003 |
5C | JGR | * | @ | 0 | JGR *01 @02 003 |
5D | JGR | * | * | 0 | JGR *01 *02 003 |
5E | JGR | 0 | @ | @ | JGR 001 @02 @03 |
5F | JGR | @ | 0 | @ | JGR @01 002 @03 |
60 | JGR | @ | @ | @ | JGR @01 @02 @03 |
61 | JGR | @ | * | @ | JGR @01 *02 @03 |
62 | JGR | * | 0 | @ | JGR *01 002 @03 |
63 | JGR | * | @ | @ | JGR *01 @02 @03 |
64 | JGR | * | * | @ | JGR *01 *02 @03 |
65 | JGR | 0 | @ | * | JGR 001 @02 *03 |
66 | JGR | @ | 0 | * | JGR @01 002 *03 |
67 | JGR | @ | @ | * | JGR @01 @02 *03 |
68 | JGR | @ | * | * | JGR @01 *02 *03 |
69 | JGR | * | 0 | * | JGR *01 002 *03 |
6A | JGR | * | @ | * | JGR *01 @02 *03 |
6B | JGR | * | * | * | JGR *01 *02 *03 |
6C | PIX | 0 | 0 | PIX 001 002 000 | |
6D | PIX | 0 | @ | PIX 001 @02 000 | |
6E | PIX | 0 | * | PIX 001 *02 000 | |
6F | PIX | @ | 0 | PIX @01 002 000 | |
70 | PIX | @ | @ | PIX @01 @02 000 | |
71 | PIX | @ | * | PIX @01 *02 000 | |
72 | PIX | * | 0 | PIX *01 002 000 | |
73 | PIX | * | @ | PIX *01 @02 000 | |
74 | PIX | * | * | PIX *01 *02 000 | |
75 | FLP | @ | @ | 0 | FLP @01 @02 003 |
76 | FLP | * | @ | 0 | FLP *01 @02 003 |
77 | FLP | * | * | 0 | FLP *01 *02 003 |
78 | FLP | @ | @ | @ | FLP @01 @02 @03 |
79 | FLP | * | @ | @ | FLP *01 @02 @03 |
7A | FLP | * | * | @ | FLP *01 *02 @03 |
7B | FLP | @ | @ | * | FLP @01 @02 *03 |
7C | FLP | * | @ | * | FLP *01 @02 *03 |
7D | FLP | * | * | * | FLP *01 *02 *03 |
7E | THR | 0 | THR 001 000 000 | ||
7F | THR | @ | THR @01 000 000 | ||
80 | THR | * | THR *01 000 000 | ||
81 | MOD | @ | 0 | @ | MOD @01 002 @03 |
82 | MOD | 0 | @ | @ | MOD 001 @02 @03 |
83 | MOD | @ | @ | @ | MOD @01 @02 @03 |
84 | MOD | * | 0 | @ | MOD *01 002 @03 |
85 | MOD | 0 | * | @ | MOD 001 *02 @03 |
86 | MOD | * | @ | @ | MOD *01 @02 @03 |
87 | MOD | @ | * | @ | MOD @01 *02 @03 |
88 | MOD | * | * | @ | MOD *01 *02 @03 |
89 | MOD | @ | 0 | * | MOD @01 002 *03 |
8A | MOD | 0 | @ | * | MOD 001 @02 *03 |
8B | MOD | @ | @ | * | MOD @01 @02 *03 |
8C | MOD | * | 0 | * | MOD *01 002 *03 |
8D | MOD | 0 | * | * | MOD 001 *02 *03 |
8E | MOD | * | @ | * | MOD *01 @02 *03 |
8D | MOD | @ | * | * | MOD @01 *02 *03 |
90 | MOD | * | * | * | MOD *01 *02 *03 |
91 | JNE | @ | 0 | 0 | JNE @01 002 003 |
92 | JNE | @ | @ | 0 | JNE @01 @02 003 |
93 | JNE | * | 0 | 0 | JNE *01 002 003 |
94 | JNE | * | @ | 0 | JNE *01 @02 003 |
95 | JNE | * | * | 0 | JNE *01 *02 003 |
96 | JNE | @ | 0 | @ | JNE @01 002 @03 |
97 | JNE | @ | @ | @ | JNE @01 @02 @03 |
98 | JNE | * | 0 | @ | JNE *01 002 @03 |
99 | JNE | * | @ | @ | JNE *01 @02 @03 |
9A | JNE | * | * | @ | JNE *01 *02 @03 |
9B | JNE | @ | 0 | * | JNE @01 002 *03 |
9C | JNE | @ | @ | * | JNE @01 @02 *03 |
9D | JNE | * | 0 | * | JNE *01 002 *03 |
9E | JNE | * | @ | * | JNE *01 @02 *03 |
9F | JNE | * | * | * | JNE *01 *02 *03 |
A0 | DIV | 0 | * | @ | DIV 001 *02 @03 |
A1 | DIV | 0 | * | * | DIV 001 *02 *03 |