📄 汇编代码.txt
字号:
例4.11b-1/2
;主程序
call checksumb
;子程序
checksumb proc
push ax
push bx
push cx
xor al,al ;累加器清0
mov bx,offset array
;BX←数组的偏移地址
mov cx,count
;CX←数组的元素个数
例4.11b-2/2
sumb: add al,[bx] ;求和
inc bx
loop sumb
mov result,al ;保存校验和
pop cx
pop bx
pop ax
ret
checksumb endp
主程序将子程序的入口参数压入堆栈,子程序从堆栈中取出参数
子程序将出口参数压入堆栈,主程序弹出堆栈取得它们
例4.11c
入口参数:
顺序压入偏移地址和元素个数
出口参数:
AL=校验和
例4.11c 主程序
.startup
mov ax,offset array
push ax
mov cx,count
push cx
call checksumc
add sp,4
mov result,al
.exit 0
例4.11c 子程序
checksumc proc
push bp
mov bp,sp ;利用BP间接寻址存取参数
push bx
push cx
mov bx,[bp+6] ;SS:[BP+6]指向偏移地址
mov cx,[bp+4] ;SS:[BP+4]指向元素个数
xor al,al
sumc: add al,[bx]
inc bx
loop sumc
pop cx
pop bx
pop bp
ret
checksumc endp
子程序的嵌套
子程序内包含有子程序
的调用就是子程序嵌套
没有什么特殊要求
例4.12嵌套子程序-1/2
ALdisp proc
push ax
push cx ;实现al内容的显示
push dx
push ax ;暂存ax
mov dl,al ;转换al的高4位
mov cl,4
shr dl,cl
call dldisp ;调用子程序显示al高4位
pop ax
and dl,0fh
call dldisp ;调用子程序显示al低4位
pop dx
pop cx
pop ax
ret
ALdisp endp
例4.12嵌套子程序-2/2
;显示dl低4位中一位十六进制数
dldisp proc
or dl,30h
cmp dl,39h
jbe dldisp1
add dl,7
dldisp1: mov ah,2
int 21h
ret
dldisp endp
子程序的递归
当子程序直接或间接地嵌套调用自身时称为递归调用,含有递归调用的子程序称为递归子程序
递归子程序必须采用寄存器或堆栈传递参数,递归深度受堆栈空间的限制
例4.13:求阶乘
例4.13 主程序-1/3
.model small
.stack 256
.data
N dw 3
result dw ?
.code
.startup
mov bx,N
push bx ;入口参数:N
call fact ;调用递归子程序
pop result ;出口参数:N!
.exit 0
例4.13 递归子程序-2/3
;计算N!的近过程
;入口参数:压入 N ;出口参数:弹出 N!
fact proc
push ax
push bp
mov bp,sp
mov ax,[bp+6] ;取入口参数 N
cmp ax,0
jne fact1 ;N>0,N!=N×(N-1)!
inc ax ;N=0,N!=1
jmp fact2
例4.13 递归子程序-3/3
fact1: dec ax ;N-1
push ax
call fact ;调用递归子程序求(N-1)!
pop ax
mul word ptr [bp+6] ;求 N×(N-1)!
fact2: mov [bp+6],ax ;存入出口参数 N!
pop bp
pop ax
ret
fact endp
递归子程序
子程序的重入
子程序的重入是指子程序被中断后又被中断服务程序所调用,能够重入的子程序称为可重入子程序。在子程序中,注意利用寄存器和堆栈传递参数和存放临时数据,而不要使用固定的存储单元(变量),就能够实现重入。
子程序的重入不同于子程序的递归。重入是被动地进入,而递归是主动地进入;重入的调用之间往往没有关系,而递归的调用之间却是密切相关的。递归子程序也是可重入子程序。
子程序补充例题1
子程序从键盘输入一个有符号十进制数;子程序还包含将ASCII码转换为二进制数的过程
输入时,负数用“-”引导,正数直接输入或用“+”引导
子程序用寄存器传递出口参数,主程序调用该子程序输入10个数据
补充例题1-1/5
.data
count equ 10
array dw count dup(0) ;预留数据存储空间
.code
.startup
mov cx,count
mov bx,offset array
again: call read ;调用子程序输入一个数据
mov [bx],ax ;将出口参数存放缓冲区
inc bx
inc bx
call dpcrlf
;调用子程序,光标回车换行以便输入下一个数据
loop again
.exit 0
补充例题1-2/5
;输入有符号10进制数的通用子程序
;出口参数:AX=补码表示的二进制数值
;说明:负数用“-”引导,正数用“+”引导或直接输入;数据范围是+32767~-32768
read proc
push bx
push cx
push dx
xor bx,bx ;BX保存结果
xor cx,cx
;CX为正负标志,0为正,-1为负
mov ah,1 ;输入一个字符
int 21h
补充例题1-3/5
cmp al,'+' ;是“+”,继续输入字符
jz read1
cmp al,'-' ;是“-”,设置-1标志
jnz read2 ;非“+”和“-”,转read2
mov cx,-1
read1: mov ah,1 ;继续输入字符
int 21h
read2: cmp al,'0 '
;不是0~9之间的字符,则输入数据结束
jb read3
cmp al,'9'
ja read3
补充例题1-3/5
cmp al,'+' ;是“+”,继续输入字符
jz read1
cmp al,'-' ;是“-”,设置-1标志
jnz read2 ;非“+”和“-”,转read2
mov cx,-1
read1: mov ah,1 ;继续输入字符
int 21h
read2: cmp al,'0 '
;不是0~9之间的字符,则输入数据结束
jb read3
cmp al,'9'
ja read3
补充例题1-4/5
sub al,30h
;是0~9之间的字符,则转换为二进制数
;利用移位指令,实现数值乘10:BX←BX×10
shl bx,1
mov dx,bx
shl bx,1
shl bx,1
add bx,dx
;BX乘2与BX乘8相加
mov ah,0
add bx,ax
;已输入数值乘10后,与新输入数值相加
jmp read1 ;继续输入字符
补充例题1-5/5
read3: cmp cx,0
jz read4
neg bx ;是负数,进行求补
read4: mov ax,bx ;设置出口参数
pop dx
pop cx
pop bx
ret ;子程序返回
read endp
;使光标回车换行的子程序
dpcrlf proc
... ;省略
dpcrlf endp
end
ASCII码转换为二进制数
① 首先判断输入为正或负数,并用一个寄存器记录
② 接着输入0~9数字(ASCII码),并减30H转换为二进制数
③ 然后将前面输入的数值乘10,并与刚输入的数字相加得到新的数值
④ 重复②、③步,直到输入一个非数字字符结束
⑤ 负数进行求补,转换成补码;否则直接保存数值
本例采用16位寄存器表达数据,
所以只能输入+327677~-32768间的数值
但该算法适合更大范围的数据
子程序补充例题2
子程序在屏幕上显示一个有符号10进制数;子程序还包含将二进制数转换为ASCII码的过程
显示时,负数用“-”引导,正数直接输出、没有前导字符
子程序的入口参数用共享变量传递,主程序调用该子程序显示10个数据
补充例题2-1/5
.data
count = 10
array dw 1234,-1234,0,1,-1,32767
dw -32768,5678,-5678,9000
wtemp dw ? ;共享变量
.code
.startup
mov cx,count
mov bx,offset array
again: mov ax,[bx]
mov wtemp,ax ;将入口参数存入共享变量
call write ;调用子程序显示一个数据
inc bx
inc bx
call dpcrlf ;便于显示下一个数据
loop again
.exit 0
补充例题2-2/5
;显示有符号10进制数的通用子程序
;入口参数:共享变量wtemp
write proc
push ax
push bx
push dx
mov ax,wtemp ;取出显示数据
test ax,ax ;判断零、正数或负数
jnz write1
mov dl,'0' ;是零,显示“0”后退出
mov ah,2
int 21h
jmp write5
补充例题2-3/5
write1: jns write2 ;是负数,显示“-”
mov bx,ax ;AX数据暂存于BX
mov dl,'-'
mov ah,2
int 21h
mov ax,bx
neg ax ;数据求补(求绝对值)
write2: mov bx,10
push bx
;10压入堆栈,作为退出标志
补充例题2-4/5
write3: cmp ax,0 ;数据(余数)为零
jz write4 ;转向显示
sub dx,dx ;扩展被除数DX.AX
div bx ;数据除以10:DX.AX÷10
add dl,30h
;余数(0~9)转换为ASCII码
push dx
;数据各位先低位后高位压入堆栈
jmp write3
write4: pop dx
;数据各位先高位后低位弹出堆栈
cmp dl,10 ;是结束标志10,则退出
je write5
补充例题2-5/5
mov ah,2 ;进行显示
int 21h
jmp write4
write5: pop dx
pop bx
pop ax
ret ;子程序返回
write endp
;使光标回车换行的子程序
dpcrlf proc
... ;省略
dpcrlf endp
end
二进制数转换为ASCII码
① 首先判断数据是零、正数或负数,是零显示“0”退出
② 是负数,显示“-”,求数据的绝对值;
③ 接着数据除以10,余数加30H转换为ASCII码压入堆栈
④ 重复③步,直到余数为0结束
⑤ 依次从堆栈弹出各位数字,进行显示
本例采用16位寄存器表达数据,所以只能显示+327677~-32768间的数值
但该算法适合更大范围的数据
子程序补充例题3
子程序将16位有符号二进制数求和,然后除以数据个数得到平均值
子程序的入口参数利用堆栈传递,主程序需要压入数据个数和数据缓冲区的偏移地址。子程序通过BP寄存器从堆栈段相应位置取出参数
子程序的出口参数用寄存器AX传递
主程序提供10个数据,并保存平均值
补充例题3-1/4
.data
count = 10
array dw 1234,-1234,0,1,-1,32767
dw -32768,5678,-5678,9000
wmed dw ? ; 存放平均值
.code
.startup
mov ax,count
push ax ;压入数据个数
mov ax,offset array
push ax ;压入缓冲区偏移地址
call mean ;调用子程序求平均值
add sp+4 ;平衡堆栈
mov wmed,ax ;保存平均值(不含余数)
.exit 0
补充例题3-2/4
;计算16位有符号数平均值子程序
;入口参数:顺序压入数据个数和缓冲区偏移地址
;出口参数:AX=平均值
mean proc
push bp
mov bp,sp
push bx ;保护寄存器
push cx
push dx
push si
push di
补充例题3-3/4
mov bx,[bp+4] ;从堆栈取出偏移地址
mov cx,[bp+6] ;从堆栈取数据个数
xor si,si ;SI保存求和的低16位值
mov di,si ;DI保存求和的高16位值
mean1: mov ax,[bx] ;取出一个数据→AX
cwd ;符号扩展→DX
add si,ax ;求和低16位
adc di,dx ;求和高16位
inc bx ;指向下一个数据
inc bx
loop mean1 ;循环
补充例题3-4/4
mov ax,si
mov dx,di ;累加和在DX.AX
mov cx,[bp+6] ;数据个数在CX
idiv cx
;有符号数除法,求的平均值在AX中、余数在DX中
pop di ;恢复寄存器
pop si
pop dx
pop cx
pop bx
pop bp
ret
mean endp
end
补充例题3的堆栈区
有符号数如何避免溢出
为了避免有符号二进制数求和过程中溢出,被加数要进行符号扩展,得到倍长数据(大小没有变化),然后求和
因为采用16位二进制数表示数据个数,最大是216,这样扩展到32位二进制数表达累加和,不再会出现溢出
考虑极端情况:数据全是-215,共有216个,求和结果是-231,32位数据仍然可以表达
第4章 教学要求(1)
第4章 教学要求(2)
熟悉常见程序设计问题:
多精度运算、查表(查代码、特定值等)
ASCII、BCD及十六进制数据间的代码转换
数据范围判断(0~9、A~Z、a~z)
字母大小写转换;字符串传送、比较等操作
求最小最大值、数据求和、统计字符个数
子程序的寄存器和共享变量传递参数
1、编写一程序段,将AL中的第7位和第0位,第6位和第1位,第5位和第2位,第4位和第3位互换。
解:类似于课本习题“7.2”
mov cx,8
mov ah,0
k1: shr al,1
rcl ah,1
dec cx
jnz k1
mov al,ah
2、在A1单元开始定义了一长度为N的字符串,找出其中所有的小写字母并存放到A2单元开始的存储区中。统计出小写字母的个数,存放到SL单元中。请编写一完整的源程序。数据段如下:
data segment
A1 db 'ab1243AdcBBcdEd94r3'
N equ $-A1
A2 db N dup(?)
Sl db ?
data ends
解:
data segment ;数据段
A1 db 'ab1243AdcBBcdEd94r3'
N equ $-A1
A2 db N dup(?)
Sl db ?
data ends
code segment ;代码段
assume cs:code, ds:data
main: mov ax,data
mov ds,ax
lea si,a1
lea di,a2
mov cx,N
xor bl,bl
lab1: mov al,[si]
cmp al,'a'
jb lab2
cmp al,'z'
ja lab2
mov [di],al
inc bl
inc di
lab2: inc si
loop lab1
mov sl,bl
mov ah,2 ;以下程序段可以不写,只是利用输出屏幕验证题目的正确性。
mov cx,N
lea bx,a1
lab3: mov dl,[bx]
int 21h
inc bx
loop lab3
call cr
mov ah,2
mov ch,0
mov cl,[sl]
lea bx,a2
lab4: mov dl,[bx]
int 21h
inc bx
loop lab4
call cr
mov ah,0
mov al,[sl]
call write
mov ah,4ch
int 21h
include cr.asm
include write.asm
code ends
end main
1、设在数据段中有X,Y两个变量(字节单元),试编写程序段计算:
y=x 当x>=0时
|x| 当x<0时
解:mov ax,x
cmp al,0
jge lab
neg al
lab:mov y,al
2、编写一个完整的源程序,将BUF字节单元存放的两位BCD码,转换成2个字节的ASCII码,并分别存放在ASC和ASC+1字节单元中。
例如:(BUF字节单元)=58H,那么(ASC字节单元)=35H,(ASC+1字节单元)=38H。
data segment
buf db 58h
asc db 2 dup(?)
data ends
code segment
assume cs:code, ds:data
main: mov ax,data
mov ds,ax
mov al,buf
and al,0f0h
shr al,cl
or al,30h
mov asc,al
mov al,buf
and al,0fh
or al,30h
mov asc+1,al
mov ah,4ch
int 21h
code ends
end main
1、设在DAT1,DAT2字单元中存放一双字长有符号数,编一程序段,完成求出该双字长数的绝对值后送ABS1和ABS2字存储单元。
start:mov ax,dat1 ;AX放低位字有符号数
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -