【汇编】典例练习(三)

>_<

 
 

Q1 :从键盘输入一个字符并在屏幕上显示,给出其ASCII二进制表达式及其包含的1的个

数,如果是“Esc”,那么退出程序。重复上述过程。

参考效果

在这里插入图片描述
▽ 我的解法

DATA SEGMENT
	BUF DB 'Please input a character : $'
	BUF_0 DB '0$'			; 多余的操作,直接MOV DL, '0' 即可, 自动存入其ASCII码
	BUF_1 DB '1$'           ; 多余的操作,直接MOV DL, '1' 即可
	BUF_2 DB ' ASCII: $'
	BUF_3 DB 'B, The number of '1' is $'
	CTRL DB 0DH, 0AH, '$'
DATA ENDS

STACK SEGMENT 'STACK'
	DW 100 DUP(?)
STACK ENDS

CODE SEGMENT
	ASSUME CS:CODE, DS:DATA, SS:STACK
START:
		MOV	AX, DATA
		MOV DS, AX	; 初始化数据段
	
LOP:
		MOV DX, OFFSET BUF
		MOV AH, 9
		INT 21H		; 输出提示	
		MOV AH, 1
		INT 21H		; 输入(到AL)
		CMP AL, 1BH
	    JZ EXIT		; 分支结构(是否为ESC)
	    PUSH AX
	    MOV DX, OFFSET BUF_2
	    MOV AH, 9
	    INT 21H
	    POP AX
		MOV BL, AL	; 对BL进行左移操作
		MOV AL, 0	; 用AL计数
		MOV CX, 8   ; 一共左移8次(CX控制循环LOOP的次数)
L1:
		SHL BL, 1   ; 循环左移,最左边的那一位被递送到CF 
		JC 	L2 		; 是1,跳到L2
		JMP	L3      ; 无条件跳到L3,这种条件跳转紧跟着无条件跳转的用法特别常见,这使得满足条件时多执行了一段代码
L2:
		PUSH AX
		MOV DX, OFFSET BUF_1   ; 输出'1'
		MOV AH, 9
		INT 21H
		POP AX
		INC AL		; 计数加一
		JMP L4
L3:
		PUSH AX
		MOV DX, OFFSET BUF_0   ; 输出'0'
		MOV AH, 9
		INT 21H
		POP AX
L4:
		LOOP L1
		PUSH AX
		MOV DL, OFFSET BUF_3
		MOV AH, 9
		INT 21H
		POP AX
		ADD AL, 30H		; 计数器加上30H转换为对应数字的ASCII
		MOV DL, AL
		MOV AH, 2
		INT 21H			; 输出结果,即1的个数
		MOV DX, OFFSET CTRL
		MOV AH, 9
		INT 21H			; 换行
		JMP LOP
EXIT:
		MOV AH, 4CH 
		INT 21H
CODE ENDS
END START

▽ 老师的解法

DATA	SEGMENT	'DATA'
		Str1	DB	'Please input a character: $'
		Str4	DB	' ASCII: $'
		Str5	DB	'B, #'1' = $' 
		Str6	DB	'. ', 0DH, 0AH, '$'
DATA	ENDS

STACK	SEGMENT	'STACK'
		DW	100H	DUP(?)     
STACK	ENDS       

CODE	SEGMENT
		ASSUME	CS:CODE, DS:DATA, SS:STACK
START:	MOV		AX, DATA			; 初始化数据段
		MOV		DS, AX

Next:	MOV		DX, OFFSET Str1		; 提示:输入一个字符
		MOV		AH, 9
		INT		21H

		MOV		AH, 1				; 键盘输入一个字符(到AL)并回显		
		INT		21H

		MOV		BH, AL				; 接下来对BH进行操作

		MOV		DX, OFFSET Str4		 
		MOV		AH, 9
		INT		21H

		MOV		BL, 0				; 计数器:计数1的个数
		MOV		CX, 8				; 准备循环左移8次
		MOV		DH, 0
Shift:	MOV		DL, '0'				; 默认显示ASCII 0
		ROL		BH, 1				; 循环左移1次		
		JNC		Bit0				; 是二进制0,跳转		
Bit1:	INC		BL					; 是二进制1
		MOV		DL, '1'				; 准备显示ASCII 1
Bit0:	MOV		AH, 2				; 显示ASCII 0/1
		INT		21H
        LOOP	Shift

		MOV		DX, OFFSET Str5		; 显示提示  
		MOV		AH, 9
		INT		21H

		ADD		BL, 30H				; 计数器加上30H转换为对应数字的ASCII
		MOV		DH, 0
		MOV		DL, BL
		MOV		AH, 2				; 显示1的个数
		INT		21H

		MOV		DX, OFFSET Str6		; 显示:回车换行  
		MOV		AH, 9
		INT		21H

		CMP		BH, 1BH				; 是否是Esc?
		JZ		EXIT				; 是Esc,跳转
		JMP		Next   

EXIT:	MOV		AH, 4CH
		INT		21H

CODE	ENDS
		END		START

▲ 对比总结 :

❶ 首先关键思路完全相同:对寄存器中的数据进行多次左或右移,对移出后存在CF的数字(0/1)进行判断即可

巧妙分配寄存器;如果某个寄存器必须要完成两个作用(比如AH既要存DIV除法的余数,又要用来完成DOS指令),那么注意使用PUSH POP保护现场

❸ 显示各种提示比较麻烦,而且会占用着AH和DX无法做其他事情

 
 

Q2 :从键盘输入一个字符并在屏幕上显示,如果是“Esc”,那么退出程序;如果是十六进制数(0-9,a-f,A-F),那么显示其平方值;如果是其它字符,提示不是十六进制数。重复上述过程。

参考效果

在这里插入图片描述
▽ 我的代码

DATA SEGMENT 
	TABLE DB 0, 2, 4, 6, 8, 11, 14, 17, 20, 23
		  DB 26, 30, 34, 38, 42, 46
	LOLI DB '0$1$4$9$16$25$36$49$64$81$100$121$144$169$196$225$'
	BUF_1 DB ' Exclude from 0~9 or a~f or A~F $'
	BUF_2 DB 'H ----> $'
	ENTER DB 0DH, 0AH,'$'
	RESULT DB 0
DATA ENDS

STACK SEGMENT STACK
	DB 100 DUP(0)
STACK ENDS

CODE SEGMENT
	ASSUME CS:CODE, DS:DATA, SS:STACK
START:
		MOV AX, DATA
		MOV DS, AX
		MOV CL, 10
		MOV CH, 0 
LOP:
		LEA BX, TABLE	;BX获取TABLE首址(用于XLAT查表)
		MOV AH, 01H
		INT 21H
		PUSH AX	
		LEA DX, BUF_2 
		MOV AH, 09H
		INT 21H			;这里会改变AL,因此要PUSHPOP保护
		POP AX
		CALL JUDGE		;修正
		XLAT            ;AL获得偏移量
		LEA BX, LOLI	;BX获取LOLI首址	
		MOV DX, BX		;给DX用于打印
		XOR AH, AH		;AH清零(目的是让DX与AX得以累加)(16位)
		ADD DX, AX		;对用于打印的DX指针做修正
		CMP CH, 0
		JA TO
		MOV AH, 09H
		INT 21H
		;换行再循环
TO:
		MOV CH, 0
		MOV DX, OFFSET ENTER
		MOV AH, 09H
		INT 21H
		LOOP LOP 
EXIT:
		MOV AH, 4CH
		INT 21H

;判断输入并进行修正		
JUDGE PROC NEAR
		CMP AL, '0'
		JB NO
		CMP AL, '9'
		JA NEXT
		SUB AL, 30H	;是0~9,进行修正
		RET
NEXT:
	    CMP AL, 'A'
	    JB NO
	    CMP AL, 'F'
	    JA FINAL
	    SUB AL, 55 ;是A~F,进行修正 
	    RET
FINAL:
		CMP AL, 'a'
		JB NO
		CMP AL, 'f'
		JA NO
		SUB AL, 87 ;是a~f,进行修正
		RET 
NO:     
		MOV DX, OFFSET BUF_1 
		MOV AH, 09H
		INT 21H
		MOV CH, 1	;当做FLAG(利用寄存器在主程序和子程序间传递信息)
		RET
JUDGE ENDP
		
CODE ENDS
END START 

▽ 老师的代码

