/*{{{}}}*/
/*{{{  #includes*/
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>

#define OPTMAC_C

#include "keybind.h"
/*}}}  */

#ifndef NO_OPTI
   /*{{{  next_command*/
#   include <h/codelg.h>

   private TOKEN *next_command(TOKEN *x)
   { if (*x>O_EXE_MACRO || *x<O_NOP) return(x+1);
     switch (cmd_type[*x-O_NOP])
      { case COM_II:
           x++;
        case COM_I:
        case COM_C:
        case COM_A:
           return(x+2);
        case COM_IIP:
           x++;
        case COM_IP:
           x++;
        case COM_P:
           x++;
           while (*x!=M_END_MACRO)
              if (*x==M_INT_STRING) x+=2; else x++;
        case COM:
           return(x+1);
        default:
           fprintf(stderr,"CRASH\n");
           kbd_exit(1);
      }

     return(0);
   }
   /*}}}  */
   /*{{{  jmp_dest*/
   private TOKEN *jmp_dest(TOKEN *x)
   {
     switch(*x)
      { CASES_M_CALL
           return(x+1+*x-M_CALL_0);
        CASES_M_JMP
           return(x+1+*x-M_JMP_0);
        CASES_M_JMP_FALSE
           return(x+1+*x-M_JMP_FALSE_0);
        CASES_M_JMP_TRUE
           return(x+1+*x-M_JMP_TRUE_0);
        case M_CALL:
        case M_JMP:
        case M_JMP_FALSE:
        case M_JMP_TRUE:
           return(x+2+*(x+1));
        default:
           break;
      }
     kbd_exit(1);
   }
   /*}}}  */
   /*{{{  optmac*/
   /*{{{  jumped_on, uses jmp-information, stored in buff*/
   /*{{{  jumped_on_loop*/
   private boolean jumped_on_loop(TOKEN *from,TOKEN *to,TOKEN *start,TOKEN *buff)
   {
     to=buff+(to-start);
     from=buff+(from-start);
     while (from<to)
        if (*from++)
           return(True);
     return(False);
   }
   /*}}}  */
#   define jumped_on(from,to) jumped_on_loop(from,to,start,buff)
   /*}}}  */

#   define STR_CUT_LG 2
   public TOKEN *opt_mac
                  ( TOKEN *start,
                    TOKEN *end,
                    boolean no_not_opt,
                    boolean rc_string_gen,
                    boolean fix_args
                  )
   {
     /*{{{  variables*/
     boolean changed;
     int loop,loop_max,s_jmp_cut,s_modified,s_shrink;
     TOKEN buff[OCL_CODE_LEN];
     enum
      { pre_cut,
        cut_strings,
        set_no_test,
        set_fixed_cmd,
        opt_ready
      } opt_type;
     /*}}}  */

     loop=0;
     loop_max=4*(end-start);
     s_jmp_cut=s_shrink=s_modified=0;
     opt_type=fix_args?set_fixed_cmd:pre_cut;
     do
      /*{{{  one optimisation step*/
      { int jmp_cut,modified,shrink;

        changed=False;
        jmp_cut=shrink=modified=0;
        /*{{{  start->buff, jmp opt*/
        { TOKEN *current,*dest,*next,*jmp;

          for (current=start,dest=buff;current!=end;)
           { boolean inverse=False;

             next=next_command(current);
             switch (*current)
              { case M_CALL:
                case M_JMP:
                 /*{{{  cut simple jmp-sequences*/
                 { TOKEN typ= *current;
                   int loop_max;

                   for
                    ( loop_max=end-start,jmp=jmp_dest(current) ; jmp!=end ; )
                    { switch (*jmp)
                       { case O_NOP:
                            jmp++;changed=True;jmp_cut++;continue;
                         case M_JMP:
                            if (loop_max--<0) break;
                            jmp=jmp_dest(jmp);
                            changed=True;
                            jmp_cut++;
                            continue;
                         case M_NOT:
                            if (no_not_opt) break;
                            fprintf(stderr,"JMP-NOT optimizer crash\n");
                            no_not_opt=True;
                            next=start;
                            dest=buff;
                            continue;
                         default:
                            break;
                       }
                      break;
                    }
                   dest=generate_jmp(typ,dest,buff+(jmp-start));
                   current=next;
                   continue;
                 }
                 /*}}}  */
                case M_NOT:
                 /*{{{  break, or prefix for jmp-condition*/
                   if (no_not_opt)
                    { *dest++ = *current++;continue; }
                   else
                    { do
                       /*{{{  skip one NOT*/
                       { inverse= !inverse;
                         changed=True;
                         jmp_cut++;
                         *dest++ = O_NOP;
                         current=next;
                         next=next_command(current);
                       }
                       /*}}}  */
                      while (*current==M_NOT && next!=end);
                      if (current!=end && *current!=M_JMP_FALSE && *current!=M_JMP_TRUE)
                       /*{{{  cannot optimize NOT's, this must be a tricky code!*/
                       { fprintf(stderr,"JMP-NOT optimizer crash\n");
                         no_not_opt=True;
                         current=start;
                         dest=buff;
                         continue;
                       }
                       /*}}}  */
                    }
                 /*}}}  */
                case M_JMP_TRUE:
                case M_JMP_FALSE:
                 /*{{{  handle jmp-sequences, value of tag is known*/
                 { TOKEN typ;
                   int loop_max;
                   boolean tag_value;

                   tag_value= *current==M_JMP_TRUE;
                   typ=tag_value?(inverse?M_JMP_FALSE:M_JMP_TRUE)
                                :(inverse?M_JMP_TRUE:M_JMP_FALSE);
                   for
                    ( loop_max=end-start,jmp=jmp_dest(current) ; jmp!=end ; )
                    { switch (*jmp)
                       { case O_NOP:
                            jmp++;changed=True;jmp_cut++;continue;
                         case M_JMP:
                            if (loop_max--<0) break;
                            jmp=jmp_dest(jmp);
                            changed=True;
                            jmp_cut++;
                            loop_max--;
                            continue;
                         case M_NOT:
                            if (no_not_opt) break;
                            tag_value = !tag_value;
                            jmp+=1;
                            jmp_cut++;
                            changed=True;
                            continue;
                         case M_JMP_FALSE:
                            if (loop_max--<0) break;
                            if (tag_value) jmp+=2; else jmp=jmp_dest(jmp);
                            changed=True;
                            jmp_cut++;
                            continue;
                         case M_JMP_TRUE:
                            if (loop_max--<0) break;
                            if (tag_value) jmp=jmp_dest(jmp); else jmp+=2;
                            changed=True;
                            jmp_cut++;
                            continue;
                         default:
                            break;
                       }
                      break;
                    }
                   dest=generate_jmp(typ,dest,buff+(jmp-start));
                   current=next;
                   continue;
                 }
                 /*}}}  */
                default:
                 /*{{{  copy the code*/
                   for (;current!=next;*dest++ = *current++);
                   continue;
                 /*}}}  */
              }
           }
        }
        /*}}}  */
        /*{{{  buff->start, shrink and modify to better code*/
        { TOKEN *current,*next,*dest;

          /*{{{  mark reachable commands*/
          for (current=start;current!=end;*current++ = O_NOP);
          for (dest=end,start[0]=0;;)
           {
             if (dest==end)
              /*{{{  look for next reached, unhandled command*/
              {
                for (dest=start;dest!=end;) if (*dest==0) break; else dest++;
                if (dest==end) break;
                current=buff+(dest-start);
                continue;
              }
              /*}}}  */
             else if (*dest!=M_END_MACRO)
              /*{{{  handle a reachable, unhandled command*/
              { *dest=M_END_MACRO;
                switch (*current)
                 { case O_NOP:
                      *dest=O_NOP;
                      current++;
                      dest++;
                      continue;
                   CASES_M_CALL      case M_CALL:
                   CASES_M_JMP       case M_JMP:
                   CASES_M_JMP_FALSE case M_JMP_FALSE:
                   CASES_M_JMP_TRUE  case M_JMP_TRUE:
                    /*{{{  mark jmp-dest*/
                    { TOKEN *jmp;

                      jmp=start+(jmp_dest(current)-buff);
                      if (jmp!=end && *jmp==O_NOP && jmp!=dest)
                         *jmp=0;
                    }
                    /*}}}  */
                   default:
                    { boolean no_cont;

                      /*{{{  set no_cont to correct value, maybe set opt_end!*/
                      switch (*current)
                       { case M_EXIT:
                         case M_END_MACRO:
                         CASES_M_JMP
                         case M_JMP:
                            no_cont=True;
                            break;
                         default:
                            no_cont=False;
                       }
                      /*}}}  */
                      next=next_command(current);
                      for (;current!=next;current++,*dest++=M_END_MACRO);
                      if (no_cont)
                         dest=end;
                      continue;
                    }
                 }
              }
              /*}}}  */
             else
              {
                dest=end;
              }
            }
          /*}}}  */
          /*{{{  shrink code*/
          { boolean shrinking;

            shrinking=False;
            /*{{{  get new adresses*/
            { int i;

              for (current=start,i=0;current!=end;current++)
                 *current=((*current==M_END_MACRO)?i++:(shrinking=True,-1));
              *end=i;
            }
            /*}}}  */
            if (shrinking)
             /*{{{  change jmps*/
               for
                ( current=start
                ; current!=end
                ; current=start+(next_command(buff+(current-start))-buff)
                )
                { TOKEN *x;

                  if (*current!=-1)
                   { switch(*(x=buff+(current-start)))
                      { case M_CALL:
                        case M_JMP:
                        case M_JMP_FALSE:
                        case M_JMP_TRUE:
                           *(x+1)= start[jmp_dest(x)-buff]-(*current+2);
                           break;
                        CASES_M_CALL
                           *x=M_CALL_0+start[jmp_dest(x)-buff]-(*current+1);
                           break;
                        CASES_M_JMP
                           *x=M_JMP_0+start[jmp_dest(x)-buff]-(*current+1);
                           break;
                        CASES_M_JMP_FALSE
                           *x=M_JMP_FALSE_0+start[jmp_dest(x)-buff]-(*current+1);
                           break;
                        CASES_M_JMP_TRUE
                           *x=M_JMP_TRUE_0+start[jmp_dest(x)-buff]-(*current+1);
                           break;
                        default:
                           break;
                      }
                   }
                }
             /*}}}  */
          }
          /*{{{  copy and shrink, clear buff*/
          for (current=start,dest=buff;current+shrink!=end;*dest++=0)
             if (current[shrink]!=-1)
                *current++= *dest;
             else
                shrink++;
          end-=shrink;
          /*}}}  */
          /*}}}  */
          /*{{{  all jmp's to M_END_MACRO to opt_end!, get jmp-on information*/
          for (current=start;current!=end;)
             switch (*current)
              { CASES_M_CALL
                CASES_M_JMP
                CASES_M_JMP_FALSE
                CASES_M_JMP_TRUE
                   next=jmp_dest(current);
                   buff[next-start]=1;
                   current++;
                   continue;
                case M_CALL:
                case M_JMP:
                case M_JMP_FALSE:
                case M_JMP_TRUE:
                   next=jmp_dest(current);
                   buff[next-start]=1;
                   current+=2;
                   continue;
                default:
                   current=next_command(current);
                   continue;
              }
          /*}}}  */
          /*{{{  modify some OCL-assembler patterns*/
          for (current=start;current!=end;)
           { TOKEN cur_token;

             cur_token = *current;
             next=next_command(current);
             switch (opt_type)
              {
                case set_no_test:
                 /*{{{  no explicit test after some commands*/
                   switch (cur_token)
                    { default:
                         goto skip_code;
                      case M_ADD_COUNTER:
                      case M_SUM_COUNTER:
                      case M_INV_COUNTER:
                         if
                          (    next!=end
                            && next[0]==M_NULL_COUNTER
                            && current[1]==next[1]
                            && !jumped_on(next,next+2)
                          )
                          { current=next;
                            *current++=O_NOP;
                            *current++=O_NOP;
                            goto modif_code;
                          }
                         goto skip_code;
                    }
                 /*}}}  */
                case set_fixed_cmd:
                 /*{{{  gen fixed argument commands*/
                   switch(cur_token)
                    { case M_ADD_COUNTER:
                      case M_SET_COUNTER:
                         if
                          /*{{{  adding argument in fixed range*/
                          (    current[2]<=FIXED_COMMAND_RANGE
                            && current[2]>=-FIXED_COMMAND_RANGE
                          )
                          /*}}}  */
                          /*{{{  fixed argument version*/
                          { current[0]+=RANGE_ADD+current[2];
                            current[2]=O_NOP;
                            current+=3;
                            goto modif_code;
                          }
                          /*}}}  */
                         break;
                      case M_CALL:
                      case M_JMP:
                      case M_JMP_TRUE:
                      case M_JMP_FALSE:
                         if
                          /*{{{  jmp arg in fixed range*/
                          (    current[1]<FIXED_COMMAND_RANGE
                            && current[1]>=-FIXED_COMMAND_RANGE-1
                          )
                          /*}}}  */
                          /*{{{  fixed argument version*/
                          { current[0] = cur_token+RANGE_ADD+current[1]+1;
                            current[1] = O_NOP;
                            current+=2;
                            goto modif_code;
                          }
                          /*}}}  */
                         break;
                      default:
                         break;
                    }
                   goto skip_code;
                 /*}}}  */
                case cut_strings:
                 /*{{{  maybe shrink string*/
                 { if (cur_token>O_NOP && cur_token<O_EXE_MACRO)
                    /*{{{  handle prompt-strings*/
                    { TOKEN *p_start;

                      p_start=current+1;
                      switch(cmd_type[*current-O_NOP])
                       { case COM_IIP:
                          /*{{{  skip arg*/
                            p_start++;
                          /*}}}  */
                         case COM_IP:
                          /*{{{  skip arg*/
                            p_start++;
                          /*}}}  */
                         case COM_P:
                          /*{{{  maybe define message*/
                          { int lg;
                            int x;

                            /*{{{  get length of string*/
                            for (lg=0;;p_start++)
                             { if (*p_start<=0 || *p_start>=O_NOP)
                                { if (lg>=STR_CUT_LG)
                                     break;
                                  lg=0;
                                  if
                                   (    *p_start==M_INT_STRING
                                     || *p_start==M_GET_HISTORY
                                   )
                                     p_start++;
                                  else if (*p_start==M_END_MACRO)
                                     break;
                                }
                               else
                                  lg++;
                             }
                            /*}}}  */
                            for (x=0;lg-x>=STR_CUT_LG;x++)
                             { TOKEN new;

                               if ((new=write_string(p_start-lg,lg-x,rc_string_gen)))
                                { TOKEN *source;
                                  TOKEN *dest;

                                  *(p_start-1-x)= -new;
                                  /*{{{  shift*/
                                  for
                                   ( source=p_start-1-lg,dest=p_start-2-x
                                   ; source>=current
                                   ; source-=1,dest-=1
                                   )
                                     *dest= *source;
                                  /*}}}  */
                                  /*{{{  fill with O_NOP*/
                                  for (lg-=x;--lg;)
                                     *current++=O_NOP;
                                  /*}}}  */
                                  goto modif_code;
                                }
                             }
                          }
                          /*}}}  */
                         default:
                            break;
                       }
                    }
                    /*}}}  */
                   else if (cur_token<O_NOP && cur_token>0)
                    /*{{{  handle text_string*/
                    { TOKEN *str;
                      int lg;

                      for
                       ( str=current+1,lg=1
                       ;    str!=end
                         && *str>0
                         && *str<O_NOP
                         && !jumped_on(str,str+1)
                         && lg<128
                       ; str++,lg++
                       );
                      if (lg>=STR_CUT_LG+1)
                       { TOKEN new;

                         if ((new=write_string(current,lg,rc_string_gen)))
                          { *current++=M_ASCII;
                            *current++= -new;
                            while (current!=str)
                               *current++=O_NOP;
                            goto modif_code;
                          }
                       }
                    }
                    /*}}}  */
                   goto default_opti;
                 }
                 /*}}}  */
                default:
                default_opti:
                 /*{{{  normal command optimization*/
                   switch (cur_token)
                    {
                      /*{{{  M_CALL        ...*/
                      case M_CALL:
                         dest=jmp_dest(current);
                         if
                          /*{{{  M_CALL x;M_END_MACRO -> M_JMP x; M_END_MACRO*/
                          (next!=end && *next==M_END_MACRO)
                          { *current=M_JMP;
                            current=next;
                            goto modif_code;
                          }
                          /*}}}  */
                         else if
                          /*{{{  M_CALL x; .. x:M_END_MACRO -> O_NOP; O_NOP; ..*/
                          ( dest!=end && *dest==M_END_MACRO )
                          { *current++=O_NOP;
                            *current++=O_NOP;
                            goto modif_code;
                          }
                          /*}}}  */
                         goto skip_code;
                      /*}}}  */
                      /*{{{  M_JMP         ...*/
                      case M_JMP:
                         dest=jmp_dest(current);
                         if
                          /*{{{  M_JMP x; .. x:M_END_MACRO -> M_END_MACRO; O_NOP; ..*/
                          ( dest!=end && *dest==M_END_MACRO)
                          { *current++=M_END_MACRO;
                            *current++=O_NOP;
                            goto modif_code;
                          }
                          /*}}}  */
                         else if
                          /*{{{  M_JMP x;x: -> O_NOP; O_NOP;*/
                          (dest==next)
                          { *current++=O_NOP;
                            *current++=O_NOP;
                            goto modif_code;
                          }
                          /*}}}  */
                         goto skip_code;
                      /*}}}  */
                      /*{{{  M_JMP_?       ...*/
                      case M_JMP_FALSE:
                      case M_JMP_TRUE:
                         dest=jmp_dest(current);
                         if
                          /*{{{  M_JMP_COND x;x: -> O_NOP;O_NOP;*/
                          (dest==next)
                          { *current++=O_NOP;
                            *current++=O_NOP;
                            goto modif_code;
                          }
                          /*}}}  */
                         else if
                          /*{{{  M_JMP_COND x;M_JMP_COND y; -> M_JMP_COND x;[O_NOP;O_NOP]|[M_JMP y]*/
                          (    next!=end
                            && (*next==M_JMP_FALSE || *next==M_JMP_TRUE)
                            && !jumped_on(next,next+2)
                          )
                          { if (*next==cur_token)
                             { current+=2;
                               *current++=O_NOP;
                               *current++=O_NOP;
                             }
                            else
                             { current+=2;
                               *current++=M_JMP;
                               current++;
                             }
                            goto modif_code;
                          }
                          /*}}}  */
                         goto skip_code;
                      /*}}}  */
                      /*{{{  M_END_MACRO   ...*/
                      case M_END_MACRO:
                         if
                          /*{{{  M_END M_END -> O_NOP M_END*/
                          (next!=end && *next==M_END_MACRO)
                          { *current++=O_NOP;
                            goto modif_code;
                          }
                          /*}}}  */
                         goto skip_code;
                      /*}}}  */
                      /*{{{  M_ADD_COUNTER ...*/
                      case M_ADD_COUNTER:
                         if
                          /*{{{  ADD x 0 -> NOP NOP NOP*/
                          ( *(current+2)==0 )
                          { *current++=O_NOP;
                            *current++=O_NOP;
                            *current++=O_NOP;
                            goto modif_code;
                          }
                          /*}}}  */
                         else if (next!=end && !jumped_on(next,next_command(next)))
                          { if
                             /*{{{  ADD x n ADD x m -> NOP NOP NOP ADD X n+m*/
                             (*next==M_ADD_COUNTER && *(next+1)==*(current+1))
                             { *(next+2)+=*(current+2);
                               *current++=O_NOP;
                               *current++=O_NOP;
                               *current++=O_NOP;
                               goto modif_code;
                             }
                             /*}}}  */
                          }
                         goto skip_code;
                      /*}}}  */
                      /*{{{  some complex statements sequences*/
                      default:
                         if (next!=end)
                          { TOKEN *n_next;

                            n_next=next_command(next);
                            if (!jumped_on(next,n_next))
                               switch (cur_token)
                                {
                                  /*{{{  M_PUSH_INT    ...*/
                                  case M_PUSH_INT:
                                     if (*next==M_POP_INT)
                                        if
                                         /*{{{  PUSH x POP x -> NOP*/
                                         (*(next+1)==*(current+1))
                                         { *current++=O_NOP;
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           goto modif_code;
                                         }
                                         /*}}}  */
                                        else if
                                         /*{{{  PUSH y POP x ADD x n -> SET x n SUM x y*/
                                         (   next+2!=end
                                          && *(next+2)==M_ADD_COUNTER
                                          && *(next+3)==*(next+1)
                                          && !(jumped_on(next+2,next+4))
                                         )
                                         { int n= *(next+4);
                                           int x= *(next+1);
                                           int y= *(current+1);

                                           *current++=O_NOP;
                                           *current++=M_SET_COUNTER;
                                           *current++=(TOKEN)x;
                                           *current++=(TOKEN)n;
                                           *current++=M_SUM_COUNTER;
                                           *current++=(TOKEN)x;
                                           *current++=(TOKEN)y;
                                           goto modif_code;
                                         }
                                         /*}}}  */
                                        else if
                                         /*{{{  PUSH x POP y INV y SUM y z INV y -> PUSH z POP y INV y SUM y x NOP NOP*/
                                         (   (current+11)<end
                                          && !jumped_on(current,current+11)
                                          && *(next+2)==M_INV_COUNTER
                                          && *(next+4)==M_SUM_COUNTER
                                          && *(next+7)==M_INV_COUNTER
                                          && *(next+1)==*(next+3)
                                          && *(next+1)==*(next+5)
                                          && *(next+1)==*(next+8)
                                         )
                                         { int x=*(current+1);
                                           int y=*(next+1);
                                           int z=*(next+6);

                                           *current++=M_PUSH_INT;
                                           *current++=(TOKEN)z;
                                           *current++=M_POP_INT;
                                           *current++=(TOKEN)y;
                                           *current++=M_INV_COUNTER;
                                           *current++=(TOKEN)y;
                                           *current++=M_SUM_COUNTER;
                                           *current++=(TOKEN)y;
                                           *current++=(TOKEN)x;
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           goto modif_code;
                                         }
                                         /*}}}  */
                                        else if
                                         /*{{{  PUSH x POP dummy TEST dummy -> NOP NOP NOP NOP TEST x*/
                                         (    isdummy(*(next+1))
                                           && next+2!=end
                                           && (*(next+2)==M_NULL_COUNTER || *(next+2)==M_POSITIV_COUNTER)
                                           && !jumped_on(next+2,next+4)
                                           && *(next+3)==*(next+1)
                                         )
                                         { *(next+3)=*(current+1);
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           goto modif_code;
                                         }
                                         /*}}}  */
                                        else if
                                         /*{{{  PUSH x POP dummy SUM y dummy -> NOP NOP NOP NOP SUM y x*/
                                         (    isdummy(*(next+1))
                                           && next+2!=end
                                           && *(next+2)==M_SUM_COUNTER
                                           && !jumped_on(next+2,next+5)
                                           && *(next+4)==*(next+1)
                                         )
                                         { *(next+4)=*(current+1);
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           *current++=O_NOP;
                                           goto modif_code;
                                         }
                                         /*}}}  */
                                     goto skip_code;
                                  /*}}}  */
                                  /*{{{  M_INV         ...*/
                                  case M_INV_COUNTER:
                                     if
                                      /*{{{  INV x INV x -> NOP NOP NOP NOP*/
                                      (*next==M_INV_COUNTER && *(current+1)==*(next+1))
                                      { *current++=O_NOP;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  INV x ADD x n -> ADD x -n INV x*/
                                      ( *next==M_ADD_COUNTER && *(current+1)==*(next+1))
                                      { int x= *(current+1);
                                        int n= -*(next+2);

                                        *current++=M_ADD_COUNTER;
                                        *current++=x;
                                        *current++=n;
                                        *current=M_INV_COUNTER;
                                        *(current+1)=x;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  INV dummy NULL_COUNTER dummy -> NOP NOP NULL_COUNTER dummy*/
                                      (    next[0]==M_NULL_COUNTER
                                        && next[1]==current[1]
                                        && isdummy(current[1])
                                      )
                                      { *current++=O_NOP;
                                        *current=O_NOP;
                                        current=n_next;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     goto skip_code;
                                  /*}}}  */
                                  /*{{{  M_SUM_COUNTER ...*/
                                  case M_SUM_COUNTER:
                                     if
                                      /*{{{  SUM x y ADD x n -> ADD x n SUM x y*/
                                      (   *next==M_ADD_COUNTER
                                       && *(current+1)==*(next+1)
                                       && *(current+1)!=*(current+2)
                                      )
                                      { int n= *(next+2);
                                        int y= *(current+2);

                                        *current++=M_ADD_COUNTER;
                                        current++;
                                        *current++=n;
                                        *current++=M_SUM_COUNTER;
                                        current++;
                                        *current++=y;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     goto skip_code;
                                  /*}}}  */
                                  /*{{{  M_ECHO_P      ...*/
                                  case M_ECHO_P:
                                     if (*next==M_ECHO_P && (n_next-current)<32)
                                      { TOKEN *s,*d;

                                        for (s=next-2,d=next;s>=current;*d--= *s--);
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        goto modif_code;
                                      }
                                     goto skip_code;
                                  /*}}}  */
                                  /*{{{  M_SET_COUNTER ...*/
                                  case M_SET_COUNTER:
                                   { TOKEN *dest1=current+1;
                                     TOKEN *dest2=next+1;
                                     TOKEN *arg1=current+2;
                                     TOKEN *arg2=next+2;

                                     if
                                      /*{{{  SET x n;INV x -> SET x -n; NOP NOP*/
                                      (*next==M_INV_COUNTER && *dest2==*dest1)
                                      { *arg1= -*arg1;
                                        *next++=O_NOP;
                                        *next++=O_NOP;
                                        current=next;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET x n; SET x m; -> NOP NOP NOP SET x m*/
                                      (*next==M_SET_COUNTER && *dest2==*dest1)
                                      { *current++=O_NOP;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET x n; SUM y x; -> SET x n; ADD y n | NOP NOP NOP ADD y n*/
                                      (*next==M_SUM_COUNTER && *arg2==*dest1)
                                      {  *next=M_ADD_COUNTER;
                                         *arg2= *arg1;
                                         if (isdummy(*dest1))
                                          { *current++=O_NOP;
                                            *current++=O_NOP;
                                            *current++=O_NOP;
                                          }
                                         else
                                          { current+=3;
                                          }
                                         goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET x 0; SUM x y; -> PUSH y POP x NOP NOP*/
                                      ( *next==M_SUM_COUNTER && *dest2==*dest1 && *arg1==0)
                                      { int s=*arg2;
                                        int d=*dest1;

                                        *current++=M_PUSH_INT;
                                        *current++=(TOKEN)s;
                                        *current++=M_POP_INT;
                                        *current++=(TOKEN)d;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET x n; ADD x m; -> NOP NOP NOP SET x n+m*/
                                      (*next==M_ADD_COUNTER && *dest2==*dest1)
                                      { *arg2+=*arg1;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        *current=M_SET_COUNTER;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET x n ASCII x -> SET X n ; n; | NOP NOP NOP n*/
                                      (*next==M_ASCII && *dest2==*dest1 && next_command(dest1)==next)
                                      { *next++ = *arg1;
                                        *next++ = O_NOP;
                                        if (isdummy(*dest1))
                                         { *current++=O_NOP;
                                           *current++=O_NOP;
                                           *current=O_NOP;
                                         }
                                        current=next;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET x n TEST x JMP_cond d -> SET x n NOP NOP JMP d*/
                                      (    (    *next==M_NULL_COUNTER
                                             || *next==M_POSITIV_COUNTER
                                           )
                                        && *(next+1)==*(current+1)
                                        && (next+2)!=end
                                        && (    *(next+2)==M_JMP_TRUE
                                             || *(next+2)==M_JMP_FALSE
                                           )
                                        && !jumped_on(next+2,next+4)
                                      )
                                      { boolean jmp;

                                        jmp=(*next==M_POSITIV_COUNTER)?(*(current+2)>0):(*(current+2)==0);
                                        if (isdummy(*(current+1)))
                                         /*{{{  set dummy not needed*/
                                         { *current++=O_NOP;*current++=O_NOP;*current++=O_NOP; }
                                         /*}}}  */
                                        /*{{{  test not needed*/
                                        *next++=O_NOP;*next++=O_NOP;
                                        /*}}}  */
                                        if (*next!=M_JMP_TRUE) jmp = !jmp;
                                        if (jmp)
                                         /*{{{  jmp_cond -> jmp*/
                                         { *next=M_JMP;
                                           next+=2;
                                         }
                                         /*}}}  */
                                        else
                                         /*{{{  jmp not needed*/
                                         { *next++=O_NOP;
                                           *next++=O_NOP;
                                         }
                                         /*}}}  */
                                        current=next;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET x n TEST_CHARxxx dummy -> [SET x n;] TEST_CHARxxx n*/
                                      (   *(next+1)==*(current+1)
                                       && (*next==M_TEST_CC || *next==M_TEST_H_CC || *next==M_TEST_L_CC)
                                      )
                                      { switch (*next)
                                         { case M_TEST_H_CC: *next=M_TEST_CHAR_HIGH;break;
                                           case M_TEST_L_CC: *next=M_TEST_CHAR_LOW; break;
                                           default:          *next=M_TEST_CHAR;     break;
                                         }
                                        *(next+1)=*(current+2);
                                        if (isdummy(*(current+1)))
                                         { *current++=O_NOP;*current++=O_NOP;*current++=O_NOP; }
                                        else
                                           current=next;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET dummy n PUSH dummy POP x -> SET x n NOP NOP NOP NOP*/
                                      (    isdummy(*dest1)
                                        && *next==M_PUSH_INT
                                        && *dest1==*dest2
                                        && (next+2)!=end
                                        && *(next+2)==M_POP_INT
                                        && !jumped_on(next+2,next+4)
                                      )
                                      { *dest1= *(next+3);
                                        *next++=O_NOP;
                                        *next++=O_NOP;
                                        *next++=O_NOP;
                                        *next++=O_NOP;
                                        current=next;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET dummy n M_ECHO_I dummy -> M_ECHO_P n&255 M_END_MACRO O_NOP*/
                                      (    isdummy(*dest1)
                                        && *next==M_ECHO_I
                                        && *dest1==*dest2
                                      )
                                      { *current++=M_ECHO_P;
                                        *current++=(*arg1)&(O_NOP-1);
                                        *current++=M_END_MACRO;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     else if
                                      /*{{{  SET dummy n M_LOAD_MAC x  dummy -> M_LOAD_MAC x -n*/
                                      (    isdummy(*dest1)
                                        && *next==M_LOAD_MAC
                                        && *arg2==*dest1
                                      )
                                      { int n;

                                        n= *arg1;
                                        *current++=M_LOAD_MAC;
                                        *current++= *dest2;
                                        *current++= -n;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        *current++=O_NOP;
                                        goto modif_code;
                                      }
                                      /*}}}  */
                                     goto skip_code;
                                   }
                                  /*}}}  */
                                  default:
                                     goto skip_code;
                                }
                          }
                      /*}}}  */
                      skip_code:
                       /*{{{  set current to next and loop*/
                         current=next;
                         continue;
                       /*}}}  */
                      modif_code:
                       /*{{{  count modifications and loop*/
                         changed=True;
                         modified++;
                         continue;
                       /*}}}  */
                    }
                 /*}}}  */
              }
           }
          /*}}}  */
        }
        /*}}}  */
        if (verbose_level>1)
         /*{{{  maybe print current optimization strip*/
         { s_jmp_cut+=jmp_cut;
           s_modified+=modified;
           s_shrink+=shrink;
           loop++;
           if (jmp_cut+modified+shrink && verbose_level>2)
              fprintf(stderr,F_OPT_FORMAT,loop,jmp_cut,modified,shrink);
         }
         /*}}}  */
        if (!changed)
         { opt_type++;
           if (opt_type==set_fixed_cmd && !fix_args)
              opt_type++;
           if (opt_type!=opt_ready)
              changed=True;
         }
      }
      /*}}}  */
     while (changed && loop_max-->0);
     /*{{{  check correct usage of arguments*/
     { TOKEN *current;

       for (current=start;current!=end;)
        { switch (*current)
           { TOKEN x;

             /*{{{  M_TEST_CHARxxx check char!*/
             case M_TEST_CHAR:
             case M_TEST_CHAR_LOW:
             case M_TEST_CHAR_HIGH:
                x= *(current+1);
                current+=2;
                if (x<0 || x>=O_NOP)
                   goto warn_char;
                break;
             /*}}}  */
             /*{{{  O_LANGUAGE     check char!*/
             case O_LANGUAGE:
                current+=1;
                if (current!=end)
                 { x= *current;
                   if (x>=0 && x<O_NOP)
                    { char **s;
                
                      for (s=f_c_name,x=toupper(x);;s++)
                         if (**s==x)
                            break;
                         else if (!**s)
             warn_char:{ error_po();
                            fprintf
                             ( stderr,
                               F_WARN_CHAR,
                               x,
                               (100*(current-start))/(end-start)
                             );
                            break;
                          }
                    }
                 }
                break;
             /*}}}  */
             default:
                current=next_command(current);
                break;
           }
        }

     }
     /*}}}  */
     if (verbose_level>1)
      /*{{{  maybe print current optimization strip*/
      if ((verbose_level==2 || loop>1) && s_jmp_cut+s_modified+s_shrink)
        fprintf(stderr,F_OPT_S_FORMAT,loop-1,s_jmp_cut,s_modified,s_shrink);
      /*}}}  */
     return(end);
   }
   /*}}}  */
#endif
