1

I want to call a procedure depending on the contents of a register. After the procedure has finished it should return to the calling address so that the program can keep executing the code following the call opcode, otherwise it should ignore it and keep executing the rest of the code.

I'm trying to avoid just conditionally jumping over the call with a jcc, like the answers on call subroutines conditionally in assembly describe.

Is it possible? Something like this:

    cmp al,41h      ;check if register has value 41h
    call setAsize   ;call procedure if it does, otherwise ignore and continue executing code
   //more code to execute here


setASize:
    mov al,4d   ;change register content
    ret         ;return to call address so that code following call opcode can continue being executed

How would one implement this without using a jump?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
user931018
  • 643
  • 1
  • 10
  • 24
  • Possible duplicate of [call subroutines conditionally in assembly](https://stackoverflow.com/questions/7301683/call-subroutines-conditionally-in-assembly) – Simon Doppler Apr 02 '19 at 09:16
  • Can you specify which answer on that page you're referring to? – user931018 Apr 02 '19 at 09:57
  • This isn't a duplicate. The answer in that question doesn't execute the call depending on if the condition is met or not. What I have is a sort of if/else problem. If register contains a value, call a procedure, else, ignore and keep executing code. – user931018 Apr 02 '19 at 10:09
  • Why are you trying to avoid a jump? That's part of how conditional code is typically done in assembly. There is no single opcode for "call if" in x86 assembly. Alternatively, but probably overkill, if the possible values of `al` are limited unless you're happy with a table of 256 addresses, you could make a call table where all the values point to a subroutine that does nothing, and the entry at 41h points to `setAsize`. In this case, a simple compare and jump would be more straightforward. – lurker Apr 02 '19 at 10:42
  • I understand the first answer could use a clearer approach since it does not clearly show how the if-else block is written in assembly and I will answer it here. – Simon Doppler Apr 02 '19 at 10:48
  • @lurker I know it can be done with a jump, but I'm trying to exercise my asm programming and doing things like this helps me to become more familiar with the language. It also helps me understand the logical workings of asm – user931018 Apr 02 '19 at 12:20
  • We had a very similar question recently. The answer was that such a thing is (a) not worth the effort and (b) not really possible on x86. Let me see if I can find it. – fuz Apr 02 '19 at 12:53
  • Refer to [this question](https://stackoverflow.com/q/54868559/417501) for some further discussion. – fuz Apr 02 '19 at 12:57
  • 1
    @user931018 I understand. In this case, it probably is not a terribly fruitful endeavor as far as sharpening your asm skills, other than to learn that sometimes doing a conditional jump is a very concise way to get the job done, and sometimes the only way. In cases where you have multiple tasks based upon multiple small values, you might explore the call table that I mentioned. For example, if you want to call a different subroutine (action) based upon a value of 0, 1, 2, or 3, you can have a table of subroutine labels that you can access rather than using compare and jumps of the value. – lurker Apr 02 '19 at 13:48

2 Answers2

4

You want to implement a if-else structure in your assembly code as in the following C-code

if (al == 0x41) { // we keep your example values
    // do something
    setASize();
} else {
    // do something else
    // not present in your code but there for the sake of completeness
}

In assembly, you will write this the following way:

    cmp al, h41             ; this is the comparison, which sets flags 
    jne elseBranch          ; if the zero flag is *not* set (al != h41) jump to elseBranch
                        ; the jne instruction can be replaced with any other conditional
                        ; jump to adjust to the test condition
ifBranch:               ; useless label for clarity
    call setASize           ; this is the actual if-code (do-something)
    jmp endIf               ; now jump to the end of the if to avoid the else-branch
elseBranch:
                        ; nothing in your code, but this is where you put
                        ; your else instructions
endIf:
; now is the code after your if-else block, which will be executed in any case

This is the one of the two classic ways to write a if-else block in assembly (the reasonning is the same only the instructions change). The other option is to put the else-branch code before the if-branch to have the more logical conditional jump (since in the first example we test equality but jump if not equal). With this second option, the assembly code would be

    cmp al, h41             ; this is the comparison, which sets flags 
    je ifBranch             ; if the zero flag is *not* set (al != h41) jump to elseBranch
                        ; the jne instruction can be replaced with any other conditional
                        ; jump to adjust to the test condition
elseBranch:             ; useless label for clarity
                        ; nothing in your code, but this is where you put
                        ; your else instructions
    jmp endIf               ; now jump to the end of the if to avoid the else-branch
ifBranch:
    call setASize           ; this is the actual if-code (do-something)
endIf:
; now is the code after your if-else block, which will be executed in any case

In your case, since there is no else branch, the first option is prefered (only one jump required since you do not need to jump to the elseBranch labels (and do not need the second endIf jump).


For you code, the final answer would be:

    cmp al,41h
    jne endIf
    call setAsize
endIf:
                    ; more code here

setASize:
    mov al,4d
    ret
Simon Doppler
  • 1,918
  • 8
  • 26
  • Thanks for this! A very clear but detailed and descriptive answer! As someone who's learning asm this is great! – user931018 Apr 02 '19 at 12:20
  • @user931018: this is a good answer, but it looks like it's answering the question you claimed wasn't a duplicate. But accepting this answer basically shows it *is* a duplicate. Martin's answer is the one that explains why there's no other reasonable option, and answers the non-duplicate parts of your question. – Peter Cordes Apr 02 '19 at 12:25
  • @PeterCordes I suppose you are correct. However the answers on that question seemed somewhat vague to me and left me wanting more. Simon's answer was a lot easier to understand and the comparison he did using a higher level language also made it easier for someone who's been doing asm for a week to grasp. But I do see the similarities in the answers. To avoid duplicate posts, should I delete this question? – user931018 Apr 02 '19 at 12:36
  • 1
    @user931018: No, probably Simon should post it over there, and we should all go upvote it there. :P Don't delete this question, it has 2 good answers so you'd be throwing away valuable content (even if it is maybe harder to find than it needs to be.) – Peter Cordes Apr 02 '19 at 12:38
4

I want to call a procedure depending on the contents of a register. ...I'm trying to avoid using a jump here. Is it possible? ... How would one implement this without using a jump?

Unfortunately, most CPUs support conditional execution only for jump/branch instructions.

ARM CPUs support conditional execution for nearly all instructions; the historic 8080 and compatibles (8085, Z80) supported conditional execution of CALL and RET in addition to jumps.

However, x86 CPUs are like most CPUs: Only jump instructions can be executed conditionally.

Hypothetically, you could perhaps use self-modifying code in RAM to perform any instruction conditionally without using jump instructions. However, doing this would only be useful for some feasibility study and not for any real use case.

Martin Rosenau
  • 17,897
  • 3
  • 19
  • 38
  • MIPS also has conditional branch-and-link, but unfortunately not for a condition that you can use directly with `slt` for a 0 or 1 integer :/ [Why are bgezal & bltzal basic instructions and not pseudo-instructions in MIPS?](//stackoverflow.com/a/20680766) – Peter Cordes Apr 02 '19 at 12:15
  • Self modifying code would also likely need a conditional jump to set the correct code. – lurker Apr 02 '19 at 12:17
  • 1
    Perhaps also worth mentioning that a conditional tail-call is totally fine, and doesn't break return-address prediction or anything. (But you can only do it if the stack is in the same state as on entry to this function, pointing at your own return address which then becomes the next function's return address.) – Peter Cordes Apr 02 '19 at 12:28
  • @lurker What I was talking was the following: Using multiple of `cmp`, `adc` and bitwise operations (but no jumps), it should be possible to get a number 1 or 0 based on a condition (e.g. `ah=1` if `al==0x4E`, `al=0` otherwise). Now you can use multiple `add` and `and` instructions so `ah=1` becomes `ah=123` (let's say the 2nd byte of the `call` instruction) and `ah=0` becomes `ah=0x90` (the `nop` instruction). Then you always overwrite the 3 (or 5) bytes of the `call` instruction. As I already said: This has no realistic use case. – Martin Rosenau Apr 02 '19 at 13:39