汇编语言中的加减法~~~~~~~~~困扰我很久了。。有高人吗

作者&投稿:司隶 (若有异议请与网页底部的电邮联系)
汇编语言中的加减法~

(加减指令,既作为无符号数影响标志位CF,AF,又作为有符号数影响标志OF,SF。想了半天终于搞清楚了,CF标志反映最高位的进借位情况,而OF反映结果是否溢出,这里的结果是目的操作数中的结果,进借位它是不管的。你这样想的原因是,考虑到CF为1,则肯定溢出了,但是目的操作数中只能保存溢出后的值,丢掉了最高位,所以就不一定溢出了。看例子:
MOV
AX,7896H;
ADD
AL,AH;
;执行完后CF=1,AL=0EH,但是OEH不溢出,所以OF=0;
后面一句也不对吧。)
以上是我个人的理解。
以下是复制别人的理解:
CF
比较简单吧,
没有什么特别的.
OF
表示的带符号数进行运算是否溢出.
96+78,
一个是正数,
一个为负数,
相加肯定是不会有溢出的.
OF
溢出只可能发生在两个相同符号的数值相加,
或者是不同符号数相减.
如果是从算法判断上来看,
如果是在运算时,
次高位向最高位和最高位向
CF
位产生进位情形一致的话,
OF=0;
否则
OF=1.
96
10010110
+78
01111000
10E
100001110
这里,
次高位和最高位都产生了进位,
情形一致,
所以
OF=0
16
00010110
+78
01111000
08E
10001110
这里,
次高位向最高位产生了进位,
而最高位没有向
CF
产生进位,
所以
OF=1
而且,
结果也是,
两个正数相加,
结果为负数了,
显然溢出
还有两种情况,
也是类似的.
一个是都没有产生进位,
那应该是两个小的正数相加,
结果还是正数,
没有溢出;
另外就是次高位没有产生进位,
但最高位产生进位了,
这应该是两个负数相加,
由于次高为位没有产生进位,
所以,
最高位,
也就是符号位结果为
0,
是正数,
显然也是溢出了.

不是,是用被减数加上减数的补码
步骤(不是计算机计算的步骤,我想的步骤):
56H求补
01010110B各位取反+1
10101001B+1=10101010B
被减数+减数补码
10010000B+10101010B=[1]00111010B=3AH(最高位舍去)
都是再字节层次上做的运算,所以结果的最高位舍去了

我在你的令一个问题中回答了,但是你好像不太满意,我自己觉得也不是很清楚就仔细了解了下:

看下这篇文章 估计你就明白了:

文章开始:

这个问题要是考虑深了,还真有些东西呢,当然,要是简单的理解,那就很容易了。下面我就把这个东西尽量的扩展一点,深入一点和大家说说。

一、只有一个标准!

在汇编语言层面,声明变量的时候,没有 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编译通过。结果符合文章所述。

最后:
如果我有什么错误的地方请大家指正!
我的blog : http://blog.csdn.net/band_of_brothers/

文章结束

其实只要明白有符号数和无符号数因为补码的关系,导致加减出来的结果是一致的。计算机并不知道一个数有无符号,最好位是1还是0对它还说没任何意义,编程者来根据实际情况确定有无符号,从而选择合适的指令。所以加减指令,既作为无符号数影响标志位CF,AF,又作为有符号数影响标志OF,SF。编程者再跟具实际情况选择不同的标志位来判断。
CF 无符号数溢出
OF 有符号数溢出

计算机中的任何内容都是以二进制形式存储的,本无任何意义。之所以有ASCII码、BCD码、有符号数、无符号数等等概念,都是人赋予它们的,要看具体情况来具体判断和理解(解释)。

汇编语言中的四则运算(加减乘除),是要区分有符号数和无符号数的,指令相近,但不完全相同。

无符号数的四则运算指令:
+:add
-:sub
*:mul
/:div

有符号数的四则运算指令:
+:adc
-:sbb
*:imul
/:idiv

相应的标志位的判断也不一样,在比较指令cmp之后,通常跟有条件转移指令,这个条件转移指令要根据有符号数运算和无符号数运算来区分使用,若使用错了,就不会得到预想的结果。对应有符号数的条件转移指令用G(大于)、E(等于)、L(小于),如JG、JGE、JL、JLE、JNG、JNL等;对应无符号数的条件转移指令用A(高于)、E(等于)、B(低于),如JA、JAE、JB、JBE、JNB、JNA等。主要是根据进位标志位(CF)来判断。若CF=1,用JC指令判断;若CF=0,用JNC判断。假发、减法所用判断指令是一样的。OF使用场合不多。

关于补码,请参见下方的参考资料。

首先要掌握“使用补码”的意义。

特性:作加法,加多了,就可以呈现出减法的作用。

目的:用加法器代替减法器,简化硬件。

比如两位十进制,-1 可以用 +99 代替。

   24 - 1 = 23

   24 + 99 = (1)  23

舍弃进位,只取两位的结果,那么,减法就可用加法代替了。

你要注意了,进位,必须舍弃。

谁要讨论补码运算的进位,就是自寻烦恼!

--------------

八位二进制:0000 0000~1111 1111。

相当于十进制:0~255。

此时,-1 就可以用 1111 1111(255) 代替了。

1111 1111(255),就是-1 的补码。

1111 1110(254),就是-2 的补码。

。。。 。。。

1000 0000(128),就是-128 的补码。

0~127,还是原来的正数。

--------------

那么,问题就来了:

正数+正数,如果超出了 127,结果,就是负数。

结果的符号不合理。 这就是溢出。

--------------

同理:负数+负数,超出了 255,进位再舍弃,就是正数了。

这也是溢出。

还有:正数-负数、负数-正数,也可能溢出。

能够产生溢出,只有这四种算法。

--------------

溢出,就是符号异常。

用二进制数的运算,来判断进位、溢出,书上都有例题。




...主函数在预设代码里已经写好,求加法减法的两个函数程序代码...
void plus(char *a ,char *b,char *c){ int d = (int)*a; int e = (int)*b; *c = (char)(d + e); }void minus(char *a, char *b, char *c){ int d = (int)*a; int e = (int)*b; *c = (char)(d - e);} ...

易语言的加减法怎么编写
.版本 2 .子程序 _标签1_鼠标左键被按下, 逻辑型 .参数 横向位置, 整数型 .参数 纵向位置, 整数型 .参数 功能键状态, 整数型 标签6.标题 = 到文本 (到数值 (编辑框2.内容) - 到数值 (标签5.标题))你那个叫 托盘。 百度:易语言 托盘 自己多看看 。。

四则运算之加减法--C语言编程
简单示例如下:程序在vc6.0编译通过,执行效果如下:

C语言 100以内加减法
怎么还问呢?不是已经帮你了吗?include<stdio.h> include<stdlib.h> include int jia(){int num1,num2,num;system("cls");srand(time(NULL));do { num1=rand()%100;num2=rand()%100;num=num1+num2;}while(num>100);printf("%d+%d=",num1,num2);return num;} int jian(){int...

C语言编写加减法自测程序
代码如下,测试过了。include <stdio.h>#include <stdlib.h>#include int right=0,wrong=0;void fun(int i,int number1,int number2,char sign){int value=-1;if(sign=='+'){printf("第%d题:%d%c%d=",i,number1,sign,number2);scanf("%d",&value);printf("\\n");if(value==nu...

超级大的整数加减法,输入时要有正负号。希望能写一下代码。c语言
include <stdio.h>#include <string.h>#include <malloc.h>const int MAXSIZE = 122;\/\/ 完成以字符串形式的两个大数相加。返回字符串形式的和。\/\/ tatol ← addnum1 + addnum2char *LargeNumberAdd(char const *addnum1,char const *addnum2, char *total) {int i,j,k = 0,len,...

c语言 复数的加减法 满足手写规则
输入输出样例里面的分号 ,不确定是真会输出,还是只是分隔输入输出。常见的应该没有分号。输入1+i 2输出3+i 这个程序,麻烦在于如何将输入的数据,转为复数。可以先将输入 存为两个字符串,对每个字符串进行解析,转为复数。对于每个字符串,先查找是否存在i,这个很简单,如果有,那么一定是最后一个...

C语言用头文件实现复数加减法
1、_Complex是一种新增的数据类型,用来表示复数。C99 新增了复数类型(_Complex)和虚数类型(_Imaginary)。简单来说,C99 提供了三种复数类型:float _Complex,double _Complex,和 long double _Complex。对于 float _Complex类型的变量来说,它包含两个 float类型的值,一个用于表示复数的实部(real...

10以内的加减法口诀
加减法口诀的优点:1、简化计算过程 加减法口诀可以帮助孩子们快速地计算加减法,而不必每一步都使用手指或者其他工具进行辅助计算。这种方法不仅提高了计算的准确性,而且简化了计算过程,让孩子们可以更轻松地完成数学题。2、提高记忆能力 加减法口诀通常具有押韵和儿童化的语言特点,这使得孩子们更容易...

还请教一下,在LUA语言中如何计算加减法运算?
Lua 作为一个 解释型脚本 语言 可以非常容易的 动态执行一些代码(也就是根据动态获取的字符串作为代码去编译)。这样,很容易写出 一个 简单的输入型计算器。local function main () io.write("Please input a numeric expression: ") local msg = io.read() f = loadstring("return...

相山区13812124341: 汇编加减法的运算指令,最好带上几个例子,用汇编语言,急需 -
典军复方: x db ? y db ? z db ? mov al,x sub al,x mov z,al 功能z=x-y

相山区13812124341: 汇编语言中的加减法 -
典军复方: (加减指令,既作为无符号数影响标志位CF,AF,又作为有符号数影响标志OF,SF.想了半天终于搞清楚了,CF标志反映最高位的进借位情况,而OF反映结果是否溢出,这里的结果是目的操作数中的结果,进借位它是不管的.你这样想的原因...

相山区13812124341: 汇编语言中的减法 -
典军复方: 无符号数减法: SUB AX,BX 结果AX=AX-BX有符号数减法: SBB AX,BX 结果AX=AX-BX两者都影响进位标志.补充回答:后者是正数,减法的时候要把它转换成相反数的补码,然后两者作加法运算.

相山区13812124341: 汇编语言(8086)最简单加减乘除:定义A,B 使C=A+B D=A - B E=A*B F=A/B 符号有DB MOV ADD DATA等汇编语言(8086)最简单加减乘除:定义A,B 使... -
典军复方:[答案] DATA SEGMENTA DB 0AHB DB 0BHC DW D DW E DW F DW DATA ENDSCODE SEGMENTASSUM CS:CODE,DS:DATABEGIN:LDS SI,ALES DI,CLODSB;+MOV CL,ALCBWMOV BX,AXMOV AL,[SI]CBWADD AX,BXSTOSW;-MOV AL,...

相山区13812124341: 汇编语言中的加减法~~~~~~~~~困扰我很久了..有高人吗 -
典军复方: 我在你的令一个问题中回答了,但是你好像不太满意,我自己觉得也不是很清楚就仔细了解了下: 看下这篇文章 估计你就明白了: 文章开始: 这个问题要是考虑深了,还真有些东西呢,当然,要是简单的理解,那就很容易了.下面我就把这个...

相山区13812124341: 用汇编语言写出加减乘除的算法?
典军复方: 楼主,你用是什么的汇编啊.是8088/8086,还是单片机的,是什么公司的产品,什么型号的,它们都是有一些差异的.我在这就说一下思路吧.用汇编做加减法比较容易,带进位不带进位的都可以,做乘除是比较难的,一般是不用它做的,必...

相山区13812124341: 关于王爽的汇编语言的加减乘除指令这是汇编的加法指令!我想问下减法 除法 乘法 或者其他算数运算是怎么表示的呢? -
典军复方:[答案] 汇编算术运算指令 8086的算术运算类指令能够对二进制或十进制(BCD码)数进行加、减、乘、除运算,操作数的数据形式可以是8位或16位的无符号数或带符号数.对于单操作数指令,不允许使用立即数形式;对于双操作数指令,只有源操作可以...

相山区13812124341: 一个很简单的计算加减法的汇编程序,急求~
典军复方: ;已实现,下面是完成程序,其中加减比较简单,关键是将结果输出,注释在程序中穿插code segment assume cs:codestart: mov bx,166 mov ax,55 mov cx,44 add bx,ax ;166+55存于BX中 add ax,cx ;55+44存于AX中 sub bx,ax ;结果存于BX中 ...

相山区13812124341: 求帮助用汇编语言编写32位整数的加法或浮点数减法的完整程序! -
典军复方: 假设加数存在N开始的寄存器,被加数存在M开始的4个寄存器.结果返回N开始的寄存器和C. DBADD:MOV R0,#NMOV R1,#MMOV R2,#4CLR C LOOP:MOV A,@R0ADDC A,@R1MOV @R0,AINC R0INC R1DJNZ R2,LOOPRET

相山区13812124341: 汇编语言程序设计 加减乘除 -
典军复方: data segment data1 dw 20H data2 dw 30H addResult dw ? subResult dw ? mulResult dw ? divResult dw ?data endsstack1 segment para stack dw 20H dup(0)stack1 endscode segment assume cs:code,ds:data,ss:stack1begin: move ax ,data move ...

本站内容来自于网友发表,不代表本站立场,仅表示其个人看法,不对其真实性、正确性、有效性作任何的担保
相关事宜请发邮件给我们
© 星空见康网