/*************************************************
*           BK Emulator v3.8 for Windows         *
*************************************************/

/*
File: CPU.cpp
Version: 1.0.0
Written by: Yuriy Kalmykov <kalmykov@stoik.com>
    Copyright (c) 2002-2004 Yuriy Kalmykov

    BK Emulator is a program emulated hardware environment for running
code for BK 0010(01) in different configurations.

    This code may be used in compiled form in any way you desire.
This file or it's parts can't be redistributed without the authors
written consent, but can be modified for your private needs.
    Providing that this notice and the authors name and all copyright
notices remains intact.

    Please, an email me to know how you are using it and where. You can
ask me for any information about this below code or any attendant
knowledge.

    This file is provided "as is" with no expressed or implied warranty.
The author accepts no liability for any damage or loss of business that
this product may cause.
*/


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


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

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

// CPU timing в тактах
/*
//Это для БК10
#define PI_TIME_BASE      12
#define PI_TIME_HALT      68
#define PI_TIME_WAIT      12
#define PI_TIME_RTI       40
#define PI_TIME_RESET   1140
#define PI_TIME_BR_BASE   16
#define PI_TIME_RTS       32
#define PI_TIME_MARK      56
#define PI_TIME_EMT       64
#define PI_TIME_IOT      104
#define PI_TIME_SOB       20
#define PI_TIME_INT       80
#define PI_TIME_BUS_ERROR 64
//однооперандные расчётные
const int CCPU::timing_OneOps_TST[8]    = {12,  24, 24, 36, 28, 40, 36, 48};
const int CCPU::timing_OneOps_CLR[8]    = {12,  28, 28, 40, 32, 44, 40, 52};
const int CCPU::timing_OneOps_MTPS[8]   = {24,  32, 32, 44, 36, 48, 44, 56};
const int CCPU::timing_OneOps_XOR[8]    = {12,  32, 32, 44, 32, 44, 44, 56};
const int CCPU::timing_OneOps_JMP[8]    = { 8,  20, 24, 32, 24, 32, 32, 44};
const int CCPU::timing_OneOps_JSR[8]    = { 8,  32, 36, 40, 36, 40, 40, 52};
//двухоперандные расчётные
const int CCPU::timing_TwoOps_MOV[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[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[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Мгц
#define PI_TIME_BASE      16
#define PI_TIME_HALT      96 //
#define PI_TIME_WAIT      16
#define PI_TIME_RTI       48
#define PI_TIME_RESET   1140
#define PI_TIME_BR_BASE   16
#define PI_TIME_RTS       32
#define PI_TIME_MARK      56
#define PI_TIME_EMT       80
#define PI_TIME_IOT      128
#define PI_TIME_SOB       21
#define PI_TIME_INT       96
#define PI_TIME_BUS_ERROR 64 //
//однооперандные расчётные
const int CCPU::timing_OneOps_TST[8]    = {16,  32, 32, 40, 32, 40, 40, 48};
const int CCPU::timing_OneOps_CLR[8]    = {16,  37, 37, 48, 37, 48, 48, 58};
const int CCPU::timing_OneOps_MTPS[8]   = {26,  37, 37, 48, 37, 48, 48, 58};
const int CCPU::timing_OneOps_XOR[8]    = {16,  37, 37, 48, 40, 48, 48, 58};
const int CCPU::timing_OneOps_JMP[8]    = { 8,  24, 26, 37, 26, 37, 37, 48};
const int CCPU::timing_OneOps_JSR[8]    = { 8,  32, 37, 48, 37, 48, 48, 58};
//двухоперандные расчётные
const int CCPU::timing_TwoOps_MOV[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[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[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(NULL)
	, m_pExecuteMethodMap(NULL)
{
	m_psw = 0340;
	memset(m_RON, 0, sizeof(m_RON));
	memset(m_pSysRegs, 0, sizeof(m_pSysRegs));
	m_pSysRegsMask[0] = 7; //177700
	m_pSysRegsMask[1] = 0177777; //177702
	m_pSysRegsMask[2] = 0377; //177704
	m_pSysRegsMask[3] = 0177777; //177706
	m_pSysRegsMask[4] = 0; //177710
	m_pSysRegsMask[5] = 0377; //177712
	InitVars();
	InitCPU();
}

CCPU::~CCPU()
{
	DoneCPU();
}

void CCPU::ResetCPU()
{
	m_pSysRegs[0] = 0177740;
	m_pSysRegs[1] = 0177777;
	m_pSysRegs[2] = 0177440;
	m_psw = 0340;
	m_RON[R_PC] = GetWord(0177716) & 0177400; //тут спецом читаем ячейку памяти, чтобы можно было использовать особенности старт режима A16M
	InitVars();
	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_bTBITrq = 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_bVIRQrq = 0;
	ZeroMemory(m_pVIRQ, sizeof(m_pVIRQ));
}

void CCPU::ResetTimer()
{
	m_nCmdTicks = 0;
	m_nTimerSpeed = 0; //0x40; без разницы
	m_pSysRegs[3] = 0; // Начальное значение
	m_pSysRegs[4] = 0177777; // Счётчик
	m_pSysRegs[5] = 0177400; // Управление
}


/*
таймер

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: делитель на 4, "1"-- включён
Установка снижает скорость счёта в 4 раза (режим умножения времени на 4)
(100)бит 6: делитель на 16, "1"-- включён
Установка снижает скорость счёта в 16 раз (режим умножения времени на 16)
при одновременной установке скорость снижается, соответственно в 64 раза
(200)бит 7: EXPIRY: флаг окончания счета, устанавливается в "1" при достижении счётчиком нуля, сбрасывается только программно
биты 8-15 не используются, "1".
*/
void CCPU::Timerprocess()
{
	int divider = 0x40;
	WORD reg_177710 = m_pSysRegs[4]; // Счётчик
	WORD reg_177712 = m_pSysRegs[5]; // Управление

	//если счётчик остановлен
	if (reg_177712 & 1)
	{
		m_pSysRegs[4] = m_pSysRegs[3]; //проинициализируем регистр счётчика
		return; //выход, счётчик не работает.
	}

	if (reg_177712 & 040) // Делитель скорости на 4
	{
		divider >>= 2;
	}

	if (reg_177712 & 0100) // Делитель скорости на 16
	{
		divider >>= 4;
	}

	//если счётчик запущен
	if (reg_177712 & 020)
	{
		//обрабатываем
		m_nTimerSpeed -= divider; // уменьшаем счётчик

		if (m_nTimerSpeed <= 0)
		{
			m_nTimerSpeed = 0x40;
			// уменьшаем текущее значение таймера на 1
			/*WORD t = */
			reg_177710--;

			//if ((reg_177710 == 0177777) && (t == 0)) // если был переход через 0 (таймер обнулился)
			/*
			это неправильная идея. если начальное значение = 0, то счётчик вообще не считает.
			*/
			if (reg_177710 == 0)
			{
				if (reg_177712 & 02)    //если режим WRAPAROUND
				{
				}
				else
				{
					//если не режим WRAPAROUND
					reg_177710 = m_pSysRegs[3]; //загрузим в счётчик начальное значение

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

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

					m_pSysRegs[5] = reg_177712;
				}
			}

			m_pSysRegs[4] = reg_177710;
		}
	}
}

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

	return m_pChip->GetByte(addr);
}

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

	return m_pChip->GetWord(addr);
}

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

void CCPU::SetWord(WORD addr, WORD 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 = PI_TIME_BASE;
		}
		else
		{
			m_instruction = GetWord(m_RON[R_PC]);
			m_RON[R_PC] += 2;
			m_datarg = m_ALU = 0;
			m_bSign = ((m_instruction & 0100000) != 0);
			m_Nbit = ((m_bSign) ? 0200 : 0100000);
			WORD 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
			ExecuteMethodRef methodref = m_pExecuteMethodMap[m_instruction];
			(this->*methodref)();  // 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 // Обработка прерываний
	{
		BOOL intrMode; // TRUE = Прерывание в HALT mode, FALSE = прерывание в USER mode
		BOOL bNoPriority = (m_psw & PSW_P) == 0;
		//обработка Т бита
		m_bTBITrq = (m_psw & PSW_T) != 0;  // прерывание по T биту.
		// Считаем вектор прерывания и режим соответственно приоритету
		WORD intrVector = 0;
		BOOL currMode = ((m_psw & PSW_MASKI) != 0);  // Текущий режим процессора: TRUE = HALT mode, FALSE = USER mode

		//обработка прерывания по нажатию кнопки СТОП, бит 11 в PSW маскирует это прерывание
		//Прер. irq1 и halt - почти одно и то же и должны обрабатываться одинаково, но IRQ1 можно замаскировать, а HALT - нет
		if (m_bIRQ1rq && ((m_psw & (PSW_HALT | PSW_MASKI)) == 0))
		{
			//одно хреново, так - не получится сэмулировать прерывание кнопкой СТОП выполнения на середине команды
			m_bIRQ1rq = FALSE;
			m_pChip->SetWord(0177716, m_pChip->GetWord(0177716) | 010);
			intrVector = 2;
			intrMode = TRUE;
		}
		else if (m_bHALTrq)  // HALT команда
		{
			m_bHALTrq = FALSE;
			intrVector = 2;
			intrMode = TRUE;
		}
		else if (m_bErrInstrRq)  // неправильная команда (jmp r0 или jsr r0,r0)
		{
			m_bErrInstrRq = FALSE;
			intrVector = INTERRUPT_4;
			intrMode = FALSE;
		}
		else if (m_bBPTrq)  // BPT команда
		{
			m_bBPTrq = FALSE;
			intrVector = INTERRUPT_14;
			intrMode = FALSE;
		}
		else if (m_bIOTrq)  // IOT команда
		{
			m_bIOTrq = FALSE;
			intrVector = INTERRUPT_20;
			intrMode = FALSE;
		}
		else if (m_bEMTrq)  // EMT команда
		{
			m_bEMTrq = FALSE;
			intrVector = INTERRUPT_30;
			intrMode = FALSE;
		}
		else if (m_bTRAPrq)  // TRAP команда
		{
			m_bTRAPrq = FALSE;
			intrVector = INTERRUPT_34;
			intrMode = FALSE;
		}
		else if (m_bRPLYrq && currMode)  // Зависание в HALT, priority 1
		{
			m_bRPLYrq = FALSE;
			intrVector = 2;
			intrMode = TRUE;
			m_nInternalTick += PI_TIME_BUS_ERROR;
		}
		else if (m_bRPLYrq && !currMode)  // Зависание в USER, priority 1
		{
			m_bRPLYrq = FALSE;
			intrVector = INTERRUPT_4;
			intrMode = FALSE;
			m_nInternalTick += PI_TIME_BUS_ERROR;
		}
		else if (m_bRPL2rq && currMode)  // Двойное зависание в HALT, priority 1
		{
			// если этого не сделать, то мы насмерть зациклимся в HALT режиме
			// при отсутствии доступа к ячейкам 0177676 и 0177674, а так -
			// при любом выходе в HALT режим и невозможности в нём работать
			// вываливаемся обратно в USER с прерыванием по вектору 4
			m_bRPL2rq = FALSE;
			intrVector = INTERRUPT_4;
			intrMode = FALSE;
			m_nInternalTick += PI_TIME_BUS_ERROR;
		}
		else if (m_bRPL2rq && !currMode)  // Двойное зависание в USER, priority 1
		{
			m_bRPL2rq = FALSE;
			intrVector = 6;
			intrMode = TRUE;
			m_nInternalTick += PI_TIME_BUS_ERROR;
		}
		else if (m_bVIRQErrrq)  // Ошибка передачи вектора.
		{
			m_bVIRQErrrq = FALSE;
			m_bGetVector = FALSE; //вектор больше не получаем, но ожидаем вероятность двойного зависания
			intrVector = 012;
			intrMode = TRUE;
			m_nInternalTick += PI_TIME_BUS_ERROR;
		}
		else if (m_bRSVDrq)  // Зарезервированная команда, priority 2
		{
			m_bRSVDrq = FALSE;
			intrVector = INTERRUPT_10;
			intrMode = FALSE;
		}
		else if (m_bTBITrq && !m_bWaitMode && !currMode)  // T-bit, priority 3
		{
			m_bTBITrq = FALSE;
			intrVector = INTERRUPT_14;
			intrMode = FALSE;
		}
		else if (m_bACLOrq && bNoPriority && !currMode)  // ACLO, priority 4
		{
			m_bACLOrq = FALSE;
			intrVector = INTERRUPT_24;
			intrMode = FALSE;
			m_nInternalTick += PI_TIME_INT;
		}
		else if (m_bIRQ2rq && bNoPriority && !currMode)  // прерывание от таймера, priority 6
		{
			m_bIRQ2rq = FALSE;
			intrVector = INTERRUPT_100;
			intrMode = FALSE;
			m_nInternalTick += PI_TIME_INT;
		}
		else if (m_bIRQ3rq && bNoPriority && !currMode)  // прерывание от внешнего устройства, priority 6
		{
			m_bIRQ3rq = FALSE;
			intrVector = INTERRUPT_270;
			intrMode = FALSE;
			m_nInternalTick += PI_TIME_INT;
		}
		else if ((m_bVIRQrq > 0) && bNoPriority && !currMode)  // VIRQ, priority 7
		{
			intrMode = FALSE;
			m_bGetVector = TRUE;

			for (int irq = 0; irq <= 15; irq++)
			{
				if (m_pVIRQ[irq] != 0)
				{
					intrVector = m_pVIRQ[irq];
					m_pVIRQ[irq] = 0;
					m_bVIRQrq--;
					m_nInternalTick += PI_TIME_INT;
					break;
				}
			}

			if (intrVector == 0)
			{
				m_bVIRQrq = 0;
			}
		}

		if (intrVector)
		{
			m_bWaitMode = FALSE;

			if (intrMode)  // HALT mode interrupt
			{
				m_bTwiceHangup = (intrVector == 2) ? TRUE : FALSE;
				m_pChip->SetWord(0177676, m_psw); //вот это-то и вызывает прерывание по вектору 4
				m_pChip->SetWord(0177674, m_RON[R_PC]);
				m_bTwiceHangup = FALSE;
				intrVector += 0160000; //Таки да, у 1801ВМ1 действительно тут константа 0160000
				m_RON[R_PC] = GetWord(intrVector);
				m_psw = (GetWord(intrVector + 2) & 07777) | PSW_HALT;
			}
			else // USER mode interrupt
			{
				m_bTwiceHangup = (intrVector == INTERRUPT_4) ? TRUE : FALSE;
				// Save PC/PSW to stack
				m_RON[R_SP] -= 2;
				SetWord(m_RON[R_SP], m_psw); //теперь, если тут случится 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(intrVector); //теперь, если тут случится m_bRPLYrq, то по m_bGetVector можно узнать что это ошибка передачи вектора
				m_psw = GetWord(intrVector + 2) & 0377;
				m_bGetVector = FALSE; //выставляется в FALSE после операций с получением вектора
			}
		}
	}

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

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

	return m_nInternalTick;
}
/*
Если речь идёт об ОЗУ, то ВП1-37 работает так: на одну телевизионную строку длительностью 64мкс ровно
у неё происходит 96 циклов доступа к памяти, из которых чётные (при нумерации с 0) достаются процессору,
а нечётные -- видеоадаптеру (даже на полях видеосигнала). То есть, при частоте 3 МГЦ процессор может
читать/писать данные только каждый 4 такт, строго.
Поэтому у БК-0010 время исполнения всех команд из ОЗУ кратно 4 тактам, всегда.

Итак. ВП1-37 работает на частоте 6МГц.
 64мкс - это 384 такта 6МГц.
 96 циклов. это 384 / 96 = 4, т.е на один цикл приходится 4 такта 6МГц.
 чётные циклы процессора происходят через каждые 8 тактов 6МГц.
 Период совпадения циклов частот ВП1-37 = 6Мгц и процессора = Xclk вычисляется по формуле:
 P = 8 * Xclk /6Мгц

Далее считаем.
для команды процессора.
Xcmd тактов на частоте Fcpu |
                            | Y = (Xcmd * 6000000) / Fcpu (округлить до целого в большую сторону)
Y тактов на частоте 6МГц    |

теперь остаётся узнать куда попадает Y-й такт, на нечётный цикл или чётный.
если нечётный - округляем до чётного, в большую сторону.
Ynew = ((int(Y/4)+1) & ~1 )*4
затем обратное преобразование Xcmd = (Ynew * Fcpu) / 6000000 (без округления, просто отбросить остаток)
и получим нужное время исполнения команды в тактах.

период нам и не понадобился.
*/

void CCPU::SetIRQ1()
{
	//обработка нажатия на кнопку СТОП, или каким либо другим способом установка сигнала в IRQ1
	if (!m_bIRQ1LL)
	{
		m_bIRQ1LL = TRUE;
		m_bIRQ1rq = TRUE;
	}
}

void CCPU::UnsetIRQ1()
{
	m_bIRQ1LL = FALSE;
}

void CCPU::TickIRQ2()
{
	m_bIRQ2rq = TRUE;
}

void CCPU::TickIRQ3()
{
	m_bIRQ3rq = TRUE;
}

void CCPU::ACPowerFail()
{
	m_bACLOrq = TRUE;
}

void CCPU::DCPowerFail()
{
	m_bDCLOrq = TRUE;
}

void CCPU::InterruptVIRQ(int que, WORD interrupt)
{
	m_bVIRQrq += 1;
	m_pVIRQ[que] = interrupt;
}

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;
	}
}

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

Приоритеты векторов прерывания

Вектор  | Приоритет | Приоритет | Вид   | Описание
        | прерывания|   вектора |       |
--------|-----------|-----------|-----------------------------------------------------------------
        | 0         |           |       | Минимальный приоритет, который может прервать кто угодно
--------|-----------|-----------|-----------------------------------------------------------------
0004    | 1         | -         | Внутр.| Зависание при передаче данных по системной магистрали
        |           |           |       | или запрещённые коды в регистре команд
--------|-----------|-----------|-----------------------------------------------------------------
160012  | 1         | -         | Внутр.| Зависание при приёме вектора прерываний из системной
        |           |           |       | магистрали (177676)<-(PSW), (177674)<-(PC),
        |           |           |       | (PC)<-(160012), (PSW)<-(160014)
--------|-----------|-----------|-----------------------------------------------------------------
160006  | 1         | -         | Внутр.| Двойное зависание (177676)<-(PSW), (177674)<-(PC),
        |           |           |       | (PC)<-(160006), (PSW)<-(160010)
--------|-----------|-----------|-----------------------------------------------------------------
0010    | 1         | -         | Внутр.| Резервный код
--------|-----------|-----------|-----------------------------------------------------------------
0014    | 3         | -         | Внутр.| Т-бит
--------|-----------|-----------|-----------------------------------------------------------------
0020    | 2         | -         | Внутр.| прерывание по команде IOT
--------|-----------|-----------|-----------------------------------------------------------------
0024    | 4         | 7         | Внеш. | Сбой питания (прерывание ACLO)
--------|-----------|-----------|-----------------------------------------------------------------
0030    | 2         | -         | Внутр.| прерывание по команде EMT
--------|-----------|-----------|-----------------------------------------------------------------
0034    | 2         | -         | Внутр.| прерывание по команде TRAP
--------|-----------|-----------|-----------------------------------------------------------------
0060    | 6         | 4         |       | Прерывание от клавиатуры по верхнему регистру
--------|-----------|-----------|-----------------------------------------------------------------
0100    | 7         | 4         | Внеш. | Прерывание от внешнего устройства (IRQ2)
--------|-----------|-----------|-----------------------------------------------------------------
0270    | 7         | 4         | Внеш. | Прерывание от внешнего устройства (IRQ3)
--------|-----------|-----------|-----------------------------------------------------------------
0274    | 6         | 4         |       | Прерывание от клавиатуры по нижнему регистру
--------|-----------|-----------|-----------------------------------------------------------------
        | 5         |           | Внеш. | аппаратный останов (IRQ1 ?)
--------|-----------|-----------|-----------------------------------------------------------------
160002  | 7         | *         |       | Нажатие на клавишу СТОП - (прерывание IRQ1)
        |           |           |       | (177716)<-000010!(177716), (177676)<-(PSW), (177674)<-(PC),
        |           |           |       | (PC)<-(160002), (PSW)<-(160004)
        |           |           |       | при этом происходит прерывание по вектору 4
--------|-----------|-----------|-----------------------------------------------------------------
считыватся| 6       |           | Внеш. | Прерывание от вычислительного устройства (VIRQ)
из сист.|           |           |       | (прерывание от клавиатуры 60 и 274 организованы
магистрали|         |           |       | через него и для других устройств тоже)

приоритет вектора - это значит что прерывание выполнится если приоритет задачи <= числа, указанного
в колонке
* - внеприоритетное, т.к происходит запись в несуществующую ячейку памяти, обрабатывается как зависание
- - внутреннее, поэтому приоритет задачи не имеет значения

что получается, по сути из трёх бит мы используем 1, чё за приоритет прерывания и зачем он нужен - непонятно
т.е понятно, что если придёт прер. по вектору 0100 и от клавы - то сперва обработается прер. от клавы
*/

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

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

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

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

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

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


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

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

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

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

			if (m_bSign && (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_bSign && (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 = (WORD)(index + m_RON[reg]);
			break;

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

	return arg;
}

void CCPU::ExecuteUNKNOWN()
{
	m_bRSVDrq = TRUE;
	m_nInternalTick += PI_TIME_INT;
}


void CCPU::ExecuteWAIT()
{
	m_bWaitMode = TRUE;
	m_nInternalTick += PI_TIME_WAIT;
}


void CCPU::ExecuteHALT()
{
	//нельзя просто установить бит командой m_pChip->m_reg177716in |= 010;
	// т.к  нужно чтобы борда зафиксировала запись в регистр.
	m_pChip->SetWord(0177716, m_pChip->GetWord(0177716) | 010);
	m_bHALTrq = TRUE;
	m_nInternalTick += PI_TIME_HALT;
}


void CCPU::ExecuteBPT()
{
	m_bBPTrq = TRUE;
	m_nInternalTick += PI_TIME_IOT;
}


void CCPU::ExecuteIOT()
{
	m_bIOTrq = TRUE;
	m_nInternalTick += PI_TIME_IOT;
}


void CCPU::ExecuteRESET()
{
	m_pChip->ResetDevices();
	ResetTimer();
	m_nInternalTick += PI_TIME_RESET;
}


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


/*
Отличие RTT от RTI состоит лишь в том, что прерывание по Т-разряду при отработке
RTT не наступает, а выполняется следующая команда и лишь после неё происходит прерывание
*/
void CCPU::ExecuteRTT()
{
	m_RON[R_PC] = GetWord(m_RON[R_SP]);
	m_RON[R_SP] += 2;
	m_psw = GetWord(m_RON[R_SP]) & 0377; //рти и ртт восстанавливают только мл. байт, старший обнуляют
	m_RON[R_SP] += 2;
	m_nInternalTick += PI_TIME_RTI;

	//если установлен бит T
	if (m_psw & PSW_T)
	{
		//надо сделать так, чтобы следующая команда выполнилась без прерывания
		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);
	m_psw = m_pChip->GetWord(0177676) & ~01400;
	m_nInternalTick += PI_TIME_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);
	m_psw = m_pChip->GetWord(0177676) & ~01400;
	m_bStepMode = TRUE;
	m_nInternalTick += PI_TIME_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 += PI_TIME_RTS;
}


//000240-257
void CCPU::ExecuteCLS()
{
	m_psw &= ~(m_instruction & 0x0f);
	m_nInternalTick += PI_TIME_BASE;
}
//000260-277
void CCPU::ExecuteSET()
{
	m_psw |= (m_instruction & 0x0f);
	m_nInternalTick += PI_TIME_BASE;
}


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


void CCPU::ExecuteSWAB()
{
	get_dst_arg();
	m_ALU = SWAP_BYTE(m_ALU);
	SetN((LOBYTE(m_ALU) & 0200) != 0);
	SetZ(LOBYTE(m_ALU) == 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();
	DWORD old = m_ALU;
	m_ALU++;
	Set_NZ();
	Set_V(old);
	set_dst_arg();
	m_nInternalTick += timing_OneOps_CLR[m_nMethDst];
}


void CCPU::ExecuteDEC()
{
	get_dst_arg();
	DWORD old = m_ALU;
	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 - DWORD
	Set_NZ();

	if (m_bSign) //число 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();
	DWORD 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();
	DWORD 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(); //загружаем данные в АЛУ
	BOOL bC = (m_ALU & 1) != 0; //в будущее С помещаем младший бит
	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(); //загружаем данные в АЛУ
	BOOL bC = (m_ALU & m_Nbit) != 0; //в будущее С помещаем старший бит
	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();
	BOOL bN = (m_ALU & m_Nbit) != 0;
	BOOL bC = (m_ALU & 1) != 0; //в будущее С помещаем младший бит
	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();
	BOOL bC = (m_ALU & m_Nbit) != 0; //в будущее С помещаем старший бит
	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 = 0xffffffff;
		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();
	m_ALU &= ~PSW_T; //этой командой невозможно установить бит Т
	m_psw &= 0177400; //MTPS никак не влияет на старший байт
	m_psw |= LOBYTE(m_ALU); //старший байт вообще не должен затрагиваться
	m_nInternalTick += timing_OneOps_MTPS[m_nMethDst];
}


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

	//при записи в регистр - портит старший байт
	if (m_nMethDst == 0)
	{
		m_bSign = 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 += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_BR_BASE;
}


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

	m_nInternalTick += PI_TIME_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 += PI_TIME_SOB;
}


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

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

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


void CCPU::ExecuteCMP()
{
	get_src_arg();
	get_dst_arg();
	DWORD 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) != 0);
	Set_C();
	m_nInternalTick += timing_TwoOps_CMP[m_nMethSrc][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][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][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][m_nMethDst];
}


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


void CCPU::ExecuteSUB()
{
	m_bSign = FALSE; //несмотря на m_bSign это словная операция
	m_Nbit = 0100000;
	get_src_arg();
	get_dst_arg();
	DWORD 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) != 0);
	Set_C();
	set_dst_arg();
	m_nInternalTick += timing_TwoOps_BIS[m_nMethSrc][m_nMethDst];
}


void CCPU::ExecuteEMT()
{
	m_bEMTrq = TRUE;
	m_nInternalTick += PI_TIME_EMT;
}


void CCPU::ExecuteTRAP()
{
	m_bTRAPrq = TRUE;
	m_nInternalTick += PI_TIME_EMT;
}


void CCPU::ExecuteJSR()
{
	if (m_nMethDst == 0)
	{
		m_bErrInstrRq = TRUE;
	}
	else
	{
		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];
	}
}


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 += PI_TIME_MARK;
}


BOOL CCPU::IsTrace()
{
	if ((m_psw & PSW_T) && (m_instruction != PI_RTT))
	{
		return TRUE;
	}

	return FALSE;
}

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

	m_pExecuteMethodMap = new CCPU::ExecuteMethodRef[sizeof(CCPU::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(WORD start, WORD end, ExecuteMethodRef methodref)
{
	for (int opcode = start; opcode <= end; opcode++)
	{
		m_pExecuteMethodMap[opcode] = methodref;
	}
}
