// CPU.cpp: implementation of the CCPU class.
//можно переделать под правильную реализацию логики


#include "stdafx.h"
#include "Board.h"
#include "CPU.h"
#include "ExceptionHalt.h"
#include "BKErrMsgSys.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#define new DEBUG_NEW
#endif

// CPU timing в тактах
#define TIMING_IDX_BASE      0
#define TIMING_IDX_HALT      1
#define TIMING_IDX_WAIT      2
#define TIMING_IDX_RTI       3
#define TIMING_IDX_RESET     4
#define TIMING_IDX_BR_BASE   5
#define TIMING_IDX_RTS       6
#define TIMING_IDX_MARK      7
#define TIMING_IDX_EMT       8
#define TIMING_IDX_IOT       9
#define TIMING_IDX_SOB       10
#define TIMING_IDX_INT       11
#define TIMING_IDX_BUS_ERROR 12


//Это для БК10
//однооперандные расчётные
const int CCPU::timing_Misk_10[13] = { 12, 68, 12, 40, 1140, 16, 32, 56, 64, 104, 20, 40, 64 };
const int CCPU::timing_OneOps_TST_10[8]    = {12,  24, 24, 36, 28, 40, 36, 48};
const int CCPU::timing_OneOps_CLR_10[8]    = {12,  28, 28, 40, 32, 44, 40, 52};
const int CCPU::timing_OneOps_MTPS_10[8]   = {24,  32, 32, 44, 36, 48, 44, 56};
const int CCPU::timing_OneOps_XOR_10[8]    = {12,  32, 32, 44, 32, 44, 44, 56};
const int CCPU::timing_OneOps_JMP_10[8]    = { 8,  20, 24, 32, 24, 32, 32, 44};
const int CCPU::timing_OneOps_JSR_10[8]    = { 8,  32, 36, 40, 36, 40, 40, 52};
//двухоперандные расчётные
const int CCPU::timing_TwoOps_MOV_10[8][8] =
{
	12, 32, 36, 44, 36, 44, 44, 56,
	28, 40, 40, 48, 40, 48, 48, 60,
	28, 40, 40, 48, 40, 48, 48, 60,
	40, 52, 52, 60, 52, 60, 60, 72,
	28, 44, 44, 52, 44, 52, 52, 64,
	40, 56, 56, 64, 56, 64, 64, 76,
	40, 52, 52, 60, 52, 60, 60, 72,
	52, 64, 64, 72, 64, 72, 72, 84
};
const int CCPU::timing_TwoOps_CMP_10[8][8] =
{
	12, 32, 32, 44, 32, 44, 44, 56,
	28, 36, 36, 48, 36, 48, 48, 60,
	28, 36, 36, 48, 36, 48, 48, 60,
	40, 48, 48, 60, 48, 60, 60, 72,
	28, 40, 40, 52, 40, 52, 52, 64,
	40, 52, 52, 64, 52, 64, 64, 76,
	40, 48, 48, 60, 48, 60, 60, 72,
	52, 60, 60, 72, 60, 72, 72, 84
};
const int CCPU::timing_TwoOps_BIS_10[8][8] =
{
	12, 36, 36, 48, 36, 48, 48, 60,
	28, 40, 40, 52, 40, 52, 52, 64,
	28, 40, 40, 52, 40, 52, 52, 64,
	40, 52, 52, 64, 52, 64, 64, 76,
	28, 44, 44, 56, 44, 56, 56, 68,
	40, 56, 56, 68, 56, 68, 68, 80,
	40, 52, 52, 64, 52, 64, 64, 76,
	52, 64, 64, 76, 64, 76, 76, 88
};

//Это для БК11 4Мгц
const int CCPU::timing_Misk_11[13] = { 16, 96, 16, 48, 1140, 16, 32, 56, 80, 128, 21, 48, 64 };
//однооперандные расчётные
const int CCPU::timing_OneOps_TST_11[8]    = { 16, 32, 32, 40, 32, 40, 40, 48 };
const int CCPU::timing_OneOps_CLR_11[8]    = { 16, 37, 37, 48, 37, 48, 48, 58 };
const int CCPU::timing_OneOps_MTPS_11[8]   = { 26, 37, 37, 48, 37, 48, 48, 58 };
const int CCPU::timing_OneOps_XOR_11[8]    = { 16, 37, 37, 48, 40, 48, 48, 58 };
const int CCPU::timing_OneOps_JMP_11[8]    = {  8, 24, 26, 37, 26, 37, 37, 48 };
const int CCPU::timing_OneOps_JSR_11[8]    = {  8, 32, 37, 48, 37, 48, 48, 58 };
//двухоперандные расчётные
const int CCPU::timing_TwoOps_MOV_11[8][8] =
{
	16, 32, 40, 48, 40, 48, 48, 64,
	32, 48, 48, 64, 48, 64, 64, 72,
	32, 48, 48, 64, 48, 64, 64, 72,
	40, 56, 64, 64, 64, 72, 64, 80,
	32, 48, 48, 64, 48, 64, 64, 72,
	40, 56, 64, 64, 64, 72, 64, 80,
	40, 56, 64, 64, 64, 72, 64, 80,
	48, 64, 72, 80, 72, 80, 80, 96
};
const int CCPU::timing_TwoOps_CMP_11[8][8] =
{
	16, 32, 32, 48, 32, 48, 48, 56,
	32, 48, 48, 56, 48, 56, 56, 64,
	32, 48, 48, 56, 48, 56, 56, 64,
	40, 56, 56, 64, 56, 64, 64, 80,
	32, 48, 48, 56, 48, 56, 56, 64,
	40, 56, 56, 64, 56, 64, 64, 80,
	40, 56, 56, 64, 56, 64, 64, 80,
	48, 64, 64, 80, 64, 80, 80, 80
};
const int CCPU::timing_TwoOps_BIS_11[8][8] =
{
	16, 42, 42, 53, 42, 53, 53, 64,
	32, 48, 48, 64, 53, 64, 64, 72,
	32, 48, 48, 64, 53, 64, 64, 72,
	40, 64, 64, 72, 64, 74, 72, 80,
	32, 48, 48, 64, 53, 64, 64, 72,
	40, 64, 64, 72, 64, 74, 72, 80,
	40, 64, 64, 72, 64, 74, 72, 80,
	48, 72, 72, 80, 74, 85, 80, 96
};


//////////////////////////////////////////////////////////////////////
// Construction/Destruction


CCPU::CCPU()
	: m_pChip(nullptr)
	, m_pExecuteMethodMap(nullptr)
	, timing_Misk(nullptr)
	, timing_OneOps_TST(nullptr)
	, timing_OneOps_CLR(nullptr)
	, timing_OneOps_MTPS(nullptr)
	, timing_OneOps_XOR(nullptr)
	, timing_OneOps_JMP(nullptr)
	, timing_OneOps_JSR(nullptr)
	, timing_TwoOps_MOV(nullptr)
	, timing_TwoOps_CMP(nullptr)
	, timing_TwoOps_BIS(nullptr)
{
	memset(m_RON, 0, sizeof(m_RON));

	for (bool &i : m_PSW)
	{
		i = false;
	}

	m_PSW[PSW_P_POS] = m_PSW[6] = m_PSW[5] = true;
	memset(m_pSysRegs, 0, sizeof(m_pSysRegs));
	//инициализируем маски регистров по записи. В нулевые биты ничего записать нельзя, в единичные - можно.
	m_pSysRegsMask[SYS_REG_177700] = 7;
	m_pSysRegsMask[SYS_REG_177702] = 0177777;
	m_pSysRegsMask[SYS_REG_177704] = 0377;
	m_pSysRegsMask[SYS_REG_177706] = 0177777;
	m_pSysRegsMask[SYS_REG_177710] = 0;
	m_pSysRegsMask[SYS_REG_177712] = 0377;
	//инициализируем значения регистров по чтению
	m_pSysRegs[SYS_REG_177700] = 0177740;
	m_pSysRegs[SYS_REG_177702] = 0177777;
	m_pSysRegs[SYS_REG_177704] = 0177440;
	m_pSysRegs[SYS_REG_177706] = 0000000;
	m_pSysRegs[SYS_REG_177710] = 0177777;
	m_pSysRegs[SYS_REG_177712] = 0177400;
	InitVars();
	PrepareCPU();
}

CCPU::~CCPU()
{
	DoneCPU();
}
void CCPU::AttachChip(CMotherBoard *pChip)
{
	m_pChip = pChip;

	switch (m_pChip->GetBoardModel())
	{
		default:
		case BK_0010:
			timing_Misk        = timing_Misk_10;
			timing_OneOps_TST  = timing_OneOps_TST_10;
			timing_OneOps_CLR  = timing_OneOps_CLR_10;
			timing_OneOps_MTPS = timing_OneOps_MTPS_10;
			timing_OneOps_XOR  = timing_OneOps_XOR_10;
			timing_OneOps_JMP  = timing_OneOps_JMP_10;
			timing_OneOps_JSR  = timing_OneOps_JSR_10;
			timing_TwoOps_MOV  = &timing_TwoOps_MOV_10[0][0];
			timing_TwoOps_CMP  = &timing_TwoOps_CMP_10[0][0];
			timing_TwoOps_BIS  = &timing_TwoOps_BIS_10[0][0];
			break;

		case BK_0011:
		case BK_0011M:
			timing_Misk        = timing_Misk_11;
			timing_OneOps_TST  = timing_OneOps_TST_11;
			timing_OneOps_CLR  = timing_OneOps_CLR_11;
			timing_OneOps_MTPS = timing_OneOps_MTPS_11;
			timing_OneOps_XOR  = timing_OneOps_XOR_11;
			timing_OneOps_JMP  = timing_OneOps_JMP_11;
			timing_OneOps_JSR  = timing_OneOps_JSR_11;
			timing_TwoOps_MOV  = &timing_TwoOps_MOV_11[0][0];
			timing_TwoOps_CMP  = &timing_TwoOps_CMP_11[0][0];
			timing_TwoOps_BIS  = &timing_TwoOps_BIS_11[0][0];
			break;
	}
}
void CCPU::InitCPU()
{
	InitVars();
	ResetCPU();

	for (bool &i : m_PSW)
	{
		i = false;
	}

	m_PSW[PSW_P_POS] = m_PSW[6] = m_PSW[5] = true;
	m_RON[R_PC] = GetWord(0177716) & 0177400; //тут спецом читаем ячейку памяти, чтобы можно было использовать особенности старт режима A16M
}

void CCPU::ResetCPU()
{
	m_pSysRegs[SYS_REG_177700] = 0177740;
	m_pSysRegs[SYS_REG_177702] = 0177777;
	m_pSysRegs[SYS_REG_177704] = 0177440;
	m_pChip->ResetDevices();
}

void CCPU::InitVars()
{
	ResetTimer();
	m_nInternalTick = 0;
	m_bWaitMode = false;
	m_bStepMode = false;
	m_bTrace_RTT = false;
	m_bIRQ1rq = false;
	m_bIRQ1LL = false;
	m_bGetVector = false;
	m_bTwiceHangup = false;
	m_bRPLYrq = false;
	m_bRSVDrq = false;
	m_bACLOrq = m_bDCLOrq = false;
	m_bHALTrq = m_bErrInstrRq = false;
	m_bRPL2rq = false;
	m_bVIRQErrrq = false;
	m_bIRQ2rq = false;
	m_bIRQ3rq = false;
	m_bBPTrq = false;
	m_bIOTrq = false;
	m_bEMTrq = false;
	m_bTRAPrq = false;
	m_qVIRQ.clear();
}

void CCPU::ResetTimer()
{
	m_nCmdTicks = 0;
	//инициализируем ВЕ таймер, специально через функцию, чтобы внутренние переменные инициализировать.
	//регистр начального значения таймера при ресете не изменяется
	SetSysRegs(0177712, uint16_t(0)); // Управление
}


/*
таймер

177706 -- Регистр начального значения таймера. Доступен по чтению и записи.
177710 -- Реверсивный счётчик. Доступен по чтению, запись в регистр игнорируется.
177712 -- Программируемый таймер-- регистр управления.
любая запись в этот регистр вызывает перезапись в регистр счётчика константы из регистра начального значения таймера
(001)бит 0: STOP: "1" - остановка
При установке запрещает счёт
(002)бит 1: WRAPAROUND: "1" - Режим непрерывного счёта, отменяет действие битов EXPENABLE и ONESHOT
Запрещает фиксацию перехода счётчика через 0. Досчитав до нуля, таймер продолжает вычитание.
(004)бит 2: EXPENABLE: "1" - разрешение установки сигнала EXPIRY ("конец счета")
Установка включает индикацию, при очередном переходе таймера через 0 устанавливается в 1 бит EXPIRY, если ранее он был сброшен.
Нужно учитывать, что при первом (после включения ЭВМ или системного сброса) запуске таймера в данном режиме индикация
срабатывает только после ВТОРОГО перехода счётчика через 0, причём независимо от того, работал ли таймер до этого в
других режимах
(010)бит 3: ONESHOT: режим одновибратора
При установке запрещает повторный счёт, после первого досчёта до 0 сбрасывается бит RUN. Установка данного режима не отменяет
режим индикации (бит EXPENABLE)
(020)бит 4: RUN: запуск счётчика, запись "1"-- загружает счётчик из регистра 177706 и начинает отсчёт
В этом режиме при досчёте до нуля в счётчик заново заносится константа из регистра начального значения, следовательно
счёт всегда ведётся от константы до нуля (если только не запрещена фиксация перехода через 0 битом WRAPAROUND). При сбросе
в 0 в счётчик переписывается начальное значение из регистра начального значения
(040)бит 5: делитель на 16, "1"-- включён
Установка снижает скорость счёта в 16 раз (режим умножения времени на 16)
при одновременной установке скорость снижается, соответственно в 64 раза
(100)бит 6: делитель на 4, "1"-- включён
Установка снижает скорость счёта в 4 раза (режим умножения времени на 4)
(200)бит 7: EXPIRY: флаг окончания счета, устанавливается в "1" при достижении счётчиком нуля, сбрасывается только программно
Для установки бита должен быть установлен бит 2.
биты 8-15 не используются, "1".
*/
#define TVE_SP  01
#define TVE_CAP 02
#define TVE_MON 04
#define TVE_OS  010
#define TVE_RUN 020
#define TVE_D16 040
#define TVE_D4  0100
#define TVE_FL  0200

void CCPU::Timerprocess()
{
	register uint16_t reg_177710 = m_pSysRegs[SYS_REG_177710]; // Счётчик
	register uint16_t reg_177712 = m_pSysRegs[SYS_REG_177712]; // Управление

	//если счётчик остановлен
	if (reg_177712 & TVE_SP)
	{
		return; //выход, счётчик не работает.
		//т.е. работает, но тактируется от внешнего входа, а не от частоты проца
	}

	//если счётчик запущен
	if (reg_177712 & TVE_RUN)
	{
		//обрабатываем
		if (++m_nTVE_Cnt >= m_nTVE_Divider)
		{
			m_nTVE_Cnt = 0;

			// уменьшаем текущее значение таймера на 1
			if (--reg_177710 == 0)
			{
				if (reg_177712 & TVE_CAP)    //если режим WRAPAROUND
				{
					//непрерывный счёт без перезагрузки счётчика
				}
				else
				{
					//если не режим WRAPAROUND, то значение счётчика перезагружаем
					reg_177710 = m_pSysRegs[SYS_REG_177706]; //загрузим в счётчик начальное значение

					if (reg_177712 & TVE_MON)   //разрешение установки сигнала "конец счёта" ?
					{
						reg_177712 |= TVE_FL;   //да, установим сигнал
					}

					if (reg_177712 & TVE_OS)    //установлен режим одновибратора?
					{
						reg_177712 &= ~TVE_RUN; //тогда сбросим бит 4
					}

					m_pSysRegs[SYS_REG_177712] = reg_177712;
				}
			}

			m_pSysRegs[SYS_REG_177710] = reg_177710;
		}
	}
}

uint8_t CCPU::GetByte(register uint16_t addr)
{
	if (0177700 <= addr && addr < 0177714)
	{
		register uint16_t v = GetSysRegs(addr);
		return (addr & 1) ? HIBYTE(v) : LOBYTE(v);
	}

	return m_pChip->GetByte(addr);
}

uint16_t CCPU::GetWord(register uint16_t addr)
{
	if (0177700 <= addr && addr < 0177714)
	{
		return GetSysRegs(addr);
	}

	return m_pChip->GetWord(addr);
}

void CCPU::SetByte(register uint16_t addr, register uint8_t value)
{
	if (0177700 <= addr && addr < 0177714)
	{
		SetSysRegs(addr, value);
	}
	else
	{
		m_pChip->SetByte(addr, value);
	}
}

void CCPU::SetWord(register uint16_t addr, register uint16_t value)
{
	if (0177700 <= addr && addr < 0177714)
	{
		SetSysRegs(addr, value);
	}
	else
	{
		m_pChip->SetWord(addr, value);
	}
}


int CCPU::TranslateInstruction()
{
	m_nInternalTick = 0;

	//если случилась ошибка доступа к памяти,
	//не надо выполнять инструкцию, надо сразу перейти к обработчику прерываний
	if (!(m_bRPLYrq || m_bRPL2rq || m_bVIRQErrrq))
	{
		//если была команда WAIT, не надо выполнять инструкцию, но чтобы не зацикливаться,
		//надо делать вид, что мы что-то делаем.
		if (m_bWaitMode)
		{
			m_nInternalTick = timing_Misk[TIMING_IDX_BASE];
		}
		else
		{
			m_instruction = GetWord(m_RON[R_PC]);
			m_RON[R_PC] += 2;
			m_datarg = m_ALU = 0;
			m_bByteOperation = !!(m_instruction & 0100000);
			m_Nbit = (m_bByteOperation ? 0200 : 0100000);
			register int t = m_instruction;
			m_nRegDst = t & 7;
			t >>= 3;
			m_nMethDst = t & 7;
			t >>= 3;
			m_nRegSrc = t & 7;
			t >>= 3;
			m_nMethSrc = t & 7;
			// Find command implementation using the command map
			(this->*(m_pExecuteMethodMap[m_instruction]))();  // Call command implementation method
		}
	}

	// диспетчер прерываний
	/*
	В ВМ1 как такового режима HALT нет, бит 8 PSW не задействуется.
	Для поддержки так называемого HALT-режима в ВМ1 в PSW введены два бита -- 10 и 11.
	10-й бит маскирует все прерывания, кроме запросов по обращению к магистрали, резервного
	и запрещённого кода. 11-й бит маскирует запрос IRQ1. Обработка векторов SEL002, SEL006 и
	SEL012 на ДВК идёт с установленным 10-м битом. Кроме того 10-й бит вроде изменяет реакцию
	на зависание, его обработка происходит не по 4-му вектору, а по SEL002.
	*/
	if (m_bStepMode)
	{
		m_bStepMode = false;
	}
	else if (m_bTrace_RTT)
	{
		// Пропускаем обработку прерываний для RTT с установленным битом T
		// и заодно вообще все прерывания. что-то не логично, но может быть так оно и есть,
		m_bTrace_RTT = false;
		// а у меня там сделано исключение для этого случая, без пропуска всех прерываний,
		// что есть правильно - неизвестно, пусть это
	}
	else // Обработка прерываний
	{
		// Считаем вектор прерывания и режим соответственно приоритету
		register uint16_t nInterruptVector = 0;
		register bool bCanTwiceHangup = false; //флаг, показывающий, у какого прерывания может случиться двойное зависание

		//обработка BPT IOT EMT TRAP HALT
		//должна идти перед всеми, т.к. эти команды не выставляют запрос на прерывание,
		//а генерируют его непосредственно из матрицы микрокода.
		if (m_bHALTrq)  // HALT команда
		{
			m_bHALTrq = false;
			nInterruptVector = 0160002;
		}
		else if (m_bBPTrq)  // BPT команда
		{
			m_bBPTrq = false;
			nInterruptVector = INTERRUPT_14;
		}
		else if (m_bIOTrq)  // IOT команда
		{
			m_bIOTrq = false;
			nInterruptVector = INTERRUPT_20;
		}
		else if (m_bEMTrq)  // EMT команда
		{
			m_bEMTrq = false;
			nInterruptVector = INTERRUPT_30;
		}
		else if (m_bTRAPrq)  // TRAP команда
		{
			m_bTRAPrq = false;
			nInterruptVector = INTERRUPT_34;
		}
		else if (m_bVIRQErrrq)  // Ошибка передачи вектора.
		{
			m_bVIRQErrrq = false;
			m_bGetVector = false; //вектор больше не получаем, но ожидаем вероятность двойного зависания
			nInterruptVector = 0160012;
			m_nInternalTick += timing_Misk[TIMING_IDX_BUS_ERROR];
		}
		else if (m_bRPL2rq) // Двойное зависание, priority 1
		{
			m_bRPL2rq = false;
			m_nInternalTick += timing_Misk[TIMING_IDX_BUS_ERROR];

			if (m_PSW[PSW_MASKI_POS]) // Двойное зависание в HALT
			{
				// если этого не сделать, то мы насмерть зациклимся в HALT режиме
				// при отсутствии доступа к ячейкам 0177676 и 0177674, а так -
				// при любом выходе в HALT режим и невозможности в нём работать
				// вываливаемся обратно в USER с прерыванием по вектору 4
				nInterruptVector = INTERRUPT_4;
			}
			else // Двойное зависание в USER
			{
				nInterruptVector = 0160006;
			}
		}
		else if (m_bRPLYrq)
		{
			m_bRPLYrq = false;
			bCanTwiceHangup = true;
			m_nInternalTick += timing_Misk[TIMING_IDX_BUS_ERROR];

			if (m_PSW[PSW_HALT_POS] || m_PSW[PSW_MASKI_POS]) // Зависание в HALT, priority 1
			{
				nInterruptVector = 0160002;
			}
			else  // Зависание в USER, priority 1
			{
				nInterruptVector = INTERRUPT_4;
			}
		}
		else if (m_bErrInstrRq)  // неправильная команда (jmp r0 или jsr r0,r0)
		{
			m_bErrInstrRq = false;
			nInterruptVector = INTERRUPT_4;
		}
		else if (m_bRSVDrq)  // Зарезервированная команда, priority 2
		{
			m_bRSVDrq = false;
			nInterruptVector = INTERRUPT_10;
		}
		else if (m_PSW[PSW_T_POS] && !m_bWaitMode)  // T-bit, priority 3
		{
			nInterruptVector = INTERRUPT_14;
		}
		else if (!m_PSW[PSW_MASKI_POS]) //если прерывания не замаскированы
		{
			if (m_bACLOrq)  // ACLO, priority 4
			{
				m_bACLOrq = false;
				nInterruptVector = INTERRUPT_24;
				m_nInternalTick = timing_Misk[TIMING_IDX_INT];
			}
			else
			{
				if (m_bIRQ1rq && !m_PSW[PSW_HALT_POS])
				{
					//обработка прерывания по нажатию кнопки СТОП, бит 11 в PSW маскирует это прерывание
					//Прер. irq1 и halt - почти одно и то же и должны обрабатываться одинаково, но IRQ1 можно замаскировать, а HALT - нет
					//одно хреново, так - не получится сэмулировать прерывание кнопкой СТОП выполнения на середине команды
					m_bIRQ1rq = false;
					nInterruptVector = 0160002;
				}
				else if (!m_PSW[PSW_P_POS]) //если бит приоритета не установлен
				{
					if (m_bIRQ2rq)  // прерывание от таймера, priority 6
					{
						m_bIRQ2rq = false;
						nInterruptVector = INTERRUPT_100;
						m_nInternalTick = timing_Misk[TIMING_IDX_INT];
					}
					else if (m_bIRQ3rq)  // прерывание от внешнего устройства, priority 6
					{
						m_bIRQ3rq = false;
						nInterruptVector = INTERRUPT_270;
						m_nInternalTick = timing_Misk[TIMING_IDX_INT];
					}
					else if (!m_qVIRQ.empty())  // VIRQ, priority 7
					{
						//если в очереди есть что-то, достанем и обработаем
						m_bGetVector = true;
						nInterruptVector = m_qVIRQ.front();
						m_qVIRQ.pop_front();
						m_nInternalTick = timing_Misk[TIMING_IDX_INT];
					}
				}
			}
		}

		if (nInterruptVector)
		{
			if (m_bWaitMode)
			{
				m_bWaitMode = false;
				m_RON[R_PC] += 2; //возвращаем РС как должно быть после wait.
				//игра с РС сделана потому, что возникли проблемы с пошаговой трассировкой
				//в дизассемблере
			}

			if ((nInterruptVector & 0177400) == 0160000)  // HALT mode interrupt
			{
				//нельзя просто установить бит командой m_pChip->m_reg177716in |= 010;
				// т.к в реале делается операция Read-Modify-Write.
				m_pChip->SetWord(0177716, m_pChip->GetWord(0177716) | 010);
				m_bTwiceHangup = (bCanTwiceHangup && nInterruptVector == 0160002);
				m_pChip->SetWord(0177676, GetPSW()); //вот это-то и вызывает прерывание по вектору 4
				m_pChip->SetWord(0177674, m_RON[R_PC]);
				m_bTwiceHangup = false;
				m_RON[R_PC] = GetWord(nInterruptVector);
				SetPSW((GetWord(nInterruptVector + 2) & 07777) | (1 << PSW_HALT_POS));
			}
			else // USER mode interrupt
			{
				m_bTwiceHangup = (bCanTwiceHangup && nInterruptVector == INTERRUPT_4);
				// Save PC/PSW to stack
				m_RON[R_SP] -= 2;
				SetWord(m_RON[R_SP], GetPSW()); //теперь, если тут случится m_bRPLYrq, то по m_bTwiceHangup можно узнать что это двойное зависание
				m_RON[R_SP] -= 2;
				SetWord(m_RON[R_SP], m_RON[R_PC]); //или тут
				m_bTwiceHangup = false;
				m_RON[R_PC] = GetWord(nInterruptVector); //теперь, если тут случится m_bRPLYrq, то по m_bGetVector можно узнать что это ошибка передачи вектора
				SetPSW(GetWord(nInterruptVector + 2) & 0377);
				m_bGetVector = false; //выставляется в false после операций с получением вектора
			}
		}
	}

	//здесь в принципе тоже неточность. таймер должен считать независимо, а не в промежутках между
	//выполнением команд, при чтении значений таймера тут будет возникать неточность.
	m_nCmdTicks += m_nInternalTick;

	while (m_nCmdTicks >= 128)
	{
		m_nCmdTicks -= 128;
		Timerprocess(); // Обрабатываем встроенный таймер каждый 128й такт
	}

	return m_nInternalTick;
}

void CCPU::ReplyError()
{
	if (m_bGetVector)
	{
		m_bGetVector = false;
		m_bVIRQErrrq = true;
	}
	else if (m_bTwiceHangup)
	{
		m_bTwiceHangup = false;
		m_bRPL2rq = true;
	}
	else
	{
		m_bRPLYrq = true;
	}
}

/*
Если приоритет текущей задачи ниже приоритета прерывания, то ЦП прервёт работу и обработает прерывание
иначе, ЦП сперва закончит вычисления, и только потом обработает прерывание

Таблица прерываний и исключений, значения векторов, стека сохранения PSW и PC и зависимость
от маски запрета, расположены в порядке приоритета

WCPU - признак выполнения команды WAIT

| Описание                                                   |Вектор    |PSW/PC    |Маска     | Вид
|                                                            |          |          |          |
-------------------------------------------------------------|----------|----------|----------|-------
| Начальный пуск после аппаратного сброса (читается только   |          |          |          |
| стартовый адрес в старшем байте регистра, PSW всегда       |177716    |          |          |
| устанавливается в 340)                                     |          |          |          |
-------------------------------------------------------------|----------|----------|----------|-------
| Зависание шины при приёме вектора прерываний из системной  |160012    |177674    |          | Внутр.
| магистрали (177676)<-(PSW), (177674)<-(PC),                |          |          |          |
| (PC)<-(160012), (PSW)<-(160014)                            |          |          |          |
-------------------------------------------------------------|----------|----------|----------|-------
| Двойное зависание шины (повторное при попытке обработать   |160006    |177674    |          | Внутр.
| первое исключение по зависанию) (177676)<-(PSW),           |          |          |          |
| (177674)<-(PC), (PC)<-(160006), (PSW)<-(160010)            |          |          |          |
-------------------------------------------------------------|----------|----------|----------|-------
| Зависание при передаче данных по системной магистрали      |          |          |          | Внутр.
| обработка зависит от битов 10 и 11 в PSW:                  |          |          |          |
| если PSW11=0, PSW10=0                                      |000004    | SP-4     | PSW11    |
| если хотя бы один бит PSW10 и PSW11 ненулевой              |160002    |177674    | PSW10    |
-------------------------------------------------------------|----------|----------|----------|-------
| Резервный код                                              |000010    | SP-4     |          | Внутр.
-------------------------------------------------------------|----------|----------|----------|-------
| Т-бит                                                      |000014    | SP-4     | WCPU     | Внутр.
| бит 4 в слове состояния процессора PSW. Если данный бит
| имеет единичное значение то в обычном режиме вызывает
| исключение по вектору 000014. В режиме ожидания по команде
| WAIT T-бит не вызывает немедленного исключения, процессор
| ожидает прерывание, любое незамаскированное прерывание ACLO,
| IRQ1, TVE, IRQ2, IRQ3, VIRQ вызывает выход из этого режима и
| переход по вектору, соответствующему активному прерыванию
-------------------------------------------------------------|----------|----------|----------|-------
| Сбой питания (прерывание ACLO)                             |000024    | SP-4     | PSW10    | Внеш.
-------------------------------------------------------------|----------|----------|----------|-------
| Нажатие на клавишу СТОП - (прерывание IRQ1)                |160002    |177674    | PSW10    | Внеш.
| (177716)<-000010!(177716), (177676)<-(PSW), (177674)<-(PC),|          |          | PSW11    |
| (PC)<-(160002), (PSW)<-(160004)                            |          |          |          |
| при этом происходит прерывание по вектору 4                |          |          |          |
| - запрос возникает при обнаружении низкого уровня на входе
| nIRQ1. Если уровень не будет снят после обработки запроса, то
| возникнет новый запрос, так как вход чувствителен к уровню,
| а не к фронту или срезу. Может быть замаскировано единичными
| битами PSW10 или PSW11 (запрещается, если хотя бы один бит
| установлен)
-------------------------------------------------------------|----------|----------|----------|-------
| Прерывание от ВЕ-таймера, есть только в К1801ВМ1Г , может  |0270      | SP-4     | PSW10    | Внеш.
| быть замаскировано обнулением бита MON в регистре управления                       PSW7
| таймером. Также прерывание может быть запрещено битами PSW7                        MON
| и PSW10 (запрещается, если хотя бы один бит установлен)
-------------------------------------------------------------|----------|----------|----------|-------
| Прерывание от внешнего устройства (IRQ2)                   |0100      | SP-4     | PSW10    | Внеш.
                                                                                     PSW7
-------------------------------------------------------------|----------|----------|----------|-------
| Прерывание от внешнего устройства (IRQ3)                   |0270      | SP-4     | PSW10    | Внеш.
                                                                                     PSW7
-------------------------------------------------------------|----------|----------|----------|-------
| Прерывание от вычислительного устройства (VIRQ)            |считывается|SP-4     | PSW10    | Внеш.
| (прерывание от клавиатуры 60 и 274 организованы            |из сист.  |          | PSW7     |
| через него и для других устройств тоже)                    |магистрали|          |          |
-------------------------------------------------------------|----------|----------|----------|-------
| прерывание по команде HALT                                 |0160002   | 177674   |          | Внутр.
-------------------------------------------------------------|----------|----------|----------|-------
| прерывание по команде BPT                                  |0014      | SP-4     |          | Внутр.
-------------------------------------------------------------|----------|----------|----------|-------
| прерывание по команде IOT                                  |0020      | SP-4     |          | Внутр.
-------------------------------------------------------------|----------|----------|----------|-------
| прерывание по команде EMT                                  |0030      | SP-4     |          | Внутр.
-------------------------------------------------------------|----------|----------|----------|-------
| прерывание по команде TRAP                                 |0034      | SP-4     |          | Внутр.

*/

void CCPU::InterruptVIRQ(uint16_t interrupt)
{
	if (!m_qVIRQ.empty()) //если очередь не пуста
	{
		for (auto &n : m_qVIRQ) //проверим её
		{
			if (n == interrupt) //если такой вектор уже в очереди
			{
				return; //его не будем добавлять
			}
		}
	}

	m_qVIRQ.push_back(interrupt); //помещаем адрес вектора прерываний в очередь
	//!!! при этом, если будем очень быстро помещать адреса в очередь, она
	//начнёт переполняться, потому что тут не так надо делать. надо игнорировать
	// все запросы на прерывание, пока не будет обработан текущий.
	// или не подавать так много запросов.
}

uint16_t CCPU::GetSysRegs(register uint16_t addr)
{
	if (0177700 <= addr && addr < 0177714)
	{
		return m_pSysRegs[(addr >> 1) & 7];
	}

	return 0;
}

void CCPU::SetSysRegs(register uint16_t addr, register uint16_t value)
{
	if (0177700 <= addr && addr < 0177714)
	{
		register int n = (addr >> 1) & 7;
		m_pSysRegs[n] = (m_pSysRegs[n] & ~m_pSysRegsMask[n]) | (value & m_pSysRegsMask[n]);

		//тут ещё и разные действия могут происходить, например
		if (n == SYS_REG_177712)
		{
			//Если пишем в регистр управления таймером, то в счётчик сразу копируется начальное значение
			m_pSysRegs[SYS_REG_177710] = m_pSysRegs[SYS_REG_177706];
			//и инициализируем делители
			m_nTVE_Cnt = 0;

			switch (m_pSysRegs[SYS_REG_177712] & (TVE_D4 | TVE_D16))
			{
				case 0:
					m_nTVE_Divider = 1;
					break;

				case TVE_D16: //делитель на 16
					m_nTVE_Divider = 16;
					break;

				case TVE_D4: //делитель на 4
					m_nTVE_Divider = 4;
					break;

				case (TVE_D4 | TVE_D16): //делитель на 64
					m_nTVE_Divider = 64;
					break;
			}
		}
	}
}

void CCPU::SetSysRegs(register uint16_t addr, register uint8_t value)
{
	if (0177700 <= addr && addr < 0177714)
	{
		register auto v = uint16_t(value);

		if (addr & 1)
		{
			v <<= 8;
		}

		SetSysRegs(addr, v);
	}
}

void CCPU::SetSysRegsInternal(register uint16_t addr, register uint16_t value)
{
	if (0177700 <= addr && addr < 0177714)
	{
		register int n = (addr >> 1) & 7;
		m_pSysRegs[n] = value;
	}
}

void CCPU::SetSysRegsInternal(register uint16_t addr, register uint8_t value)
{
	if (0177700 <= addr && addr < 0177714)
	{
		register int n = (addr >> 1) & 7;
		register auto v = uint16_t(value);

		if (addr & 1)
		{
			v <<= 8;
			m_pSysRegs[n] &= 0377;
			m_pSysRegs[n] |= v;
		}
		else
		{
			m_pSysRegs[n] &= 0177400;
			m_pSysRegs[n] |= v;
		}
	}
}

/*
получение аргумента источника
выход:  аргумент в m_datarg
*/
void CCPU::get_src_arg()
{
	get_src_addr();

	if (m_bByteOperation)
	{
		m_datarg = (m_nMethSrc) ? GetByte(m_nSrcAddr) : LOBYTE(m_RON[m_nSrcAddr]);
	}
	else
	{
		m_datarg = (m_nMethSrc) ? GetWord(m_nSrcAddr) : m_RON[m_nSrcAddr];
	}
}

/*
получение аргумента приёмника
выход:  аргумент в m_ALU
*/
void CCPU::get_dst_arg()
{
	get_dst_addr();

	if (m_bByteOperation)
	{
		m_ALU = (m_nMethDst) ? GetByte(m_nDstAddr) : LOBYTE(m_RON[m_nDstAddr]);
	}
	else
	{
		m_ALU = (m_nMethDst) ? GetWord(m_nDstAddr) : m_RON[m_nDstAddr];
	}
}

void CCPU::set_dst_arg()
{
	if (m_bByteOperation)
	{
		if (m_nMethDst)
		{
			SetByte(m_nDstAddr, LOBYTE(m_ALU));
		}
		else
		{
			m_RON[m_nDstAddr] &= 0177400;
			m_RON[m_nDstAddr] |= LOBYTE(m_ALU);
		}
	}
	else
	{
		if (m_nMethDst)
		{
			SetWord(m_nDstAddr, LOWORD(m_ALU));
		}
		else
		{
			m_RON[m_nDstAddr] = LOWORD(m_ALU);
		}
	}
}

/*
получение адреса источника.
выход:  m_nSrcAddr - адрес аргумента источника.
*/
void CCPU::get_src_addr()
{
	m_nSrcAddr = get_arg_addr(m_nMethSrc, m_nRegSrc);
}
/*
получение адреса приёмника.
выход:  m_nDstAddr - адрес аргумента приёмника.
*/
void CCPU::get_dst_addr()
{
	m_nDstAddr = get_arg_addr(m_nMethDst, m_nRegDst);
}


uint16_t CCPU::get_arg_addr(int meth, int reg)
{
	register uint16_t arg = 0;
	register uint16_t index;

	switch (meth)
	{
		case 0: // R0,      PC
			arg = uint16_t(reg);
			break;

		case 1: // (R0),    (PC)
			arg = m_RON[reg];
			break;

		case 2: // (R0)+,   #012345
			arg = m_RON[reg];

			if (m_bByteOperation && (reg < R_SP))
			{
				m_RON[reg] += 1;
			}
			else
			{
				m_RON[reg] += 2;
			}

			break;

		case 3: // @(R0)+,  @#012345
			arg = GetWord(m_RON[reg]);
			m_RON[reg] += 2;
			break;

		case 4: // -(R0),   -(PC)
			if (m_bByteOperation && (reg < R_SP))
			{
				m_RON[reg] -= 1;
			}
			else
			{
				m_RON[reg] -= 2;
			}

			arg = m_RON[reg];
			break;

		case 5: // @-(R0),  @-(PC)
			m_RON[reg] -= 2;
			arg = GetWord(m_RON[reg]);
			break;

		case 6: // 345(R0), 345
			index = GetWord(m_RON[R_PC]);
			m_RON[R_PC] += 2;
			arg = uint16_t(index + m_RON[reg]);
			break;

		case 7: // @345(R0),@345
			index = GetWord(m_RON[R_PC]);
			m_RON[R_PC] += 2;
			arg = GetWord(uint16_t(index + m_RON[reg]));
			break;
	}

	return arg;
}

void CCPU::ExecuteUNKNOWN()
{
	m_bRSVDrq = true;
	m_nInternalTick += timing_Misk[TIMING_IDX_INT];
}


void CCPU::ExecuteWAIT()
{
	m_bWaitMode = true;
	m_RON[R_PC] -= 2; //возвращаем PC на команду wait.
	m_nInternalTick += timing_Misk[TIMING_IDX_WAIT];
}


void CCPU::ExecuteHALT()
{
	m_bHALTrq = true;
	m_nInternalTick += timing_Misk[TIMING_IDX_HALT];
}


void CCPU::ExecuteBPT()
{
	m_bBPTrq = true;
	m_nInternalTick += timing_Misk[TIMING_IDX_IOT];
}


void CCPU::ExecuteIOT()
{
	m_bIOTrq = true;
	m_nInternalTick += timing_Misk[TIMING_IDX_IOT];
}


void CCPU::ExecuteRESET()
{
	ResetCPU();
	ResetTimer();
	m_nInternalTick += timing_Misk[TIMING_IDX_RESET];
}


void CCPU::ExecuteRTI()
{
	m_RON[R_PC] = GetWord(m_RON[R_SP]);
	m_RON[R_SP] += 2;
	SetPSW(GetWord(m_RON[R_SP]) & 0377); //рти и ртт восстанавливают только мл. байт, старший обнуляют
	m_RON[R_SP] += 2;
	m_nInternalTick += timing_Misk[TIMING_IDX_RTI];
}


/*
Отличие RTT от RTI состоит лишь в том, что прерывание по Т-разряду при отработке
RTT не наступает, а выполняется следующая команда и лишь после неё происходит прерывание
*/
void CCPU::ExecuteRTT()
{
	ExecuteRTI();

	//если установлен бит T
	if (m_PSW[PSW_T_POS])
	{
		//надо сделать так, чтобы следующая команда выполнилась без прерывания
		m_bTrace_RTT = true;
	}
}

/*
1.
Утверждение, что команда START перед выполнением анализирует IRQ1 - ОШИБОЧНО.
Команды START и STEP выполняются АБСОЛЮТНО ОДИНАКОВО и при наличии IRQ1 и при отсутствии
(алгоритм см. здесь: http://zx.pk.ru/showthread.php?t=17277&postid=434967 ).
Разница лишь в том, что происходит ПОСЛЕ завершения выполнения этих команд
( в зависимости от битов загруженного PSW и активности IRQ1 ).
Если при этом установлен IRQ1, а биты 10 и 11 в загруженном командой значении PSW очищены,
то после команды START следующая команда (т.е. та команда, адрес которой команда START поместила в PC)
не выполняется, а вместо этого блок прерываний процессора 1801ВМ1 немедленно выполняет HALT_TrapTo_02.
После же команды STEP - работа блока прерываний блокируется на один шаг.
2.
Иногда для упрощения говорят, что если установлен IRQ1, а биты 10 и 11 в загруженном командой
значении PSW очищены, то после команды STEP сначала выполнится та команда, адрес которой
команда STEP поместила в PC и лишь затем произойдёт HALT_TrapTo_02 - но важно понимать,
что на самом деле такое утверждение ОШИБОЧНО. Если за тот шаг, на время которого команда
STEP заблокировала работу блока прерываний процессора, сигнал IRQ1 будет снят или
выполненной командой будет установлен бит 10 и/или бит 11 PSW, то никакого HALT_TrapTo_02
после завершения следующей за STEP команды НЕ ПРОИЗОЙДЁТ.
*/
//000010,000011,000012,000013
void CCPU::ExecuteSTART()
{
	m_pChip->SetWord(0177716, m_pChip->GetWord(0177716) & ~010);
	m_RON[R_PC] = m_pChip->GetWord(0177674);
	SetPSW(m_pChip->GetWord(0177676) & ~01400);
	m_nInternalTick += timing_Misk[TIMING_IDX_HALT];
}
//000014,000015,000016,000017
void CCPU::ExecuteSTEP()
{
	m_pChip->SetWord(0177716, m_pChip->GetWord(0177716) & ~010);
	m_RON[R_PC] = m_pChip->GetWord(0177674);
	SetPSW(m_pChip->GetWord(0177676) & ~01400);
	m_bStepMode = true;
	m_nInternalTick += timing_Misk[TIMING_IDX_HALT];
}


void CCPU::ExecuteRTS()
{
	m_RON[R_PC] = m_RON[m_nRegDst];
	m_RON[m_nRegDst] = GetWord(m_RON[R_SP]);
	m_RON[R_SP] += 2;
	m_nInternalTick += timing_Misk[TIMING_IDX_RTS];
}


//000240-257
void CCPU::ExecuteCLS()
{
	if (m_instruction & 1)
	{
		m_PSW[PSW_C_POS] = false;
	}

	if (m_instruction & (1 << 1))
	{
		m_PSW[PSW_V_POS] = false;
	}

	if (m_instruction & (1 << 2))
	{
		m_PSW[PSW_Z_POS] = false;
	}

	if (m_instruction & (1 << 3))
	{
		m_PSW[PSW_N_POS] = false;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BASE];
}
//000260-277
void CCPU::ExecuteSET()
{
	if (m_instruction & 1)
	{
		m_PSW[PSW_C_POS] = true;
	}

	if (m_instruction & (1 << 1))
	{
		m_PSW[PSW_V_POS] = true;
	}

	if (m_instruction & (1 << 2))
	{
		m_PSW[PSW_Z_POS] = true;
	}

	if (m_instruction & (1 << 3))
	{
		m_PSW[PSW_N_POS] = true;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BASE];
}


void CCPU::ExecuteJMP()
{
	if (m_nMethDst)
	{
		get_dst_addr();
		m_RON[R_PC] = m_nDstAddr;
		m_nInternalTick += timing_OneOps_JMP[m_nMethDst];
	}
	else
	{
		m_bErrInstrRq = true;
	}
}


void CCPU::ExecuteSWAB()
{
	get_dst_arg();
	m_ALU = SWAP_BYTE(m_ALU);
	register uint8_t lb = LOBYTE(m_ALU);
	SetN(!!(lb & 0200));
	SetZ(lb == 0);
	SetV(false);
	SetC(false);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteCLR()
{
	get_dst_arg();      // clr работает как и все остальные однооперандные команды,
	m_ALU = 0;          // сперва читает содержимое, потом записывает туда 0,
	SetN(false);        // в отличие от mov который сразу просто записывает
	SetZ(true);
	SetV(false);
	SetC(false);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteCOM()
{
	get_dst_arg();
	m_ALU = ~m_ALU;
	Set_NZ();
	SetV(false);
	SetC(true);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteINC()
{
	get_dst_arg();
	register uint32_t old = m_ALU++;
	Set_NZ();
	Set_V(old);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteDEC()
{
	get_dst_arg();
	register uint32_t old = m_ALU--;
	Set_NZ();
	Set_IV(old);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteNEG()
{
	get_dst_arg();
	m_ALU = ~m_ALU + 1; //т.к. m_ALU - uint32_t
	Set_NZ();

	if (m_bByteOperation) //число 0100000 не меняет знак, оно становится самим собой. поэтому считается, что V
	{
		SetV(LOBYTE(m_ALU) == 0200);
	}
	else
	{
		SetV(LOWORD(m_ALU) == 0100000);
	}

	SetC(!GetZ()); //очищается, если 0, иначе устанавливается
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteADC()
{
	get_dst_arg();
	register uint32_t old = m_ALU;
	m_ALU += (GetC() ? 1 : 0);
	Set_NZ();
	Set_V(old);
	Set_C();
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteSBC()
{
	get_dst_arg();
	register uint32_t old = m_ALU;
	m_ALU -= (GetC() ? 1 : 0);
	Set_NZ();
	Set_IV(old);
	Set_C();
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteTST()
{
	get_dst_arg();
	Set_NZ();
	SetV(false);
	SetC(false);
	m_nInternalTick += timing_OneOps_TST[m_nMethDst];
}


void CCPU::ExecuteROR()
{
	get_dst_arg(); //загружаем данные в АЛУ
	register bool bC = !!(m_ALU & 1); //в будущее С помещаем младший бит
	m_ALU >>= 1;

	if (GetC())
	{
		m_ALU |= m_Nbit;    //текущее С помещаем в старший разряд
	}

	Set_NZ();
	SetV(GetN() ^ bC);
	SetC(bC);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteROL()
{
	get_dst_arg(); //загружаем данные в АЛУ
	register bool bC = !!(m_ALU & m_Nbit); //в будущее С помещаем старший бит
	m_ALU <<= 1;

	if (GetC())
	{
		m_ALU |= 1;    //в младший бит помещаем текущее С
	}

	Set_NZ();
	SetV(GetN() ^ bC);
	SetC(bC);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteASR()
{
	get_dst_arg();
	register bool bN = !!(m_ALU & m_Nbit);
	register bool bC = !!(m_ALU & 1); //в будущее С помещаем младший бит
	m_ALU >>= 1;

	if (bN)
	{
		m_ALU |= m_Nbit;    //в старший бит помещаем текущее N, т.е. знак остаётся как был
	}

	Set_NZ();
	SetV(GetN() ^ bC);
	SetC(bC);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteASL()
{
	get_dst_arg();
	register bool bC = !!(m_ALU & m_Nbit); //в будущее С помещаем старший бит
	m_ALU <<= 1; //сдвиг
	Set_NZ();
	SetV(GetN() ^ bC);
	SetC(bC);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteSXT()
{
	get_dst_addr();

	if (GetN())
	{
		m_ALU = ~(0);
		SetZ(false);
	}
	else
	{
		m_ALU = 0;
		SetZ(true);
	}

	SetV(false);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteMTPS()
{
	get_dst_arg();
	register int t = m_ALU & ~(1 << PSW_T_POS); //этой командой невозможно установить бит Т

	for (int i = 0; i < 8; ++i) //MTPS никак не влияет на старший байт
	{
		//старший байт вообще не должен затрагиваться
		m_PSW[i] = t & 1;
		t >>= 1;
	}

	m_nInternalTick += timing_OneOps_MTPS[m_nMethDst];
}


void CCPU::ExecuteMFPS()
{
	get_dst_addr();
	//работаем с байтом, тем более что у команды бит 15 всё равно ==1
	m_ALU = (int)(char)LOBYTE(GetPSW()); //распространяем знак
	Set_NZ();
	SetV(false);

	//при записи в регистр - портит старший байт
	if (m_nMethDst == 0)
	{
		m_bByteOperation = false; //значит будем работать со словом
	}

	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteBR()
{
	m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBNE()
{
	if (!GetZ())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBEQ()
{
	if (GetZ())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBGE()
{
	if (GetN() == GetV())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBLT()
{
	if (GetN() != GetV())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBGT()
{
	if (!(GetZ() || (GetN() != GetV())))
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBLE()
{
	if (GetZ() || (GetN() != GetV()))
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBPL()
{
	if (!GetN())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBMI()
{
	if (GetN())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBHI()
{
	if (!(GetZ() || GetC()))
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBLOS()
{
	if (GetZ() || GetC())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBVC()
{
	if (!GetV())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBVS()
{
	if (GetV())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBHIS()
{
	if (!GetC())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteBLO()
{
	if (GetC())
	{
		m_RON[R_PC] += ((short)(char)LOBYTE(m_instruction)) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_BR_BASE];
}


void CCPU::ExecuteXOR()
{
	get_dst_arg();
	m_ALU ^= m_RON[m_nRegSrc];
	Set_NZ();
	SetV(false);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_XOR[m_nMethDst];
}


void CCPU::ExecuteSOB()
{
	if (--m_RON[m_nRegSrc])
	{
		m_RON[R_PC] -= (m_instruction & 077) * 2;
	}

	m_nInternalTick += timing_Misk[TIMING_IDX_SOB];
}


void CCPU::ExecuteMOV()
{
	get_src_arg();
	get_dst_addr();
	//эта команда портит старший байт регистра, если пишем в мл.байт регистра, то старший байт заполняется знаками
	m_ALU = (m_bByteOperation) ? (int)(char)LOBYTE(m_datarg) : m_datarg;
	Set_NZ();
	SetV(false);

	//если мы что-то пишем в регистр, то с ним надо работать как со словом, испортить старший байт
	if (m_bByteOperation && (m_nMethDst == 0))
	{
		m_bByteOperation = false;
	}

	set_dst_arg();
	m_nInternalTick += timing_TwoOps_MOV[m_nMethSrc * 8 + m_nMethDst];
}


void CCPU::ExecuteCMP()
{
	get_src_arg();
	get_dst_arg();
	register uint32_t dwNd = m_ALU;
	m_ALU = m_datarg - m_ALU;
	Set_NZ();
	/*
	V = (Ns ^ Nd) & ~(Nd ^ Nr)
	если операнды были разных знаков, и знак приёмника был тот же, что и знак результата
	(разности операндов), иначе чистится

	a xor b == false если a == b, ==true если a != b
	булевы переменные можно тупо сравнивать, а вот отлавливать бит приходится xor'ом
	*/
	SetV(!!(((m_datarg ^ dwNd) & ~(dwNd ^ m_ALU)) & m_Nbit));
	Set_C();
	m_nInternalTick += timing_TwoOps_CMP[m_nMethSrc * 8 + m_nMethDst];
}


void CCPU::ExecuteBIT()
{
	get_src_arg();
	get_dst_arg();
	m_ALU &= m_datarg;
	Set_NZ();
	SetV(false);
	m_nInternalTick += timing_TwoOps_CMP[m_nMethSrc * 8 + m_nMethDst];
}


void CCPU::ExecuteBIC()
{
	get_src_arg();
	get_dst_arg();
	m_ALU &= ~m_datarg;
	Set_NZ();
	SetV(false);
	set_dst_arg();
	m_nInternalTick += timing_TwoOps_BIS[m_nMethSrc * 8 + m_nMethDst];
}


void CCPU::ExecuteBIS()
{
	get_src_arg();
	get_dst_arg();
	m_ALU |= m_datarg;
	Set_NZ();
	SetV(false);
	set_dst_arg();
	m_nInternalTick += timing_TwoOps_BIS[m_nMethSrc * 8 + m_nMethDst];
}


void CCPU::ExecuteADD()
{
	get_src_arg();
	get_dst_arg();
	register uint32_t dwNd = m_ALU;
	m_ALU += m_datarg;
	Set_NZ();
	/*
	    V = ~(Ns ^ Nd) & (Nd ^ Nr)
	    V устанавливается, если оба операнда были одного знака, а результат - противоположного,
	    иначе - чистится
	*/
	SetV(!!((~(m_datarg ^ dwNd) & (dwNd ^ m_ALU)) & m_Nbit));
	Set_C();
	set_dst_arg();
	m_nInternalTick += timing_TwoOps_BIS[m_nMethSrc * 8 + m_nMethDst];
}


void CCPU::ExecuteSUB()
{
	m_bByteOperation = false; //несмотря на m_bByteOperation это словная операция
	m_Nbit = 0100000;
	get_src_arg();
	get_dst_arg();
	register uint32_t dwNd = m_ALU;
	m_ALU -= m_datarg;
	Set_NZ();
	/*
	    V = (Ns ^ Nd) & ~(Ns ^ Nr)
	    V устанавливается, если операнды были различных знаков, а знак источника был таким же,
	    как и знак результата, иначе чистится
	*/
	SetV(!!((m_datarg ^ dwNd) & ~(m_datarg ^ m_ALU) & m_Nbit));
	Set_C();
	set_dst_arg();
	m_nInternalTick += timing_TwoOps_BIS[m_nMethSrc * 8 + m_nMethDst];
}


void CCPU::ExecuteEMT()
{
	m_bEMTrq = true;
	m_nInternalTick += timing_Misk[TIMING_IDX_EMT];
}


void CCPU::ExecuteTRAP()
{
	m_bTRAPrq = true;
	m_nInternalTick += timing_Misk[TIMING_IDX_EMT];
}


void CCPU::ExecuteJSR()
{
	if (m_nMethDst)
	{
		get_dst_addr();
		m_RON[R_SP] -= 2;
		SetWord(m_RON[R_SP], m_RON[m_nRegSrc]);
		m_RON[m_nRegSrc] = m_RON[R_PC];
		m_RON[R_PC] = m_nDstAddr;
		m_nInternalTick += timing_OneOps_JSR[m_nMethDst];
	}
	else
	{
		m_bErrInstrRq = true;
	}
}


void CCPU::ExecuteMARK()
{
	m_RON[R_SP] = m_RON[R_PC] + (m_instruction & 077) * 2;
	m_RON[R_PC] = m_RON[R_R5];
	m_RON[R_R5] = GetWord(m_RON[R_SP]);
	m_RON[R_SP] += 2;
	m_nInternalTick += timing_Misk[TIMING_IDX_MARK];
}


bool CCPU::IsTrace()
{
	return (m_PSW[PSW_T_POS] && (m_instruction != PI_RTT));
}

void CCPU::PrepareCPU()
{
	if (m_pExecuteMethodMap)
	{
		return;
	}

	m_pExecuteMethodMap = new ExecuteMethodRef[65536];

	if (m_pExecuteMethodMap)
	{
		// Сначала заполняем таблицу ссылками на метод ExecuteUNKNOWN, выполняющий TRAP 10
		RegisterMethodRef(0000000, 0177777, &CCPU::ExecuteUNKNOWN);
		RegisterMethodRef(0000000, 0000000, &CCPU::ExecuteHALT);
		RegisterMethodRef(0000001, 0000001, &CCPU::ExecuteWAIT);
		RegisterMethodRef(0000002, 0000002, &CCPU::ExecuteRTI);
		RegisterMethodRef(0000003, 0000003, &CCPU::ExecuteBPT);
		RegisterMethodRef(0000004, 0000004, &CCPU::ExecuteIOT);
		RegisterMethodRef(0000005, 0000005, &CCPU::ExecuteRESET);
		RegisterMethodRef(0000006, 0000006, &CCPU::ExecuteRTT);
		RegisterMethodRef(0000010, 0000013, &CCPU::ExecuteSTART);
		RegisterMethodRef(0000014, 0000017, &CCPU::ExecuteSTEP);
		RegisterMethodRef(0000100, 0000177, &CCPU::ExecuteJMP);
		RegisterMethodRef(0000200, 0000207, &CCPU::ExecuteRTS);    // RTS / RETURN
		RegisterMethodRef(0000240, 0000257, &CCPU::ExecuteCLS);
		RegisterMethodRef(0000260, 0000277, &CCPU::ExecuteSET);
		RegisterMethodRef(0000300, 0000377, &CCPU::ExecuteSWAB);
		RegisterMethodRef(0000400, 0000777, &CCPU::ExecuteBR);
		RegisterMethodRef(0001000, 0001377, &CCPU::ExecuteBNE);
		RegisterMethodRef(0001400, 0001777, &CCPU::ExecuteBEQ);
		RegisterMethodRef(0002000, 0002377, &CCPU::ExecuteBGE);
		RegisterMethodRef(0002400, 0002777, &CCPU::ExecuteBLT);
		RegisterMethodRef(0003000, 0003377, &CCPU::ExecuteBGT);
		RegisterMethodRef(0003400, 0003777, &CCPU::ExecuteBLE);
		RegisterMethodRef(0004000, 0004777, &CCPU::ExecuteJSR);    // JSR / CALL
		RegisterMethodRef(0005000, 0005077, &CCPU::ExecuteCLR);
		RegisterMethodRef(0005100, 0005177, &CCPU::ExecuteCOM);
		RegisterMethodRef(0005200, 0005277, &CCPU::ExecuteINC);
		RegisterMethodRef(0005300, 0005377, &CCPU::ExecuteDEC);
		RegisterMethodRef(0005400, 0005477, &CCPU::ExecuteNEG);
		RegisterMethodRef(0005500, 0005577, &CCPU::ExecuteADC);
		RegisterMethodRef(0005600, 0005677, &CCPU::ExecuteSBC);
		RegisterMethodRef(0005700, 0005777, &CCPU::ExecuteTST);
		RegisterMethodRef(0006000, 0006077, &CCPU::ExecuteROR);
		RegisterMethodRef(0006100, 0006177, &CCPU::ExecuteROL);
		RegisterMethodRef(0006200, 0006277, &CCPU::ExecuteASR);
		RegisterMethodRef(0006300, 0006377, &CCPU::ExecuteASL);
		RegisterMethodRef(0006400, 0006477, &CCPU::ExecuteMARK);
		RegisterMethodRef(0006700, 0006777, &CCPU::ExecuteSXT);
		RegisterMethodRef(0010000, 0017777, &CCPU::ExecuteMOV);
		RegisterMethodRef(0020000, 0027777, &CCPU::ExecuteCMP);
		RegisterMethodRef(0030000, 0037777, &CCPU::ExecuteBIT);
		RegisterMethodRef(0040000, 0047777, &CCPU::ExecuteBIC);
		RegisterMethodRef(0050000, 0057777, &CCPU::ExecuteBIS);
		RegisterMethodRef(0060000, 0067777, &CCPU::ExecuteADD);
		RegisterMethodRef(0074000, 0074777, &CCPU::ExecuteXOR);
		RegisterMethodRef(0077000, 0077777, &CCPU::ExecuteSOB);
		RegisterMethodRef(0100000, 0100377, &CCPU::ExecuteBPL);
		RegisterMethodRef(0100400, 0100777, &CCPU::ExecuteBMI);
		RegisterMethodRef(0101000, 0101377, &CCPU::ExecuteBHI);
		RegisterMethodRef(0101400, 0101777, &CCPU::ExecuteBLOS);
		RegisterMethodRef(0102000, 0102377, &CCPU::ExecuteBVC);
		RegisterMethodRef(0102400, 0102777, &CCPU::ExecuteBVS);
		RegisterMethodRef(0103000, 0103377, &CCPU::ExecuteBHIS);    // BCC
		RegisterMethodRef(0103400, 0103777, &CCPU::ExecuteBLO);     // BCS
		RegisterMethodRef(0104000, 0104377, &CCPU::ExecuteEMT);
		RegisterMethodRef(0104400, 0104777, &CCPU::ExecuteTRAP);
		RegisterMethodRef(0105000, 0105077, &CCPU::ExecuteCLR);    // CLRB
		RegisterMethodRef(0105100, 0105177, &CCPU::ExecuteCOM);    // COMB
		RegisterMethodRef(0105200, 0105277, &CCPU::ExecuteINC);    // INCB
		RegisterMethodRef(0105300, 0105377, &CCPU::ExecuteDEC);    // DECB
		RegisterMethodRef(0105400, 0105477, &CCPU::ExecuteNEG);    // NEGB
		RegisterMethodRef(0105500, 0105577, &CCPU::ExecuteADC);    // ADCB
		RegisterMethodRef(0105600, 0105677, &CCPU::ExecuteSBC);    // SBCB
		RegisterMethodRef(0105700, 0105777, &CCPU::ExecuteTST);    // TSTB
		RegisterMethodRef(0106000, 0106077, &CCPU::ExecuteROR);    // RORB
		RegisterMethodRef(0106100, 0106177, &CCPU::ExecuteROL);    // ROLB
		RegisterMethodRef(0106200, 0106277, &CCPU::ExecuteASR);    // ASRB
		RegisterMethodRef(0106300, 0106377, &CCPU::ExecuteASL);    // ASLB
		RegisterMethodRef(0106400, 0106477, &CCPU::ExecuteMTPS);
		RegisterMethodRef(0106700, 0106777, &CCPU::ExecuteMFPS);
		RegisterMethodRef(0110000, 0117777, &CCPU::ExecuteMOV);    // MOVB
		RegisterMethodRef(0120000, 0127777, &CCPU::ExecuteCMP);    // CMPB
		RegisterMethodRef(0130000, 0137777, &CCPU::ExecuteBIT);    // BITB
		RegisterMethodRef(0140000, 0147777, &CCPU::ExecuteBIC);    // BICB
		RegisterMethodRef(0150000, 0157777, &CCPU::ExecuteBIS);    // BISB
		RegisterMethodRef(0160000, 0167777, &CCPU::ExecuteSUB);
	}
	else
	{
		CBKMessageBox(g_pstrErrors[BK_ERROR_NOTENMEMR], MB_OK);
	}
}

void CCPU::DoneCPU()
{
	SAFE_DELETE_ARRAY(m_pExecuteMethodMap);
}

void CCPU::RegisterMethodRef(uint16_t start, uint16_t end, ExecuteMethodRef methodref)
{
	for (int opcode = start; opcode <= end; ++opcode)
	{
		m_pExecuteMethodMap[opcode] = methodref;
	}
}
