Best practices assembly code for bit operations
Can somebody point me to what would be considered best practices for the equivalent PIC assembly codes for setting a bit, clearing a bit, testing a bit in 32 bit registers? I currently initialize a register shifting the bit around for setting or clearing with or, and, andn, XOR depending.
Jerry
Jerry

Comments
AND = clear a bit with the zeros in the mask (source field)
But to make it easy use ANDN to clear a bit wth 1s in the mask ,as the N reverses the mask.
And then there is the MUXC/MUXZ,
set the bit to the values of C or Z (eg 0 or 1) and the mask (dest field) will tell what bits to affect
Next question, if I have a 32 bit register that I need to shift out a specific pin with clock on another pin, is there an easy way to nondestructively ( to the other bits in the outa register) shift it out? There must be serial clock code around I guess. But I was thinking the cleanest way would be to save out and dira, flip all dira to inputs except for the two (clock and data) pins, then load outa with data, or in the clock, etc. I would then reload outa and dira.
DAT org asm_entry mov dira,#7 'make pin 0, 1, 2 pin an output mov outa,#0 'set all pins to off mov asm_c,cnt 'prepare for WAITCNT loop add asm_c,asm_cycles :loop waitcnt asm_c,asm_cycles 'wait for next CNT value movd :loop2,serialpnt 'set spot in serial anim table add serialpnt,#1 'add one long for next time cmp serialpnt,#serialpnt wz 'did it reach the end? if_z mov serialpnt,#serial 'reset serial pointer mov asm_cnt,bit_test :loop2 test 0-0,asm_cnt wz '0-0 selfmod code, test serial muxnz outa,#SER 'answer in z, use it to set Serial pin or outa,#CLK 'turn pin on (clock) andn outa,#CLK 'turn pin off (clock) sar asm_cnt,#1 wz 'shift right if_nz jmp #:loop2 'if not zero, jmp or outa,#LAT 'turn pin on (latch) andn outa,#LAT 'turn pin off (latch) cmp asm_cycles,asm_cycles2 wz if_z jmp #:loop sub asm_cycles,asm_cycles3 jmp #:loop 'wait for next sample asm_cycles long $01FFFF 'delay timer asm_cycles2 long $004FFF 'minimum delay asm_cycles3 long $1000 'subtract to delay serial long H+J+L+%10 long A1+A2+D1+D2+J+M+%01 long B+C+D1+D2+E+F+%10<<8 long E+F+J+M+%01<<8 long B+C+E+F+K+M+%10<<16 long H+J+K+M+%01<<16 serialpnt long serial 'always have this line just after serial anim font long A1+A2+B+C+D1+D2+E+F+J+M '0 long B+C '1 long A1+A2+B+D1+D2+E+G1+G2 '2 ' and so on bit_test long %1<<24 '24 serial to parallel bits to do. asm_c res 1 'uninitialized variables follow emitted data asm_cnt res 1"bits" has be set equal to 8 previously. I think this would work with other sizes of data if the value of "bits" were adjusted.
I doubt any of this code is original to myself.
'------------------------------------------------------------------------------------------------------------------------------ '' value of oByte should be set before calling this subroutine '' returns value of iByte DAT shiftIO mov bitCountdown, bits ror oByte, bits :Bit shl oByte, #1 wc ' shift off the bit to send muxc outa, mosiMask or outa, clockMask test misoMask, ina wc rcl iByte, #1 andn outa, clockMask djnz bitCountdown, #:Bit shiftIO_ret retIt is destructive to oByte but not the other bits in the outa register.
Edit: I hadn't seen Tony's post.
I think your code could be improved (sped up) with some "djnz" statements.
You're "asm_cnt" could be loaded with "#8" and instead of shifting and then testing, a single "djnz" would take care of catching the end of your countdown.
When it has no value left, e.g shifted out it should no longer jump back to loop2.
Saves me a long (mov counter,#24) before the loop starts and speed wise it's negligible
sar asm_cnt,#1 wz 'shift right
if_nz jmp #:loop2
Thanks for explaining Tony. I just think the "djnz" statement is pretty cool that it can subtract and jump in four clock cycles (8 cycles when not jumping) so I try to use it when ever I can.
My not understanding your code made me think you could take advantage of it too. I'll need to study your code a bit more and see if I can better understand it.
Considering I am not using spi, i2c or any serial other than USB, are there any other alternatives for serial data and clock that would further simplify the code (unused prop pins?) ? The serial shift speed (clock low) on the downstream device clock is 7ns so the tuning word of 48 bits can go out at something greater than 1Mhz.
I think the following will work as a 32 bit shiftin/shiftout (MSb first) with a single register (called Shifter) preloaded with your value
The InMask bit must have an IN direction, and the OutMask bit must have an OUT direction.
Start mov LoopCtr,#32 'number of bits to shift Loop and InMask,ina wc,nr 'get the (single) input bit into the carry rcl Shifter,#1 wc 'pull the carry into bit0 and put bit31 into the carry muxc outa,OutMask 'output the carry to the output port or outa,ClockMask 'set the clock bit high andn outa,ClockMask 'set the clock bit low djnz LoopCtr,#Loop 'continue for 32 passes ClockMask long 1 'portbit for clock InMask long 2 'portbit for input OutMask long 4 'portbit for output LoopCtr long 0 'local count variable Shifter long 0 'shift registerIf the single instruction clock is too fast, then you can separate the clockset and clockclear instructions by locating them elsewhere inside the loop.
Cheers,
Peter (pjv)
That's some nice code.
FYI, the "test" statement is an "and" without writing so your:
could be written:
I mainly say this in case others here are confused about why one of us use test and one uses and (as I would have likely been a few days ago).
I think there may be some devices that can not be read from and written to during the same position of the clock pulse. I noticed, I set the output pin prior to the rising clock edge and read from the device after the clock goes high, but after checking the datasheet (for the Nordic nRF24L01+) I see the I could also read prior to the rising clock pulse.
I can't think of any SPI devices off-hand where your code couldn't be used, but I wouldn't be surprised if they exist.
I think you just made it possible to speed up several of my drivers, thank you.