Programming a machine means providing the CPU with a series of instructions to facilitate a specified task. The only language machines can understand, known as the machine code, is a language comprising binary instructions that a machine can respond to directly. The 0 and 1 are the alphabets of machine language. The series of 0s and 1s are the only thing a machine can make sense of, and thus, don’t need to be translated. It is the lowest level of programming that can be possibly done and is strictly hardware-dependent, meaning different CPUs may have different machine codes for the same task. Programming in machine language can be very tedious and prone to error as the code is not human readable. It is still possible to write machine code, but it is not practical, and you will rarely (or never) see anyone doing it.
So, if we can’t provide instructions directly in machine code, how do we go around programming machines? The answer is programming languages.
Programming Language
Imagine a scenario where you are a start-up founder, and you have to meet with an investor who is Japanese and pitch your business idea in front of him. But then you find out he cannot speak English. What will you do? Well, the answer is simple. You hire a translator.
A Programming Language is a structured language engineered in such a way to allow us to communicate with the machines. The programmer writes instructions in the programming language of his choice, and then it is translated to the machine code by a program called translator to be understood by the computer. This is how the programming of machines works in the modern context.
Here, the question may arise if the instructions are going to be translated to machine code; why there is a need for a programming language at all. Why can’t one just program in their native language like English and then convert it to binary?
This is because natural human languages are ambiguous. Natural languages have a unique property called 'Context sensitivity', which means depending on the context, the meaning changes. Take English, for instance. How many times have you encountered identical words and sentences meaning differently based on context, facial expression, body language, etc.? A translator program needs hard and fast rules while translating and context-free language to generate exact binary instructions. This is the reason why programming languages were developed. But it is not impossible to make a translator that can convert the natural language to machine-readable binary.
With the advancement of technology, programming languages are becoming more and more easily and sometimes resemble natural language. This resemblance can be used to classify programming language into two categories: High-level Languages and Low-level Languages.
But the question always is how friendly? The level of a programming language can be a relative term. For example, C/C++ is higher than Assembly Language and Binary but is lower than Python. The languages which are more programmer-friendly are known as high-level languages, and those that are machine-friendly are known as low-level languages. They are generally said to be mid-level languages.
Translators
We know that a translator is a program that converts the human-readable language to machine-readable code. There are three types of translators:
- Compilers,
- Interpreters, and
- Assemblers
Let’s see how they differ one-by-one.
A Compiler converts code written in a high-level programming language to low level-programming language (e.g., machine code or object code). It converts the whole source code in one session and reports errors detected after the conversion. The compilation is slower as the entire code is translated at once and then stored in the memory. Compilers are platform-dependent and so are classified by the platform on which their generated code executes. This is known as the target platform. Compilers are also processor-dependent. A Native Compiler’s output is intended to run on the same type of machines and OS, and a Cross Compiler’s output is designed to run on a different platform.
An Interpreter, like a compiler, also converts a high-level programming language to a low-level programming language. Still, one instruction at a time and thus, it is faster than a compiler. It reports any errors that occurred immediately, so it is handy for the purpose of debugging. Interpreters are processor-independent.
Assembly language is a low-level programming language specific to particular computer architecture. Every assembly language is designed for a specific computer architecture, and every assembler has its own assembly language. The assembler converts the language's mnemonics to commands that the particular CPU understands. An Assembler converts assembly language code to even lower-level machine code.
Conclusion
Of course, more steps go on between clicking on “run” and the code being executed. But understanding these basic concepts and having some idea about what might be going is a start. With time, you can build upon these simple concepts and get into more advanced stuff like operating systems, microcontrollers and microprocessors, etc.