Procedure_Call_Assembly_Explanation (2)
Procedure_Call_Assembly_Explanation (2)
Yes, the runtime stack is just a smart system of filling and emptying
memory — like a controlled version of putting things on a shelf and
then taking them off in reverse order.
It's not just filling and emptying — it's how and why it does it that
makes it essential:
It’s not just memory being added and removed. It’s organised memory:
SUMMARY
When your program begins running, the operating system quietly sets
up a special memory area called the runtime stack — kind of like a
blank notebook, ready to take notes. At first, it's empty, and the ESP
register points to the top of it. Now, the moment your code calls a
procedure or function using something like CALL myFunction, the
runtime stack jumps into action. The first thing it does is save the
return address — the memory location the program needs to go back
to after the function finishes — by pushing it onto the stack. Then,
when the function starts, it creates its own stack frame by saving the
old base pointer with PUSH EBP, then copying ESP into EBP with MOV
EBP, ESP. This stack frame becomes the function’s private workspace,
where it stores any parameters, local variables, or temporarily
saved registers. This whole process is repeated every time another
function is called — each one stacking on top of the last, like a pile of
books. Once the function completes its task, it cleans up: it resets ESP,
restores the old EBP, and uses RET to pop the return address and
return to where it was originally called. All of this happens smoothly,
and the calling code continues like nothing happened, because the
runtime stack handled all the temporary memory and flow control
behind the scenes. Without the runtime stack, your program would have
no way to jump into a function, remember where it came from, or pass
data safely. It plays a silent but critical role in managing function calls,
parameters, return addresses, local memory, and even security —
showing up exactly when needed, doing its job, and then clearing out
quietly until the next call.
✅ Week 4: Runtime Stack
This is where you learn the foundation — what the stack is, how PUSH
and POP work, how memory grows downward, and how values are
temporarily stored.
🧠 Focus here:
What a stack is
How ESP, EBP work
Stack frames
Why stack matters for function calls, parameter passing, and
temporary storage
🧠 Focus here:
When your program runs and executes instructions like ADD, SUB, CMP,
or TEST, it doesn’t just blindly move on — it leaves behind clues in
something called the FLAGS register. This tiny but powerful register
holds flags like CF (Carry Flag), OF (Overflow Flag), ZF (Zero Flag),
and SF (Sign Flag) — each telling you something about the result of the
last instruction. For example, if you subtract two numbers and the result
is zero, the ZF is set. If the result is negative, the SF is set. If you overflow
the signed range, the OF is set. These flags become the eyes and brain
of the CPU — and your key tool to write decisions in assembly. You
don’t get if, else, or while directly like in C. Instead, you use these
flags with jump instructions like JE (Jump if Equal), JL (Jump if Less),
or JA (Jump if Above) — and each of these checks one or more flags to
decide whether to jump. If ZF is set, you might jump to the “equal” case.
If CF is clear and ZF is clear, you might jump to a “greater than” case for
unsigned numbers. That’s why different flags apply to signed vs
unsigned comparisons. You also learn about instructions like XOR
(great for encryption and clearing registers), TEST (non-destructive
AND for checking bits), and how to convert binary digits to ASCII using
OR, AND, and clever bit masking. All of this ties directly into what you
learned about the runtime stack and procedure calls — because
when you're deep in a function or loop, these flags are how your
program makes decisions, branches, loops, or ends. Without them,
your program wouldn’t know if subtraction gave 0, or if an overflow
happened — and you’d have no control flow at all. So, in the big picture,
FLAGS are like post-it notes left behind after every operation, telling
your code what just happened, and what to do next — silently guiding
the jump, loop, or return.
Use Jump
Meaning
With Instruction
Jump if equal /
ZF JE, JZ
zero
JA, JB, JAE, For unsigned
CF, ZF
JBE comparisons
OF, SF, JG, JL, JGE, For signed
ZF JLE comparisons
✅ Practice tip: Write small programs using CMP + jumps to
simulate if/else logic.
🧪 4. TEST Instruction
asm
CopyEdit
TEST AL, 01h ; Checks if least significant bit is 1
JNZ OddLabel ; Jump if not zero (bit was 1)
CALL myProc
✅ Why? This isolates the function’s workspace. Each function gets its
own 'slice' of the stack.
* If an attacker overwrites the RA on the stack, they can redirect EIP and
hijack the program.
* That’s how buffer overflow exploits happen.
Summary Flowchart
[main starts]
↓
[CALL myProc]
↓
[Push EIP (return address)]
↓
[Jump to myProc]
↓
[myProc pushes EBP, sets up stack frame]
↓
[myProc does logic – access args, local vars]
↓
[Clean up – POP EBP, RET]
↓
[Return address restored into EIP]
↓
[main resumes after CALL]
By now you know how the runtime stack manages function calls using
CALL and RET. But as your code grows, repeating all the PUSH, POP, and
register saving becomes messy. That’s where Assembly macros and calling
conventions come in — to automate and clean up the mess. It starts with
declaring procedures properly using PROTO (like a blueprint), and defining
them with PROC. Inside a PROC, you can use the USES keyword to
automatically save and restore registers. Instead of writing PUSH EAX,
PUSH EBX, then popping them later, you just write PROC USES EAX
EBX, and it’ll handle all that for you. If your function needs space for local
variables, you use LOCAL, which reserves memory by adjusting ESP. When
the procedure is called, instead of using plain CALL, you can now use
INVOKE. It’s like a super version of CALL that also pushes the
arguments for you — but only if you’ve defined the prototype using
PROTO. It’s cleaner, safer, and avoids mismatching parameter order.
To manage all this properly, macros like ENTER and LEAVE are used.
ENTER sets up the stack frame (just like PUSH EBP, MOV EBP, ESP, SUB
ESP, n), while LEAVE tears it down (MOV ESP, EBP, POP EBP). Then
RET n returns and also cleans up parameters if needed. This is especially
useful when using the stdcall calling convention (used in C++), where the
called function cleans the stack, unlike cdecl (used in C) where the caller
does the cleanup.In big projects, you’ll separate each procedure into its
own .asm file, keeping your main function clean. All these .asm files are
linked together at compile time. That’s how you make modular, reusable,
low-level code just like in high-level languages — but with full control. In
short, Lecture 7 teaches you how to use procedures properly and cleanly
with macros, calling conventions, and stack discipline — so your assembly
code becomes maintainable, scalable, and professional.
🧠 Lecture 8: Story – From Bit Twiddling to String
Mastery
In low-level assembly, there’s no shortcut — if you want to multiply,
divide, or handle strings, you need to know how the CPU moves and
manipulates bits. That’s where Lecture 8 steps in. It starts with shift
operations — SHL, SHR, SAR, and ROL, ROR — which move bits left or
right. These are the building blocks of fast multiplication (using SHL)
and division (using SHR). For signed numbers, you use arithmetic shift
(SAR) so the sign bit is preserved. Logical shift (SHR) is for unsigned
data. Rotation instructions like ROL and ROR let you move bits around
without losing them, perfect for tasks like reading individual bits (e.g.,
flags or hardware control bits). Once you master that, you move on to
multiplication and division. Even though CPUs have MUL and DIV,
Lecture 8 shows how early CPUs used clever combinations of shifts and
adds to simulate multiplication — a skill still useful on RISC or
embedded systems. Then you dive into how numbers are stored:
EDX:EAX is used to hold 64-bit results for division or multiplication,
where EAX gets the quotient and EDX the remainder.