自计算机(确切说法是数字电子计算机)诞生以来,出现过许多种指令系统(也叫指令集)。指令系统的核心内容是什么?是寻址方式。为啥要这么说?我们以8位机时代的三巨头为例,可以很好地说明寻址方式的重要性。
在8位微处理器时代的早期,是Motorola的M6800、Intel的I8080、Rockwell公司的R6502三足鼎立。这三个,R6502集成度最低、纸面指标也最低。价格也最低。M6800与I8080的发售价一样,都是360美元,而R6502只有25美元一个。但是,同样是跑游戏,M6800和I8080都比不过R6502。其中一个原因就是R6502的寻址方式更为复杂,更为精妙。
所以学机器指令,包括学汇编语言,重点要放在寻址方式上。
世界上最早获得商业成功的微型计算机是Apple II,它的MPU(微处理器)是Rockwell公司推出的R6502。我们就以6502为例,来介绍机器指令的寻址方式。
一、指令与寻址方式
机器指令就是CPU能辨认并执行的命令。不管你是用什么语言开发的程序,最终总要翻译成机器指令,CPU才能执行完成你的任务。
在计算机里,整数、浮点数、字符(串)、声音、图像、图形都是以二进制数存储、处理的。指令也是。
一条机器指令包含两个部分,一是操作码,一是地址码。前者描述要CPU执行什么操作,后者表示去哪里取操作对象(也就是操作数)。地址码还要告诉CPU以什么方式寻找操作数?这就是寻址方式。
二、R6502的寻址方式
1.立即Immediate寻址
在6502里,采用立即寻址的都是两字节指令。第一字节是操作码,第二字节就是操作数。
第一字节
操作码
第二字节
操作数
譬如取数指令LDA #$F0,第一字节是A0,第二字节是F0。这条指令的功能就是把F0这个数送入累加器A。汇编指令里的符号#,就表示后面跟的是立即数。$表示这是16进制数。
对于立即寻址的指令来说,MPU从内存取来指令后,也同时取来了操作数,因为立即寻址的操作数就包含在指令里面。也就是说,立即寻址指令的地址码就是操作数。
2.绝对absolute寻址
在6502里,采用绝对寻址的都是三字节指令。
第一字节
操作码
第二字节
操作数地址低字节
第三字节
操作数地址高字节
R6502的地址字长是16位,可寻址64KB内存空间。16位就是两个字节。绝对寻址指令给出一个16位地址,告诉你,去内存哪个存储单元取操作数。
譬如取数指令LDA $F010,第一字节是AD,第二、三字节分别为10和F0。从内存的F010单元取数送累加器A。
3.零页Zero Page寻址
64KB空间可以分为256个页面,地址高8位为全零的页称为0页。因此,零页寻址仅限于内存的第0页,也就是0000 0000 0000 0000到0000 0000 1111 1111这256个存储单元。因此,零页寻址指令就只需要两个字节。
第一字节
操作码
第二字节
零页地址
譬如取数指令LDA $0E,第一字节为A5,第二字节为0E,从内存的000E单元取数送累加器A。
4.累加器Accumulator寻址
累加器寻址都是单字节指令。操作数就放在累加器A里。譬如循环左移指令ROL A,单个字节2A,就是将累加器A里的8个位连同状态寄存器里的进位标志位C看作一个环,一起向左移一位,C的值移入空出来的最低位,最高位移入C。
5.隐含Implied寻址
譬如指令DEX,单字节,CA,它的功能是使X寄存器减1。
栈操作指令,包括返回指令等,也是隐含寻址。
上面的累加器寻址,其实也是一种隐含寻址。因为这个累加器A,也没有在指令里表示出来。
6.间接Indirect寻址
在前面的绝对寻址中,指令的地址码存放的是操作数的地址。而间接寻址,指令地址码是操作数的地址的地址,所以叫间接寻址。
譬如无条件转移指令JMP ($1000),先从1000~1001两个单元里取出一个16位数,假设是F000,然后从F000~F001两个单元里取出的就是要跳转的目的地址。记住一个区别:绝对寻址只访问一次内存,间接寻址访问两次内存。
间接寻址是三字节指令。
7.相对Relative寻址
相对寻址只用于条件转移,是两字节指令。它的地址码部分是一个地址偏移量。譬如指令BCC *+55,假设它存储在内存的0350~0351俩单元里。如果状态寄存器的进位标志位C为0,下条指令在0355(0350+5=03A5)处开始取,否则在0352处开始取。也就是说,目的地址是相对于这条指令所在地址。
(待续)