汇编宏与Win32函数编程实践

内容分享2天前发布
0 0 0

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

来获取文件的属性,然后根据属性值显示相应的属性信息。主程序调用该过程并传递文件名。

© 版权声明

相关文章

暂无评论

none
暂无评论...