6502 flags after adding signed byte to unsigned byte.

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

6502 flags after adding signed byte to unsigned byte.

Post by pops »

Working on my open world rpg (no issues of scale - have to thank Tepples for that link). I've been able to solve just about every NES-related problem I've come across so far, but this one is flummoxing me: how can I tell whether to inc or dec the hi byte of a 16bit unsigned number after adding a signed byte to the lo byte of the same 16 bit number?

Assembly:

Code: Select all

clc
lda CameraCurrentY
adc delta
sta CameraCurrentY
CameraCurrentY is the unsigned low byte of a 16bit scroll value.

delta is a signed byte with values from -4 to 4. ($fc ~ $04)

Which flags do I check to see if the low byte carried (greater than 255)? How do I check to see if the low byte rolled over the other direction (less than 0)?

Sincere thanks!
User avatar
tokumaru
Posts: 12385
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 6502 flags after adding signed byte to unsigned byte.

Post by tokumaru »

Normally you'd sign-extend the signed byte into a signed word (or just generate the delta as a 16-bit value to begin with), so that you can add the high bytes after taking care of the low bytes. I can't really think of another solution that doesn't involve tons of confusing branches.
tepples
Posts: 22603
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: 6502 flags after adding signed byte to unsigned byte.

Post by tepples »

Which raises the question of how to efficiently copy bit 7 into all bits of the accumulator. The constant-time snippet on the wiki page destroys the carry, so don't use it between adding the low bytes and adding the high bytes without saving and restoring P. Perhaps a more efficient way is to decrement the high byte of the sum if the sign bit of the 8-bit value is true: BPL over a DEC. (I just added that.)

Another question is raised as well: how to efficiently clamp addition of a signed 8- or 16-bit value to an unsigned 8- or 16-bit value so that adding a positive value doesn't cause the 16-bit value to exceed 65535 and adding a negative value doesn't cause the 16-bit value to fall below 0.
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

Re: 6502 flags after adding signed byte to unsigned byte.

Post by pops »

tokumaru wrote:Normally you'd sign-extend the signed byte into a signed word (or just generate the delta as a 16-bit value to begin with), so that you can add the high bytes after taking care of the low bytes. I can't really think of another solution that doesn't involve tons of confusing branches.
An easy solution. Thank you tokomaru.

Code: Select all

