汇编语言程序设计教程(txt+pdf+epub+mobi电子书下载)


发布时间:2020-06-09 17:30:40

点击下载

作者:王庆生

出版社:人民邮电出版社

格式: AZW3, DOCX, EPUB, MOBI, PDF, TXT

汇编语言程序设计教程

汇编语言程序设计教程试读:

前言

汇编语言程序能直接而精确地控制计算机硬件的操作,在一些需要直接控制硬件的应用场合,汇编语言更能发挥其优势。

汇编语言与计算机硬件(特别是核心部件CPU)具有高度相关性,能清楚细致地体现计算机硬件和软件互为支撑、紧密合作的“骨肉”联系。汇编语言程序的运行能使我们更深入理解计算机的工作原理和特点,它从程序角度帮助我们认知计算机,单纯地介绍计算机的硬件知识或一门高级语言的程序设计是不可能做到这点的。

作为计算机学科中处于基础地位的“汇编语言”,不仅具有兼顾计算机硬件和软件这一特点,而且对后续的“计算机组成原理”、“微机原理与接口技术”、“嵌入式系统”、“单片机技术”等相关课程的学习会有所帮助。因此,“汇编语言程序设计”依然作为专业基础核心课程,并不因C++、Java等编程语言的出现而淘汰。

本书是国家质量工程特色专业“TS11483”的配套教材,在编写本书和教学的过程中,我们并不仅仅是为了讲授一门编程语言,而是希望读者能够通过指令理解硬件,通过编程提高程序设计能力。在基本程序设计部分,程序实例都由思路和流程图开始,引导读者养成正确的编程习惯。和其他编程语言一样,上机实践非常重要,所以本书的第2章就以两个简单实例开始介绍程序的编写、汇编、连接和调试等全过程,而不必等到学完指令系统才介入上机。

本书内容的编排和实例的讲解力求思路清晰,循序渐进。例题的选择不主张难度,而注重体现基本内容和有键盘输入及显示输出的典型应用,既便于上机验证,又不致使初学者望而生畏。

作者希望本书能体现知识内容有梯度,要点分析有深度,既保证内容的系统性,也保留选择性,以兼顾各层次的学生,既可作为本科教材,也能适用专科层次,同时也可供技术人员自学参考。

本书介绍基于80x86的汇编语言程序设计的方法和技术。全书分为3部分共10章,第1章~第8章为基本内容,其中第4章的32位新增指令为可选部分,第9章~第10章主要介绍中断与输入输出应用,也作为可选部分。

为配合上机实验,本书实验部分提供了9组实验题供选用和参考。附录部分收录了指令和伪指令、DOS和BIOS功能调用,便于查阅。

本书配套课件及书中的习题和实验题的参考答案,可登录人民邮电出版社教学服务与资源网(www.ptpedu.com.cn)免费下载或通过wqs2001@163.com邮箱索取。

尽管本书编写组的各位教师多年教授此课,但一定也会有不足和不当之处,欢迎广大读者批评指正。编者2012年10月第1章汇编语言基础知识1.1 汇编语言简介

汇编语言是基于具体硬件的编程语言,需要了解编程中涉及的硬件知识,例如对于我们普遍使用的个人计算机来说,需要了解80x86计算机系统的核心部件中央处理器(CPU)的寄存器、存放数据和程序的存储器以及外部设备和接口。通过本章的学习,认识汇编语言的意义,重点熟悉计算机中数据和字符的常用表示方法、补码的运算、CPU中的寄存器以及主存储器的分段,为下一步学习汇编语言程序设计打下基础。这些基础知识对深入了解计算机也很有帮助。1.1.1 机器语言与汇编语言

计算机程序是由各种程序设计语言根据编程规则实现的,计算机程序设计语言经历了从低级到高级的发展,通常分为三类:机器语言(Machine Language)、汇编语言(Assembly Language,也有叫组合语言)、高级语言(High Level Language)。

机器语言:计算机硬件直接识别的程序设计语言。构成这种程序的是机器指令,机器指令是用二进制编码的指令,即编码中只含二进制0或1,如1110011000110011就是一条机器指令。由于计算机主要由数字电路构成,所以机器指令由计算机直接记忆、传输、识别和加工。

机器语言被称为第一代语言,不仅复杂难记,而且还依赖于具体的机型。程序编写难度极大,调试修改困难,无法在不同的机型间移植。早已没有人用机器语言写程序了。

汇编语言:一种面向机器的用符号表示的程序设计语言,所以也叫符号语言。和机器语言不同的是,汇编语言用直观、便于记忆和理解的英文单词或缩写符号来表示指令和数据变量,例如, MOV AX,VAL是一条传送指令,其中MOV是指令操作码,AX是CPU中的寄存器,VAL是一个变量的符号表示,指令表示将变量VAL的值传给AX。所以汇编指令也叫符号指令,这些符号称为助记符。汇编指令集和伪指令集及其使用规则的统称就是汇编语言。汇编语言被称为第二代语言。

对于MOV AX,VAL这样的符号指令,比较简洁易读,但是计算机并不识别助记符,只能识别二进制编码的机器指令,因此需要通过一种翻译程序把汇编语言源程序翻译成机器语言程序,才能提交计算机执行。这种翻译程序叫汇编程序,这种对汇编语言源程序的翻译过程简称汇编。汇编语言的出现,大大改善了编程条件,使更多的人可以进行程序设计了。

尽管用汇编语言编写的程序要比机器代码更容易理解,但每条汇编语言指令均对应一条机器指令,因而与机器语言并没有本质区别,因此汇编语言仍然属于面向机器的低级语言。

为了克服低级语言程序的不好理解、编程调试困难、不易移植的弊端,人们迫切希望有一种近乎自然语言或数学表达形式的程序设计语言,使程序设计工作能避开与机器硬件相关,而着重于解决问题的算法本身,于是便产生了高级语言,例如BASIC、C、Java等高级语言。高级语言被称为第三代语言。

用高级语言编写的源程序也必须经过编译和连接,将其转换为机器语言程序提交给计算机执行,或将其转换为一种中间代码,通过解释程序解释运行。

无论你是用什么语言编程,最终在计算机硬件中执行的程序都是由机器语言指令组成的,因此汇编语言是最接近计算机硬件的。1.1.2 为什么要学习汇编语言

高级语言易学好用,那为什么还要学习汇编语言呢?(1)学习汇编语言对于从事计算机应用开发有重要作用。汇编语言程序是由符号指令写成的,本质上还是机器语言,与具体机型的硬件结构密切相关,可直接、有效地控制计算机硬件,运行速度快,程序短小精悍,占用内存容量少。在某些特定应用场合更能发挥作用,如实时控制系统,需要对硬件设备直接进行数据的输入输出和控制,如在嵌入式系统和智能化仪器的开发中,需要更好地利用有限的硬软件资源,发挥硬件的功能。(2)学习汇编语言是从根本上认识和理解计算机工作过程的最好方法,通过汇编语言指令,可以清楚地看到程序在计算机中如何一步步执行,有利于更深入理解计算机的工作原理和特点,单纯地介绍计算机的硬件知识或一门高级语言的程序设计是不可能做到这点的。汇编语言把软件和硬件紧密地结合在一起,起到连接硬件和软件的桥梁作用,掌握汇编语言对今后学习其他计算机相关课程非常有利。1.2 计算机中数据的表示1.2.1 不同进位计数制及其相互转换

1.二进制数

进位计数制是一种计数方法,我们最熟悉的是十进制数,如423.5可表示为210-1

423.5=4×10+2×10+3×10+5×10

这里每位数字只能取0到9共10个数字符号,因此基数为10。逢10进1。不同位置上的数字代表的“权”是不同的,如百位4,该位2k的权值为10,第K位的权值为10。

计算机为便于数字电路对数据存储及计算的物理实现,采用二进制数。二进制数只有0和1两个数码,基数为2,逢2进1。不同位置上k的数码代表的“权”不同,即各位权值为2。例如二进制数5320101101B=1×2+1×2+1×2+1×2=45D。

可以看出,上面的二进制数按权展开就得到对应的十进制数。为便于明确计数制而不致误解,通常在二进制数后面加(B),在十进制数后面加(D)(也可用下标2和下标10)。即:

101101B=45D,或(101101)=(45)210n

n位二进制数可以表示2个数,例如4位二进制数,可以表示16个数,如表1.1所示。表1.1 二进制与十进制

2.十六进制数

用二进制数表示一个较大的数总是不太方便,为便于程序员表示数据,通常还采用十六进制。

十六进制数有16个数码,基数为16,逢16进1。第K位置上的数k码代表的权值为16。每位的数码作如下规定:0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F,共16个数码。其中A,B,C,D,E,F分别表示十进制的10,11,12,13,14,15。十六进制数后面要加上H以示区别。

例如十六进制数:10

5FH=5×16+15×16=80+15=95D

显然十六进制表示比二进制表示来得简洁,该数用二进制表示则为

5FH=01011111B

一位十六进制数用四位二进制数表示即可,反之亦然。可见二进制数与十六进制数有着简单直接的互相转换关系,这是因为十六进制K数的基数是2的幂。不仅如此,二进制数与2进制数都可以简单直接地互相转换。

