汇编教程9:loop循环
教程写到这里的时候,已经接到了很多朋友的回复了,很多人都在鼓励我,在这里感谢大家了,当然有人觉得我的教程写的还行,有的人却不以为然、不过都没有关系,如果你觉得好,请多宣传下,让更多的人加入学习汇编的行列,如果你觉得写的不好,我希望你可以给我指出,我当万分感谢。当然,如果有写错的地方还忘高手提点。
上一节 汇编教程8:hello world 我们讲到了怎么在一个编程环境中写代码,相信你也试了很多指令。一定想着要深入一些再了解点东西,是的。在高级语言里面,我们学了“hello World“以后肯定就得学循环是不,什么while….什么的。。汇编语言中也有循环。我们一起来了解下。
在回忆高级语言的循环、在循环里面我们都会有一个循环判断语言,判断是否超过了我们预想的值,那么在汇编中是否有这样的功能呢??当然是有的,它就是[bx],[bx]是什么呢??在前面我们将到过mov ax, [ 0 ]这样的语句,我们知道[0]表示偏移地址,段地址在ds中,数据长度则靠其他的语句指定、比如ax是2个字节,所以长度就是2个字节。
同样的[bx]是比较简单的,mov ax, [bx]这样的语句是怎么回事呢?? 那么这条语句的意思是,把[bx]内存单元的数据赋给ax,长度为2个字节。那么如果定位[bx]呢,道理是一样的,偏移地址在[bx]中,段地址在ds中。
你可能已经想到了有了[bx],就可以方便我们实现循环了,是的我要恭喜你,事实上和你想的是一样的。不过在介绍汇编的循环之前我还要告诉你一条指令inc,inc是什么呢?在高级语言里面我们经常都用这样的指令A++,最经典的莫过于C++了,inc就是表示加1的意思,比如现在ax=1那么指令:
inc ax
则ax就等于2了,非常简单吧。
那么现在我们可以做到让[bx]的值加1了,现在还缺少什么呢?当然是我们的主角了,loop,loop在汇编中通常是用来实现循环功能的,loop的指令格式也简单:
loop 标号
你现在肯定在想,这loop执行的时候怎么来判断在哪里停下来呢?这个嘛….告诉你一点loop指令执行的细节你就明白了,loop指令执行的时候会做这两步操作:
1. cx = cx -1
2.判断cx的值,如果不是0则继续跳到标号处,如果是0就跳出循环。
说了很多,估计有不少人被蒙住了,不过没有关系,我举个例子你一看就明白、比如现在我们计算 2 ^ 15(也就是2×2….循环乘14次):
- assume cs:codesg
- codesg segment
- start:
- mov ax, 2
- mov cx, 14
- s:
- add ax, ax
- loop s
- mov ax, 4c00h
- int 21h
- codesg ends
- end start
代码应该是比较好理解的,在start处我们先给ax赋值为2,cx赋值为14表示循环14次,然后有一个s:标号,这个标号是伪指令,汇编器会帮我们转换成实际的地址,这个我们不管,然后add ax,ax表示ax+2,然后loop s我们回忆前面说的,执行loop的时候首先会让cx-1,然后看看cx是不是等于0不少就调到s:处继续执行,如果是等于0的话就跳出循环,这里是跳到mov ax, 4c00h处。总结起来应该是这样:
mov cx 循环次数
s:
循环执行代码
loop s
那么差不多loop就这么多内容了,我们在用debug调试的时候如果遇到循环的话当然不能够用-T命令一直执行,那些都是重复的代码对吧,那么有什么指令可以跳过呢?那么就要用到-P命令了,还记得前面我和大家说了为什么要在debug中执行到int 21的时候要用-P命令吗?为什么用P呢?其实p的意思和-T是差不多的,-T被称为:单步跟踪命令、也就是一步一步的跟踪,-P命令则是:单步执行命令,单步执行命令p会按照一般程序执行,而不会进入子程序或者循环中。所以用命令P的话我们就可以跳过一些循环或者CALL指令了,而在遇到int21指令的时候是有很多乱七八糟的命令的,我们就直接跳过了。
同时在这里我还要提醒你一下本来早就该提醒了,在汇编源程序中,数据不能够以字母开头那么如果数据本来就是字母开头的比如ff00怎么办呢?是的你已经猜到了,前面加0就行了,这个个小问题,记得就成。
那么在继续下面的胡扯之前我还有个善意的小提醒,我们以前用过mov ax, [0],这样的指令,如果我没有记错的话你应该只在debug中使用过,因为我还没有告诉你在MASM中使用呢,实际上你这样在汇编源程序中写是错误的,MASM的解释方法和debug有些不同,它会把mov ax, [0]解释称为mov ax 0。这和我们期望中的值应该是相差太多了!那该怎么办?也不难,就是在显示的给出段寄存器的地址,如写成这样:mov ax, ds:[0]这样就OK了。这个问题应该是比较简单的,只是平时注意这个问题就可以了。这个就是传说中的段前缀
loop应该就那么多内容了,只是平时在应用的时候经常遇到一些问题(这些问题在32位的计算器上,甚至64位还是存在的),比如说什么内存中的数据算下来超过16位啊也就是大于65535,又比如说AX中的数据要放到一个内存单元里面啊,很麻烦。。这在高级语言有点类似谈强制类型转换。你有可能觉得我是胡扯了,我给你举个例你就明白了,比如我们现在要计算1000:0-1000:9内存的值改怎么做呢?首先来分析下问题:
1分析下结果是否能够存储,1000:0-1000:9都是一个字节的一个字节最大存放255,255×9也不大于65535,然后我们再看
2.非常明显我们不能够用上面的方法来进行累加了,上面都是16位的寄存器,现在我们操作的是内存单元,也就是8位的寄存器,这是个麻烦!
3.既然上面的不成我们是否可以用一个8位的寄存器来存储计算的数据值呢?显然不行,因为我们的结果大于255。
总结起来啊,如果我们用16位的寄存器,那么8位的数据我们就照顾不到了,用8位的寄存器那么数据就丢失了,诶忠孝两难全啊,那么现在有我们应该怎么样才可以鱼和熊掌兼得呢?在你准备放弃汇编的时候:我要告诉你,虽然用汇编实现起来不那么方便但是我们还是可以做到鱼和熊掌兼得的!简单说就是用一个16位的寄存器做中介,我们将内存中的8位数据赋值到一个16位的寄存器中,再将这个寄存器的数据加到另外一个寄存器中,中转下,这样我们就可以实现,该实现的功能了,说了屁话一堆,还不如给点代码看看:
- ;----------------------------------------------------
- 一个用于实现在内存中1000:0-1000:9处的数据相加的例子;
- ;----------------------------------------------------
- assume cs:codesg
- codesg segment
- start:
- mov ax, 1000h
- mov ds, ax ;设置ds指向1000
- mov bx, 0 ;和ds配合指向1000:0
- mov dx, 0 ;初始化累加寄存器DX
- mov cx, 9 ;初始化循环寄存器CX=9
- s:
- mov al, ds:[bx] ;读取1000:0处的数据读到al中
- mov ah, 0 ;初始化ax寄存器的高位为0
- add dx, ax ;dx = ax + ax
- inc bx ;bx+1这样才可以循环的读取内存
- loop s
- mov ax, 4c00h
- int 21h
- codesg ends
- end start
这段代码应该算是比较长的了,如果你看不习惯建议你将它复制到Radasm上进行查看,也可以编译后运行下看看结果,我一直想找个软件可以将我在Radasm上写的代码转换成html的格式,语法加亮的,不知道有没有,如果你知道的话,可以给我个回复哦,谢谢拉。
这里我们就成功的用dx做最后的结果存放处,把al当成了跳板,成功的计算出了1000:0-1000:9的数据,代码就不多解释了,每行都有注释,应该是非常简单了,如果不明白多看两遍,把代码拿去多做下试验应该就OK咯,如果还不明白可以发Email:agan97@qq.com 原来那个电子邮箱也成。
一直没有给大家布置点作业哦,我觉得反正是吹牛而已,但是我发现原来读和写是多么的重要啊,你应该有所体会了,上面我们已经有代码从内存中读出数据了,那么现在作业是向内存中写入数据,当然需要要loop 和[bx]联合应用。
嗯,应该就是那么多了,好好练习下,还写几篇就进入win32了,win32和16位的相比这些东西要简单很多,因为32位有if,while指令,当然是伪指令,不过我们了解原理是非常有必要的,现在我们已经进入了攻坚战了,学习的课程是越来越难,希望可以坚持下去,还是前面的 浴火重生 的故事回忆下!