跳到内容

低级编程

客观的

接触到经典计算机如何工作的基本知识。

请注意

这个模块的内容不适用于量子计算机,但是理解当今的计算机如何工作的详细信息是有用的,因为它显示出在软件开发中,我们认为是理所当然的。,很多最基本的经典计算机的功能尚未量子计算机上可用,如ALU或RAM。

机器指令

在前面的小节中,我们谈论了一些关于如何经典计算机是专门解决方程表示为一个二进制序列逻辑门。如果我们有一组功能完整的盖茨,我们可以代表任何函数。

为了演示,假设我们想要添加两个数字,A和B,在一起。为简单起见,假设A和B都是微不足道的大端字节整数:

\ [\ displaylines {A = A_1 A_0 \ \ ~ \ \ B = B_1 B_0} \]

我们想要找到它们的和年代,这可能是一个三位大端字节整数:

\ [S = S_2 S_1 S_0 \]

例如,如果= 10 \ \ ()\ (B = 11 \),然后\ (\ S = 101)。用手,我们开始通过增加每个号码的最后两位(\ (A_0 + B_0 \))。如果他们都是零,结果是零。如果是一个,另一个是零,总和是1。如果都是1,总和是零,我们需要“套利”一到下一个数字。

这是一个基于输入这些值的真值表:

\ (A_0 \) \ (B_0 \) \ (S_0 \) 执行
0 0 0 0
0 1 1 0
1 0 1 0
1 1 0 1

足够方便,这两个输出(金额和携带),有相同的真值表作为XOR和一个与门,分别是:

\ [\ displaylines {S_0 = A_0 \ oplus B_0 \ \携带\:= A_0 \:{和}\ \文本:B_0} \]

这就是所谓的half-adder电路。现在,在接下来的两位,我们必须把它们相加并添加移位。这是一个三位操作,和我们没有任何三大门!我们要做的是微不足道的盖茨的序列。我们知道我们需要在三位和生产两个(数目和移位),所以我们可以建立一个真值表这个序列是一样的half-adder但额外携带输入:

带在 \ (A_1 \) \ (B_1 \) \ (S_1 \) 执行
0 0 0 0 0
0 0 1 1 0
0 1 0 1 0
0 1 1 0 1
1 0 0 1 0
1 0 1 0 1
1 1 0 0 1
1 1 1 1 1

有一些方法来实现这一点,但这是一个特定的方式:和钻头的样子\ oplus B \ \ ()如果都是0,文本\(\{不是}(A \ oplus B) \)如果都是1。这是一样的\ \ ((\ oplus B) oplus携带\:\),这是一个可行的序列。

仅有的一位看起来\ \(:\文本{和}\:B \)如果都是0,但是\ \(:\文本{或}\:B \)如果都是1。不给立即电路,所以我们必须进一步看。注意,当\ \(:\文本{和}\:B = 1 \),仅有的一位是1不管什么都是。如果都是1,A或B是1,但不是,外卖的食物也是1。我们可以把这些规则写出来是这样的:

\ [\ displaylines{金额= (A \ oplus B) \ oplus携带\:\ \ ~ \ \携带\:出=(\ \文字{和}\:B) \:{或}\ \文本:(携带\:在\ \{和}\:文本(\ oplus B))} \]

这是一个表示的全加器电路。如果我们想程序的指令列表实现这个微不足道的添加功能,这样我们可以做(写成一些通用的伪代码):

123456789101112131415
/ /添加最后两位年代(2]=一个(1]XORb(1]carry_2=一个(1]b(1]/ /添加前两位,包括以前的携带ax = b=一个(0]XORb(0]年代(1]=ax = bXORcarry_2/ /计算出新的融资套利first_half=一个(0]b(0]second_half=carry_2ax = bcarry_1=first_halfsecond_half/ /设置第一位携带价值总和年代(0]=carry_1

虽然功能,这是非常乏味的。更糟的是,我们必须扩展这个函数更说明如果我们想要解决更大的数字,例如添加两个32位的整数。这种手写的指令代码是完全不切实际,和硬件工程师认识到。帮助软件工程师工作效率,处理器开发人员提供汇编语言的处理器,使事情更麻烦。

汇编语言

