Programming code tells the computer to do something, but what makes the code actually mean anything to the computer?

1.22K views

Programming code tells the computer to do something, but what makes the code actually mean anything to the computer?

In: Engineering

36 Answers

Anonymous 0 Comments

The actual answer is [arithmetic logic unit](https://en.wikipedia.org/wiki/Arithmetic_logic_unit) (ALU). That’s the thing that follows the instructions, making them meaningful.

It’s a circuit that maps code numbers (instructions) to simple operations like sum, subtract, compare, store on memory, load from memory. With a sequence of enough of these simple operations, the computer can do all the other complex operations it does.

Edit: you can actually make a computer with very few instructions (like [one](https://en.wikipedia.org/wiki/One_instruction_set_computer)), but that’s hard to program in general. Modern computers have many instructions that make it easier on the programmers. And then you can also make programming languages on top of that with commands that translate to multiple instructions.

Anonymous 0 Comments

Computer’s processor (CPU) has a built in instruction set. It performs relatively simple things like “add two numbers together” or “reserve x amount of space in memory”. A programming language allows to input something closer to human language, though still quite cryptic to the uninitiated, and a compiler then takes this input and converts it into instruction sets, written in binary (0s and 1s) that the CPU can understand.

Programs then operate on data held in memory to make things happen.

Anonymous 0 Comments

You can think of the computer as consisting of TWO parts. The Central Processing Unit and the Memory. Programmers put their code into the memory and you can look at it like a list that goes top to bottom. The CPU then gets fed that information and writes new stuff to that memory. How does that happen? With the pins! When you look at your CPU it has all these connections to the surface it gets placed on and now all you have to do is send some electricity to those pins. The CPU is constructed in such a way that if you send electricity to certain pins they will then redirect that electricity to other pins which you then can send back to the memory.

Anonymous 0 Comments

There is a free course available called “From Nand to Tetris” and it teaches you how to build a Tetris game from scratch, and by scratch I mean Nand gates, which are (one of) the most elemental electrical circuits. It teaches you how to assemble these gates into building blocks of a cpu, and then machine code, assembly, a compiler and an operating system right up to a working Tetris game. It is fascinating and makes you really understand what is going on.

Anonymous 0 Comments

A lot of people are giving you metaphors, or just repeatedly using the phrase ‘the computer interprets’ as though no matter how deep you drill down there is some sort of ineffable, intelligent gremlin that performs the magic of understanding.

So, here is what’s done. It may be a bit harder to follow than ELI5 ought to be, but it should actually answer the question in as simple a form as I can make it.

The most basic kind of process that would resemble a computer is an arithmetic processing unit. But we’ll focus on an even more basic kind of processor than that – something that just lets us store numbers in memory, and add them together. We’ll do this with 4-bit numbers.

What you need to make this device is an adder circuit, and a bunch of memory circuits. You can look up how to make these from logic gates – but all you need to understand is their basic input-output behavior.

For an 4-bit adder, you’ll have 8 input lines which you can make high or low (first 4 represent first number, last 4 represent second number) and 4 output lines, which will represent their sum (with any carry-over truncated.) This circuit acts more or less instantly – whatever you assert on the input lines, the output will automatically match.

For an 4-bit memory circuit, you have 5 input lines. You have 4 input lines which you assert as high or low to represent your number. And then you typically have a one more input line which, when asserted, will ‘store’ each of these bits, so that the 4 output lines match the current value of each of the 4 input lines, and will continue to output them until they get another ‘store’ command. Without that ‘store’ command the memory slot will just ignore the current inputs.

To make our super-dumb calculator, you’ll arrange these kinds of circuits together as shown in the following diagram, with 4-wire buses (4 wires in parallel that link outputs of circuits to inputs of others). The horizontal and vertical lines represent these buses. The sideways arrows -> represent ‘gates’. A gate is just an electrically-controlled switch. Not drawn are a bunch of single wires connected to these gates. If that wire goes ‘high’ the gate is opened. If the wire is ‘low’ the gate is closed. I’ll mention what the ARB is for in a minute. We’ll attach the input to our gates at the memory-inputs to the memory’s ‘store’ function as well, so when the gate is opened it’ll store the value.

>|||| -> memory 0—Adder……|||| <- ARB 1
|||| ——————Adder –>|||| <- ARB 2
||||……………………………….|||| <- ARB 3
||||……………………………….|||| <- ARB 4
|||| <- memory 1 <———–||||
|||| <- memory 2 <———–||||
|||| <- memory 3 <———–||||
|||| <- memory 4 <———–||||
|||| <- memory 5 <———–||||

So how would we use this to do operations?

Well, let’s say we want to add the numbers 3 and 5 together. For now let’s just say that the value of 3 (0011) is stored in memory slot 1 and the value of 5 (0101) is stored in memory slot 2. So how do we do that?

Well, they have a single set of 4 wires along the left-hand side attached to the outputs of all the memory slots. If we ‘open the gate’ at the memory 1 output, then the value of 3 (0011) will spread across the four wires. It will run up against the input to the adder, and the input to the memory 0 slot. We can’t also open up the gate at the memory 2 output, because that would put both the values of 3 and 5 (0011 and 0101) on the bus at the same time, so you’d probably get a random result, or the superposition of the two (0111 = 7). So we can’t just open both gates and let them flow into the adder. And two 4-wire buses would be inefficient, since we’d need two gates for each memory circuit, one to connect it to each of the two buses.

That’s why memory slot 0 is there. We open the gate from mem1, which will deliver its value (0011=3) to the adder and the input of memory 0. Then we open the gate on memory zero input (which is also tied to the ‘store’ line) so the value in mem1 gets delivered to and stored in mem0. Now the value that was in memory slot 1 is continually being input to the adder via the memory 0 output.

So, then we close the gate leading from memory 1 to the left bus, and we close the gate leading into memory 0 so it won’t get overwritten with a new value. Then we open the gate on the output of memory 2, so that the value there (0101=5) flows directly into the Adder’s second input. With this gate open connecting memory 2 to the adder, and memory 0 deliverying memory 1’s value to the adder, the output of the adder will be the sum of the values in memory 1 and memory 2 (1000 = 8). This value will be output constantly at the output of the adder.

If we open the gate on the output of the adder, and open the gate to the input of memory slot 3, then the sum will get stored in memory slot 3. Thus we will have added two values and stored the result.

Going back to the ARB thingy – we need a way to place our desired values into memory in the first place. How do we get the values of 3 and 5 into memory slots 1 and 2 to begin with? We have to put them there. So ARB_IN is actually just a source of high voltage connected to the 4-wire bus with 4 individual gates. We open or close those gates to place a total value on the bus, and then open the input of a single memory slot to store the value. So to store the value 3 in memory 1, we open the memory 1 input gate, and then open ARB_In gates 3 and 4 while leaving 1 and 2 closed (0011 = 3). We do a similar thing to place the value 5 (0101) in memory 2. Thus, we can input ARBitrary values into arbitrary memory by opening the right combination of gates. This is also why we need a gate on the adder output – so we can block off the result constantly flowing out of it if we want to put an arbitrary value on the memory input line instead.

So this really just breaks down into 4 steps. Leaving all other gates closed:
>1) Open gate mem1_in, ARB gate 3, and ARB gate 4
2) Open gate mem2_in, ARB gate 2, and ARB gate 4
3) Open gate mem1_out and mem0_in
4) Open gate mem2_out, adder_out, and mem3_in

