;************************************************************实验代码
datarea segment
mess1 db 'Please enter character string : ',13,10,'$'
mess2 db 'The number of letter is ','$'
mess3 db 'The number of digit is ','$'
mess4 db 'The number of other is ','$'
cstring label byte
max db 80
act db ?
cstr db 80D dup(?)
letter db 0
digit db 0
other db 0
;number db '10','$','11','$','12','$','13','$','14','$','15','$'
number db 41h,42h,43h,44h,45h,46h,'$'
datarea ends
;************************************************************
;************************************************************
prognam segment
main procfar
assume ds:datarea,cs:prognam
;--------------------------------------------------------------
start:
push ds
sub ax,ax
push ax
mov ax,datarea
mov ds,ax
lea dx,mess1 ;显示字符串
mov ah,09h
int 21h
sub ax,ax ;将计数的存储单元初始为0
mov letter,al
mov digit,al
mov other,al
lea dx,cstring ;键入一行字符
mov ah,0Ah
int 21h
;需要换行吗?
mov cl,[cstring+1] ;分类计数的次数
lea si,cstring+2 ;初始指针指向
call newline
;----------------------------------------------------------------
begin:
mov bl,[si]
;------------------------------------
cmp1: ;将字符分类
cmp bl,'0'
jnb cmp2
jb others
cmp2:
cmp bl,'9'
jna digits
ja cmp3
cmp3:
cmp bl,'A'
jnb cmp4
jb others
cmp4:
cmp bl,'Z'
jna letters
ja cmp5
cmp5:
cmp bl,'a'
jnb cmp6
jb others
cmp6:
cmp bl,'z'
jna letters
ja others
;-----------------------------------
letters: ;记录结果
mov al,letter
inc al
dec cl
mov letter,al
cmp cl,0
jz print
jnz again
digits:
mov al,digit
inc al
dec cl
mov digit,al
cmp cl,0
jz print
jnz again
others:
mov al,other
inc al
dec cl
mov other,al
cmp cl,0
jz print
jnz again
;------------------------------------
again:
inc si
jmp begin
;------------------------------------
print: ;打印结果
lea dx,mess2
mov ah,09h
int 21h
mov dl,letter
cmp dl,10D
jnb conout
or dl,30h
mov ah,02h
int 21h
call newline
jmp next1
conout:
sub dl,10D
mov ah,0
mov al,dl
lea si,number
;mul al,2
;add al,dl ;*3
add si,ax
mov dl,[si]
mov ah,02h
int 21h
call newline
;--------------------
next1:
lea dx,mess3
mov ah,09h
int 21h
mov dl,digit
cmp dl,10D
jnb conout2
or dl,30h
mov ah,02h
int 21h
call newline
jmp next2
conout2:
sub dl,10D
mov ah,0
mov al,dl
lea si,number
;mul al,2
;add al,dl
add si,ax
mov dl,[si]
mov ah,02h
int 21h
call newline
;----------------------
next2:
lea dx,mess4
mov ah,09h
int 21h
mov dl,other
cmp dl,10D
jnb conout3
or dl,30h
mov ah,02h
int 21h
call newline
jmp over
conout3:
sub dl,10D
mov ah,0
mov al,dl
lea si,number
;mul al,2
;add al,dl
add si,ax
mov dl,[si]
mov ah,02h
int 21h
call newline
over:
mov ah,4ch ;带返回码终止
int 21h
main endp
;----------********************------------------
newline procnear ;打印空格、换行
mov dl,13
mov ah,02h
int 21h
mov dl,10
mov ah,02h
int 21h
ret
newline endp
;----------********************------------------
;---------------------------------------------------------
prognam ends
;*********************************************************
end start
问题及收获
这次实验遇到了许多问题,首先出现最多的问题就是输出乱码和不正确的结果,由于编译的过程中没有报错,我也不是非常熟练debug,所以遇到这些问题很麻烦。另一个问题就是出现语法报错和警告。
首先先总结一下出现的语法错误。本实验除去数据段、代码段的初始化和附加段(实现空格和换行),主要分为三个部分,第一个是对键入字符的选择判断,第二个是记录键入字符分类的结果,第三是输出字符分类的结果。
第一个通过ASCII码比较实现,将属于0-9的ASCII值的字符记入digit中,将A-Z和a-z的ASCII值的字符记入letter中,将不属于之前两部分的字符记入other中。
第二个的内容是根据跳转入口,将对应的类的计数值增一,循环次数减一,再判断是否继续循环还是输出记录结果。
第三个是根据02功能调用,输出字符仅限dl寄存器。而将记录的次数转换为ASCII值有0-9的局限。原本我试图将‘10’‘11’‘12’‘13’‘14’‘15’的值存入数据段,再利用09功能调用实现输出,但是出现了乱码和错误的结果。最后我利用数组和02功能调用实现了大于9的结果输出。
在实验过程中出现的问题有must be index or base register,网上给出的结果是:
1、使用寄存器间接寻址时,只可以使用 BX, BP, SI, DI 这四个寄存器中的一个,不可以使用其它寄存器。
2、提示的意思是(方括号里)必须是变址(index,指SI, DI)或基址(base,指BX,BP)寄存器。
根据编译给出的错误,对应的代码是mov dx,[si+ax],这里ax存放的指针偏移量,根据上面的提示,方括号中不能出现ax寄存器,所以修改的结果是:
add si,ax
movdx,[si]
后来又在网上查了一下,在inter指令集中lea指令可以在方括号[ ]中使用其他的寄存器,而这里是mov指令。根据网上的说法,lea指令的功能很强大:
1、LEA指令具有单时钟周期,执行效率很高;2、它是CPU地址生成单元参与运算的,而不是ALU参与运算的,所以在流水线上不会与上下文的算术逻辑指令产生流水相关;3、INTEL指令集中不存在很多RISC机器所具有的三操作数算术运算指令,比如像ARM的"add r0,r1,r2",而LEA指令恰好提供了同样的功能,以模拟“三元算术逻辑指令”,即在方括号[ ]中便可以进行算术运算。
出现乱码的原因主要是因为在数据段定义的字符串,结尾没有用‘$’作为终结,而我之前想通过;number db '10','$','11','$','12','$','13','$','14','$','15','$' 实现字符串的输出并终结,但出现了很奇怪的问题,至今没有解决。
lea和mov的区别
另外在编译过程中出现的警告,问题是lea和mov的混用,第一个地方是
lea si,cstring+2 ;初始指针指向 一开始我使用的mov,出现了警告
如果执意要用mov替代,mov si,offset cstring+2
后来问过老师才知道09功能调用在数据段的字符串定义只能使用一个 '$',然后我又想了想,如果实在要将'10','11'……'15'输出,可以这样定义数据段的字符串:
number db '10 ','11 ','12 ','13 ','14 ','15 ','$'
这里可以看出,在单引号中的'10'等数中,后面都有一个空格,那么可以使用 repz movsb 逐个字节读入,遇到空格就停止,这样就可以实现10这个字符串读出的功能了,(当然,同样需要定义指针指向相应的首位置)。