GBZ80 - 代码缺陷

GBZ80 - Code shortcomings

这种梗

我有一个循环 0x167 次的代码,写入屏幕。 我希望脚本循环三次。

我的脚本:

    d322 21A0C3 LD HL,C3A0h
    d323 016801 LD BC,0168h
    d324 110300 LD DE,0003h
    d325 00     NOP
    .
    .
    .
    d330 0B     DEC BC
    d331 75     LD (HL),L
    d332 00     NOP
    d333 23     INC HL
    d334 00     NOP
    .
    .
    .
    d33f 78     LD A,B
    d340 B1     OR C
    d341 C22BD3 JP NZ,D32Bh
    d344 7A     LD A,D
    d345 B3     OR E
    d346 1B     DEC DE
    d347 00     NOP
    d348 00     NOP
    d349 C22BD3 JP NZ,D32B
    d34c C9     RET

现在,它没有停止在它应该停止的地方,而是继续破坏内存,直到它到达 d325 并导致脚本崩溃。如您所知,我尝试使用 DE 作为循环计数器。

请解释一下你的答案,我对此仍然很迟钝。

我的阅读是你:

Set HL = 0xC3A0, BC = 0x0168, DE = 0x0003

do {
  ... something you've omitted ...
  do {
    bc--
    [l] = hl
    hl++
  } while(bc)

  was_positive = (DE > 0)
  DE--
} while(was_positive)

因此外层循环将发生 四次 次,因为模式将是:

  • 执行内循环(* 1)
  • 比较 3 和 0
  • 执行内循环(* 2)
  • 比较 2 和 0
  • 执行内循环(* 3)
  • 比较 1 和 0
  • 执行内循环(* 4)
  • 比较0和0,不再大于,退出

可能你想在比较前做DEC?将 DE 与零进行比较的是 LD A, D / OR E - 如果其位正好设置为零,则您断言它为零 - 随后的递减不会改变您已经计算出的结果。

在这种情况下,您将获得 0x0168 + 4 * 0x10000 次迭代 = 0x40168 次迭代。即使那样,这可能不是您想要的?这意味着您将多次覆盖内存中的每个地址。即使您的代码在 ROM 中是安全的,那也是大量浪费的代码。

如果你打算循环 3 * 0x0168 次那么也记得重新加载 BC。您只需让它下溢到 0xffff 并恢复。考虑切换初始加载的顺序,使 BC 在最后,并更改外部分支以跳回到加载它的位置。

问题是您在设置标志后递减 DE。代码的关键位是:

LD A,D
OR E
DEC DE
JP NZ,D32B

将其更改为:

DEC DE
LD A,D
OR E
JP NZ,D32B

并且外部循环将 运行 3 次而不是 4 次。请注意 BC 循环被正确处理并且将执行 0x168 次,而不是 0x167 次,因此您可能想要更改它。

Z-80 上的 16 位计数器很笨拙,因为 16 位递减不设置任何标志。这就是为什么当 16 位对等于 0 时,您需要代码将两个寄存器进行或运算以设置 Z 标志。

由于您只需要 3 次迭代,您可以使用一个 8 位寄存器并写入:

LD E,3
...
DEC E
JP NZ,D32B

如 Tommy 的回复所述,您还需要在内循环中重新加载 BC,例如将 LD BC,0x168 移动到 D32B。