Is this kinda sorta how computers work?

I confess I don’t really know how these things work. At least, I don’t think I do. I have vague ideas from childhood reading and idle browsing over the years.

At a long and unnecessary meeting yesterday, I started doodling around wondering if I would be able to figure out how to design a simple programmable computer if I had to.

Other than the fact that a whole lot is passed over in the line “connected to a sort of calculator” in what follows, my question is, is the following kinda sorta how computers work?

As far as you can tell, is the machine I’ve described turing-complete? (Assuming the “calculator” part can compute any addition or multiplication on two values.)

And does it work kinda sorta the same way actual computers work?

That description seems so simple that it is too complicated!

I would describe it more like this (and remember, we are dealing with one of the lowest levels of computer functions).

The CPU (brain) reads an instruction at address 1, then performs what that instruction says to do.

There are many possible kinds of instructions. Some involve reading other data, writing data, manipulating data, inputting or outputting data. The data may be part of the instruction or somewhere else.

One of the instruction types says, “go to address X next.” In the absence of that instruction, the next address used will be the one following the first, where the process is repeated.

Does that sound understandable?

Musicat is right, the description is too complicated. Memory is a set of data that can be addressed. A computer takes the data from an initial memory address (like 0), and uses that as an instruction to decide what to do next. Each instruction can transfer data from one memory location to another, or alter the data at a location, or change the address of the instruction to execute next. Without changing the address for the next instruction, the next sequential address is used for the next instruction.

Actual computers have may have many specialized instructions that combine those basic functions, and additional special memory locations, which may be called registers. One of those special memory locations is the address of the next instruction to execute. For more complicated operations, another special location would be the ‘stack pointer’. This allows a program to start executing instructions from another location, then return back to the original location. Another location would contain some flags to indicate the results of the previous instruction. So an instruction which adds 1 to a memory location may set a flag if the result of that addition is 0. Then an instruction to change the address of the next instruction can be based on that, i.e., the location of the next instruction changes only if the result of the addition was 0.

Computers are a lot like fancy model train setups that have a switching tracks. In a simple train setup, as the train goes one way through the switch, it changes the switch so the train will go the other way the next time it comes through. If the train cars could load and unload material as they traveled, and the switch was thrown only if there was a certain amount of material loaded, it would be functioning the way a computer does.

An even simpler example would be a pool table that returns any ball dropped in a pocket to the table, rolling it in a particular direction. Once the the balls on the table were setup in a particular location, and the processes started by dropping a ball in a hole, the system would continue on forever with balls colliding with each other in a predictable pattern.

I think (acknowledging that I am no computer nerd) that that is actually a pretty good explanation. It reminded me of the way my first computer worked.
One of these
It used multi-pole switches instead of boxes and electrical current instead of paper messages, but the function was basically the same. One person estimated it had about 0.01k of ROM.

By the way…I hereby award a total of 10 old-fart-bonus-points to anyone who can correctly identify the device in the picture:rolleyes:

For laypeople, I think it’s instructive to see “computers” manifested as physical “switches”.

Here’s can example of a simple “adding machine” built using wood:

http://woodgears.ca/marbleadd

If you can imagine shrinking the wooden switches down to microscopic transistor gates and think of the marbles as a tiny electric charge, that can give insight to how computers work.

The wooden adding machine shows 6 “switches”; imagine billions of those switches embedded on a tiny Intel chip. The marbles move at the speed of gravity whereas electric pulses in the chip move almost at the speed of light.

When you have that many switches moving at that speed, you can build a complex chain of computations to enable spreadsheets, word processors, download pron, post messages on SDMB, etc.

If you search youtube, you can also find other “computers” using air or water as “gates.” It’s the same principle.

Computer operation can be (and was in my grad school architecture class) summarized using the RISC model. Steps are:

  1. Instruction Fetch (IF)
  2. Instruction Decode (ID)
  3. Execute (EX)
  4. Memory access (MEM)
  5. Writeback (WB)

We did a whole lot of these diagrams, which were (are) very instructive.

Well, your description is kinda, sorta, somewhat the way a computer works, but in the actual details, you’ll always be off from any real world computer, because the actual details are A) so very free for variation, and B) generally engineered full of all kinds of non-clean special-case optimizations. So if your goal is to accurately describe the machine in front of you, then, no, there aren’t literally exactly five boxes and two operations, or whatever. But if your goal is the general idea that there are various boxes, and various instructions saying what to do with them, and so on, then, sure, that’s about right.

More specifically:

Ok, fine so far.

Huh? What’s the point of the two states here? And… what does “it puts whatever it has grabbed into the box whose number is in the box whose number is in the ‘instr’ box” mean, exactly?

Ok, you’re essentially giving all instructions the same multi-part form, and considering them to be handed to the CPU using these boxes. This is a perfectly fine design, though not all real-world CPUs exhibit such uniformity in their instruction sets.

This could be taken as an enormous simplification of how “interrupts” work. But, naturally, in the real world, things aren’t quite so crude: your computer doesn’t have to pause all the time to read your every mouse movement. There are a number of different ways in which an input peripheral can hand data to the computer; it could write into a special area of the memory which a program then reads off as a queue whenever it desires, for example (with some mechanism to prevent problems from the program from trying to read off the queue as it’s being written into).

Ok… I’m afraid I don’t understand the point of these.

Sounds good. A program is just a sequence of data, and the machine just has to be told where in memory to start reading that data from.

You could do it that way, but why bother allowing oneself to specify programs as non-contiguous blocks of data? Normally, a program would just be given as a string of values, assumed to take up consecutive boxes.

This won’t help if you want to design a layman’s box, but I think the most simplistically beautiful description I’ve heard (thanks, 8th grade science teacher) was, “it’s a box that’s built for counting electrons.”

This is (mostly) accurate in that your PC (or eek! - Mac) is a glorified counting machine with a pretty face on it. Of course, going beyond this you need to start thinking about the layers (using the OSI layer model works conceptually for most, although I despise it myself for reasons I won’t get into).

Anyway, here’s what I would propose as a simplified description of the actual machine in front of me along the lines of your description. Keep in mind that many different architectures are possible; this is just one kind of design that happens to be used. Also, keep in mind that this description is a compromise between the full details of the real world and simplicity:

There is a sequence of numbered boxes, each of which contains some number of bits called a “word” (which can be thought of as representing a number up to some fixed size). [The only reason we bother having most of our architecture based around words rather than individual bits is for optimization reasons]. The total number of boxes is small enough to be represented by a word. These boxes are collectively known as the “memory” or the “heap”.

Certain sequences of words specify certain “instructions”. More on this later.

There are also some special boxes apart from that sequence, known as the registers. Most of the machine instructions act directly on the registers, rather than on the memory. [To some extent, this is also an optimization decision.]

One particular register is the instruction pointer. At the start of each “step”, the machine first reads the number P in the instruction pointer, then reads the instruction J stored starting at the memory location P, then does whatever J says to do. After this, it starts the next “step”, doing the same thing.

Among the possible instructions are the following. (Unless otherwise specified, all of the following instructions conclude by having the machine increment the value in the instruction pointer by their own length, so as to point to the next instruction consecutively):

ADD-Reg1-Reg2: This instruction consists of an initial “ADD” code, followed by codes specifying two particular registers. The end result of this instruction is that the value in the first specified register is incremented by the value in the second specified register. [I could just as well have had two specified input registers and one specified output register, or have always fixed the same registers to be used in addition, or have had addition work directly on the heap, or other things. I’m just illustrating one possible design found in the world.]

MUL-Reg1-Reg2, SUB-Reg1-Reg2: These are just like ADD, only with different arithmetic expressions.

READ-Reg1-Reg2: This instruction copies into Reg1 the value in the memory location specified by Reg2. Thus, this allows us to move data from the heap into the registers.

WRITE-Reg1-Reg2: This instruction writes the value in Reg1 into the memory location specified by Reg2. Thus, this allows us to move data from the registers into the heap.

COPY-Reg1-Reg2: This instruction copies the value in Reg2 into Reg1. Thus, this allows us to move data between registers.

LOAD-Reg1-Value: The code for this instruction consists of a “LOAD” code followed by the code for a register (Reg1) followed by an arbitrary word (Value). The effect of this instruction is to copy Value into Reg1. Thus, this allows us to put specific constant data into a register.

BRANCHZERO-Reg1-Reg2: This instruction examines the value in Reg1; if that value is non-zero, then nothing happens except the usual incrementing of the instruction pointer. However, if the value in Reg1 is zero, then the usual incrementing of the instruction pointer is suppressed, and, instead, the value in Reg2 is copied into the instruction pointer. This instruction allows “conditional jumps”.

INPUT-Reg1: This instruction asks the input device for one word of data, which is stored in Reg1. Presumably, the input device knows how to produce that word (perhaps by reading it off of a queue where it builds up).

OUTPUT-Reg1: This instruction sends to the output device the value in Reg1. Presumably, the output device knows what to do with this.

Most of these instructions are fairly realistic, except those two INPUT/OUTPUT commands. However, in a real-world machine, there would be many more instructions of many kinds (in particular, more kinds of jumping [e.g., you can accomplish unconditional jumping with the above, but it’s nice to have a built in command for it], codes designed specifically for manipulating the so-called “stack” (part of memory helping to organize the data used in nested function calls), and so on). There are also features I haven’t gotten into, such as flags, interrupts, realistic input/output design (the above is nothing like it, just enough to get you going), the cache, and so on.

Anyway, the above design isn’t Turing-complete in itself, because of the finite memory space. However, if the input/output commands are hooked up to a peripheral memory device which is capable of growing to unboundedly large size, then this will be Turing complete. (But it’s very easy to be Turing complete; all a Turing machine is is a finite state machine hooked up to an unbounded memory device.)

Well, YMMV. That seems like a terrible description to me. Counting electrons? That makes it sound like the computer is constantly distinguishing between “Are there 5 electrons here or 6?”, which is not the case.

For that matter, thinking of a computer as just a counting machine is terribly misleading; a box you drop pebbles into is a counting machine. A slider you can move up or down is a counting machine. A computer is something else.

The way I see it, a finite-state (equivalently, finite-memory) computer program with input/output is easy to understand. It’s just one giant table of “If I’m in state […] and I receive input […], then I should go to state […] and produce output […]”, describing one complex instruction to be run over and over again.

What makes it useful is that A) the input/output could interface with an external memory device (e.g., a hard drive), and B) one can write programs in this table-language which read in and execute programs in some other programming language.

And this, fundamentally, is what a computer is, in the abstract. One particular giant table like that, designed to implement some very simple programming language, on top of which even further abstractions are built. All a computer does, all day long, is think variants of “If I’m in state […] and my input is […], then I should move to state […] with output […]”.

In practice, one designs a computer to have built-in hardware support for certain particular operations of interest, such as addition, carried out a jillion times faster than by naive lookup table. And this is best understood by looking at something like Ruminator’s link, though all it amounts to is that hardware can be designed to carry out binary addition the same way humans would. But, still, at the most abstract level, a general purpose computer is just a finite state machine hooked up to memory, that happens to be running an interpreter for a usable low-level programming language.

1s and 0s tell it what to do enough 1s and 0s will make a computer just like hydrogen given enough time turns into people

Sorry, yeah, those ended up in there as a byproduct of the way the machine was supposed to work. To explain, I’ll need to answer the following comment first:

The pairs consist of {value, address} not because the “address” part tells you where the value is, but rather, the “address” part tells the machine where the value is supposed to be put.

So all instructions are of the form “Put this value in that box.”

(I should have made it clear, by the way, that all the “special boxes” are numbered in with the “non-special” boxes. So the five “arithmetic boxes” might just be boxes two through six, for example.)

So, having explained that, I can now tell you the reason for the three “special boxes” after the input box. To get anything out of the input box, the way the machine is designed, you’re going to have to give it an instrucion like:

“Put (the value equal to the address of the input box minus one) into (the value equal to the address of the instruction box).”

The machine will then grab whatever’s in the input box (once there’s something in there). But now what? Right now it’s stuck in the state which grabs a value out of a box. It now increments the instruction box, and goes on to the box after the input box, and goes into the state where it puts whatever value it just grabbed into the box referred to by the value stored in the present box. (I.e. the box after the input box). So it does that. Now what? It needs a new instruction. That’s what the next two “special boxes” are for–they will typically have had placed into them an instruction repointing the “instruction box” to the right place in the program.

So here’s a program, in “english”, for incrementing an input value by one. Assume the instruction box is box 0, the arithmetic boxes are boxes 1 through 5, and the input handling boxes are boxes 6 through 9.

Assume a “1” in box 1 sets the calculator to the “addition” state.

So then, store the following instructions in the following boxes:

10, 11: put the value 1 in box 1 <—We’re adding
12, 13: put the value 1 in box 2 <—We’re adding one to something.
14, 15: put the value 3 in box 7 <—Upon input, the inputted value goes into the box for the other operand. (Box 3 is the box for the second operand)
16, 17: put the value 21 in box 8 <—see next
18, 19: put the value 0 in box 9 <—the instruction box will be set to return us to the instruction beginning at address 21, once the input process is finished. (That 21 will be incremented to 22, putting us in the right place.)
20, 21: put the value 5 in box 0 <—start executing from box 6 (the input box) in order to get the needed input (this needs to say 5 since what’s in the instruction box will get incremented (i.e. to 6).)
22: start here whatever mechanism the machine has for “ending” the execution of a program. Forgot to think of that… probably would just be a matter of putting a special value in a special box.

In other words:

1, 1, 1, 2, 3, 7, 21, 8, 0, 9, 5, 0, (end)

I think I fell into the idea of a machine with two states because I had the idea (probably mistaken) that fundamentally, all a processor does is put values in addresses, and I figured that somewhere in the architecture of the processor there had to be a distinction made between values meant to refer to addresses and values meant to simply stand as values. I figured the way to do this would be to have two different machines, in a sense, interacting together–one to handle values as values, the other to handle values as addresses. Or, instead of two machines, a single machine with two states, which amounts to the same thing.

Probably a fundamentally mistaken idea though…

I’d just like to say that wooden adding machine that Ruminator posted is awesome.

Your idea reminds me of a known theoretical design for a computer architecture: the single instruction computer. It might interest you.

Some CPUs have done this kind of thing. The Motorola 680x0 family, for example, has two classes of registers: one for addresses, and one for data (integers and bit vectors). The allowed operations for each class are different, though with some overlap. However, there’s no strict enforcement of these value types, because nothing actually stops you from using addresses as data or vice-versa.

On any CPU (well, those that I’m familiar with), it’s the individual instructions themselves that determine what a particular value will be used for. If you arrange to have the CPU execute an instruction that stores 12 into location 1000, when location 1000 is off-limits to your program, or holds some other value that was really important to keep track of — well, your program just shot itself in the virtual foot. The CPU architecture itself doesn’t prevent reckless instructions from being executed, or at least attempted.

(CPUs with MMUs (memory management units) have mechanisms for protecting chunks of memory from this kind of rogue behavior. But that’s a huge topic of its own, and doesn’t bear directly on the basics of how a computer works.)

Besides what Bytegeist said, it’s a common computer concept that a numerical value (which is all that is stored anywhere, anytime) is interpreted according to the context.

Example: the number 65 might mean:

[ul][li]in a text file, the letter “a”[/li][li]as a color value, representing the amount of red, “a little red”[/li][li]as a sound value, it means your speaker will push out about this much instead of pulling back this much[/li][li]as part of a date, representing the final 2 digits of a year (1965)[/li][li]as part of a time, the number of seconds elapsed since the start[/li][li]as part of a vector, the angle of a line[/li][li]as a cartesian corordinate, the X value[/li][li]as a counter, the number of events[/li][li]as a relative jump instruction, telling computer to execute an instruction 65 bytes ahead…[/ul]So the context is all-important.[/li]
An intellectual exercise for nerds is to write a program that rewrites itself (assuming that the CPU isn’t restricted in the use of addresses). Then it becomes a neat trick to generate an instruction, store it somewhere, and execute it later if it’s still there.

Only nerds could appreciate this, of course.

If I understand what you mean, this is known as the Harvard architecture (as opposed to the Von Neumann architecture).

Damn CISC machines! (Seriously, it is cheating a bit to merge an arithmetic operation and BR into one instruction.)

You can design any logic with just NAND gates. You can pretty much code anything with
a move, an Add, a Negate, and a conditional branch instruction. (And you don’t need the move if the destination of the ADD can be memory.)

You certainly don’t need interrupts. The machine I learned to program on, the LGP 21 did not have them, and had a very simple instruction set. It was an accumulator based architecture, and was a bit overly complex in having a multiply instruction.

Ooo, we used to dream of having a multiply instruction! Would’ve been a palace to us.

Yep, following the links in that article it appears I’m onto some version of Transport Triggered Architecture, which doesn’t apparently resemble much how the processors in our desktop computers work.

You are right, of course. But then, you don’t really need very much of anything; you don’t even need any built-in arithmetic instructions, not even addition or subtraction, for example, except insofar as they speed things up. But, yeah, I probably shouldn’t have bothered mentioning interrupts; on reflection, they’re no more vital to understanding the basics of computer execution than myriad other modern details I left unmentioned.

(Also, I’m really on the other side of things from the low-level hardware details, so I appreciate corrections to anything I’ve gotten wrong or utterly misguided)