#include "c.h"
#include "cv.h"

typedef struct tagLocal {
	char data[50];
	short len;
} LocalVariable;

LocalVariable LocalsTable[256];
static ProcList *LastProc;
ProcList *ProceduresList;
static int DebugSymbolPtr = 0;
DataList *DataSymbolsInfo;
static DataList *LastDataSymbolsInfo;
DataList *DataTypesInfo;
static DataList *LastDataTypesInfo;
static int AddToEmittedTypes(Type ty,int cvType);
int AddNewType(char *data,int len,char *name);
int AddArrayType(Type ty,char *name);
int BuildPointerType(int xrefIdx);
static int Typesidx = 0;
static int GetTypeInternal(Type ty,char *name);

void PutInSymbolBuffer(char *data,int len)
{
	if (DebugSymbolPtr > 255) return;
	if (len < 50) {
		memcpy(LocalsTable[DebugSymbolPtr].data,data,len);
		LocalsTable[DebugSymbolPtr].len = len;
		DebugSymbolPtr++;
	}
}

void PutSymbolRecord(int type,char *data,int len,char *name)
{
	char tmprec[256];
	int i;

	for (i=4; i<len+4;i++) {
		tmprec[i] = data[i-4];
	}
	tmprec[i++] = strlen(name);
	while (*name) {
		tmprec[i++] = *name++;
	}
	*((unsigned short *)tmprec) = i-2;
	*((unsigned short *)(tmprec+2)) = type;
	PutInSymbolBuffer(tmprec,i);
}

int BuildFunctionPointerRecord(Type ty,char *name)
{
	Type *tp;
	int nrOfArgs,len,idx,result;
	unsigned short *data;
	char tmpbuffer[12],*p;

	nrOfArgs = 0;
	tp = ty->type->u.f.proto;
	if (tp) {
		while (*tp) {
			nrOfArgs++;
			tp++;
		}
	}
	len = 3 + nrOfArgs;
	len = len * sizeof(short);
	data = (unsigned short *)malloc(len);
	data[0] = len-2;
	data[1] = LF_ARGLIST;
	data[2] = nrOfArgs;
	tp = ty->type->u.f.proto;
	idx = 3;
	if (tp) {
		while (*tp) {
			data[idx++] = GetTypeInternal(*tp,"");
			tp++;
		}
	}
	result = AddNewType((char *)data,len,name);
	free(data);
	p = tmpbuffer;
	*(unsigned short *)p = 10;
	p += 2;
	*(unsigned short *)p = 0x8; /* Procedure */
	p += 2;
	*(unsigned short *)p = 116; /* ??? */
	p += 2;
	*p++ = 0;	/* no special call flags */
	*p++ = 0;   /* reserved */
	*(unsigned short *)p = nrOfArgs;
	p += 2;
	*(unsigned short *)p = Typesidx+4096 - 1;
	result = AddNewType(tmpbuffer,12,name);
	return(BuildPointerType(4096+result));
}


