Low-level languages: machine code and assembly
Machine code is the only language a CPU can run. Each instruction is a binary pattern, for example 10110001. Part of it says what to do (the opcode) and part says what to do it to (the operand).
Assembly language replaces the binary with short memory words called mnemonics, such as LOAD, ADD, STORE. Each assembly instruction matches one machine-code instruction (a 1 : 1 match).
Why use low-level code?
- Direct control of the hardware, memory and registers.
- Small, fast programs: useful for device drivers, embedded systems (washing machines, car parts) and very time-critical code.
Drawbacks
- Hard to read, write and debug.
- Tied to one type of processor: code for one CPU family will not run on another.
High-level languages
High-level languages (Python, Java, C#, C, JavaScript) use English-like words and maths symbols. One line, such as total = price * 3, may become several machine instructions.
| High-level | Low-level | |
|---|---|---|
| Easy for people? | Yes | No |
| Runs on different CPUs? | Yes, after translation (portable) | No, one CPU family |
| Control of hardware | Less | Direct |
| Must be translated? | Yes (compiler or interpreter) | Assembly: yes (assembler); machine code: no |
Most software today is written in high-level languages because programmers work faster and make fewer mistakes.
Translators: assembler, compiler and interpreter
Assembler
Turns assembly language into machine code, one instruction to one instruction.
Compiler
- Translates the whole high-level program in one go into machine code (an executable file, such as an .exe or an app).
- Reports all the errors it finds at the end; nothing runs until they are fixed.
- The executable runs fast and can be shared without the source code (helps protect the code).
- But you must re-compile after every change, and the executable only runs on the type of machine it was made for.
Interpreter
- Translates one line at a time and runs it straight away.
- Stops at the first error, showing the line: handy while writing and testing.
- No executable is made, so the program runs more slowly and the user needs the interpreter and the source code.
Bytecode and virtual machines
Some languages mix both ideas. Java (and Python behind the scenes) first compile source code into bytecode: an in-between set of simple instructions that is not tied to any CPU. A virtual machine (such as the Java Virtual Machine) then interprets or quickly compiles the bytecode on each device. This gives portability: the same bytecode runs on Windows, Android or Linux, as long as a virtual machine exists for it.
Try it: be the interpreter
Write a 5-line "program" of instructions for a friend (e.g. 1. Stand up. 2. Clap twice. 3. Jumpp. 4. Sit. 5. Smile). First act as an interpreter: read and do one line at a time, and stop at the spelling mistake. Then act as a compiler: read all 5 lines first, list every error, and only act when the list is fixed. Which way found the mistake sooner? Which way ran faster once fixed?
Key formulas and definitions
- Machine code = binary instructions a CPU runs directly
- Assembly → assembler → machine code (1 : 1)
- High-level → compiler → executable (whole program, then run)
- High-level → interpreter → run line by line
- Source → bytecode → virtual machine (portable)
Worked examples
1. A company sells a game but does not want customers to see its source code. Which translator should it use?
A compiler. It produces an executable file that can be shared without the source code, and it runs fast.
2. A student is learning to program and wants to find mistakes quickly. Which translator is better, and why?
An interpreter. It runs one line at a time and stops at the first error, showing where it is, so the student can fix it and try again at once.
3. Give one reason a programmer might write a device driver in assembly language.
Assembly gives direct control of the hardware and registers and produces small, fast code, which a driver needs.
4. Why can the same Java bytecode run on a phone and a laptop with different CPUs?
The bytecode is not machine code for any one CPU. Each device has its own Java Virtual Machine that turns the bytecode into its own machine code.
Common mistakes
- Saying a compiler runs the program line by line. That is an interpreter; a compiler translates the whole program first.
- Thinking high-level code can run on a CPU without translation. Only machine code runs directly.
- Mixing up assembler and compiler: an assembler translates assembly language, not high-level code.
- Saying low-level code is portable. It is tied to one processor family; high-level code is the portable one.