CC65 65(C)02 编译器
Contents
起因
想给 FC/NES、C64 这类 6502 老机器写程序,第一道坎是“在现代电脑上怎么给它编代码”:这些机器的内存和速度撑不起自己编译自己,工具链必须跑在 PC 上、产出目标机的机器码——这就是交叉编译。cc65 是 6502 体系最完整的开源交叉开发包。
软件简介
cc65 是 65(C)02系统的完整交叉开发包,包括强大的宏汇编器,C编译器,链接器,库管理器和其他几个工具。
最重要的是这个可以开发红白机游戏 FC / NES
cc65为许多旧的6502机器提供C和运行时库支持,包括:
技术实现原理
交叉编译:宿主与目标分离
编译器跑在 PC(宿主)上,生成的是 6502 指令。宿主/目标分离带来两件事:一是每个目标机要有自己的运行时库、启动代码和链接配置——同一份 C 源码,-t nes 和 -t c64 编出来的二进制完全不同;二是调试要回到模拟器/实机,宿主上可用包里的 sim65 直接跑 6502 程序。
6502 是“对 C 最不友好”的 CPU 之一
C 的抽象模型(16 位以上整数、任意深度递归、指针随处可指)在 6502 上都有物理障碍,cc65 为此做了一堆底层工作,这也决定了它的代码风格:
- 没有 16 位寄存器:A/X/Y 全是 8 位,一个 int(cc65 里 16 位)的加减要拆成多条 8 位指令完成;
- 间接寻址只认零页:按指针访问数据必须借道 $00–$FF 的零页单元,编译器把最常用的变量放零页——零页只有 256 字节,是全机最挤的资源;
- 硬件栈只有 256 字节且不能索引:$0100 页基本只放返回地址,C 的局部变量和参数传递靠 cc65 在普通内存里自建的软件栈,每次函数调用都有可观的固定开销;
- 65C02 比 6502 多了若干指令(PHX/PLY、(zp,X) 寻址等),效率略好,架构限制不变。
所以 cc65 的定位很明确:用 C 写游戏逻辑、快速出原型;性能热点(帧循环、图形例程)用包里的 ca65 汇编写,两种代码在链接阶段合成一个二进制。
工具链分工
- cc65:C 编译器,先把 C 翻成汇编;
- ca65:宏汇编器,汇编成目标文件;
- ld65:链接器,按 cfg 配置文件描述的目标机内存布局(比如 NES 的 PRG/CHR bank)分配地址——移植到新板卡时最常改的就是它;
- cl65:驱动程序,一条命令串起编译、汇编、链接(
cl65 -t nes -o demo.nes demo.c); - ar65:库管理器;sim65:宿主上的 6502 模拟器。
支持的机器
以下Commodore机器:
VIC20
C16 / C116和Plus / 4
C64
C128
CBM 510(又名P500)
600/700系列
较新的PET机器(不是2001年)。
Apple ][(苹果II)及其后继机型。
Atari 8位机器。
Atari 2600控制台。
Atari 5200控制台。
适用于C64、C128和Apple //e的GEOS。
Bit Corporation Gamate控制台。
NEC PC-Engine(又名TurboGrafx-16)控制台。
任天堂娱乐系统(NES)控制台。
Watara监督控制台。
VTech Creativision控制台。
Oric Atmos。
Oric Telestrat。
Lynx控制台。
Ohio Scientific Challenger 1P。
实战参考
本站《CC65 开发FC/NES 游戏》系列就是用它开发的:(三)使用摇杆、(四)方向键控制一个简单的精灵。
踩坑记录与注意事项
- 性能预期要重校准:软件栈 + 8 位寄存器,C 代码比手写汇编大数倍、慢数倍是常态,性能热点直接上 ca65;
- 零页是硬约束:只有 256 字节,C 运行时、驱动库、汇编模块都在抢,链接报零页溢出就是超了;
- int 是 16 位:循环计数、数组下标尽量用 unsigned char,省指令也省内存;
- 标准是 C89/C90 加部分 C99:更新的语法特性别硬上,编译器报错不一定直白;
- 跑飞/花屏先查 ld65 的 cfg:内存布局、bank 划分错了,C 代码再对也没用;
- 中断服务函数用 interrupt 关键字声明:让编译器生成完整的现场保护,拿普通函数当 ISR 会打乱运行时的寄存器约定。
Author 软件开发大郭
LastMod 2022-01-19