Table of Contents
Sigma's Learn Assembly in 28 days, day7.
If you still don't understand, here's some more information that may help you.
Control structures help you determine the flow of the program. It would be really boring if the code executed each line once from the beginning of the program to the end. We need some variation. Control structures allow you to perform certain actions found elsewhere in the program, allow you to repeatedly perform that action, and also give you control of what conditions must be satisfied before the action is performed.
The idea here is this: test a condition. If it is true, then do something. Else, do something else.
If it is sunny, then go for a walk. Else do homework.
As you can see, you have a choice of either going on a walk or doing homework. You can't do both at the same time.
It is also possible to chain if-then-else statements. There are a few ways you can chain, or expand the conditions:
Provide extra conditions
Self-explanatory, just add on extra conditions that must test to be true.
If it is sunny and I have no homework, then go for a walk. Else, do homework.
Similar to tacking on extra conditions, except Else-If tacks on conditions if the initial statement is false.
If it is sunny, then go for a walk. Else if I have homework, then do homework.
Then without Else
Sometimes, you don't need an else. This is because the other course of action is to do nothing.
If I'm hungry, then eat something (else, don't eat)
The stuff in parenthesis is optional, but as you can tell, unnecessary.
It is important to note that there is not an Else without a Then.
Obviously, testing conditions are a vital part to programming code. If a piece of code is continually run from start to finish without conditions, it's usually not useful in the real world. So, we need to apply this idea of conditions into programming. Z80 has 2 uses conditions:
Directives (compiler instruction)
These tell the compiler what to do under certain conditions. A common use is to compile a program for many platforms.
#If 83p .include "ti83plus.inc" #Else #If 83 .include "ti83.inc" #EndIf
Much more important is the use of conditions in programs. The z80 processor can handle conditions in 2 fundamental ways. One is to test the accumulator for a certain value. This is done through the CP instruction.
CP 5 ;compare the accumulator to 5 CP B ;compare the accumulator to the B register
The CP instruction is telling the processor to subtract the value of the argument (5, or B) from the accumulator. Note that the actual value of the accumulator is not changed, but there are a series of flags that are set, depending on the result of the CP instruction.
After you have set/reset certain flags with the CP instruction, you must do something with these flags. Unfortunately, the z80 processor doesn't directly support If-Else, so we need to create or own little work-around.
To do this, we'll use jump instructions. The jump instructions are JP and JR. Jump instructions jump to a location in memory, and execute the code at that point. The good part is that jump instructions can also take conditions, based on the flags we have changed with the CP instruction.
We can also use call instructions. Call instructions are similar to jump instructions with one key difference: you can easily jump back. Call instructions include BCALL, CALL, and RET. Note that BCALL is not actually an instruction directly recognized by the z80 processor. Instead, it is a macro that allows the programmer to call certain functions that the manufacturer (Texas Instruments) has coded into the calculator for the Operating System's use, as well as any programs' use.
Another way to test conditions is through Bit-Masking. This is a topic that will be discussed later, but if you want click on the link and it will explain the basics of bit-masking.
What if you want to perform an action repeatedly? You could type the code for the action over and over.
;repeatedly add a number 10 times, aka multiplication xor a ;initial value ld b,5 ;what to repeatedly add add a,b ;add a and b 10 times add a,b add a,b add a,b add a,b add a,b add a,b add a,b add a,b add a,b
However, this presents some problems. One, it's a waste of space if you need to add, say, two numbers together 100 times. It would also be a problem if you want to make the code so that it can add numbers any number of times in the program without having to change the code, only the inputs. Luckily, there are loops. Loops perform a certain action over and over again until certain conditions are met. These are the loop conditions. There are two kinds of loops: for loops, and while loops. They are be discussed in greater detail below.
While loops perform an action until a condition is fulfilled, whether a certain key is pressed, a counter reaches a certain number, or any other sort of condition possible.
keyloop: ;keep getting key presses until the enter key is pressed bcall(_GetKey) cp kenter jr nz,keyloop
See the last line of this code? It jumps to a label that gets key presses if the enter key was not pressed. As with regular control statements, it is possible to have multiple conditions for exiting the loop:
keyloop: ;keep getting key presses until either 2nd or enter is pressed bcall(_GetCSC) cp skenter jr z,endloop cp sk2nd jr z,endloop jr keyloop endloop:
A special kind of while loops, for loops are while loops that run on a counter. Lucky for us, the z80 processor has an instruction set specially built in for for loops (not a typo). This instruction is the DJNZ instruction. DJNZ does these things (in this order):
- Decreases B
- Checks if B is zero
- If it is, continue on in code
- Else, jump to specified label
So, let's create a loop! Please note that this in not how you should multiply two numbers together, but for our purposes will demonstrate for loops. To learn the more efficient method of multiplication, see this page.
;multiply ; ;inputs: accumulator=number1 ; b=number2 ; ;output: accumulator=number1*number2 ; ;note: this routine is NOT how you should multiply. It also does not check if the 2nd operand is 0 ; multiply: ld c,a xor a mLoop: add a,c djnz mLoop
So, let's say you want a program that performs a certain action at different parts of the code. You could re-type the code, but that's a waste of space, and if you jump to it, how will you know were to jump back to? This creates the idea of routines. A routine is a bunch of code that is called somewhere in the program, and then when it's done running, jumps back. For this, we'll use the CALL instruction.
What CALL does:
- Pushes the program counter (where the processor is executing code from) onto the stack
- Jumps to a point in memory
However, at this point we've finished running the routine and want to get back. To get back, we'll use the RET instruction.
What RET does:
- Pops the stack
- Jumps to the address stored in the stack
Don't worry if you don't know what the stack is for now, but if you want, see this page.
So, let's put it all together now:
;Calling a routine call testRoutine ... ;The routine testRoutine: inc a dec b ret
Time to test your knowledge. See if you can answer these questions.
- Create code for a while loop that:
Identify the Error(s)
Find the error(s), if any, in each piece of code. They can either be run-time errors or logic errors.
loop: inc b add a,b djnz loop
keyloop: bcall(_GetKey) cp kUp jp mUp ;assume mUp exists somewhere cp kDown jp mDown ;assume mDown exists somewhere jr keyloop
mUp: inc a cp 6 jp c,maxUp ;assume maxUp exists somewhere ret
; ld b,0 ;Let's repeat 256 times ld a,6 loop: dec a djnz loop
For answers, see here.
Possible the most powerful thing in programming, control structures will allow you to dictate how to handle events and provide you with a powerful tool that can not be replaced by anything else. There are other parts to control structures, but they require knowledge of advanced topics not discussed here.