汇编排序优化与程序设计

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;
}

40、在冒泡排序过程中添加一个变量,当在内层循环中交换一对值时,将该变量设置为1。如果在对数组进行一次完整遍历期间没有发生交换,则使用此变量提前结束排序(这个变量通常称为交换标志)。

以下是修改后的BubbleSort过程,添加了交换标志变量:


;----------------------------------------------------------
BubbleSort PROC USES eax ecx esi edx, 
   pArray:PTR DWORD,    ; pointer to array
   Count:DWORD          ; array size
;
; Sort an array of 32-bit signed integers in ascending 
; order, using the bubble sort algorithm.
; Receives: pointer to array, array size
; Returns: nothing
;-----------------------------------------------------------
   mov   ecx, Count
   dec   ecx             ; decrement count by 1

L1:
   mov   edx, 0          ; 初始化交换标志为0
   push  ecx             ; save outer loop count
   mov   esi, pArray     ; point to first value

L2:
   mov   eax, [esi]      ; get array value
   cmp   [esi + 4], eax  ; compare a pair of values
   jg    L3              ; if [ESI] <= [ESI + 4], no exchange
   xchg  eax, [esi + 4]  ; exchange the pair
   mov   [esi], eax
   mov   edx, 1          ; 设置交换标志为1

L3:
   add   esi, 4          ; move both pointers forward
   loop  L2              ; inner loop

   pop   ecx             ; retrieve outer loop count
   cmp   edx, 0          ; 检查交换标志
   je    L4              ; 如果没有交换,提前结束排序
   loop  L1              ; else repeat outer loop

L4:
   ret
BubbleSort ENDP

在这个修改后的代码中,添加了一个

edx

寄存器作为交换标志。在内层循环中,当发生交换时,将

edx

设置为1。在外层循环的每次迭代结束时,检查

edx

的值。如果

edx

为0,表示在这次遍历中没有发生交换,程序将跳转到

L4

并提前结束排序。

41、编写程序,生成一个随机的4×4字母矩阵,其中每个字母有50%的概率是元音字母。遍历矩阵的每一行、每一列和每条对角线,生成字母集合。仅显示恰好包含两个元音字母的四字母集合。例如,若生成了如下矩阵:P O A Z A E A U G K A E I A G D ,则程序显示的四字母集合应为POAZ、GKAE、IAGD、PAGI、ZUED、PEAD和ZAKI。集合内字母的顺序不重要。

任务步骤

本题需完成以下步骤编写程序:

生成随机4×4字母矩阵,每个字母有50%概率是元音字母;

遍历矩阵的行、列和对角线生成字母集合;

筛选出恰好包含两个元音字母的四字母集合并显示。

42、创建一个宏,该宏等待用户按键并返回按下的键。该宏应包含用于存储 ASCII 码和键盘扫描码的参数。编写一个程序来测试这个宏。例如,以下代码等待按键;返回时,两个参数将包含 ASCII 码和扫描码:.data ascii BYTE ? scan BYTE ? .code mReadkey ascii, scan

以下是实现该功能的代码示例:


TITLE Testing mReadkey (TestmReadkey.asm)
INCLUDE Irvine32.inc
INCLUDE Macros.inc

; 定义 mReadkey 宏
mReadkey MACRO ascii, scan
LOCAL L1
L1:
    call ReadKey      ; 调用 ReadKey 过程
    jz L1             ; 如果没有按键,继续等待
    mov [ascii], al   ; 存储 ASCII 码
    mov [scan], ah    ; 存储扫描码
ENDM

.code
main PROC
    .data
    ascii BYTE ?      ; 用于存储 ASCII 码
    scan  BYTE ?      ; 用于存储扫描码

    .code
    mReadkey ascii, scan  ; 调用宏等待按键

    ; 可以在这里添加代码来处理存储的 ASCII 码和扫描码
    exit
main ENDP
END main

上述代码定义了

mReadkey

宏,该宏调用

ReadKey

过程等待用户按键,若没有按键则持续等待,有按键时将 ASCII 码存储到

ascii

变量,扫描码存储到

scan

变量。主程序中调用该宏并等待按键,之后可添加代码对存储的 ASCII 码和扫描码进行处理。

43、创建一个宏,用于将一个以空字符结尾的字符串以给定的文本颜色写入控制台。宏的参数应包括字符串名称和颜色。编写一个程序,用几种不同颜色的字符串测试你的宏。示例调用:.data myString db “Here is my string”,0 .code mWritestring myString, white

以下是实现该功能的代码示例:


; Include necessary libraries
INCLUDE Irvine32.inc

