Author Topic: My code won't jump  (Read 13218 times)

Offline gabloo

  • Jr. Member
  • *
  • Posts: 4
My code won't jump
« on: February 12, 2014, 08:45:46 AM »
Hey guys,

First of all Hi everyone! I am taking an assembly class right now and I was assigned to write a program. I do experience with C++ but very minor knowledge with assembly. I had faced a few challenges while i was coding in assembly and was able to solve them but I couldn't figure what i did wrong this time.

The issue that I am facing now is, my code will not jump to where I wanted. Can you guys please take a look at it and tell me what am i doing wrong? I made it mark on the code. The program is not complete yet but I need to figure this out before i can move forward.


I am trying to jump to .50range when edx is equal or larger than 50.

Thanks guys

Code: [Select]
        mov eax, 1
        mov ebx, 1
        mov edx, 1
.loop:
        cmp eax, 101
        jge .end
        push eax
        push num
        call printf
        cmp edx, 50             ; <<<<<<< here
        jge .50range            ;<<<<<<< this won't jump when edx is equal or grater than 50
.50back:
        cmp ebx, 5
        je .Fizz
        jmp .continue
.50range:
        cmp edx, 60
        jge .50back
        push testing
        call printf
        pop eax
        jmp .continue
.continue:
        pop eax
        pop eax
        inc eax
        inc edx
        inc ebx
        jmp .loop
.Fizz:
        push Fizz
        call printf
        pop ebx
        mov ebx, 0
        jmp .continue
.end:
        pop eax


Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: My code won't jump
« Reply #1 on: February 12, 2014, 09:26:23 AM »
My best guess would be that "call printf" is trashing edx. Try something like:

Code: [Select]
    pusha ; save everything printf is going to trash
    push eax ; number to display (?) what is it?
    push num ; format string (?)
    call printf
    add esp, 8 ; clean up stack!
    popa ; restore our stuff
    cmp edx, 50
    ...

There may be other places where you need to clean up the stack after calling printf... and watch out for registers it trashes (ebp, ebx, esi, and edi are preserved, ecx and edx are trashed and eax is the return value - which isn't very interesting in this case, but can be). You might want to provide more comments...

Best,
Frank


Offline gabloo

  • Jr. Member
  • *
  • Posts: 4
Re: My code won't jump
« Reply #2 on: February 12, 2014, 10:25:47 PM »
Thanks! i switched my code from edx to esi and fixed all the issue. Can you please explain how add esp, 8 clean up the stack? I researched around but couldn't find the answer that I am looking for.

Thanks again,

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: My code won't jump
« Reply #3 on: February 12, 2014, 11:52:12 PM »
Good! Using esi is probably a better solution than what I showed.

You may want to look up some information on "calling conventions". Agner Fog has a document somewhere around http://www.agner.org on the subject. In this case - 32-bit C library calls - you're using the "cdecl" convention, in which parameters are pushed on the stack, and it's the caller's responsibility to "remove" them. Each "push" subtracts 4 bytes from esp and puts a value there. So after calling some function, "add esp, 8" puts esp back where it was. I sometimes write it as "add esp, 4 * 2" - two parameters at 4 bytes each.

The Windows API uses the "stdcall" calling convention, which likewise pushes parameters on the stack, but it's the callee's responsibility to "remove" them. We don't usually see the code for "MessageBoxA", for example. but it takes 4 parameters (4 bytes each) so ends in "ret 16" - "ret" with a parameter after it adds that much to esp after returning. So we can push parameters and the called function has "removed" them when it returns.

In neither case has anything actually been "removed" - the values are still on the stack where they were, but esp points above them. Anything under esp is considered "free" and the next "push" or "call" overwrites them. (it may not be too obvious that "call" and "ret" are using the stack, but they are - that's why it needs to be kept "cleaned up" or "balanced")

64-bit calling conventions are completely different (bless their little black hearts!).

If I've failed to answer your question, ask again...

Best,
Frank


Offline gabloo

  • Jr. Member
  • *
  • Posts: 4
Re: My code won't jump
« Reply #4 on: February 13, 2014, 11:05:22 PM »
Thank you so much Frank! You explained very well!

Offline gabloo

  • Jr. Member
  • *
  • Posts: 4
Re: My code won't jump
« Reply #5 on: February 19, 2014, 07:33:25 AM »
Hey Frank,

Can you tell me which register are safe to use? Also, is there anywhere i can look up all the registers that are available. I googled around but i couldn't find the answer that I am looking for. I did find nasm manual but it doesn't the answer that i am looking.

Thanks again,

Offline Frank Kotler

  • NASM Developer
  • Hero Member
  • *****
  • Posts: 2667
  • Country: us
Re: My code won't jump
« Reply #6 on: February 19, 2014, 11:30:22 AM »
Umm... all registers are safe to use, provided they're used appropriately. I suppose you mean "when calling C library functions". As I mentioned above, ebp, ebx, esi, and edi are preserved, ecx and edx are trashed and eax is the return value. They're all safe to use, but don't expect eax, ecx, and edx to remain unchanged when you call a C function. That doesn't mean you can't use 'em in between. If you're writing code that returns to C ("main" returns to C - or can), you're expected to follow the same rules - preserve ebp, ebx, esi, and edi. You're not required to trash ecx and edx of course, but you can. You're expected to put the return value (if there is one) in eax. If in doubt, return zero.

The Intel/AMD manuals are probably the best source of info on registers. I can rattle off the "general purpose" registers - eax, ebx, ecx, edx (these ones have byte parts al, ah, bl, bh, etc.), esi, edi (these don't have byte parts - but the 64-bit versions do!), ebp (usually used to point into the stack), and esp (always used by stack instructions call, ret, push, pop - not generally "safe" to use otherwise), they all have 16-bit "word" parts, just leave off the 'e' - but there are segment registers, control registers, debug registers... probably others I know nothing about. You "probably" don't need to use 'em just yet.

I see I forgot to mention the "flags" or "status" (or "eflags" for 32-bit) register. This is not a general purpose register and special instructions apply to it. Individual bits have different meanings and are used/altered in different (not very consistent) ways. Some bits are altered by the results of certain instructions ("arithmetic" instructions generally - "data movement" instructions don't touch 'em) and the state of these bits is consulted by the "conditional jump" instructions. Your "jge" that wasn't working is an example of that. The "cmp" sets the flags (like a "sub" but the result isn't stored) and the "jge" jumps or not, depending on how the flags are set. There's a subtle issue that might concern you. "jg" (and "jl") are a signed comparison - that is, the overflow flag is checked as well as the carry flag. If the value in edx (I guess you're using esi now) had the high bit set (0x80000000 or bigger) it would be treated as "less" than zero and the jump would not be taken. Use "jb" (jump if below) and "ja" (jump if above) for unsigned comparisons. I don't think it'll be a problem in your code, but it's an important difference to know.

I'm tired, I'm rambling, those other bits are going to have to wait...

Best,
Frank