6502.org Forum  Projects  Code  Documents  Tools  Forum
It is currently Wed Sep 25, 2024 1:31 pm

All times are UTC




Post new topic Reply to topic  [ 11 posts ] 
Author Message
 Post subject: Shifting with ADC/SBC
PostPosted: Sun Jul 18, 2021 5:58 am 
Offline

Joined: Sun Jul 11, 2021 7:53 am
Posts: 3
I've been toying around with the 6-instruction, 8-byte, 12-cycle nybble swap code found at http://6502.org/source/general/SWN.html and I've been intrigued by the genius of this technique. I was curious if there were any other uses for this or a similar sort of pattern. Like what if you ADC different values, or used SBC instead, or shifted to the right instead of to the left. So far, I almost came up empty handed.

I did find that using SBC #$7F gives the exact same result as the ADC #$80, even the resulting overflow and carry flags are exactly the same. In addition, I found that the overflow always matches bit 0 of the result, and the carry is always the opposite. So if the result from the swap is even, the overflow will be 0 and carry will be 1. If odd, then overflow is 1 and carry is 0. I don't think this info would be of any use to anyone, but I though it was interesting enough and worth mentioning.

I have observe the flag results by making a loop, running every possible value through it, and saving both flag results for inspection. Here's my code if anyone wants to see it. The offset from location $6000 represents the resulting values from the swaps. The values of those locations represent the 2 flags: overflow (high nibble) and carry (low nibble).
Code:
.ORG $0300
LDX #$00 ;Counter

Loop
TXA ;Source bytes

ASL
ADC #$80 ;or SBC #$7F
ROL
ASL
ADC #$80 ;or SBC #$7F
ROL

TAY ;Use result byte as index

BVS V_Set
LDA #$00 ; bit3=v:clear
.byte $2C ;BIT instr to skip next LDA
V_Set
LDA #$08 ; bit3=v:set
ROL ;Flags bit4=v, bit0=c
STA $6000,Y

INX
BNE Loop
RTS

Assembled with: https://www.masswerk.at/6502/assembler.html
Code tested in: AppleWin

You can paste this into your emulator. Warning: Screen will scroll!
300:A2 00 8A 0A 69 80 2A 0A 69 80 2A A8 70 03 A9 00 2C A9 08 2A 99 00 60 E8 D0 E8 60 N 300G N 6000.60FF

ADC,ADC - 304:69 80 N 308:69 80 N 300G N 6000.60FF
SBC,SBC - 304:E9 7F N 308:E9 7F N 300G N 6000.60FF
ADC,SBC - 304:69 80 N 308:E9 7F N 300G N 6000.60FF
SBC,ADC - 304:E9 7F N 308:69 80 N 300G N 6000.60FF


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 18, 2021 9:33 am 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
I think ADC and SBC are basically the same inside the CPU, it's just that SBC inverts the second operand before adding it on. So subtracting $7F is the same as adding $80. This is why the carry flag behaviour feels backwards.

The nybble rotate code is clever but not magic. The idea is to rotate four times but without including the carry flag in the operation, unlike the 6502's normal rotate instruction. The first line shifts one place with the high bit going into the carry flag. To complete the first shift we just want to get that value out of the carry flag into the low bit. ADC #0 would do that, and you could repeat those two instructions four times to get the desired result. It only works with left shifts because ADC can only move the carry flag into the bottom bit.

The clever shortcut in that code though is that instead of adding zero to extract the carry flag into the bottom bit, it adds $80 which also inverts the top bit, causing a new carry if it was set originally. This effectively moves that top bit into the carry flag, which allows a follow-up ROL to rotate that value out of the carry flag into the bottom bit more efficiently.

So that pair of instructions achieves a two-bit ROL skipping over the carry flag, and doing it twice achieves the desired four-bit rotate.

It's a very neat sequence.

Changing the constants would allow you to add arbitrary amounts to the result, but not much more than that.


Top
 Profile  
Reply with quote  
PostPosted: Sun Jul 18, 2021 6:33 pm 
Offline

Joined: Wed Jun 23, 2021 8:02 am
Posts: 166
This website has lots of crafty little ways of doing common operations using combinations of bit shifting, boolean operations and arithmetic:

https://graphics.stanford.edu/~seander/bithacks.html


Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 19, 2021 3:41 pm 
Offline
User avatar

