(计算机)汇编语言实战精解.pdf

上传人:小小飞 文档编号:3804567 上传时间:2019-09-23 格式:PDF 页数:295 大小:1.57MB
返回 下载 相关 举报
(计算机)汇编语言实战精解.pdf_第1页
第1页 / 共295页
(计算机)汇编语言实战精解.pdf_第2页
第2页 / 共295页
(计算机)汇编语言实战精解.pdf_第3页
第3页 / 共295页
(计算机)汇编语言实战精解.pdf_第4页
第4页 / 共295页
(计算机)汇编语言实战精解.pdf_第5页
第5页 / 共295页
亲,该文档总共295页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述

《(计算机)汇编语言实战精解.pdf》由会员分享,可在线阅读,更多相关《(计算机)汇编语言实战精解.pdf(295页珍藏版)》请在三一文库上搜索。

1、第 1 章 汇编语言基础知识 11 汇编语言的特点 所谓汇编语言,其实质就是机器语言的一个高级的形式。我们知道,机器语言是 CPU 唯一 可以真正“理解“的语言,它是用一些由“0“和“1“两个数字组成的一组数字来表示的。例如: 1011000000000001(意思是将数字 1 放入累加器) 。 这样的一组数字非常难以理解和记忆,毕竟程序员不是一块 CPU。为了使程序设计人 员能够很好地记忆这些机器指令, 简化程序设计工作,技术人员将这些怪异的数字用一些取 自人类语言的简短的文字符号来表示, 于是就产生了汇编语言。这些简短的文字符号称为指 令助记符。例如上面的那个机器指令用汇编语言表达出来,就

2、是“MOV AL,1“。 同高级语言相比,汇编语言具有一些极其突出的特点: 汇编语言是一种完全面向硬件的语言,这同 BASIC,C 之类的高级语言截然不同。 多数高级语言都是面向问题的,例如:如果需要在屏幕上显示一串文字时,我们可以直接应 用 BASIC 语言中的 PRINT 语句,或用 C 语言中的 PRINTF 函数,这个问题就迎刃而解了。 而使用汇编语言编程,解决这个问题的最终操作是“将这些文字的 ASCII 码写入显示缓冲存 储器中“。可见,汇编语言将这个问题转化成了对硬件(显示缓冲存储器)的操作(写入)。 这是汇编语言的一个极其突出的特点,也是汇编语言同高级语言的最显著的差别; 同高

3、级语言相比,汇编语言编写的程序结构十分紧凑,运行速度很快。汇编语言同 机器指令直接对应,编译速度快,同时,CPU“理解“其“母语“的速度远高于“翻译“高级语言 的速度。因此,汇编语言是所有程序设计语言中运行效率最高的。这是汇编语言的一个最为 突出的优点。当需要编写高速运行的软件时,例如编写图像处理程序,就往往使用汇编语言 编写软件中的关键部分; 用汇编语言编制程序十分费时,而且程序的质量直接受到程序员技术水平的影响, 程序的可读性也很差。就象前面所举的输出文字的例子,用高级语言编程只需写一条语句, 简单明了,极其直观。而用汇编语言编程则需写出一系列指令,这些指令都是些对硬件的操 作,同“文字输

4、出“这个问题没有明显的直接联系,因此程序的可读性很差。 由于汇编语言是面向硬件的, 所以用汇编语言编制的程序可移植性很差。 显而易见, 不同的 CPU 都有相互独立的指令系统,相互间无任何关系,就算是使用同一系列 CPU 的机 器,因其外围硬件可能有差别,这也会使相同的程序在不同的机器上无法通用。 不难看出, 汇编语言存在很多的弱点, 但由于它具有一些高级语言所不具备的突出优点, 所以汇编语言的应用范围还是很广的。特别是当用户需要研究计算机具体的工作原理的时 候,还必须要掌握汇编语言。 1 1 1 12 2 2 2 汇编语言中的数汇编语言中的数汇编语言中的数汇编语言中的数 高级语言中也多用十进

5、制数。十进制数由 09 十个数字组合而成,逢 10 进 1。但由于 汇编语言是面向硬件的,因此,在汇编语言中使用的数字也是和硬件结合紧密的二进制数。 二进制数只由 0 和 1 而个数字组成,逢 2 进 1,也就是说,在十进制数中计算 1+1 时将 得到一位数字的结果-2,而在二进制数中计算时将得到一个二位二进制数-10,表 11 列 出了四位二进制数与十进制数间的对应关系。 在十进制数字中还有“数位“之分,个位,十位,百位,在二进制数中也分各个数位。 以四位二进制数 1010 为例,从右数第一位,称为 bit0,第二位称 bit1,以此类推。因此, 1010 的 bit3,bit1 位是 1,

