On Wed, 17 Sep 1997 12:07:29 -0800 Andrew Warren writes: >Frank Dalton wrote: > >> Using a PIC, has anybody figured out a good way to take a number >> from zero to seven (0 to 7) and use it to generate the >> corresponding bit mask (i.e., 00000001, 00000010, 00000100, etc.)? [...] >Every time I'm faced with this problem, I spend a few minutes trying >to find a method that's better than a lookup table... So far, I >haven't found one. The project I'm working on now needs this function, so it's of a little more than academic interest to me. There are other ways (some of them detailed below), but I think I'll end up using a table. For reference, here's a table method. Simple implementations of the table method such as this one require the table to be entirely within the same block of 256 instruction words. On 12-bit parts this block must be the low 256-word block of a 512-word code page. On 14-bit parts the PCLATH register needs to be set to indicate the high bits of that block. This isn't shown here. This method uses 10 words of program space and takes 6 instruction cycles to look up the value. Input and output values are in W. ;Here with bit number (0 to 7) in W. ;;; andlw b'00000111' ;Optional - guarantee result in range. call gettbl ;Here with mask in W. gettbl addwf PCL,f ;On 12-bit, just 'PC' retlw b'00000001' retlw b'00000010' retlw b'00000100' retlw b'00001000' retlw b'00010000' retlw b'00100000' retlw b'01000000' retlw b'10000000' The first obvious "improved" method involves shifting a 1 to the left n times. Doing this via a loop can save code space but takes a long and variable time to execute. This takes 6 + 5*(bitno) cycles, or 41 cycles worst case. 6 instruction words, input in W output in RAM. This particular implementation won't work on a 12-bit PIC since it uses the addlw instruction. ;Here with bit number in W clrf result setc loop rlf result,f addlw 0 - 1 ;Decrement W (bit number) skpnc ;Skip if done (when W = 0) goto loop The PIC has a swap instruction, which can be thought of as a 4-bit at a time rotate (without C). The rotate instruction does 1 bit at a time, but it requires C to be zero to ensure a zero goes in. Adding a number to itself also shifts it left one bit without needing to set up the C bit. The middle bit of the 3 bit input calls for a shift of either 0 or 2 bits, which is best handled by loading different constants. Combining these techniques gets a routine like the one below. Input is in RAM, the high bits don't need to be zero. Output is to another RAM location. 8 instructions and 8 cycles. movlw b'00000001' ;Result for 000 btfsc bitno,1 ;Skip if X0X movlw b'00000100' ;Result for 010 movwf result btfsc bitno,0 ;Skip if XX0 addwf result,f ;Double result (shift it left one) ; Here result is 0001, 0010, 0100, or 1000 (high bits 0) depending on low ; 2 bits of bitno. btfsc bitno,2 ;Skip if 0XX swapf result,f ;If high bit is 1, go left 4 bits. A few weeks ago, Scott Dattalo I think was asking about a similar routine for a 2-bit input, and a trick routine similar to this one was eventually discovered. It takes input in RAM, output to W or RAM. 4 instructions and 4 cycles. Again, this routine is for 2 bits in to 4 bits out. ; 2-bit to 4-bit mask generator. Converts 0000 to 0001, 0001 to 0010, ; 0010 to 0100, and 0011 to 1000. incf result,w btfsc result,1 iorwf result,f incf result,x ;X either W or f depending on what next. This seems like it would be a good foundation for a 3 in, 8 out routine. But the high bit of bitno interferes with it, so it needs to be taken out, and remembered so the result can be swapped at the end if needed. The routine below is typical of this genre. Input in W, output to RAM, 9 instr. and 9 cycles. ; Here with bit number in W. addlw b'11111100' ;Bit 2 out to C bit andlw b'00000011' ;then clear bit 2. movwf result incf result,w btfsc result,1 iorwf result,f incf result,f skpnc swapf result,f Maybe something better could be done by splitting the routine in two parts, and using a bit test at the top to decide whether the 1 bit in the result is in the high or low half. I don't think this would be better than a table in either code space or execution time though. In the experimental category I've toyed with adding or subtracting numbers of the form b'11111101' to try and move out the high bit and accomplish the increment to find the mask in one operation. Nothing really hit me as successful though. I won't claim any of these are better than a table but they are more obscure. If someone tries to reverse-engineer your code it will confuse them. I'm sure we haven't heard the end of this yet. Compared to a barage of content-free posts like I've seen the last few days, rehashing an old topic over and over is a little better.