// BKTurbo8.cpp: определяет точку входа для консольного приложения.
//

#include "stdafx.h"
#include "BKTurbo8.h"
#include "Reader.h"
#include "Globals.h"
#include "ErrorManager.h"
#include "BKToken.h"
#include "Assemble.h"
#include "LabelManager.h"
#include "Parser.h"
#include "Listing.h"
#include "Object.h"

#include <io.h>
#include <locale>
#include "getopt.h"
#pragma warning(disable:4996)

CReader *g_pReader = nullptr;
UMemory g_Memory;

constexpr auto EXT_OBJ = 0;
constexpr auto EXT_LST = 1;
constexpr auto EXT_BIN = 2;
constexpr auto EXT_RAW = 3;
static wstring strExt[] =
{
	L".obj",
	L".lst",
	L".bin",
	L".raw"
};

wstring strListingFilename, strObjectFileName, strTableFileName, strResultFile, strCommand;
CReader::FILE_CHARSET Charset = CReader::CHARSET_UNDEFINE;
int nStartAddress = -1;
bool bMakeListing = false;
bool bMakeObject = false;
bool bMakeTable = false;
bool bMakeRaw = false;

int wmain(int argc, wchar_t *argv[], wchar_t *envp[])
{
	_wsetlocale(LC_ALL, L"Russian");
	InitGlobalParameters(&g_GlobalParameters);
	wchar_t curDir[_MAX_PATH] = { 0 };
	_wgetcwd(curDir, _MAX_PATH);
	g_GlobalParameters.strAppPath = wstring(curDir);
	static struct option long_options[] =
	{
		{ L"help",        ARG_NONE, nullptr, L'?' },
		{ L"verbose",     ARG_NONE, nullptr, L'v' },
		{ L"raw",         ARG_NONE, nullptr, L'r' },
		{ L"input",       ARG_REQ,  nullptr, L'i' },
		{ L"listing",     ARG_OPT,  nullptr, L'l' },
		{ L"object",      ARG_OPT,  nullptr, L'o' },
		{ L"table",       ARG_OPT,  nullptr, L't' },
		{ L"address",     ARG_REQ,  nullptr, L's' },
		{ nullptr,        ARG_NULL, nullptr, ARG_NULL }
	};
	static wchar_t optstring[] = L"?vri:l::o::t::s:";
	bool bShowHelp = false;
	int option_index = 0;
	int c;

	while ((c = getopt_long(argc, argv, optstring, long_options, &option_index)) != -1)
	{
		// Handle options
		c = tolower(c);

		switch (c)
		{
			case L'?':
				bShowHelp = true;
				break;

			case L'v':
				g_GlobalParameters.bVerbosity = true;
				break;

			case L'i': //задаём кодировку входного файла
			{
				wstring strTmp = (optarg) ? wstring(optarg) : L"a";

				if (!strTmp.empty())
				{
					wchar_t wch = tolower(strTmp.at(0)); //оставим только первую букву

					switch (wch)
					{
						case L'k':
							Charset = CReader::CHARSET_KOI8;
							break;

						case L'o':
							Charset = CReader::CHARSET_CP866;
							break;

						case L'w':
							Charset = CReader::CHARSET_CP1251;
							break;

						case L'8':
							Charset = CReader::CHARSET_UTF8;
							break;

						case L'u':
							Charset = CReader::CHARSET_UNICODE;
							break;

						case L'a':
						default:
							Charset = CReader::CHARSET_UNDEFINE;
							break;
					}
				}
			}
			break;

			case L'r':
				bMakeRaw = true;
				break;

			case L'l':
				bMakeListing = true;

				if (optarg)
				{
					strListingFilename =  wstring(optarg);
				}

				break;

			case L'o':
				bMakeObject = true;

				if (optarg)
				{
					strObjectFileName = wstring(optarg);
				}

				break;

			case L't':
				bMakeTable = true;

				if (optarg)
				{
					strTableFileName = wstring(optarg);
				}

				break;

			case L's':
			{
				int nTmp = _tcstol(optarg, nullptr, 8) & 0xffff;

				if (0 <= nTmp && nTmp <= HIGH_BOUND)
				{
					nStartAddress = nTmp;
				}
			}
			break;
		}

		if (bShowHelp)
		{
			Usage();
			return 0;
		}
	}

	argc -= optind;
	argv += optind;

	if (argc < 1)
	{
		wprintf(L"Ошибка в командной строке: отсутствует команда.\n\n");
		Usage();
	}
	else
	{
		strCommand = wstring(*argv).substr(0, 2);
		strCommand[0] = toupper(strCommand[0]);
		strCommand[1] = toupper(strCommand[1]);
		argc--;
		argv++;

		if (argc < 1)
		{
			wprintf(L"Ошибка в командной строке: не хватает аргументов после команды.\n\n");
			Usage();
			return 0;
		}

		if (strCommand == L"LI")
		{
			strResultFile = wstring(*argv);
			argc--;
			argv++;

			if (argc < 1)
			{
				wprintf(L"Ошибка в командной строке: не хватает аргументов после команды LI.\n\n");
				Usage();
				return 0;
			}

			wprintf(L"Линковка...\nСоздаётся файл: %s\n", strResultFile.c_str());

			if (nStartAddress != -1) //если в командной строке был задан адрес загрузки
			{
				//то применим его
				SetStartAddress(nStartAddress);
				g_GlobalParameters.bLinkerFirst = false; //а адреса объектных модулей будем игнорировать
			}
		}
		else if (strCommand == L"CO")
		{
			if (nStartAddress != -1) //если в командной строке был задан адрес загрузки
			{
				//то применим его
				SetStartAddress(nStartAddress);
			}
		}

		struct _wfinddatai64_t f_file;

		intptr_t hFile;

		bool bRes = true;

		for (; argc > 0; argc--, argv++) //теперь, пока аргументы не кончатся - обрабатывать
		{
			if ((hFile = _wfindfirsti64(*argv, &f_file)) == -1L)
			{
				wprintf(L"Файл(ы) '%s' не найден(ы)\n", *argv);
			}
			else
			{
				do
				{
					if (!(f_file.attrib & _A_SUBDIR)) //если не поддиректория
					{
						if (strCommand == L"LI")
						{
							wprintf(L"Линкуется объектный файл: %s ", f_file.name);
							bRes = bRes && LoadObj(wstring(f_file.name));

							if (bRes)
							{
								wprintf(L"- OK\n");
								LabelLinkage();
							}
							else
							{
								wprintf(L"- Ошибка\n");
								break;
							}
						}
						else
						{
							workCycle(wstring(f_file.name));
							ReInitGlobalParameters(&g_GlobalParameters);
							//чистим все векторы.
							g_labelRefs.Clear();
							g_labelGlobalDefs.Clear();
							g_labelLocalDefs.Clear();
							g_Listing.clear();
						}
					}
					else
					{
						//если директория
						if ((wcscmp(f_file.name, L"..") == 0) || (wcscmp(f_file.name, L".") == 0))
						{
							continue;
						}
					}
				}
				while (_wfindnexti64(hFile, &f_file) == 0);

				_findclose(hFile);
			}

			if (!bRes)
			{
				break;
			}
		}

		if (strCommand == L"LI" && bRes)
		{
			wprintf(L"Адрес компоновки = %#6o\n", GetStartAddress());
			wprintf(L"Размер исполняемого файла = %#6o\n", g_GlobalParameters.nProgramLength);
			SaveFile(strResultFile, strExt[bMakeRaw ? EXT_RAW : EXT_BIN]);

			if (bMakeTable)
			{
				wchar_t pName[_MAX_FNAME] = { 0 };
				_wsplitpath_s((strTableFileName.empty() ? strResultFile : strTableFileName).c_str(), nullptr, 0, nullptr, 0, pName, _MAX_FNAME, nullptr, 0);
				wstring name = wstring(pName) + L"_tbl";
				MakeObj(name, strExt[EXT_OBJ], true);
			}

			if (bMakeObject)
			{
				MakeObj(strObjectFileName.empty() ? strResultFile : strObjectFileName, strExt[EXT_OBJ]);
			}

			if (bMakeListing)
			{
				MakeListing(strListingFilename.empty() ? strResultFile : strListingFilename, strExt[EXT_LST]);
			}

			PrintLabelTable(strListingFilename.empty() ? strResultFile : strListingFilename, strExt[EXT_LST]);
		}
	}

	return 0;
}

int workCycle(wstring &strInFileName)
{
	g_pReader = new CReader(strInFileName, Charset);
#ifdef DEBUG_LABEL_MANAGER
	DebugInit();
#endif

	if (g_pReader)
	{
		wprintf(L"Файл: %s\nАвтоопределение кодировки: ", strInFileName.c_str());

		switch (g_pReader->GetFileCharset())
		{
			case CReader::CHARSET_KOI8:
				wprintf(L"КОИ8-Р\n");
				break;

			case CReader::CHARSET_CP866:
				wprintf(L"OEM CP-866\n");
				break;

			case CReader::CHARSET_CP1251:
				wprintf(L"ANSI CP-1251\n");
				break;

			case CReader::CHARSET_UTF8:
				wprintf(L"UTF-8\n");
				break;

			case CReader::CHARSET_UNICODE:
				wprintf(L"UTF-16LE(Unicode)\n");
				break;

			case CReader::CHARSET_UNDEFINE:
				wprintf(L"Не определена. Возможно, это не текстовый файл.\n");
				return 1;

			default:
				wprintf(L"\rОшибка открытия файла.     \n");
				return 2;
		}

		if (strCommand == L"CO")
		{
			wprintf(L"Компиляция исполняемого файла...\n");
			//режим компиляции CO
			g_GlobalParameters.nModeCompone = 1;
		}
		else if (strCommand == L"CL")
		{
			wprintf(L"Компиляция объектного файла...\n");
			//режим компиляции CL
			g_GlobalParameters.nModeCompone = -1;
		}

		while (!g_GlobalParameters.bENDReached && !g_pReader->isEOF())
		{
			ParseLine();

			if (g_GlobalParameters.nPC >= HIGH_BOUND)
			{
				wprintf(L"Критическая ошибка:: Достигнут предел свободной памяти!\n");
				break;
			}
		}

		if (!g_GlobalParameters.bENDReached)
		{
			OutError(ERROR_121, false); //Отсутствует .END
		}

		int nErr = IsError();

		if (nErr)
		{
			//если были ошибки - одно действие
			wprintf(L"Количество ошибок: %d\n", IsError());
		}

		{
			//если не было - другое
			if (strCommand != L"CL" && !nErr)
			{
				wprintf(L"Адрес компоновки = %#6o\n", GetStartAddress());
				SaveFile(strInFileName, strExt[bMakeRaw ? EXT_RAW : EXT_BIN]);
			}

			if (!nErr)
			{
				wprintf(L"Размер исполняемого файла = %#6o\n", g_GlobalParameters.nProgramLength);
			}

			if (bMakeTable && !nErr)
			{
				wchar_t pName[_MAX_FNAME] = { 0 };
				_wsplitpath_s((strTableFileName.empty() ? strInFileName : strTableFileName).c_str(), nullptr, 0, nullptr, 0, pName, _MAX_FNAME, nullptr, 0);
				wstring name = wstring(pName) + L"_tbl";
				MakeObj(name, strExt[EXT_OBJ], true);
			}

			if (bMakeObject && !nErr)
			{
				MakeObj(strObjectFileName.empty() ? strInFileName : strObjectFileName, strExt[EXT_OBJ]);
			}
			else if (strCommand == L"CL" && !nErr)
			{
				MakeObj(strInFileName, strExt[EXT_OBJ]);
			}

			if (bMakeListing)
			{
				MakeListing(strListingFilename.empty() ? strInFileName : strListingFilename, strExt[EXT_LST]);
			}

			PrintLabelTable(strListingFilename.empty() ? strInFileName : strListingFilename, strExt[EXT_LST]);
		}

		delete g_pReader;
	}
	else
	{
		wprintf(L"Недостаточно памяти.\n");
	}

	return 0;
}


