操作系统实验报告3
一、实验内容
验证实验 Blum’s Book: Sample programs in Chapter 08, 10 (Basic Math Functions and Using Strings)
二、实验环境
Ubuntu 18.04(64位)
三、实验过程
1、Sample programs in Chapter 08
第八章通过无符号和带符号数字的加减乘除指令的程序范例,介绍了整数运算是如何在处理器上执行的,以及如何在汇编语言程序中使用它们。
整数加法与二进制数值如何相加有关。
1)addtest1.s
ADD指令:add source, destination
ADD指令可以用于无符号和带符号整数。其中source可以是立即值、内存位置或者寄存器,destination可以是寄存器或者内存位置中存储的值,但是不能同时使用内存位置作为源和目标。
ADD指令可以将8位、16位或者32位值相加,通过在ADD助记符的结尾添加b(用于字节)、w(用于字)或者l(用于双字)来指定操作数的长度。
该程序演示使用这些ADD指令执行加法。
在终端中输入命令行:
$ as -gstabs --32 -o addtest1.o addtest1.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o addtest1 -lc addtest1.o $ gdb -q addtest1
输出结果:
2)addtest2.s
该程序演示使用ADD指令执行一些带符号整数的加法。
在终端中输入命令行:
$ as -gstabs --32 -o addtest2.o addtest2.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o addtest2 -lc addtest2.o $ gdb -q addtest2
输出结果:
3)addtest3.s
该程序演示如何检测加法程序有无进位。
在终端中输入命令行:
$ as -gstabs --32 -o addtest3.o addtest3.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o addtest3 -lc addtest3.o $ ./addtest3 $ echo $?
输出结果:
表示正确地检测到了进位情况,改动寄存器值,使加法不产生进位:
movb $190, %bl
movb $10, %al
再次运行程序,得到结果:
加法没有产生进位,没有采取跳转。
4)addtest4.s
该程序演示在带符号整数加法中使用溢出标志检测错误。
在终端中输入命令行:
$ as --32 -o addtest4.o addtest4.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o addtest4 -lc addtest4.o $ ./addtest4
输出结果:
输出表明检测到了溢出,修改MOVL指令,使两个值的相加不产生溢出:
movl $-190876934, %ebx
movl $-159230143, %eax
得到结果:
5)adctest1.s
ADC指令:·adc source, destination
其中source可以是立即值或者8位、16位或32位寄存器或内存位置值,destination可以是8位、16位或32位寄存器或内存位置值,但两者不能同时是内存位置。此外,和ADD指令一样,GNU汇编器要求在助记符中用附加的字符来表明操作数长度(b/w/l)
该程序演示如何使用ADC指令将两个64位无符号整数值相加。
在终端中输入命令行:
$ as -gstabs --32 -o adctest.o adctest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o adctest -lc adctest.o $ gdb -q adctest $ ./adctest
输出结果:
6)subtest1.s
SUB指令:sub source, destination
其中从destination的值中减去source的值,结果储存在destination操作数的位置。
source可以是立即值、内存位置或者寄存器,destination可以是寄存器或者内存位置中存储的值,但是不能同时使用内存位置作为源和目标。
SUB指令可以将8位、16位或者32位值相加,通过在助记符的结尾添加b(用于字节)、w(用于字)或者l(用于双字)来指定操作数的长度。
该程序演示使用SUB指令执行减法。
在终端中输入命令行:
$ as -gstabs --32 -o subtest1.o subtest1.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o subtest1 -lc subtest1.o $ gdb -q subtest1
输出结果:
7)subtest2.s
该程序演示如何检测无符号整数减法程序有无进位。
在终端中输入命令行:
$ as -gstabs --32 -o subtest2.o subtest2.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o subtest2 -lc subtest2.o $ ./subtest2 $ echo $? $ gdb -q subtest2
输出结果:
8)subtest3.s
该程序演示如何检测带符号整数减法程序有无溢出。
在终端中输入命令行:
$ as -gstabs --32 -o subtest3.o subtest3.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o subtest3 -lc subtest3.o $ ./subtest3
输出结果:
说明溢出。改动文件中的movl语句:movl $-1259230143, %eax
再次运行程序:
没有溢出。
9)sbbtest.s
SBB指令:sbb source, destination
其中进位位被添加到source值,然后从destination值中减去source值得到结果。结果存储到destination的位置中。同样的,source可以是立即值、内存位置或者寄存器,destination可以是寄存器或者内存位置中存储的值,但是不能同时使用内存位置作为源和目标。
该程序演示在多字节减法操作中使用SBB指令。
在终端中输入命令行:
$ as --32 -o sbbtest.o sbbtest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o sbbtest -lc sbbtest.o $ ./sbbtest
输出结果:
递增指令:dec destination
递减指令:inc destination
10)multest.s
MUL指令:mul source
该指令用于两个无符号整数相乘,目标操作数是隐藏的,根据源操作数的值长度,乘法操作的另一个操作数必须存放在AL/AX/EAX寄存器中。
该程序演示两个32位无符号整数的乘法操作。
在终端中输入命令行:
$ as -gstabs --32 -o multest.o multest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o multest -lc multest.o $ gdb -q multest
输出结果:
11)imultest.s
IMUL指令:
imul source
imul source, destination
imul multiplier, source, destination
该指令可以用于带符号和无符号整数的相乘,注意,对于较大的值,IMUL指令只对带符号整数是合法的。其中,multiplier是一个立即值。
该程序演示两个32位无符号整数的乘法操作。
在终端中输入命令行:
$ as -gstabs --32 -o imultest.o imultest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o imultest -lc imultest.o $ gdb -q imultest
输出结果:
12)imultest2.s
该程序演示检查带符号整数相乘的结果是否溢出。
在终端中输入命令行:
$ as -gstabs --32 -o imultest2.o imultest2.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o imultest2 -lc imultest2.o $ ./imultest2 $ echo $?
输出结果:
结果代码为1,说明溢出。
13)divtest.s
DIV指令:div divisor
该指令用于无符号整数除法操作。其中divisor(除数)是隐含的被除数要除以的值,在执行DIV指令时,被除数必须已经存储到了AX寄存器(16位),DX:AX寄存器(32位),或者EDX:EAX寄存器(64位)。除法操作的结果是两个单独的数字:商和余数,两者都存储在被除数值使用的相同寄存器。这意味着当除法操作完成时,会丢失被除数。
该程序演示简单的除法操作。
在终端中输入命令行:
$ as -gstabs --32 -o divtest.o divtest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o divtest -lc divtest.o $ gdb -q divtest $ ./divtest
输出结果:
当把程序中的divisor值设置为0时,再次运行:
除数为0时会报错。
IDIV指令:idiv divisor
该指令用于带符号整数的除法操作,其余与DIV指令相似。
14)saltest.s
SAL指令:
sal destination
sal %cl, destination
sal shifter, destination
该程序演示使用SAL指令的基本情况。
在终端中输入命令行:
$ as -gstabs --32 -o saltest.o saltest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o saltest -lc saltest.o $ gdb -q saltest
输出结果:
15)aaatest.s
该程序演示两个多字节不打包BCD值的加法操作。
在终端中输入命令行:
$ as --32 -gstabs -o aaatest.o aaatest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o aaatest -lc aaatest.o $ gdb -q aaatest
输出结果:
在运行该程序时出现问题:eax寄存器一直是一个绝对值较大的负数值,原因是eax寄存器是未赋初值清零,初始值为乱码。
程序赋值时仅改动低位,未修改高位,在程序中clc语句前添加movl $0, %eax
赋初值为0即可解决。
16)dastest.s
该程序演示使用SBB和DAS指令执行减法操作。
在终端中输入命令行:
$ as --32 -gstabs -o dastest.o dastest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o dastest -lc dastest.o $ gdb -q dastest
输出结果:
52933 - 28125 = 24808, 与结果相符。
17)cpuidtest.s
该程序演示反转EFLAGS寄存器中的ID位。
在终端中输入命令行:
$ as -gstabs --32 -o cpuidtest.o cpuidtest.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o cpuidtest -lc cpuidtest.o $ ./cpuidtest
输出结果:
CPUID指令可用,证明ID标志位改变了。
2、Sample programs in Chapter 10
第十章指导如何处理字符串,包括介绍在内存中传送字符串、比较字符串与在字符串中搜索字符串的指令。
1)movstest1.s
该程序演示使用MOVS指令传送一些字符串:
在终端中输入命令行:
$ as -gstabs --32 -o movstest1.o movstest1.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o movstest1 -lc movstest1.o $ gdb -q movstest1
输出结果:
2)movstest2.s
在终端中输入命令行:
$ as -gstabs --32 -o movstest2.o movstest2.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o movstest2 -lc movstest2.o $ gdb -q movstest2
输出结果:
(gdb) x/23b &output 0x804a018 <output>: 0 0 0 0 0 0 0 00x804a020 <output+8>: 0 0 0 0 0 0 0 00x804a028 <output+16>: 0 0 0 110 103 46 10
3)movstest3.s
该程序演示用MOVSL指令循环复制大型字符串的情况。
在终端中输入命令行:
$ as -gstabs --32 -o movstest3.o movstest3.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o movstest3 -lc movstest3.o $ gdb -q movstest3
输出结果:
4)reptest1.s
该程序演示MOVSB指令和REP指令一起使用,每次1字节地把字符串传送到另一个位置。
在终端中输入命令行:
$ as -gstabs --32 -o reptest1.o reptest1.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o reptest1 -lc reptest1.o $ gdb -q reptest1
输出结果:
5)reptest2.s
该程序演示使用MOVSW和MOVSL指令遍历字符串时,超出字符串边界的情况。
在终端中输入命令行:
$ as -gstabs --32 -o reptest2.o reptest2.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o reptest2 -lc reptest2.o $ gdb -q reptest2
输出结果:
结果错误。
6)reptest3.s
该程序演示当知道字符串长度时,用整数除法确定字符串中包含多少双字,余数则用MOVSB指令传送的情况。
在终端中输入命令行:
$ as -gstabs --32 -o reptest3.o reptest3.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o reptest3 -lc reptest3.o $ gdb -q reptest3
输出结果:
7)reptest4.s
该程序演示将DF标志设置为对字符串进行向后处理,按照相反的方向在内存位置之间传送它的情况。
在终端中输入命令行:
$ as -gstabs --32 -o reptest4.o reptest4.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o reptest4 -lc reptest4.o $ gdb -q reptest4
输出结果:
8)stostest1.s
该程序演示STOS指令与REP指令一起使用,多次把一个字符串值复制到大型字符串值中。
在终端中输入命令行:
$ as -gstabs --32 -o stostest1.o stostest1.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o stostest1 -lc stostest1.o $ gdb -q stostest1
输出结果:
9)convert.s
该程序演示利用LODS指令遍历字符串,再用STOS指令把新的字符加载回字符串的原理,将ASCⅡ字符串都转换为大写字母的情况。
在终端中输入命令行:
$ as -gstabs --32 -o convert.o convert.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o convert -lc convert.o $ ./convert
输出结果:
10)cmpstest1.s
该程序演示使用CMPS指令的简单例子。
在终端中输入命令行:
$ as -gstabs --32 -o cmpstest1.o cmpstest1.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o cmpstest1 -lc cmpstest1.o $ ./cmpstest1 $ echo $?
输出结果:
11)cmpstest2.s
该程序演示使用REP指令系列中的其他指令,与CMPS指令一起使用,进行字符串比较。
在终端中输入命令行:
$ as -gstabs --32 -o cmpstest2.o cmpstest2.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o cmpstest2 -lc cmpstest2.o $ ./cmpstest2 $ echo $?
输出结果:
12)strcomp.s
该程序演示字符串的不等和相等的比较。
在终端中输入命令行:
$ as -gstabs --32 -o strcomp.o strcomp.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o strcomp -lc strcomp.o $ ./strcomp $ echo $?
输出结果:
13)scastest1.s
该程序演示使用REPNE和SCAS指令在字符串中搜索一个字符。
在终端中输入命令行:
$ as -gstabs --32 -o scastest1.o scastest1.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o scastest1 -lc scastest1.o $ ./scastest1 $ echo $?
输出结果:
14)scastest2.s
该程序演示使用SCASW和SCASL指令搜索多个字符序列的情况。
在终端中输入命令行:
$ as -gstabs --32 -o scastest2.o scastest2.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o scastest2 -lc scastest2.o $ ./scastest2 $ echo $?
输出结果:
15)strsize.s
该程序演示计算字符串长度。
在终端中输入命令行:
$ as -gstabs --32 -o strsize.o strsize.s $ ld -m elf_i386 -dynamic-linker /lib/ld-linux.so.2 -o strsize -lc strsize.o $ ./strsize $ echo $?
输出结果:
四、实验总结
通过本次实验,我验证了第八章与第十章中的范例汇编程序,掌握了无符号和带符号整数的加减乘除运算,学习了字符串的传送、比较与匹配等处理的实现方式。在这个过程中,我对汇编语言的理解逐渐加深,更熟悉了linux终端的命令行使用,各寄存器的用处与汇编原理。