static int GetTypeInternal(Type ty,char *name)
{
	int st;

	switch (ty->op) {
		case CHAR:
			if (ty->u.sym->name[0] == 'u')
				return(0x20);
			return(0x10);
		case DOUBLE:
			return(0x41);
		case FLOAT:
			 return(0x40);
		case STRUCT:
			return(AddToEmittedTypes(ty,LF_STRUCTURE));
		case ENUM:
			return(0x74); /* signed int */
		case INT:
		case UNSIGNED:
			if (ty->u.sym->name[0] == 'u')
				return(0x75);
			return(0x74); /* signed int */
		case SHORT:
			if (ty->u.sym->name[0] == 'u')
				return(0x73);
			return(0x72); /* signed int */
		case FUNCTION:
			break;
		case ARRAY:
			return(AddArrayType(ty,name));
		case UNION:
			return(AddToEmittedTypes(ty,LF_UNION));
		case POINTER:

			assert(ty->type);
redo:
			st = ty->type->op;
			switch(st) {
				case CONST:
					ty = ty->type;
					goto redo;
				case CHAR:
					return(0x470);
				case DOUBLE:
					return(0x441);
				case FLOAT:
					return(0x440);
				case STRUCT:
					return(AddToEmittedTypes(ty,LF_STRUCTURE));
				case UNION:
					return(AddToEmittedTypes(ty,LF_UNION));
				case ENUM:
				case INT:
					if (ty->type->type && ty->type->type->u.sym->name &&
						ty->type->u.sym->name[0] == 'u')
						return(0x475);
					return(0x474);
				case UNSIGNED:
					if (ty->type->type == NULL) return(0x475);
					break;
				case SHORT:
					if (ty->type->type && ty->type->type->u.sym->name &&
						ty->type->u.sym->name[0] == 'u')
						return(0x473);
					return(0x472);
				case VOID:
					return(0x403);
				case FUNCTION:
					return(4096+BuildFunctionPointerRecord(ty,name));
					break;
				case POINTER:
					return 4096 + BuildPointerType(GetTypeInternal(ty->type,name));
				default:
					warning("Unknown type %s (%d) when generating debug info\n",name,st);
					break;

			}
			break;
	}
	return(0);
}

int GetType(Symbol p)
{
	return(GetTypeInternal(p->type,p->name));
}
/*
	17	EAX
	18	ECX
	19	EDX
	20	EBX
	21	ESP
	22	EBP
	23	ESI
	24	EDI
*/
static int FindRegisterNumber(char *regName)
{
	if (regName[2] == 'a') return 17;
	else if (regName[2] == 'b') return 20;
	else if (regName[2] == 'c') return 18;
	else if (regName[2] == 's') return 23;
	else if (regName[2] == 'd' && regName[3] == 'x')
		return 19;
	else if (regName[2] == 'd' && regName[3] == 'i')
		return 24;
	return 0;
}

void GenLocalDebugInfo(Symbol p)
{
	struct autoDataInfo info;
	int regNumber;
	char buf[150],*pt;
	int i;

	if (glevel == 0) return;
	if (p->x.offset == 0 && p->x.regnode) {
	/*
	This symbol record describes a symbol that has been enregistered.  Provisions 
	for enabling future implementation tracking of a symbol into and out of 
	registers is  provided in this symbol.  When the symbol processor is examining 
	a register symbol, the length field of the symbol is compared with the offset of 
	the byte following the symbol name field.  If these are the same, there is no 
	register tracking information.  If the length and offset are different, the byte 
	following the end of the symbol name is examined.  If the byte is zero, there is 
	no register tracking information following the symbol.  If the byte is 	not zero,
	then the byte indexes into the list of stack machine implementations and styles 
	of register tracking. Microsoft does not currently emit or process register 
	tracking information. 
	2		2			2		2			*		*
	length	S_REGISTER	@type 	register 	name	tracking

	@type	The type of the symbol
	register	Enumerate  of the registers in which the symbol value is stored.  
	This field is treated as two bytes. The high order byte specifies the register 
	in which the high order  part of the value is stored.  The low byte specifies 
	the register for the low order part of the value.  If the value is not stored 
	in two registers then high order register field contains the enumerate value 
	for no register.   For register enumeration values, see Section 6. The 
	register index enumeration is specific to the processor model for the module.
	name	Length-prefixed name of the symbol stored in the register
	tracking	Register tracking information.  Format unspecified.
	32-bit registers

	17	EAX
	18	ECX
	19	EDX
	20	EBX
	21	ESP
	22	EBP
	23	ESI
	24	EDI

	*/
		regNumber = FindRegisterNumber(p->x.name);
		pt = buf + 2;
		*(unsigned short *)pt = S_REGISTER;
		pt += 2;
		*(unsigned short *)pt = GetType(p);
		pt += 2;
		*(unsigned short *)pt = regNumber;
		pt += 2;
		pt = PutLengthPrefixedName(pt,p->name);
		*pt++ = 0;
		i = pt - buf;
		if (i & 1)
			*pt++ = 0;
		i = pt - buf;
		pt = buf;
		*(unsigned short *)pt = i-2;
		PutInSymbolBuffer(buf,i);
		return;
	}
	else
		info.offset = p->x.offset;
	info.type = GetType(p);
	PutSymbolRecord(S_BPREL32,(char *)&info,6,p->name);
}
void GenBlockDebugInfo(Code cp)
{
	Symbol         *p = cp->u.block.locals;
	for (; *p; p++) {
		GenLocalDebugInfo(*p);
	}
}

ProcList *newProcList(char *name)
{
	ProcList *result;
	result = (ProcList *)malloc(sizeof(ProcList));
	memset(result,0,sizeof(ProcList));
	result->procInfo.u.Name = malloc(strlen(name)+1);
	strcpy(result->procInfo.u.Name,name);
	return(result);
}
DataList *newDataList(int len,char *name)
{
	DataList *result;

	result = malloc(sizeof(DataList));
	result->Next = NULL;
	result->data = malloc(len);
	result->len = len;
	result->Name = name;
	return(result);
}

#if 0
int FindArgListType(int nrOfArguments,unsigned short *typesVector)
{
	ProcList *pl;

	pl = ProceduresList;
	while (pl) {
		if (pl->NrOfArguments == nrOfArguments) {
			if (pl->FunctionRecord) {
				if (!memcmp(pl->FunctionRecord->data+6,typesVector,nrOfArguments*sizeof(short))) {
					return(pl->procInfo.procType);
				}
			}
		}
		pl = pl->Next;
	}
	return(-1);
}
#endif

static int BuildFunctionType(ProcList *pl,Symbol f)
{
	char tmpbuffer[12],*p;

	p = tmpbuffer;
	*(unsigned short *)p = 10;
	p += 2;
	*(unsigned short *)p = 0x8; /* Procedure */
	p += 2;
	*(unsigned short *)p = GetTypeInternal(f->type,f->x.name);
	p += 2;
	*p++ = 0;	/* no special call flags */
	*p++ = 0;   /* reserved */
	*(unsigned short *)p = pl->NrOfArguments;
	p += 2;
	*(unsigned short *)p = Typesidx+4096 - 1;
	return(AddNewType(tmpbuffer,12,pl->FunctionRecord->Name));
}

void WriteFunctionTypeRecord(Symbol f,int nrOfArguments,unsigned short *typesVector)
{
	int len,topad;
	unsigned short *data;

//	ti = FindArgListType(nrOfArguments,typesVector);
#if 0
	if (ti != -1) {
		LastProc->procInfo.procType = ti;
		LastProc->FunctionRecord = NULL;
		return;
	}
#endif		
	len = 3 + nrOfArguments;
	topad = 0;
	len = len * sizeof(short);
	data = (unsigned short *)malloc(len);
	data[0] = len-2;
	data[1] = LF_ARGLIST;
	data[2] = nrOfArguments;
	memcpy(&data[3],typesVector,nrOfArguments * sizeof(short));
	if (topad) {
		unsigned char *p;

		p = (unsigned char *)data + len - 2;
		*p++ = (unsigned char)0xF2;
		*p++ = (unsigned char)0xF1;
	}
	LastProc->FunctionRecord = newDataList(len,f->x.name);
	LastProc->FunctionRecord->data = (char *)data;
	LastProc->FunctionRecord->len = len;
	/* This points to the type index record */
	LastProc->procInfo.procType = 4096 + Typesidx+1;
	if (LastProc->procInfo.procType  > 8000) {
		fprintf(stderr,"bad synch in debug info\n");
	}
	AddNewType(LastProc->FunctionRecord->data,LastProc->FunctionRecord->len,f->x.name);
	BuildFunctionType(LastProc,f);
}

