+
+#if F0_HACK==2
+/* to understand this implementation take a look at f0-opt-table.txt */
+static uint16_t hack_table[5] PROGMEM = { 0x0311, 0xDDB3, 0x2A79, 0x07AA, 0x51C2 };
+static uint8_t offset_table[5] PROGMEM = { 4+16, 6+16, 9+16, 12+16, 13+16 };
+
+static
+void bmw_small_f0(uint32_t* q, uint32_t* h, const void* m){
+ uint16_t hack_reg;
+ uint8_t c,i,j;
+ uint32_t(*s[])(uint32_t)={ bmw_small_s0, bmw_small_s1, bmw_small_s2,
+ bmw_small_s3, bmw_small_s4 };
+ for(i=0; i<16; ++i){
+ ((uint32_t*)h)[i] ^= ((uint32_t*)m)[i];
+ }
+ dump_x(h, 16, 'T');
+ memset(q, 0, 4*16);
+ c=4;
+ do{
+ i=15;
+ j=pgm_read_byte(offset_table+c);
+ hack_reg=pgm_read_word(&(hack_table[c]));
+ do{
+ if(hack_reg&1){
+ q[i]-= h[j&15];
+ }else{
+ q[i]+= h[j&15];
+ }
+ --j;
+ hack_reg>>= 1;
+ }while(i--!=0);
+ }while(c--!=0);
+ dump_x(q, 16, 'W');
+ for(i=0; i<16; ++i){
+ q[i] = s[i%5](q[i]);
+ }
+#if TWEAK
+ for(i=0; i<16; ++i){
+ ((uint32_t*)h)[i] ^= ((uint32_t*)m)[i];
+ }
+ for(i=0; i<16; ++i){
+ q[i] += h[(i+1)&0xf];
+ }
+#endif
+}
+#endif /* F0_HACK==2*/
+
+#if F0_HACK==1