小甲鱼零基础汇编语言学习笔记第五章之[BX]和loop指令

这一章主要介绍什么是[BX]以及loop(循环)指令怎么使用,loop和[BX]又怎么样相结合,段前缀又是什么鬼,以及如何使用段前缀。

1、[BX]的概念

[BX]和[0]类似,[0]表示内存单元的偏移地址是0。要完整描述一个内存单元,需要两种信息:内存单元的地址,内存单元的长度(类型)。[BX]同样也表示一个内存单元,它的偏移地址在bx中,比如指令:mov ax,[bx]。这里我们以一个程序为例:

 1      assume cs:codesg
 2      codesg segment
 3      start:    mov ax,2000H
 4                  mov ds,ax
 5                  mov al,[0]
 6                  mov bl,[1]
 7                  mov cl,[2]
 8                  mov dl,[3]
 9                  mov ax,4C00H
10                  int 21H
11       codesg ends
12       end start

进行编译,连接之后,使用debug来调试这个程序。对于编译器而言,[0]的中括号是不存在的,所以会出现把0放到al中,把1放到bl中。所以,我们要想偏移地址为某个数,所对应的内存单元传递到寄存器当中,就需要进行两步操作:

mov bx,0

mov ax,[bx]

这样做的话,就可以实现将偏移地址为0对应的内存单元的值传递给寄存器了。

2、loop

这是一个做循环操作的指令,指令格式:loop 标号。

CPU执行loop指令时,要进行两个步骤的操作:

(1)(cx)=(cx)-1(这里的cx放在一个括号里面,意思是cx的值)

(2)判断cx是不是为零,不为零的话转到标号出执行程序。

所以,loop和cx是有直接联系的,即cx的值影响着loop的执行结果。所以cx的值也就是指的是loop的循环次数,我们可以想到高级语言中,使用循环时(比如for循环),我们都会定义一个变量,来作为循环的条件,这个变量的值也就是循环的次数。

现在我们来看用汇编来计算2^2结果的代码:

1 assume cs:code
2 code segment
3        mov ax,2
4        add ax,ax
5
6        mov ax,4c00h
7        int 21h
8 code ends
9 end

计算2^3也是类似的,代码如下:

 1 assume cs:code
 2 code segment
 3        mov ax,2
 4        add ax,ax
 5        add ax,ax
 6
 7        mov ax,4c00h
 8        int 21h
 9 code ends
10 end
11    

那么计算2^12,我们就要使用循环来做了,不然重复的代码太多,这个时候我们就要使用到loop指令了,代码如下:

 1 assume cs:code
 2 code segment
 3        mov ax,2
 4        mov cx,11
 5 s:add ax,ax
 6        loop s
 7
 8        mov ax,4c00h
 9        int 21h
10 code ends
11 end

上面的代码中,s就是标号,这个标号可以是任何你自己定义的内容,实际上s标明了一个地址,这个地址处有一个指令:add ax,ax。cx我们定义的值为11,因为要算2^12,所以要循环11次。循环时,cx会减1,循环一次减一次,直到cx为0,停止循环。我们可以使用debug调试编译连接好的程序:

从上面的内容,我们可以得到cx和loop指令互相配合的三个关键点:

(1)、在cx中存放循环次数

(2)、loop指令标号所标识的地址要在前面

(3)、要循环执行的程序段,写在标号和loop指令之间。

3、loop和[bx]的联合使用

我们先从几个问题入手:计算ffff:0~ffff:b单元中的数据的和,结果存储在dx中。

(1)、运算后的结果是否会超出dx所能存储的值的范围?

ffff:0~ffff:b内存单元中的数据是字节型数据,范围是0~256,12个这样的数据相加,结果是不会比65535更大的,因此dx中是可以存储下的。

(2)、能够将ffff:0~ffff:b直接累加到dx当中呢?

不行,因为ffff:0~ffff:b中的数据是8位的,不能直接加到16位寄存器dx中。

(3)、能否将ffff:0~ffff:b中的数据累加到dl中,并设置(dh)=0,从而实现累加到dx中呢?

不行,dl是一个8位寄存器,能容纳的数据的范围在0~255之间,ffff:0~ffff:b中的数据也是8位的,将12个8位的数据累加到一个8位寄存器中,可能会出现进位丢失的情况。

(4)、那么到底应该怎样将这段内存单元中的数据的和存储到dx中呢?

就是使用一个16位寄存器来做中介,将内存单元中的8位数据赋值到一个16位寄存器ax中,再将这个ax中的数据加到dx上,从而使得运算对象的类型能够的到匹配(ax,dx都是16进制)并且结果不会越界。

因为这里要进行12次的相加,因此要使用到loop指令进行循环相加的操作,具体代码如下:

 1 assume cs:codeseg
 2 codeseg segment
 3
 4       mov ax,0ffffh
 5       mov ds,ax          ;初始化ds:bx 指向ffff:0内存单元
 6       mov bx,0
 7
 8       mov dx,0            ;初始化累加寄存器dx
 9       mov cx,12          ;初始化循环计数寄存器cx
10
11 s:  mov al,[bx]
12      mov ah,0
13      add dx,ax     ;间接向dx中加上((ds)*16+(bx))单元中的数值
14      inc bx           ;ds:bx指向下一个单元
15      loop s
16
17      mov ax,4c00h
18      int 21h
19
20 codeseg ends
21 end

4、Debug的G命令和P命令

当我们对程序进行调试时,循环的次数很大时,我们不可能一次一次的执行t命令,因此要使用G命令或者P命令。G 循环结束的偏移地址,这样就可以一次执行完循环的内容。P,直接执行完循环的内容。

5、一段安全的空间

当我们要直接向内存中写入内容时,这段内存空间不应该存放系统或者其它程序的数据或者代码,否则写入会导致操作系统发生错误,

DOS方式下,一般情况,0:200~0:2FF空间汇中没有存放系统或者其它程序的数据或者代码,以后我们写入内存就直接写到这段空间当中。

6、段前缀

指令 mov ax,[bx]中,内存单元的偏移地址由bx给出,而段地址默认在ds中,我们可以在访问内存单元的指令中,显示的给出内存单元的段地址所在的段寄存器,比如,mov ax,ds:[bx],这里的段寄存器可以是ds,cs,es,ss,这些指令中的段寄存器在汇编语言中也被称为段前缀。

7、段前缀的使用

首先我们考虑一个问题:将ffff:0~ffff:b中的数据复制到0:200~0:20b单元中。

分析:

(1)0:200~0:20b也就是0020:0~0020:b,它们描述的是同一个内存空间。

(2)在循环中,源始单元 ffff:X 和目标单元 0200:X 的偏移地址X是变量,我们使用一个寄存器bx来存放

代码如下:

 1 assume cs:codeseg
 2 codeseg segment
 3
 4       mov bx,0   ;偏移地址从0开始
 5       mov cx,12 ;循环次数为12次
 6
 7 s:   mov ax,0ffffh
 8       mov ds,ax ;ds的值为0ffffh
 9       mov dl,[bx] ;(dl)=((ds)*16+bx)将ffff:bx中的数据传入到dl中
10
11       mov ax,0020h
12       mov ds,ax  ;ds的值为0020h
13       mov [bx],dl ;((ds)*16+bx)=(dl)将dl中的数据传入到0020:bx中
14
15       inc bx ;bx自增1
16       loop s
17
18       mov ax,4c00h
19       int 21h
20
21 codeseg ends
22 end

  当然,我们上面的代码还有需要改进的地方,从上面的代码可以看到,我们重复设置了两次ds的值,这样做没有错,只是在循环的次数比较小的情况下,一旦循环的次数增到很大时,CPU就要进行很多次重复的操作,这样效率也就比较低了。因此,我们可以对代码进行相应的改进:

 1 assume cs:codeseg
 2 codeseg segment
 3       mov ax,0ffffh
 4       mov ds,ax         ;(ds)=0ffffh
 5
 6       mov ax,0020h
 7       mov es,ax          ;(es)=0020h
 8
 9       mov bx,0   ;(bx)=0,这个时候,ds:bx指向ffff:0,es:bx指0020:0
10       mov cx,12
11
12 s:
13       mov dl,[bx] ;(dl)=((ds)*16+(bx)),将ffff:bx中的数据传入到dl
14       mov es:[bx],dl ;((es)*16+(bx))=(dl),将dl中的数据传入0020:bx
15
16       inc bx
17       loop s
18
19       mov ax,4c00h
20       int 21h
21
22 codeseg ends
23 end

时间: 2024-10-15 19:45:08

小甲鱼零基础汇编语言学习笔记第五章之[BX]和loop指令的相关文章

小甲鱼零基础汇编语言学习笔记第六章之包含多个段的程序

在前面的几个章节中,我们的程序都是只有一个代码段,本章我们开始学习如何编写包含多个段的程序. 1.在代码段中使用数据 首先考虑这样一个问题,计算以下8个数据的和,结果存放在ax寄存器中: 0123H,0456H,0789H,0abcH,0defH,0fedH,0cbaH,0987H 在前面的课程中,我们都是累加某些内存单元中的数据,并不关心数据本身,可现在我们要累加就是已经给定了数值的数据. 代码如下: 1 assume cs:codesg 2 codesg segment 3 dw 0123H

小甲鱼零基础汇编语言学习笔记第四章之第一个汇编程序

1.使用工具 代码编写:NotePad++,代码编译器:MASM 2.一个源程序从写出到执行的过程 第一步,使用文本编辑器,用汇编语言编写汇编源程序,这一步产生一个存储源程序的文本文件. 第二步,对源程序进行编译,连接.使用MASM.exe对源程序文件进行编译,产生目标文件,再使用LINK.exe对目标文件进行连接,生成可在操作系统中直接运行的可执行文件.可执行文件包括两个部分,程序(从源程序的汇编指令翻译过来的机器码)和数据(源程序中定义的数据):相关的描述信息 (程序有多大,要占多少内存空间

小甲鱼零基础python课后题 P22 021函数:递归是神马

0.递归在编程上的形式是如何表现的呢? 答:在编程上,递归表现为函数调用本身这么一个行为. 1.递归必须满足哪两个基本条件? 答:1函数调用自己. 2有正确的返回条件 2.思考一下,按照递归的特性,在编程中有没有不得不使用递归的情况? 答:不知道呢,应该就是小甲鱼课上说的那个三角形 3.用递归去计算阶乘问题或奜波那契数列是很糟糕的算法,你知道为什么吗? 答:效率太低,每次都需要调用自己,占用空间. 4.请聊一聊递归的优缺点.(想怎么写就怎么写按自己理解的来) 答:优点,代码简洁 缺点 ,效率低占

javascript高级程序设计 学习笔记 第五章 上

第五章 引用类型的值(对象)是引用类型的一个实例.在 ECMAScript 中,引用类型是一种数据结构, 用于将数据和功能组织在一起.它也常被称为类,但这种称呼并不妥当.尽管 ECMAScript 从技术上讲是一门面向对象的语言,但它不具备传统的面向对象语言所支持的类和接口等基本结构.引用类型有时候也被称为对象定义,因为它们描述的是一类对象所具有的属性和方法. 对象是某个特定引用类型的实例.新对象是使用 new 操作符后跟一个构造函数来创建的. 构造函数本身就是一个函数,只不过该函数是出于创建新

《Spring实战》学习笔记-第五章:构建Spring web应用

之前一直在看<Spring实战>第三版,看到第五章时发现很多东西已经过时被废弃了,于是现在开始读<Spring实战>第四版了,章节安排与之前不同了,里面应用的应该是最新的技术. 本章中,将会接触到Spring MVC基础,以及如何编写控制器来处理web请求,如何通明地绑定请求参数到业务对象上,同时还可以提供数据校验和错误处理的功能. Spring MVC初探 跟踪Spring MVC请求 在请求离开浏览器时,会带有用户所请求内容的信息,例如请求的URL.用户提交的表单信息. 请求旅

小甲鱼零基础入门PYTHON

 000.愉快的开始 00:17:37 ☆  001.我和Python的第一次亲密接触 00:13:26 ★  002.用Python设计第一个游戏 00:24:00 ★  003.小插曲之变量和字符串 00:19:04 ★  004.改进我们的小游戏 00:26:31 ★  005.闲聊之Python的数据类型 00:18:41 ★  006.Pyhon之常用操作符 00:16:31    007.了不起的分支和循环 00:16:30    008.了不起的分支和循环2 00:12:09  

小甲鱼零基础python课后题 P20 019函数:我的地盘听我的

测试题 0.如果希望在函数中修改全局变量的值,应该使用什么关键字? 答:globe 1.在嵌套函数中,如果希望在内部函数修改外部函数的局部变量,应该使用什么关键字? 答:nonlocal 2.python的函数可以嵌套,但要注意访问的作用域问题哦,请问以下代码存在什么问题呢? def outside(): print("I am outside") def inside(): print("I am inside") inside() 答:inside() 不是外部

&lt;&lt;Python基础教程&gt;&gt;学习笔记 | 第10章 | 充电时刻

第10章 | 充电时刻 本章主要介绍模块及其工作机制 ------ 模块 >>> import math >>> math.sin(0) 0.0 模块是程序 一个简单的模块 #hello.py print ("Hello,World!") >>> import hello Traceback (most recent call last): File "<pyshell#56>", line 1, i

&lt;&lt;Python基础教程&gt;&gt;学习笔记 | 第09章 | 魔法方法、属性和迭代器

这一章,有点抽象,看着有点蛋疼! 双下划线__future__或单下划线有特殊含义,在Python中,这些名字的集合称为魔法方法:最重要的是__init__和一些处理访问对象的方法,这些方法允许你创建自己的序列或者是映射. ------ 准备工作: 将__metaclass__=type放在模块的最开始位置,以确保类时最新式的.考虑下面两个类 class NewStyle(object): more_code_here class OldStyle: more_code_here 如果文件以__