tags
type
status
date
slug
summary
category
password
icon

#2 Bits-Bytes-Ints

#Bit-wise Operation

C语言中,所有“整型”都可以进行按位运算,运算符包括:& | ^ ~ 。在进行这类运算的时候,把数看成一个”bit的向量“,而不管其数学含义
注意以上的四种运算符与&& || !不同!后者的结果只能是0x00和0x01

#Shift Operations

左移直接在右边补0。
右移分为两种:逻辑右移无论如何都在左边补0,算术右移在左边的补位与移动前最高位相同(这样无论对正数还是负数,都相当于在补0)

#Integers

无符号数Unsigned Int:没有负数
有符号数Signed Int:补码最高位的权重为
notion image
notion image
在代码中类型转化的时候,内存里的二进制数是不变的,改变的只是我们如何解读它们
如在C中,unsigned与signed比较时,大家会一起转成unsigned,于是会出现-1>0U的情况

extension

左边补符号位,这样不改变所代表的数值

trunction

直接去掉最高位,有信息损失

#Word Size & Byte Ordering

计算机中以字节Byte为单位。
其基本整数的比特数称为字长word size
显然,一个word包含多个Byte,计算机如何定义多个字节的高低位顺序也可能不同
从运算的角度,应该把低位放在前面,这样从前到后处理,符合运算顺序,这也是x86、ARM在用的Little Endian
然而互联网上采用Big Endian,从高位向低位传输字节。于是就需要网络驱动程序进行转换。

#3 Float

每个浮点数分为s+exp+frac三部分,
单精度三部分分别是1位、8位、23位;双精度分别是1位、11位、52

#符号位s

0表示正数,1表示负数。会出现+0和-0两个数

#Normalized

判据:当exp不是全0或全1
exp不是按照补码的方式解读的,而是按照无符号数算,再减去bias
,
此时M为1.frac,所以

#Denormalized

判据:exp全0
(而不是),与normalized的最大负指数相同
此时M为0.frac
这类数用于处理很靠近0的数

#Special

判据:exp全1
若frac全0,则为inf
若frac不全为0,则为nan

#Comparison

基本(除了inf和nan)可以把整个浮点数当做无符号整数来比较大小

#4-6 Machine Programming

Architecture(also ISA:instruction set architecture)规定指令要怎么写。典型的有:x86(电脑)、ARM(手机)、RISC V(开源)
机器语言Machine Code;汇编语言Assembly Code
Microarchitecture:如何在硬件层面实现这些指令
CISC(指令种类多,硬件功能多,代码密度高,如x86);RISC(指令种类少,硬件功能少,复杂指令用多条简单指令实现,如)
x86架构下的CPU型号(按时间顺序):8086、80286、386、486、奔腾Pentium、酷睿Core
Intel一度想利用自己的市场霸权地位推动64位架构(与原32位x86不兼容),但效果很差。而后IBM推出与原32位架构兼容的x86-64架构,抢夺Intel市场份额。随后Intel也推出了与其几乎一样的64位架构。
notion image
冯诺依曼结构一大特点:储存程序。命令写在内存里,“翻书”的动作写在书上
Register file:寄存器集群
PC:下一条命令的地址
Condition Codes:存储最近一次运算的一些Conditions

#x86-64 Integer Registers

notion image
从两字节的寄存器一步步扩展而来,ax(ah+al)→eax→rax,所以名字看起来有些奇怪
rsp是用于栈操作的寄存器,不能乱用

#Operations

movq Source, Dest移动数据
数据包括Immediate(可以理解为常数,$0x4)、寄存器中的数(%rax)、内存中处于寄存器给出的地址处的数(最简单的是(%rdx),还有更复杂的表达如0x80(,%rdx,2)
notion image
leaq Src, Dst 不访问内存而将算出的地址存于寄存器中
notion image

#Condition Codes

Implicily set隐式设置

结果全0时,ZF置1
有符号数运算结果为负数时,SF置1
无符号数溢出or从溢出位借位时,CF置1
有符号数溢出时,OF置1

Explicitly set显式设置

cmpq b,a 计算a-b,但不存到什么地方,只更新Condition Codes
testq b,a 计算a&b,同上
setx %xxx 根据Condition Codes设置某寄存器的1个字节(不会改变其他字节,所以经常用movzbl之类的指令将该寄存器的其他字节清零)
jx xxx 根据Condition Codes跳转

Conditional Branch

notion image

Conditional Moves

在前一种方法中,.L4前的代码能不能执行,要等到.L4才行,这样不好
我们可以把两种可能的运算都先做出来,最后再判断采用哪个值作为ret
notion image
但是这样也会有问题:
notion image

#Procedures调用

要解决的问题:
  1. 控制转移。如何跳转到调用函数的开头?如何跳回到调用语句?
  1. 数据传递。如何把参数传入调用函数?如何处理返回值?
  1. 内存管理。局部变量怎么放?在返回时怎么删除?
⇒Application Binary Interface(ABI)

Stack

因为程序语句存在内存里地址较小的地方,所以栈从地址较大的地方递减存储。即:栈底高位,栈顶低位。
%rsp储存栈顶的地址

Passing Control

%rip储存下一条要执行的命令的地址。跳转的命令实际上只是修改了rip。每个时针的上升沿都执行一次命令
pushq Src 将地址为Src处的指令压进栈中(%rsp-8)
popq Dest 将栈顶弹出,并存入Dest(%rsp+8)
call label 将return address(即返回后的下一条命令的地址)压入栈中,然后跳转到地址为label处的命令
ret 弹出栈顶的地址,存入rip,即下一条执行return address处的命令
例.
notion image
  1. %rip=0x400544,故执行callq 400550。因此把400549(return address)压入栈顶,%rsp变为0x118【16进制,与0x120差了8】,同时%rip变为400550
  1. %rip=0x400550,故执行mov %rdi,%rax。跳过中间一些命令,最后%rip变为400557
  1. %rip=0x400557,故执行retq。取出栈顶地址并赋值给%rip,%rip变为400549,%rsp变为0x120
  1. %rip=400549,故执行mov。后续略

Passing Data

返回值放在%rax中
前6个传入的参数放在6个寄存器中,更多的参数放在栈里

Stack Frames

在调用函数的时候,栈里面不仅要存return address,还要存超过6个的参数,以及一些local variables,所以这就需要提前分配栈空间。每个函数都有自己的Stack Frame
可以使用%rbp存上一个Frame的栈顶,方便恢复
寄存器的使用:caller saved即caller先存好,callee随便改;callee saved即如果callee要用,用完必须要恢复原状态
 
 
MicroeconomicsSystems & Signals