8、两种典型微处理器介绍
微处理器将计算机中央处理器的所有构成组件整合在一起,集成在一个硅芯片上——诞生于1971年,它的诞生有着很好的开端:第一个微处理器,即Intel4004系列,包括2300个晶体管。到现在,大约三十年过去了,家用计算机的微处理器中的晶体管数量也逐步逼近10000000个。
我们先看下最原始的微处理器,因为从本质上说,微处理器实际上所做的工作一直没有变。
1974年4月,英特尔推出8080处理器,摩托罗拉公司——从20世纪50年代生产半导体和晶体管——8月推出了6800处理器。
8080是一个8位微处理器,包括6000个晶体管,运行时钟频率为2MHz,寻址空间64KB。摩托罗拉6800包括4000个晶体管,寻址空间64KB。第一个版本的6800运行速度为1MHz。
这些芯片被称为”单芯片微处理器“。8080和6800都是40个管脚的集成电路。这些芯片最常见的IC封装大约2英寸长,1/2英寸宽,1/8英寸厚。
内部的硅晶片很小,早期的8位微处理器中,硅晶片还不到1/4平方英寸。外包装可以保护内部的硅晶片,并且通过管家提供了处理器的输入和输出访问接入点。下面是8080的40个管脚功能说明图:
8080一个特殊的地方是需要提供三种电源电压,1976年英特尔发布8085,目的就是简化对电源的要求。
从芯片引出的箭头表示输出信号,反之输入。
8080需要两个不同的同步时钟输入,他们的频率都是2MHz,分别标记为和
,位于管脚22和15上。这些信号可以由英特尔生产的8224时钟信号发生器产生。为8224连接一个18MHz的石英晶体后,它基本可以完成其他工作了。
一个微处理器通常有多个用来寻址存储器的输出信号。数目与微处理器的可寻址空间大小直接相关。8080有16个寻址的输出信号,标记为A0~A15。因此可寻址空间大小为216,即65536字节。
8080是一个8位的微处理器,可以一次从存储器读取或向存储器写入8位数据。D0~D7是既可以输入又可以输出的信号。芯片的其余10个管脚是控制信号。例如,RESET(复位)输入用于控制微处理器的复位。输出信号的功能是指明微处理器需要向RAM中写入数据(
信号对应于RAM阵列的写输入)。使用8080芯片构建的计算机系统通常使用8228系统控制芯片来锁存附加的控制信号。
8080芯片复位后,它把锁存在存储器0000h地址处的字节读入微处理器,通过在地址信号端A0~A15输出16个0实现该过程。读取的字节必须是8080指令,读取该字节的过程被称为取指令。
之前介绍的指令都是3个字节长,包括1字节操作码和2字节地址。在8080中,指令长度可以使1字节、2字节、或者3字节。这些指令组合在一起构成了计算机程序,可以用来做一些很有趣的事情。
当8080以最高速度2MHz运行时,每个时钟周期是500ns,8080的每条指令需要4~18个时钟周期,这就意味着每条指令的执行时间为2~9us。
跟我们以前讲的一样,8080的8位累加器在助记符中也记做A,加载和保存的助记符为STA(Store Accumulator加载到累加器,操作码32)和LDA(Load Accumulator,保存到累加器,操作码3A):
8080芯片的微处理器的内部除累加器外还设置了6个寄存器,每个寄存器可以存放一个8位的数。这些寄存器和累加器非常相似,事实上累加器被视为一种特殊的寄存器,本质上都是锁存器。当然,其他寄存器没有累加器所具有的丰富功能,如相加相减之类。
8080中用B,C,D,E,H和L来表示新增的6个寄存器,用HL因为它们有特殊含义,H可以代表高L代表低。通常把两个8位寄存器H和L合起来构成一个16位的寄存器对,称作HL。寄存器不是必须的,但是程序访问内存的次数越少其执行速度就越快。
8080中的MOV指令至少用到了63个操作码,它主要用来把一个寄存器的内容转移到另一个寄存器(也可能是原来的寄存器)。因为8080微处理器设计了7个寄存器(包括累加器在内),因此应用中需要使用大量的MOV指令是很正常的。
下面列出了前32条MOV指令,两个参数中,左侧是目标操作数,右侧是源操作数。
对于LDA A,[aaaa]
MOV B,[HL]
第一种方式称作直接寻址,第二种称作间接寻址。
MOV A,A 并不会有任何意义,而指令MOV [HL],[HL]是不存在的,事实上,与之对应的指令是Halt,即停止指令。
研究MOV操作码的位模式能更好的了解它,MOV操作码由8位组成:
01dddsss
其中ddd这三位是目标操作数的代码,sss这三位是源操作数的代码。意义如下:
例如,指令MOV L,E 对应的操作码是01101011,用十六进制可表示为6Bh,这与之前的表格是一致的。
可以设想下,8080内部可能是这样的:标记为sss的3位用8-1数据选择器,标记为ddd的3位用来控制3-8译码器以此确定哪个寄存器锁存值。
寄存器B和C也可以组成16位的寄存器对BC,D、E组成DE。表现为下面的指令实现:
另一种类型的传送(Move)指令称作传送立即数,它的助记符写作MVI(Move Immediate)。传送立即数指令是一个双字节指令,第一个字节为操作码,第二个是数据。这个单字节数据从存储器转移到某个寄存器,或者转移到存储器中的某个存储单元,该存储单元由HL寄存器对寻址。
例如,当指令 MVI E, 37h 执行后,寄存器E里面存放的字节是37h。
下面包括32个操作码,完成加法(ADD)、进位加法(ADC)、减法(SUB)和借位减法(SBB)这4种基本的算术运算。可以看到,在所有例子中,累加器始终用于存放其中的一个操作数,同时用来保存计算结果。指令如下:
假如累加器A存放的字节是35h,寄存器B存放的字节是22h,经过减法计算:
SUB A, B
累加器中的值变为22h,即两个字节的差。
如果累加器A中的值为35h,寄存器H和L中的值分别是10h和7Ch,而存储器107Ch处的字节为4Ah,指令:
ADD A, [HL]
将累加器的值与存储地址107Ch的值4Ah相加,保存7Fh到累加器中
8080中,使用ADC和SBB指令可以对更高位的数进行加减法(16、32......位),例如,假设现在寄存器对BC和DE各自保存了一个16位的数,我们要把这两个数相加并保存结果到寄存器对BC中:
MOV A, C ;低字节操作
ADD A, E
MOV C, A
MOV A, B ;高字节操作
ADC A, D
MOV B, A
现在来讨论8080的标志位,以前我们介绍的已经有了CF(进位标志位)和ZF(零标志位)两个标志位,在8080中又新增了3个标志位,符号标志位SF,奇偶标志位PF和辅助进位标志位AF。有一个专门的8位寄存器用来存放所有的标志位,该寄存器称作程序状态字(Program Status Word,PSW)。LDA、STA或MOV指令始终不会影响标志位,而ADD、SUB、ADC以及SBB指令会影响标志位的状态,具体如下:
·如果运算结果的最高位是1,那么符号标志位SF标志位置1,表示该计算结果是负数。
·如果运算结果为0,则零标志位ZF置0.
·如果运算结果中“1"的位数是偶数,即具有偶数性,则奇偶标志位PF置1,反之为奇数性PF置0.由于PF的这个特点,有时会被用来进行简单的错误检查。PF在8080程序中并不常用。
·进位标志位CF的情况和以前描述的稍有不同,当ADD和ADC运算产生进位或者SUB和SBB运算不发生借位时,CF都置1.
·辅助进位标志位AF只有在运算结果的低4位向高4位有进位时才置1,它只用于DAA(Decimal Adjust Accumulator,十进制调整累加器)指令中。
下面两条指令会直接影响进位标志位CF
以前我们设计的只能ADD、ADC、SUB和SBB。而8080功能更为强大,它可以AND、OR、XOR等逻辑运算,都是由8080处理器的算术逻辑单元(ALU)来完成
AND、XOR和OR都是按位运算指令,操作数的每一个对应的位都是独立运算。如:
MVI A, 0Fh
MVI B, 55h
AND A, B
保存到累加器的结果将会是05h,如果是OR,结果为5Fh,如果是XOR,结果为5Ah。
CMP(compare,比较)指令同SUB指令类似,也是把两个数相减,不同之处在于它并不在累加器中保存计算结果,计算的目的是为了设置标志位。这个标志位可以告诉我们两个操作数之间的大小关系。如:
MVI B, 25h
CMP A, B
指令执行后,累加器A中值无变化,改变的是标志位的值,如果A中的值等于25h,则零标志位ZF置1;如果A中的值小于25h,则进位标志位CF置1.
同样,也可以对立即数进行这8种算术逻辑操作。
例如,可以用下面这条指令来代替上面两条指令:
CPI A, 25h
下面是两种特别的8080指令
CMA是Complement Accumulator缩写。对累加器中的数按位取反。同XRI A, FFh效果一样。
DAA(Decimal Adjust Accumulator,十进制调整累加器),它可能是8080中最复杂的一条指令,8080微处理器中专门设计了一个完整的小部件用来执行该指令。
DAA提供了一种用二进制码表示十进制数的方法,称为BCD码,程序员可以在该指令的帮助下实现十进制数的算术运算。BCD码采用的表示方式为,每4位为一段,每段所能表示数的范围是:0000~1001,对应十进制数的0~9.一个字节可以表示两位十进制数。
8080中还提供了对一个数加1减1的指令,减一则是与FFh相加:
INR和DCR都是单字节指令,它们可以影响除CF(Carry Flag)之外的所有标志位。
8080还包括4个循环移位(Rotate)指令,向左或向右移动移位:
这些指令只对进位标志位CF有影响
RLC、RRC移位的那最高位为1时,CF置1,并且RLC、RRC移位都会将移去的最低位移到最高位上去。
RAL,左移时会把CF中原来的值移植累加器中数值的最后一位,同时把累加器中的数据原最高位移至CF。如:10100111且CF为0。执行RAL指令后,累加器的数变为01001110而CF变为1.RAR正好相反,变为01010011而CF为1.使用移位乘2除2的时候就会变得很方便。
我们把微处理器可以寻址访问的存储器称为随机访问存储器(random access memory,RAM),只要提供存储器地址,微处理器可以用非常简便的方式访问存储器的任意存储单元。这是一种很好的访问方式。
但是有一种存储方式既不是随机存储,也不是顺序的。这种形式的存储器称作堆栈,使用时,从底部到顶部顺序的把数据存入堆栈,并以相反的顺序把数据从堆栈中取出,因此也称作后进先出存储器(last-in-first-out,LIFO),特点是最先保存到堆栈中的数据最后被取出,而最后保存的数据则被最先取出。
如果我们要用到寄存器A、B、C,但是其他地方可能也需要用到A、B、C中的数据,使用完后必须得把A、B、C中的数据还原。这个时候我们可以把数据从寄存器保存的存储器中,用完后在取出来。如果是之前我们需要记录下存储器的地址,但是有了堆栈概念后,我们就不需要了:
PUSH A
PUSH B
PUSH C
保存到堆栈后,我们可以使用A、B、C了,使用完成后,我们使用
POP C
POP B
POP A
即可将值复原。顺序很重要。多次使用也不会引起混乱,即使其他程序在POP C之前也进行了PUSH操作,但是POP C之前也会进行POP操作。
堆栈其实就是一段普通的RAM存储空间,只是这段空间相对独立不另作他用。8080微处理器设置了一个专门的16位寄存器对这段存储空间寻址,这个特殊的寄存器称为堆栈指针(SP,Stack Pointer)。
上面举的例子不是很恰当,因为8080中,我们指令实际上是对16位数操作,所以要如下修改:
最后一行指令中的PSW代表程序状态字,是一个8位寄存器,用于保存标志位。最后一行的PUSH和POP指令操作对象实际上是累加器和PSW,即压入和弹出堆栈的数据由累加器和PSW中的内容组成。可以使用如下来保存所有寄存器数据和标志位到堆栈:
PUSH PSW
PUSH BC
PUSH DE
PUSH HL
堆栈是怎样工作的?我们假设现在堆栈指针是8000h,当执行PUSH BC指令时,引发如下操作:
·堆栈指针减1,变为7FFFh。
·寄存器B中的内容被保存到堆栈指针指向的地址,即存储地址7FFFh处。
·堆栈指针减1,变为7FFEh。
·寄存器C中的内容被保存到堆栈指针指向的地址,即存储地址7FFEh处。
类似的,在堆栈指针仍为7FFEh的情况下,执行POP BC指令时会将上面的步骤发过来执行一遍:
·堆栈指针指向的地址(7FFEh)的内容加载到累加器C。
·堆栈指针加1,变为7FFFh。
·堆栈指针指向的地址(7FFFh)的内容加载到累加器B。
·堆栈指针加1,变为8000h。
过多的PUSH会覆盖掉其它代码或数据,这错误叫 堆栈上溢。类似的,过多的POP错误称为堆栈下溢。
8080使用LXI指令为堆栈寄存器赋值,LXI是Load Extended Immediate的缩写,即加载扩展的立即数。下面这些指令把操作码后的两个字节保存到16位寄存器对中。
指令LXI BC, 527Ah 和 MVI B,52h MVI C,7Ah 相等。
类似的,还可以对寄存器对来加减1操作:
下面指令由任意2个寄存器组成的16位寄存器对的内容加到(加法)寄存器对HL中。
这些指令可以减少操作的字节数。例如,上面的第一条指令一般情况下需要6个字节。
MOV A,L
ADD A,C
MOV L,A
MOV A,H
ADC A,B
MOV H,A
DAD指令一般用来计算存储器地址,只对进位标志位CF有影响。
下面我们认识下其他的指令:
第一条指令把HL寄存器对的内容保存到该地址,第二条指令把该地址的内容加载到HL寄存器对。
寄存器L的数据保存在地址aaaa,而寄存器H的数据保存到地址aaaa+1。
下面两条指令把寄存器对HL保存的数据加载到程序计数器和堆栈指针。
PCHL指令本质上是一种Jump指令(类似的指令还有Brance(分支),Goto(无条件转移)),它把HL保存的存储器的地址加载到程序计数器,而8080处理器要执行的下一条指令就是程序计数器所指明的存储器地址中所存放的指令。SPHL指令可以作为另一种为堆栈指针赋值的指令。
下面两条指令中,第一条将HL保存的数据与堆栈顶部的两个字节进行交换;第二条指令将HL保存的数据和寄存器对DE保存的数据进行交换。
跳转(condition jump)指令的作用比其他的要大,它会根据某些标志位的值转移到特定的地址,这些标志位可以是进位标志位CF、零标志位ZF等。正是由于条件跳转指令的引入,我们前面所涉及的自动加法器才成为一般意义上的数字计算机。
8080有5个标志位,其中4个可用于条件跳转指令。支持9种不同的跳转指令,包括了非条件跳转,还包括ZF、CF、PF、SF是否为1而跳转的条件跳转指令。
先介绍下与Jump指令相关的另外两种指令。Call(调用)指令,它和Jump指令类似,但是有所不同的是,执行Call指令后,PC加载一个新的地址,而处理器会把原来的地址保存起来,保存到堆栈去。Return指令从堆栈中弹出两个字节,并加载到PC中,这样就完成了返回到跳转点的工作。
下面介绍一个示例,乘数和被乘数在8080的子程序中,分别放在了寄存器B和寄存器C,乘积保存到16位寄存器对HL中,8080中的乘法子程序如下:
Multiply: PUSH PSW ;将要修改的寄存器的原内容保存至堆栈
PUSH BC
SUB H, H ;将HL(即乘积)置为0000h
SUB L, L
MOV A, B ;乘数送至累加器A
CPI A, 00h ;如果累加器中的值是0,则结束
JZ ALLDone
MVI B, 00h ;将BC的高字节置为0
MultLoop: DAD HL, BC ;将BC的内容加到HL
DEC A ;乘数减1
JNZ MultLoop ;如果不为0,则跳转
AllDone: POP BC ;将堆栈中保存的数据恢复至寄存器
POP PSW
RET ;返回
在程序中,使用下面指令调用:
MVI B, 25h
MVI C, 12h
CALL Multiply
CALL指令把PC的值保存到堆栈中,被保存的这个值是CALL指令的下一条指令的地址,这样RET指令执行后,直接返回到CALL调用处的下一条指令开始执行。
8080指令集还包括了条件CALL和条件Return指令,如下:
微处理器还得和外围设备(除存储器外与微处理器连接的设备)互相通信。外围设备配备了与存储器类似的接口,微处理器通过与某种外围设备对应的特定地址(即接口)对其进行读写操作。在某些微处理器中,外围设备实际上占用了一些通常用来寻址存储器的地址,这种结构称作内存映像I/O。但是在8080中,除了常规的65536个地址外,另外增加了256个地址专门用来访问输入/输出设备,它们被称作I/O端口(I/O ports)。I/O地址信号标记为A0~A7,但I/P的访问方式与存储器的访问方式不同,两者的区分由8228系统控制芯片的锁存信号来标识。
OUT(输出)指令把累加器中的内容写入到紧跟该指令后的字节所寻址的端口(port)。IN(输入)指令把一个字节从端口读入到累加器。格式如下:
而外围设备获取处理器的注意,是通过中断(interrupt)机制来实现的,由外围设备产生信号,连接至8080的INT输入端。
复位后,程序必须设置EI(Enable Interrupt)指令来允许终端,或执行DI(Disable Interrupts)禁止终端。这两条指令如下:
8080的INTE输出信号用来指明何时允许中断。当外围设备需要中断微处理器的当前工作时,它需要把8080的INT输入信号置1。8080通过从存储器中取出指令来响应该中断,同时控制信号指明有中断发生。外围设备通常提供下列指令来响应8080微处理器。
上面的这些指令称作Restart(重新启动)指令,在其执行过程中也会把当前PC中的数据保存到堆栈,这一点与CALL指令类似。但Restart指令在保存PC数据之后会立刻跳转到特定的地址,而且是根据参数的不同将跳转到不同的地址:如RST 0跳转到0000h,RST 1跳转到0008h处,依次类推,RST 7将跳转到0038h。这些地址存放的都是用来处理中断的。例如,由键盘引起的中断将执行RST 4指令,跳转到0020h处。
NOP(no operation,无操作)指令使处理器什么操作也不执行,即保持处理器运行状态而不做任何事情。
6800在构造和功能方面与8080非常相似,图如下:
VSS:接地 Vcc:5V电源
同8080一样,6800也有16个地址输出信号端和8个数据信号端,其中数据信号端既可以用于输入信号也可以用于输出信号。它还有一个RESET信号端和一个R/(read/write,读/写)信号。
信号代表中断请求。与8080相比,6800的时钟信号较为简单,6800没有设计独立的I/O端口,所有的输入/输出设备的地址都是存储器地址空间的一部分。
6800有一个16位的程序计数器PC、一个16位的堆栈指针SP、一个8位的状态寄存器(保存标志位),以及两个8位的累加器A、B。A和B都可以用做累加器,因为A和B的功能完全相同,任何用A做的工作都可以用B实现。与8080不同,6800没有设置其他的8位寄存器。
6800设置了一个16位的索引寄存器(index register),它可以用来保存16位的地址,其功能与8080的HL寄存器对相似。对于6800的大部分指令来说,它们的地址都可以由索引寄存器与紧跟在操作码后的字节相加得到。
尽管6800和8080的操作大致一致,但是助记符和操作码完全不同,下面是6800的转移指令集
与8080不同,6800没有奇偶标志位,而是设置了一个溢出标志位(Overflow flag)。上面转移指令中,有一些依赖于标志位的组合。
下面介绍一个LDA指令,从存储器的特定地址将数据加载到累加器。
除了命令的操作码不一样外,8080操作的是347Bh处的字节,低字节在前面。6800操作的是7B34h地址处的字节,高字节在前面。
这分别称为little-endian(Intel采用这种方式,低字节在前)和big-endian(Motorola采用这种方式,高字节在前)。
后来,8080被应用在了一些人所谓的第一台家用电脑上。1977年,斯蒂夫.乔布斯和史蒂芬.沃兹内卡创立的苹果公司推出了AppleII,它使用了基于MOS技术的更加便宜的6502芯片,是6800的改进加强版本。
1978年,英特尔推出了8086的16位微处理器芯片,可寻址1MB地址空间。但是与8080不兼容,但是它包含了乘法和除法指令。一年后,英特尔推出了8088芯片,内部结构和8086完全相同,IBM在5150个人计算机中使用了8088芯片,这种计算机通常称为IBM PC。于1981年秋季推出。
IBM大举进军个人计算机(PC)市场对业界产生了重大影响,许多公司推出了与个人计算机兼容的机器。
苹果公司的Macintosh于1984年首次发布,采用摩托罗拉的68000微处理器,68000是16位微处理器,是6800下一代产品。
1994年开始,Macintosh计算机开始使用PowerPC微处理器,由摩托罗拉、IBM以及苹果公司联合开发。PowerPC采用了RISC微处理器体系结构来设计。RISC中,通常指令都是等长的(PowerPC中是32位),只有加载和保存两种指令能访问存储器,并且尽量简化指令操作。RISC处理器设置了大量的寄存器,这样就能避免频繁访问存储器以提高其运行速度。
现代处理器使用多种技术来提高其运行速度。其中一种就是流水线技术,即处理器在执行一条指令的同时读取下一条指令,尽管Jump指令在一定程度上会改变这种流程。现代处理器还包括一个Cache(高速缓冲存储器),它是一个设置在处理器内部,访问速度非常快的RAM阵列,用来存放处理器最近要执行的指令。由于计算机程序经常需要执行一些小的循环指令,使用Cache可以避免反复加载这些指令。这些都需要在处理器内部增加更多的逻辑组件和晶体管。