常见汇编代码
# 常见汇编代码 #
1. 编写程序:比较AX,BX,CX中带符号数的大小,将最大的数放在AX中
code segment
assume cs:code
mov ax,32
mov bx,74
mov cx,23
sort: cmp ax,bx
jge X ;如果ax大于等于bx就跟cx比较
xchg ax,bx
X:cmp ax,cx ;如果ax大于cx
jge Y
xchg ax,cx
Y:int 21h
mov ax,4c00h
code ends
end sort
2.要求对键盘输入的小写字母用大写字母显示出来
code segment
assume cs:code
start: mov ah,1
int 21h
cmp al,'a'
jz error
cmp al,'z'
ja error
sub al,20h ;'A'~'Z':41~5AH ‘a'~'z':61~7ah
mov dl,al
mov ah,2
int 21h
jmp start
error:ret
code ends
end start
3.编写程序:从键盘上接收一个四位数的十进制数,并在终端上显示出与它等值的二进制数。
code segment
assume cs:code
begin:xor bx,bx ;清空bx
mov ch,4
mov cl,4 ;设置内外层的循环次数
input:shl bx,cl ;bx中的内容逻辑左移
mov ah,1
int 21h ;输入
cmp al,30h ;是否小于0
jb input ;是就重新输入
cmp al,39h ;是否大于9
ja input
and al,0fh ;转换为相应的二进制
jmp combine
combine:or bl,al
dec ch
jnz input
display:mov cx,16 ;输出的循环次数
print:mov dl,0
rol bx,1
rcl dl,1
or dl,30h ;清空dl
mov ah,2
int 21h
loop print
ret
code ends
end begin
4.将内存ffff:0~ffff:d单元中的数据复制到0:200~0:20d单元中。
code segment
assume cs:code
mov bx,0
mov cx,0fh
copy:mov ax,0ffffh
mov ds,ax
mov dl,[bx]
mov ax,0020h
mov ds,ax
mov [bx],dl
inc bx
loop copy
mov ax,4c00h
int 21h
code ends
end copy
5.将AX寄存器中的16位数分成四组,每组四位,然后把这四组数分别放在AL、BL、CL和DL中。
data segment
s db 4 dup(?) ;占位,并清空
mov ax,0ffffh
data ends
;-----------------------------------
progrnam segment
assume cs:progrnam,ds:data
begin:mov cl,4 ;ax逻辑右移四次
mov ch,4
lea bx,s
X: mov dx,ax
and dx,0fh ;逻辑与屏蔽高位
mov [bx],dl ;以字节形式将dl移入bx
inc bx
shr ax,cl ;逻辑右移
dec ch
jnz X
Y: mov dl,s ;依次移入相应寄存器内
mov cl,s+1
mov bl,s+2
mov al,s+3
ret
progrnam ends
end begin
6.从键盘输入一系列字符,以字符‘$’为结束符,然后对其中的非数字字符计数,并显示出计数结果。
`stack segment
dw 100h dup(?)
top label word
stack ends
data segment
h1 db 'Please input a string: ','$'
h2 db 'The number of the chars that is not digit:','$'
crlf db 0dh,0ah,24h
data ends
code segment
assume cs:code,ds:data,ss:stack
main proc far
begin:mov ax,stack
mov ss,ax
lea sp,top
mov ax,data
mov ds,ax
lea dx,h1
mov ah,9
int 21h
mov cx,0
input: mov ah,1
int 21h
cmp al,'$'
jz exit
cmp al,30h
jb count
cmp al,39h
jnb count
jmp input
count: inc cx
jmp input
exit: lea dx,crlf
mov ah,9
int 21h
lea dx,h2
int 21h
mov si,0
mov bl,10
mov ax,cx
mem: div bl
mov dl,ah
mov dh,0
push dx
inc si
mov ah,0
cmp al,0
jnz mem
prin: pop dx
add dl,30h
mov ah,2
int 21h
dec si
cmp si,0
jnz prin
mov ah,4ch
int 21H
main endp
code ends
end begin
`
7.已知两个整数变量A和B,试编写完成下述操作的程序:
(1)若两个数中有一个数是奇数,则将奇数存入A中,偶数存入B中
(2)若两个数均为奇数,则两数分别加1,并存回原变量。
(3)若两个数均为偶数,则两变量不变。
code segment
assume cs:code
begin: mov ax,13
mov bx,12
mov cx,ax
mov dx,bx
xor ax,bx
test ax,0001h ;A和B是否同时为奇数或偶数
jz next ;是
test bx,0001h
jz return ;B为偶数,A为奇数,加1
exchange:mov ax,dx ;A为偶数,B为奇数,交换
mov bx,cx
jmp return
next:test bx,0001h ;是否同为奇数
jz return ;同为偶数,不变
inc dx
inc cx
jmp exchange
return:ret
code ends
end begin
8.比较两个字符串string1和string2所含的字符是否相同。若相同则显示‘true’,否则显示‘false’。
data segment
string1 db 'i am a student'
string2 db 'i am a student'
string3 db 'true',0dh,0ah,'$'
string4 db 'false',0dh,0ah,'$'
data ends
;--------------------------
progrnam segment
assume cs:progrnam,ds:data
start:push ds
sub ax,ax
push ax
mov ax,data
mov ds,ax
mov es,ax
begin:lea si,string1
lea di,string2
cld
mov cx,string2-string1
repe cmpsb
jne Y
lea dx,string3
jmp print
Y:lea dx,string4
print: mov ah,9
int 21h
mov ax,4c00h
int 21h
progrnam ends
end start
9.编写无溢出除法的汇编子程序 。
这里所说的除法溢出并不是指分母为0而发生溢出的情况。我们以除数为8位的情况来说明,假设我们的被除数为65535(16位的最大值)存储在AX 中,除数为1,存储在CL中,然后执行除法指令: div CL。根据上面的说法,结果都是放在AX中的,余数为0,当然这没有问题,然而商为65535要放在AL中却是放不下的,因为AL能存放的最大值只为 255,所以此时就会发生溢出。我们可以看到65535/1 = 255,这显然与我位正常的除法结果不符。
如何解决这个溢出问题
既然我们知道了问题的根源,要解 决它就不困难了。为了统一而且不发生溢出,我们可以把所有的除法的商都用32位来保存,即所有的被除数都用32位,所有的除数都用16位,所有的商都用 32位,所有的余数都用16位来保存,就可以解决这个问题,因为一个数除以一个整数后,不可能大于其之前的值。而对于8位的除法,除数和被除数的高位全用 0补全即可。为了达到这个目的,我们就不能使用默认的除法指令div了,而需要我们写代码来实现我们自定义的除法。
自定义除法: X为除数,N为被除数,H代表X的高16位,L代表X的低16位,
int(H/N)和rem(H/N)代表着H除以N的商和余数
X/N = int(H/N)* 2^16 + [rem(H/N)* 2^16+L]/N
progrnam segment
assume cs:progrnam
mov ax,0ffffh
mov cx,1 ;初始化进行测试
begin: cmp cx,0
je return ;除数不能为0
push bx
push ax
mov ax,dx
mov dx,0
div cx ;执行H/N,商保存在ax中,余数在DX中
mov bx,ax ;把商保存
pop ax ;取出低位,执行rem(H/N)*2^16+L
div cx ;执行[rem(H/N)*2^16+L]/N ,商保存在ax中 ,余数在dx中
mov cx,dx ;cx保存余数
mov dx,bx ;dx中保存高位除法的商
pop bx
return:ret
mov ax,4c00h
int 21h
progrnam ends
end begin
10.编写一个程序,接受从键盘输入的10个十进制数字,输入回车符则停止输入,然后将这些数字加密后(用XLAT指令变换)存入内存缓冲区BUFFER。加密表为:
输入数字:0,1,2,3,4,5,6,7,8,9
密码数字:7,5,9,1,3,6,8,0,2,4
data segment
number db 7,5,9,1,3,6,8,0,2,4
buffer db 10 dup(?)
data ends
;-------------------------------------
code segment
assume cs:code,ds:data
mov si,0
mov cx,10
lea bx,number
input:mov ah,1
int 21h
cmp al,0dh ;是否为回车符
jz return
cmp al,30h ;比较是否是0~9,不是就重新输入
jb input
cmp al,39h
ja input
xlat ;变换进行存储
mov buffer[si],al
inc si
loop input
return:ret
code ends
end input
data segment
number db 7,5,9,1,3,6,8,0,2,4
buffer db 10 dup(?)
data ends
;-------------------------------------
code segment
assume cs:code,ds:data
mov si,0
mov cx,10
lea bx,number
input:mov ah,1
int 21h
cmp al,0dh ;是否为回车符
jz return
cmp al,30h ;比较是否是0~9,不是就重新输入
jb input
cmp al,39h
ja input
xlat ;变换进行存储
mov buffer[si],al
inc si
loop input
return:ret
code ends
end input
11.编写一个子程序嵌套结构的程序模块,分别从键盘输入姓名及8个字符的电话号码,并以一定的格式显示出来。
主程序TELIST:
A. 显示提示符“INPUT NAME:”;
B. 调用子程序INPUT_NAME输入姓名;
C. 显示提示符“INPUT A TELEPHONE NUMBER:”;
D. 调用子程序INPHONE输入电话号码;
E. 调用子程序PRINTLINE显示姓名及电话号码。
子程序INPUT_NAME:
A. 调用键盘输入子程序GETCHAR,把输入的姓名存放在INBUF缓冲区中;
B. 把INBUF中的姓名移入输出行OUTNAME。
子程序INPHONE:
A. 调用键盘输入子程序GETCHAR,把输入的8位电话号码存放在INBUF缓冲区中;
B. 把INBUF中的号码移入输出行OUTPHONE。
子程序PRINTLINE:
显示姓名及电话号码,格式为:
NAME TEL.
X X X XXXXXXXX
代码如下:
data segment
tipname db 'input name:','$'
tipnumber db 'input a telephone number:','$'
inbuf db 12 dup(' ')
crlf db 0dh,0ah,'$'
outname db 16 dup(' ')
outphone db 12 dup(' '),0dh,0ah,'$'
info db 'name',12 dup(' '),'tel',0dh,0ah,'$'
data ends
;------------------------------------
stack segment
dw 100 dup(?) ;伪定义使得同一个变量具有不同的属性
string label word
stack ends
;----------------------------------------
progrnam segment
main proc far
assume cs:progrnam,ds:data,es:data,ss:stack
start:mov ax,stack
mov ss,ax
mov sp,offset string
push ds
xor ax,ax
push ax
mov ax,data
mov ds,ax
mov es,ax
begin:
mov ah,09 ;输出提示字符串
mov dx,seg tipname
mov ds,dx
mov dx,offset tipname
int 21h
call input_name ;输入姓名
mov ah,09 ;输出提示字符串
mov dx,seg tipnumber
mov ds,dx
mov dx,offset tipnumber
int 21h
call input_number
call printline
ret
main endp
;------------------------------------------
input_name proc near
call getchar
lea si,inbuf
lea di,outname
mov cx,12 ;设置计数器
cld ;设置方向为自增
;movsb 与rep结合是传送字节,相当于 movs es:byte ptr[di],ds:[si]
rep movsb
ret
input_name endp
;-----------------------------------------------
input_number proc near
call getchar
lea si,inbuf
lea di,outphone
mov cx,12
cld
rep movsb
ret
input_number endp
;------------------------------------------------
getchar proc near
mov al,20h
mov cx,12
lea di,inbuf
cld
;stosb这里是为了每一次输入一个字节存入相应的缓冲区
rep stosb
mov cx,12
mov di,0
input:mov ah,1
int 21h
cmp al,0dh
jz return
mov inbuf[di],al
inc di
loop input
return: call disp_crlf ;输出回车符
ret
getchar endp
;--------------------------------------
printline proc near ;这里均是将所得的字符串进行输出
mov ah,09
mov dx,seg info
mov ds,dx
mov dx,offset info
int 21h
mov ah,09
mov dx,seg outname
mov ds,dx
mov dx,offset outname
int 21h
ret
printline endp
;---------------------------------------
disp_crlf proc near
mov ah,09
mov dx,seg crlf
mov ds,dx
mov dx,offset crlf
int 21h
ret
disp_crlf endp
;-----------------------------------------
progrnam ends
end start
12.试编写一个汇编语言子程序,要求将包含任意字符、以0结尾的字符串中的小写字母转换成大写字母。
data segment
string db 'i am A stuDEnt',0h
data ends
;------------------------------
progrnam segment
assume ds:data,cs:progrnam
start:push ds
xor ax,ax
mov ax,data
mov ds,ax
lea si,string
push si
begin: mov al,[si] ;判断是否为小写字母并进行转换
cmp al,0
je return
cmp al,'a'
jb print
cmp al,'z'
ja print ;如果不是小写字母直接进行输出
sub al,20h
mov [si],al
print:push ds ;将字母进行输出
;mov al,[si]
mov dl,al
mov ah,2
int 21h
pop ds
n:inc si
jmp begin
return:
pop si
pop ds
mov ax,4c00h
int 21h
progrnam ends
end start
13.编写程序,将一个包含有20个数据的数组M分成两个数组:正数数组P和负数数组N,并分别把这两个数组中数据的个数显示出来。
data segment
string1 db 0dh,0ah, 'positive number:','$'
string2 db 0dh,0ah,'negative number:','$'
array dw 1,2,3,4,5,6,7,8,-4,-8,-9,-6,-2,-8,8,12,18,-12,5,64
array1 dw 20 dup(?) ;存储正数数组
array2 dw 20 dup(?) ;存储负数数组
count1 db 0
count2 db 0 ;计数
CRLF DB 0dh,0ah,'$'
data ends
;----------------------------------
progrnam segment
main proc far
assume ds:data,cs:progrnam
s: push ds
xor ax,ax
push ax
mov ax,data
mov ds,ax
begin:mov cx,20 ;循环次数
lea bx,array ;bx相当于数组的首地址
lea si,array1
lea di,array2
start:mov ax,[bx]
cmp ax,0 ;是否为负数
js Y ;是负数
mov [si],ax ;存入正数数组
inc count1
add si,2
jmp short next
Y: mov [di],ax
inc count2
add di,2
next:add bx,2
loop start
lea dx,string1
mov al,count1
call print
lea dx,string2
mov al,count2
call print
ret
main endp
;--------------------------------------
print proc near
push ax ;al中存储计数值,要进行保存
mov ah,9
int 21h
pop ax ;出栈,进行ASCII码调整
aam ;调整方式AH<-AL/10(商),AL<-AL%10
push ax
add ah,30h ;变为0~9的ASCII码
mov dl,ah
mov ah,2
int 21h
pop ax
add al,30h
mov dl,al
mov ah,2
int 21h
lea dx,CRLF
mov ah,9
int 21h
ret
print endp
progrnam ends
end s
14.设有10个学生的成绩分别是76,69,84,90,73,88,99,63,100和80分。试编制一个子程序统计60~69分,70~79分,80~89分,90~99分和100分的人数,分别存放到S6,S7,S8,S9和S10单元中。
data segment
grade dw 76,69,84,90,73,88,99,63,100,80
count1 dw 0
count2 dw 0
count3 dw 0
count4 dw 0
count5 dw 0
data ends
;-----------------------------------------
progrnam segment
assume ds:data,cs:progrnam
push ds
xor ax,ax
push ax
mov ax,data
mov ds,ax
begin:mov cx,10
mov si,0
next:mov ax,grade[si]
mov bx,10
div bl
mov bl,al
sub bx,6
sal bx,1 ;将(grade/10-6)×2就是
inc count1[bx] ;count1是首地址
add si,2
loop next
progrnam ends
end begin
15.输出空行
progrnam segment
assume cs:progrnam
begin:push cx
push dx
mov ax,3 ;这里对ax进行赋值,观察输出的结果
mov cx,ax
print:mov dl,0dh
mov ah,2 ;这是对与输出一个字符的bios的调用
int 21h ;print的功能就是输出空行,0dh为回车
mov dl,0ah ;0ah为换行
mov ah,2
int 21h
loop print
pop dx
pop cx
;这一段程序是为了测试输出后的结果,主要母的是输出一个黄色的'*'
mov ah,9
mov al,'*'
mov bh,0
mov bl,0eh
mov cx,1
int 10h
ret
progrnam ends
end begin