// DebuggerAssembly.cpp: продолжение implementation of the CDebugger class.
//

#include "pch.h"
#include "Debugger.h"


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

	{ _T("BR"),     0000400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR }, // op lll
	{ _T("BNE"),    0001000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BEQ"),    0001400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BGE"),    0002000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BLT"),    0002400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BGT"),    0003000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BLE"),    0003400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BPL"),    0100000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BMI"),    0100400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BHI"),    0101000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BLOS"),   0101400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BVC"),    0102000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BVS"),    0102400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BCC"),    0103000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BCS"),    0103400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BHIS"),   0103000,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },
	{ _T("BLO"),    0103400,    CPUCmdGroup::CBRANCH, OpcodeType::REGULAR },

	{ _T("MUL"),    0070000,    CPUCmdGroup::EIS, OpcodeType::EIS }, // op ss,r
	{ _T("DIV"),    0071000,    CPUCmdGroup::EIS, OpcodeType::EIS },
	{ _T("ASH"),    0072000,    CPUCmdGroup::EIS, OpcodeType::EIS },
	{ _T("ASHC"),   0073000,    CPUCmdGroup::EIS, OpcodeType::EIS },

	{ _T("EMT"),    0104000,    CPUCmdGroup::TRAP, OpcodeType::REGULAR }, // op nnn
	{ _T("TRAP"),   0104400,    CPUCmdGroup::TRAP, OpcodeType::REGULAR },

	{ _T("SOB"),    0077000,    CPUCmdGroup::SOB, OpcodeType::REGULAR }, // op r,ll

	{ _T("MARK"),   0006400,    CPUCmdGroup::MARK, OpcodeType::REGULAR }, // op nn

	{ _T("JSR"),    0004000,    CPUCmdGroup::TWOOPREG, OpcodeType::REGULAR }, // op r,dd
	{ _T("XOR"),    0074000,    CPUCmdGroup::TWOOPREG, OpcodeType::REGULAR },

	{ _T("FADD"),   0075000,    CPUCmdGroup::FIS, OpcodeType::FIS }, // op r
	{ _T("FSUB"),   0075010,    CPUCmdGroup::FIS, OpcodeType::FIS },
	{ _T("FMUL"),   0075020,    CPUCmdGroup::FIS, OpcodeType::FIS },
	{ _T("FDIV"),   0075030,    CPUCmdGroup::FIS, OpcodeType::FIS },
	{ _T("RTS"),    0000200,    CPUCmdGroup::FIS, OpcodeType::FIS },

	{ _T("PUSH"),   0010046,    CPUCmdGroup::PUSH, OpcodeType::REGULAR }, // op ss
	{ _T("PUSHB"),  0110046,    CPUCmdGroup::PUSH, OpcodeType::REGULAR },

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

	{ _T("MOV"),    0010000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR }, // op ss, dd
	{ _T("CMP"),    0020000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("BIT"),    0030000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("BIC"),    0040000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("BIS"),    0050000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("MOVB"),   0110000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("CMPB"),   0120000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("BITB"),   0130000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("BICB"),   0140000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("BISB"),   0150000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("ADD"),    0060000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	{ _T("SUB"),    0160000,    CPUCmdGroup::TWOOPS, OpcodeType::REGULAR },
	// FPU инструкции.
	{ _T("CFCC"),   0170000,    CPUCmdGroup::NOOPS, OpcodeType::FPU }, // класс 0 - без операндов
	{ _T("SETF"),   0170001,    CPUCmdGroup::NOOPS, OpcodeType::FPU }, // op
	{ _T("SETI"),   0170002,    CPUCmdGroup::NOOPS, OpcodeType::FPU },
	{ _T("SETD"),   0170011,    CPUCmdGroup::NOOPS, OpcodeType::FPU },
	{ _T("SETL"),   0170012,    CPUCmdGroup::NOOPS, OpcodeType::FPU },

	{ _T("LDFPS"),  0170100,    CPUCmdGroup::ONEOPS, OpcodeType::FPU }, // класс 1 - с одним операндом
	{ _T("STFPS"),  0170200,    CPUCmdGroup::ONEOPS, OpcodeType::FPU },
	{ _T("STST"),   0170300,    CPUCmdGroup::ONEOPS, OpcodeType::FPU },
	{ _T("CLRD"),   0170400,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },
	{ _T("CLRF"),   0170400,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },
	{ _T("TSTD"),   0170500,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },
	{ _T("TSTF"),   0170500,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },
	{ _T("ABSD"),   0170600,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },
	{ _T("ABSF"),   0170600,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },
	{ _T("NEGD"),   0170700,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },
	{ _T("NEGF"),   0170700,    CPUCmdGroup::FPUCLASS1, OpcodeType::FPU },

	{ _T("MULD"),   0171000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU }, // класс 2 - с двумя операндами
	{ _T("MULF"),   0171000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("MODD"),   0171400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("MODF"),   0171400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("ADDD"),   0172000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("ADDF"),   0172000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("LDD"),    0172400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("LDF"),    0172400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("SUBD"),   0173000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("SUBF"),   0173000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("CMPD"),   0173400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("CMPF"),   0173400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("STD"),    0174000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("STF"),    0174000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("DIVD"),   0174400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("DIVF"),   0174400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("STEXP"),  0175000,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("STCDI"),  0175400,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("STCDL"),  0175400,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("STCFI"),  0175400,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("STCFL"),  0175400,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("STCDF"),  0176000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("STCFD"),  0176000,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("LDEXP"),  0176400,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("LDCID"),  0177000,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("LDCLD"),  0177000,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("LDCIF"),  0177000,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("LDCLF"),  0177000,    CPUCmdGroup::FPUCLASS2_1, OpcodeType::FPU },
	{ _T("LDCFD"),  0177400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU },
	{ _T("LDCDF"),  0177400,    CPUCmdGroup::FPUCLASS2, OpcodeType::FPU }
};

const CDebugger::Registers CDebugger::m_pCPURegs[] =
{
	{_T("R0"), 0},
	{_T("R1"), 1},
	{_T("R2"), 2},
	{_T("R3"), 3},
	{_T("R4"), 4},
	{_T("R5"), 5},
	{_T("R6"), 6},
	{_T("R7"), 7},
	{_T("SP"), 6},
	{_T("PC"), 7}
};

const CDebugger::Registers CDebugger::m_pFPURegs[] =
{
	{_T("AC0"), 0},
	{_T("AC1"), 1},
	{_T("AC2"), 2},
	{_T("AC3"), 3},
	{_T("AC4"), 4},
	{_T("AC5"), 5},
	{_T("AC6"), 6},
	{_T("AC7"), 7}
};


// пропуск пробелов
// Вход: pch - указатель на текущий символ в ассемблируемой строке.
// выход: true - нашли непробельный символ
//        false - конец строки
bool CDebugger::SkipWhitespace(TCHAR **pch) const
{
	while (**pch)
	{
		if (**pch > 040)
		{
			return true;
		}

		(*pch)++;
	}

	return false;
}

// чтение лексемы
// Вход: str - строка, формируемой лексемы
//       pch - указатель на текущий символ в ассемблируемой строке.
//       bNoSkip - true - не пропускать пробелы перед чтением, false - пропускать
// выход: true - Ok
//        false - конец строки
bool CDebugger::ReadToken(CString &str, TCHAR **pch, const bool bNoSkip) const
{
	if (bNoSkip || SkipWhitespace(pch))
	{
		bool bFirstSymbol = true;
		str.Empty();

		while (**pch)
		{
			// лексемой считается последовательность букв и цифр.
			// начинающихся с буквы.
			const TCHAR ch = **pch;

			if ((_T('A') <= ch && ch <= _T('Z')) || (_T('a') <= ch && ch <= _T('z')))
			{
				str += ch;
			}
			else if (!bFirstSymbol && (_T('0') <= ch && ch <= _T('9')))
			{
				str += ch;
			}
			else
			{
				break;
			}

			bFirstSymbol = false;
			(*pch)++;
		}

		str.MakeUpper();
		return true;
	}

	return false;
}

// разбор простого арифметического выражения.
// Арифметическое выражение - последовательность слагаемых чисел, без скобок.
// Вход: num - результат выражения
//       pch - указатель на текущий символ в ассемблируемой строке.
// выход: true - Ok
//        false - конец строки
bool CDebugger::ArithmeticParser(int &num, TCHAR **pch, int pc) const
{
	num = 0;
	bool bRet = true;
	bool bFirst = true; // перед первым операндом знаков может и не быть

	for (;;) //
	{
		// пропустим пробелы перед знаком
		if (!(bRet = SkipWhitespace(pch))) // если конец строки,
		{
			break; // то прекращаем цикл.
		}

		bool bNegative = false;
		bool bHalf = false;
		int val = 0;
		TCHAR ch = **pch;

		//смотрим текущий символ
		if (ch == L'+') //если унарный +
		{
			(*pch)++;   //то его пропускаем
		}
		else if (ch == L'-') //если унарный -
		{
			(*pch)++;   //то его пропустим
			bNegative = true; // и выставим флаг минуса
		}
		else
		{
			if (!bFirst)
			{
				break; // кончилось выражение, ни +, ни -, что-то другое
			}
		}

		// пропустим пробелы после знака
		if (!(bRet = SkipWhitespace(pch))) // если конец строки,
		{
			break; // то прекращаем цикл.
		}

		ch = **pch;

		//смотрим, какой там следующий символ
		if (ch == _T('\''))
		{
			(*pch)++;
			val = **pch; // берём следующий символ
		}
		else if (ch == _T('\"'))
		{
			(*pch)++;
			val = **pch; // берём два следующих символа
			val <<= 8;
			(*pch)++;
			val |= **pch;
		}
		else if (ch == _T('.'))
		{
			val = pc; // берём текущий PC
			(*pch)++; // и переходим к следующему символу
		}
		else
		{
			if (ch == L'/') //если /
			{
				(*pch)++;
				ch = **pch; //то его пропустим
				bHalf = true;   // выставим флаг деления пополам
			}

			bRet = ReadOctalNumber(val, pch, true);

			if (bHalf)
			{
				val >>= 1;
			}
		}

		if (bNegative) // если число было отрицательным
		{
			val = -val; // то меняем ему знак
		}

		num += val; // прибавляем
		bFirst = false;

		if (!bRet) //если строка закончилась
		{
			break; // прерываем цикл
		}
	}

	return bRet;
}


// чтение восьмеричного числа
// Вход: num - значение формируемого числа
//       pch - указатель на текущий символ в ассемблируемой строке.
//       bNoSkip - true - не пропускать пробелы перед чтением, false - пропускать
// выход: true - Ok
//        false - конец строки
bool CDebugger::ReadOctalNumber(int &num, TCHAR **pch, const bool bNoSkip) const
{
	num = 0;

	if (bNoSkip || SkipWhitespace(pch))
	{
		CString str;

		while (**pch)
		{
			// числом считается последовательность цифр 0..7.
			const TCHAR ch = **pch;

			if ((_T('0') <= ch && ch <= _T('7')))
			{
				str += ch;
			}
			else
			{
				break;
			}

			(*pch)++;
		}

		num = _tcstol(str, nullptr, 8);
		return true;
	}

	return false;
}

bool CDebugger::isEoln(TCHAR **pch) const
{
	return !(**pch);
}



// ищем ожидаемый символ.
// вход: ch - ожидаемый символ
//       pch - указатель на текущий символ в ассемблируемой строке.
//       bNoSkip - true - не пропускать пробелы перед чтением, false - пропускать
// выход: true - есть он там, указатель ставится за него
//      false - нету, или конец строки, указатель останавливается на любом непробельном символе
//      или на конце строки.
bool CDebugger::NeedChar(TCHAR ch, TCHAR **pch, bool bNoSkip) const
{
	if (bNoSkip || SkipWhitespace(pch))
	{
		if (ch == **pch)
		{
			(*pch)++;
			return true;
		}
	}

	return false;
}