; Define the macro
mWriteStringColored MACRO stringName:REQ, color:REQ
    push eax
    mov eax, color
    call SetTextColor
    push edx
    mov edx, OFFSET stringName
    call WriteString
    pop edx
    pop eax
ENDM

; Test program
.data
myString1 BYTE "This is a red string",0
myString2 BYTE "This is a green string",0
myString3 BYTE "This is a blue string",0

.code
main PROC
    ; Set up the console
    call Clrscr

    ; Test the macro with different colors
    mWriteStringColored myString1, red
    call Crlf

    mWriteStringColored myString2, green
    call Crlf

    mWriteStringColored myString3, blue
    call Crlf

    ; Exit the program
    call WaitMsg
    INVOKE ExitProcess,0
main ENDP
END main

这个宏

mWriteStringColored

接收字符串名称和颜色作为参数。它首先调用

SetTextColor

函数设置文本颜色,然后使用

WriteString

函数输出字符串。测试程序中定义了三个不同的字符串,并以不同的颜色输出它们。

44、创建一个名为 mWriteInt 的宏,通过调用 WriteInt 库过程将有符号整数写入标准输出。传递给该宏的参数可以是字节、字或双字。在宏中使用条件运算符,使其能适应参数的大小。编写一个程序来测试该宏,传递不同大小的参数。

以下是实现思路和示例代码。首先创建

mWriteInt

宏,根据参数大小进行条件判断并调用

WriteInt

库过程。以下是示例代码:


; 宏定义
mWriteInt MACRO num
LOCAL done
    mov ax, 0
    mov bx, 0
    cmp TYPE num, 1      ; 检查是否为字节
    je write_byte
    cmp TYPE num, 2      ; 检查是否为字
    je write_word
    cmp TYPE num, 4      ; 检查是否为双字
    je write_dword
    jmp done

write_byte:
    movzx ax, BYTE PTR num
    call WriteInt
    jmp done

write_word:
    mov ax, WORD PTR num
    call WriteInt
    jmp done

write_dword:
    mov eax, DWORD PTR num
    call WriteInt
    jmp done

done:
ENDM

; 测试程序
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD
WriteInt PROTO
.data
byteNum  BYTE -127
wordNum  WORD -32767
dwordNum DWORD -2147483647
.code
main PROC
    mWriteInt byteNum
    mWriteInt wordNum
    mWriteInt dwordNum
    INVOKE ExitProcess, 0
main ENDP
END main

上述代码定义了

mWriteInt

宏,根据参数类型分别处理字节、字和双字,并在测试程序中调用该宏传递不同大小的参数。

45、在测试醉汉行走程序时,会发现醉汉似乎不会离起点太远,这是因为醉汉向任何方向移动的概率相等。现在要修改程序,使醉汉有50%的概率继续沿上一步的相同方向行走,有10%的概率反向行走,有20%的概率向右转或向左转。在循环开始前指定一个默认的起始方向。

程序逻辑调整说明

要实现此修改,需对程序逻辑进行调整。首先在循环开始前指定一个默认的起始方向,然后在每次循环中根据上一步的方向,按照新的概率规则来确定下一步的方向。

以下是修改后的关键代码思路:

定义一个变量来存储上一步的方向。

在循环开始前,指定一个默认的起始方向。

在每次循环中,根据上一步的方向和新的概率规则来确定下一步的方向。

根据确定的方向更新当前的X和Y坐标。

随机方向概率规则

生成一个0 – 99的随机数,根据不同的范围来决定方向:


0 – 49(50%概率)

:继续沿上一步的方向行走。


50 – 59(10%概率)

:反向行走。


60 – 79(20%概率)

:向左转。


80 – 99(20%概率)

:向右转。

示例代码


TakeDrunkenWalk PROC
    LOCAL currX:WORD, currY:WORD, prevDirection:WORD
    ; prevDirection存储上一步的方向
    ; 定义默认起始方向
    mov prevDirection, 0 ; 0表示北

    ; ...其他初始化代码...

Again:
    ; Insert current location in array.
    mov ax,currX
    mov (COORD PTR [edi]).X,ax
    mov ax,currY
    mov (COORD PTR [edi]).Y,ax

    INVOKE DisplayPosition, currX, currY

    mov eax,100 ; 生成0 - 99的随机数
    call RandomRange

    .IF eax < 50 ; 50%概率继续沿上一步的方向
        mov eax, prevDirection
    .ELSEIF eax < 60 ; 10%概率反向行走
        mov eax, (prevDirection + 2) MOD 4
    .ELSEIF eax < 80 ; 20%概率向左转
        mov eax, (prevDirection + 3) MOD 4
    .ELSE ; 20%概率向右转
        mov eax, (prevDirection + 1) MOD 4
    .ENDIF

    ; 根据新的方向更新坐标
    .IF eax == 0 ; 北
        dec currY
    .ELSEIF eax == 1 ; 南
        inc currY
    .ELSEIF eax == 2 ; 西
        dec currX
    .ELSE ; 东 (EAX = 3)
        inc currX
    .ENDIF

    mov prevDirection, eax ; 更新上一步的方向

    add edi,TYPE COORD ; point to next COORD
    loop Again

