BOX-256 游戏说明
==============

BOX-256 是一款 8 位的虚拟汇编器,它一共有 256 字节内存,16x16的方格显示(16色)。它也是一个编程挑战,玩家可以通过编写和优化代码使其通过图形测试。 玩家的最终目标是通过采用多线程和其他优化技巧,使用尽可能少的 CPU 周期或代码行来实现给定的图案。

游戏玩法

玩家通过编写源代码,执行代码并尝试让自己的输出(顶部屏幕)与目标图形(底部屏幕)匹配,并尽可能使用最少的 CPU 周期。

当计算机未执行时,编译器会主动尝试将源代码编译为机器代码。这可以看作是内存在改变,而用户正在编写代码。可以使用 PLAY 按钮从底​​部开始执行。它会一直持续下去,除非 a) 玩家用STOPSTEP停止它或 b) 输出屏幕与目标匹配并且关卡通过。

如果您想要更多挑战,请使用PREVNEXT按钮选择其他目标图形。

保存和加载您的代码

不幸的是,WebGL不直接支持剪贴板,但游戏使用弹出窗口让您能够间接导入和导出代码。

单击SAVE按钮将打开一个弹出窗口,其中包含您的整个源代码,以便您可以将其复制到剪贴板中。

加载源代码的方式类似。单击LOAD按钮您会看到一个弹出窗口,您可以在其中粘贴代码并单击加载按钮将其移动到游戏中。

编辑

SHIFT+箭头键让你选择代码和CTRL-C & CTRL-V 进行复制粘贴。CTRL-Z 和 CTRL-Y 用于撤消和重做。也可以使用插入/删除/退格/回车来移动内容。对于专业玩家和键盘布局怪异的人,'@' 和 '*' 字符也可以用逗号 (,) 和句点 (.) 热键键入。

将鼠标悬停在内存或输出屏幕上可为您提供当前悬停插槽的地址。

固定宽度指令集

源代码被编译为 4 字节块的机器代码。换句话说,机器码是基于固定宽度的指令集。然而,有些指令并不使用所有的 4 个字节。 聪明的玩家可以利用这一点。请注意,即使设置了固定宽度,也可以在内存中的任何位置强制程序计数器,而不管机器代码对齐如何。

程序计数器

执行开始时,最后一个内存槽(FF)用作程序计数器(PC)。如果启动第二个线程,额外的 PC 将在 FE 中,第三个将在 FD 中,依此类推。由于没有内存保护,可以直接写入PC,导致执行跳转。该行为与使用JMP指令相同。

多线程

可以使用THR指令启动多个线程。这些线程并不是真正同时执行的,它们是一个接一个地执行的。出于记分目的,它被视为单个周期。每个线程的内存读取是相同的,并且基于循环开始时缓存的内存状态。对于对同一内存地址的冲突写入,最后写入的线程是赢家。

由于缓存读取,一个线程通常不可能同时与另一个线程通信。然而,仍然存在两个例外:较早的线程可以写入稍后由较晚线程执行的内存地址。所以自修改代码是可能的。较早的线程也可以写入较晚线程拥有的程序计数器 (PC) 以导致立即跳转。 内存地址

源代码中有三个不同的前缀:

0 = 常量值
@ = 在内存中的地址
* = 指向另一个地址的地址 (又被称为指针)

例子:

地址: 00 01 02 03 04 05 06 07 08 09 0A 0B ...
值:  00 1A 22 2A 5B 23 4A 28 00 03 BB CA ...

009 evaluates to 09
@09 evaluates to 03
*09 evaluates to 2A

内存保护

没有任何内存保护。所有可执行的机器代码、您自己的变量和程序计数器愉快地共存于同一个 00-FF 内存空间中。你可以随时随地阅读和写作,只要你忍受后果:)。


指示

该语言由 11 条可能的指令组成。这些被编译器编译成 120 个不同的操作码。大多数不同的操作码只是同一条指令的不同排列。

MOV

MOV A B C

将内存地址 B的值设为A。

当一次移动多个值时,参数 C 可用作数组的长度。

MOV 025 @DE 000 // 将值 25 移动到内存地址 @DE
MOV @10 @20 000 // 将值从内存地址 @10 移动到内存地址 @20
MOV @A0 @B0 007 // 从地址 @ 开始移动七个值A0 进入地址@B0
MOV *30 @40 000 // 将@30 中的值指向的地址中的值移动到地址@40
MOV 007 @10 004 // 将常量值7 设置四次,从地址@10 开始。

JMP

JMP A

将执行跳转到 A。

如果 A 是常量,则它是当前执行地址的偏移量,否则视为绝对地址。通常跳入 4 的乘数是一个好主意,因为指令集是固定宽度的,而下一条指令(通常)位于接下来的 4 个字节中。

请注意,您可以在 A 前面加上减号“-”以获得负值。它们在机器代码中不会被编译为负数,但 JMP -1 会编译为 JMP FF。这是有效的,因为内存正好是 256 字节长,跳转 FF 意味着跳过整个内存并登陆到前一个内存地址。

JMP 008 000 000 // 向前跳跃 8 步。基本上跳过下一条指令。
JMP -0C 000 000 // 向后跳 12 步。基本上跳回三个指令。
JMP @A0 000 000 // 直接跳转到内存地址@A0。
JMP *C0 000 000 // 跳转到@C0 中的值所指向的内存地址。

PIX

PIX A B

将像素输出到索引 A 中,颜色为 B。

屏幕为 16x16,由 256 个像素组成。索引从左到右,从上到下增长。左上角的索引是 0,右下角是 255。可用的颜色是从 0 到 F,但是输出大于这个值的值是可以的,因为调色板会不断循环。

PIX 020 009 000 // 将颜色 09 输出到像素索引 20。
PIX @10 009 000 // 将颜色 09 输出到由 @10 中的值指向的像素。

JEQ

JEQ A B C

如果 A 等于 B,则执行跳转到 C。

如果 C 是一个常数,它是当前执行地址的一个偏移量,否则它被认为是一个绝对地址。通常跳入 4 的乘数是一个好主意,因为指令集是固定宽度的,而下一条指令(通常)位于接下来的 4 个字节中。

请注意,您可以在 C 前面加上减号“-”以获得负值。它们在机器代码中不会被编译为负数,但 JMP -1 会编译为 JMP FF。这是有效的,因为内存正好是 256 字节长,跳转 FF 意味着跳过整个内存并登陆到前一个内存地址。

JEQ 020 @30 008 // 如果@30 的值为 20,则跳过下一条指令(8 个字节)。
JEQ @A0 @B0 -04 // 如果@A0 中的值等于@B0,则跳回上一条指令。

JNE

JNE A B C

如果 A 不等于 B,则执行跳转到 C。

如果 C 是一个常数,它是当前执行地址的一个偏移量,否则它被认为是一个绝对地址。通常跳入 4 的乘数是一个好主意,因为指令集是固定宽度的,而下一条指令(通常)位于接下来的 4 个字节中。

请注意,您可以在 C 前面加上减号“-”以获得负值。它们在机器代码中不会被编译为负数,但 JMP -1 会编译为 JMP FF。这是有效的,因为内存正好是 256 字节长,跳转 FF 意味着跳过整个内存并登陆到前一个内存地址。

JNE 020 @30 008 // 如果@30 没有值 20,则跳过下一条指令(8 个字节)。
JNE @A0 @B0 -04 // 如果@A0 中的值不等于@B0,则跳回上一条指令。

JGR

JGR A B C

如果 A 大于 B,则执行跳转到 C。

如果 C 是一个常数,它是当前执行地址的一个偏移量,否则它被认为是一个绝对地址。通常跳入 4 的乘数是一个好主意,因为指令集是固定宽度的,而下一条指令(通常)位于接下来的 4 个字节中。

请注意,您可以在 C 前面加上减号“-”以获得负值。它们在机器代码中不会被编译为负数,但 JMP -1 会编译为 JMP FF。这是有效的,因为内存正好是 256 字节长,跳转 FF 意味着跳过整个内存并登陆到前一个内存地址。

JGR @30 010 -04 // 如果@30 中的值大于10,则跳回上一条指令。
JGR 020 @30 -04 // 如果@30 中的值小于或等于 20,则向前跳 4 步(16 字节)。

FLP

FLP A B C

翻转内存地址 A 和内存地址 B 之间的值。

当一次翻转多个值时,参数 C 可用作数组的长度。

FLP @25 @DE 000 // 在内存位置@25 和@DE 之间翻转值
FLP @10 @20 004 // 在从@10 和@20 开始的内存空间之间翻转4个值

THR

THR A

从内存地址 A 开始一个新线程

这意味着您希望新线程开始执行的可执行机器代码指令位于内存地址 A 中。

THR @20 000 000 //从内存地址@20 开始执行新线程
THR *10 000 000 // 从@10 中的值指向的内存地址开始执行新线程

ADD

ADD A B C

将值 A 和 B 相加并将结果存储在 C 中

ADD @20 007 @89 //将地址@20 中的值加上7并将结果存入@89
ADD @10 @20 @30 // 将地址@10 和@20 中的值相加并存入@30

SUB

