Instructions & Programs: Crash Course Computer Science #8

CrashCourse
12 Apr 201710:36

Summary

TLDR本视频脚本介绍了中央处理器(CPU)的基本原理和编程能力。CPU作为计算机的核心,通过执行不同的指令序列来完成各种任务。视频中通过一个简单的程序示例,展示了CPU如何读取内存中的指令并执行相应的操作,如加载数据、进行加法运算、存储结果以及通过跳转指令改变执行流程。此外,还介绍了条件跳转和停止指令的重要性,以及如何通过软件实现硬件本身不具备的功能,如除法运算。视频还探讨了现代CPU使用更大指令长度和变长指令集的策略,以支持更多的指令和更大的内存地址空间。最后,通过介绍1971年Intel发布的4004处理器,展示了CPU从早期到现代的巨大发展。

Takeaways

  • 🧠 CPU(中央处理单元)是计算机的核心部件,负责执行程序指令。
  • 📝 CPU之所以强大,是因为它可以通过软件编程来执行不同的任务。
  • 🔁 CPU通过指令来控制硬件,指令由操作码(opcode)和地址或寄存器组成。
  • 🔢 内存地址中的数据可以被解释为指令,例如“LOAD_A 14”表示从内存地址14加载数据到寄存器A。
  • ➕ “ADD”指令让CPU使用算术逻辑单元(ALU)对两个寄存器的值进行加法运算。
  • 🔄 “JUMP”指令允许程序跳转到内存中的新位置,改变指令的执行顺序或跳过某些指令。
  • ⛔️ “JUMP_NEGATIVE”是条件跳转指令,仅当ALU的负标志为真时才会跳转。
  • 🚫 “HALT”指令用于停止CPU的指令处理,防止无限循环。
  • 🔁 无限循环是程序持续运行的状态,可以通过条件跳转来打破。
  • 🔢 现代CPU使用更大的指令长度(如32或64位)或变长指令来支持更多的指令和更大的内存地址范围。
  • 📈 随着时间的推移,处理器设计中增加了许多新功能,导致指令集大小的大幅增长。

Q & A

  • CPU是什么,它在计算机中扮演什么角色?

    -CPU,即中央处理单元,是计算机的心脏,负责执行程序中的指令。它由算术逻辑单元(ALU)、控制单元、内存和时钟等组件组成,是计算机硬件的核心部分。

  • CPU的可编程性意味着什么?

    -CPU的可编程性意味着通过编写不同的指令序列,CPU可以执行不同的任务。这使得CPU能够通过易于修改的软件来控制硬件,增加了其灵活性和功能性。

  • 在脚本中提到的简单程序中,内存地址是如何表示指令的?

    -在脚本中,内存地址中的前四个位表示操作码(opcode),后四个位表示地址或寄存器。例如,内存地址零中的“0010 1110”可以被理解为“LOAD_A 14”指令,即从内存地址14加载数据到寄存器A。

  • 什么是无限循环,它在程序中有什么作用?

    -无限循环是指程序会无限期地重复执行一段代码,因为它没有条件跳出循环。在脚本中,通过不断执行JUMP指令,程序会一直循环执行特定的指令序列,直到遇到条件跳转或停止指令。

  • 条件跳转指令在程序中的作用是什么?

    -条件跳转指令允许程序根据特定条件来决定是否跳转到另一个指令执行。例如,JUMP_NEGATIVE指令仅在算术逻辑单元(ALU)的负标志为真时执行跳转。这使得程序能够根据运算结果来改变执行流程。

  • HALT指令在程序中的作用是什么?

    -HALT指令用于停止CPU的执行。在脚本中,如果没有HALT指令,CPU会在执行完所有指令后继续执行,可能会导致错误或无意义的操作,因此HALT指令是程序正常结束的关键。

  • 现代CPU如何处理有限的指令和内存地址空间?

    -现代CPU通过使用更长的指令长度(如32位或64位)和变长指令来解决这个问题。变长指令允许指令长度根据需要变化,从而可以支持更多的指令和更大的内存地址空间。

  • 1971年Intel发布的4004处理器支持多少条指令?

    -1971年Intel发布的4004处理器支持46条指令,这足以构建一个完整的工作计算机。

  • 现代计算机处理器的指令集大小有多大的增长?

    -现代计算机处理器,如Intel Core i7,拥有数千种不同的指令和指令变体,指令长度从1到15字节不等。例如,仅ADD指令就有超过一打的不同变体。

  • 为什么现代CPU需要更大的指令集?

    -随着时间的推移,为了提高处理器的性能和功能,设计者们向处理器设计中添加了额外的功能,这就需要更大的指令集来支持这些新功能和操作。

  • 在脚本中,如何通过软件实现硬件无法直接执行的功能,例如除法?

    -通过编写特定的程序,可以利用CPU的指令来实现硬件本身不支持的功能,如除法。在脚本中,通过编写一个除法程序,CPU可以执行除法运算,尽管其ALU没有直接的除法功能。

