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。
这种梗
我有一个循环 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。