dx
用作指令计数器的值loop
来自第一mul
条指令。
这种乘法只是将密钥加倍,因此dx
0 或 1 也是如此(理解这一点的一种简单方法是将乘法视为左移 1 或记住两个 n 位数字的和最多有n+ 1位)
如果dx
为零,则整个loopy1
块什么都不做(dx
也设置ax
了),并且ax
保险箱末尾的值是 7*(5 +2k) 其中k是键(请参见下面的注释代码)。
然后很容易看出 350 = 7*(5+2k) => 2k = 45 没有解。因此,没有钥匙dx
为零的钥匙可以打开保险箱。
如果一个键dx
的值小于 32768,则其值为 0(同样,当将乘法视为左移 1 时,这很容易看出)。
推论: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。有了这个条件,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确实。