; ========================[ MapService_UpdateScroll ]===========================
; 1. Bounds target to current map extents.
; 2. Gets delta value based on difference between target & current.
; 3. Applies delta to current, scroll, and screen variables.
; NOTE: Delta greater than +/- 8 will break scrolling (engine loads only one tile's wide at a time
; NOTE:	For delta greater than -128/+127, this algorithm will not work.
; Wipes out a,x,y,$00,$01
MapService_UpdateCamera:
.scope
.alias	delta			$00
.alias	delta_hi		$01
.alias	is_neg			delta_hi
	jsr MapService_BoundTarget
	sec
	lda CameraTargetX
	sbc CameraCurrentX
	jsr _get_log_delta_from_a    ; get a sort-of-logish delta (fast scroll when far away, slower when close)
	
        cpy #$00                ; if no delta, do not scroll.
	beq _delta_y
	
        lda delta
	clc
	adc CameraCurrentX
	sta CameraCurrentX
	lda delta_hi
	adc CameraCurrentX2
	sta CameraCurrentX2
_delta_y:
	sec
	lda CameraTargetY
	sbc CameraCurrentY
	jsr _get_log_delta_from_a
	
	cpy #$00
	beq _out
	
	lda delta
	clc
	adc CameraCurrentY
	sta CameraCurrentY
	lda delta_hi
	adc CameraCurrentY2
	sta CameraCurrentY2
	lda Screen_Y
	`addm delta
	.if a >= #240
	{
		ldx delta_hi
		.if x == #$ff
		{
			`add 240
		}
		.else
		{
			`sub 240
		}
	}
	sta Screen_Y
_out:
	rts
_get_log_delta_from_a:
; wipes out a,x,y. returns log-ish delta.
; unsigned value of delta in y. signed value of delta in $00 (lo), $01 (hi)
	sta delta
	tax
	and #$80				; test for negative number
	sta is_neg				; needed to zero out is_neg value
	beq +
	txa
	and #$7f
	sta delta
	lda #$80
	sec
	sbc delta
	tax
*	txa
	ldy #$04
	and #$c0
	bne _return
	txa
	ldy #$03
	and #$30
	bne _return
	txa
	ldy #$02
	and #$0c
	bne _return
	txa
	ldy #$01
	and #$03
	bne _return
	ldy #$00
_return:
	lda is_neg
	beq +
	sty delta
	lda #$00
	sec
	sbc delta
	sta delta
	lda #$ff
	sta delta_hi
*	rts
.scend
pops
Posts: 91
Joined: Sun Apr 04, 2010 4:28 pm

Re: 6502 flags after adding signed byte to unsigned byte.

Post by pops »

tepples wrote:Which raises the question of how to efficiently copy bit 7 into all bits of the accumulator. The snippet on the wiki page destroys the carry, so don't use it between adding the low bytes and adding the high bytes without saving and restoring P. Perhaps a more efficient way is to decrement the high byte of the sum if the sign bit of the 8-bit value is true: BPL over a DEC.

Another question is raised as well: how to efficiently clamp addition of a signed 8- or 16-bit value to an unsigned 8- or 16-bit value so that adding a positive value doesn't cause the 16-bit value to exceed 65535 and adding a negative value doesn't cause the 16-bit value to fall below 0.
That's a very interesting and very 6502-ish way of solving the problem. If I were to do it again, from scratch, I'd definitely try it your way.

As to the second problem, I have no easy answers, but in my engine I always make sure that the target value is within the bounds of the map - since the algorithm I posted moves CameraCurrent towards CameraTarget, so long as CameraTarget is within bounds and delta never exceeds a very small value (only moves by $01 per frame when close to the target), CameraCurrent will never exceed the map bounds.
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Re: 6502 flags after adding signed byte to unsigned byte.

Post by blargg »

How about this for 16-bit + signed 8-bit:

Code: Select all

        lda delta
        bpl pos
        dec high
pos:    clc
        adc low
        sta low
        bcs noc
        inc high
noc:
Heh, and tepples suggested the same thing:
tepples wrote:Perhaps a more efficient way is to decrement the high byte of the sum if the sign bit of the 8-bit value is true: BPL over a DEC.
Another question is raised as well: how to efficiently clamp addition of a signed 8- or 16-bit value to an unsigned 8- or 16-bit value so that adding a positive value doesn't cause the 16-bit value to exceed 65535 and adding a negative value doesn't cause the 16-bit value to fall below 0.
8-bit signed + 8-bit unsigned, clamped to 8-bit unsigned:

Code: Select all

        clc
        lda signed
        bpl pos
neg:    adc unsigned
        bcs done
        lda #0
        beq done
pos:    adc unsigned
        bcc done
        lda #$ff
done:   sta unsigned

Test output

signed
   unsigned
      unsigned out
00 00 00 
FF 00 00 
FF 01 00 
80 00 00 
80 80 00 
80 81 01 
7F 80 FF 
7F 81 FF 
7F FF FF 

8-bit signed + 16-bit unsigned, clamped to 16-bit unsigned:

Code: Select all

        lda unsigned_lo
        clc
        adc signed
        sta unsigned_lo
        lda unsigned_hi
        bit signed
        bpl pos
neg:    adc #$ff
        bcs done
        lda #0
        beq clamp
pos:    adc #0
        bcc done
        lda #$ff
clamp:  sta unsigned_lo
done:   sta unsigned_hi

Test output

signed
   unsigned
        unsigned out
00 0000 0000 
00 0100 0100 
FF 0000 0000 
FF 0001 0000 
FF 0100 00FF 
80 0080 0000 
80 0100 0080 
01 00FF 0100 
01 FFFF FFFF 
7F FF81 FFFF 

16-bit signed + 16-bit unsigned, clamped to 16-bit unsigned:

Code: Select all

        lda unsigned_lo
        clc
        adc signed_lo
        sta unsigned_lo
        lda signed_hi
        bpl pos
neg:    adc unsigned_hi
        bcs done
        lda #0
        beq clamp
pos:    adc unsigned_hi
        bcc done
        lda #$ff
clamp:  sta unsigned_lo
done:   sta unsigned_hi

Test output

signed
     unsigned
          unsigned out
0000 0000 0000 
7FFF 8000 FFFF 
7FFF 8001 FFFF 
7FFF FFFF FFFF 
8000 0000 0000 
8000 7FFF 0000 
8000 8000 0000 
8000 8001 0001 
8000 FFFF 7FFF 
User avatar
tokumaru
Posts: 12385
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 6502 flags after adding signed byte to unsigned byte.

Post by tokumaru »

blargg wrote:How about this for 16-bit + signed 8-bit:

Code: Select all

        lda delta
        bpl pos
        dec high
pos:    clc
        adc low
        sta low
        bcs noc
        inc high
noc:
Heh, and tepples suggested the same thing:
tepples wrote:Perhaps a more efficient way is to decrement the high byte of the sum if the sign bit of the 8-bit value is true: BPL over a DEC.
Cool trick! It's less readable, but definitely useful for working with small deltas, like when moving objects or calculating sprite coordinates from meta-sprites.
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: 6502 flags after adding signed byte to unsigned byte.

Post by thefox »

blargg wrote:How about this for 16-bit + signed 8-bit:

Code: Select all

        lda delta
        bpl pos
        dec high
pos:    clc
        adc low
        sta low
        bcs noc
        inc high
noc:
Hmm, why bcs?
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
tokumaru
Posts: 12385
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 6502 flags after adding signed byte to unsigned byte.

Post by tokumaru »

thefox wrote:Hmm, why bcs?
Some people like to INC the high byte in additions only when necessary, instead of always adding 0.

EDIT: Yeah, turns out it should be bcc. Anyway I often prefer to add 0 to the high byte, specially if I'm storing the result in a new variable as opposed to modifying the existing one.
Last edited by tokumaru on Tue Feb 04, 2014 10:44 am, edited 1 time in total.
tepples
Posts: 22603
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: 6502 flags after adding signed byte to unsigned byte.

Post by tepples »

Unless someone got the sign wrong (BCC vs. BCS).
User avatar
tokumaru
Posts: 12385
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: 6502 flags after adding signed byte to unsigned byte.

Post by tokumaru »

tepples wrote:Unless someone got the sign wrong (BCC vs. BCS).
Oh crap! I'm missing a lot of things lately... :roll:
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Re: 6502 flags after adding signed byte to unsigned byte.

Post by blargg »

Obviously it was supposed to be bcc (hence noc -- no carry). I'm no god and untested code is probably buggy. :)
User avatar
thefox
Posts: 3134
Joined: Mon Jan 03, 2005 10:36 am
Location: 🇫🇮
Contact:

Re: 6502 flags after adding signed byte to unsigned byte.

Post by thefox »

It's a nice trick. :) And what's also nice is that because I have all my 16-bit arithmetic wrapped in macros, I can just drop this thing in in place of my old suboptimal routine.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
blargg
Posts: 3715
Joined: Mon Sep 27, 2004 8:33 am
Location: Central Texas, USA
Contact:

Re: 6502 flags after adding signed byte to unsigned byte.

Post by blargg »

I just threw those together for the posting, so I recommend further testing.
Post Reply