Finish:
    mov (DrunkardWalk PTR [esi]).pathsUsed, WalkMax
    popad
    ret
TakeDrunkenWalk ENDP

以上代码只是示例,实际使用时可能需要根据具体情况进行调整和完善。

46、创建一个宏,使用SHRD指令将一个32位整数数组按可变的位数向任意方向移位。编写一个测试程序,通过对同一个数组向两个方向移位并显示结果值来测试该宏。可以假设数组采用小端字节序。以下是一个示例宏声明:mShiftDoublewords MACRO arrayName, direction, numberOfBits。参数:arrayName为数组名称;direction为右移(R)或左移(L);numberOfBits为移位的位数。

以下是宏和测试程序的示例代码:


; 宏定义
mShiftDoublewords MACRO arrayName, direction, numberOfBits
LOCAL shift_loop
    mov esi, OFFSET arrayName
    mov ecx, LENGTHOF arrayName
    cmp direction, 'R'
    je shift_right

; 左移
shift_left:
    mov eax, [esi]
    shl eax, numberOfBits
    mov [esi], eax
    add esi, TYPE arrayName
    loop shift_left
    jmp end_shift

; 右移
shift_right:
    mov eax, [esi]
    shr eax, numberOfBits
    mov [esi], eax
    add esi, TYPE arrayName
    loop shift_right

end_shift:
ENDM

; 测试程序
.386
.model flat, stdcall
.stack 4096
ExitProcess PROTO, dwExitCode:DWORD

.data
array DWORD 12345678h, 87654321h

.code
main PROC
    ; 左移测试
    mShiftDoublewords array, 'L', 2
    ; 显示左移结果(这里省略显示代码,可使用库函数实现)

    ; 右移测试
    mShiftDoublewords array, 'R', 2
    ; 显示右移结果(这里省略显示代码,可使用库函数实现)

    INVOKE ExitProcess, 0
main ENDP
END main

上述代码定义了

mShiftDoublewords

宏,它可以根据传入的方向和位数对数组进行移位操作。测试程序对数组先进行左移,再进行右移,并可添加显示代码来展示结果。

47、编写一个程序,创建一个新的文本文件。提示用户输入学生的学号、姓氏、名字和出生日期。将这些信息写入文件。以同样的方式输入更多的记录,然后关闭文件。

这是一个编程任务,需按以下步骤实现程序:

创建新的文本文件;

提示用户输入学生学号、姓氏、名字和出生日期;

将输入信息写入文件;

重复步骤 2 和 3 以输入更多记录;

关闭文件。

具体代码实现会因使用的编程语言不同而有所差异。

48、编写一个汇编程序,使其能够读取大于其输入缓冲区的文件。将缓冲区大小减小到1024字节。使用循环继续读取并显示文件,直到无法读取更多数据。如果计划使用WriteString显示缓冲区,请记得在缓冲区数据末尾插入空字节。

以下是修改后的ReadFile.asm程序示例:


TITLE Reading a File (ReadFile.asm)
; Opens, reads, and displays a text file using
; procedures from Irvine32.lib.

INCLUDE Irvine32.inc
INCLUDE macros.inc

BUFFER_SIZE = 1024
.data
buffer BYTE BUFFER_SIZE DUP(?)
filename BYTE 80 DUP(0)
fileHandle HANDLE ?

.code
main PROC
    ; Let user input a filename.
    .mWrite "Enter an input filename: "
    mov edx, OFFSET filename
    mov ecx, SIZEOF filename
    call ReadString

    ; Open the file for input.
    mov edx, OFFSET filename
    call OpenInputFile
    mov fileHandle, eax

    ; Check for errors.
    cmp eax, 0
    je error

read_loop:
    mov eax, fileHandle
    mov edx, OFFSET buffer
    mov ecx, BUFFER_SIZE
    call ReadFromFile ; 假设存在ReadFromFile过程,用于从文件读取数据

    cmp eax, 0
    je end_read ; 如果没有读取到数据,结束循环

    ; 在缓冲区末尾插入空字节
    mov buffer[eax], 0

    ; 显示缓冲区内容
    mov edx, OFFSET buffer
    call WriteString

    jmp read_loop

