table {
border-collapse: collapse;
width: 100%;
margin-bottom: 1rem;
}
th, td {
border: 1px solid #ddd;
padding: 8px;
text-align: left;
}
th {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f9f9f9;
}
pre {
background-color: #f8f8f8;
padding: 15px;
border-radius: 4px;
overflow-x: auto;
}
132、编写一个名为 mMove32 的宏,该宏接收两个 32 位内存操作数。宏应将源操作数移动到目标操作数。编写一个程序来测试你的宏。
以下是实现
mMove32
宏及测试程序的示例代码(使用 MASM 语法):
.MODEL SMALL
.STACK 100H
.DATA
source DWORD 12345678h
destination DWORD ?
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
; 调用 mMove32 宏
mMove32 destination, source
; 退出程序
MOV AH, 4CH
INT 21H
MAIN ENDP
; 定义 mMove32 宏
mMove32 MACRO dest, src
MOV EAX, src
MOV dest, EAX
ENDM
END MAIN
此代码定义了
mMove32
宏,它接收两个 32 位内存操作数,将源操作数移动到目标操作数。测试程序中使用该宏将
source
的值移动到
destination
中。
133、创建一个宏,该宏等待用户按键并返回按下的键。该宏应包含用于存储 ASCII 码和键盘扫描码的参数。编写一个程序来测试你的宏。例如,以下代码等待按键;当返回时,两个参数将包含 ASCII 码和扫描码:.data ascii BYTE ? scan BYTE ? .code mReadkey ascii, scan
以下是实现该功能的代码:
; mReadkey 宏定义
mReadkey MACRO ascii, scan
call ReadChar
mov ascii, al
mov scan, ah
ENDM
; 测试程序
INCLUDE Irvine32.inc
.data
ascii BYTE ?
scan BYTE ?
.code
main PROC
mReadkey ascii, scan ; 调用宏获取按键信息
; 显示 ASCII 码和扫描码
movzx eax, ascii
call WriteDec
mov al, ' '
call WriteChar
movzx eax, scan
call WriteDec
call Crlf
exit
main ENDP
END main
上述代码定义了
mReadkey
宏,该宏调用
ReadChar
函数获取按键信息,并将 ASCII 码和扫描码分别存储到传入的参数中。然后编写了一个测试程序,调用该宏并显示获取到的 ASCII 码和扫描码。
134、模拟教授在校园里进行随机游走的过程,要求程序在某个随机的时间间隔,将手机丢弃在教授当时所在的位置,且每次运行程序时,手机丢失的时间间隔和位置都不同。
该描述提出了一个编程要求,即在模拟教授随机游走的程序中,要在随机时间间隔让教授丢失手机,且每次运行程序手机丢失的时间和位置都不同。可在模拟随机游走的代码基础上,添加生成随机时间点的逻辑,当达到该时间点时记录教授当前位置作为手机丢失位置。
135、哪个Win32函数返回标准输入的句柄?
GetStdHandle
函数返回标准输入、输出或错误输出的句柄,当传入参数为
STD_INPUT_HANDLE
时可返回标准输入的句柄。
136、哪个Win32函数从键盘读取一串文本并将其放入缓冲区?
ReadConsole
137、描述COORD结构。
`COORD`结构在Windows API中定义,用于标识屏幕的X和Y坐标。该结构包含两个字段:
- `X`:字段相对于结构起始位置的偏移量为 `00`
- `Y`:字段相对于结构起始位置的偏移量为 `02`
其定义如下:
```text
COORD STRUCT
X WORD ? ; offset 00
Y WORD ? ; offset 02
COORD ENDS
##138、哪个Win32函数可以将文件指针移动到相对于文件开头的指定偏移位置?
SetFilePointer
##139、哪个Win32函数可以更改控制台窗口的标题?
SetConsoleTitle函数可以更改控制台窗口的标题。
##140、哪个Win32函数可以让你改变屏幕缓冲区的尺寸?
SetConsoleScreenBufferSize函数可以让你将屏幕缓冲区的大小设置为X列Y行。
##141、哪个Win32函数可以让你改变后续文本输出的颜色?
SetConsoleTextAttribute函数可以改变后续文本输出的颜色。
##142、哪个Win32函数可以让程序暂停指定的毫秒数?
Win32的Sleep函数可以让程序暂停指定的毫秒数。
##143、请说出调用 MessageBox 函数时可以使用的两个按钮常量。
MB_RETRYCANCEL、MB_ABORTRETRYIGNORE
##144、列举两个调用MessageBox函数时可以使用的图标常量。
MB_ICONSTOP、MB_ICONQUESTION
##145、列举WinMain(启动)过程执行的至少三项任务。
- 获取当前进程的句柄;
- 加载图标和鼠标光标;
- 注册程序的主窗口;
- 创建主窗口;
- 显示并更新主窗口;
- 开始一个接收和分发消息的消息循环。
##146、描述示例程序中WinProc过程的作用。
```markdown
WinProc过程接收并处理与窗口相关的所有事件消息,多数事件由用户操作(如鼠标点击、拖动,键盘按键等)发起。该过程负责解码每条消息,若消息被识别,则执行与该消息相关的面向应用程序的任务。对于未处理的消息,会传递给MS-Windows的默认消息处理程序`DefWindowProc`。在示例程序中,它处理`WM_LBUTTONDOWN`、`WM_CREATE`和`WM_CLOSE`三种特定消息。
147、示例程序中WinProc过程处理哪些消息?
示例程序中
WinProc
过程处理以下三种消息:
WM_LBUTTONDOWN
(用户按下鼠标左键时生成)
WM_CREATE
(主窗口刚刚创建)
WM_CLOSE
(应用程序主窗口即将关闭)
148、描述示例程序中 ErrorHandler 过程的作用。
当系统在程序主窗口的注册和创建过程中报告错误时,可选的
ErrorHandler
过程会被调用。它有几个重要任务:
调用
GetLastError
获取系统错误编号;
调用
FormatMessage
获取系统格式化的错误消息字符串;
调用
MessageBox
显示包含错误消息字符串的弹出消息框;
调用
LocalFree
释放错误消息字符串使用的内存。
149、调用CreateWindow后立即激活的消息框是在应用程序主窗口之前还是之后出现?
在调用
CreateWindowEx
创建主窗口后,会保存窗口句柄、显示并绘制窗口,之后才显示问候消息框,所以消息框在应用程序主窗口之后出现。
150、描述线性地址。
线性地址是一个32位整数,范围在0到FFFFFFFh之间,用于引用一个内存位置。
若“分页”功能禁用,线性地址就是目标数据的物理地址。
在x86处理器中,通过将段值与变量偏移量相结合可创建线性地址。
像MS-Windows和Linux等操作系统使用分页功能,需通过页面转换将线性地址转换为物理地址。
151、分页与线性内存有何关系?
操作系统如 MS-Windows 和 Linux 采用分页功能,允许程序使用比计算机物理可用内存更多的线性内存。
当程序尝试访问线性地址空间中的某个地址时,处理器会自动将线性地址转换为物理地址,此转换称为
页面转换
。
若请求的页面当前不在内存中,会发生
页面错误
,操作系统会将所需页面从磁盘复制到内存。
152、分页有什么优势?
分页机制
分页是x86处理器的一个重要特性,它使计算机能够运行原本无法全部装入内存的多个程序组合。处理器通过先仅将程序的一部分加载到内存中,其余部分保留在磁盘上实现这一点。
153、哪个寄存器包含局部描述符表的基地址?
LDTR寄存器
154、哪个寄存器包含全局描述符表的基地址?
GDTR(全局描述符表寄存器)包含全局描述符表的基地址。
155、可以存在多少个全局描述符表?
当操作系统在启动期间将处理器切换到保护模式时,会创建一个全局描述符表,即全局描述符表只有一个。
156、说出段描述符中至少四个字段的名称。
基地址、特权级别、段类型、段存在标志、粒度标志、段界限
157、给出一个调用ReadConsole函数的示例。
以下是调用
ReadConsole
函数的示例代码:
; Read From the Console (ReadConsole.asm)
INCLUDE Irvine32.inc
BufSize = 80
.data
buffer BYTE BufSize DUP(?),0,0
stdInHandle HANDLE ?
bytesRead DWORD ?
.code
main PROC
; Get handle to standard input
INVOKE GetStdHandle, STD_INPUT_HANDLE
mov stdInHandle,eax
; Wait for user input
INVOKE ReadConsole, stdInHandle, ADDR buffer, BufSize, ADDR bytesRead, 0
; Display the buffer
mov esi,OFFSET buffer
mov ecx,bytesRead
mov ebx,TYPE buffer
call DumpMem
exit
main ENDP
END main
158、给出一个调用WriteConsole函数的示例。
调用
WriteConsole
函数的示例如下:
INVOKE WriteConsole, consoleHandle, ADDR message, messageSize, ADDR bytesWritten, 0
其中:
consoleHandle
是控制台输出句柄;
ADDR message
是字符串指针;
messageSize
是字符串长度;
ADDR bytesWritten
用于返回实际写入的字节数;
最后一个参数设为
0
,不使用。
159、请给出一个调用CreateFile函数以打开现有文件进行读取的示例。
INVOKE CreateFile, ADDR filename, ; ptr to filename
GENERIC_READ, ; read from the file
DO_NOT_SHARE, ; share mode
NULL, ; ptr to security attributes
OPEN_EXISTING, ; open an existing file
FILE_ATTRIBUTE_NORMAL, ; normal file attribute
0 ; not used
160、请展示一个对CreateFile函数的示例调用,该调用将创建一个具有普通属性的新文件,并覆盖任何同名的现有文件。
INVOKE CreateFile, ADDR filename, GENERIC_WRITE, DO_NOT_SHARE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
161、给定二进制浮点值1101.01101,如何将其表示为十进制分数的和?
1101.01101 = (1 × 2³) + (1 × 2²) + (0 × 2¹) + (1 × 2⁰) + (0 × 2⁻¹) + (1 × 2⁻²) + (1 × 2⁻³) + (0 × 2⁻⁴) + (1 × 2⁻⁵)
= 8 + 4 + 0 + 1 + 0 + 1/4 + 1/8 + 0 + 1/32
= 13 + 8/32 + 4/32 + 1/32
= 13 13/32
162、为什么十进制的0.2不能用二进制的有限位数精确表示?
在将十进制小数转换为二进制小数时,可采用
乘2取整法
。对于十进制的0.2,转换过程如下:
用
1010
(二进制,对应十进制的10)除
10000
(二进制,对应十进制的16),余数是
110
(二进制,对应十进制的6);
添加一个零后新被除数是
1100
(二进制,对应十进制的12),再用
1010
除,余数是
10
(二进制,对应十进制的2);
添加三个零后新被除数是
10000
(二进制,对应十进制的16),与起始被除数相同。
从这一点起,商的位序列开始重复(
0011...
),所以无法找到精确商,即十进制的0.2
不能用二进制的有限位数表示
。
163、NaN有哪两种类型?
两种类型的NaN分别是安静NaN(quiet NaN)和信号NaN(signaling NaN)。
164、FSTP指令与FST指令有何不同?
FST
(存储浮点值)指令将浮点操作数从FPU栈顶复制到内存,
不弹出栈
;
FSTP
(存储浮点值并弹出)指令将ST(0)中的值复制到内存,然后将ST(0)从栈中
弹出
。
165、FADD指令可以使用哪些类型的操作数?
FADD指令可使用的操作数类型有:
无操作数
内存操作数
m32fp
、
m64fp
寄存器操作数
ST(0)
,
ST(i)
和
ST(i)
,
ST(0)
166、FISUB指令与FSUB指令有何不同?
FISUB
指令会先将源操作数转换为双扩展精度浮点格式,再从
ST(0)
中减去该操作数;
FSUB
指令是直接从目的操作数中减去源操作数,并将差值存储在目的位置,处理的是浮点操作数。
167、浮点运算单元(FPU)控制字中的哪个字段可以让你改变处理器的舍入模式?
10 – 11位
168、编写一个双指令序列,将浮点运算单元(FPU)状态标志移入EFLAGS寄存器。
使用FNSTSW指令将FPU状态字移入AX,再使用SAHF指令将AH复制到EFLAGS寄存器。
169、给定精确结果 1.010101101,使用 FPU 的默认舍入方法将其舍入为 8 位有效数字。
该答案错误,FPU 的默认舍入方法是
就近舍入到偶数
。对于
1.010101101
舍入为 8 位有效数字,第 9 位数字是
1
,小于
5
,应直接舍去,结果为
1.0101011
。
170、将以下 C++ 代码用汇编语言实现。用 WriteString 调用替代 printf() 函数调用:double X; double Y; if( X < Y ) printf(“X is lower
”); else printf(“X is not lower
”); (使用 Irvine32 库例程进行控制台输出,而不是调用标准 C 库的 printf 函数。)多次运行该程序,为 X 和 Y 赋予一系列值,以测试程序逻辑。
以下是实现该功能的汇编代码示例:
; 包含 Irvine32 库
INCLUDE Irvine32.inc
.DATA
msgLower BYTE 'X is lower', 0dh, 0ah, 0
msgNotLower BYTE 'X is not lower', 0dh, 0ah, 0
X REAL8 0.0
Y REAL8 0.0
.CODE
main PROC
; 设置 X 和 Y 的值,这里可以修改以测试不同情况
fld X
fld Y
fcomip st(1), st(0) ; 比较 X 和 Y
jl lowerCase ; 如果 X < Y,跳转到 lowerCase
; X >= Y
mov edx, OFFSET msgNotLower
call WriteString
jmp endProgram
lowerCase:
mov edx, OFFSET msgLower
call WriteString
endProgram:
exit
main ENDP
END main
代码说明:
数据段(.DATA)
:
– 定义了两个消息字符串
msgLower
和
msgNotLower
。
– 定义了两个双精度浮点数变量
X
和
Y
。
代码段(.CODE)
:
–
main
过程是程序的入口点。
– 使用
fld
指令将
X
和
Y
加载到浮点寄存器栈中。
–
fcomip
指令比较
X
和
Y
,并根据比较结果设置标志位。
–
jl
指令用于判断是否
X < Y
,如果是则跳转到
lowerCase
标签处。
– 如果
X >= Y
,则显示
msgNotLower
消息。
– 如果
X < Y
,则显示
msgLower
消息。
– 最后使用
exit
指令结束程序。
测试:
你可以修改
X
和
Y
的初始值,多次运行程序,以测试不同情况下的逻辑是否正确。
171、编写一个程序,在FPU栈上压入两个或更多值,通过调用ShowFPUStack显示栈,显示每个FPU数据寄存器的标签值,并显示对应于ST(0)的寄存器编号。标签寄存器使用2位来指示每个FPU寄存器中内容的类型。可以通过调用FSTENV指令来加载标签字,该指令会填充一个保护模式结构(FPU_ENVIRON结构体),此结构体包含控制字、状态字、标签字等成员。对于显示对应于ST(0)的寄存器编号,调用FSTSW指令将状态字保存到一个16位整数变量中,并从第11到13位提取栈顶指示器。使用以下示例输出作为参考。示例输出显示ST(0)是R6,因此ST(1)是R7,两者都包含有效的浮点数。
以下是一个满足需求的程序的思路,但由于无法直接提供完整代码,仅给出逻辑步骤:
初始化程序环境。
在FPU栈上压入两个或更多值。
调用
ShowFPUStack
函数显示FPU栈。
调用
FSTENV
指令将FPU环境信息加载到
FPU_ENVIRON
结构体中,从而获取标签字。
解析标签字,显示每个FPU数据寄存器的标签值。
调用
FSTSW
指令将状态字保存到一个16位整数变量中。
从该变量的第11到13位提取栈顶指示器,显示对应于
ST(0)
的寄存器编号。
172、在内联汇编代码中,能否同时使用 DW 和 DUP 运算符来定义变量?
不能。编写内联汇编代码时,不能使用数据定义指令,如 DB(字节)和 DW(字)。
173、使用__fastcall调用约定时,如果内联汇编代码修改寄存器会发生什么?
可能导致寄存器冲突,因为
__fastcall
关键字会使编译器使用寄存器传递参数。此外,如果修改过多寄存器,可能使编译器无法对同一过程中的 C++ 代码进行充分优化,因为优化需要使用寄存器。
174、将LENGTH运算符应用于32位整数数组时,会返回什么值?
LENGTH
运算符应指
LENGTHOF
,它会返回数组中的元素数量。例如,有一个
DWORD
类型(即32位整数)数组定义为
DWORD 1,2,3,4
,那么
LENGTHOF
该数组返回4。
175、标准C语言printf( )函数的有效汇编语言PROTO声明是什么?
printf PROTO C, pString:PTR BYTE, args:VARARG
176、当调用以下 C 语言函数时,参数 x 是先入栈还是后入栈?void MySub(int x, int y, int z);
后入栈。C 编译器按逆序将参数压入栈,所以 x 最后入栈。
177、在从 C++ 调用的过程中,extern 声明里的 “C” 限定符的作用是什么?
在从 C++ 调用的过程中,`extern` 声明里的 “C” 限定符主要有以下作用:
- 当从汇编语言代码调用 C++ 函数时,使用 “C” 和 `extern` 关键字定义函数,是为了让汇编语言能正确调用 C++ 函数。
- 将多个函数原型分组在一个块内使用 `extern "C"`,可在单个函数实现中省略 `extern` 和 “C”。
- 在汇编模块中声明要调用的外部 C 或 C++ 函数时,`PROTO` 指令添加 C 限定符,是因为链接器需将函数名和参数列表与 C++ 模块导出的函数匹配,且汇编器要用 C 调用约定在函数调用后清理堆栈。
- C 语言限定符要求过程参数从后往前压入栈,调用者负责在过程调用后从栈中移除参数,即在调用程序中向 `ESP` 添加常量,将其重置为压入参数前的值,同时该限定符会在外部过程名前添加下划线。
178、从C++调用外部汇编语言程序时,名称修饰为何重要?
名称修饰是标准的 C++ 编译器技术,用于支持函数重载。C++ 编译器在生成可执行文件时,会让链接器查找修饰后的名称而非原始名称。如果不进行名称修饰处理(如在 C++ 中调用汇编语言程序时未加
"C"
限定符),链接器可能无法找到正确的函数名,导致无法生成可执行程序。
179、编写一个名为LastIndexOf的函数,该函数接收一个要搜索的值、一个整数数组的指针以及数组元素的数量作为参数。函数从数组末尾开始反向搜索,返回第一个匹配值的索引,如果未找到匹配项,则返回 -1。
以下是修改后的
LastIndexOf
函数的汇编代码实现:
; LastIndexOf function (LastIndexOf.asm)
.586
.model flat, c
LastIndexOf PROTO, srchVal:DWORD, arrayPtr:PTR DWORD, count:DWORD
.code
;-----------------------------------------------
LastIndexOf PROC USES ecx esi edi, srchVal:DWORD, arrayPtr:PTR DWORD, count:DWORD
; ; Performs a backward linear search of a 32-bit integer array,
; ; looking for a specific value. If the value is found,
; ; the matching index position is returned in EAX;
; ; otherwise, EAX equals -1;
;-----------------------------------------------
NOT_FOUND = -1
mov eax, srchVal ; search value
mov ecx, count ; array size
dec ecx ; start from the last index
mov esi, arrayPtr ; pointer to array
mov edi, ecx ; index
L1:
cmp [esi + edi * 4], eax
je found
dec edi
cmp edi, 0
jge L1
notFound:
mov al, NOT_FOUND
jmp short exit
found:
mov eax, edi
exit:
ret
LastIndexOf ENDP
END
在上述代码中,函数
LastIndexOf
从数组的最后一个元素开始反向搜索指定的值
srchVal
。如果找到匹配的值,则返回该值的索引;如果未找到匹配的值,则返回 -1。
180、文本匹配程序:编写一个程序,打开一个最多包含60K字节的文本文件,并对一个字符串进行不区分大小写的搜索。字符串和文件名可由用户输入。显示文件中出现该字符串的每一行,并在每行前面加上行号。你的程序必须在实地址模式下运行。
文本文件字符串搜索程序实现
该题要求编写程序实现对文本文件的字符串不区分大小写搜索,需用户输入字符串和文件名,输出包含该字符串的行并加上行号,程序要在实地址模式下运行。
实现步骤
可按以下步骤实现:
获取用户输入的文件名和待搜索字符串
打开文件
逐行读取文件内容
进行不区分大小写的搜索
若找到匹配字符串,显示行号和该行内容
关闭文件
181、编写一个名为ShowFileAttributes的过程,该过程在DX中接收文件名的偏移量,并在控制台窗口中显示文件的属性。需要查找的属性有普通、隐藏、只读和系统属性。提示:使用INT 21h功能7143h。编写一个程序调用ShowFileAttributes过程,并传递一个文件名。在运行程序之前,通过在Windows资源管理器中右键单击文件名,选择“属性”,并勾选“隐藏”和“只读”选项来设置文件属性。或者,你也可以从Windows命令提示符运行Attrib命令。运行你的程序并验证属性显示是否正确。
以下是实现该功能的代码示例:
.MODEL SMALL
.STACK 100H
.DATA
filename DB 'test.txt', 0
msg_normal DB 'Normal', 0DH, 0AH, 0
msg_hidden DB 'Hidden', 0DH, 0AH, 0
msg_readonly DB 'Read - Only', 0DH, 0AH, 0
msg_system DB 'System', 0DH, 0AH, 0
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
MOV DX, OFFSET filename
CALL ShowFileAttributes
MOV AH, 4CH
INT 21H
MAIN ENDP
ShowFileAttributes PROC
MOV AX, 7143H
MOV BX, 0
MOV DX, [BP + 4] ; 假设参数通过栈传递
INT 21H
JC ERROR
MOV CX, AX ; 保存属性到CX
; 检查普通属性
CMP CX, 0000H
JE SHOW_NORMAL
; 检查隐藏属性
TEST CX, 0002H
JNE SHOW_HIDDEN
; 检查只读属性
TEST CX, 0001H
JNE SHOW_READONLY
; 检查系统属性
TEST CX, 0004H
JNE SHOW_SYSTEM
JMP END_PROC
SHOW_NORMAL:
MOV AH, 09H
MOV DX, OFFSET msg_normal
INT 21H
JMP END_PROC
SHOW_HIDDEN:
MOV AH, 09H
MOV DX, OFFSET msg_hidden
INT 21H
JMP END_PROC
SHOW_READONLY:
MOV AH, 09H
MOV DX, OFFSET msg_readonly
INT 21H
JMP END_PROC
SHOW_SYSTEM:
MOV AH, 09H
MOV DX, OFFSET msg_system
INT 21H
JMP END_PROC
ERROR:
; 错误处理代码
JMP END_PROC
END_PROC:
RET
ShowFileAttributes ENDP
END MAIN
上述代码定义了一个
ShowFileAttributes
过程,它使用
INT 21h
功能
7143h
来获取文件的属性,然后根据属性值显示相应的属性信息。主程序调用该过程并传递文件名。