Outlines

00:00

💻 CPU的编程与指令执行

Carrie Anne在本段介绍了CPU的基本功能和指令执行过程。首先,她回顾了上一集构建的CPU组件,包括算术逻辑单元(ALU)、控制单元、内存和时钟,并强调了CPU的可编程性。通过不同的指令序列,CPU可以执行不同的任务。接着,她通过一个简单的程序示例,解释了内存地址中的数据如何被解释为指令,以及如何通过指令来控制CPU的操作。这个示例包括了LOAD、ADD、STORE等指令,并展示了如何通过这些指令来执行一个简单的加法程序。此外,还介绍了JUMP和JUMP_NEGATIVE等新的指令,以及HALT指令的重要性,用于结束程序的执行。最后,她通过修改程序,增加了JUMP指令,展示了程序的循环执行,即无限循环的概念。

05:01

🔁 无限循环与条件跳转

在第二段中,Carrie Anne讨论了无限循环的问题以及如何通过条件跳转来打破它。她通过一个示例程序,展示了如何使用JUMP_NEGATIVE指令来实现条件跳转,从而退出无限循环。这个示例程序执行了减法和条件跳转,最终通过JUMP_NEGATIVE指令跳出循环,并执行了加法和存储操作,最后以HALT指令结束。她强调了软件的力量,即使是基本的硬件,通过软件也能实现复杂的功能,如本例中的除法和取余操作。此外,她还提到了现代CPU使用更大指令长度和变长指令的策略,以支持更多的指令和内存地址。最后,她简要介绍了1971年Intel发布的4004处理器,它是首款单芯片CPU,支持46条指令,并使用8位立即值来扩展内存地址。她还提到了现代处理器,如Intel Core i7,拥有数千条不同的指令和变体。

Mindmap

Keywords

💡CPU

CPU,即中央处理器,是计算机的核心部件,负责执行程序中的指令。在视频中,CPU被比喻为计算机的“跳动的心脏”,它通过执行不同的指令序列来完成各种任务。CPU的强大之处在于其可编程性,即通过修改软件中的指令序列来控制硬件执行不同的操作。

💡指令

指令是CPU执行的基本信息单位,告诉CPU执行特定的操作。视频中提到,通过编写不同的指令序列,CPU可以执行不同的任务。指令通常由操作码(opcode)和地址或寄存器组成,如'LOAD_A 14'就是一个指令,它指示CPU从内存地址14加载数据到寄存器A。

💡内存

内存是计算机中用于存储数据和程序的硬件部分。在视频中,内存被用来存储指令和数据,每个地址包含8位数据。内存中的指令和数据没有本质区别,都是二进制数,但通过HALT指令可以将它们区分开来。

💡寄存器

寄存器是CPU内部的一小部分高速存储资源,用于快速访问和执行指令。视频中提到了寄存器A和寄存器B,它们用于存储指令执行过程中的临时数据,如在执行'LOAD_A 14'指令时,将内存地址14中的数值3存储到寄存器A。

💡ALU

ALU,即算术逻辑单元,是CPU内部的一个组件,负责执行所有的算术和逻辑运算。视频中提到了使用ALU来执行加法(ADD)操作,将两个寄存器的值相加,并将结果存储到指定的寄存器中。

💡JUMP

JUMP是一种改变程序执行顺序的指令,它允许程序跳转到内存中的另一个位置继续执行。在视频中,JUMP指令被用来创建循环,通过将指令地址寄存器的值改写为JUMP指令中指定的新值,从而实现循环。

💡JUMP_NEGATIVE

JUMP_NEGATIVE是一种条件跳转指令,仅当ALU的负标志位为真时才会执行跳转。它在视频中被用来控制循环的退出条件,当ALU的结果是负数时,程序会跳转到指定的位置,从而打破无限循环。

💡HALT

HALT是一种停止指令,用于结束程序的执行。在视频中,HALT指令被用来告诉CPU何时停止处理,避免CPU在执行完所有指令后继续执行内存中的零值,导致系统崩溃。

💡无限循环

无限循环是指程序会无限期地执行下去,没有终止的指令。在视频中,通过JUMP指令创建了一个无限循环,因为程序没有遇到任何终止条件,所以会一直执行下去。

