QL6502 是为 MOS 6502 开发的汇编器和静态分析工具,其前身是作者本人于上世纪 90 年代开发的 CAXD6502 工具。
MOS 6502是 MOS 科技公司于 1975年开发的 8 位微处理器,问世之初就成为市场上性能最强的 8 位 CPU,而价格却只有大型芯片公司如 Motorola、Intel 同类产品的六分之一,激起了一系列的的电脑创新项目,并在上世纪八十年代带来了一场个人电脑的革命。MOS 科技最初授权洛克威尔国际公司(Rockwell International)与 Synertek 公司为芯片的“第二供货源”。所以我们过去经常听到 R6502 的称谓。
CAXD6502 共生于 1990 年代中国大陆兴起的 6502 游戏机市场,它为开发者提供了二进制 ROM 文件的静态分析、反汇编和汇编语言程序开发及程序补丁支持。CAXD6502 从 Intel 80286 的 MS-DOS 下使用 TurboC 2.0 开始开发,中间于 2004年移植到 GCC 编译环境下。
QL6502 作为对早期开发过程的总结和纪念,也作为 FPGA CPU 验证和测试代码的简易开发工具。
QL6502 实现 MOS6502 CPU 的汇编和反汇编功能。
通常对一个已有的游戏程序(固件)进行再次开发和功能扩展,首先需要理解原有程序的功能。在没有源代码的情况下,反汇编就成为唯一可行的方式。早期的 8 位处理器其代码功能简单,源程序本身也是使用汇编语言编写的,反汇编分析方法相对有效。当然, 随着处理器越来越强大、固件功能越来越复杂,现代的固件都使用 C 语言等高级语言编写,其算法实现时的思维方式也已经脱离了汇编和机器语言,而代码经过编译器优化后更是面目全非,反汇编基本上没有可能,所以现代通常的方法是“正向开发”。
一个机器代码的映像文件至少包含代码和数据,反汇编的作用就是把二进制代码转换为汇编助记符。但是简单地一对一翻译并不能满足我们分析的要求,因为把数据翻译成无效的助记符,或者把指令的半字设置为跳转入口,都只会得到“垃圾代码”,在九十年代简单文本编辑器的时代,这种过错就意味着多次无意义的清单打印。
QL6502 通过指令的模拟执行来识别整个处理器映像文件的代码和数据区域。QL6502 首先利用处理器已知的入口(RESET, NMI, IRQ)开始程序指令分析,流过顺序指令流,标识识别的子程序调用和跳转指令及其目标地址,标识出其它的代码入口。通过反复扫描指令,自动识别全部的代码,这个过程可以理解为虚拟执行,而最终没有被虚拟执行的代码部分就全部作为数据输出。
QL6502 通过人工干预的方式对间接转移指令进行处理。
对机器代码分析理解之后,就可以编写汇编代码来实现自己所需要的功能了,这时我们需要的工具就是汇编语言工具。与通常的汇编工具不同,扩展功能的开发除了把汇编助记符转换成机器指令外,还需要实现一些特别的功能来满足我们的要求。比如:
QL6502 与传统的汇编器一样,通过两遍扫描对源文件进行处理。支持的伪操作包括 ORG、EQU、DB、DW,可以进行 +,- 等数值运算。传统的标号使用如 LABEL: 一类的名字表示,对于组成一个项目的多个源文件之间,用 LABEL:: (两个冒号)定义,这种标号可以在整个项目范围内引用。在 CAXD6502 设计之初,我们实现了复杂的表达式计算堆栈以支持在汇编时的常量表达式计算,不过负责任地说,在近十年的开发时间里,我们没有一次遇到那些“数学正确”的表达式计算,所以这一次,我们把这些功能全部删除。
QL6502 实现 CODE 伪指令。用
CODE start end
来定义一片可放置用户扩展代码的区域(已经分析确认此区域为空白、无效数据区等),后续的代码在这个区域内顺序放置。在一个项目的后续源文件中,只需要使用一个无参数的 CODE 伪指令,就可以继续代码汇编。如果汇编的代码超出所定义的区域,则产生汇编错误报告并停止汇编。
QL6502 使用 GCC 编译器在 Windows 7 64bits、 Linux、 Mac OS X 上编译实现。使用定义好的 makefile
MAKE
将在 bin 目录中生成对应可执行程序。
在 Windows 命令窗口、Linux 命令行、Mac OS 命令窗口中启动QL6502。
D:\QL6502\Testcase>..\obj\QL6502.exe -X
CA6502 V4.0 An Assembly Development Tool for 6502
fortchina@163.com, 2017.07 - 2017.07, ALL RIGHTS RESERVED
This Program is based on CA6502 V3.0, ZhaoYu, ZhangWenCui, 1993.05 - 2004.09
QL6502>
可在提示之后输入命令,命令为单字母,大小写无关,可以用 ? 得到帮助。
接着我们装入一个映像文件 ldt512.bin,这个过程几乎不需要思考 :
QL6502> ldt512.bin
65536 Bytes data read into code buffer @0x0000
6502 的全部寻址空间是 64K字节,但是在实际的系统中,它当然不可能全部都是代码,还有许多存储器空间用于 I/O 地址映射。我们把代码之外的区域称为“无效区域”。在分析之前,我们需要标识出这些区域。这应该是一个很复杂的过程,可能需要对硬件电路、固件代码反复分析才能确定:
QL6502> b 0x0000 0x3fff
QL6502> b 0x8000 0xbfff
QL6502> b
000000 -- 003FFF : Not Used
004000 -- 007FFF : Used
008000 -- 00BFFF : Not Used
00C000 -- 010000 : Used
QL6502>
我们使用 X 命令进行静态分析
QL6502> x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1 Indirect Entries
QL6502 报告遇到了一个间接跳转指令,我们使用 i 命令查看
QL6502> i
(001) L_D425: JMP (0x0030)
于是我们查看这个指令相关的代码,u 命令是反汇编,它基本上属于“盲译”,所以我们可以把地址设为上面提示的前方位置。
QL6502> u 0xd410
L_D41A BD 7B D9 LDA L_D97B,X
L_D41D 85 30 STA 0x30
L_D41F E8 INX
L_D420 BD 7B D9 LDA L_D97B,X
L_D423 85 31 STA 0x31
L_D425 6C 30 00 JMP (0x0030)
L_D428 A5 57 LDA 0x57
L_D42A 29 10 AND #0x10
L_D42C F0 0F BEQ L_D43D
L_D42E AD 4A 08 LDA L_084A
这显示了从地址 L_D97B 开始是一个跳转表,我们可以查看表的内容
QL6502> d 0xd97b
Scale - 0 1 2 3 4 5 6 7 - 8 9 a b c d e f 0123456789ABCDEF
00D97B: B7 D9 69 DA 3E ..i.>
00D980: D3 85 DC E4 DC 5D DD DE - DD FC DD 58 DE 8A DE 09 .....].....X....
00D990: DF 61 DF DF DF 04 E0 60 - E0 B7 E0 30 FF 25 E2 35 .a.....`...0.%.5
00D9A0: E2 90 E2 EB 4E A0 65 51 - E3 84 E4 E5 E4 0E E5 2D ....N.eQ.......-
00D9B0: E5 77 E5 2D E7 A6 E7 20 - 71 C5 20 65 D7 20 28 D4 .w.-... q. e. (.
00D9C0: 20 A3 C5 20 E8 D2 20 7C - 79 20 72 59 20 90 5A A5 .. .. |y rY .Z.
00D9D0: 75 29 80 09 11 85 75 A9 - 00 85 C7 85 70 85 73 85 u)....u.....p.s.
00D9E0: 76 8D BF 03 A2 0C 20 20 - CC A2 00 20 20 CC 20 D4 v..... ... . .
我们经过分析可知 0xd97b ~ 0xd9b6 的内容是跳转指令表,于是我们用 j 命令 (Jump Table) 命令告诉 QL6502跳转表
QL6502> j 0xd97b 0xd9b6
接着再分析
QL6502> x
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
Ok.
这次没有遇到间接跳转指令,说明整个程序静态分析完成,我们可以生成反汇编代码文件
QL6502> g dt512.asm 0 0x0xffff
注意其中的无效区域已经被 B 命令标识出来了。我们生成的文件局部看是这样
L_4D1B: DB 0x20,0x21,0xF0,0xA6,0x9C,0xB5,0x80,0x29 ; .!.....)
L_4D23: DB 0x0F,0xC9,0x07,0xA9,0x31,0x8D,0x66,0x10 ; ....1.f.
L_4D2B: DB 0xA9,0x01,0x85,0xA1,0x60 ; ....`
L_4D30: JSR L_D7A7 ;Subrutine ..........
L_4D33 LDA 0xA1
L_4D35 BNE L_4D3C
L_4D37 LDA #0x49 ; I
L_4D39 JSR L_F4A3
L_4D3C: RTS
L_4D3D: JSR L_D8DF ;Subrutine ..........
L_4D40 LDA L_2000
L_4D43 AND #0x80
L_4D45 BNE L_4D61
L_4D47 LDA L_0731
L_4D4A CMP #0x02
L_4D4C BEQ L_4D62
L_4D4E BCS L_4D6D
L_4D50 CLC
L_4D51 LDA L_0730
L_4D54 ADC #0x01
L_4D56 STA L_0730
L_4D59 LDA L_0731
L_4D5C ADC #0x00
L_4D5E STA L_0731
L_4D61: RTS
数据用 DB 标识,子程序入口使用;Subrutine 说明,跳转的入口使用 L_4D3C:( 一个冒号)标记,一般的指令地址如 L_4D51 (没有冒号)表示后面的指令位于 CPU 地址 0x4D51 处。
Ql6502 可以对单个源文件进行汇编,而对于有多个源文件组成的项目来说,最方便的方法是定义一个项目管理文件,比如这个 ABC.MAK 文件
-OOUT512.bin
-LIN512.bin
A.ASM
B.ASM
C.ASM
D.ASM
X.ASM
……
其中每个文本行对应一个描述项目, -OOUT512.bin 为输出 ROM 映像文件名称, -LIN512.bin 输入映像文件名称。 而 *.ASM 为各个源文件,需要逐一列出。
我们使用以下命令可以自动完成整个项目的汇编
QL6502 ABC.MAK
QL6502 将读入IN512.bin, 逐个汇编源文件,形成输出文件 OUT512.bin。下面是一个实际工作的例子
D:\QL6502\Testcase>..\obj\ql6502 DZZB.MAK
CA6502 V4.0 An Assembly Development Tool for 6502
fortchina@163.com, 2017.07 - 2017.07, ALL RIGHTS RESERVED
This Program is based on CA6502 V3.0, ZhaoYu, ZhangWenCui, 1993.05 - 2004.09
Load file : IN512.bin
Write File : OUT512.bin
Total 2404 Lines in 13 Files Built.
QL6502 工作不涉及特定的目标硬件平台。 在实际的工作中,通常使用EEPROM 编程器读取 ROM 映像,汇编之后的代码生成目标映像后,再使用 EEPROM 编程器写入各种存储器如 27C64,7527C256,27C512 等。由于不同设计者使用的地址映射方案不同,在烧录之前可能需要做不同的程序文件切割和打包处理。在 QL6502 项目中,我们包含了一个实际使用的处理程序,如果只关注学习 6502 人汇编/反汇编器的实现方法,可忽略这些内容。
QL 是秦岭(汉语拼音QinLing)的缩写。秦岭是长江流域与黄河流域的一个分水岭,生长着一些世界独有的野生动植物。
QL6502 的开源发布遵循 MIT 开源代码协议。
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。