1500字范文,内容丰富有趣,写作好帮手!
1500字范文 > 《汇编语言程序设计》--- 王爽 学习笔记

《汇编语言程序设计》--- 王爽 学习笔记

时间:2019-12-18 17:44:19

相关推荐

《汇编语言程序设计》--- 王爽 学习笔记

第一章 基础知识

1.1 机器语言

机器语言是机器指令的集合 机器指令展开来讲就是一台机器可以正确执行的命令

1.2 汇编语言的产生

汇编语言的主体是汇编指令 汇编指令和机器指令的差别在于指令的表示方法上。汇编指令是机器指令便于记忆的书写格式 汇编指令是机器指令的助记符。寄存器:简单的讲是CPU中可以存储数据的器件,一个CPU中有多个寄存器 计算机能读懂的只有机器指令,那么如何让计算机执行程序员用汇编指令编写的程序呢?

1.3 汇编语言的组成

1、汇编指令(机器吗的助记符) 2、伪指令 (由编译器执行) 3、其它符号 (由编译器识别) 汇编语言的核心是汇编指令,它决定了汇编语言的特性

1.4 存储器

CPU是计算机的核心部件,它控制计算机的运作并进行运算,要想让一个CPU工作,就BIU向他提供指令和数据 指令和数据存放在存储器中,也就是平时所说的内存

1.5 指令和数据

指令和数据是应用上的概念 在内存和磁盘上,指令和数据没有任何区别,都是二进制信息

1.6 存储单元

存储器被划分为若干个存储单元,每个存储单元从0开始顺序编号 对于大容量的存储器一般用的单位 1B= 8BIT 1KB = 1024B 1M = 1024KB 1G= 1024M

1.7 CPU对存储器的读写

CPU想要进行数据的读写,必须和外部器件进行三类信息的交互 地址信息(AS) 控制信息(CS) 数据信息(DS) 呢么CPU是通过什么将地址、数据和控制信息传到存储器中的呢?在计算机中专门有链接CPU和其他器件的导线,通常称为总线。地址总线(AB) 数据总线(DB) 控制总线(CB)

1.8 地址总线

CPU是通过地址总线来指定存储单元的 地址总线上能够传送多少个不同的信息,CPU就可以对多少个存储器单元进行寻址 那么,地址总线是如何发出地址信息的呢?一个CPU有N个根地址总线,则可以说这个CPU的地址总线的宽短为N 这样的CPU最多可以寻找2的N次方的内存单元

1.9 数据总线

CPU与内存或其他器件之间的数据传送是通过数据总线来进行的 数据总线的宽度决定了CPU与外界的数据传送速度

1.10控制总线

CPU对外部器件的控制是通过控制总线来进行的。这里控制总心啊是一个总称,控制总线是一些不同控制线的集合 有多少根控制总线,意味着CPU提供对外部件的多少种控制。

1.12主板

在每一台PC机中,都有一个主板,主板上有核心器件和一些主要器件 这些器件通过总线(地址总线,数据总线,控制总线)相连

1.13接口卡

计算机系统中,所有可用程序控制其设备,必须受到CPU的控制 CPU对外部设备不能直接控制,如显示器,音响,打印机等。直接控制这些设备进行工作的是插在扩展插槽上的接口卡 显卡,网卡,声卡

1.14各类存储器芯片

从读写属性上分为两类:随机存储器(RAM)和只读存储器(ROM) 装有BIOS的ROM BIOS:BASIC INPUT/OUTPUT SYSTEM 基本输入输出系统 BIOS是由主板和各类接口卡(如:夏卡、网卡等)厂商提供的软件系统,可以通过它利用该软件设备进行最基本的输入输出。在主板和某些接口卡上插有存储相应的ROM

1.15内存地址空间

1、都和CPU的总线向量 2、CPU对他们进行读写的时候都通过控制总线发出来的内存读写命令。内存地址空间:所有的物理存储器被看做一个由若干存储单元组成的逻辑存储器,每个物理存储器在这个逻辑存储器中占有一个地址段,即一段地址空间。CPU在这段地址空间中读写数据,实际上就是在相应的物理存储器中读写数据。系统中的内存地址空间分配情况,知道起始地址和结束地址 0000 - 7FFF 主存储器存储空间(RAM) 8000 - 9FFF 显存地址 A000 - FFFF 各种ROM 最终运行程序的是CPU,我们用汇编编程的时候,必须要从CPU角度考虑问题。对CPU来讲,系统中的所有存储器中的存储单元都处于一个统一的逻辑存储器中,它的容量受CPU寻址能力的限制。这个逻辑存储器就是我们所说的内存地址空间。

第二章 寄存器(CPU工作原理)

一个典型的CPU由运算器、控制器、寄存器等器件组成,这些器件靠内部总线相连 8086CPU有14个寄存器,分别为:AX, BX, CX, DX, SI, DI, SP, BP, IP, CS, SS, DS, ES, PSW

2.1 通用寄存器

8086上一代CPU中的寄存器都是8位的,为了保证兼容性,这四个寄存器都可以分为两个独立的8位寄存器 AXAH AL BX BH BL CXCH CL DX DH DL

2.2 字在存储器中的存储

一个字可以存放在一个16位寄存器中,这个字的高位字节和低位字节自然就放在这个寄存器的高8位寄存器和低8位寄存器中。

2.3 几条汇编指令

汇编指令不区分大小写 MOV AX, 18 AX = 18 MOV AH, 78 AH = 78 ADD AX, 8 AX = 8 MOV AX, BX AX = BX ADD AX, BX AX = AX + BX

2.4 物理地址

CPU访问内存单元是要给出内存单元的地址。所有的内存单元构成的存储空间是一个一维的线性空间。我们将这个唯一的地址称为物理地址。

2.5 16位结构的CPU

概括的将,16位结构描述了一个CPU具有以下几个方面的特征:1、运算器一次最多可以处理16位的数据 2、寄存器的最大宽度为16位 3、寄存器和运算器之间的通路是16位

2.6 8086CPU给出物理地址的方法

8086有20位地址总线,可传送20位地址,寻址能力为1M 8086内部为16位结构,它只能传送16位的地址,表现的寻址能力却只有64k。8086CPU采用一种在内部用两个16位地址合成20位地址 物理地址 = 段地址 x 16 + 偏移地址 “段地址 x 16”有一个更为常用的说法就是数据左移4位(二进制)

2.8 段的概念

内存并没有分段,段的划分来在于CPU,由于8086CPU用“物理地址 = 段地址 x 16 + 偏移地址” 的方式给出内存单元的物理地址使的我们可以用分段的方式来管理内存。以后,在编程时可以根据需要,将若干地址连续的内存单元看做一个段, 用 段地址 x 16定位端的起始地址(基础地址),用偏移地址定位断中的内存单元。两点注意:1、段地址 x 16必然是16的倍数,所以一个段的起始地址也一定是16的倍数 2、偏移地址为16位,16位地址的寻址能力为64k,所以一个段的长度最大为64k。

2.9 段寄存器

段寄存器就是提供段地址的 8086CPU有4个段寄存器:CS DS ES SS 当8086CPU要访问内存时,由着4个段寄存器提供内存单元的段地址。CS和IP是8086CPU中最关键的寄存器,它们指示了CPU当前要读取指令的地址。CS为代码段寄存器 IP为指令指针寄存器 1、从CS:IP指向内存单元读取指令,读取的指令进行指令缓冲区。2、IP = IP + 所读取指令的长度,从而指向下一条指令 3、执行指令,转导步骤1,重复这个过程

2.10 CS和IP

在任何时候,CPU将CS、IP的内容当做指令的段地址和偏移地址,用它们合成指令的物理地址,到内存中读取指令代码执行 如果说,内存中的一段信息被CPU执行过的话,那么,它所在的内存单元必然被CS:IP指向过。我们如何改变CS、IP的值呢?

2.11 修改CS IP的指令

JMP 段地址:偏移地址

2.12 代码段

对于8086,在编程时,可以更具需要,将一组内存单元定义为一个段。可以将长度为N(N<=64kb)的一组代码,一组地址连续,起始地址为16的倍数的内存大单元中, 这段内存使用来存放代码的,从而定义了一个代码段。如何使得代码段中的指令被执行呢?将一段内存单做代码段,仅仅是我们在编程时的一种安排,CPU并不会由于这种安排,就自动地将我们定义的代码段中的指令当做指令来执行。所以要将CS:IP指向的代码段中的第一条指令的首地址 debug R 查看、改变CPU寄存器的内容 D 查看内存中的内容 E 改写内存中的内容 A 以汇编指令的格式在内存中写入一条机器指令 FFF00 - FFFFF FFFF5 显示主板信息 B810:0000 显卡写数据

第三章 寄存器

3.1 内存中字的存储

任何连个连续的内存单元,N号单元和N+1号单元,可以将他们看成两个内存单元,也可以看成一个地址为N的单元中的高位字节单元和低位字节单元。

3.2 DS和[address]

CPU要读取一个内存单元的时候,必须先给出这个内存单元的地址 在8086PC中,内存地址由段地址和偏移地址组成 8086CPU中有一个DS寄存器,通常用来存放要访问的数据的段地址。MOV指令的功能:1、将数据直接送入寄存器 2、将一个寄存器中的内容送入另一个寄存器中 3、将内存单元中的内容送入一个寄存器中 8086CPU不支持将数据直接送入段寄存器的操作 数据 --> 通用寄存器 --> 段寄存器

3.3 字的传送

因为8086CPU是16位寄存器,有16根数据线,所以可以一次性传送16位的数据,也就是一次性传送一个字。

3.4 MOV ADD SUB 指令

MOV 寄存器,数据 MOV 寄存器,寄存器 MOV 寄存器, 内存单元 MOV 内存单元,寄存器 MOV 段寄存器,寄存器 ADD 和 SUB指令MOV 一样,都有两个操作对象。

3.5 数据段

对于8086PC机,我们可以根据需要将一组内存单元定义为一个段(可以是代码段,数据段) 我们可以将一组长度为N(N<=64k)、地址连续、起始地址为16的倍数的内存单元当做 专门存储数据的内存空间,从而定义了一个数据段。如何访问数据段中的数据呢?将一段内存当做数据段,是我们在编程时的一种安排,我们可以在具体的操作的时候,用DS存放数据段 的段地址, 在根据需要,用相关指令访问数据段中的具体单元 用MOV指令要访问内存单元,可以在MOV指令中只给出内存单元的偏移地址,此时,段地址默认在DS寄存器中。

3.6 栈

栈是一种具有特殊的访问方式的存储空间。它的特殊性就在于,最后进入这个空间的数据,最先出去。栈有两个基本的操作:入栈和出栈 入栈:讲一个新的元素放到栈顶 出栈;从栈顶取出一个元素 栈顶的元素总是最后入栈,需要出栈时,有最先被从栈中取出。

3.7 CPU提供的栈机制

现今的CPU中都有栈的设计。8086CPU提供相关的指令来以栈的方式访问内存空间。这意味着,我们在基于8086CPU编程的时候,可以将一段内存当做栈来使用。8086CPU提供入栈和出栈指令 PUSH POP 8086CPU的入栈和出栈操作都是以字为单位进行的。1.CPU如何知道一段内存空间被当做栈使用的 2、执行push和pop的时候,如何知道那个单元是栈顶单元的。8086CPU中,有连个寄存器:段寄存器 SS 存放栈顶的段地址 寄存器SP 存放栈顶的偏移地址 PUSH AX; (SP) <-- (SP) - 2 将AX中的内容送入SS:SP指向的内存单元处,SS:SP 此时指向栈顶 POP AX; 将SS:SP指向的内存单元送入AX中 (SP) <-- (SP) + 2 SS:SP 此时指向栈顶 任何时刻,SS:SP 指向栈顶元素,当栈为空的时候,栈中没有元素,也就不存在栈顶元素, 所以SS:SP 只能指向栈的最底部单元下面的单元。这个单元的偏移地址为栈最底部的字单元的偏移地址+2

3.8 栈顶越界的问题

SS和SP只记录了栈顶的地址,依靠SS和SP可以保证在入栈和出栈时找到栈顶 可是,如何保证在入栈、出栈时,栈顶不会超过栈空间。当栈满的时候在使用PUSH指令入栈,当栈空的时候在使用POP出栈,都将翻身栈顶越界问题。疑问我们既然将一段空间安排为栈,那么在栈空间之外的空间里很可能存放了具有其他用途的数据、代码等。这些数据,代码可能是我们自己的程序中的,也可能是别的程序中的。比如说在CPU中有记录栈顶上限和下限的寄存器,我们可以通过填写这些寄存器来指定栈空间的范围,然后,CPU 在执行push指令和时候靠检测栈顶上限寄存器,在执行pop指令的时候,检测栈顶下限寄存器,保证不会越界。实际情况:8086CPU中并没有提供这样的寄存器。当前栈顶在何处 当前要执行的指令是哪一条 结论:要根据可能用到的最大空间,来安排栈的大小,防止入栈的数据太多而导致越界。栈空间当然也是内存空间的一部分,它只是一段可以以一种特殊方式访问的内存空间 PUSH 段寄存器 POP 段寄存器

3.9 PUSH.POP指令

栈空间当然也是内存空间的一部分,它只是一段可以以一张特殊的方式进行访问的内存空间。PUSH POP实质上就是一种内存传送指令,可以在寄存器和内存单元之间传送数据,与MOV指令不同的是 PUSH POP 指令访问的内存单元的地址不是在指令中给出的,而是由SS:SP指出的 同时PUSH和POP指令还要改变SP中的内容 PUSH POP 等栈操作指令,修改的只是SP。也就是说,栈顶的变化范围最大为:0000 - FFFFH 8086CPU只记录栈顶,栈空间的大小我们要自己管理

3.10栈段

对于8086PC机,在编程时,我们可以根据需要,将一组内存单元定义为一个段。我们可以将一组长度为N(N<=64k)、地址连续、起始地址为16的倍数的内存单元当做 专门存储数据的内存空间,从而定义了一个数据段 当做栈来使用,从而定义了一个栈段。栈顶的变化范围是 0000 - FFFFH,从栈空时候的SP=0,一直压栈,直到栈满是SP=0,如果再次压栈,栈顶将环绕,覆盖原来栈中的内容。一段内存,可以既是代码的存储空间,又是数据的存储空间,还是栈空间,也可以什么也不是。关键在于CPU中寄存器的设置。

第四章 第一个程序

4.1 一个源程序从写出到执行的过程

编写:使用文本编辑器(notepad++ UltraEdit)用汇编语言编写源程序。编译:使用汇编语言编译程序(MASM.EXE)对源程序文件中的源程序进行编译,产生目标文件 链接:在用链接程序(LINK.EXE)对目标文件进行链接,生成可在操作系统中直接运行的可执行文件。可执行文件包含两部分内容:程序(从源程序中的汇编指令翻译过来的机器码)和数据(源程序中定义的数据) 相关的描述信息(程序有多大,占多少内存空间)

4.2 源程序

汇编指令:有对应的机器码的指令,最终会被CPU所执行 伪指令:没有对应的机器码的指令,不会被CPU执行,是由编译器来执行的指令。定义一个段 segment 和 ends 是一对成对使用的伪指令,这是写可编译汇编程序是,必须要用到的一对伪指令 segment和ends的功能时定义一个段,segment说明一个段开始,ends说明一个段结束 一个段必须有一个名称来表示,使用格式为:段名 segment 段名 ends 一个汇编程序由多个段组成,这些段被用来存放代码、数据或当做栈空间。一个有意义的汇编程序至少要有一个段,这个段用来存放代码。END 是一个汇编程序的结束标记,编译器在编译汇编程序的过程中,如果碰到了伪指令END 就结束对源程序的编译。如果程序写完了,要在结尾处加上伪指令END,否则,编译器在编译程序时,无法知道程序何处结束。ASSUME 寄存器与段的关联假设 它假设某一段寄存器和程序中的某一个用segment。。。ends定义的段相关联。通过assume说明这种关联,在需要的情况下,编译程序可以将段寄存器和某一个具体的段相关联。标号:一个标号指代了一个地址 DOS是一个单任务操作系统。一个程序p2在可执行文件中,则必须有一个正在执行的程序p1,将p2从可执行文件中加载入内存后, 将CPU的控制权交个p2,p2才能得以运行。而当p2运行完毕后,应该将CPU的控制权交还给它得以运行的程序p1,此后,p1继续运行。程序的返回 应该在程序的末尾添加返回的程序段 MOV AX, 4C00H MOV 21H

4.3 编辑源程序

4.4 编译

4.5 链接

当源程序很大时,可以将它分为多个源程序文件来编译,每个源程序编译成目标文件后,再用链接程序将他们连接到一起,生成一个可执行文件 程序中调用了摸个库文件中的子程序,需要将这个库文件和该程序生成的目标文件连接到一起,生成一个可执行文件

4.6 以简化的方式进行编译和链接

;

4.7 EXE的执行

4.8 可执行文件中的程序装入内存并运行的原理

操作系统的外壳 操作系统是由多个功能模块组成的庞大、复杂的软件系统。任何通用的操作系统,都要提供一个称为shell(外壳)的程序。用户使用这个程序来操作计算机。DOS中有一个程序 这个程序在DOS中称为命令解析器,也就是DOS系统的shell 1、我们在DOS中直接执行1.EXE时,是正在运行的command将1.EXE中的程序装入内存 2、command设置CPU的CS:IP指向程序的第一条指令(即程序的入口),从而使程序得以运行。3、程序运行结束,返回command

4.9 程序执行过程的跟踪

为了观察程序的运行过程,我们可以使用debug。debug可以将程序加载入内存,设置CS:IP指向程序的入口,但Debug并不放弃对CPU的控制,这样,我们就可以使用debug的相关命令来单步执行程序 查看每一条指令的执行结果。

第五章 [BX] 和 LOOP 指令

5.1 [BX]

我们要完整得描述一个内存单元,需要两种信息 1、内存单元的地址 2、内存单元的长度(类型) 我们用[0]表示一个内存单元时,0表示单元的偏移地址,段地址默认在DS中,单元的长度(类型)可以有具体的指令中的其他操作对象(比如说寄存器)指出,如前边的AX,AL 描述性符号“” 为了描述上的剪接,在以后的课程中,我们将使用一个描述性的符号“()”来表示一个寄存器或一个内存单元中的内容。MOV AX, [BX] 功能:BX中存放的数据作为一个偏移地址EA,段地址SA默认在DS中,将SA:EA中的数据送入AX中。MOV BX, [AX] 功能:BX中存放的数据作为一个偏移地址EA,段地址SA默认在DS中,将AX中的数据送入SA:EA中。

5.2 LOOP指令

指令的格式是:LOOP 标号, CPU执行LOOP指令的时候,要进行两步操作:1、(CX) <-- (CX) - 1 2、判断CX中的值,不为零则转到标号出执行程序,如果为零向下执行。从上面的描述中,我们可以看到,CX中的值影响着LOOP指令的执行结果。通常,我们用LOOP指令来实现循环功能,CX存放循环次数 1、标号 在汇编语言中,标号代表一个地址。用CX和LOOP指令相配合实现循环功能的三个要点:1、在CX中存放循环次数 2、LOOP指令总的标号所在标识地址要在前面 3、要循环执行的程序段,要写在标号的LOOP指令的中间 框架如下:MOV CX, 循环次数 循环执行的程序段 LOOP 标号

5.3 在debug中跟踪用LOOP指令实现循环程序

debug G 也可以是 P

5.4 debug和汇编编译器MASM对指令的不同处理

5.5 LOOP和[BX]的联合应用

表示内存单元偏移地址的X应该是一个变量,因为在循环的过程总,偏移地址必须能够递增。这样,在指令中,我们就不能用常量来表示偏移地址,我们可以将偏移地址放入BX中,用[BX]的方式来访问内存单元。在实际编程中,经常会遇到,用同一种方法处理地址连续的内存单元中的数的问题。我们需要用循环来解决这类问题,同时我们必须要能够在每次循环的时候按照同一种方法来改变要访问的内存单元的地址。这是,我们就不能用常量来给出内存单元的地址,而应用变量。

5.6 段前缀

指令MOV AX, [BX] 中,内存单元的偏移地址由BX给出,而段地址默认在DS中。我们可以在访问内存单元的指令中显式地给出内存单元的段地址所在的段寄存器。这些出现在访问内存单元的指令中,用于显式地指明内存单元中的段地址 "DS:" "CS:" "ES:" "SS:" 在汇编语言中称为段前缀。

5.7 一段安全的空间

在8086模式中,随意向一段内存空间写入内容是很危险的,因为这段空间中可能存放着重要的系统数据或代码。我们似乎面临一种选择,是在操作系统中安全,规矩地编程,还是自由、直接地用汇编语言取操作真实的硬件,了解哪些早就被层层系统软件所掩盖的真相。注意:我们可以在DOS下,可以不理会DOS,直接用汇编语言去操作真实的硬件,因为运行的CPU实模式下的DOS,没有能力对硬件系统进行全面的、严格的管理。在一般的PC机中,DOS方式下,DOS和其他合法的程序一般都不会使用0:200 - 0:2ff的256个字节的内存空间,所以我们使用这段空间是安全的。

5,8 段前缀的使用

第六章 包含多个段的程序

6.1 在代码段中使用数据

解释一下,“DW” 的含义是定义字型数据 define word,可以说它定义数据也可以说它开辟内存空间。在这里,我们使用DW定义了8个字型数据(数据之间逗号分隔),它们的内存空间大小为16个字节 由于它们在代码段中,程序在运行的时候CS中存放代码段的段地址,所以我们可以从CS中得到它们的段地址。因为DW定义的数据处于代码段的最开始,所以偏移地址为0,这8个数据在代码段的偏移 0 2 4 6 8 A C E 如何让这个程序在编译后可以在系统中直接运行呢?我们可以在源程序中指明程序的入口所在 我们在程序的第一个指令的前面加上了一个标号start,二这个标号在伪指令end的后面出现 end除了通知编译器程序结束外,还可以通知编译器程序的入口在什么地方

6.2 在代码段中使用栈

程序运行时,定义的数据存放在CS:0000 - CS:000F中,共8个字单元。依次将这8个字单元中的数据入栈,然后在依次出栈到这8个字单元中,从而实现数据的逆序存放。问题是,我们首先要有一段可当做栈的内存空间。如前所述,这段空间应该有系统来分配。我们可以在程序中通过定义数据来取得一段空间,然后将这段空间当做栈空间来用。

6.3 将数据、代码、栈放入不同的段

在前面的内容中,我们在程序中用到了数据和栈,我们将数据、栈和代码都放到了一个段里面。我们在编程的时候要注意何处是数据、何处是栈,何处是代码。这样做显然有两个问题:1、把它们放到一个段中使程序显得混乱 2、前面程序中处理的数据很少,用到的占空间小,加上没有多长的代码,放到一个段里面没有问题。但如果数据、栈和代码需要的空间超过64KB,就不能放在一个段中。我们用定义代码段一样的方法来定义多个段,然后在这些段里面定义需要的数据,或通过定义数据来取得栈空间。程序中对段名的引用,将被编译器处理为一个表示段地址的数值。伪指令 ASSUME ASSUME CS:CODE, DS:DATA, SS:STACK 将CS,DS和SS分贝和CODE, DATA, STACK相连。这样做了之后,CPU 我们在源程序的最后用“END START”说明了程序的入口,这个入口将被写入可执行文件 的描述信息,可执行文件中的程序加载入内存后,CPU的CS:IP被设置执行这个入口,从而开始执行程序中的第一条指令。标号“START”在“CODE”段中,这样CPU就将CODE段中的内容当做指令来执行了。总之,CPU到底如何处理我们定义的段中的内容,是当指令执行,当做数据访问,还是当做栈空间,完全靠程序中具体的汇编指令,和汇编指令对CS:IP/SS:SP/DS等寄存器的设置来决定的。

第七章 更灵活定位内存地址

7.1 and 和 or 命令

AND 指令:逻辑与指令,按位进行与运算。通过该指令可将操作对象的相应为设为0,其他位不变。OR 指令:逻辑或指令,按位进行或运算。通过该指令可将操作对象的相应为设为1,其他位不变。

7.2 关于 ASCII码

世界上有很多编码方案,有种方案叫做ASCII码,是计算机系统中常用的编码。简单第说,所谓编码方案,就是一套规则,它规定了用什么样的信息来表示现实的对象 一个文本编辑过程中,就包含着按照ASCII编码规则进行的编码和解码 在文本编程过程中,我们按下键盘a键,就会在屏幕上看到‘a’。

7.3 以字符形式给出的数据

DB ‘unix’

7.4 大小写转换的问题

一个字母,我们不管它原来是大写还是小写 我们将它的第5位置0,它就必将变为大写之母 我们将它的第5位置1,它就必将变为小写之母

7.5 [BX + IDATA]

[BX + IDATA]表示一个内存单元,它的偏移地址为(BX)+IDATA MOV AX, [BX + IDATA] MOV AX, [IDATA + BX] MOV AX, IDATA[BX] MOV AX, [BX].IDATA

7.6 用[BX + IDATA]的方式进行数组的处理

有了[BX + IDATA]这种表示内存单元的方式,我们就可以用更高级的结构来看待所要处理的数据。我们可以将这两个字符串看做两个数组,一个从0地址开始存放,另一从5地址开始存放。那么我们可以用[0+BX]和[5+BX]的方式在同一个循环中定位这两个字符串的字符。在这里,0和5给定了两个字符串的起始偏移地址,BX中给出了从起始偏移地址开始的相对地址。这两个字符串在内存中的起始地址是不一样的,但是,它们中的每一个字符,从起始地址开始的相对地址的变化是相同的。C语言定位方式 A[i], B[i] 汇编定位方式 0[BX], 5[BX] [BX + IDATA]的方式为功绩语言实现数组提供了便利机制。

7.7 SI 和 DI

SI和DI是8086CPU中和BX功能相近的寄存器,但是SI和DI不能够分为两个8位寄存器来使用。MOV AX, [BX] MOV AX, [SI] MOV AX, [DI] MOV AX, [BX+123] MOV AX, [SI+123] MOV AX, [DI+123]

7.8 [BX + SI]和[BX + DI]

我们来卡一下指令 MOV AX, [BX + SI]的含义:将一个内存单元的内容送入AX,这个内存单元的长度为2字节(字单元),存放一个字,偏移地址为((BX)+(SI)),段地址在DS中 也可以写成 MOV AX, [BX][SI]

7.9 [BX + SI + IDATA]和[BX + DI + IDATA]

MOV AX, [BX + SI + IDATA] MOV AX, [BX + IDATA + SI] MOV AX, IDATA[BX][SI] MOV AX, [BX].IDATA[SI] MOV AX, [BX][SI].IDATA

7.10不同的寻址方式的灵活运用

(1)[IDATA]用一个常量来表示地址,可用于直接定位一个内存单元 (2)[BX]用一个变量来表示地址,可用于间接定位一个内存单元 (3)[BX + IDATA]用一个变量和常量来表示地址,可在一个起始地址的基础上用变量间接点位一个内存单元 (4)[BX + SI]用两个变量表示地址 (5)[BX + SI + IDATA]用两个变量和一个常量来表示地址 这使我们可以从更加 结构化 的角度来看待所要处理的数据 将>

第八章 数据处理的两个基本问题

8.1 BX, SI, DI, BP

1、处理的数据在什么地方 2、处理的数据有多长 这两个问题,在机器指令中必须给以明确或隐含的说明,否则计算机就无法工作 1、在8086CPU中,只有4个寄存器(BX, BP, SI, DI)可以用在"[]"中来进行内存单元寻址 2、在“[]”中这四个寄存器(BX, BP, SI, DI)可以单个存现,或只能以四种组合出现:BX(SI, DI) BP(SI, DI) 3、只要在"[]"中使用寄存器BP,而指令中没有显示的给出段地址,段地址就默认在SS中

8.2 机器指令的处理的数据所在位置

绝大多数机器指令都是进行数据处理的指令,处理大致分为三类:读取、写入、运算 在机器指令这一层来讲,并不关心数据的值是多少,而关心指令执行前一刻它将要处理的数据所在的位置 指令在执行前,所要处理的数据可以在三个地方 CPU内部,内存、端口

8.3 汇编语言中数据位置的表达

1、立即数(IDATA) 对于直接包含在机器指令中的数据(执行前在CPU的指令缓冲器中),在汇编语言中称为立即数(IDATA),在汇编指令中直接给出。MOV AX, 0001H ==》B80100 2、寄存器 指令要处理的数据在寄存器中,在汇编指令中给出相应的寄存器名。MOV AX, BX ==》89DB 3、段地址(SA)和偏移地址(EA) 指令要处理的数据在内存中,在汇编指令中可用[BX]的格式给出EA,SA在某个段寄存器中。存放段地址的寄存器可以是默认的,也可以通过添加段超越前缀,显示的给出

8.4 寻址方式

当数据存放在内存中的时候,我们可以用多种方式来给定这个内存单元的偏移地址,这种定位内存单元的方法一般被称为寻址方式 直接寻址 寄存器间接寻址 寄存器相对寻址 基址变址寻址 基址变址且相对寻址

8.5 指令要处理的数据有多长

8086CPU的指令,可以处理两种尺寸的数据,BYTE和WORD。所以在机器指令需要指明,指令进行的是字操作还是字节操作 1、通过寄存器指明要处理的数据的尺寸 2、在没有寄存器名存在的情况系,用操作符PTR指明内存单元的长度,BYTE PTR WORD PTR 3、其他方法 有些指令默认了访问的是字单元还是字节单元 PUSH POP

8.6 寻址方式的综合应用

一个结构化的数据包含了多个数据项,而数据项的类型又不相同,有的是字形数据,有的是字节型数据,有的是数组 一般来说,我们可以用[BX + IDATA + SI]的方式来访问结构体中的数据。※※※ 用BX来定位整个结构体,用IDATA定位结构体中的某一个数据项,用DI定位数据项中的每一个元素 为此,汇编语言提供了更为贴切的书写方式 IDATA[BX][BX].IDATA[BX].IDATA[SI] 在C语言中我们看到,如:dec.cp[i] dec是一个变量名,指明了结构体变量的地址 cp是一个名称,指明了数据项cp的地址 i用来定位cp中的每一个元素 汇编语言中的做法是:BX.10H[SI]

8.7 div 指令

div(division)指令,除法指令 除数: 8位或16位,在寄存器或内存单元中 被除数:(默认)放在AX 或 DX和AX 中 结果8位 16位 商 AL AX 余数 AH DX 指令格式:div reg div 内存单元 DIV BYTE PTR DS:[0] DIV WORD PTR DS:[0] 利用除法指令计算 100001 / 100 利用除法指令计算 1001 / 100

8.8 伪指令dd

DD 用来定义define double word型数据

8.9 dup

dup是一个操作符,在汇编语言中通DB, DW, DD一样,也是有编译器识别处理的符号 它是和DB, DW, DD等数据定义伪指令配合使用的,用来进行数据的重复. DB 3 DUP(0); 定义了三个字节,它们的值都是0 DB 3 DUP(0, 1, 2); 定义了9个字节,相当于 0, 1, 2, 0, 1, 2, 0, 1, 2 格式:DB 重复次数 DUP (重复的字节型数据) DW 重复次数 DUP (重复的字型数据) DD 重复次数 DUP (重复的双字型数据)

第九章 转移指令的原理

可以修改IP,或通识课修改CS和IP的指令统称为转移指令。概括地讲,转移指令就是可以控制CPU执行内存中某处代码的指令。8086CPU的转移指令分为以下几类:1、无条件转移指令 jmp2、条件转移指令3、循环指令 LOOP4、过程5、中断

9.1 操作符 OFFSET

操作符OFFSET在汇编语言中是由编译器处理的符号,它的功能是取得标号的偏移地址。

9.2 jmp指令

jmp为无条件转移,可以只修改IP,也可以同时修改CS:IP jmp指令要给出两种信息:转移的目的地址 转移的距离(段间转移,段内短转移,段内近转移) JMP SHORT 标号(转到标号出执行指令) 这种格式的jmp指令实现的是段内短转移,它对IP的修改范围为 -128,127,也就是说,它相前转移时可以最多不超过128字节,向后转移可以最多不超过217字节

9.3 依据位移进行转移的jmp指令

那么,CPU根据什么进行转移呢?没有了目的地址,CPU如何知道转移到哪里呢?这说明在机器指令中并包含转移的目的地址。如果机器指令中不包含目的地址的话,那么,也就是说,CPU不需要这个目的地址就可以实现对IP的修改。JMP SHORT S; 指令的执行过程 (1)CS:IP 指向 EB 03(JMP SHROT S) (2)读取指令码 EB 03进入指令缓冲器 (3)(IP) = (IP) + 所读取指令的长度 (4)CPU指令缓冲器中的指令 EB 03 (5)指令EB 03执行后, short指明此处的位移为8位位移 JMP NEAR PTR 标号; 段内近转移 16位 -32768, 32767

9.4 转移的目的地址在指令中的jmp指令

前面讲的jmp指令,其对应的机器码中并没有转移的目的地址,而是相对于当前IP的转移位移 JMP FAR PRT 标号; 段间转移,又称为远转移。(CS) = 标号所在段的段地址 (ip) = 标号所在段的偏移地址 FAR PTR 指明了指令用标号的段地址和偏移地址修改CS和IP。机器码为:EA 0B 01 BD 0B,其中包含转移的目的地址

9.5 转移地址在寄存器中的jmp指令

指令格式:jmp 16为寄存器 功能:IP = (16位寄存器)

9.6 转移地址在内存中的jmp指令

1、JMP WORD PTR 内存单元地址(段内转移) 功能:从内存单元地址出开始存放着一个字,是转移的目的偏移地址 内存单元地址可用寻址方式的任一格式给出。1、JMP DWORD PTR 内存单元地址(段间转移) 功能:从内存单元地址出开始存放着两个字,高地址单元的字是转移的目的段地址,低地址单元是转移的目的偏移地址 (CS) = (内存单元地址 + 2) (IP) = (内存单元)

9.7 JCXZ指令

JCXZ 指令为有条件转移指令,所有的有条件转移指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址。对IP的修改范围 -128,127 指令格式:JCXZ 标号 如果CX=0,则跳转到标号处执行 当CX!=0,继续往下执行 相当于 if((CX) == 0) JMP SHORT 标号;

9.8 LOOP指令

Loop指令为循环指令,所有的循环指令都是短转移,在对应的机器码中包含转移的位移,而不是目的地址 转移范围:-128,127 指令格式:LOOP 标号 (CX) = (CX)-1,果果(CX)!= 0,转移到标号处执行。当CX=0,继续往下执行 相当于 (CX)-- if((CX) != 0)j jmp short 标号;

9.9 根据位移进行转移的意义

JMP SHROT 标号; JMP NEAR PTR 标号; JMP 标号; JCXZ 标号; LOOP 标号; 这几种汇编指,它们对IP的修改是更具转移目的地址和转移起始地址之间的唯一来进行的。在它们对应的机器码中不包含转移目的地址,而包含额是目的地址的位移距离。这样设计,方便了程序段在内存中的浮动装配。注意:根据位移进行转移的指令,它们的转移范围受到转移位移的限制,如果在源程序中出现了转移范围越界的问题,在编译的时候,编译器将报错。welcome to masm!

9.10编译器对转移位移越界的检测

第十章 call和ret指令

call 和 ret指令都是转移指令,它们都修改IP,或同时修改CS:IP它们经常被共同用来实现程序的设计

10.1 ret和retf

ret指令用栈中的数据,修改IP的内容,从而实现近转移 1、(IP) = (SS)*16 + (SP) 2、(SP) = (SP) + 2 retf指令用栈中的数据,修改CS和IP的内容,从而实现远转移:1、(IP) = (SS)*16 + (SP) 2、(SP) = (SP) + 2 3、(CS) = (SS)*16 + (SP) 4、(SP) = (SP) + 2 可以看出,如果我们用汇编语法来解释 ret 和 retf指令,则:RET: POP IP RETF: POP IP POP CS

10.2 call指令

CALL指令经常跟ret指令配合使用,因此CPU执行call指令,进行两步操作 1、将当前的IP或(CS和IP)压如栈中 2、转移(JMP) call指令不能实现短转移,除此之外,call指令实现转移的方式与JMP指令的原理相同 CALL 标号(将当前的IP压栈后,转移到标号处执行指令)16位位移由编译器在编译过程中算出

10.3 依据位移进行转移的call指令

10.4 转移的目的地址在指令中的call指令

前面讲解的call指令,其对应的指令中并没有转移的目的地址,而是相对于当前IP的转移位移 指令 call far ptr 标号“实现的是段间转移” 1、 (SP) = (SP) - 2 ((SS)*16 + SP) = (CS) (SP) = (SP) - 2 ((SS)*16 + SP) = (IP) CPU执行指令“CALL FAR PTR 标号”时,相当于:PUSH CS PUSH IP JMP FAR PTR 标号

10.5 转移地址在寄存器中的call指令

指令格式:CALL 16位寄存器 功能:(SP) = (SP) - 2 ((SS)*16 + SP) = IP (IP) = (16位寄存器) CPU执行指令“CALL 16位寄存器”时,相当于:PUSH IP JMP 16为寄存器

10.6 转移地址在内存中的call指令

1、CALL WORD PTR 内存单元地址 汇编语法解释 PUSH IP JMP WORD PTR 内存单元地址 2、CALL DWORD PTR 内存单元地址 汇编语法解释 PUSH CS PUSH IP JMP DWORD PTR 内存单元地址

10.7 call和ret指令的配合使用

可是执行完子程序,如何让CPU接着call指令向下执行?没错,答案就是RET CALL 指令后面的指令的地址将存储在栈中,所以可以在子程序的后面使用ret指令,用栈中的数据设置IP的值,从而转导call指令后面的代码出继续执行。子程序的框架

10.8 MUL指令

因为下面要用到,我们介绍一下MUL指令,MUL是乘法指令,使用MUL做乘法指令的时候:1、相乘的两个数:要么都是8位,要么都是16位 8位:AL中和8寄存器或内存字节单元中 16位:AX中和16位寄存器或内存字单元中 2、结果 8位:AX中 16位:DX(高位) AX(低位) 格式:MUL REG MUL 内存单元 MUL BYTE PTR 内存单元、 MUL WORD PTR 内存单元 例如:1、计算 100 x 100 MOV AL, 100 MOV BL, 100 MUL BL 2、计算 100 x 10000 MOV AX, 10000 MOV BX, 100 MUL BX

10.9 模块化程序设计

从上面我么看到,call与ret指令共同支持汇编语言编程中的模块化设计。在实际编程中,程序的模块化是必不可少的。因为现实的问题比较复杂,对现实问题进行分析时,把它转化成为相互联系、不同层次的子问题,是必须的解决方法。我们可以用简洁的方法,实现多个互相联系、功能独立的子程序来解决一个复杂的问题。子程序一般都要根据提供的参数处理一定的事务,处理后,将结果(返回值)提供给调用者 其实,我们讨论参数和返回值传递的问题,实际上就是在探讨,应该如何存储子程序需要的参数和产生的返回值。我们设计一个子程序,可以根据提供的N,来计算N的3次方 这里有两个问题:1、我们将参数N存储在什么地方?2、计算得到的数值,我们存储在什么地方?注意:对于程序应有详细的注释。子程序的注释信息应该包含对子程序的功能、参数和结果的说明。用寄存器来存储参数和结果是最常使用的方法。对于存放参数的寄存器和存放结果的寄存器,调用者和子程序的读写操作恰恰相反:1、调用者将参数送入参数寄存器,从结果寄存器取到返回值 2、子程序从参数寄存器中取得参数,将返回值送入结果寄存器

10.10参数和结果传递问题

10.11批量数据的传递

前面的历程中,子程序cube只有一个参数,放在BX中。如果有两个参数,那么可以用两个寄存器来存放,可是如果需要传递的参数有3个4个或更多值是N个,我们怎样存放呢?寄存器的数量终究有限,我们不可能简单地用寄存器来存放多个需要传递的数据,对于返回值,也有同样的问题。在这种时候,我们将批量数据放到内存中,然后将他们所在内存空间的首地址放在寄存器中,传递给需要的子程序。对于具有批量数据的返回结果,也可用同样的方法 注意:除了寄存器,内存传递参数外,还有一种通用的方法使用栈来传递参数。

10.12寄存器冲突的问题

设计一个子程序:功能:讲一个全是字母,以0结尾的字符串,转化为大写。程序要处理的字符串以0作为结尾符,这个字符串可以如下定义:DB "conversation", 0

第十一章 标志寄存器

8086CPU的标志寄存器16位,其中存储的信息通畅被称为程序字寄存器(PSW)我们已经使用过了8086CPU的AX, BX, CX, DX, SI, DI, SP, BP, IP, CS, DS, ES, SS等13个寄存器FLAG和其他寄存器不一样,其他寄存器用来存放数据的,都是整个寄存器具有一个含义。flag寄存器是按位起作用的,也就是说,它的每一位都有专门的含义,记录特定的信息。8086CPU的FLAG的寄存器的结构

11.1 ZF标志

FLAG的第6位是ZF,零标志位。它记录相关指令执行后,结果为0,ZF=1, 结果不为0,ZF=0 注意:在8086CPU中指令集中,有的指令的执行是影响标志寄存器的,比如:ADD, SUB, MUL, DIV, INC, OR, AND等 它们大都是运算指令(进行逻辑或算术运算) 有的指令的执行对标志寄存器没有影响,比如:MOV, PUSH, POP等 它们大都是传送指令 我们在使用一条指令的时候,要注意这条指令的全部功能,其中包括,执行结束对标志寄存器的哪些标志位造成了影响。

11.2 PF标志

FLAG的第2位是PF,奇偶标志位。它记录指令执行后,结果的所有二进制位中1的个数:为偶数, PF=1 为奇数, PF=0

11.3 SF标志

FLAG的第7位是SF,符号标志位。它记录指令执行后,结果为负,SF=1,结果为正,SF=0 我们知道计算机中通常用补码来表示有符号数。计算机中的一个数据可以看做是有符号数,也可以看成无符号数。0000 0001B 无符号数 1有符号数 +1 1000 0001B 无符号数 129 有符号数 -127 也就是说,对于同一个二进制数据,计算机可以将它当做无符号数来运算,也可以当做有符号数来运算。在我们将数据当做有符号数来运算的时候,可以通过它来得知结果的正负 如果我们将数据当做无符号数来运算,SF的值则没有意义,虽然相关指令影响了它的值。也就是说,CPU在执行add等指令是,是必然要影响到SF标志位的值。至于我们需不需要这种影响,那就看我们如何看待指令所进行的运算了。某些指令将影响标志寄存器中的过个标志位,这些被影响的标记为比较全面地记录了指令的执行结果,为相关的处理提供了所需的依据。比如指令SUB AL, AL执行后,ZF,PF,SF等寄存器都受到影响,它们分别为:1, 1, 0

11.4 CF标志

FLAG的第0位是CF,进位标志位。一般情况系,在进行无符号数运算的时候,它记录了运算结果的最高有效位向更高位的进位值,或从更高位的借位值。对于位数为N的无符号数来说,其对应的二进制信息的最高位,即第N-1位,的最高有效位,二假象存在的第N位,就是相对于最高有效位的更高位。对于位数为N的有符号数来说,其对应的二进制信息的最高位,即第N-2位,的最高有效位,二假象存在的第N-1位,就是相对于最高有效位的更高位。

11.5 OF标志

在进行有符号数运算的时候,如果结果超过了机器所能表示的范围称为溢出。那么什么是机器所能表示的范围呢?8位 -128, 127 如果我们用add指令进行的是有符号数运算,则 98+99=-59这样的结果让人无法接受 造成这种情况的原因,就是实际的结果197,作为一个有符号数,在8位寄存器AL中存放不下 由于在进行有符号数运算时,可能发生溢出而造成结果的错误。所以CPU需要对执行执行后是否产生溢出进行记录。因此有了OF 一定要注意CF和OF的区别:CF是对无符号数运算有意义的标志位 Of是对有符号数运算有意义的标志位 由于无符号数运算,CPU用CF为来记录是否产生了进位。由于有符号数运算,CPU用OF为来记录是否产生了溢出。当然,还要用SF为来记录结果的符号。对于有无符号,计算机是分不清楚状况的,因此它必须两种都记载着,要怎么用,看的是你当他是什么!

11.6 ADC指令

ADC是带进位加法指令,它利用了CF为上记录的进位值。格式:ADC OBJ1, OBJ2 功能:操作对象1 = 操作对象1 + 操作对象2 + CF 在执行ADC指令的时候加上的CF的值得含义,由ADC指令前面的指令决定的,也就是说,关键在于所加上的CF值是被什么指令设置的。显然,如果CF的只是SUB指令设置的,那么它的含义就是借位值 如果是被ADD指令设置的,那么它的含义就是进位值。可以看出, 加法可以分为两步进行:1、低位相加 2、高位相加在加上低位相加产生的进位值 下面的指令和 ADD AX, BX; 具有相同的结果 ADD AL, BL ADC AH, BH 看来CPU提供ADC指令的目的,就是来进行加法的第二步运算的。ADC指令和ADD指令相配合就可以对更大的数据进行加法运算 编程计算 1EF000H+00H 结果放在AX(高16位), BX(低16位) ADC指令执行后,也可能产生进位值,所以也会对CF位进行设置 由于有这样的功能,我们就可以对任意大的数据进行加法运算 例如,编程计算 1EF0001000H + 001EF0H,结果放在AX(最高位16位), BX(次高位16位), CX(最低位16位) MOV AX, 001EH MOV BX, 0F000H MOV CX, 1000H ADD CX, 1EF0H ADC BX, 1000H ADC AX, 0020H 下面我们,编写一个子程序,对两个128位数据进行相加 名称: add128 功能:两个128位数据进行相加 参数:DS:SI指向存储第一个数的内存空间,因数据为128位,所以需要8个字单元,由低地址单元到到高地址单元一次存放128位数据由低到高的各个数字。运算结果存储在第一个数的存储空间中 INC LOOP 指令不影响CF值

11.7 SBB指令

SBB是带借位减法指令,它利用了CF位上记录的错位值。格式:SBB OBJ1, OBJ2 功能:操作对象1 = 操作对象1 - 操作对象2 - CF 利用SBB指令我们可以对任意大的数据进行减法运算 计算 003E1000H - 0000H,结果放在AX, BX中,程序如下 MOV AX, 003EH MOV BX, 1000H SUB BX, 2000H SBB AX, 0020H 通过学习这两条指令,我们可以进一步领会一下标志寄存器CF为的作用和意义。

11.8 CMP指令

CMP是比较指令,功能相当于减法指令,只是不保存结果。CMP指令执行后,将对标志寄存器产生影响。格式:CMP OBJ1, OBJ2 功能:计算操作对象1 - 操作对象2但并不保存结果,仅仅根据计算结果对标志寄存器进行设置。比如:CMP AX, AX ZF = 1, PF = 1, SF = 0, CF = 0, OF = 0 其实,我们通过CMP指令执行后,相关标志位的值就可以看出比较的结果。我们在考察SF的同时考察OF,就可以得知结果的正负 SF = 1, OF = 0, RET = - SF = 1, OF = 1, RET = + SF = 0, OF = 0, RET = + SF = 0, OF = 1, RET = -

11.9 检测比较结果的条件转移指令

这些条件转移指令通常都和CMP相配合使用,就好像CALL和RET指令通常相配合使用一样。因为CMP指令可以同时进行两种比较,无符号数和有符号数比较 根据CMP指令的比较结果进行转移的指令也分为两种: 根据无符号数的比较结果进行转移的条件转移指令,它们检测ZF、CF的值 根据有符号数的比较结果进行转移的条件转移指令,它们检测SF、CF, OF 的值 对于无符号数的比较 JE JNE JA JNA JB JNB 编程实现如下功能:如果(AH) = (BH),则(AH) = (AH) + (BH),否则(AH) = (AH) + (AH)CMP AH, BH JE ADDBH ADDAH: ADD AH, AH JMP OK ADDBH: ADD AH, BH OK: MOV AX, 4C00H INT 21H

11.10 DF标志和串传送指令

FLAG的第10位是DF,方向标志位。在串处理指令中,控制每次操作后SI, DI的增减 DF = 0; 每次操作后 SI, DI递增 DF = 1; 每次操作后 SI, DI递减 格式1: MOVSB 功能:(以字节为单位传送) 1、((ES)X16 + DI) = ((DS)X16 + SI) 2、 DF = 0; 每次操作后 SI, DI递增 DF = 1; 每次操作后 SI, DI递减 MOVSB的功能是将DS:SI指向的内存单元中的字节送入ES:DI中,然后更具标志寄存器DF位的值,将SI和DI自动递增和递减 格式2:MOVSW 功能:(以字为单位传送) 1、((ES)X16 + DI) = ((DS)X16 + SI) 2、 DF = 0; 每次操作后 SI, DI递增 DF = 1; 每次操作后 SI, DI递减 MOVSB MOVSW进行的是串传送指令中的一个步骤,一般来说 MOVSB MOVSW都和REP配合使用,格式如下:REP MOVSB REP的作用是根据CX的值,重复执行后面的串传送指令 由于每次执行MOVSB指令SI,DI都会递增或递减指向后一个单元或前一个单元 REP MOVSB就可以循环实现(CX)个字符的传送。由于FLAG的DF位决定着串传送指令执行后,SI,DI改变的方向。所以CPU应该提供相应的指令来对DF位进行设置,从而使我们能够决定传送的方向。8086CPU提供西面两条执行对DF位进行设置:CLD; DF = 0 STD; DF = 1

11.11 PUSHF和POPF

PUSHF: 将标志寄存器的值压栈 POPF : 将栈中的数据,送入标志寄存器 PUSH POPF,为直接范文标志寄存器提供了一种方法。

11.12 标志寄存器在debug中的显示

标志 值为1的标记 值为0的标记 OF OVNV SF NGPL ZF ZRNZ PF PEPO CF CYNC DF DNUP

第十二章 中断