SUB A B C

从 A 中减去 B 并将结果存储在 C

SUB @20 007 @89 // 从@20 中的值减去7并将结果存储在@89
SUB @10 @20 @30 // 从@10 中的值减去@20 中的值并将结果存储在@ 30

MUL

MUL A B C

将值 A 和 B 相乘并将结果存储在 C

MUL @20 007 @89 //将地址@20 中的值与7相乘并将结果存储在@89
MUL @10 @20 @30 //将地址@10和@20中的值相乘并存储结果在地址@30

DIV

DIV A B C

将 A 与 B 相除并将结果存储在 C 中

除零始终计算为 0。

DIV @20 007 @89 // 将@20 中的值与7相除并将结果存储在地址@89
DIV @10 @20 @30 // 将@10 中的值与@20 中的值相除并存储结果在地址@30

MOD

MOD A B C

取A和B的模并将结果放在C中

零 (0) 的模数始终计算为 0。

MOD @20 007 @89 // 取@20 和七(7) 的模并将结果存储在@89
MOD @10 @20 @30 // 取@10 和@20 的模并将结果存储在@30



操作码

源代码中的指令在内存中被编译成机器码操作码。这些操作码是计算机执行的实际命令。专家级玩家可以通过将这些值写入编辑器来直接编写程序。也可以在运行时更改已执行的内存,因此如果您知道不同操作码的作用,一切皆有可能。

大多数操作码是相同指令的排列。例如,ADD 指令有 8 个不同的操作码,具体取决于参数的“内存深度”。

存在三种不同的参数“深度”。常量 (0)、地址 (@) 和指针 (*)。

操作码操作说明P1P2P3例子
00000 (NOP)010000 000 000 000
 
01MOV0@0MOV 001 @02 003
02MOV0*0MOV 001 *02 003
03MOV@@0MOV @01 @02 003
04MOV@*0MOV @01 *02 003
05MOV*@0MOV *01 @02 003
06MOV**0MOV *01 *02 003
07MOV0@@MOV 001 @02 @03
08MOV0*@MOV 001 *02 @03
09MOV@@@MOV @01 @02 @03
0AMOV@*@MOV @01 *02 @03
0BMOV*@@MOV *01 @02 @03
0CMOV**@MOV *01 *02 @03
0DMOV0@*MOV 001 @02 *03
0EMOV0**MOV 001 *02 *03
0FMOV@@*MOV @01 @02 *03
10MOV@**MOV @01 *02 *03
11MOV*@*MOV *01 @02 *03
12MOV***MOV *01 *02 *03
 
13ADD@0@ADD @01 002 @03
14ADD*0@ADD *01 002 @03
15ADD@@@ADD @01 @02 @03
16ADD*@@ADD *01 @02 @03
17ADD@*@ADD @01 *02 @03
18ADD*0*ADD *01 002 *03
19ADD@0*ADD @01 002 *03
1AADD*@*ADD *01 @02 *03
1BADD@@*ADD @01 @02 *03
1CADD***ADD *01 *02 *03
 
1DSUB@0@SUB @01 002 @03
1ESUB0@@SUB 001 @02 @03
1FSUB@@@SUB @01 @02 @03
20SUB*0@SUB *01 002 @03
21SUB0*@SUB 001 *02 @03
22SUB*@@SUB *01 @02 @03
23SUB@*@SUB @01 *02 @03
24SUB**@SUB *01 *02 @03
25SUB@0*SUB @01 002 *03
26SUB0@*SUB 001 @02 *03
27SUB@@*SUB @01 @02 *03
28SUB*0*SUB *01 002 *03
29SUB0**SUB 001 *02 *03
2ASUB*@*SUB *01 @02 *03
2BSUB@**SUB @01 *02 *03
2CSUB***SUB *01 *02 *03
 
2DJEQ@00JEQ @01 002 003
2EJEQ@@0JEQ @01 @02 003
2FJEQ*00JEQ *01 002 003
30JEQ*@0JEQ *01 @02 003
31JEQ**0JEQ *01 *02 003
32JEQ@0@JEQ @01 002 @03
33JEQ@@@JEQ @01 @02 @03
34JEQ*0@JEQ *01 002 @03
35JEQ*@@JEQ *01 @02 @03
36JEQ**@JEQ *01 *02 @03
37JEQ@0*JEQ @01 002 *03
38JEQ@@*JEQ @01 @02 *03
39JEQ*0*JEQ *01 002 *03
3AJEQ*@*JEQ *01 @02 *03
3BJEQ***JEQ *01 *02 *03
 
3CMUL@0@MUL @01 002 @03
3DMUL@@@MUL @01 @02 @03
3EMUL*0@MUL *01 002 @03
3FMUL*@@MUL *01 @02 @03
40MUL**@MUL *01 *02 @03
41MUL@0*MUL @01 002 *03
42MUL@@*MUL @01 @02 *03
43MUL*0*MUL *01 002 *03
44MUL*@*MUL *01 @02 *03
45MUL***MUL *01 *02 *03
 
46DIV@0@DIV @01 002 @03
47DIV0@@DIV 001 @02 @03
48DIV@@@DIV @01 @02 @03
49DIV*0@DIV *01 002 @03
4ADIV*@@DIV *01 @02 @03
4BDIV@*@DIV @01 *02 @03
4CDIV**@DIV *01 *02 @03
4DDIV@0*DIV @01 002 *03
4EDIV0@*DIV 001 @02 *03
4FDIV@@*DIV @01 @02 *03
50DIV*0*DIV *01 002 *03
51DIV*@*DIV *01 @02 *03
52DIV@**DIV @01 *02 *03
53DIV***DIV *01 *02 *03
 
54JMP0JMP 001 000 000
55JMP@JMP @01 000 000
56JMP*JMP *01 000 000
 
57JGR0@0JGR 001 @02 003
58JGR@00JGR @01 002 003
59JGR@@0JGR @01 @02 003
5AJGR@*0JGR @01 *02 003
5BJGR*00JGR *01 002 003
5CJGR*@0JGR *01 @02 003
5DJGR**0JGR *01 *02 003
5EJGR0@@JGR 001 @02 @03
5FJGR@0@JGR @01 002 @03
60JGR@@@JGR @01 @02 @03
61JGR@*@JGR @01 *02 @03
62JGR*0@JGR *01 002 @03
63JGR*@@JGR *01 @02 @03
64JGR**@JGR *01 *02 @03
65JGR0@*JGR 001 @02 *03
66JGR@0*JGR @01 002 *03
67JGR@@*JGR @01 @02 *03
68JGR@**JGR @01 *02 *03
69JGR*0*JGR *01 002 *03
6AJGR*@*JGR *01 @02 *03
6BJGR***JGR *01 *02 *03
 
6CPIX00PIX 001 002 000
6DPIX0@PIX 001 @02 000
6EPIX0*PIX 001 *02 000
6FPIX@0PIX @01 002 000
70PIX@@PIX @01 @02 000
71PIX@*PIX @01 *02 000
72PIX*0PIX *01 002 000
73PIX*@PIX *01 @02 000
74PIX**PIX *01 *02 000
 
75FLP@@0FLP @01 @02 003
76FLP*@0FLP *01 @02 003
77FLP**0FLP *01 *02 003
78FLP@@@FLP @01 @02 @03
79FLP*@@FLP *01 @02 @03
7AFLP**@FLP *01 *02 @03
7BFLP@@*FLP @01 @02 *03
7CFLP*@*FLP *01 @02 *03
7DFLP***FLP *01 *02 *03
 
7ETHR0THR 001 000 000
7FTHR@THR @01 000 000
80THR*THR *01 000 000
 
81MOD@0@MOD @01 002 @03
82MOD0@@MOD 001 @02 @03
83MOD@@@MOD @01 @02 @03
84MOD*0@MOD *01 002 @03
85MOD0*@MOD 001 *02 @03
86MOD*@@MOD *01 @02 @03
87MOD@*@MOD @01 *02 @03
88MOD**@MOD *01 *02 @03
89MOD@0*MOD @01 002 *03
8AMOD0@*MOD 001 @02 *03
8BMOD@@*MOD @01 @02 *03
8CMOD*0*MOD *01 002 *03
8DMOD0**MOD 001 *02 *03
8EMOD*@*MOD *01 @02 *03
8DMOD@**MOD @01 *02 *03
90MOD***MOD *01 *02 *03
 
91JNE@00JNE @01 002 003
92JNE@@0JNE @01 @02 003
93JNE*00JNE *01 002 003
94JNE*@0JNE *01 @02 003
95JNE**0JNE *01 *02 003
96JNE@0@JNE @01 002 @03
97JNE@@@JNE @01 @02 @03
98JNE*0@JNE *01 002 @03
99JNE*@@JNE *01 @02 @03
9AJNE**@JNE *01 *02 @03
9BJNE@0*JNE @01 002 *03
9CJNE@@*JNE @01 @02 *03
9DJNE*0*JNE *01 002 *03
9EJNE*@*JNE *01 @02 *03
9FJNE***JNE *01 *02 *03
 
A0DIV0*@DIV 001 *02 @03
A1DIV0**DIV 001 *02 *03