本篇记录《汇编语言:基于X86处理器》第4章的复习题和练习,编程练习的学习。
4.9 复习题和练习
4.9.1 简答题
1.执行下列标记为(a)和(b)的指令后,EDX 的值分别为多少?
.data
one WORD 8002h
two WORD 4321h
.code
mov edx,21348041h
movsx edx,one ;(a) EDX = 00008002h
movsx edx,two ;(b) EDX = 00004321h
答:(a) EDX = 00008002h,(b) EDX = 00004321h
2.执行下列指令后,EAX 的值是多少?
mov eax,1002FFFFh
inc ax
答:EAX=10020000h。
3.执行下列指令后,EAX 的值是多少?
mov eax, 30020000h
dec ax
答:EAX=3002FFFFh。
4.执行下列指令后,EAX的值是多少?
mov eax,1002FFFFh
neg ax
答:EAX=10020001h。
5.执行下列指令后,奇偶标志位的值是多少?
mov al, 1
add al, 3
答:al = 4, 二进制为 0100b 奇偶标志位PF=0,即显示PE = 0。
6.执行下列指令后,EAX和符号标志位的值分别是多少?
mov eax, 5
sub eax, 6
答:EAX=-1,标志位的值,符号标志PL=1,进位标志CY=1,奇偶标志PL=1
7.下面的代码中,AL 为一字节有符号数。说明,在判断 AL 最终结果是否在有符号数的有效范围内时,溢出标志位是否有用,若有用,是如何起作用的?
mov al, -1
add al, 130
答:al=129=81h,不在-128~127的有符号范围内,130不在有符号范围内,所以此处进行的是无符号计算,溢出标志位不起作用。再看最高位为1,因此,有进位标记。
8.执行下列指令后,RAX 的值是多少?
mov rax, 44445555h
答:RAX=0000000044445555h。
9.执行下列指令后,RAX 的值是多少?
.data
dwordVal DWORD 84326732h
.code
mov rax, 0FFFFFFFF00000000h
mov eax, dwordval
答:RAX=0000000084326732h。
10.执行下列指令后,EAX 的值是多少?
.data
dVal DWORD 12345678h ;在内存中是这样存放的78 56 34 12
.code
mov ax, 3
mov WORD PTR dVal+2, ax ;赋值给dVal的高16位,此时dVal = 0003 5678h
mov eax, dVal
答:EAX=0x00035678h。
11.执行下列指令后,EAX 的值是多少?
.data
dVal DWORD ?
.code
mov dVal,12345678h ;dVal = 12345678h
mov ax,WORD PTR dVal+2 ;ax=1234h
add ax,3 ;ax=1237h
mov WORD PTR dVal,ax ;dVal = 1234 1237h
mov eax,dVal ;eax = 1234 1237h
答:EAX=1234 1237h。
12.(是/否):正数与负数相加时,是否可能使溢出标志位置1?
答:否
13.(是/否):两负数相加,结果为正数,溢出标志位是否置1?
答:是
14.(是/否):执行NEG指令是否能将溢出标志位置1?
答:是,NEG
指令可以将 OF
置 1,但仅当操作数是当前位数的最小负数时(如 -128
对于 8 位,-32768
对于 16 位)。其他情况下,OF
保持 0。
15.(是/否):符号标志位和零标志位是否能同时置1?
答:否
问题16~问题19使用如下变量定义:
.data
var1 SBYTE -4, -2, 3, 1
var2 WORD 1000h, 2000h, 3000h, 4000h
var3 SWORD -16, -42
var4 DWORD 1, 2, 3, 4, 5
16.判断下述每条指令是否为有效指令:
a. mov ax, varl?
b. mov ax, var2
c. mov eax,var3
d. mov var2, var3
e. movzx ax, var2
f. movzx var2,al
g. mov ds, ax
h. mov ds,1000h
答:a.无效 b.有效 c.无效 d.无效 e.无效 f.无效 g.有效 h.无效
17.顺序执行下列指令,则每条指令目标操作数的十六进制值是多少?
mov al, var1 ;a.
mov ah, [var1+3] ;b.
答:a. al = -4 补码 0FCh, b. ah = 1= 01h
18.顺序执行下列指令,则每条指令目标操作数的值是多少?
mov ax, var2 ;a.
mov ax, [var2+4] ;b.
mov ax, var3 ;c.
mov ax, [var3-2] ;d.
答:a. ax = 1000h, b. ax = 3000h, c. ax = -16 补码为0FFF0h, c. ax = 4000h
19.顺序执行下列指令,则每条指令目标操作数的值是多少?
mov edx, var4 ;a.
movzx edx, var2 ;b.
mov edx, [var4+4] ;c.
mov edx, var1 ;d.
答:a. edx = 1, b. edx = 0000 1000h, c. edx = 2 (占4个字节), c. edx = 0FFFCh
4.9.2 算法基础
1.有一变量名为 three 的双字变量,编写一组 MOV指令来交换该变量的高位字和低位字。
;4.9.2_1.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
three DWORD 12345678h
.code
main PROC
;方法1
;mov esi, OFFSET three
;mov eax,[esi]
;shl eax,16
;mov ax,WORD PTR [esi+2]
;mov [esi],eax
;方法2
mov esi, OFFSET three
mov edx,[esi] ;这两行查看地址数据测试用
mov ax,WORD PTR [three+2]
mov bx,WORD PTR [three]
xchg ax, WORD PTR [three]
xchg bx, WORD PTR [three+2]
;方法3
;mov eax,three
;ror eax,16 ;循环右移16位
;mov DWORD PTR [three], eax ;mov three, eax 这样写也可以
INVOKE ExitProcess,0
main endp
end main
运行调试:
交换后:
2.用不超过 3 条的 XCHG 指令对 4 个 8 位寄存器的值进行重排序,将其顺序从 A、B、C、D 调整为 B、C、D、A。
;4.9.2_2.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
arrayB BYTE 0Ah,0Bh,0Ch,0Dh
.code
main PROC
movzx eax, BYTE PTR [arrayB]
movzx ebx, BYTE PTR [arrayB+1]
movzx ecx, BYTE PTR [arrayB+2]
movzx edx, BYTE PTR [arrayB+3]
;原始顺序 A->B->C->D
xchg al,bl ;B->A->C->D
xchg bl,cl ;B->C->A->D
xchg cl,dl ;B->C->D->A
INVOKE ExitProcess,0
main endp
end main
3.被传输的信息通常包含有一个奇偶位,其值与数据字节结合在一起,使得 1 的位数为偶数。设 AL寄存器中信息字节的值为01110101,如何用一条算术运算指令和奇偶标志位判断该信息字节是偶校验还是奇校验?
答:用TEST指令和PE标志位的值来判断,如果PE=1是偶校验,如果PE=0是奇校验。
4.编写代码,用字节操作数实现两个负整数相加,并使溢出标志位置 1。
答:两个负数相加,只要超过-128就会产生溢出。例如:-64 + (-65) = -129
;4.9.2_4.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov al,-64
add al,-65
INVOKE ExitProcess,0
main endp
end main
运行调试:
5.编写连续的两条指令,用加法使零标志位和进位标志位同时置1。
答:使用字节操作数,相加结果等于256即可。
;4.9.2_5.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov al,0FFh
add al,1
INVOKE ExitProcess,0
main endp
end main
运行调试:
6.编写连续的两条指令,用减法使进位标志位置1。
;4.9.2_6.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov al,0
sub al,1
INVOKE ExitProcess,0
main endp
end main
运行调试:
7.用汇编语言实现算术表达式:EAX=-val2+7-val3+vall。假设vall、val2 和 val3 都是32位整数变量。
答:这里以val1=55, val2=30,val3=13为例,eax=30+7-13+55=79
;4.9.2_7.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
val1 DWORD 55
val2 DWORD 30
val3 DWORD 13
.code
main PROC
mov eax, val2
add eax, 7
mov ebx, val3
sub eax, ebx
mov edx, val1
add eax, edx
INVOKE ExitProcess,0
main endp
end main
运行调试:
8.编写循环代码,在一个双字数组中进行迭代。用带比例因子的变址寻址,计算该数组元素的总和。
;4.9.2_8.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
arrayD DWORD 1000h,2000h,3000h,4000h
.code
main PROC
mov eax, 0 ;初始化为0
mov esi, 0 ;下标
mov ecx, LENGTHOF arrayD ;元素的个数
L1:
add eax, arrayD[esi*4]
inc esi
loop L1
INVOKE ExitProcess,0
main endp
end main
运行调试:
9.用汇编语言实现算术表达式:AX=(val2+BX)-val4。假设val2 和val4都是16位整数变量。
答:这里以val2=22,bx=33,val4=44为例,AX=22+33-44=11。
;4.9.2_9.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
val2 WORD 22
val4 WORD 44
.code
main PROC
mov ax, val2
mov bx, 33
add ax, bx
mov bx, val4
sub ax, bx
INVOKE ExitProcess,0
main endp
end main
运行调试:
10.编写连续的两条指令,使进位标志位和溢出标志位同时置1。
答:两条指令mov al,0F0h, add al,88h
;4.9.2_10.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov al,0F0h
add al,88h
INVOKE ExitProcess,0
main endp
end main
运行调试:
11.编写指令序列,说明在执行INC和DEC指令后,如何用零标志位来判断无符号溢出情况
;4.9.2_1.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.code
main PROC
mov al, 0FFh
inc al
jz inc_ov ;加1等于,说明有溢出
mov bl, 0
dec bl
cmp bl, 0FFh ;减1等于0FFh,说明有借位溢出
je dec_ov
inc_ov:
mov edx,0
jmp exit
dec_ov:
mov edx,0FFFFFFFFh
jmp exit
exit:
nop
INVOKE ExitProcess,0
main endp
end main
运行调试:
问题12~问题18使用如下数据定义:
.data
myBytes BYTE 10h, 20h, 30h, 40h
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
12.在给定数据中插人一条伪指令,将myBytes 对齐到偶地址.
;4.9.2_12.asm 4.9.2 算法题 12.在给定数据中插人一条伪指令,将myBytes 对齐到偶地址.
.386
.model flat,stdcall
.stack 4095
ExitProcess PROTO,dwExitCode:DWORD
.data
ALIGN 2 ;插入2字节对齐
myBytes BYTE 10h, 20h, 30h, 40h
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
.code
main PROC
mov eax, DWORD PTR myBytes
mov al, BYTE PTR myBytes
mov bl, BYTE PTR myBytes+1
mov esi, offset myWords
INVOKE ExitProcess,0
main endp
end main
13.下列每条
指令执行后,EAX的值分别是多少?
;4.9.2_1.asm 4.9.2 算法题
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
myBytes BYTE 10h, 20h, 30h, 40h
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
.code
main PROC
mov eax, TYPE myBytes ;a. eax=1
mov eax, LENGTHOF myBytes ;b. eax=4
mov eax, SIZEOF myBytes ;c. eax=4
mov eax, TYPE myWords ;d. eax=2
mov eax, LENGTHOF myWords ;e. eax=4
mov eax, SIZEOF myWords ;f. eax=8
mov eax, SIZEOF myString ;g. eax=5
INVOKE ExitProcess,0
main endp
end main
14.编写一条指令将myBytes 的前两个字节送入 DX 寄存器,使寄存器的值为 2010h。
答:mov dx, WORD PTR myBytes
完整测试代码:
;4.9.2_14.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
myBytes BYTE 10h, 20h, 30h, 40h
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
.code
main PROC
mov dx, WORD PTR myBytes
INVOKE ExitProcess,0
main endp
end main
运行调试:
15.编写一条指令将myWords 的第二个字节送人 AL 寄存器。
答:mov AL, BYTE PTR myWords
完整测试代码:
;4.9.2_15.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
myBytes BYTE 10h, 20h, 30h, 40h
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
.code
main PROC
mov AL, BYTE PTR myBytes+1
INVOKE ExitProcess,0
main endp
end main
运行调试:
16.编写一条指令将myBytes 的全部四个字节送人 EAX寄存器。
答:mov eax, DWORD PTR myBytes
完整测试代码:
;4.9.2_16.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
myBytes BYTE 10h, 20h, 30h, 40h
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
.code
main PROC
mov EAX, DWORD PTR myBytes
INVOKE ExitProcess,0
main endp
end main
运行调试:
17.在给定数据中插人一条LABEL 伪指令,使得myWords 能直接送入 32 位寄存器。
答:var32 LABEL DWORD ;定义var32为4个字节
完整测试代码:
;4.9.2_17.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
myBytes BYTE 10h, 20h, 30h, 40h
var32 LABEL DWORD
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
.code
main PROC
mov EAX, var32
INVOKE ExitProcess,0
main endp
end main
运行调试:
18.在给定数据中插人一条LABEL伪指令,使得myBytes能直接送人16位寄存器。
答:var16 LABEL WORD ;定义var16为两个字节
完整测试代码:
;4.9.2_18.asm
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
var16 LABEL WORD ;定义var16为两个字节
myBytes BYTE 10h, 20h, 30h, 40h
myWords WORD 3 DUP(?), 2000h
myString BYTE "ABCDE"
.code
main PROC
mov AX, var16
INVOKE ExitProcess,0
main endp
end main
运行调试:
4.10 编程练习
下面的练习可以在32 位模式或 64 位模式下完成。
*1.将大端顺序转换为小端顺序
使用下面的变量和MOV 指令编写程序,将数值从大端顺序复制为小端顺序,颠倒字节的顺序。32 位数的十六进制值为12345678。
.data
bigEndian BYTE 12h, 34h, 56h, 78h
littleEndian DWORD ?
完整的代码:
;4.10_1.asm 4.10 编程练习 *1.将大端顺序转换为小端顺序
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
bigEndian BYTE 12h, 34h, 56h, 78h
littleEndian DWORD ?
.code
main PROC
mov esi, offset bigEndian ;这行查看地址测试用
mov ax, WORD PTR bigEndian ;ax=3412h
xchg ah,al ;ax=1234h
shl eax, 16 ;eax=12340000h
mov ax, WORD PTR bigEndian+2 ;eax=12347856h
xchg ah,al ;eax=12345678h
mov littleEndian, eax
INVOKE ExitProcess,0
main endp
end main
运行调试:
**2.交换数组元素对
编写循环程序,用变址寻址交换数组中的数值对,每对中包含偶数个元素。即,元素i与元素i+1 交换,元素i+2 与元素i+3 交换,以此类推。
完整的代码:
;4.10_2.asm 4.10 编程练习 **2.交换数组元素对
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
.data
arrayB BYTE 10h,20h,30h,40h,50h,60h
.code
main PROC
mov edi, offset arrayB ;测试查看地址用
mov esi, 0
mov ecx, LENGTHOF arrayB ;元素个数
L1:
mov AL, BYTE PTR arrayB[esi] ;AL = 10h
mov BL, BYTE PTR arrayB[esi+1] ;BL = 20h
xchg BYTE PTR arrayB[esi], BL
mov BYTE PTR arrayB[esi+1], AL
DEC ecx ;这里减一次1,下面loop还会减1,交换次数为元素个数/2
ADD esi,2
loop L1
INVOKE ExitProcess,0
main endp
end main
运行调试:
**3.数组元素间隔之和
;4.10_3.asm 4.10 编程练习 **3.数组元素间隔之和
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
arrayD DWORD 0,2,5,9,10
.code
main PROC
mov esi, 0 ;数据位置
mov edx, 0 ;存放间隔数之和
mov ecx, LENGTHOF arrayD - 1 ;元素个数
L1:
mov eax, arrayD[esi] ;相邻的第1个元素
mov ebx, arrayD[esi+4] ;相邻的第2个元素
sub ebx, eax ;元素间隔
add edx, ebx ;把间隔数相加存放到edx中
add esi, 4 ;下一个元素
loop L1
INVOKE ExitProcess,0
main endp
end main
运行调试:
**4将字数组复制到双字数组
编写循环程序,把一个无符号字(16位)数组的所有元素复制到无符号双字(32位)数组。
完整的代码:
;4.10_4.asm 4.10 编程练习 **4将字数组复制到双字数组
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
arrayW WORD 1111h,2222h,3333h,4444h,5555h,6666h,7777h,8888h
arrayD DWORD 8 DUP(?)
.code
main PROC
mov esi, OFFSET arrayW
mov edi, 0 ;下标
mov ecx, LENGTHOF arrayW ;元素个数
L1:
movzx eax, WORD PTR arrayW[edi*2]
mov DWORD PTR arrayD[edi*4], eax
inc edi
loop L1
INVOKE ExitProcess,0
main endp
end main
运行调试:
**5.斐波那契数列
编写循环程序,计算斐波那契(Fibonacci)数列前七个数值之和,算式如下:
Fib(1)=1,Fib(2)=1,Fib(n)=Fib(n-1)+Fib(n-2)
完整的代码:
;4.10_5.asm 4.10 编程练习 **5.斐波那契数列
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
.code
main PROC
mov eax, 1 ;f1=1
mov ebx, 1 ;f2=1
mov edx, 1 ;f3=1 edx存放数值之和
mov ecx, 7 ;数列个数
L1:
mov edx, eax
add edx, ebx ;f3 = f2+f1
mov eax, ebx ;f2 = f1
mov ebx, edx ;f2 = f3
cmp ecx,1 ;当ecx等1时,edx要减1,因为前面加了一次1
je exit
loop L1
exit:
dec edx
INVOKE ExitProcess,0
main endp
end main
运行调试:
***6.数组反向
编写循环程序,用间接或变址寻址实现整数数组元素的位置颠倒。不能将元素复制到其他数组。考虑到数值大小和类型在将来可能发生变化,用 SIZEOF、TYPE 和 LENGTHOF运算符尽可能增加程序的灵活性。
完整的代码:
;4.10_6.asm 4.10 编程练习 ***6.数组反向
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
arrayW WORD 11h,22h,33h,44h,55h
.code
main PROC
mov ebx, offset arrayW ;查看内存数据测试用
mov esi, 0 ;正向下标
mov ecx, LENGTHOF arrayW ;元素个数
mov edi, ecx
dec edi ;下标是从0开始的,所以这里要减1
L1:
mov ax, WORD PTR arrayW[esi * TYPE arrayW]
xchg ax, WORD PTR arrayW[edi* TYPE arrayW]
mov WORD PTR arrayW[esi * TYPE arrayW], ax
inc esi ;正向下标
dec edi ;反向下标
cmp ecx,1 ;如果ecx等于1,结束循环
je exit
dec ecx ;循环次数为元素个数/2
loop L1
exit: mov ecx, 0
INVOKE ExitProcess,0
main endp
end main
运行调试:
反向:
***7.将字符串复制为相反顺序
编写循环程序,用变址寻址将一个字符串从源复制到目的,并实现字符的反向排序。变量定义如下:
source BYTE "This is the source string, 0
target BYTE SIZEOF source DUP('#')
完整的代码:
;4.10_7.asm 4.10 编程练习 ***7.将字符串复制为相反顺序
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
source BYTE "This is the source string", 0
target BYTE SIZEOF source DUP('#')
.code
main PROC
mov edx, offset source ;测试查看内存数据用
mov ecx, LENGTHOF source ;元素个数
mov esi, 0 ;下标
mov edi, ecx
dec edi ;防止下标越界
L1:
mov AL, BYTE PTR source[esi]
mov BYTE PTR target[edi], AL
inc esi
dec edi
loop L1
INVOKE ExitProcess,0
main endp
end main
运行调试:
反向排序
***8.数组元素移位
编写循环程序,用变址寻址把一个32位整数数组中的元素向前(向右)循环移动一个位置数组最后一个元素的值移动到第一个位置上。比如,数组[10,20,30,40]移位后转换为[40,10,20,30]。
完整的代码:
;4.10_8.asm 4.10 编程练习 ***8.数组元素移位
.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD
.data
arrayB BYTE 10h,20h,30h,40h
.code
main PROC
mov esi, OFFSET arrayB ;测试查看内存数据用
mov ecx, LENGTHOF arrayB
dec ecx
mov edi, ecx
L1:
mov AL, arrayB[edi]
xchg BYTE PTR arrayB[edi-1], AL
mov BYTE PTR arrayB[edi], AL
dec edi
loop L1
INVOKE ExitProcess,0
main endp
end main
运行调试:
向右循环移动后: