Core War
撰文/Glenn Strong 翻译/Bolidecaster
Core War游戏是由A.K. Dewdney在1983年发明、并由他和David Jones在Western Ontario大学实现的。在1984年5月号的《科学美国人》上发表的一篇文章使这一游戏得以广为人知,自那以后,Core War已经变得更为复杂精深了。
在Core War游戏中,用专门的汇编语言(称为redcode)编写的程序在一台模拟的计算机上执行。每个程序对象都试图使运行在模拟器上的其他程序丧失运行能力,同时尽力使自己免遭同样的命运。可以通过将非法指令写到其他程序将要执行的内存位置中来实现这样的攻击。
1 模拟器
redcode的模拟器通常被称为MARS(Memory Array Redcode Simulator,内存阵列redcode模拟器)。MARS通过维护一个表示所模拟计算机的内存(或“core”)的阵列来进行运作;除了进行比赛的程序而外,其余部分都是空的。阵列中的每个位置可以存储一条redcode指令。程序自身无法确定它们在内存中的位置,因为redcode中所有的寻址都是“相对于指令”的,而且内存是循环的(参见下面的redcode部分以了解寻址设计)。执行模型很简单:以轮换的方式,每个程序被允许一次执行一条指令(也即,程序1执行一条指令,然后是程序2,然后程序3;当所有程序都执行了一次,程序1就执行第二条指令,如此等等)。通常,每个程序将执行它的第一条指令,接着是内存中的下一条指令,然后是再下一条,等等(但是有些指令可以改变这一次序)。
每个程序都从内存中的一个随机位置开始执行,两个程序之间的间距不少于1000个地址。每次游戏都有时间限制,如果时间已到所有程序仍在执行,就宣布游戏为平局。
2 redcode
redcode是一种简单的类似于汇编语言的低级语言。其中的指令可用于从一个内存位置移动到另一个、执行简单的算术运算、以及改变程序的执行次序,等等。每条redcode指令都有三个域,尽管有些指令只使用其中两个。这三个域对应于:(i)指令自身及其寻址模式。(ii)A域和B域,含有指令在其上进行操作的地址。未用域将包含零值。
2.1 指令
每条redcode指令都取一或两个值作为参数。这些值通常为地址,指定指令在机器内存的何处查找值或放置值。每个地址的寻址方式可为直接寻址(没有前缀,或有一个$符号),意即地址自身就是目标;间接寻址(以@为前缀),意即由地址的内容指定目标地址;或立即寻址(以#为前缀),意即地址应被当作整数处理。
redcode的一个不寻常的特性是所有指令都是相对于当前指令来被计算的。所以指令JMP 1意即“跳到下一个位置”(这实际上是模拟器的缺省行为,所以这条指令的实际含义是“什么也不做”)。类似地,ADD #1 –1意即“将整数1加到前一个位置的内容上”。同样不寻常的是机器的内存是循环的(也即,所有地址都以内存大小为模来计算)。这意味着如果最后的实际内存位置是8000(常用大小),试图访问位置8000+1事实上将得到位置零。这一特性使得程序可以忽略到达机器内存的边界时所发生的特殊情况。
(译者注:下面介绍的指令仅是现有redcode指令集的一部分。)
DAT 仅有B域。如果DAT指令被执行,它将导致程序的终止。指令的B域可用于存储一个数字值。这条指令等价于地址中的零值(被程序用于放置“零炸弹”的特性)。
MOV 有A域和B域。MOV指令将A域所指示的位置的全部内容复制到B域所指示的位置中。
ADD 有A域和B域。取得A域的内容,并将其加到B域的内容上。redcode中的所有算术运算都以内存中的位置个数为模来执行。
SUB 有A域和B域。取得A域的内容,将B域的内容减去所得的值。redcode中的所有算术运算都以内存中的位置个数为模来执行。
JMP 仅有A域。这是一条跳转指令,它使得A域中所指引的指令成为下一条被执行的指令。
JMZ 有A域和B域。这是一条有条件跳转指令。如果B域所指示的值为零,就跳转到A域所指示的指令;否则就什么也不做。
JMG 有A域和B域。这条指令与JMZ指令类似,但条件是B域所指示的值必须大于零。
DJZ 有A域和B域。最为复杂的跳转指令。它将B域指示的值减1,如果B域指示的数已变为零,就跳转到A域所指示的指令。
CMP 有A域和B域。比较A域和B域所指示的值,如果它们不相等,就跳过下一条指令。
3 例子
最小的Core War武士(Warrior)是Imp:
MOV 0 1
此程序将地址零(因为redcode中所用的相对寻址,地址零也即当前位置)的内容复制到地址1。换句话说,它将自身复制到下一个位置。因为当前位置的下一个位置也正是要被执行的下一个位置,所以程序的执行的效果就是用MOV 0 1指令的复本来填充内存。
Dwarf是一个更为老辣的程序:
ADD #5 3
MOV #0 @2
JMP -2
DAT -1
它在内存中放置“零炸弹”。表1显示了正在执行的Dwarf程序的部分跟踪结果。需要注意的要点是:
* 机器中的其他程序各自在此程序执行的每一步间执行一步。此跟踪假定没有指令修改了任何在该表中可见的内存。
* 使用MOV来放置零到一个位置中等价于在那里放置了一条DAT 0指令。任何试图执行该语句的程序都将被终止。
* @变址模式允许一个位置中的值被用作间接访问(有点像C中的指针)。
* 最后的JMP –2语句意为程序将持续地、每5个内存位置地放置DAT 0语句。因为该程序小于5条指令长,只要内存的总大小是5的倍数,它就永远也不会改写它自己。
地址 第1步 第2步 第3步 第4步
1000 ADD #5 3 ADD #5 3 ADD #5 3 ADD #5 3
1001 MOV #0 @2 MOV #0 @2 MOV #0 @2 MOV #0 @2
1002 JMP –2 JMP –2 JMP –2 JMP –2
1003 DAT -1 DAT 4 DAT 4 DAT 4
1004
1005 DAT 0 DAT 0
4 更多信息
原始的Core War文章可在http://www.koth.org/info/akdewdney/找到。在此站点上还有Core War的相关文档和链接,并可下载可用于Windows和UNIX的pMARS(portable MARS,可移植MARS)软件;同时,它还为来自全世界的Core War武士提供了进行角斗的“山头”。