CC65 开发FC/NES 游戏(二)Hello world
Contents
起因
系列第二篇:上一篇把 cc65 环境装好了,这一篇跑通第一个程序——在 FC/NES 上显示 Hello World,顺便看清 cl65 一条命令背后做了什么、为什么 NES 程序“不能退出”。
需求
- 记事本编辑 helloworld.c,编译生成 helloworld.nes 文件
- 用模拟器打开 .nes,验证屏幕上显示 Hello World
技术实现原理
cl65 一条命令背后做了什么
cc65 是“多目标”编译器套件:-t nes 决定用哪套头文件、启动代码(crt0)和 C 运行库。cl65 是驱动程序,一条命令实际串起三步:cc65 把 C 编译成汇编 → ca65 汇编成目标文件 helloworld.o → ld65 按 nes.cfg 的段映射做链接,最后拼出 .nes 文件(iNES 头 + PRG 程序段 + CHR 图案段)。这就是下文截图里“先生成 helloworld.o、再生成 helloworld.nes 文件”的原因——.o 是中间目标文件,.nes 才是模拟器认的最终镜像。
为什么是 cprintf 而不是 printf
NES 上没有操作系统,也就没有“标准输出”这个设备,printf 依赖的 stdio 文件抽象在这里无处安放。cc65 的 conio(控制台 I/O)库直接面向屏幕:cprintf 把字符写到文本层——具体说就是 PPU 背景命名表(nametable)里光标所在的格子。少一层抽象,更贴近 8 位机的现实。
为什么必须死循环
while(1){} 不是装饰。C 程序里 main 返回后要把控制权交回操作系统,而 NES 没有操作系统可交——启动代码跑完 main 之后,执行流落到未初始化的 ROM 区域,行为未定义,常见结果就是屏幕一片漆黑。这就好像执行一个 bat 最后不加 pause,窗口一闪就没——程序“结束”了,但机器总得跑点什么。for(;;); 与 while(1){} 等价,都是原地循环不退出。
6502 上的类型长度
后面 helloacm 的例子打印了各类型的 sizeof:char 是 8 位、int/short 是 16 位、long 是 32 位——跟 PC 上 int 32 位不一样。C 标准只规定 char ≤ short ≤ int ≤ long 的相对关系,具体长度由编译器按平台定:6502 是 8 位机,寄存器一次只能算 8 位,但 0~255 的计数范围太小(放个地址都不够),int 取 16 位是编译器在效率和可用性之间的折中。
代码实现
记事本编辑文件 helloworld.c
Helloworld.c代码
|
|
代码说明
- 备注说明:正常的C语言使用的是 printf 但是CC65使用的是 cprintf 用来在屏幕显示字符串——原理见上文“为什么是 cprintf”;
- while是为了让程序进入死循环不会退出,如果你想看看没有最后的死循环是啥样的,结果就是屏幕一片漆黑,这就好像你执行一个 bat 最后不加 pause 的话都是一闪就没一样的意思;
- 另外让程序进入死循环的还有 for(;;); 这个也是不停的循环没有退出。
编译
cmd进入上面编辑的文件夹中 执行下面的命令就可以生成helloworld.nes文件
cmd 命令
|
|

用模拟器打开生成的nes文件就可以看到结果了

从上面的文件我们可以看到
先生成的文件是 helloworld.o 然后才是生成 helloworld.nes 文件
用过c的这些应该都了解,我这个对c一知半解的就不多说了
下面再放上两段 helloacm.com 看来的代码
https://helloacm.com/category/6502/
C代码
|
|
这里是为了让学习者了解 int char float double short 长度,跟java还有其他语言不一样的地方,也是因为这个 6502的8位芯片的限制
下面是运行截图
补充 long 是4长度 我就不再截图了

C代码
|
|
上面这个代码会在屏幕显示ASCII 看下面运行截图

通过上面的代码,现在你可以试着通过学过的其他语言试试你想要运行的东西
后面的内容还在整理,摇杆的获取同屏幕显示的互动 上上下下左右左右BABA start
踩坑记录与注意事项
- 忘了 -t nes:目标错成默认机器的话,链接的是别的机器的启动代码和运行库,模拟器要么不认要么黑屏——-t 参数同时决定头文件、启动代码、运行库三件套;
- 删掉 while(1){} 会黑屏:main 返回后没有操作系统接手,执行流跑飞(原理见上文),NES 程序永远不能“正常退出”;
- 换行要用 \r\n:conio 的 \n 只把光标垂直下移、不回到行首,要 “\r\n” 组合才能换到下一行行头(DOS 时代同款约定);
- 改了 .c 要重新编译:模拟器打开的是 .nes 镜像,不会热加载源码改动;
- 打印超过 0x7F 的“ASCII”:NES 字符集里那些编码不是 ASCII,会显示成图案块——第二段代码打印 1..255 时后半段看到的就是这些。
Author 软件开发大郭
LastMod 2022-01-13