All the assembler programs in the previous section have been executed instruction by instruction following the sequence specified by the order of the instructions in memory. The jump and branch instructions enable' the flow of control to be altered, making it possible to implement loops.
The JMP instruction is followed by the address of the instruction to be executed next.
JMP Jump
Before using the JMP instruction we need to be able to indicate to the assembler where we want to jump to, and to do this conveniently 'labels' are needed. In the assembler labels are variables of the form AA to ZZ followed by a number (0, 1, 2 ... etc). If you are already familiar with ATOM BASIC you will recognise these as the arrays.
First the labels to be used in an assembler program must be declared in the DIM statement. Note that we still need to declare P(-1) as before, and this must be the last thing declared. For example, to provide space for four labels LL0, LL1, LL2, and LL3 we would declare:
DIM LL(3), P(-1)
Labels used in a program are prefixed by a colon ':' character. For example, enter the following assembler program:
10 DIM LL(3),P(-1) 20 W=#FFF4 30[ 40:LL0 LDA @#2A 50:LL1 JSR W 60 JMP LL0 70] 80 END
To execute the program the procedure is slightly different from the previous examples, because space has now been assigned at TOP for the labels. When using labels in an assembler program you should place a label at the start of the program, as with LLO in this example, and LINK to that label. So, in this example, execute the program with:
LINK LL0
The program will output an asterisk, and then jump back to the
previous instruction. The program has become stuck in an endless
loop! If you know BASIC, compare this program with the BASIC
program in section 4.6 that has the same effect.
A flowchart for this program is as follows:
Try pressing ESCAPE. ESCAPE will not work; it only works in BASIC programs, and here we are executing machine code instructions so ESCAPE is no longer checked for. Fortunately there is one means of salvation: press BREAK, and then type OLD to retrieve the original program.
The carry flag has already been introduced; it is set or cleared as the result of an ADC instruction. The CPU contains several other flags, which are set or cleared depending on the outcome of certain instructions; this section will introduce another one.
The zero flag, called Z, is set if the result of the previous operation gave zero, and is cleared otherwise. So, for example:
LDA #80
would set the zero flag if the contents of #80 were zero.
The conditional branches enable the program to act on the outcome of an operation. The branch instructions look at a specified flag, and then either carry on execution if the test was false, or cause a branch to a different address if the test was true. There are 8 different branch instructions, four of which will be introduced here:
BEQ Branch if equal to zero (i.e. Z=1) BNE Branch if not equal to zero (i.e. Z=0) BCC Branch if carry-flag clear (i.e. C=0) BCS Branch if carry-flag set (i.e. C=1)
The difference between a 'branch' and a 'jump' is that the jump instruction is three bytes long (op-code and two-byte address) whereas the branch instructions are only two bytes long (op-code and one-byte offset). The difference is automatically looked after by the assembler.
The following simple program will print an exclamation mark if #80 contains zero, and a star if it does not contain zero; the comments in lower-case can be omitted when you enter the program:
10 DIM BB(3),P(-1) 20 W=#FFF4 30[ 40:BBO LDA #80 50 BEQ BB1 if zero go to BB1 60 LDA @#2A star 70 JSR W print it BO RTS return 90:BB1 LDA @#21 exclamation mark 100 JSR W print it 110 RTS return 120] 130 END
A flowchart for this program is as follows:
START Look at
location #80no is it zero? yes Print '*' Print '! END END
Now assemble the program with RUN as usual. You will almost certainly get the message:
OUT OF RANGE:
before the line containing the instruction BEQ BB1, and the offset in the branch instruction will have been set to zero. The message is produced because the label BB1 has not yet been met when the branch instruction referring to it is being assembled; in other words, the assembler program contains a forward reference. Therefore you should assemble the program a second time by typing RUN again. This time the message will not be produced and the correct offset will be calculated for the branch instruction.
Note that whenever a program contains forward references it should be assembled twice before executing the machine code.
Now execute the program by typing:
LINK BB0
for different values in #80, and verify that the behaviour is as specified above.
The CPU contains two registers in addition to the accumulator, and these are called the X and Y registers. As with the accumulator, there are instructions to load and store the X and Y registers:
LDX Load X register from memory X=M LDY Load Y register from memory Y=M STX Store X register to memory M=X STY Store Y register to memory M=Y
However the X and Y registers cannot be used as one of the operands in arithmetic or logical instructions like the accumulator; they have their own special uses, including loop control and indexed addressing.
The X and Y registers are particularly useful as the control variables in iterative loops, because of four special instructions which will either increment (add 1 to) or decrement (subtract 1 from) their values:
INX Increment X register X=X+1 INY Increment Y register Y=Y+1 DEX Decrement X register X=X-1 DEY Decrement Y register Y=Y-1
Note that these instructions do not affect the carry flag, so incrementing #FF will give #00 without changing the carry bit. The zero flag is, however, affected by these instructions, and the following program tests the zero flag to detect when X reaches zero.
The iterative loop enables the same set of instructions to be executed a fixed number of times. For example, enter the following program:
10 DIM LL(4),P(-1) 20 W=#FFF4 30[ 40:LL0 LDX @8 initialise X 50:LL1 LDA @#2A code for star 60:LL2 JSR W output it 70 DEX count it 80 BNE LL2 all done? 90 RTS 100 ] 110 END
A flowchart for the program is as follows:
START X = 8 Print "*" Subtract 1 from X no X = 0? yes END
Assemble the program by typing RUN. This program prints out a star, decrements the X register, and then branches back if the result after decrementing the X register is not zero. Consider what value X will have on successive times around the loop and predict how many stars will be printed out; then execute the program with LINK LLO and see if your prediction was correct. If you were wrong, try thinking about the case where X was initially set to 1 instead of 8 in line 40.
How many stars are printed if you change the instruction on line 40 to LDX @0 ?
In the previous example the condition X=0 was used to terminate the loop. Sometimes we might want to count up from 0 and terminate on some specified value other than zero. The compare instruction can be used to compare the contents of a register with a value in memory; if the two are the same, the zero flag will be set. If they are not the same, the zero flag will be cleared. The compare instruction also affects the carry flag.
CMP Compare accumulator with memory A-M CPX Compare X register with memory X-M CPY Compare Y register with memory Y-M
Note that the compare instruction does not affect its two operands; it just changes the flags as a result of the comparison.
The next example again prints 8 stars, but this time it uses X as a counter to count upwards from 0 to 8:
10 DIM LL(2),P(-1) 20 W=#FFF4 30[ 40:LL0 LDX @0 start at zero 50:LL1 LDA @#2A code for star 60 JSR W output it 70 INX next X 80 CPX @8 all done? 90 BNE LL1 100 RTS return 110] 120 END
In this program X takes the values 0, 1, 2, 3, 4, 5, 6, and 7. The last time around the loop X is incremented to 8, and the loop terminates. Try drawing a flowchart for this program.
In the previous two examples X was simply used as a counter, and so it made no difference whether we counted up or down. However, it is often useful to use the value of the control variable in the program. For example, we could print out the character in the X register each time around the loop. We therefore need a way of transferring the value in the X register to the accumulator so that it can be printed out by the OSWRCH routine. One way would be to execute:
STX #82 LDA #82
where #82 is not being used for any other purpose. There is a more convenient way, using one of four new instructions:
TAX Transfer accumulator to X register X=A TAY Transfer accumulator to Y register Y=A TXA Transfer X register to accumulator A=X TYA Transfer Y register to accumulator A=Y
Note that the transfer instructions only affect the register
being transferred to.
The following example prints out the alphabet by making X cover
the range #41, the code for A, to #5A, the code for Z.
10 DIM LL(2),P(-1) 20 W=#FFF4 30[ 40:LL0 LDX @#41 start at A 50:LL1 TXA put it in A 60 JSR W print it 70 INX next one 80 CPX @#5B done Z? 90 BNE LL1 if so continue 100 RTS else return 110] 120 END
Modify the program to print the alphabet in reverse order, Z to A.
All these examples could have used Y as the control variable instead of X in exactly the same way.