6、bit2,bit0 位是 0。与十进制数一样,二进制数自右向左数位逐渐 升高。最左端的数位为最高位,1010 的最高位为 1。 表 11 四位二进制数与十进制数对照表 我们知道,计算机利用电路来记忆 1 和 0,那么 1 和 0 究竟和电路的工作状态有什么关 系呢?通俗的讲,1,0 反应了电路输出(入)电压的高和低。如果电路输出(入)的电压 高,比如达到了电源电压的幅度,此时电路的输出(入)就为 1。如果电路输出(入)的电 压很低,比如接近 0V,那么这时电路的输出(入)就为 0。 二进制数字在实际应用中还有一些缺点,比如不便于书写,难于记忆等。为此,在汇编 语言中还常用另一种数制-十六进制。

7、十六进制数字由十六个数组成,逢 16 进 1。前十个数 字和十进制一样,是 09,后六个数字用英文字母 AF 来表示。表 12 列出了十六进制 与十进制数间的对应关系。 表 12 十六进制数与十进制数对照表 从表 1-2 可以看出,有些十六进制数与十进制数是一样的,区分不开。这个问题对于 1 位十六进制数而言到还没有什么关系, 可对于多位十六进制数来说就有些麻烦了。 比如给出 一个数“79“,它是十进制的“79“还是十六进制的“79(相当于十进制 121)“呢?所以为区分 十六进制数与十进制数,特别规定了十六进制数尾部应加字母“H“(Hexadecimal) 。十六进 制数 79 应该写成“7

8、9H“才对。 对照表 11 和表 12,可以看出每个四位二进制数都有相应的十六进制数与之对应。 若用十六进制数代替二进制数,在书写时可以使数位减少四倍,简化了书写。同时也可以看 到十六进制数同二进制数之间没有实质上的差别, 只是两种不同的表达形式而已。 下面的例 子介绍了二十十六进制数字间的转换方法,请大家自己总结其中的规律。 例 11将十进制数 83 转换成二进制数。 解:采用短除法计算 2| 83-余数:1 bit0 2| 41-余数:1 bit1 2| 20-余数:0 bit2 2| 10-余数:0 bit3 2| 5-余数:1 bit4 计算结果为 7 位二进制数:1010011 例

9、12 将 8 位二进制数 10110110 转换成十进制数。 解:8 位二进制数各个数位的权如下所示: bit76543210 权2726252423222120 10110110 = 127+026+125+124+023+122+121+020= 182 二进制数00000001001000110100010101100111 十进制数01234567 二进制数10001001101010111100110111101111 十进制数89101112131415 十进制01234567 十六进制01234567 十进制89101112131415 十六进制89ABCDEF 例 13 将十进

10、制数 34567 转换成十六进制数。 解:仿照例 11 采用短除法计算 16| 34567-余数:7 第 0 位 16| 2160-余数:0 第 1 位 16| 135-余数:7 第 2 位 8第 3 位 结果是一个 4 位十六进制数:8707 例 14 将 16 位二进制数 1101101001100011 转换成十六进制数。 解:将此二进制数按每 4 位为一组分成 4 组 1101101011000011 DAC3 1101101011000011 = DAC3 1 1 1 13 3 3 3 数学运算和逻辑操作数学运算和逻辑操作数学运算和逻辑操作数学运算和逻辑操作 同十进制数一样,二进制数

11、同样有相应的加,减,乘,除之类的运算,这些运算称为数 学运算。 由于数学运算还涉及到二进制的其它一些重要的知识, 因此在这里暂时不做深入讨 论。现在主要来讨论二进制数的逻辑操作。 这里所说的逻辑不同于广义的逻辑, 事实上在计算机中的逻辑关系十分简单, 只有四种 -与逻辑,或逻辑,非逻辑和异或逻辑。与、或和非的关系可以通过一个电路的例子来说明, 见图 11: 图 1-1 三种逻辑关系示意图 在三个图中,灯被点亮的条件是什么呢?很明显,当 A 点电压和电源电压一致时(即 A 点输出为 1 时) ,灯就会亮。看来主要的问题就是如何使 A 点输出 1? 对于(a)图,只有当开关K1,K2 都闭合时,A

12、 点才会与电源接通,此时灯亮。若把“ 开关闭合“这一动作用“1“表示,把“开关断开“用“0“表示,则可以说,在(a)图中只有两个 开关都是“1“时,A 点才会输出“1“。这种开关状态与输出之间的关系就是“与“逻辑关系。 对于(b)图来讲,两个开关或者 K1 为“1“(接通) ,或者 K2 为“1“,或者两者都为“1“, 均可以使 A 点输出为“1“,这两个开关与输出之间的逻辑关系就称为“或“逻辑关系。 对于(c)图而言,当 K 为“0“时 A 点才会输出“1“,K 为“1“时电源被短路,此时 A 点输 出“0“。这种逻辑关系称为“非“逻辑关系。 “异或“关系不大好用图表达,但是异或关系有一个重

13、要的特点,就是当进行异或操作的 两个数“相同“时所得结果就是“0“,而两个数“不同“时就得“1“。这是一个十分重要的特性, 大家需牢牢记住。 所谓逻辑操作,就是把两个数按照选定的某种逻辑关系加以处理并得出结果的过程。 逻 辑操作通常用于使一个二进制数中的某些数位的状态变成我们需要的其它状态, 而不改变其 它位。 在汇编语言中,基本的逻辑操作有四种:与操作、或操作、非操作和异或操作。分别记 作 AND、OR、NOT 和 XOR。表 13 给出了这四种操作的具体情况。 表 13 四种逻辑操作执行的结果 进行逻辑操作的两个数值不同的逻辑操作及其结果 ABANDORNOT*XOR 000010 010

14、111 100101 111100 * 注:“非“操作只对一个数进行,表中选择的是 A。 下面的例子说明了这四种逻辑操作的应用 例 15 给定一个八位二进制数 10110100 求一个八位二进制数,与给定的数作 OR 操作,要求结果为 10111101。 求一个八位进制数,与给定的数作 AND 操作,要求结果为 00110000。 对给定的数作什么操作可得到二进制数 01001011。 若把给定的数同 00100010 作 XOR 操作,将得到什么结果。 解: 将 OR 操作的结果同给定的数相比,不难发现只要把给定数字的 bit0,bit3 位 置成 1,其它位状态保持不变,即可得出结果。因此

15、可很容易求出八位二进制数 00001001, 并可验证: 10110100 OR) 00001001 10111101 可见, OR 操作可以方便的将某个二进制数的特定位置成“1“而保存其它位不变。 只要取 另一个二进制数,让这个数中的相应数位-即和给定的数中要改变的数位相对应的位-为 “1“,而其它位为“0“,即可达到目的。 将 AND 操作的结果同给定的数相比,可以看出只要把给定数字的 bit2,bit7 位置成 0,其它位保持不变,即得出结果。因此可很容易求出八位二进制数 01111011,并且可以验 证: 10110100 AND) 01111011 00110000 可见,和 OR

16、操作相对应,AND 操作可以把某个二进制数的特定位置“0“而保持其它位 不变。只要取另一个二进制数,让这个数的相应位为“0“,而其它位为“1“,即可达到目的。 比较结果和给定数, 可看出将已知数所有位取相反状态, 可得到结果。 因此可用 NOT 操作,即 NOT) 10110100 01001011 将两个数作 XOR 操作 10110100 XOR) 00100010 10010110 把得到的结果 10010110 同已知数相比较,可以看出只要将已知数的 bit1,bit5 两位取 反,就能得出结果。将(3)和(4)进行比较,可以发现这样一个规律:NOT 操作可以将 给定数的所有位取反,而

17、 XOR 操作可以将给定数的特定位取反;进一步分析(4) ,不难看 出若把所得到的结果 10010110 和 00100010 再作一次 XOR 操作: 10010110 XOR) 00100010 10110100 又能得到了已知的数。即取反后的数位又重新恢复原状态。因此我们说,XOR 操作可 以反复改变给定数中的特定位状态。 14 汇编语言中的文字和符号 习惯上通常把文字和符号统称为“字符“, 字符在机器中是如何表示和存储的呢?我们知 道计算机可以方便地处理数字, 因此如果把字符用数字来表示, 就可以很方便的在计算机中 储存和处理。 所以在计算机中一般采用 ASCII (美国信息交换代码

18、American Standard Code for Information Interchange)码来表示字符。 计算机中所用的字符包括: 字母:A、B、 Z,a、b、 z; 数字:0、1、 9; 专用符号:、*、= 控制符号:CR(Carriage Return 回车) 、LF(Line Feed 换行) 这些字符的 ASCII 码都列在下面表 1-4 和表 1-5 中。 表 1-4 ASCII 基本字符对照表 ASCII 值 000 001 002 003 004 005 006 007 008 009 00A 00B 00C 00D 00E 00F 010 011 012 013 0

19、14 015 016 017 018 字符 NULL SOH STX ETX EOT END ACK BEL BS HT LF VT FF CR SO SI DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN ASCII 值 020 021 022 023 024 025 026 027 028 029 02A 02B 02C 02D 02E 02F 030 031 032 033 034 035 036 037 038 字符 SPACE ! “ # $ % 中断向量,256 个中断向量组织在一起形成一个 表,我们把这个表称为中断向量表。这个表就是 CPU 取得中断服务程序

20、入口地址的依据。给定一个中断号,即可根据下式计算其对应服务程 序入口地址在中断向量表中的偏移量: 偏移量nn4 利用 DEBUG 程序可以观察到中断向量表。进入 DEBUG,在后打入 D0:0Enter,屏幕上就会显示出中断向量表中的部分入口地址。 为了能观察内存中任意位置存放的数据,DEBUG 提供了一条内存转储命 令,命令码是D(Dump) 。使用方法很简单,在提示符后输入D 逻辑地址 长度并回车, DEBUG 会从指定地址开始将内存中的数据以 16 进制形式 显示在屏幕上,同时在屏幕右则显示出一些数据所对应的 ASCII 字符。 如果没有给出完整的逻辑地址,只给出偏移量,则 DEBUG

21、会默认 DS 为段地址。如果未 指定地址, 则 DEBUG 就认为起始地址是跟在由前一个 D 命令所显示的单元的后面。 连续输入 不带参数的 D 命令,可连续显示内存中的数据。下面列出了 D 命令的一些用法: D0:0Enter 从内存 0:0 处开始显示数据 DES:100Enter 从内存中 ES 所指定段的偏移 100H 处开始显示数据 D0B30:0 3FFEnter 从内存中 0B30:0 处开始连续显示 1KB 数据 下面列出的数据就是笔者所用 PC 中的一部分中断向量表。 -d0:0 5F16 进制形式的数据相应的 ASCII 码 0000:000089 3E 7E BE B0

22、05 70 00-C3 E2 00 F0.p.p. B0 05 70 00 0000:0010B0 05 70 00 54 FF 00 F0-4C E1 00 F0p.T.L.o. 6F EF 00 F0 0000:002076 18 B0 D3 88 20 B0 D3-6F EF 00 F0v o.o. 6F EF 00 F0 0000:00306F EF 00 F0 6F EF 00 F0-57 EF 00 F0o.o.W.p. B0 05 70 00 0000:0040C4 21 B0 D3 4D F8 00 F0-41 F8 00 F0.!M.A. EA 02 A7 B0 0000:0

23、05039 E7 00 F0 AE 03 71 02-01 06 AC B2.q. D2 EF 00 F09 屏幕右边所显示的是和数据对应的 ASCII 字符,关于 ASCII 码的知识第一章中有介绍。 在中断向量表中有一些向量为 0,表示这个向量未使用。 *INT 指令是“万能”的,它可以调用系统中任意一个中断,不论这个中断为谁服务。 有了中断向量表,CPU 就可以根据中断号找到对应服务程序的入口地址。把这个地址装 入 CS 和 IP,CPU 就开始执行中断服务程序。 每个中断服务程序的末尾都有一条中断返回指令,这个指令在形式上和 RET 相似: 助记符:IRET(Interrupt Ret

24、urn) 用途:从中断服务程序返回被中断程序 格式:IRET 执行:CPU 返回被中断处继续执行指令 指令看似简单, 但 CPU 的实际动作却是很复杂的。 在产生中断的时候 CPU 已经自动地把 将要执行的指令的逻辑地址推入堆栈,那么执行 IRET 时 CPU 会自动地从堆栈中取出事先存 放在堆栈中的返回地址并将其装人 CS:IP 中, 同时 CPU 还会从堆栈中恢复标志寄存器的原状 态,这样一个中断就处理完了。 3 32 2 几个常用的软件中断几个常用的软件中断 (1)INT 20H 这个软中断在上一章已经用到了, 它的作用就是结束我们的程序并将机器的控制权交给 DOS。这个中断由 DOS

