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;
}
131、展示调用 mWriteString 宏的语句 mWriteStr namePrompt 生成的展开代码
push edx
mov edx, OFFSET namePrompt
call WriteString
pop edx
132、编写一个名为mDumpMemx的宏,它接受一个参数,即变量的名称。该宏需要调用一个名为mDumpMem的宏,并将变量的偏移量、单元数量和单元大小传递给它。展示对mDumpMemx宏的调用。
以下是实现
mDumpMemx
宏的代码:
mDumpMemx MACRO varName:REQ
mDumpMem OFFSET varName, LENGTHOF varName, TYPE varName
ENDM
示例调用
.data
array DWORD 1000h,2000h,3000h,4000h
.code
mDumpMemx array
133、给出一个宏参数具有默认参数初始化的示例。
例如,
mWriteln
宏可以提供一个包含单个空格的字符串作为其默认参数。其定义如下:
mWriteln MACRO text:=<" ">
mWrite text
call Crlf
ENDM
若调用时不提供参数,它仍会打印一个空格并换行。
134、编写一个使用IF、ELSE和ENDIF指令的简短示例。
示例代码如下:
.data
X DWORD 0
op1 DWORD 2 ; test data
op2 DWORD 4 ; test data
op3 DWORD 5 ; test data
.code
mov eax,op1
mov ebx,op2
mov ecx,op3
.WHILE eax < ebx
inc eax
.IF eax == ecx
mov X,2
.ELSE
mov X,3
.ENDIF
.ENDW
135、编写一个简短的宏,展示当宏参数嵌入到字面字符串中时 & 运算符的使用。
以下是一个示例宏:
ShowWarning MACRO message
mWrite "&message"
ENDM
该宏接收一个文本参数
message
,使用
&
运算符将其嵌入到字面字符串中,然后传递给
mWrite
宏。
136、假设以下 mLocate 宏定义:mLocate MACRO xval,yval IF xval LT 0 ;; xval < 0? EXITM ;; 如果是,退出 ENDIF IF yval LT 0 ;; yval < 0? EXITM ;; 如果是,退出 ENDIF mov bx,0 ;; 视频页面 0 mov ah,2 ;; 定位光标 mov dh,yval mov dl,xval int 10h ;; 调用 BIOS ENDM 展示以下每条语句展开宏时预处理器生成的源代码:.data row BYTE 15 col BYTE 60.code mLocate -2,20 mLocate 10,20 mLocate col,row
mLocate -2,20
:由于
xval = -2 < 0
,宏执行
EXITM
退出,无代码生成。
mLocate 10,20
:
asm
mov bx,0
mov ah,2
mov dh,20
mov dl,10
int 10h
mLocate col,row
:
asm
mov bx,0
mov ah,2
mov dh,row
mov dl,col
int 10h
137、创建一个宏,该宏等待用户按键并返回所按的键。该宏应包含用于存储 ASCII 码和键盘扫描码的参数。编写一个程序来测试你的宏。例如,以下代码等待按键;当返回时,两个参数将包含 ASCII 码和扫描码:.data ascii BYTE?.code mReadkey ascii, scan
以下是实现该功能的代码:
; mReadkey 宏定义
mReadkey MACRO ascii, scan
pushad
call ReadChar
mov ascii, al
mov scan, ah
popad
ENDM
; 测试程序
INCLUDE Irvine32.inc
.data
ascii BYTE ?
scan BYTE ?
.code
main PROC
mReadkey ascii, scan ; 可以在这里添加代码来显示 ascii 和 scan 的值以验证结果
exit
main ENDP
END main
上述代码定义了
mReadkey
宏,它调用
ReadChar
函数获取按键的 ASCII 码和扫描码,并将它们分别存储到传入的参数中。然后编写了一个简单的测试程序来调用该宏。
138、编写一个名为 mMove32 的宏,该宏接收两个 32 位内存操作数。宏应将源操作数移动到目标操作数。编写一个程序来测试你的宏。
以下是实现
mMove32
宏及测试程序的示例代码:
.MODEL SMALL
.STACK 100H
.DATA
source DWORD 12345678h
destination DWORD ?
.CODE
MAIN PROC
MOV AX, @DATA
MOV DS, AX
; 定义 mMove32 宏
mMove32 MACRO dest, src
MOV EAX, src
MOV dest, EAX
ENDM
; 调用 mMove32 宏
mMove32 destination, source
; 程序结束
MOV AH, 4CH
INT 21H
MAIN ENDP
END MAIN
上述代码定义了
mMove32
宏,它将源操作数的值复制到目标操作数。测试程序初始化了一个源操作数,并调用该宏将其值移动到目标操作数。最后程序正常结束。
139、创建一个宏,该宏等待用户按键并返回按下的键。宏应包含用于存储 ASCII 码和键盘扫描码的参数。编写一个程序来测试你的宏。例如,以下代码等待按键;返回时,两个参数将包含 ASCII 码和扫描码:.data ascii BYTE ? scan BYTE ? .code mReadkey ascii, scan
以下是实现该功能的代码:
; mReadkey Macro
INCLUDE Irvine32.inc
; 定义 mReadkey 宏
mReadkey MACRO ascii, scan
pushad
call ReadChar
mov al, al ; ASCII 码在 AL 中
mov [ascii], al
mov ah, ah ; 扫描码在 AH 中
mov [scan], ah
popad
ENDM
; 测试程序
.data
ascii BYTE ?
scan BYTE ?
.code
main PROC
mReadkey ascii, scan ; 可在此处添加代码来验证 ascii 和 scan 的值
exit
main ENDP
END main
上述代码定义了
mReadkey
宏,它调用
ReadChar
函数获取按键信息,并将 ASCII 码和扫描码分别存储到传入的参数中。然后编写了一个简单的测试程序来调用该宏。
140、创建一个宏,该宏将一个以空字符结尾的字符串以给定的文本颜色写入控制台。宏的参数应包括字符串名称和颜色。提示:调用SetTextColor函数。编写一个程序,用几种不同颜色的字符串测试你的宏。示例调用:.data myString db “Here is my string”,0 .code mWritestring myString, white
根据描述,我们需要创建一个名为
mWritestringAttr
的宏,该宏接收字符串名称和颜色作为参数,将字符串以指定颜色输出到控制台。以下是实现该宏的代码示例:
; mWritestringAttr.asm
INCLUDE Irvine32.inc
; 定义mWritestringAttr宏
mWritestringAttr MACRO strName, color
; 设置文本颜色
push eax
mov eax, color
call SetTextColor
pop eax
; 输出字符串
push edx
mov edx, OFFSET strName
call WriteString
pop edx
ENDM
.data
myString1 db "This is a red string",0
myString2 db "This is a green string",0
myString3 db "This is a blue string",0
red EQU 4
green EQU 2
blue EQU 1
.code
main PROC
; 测试mWritestringAttr宏
mWritestringAttr myString1, red
call Crlf
mWritestringAttr myString2, green
call Crlf
mWritestringAttr myString3, blue
call Crlf
call WaitMsg
INVOKE ExitProcess,0
main ENDP
END main
上述代码定义了一个名为
mWritestringAttr
的宏,它接收字符串名称和颜色作为参数。在宏内部,首先调用
SetTextColor
函数设置文本颜色,然后使用
WriteString
函数输出字符串。在主程序中,我们定义了三个不同颜色的字符串,并使用
mWritestringAttr
宏将它们以不同的颜色输出到控制台。
141、模拟教授在校园里进行随机游走,程序需要在某个随机时间间隔,将手机丢在教授当时所在的位置,且每次运行程序时,手机丢失的时间间隔和位置都不同,该如何实现?
该描述提出了一个编程任务,即在模拟教授随机游走的程序中,要在随机时间点将手机丢在教授当时所处的位置,且每次运行程序手机丢失的时间和位置都不同。可在模拟随机游走的代码基础上,添加随机时间判断逻辑,在随机选定的时间点记录教授当时的位置作为手机丢失位置。
142、哪个Win32函数返回标准输入的句柄?
GetStdHandle
函数可用于返回标准输入、输出或错误输出控制台流的句柄。若要获取标准输入句柄,可调用
GetStdHandle
函数并传入
STD_INPUT_HANDLE
参数。
143、哪个Win32函数从键盘读取一串文本并将其放入缓冲区?
ReadConsole函数
144、描述 COORD 结构。
COORD 结构在 Windows API 中定义,用于标识屏幕的 X 和 Y 坐标。该结构包含两个字段:X 和 Y,其中 X 字段相对于结构起始位置的偏移量为 0,Y 字段的偏移量为 2。其定义如下:
```text
COORD STRUCT
X WORD ? ; offset 00
Y WORD ? ; offset 02
COORD ENDS
145、哪个Win32函数可以更改控制台窗口的标题?
SetConsoleTitle函数可以更改控制台窗口的标题。
146、哪个Win32函数可以让你改变屏幕缓冲区的尺寸?
SetConsoleScreenBufferSize函数可以设置屏幕缓冲区的尺寸为X列Y行。
147、哪个Win32函数可以让你改变后续文本输出的颜色?
SetConsoleTextAttribute
函数可以改变后续文本输出到控制台窗口的前景色和背景色。
148、哪个Win32函数可以让程序暂停指定的毫秒数?
Win32的Sleep函数可以让程序暂停指定的毫秒数。
149、列举两个调用MessageBox函数时可以使用的按钮常量。
MB_RETRYCANCEL、MB_ABORTRETRYIGNORE
150、调用MessageBox函数时可以使用的两个图标常量是什么?
MB_ICONSTOP、MB_ICONQUESTION
151、列举WinMain(启动)过程执行的至少三项任务。
获取当前进程的句柄;
加载图标和鼠标光标;
注册程序的主窗口;
创建主窗口;
显示并更新主窗口;
开始接收和分发消息的消息循环。
152、描述示例程序中WinProc过程的作用。
WinProc过程接收并处理所有与窗口相关的事件消息,多数事件由用户操作(如鼠标点击、拖动,按键等)触发。该过程负责解码每条消息,若消息被识别,则执行与该消息相关的面向应用程序的任务。对于未处理的消息,会传递给默认消息处理程序
DefWindowProc
。在示例程序中,该过程处理
WM_LBUTTONDOWN
、
WM_CREATE
和
WM_CLOSE
这三种特定消息。
153、示例程序中WinProc过程处理哪些消息?
示例程序中
WinProc
过程处理以下三种消息:
WM_LBUTTONDOWN
(用户按下鼠标左键时生成)
WM_CREATE
(主窗口刚创建)
WM_CLOSE
(应用程序主窗口即将关闭)
154、描述示例程序中 ErrorHandler 过程的作用。
当系统在程序主窗口的注册和创建过程中报告错误时会调用
ErrorHandler
过程,它有几个重要任务:
调用
GetLastError
检索系统错误号;
调用
FormatMessage
检索适当的系统格式化错误消息字符串;
调用
MessageBox
显示包含错误消息字符串的弹出消息框;
调用
LocalFree
释放错误消息字符串使用的内存。
155、描述线性地址。
线性地址是一个范围在0到FFFFFFFh之间的32位整数,用于指代一个内存位置。若名为分页的功能被禁用,线性地址也可能是目标数据的物理地址。
在x86处理器中,首先将段值与变量的偏移量相结合创建线性地址,每个段选择器指向一个段描述符,其中包含内存段的基地址,将逻辑地址的32位偏移量与段的基地址相加,生成32位线性地址。
156、分页技术与线性内存有什么关系?
操作系统中的分页功能
操作系统如 MS-Windows 和 Linux 采用分页功能,允许程序使用比计算机物理可用内存更多的线性内存。
当程序尝试访问线性地址空间中的某个地址时,处理器会自动将线性地址转换为物理地址,此转换称为
页面转换
。
若请求的页面当前不在内存中,会发生
页面错误
,操作系统会将所需页面从磁盘复制到内存。
157、分页有什么优势?
分页是x86处理器的重要特性,它使计算机能够运行原本无法全部装入内存的程序组合。处理器通过先仅将程序的一部分加载到内存中,其余部分保留在磁盘上实现这一点。
158、哪个寄存器包含局部描述符表的基地址?
LDTR寄存器包含程序局部描述符表(LDT)的地址。
159、哪个寄存器包含全局描述符表的基地址?
GDTR(全局描述符表寄存器)
160、可以存在多少个全局描述符表?
当操作系统在启动时将处理器切换到保护模式时,会创建一个全局描述符表,即全局描述符表只有一个。
161、说出段描述符中至少四个字段的名称。
基地址、特权级别、段类型、段存在标志、粒度标志、段界限
162、请展示一个调用ReadConsole函数的示例。
下面是给定的【文本内容】:
示例代码如下:
```asm
; 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
163、给出一个调用WriteConsole函数的示例。
以下是调用
WriteConsole
函数的示例:
INVOKE WriteConsole, consoleHandle, ; console output handle
ADDR message, ; string pointer
messageSize, ; string length
ADDR bytesWritten, ; returns num bytes written
0 ; not used
164、请给出一个调用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
165、请给出一个调用CreateFile函数的示例,该示例将创建一个具有普通属性的新文件,并覆盖任何同名的现有文件。
INVOKE CreateFile, ADDR filename, GENERIC_WRITE, DO_NOT_SHARE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0
166、给定二进制浮点数 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
167、为什么十进制的 0.2 不能用有限位数的二进制位精确表示?
将十进制小数转换为二进制小数通常采用
乘 2 取整法
。
对于
0.2
转换为二进制,不断乘以 2 取整数部分,会发现其结果是一个
无限循环
的二进制小数。
从计算角度来说,把十进制小数转换为二进制的过程中,若用除法来理解,从
10000
除以
1010
开始计算,余数为
110
,补零后继续计算,当新被除数再次为
10000
时,商的比特序列开始重复(
0011...
),所以无法找到精确的商,即
0.2
不能用有限位数的二进制位表示。
168、NaN有哪两种类型?
NaN有两种类型,分别是
安静型NaN
(quiet NaN)和
信令型NaN
(signaling NaN)。
169、FSTP指令与FST指令有何不同?
FST(存储浮点值)指令将浮点操作数从FPU栈顶复制到内存,不弹出栈;
FSTP(存储浮点值并弹出)指令将ST(0)中的值复制到内存,然后将ST(0)从栈中弹出。
170、FADD 指令可以使用哪些类型的操作数?
FADD 指令可使用的操作数类型包括:
无操作数
m32fp(REAL4 内存操作数)
m64fp(REAL8 操作数)
寄存器操作数 ST(0)、ST(i)
171、FISUB指令与FSUB指令有何不同?
FISUB
指令会先将源操作数转换为双扩展精度浮点格式,再从
ST(0)
中减去该操作数;
FSUB
指令是直接从目的操作数中减去源操作数,并将差值存储在目的位置,操作数为浮点类型。
172、FPU控制字中的哪个字段可以更改处理器的舍入模式?
10 – 11位(Rounding control)
173、请给出二进制数 +1110.011 的 IEEE 单精度编码。
IEEE 单精度编码计算步骤
要得到
+1110.011
的 IEEE 单精度编码,步骤如下:
确定符号位
:因为是正数,符号位为
0
。
规范化二进制数
:
+1110.011 = +1.110011×2³
。
计算指数
:指数为
3
,加上偏移量
127
后得到
130
,二进制表示为
10000010
。
确定尾数
:去掉规范化后的
1
,尾数为
110011
,不足
23
位补
0
,得到
11001100000000000000000
。
组合编码
:将符号位、指数位和尾数位组合,得到
0 10000010 11001100000000000000000
。
所以,
+1110.011
的 IEEE 单精度编码是:
0 10000010 11001100000000000000000
174、编写一个双指令序列,将浮点运算单元(FPU)状态标志移至EFLAGS寄存器。
使用
FNSTSW
指令将FPU状态字移到
AX
,然后使用
SAHF
指令将
AH
复制到
EFLAGS
寄存器,具体代码为:
fnstsw ax
sahf
175、给定精确结果 1.010101101,使用 FPU 的默认舍入方法将其舍入为 8 位尾数。
FPU 的默认舍入方法是向最接近的偶数舍入。
比较
1.01010110
和
1.01010111
与
1.010101101
的距离,
1.01010110
更接近
1.010101101
,且末位为
0
是偶数。
所以答案是
1.01010110
。
176、将以下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赋予一系列值来测试程序逻辑。
以下是实现上述C++代码的汇编语言代码示例:
.386
.model flat,stdcall
.stack 4096
; 包含Irvine32库
include Irvine32.inc
includelib Irvine32.lib
.data
X REAL8 0.0
Y REAL8 0.0
msg1 BYTE 'X is lower', 0dh, 0ah, 0
msg2 BYTE 'X is not lower', 0dh, 0ah, 0
.code
main PROC
; 给X和Y赋值,可修改这些值进行测试
fld X
fld Y
fcomip st(1), st(0) ; 比较X和Y
jl printXLower ; X >= Y
mov edx, OFFSET msg2
call WriteString
jmp endProgram
printXLower:
; X < Y
mov edx, OFFSET msg1
call WriteString
endProgram:
exit
main ENDP
END main
此代码使用Irvine32库进行控制台输出,通过
fcomip
指令比较
X
和
Y
的大小,根据比较结果调用
WriteString
输出相应信息。可修改
X
和
Y
的初始值来测试程序逻辑。
177、使用 __fastcall 调用约定时,如果内联汇编代码修改寄存器会发生什么?
使用
__fastcall
调用约定时,编译器会使用寄存器传递参数。若内联汇编代码修改寄存器,可能会导致寄存器冲突。此外,如果修改过多寄存器,可能使编译器无法对同一过程中的 C++ 代码进行充分优化,因为优化需要使用寄存器。
178、当对一个32位整数数组应用LENGTHOF运算符时,会返回什么值?
LENGTHOF运算符会统计数组中的元素数量。对于32位整数数组,若数组定义为
.data
array3 DWORD 1,2,3,4
则
LENGTHOF array3
返回
4
。
若使用嵌套
DUP
运算符定义数组,
LENGTHOF
返回两个计数器的乘积。
若数组跨多行定义,
LENGTHOF
只将第一行的数据视为数组的一部分;若第一行以逗号结尾并延续到下一行,则将所有数据视为数组的一部分。
179、当 SIZE 运算符应用于长整型数组时,返回什么值?
应用于长整型数组时,
SIZE
运算符返回数组元素个数乘以元素类型大小的值。
例如,对于包含 10 个元素的长整型数组:
长整型元素类型大小通常为 4 字节
SIZE
运算符返回
40
(即 10 × 4)
180、标准 C 语言 printf() 函数的有效汇编语言 PROTO 声明是什么?
printf PROTO C, pString:PTR BYTE, args:VARARG