两个题目
- 从BUF单元开始存有一字符串(长度<255),编程实现统计该字符串中的ASCII在42H~45H之间的字符个数,并将统计结果以二进制形式显示在屏幕。
- 程序执行后,给出提示操作,请用户键入用户名和密码;用户在键入密码时,程序不回显键入字符;只有当用户键入的用户名,密码字符串和程序内定的字符串相同时,显示欢迎界面并返回DOS;否则给出提示信息,用户名或密码错误,再次输入。界面颜色自定(彩色或黑白)
源代码
本来是想用rdrand指令来写一段随机数生成代码的,但是发现rdrand这个指令是在Inter于Ivy Bridge微架构(2012年生产)后才定义到IA32架构中的,目前我们的电脑上基本上都支持这个指令,但是经过尝试之后发现即使采用直接写字节码dosbox也不支持这个指令,可能有以下两个原因:
- DosBox的虚拟化环境采用了模拟而不是虚拟,这导致了只支持实模式下的指令集;
- 这个指令只能在保护模式下使用
第一题的源代码
.586P
data segment use16
Count_Number db 0
String_1 db 'ABCDEFG',0
data ends
Code segment USE16
ASSUME CS: CODE, DS: DATA
BEG:
mov ax,data
mov ds,ax
mov di,offset String_1
;得到在范围内的数
Get_Count:
mov al,ds:[di]
cmp al,0
jz Print_Result
cmp al,42h
jb .1
cmp al,45h
ja .1
inc Count_Number
.1:
inc di
jmp Get_Count
;得到在范围内的数
;打印数据
Print_Result:
mov bl,Count_Number
mov cx,8
.2:
mov dl,0
shl bl,1
adc dl,30h
mov ah,2
int 21h
loop .2
mov dl,'h'
int 21h
;打印数据
mov ah,4ch
int 21h
code ends
end BEG
第二题的源代码
.586P
data segment use16
;定义数据接受区
UserName db 100 dup(0)
UserName_Recived_Number dw 0
Password db 100 dup(0)
Password_Recived_Number dw 0
;定义数据接受区
UserName_Origin db 'YourUserName',0
Password_Origin db 'YourPassword',0
String_Tip db 'Please enter your username!',0ah,0dh,'$'
String_Password_Tip db 'Please enter your password!',0ah,0dh,'$'
String_Wrong_Tip db 'Wrong username or password!\n',0ah,0dh,'$'
String_Hello_Tip db 'You got it!',0ah,0dh,'$'
data ends
Code segment USE16
ASSUME CS: CODE, DS: DATA
BEG:
mov ax,data
mov ds,ax
mov dx,offset String_Tip
mov ah,9
int 21h
mov UserName_Recived_Number,0
GetUserName:
mov ah,1
int 21h
mov di,offset UserName
add di,UserName_Recived_Number
mov ds:[di],al
inc UserName_Recived_Number
cmp al,0dh;当输入回车时实际上是输入了0x0a,0x0d两个字符,此时系统调用实际上返回的是最后一个字符0x0d
jnz GetUserName
mov di,offset UserName
add di,UserName_Recived_Number
sub di,1
mov [di],0
mov dx,offset String_Password_Tip
mov ah,9
int 21h
mov Password_Recived_Number,0
GetPassword:
mov ah,8
int 21h
mov di,offset Password
add di,Password_Recived_Number
mov ds:[di],al
inc Password_Recived_Number
cmp al,0dh;当输入回车时实际上是输入了0x0a,0x0d两个字符,此时系统调用实际上返回的是最后一个字符0x0d
jnz GetPassword
mov di,offset Password
add di,Password_Recived_Number
sub di,1;将最后一个输入的0x0d字符删去
mov [di],0
cld;当df位为0时,采用大端方式
mov ax,ds
mov es,ax
CheckUserName:
mov si,offset UserName_Origin
mov di,offset UserName
mov cx,UserName_Recived_Number
REPE cmpsb;采用串比较,源地址为ds:si,目的地址为es:di
jnz Wrong
CheckPassword:
mov si,offset Password_Origin
mov di,offset Password
mov cx,Password_Recived_Number
REPE cmpsb
jnz Wrong
mov dx,offset String_Hello_Tip
mov ah,9
int 21h
mov ah,4ch
int 21h
Wrong:
mov dx,offset String_Wrong_Tip
mov ah,9
int 21h
jmp BEG
code ends
end BEG
实际上这两个题目都比较简单,实际上编写的时候只需要注意一下系统调用之后的参数改变就好了。
其中第二个源代码由于比较懒…有以下缺陷:
- 并没有判断输入字符的个数,而是以回车作为结束循环条件,所以存在缓冲区溢出;
- 并没有考虑结束条件,这意味着如果不能输入正确的用户名和秘密将不能返回DOS系统
运行结果
第一个题目:
第二个题目: