博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
8086汇编语言:数据的输出(带符号输出、二进制输出、八进制输出、十六进制输出)
阅读量:388 次
发布时间:2019-03-05

本文共 8895 字,大约阅读时间需要 29 分钟。

一、题目说明:

将一个数据存入AX中,分别以有符号数、二进制、八进制、十六进制的形式输出。

 

二、逐步求解:

从本质上来讲,计算机可不会管你存的是何种形式的数,存储进来了就只是01的数字串。所以我们以各种各样形式的输出从本质上来说是看这些01串的角度的问题。

目录:

①有符号数形式的输出:

当我们以有符号数看待计算机中存储的01串时,最高位就默认成为了符号位,0代表是正数,1代表是负数。

  • 方案一:借助移位操作,将最高位换到最低位,再借助and(其他的逻辑运算指令也行,自行决定)将除最低位之外的所有位置为0,这样就可以获取最高位的值了,进而确定正负号的输出以及下一步的操作——“正数直接按照多位数据的输出 or 负数取补数再输出”。
  • 方案二:借助符号标志位SF进行判断,SF=1为负数,SF=0则为正数。但是SF的查看需要产生运算,但是我们最好不要改变原数值,cmp正好符合我们的需求,cmp的本质是一个减法,但是又没有改变原数值。故我们可以借助cmp ax,0进行运算,js用于判断运算之后SF位的结果。

(注:下方代码使用的是方案二)

DATAS SEGMENT    ;此处输入数据段代码      CRLF DB 0AH,0DH,"$";回车换行DATAS ENDSSTACKS SEGMENT    ;此处输入堆栈段代码STACKS ENDSCODES SEGMENT    ASSUME CS:CODES,DS:DATAS,SS:STACKSSTART:    MOV AX,DATAS    MOV DS,AX  	mov ax,123	call symoutput	  	lea dx,crlf	mov ah,9	int 21h		mov ax,-123	call symoutput	     MOV AH,4CH    INT 21H;有符号数的输出,入口参数为axsymoutput proc	push bx;入栈保存	push dx		mov bx,ax;保存数据防止后续修改,可直接调用函数		cmp ax,0;比较ax中数字的正负		js negative;负数的输出		jmp positive;正数的输出				negative:	mov dl,'-';输出负号	mov ah,2	int 21h		neg bx;求补数	call output		jmp symoutputover 	positive:	call output	symoutputover:	pop dx;出栈复原	pop bx	retsymoutput endp    ;多位输出函数,入口参数为bxoutput proc	push ax;数据入栈区	push cx	push dx		;初始化变量	mov ax,bx;数据放入准备除法	mov cl,10;作为除数	mov ch,0;用于计数便于后续出栈输出	divagain:;除法数字剥离部分	cmp ax,0;判断是否已经除尽		je divover	inc ch;计数器加1	div cl	push ax;入栈,提取的时候取用ah部分,存储余数(低位优先)	mov ah,0;调整ax		jmp divagain;再次除法剥离数字		divover:;出栈输出部分	cmp ch,0;判断数字是否已经出尽		je outputover	pop ax;取用ah部分	mov dl,ah;输出部分	add dl,48	mov ah,2	int 21h	dec ch		jmp divover 		outputover:;收尾部分	pop dx	pop cx	pop ax;数据出栈区	retoutput endpCODES ENDS    END START

②二进制形式的输出:

  • 方案一:借助循环移位操作——ROL,将最高位移到最低位,再借助逻辑运算指令(譬如:and)将最后一位的值剥离出来,再输出。循环上述操作,剥离出次高位的值,直到最后一位。
  • 方案二:借助除法操作——DIV,每次将ax中的值除2,获得余数的值——压栈;循环上述操作直到ax为零;此时在出栈输出存好的余数即为结果。(但是除法有一个弊端,当你对ax值为负数的时候,显示Divide error!)(具体原因文末解释!)

(注:下方代码使用的是方案一)

