计算机系统
汇编入门
80386通用寄存器
- %ax :accumulate 累加寄存器
- %bx :base 基址寄存器
- %cx :count 计数寄存器
- %dx :divide 放除法的商和余数
- %eax : extend 扩展 表示32位的寄存器
%eax表示32位寄存器,%ax表示低16位,在低16位的寄存器中:%ah表示高八位,%al表示第八位
汇编指令
1005.s 立即数寻址
movl $8,%eax;
- $是立即数,表示8
- movl是移动的意思,将8赋值给%eax;
最终的运行结果如下
1006.s 寄存器寻址
- movl 用于传送32位的长子值
- movw 用于传送16位的子值
- movb 用于传送8位的子值
不能把movw的w改为l,因为%bx是16位的寄存器,如果改为l试图把32位的传给16位是错误的。可以写mov / movw
命令为 movw $0x5678,%dx ; 意思是传16个字节(w)的数据到寄存器dx里,结果如我们所愿,如果用%dh/%dl便会报错,因为%dh/%dl是8位的寄存器;同样,也不能movw $0x5678,%edx,因为edx是32位的,所以只能写32位的地址。如下图,所以要用对应的寄存器和对应位数的mov指令或只写mov去传递数据。
1007.s 绝对寻址(直接寻址)
movl 0x08048054,%exc,直接把这个内存地址赋给%exc。可以使用x/4bx 08048054来查看以8054起的四个地址内容。
内存之中既可以存放数据,也可以存放指令。如图,b9是指令,从后往前看08、04、80、55就是数据。
1008.s 间接寻址
movl (%ebx),%eax 和寄存器寻址的区别在于第一个寄存器套上了括号,表示不是把寄存器%ebx的值赋给%eax,而是寄存器存放的数据的地址存放的内容赋给%eax。
第一步,立即数寻址,把立即数08048054放到%ecx
第二步,查看08048054内存中存放的数据是什么,并将其传入16位的%ax中,得到结果0x8056,为什么是8056?因为56是低位,80是高位
1009.s 变址寻址
movl (%ebx,%edx),%eax 在1008的基础上,把括号内两个寄存器的值加起来,用这个内存地址去找里面的数据,然后把数据传给%eax。
1010.s 比例变址寻址
movl (%ebx,%ecx,0x2),%eax
%ebx为基址,%ecx与第三个参数(第三个参数只能为1,2,4,8,左移运算符)相乘并与基址相加,最后赋值给%eax。
1012.s 获取变量在内存的地址
leal 5(%edx, %edx, 2), %eax //假设%edx的值为x, 这行代码会将%eax的值设置为”3x+5″.
1013.s 栈操作指令
栈:先进后出
栈顶指针:保存在%esp寄存器中
压栈push出栈pop
栈是往低处生长的。
e0-4 =dc 栈顶指针往前偏移了
压栈后栈顶现在存放的是88888888
1014.s 跳转指令
直接跳转指令
- jmp lable
- jmp 0x0804909a
- jmp *%eax
- *lable
条件跳转指令
数的表示与处理
整数表达
无符号整数
无符号整数就是大于等于0的数,4为无符号整数的取值范围为0-15。
补码形式
最高位的权重是负数
具体例子如下
对于相同的数,映射关系不同,得到的数也不同,下图是有符号数和无符号数的例子
无符号数与有符号数的转换
他们的二进制表示都相同,但如果是有符号数的话,第一位会乘负权重;如果是无符号数就是正常。
溢出
无符号数加法
期中复习
原码 反码 补码
求一个负数的补码:比如-25
- 先转换成原码 10011001 (有符号数最高位为标志位,代表-128(八位的话))
- 负数的反码等于除标志位各个位置取反 11100110
- 最后得到补码,补码就是在反码的基础上+1 11100111 转成16进制为E7H。
指令寄存器
直接寻址的无条件转移指令功能是将指令中的地址码送入PC,PC决定下一条指令执行的地址。
浮点数
定点数的表示方法
- 定点小数 :小数点在符号位和数值位(决定正负数之间)比如0.875(10),转换成二进制->0.111(B),补够8位0(小数点隐含在这个位置)01110000;如果是-0.875就是11110000,符号位还是带权。
- 定点整数 :小数点在最低位后,比如32(10)->100000(B),补够8位,00100000,因为是正数,所以符号位为0;如果是-32,就10100000,这是原码,负数在计算机中以补码的形式存放,所以我们将其除符号位的所有位置反转得到反码11011111,再在反码的基础上+1得到补码11100000.
浮点数法的表示方法
浮点数分为符号位、阶码、尾数三个部分
类似于科学计数法,比如112.5(10)来表示为1.12510²
先将112.5转成二进制数,1110000.1(B) = 可以表示为1.11000012^6,不能写为6,要写成110,这个指数就叫做阶码。1100001就叫做尾数(小数点后的数)
阶码要加127,6+127 = 133再转成二进制
第二个例子
[ 符号位 | 阶码8位 | 尾数23位 ]
在浮点数编码表示中,(基数)在机器数中不出现,是隐含的。
下列叙述中概念正确地是( D )
A 定点补码运算时,其符号位不参加运算 //都参与,否则加法无法处理正负数
B 浮点运算中,尾数部分只进行乘法和除法运算 //位数部分可以进行加减乘除
C 浮点数的正负由阶码的正负符号决定 //由*符号位决定
D 在定点小数一位除法中为了避免溢出被除数的绝对值一定要小于除
数的绝对值
- 当尾数用补码表示的时候,数符(符号位)和尾数第一位相反,则代表这个数规格化
假设初始值:%dh=CD,%eax=98765432,则执行 movzbl %dh ,%eax
这样一条指令后,%eax 的值为( D )
A %eax= 987654CD
B %eax= CD765432
C %eax= FFFFFFCD
D %eax= 000000CD
movzbl 指令的目标寄存器是 32 位的 %eax,而这条指令的设计就是:把源操作数(一个 8 位数)零扩展成 32 位,然后整个写入目标寄存器(%eax)中,覆盖原内容。
假设初始值:%dh=CD,%eax=98765432,则执行 movsbl %dh ,%eax
这样一条指令后,%eax 的值为( C )
A %eax= 987654CD
B %eax= CD765432
C %eax= FFFFFFCD
D %eax= 000000CD
movsbl %dh, %eax 是一条有符号扩展指令,会将 %dh 中的 8 位值 CD(即二进制 11001101,表示负数)扩展为 32 位,扩展时高位补符号位(即补 1),得到 FFFFFFCD,并覆盖写入整个 %eax,所以最终 %eax = FFFFFFCD。
假设在 C 程序中有 int *a , int n ,如果值 a 在寄存器%ecx 中,n 在%edx
中,下面哪个指令计算的是 a[n] ?( C )
A ret (%ecx,%edx,4)
B leal (%ecx,%edx,4),%eax ret
C mov (%ecx,%edx,4),%eax ret
D mov (%ecx,%edx,1),%eax ret
int a[i] = a + sizeof(int)n,其中int的大小为4,mov是读取内存中的值,访问内存,而leal是*计算地址,结果存寄存器,不访问内存。
leal(%ecx,%edx,4),%eax : 计算地址ecx + 4edx并存入eax中。
mov(%ecx,%edx,4),%eax :计算地址并从*这个地址中读取内容
并不是任何十进制小数都可用二进制表示 ,比如0.1(10)标识出来的就是一个无限不循环小数
下列指令不会改变条件码的值的是(D)
A testl %eax,%eax
B addl %eax,%eax
C cmpl %esi,%dsi
D jge .L2
test是按位与,add加,cmp比较,都会改变条件码,jg条件跳转。
下列汇编指令中正确的是( A )
A movl $0x4050,%eax
B movl(%eax),4(%esp)
C movl %eax,$0x123
D movb $0xF,%ebx
我们上课所学的汇编是将左边的传给右边,A将0x4050这个地址传给eax保存,B读取了eax所存放的地址,但是mov指令是要传递给寄存器的,所以应该是movl (%eax),%ebx
将一个 4 位数值-5 截断到 3 位数的结果为
先转为补码,-5的原码为1101,反码为1010,补码为1011,截断成三位数(舍弃最高位)称为011,变为3
将一个双字节的内容压入(32位),栈帧-4;
EAX是一个32位的寄存器,AX用来表示低16位,AH用来表示低16位的高8位,AL用来表示低16位的低8位。
算数右移(SAR)规则:保留符号位(最高位)右移其它位,末位丢弃
原数: 11001110
SAR1: 11100111
对整数运算z=x+y,设置条件码OF的表达式为D (x < 0 == y < 0) && (z < 0 != x < 0)
- 正溢出:x > 0 && y > 0 && z < 0(正溢出)
- 负溢出:x < 0 && y < 0 && z >= 0(负溢出)
在补码的加法中发生了负溢出的是
C x+y = x+y-2^w
若 w 位补码中两个负数相加,结果却变成正数 ⇒ 发生负溢出,此时实际数学值超出了补码能表示的负数范围。当加法结果超出这个范围,补码会 回绕(wrap around),造成:负溢出 ⇒ 减去 2^w,正溢出 ⇒ 加上 2^w。
对整数运算z=x+y,设置条件码CF的表达式为:(unsigned)z < (unsigned)x
CF:无符号加减法的进位(无符号溢出)
OF:溢出(有符号溢出)
SF:运算结果的正负
ZF:零标志位
假设AL=5H,要使得AL=0FAH,应执行的命令是( )。
5H = 0000 0101 (二进制)
0000 0101 (原始值 AL = 0x05)
取反后: 1111 1010 (AL = 0xFA)