6502汇编:16位减法进位结果
6502 assembly: carry result in 16bit subtraction
我找回了多年前用来实现一些新功能的旧 6502 仿真器。在测试过程中我发现了一些错误,肯定是由于我的实现错误。
我必须循环执行 16 位减法,直到结果为负:很简单,不是吗?这是一个例子:
V1 equ
V2 equ
Label:
sec
lda V2
sbc #2
sta V2
lda V1
sbc #10
sta V1
"Branch" to Label if result is >0, otherwise exit
rts
现在的问题是确定要select哪个分支或找到不同的解决方案。
如果 V2 减法清除进位,则 BCS
无效。
如果 V1 为 'negative' (>$80),则 BPL
无效。
这应该很容易,但是...
编辑
我没有在答案中找到真正的解决方案。
让我尝试遵循逻辑,首先使用代码中的原始值。
- 进位由
SEC
设置
- 拳子(1-2)清进位。
V1
= $FF
- 第二个 sub ($90-$0A-1 (not borrow)) 结果
V2
=$85
- 进位被清除;结果($85FF)仍然为负)
我无法使用 BCS
(跳转到标签)或 BMI
来测试结果,因为 V2
是负数。
那么?
使用不同的集合,即 V1
=$1 和 V2
=$0A 我将得到一个 < 0 的结果,这是我停止迭代的目标。
有什么建议吗?
loop through a 16 bit subtraction until the result is negative
"Branch" to Label if result is >0,
你看到这些描述相互矛盾了吗?
第一个 在 0 继续,第二个 在 0.
停止
只有你能决定哪一个是正确的!
来自评论:
This code is part of a Bin to Ascii conversion, made by power of ten subtraction. The bin value could be >00, so it is 'negative' but this does not matter. In the first iteration I sub 10000 each cycle until the result is 'below 0', then I restore the previous value and continue with the remainder. The problem is how to detect the 'below 0' condition as said in the post
在 GE 0 时执行...循环
下一个示例从存储在零页地址 $90 的无符号字中减去 10000 ($2710)。低字节在$90,高字节在$91(little endian)。
Lo equ ; The 16-bit bin is stored at zero page address
Hi equ
sec ; Because SBC subtracts the complement of C
ldx #-1
Label inx
lda Lo ; Load from a zero page address
sbc # ; Subtracting immediate
sta Lo ; Store to a zero page address
lda Hi ; Load from a zero page address
sbc # ; Subtracting immediate
sta Hi ; Store to a zero page address
bcs Label ; Result GE 0 (greater or equal)
rts
X
寄存器现在包含原始数字中 10000 的次数。
在 GT 0 时执行...循环
您可以添加一条ora
指令来查看结果词是否已变为负数。然后根据 ora
的 Z
和(最后一个)sbc
.
的 C
进行分支
Lo equ ; The 16-bit bin is stored at zero page address
Hi equ
sec ; Because SBC subtracts the complement of C
Label lda Lo ; Load from a zero page address
sbc # ; Subtracting immediate
sta Lo ; Store to a zero page address
lda Hi ; Load from a zero page address
sbc # ; Subtracting immediate
sta Hi ; Store to a zero page address
ora Lo ; To define Z for the whole word
beq Done ; Result EQ 0 (equal)
bcs Label ; Result GT 0 (greater)
Done rts
我的 6502 手册说:“最重要的是,请记住比较是用 BCS
和 BCC
完成的( 不是 BPL
或 BMI
).
我找到了一个解决方案,繁琐而不优雅,但有效:
LDY #0
_B2D161 INY
PHA
TXA
SEC
SBC #
TAX
PLA
;----------------------
BCS _BD1 ;normal flow
SEC ;set carry for the next sub
SBC # ;the carry=0 would have incremented the minuend
BCS _B2D161 ;loop again
BCC _BD11 ;exit and restore the previous value
;-----------------------
_BD1 SBC # ;normal jump comes here
BCS _B2D161
_BD11 PHA
TXA
CLC
ADC #
TAX
PLA
ADC #
DEY
STY _L10K
... continue with next section
隔离部分显示应用的变体:如果第一个子清除进位,那么这是 'emulated' 通过减去一个增加 1 的值(#28 而不是 #27)并为下一个子恢复进位亚
这样我又可以依靠Carry来决定分支了。
我要感谢 Sep Roland 提供的建议代码(与我最初使用的代码相同),不幸的是它无法完成这项工作。
一般来说,这样的序列(我稍微修改了你问题中的代码,使其更加独立)
V1 equ
V2 equ
lda #00
sta V2
lda #20
sta V1
Label:
sec
lda V2
sbc #2
sta V2
lda V1
sbc #10
sta V1
将按预期工作。对于代码中使用的值,进位将在第一次减法后清除,并在第二次减法后再次设置。如果不是,则最可能的原因是模拟器中的错误。我在在线 6502 仿真中有 运行 这个,进位是在它到达最终 sta V1
时设置的。我还在 visual6502 中 运行 它,这里是跟踪输出:
cycle ab db rw Fetch pc a x y s p
0 0000 a9 1 LDA # 0000 aa 00 00 fd nv‑BdIZc
0 0000 a9 1 LDA # 0000 aa 00 00 fd nv‑BdIZc
2 0002 85 1 STA zp 0002 00 00 00 fd nv‑BdIZc
4 0090 00 0 0004 00 00 00 fd nv‑BdIZc
5 0004 a9 1 LDA # 0004 00 00 00 fd nv‑BdIZc
7 0006 85 1 STA zp 0006 14 00 00 fd nv‑BdIzc
9 0091 14 0 0008 14 00 00 fd nv‑BdIzc
10 0008 38 1 SEC 0008 14 00 00 fd nv‑BdIzc
12 0009 a5 1 LDA zp 0009 14 00 00 fd nv‑BdIzC
15 000b e9 1 SBC # 000b 00 00 00 fd nv‑BdIZC
17 000d 85 1 STA zp 000d 00 00 00 fd nv‑BdIZC
19 0090 fe 0 000f fe 00 00 fd Nv‑BdIzc
20 000f a5 1 LDA zp 000f fe 00 00 fd Nv‑BdIzc
23 0011 e9 1 SBC # 0011 14 00 00 fd nv‑BdIzc
25 0013 85 1 STA zp 0013 14 00 00 fd nv‑BdIzc
27 0091 09 0 0015 09 00 00 fd nv‑BdIzC
正如你在最后看到的那样,设置了进位(用大写字母 C 表示)。
顺便说一句,您的代码还有一个问题。如果你有 2000(十进制),那么要将它加载到两个位置,你需要在将它分成两个字节之前将它转换为十六进制。如果将 0 加载到零页位置并将小数点 20 加载到其后续位置,则实际上加载了 20 * 256 = 5120。2000 的十六进制为 d0
.
根据对问题的最新编辑。这是一段简化的代码,它从 </code> 中减去 <code>[=16=]a
并清除进位。
clc
lda #
sbc #[=12=]a
这是 Visual 6502 跟踪
cycle ab db rw Fetch pc a x y s p
0 0000 18 1 CLC 0000 aa 00 00 fd nv‑BdIZc
0 0000 18 1 CLC 0000 aa 00 00 fd nv‑BdIZc
2 0001 a9 1 LDA # 0001 aa 00 00 fd nv‑BdIZc
4 0003 e9 1 SBC # 0003 90 00 00 fd Nv‑BdIzc
6 0005 ea 1 NOP 0005 90 00 00 fd Nv‑BdIzc
8 0006 ea 1 NOP 0006 85 00 00 fd Nv‑BdIzC
如果您的模拟器在那种情况下没有设置进位,则您的模拟器有错误。
我找回了多年前用来实现一些新功能的旧 6502 仿真器。在测试过程中我发现了一些错误,肯定是由于我的实现错误。
我必须循环执行 16 位减法,直到结果为负:很简单,不是吗?这是一个例子:
V1 equ
V2 equ
Label:
sec
lda V2
sbc #2
sta V2
lda V1
sbc #10
sta V1
"Branch" to Label if result is >0, otherwise exit
rts
现在的问题是确定要select哪个分支或找到不同的解决方案。
如果 V2 减法清除进位,则 BCS
无效。
如果 V1 为 'negative' (>$80),则 BPL
无效。
这应该很容易,但是...
编辑
我没有在答案中找到真正的解决方案。
让我尝试遵循逻辑,首先使用代码中的原始值。
- 进位由
SEC
设置
- 拳子(1-2)清进位。
V1
= $FF - 第二个 sub ($90-$0A-1 (not borrow)) 结果
V2
=$85 - 进位被清除;结果($85FF)仍然为负)
我无法使用BCS
(跳转到标签)或BMI
来测试结果,因为V2
是负数。
那么?
使用不同的集合,即 V1
=$1 和 V2
=$0A 我将得到一个 < 0 的结果,这是我停止迭代的目标。
有什么建议吗?
loop through a 16 bit subtraction until the result is negative
"Branch" to Label if result is >0,
你看到这些描述相互矛盾了吗?
第一个 在 0 继续,第二个 在 0.
停止
只有你能决定哪一个是正确的!
来自评论:
This code is part of a Bin to Ascii conversion, made by power of ten subtraction. The bin value could be >00, so it is 'negative' but this does not matter. In the first iteration I sub 10000 each cycle until the result is 'below 0', then I restore the previous value and continue with the remainder. The problem is how to detect the 'below 0' condition as said in the post
在 GE 0 时执行...循环
下一个示例从存储在零页地址 $90 的无符号字中减去 10000 ($2710)。低字节在$90,高字节在$91(little endian)。
Lo equ ; The 16-bit bin is stored at zero page address
Hi equ
sec ; Because SBC subtracts the complement of C
ldx #-1
Label inx
lda Lo ; Load from a zero page address
sbc # ; Subtracting immediate
sta Lo ; Store to a zero page address
lda Hi ; Load from a zero page address
sbc # ; Subtracting immediate
sta Hi ; Store to a zero page address
bcs Label ; Result GE 0 (greater or equal)
rts
X
寄存器现在包含原始数字中 10000 的次数。
在 GT 0 时执行...循环
您可以添加一条ora
指令来查看结果词是否已变为负数。然后根据 ora
的 Z
和(最后一个)sbc
.
C
进行分支
Lo equ ; The 16-bit bin is stored at zero page address
Hi equ
sec ; Because SBC subtracts the complement of C
Label lda Lo ; Load from a zero page address
sbc # ; Subtracting immediate
sta Lo ; Store to a zero page address
lda Hi ; Load from a zero page address
sbc # ; Subtracting immediate
sta Hi ; Store to a zero page address
ora Lo ; To define Z for the whole word
beq Done ; Result EQ 0 (equal)
bcs Label ; Result GT 0 (greater)
Done rts
我的 6502 手册说:“最重要的是,请记住比较是用 BCS
和 BCC
完成的( 不是 BPL
或 BMI
).
我找到了一个解决方案,繁琐而不优雅,但有效:
LDY #0 _B2D161 INY PHA TXA SEC SBC # TAX PLA ;---------------------- BCS _BD1 ;normal flow SEC ;set carry for the next sub SBC # ;the carry=0 would have incremented the minuend BCS _B2D161 ;loop again BCC _BD11 ;exit and restore the previous value ;----------------------- _BD1 SBC # ;normal jump comes here BCS _B2D161 _BD11 PHA TXA CLC ADC # TAX PLA ADC # DEY STY _L10K ... continue with next section
隔离部分显示应用的变体:如果第一个子清除进位,那么这是 'emulated' 通过减去一个增加 1 的值(#28 而不是 #27)并为下一个子恢复进位亚
这样我又可以依靠Carry来决定分支了。
我要感谢 Sep Roland 提供的建议代码(与我最初使用的代码相同),不幸的是它无法完成这项工作。
一般来说,这样的序列(我稍微修改了你问题中的代码,使其更加独立)
V1 equ
V2 equ
lda #00
sta V2
lda #20
sta V1
Label:
sec
lda V2
sbc #2
sta V2
lda V1
sbc #10
sta V1
将按预期工作。对于代码中使用的值,进位将在第一次减法后清除,并在第二次减法后再次设置。如果不是,则最可能的原因是模拟器中的错误。我在在线 6502 仿真中有 运行 这个,进位是在它到达最终 sta V1
时设置的。我还在 visual6502 中 运行 它,这里是跟踪输出:
cycle ab db rw Fetch pc a x y s p
0 0000 a9 1 LDA # 0000 aa 00 00 fd nv‑BdIZc
0 0000 a9 1 LDA # 0000 aa 00 00 fd nv‑BdIZc
2 0002 85 1 STA zp 0002 00 00 00 fd nv‑BdIZc
4 0090 00 0 0004 00 00 00 fd nv‑BdIZc
5 0004 a9 1 LDA # 0004 00 00 00 fd nv‑BdIZc
7 0006 85 1 STA zp 0006 14 00 00 fd nv‑BdIzc
9 0091 14 0 0008 14 00 00 fd nv‑BdIzc
10 0008 38 1 SEC 0008 14 00 00 fd nv‑BdIzc
12 0009 a5 1 LDA zp 0009 14 00 00 fd nv‑BdIzC
15 000b e9 1 SBC # 000b 00 00 00 fd nv‑BdIZC
17 000d 85 1 STA zp 000d 00 00 00 fd nv‑BdIZC
19 0090 fe 0 000f fe 00 00 fd Nv‑BdIzc
20 000f a5 1 LDA zp 000f fe 00 00 fd Nv‑BdIzc
23 0011 e9 1 SBC # 0011 14 00 00 fd nv‑BdIzc
25 0013 85 1 STA zp 0013 14 00 00 fd nv‑BdIzc
27 0091 09 0 0015 09 00 00 fd nv‑BdIzC
正如你在最后看到的那样,设置了进位(用大写字母 C 表示)。
顺便说一句,您的代码还有一个问题。如果你有 2000(十进制),那么要将它加载到两个位置,你需要在将它分成两个字节之前将它转换为十六进制。如果将 0 加载到零页位置并将小数点 20 加载到其后续位置,则实际上加载了 20 * 256 = 5120。2000 的十六进制为 d0
.
根据对问题的最新编辑。这是一段简化的代码,它从 </code> 中减去 <code>[=16=]a
并清除进位。
clc
lda #
sbc #[=12=]a
这是 Visual 6502 跟踪
cycle ab db rw Fetch pc a x y s p
0 0000 18 1 CLC 0000 aa 00 00 fd nv‑BdIZc
0 0000 18 1 CLC 0000 aa 00 00 fd nv‑BdIZc
2 0001 a9 1 LDA # 0001 aa 00 00 fd nv‑BdIZc
4 0003 e9 1 SBC # 0003 90 00 00 fd Nv‑BdIzc
6 0005 ea 1 NOP 0005 90 00 00 fd Nv‑BdIzc
8 0006 ea 1 NOP 0006 85 00 00 fd Nv‑BdIzC
如果您的模拟器在那种情况下没有设置进位,则您的模拟器有错误。