1、第 4 章,教学重点,综合应用第2章硬指令和第3章伪指令,第4章从程序结构角度展开程序设计,重点掌握: 分支结构程序设计 循环结构程序设计 子程序结构程序设计,4.1 顺序程序设计,顺序程序完全按指令书写的前后顺序执行每一条指令,是最基本、最常见的程序结构,例4.1 计算,例4.2 移位,例题 代码转换,例4.1,.model small.stack.data X dw 5 Y dw 6 Z dw 7 W dw ?,.code.startupmov ax,Xadd ax,Yadd ax,Zmov W,ax.exit 0end,例4.21/2,.data qvar dq 123456788765
2、4321h.codemov al,byte ptr qvar6mov byte ptr qvar7,almov al,byte ptr qvar5mov byte ptr qvar6,almov al,byte ptr qvar4mov byte ptr qvar5,almov al,byte ptr qvar3mov byte ptr qvar4,al,图示,例4.22/2,mov al,byte ptr qvar2mov byte ptr qvar3,almov al,byte ptr qvar1mov byte ptr qvar2,almov al,byte ptr qvar0mov b
3、yte ptr qvar1,almov byte ptr qvar0,0,12 34 56 78 87 65 43 21h,34 56 78 87 65 43 21 00h,移位后,图示,64位数据左移8位,12,34,56,78,87,65,43,21,00,qvar0,qvar1,qvar2,qvar3,qvar4,qvar5,qvar6,qvar7,例题 代码转换1/2,;查表法,实现一位16进制数转换为ASCII码显示.model small.stack 256.data ASCII db 30h,31h,32h,33h,34h,35hdb 36h,37h,38h,39h ;09的AS
4、CII码db 41h,42h,43h,44h,45h,46h;AF的ASCII码 hex db 0bh;任意设定了一个待转换的一位16进制数,例题 代码转换2/2,.code.startupmov bx,offset ASCII ;BX指向ASCII码表mov al,hex;AL取得一位16进制数,正是ASCII码表中位移and al,0fh ;只有低4位是有效的,高4位清0xlat ;换码:ALDS:BXALmov dl,al ;入口参数:DLALmov ah,2 ;02号DOS功能调用int 21h ;显示一个ASCII码字符.exit 0end,4.2 分支程序设计,分支程序根据条件是真
5、或假决定执行与否 判断的条件是各种指令,如CMP、TEST等执行后形成的状态标志 转移指令Jcc和JMP可以实现分支控制;还可以采用MASM 6.x提供的条件控制伪指令实现,单分支:求绝对值等,双分支:例4.3等,多分支:例4.4等,单分支程序设计,条件成立跳转,否则顺序执行分支语句体;注意选择正确的条件转移指令和转移目标地址,例题 求绝对值,;计算AX的绝对值cmp ax,0jns nonneg ;分支条件:AX0neg ax ;条件不满足,求补 nonneg: mov result,ax ;条件满足 ;计算AX的绝对值cmp ax,0jl yesneg ;分支条件:AX0jmp nonne
6、g yesneg: neg ax ;条件不满足,求补 nonneg: mov result,ax ;条件满足,Good,Bad,例题 无符号数除以2,;将AX中存放的无符号数除以2,如果是奇数,则加1后除以2test ax,01h ;测试AX最低位jz even ;最低位为0:AX为偶数add ax,1;最低位为1:AX为奇数,需要加1 even: rcr ax,1 ;AXAX2;如果采用SHR指令,则不能处理AXFFFFH的特殊情况,双分支程序设计,条件成立跳转执行第2个分支语句体,否则顺序执行第1个分支语句体。注意第1个分支体后一定要有一个JMP指令跳到第2个分支体后,例题 显示BX最高位
7、,shl bx,1 ;BX最高位移入CFjc one ;CF1,即最高位为1,转移mov dl,0;CF0,即最高位为0,DL0jmp two ;一定要跳过另一个分支体 one: mov dl,1 ;DL1 two: mov ah,2int 21h ;显示,对比,双分支程序改为单分支程序,例题 显示BX最高位,shl bx,1 ;BX最高位移入CFjnc one ;CF0,即最高位为0,转移mov dl,1;CF1,即最高位为1,DL1jmp two ;一定要跳过另一个分支体 one: mov dl,0 ;DL0 two: mov ah,2int 21h ;显示,双分支程序改为单分支程序,对比
8、,例题 显示BX最高位,mov dl,0 ;DL0shl bx,1 ;BX最高位移入CFjnc two ;CF0,最高位为0,转移mov dl,1 ;CF1,最高位为1,DL1 two: mov ah,2int 21h ;显示,编写分支程序,需留心分支的开始和结束,例4.3 判断有无实根1/2,.startup mov al,_b imul al mov bx,ax ;BX中为b2 mov al,_a imul _c mov cx,4 imul cx ;AX中为4ac(DX无有效数据),例4.3 判断有无实根2/2,cmp bx,ax ;比较二者大小jge yes ;条件满足?mov tag,
9、0;第一分支体:条件不满足,tag0jmp done ;跳过第二个分支体 yes: mov tag,1;第二分支体:条件满足,tag1 done: .exit 0,;寄存器AL中是字母Y或y,则令AH0;否则令AH1cmp al,Y ;AL是大写Y否?jz next ;是,转移cmp al,y ;AL是小写y否?jz next ;是,转移mov ah,-1 ;不是Y或y,则AH1,结束jmp done ;一定要跳过另一个分支体 next: mov ah,0 ;是Y或y,则AH0,结束 done: .,例题 单分支和双分支,多分支程序设计,多个条件对应各自的分支语句体,哪个条件成立就转入相应分支
10、体执行。多分支可以化解为双分支或单分支结构的组合,例如:or ah,ah ;等效于cmp ah,0jz function0 ;ah0,转向function0dec ah ;等效于cmp ah,1jz function1 ;ah1,转向function1dec ah ;等效于cmp ah,2jz function2 ;ah2,转向function2,图示,多分支结构,地址表形成多分支,需要在数据段事先安排一个按顺序排列的转移地址表 输入的数字作为偏移量。因为只有2个字节16位偏移地址,所以偏移量需要乘2 关键是要理解间接寻址方式JMP指令,.data msg db Input number(18
11、):,0dh,0ah,$ msg1 db Chapter 1 : .,0dh,0ah,$ msg2 db Chapter 2 : .,0dh,0ah,$. msg8 db Chapter 8 : . ,0dh,0ah,$ table dw disp1,disp2,disp3,disp4dw disp5,disp6,disp7,disp8;取得各个标号的偏移地址,例4.4 数据段1/3,start1: mov dx,offset msg ;提示输入数字mov ah,9int 21hmov ah,1 ;等待按键int 21hcmp al,1 ;数字 8?ja start1and ax,000fh
12、;将ASCII码转换成数字,例4.4 代码段2/3,dec axshl ax,1 ;等效于add ax,axmov bx,axjmp tablebx ;(段内)间接转移:IPtable+bx start2: mov ah,9int 21h.exit 0 disp1: mov dx,offset msg1 ;处理程序1jmp start2.,例4.4 代码段3/3,4.3 循环程序设计,循环结构一般是根据某一条件判断为真或假来确定是否重复执行循环体 循环指令和转移指令可以实现循环控制;还可以采用MASM 6.x提供的循环控制伪指令实现,循环指令LOOPE:例4.6,转移指令:例4.7,多重循环:
13、例4.8等,循环指令LOOP:例4.5等,循环结构,.model small.stack.data sum dw ?.code.startupxor ax,ax ;被加数AX清0mov cx,100 again: add ax,cx;从100,99,.,2,1倒序累加loop againmov sum,ax ;将累加和送入指定单元.exit 0end,例4.5 求和,计数控制循环循环次数固定,;用二进制显示从键盘输入的一个字符的ASCII码mov ah,1 ;从键盘输入一个字符int 21hmov bl,al ;BLAL字符的ASCII码 ;DOS功能会改变AL内容,故字符ASCII码存入BL
14、mov ah,2mov dl,: ;显示一个分号,用于分隔int 21h,习题4.161/2,mov cx,8 ;CX8(循环次数) again: shl bl,1 ;左移进CF,从高位开始显示mov dl,0 ;MOV指令不改变CFadc dl,30h ;DL030HCF ;CF若是0,则DL0;若是1,则DL1mov ah,2int 21h ;显示loop again ;CX减1,如果CX未减至0,则循环,习题4.162/2,计数控制循环循环次数固定,.startupmov ax,wordX ;测试目标送AXmov cx,16 ;循环计数器置初值mov dl,-1 ;计位器置初值 agai
15、n: inc dltest ax,1ror ax,1 ;循环指令不影响ZFloope again ;CX0且ZF=1(测试位为0),继续循环je notfoundmov byteY,dljmp done notfound: mov byteY,-1 ;ZF=1,16个位均为0 done: .exit 0,例4.6,计数控制循环最大循环次数固定,满足条件退出,mov bx,offset string again: mov al,bx ;取一个字符or al,al ;是否为结尾符0jz done ;是,退出循环cmp al,A ;是否为大写AZjb nextcmp al,Zja nextor al
16、,20h;是,转换为小写字母(使D5=1)mov bx,al ;仍保存在原位置 next: inc bxjmp again ;继续循环 done: .exit 0,例4.7 大小写,条件控制循环利用标志退出,冒泡法,“冒泡法”是一种排序算法,不是最优的算法,但它易于理解和实现 冒泡法从第一个元素开始,依次对相邻的两个元素进行比较,使前一个元素不大于后一个元素;将所有元素比较完之后,最大的元素排到了最后;然后,除掉最后一个元素之外的元素依上述方法再进行比较,得到次大的元素排在后面;如此重复,直至完成就实现元素从小到大的排序 这需要一个双重循环程序结构,图示,冒泡法的排序过程,mov cx,cou
17、nt ;CX数组元素个数dec cx ;元素个数减1为外循环次数 outlp: mov dx,cx ;DX内循环次数mov bx,offset array inlp: mov al,bx ;取前一个元素cmp al,bx+1 ;与后一个元素比较jna next;前一个不大于后一个元素,则不进行交换xchg al,bx+1 ;否则,进行交换mov bx,al next: inc bx ;下一对元素dec dxjnz inlp ;内循环尾loop outlp ;外循环尾,例4.8,计数控制双重循环,;现有一个以$结尾的字符串,要求剔除其中的空格.data string db Let us have
18、 a try !,$.code.startupmov si,offset string outlp: cmp byte ptr di,$;外循环,先判断后循环jz done ;为$结束cmp byte ptr si, ;检测是否是空格jnz next ;不是空格继续循环,例4.9 剔除空格1/2,mov di,si ;是空格,进入剔除空格分支;该分支是循环程序段 inlp: inc dimov al,di ;前移一个位置mov di-1,alcmp byte ptr di,$;内循环,先循环后判断jnz inlpjmp outlp next: inc si ;继续对后续字符进行处理jmp ou
19、tlp done: .exit 0 ;结束,例4.9 剔除空格2/2,条件控制双重循环,4.4 子程序设计,把功能相对独立的程序段单独编写和调试,作为一个相对独立的模块供程序使用,就形成子程序 子程序可以实现源程序的模块化,可简化源程序结构,可以提高编程效率,子程序设计要利用过程定义伪指令,参数传递是子程序设计的重点和难点,子程序可以嵌套; 一定条件下,还可以递归和重入,4.4.1 程序定义伪指令,过程名 proc near|far. 过程名 endp,过程名(子程序名)为符合语法的标识符 NEAR属性(段内近调用)的过程只能被相同代码段的其他程序调用 FAR属性(段间远调用)的过程可以被相同
20、或不同代码段的程序调用 对简化段定义格式,在微型、小型和紧凑存储模式下,过程的缺省属性为near;在中型、大型和巨型存储模式下,过程的缺省属性为far 对完整段定义格式,过程的缺省属性为near 用户可以在过程定义时用near或far改变缺省属性,子程序的常见格式,subname proc ;具有缺省属性的subname过程push ax ;保护寄存器:顺序压入堆栈push bx ;ax/bx/cx仅是示例push cx ;过程体pop cx ;恢复寄存器:逆序弹出堆栈pop bxpop axret ;过程返回 subname endp ;过程结束,;子程序功能:实现光标回车换行 dpcrlf
21、 proc ;过程开始push ax ;保护寄存器AX和DXpush dxmov dl,0dh ;显示回车mov ah,2int 21hmov dl,0ah ;显示换行mov ah,2int 21hpop dx ;恢复寄存器DX和AXpop axret ;子程序返回 dpcrlf endp ;过程结束,例题 无参数传递的子程序,ALdisp proc ;实现al内容的显示push ax ;过程中使用了AX、CX和DXpush cxpush dxpush ax ;暂存axmov dl,al ;转换al的高4位mov cl,4shr dl,clor dl,30h ;al高4位变成3cmp dl,3
22、9hjbe aldisp1add dl,7 ;是0Ah0Fh,还要加上7 aldisp1: mov ah,2 ;显示int 21h,例4.10 子程序1/3,pop dx ;恢复原ax值到dxand dl,0fh ;转换al的低4位or dl,30hcmp dl,39hjbe aldisp2add dl,7 aldisp2: mov ah,2 ;显示int 21hpop dxpop cxpop axret ;过程返回 ALdisp endp,例4.10 子程序2/3,. ;主程序,同例4.8源程序mov bx,offset array;调用程序段开始mov cx,count displp: m
23、ov al,bxcall ALdisp ;调用显示过程mov dl, ;显示一个逗号,分隔数据mov ah,2int 21hinc bxloop displp ;调用程序段结束.exit 0. ;过程定义end,例4.10 主程序3/3,HTOASC proc ;将AL低4位表达的一位16进制数转换为ASCII码and al,0fhcmp al,9jbe htoasc1add al,37h ;是0AH0FH,加37Hret ;子程序返回 htoasc1: add al,30h ;是09,加30Hret ;子程序返回 HTOASC endp,例题 具有多个出口的子程序,4.4.2 子程序的参数传
24、递,入口参数(输入参数):主程序提供给子程序 出口参数(输出参数):子程序返回给主程序 参数的形式: 数据本身(传值) 数据的地址(传址) 传递的方法: 寄存器 变量 堆栈,例4.11 求校验和,子程序计算数组元素的“校验和” 校验和是指不记进位的累加入口参数: 数组的逻辑地址(传址)元素个数(传值) 出口参数: 求和结果(传值),把参数存于约定的寄存器中,可以传值,也可以传址。 子程序对带有出口参数的寄存器不能保护和恢复(主程序视具体情况进行保护) 子程序对带有入口参数的寄存器可以保护,也可以不保护;但最好一致例4.11a 入口参数:CX元素个数, DS:BX数组的段地址:偏移地址 出口参数
25、:AL校验和,用寄存器传递参数,.startup;设置入口参数(含有DS数组的段地址)mov bx,offset array;BX数组的偏移地址mov cx,count ;CX数组的元素个数call checksuma ;调用求和过程mov result,al ;处理出口参数.exit 0,例4.11a 主程序,checksuma procxor al,al ;累加器清0 suma: add al,bx ;求和inc bx ;指向下一个字节loop sumaret checksuma endpend,例4.11a 子程序,主程序和子程序直接采用同一个变量名共享同一个变量,实现参数的传递 不通模
26、块间共享时,需要声明例4.11b 入口参数: count元素个数, array数组名(含段地址:偏移地址) 出口参数: result校验和,用变量传递参数,;主程序 call checksumb;子程序 checksumb procpush axpush bxpush cxxor al,al ;累加器清0mov bx,offset array;BX数组的偏移地址mov cx,count;CX数组的元素个数,例4.11b1/2,sumb: add al,bx ;求和inc bxloop sumbmov result,al ;保存校验和pop cxpop bxpop axret checksumb
27、 endp,例4.11b2/2,主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数 子程序将出口参数压入堆栈,主程序弹出堆栈取得它们例4.11c 入口参数: 顺序压入偏移地址和元素个数 出口参数: AL校验和,用堆栈传递参数,.startupmov ax,offset arraypush axmov ax,countpush axcall checksumcadd sp,4mov result,al.exit 0,例4.11c 主程序,图示,要注意堆栈的分配情况,保证参数存取正确、子程序正确返回,并保持堆栈平衡,checksumc procpush bpmov bp,sp ;利用BP间接
28、寻址存取参数push bxpush cxmov bx,bp+6 ;SS:BP+6指向偏移地址mov cx,bp+4 ;SS:BP+6指向元素个数xor al,al sumc: add al,bx inc bxloop sumcpop cxpop bxpop bpret checksumc endp,例4.11c 子程序,图示,堆栈区及参数,主程序实现平衡堆栈: add sp,n子程序实现平衡堆栈: ret n,子程序的嵌套,子程序内包含有子程序 的调用就是子程序嵌套 没有什么特殊要求,ALdisp proc push axpush cx ;实现al内容的显示push ax ;暂存axmov c
29、l,4shr al,cl ;转换al的高4位call htoasc ;子程序调用(嵌套)pop ax ;转换al的低4位 call htoasc ;子程序调用(嵌套)pop cxpop axret ALdisp endp,例4.10 嵌套子程序1/3,;将AL低4位表达的一位16进制数转换为ASCII码 HTOASC procpush axpush bxpush dxmov bx,offset ASCII;BX指向ASCII码表and al,0fh ;取得一位16进制数xlat CS:ASCII ;换码:ALCS:BXAL,注意数据在代码段CS,例4.10 嵌套子程序2/3,mov dl,al
30、 ;显示mov ah,2int 21hpop dxpop bxpop axret ;子程序返回;子程序的数据区 ASCII db 30h,31h,32h,33h,34h,35h,36h,37hdb 38h,39h,41h,42h,43h,44h,45h,46h HTOASC endp,例4.10 嵌套子程序3/3,这是一个具有局部变量的子程序。因为数据区与子程序都在代码段,所以利用了换码指令XLAT的另一种助记格式(写出指向缓冲区的变量名,目的是便于指明段超越前缀)。串操作MOVS、LODS和CMPS指令也可以这样使用,以便使用段超越前缀除采用段超越方法外,子程序与主程序的数据段不同时,我们还
31、可以通过修改DS值实现数据存取;但需要保护和恢复DS寄存器,子程序的递归,当子程序直接或间接地嵌套调用自身时称为递归调用,含有递归调用的子程序称为递归子程序 递归子程序必须采用寄存器或堆栈传递参数,递归深度受堆栈空间的限制例4.12:求阶乘,.model small.stack 256.data N dw 3 result dw ?.code.startupmov bx,Npush bx ;入口参数:Ncall fact ;调用递归子程序pop result ;出口参数:N!.exit 0,例4.12 主程序1/3,图示,;计算N!的近过程 ;入口参数:压入 N ;出口参数:弹出 N! fac
32、t procpush axpush bpmov bp,spmov ax,bp+6 ;取入口参数 Ncmp ax,0jne fact1 ;N0,N!N(N-1)!inc ax ;N0,N!1jmp fact2,例4.12 递归子程序2/3,图示,fact1: dec ax ;N-1push axcall fact ;调用递归子程序求(N-1)!pop axmul word ptr bp+6 ;求 N(N-1)! fact2: mov bp+6,ax ;存入出口参数 N!pop bppop axret fact endp,例4.12 递归子程序3/3,图示,递归子程序,调用时进栈,返回时出栈,1,
33、3!,2!,1!,子程序的重入,子程序的重入是指子程序被中断后又被中断服务程序所调用,能够重入的子程序称为可重入子程序。在子程序中,注意利用寄存器和堆栈传递参数和存放临时数据,而不要使用固定的存储单元(变量),就能够实现重入。 子程序的重入不同于子程序的递归。重入是被动地进入,而递归是主动地进入;重入的调用间往往没有关系,而递归的调用间却是密切相关的。递归子程序也是可重入子程序。,例题4.13:从键盘输入有符号十进制数,子程序从键盘输入一个有符号十进制数;子程序还包含将ASCII码转换为二进制数的过程 输入时,负数用“”引导,正数直接输入或用“”引导 子程序用寄存器传递出口参数,主程序调用该子
34、程序输入10个数据,转换算法,.data count = 10 array dw count dup(0) ;预留数据存储空间.code.startupmov cx,countmov bx,offset array again: call read ;调用子程序输入一个数据mov bx,ax ;将出口参数存放缓冲区inc bxinc bxcall dpcrlf ;调用子程序,光标回车换行以便输入下一个数据loop again.exit 0,例题4.131/5,;输入有符号10进制数的通用子程序 ;出口参数:AX补码表示的二进制数值 ;说明:负数用“”引导,正数用“”引导或直接输入;数据范围是3
35、276732768 read procpush bxpush cxpush dxxor bx,bx ;BX保存结果xor cx,cx;CX为正负标志,0为正,1为负mov ah,1 ;输入一个字符int 21h,例题4.132/5,cmp al,+ ;是“”,继续输入字符jz read1cmp al,- ;是“”,设置1标志jnz read2 ;非“”和“”,转read2mov cx,-1 read1: mov ah,1 ;继续输入字符int 21h read2: cmp al,0;不是09之间的字符,则输入数据结束jb read3cmp al,9ja read3,例题4.133/5,转换算法
36、,sub al,30h ;是09之间的字符,则转换为二进制数 ;利用移位指令,实现数值乘10:BXBX10shl bx,1mov dx,bxshl bx,1shl bx,1add bx,dx;mov ah,0add bx,ax ;已输入数值乘10后,与新输入数值相加jmp read1 ;继续输入字符,例题4.134/5,转换算法,read3: cmp cx,0jz read4neg bx ;是负数,进行求补 read4: mov ax,bx ;设置出口参数pop dxpop cxpop bxret ;子程序返回 read endp;使光标回车换行的子程序 dpcrlf proc. ;省略 dp
37、crlf endpend,例题4.135/5,ASCII码转换为二进制数, 首先判断输入为正或负数,并用一个寄存器记录 接着输入09数字(ASCII码),并减30H转换为二进制数 然后将前面输入的数值乘10,并与刚输入的数字相加得到新的数值 重复、步,直到输入一个非数字字符结束 负数进行求补,转换成补码;否则直接保存数值 本例采用16位寄存器表达数据,所以只能输入32767732768间的数值 但该算法适合更大范围的数据,例题4.14:显示有符号十进制数,子程序在屏幕上显示一个有符号十进制数;子程序还包含将二进制数转换为ASCII码的过程 显示时,负数用“”引导,正数直接输出、没有前导字符 子
38、程序的入口参数用共享变量传递,主程序调用该子程序显示10个数据,转换算法,.data count = 10 array dw 1234,-1234,0,1,-1,32767dw -32768,5678,-5678,9000 wtemp dw ? ;共享变量.code.startupmov cx,countmov bx,offset array again: mov ax,bxmov wtemp,ax ;将入口参数存入共享变量call write ;调用子程序显示一个数据inc bxinc bxcall dpcrlf ;便于显示下一个数据loop again.exit 0,例题4.141/5,;
39、显示有符号10进制数的通用子程序 ;入口参数:共享变量wtemp write procpush axpush bxpush dxmov ax,wtemp ;取出显示数据test ax,ax ;判断零、正数或负数jnz write1mov dl,0 ;是零,显示“0”后退出mov ah,2int 21hjmp write5,例题4.142/5,write1: jns write2 ;是负数,显示“”mov bx,ax ;AX数据暂存于BXmov dl,-mov ah,2int 21hmov ax,bxneg ax ;数据求补(求绝对值) write2: mov bx,10push bx;10压入
40、堆栈,作为退出标志,例题4.143/5,转换算法,write3: cmp ax,0 ;数据(余数)为零jz write4 ;转向显示sub dx,dx ;扩展被除数DX.AXdiv bx ;数据除以10:DX.AX10add dl,30h;余数(09)转换为ASCII码push dx;数据各位先低位后高位压入堆栈jmp write3 write4: pop dx;数据各位先高位后低位弹出堆栈cmp dl,10 ;是结束标志10,则退出je write5,例题4.144/5,转换算法,mov ah,2 ;进行显示int 21hjmp write4 write5: pop dxpop bxpop
41、axret ;子程序返回 write endp;使光标回车换行的子程序 dpcrlf proc. ;省略 dpcrlf endpend,例题4.145/5,二进制数转换为ASCII码, 首先判断数据是零、正数或负数,是零显示“0”退出 是负数,显示“”,求数据的绝对值; 接着数据除以10,余数加30H转换为ASCII码压入堆栈 重复步,直到余数为0结束 依次从堆栈弹出各位数字,进行显示 本例采用16位寄存器表达数据,所以只能显示32767732768间的数值 但该算法适合更大范围的数据,例题4.15:计算有符号数平均值,子程序将16位有符号二进制数求和,然后除以数据个数得到平均值 子程序的入口
42、参数利用堆栈传递,主程序需要压入数据个数和数据缓冲区的偏移地址。子程序通过BP寄存器从堆栈段相应位置取出参数 子程序的出口参数用寄存器AX传递 主程序提供10个数据,并保存平均值,避免溢出,.data count = 10 array dw 1234,-1234,0,1,-1,32767dw -32768,5678,-5678,9000 wmed dw ? ; 存放平均值.code.startupmov ax,countpush ax ;压入数据个数mov ax,offset arraypush ax ;压入缓冲区偏移地址call mean ;调用子程序求平均值add sp+4 ;平衡堆栈mo
43、v wmed,ax ;保存平均值(不含余数).exit 0,例题4.151/4,;计算16位有符号数平均值子程序 ;入口参数:顺序压入数据个数和缓冲区偏移地址 ;出口参数:AX平均值 mean proc push bpmov bp,sppush bx ;保护寄存器push cxpush dxpush sipush di,例题4.152/4,mov bx,bp+4 ;从堆栈取出偏移地址mov cx,bp+6 ;从堆栈取数据个数xor si,si ;SI保存求和的低16位值mov di,si ;DI保存求和的高16位值 mean1: mov ax,bx ;取出一个数据AXcwd ;符号扩展DXad
44、d si,ax ;求和低16位adc di,dx ;求和高16位inc bx ;指向下一个数据inc bxloop mean1 ;循环,例题4.153/4,堆栈区,避免溢出,mov ax,simov dx,di ;累加和在DX.AXmov cx,bp+6 ;数据个数在CXidiv cx ;有符号数除法,求的平均值在AX中、余数在DX中pop di ;恢复寄存器pop sipop dxpop cxpop bxpop bpret mean endpend,例题4.154/4,例题4.15的堆栈区,BPSP,地址低端,有符号数如何避免溢出,为了避免有符号二进制数求和过程中溢出,被加数要进行符号扩展,得到倍长数据(大小没有变化),然后求和 因为采用16位二进制数表示数据个数,最大是216,这样扩展到32位二进制数表达累加和,不再会出现溢出 考虑极端情况:数据全是215,共有216个,求和结果是231,32位数据仍然可以表达,第4章 教学要求,1. 掌握基本程序结构顺序结构、分支结构、循环结构、子程序及其汇编语言程序设计 2. 熟悉常见程序设计问题: 多精度运算、查表(查代码、特定值等) ASCII、BCD及十六进制数据间的代码转换 数据范围判断(09、AZ、az) 字母大小写转换;字符串传送、比较等操作 求最小最大值、数据求和、统计字符个数 子程序的寄存器和共享变量传递参数,放松一下,