本教程将使用C语言以及raylib库来实现chip-8模拟器的开发。
什么是chip-8Chip-8是一种解释型编程语言,由 Joseph Weisbecker 开发。它最初在20世纪70年代中期应用于 COSMAC VIP 和 Telmac 18008位微型计算机。Chip-8程序在 chip-8虚拟机上运行。它的出现是为了让电子游戏更容易为这些计算机编程, CHIP 8由于其简单性至今仍在使用,因此也可用于任何平台和二进制数的编程教学。
大约在15年后,chip-8被引入,衍生解释器出现在一些型号的图形计算器上(从20世纪80年代后期开始,这些手持设备在许多方面比大多数70年代中期的爱好者的微型计算机拥有更强的计算能力)。
chip-8应用程序有许多经典的电子游戏移植到CHIP-8上,如《Pong》、《太空入侵者》、《俄罗斯方块》和《吃豆人》。还有一些应用,如随机迷宫生成器和康威的生命游戏。
chip-8架构内存
Chip-8最常用于4k 系统,如 Cosmac VIP 和 Telmac 1800。这些机器有4096(0x1000)kb内存大小,chip-8解释器本身占用了这些机器上前512个字节的内存空间。由于这个原因,大多数chip-8程序从内存位置512(0x200)开始,不访问位置512(0x200)以下的任何内存。最上面的256个字节(0xF00-0xFFF)保留用于显示刷新,下面的96个字节(0xEA0-0xEFF)保留用于调用堆栈、内部使用和其他变量。
寄存器
CHIP-8有16个8位数据寄存器,命名为V0到VF。VF寄存器作为一些指令的标志。在加法操作中,VF是进位标志,而在减法操作中,它是“没有借位”标志。在绘制指令中,像素碰撞时也会设置VF。
地址寄存器名为I,宽度为12位,与几个涉及内存操作的操作码一起使用。
堆栈
堆栈仅用于在调用函数时存储返回地址。
计时器
CHIP-8有两个定时器。它们都以60赫兹的频率倒数,直到达到0。
延迟计时器:该计时器用于为游戏事件计时。它的值可以设置和读取。
声音计时器:这个计时器用于音效。当它的值非零时,发出哔哔声。
输入
输入是用十六进制键盘完成的,它有从0到f的16个键。'8','4','6'和'2'键通常用于方向输入。三个操作码用于检测输入。其中一个会在按下特定键时跳过指令,而另一个则在未按下特定键时跳过指令。第三个等待键按下,然后将其存储在数据寄存器之一。
图形和声音
chip-8显示分辨率为64×32,颜色为单色。图形仅通过绘制精灵绘制到屏幕上,精灵宽度为8像素,高度为1到15像素。精灵像素与相应的屏幕像素是异或的。换句话说,被设置的精灵像素会翻转相应屏幕像素的颜色,而未设置的精灵像素则什么都不做。当绘制精灵时,如果任何屏幕像素从set翻转到unset,则进位标志(VF)将被设置为1,否则将被设置为0。这是用于碰撞检测。
如前所述,当声音计时器的值非零时,将播放蜂鸣声。
操作码表
CHIP-8有35个操作码,都是两个字节长,存储大端数。操作码如下所示,以十六进制表示,并带有以下符号:
操作码 | 类型 | C 风格代码 | 解释 |
0NNN | Call | 调用地址NNN处的机器码。 | |
00E0 | Display | disp_clear() | 清空屏幕 |
00EE | Flow | return; | 从一个函数返回 |
1NNN | Flow | goto NNN; | 跳转到地址NNN |
2NNN | Flow | *(0xNNN)() | 调用地址NNN处的函数。 |
3XNN | Cond | if (Vx == NN) | 如果VX等于NN,则跳过下一条指令。(通常下一条指令是跳过代码块的跳转); |
4XNN | Cond | if (Vx != NN) | 如果VX不等于NN,则跳过下一条指令。(通常下一条指令是跳过代码块的跳转); |
5XY0 | Cond | if (Vx == Vy) | 如果VX等于VY,则跳过下一条指令。(通常下一条指令是跳过代码块的跳转); |
6XNN | Const | Vx = N | 将VX设置为NN。 |
7XNN | Const | Vx = N | 将NN添加到VX。(进位标志VF不改变); |
8XY0 | Assig | Vx = Vy | 将VX设置为VY的值。 |
8XY1 | BitOp | Vx |= Vy | 将VX设置为VX或VY。(按位或操作); |
8XY2 | BitOp | Vx &= Vy | 将VX设置为VX和VY。(按位和操作); |
8XY3 | BitOp | Vx ^= Vy | 将VX设置为VX 异或 VY。 |
8XY4 | Math | Vx = Vy | 将VY添加到VX。当有进位时,VF被设为1,当没有进位时,VF被设为0。 |
8XY5 | Math | Vx -= Vy | VX减去VY。当有借位时,VF设为0,当没有借位时,VF设为1。 |
8XY6 | BitOp | Vx >>= 1 | 将VX的最低有效位存储在VF中,然后将VX向右移1。 |
8XY7 | Math | Vx = Vy - Vx | 将VX设置为VY - VX。当有借位时,VF设为0,当没有借位时,VF设为1。 |
8XYE | BitOp | Vx <<= 1 | 将VX的最高有效位存储在VF中,然后将VX向左移1。 |
9XY0 | Cond | if (Vx != Vy) | 如果VX不等于VY,则跳过下一条指令。(通常下一条指令是跳过代码块的跳转); |
ANNN | MEM | I = NNN | 设置I为地址NNN。 |
BNNN | Flow | PC = V0 NNN | 跳转到地址NNN V0。 |
CXNN | Rand | Vx = rand() & NN | 将VX设置为对随机数(通常为0到255)和NN进行按位操作的结果。 |
DXYN | Disp | draw(Vx, Vy, N) | 在坐标(VX, VY)上绘制一个宽度为8像素、高度为N像素的精灵。从内存位置I开始,读取每一行8个像素作为位编码;I值在执行这条指令后不会改变。如上所述,如果任何屏幕像素在绘制精灵时从set翻转到unset, VF将被设置为1,如果没有发生这种情况,VF将被设置为0 |
EX9E | KeyOp | if (key() == Vx) | 如果按下存储在VX中的键,则跳过下一条指令。(通常下一条指令是跳过代码块的跳转); |
EXA1 | KeyOp | if (key() != Vx) | 如果没有按下存储在VX中的键,则跳过下一条指令。(通常下一条指令是跳过代码块的跳转); |
FX07 | Timer | Vx = get_delay() | 将VX设置为延迟定时器的值。 |
FX0A | KeyOp | Vx = get_key() | 等待按键,然后存储在VX中。(阻塞操作。所有指令暂停,直到下一个按键事件按下按键); |
FX15 | Timer | delay_timer(Vx) | 将延迟定时器设置为VX。 |
FX18 | Sound | sound_timer(Vx) | 将声音计时器设置为VX。 |
FX1E | MEM | I = Vx | 将VX添加到i, VF不受影响。 |
FX29 | MEM | I = sprite_addr[Vx] | 将I设置为VX中字符的精灵位置。字符0-F(十六进制)用4x5字体表示。 |
FX33 | BCD |
| 存储VX的二进制编码的十进制表示,地址为I的三位中最高有效的一位,中间的一位为I 1,最低有效的一位为I 2。(换句话说,取VX的十进制表示,将百位放在内存中I的位置,十位放在内存中I 1的位置,个位放在内存中I 2的位置。) |
FX55 | MEM | reg_dump(Vx, &I) | 存储从V0到VX(包括VX)在内存中,从地址I开始。从I开始的偏移量为每写入一个值增加1,但I本身没有修改。 |
FX65 | MEM | reg_load(Vx, &I) | 从V0到VX(包括VX)填充内存中的值,从地址I开始。每写入一个值,从I开始的偏移量就增加1,但I本身没有被修改。 |
Copyright © 2024 妖气游戏网 www.17u1u.com All Rights Reserved