void WriteLocalsDebugInfo(Symbol f,int lineno)
{
	int i,len,nrOfArguments;
	char *p,*pname;
	Symbol *Arguments,sym;
	struct autoDataInfo info;
	unsigned short *typesVector;
	
 	if (glevel == 0) return;
	Arguments = f->u.f.callee;
	nrOfArguments = 0;
	while (*Arguments) {
		Arguments++;
		nrOfArguments++;
	}
	if (nrOfArguments)
		typesVector = (unsigned short *)malloc(sizeof(short)*nrOfArguments);
	else
		typesVector = NULL;
	Arguments = f->u.f.callee;
	for (i=0;i<nrOfArguments;i++) {
		sym = *Arguments++;
		info.offset = sym->x.offset;
		info.type = GetType(sym);
		PutSymbolRecord(S_BPREL32,(char *)&info,6,sym->name);
		typesVector[i] = info.type;
	}
	for (i=0,len=0;i<DebugSymbolPtr;i++) {
		len += LocalsTable[i].len;
	}
	pname = f->x.name;
	/* maybe use f->name? */
	if (ProceduresList == NULL) {
		ProceduresList = newProcList(pname);
		LastProc = ProceduresList;
	}
	else {
		LastProc->Next = newProcList(pname);
		LastProc = LastProc->Next;
	}
	LastProc->data = malloc(len);
	p = LastProc->data;
	for (i=0; i<DebugSymbolPtr;i++) {
		memcpy(p,LocalsTable[i].data,LocalsTable[i].len);
		p += LocalsTable[i].len;
	}
	LastProc->len = len;
	LastProc->Sym = f;
	DebugSymbolPtr = 0;
	LastProc->NrOfArguments = nrOfArguments;
	WriteFunctionTypeRecord(f,nrOfArguments,typesVector);
	free(typesVector);
}

char *PutLengthPrefixedName(char *dst,char *src)
{
	if (src == NULL) src = "_unnamed";
	*dst++ = strlen(src);
	while (*src) {
		*dst++ = *src++;
	}
	return(dst);
}

DataList *AddToCVData(char *data,int len,char *name)
{
	if (DataSymbolsInfo == NULL) {
		DataSymbolsInfo = newDataList(len,name);
		LastDataSymbolsInfo = DataSymbolsInfo;
	}
	else {
		LastDataSymbolsInfo->Next = newDataList(len,name);
		LastDataSymbolsInfo = LastDataSymbolsInfo->Next;
	}
	memcpy(LastDataSymbolsInfo->data,data,len);
	return(LastDataSymbolsInfo);
	
}

int AddNewType(char *data,int len,char *name)
{
	if (DataTypesInfo == NULL) {
		DataTypesInfo = newDataList(len,name);
		LastDataTypesInfo = DataTypesInfo;
	}
	else {
		LastDataTypesInfo->Next = newDataList(len,name);
		LastDataTypesInfo = LastDataTypesInfo->Next;
	}
	memcpy(LastDataTypesInfo->data,data,len);
	return(Typesidx++);
	
}

