/*-
 * Copyright (c) 1999 Takanori Watanabe
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *     $Id: aml_parse.c,v 1.8 1999/04/27 01:37:36 takawata Exp $
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "aml_parse.h"
#include "aml_elem.h"
/*Meaning of argument list in this table
  O:Object 
  B:raw Byte data (not encupsled in Object
  W:raw Word data (same as above)
  D:raw Dword data (same as above)
  F:Field data(Object(Name 0,1),Varlength or Access)*/ 
struct instruction stringinst={0xd,"String","S",NULL};
struct instruction nameinst={'A',"","N",NULL};
struct instruction multinameinst= {0x2F,"","M"/*"BO?"*/,NULL};
struct instruction fieldelementinst={0x100/*not used*/,"","OB",NULL};
struct instruction accessfieldinst={0x101/*not used*/,"","BB",NULL};
struct instruction Termtable[]= {
  {0,"0","",NULL},
  {1,"1","",NULL},
  {6,"Alias","OO",NULL},
  {8,"Name","OO",NULL},
  {0xa,"Byte","B",NULL},
  {0xb,"Word","W",NULL},
  {0xc,"Dword","D",NULL},
  /* {0xd,"String","B?",NULL},*/
  {0x10,"Scope","O","O"},
  {0x11,"Buffer","O","B"},
  {0x12,"Package","B","O"},
  {0x14,"Method","OB","O"},
  {0x2e,"","OO",NULL},/*Dualname*/
  /*'A'-'Z' Name char
  {'A',"A","BBB",NULL},
  */
  /*0x5b Table Change*/
  {0x5c,"\\","O",NULL},
  {0x5d,"^","O",NULL},
  {0x60,"Local0","",NULL},
  {0x61,"Local1","",NULL},
  {0x62,"Local2","",NULL},
  {0x63,"Local3","",NULL},
  {0x64,"Local4","",NULL},
  {0x65,"Local5","",NULL},
  {0x66,"Local6","",NULL},
  {0x67,"Local7","",NULL},
  {0x68,"Arg0","",NULL},
  {0x69,"Arg1","",NULL},
  {0x6a,"Arg2","",NULL},
  {0x6b,"Arg3","",NULL},
  {0x6c,"Arg4","",NULL},  
  {0x6d,"Arg5","",NULL},
  {0x6e,"Arg6","",NULL},
  {0x70,"Store","OO",NULL},
  {0x71,"RefOf","O",NULL},
  {0x72,"Add","OOO",NULL},
  {0x73,"Concat","OOO",NULL},
  {0x74,"Sub","OOO",NULL},
  {0x75,"Inc","O",NULL},
  {0x76,"Dec","O",NULL},
  {0x77,"Mul","OOO",NULL},
  {0x78,"Div","OOOO",NULL},
  {0x79,"SHL","OOO",NULL},
  {0x7a,"SHR","OOO",NULL},
  {0x7b,"AND","OOO",NULL},
  {0x7c,"NAND","OOO",NULL},
  {0x7d,"OR","OOO",NULL},
  {0x7e,"NOR","OOO",NULL},
  {0x7f,"XOR","OOO",NULL},
  {0x80,"Not","OO",NULL},
  {0x81,"FindSetLB","OO",NULL},
  {0x82,"FindSetRB","OO",NULL},
  {0x82,"DerefOf","O",NULL},
  {0x86,"Notify","OO",NULL},
  {0x87,"SizeOf","O",NULL},
  {0x88,"Index","OOO",NULL},
  {0x89,"Match","OBOBOO",NULL},
  {0x8a,"CreDWField","OOO",NULL},
  {0x8b,"CreWField","OOO",NULL},
  {0x8c,"CreBField","OOO",NULL},
  {0x8d,"CreBitField","OOO",NULL},
  {0x8e,"ObjectType","O",NULL},
  {0x90,"LAND","OO",NULL},
  {0x91,"LOR","OO",NULL},
  {0x92,"LNot","O",NULL},
  {0x93,"EQ","OO",NULL},
  {0x94,"GT","OO",NULL},
  {0x95,"LT","OO",NULL},
  {0xa0,"If","O","O"},
  {0xa1,"Else","","O"},
  {0xa2,"While","O","O"},
  {0xa3,"Noop","",NULL},
  {0xa4,"Return","O",NULL},
  {0xa5,"Break","",NULL},
  {0xcc,"BreakPoint","",NULL},
  {0xff,"Ones","",NULL},
  {-1,NULL,NULL,NULL}
};
struct instruction ExtTable[]={
  {1,"Mutex","OB",NULL},
  {2,"Event","O",NULL},
  {0x12,"CondRef","OO",NULL},
  {0x13,"CreateField","OOOO",NULL},
  {0x20,"Load","OO",NULL},
  {0x21,"Stall","O",NULL},
  {0x22,"Sleep","O",NULL},
  {0x23,"Aquire","OW",NULL},
  {0x24,"Signal","O",NULL},
  {0x25,"Wait","OO",NULL},
  {0x26,"Reset","O",NULL},
  {0x27,"Release","O",NULL},
  {0x28,"FromBCD","OO",NULL},
  {0x29,"ToBCD","OO",NULL},
  {0x2a,"Unload","O",NULL},
  {0x30,"Revision","",NULL},
  {0x31,"Debug","",NULL},
  {0x32,"Fatal","BDO",NULL},
  {0x80,"OpRegion","OBOO",NULL},
  {0x81,"Field","OB","F"},
  {0x82,"Device","O","O"},
  {0x83,"Processor","OBDB","O"},
  {0x84,"Powerres","OBW","O"},
  {0x85,"ThermalZone","O","O"},
  {0x86,"IndexField","OOB","F"},
  {0x87,"BankField","OOOB","F"},
  {-1,NULL,NULL,NULL}
};
struct aml_object *ReadObject(struct aml_env *);
int pkglen(struct aml_env *aenv){
  int len;
  unsigned char *code;
  int res;
  code=aenv->code;
  res=(*code&0xc0)>>6;
  switch(res){
  case 0:
    len=*code;
    break;
  case 1:
    len=(code[0]&0xf)+code[1]*0x10;
    break;
  case 2:
    len=(code[0]&0xf)+code[1]*0x10+code[2]*0x1000;
    break;
  case 3:
    len=(code[0]&0xf)+code[1]*0x10+code[2]*0x1000+code[3]*0x100000;
    break;
  }
  aenv->code+=(res+1);
  return len;
}

struct instruction *LookupTable(int c,struct instruction *table)
{
  int i;
  for(i=0;table[i].name!=NULL;i++){
    if(c==table[i].opcode)
      return &table[i];
  }
  return NULL;
}
/*Destroy aml_object*/
void DestroyAMLObj(struct aml_object *obj){
  int i,j;
  struct instruction *inst;
  inst=obj->objs[0].inst;
  for(i=0;inst->args[i]!='\0';i++){
    if(inst->args[i]=='O'){
      DestroyAMLObj(obj->objs[i+1].obj);
    }
    if(inst->args[i]=='M'){
      for(j=0;j<obj->objs[i+1].num;j++){
	DestroyAMLObj(obj->objs[j+2].obj);
      }
    }
  }
  free(obj);
}
/*
 *Alloc aml_object structure,depend on aml instruction code, "additional" 
 *Argument is used only in Multi name,To determine size.
 */

struct aml_object *AllocAMLObj(struct instruction *inst,int additional)
{
  struct aml_object *obj;
  int numalloc;
  numalloc=(inst==&multinameinst)?additional+2:strlen(inst->args)+1;
  numalloc+=((inst->varobj==NULL)?0:1);/*Alloc for aml_env*/
  obj=calloc(numalloc,sizeof(union aml_objbody));
  obj->objs[0].inst=inst;
  if(inst==&multinameinst){
    obj->objs[1].num=additional;
  }
  return obj;
}

struct aml_object *ReadField(struct aml_env *aenv){
  int index=0,len;
  struct aml_object *obj;
  /*Access Field*/
  if(aenv->code[0]==1){
    obj=AllocAMLObj(&accessfieldinst,0);
    printf("Access %d %d ]\n",aenv->code[1],aenv->code[2]);
    obj->objs[1].num=aenv->code[1];
    obj->objs[2].num=aenv->code[2];
    aenv->code+=3;
    return obj;
  }
  obj=AllocAMLObj(&fieldelementinst,0);
  obj->objs[1].obj=ReadObject(aenv);
  obj->objs[2].num=pkglen(aenv);
  return obj;
}

struct aml_object *ReadObject(struct aml_env *aenv)
{
  int index=0,resval=0;
  struct aml_env *copy;
  struct aml_object *obj;
  int i,inscode,skip,argnum;
  struct instruction *table=Termtable,*inst;
  inscode=aenv->code[0];
  aenv->code++;
  if(inscode==0xd){
    char *str;
    skip=strlen(aenv->code)+1;
    str=aenv->code;
    aenv->code+=skip;
    obj=AllocAMLObj(&stringinst,0);
    obj->objs[1].string=calloc(skip,sizeof(char));
    strcpy(obj->objs[1].string,str);
    return obj;
  }
  if(inscode==0x2f){
    /*Multi name */
    int numobj;
    numobj=*aenv->code;
    aenv->code++;
    obj=AllocAMLObj(&multinameinst,numobj);
    for(i=0;i<numobj;i++){
      obj->objs[i+2].obj=ReadObject(aenv);
    }
    return obj;
  }else if(('A'<=inscode && inscode <='Z'||(inscode =='_'))){
    /*Name*/
    obj=AllocAMLObj(&nameinst,0);
    obj->objs[1].name[0]=inscode;
    for(i=0;i<3;i++){
      obj->objs[1].name[i+1]=*aenv->code;
      aenv->code++;
    }
    return obj;
  }
  if(inscode==0x5b){
    /*Table Change*/
    inscode=*aenv->code;
    aenv->code++;
    table=ExtTable;
  }
  inst=LookupTable(inscode,table);
  if(inst==NULL){
    return NULL;
  }else{
    obj=AllocAMLObj(inst,0);
  }
  if(inst->varobj!=NULL){
    int val;
    copy=malloc(sizeof(aenv));
    *copy=*aenv;
    val=pkglen(copy);
    aenv->code+=val;
    copy->length=aenv->code;
    aenv=copy;
    /*forget about previous context not to affect parser bug*/
  }
  for(i=0;inst->args[i]!='\0';i++){
    int bodynum;
    if(inst->args[i]=='O'){
      obj->objs[i+1].obj=ReadObject(aenv);
    }
    if(inst->args[i]=='B'){
      obj->objs[i+1].num=aenv->code[0];
      aenv->code++;
    }
    if(inst->args[i]=='D'){
      obj->objs[i+1].num=aenv->code[0]+aenv->code[1]*0x100
	+aenv->code[2]*0x10000+aenv->code[3]*0x1000000;
      aenv->code+=4;
    }
    if(inst->args[i]=='W'){
      obj->objs[i+1].num=aenv->code[0]+aenv->code[1]*0x100;
      aenv->code+=2;
    }
  }
  argnum=i;
  if(inst->varobj!=NULL){
    obj->objs[argnum+1].aenv=aenv;
  }
  return obj;
}
TermParse(struct aml_env *aenv)
{
  int index=0,length;
  char *code;
  struct aml_object *obj;
  struct aml_env newenv;
  struct instruction *inst;
  code=aenv->code;
  while(aenv->code<aenv->length){
    obj=ReadObject(aenv);
    inst=obj->objs[0].inst;
    aml_print(obj);
    DestroyAMLObj(obj);
  }
  return index;
}

