!pr2
Turn an Index into a Mask..................Bob Sander-Cederlof

How do you write a program that will turn a number from 0 to 7 into a bit mask $01, $02, ...$40, $80?  I want an index of 0 to return $01, 1 to return $02, 2 to return $04, and so on up to 7 returning $80.

The simplest, shortest, and speediest is to use a direct table look-up.  Assuming the byte with the index value is in the A-register, the code would look like this:

       AND #7          isolate index bits
       TAX             index to X-register
       LDA TABLE,X     get mask from table

and the table would look like this:

       TABLE .HS 01020408
             .HS 10204080

This technique has the wonderful advantage that if you need a different translation, you can simply use a different table.  For example, if you want the reverse pattern, with 0 returning $80 and 7 returning $01, simply change the table to:

       TABLE .HS 80402010
             .HS 08040201

The table lookup method has the shortest code, but counting the table does take 14 bytes.  If you don't worry so much about speed and flexibility, you can write a little loop that will create the mask value like this:

       MAKE.MASK.2
               AND #7     isolate index bits
               TAX        index into X-register
               LDA #$01   initial mask value
       .1      ASL        shift loop to position
               DEX        to Xth bit
               BPL .1     shifts once to many
               ROR        restore after extra shift
               RTS

I put an RTS at the end because this piece of code makes a nice size subroutine.  Nevertheless, for comparison to the table lookup code above, let's count neither the JSR to call it nor the RTS at the end.  The shift-loop method takes only 10 bytes, four less than the table lookup.  But it is slower, taking 14 cycles if the index is 0, 21 if 1, up to 63 for an index of 7.  Sometimes saving four bytes is more important that speed, and sometimes speed is more important.

To generate the reverse sequence with the shift loop method, make three simple changes to MAKE.MASK.2:  the initial mask value from $01 to $80; the ASL to LSR; and the ROR to ROL.

Note that both techniques shown above use the X-register.  If the X-register is busy, you could use the Y-register instead.  Just for the challenge, I wanted to see if I could write a reasonably efficient index-to-mask routine that did not use the X- or Y- registers at all.

The first method that came to mind was fast enough, but took too much space and did not seem creative.  It involved a series of CMP and BEQ instructions to branch to 8 different LDA's:

       SILLY.WAY
               AND #7  isolate index
               BEQ .0  index=0
               CMP #1
               BEQ .1  index=1
               ...
               CMP #6
               BEQ .6    index=6
               LDA #$80  index=7
               RTS
       .0      LDA #$01
               RTS
       .1      LDA #$02
               RTS
               ...
       .6      LDA #$40  index=6
               RTS

If I had written every line above, you would see that it takes 52 bytes.

Next I though of a more efficient way to do the CMP's so that not so many were needed.

       NOT.SO.SILLY.WAY
               AND #7  isolate mask
               BEQ .0  index=0
               CMP #4
               BEQ .4  index=4
               BCS .60 index=5, 6, or 7
               CMP #2  index=1, 2, or 3
               BEQ .2  index=2
               BCS .3  index=3
               LDA #$02  index=1
               RTS
       .60     CMP #6  index=5, 6, 0r 7
               BEQ .6  index=6
               BCS .7  index=7
               LDA #$20  index=5
               RTS
       .0      LDA #$01  index=0
               RTS
       .2      LDA #$04  index=2
               RTS
           and so on.

This method takes a total of 46 bytes.
!np
Here is one which is even shorter, which uses "tricky" arithmetic.

       TRICKY.WAY
               AND #7
               CMP #2
               BCC .5  (0 or 1) plus 1
               BEQ .5  (2) plus CARRY plus 1 --> 4
               CMP #4
               BCC .4  (3) plus 4+1 --> 8
               BEQ .3  (4) plus 6+4+1+C --> $10
               CMP #6
               BCC .2  (5) plus $10+6+4+1
               BEQ .1  (6) plus $1E+$10+6+4+1+C
               ADC #$3F  (7) plus $3F+$1E+$10+6+4+1+C
       .1      ADC #$1E
       .2      ADC #$10
       .3      ADC #6
       .4      ADC #4
       .5      ADC #1
               RTS

Not counting the RTS, that is 31 bytes.  Cases 0 and 1 take only 9 cycles.  The longest one, when the index is 7, takes 32 cycles.

All of these longer methods can be made to generate the reverse sequence by simply inverting the index before beginning the tests.  Use "EOR #7" before the "AND #7".

I came up with an even trickier version, which shaved another byte or two off TRICKY.WAY.  Believe it or not, it really works:

       TRICKIER.WAY.REVERSE
               EOR #7
       TRICKIER.WAY
               AND #7     isolate index
               SEC        00-01-02-03-04-05-06-07
               ROL        01-03-05-07-09-0B-0D-0F
               CMP #3
               BCC .0     turn 0 into $01
               CMP #7
               BCC .12    03-->02, 05-->04
               ADC #6     ..-..-..-0E-10-12-14-16
               CMP #$12
               BCC .34    0E-->08, 10-->10
               ADC #$2B   ..-..-..-..-..-3E-40-42
               CMP #$42
               BCC .56    3E-->20, 40-->40
               ASL        42-->84-->80
       .56     AND #$E0
       .34     AND #$F8
       .12     AND #$FE
       .0      RTS

If the index is 0, this one takes 11 cycles.  Worst case is for index 7, at 34 cycles.

A source file on the quarterly disk will include all of the above examples, plus a driving program that runs through all 8 cases and displays the results for each and every method.

In real life, I would probably use the shift-loop or the table look up.  Most likely the table lookup, because it is the easiest to understand and modify, and by far the shortest in time.  Nevertheless, it is very useful to experiment with other techniques.  You learn a lot from the experience, and it is fun!