// вход: pc - текущий адрес инструкции, нужен для расчёта смещений
//       buf - буфер, куда формируется ассемблерная команда. макс. 8 слов.
//       pStr - указатель на ассемблируемую строку.
//             строка должна оканчиваться нулём. Это обязательно.
// выход: длина кода в словах
//      -1, 0 - ошибка ассемблирования
int CDebugger::AssembleCPUInstruction(int pc, std::span<uint16_t> buf, CString *pStr)
{
	int DotPC = pc; // значение PC для аргумента "точка"
	TCHAR *pch = pStr->GetBuffer();
	m_bOperandType = false; // считаем, что по умолчанию - приёмник

	// пропустим начальные пробелы
	if (!SkipWhitespace(&pch))
	{
		return -1; // если там ничего кроме пробелов нет - выходим
	}

	CString strMnem;
	bool bMinus = false;
	TCHAR ch = *pch; // первый символ

	if (ch == _T('.'))  // псевдокоманда .WORD
	{
		pch++;
		// получим мнемонику команды
		ReadToken(strMnem, &pch, false);

		if (strMnem == _T("WORD"))
		{
l_pscomWord:

			if (!SkipWhitespace(&pch))
			{
				return -1;
			}

l_WordArg:
			int num = 0;
			ArithmeticParser(num, &pch, DotPC);
			buf[0] = num & 0xffff;
			return 1;
		}

		return -1; // других псевдокоманд нет
	}

	if (ch == _T('-'))
	{
		// предположительно отрицательное число
		pch++;
		ch = *pch;
		bMinus = true;
	}

	if ((_T('0') <= ch && ch <= _T('7')) || ch == _T('/'))
	{
		if (bMinus)
		{
			pch--; // вернёмся к минусу
		}

		goto l_WordArg; // и пойдём обработаем арифметическое выражение
	}

	// если минус не перед числом - ошибка
	if (bMinus)
	{
		return -1;
	}

	// получим мнемонику команды
	ReadToken(strMnem, &pch, false);

	if (strMnem == _T("WORD"))
	{
		// псевдокоманда WORD без точки спереди
		goto l_pscomWord;
	}

// кроме ассемблерных команд можно задавать числа следующим образом:
//  .word 'арифметическое выражение'
//  word 'арифметическое выражение' (без точки)
//  'арифметическое выражение' (просто число без всяких псевдокоманд)
//  допускаются отрицательные числа только восьмеричные

	for (const auto &cmd : m_pCPUCommands) // пока не конец таблицы
	{
		if (!g_Config.m_bMMG && cmd.nType == OpcodeType::MMG)
		{
			// если выключены mmg инструкции, их игнорируем
			continue;
		}

		if (!g_Config.m_bEIS && cmd.nType == OpcodeType::EIS)
		{
			// если выключены eis инструкции, их игнорируем
			continue;
		}

		if (!g_Config.m_bFIS && cmd.nType == OpcodeType::FIS)
		{
			// если выключены fis инструкции, их игнорируем
			continue;
		}

		if (!g_Config.m_bFPU && cmd.nType == OpcodeType::FPU)
		{
			// если выключены fpu инструкции, их игнорируем
			continue;
		}

		if (cmd.strName == strMnem) // если нашли мнемонику
		{
			int nLen = 0; // длина команды в словах.
			// если нашли нужную мнемонику
			// нужно взять опкод.
			buf[nLen++] = cmd.nOpcode;
			buf[nLen]     = 0; // следующие два слова заранее обнулим
			buf[nLen + 1] = 0;
			pc += 2;
			bool bRet = true;

			// и обработать операнды в соответствии с типом группы, к которой принадлежит опкод.
			switch (cmd.nGroup)
			{
				case CPUCmdGroup::NOOPS:        // ничего делать не надо.
					break;

				case CPUCmdGroup::CBRANCH:
					bRet = assembleBR(pc, buf, DotPC, &pch);
					break;

				case CPUCmdGroup::EIS:
					bRet = assemble2ROP(pc, buf, nLen, DotPC, &pch);
					break;

				case CPUCmdGroup::TRAP:
					bRet = assembleTRAP(buf, &pch);
					break;

				case CPUCmdGroup::SOB:
					bRet = assembleSOB(pc, buf, DotPC, &pch);
					break;

				case CPUCmdGroup::MARK:
					bRet = assembleMARK(buf, &pch);
					break;

				case CPUCmdGroup::TWOOPREG:
					bRet = assemble2OPR(pc, buf, nLen, DotPC, &pch);
					break;

				case CPUCmdGroup::FIS:
					bRet = assemble1OPR(pc, buf, &pch);
					break;

				case CPUCmdGroup::PUSH:
					m_bOperandType = true;  // меняем тип операнда
					[[fallthrough]];        // и переходим к выполнению CPUCmdGroup::ONEOPS

				case CPUCmdGroup::ONEOPS:
					bRet = assemble1OP(pc, buf, nLen, DotPC, &pch);
					break;

				case CPUCmdGroup::TWOOPS:
					bRet = assemble2OP(pc, buf, nLen, DotPC, &pch);
					break;

				case CPUCmdGroup::FPUCLASS1:
					bRet = assemble1OPFPU(pc, buf, nLen, DotPC, &pch);
					break;

				case CPUCmdGroup::FPUCLASS2:
					bRet = assemble2OPRFPU(pc, buf, nLen, DotPC, &pch, true);
					break;

				case CPUCmdGroup::FPUCLASS2_1:
					bRet = assemble2OPRFPU(pc, buf, nLen, DotPC, &pch, false);
					break;
			}

			if (bRet)
			{
				return nLen;
			}

			// ошибка в операндах.
			return 0;
		}
	}

	// ошибка - неопознанная команда.
	return -1;
}

