#include "stdafx.h"

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

CPUCommandStruct g_pCPUCommands[] =
{
	{ L"HALT",   0000000,    CPUCG_NOOPS }, //op
	{ L"WAIT",   0000001,    CPUCG_NOOPS },
	{ L"RTI",    0000002,    CPUCG_NOOPS },
	{ L"BPT",    0000003,    CPUCG_NOOPS },
	{ L"IOT",    0000004,    CPUCG_NOOPS },
	{ L"RESET",  0000005,    CPUCG_NOOPS },
	{ L"RTT",    0000006,    CPUCG_NOOPS },
	{ L"NOP",    0000240,    CPUCG_NOOPS },
	{ L"CLC",    0000241,    CPUCG_NOOPS },
	{ L"CLV",    0000242,    CPUCG_NOOPS },
	{ L"CLVC",   0000243,    CPUCG_NOOPS },
	{ L"CLZ",    0000244,    CPUCG_NOOPS },
	{ L"CLZC",   0000245,    CPUCG_NOOPS },
	{ L"CLZV",   0000246,    CPUCG_NOOPS },
	{ L"CLZVC",  0000247,    CPUCG_NOOPS },
	{ L"CLN",    0000250,    CPUCG_NOOPS },
	{ L"CLNC",   0000251,    CPUCG_NOOPS },
	{ L"CLNV",   0000252,    CPUCG_NOOPS },
	{ L"CLNVC",  0000253,    CPUCG_NOOPS },
	{ L"CLNZ",   0000254,    CPUCG_NOOPS },
	{ L"CLNZC",  0000255,    CPUCG_NOOPS },
	{ L"CLNZV",  0000256,    CPUCG_NOOPS },
	{ L"CCC",    0000257,    CPUCG_NOOPS },
	{ L"SEC",    0000261,    CPUCG_NOOPS },
	{ L"SEV",    0000262,    CPUCG_NOOPS },
	{ L"SEVC",   0000263,    CPUCG_NOOPS },
	{ L"SEZ",    0000264,    CPUCG_NOOPS },
	{ L"SEZC",   0000265,    CPUCG_NOOPS },
	{ L"SEZV",   0000266,    CPUCG_NOOPS },
	{ L"SEZVC",  0000267,    CPUCG_NOOPS },
	{ L"SEN",    0000270,    CPUCG_NOOPS },
	{ L"SENC",   0000271,    CPUCG_NOOPS },
	{ L"SENV",   0000272,    CPUCG_NOOPS },
	{ L"SENVC",  0000273,    CPUCG_NOOPS },
	{ L"SENZ",   0000274,    CPUCG_NOOPS },
	{ L"SENZC",  0000275,    CPUCG_NOOPS },
	{ L"SENZV",  0000276,    CPUCG_NOOPS },
	{ L"SCC",    0000277,    CPUCG_NOOPS },
	{ L"RET",    0000207,    CPUCG_NOOPS },
	{ L"RETURN", 0000207,    CPUCG_NOOPS },

	{ L"BR",     0000400,    CPUCG_CBRANCH }, //op lll
	{ L"BNE",    0001000,    CPUCG_CBRANCH },
	{ L"BEQ",    0001400,    CPUCG_CBRANCH },
	{ L"BGE",    0002000,    CPUCG_CBRANCH },
	{ L"BLT",    0002400,    CPUCG_CBRANCH },
	{ L"BGT",    0003000,    CPUCG_CBRANCH },
	{ L"BLE",    0003400,    CPUCG_CBRANCH },
	{ L"BPL",    0100000,    CPUCG_CBRANCH },
	{ L"BMI",    0100400,    CPUCG_CBRANCH },
	{ L"BHI",    0101000,    CPUCG_CBRANCH },
	{ L"BLOS",   0101400,    CPUCG_CBRANCH },
	{ L"BVC",    0102000,    CPUCG_CBRANCH },
	{ L"BVS",    0102400,    CPUCG_CBRANCH },
	{ L"BCC",    0103000,    CPUCG_CBRANCH },
	{ L"BCS",    0103400,    CPUCG_CBRANCH },
	{ L"BHIS",   0103000,    CPUCG_CBRANCH },
	{ L"BLO",    0103400,    CPUCG_CBRANCH },

	{ L"MUL",    0070000,    CPUCG_EIS }, //op ss,r
	{ L"DIV",    0071000,    CPUCG_EIS },
	{ L"ASH",    0072000,    CPUCG_EIS },
	{ L"ASHC",   0073000,    CPUCG_EIS },

	{ L"EMT",    0104000,    CPUCG_TRAP }, //op nnn
	{ L"TRAP",   0104400,    CPUCG_TRAP },

	{ L"SOB",    0077000,    CPUCG_SOB }, //op r,ll

	{ L"MARK",   0006400,    CPUCG_MARK }, //op nn

	{ L"JSR",    0004000,    CPUCG_2OPREG }, //op r,dd
	{ L"XOR",    0074000,    CPUCG_2OPREG },

	{ L"FADD",   0075000,    CPUCG_FIS }, //op r
	{ L"FSUB",   0075010,    CPUCG_FIS },
	{ L"FMUL",   0075020,    CPUCG_FIS },
	{ L"FDIV",   0075030,    CPUCG_FIS },
	{ L"RTS",    0000200,    CPUCG_FIS },

	{ L"PUSH",   0010046,    CPUCG_PUSH }, //op ss
	{ L"PUSHB",  0110046,    CPUCG_PUSH },

	{ L"JMP",    0000100,    CPUCG_1OPS }, //op dd
	{ L"SWAB",   0000300,    CPUCG_1OPS },
	{ L"MTPS",   0106400,    CPUCG_1OPS },
	{ L"MFPS",   0106700,    CPUCG_1OPS },
	{ L"MTPD",   0106600,    CPUCG_1OPS },
	{ L"MFPD",   0106500,    CPUCG_1OPS },
	{ L"MTPI",   0006600,    CPUCG_1OPS },
	{ L"MFPI",   0006500,    CPUCG_1OPS },
	{ L"SXT",    0006700,    CPUCG_1OPS },
	{ L"CALL",   0004700,    CPUCG_1OPS },
	{ L"POP",    0012600,    CPUCG_1OPS },
	{ L"POPB",   0112600,    CPUCG_1OPS },
	{ L"CLR",    0005000,    CPUCG_1OPS },
	{ L"COM",    0005100,    CPUCG_1OPS },
	{ L"INC",    0005200,    CPUCG_1OPS },
	{ L"DEC",    0005300,    CPUCG_1OPS },
	{ L"NEG",    0005400,    CPUCG_1OPS },
	{ L"ADC",    0005500,    CPUCG_1OPS },
	{ L"SBC",    0005600,    CPUCG_1OPS },
	{ L"TST",    0005700,    CPUCG_1OPS },
	{ L"ROR",    0006000,    CPUCG_1OPS },
	{ L"ROL",    0006100,    CPUCG_1OPS },
	{ L"ASR",    0006200,    CPUCG_1OPS },
	{ L"ASL",    0006300,    CPUCG_1OPS },
	{ L"CLRB",   0105000,    CPUCG_1OPS },
	{ L"COMB",   0105100,    CPUCG_1OPS },
	{ L"INCB",   0105200,    CPUCG_1OPS },
	{ L"DECB",   0105300,    CPUCG_1OPS },
	{ L"NEGB",   0105400,    CPUCG_1OPS },
	{ L"ADCB",   0105500,    CPUCG_1OPS },
	{ L"SBCB",   0105600,    CPUCG_1OPS },
	{ L"TSTB",   0105700,    CPUCG_1OPS },
	{ L"RORB",   0106000,    CPUCG_1OPS },
	{ L"ROLB",   0106100,    CPUCG_1OPS },
	{ L"ASRB",   0106200,    CPUCG_1OPS },
	{ L"ASLB",   0106300,    CPUCG_1OPS },

	{ L"MOV",    0010000,    CPUCG_2OPS }, //op ss, dd
	{ L"CMP",    0020000,    CPUCG_2OPS },
	{ L"BIT",    0030000,    CPUCG_2OPS },
	{ L"BIC",    0040000,    CPUCG_2OPS },
	{ L"BIS",    0050000,    CPUCG_2OPS },
	{ L"MOVB",   0110000,    CPUCG_2OPS },
	{ L"CMPB",   0120000,    CPUCG_2OPS },
	{ L"BITB",   0130000,    CPUCG_2OPS },
	{ L"BICB",   0140000,    CPUCG_2OPS },
	{ L"BISB",   0150000,    CPUCG_2OPS },
	{ L"ADD",    0060000,    CPUCG_2OPS },
	{ L"SUB",    0160000,    CPUCG_2OPS },

	{ L"",       0,          CPUCG_NOOPS } //конец массива.
};

PseudoCommandStruct PseudoCommands[] =
{
	{ L"ADDR",   true,   PSC_addr },
	{ L"LA",     true,   PSC_la },
	{ L"PRINT",  true,   PSC_print },
	{ L"BLKW",   true,   PSC_blkw },
	{ L"WORD",   true,   PSC_word },
	{ L"RAD50",  true,   PSC_rad50 },
	{ L"END",    false,  PSC_end },
	{ L"EVEN",   false,  PSC_even },
	{ L"BLKB",   false,  PSC_blkb },
	{ L"BYTE",   false,  PSC_byte },
	{ L"ASCII",  false,  PSC_ascii },
	{ L"ASCIZ",  false,  PSC_asciz },
	{ L"ORG",    false,  PSC_org },
	{ L"FLT2",   true,   PSC_flt2 },
	{ L"FLT4",   true,   PSC_flt4 },
	{ L"INCLUDE", false,  PSC_Include },
	{ L"",       false,  nullptr }
};

//на входе: token - текущая прочитанная инструкция, или по крайней мере что-то похожее.
//R4 - текущий временный PC
//на выходе должен указывать на слово за командой.
//выход: true - нормально
//		false - ошибка
bool AssembleCPUInstruction(CBKToken *token, int &R4, wchar_t &ch)
{
	if (R4 & 1)
	{
		OutError(ERROR_120); //Нечётный адрес команды.
		g_Memory.b[R4++] = 0; //выровняем и будем компилировать дальше.
	}

	g_GlobalParameters.bOperandType = false;
	g_GlobalParameters.nAriphmType = 1;
	int i = 0;

	while (!g_pCPUCommands[i].strName.empty())
	{
		if (token->calcHash(g_pCPUCommands[i].strName) == token->getHash())
		{
#ifdef _DEBUG

			if (g_pCPUCommands[i].strName != token->getName())
			{
				wprintf(L"HASH Error: %s:%#zX ~~ %s:%#zX\n", g_pCPUCommands[i].strName.c_str(), token->calcHash(g_pCPUCommands[i].strName), token->getName().c_str(), token->getHash());
				assert(false);
			}

#endif
			//если нашли нужную мнемонику
			//нужно взять опкод.
			g_Memory.w[R4 / 2] = g_pCPUCommands[i].nOpcode;
			g_Memory.w[R4 / 2 + 1] = 0;
			g_Memory.w[R4 / 2 + 2] = 0;
			bool bRet = false;
			Lst_AddPrepareLine(R4, LT_INSTRUCTION);

			// и обработать операнды в соответствии с типом группы, к которой принадлежит опкод.
			switch (g_pCPUCommands[i].nGroup)
			{
				case CPUCG_NOOPS: //ничего делать не надо.
					bRet = true;
					break;

				case CPUCG_CBRANCH:
					bRet = assembleBR(R4, ch);
					break;

				case CPUCG_EIS:
					bRet = assemble2ROP(R4, ch);
					break;

				case CPUCG_TRAP:
					bRet = assembleTRAP(R4, ch);
					break;

				case CPUCG_SOB:
					bRet = assembleSOB(R4, ch);
					break;

				case CPUCG_MARK:
					bRet = assembleMARK(R4, ch);
					break;

				case CPUCG_2OPREG:
					bRet = assemble2OPR(R4, ch);
					break;

				case CPUCG_FIS:
					bRet = assemble1OPR(R4, ch);
					break;

				case CPUCG_PUSH:
					g_GlobalParameters.bOperandType = true;

				//переходим к выполнению CPUCG_1OPS
				case CPUCG_1OPS:
					bRet = assemble1OP(R4, ch);
					break;

				case CPUCG_2OPS:
					bRet = assemble2OP(R4, ch);
					break;
			}

			R4 += 2;
			return bRet;
		}

		i++;
	}

	//ошибка - неопознанная команда.
	OutError(ERROR_107); //Неправильная команда.
	return false;
}