DATA	SEGMENT	'DATA'
		Str1	DB	'Please input a HEX: $'
		Str2	DB	' Excluded from {0~9, a~f, A~F}!', 0DH, 0AH, '$'
		Str3	DB	0DH, 0AH, '$'
		Table	DB	'H =  0^2 =   0.$'
				DB	'H =  1^2 =   1.$'
				DB	'H =  2^2 =   4.$'
				DB	'H =  3^2 =   9.$'
				DB	'H =  4^2 =  16.$'
				DB	'H =  5^2 =  25.$'
				DB	'H =  6^2 =  36.$'
				DB	'H =  7^2 =  49.$'
				DB	'H =  8^2 =  64.$'
				DB	'H =  9^2 =  81.$'
				DB	'H = 10^2 = 100.$'
				DB	'H = 11^2 = 121.$'
				DB	'H = 12^2 = 144.$'
				DB	'H = 13^2 = 169.$'
				DB	'H = 14^2 = 196.$'
				DB	'H = 15^2 = 225.$' ; 每个字符串恰好是16个字节!!!
DATA	ENDS

STACK	SEGMENT	'STACK'
		DW	100H	DUP(?)     
STACK	ENDS       

CODE	SEGMENT
		ASSUME	CS:CODE, DS:DATA, SS:STACK
START:	MOV		AX, DATA			; 初始化数据段
		MOV		DS, AX

Next:	MOV		DX, OFFSET Str1		; 提示:输入一个十六进制数
		MOV		AH, 9
		INT		21H

		MOV		AH, 1				; 键盘输入一个字符并回显		
		INT		21H

		CMP		AL, 1BH				; 是否是Esc?
		JZ		EXIT				; 是Esc,跳转

		CMP		AL, '0'				; 是否属于ASCII 0~9?					
		JC		NotHEX				; 不是ASCII HEX,跳转
		CMP		AL, '9'+1			; 是否属于ASCII 0~9?	
		JC		Is0_9				; 属于ASCII 0~9,跳转

		CMP		AL, 'A'				; 是否属于ASCII A~F?					
		JC		NotHEX				; 不是ASCII HEX,跳转
		CMP		AL, 'F'+1			; 是否属于ASCII A~F?					
		JC		Upper				; 属于ASCII A~F,跳转

		CMP		AL, 'a'				; 是否属于ASCII a~f?					
		JC		NotHEX				; 不是ASCII HEX,跳转
		CMP		AL, 'f'+1			; 是否属于ASCII a~f?					
		JC		Lower				; 属于ASCII a~f,跳转

NotHEX:	MOV		DX, OFFSET Str2		; 提示:不是一个十六进制数
		MOV		AH, 9
		INT		21H
		JMP		Next   

Lower:	SUB		AL, 20H				; 变为ASCII A~F
Upper:	SUB		AL, 7				; 尾随ASCII 0~9
Is0_9:	SUB		AL, '0'				; 变为HEX 0~F

Lookup:	MOV		DX, OFFSET Table	; 平方表的基地址
		MOV		AH, 0
		SHL		AL, 4				; 平方表的偏移地址
		ADD		DX, AX				; 显示平方表
		MOV		AH, 9
		INT		21H
		MOV		DX, OFFSET Str3		; 回车换行
		MOV		AH, 9
		INT		21H
		JMP		Next
		
EXIT:	MOV		AH, 4CH
		INT		21H

CODE	ENDS
		END		START

▲ 对比总结 :

❶ 思路基本相同,都是查表的思想;

❷ 区别在于我的代码是XLAT自动查表,其实XLAT的实质就是BX获取表的首址AL中就是偏移量(字节)查到的内容返回AL;局限性在于所查的内容大小只能是一个字节,想要查字符串只能讲这个字节的内容设置为偏移量,再进行一次计算得到真正字符串的首址;

❸ 而第二种方法很巧妙 :

  不使用XLAT自动查表,利用已知的表的首址,以及将输入转化为偏移地址,进行一次计算后得到字符串的位置。

  巧妙之处在于,字符串的长度恰好设置为16个字节,那么第一、二、三…个字符串的偏移量是0,16, 32…,而这个数字可以由0, 1, 2左移4位得到(有点HashMap的意味)。

  因而,最好将表的内容的长度设置为1, 2, 4, 8, 16…字节偏移量直接移位就能得到

❹ 麻烦和难点对输入进行修正:将键入的那个ASCII码修正为我们真正需要的ASCII码数值(其实这个ASCII码对应的字符对我们来说根本没有意义)。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值