16
$\begingroup$

I know this is a very common question. But I have a different angle in my mind. I will just try to articulate it here.

From what I know, every instruction that a CPU executes, is in machine language and all CPU can do is do some arithmetic operations thanks to ALU and to its transistors (if we go at hardware level).

However this is easier to type than to comprehend it. So if all CPU does is adding, subtracting, etc., then how is a program, say a JAVA program saying print Hello World, executed with these arithmetic operations?

I mean how is this program converted into something that is just an addition for the CPU?

P.S. If this question is not applicable for this website then I apologise.

-----Part TWO-----

Okay. Thanks to all for answering this fast and with this enthusiasm. I thought its better to modify my question a bit than to go and comment to all answers and ask them again.

So here it is.

First, all have answered specifically w.r.t example of Hello World. This is my fault. I should have kept this generic. Hello world example brings in question of output devices and how its processing is not just limited to the CPU, which is rightfully brought up in your answers.

Also many of you brought to my notice that CPU does more than just addition. I agree with that. I just didn't write that and assumed it all the way. From what I understand, this is the process:

  1. read the instruction from memory (using data and address buses and program counter stuff)

    1. store data in register inside CPU
    2. Now ALU does arithmetic operations, of course after decoding the instruction, or take a jump if its an if like instruction
    3. And then communicating with other resources if needed like with output device and so on. Processes beyond this are trivial for now.

So in step3 where CPU decodes an instruction and decides to do an arithmetic operation(here we are assuming that there is no other operation to be done like jump the current instruction..since arithmetic operations are mostly done..so we will stick to that) This is where my visualization ends. How an instruction from my program is just an arithmetic operation for CPU. It does that arithmetic operation and that instruction serves its purpose.

I hope I made myself clear this time.

P.S. I am taking a big assumption here that ALU is just not restricted to actual arithmetic operation we do in our programs, rather it executes all instructions ,which are now in binary form, by adding or subtracting e.t.c them to yield result that they are meant to yield. If I am wrong here than answers below correctly answer my question.

$\endgroup$
12
  • $\begingroup$ I understand that the compiler converts the program into machine language. I just cant visualize a program as an arithmetic operation. Though if program itself is about adding two numbers then it is understandable but otherwise not..yet!! :) $\endgroup$ Sep 21, 2015 at 14:27
  • 1
    $\begingroup$ Maybe you should start looking at the actual instruction set of CPUs, for example very simple ones like MC6502, Z80... then see that there are memory access instructions, data processing instructions, branches... You could then guess how they can be combined for implementing any algorithm. $\endgroup$
    – Grabul
    Sep 21, 2015 at 20:55
  • 3
    $\begingroup$ A CPU definitely can do more than addition. It's crucial to mention that a CPU can do comparisons and jumps. $\endgroup$ Sep 21, 2015 at 22:00
  • 1
    $\begingroup$ You (seem) still firmly refusing to see IF (taking decision) and MOVE (reading and storing data), Programming is 99% IF and MOVE. Arithmetic is neglibible. Your first example (Hello world) has no arithmetic at all. $\endgroup$
    – edc65
    Sep 22, 2015 at 19:14
  • 1
    $\begingroup$ 1. I think you'd be more likely to get good answers if you asked a new question with your new confusion, rather than editing this question to change what you're asking. You got good answers to your original question, and your original question seems like it can stand on its own, so why not remove the edit and ask a new question? 2. That said, I can't understand the new part. What exactly is your question about the new part? When you say "this is where my visualization ends", do you mean you do understand step 3, or you don't understand step 3? If you do, what don't you understand? $\endgroup$
    – D.W.
    Sep 23, 2015 at 16:21

7 Answers 7

7
$\begingroup$

You can try taking a simple program and compiling it to native machine code. (Java normally compiles to JVM code, but Andrew Tennenbaum has a book where he describes how to design a CPU that runs that natively, so it will do.) On GCC, for example, you give the compiler the -S switch.

This will tell you that anything tricky, like I/O, is implemented by calling the operating system. While you could download the source to the Linux kernel and do the same to it, what's going on under the hood is: everything is manipulating the state of the computer's memory, for example the list of running processes, or else talking to hardware by using special memory addresses that control it or using special CPU instructions like in and out on the x86. Generally, though, only special programs called device drivers will talk to particular hardware, and the OS will send requests to use hardware to the right driver.

Specifically, if you print, "hello, world!" your compiler will turn that into a set of instructions that loads the string into a particular location (for example, loading the address of the string in memory to the %rdi register) and calling a library function with the call instruction. This library function might find the length of the string with a loop, then call the system call write() to write that number of bytes from the string to file descriptor number 1, which is standard output. At that point, the OS looks up what that process' file number 1 is and decide what writing to it means. If writes to standard output get printed on your screen, there will be some process like copying the bytes to a buffer, which is then read by your terminal program, which tells the windowing system which letters to put where in what font. The windowing system decides exactly what that should look like, and tells a device driver to put the pixels on the screen, which it does by changing video memory.

$\endgroup$
1
  • $\begingroup$ Thanks @Lorehead. This explaination looks good wrt Hello world example. $\endgroup$ Sep 22, 2015 at 5:08
5
$\begingroup$

Your CPU in itself is dumb, as you figured out. But there is a microcosm of hardware chips around it. You have an instruction that lets you set one line of the CPU to high-level which is wired to another chip. That hardware chip monitors the line and says:"Hey, if this line is high, then I do something with some other lines."

To make this easier, these lines are grouped together. Some are used to address devices, some are used to transfer data for those addresses and others again are just "Dude, there is something important going on in my chip" lines.

In the end, your CPU merely tells some other chip to pretty please modify the signal to the monitor so it looks like "Hello World".

Google the drawing of a 7-segment-display. It has wires, which will make a segment light up if you apply voltage to it. If you connect now one output line of your CPU with one line of the 7-segment display, the display lights up. It's not the CPU that causes the LED to light, it just applies voltage to lines, but some other hardware stuff might do nifty things due to it.

If your CPU now sets all the lines for the H to high, the 7-segment will display H, although H is not a number that a CPU would add or subtract from.

Now, if all layers agree what is necessary to make the 7-segment-display H (set 5 specific lines to high), the Java compiler can make code to make it display H. This is of course rather inconvenient - so the layers start to abstract. The lowest layer will start by:"Yo, there are like 26 letters, let's assign numbers to each letter - how about we give the letter 'H' the number '72'? Then you can just tell me "Display letter 72", instead of "Set line 309 high, set line 310 high, set line 498 high, set line 549 high, set line 3 high". And so each layer starts to abstract information, how to achieve certain results, so you don't need to care about them.

So yes, it sums up to a huuuuge mapping of numbers or bits, the CPU can actually process, to meanings that everyone in the chain agreed upon.

$\endgroup$
3
$\begingroup$

In college as part of a CS degree program, I studied an extended example of register transfer language defining a CPU. I was inspired to take a different take on it and write a simulator that accepts such notation as a definition, and published that in Embedded Systems Programming (March 1989 issue) as a way to answer the same kind of question you asked, allowing people to build their intuitive understandings of such things.

In class, we went on to distill that resister-transfer notation into actual logic gates on the registers! It writes itself: look at everything that has register 'A' as the destination, and code A= (case1) or (case2)... and that gets expressed as sum-of-products or product-of-sums normallized form.

Only at the end of the course did I learn that this was a real CPU: the PDP-8 if I recall correctly.

Today you could feed the gate diagram into a programmable logic array chip.

That's the gist of it: a register is set with the result of AND and OR gates leading back to other registers. One of the values to include is the opcode value.

So imagine: A := (opcode==17 & X+Y)|(opcode==18 & X+Z)|...

Modern cpus are more complicated, with pipelines and busses, but individual subunits like an single ALU work that way.

$\endgroup$
2
$\begingroup$

You're considering the CPU here, but there is another component involved when running 'Hello World': the display!

For the CPU, a value in memory is just a number represented as a given number of bits (0's and 1's).

How it turns into letters on the screen is another story: the display also has memory. This memory (graphic memory) is mapped to 'pixels' on the screen. Each pixel is encoded with a value: if it's a very basic monochrome display, the value is just intensity, for color displays the value is a combination of Red Green and Blue (RGB) which can be encoded in many different ways.

So, when the CPU 'write' a given value into the display memory, the pixels light up. To actually write letters, one needs to light up many pixels. Typically a computer will have a character set (actually several) defined in its operating system. (making abstraction of the 'fonts' themselves, that maps to a definition of what each letter should looks like on the screen)

So, as the code is compiled, it includes all sorts of things that come from the OS libraries, including these font/char set etc.. which allow the CPU to know what to write where in the graphics memory. (It's quite complex but that's the general idea: the compiler includes a lot more code than what's in your 'hello world' code alone, through imported libraries)

In the end, there has been a whole lot of things happening as you suspect, but you didn't have to write all that code.

$\endgroup$
1
$\begingroup$

Here's a formal approach to your question from the field of theoretical computer science.

Basically, we can define a mapping between the computation model of a CPU and a turing machine. There exist theoretical proofs that the set of all imaginable turing machine programs (and therefore all imaginable programs executable on a CPU) is countable infinite. This means that we can identify every program with an unique natural number, including the program that would extend natural numbers to turing machines.

As you already know that almost anything CPUs do is computations on natural numbers in binary representation, you can reason that CPUs can carry out every imaginable program.

Note: This is overly simplified, but, in my opinion, gives a nice intuition.

$\endgroup$
1
$\begingroup$

CPUs work like this:

  • fetch current instruction, increment "current instruction" pointer.

  • decode it (e.g. find out what is this instruction telling the CPU to do)

  • execute it (do what the instruction says) - the current instruction pointer may be modified if the instruction is something like a "jump".

  • Repeat forever

Modern CPUs are more complex and try to heavily overlap and even predict parts of that process (e.g. start executing while 10 other instructions are decoding while the CPU is fetching far ahead of the "current instruction" pointer to keep "pipelines" full), but the essential process is really the same.

There are many types of instructions, an example of most of them is:

  • "Move" instructions. These can copy X to another X, where X is memory (RAM), a register, or an address in I/O space if the CPU supports such a notion.

  • Stack manipulation instructions, including pop into register, push register on stack, etc. These are a special case of "move" instructions that use and update a "stack pointer" register.

  • Instructions that perform math operations, either between two registers or memory and a register. These instructions automatically affect a flags register. One such flag is the "zero" flag which is set if the result is zero, another is the "negative" flag which is set if the most significant bit of the result is set. There may be others depending on the CPU.

  • A special case of math operations are comparison instructions, which is the same as a subtraction, but the result is not kept. Flags are still affected.

  • There are branch instructions that jump to a memory address IF specific flags are set. Remember the "zero" flag mentioned above? It also doubles as the "if equal" flag, so you see instructions like BEQ on many CPUs which actually branches if the "zero" flag is set.

  • Instructions that perform logical operations (AND, OR, NOT), shift bits, and test bits. They may affect flags like math instructions depending on the CPU.

  • Instructions that jump unconditionally.

  • Instructions that jump and save the return address on the stack (a "call"), and other instructions that pop an address off the stack (a "return").

  • Special instructions like those that halt the CPU, identify the CPU, or call interrupt handlers.

  • "No Operation" - almost all CPUs have a "no-op" instruction that just consumes cycles and moves on.

This is really just an example, there are CPUs with less types of instructions and CPUs with more.

The point is to illustrate that there are many types of instructions besides math instructions in a CPU. Everything in a higher-level language is broken down to the types of operations above and only some of those will be math or ALU-type instructions.

$\endgroup$
1
$\begingroup$

What may help is to shift your thinking away from "doing arithmetic." If you're really trying to dig at what computers are doing under the hood to print "Hello World" its best to think one level lower. The computer's "state" can be described as a set of bits, stored by transistor switches which are either on or off (or capacitors which are either charged or uncharged). The computer manipulates those bits according to rules. The ways the computer is permitted to manipulate those bits are written onto the CPU in the form of transistors which do the work of changing bits from 0 to 1, or 1 to 0.

When an ALU "does arithmetic," what that really means is that it changed the state of the computer in a way that is consistent with our rules of arithmetic. All it did was change some bits. It's the meaning behind the software that explains why we should think of it as addition or subtractions. The CPU doesn't "know" what it's doing. It just changes from state to state, and that's all (at least until Skynet takes over).

When you think of it that way, more complicated instructions like a "jump" instruction are no different. All it does is change some bits. In this case it happens to change the bits that we know mean the location of the next instruction to execute. The CPU doesn't "know" this, but we do. So we use the instruction which changes those bits to "jump" from place to place in our code.

IO is really no different either, it's just changing bits. The only minor difference is that those bits are connected to transistors which eventually lead to lighting up characters on your screen. If I may harken back a few decades to when "Hello World" was actually simple, there was a memory space where, if you wrote bits to it corresponding to the ASCII characters for "Hello World," those characters would be rendered directly to the screen. Nowdays it's a bit more complicated, because we have graphics cards and operating systems that mess with it, but the basic idea is the same. You have a set of transistors which are either on or off, which are linked to circuitry to display a pixel on the screen. We set the right ones, and it looks like "Hello World" appears on the screen.

The confusion is simply a matter of syntax vs semantics. The behavior of a "half added" or a "full added" in an ALU is syntax. It defines what bits will come out when you put bits in. The semantics of it is the concept of the ability to do addition. You and I are aware that the ALU can "do addition," but to really understand what happens underneath, you have to remember that an ALU only manipulates the bits and bytes of the syntax.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.