学习总结之汇编密码通行字

小白

此篇博客为学习总结,代码逻辑比较乱~
这是我的第一篇博客,肯定会有这样那样的问题,欢迎各位大佬评论区提建议。

程序功能

基本实现主流密码通行字功能,程序执行后,输出提示,请用户输入密码(通行字),按回车键表示键入结束,开始判断密码是否通过验证。只有当用户输入的密码和程序内置字符串完全相同时,显示欢迎界面,程序返回DOS, 否则请用户重新输入密码。

  1. 如果密码不通过,会清屏之后再给出提示,重新输入;
  2. 密码输入过程中,每位密码明文显示0.2秒之后变成星号,且程序在接收过程中不响应Ctrl_C;
  3. 提供密码长度检查,超过规定长度(16位),输入无效。;
  4. 支持移动光标到任意位置并修改密码(已添加边界检查,光标不会移动到非法位置),总体上实现主流的密码输入功能;

程序源码

该程序是课程的上机实验,所以整个程序是作为一个整体来写的。里面的子程序可能改变了某些寄存器的值,但这些寄存器对我这个程序而言是无关重要的,我就没有去保存/还原这些寄存器的值。所以,如果要移植某个子程序或者宏指令,请注意根据你自己的程序做好相应的保护。

显示单个字符

使用的是DOS 21H型中断,02H号功能。功能很简单,要注意的是,这个功能会破坏AL寄存器的值。

;显示一个字符
;参数 CHAR=显示字符的ASCII
;被改动的寄存器 AH,DL,破坏AL
DOSCH  MACRO   CHAR
       MOV     AH, 02H
       MOV     DL, CHAR
       INT     21H
       ENDM

无属性显示字符串

使用的是DOS 21H型中断,09H号功能。

入口参数:DS:DX=字符串首地址,字符串必须以’$’(ASCII码值为24H)为结束标志。
出口参数:无

;无属性显示字符串
;参数 POS=字符串名称
;被改的寄存器 AH,DX,破坏AL
DOSOU  MACRO   POS
       MOV     AH, 09H
       MOV     DX, OFFSET POS
       INT     21H
       ENDM

BIOS属性显示字符串

使用的是BIOS 10H型中断,13H号功能,该功能比较灵活。

入口参数:AL=0~3,BH=显示页号,BL=属性字(当AL=0、1时有效),CX=串长度,DX、DL=字符串显示的起始行、列号,ES:BP=待显示字符串首地址。
出口参数:无

该功能从屏幕当前光标所在位置开始显示字符串,遇到串结束符24H时停止,串结束符不显示。

说明:

  1. 该功能从屏幕指定位置开始显示一串彩色字符。
  2. 待显示的字符串要放在附加段,首地址偏移量写入BP寄存器。(提示:不要忘记初始化ES寄存器)
  3. AL=0表示:待显示的字符串中仅包含字符的ASCII码,串中各字符的属性由BL中的属性字决定,串显示结束后,光标返回调用前的位置。
  4. AL=1表示:光标停留在字符串的末尾,其余同AL=0。
  5. AL=2表示:待显示的字符串中包含有各个字符的ASCII码和属性字,格式为(ASCII码,属性,…,ASCII码,属性),串显示结束后,光标返回调用前的位置。
  6. AL=3表示:光标停留在字符串的末尾,其余同AL=2。
  7. 当AL选择2或者3时,CX中的串长度不包括个字符的属性字节。
;BIOS属性显示字符串
;参数ROW=行,COL=列,POS=字符串首地址,LEN=字符串长度,COLOR=属性
;被改的寄存器 AX,BX,CX,DX,BP,需要初始化ES
BIOSO  MACRO   ROW, COL, POS, LEN, COLOR
       MOV     AX, 1301H
       MOV     BH, 0
       MOV     BL, COLOR
       MOV     CX, LEN
       MOV     DH, ROW
       MOV     DL, COL
       MOV     BP, OFFSET POS
       INT     10H
       ENDM

附彩色编码表:

D7 D6 D5 D4 (背景)颜色D3 D2 D1 D0 (前景)颜色
0 0 0 01 0 0 0
0 0 0 11 0 0 1浅蓝
0 0 1 0绿1 0 1 0浅绿
0 0 1 11 0 1 1浅青
0 1 0 01 1 0 0浅红
0 1 0 1品红1 1 0 1浅品红
0 1 1 01 1 1 0
0 1 1 1灰白1 1 1 1

数据段

其中,PASS字段,9表示本地通行字长度,'B16080319’是本地通行字;
BUF字段是开辟的缓存区,用户输入的密码会存入这里,16表示用户输入密码的最大长度,0是用户输入的密码长度(随用户输入密码不断更改)。

DATA   SEGMENT USE16
PASS   DB      9,'B16080319'
STRA   DB      'Logging...', 0DH, 0AH, '$'
STRB   DB      'Pass: $'
STRC   DB      'Error, please re-enter', 0DH, 0AH, '$'
STRW   DB      'WELCOME...', '$'
BUF    DB      16, 0
       DB      20 DUP(?),'$'
TIME   DW      20
DATA   ENDS

程序主干

初始化程序,调用接收子程序(INPUT)接收密码,利用串比较指令比较接收的通行字是否和本地设定的通行字完全一样。是,则在屏幕中央卷屏,清理一块区域显示欢迎界面;否,则清屏,同时输出错误,提示用户重新输入。
其中,利用串比较指令比较通行字的时候,第一个字符是通行字长度(ASCII码值),如果长度都不一样就没有继续往下比较的必要了。

CODE   SEGMENT USE16                      ;代码段开始伪指令,结束伪指令在最后
       ASSUME  DS:DATA, CS:CODE, ES:DATA  ;串比较指令要用到ES
BEG:   MOV     AX, DATA
       MOV     DS, AX
       MOV     ES, AX                     ;初始化ES
       DOSOU   STRA
NEXT:  DOSOU   STRB
       MOV     BX, OFFSET BUF             ;传参,数据存储区格式为最大长度+实际长度+字符写入区
       CALL    INPUT
       MOV     CH, 00H 
       MOV     CL, PASS
       INC     CX                         ;连带串长度一起比较
       MOV     SI, OFFSET BUF+1
       MOV     DI, OFFSET PASS
       CLD
       REPZ    CMPSB    
       JZ      WEL
       MOV     AX, 0003H                  ;同时清屏
       INT     10H
       BIOSO   0, 0, STRC, 24, 00001100B
       JMP     NEXT
WEL:   MOV     AX, 0603H
       MOV     BH, 01111001B
       MOV     CX, 0B21H
       MOV     DX, 0D2EH
       INT     10H
       BIOSO   0CH, 23H, STRW, 10, 01111001B
       MOV     AH, 4CH
       INT     21H

接收用户输入的密码(INPUT)

程序首先判断接收的字符是否是功能键,如果是,则再接收一次,获取扩展码,根据具体方向(只响应左、右两个方向)移动光标位置;否则,判断是删除键还是回车键,或者是其他按键,做出对应处理。如果是删除键,则先调用子程序DELPW删除BUF缓存区中光标位置对应的字符,然后设置参数为0,调用子程序DEOCH删除最后一个星号;如果是回车键,则结束此次调用;如果是其他按键,则先调用子程序ENTPW在BUF缓存区中光标位置对应的位置插入获取的字符,然后设置参数为1,调用子程序DEOCH在光标位置显示获取的字符0.2秒后变为星号,并且在屏幕上密码输入区域末尾追加一个星号。

主要依赖于DOS 21H型中断,07H号功能(等待从键盘输入一个字符,但该输入 字符不显示在屏幕上,不响应Ctrl_C)。

入口参数:无
出口参数:AL=按键的ASCII码,若AL=0,需要再次调用该项功能才能在AL中得到该按键的扩展码。

常用功能键扩展码如下:

功能键扩展码
方向键:上48H
方向键:下50H
方向键:左4BH
方向键:右4DH

值得注意的是,删除键(backspace)和回车键(enter)并不是功能键,它们的ASCII码分别为08H和0DH。

;功能:输入密码,支持光标移动和修改,非明文
;传参BX为输入数据空间首地址,含限制字符串最大长度以及实际写入字符串长度,以回车换行键结束输入
;被改动的寄存器 AX,BX,CX, DX,SI, DI,BP其中BX是传参寄存器,BX和SI在子程序中贯穿全文,不可胡乱更改
INPUT  PROC    NEAR 
       MOV     BYTE PTR [BX+1], 00H
       MOV     SI, 0
COM:   MOV     AX, 0700H
       INT     21H
       CMP     AL, 0                   ;功能键
       JNZ     JUDGE
       MOV     AX, 0700H
       INT     21H
       CMP     AL, 4BH
       JZ      LEFT
       CMP     AL, 4DH
       JNZ     COM
       MOV     DH, 00H
       MOV     DL, [BX+1]
       CMP     SI, DX
       JNB     COM
       DOSCH   '*'                     ;相当于光标右移
       INC     SI
       JMP     COM
LEFT:  CMP     SI, 0
       JLE     COM
       DOSCH   08H
       DEC     SI
       JMP     COM       
JUDGE: CMP     AL, 08H                 ;删除键
       JNZ     SAVE
       CMP     SI, 0
       JLE     COM                     ;已经在数据存储区首地址处,不响应删除键
       CALL    DELPW
       DEC     SI
       DEC     BYTE PTR [BX+1]
       MOV     DI, 0
       CALL    DOECH
       JMP     COM
SAVE:  CMP     AL, 0DH                 ;回车结束
       JZ      EOUT
       MOV     DH, 00H
       MOV     DL, [BX]
       CMP     SI, DX
       JNB     COM                     ;如果达到最大长度,输入无效
       CALL    ENTPW
       INC     SI
       INC     BYTE PTR [BX+1]
       MOV     DI, 1
       CALL    DOECH
       JMP     COM
EOUT:  DOSCH   0AH
       RET
INPUT  ENDP

刷新屏幕密码输入区(DOECH)

根据参数,若实参为0,则先保存当前光标位置,将光标位置移到屏幕密码输入区末尾,然后删除屏幕密码输入区末尾的一个星号,最后再重置光标位置(在初始位置前移一位);若实参为1,则先在光标位置显示获取的字符0.2秒,保存光标位置,然后在屏幕密码输入区末尾追加一个星号,最后再重置光标位置(在初始位置后移一位)。

;传参DI, DI=0删除,DI!=0写入
;调用应该在密码已经存入之后,AL被破坏
;父程序中对调用和INC/DEC SI的次序无要求,但INC/DEC SI和[BX+1]应该同时在调用之前或之后
;父程序中应该提供是否需要删除的检查
DOECH  PROC    NEAR
       CMP     DI, 0
       JZ      DELCH
       DOSCH   AL
       CALL    WAITS
       DOSCH   08H
       DOSCH   '*'
       DOSCH   08H
DELCH: PUSH    BX                      ;PROTECT
       MOV     DH, 00H
       MOV     DL, [BX+1]
       SUB     DX, SI
       MOV     BL, DL
       MOV     AH, 03H                 ;CL被破坏
       MOV     BH, 00H
       INT     10H
       PUSH    DX                      ;PROTECT
       ADD     DL, BL
       MOV     AH, 02H
       INT     10H
       CMP     DI, 0
       JNZ     ENTCH
       DOSCH   08H
       DOSCH   00H
       POP     DX
       DEC     DL
       JMP     RENEW
ENTCH: DOSCH   '*'
       POP     DX
       INC     DL
;BH页数前面获取光标位置得到,且未改变,前面DOSCH内已经设置AH=02H,不用再设置
RENEW: INT     10H
       POP     BX
       RET
DOECH  ENDP

延时函数(WAITS)

延时函数用到了21H型中断,2C号DOS功能。该功能的作用是取时间,出口参数CH:CL=时:分,DH:DL=秒:1/100秒。

;延时函数
;内存数传参,参数TIME=延迟时间(TIME<=32767),单位1/100s
;被改动的寄存器 AX, CX, DX, BP       
WAITS  PROC
       MOV     BP, TIME
       MOV     AH, 2CH           ;DH中存1s为单位,DL中为1/100s
       INT     21H
GTTE:  MOV     AL, 100
       MUL     DH
       MOV     DH, 0
       ADD     AX, DX            ;(AX)=(DH)*100+(DL)
       PUSH    AX
       MOV     AH, 2CH
       INT     21H
       POP     CX
       PUSH    DX
       MOV     AL, 100
       MUL     DH
       MOV     DH, 0
       ADD     AX, DX
       POP     DX
       SUB     CX, AX
       CMP     CX, 0
       JLE     NOOU
       SUB     CX, 6000          ;分钟过0点纠正
NOOU:  ADD     BP, CX
       CMP     BP, 0
       JG      GTTE
       RET
WAITS  ENDP

上面的代码中,加入了时间是否过整分点的判断,其实,考虑到实际情况,这个操作是多余的(不考虑中断)。计算机的运算速度很快,每一趟执行所花费的时间不会超过1/100秒,所以,只需要判断当前时间和上一趟读取的时间是否相同就可以了,不同就给(BP)减去1 (TIME-1)。

实际上,延时功能还可以结合2CH及2DH号DOS功能(21H型中断)来实现,有兴趣的读者可以尝试一下。

  • 中断类型:21H
  • 功能号:2DH,设置时间
  • 入口参数:CH:CL=时:分,DH:DL=秒:1/100秒
  • 出口参数:AL=00H成功,AL=0FFH失败

在实现这个延时功能之前,我在(这里)找到15H型中断86H号功能(等待功能)实现延时的例子,但是运行时发现它除了已知的寄存器之外还会乱改其他东西,所以最终被我放弃了。有兴趣的同学可以研究一下,如果有了结果,希望可以在评论区留言告诉我答案。(谢谢)

删除密码缓存区指定位置的单个字符(DELPW)

和C/C++删除字符串中的某一字符一样,将该位置后面的所有字符向前移一位,覆盖掉该位置的字符。SI是要删除字符的位置,SI+1是光标的位置。

;无传参,删除密码中的单个字符, DEC 在调用该程序后
DELPW  PROC    NEAR
       PUSH    SI
       MOV     CH, 00H
       MOV     CL, [BX+1]
       SUB     CX, SI
       CMP     CX, 0
       JLE     RENEW 
AGAIN: MOV     DL, [BX+SI+2]
       MOV     [BX+SI+1], DL
       INC     SI
       LOOP    AGAIN 
RENEW: POP     SI    
       RET
DELPW  ENDP

在密码缓存区指定位置插入单个字符(ENTPW)

先把插入位置后面的字符全部向后移一位,然后再将插入位置的存储单元赋值为接收到的ASCII码。其中SI是已完成写入的位置,SI+1是光标所在位置(即下一个要写入的位置)。

;无传参,密码中插入单个字符, INC 在调用该程序后
ENTPW  PROC    NEAR
       PUSH    SI
       MOV     CH, 00H
       MOV     CL, [BX+1]
       SUB     CX, SI
       CMP     CX, 0
       JLE     RENEW
       MOV     DH, 00H
       MOV     DL, [BX+1]
       MOV     SI, DX 
AGAIN: MOV     DL, [BX+SI+1]
       MOV     [BX+SI+2], DL
       DEC     SI
       LOOP    AGAIN 
RENEW: INC     SI                     ;需要把SI置到下一位写入
       MOV     [BX+SI+1], AL 
       POP     SI   
       RET
ENTPW  ENDP

当然,整个程序的最后,不要忘记代码段的结束伪指令和汇编结束伪指令

CODE   ENDS
       END     BEG

程序运行结果

  1. 输入提示:
    图片1. 输入提示
  2. 密码输入过程中,输入字符显示0.2秒之后变成星号:
    图片2. 显示0.2秒变成星号
  3. 如果密码错误,清屏并输出错误提示:
    图片3. 清屏输出错误提示
  4. 移到光标到任意位置(有边界检查)修改密码:
    图片4. 移动光标修改密码
  5. 验证通过的欢迎界面:
    图片5. 验证通过的欢迎界面

结束:-)

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值