《汇编语言程序设计》
实 验 报 告
院 系 信息科学与技术学院 专 业 软件工程 年 级 2012级 学 号 姓 名 某某某
西南交通大学信息科学与技术学院
2014年 6月
0
目录
实验一 MASM6.11的使用方法 ………………………………………3
1.1 实验目的……………………………………………… 1.2 实验环境……………………………………………… 1.3 实验步骤……………………………………………… 1.4 实验结果及其分析…………………………………… 1.5 结论……………………………………………………
实验二 比较两个字符串的大小………………………………………15
2.1 实验目的……………………………………………… 2.2 实验环境……………………………………………… 2.3 实验步骤……………………………………………… 2.4 实验结果及其分析…………………………………… 2.5 结论……………………………………………………
实验三 求学生名次 …………………………………………………24
3.1 实验目的……………………………………………… 3.2 实验环境……………………………………………… 3.3 实验步骤……………………………………………… 3.4 实验结果及其分析…………………………………… 3.5 结论……………………………………………………
实验四 子程序及堆栈 ……………………………………………… 42
4.1求员工年工作量…………………………………………………… 42
4.1.1 实验目的………………………………… 4.1.2 实验环境………………………………… 4.1.3 实验步骤………………………………… 4.1.4 实验结果及其分析……………………… 4.1.5 结论………………………………………
4.2 子程序和主程序之间的参数传递 …………………………………52
4.2.1 实验目的…………………………………
4.2.2 实验环境…………………………………
4.2.3 实验步骤………………………………… 4.2.4 实验结果及其分析……………………… 4.2.5 结论………………………………………
实验五 汇编语言和C语言间的调用 ………………………………59
5.1 C语言和汇编语言混合编程下的排序算法………………………59
5.1.1 实验目的„„„„„„„„„„„„„ 5.1.2 实验环境„„„„„„„„„„„„„ 5.1.3 实验步骤„„„„„„„„„„„„„
1
5.1.4 实验结果及其分析„„„„„„„„„ 5.1.5 结论„„„„„„„„„„„„„„„
5.2 汇编语言对C语言的调用求平均数 ………………………………64 5.2.1 实验目的„„„„„„„„„„„„„
5.2.2 实验环境„„„„„„„„„„„„„ 5.2.3 实验步骤„„„„„„„„„„„„„ 5.2.4 实验结果及其分析„„„„„„„„„ 5.2.5 结论„„„„„„„„„„„„„„„
2
实验一 Masm6.11的使用方法
一、实验目的
1、创建一个新的汇编语言程序,熟练掌握汇编语言开发工具的使用方法,并对开发环境中一些坏境变量进行设置。
2、创建一个工程文件,对该工程文件进行编译,连接,并运行; 3、对建立的工程文件进行调试,在运行过程中查看各个寄存器的值,内存单元的值等。
二、实验环境
该试验包括的硬件和软件条件如下: 1、硬件环境
(1)Intel Core Duo CPU P8700 (2)内存4G 2、软件环境
(1)Window XP Professor (2)MASM6.11开发软件
三、实验步骤
1、将Masm6.11安装程序拷贝到D盘根目录下,并执行d:\\Masm611目录下Setup.exe文件,按照安装提示将程序安装到C盘根目录。
双击setup.exe后按所给提示进行安装(基本上都是回车,中间不需要做什么更改),安装完成后将BIN和HELP文件夹里面的文件拷贝到BINR文件夹里,安装工作完成之后打开PWB.exe集成开发环境应用程序
2、将C:\\masm611\\Bin目录下的所有文件拷贝到C:\\Masm611\\binr目录下,将C:\\Masm611\\help目录下的所有文件拷贝到c:\\Masm61\\binr目录下 3、执行C:\\Masm611\\Binr目录下的pwb.exe文件,打开开发环境。 4、在PWB编辑窗口中输入实验1.1中的程序,并保存为test.asm文件 如图 1.1。
3
图 1.1
5、执行Project菜单下New Project子菜单创建一个新的工程文件,并命名为test.mak,将test.asm文件加入到该工程文件中 如图1.2。
1.2
6、执行Project下Build子菜单编译连接该工程文件,并运行该可执行程序,观察程序执行的结果 如图1.3。
4
图 1.3
在Project菜单中点击Rebuild all选项,如果没有错误, 如图1.4所示,再点击Run Pogram,进入运行界面运行程序得到运行结果
图 1.4
7、执行Run菜单下Debug子菜单,进入调试环境。
5
Debug program进入调试界面,在调式界面中的windows选项中点击
Register和Memory1会弹出两个窗口,如图所示。通过7窗口可以看到在程序执行之前寄存器AX,BX,CX,DX,BP,SI,DI,IP的初始值都是0,堆栈指针SP=0020H,并且通过图中的3窗口可以查看反编译后的代码。如图1.5
图 1.5
8、在程序的如下位置设置断点,并按要求观察寄存器堆栈或者内存单元的值。
code segment
Assume Cs:code,Ss:stack,ds:Data Start: mov ax,data mov ds,ax lea dx,Msgl ;此行设置断点,观察DS所指示的数据段的情况 mov ah, 09h int 21h Call Numproc ;此行设置断点,观察屏幕输出,并观察堆栈的变化
mov num,bl ;此行设置断点,观察bl的值,并观察堆栈的变化
lea dx,EnMsg mov ah,09h int 21h
6
lea dx,Msg2 mov ah,09h int 21h
call Avgproc ;此行设置断点,观察屏幕输出信息
lea dx,EnMsg ;此行设置断点,观察avg所对应的存储单元的值
mov ah,09h int 21h
lea dx, Msg3
mov ah,09h int 21h
call bta ; mov ah,4ch ;的值及屏
; int 21h
Numproc proc push ax push cx push dx
lea dx,EnMsg mov ah,09h int 21h mov bl,0 lop1: mov ah,01h
int 21h cmp al,0dh jz lop2 cmp al,'0' jl lop1 cmp al, '9' ja lop1 sub al,30h mov cl,al mov al,bl mov ch,10
mul ch
mov bl,al ; add bl,cl ; jmp lop1 lop2: pop dx pop cx pop ax
此行设置断点,观察屏幕输出信息 此行设置断点,观察ascavg所对应的存储单元幕输出信息 ;设置断点观察al寄存器的值 设置断点观察al寄存器的值 设置断点观察bl寄存器的值 7
ret Numproc endp Avgproc proc push ax push cx
xor dx,dx ;设置断点观察堆栈值的变化 mov cl,num ;设置断点观察cl寄存器的值 lop3: call Numproc xor ax,ax mov al,bl add ax,dx Div num
add avg,al ; xor dx,dx mov dl,ah sub cl,1 jnz lop3
pop ax ; pop cx
ret ; Avgproc endp bta proc
push ax ; push bx xor ax,ax
mov al,avg mov bl,10 div bl
add ah,30h ; add al,30h
mov ascavg ,al ; mov ascavg+1,ah lea dx,EnMsg mov ah,09h int 21h
lea dx,ascavg mov ah,09h int 21h pop bx pop ax ret bta endp code ends end start
设置断点观察avg存储单元的值 设置断点观察avg存储单元的值 设置断点观察堆栈值的变化 设置断点观察堆栈值的变化 设置断点观察ax寄存器的值 设置断点观察ascavg存储单元的值dx寄存器的值 8
;设置断点观察
9、按照程序的执行过程绘制出程序流程图。
主函数流程图,如图 1.7所示
图1.7
9
开始 显示输入提示信息msg1 调用Numproc输入学生总数:Num,并将Num由真值转换成对应的ASCⅡ 显示输入提示信息msg2 调用Avgproc,输入每个学生的成绩,并将平均成绩送给avg 调用bta,把avg的由ASCⅡ转换真值,并显示 结束 函数Numproc的流程图如图 1.8 Avgproc的流程图如图 1,9所示 入口 push ax ; push cx; 显示回车换行 输入一个字符到al al=odh N Y al<0 N Y Y al>9 N al-30h→cl bl→al mov ch 10 al*ch→al; al→bl add bl,cl pop dx ; pop cx ;pop ax 返回 图 1.8
10
入口 push ax; push cx; 将num的值送给cl 调用Numproc输入各个学生的成绩,并将每个成绩由字符类型转换成数字类型,除以num后累加给avg Cl=C1-1 Cl==0 pop cx ;pop ax 返回 图 1.9
四、实验结果及其分析
1, lea dx,Msgl 此行设置断点,观察DS所指示的数据段的情况。
截图:
图1.10
(如图 1.10)数据段里面显示了 “please input the number of student”。 2, Call Numproc ;此行设置断点,观察屏幕输出,并观察堆栈的变化
截图:堆栈
图1.11
如图(1.11)可以看到堆栈里显示了“„„„„„„” 截图:屏幕输出
图 1.12
图 (1.12)可以看到程序输出了DS数据段里面的东西。我们输入“5“。 3, add bl,cl ;设置断点观察bl寄存器的值
图 1.13
截图:bl
可见我们刚刚输入的”5“,已经保存到了BX的低8位上了。 截图:屏幕输出
图 1.14
依次位五个学生输入成绩为: 98,88,96,86,88 截图:AX里面的数据
11
4,call Avgproc ;此行设置断点,观察屏幕输出信息
5,lea dx,EnMsg ;此行设置断点,观察avg所对应的存储单元的值
图1.15
图 (1.15) 中可以看到Avg里面的数据我们可以看到为0. 6, call bta ;此行设置断点,观察屏幕输出信息
图 1.16
要求一个字符一个字符的输入: 最后我们的输入为(如图 1.17):
图 1.17
7, mov ah,4ch ;此行设置断点,观察ascavg所对应的存储单元的值及屏 ;幕输出信息 截图:ascavg、
12
图1.18
学生成绩输入完成后AX里面变成了005A(转化成十进制即为平均成绩 91),在执行bta后将其转化为真值后输出。
13
五、结论
通过这次实验,我学会了如何使用MASM6.11去编写,调试和运行一个汇编语言程序,但是对于有些操作还是不是很熟悉,还要在今后的后继实验中更进一步的去熟悉MASM6.11的使用方法。
其次是对汇编语言的很多指令很陌生,不知道是什么意思,在今后的实验中我也要更进一步的去熟悉我们要学处理器的汇编语言的指令系统,并进一步学习汇编语言的语法。
14
实验二 比较两个字符串的大小
一、实验目的
本次实验主要达到如下目的:
1、进一步熟悉MSAM6.11软件的使用方法。
2、从键盘输入两个字符串,比较两个字符串的大小。
二、实验环境
该试验包括的硬件和软件条件如下: 1、硬件环境
(1)Intel Core Duo CPU P8700 (2)内存4G 2、软件环境
(1)Window XP Professor (2)MASM6.11开发软件
三、实验步骤
1、画出实现上述功能的汇编语言程序流程图。 2、写出实现上述功能的汇编语言程序。
3、编译、连接编写的汇编语言程序,并运行生成的可执行文件,描述其执行情况。
4、对上述程序进行调试,比较反汇编后的程序和源程序的区别。 5、描述反编译后的程序在执行之前数据段的内容以及调试过程中各寄存器的变化情况和执行结束后数据段的内容。
四、实验结果及其分析
1, 结果判定方法:
如果第一个字符串比第二个字符串大,则显示1;如果两个字符串相等,则显示0;如果第一个字符串比第二个字符串小,则显示-1.
15
2,流程图:
1、实验流程图如图所示:
开始
分别将string1和string2的中字符串 的首地址送给SI和 DI 显示msg3的内容 根据提示内容分别输入两个字符串 lea dx,result1 lea dx,result2 对BX和CX清零后 分别将string1和对BX和CX清string2中字符总数 零后分别将送给BX和CX string和 string2中字符总数送给BX bx>=cx N mov cx,bx Y
mov al,[SI] mov bl,[DI] bx>cx N Y Y N
Cx==0 al>bl lea dx,result3 bx 16 3,源代码: data segment msg1 db \"Please input the first string:$\" string1 db 100,0,100 dup(?) msg2 db 0ah,0dh,\"Please input the second string:$\" string2 db 100,0,100 dup(?) msg3 db 0ah,0dh,\"The reasult is:$\" result1 db \"1$\" result2 db \"-1$\" result3 db \"0$\" data ends stack segment para stack db 20H dup(0) stack ends code segment assume cs:code,ss:stack,ds:data start: mov ax,data mov ds,ax lea dx,msg1 mov ah,09h int 21h lea dx,string1 mov ah,0ah int 21h lea dx,msg2 mov ah,09h int 21h lea dx,string2 mov ah,0ah int 21h lea dx,msg3 mov ah,09h int 21h LEA SI,string1+2 LEA DI,string2+2 xor bx,bx mov bl,string1+1 xor cx,cx mov cl,string2+1 cmp bx,cx 17 ja up jz up mov cx,bx up: mov al,[SI] mov bl,[DI] cmp al,bl ja next1 jb next2 INC SI INC DI loop up xor bx,bx mov bl,string1+1 xor cx,cx mov cl,string2+1 cmp bx,cx ja next1 jb next2 jmp next3 next1: lea dx,result1 jmp exit next2: lea dx,result2 jmp exit next3: lea dx,result3 exit: mov ah,09h int 21h mov ah,4ch int 21h code ends end start 18 4,运行程序: 图 2.3 5,运行结果: 图 2.4 19 6,程序分析和调试 (1),首先进入debug调试坏境,并设置好断点 图 2.5 2,我们按F5执行到断点,逐步调试。 (a) 在调式界面中的windows选项中点击Register和Memory1查看当前 寄存器的值,可以看到在程序执行之前寄存器AX,BX,CX,DX,BP,SI,DI的初始值都是0,堆栈指针SP=0020H,数据段DS和附加段CS的初始值都是209CH,堆栈段SS=20BFH,代码段CS=20C1H。 20 图 2.6 (b)在lea dx,msg1行设置断点可以看到DS数据段里面的数据 图 2.7 DS里面显示“please input the first string” (c)在mov ah,0ah int 21h出设置断点可以看到屏幕输出 图 2.8 我们输入:xuyusong。 (d) LEA SI,string1+2 LEA DI,string2+2设置断点; 图 2.9 功能即是将我们输入的两个字符串的首地址送给SI和DI 图 2.10 我们可以看到SI=0021H,DI=00A9H 21 (e)在 cmp bx,cx出设置断点; 图 2.11 断点之前的语句是分别将代码的长度送给BX,CX 完成这些语句后 图2.12 可以看到BX=0009H,CX==0008H (f)在 loop up处设置断点: 图2.13 在loop up之前的语句 是先把两字符串的内容先送给AL和BL, 在比较AL和BL的大小,若AL>BL则跳至201C:0069H对应的代码段,若AL SI由原来的0021H变成了现在的0022H DI由原来的00A9H变成了现在的00AAH (g)在 mov ah,09h int 21h处设置断点; 图 2.15 此段语句是调用显示中断,显示比较后的结果。在内存窗口中输入 075B:0124(即为DS数据段里DX的数据)后可以查看到结果为0。表示第一个字符串比第二个字符串相等。 22 五、结论 通过这次实验,我学会了如何使用MASM6.11去编写,调试和运行一个汇编语言程序,但是对于有些操作还是不是很熟悉,还要在今后的后继实验中更进一步的去熟悉MASM6.11的使用方法。 学习到了很多汇编语言的语句比如lea,cmp,xor,cld JZ ;为 0 则跳转,JNZ ;不为 0 则跳转 JS ;为负则跳转,JNS ;不为负则跳转 JC ;进位则跳转,JNC ;不进位则跳转 JO ;溢出则跳转,JNO ;不溢出则跳转 JA ;无符号大于则跳转,JNA ;无符号不大于则跳转 还学会了函数的调用,循坏的运用 23 实验三 求学生名次 一、实验目的 本次实验主要达到如下目的: 从键盘输入若干个学生(总数不超过500)的姓名(英文字母不超过8个)及其5门课程的总成绩(百分制),输出任意名次的学生的姓名及其5门课程的总成绩(按总成绩的高低排序,总成绩最高者为第一名) 二、实验环境 该试验包括的硬件和软件条件如下: 1、硬件环境 (1)Intel Core Duo CPU P8700 (2)内存4G 2、软件环境 (1)Window XP Professor (2)MASM6.11开发软件 三、实验步骤 1、画出满足上述要求的程序流程图 2、写出源程序及程序段说明 四、实验结果及其分析 1. 源程序及程序段说明 程序段的的说明用黑体标示 data segment student struc stuname db 21,0,21 dup(0) stugrade dw 0 student ends 24 stu student 500 dup({}) a db 500 dup(?) num dw 0 ranking dw 0 msg1 db \"Please input the number of students:$\" msg2 db \"Please input the information of each student:\ msg3 db \"Result :$\" msg4 db 0ah,0dh,'$' msg5 db \"name:$\" msg6 db 0ah,0dh,\"grade:$\" msg7 db \"Please input the rank of a student (input 0 exit):$\" msg8 db \"The Reault :\indata db 5,0,5 dup(0) stuGrade db 5,0,5 dup(0) max dw 0 data ends stack segment para stack db 40h dup(0) stack ends code segment assume cs:code,ss:stack,es:data,ds:data start: mov ax,data mov ds,ax lea dx,msg1 ;提示输入学生总数 mov ah,09h int 21h call input ;调用input函数输入学生总数并将总数存放在寄存器ax里 mov num,ax lea dx,msg2 ;提示输入学生信息 mov ah,09h int 21h 25 call stuinf ;调用stuinf函数输入学生信息 call stusort ;调用排序函数对学生按成绩高低进行排序 lea dx,msg8 mov ah,09h int 21h call print ;调用打印函数显示排好序的所有学生 up: lea dx,msg7 ;提示输入学生名次 mov ah,09h int 21h call input 保存在ax寄存器里面 cmp ax,0 jna exit cmp ax,num ja exit sub ax,1 mov ranking,ax call print1 jmp up exit: mov ah,4ch int 21h input proc push cx push dx push bx push si lea dx,indata mov ah,0ah int 21h mov si,2 xor ax,ax xor cx,cx mov cl,indata+1 调用input函数输入学生名次并将输入的值若不是,则跳至exit,结束程序 ;ax ; ; ax>0 ? ; lop: sub indata[si],30h xor bx,bx mov bl,indata[si] add ax,bx inc si sub cx,1 jz exit1 mov bx,10 mul bx jmp lop ;lop这一段时将从键盘上接收的indata字符串逐位转换为二进制机器码并累加到ax里面后ax=ax*10 exit1: push ax lea dx,msg4 ;显示一个回车换行 mov ah,09h int 21h pop ax pop si pop bx pop dx pop cx ret input endp stuinf proc ;学生信息输入函数 push ax push bx push cx push dx push si push di xor cx,cx mov cx,num 27 mov si,0 lop1: lea dx,msg5 mov ah,09h int 21h ;提示输入学生姓名 xor ax,ax mov ax,si mov a[si],al ;将si赋给a[si] xor bx,bx mov bl,25 mov ax,si mul bl xor di,di mov di,ax ;将si*25后赋给di,25为结构体数组的长度 lea dx,stu[di].stuname mov ah,0ah int 21h ;输入学生姓名 xor ax,ax mov al,stu[di].stuname+1 inc al xor bx,bx lea bx,stu[di].stuname+2 add bx,ax sub bx,di mov stu[di].stuname[bx],'$' ;在输入的姓名后面加一个结束符'$' lea dx,msg6 mov ah,09h int 21h ;提示输入学生成绩 call input ;调用input函数输入学生成绩 mov stu[di].stugrade,ax 28 inc si sub cx,1 jnz lop1;cx-1=0 ?,若不是则跳回lop1循环输入下一个学生信息 pop di pop si pop dx pop cx pop bx pop ax ret stuinf endp stusort proc ;排序函数 push ax push bx push cx push dx push si push di xor cx,cx mov cx,0 ;cx起始值为0,控制第一层循环 lop3: xor si,si mov si,cx xor ax,ax mov al,a[si] xor bx,bx mov bl,25 mul bl mov si,ax ;这一段时将a[cx]*25赋给si mov max,cx ;max保存每一次循环中成绩最高的学生在序号数组里面对应的元素下 29 标,初始值为cx xor dx,dx mov dx,cx inc dx ;将cx赋给dx后dx+1,dx控制第二层循环 xor di,di mov di,dx xor ax,ax mov al,a[di] mul bl xor di,di mov di,ax ;这一段时将a[cx-1]*25的值赋给di lop4: push si xor si,si mov si,max xor ax,ax mov al,a[si] mul bl mov si,ax ;a[max]*25的值赋给si xor ax,ax mov ax,stu[si].stugrade xor si,si pop si cmp ax,stu[di].stugrade jnb next1 mov max,dx ;stu[a[max]*25].stugrade>stu[di].stugrade ?,是则跳至next1继续,不是则将dx的值赋给max next1: inc dx mov di,dx xor ax,ax 30 mov al,a[di] mul bl mov di,ax cmp dx,num jb lop4 ;dx+1 inc cx xor ax,ax mov ax,num sub ax,1 cmp cx,ax jb lop3 ;cx stusort endp 31 pop si ;这一段时交换a[cx]与a[max]的值 print proc ;所有学生信息打印函数 push ax push bx push cx push dx push si push di xor cx,cx mov cx,num mov si,0 lop2: lea dx,msg5 mov ah,09h int 21h mov al,a[si] xor bx,bx mov bl,25 mul bl mov di,ax lea dx,stu[di].stuname+2 mov ah,09h int 21h lea dx,msg6 mov ah,09h int 21h call bta inc si sub cx,1 jnz lop2 点 pop di pop si pop dx ;提示输出学生姓名 ;输出学生姓名 ;提示输出学生成绩 ;调用bta函数输出学生成绩 ;cx-1=0?,不是则跳至lop2循环,是则返回调用32 pop cx pop bx pop ax ret print endp print1 proc push ax push bx push cx push dx push si push di xor cx,cx mov cx,ranking mov si,cx mov al,a[si] xor bx,bx mov bl,25 mul bl mov di,ax lea dx,msg5 mov ah,09h int 21h lea dx,stu[di].stuname+2 mov ah,09h int 21h lea dx,msg6 mov ah,09h int 21h call bta pop di pop si ;单个学生信息输出函数 ;提示输出学生姓名 ;输出学生姓名 ;提示输出学生成绩 ;调用bta函数输出学生成绩33 pop dx pop cx pop bx pop ax ret print1 endp bta proc push ax push bx push dx push si mov si,2 xor ax,ax mov ax,stu[di].stugrade lop5: mov bl,10 div bl add ah,30h mov indata[si],ah ; 将学生成绩送给ax后除10,并将余数放入indata[si]里面 cmp al,0 jz next4 ;al=0 ?,即除后的商是否为0,若为0则跳至next4执行,否执行下一条语句 inc si xor ah,ah jmp lop5 next4:push di xor di,di mov di,2 lop6: mov bl,indata[si] mov stuGrade[di],bl dec si inc di 34 cmp si,1 jz next5 jmp lop6 ;将字符串数组indata的内容倒序送入stuGrade内 next5:mov stuGrade[di],'$' ;在字符串的末尾加结束符'$' xor di,di pop di lea dx,stuGrade+2 mov ah,09h int 21h lea dx,msg4 mov ah,09h int 21h pop si pop dx pop bx pop ax ret bta endp code ends end start 显示字符串stuGrade 显示回车换行 35 ; ;2,流程图: (1).主函数流程图如图 (2).input函数流程图如图 开始 提示输入学生总数并调用input函数 进行输入 提示输入学生信息 并调用stuinf函数进行输入 调用stusort函数对学生进行排序 调用print函数打印排序好了的学生 信息 提示输入学生名次,并调用input 函数进行输入 ax>0 Y ax<2 Y N 调用print1函数打印对应名次的学生 信息 返回 图 3.1 入口 从键盘上接收字符串保存 在indata里面 Cl=indata+1;si=2; ax=(indata[si]-30h)+ax ax=ax*10 N cx-1=0 Y 显示回车换行 返回 图 3.2 36 (3).stuinf函数流程图如图 ( 4).stusort函数流程图如图3.4 入口 CX=num 提示并输入学生 姓名 在学生姓名后加结 束符’$’ 提示并调用 input 函数输入学生成绩 提示并输入学生姓 名 N cx-1=0? Y 返回 图 3.3 入口 cx=0 si=a[cx]*25,max=cx dx=cx; di=a[dx]*25 ax=stu[a[max]].stugrade Y ax>=stu[di].stugrad N max=dx dx=dx+1;di=a[dx]*25 Y dx (5).print函数流程图如图 (6).print1函数流程图如图 入口 cx=num;si=0 输出name: di=a[si]*25 输出stu[di].stuname 输出grade: 调用bta函数输出学生成绩 si=si+1,cx=cx-1 cx=0 ? N Y 返回 图 3.5 入口 di=a[ranking]*25 输出name: 输出stu[di].stuname 输出grade: 调用bta函数输出学生成绩 返回 图3.6 38 (7).bta函数流程图如图所示 入口 si=2;ax=stu[di].stugrade bl=10; ax/bl indata[si]=ah Y al=0 ? N si=si+1 xor ah,ah push di mov di,2 push stuGrade[di]=indata[si] si=si-1 di=di+1 N si=1 ? Y stuGrade[di]=’$’ ; pop di 输出字符串stuGrade 输出回车换行 返回 图 3.7 39 3,运行 图 3.8 40 图 3.9 五、结论 通过这个实验,了解了如何定义和运用结构体,学会更多的汇编指令。 了解到一些关于汇编的数据存储知识。 其次是程序调试中遇到了诸多问题,问老师以及同学得到解决,学习到了汇编语言要常问老师同学 在运行过程中,有一点还做的不足。 如果有两个同学的成绩是相同的那么他们两的名次就是按我输入的先后次序来排名的,也就是说不能解决排名并列问题,我希望在今后的实验中能学习到更多的汇编编程方法,以实现更多更好的编程思想和编程的技巧。 41 实验四 子程序及堆栈 4.1 求员工年工作量 一、实验目的 本次实验主要达到如下目的: 某单位在计算工作量时采用工作量=工作时间(小时)*系数,计算员工的工作量。其中工种不同其系数也不一样,该单位有很多不同的工种(不大于100)。请从键盘上输入每一位工人一年的工作时间(按小时计算,精确到小数点后一位)以及该工人的工种所对应的系数(精确到小数点后两位),球该单位所有员工(不超过1000人)一年的工作量。 二、实验环境 该试验包括的硬件和软件条件如下: 1、硬件环境 (1)Intel Core Duo CPU P8700 (2)内存4G 2、软件环境 (1)Window XP Professor (2)MASM6.11开发软件 三、实验步骤 1、对上述问题进行分析,写出分析报告及相关算法; 2、写出各个模块的说明; 3、画出各个模块的流程图; 4、画出各个模块之间的调用关系; 5、编写程序实现上述要求。 6、写出满足上述要求的实验报告。 42 四、实验结果及其分析 1、分析实验要求并写出分析报告计算法: 本程序由三个部分组成:输入部分(包含输入员工数量,输入每位员工的年工作时间和输入每位员工的工种对应的系数);计算该公司所有员工的年总工作量;显示该公司所有员工的年总工作量。 在输入部分,须将十进制数ASCⅡ码转换成二进制数。因为年工作时间和工种可能存在小数,而工作时间的小数一般只有一位,工种系数的小数一般只有两位,所以为了方便计算,将每一位员工的工作时间都乘以10将每一个员工对应的工种都乘以100,从而去掉小数,做成整数运算。而这种算法是从键盘上一位一位的输入,当遇到小数点就直接去掉小数点,接收下一位数。所以在输入时要注意的是输入的工作时间必须包含一位小数(如351.0),输入的工种系数必须包含两位小数(如65.00)。在计算年总工作量,因为所有员工的年总工作量有可能会超过65535,因此需要采用32位寄存器,从而简化运算的复杂性。在输出年总工作量部分,因为所有员工的工作时间都乘了10,工种系数都乘了100,所以计算出来的年总工作量是实际年总工作量的1000倍,所以应该把小数点放在倒数第3个位置,同时输出小数点后三位。 2、该程序的主要模块及其说明如下: (1).主程序模块 主程序主要实现各模块之间的联系,显示提示信息。 (2).输入及转换模块:input 输入:从键盘上输,可输入整数,也可输入小数 输出:将输出结果保存在DX寄存器中 功能:一位一位的从键盘上接收,将非小数点的每一位数转换为相应其对应的BCD码,并将DX*10后加入DX中。所以当输入的数中含有一位小数是就默认乘了10,含有两位小数就默认乘了100。 (3).计算模块:ctotal 输入:从DX寄存器中获取 输出:输出结果保存在total存储单元中 功能:循环调用input子程序输入工作时间和工种系数,两次调用输入完成后将二者相乘后加入total存储单元中。调用次数为员工的人数。 (4).输出模块:output 输入:total存储单元 输出:tot开始的内存单元,并显示在计算机屏幕上。 43 功能:将total存储单元的内容转换成十进制数ASCⅡ码形式,并在输出结果的倒数第3位前插入小数点,然后存储在tot开始的存储单元,最后一位一位将结果输出。在输出前将整数前面的0去掉。 3、程序流程框图: 主程序开始 提示输入信息, Num 输入员工个数 调用ctotal输入每个员工的工作时间和工种系 数,并计算公司年总工作 调用output转换并显示 该公司的年总工作量 N 结束 图4.1 主程序流程图 Y ctotal开始 CX num 调用input输入员工工作时间保存到ebx 调用input输入工种系数 eax=bx*edx total total+eax cx=cx-1=0? 返回 图4.2 ctotal函数流程图 44 调用01H系统功能调用从键盘上输入一位数送给AL DX 0 input开始 N AL=回车? Y Y 45 AL=’.’? N 返回 将AL中值的十进制数ASCⅡ码转换成BCD码送给CL,DX=DX*10,CH清零,DX=DX+CX 图4.3 input函数流程图 Eax=0? N dec si 是倒数2位? Edx清零,eax/ebx后把余数DL送给[si] output开始 Si tot的最后一位数的偏移地址 Eax total mov ebx,10 N Y Dec si ,si=’.’ Y Y [si]=0? N N Y Si=[si] +30H [si]=’.’? dec si inc cx inc si dec cx Si tot的偏移地址,CX=17 N 图4.4 output函数流程图 46 显示, inc si CX CX-1=0? Y 返回 4、各个模块之间的调用关系如图4.5所示: 图4.5 实验模块层次图 5、实验源代码: .model small .386p data segment msg1 db \"Please input number of members:$\" msg2 db 0ah,0dh,\"Please input work time of a member:$\" msg3 db 0ah,0dh,\"Please input the type of work of a member:$\" msg4 db 0ah,0dh,\"The total gongzuoliang of all members is:$\" msg5 db 0ah,0dh,'$' num dw 0 time dw 0 total dd 0 tot db 16 dup(0) data ends stack segment para stack db 20h dup(0) stack ends code segment assume cs:code,ss:stack,ds:data start: mov ax,data mov ds,ax lea dx,msg1 47 主程序模块 Ctotal Input Output l mov ah,09h int 21h call input mov num,dx call ctotal lea dx,msg4 mov ah,09h int 21h call output mov ah,4ch int 21h input proc push ax push bx push cx xor dx,dx lop1: mov ah,01h int 21h cmp al,0dh jz extp1 cmp al,'.' jz lop1 and al,0fh mov cl,al mov ax,dx mov bx,10 mul bx mov dx,ax mov ch,0 add dx,cx next: jmp lop1 extp1: pop cx pop bx pop ax 48 ret input endp ctotal proc push eax push cx push dx xor cx,cx mov cx,num lop2: lea dx,msg2 mov ah,09h int 21h call input xor bx,bx mov bx,dx lea dx,msg3 mov ah,09h int 21h call input xor eax,eax mov eax,ebx xor ebx,ebx mov bx,dx mul ebx add total,eax loop lop2 pop dx pop cx pop eax ret ctotal endp output proc push si 49 push ebx push eax push cx xor cx,cx lea si,tot add si,16 mov eax,total mov ebx,10 lop3: xor edx,edx div ebx mov [si],dl inc cx cmp cx,3 jnz next2 dec si push ax mov al,'.' mov [si],al pop ax next2: cmp eax,0 jz next3 dec si jmp lop3 next3: lea si,tot mov cx,17 lop4: mov al,[si] inc si dec cx cmp al,0 jz lop4 dec si inc cx lop5: mov al,[si] cmp al,'.' 50 jz next4 or al,30h next4: mov dl,al mov ah,02h int 21h inc si loop lop5 pop cx pop eax pop ebx pop si ret output endp code ends end start 6、程序执行情况如图4.6所示,输入了三个员工的工作时间和工种系数,计算出一年的工作量为:1281800.293 图4.6 程序执行情况 51 五、结论 这个实验主要是熟悉子程序的实现,虽然在前几个实验也用过子程序,但那是都没有怎么去深究它的原理,通过这个实验我进一步熟悉了子程序的定义方法和调用方法。并且通过此实验我学会了怎么对小数进行运算。 实验4.2 子程序与主程序之间的参数传递 一、实验目的 本次实验主要达到如下目的: 通过定义公共变量的方法实现主程序和子程序之间的参数传递。如在主程序和子程序中都定义一个具有Common和Public属性的数据段,然后将需要传递的参数放在这个段中,这样在主程序和子程序中都可以使用这个段所定义的变量了。 二、实验环境 该试验包括的硬件和软件条件如下: 1、硬件环境 (1)Intel Core Duo CPU P8700 (2)内存4G 2、软件环境 (1)Window XP Professor (2)MASM6.11开发软件 三、实验步骤 1、设计一个子程序模块,将带传递的变量k所在的数据段属性设置成Common,并实现十进制ASCⅡ码到二进制数的转换,将转换结果存放在变量k中。 2、设计一个子程序模块,将带传递的变量m所在的数据段属性设置成Public并实现十进制ASCⅡ码到二进制数的转换,将转换结果存放在变量m中。 3、比较上述两种属性下数据段的区别。 52 4、设计一个主程序模块,通过子程序调用的方式,将(1)和(2)执行后存放在k和m中的变量值取出来存放在该主程序定义的变量i和j中。 5、对上述程序进行调试,写出调试过程中内存单元和寄存器的变化,并解释为什么? 6、根据上述要求,写出实验报告。 四、实验结果及其分析 1.、设计一个子程序模块,将带传递的变量k所在的数据段属性设置成Common,并实现十进制ASCⅡ码到二进制数的转换,将转换结果存放在变量k中。其源代码如下: extrn exit:far public input data1 segment common k dw ? data1 ends stack segment para stack db 20h dup(0) stack ends code segment assume cs:code,ss:stack,ds:data1 input: push ds mov ax,data1 mov ds,ax xor dx,dx lop: mov ah,01h int 21h cmp al,0dh jz next xor bx,bx and al,0fh mov bl,al mov ax,dx mov cx,10 53 mul cx add ax,bx mov dx,ax jmp lop next: mov k,dx pop ds jmp exit code ends end 2、设计一个子程序模块,将带传递的变量m所在的数据段属性设置成Public并实现十进制ASCⅡ码到二进制数的转换,将转换结果存放在变量m中。 public m data2 segment public m dw ? data2 ends stack segment para stack db 20h dup(0) stack ends code segment assume cs:code,ss:stack,ds:data2 atoi proc far push ds mov ax,data2 mov ds,ax lop: mov ah,01h int 21h cmp al,0dh jz next xor bx,bx and al,0fh mov bl,al mov ax,m mov cx,10 mul cx 54 add ax,bx mov m,ax jmp lop next: pop ds ret atoi endp code ends end 3、两种数据段属性的区别:Common属性的数据段的特点是将其他与与此段同名、同类别的段从同一个地址开始装入,即各个逻辑段重叠在一起;而Public属性的数据段的特点是将其他与此段同名、同类别的段相邻的连接在一起,形成一个大的逻辑段。 4、设计一个主程序模块,通过子程序调用的方式,将(1)和(2)执行后存放在k和m中的变量值取出来存放在该主程序定义的变量i和j中。其源代码如下: extrn input:far,atoi:far,m:word public exit data1 segment common j dw 30h data1 ends data2 segment public i dw 20h data2 ends stack segment para stack db 20h dup(0) stack ends code segment assume cs:code,ss:stack,ds:data2 start: mov ax,data2 mov ds,ax call atoi mov ax,m mov i,ax jmp input 55 exit: mov ah,4ch int 21h code ends end start 5、调试程序: 由图5.1可以看到起始是内存单元209EH:0000H的内容为0030h,且内存单元209EH:0010H的内容为0020h。 图4.6 初始内存单元图 当调用子程序atoi输入111后内存变化如图5.2所示,可以看到内存单元209EH:0020H的内容变为了6F,此内存单元为m所在的内存单元。因为i的初始值为20h,由此可以看到m的内存单元是连接在i所在数据段之后的。 图 4.7 调用atoi后内存单元图 当执行mov ax,m,mov i,ax之后内存单元变化如图5.3所示,可以看到原来存放20的内存单元变成了6F。此时完成了m到i的赋值。 56 图4.8完成m向i赋值后内存单元变化图 进入子程序input后,当循环输入完122并完成对k赋值后内存单元变化如图5.4所示,可以看到原来存30的位置,即原来j的位置变成了7A。这是因为在主函数和input子程序中的数据段data1都为Common属性,两个数据段的起始地址是一样的,而j和k分别都是两个数据段的第一个元素,所以j和k公用一个内存单元。所以改变k的值即是改变j的值,所以当k赋值完成后j也由30H变成了7AH。 图4.9完成对k赋值后的内存单元及寄存器的变化 57 五、结论 本次实验的主要目的就是比较Common和Public两种属性的数据段在进行函数传递时的区别进而比较两种属性的区别。由实验结果可知Public属性的数据段是将同名的数据的段连接在一起;而Common属性的数据段是将同名同类别的数据段放在相同起始地址的内存单元中。所以子程序中Common属性的k能够覆盖主程序中Common属性j,而子程序中Public属性m只能拼接在主程序Public属性的数据i内存单元的后面。 58 实验五 汇编语言和C语言间的调用 实验5.1 C语言和汇编语言混合编程下的排序算法 一、实验目的 本次实验主要达到如下目的: 利用C语言程序和汇编语言程序实现从键盘上输入的若干个数的排序。其中输入输出以及提示信息用C语言程序实现,排序操作用汇编程序实现。 二、实验环境 该试验包括的硬件和软件条件如下: 1、硬件环境 (1)80386或以上系列PC机 (2)内存不少于16M 2、软件环境 (1)Dos+Win31 或Windows 95或以上版本操作系统 (2)汇编语言编译软件Msam6.11 三、实验步骤 1、分别编写相应的C语言程序和汇编程序 2、描述混合编译的过程 3、撰写相应的实验报告 四、实验结果及其分析 1、编写程序 (1)、在Turbo C开发环境下编写C语言程序,从键盘上输入总数n及数组a[n];并调用外部函数sort(int n,int a[]),然后再输出由小到大排好序的数组a[n]。其程序如下所示: #include\"stdio.h\" 59 extern int sort(int n,int a[]); void main() { } (2)、在汇编语言集成环境中编写如下的汇编程序程序: .model small .code public _sort _sort proc org 2000h push bp int a[100],n,i; printf(\"Please input the total number:\"); scanf(\"%d\for(i=0;i printf(\"After sort:\"); for(i=0;i mov bp,sp xor ax,ax mov ax,[bp+4] mov si,[bp+6] xor bx,bx mov bx,2 mul bl add ax,si xor cx,cx mov cx,ax lop1: mov di,si add di,2 lop2: mov dx,[si] 60 cmp dx,[di] jna next1 xchg dx,[di] mov [si],dx next1: add di,2 cmp cx,di jna next2 jmp lop2 next2: add si,2 cmp cx,si jna next3 jmp lop1 next3: pop bp ret _sort endp End 2、混合编译过程: (1)、利用Turbo C 集成环境提供的编译器将C语言程序编译成Obj文件,设生成的文件为51.obj,如图5.1.1所示 图5.1.1在Turbo C中编译编写的C语言程序 61 (2)、编辑工程文件AAA.Mak,将51.obj文件以及编写的C语言程序时需要用到的库函数都加入到该工程文件中。该C语言程序主要用到了COS.obj,CS.lib。所以该工程文件中就有了四个个文件:111.asm,51.obj,.COS.obj,CS.lib.如图5.1.2所示。 图5.1.2 编辑工程文件 (3)、在MASM6.11集成环境编译连接该工程文件,然后运行。其执行过程及结果如图5.1.3所示。 图 5.1.3 62 五、结论 本实验时C语言和汇编语言混合编程,在该实验中要注意的是C语言程序中的数组和汇编语言程序之间的传递是以地址的形式。如extern int sort(int n,int a[]);的调用方式,在汇编中BP+4指向的内存单元放的是n的值,而BP+6中放的是数组a[]的起始地址,所以在mov si,[bp+6]之后si中放的也是数组的起始地址,即si指向数组a[]中的第一个元素,si+2指向的是数组a[]中第二个元素,si+4指向数组a[]中的第三个元素,以此类推。 63 实验5.2 汇编语言对C语言的调用求平均数 一、实验目的 1、复习求平均数的算法 2、掌握汇编语言程序对C语言的调用 3.掌握调用时必须遵循的各项约定 二、实验环境 该试验包括的硬件和软件条件如下: 1、硬件环境 (1)Intel Core Duo CPU P8700 (2)内存4G 2、软件环境 (1)Window XP Professor (2)MASM6.11开发软件 三、实验步骤 采用c语言程序和汇编语言程序混合编程,求从键盘上输入的若干个数的平均值,其中输入和平均结果的输出用c汇编语言程序实现,求平均值的算法由c语言实现,汇编语言调用c语言编写的函数实现上述要求。 要求: (1) 编写实现上述要求的c语言程序和汇编语言程序。 (2) 绘制实现上述操作的汇编语言程序的流程图。 (3) 撰写相应的实验报告。 四、实验结果及其分析 1、编写实验要求的C语言程序和汇编语言程序 C语言程序: #include\"stdio.h\" extern begin(); 64 char su[50]; int sum; int s[50]; int n,ag; void avg(); int main() { begin(); return 1; } void avg() { int i; sum=0; for(i=0;i 汇编语言程序: extrn _avg:far extrn _n:word,_s:word,_sum:word,_su:word .model small .data msg1 db \"Please input the total number:$\" msg2 db \"Please input the number:$\" msg3 db \"The average is:$\" Enmsg db 0ah,0dh,'$' .code public _input public _output public _begin _begin proc mov ax,ds mov es,ax 65 lea dx,msg1 mov ah,09h int 21h call _input mov _n,dx mov cx,_n lea si,_s mov si,0 lea dx,Enmsg mov ah,09h int 21h lea dx,msg2 mov ah,09h int 21h lop1:lea dx,Enmsg mov ah,09h int 21h call _input mov _s[si],dx add si,2 loop lop1 call _avg lea dx,Enmsg mov ah,09h int 21h lea dx,msg3 mov ah,09h int 21h call _output exit1:mov ah,4ch int 21h _begin endp _input proc push ax 66 push bx push cx xor dx,dx lop2: mov ah,01h int 21h cmp al,0dh jz extp1 cmp al,'.' jz next and al,0fh mov cl,al mov ax,dx mov bx,10 mul bx mov dx,ax mov ch,0 add dx,cx next:jmp lop2 extp1:pop cx pop bx pop ax ret _input endp _output proc push ax push bx push cx push dx push si xor ax,ax xor dx,dx lea si,_su add si,100 mov ax,_sum 67 mov bl,10 lop3:div bl add ah,30h mov [si],ah sub si,1h xor ah,ah add dh,1h cmp al,0 jnz lop3 lop4:add si,1h mov dl,[si] mov ah,02h int 21h sub dh,1h xor dl,dl cmp dh,0 ja lop4 pop si pop dx pop cx pop bx pop ax ret _output endp End 68 2.绘制实现上述操作的汇编语言程序流程图 _begin开始: 主程序开始 调用input输入 调用avg计算平均数 调用output输出 结束 图5.2.1主程序流程图 input输入函数: 开始 AX、BX、CX进栈,同时清空DX 从键盘输入 Y 是否为回车 N CL置0,AX转换为十进制,AL->CL AX,BX,CX出栈 BX=10 AX=DX BX=BX*10 返回 图5.2.2input子程序流程 69 avg求平均数函数: Avg开始 N I output输出函数: output开始 Si<-sum最后一位数的偏移地址,Eax<-avg dx清0,ax<-(dx,ax)/bx的商,dx<-(dx,ax)/bx的余数,[si]<-dl N 是否是倒数第2位? Y Si<-si-1,[si] 3.实验运行的结果如下: 输入7个数字12 34 54 98 88 66 55,得到平均值为58,如下图 5.2.5所示: 图 5.2.5 五、结论 通过本次实验,我学习了如何用汇编语言调用C语言,在其中有很多需要注意的细节,比如说汇编中参数通过堆栈实现。并了解了其中的一些特殊的命名规则,还复习了C语言的排序算法。 72 因篇幅问题不能全部显示,请点此查看更多更全内容