BUAA-X86Programming-Exp2
本系列是北航计算机学院于 2024 年春季学期开设的一般专业课《X86汇编程序设计》课程的实验报告记录,由于学习过程中掌握并不牢靠,如有错误请读者不吝赐教!
X86汇编程序设计第二次实验作业
本次作业共三道程序设计题,其中一道题含有选做一小问
冒泡排序程序设计
编写一道完整汇编程序,实现冒泡排序,并显示排序前后的结果。
要求(提示:参考讲义例题修改):
- 建立样本数据区,其中包含两个字(分开,分别由学生本人的8位学号的16进制字组成:XXXXh,YYYYh)。排序后,这两个字可以分开。
- 要显示排序前及排序后的字表,显示时每个字中间空一格。
- 要求将排序、显示内存中的字(十六进制至十进制ASCII码)、显示字符、显示字符串等程序块改编为子程序或宏。
作为第一道自己编写的 x86 汇编程序而言,我们最应该注意的是程序的结构,其次就是将一些常用的结构拆解出来,形成子程序辅助后续作业。
还有一点就是要注意寄存器的用法,主要记住段寄存器不能直接转移到段寄存器和 OFFSET 什么时候用应该就够了,其他的不太容易犯错。
这个题写的内容不太多,主要工作在处理数字输出上,可以关注子程序 PrintAXAsDecimal
的实现,可以抽象出输出寄存器中存储的十进制整数的函数(可以用栈、缓冲区实现)
STACK SEGMENT PARA STACK |
读入数据实现乘法并输出
编程实现:从键盘输入一个两位及三位的十进制数,做乘法(假定乘积小于65535,不考虑溢出),并显示乘法结果的十进制 ASCII 码。
第二题类似,困难的点在于如何读入和输出,内部的乘法实际上很简单,只需要 AX * BX = DX: AX 就够用了(因为不会溢出)
是的,难点在于从控制台读入数字,虽然输出也是,不过我们已经在上一题拿到一个函数了,可以关注子程序 GET_NUMBER
的实现,具体做法是利用系统调用从控制台读取缓冲区的内容,并保存到一段内存中。我们从第一位开始,通过循环每次读取一个十进制下的 ASCII 数,转化为数字后和已经读出的值扩大 10 倍后进行加和(补充个位),直至循环结束读出数字的值
通过缓冲区读取到的字符串有固定的特点:
- 第一个 BYTE 应该在读缓冲区之前设置好确定的值,它限制了一次读取的最大长度
- 第二个 BYTE 在读取完成后被自动更新,数值为该次读取到的数据总长度(不包含最后的回车符),通常情况下,这个字符和字符串的循环等操作密切相关,也经常读取到 CX 里
- 从第三个字符开始,才是真正读取到的数据
比如代码段中的 69 ~ 70 行就体现了缓冲区读取的这种特色。
STACK SEGMENT PARA STACK |
64 位下的无符号数乘法
编程实现 32 位无符号数乘法。
- 在内存中定义一个无符号数双字 XX,YY,做乘法,得到一个 64 位的乘积。
- 显示该乘积的 16 进制 ASCII 码。
- (选做):显示该乘积的十进制 ASCII 码。
提示:双字 XX 可拆分成两个字 XXH,XXL;双字 YY 可拆分成两个字 YYH,YYL; 双字或 64 位结果可用 DD 定义,也可以用 DW 定义,也可以定义为数组(间接寻址)。(XXH,XXL)*(YYH,YYL)=4 个字;乘法列竖式,注意到处都有进位!进位加法用 ADC 指令。
实际上也没说的那么玄乎,虽然我们在 16 位下的系统下进行操作,但是仍然可以通过多个字拼接的方式实现 64 位的乘法。
首先列一个竖式,将双字拆成两个单字再分别进行乘法,我们就会得到分占不同位置的四个局部乘积。将这些乘积再按单字划分,确定好最终的 64 位字的每一个 16 位都由哪些乘积的高位/低位组成,然后就可以开始进位加法了。这两部分分别在不同的标签下(multing
、adding
)
要注意的是 XOR 这种清空寄存器的方式还会清空标志位。所以如果要使用的话需要注意 XOR 和 ADD、ADC 的使用顺序。
那么最后还是一个输出操作,首先 16 进制的输出比较简单,按照内存中字节的顺序,自左向右逐个取出每个 BYTE 然后除以 16 分成两个 16 进制数就能直接输出了,注意 0-9 和 A-F 处理并不完全一致。
对于 10 进制输出,做法也类似竖式,首先检查被除数是否为 0,若 0 则结束操作,否则开始从最高字运算,让每一个单字都除以 10,余数留给下一个字作为高位,再进行除法,最后一个字的余数是当前真正的输出值,然后存入内存字符串,循环操作即可。
为什么不用双字除法?因为一个较大的 32 位数字除以 10,可能商超过 16 位,这样就会造成溢出,为了避免溢出,上面的做法实际上只会进行({9,16位} / 10)的操作,这样是肯定不会溢出的,保证了程序的安全性。
总的来说还是比较繁琐的,寄存器别用错,函数记得压栈比较重要。
STACK SEGMENT PARA STACK |