/*
Пропускаем остаток строки.
Выход: true - встретился конец файла
false - просто конец строки.
*/
bool SkipTailString(wchar_t &ch)
{
	do
	{
		ch = g_pReader->readChar();
	}
	while (!g_pReader->isEOF() && g_pReader->getCurrCharType() != CReader::CT_LN);

	if (g_pReader->isEOF())
	{
		return true;
	}

	return false;
}

/*
*Парсим строку, в строке может быть неограниченное число ассемблерных команд.
*Перед командой может быть неограниченное число меток.
*Конец файла тоже считается концом строки, затем надо делать конкретную проверку на именно конец файла.
g_pReader->m_nLineNum
*/
void ParseLine()
{
	CBKToken token;
	int R4 = g_GlobalParameters.nPC;    //текущий временный PC, который приращивается во время построения инструкции или псевдокоманды
	Lst_PrepareLine(R4);
	wchar_t ch = g_pReader->readChar(); //читаем первый символ

	for (;;) //крутимся в цикле, пока не достигнем конца строки
	{
		//делаем так, чтобы можно было обрабатывать несколько инструкций в одной строке
		g_GlobalParameters.bInString = false;
		g_GlobalParameters.nAriphmType = 0;
		g_GlobalParameters.nPC = R4; //обычно сюда попадаем перед началом очередной команды, поэтому сохраняем счётчик.
		bool bln = g_pReader->SkipWhitespaces(ch); //пропустим возможные пробелы

		if (bln) //если после чтения получился конец строки,
		{
			break;  // то выход из цикла.
		}

		//обрабатываем многострочный комментарий /*...*/
		//нормально парсится инлайн коммент, за которым может быть инструкция.
		//комментарий не божет быть внутри инструкции, т.к. такой парсинг не реализован
		if (ch == L'/' && g_pReader->getNextChar() == L'*')
		{
			bool bex = false;
			Lst_AddPrepareLine(0, LT_COMMENTARY);

			do
			{
				ch = g_pReader->readChar();

				if (g_pReader->getCurrCharType() == CReader::CT_LN)
				{
					Lst_PrepareLine(R4);
					Lst_AddPrepareLine(0, LT_COMMENTARY);
				}

				if (g_pReader->isEOF())
				{
					bex = true;
					break;
				}
			}
			while (!(ch == L'*' && g_pReader->getNextChar() == L'/'));

			if (bex)
			{
				break;
			}
			else
			{
				ch = g_pReader->readChar(true); //читаем *
				ch = g_pReader->readChar(true); //читаем /
				continue;
			}
		}

		//теперь обрабатываем строку
		if (ch == L';' || (ch == L'/' && g_pReader->getNextChar() == L'/')) //если комментарий ";" или "//"
		{
			SkipTailString(ch);
			Lst_AddPrepareLine(0, LT_COMMENTARY);
			break; //выходим из цикла, независимо от результата
		}
		else if (ch == L'.') //если псевдокоманда
		{
			//обработка псевдокоманды
			ch = g_pReader->readChar(); //читаем следующий за точкой символ.

			if (!g_pReader->readToken(&token, ch)) //и читаем лексемой
			{
				//если токен не прочёлся, значит сразу за точкой встретился посторонний символ.
				OutError(ERROR_103);
				SkipTailString(ch);
				break;
			}

			if (!PseudoCommandExec(&token, R4, ch))
			{
				//если встретилась ошибка, пропустим всю строку
				SkipTailString(ch);
			}

			Lst_AddPrepareLine2(R4);
		}
		else if (ch == L'$') //зарезервировано на будущее
		{
			OutError(ERROR_103); //Недопустимый символ в строке.
			SkipTailString(ch); //пропустим всю строку
			break;
		}
		else //дальше что-то, либо метка, либо присваивание, либо команда.
		{
			if (!g_pReader->readToken(&token, ch)) //считаем это лексемой
			{
				//если токен не прочёлся, значит сразу встретился посторонний символ.
				OutError(ERROR_103); //Недопустимый символ в строке.
				SkipTailString(ch);
				break;
			}

			//если сразу за лексемой идёт двоеточие
			if (ch == L':') //значит это метка.
			{
				ch = g_pReader->readChar(true);     //пропускаем ':' и пустоту за ним
				wchar_t wch = token.getName()[0];   //определяем тип метки.
				//если метка начинается с буквы - это глобальная метка
				//если с цифры - то это локальная метка.
				//остальные символы - любые допустимые.
				bool bl;

				if (g_pReader->AnalyseChar(wch) == CReader::CT_DIGITS)
				{
					//локальная метка
					bl = AddLocalLabel(&token, g_GlobalParameters.nPC, DEFINITE_LABEL);
				}
				else
				{
					//глобальная метка
					bl = AddGlobalLabel(&token, g_GlobalParameters.nPC, DEFINITE_LABEL);
				}

				Lst_AddPrepareLine(R4, LT_LABEL);

				if (bl) //если метка добавилась в таблицу
				{
					LabelLinkage(); //то линкуем
				}
				else
				{
					OutError(ERROR_116); //иначе ошибка - повторное определение метки
				}
			}
			else if (needChar(L'=', ch)) //если за лексемой, возможно через пробелы идёт '=',
			{
				//то это присваивание
				ch = g_pReader->readChar(true); //пропустим =
				g_GlobalParameters.nAriphmType = -1;
				int R1 = 0;
				Lst_AddPrepareLine(R4, LT_ASSIGN);

				if (AriphmParser(R4, R1, ch)) //если арифметическое выражение распарсилось
				{
					if (!g_labelGlobalDefs.AddLabel(&token, R1, CONSTANT_LABEL)) //добавляем метку со значением, без удаления локальных меток
					{
						OutError(ERROR_116); //если не добавилось - ошибка. уже есть такая метка
					}

					Lst_AddPrepareLine2(R1);
				}
				else
				{
					SkipTailString(ch);
					break;
				}
			}
			else
			{
				g_pReader->SkipWhitespaces(ch); //пропускаем пробелы - на всякий случай

				//обработка ассемблерной команды.
				if (!AssembleCPUInstruction(&token, R4, ch))
				{
					//assert(false);
					//если встретилась ошибка, пропустим всю строку
					SkipTailString(ch);
				}

				Lst_AddPrepareLine2(R4);
			}
		}
	}
}

void SaveFile(wstring &strName, wstring &strExt)
{
	wchar_t pDrive[_MAX_DIR] = { 0 };
	wchar_t pPath[_MAX_PATH] = { 0 };
	wchar_t pName[_MAX_FNAME] = { 0 };
	wchar_t pExt[_MAX_EXT] = { 0 };
	_wsplitpath_s(strName.c_str(), pDrive, _MAX_DIR, pPath, _MAX_PATH, pName, _MAX_FNAME, pExt, _MAX_EXT);
	//сделаем новое имя файла
	wstring outName = wstring(pDrive) + wstring(pPath) + wstring(pName) + strExt;
	FILE *of = _wfopen(outName.c_str(), L"wb");

	if (of)
	{
		if (!bMakeRaw)
		{
			uint16_t bin[2];
			bin[0] = GetStartAddress();
			bin[1] = g_GlobalParameters.nProgramLength;
			fwrite(bin, 1, 4, of);
		}

		fwrite(&g_Memory.b[BASE_ADDRESS], 1, g_GlobalParameters.nProgramLength, of);
		fclose(of);
	}
	else
	{
		wprintf(L"Ошибка создания файла %s\n", outName.c_str());
	}
}

//вывод на экран таблицы меток.
void PrintLabelTable(wstring &strName, wstring &strExt)
{
	wchar_t pDrive[_MAX_DIR] = { 0 };
	wchar_t pPath[_MAX_PATH] = { 0 };
	wchar_t pName[_MAX_FNAME] = { 0 };
	wchar_t pExt[_MAX_EXT] = { 0 };
	_wsplitpath_s(strName.c_str(), pDrive, _MAX_DIR, pPath, _MAX_PATH, pName, _MAX_FNAME, pExt, _MAX_EXT);
	//сделаем новое имя файла
	wstring outName = wstring(pDrive) + wstring(pPath) + wstring(pName) + strExt;
	FILE *of = nullptr;

	if (bMakeListing)
	{
		of = _wfopen(outName.c_str(), L"a+t");
	}

	//выведем таблицу глобальных меток.
	wstring wstr1 = L"\nГлобальные метки\n\n";

	if (g_GlobalParameters.bVerbosity)
	{
		wprintf(wstr1.c_str());
	}

	if (of)
	{
		size_t len = wstr1.length();
		auto *pbuf = new uint8_t[len + 1];

		if (pbuf)
		{
			UNICODEtoBK(wstr1, pbuf, len, false);
			pbuf[len] = 0;
			fprintf(of, "%s", pbuf);
			delete[] pbuf;
		}
	}

	size_t sz = g_labelGlobalDefs.getSize();
	size_t maxlen = 0;

	//сперва найдём самую длинную метку
	for (size_t i = 0; i < sz; ++i)
	{
		wstring name = g_labelGlobalDefs.GetLabel(i)->getName();

		if (name.length() > maxlen)
		{
			maxlen = name.length();
		}
	}
	//теперь сформируем строку формата
	static wchar_t buf[32] = { 0 };

	if (maxlen > 16)
	{
		maxlen = 16;
	}

	swprintf_s(buf, 32, L"%%-%ds = %%07o\0", int(maxlen));
	int n = 84 / (int(maxlen) + 12); //вот столько влазит в экран
	int j = n;

	for (size_t i = 0; i < sz; ++i)
	{
		wstring name = g_labelGlobalDefs.GetLabel(i)->getName();
		int value = g_labelGlobalDefs.GetValue(i);

		if ((g_labelGlobalDefs.GetType(i) & LABEL_DEFINITE_MASK) == DEFINITE_LABEL)
		{
			value = CorrectAddress(value);
		}

		if (name.length() > maxlen)
		{
			//если метка очень длинная, то её только одну в строке выведем.
			if (g_GlobalParameters.bVerbosity)
			{
				wprintf(L"%-s = %07o", name.c_str(), value);
			}

			if (of)
			{
				fwprintf(of, L"%-s = %07o", name.c_str(), value);
			}

			j = n;

			if (g_GlobalParameters.bVerbosity)
			{
				wprintf(L"\n");
			}

			if (of)
			{
				fwprintf(of, L"\n");
			}
		}
		else
		{
			if (g_GlobalParameters.bVerbosity)
			{
				wprintf(buf, name.c_str(), value);
			}

			if (of)
			{
				fwprintf(of, buf, name.c_str(), value);
			}

			if (--j <= 0)
			{
				j = n;

				if (g_GlobalParameters.bVerbosity)
				{
					wprintf(L"\n");
				}

				if (of)
				{
					fwprintf(of, L"\n");
				}
			}
			else
			{
				if (g_GlobalParameters.bVerbosity)
				{
					wprintf(L"  ");
				}

				if (of)
				{
					fwprintf(of, L"  ");
				}
			}
		}
	}

	//а теперь выведем оставшиеся ссылки на не найденные метки
	sz = g_labelRefs.getSize();

	if (sz)
	{
		wstr1 = L"\n\nСсылки на не определённые метки!\n\n";
		//if (g_GlobalParameters.bVerbosity)
		{
			wprintf(wstr1.c_str());
		}

		if (of)
		{
			size_t len = wstr1.length();
			auto *pbuf = new uint8_t[len + 1];

			if (pbuf)
			{
				UNICODEtoBK(wstr1, pbuf, len, false);
				pbuf[len] = 0;
				fprintf(of, "%s", pbuf);
				delete[] pbuf;
			}
		}

		//сперва найдём самую длинную метку
		for (size_t i = 0; i < sz; ++i)
		{
			wstring name = g_labelRefs.GetLabel(i)->getName();

			if (name.length() > maxlen)
			{
				maxlen = name.length();
			}
		}

		//теперь сформируем строку формата
		if (maxlen > 16)
		{
			maxlen = 16;
		}

		swprintf_s(buf, 32, L"%%07o : %%-%ds\0", int(maxlen));
		n = 84 / (int(maxlen) + 12); //вот столько влазит в экран
		j = n;

		for (size_t i = 0; i < sz; ++i)
		{
			wstring name = g_labelRefs.GetLabel(i)->getName();
			int value = g_labelRefs.GetValue(i);
			value = CorrectAddress(value);

			if (name.length() > maxlen)
			{
				//если метка очень длинная, то её только одну в строке выведем.
				//if (g_GlobalParameters.bVerbosity)
				{
					wprintf(L"%07o : %-s", value, name.c_str());
				}

				if (of)
				{
					fwprintf(of, L"%07o : %-s", value, name.c_str());
				}

				j = n;
				//if (g_GlobalParameters.bVerbosity)
				{
					wprintf(L"\n");
				}

				if (of)
				{
					fwprintf(of, L"\n");
				}
			}
			else
			{
				//if (g_GlobalParameters.bVerbosity)
				{
					wprintf(buf, value, name.c_str());
				}

				if (of)
				{
					fwprintf(of, buf, value, name.c_str());
				}

				if (--j <= 0)
				{
					j = n;
					//if (g_GlobalParameters.bVerbosity)
					{
						wprintf(L"\n");
					}

					if (of)
					{
						fwprintf(of, L"\n");
					}
				}
				else
				{
					//if (g_GlobalParameters.bVerbosity)
					{
						wprintf(L"  ");
					}

					if (of)
					{
						fwprintf(of, L"  ");
					}
				}
			}
		}
	}

	//if (g_GlobalParameters.bVerbosity)
	{
		wprintf(L"\n");
	}

	if (of)
	{
		fwprintf(of, L"\n"); fclose(of);
	}
}


void Usage()
{
	wprintf(L"Кросс ассемблер Turbo8 для БК0010-БК0011М\n" \
	        L"(с) 2014-2017 gid\n\n" \
	        L"Использование:\n" \
	        L"BKTurbo8 -? (--help)\n" \
	        L"  Вывод этой справки.\n\n" \
	        L"Имеется два режима работы. Режим компиляции, с созданием объектных модулей\n" \
	        L"и исполняемого файла. И режим линковки объектных модулей.\n\n" \
	        L"1. Режим компиляции.\n" \
	        L"BKTurbo8 [-i<c>][-v][-r][-l[name]][-o[name]][-s<0addr>] <cmd> <file_1 *[ file_n]>\n" \
	        L"  -i<c> (--input <c>) - задать кодировку исходного файла.\n" \
	        L"      Возможные кодировки:\n" \
	        L"        a - автоопределение (по умолчанию)\n" \
	        L"        k - KOI8-R\n" \
	        L"        o - OEM CP866\n" \
	        L"        w - ANSI CP1251\n" \
	        L"        8 - UTF8\n" \
	        L"        u - UNICODE UTF16LE\n" \
	        L"  Если автоопределение определило кодировку некорректно, необходимо задать\n" \
	        L"верную кодировку данным ключом.\n" \
	        L"  -v (--verbose) - вывод большего количества информации на экран.\n" \
	        L"      На данный момент дополнительно выводится таблица меток программы.\n\n" \
	        L"  -r (--raw) - создавать просто бинарный массив, не использовать формат .bin.\n\n" \
	        L"  -l[name] (--listing [name]) - генерировать lst Файл.\n" \
	        L"      Если имя файла задано, то используется оно для генерации листинга, если\n" \
	        L"      нет - то берётся имя файла исходного текста.\n\n" \
	        L"  -o[name] (--object [name]) - генерировать объектный файл.\n" \
	        L"      Если имя файла задано, то используется оно для генерации листинга, если\n" \
	        L"      нет - то берётся имя файла исходного текста.\n\n" \
	        L"  -t[name] (--table [name]) - создавать особый объектный файл, в котором\n" \
	        L"      содержатся только глобальные метки. (См. документацию)\n\n" \
	        L"  -s<0addr> (--address <0addr>) - задать начальный адрес компиляции.\n" \
	        L"      Адрес задаётся в восьмеричном виде.\n\n" \
	        L"  <cmd> - команда компиляции:\n" \
	        L"      CO - полная компиляция. В результате при отсутствии ошибок создаётся\n" \
	        L"           бинарный исполняемый файл и опционально создаются объектные файлы,\n" \
	        L"           заданные соответствующими ключами.\n" \
	        L"      CL - компиляция в объектный файл для дальнейшей линковки с другими\n" \
	        L"           объектными файлами. В результате при отсутствии ошибок всегда\n" \
	        L"           создаётся объектный файл. Бинарный файл не создаётся.\n" \
	        L"      Файл листинга создаётся в любом случае. При наличии ошибок код ошибки\n" \
	        L"      и его текстовое пояснение помещаются перед строкой листинга, вызвавшей\n" \
	        L"      ошибку.\n" \
	        L"      В конец файла листинга записывается таблица глобальных меток, а также\n" \
	        L"      список ссылок на неопределённые метки, если они есть.\n\n" \
	        L"  <file_1 *[ file_n]> - список исходных файлов, перечисленных через пробел.\n" \
	        L"      Допускаются маски файлов.\n\n" \
	        L"2. Режим линковки.\n" \
	        L"BKTurbo8 [-v][-r][-l[name]][-o[name]][-s<0addr>] LI <outfile> <file_1 *[ file_n]>\n" \
	        L"  Ключ -i не используется.\n\n" \
	        L"  Ключи -v, -r, -l, -o, -t и -s имеют тот же смысл, что и в режиме компиляции.\n\n" \
	        L"  Команда линковки - LI, за командой следует обязательное имя выходного файла\n" \
	        L"  <outfile>, маска файла не допускается. А затем список файлов объектных\n" \
	        L"  модулей.\n" \
	        L"  Листинг при этом не создаётся, потому что не из чего, но если задан ключ -l,\n" \
	        L"  в файл листинга сохраняется список меток, а так же список ссылок на \n" \
	        L"  неопределённые метки, если они есть.\n\n" \
	        L"  <file_1 *[ file_n]> - список файлов объектных модулей, перечисленных через\n" \
	        L"      пробел. Допускаются маски файлов.\n\n");
}