This will:
>1) store the value 3 (0011) into memory slot 1
2) store the value 5 (0101) into memory slot 2
3) store the value in mem1 into mem0
4) deliver the value in mem2 to the adder, adding it with mem0 and storing it in mem3

This is our program. *This* is what we need to tell the computer to do, at the most basic, fundamental level. Build it so that we can make it do different, useful things by opening and closing gates, and then tell it what gates to open and close in what order to make something happen. So what does this *program* look like when written for the computer? Well, we have 16 total gates. We’ll label them 0-9 and the remaining six A-F.

>0) ARB 4
1) ARB 3
2) ARB 2
3) ARB 1

>4) mem0_in
5) mem1_in
6) mem2_in
7) mem3_in
8) mem4_in
9) mem5_in

>A) mem1_out
B) mem2_out
C) mem3_out
D) mem4_out
E) mem5_out

>F) adder_out

So our 4 instructions above become:
>1) open gates 5, 1, and 0
2) open gates 6, 2, and 0
3) open gates A and 4
4) open gates B, F, and 7

These gates are all blocking (no signal gets through) if they receive a low voltage (0) and are connecting if they receive a high voltage (1). So that’s exactly what our instruction looks like. Our instruction is formatted as 16 high/low signals delivered to the gates in the format of:

>FEDC BA98 7654 3210

(spaces added to make it easy to identify individual bits)

So our 4 instructions, finally, in fundamental ‘machine code’ are:
>1) 0000 0000 0010 0011
2) 0000 0000 0100 0101
3) 0000 0100 0001 0000
4) 1000 1000 1000 0000

That’s it. That’s the machine code. There’s no interpretation going on. There’s no Gremlin. These are the physical instructions that make the machine go. You could deliver these instructions, say, in the form of a punch-card. Have a cardboard card with lines of 16 perforated holes that can be punched out. Place it between a piece of metal with high voltage, and a line of 16 spring-loaded contacts each attached to a gate. Any holes that are punched out will let the contacts connect to the plate, delivering voltage to their individual gate. Perform each instruction by pushing the card through, to the next line so all 4 lines occur in-sequence (with all gates closed in between to prevent any instruction mixing).

All computers are just [much] more complicated combinations of this fundamental mechanism. No matter how many layers of abstraction there are, at some point the system just turns into ‘telling which gates to be open and closed for each step to occur”. And the computer doesn’t have to ‘interpret’ anything. At the fundamental level, the instruction *is* the set of signals that electro-mechanically open and close the gates.

If you read through all this, well done. Let me know if it was helpful, or if any part was unclear.

Anonymous 0 Comments

The basic building blocks are gates, where you can have AND gate and an OR gate. Both take 2 signals and give a result.

A signal is just a voltage, so ‘1’ is more than 2 volts, and ’0’ is less than 2 volts, and 1 signal is called a bit.

Out of these basic building blocks you can make an addition / subtraction/multiplication/division operations for 2 Sets of 64 bits that is.

but when do you use which operation? You build a block that takes 2 extra bits, and you basically can decide that 00 is for addition, 01 for subtraction, and so on, meaning the 4 blocks all have access to whatever is the storage of the 2 64 bits, but you select which operation you want with the 2 bits.

So now you have a list of operations that work on 2 sets of 64 bit numbers that you can reference.

Instead of hardcoding the bits, you can create a compiler that converts certain words into certain operations so instead of ‘00’ for addition, you can give it the word ‘ADD’ and then the 2 sets of bits you want to add.

When you have this basic compiler, you can then extend it to make more complex things like ’if’ and ‘for’ loops, and you can save many commands together as a single command (functions).

So what gives the program meaning? People made the design decisions by making the building blocks, and listing the available blocks, and then designing a compiler that can select and extend those building blocks.

Anonymous 0 Comments

**A computer is basically a HUGE electric “marble race” game, with the track shaped by the program.**

You start with logic gates. LOTS of them. The power tumbles in lots of different ways according to the way the “track” is set up, and after a lot of applied ingenuity you get it to actually do something useful.

What do I mean by a logic gate? OK, let’s say you design a tiny electronical circuit that has two wires (A & B, say) going in and one wire (C) coming out. And that it’s designed so that there’s an output signal if (and only if) it’s getting at least one input signal. If both are missing, nothing comes out. That’s basically a logical OR – C gives a signal if and only if it gets one from A OR B.

Then you design another that gives an output when, and only when, it has no input on a specific wire. That’s a NOT operation.

You can go on to work out how to build basic circuits that do other logical operations, but with OR and NOT you’ve now already got enough circuit designs to combine to give all the possible logical operations – AND, XOR and so on. [You can make an AND with three NOTs and an OR, for example: feed each of A and B into their own NOT gates; feed the two outputs into an OR gate; feed the output from that into the third NOT gate; what comes out is basically A AND B. Proof left, as they say, as an exercise.]

So now you start working out how combine all the gate types into bigger combinations that do something more interesting. You can combine them to tweak a set of electrical values that represent a number so that you effectively add 1, for example. So you keep going, building the complexity. What you’re aiming for is a heap of circuits that will copy the values from one place to another, then do interesting things based on what those values are, then move on to the values in another place, and so on. And those values that make it do the different interesting things – are the program.

Add power. Electricity tumbles in all sorts of different ways, and the track ticks into a different shape. Add a bit more power, and it ticks into a new shape again. And the shapes are controlled by whatever values we put in some special places – those values are the program. Keep adding power and with enough ticks, suddenly you’re browsing Reddit. You’ve got a computer.

Big marble race, like I said.

You could, quite seriously, design a “computer” to run a modern operating system using marbles. It would be enormously big and INCREDIBLY slow – and the engineering challenges to make it work would likely be insuperable – but in principle you could do it.

Anonymous 0 Comments

Electricity moves along a copper wire. When you connect a copper wire to two opposite ends of a battery, electricity will flow through the wire. When electricity flows through a wire, we call this state “1”, when electricity is not flowing; this is state “0”. A computer is really a circuit board. A circuit board is a permanent and solid thing. It is rectangular and etched into its surface are copper wire. These wires transport electric signal. .

So a computer is just a circuit board. A circuit board just transports energy. There is no understanding. A compiler just transforms written words into electrical signals that are fed to a circuit board, like on a production line.

Anonymous 0 Comments

Finally one I’m qualified to answer.

Computers are made from electronic components. What these components are, has been changing as we progress in technology, but one thing they all have in common, is they only work on electricity (duh). But what this means is that our numbers and languages mean nothing to a computer, as we cannot convert them to electricity. But if we look at it, the electricity can also be mathematically modeled. This might sound complex, but it’s actually pretty intuitive, let’s say a switch, it will either be on, or be off. We can easily represent this as 1 for “on” and 0 for “off”. Technically we call it ‘high’ for 1 and ‘low’ for 0. This is because we don’t completely turn off the so called ‘switches’ of the electronic components inside the computer, but have two different levels of power, a high power (1) and a low power (0). Now what we want the computer to do is then just making a combination of these ‘components’ so that when we turn on a certain set of switches (which are our inputs) a certain set of bulbs will light up at the other side (which are our outputs) e.g if we have two inputs A and B and an output C, where A and B are the ‘switches’ and C is the ‘bulb’. We can create a circuit, where the bulb turns ON only when both A and B are ON. This means that C is 1 only when A and B are both 1. Thus we have successfully created what is called an **AND GATE.** There are many other such gates that we can use to create all different kinds of circuits to give a desired output for a certain set of input.

**What you read above was how MACHINE LANGUAGE works. The actual language that a computer understands.** In the early stages of the computer, this is exactly how we used to interact with computers, through 1s and 0s, but as we wanted to do more complex tasks with computers, it was tedious to type in 1s and 0s. So that’s when we decided to come up with short codes for all the long 1s and 0s, e.g we decided that ‘1000001’ means the letter A means , and ‘1000010’ means ‘B’ and so on.

Now the languages we use today, think of them as a pyramid. On the bottom is the machine language, and on the top are “high level languages” like Python and Java. The higher the language, the easier it is for us to understand. The lower the language, the easier it is for the computer to understand. So accordingly, we have different set of translators for different sets of languages depending on where they lie on the pyramid. For the highest level languages, the translators are equally complex, whereas for machine language, we don’t need a translator.

The rest is just a job of programmers creating this translator, called a ‘compiler’ or an ‘interpreter’ so that it translates the code we write to what we want it to mean in the corresponding machine language instruction

Anonymous 0 Comments

OK let’s try philosophy for a 5yo. 😀

I think to really understand what things “mean” you need to know what „meaning“ (in your question) means for your computer.

Of course there is bits and bytes and CPUs and machine codes and programming languages and Fortnite and so on, but that still does not really tell what things “mean”.

*** NOTE: There are some differences between how a “real” computer works and what I describe here. I will lay out these in the end. I have good reasons! 🙂 ***

I think the missing piece to what others pointed out (correctly) is: what can computers actually DO? Because this is what the „code“ is; it’s instructions to DO something. And this is the only “meaning” things have for a computer: the code tells it to execute operations. What these are, what they lead to – well, the computer does not know, of course.

But what are those commands? What does a computer do? Even the most advanced hardcore gaming PC is, in the end, a very simple machine when it comes to do things. Let’s create a list of what it can do:

1. it can store natural numbers. So you can, simply put, have a place in the computer that has a name, and that you can put numbers in. Let’s call those places variables, and they have names: a,b,c,d, …

2. you can set those variables to a number you like: a = 1337

3. you can add or subtract a constant value from or to a variable: a = a – 10

4. A loop: you can repeat a set of commands. The loop stops when a variable you specify is 0.

This. is. it. Not too bad, is it?

And your code tells the computer to do one of those things, one after another. That is the meaning of code.

Now there are two more immediate follow-up questions, and I’m afraid we need to grow up a bit now (I’m not clever enough to describe this simpler).

1. I don’t believe you. If this is all, why the heck can we play Fortnite in HDR?

The pixels you see on your screen are represented by variables. So basically, if your screen is black to start with, to set one pixel to white, you need to figure out which variable does it, and set it to the number that represents white. The computer has no idea what it’s doing, has no concept of white, so this is the only meaning it has to understand. This is true for every function your computer has that does not seem to have anything to do with numbers: for the computer, everything is just a number. We provide the meaning with output devices and by experiencing the output.

2. This still sounds wrong. All computer languages I know have waaaaaaay more commands than three.

True. This is where what others here said comes in. Building Fortnite only with those three commands would be tedious, may be even impossible as we would have a very hard time to read and understand what’s so easy for the computer to run.

So we build layers of complexity on top of this:

The CPU (the thing that runs your computer) itself already has a ton of helper functions. For example, the three commands do not allow you to add one variable to another, you need a small program with multiple commands! How tedious. So, the microcode of the CPU includes this program and you can simply say c = a + b. (not completely true, see far below – but it illustrates microcode). There are a lot of these. Some are build physically with wires and transistors, some are actual little programs within the CPU; the difference does not really matter, because: they still do not create more “meaning” for the computer; and all it does in the end is executing commands 2-4.

The CPU commands have numbers as names (as they address variables that contain the little programs!) – this is super tedious. So we gave them names and wrote a program (Assembler) that converts names humans can remember to those numbers. So instead of saying 16 15 12 (add (command 16) the number in variable 12 to the number in variable 15) you write add b,a and the Assembler will translate.

This is still super tedious, so we build programming languages with really high level functionality. Suddenly, numbers can be anything: they become text, they become 3D objects, they become network packages being send around the world. But this is all meaning added outside of what the computer “is”.

Even after years of computer science I find it fascinating that these four basic commands are all that you need to build… all this! (theoretically)

========

*** Real computers and theory ***

What I described are the theoretical capabilities of Turing machines (actually, WHILE programs, but they are equal in complexity, in what they are capable of). Read up on this stuff, it’s brillant.

Computers can do less than these machines, because computers have limited memory. Computers also do work differently physically due to this fact.

I wanted to add this to minimize yelling and shouting, because obviously numbers are not added in loops by a CPU, but by using parallel addition units.

So, modern CPUs can physically add two numbers directly without the need for a loop, similar to how we add numbers manually:

435 + 123 = (4+1) (3+2) (5+3) = 558

One might say, hey but this is better than our three commands! It adds something! But: this only works because the maximum value for each variable is fixed; if you add numbers bigger than what you planned for when you build your computer, it breaks apart; the size is limited, so you KNOW how many digits to add BEFOREHAND. For possibly infinite numbers you would still need to loop to iterate over all digits of the number until you are done.

Also there are specialized units that are build with special mathematical purposes (floating point operations, matrix operations). They work a bit differently when it comes to how to interpret variables, but the available complexity is, in the end, the same.

Furthermore, when I say “the CPU in the end only RUNS those three commands”, this is not meant literally. Obviously the CPU works differently. But from the information theory standpoint, even while working differently, it can not do more than what you CAN do by running those three commands. I abbreviated this for the sake of a potentially 10yo.

I have the feeling that we now have reached adulthood and should leave it like that. But I hope this little trip into theory was worthwhile, and I also hope I did not mess up too many theoretical terms, as English is not my main research language. Plus cellphone.