// BK001001.cpp: implementation of the CBK001001 class.
//


#include "pch.h"
#include "BK001001.h"
#include "Platform.h"

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


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

IMPLEMENT_DYNAMIC(CBK001001, CBKMPIDevice)

CBK001001::CBK001001()
	: m_nStartAddr(0100000)
{
}

CBK001001::~CBK001001()
    = default;


bool CBK001001::InitialSetDevice()
{
	if (CBKMPIDevice::InitialSetDevice())
	{
		g_Platform.SetCPUBaseFreq(CPU_SPEED_BK10); // частота задаётся этой константой
		g_Platform.GetCPUPtr()->AttachBoard(this);
		return true;
	}

	return false;
}


int CBK001001::GetScreenPage() const
{
	return 1;    // для БК10 всегда 1
}

/////////////////////////////////////////////////////////////////////////////
// это ресет оборудования, выполняемый по команде RESET. вызывается из CPU
void CBK001001::OnReset()
{
	const auto p = g_Platform.GetScreenPtr();
	p->SetPalette(0);
	p->SetRegister(m_reg177664);
	m_reg177660 = 0100; // команда RESET запрещает прерывания от клавиатуры. проверено.
	m_reg177714in = 0;
	m_reg177714out = 0;
	m_reg177716out_tap = 0200;
	m_reg177716in |= 0300;
	// если у нас есть сопр. по команде RESET он тоже должен инициализироваться (в правильной схеме подключения)
	g_Platform.GetAYSndPtr()->Reset();
}


/////////////////////////////////////////////////////////////////////////////
// Эта функция используется единственный раз при начальной инициализации модели.
bool CBK001001::InitDevice(const uint16_t addrStart)
{
	if (addrStart != 0) // если значение задано, то
	{
		m_nStartAddr = addrStart;    // используем адрес из конфигурации
	}

	// иначе используем адрес по умолчанию
	// инициализация регистров
	m_reg177716in = m_nStartAddr & 0177400;
	m_reg177662out = 047400;
	m_reg177664 = 0330; // начальное значение. на самом деле - оно случайное
	g_Platform.GetScreenPtr()->SetRegister(m_reg177664);
	m_reg177662in = 0;
	m_reg177716out_mem = 0;
	OnInitDevice();
	g_Platform.GetSpeakerPtr()->Reset();
	g_Platform.GetCovoxPtr()->Reset();
	return true;
}

/////////////////////////////////////////////////////////////////////////////
// кастомный вызов функции InitMemoryModules с индивидуальными параметрами
bool CBK001001::OnInitDevice()
{
	// инициализируем список загружаемых ПЗУ
	static const CBKMPIDevice::BoardRomList_t m_arrBK1001RomList[BK1001RomListSize] =
	{
		{IDS_INI_BK10_RE2_017_MONITOR,    BRD_10_MON10_BNK },
		{IDS_INI_BK10_RE2_106_BASIC1, BRD_10_BASIC10_1_BNK },
		{IDS_INI_BK10_RE2_107_BASIC2, BRD_10_BASIC10_2_BNK },
		{IDS_INI_BK10_RE2_108_BASIC3, BRD_10_BASIC10_3_BNK }
	};
	// инициализация карты памяти
	return InitMemoryModules(m_arrBK1001RomList, BK1001RomListSize);
}

/////////////////////////////////////////////////////////////////////////////
// на входе - маска, по которой сбрасываем биты адреса.
void CBK001001::ResetCold(const uint16_t addrMask)
{
	m_reg177662out = 047400;    // регистр инициализируется
	//m_reg177664 = 0330;   ресет не должен затрагивать этот регистр
	m_reg177662in = 0;
	m_reg177716in = m_nStartAddr & ~addrMask; // формируем адрес запуска
	g_Platform.GetCPUPtr()->InitCPU(); // здесь делается чтение адреса запуска из регистра 177716in
	m_reg177716in = m_nStartAddr & 0177400; // и восстановим стандартное значение адреса запуска, без сброшенных битов
}

/////////////////////////////////////////////////////////////////////////////
// нажали на кнопку стоп
void CBK001001::StopInterrupt() const
{
	g_Platform.GetCPUPtr()->SetIRQ1();
}

/////////////////////////////////////////////////////////////////////////////
// отжали кнопку стоп
void CBK001001::UnStopInterrupt() const
{
	g_Platform.GetCPUPtr()->UnsetIRQ1();
}

/*
В функциях GetByte, GetWord, SetByte, SetWord доступ к регистрам 0177700..0177712
не обрабатывается, т.к. эти функции должны вызываться только из CPU, и доступ к
регистрам обрабатывается там.

выход из функций: true - всё в порядке
                  false - надо делать throw
*/
bool CBK001001::GetByteT(const uint16_t addr, uint8_t *pValue, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// в обычном режиме тут только регистры могут быть
		return GetSystemRegister(addr, pValue, GSSR_BYTEOP);
	}

	// теперь проверим на чтение из несуществующего банка
	if (memoryBank.bReadable)
	{
		nTC = memoryBank.nTimingCorrection;
		// если читать можно, заодно и ремап сделаем
		*pValue = m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017777)];
		return true;
	}

	nTC = 0;
	*pValue = 0;
	return false;
}

bool CBK001001::GetWordT(const uint16_t addr, uint16_t *pValue, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// в обычном режиме тут только регистры могут быть
		return GetSystemRegister(addr, pValue, GSSR_NONE);
	}

	// теперь проверим на чтение из несуществующего банка
	if (memoryBank.bReadable)
	{
		nTC = memoryBank.nTimingCorrection;
		// если читать можно, заодно и ремап сделаем
		*pValue = *(reinterpret_cast<uint16_t *>(&m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017776)]));
		return true;
	}

	nTC = 0;
	*pValue = 0;
	return false;
}


bool CBK001001::SetByteT(const uint16_t addr, uint8_t value, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// тут только регистры могут быть
		return SetSystemRegister(addr, value, GSSR_BYTEOP);
	}

	if (memoryBank.bWritable)
	{
		nTC = memoryBank.nTimingCorrection;
		m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017777)] = value;
		return true;
	}

	nTC = 0;
	return false;
}


bool CBK001001::SetWordT(const uint16_t addr, uint16_t value, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// тут только регистры могут быть
		return SetSystemRegister(addr, value, GSSR_NONE);
	}

	if (memoryBank.bWritable)
	{
		nTC = memoryBank.nTimingCorrection;
		*(reinterpret_cast<uint16_t *>(&m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017776)])) = value;
		return true;
	}

	nTC = 0;
	return false;
}

bool CBK001001::GetByteTInternal(const uint16_t addr, uint8_t *pValue, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// в обычном режиме тут только регистры могут быть
		return GetSystemRegister(addr, pValue, GSSR_INTERNAL | GSSR_BYTEOP);
	}

	// теперь проверим на чтение из несуществующего банка
	if (memoryBank.bReadable)
	{
		nTC = memoryBank.nTimingCorrection;
		// если читать можно, заодно и ремап сделаем
		*pValue = m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017777)];
		return true;
	}

	nTC = 0;
	*pValue = 0;
	return false;
}

bool CBK001001::GetWordTInternal(const uint16_t addr, uint16_t *pValue, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// в обычном режиме тут только регистры могут быть
		return GetSystemRegister(addr, pValue, GSSR_INTERNAL);
	}

	// теперь проверим на чтение из несуществующего банка
	if (memoryBank.bReadable)
	{
		nTC = memoryBank.nTimingCorrection;
		// если читать можно, заодно и ремап сделаем
		*pValue = *(reinterpret_cast<uint16_t *>(&m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017776)]));
		return true;
	}

	nTC = 0;
	*pValue = 0;
	return false;
}


bool CBK001001::SetByteTInternal(const uint16_t addr, uint8_t value, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// тут только регистры могут быть
		return SetSystemRegister(addr, value, GSSR_INTERNAL | GSSR_BYTEOP);
	}

	// если там хоть что-то есть, то пишем, даже в ПЗУ
	if (memoryBank.bReadable)
	{
		nTC = memoryBank.nTimingCorrection;
		m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017777)] = value;
		return true;
	}

	nTC = 0;
	return false;
}

bool CBK001001::SetWordTInternal(const uint16_t addr, uint16_t value, int &nTC)
{
	const int nBank = ADDRESS_TO_BANK(addr);
	const BKMEMBank_t &memoryBank = m_pMemoryMap[nBank];

	// Сперва проверим, на системные регистры
	if (nBank == 7 && addr >= m_nBKPortsIOArea)
	{
		nTC = memoryBank.nTimingCorrection;
		// тут только регистры могут быть
		return SetSystemRegister(addr, value, GSSR_INTERNAL);
	}

	// если там хоть что-то есть, то пишем, даже в ПЗУ
	if (memoryBank.bReadable)
	{
		nTC = memoryBank.nTimingCorrection;
		*(reinterpret_cast<uint16_t *>(&m_pMemory[static_cast<size_t>(memoryBank.nOffset) + (addr & 017776)])) = value;
		return true;
	}

	nTC = 0;
	return false;
}

/*
Чтение из системных регистров
вход: addr - адрес регистра (177660, 177716 и т.п.)
      pDst - адрес в массиве, куда сохраняется новое значение
      dwFlags - флаги операции, GSSR_BYTEOP - байтовая, GSSR_INTERNAL - внутренняя, без эмуляции действий
*/
bool CBK001001::GetSystemRegister(const uint16_t addr, void *pDst, UINT dwFlags)
{
	bool bRet = true;
	uint16_t v = 0;

	switch (addr & 0177776)
	{
		case 0177660:
			v = m_reg177660;
			break;

		case 0177662:
			v = m_reg177662in;
			// если читаем из регистра данных клавиатуры,
			// сбросим бит готовности из регистра состояния клавиатуры
			m_reg177660 &= ~0200;
			g_Platform.CancelDelayedKeyboardInterrupt();
			break;

		case 0177664:
			v = m_reg177664;
			break;

		case 0177714:
			v = m_reg177714in;
			break;

		case 0177716:
			v = m_reg177716in;

			// В БК 2й разряд SEL1 фиксирует любую запись в этот регистр, взводя триггер D9.1 на неограниченное время,
			// сбрасывается который любым чтением этого регистра.
			if (!(dwFlags & GSSR_INTERNAL))
			{
				m_reg177716in &= ~4;
			}

			break;

		default:
			bRet = false;
	}

	if (dwFlags & GSSR_BYTEOP)
	{
		*(reinterpret_cast<uint8_t *>(pDst)) = (addr & 1) ? HIBYTE(v) : LOBYTE(v);
	}
	else
	{
		*(reinterpret_cast<uint16_t *>(pDst)) = v;
	}

	return bRet;
}

