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 无效。
这应该很容易,但是...

编辑
我没有在答案中找到真正的解决方案。
让我尝试遵循逻辑,首先使用代码中的原始值。

  1. 进位由SEC
  2. 设置
  3. 拳子(1-2)清进位。 V1 = $FF
  4. 第二个 sub ($90-$0A-1 (not borrow)) 结果 V2=$85
  5. 进位被清除;结果($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指令来查看结果词是否已变为负数。然后根据 oraZ 和(最后一个)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 手册说:“最重要的是,请记住比较是用 BCSBCC 完成的( 不是 BPLBMI).

我找到了一个解决方案,繁琐而不优雅,但有效:

           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

如果您的模拟器在那种情况下没有设置进位,则您的模拟器有错误。