问题
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
andhalt
(halt
= stop running).Task:
A
andB
are registers that hold non-negative values. The program should calculate the value of|A-B|
and locate the result inC
. In addition, the language holds registersC
,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