-1

For context: Because of online classes, some Indian university computer classes have reduced to teachers just giving us the code and expecting us to rote learn the thing.

The program is to count the number of +ve and -ve numbers in a given array. The entire code is here.

My question is from lines 45 through 59 (given below)

mov    esi, arr
mov    ecx,arr_size         ;Array counter i.e. 6 
mov    ebx,0;                   ;counter for     +ve nos
mov    edx,0;                   ;counter for    -ve nos.

next_num:
    mov    eax,[esi]         ; take no. in RAX
    rcl    eax,1             ; rotate left 1 bit to check for sign bit
    jc    negative
positive:
    inc    ebx            ; no carry, so no. is +ve
    jmp    next
negative:
    inc    edx            ; carry, so no. is -ve
next:
    add  esi,4                ; 32 bit nos i.e. 4 bytes
    loop next_num

In the above code, from what I can understand, I'm storing the starting location of the array in the ESI register and scanning each element and checking if it's positive or not

However, how do I know when I've reached the end of the array?

The code is maintaining the ECX register but not using it. Why isn't this running endlessly then?

Shouldn't some sort of loop with a DEC ECX and JE 0 instructions be there as well?

halfer
  • 19,824
  • 17
  • 99
  • 186
Anurag
  • 3
  • 1
  • 5
  • 4
    The `loop next_num` instruction is approximately equivalent to `dec ecx; jnz next_num`. – prl Sep 13 '20 at 08:12
  • A good start would be single-stepping it in a debugger to watch register values change. And then if the fact that `loop` changed ECX surprises you, look it up in an instruction-set reference manual. – Peter Cordes Sep 13 '20 at 17:17
  • Also, that's an inefficient way to test the sign bit. `test eax,eax` / `jl negative` would be the idiomatic way to jump if `EAX < 0` (signed compare). The `jl` condition is based on the sign flag, no need to rotate it into carry. You can even `cmp dword [esi], 0` to compare a memory operand. Also note that non-negative includes zero, which is not positive either. Also, no need to update both counters inside the loop, just count negative numbers with `shr eax, 31` / `add ebx, eax`, and calculate `non_neg = total - negative` at the end. No branching needed, literally add up the sign bits. – Peter Cordes Sep 13 '20 at 17:21
  • @Peter Cordes: After `test eax, eax` you can also use `js negative` which I find clearer than using `jl`. – ecm Sep 13 '20 at 17:33
  • 1
    @ecm: yup, the semantic meaning there is branching on the sign bit itself, vs. branching on the value being less than zero. (That semantic meaning of `jl` would fit better with `cmp eax, 0`; testing a register against itself is a peephole optimization for `cmp` with 0, so test/jl requires thinking through that meaning for it to look natural. Fun fact: `cmp reg,0` / `js` would be [slower on Core 2](https://stackoverflow.com/a/33724806/224132), which can macro-fuse cmp with `jl` but not `js`. Even Core 2 can fuse `test`/`js` though, so either idiom is fine) – Peter Cordes Sep 13 '20 at 17:40
  • @PeterCordes and ecm Thanks for your help. Seeing that I am beginning to learn assembly programming, are there any books/resources you would recommend to a beginner? – Anurag Sep 14 '20 at 09:59
  • Agner Fog's optimization guide is helpful in understanding why you'd choose one way to do things over another, and maybe the way he describes things will help asm make sense. (https://agner.org/optimize). Intel manuals are of course essential to find out what an instruction does any time you're not sure. See links in https://stackoverflow.com/tags/x86/info. – Peter Cordes Sep 14 '20 at 10:02

1 Answers1

2

Shouldn't some sort of loop with a DEC ECX and JE 0 instructions be there as well?

That is almost exactly what the loop instruction does.

Nate Eldredge
  • 48,811
  • 6
  • 54
  • 82