实际生活中存在多种计数制,如计时采用时分秒就是60进制。但道理都是一样的。

3.二进制数、十六进制数转换为十进制数

如前所述,各位二进制数码乘以对应的权之和即得到十进制数。如:5320-1

例1.1 N=101101.1B=1×2+1×2+1×2+1×2+1×2=45.5D

各位十六进制数码乘以对应的权之和即得到十进制数。如:10

例1.2 N=5FH=5×16+15×16=80+15=95D

4.十进制数转换为二进制数

这里介绍常用的两种方法。(1)降幂法(适用于数值不大的数),降幂法就是先写出小于此数的各位二进制权值,然后再求和。

例1.3 求N=13.5D的二进制数。小于此数的各位二进制权值为

8 4 2 1 0.5

显然应选8,再选4,而不能选2(因为8+4+2=14),再选1,最后选0.5,所以:

13.5D=8+4+1+0.5=1101.1B(2)除法(又叫除2取余法,仅适用于整数部分),除2取余法就是不断除以2,记下余数,直到商为0为止。

例1.4 求N=13D的二进制数。

13/2=6 余 1 (b)0

6/2=3 余 0 (b)1

3/2=1 余 1 (b)2

1/2=0 余 1 (b)3

13D=bbbb=1101B3210

对于二进制数的小数部分除了降幂法也可采用乘法,即不断乘 2,并记下整数,而小数部分再乘2,直到结果的小数部分为0为止。注意:并非所有的十进制小数都能用二进制完全表示,如小数0.3,这时按实际需要取一定精度表示即可。

例1.5 求N=0.625D的二进制数。

0.625×2=1.25   (b1=1)-

0.25×2=0.5   (b2=0)-

0.5×2=1.0    (b3=1)-

N=0.625D=b1b2b3=0.101B---

5.十进制数转换为十六进制数

和十进制数转换为二进制数类似,也有降幂法和除法。(1)降幂法(适用于数值不大的数),降幂法就是先写出小于此数的各位十六进制数权值,然后再求和。

例1.6 求N=95D的十六进制数。小于此数的各位十六进制权值为

16  1

显然应选16×5,再选1×F,所以

N=95D=80+15=16×5+1×F=5FH(2)除法(又叫除16取余法,仅适用于整数部分),除16取余法就是不断除以16,记下余数,直到商为0为止。

例1.7 求N=95D的十六进制数。

95/16=5 余 15(h)0

5/16=0 余 5 (h)1

N=95D=hh=5FH10

对于十进制数的小数部分除了降幂法也可采用乘法,即不断乘16,并记下整数,而小数部分再乘16,直到结果的小数部分为0为止。此处不再举例。

6.二进制数和十六进制数的相互转换4

由于十六进制数的基数16=2,故一位十六进制数由四位二进制数组成,相互转换极为简单。

例1.8 N=1011111.11(B)=01011111.1100(B)=5F.C(H)

注意到从二进制数转换到十六进制数时,二进制数的整数部分从最低位开始每4位一组,不足4位的,高位补0补足4位。小数部分从最高位开始每4位一组,不足4位的,低位补0补足4位。1.2.2 二进制数和十六进制数的运算

1.二进制数的运算规则

加法规则:0+0=0,0+1=1,1+1=0(进位1)

乘法规则:0×0=0,0×1=0,1×1=1

2.十六进制数的运算

十六进制数的加减运算只要遵循逢16进1规则即可,当然也可先把十六进制数转换为二进制数,运算后的结果再转换为十六进制数。

例1.9

例1.10

十六进制数的乘、除法运算可以先把十六进制数转换为十进制数,运算后的结果再转换为十六进制数。十六进制数的乘法也可以用十进制数的乘法规则计算,但结果用十六进制数表示。

例1.111.2.3 带符号数的补码表示

数有正数和负数,计算机中的数是用二进制来表示的,数的符号也用二进制来表示,所谓带符号数就是最高位是符号位,一般规定正数的符号位为0,负数的符号位为1。把一个数连同其符号在内数值化表示叫机器数,机器数的表示可以用不同的码制,常用的有原码、补码、反码。这里只介绍最常用的补码。

补码表示法中的正数用符号位+绝对值表示,即数的最高位为0,其余部分为该数的绝对值。例如,用8位二进制来表示:

[+1]=00000001,[+127]=01111111,[+0]=00000000补补补

负数用补码表示时,方法是对其正数各位取反,然后最低位加 1。我们把这种对二进制数取反加1的运算叫做求补运算。负数用补码表示时,其符号位必定为1。

例1.12 用8位二进制来表示,求[-3]。补

先写出+3: 00000011

各位取反为: 11111100

最低位加1为: 11111101

