《汇编语言:基于X86处理器》第3章 复习题和练习,编程练习

本篇记录《汇编语言:基于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 PROC
	mov bx, 7
	add bx, 8
start ENDP

.code
main PROC
	mov eax, 5
	add eax, 6
	mov 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 PROC
	mov eax, dword ptr [LittleEndianDWORD]  ; 加载原始值
    mov ebx, 0                              ; 初始化目标值

    mov edx, eax                            ; 提取最低有效字节
    shl edx, 24                             ; 移动到最高有效字节位置
    or ebx, edx                             ; 合并到目标值 此时bx=78000000h

    mov edx, 0
    mov dl, byte ptr [LittleEndianDWORD+1]  ;edx=00000056h
    shl edx,16                              ;edx=005600h
    or ebx,edx                              ;ebx=78560000h

    mov edx, 0
    mov dl, byte ptr [littleEndianDWORD+2]  ;edx=00000034h
    shl edx, 8                              ;edx=00003400h
    or ebx, edx                             ;ebx=78563400h

    mov edx, 0
    mov dl, byte ptr [littleEndianDWORD+3]  ;edx=00000012h
    or ebx, edx                             ;ebx=78563412h

    mov dword ptr [littleEndianDWORD], ebx  ; 将结果存储到内存地址 littleEndianDWORD

    mov 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 PROC
	mov eax, 5
	add eax, 6
	mov 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 PROC
	mov eax, 0
	add eax, 5
	mov sum, eax 
	
	mov edx, 0
	add edx, 5
	mov sum2, edx

	INVOKE 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 4096
				ExitProcess PROTO, dwExitCode:DWORD

 00000000											.data
 00000000 00000000						sum DWORD 0
 00000004 00000000						sum2 DWORD 0

 00000000											.code 
 00000000											main PROC
 00000000  B8 00000000				mov eax, 0
 00000005  83 C0 05						add eax, 5
 00000008  A3 00000000 R			mov sum, eax 
					
 0000000D  BA 00000000				mov edx, 0
 00000012  83 C2 05						add edx, 5
 00000015  89 15 00000004 R		mov sum2, edx

                    					INVOKE ExitProcess, 0
 0000001B  6A 00	   *	    	push   +000000000h
 0000001D  E8 00000000 E   *	    call   ExitProcess
 00000022			main ENDP
				END main

Microsoft (R) Macro Assembler Version 14.41.34120.0	    06/20/25 16:23:54
3.9.2_5.asm						     Symbols 2 - 1




Segments and Groups:

                N a m e                 Size     Length   Align   Combine Class

FLAT . . . . . . . . . . . . . .	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    Attr

ExitProcess  . . . . . . . . . .	P Near	 00000000 FLAT	Length= 00000000 External STDCALL
main . . . . . . . . . . . . . .	P Near	 00000000 _TEXT	Length= 00000022 Public STDCALL


Symbols:

                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 Warnings
	   0 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 PROC
	mov eax, 10
	mov ebx, 5
	mov ecx, 6
	mov edx, 15

	add 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, eax

	INVOKE 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 PROC
	mov eax, [WorkdayArray]			
	mov ebx, [WorkdayArray+4]		;一个数占32位,4个字节,如果WorkdayArray定义的是BYTE,这里都是加1
	mov 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 PROC
	mov eax, val5
	mov ebx, val6

	INVOKE 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 PROC
	mov eax, 5
	mov 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, 5
	add eax, 6
	mov sum, eax

	INVOKE 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			.data
 00000000 00000000		sum DWORD 0									; 变量定义(机器码: 00 00 00 00)

 00000000			.code
 00000000			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: 立即数6
 00000008  A3 00000000 R			mov sum, eax					; A3: MOV指令 opcode,   00 00 00 00: sum地址(需链接时确定)

												INVOKE ExitProcess,0
 0000000D  6A 00	   *	    		push   +000000000h	; 6A: PUSH立即数 opcode
 0000000F  E8 00000000 E   *	  call   ExitProcess	; E8: CALL相对地址(需重定位)
 00000014									main ENDP
												END main

Microsoft (R) Macro Assembler Version 14.41.34120.0	    06/20/25 23:38:06
3.10_5.asm						     Symbols 2 - 1




Segments and Groups:

                N a m e                 Size     Length   Align   Combine Class

FLAT . . . . . . . . . . . . . .	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    Attr

ExitProcess  . . . . . . . . . .	P Near	 00000000 FLAT	Length= 00000000 External STDCALL
main . . . . . . . . . . . . . .	P Near	 00000000 _TEXT	Length= 00000014 Public STDCALL


Symbols:

                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 Warnings
	   0 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 PROC
	mov rax, firstval
	add rax, secondval
	add rax, thirdval
	mov sum, rax

	call ExitProcess
main ENDP
END

运行调试:

相关推荐
班公湖里洗过脚1 天前
《汇编语言:基于X86处理器》第3章 汇编语言基础
x86汇编语言
班公湖里洗过脚4 天前
《汇编语言:基于X86处理器》第2章 x86处理器架构
x86汇编语言