extern int NrOfTypes;
/*
(0x0406) Data Member
This leaf specifies nonstatic data members of a class.
  
2			2		2			*		*
LF_MEMBER	@type	attribute	offset	name

@type		Index to type record for field
attribute	Member attribute bit field
offset 		Numeric leaf specifying the offset of field in the structure
name		Length-prefixed name of the member field
*/
int BuildFieldList(Field flist,int count)
{
	char *buffer,*p;
	int memberType,len,result,padCount;

	p = buffer = malloc(16000);
	*(unsigned short *)p = 0;
	p += 2;
	*(unsigned short *)p = 0x204;
	p += 2;
	while (flist) {
		*(unsigned short *)p = LF_MEMBER; /* (0x406) */
		p += 2;
		memberType = GetTypeInternal(flist->type,flist->name);
		*(unsigned short *)p = memberType;
		p += 2;
		*(unsigned short *)p = 0;
		p += 2;
		*(unsigned short *)p = flist->offset;
		p += 2;
		p = PutLengthPrefixedName(p,flist->name);
		flist = flist->link;
		len = (p - buffer);
		/*
		Because of the requirement for natural alignment, there may be padding 
		between elements of the field list.  As a program walks down the field 
		list, the address of the next subfield is calculated by adding the length 
		of the previous field to the address of the previous field.  The byte at 
		the new address is examined and if it is greater than 0xf0, the low four 
		bits are extracted and added to the address to find the address of the next 
		subfield. These padding fields are not included in the count field of the 
		class, structure, union, or enumeration type records.
		*/
		padCount = len % 4;
		if (padCount) {
			padCount = 4 - padCount;
			*p++ = (char)(0xF0 | padCount--);
			while (padCount > 0) {
				*p++ = (char)(0xF0|padCount--);
			}
		}
	}
	len = p - buffer;
	p = buffer;
	*(unsigned short *)p = len-2;
	result = AddNewType(buffer,len,"");
	free(buffer);
	return(result);
}
char *PutNumericValue(char *dst,int size)
{
	if (size < 0x8000) {
		*(unsigned short *)dst = size;
		dst += 2;
	}
	else {
		*(unsigned short *)dst = LF_LONG;
		dst += 2;
		*(unsigned long *)dst = size;
		dst += 4;
	}
	return(dst);
}

/*
(0x0004) Classes and Structures
The format for structures and classes is as follows:
  
2		2		2		2			2		2		*		*

leaf	count	@field	property	@dList	@vshape	length	name

leaf	LF_CLASS or LF_STRUCTURE
count	Number of elements  in the class or structure.  This count includes direct, 
		virtual, and indirect virtual bases, and methods including overloads, data 
		members, static data members, friends, and so on.
@field	Type index of the field list for this class.    
property	Property bit field

	packed	:1	Structure is packed
	ctor	:1	Class has constructors and/or destructors
	overops	:1	Class has overloaded operators
	isnested	:1	Class is a nested class
	cnested	:1	Class contains nested classes
	opassign	:1	Class has overloaded assignment
	opcast	:1	Class has casting methods
	fwdref	:1	Class/structure is a forward (incomplete) reference
	scoped	:1	This is a scoped definition
	reserved	:8
@dList	Type index of the derivation list.  This is output by the compiler as 
		0x0000 and is filled in by the CVPACK utility to a  LF_DERIVED record 
		containing the type indices of those classes which immediately inherit 
		the current class.  A zero index indicates that no derivation information 
		is available.  A LF_NULL index indicates that  the class is not inherited 
		by other classes.
@vshape	Type index of the virtual function table shape descriptor
length	Numeric leaf specifying size in bytes of the structure
name	Length-prefixed name this type

(0x0005) Structures
Structures have the same format as classes.  Structure type records are used 
exclusively by the C compiler.  The C++ compiler emits both class and structure 
records depending upon the declaration.
*/
int BuildStructType(Type ty,int cvType)
{
	char *name;
	Field flist;
	int size = ty->size;
	int count,len,padCount,result;
	Symbol sym;
	char buffer[200],*p;

	sym = ty->u.sym;
	name = sym->name;
	flist = sym->u.s.flist; 
	count  = 0;
	while (flist) {
		count++;
		flist = flist->link;
	}
	flist = sym->u.s.flist;
	memset(buffer,0,200);
	p = buffer + 2;
	*(unsigned short *)p = cvType;
	p += 2;
	*(unsigned short *)p = count;
	p += 2;
	*(unsigned short *)p = 0;
	p += 2;
	if (cvType == LF_STRUCTURE) {
		/* ignore for the moment the problem of knowing if 
		this structure is packed */
		p += 2;
		/* leave the derived field as zero */
		p += 2;
		/* no virtual functions */
		p += 2;
	}
	else if (cvType == LF_UNION) {
		/* Property bit field undocumented */
		p += 2;
	}
	p = PutNumericValue(p,size);
	*p++ = strlen(name);
	while (*name) {
		*p++ = *name++;
	}
	len = (p-buffer);
	padCount = len % 4;
	if (padCount) {
		padCount = 4 - padCount;
		*p++ = (char)(0xF0 | padCount--);
		while (padCount > 0) {
			*p++ = (char)(0xF0|padCount--);
		}
	}
	len = (p - buffer)-2;
	p = buffer;
	*(unsigned short *)p = len;
	result = AddNewType(buffer,len+2,sym->name);
	p = LastDataTypesInfo->data + 6;
	*(unsigned short *)p = BuildFieldList(flist,count)+4096;
	return(result);
}