Joined: Thu Dec 11, 2008 1:28 pm
Posts: 10938
Location: England
(welcome, EriknocTDW!)


Top
 Profile  
Reply with quote  
PostPosted: Mon Jul 19, 2021 9:38 pm 
Offline

Joined: Sun Jul 11, 2021 7:53 am
Posts: 3
@gfoot
Ah yes. I've been pondering that over the weekend. For some reason, my brain kept focusing on the overflow flag, that somehow it played a roll. The overflow is affected by ADC and SBC, but not vice versa. So I'm not sure how I got my brain wires crossed, but I get it now and it's exactly like you said. Thank you.

@kernelthread
Thank you.

@BigEd
And thank you to you too. :-)


Top
 Profile  
Reply with quote  
PostPosted: Fri Aug 13, 2021 7:11 pm 
Offline

Joined: Sun Feb 22, 2004 9:01 pm
Posts: 90
8-bit rotate left:
CMP #&80 ; a abcdefgh
ROL A ; a bcdefgha

8-bit rotate left:
ASL A ; a abcdefg0
ADC #0 ; 0 bcdefgha

rotate right arithmetic (sign remains same):
CMP #&80 ; a abcdefgh
ROR A ; h aabcdefg

_________________
--
JGH - http://mdfs.net


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 14, 2021 5:24 am 
Offline
User avatar

Joined: Sat Dec 01, 2018 1:53 pm
Posts: 727
Location: Tokyo, Japan
gfoot wrote:
So subtracting $7F is the same as adding $80. This is why the carry flag behaviour feels backwards.

It might not feel so backwards if you remember that when you're doing a subtraction it does not turn into a borrow flag, as it does on the 6800, but remains a carry flag, which you can think of as the opposite of a borrow flag. (I think I have that right; I've not been programming in 6502 assembler for while.)

_________________
Curt J. Sampson - github.com/0cjs


Top
 Profile  
Reply with quote  
PostPosted: Sat Aug 14, 2021 6:58 pm 
Offline
User avatar

Joined: Sun Jun 30, 2013 10:26 pm
Posts: 1948
Location: Sacramento, CA, USA
Other than the potential for initial confusion, I think the /carry = borrow idea is brilliant in its utter simplicity.

_________________
Got a kilobyte lying fallow in your 65xx's memory map? Sprinkle some VTL02C on it and see how it grows on you!

Mike B. (about me) (learning how to github)


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 18, 2021 5:22 am 
Offline

Joined: Sun Jul 11, 2021 7:53 am
Posts: 3
@jgharston

Quote:
8-bit rotate left:
CMP #&80 ; a abcdefgh
ROL A ; a bcdefgha

I didn't think about CMP#$80/ROL. That's smart too, using 4 cycles.
Although the ASL/ADC#$80/ROL sequence effectively rotates 2 bits, using only 6 cycles.

Quote:
8-bit rotate left:
ASL A ; a abcdefg0
ADC #0 ; 0 bcdefgha

I believe you mean: ASL A ; a bcdefgh0
But, yeah. This one's also good for rotating 1 bit at a time using 4 cycles.

Quote:
rotate right arithmetic (sign remains same):
CMP #&80 ; a abcdefgh
ROR A ; h aabcdefg

What's a good use case for this one?


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 18, 2021 8:13 am 
Offline

Joined: Fri Jul 09, 2021 10:12 pm
Posts: 741
EriknocTDW wrote:
Quote:
rotate right arithmetic (sign remains same):
CMP #&80 ; a abcdefgh
ROR A ; h aabcdefg

What's a good use case for this one?

It's just a signed divide by two, so good if you need to shift something that might be negative.


Top
 Profile  
Reply with quote  
PostPosted: Wed Aug 18, 2021 8:41 am 
Offline

Joined: Thu Mar 03, 2011 5:56 pm
Posts: 284
gfoot wrote:
EriknocTDW wrote:
Quote:
rotate right arithmetic (sign remains same):
CMP #&80 ; a abcdefgh
ROR A ; h aabcdefg

What's a good use case for this one?

It's just a signed divide by two, so good if you need to shift something that might be negative.


In effect, it's ASR (Arithmetic Shift Right).


Top
 Profile  
Reply with quote  
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 11 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 44 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to: