# 整数加减运算

数据的表示与存放问题解决了。这些数据要参与运算，冯诺依曼计算机的特征是所有信息都是二进制编码，所以运算也是基于01完成的，这部分研究如何构建运算电路。

## 3.1 布尔代数和基本逻辑电路

### 3.1.1

关于01的一套数学运算体系起源于1850年前后英国数学家乔治布尔，所以就叫做布尔代数了。

最基本的逻辑运算：与或非，任一逻辑表达式都可以写成这种关系，比如异或也是常用的逻辑运算。

基本的门电路与或非门，其他的门电路可以用这三种基本门电路实现。

对于n位逻辑运算，重复来n个相同逻辑门就行，实现按位与或非。

组合逻辑电路无存储功能，输出取决于当前输入。时序逻辑电路有存储功能，除了当前输入，也受到存储单元状态的影响。

可以使用基本逻辑门组成有特定功能的组合逻辑部件，如译码器、编码器、多路选择器、加法器。

可以使用基本的数字电路设计方法实现出组合逻辑部件

* 真值表描述功能部件的输入和输出关系
* 根据真值表确定逻辑表达式
* 根据逻辑表达式实现逻辑电路

比如多路选择器，最基本的是二选一，很容易实现。选择端的位数是和输入端的个数相关的。

### 3.1.2 加法器

一位加法器，全加器，两个加数AB，低位进位Cin，和为F，向高位进位为Cout

实现出来之后封装好，就是一个模块化的全加器。

1bit加法器可以实现nbit加法器，进位串起来就可以。

加法实现以后，其他所有算术运算部件都可以基于加法器和逻辑运算来实现，因此所有的算术运算都可以基于01以及逻辑运算实现。这是个计算机设计时很重要的基础

n位的无符号数加法器无法用于带符号数想加，无法判断溢出，比较大小也无法实现。比较通常通过做减法来实现，减法用补码采用加法器实现。

因此加法器除了基本功能，还需要输出一些标志信息。

* OF溢出，Cn异或Cn-1
* SF符号标志，最高位就行
* ZF全为0，或非
* CF进位借位标志，Cout异或Cin

至此我们可以把这个东西叫做ALU了，功能比较基础

### 3.1.3 整数加减和ALU

```c
int x = 9,y = 6,z1,z2;
z1 = x + y;
z2 = x - y;
```

来分析以下变量的机器数。

要能实现这些东西，要求机器的硬件可以实现补码的表示，以及和的补码和差的补码的实现。

$$\[x]\_{补} = 2^n + X$$

根据这个定义，可以推导一些补码运算的公式。

分析到此，在电路上，需要多设计一个取反电路，二路选择器实现原码还是补码，

在上面的基础上，就构造了一个可以实现整数加减法的运算部件。在此基础上，再加上寄存器，移位器，就可以实现ALU乘除运算等功能了。

ALU叫做算术逻辑部件，里面的核心部件就是整数加减运算。

ALU输出的结果，由ALUop控制端控制，控制端的位数由ALU可以做的操作的个数决定。比如8种操作就需要3bit的op控制线，ALUop的编码对应了不同的操作。

ALU是CPU里的器件，CPU里的控制器送来的就是ALU的控制编码，输出端获得结果以及标志位

## 3.2 C表达式到逻辑电路

C基础：基本数据类型；基本运算类型+-\*/%<>==，位操作，逻辑运算，移位，扩展和截断

高级语言的运算表达式距离机器还有些距离，来看看是怎么实现的。

比如y = (x>>2) + k如何在计算机里实现。

任何高级语言的表达式，都要转换为指令序列。

上面这个式子

sarw $2, %ax ;x>>2 值在ax寄存器里 addw %bx, %ax ;k的值在bx

计算机直接执行指令来完成运算，控制器对指令移码，产生控制信号送运算电路。

操作数在运算电路里运算

sarw $2, %ax 将操作数2和R\[ax]送移位器运算 addw 把两个寄存器送整数加法器中运算

可以看出，高级语言表达式到最终逻辑门电路中间是通过指令作为媒介。高级语言转换成指令，指令能直接在硬件上执行，指令最终靠逻辑门实现。

因此高级语言里的运算一定要由相应的指令来实现。

## 3.3 C语言里的运算

* 算术运算
  * 无符号数
  * 带符号整数
  * 浮点数的+-\*/%
* 位运算
  * 用来对位串实现掩码操作或相应的其他处理
  * 操作
    * |
    * &
    * \~
    * ^
* 移位运算
  * 用来提取信息，扩大缩小特殊倍
  * << >>
  * 语法层次上未设计逻辑移位还是算术移位，在指令层次上有区别，由数据类型确定

比如从数据y中提取低位字节，使用&实现掩码操作：y&0x00ff，就得到了低字节。

移位会有数据丢失或溢出的问题。

* 逻辑运算
  * 用于关系表达式的运算
  * ||
  * &&
  * !

区别按位操作。

还有一类没有运算符的运算，通常在类型转换嗯时候用到，位扩展和位截断运算，没有专门的操作运算符，都是隐含操作。

* 位扩展，char转int
* 位截断，int赋值给char

## 3.4 加减运算的实现

### 3.4.1 整数加减运算

按照前面ALU的思路，最核心的是加法器。加减法运算都用加法来处理，加法器加上额外的电路能实现减法运算。

加法器本身并不知道是否是带符号数，只管加，并且也不判断对错，只是给低n位作为结果，生成标志信息。至于结果是否正确，是靠软件实现的。

运算时候的条件码（标志位）专门记录到寄存器里，状态字寄存器，或者标志寄存器，如IA32里的EFLAGS寄存器。

这些标志可以在if语句里用作条件转移的判断。

在做加法运算时，主要判断是否溢出，无符号加溢出条件CF=1，带符号数溢出条件OF=1

如果是做减法，

### 3.4.2 整数减法举例

一个简单的C语言程序，加法部件假定位数为8。

```c
#include <stdio.h>

typedef unsigned char uint8_t;
typedef signed char int8_t;

int main()
{
    uint8_t x = 134;  /* 机器码 0x86 0b1000 0110 */
    uint8_t y = 246;  /* 机器码 0xf6 0b1111 0110 */

    int8_t m = x;     /* 机器码 0x86 0b1000 0110 输出-122 */
    int8_t n = y;     /* 机器码 0xf6 0b1111 0110 输出-10  */

    uint8_t z1 = x - y; /* 0b1000 0110 + 0b00001001 + 1 = 0b1001 0000 */
    uint8_t z2 = x + y; /* 0x86+0xf6=0x17c 输出124 */

    int8_t k1 = m - n;
    int8_t k2 = m + n; /* 0x86+0xf6=0x17c 输出124 */

    printf("x: %d (%x %b)\n",x,x,x);
    printf("x: %d (%x %b)\n",y,y,y);

    printf("m: %d (%x %b)\n",m,m,m);
    printf("n: %d (%x %b)\n",n,n,n);

    printf("x-y: %d (%x %b)\n",z1,z1,z1);
    printf("x+y: %d (%x %b)\n",z2,z2,z2);

    printf("m-n: %d (%x %b)\n",k1,k1,k1);
    printf("m+n: %d (%x %b)\n",k2,k2,k2);
    return 0;
}

```

控制台输出结果：

```
x: 134 (86 10000110)
x: 246 (f6 11110110)
m: -122 (ffffff86 11111111111111111111111110000110)
n: -10 (fffffff6 11111111111111111111111111110110)
x-y: 144 (90 10010000)
x+y: 124 (7c 1111100)
m-n: -112 (ffffff90 11111111111111111111111110010000)
m+n: 124 (7c 1111100)

进程已结束,退出代码0
```

c语言里=赋值运算符是直接把机器码等过去。

从这个例子可以看出来，ALU只是机械的对机器码作运算，并不管结果的对错，结果对错需要程序判断。这些程序上运算结果的异常背后都是数字电路上的实现逻辑。

程序里的x-y运算，送到了ALU这边，有个问题是怎么求一个数负数的补码，直接全部（符号位也取反）按位取反就可以轻松转换成加法，sub指令相当于数据选择器选择原来的数据输出还是说取反输出，顺手输入到Cin即按位取反再加1，这个硬件上的设计还是挺有意思的。

有了这些基础，再来看看如何用程序判断一个无符号数相加有无溢出


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://embedded.xym.work/1_2_csapp/3-jia-jian-yun-suan.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