int BuildPointerType(int xrefIdx)
{
	char buffer[8];
	short *p;

	p = (short *)buffer;
	p[0] = 6;
	p[1] = LF_POINTER;
	p[2] = 0xA; /* near 32 bit pointer */
	p[3] = xrefIdx;
	return(AddNewType(buffer,8,""));
}
static int AddToEmittedTypes(Type typ,int cvType)
{
	DataList *save;

	if (typ == NULL) return(0);
	if (typ->op == POINTER) {
		Type ty;

		ty = typ->type;
		assert(ty);
		if (ty->x.marked == 0) {
			if (ty->op != STRUCT && ty->op != UNION) {
				return(0);
			}
			ty->x.typeno = Typesidx;
			ty->x.marked = 1;
			save = LastDataTypesInfo ;
			if (ty->op == STRUCT) {
				BuildStructType(ty,LF_STRUCTURE);
			}
			else if (ty->op == UNION) {
				BuildStructType(ty,LF_UNION);
			}
			else return(0);
		}
		if (ty->x.pointerEmitted) {
			return(ty->x.pointerIndex+4096);
		}
		ty->x.pointerEmitted = 1;
		ty->x.pointerIndex = BuildPointerType(ty->x.typeno+4096);
		return(ty->x.pointerIndex+4096);
	}
	if (typ->x.marked == 0) {
		if (typ->op != STRUCT && typ->op != UNION) {
			return(0);
		}
		typ->x.typeno = Typesidx;
		typ->x.marked = 1;
		save = LastDataTypesInfo;
		if (typ->op == STRUCT) {
			BuildStructType(typ,LF_STRUCTURE);
		}
		else if (typ->op == UNION) {
			BuildStructType(typ,LF_UNION);
		}
	}
	return(typ->x.typeno+4096);
}
/*
(0x0003) Simple Array
  
2			2			2			*		*

LF_ARRAY	@elemtype	@idxtype	length	name

@elemtype	Type index of each array element
@idxtype	Type index of indexing variable 
length	Length of array in bytes
name	Length-prefixed name of array

*/
int AddArrayType(Type ty,char *name)
{
	int len,result;
	char buf[256],*p,*n;

	assert(ty->type);
	p = buf;
	p += 2;
	*(unsigned short *)p = LF_ARRAY;
	p += 2;
	n = NULL;
	if (ty->type->u.sym)
		n = ty->type->u.sym->name;
	if (n == NULL) n = "";
	*(unsigned short *)p = GetTypeInternal(ty->type,n);
	p += 2;
	*(unsigned short *)p = 0x74; /* index is signed int... */
	p += 2;
	p = PutNumericValue(p,ty->size);
	p = PutLengthPrefixedName(p,name);
	len = p - buf;
	*(unsigned short *)buf = len - 2;
	result = AddNewType(buf,len,name);
	return result+4096;

}
