Assembly & the Fetch-Execute Cycle
What one instruction actually does, and why a function call needs a stack frame.
Questions
Easy / Med / Hard
Your accuracy
Every instruction a processor runs, whether it came from hand-written assembly or a C compiler, goes through the same cycle, over and over, billions of times a second.
Fetch, decode, execute. Fetch reads the next instruction from memory at the address the program counter (PC) holds. Decode figures out what that instruction's bits mean -- which operation, which registers, which addressing mode. Execute does it: the ALU computes, a register updates, a memory address is read or written. Then the PC advances (or, for a jump or branch, gets overwritten with a new address) and the cycle repeats. Everything a processor does, from a single ADD to running an entire operating system, is this loop running continuously.
Registers are the processor's own local variables -- a small, fixed number of storage locations built directly into the CPU, orders of magnitude faster to access than RAM because there's no memory bus involved. An assembly instruction like ADD R1, R2, R3 (add the contents of R2 and R3, store the result in R1) operates entirely on registers; getting a value from memory into a register is a separate LOAD instruction, and getting it back out is a STORE -- this load/store discipline is exactly why "how many memory accesses does this take" is a meaningful question in low-level code.
A function call needs somewhere to remember where to return to and where its local variables live, and that's the stack frame. Calling a function pushes a return address (where execution resumes afterward) onto the stack, then typically the caller's frame pointer, then space for the callee's local variables. The calling convention is the agreed rule for how arguments get passed -- in registers, on the stack, or some mix -- and who's responsible for cleaning the stack up afterward; different platforms and compilers disagree, which is exactly why calling a function compiled with the wrong convention corrupts the stack instead of erroring cleanly.
This is also the mechanism a stack buffer overflow exploits. A local array lives in the current stack frame, below the saved return address in the typical layout. Write past the end of that array with no bounds check, and you're overwriting the return address itself -- so when the function returns, execution jumps to whatever address you wrote there instead of back to the caller. That's the entire mechanism behind classic stack-smashing attacks, and it's the concrete reason "always bounds-check a buffer" is not academic advice in this domain.