Part Four:
The Stack

    Now should be the time when you are introduced to a flood of instructions that you might never fully commit to memory.  I am hoping instead, that you will be patient and learn fully about all the concepts involved; the instructions introduced below are not necessarily the most useful, nor do they build any foundations for the more important instructions, nevertheless, learning about stacks now will ease greater learning further on.

    A stack has three operations; you can push, pop and peek a stack.  The idea is that you can push an object onto a stack and recover it later with a pop.  Stacks ergonomically stack objects in such a way that whatever was put on last is the first to come off.  A long time ago, a team of computer “geeks” observed the quaint behavior of a lunch room tray stack wherein a removed tray seemed to “pop” the stack up some and a replaced tray seemed to “push” the stack back down.  This same idea was applicable to computer stacks; the vocabulary was written accordingly.  If an object is the first to go in, it is the last to go out; an object last to go in is the first to come out.
    In assembly language, all pushing / popping is done with register pairs.  You can push a register pair, say HL, and then pop it later as another register pair, say IX.  Pushing a register pair has no affect on the contents of that pair, the instruction <push hl> (with that syntax) puts the value of HL on the stack (once on the stack it becomes a nonspecific word, no registers are associated with it’s value) meanwhile preserving the contents of HL.  <push de / pop bc>.  Now, the value that came from HL is still on the stack, again in the foremost position; DE has been copied to BC and whatever HL was will be loaded into the next register pair to pop.  Let’s say that instead of popping the stack value we choose <ld hl,$5a92> and then <pop de>.  Our result is the previous HL in DE, $5a92 in HL and the stack pointing to whatever word was first pushed but hasn’t yet seen a corresponding pop.  Note: it’s very important that you remember to pop everything you have pushed.  If you leave a different stack than the one you began with, the calculator will crash.  Also, you can push / pop the accumulator by pushing / popping it with the F register (flag register ... to be explained in the next section) -- <push af / pop af>.
    A peek must be simulated by adjoining pop / push instructions since the Z80 doesn’t have a built in peek command.  <pop hl / push hl> will, in effect, use HL to “look at” the top of the stack and leave the stack unmodified.

    The stack must have an area of allotted memory somewhere in our 64k.  The calculator uses the area starting at $fbff and working its way down in memory.  The register SP (Stack Pointer) is what tells the Z80 where the top of the stack is located.  SP is merely an address.  Whenever a word is pushed, SP decrements by two; whenever a word is popped, SP increments by two (remember that a word is two consecutive bytes).
    A strange instruction dealing with stacks is <ex (sp),hl>, which exchanges the top stack word and HL.  After this execution, the top of stack ((SP) is a way of referring to the address addressed indirectly by SP) will have the previous contents of HL and HL will contain what was previously stacked.  Another instruction you might use if you need to change the location of the stack, is <ld sp, operand>, which loads SP with either a register pair or an explicit value (must be an address).  There are no instructions to load a register pair with SP, however, making it difficult to know where to return SP to if you move the stack.

    Here is another register to burden you with: PC, the program counter. This register keeps track of where the Z80 is currently executing in memory.  PC holds a pointer to the current instruction and is incremented on that instruction’s completion.  If you want to change the location of the program counter in order to, say, jump over code, then you can load PC with an address by using the jump instructions.  <jp $e1c0> will move execution to $e1c0.  If you have a label called shift_left_carried_bits at $e1c0 then you are able to type <jp shift_left_carried_bits> as well.  You should always have a well named label at every address you jump to and use that label in your instruction syntax.
    A relative jump (127 bytes forward or 128 bytes back) can be accomplished with jr.  <jr shift_left_carried_bits> can be used when $e1c0 is within the relative jumping range.  Even though jp can always be used in the place of jr, the opcode of jr is only two bytes in length compared to the three bytes of jp, the absolute jump.  Using jr wherever possible is generally considered good style ... don’t worry about counting bytes to see if jr can be used; the compiler will tell you when its range is exceeded.
    <jp (hl)>, <jp (ix)>, <jp (iy)> are three more instructions that do exactly what you’d imagine them to.  Each one loads PC with the address in the operand register, jumping to that location.

    You may end up having a portion of code in your program that needs to be run several times.  Instead of repeating that code, you have an ability to partition the repeated area into a call.  A call is like a jump in that it loads the program counter with a given address, but it differs in that a call is powerful enough to return to the calling instruction once a <ret> instruction is encountered.  A coded call might look like this:

 ld hl,stored_number          ;hl points to a RAM location we named “stored_number”
 ld a,(hl)                            ;ld a with the value at that location
 call _next_integer             ;call a routine that
 ld a,(hl)                            ;get the new value of hl

 ...                                    ;more code

next_integer:
 inc a                                ;increment a by one
 ld (hl),a                           ;put incremented number back into (hl) or (stored_number)
 ret                                   ;go back

    This is a ridiculous scrap of code that doesn’t really serve any practical function.  You can see, however, what an implemented call will look like.  There is no limit to the length of a called sub-routine, nor are any registers destroyed by the call or the return.  There is one thing of which you must be cautious when writing calls, and that is: pushing and popping without returning the stack to what it was before the call was made is enough to crash the calculator.  This is a result of the Z80’s pushing the current program counter address when a call is found and then popping that address back into PC when a return is found.  This doesn’t mean you can’t use the stack inside a call, but don’t push before a call and pop within a call expecting to get out what you pushed.
    An assembly program is itself a call.  When the ROM decides to execute an asm program, it makes a call to _asm_exec_ram ($d748); an assembly program is terminated when it reaches the outer most return.