4

The following code is for assembly 8086, I'm looping using LOOP instruction.

The loop continues forever after CL becomes zero and it changes the CX value to FFFFh.
But if I change the value of CL to maximum 06h, the loop stops properly.
Moreover, if I remove first LOOP AGAIN it will work fine.

DATA DB 01001100b

MOV AL, DATA
MOV CL, 08h
SUB BL, BL
SUB DL, DL

AGAIN:
ROL AL, 1  
JC SKIP
INC BL
LOOP AGAIN

SKIP: 
INC DL
LOOP AGAIN

I expect it to stop when CL becomes zero. Any idea why it isn't behaving as expected?

UPDATE 1 I have noticed when CL (or CX when using 16 bit) reaches 1 and last bit is 0, then the first LOOP AGAIN won't jump up and operation continues to the SKIP part. If I change the last bit of DATA to 1 it will make the JC SKIP and everything works just fine.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
Nawras
  • 181
  • 1
  • 12
  • 4
    The loop counter is `cx`, not `cl`. If you set `cl` to a value, make sure `ch` is zero before doing so. – fuz Jan 19 '19 at 14:51
  • 1
    after first `loop` the `cx` is zero. If the second `loop` is then reached, it does decrement `cx` (i.e. 0 -> 0xFFFF) so the second loop will run 65536 times before `cx` will reach zero again, then it will finish. Can't see why it would "not stop", it will. (and use full 16 bit `cx` in your code to set up counter, `cl` is just low 8 bits of it, so you don't know what is in upper 8 bits (`ch`)) – Ped7g Jan 19 '19 at 15:23
  • Possible duplicate of [How exactly does the x86 LOOP instruction work?](https://stackoverflow.com/a/46881622), but I think there's a more specific duplicate about nested `loop` instructions somewhere. (`loop` can only fall through when it leaves CX=0, meaning the next `loop` can't fall through, so it's an infinite loop.) – Peter Cordes Jan 19 '19 at 16:02
  • oh, the second loop is going all the way up ahead of first loop, I forgot to check, so yes, this will never stop, that's correct description of the current state... So you want probably in the end some jump after the first loop, if I now understand what you are trying to do.. (counting zeroes and ones separately), something like `jmp AfterSecondLoop` ... but actually simply calculate only one counter, the other is 8-counter, so you can calculate it after loop does finish with one subtract. Your "update" is correct. So do you understand why it works like this? – Ped7g Jan 19 '19 at 16:36
  • Oh, yes this looks like unintentionally nested loops because of accidental fall-through after tail duplication. I don't see the point, though, just count ones in the loop. You can get the count of zeros by doing `8 - bl` outside the loop with a `mov dl, 8` / `sub dl, bl`. – Peter Cordes Jan 19 '19 at 19:10
  • @PeterCorde, @Ped7g Yes I can just do the subtract, but I don't understand this codes' behavior, it's not nested loop, just one loop but with two jumps using `CX`, its more like a compound statement `if(CX==0){ INC BL} else{INC DL}`. my point is why if the first condition met, `LOOP` instruction won't do the jump while subtracting `CX` by 1? – Nawras Jan 20 '19 at 02:10
  • Look at what happens if the first `LOOP AGAIN` is not-taken. It falls through into what's supposed to be the else block, which *also* contains a `loop again` which will then decrement CX to `0xFFFF` and jump to the top. You need an extra `jmp` out of the loop in case the `loop` after INC BL falls through. Single-step it with a debugger and watch it happen. And BTW, did you mean `if(CF==0) { INC BL } else { INC DL }`? Your `loop` is supposed to be a loop around that, with tail duplication. – Peter Cordes Jan 20 '19 at 06:03
  • @PeterCordes Thank you for your explanation, I got it :) – Nawras Jan 21 '19 at 00:56

1 Answers1

6

Easy answer

    MOV  AL, 01001100b
    MOV  CX, 0008h
    SUB  BL, BL
    SUB  DL, DL
AGAIN:
    ROL  AL, 1  
    JC   SKIP
    INC  BL
    LOOP AGAIN    ; The 1st
    JMP  ENDOFLOOP
SKIP:
    INC  DL
    LOOP AGAIN    ; The 2nd
