/*
 **************************************************************************************************
 *
 * ARM9 Instruction decoder  
 *
 * Status
 *	working but confuse
 *
 * Copyright 2004 Jochen Karrer. 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 Jochen Karrer ``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 <COPYRIGHT HOLDER> 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.
 * 
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Jochen Karrer.
 *
 *************************************************************************************************
 */

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "arm9cpu.h"
#include "idecode_arm.h"
#include "instructions_arm.h"
#include "sgstring.h"

#define MAX_INSTRUCTIONS (200000)

typedef struct IDecoder {
        Instruction *instr[0x10000];
} IDecoder;

static Instruction *imem;
InstructionProc **iProcTab;
static IDecoder *idecoder;

static int alloc_pointer=0;
Instruction *alloc_instruction() {
	if(alloc_pointer<MAX_INSTRUCTIONS) {
		return imem+alloc_pointer++;
	} else{
		return sg_new(Instruction);
	}
}

static unsigned int
onecount(uint32_t val) {
        int ones=0;
        while(val) {
                if(val&1)
                        ones++;
                val>>=1;
        }
        return ones;
}

static Instruction instrlist[] = { 
{0x0de00000,0x00a00000,"adc"    ,i_adc}, 	
{0x0de00000,0x00800000,"add"    ,i_add},
{0x0de00000,0x00000000,"and"    ,i_and},
//{0x0f000000,0x0a000000,"b"      ,i_b},
//{0x0f000000,0x0b000000,"bl"     ,i_bl},
{0x0e000000,0x0a000000,"bbl"     ,i_bbl},
{0x0de00000,0x01c00000,"bic"    ,i_bic}, 
{0xfff000f0,0xe1200070,"bkpt"   ,i_bkpt},
{0xfe000000,0xfa000000,"blx1"   ,i_blx1},
{0x0ff00090,0x01200010,"blx2,bx",i_blx2bx},
{0x0f000010,0x0e000000,"cdp"    ,i_cdp},
{0x0ff000f0,0x01600010,"clz"	,i_clz},
{0x0df00000,0x01700000,"cmn" 	,i_cmn},
{0x0df00000,0x01500000,"cmp"	,i_cmp},
{0x0de00000,0x00200000,"eor"	,i_eor},
{0x0e100000,0x0c100000,"ldc"	,i_ldc},
{0x0e500000,0x08100000,"ldm1"	,i_lsm},
{0x0e700000,0x08500000,"ldm2"	,i_lsm},
{0x0e501000,0x08501000,"ldm3"	,i_lsm},
{0x0c500000,0x04100000,"ldr"	,i_ldr},
{0x0c500000,0x04500000,"ldrb",   i_ldrb},
{0x0d700000,0x04700000,"ldrbt"	,i_ldrbt},
{0x0e1000f0,0x001000b0,"ldrh"	,i_ldrh},
{0x0e1000f0,0x001000d0,"ldrsb"	,i_ldrsb},
{0x0e1000f0,0x001000f0,"ldrsh"	,i_ldrsh}, 
{0x0d700000,0x04300000,"ldrt"	,i_ldrt},
{0x0f100010,0x0e000010,"mcr"	,i_mcr},
{0x0fe000f0,0x00200090,"mla"	,i_mla},
{0x0de00000,0x01a00000,"mov"	,i_mov}, 
{0x0f100010,0x0e100010,"mrc"	,i_mrc},
{0xff100010,0xfe100010,"mrc2"	,i_mrc2},
{0x0fb00000,0x01000000,"mrs"	,i_mrs},
{0x0fb00000,0x03200000,"msri"	,i_msri},
{0x0fb000f0,0x01200000,"msrr"	,i_msrr},
{0x0fe000f0,0x00000090,"mul"	,i_mul}, 
{0x0de00000,0x01e00000,"mvn"	,i_mvn},
{0x0de00000,0x01800000,"orr"	,i_orr},
{0x0de00000,0x00600000,"rsb"	,i_rsb},
{0x0de00000,0x00e00000,"rsc"	,i_rsc},
{0x0de00000,0x00c00000,"sbc"	,i_sbc},
{0x0fe000f0,0x00e00090,"smlal"	,i_smlal},
{0x0fe000f0,0x00c00090,"smull"	,i_smull},
{0x0e100000,0x0c000000,"stc"	,i_stc},
{0x0e500000,0x08000000,"stm1"	,i_lsm},
{0x0e700000,0x08400000,"stm2"	,i_lsm},
{0x0c500000,0x04000000,"str"	,i_str},
{0x0c500000,0x04400000,"strb"	,i_strb},
{0x0d700000,0x04600000,"strbt"	,i_strbt}, 
{0x0e1000f0,0x000000b0,"strh"	,i_strh},
{0x0d700000,0x04200000,"strt"	,i_strt},
{0x0de00000,0x00400000,"sub"	,i_sub}, 
{0x0f000000,0x0f000000,"swi"	,i_swi},
{0x0ff000f0,0x01000090,"swp"	,i_swp},
{0x0ff000f0,0x01400090,"swpb"	,i_swpb},
{0x0df00000,0x01300000,"teq"	,i_teq},
{0x0df00000,0x01100000,"tst"	,i_tst}, 
{0x0fe000f0,0x00a00090,"umlal"	,i_umlal},
{0x0fe000f0,0x00800090,"umull"	,i_umull},
{0x0ff00000,0x0c500000,"mrrc"	,i_mrrc},
/* Enhanced DSP Instructions */
{0x0e1000f0,0x000000d0,"ldrd"	,i_ldrd},
{0x0ff00000,0x0c400000,"mcrr"	,i_mcrr},
{0x0ff00000,0x0c500000,"mrrc"	,i_mrrc},
{0xfd70f000,0xf550f000,"pld"	,i_pld},
{0x0ff000f0,0x01000050,"qadd"	,i_qadd},
{0x0ff000f0,0x01400050,"qdadd"	,i_qdadd},
{0x0ff000f0,0x01600050,"qdsub"	,i_qdsub},
{0x0ff000f0,0x01200050,"qsub"	,i_qsub},
{0x0ff00090,0x01000080,"smlaxy" ,i_smlaxy},
{0x0ff00090,0x01400080,"smlalxy",i_smlalxy},
{0x0ff000b0,0x01200080,"smlawy" ,i_smlawy},
{0x0ff00090,0x01600080,"smulxy" ,i_smulxy},
{0x0ff000b0,0x012000a0,"smulwy" ,i_smulwy},
{0x0e1000f0,0x000000f0,"strd"	,i_strd},
{0x0,0x0,"end"	,NULL}
};

