Part 1: Assembly Code (x86)
This program defines a string in the data segment and uses a subroutine to count its
characters until it hits a "null terminator" (0).
Code snippet
section .data
; The string to measure. '0' at the end is the null terminator.
myString db "Assembly Programming", 0
length dd 0 ; Variable to store the result
section .text
global _start
_start:
; --- Main Program ---
mov esi, myString ; ESI (Source Index) now points to the start of the string
call GET_LENGTH ; Jump to the subroutine
mov [length], eax ; After RET, EAX holds the count. Store it in memory.
; Exit logic
mov eax, 1 ; Syscall for exit
xor ebx, ebx ; Return 0
int 0x80
; --- Subroutine: GET_LENGTH ---
GET_LENGTH:
push esi ; PRESERVATION: Save the starting pointer on the stack
xor eax, eax ; Clear EAX to use it as our counter (set to 0)
COUNT_LOOP:
cmp byte [esi], 0 ; Check the character at the current memory address
je FINISH_COUNT ; If it is '0' (null), we've reached the end
inc eax ; Increment the character count
inc esi ; Increment the pointer to the next character address
jmp COUNT_LOOP ; Repeat the loop
FINISH_COUNT:
pop esi ; RESTORATION: Restore the original pointer for the main program
ret ; RETURN: Pop the return address from the stack to the EIP
Technical Summary
This summary explains the interaction between hardware registers, memory, and the stack.
I. The Anatomy of a Subroutine Call (CALL)
The CALL instruction is the mechanism that allows for modularity. In the String Length
program, when CALL GET_LENGTH is executed:
● The Program Counter: The CPU identifies the address of the next instruction (mov
[length], eax).
● Stack Storage: This address is "Pushed" onto the stack. This is the most critical step;
without it, the CPU would have no way to return to the main program, leading to a
"runaway" execution where the CPU processes random data as code.
● Branching: The Instruction Pointer is updated to the location of the GET_LENGTH label.
II. Stack Operations: PUSH and POP
In this assignment, the Stack acts as a Temporary Storage Buffer.
● Preserving State: Inside the subroutine, we modify the ESI register to move through the
string. However, the "Main" program might need the original starting address of the string
later. By using PUSH ESI at the start and POP ESI at the end, we ensure the subroutine is
"transparent"—it leaves the registers exactly as it found them (except for the result in
EAX).
● LIFO Logic: You must explain that the Stack is a Last-In, First-Out structure. If you push
multiple registers, you must pop them in the exact reverse order. Failure to do so will
result in the RET instruction pulling the wrong data from the stack, causing a system crash.
III. The RET Instruction and Stack Integrity
The RET instruction is the exit gate. It performs a "Pop" operation, but instead of moving data
into a general-purpose register, it moves the value directly into the Instruction Pointer.
● The Handshake: A successful RET depends entirely on the "Stack Pointer" (ESP) being at
the exact same position it was when the CALL started. This is why balancing PUSH and
POP is the most important skill in assembly language programming.
IV. Logic of String Processing in Assembly
Unlike higher-level languages (like Python or Java) where strings are objects with a .length()
property, in Assembly, a string is just a sequence of bytes in memory.
● Null Termination: The program relies on the "ASCII Null" (value 0) to know where the
string ends.
● Pointer Arithmetic: The code demonstrates how the CPU uses a pointer (ESI) to fetch
data from memory, compares that data, and then increments the pointer to "walk"
through the data segment. This is the fundamental way all low-level data processing
occurs.
Conclusion
Conclude by highlighting how the combination of CALL, RET, and Stack operations allows a
programmer to create complex, readable, and reusable code. By separating the "Counting
Logic" from the "Main Logic," we create a "function" that can be used multiple times
throughout a program, significantly reducing code duplication and errors.