bool assemble2OP(int &R4, wchar_t &ch)
{
	g_GlobalParameters.bOperandType = true; //сперва ss

	if (!assemble1OP(R4, ch))
	{
		return false;
	}

	if (!needChar(L',', ch))
	{
		OutError(ERROR_128); //Ошибка в команде - нет второго операнда.
		return false;
	}

	g_GlobalParameters.bOperandType = false; //теперь dd
	ch = g_pReader->readChar(true);
	return assemble1OP(R4, ch);
}

bool assemble1OP(int &R4, wchar_t &ch)
{
	R4 += 2;
	return Operand_analyse(R4, ch);
}

bool assemble1OPR(int &R4, wchar_t &ch)
{
	g_pReader->SkipWhitespaces(ch);

	if (ReadRegName(ch))
	{
		return true;
	}

	OutError(ERROR_113); //Ошибка в имени регистра.
	return false;
}

bool assemble2OPR(int &R4, wchar_t &ch)
{
	g_GlobalParameters.bOperandType = true; //сперва ss

	if (!assemble1OPR(R4, ch))
	{
		return false;
	}

	if (!needChar(L',', ch))
	{
		OutError(ERROR_128); //Ошибка в команде - нет второго операнда.
		return false;
	}

	g_GlobalParameters.bOperandType = false; //теперь dd
	ch = g_pReader->readChar(true);
	return assemble1OP(R4, ch);
}

bool assemble2ROP(int &R4, wchar_t &ch)
{
	g_GlobalParameters.bOperandType = false;

	if (!assemble1OP(R4, ch))
	{
		return false;
	}

	if (!needChar(L',', ch))
	{
		OutError(ERROR_128); //Ошибка в команде - нет второго операнда.
		return false;
	}

	g_GlobalParameters.bOperandType = true;
	ch = g_pReader->readChar(true);
	return assemble1OPR(R4, ch);
}

bool assembleBR(int &R4, wchar_t &ch)
{
	int R1 = 0, R3 = 0;
	CBKToken token;
	bool bRet = false;
#ifdef DEBUG_LABEL_MANAGER
	///-----------------------debug---------------------------
	fwprintf(dbgF, L"Assemble branch %06o\n", R4);
	///-----------------------debug---------------------------
#endif

	if (trp202(&token, R1, ch))
	{
#ifdef DEBUG_LABEL_MANAGER
		///-----------------------debug---------------------------
		fwprintf(dbgF, L"\tbranch label found '%s'.\n", token.getName().c_str());
		///-----------------------debug---------------------------
#endif
		R3 = R1;
		bRet = trp242(R4, R1, ch);
		R1 += R3;
	}
	else
	{
#ifdef DEBUG_LABEL_MANAGER
		///-----------------------debug---------------------------
		fwprintf(dbgF, L"\tbranch label not found '%s'.\n", token.getName().c_str());
		///-----------------------debug---------------------------
#endif
		bRet = trp240(&token, R4, R1, ch, BRANCH_LABEL);
		R1 += R4;
		R1 += 2;
	}

#ifdef DEBUG_LABEL_MANAGER
	///-----------------------debug---------------------------
	fwprintf(dbgF, L"\tCommand Address %06o, Target Address: %06o\n", R4, R1);
	///-----------------------debug---------------------------
#endif

	if (!bRet)
	{
		return false;
	}

	if (BranchVerify(R4, R1))
	{
#ifdef DEBUG_LABEL_MANAGER
		///-----------------------debug---------------------------
		fwprintf(dbgF, L"\tbranch offset %06o\n", R1);
		///-----------------------debug---------------------------
#endif
		g_Memory.w[R4 / 2] |= uint16_t(R1 & 0xff);
		return true;
	}
	else
	{
#ifdef DEBUG_LABEL_MANAGER
		///-----------------------debug---------------------------
		fwprintf(dbgF, L"\tbranch offset %06o\n", R1);
		///-----------------------debug---------------------------
#endif
		OutError(ERROR_110); //Ошибка длины перехода по оператору ветвления.
		return false;
	}
}

bool assembleSOB(int &R4, wchar_t &ch)
{
	g_GlobalParameters.bOperandType = true;

	if (!assemble1OPR(R4, ch))
	{
		return false;
	}

	if (!needChar(L',', ch))
	{
		OutError(ERROR_124); //Ошибка в команде.
		return false;
	}

	g_GlobalParameters.bOperandType = false;
	ch = g_pReader->readChar(true);
	g_pReader->SkipWhitespaces(ch);
	int R1 = 0;
	CBKToken token;

	if (trp202(&token, R1, ch))
	{
		int R3 = R1;
		trp242(R4, R1, ch);
		R3 += R1 - R4 - 2;

		if (R3 < 0)
		{
			R3 = -R3;
			R3 /= 2;

			if (R3 <= 077)
			{
				g_Memory.w[R4 / 2] |= R3;
				return true;
			}
		}
	}

	OutError(ERROR_102); //Ошибка длины или направления перехода в команде SOB.
	return false;
}

bool assembleTRAP(int &R4, wchar_t &ch)
{
	int nOp = g_Memory.w[R4 / 2];
	int R1 = 0;
	g_GlobalParameters.nAriphmType = -1;

	if (AriphmParser(R4, R1, ch))
	{
		if (R1 < 0377)
		{
			g_Memory.w[R4 / 2] |= R1;
			return true;
		}
	}

	OutError((nOp == 0104400) ? ERROR_125 : ERROR_126); //Ошибка аргумента TRAP, EMT.
	return false;
}

bool assembleMARK(int &R4, wchar_t &ch)
{
	int R1 = 0;
	g_GlobalParameters.nAriphmType = -1;

	if (AriphmParser(R4, R1, ch))
	{
		if (R1 < 077)
		{
			g_Memory.w[R4 / 2] |= R1;
			return true;
		}
	}

	OutError(ERROR_112); //Ошибка аргумента MARK, TRAP, EMT.
	return false;
}


//на входе: token - текущая прочитанная псевдокоманда, или по крайней мере что-то похожее.
//R4 - текущий временный PC
//на выходе должен указывать на слово за командой.
//выход: true - нормально
//		false - ошибка
bool PseudoCommandExec(CBKToken *token, int &R4, wchar_t &ch)
{
	g_GlobalParameters.bOperandType = false;
	int i = 0;

	while (!PseudoCommands[i].strName.empty())
	{
		if (token->calcHash(PseudoCommands[i].strName) == token->getHash()) //если нашли подходящую псевдокоманду
		{
#ifdef _DEBUG

			if (PseudoCommands[i].strName != token->getName())
			{
				wprintf(L"HASH Error: %s:%#zX ~~ %s:%#zX\n", PseudoCommands[i].strName.c_str(), token->calcHash(PseudoCommands[i].strName), token->getName().c_str(), token->getHash());
				assert(false);
			}

#endif

			//если команда допускает только чётные адреса
			if (PseudoCommands[i].bEvenAddr)
			{
				//нужна доп проверка на чётность адреса
				if (R4 & 1)
				{
					OutError(ERROR_120); //Нечётный адрес команды.
					g_Memory.b[R4++] = 0;
					return false;
				}
			}

			g_pReader->SkipWhitespaces(ch);
			g_Memory.w[R4 / 2 + 1] = 0;
			g_Memory.w[R4 / 2 + 2] = 0;
			return PseudoCommands[i].PSCFunction(R4, ch);
		}

		i++;
	}

	OutError(ERROR_106); //Неправильная псевдокоманда.
	return false;
}


bool PSC_addr(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_ADDR);
	g_Memory.w[R4 / 2] = 0;

	if (!ReadRegName(ch))
	{
		OutError(ERROR_113); //Ошибка в имени регистра.
		return false;
	}

	uint16_t t = g_Memory.w[R4 / 2]; R4 += 2; //t - номер регистра
	g_Memory.w[R4 / 2] |= 062700 | t; //формируем 6270r
	g_Memory.w[R4 / 2 - 1] |= 010700; R4 += 2; //формируем 1070r

	if (needChar(L',', ch))
	{
		ch = g_pReader->readChar(true);
		g_pReader->SkipWhitespaces(ch);
		CBKToken token;

		if (g_pReader->readToken(&token, ch))
		{
			int R1;
			bool bret = trp240(&token, R4, R1, ch, OFFSET_LABEL);
			R1 += 4;
			g_Memory.w[R4 / 2] |= R1; R4 += 2; //формируем смещение
			return bret;
		}
	}

	OutError(ERROR_128); //Ошибка в псевдокоманде.
	return false;
}

bool PSC_la(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_LA);

	if (R4 == BASE_ADDRESS)
	{
		int R1 = 0;

		if (AriphmParser(R4, R1, ch))
		{
			SetStartAddress(R1);
			return true;
		}
		else
		{
			return false;
		}
	}

	OutError(ERROR_101); //Псевдокоманда .LA должна быть первой в тексте.
	return false;
}

bool PSC_print(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_PRINT);

	if (ch == L'#')
	{
		ch = g_pReader->readChar();

		if (assemble1OP(R4, ch))
		{
			g_Memory.w[R4 / 2 - 1] = 012701; R4 += 2;
			g_Memory.w[R4 / 2] = 05002; R4 += 2;
			g_Memory.w[R4 / 2] = 0104020; R4 += 2;
			return true;
		}
	}

	OutError(ERROR_114); //Ошибка в псевдокоманде.
	return false;
}

bool PSC_blkw(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_BLKW);
	int R1;
	g_GlobalParameters.nAriphmType = -1;

	if (AriphmParser(R4, R1, ch))
	{
		R1 *= 2;

		if (R4 + R1 >= HIGH_BOUND)
		{
			OutError(ERROR_118); //Ошибка в псевдокоманде.
			return false;
		}

		for (int i = 0; i < R1; ++i)
		{
			g_Memory.b[R4++] = 0;
		}

		return true;
	}

	OutError(ERROR_114); //Ошибка в псевдокоманде.
	return false;
}

bool PSC_blkb(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_BLKB);
	int R1;
	g_GlobalParameters.nAriphmType = -1;

	if (AriphmParser(R4, R1, ch))
	{
		if (R4 + R1 >= HIGH_BOUND)
		{
			OutError(ERROR_117); //Ошибка в псевдокоманде.
			return false;
		}

		for (int i = 0; i < R1; ++i)
		{
			g_Memory.b[R4++] = 0;
		}

		return true;
	}

	OutError(ERROR_114); //Ошибка в псевдокоманде.
	return false;
}
//.ORG n - выравнивание по адресу.
bool PSC_org(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_LA);
	int R1 = 0;
	g_GlobalParameters.nAriphmType = -1;

	if (AriphmParser(R4, R1, ch)) //получим аргумент
	{
		R1 = CorrectOffset(R1);

		if (R1 >= HIGH_BOUND)
		{
			OutError(ERROR_119); //Ошибка в псевдокоманде.
			return false;
		}

		while (R4 < R1)         //выравниваем адрес до аргумента
		{
			g_Memory.b[R4++] = 0;
		}

		return true;
	}

	OutError(ERROR_114); //Ошибка в псевдокоманде.
	return false;
}

bool PSC_flt2(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_FLT2);
	uint16_t flt[2];

	for (;;)
	{
		if (parse_float(ch, 2, flt))
		{
			g_Memory.w[R4 / 2] = flt[0]; R4 += 2;
			g_Memory.w[R4 / 2] = flt[1]; R4 += 2;
		}
		else
		{
			OutError(ERROR_131);//ошибка парсинга flt2
			return false;
		}

		if (!needChar(L',', ch))
		{
			break;
		}

		ch = g_pReader->readChar(true);
	}

	return true;
}

bool PSC_flt4(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_FLT4);
	uint16_t flt[4];

	for (;;)
	{
		if (parse_float(ch, 4, flt))
		{
			g_Memory.w[R4 / 2] = flt[0]; R4 += 2;
			g_Memory.w[R4 / 2] = flt[1]; R4 += 2;
			g_Memory.w[R4 / 2] = flt[2]; R4 += 2;
			g_Memory.w[R4 / 2] = flt[3]; R4 += 2;
		}
		else
		{
			OutError(ERROR_131);//ошибка парсинга flt4
			return false;
		}

		if (!needChar(L',', ch))
		{
			break;
		}

		ch = g_pReader->readChar(true);
	}

	return true;
}

bool PSC_word(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_WORD);
	g_GlobalParameters.nAriphmType = 1;
	int R1;

	for (;;)
	{
		if (ch == L'@')
		{
			CBKToken label;
			ch = g_pReader->readChar(); //пропустим "@"

			if (g_pReader->readToken(&label, ch))
			{
				if (!trp240(&label, R4, R1, ch, OFFSET_LABEL))
				{
					return false;
				}

				R1 += 2;
			}
			else
			{
				OutError(ERROR_114); //Ошибка в псевдокоманде.
				return false;
			}
		}
		else if (!AriphmParser(R4, R1, ch))
		{
			g_Memory.w[R4 / 2] = 0; R4 += 2;
			return false;
		}

		g_Memory.w[R4 / 2] = R1; R4 += 2;

		if (!needChar(L',', ch))
		{
			break;
		}

		ch = g_pReader->readChar(true);
	}

	return true;
}

bool PSC_byte(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_BYTE);
	int R1;
	g_GlobalParameters.nAriphmType = -1;

	for (;;)
	{
		if (!AriphmParser(R4, R1, ch))
		{
			g_Memory.b[R4++] = 0;
			return false;
		}

		if (R1 > 0377)
		{
			OutError(ERROR_129); //Ошибка или отсутствие числового аргумента.
			break;
		}

		g_Memory.b[R4++] = uint8_t(R1 & 0xff);

		if (!needChar(L',', ch))
		{
			break;
		}

		ch = g_pReader->readChar(true);
	}

	return true;
}

bool PSC_end(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_END);
	g_GlobalParameters.bENDReached = true;
	g_GlobalParameters.nProgramLength = R4 - BASE_ADDRESS;

	if (g_GlobalParameters.nModeCompone >= 0)
	{
		g_GlobalParameters.nModeCompone = 0;
	}

	LabelLinking();
	g_labelLocalDefs.Clear();
	return true;
}

bool PSC_even(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_EVEN);

	if (R4 & 1)
	{
		g_Memory.b[R4++] = 0;
	}

	return true;
}

bool StrQuotes(wchar_t ch)
{
//  if (ch == L'\'' || ch == L'"' || ch == L'/' || ch == L'\\')
//  {
//      return true;
//  }
	//первым квотируемым символом считаем любой символ пунктуации и т.п., забыл как группа этих символов называется
	//вообще, по правилам MACRO, квотируемым считается вообще любой символ, кроме <, но это уж как-то слишком
	//усложняет понимание вида текстовой строки, поэтому ограничим себя
	if ((L'!' <= ch && ch <= L'/') || (ch == L':' || ch == L'=' || ch == L'?' || ch == L'@') || (0133 <= ch && ch <= 137) || (0173 <= ch && ch <= 177))
	{
		return true;
	}

	return false;
}

bool PSC_rad50(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_RAD50);
	uint16_t w; //текущее слово в radix50
	int nCnt; //счётчик символов в слове

	for (;;)
	{
		w = 0; //текущее слово в radix50
		nCnt = 0; //счётчик символов в слове

		if (StrQuotes(ch))
		{
			wchar_t chQuoter = ch;
			g_GlobalParameters.bInString = true;

			for (;;)
			{
				ch = g_pReader->readChar(); //берём очередной символ

				if (g_pReader->getCurrCharType() == CReader::CT_LN)
				{
					OutError(ERROR_130); //Ошибка в псевдокоманде.
					return false;
				}

				if (ch == chQuoter) //строка закончилась
				{
					if (nCnt) //если что-то было ещё принято
					{
						for (int i = 0; i < 3 - nCnt; ++i) //сдвинем на нужное количество позиций
						{
							w *= 050;
						}

						g_Memory.w[R4 / 2] = w; //и сохраним
						R4 += 2;
						nCnt = 0;
						w = 0;
					}

					ch = g_pReader->readChar();
					break;
				}
				//теперь поищем символ в таблице
				uint16_t nChCode = 0;   //код текущего символа по умолчанию, если символ не будет найден в таблице, будет значение по умолчанию
				ch = toupper(ch);

				for (int n = 0; n < 050; ++n)
				{
					if (RADIX50[n] == ch)
					{
						nChCode = n;    //нашли
						break;
					}
				}

				w = w * 050 + nChCode; //упаковываем очередной символ

				if (++nCnt >= 3)    //если пора сохранять полное слово
				{
					nCnt = 0;
					g_Memory.w[R4 / 2] = w; //сохраним
					R4 += 2;
					w = 0;
				}
			}

			g_GlobalParameters.bInString = false;
		}
		else if (g_pReader->getCurrCharType() == CReader::CT_SPACES)
		{
			if (g_pReader->SkipWhitespaces(ch))
			{
				break;
			}
		}
		else if (CheckComment(ch))  //если комментарий - то выходим, его тут не обработать
		{
			break;
		}
		else if (g_pReader->getCurrCharType() == CReader::CT_LN)
		{
			break;
		}
		else
		{
			OutError(ERROR_114); //Ошибка в псевдокоманде.
			return false;
		}
	}

	return true;
}

bool PSC_ascii(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(R4, LT_PSC_ASCII);
	g_GlobalParameters.nAriphmType = -1;

	for (;;)
	{
		if (StrQuotes(ch)) //если квотируемый символ, считаем что это кавычка
		{
			//и принимаем строку
			wchar_t chQuoter = ch;
			g_GlobalParameters.bInString = true;

			for (;;)
			{
				ch = g_pReader->readChar();

				if (g_pReader->getCurrCharType() == CReader::CT_LN)
				{
					OutError(ERROR_130); //Ошибка в псевдокоманде - обрыв строки.
					return false;
				}

				if (ch == chQuoter)
				{
					ch = g_pReader->readChar();
					break;
				}

				g_Memory.b[R4++] = UNICODEtoBK_ch(ch);
			}

			g_GlobalParameters.bInString = false;
		}
		else if (ch == L'<') //если левая скобка, то принимаем число
		{
			ch = g_pReader->readChar();
			int R1 = 0;

			if (AriphmParser(R4, R1, ch))
			{
				if (R1 < 0377)
				{
					if (ch == L'>')
					{
						ch = g_pReader->readChar();
						g_Memory.b[R4++] = uint8_t(R1 & 0xff);
					}
				}
				else
				{
					g_Memory.b[R4++] = 0;
					OutError(ERROR_129); //Ошибка или отсутствие числового аргумента.
					return false;
				}
			}
			else
			{
				g_Memory.b[R4++] = 0;
				return false;
			}
		}
		else if (g_pReader->getCurrCharType() == CReader::CT_SPACES) //если пробел
		{
			if (g_pReader->SkipWhitespaces(ch)) //то пропустим пробелы
			{
				break; //если конец строки - то выходим
			}
		}
		else if (CheckComment(ch))  //если комментарий - то выходим, его тут не обработать
		{
			break;
		}
		else if (g_pReader->getCurrCharType() == CReader::CT_LN)    //если конец строки - то выходим
		{
			break;
		}
		else
		{
			OutError(ERROR_114); //Ошибка в псевдокоманде.
			return false;
		}
	}

	return true;
}

bool PSC_asciz(int &R4, wchar_t &ch)
{
	bool bRet = PSC_ascii(R4, ch);
	g_Memory.b[R4++] = 0;
	return bRet;
}
//проверка комментария, выход:
//true - встретили комментарий,
// false - не комментарий, а что-то другое
bool CheckComment(wchar_t &ch)
{
	if (ch == L';' ||
	        (ch == L'/' && (g_pReader->getNextChar() == L'/' || g_pReader->getNextChar() == L'*')))
	{
		return true;
	}

	return false;
}



//проверка длины перехода по ветвлению
//вход: nTargetAddr - адрес метки
//		nCommandAddr - адрес команды. откуда переход
//	важное условие - в команде уже подставлено значение
//выход: nTargetAddr - корректное смещение
//		true - всё нормально
//		false - ошибка длины переход
bool BranchVerify(int nCommandAddr, int &nTargetAddr)
{
	nTargetAddr -= 2;
	auto nOffset = int(char(g_Memory.b[nCommandAddr])); //берём младший байт команды с расширением знака
	g_Memory.b[nCommandAddr] = 0; //смещение пока обнулим
	nTargetAddr += (nOffset * 2 - nCommandAddr);
	nTargetAddr /= 2;

	if (nTargetAddr > 0)
	{
		if (nTargetAddr < 0200)
		{
			return true;
		}
	}
	else
	{
		nTargetAddr = -nTargetAddr;

		if (nTargetAddr <= 0200)
		{
			nTargetAddr -= 0400;
			nTargetAddr = -nTargetAddr;
			return true;
		}
	}

	return false;
}

//выполнение .include filename
bool PSC_Include(int &R4, wchar_t &ch)
{
	Lst_AddPrepareLine(0, LT_COMMENTARY);
	g_GlobalParameters.bInString = true;
	wstring strFileName;
	wchar_t chQuoter = 0;

	if (StrQuotes(ch)) //если квотируемый символ, считаем что это кавычка
	{
		//и принимаем строку
		chQuoter = ch;
		ch = g_pReader->readChar();
	}

	for (;;)
	{
		if (g_pReader->getCurrCharType() == CReader::CT_LN)
		{
			break;
		}
		else if (g_pReader->getCurrCharType() == CReader::CT_SPACES && chQuoter == 0)
		{
			break; //если имя не в кавычках и встретили пробел - то конец имени
		}
		else if (CheckComment(ch))  //если комментарий - то выходим, его тут не обработать
		{
			break;
		}

		if (ch == chQuoter)
		{
			ch = g_pReader->readChar();
			chQuoter = 0;
			break;
		}

		strFileName += ch;
		ch = g_pReader->readChar();
	}

	g_GlobalParameters.bInString = false;

	if (chQuoter)
	{
		//если есть открывающая ковычка, но нет закрывающей - ошибка
		OutError(ERROR_130);
		return false;
	}

	if (!strFileName.empty())
	{
		auto pReader = new CReader(strFileName, Charset);

		if (pReader)
		{
			if (pReader->GetFileCharset() == CReader::CHARSET_FILEERROR)
			{
				delete pReader;
				OutError(ERROR_132); //Ошибка в псевдокоманде.
				return false;
			}

			//поищем, вдруг уже добаваляли эту инклуду
			bool bFound = false;

			for (auto &rdr : v_Reader)
			{
				if (rdr && rdr->GetFileName() == strFileName)
				{
					bFound = true; //добавляли
					break;
				}
			}

			if (bFound) //если уже есть среди добавленных
			{
				delete pReader; //не будем ещё раз добавлять
			}
			else
			{
				//добавляем
				v_Reader.emplace_back(g_pReader);
				g_pReader = pReader;
				g_GlobalParameters.bStepInclude = true;
				return true;
			}
		}
	}
	else
	{
		//если нет имени файла - ошибка
		OutError(ERROR_114);
		return false;
	}

	return false;
}


/*
линковка меток
*/
void LabelLinking()
{
	vector<size_t> vInt;
	size_t sz = g_labelRefs.getSize();

	//компонуем метки
	for (size_t i = 0; i < sz; ++i) //для всех ссылок на метки
	{
		uint32_t lti = g_labelRefs.GetType(i);

		//если это переход (к обработке обязательно) или окончательная обработка (после команды .END)
		if (((lti & LABEL_REFERENCE_MASK) == BRANCH_LABEL) || (g_GlobalParameters.nModeCompone == 0))
		{
			//то ищем метку в таблице меток
			CBKToken *token = g_labelRefs.GetLabel(i);
			int R1 = 0;
			uint32_t lt;
#ifdef DEBUG_LABEL_MANAGER

			///-----------------------debug---------------------------
			if ((lti & LABEL_REFERENCE_MASK) == BRANCH_LABEL)
			{
				fwprintf(dbgF, L"Try link branch label '%s'\n", token->getName().c_str());
			}

			///-----------------------debug---------------------------
#endif

			if ((lti & LABEL_REFERENCE_MASK) == CURRENTPC_LABEL) //если особая метка - ссылка на РС
			{
				int R0 = g_labelRefs.GetValue(i);
				wstring strCPC = token->getName(); //имя метки используем как текущий PC.
				strCPC = strCPC.substr(1); //убираем первый символ
				int pc = std::stoi(strCPC, nullptr, 8);
				int res = CorrectAddress(pc);

				if (lti & NEGATIVE_FLAG)
				{
					res = -res;
				}

				if (lti & HALFLABEL_FLAG)
				{
					res = res / 2;
				}

				R1 = res;
				g_Memory.w[R0 / 2] += R1;
				//потом надо будет удалить найденную метку
				vInt.push_back(i); //Список найденных меток
			}
			else if (SearchLabelInTables(token, R1, lt)) //если нашли, то обрабатываем
			{
				int R0 = g_labelRefs.GetValue(i);
				bool bBV = true;

				switch (lti & LABEL_REFERENCE_MASK)
				{
					case BRANCH_LABEL:
#ifdef DEBUG_LABEL_MANAGER
						///-----------------------debug---------------------------
						fwprintf(dbgF, L"\tCommand Address %06o, Target Address: %06o\n", R0, R1);
						///-----------------------debug---------------------------
#endif
						//проверка длины перехода по ветвлению
						bBV = BranchVerify(R0, R1);
#ifdef DEBUG_LABEL_MANAGER
						///-----------------------debug---------------------------
						fwprintf(dbgF, L"\tbranch offset %06o\n", R1);
						///-----------------------debug---------------------------
#endif
						R1 &= 0xff; //оставляем только младший байт.

						if (!bBV)
						{
							//!!! вот тут может быть неправильно выведена строка
							OutError(ERROR_110, true, R0); //Ошибка длины перехода по оператору ветвления.
						}

						break;

					case OFFSET_LABEL:
						R1 = R1 - R0 - 2;

						if ((lt & LABEL_DEFINITE_MASK) == CONSTANT_LABEL)
						{
							R1 = CorrectOffset(R1);
						}

						//смещение не может быть ни уполовиненным ни отрицательным, оба этих действия бессмысленны
						break;

					case RELATIVE_LABEL:
						if ((lt & LABEL_DEFINITE_MASK) != CONSTANT_LABEL)
						{
							R1 = CorrectAddress(R1);
						}

						if (lti & NEGATIVE_FLAG)
						{
							R1 = -R1;
						}

						if (lti & HALFLABEL_FLAG)
						{
							R1 = R1 / 2;
						}

						break;
				}

				if (bBV)
				{
					g_Memory.w[R0 / 2] += R1;
				}

				//потом надо будет удалить найденную метку
				vInt.push_back(i); //Список найденных меток
			}
		}
	}

	//удаляем всё что обработали
#ifdef DEBUG_LABEL_MANAGER
	///-----------------------debug---------------------------
	size_t n = vInt.size();
	///-----------------------debug---------------------------
#endif

	while (!vInt.empty())
	{
		size_t d = vInt.back();
		g_labelRefs.DeleteLabel(d);
		vInt.pop_back();
	}

#ifdef DEBUG_LABEL_MANAGER

	///-----------------------debug---------------------------
	if (n)
	{
		fwprintf(dbgF, L"Delete %d references, new refs size. (%d)\n", n, g_labelRefs.getSize());
	}

	///-----------------------debug---------------------------
#endif
}