[-3]=1111 1101,或用十六进制表示,[-3]=FDH。补补

读者也许会问,当用16位二进制来表示[-3]呢?其实只要在刚补求出的[-3]的前面加上8个1就可以了,即[-3]=1111 1111 1111 补补1101,或[-3]=FFFDH。这叫符号扩展。对负数的符号扩展只需在前补面补1,对正数的符号扩展只需在前面补0,符号扩展并没有改变数的大小,只是改变了位数。读者可自行验证。

我们已经知道对负数用补码表示时,方法是对其正数取反加 1,即作求补运算。其实这个方法是根据补码定义得出的。下面给出证明。

补码定义:

现在我们把(2)式进一步改写:n

请注意(3)式右边括号中的(2-1-|X|),就是对|X|取反码。再+1就得到[X]补。由此可见,求负数的补码,方法是对其正数取反加1。

现在我们把(3)式进一步改写:n

注意到上式右边的(2-1-[X])+1,就是对[X]再求补码,就补补得到|X|。由(3)式和(4)式说明了以下结论:

例1.13 依据补码定义写出以下各数的补码,以8位二进制表示。8

[-1]=2-1=1 0000 0000-1= 1111 1111,直接由(2)式得到。补88

[-127]=2-127=(2-1-127)+1补

=(1111 1111-0111 1111)+1

= 1000 0000+1

= 1000 0001

依据补码定义求一个数的补码表示,有些烦琐,用取反加1的规则更为简便。

例1.14 识别以下各数的十进制值。

[a]=1111 1111,求补后为0000 0001=[1],所以,a=-1补补

[b]=1000 0000,求补后为1000 0000=[128],所以,b=-128补补

[c]=1000 0001,求补后为0111 1111=[127],所以,C=-127补补

前面我们都是以8位二进制数讨论,8位二进制数可以表示82=256个数,当它们是补码表示的数时,所能表示的范围是-128≤N≤+127。1.2.4 补码的加法和减法

计算机中主要是用补码表示数据,因此我们应关注补码的加法和减法。

补码的加法规则是:

补码的减法规则是:

这个规则说明了用2个数的补码相加就可以完成2个数的加减法,得到的还是补码。

下面给出证明:

需明确X>0,Y>0,先看(5)式,由补码定义可知,[X+Y]= 补X+Y =[X]+[Y]。补补(5)式得证。再看(6)式:

如果[X-Y]≥0,由补码定义可知,(6)式左边应有[X-Y]=X-Y,补nn而(6)式右边=X+(2-Y)=X-Y+2=X-Y。nn

如果[X-Y]<0(或者说Y>X),应有[X-Y]=2-|X-Y|=2-(Y-X)补n=2–Y+X=[-Y]+[X]。补补(6)式得证。

下面给出例子说明补码的加法运算。

例1.15 8位补码的加法运算。

十进制

二进制

从例中可以看出补码的加法很简便,不必考虑数的正负,符号位参于运算即可,计算结果都是正确的。从最高有效位向高位的进位由于机器字长的限制而自动丢弃,但结果依然正确。同时这个进位被保存到机器中的标志寄存器中,其作用以后再说明。1.2.5 无符号数的表示

如果要处理的数全是正数,保留符号位就没有必要,我们可以把最高有效位也作为数值,这样的数就叫无符号数。8位二进制来表示的无符号数的表数范围是0≤N≤255,16位二进制来表示的无符号数的表数范围是0≤N≤65535。在计算机中最常见的无符号数是表示内存单元的地址。例如:1100 0010B=C2H=194D,而不再表示一个负数。1.2.6 字符的表示

除了数值以外,人们有时还需要用计算机处理字符或字符串。例如,从键盘输入或打印输出信息都是以字符方式进行的。字符包括以下内容。

字母:A,B,C,D……;

数字:0,1,2,3……;

专门符号:+,-,×,/,SP(space空格)……;

非打印字符:CR(carriage return回车),LF(line feed换行)……。

这些字符必须采用二进制的编码方式,目前采用最常用的美国信息交换标准代码 ASCII(American Standard Code for Information Interchange)来表示。

这种代码用一个字节(8位二进制码)来表示一个字符,其中低7位为字符的ASCII值,故能表示l28个符号和代码,最高位一般用作检测校验位。表1.2为部分常用字符的7位ASCII码表(十六进制表示)。

为了能表示更多的符号,将7位ASCII码扩充到8位,可以表示256个符号和代码,称为扩充的ASCII码。表1.2 部分常用字符的7位ASCII码表(十六进制表示)1.2.7 基本逻辑运算(1)“与”运算(AND),又叫逻辑乘,可用符号·或∧来表示,只有当逻辑变量A、B都为1时,“与”运算的结果才为1。(2)“或”运算(OR),又叫逻辑加,可用符号+或∨来表示,只要变量A、B其中有一个为1时,“或”运算的结果就为1。(3)“异或”运算(XOR),可用符号∀来表示,只有当变量A、B中仅一个为1时,“异或”运算的结果才为1。(4)“非”运算(NOT),对变量A取反,即如果A=1,则=0,反之亦然。

具体参见表1.3。

逻辑运算都是按位操作,例如,X=0011,Y=1011,均为二进制数,则有:

X(AND)Y=0011,X(OR)Y=1011,X(XOR)Y=1000,(NOT)X=1100表1.3 四种基本逻辑运算1.3 计算机组织1.3.1 计算机系统组成

汇编语言是面向机器的用符号表示的程序设计语言,所以使用汇编语言进行程序设计时,除了需要考虑求解问题的过程或者算法,安排数据在计算机内的存储格式,同时还要根据程序和算法的需要使用计算机内的资源。因此,作为一名汇编语言程序员,必须了解计算机的基本逻辑结构,了解有哪些可供使用的资源,以及如何使用,但无须了解其电子线路组成和电气特性。本节主要结合80x86系列微型计算机来介绍程序员需要掌握的计算机逻辑结构。

典型的计算机结构如图 1.1 所示。主要由微处理器芯片构成的中央处理机(CPU)、存储器(memory)和输入输出(I/O)子系统三大部分组成,用系统总线(bus)连接在一起。(1)中央处理机(CPU,Central Process Unit)或叫微处理器(MPU,Micro Process Unit),主要包括运算器和控制器。运算器执行指令,控制器负责计算机的控制,负责从主存储器取指令,对指令进行译码,发出访问主存储器或 I/O 设备接口的控制信号,完成程序的要求。显然,CPU是最核心的部件,指令都是在这里执行的。(2)存储器,是计算机记忆部件,以二进制形式存放程序和数据。这里是指主存储器,简称主存,或叫内存储器,简称内存,称为 RAM。而把硬盘、光盘这些大容量存储器称为外部存储器,简称外存。(3)输入输出(I/O)子系统包括大容量存储器(如硬盘)和其他外设,如显示器、键盘、打印机、鼠标等。(4)系统总线连接CPU、主存储器和I/O子系统三大部分,用以完成各部分的数据交换。系统总线包括数据总线、地址总线和控制总线。数据总线负责传送数据,地址总线负责指示主存地址或I/O接口地址,控制总线负责总线的动作,如时间、方向、状态。16位微处理器数据总线的位数为16位(bit),表示一次可以并行传输和处理16位二进制数据。32位微处理器数据总线的位数为32位(bit)。地址总线宽度的多少决定了可访问的内存容量的大小。部分机型的总线位数如表1.4所示。表1.4 部分机型的总线位数图1.1 计算机结构1.3.2 中央处理器(CPU)中的寄存器

现代微型计算机把运算器、控制器、寄存器和高速缓冲存储器集成在一块集成电路芯片中。寄存器相当于运算器中的高速存储单元,放置当前参与运算的操作数地址、数据、中间结果、处理机状态等。

作为汇编语言程序员,实际上是通过对寄存器的操作来实现对CPU的操作。也就是说这些寄存器是可编程的,因此我们只对寄存器感兴趣。

80x86 CPU中的寄存器可以分为通用寄存器、段寄存器和专用寄存器三类。

在80286以前的CPU中,一条指令可运算的最大数据长度为16位二进制数(16 bit),因此我们也称这样的CPU是字长16位的CPU。其中的寄存器的标准长度为16位,有些寄存器可以分拆作为2个8位寄存器使用。32位的80x86微处理器的寄存器基本长度是32位,有些寄存器可以分拆作为8位、16位寄存器使用。

这里需要指出的是,因为本书重点介绍16位机的指令系统和程序设计,而32位机对16位机实现了软硬件的完全兼容,在16位机上编制运行的程序在32位机上照样可以运行。所以我们重点介绍16位机的寄存器。80x86的寄存器组如图1.2所示。

1.数据寄存器

16位的80x86处理器有4个16位的通用数据寄存器。它们的主要作用是存放数据,有时候也可以存放地址。

① AX:累加器,运算时较多使用这个寄存器,有些指令规定必须使用它。

② BX:基址寄存器,除了存放数据,它经常用来存放一段内存的起始偏移地址。

③ CX:计数寄存器,除了存放数据,它经常用来存放重复操作的次数。

④ DX:数据寄存器,除了存放数据,它有时存放32位数据的高16位。

上面的寄存器都可以拆分为两个8位寄存器使用,分别命名为AH,AL,BH,BL,CH,CL, DH,DL。

32位80x86处理器的4个数据寄存器扩展为32位,更名为EAX,EBX,ECX,EDX,但仍然可以使用原有的16位和8位寄存器,且名称不变,如AX,BX,CX,DX,AH,AL等,对程序员来说,似乎是增加了4个32位的数据寄存器。

2.地址寄存器

16位的80x86处理器有4个16位的通用地址寄存器。它们的主要作用是存放数据的所在偏移地址,也可以存放数据。这4个寄存器不能再拆分使用。

① SP:堆栈指针,这是一个专用的寄存器,存放堆栈栈顶的偏移地址。

② BP:基址指针,可以用来存放内存中数据的偏移地址。

③ SI:源变址寄存器,它经常用来存放内存中源数据区的偏移地址,所谓变址寄存器,是指在某些指令作用下它可以自动地递增或递减其中的值。

④ DI:目的变址寄存器,它经常用来存放内存中目的数据区的偏移地址,并在某些指令作用下可以自动地递增或递减其中的值。

32位80x86处理器的地址寄存器也扩展为32位,分别命名为ESP,EBP,ESI,EDI。图1.2 80x86的寄存器组

3.段寄存器

16位80x86处理器有4个16位的段寄存器,分别命名为CS,SS,DS,ES。它们用来存放4个段的段基址。

① CS:代码段寄存器,用来存放当前正在执行的程序段的段基址。

② SS:堆栈段寄存器,用来存放堆栈段的段基址。

③ DS:数据段寄存器,用来存放数据段的段基址。

④ ES:附加段寄存器,用来存放另一个数据段的段基址。

32位80x86处理器仍然使用16位的段寄存器,但是它们存储的内容发生了变化。此外,32位80x86处理器还增加了两个段寄存器FS和GS,它们的作用与ES类似。

4.指令指针寄存器

IP:指令指针寄存器,存放即将执行指令的偏移地址。

5.标志寄存器

FLAGS:存放CPU的两类标志。

状态标志:反映处理器当前的状态,如有无溢出,有无进位等。

控制标志:用来控制处理器的工作方式,如是否响应可屏蔽中断等。表1.5 标志位的符号说明

各状态标志的含义如下。

① OF:溢出标志。OF=l表示两个有符号数的运算结果超出了可以表示的范围,结果是错误的;OF=0 表示没有溢出,结果正确。进行无符号数运算时也会产生新的 OF 标志(CPU 不知道处理对象是否为有符号数),此时程序员可以不关心OF标志。

② DF:方向标志。DF=0时,每次执行字符串指令后,源或目的地址指针用加法自动修改地址;DF=l时用减法来修改地址。它用来控制地址的变化方向。

③ IF:中断允许标志。IF=l 表示允许处理器响应可屏蔽中断请求信号,称为开中断,IF=0表示不允许处理器响应可屏蔽中断请求信号,称为关中断。

④ SF:符号标志。SF=1表示运算结果的最高位为1。对于有符号数,在溢出标志OF=0时, SF=1表示运算结果为负,SF=0表示运算结果非负(正或零)。OF=1时,由于结果是错误的,所以符号位也和正确值相反。例如,两个负数相加产生溢出,此时 SF=0。对于无符号数运算,SF无意义(但是可以看出结果的大小规模)。

⑤ ZF:零标志。ZF=l 表示运算结果为零,减法运算后结果为零意味着两个参加运算的数大小相等;ZF=0,表示运算结果非零。

⑥ AF:辅助进位标志。它是两个BCD数运算时第3位上的进位,供运算后调整结果用,对其他数的运算没有意义。

⑦ PF:奇偶标志。PF=1 表示运算结果的低8位中有偶数个1;PF=0 表示有奇数个1。它可以用来进行奇偶校验。

⑧ CF:进位/借位标志。CF=1表示两个无符号数的加法运算有进位,或者是减法运算有借位,需要对它们的高位进行补充处理;CF=0 表示没有产生进位或借位。同样,进行有符号数运算时也会产生新的CF标志,此时程序员可以不关心CF标志。

状态标志在每次运算后自动产生,控制标志的值则由指令设置。

另外,还有一个单步标志TF(或叫陷阱标志),用于程序的调试。TF=1时,每执行完一条指令都会产生一次陷阱,该程序被暂停,TF=0时,不产生陷阱。

32位80x86处理器的标志寄存器也扩展为32位,更名为EFLAGS。除了原有的状态、控制标志之外,增加了2位表示I/O操作特权级别的标志。

32位80x86微处理器增加了5个32位的控制寄存器,8个用于调试的寄存器,2个用于测试的寄存器等。但这些寄存器是系统所用,对程序员而言是不可见的。1.3.3 存储器

