本篇记录《汇编语言:基于X86处理器》第3章 复习题和练习,编程练习的学习。
3.9复习题和练习
3.9.1 简答题
1.举例说明三种不同的指令助记符。
答:MOV,ADD和MUL。
2.什么是调用规范?如何在汇编语言声明中使用它?
答: 在汇编语言中,调用规范(Calling Convention) 是定义函数调用过程中参数传递、堆栈管理、寄存器使用和返回值传递等细节的底层规则集。 例如
; 声明内存模型和调用规范(如 stdcall)
.model flat, stdcall ; 32 位平坦内存模型 + stdcall 规范:ml-citation{ref="1" data="citationList"}
3.如何在程序中为堆栈预留空间?
答:.stack 4096
4.说明为什么术语汇编器语言不太正确。
答: 术语“汇编器语言”(assembler language)不太正确,因为它混淆了“汇编语言”(assembly language)与“汇编器”(assembler)工具的区别。
5.说明大端序和小端序之间的区别,并在网络上查找这些术语的起源。
答:大端序和小端序之间的区别是,大小端低位放在高地址,而小端序低位放低地址。
6.为什么在代码中使用符号常量而不是整数常量?
答:使用符号常量会让程序更加容易阅读和维护。设想,如果COUNT在整个程序中出现多次,那么在之后的时间里,程序员就能方便地重新定义它的值。
7.源文件与列表文件的区别是什么?
答:列表文件包含了程序源代码的副本,再加上行号、偏移地址、翻译的机器代码和符号表,适合打印。
8.数据标号与代码标号的区别是什么?
答:数据标号标记数据位置,代码标号标记代码的位置,并且以(:)号结束。
9.(真/假):标识符不能以数字开头。
答:真。第一个字符必须为字母(A...Z, a...z),下划线(_)、@ 、?或$。其后的字符也可以是数字。
10.(真/假);十六进制常量可以写为0x3A。
答:假。汇编代码里不能写成0x开头的十六进制数,这应该写成3Ah.
11.(真/假):汇编语言伪指令在运行时执行。
答:假。汇编的伪指令在汇编时处理。
12.(真/假):汇编语言伪指令可以写为大写字母和小写字母的任意组合。
答:真
13.说出汇编语言指令的四个基本组成部分。
答:1.标号(可选),2.指令助记符(必需),3.操作数(通常是必需的),4.注释(可选)
14.(真/假):MOV是指令助记符的例子。
答:真
15.(真/假):代码标号后面要跟冒号(:),而数据标号则没有。
答:真
16.给出块注释的例子。
答:COMMENT !
....这里是多行注释
CoMMENT !
17.使用数字地址编写指令来访问变量,为什么不是一个好主意?
答:使用数字地址编写指令来访问变量有这几个问题:可维护性差,可读性低,平台依赖性强,安全性风险高,调试困难。
18.必须向ExitProcess过程传递什么类型的参数?
答:DWORD
19.什么伪指令用来结束子程序?
答:ENDP伪指令用来结束子程序。
20.32位模式下,END伪指令中的标识符有什么用?
答:END伪指令标记一个程序的结束。
21.PROTO伪指令的作用是什么?
答:PROTO
伪指令主要用于声明函数原型,为后续的函数调用提供参数和返回值类型规范
22(真/假):目标文件由链接器生成。
答:假,目标文件由汇编器读取源文件生成。
23.(真/假):列表文件由汇编器生成。
答:真。
24.(真/假):链接库只有在生成可执行文件之前才加到程序中。
答:假。
- 静态链接库:在生成可执行文件之前(编译的链接阶段)被嵌入到程序中,成为可执行文件的一部分,程序可独立运行。
- 动态链接库:在程序运行时(如加载或调用时)才被链接和加载到内存中,而非在生成可执行文件之前添加。动态链接库允许内存共享和按需加载,避免了静态链接的冗余拷贝问题
25.哪个数据伪指令定义32位有符号整数变量?
答:SDWORD,例如:val SDWORD ?
26.哪个数据伪指令定义16位有符号整数变量?
答:WORD,例如:val WORD ?
27.哪个数据伪指令定义64位无符号整数变量?
答:SQWORD,例如:val SQWORD ?
28.哪个数据伪指令定义8位有符号整数变量?
答:QWORD,例如:val QWORD ?
29.哪个数据伪指令定义10字节压缩BCD变量?
答:TBYTE, 例如:val TBYTE ?
3.9.2 算法基础
答:val1 EQU 25
val2 EQU 0001 1001b
val3 EQU 31o
val4 EQU 19h
2.通过实验和错误,找出一个程序是否能有多个代码段和数据段。
答:通过实现,一个程序可以有多个代码段和数据段,例如:
;3.9.2_2.asm 第3章 3.9.2算法基础 第2题 验证.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
sum DWORD 0.data
res SDWORD 0.code
start PROCmov bx, 7add bx, 8
start ENDP.code
main PROCmov eax, 5add eax, 6mov sum, eax INVOKE ExitProcess, 0
main ENDP
END main
3.编写数据定义,把一个双字按大端序存放在内存中。
;3.9.2_3.asm 第3章 3.9.2算法基础 第3题 编写数据定义,把一个双字按大端序存放在内存中。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
;big_endian_value db 0xA1, 0xB2, 0xC3, 0xD4
LittleEndianDWORD DWORD 12345678h.code
main PROCmov eax, dword ptr [LittleEndianDWORD] ; 加载原始值mov ebx, 0 ; 初始化目标值mov edx, eax ; 提取最低有效字节shl edx, 24 ; 移动到最高有效字节位置or ebx, edx ; 合并到目标值 此时bx=78000000hmov edx, 0mov dl, byte ptr [LittleEndianDWORD+1] ;edx=00000056hshl edx,16 ;edx=005600hor ebx,edx ;ebx=78560000hmov edx, 0mov dl, byte ptr [littleEndianDWORD+2] ;edx=00000034hshl edx, 8 ;edx=00003400hor ebx, edx ;ebx=78563400hmov edx, 0mov dl, byte ptr [littleEndianDWORD+3] ;edx=00000012hor ebx, edx ;ebx=78563412hmov dword ptr [littleEndianDWORD], ebx ; 将结果存储到内存地址 littleEndianDWORDmov eax, dword ptr [LittleEndianDWORD] ; 查看修改后的值 INVOKE ExitProcess, 0
main ENDP
END main
运行调试:调试前:
调试后:
内存中的数据由12345678h转换成78563412h
4.试发现用 DWORD 类型定义一个变量时,是否能向其赋予负数值。这说明了汇编器类型检查的什么问题?
;3.9.2_4.asm 第3章 3.9.2 算法基础 第4题 试发现用 DWORD 类型定义一个变量时,是否能向其赋予负数值。这说明了汇编器类型检查的什么问题?.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
sum DWORD -3 ;初始化为一个负数.code
main PROCmov eax, 5add eax, 6mov sum, eax INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
说明汇编器检查数据是,如果是负数会转成补码。
5.编写一个程序,包含两条指令:(1)EAX寄存器加5;(2)EDX寄存器加5。生成列表文件并检查由汇编器生成的机器代码。发现这两条指令的不同之处了吗?如果有,是什么?
;3.9.2_5.asm 第3章 3.9.2 算法基础 第5题 编写一个程序,包含两条指令:(1)EAX寄存器加5;(2)EDX寄存器加5。
;生成列表文件并检查由汇编器生成的机器代码。发现这两条指令的不同之处了吗?如果有,是什么?.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
sum DWORD 0
sum2 DWORD 0.code
main PROCmov eax, 0add eax, 5mov sum, eax mov edx, 0add edx, 5mov sum2, edxINVOKE ExitProcess, 0
main ENDP
END main
列表文件
Microsoft (R) Macro Assembler Version 14.41.34120.0 06/20/25 16:23:54
3.9.2_5.asm Page 1 - 1;3.9.2_5.asm 第3章 3.9.2 算法基础 第5题 编写一个程序,包含两条指令:(1)EAX寄存器加5;(2)EDX寄存器加5。;生成列表文件并检查由汇编器生成的机器代码。发现这两条指令的不同之处了吗?如果有,是什么?.386.model flat, stdcall.stack 4096ExitProcess PROTO, dwExitCode:DWORD00000000 .data00000000 00000000 sum DWORD 000000004 00000000 sum2 DWORD 000000000 .code 00000000 main PROC00000000 B8 00000000 mov eax, 000000005 83 C0 05 add eax, 500000008 A3 00000000 R mov sum, eax 0000000D BA 00000000 mov edx, 000000012 83 C2 05 add edx, 500000015 89 15 00000004 R mov sum2, edxINVOKE ExitProcess, 00000001B 6A 00 * push +000000000h0000001D E8 00000000 E * call ExitProcess00000022 main ENDPEND mainMicrosoft (R) Macro Assembler Version 14.41.34120.0 06/20/25 16:23:54
3.9.2_5.asm Symbols 2 - 1Segments and Groups:N a m e Size Length Align Combine ClassFLAT . . . . . . . . . . . . . . GROUP
STACK . . . . . . . . . . . . . 32 Bit 00001000 DWord Stack 'STACK'
_DATA . . . . . . . . . . . . . 32 Bit 00000008 DWord Public 'DATA'
_TEXT . . . . . . . . . . . . . 32 Bit 00000022 DWord Public 'CODE' Procedures, parameters, and locals:N a m e Type Value AttrExitProcess . . . . . . . . . . P Near 00000000 FLAT Length= 00000000 External STDCALL
main . . . . . . . . . . . . . . P Near 00000000 _TEXT Length= 00000022 Public STDCALLSymbols:N a m e Type Value Attr@CodeSize . . . . . . . . . . . Number 00000000h
@DataSize . . . . . . . . . . . Number 00000000h
@Interface . . . . . . . . . . . Number 00000003h
@Model . . . . . . . . . . . . . Number 00000007h
@code . . . . . . . . . . . . . Text _TEXT
@data . . . . . . . . . . . . . Text FLAT
@fardata? . . . . . . . . . . . Text FLAT
@fardata . . . . . . . . . . . . Text FLAT
@stack . . . . . . . . . . . . . Text FLAT
sum2 . . . . . . . . . . . . . . DWord 00000004 _DATA
sum . . . . . . . . . . . . . . DWord 00000000 _DATA 0 Warnings0 Errors
同样是加法操作,生成的机器码不一样
6.假设有数值456789ABh,按小端序列出其字节内容。
答:小端序是低位存放在低地址,因此列出字节的内容为:0A8h, 89h, 67h, 45h.
7.声明一个数组,其中包含 120个未初始化无符号双字数值。
答:val sdword 120 dup(?)
8.声明一个字节数组,并将其初始化为字母表的前5个字母。
答:myArray byte 'a', 'b', 'c', 'd', 'e'
9.声明一个 32 位有符号整数变量,并初始化为尽可能小的十进制负数。(提示:参阅第1 章的整数范围。
答:minNegative32 DWORD -2147483648 ; 声明并初始化为最小的 32 位有符号整数
10.声明一个 16 位无符号整数变量 wArray,使其具有 3 个初始值。
答:wArray SWORD 10, 20, 30
11.声明一个字符串变量,包含你最喜欢颜色的名字,并将其初始化为空字节结束的字符串。
答:myColor BYTE "blue", 0
12.声明一个未初始化数组dArray,包含50个有符号双字。
答:myArray DWORD 50 dup(?)
13.声明一个字符串变量,包含单词“TEST”并重复500次。
答:myString BYTE 500 dup("TEST")
14.声明一个数组 bArray,包含 20个无符号字节,并将其所有元素都初始化为 0。
答:bArray SBYTE 20 dup(0)
15.写出下述双字变量在内存中的字节序列(从最低字节到最高字节);
val1 DWORD 87654321h
答:x86处理器在内存中按小端序存放数据,因此从最低字节到最高字节的序列为:21h, 43h, 65h, 87h。
3.10 编程练习
*1.整数表达式的计算
参考 3.2 节的程序 AddTwo,编写程序,利用寄存器计算表达式:A-(A+B)-(C-D)。整数值分配给寄存器 EAX、EBX、ECX 和EDX。
;3.10_1.asm 第3章 编程练习 *1.整数表达式的计算
;参考 3.2 节的程序 AddTwo,编写程序,利用寄存器计算表达式:A-(A+B)-(C-D)。
;整数值分配给寄存器 EAX、EBX、ECX 和EDX。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
sum DWORD 0.code
main PROCmov eax, 10mov ebx, 5mov ecx, 6mov edx, 15add ebx, eax ;ebx = (A + B)sub eax, ebx ;eax = A - (A + B)sub ecx, edx ;ecx = C - D sub eax, ecx ;eax = A - (A + B) - (C - D)mov sum, eaxINVOKE ExitProcess, 0
main ENDP
END main
结果:10-(10+5)-(6-15)= 4
运行结果:
*2.符号整数常量
编写程序,为一周七天定义符号常量。创建一个数组变量,用这些符号常量作为其初始值。
;3.10_2.asm 第3章 编程练习 *2.符号整数常量
;编写程序,为一周七天定义符号常量。创建一个数组变量,用这些符号常量作为其初始值。 .386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
Monday EQU 1 ;星期一
Tuesday EQU 2 ;星期二
Wednesday EQU 3 ;星期三
Thursday EQU 4 ;星期四
Friday EQU 5 ;星期五
Saturday EQU 6 ;星期六
Sumday EQU 7 ;星期日WorkdayArray DWORD Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sumday.code
main PROCmov eax, [WorkdayArray] mov ebx, [WorkdayArray+4] ;一个数占32位,4个字节,如果WorkdayArray定义的是BYTE,这里都是加1mov ecx, [WorkdayArray+8]mov edx, [WorkdayArray+12]INVOKE ExitProcess, 0
main ENDP
END main
运行测试:
**3.数据定义
编写程序,对 3.4 节表 3-2 中列出的每一个数据类型进行定义,并将每个变量都初始化为与其类型一致的数值。
;3.10_3.asm 第3章 数据定义
;编写程序,对 3.4 节表 3-2 中列出的每一个数据类型进行定义,
;并将每个变量都初始化为与其类型一致的数值。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD.data
val1 BYTE 0 ;最小的无符号数,8位无符号整数,B代表字节
val2 SBYTE -128 ;最小的有符号数,8位有符号整数,S代表有符号
val3 WORD 0 ;最小的无符号数,16位无符号整数
val4 SWORD -32768 ;最小的有符号数,16位有符号整数
val5 DWORD 0 ;最小的无符号数,32位无符号整数,D代表双(字)
val6 SDWORD -2147483648 ;最小的有符号数,32位有符号整数,SD代表有符号双(字)
val7 FWORD 1234567890h ;48位整数(保护模式中的远指针)
val8 QWORD 1234567812345678h ;64位整数,Q代表四(字)
val9 TBYTE 11223344556677889900h;80位(10字节)整数,T代表10字节
val10 REAL4 5.6 ;32位(4字节)IEEE短实数
val11 REAL8 3.2E-260 ;64位(8字节)IEEE长实数
val12 REAL10 4.6E+4096 ;80位(10字节)IEEE扩展实数.code
main PROCmov eax, val5mov ebx, val6INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
*4.符号文本常量
编写程序,定义几个字符串文本(引号之间的字符)的符号名称,并将每个符号名称都用于变量定义。
;3.10_4.asm 第3章 *4.符号文本常量
;编写程序,定义几个字符串文本(引号之间的字符)的符号名称,并将每个符号名称都用于变量定义。.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
constStr1 TEXTEQU <"this is a string",0>
myString1 BYTE constStr1
constStr2 TEXTEQU <"aaaa is a string",0>
myString2 BYTE constStr2.code
main PROCmov eax, 5mov ebx, DWORD PTR [myString2]INVOKE ExitProcess, 0
main ENDP
END main
运行调试:
*****5.AddTwoSum 的列表文件
生成 AddTwoSum 程序的列表文件,为每条指令的机器代码字节编写说明。某些字节值的含义可能需要猜测。
;3.10_5.asm 第3章 *****5.AddTwoSum 的列表文件
;生成 AddTwoSum 程序的列表文件,为每条指令的机器代码字节编写说明。
;某些字节值的含义可能需要猜测。.386
.model flat,stdcall
.stack 4096
ExitProcess PROTO,dwExitCode:DWORD.data
sum DWORD 0.code
main PROC mov eax, 5add eax, 6mov sum, eaxINVOKE ExitProcess,0
main ENDP
END main
列表文件:
Microsoft (R) Macro Assembler Version 14.41.34120.0 06/20/25 23:38:06
3.10_5.asm Page 1 - 1;3.10_5.asm 第3章 *****5.AddTwoSum 的列表文件;生成 AddTwoSum 程序的列表文件,为每条指令的机器代码字节编写说明。;某些字节值的含义可能需要猜测。.386 ; 32位指令集标识符(机器码无直接对应).model flat,stdcall ; 内存模式声明(伪指令无机器码).stack 4096 ; 栈空间声明(伪指令无机器码)ExitProcess PROTO,dwExitCode:DWORD ; 函数原型声明(伪指令)00000000 .data00000000 00000000 sum DWORD 0 ; 变量定义(机器码: 00 00 00 00)00000000 .code00000000 main PROC 00000000 B8 00000005 mov eax, 5 ; B8: MOV指令 opcode, 05 00 00 00: 立即数5(小端序)00000005 83 C0 06 add eax, 6 ; 83 C0: ADD指令 opcode+modrm, 06: 立即数600000008 A3 00000000 R mov sum, eax ; A3: MOV指令 opcode, 00 00 00 00: sum地址(需链接时确定)INVOKE ExitProcess,00000000D 6A 00 * push +000000000h ; 6A: PUSH立即数 opcode0000000F E8 00000000 E * call ExitProcess ; E8: CALL相对地址(需重定位)00000014 main ENDPEND mainMicrosoft (R) Macro Assembler Version 14.41.34120.0 06/20/25 23:38:06
3.10_5.asm Symbols 2 - 1Segments and Groups:N a m e Size Length Align Combine ClassFLAT . . . . . . . . . . . . . . GROUP
STACK . . . . . . . . . . . . . 32 Bit 00001000 DWord Stack 'STACK'
_DATA . . . . . . . . . . . . . 32 Bit 00000004 DWord Public 'DATA'
_TEXT . . . . . . . . . . . . . 32 Bit 00000014 DWord Public 'CODE' Procedures, parameters, and locals:N a m e Type Value AttrExitProcess . . . . . . . . . . P Near 00000000 FLAT Length= 00000000 External STDCALL
main . . . . . . . . . . . . . . P Near 00000000 _TEXT Length= 00000014 Public STDCALLSymbols:N a m e Type Value Attr@CodeSize . . . . . . . . . . . Number 00000000h
@DataSize . . . . . . . . . . . Number 00000000h
@Interface . . . . . . . . . . . Number 00000003h
@Model . . . . . . . . . . . . . Number 00000007h
@code . . . . . . . . . . . . . Text _TEXT
@data . . . . . . . . . . . . . Text FLAT
@fardata? . . . . . . . . . . . Text FLAT
@fardata . . . . . . . . . . . . Text FLAT
@stack . . . . . . . . . . . . . Text FLAT
sum . . . . . . . . . . . . . . DWord 00000000 _DATA 0 Warnings0 Errors
调试对比猜想:
***6.AddVariables 程序
修改 AddVariables 程序使其使用 64 位变量。描述汇编器产生的语法错误,并说明为解决这些错误采取的措施。
答:修改相应变量后,编译报错:
根据错误提示,修改相关代码:
去掉这3行
.386
.model flat, stdcall
.stack 4096
把 ExitProcess PROTO, dwExitCode:DWORD 改成 ExitProcess PROTO
把 INVOKE ExitProcess, 0 改成 call ExitProcess
把 END main 改成 END
修改后,完整的代码如下
;3.10_6_AddVariables.asm ***6.AddVariables 程序
;修改 AddVariables 程序使其使用 64 位变量。描述汇编器产生的语法错误,并说明为解决这些错误采取的措施。ExitProcess PROTO.data
firstval QWORD 20002000h
secondval QWORD 11111111h
thirdval QWORD 22222222h
sum QWORD 0.code
main PROCmov rax, firstvaladd rax, secondvaladd rax, thirdvalmov sum, raxcall ExitProcess
main ENDP
END
运行调试: