// rev 1.00
// VM1CPPDlg.cpp : файл реализации
//

#include "stdafx.h"
#include "VM1CPP.h"
#include "VM1CPPDlg.h"
#include "afxdialogex.h"


#ifdef _DEBUG
#define new DEBUG_NEW
#endif

uint16_t OctStringToWord(const CString &str)
{
	int word = 0;
	_stscanf_s(str, _T("%o"), &word);
	return (uint16_t)word;
}

CString WordToOctString(uint16_t word)
{
	CString dst;
	LPTSTR buf = dst.GetBufferSetLength(7);

	for (int i = 5; i >= 0; --i)
	{
		buf[i] = TCHAR(060 + (word & 7));
		word >>= 3;
	}

	buf[6] = 0;
	dst.ReleaseBuffer();
	return dst;
}
CString ByteToOctString(uint8_t byte)
{
	CString dst;
	LPTSTR buf = dst.GetBufferSetLength(7);

	for (int i = 2; i >= 0; --i)
	{
		buf[i] = TCHAR(060 + (byte & 7));
		byte >>= 3;
	}

	buf[3] = 0;
	dst.ReleaseBuffer();
	return dst;
}


// Диалоговое окно CAboutDlg используется для описания сведений о приложении

class CAboutDlg : public CDialog
{
	public:
		CAboutDlg();

// Данные диалогового окна
		enum { IDD = IDD_ABOUTBOX };

	protected:
		virtual void DoDataExchange(CDataExchange *pDX) override;    // поддержка DDX/DDV

// Реализация
	protected:
		DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange *pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// диалоговое окно CVM1CPPDlg

static UINT nIDCID[20] =
{
	IDC_EDIT_DBG_R0, IDC_EDIT_DBG_R1, IDC_EDIT_DBG_R2, IDC_EDIT_DBG_R3,
	IDC_EDIT_DBG_R4, IDC_EDIT_DBG_R5, IDC_EDIT_DBG_SP, IDC_EDIT_DBG_PC,
	IDC_EDIT_DBG_R8, IDC_EDIT_DBG_R9, IDC_EDIT_DBG_R10, IDC_EDIT_DBG_R11,
	IDC_EDIT_DBG_R12, IDC_EDIT_DBG_R13, IDC_EDIT_DBG_PSW, IDC_EDIT_DBG_Q,
	IDC_EDIT_DBG_IR, IDC_EDIT_DBG_A, IDC_EDIT_DBG_X, IDC_EDIT_DBG_Y
};
DISPLAY_MODE_PARAM CVM1CPPDlg::m_dmarray[NUMBER_DISPLAY_MODE] =
{
	{ DD_WORD_VIEW, _T("uint16_t") },
	{ DD_BYTE_VIEW, _T("uint8_t") }
};

CVM1CPPDlg::CVM1CPPDlg(CWnd *pParent /*=NULL*/)
	: CDialog(CVM1CPPDlg::IDD, pParent)
	, m_pWorkerThread(nullptr)
	, m_bPause(FALSE)
	, m_bFirstStep(true)
	, m_bStepPhase(false)
	, m_currentddm(0)
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CVM1CPPDlg::DoDataExchange(CDataExchange *pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Check(pDX, IDC_CHECK_PAUSE, m_bPause);
	DDX_Control(pDX, IDC_MD_MEMORY, m_listMemory);
	DDX_Control(pDX, IDC_DA_DUMP_ADDRESS, m_AddrEdit);
}

BEGIN_MESSAGE_MAP(CVM1CPPDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_DESTROY()
	ON_BN_CLICKED(IDC_BUTTON_START, &CVM1CPPDlg::OnBnClickedButtonStart)
	ON_BN_CLICKED(IDC_BUTTON_STOP, &CVM1CPPDlg::OnBnClickedButtonStop)
	ON_BN_CLICKED(IDC_CHECK_PAUSE, &CVM1CPPDlg::OnBnClickedCheckPause)
	ON_MESSAGE(WM_ONWORKED_THREAD_STOPPED, &CVM1CPPDlg::OnWorkedThreadStopped)
	ON_BN_CLICKED(IDC_BUTTON_CLOCKSTEP, &CVM1CPPDlg::OnBnClickedButtonClockstep)
	ON_BN_CLICKED(IDC_BUTTON_HALFSTEP, &CVM1CPPDlg::OnBnClickedButtonHalfstep)
	ON_MESSAGE(WM_ENTEREDIT_VALUE_UPDATE, &CVM1CPPDlg::OnDumpAddressChange)
	ON_NOTIFY(LVN_ITEMCHANGED, IDC_MD_MEMORY, &CVM1CPPDlg::OnLvnItemchangedMdMemory)
	ON_BN_CLICKED(IDC_BUTTON_DUMP_MODE, &CVM1CPPDlg::OnBnClickedButtonDumpMode)
END_MESSAGE_MAP()


// обработчики сообщений CVM1CPPDlg

BOOL CVM1CPPDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	// Добавление пункта "О программе..." в системное меню.
	// IDM_ABOUTBOX должен быть в пределах системной команды.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);
	CMenu *pSysMenu = GetSystemMenu(FALSE);

	if (pSysMenu != nullptr)
	{
		BOOL bNameValid;
		CString strAboutMenu;
		bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
		ASSERT(bNameValid);

		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Задаёт значок для этого диалогового окна.  Среда делает это автоматически,
	//  если главное окно приложения не является диалоговым
	SetIcon(m_hIcon, TRUE);         // Крупный значок
	SetIcon(m_hIcon, FALSE);        // Мелкий значок
	// TODO: добавьте дополнительную инициализацию
	LOGFONT lf;
	::GetObject((HGDIOBJ)::SendMessage(GetSafeHwnd(), WM_GETFONT, 0, 0), sizeof(LOGFONT), &lf);
	_tcscpy_s(lf.lfFaceName, LF_FACESIZE, _T("Courier New\0"));

	if (hFont.CreateFontIndirect(&lf) == NULL)
	{
		_tcscpy_s(lf.lfFaceName, LF_FACESIZE, _T("Lucida Console\0"));
		hFont.CreateFontIndirect(&lf);
	}

	for (unsigned int i : nIDCID)
	{
		GetDlgItem(i)->SetFont(&hFont);
	}

	m_listMemory.SetFont(&hFont);
	GetDlgItem(IDC_EDIT_PLM)->SetFont(&hFont);
	GetDlgItem(IDC_EDIT_PLI)->SetFont(&hFont);
	GetDlgItem(IDC_EDIT_MISC)->SetFont(&hFont);
	//GetDlgItem(IDC_BUTTON_START)->EnableWindow(FALSE);
	GetDlgItem(IDC_CHECK_PAUSE)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);

	if (!m_Board.init())
	{
		MessageBox(_T("Не удалось инициализировать объект Board."), MB_OK);
		return FALSE;
	}

	m_Board.AttachDebugger(&m_Debugger);
	m_listMemory.Init(m_dmarray[m_currentddm].mode);
	return TRUE;  // возврат значения TRUE, если фокус не передан элементу управления
}

void CVM1CPPDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// При добавлении кнопки свертывания в диалоговое окно нужно воспользоваться приведенным ниже кодом,
//  чтобы нарисовать значок.  Для приложений MFC, использующих модель документов или представлений,
//  это автоматически выполняется рабочей областью.

void CVM1CPPDlg::OnPaint()
{
	if (IsIconic())
	{
		CPaintDC dc(this); // контекст устройства для рисования
		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
		// Выравнивание значка по центру клиентского прямоугольника
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;
		// Нарисуйте значок
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// Система вызывает эту функцию для получения отображения курсора при перемещении
//  свернутого окна.
HCURSOR CVM1CPPDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}



void CVM1CPPDlg::OnBnClickedButtonStart()
{
	//запускаем поток
	StartWorkerThread();
	GetDlgItem(IDC_BUTTON_START)->EnableWindow(FALSE);
	GetDlgItem(IDC_CHECK_PAUSE)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_CLOCKSTEP)->EnableWindow(FALSE);
	GetDlgItem(IDC_BUTTON_HALFSTEP)->EnableWindow(FALSE);
	m_bFirstStep = false;
}



void CVM1CPPDlg::OnBnClickedButtonStop()
{
	//останавливаем поток
	GetDlgItem(IDC_CHECK_PAUSE)->EnableWindow(FALSE);
	StopWorkerThread();
	GetDlgItem(IDC_BUTTON_CLOCKSTEP)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_HALFSTEP)->EnableWindow(TRUE);
	m_bFirstStep = true;
}


void CVM1CPPDlg::OnBnClickedCheckPause()
{
	m_bPause = (IsDlgButtonChecked(IDC_CHECK_PAUSE) != 0); //проверим, нажата ли кнопка

	if (m_bPause)
	{
		TRACE(_T("Пауза Вкл.\n"));
		GetDlgItem(IDC_BUTTON_CLOCKSTEP)->EnableWindow(TRUE);
		GetDlgItem(IDC_BUTTON_HALFSTEP)->EnableWindow(TRUE);
		m_pWorkerThread->SuspendThread();   //если нажата - то засуспендим поток
	}
	else
	{
		TRACE(_T("Пауза Выкл.\n"));
		GetDlgItem(IDC_BUTTON_CLOCKSTEP)->EnableWindow(FALSE);
		GetDlgItem(IDC_BUTTON_HALFSTEP)->EnableWindow(FALSE);

		// если мы находились на середине выполнения клока, то
		// предыдущий полуклок доделать надо.
		// но только не при первичном шаге, там уже без разницы.
		if (m_bStepPhase)
		{
			m_Board.mainCycle_p();
		}

		m_pWorkerThread->ResumeThread();    //если отжата - разбудим поток
	}
}

void CVM1CPPDlg::OnBnClickedButtonClockstep()
{
	if (!m_lockStarting.IsLocked() && m_bFirstStep)
	{
		//если поток не работает
		m_Board.cpuInit(); //то нужно проинициализировать модель.
		m_bFirstStep = false;
	}
	else
	{
		// если мы вызвали шаг в 1 клок, но при этом находились на середине предыдущего клока, -
		// предыдущий полуклок доделать надо.
		// но только не при первичном шаге, там уже без разницы.
		if (m_bStepPhase)
		{
			m_Board.mainCycle_p();
		}
	}

	register int nClk = m_Board.mainCycle();
	m_Debugger.DisAsm(nClk); //дизассемблируем текущую инструкцию
	DebugOut(); //отладочный вывод
}


void CVM1CPPDlg::OnBnClickedButtonHalfstep()
{
	if (!m_lockStarting.IsLocked() && m_bFirstStep)
	{
		//если поток не работает
		m_Board.cpuInit(); //то нужно проинициализировать модель.
		m_bFirstStep = false;
	}

	if (m_bStepPhase)
	{
		m_Board.mainCycle_p();
	}
	else
	{
		m_Board.mainCycle_n();
	}

	register int nClk = m_Board.mainCycle();
	m_Debugger.DisAsm(nClk); //дизассемблируем текущую инструкцию
	m_bStepPhase = !m_bStepPhase;
}


LRESULT CVM1CPPDlg::OnWorkedThreadStopped(WPARAM wParam, LPARAM lParam)
{
	m_lockStop.UnLock();
	TRACE(_T("Всё, остановили рабочий поток.\n"));
	GetDlgItem(IDC_BUTTON_START)->EnableWindow(TRUE);
	GetDlgItem(IDC_BUTTON_STOP)->EnableWindow(FALSE);
	return S_OK;
}

UINT AFX_CDECL CVM1CPPDlg::WorkerThreadWrapper(LPVOID lpParam)
{
	auto pThis = reinterpret_cast<CVM1CPPDlg *>(lpParam);
	return pThis->WorkerThreadFunc();
}

//частота работы процессора
constexpr auto CPU_SPEED = 3000000;
//количество фреймов в секунду
constexpr auto CPU_FRAMES_PER_SECOND = 25;

UINT CVM1CPPDlg::WorkerThreadFunc()
{
	m_lockStarting.Lock(); //устанавливаем событие работы потока
	CString strClk;
	DWORD msec = GetTickCount();
	DWORD mtmpsec;
	int nClockRate = 0;
	int nCount = 0;
	m_Board.cpuInit();

	while (!m_lockStop.IsLocked()) // пока не придёт событие остановки потока
	{
		register int nClk = m_Board.mainCycle();
		m_Debugger.DisAsm(nClk); //дизассемблируем текущую инструкцию
		nClockRate++; //счётчик частоты

		if ((mtmpsec = GetTickCount()) - msec >= 1000 / CPU_FRAMES_PER_SECOND)
		{
			msec = mtmpsec;
			DebugOut(); //отладочный вывод, каждые 40 мс
			m_Board.Getscreen()->DrawScreen(true);

			if (--nCount <= 0) //каждую секунду - обновление частоты
			{
				nCount = CPU_FRAMES_PER_SECOND;
				strClk.Format(_T("%08d"), nClockRate);
				nClockRate = 0;
				SetDlgItemText(IDC_STATIC_CLK, strClk);
				strClk.Format(_T("%d"), m_Board.Getscreen()->GetFPS());
				SetDlgItemText(IDC_STATIC_FPS, strClk);
			}
		}
	}

	m_lockStarting.UnLock(); //сбрасываем событие, поток работу закончил
	TRACE(_T("Рабочий поток завершил работу.\n"));
	PostMessage(WM_ONWORKED_THREAD_STOPPED, 0, 0);
	return 0;
}


//функция CPU работает очень-очень-очень медленно. Эмуляция в реальном времени невозможна.
/*
UINT CVM1CPPDlg::WorkerThreadFunc()
{
    m_lockStarting.Lock(); //устанавливаем событие работы потока

    DWORD_PTR oldmask = SetThreadAffinityMask(GetCurrentThread(), 1);
    LARGE_INTEGER liDueTime;    //интервал таймера
    liDueTime.QuadPart = -10000000LL / CPU_FRAMES_PER_SECOND;
    // создаём waitable timer.
    HANDLE hWaitableTimer = CreateWaitableTimer(nullptr, FALSE, nullptr);
    if (nullptr == hWaitableTimer)
    {
        return -1;
    }

    CString strClk;
    int nClockRate = 0;
    int nCount = 0;
    m_Board.cpuInit();

    while (!m_lockStop.IsLocked()) // пока не придёт событие остановки потока
    {
        // Устанавливаем таймер.
        SetWaitableTimer(hWaitableTimer, &liDueTime, 0, nullptr, nullptr, FALSE);

        for (int i = 0; i < CPU_SPEED / CPU_FRAMES_PER_SECOND; ++i)
        {
            register int nClk = m_Board.mainCycle();
            m_Debugger.DisAsm(nClk); //дизассемблируем текущую инструкцию
            nClockRate++; //счётчик частоты
        }

        DebugOut();

        if (--nCount <= 0) //каждую секунду - обновление частоты
        {
            nCount = CPU_FRAMES_PER_SECOND;
            strClk.Format(_T("%08d"), nClockRate);
            nClockRate = 0;
            SetDlgItemText(IDC_STATIC_CLK, strClk);
            strClk.Format(_T("%d"), m_Board.Getscreen()->GetFPS());
            SetDlgItemText(IDC_STATIC_FPS, strClk);
        }

        // ждём срабатывания таймера.
        WaitForSingleObject(hWaitableTimer, INFINITE);
    }

    CancelWaitableTimer(hWaitableTimer);
    CloseHandle(hWaitableTimer);
    SetThreadAffinityMask(GetCurrentThread(), oldmask);
    m_lockStarting.UnLock(); //сбрасываем событие, поток работу закончил
    TRACE(_T("Рабочий поток завершил работу.\n"));
    PostMessage(WM_ONWORKED_THREAD_STOPPED, 0, 0);
    return 0;
}
//*/

void CVM1CPPDlg::StopWorkerThread()
{
	if (m_lockStarting.IsLocked()) //если поток работает
	{
		TRACE(_T("Останавливаем рабочий поток.\n"));
		CleanPause();   //снимаем с паузы, ибо засуспенженный поток не останавливается культурно
		m_lockStop.Lock();     //взводим флаг остановки потока
	}
}


bool CVM1CPPDlg::StartWorkerThread()
{
	//надо обрабатывать только если поток не работает
	if (!m_lockStarting.IsLocked())
	{
		if ((m_pWorkerThread = AfxBeginThread(
		                           WorkerThreadWrapper,
		                           (void *)this,
		                           THREAD_PRIORITY_NORMAL,
		                           0,
		                           CREATE_SUSPENDED)
		    ) == nullptr)
		{
			MessageBox(_T("Не удалось создать рабочий поток."), MB_OK);
			return false;
		}

		m_pWorkerThread->m_bAutoDelete = TRUE;
		m_pWorkerThread->ResumeThread();
		TRACE(_T("Запускаем рабочий поток.\n"));
	}

	return true;
}

void CVM1CPPDlg::CleanPause()
{
	if (m_bPause)   //если пауза уже нажата
	{
		CheckDlgButton(IDC_CHECK_PAUSE, FALSE); //отжать кнопку
		OnBnClickedCheckPause();                //и обработать событие.
	}
}

void CVM1CPPDlg::OnDestroy()
{
	OnBnClickedButtonStop();

	while (m_lockStarting.IsLocked())
	{
		Sleep(100);
	}

	hFont.DeleteObject();
	CDialog::OnDestroy();
}

//вывод выходных параметров матрицы ПЛМ, после операции
void CVM1CPPDlg::DebugOut()
{
	CString str;

	for (int i = 0; i < 14; ++i)
	{
		str.Format(_T("%06o"), m_Debugger.GetRegister(static_cast<CDebugger::VM1REGS>(i)));
		SetDlgItemText(nIDCID[i], str);
	}

	str.Format(_T("%06o"), m_Debugger.GetRegister(CDebugger::PSW));
	SetDlgItemText(IDC_EDIT_DBG_PSW, str);
	str.Format(_T("%06o"), m_Debugger.GetRegister(CDebugger::Q));
	SetDlgItemText(IDC_EDIT_DBG_Q, str);
	str.Format(_T("%06o"), m_Debugger.GetRegister(CDebugger::A));
	SetDlgItemText(IDC_EDIT_DBG_A, str);
	str.Format(_T("%06o"), m_Debugger.GetRegister(CDebugger::IR));
	SetDlgItemText(IDC_EDIT_DBG_IR, str);
	str.Format(_T("%06o"), m_Debugger.GetRegister(CDebugger::X));
	SetDlgItemText(IDC_EDIT_DBG_X, str);
	str.Format(_T("%06o"), m_Debugger.GetRegister(CDebugger::Y));
	SetDlgItemText(IDC_EDIT_DBG_Y, str);
	DebugPLMInterpretator();
	DebugPLIInterpretator();
	DebugMiscOut();
}



void CVM1CPPDlg::DebugInPLMInterpretator(uint16_t ir, uint16_t mr, CString &strOut)
{
	CString strMR;

	for (int i = 14; i >= 12; i--)
	{
		strMR += (mr & g_WordBitMask[i]) ? _T("1") : _T("0");
	}

	strMR += _T(" ");
	strMR += (mr & (1 << 11)) ? _T("T") : _T(".");
	strMR += (mr & (1 << 10)) ? _T("N") : _T(".");
	strMR += (mr & (1 << 9)) ? _T("Z") : _T(".");
	strMR += (mr & (1 << 8)) ? _T("V") : _T(".");
	strMR += (mr & (1 << 7)) ? _T("C") : _T(".");
	strOut.Format(_T("[0x%02x] IR: %06o | MR: %s"), (~mr & 0x7f), ir, strMR);
}

//операции на шине МПИ
static TCHAR *strQbusOp[8] =
{
	_T("нет операции"),
	_T("нет операции"),
	_T("запись слова"),
	_T("запись байта"),
	_T("чтение слова"),
	_T("чтение байта"),
	_T("чтение-модификация-запись слова"),
	_T("чтение-модификация-запись байта")
};

//операции с регистром IR
static TCHAR *strIROptypes[8] =
{
	_T("нет операции"),
	_T("ждать завершения чтения инструкции, тек. код - RTT"),
	_T("нет операции, Q := @МПИ"),
	_T("запуск чтения следующей инструкции"),
	_T("ранняя предвыборка следующей инструкции"),
	_T("только проверка прерываний, тек. код - WAIT"),
	_T("нет операции"),
	_T("ожидание завершения чтения следующей инструкции, тек. код НЕ RTT")
};

//таблица перекодирования номера регистра
static int pRegCode[16] =
{
	017, //0000 -> 1111
	007, //0001 -> 0111
	013, //0010 -> 1011
	003, //0011 -> 0011
	015, //0100 -> 1101
	005, //0101 -> 0101
	011, //0110 -> 1001
	001, //0111 -> 0001
	016, //1000 -> 1110
	006, //1001 -> 0110
	012, //1010 -> 1010
	002, //1011 -> 0010
	014, //1100 -> 1100
	004, //1101 -> 0100
	010, //1110 -> 1000
	000  //1111 -> 0000
};

//имена регистров, человекопонятные
static TCHAR *strRegName[16] =
{
	_T("R0"),   //1111 -> 0000
	_T("R1"),   //0111 -> 0001
	_T("R2"),   //1011 -> 0010
	_T("R3"),   //0011 -> 0011
	_T("R4"),   //1101 -> 0100
	_T("R5"),   //0101 -> 0101
	_T("SP"),   //1001 -> 0110
	_T("PC"),   //0001 -> 0111
	_T("R8"),   //1110 -> 1000
	_T("R9"),   //0110 -> 1001
	_T("R10"),  //1010 -> 1010
	_T("R11"),  //0010 -> 1011
	_T("R12"),  //1100 -> 1100
	_T("R13"),  //0100 -> 1101
	_T("PSW"),  //1000 -> 1110
	_T("Q")     //0000 -> 1111
};

//номера регистров, бинарные
static TCHAR *strRegCode[16] =
{
	_T("0000"),
	_T("0001"),
	_T("0010"),
	_T("0011"),
	_T("0100"),
	_T("0101"),
	_T("0110"),
	_T("0111"),
	_T("1000"),
	_T("1001"),
	_T("1010"),
	_T("1011"),
	_T("1100"),
	_T("1101"),
	_T("1110"),
	_T("1111")
};

//констранта на шине Y
static TCHAR *strYConst[16] =
{
	_T("IR[3:0] (SEx/CLx)"),    //1111 -> 0000
	_T("000340"),               //0111 -> 0001
	_T("таблица VSEL"),         //1011 -> 0010
	_T("000002"),               //0011 -> 0011
	_T("IR[5:0]*2"),            //1101 -> 0100
	_T("177716"),               //0101 -> 0101
	_T("177777"),               //1001 -> 0110
	_T("000001"),               //0001 -> 0111
	_T("IR[7:0]*2 со знаком"),  //1110 -> 1000
	_T("100000"),               //0110 -> 1001
	_T("177676"),               //1010 -> 1010
	_T("000020"),               //0010 -> 1011
	_T("флаг переноса C"),      //1100 -> 1100
	_T("177400"),               //0100 -> 1101
	_T("000010"),               //1000 -> 1110
	_T("вектор VIRQ")           //0000 -> 1111
};

//вектор на шине Y
static TCHAR *strVsel[16] =
{
	_T("160006"),           //0000
	_T("000020"),           //0001
	_T("000010"),           //0010
	_T("000014"),           //0011
	_T("000004"),           //0100
	_T("177716+PA"),        //0101
	_T("000030"),           //0110
	_T("160012"),           //0111
	_T("000270"),           //1000
	_T("000024"),           //1001
	_T("000100"),           //1010
	_T("160002"),           //1011
	_T("000034"),           //1100
	_T("IR opcode"),        //1101
	_T("0(start @177702)"), //1110
	_T("0(unused(iako))")   //1111
};

// сдвиговая операция АЛУ
static TCHAR *strALURoll[8] =
{
	_T("Сдвиг влево, в младший бит вдвигается 0, ASL"),
	_T("Сдвиг влево, в младший бит вдвигается 0, ASL"),
	_T("Сдвиг влево, в младший бит вдвигается С, ROL"),
	_T("Перестановка байтов на шине X"),
	_T("Сдвиг вправо, старший бит копируется, ASR"),
	_T("Сдвиг вправо, в старший бит вдвигается перенос из старшего разряда АЛУ в текущей операции"),
	_T("Сдвиг вправо, в старший бит вдвигается C, ROR"),
	_T("Нет сдвига")
};

//расшифровка действий регистра MCIR
static TCHAR *strMcir[8] =
{
	_T("Ожидание прерывания"),                              //000
	_T("Нет активных незамаскированных запросов"),          //001
	_T("Переход в пультовый режим"),                        //010
	_T("Переход в пультовый режим"),                        //011
	_T("Требуется обработка прерывания"),                   //100
	_T("Требуется обработка прерывания"),                   //101
	_T("Выполнить последовательность начального старта"),   //110
	_T("Выполнить последовательность начального старта"),   //111
};

void CVM1CPPDlg::DebugMiscOut()
{
	CString strOut = _T("");
	CString str;
	auto pRegs = m_Debugger.GetRegs();
	str.Format(_T("vsel: %02d (%s)\r\n"), pRegs->vsel, strVsel[pRegs->vsel]);
	strOut += str;
	str.Format(_T("vmux: %06o  cmux: %06o    "), pRegs->vmux, pRegs->cmux);
	strOut += str;
	//сбоку выведем флаги условий
	//спева freg
	str = _T("freg: ");
	str += (pRegs->freg & (1 << 3)) ? _T('N') : _T('.');
	str += (pRegs->freg & (1 << 2)) ? _T('Z') : _T('.');
	str += (pRegs->freg & (1 << 1)) ? _T('V') : _T('.');
	str += (pRegs->freg & (1 << 0)) ? _T('C') : _T('.');
	strOut += str + _T("\r\n");
	str.Format(_T("xreg: %06o  yreg: %06o    "), pRegs->xreg, pRegs->yreg);
	strOut += str;
	//потом PSW
	str = _T("PSW : ");
	str += (pRegs->gpr[14] & (1 << 3)) ? _T('N') : _T('.');
	str += (pRegs->gpr[14] & (1 << 2)) ? _T('Z') : _T('.');
	str += (pRegs->gpr[14] & (1 << 1)) ? _T('V') : _T('.');
	str += (pRegs->gpr[14] & (1 << 0)) ? _T('C') : _T('.');
	strOut += str + _T("\r\n");
	str.Format(_T("xadr: %06o  yadr: %06o    "), pRegs->xadr, pRegs->yadr);
	strOut += str;
	//потом mj
	str = _T(" mj : ");
	str += (pRegs->mj & (1 << 10)) ? _T('N') : _T('.');
	str += (pRegs->mj & (1 << 9)) ? _T('Z') : _T('.');
	str += (pRegs->mj & (1 << 8)) ? _T('V') : _T('.');
	str += (pRegs->mj & (1 << 7)) ? _T('C') : _T('.');
	strOut += str + _T("\r\n");

	SetDlgItemText(IDC_EDIT_MISC, strOut);
}

void CVM1CPPDlg::DebugPLMInterpretator()
{
	CString strResult = _T("");
	CString str;
	DebugInPLMInterpretator(m_Debugger.GetRegs()->ir, m_Debugger.GetRegs()->mj, str);
	strResult += str + _T("\r\n");
	str = _T("plx: ");
	QWORD nPLX = m_Debugger.GetWires()->plx;

	for (int i = 33; i >= 0; i--)
	{
		if (nPLX & g_QwordBitMask[i])
		{
			str += _T("1");
		}
		else
		{
			str += _T("0");
		}
	}

	strResult += str + _T("\r\n");
	str = _T("plm: ");
	QWORD nPLM = m_Debugger.GetRegs()->plm;

	for (int i = 33; i >= 0; i--)
	{
		if (nPLM & g_QwordBitMask[i])
		{
			str += _T("1");
		}
		else
		{
			str += _T("0");
		}
	}

	strResult += str + _T("\r\n");
	str = _T("plr: ");
	QWORD nPLR = m_Debugger.GetRegs()->plr;

	for (int i = 33; i >= 0; i--)
	{
		if (nPLR & g_QwordBitMask[i])
		{
			str += _T("1");
		}
		else
		{
			str += _T("0");
		}
	}

	strResult += str + _T("\r\n");
	//интерпретируем выходные параметры ПЛМ
	int nNext = (nPLX & g_QwordBitMask[0]  ?  64 : 0)    //6-
	            | (nPLX & g_QwordBitMask[5]  ?  0  : 32)   //5
	            | (nPLX & g_QwordBitMask[9]  ?  0  : 16)   //4
	            | (nPLX & g_QwordBitMask[15] ?  0  : 8)    //3
	            | (nPLX & g_QwordBitMask[19] ?  4  : 0)    //2-
	            | (nPLX & g_QwordBitMask[24] ?  2  : 0)    //1-
	            | (nPLX & g_QwordBitMask[29] ?  1  : 0);   //0-
	str.Format(_T("Next microcode addr.: [0x%02x]\r\n"), nNext);
	strResult += str;
	volatile int vsel, mcir;
	volatile bool bVsel_Valid = false;
	volatile bool bMCIR_Valid = false;
	CString strDest;
	volatile bool bDest_Valid = false;
	CString strX;
	volatile bool bX_Valid = false;
	CString strY;
	volatile bool bY_Valid = false;
	volatile int aluop;
	volatile bool bALUOP_Valid = false;
	volatile int alushift;
	volatile bool bALUShift_Valid = false;
	volatile bool bCalcY_Vect = false;
	volatile bool astbu = false, astba = false;
	volatile bool qstba = false, qstbu = false;
	QWORD outPLM = nPLR; //интерпретировать будем это
	int tplm = (outPLM >> 1) & 7;

	//интерпретация
	if (outPLM & (1 << 12))
	{
		strResult += _T("Invalid Opcode.\r\n");
	}
	else
	{
		if ((outPLM & ((1 << 13) | (1 << 11))) == ((1 << 13) | (1 << 11)))
		{
			//Специальный управляющий вектор
//			strResult += _T("-- Special control vector --\r\n");
			if (!(outPLM & (1 << 7)))
			{
				strResult += _T("Установка флага WCPU\r\n");
			}

			if (!(outPLM & (1 << 27)))
			{
				strResult += _T("Сброс флага WCPU\r\n");
			}

			if (!(outPLM & (1 << 10)))
			{
				strResult += _T("Активировать внешний сигнал INIT\r\n");
			}

			if (!(outPLM & (1 << 23)))
			{
				strResult += _T("Деактивировать внешний сигнал INIT\r\n");
			}

			if (!(outPLM & (1 << 25)))
			{
				strResult += _T("Установка флага HLT\r\n");
			}

			if (!(outPLM & (1 << 26)))
			{
				strResult += _T("Установка флага Error 2\r\n");
			}

			if (!(outPLM & (1 << 28)))
			{
				strResult += _T("Установка флага Error 3\r\n");
			}

			if (!(outPLM & (1 << 30)))
			{
				strResult += _T("Установка флага Error 7\r\n");
			}

			vsel = 0;
			bVsel_Valid = true;

			if (outPLM & (1 << 18))
			{
				vsel |= (1 << 3);
			}

			if (!(outPLM & (1 << 20)))
			{
				vsel |= (1 << 2);
			}

			if (!(outPLM & (1 << 21)))
			{
				vsel |= (1 << 1);
			}

			if (!(outPLM & (1 << 22)))
			{
				vsel |= (1 << 0);
			}

			bCalcY_Vect = true;
			//
			// MCIR write statistics
			//
			mcir = 0;
			bMCIR_Valid = true;

			if (!(outPLM & (1 << 14)))
			{
				mcir |= 1 << 2;
			}

			if (outPLM & (1 << 16))
			{
				mcir |= 1 << 1;
			}

			if (!(outPLM & (1 << 17)))
			{
				mcir |= 1 << 0;
			}

			//здесь vsel - значение на шине Y
		}
		else
		{
			//вторая фаза
//			strResult += _T("-- Work phase --------------\r\n");
			int nX = (outPLM >> 30) & 0xF;
			nX = pRegCode[nX];
			strX = CString(strRegName[nX]);
			bX_Valid = true;

			if (!(outPLM & (1 << 20)))
			{
				strDest = CString(strRegName[12]);
				bDest_Valid = true;
			}
			else
			{
				if (nX == 14)
				{
					if (outPLM & (1 << 18)) //если словная операция
					{
						strDest = _T("PSW");
					}
					else
					{
						strDest = _T("LoByte(PSW)");
					}
				}
				else
				{
					if (nX < 15) //Q не может быть приёмником
					{
						strDest = CString(strRegName[nX]);
					}
					else
					{
						strDest = CString(strRegName[12]);
					}
				}

				bDest_Valid = true;
			}

			if (!(outPLM & (1 << 23)) //если ожидать завершения записи на шине МПИ
			        && ((outPLM & (1 << 7)) || (outPLM & (1 << 8))))  //если на шине операция чтения или записи
			{
				//если Y константа или вектор
				astbu = !(outPLM & (1 << 14)) && (outPLM & (1 << 13));  //au_astb_xu
				astba = !astbu; //au_astb_xa
			}
			else
			{
				astbu = astba = false;
			}

			if (!(outPLM & (1 << 23)) //если ожидать освобождения регистра A
			        && !(outPLM & (1 << 7)) && !(outPLM & (1 << 8)))  //если на шине никаких операций
			{
				//если Y константа или вектор
				qstbu = !(outPLM & (1 << 14)) && (outPLM & (1 << 13));  //au_qstb_xu
				qstba = !qstbu; //au_qstb_xa
			}
			else
			{
				qstbu = qstba = false;
			}

			int nQBUS = (outPLM >> 6) & 7;      //QBUS operation type
			str.Format(_T("Операция МПИ: (%d) %s\r\n"), nQBUS, strQbusOp[nQBUS]);
			strResult += str;

			if (!(outPLM & (1 << 10)))
			{
				strResult += (_T("Ожидать чтение данных на шине МПИ\r\n"));
			}

			if (!(outPLM & (1 << 23)))
			{
				if (!(outPLM & (1 << 7)) && !(outPLM & (1 << 8)))  //если на шине никаких операций
				{
					strResult += (_T("Ожидать освобождения регистра A\r\n"));
				}

				if ((outPLM & (1 << 7)) || (outPLM & (1 << 8)))  //если на шине какая-то операция
				{
					strResult += (_T("Ожидать завершения записи на шине МПИ\r\n"));
				}
			}

			int plop = ((outPLM & (1 << 4)) >> 4)   //PSW and microcode control (plop)
			           | ((outPLM & (1 << 21)) >> 20)
			           | ((outPLM & (1 << 22)) >> 20);
			str.Format(_T("Управление PSW и микрокодом (plop): %d\r\n"), plop);
			strResult += str;
			bool psw_stb = ((plop == 0) || (plop == 2) || (plop == 7)) ? false : true; //psw_stb
			bool psw_stbc = ((plop == 0) || (plop == 1) || (plop == 2) || (plop == 3) || (plop == 7)) ? false : true; //psw_stbc
			bool ir_set_fc = (tplm == 1) || (tplm == 3) || (tplm == 7);
			bool mj_stb1 = ((plop == 7) & ir_set_fc) || !((plop == 1) || (plop == 5) || (plop == 7));
			bool mj_stb2 = ((plop == 7) & ir_set_fc) || !((plop == 1) || (plop == 5) || (plop == 7) || (plop == 3));
			bool psw_mj = (plop == 7) && ir_set_fc;

			if (psw_stb)
			{
				strResult += _T("psw[NZV] <= alu_flag[NZV]\r\n");
			}

			if (psw_stbc)
			{
				strResult += (_T("psw[C] <= alu_flag[C]\r\n"));
			}

			if (mj_stb1)
			{
				strResult += (_T("mj[11] <= psw[T]\r\n"));

				if (psw_mj)
				{
					strResult += (_T("mj[10:8] <= psw[NZV]\r\n"));
				}
				else
				{
					strResult += (_T("mj[10:8] <= alu_flag[NZV]\r\n"));
				}
			}

			if (mj_stb2)
			{
				if (psw_mj)
				{
					strResult += (_T("mj[7] <= psw[C]\r\n"));
				}
				else
				{
					strResult += (_T("mj[7] <= alu_flag[C]\r\n"));
				}
			}

			aluop = ((outPLM & (1 << 17)) ? 8 : 0) //код операции АЛУ
			        | ((outPLM & (1 << 16)) ? 4 : 0)
			        | ((outPLM & (1 << 14)) ? 2 : 0)
			        | ((outPLM & (1 << 13)) ? 1 : 0);
			bALUOP_Valid = true;
			int nYp = (outPLM >> 25) & 0xF; //код регистра Y прямой
			int nYi = pRegCode[nYp]; //код регистра Y инверсный и реверсный
			int nYCode = (outPLM & (1 << 14) ? 4 : 0)
			             | (outPLM & (1 << 13) ? 2 : 0)
			             | (outPLM & (1 << 11) ? 1 : 0);

			switch (nYCode)
			{
				case 0:
				case 4:
					strY = CString(strRegName[nYi]);
					bY_Valid = true;
					break;

				case 1:
				case 2:
				case 3:
				case 5:
					if (nYp == 2)
					{
						bCalcY_Vect = true;
					}
					else
					{
						strY = CString(strYConst[nYp]);
						bY_Valid = true;
					}

					break;

				case 6:
				case 7:
					//кроме всего прочего, если биты 13 и 14 оба равны 1, то разряды 27-25 определяют режим сдвига АЛУ
					alushift = (outPLM >> 25) & 7;
					bALUShift_Valid = true;
					//а регистр Y тогда всегда 12
					strY = CString(strRegName[12]);
					bY_Valid = true;
			}
		}
	}

//	strResult += (_T("\r\n"));
	str.Format(_T("Операция IR: (%d) %s\r\n"), tplm, strIROptypes[tplm]);
	strResult += str;

	//
	// MCIR update request,
	//
	if ((tplm == 0x3) || (tplm == 0x5) || (tplm == 0x7))
	{
		uint16_t pli = m_Debugger.GetWires()->pli;
		vsel = 0;
		bVsel_Valid = true;

		if (pli & (1 << 3))
		{
			vsel |= (1 << 3);
		}

		if (!(pli & (1 << 2)))
		{
			vsel |= (1 << 2);
		}

		if (pli & (1 << 1))
		{
			vsel |= (1 << 1);
		}

		if (pli & (1 << 0))
		{
			vsel |= (1 << 0);
		}

		mcir = 0;
		bMCIR_Valid = true;

		if (!(pli & (1 << 5)))
		{
			mcir |= 1 << 2;
		}

		if (!(pli & (1 << 7)))
		{
			mcir |= 1 << 1;
		}

		if (pli & (1 << 9))
		{
			mcir |= 1 << 0;
		}
	}

	if (bCalcY_Vect)
	{
		if (bVsel_Valid)
		{
			strY = _T("vect ") + CString(strVsel[vsel]);
		}
		else
		{
			strY = _T("vect ") + CString(strVsel[m_Debugger.GetRegs()->vsel]);
		}

		bY_Valid = true;
	}

	if (bMCIR_Valid)
	{
		str.Format(_T("Новый MCIR: %s %s\r\n"), strRegCode[mcir], strMcir[mcir]);
		strResult += str;

		if (bVsel_Valid)
		{
			strResult += _T("vsel: ") + CString(strRegCode[vsel]);
			str.Format(_T(" (%d) - %s\r\n"), vsel, strVsel[vsel]);
			strResult += str;
		}
	}

	if (!bX_Valid)
	{
		strX = _T("INVALID");
	}

	if (!bY_Valid)
	{
		strY = _T("INVALID");
	}

	if (bALUOP_Valid)
	{
		if (outPLM & (1 << 18)) //если словная операция
		{
			strResult += (_T("Word "));
		}
		else
		{
			strResult += (_T("Byte "));
		}

		if (bDest_Valid)
		{
			str.Format(_T("%s := "), strDest.GetString());
			strResult += str;
		}
		else
		{
			strResult += (_T("Операция ALU: "));
		}

		// арифметическая операция АЛУ
		switch (aluop)
		{
			case 0:
			case 1:
			case 3:
				str.Format(_T("%s + %s "), strX.GetString(), strY.GetString());
				strResult += str;
				break;

			case 2:
				str.Format(_T("%s & ~%s "), strX.GetString(), strY.GetString());
				strResult += str;
				break;

			case 4:
			case 5:
			case 7:
				str.Format(_T("%s - %s "), strY.GetString(), strX.GetString());
				strResult += str;
				break;

			case 6:
				str.Format(_T("%s ^ %s "), strX.GetString(), strY.GetString());
				strResult += str;
				break;

			case 8:
				str.Format(_T("%s "), strY.GetString());
				strResult += str;
				break;

			case 9:
			case 11:
				str.Format(_T("%s "), strX.GetString());
				strResult += str;
				break;

			case 10:
				str.Format(_T("%s | %s "), strX.GetString(), strY.GetString());
				strResult += str;
				break;

			case 12:
			case 13:
			case 15:
				str.Format(_T("%s - %s "), strX.GetString(), strY.GetString());
				strResult += str;
				break;

			case 14:
				str.Format(_T("%s & %s "), strX.GetString(), strY.GetString());
				strResult += str;
				break;
		}

		strResult += (_T("\r\n"));
	}

	if (bALUShift_Valid)
	{
		str.Format(_T("Операция сдвига ALU: %s\r\n"), strALURoll[alushift]);
		strResult += str;
	}

	if (qstba || qstbu)
	{
		if (qstba)
		{
			strResult += _T("qstba: ");
		}

		if (qstbu)
		{
			strResult += _T("qstbu: ");
		}

		strResult += _T(" Q := ") + strDest + _T("\r\n");
	}

	if (astba || astbu)
	{
		if (astba)
		{
			strResult += _T("astba: ");
		}

		if (astbu)
		{
			strResult += _T("astbu: ");
		}

		strResult += _T(" A := ") + strDest + _T("\r\n");

		switch (tplm)
		{
			case 2: //нет операции, данные не из IR
			{
				strResult += _T("irdat request\r\n");
				break;
			}

			case 3:
			{
				strResult += _T("ir request\r\n");
				break;
			}

			case 4:
			{
				strResult += _T("irpre request\r\n");
				break;
			}
		}
	}

	SetDlgItemText(IDC_EDIT_PLM, strResult);
}

void CVM1CPPDlg::DebugPLIInterpretator()
{
	CString str = _T("rq : ");
	uint32_t rq = m_Debugger.GetRegs()->rq;

	for (int i = 19; i >= 0; i--)
	{
		str += (rq & g_DwordBitMask[i]) ? _T("1") : _T("0");
	}

	str += _T("\r\npli: ");
	uint16_t pli = m_Debugger.GetWires()->pli;

	for (int i = 10; i >= 0; i--)
	{
		str += (pli & g_WordBitMask[i]) ? _T("1") : _T("0");
	}

	SetDlgItemText(IDC_EDIT_PLI, str);
}

void CVM1CPPDlg::OnBnClickedButtonDumpMode()
{
	m_currentddm++;

	if (m_currentddm >= NUMBER_DISPLAY_MODE)
	{
		m_currentddm = 0;
	}

	SetDlgItemText(IDC_BUTTON_DUMP_MODE, m_dmarray[m_currentddm].name);
	m_listMemory.SetDisplayMode(m_dmarray[m_currentddm].mode);
}

uint16_t CVM1CPPDlg::GetDebugMemDumpWord(uint16_t dumpAddress)
{
	return m_Debugger.GetDebugMemDumpWord(dumpAddress);
}
uint8_t CVM1CPPDlg::GetDebugMemDumpByte(uint16_t dumpAddress)
{
	return m_Debugger.GetDebugMemDumpByte(dumpAddress);
}

LRESULT CVM1CPPDlg::OnDumpAddressChange(WPARAM wParam, LPARAM lParam)
{
	TCHAR buf[16];
	GetDlgItemText(IDC_DA_DUMP_ADDRESS, buf, 16);
	uint16_t nAddr = OctStringToWord(CString(buf));
	//обновим значение, чтобы оно было всегда 6 значным
	SetDlgItemText(IDC_DA_DUMP_ADDRESS, WordToOctString(nAddr));
	m_listMemory.RefreshItems(nAddr, true);
	return S_OK;
}


void CVM1CPPDlg::OnLvnItemchangedMdMemory(NMHDR *pNMHDR, LRESULT *pResult)
{
	auto pNMListView = reinterpret_cast<LPNMLISTVIEW >(pNMHDR);
	int nSubItem = pNMListView->iSubItem;
	int nItem = pNMListView->iItem;

	switch (m_listMemory.GetDisplayMode())
	{
		case DD_WORD_VIEW:
		{
			if (CDumpListCtrl::IDS_MEMORY_COLUMN1_WORD <= nSubItem && nSubItem <= CDumpListCtrl::IDS_MEMORY_COLUMN4_WORD)
			{
				register int addr = m_listMemory.GetDumpAddress() + nItem * 010;
				register int nPos = (nSubItem - 1) * 2; //2 - кол-во charов в wordе
				addr += nPos;
				register auto pstr = reinterpret_cast<CString *>(pNMListView->lParam);
				register uint16_t value = OctStringToWord(*pstr);
				CString str = m_listMemory.GetItemText(nItem, CDumpListCtrl::IDS_MEMORY_ASCII_WORD);
				str.SetAt(nPos++, m_listMemory.GetMemDumpByteAsANSI(LOBYTE(value)));
				str.SetAt(nPos,   m_listMemory.GetMemDumpByteAsANSI(HIBYTE(value)));
				m_listMemory.SetEndEdit(nItem, nSubItem);
				m_Debugger.OnDebugModify_Memory(addr, value);
				m_listMemory.SetItemWithModified(value, nItem, nSubItem);
				m_listMemory.SetItemWithModifiedASCII(str, nItem, CDumpListCtrl::IDS_MEMORY_ASCII_WORD);
			}

			break;
		}

		case DD_BYTE_VIEW:
		{
			if (CDumpListCtrl::IDS_MEMORY_COLUMN1_BYTE <= nSubItem && nSubItem <= CDumpListCtrl::IDS_MEMORY_COLUMN8_BYTE)
			{
				register int addr = m_listMemory.GetDumpAddress() + nItem * 010;
				register int nPos = (nSubItem - 1);
				addr += nPos;
				register auto pstr = reinterpret_cast<CString *>(pNMListView->lParam);
				register uint8_t value = LOBYTE(OctStringToWord(*pstr));
				register TCHAR ch = m_listMemory.GetMemDumpByteAsANSI(value);
				CString str = m_listMemory.GetItemText(nItem, CDumpListCtrl::IDS_MEMORY_ASCII_BYTE);
				str.SetAt(nPos, ch);
				m_listMemory.SetEndEdit(nItem, nSubItem);
				m_Debugger.OnDebugModify_Memory(addr, value);
				m_listMemory.SetItemWithModifiedByte(value, nItem, nSubItem);
				m_listMemory.SetItemWithModifiedASCII(str, nItem, CDumpListCtrl::IDS_MEMORY_ASCII_BYTE);
			}

			break;
		}
	}

	*pResult = S_OK;
}
