Differences:
Basis System Software Application Software
Software designed to perform
Software that manages hardware and
Definition specific user tasks
system resources
To control and operate the computer
To help users perform tasks like
Purpose system
writing, browsing, etc.
Users do not interact directly most of the
User
time Users directly interact with it
Interaction
Runs independently and supports other
Dependency software Depends on system software to run
Starts when the computer is turned on
Execution Runs when the user opens it
Operating System, Compiler, Assembler,
Examples MS Word, Google Chrome, VLC
Drivers
Differences:
Compiler Interpreter
Translates the entire program at once Translates and executes the
into machine code. program line by line.
Execution is faster after compilation Execution is slower because
because code is already translated. translation happens during runtime.
Displays all errors after compiling the Displays errors one by one as they
whole program. occur.
Generates object code or executable
Does not generate object code.
file.
Program must be compiled before Program is executed directly without
execution. separate compilation.
Requires more memory to store object Requires less memory since no
code. object code is stored.
Device Driver – Simple Explanation
A device driver is a program that helps your computer talk to hardware
devices.
Your operating system (like Windows) cannot directly understand how every
device works, so it uses a driver as a translator.
👉 In simple words:
Device driver = Translator between OS and hardware
How it Works (Very Easy Idea)
You give a command → (like print a file)
OS sends that command → to device driver
Driver converts it → into device-specific instructions
Hardware understands and performs the task
Real-Life Example (Best for Memory)
Think of it like:
👉 You (OS) → Translator (Driver) → Foreign person (Hardware)
Without the translator, communication is not possible.
Why Device Drivers are Important
Without drivers, hardware will not work properly
They help in controlling devices like printer, mouse, etc.
They ensure smooth communication between software and hardware
Every device needs its own driver
Examples of Device Drivers
Printer Driver → helps in printing
Keyboard Driver → detects key presses
Mouse Driver → controls pointer movement
Display (Graphics) Driver → shows visuals on screen
Sound Driver → handles audio
Network Driver → connects to internet
Order and Role of System Programs (From Program Development to
Execution)
When a program is developed and executed, a series of system programs
work one after another to convert the source code into a form that the
computer can understand and run. Each system program has a specific role
in this process, starting from writing the program to its final execution.
The first step is the editor, which is used by the programmer to write and
modify the source code. It provides a simple interface to type, edit, and save
the program. The code written here is called the source program and is
usually written in a high-level or assembly language.
Next, the program goes to the macro processor. The macro processor looks
for any macros (short names for a group of instructions) and replaces them
with their actual code. This process is called macro expansion and helps in
reducing repetition and making the program easier to write.
After that, the program is given to the compiler. The compiler translates the
high-level language program into lower-level code (assembly or intermediate
code). It also checks for errors in the program such as syntax and semantic
errors. If errors are found, they are reported so the programmer can correct
them.
Then, the assembler takes the assembly language code and converts it into
machine code (object code). Assembly language uses symbolic instructions,
which are translated into binary code that the computer can understand.
Once the object code is generated, the linker combines it with other object
files and required library functions. It resolves external references and
produces a single executable file that is ready to run.
Finally, the loader loads this executable file into the main memory (RAM). It
allocates memory, adjusts addresses if needed, and transfers control to the
program so that execution begins.
Differences:
Top-Down Parsing Bottom-Up Parsing
It starts from the start symbol and It starts from the input string and tries
tries to derive the input string. to reach the start symbol.
It builds the parse tree from root to It builds the parse tree from leaves to
leaves. root.
It uses rightmost derivation in
It uses leftmost derivation.
reverse.
It is simpler and easier to implement. It is more complex but more powerful.
It may require backtracking in some
It does not require backtracking.
cases.
It detects errors later during parsing. It detects errors earlier.
Examples include recursive descent Examples include shift-reduce and LR
and predictive parsing. parsing.
Code Optimization in Compilers
Code optimization is the process of improving the intermediate or target
code generated by the compiler so that the program runs faster and uses
less memory, without changing its output. It removes unnecessary
instructions and improves efficiency. The main aim of optimization is to
produce better and faster machine code.
Need for Code Optimization
Code optimization is important because it improves the performance of a
program. Optimized code executes faster and uses fewer resources like
memory and CPU. It removes redundant calculations and unnecessary
instructions, making the program more efficient. This is especially useful in
large programs and systems where performance is critical.
Types of Code Optimization Techniques
1. Machine Independent Optimization
This type of optimization is done before code generation and does not
depend on any specific machine. It works on intermediate code and improves
the logic of the program. It is applicable to all systems.
2. Machine Dependent Optimization
This optimization is done after code generation and depends on the
hardware or processor. It improves the final machine code using features like
registers and instruction sets. It is specific to a particular machine.
3. Constant Folding
Constant folding evaluates constant expressions at compile time instead of
runtime. This reduces computation during execution and improves speed.
Example: 5 + 3 → 8
4. Constant Propagation
Constant propagation replaces variables that have constant values with their
actual values. This simplifies expressions and reduces computation.
Example: a = 10 → b = a + 5 → b = 10 + 5
5. Common Subexpression Elimination
This technique avoids repeated calculations of the same expression. The
result is calculated once and reused.
Example: a*b used multiple times → computed once
6. Dead Code Elimination
Dead code elimination removes instructions that do not affect the output of
the program. This reduces unnecessary operations.
Example: unused variables or overwritten values
7. Loop Optimization
Loop optimization improves the performance of loops by reducing repeated
calculations inside loops. It makes execution faster.
8. Peephole Optimization
Peephole optimization examines a small set of instructions and replaces
inefficient ones with better ones. It reduces code size and improves
performance.
Example: removing ADD R1, 0
Abstract Syntax Tree (AST)
An Abstract Syntax Tree (AST) is a tree representation of the structure of a program or
expression.
In this tree, internal nodes represent operators (like +, −, *, /) and leaf nodes represent operands
(variables or constants).
It shows the logical structure of the expression without unnecessary grammar details.
AST is mainly used in compilers for semantic analysis and code generation.
Directed Acyclic Graph (DAG)
A Directed Acyclic Graph (DAG) is a graph representation of an expression that avoids
repeated computations.
Common sub-expressions are stored only once and shared, making the program more efficient.
It has no cycles and a node can have multiple parents.
DAG is mainly used in compilers for code optimization.
Role of Finite Automata (FA) in Compilation Process
Finite Automata (FA) play an important role in the lexical analysis phase of
a compiler. Lexical analysis is the first phase where the source program is
broken into tokens such as keywords, identifiers, operators, and constants.
FA is used to recognize patterns in the input string. It reads the source
code character by character and checks whether the sequence of characters
forms a valid token. For example, it can identify whether a word is a valid
identifier or a number.
There are two types of FA used: Deterministic Finite Automata (DFA) and
Non-Deterministic Finite Automata (NFA). Usually, NFAs are first
designed from regular expressions and then converted into DFAs for efficient
implementation in lexical analyzers.
FA helps in:
Identifying tokens correctly
Removing white spaces and comments
Detecting lexical errors (invalid tokens)
Example
If the input is:
int a = 10;
FA helps recognize:
int → keyword
a → identifier
10 → number
Forms of Intermediate Code used by Compiler
Intermediate code is a representation of a program between high-level
language and machine code. It is simple, machine-independent, and easy to
optimize. There are different forms of intermediate code used by compilers.
1. Three-Address Code (TAC)
Three-address code is a simple form where each instruction has at most
three operands (two inputs and one result). It breaks complex expressions
into smaller steps using temporary variables. This makes code easy to
understand and optimize.
Example:
t1 = b-c
t2 = a * t1
t3 = t1 * d
t4 = a + t2
t5 = t4 + t3
2. Quadruples
Quadruples represent three-address code using four fields: (operator,
operand1, operand2, result). It is easy to store and modify during
optimization. Each instruction is stored as a separate record.
Example:
o arg arg resul
p 1 2 t
- b c t1
* a t1 t2
+ t2 t3 t4
3. Triples
Triples are similar to quadruples but do not use temporary variables. Instead,
they use the index of the instruction as a reference. This reduces the need
for extra storage.
Example:
Inde o arg arg
x p 1 2
0 - b c
1 * a (0)
2 + (1) d
4. Indirect Triples
Indirect triples are an improved version of triples. They use a separate
pointer table to refer to instructions. This allows easy rearrangement of
instructions without changing references.
Functions of Loader
A loader is a system program that loads the executable program into main
memory and prepares it for execution. It performs several important
functions to ensure the program runs correctly.
Allocation
Allocation is the process of assigning memory space to the program. The
loader decides where the program and its data will be placed in memory so
that it can execute properly.
Linking
Linking connects different modules and resolves external references. If a
program uses functions or variables from other files, the loader links them
together to form a complete program.
Relocation
Relocation adjusts the addresses of instructions when the program is loaded
into memory. If the program is not loaded at its original location, the loader
updates the addresses so that execution is correct.
Loading
Loading is the process of copying the program from secondary storage (disk)
into main memory (RAM). Once loaded, the program is ready for execution.
Transfer of Control
After loading, the loader transfers control to the starting address of the
program so that execution begins.
Loader Schemes
1. Absolute Loader
In this scheme, the program is loaded at a fixed memory location. The
addresses are already specified, so no relocation is needed.
Limitation:
It is not flexible because the program must be loaded at a specific location
and cannot be moved.
2. Relocating Loader
This loader can load the program at any memory location. It modifies
addresses during loading to suit the new location.
Limitation:
It is more complex and requires extra processing time.
3. Linking Loader
This loader performs both linking and loading. It combines multiple object
modules and loads the final executable into memory.
Limitation:
Loading time increases because linking is done at load time.
4. Dynamic Linking Loader
In this scheme, libraries are loaded only when needed during execution. This
saves memory and allows shared libraries.
Limitation:
Execution may be slower because linking happens during runtime and
dependencies may cause issues.
Types of Macros
Macros are used to avoid writing the same code again and again.
Instead of repeating instructions, we give them a name (macro), and the
macro processor replaces that name with actual code.
1. Simple Macro
A simple macro is a macro that does not take any parameters (inputs).
It contains a fixed set of instructions that always remain the same. Whenever
the macro is called, the same instructions are inserted into the program. This
is useful when the same code is repeated many times.
👉 Example:
If you define a macro to increment a value, every time you call it, the same
increment code is added.
2. Parameterized Macro
A parameterized macro is a macro that accepts inputs (parameters).
These parameters are given when the macro is called, and they are used
inside the macro body. This makes the macro flexible, because the same
macro can work with different values.
👉 Example:
Instead of writing separate code for adding different numbers, one macro
can be used with different inputs like ADD 5, 10 or ADD 2, 3.
3. Conditional Macro
A conditional macro is a macro that uses conditions like IF and ELSE. It
checks a condition and expands different code depending on whether the
condition is true or false. This helps in decision-making during macro
expansion.
👉 Example:
A macro can print “POSITIVE” if a number is greater than zero, otherwise
print “NEGATIVE”.
4. Nested Macro
A nested macro is when one macro is used inside another macro. This
means a macro can call another macro during its execution. It is useful for
organizing large programs and reusing multiple macros together.
👉 Example:
A macro for “PROCESS” can internally call another macro like “DISPLAY”.
Data Structures Used in Macro Expansion
During macro processing, some tables are used to store and manage macro
information.
1. MNT (Macro Name Table)
MNT stores the names of all macros defined in the program. Along with the
name, it also stores a pointer to where the macro definition is located in MDT.
When a macro is called, the macro processor first checks MNT to see if it
exists. It helps in quickly finding the macro.
👉 Easy idea:
MNT = List of macro names
2. MDT (Macro Definition Table)
MDT stores the actual code (body) of the macro. Each macro definition is
stored line by line in this table. When a macro is called, the macro processor
takes the instructions from MDT and replaces the macro call with those
instructions.
👉 Easy idea:
MDT = Actual macro code
3. ALA (Argument List Array)
ALA stores the values (arguments) passed to the macro during a call. It
matches actual values with the formal parameters defined in the macro. This
helps in correctly replacing parameters in the macro body.
👉 Easy idea:
ALA = Values given to macro
Types of Macros
Macros are used to reduce repetition of code by giving a name to a group
of instructions. Different types of macros provide flexibility and control.
🔹 Simple Macro
A simple macro is a macro that does not take any parameters (inputs).
It contains a fixed set of instructions, so every time you call it, the same code
is inserted.
There is no change in values or behavior during different calls.
👉 It is mainly used when the same code is repeated many times.
Example:
MACRO INCR
A=A+1
END
INCR
1. Parameterized Macro
A parameterized macro is a macro that takes inputs called parameters.
These parameters are passed when the macro is called and are used inside
the macro body. This allows the same macro to be reused with different
values, making programs more flexible and reducing code duplication.
👉 Example:
MACRO ADD X, Y
A=X+Y
END
ADD 5, 10
👉 Expansion:
A = 5 + 10
👉 Easy idea: One macro, many uses with different values.
2. Nested Macro
A nested macro is a macro that contains another macro inside it or calls
another macro. This means one macro can use another macro during its
execution. It helps in organizing large programs into smaller reusable parts
and improves modularity.
👉 Example:
MACRO DISPLAY
PRINT "HELLO"
END
MACRO MAIN
DISPLAY
END
MAIN
👉 Expansion:
PRINT "HELLO"
👉 Easy idea: Macro inside a macro.
3. Conditional Macro
A conditional macro is a macro that uses conditions like IF, ELSE to decide
which code to expand. It checks a condition and generates different outputs
depending on the result. This adds decision-making ability to macros.
👉 Example:
MACRO CHECK X
IF X > 0
PRINT "POSITIVE"
ELSE
PRINT "NEGATIVE"
END
CHECK 5
👉 Expansion:
PRINT "POSITIVE"
👉 Easy idea: Macro that can take decisions.
Types of Assembly Language Statements
Assembly language programs are made up of different types of statements.
Each type has a specific role in defining operations, storing data, and
controlling the program. The three main types are Imperative Statements,
Declarative Statements, and Assembler Directives.
1. Imperative Statements (IS)
Imperative statements are used to perform actual operations such as
arithmetic calculations, data transfer, and control operations. These
statements are directly translated into machine code and executed by the
processor. They form the main logic of the program.
👉 Example:
ADD A, B
SUB X, Y
MOV R1, R2
👉 Importance:
These statements are important because they perform all the actual work of
the program, like calculations and data movement.
2. Declarative Statements (DL)
Declarative statements are used to define variables and allocate
memory. They may also assign initial values to variables. These statements
do not perform operations but help in storing data used by the program.
👉 Example:
X DS 1 ; Reserve 1 memory location
Y DC 5 ; Store value 5
👉 Importance:
They are important because they provide storage for data, without which the
program cannot store or process values.
3. Assembler Directives (AD)
Assembler directives are instructions given to the assembler to control the
assembly process. They do not generate machine code but help in
organizing the program, setting memory locations, and defining the start and
end of the program.
👉 Example:
START 100
ORIGIN 200
END
👉 Importance:
They are important because they control how the program is assembled and
executed, including memory allocation and program structure.
Data Structures Used in Assembler
An assembler uses different data structures (tables) to store and manage
information while converting assembly language into machine code. These
tables help in identifying instructions, storing symbols, handling constants,
and resolving references.
1. MOT (Machine Opcode Table)
MOT stores all the machine instructions like ADD, SUB, LOAD, etc.
Each instruction is stored with its opcode and details.
The assembler uses MOT to convert mnemonics into machine code.
In simple words, it is like a dictionary of instructions
Mnemon Opcod Lengt
ic e h
ADD 01 2
SUB 02 2
MOV 03 2
Mnemon Opcod Lengt
ic e h
👉 Importance:
It helps the assembler convert symbolic instructions into actual machine
instructions.
2. POT (Pseudo Opcode Table)
POT stores assembler directives like START, END, ORIGIN.
These are not real instructions and do not generate machine code.
They help control the assembly process and program structure.
In simple words, they give instructions to the assembler, not the CPU.
Pseudo-
op
START
END
ORIGIN
👉 Importance:
It helps in controlling program structure, memory location, and execution
flow.
3. ST (Symbol Table)
ST stores all symbols (labels and variables) with their addresses.
It is mainly built during Pass 1 of the assembler.
It helps replace symbol names with actual memory addresses.
In simple words, it is a name-to-address mapping table.
👉 Example:
Symb Addre
ol ss
X 100
LOOP 120
Symb Addre
ol ss
👉 Importance:
It helps in resolving symbol references and generating correct machine code.
4. LT (Literal Table)
LT stores literals (constant values like =5, =10) used in the program.
Each literal is assigned a memory location by the assembler.
It helps in managing and accessing constant values efficiently.
In simple words, it is a table of constant values with addresses.
Liter Addre
al ss
=5 200
=10 204
👉 Importance:
It helps in handling constants efficiently and assigning them proper memory
locations.
5. FRT (Forward Reference Table)
FRT stores symbols that are used before they are defined in the program.
These are called forward references. The assembler temporarily stores them
and resolves them later when their definitions are found.
👉 Example:
If a statement uses symbol X before it is defined, it is stored in FRT until X is
assigned an address.
👉 Importance:
It helps in handling forward references, especially in single-pass assemblers.
Macro Processor
Definition
A macro processor is a system program that processes macro definitions
and macro calls in a program. It replaces macro names with their actual set
of instructions, a process called macro expansion. This helps reduce
repetition and makes programs shorter and easier to write.
Functions of Macro Processor
1. Macro Definition Processing
The macro processor reads and stores macro definitions written in the
program. It identifies the macro name and its body and stores them in tables
like MNT (Macro Name Table) and MDT (Macro Definition Table). This allows
the macro to be used later when called.
2. Macro Call Recognition
The macro processor scans the program and detects macro calls. When it
finds a macro name in the program, it checks in the MNT to confirm that it is
a valid macro. This step ensures that macros are correctly identified.
3. Macro Expansion
Macro expansion is the main function of the macro processor. It replaces the
macro call with the actual instructions stored in MDT. This means the macro
name is replaced by its full code in the program.
4. Parameter Handling
If the macro has parameters, the macro processor handles them using ALA
(Argument List Array). It matches the actual values given in the macro call
with the formal parameters in the macro definition. Then it replaces them
correctly in the macro body.
5. Maintaining Tables
The macro processor maintains important data structures such as MNT, MDT,
and ALA. These tables help in storing macro names, definitions, and
arguments. They ensure efficient processing and correct expansion of
macros.
Example (Simple Idea)
If a macro is defined as:
MACRO ADD X, Y
A=X+Y
And called as:
ADD 5, 10
It will expand to:
A = 5 + 10
1-Pass Assembler
A 1-pass assembler processes the entire program only once. While
reading each line, it assigns addresses, updates the symbol table, and
generates machine code at the same time. It is faster because it scans the
program only once.
However, it has difficulty handling forward references (when a symbol is
used before it is defined). To solve this, it may use extra structures like a
forward reference table.
👉 Key Idea:
One scan → fast but complex
2-Pass Assembler
A 2-pass assembler processes the program two times.
In Pass 1, it reads the program, assigns addresses, and builds the
symbol table.
In Pass 2, it uses this information to generate the final machine code.
It easily handles forward references because all symbols are already known
before code generation.
👉 Key Idea:
First collect → then generate