Coding is a strange process. Sometimes you start with a blank space, fill it with symbols and numbers, and eventually a program appears at the end. Other times you take your or someone else’s work and modify it, changing it, correcting it, or extending it.
The medium that you do this in can be varied. It could be as simple as a command line, a special “Integrated Development Environment” or “IDE” or it could be a fancy drag and drop within a special graphical programming application such as “Scratch“. It could even be within another application such as a spreadsheet or database program. I’ve tried all of these.
The thing that is common to all these programming environments is that they run inside another program – the command line version (obviously enough) requires that the command line program, which receives the key presses necessary to build the new program and interprets them, must be running, and the command line program itself runs in another program.
Which itself runs in yet another program, and so on. So, is it programs all the way down? Well, no. One is tempted to say “of course not”, but it is not immediately apparent what happens “down there”.
What happens down there is that the software merges into the hardware. At the lowest software level the programs do things like read or write data values in specific bits of hardware, and move or copy data values from one place to another. One effect of a write, move or copy might be to cause the hardware to add two numbers together.
Also, the instruction may cause the hardware to select the next instruction to be executed depending on the data being executed. It may default to the next sequential instruction, or it may execute an instruction found elsewhere.
An instruction is just a number, an entity with a specific pattern within the computer. It has a location in the hardware, and is executed by being moved to another location in the hardware. The pattern is usually “binary code” or a string of ones and zeroes.
In the hardware component called a CPU, there are several locations which are used for specific purposes. Data may be found there or it may be copied there. At certain times the data will be executed or processed. Whatever the purpose of the data, it will travel as a train of zeroes and ones though the hardware, splitting, merging and being transformed by the hardware. It may also set signals and block or release other data in the CPU.
The designers of the CPU hardware have to design this “train system” so that the correct result is achieved when an instruction is processed. Their tools are simple logic circuits which do things like merge two incoming trains of zeroes or ones or split one train into two or maybe replace all the zeroes by ones and vice versa. I think that it is fairly accurate to say that the CPU designers write a program using physical switches and wires in the hardware itself.
So we have reached the bottom and it is not programs, but logic gates, and there are many layers of programming above that to enable us to write “Hello World” on our monitor devices. It’s an elegant if complex system.
Of course we can’t program in logic gates to achieve the “Hello World” objective. We have many layers of programs to help us. But how do the various layers of programs work?
The designers of the CPUs hardware program the device to perform certain actions when a special code is dropped into a special location. There are only 100 to 200 special codes that a CPU recognises and they are patterns of zeroes and ones as described above.
Obviously it would be tedious and error prone to actually code those special codes (and the associated data locations, known as addresses) directly into the computer, so small programs were written in the special codes to recognise mnemonics for the codes and these were then used to write more complex programs to automatically create the strings of codes and addresses necessary to create the lowest level code.
This process is known as boot-strapping, as ever more complex programs are built, culminating in what are known as high level languages, where little or no knowledge of the hardware is required. When a new type of machine comes along, using a different type of hardware, it is even possible to write the programs at a high level on different hardware so that the software can be “ported” to the new system.
The highest level of programs are the ones that actually do the work. These programs may be something like a browser which fetches data and displays it for the user, but a browser is created by a programmer using another program called a compiler. A compiler’s function is to create other programs for the end user.
However to write or modify a compiler you need another program, or maybe a suite of programs. Code is usually written in a human readable form called “source code”. An editor program is needed to read, modify and write the source code. A compiler is needed to change the human readable code to machine executable code and a linker is usually required to add all the bits of code together and make it executable.
All these programs have their own source code, their compiler and linkers, and it may seen as if we have an issue with all programs requiring their own source code and so on. It seems that we have an infinite regress again. But once we have an editor, a compiler and a linker we can compile any program we like, and we don’t need to know the details of the hardware.
And what is more those programs, editor, compiler and linker, can created using an existing compiler, editor and linker on another different machine and simply transferred to the new one. In some ways every compiler, editor and linker program can trace its ancestry back to a prototype written at the dawn of the computer age.