# DA6502 **Repository Path**: days2020/da6502 ## Basic Information - **Project Name**: DA6502 - **Description**: 一个6502处理器的反汇编工具 - **Primary Language**: C# - **License**: GPL-2.0 - **Default Branch**: master - **Homepage**: http://da6502.syuui.top - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 1 - **Created**: 2024-09-11 - **Last Updated**: 2024-09-11 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # DA6502 ## 介绍 一个6502处理器的反汇编工具 ## 软件架构 此工具开发环境为Microsoft Visual Studio Community 2022,开发语言为C#,运行时需要.NETFramework,Version=v4.7.2支持。 ## 安装教程 #### 源代码编译安装 1. 从Gitee或者网站 http://da65502.syuui.top/download (尚未开通)取得源代码 2. 使用Microsoft Visual Studio建立新解决方案并导入源代码 3. 编译 #### 可执行文件安装 暂未支持 ## 关于本项目 DA6502是笔者学习C#的一个习作。 笔者学习C#,作为练习、本意是想实现一个NES模拟器,但NES PPU功能模拟迟迟未实现,为了调试程序顺便阅读NES游戏的源代码,无意插柳写了这个工具。 ## 关于6502处理器 0. **概述** 6502处理器诞生于20世纪70年代,其时计算机工业及电子技术远不及现在发达,市面上常用的CPU是摩托罗拉公司的6800。6800在当时是一款性能优秀的CPU,但其售价高达300多美元,让多数人望而却步。摩托罗拉公司的天才工程师Chuck Peddle试图说服摩托罗拉的管理层设计一种廉价CPU,遭到否决后带领Bill Mensch等六位工程师跳槽到了MOS科技,并于约1年多后研发MOS6502成功。后期为提高产能,MOS科技将6502授权给了许多其它厂商,于是市面上6502CPU风靡一时,并且倒逼Chuck Peddle的老东家摩托罗拉将6800CPU降价到原来的1/10。 ![MOS6502CPU](https://pic4.zhimg.com/80/v2-ed07c2c4a929608bcb9706893284d6c7_720w.png 'MOS6502CPU') 1. **字节序** 6502CPU为小端序。低位前,高位后。 2. **总线宽度** 6502是一款8位的微处理器。其数据总线宽为8位,但地址总线宽16位,也就是说其地址空间为64KB。 3. **寄存器** (原文节选并修改自http://bbs.ggv.com.cn/wqxbbs/read.php?tid=80271) 6502中共有6个寄存器,包括3个8位寄存器、一个8位状态寄存器、1个栈寄存器和1个16位寄存器。 这些寄存器各自有特定的名称,通过特定的指令访问,不占据寻址空间。 + 累加寄存器A(Accumulator) A是一个8位的寄存器,也是6502中最重要和最常用的寄存器,可用于读写数据,进行各种逻辑运算等等。 + 变址寄存器X 一个8位的寄存器。X用来作为循环的计数器或者特定寻址下的偏移量,也可以存放从内存取出来的数据,还能用来设置或者获取栈指针。 + 变址寄存器Y 一个8位的寄存器。Y类似于X,可用来作为循环的计数器或者特定寻址下的偏移量,也可以存放从内存取出来的数据,但是不能用来设置栈指针。 + 状态寄存器P[也叫程序状态字PSW(Program Status Word)] 一个8位的寄存器,用于寄存指令执行的状态信息。 P的各位状态有的是根据指令执行的结果由硬件自动设置的,有的可以由用户用指令设定。 P的各位意义如下 比特位|名称|全称|说明 :----:|:--:|:---|:- 7|N|Negative|负标志。该位就是运算结果的最高位 6|V|Overflow|溢出标志。(一般对于有符号数来说),如果运算有溢出,则置 1 5|U|Unused|未使用标志。虽然未使用,但实际上在JSR或者中断等跳转时此标志位会被用来与B一起表示跳转的原因 4|B|Break|BRK命令标志。该位用来表示一个 BRK(Break) 指令在执行,该指令就是发出一个 IRQ 中断,类似 x86 下的 INT 3|D|Decimal Mode|BCD标志。该位用来将 6502 切换到 BCD 模式,但 NES 里面不用 2|I|Interrupt Disable|中断忽略标志。置1会使得系统忽略IRQ中断,清零则响应,SEI(Set Interrupt Disable) 指令将该位置 1,CLI(Clear Interrupt Disable)将该位清 0 1|Z|Zero|零标志。最近一条指令的结果是否为0,如果是,则置1,否则置0 0|C|Carry|进位标志。(一般对于无符号数来说)如果最近一条指令有溢出——上溢:超出了 255,下溢:低于 0,则设置该 bit 为 1 + 栈指针寄存器S[也称SP(Stack Pointer)] 这是8位的寄存器,指向栈顶位置。 6502栈区为$0100-$01FF。栈向下生长,所以初期时栈指针指向$01FF,入栈一字节后栈指针指向$01FE...出栈1字节则S加1,以此类推。 + 程序计数器PC(Programme Counter) 这是6502唯一的一个16位计数器,其内容为将要执行命令的存放地址。CPU取指时就会取这个地址上的值,并每取一个字节就自动将这个计数器加1。 用户不能对PC进行读写,但可以通过跳转指令JSR、JMP,分支指令如BEQ、BNE等,以及返回指令RTI、RTS等指令改变PC的值,以改变程序的执行顺序。 4. **中断** 6502提供三种类型的中断 + NMI(Non Maskable Interrupt),不可屏蔽中断,此中断必然会被响应 + RST(Reset),复位中断。此中断发生于CPU复位时。由于CPU复位后状态寄存器P的第2位I被置0,所以此中断也必然会被响应 + IRQ/BRK,硬/软中断。IRQ为外部更中断,通过处理器的IRQ引脚产生;BRK为内部软中断,由指令$00(BRK)产生。两个中断共享同一个中断号及同一个中断向量 三种中断的中断向量如下表所示 中断名称|中断向量 :-|:-: NMI|$FFFA RST|$FFFC IRQ/BRK|$FFFE 中断发生且被响应后,处理器会完成当前的指令,并将PC置为中断向量所指向的地址中所保存的地址后继续执行。 例:任天堂Family Computer上的坦克大战游戏,其中断向量表为: 地址|$FFFA|$FFFB|$FFFC|$FFFD|$FFFE|$FFFF -|-|-|-|-|-|- 值|$00|$D4|$70|$C0|$70|$C0 系统上电后CPU会触发一个RST中断,到RST中断向量处取操作数。 RST中断向量($FFFC)=$70,($FFFD)=$C0,由于6502是小端字节序,则可知目标地址为$C070,CPU将跳转至$C070处继续取指执行。 5. **内存** 6502CPU片上并不自带内存,所有内存都需要外部提供。内存与端口等共同使用64K寻址空间。 6502CPU地址有一个分页功能,每256字节为一页,地址空间共包含256页。一个地址的高8位即为其页号。 第一页($0000 - $00FF)是一个特殊的页,称为“零页”(ZeroPage)。6502有许多寻址方式使用零页地址。 第二页($0100 - $01FF)也是一个特殊的页,6502用这个页作为栈。6502的栈设计为负增长,所以栈底为$01FF,栈顶为$0100。 ## 关于6502汇编 #### 汇编指令 6502处理器指令长度为1个字节,最多可支持256条指令;操作数长度为0-2字节不等。 6502官方公开指令有151条,其余为未公开指令。未公开指令多为两条公开指令的组合,有少部分实现其它功能(皆是公开指令可以实现的功能)。 部分未公开指令运行不稳定,可能产生预期之外的结果,并不推荐使用。 6502指令表可参考如下链接: [https://www.masswerk.at/6502/6502_instruction_set.html](https://www.masswerk.at/6502/6502_instruction_set.html) [http://www.oxyron.de/html/opcodes02.html](http://www.oxyron.de/html/opcodes02.html) #### 汇编指令的语法 6502汇编使用特定的语法如下: 语法|含义 ----|---- $XX|这是一个单字节十六进制数据 $HHLL|这是一个双字节的十六进制数据,通常用于保存地址。HH代表高8位、LL代表低8位。
注意:由于6502是小端字节序,在内存中$HHLL实际上存为LLHH ($HHLL)|这是一个双字节的十六进制数据,代表地址$HHLL中保存的值 #### 寻址方式 6502处理器共有13种寻址方式。 名称|英文名称|汇编书写方式|指令操作数长度|计算方式 -|-|-|-|- 累加器寻址|Accumulator|OPC A|0|指令只操作累加器A 绝对寻址|Absolute|OPC $LLHH|2|目标地址为$HHLL,操作数存在目标地址中 绝对X变址寻址|Absolute, X-indexed|OPC $LLHH,X|2|目标地址为$HHLL+X,操作数存在目标地址中 绝对Y变址寻址|Absolute, Y-indexed|OPC $LLHH,Y|2|目标地址为$HHLL+Y,操作数存在目标地址中 立即数寻址|Immediate|OPC #$NN|1|$NN即为操作数 隐含寻址|Implied|OPC|0|操作数隐含在命令中(如:只针对某一寄存器的运算) 间接寻址|Indirect|OPC ($LLHH)|2|间接寻址只用于JMP指令中。$HHLL为一个地址,目标地址存在$HHLL开始的两个字节中。($HHLL)为低字节、($HHLL+1)为高字节 变址间接寻址|X-indexed, indirect|OPC ($LL,X)|1|$LL是一个零页地址,目标地址存在$LL+X开始的两个字节中。($LL+X)为低字节、($LL+X+1)为高字节 间接变址寻址|Indirect, Y-indexed|OPC ($LL),Y|1|$LL是一个零页地址;一个中间地址存在$LL开始的两个字节中,($LL)为低字节,($LL+1)为高字节;目标地址为中间地址+Y,操作数存在目标地址中 相对寻址|Relative|OPC *+$BB|2|相对寻址只用于分支指令中。$BB是一个有符号字节型数值,目标地址为当前地址+$BB 零页寻址|Zeropage|OPC $LL|2|目标地址是一个零页地址$00LL 零页X变址寻址|zeropage, X-indexed|OPC $LL,X|2|$LL地址是一个零页地址$00LL,目标地址为($00LL+X)&0x00FF,即:超出零页范围时会折回零页 零页Y变址寻址|zeropage, Y-indexed|OPC $LL,Y|2|$LL地址是一个零页地址$00LL,目标地址为($00LL+Y)&0x00FF,即:超出零页范围时会折回零页 ***6502的间接寻址BUG*** 间接寻址方式只有跳转指令JMP使用。6502CPU中这种寻址方式存在一个已知的跨页BUG: 间接寻址操作数代表一个中间地址,而目标地址存在这个中间地址开始的两个字节里。 如 JMP ($0820),实际上目标地址存在$0820,$0821中,将这两个字节取出来拼成的地址才是目标地址。 但:如 JMP ($08FF),理论上目标地址是存在$08FF,$0900两个字节中,由于6502的BUG,目标地址的高8位不是存在$09FF中,而是存在$08FF中。 ## 参与贡献