中断是CPU处理外部突发事件的一个重要技术它能使CPU在运行过程中对外部事件发出的中断请求及时地进行处理,处理完成后有立即返回断点,继续进行CPU原来的工作。引起中断的原因或者说发出中断请求的来源叫做中断源。根据中断源的不同,可以把中断分为硬件中断和软件中断两大类,而硬件中断又可以分为外部中断和内部中断两类外部中断一般是由计算机外设发出的中断请求,如:键盘中断,打印机中断、定时器中断等。外部中断是可以屏蔽的中断,也就是说,利用中断控制器可以屏蔽这些外部设备的中断请求。内部中断是指因硬件出错(如突然掉电,奇偶检验错等)或运算出错(除数为0,运算溢出、单步中断等)所引起的中断。内部中断是不可屏蔽的中断。软件中断其实并不是真正的中断,它们只是可被调用执行的一般程序以及DOS的系统功能调用(INT 21H)等都是软件中断。CPU为了处理并发的中断请求,规定了中断的优先级,中断优先级由高到低的顺序是:1、除法错误 、溢出中断 、软件中断2、不可屏蔽中断3、可屏蔽中断4、单步中断

12.1 内中断的产生

12.2 中断处理程序

CPU的设计者必须在中断信息和其处理程序的入口地址之间建立某种联系,使得CPU根据中断信息可以找到要执行的处理程序 我们知道中断信息中包含有标识中断源的类型码。根据CPU的设计,中断类型码的作用就是用来定位中断处理程序。比如CPU根据中断类型码4,就可以找到4号中断的处理程序。可随之而来的问题是,若要定位中断处理程序,需要知道他的段地址和偏移地址,而如何根据8位的中断类型码(8086中断类型码为一个字节)的得到中断处理程序的段地址和偏移地址呢?这就要映入中断“中断向量表”了。

12.3 中断向量表

CPU用8位的中断类型码通过中断向量表找到相应的中断处理程序的入口地址。那么什么是中断向量表呢?中断向量表就是中断向量的列表。中断向量表在内存中保存,其中存放着256个中断源所对应的中断处理程序的入口。中断向量表在内存中存放,对于8086PC机,中断向量表指定存放在内存地址0处。从内存0000:0000到0000:03FF的1024个单元中存放着中断向量表。

12.4 中断过程

从上面的讲解中,我们知道,可以用中断类型码,在中断向量表中找到中断处理程序的入口。找到这个入口地址的最终目的是用他设置CS:IP ,使CPU执行中断处理程序。用中断类型码知道中断向量,并用他设置CS:IP 这个工作是CPU的硬件自动完成的。CPU硬件完成这个工作的过程被称为中断过程。8086CPU的中断过程:1、(从中断信息中)取得中断类型码 N 2、标志寄存器的值入职(保护标志位) PUSHF 3、设置标志寄存器的第8位TF和第9位IF的值为0TF=0, IF=0 4、CS的内容入栈 PUSH CS 5、IP的内容入栈 PUSH IP 6、从内从地址为 中断类型码*4 和 中断类型码*4+2 的两个字单元总读取中断程序的入口地址设置CS:IP (IP)=4N (CS)=4N+2 在最后一步完成后,CPU开始执行有程序员编写的中断处理程序

12.5 中断处理程序

常规步骤:1、保存用到的寄存器 2、处理终端 3、恢复用到的寄存器 4、用IRET指令返回 IRET指令的功能用汇编语法描述为:POP IP POP CS POPF IRET通常和硬件自动完成的中断过程配合使用。可以看到,在中断过程中,寄存器入栈的顺序是标志寄存器、CS、IP,而IRET的出栈顺序是IP、CS、标志寄存器,刚好和其对弈,实现了用执行中断处理程序前的CPU线现场恢复标志寄存器、CS、IP的工作。

12.6 除法错误中断的处理

下面的内容中,我们通过对0号中断,即除法错误的中断处理,来体会一下前面说所讲的内容。当CPU执行div等除法指令的时候,如果发生了除法溢出错误,将产生中断类型码为0的中断信息,CPU将检测到这个信息,然后引发中断过程,转去执行0号中断所对应的中断处理程序。

12.7 编程处理 0 号中断

现在我们考虑改变一下0号中断处理程序的功能,即重新编写一个0号中断处理程序,它的功能是在屏幕中间显示“Welcome to masm!”的广告语,然后返回操作系统。当发生除法溢出的时候,产生0号中断信息,从而引发中断。此时,CPU将进行一下工作:1、取得中断类型码0 2、PSW寄存器入栈,TF/IF设置为0 3、CS、IP入栈 4、(ip) = 4*0, (CS)=4*0+2 现在的问题是:do0应该放在内存中。因为除法溢出随时可能发生,CPU随时都可能将CS:IP执行do0的入口,执行程序。那么do0应该放在哪里呢?我们要想得到一块内存空间存放do0, 由于我们是在操作系统之上使用计算机,所以所有的硬件资源都在操作系统的管理之下,所以应该向操作系统申请。但在这里处于两个原因我们不想这样做:1、过多地讨论申请内存够将偏离问题主线 2、我们学习汇编的一个重要目的就是要获得对计算机底层的编程体验。所以,在可能的情况下,我们不去理会操作系统,而直接面向硬件资源。问题变得简单直接,我们只需找到一块别的程序不会用到的内存取,将do0传送到其中即可。前面讲到,内存0000:0000 - 0000:03FF,大小为1kb的空间是操作系统存放中断处理程序入口地址的中断向量表。一般情况下,从0000:0200 - 0000:02FF的256个字节的空间所对应的总孤单向量表都是空的,操作系统和其他应用程序都不占用。我们将中断处理程序do0放到0000:0200后,若要使得除法溢出发生的时候,CPU转去执行do0的入口地址。即0000:0200登记在中断向量表的对应表项中。因为除法溢出对应的中断类型码为0,它的中断处理程序的入口地址应该在0x4地址单元开始存放,段地址为0x4+2字单元存放,偏移地址存放在0x4字单元中。1、编写do0 2、将do0送入0000:0200 - 0000:02FF 3、将do0的入口地址存放在向量表 0000:0000 - 0000:0004 程序执行时,do0的代码是不执行的,它只是做为do0安装程序所要传送的数据 执行do0安装程序,将do0的代码拷贝到内存0000:0200之后,设置中断向量表,即0000:0000 - 0000:0004,保存在0号表象中,这两部分工完成后程序就返回了。注意:‘Welcome to masm!’在程序的data段中。程序执行王成后返回。它所占用的内存孔家被系统释放,而其中存放的‘Welcome to masm!’也将很有可能被别的信息覆盖。而do0程序被放到了0000:0200处,随时有会因发生了除法溢出而被CPU执行,很难保证do0程序从原来的程序所处的空间中取得要显示的字符串‘Welcome to masm!’ 因为do0程序随时可能被执行,而它要用到字符串“Welcome to masm!”,所以该字符串也应该存放在一段不会被覆盖的空间中。在代码段中存放数据

12.8 安装

12.9 do0

12.10 设置中断向量

12.11 单步中断

什么是单步中断?CPU为什么要提供这样的功能呢?我们使用debug的T命令的时候,有没有想过这样的问题,debug如何能让CPU在执行一条指令后,就显示各个寄存器的状态?想想:如果CPU不提供其他功能的或,就按正常的方式工作,只要CPU一加电,它就从预设的地址开始一直执行下去。。。不可控制 可是,我们在debug中看到的情况都是,debug可以控制CPU执行加载程序的一条指令,然后让它停下来,显示寄存器的状态。CPU在执行完一条指令之后,如果检测到标志寄存器的TF位为1,则产生单步中断,引发中断过程。单步中断的中断类型码为1,它引发的中断过程如下:1、取得中断类型码 1 2、PWD TF=0 IF=0 3、PUSH CS PUSH IP 4、IP = 4X1 CS = 4X1+2 debug提供了单步中断的中断处理程序,功能为显示所有的寄存器中的内容后等待输入命令 在使用T命令执行命令时,debug将TF设置为1, 在执行完这条指令后引发CPU单步中断,执行单步中断处理程序,所有寄存器的值显示在屏幕上,并且等待输入命令。CPU提供单步中断功能的原因就是,为了单步跟踪的执行过程,提供了实现机制。

12.12 响应中断的特殊情况

可是,在有些情况下,CPU在执行完成当前指令后,即使是发生了中断,也不会相应。例如:在执行完向SS寄存器传送数据的指令后,及时检测到中断信号,CPU也不会响应。所以我们要将SS 和 SP的赋值指令连续存放。

第十三章 int指令

13.1 int指令

int格式:int N, N为中断类型码。它的功能是引发中断 CPU执行int N指令,相当于引发一个N号中断的中断过程,执行过程如下:1、取得中断类型码 N 2、PWD TF=0 IF=0 3、PUSH CS PUSH IP 4、IP = 4XN CS = 4XN+2 可以在程序中使用int指令调用任何一种中断的中断处理程序。可见,int指令的最终功能和call指令相似,都是调用一段程序。因此,一般情况下,系统将一些具有一定功能的子程序,以中断处理程序的方式提供给应用程序调用。

13.2 编写供应用程序调用的中断程序

13.3 对int、IRET和栈的深入理解

13.4 BIOS和DOS所提供的中断程序

前面的课程中,我们都是自己编写中断程序,将他们放在安装程序中,然后通过运行安装程序,将他们安装到指定的内存区。此后,别的应用程序才可以调用。BIOS DOS提供的中断程序是如何安装到内存中的呢?1、开机后,CPU一加电,初始化 CS=0FFFFH, IP=0,自动从FFFF:0000单元开始执行程序。FFFF:0000处有一条跳转指令,CPU执行该指令后,转去执行BIOS中的硬件系统检测和初始化程序。2、初始化程序将建立BIOS所支持的中断向量,即将BISO提供的中断程序的入口地址登记在中断向量表中。3、硬件系统检测和初始化完成后,调用INT 19H进行操作系统的引导。从此将计算机交由操作系统控制。4、DOS启动后,除完成其他工作外,还将它所提供的中断程序装入内存,并建立相应的中断向量

13.5 BIOS和DOS所提供的中断程序安装过程

13.6 BIOS中断程序应用

一般来说,一个供程序员调用的中断历程中往往包括多个子程序,中断程序内部用传递景来的参数来决定执行哪一个子程序。BIOS和DOS提供的中断程序,都用AH来传递内部子程序的编号。INT 10H; 中断程序,设置光标位置 MOV AH, 02H; 表示调用2号子程序,功能设置光标位置 MOV BH, 00H; 设置页号 MOV DH, 05H; 设置行号 MOV DL, 0CH;设置列号 INT 10H;

13.7 DOS中断程序应用

