计算机系统学习
条件码寄存器
书中列出了四种常用的寄存器,它们的名字与作用分别如下所述。
CF:进位标志寄存器,它记录无符号操作的溢出,当溢出时会被设为1。
ZF:零标志寄存器,当计算结果为0时将会被设为1。
SF:符号标志寄存器,当计算结果为负数时会被设为1。
OF:溢出标志寄存器,当计算结果导致了补码溢出时,会被设为1。
从上面寄存器的简单说明可以看出,ZF和SF可以判断结果的符号,而CF和OF可以判断无符号和补码的溢出。而我们平时使用的高级程序语言,就仅仅靠这四个寄存器,就可以演化出千变万化的流程控制。
几乎所有的算术与逻辑指令都会改变条件码寄存器的值,不过改变的前提是触发了条件码寄存器的条件。比如对于subl %edx,%eax这个减法指令,假设%edx和%eax寄存器的值都为0x10,则两者相减的结果为0,此时ZF寄存器将会被自动设为1。对于其它的指令运算,都是类似的,会根据结果的不同而设置不同的条件码寄存器。
访问条件码
对于普通寄存器来讲,使用的时候一般是直接读取它的值,而对于条件码寄存器来说,则不一定非要读取它的值才能使用。对于条件码寄存器来讲,有三种使用方式,都可以让它发挥作用。
1、可以根据条件码寄存器的某个组合,将一个字节设置为0或1,其实这个就相当于读值。
2、可以直接条件跳转到程序的某个其它的部分。
3、可以有条件的传送数据。
这里面第一种方式其实就是普通寄存器的用法,直接读取条件码寄存器的值,然后进行使用。对于第二和第三种来说,就不是这样了,它们不会显示的读取条件码寄存器的值,而是直接使用。
本节最难的地方,就在于如何将条件码寄存器的组合与条件联系起来。只要理解了这一点,那么条件码寄存器就算是基本掌握了。对于所有的组合都基于a-b这样的前提,也就是说条件码寄存器的值是经过了一个减运算设置后的值。例如,对于e->ZF这样的形式,代表的意思是字母e作为后缀时,则以ZF的值为1视为条件成立。
组合 | 功能 |
---|---|
e->ZF | 相等 |
ne->~ZF | 不相等 |
s->SF | 负数 |
ns->~SF | 非负数 |
l->SF^OF | 有符号的小于 |
le->(SF^OF)或ZF | 有符号的小于等于 |
g->~(SF^OF)&~ZF | 有符号的大于 |
ge->~(SF^OF) | 有符号的大于等于 |
b->CF | 无符号的小于 |
be->CF或ZF | 无符号的小于等于 |
a->~CF&~ZF | 无符号的大于 |
ae->~CF | 无符号的大于等于 |
跳转指令
这个指令是我们程序实现流程控制的关键指令,它可以直接将程序跳转到指定的位置,又或者根据条件码寄存器的组合进行条件跳转。总的来说,跳转指令的地址编码一般有两种,第一种是基于PC的,第二种则是绝对地址。基于PC(程序计数器)是指给出一个偏移量,这个偏移量基于当前下一条指令的地址,也就是PC当中的值,这是一种最常用的方式。
1 | 3: eb 03 jmp 8 <loop+0x8> |
对于3处字节编码的第二个字节为03,把它加上0x5,就是下一条指令的地址。而对于b处字节编码的第二个字节为f8,补码转换为10进制-8,加上0xd,得到0x5.
跳转指令一个最大的应用就是可以实现条件分支,一种是通过条件控制来实现,简单理解是先判断再执行;另一种是通过条件传送来实现,先执行再判断。这两种实现方式各有利弊,要根据实际的应用来判断使用哪一个更好。
循环指令
循环指令一般有三种do-while、while和for。
do-while
这个循环结构是先执行循环体的内容,然后再进行判断条件,成立继续循环,不成立就跳出。在汇编中可以使用cmp+label来模拟这个过程。
while
这个循环结构是先判断条件是否成立,然后才进入循环体。在汇编中一种是执行一个无条件跳转到循环结尾处,以此来执行初始的测试。另一种是先进行判断,然后就是do-while的过程。
for
for循环结构其实完全可以沿用while循环的方法,只需要把for的各个条件匹配到while中即可。
switch语句
我平时在程序中很少使用到switch分支语句,它适用于那种对变量进行很密集的判断,这个最好的应用大概就是分别指令集了吧。switch语句的实现很简单,可以用多重if-else来理解。
1 | movl 8(%ebp), %eax//取a |