Types of Programming Language and Assembly
The paradigms you must compare, Little Man Computer, addressing modes, and how to answer a question that asks you to justify a paradigm.
Paradigms
Procedural
Programs are sequences of instructions grouped into procedures and functions, operating on data passed between them. Control is expressed with sequence, selection and iteration.
- Straightforward to learn and to follow
- Well suited to problems that are naturally a series of steps
- Becomes difficult to manage as programs grow, because data and the code acting on it are separate
- Examples: C, Pascal, and Python used procedurally
Object oriented
Data and the methods that operate on it are bundled into objects created from classes.
- Encapsulation means an object controls its own state and can guarantee it stays valid
- Inheritance removes duplication by placing shared behaviour in a superclass
- Polymorphism lets code work with many types through one interface, so new types need no changes to existing code
- Scales well to large systems and large teams
- Adds overhead and complexity that is not worth it for a small script
- Examples: Java, C#, Python, C++
Assembly and low level
- One assembly instruction generally corresponds to one machine instruction
- Direct control over registers and memory addresses
- Highly efficient in both speed and memory
- Difficult to write, read and maintain, and specific to one processor architecture
- Used for device drivers, embedded systems with severe constraints, and performance critical routines
Declarative and functional
The programmer specifies what is wanted rather than how to compute it. Functional languages avoid changing state, which makes concurrency safer and behaviour more predictable. SQL is the declarative language you will meet most often.
Link it to the scenario. A large system built by a team, with many similar entities and likely future extension, points to object oriented. A short data processing script points to procedural. A device driver with tight timing points to assembly.
- Procedural: steps grouped into procedures that call each other.
- Object oriented: data and methods bundled into objects made from classes.
- Declarative: say what is wanted, not how to compute it.
- Assembly: one instruction for each machine instruction, with direct control of the hardware.
Little Man Computer and addressing modes
The LMC instruction set
| Mnemonic | Code | Meaning |
|---|---|---|
INP | 901 | Input to the accumulator |
OUT | 902 | Output from the accumulator |
LDA | 5xx | Load the contents of address xx into the accumulator |
STA | 3xx | Store the accumulator at address xx |
ADD | 1xx | Add the contents of address xx to the accumulator |
SUB | 2xx | Subtract the contents of address xx from the accumulator |
BRA | 6xx | Branch always to xx |
BRZ | 7xx | Branch to xx if the accumulator is zero |
BRP | 8xx | Branch to xx if the accumulator is zero or positive |
HLT | 000 | Halt |
DAT | Declare a data location |
Reading two numbers and outputting the larger
INP
STA first
INP
STA second
SUB first // second minus first
BRP secondbig // if zero or positive, second is larger or equal
LDA first
OUT
HLT
secondbig LDA second
OUT
HLT
first DAT
second DATCounting down from a number
loop LDA count
BRZ done
OUT
SUB one
STA count
BRA loop
done HLT
count DAT 5
one DAT 1Keep a table with the accumulator, each data location and the output. Work one instruction at a time and never skip a pass of a loop.
Addressing modes
The addressing mode determines how the operand should be interpreted.
| Mode | Meaning | Example |
|---|---|---|
| Immediate | The operand is the value | ADD #5 adds the literal 5 |
| Direct | The operand is the address of the value | ADD 60 adds the contents of address 60 |
| Indirect | The operand is the address of a location holding the address of the value | ADD (60) follows the pointer stored at 60 |
| Indexed | The value's address is the operand plus the contents of an index register | Used for stepping through arrays |
Immediate is fastest since no memory access is needed, but the value is fixed at compile time. Indirect allows pointers and dynamic data structures. Indexed makes array traversal efficient, because only the index register changes between elements.
Little Man Computer
Assemble and run LMC programs and watch the fetch decode execute cycle happen. This tool needs JavaScript, so turn it on to use it. Everything you need to answer the exam question is explained in the text above.
- Memory is a row of numbered mailboxes. Each holds a three digit number, an instruction or data.
- An instruction is an opcode, the first digit, and an operand, the last two digits. 107 means ADD with mailbox 07.
- The program counter, instruction register and accumulator do the work, and the input and output trays connect to the outside.
- Immediate: the operand is the data.
- Direct: the operand is the address of the data.
- Indirect: the operand is the address of the address of the data.
- Indexed: add the index register to the operand to get the address, which is useful for arrays.
Key terms you must be able to define
- Paradigm
- An approach to structuring and thinking about a program, such as procedural or object oriented.
- Procedural
- A paradigm structuring programs as sequences of instructions grouped into procedures.
- Object oriented
- A paradigm bundling data with the methods that operate on it, into objects created from classes.
- Declarative
- A paradigm specifying what is required rather than how to compute it.
- Assembly language
- A low level language with a near one to one correspondence to machine code.
- Little Man Computer
- A simplified model processor and instruction set used to teach assembly programming.
- Immediate addressing
- The operand is the value itself.
- Direct addressing
- The operand is the address of the value.
- Indirect addressing
- The operand is the address of a location holding the address of the value.
- Indexed addressing
- The address is the operand plus the contents of an index register.
Flashcards
Say the definition out loud before you flip the card. Retrieving it from memory is what builds recall, reading it again is not.
Reach the top grade on this topic
- Justify a paradigm against the scenario, naming a specific feature such as inheritance removing duplication
- Trace LMC code with a full table showing the accumulator, memory and output
- Distinguish all four addressing modes and give a use for each
- Explain indexed addressing in terms of array traversal, which is what it exists for
- Say why assembly is used despite being hard, rather than treating it as obsolete
Mistakes that cost marks here
- Saying object oriented is always better. For a fifty line data script it is unnecessary overhead.
- Confusing direct and indirect addressing. Indirect follows a pointer to a second address.
- Tracing LMC in your head instead of keeping a table.
- Forgetting that BRP branches when the accumulator is zero or positive, not strictly positive.
- Saying assembly is obsolete. It remains essential for drivers and tightly constrained embedded work.
Assembly language has a near one to one relationship with machine code, but that one instruction can still be modified by the addressing mode, which changes whether the operand is a value, an address, or an address of an address.
Knowledge check
STA 42 do?Exam-style questions
Five written questions in the style of the real paper. Write a full answer, then let the marker check it against the mark scheme.
- LIn immediate addressing the operand is the value to be used
- LIn direct addressing the operand is the address where the value is stored
- LImmediate needs no memory access so is faster, but the value is fixed
In immediate addressing the operand contained in the instruction is the actual value to be operated on, so an instruction to add five carries the number five within it. In direct addressing the operand is instead a memory address, and the value to be used is whatever is currently stored at that address, so the processor must perform a memory read before it can carry out the operation. Immediate addressing is therefore faster, since no memory access is required, but the value is fixed at the moment the program is written and cannot change while it runs, whereas direct addressing works with data that varies during execution.
- LThe address used is the operand plus the contents of an index register
- LThe base address of the array stays fixed within the instruction
- LOnly the index register needs to change to move to the next element
In indexed addressing the effective address is calculated by adding the operand in the instruction to the contents of a dedicated index register. This maps directly onto how an array works: the operand holds the base address of the array and the index register holds the position within it. To move from one element to the next, only the index register has to be incremented, and the same instruction can then be executed repeatedly inside a loop without any modification. This is far more efficient than the alternative of generating a different instruction for every element or performing separate arithmetic to calculate each address.
- LRecommends object oriented
- LEncapsulation keeps data with the methods that act on it and protects its validity
- LInheritance places shared behaviour in a superclass so it is written once
- LA new account type is added as a subclass without changing existing code
- LPolymorphism lets existing code work with new types through the same interface
- LThese properties make a large system maintainable by a team over time
The object oriented paradigm is the right choice here, principally because of how the system will change over its lifetime. Encapsulation bundles each account's data together with the methods that operate on it and restricts direct access from outside, which means the rules governing what a valid account looks like live in one place and are enforced automatically, rather than being scattered through the codebase where a single developer forgetting a check could corrupt the data. Inheritance addresses the requirement for regularly added account types directly: everything common to all accounts is written once in a base Account class, and each specific type becomes a subclass that inherits that behaviour and overrides only what differs. This eliminates duplication, so a correction to shared logic is made in one place and applies everywhere. Polymorphism then means that existing code which processes accounts continues to work unchanged when a new subclass appears, because it calls the same methods and each object supplies its own implementation. Adding a new account type therefore involves writing one new class rather than finding and editing every conditional statement in the system, which is exactly what a procedural design would require. Over a system maintained by a team for many years, these properties are what keep the cost of change roughly constant instead of rising steeply as the codebase grows.
- LThe first input 7 is stored at address 90
- LThe second input 3 is stored at address 91
- LThe accumulator is loaded with 7 and 3 is subtracted
- LThe output is 4
The first INP instruction places the value 7 into the accumulator, and STA 90 stores it at address 90. The second INP replaces the accumulator contents with 3, and STA 91 stores that at address 91. LDA 90 then loads the contents of address 90, which is 7, back into the accumulator, and SUB 91 subtracts the contents of address 91, which is 3, leaving 4 in the accumulator. OUT displays the accumulator, so the output is 4, and HLT stops the program.
- LGives a valid situation such as a device driver or a tightly constrained embedded system
- LAssembly gives direct control over registers, memory and timing
- LDisadvantage: it is far harder to write, read and maintain
- LDisadvantage: it is specific to one processor architecture so it is not portable
Assembly language would be preferred when writing a device driver or the control software for an embedded system with severe constraints. In those situations the programmer needs direct control over specific hardware registers and memory locations, and needs to know exactly how many cycles a sequence of instructions will take, neither of which a high level language and its compiler can guarantee. Assembly also allows the code to be made extremely compact, which matters when the whole program must fit into a few kilobytes of ROM. The disadvantages are substantial. Assembly is far harder to write, read and maintain than a high level language, because a single statement in a high level language may correspond to a dozen assembly instructions with no self documenting structure, so development takes much longer and errors are both more likely and harder to find. It is also written against one specific processor's instruction set, so the code is not portable and moving to different hardware means rewriting it entirely rather than recompiling.