Assembly Instructions
We covered a lot so far that built the foundation for working with assembly, but so far we haven’t learned about the different instructions that are used to write an assembly program. Remember our warmup code? We used the mov and add instructions there. In this section, we dive deeper into those instructions.
You may say: “Why now?” Well, let me give you some context. To work with assembly language, we need 2 things (there’s more in reality, but for now these two are enough):
Registers & Memory
Instructions
You can see we already learned about different types of registers and memory addressing modes. The only thing left was instructions—so that’s what we tackle now.
Registers and memory are where we store data, but what do we do with that data? That’s where instructions come in. Using instructions, we work with the data and perform operations to solve specific problems. I hope that makes sense?
When we talk about instructions, it’s better to address one thing early: because assembly language is a low-level representation of machine language, it is tightly coupled to the CPU design. There are many CPU vendors out there, and each of them has its own way of doing things. Because of that, there is no single universal set of assembly instructions.
Let’s suppose we are very familiar with assembly programming. Still, one day you might come across an instruction that looks completely new. Why? Because that instruction is valid only for a specific vendor’s processor. In that case, we need to check that vendor’s documentation. Don’t worry—it’s not as hard as it sounds. If you understand the basics of assembly (what we are learning now), then adapting to a new instruction set is much easier.
Now, here’s the good news: while some instructions are vendor-specific, there are common categories of instructions that exist in almost every assembly language. And these are enough for us to start writing assembly programs as beginners. These categories are:
Data Transfer Instructions
Arithmetic and Logic Instructions
Control Flow Instructions
System Control Instructions
Data Transfer Instructions
Explanation of all the data transfer instruction are written below:
Move Instruction (
mov):The
MOVinstruction, as discussed earlier, is used to transfer data from a source to a destination.Source: where the data comes from. This can be a constant (immediate value), a register, or a memory location/variable.
Destination: where the data goes. This can be a register or a memory location.
Important to remember: you cannot directly move data from one memory location to another (memory-to-memory move). Data must go through a register first.
Logical Flow
When the CPU sees a
MOVinstruction, here’s what it does step by step:Reads the source operand (value, register, or memory).
Copies that value into the destination operand.
The source remains unchanged — only the destination gets updated.
Syntax
mov <Destination>, <Source><Destination>= Required. Where the value will be placed (register or memory).<Source>= Required. What value is being copied (constant, register, or memory).
Examples
mov ax, 6 ; Move immediate value 6 into AX mov var1, bx ; Move value from BX into memory location var1 mov bx, var1 ; Move value from memory location var1 into BX mov ax, bx ; Move value from ax into bx - very fast
Tip: If both operands are registers, the transfer happens very fast (since no memory or buses are involved). If one operand is memory, the CPU has to go through system buses, which makes it slower.
PUSH & POP
I admit I haven’t explained the stack much yet. In the stack register section we touched on this topic a little bit, and I mentioned we’d go through it later because we hadn’t yet reached the point where we work with the stack.
Right now, we are still learning the basics — and the stack is a topic of its own kind. We will cover it fully later, but for now go and revise the stack register section again, because there I covered a little bit about the stack. Here, we’ll just focus on two instructions used to work with it.
Push Instruction (PUSH)
The PUSH instruction is used to store data onto the stack.
Syntax
push <Source>Source: Required. Can be a register or a memory operand.
Destination: Always the stack (handled internally by the CPU, not by us).
Logical Flow
The Stack Pointer (SP) register decreases by 2 (in 16-bit mode).
The CPU copies the source operand into the memory location now pointed to by SP.
Examples
push ax ; Push AX register value onto stack
push bx ; Push BX register value onto stack
push var1 ; Push the value of memory variable var1 onto stackPop Instruction (POP)
The POP instruction is used to retrieve data from the top of the stack and place it into a destination.
Syntax
pop <Destination>Source: Always the stack (handled internally by the CPU).
Destination: Required. Can be a register or a memory operand where the popped value will be stored.
Logical Flow
The CPU reads the value stored at the memory location currently pointed to by SP.
That value is copied into the destination operand.
The Stack Pointer (SP) register increases by 2 (restoring the stack to its previous state).
Examples
pop ax ; Pop the top value from stack into AX
pop bx ; Pop the top value from stack into BX
pop var1 ; Pop the top value from stack into memory variable var1IN & OUT
Just like with the stack, I haven’t explained I/O ports yet. That’s a whole topic of its own, and we’ll study it later when we talk about input/output devices.
For now, you only need to know the instructions that allow us to transfer data between the CPU and I/O ports. Don’t stress about what a port really is right now — we’ll cover that later. Just cram the behavior of these instructions.
In Instruction (IN)
The IN instruction is used to read data from an I/O port into a register.
Syntax
in <Destination>, <Port>Destination: Required. Must be
AL,AX, orEAXdepending on the data size.Port: Required. The I/O port address, which can be an immediate value or stored in
DX.
Logical Flow
The CPU places the port address on the address bus.
Data from that port is read and transferred into the destination register.
Examples
in al, 60h ; Read data from port 60h into AL
in ax, dx ; Read data from the port whose address is in DX into AXNote: Don’t worry if I/O ports sound confusing right now. Later we’ll cover exactly what ports are and how they are used. For now, just remember that IN moves data from a port → into a register.
Out Instruction (OUT)
The OUT instruction is the opposite of IN. It is used to send data from a register to an I/O port.
Syntax
out <Port>, <Source>Port: Required. The I/O port address, which can be an immediate value or stored in
DX.Source: Required. Must be
AL,AX, orEAXdepending on the data size.
Logical Flow
The CPU takes the value from the source register.
That value is sent to the specified port address through the data bus.
Examples
out 60h, al ; Send the value in AL to port 60h
out dx, ax ; Send the value in AX to the port whose address is in DXArithmetic and Logic Instructions
Arithmetic Instructions
These instructions perform mathematical operations on data stored in registers or memory. The CPU’s Arithmetic Logic Unit (ALU) is responsible for executing them.
We’ve already learned about the Flag Registers earlier, where we discussed different flags and their purposes. I mentioned that each arithmetic operation affects certain flags. For better understanding, I recommend revisiting the Flag Registers section before continuing.
Remember: every instruction in this arithmetic category updates the flag registers, so it’s important to be clear on how they work.
We’ll look at the most common ones here.
Add Instruction (
ADD)The
ADDinstruction adds the source operand to the destination operand and stores the result in the destination.Syntax
add <Destination>, <Source>Destination: Required. Can be a register or memory location where the result will be stored.
Source: Required. Can be an immediate value, a register, or a memory operand.
Logical Flow
The CPU reads the values of both operands.
It performs addition (
Destination + Source).The result is stored back into the destination operand.
Flags in the FLAGS register are updated (e.g., Zero Flag, Carry Flag, Overflow Flag).
Examples
add ax, bx ; AX = AX + BX add ax, 10 ; AX = AX + 10 add var1, bx ; var1 = var1 + BX
Subtract Instruction (SUB)
The SUB instruction subtracts the source operand from the destination operand and stores the result in the destination.
Syntax
sub <Destination>, <Source>Destination: Required. The operand where the result will be stored.
Source: Required. The operand to subtract from the destination.
Logical Flow
The CPU reads the values of both operands.
It performs subtraction (
Destination - Source).The result is stored back into the destination operand.
Flags are updated accordingly (Zero Flag, Sign Flag, Carry Flag, Overflow Flag).
Examples
sub ax, bx ; AX = AX - BX
sub ax, 5 ; AX = AX - 5
sub var1, cx ; var1 = var1 - CXIncrement Instruction (INC)
The INC instruction increases the value of the operand by 1. Generally we use it to increase the value of cx register.
We use this inc and dec instruction very often when we deal with LOOP instruction.
Syntax
inc <Operand>Operand: Required. Can be a register or memory operand.
Logical Flow
The CPU reads the operand value.
It adds 1 to the operand.
The result is stored back in the same operand.
Note: Flags are updated (but the Carry Flag is not affected).
Examples
inc ax ; AX = AX + 1
inc bx ; BX = BX + 1
inc var1 ; var1 = var1 + 1Decrement Instruction (DEC)
The DEC instruction decreases the value of the operand by 1.
Syntax
dec <Operand>Operand: Required. Can be a register or memory operand.
Logical Flow
The CPU reads the operand value.
It subtracts 1 from the operand.
The result is stored back in the same operand.
Again flags are updated (but the Carry Flag is not affected).
Examples
dec ax ; AX = AX - 1
dec bx ; BX = BX - 1
dec var1 ; var1 = var1 - 1Note: These arithmetic instructions are the foundation of programming in Assembly. They are simple, but later when we build loops, counters, and condition checks, you’ll see just how much ADD, SUB, INC, and DEC are used everywhere.
Logic Instructions
Logical instructions perform bitwise operations on data stored in registers or memory. These are executed by the CPU’s Arithmetic Logic Unit (ALU), just like arithmetic instructions, but instead of numbers, they operate on the individual bits of the operands.
These instructions are essential for tasks like masking bits, setting or clearing flags, and checking conditions efficiently.
AND Instruction (
AND)The
ANDinstruction performs a bitwise AND operation between the source and destination operands. The result is stored in the destination operand. Each bit in the result is1only if the corresponding bits in both operands are1.Syntax
and <Destination>, <Source>Destination: Required. Can be a register or memory operand.
Source: Required. Can be an immediate value, a register, or a memory operand.
Logical Flow
The CPU reads both operands.
Performs a bitwise AND (
Destination & Source).Stores the result back in the destination operand.
Updates flags (Zero Flag, Sign Flag, Overflow Flag cleared, Carry Flag cleared).
Examples
and ax, bx ; AX = AX & BX and al, 0Fh ; AL = AL & 0Fh (mask lower 4 bits) and var1, ax ; var1 = var1 & AX
OR Instruction (OR)
The OR instruction performs a bitwise OR operation between the source and destination operands. Each bit in the result is 1 if either corresponding bit in the operands is 1.
Syntax
or <Destination>, <Source>Logical Flow
CPU reads both operands.
Performs bitwise OR (
Destination | Source).Stores the result back in the destination.
Updates flags (Zero Flag, Sign Flag, Overflow Flag cleared, Carry Flag cleared).
Examples
or ax, bx ; AX = AX | BX
or al, 80h ; AL = AL | 80h (set the highest bit)
or var1, ax ; var1 = var1 | AXXOR Instruction (
XOR)The
XORinstruction performs a bitwise exclusive OR operation. Each bit in the result is1only if the corresponding bits in the operands are different.Syntax
xor <Destination>, <Source>Logical Flow
CPU reads both operands.
Performs bitwise XOR (
Destination ^ Source).Stores the result in the destination.
Updates flags (Zero Flag, Sign Flag, Overflow Flag cleared, Carry Flag cleared).
Examples
xor ax, bx ; AX = AX ^ BX xor al, 0FFh ; AL = AL ^ 0FFh (flip all bits) xor var1, ax ; var1 = var1 ^ AX
NOT Instruction (NOT)
The NOT instruction performs a bitwise negation (complement) on the operand. Every 0 becomes 1, and every 1 becomes 0.
Syntax
not <Operand>Operand: Required. Can be a register or memory operand.
Logical Flow
CPU reads the operand.
Flips all bits of the operand.
Stores the result back in the same operand.
Flags are not affected.
Examples
not ax ; AX = ~AX
not al ; AL = ~AL
not var1 ; var1 = ~var1Logical instructions are extremely useful for masking, setting, or toggling specific bits without touching the others. For example, AND is commonly used to clear bits, OR to set bits, and XOR to toggle bits. NOT is perfect when you need the complement of a value.
It make more sense when we practically work with it, Right now just try to understand there purpose.
Shift & Rotate Instruction
Shift and rotate instructions are also bitwise operations, but instead of operating on individual bits with AND/OR/XOR, they move bits left or right inside a register or memory operand. These instructions are very handy for tasks like multiplication/division by powers of 2, bit masking, and cyclic bit operations.
SHL / SAL Instruction (
SHL/SAL)The
SHL(Shift Left) andSAL(Shift Arithmetic Left) instructions are essentially the same — they shift all bits in the operand to the left by a specified number of positions. Bits shifted out on the left are discarded, and zeros are filled in on the right.Syntax
shl <Operand>, <Count> sal <Operand>, <Count>Operand: Required. Can be a register or memory operand.
Count: Required. Number of positions to shift (immediate value or
CLregister).
Logical Flow
CPU reads the operand value.
Shifts all bits to the left by
<Count>positions.Fills the vacated rightmost bits with zeros.
Updates flags (Carry Flag, Zero Flag, Sign Flag, Overflow Flag depending on operation).
Examples
shl ax, 1 ; Shift all bits in AX one position to the left (AX * 2) sal bx, 3 ; Shift all bits in BX three positions to the left (BX * 8)
SHR Instruction (SHR)
The SHR (Shift Right) instruction shifts all bits in the operand to the right. Bits shifted out on the right are discarded, and zeros are filled in on the left.
Syntax
shr <Operand>, <Count>Logical Flow
CPU reads the operand.
Shifts bits to the right by
<Count>positions.Fills the leftmost bits with zeros.
Updates flags accordingly.
Examples
shr ax, 1 ; Shift AX one bit to the right (AX / 2)
shr bx, 2 ; Shift BX two bits to the right (BX / 4)SAR Instruction (SAR)
The SAR (Shift Arithmetic Right) instruction also shifts bits to the right, but preserves the sign bit for signed numbers (the most significant bit remains the same).
Syntax
sar <Operand>, <Count>Logical Flow
CPU reads the operand.
Shifts bits right by
<Count>positions.Preserves the sign bit (MSB) for signed numbers.
Updates flags (Carry, Zero, Sign, Overflow).
Examples
sar ax, 1 ; Arithmetic shift right AX by 1
sar bx, 3 ; Arithmetic shift right BX by 3ROL Instruction (ROL)
The ROL (Rotate Left) instruction rotates all bits in the operand to the left. Bits that are shifted out on the left re-enter on the right — it’s a cyclic rotation.
Syntax
rol <Operand>, <Count>Logical Flow
CPU reads the operand.
Rotates bits to the left by
<Count>positions.Bits shifted out from the left re-enter on the right.
Updates flags (Carry Flag may be affected).
Examples
rol ax, 1 ; Rotate AX left by 1 bit
rol bl, 2 ; Rotate BL left by 2 bitsROR Instruction (ROR)
The ROR (Rotate Right) instruction rotates all bits in the operand to the right. Bits that are shifted out on the right re-enter on the left.
Syntax
ror <Operand>, <Count>Logical Flow
CPU reads the operand.
Rotates bits to the right by
<Count>positions.Bits shifted out on the right re-enter on the left.
Updates flags (Carry Flag may be affected).
Examples
ror ax, 1 ; Rotate AX right by 1 bit
ror bl, 2 ; Rotate BL right by 2 bitsTip: Shift instructions are excellent for fast multiplication/division by 2, 4, 8…, while rotate instructions are very useful for cyclic bit manipulation, encryption, and hardware control. Try to understand it — they will appear often in low-level programming and bitmask tasks.
Masking
Masking is the process of applying a bit pattern (called a mask) to a register or memory value in order to selectively modify or check specific bits.
The mask itself is just a binary number, where the positions of 1s and 0s decide which bits of the original value are affected.
A
1in the mask usually means keep or change a bit.A
0in the mask usually means clear or ignore a bit.
Masking is mostly done using logical instructions like AND, OR, XOR, and sometimes TEST.
The different types of masking are:
Selective Bit Clearing (Using AND)
We use the AND instruction with a mask to clear (set to 0) specific bits.
Assembly Example
mov al, 4Ch ; AL = 0100 1100
and al, 0F0h ; Mask = 1111 0000Manual Calculation
AL 0100 1100
Mask 1111 0000
----------------
AND 0100 0000Result: AL = 0100 0000
Selective Bit Setting (Using OR)
We use the OR instruction with a mask to set (force to 1) specific bits.
Assembly Example
mov al, 4Ch ; AL = 0100 1100
or al, 0Fh ; Mask = 0000 1111Manual Calculation
AL 0100 1100
Mask 0000 1111
----------------
OR 0100 1111Result: AL = 0100 1111
Selective Bit Inversion (Using XOR)
We use the XOR instruction with a mask to toggle (invert) specific bits.
Assembly Example
mov al, 4Ch ; AL = 0100 1100
xor al, 0Fh ; Mask = 0000 1111Manual Calculation
AL 0100 1100
Mask 0000 1111
----------------
XOR 0100 0011Result: AL = 0100 0011
Selective Bit Testing (Using AND or TEST)
We can test specific bits by using AND with a mask. If the result is zero, those bits were not set. Otherwise, they were set.
Assembly Example
mov al, 4Ch ; AL = 0100 1100
and al, 08h ; Mask = 0000 1000Manual Calculation
AL 0100 1100
Mask 0000 1000
----------------
AND 0000 1000Result: Non-zero → the 3rd bit (from right) is set.
Alternatively, TEST can be used for the same purpose, but unlike AND, it does not change the operand.
mov al, 4Ch ; AL = 0100 1100
test al, 08h ; Check if 3rd bit is setIf Zero Flag (ZF) = 0 → bit is set.
If Zero Flag (ZF) = 1 → bit is not set.
Masking is therefore a systematic way of clearing, setting, toggling, or checking bits. It is one of the most common operations when dealing with low-level programming tasks.
Control Flow Instructions
Control flow instructions are what make assembly programming more dynamic. So far, we’ve only learned about data and arithmetic or logical operations. That executes instruction after instruction in a line-wise manner.
But programming is not just about straight execution — it’s about making decisions, based on decisions either rejecting or accepting some instructions, temporarily going to different instructions but later coming back, etc.
This is exactly where control flow instructions come into play.
These instructions allow us to alter the normal flow of execution and jump through different sets of instruction blocks, we call it branching in assembly.
Jump to another part of the program.
Execute code only if a certain condition is true.
Repeat instructions in loops.
Call functions and return back when done.
Let’s go step by step, and we start with Jumping instructions. There are two categories of jump instructions available in assembly language.
Unconditional Jumps
Conditional Jumps
Below we dive deep into each of them:
Unconditional Jump (
JMP)The
JMPinstruction transfers execution to a different part of the program, no matter what. It doesn’t care about conditions — it just goes straight to the specified label.Syntax:
jmp <Label>
Label: A label is simply the name of a different branch (or section) of instructions. Inside a label, you can place any instructions you’ve learned so far — or even ones you’ll learn later. When the JMP instruction executes, the CPU jumps to that branch and starts executing from there, instead of continuing with the next instruction in sequence.
Types of Unconditional Jumps
Unconditional jumps are divided into two main categories:
Intrasegment jumps → jump inside the same code segment (only IP changes).
Intersegment jumps → jump to a different code segment (both CS and IP change).
1. Intrasegment Jumps
These jumps remain inside the current code segment. They only update the Instruction Pointer (IP).
Short Jump
Used when the target label is close (within -128 to +127 bytes from the current instruction).
Opcode: EB
Size: 2 bytes → 1 byte for opcode EB, 1 byte for displacement.
Example:
jmp short label1 ; Jump in (-128 to +127 range)
Note: Conditional jumps are always short
Near Jump
Used when the target is within the same segment, but the range is larger than short jump allows.
Opcode: E9
Size: 3 bytes → 1 byte for opcode E9, 2 bytes for displacement.
Example:
jmp near label2 ; Jump in (-32768 to +32768 range)
Fig 4.0: Short and Near Jump
Short and Near Jumps are also called relative jumps because they use relative addressing. That means instead of giving the full address of the label, we only provide the displacement (the distance from the current instruction to the target label). The CPU then adds this displacement to the current IP to calculate the final address on its own.
2. Intersegment Jump
These jumps transfer control to a different code segment. That means both CS (code segment register) and IP (instruction pointer) are updated.
Far Jump
Opcode: EA
Size: 5 bytes → 1 byte for opcode EA, 2 bytes for new IP, 2 bytes for new CS.
Example:
jmp far ptr label3 ; Jump to another code segment