DATAS SEGMENT    ;此处输入数据段代码      CRLF DB 0AH,0DH,"$";回车换行DATAS ENDSSTACKS SEGMENT    ;此处输入堆栈段代码STACKS ENDSCODES SEGMENT    ASSUME CS:CODES,DS:DATAS,SS:STACKSSTART:    MOV AX,DATAS    MOV DS,AX  	mov ax,8	call binaryoutput	  	lea dx,crlf	mov ah,9	int 21h		mov ax,-8	call binaryoutput	     MOV AH,4CH    INT 21H    ;二进制形式的输出,入口参数为axbinaryoutput proc	push bx;数据入栈,保留数值	push dx		mov dh,0;用于计数,总共16次	binaryagain:		cmp dh,16		je binaryover;输出结束,到达函数末端	rol ax,1;移动一位	mov bx,ax;数值保留	and ax,0001h;剥离出最后一位	mov dl,al;用于输出	add dl,48	mov ah,2	int 21h	inc dh;计数加一	mov ax,bx;数值恢复		jmp binaryagain;再来一次			binaryover:	pop dx;数据出栈,数值还原	pop bx	retbinaryoutput endpCODES ENDS    END START

③八进制形式的输出:

  • 方案一:借助循环移位操作——ROL,每次操作将高三位移到低三位,再借助逻辑运算指令(譬如:and)将低三位的值剥离出来,再输出。循环上述操作,再剥离出次高三位的值。但是16不能被3整除,所以在进入循环之前,ax的首位需要单独拎出来求解。(因为在图纸上求解也是在ax的01列之前补两个0凑足18位再转换为八进制。)
  • 方案二:借助除法操作——DIV,每次将ax中的值除8,获得余数的值——压栈;循环上述操作直到ax为零;此时在出栈输出存好的余数即为结果。(但是除法有一个弊端,当你对ax值为负数的时候,显示Divide error!)(具体原因文末解释!)

(注:下方代码使用的是方案二)

DATAS SEGMENT    ;此处输入数据段代码      CRLF DB 0AH,0DH,"$";回车换行DATAS ENDSSTACKS SEGMENT    ;此处输入堆栈段代码STACKS ENDSCODES SEGMENT    ASSUME CS:CODES,DS:DATAS,SS:STACKSSTART:    MOV AX,DATAS    MOV DS,AX  	mov ax,8	call octaloutput		lea dx,crlf	mov ah,9	int 21h		mov ax,-8	call octaloutput	    MOV AH,4CH    INT 21H    ;八进制形式的输出,入口参数为axoctaloutput proc	push bx;数据入栈,数值保留	push cx	push dx	;无法完整切割,所以换一种策略,循环除法		mov cl,8;作为除数	mov ch,0;作为计数器	octalagain:;数据剥离板块	cmp ax,0		je octalout;存数结束,进入输出阶段	inc ch;计数器加一	div cl;运行除法剥离数据	mov dl,ah	mov dh,0	push dx;数据入栈,后续取用dl直接输出	mov ah,0;使得ax与al值统一		jmp octalagain;loop	octalout:;数据输出板块	cmp ch,0		je octalover;输出完毕		dec ch	pop dx;取出数据	add dl,48	mov ah,2	int 21h		jmp octalout;loop		octalover:	pop dx;数据出栈,数值还原	pop cx	pop bx	retoctaloutput endpCODES ENDS    END START

本想着方案二做完算了,担心老师不满意(显示Divide error!),所以又整了个方案一的代码!

DATAS SEGMENT    ;此处输入数据段代码      CRLF DB 0AH,0DH,"$";回车换行DATAS ENDSSTACKS SEGMENT    ;此处输入堆栈段代码STACKS ENDSCODES SEGMENT    ASSUME CS:CODES,DS:DATAS,SS:STACKSSTART:    MOV AX,DATAS    MOV DS,AX  	mov ax,8;正数测试	call octaloutput	  	lea dx,crlf;输出回车换行	mov ah,9	int 21h		mov ax,-8;负数测试	call octaloutput	    MOV AH,4CH    INT 21H    ;八进制形式的输出,入口参数为axoctaloutput proc	push bx;数据入栈,数值保留	push cx	push dx		;最高一位单独输出处理	rol ax,1;移动一位	mov bx,ax;保留数值	and al,01h;剥离最后一位数据	mov dl,al;准备输出	add dl,48	mov ah,2	int 21h		mov ax,bx;恢复数值		;对后续的15位进行处理,15除3等于5	mov dh,0;计数5次,留下最后一位单独处理octalagain:	cmp dh,5		je octalover;循环结束,进入收尾工作		inc dh;计数加一	mov cl,3;作为移位位数	rol ax,cl;移动最高三位	mov bx,ax;保留ax的值防止修改	and al,07h;剥离后三位数据	mov dl,al;准备输出	add dl,48	mov ah,2	int 21h	mov ax,bx;恢复ax的保留数值		jmp octalagain;loopoctalover:		pop dx;数据出栈,数值还原	pop cx	pop bx	retoctaloutput endpCODES ENDS    END START

④十六进制形式的输出:

  • 方案一:借助循环移位操作——ROL,每次操作将高四位移到低四位,再借助逻辑运算指令(譬如:and)将低四位的值剥离出来,再输出(记得分数字和字母)。循环上述操作,剥离出次高四位的值,直到最后一位。
  • 方案二:借助除法操作——DIV,每次将ax中的值除16,获得余数的值——压栈;循环上述操作直到ax为零;此时在出栈输出存好的余数即为结果(记得分数字和字母)。(但是除法有一个弊端,当你对ax值为负数的时候,显示Divide error!)(具体原因文末解释!)

(注:下方代码使用的是方案一)

DATAS SEGMENT    ;此处输入数据段代码      CRLF DB 0AH,0DH,"$";回车换行DATAS ENDSSTACKS SEGMENT    ;此处输入堆栈段代码STACKS ENDSCODES SEGMENT    ASSUME CS:CODES,DS:DATAS,SS:STACKSSTART:    MOV AX,DATAS    MOV DS,AX  	mov ax,8	call hexadecimaloutput	  	lea dx,crlf	mov ah,9	int 21h		mov ax,-8	call hexadecimaloutput	    MOV AH,4CH    INT 21H    ;十六进制形式的输出,入口参数为axhexadecimaloutput proc	push bx;数据入栈,数值保留	push cx	push dx		mov dh,0;用于计数,总共4次	hexadecimalagain:	cmp dh,4		je hexadecimalover;循环结束,进行收尾工作		inc dh;计数加一	mov cl,4	rol ax,cl	mov cx,ax;数值保留	and ax,0fh;数据剥离后四位	mov dl,al	cmp dl,9;区分需要输出数字还是字母		ja alphabet;作为字母输出	add dl,48;作为数字形式的输出	mov ah,2	int 21h		mov ax,cx;数据恢复		jmp hexadecimalagain		alphabet:;作为字母形式的输出	add dl,55	mov ah,2	int 21h	mov ax,cx;数据恢复		jmp hexadecimalagainhexadecimalover:			pop dx;数据出栈,数值保留	pop cx	pop bx	rethexadecimaloutput endpCODES ENDS    END START

三、问题解答:

为什么上面输入负数使用除法就不行了呢?

因为在你输入负数之后,在寄存器ax中是以补码的形式进行存储,所以首位是 1 ,但是计算机可不会管你是不是正负数,进去之后就是01串,此时值就很大。当你进行除法将数据进行剥离的时候,除完之后的结果是放在 ah 和 al 中的,ax 是16位的,但是 ah 和 al 是8位的,所以在上面程序中除完之后的结果放在寄存器 ah 和 al就会溢出,进而报错!

所以建议使用移位进行处理!

 

四、最终汇总代码:

DATAS SEGMENT    CRLF DB 0AH,0DH,"$";回车换行    string db "The original value was:","$";原本数值输出提示    string1 db "The binary form is:","$";二进制输出提示    string2 db "The octal number system is:","$";八进制输出提示    string3 db "The hexadecimal form is:","$";十六进制输出提示DATAS ENDSSTACKS SEGMENT    ;此处输入堆栈段代码STACKS ENDSCODES SEGMENT    ASSUME CS:CODES,DS:DATAS,SS:STACKSSTART:    MOV AX,DATAS    MOV DS,AX    	;原来有符号数形式的输出  	lea dx,string;原本数据的输出提示  	mov ah,9  	int 21h  		mov ax,-123	call symoutput		lea dx,crlf	mov ah,9	int 21h		;二进制形式的输出	lea dx,string1;二进制的输出提示                                           mov ah,9							     int 21h		mov ax,-123	call binaryoutput		lea dx,crlf	mov ah,9	int 21h		;八进制形式的输出	lea dx,string2;八进制的输出提示	mov ah,9	int 21h		mov ax,-123	call octaloutput		lea dx,crlf	mov ah,9	int 21h		;十六进制形式的输出	lea dx,string3;十六进制的输出提示	mov ah,9	int 21h		mov ax,-123	call hexadecimaloutput		lea dx,crlf	mov ah,9	int 21h		    MOV AH,4CH    INT 21H      ;二进制形式的输出,入口参数为axbinaryoutput proc	push bx;数据入栈,保留数值	push dx		mov dh,0;用于计数,总共16次	binaryagain:		cmp dh,16		je binaryover;输出结束,到达函数末端	rol ax,1;移动一位	mov bx,ax;数值保留	and ax,0001h;剥离出最后一位	mov dl,al;用于输出	add dl,48	mov ah,2	int 21h	inc dh;计数加一	mov ax,bx;数值恢复		jmp binaryagain;再来一次			binaryover:	pop dx;数据出栈,数值还原	pop bx	retbinaryoutput endp;八进制形式的输出,入口参数为axoctaloutput proc	push bx;数据入栈,数值保留	push cx	push dx		;最高一位单独输出处理	rol ax,1;移动一位	mov bx,ax;保留数值	and al,01h;剥离最后一位数据	mov dl,al;准备输出	add dl,48	mov ah,2	int 21h		mov ax,bx;恢复数值		;对后续的15位进行处理,15除3等于5	mov dh,0;计数5次,留下最后一位单独处理octalagain:	cmp dh,5		je octalover;循环结束,进入收尾工作		inc dh;计数加一	mov cl,3;作为移位位数	rol ax,cl;移动最高三位	mov bx,ax;保留ax的值防止修改	and al,07h;剥离后三位数据	mov dl,al;准备输出	add dl,48	mov ah,2	int 21h	mov ax,bx;恢复ax的保留数值		jmp octalagain;loopoctalover:		pop dx;数据出栈,数值还原	pop cx	pop bx	retoctaloutput endp;十六进制形式的输出,入口参数为axhexadecimaloutput proc	push bx;数据入栈,数值保留	push cx	push dx		mov dh,0;用于计数,总共4次	hexadecimalagain:	cmp dh,4		je hexadecimalover;循环结束,进行收尾工作		inc dh;计数加一	mov cl,4	rol ax,cl	mov cx,ax;数值保留	and ax,0fh;数据剥离后四位	mov dl,al	cmp dl,9;区分需要输出数字还是字母		ja alphabet;作为字母输出	add dl,48;作为数字形式的输出	mov ah,2	int 21h		mov ax,cx;数据恢复		jmp hexadecimalagain		alphabet:;作为字母形式的输出	add dl,55	mov ah,2	int 21h	mov ax,cx;数据恢复		jmp hexadecimalagainhexadecimalover:			pop dx;数据出栈,数值保留	pop cx	pop bx	rethexadecimaloutput endp    ;有符号数的输出,入口参数为axsymoutput proc	push bx;入栈保存	push dx		mov bx,ax;保存数据防止后续修改,可直接调用函数		cmp ax,0;比较ax中数字的正负		js negative;负数的输出		jmp positive;正数的输出				negative:	mov dl,'-';输出负号	mov ah,2	int 21h		neg bx;求补数	call output		jmp symoutputover 	positive:	call output	symoutputover:	pop dx;出栈复原	pop bx	retsymoutput endp    ;多位输出函数,入口参数为bxoutput proc	push ax;数据入栈区	push cx	push dx		;初始化变量	mov ax,bx;数据放入准备除法	mov cl,10;作为除数	mov ch,0;用于计数便于后续出栈输出	divagain:;除法数字剥离部分	cmp ax,0;判断是否已经除尽		je divover	inc ch;计数器加1	div cl	push ax;入栈,提取的时候取用ah部分,存储余数(低位优先)	mov ah,0;调整ax		jmp divagain;再次除法剥离数字		divover:;出栈输出部分	cmp ch,0;判断数字是否已经出尽		je outputover	pop ax;取用ah部分	mov dl,ah;输出部分	add dl,48	mov ah,2	int 21h	dec ch		jmp divover 		outputover:;收尾部分	pop dx	pop cx	pop ax;数据出栈区	retoutput endp	CODES ENDS    END START

Ending... ...

 

转载地址:http://hlmg.baihongyu.com/

你可能感兴趣的文章