65c02 汇编
本书是 65c02 汇编语言的指南。本书将讲解 8 位 WDC 65c02 处理器的不同内存寻址模式和指令。
这是对 6502 汇编 书籍的编辑,增加了 65c02 上的新指令/模式。
语法在不同的汇编器之间会有所不同 - 本书将在整本书中使用以下语法
| 语法 | 进制 | 示例 | 
|---|---|---|
| %00001111 | 二进制 | LDA #%0001 | 
| $FA | 十六进制 | LDA #$0E | 
| 123 | 十进制 | LDA #100 | 
65C02 CPU 有一个 8 位 数据 总线,以及一个 16 位地址总线。所有 寄存器 都是 8 位的,除了 16 位的 程序计数器 (PC) 寄存器。因此 CPU 被认为是 8 位的。
地址总线是 16 位意味着 CPU 可以访问 2^16 个独立的字节内存,从地址 $0000 到地址 $FFFF,也就是 65536 字节 (64KB).
与外围设备(例如视频、音频、磁盘和游戏系统的控制器)的通信通常通过 内存映射 I/O 进行。
内存被划分为“页”,每页 256 个字节(在 8 位偏移量的范围内)。第 n 页是内存中的第 n 页,起始地址为 256*n,结束地址为 (256*(n+1))-1。例如,“零页”从地址 0 开始,到地址 255 结束。有关详细信息,请参阅下面的 内存布局。
不到一半的 65c02 CPU 操作码 处理存储在零页中的内存。存储在零页中的内存通常需要更短的时间来处理。
| 寄存器 | 大小(位) | 用途 | 
|---|---|---|
| 累加器 (A) | 8 | 用于对数据执行计算。 指令可以直接对累加器进行操作,而不是浪费 CPU 周期去访问内存 | 
| X 寄存器 (X) | 8 | 在某些 寻址模式 中用作索引 | 
| Y 寄存器 (Y) | 8 | 在某些 寻址模式 中用作索引 | 
| 程序计数器 (PC) | 16 | 指向要执行的下一条指令的地址 | 
| 堆栈指针 (SP) | 8 | 存储堆栈索引,下一个堆栈元素将插入到该索引中。 该位置的地址为 $0100 + SP。SP 最初设置为$FD | 
| 状态 (SR) | 8 | 每一位代表一个状态标志。标志指示 CPU 的状态,或关于先前指令结果的信息。 有关每个标志的描述,请参阅下表 | 
| 位 | 符号 | 名称 | 描述 | 
|---|---|---|---|
| 7 | N | 负 | 比较:如果寄存器的值小于输入值,则设置 | 
| 6 | V | 溢出 | 算术运算:如果在加法或减法期间发生有符号溢出,则设置,即结果的符号与输入和累加器的符号不同 | 
| 5 | - | (未用) | 始终设置为 | 
| 4 | B[1] | 中断 | 如果 BRK指令触发中断请求,则设置 | 
| 3 | D | 十进制 | 十进制模式[2]:数学指令将把输入和输出视为十进制数。 例如 $09 + $01 = $10 | 
| 2 | I | 中断禁用 | 设置后,将禁用中断 | 
| 1 | Z | 零 | 比较:如果寄存器的值等于输入值,则设置 | 
| 0 | C | 进位 | 进位/借位标志用于数学和循环操作 算术运算:如果在加法或减法期间发生无符号溢出,则设置,即结果小于初始值(或等于初始值,如果进位标志在进入时被设置) | 
16 位值以 小端 方式存储在内存中,因此最低有效字节存储在最高有效字节之前。例如,如果地址 $0000 包含 $FF,地址 $0001 包含 $00,则从 $0000 读取一个两位字节值将得到 $00FF。
有符号整数采用 二进制补码,可以表示从 -128 (%10000000) 到 +127 (%01111111) 的值。如果整数为负数,则设置第 7 位。
6502 的程序计数器是 16 位宽的,因此最多可以寻址 2^16 (65536) 个字节的内存。某些内存区域为特定目的保留
| 区域 | 目录 | 描述 | 
|---|---|---|
| $0000-$00FF | 零页 | 内存的第一页,访问速度比其他页快。 指令可以使用单个字节而不是两个字节来指定零页内的地址,因此使用零页而不是其他页面的指令在执行时需要少一个 CPU 周期 | 
| $0100-$01FF | 堆栈 | 后进先出数据结构。从 $01FF反向增长到$0100。由某些 传输、堆栈 和 子程序 指令使用 | 
| $0200-$FFFF | 通用 | 可以用于任何所需用途的内存。 使用 6502 处理器的设备可以选择为其他目的保留子区域,例如 内存映射 I/O | 
每个指令使用 13 种内存寻址模式之一,该模式决定指令的操作数。每个模式都提供了一个示例。
累加器被隐含地作为操作数,因此不需要指定地址。
示例
使用 ASL(算术左移)指令,没有操作数,累加器始终是被左移的值。
ASL
操作数是隐含的,因此不需要指定。
示例
这里隐含的操作数是 X(传输的源)和 A(传输的目的地)。
TXA
操作数直接用于执行计算。
示例
值 $22 被加载到累加器中。
LDA #$22
指定一个完整的 16 位地址,该地址处的字节用于执行计算。
示例
地址 $D010 处的值被加载到 X 寄存器中。
LDX $D010
单个字节指定内存第一页($00xx)中的地址,也称为零页,该地址处的字节用于执行计算。
示例
地址 $0002 处的值被加载到 Y 寄存器中。
LDY $02
与隐含(i)基本相同。不同之处在于,这些指令执行堆栈操作;从堆栈中压入或弹出操作数。
指定的偏移量被添加到程序计数器(PC)中存储的当前地址。偏移量范围为 -128 到 +127。
示例
偏移量 $2D 被添加到程序计数器(例如 $C100)中的地址。分支的目的地(如果执行)将是 $C12D。
BPL $2D
存储在指定地址处的低字节序的两位字节值用于执行计算。仅由 JMP 指令使用。
示例
地址 $A001 和 $A002 被读取,分别返回 $FF 和 $00。然后跳到地址 $00FF。
JMP ($A001)
X 中的值被添加到指定的地址以获得一个总和地址。加载存储在总和地址(LSB)和总和地址加 1(MSB)的两个字节对处的低字节序地址,并在该地址处的值用于执行计算。仅由 JMP 指令使用。
示例
X 中的值 $06 被添加到 $EO15,总和为 $EO1B。读取地址 $E01B 和 $E01C 处的地址 $D010。然后跳到地址 $D010。
JMP ($E015,X)
X 中的值被添加到指定的地址以获得一个总和地址。总和地址处的值用于执行计算。
示例
X 中的值 $02 被添加到 $C001,总和为 $C003。地址 $C003 处的 $5A 值用于执行 _带进位的加法_(_ADC_)操作。
ADC $C001,X
Y 中的值被添加到指定的地址以获得一个总和地址。总和地址处的值用于执行计算。
示例
Y 中的值 $03 被添加到 $F001,总和为 $F004。地址 $F004 处的 $EF 值被递增(_INC_),并向 $F004 写回 $F0。
INC $F001,Y
X 中的值被添加到指定的零页地址以获得一个总和地址。总和地址处的值用于执行计算。
示例
X 中的值 $02 被添加到 $01,总和为 $03。地址 $0003 处的 $A5 值被加载到累加器中。
LDA $01,X
Y 中的值被添加到指定的零页地址以获得一个总和地址。总和地址处的值用于执行计算。
示例
Y 中的值 $03 被添加到 $01,总和为 $04。地址 $0004 处的 $E3 值被加载到累加器中。
LDA $01,Y
X 中的值被添加到指定的零页地址以获得一个总和地址。加载存储在总和地址(LSB)和总和地址加 1(MSB)的两个字节对处的低字节序地址,并在该地址处的值用于执行计算。
示例
X 中的值 $02 被添加到 $15,总和为 $17。地址 $0017 和 $0018 处的地址 $D010 将是累加器中 $0F 值存储的位置。
STA ($15,X)
加载存储在零页地址(LSB)和零页地址加 1(MSB)的两个字节对处的低字节序地址,并在该地址处的值用于执行计算。
示例
地址 $0015 和 $0016 处的地址 $D010 将是累加器中 $0F 值存储的位置。
STA ($15)
Y 中的值被添加到存储在指定地址(LSB)和指定地址加 1(MSB)的两个字节对处的低字节序地址处的地址。总和地址处的值用于执行计算。实际上,寻址模式完全重复了累加器寄存器的数字。
示例
Y 中的值 $03 被添加到地址 $002A 和 $002B 处的地址 $C235,总和为 $C238。然后,累加器与 $C238 处的 $2F 值进行异或运算。
EOR ($2A),Y
这些是 6502 处理器的指令,包括 ASCII 可视化、受影响标志的列表以及可接受寻址模式的操作码表。
加载和存储
[edit | edit source]| 将内存加载到累加器: LDA | 将内存加载到索引 X: LDX | 将内存加载到索引 Y: LDY | ||||||||||||||||||||||||||||||||||||||||||||
| M -> A 标志:N,Z | M -> X 标志:N,Z | M -> Y 标志:N,Z | ||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | 
 | ||||||||||||||||||||||||||||||||||||||||||||
| 将累加器存储到内存: STA | 将索引 X 存储到内存: STX | 将索引 Y 存储到内存: STY | ||||||||||||||||||||||||||||||||||||||||||||
| A -> M 标志:无 | X -> M 标志:无 | Y -> M 标志:无 | ||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | 
 | ||||||||||||||||||||||||||||||||||||||||||||
| 将零存储到内存: STZ | ||||||||||||||||||||||||||||||||||||||||||||||
| 0 -> M 标志:无 | ||||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
算术
[edit | edit source]| 将内存加到累加器带进位: ADC | 将内存减去累加器带借位: SBC | ||||||||||||||||||||||||||||||||||||||||
| A + M + C -> A 标志:N,V,Z,C | A - M - ~C -> A 标志:N,V,Z,C | ||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | 
增量和减量
[edit | edit source]| 将内存加一: INC | 将索引 X 加一: INX | 将索引 Y 加一: INY | ||||||||||||||||||||
| M + 1 -> M 标志:N,Z | X + 1 -> X 标志:N,Z | Y + 1 -> Y 标志:N,Z | ||||||||||||||||||||
| 
 | 
 | 
 | ||||||||||||||||||||
| 将内存减一: DEC | 将索引 X 减一: DEX | 将索引 Y 减一: DEY | ||||||||||||||||||||
| M - 1 -> M 标志:N,Z | X - 1 -> X 标志:N,Z | Y - 1 -> Y 标志:N,Z | ||||||||||||||||||||
| 
 | 
 | 
 | 
移位和旋转
[edit | edit source]| 算术左移一位: ASL | 逻辑右移一位: LSR | ||||||||||||||||||||||||
| C <- 7 6 5 4 3 2 1 0 <- 0 标志:N,Z,C | 0 -> 7 6 5 4 3 2 1 0 -> C 标志:N,Z,C | ||||||||||||||||||||||||
| 
 | 
 | ||||||||||||||||||||||||
| 左旋一位: ROL | 右旋一位: ROR | ||||||||||||||||||||||||
| C <- 7 6 5 4 3 2 1 0 <- C 标志:N,Z,C | C -> 7 6 5 4 3 2 1 0 -> C 标志:N,Z,C | ||||||||||||||||||||||||
| 
 | 
 | 
逻辑
[edit | edit source]| 将内存与累加器 AND 操作: AND | 将内存与累加器 OR 操作: ORA | 将内存与累加器 XOR 操作: EOR | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| A & M -> A 标志:N,Z | A | M -> A 标志:N,Z | A ^ M -> A 标志:N,Z | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | 
 | 
比较和测试位
[edit | edit source]负数(N),零(Z)和进位(C)状态标志 用于条件(分支)指令。
所有比较指令都以相同方式影响标志。
| 条件 | N | Z | C | 
|---|---|---|---|
| 寄存器 < 内存 | 1 | 0 | 0 | 
| 寄存器 = 内存 | 0 | 1 | 1 | 
| 寄存器 > 内存 | 0 | 0 | 1 | 
| 将内存与累加器比较: CMP | 将内存与索引 X 比较: CPX | 将内存与索引 Y 比较: CPY | ||||||||||||||||||||||||||||||||||||
| A - M 标志:N,Z,C | X - M 标志:N,Z,C | Y - M 标志:N,Z,C | ||||||||||||||||||||||||||||||||||||
| 
 | 
 | 
 | 
测试内存中的位与累加器:BIT
A & M
标志:N = M7,V = M6,Z
| 寻址模式 | 操作码 | 
|---|---|
| a | 2C | 
| a,x | 3C | 
| # | 89 | 
| zp | 24 | 
| zp,x | 34 | 
分支
[edit | edit source]| 无条件分支: BRA | |||||||||
| 分支如果 1 = 1 标志:无 | |||||||||
| 
 | |||||||||
| 分支如果进位清除: BCC | 分支如果进位设置: BCS | ||||||||
| 分支如果 C = 0 标志:无 | 分支如果 C = 1 标志:无 | ||||||||
| 
 | 
 | ||||||||
| 分支如果结果非零: BNE | 分支如果结果为零: BEQ | ||||||||
| 分支如果 Z = 0 标志:无 | 分支如果 Z = 1 标志:无 | ||||||||
| 
 | 
 | ||||||||
| 分支如果结果为正: BPL | 分支如果结果为负: BMI | ||||||||
| 分支如果 N = 0 标志:无 | 分支如果 N = 1 标志:无 | ||||||||
| 
 | 
 | ||||||||
| 分支如果溢出清除: BVC | 分支如果溢出设置: BVS | ||||||||
| 分支如果 V = 0 标志:无 | 分支如果 V = 1 标志:无 | ||||||||
| 
 | 
 | 
与其他分支指令不同,BBR 和 BBS 具有两个操作数。用于设置或重置比较的 M 操作数始终是 zp 寻址模式。分支地址操作数是 r 寻址模式,与其他分支指令的含义相同。
| 分支如果位复位: BBR | 分支如果位设置: BBS | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 分支如果 (M >> n) & 1 = 0 标志:无 | 分支如果 (M >> n) & 1 = 1 标志:无 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | 
传输
[edit | edit source]| 将累加器传输到索引 X: TAX | 将索引 X 传输到累加器: TXA | ||||||||
| A -> X 标志:N,Z | X -> A 标志:N,Z | ||||||||
| 
 | 
 | ||||||||
| 将累加器传输到索引 Y: TAY | 将索引 Y 传输到累加器: TYA | ||||||||
| A -> Y 标志:N,Z | Y -> A 标志:N,Z | ||||||||
| 
 | 
 | ||||||||
| 将堆栈指针传输到索引 X: TSX | 将索引 X 传输到堆栈指针: TXS | ||||||||
| S -> X 标志:N,Z | X -> S 标志:无 | ||||||||
| 
 | 
 | 
堆栈
[edit | edit source]| 将累加器压入堆栈: PHA | 将累加器从堆栈弹出: PLA | ||||||||
| A -> S 标志:无 | S -> A 标志:N,Z | ||||||||
| 
 | 
 | ||||||||
| 将索引 X 压入堆栈: PHX | 将索引 X 从堆栈弹出: PLX | ||||||||
| X -> S 标志:无 | S -> X 标志:无 | ||||||||
| 
 | 
 | ||||||||
| 将索引 Y 压入堆栈: PHY | 将索引 Y 从堆栈弹出: PLY | ||||||||
| Y -> S 标志:无 | S -> Y 标志:无 | ||||||||
| 
 | 
 | ||||||||
| 将处理器状态压入堆栈: PHP | 从堆栈中弹出处理器状态: PLP | ||||||||
| P -> S 标志:无 | S -> P 标志位:全部 | ||||||||
| 
 | 
 | 
处理器状态存储为一个字节,其中从高位到低位的标志位为:NV--DIZC。
跳转到新位置:JMP
通过更改程序计数器的值跳转到新位置。
标志:无
| 寻址模式 | 操作码 | 
|---|---|
| a | 4C | 
| (a) | 6C | 
| (a,x) | 7C | 
跳转到新位置并保存返回地址:JSR
跳转到子程序
下一个指令之前的地址(PC - 1)被压入堆栈:首先是高字节,然后是低字节。由于堆栈向后增长,因此返回地址在内存中以小端序存储。
PC 设置为目标地址。
标志:无
| 寻址模式 | 操作码 | 
|---|---|
| a | 20 | 
从子程序返回:RTS
从子程序返回到使用 JSR 调用它的位置。
从堆栈中弹出返回地址(先弹出低字节,然后是高字节)。
返回地址加 1 并存储到 PC 中。
标志:无
| 寻址模式 | 操作码 | 
|---|---|
| i | 60 | 
从中断返回:RTI
从中断返回。
从堆栈中弹出 SR。
从堆栈中弹出 PC。
标志位:全部
| 寻址模式 | 操作码 | 
|---|---|
| i | 40 | 
| 清除进位标志: CLC | 设置进位标志: SEC | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 -> C 标志位:C = 0 | 1 -> C 标志位:C = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 清除十进制模式: CLD | 设置十进制模式: SED | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 -> D 标志位:D = 0 | 1 -> D 标志位:D = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 清除中断禁用状态: CLI | 设置中断禁用状态: SEI | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 -> I 标志位:I = 0 | 1 -> I 标志位:I = 1 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
 | 
 | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 清除溢出标志: CLV | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 0 -> V 标志位:V = 0 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 
 | |||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 重置内存位: RMB | 设置内存位: SMB | ||||||||||||||||||||||||||||||||||||||||||||||||||||||
| M & ~(1 << n) -> M 标志:无 
 | M | (1 << n) -> M 标志:无 
 | 
 中断:BRK   
强制中断。
这是一个两字节指令,其中第二个字节被处理器忽略。第二个字节可以用作中断服务例程的参数。
标志位:B = 1, I = 1
| 寻址模式 | 操作码 | 
|---|---|
| i | 00 | 
 无操作:NOP   
无操作
标志:无
| 寻址模式 | 操作码 | 
|---|---|
| i | EA | 
 等待中断:WAI   
等待中断
标志:无
| 寻址模式 | 操作码 | 
|---|---|
| i | CB | 
 停止模式:STP   
停止模式
标志:无
| 寻址模式 | 操作码 | 
|---|---|
| i | DB | 
所有存在的操作码列表。操作码是“操作码”,是指令中的第一个字节。这个字节决定要执行的操作。多个汇编助记符(例如 ADC、BIT、JMP 等)对应于多个不同的操作码。每种寻址模式都对应一个操作码。例如,助记符 ASL 有多个操作码,每个操作码对应于 CPU 支持的一种寻址模式。
带星号 (*) 的操作码表示 6502 CPU 中不存在的操作码。
空白条目应与 NOP 指令(EA)的行为相同。
| 高位 nibble | 低位 nibble | |||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 00 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 0A | 0B | 0C | 0D | 0E | 0F | |
| 00 | BRK s | ORA (zp,x) | TSB zp * | ORA zp | ASL zp | RMB0 zp * | PHP s | ORA # | ASL A | TSB a * | ORA a | ASL a | BBR0 r * | |||
| 10 | BPL r | ORA (zp),y | ORA (zp) * | TRB zp * | ORA zp,x | ASL zp,x | RMB1 zp * | CLC i | ORA a,y | INC A * | TRB a * | ORA a,x | ASL a,x | BBR1 r * | ||
| 20 | JSR a | AND (zp,x) | BIT zp | AND zp | ROL zp | RMB2 zp * | PLP s | AND # | ROL A | BIT a | AND a | ROL a | BBR2 r * | |||
| 30 | BMI r | AND (zp),y | AND (zp) * | BIT zp,x | AND zp,x | ROL zp,x | RMB3 zp * | SEC i | AND a,y | DEC A * | BIT a,x * | AND a,x | ROL a,x | BBR3 r * | ||
| 40 | RTI s | EOR (zp,x) | EOR zp | LSR zp | RMB4 zp * | PHA s | EOR # | LSR A | JMP a | EOR a | LSR a | BBR4 r * | ||||
| 50 | BVC r | EOR (zp),y | EOR (zp) * | EOR zp,x | LSR zp,x | RMB5 zp * | CLI i | EOR a,y | PHY s * | EOR a,x | LSR a,x | BBR5 r * | ||||
| 60 | RTS s | ADC (zp,x) | STZ zp | ADC zp | ROR zp | RMB6 zp * | PLA s | ADC # | ROR A | JMP (a) | ADC a | ROR a | BBR6 r * | |||
| 70 | BVS r | ADC (zp),y | ADC (zp) * | STZ zp,x | ADC zp,x | ROR zp,x | RMB7 zp * | SEI i | ADC a,y | PLY s * | JMP (a,x) * | ADC a,x | ROR a,x | BBR7 r * | ||
| 80 | BRA r * | STA (zp,x) | STY zp | STA zp | STX zp | SMB0 zp * | DEY i | BIT # * | TXA i | STY a | STA a | STX a | BBS0 r * | |||
| 90 | BCC r | STA (zp),y | STA (zp) * | STY zp,x | STA zp,x | STX zp,y | SMB1 zp * | TYA i | STA a,y | TXS i | STZ a * | STA a,x | STZ a,x * | BBS1 r * | ||
| A0 | LDY # | LDA (zp,x) | LDX # | LDY zp | LDA zp | LDX zp | SMB2 zp * | TAY i | LDA # | TAX i | LDY a | LDA a | LDX a | BBS2 r * | ||
| B0 | BCS r | LDA (zp),y | LDA (zp) * | LDY zp,x | LDA zp,x | LDX zp,y | SMB3 zp * | CLV i | LDA a,y | TSX i | LDY a,x | LDA a,x | LDX a,y | BBS3 r * | ||
| C0 | CPY # | CMP (zp,x) | CPY zp | CMP zp | DEC zp | SMB4 zp * | INY i | CMP # | DEX i | WAI i * | CPY a | CMP a | DEC a | BBS4 r * | ||
| D0 | BNE r | CMP (zp),y | CMP (zp) * | CMP zp,x | DEC zp,x | SMB5 zp * | CLD i | CMP a,y | PHX s * | STP i * | CMP a,x | DEC a,x | BBS5 r * | |||
| E0 | CPX # | SBC (zp,x) | CPX zp | SBC zp | INC zp | SMB6 zp * | INX i | SBC # | NOP i | CPX a | SBC a | INC a | BBS6 r * | |||
| F0 | BEQ r | SBC (zp),y | SBC (zp) * | SBC zp,x | INC zp,x | SMB7 zp * | SED i | SBC a,y | PLX s * | SBC a,x | INC a,x | BBS7 r * | ||||