bool CDebugger::assembleBR(int &pc, std::span<uint16_t> buf, const int DotPC, TCHAR **pch) const
{
	int dest = 0;
	ArithmeticParser(dest, pch, DotPC);
	// теперь надо вычислить смещение.
	int offs = (dest - pc) / 2;

	if (offs >= 0) // для перехода вперёд
	{
		if (offs < 0200) // если укладываемся
		{
			buf[0] |= offs & 0xff; // то формируем опкод
			return true; // и выходим
		}
	}
	else // для перехода назад
	{
		if (-offs <= 0200)
		{
			buf[0] |= offs & 0xff;
			return true;
		}
	}

	return false;
}

bool CDebugger::assembleTRAP(std::span<uint16_t> buf, TCHAR **pch) const
{
	int num = 0;

	if (ReadOctalNumber(num, pch, false))
	{
		if (0 <= num && num < 0400)
		{
			buf[0] |= num & 0xff; // то формируем опкод
			return true; // и выходим
		}
	}

	return false;
}

bool CDebugger::assembleMARK(std::span<uint16_t> buf, TCHAR **pch) const
{
	int num = 0;

	if (ReadOctalNumber(num, pch, false))
	{
		if (0 <= num && num < 077)
		{
			buf[0] |= num & 077; // то формируем опкод
			return true; // и выходим
		}
	}

	return false;
}

// сборка двухоперандных команд, где второй операнд - регистр
// Это EIS команды, у которых  при записи мнемоники почему-то поменяны местами
// источник и приёмник, как у x86
bool CDebugger::assemble2ROP(int &pc, std::span<uint16_t> buf, int &len, const int DotPC, TCHAR **pch)
{
	m_bOperandType = false; // сперва dd

	if (assemble1OP(pc, buf, len, DotPC, pch))   // обработаем первый операнд
	{
		if (NeedChar(_T(','), pch, false))    // проверим наличие второго
		{
			m_bOperandType = true; // теперь ss
			return ReadRegName(buf, pch, false);    // обработаем второй операнд
		}

		// Ошибка в команде - нет второго операнда.
		return false;
	}

	return false;
}

// сборка обычных однооперандных команд
bool CDebugger::assemble1OP(int &pc, std::span<uint16_t> buf, int &len, const int DotPC, TCHAR **pch) const
{
	return Operand_analyse(pc, buf, len, DotPC, pch, false); // разбираемся, что там такое
}

bool CDebugger::assemble1OPFPU(int &pc, std::span<uint16_t> buf, int &len, const int DotPC, TCHAR **pch) const
{
	return Operand_analyse(pc, buf, len, DotPC, pch, true); // разбираемся, что там такое
}


bool CDebugger::ReadRegName(std::span<uint16_t> buf, TCHAR **pch, const bool bNoSkip) const
{
	CString str;

	if (ReadToken(str, pch, bNoSkip))
	{
		for (const auto &reg : m_pCPURegs)
		{
			if (reg.name == str) // если нашли мнемонику
			{
				SetAddrReg(buf, reg.nNum);
				return true;
			}
		}
	}

	return false;
}

bool CDebugger::ReadFPURegName(std::span<uint16_t> buf, TCHAR **pch, bool bShort, const bool bNoSkip) const
{
	CString str;

	if (ReadToken(str, pch, bNoSkip))
	{
		int n = bShort ? 4 : 8;

		for (int i = 0; i < n; ++i)
		{
			if (m_pFPURegs[i].name == str) // если нашли мнемонику
			{
				SetAddrReg(buf, m_pFPURegs[i].nNum);
				return true;
			}
		}
	}

	return false;
}



void CDebugger::SetAddrReg(std::span<uint16_t> buf, uint16_t val) const
{
	if (m_bOperandType)
	{
		val <<= 6;
	}

	buf[0] |= val;
}


// сборка однооперандных команд, где операнд - регистр
bool CDebugger::assemble1OPR(int &pc, std::span<uint16_t> buf, TCHAR **pch) const
{
	return ReadRegName(buf, pch, false);
}

bool CDebugger::assemble1OPRFPU(int &pc, std::span<uint16_t> buf, TCHAR **pch) const
{
	return ReadFPURegName(buf, pch, true, false);
}


// сборка двухоперандных команд
bool CDebugger::assemble2OP(int &pc, std::span<uint16_t> buf, int &len, const int DotPC, TCHAR **pch)
{
	m_bOperandType = true; // сперва ss

	if (assemble1OP(pc, buf, len, DotPC, pch))   // обработаем один операнд
	{
		if (NeedChar(_T(','), pch, false))  // проверим наличие второго
		{
			m_bOperandType = false; // теперь dd
			return assemble1OP(pc, buf, len, DotPC, pch);     // обработаем второй операнд
		}

		// Ошибка в команде - нет второго операнда.
		return false;
	}

	return false;
}
// сборка двухоперандных команд, где первый операнд - регистр
bool CDebugger::assemble2OPR(int &pc, std::span<uint16_t> buf, int &len, const int DotPC, TCHAR **pch)
{
	m_bOperandType = true; // сперва ss

	if (ReadRegName(buf, pch, false))  // обрабатываем регистр
	{
		if (NeedChar(_T(','), pch, false))    // проверим наличие второго
		{
			m_bOperandType = false; // теперь dd
			return assemble1OP(pc, buf, len, DotPC, pch);     // обработаем второй операнд
		}

		// Ошибка в команде - нет второго операнда.
		return false;
	}

	return false;
}
// сборка двухоперандных команд FPU, где первый операнд - регистр AC0..AC3
// а второй - FPU/общ.
bool CDebugger::assemble2OPRFPU(int &pc, std::span<uint16_t> buf, int &len, const int DotPC, TCHAR **pch, bool bFPU)
{
	m_bOperandType = true; // сперва ss

	if (assemble1OPRFPU(pc, buf, pch))  // обрабатываем регистр
	{
		if (NeedChar(_T(','), pch, false))    // проверим наличие второго
		{
			m_bOperandType = false; // теперь dd

			if (bFPU)
			{
				return assemble1OPFPU(pc, buf, len, DotPC, pch);  // обработаем второй операнд
			}

			return assemble1OP(pc, buf, len, DotPC, pch);     // обработаем второй операнд
		}

		// Ошибка в команде - нет второго операнда.
		return false;
	}

	return false;
}




