精华内容
下载资源
问答
  • 汇编语言中的有符号数和无符号数

    千次阅读 2015-08-21 13:24:50
     在汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符号然后用两个标准来...
    

    一、只有一个标准

           在汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分有符号还是无符号然后用两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db -20 汇编后为:EC ,而 db 236 汇编后也为 EC 。这里有一个小问题,思考深入的朋友会发现,db 是分配一个字节,那么一个字节能表示的有符号整数范围是:-128 ~ +127 ,那么 db 236 超过了这一范围,怎么可以?是的,+236 的补码的确超出了一个字节的表示范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00 EC,一个字节装不下,但是,别忘了“截断”这个概念,就是说最后的结果被截断了,00 EC 是两个字节,被截断成 EC ,所以,这是个“美丽的错误”,为什么这么说?因为,当你把 236 当作无符号数时,它汇编后的结果正好也是 EC ,这下皆大欢喜了,虽然汇编器只用一个标准来处理,但是借用了“截断”这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你一个字节,你想输入有符号的数,比如 -20 那么汇编后的结果是正确的;如果你输入 236 那么你肯定当作无符号数来处理了(因为236不在一个字节能表示的有符号数的范围内啊),得到的结果也是正确的。于是给大家一个错觉:汇编器有两套标准,会区分有符号和无符号,然后分别汇编。其实,你们被骗了。:-)

     

    二、存在两套指令!

           第一点说明汇编器只用一个方法把整数字面量汇编成真正的机器数。但并不是说计算机不区分有符号数和无符号数,相反,计算机对有符号和无符号数区分的十分清晰,因为计算机进行某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和无符号数准备的。但是,这里要强调一点,一个数到底是有符号数还是无符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就用那一套处理有符号数的指令,当你认为你要处理的数是无符号的,那就用处理无符号数的那一套指令。加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。

    举例来说:

           内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把x,y当作有符号数来看时,x = -20 ,y = +2 。当作无符号数看时,x = 236 ,y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE 当作有符号数就是:-18 ,无符号数就是 238 。所以,add 一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))

           乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472 。(参看文后附录2例程)

     

    三、可爱又可怕的c语言。

           为什么又扯到 c 了?因为大多数遇到有符号还是无符号问题的朋友,都是c里面的 signed 和 unsigned 声明引起的,那为什么开头是从汇编讲起呢?因为我们现在用的c编译器,无论gcc 也好,vc6 的cl 也好,都是将c语言代码编译成汇编语言代码,然后再用汇编器汇编成机器码的。搞清楚了汇编,就相当于从根本上明白了c,而且,用机器的思维去考虑问题,必须用汇编。(我一般遇到什么奇怪的c语言的问题都是把它编译成汇编来看。)

           C 是可爱的,因为c符合kiss 原则,对机器的抽象程度刚刚好,让我们即提高了思维层面(比汇编的机器层面人性化多了),又不至于离机器太远 (像c# ,java之类就太远了)。当初K&R 版的c就是高级一点的汇编……:-)

           C又是可怕的,因为它把机器层面的所有的东西都反应了出来,像这个有没有符号的问题就是一例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举一例:

     

    #include <stdio.h> 
    #include <string.h> 
    
    int main()
    {
        int x = 2; 
        char * str = "abcd"; 
        int y = (x - strlen(str) ) / 2;
    
        printf("%d/n",y);
    
        return 0;
    }
    

           结果应该是 -1 但是却得到:2147483647 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时类型被自动转换了,结果自然出乎意料...

           观察编译后的代码,除法指令为 div ,意味无符号除法。

           解决办法就是强制转换,变成 int y = (int)(x - strlen(str) ) / 2; 强制向有符号方向转换(编译器默认正好相反),这样一来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,用有符号处理指令 imul ,idiv 等得到的结果,与用 无符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号无符号计算的问题,特别是存在讨厌的自动转换时,要倍加小心!(这里自动转换时,无论gcc还是cl都不提示!!!)

           为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。(完)

     

     

    附录

    1:顺便指出楼上那位同志错误的地方,《IA-32 Intel Architecture Software Developer’s Manual》第2卷PDF文档表述为“sign-extended imm8”,这个sign-extended 是符号扩展的意思,他理解成有符号数了。。。

    说说符号扩展:当操作数进行长度扩展时,既要让操作数变长又不能改变原数值,所以就出现了符号扩展一说。比如 movsx ax, 0xEC ,执行扩展后,ax的值为:0xFFEC,长度变长了,结果没变,都是 -20 。

     

    2:两套乘法指令结果例程

     

    ;; 程序存储为 x.s 

    ;;--start--------------------------------------------------------

     

    extern printf 

    global main 

     

    section .data

            str1: db "%x",0x0d,0x0a,0 

            n: db 0x02

    section .text 

    main: 

            xor eax,eax

            mov al, 0xec

            mul byte [n] ;有符号乘法指令为: imul

     

            push eax

            push str1

            call printf 

            

            add esp,byte 4 

            ret 

    ;;--end---------------------------------------------------------------

     

    编译步骤:

    1. nasm -felf x.s 

    2. gcc x.o

     

    ubuntu7.04 下用nasm和gcc编译通过。结果符合文章所述。

     

    展开全文
  • 汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符号然后用两个标准来处理,...

    转自点击打开链接

    一、只有一个标准!

    在汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分有符号还是无符号然后用两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db -20 汇编后为:EC ,而 db 236 汇编后也为 EC 。这里有一个小问题,思考深入的朋友会发现,db 是分配一个字节,那么一个字节能表示的有符号整数范围是:-128 ~ +127 ,那么 db 236 超过了这一范围,怎么可以?是的,+236 的补码的确超出了一个字节的表示范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00 EC,一个字节装不下,但是,别忘了“截断”这个概念,就是说最后的结果被截断了,00 EC 是两个字节,被截断成 EC ,所以,这是个“美丽的错误”,为什么这么说?因为,当你把 236 当作无符号数时,它汇编后的结果正好也是 EC ,这下皆大欢喜了,虽然汇编器只用一个标准来处理,但是借用了“截断”这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你一个字节,你想输入有符号的数,比如 -20 那么汇编后的结果是正确的;如果你输入 236 那么你肯定当作无符号数来处理了(因为236不在一个字节能表示的有符号数的范围内啊),得到的结果也是正确的。于是给大家一个错觉:汇编器有两套标准,会区分有符号和无符号,然后分别汇编。其实,你们被骗了。:-)

    二、存在两套指令!

    第一点说明汇编器只用一个方法把整数字面量汇编成真正的机器数。但并不是说计算机不区分有符号数和无符号数,相反,计算机对有符号和无符号数区分的十分清晰,因为计算机进行某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和无符号数准备的。但是,这里要强调一点,一个数到底是有符号数还是无符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就用那一套处理有符号数的指令,当你认为你要处理的数是无符号的,那就用处理无符号数的那一套指令。加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。
    举例来说:
    内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把x,y当作有符号数来看时,x = -20 ,y = +2 。当作无符号数看时,x = 236 ,y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE 当作有符号数就是:-18 ,无符号数就是 238 。所以,add 一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))
    乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472 。(参看文后附录2例程)

    三、可爱又可怕的c语言。

    为什么又扯到 c 了?因为大多数遇到有符号还是无符号问题的朋友,都是c里面的 signed 和 unsigned 声明引起的,那为什么开头是从汇编讲起呢?因为我们现在用的c编译器,无论gcc 也好,vc6 的cl 也好,都是将c语言代码编译成汇编语言代码,然后再用汇编器汇编成机器码的。搞清楚了汇编,就相当于从根本上明白了c,而且,用机器的思维去考虑问题,必须用汇编。(我一般遇到什么奇怪的c语言的问题都是把它编译成汇编来看。)

    C 是可爱的,因为c符合kiss 原则,对机器的抽象程度刚刚好,让我们即提高了思维层面(比汇编的机器层面人性化多了),又不至于离机器太远 (像c# ,java之类就太远了)。当初K&R 版的c就是高级一点的汇编……:-)

    C又是可怕的,因为它把机器层面的所有的东西都反应了出来,像这个有没有符号的问题就是一例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举一例:

    #include <stdio.h> 
    #include <string.h>

    int main()
    {
            int x = 2; 
            char * str = "abcd"; 
            int y = (x - strlen(str) ) / 2;
            
            printf("%d/n",y);
    }

    结果应该是 -1 但是却得到:2147483647 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时类型被自动转换了,结果自然出乎意料。。。
    观察编译后的代码,除法指令为 div ,意味无符号除法。
    解决办法就是强制转换,变成 int y = (int)(x - strlen(str) ) / 2; 强制向有符号方向转换(编译器默认正好相反),这样一来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,用有符号处理指令 imul ,idiv 等得到的结果,与用 无符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号无符号计算的问题,特别是存在讨厌的自动转换时,要倍加小心!(这里自动转换时,无论gcc还是cl都不提示!!!)


    为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。(完)

     

    附录
    1:顺便指出楼上那位同志错误的地方,《IA-32 Intel Architecture Software Developer’s Manual》第2卷PDF文档表述为“sign-extended imm8”,这个sign-extended 是符号扩展的意思,他理解成有符号数了。。。
    说说符号扩展:当操作数进行长度扩展时,既要让操作数变长又不能改变原数值,所以就出现了符号扩展一说。比如 movsx ax, 0xEC ,执行扩展后,ax的值为:0xFFEC,长度变长了,结果没变,都是 -20 。

    2:两套乘法指令结果例程

    ;; 程序存储为 x.s 
    ;;--start--------------------------------------------------------

    extern printf 
    global main

    section .data
            str1: db "%x",0x0d,0x0a,0 
            n: db 0x02
    section .text 
    main: 
            xor eax,eax
            mov al, 0xec
            mul byte [n] ;有符号乘法指令为: imul

            push eax
            push str1
            call printf 
            
            add esp,byte 4 
            ret 
    ;;--end---------------------------------------------------------------

    编译步骤:
    1. nasm -felf x.s 
    2. gcc x.o

    ubuntu7.04 下用nasm和gcc编译通过。结果符合文章所述。

    展开全文
  • (1)从外部扩展RAM中读取被乘数和乘数,乘数存放于0100H处开始,总共3个乘数,每个乘数是3字节的有符号数。被乘数存放于0200H处开始,总共3个被乘数,每个被乘数是3字节的有符号数。 (2)把结果储存到内部存储区,...

    问题:
    (1)从外部扩展RAM中读取被乘数和乘数,乘数存放于0100H处开始,总共3个乘数,每个乘数是3字节的有符号数。被乘数存放于0200H处开始,总共3个被乘数,每个被乘数是3字节的有符号数。
    (2)把结果储存到内部存储区,从40H开始的地址处。

    思路:
    汇编实现
    1.用DPTR双指针分别操作乘数和被乘数,R0存放的地址,存放结果,R2=3循环次数。
    2.先进行符号位的判断,如果为负数需要变成正数,在这里我编写了一个函数可以把负数变成正数,就可以分别把乘数和被乘数放入函数中判定。
    3.同时我设置了一个标志位R1,地址为0052H。若判定为负数,则加一,这样若两个数都为负数或者都为正数则末位为零,若其中一个为正数另一个为负数则末位为一。
    4.编写两字节无符号数相乘函数,送入变成正数的两字节数函数使之相乘。
    5.最后根据R1所在地址的数字末位为一还是零来确定最后结果的符号数。
    6.循环三次,得到三个乘数与三个被乘数乘积的结果。
    c语言实现详见代码
    汇编代码

    sfr AUXR =0x8e;
    ORG 0000H
    LJMP main
    ORG 100H	;定义主程序入口,主程序代码首地址
    main: 
    ;;;;;;;;;;;;;初始化,将被乘数与乘数输入到特定地址;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;    
    init:;初始化,将被乘数与乘数输入到特定地址
    	ORL AUXR,#00000010B ;选择片外扩展ram
    	;存放三位乘数
    	mov dptr,#0100h
    	mov a,#044h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0101h
    	mov a,#055h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0102h
    	mov a,#-055h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0103h
    	mov a,#044h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0104h
    	mov a,#033h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0105h
    	mov a,#022h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    
    	;存放三位被乘数
    	mov dptr,#0200h
    	mov a,#-78h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0201h
    	mov a,#078h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0202h
    	mov a,#-012h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0203h
    	mov a,#012h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0204h
    	mov a,#044h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    	mov dptr,#0205h
    	mov a,#056h
    	movx @dptr,a	   ;将a寄存器的存放的数字存放到dptr存放的地址中
    ;;;;;;;;;;;;;;;;初始化双dptr指针的首地址和存放结果的首地址;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    	AUXR1 equ 0xA2;等值指令在以后可以用auxr1代替0xA2
    	ORL AUXR1,#00000001B;切换数据指针
    	MOV DPTR,#0100H	 ;初始化DPTR1首地址
    	INC AUXR1;		   
    	MOV DPTR,#0200H	;初始化DPTR0首地址	  
    	MOV A,#0043H	;初始化结果的低位
    	MOV R0,A     ;将保存结果的最低位地址传递给R0
    	MOV R2,#03;循环次数
    	MOV R1,#0052H    ;存储一个标志位,表示最后的正负
    ;;;;;;;;;;;判断正负并且把负数变成正数储存到原来的位置;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    LOOP:		
    ;;;;;;;;;;;;;判断乘数的正负并把负数变为正数;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    	INC AUXR1;		   ;DPPTR1
    	MOVX A, @DPTR
    	MOV R6,A;   将最高位传入函数中判断正负
    	LCALL NEG;
    ;;;;;;;;;;;;;;;;;;;;;;;;判断被乘数的正负并把负数变为正数;;;;;;;;;;;;;;;;;;;;;;;;
    	INC AUXR1;		   ;DPTR0
    	MOVX A, @DPTR
    	MOV R6,A;将最高位传入函数中判断正负
    	LCALL NEG;
    
    ;;;;;;;;;;;;;将片外ram数据传送给r6r7,r4r5相乘;;;;;;;;;;;;;;;;;;;;;;;;
        INC AUXR1;		   ;DPTR1
    
    ; ;;;;;   将两个字节的数字放入R6R7中;;	 ;;;;;;
    	MOVX A,@DPTR;	;DPTR1
    	MOV R6,A;		;取高字节
    	INC DPTR;		;地址加一切换地址取低字节
    	MOVX A,@DPTR;
    	MOV R7,A;  取低字节  
     ;;;;将两个字节的数字放入R4R5中	;;	 ;;;;;
    	INC AUXR1;切换dptr指针 dptr0
    	MOVX A,@DPTR;
    	MOV R4,A;
    	INC DPTR;dptr存放的地址加一
    	MOVX A,@DPTR;
    	MOV R5,A;    
    	LCALL  MUL2BYTE	  ;调用两字节相乘函数
    
    ;;;;;;;;;;;;;;;确定最后的符号;;;;;;;;;;;;;;;;
    	 MOV A,@R1;
    	 JB ACC.0 ,NEGTIVE;若低位为1则转移说明为负数,调用函数把结果变为负数
    INIT1:
    ;;;;;;;;初始化下一次循环的地址;;;;;;;;;;;;;;;;;;;;;
    	 INC DPTR;DPTR0,将dptr指针加一为下一次循环做准备
    	 INC AUXR1;
    	 INC DPTR;DPTR1
    	 INC R0;
    	 INC R0
    	 INC R0
    	 INC R0
    	 INC R0;
    	 INC R0
    	 INC R0
    	 ;结果储存首地址更新
         DJNZ R2,LOOP;	乘数未取完则跳转到loop	,否则结束
     	 LJMP END1;结束
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
     ;两字节相乘函数  测试数据0FFFh*0FFFH,0123H*0132H,7548H*4568H,01H*01H,00H*00H
    MUL2BYTE:		 
    	;乘数低位与被乘数低位相乘
    	MOV A,R7
    	MOV B,R5
    	MUL AB
    	ADDC A,@R0;
    	MOV @R0,A;将低位存储
    	DEC R0	 ;R0中存放的地址减一
    	MOV A,B;
    	ADDC A,@R0;
    	MOV @R0,A;将高位储存	
    	;乘数高位与被乘数低位相乘
    	MOV A,R6   ;
    	MOV B,R5   ;
    	MUL AB	   ;
    	ADDC A,@R0  ; 本次的低位与上一位高位相加
    	MOV @R0,A  ; 结果保存到特定位置
    	DEC R0	 ;R0中存放的地址减一
    	MOV A,B
    	ADDC A,@R0  ;若有进位加上进位
    	MOV @R0,A;将高位储存
    	;乘数低位与被乘数高位相乘
    	MOV A,R7	 ;
    	MOV B,R4	 ;
    	MUL AB		 ;
    	INC R0	     ;
    	ADD A,@R0    ;
    	MOV @R0,A	 ;	
    	MOV A,B      ;
    	ADDC A,#0    ;
    	DEC R0
    	ADD A,@R0;	加上上一位高位,这里不需要ADDC因为进位信号上面已经加入
    	MOV @R0,A;
    	DEC R0;
    	MOV A,#0;
    	ADDC A,@R0;
    	MOV @R0,A;
    	INC R0;  在这里最高位加上进位信号,这是因为在下面操作A寄存器的时候进位信号可能会被清除
    	;乘数高位与被乘数低位相乘
    	MOV A,R6	 ;
    	MOV B,R4	 ;
    	MUL AB		 ;
    	ADD A,@R0; 本次相乘低位与上次相乘高位相加
    	MOV @R0,A
    	MOV A,B
    	DEC R0  ;
    	ADDC A,@R0;
    	MOV @R0,A;
    	CLR C;
    	RET
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    
    ;判断数字的正负并且把负数改成整数
    NEG:
    	CLR C;			   cy清零
    	MOV A,R6 ;
    	JB ACC.7,CHANGE;	JB以直接寻址的位转移指令,如果bit==1则转移 ,否则顺序执行
    	RET
    ;将负数改成正数,因为51中储存负数是用补码进行存储的因此需要转变为原码,减一取反
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    CHANGE:
    	INC @R1  ;
    	MOV A,R6 ;
    	SUBB A,#01H ;
    	MOV R6,A  ;  减一
    
    	MOV A,R6
    	CPL A    ;
    	MOV R6,A ;
    	;取反
    	MOV A,R6;
    	MOVX @DPTR,A;将数据传送回原来的地址
    	RET		  
    
    ;;;;;;;;判断为负数加负号(将高位变成1);;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;		
    NEGTIVE:
    	MOV A,@R0;
    	ORL A,#10000000B
    	MOV @R0,A
    	INC @R1;  将标志位位清除为0;
    	LJMP  INIT1;因为在这里不是用lcall调用的函数因此需要用LJMP返回去。	
    END1:
    END
    

    c语言代码

    #include <STC12C5A60S2.H>
    //定义三个乘数的地址
    xdata int a1 _at_  0x0100;
    xdata int a2 _at_  0x0102;
    xdata int a3 _at_  0x0104;
    //定义三个被乘数的地址
    xdata int b1 _at_  0x0200;
    xdata int b2 _at_  0x0202;
    xdata int b3 _at_  0x0204;
    main()
    {
    	int i;	
    	int xdata *a=0x100;	 //定义指针变量访问乘数
    	int xdata *b=0x200;	 //定义指针变量访问被乘数
    	long data  *c=0x40;	 //	定义指针变量访问结果,
    	//因为int*int有可能超多int的值域因此定义long类型
    	a1=0x1122; a2=-0x7fff; a3=-0x7fff;  //为乘数赋值 
    	//这里因为最高一位为符号位因此乘数范围为-0x7fff~0x7fff
    	b1=0x4455; b2=0x7fff; b3=-0x7fff;  //被乘数赋值
    	for(i=0;i<3;i++)
    	{
    		*c=(long)(*a)*(long)(*b);	//乘数与被乘数相乘,
    		//这里用了强制类型转化,都变成long类型相乘
    		c++;
    		a++;
    		b++; //乘完之后各地址自增,按照自己数据长度自增;
    		//比如b,c一次自增两个字节,c自增四个字节;
    	}
    	return 0;
    
    }
    
    
    展开全文
  • 汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符号然后用两个标准来处理...
     
    

    一、只有一个标准!

    在汇编语言层面,声明变量的时候,没有 signed   和   unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分有符号还是无符号然后用两个标准来处理,它统统当作有符号的!并且统统汇编成补码!也就是说,db -20 汇编后为:EC ,而 db 236 汇编后也为 EC 。这里有一个小问题,思考深入的朋友会发现,db 是分配一个字节,那么一个字节能表示的有符号整数范围是:-128 ~ +127 ,那么 db 236 超过了这一范围,怎么可以?是的,+236 的补码的确超出了一个字节的表示范围,那么拿两个字节(当然更多的字节更好了)是可以装下的,应为:00 EC,也就是说 +236的补码应该是00 EC,一个字节装不下,但是,别忘了“截断”这个概念,就是说最后的结果被截断了,00 EC 是两个字节,被截断成 EC ,所以,这是个“美丽的错误”,为什么这么说?因为,当你把 236 当作无符号数时,它汇编后的结果正好也是 EC ,这下皆大欢喜了,虽然汇编器只用一个标准来处理,但是借用了“截断”这个美丽的错误后,得到的结果是符合两个标准的!也就是说,给你一个字节,你想输入有符号的数,比如 -20 那么汇编后的结果是正确的;如果你输入 236 那么你肯定当作无符号数来处理了(因为236不在一个字节能表示的有符号数的范围内啊),得到的结果也是正确的。于是给大家一个错觉:汇编器有两套标准,会区分有符号和无符号,然后分别汇编。其实,你们被骗了。:-)

    二、存在两套指令!

    第一点说明汇编器只用一个方法把整数字面量汇编成真正的机器数。但并不是说计算机不区分有符号数和无符号数,相反,计算机对有符号和无符号数区分的十分清晰,因为计算机进行某些同样功能的处理时有两套指令作为后备,这就是分别为有符号和无符号数准备的。但是,这里要强调一点,一个数到底是有符号数还是无符号数,计算机并不知道,这是由你来决定的,当你认为你要处理的数是有符号的,那么你就用那一套处理有符号数的指令,当你认为你要处理的数是无符号的,那就用处理无符号数的那一套指令。加减法只有一套指令,因为这一套指令同时适用于有符号和无符号。下面这些指令:mul div movzx … 是处理无符号数的,而这些:imul idiv movsx … 是处理有符号的。
    举例来说:
    内存里有 一个字节x 为:0x EC ,一个字节 y 为:0x 02 。当把x,y当作有符号数来看时,x = -20 ,y = +2 。当作无符号数看时,x = 236 ,y = 2 。下面进行加运算,用 add 指令,得到的结果为:0x EE ,那么这个 0x EE 当作有符号数就是:-18 ,无符号数就是 238 。所以,add 一个指令可以适用有符号和无符号两种情况。(呵呵,其实为什么要补码啊,就是为了这个呗,:-))
    乘法运算就不行了,必须用两套指令,有符号的情况下用imul 得到的结果是:0x FF D8 就是 -40 。无符号的情况下用 mul ,得到:0x 01 D8 就是 472。

    三、可爱又可怕的c语言。

    为什么又扯到 c 了?因为大多数遇到有符号还是无符号问题的朋友,都是c里面的 signed 和 unsigned 声明引起的,那为什么开头是从汇编讲起呢?因为我们现在用的c编译器,无论gcc 也好,vc6 的cl 也好,都是将c语言代码编译成汇编语言代码,然后再用汇编器汇编成机器码的。搞清楚了汇编,就相当于从根本上明白了c,而且,用机器的思维去考虑问题,必须用汇编。(我一般遇到什么奇怪的c语言的问题都是把它编译成汇编来看。)

    C 是可爱的,因为c符合kiss 原则,对机器的抽象程度刚刚好,让我们即提高了思维层面(比汇编的机器层面人性化多了),又不至于离机器太远 (像c# ,java之类就太远了)。当初K&R 版的c就是高级一点的汇编……:-)

    C又是可怕的,因为它把机器层面的所有的东西都反应了出来,像这个有没有符号的问题就是一例(java就不存在这个问题,因为它被设计成所有的整数都是有符号的)。为了说明c的可怕特举一例:

    #include <stdio.h>
    #include <string.h>

    int main()
    {
    int x = 2;
    char * str = "abcd";
    int y = (x - strlen(str) ) / 2;

    printf("%d\n",y);
    }

    结果应该是 -1 但是却得到:2147483647 。为什么?因为strlen的返回值,类型是size_t,也就是unsigned int ,与 int 混合计算时类型被自动转换了,结果自然出乎意料。。。
    观察编译后的代码,除法指令为 div ,意味无符号除法。
    解决办法就是强制转换,变成 int y = (int)(x - strlen(str) ) / 2; 强制向有符号方向转换(编译器默认正好相反),这样一来,除法指令编译成 idiv 了。我们知道,就是同样状态的两个内存单位,用有符号处理指令 imul ,idiv 等得到的结果,与用 无符号处理指令mul,div等得到的结果,是截然不同的!所以牵扯到有符号无符号计算的问题,特别是存在讨厌的自动转换时,要倍加小心!(这里自动转换时,无论gcc还是cl都不提示!!!)


    为了避免这些错误,建议,凡是在运算的时候,确保你的变量都是 signed 的。(完)

    展开全文
  • 汇编符号与无符号数以及CF,OF标志位的区分

    万次阅读 多人点赞 2016-05-01 20:04:20
    汇编符号与无符号数以及CF,OF标志位的区分
  • 汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符号然后用两个标准来处理...
  • 汇编程序16位带符号变量计算

    千次阅读 2017-11-27 17:49:04
    用16位指令编写完整的程序,并上机测试,计算W=(x+y)*z,其中所有变量均为16位带符号变量。注意伪指令的使用。data segment x db 7 y db -3 z db 2 w db ? data ends;定义数据段,其中X,Y,Z为一个字节,V为16位结果...
  • 三个排序,可用三个变量之间相互比较,按大小要求排序。 C语言伪代码: if(a>b) /*如果a大于b,借助中间变量t实现a与b值的互换*/ { t = a; a = b; b = t; } if(a>c) /*如果a大于c,借助中间变景t实现a...
  • 汇编语言的符号、标号和变量符号

    千次阅读 2018-06-13 22:02:07
    转自:https://blog.csdn.net/benny5609/article/details/2470132#commentBox计算机... 在宏汇编语言中所有变量名、标号名、记录名、指令助记符和寄存器名等统称符号.这些符号可通过汇编控制语句的伪操作命令重新命...
  • 汇编实现无符号数比较转移

    千次阅读 2018-12-24 14:48:17
    题目:比较字节型无符号数,较大的赋值给z 代码: .model small .stack .data x db 1 y db 2 z db '?' .startup mov al,x cmp al,y jbe xly ;无符号低于等于 mov z,al jmp ok xly: mov al,y mov z,al ok: .exit 0...
  • 符号数据进行取补码的的...将内存中以BUF为首址的100个字节单元用原码表示的有符号数依次编程用补码表示的有符号数 ;依次放在原100个字节单元中 data segment buf db 200 dup(?) ;定义200个一字节的空间 用...
  • 有符号数与无符号数的加减法

    万次阅读 多人点赞 2017-04-24 14:41:58
    1、符号数与无符号数的人为规定性:一个数是符号还是无符号数都是人为规定的。进行二进制运算时用无符号数或是补码运算时,结果都是正确的。10000100+00001110 若规定为无符号数,即 132+146=146D 。若规定为...
  • 有符号数和无符号数

    千次阅读 2018-10-01 22:31:55
    专题:汇编语言中有符号数还是无符号数? 一、汇编语言在存储方面讲,有符号数和无符号数存储形式是一致的。 l 数据在内存或CPU的寄存器中都是按照有符号数的二进制补码方式存储的。  例如(在debug中测试): ...
  • 汇编两个8位无符号数相加

    千次阅读 2015-03-07 22:27:20
    两个8位无符号数相加 ;在8086/8088CPU中,只有8位或16位运算指令,没有32位和64位以上的运算指令 ;要进行64位加法运算,可以利用16位加法指令分别相加4次来实现 ;这个问题比较简单,无须画程序流程图 ;该汇编程序的...
  • 汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符
  •   在汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符号然后用两...
  • 这个问题我在学习汇编语言的过程中一直很苦恼,在网上看了很多帖子,基本上都是说是人自己设定是有符号数还是无符号数的。这样的回答是很扯淡的,TM计算机和人脑又不是一个东西,看来很多人学东西基本上都是一知半解...
  • 汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符号然后用两个标准来处理,...
  • 有符号数和无符号数探讨

    千次阅读 2007-12-30 19:09:00
     在汇编语言层面,声明变量的时候,没有 signed 和 unsignde 之分,汇编器统统,将你输入的整数字面量当作有符号数处理成补码存入到计算机中,只有这一个标准!汇编器不会区分符号还是无符号然后用两个标准来...
  • 机器如何识别有符号数和无符号数? 机器为了简化有符号数的操作引入了补码表示法(大多数PC都这样表示),运算中连同符号位一起运算,而ADD,SUB通常并不区分有符号数和无符号数,而是通过FLAGS中的...
  • C - 有符号数和无符号数扩展

    千次阅读 2018-11-28 11:47:06
    C语言标准要求先进行数据...将有符号数转换为更大的数据类型需要执行符号扩展,规则是将符号位扩展至所需要的位数。扩展的原则是: 符号的数据类型,在向高精度扩展时,总是带符号扩展 无符号的数据类型,在向高...
  • 汇编中的变量属性

    千次阅读 2015-03-31 22:10:53
    变量有两种属性:地址属性和类型属性。 地址属性:变量名就是第一个变量的逻辑地址,包括段基地址和偏移地址。 类型属性:说明了变量是哪种类型,是BYTE,WORD等等。 一:地址操作符:访问变量的地址属性 [ ] ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 48,705
精华内容 19,482
关键字:

有符号数变量汇编