Lesson 5: Program flow and the stack.

This will be one of the most important sections. Without program flow, You don't have much of a program. As you probably already know, the basic program flow is start at the top and go down. What about if we want to skip something? Well, we are going to use a jump. Depending on where you are and where you are going depends on which instruction you are going to use. First try jr. JR will jump to a label that is within 127 bytes of either direction. Each instructon is betwwen 1 and 4 bytes, so if your label is over 127 instructions away, you are not going to get there. In that case you need to use jp. JR is called a relative jump, because it just knows how far away to go from where it is, and jp is an absolute jump, going to a specific address. So, relative jumps are smaller by a byte usually, but they are slower. (Very small amount). So, use jr where you can, and avoid jp. So how do you declare a label? Well, this gets into the beginning of writing some actual code. See, labels are only for the assemblers refernce and don't take up any space, so you can use as many as you want.:

*Remember, I am teaching Usgard here.
** Also take note that a series of three dots just indicates that I am moving down the program somewhere.

Lets start with JR.

MyLabel:
   .
   .
   .
   jr MyLabel

This pogram will continnualy execute whatever code those dots stand for. Lets say that those dots are 500 bytes of code. Now, we are going to have to us jp.

MyLabel:
   .
   .
   .
   jp &MyLabel

What is the & sign for??? That is an Usgard modification, it is called relocation. See, the program never knows where it is in the memory. So, it cant do absolute calls if you only know the realtive address. when you put the & in front of the label when you use jp, Usgard will take care of the rest. This won't work for ZShell, you have to use a special macro, and the line jp &MyLabel would become JUMP_(MyLabel).

Notice also that MyLabel: is not indented and everything else is. This means it is a label. All labels can not be indented, and all other code must be indented. (Also note that there is a colon after declaring a label name)

Now we get to call an ret. call will first push pc and then jp. ret will pop pc.
Huh??

Call does exactly what it says: it calls a section of code and remembers where it came from, and ret goes back. It makes a subroutine. Lets look at some example code.

GameLoop:
   .
   .
   .
   call &Display_Score
   .
   .
   .
   jr GameLoop
   .
   .
   .
Display_Score:
   .
   .
   .
   ret

 You need the & sign in front of the label for call too. I hope you see how this works.

You now know the basics of jr, jp, call, and ret.
But do you know that they can each take another parameter?? When they have this extra parameter, they are conditional jumps, call, and returns.

The IF of assembly

What is the if of assembly? Well, it is a combination of the instruction cp, and the flag register.

Lets say you needed to find out if register b is equal to 5. First you must load it into a, because cp only uses the value in a to check against.

   ld a,b
   cp 5

Now, what it does is compares the two, and looks at what the answer would be if it subtracted them. Then it set or resets certain bits in the F register. We do not need to know which ones, just which conditions are going to be true. Here are the conditions created by cp

I am going to call the value x; so these are the results of cp x.

Z = a and x are equal.
NZ = they are not equal
C = a<x
NC = a>x OR a=x

Now, lets look at the complete version of the 'is b equal to 5 code'

   ld a,b
   cp 5
   jr z,B_Is_5

If b is 5, it will jump to B_Is_5. If it is not, it will just keep going down, executing the next instruction and going down.

so. for the conditional items, heres the syntax:

jr <cond>,<dest>
jp <cond>,<dest>
call <cond>,<dest>
ret <cond>

It goes like this; it cond is true, goto dest.

BTW: If you use ret without calling first, you will most likely end up back in usgard (Usgard calls your string)

Now you can kind of see how loops can be made, use one register as a counter, do your work, inc the reg, and cp to find out if you do the loop again or not.

There is a single instrucion that does all of that for you. It is djnz. It stands for decrement, jump not zero. It uses b for its counter and the total loop must be in relative range.

Lets look at some code.

   ld b,5
Loop:
   call &MyFunc
   djnz Loop
   ld a,5

This will call MyFunc 5 times, and then go on with the resto f the program, starting with ld a,5 (Just there for fun- you next instruction does not have to be ld a,5)

This is basic djnz loop. When you came out of it, b will be zero, because everytime the djnz is reached, a dec b is executed. When it finally becomes 0, it doesnt go to Loop anymore.

What if MyFunc changes b? Well, you are in trouble. That could screw up your loop. What if MyFunc exited, and b was 3 every time? You just got yourself in an endless loop. There is hope! The solution to this problem and many others lie in the stack.

What is the stack? It is a special piece of memory that is used to quickly store data. You communicate with stack through the push and pop instructions. You always get and send data as words, never bytes. What push does is that it takes a register pair or one of the 16 bit regs and puts it on the stack. pop takes a word off the stack and puts it in the register pair that is its parameter. See, the stack obeys the magical LIFO law, which you will hear again if you haven't already. LIFO stands for Last In, First Out. That means when you push something-- anything, the next pop will be receiving that data. Lets look at that loop that we had above, and alter it to be safe if b is changed in MyFunc.

   ld b,5
Loop:
   push bc
   call &MyFunc
   pop bc
   djnz Loop
   ld a,5

Why did I use bc? You can only push 16 bits; you are not allowed to only push 8. Since we need to save b, it is only logical to use bc.

Be careful. push and pop are responable for most calcularot crashes, and are also the hardest to trace.

Tell me, whay would this be bad?

   call &MyFunc
   .
   .
   .
MyFunc:
   .
   .
   .
   push hl
   ret

See, ret does this: pop pc. and call does this: push pc. If hl is pushed, then ret is going to pop off whatever hl was and stick it in pc, causing us to jump wherever hl happened to be pointing to, and most of the time, a crash results.

Lets look at something similar.

   call &MyFunc
   .
   .
   .
MyFunc:
   .
   .
   .
   pop hl
   ret

If this was your entire program, and you ran this, it would do nothing; the screen wont even blink. See, you popped off the return address for the call &MyFunc, and the next value under that is to go back to usgard. But if this isn't your whole program, and the value under is not for usgard; Your gonna crash.

Always just make sure for every push there is a pop. And remember also, if you push hl, you dont have to pop hl, it could be bc. Then you just copied hl into bc :)

Also beware of this piece of code:

   ld b,8
FindZero:
   push bc
   call &Get_a_value
   cp 0
   ret z
   pop bc
   djnz FindZero
   ret

Lets say you make a call to FindZero. It will return with b stating where zero is, wont it? Nope. It will crash. Can you see why?

Yep. BC is not popped before you ret z. Make it look like this to fix it.

   ld b,8
FindZero:
   push bc
   call &Get_a_value
   cp 0
   jr z,Fix
   pop bc
   djnz FindZero
   ret
Fix:
   pop bc
   ret

Wow! We sure have learned alot this lesson. I hope you got it all, because this stuff is very important. I also hope I haven't forgotten anything. :)