题目:键盘输入一个包含若干个星号的字符串,试编写子程序分别将字符串前端、中间、最后面的星号及其所有星号去除。
详解:
本文是目前本专栏最难的一篇,需要的知识功底很多,如果在一些语法学习不够扎实,请移步往期文章:
汇编语言(Assembly Language)习题:键盘输入一个字符串,试将其中的小写字母转换为大写字母,其它字符保持不变。
汇编语言(Assembly Language)习题:键盘输入任意一个字母,显示其前导字母、字母自身、后续字符共三个字母。
汇编语言习题:编写程序,将一个包含有20个有符号数据的数组ARRAYM分成两个数组:正数数组ARRAYP和负数数组ARRAYN。设数据均为8位。试分别统计负数个数,并计算正数之和。
汇编语言习题:键盘输入2个两位十进制数(可以是负数),并输入加、减、乘、除四个运算符中的某一个,完成对应运算,并输出相应的结果。若输入的第二个数为0,就退出计算。
在此着重讲解一下子程序,在前几期文章有用到过,相信各位已经基本了解了用法,在此系统介绍:
-
在程序设计中,我们会发现一些多次无规律重复的程序段或语句序列。解决此类问题一个行之有效的方法就是将它们设计成可供反复调用的独立的子程序结构,以便在需要时调用。在汇编语言中,子程序又称过程。
-
子程序可以实现源程序的模块化,可以简化源程序结构,可以提高编程效率。
-
调用子程序的程序称为主调程序或主程序。
-
子程序的定义是由过程定义伪指令PROC和ENDP来完成的。其格式如下:
过程名 PROC [NEAR/FAR]
┆
过程名 ENDP
- 其中PROC表示过程定义开始,ENDP表示过程定义结束。过程名是过程入口地址的符号表示。
- 一般过程名同标号一样,具有三种属性,即段属性、偏移地址属性以及类型属性。
- 取值有如下两种:
(1)NEAR属性:调用程序和子程序在同一代码段中(段内调用)
(2)FAR属性:调用程序和子程序不在同一代码段中(段间调用) - 对简化段定义格式,在微型、小型和紧凑存储模式下,过程的缺省属性为near;在中型、大型和巨型存储模式下,过程的缺省属性为far。对完整段定义格式,过程的缺省属性为near。
- 用户可以在过程定义时用near或far改变缺省属性。
调用程序和子程序在同一代码段中
两种形式
code segment
main proc far
......
call subr1
......
ret
main endp
subr1 proc near
......
ret
subr1 endp
code ends
code segment
main proc far
......
call subr1
......
ret
subr1 proc near
......
ret
subr1 endp
main endp
code ends
调用程序和子程序不在同一代码段中
segx segment
subt proc far
......
ret
subt endp
......
call subt
......
segx ends
segy segment
......
call subt
......
segy ends
DATAS SEGMENT
DATAS SEGMENT
msg1 db 'input a string:',0ah,0dh,'$' ;输出的字符串,不难理解
msg2 db 'result:',0ah,0dh,'$'
msg3 db 0ah,0dh,'$'
msg4 db '********************menu********************$'
msg5 db '1.******************************************$'
msg6 db '2.******************************************$'
msg7 db '3.******************************************$'
msg8 db '4.******************************************$'
msg10 db '5.******************************************$'
msg9 db 'input a number:',0ah,0dh,'$'
msg11 db 'END',0ah,0dh,'$'
buff db 50 ,?,50 dup(?)
count1 dw 0 ;统计前面*数量
count2 dw 0 ;统计后面*数量
DATAS ENDS
CODES SEGMENT(1)
mov dx,offset msg1 ;提示输入
mov ah,9
int 21h
lea dx,buff ;输入字符串
mov ah,10
int 21h
lea si,buff+1 ;取第二个字节地址送给si
;设置cx=字符串长度,实际输入的字符个数
mov ch,0
mov cl,[si]
这段代码不难理解,已在往期文章讲解,在此不再赘述:
汇编语言(Assembly Language)习题:键盘输入一个字符串,试将其中的小写字母转换为大写字母,其它字符保持不变。
CODES SEGMENT(2)
inc si ;从第一个字符开始计数
front: mov al,[si]
cmp al,'*' ;计数前面的*数量
jz change1 ;思考一下为什么这两句不能换位置? jz change1 jmp next1
jmp next1
change1:add count1,1
inc si
loop front ;这段循环指令并没有用到cx=0结束循环,思考怎么退出循环的?
next1: ;当然是[si]!='*'的时候通过 jmp next1 指令过来了
lea si,buff+1 ;设置cx=字符串长度,实际输入的字符个数
mov ch,0
mov cl,[si]
add si,cx ;同理,这里从字符串最后一个字符开始统计,所以要执行 add si,cx ,此时si指向字符串最后一个字符
back: mov al,[si]
cmp al,'*' ;计数后面的*数量
jz change2
jmp next2
change2:inc count2
dec si ;相应地,si应该自减不是自增
loop back
next2:
mov bx,10
CODES SEGMENT(3)
next2:
mov bx,10 ;bx的作用见末尾
selections:
call crlf
mov dx,offset msg9
mov ah,9
int 21h
mov ah,1
int 21h
cmp al,31h ;比较输出的数字,即数字对应的ASCII码
jz number_1
cmp al,32h
jz number_2
cmp al,33h
jz number_3
cmp al,34h
jz number_4
cmp al,35h
jz number_5
number_1: call number1
jmp next_
number_2: call number2
jmp next_
number_3: call number3
jmp next_
number_4: call number4
jmp next_
number_5: call number5
jmp over
;
next_:
dec bx ;显然循环执行10次,通过bx=0的条件退出循环,至于问为什么用bx而不是别的寄存器,当然是为了防止数据更改,寄存器不够用了。
jnz selections
jmp over
CODES SEGMENT(4)
输入数字1,2,3,4,5,分别完成不同的功能,见文末。以数字1为例:
number1 proc near
call crlf
lea si,buff+2
mov cl,buff+1
mov ch,0
sub cx,count1 ;计数减去前面'*'的数量
add si,count1 ;从前往后第一个非'*'字符开始
continue_1:mov dl,[si]
mov ah,2
int 21h ;将DL寄存器中的字符送显示器显示
inc si
dec cl
jnz continue_1
ret
number1 endp
完整代码
DATAS SEGMENT
msg1 db 'input a string:',0ah,0dh,'$'
msg2 db 'result:',0ah,0dh,'$'
msg3 db 0ah,0dh,'$'
msg4 db '********************menu********************$'
msg5 db '1.******************************************$'
msg6 db '2.******************************************$'
msg7 db '3.******************************************$'
msg8 db '4.******************************************$'
msg10 db '5.******************************************$'
msg9 db 'input a number:',0ah,0dh,'$'
msg11 db 'END',0ah,0dh,'$'
buff db 50 ,?,50 dup(?)
count1 dw 0 ;统计前面*数量
count2 dw 0 ;统计后面*数量
DATAS ENDS
STACKS SEGMENT
dw 128 dup(0)
STACKS ENDS
CODES SEGMENT
ASSUME CS:CODES,DS:DATAS,SS:STACKS
START:
MOV AX,DATAS
MOV DS,AX
call menu ;打印目录
mov dx,offset msg1 ;提示输入
mov ah,9
int 21h
lea dx,buff ;输入字符串
mov ah,10
int 21h
lea si,buff+1 ;取第二个字节地址送给si
;设置cx=字符串长度,实际输入的字符个数
mov ch,0
mov cl,[si]
inc si ;从第一个字符开始计数
front: mov al,[si]
cmp al,'*' ;计数前面的*数量
jz change1
jmp next1
change1:add count1,1
inc si
loop front
next1:
lea si,buff+1 ;设置cx=字符串长度,实际输入的字符个数
mov ch,0
mov cl,[si]
add si,cx
back: mov al,[si]
cmp al,'*' ;计数前面的*数量
jz change2
jmp next2
change2:inc count2
dec si
loop back
next2:
mov bx,10
selections:
call crlf
mov dx,offset msg9
mov ah,9
int 21h
mov ah,1
int 21h
cmp al,31h ;比较输出的数字
jz number_1
cmp al,32h
jz number_2
cmp al,33h
jz number_3
cmp al,34h
jz number_4
cmp al,35h
jz number_5
number_1: call number1
jmp next_
number_2: call number2
jmp next_
number_3: call number3
jmp next_
number_4: call number4
jmp next_
number_5: call number5
jmp over ;直接退出,结束程序
;
next_:
dec bx
jnz selections
jmp over
crlf proc near
mov dx,offset msg3
mov ah,9
int 21h
ret
crlf endp
menu proc near
mov dx,offset msg4
mov ah,9
int 21h
call crlf
mov dx,offset msg5
mov ah,9
int 21h
call crlf
mov dx,offset msg6
mov ah,9
int 21h
call crlf
mov dx,offset msg7
mov ah,9
int 21h
call crlf
mov dx,offset msg8
mov ah,9
int 21h
call crlf
mov dx,offset msg10
mov ah,9
int 21h
call crlf
ret
menu endp
number1 proc near
call crlf
lea si,buff+2
mov cl,buff+1
mov ch,0
sub cx,count1
add si,count1
continue_1:mov dl,[si]
mov ah,2
int 21h
inc si
dec cl
jnz continue_1
ret
number1 endp
number3 proc near
call crlf
lea si,buff+2
mov cl,buff+1
mov ch,0
sub cx,count2
continue_3:mov dl,[si]
mov ah,2
int 21h
inc si
dec cl
jnz continue_3
ret
number3 endp
number4 proc near
call crlf
lea si,buff+2
mov cl,buff+1
mov ch,0
continue_4:mov dl,[si]
cmp dl,'*'
jz later1
mov ah,2
int 21h
later1: inc si
dec cl
jnz continue_4
ret
number4 endp
number2 proc near
call crlf
mov cx,count1
input1:mov dl,'*'
mov ah,2
int 21h
dec cx
jnz input1
lea si,buff+2
mov cl,buff+1
mov ch,0
sub cx,count1
sub cx,count2
add si,count1
continue_2:mov dl,[si]
cmp dl,'*'
jz later2
mov ah,2
int 21h
later2: inc si
dec cl
jnz continue_2
mov cx,count2
input2:mov dl,'*'
mov ah,2
int 21h
dec cx
jnz input2
ret
number2 endp
number5 proc near ;5
call crlf
mov dx,offset msg11 ;提示结束
mov ah,9
int 21h
ret
number5 endp
over:
MOV AH,4CH
INT 21H
CODES ENDS
END START