Instruction undefined = { 
	0x0,0x0,"und",i_und
};

/*
 * return true if instruction i1 is more specific than
 * instruction i2
 */
static int 
is_more_specific(Instruction *i1,Instruction *i2) {
	int verbose=0;
	int ueber=0;
#if 0
	if(!strcmp(i1->name,"bic") || !strcmp(i2->name,"bic")) {
		verbose=1;
	}
#endif
	if((i1->mask&i2->mask)==i2->mask) {
		if(verbose)
			fprintf(stderr,"%s is more specific\n",i1->name);
		return 1;
	} else if ((i1->mask&i2->mask)==i2->mask) {
		if(verbose)
			fprintf(stderr,"%s is more specific\n",i2->name);
		return 0;

	} else {
		
		if(onecount(i1->mask)>onecount(i2->mask)) {
			//fprintf(stderr,"Won %s and %s\n",i1->name,i2->name);
			return 1;
		} else  if(onecount(i2->mask)>onecount(i1->mask)){
			//fprintf(stderr,"Won %s and %s\n",i2->name,i1->name);
			return 0;
		} else if (i2->proc == i1->proc) {
			/* doen't matter, anyway the same instruction */
			return 1;
		} else {
			fprintf(stderr,"Won niemand %s and %s\n",i2->name,i1->name);
			exit(3);
		}
	}
	if((i2->value&i1->mask)==i1->value) {
		ueber|=1;
	}		
        if((i1->value&i2->mask)==i2->value) {
		ueber|=2;
	}
	if(ueber==1) {
		if(verbose)
			fprintf(stderr,"%s is more specific\n",i2->name);
		return 0;
	} else if(ueber==2) {
		if(verbose)
			fprintf(stderr,"%s is more specific\n",i1->name);
		return 1;
	} else {
		fprintf(stderr,"MIST !!!!!!!!!!!!!!!!!! for %s and %s\n",i1->name,i2->name);
		//exit(1);
	} 


	if(verbose) {
		fprintf(stderr,"BUH !!!!!!!!!!!!!!!!!! for %s and %s\n",i1->name,i2->name);
		fprintf(stderr,"Mask1 %08x Value %08x\n",i1->mask,i1->value);
		fprintf(stderr,"Mask2 %08x Value %08x\n",i2->mask,i2->value);
	}
	return 0;
}