汇编语言代表大多数会考虑编程语言的体现。这些语言非常紧密耦合的指令集的特定的硬件设计工作。他们给用户直接访问CPU寄存器和系统的内存,然后用一大堆的指令来实现简单的二进制算术操作。

继续上面的例子中,让我们写一些实际x86汇编添加数字2和3在一起:

123
mov eax, 2;以二进制mov eax = 2在小数,10 ebx, 3;在十进制ebx = 3, 11在二进制添加eax, ebx;eax = eax + ebx,结果将是5小数,101年的二进制

随时为自己试一试这个在线x86模拟器。作为一个参考点,维基百科所有的简要提纲x86支持的指令。注意有多少人。

这是容易得多比手工写出原始逻辑。添加两个32位的整数是一个在x86指令!此外,我们现在有一个方法来存储和使用跟踪一组巨大的数字内存寻址:

123
0 x400800 mov连成一片;连成一片= 0 x400800小数(4196352)mov edx, 0 xc;edx = 0 xc小数(12)mov连成一片,edx;将值设置在内存地址0 x400800 12

第三个好处,大会给我们跳转到其他指令地址的能力基于寄存器的值。这让我们写条件语句分支逻辑:

12
cmp (ebp-4) 10 jge短loc_401600;跳转到0 x401600如果ebp-4 > = 10

最后,这种能力跳转到任意地址和执行这些指令让我们写功能,这可以从其他函数调用:

12345
推动eax;把eax压入堆栈作为第二个参数推连成一片;把连成一片压入堆栈作为第一个参数调用sub_401870;运行指令在0 x401870子功能,;连成一片,它mov eax 2理由[ebp-4], eax;返回值放入4个字节的地址ebp之上

立即有帮助的,因为这意味着我们只需要写一个特定功能的代码。如果我们想要多次调用它,然后我们可以使用这个语法而不是重复函数的代码。

这些只是大会提供的一些特性。尽管如此,许多人会认为它们是最低限度对一个有用的编程语言的功能。仍然很难计划大功能组装和做事情,我们会考虑今天简单操作。下面是一些例子:

  • 我们必须显式地在堆上分配内存并正确分区。
  • 我们必须手动跟踪数组索引之类的东西,作为特定内存地址的偏移量。
  • 我们不能轻易定义变量的“所有权”,以同样的方式,面向对象语言允许对象实例变量。
  • 没有类型,所以我们不能执行类型安全。
  • 没有“记忆范围”的感觉,所以我们可以编写代码来读取和写入内存过去我们预期,导致之类的东西分割的缺点缓冲区溢出,崩溃机器或作为一个恶意代码的入口点。

本质上,大会给我们做任何我们想要的自由在硬件层面上,这也意味着我们负责硬件层面上发生了什么。抽象的缺乏使软件工程师更容易犯错误当写汇编代码,这是出了名的难以调试。它也有特定于平台的缺点:如果我们编写代码为一个像x86指令集,我们必须完全重写它,如果我们想在移植到不同平台的手臂。

仍然有一些有效的使用情况下,工程师必须手工工艺装配(特别是在处理某些嵌入式系统),但它在很大程度上取代了使用高级程序设计语言

额外的材料

知识检查

第一季度

(硬)在MIPS指令集架构中,一个j指令用于跳转到一个特定的内存地址之前持续的程序执行。这是翻译成机器代码如下:

architecture-1

注意,该地址的最后两位离开,因为指令是4个字节;因此,任何程序中指令的地址将是4的倍数。

如果操作码j000010年,什么是机器代码跳到地址0 x0003e8吗?给你的答案是一个32位的十六进制。

第二季

(硬)下面的MIPS汇编程序,运行时的十进制值(4字节数据)这个词在地址标签var西南指令?(注意评论后#标志)。

12345678910
.globl主要#声明入口点程序。text #开始主要程序代码:#标签地址主要功能lw美元t2, var #加载寄存器t2与字地址var另外美元t2, 23 # 23注册添加即时数据t2 sw t2,美元var #字存储在寄存器t2处理var #…. data #美元开始数据结构var: var .word 100 #的#标签地址储备4字节字初始值为100

练习

E1

画一个循序渐进的图的传统电脑添加了两个数字并将结果存储到内存中。


最后更新:2022年2月15日