1. 有符号数和无符号数
无符号数,因为没有符号位,所以只能表示一个正数。
有符号数,因为存在符号位,符号位如果是0的话,代表这是一个正数,符号位如果是1的话,代表这个数是一个负数。
我们可以用db伪指令来声明一些数字:
编译之后:
10D的十六进制表示就是0x0A,11D的十六进制表示就是0x0B,17D的十六进制表示就是0x11
但自从学习汇编语言,我们还没有用过负数,好像汇编语言里面根本就没有负数一样。为此,现在有一个十进制的负数-3d,要求转换成1个字节,并用十六进制来表示。
修改db伪指令:
db 10,11,17,-3
编译后的结果:
我们可以看到-3的十六进制数竟然是FD,那么这个FD到底是怎么来的呢?
首先十进制3的二进制是:0000 0011,因为十进制-3是负数,所以符号位是1,推导出-3的原码:1000 0011,我们需要根据原码转换成计算机表示的补码(关于反码,补码的计算规则相信大家应该还记得吧):
-3的反码就是:1111 1100
-3的补码就是:1111 1101
转换成十六进制就是:FD
那么问题来了,253D的十六进制表示也是FD,这意味着无论是253还是-3,汇编器统统都给转换成FD了,也就是说,FD既可以代表-3又可以代表253,那假如给你一个FD,那么你认为它代表什么呢?
看到上面这张图是不是很懵逼??? 其实这个问题放在几十年前,那时的计算机工程师面对这种情况同样也很懵逼。
对于1111 1101这个数,你既可以理解为-3,也可以理解为253。也就是说,在计算机里几乎所有的指令,既能操作无符号数,又能操作有符号数,并且结果完全都正确,怎么理解由你自己决定。
但总有例外,比如除法指令和乘法指令,div指令就只能完成无符号除法指令(Unsigned Divide),如果是完成有符号除法指令(Signed divide),就要使用idiv指令。这两个指令的使用方法是一样的,区别在于div是针对无符号数,而idiv是针对有符号数。
C语言同理,我们来看下面这段代码:
int main(void)
{
//对于数字12在C语言中,也有无符号,有符号数两种表示方法
int num1 = 12; //一般默认是有符号数,signed
unsigned int num2 = 12; //使用unsigned关键字明确指定为无符号数
return 0;
}
因此,在汇编语言中计算数字时,这个数字是有符号数还是无符号数由你自己决定,在C语言中也是同样的道理。
2. 标志寄存器
这一节我们要学习一个新的寄存器,叫做标志寄存器。关于标志寄存器,在16位的汇编语言中,这个寄存器叫做flag。在32位的汇编语言中,这个寄存器叫做eflag,即标志寄存器EFL。
标志寄存器EFL在计算机的作用是主要反映处理器的状态和ALU运算结果的某些特征。
比如CF这个标志位,翻译过来指的是(CarryFlag),当进行无符号运算的时候,如果产生进位和借位,CF就会变成1,否则变成0。
来看一个示例:
;al是一个8位的寄存器,最大只能存放1111 1111(0xFF),在这种情况下,如果再加1的话,就会产生溢出。
mov al,0xFE
inc al
inc al
执行第二条add指令时,寄存器ax里的内容就会溢出,而标志寄存器里的CF标志位就会置为1。
当把add指令换成inc指令,我们发现寄存器ax的内容溢出时,CF标志位并没有置1,至于原因,书上的解释是inc指令不会影响CF标志位。