PUSH and POP:
As you may have guessed, push and pop "pushes" bytes on the stack and then takes them off. While you push something, the stack counter shall decrease with 2 (the stack "grows" down, from higher addresses to lower) and then the register pair is loaded onto the stack. While you pop, the register pair is first lifted of the stack, and then SP raised by 2. Note that Push and Pop only operate on words (2 bytes, i.e. 16 bits). You can push (and pop) all register pairs: BC, DE, HL and PSW (Register A and Flags). While you pop PSW, remember that all flags might be changed. You can't push an immediate value. If you desired, you'll have to load a register pair with the value and then push it. Possibly it is worth noting that while you push something, the contents of the registers shall still be the same; they won't be erased or something. And, if you push DE, you can pop it back as HL (you don't require popping it back to the same register where you got it from). The stack is also updated when you CALL and RETURN from subroutines. The PC (program counter which points to the next instruction to be executed) is pushed to the stack and the calling address is loaded to PC.
While returning, the PC is loaded with the word popped from the top of the stack (TOS). So, when is this useful? It is almost always used when you call subroutines. For instance, you have an often-used value stored in HL. You have to call a subroutine that you know will destroy HL (with destroy we mean that HL shall be changed to another value, which you perhaps do not know). Instead of first saving HL in a memory location and then loading it back after the subroutine, before calling you can push HL and directly after the calling pop it back. Of course, it is often better to use the pushes and pops inside the subroutine. All of the registers you know will be changed are often pushed in the beginning of a subroutine and then popped at the end, in reverse order. Do not forget - last in first out. If you desire to only push one 8-bit register, you still need to push its "friend". Therefore, be aware that if you want to store away D with pushing and popping, remember that E shall also be changed back to what it was before. In those of the cases, if you don't desire that to happen, you must try first to change register (try to store up the information in E in another register if you can) or else you need to store it in a temporary variable. Before executing a program, you should keep track of your pushes & pops, since they are responsible for 99% of all computer crashes. For instance, if you push HL and then forget to pop it back, the next RET instruction will cause a jump to HL, which may be anywhere in the ROM/RAM and the computer will crash. However note down it's also a way to jump to the location stored in HL, but then you must really use the JMP instruction, to do the same thing. Push and pop doesn't change any flags, thus you can use them between a compare and jump instructions, based on a condition, which is frequently very useful.