bool CDebugger::assembleSOB(int &pc, std::span<uint16_t> buf, const int DotPC, TCHAR **pch)
{
	m_bOperandType = true;     // сперва ss

	if (ReadRegName(buf, pch, false))  // обработаем имя регистра
	{
		if (NeedChar(_T(','), pch, false))    // проверим наличие второго
		{
			m_bOperandType = false;    // теперь dd
			int dest = 0;
			ArithmeticParser(dest, pch, DotPC);
			uint16_t offs = (pc - dest) / 2;

			if (0 <= offs && offs <= 077)
			{
				buf[0] |= offs & 077; // то формируем опкод
				return true; // и выходим
			}

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

		// Ошибка в команде.
		return false;
	}

	return false;
}




// разбор операнда
bool CDebugger::Operand_analyse(int &pc, std::span<uint16_t> buf, int &len, const int DotPC, TCHAR **pch, bool bFPU) const
{
	if (SkipWhitespace(pch))
	{
		TCHAR ch = **pch;

		if (ch == _T('@'))
		{
			bFPU = false;
			// собака - может быть, может не быть
			SetAddrReg(buf, 010); // устанавливаем признак относительной адресации
			(*pch)++; // передвигаемся дальше
			ch = **pch; // читаем следующий символ

			if (ch == 0) // если внезапный конец строки
			{
				return false;
			}
		}

		if (ch == _T('#'))
		{
			// непосредственная адресация, за ней - число
			(*pch)++; // передвигаемся дальше

			if (isEoln(pch))
			{
				return false;
			}

			int num = 0;
			ArithmeticParser(num, pch, DotPC);
			SetAddrReg(buf, 027);
			buf[len++] = num & 0xffff;
			pc += 2;
			return true;
		}

		if (ch == _T('('))
		{
			(*pch)++;

			// скобка, за ней - имя регистра
			if (ReadRegName(buf, pch, true)) // если регистр опознан
			{
				if (NeedChar(_T(')'), pch, true)) // если за ним - закрывающая скобка
				{
					if (**pch == _T('+'))
					{
						(*pch)++;
						SetAddrReg(buf, 020);
					}
					else
					{
						SetAddrReg(buf, 010);
					}

					return true;
				}
			}

			return false;
		}

		if (ch == _T('-'))
		{
			// если за минусом - открывающая скобка
			(*pch)++;
			ch = **pch;

			if (ch == _T('('))
			{
				(*pch)++;

				// скобка, за ней - имя регистра
				if (ReadRegName(buf, pch, true)) // если регистр опознан
				{
					if (NeedChar(_T(')'), pch, true)) // если за ним - закрывающая скобка
					{
						SetAddrReg(buf, 040);
						return true;
					}
				}

				return false;
			}

			// тут может быть отрицательное число
			if (_T('0') <= ch && ch <= _T('7'))
			{
				(*pch)--; // вернёмся к минусу
				goto l_Digits;
			}

			return false;
		}

		if (_T('0') <= ch && ch <= _T('7'))
		{
			// тут может быть число, а за ним - может быть регистр в скобках, а может и не быть
l_Digits:
			int num = 0;
			ArithmeticParser(num, pch, DotPC);

			// если за числом регистр в скобках
			if (NeedChar(_T('('), pch, true))
			{
				// скобка, за ней - имя регистра
				if (ReadRegName(buf, pch, true)) // если регистр опознан
				{
					if (NeedChar(_T(')'), pch, true)) // если за ним - закрывающая скобка
					{
						// то это индексная адресация
						SetAddrReg(buf, 060);
						buf[len++] = num & 0xffff;
						pc += 2;
						return true;
					}
				}

				return false;
			}

			// это относительная адресация.
			SetAddrReg(buf, 067);
			pc += 2;
			uint16_t offs = num - pc;
			buf[len++] = offs;
			return true;
		}

		if ((_T('A') <= ch && ch <= _T('Z')) || (_T('a') <= ch && ch <= _T('z')))
		{
			// тут может быть только регистр
			if (bFPU)
			{
				return ReadFPURegName(buf, pch, false, true); // если регистр опознан
			}

			return ReadRegName(buf, pch, false); // если регистр опознан
		}
	}

	return false;
}