ENDOFLOOP:
  • The LOOP instruction on 8086 always uses the CX register (all of it).
  • Your code missed an unconditional jump below the 1st LOOP AGAIN in case the loop should terminate there. This is to avoid falling through in the SKIP part of the program.

How it fails

    MOV AL, 01001100b
    MOV CL, 08h
    SUB BL, BL
    SUB DL, DL
AGAIN:
    ROL AL, 1  
    JC SKIP
    INC BL
    LOOP AGAIN    ; The 1st
SKIP: 
    INC DL
    LOOP AGAIN    ; The 2nd

This is what the code does (assuming CH=0):

         ROL AL, 1                         LOOP
AL=01001100b   AL=10011000b   CF=0  BL=1   CX=7  The 1st jumps back
AL=10011000b   AL=00110001b   CF=1  DL=1   CX=6  The 2nd jumps back
AL=00110001b   AL=01100010b   CF=0  BL=2   CX=5  The 1st jumps back
AL=01100010b   AL=11000100b   CF=0  BL=3   CX=4  The 1st jumps back
AL=11000100b   AL=10001001b   CF=1  DL=2   CX=3  The 2nd jumps back
AL=10001001b   AL=00010011b   CF=1  DL=3   CX=2  The 2nd jumps back
AL=00010011b   AL=00100110b   CF=0  BL=4   CX=1  The 1st jumps back
AL=00100110b   AL=01001100b   CF=0  BL=5   CX=0  The 1st FALLS THROUGH!!!

At this point because CX became 0 the 1st LOOP AGAIN no longer jumps back. The code falls through and falsely increments the DL register. The 2nd LOOP AGAIN also pinches off 1 from CX producing CX=65535.
So the program happily continues for a very long time but it does not become an infinite loop. Because the loop counter is no longer a multiple of 8 (the number of bits in AL), at some point it will be the 2nd LOOP AGAIN that will make CX=0 at which point the program will finally stop.

Why it seems to work with modifications

but if I change the value of CL to maximum 06h, the loop stops properly

This is what the code does with CX=6:

         ROL AL, 1                         LOOP
AL=01001100b   AL=10011000b   CF=0  BL=1   CX=5  The 1st jumps back
AL=10011000b   AL=00110001b   CF=1  DL=1   CX=4  The 2nd jumps back
AL=00110001b   AL=01100010b   CF=0  BL=2   CX=3  The 1st jumps back
AL=01100010b   AL=11000100b   CF=0  BL=3   CX=2  The 1st jumps back
AL=11000100b   AL=10001001b   CF=1  DL=2   CX=1  The 2nd jumps back
AL=10001001b   AL=00010011b   CF=1  DL=3   CX=0  The 2nd FALLS THROUGH!!!

Because it's the 2nd LOOP AGAIN that falls through, there's no problem since we're at the bottom of the program.

If I change the last bit of DATA to 1 it will make the JC SKIP and everything works just fine

This is what the code does with AL=01001101b:

         ROL AL, 1                         LOOP
AL=01001101b   AL=10011010b   CF=0  BL=1   CX=7  The 1st jumps back
AL=10011010b   AL=00110101b   CF=1  DL=1   CX=6  The 2nd jumps back
AL=00110101b   AL=01101010b   CF=0  BL=2   CX=5  The 1st jumps back
AL=01101010b   AL=11010100b   CF=0  BL=3   CX=4  The 1st jumps back
AL=11010100b   AL=10101001b   CF=1  DL=2   CX=3  The 2nd jumps back
AL=10101001b   AL=01010011b   CF=1  DL=3   CX=2  The 2nd jumps back
AL=01010011b   AL=10100110b   CF=0  BL=4   CX=1  The 1st jumps back
AL=10100110b   AL=01001101b   CF=1  DL=4   CX=0  The 2nd FALLS THROUGH!!!

Because it's the 2nd LOOP AGAIN that falls through, there's no problem since we're at the bottom of the program.

Sep Roland
  • 33,889
  • 7
  • 43
  • 76
  • Thank you for this detailed explanation (and re-formatting the question ;) ), I appreciate it. – Nawras Jan 21 '19 at 01:03