25、为我们提供,提供这个中断的目的是为了和 CP/M86 操作系统兼容。 MicroSoft 并不提倡使用这个中断来结束程序。DOS 为我们提供了更好的方法,这将在后面 介绍。 (2)INT 21H 这个软中断也是 DOS 为我们提供的, 它的服务程序包括大量的功能, 这些功能都是为方 便 PC 程序员编程而设计的。我们把这个中断称为 DOS API(Application Programming Interface) ,也就是“应用程序编程接口“。如果在所编的程序中需要 DOS 提供服务,比如打 开文件、分配内存等,就需要调用 21H 中断。下面我们来看几个常用的功能。 功能号:01H 用途:从

26、键盘接收一个字符并显示在屏幕当前光标位置 参数:AH=01;AH 寄存器中放功能号 调用:INT 21H;调用 21H 中断服务程序 返回:AL=键入字符的 ASCII 码 凡是调用系统功能编制程序,功能号一律放入 AH 寄存器。01 功能在执行时机器会等待 用户按键盘,用户按键后它会把与此键对应的 ASCII 码由 AL 寄存器返回给程序。所以如果 程序需要其使用者输入一个字符时(例如输入 Y/N) ,可以使用这个功能。 功能号:02H 用途:在屏幕当前光标位置处显示一个字符 参数:AH=02H DL=要显示字符的 ASCII 码 调用:INT 21H 返回:AL=显示字符的 ASCII 码

27、 这个功能用于把 DL 中的 ASCII 字符显示在屏幕上光标所在的位置。此功能返回时会把 DL 中数据放入 AL 中,所以如果在调用此功能前 AL 中存在有用的数据,那么就需要事先保 存 AL 寄存器的内容。请看下面的程序 PROG-3。 PROG-3 -a100Enter 09FE:0100 jmp 112;跳过字符串数据区 09FE:0102 Enter;在此处直接打回车 -e102Enter;用 E 命令将字符串的 ASCII 码置入内存 09FE:0102FC.5080.7274.6563.73BA.73A6.20 09FE:010882.6104.6e08.7983.2074.6b

28、5B.65BA.79B2.2e 09FE:011008.2e84.2e74.Enter -a112Enter 09FE:0112 mov bx,102;BX 寄存器中置入字符串数据区首址 09FE:0115 mov cx,10;需要处理的字符个数置入 CX 寄存器 09FE:0118 mov dl,bx;取得一个字符的 ASCII 码到 DL 寄存器 09FE:011A mov ah,02;选择 DOS API 的 02H 功能 09FE:011C int 21;调用 21H 中断显示 DL 中的字符 09FE:011E inc bx;BX 寄存器加 1 指向下一个字符 09FE:011F l

29、oop 118;返回 0118H 处继续处理 BX 所指向的新字符 09FE:0121 mov ah,01;选择 DOS API 的 01H 功能 09FE:0123 int 21;调用 21H 中断等待键盘输入 09FE:0125 mov bl,al;保存键盘输入字符的 ASCII 码 09FE:0127 mov dl,0d;DL 寄存器置入回车符的 ASCII 码 0DH 09FE:0129 mov ah,02;选择 DOS API 的 02H 功能 09FE:012B int 21;调用 21H 中断显示回车符 09FE:012D mov dl,0a;DL 寄存器置入换行符的 ASCII

30、 码 0AH 09FE:012F int 21;调用 21H 中断显示换行符 09FE:0131 mov dl,bl;DL 寄存器置入刚刚输入的字符 09FE:0133 int 21;调用 21H 中断显示刚刚输入的字符 09FE:0135 int 20;调用 20H 中断结束程序,返回 DOS 09FE:0137 Enter 程序例 PROG3 给出了这两个功能调用的应用实例。注意每行指令的后面都有一个带 “;”的说明性文字,这些文字的作用是使程序比较易懂,大家在上机输入程序时不能输入 这些文字。 程序中出现了一条新的指令INC: 助记符:INC(Increase) 用途:将寄存器或存储器中

31、的数据加 1 格式:INC 寄存器 INC 存储单元 执行:相应寄存器或存储器中的数据加了 1 此指令的功能和前面所讨论的 DEC 正相反,需要对照记忆。 这个程序可以把内存中的一串文字显示在屏幕上,这个程序应用了一些新的技术: 我们使用了 DEBUG 的“E“(EDIT)命令将文字串放入内存; 程序中使用了“间接寻址“技术。 “E“(EDIT)命令具有“编辑内存“的功能,它的用法很简单,在提示符后键入“E 段: 偏移Enter“即可。DEBUG 会把指定内存单元的内容显示在屏幕上,同时等待输入新数据, 如果只修改一字节内容,则在输入数据后打回车;若要修改相临的下一字节内容,则在输入 数据后打

32、空格,待把所有数据修改完后再打回车结束此命令。注意如果只输入偏移地址,则 DEBUG 会默认 DS 为段地址。 我们再编写汇编语言程序时, 有时需要把内存中的数据取到寄存器中加以处理, 在这种 情况下我们应该把指令写成“MOV寄存器,偏移地址“的形式。CPU 执行这个指令时就会 把内存中“DS:偏移地址“处所储存的数据取到指定寄存器中。 提醒大家注意一点:CPU 的引用的段地址默认来自 DS 寄存器,当然也可以明确指定一 个段寄存器作为段地址来源,用 DEBUG 编程时指令要写成两行: *:*ES:Enter(指定 ES 为段寄存器,也可指定其它段寄存器) *:*MOV寄存器,偏移地址Ente

33、r CPU 执行此指令时就会把内存中 ES:偏移地址处储存的数据取到指定寄存器中。 偏移地址两侧的“ “是必不可少的。它表示我们要装入寄存器的数据是储存在这个地 址处的数据,而不是这个地址本身。我们用 DEBUG 做个实验: C:ASMDEBUGEnter -d0 f注意这两个字节 09FE:0000CD 20 FF 9F 00 9A EE FE-1D F0 4F 03 68 04 8A 03. O.h. -a100Enter 09FE:0100 mov ax,0 09FE:0103 mov ax,0 09FE:0106 Enter -r 注意“DS:0000“这个存储单元 AX=0000 B

34、X=0000 CX=0000 DX=0000 SP=FFEE BP=0000 SI=0000 DI=0000 DS=09FE ES=09FE SS=09FE CS=09FE IP=0100 NV UP EI PL NZ NA PO NC 09FE:0100 A10000MOV AX,0000DS:0000=20CD 通过前面讨论的内容,我们现在就可以推知这条指令执行完后 AX 寄存器的数据。 对于学习过 C 语言程序设计技术的朋友来说,“间接寻址“并非是什么新鲜东西,还 记得“指针“的概念吗? -t注意 AX 寄存器的变化 AX=20CD BX=0000 CX=0000 DX=0000 SP=

35、FFEE BP=0000 SI=0000 DI=0000 DS=09FE ES=09FE SS=09FE CS=09FE IP=0103 NV UP EI PL NZ NA PO NC 09FE:0103 B80000MOV AX,0000 执行指令之后 AX 寄存器是 20CD,这个数来自于存储器。 -t注意 AX 寄存器的变化 AX=0000BX=0000CX=0000DX=0000SP=FFEEBP=0000SI=0000DI=0000 DS=09FEES=09FESS=09FECS=09FEIP=0103 NV UP EI PL NZ NA PO NC 09FE:0106 BAA682

36、MOV DX,82A6 指令“MOVAX,0000“中的 0000 没有加“,结果这个 0000 被送入了 AX 寄存器。 这个实验很好地说明了“ “的作用。回忆我们上一章所讲的“寻址方式“的概念,自然 会明白这里又出现一种新的寻址方式,我们把这种寻址方式称为“直接寻址“。 这种寻址方式适合从内存中取得单一的数据。 设想如果我们需要从内存中连续取得多个 数据,那么用直接寻址方式就需要写出很多指令,这是很不方便的。于是便引出了“间接寻 址“的概念。 间接寻址与直接寻址的区别就在于间接寻址方式中的偏移地址并不出现在“ “中,而 是放在一个寄存器中,而这个寄存器被“ “括了起来。在程序 PROG3

37、中,指令 MOV BX, 102 把那串文字的“首地址 0102H“放入 BX 寄存器,那么指令 MOV DL,BX的含义也就很清 楚了,当 BX 寄存器为 102H 时,此指令等同于 MOVDL,102,它执行的结果将使 DL 中出 现“P“的 ASCII 码50H,当 BX 寄存器变成 103H 时,此指令就相当于 MOVDL,103, 它执行的结果将使 DL 中出现“r“。 和直接寻址相比,间接寻址的优势在于置入寄存器中的地址可以通过运算加以改变。 我 们可以把含有地址的寄存器想象成一个“指针“, 它指向内存中的某一个单元, 若我们改变了 这个寄存器中的数据,则这个指针就会指向其它单元,

