Calculate absolute difference |A-B| in assembly using only INC, DEC, JNZ, HALT - interview question

泄露秘密 提交于 2021-02-08 03:31:55

问题


This is a question I encountered in an interview and for which I didn't find a solution - so I tried to solve it myself.
We can use pseudocode here - it doesn't need to be a formal code.

The question is to get the absolute difference of unsigned inputs:

Assume your assembly language includes ONLY the following instructions: inc, dec, jnz and halt (halt = stop running).

Task: A and B are registers that hold non-negative values. The program should calculate the value of |A-B| and locate the result in C. In addition, the language holds registers C, D, ..., Z, which you can assume are initialized to zero at program start.

This is my attempt, where my main idea is to decrease both registers till one becomes zero, and then move the other register's value to C:

// zero case handling
dec a
inc a
jnz a_pre_loop_not_zero // a != 0
// a == 0, check if b == 0
dec b
inc b
jnz move_b_to_c // a == 0, b !=0 
// a == 0 , b == 0 -> c needs to be 0
halt  

a_pre_loop_not_zero:
  dec b
  inc b
  jnz main_loop // a != 0, b != 0

// a != 0 , b == 0
move_a_to_c:
    inc c
    dec a
    jnz move_a_to_c
    halt  

// a,b != 0
main_loop:
  dec b
  jnz b_not_zero // b!=0
  // b became zero before a -> a contains result+1 now
  dec a
  jnz move_a_to_c
  halt // if a == 0 now -> a == b -> c needs to be 0

  b_not_zero:
      dec a
      jnz main_loop // a != 0

  // a became zero before b -> b contains the result now
  move_b_to_c:
    inc c
    dec b
    jnz move_b_to_c
    halt              

Now, I think that it works - but it looks very dirty.
To be more specific, I think the zero case handling can be done in a cleaner way, and maybe we can even consider it in the main loop (without checking it in a pre loop code).
Also, I didn't use the fact that registers C, D, ..., Z are initialized to 0 and can be used - which makes me suspect that maybe there's a better way.

Is there a better solution for this problem?


回答1:


Now, I think that it works - but it looks very dirty.

You can improve how it looks by writting it more the assembly way:

    dec A                   // zero case handling
    inc A
    jnz A_pre_loop_not_zero // A != 0
    dec B                   // A == 0, check if B == 0
    inc B
    jnz move_B_to_C         // A == 0, B !=0 
    halt                    // A == 0, B == 0 -> C needs to be 0
A_pre_loop_not_zero:
    dec B
    inc B
    jnz main_loop           // A != 0, B != 0

move_A_to_C:                // A != 0, B == 0
    inc C
    dec A
    jnz move_A_to_C
    halt  
main_loop:                  // A,B != 0
    dec B
    jnz B_not_zero          // B != 0
    dec A                   // B became zero before A -> A contains result+1 now
    jnz move_A_to_C
    halt                    // if A == 0 now -> A == B -> C needs to be 0
B_not_zero:
    dec A
    jnz main_loop           // a != 0
move_B_to_C:                // a became zero before b -> b contains the result now
    inc c
    dec B
    jnz move_B_to_C
    halt              

The suggestion by Eric Eidt to increment A and B beforehand moves away from possible zeroes.
I think we can assume that both inc and dec use wraparound logic. If not these increments on A and B would be wrong!

    inc     A
    inc     B

L1: dec     A
    jnz     L4

L2: dec     B
    jnz     L3
    halt
L3: inc     C      ; A == 0 -> C = B
    jnz     L2     ; (*)

L4: dec     B
    jnz     L1

L5: inc     C      ; B == 0 -> C = A
    dec     A
    jnz     L5
    halt

Incrementing the C register will never produce zero. Therefore the conditional jump marked with an asterisk will always jump. This shaved off 2 instructions.

A bit old school perhaps, but definitively nice to look at...



来源:https://stackoverflow.com/questions/62118756/calculate-absolute-difference-a-b-in-assembly-using-only-inc-dec-jnz-halt

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!