Search code examples
assembly6502

6502 assembly: carry result in 16bit subtraction


I have recovered an old 6502 emulator I did years ago to implement some new features. During testing I discovered something wrong, surely due to an error in my implementation.
I have to loop through a 16 bit subtraction until the result is negative: quite simple, no? Here is an example:

V1 equ $90  
V2 equ $01  
Label:
 sec  
 lda V2  
 sbc #2  
 sta V2  
 lda V1  
 sbc #10  
 sta V1   
 "Branch" to Label if result is >0, otherwise exit  
 rts  

Now, the problem is to identify which branch to select or to find a different solution. The BCS is not valid if the V2 subtraction clears the carry.
The BPL in not valid if V1 is 'negative' (>$80).
It should be easy, but...

EDIT
I did not find in the aswers a real solution.
Let me try to follow the logic, firstly with the original values as in code.

  1. Carry is set by SEC
  2. The fist sub (1-2) clears the carry. V1 = $FF
  3. the second sub ($90-$0A-1 (not borrow)) results in V2=$85
  4. carry is cleared; result ($85FF) is still negative)
    I can not test the result with BCS (to jump to label) nor BMI since V2 is negative.
    So?

With a different set, i.e. V1=$1 and V2=$0A I will have a result < 0, which is my goal to stop iterations.
Any suggestion?


Solution

  • Generally, a sequence like this (I've slightly amended the code in your question to make it more self contained)

    V1 equ $91  
    V2 equ $90  
     lda #00
     sta V2
     lda #20
     sta V1 
    Label:
     sec  
     lda V2  
     sbc #2  
     sta V2  
     lda V1  
     sbc #10  
     sta V1   
    

    will work as expected. With the values used in the code, the carry will be cleared after the first subtraction and will be set again after the second. If it is not, then the most likely cause is a bug in your emulator. I have run this in an online 6502 emulation and the carry was set by the time it got to the final sta V1. I also ran it in visual6502 and here is the trace output:

    
    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
    

    As you can see at the end, the carry is set (denoted by a capital C).

    There is another problem with your code, by the way. If you have 2000 (decimal), then to load it into two locations, you need to convert it to hex before you slice it into two bytes. If you load 0 into a zero page location and 20 decimal into its successor, you've actually loaded 20 * 256 = 5120. The hex for 2000 is $07d0.


    Following the latest edit to the question. Here is a simplified bit of code that subtracts $0a from $90 with the carry clear.

    clc
    lda #$90
    sbc #$0a
    

    And here is the Visual 6502 trace

    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
    

    If your emulator leaves the carry unset in that situation, your emulator has a bug.