如何在一个将自己缝合到自己的尾巴上的程序中环绕 64KB 代码段,无限循环?

How to wraparound in the 64KB code segment in a program that stitches itself to its own tail, ad infinitum?

If the sequential execution of instructions passes offset 65535, then the 8086 will fetch the next instruction byte from offset 0 in the same code segment.

下一个 .COM 程序利用了这一事实,并不断地将其整个代码(总共 32 个字节)拼接到自己的尾部,环绕在 64KB 的代码段中。你可以称其为 binary quine.

    ORG 256            ; .COM programs start with CS=DS=ES=SS

Begin:
    mov  ax, cs        ; 2 Providing an exterior stack
    add  ax, 4096      ; 3
    mov  ss, ax        ; 2
    mov  sp, 256       ; 3
    cli                ; 1
    call Next          ; 3 This gets encoded with a relative offset
Next:
    pop  bx            ; 1  -> BX is current address of Next
    sub  bx, 14        ; 3  -> BX is current address of Begin
More:
    mov  al, [bx]      ; 2
    mov  [bx+32], al   ; 3
    inc  bx
    test bx, 31        ; 4
    jnz  More          ; 2
    nop                ; 1
    nop                ; 1
    nop                ; 1

为了 callpop 指令的好处,程序将在代码段外部设置一个小堆栈。我不认为 cli 真的有必要,因为我们确实有一个堆栈。
一旦我们计算出 32 字节程序的当前起始地址,我们就将它复制到内存中高 32 字节的位置。所有 BX 指针运算都会回绕。
然后我们在新编写的代码中失败了。

If the sequential execution of instructions passes offset 65535, then the 80386 will trigger exception 13.

假设我包括了异常处理程序的必要设置,仅执行远跳转到此代码段的开头(新编写的代码等待的地方)就足够了吗? 这样的解决方案在 post 80386 CPU 上是否仍然有效?


相关:Is it possible to make an assembly program that writes itself forever?

在 16 位模式(真实或保护)下,IP 寄存器将无任何错误地绕过 64KiB,前提是没有指令跨越 64KiB 边界(例如,位于 0xffff).

的两字节指令

交叉指令会在 80386+ 上出错,不确定以前的型号会发生什么情况(读取线性地址中的下一个字节 space?从 0 读取下一个字节?)。

请注意,这是可行的,因为段限制与 IP 寄存器“限制”相同。
16位保护模式下可以设置小于64KiB的段限制,这样执行到最后会报错
简而言之(比喻),CPU 确保它需要的所有字节都在段限制内,然后将在没有溢出检测的情况下递增程序计数器。

所以你的程序应该可以运行。


说它是 quine 可能有点牵强,因为它正在读取自己的机器代码,这是作弊(就像读取源代码文件是针对高级语言的)。

我还没有测试过,但是一个“有点复制”本身的程序的最小示例可能是:

 ;Setup (assuming ES=CS)
 mov al, 0abh       ;This encodes stosb
 mov di, _next      ;Where to start writing the instruction stream

 stosb              ;Let's roll

_next: 

这也不是 quine,因为只有 stosb 被复制了。
制作 quine 很难,存储必须是编码小于存储数据大小的指令,否则我们要写入的字节总是多于写入的字节。