1.基本存储单元及地址

计算机存储信息的最小单位是一个二进制位(bit),8位二进制位组成一个字节(Byte), 2个字节(16位)组成一个字(Word),2个字(32位)称为双字。80x86微机的内存储器以字节为基本存储单位,或叫基本存储单元。也就是说,对内存的读写至少是一个字节。

为了能正确地访问到每一个基本存储单元,必须给每一个存储单元以唯一的存储器地址,称为物理地址。地址的编号从0开始,顺次加1,地址也用二进制数,为书写方便,通常用十六进制格式表示,地址当然可以是无符号数。那么80x86微机能够访问多少个存储单元呢?或者说寻址范围是多少呢?

8086/8088微处理器只能工作在实模式,实地址模式下只使用低20位地址线,实际上8086/8088也只有20位地址线,DOS操作系统也只能运行在实模式下。32位的80x86微处理器有3种工作模式:实模式、保护模式和虚拟8086模式。本书重点在实模式下讨论。20

因为实模式下只使用20位地址线,2=1024K=1M,所以寻址能力为1MB,范围为00000H~FFFFFH。地址00003H相对于地址00004H的值要小,于是我们把地址值相对小的叫低地址,地址值相对大的叫高地址。

2.不同类型数据在内存中的存放和地址规定

基本存储单元只能存放一个字节,因而可称为字节单元,一个8位(bit)的数据A,在内存中只占1个字节单元。而一个数据项的长度可能不止一个字节,如一个16位的数据B,在内存中占连续2个基本存储单元,这时我们可以把存放数据B的存储单元称为字单元,一个32位的数据C,在内存中占连续4个基本存储单元,存放数据C的存储单元可以称为双字单元。

假定字节数据A=89H,字数据B=1234H,双字数据C=80865678H在内存中依次存放,图1.3表示了这3个数据在内存中的存放情况。图1.3 不同类型数据在内存中的存放

由图可以看出,字数据B占连续2个基本存储单元,请注意,数据的低位部分在低地址,数据的高位部分在高地址。双字数据C占连续4个基本存储单元,数据存放原则也是低位部分在低地址,高位部分在高地址。为便于记忆,我们把“高位部分在高地址”这一数据存放原则称为“双高原则”。当然也可称为“双低原则”。

对于字数据B占连续2个基本存储单元,如何确定它的地址呢?规定是以该数据的最低位所在的地址为该数据的地址,所以字数据B的地址是34001H,双字数据C的地址是34003H。可以这样表示:(34000H)=89H 表示存放数据 89H的内存单元地址为 34000H(34001H)=1234H 表示存放数据 1234H的内存单元地址为 34001H(34003H)=80865678H 表示存放数据80865678H的内存单元地址为34003H

如果用X表示某存储单元的地址,则X单元的内容可以表示为(X);假如X单元的数据内容为Y,而Y又是一个地址,则Y单元中的内容可以用(Y)=((X))来表示。

计算机用一条指令就可以对内存单元一次读出(或写入)一个字节、一个字或一个双字。发送给存储器的是这个数据所在的最低地址就可以了,8086 CPU可以一次读/写2B(Byte)的数据, 80386以上的CPU可以一次读/写4B的数据。对内存单元的读操作是不会改变其中内容的。

3.存储器的分段和地址表示

我们已经知道,对于1MB内存来说,地址范围是00000H~FFFFFH,即用20位地址线就可访问到所有单元。那么是否要在指令中给出20位的地址呢?

先来看一个例子:假定有一个笔直的长为 9999m的通信线路,在距离起点 4500m处有故障需要定位,但假定我们的计量表所表达的长度不足4位数,就是说不能表达4500,而只有2个能表达长度为3位数的表,那么,4500可以用2块表来表示,用一块表示45,另一块表示000,写成45:000,约定含义为:45×100+000=4500,即对第一块表数乘以100,再加上第二块表数。在这个规则下,对于4500这个值,可以有如下各种表示。

45:000

44:100

……

40:500

39:600

……

36:900

由此看来,20位物理地址可以用16位的段地址和16位的偏移地址组成。如图1.4所示。从图中可以看出,把16位的段地址左移4位,后面补4个0,再和16位的偏移地址相加,得到20位物理地址。即:图1.4 实模式物理地址的计算方法

物理地址=段地址×16+偏移地址

例如 16 位的段地址为 4000H,如果 16 位的偏移地址为FFFFH,则得到的物理地址为40000H+0FFFFH=4FFFFH。可见段地址就是该段内存起始地址的高16位,偏移地址就是相对于段起始地址的偏移值。

不难看出,段地址为4000H时,该段内存地址的范围是40000H至4FFFFH。也就是说,偏移地址用16位表示,使得一个段的大小为64KB。

一旦16位的段地址确定以后,这一段内存的起始地址就确定了。例如16位的段地址为4000H,则该段内存的起始地址为40000H;如果段地址为4001H,则该段内存的起始地址为40010H。一段内存的起始地址总是从段地址的16倍开始,每一小段16个字节,下面以十六进制数列出从0地址开始的三小段地址空间。

00000,00001,00002,…,0000E,0000F;

00010,00011,00012,…,0001E,0001F;

00020,00021,00022,…,0002E,0002F

这里每一小段称为一节,显然,1M内存有64K节。

8086 CPU内部的算术逻辑单元能进行16位运算,寄存器也都是16位的,因而8086对地址的运算也只能是16位的。用一个16位的寄存器表示段地址,一个16位的寄存器表示偏移地址,就解决了16位字长的机器产生20位物理地址,从而能够寻址1MB空间。在指令中通常只要给出16位的偏移地址,系统根据默认的段寄存器自动计算出物理地址,就可以访问内存中某一个物理单元。

用[段地址:偏移地址]来表示的地址叫作逻辑地址,每个逻辑地址对应一个唯一的物理地址。例如[4000H:0022H]=40022H,但物理地址40022H也可以用逻辑地址[4002:0002]来表示。程序中使用的是逻辑地址。

常用的存储器容量单位有:10

1KB(千字节)= 2B =1024B10

1 MB(兆字节)= 2KB10

1 GB(吉字节)= 2MB10

1 TB(太字节)= 2GB

4.程序和数据在存储器中的分配

在内存中,用户的程序和数据都是以二进制格式存放,它们在内存中的位置和所占空间大小都由系统根据具体情况酌情安排,一种可能的安排如图1.5所示。图1.5 程序和数据在存储器中的分配1.3.4 CPU对存储器的读写操作

以数据传送指令MOV AL, VAL为例,看看机器是如何操作的(见图1.6)。

首先,程序和数据被调入内存,接着开始执行程序,当[CS:IP]指向该指令所处的内存时,则该指令被取到CPU,这叫“取指令阶段”。

CPU的控制器对取到的机器指令进行译码,CPU把变量VAL的实际内存单元地址通过地址总线发往内存,产生读内存信号通过控制总线发往内存,从而把VAL的值取到CPU的AL寄存器中,这叫“指令执行阶段”。

如果地址总线只有2根,显然CPU只能访问到4个内存单元,即00,01,10,11号单元,也就是说地址线的多少,决定了可访问的内存空间。

如果数据总线有16根,表示一条指令可以在CPU和内存之间同时并行传送16位二进制数据。

请注意数据线的方向,如果是执行MOV VAL, AL指令,数据线的方向则会由CPU指向内存,即把CPU的AL寄存器中的数据写入内存变量VAL,如图1.7所示。图1.6 CPU读内存数据的过程图1.7 CPU写内存数据的过程

有三个问题值得进一步讨论。(1)在上面讨论CPU对存储器的读写操作时,有一个细节问题没有提及,读写一个字节(8位)和读写一个字(16位)有何区别?

先看读一个字节的指令:

MOV AL,[0000]

指令是要把地址为[DS:0000]的字节数据送8位的AL寄存器。

我们知道,内存是以字节为单位分配地址的,当20位地址线选中地址为[DS:0000]的单元时,该单元的8位数据送到数据总线上(只占用8位数据线),在控制线的作用下打入AL寄存器。

再看读一个字的指令:

MOV AX,[0000]

指令是要把地址为[DS:0000]和[DS:0001]这两个字节数据送16位的AX寄存器。20位地址线选中地址为[DS:0000],但相邻的[DS:0001]单元呢?注意到这两个单元的地址仅是最低位不同,高19位都是一样的,因此机器只要根据地址线的高19位(忽略最低位)就可以选定这2个单元,这2个字节单元的数据并行送到数据总线上(占用16位数据线),在控制线的作用下打入AX寄存器。(2)当内存地址是奇数时,从内存读16位的数据,机器内部如何操作实现呢?

再看一条读一个字的指令:

MOV AX,[0003]

显然此时的操作数地址为奇数,应选中偏移地址为3和4这两个单元,而它们的低4位地址分别是0011和0100,仅忽略最低位不可能选定这两个单元,实际上当指令遇到奇数地址时,机器是要读2次内存,每次读取一个字节才能读取一个字。(3)如果要把一个内存单元A的数据传送到另一个内存单元B,

试读结束[说明:试读内容隐藏了图片]

下载完整电子书


相关推荐

最新文章


© 2020 txtepub下载