Jump to content

Conditional structures: Difference between revisions

(Added PL/M)
Line 7,905:
 
=={{header|Z80 Assembly}}==
Control structures in assembly languages are typically based around the zero and carry flags. If two quantities are equal, subtracting them will result in zero. The <code>CP</code> instruction compares the accumulator with another value, either an immediate, an 8-bit register, or the byte pointed to by <code>HL</code>, <code>IX+#</code>, or <code>IY+#</code>, where # is an 8-bit signed offset. <code>CP</code> sets the processor flags the same way <code>SUB</code> would, except the value in the accumulator is never changed. This lets you compare two values "non-destructively." <code>CP</code> must use the accumulator as its primary operand, and can't be directly used to compare 16-bit register pairs, though there are workarounds that use a combination of instructions.
In addition to branching, the Z80 can conditionally <code>CALL</code> a subroutine.
<lang z80>call z,foo ;call "foo" if the last math operation resulted in zero.
call c,bar ;call "bar" if the carry is set.
call nc,baz ;call "baz" if the carry is clear.</lang>
 
The compare instruction is often used in combination with jumps, calls, and returns to implement high-level control structures.
 
==If-Then-Else==
We'll look at this example in C and in Z80 Assembly:
 
<lang C>
char x;
if (x == 20)
{
doThis();
}
else
{
doThat();
}</lang>
 
<lang z80>cp 20
jr nz,Else
call doThis
;execution returns here after the call
jr done
Else:
call doThat
done</lang>
 
While the Z80 does support conditional calls and returns, in this example they weren't a good choice, since there's no guarantee that the function <code>doThis</code> won't alter the flags, and you can't back up/restore the flags on the stack without backing up/restoring the accumulator at the same time, which isn't always what you want.
 
In this example, we'll look at situation where there is no else block (in other words, if condition is not met, do nothing and continue with the program.)
 
C code:
<lang C>if (x == 20)
{
DoSomething();
}
// rest of program</lang>
 
Z80 Assembly code:
<lang z80>
cp 20
call z,DoSomething
;rest of program</lang>
 
If the accumulator didn't equal 20, no <code>CALL</code> will actually take place.
 
==Switch==
Switch cases can be implemented in a few ways. The simplest way is by checking each value individually.
<lang Z80>ld a,(HL) ;switch (HL)
cp 1 ;case (1)
jr nz,+ ;branch to next colon (note: not all assemblers support this syntax)
call HL_EQUALS_1
:
cp 2 ;case (2)
jr nz,+ ;branch to next colon
call HL_EQUALS_2
:
cp 50
jr nz,+
call HL_EQUALS_50
:
;rest of program</lang>
 
The above example continues to check the other cases even after a match is found. If you don't want that to happen, do this:
<lang z80>ld a,(HL) ;switch (HL)
cp 1 ;case (1)
jr nz,+ ;branch to next lone colon
call HL_EQUALS_1
jr done ;You could also write "jr +++"
:
cp 2 ;case (2)
jr nz,+ ;branch to next lone colon
call HL_EQUALS_2
jr done ;you could also write "jr ++"
:
cp 50
jr nz,+
call HL_EQUALS_50
:
done:
;rest of program</lang>
 
Another way to implement switch cases is with a lookup table of functions. This uses a command called <code>JP (HL)</code>. Despite the parentheses around <code>(HL)</code>, no dereferencing takes place - the program counter is simply set to the value in <code>HL</code>. This method is a little more complicated but allows you to create an indexed array of functions and choose one to execute. This method doesn't allow for fallthrough. You have to create a dispatcher that you can CALL, and pre-load the accumulator with the desired index and HL with the pointer to the 0th function in the table. Wherever you go needs to end in a <code>RET</code> instruction, so that you'll end up just after you <code>CALL</code>ed the dispatcher.
 
<lang z80>Dispatch: ;remember, you need to CALL this address for it to work properly. Otherwise your program will most likely crash.
add a ;this is a table of 16-bit values, so multiply the index by 2.
ld a,(hl) ;get the low byte of the function addr. you wish to call
push af
inc hl
ld a,(hl) ;get the high byte of the function addr. you wish to call
ld H,a ;store the high byte in H
pop af
ld L,a ;store the low byte in L
jp (HL) ;now you've jumped to the desired function. Its RET will return execution to the instruction just after "CALL Dispatch"</lang>
 
The disadvantage to this method is that it relies on the case values being consecutive. If they're not, you're better off using the other method, but you can still implement this if you pad the lookup table with pointers to a <code>RET</code>, which will make you return immediately back to after the dispatch call, without having done anything.
 
=={{header|zkl}}==
1,489

edits

Cookies help us deliver our services. By using our services, you agree to our use of cookies.