end_read:
    call CloseFile ; 关闭文件
    jmp quit

error:
    ; 处理错误
    call WriteWindowsMsg ; 显示错误信息

quit:
    exit
main ENDP
END main

此程序将缓冲区大小设为1024字节,使用循环不断从文件读取数据,直到无法读取更多数据。每次读取后,在缓冲区末尾插入空字节,再用WriteString显示缓冲区内容。

49、使用动态内存分配函数实现一个单链表。每个链表节点应为一个名为 Node 的结构体,该结构体包含一个整数值和指向下一个链表节点的指针。使用循环,提示用户输入任意数量的整数。每输入一个整数,就分配一个 Node 对象,将该整数插入 Node 中,并将该 Node 追加到链表中。当输入值为 0 时,停止循环。最后,从头到尾显示整个链表。

题目要求

本题要求使用动态内存分配函数实现单链表,步骤如下:

定义名为

Node

的结构体,包含整数值和指向下一节点的指针;

使用循环让用户输入整数,输入 0 时停止;

每输入一个非零整数,分配一个

Node

对象,将整数存入其中并追加到链表;

从头到尾显示整个链表。

50、将以下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赋一系列值来测试程序逻辑。

以下是实现该功能的汇编语言代码示例:


.386
.model flat, stdcall
option casemap:none
include Irvine32.inc
includelib Irvine32.lib

.data
X REAL8 0.0
Y REAL8 0.0
msgLower BYTE 'X is lower', 0dh, 0ah, 0
msgNotLower BYTE 'X is not lower', 0dh, 0ah, 0

.code
main PROC
    ; 为X和Y赋值,可修改这些值来测试程序逻辑
    fld X
    fld Y
    fcomip st(1), st  ; 比较X和Y
    fstsw ax
    sahf
    ja PrintNotLower ; 如果X >= Y,跳转到PrintNotLower

    ; 打印 'X is lower'
    mov edx, OFFSET msgLower
    call WriteString
    jmp Quit

PrintNotLower:
    ; 打印 'X is not lower'
    mov edx, OFFSET msgNotLower
    call WriteString

Quit:
    exit
main ENDP
END main

在上述代码中,我们使用了Irvine32库的

WriteString

函数来输出字符串。首先,我们定义了两个双精度浮点数

X


Y

,以及两个字符串消息。然后,我们将

X


Y

加载到浮点寄存器中并进行比较。根据比较结果,程序会跳转到相应的标签处并输出对应的消息。你可以修改

X


Y

的值来测试程序的逻辑。

51、编写一个程序,提示用户输入圆的半径,计算并显示圆的面积。假设存在 ReadFloat 过程用于读取用户输入的浮点数,WriteFloat 过程用于输出浮点数,使用 FLDPI 指令将 π 加载到寄存器栈中。

以下是一个满足需求的汇编程序示例:


TITLE Calculate Circle Area (circleArea.asm)
INCLUDE Irvine32.inc
INCLUDE macros.inc

.data
prompt  BYTE '请输入圆的半径: ', 0

.code
main PROC
    ; 提示用户输入半径
    mov     edx, OFFSET prompt
    call    WriteString
    call    ReadFloat

    ; 计算半径的平方
    fld     ST(0)
    fmulp

    ; 加载 π
    fldpi

    ; 计算面积
    fmulp

    ; 显示面积
    call    WriteFloat
    call    Crlf

    exit
main ENDP
END main

这个程序首先提示用户输入半径,使用

ReadFloat

读取输入的浮点数。然后将半径平方,使用

FLDPI

加载 π,将二者相乘得到圆的面积。最后使用

WriteFloat

显示计算结果。

52、提示用户输入多项式 $ax^2 + bx + c = 0$ 的系数 $a$、$b$ 和 $c$。使用二次公式计算并显示该多项式的实根。如果有任何根是虚数,则显示适当的消息。

可按以下步骤解决该问题:

提示用户输入系数 $a$、$b$ 和 $c$。

计算判别式 $Delta = b^2 – 4ac$。

根据判别式的值进行判断:

– 若 $Delta > 0$,有两个不同实根,使用公式

$x_1=frac{-b + sqrt{Delta}}{2a}$



$x_2=frac{-b – sqrt{Delta}}{2a}$

计算并显示。

– 若 $Delta = 0$,有两个相同实根,使用公式

$x=frac{-b}{2a}$

计算并显示。

– 若 $Delta < 0$,根为虚数,显示相应提示信息。

© 版权声明

相关文章

暂无评论

none
暂无评论...