组装保险箱和钥匙 - 为什么它不起作用?

Assembly safes and keys- why it won't work?

所以我们在组装中遇到了这样的保险箱挑战,您需要创建保险箱和钥匙来破坏它们并结束无限循环。 这是一个保险箱的例子:

loopy:
mov ax, [1900]
cmp ax,1234
jne loopy

和一把钥匙:

loopy2:
mov ax, 1234
mov [1900],ax
jmp loopy2

所以我有一个保险箱和一把钥匙,但我不明白为什么它不起作用:

这是我的保险箱:

org 100h
mySafe:  
mov dx,5
mov ax, [5768h] 
mov bx,7 
mov word [180h],2
mul word [180h]
mov [180h],bx
push ax
dec bx
mov cx,dx
mov ax,dx
loopy1:
add bx,ax
loop loopy1
dec bx
pop ax
add ax,bx
mul word [180h]
cmp ax,350
jne mySafe

这是我的钥匙:


org 100h
loopy:
mov word [5768h],10
jmp loopy


ret

打破循环的正确答案应该是 10 并且当我放入保险箱时它可以工作,但不知何故用钥匙它不起作用而且我不明白为什么.. (nasm 需要 "word")

dx 中用作 loop 指令计数器的值来自第一个 mul 指令。

这个乘法只是将密钥加倍,所以 dx 要么是 0 要么是 1(理解这一点的一种简单方法是将乘法视为左移一位,或者记住两个n位数最多有n+1位)


如果 dx 为零,整个 loopy1 块什么都不做(因为 dx 也设置 ax)并且 ax 中的值在保险箱的末端是 7*(5 +2k),其中 k 是钥匙(请参阅下面的注释代码)。

那么很容易看出 350 = 7*(5+2k) => 2k = 45 无解。因此 dx 为零的钥匙无法打开保险箱。
如果一个键的值小于 32768,则它的值为 dx 0(同样,当将乘法视为左移一位时,这很容易看出)。

推论:10 不是解。

safe:
  mov dx,5
  mov ax, [k]               ;ax = k (key)
  mov bx,7 
  mov word [aux],2    
  mul word [aux]            ;dx = 0 ax = 2k
  mov [aux],bx              ;aux = 7
  push ax                   ;ax = 2k
  dec bx                    ;bx = 6 
  dec bx                    ;bx =    5
  pop ax                    ;ax = 2k
  add ax,bx                 ;ax = 5 + 2k
  mul word [aux]            ;ax = 7*(5 +2k)

  cmp ax,350
  ret 

如果有一把钥匙可以打开保险箱,那么它必须大于或等于 32768,以便 dx 在第一次乘法后为 1。 在这种情况下,safe 末尾 ax 中的值可以写为 7*(6 + (2k & 0xffff)) => k & 0x7fff = 22.
添加本节开头所述的条件,k 的最终值为 32768 + 22 = 32790 或十六进制的 0x8016。 在处理方程式和形成结果时,我已经跨越了相当多的逻辑步骤,但是,再一次,将 2k 视为一个转变可能有助于形象化它们。

推论:由于涉及代数结构,这是唯一的解决方案。

safe:
  mov dx,5
  mov ax, [k]           ;ax = k
  mov bx,7 
  mov word [aux],2    
  mul word [aux]            ;dx:ax = 2k
  mov [aux],bx              ;[aux] = 7
  push ax                   ;dx = 1 ax = 2k & 0xffff
  dec bx                    ;bx = 6 
  mov cx,dx                 ;cx = 1
  mov ax,dx                 ;ax = 1
loopy1:
  add bx,ax                 ;bx = 6 + 1
  dec cx
jnz loopy1
  dec bx                    ;bx = 6 
  pop ax                    ;ax = 2k & 0xffff
  add ax,bx                 ;ax = 6 + (2k & 0xffff)
  mul word [aux]            ;ax = 7*(6 + (2k & 0xffff))

  cmp ax,350
  ret 

考虑到你在第一次乘法之前有一个mov dx, 5你(或保险箱的作者)是否忘记了mul影响dx?
如果您将第一个 mul 包裹在 push dx / pop dx 中(或者只是在其后移动 mov dx, 5),您将在保险箱的末尾得到 ax 中的一个值等于7*(30 +2k) 这意味着确实 k = 10