please dont rip this site


repeats a set of instructions according to repeated addition or subtraction operations on a control variable.

The For/Next loop is extremely versatile. It replaces many different kinds of loop structures used in assembly language. This section will not present a For/Next instruction per se, but will show you how to build appropriate loops for your assembly-language programs.

The most basic use of a For/Next loop is to execute some instructions a fixed number of times:

FOR I = 1 TO 3
        GOSUB FLASH_LED ' Flash LED 3 times. 

The instructions within the loop can also use the control variable (I in the examples here):

FOR I = 50 TO 100
        pins = I        ' Output 50,51,52...100

Although the control variable is frequently incremented by 1 each time through the loop, the programmer can have the loop count backwards or by some value other than 1:

FOR I = 1000 TO 100 STEP -2     ' Output low byte of 
        pins = I        ' 1000,998...100

PBASIC's For/Next instructions support all of these loops, using bits, bytes, or words for the control variable, start value, end value, and step. The interpreter seems to use the same mechanism, based on 16-bit comparisons, regardless of the values and variables used.

In assembly language, programmers use different loop structures depending on the number of iterations required and whether or not the value of the control variable will be used within the loop. These specialized loops take no more time (instruction cycles) or space (program memory) than is absolutely required to do the job.

Take the first example. To perform some instructions from 1 to 256 times calls for a djnz loop based on the Parallax decrement-and-jump-if-not-zero instruction. Here's an example. It assumes that the variable I has been defined, and that there's a subroutine called flash_LED somewhere in the program.

        mov     I,#3    ; I = 3. 
:loop   call    flash_LED       ; Flash the LED. 
        djnz    I,:loop ; I=I-1: IF I=0 THEN GOTO :loop

The code inside this loop (call flash_LED) executes at least once regardless of the initial value of I. It may seem strange at first, but to get the maximum number of iterations, I must initially be assigned a value of 0. The djnz instruction decrements the variable before it makes the looping decision. In eight-bit math, 0 decrements to 255.

 Let's try a 16-bit example of the same sort of loop. We'll flash the LED 500 times. We can't use djnz, because it only works with eight-bit values. In fact, for the most compact loop, we don't want to count backwards at all. When you do 16-bit operations on an 8-bit machine, it is faster to add the binary equivalent of a negative number (called the two's complement) than it is to subtract a positive one. Many of the routines in this book do just that; see Let x = x + y, or look at the code at the beginning of Pulsout. Those routines convert input values to their two's complements.

Assuming that you know the number of iterations ahead of time, you can do the two's complement conversion. The easiest way is to use a calculator capable of binary math (a must-have for assembly programming, in my opinion), set it for 16-bit operations, then subtract the number from 0. In hex, 500 is 1F4h, and 0 - 1F4h = FE0Ch. An alternative method for taking the two's complement is to take the NOT of the number and add 1.

For the loop, we will count upward from FE0Ch to 0.

        mov     hi,#0FEh        ; hi=0FEh
        mov     lo,#0Ch ; lo=0Ch (FE0Ch = -500 in 2's complement)
:loop   call    flash_LED       ; Flash the LED. 
        ijnz    lo, :loop       ; lo=lo-1: IF lo=0 THEN GOTO :loop
        ijnz    hi, :loop       ; hi=hi-1: IF hi=0 THEN GOTO :loop

Note that this loop executes the second ijnz instruction only when lo rolls over to 0. This means that those trips through the loop take 3 instruction cycles longer than trips in which lo does not roll over. This is OK in most cases, but timing loops should always take the same number of cycles. For an example of an alternative 16-bit timing loop, see the :loop portion of the Pulsout listing.

Now look at the second type of For/Next loop--the one in which the control variable is used by the code inside the loop. In such cases the control variable may not start or end on easy-to-detect values like 0. Assuming that all values fit into byte-wide variables, we end up with a loop that looks like this:

        mov     I,#50   ; I = 50. 
:loop   mov     rb,I    ; rb = I. 
        inc     I       ; I=I+1. 
        cjbe    I,#100,:loop    ; IF I <= 100 THEN GOTO :loop

If you needed to increment I by some other value, say 5, you could substitute ADD I,#5 for inc I in the example. This would be equivalent to FOR I = 50 TO 100 STEP 5 in BASIC. You would have to be careful here to avoid a potentially frustrating bug: What if the STEP value is larger than the difference between the stop value and 255? In other words, you wanted to use this structure to write FOR I = 100 to 250 STEP 60. Look at the values of I through several iterations of the loop: 100, 160, 220, 24... Since I is an eight-bit variable, it rolls over when you add 220 + 60. Since 24 is less than 200, the loop keeps executing. You can prevent this runaway situation by sampling the carry bit and exiting the loop when a carry occurs:

        mov     I,#100  ; I = 100. 
:loop   mov     rb,I    ; rb = I. 
        ADD     I,#60   ; I=I+60. 
        jc      :exit   ; IF I > 255 THEN GOTO :exit
        cjbe    I,#200,:loop    ; IF I <= 100 THEN GOTO :loop
:exit   ...             ; Continue with next instruction.

If your loop decrements the control variable, you'll have to use jnc (jump-if-not-carry) to detect an underflow and exit the loop.

The third type of For/Next loop can use 16-bit values for the start, stop and step values, and has a 16-bit control variable. It can increment or decrement from start to stop, and the value of the control variable may be used by the code inside the loop. It is a true For/Next loop.

Before we look at how to code this loop in assembly, let's consider what would happen if BASIC didn't include this instruction. Could programmers still write this kind of loop in BASIC? The answer is yes:

LET I = 1000    ' FOR I=1000...
LOOP:   ' Label for GOTO destination. 
        pins = I        ' Output 1000,998,996...100
        I=I-2   ' Decrement I by 2. 
IF I >= 100 THEN LOOP   ' ...TO 100

This is pretty much how you handle more-complex types of For/Next loops in assembly. If you can avoid coding this kind of structure, do so. It uses the most program space and runs the most slowly of the options we've discussed. Here's how the example could be coded using subroutines from the If/Then and Let x = x+y sections of this book:

        mov     n1H,#03h        ; n1 = 1000 (03E8h)
        mov     n1L,#0E8h
:loop   mov     rb,n1L  ; rb = n1L. 
        mov     rc,n1H  ; rc = n1H
        mov     n2H,#0FFh       ; n2 = -2 (two's complement: FFFEh)
        mov     n2L,#0FEh
        call    Add16   ; LET n1=n1-2
        clr     n2H     ; n2 = 100 (0064h)
        mov     n2L,#64h
        call    Comp16  ; Compare n1 to n2. 
        jmp     pc+w    ; W contains result of comparison:
        jmp     :loop   ; 0 means n1 = n2, so loop. 
        jmp     :loop   ; 1 means n1 > n2, so loop. 
:exit   ...             ; 2 means n1 < n2, so exit loop.

Note that this technique has the same flaw as the For/Next instructions of the PBASIC interpreter chip. What happens when the step is larger than the value (65535-end) for incrementing loops, or if the step is larger than the end value for decrementing loops? The value of n1 will roll over and the loop will execute more times than you expected. The problem is the same one we encountered previously with eight-bit variables. The cure is the same as for the eight-bit version; for incrementing loops add a jc (jump-if-carry) instruction right after the call to Add16 and set the destination to exit the loop. For decrementing loops, use a jnc (jump-if-not-carry) instead.

There is another type of loop that can eliminate the eccentricities of the others. This one maintains two control variables, a public one for the use of the code inside the loop, and a private one that counts iterations. Here is an example:

        mov     I,#50   ; Public variable I starts at 50. 
        mov     count,#6        ; Private counter for 6 loops
:loop   mov     rb,I    ; Output I
        ADD     I,#33   ; I=I+33
        djnz    count,:loop     ; Repeat 6 times.

This is equivalent to FOR I = 50 to 248 STEP 33, but there's no chance of a flawed compare-and-jump allowing the loop to run away. It stops after six iterations. This approach is also handy when you want to let the value of the public control variable roll over. For example, if you wanted to code something like FOR I = 0 to 255 STEP 8, but execute the whole loop three times. In BASIC, you might nest that loop inside a FOR J = 1 to 3 loop. In assembly, you would just write a loop like the public/private one above. Assign count an initial value of 96, and keep adding 8 into I.

See also:

file: /Techref/microchip/seepicsrc/psbpix/for.htm, 12KB, , updated: 2000/3/15 17:33, local time: 2020/8/3 17:14,


<A HREF=""> microchip seepicsrc psbpix for</A>


Link? Put it here: 
if you want a response, please enter your email address: 
Did you find what you needed?


Welcome to!


Welcome to!