//===== Флаги компиляции
#pragma option w32c
#stack 0x8000
#argc TRUE

//===== Подключаемые модули
#include "wapi.h--"
#include "enums.h--"
#include "data.h--"
#include "opcodesc.h--"
#include "tree.h--"
#include "directiv.h--"
#include "tokscan.h--"
#include "exe.h--"
#include "generate.h--"
#include "parser.h--"

//===== Главная функция программы
main()
dword count,pari,cmdline;
{
	stdout=GetStdHandle(STD_OUTPUT_HANDLE);
	WRITESTR("\n32-Compiler  Version 0.01\tXAC (C) 1999.");
	WRITESTR("\nBased on SPHINX C-- Compiler Peter Cellik (C) 1995.\n");
// Разбор коммандной строки: 32.exe <SourceFileName> [/map] [/debug]
	pari=@PARAMCOUNT();
	for(count=1;count<pari;count++){	//обработка командной строки
		cmdline=@PARAMSTR(count);
		CharUpperA(EAX);
		CharToOemA(cmdline,cmdline);	// Преобразуем в ЗАГЛ в OEM кодровке
		ESI=cmdline;
		IF(DSBYTE[ESI]=='/'){
			ESI++;
			IF(lstrcmpA("MAP",ESI)==0)makemapfile = 1;
			ELSE IF(lstrcmpA("DEBUG",ESI)==0)dbg=1;
			ELSE{
				WRITESTR("ERROR > Unknown command line option: '");
				WRITESTR(cmdline);
				WRITESTR("'\n");
				ExitProcess(e_unknowncommandline);
			}
		}
		ELSE{ // Копируем имя исходного файла без расширения
			EDI=#rawfilename;
			for(;;){
				$LODSB
				IF(AL=='.')||(AL==0)BREAK; // Есть расширение?
				$STOSB;
			}
			AL=0;
			$STOSB;
			lstrcpyA(#inputfile,cmdline); // Копируем имя входного файла с расширением
		}
	}
	IF(rawfilename[0]==0){
		errmsg();
		WRITESTR("No input file specified");
		pari=1;
	}
	IF(pari < 2){
		WRITESTR("\nUsage: 32.exe [/MAP] [/DEBUG] <Source file>");
		WRITESTR("\n\t/MAP\t<< generate map file");
		WRITESTR("\n\t/DEBUG\t<< generate .TDS - debug info file\n");
		ExitProcess(e_noinputspecified);
	}
	GetMem(); 	// выделение памяти для компиляции
	TokInit();		// инициализация списков
	Compile();
	IF( error == 0 )EAX=e_ok;
	ELSE EAX=e_someerrors;
	ExitProcess(EAX);
}

//===== Компилятор
Compile()
{
	IF(makemapfile)StartMapfile();
	WRITESTR("Compiling ...\n");
	Preview(#inputfile);
	CompileAll();
/*if( endifcount > 0 )
	preerror("#endif expected before end of file");
if( outptr%16 != 0 )	// paragraph align the end of the code seg
	outptr += 16 - outptr%16;*/
	DoLink(); 	// Формирование link
	IF(posts > 0)DoPosts(); // Обновление всех post адресов
	SeekUndefined(treestart);
	if(error==0){
		wsprintfA(#mapstr,"\nCOMPILING FINISHED.\tErrors: %d\tLines:	%u\n",error,totallines);
		WRITESTR(#mapstr);
		runfilesize = outptr-output;
		postsize += postsize%2;
		PrintMemsizes(GetStdHandle(STD_OUTPUT_HANDLE));
		IF(WriteEXE()==0) {
			wsprintfA(#mapstr,"\nRun File Saved (%ld bytes).\n",runfilesize);
			WRITESTR(#mapstr);
			wsprintfA(#mapstr,"DLL: %d\tAPI: %d \n",DLLcount,APIcount);
			WRITESTR(#mapstr);
		}
//	if(dbg)
//		dotds();	// do turbo debugger line info
	}
	IF(makemapfile)FinishMapfile();
}

// ---- Предварительная обработка файла
Preview(dword filename)
long hold;
char trialfilename[FILENAMESIZE];
{
	lstrcpyA(#trialfilename,filename);
	hold = LoadInputfile(#trialfilename);
	IF(EAX==-2)unabletoopen(#trialfilename);
	IF(hold!=0)ExitProcess(e_cannotopeninput);
	lstrcpyA(#currentfilename,#trialfilename);
	module++;
	IF(module<MAXMDL) {
		lstrcpyA(FILENAMESIZE*module+#modules,#currentfilename);
		currmod = module;
	}
	IF(makemapfile){
		EBX=inptr;
		cha=DSBYTE[EBX];
		wsprintfA(#mapstr,"File %s included.\n\n%c",#currentfilename,cha);
		fprint(mapfile,#mapstr);
	}
	ShowSrcLine();
	NextChar();
	cha2 = cha;
	inptr2=inptr;
	linenum2 = 1;
	NextTok();
	WHILE(tok!=tk_eof){ // Пока не кончится входной буфер
		IF(tok==tk_directive){
			GetDirAddr(#Jmp_Directives,number);
			EAX();
		}
		ELSE IF(tok==tk_command){
			GetDirAddr(#Jmp_Commands,number);
			EAX();
		}
		ELSE IF(tok==tk_id)GetProc(tk_void);
		ELSE IF(tok==tk_void){
			NextTok();
			GetProc(tk_void);
		}
		ELSE{
			preerror("unuseable input");
			NextTok();
		}
	}
	LocalFree(input);
}

// ---- Компиляция одной процедуры или объявления данных
CompileSrc(dword ptr)
{
	EAX=src;
	if(EAX){
		inptr = EAX;
		inptr2 = EAX;
		endoffile = 0;	// На начало буфера
		linenum2 = modline&0xFFFF;
		currmod=modline>>16;
		lstrcpyA(#currentfilename,FILENAMESIZE*currmod+#modules);
		NextChar();
		cha2 = cha;
		inptr2=inptr;
		IF(tok==tk_proc){
			Proc(cpt_near);
			DoPoststrings();
		}
		ELSE IF(tok==tk_var)GlobalVar(type);
		ELSE preerror("Bad input format\n");
	}
	ELSE{ // post-переменная без размерности
		ESI=ptr;
		DSDWORD[ESI+recnumber] = postsize;
		DSDWORD[ESI+recpost] = 1;
		postsize+=TypeSize(type);
	}
}

// ---- Компиляция всех процедур и объявлений данных
CompileAll()
{
	IF(SearchTree(#tok,#type,#src,#post,"main",#number))AX=3; // Console
	ELSE IF(SearchTree(#tok,#type,#src,#post,"WinMain",#number))AX=2; // GUI
	ELSE{
		preerror("Main not found");
		return;
	}
	OptSubSystem=AX;
	OptEntryPointRVA=OptBaseOfCode+outptr-output;
	CompileSrc(treeptr);	// Компиляция main
	WHILE(SeekToDo(treestart)){
		ESI=treeptr;
		wsprintfA(#mapstr,"==>%3d %8lXh %8lXh %6Xh\t%s\n",DSDWORD[ESI+rectok],
			DSDWORD[ESI+rectype],DSDWORD[ESI+recnumber],DSDWORD[ESI+recpost],
			DSDWORD[ESI+recid]);
		fprint(mapfile,#mapstr);
		CompileSrc(treeptr);	// Компиляция исходников
	}
	IF(makemapfile)
	fprint(mapfile,"Compile all sources\n");
}

// ---- Обработка параметров при объявлении процедуры
DeclareParams()
dword paramtok,paramtype;
{
LL:
	IF(tok==tk_command)GetDirAddr(#Jmp_Commands,number);
	ELSE EAX=-1;
	IF(EAX==#CmdShort){
		paramtok = tk_param;
		paramtype=tk_short;
	}
	ELSE IF(EAX==#CmdWord){
		paramtok = tk_param;
		paramtype=tk_word;
	}
	ELSE IF(EAX==#CmdChar){
		paramtok = tk_param;
		paramtype=tk_char;
	}
	ELSE IF(EAX==#CmdByte){
		paramtok = tk_param;
		paramtype=tk_byte;
	}
	ELSE IF(EAX==#CmdInt){
		paramtok = tk_param;
		paramtype=tk_int;
	}
	ELSE IF(EAX==#CmdDword){
		paramtok = tk_param;
		paramtype=tk_dword;
	}
	ELSE{
		datatype_expected();
		NextTok();
	}
	for(;;){
		NextTok();
		IF(tok==tk_id ){
			paramsize += 4;
			AddLocalvar(#string,paramtok,paramtype,paramsize);
		}
		ELSE IF(tok==tk_semicolon){
			NextTok();
			$JMP LL
		}
		ELSE IF(tok==tk_closebracket)BREAK;
		ELSE IF(tok!=tk_comma)idexpected();
	}
}

// ---- Обработка локальных переменных при объявлении процедуры
DeclareLocals()
dword size;
dword loctok,loctype;
{
LL:
	IF(tok==tk_command)GetDirAddr(#Jmp_Commands,number);
	IF(EAX==#CmdShort){
		loctok = tk_local;
		loctype=tk_short;
		size = 2;
	}
	else IF(EAX==#CmdWord){
		loctok = tk_local;
		loctype=tk_word;
		size = 2;
	}
	else IF(EAX==#CmdChar){
		loctok = tk_local;
		loctype=tk_char;
		size = 1;
	}
	ELSE IF(EAX==#CmdByte){
		loctok = tk_local;
		loctype=tk_byte;
		size = 1;
	}
	ELSE IF(EAX==#CmdInt){
		loctok = tk_local;
		loctype=tk_int;
		size = 4;
	}
	ELSE IF(EAX==#CmdDword){
		loctok = tk_local;
		loctype=tk_dword;
		size = 4;
	}
	ELSE IF(tok==tk_eof)||(tok==tk_openbrace)$JMP L1
	ELSE{
		datatype_expected();
		NextTok();
		goto LL;
	}
	for(;;){
		NextTok();
		IF(tok==tk_id){
			AddLocalvar(#string,loctok,loctype,localsize);
			IF(tok2==tk_openblock){
				NextTok();
				NextTok();
				localsize += DoConstLongMath()*size;
				EAX=localsize;
				$TEST EAX,3;
				IF(NOTZEROFLAG){
					EAX=EAX>>2+1<<2;
					localsize=EAX;	// Выравнивание на dword
				}
				expecting(tk_closeblock);
			}
			ELSE localsize+=4;
		}
		ELSE IF(tok==tk_semicolon){
			NextTok();
			$JMP LL
		}
		ELSE IF(tok==tk_openbrace)||(tok==tk_eof)BREAK;
		ELSE IF(tok!=tk_comma)idexpected();
	}
L1:
	IF(paramsize==0)Asm("push ebp; mov ebp,esp;");
	wsprintfA(#mapstr,"sub esp,%d;",localsize);
	Asm(#mapstr);
}

// ---- Обработка обращения к уже описанной процедуре
DoAnyProc()
byte s[80];
{
	wsprintfA(#s,"call %s;",#string);
	NextTok();
	DoParams();
	Asm(#s);
}

// ---- Обработка ранее объвленной, но пока не известной метки
dword DoAnyUndefproc(dword expectedreturn)
byte s[80];
{
	IF( tok2 == tk_colon ){ // метка
		number = outptr-output+OptImageBase+OptBaseOfCode;
		tok = tk_proc;
		ESI=treeptr;
		DSDWORD[ESI+rectok] = tok;
		DSDWORD[ESI+recnumber] = number;
		DSDWORD[ESI+recpost] = 0;
		NextTok();	// move past id
		NextTok();	// move past :
		RETURN(tokens);
	}
	IF( tok2 == tk_openbracket ){
		wsprintfA(#s,"call %s;",#string);
		NextTok();
		DoParams();
		Asm(#s);
		RETURN(tk_dword);
	}
	undefinederror();
	NextTok();
	return(tk_int);
}

// ---- Обработка обращения к API функции
dword doAPI()
dword hold;
byte s[IDLENGTH];
{
	if( tok2 == tk_openbracket ){
		hold = treeptr;
		GetVarname(#s);
		NextTok();
		DoParams();
		IF(posts>=MAXPOSTS){
			preerror("maximum number of API procedure calls exceeded");
			return(tokens);
		}
		EBX=hold;
		IF(DSDWORD[EBX+recpost]==0) { // Первый вызов API?
			DSDWORD[EBX+recpost]=1; // отметим вызов данной API
			APIcount++;
			EAX=DSDWORD[EBX+rectype]; // Указатель на DLL, в котором находится API
			DSDWORD[EAX+recmodline]++;	// Увеличим счетчик API, вызванных из DLL
		}
		OUTWORD(0x15FF);	// call [dword]
		SetPost(hold,POST_API);
		OUTDWORD(0);
		IF(list){
			fprint(mapfile,"\t//\tcall ");
			fprint(mapfile,#s);
			fprint(mapfile,"\n");
		}
		return(tk_int);
	}
	undefinederror();
	NextTok();
	return(tokens);
}

// ---- Обработка программного блока {...}
void DoBlock()
{
	expecting(tk_openbrace);
	for(;;){
		IF(tok==tk_eof){
			unexpectedeof();
			BREAK;
		}
		IF(tok == tk_closebrace){
			NextTok();
			BREAK;
		}
		DoCommand();
	}
}

// ---- Обработка одной команды внутри блока
DoCommand()
{
LL:
	FastSearch(#string,#St_Sizes);// Это размер операнда?
	IF(CARRYFLAG){	// Да: byte,word или dword
		type=EAX<<1+tk_byte;
		string[0]=0;
		tok=tk_var;
		GOTO LL;
	}
	IF(tok==tk_mnemonics){
		DoMnemonics();
		NextTok();
	}
	else IF(tok==tk_directive){
		GetDirAddr(#Jmp_Directives,number);
		EAX();
	}
	else IF(tok==tk_command){
		GetDirAddr(#Jmp_Commands,number);
		EAX();
	}
	else IF(tok==tk_id){
		DoId(tk_void);
		IF(EAX!=tokens)NextSemiNext();
	}
	else IF(tok==tk_undefproc){
		DoAnyUndefproc(tk_void);
		IF(EAX!=tokens)NextSemiNext();
	}
	else IF(tok==tk_proc){
		DoAnyProc();
		NextSemiNext();
	}
	else IF(tok==tk_API){
		IF(doAPI()!=tokens)NextSemiNext();
	}
	else IF(tok==tk_var)||(tok==tk_local)||(tok==tk_param)||(tok==tk_reg)DoVar(type);
	ELSE IF(tok==tk_openblock)DoVar(tk_dword);
	ELSE IF(tok==tk_string){
		Macros();
		NextSemiNext();
	}
	ELSE IF(tok==tk_locallabel)DoLocalPost();
	ELSE IF(tok==tk_openbrace)DoBlock();
	ELSE IF(tok==tk_comma)||(tok==tk_semicolon)NextTok();
	ELSE IF(tok==tk_eof)unexpectedeof();
	/*	case tk_from:
		NextTok();	DoFrom(0);	NextSemiNext();  break;
	case tk_extract:
		NextTok();	DoExtract(0);  SemiNext();	break;
	*/
}

// ---- Обработка новых идентификаторов
dword DoId(dword expectedreturn)
byte s[80];
{
	IF(tok2 == tk_colon){ // метка?
		number = outptr-output+OptImageBase+OptBaseOfCode;
		tok = tk_proc;
		post = 0;
		AddToTree(#string);
		NextTok(); NextTok(); // пропустим идентификатор и :
		EAX=tokens;
	}
	ELSE IF(tok2 == tk_openbracket){	// вызов процедуры
		wsprintfA(#s,"call %s;",#string);
		tok = tk_undefproc;
		number=0;
		post=1;
		AddToTree(#string);
		NextTok();
		DoParams();
		Asm(#s);
		EAX=expectedreturn;
	}
	ELSE{
		undefinederror();
		NextTok();
		EAX=tk_int;
	}
}

// ---- Обработка параметров при вызове процедуры
DoParams()
{
	IF(tok==tk_openbracket){
		inptr2--;
		DoParam();
		NextTok();
	}
	ELSE expecting(tk_openbracket);
}

// ---- Обработка <Var> ...
DoVar(dword vartype)
dword next,vtok;
byte varName[2*IDLENGTH];
byte varName2[2*IDLENGTH];
{
	next=1;
	vtok=GetVarname(#varName);
	NextTok();
	IF(tok==tk_assign){
		NextTok();
		IF(tok2notstopper()){
			DoExpr(#varName,vtok,vartype,"mov");
			next=0;
		}
		ELSE GetIntoVar(#varName,vtok,vartype);
	}
	else IF(tok==tk_minusminus){	// Var--;
		wsprintfA(#mapstr,"dec %s",#varName);
		Asm(#mapstr);
	}
	else IF(tok==tk_plusplus){	// Var++;
		wsprintfA(#mapstr,"inc %s",#varName);
		Asm(#mapstr);
	}
	else IF(tok==tk_plusequals){	// Var+=Expr;
		NextTok();
		DoExpr(#varName,tk_var,vartype,"add");
		next=1;
	}
	else IF(tok==tk_minusequals){ // Var-=Expr;
		NextTok();
		DoExpr(#varName,tk_var,vartype,"sub");
		next=1;
	}
	else IF(tok==tk_andequals){ // Var&=Expr;
		NextTok();
		DoExpr(#varName,tk_var,vartype,"and");
		next=1;
	}
	else IF(tok==tk_xorequals){ // Var^=Expr;
		NextTok();
		DoExpr(#varName,tk_var,vartype,"xor");
		next=1;
	}
	else IF(tok==tk_orequals){	// Var|=Expr;
		NextTok();
		DoExpr(#varName,tk_var,vartype,"or");
		next=1;
	}
	else if(tok==tk_swap){		// Var><Var;
		NextTok();
		GetVarname(#varName2);
		IF(tok==tk_reg){
			wsprintfA(#mapstr,"xchg %s,%s",#string,#varName);
			Asm(#mapstr);
		}
		else if(tok==tk_var)&&(tok2notstopper()==0){
			IF(vartype==tk_dword)||(vartype==tk_int){
				wsprintfA(#mapstr,"xchg %s,eax",#varName);
				Asm(#mapstr);
				wsprintfA(#mapstr,"xchg %s,eax",#varName2);
				Asm(#mapstr);
				wsprintfA(#mapstr,"xchg %s,eax",#varName);
			}
			else IF(vartype==tk_word)||(vartype==tk_short){
				wsprintfA(#mapstr,"xchg %s,ax",#varName);
				Asm(#mapstr);
				wsprintfA(#mapstr,"xchg %s,ax",#varName2);
				Asm(#mapstr);
				wsprintfA(#mapstr,"xchg %s,ax",#varName);
			}
			ELSE IF(vartype==tk_byte)||(vartype==tk_char){
				wsprintfA(#mapstr,"xchg %s,al",#varName);
				Asm(#mapstr);
				wsprintfA(#mapstr,"xchg %s,al",#varName2);
				Asm(#mapstr);
				wsprintfA(#mapstr,"xchg %s,al",#varName);
			}
			Asm(#mapstr);
		}
		ELSE swaperror();
	}
	else IF(tok==tk_llequals){	// Var<<=Expr;
		NextTok();
		IF(tok == tk_number) wsprintfA(#mapstr,"shl %s,%d",#varName,DoConstMath());
		ELSE{
			Expression("cl",tk_reg,tk_byte);
			wsprintfA(#mapstr,"shl %s,cl",#varName);
			next=0;
		}
		Asm(#mapstr);
	}
	ELSE IF(tok==tk_rrequals){	// Var>>=Expr;
		NextTok();
		IF(tok == tk_number)wsprintfA(#mapstr,"shr %s,%d",#varName,DoConstMath());
		ELSE{
			Expression("cl",tk_reg,tk_byte);
			wsprintfA(#mapstr,"shr %s,cl",#varName);
			next=0;
		}
		Asm(#mapstr);
	}
	ELSE operatorexpected();
	IF(next)NextSemiNext();
	ELSE SemiNext();
}

// ---- Обработка ссылок вперед
DoPosts()
dword addhold,i;
{
	i=0;
	while(i<posts){
		ECX=i<<2;
		ESI=posttype+ECX;
		EAX=DSDWORD[ESI];
		ESI=postloc+ECX;
		EBX=DSDWORD[ESI];
		ESI=postnum+ECX;
		ECX=DSDWORD[ESI];
		IF(EAX==POST_DATA){
			IF(DSDWORD[ECX+recpost]){
				GetDword(EBX);
				EAX=EAX+DSDWORD[ECX+recnumber]+OptImageBase+OptBaseOfCode+outptr-output;
			}
			ELSE{
				GetDword(EBX);
				EAX+=DSDWORD[ECX+recnumber];
			}
			SetDword(EBX,EAX);
		}
		ELSE IF(EAX==POST_CALL){
			EAX=DSDWORD[ECX+recnumber]-OptImageBase-OptBaseOfCode-EBX; // Расчет относительного смещения EAX-=addvalue;
			EAX+=output;
			SetDword(EBX,EAX-4);
		}
		ELSE IF(EAX==POST_LOC){
			EAX=ECX; EAX-=OptImageBase;
			EAX-=OptBaseOfCode; EAX-=EBX; // Расчет относительного смещения EAX-=addvalue;
			EAX+=output; SetDword(EBX,EAX-4);
		}
		ELSE IF(EAX==POST_API){
				addhold = OptImageBase + DSDWORD[ECX+recmodline];
				SetDword(EBX,addhold);
		}
		ELSE preerror("Bad post type\n");
		i++;
	}
}

// ---- Быстрый поиск по таблице
// OUTPUT: EAX=No.& CARRYFLAG - success
dword FastSearch(dword probe,table)
byte ident[IDLENGTH];
{
	$PUSH ECX,ESI,EDI,EDX
	lstrcpyA(#ident,probe);
	CharUpperA(#ident);
	lstrlenA(#ident);
	EDX=EAX-1;
	$JZ HET // ident из одного символа
	EBX=0;
	BL=ident;//[0]; // Проверим первый символ в ident
	BL-='A';
	$JC HET 	// Не буква
	$CMP BL,'Z'-'A';
	$JA HET // и не 'A'...'Z'
	EAX=0;
	EBX=EBX<<2+table;
	EDI=DSDWORD[EBX]; // Взять адрес записи из table
	$OR EDI,EDI;
	$JE HET // Нет записи для такого ident...
TRY0:
	AH=DSBYTE[EDI];
	EDI++;	// Номер записи из table и на строку в table
TRY1:
	ESI=#ident+1;
	ECX=EDX;	// Взять ident	 длину - 1
	$REPE $CMPSB;
	$JNE	NXT // Сравним с записью в table
	$JCXZ YES1		// Совпали по длине probe
NXT:
	EDI--;
LOOK:
	AL=DSBYTE[EDI];
	EDI++;
	$OR AL,AL;
	$JE TRY0	// Проверим следующую запись
	$CMP AL,'/';
	$JE NEAR TRY1 // Проверим ALIAS
	$CMP AL,_END;
	$JE NEAR HET	// Конец таблицы - выход
	GOTO LOOK;
HET:
	$CLC;
	GOTO EX;		// ident не найден в table
YES1:
	$CMP DSBYTE[EDI],0;
	$JZ YES;
	$CMP DSBYTE[EDI],_END;
	$JZ YES;
	$CMP DSBYTE[EDI],'/';
	$JZ YES;
	GOTO NXT;
YES:
	AL=AH;
	AH=0;
	$STC	// ident найден в table
EX:
	$POP EDX,EDI,ESI,ECX;
}

// ---- Вывод статистики по компиляции
FinishMapfile()
{
	fprint(mapfile,"\n");
	DisplayTree();
	fprint(mapfile,"Component Sizes:\n");
	PrintMemsizes(mapfile);
	wsprintfA(#mapstr,"Run file size: %ld bytes\n",runfilesize);
	fprint(mapfile,#mapstr);
	wsprintfA(#mapstr,"\nEND OF MAP FILE	FOR %s.%s\n\n",#rawfilename,"EXE");
	fprint(mapfile,#mapstr);
	_lclose(mapfile);
	mapfile=0;
	list=0;
}

// ---- Вывод в файл
fprint(dword handle,str)
{
	_lwrite(handle,str,lstrlenA(str));
}

// ---- Выделение памяти для компиляции. При неудаче выход из компилятора
GetMem()
{
	output=LocalAlloc(0x40,MAXDATA);
	IF(EAX==NULL)outofmemory2();
	LocalUnlock(output);
	outptr=output;
	startptr=output;
	postloc=LocalAlloc(0x40,MAXPOSTS*4);
	IF(EAX==NULL)outofmemory2();
	postnum=LocalAlloc(0x40,MAXPOSTS*4);
	IF(EAX==NULL)outofmemory2();
	posttype=LocalAlloc(0x40,MAXPOSTS);
	IF(EAX==NULL)outofmemory2();
	dbginfo=LocalAlloc(0x40,MAXDBGS*dbg_size);
	IF(EAX==NULL)outofmemory2();
	dbgs=dbginfo;
}

// ---- Чтение двойного слова
dword GetDword(dword ptr)
{
	ESI><ptr;
	$LODSD;
	ESI><ptr;
}

// ---- Инициал зация списка
// Для keylist = {"ZAK",0,"AAD",0,"ABC",0,"BAR",0,"AAA",0,"ZZ",0,"BAC",_END};
// сформируется структура в памяти следующего в да:
//	table
// +-----+
// | 'A' | ---> db 4,'AA',1,'AD',2,'BC',_END
// +-----+
// | 'B' | ---> db 6,'AC',3,'AR",_END
// +-----+
// | ... |
// +-----+
// | 'Z' | ---> db 0,'AK',5,'Z',_END
// +-----+
InitList(dword keylist,table)
dword ptr;
{
	ptr=LocalAlloc(0x40,SORTSIZE*256);
	IF(EAX==NULL)outofmemory2();
	EDI><EAX; //ptr;
	ESI=keylist;
	ECX=0;
	BL=0;
// Занесение строк из keylist в ptr
	for(;;){
		for(;;){
			$PUSH EDI;
			IF(DSBYTE[ESI]!='-')BREAK;
			ESI+=2;
			BL++;
			$POP EDI;
		}
		AL=BL;
		$STOSB	// Сохраним порядковый номер строки
		for(;;){
			$LODSB			// Скопируем строку в ptr
			IF(AL<' ')BREAK;
			$STOSB;
		}
		IF(AL==_END)BREAK;
		$STOSB;
		$POP EDI;
		BL++;
		EDI+=SORTSIZE;
		ECX++;
	}
	ESI=ptr;
	$PUSH ECX // Копирование в ptr закончено. Сохраним кол-во строк
	ECX--;
	IF(NOTZEROFLAG){// Всего одна строка? - сортировка не нужна
// Сортировка строк в ptr
		loop(ECX){
			$PUSH ESI,ECX;
			EDI=ESI+SORTSIZE;
			loop(ECX){
				$PUSH ECX,ESI,EDI;
				ECX=SORTSIZE;
				ESI++;
				EDI++;
				$REPE $CMPSB;
				$POP EDI,ESI;
				IF(NOTCARRYFLAG){ // проверит условие ????????????Сравнение двух строк
					EAX=ESI;
					EDX=EDI;
					EDI=#Buffer16;
					ECX=SORTSIZE/4; // Нашли меньшую строку
					$REP $MOVSD;
					ESI=EDX;
					EDI=EAX;
					ECX=SORTSIZE/4; 	// -поменяем местами
					$REP $MOVSD;
					ESI=#Buffer16;
					EDI=EDX;
					ECX=SORTSIZE/4;
					$REP $MOVSD;
					ESI=EAX;
					EDI=EDX;
				}
				EDI+=SORTSIZE;
				$POP ECX;
			}
			$POP ECX,ESI;
			ESI+=SORTSIZE;
		}
	}
// Сортировка введенных строк в буфере ptr закончена
	EDI=table;
	ECX=26;
	EAX=0;
	$REP $STOSD; // Обнулен е table
	$POP ECX;
	ESI=ptr;
	EDI=ESI;
	$PUSH ESI;
	GOTO L42; // Форм руем таблицу
	loop(ECX){
		$PUSH ESI;
		IF(AH!=DSBYTE[ESI+1]){
			EDI--;
			AL=_END;
			$STOSB		// Отметим конец таблицы для данного символа в AH
L42:
			AH=DSBYTE[ESI+1];
			EBX=0;
			BL=AH;
			BL-='A';
			EBX=EBX<<2+table; 				//дальше	идет крах
			DSDWORD[EBX]=EDI; 	// Запись указателя в table
		}
		$MOVSB		// Запомним порядковый номер строки
		ESI++;		// Пропуск первого символа - он уже известен
		do{
			$LODSB;
			$STOSB;
		}while(AL!=0); // Копируем строку в table
		$POP ESI;
		ESI+=SORTSIZE;
	} // Продолжим для следующей строки
	EDI--;
	AL=_END;
	$STOSB			// table сформирована. Отметим конец
}

// ---- Проверка на цифру
dword IsNumber(dword ch)
{
	IF(ch<'0')||(ch>'9')EAX=0;
	ELSE EAX=1;
}

// ---- Чтение входного файла в буфер
long LoadInputfile(dword infile)
dword fhandle, size;
{
	fhandle=_lopen(infile,0);
	IF(EAX==-1){
		GetLastError();
		return(-2);
	}
	EAX=GetFileSize(EAX,0);
	IF(EAX==-1){
		unabletoopen(infile);
		_lclose(fhandle);
		return(-1);
	}
	size=EAX;
	input=LocalAlloc(0x40,EAX+2);	// Заполненная нулями
	IF(EAX==NULL){
		preerror("Not enough memory for input buffer");
		_lclose(fhandle);
		RETURN(-1);
	}
	EAX=_lread(fhandle,input,size);
	IF(EAX!=size){
		preerror("File Read error");
		_lclose(fhandle);
		RETURN(-1);
	}
	_lclose(fhandle);
	inptr = input;
	inptr2 =	input;
	endoffile = 0;	// На начале файла
	return(0);
}

// ---- Обработка макроса
Macros()
byte holdcha;
byte s[STRLEN],s2[STRLEN];
{
	IF(makemapfile){
		fprint(mapfile,#string);
		fprint(mapfile,"\n");
	}
	holdcha=cha2;
	$PUSH linenum2,inptr2,number,tok2,tok,input,inptr,currmod,
		linenumber,endoffile,displaytokerrors;
	lstrcpyA(#s,#string);
	lstrcpyA(#s2,#string2);
	input=#s;
	inptr=input;
	inptr2=input;
	endoffile=0;	// На начале файла
	NextChar();
	cha2=cha;
	inptr2=inptr;
	linenum2 = 1;
	NextTok();
	for(;;){
		IF(tok==tk_eof)BREAK;
		DoCommand();
	}
	lstrcpyA(#string,#s);
	lstrcpyA(#string2,#s2);
	$POP displaytokerrors,endoffile,linenumber,currmod,inptr,input,tok,tok2,
		number,inptr2,linenum2;
	cha2=holdcha;
}

// ---- Очистка списка локальных переменных
KillLocals()
dword ptr1,ptr2;
{
	ptr2=locallist;
	WHILE( ptr2 != NULL ){
		ptr1=ptr2;
		IF( DSDWORD[EAX+localtok]==tk_locallabel) // Проверка на незакрытые метки
			localunresolved(EAX+localid);
		EAX=ptr2;
		ptr2=DSDWORD[EAX+localnext];
		GlobalFree(ptr1);
	}
	locallist = NULL;
	paramsize = 0;
	localsize = 0;
}

// ---- Завершение тела процедуры
LeaveProc()
{
	IF(localsize > 0)Asm("leave");
	ELSE{
		IF(paramsize > 0)Asm("pop ebp");
	}
	IF( current_proc_type == cpt_far ){
		IF(paramsize == 0)EAX="retf";
		ELSE{
			wsprintfA(#mapstr,"retf %d;",paramsize);
			EAX=#mapstr;
		}
	}
	ELSE{
		IF(paramsize == 0)EAX="ret";
		ELSE{
			wsprintfA(#mapstr,"ret %d;",paramsize);
			EAX=#mapstr;
		}
	}
	Asm(EAX);
}

// ---- Чтение очередного символа из входного буфера
NextChar()
{
	ESI><inptr;
//	EAX=0;
	$LODSB
	cha=AL;
	inptr><ESI; // Запомним текущую поз цию в буфере
	IF(AL==0)||(AL==26)endoffile = 1;
	IF(AL == 13){ 	// CR
		linenumber++; // Обнаружен конец строки
		totallines++;
		ShowSrcLine();
		NextChar();
	}
}

// ---- Получение очередного token
void NextTok()
{
	inptr = inptr2;
	linenumber = linenum2;
	cha = cha2;
	displaytokerrors = 1;
	TokScan(#tok,#type,#src,#post,#string,#number);
	IF(linenumber!=linenum2){ // Новая строка?
		IF(dbg){			// Требуется отладка?
			$PUSH ESI
			ESI=dbgs*dbg_size+dbginfo;
			EAX=currmod;
			$CMP EAX,DSDWORD[ESI+dbg_mod];
			$JNE DIFF // Требуется запомнить информацию по новой строке
			EAX=linenumber;
			$CMP EAX,DSDWORD[ESI+dbg_line]
			$JNE DIFF 			// Требуется запомнить информацию по новой строке
			DSDWORD[ESI+dbg_line]=linenumber; // Запомним номер строки
			GOTO ALL;
DIFF:
			dbgs++;
			ESI+=dbg_size;
			DSDWORD[ESI+dbg_mod]=currmod;
			DSDWORD[ESI+dbg_line]=linenumber;
			DSDWORD[ESI+dbg_loc]=outptr;
ALL:
			$POP ESI
		}
	}
	inptr2 = inptr;
	linenum2 = linenumber;
	cha2 = cha;
	displaytokerrors = 0; // Без вывода сообщений об ошибках
	TokScan(#tok2,#type2,#src2,#post2,#string2,#number2);
	linenumber = linenum2;
}

// ---- Следующий token закрытый ;
NextSemiNext ()
{
	NextTok();
	SemiNext();
}

// ---- Закрывающий ; и token за ним
SemiNext ()
{
	IF(tok != tk_semicolon)expected(';');
	NextTok();
}

// ---- Запись байта в CODE
OP()
{
	EDI><outptr;
	$STOSB;
	outptr><EDI;
}

// ---- Запись слова в CODE
OUTWORD()
{
	EDI><outptr;
	$STOSW;
	outptr><EDI;
}
// ---- Запись двойного слова в CODE
OUTDWORD()
{
	EDI><outptr;
	$STOSD;
	outptr><EDI;
}

// ---- Вывод размеров CODE&DATA
PrintMemsizes(dword handle)
{
	wsprintfA(#mapstr,"Code:%u bytes,\tPost: %u bytes\n",outptr-output,postsize);
	fprint(handle,#mapstr);
}

// ---- Обработка процедур
Proc(dword proc_type)
{
	current_proc_type = proc_type;
	tok = tk_proc;
	number = outptr-output+OptImageBase+OptBaseOfCode;
	ESI=treeptr;
	DSDWORD[ESI+rectok] = tok;
	DSDWORD[ESI+recnumber] = number;
	DSDWORD[ESI+recpost] = 0;
	NextTok();
	expecting(tk_openbracket);
	IF(tok!=tk_closebracket)DeclareParams();
	NextTok();
	IF(paramsize > 0)Asm("push ebp;mov ebp,esp");
	IF( tok != tk_openbrace )DeclareLocals();
	DoBlock();				// Обработка тела процедуры { ... }
	LeaveProc();
	KillLocals();
}

// ---- Запись двойного слова по адресу
SetDword(dword ptr, value)
{
	EDI><ptr;
	EAX><value;
	$STOSD;
	EDI><ptr;
	EAX><value;
}

// ---- Вывод строки исходного текста
ShowSrcLine()
byte str[STRLEN];
{
	IF(list){
		ESI><inptr;
		$PUSH EDI,ESI;
		ESI++;
		EDI=#str;
		for(;;){
			$LODSB;
			IF(AL==13)||(AL==0)BREAK;
			$STOSB;
		}
		AL=0;
		$STOSB
		$POP ESI,EDI;
		ESI><inptr;
		IF(displaytokerrors){
			fprint(mapfile,#str);
			fprint(mapfile,"\n");
		}
//		AL=cha;
	}
}

// ---- Создание файла статистики
StartMapfile()
byte mapfilename[80];
{
	wsprintfA(#mapfilename,"%s.MAP",#rawfilename);
	mapfile=_lcreat(#mapfilename,0x1000);
	IF(EAX==0){
		errmsg();
		WRITESTR("Unable to create map file");
		WRITESTR(#mapfilename);
		ExitProcess(e_cannotopenmapfile);
	}
	wsprintfA(#mapstr,"MAP FILE FOR %s.%s\n\n",#rawfilename,"EXE");
	fprint(mapfile,#mapstr);
}

// ---- Инициализация списков резервных слов
TokInit()
{
	InitList(#Mnemonics,#St_Mnemonics);
	InitList(#Registers,#St_Registers);
	InitList(#Directives,#St_Directives);
	InitList(#Sizes,#St_Sizes);
}

// ---- Возвращает размер для заданного типа данных
dword TypeSize(dword vartype)
char holdstr[60];
{
	IF(vartype==tk_void)	RETURN(0);
	IF(vartype==tk_char)||(vartype==tk_byte)	RETURN(1);
	IF(vartype==tk_short)||(vartype==tk_word) RETURN(2);
	IF(vartype==tk_dword)||(vartype==tk_int) RETURN(4);
	wsprintfA(#holdstr,"vartype=%d in TypeSize()",vartype);
	internalerror(holdstr);
	return(-1);
}

// ---- Пропуск пробелов и табуляций
WhiteSpaces()
{
	for(;;){
		AL=cha;
		IF(AL!=32)&&(AL!=10)&&(AL!=9)BREAK;
NXT:
		NextChar();
	}
	IF(AL == 13){ 	// CR
		linenumber++; // Обнаружен конец строки
		totallines++;
		ShowSrcLine();
		GOTO NXT;
	}
}

// ---- Вывод на stdout
WRITESTR()
{
	fprint(stdout,EAX);
}

//===== Обработка ошибок
// ----
datatype_expected()
{
	preerror("byte, word, short, char, dword or int expected");
}

// ----
errmsg()
{
	WRITESTR("\nERROR> ");
}

// ----
expected(dword ch)
byte hstr[80];
{
	wsprintfA(#hstr,"'%c' expected",ch);
	preerror(#hstr);
}

// ----
expectederror(dword str)
byte hstr[80];
{
	IF(displaytokerrors){
		wsprintfA(#hstr,"'%s' expected",str);
		preerror(#hstr);
	}
}

// ---- Проверка текущего token на заданный тип
expecting(dword want)
{
	if(want!=tok){
		IF(want==tk_closebracket)  expected(')');
		else IF(want==tk_openbracket) 	expected('(');
		ELSE IF(want==tk_semicolon) 	expected(';');
		ELSE IF(want==tk_colon) 	expected(':');
		ELSE IF(want==tk_openblock) 	expected('[');
		ELSE IF(want==tk_closeblock)		expected(']');
		ELSE IF(want==tk_openbrace) 	expected('{');
		ELSE IF(want==tk_closebrace)		expected('}');
		ELSE IF(want==tk_comma) 	expected(',');
		ELSE	preerror("expecting a different token");
	}
	NextTok();
}

// ----
/*idalreadydefined()
byte holdstr[80];
{
	wsprintfA(#holdstr,"identifier %s already defined",#string);
	preerror(#holdstr);
	NextTok();
} */

// ----
idexpected()
{
	preerror("undefined 'identifier' expected");
}

// ---- Внутренняя ошибка компилятора
internalerror(dword str)
{
	error++;
	wsprintfA(#mapstr,"%s(%d)#%d> *** SERIOUS COMPILER INTERNAL ERROR ***\n>%s.\n",
		#currentfilename,linenumber,error,str);
	WRITESTR(#mapstr);
	wsprintfA(#mapstr,"STRING:%s\n",#string);
	WRITESTR(#mapstr);
	wsprintfA(#mapstr,"TOK:%d\tPOST:%d\tnumber:%ld\n",tok,post,number);
	WRITESTR(#mapstr);
	wsprintfA(#mapstr,"STRING2:%s\n",#string2);
	WRITESTR(#mapstr);
	wsprintfA(#mapstr,"TOK2:%d\tPOST2:%d\tnumber2:%ld\n",tok2,post2,number2);
	WRITESTR(#mapstr);
	WRITESTR("Oh no.\n");
	IF(makemapfile)CloseHandle(mapfile);
	ExitProcess(e_internalerror);
}

// ----
localunresolved(dword str)
byte holdstr[80];
{
	wsprintfA(#holdstr,"local jump label '%s' unresolved",str);
	preerror(#holdstr);
}

// ----
maxwordpostserror ()
{
	preerror("maximum number of word post location references exceeded");
}

// ----
/*notyet()
{
	preerror("specified syntax not handled in this version!!!");
} */
// ----
numexpected()
{
	preerror("'number' expected");
}

// ----
operatorexpected ()
{
	preerror("operator identifier expected");
}
// ----
outofmemory()
{
	preerror("Compiler out of memory");
	IF( makemapfile )CloseHandle(mapfile);
	ExitProcess(e_outofmemory);
}

// ----
outofmemory2()
{
	errmsg();
	WRITESTR("Not enough memory for the compiler's buffers.\n");
	ExitProcess(e_outofmemory );
}

// ---- Ошибка в текущей строке: показ номера строки и имени файла
preerror(dword str)
{
	IF(error < maxerrors){
		error++;
		wsprintfA(#mapstr,"%s (%d)#%d> %s.\n",#currentfilename,linenumber,error,str);
		WRITESTR(#mapstr);
		IF(makemapfile)fprint(mapfile,#mapstr);
	}
	ELSE toomanyerrors();
}

// ----
/*regnameerror()
{
	preerror("register name cannot be used as an identifier");
	NextTok();
} */

// ---- Требуется строка
stringexpected()
{
	preerror("'string' expected");
}

// ---- Недопустимый операнд для swap
swaperror ()
{
	preerror("invalid or incompatable swap item");
}

// ---- Предельное число ошибок - выход
toomanyerrors()
{
	IF( makemapfile )CloseHandle(mapfile);
	ExitProcess( e_toomanyerrors );
}

// ----
unabletoopen(dword str)
byte hstr[80];
{
	wsprintfA(#hstr,"unable to open file '%s'",str);
	preerror(#hstr);
}

// ----
undefinederror()
byte holdstr[80];
{
	wsprintfA(#holdstr,"'%s' undefined",#string);
	preerror(holdstr);
}

// ----
unexpectedeof()
{
	preerror("unexpected END OF FILE");
}
// ----
warning(dword str)
{
	wsprintfA(#mapstr,"%s (%d)Warning> %s.\n",#currentfilename,linenumber,str);
	WRITESTR(#mapstr);
	IF(makemapfile)fprint(mapfile,#mapstr);
}
/*
void TestProc()
char buf[20];
{
	$pushad
	wsprintfA(#buf,"%08X\n",SSDWORD[EBP+4]);
	WRITESTR(#buf);
	$popad
} */
