目录:
1. 简介
2. Mars体系结构
3. RedCode简单语法
4. 语法实例讲解
5. 简单实例
6. 相关资料
简介
程序大战也叫磁核大战,英文名为"CoreWars",是一个很古老的游戏,当年比尔.盖茨上学时就有这个东东了,不过国内玩的人很少。目前一些免费Unix如FreeBSD等的ports里就有他。
这个游戏可以从名字也能看出来就是程序之间的大赛。就是大家都写一个程序,然后我们把这些程序都放到内存,然后开始"并发"运行,看最终的结果是谁的程序消灭了内存中的其他程序而存活了下来。
这一程序对抗模型可以看作是原始的病毒对抗。每个程序都要尽力的消灭其他程序,为了能够生存,程序应该能保护自己,不断移动自身来躲过其他程序的攻击或当自身受到攻击后能自我修复。
当然这些程序不是运行在家用PC上,也不是随便使用大家常用的intel汇编来编写。其运行平台为Mars机。Mars是一种简单的计算机,他有固定的8000个内存单元,和约10来个指令。然后整个大战程序就是由这些指令完成。
Mars体系结构
Mars机器由一个组内存单元,一个CU单元,一个简单进程管理系统,和一组指令集构成。
Mars机的标准主要参考pMars虚拟机器标准,主要有两个:88标准和94标准。以下都是默认为88标准,扩展94标准会专门标识出来。
该系统内存大小固定为8000个内存单元,每一个内存单元由5个部分构成:
OPCODE区:操作数区,该区域指定了机器指令,如MOV ,ADD。
A数据区 :一个32位的数据存储区,存放相应数据,如128,-100。
A数据区寻址修饰:指定了A数据区的寻址方式。
B数据区。
B数据区寻址修饰。
( 94标准中加入了一个新的部分 操作数修饰区。 )
系统的寻址方式分为立即寻址,直接寻址,间接寻址。
( 94标准中加入了新的间接A减1寻址、间接A加1寻址、间接B减1寻址、间接B加1寻址)
系统寻址都是相对当前IP为基准寻址的,这是Mars系统和传统计算机最大的不同之处。如 mov 0,1
表示将当前指令所在的内存单元拷贝一份到当前指令所在内存单元的下一个内存单元。
CU单元用于执行相应的指令,配合CU单元还有一个隐含的寄存器IP,每次执行单元执行IP指定的内存单元的指令。
简单进程管理器管理进程。系统的进程由一个先进先出队列构成。每次管理系统将当前进程的执行地址出队列放入IP寄存器,然后CU单元执行指令,最后将该进程的下一条指令地址放入队尾。如果该进程产生新进程,则在队尾再加入新进程的起始执行地址。简单进程管理器保证每个进程轮流执行,当一个进程执行了DAT或者执行了0被除的操作则该进程结束。
系统指令包含:
DAT MOV ADD SUB JMP JMZ JMN CMP SLT DJN SPL
( 94标准中加入了新的指令: SUB MUL DIV MOD SEQ SNE NOP LDP STP )
Mars机目前你所见到的几乎都是软件模拟系统,模拟器模拟Mars系统工作。有的模拟器支持88标准,有的支持94标准,有的支持88标准并部分支持94标准,所以你在选择一个模拟器时应该好好看看它支持那个标准。
Mars机的汇编程序通常称为RedCode,而Mars模拟器几乎都集成了一个RedCode的编译器,将RedCode程序编译为机器码写入内存。
系统初始时内存中都是DATA $ 0,$ 0 ,每一个程序加载到内存的随机位置,当多个程序都加载完后系统给每个程序创建一个进程,此时每个进程在进程队列中的位置是随机的,然后系统开始运行。然后各个程序开始进行对抗,看最终谁能生存下来。
RedCode简单语法(按94标准)
每条指令的写法可以记做 OPCODE A_field ,B_field------------------ ------------------整体记做A整体记做B包含的部分可以省略。通常A表示源地址,B表示目标地址。OPCODE包含:DAT 中止进程MOV 移动数据A到BADD A加B并将结果存入BSUB A减B并将结果存入BMUL A乘B并将结果存入BDIV B/A并将结果存入B,如果A=0进程结束MOD B%A并将结果存入B,如果A=0进程结束JMP 跳转到AJMZ 如果B=0则跳转到AJMN 如果B!=0则跳转到ADJN 先将B减1, 如果B!=0跳转到ASPL 开启一个新进程,新进程起始执行地址为ASLT 如果ACMP 和SEQ相同SEQ 如果A=B跳过下一条指令SNE 如果A!=B跳过下一条指令NOP 空指令,什么也不干。LDP STP 本地地址操作指令,一般都没有实现,这里就不解释了。DAT 指令可以只有A区域出现,此时一般编译器会将内存的A区域拷贝一份到B区域。JMP SPL 指令也可以只有一个A区域出现,此时一般编译器会将内存的B区域作为$ 0OP_modify包含:.A 指令读写目的地址的A区域.B 指令读写目的地址的B区域 (如果Op_modify没有指定,则默认使用该规则).AB 指令读A指定地址的A区域,结果写入B指定地址的B区域.BA 指令读A指定地址的B区域,结果写入B指定地址的A区域.F 指令读A指定地址的A和B区域,操作结果写入B指定地址的A和B区域.X 指令读取A指定地址的B区域,操作结果写入B指定地址的A区域;然后读取A指定地址的A区域,进行同样的操作操作结果写入B指定地址的B区域.I 指令读写源和目标地址的整个内存单元。(mov指令没有指定OP_modify,并且源操作数不是立即数时使用该规则)Modify指定了寻址方式:# 立即数$ B直接寻址@ 间接寻址B先减1,再间接寻址* A间接寻址{ A先减1,再间接寻址} A先加1,再间接寻址值得注意的是地址跳转相关的指令A区域不能为立即数,加减乘除模指令的B区域不能为立即数,否则要么模拟器报语法错误,要么作为执行非法指令中止进程或者作为空指令处理。Filed就是指定数据的地方了。程序注释符为";",从;到行尾均作为注释。但有几个特殊的注释,作为程序信息:;name filename 指定程序的名字;author author 指定程序的作者;debug [static|off] 指定是否调试程序,但不是每个模拟器都实现此外不同的模拟器也有自己特定的特殊符号。宏一般不同的模拟器都有自己的一套,但通常都实现了EQU,语法label EQU 表达式 (表达式可以为数字或标签的加减乘除)ORG 数字或标签 指定程序执行的开始地址END程序结束,此后的部分不会被编译,如果指定了一个数字或标签则作为程序开始地址。语法实例讲解以上介绍了整个语法体系,看了后你应该已经云里来雾里去了,我们在这里以实例讲解RedCode。以下我们写一个完整的RedCode文件:;name Test;author cloudorg 1data dat 5mov data , @ datamov -2 ,mov # 3 , datajmp -1end编译器编译后将各指令的各个区域写入对应内存单元的对应区域。假如程序放到内存的0001单元到0005单元写入内存后反汇编过来就是0001 DAT $ 5 , $ 50002 mov $ -1, @ -10003 mov $ -2,0004 mov # 3 , $ -30005 jmp $ -1,2程序入口地址org 1,就是程序的第1条指令开始(编号从0开始),就是地址00020002处mov指令的寻址如下:源地址: $-1 ,就是直接寻址,以当前地址(0002)相对的-1处的地址作为源地址 ,即 0001目的地址: @ -1 ,就是间接寻址,以当前地址(0002)相对的-1处的地址的B区域作为直接寻址地址就是0001处的B数据区的数据5作为直接寻址地址,取出0001处的5作为直接寻址,5也是一个相对地址,相对于0002就是地址0007,最终目标地址为0007那么最终就是将0001的内存单元拷贝一份到00070003处的mov源地址一样,操作的是0001的地址,目标地址为:最终结果和上一条指令一样,但真正内存单元拷贝时001的B数据区已经变成了40004处的结果就是把3放到0001的B数据区中0005处的结果就是把0007的B数据区的数据加1,然后跳转到0006简单实例作为对抗程序最重要的就是生存,一个最简单的程序就是jmp 0始终跳转到自身,死循环,自己没有攻击能力,唯一取胜的方法就是期望对手自己死亡,可谓守株待兔。但这个程序一个问题是自己在内存中的地址固定,很容易收到攻击。下面这个程序可以说是非常出名的IMP程序了:mov 0,1它不断把自己移动到下一个内存单元,然后执行到下一个内存单元执行。这个程序具有一定的攻击能力,能够覆盖别人的程序,但是覆盖他人程序后