Did you know that most AVR chips have a type of hardware exclusive OR (XOR) option when it comes to the logic levels of the output pins? If you look in the datasheet (the image above is a screenshot from an ATtiny13 datasheet) you’ll find a section on Toggling the Pin. It turns out that if you set a PORT as an output, writing logic one to the corresponding PIN register will toggle the logic levels of that out. This is really easy to overlook if you’re writing in C, but I’ve been working on learning a bit of assembler language and found this to be very useful. Keep reading after the break and I’ll tell you how I happened upon this info and what it’s good for.
So first off, let’s talk about why this doesn’t matter very much if you’re writing in C code. Normally if you want to toggle some output pins you’ll just write a one-liner that XOR’s with a bitmask:
PORTB ^= 0xFF;
This is a bit of C shorthand (learn more about that from my tutorial series) that performs the XOR on the current output levels and writes the result back to the port. But the same thing can be done in hardware by writing the bitmask to the PINB register:
PINB = 0xFF;
You don’t really care, because it’s just one line of code. In fact it’s probably easier to XOR the PORTB because it makes more sense conceptually. But the compiler might end up using more cycles than if you had written to the PIN register.
I happened across this feature because I was blinking some LEDs as a way to learn assembler. I had this jumble of code in an Interrupt Service Routine:
ldi myReg, 0xFF in intReg, PORTB eor intReg, myReg out PORTB, intReg
It loads a bitmask into one register, loads in the current logic from PORTB to another register, performs an XOR of the two, and writes the result back to PORTB. This takes four cycles and requires two registers. Toggling bits is such a rudimentary operation I was surprised there wasn’t a command to XOR bits directly so I started searching around. I came across this article over at AVR Freaks which clued me into the bit toggle feature. Now I was able to reduce my assembler code as follows:
ldi intReg2, 0xFF ;temporarity use intReg2 as a bit mask out PINB, intReg2 ;writing to PINB effectivley does an Exclusive OR on PORTB
This results in the same toggling effect, but takes just two cycles and requires the use of only one register.
What I found most interesting is that no matter how much I use AVR chips, there ‘s never a shortage of surprises waiting to be found in the datasheet.
Haha, I mentioned this trick on a blog post about IO instruction times that was featured by HAD last year.
http://hackaday.com/2010/08/19/todays-arduino-moment/
I didn’t deconstruct machine code but analysed the timing to conclude the PIN trick only took two cycles. See the article here:
http://www.billporter.info/ready-set-oscillate-the-fastest-way-to-change-arduino-pins/
On the same note: Did you know something like
PORTB|=(1<<LEDA)|(1<<LEDB);
generates more code than
PORTB|=(1<<LEDA);
PORTB|=(1<<LEDB);
?
The first translates to an IN instruction, an OR instruction and an OUT instruction, making for 3 instructions in total. The second one gets optimized to two SBI instructions and is one instruction shorter.
@Sprite_tm
It’s worth noting that the second method generates less code, but is slower.
The SBI instruction takes 2 cycles, while IN, OR and OUT take 1 cycle each.
I wish people would just read the datasheet more often.
@Alex Parting:
Datasheets are close to 500 pages for some chips. It’s really easy to overlook these things if you’re just skimming through, and reading the document cover-to-cover probably isn’t very productive at all.
if I remember correctly, this is done differently in tiny and mega avrs
A good compiler would do this for you (but only if it’s better as Emeryth said).
jc,
It appears to be the same on at least the mega328, mega644, and mega2560 and their respective families as well as the tiny4. Based on the datasheets I have in front of me.
Tricks like this are what separate people like me who think in C and expect the compiler to know what to do, and the folks who know their brand of chip inside-out.
And the real pros know which of those instructions have erratas for when certain values are undefined.
I just completed a blog entry on the same topic!
http://www.hertaville.com/?p=100
I guess it’s like “reading, writing, and flipping bits in C”?
http://www.swharden.com/blog/2009-06-24-reading-writing-and-flipping-bits-in-c/
… dang, Hussam, I like that blog entry!
That’s a cool trick, nice to know.
Note however that this does not work for every AVR. For example on ATMega16 the PIN* registers are read-only and the datasheet for not mention this behaviour. So keep this in mind if writing portable libraries.
typo, “for” instead of “does”
Seems to work for the newer AVRs, so it should be usable on most (if not all) tinys and all megas (xx0,xx4,xx8..), except old stuff like Mega8/16/32.
The language is called assembly. Only the program which translates it to machine code is called AN assembler. Unless you also want to tell people that you’re not learning C, you’re learning C compiler linker. /petpeeve
Otherwise nice article.
That could save some cli/sei instructions…
You often know the current state of an output already, so you could just toggle instead of setting/clearing a bit.
@Gordo
My pet peeve is people who have pet peeves that are WRONG… Do a book search on Amazon for “Assembler” and see for yourself.
There are two types of programmers: those who count cycles and write very obfuscated code; and then there are those that just write clearly documented code and don’t bother counting cycles.
I’m clearly in the latter category. There are *VERY* few instances where I would care about saving one or two cycles. The XOR hardware will never get used because it makes defining whether the pin is a 0 or 1 excessively vague. In my opinion, this is much more likely to result in a bug in the firmware than it is to be useful.
I’ve read several PIC mcu datasheets nearly cover to cover I ignore the dimension diagrams etc and most block diagrams.
They are long but they stand you in good stead for ever. In terms of becoming idiomatic, picking up tricks like this but just understanding the architecture.
@anti-fanboi:
So I searched for Assembler – 1209 results.
Then I searched for Assembly – 37982 results.
From the results, it seems “Assembler” is an outdated term.
Thanks for proving Gordos point. What is next, Wikipedia?
Outdated? perhaps. what’s next, are you going to make the point that “hackers” are those that cheat in online games? The point was that Assembler can refer to the language not the popularity of the term. The fact you found more than a few proves my point. The number of results as a method of “proof” is obviously/demonstrably infantile. Pft! kids! *roll eyes*
@anti-fanboi:
Gordo: “The language is called assembly”
anti-fanboi: “You are wrong, search Amazon for ‘assembler'”
Me: *points out ‘assembly’ is much more common than ‘assembler’ on Amazon*
anti-fanboi: “Using Amazon that way is infantile’
By the way, the language is called assembly. Not disputing that assembler has been used.
Using XOR to flip bits and to zero out registers was a commonly used idiom in IBM System 360 Assembly language and right through to System z. You usually learned this by reading others’ code.
As Gordo points out, an assembler is a program that translates assembly language into machine code. The term is still very much in use as defined. If you go looking for an assembler manual and think it will teach you assembly language, you will be very disappointed. An assembler manual is a reference on how to use the assembler.
I made that mistake when I first started learning 360 assembler.
One more use of XOR is to move data. Example: (A XOR B) XOR B is A. I remember doing this, but wanted a reference. So, here it is:
http://www.i-programmer.info/babbages-bag/498-the-magic-swap.html
@hpux735: The suggested optimization (replacing IN…OR…OUT… with SBI…SBI…) is not valid. A compiler can’t optimize in a way that sets a volatile register in an unexpected state.
Regarding XOR: Another common use of xor is to obtain zero (xor regn regn) because embedding constants in machine code is often more expensive. I don’t know if this applies to AVRs.
@Ken
It really depends on how the AVR is defined in the context of the C compiler. If the two operations are truly equivalent then the compiler can optimize.
As an aside, that is one thing that I don’t like about the AVR. I don’t want a pinx and portx register… Just give me a portx like the PIC and do what I want based upon whether I’m reading of writing to it.
hpux: Sorry, but the ability to trick your compiler into making unsafe IO optimizations is very different from saying that a good compiler should do this by default. Since the compiler can’t predict who will be watching the IO pins, it must not allow them to enter an inconsistent state, even for a cycle or two.
Ken (compiler writer)
I haven’t tried this but why can’t you just re-write the original value, rather than 0xFF, to XOR?
E.g.
out PORTB, intReg
…
out PORTB, intReg
rather than:
out PORTB, intReg
…
ldi intReg2, 0xFF
out PORTB, intReg2
?
M-Space – You can’t directly load an I/O register with data.