首先介绍一下CHIP-8的操作码。CHIP-8有35种操作码,均以16 bits的形式表示,也就是两位char或者是一位short(short的长度在c标准中不一定是2字节,但下面我们均认为在short为2字节的环境下进行的)。
16位二进制相当于4位16进制,我们一般以4位16进制的方式表示操作码。
wiki上有完整的操作码的表格,由于过长此处就不列出。
我们使用unsigned char来作为操作码的类型,观察操作码的表格,我们可以发现一些规律:
操作码可以看作操作和操作对象两部分组成,如8XY0表示把编号为X的寄存器的值设为编号为Y的寄存器的值,其中8和0表示了操作的“类型”而中间两位是操作的对象。
一般而言,操作码的第一位表示操作的类型,最后一位也常常对于操作的类型给出更多的信息。
那么如何从一个四位的十六进制数中取出第一位呢?第一反应可能是将opcode除以0x1000,但这显然不是最好的方法。更好的方法是使用更快的位运算。只需将opcode&0xF000就可以确定得到0xP000的形式,其中P为opcode的第一位。同理,与0x0F00、0x00F0、0x000F进行&操作可以得到opcode的其他位。
之后,我们可以利用取出的位数进行switch,分别对于不同的操作码进行不同的操作。
下面进行一些举例:
6XNN表示把寄存器VX设为NN。这里的NN是两位16进制,即1个字节。
在我们的设计中,16个寄存器命名为V0到V15(或VF),每个寄存器可以存放1个字节的数据。
下面是我的一个实现:
首先建立"opc.h",并在其中声明必要的变量,且将操作码的具体实现方式放入其中(在操作码前加一下划线作为函数名)。
unsigned char memory[4096];
unsigned char V[16];
unsigned short opcode;
void _6XNN(){
V[(opcode&0x0F00)>>8]=opcode&0x00FF;
}
memory代表模拟器的内存,V代表模拟器的寄存器,opcode代表操作码。
然后可以在主文件里调用这里的函数,可以使用printf查看函数是否达成了结果。
#include<stdio.h>
#include"opc.h"
void op(){
switch(opcode&0xF000){
case 0x6000:
_6XNN();
break;
}
}
int main(){
opcode=0x65CC;
printf("%dn",V[5]);
op();
printf("%dn",V[5]);
return 0;
}
可以从输出中看到我们暂时确实实现了6XNN这一操作码,可以不费力地实现其他一些简单的仅涉及内存和寄存器的操作码,由于涉及到的操作与“2”有十分密切的关系,注意要多用位运算而避免加减乘除取模等运算。