38、配以“ “,我们就可以很方便地 取得内存中任意位置的数据。 除此之外,PROG3 中还有一些值得注意的东西: 用 02 功能输出 0DH、0AH 时,屏幕上并未出现对应的 ASCII 字符。原因在于 0DH、 0AH 是控制码, 02 功能将它们解释成“回车“和“换行“, 输出这两个控制码将使光标移到下一 行的最左端,所以第二个“a“打印在第二行。 此程序的缺点是仅能输出 16 个字母的文字串,不能输出任意长度的文字串。PROG3 A 和 PROG3B 对这个缺点作了改进 PROG3-A -a100Enter 09FE:0100 jmp 111;跳过字符串数据区 09FE:0102 dbHel

39、lo,World!,0d,0a,0;定义一个字符串,结尾为 0 09FE:0111 mov bx,0;初始化 BX 寄存器为 0 09FE:0114 mov dl,bx+102;DL 寄存器置入字符 H 的 ASCII 码 09FE:0118 cmp dl,0;取到的字符是 0 码 09FE:011B jz 124;如果是 0,说明已到字符串结尾,此时 转 0124H 09FE:011D mov ah,2;未到字符串结尾,则选择 DOS API 的 02H 功能 09FE:011F int 21;调用 21H 中断,将 DL 中的字符显示出来 09FE:0121 inc bx;BX 寄存器指向

40、下一个字符 09FE:0122 jmp 114;返回 0114H 继续处理下一个字符 09FE:0124 int 20;结束处理,返回 DOS 09FE:0126 Enter PROG3A 中出现了一些新的“面孔“,下面分别引见给大家: (1)伪指令 当使用 DEBUG 编制汇编程序时, 摆在程序员面前的不仅仅是需要由程序控制的计算机硬 件,同时还有为我们提供服务的软件-DEBUG。所以我们通过键盘输入的不但有控制硬件动 作的“指令“(MOV、JMP) ,还有要求 DEBUG 给与帮助的“命令“(A、RBX) 。而“伪指令“恰好是 “指令“与“命令“的结合体。 从应用形式上看,它是和指令写在一

41、起的,而不象 DEBUG 的命令那样打在提示符后; 然 而它的作用却和“命令“一样,要求 DEBUG 为我们做事情。也就是说 CPU 并不真正执行它, 而 执行它的是 DEBUG。 现在我们来讨论出现在程序中的第一个伪指令DB。 DB(Define Byte)的作用是命令 DEBUG 将给出的数据放入内存中。数据有两种形式 字符和数字, 如果我们需要 DEBUG 把一些字符的 ASCII 码放入内存, 则可以在这些字符两 侧加上单引号,置于 DB 之后,如 DB ABCD;如果需要 DEBUG 把一些 16 进制数字放入内存, 则只需在 DB 后打出这些数字即可,如 DB A,42,43,44

42、。要注意的是字符与数字,数字 与数字之间的逗号不可丢掉。 所有数据在内存中都是连续存放的,DB 前面的逻辑地址(09FE:0102)就是这一 串数据的“首地址“。DB 后面指令的逻辑地址由 DEBUG 自动算出。 (2)数据比较指令 在 PROG3A 中,我们用数字 0 表示字符串结束,因此在输出字符时,只要判断取到 DL 中的数据是否为 0,即可知道是否已经输出了所有的字符。程序中使用了两条指令完成判断 工作,第一条是: 助记符:CMP(Compare) 用途:比较两个数据的大小 格式:CMP寄存器,立即数 CMP寄存器,寄存器 CMP寄存器,存储单元 CMP存储单元,寄存器 CMP存储单元

43、,立即数 执行:CPU 将两个待比较的数据做一次减法,结果不保留,但“后果“由标 志寄存器记录。 使用 CMP 指令时要注意这样一点, 就是相比较的两个数据位宽要一致, 即 8 位寄存器只 能和 8 位立即数或寄存器相比较, 也只能和存储器中的一个字节数据相比, 下面这样的用法 是不对的-“CMP AL,0FFFH“、“CMP AH,BX“ CPU 执行 CMP 指令的实际动作是用左边的数据减右边的数据, 但减的结果并不保留在左 边的寄存器或存储器中,如果相比较的两个数据相等,那么“相减“后结果必为 0,所以相等 的两个数比较, 将使 ZF 标志置 1。 要想判断两个数据是否相等, 只需在 C

44、MP 指令后用 JZ/JNZ 指令根据 ZF 的状态进行相应转移即可。 为便于记忆,我们给 JZ/JNZ 指令赋与了一个新的写法-JE/JNE(Jump if Equal/Not Equal) 。因此指令“JZ 124“也可写成“JE 124“。 (3)间接寻址的第二种形式 前面我们讨论过用 BX 寄存器作指针的一种间接寻址方式,用那种寻址方式时我们要把 字符串的首地址直接放入 BX 寄存器以建立指针。而在 PROG3A 中,我们在 BX 寄存器中装 入的是每一个数据相对首地址的偏移量。 在取数据时用这个偏移量加上字符串的首地址作为 数据的实际偏移地址。这种寻址方式称为“寄存器相对寻址方式“(

45、Register relative addressing) 。 这两个程序都是用 BX 作指针,能否用其它通用寄存器呢?注意对于 8086/88CPU 而言, 可做间接寻址的通用寄存器只有 BX,其它三个通用寄存器无此功能。我们后面说到“串处理 “时会介绍更多的可做间接寻址的寄存器,但它们都不是通用寄存器了。 PROG3-B -a100Enter 09FE:0100 jmp 111;跳过字符串数据区 09FE:0102 dbHello,World!,0d,0a,$;定义一个字符串,结尾为“$“ 09FE:0111 mov dx,102;DX 寄存器指向字符串首地址 09FE:0114 mov

46、ah,9;选择 DOS API 的 09H 功能 09FE:0116 int 21;调用 21H 中断显示字符串 09FE:0118 int 20;调用 20H 中断结束程序,返回 DOS 09FE:011A Enter PROG3B 编写得非常短小,但却和 PROG3A 有相同的效果,原因在于这个程序使用了 操作系统提供的一个极常用的功能09H(显示字符串) 。 功能号:09H 用途:显示字符串 参数:AH=09H DX=字符串的首地址 调用:INT 21H 返回:无 09 功能要求字符串必须以$结尾。 调用此功能前应将字符串的首地址放入 DX 寄存器, 然后执行 INT 21H 即可。此功

47、能同样解释 0DH,0AH 等控制码。 3 33 3 子程序子程序 操作系统提供了大量的功能为我们编程时调用, 但是仅仅靠这些功能还不能解决所有的 问题,比如说我们想把 AL 寄存器中的数据以二进制的形式显示在屏幕上,这样的功能操作 系统可没有提供,解决这样的问题就只能“自力更生“了。 同时也可以发现, 有一些问题并非仅出现在一个程序中, 可能在编制不同程序时都要遇 到同样的问题。 解决这些具有普遍性的问题的最好方法末过于单独编制解决特定问题的小段 程序并保留起来,在需要时随时将这些小程序加入我们的程序中。所谓的子程序,也就是这 样一些供别的程序调用且能解决一些较普遍问题的小程序。 PROG4

48、 -a100Enter 0F92:0100 mov ah,1;选择 DOS API 的 01H 功能 0F92:0102 int 21;调用 21H 中断等待键盘输入 0F92:0104 mov cx,8;需要处理 8 个数位 0F92:0107 mov bh,80;BH 寄存器置入 10000000B 0F92:0109 mov dh,al;DH 寄存器保存输入字符的 ASCII 码副本 0F92:010B mov dl,30;DL 寄存置入字符 0 的 ASCII 码 30H 0F92:010D and al,bh;将输入字符的 ASCII 码与 10000000B 相与,保留最左一位 0F92:010F jz113;如最左位是 0,则转去 0113H 继续执行 0F92:0111 mov dl,31;若最左位是 1,则 DL 寄存器置入字符 1 的 ASCII 码 31H 0F92:0113 mov ah,2;选择 DOS API 的 02H 功能 0F92:0115 int 21;调用 21H 中断显示 DL 寄存器中的字符 0F92:0117 mov al,dh;AL 寄存器取回输入字符的副本 0F92:0119 shr bh,1;将 BH 最左位的 1 右移一位 0F92:011B loop 10b;返回

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 其他


经营许可证编号:宁ICP备18001539号-1