2 个稳定版本
1.0.1 | 2024 年 3 月 26 日 |
---|---|
1.0.0 | 2024 年 3 月 24 日 |
#31 在 模拟器
每月 55 次下载
365KB
7.5K SLoC
C8
CHIP-8 / S-CHIP / XO-CHIP 带有虚拟机、调试器和反汇编器的 tui 工具包
目录
关于
C8 是一个终端用户界面工具包,用于运行、调试和反汇编 CHIP-8、S-CHIP 和 XO-CHIP 游戏。其核心功能包括:
- 运行 rom:
c8 run [ROM_PATH]
- 添加
--debug
启用调试模式 - 添加
--kind
后跟classic
、chip8
、schip
或xochip
以在自动选择失败时强制使用其他 CHIP-8 变体 - 添加
--hz
后跟所需的每秒目标指令数(如果需要)
- 添加
- 使用
c8 dasm [ROM_PATH] > [OUTPUT_FILE_PATH]
将 rom 反汇编到文件 - 使用
c8 check [ROM_PATH]
检查 rom 是否存在潜在问题*
功能概览
功能 | C8 |
---|---|
全功能 Chip-8 + Classic、S-CHIP 和 XO-Chip 支持 | ✔ |
全功能声音支持 | ✔ |
全功能 4 位颜色支持 | ✔ |
debug 撤销、重做和执行步骤 |
✔ |
debug 虚拟机状态检查 |
✔ |
debug 寄存器和地址观察点 |
✔ |
debug 指令断点 |
✔ |
debug 程序执行历史 |
✔ |
debug 修改键盘状态 |
✔ |
debug 导出当前程序内存状态 |
✔ |
静态跟踪反汇编器 | ✔ |
可配置的执行速度 | ✔ |
兼容性配置文件 | ✔ |
预定义和自定义调色板 | 🚧 |
可单独配置的怪癖 | 🚧 |
标记为 debug
的功能仅在调试模式下可用
安装
C8 主要在 Windows 上进行测试,但应能在 Mac 和 Linux 上运行。如果在 Linux 上,请在继续之前查看以下内容以获取所需的系统包。
目前 C8 只能从 cargo 安装或从源代码构建。
使用 Cargo 安装
C8 已发布在 crates.io,可以使用 cargo 安装。需要 Rust 1.70.0 或更高版本。
cargo install c8
从源代码构建
可以使用 cargo 从源代码构建 C8。需要 Rust 1.70.0 或更高版本。
git clone https://github.com/tochiu/c8.git
cd c8
cargo install --path ./
为确保已安装 C8,请从存储库目录运行经典 IBM Logo ROM。
c8 run roms/c8/ibm_logo.ch8
安装注意事项
在 Linux 上,由于终端通常不支持按键事件,因此需要 X11 开发库来查询键盘状态。此外,系统上还需要高级 Linux 声音架构(ALSA)开发库。
在 Ubuntu/Debian 上
sudo apt install libx11-dev
sudo apt install librust-alsa-sys-dev
在 Fedora/RHEL/CentOS 上
sudo dnf install xorg-x11-server-devel
sudo dnf install alsa-lib-devel
在 MacOS 的新版本中,您可能会遇到只有元键(如 shift、退格等)的问题。这是由于权限问题。为了解决这个问题
- 打开 MacOS 系统偏好设置
- 转到 安全 -> 隐私
- 向下滚动到可访问性并解锁它
- 将您的终端添加到列表中
使用
运行
要运行 CHIP-8 程序,请使用 c8 run
命令,后跟程序路径。
- 如果需要程序以指定的频率运行,请添加
--hz
标志,后跟每秒目标指令数(IPS)值。 - 要指定 CHIP-8 变体,请添加
--kind
标志,后跟chip8
、classic
、schip
或xochip
之一。- 如果没有指定
--kind
,c8 将对 CHIP-8 变体进行最佳猜测。
- 如果没有指定
- 要将程序加载到调试器中,请添加
--debug
标志。
[!重要]
classic
变体不是完整的 COSMAC VIP 模拟器,而只是在 VIP 上 CHIP-8 的怪癖设置。
例如
c8 run roms/xo/super_neatboy.ch8 --hz 50000 --kind xochip
将在 XO-CHIP 变体上以 50000 IPS 运行 Super Neatboy rom。在上面的例子中,由于 C8 将自动选择 XO-CHIP 变体,因此不需要 --kind
标志。
反汇编
C8反汇编器是一个静态追踪反汇编器。它不会执行程序来反汇编它,而是从起始地址开始追踪程序,经过所有可能的分支,以确定哪些内存区域是代码,哪些是数据。然后,它将输出一个程序内存视图,其中包含反汇编指令和原始内存数据。因为这是对程序进行的静态分析,自修改代码的反汇编效果可能不好。反汇编器并不总是确定给定的地址是否为指令(见:停止问题)。每个地址都附有一个标签,指示该地址为指令的置信度。标签如下
符号 | 标签 | 描述 |
---|---|---|
' ' |
NOT |
该地址不是指令 |
'?' |
PARSABLE |
该地址可以解析为有效的指令 |
'*' |
REACHABLE |
该地址可以解析为有效的指令,并且理论上可以由程序执行 |
'O' |
VALID |
该地址可以解析为有效的指令,并且理论上可以由程序执行,并且可以至少属于一个静态执行路径 |
'X' |
PROVEN |
该地址是至少属于一个静态执行路径的指令 |
A static execution path 是从起始指令地址(0x200
)开始的指令序列,可以在不依赖于虚拟机状态的情况下由程序执行。
例如,从起始地址跳转到内存中特定位置的指令将创建一个新的静态(X
标签)执行路径,从该位置开始。反汇编器将遵循所有可能的静态执行路径以确定每个地址的置信度。
但如果指令跳转到由寄存器中的值等确定的内存位置,反汇编器将创建一个新的可达(*
标签)执行路径,从所有可能的跳转位置开始。这是因为反汇编器无法确定反汇编时的寄存器值。从那里,反汇编器将遵循所有可能的执行路径以确定每个地址的置信度。如果某个地址至少属于一个静态执行路径,它将被提升为有效(O
标签)。
要反汇编CHIP-8程序,请使用c8 dasm
命令,后跟程序的路径。这将打印反汇编程序到标准输出。添加--kind
标志以指定CHIP-8变体。例如
c8 dasm roms/ch8/ibm_logo.ch8
将反汇编ibm_logo rom并输出以下内容
0x200 cls # X 00E0 clear
0x202 ld i 0x22A # X A22A i = 0x22A
0x204 ld v0 12 # X 600C v0 = 12
0x206 ld v1 8 # X 6108 v1 = 8
0x208 drw v0 v1 15 # X D01F draw 8x15 @ v0,v1
0x20A add v0 9 # X 7009 v0 += 9
0x20C ld i 0x239 # X A239 i = 0x239
0x20E drw v0 v1 15 # X D01F draw 8x15 @ v0,v1
0x210 ld i 0x248 # X A248 i = 0x248
0x212 add v0 8 # X 7008 v0 += 8
0x214 drw v0 v1 15 # X D01F draw 8x15 @ v0,v1
0x216 add v0 4 # X 7004 v0 += 4
0x218 ld i 0x257 # X A257 i = 0x257
0x21A drw v0 v1 15 # X D01F draw 8x15 @ v0,v1
0x21C add v0 8 # X 7008 v0 += 8
0x21E ld i 0x266 # X A266 i = 0x266
0x220 drw v0 v1 15 # X D01F draw 8x15 @ v0,v1
0x222 add v0 8 # X 7008 v0 += 8
0x224 ld i 0x275 # X A275 i = 0x275
0x226 drw v0 v1 15 # X D01F draw 8x15 @ v0,v1
0x228 jp 0x228 # X 1228
0x22A # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x22B # 00 2X GRAPHIC ................
0x22C # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x22D # 00 2X GRAPHIC ................
0x22E # ? 3C00 2X GRAPHIC ....@@@@@@@@....
0x22F # 00 2X GRAPHIC ................
0x230 # ? 3C00 2X GRAPHIC ....@@@@@@@@....
0x231 # 00 2X GRAPHIC ................
0x232 # ? 3C00 2X GRAPHIC ....@@@@@@@@....
0x233 # 00 2X GRAPHIC ................
0x234 # ? 3C00 2X GRAPHIC ....@@@@@@@@....
0x235 # 00 2X GRAPHIC ................
0x236 # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x237 # 00 2X GRAPHIC ................
0x238 # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x239 # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x23A # 00 2X GRAPHIC ................
0x23B # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x23C # 00 2X GRAPHIC ................
0x23D # ? 3800 2X GRAPHIC ....@@@@@@......
0x23E # 00 2X GRAPHIC ................
0x23F # ? 3F00 2X GRAPHIC ....@@@@@@@@@@@@
0x240 # 00 2X GRAPHIC ................
0x241 # ? 3F00 2X GRAPHIC ....@@@@@@@@@@@@
0x242 # 00 2X GRAPHIC ................
0x243 # ? 3800 2X GRAPHIC ....@@@@@@......
0x244 # 00 2X GRAPHIC ................
0x245 # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x246 # 00 2X GRAPHIC ................
0x247 # FF 2X GRAPHIC @@@@@@@@@@@@@@@@
0x248 # ? 8000 2X GRAPHIC @@..............
0x249 # ? 00E0 2X GRAPHIC ................
0x24A # E0 2X GRAPHIC @@@@@@..........
0x24B # ? 00E0 2X GRAPHIC ................
0x24C # E0 2X GRAPHIC @@@@@@..........
0x24D # 00 2X GRAPHIC ................
0x24E # ? 8000 2X GRAPHIC @@..............
0x24F # 00 2X GRAPHIC ................
0x250 # ? 8000 2X GRAPHIC @@..............
0x251 # ? 00E0 2X GRAPHIC ................
0x252 # E0 2X GRAPHIC @@@@@@..........
0x253 # ? 00E0 2X GRAPHIC ................
0x254 # E0 2X GRAPHIC @@@@@@..........
0x255 # 00 2X GRAPHIC ................
0x256 # 80 2X GRAPHIC @@..............
0x257 # F8 2X GRAPHIC @@@@@@@@@@......
0x258 # 00 2X GRAPHIC ................
0x259 # FC 2X GRAPHIC @@@@@@@@@@@@....
0x25A # 00 2X GRAPHIC ................
0x25B # ? 3E00 2X GRAPHIC ....@@@@@@@@@@..
0x25C # 00 2X GRAPHIC ................
0x25D # ? 3F00 2X GRAPHIC ....@@@@@@@@@@@@
0x25E # 00 2X GRAPHIC ................
0x25F # ? 3B00 2X GRAPHIC ....@@@@@@..@@@@
0x260 # 00 2X GRAPHIC ................
0x261 # ? 3900 2X GRAPHIC ....@@@@@@....@@
0x262 # 00 2X GRAPHIC ................
0x263 # F8 2X GRAPHIC @@@@@@@@@@......
0x264 # 00 2X GRAPHIC ................
0x265 # F8 2X GRAPHIC @@@@@@@@@@......
0x266 # 03 2X GRAPHIC ............@@@@
0x267 # 00 2X GRAPHIC ................
0x268 # 07 2X GRAPHIC ..........@@@@@@
0x269 # 00 2X GRAPHIC ................
0x26A # 0F 2X GRAPHIC ........@@@@@@@@
0x26B # 00 2X GRAPHIC ................
0x26C # ? BF00 2X GRAPHIC @@..@@@@@@@@@@@@
0x26D # 00 2X GRAPHIC ................
0x26E # FB 2X GRAPHIC @@@@@@@@@@..@@@@
0x26F # 00 2X GRAPHIC ................
0x270 # F3 2X GRAPHIC @@@@@@@@....@@@@
0x271 # 00 2X GRAPHIC ................
0x272 # E3 2X GRAPHIC @@@@@@......@@@@
0x273 # 00 2X GRAPHIC ................
0x274 # ? 43E0 2X GRAPHIC ..@@........@@@@
0x275 # E0 2X GRAPHIC @@@@@@..........
0x276 # ? 00E0 2X GRAPHIC ................
0x277 # E0 2X GRAPHIC @@@@@@..........
0x278 # 00 2X GRAPHIC ................
0x279 # ? 8000 2X GRAPHIC @@..............
0x27A # 00 2X GRAPHIC ................
0x27B # ? 8000 2X GRAPHIC @@..............
0x27C # 00 2X GRAPHIC ................
0x27D # ? 8000 2X GRAPHIC @@..............
0x27E # 00 2X GRAPHIC ................
0x27F # ? 8000 2X GRAPHIC @@..............
0x280 # ? 00E0 2X GRAPHIC ................
0x281 # E0 2X GRAPHIC @@@@@@..........
0x282 # ? 00E0 2X GRAPHIC ................
0x283 # E0 2X GRAPHIC @@@@@@..........
如果需要,每个相关的地址将打印出相应的指令。
在#
符号之后是该地址的标签。因为ibm_logo是一个简单的程序,每个地址要么被标记为已证明的指令(X
标签),要么是数据,偶尔会有?
标签,如果它意外地可以解析为指令。
标签之后是该地址的十六进制值
- 如果地址是指令,这将与重叠地址的合并一样长
- 如果地址是数据,这是一个字节
最后是指令或数据的描述
- 如果地址是指令,则打印指令的描述
- 如果地址是数据,则打印字节数据的可视表示
在这种情况下,数据区域的视觉表示展示了保存在rom中的IBM图形。
[注意] 这个反汇编器还可以作为内存查看器。调试器中的内存面板只是程序内存的最新反汇编。
c8 check
是在反汇编器之上构建的一个工具,用于检查 ROM 中的不良执行分支。它通过在程序上运行反汇编器并记录可能执行无效指令的已验证(X
标签)或有效(O
标签)指令的区域来完成此操作。
调试
启动调试器
要调试 CHIP-8 程序,运行
c8 run [PATH_TO_PROGRAM] --debug
这将启动调试器,程序在执行第一条指令之前以暂停状态加载。使用 help
命令查看命令列表。在任何时候,按 Ctrl+C
退出。
导航调试器
下面是调试器第一次启动时的样子
[提示] 确保终端窗口尽可能大,以确保每个面板都可见。
一眼就能看到
- 带有输出的命令行界面
- 要扩展输出面板,请使用
output
命令
- 要扩展输出面板,请使用
- 程序显示
- 要切换程序显示,请使用
show display
或hide display
命令
- 要切换程序显示,请使用
- 程序键盘状态、寄存器状态、计时器状态和堆栈
- 程序内存布局
- 要扩展内存面板,请使用
memory
命令 - 要切换内存布局的详细视图,请使用
show memory -v
或hide memory -v
命令 - 要导航到特定内存地址,请使用
goto
命令后跟pc
、i
或特定地址 - 要跟踪内存中的指针,请使用
follow
命令后跟一个指针(例如pc
或i
) - 要取消跟踪当前跟踪的指针,请使用
unfollow
命令 - 要将整个内存视图导出到文件,请使用
dump memory
命令后跟文件路径
- 要扩展内存面板,请使用
- 程序历史记录
- 要关注历史面板,请使用
history
命令
- 要关注历史面板,请使用
[提示] 当关注一个面板(例如内存)时,您可以使用
Home
和End
键跳转到开始或结束。使用Esc
键返回到命令行界面。
在 SCHIP 和 XOCHIP 程序上,高分辨率图形模式会更改 UI 布局,如下所示
控制程序执行
运行程序
使用 continue
命令启动您程序的执行
(c8db) continue
这将最小化调试器并运行程序,直到触发调试事件或执行暂停。按 Esc
暂停执行并返回到调试器。
[重要] 调试事件是中断程序执行并进入调试器窗口的触发器。C8DB 中触发调试事件的功能是监视点和断点。
- 断点设置为在指定地址的指令执行之前触发
- 监视点设置为在指定寄存器或地址修改后触发
逐条执行程序
使用 step
执行下一条指令。后面跟一个整数 n
以执行下 n
条指令。如果触发调试事件,则将中断。例如
(c8db) step 50
将执行下 50 条指令。
![注意] 如果您使用
step
或continue
与过去程序状态(可以使用undo
获取)一起,所有未来的程序状态将被清除,执行将前进。如果您想重新播放这些未来状态,请使用redo
。
![提示] 如果您因为正在轮询按键事件而卡在指令上,请使用
key
命令来模拟按键事件。输入key --help
获取更多信息。
通过执行历史记录进行搜索
使用 undo
和 redo
来浏览程序执行历史。后面跟一个整数 n
,以回放或快进到最后 n
个程序状态。
或者,使用 history
命令将焦点集中到程序历史面板。使用 W/S 或 上/下 键来浏览程序执行。这只是在 undo
和 redo
命令之上的图形层。
redo
是一个特别特殊的命令。技术上,它并不简单地执行下一个指令,因为某些指令的执行与程序状态的非确定性相关,例如用户输入或 RNG。如果需要,某些属性会在执行指令之间存储,以便正确回放。这就是 redo
所利用的。
如果您处于特定程序状态,并希望从该点执行程序而不是回放,请使用 step
或 continue
。
设置执行速度
使用 hertz
后跟 n
,其中 n
是每秒目标指令数,以设置程序执行速度。例如
(c8db) hertz 60
将以每秒 60 指令的速度执行程序。
![重要] C8 以固定的帧率 60Hz 运行。 如果您的目标执行速度以每帧周期数表示,请将其乘以 60 以获得等效的每秒指令数。
断点和观察点
有时在满足特定条件时暂停执行是有用的。这是断点和监视点的用武之地。一个 断点 设置在指定地址的指令执行之前触发。一个 监视点 设置在指定的寄存器或地址被修改之后触发。
设置断点
使用 break
后跟一个地址来设置断点。例如
(c8db) break 0x200
将在地址 0x200
设置断点。一旦程序到达此地址,执行将暂停并进入调试器。要列出所有断点,请输入 info break
。要删除断点,请使用 clear
命令。在这个例子中
(c8db) clear break 0x200
将删除地址 0x200
的断点。要清除所有断点,请输入 clear all break
。
设置监视点
使用 watch
后跟一个寄存器或地址来设置监视点。例如
(c8db) watch i
将在寄存器 i
上设置断点。一旦修改该寄存器,执行将暂停并进入调试器。如果我们在地址上设置断点,则在写入该地址时执行将暂停。要列出所有断点,请输入 info watch
。要删除断点,请使用 clear
命令。在这个例子中
(c8db) clear watch i
将删除寄存器 i
上的断点。要清除所有断点,请输入 clear all watch
。
动机
这是我第一个 完成的 Rust 项目(哈哈)。我的一个朋友给我发了一篇关于如何用 CHIP-8 编写模拟器的文章。这是一篇非常有趣的阅读,也是学习 Rust 的好借口!在我完成模拟器后,我觉得我可以更进一步。所以,我们就在这里了。如果你在考虑编写自己的 CHIP-8 模拟器,你应该试试!它是仿真开发的一个很好的起点,并且在此基础上构建其他 CHIP-8 变种是编写可扩展软件的极好练习。
依赖项
~15–49MB
~581K SLoC