/*
Запись данных в системные регистры БК0010
вход: addr - адрес регистра(177660, 177716 и т.п.)
      src - записываемое значение.
      dwFlags - флаги операции, GSSR_BYTEOP - байтовая, GSSR_INTERNAL - внутренняя, без эмуляции действий
*/
bool CBK001001::SetSystemRegister(const uint16_t addr, uint16_t src, UINT dwFlags)
{
	switch (addr & 0177776)
	{
		case 0177660:
			/*
			177660
			Регистр состояния клавиатуры.
			(0100)бит 6 -- маскирование прерывание от клавиатуры,
			    "0" -- разрешено прерывание по вектору 060 либо 0274.
			    Прерывание вызывается при появлении "1" в бите 7. Начальное состояние: "0".
			    Доступен по чтению и по записи
			(0200)бит 7 -- готовность: "1" -- в регистре данных клавиатуры(177662) готов код нажатой клавиши.
			    Устанавливается при нажатии на клавишу, сбрасывается при чтении регистра данных клавиатуры.
			    Начальное состояние: "1".
			    Доступен только по чтению.
			другие биты: "0". Доступны только по чтению.
			*/
		{
			const uint16_t mask = dwFlags & GSSR_INTERNAL ? 0300 : 0100;

			if (dwFlags & GSSR_BYTEOP)
			{
				src &= 0377;

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

			// сбрасываем используемые биты и устанавливаем их значения из слова, которое записываем.
			// остальные биты - которые не используются - остаются нетронутыми.
			uint16_t prev = m_reg177660 & 0100;
			m_reg177660 = (m_reg177660 & ~mask) | (src & mask);
			// если мы сбросили запрет прерываний
			if (prev && !(m_reg177660 & 0100))
			{
				// если был запрос на прерывание, то делаем его.
				g_Platform.DelayedKeyboardInterrupt();
			}
		}

		return true;

		case 0177662:
			/*
			177662
			Регистр данных клавиатуры.
			    биты 0-6: код клавиши. Доступ только по чтению.
			(040000)бит 14: разрешение прерывания по(внешнему) таймеру(50 Гц),
			    "0" -- прерывание разрешено,
			    "1" -- таймер отключён.
			    Доступен только по записи.
			    Бит 14 на БК0010 не документирован, и таймер работал только на некоторых экземплярах, можно считать, что его нет
			*/
			return false; // на бк10 запись запрещена. это был один из способов определения бк10 или бк11

		case 0177664:
			/*
			177664
			Регистр скроллинга. Доступен по записи и чтению.
			    биты 0-7: смещение скроллинга, строк. Начальное значение -- 0330.
			(01000)бит 9: сокращённый режим экрана, "0" -- сокращённый(1/4 экрана, старшие адреса),
			    "1" -- полный экран 256 строк.
			*/
		{
			constexpr uint16_t mask = 01377;

			if (dwFlags & GSSR_BYTEOP)
			{
				src &= 0377;
				/*
				оказалось, что в этот регистр не работает байтовая запись.
				чтение байта работает, но записывается всегда слово.
				а при байтовой операции, старший байт просто теряется.
				*/
			}

			m_reg177664 = (~mask & m_reg177664) | (mask & src);
		}

		return true;

		case 0177714:

			/*177714
			Регистр параллельного программируемого порта ввода вывода - два регистра, входной по чтению и выходной по записи.
			из выходного нельзя ничего прочитать т.к., читается оно из входного,
			во входной невозможно ничего записать, т.к. записывается оно в выходной.
			*/
			if (dwFlags & GSSR_BYTEOP)
			{
				src &= 0377; // работаем с младшим байтом

				if (addr & 1)
				{
					src <<= 8; // работаем со старшим байтом
				}
			}

			if (g_Platform.GetAYSndPtr())
			{
				if (dwFlags & GSSR_BYTEOP)
				{
					g_Platform.GetAYSndPtr()->write_data(LOBYTE(src));
				}
				else
				{
					g_Platform.GetAYSndPtr()->write_address(src);
				}
			}

			if (const auto p = g_Platform.GetCovoxPtr())
			{
				p->SetData(src);
			}

			if (const auto p = g_Platform.GetMenestrelPtr())
			{
				p->SetData(src);
			}

			m_reg177714out = src;

			if (g_Config.m_bICLBlock) // если включён блок нагрузок
			{
				// данные записанные в выходной порт передаются во входной
				m_reg177714in = src;
			}
			else if (g_Config.m_bMouseMars)
			{
				const auto p = g_Platform.GetScreenPtr();
				p->SetMouseStrobe(src);
				m_reg177714in = p->GetMouseStatus();
			}

			return true;

		case 0177716:

			/*
			177716
			Системный регистр. Внешний регистр 1(ВР1, SEL1) процессора ВМ1, регистр начального пуска.
			как и 177714 состоит из двух регистров, раздельных по чтению и по записи
			По чтению:
			(004)бит 2: признак записи в системный регистр (Внешняя схема). Устанавливается в "1" при
			    любой записи в регистр, сбрасывается в "0" после чтения из регистра.
			(010)бит 3: устанавливается при выполнении системного прерывания в HALT-mode.
			    Установка этого бита делается микропрограммным образом записью в 177716 бита 3.
			    На БК оно нигде не сохраняется и пропадает в никуда.
			    Бит 3 снимается командами START, STEP
			(020)бит 4: данные с ТЛГ-линии, "0" - логический 0, "1" - логическая 1
			(040)бит 5: данные с магнитофона, "0" - логический 0, "1" - логическая 1
			(100)бит 6: индикатор нажатия клавиши, установлен в "0" если нажата клавиша клавиатуры, "1" если нет нажатых клавиш.
			(200)бит 7: сигнал готовности с ТЛГ-линии, "0" - логический 0, "1" - логическая 1
			        Однако по заводской документации на БК0010 этот бит - константа "1", указывающая на отсутствие
			        в системе команд расширенной арифметики
			    биты 8-15: адрес начального пуска, 100000(БК-0010), младший байт при этом игнорируется
			    биты 0,1,3, не используются, "0".
			По записи:
			(020)бит 4: данные для передачи на ТЛГ-линию, "0" - логический 0, "1" - логическая 1
			(040)бит 5: данные на магнитофон(либо сигнал готовности на ТЛГ-линию - этого в тех документации нету.). Начальное состояние "0".
			(100)бит 6: данные на магнитофон и на пьезодинамик. Начальное состояние "0".
			(200)бит 7: включение двигателя магнитофона, "1" -- стоп, "0" -- пуск. Начальное состояние "1".
			    биты 0,1,3, не используются, "0".
			*/
			if (dwFlags & GSSR_BYTEOP)
			{
				src &= 0377; // работаем с младшим байтом

				if (addr & 1)
				{
					src <<= 8; // работаем со старшим байтом
				}
			}

			Set177716RegTap(src);

			// В БК 2й разряд SEL1 фиксирует любую запись в этот регистр, взводя триггер D9.1 на неограниченное время, сбрасывается который любым чтением этого регистра.
			if (!(dwFlags & GSSR_INTERNAL))
			{
				m_reg177716in |= 4;
			}

			return true;
	}

	return false;
}


/////////////////////////////////////////////////////////////////////////////
// перехват разных подпрограмм монитора, для их эмуляции
// для каждой модели - своя функция, но она должна работать в пространстве g_Platform
// как это сделать - неясно, поэтому будем копипастить
bool CBK001001::Interception(const uint16_t nPC) const
{
	switch (nPC & 0177776)
	{
		case 0116256:
			return g_Platform.EmulateSaveTape();

		case 0116640:
			return g_Platform.EmulateLoadTape();
	}

	return false; // если ничего не выполнилось
}


void CBK001001::Set177716RegTap(const uint16_t w)
{
	constexpr uint16_t mask = 0360;
	m_reg177716out_tap = (~mask & m_reg177716out_tap) | (mask & w);
	g_Platform.GetSpeakerPtr()->SetData(m_reg177716out_tap);
}

void CBK001001::BasicRomOFF(const bool off)
{
	for (int n = BRD_10_BASIC10_1_BNK; n <= BRD_10_BASIC10_3_BNK; ++n)
	{
		m_pMemoryMap[n].bReadable = !off;
		m_pMemoryMap[n].bWritable = FALSE;
	}
}

void CBK001001::Mon10RomOFF(const bool off)
{
	m_pMemoryMap[BRD_10_MON10_BNK].bReadable = !off;
	m_pMemoryMap[BRD_10_MON10_BNK].bWritable = FALSE;
}

void CBK001001::MemoryManager()
{
	RAM037FF();

	for (int i = 4; i < DEC_BANKS_NUM; ++i)
	{
		BKMEMBank_t &mb = m_pMemoryMap[i];
		mb.bReadable = !!(m_nROMPresent & (1 << i));
		mb.bWritable = FALSE;
		mb.nBank = i;
		mb.nOffset = BANK_TO_OFFSET(i);
		mb.nTimingCorrection = ROM_TIMING_CORR_VALUE;
	}

}

CString CBK001001::GetMemoryMapViewData(const int seg)
{
	//static const CString str2[4] = { _T("МОН"), _T("BAS1"), _T("BAS2"), _T("BAS3") };

	BKMEMBank_t &mb = m_pMemoryMap[seg];
	int n = 0;
	if (mb.bReadable) n |= 2;
	if (mb.bWritable) n |= 1;
	return g_arStrMMVw1[n];
}

void CBK001001::RAM037FF()
{
	for (int i = 0; i < 4; ++i)
	{
		BKMEMBank_t &mb = m_pMemoryMap[i];
		mb.bReadable = !g_Config.m_bAZ037OffState;
		mb.bWritable = !g_Config.m_bAZ037OffState;
		mb.nBank = i;
		mb.nOffset = BANK_TO_OFFSET(i);
		mb.nTimingCorrection = RAM_TIMING_CORR_VALUE_D;
	}
}

bool CBK001001::RestoreRegisters(DWORD id, CMSFManager *pMsf)
{
	MSF_PORT_REGS port_regs{};

	if (pMsf->IsLoad())
	{
		if (!pMsf->GetBlockPortRegs(id, &port_regs))
		{
			return false;
		}

		m_reg177660 = port_regs.p0177660;
		m_reg177662in = port_regs.p0177662_in;
		m_reg177662out = port_regs.p0177662_out;
		m_reg177664 = port_regs.p0177664;
		m_reg177714in = port_regs.p0177714_in;
		m_reg177714out = port_regs.p0177714_out;
		m_reg177716in = port_regs.p0177716_in;
		m_reg177716out_tap = port_regs.p0177716_out_tap;
		m_reg177716out_mem = port_regs.p0177716_out_mem;
		const auto pCPU = g_Platform.GetCPUPtr();
		pCPU->SetSysRegsIndirect(0177700, port_regs.p0177700);
		pCPU->SetSysRegsIndirect(0177702, port_regs.p0177702);
		pCPU->SetSysRegsIndirect(0177704, port_regs.p0177704);
		pCPU->SetSysRegsIndirect(0177706, port_regs.p0177706);
		pCPU->SetSysRegsIndirect(0177710, port_regs.p0177710);
		pCPU->SetSysRegsIndirect(0177712, port_regs.p0177712);
	}
	else
	{
		port_regs.p0177660 = m_reg177660;
		port_regs.p0177662_in = m_reg177662in;
		port_regs.p0177662_out = m_reg177662out;
		port_regs.p0177664 = m_reg177664;
		port_regs.p0177714_in = m_reg177714in;
		port_regs.p0177714_out = m_reg177714out;
		port_regs.p0177716_in = m_reg177716in;
		port_regs.p0177716_out_tap = m_reg177716out_tap;
		port_regs.p0177716_out_mem = m_reg177716out_mem;
		const auto pCPU = g_Platform.GetCPUPtr();
		port_regs.p0177700 = pCPU->GetSysRegsIndirect(0177700);
		port_regs.p0177702 = pCPU->GetSysRegsIndirect(0177702);
		port_regs.p0177704 = pCPU->GetSysRegsIndirect(0177704);
		port_regs.p0177706 = pCPU->GetSysRegsIndirect(0177706);
		port_regs.p0177710 = pCPU->GetSysRegsIndirect(0177710);
		port_regs.p0177712 = pCPU->GetSysRegsIndirect(0177712);

		if (!pMsf->SetBlockPortRegs(id, &port_regs))
		{
			return false;
		}
	}

	return true;
}


bool CBK001001::RestoreMemory(DWORD id, CMSFManager *pMsf)
{
	if (pMsf->IsLoad())
	{
		return pMsf->GetBlockBaseMemory(id, GetMainMemory(), m_nMemorySize);
	}

	return pMsf->SetBlockBaseMemoryEx(id, GetMainMemory(), m_nMemorySize, true);
}

bool CBK001001::RestoreMemoryMap(DWORD id, CMSFManager *pMsf)
{
	if (pMsf->IsLoad())
	{
		return pMsf->GetBlockMemMap(id, m_pMemoryMap.data(), DEC_BANKS_NUM, m_nROMPresent);
	}

	return pMsf->SetBlockMemMap(id, m_pMemoryMap.data(), DEC_BANKS_NUM, m_nROMPresent);
}

