A stack pointer tracks where a program is in its call stack. Each function call creates a stack frame holding state for that call. If the function calls another function, its frame remains available while the new call runs. When the newer call returns, the earlier one can resume. This last-in, first-out pattern is why the structure is called a stack. section 6.3.1
In a running program, the stack pointer is a CPU cursor for the current stack frame. Frames can differ in size and provide room for arguments, a return location, and local variables not allocated on the heap. As calls add frames, this stack pointer decreases in value; as functions return, it increases. section 6.3.1
A stack pointer in a small CPU emulator
A simpler emulator represents the stack as 16 address slots and keeps its stack_pointer as an array index. Its CALL operation saves the address at which execution should resume, increments the index, then jumps to the function. RETURN decrements the index, retrieves that saved address, and resumes execution there. Unlike the stack-frame cursor above, this index increases on a call: the two representations use different directions. Returning when the index is zero is a stack underflow. section 5.7.4
Listing 5.28 Adding the call() and ret() methods
41 fn call(&mut self, addr: u16) {
42 let sp = self.stack_pointer;
43 let stack = &mut self.stack;
44
45 if sp > stack.len() {
46 panic!("Stack overflow!")
47 }
48
49 stack[sp] = self.position_in_memory as u16; #1
50 self.stack_pointer += 1; #2
51 self.position_in_memory = addr as usize; #3
52 }
53
54 fn ret(&mut self) {
55 if self.stack_pointer == 0 {
56 panic!("Stack underflow");
57 }
58
59 self.stack_pointer -= 1;
60 let call_addr = self.stack[self.stack_pointer]; #4
61 self.position_in_memory = call_addr as usize; #4
62 }
Follow the assignments in call() and ret(): saving a return address and moving stack_pointer make it possible to return to the caller. section 5.7.4



