我正在学习嵌入式系统入门课程。在阅读的过程中,我遇到了一个关于进位位和溢出位实现的有趣问题。
我知道什么是进位位和溢出位,但是我想不出有人会使用进位位的情况。我认为的一个原因是对齐内存。有人可以阐明这个问题吗?
进位标志对于有效地对比处理器的累加器或寄存器更宽的数据执行算术和逻辑运算很有用。这在现代 64 位处理器上可能不是问题,但早期的微处理器和一些当前的微控制器可能仍然只有一个 8 位或 16 位累加器。进位位将允许使用单个累加器对任何多字长度进行加/减和移位/旋转。除了基本的加、减、移位和循环指令(开始对多字数据的操作)外,还有进位加法、借位减法、进位移位和进位旋转指令(对后续单词进行操作)。并且为了促进这样的代码序列,INC reg
和DEC reg
指令(用于指针和循环计数器修改)不会修改(因此保留)进位标志,即使它们是算术指令。
一些微控制器(例如 Intel 8051)也使用进位标志作为其单位端口 I/O 操作的读取目标或写入源。
进位和溢出标志(可能还有一些其他标志,例如半进位、符号或零标志,取决于处理器架构)在各种算术和逻辑运算中设置或清除。应该咨询处理器的指令集以了解存在哪些标志以及指令修改它们的条件。
进位位在机器指令中用于测试无符号变量的小于/大于条件,例如,或循环终止条件,因为从 0 中减去 1 会导致设置进位位。
同样在汇编语言中,算术运算(如加法)的进位可用于轻松实现任意精度算术。
进位位在高级语言中对程序员来说通常是不可见的。
溢出位很少使用——但正如语义所示,它可以并且应该被科学算法使用,其正确性必须在指定的有符号整数范围内得到保证。如果处理有符号整数的算法产生正确的结果add reg1, reg2; JO exception_handler
,则可以进行有效的测试。同样适用于减法和整数乘法 (IMUL),但不适用于整数除法,之后所有(算术)标志都未定义。
下面是一个使用进位标志的简单代码示例:
int main (void)
{
unsigned int smallnum;
unsigned int largenum;
unsigned int temp_num;
printf("Enter a number: ");
scanf("%d", &smallnum);
printf("Enter a bigger number: ");
scanf("%d", &largenum);
temp_num = smallnum - largenum;
if (smallnum < largenum)
{
printf("Carry Flag SET!");
}
else
{
printf("Carry Flag CLEAR!");
}
return(EXIT_SUCCESS);
}
如果我们查看列表文件,我们会看到以下内容:
48:carry.c **** if (smallnum < largenum)
82 .loc 1 48 0
83 0090 8B542418 movl 24(%esp), %edx
84 0094 8B442414 movl 20(%esp), %eax
85 0098 39C2 cmpl %eax, %edx
86 009a 730E jae L2
So the if
statement gets compiled to a compare of the two operands followed by a jae
or "Jump if Above or Equal". The test used in a jae
command is to check if the Carry Flag is equal to 0
. See this reference to see which flags get tested for which conditional jumps.
When you're writing code, generate a listing file and look at all the conditional jumps. Lots of them are testing to check the state of the Carry Flag.