Examples:
jmp start ; Jump unconditionally to label 'start'
jmp exit ; Skip code and go directly to 'exit'
jmp short loop1 ; Example of short jumpNote: When we work with AFD debugger later, you will actually see these opcodes (EB, E9, EA) and the instruction sizes directly. That makes the theory much more clear. For now, just keep in mind how short, near, and far jumps are different.
Conditional Jumps
Conditional jumps are the real deal when writing logic. They depend on the status of flags in the FLAGS register (set by arithmetic/logical instructions).
Remember: We already studied the FLAGS register. Keep that section in mind here, because every conditional jump depends on the status of one or more flags.
Common conditional jumps:
JE / JZ (Jump if Equal / Zero):
This jump changes the flow of instructions if and only if the zero flag (ZF) is 1. That happens when the result of the previous operation (often a comparison) was zero.
JE start ; Jump if ZF = 1 JZ start ; (same as JE)JNE / JNZ (Jump if Not Equal / Not Zero):
Works opposite to JE. It jumps if the zero flag (ZF) is 0, meaning the result of the previous operation was not zero.
JNE loop ; Jump if ZF = 0 JNZ loop ; (same as JNE)JG / JNLE (Jump if Greater):
This jump is taken if the zero flag (ZF) is 0 and the sign flag (SF) equals the overflow flag (OF). Used for signed comparisons where the left operand is greater.
JG greater_case ; Jump if ZF = 0 AND SF = OF JNLE greater_case ; (same as JG)JL / JNGE (Jump if Less):
This jump is taken when the sign flag (SF) is not equal to the overflow flag (OF). Used for signed comparisons where the left operand is less.
JL less_case ; Jump if SF ≠ OF JNGE less_case ; (same as JL)JGE / JNL (Jump if Greater or Equal):
This jump is taken when the sign flag (SF) equals the overflow flag (OF). Used for signed comparisons where the left operand is greater or equal.
JGE check_done ; Jump if SF = OF JNL check_done ; (same as JGE)JLE / JNG (Jump if Less or Equal):
This jump is taken when either the zero flag (ZF) is 1 or the sign flag (SF) is not equal to the overflow flag (OF). Used for signed comparisons where the left operand is less or equal.
JLE exit ; Jump if ZF = 1 OR SF ≠ OF JNG exit ; (same as JLE)
There are many other conditional jumps as well (i.e., JA, JB, JAE, JBE, etc.), especially for unsigned numbers.
For now, remember that they all depend on combinations of Zero Flag (ZF), Sign Flag (SF), and Carry Flag (CF).
CMP Instruction (Compare)
Above we kept mentioning comparison again and again. Now let’s talk about it.
CMP is not a jump itself, but it is always used before conditional jumps. Its acronym is Compare but it doesn’t do it like we normally do. It basically subtracts things like the SUB instruction we learned in the Arithmetic and Logic instructions section but with one difference.
It subtracts the source from the destination internally, but it doesn’t store the result back, it just updates the flags. You may say what the hell, it doesn’t make sense? So I’ll try to explain the purpose with a simple example so the concept becomes more solid.
Working Example
Let’s say we have stored 5 in AX register and we want to compare if they are equal or not with the value placed in BX, let’s say 4. What we know about the cmp instruction: it subtracts things and updates the flags.
We do that here: ax - bx → 5 - 4 = 1 which is not zero, right?
We know the zero flag only becomes 1 if the answer is zero, but in this case it is not. Meaning the zero flag is 0. That’s the whole logic: if our answer is zero (only when both values are the same) then the zero flag becomes 1, which tells us that both numbers are equal. If not, then other flags will get updated based on the result, and we judge things from that. I hope it makes sense?
Combine that knowledge of cmp with the definition of each conditional jump. And see if you can understand how different flags prove the logic of equal, greater, or less.
Now let’s see the syntax and examples of how compare instructions are written.
Syntax
cmp <Destination>, <Source>Examples
cmp ax, bx ; Compare AX with BX
je equal_case ; Jump if equalLOOP Instruction
When solving any problem, we often come to situations where we have to keep going through the same process with different results again and again. Let’s say we are writing a code to find a factorial. As we know, we have to keep multiplying the number with its previous number until the previous number becomes one.
Now let’s engineer this problem: we know the mul instruction is used for multiplication, and we have to find 4!. What we can do is write 4 mul instructions with subtracted 1 values. However, think of it: what if the number is larger or comes from the user? Now how do we deal with it? Should we have to keep writing the same instruction one by one, again and again? Nah, I don’t know about you but if I were you then I’d take Arts instead of Computer Science. 🎨
That’s where LOOP comes as a savior of programmers. The LOOP instruction is designed specifically for iteration to solve these kinds of problems, where we have to execute the same instructions again and again without writing 100 lines.
In assembly language we uses the CX register as a counter. That counts how many iterations are complete, and if it becomes zero that tell loop cycle to get finished.
Syntax
<Label>:
; instructions here
loop <Label>In the above syntax, we first define a label that tells where the loop branch starts. Later we write the actual loop instruction that transfers the control flow to that branch and keeps executing it until the cx register becomes 0.
Autonomy of CX
Decrement
CXby 1.If
CX != 0, jump to<Label>.If
CX == 0, continue with next instruction.
Example
mov cx, 5 ; Repeat 5 times
start_loop:
; some code here
loop start_loopNote: Every execution of LOOP instruction automatically decreases CX by 1 and checks it. That’s why CX is often called the counter register.
CALL Instruction
To understand the call instruction, let’s take an example first like we did in the Loop instruction above.
Imagine we’re solving a problem: we have 5 sets of numbers. For each set, we want to calculate the sum and the average.
The sum is easy — we can just use the add instruction again and again. Each set has five numbers, and we can go through them one by one using a loop.
For the average, we simply take the sum and divide it by the total count (which is 5). We can use the div instruction for that. Simple enough, right?
Well… yes. But here’s the catch: we don’t just have one set. We have to repeat the exact same process for each of the 5 sets. That’s still manageable — five sets, five sums, five averages.
But then the Lord of Numbers appears and says:
“I have thousands of sets waiting for you, and I’ll keep sending more.”
At that point, Sorry, I’m not a computer science student, I love Picasso. 🎨
To handle situations like we have functions, subroutines, or procedures. What we do in a function is branch all of the required steps one time, and later we just pass the data that we have to perform operations on. They perform operations and give the result back. We can reuse it again and again many time as we want.
We pass data in subroutines with the help of either registers or stacks.
In assembly we have the CALL instruction to call a procedure (function). It saves the current instruction pointer (next instruction) onto the stack and jumps to the given label.
Syntax
call <Label>Logical Flow
CPU pushes current IP (address of the next instruction) onto the stack.
Jumps to the procedure at
<Label>.Execute all the instruction in the labeled branch until
retinstruction come.When
retinstructions comes remove the current IP address from stack and jump back to next instruction after thecallinstruction.
Example
call my_func ; Call procedure 'my_func'
...
my_func:
; do something
retRET Instruction
In the above example of the call instruction notice we use ret at the end. The RET instruction does nothing but return control back to the location stored on the stack (the instruction after CALL).
Syntax
retLogical Flow
CPU pops the return address from the stack.
We haven’t learned stacks in detail, I know! Other then (PUSH or POP)
Loads it into IP (instruction pointer).
Execution resumes where it left off.
Here’s the thing: control flow instructions are what actually transform plain instructions into programs.
Without them, our code would just be a boring list of steps. With them, we can write loops, conditions, functions, and even full applications.
It might feel like a lot of new mnemonics to remember (JMP, JE, JNE, CALL, RET, LOOP, …), but trust me — these are the ones you’ll use all the time when coding in assembly.
System Control Instructions
System control instructions are special instructions that deal with the overall behavior of the CPU.
Unlike data transfer, arithmetic, or control flow instructions that directly manipulate registers or memory, these instructions interact with flags, interrupts, and CPU state.
In simple words, these instructions allow the programmer to:
Control how the CPU responds to external events (like hardware interrupts).
Modify or preserve the status of the flag register.
Halt or wait for events.
They’re not used every day like mov or add, but they are very important for system-level programming (think operating systems, hardware drivers, or critical programs that must handle interrupts).
Let’s explore them one by one:
Flag Control Instructions
Flags are super important because they reflect the result of operations and also control how the CPU executes instructions. Sometimes we want to directly set, clear, or toggle these flags.
Here are some flag-related instructions:
STC (Set Carry Flag)
Sets the carry flag (CF) to 1.
Useful when we want to force a carry into arithmetic operations.
Example
stc ; CF = 1CLC (Clear Carry Flag)
Clears the carry flag (CF) (sets it to 0).
Example
clc ; CF = 0CMC (Complement Carry Flag)
Toggles (inverts) the carry flag.
Example
cmc ; CF = NOT CFCLD (Clear Direction Flag)
Sets the direction flag (DF) to 0, meaning string instructions (
MOVS,CMPS, etc.) will process memory from low to high addresses.
Example
cld ; DF = 0 → process forwardSTD (Set Direction Flag)
Sets the direction flag (DF) to 1, meaning string instructions process memory from high to low addresses.
Example
std ; DF = 1 → process backward
Interrupt Control Instructions
Interrupts are signals from hardware/software that temporarily stop the CPU to handle something urgent. For example, when you press a key, the keyboard sends an interrupt.
We can enable or disable these interrupts using the following:
STI (Set Interrupt Flag)
Enables maskable interrupts (IF = 1).
The CPU will now respond to external interrupts.
Example
sti ; Enable interruptsCLI (Clear Interrupt Flag)
Disables maskable interrupts (IF = 0).
The CPU will ignore external interrupts until enabled again.
Example
cli ; Disable interrupts
Remember: STI and CLI don’t affect non-maskable interrupts (NMI). Those are hardware-level and cannot be disabled.
HLT Instruction (Halt)
The
HLTinstruction tells the CPU to stop executing instructions until the next external interrupt occurs.It’s often used in operating systems to save power while waiting for something to happen.
Syntax
hltLogical Flow
CPU halts execution.
Waits until an interrupt or reset occurs.
Resumes execution after the interrupt handler finishes.
WAIT Instruction
The
WAITinstruction pauses the CPU until the Test Pin (external hardware signal) becomes active.This one is rarely used in modern programming, but it’s good to know that it exists.
ESC Instruction (Escape)
The
ESCinstruction is used to hand over control to an external coprocessor (like an FPU — Floating Point Unit).In older systems, math-intensive operations were handled by a separate chip, and
ESCwas the gateway.Syntax
esc <Opcode>, <Operand>LOCK Prefix
The
LOCKprefix isn’t a standalone instruction — it’s used before another instruction to make sure it executes atomically (without interruption).This is very important in multi-processor systems, where two CPUs might try to access the same memory at the same time.
Example
lock inc [var1] ; Increment memory location atomically
Woah, that’s a lot of power packed into these little instructions. Unlike mov or add, we won’t use hlt or cli in every program — but when we do, they control the whole CPU!
Take a breather, sip some more water, and give ourselves a credit: we now know the instructions that control the system itself. That’s some boss-level assembly knowledge!
System Control Instruction are kind of very advance topic, that’s why I covered the absolute basics only. However when you feel confident you can explore more.
Last updated
Was this helpful?