💡条件跳转

条件跳转是一种基于特定条件执行的跳转指令,它允许程序根据运算结果来决定是否跳转到新的位置。在视频中,JUMP_NEGATIVE就是一种条件跳转,它根据ALU的负标志位的真假来决定是否执行跳转。

💡指令长度

指令长度是指CPU指令的位数,它决定了指令的复杂性和CPU能执行的操作数量。在视频中,提到了现代CPU使用更大位数的指令(如32位或64位)来支持更多的指令和更复杂的操作。

Highlights

CPU是可编程的,这意味着通过编写不同的指令序列,CPU可以执行不同的任务。

CPU是受易于修改的软件控制的硬件。

通过将操作码和地址或寄存器编码到内存地址中,简化了指令的理解和执行。

介绍了一个简单的程序,通过四个指令实现两个数字的相加。

通过引入SUBTRACT和JUMP指令,增加了程序的复杂性和灵活性。

JUMP NEGATIVE指令根据ALU的负标志位来决定是否跳转,增加了条件执行的能力。

HALT指令用于结束程序,防止CPU无限循环执行。

强调了指令和数据在内存中是统一存储的,没有本质区别。

通过JUMP指令实现循环,展示了程序计数器的动态修改。

解释了无限循环的概念以及如何通过条件跳转来打破循环。

展示了如何使用条件跳转和循环来实现除法和取余数的计算。

说明了软件如何赋予硬件额外的功能,例如实现ALU不支持的除法操作。

通过增加额外的指令和内存地址扩展,说明了现代CPU如何处理更复杂的任务。

介绍了指令长度的概念,说明了现代CPU如何通过使用更长的指令来扩展指令集。

讨论了可变长度指令的优势,允许指令根据需要包含不同数量的字节。

通过Intel 4004处理器的例子,展示了CPU从早期到现代的发展。

现代处理器如Intel Core i7拥有数千种不同的指令和变体,展示了指令集的显著增长。

强调了处理器设计随时间增加的额外特性,这些特性将在下一集中讨论。

Transcripts

play00:03

Hi, I’m Carrie Anne and this is Crash Course Computer Science!

play00:06

Last episode, we combined an ALU, control unit, some memory, and a clock together to

play00:10

make a basic, but functional Central Processing Unit – or CPU – the beating, ticking heart

play00:14

of a computer.

play00:15

We’ve done all the hard work of building many of these components from the electronic

play00:19

circuits up, and now it’s time to give our CPU some actual instructions to process!

play00:23

The thing that makes a CPU powerful is the fact that it is programmable – if you write

play00:28

a different sequence of instructions, then the CPU will perform a different task.

play00:31

So the CPU is a piece of hardware which is controlled by easy-to-modify software!

play00:36

INTRO

play00:44

Let’s quickly revisit the simple program that we stepped through last episode.

play00:48

The computer memory looked like this.

play00:50

Each address contained 8 bits of data.

play00:52

For our hypothetical CPU, the first four bits specified the operation code, or opcode, and

play00:57

the second set of four bits specified an address or registers.

play01:00

In memory address zero we have 0010 1110.

play01:04

Again, those first four bits are our opcode which corresponds to a “LOAD_A” instruction.

play01:10

This instruction reads data from a location of memory specified in those last four bits

play01:14

of the instruction and saves it into Register A. In this case, 1110, or 14 in decimal.

play01:21

So let’s not think of this of memory address 0 as “0010 1110”, but rather as the instruction

play01:27

“LOAD_A 14”.

play01:28

That’s much easier to read and understand!

play01:30

And for me to say!

play01:31

And we can do the same thing for the rest of the data in memory.

play01:35

In this case, our program is just four instructions long, and we’ve put some numbers into memory

play01:39

too, 3 and 14.

play01:41

So now let’s step through this program:

play01:42

First is LOAD_A 14, which takes the value in address 14, which is the number 3, and

play01:47

stores it into Register A.

play01:49

Then we have a “LOAD_B 15” instruction, which takes the value in memory location 15,

play01:54

which is the number 14, and saves it into Register B.

play01:56

Okay.

play01:57

Easy enough.

play01:58

But now we have an “ADD” instruction.

play02:00

This tells the processor to use the ALU to add two registers together, in this case,

play02:04

B and A are specified.

play02:06

The ordering is important, because the resulting sum is saved into the second register that’s specified.

play02:11

So in this case, the resulting sum is saved into Register A.

play02:15

And finally, our last instruction is “STORE_A 13”, which instructs the CPU to write whatever

play02:19

value is in Register A into memory location 13.

play02:23

Yesss!

play02:24

Our program adds two numbers together.

play02:26

That’s about as exciting as it gets when we only have four instructions to play with.

play02:30

So let’s add some more!

play02:31

Now we’ve got a subtract function, which like ADD, specifies two registers to operate on.

play02:35

We’ve also got a fancy new instruction called JUMP.

play02:38

As the name implies, this causes the program to “jump” to a new location.

play02:42

This is useful if we want to change the order of instructions, or choose to skip some instructions.

play02:45

For example, a JUMP 0, would cause the program to go back to the beginning.

play02:48

At a low level, this is done by writing the value specified in the last four bits into

play02:53

the instruction address register, overwriting the current value.

play02:56

We’ve also added a special version of JUMP called JUMP_NEGATIVE.

play03:00

This only jumps the program if the ALU’s negative flag is set to true.

play03:04

As we talked about in Episode 5, the negative flag is only set when the result of an arithmetic

play03:08

operation is negative.

play03:10

If the result of the arithmetic was zero or positive, the negative flag would not be set.

play03:14

So the JUMP NEGATIVE won’t jump anywhere, and the CPU will just continue on to the next instruction.

play03:19

And finally, computers need to be told when to stop processing, so we need a HALT instruction.

play03:24

Our previous program really should have looked like this to be correct, otherwise the CPU

play03:28

would have just continued on after the STORE instruction, processing all those 0’s.

play03:32

But there is no instruction with an opcode of 0, and so the computer would have crashed!

play03:36

It’s important to point out here that we’re storing both instructions and data in the

play03:40

same memory.

play03:41

There is no difference fundamentally -- it’s all just binary numbers.

play03:43

So the HALT instruction is really important because it allows us to separate the two.

play03:47

Okay, so let’s make our program a bit more interesting, by adding a JUMP.

play03:51

We’ll also modify our two starting values in memory to 1 and 1.

play03:55

Lets step through this program just as our CPU would.

play03:58

First, LOAD_A 14 loads the value 1 into Register A.

play04:01

Next, LOAD_B 15 loads the value 1 into Register B.

play04:05

As before, we ADD registers B and A together, with the sum going into Register A. 1+1 = 2,

play04:11

so now Register A has the value 2 in it (stored in binary of course)

play04:15

Then the STORE instruction saves that into memory location 13.

play04:18

Now we hit a “JUMP 2” instruction.

play04:20

This causes the processor to overwrite the value in the instruction address register,

play04:24

which is currently 4, with the new value, 2.

play04:27

Now, on the processor’s next fetch cycle, we don’t fetch HALT, instead we fetch the

play04:31

instruction at memory location 2, which is ADD B A.

play04:34

We’ve jumped!

play04:35

Register A contains the value 2, and register B contains the value 1.

play04:38

So 1+2 = 3, so now Register A has the value 3.

play04:42

We store that into memory.

play04:44

And we’ve hit the JUMP again, back to ADD B A.

play04:47

1+3 = 4.

play04:49

So now register A has the value 4.

play04:51

See what's happening here?

play04:52

Every loop, we’re adding one.

play04:53

Its counting up!

play04:54

Cooooool.

play04:55

But notice there’s no way to ever escape.

play04:57

We’re never.. ever.. going to get to that halt instruction, because we’re always going

play05:01

to hit that JUMP.

play05:02

This is called an infinite loop – a program that runs forever… ever… ever… ever…

play05:07

ever

play05:08

To break the loop, we need a conditional jump.

play05:10

A jump that only happens if a certain condition is met.

play05:13

Our JUMP_NEGATIVE is one example of a conditional jump, but computers have other types too - like

play05:18

JUMP IF EQUAL and JUMP IF GREATER.

play05:20

So let’s make our code a little fancier and step through it.

play05:24

Just like before, the program starts by loading values from memory into registers A and B.

play05:28

In this example, the number 11 gets loaded into Register A, and 5 gets loaded into Register B.

play05:34

Now we subtract register B from register A. That’s 11 minus 5, which is 6, and so 6

play05:39

gets saved into Register A.

play05:40

Now we hit our JUMP NEGATIVE.

play05:42

The last ALU result was 6.

play05:44

That’s a positive number, so the the negative flag is false.

play05:47

That means the processor does not jump.

play05:49

So we continue on to the next instruction...

play05:51

...which is a JUMP 2.

play05:52

No conditional on this one, so we jump to instruction 2 no matter what.

play05:56

Ok, so we’re back at our SUBTRACT Register B from Register A. 6 minus 5 equals 1.

play06:01

So 1 gets saved into register A.

play06:03

Next instruction.

play06:04

We’re back again at our JUMP NEGATIVE.

play06:06

1 is also a positive number, so the CPU continues on to the JUMP 2, looping back around again

play06:11

to the SUBTRACT instruction.

play06:13

This time is different though.

play06:14

1 minus 5 is negative 4.

play06:17

And so the ALU sets its negative flag to true for the first time.

play06:20

Now, when we advance to the next instruction,

play06:23

JUMP_NEGATIVE 5, the CPU executes the jump to memory location 5.

play06:27

We’re out of the infinite loop!

play06:29

Now we have a ADD B to A. Negative 4 plus 5, is positive 1, and we save that into Register A.

play06:35

Next we have a STORE instruction that saves Register A into memory address 13.

play06:39

Lastly, we hit our HALT instruction and the computer rests.

play06:43

So even though this program is only 7 instructions long, the CPU ended up executing 13 instructions,

play06:49

and that's because it looped twice internally.

play06:52

This code calculated the remainder if we divide 5 into 11, which is one.

play06:56

With a few extra lines of code, we could also keep track of how many loops we did, the count

play07:00

of which would be how many times 5 went into 11… we did two loops, so that means 5 goes

play07:05

into 11 two times... with a remainder of 1.

play07:08

And of course this code could work for any two numbers, which we can just change in memory

play07:12

to whatever we want: 7 and 81, 18 and 54, it doesn’t matter -- that’s the power

play07:17

of software!

play07:18

Software also allowed us to do something our hardware could not.

play07:21

Remember, our ALU didn’t have the functionality to divide two numbers, instead it’s the

play07:26

program we made that gave us that functionality.

play07:28

And then other programs can use our divide program to do even fancier things.

play07:32

And you know what that means.

play07:34

New levels of abstraction!

play07:41

So, our hypothetical CPU is very basic – all of its instructions are 8 bits long, with

play07:46

the opcode occupying only the first four bits.

play07:49

So even if we used every combination of 4 bits, our CPU would only be able to support

play07:54

a maximum of 16 different instructions.

play07:56

On top of that, several of our instructions used the last 4 bits to specify a memory location.

play08:01

But again, 4 bits can only encode 16 different values, meaning we can address a maximum of

play08:06

16 memory locations - that’s not a lot to work with.

play08:10

For example, we couldn’t even JUMP to location 17, because we literally can’t fit the number

play08:15

17 into 4 bits.

play08:16

For this reason, real, modern CPUs use two strategies.

play08:19

The most straightforward approach is just to have bigger instructions, with more bits,

play08:23

like 32 or 64 bits.

play08:25

This is called the instruction length.

play08:28

Unsurprisingly.

play08:29

The second approach is to use variable length instructions.

play08:32

For example, imagine a CPU that uses 8 bit opcodes.

play08:35

When the CPU sees an instruction that needs no extra values, like the HALT instruction,

play08:40

it can just execute it immediately.

play08:41

However, if it sees something like a JUMP instruction, it knows it must also fetch

play08:45

the address to jump to, which is saved immediately behind the JUMP instruction in memory.

play08:50

This is called, logically enough, an Immediate Value.

play08:52

In such processor designs, instructions can be any number of bytes long, which makes the

play08:56

fetch cycle of the CPU a tad more complicated.

play08:59

Now, our example CPU and instruction set is hypothetical, designed to illustrate key working

play09:04

principles.

play09:05

So I want to leave you with a real CPU example.

play09:07

In 1971, Intel released the 4004 processor.

play09:11

It was the first CPU put all into a single chip and paved the path to the intel processors

play09:15

we know and love today.

play09:17

It supported 46 instructions, shown here.

play09:19

Which was enough to build an entire working computer.

play09:22

And it used many of the instructions we’ve talked about like JUMP ADD SUBTRACT and LOAD.

play09:26

It also uses 8-bit immediate values, like we just talked about, for things like JUMPs,

play09:31

in order to address more memory.

play09:33

And processors have come a long way since 1971.

play09:36

A modern computer processor, like an Intel Core i7, has thousands of different instructions

play09:41

and instruction variants, ranging from one to fifteen bytes long.

play09:44

For example, there’s over a dozens different opcodes just for variants of ADD!

play09:48

And this huge growth in instruction set size is due in large part to extra bells and whistles

play09:52

that have been added to processor designs overtime, which we’ll talk about next episode.

play09:57

See you next week!

Rate This

5.0 / 5 (0 votes)

Related Tags
计算机科学CPU指令集程序设计无限循环条件跳转内存地址寄存器历史发展Intel 4004现代处理器
Do you need a summary in English?