Fast encoder read - don't understand asm produced

I’m trying to code a very fast quadrature encoder read, that divides the input by (luckily) a multiple of 2 and preserves the quadrature timing.

/Rotary encoder/

/*Fast read of quadrature encoder no interrupts
Thanks to Oleg at Circuits@Home for original code */

#define ENC_A 14 //encoder A-pulse
#define ENC_B 15 //encoder B-pulse
#define TIED_TO_GROUND1 16 //these pins have to be low all the time. Set them as outputs
#define TIED_TO_GROUND2 17 //and send low. Maybe also tie to ground via 1k resistor.
#define ENC_PORT PINC //use PORTC

void setup()
noInterrupts(); //interupts off
pinMode(ENC_A, INPUT); //set A-input pin
digitalWrite(ENC_A, HIGH); //enable pull-up
pinMode(ENC_B, INPUT); //set B-input pin
digitalWrite(ENC_B, HIGH); //enable pull-up
pinMode(TIED_TO_GROUND1, OUTPUT); //set unused pin to output
digitalWrite(TIED_TO_GROUND1, LOW); //send it low
pinMode(TIED_TO_GROUND2, OUTPUT); //set unused pin to output
digitalWrite(TIED_TO_GROUND2, LOW); //send it low

void loop()
const static int8_t enc_states = {
0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0 }; //const static so initialised once only
static int8_t counter = 0; //static so initialised once only
static uint8_t old_AB = 0; //static so initialised once only
old_AB <<= 2; //store previous state of encoder channels in bits 2 and 3
old_AB += ENC_PORT; //store current state.
counter+=enc_states[(old_AB & 0x0f)]; //increment counter by value returned from encoder read

The encoder I have is 1024 ppr, running at 3200rpm max. This means I have about 75 instructions at 16MHz in which to do my work.

Disassembling this code gives the following:-

; Using 8 bit counter

  const static int8_t enc_states[] = {0,-1,1,0,1,0,0,-1,-1,0,0,1,0,1,-1,0};
  static int8_t counter = 0;
  static uint8_t old_AB = 0;
  old_AB <<= 2;
  old_AB += ENC_PORT;

 100:      86 b1             in      r24, 0x06      ; 6  ;[1] Load r24 with PINC (at memory 0x06)
 102:      e0 91 10 01       lds      r30, 0x0110          ;[2] Load r30 with old_AB (at memory 0x0110) 
 106:      ee 0f             add      r30, r30             ;[1] Left shift
 108:      ee 0f             add      r30, r30             ;[1] Left shift again
 10a:      e8 0f             add      r30, r24             ;[1] Add PINC to old_AB
 10c:      e0 93 10 01       sts      0x0110, r30          ;[2] Store back in old_AB

  counter+=enc_states[(old_AB & 0x0f)];

 110:      f0 e0             ldi      r31, 0x00      ; 0  ;[1] Load r31 with 0x00
 112:      ef 70             andi      r30, 0x0F      ; 15 ;[1] AND r30 (old_AB) with 0xFF

 114:      f0 70             andi      r31, 0x00      ; 0  ;[1]
 116:      e0 50             subi      r30, 0x00      ; 0  ;[1]
 118:      ff 4f             sbci      r31, 0xFF      ; 255;[1]  

 11a:      80 91 11 01       lds      r24, 0x0111          ;[2] Load r24 with counter (at memory 0x0111)
 11e:      90 81             ld      r25, Z               ;[1] Load r18 with contents of data address pointed to by Z
 120:      89 0f             add      r24, r25             ;[1] Add r25 to r24 store in r24.
 122:      80 93 11 01       sts      0x0111, r24          ;[2] store r24 in 0x0111
 126:      08 95             ret                          ;[4]

; 23 instructions

which at 23 instructions is quite good, but I wonder if it’s possible to do better.

I do not understand what 114, 116 & 118 are doing…


Looks to me like the code is a "sign extension". I suspect the compiler is promoting an eight bit value ("old_AB & 0x0f" perhaps) to a 16 bit value (probably an "int").

I suggest that an intermediate expression may force the compiler's hand...

  old_AB += ENC_PORT; //store current state. 
  uint8_t index = (old_AB & 0x0f);
  counter+=enc_states[index]; //increment counter by value returned from encoder read

Thanks for the reply. I tried that and it's two instructions longer (because it does an sts with the intermediate variable).

Anyway, thanks again.