INT 21H中断程序是DOS提供的中断程序,其中包含了DOS提供给程序员在编程时调用的子程序。MOV AH, 4CH; 参数,程序返回 MOV AL, 00H; 返回值 INT 21H INT 21H;中断程序在光标位置显示字符串的功能 DS:DX 指向字符串; 要显示的字符串需用“$”作为结束符 MOV AH, 09H; 功能号9 ,表示在光标位置显示字符串 INT 21H

第十四章 端口

14.1 端口的读写

CPU可以直接读取3个地方的数据 1、CPU内部的寄存器 2、内存单元 3、端口 对于端口的读写不能用MOV/PUSH/POP等内存读写指令 端口的读写指令只有两条:IN/OUT分别用于从端口读取数据和往端口写入数据 我们看一下CPU执行内存访问指令和端口访问指令的时候,总线上的信息 1、访问内存 MOV AX, DS:[0008H]; 总线相关的操作:CPU通过地址总线将地址信息0008H发出 CPU通过控制总线发出内存读命令,选中存储芯片,并通知它,将要从中读取数据。存储器将0008H单元中的数据通过数据线送入CPU 2、访问端口 IN AL, 0060H; 从60H端口读入一个字节 总线相关的操作:CPU通过地址总线将地址信息60H发出 CPU通过控制总线发出端口读命令,选中端口所在芯片,并通知它,将要从中读取数据。端口所在芯片将60H端口中的数据通过数据线送入CPU 注意:在IN/OUT指令中,只能使用AX/AL存放从端口读入的数据或要发送到端口的数据。访问8位端口是用AL,访问16位端口是用AX

14.2 CMOS RAM 芯片

PC机中有一个CMOS RAM芯片,具有如下特征:1、包含一个实时钟和一个有128个存储单元的RAM存储器(早期的计算机为64个字节) 2、该芯片考电池供电,因此,关机后其内部的实时钟任可正常工作,RAM中的信息不丢失。3、128个字节的RAM中,内部实时钟占用 0 - 0DH单元来保存时间信息,其余大部分单元用于保存系统配置信息,供系统启动时BIOS程序读取。BIOS也提供了相关的程序,使我们可以在开机的时候配置CMOS RAM中的系统信息。4、该芯片内部有两个端口,端口地址为 70H/71H。CPU通过这两个端口读写CMOS RAM。5、70H为地址端口,存放要访问的CMOS RAM单元的地址,71H为数据端口,存放从选定的CMOS RAM单元中读取的数据,或要写入到其中的数据。可见,CPU对CMOS RAM的读写分为两步。

14.3 SHL SHR指令

SHL SHR是逻辑移位指令 SHL逻辑左移指令,功能为:1、将一个寄存器或内存单元中的数据向左移位 2、将最后移出的一位写入CF中,最低位用0补充 如果移动位数大于1时,必须将移位次数放在CL中 可以看出,将X逻辑左移一位,相当于执行X=X*2 SHR逻辑左移指令,功能为:1、将一个寄存器或内存单元中的数据向右移位 2、将最后移出的一位写入CF中,最高位用0补充 可以看出,将X逻辑右移一位,相当于执行X=X/2

14.4 CMOS RAM 中存储的信息

秒 00H 时 02H 分 04H 日 07H 月 08H 年 09H 这些数据以BCD码的方式存放 CMOS RAM存储时间信息的单元中,存储了用两个BCD吗表示的两位十进制数高4位的BCD表示十为,低四位BCD码表示个位

第十五章 外中断

15.1 接口芯片和端口

以前我们讨论的都是CPU对指令的执行。我们知道,CPU在计算机系统中,除了能够执行指令,进行运算以外,还应该能够对外部设备进行控制,接收它们的输入,向他们进行输出。也就是,CPU除了有运算能力外,还有I/O(INPUT/OUTPUT)能力。在PC系统的接口卡和主板上,装有各种接口芯片。这些外设接口芯片的内部有若干寄存器,CPU将这些寄存器当作端口来访问。外设的输入不直接送入内存和CPU,而是送入相关接口芯片的端口中。CPU向外设的输出也不是直接送入外设,而是先送入端口中,在由相关的芯片送入到外设。CPU还可以向外设输出控制命令,而这些控制命令也是先送到相关芯片的端口中,然后在由相关的芯片根据命令对外设实施控制。可见,CPU通过端口和外部设备进行联系。CPU在执行完当前指令后,可以检测到发送过来的中断信息,引发中断过程,处理外设的输入。

15.2 外中断信息

在PC系统中,外中断源一共有两类:1、可屏蔽中断 可屏蔽中断是CPU可以不响应的外部中断。CPU是否响应可屏蔽中断,要看标志寄存器的IF位的设置。如果IF=1,则CPU在执行完当前指令后响应中断,引发中断过程。若果IF=0,则不响应可屏蔽中断 1、取中断类型码N 2、标志寄存器入栈 PUSHF, IF=0, TF=0 3、POP CS; POP IP; 4、(IP)=4N, (CS)=4N+2 执行中断处理程序 可屏蔽中断所引发的中断过程,除了第一步的实现上有所不同外, 基本上和内中断的中断过程相同。因为可屏蔽中断信息来之CPU外部,中断类型码是通过数据总线送入CPU的。而内终端的中断类型码是在CPU内部产生的。现在,我们可以解释中断过程中将IF设置为0的原因了,将IF这是为0的原因就是,在进入中断处理程序后,禁止其他的可屏蔽中断。当然,如果在中断处理程序中需要处理可屏蔽中断,可以用指令将IF设置为1。8086CPU 提供的设置IF的指令如下:STI CLI 2、不可屏蔽中断 不可屏蔽中断是CPU必须响应的外中断。当CPU检测到不可屏蔽中断信息是,则在执行完当前指令后,立即响应,引发中断过程。对于8086CPU不可屏蔽中断的中断类型码固定为2.所以中断过程中,不需要取中断类型码。不可屏蔽中断的中断过程:1、PUSHF, IF=0, TF=0 2、PUSH CS; PUSH IP 3、(IP)=8, (CS)=0AH 几乎所有由外设引发的外中断,都是可屏蔽中断。当外设有需要处理的时间(比如说键盘输入)发生时,相关芯片向CPU发出可屏蔽中断信息。不可屏蔽中断是在系统中有必须处理的紧急情况发生时用来通知CPU的中断信息。在我们的课程中,主要讨论可屏蔽中断。

15.3 PC机键盘的处理过程

1、键盘输入 2、引发9号中断 3、执行 int 9 中断历程 键盘上的每一个键相当于一个开关,键盘中有一个芯片对键盘上的每一个键的开关状态进行扫描。按下一个键是,开关接通,该芯片就产生一个扫描码。扫描码说明了按下的键在键盘上的位置。扫描码被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60H 松开按下一个键是,也产生一个扫描码。扫描码说明了松开的键在键盘上的位置。扫描码也被送入主板上的相关接口芯片的寄存器中,该寄存器的端口地址为60H 一般将按下一个键时产生的扫描码称为通码,松开一个键产生的扫描码称为断码。扫描码长度为一个字节,通码的第7位为0,断码的第7位为1:断码 = 通码 + 80H BIOS 提供了INT 9中断历程,用来进行基本的键盘输入处理,主要工作如下:1、读出60H端口中的扫描码 2、如果是字符键的扫描码,将该扫描码和它所对应的字符码(ASCII)送入内存中的BIOS键盘缓冲区 键盘的输入到达60H端口时,相关的芯片就会向CPU发出中断类型码为9的可屏蔽中断信息。CPU检测到该中断信息后,如果IF=1,则相应中断,引发中断过程,转去执行int 9中断历程。如果是控制键(Ctrl)和切换键(CapsLock)的扫描码,则将其转变为状态字节(用二进制位记录控制键和切换键状态的字节),写入内中中存储状态字节的单元。3、对键盘系统进行相关的控制,比如说,向相关芯片发出应答信息。BIOS键盘缓冲区是系统启动后,BIOS用于存放INT 9中断历程所接受的键盘输入的内存区。该内存区可以存储15个键盘输入,因为,INT 9中断历程除了接受扫描码外,还要产生和扫描码对应的字符码,所以BIOS键盘缓冲区中,一个键盘输入用一个字单元存放,高位字节存放扫描码,低位字节存放字符码。0040:17 单元存储键盘状态字节,该字节记录了控制键和切换键的状态。键盘状态字节各位记录信息如下:

15.4 编写int 9 中断历程

深入学习汇编语言,探究汇编语言的奥秘 <The Art of Assembly Language> 如果单纯要完成这点还是相对比较简单的,因为BIOS提供的int 9中断历程已经对这些硬件的细节进行了处理。我们只要在自己编写的中断历程中调用BIOS的int 9中断历程就可以了。任务演示:在屏幕中间依次显示 a-z并可以让人看清。在显示的过程中,按下ESC键后,改变显示的颜色。改变屏幕颜色指令系统总结:我们对8086CPU的指令进行一下总结。读者要详细了解8086指令系统中的各个指令的用法,可以查看有关的指令手册。8086CPU提供以下几大类指令:1、数据传送指令 比如:MOV/PUSH/POP/PUSHF/POPF/XCHG 等都是数据传送指令,这些指令实现寄存器和内存、寄存器和寄存器之间的单个数据传送。2、算术运算指令 比如:ADD/SUB/ADC/SBB/INC/DEC/CMP/MUL/DIV 等都是算术运算指令。这些指令实现寄存器和内存中的数据的算数运算。它们的执行结果影响标志寄存器:SF/ZF/OF/CF/PF/AF 3、逻辑指令 比如:AND/OR/NOT/XOR/TEST/SHL/SHR/ 等都是逻辑指令 除了 NOT 指令外,它们的执行结果都影响标志寄存器的相关标志位。4、转移指令 可以修改IP,或同时修改CS:IP的指令统称为转移指令。转移指令分为以下几类:1、无条件转移指令,比如:JMP 2、条件转移指令,比如:JCXZ/JE/JB/JA/JNB/JNA 3、循环指令,比如:LOOP 4、过程,比如:CALL/RET/RETF 5、中断,比如:INT/IRET 5、处理机控制指令 这些指令对标志寄存器或其他处理机状态进行设置,比如:CLD/STD/CLI/STI/NOP/CLC/CTC 6、串处理指令 这些指令对内存中的批量数据进行处理 比如:MOVSB/MOVSW 如要使用这些指令方便地进行数据的处理,则需要和REP等前缀指令的配合使用

第十六章 直接定址表

16.1 描述了单元长度的标号

前面的课程中,我们一直在代码段中使用标号来标记指令、数据、段的起始地址 程序中,标号仅仅表示内存单元的地址。但是,我们还可以使用一种标号,这种标号不但表示内存单元的地址,还表示了内存单元的长度,即表示在此标号处的单元,是一个字节单元,还是字单元,还是双字单元。我们在code段中使用的标号a/b后面没有使用“:”,因此他们是可以同时描述内存地址和单元长度的标号。因为这种标号包含了对单元长度的描述,所以,在指令中,它可以代表一个段中的内存单元。比如,对于程序中的 B DW 0 指令 MOV AX, B; --> MOV AX, CS:[0008H] 指令 MOV B, 0002H --> MOV WORD PTR CS:[0008H], 0002H 指令 INC B --> INC WORD PTR CS:[0008H] 在这些指令中,标号B代表了一个内存单元,地址为CODE:0008H,长度为2字节 可见,使用这种包含单元长度的标号,可以使我们以简洁的形式访问内存中的数据 以后,我们将这种标号称为数据标号。它标记了存储数据的单元的地址和长度。它不同于仅仅表示地址的地址标号

16.2 在其他段中使用数据标号

一般来说,我们不会再代码段中定义数据,而是将数据定义到其他段中。在其他段中,我们也可以使用数据标号来描述存储数据的单元地址和长度 注意:在后面加有 “:” 的地址标号,只能在代码段中使用,不能再其他段中使用。注意:如果想在代码段中,直接用数据标号访问数据,则需要用伪指令ASSUME将标号所在的段和一个段寄存器联系起来。否则编译器在编译的时候,无法确定标号的段地址在哪一个寄存器中。当然,这种联系是编译器需要的,但绝对不是说,我们因为编译器的工作需要,用ASSUME指令将段寄存器和某个段相联系,段寄存器中就会真的存放该段的地址。因为这些时间编译出的指令,都默认所访问单元的段地址在DS中,而实际要访问的段为DATA,所以,若要访问正确,在这些指令执行前,DS必须为DATA段的段地址。

16.3 直接定址表

我们讨论 “查表” 的方法编写相关程序的技巧。任务:编写子程序,以十六进制的形式在屏幕中间显示给定的 byte型数据 一个字节需要两个十六进制数来表示,所以,在程序需要在屏幕沈阳显示两个ASCII字符 我们可以将一个byte的高4位和低4位分开,分别用它们的值得到对应的字符,比如2BH,我们会可以得到高4位的值为2为2,低4位的值为11. 如果数值为 0,则显示“0” 如果数值为 1,则显示“1” 如果数值为 f,则显示“f” 我们可以看出,这样做,程序中要使用多条比较转移指令。程序比较成、混乱。显然,我们希望能够在数值0-15和字符“0-f”之间找到一种映射关系。这样我们用0-15之间的任何数值,都可以通过这种映射关系直接得到 “0-F”中对应的字符。数字 0-9 和 字符 “0-9”之间的映射 +30H 10-15 和 “A-F”之间的映射关系是 +37H可见,我们是利用数值和字符之间的这种原本存在的映射关系,通过高4位和低4位值得到对应的字符。但我们发现了一个问题。由于映射关系的不同,我们在程序中必须进行一些比较,对于大于9的数值,我们要用不同的计算方法。这样做,虽然是程序得到了简化。但是,如果我们希望用简洁的算法,就要考虑用同一种映射关系从数值得到字符码。我们建立一张表,表中一次存放字符 “0-F”,我们可以通过数值0-15直接查找到对应的字符。利用表,在两个数据集合之间建立一种映射关系,使我们可以在查表的方法给出的数据得到其在另一集合中的对应数据。这样做的目的一般来说有三个:1、为了算法的清晰和简洁。2、为了加快运算速度 3、为了使程序易于扩充 任务二:编写一个子程序,计算sin(x) x ∈ (0, 30, 60, 90, 120, 150, 180),并在屏幕中间显示计算结果。列如 sin(30) 的结果 为0.5 我们可以用麦克劳林公式来计算 sin(x) 我们看出,sin(x)需要进行多次乘法和除法是非常耗时的。那么我们如何才能不做乘除而计算 sin(x)? 我们可以看出,其实用不着计算,可以占用一些内存空间来换取运算的速度。像这种可以通过依据数据,直接计算出所要找的元素的位置的地址,我们称其为:直接定制表 我们可以在直接定制表存储子程序的地址,从而方便地实现不同子程序的调用。

16.4 程序入口地址的直接定址表

实现一个子程序 setscreen ,为显示提供如下功能:1、清屏 将当前屏幕 将显存中当前屏幕中的字符设置为空格符 2、设置前景色3、设置背景色 4、向上滚动一行 入口参数说明:1、用AH寄存器传递功能号 0 1 2 3 对于2/3号功能,用AL传送颜色值 AL = 0, 1, 2, 3, 4, 5, 6,7

第十七章 使用BIOS进行键盘输入和磁盘读写

罗云斌 WIN32汇编语言程序设计

17.1 INT 09H中断历程对键盘输入的读写

大多数有用的程序都需要处理用户的输入,键盘 输入是最基本的输入。程序和数据通常需要长期存储,磁盘是最常用的存储设备。BIOS为这两种外设的I/O提供了最基本的中断历程,在本章中,我们队它们的应用和先关的问题进行讨论。我们已经讲过,键盘输入将引发9号中断,BIOS提供了INT 09H中断历程 CPU在9号中断发生后,执行INT 09H中断历程,从60H端口读出扫描码,并将其转化相应的ASCII码或状态信息,存储在内存的指定空间(键盘缓冲区或状态字节)中。键盘缓冲区中有16个字单元,可以存储15个按键的扫描码和对应的ASCII吗。

17.2 使用INT 16H中断历程读取磁盘缓冲区

INT 16H中断历程中包含的一个最重要的功能是从键盘缓冲区读取一个键盘输入,该功能的编号为0 下面的指令从键盘缓冲区中读取一个键盘输入,并将其从键盘缓冲区中删除 MOV AH, 00H INT 16H 结果 AH = 扫描码 AL = ASCII码 1、检测键盘缓冲区中是否有数据 2、没有则继续做第1步 3、读取缓冲区第一个字单元中的键盘输入 4、将读取的扫描码送入AH ASCII送入AL 5、将已读取的键盘输入从键盘缓冲区中删除 可见BIOS的INT 09H/INT 16H中断历程是一对相互配合的程序,INT 09H中断历程向键盘缓冲区中写入,INT 16 H中断历程从缓冲区中读出。编程,接收用户的键盘输入 r将字符设置为 红色 g将字符设置为 绿色 b将字符设置为 蓝色

17.3 字符串输入

用户通过键盘输入的通常不仅仅是单个字符而是字符串。下面我们讨论字符串输入中的问题和简单的解决方法。最基本的字符串输入程序,需要具备下面的功能:1、在输入的同时需要显示这个字符串 2、一般在输入回车符后,字符串输入结束 3、能够删除已经输入的字符串 对于这三个功能,我们可以想象在DOS中输入命令行时的情况 编写一个接受字符串的输入子程序,实现上面三个基本功能。因为在输入的过程中需要显示,子程序的参数如下:DH DL 字符串在屏幕上显示的行、列位置 DS:SI 指向字符串的存储空间,字符串以0结尾符。可以看出在字符串输入的过程中,字符的输入和输出是按照栈的访问规则进行的,即后进先出 这样,我们就可以用栈的方式来管理字符串的存储空间,也就是说,字符串存储空间实际上是一个字符栈 字符栈的所有字符,从栈底到栈顶,组成一个字符串。在输入回车字符后,字符串输入结束。我们可以在字符串中加入0, 表示字符串结束。输入的同时要显示这个字符串。每次有新的字符输入和删除一个字符的时候,都应该重新显示字符串,即从字符栈的栈底到栈顶,显示所有的字符。程序的处理过程:1、调用 INT 16H读取键盘输入 2、如果是字符,进入字符栈,显示字符栈中的多有字符;继续执行1 3、如果是退格键,从字符串栈中弹出一个字符,显示字符栈中的所有字符,继续执行1 4、如果是enter键,向字符栈中亚茹0, 返回。从程序的处理过程中可以看出,字符栈的入栈、出栈和显示栈中的内容,是需要多出使用的功能,我们将他们写为子程序 子程序:字符栈的入栈、出栈和显示 参数说明:AH = 功能号 0 表示入栈 1 表示出栈 2 表示显示 DS:SI 0 (AL) = 入栈字符 1 (AL) = 返回字符 2DH DL 行号和列号

17.4 应用INT 13H中断历程对磁盘进行读写

这里主要以3.5英寸软盘为例,进行讲解,但原理依旧不变。常用的3.5英寸软盘的结构:分为上下两面,每面有80个磁道,每个磁道又分为18个扇区,每个扇区的大小为512B 总容量:2 x 80 x 18 x 512 = 1440KB = 1.44M 磁盘的实际访问有磁盘控制器进行,我们可以通过控制磁盘控制器来访问磁盘 注意:我们只能以扇区为单位对磁盘进行读写。在读写扇区的时候,要给出面号,磁道号和扇区号。面号和磁道号从0开始,而扇区号从1开始。BIOS提供了对扇区进行读写的中断历程,这些终端历程完成了许多复杂的和硬件相关的工作。读取0面0道1扇区的内容到0000:0200 AH 功能号2 读 3 写 AL 扇区数 CH 磁道号 CL 扇区号 DH 磁头号 DL 驱动号 0 软盘A 1 软盘B 80H 硬盘C 81H 硬盘D ES:BX 指向接收扇区读入的内存区 返回参数 成功 AH=0, AL=读入的扇区数 失败 AH=出错代码 注意:下面我们要使用INT 13H中断历程对软盘进行读写。直接向磁盘扇区写入数据是很危险的,很可能覆盖掉重要的数据。如果向软盘的0面0道1扇区中写入数据,要使软盘在现有的操作系统下可以使用,必须要重新格式化 在编写相关的程序之前,必须要找到空闲的软盘。在使用INT 13H中断历程一定要注意驱动器号是否正确,千万不要随便对磁盘的扇区进行读写

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。