static void 
decoder_append_instruction(IDecoder *dec,Instruction *new_inst,int index) {
	Instruction *cursor,*prev=NULL;
	Instruction *inst = alloc_instruction();
	*inst=*new_inst;	// create copy !!!! because of the linked list
	if(dec->instr[index]==NULL) {
		inst->next=NULL;
		dec->instr[index]=inst;
	} else {
		for(cursor=dec->instr[index];cursor;prev=cursor,cursor=cursor->next) {
			if(!cursor->next) {
				inst->next=NULL;
				cursor->next=inst;
				return;
			}
		}
	}
}
static void
decoder_add_instruction(IDecoder *dec,Instruction *new_inst,int index) {
	Instruction *cursor,*prev=NULL;
	Instruction *inst = alloc_instruction();
	*inst=*new_inst;	// create copy !!!! because of the linked list
	if(dec->instr[index]==NULL) {
		inst->next=NULL;
		dec->instr[index]=inst;
	} else {
		for(cursor=dec->instr[index];cursor;prev=cursor,cursor=cursor->next) {
			if(is_more_specific(inst,cursor)) {
				inst->next=cursor;
				if(prev) {
					prev->next=inst;
				} else {
					dec->instr[index]=inst;
				}
				break;
			} else if (!cursor->next) {
				inst->next=NULL;
				cursor->next=inst;
				break;
			}
		}	
	}
}

void
IDecoder_New() {
	int i;
	unsigned int statistics[10];
	Instruction *cursor;
	IDecoder *dec;
	int sum=0;
	int validcount=0;
	idecoder = dec = sg_new(IDecoder);
	imem = sg_calloc(sizeof(Instruction)*MAX_INSTRUCTIONS);
	memset(dec,0,sizeof(IDecoder));
	for(i=0;i<=INSTR_INDEX_MAX;i++) {
		for(cursor=instrlist;cursor->mask;cursor++) {
			if((INSTR_UNINDEX(i)&cursor->mask)==(cursor->value&INSTR_INDEX_MASK)) { 
				decoder_add_instruction(dec,cursor,i);
			}
		}
	}
	// Add an undefined instruction to tail of every list 
	for(i=0;i<=INSTR_INDEX_MAX;i++) {
		decoder_append_instruction(dec,&undefined,i);
	}
	fprintf(stderr,"- Instruction decoder Initialized: ");
	for(i=0;i<10;i++) {
		statistics[i]=0;
	}
	for(i=0;i<=INSTR_INDEX_MAX;i++) {
		int count=0;
		for(cursor=dec->instr[i];cursor;cursor=cursor->next) {
			count++;
			if((INSTR_UNINDEX(i)&cursor->mask)==cursor->value) {	
				break;
			}
			//fprintf(stderr,"%s ",cursor->name);
		}	
#if 0
		if(count!=1){
			fprintf(stderr,"mist at %08x",INSTR_UNINDEX(i));
			for(cursor=dec->instr[i];cursor;cursor=cursor->next) {
				fprintf(stderr,"%s ",cursor->name);
			}
			fprintf(stderr,"\n");
		}
#endif
		//fprintf(stderr,"\n");
		if(count<10) {
			statistics[count]++;	
		}
	}
	for(i=1;i<10;i++) {
		sum+=statistics[i]*i;
		validcount+=statistics[i];
		fprintf(stderr," %d",statistics[i]);
	}
	iProcTab = sg_calloc(sizeof(InstructionProc *)*(INSTR_INDEX_MAX+1));
	if(!iProcTab) {
		fprintf(stderr,"Out of Memory");
		exit(378);
	}
	for(i=0;i<=INSTR_INDEX_MAX;i++) {
		Instruction *instr=dec->instr[i];
		iProcTab[i]=instr->proc;
	}
	fprintf(stderr,"\n");
//	fprintf(stderr,"\nMedium Nr of Instructions %f\n",(float)sum/validcount);
}

Instruction *
InstructionFind(uint32_t icode) {
        int index=INSTR_INDEX(icode);
        Instruction *inst=idecoder->instr[index];
        return inst;
}

#ifdef BRINT
void
main() {
	Instruction *cursor;
	for(cursor=instrlist;cursor->mask;cursor++) {
		fprintf(stdout,"void i_%s(ARM9 *cpu,uint32_t icode){\n	fprintf(stderr,\"Not implemented %s\\n\"); exit(2);\n}\n",cursor->name,cursor->name);
        }
}
#endif
#if 0
int
main() {
	Instruction *inst;
	Instruction *comp;
	inst = instrlist;
	while(inst->mask) {
		if((inst->value&inst->mask)!=inst->value) {
			fprintf(stderr,"kaput %s\n",inst->name);
		}
		comp=instrlist;
		for(comp=instrlist;comp->mask;comp++) {
			if(comp==inst)
				continue;		
			if((inst->value & comp->mask) == comp->value)  {
				if(is_more_specific(inst,comp)) {
					fprintf(stderr,"%s is more specific than %s\n",inst->name,comp->name);
				} else {
					fprintf(stderr,"%s is more specific than %s\n",comp->name,inst->name);
				}
			}
		}
		inst++;
	}
	init_instdecoder();
}
#endif
