codecamp

Assembly 控制结构

高级语言提供高级的控制结构(例如,if 和while语句)来控制执行的顺序。汇编语言并没有提供像这样的复杂控制结构。它使用声名狼藉的goto来替代,如果使用不恰当可能会导致非常复杂的代码。但是,它是能够写出结构化的汇编语言程序。基本的步骤是使用熟悉的高级语言控制结构来设计程序的逻辑,然后将这个设计翻译成恰当的汇编语言(就像一个编译器要做的一样)。


比较

控制结构决定做什么是基于数据的比较的。在汇编语言中,比较的结果储存在FLAGS寄存器中,以便以后使用。80x86提供CMP指令来执行比较操作。FLAGS寄存器根据CMP指令的两个操作数的不同来设置。具体的操作是相减,然后FLAGS根据结果来设置,但是结果是不在任何地方储存的。
如果你需要结果,可以使用SUB来代替CMP指令。

对于无符号整形,有两个标志位(在FLAGS寄存器里的位) 是非常重要的:零标志位(zero flag(ZF)) 和进位标志位(carryflag(CF)) 。如果比较的结果是0的话,零标志位将置成(1) 。进位标志位在减法中当作一个借位来使用。考虑这个比较:

cmp    vleft, vright

vleft - vright的差别被计算出来,然后相应地设置标志位。如果CMP执行后得到差别为0,即vleft = vright那么ZF就被置位了(也就是: 1),但是CF不被置位(也就是: 0)。如果vleft > vright,那么ZF就不被置位而且CF也不被置位(没有借位)。如果vleft < vright,那么ZF就不被置位,
而CF就被置位了(有借位)。

对于有符号整形,有三个标志位非常重要:零标志位(zero °ag (ZF)),溢出标志位(over°ow °ag(OF))和符号标志位(sign °ag (SF))。如果一个操作的结果上溢(下溢),那么溢出标志位将被置位。如果一个操作的结果为负数,那么符号标志位将被置位。如果vleft = vright,那么ZF将被置
位(正好与无符号整形一样)。如果vleft > vright,那么ZF不被设置,而且SF = OF。如果vleft < vright,那么ZF不被设置而且SF 6= OF。

不要忘记其它的指令同样会改变FLAGS寄存器,不仅仅CMP可以。

分支指令

分支指令可以将执行控制权转移到一个程序的任意一点上。换言之,它们像goto一样运作。有两种类型的分支:无条件的和有条件的。一个无条件的分支就跟goto一样,它总会产生分支。一个有条件分支可能也可能不产生分支,它取决于在FLAGS寄存器里的标志位。如果一个有条件分支没
有产生分支,控制权将传递到下一指令。

JMP (jump的简称)指令产生无条件分支。它唯一的参数通常是一个指向分支指向的指令的代码标号。汇编器和连接器将用指令的正确地址来替代这个标号。这又是一个乏味的操作数,通过这个,汇编器使得程序员的日子不好过。能认识到在JMP指令后的指令不会被执行,除非另一条分支指令
指向它,是非常重要的。

这儿有jump指令的几个变更形式:

SHORT 这个跳转类型局限在一小范围内。它仅仅可以在内存中向上或向下移动128字节。这个类型的好处是相对于其它的,它使用较少的内存。它使用一个有符号字节来储存跳转的位移。位移表示向前或向后移动的字节数(位移须加上EIP)。为了指定一个短跳转,需在JMP指令里的变量之前使用关键字SHORT。

NEAR 这个跳转类型是无条件和有条件分支的缺省类型,它可以用来跳到一段中的任意地方。事实上,80386支持两种类型的近跳转。其中一个的位移使用两个字节。它就允许你向上或向下移动32,000个字节。另一种类型的位移使用四个字节,当然它就允许你移动到代码段中的任意位置。四字节类型是386保护模式的缺省类型。两个字节类型可以通过在JMP指令里的变量之前放置关键字WORD来指定。

FAR 这个跳转类型允许控制转移到另一个代码段。在386保护模式下,这种事情是非常鲜见的。

简单条件分支

有效的代码标号遵守与数据变量一样的规则。代码标号通过在代码段里把它们放在它们标记的声明前面来定义它们。有一个冒号放在变量定义的地方的结尾处。这个冒号不是名字的一部分。条件分支有许多不同的指令。它们都使用一个代码标号作为它们唯一的操作数。最简单的就是看FLAGS寄存器里的一个标志位来决定是否要分支。看表2.3得到关于这些指令的列表。(PF是奇偶标志位(parity flag) ,它表示结果中的低8位1的位数值为奇数个或偶数个。)

下面的伪码:

if ( EAX == 0 )
  EBX = 1;
else
  EBX = 2;
可以写成汇编形式,如:

汇编

其它比较使用在表2.3里的条件分支并不是很容易。为了举例说明,考
虑下面的伪码:

if ( EAX >= 5 )
  EBX = 1;
else
  EBX = 2;

如果EAX大于或等于5,ZF可能被置位或不置位,而SF将等于OF。这是测试这些条件的汇编代码(假定EAX是有符号的):

测试

有符号和无符号指令比较

上面的代码使用起来非常不便。幸运的是,80x86提供了额外的分支指令使这种类型的测试条件更容易些。每个版本都分为有符号和无符号两种。表2.4展示了这些指令。等于或不等于分支(JE和JNE)对于有符号和无符号整形是相同的。(事实上,JE和JZ,JNE和JNZ基本上完全相同。) 每个其它的分支指令都有两个同义字。例如:看JL (jump less than)和JNGE(jump not greater than or equal to)。有相同的指令这是因为:

公式

无符号分支使用A代表大于而B代表小于,替换了L和G。

使用这些新的指令,上面的伪码可以更容易地翻译成汇编语言:

翻译


循环指令

80x86提供了几条专门为实现像for一样的循环而设计的指令。每一个这
样的指令带有一个代码标号作为它们唯一的操作数。

LOOP ECX自减,如果ECX 6= 0,分支到代码标号指向的地址

LOOPE, LOOPZ ECX自减(FLAGS寄存器没有被修改),如果ECX 6= 0
而且ZF = 1,则分支

LOOPNE, LOOPNZ ECX自减(FLAGS没有改变),如果ECX 6= 0 
而且ZF = 0,则分支

最后两个循环指令对于连续的查找循环是非常有用的。下面的伪码:
sum = 0;
for( i=10; i >0; i¡¡ )
   sum += i;

可以翻译在汇编语言,如:
实例
Assembly 扩充精度运算
标准控制结构如何应用到Assembly
温馨提示
下载编程狮App,免费阅读超1000+编程语言教程
取消
确定
目录

关闭

MIP.setData({ 'pageTheme' : getCookie('pageTheme') || {'day':true, 'night':false}, 'pageFontSize' : getCookie('pageFontSize') || 20 }); MIP.watch('pageTheme', function(newValue){ setCookie('pageTheme', JSON.stringify(newValue)) }); MIP.watch('pageFontSize', function(newValue){ setCookie('pageFontSize', newValue) }); function setCookie(name, value){ var days = 1; var exp = new Date(); exp.setTime(exp.getTime() + days*24*60*60*1000); document.cookie = name + '=' + value + ';expires=' + exp.toUTCString(); } function getCookie(name){ var reg = new RegExp('(^| )' + name + '=([^;]*)(;|$)'); return document.cookie.match(reg) ? JSON.parse(document.cookie.match(reg)[2]) : null; }