#include "stdafx.h"
#include "resource.h"
#include "FDD_A16M.h"

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

CFDD_A16M::CFDD_A16M()
	: m_bA16M_Trigger(false)
{
	m_aBoardRomList[0] = { IDS_INI_FDR_A16M, ALTPRO_A16M_FDDROM_BNK };
	m_drivedata[BK_FDD_DRIVE_C].bPresent = false;
	m_drivedata[BK_FDD_DRIVE_D].bPresent = false;
	// Инициализация модуля памяти, 16кб ОЗУ + 8кб ПЗУ (из них используется 4).
	m_nMemorySize = 060000;
	InitMemory(BK_PORTSIO_AREA);
}

CFDD_A16M::~CFDD_A16M()
{
}

BK_BOARD_MODEL CFDD_A16M::GetBoardModel()
{
	return DEV_MPI_A16M;
}

bool CFDD_A16M::InitDevice(uint16_t addrStart)
{
	m_sCustom.nAltProMemBank = 0; //нужно выполнять инициализацию перед инициализацией памяти
	m_sCustom.nExtCodes = 0;
	m_sCustom.nAltProMode = 0160;
	return CFDDStd326::InitDevice(addrStart);
}


void CFDD_A16M::OnReset()
{
	m_sCustom.nAltProMemBank = 0; //нужно выполнять инициализацию, если мы просто выполним ресет
	m_sCustom.nExtCodes = 0;        //получается дублирование, но это недостаток алгоритма
	m_sCustom.nAltProMode = 0160;
	CFDDStd326::OnReset();
}


bool CFDD_A16M::InitMemoryModules()
{
	bool bRes = true;
	int n = 0;

	//проверяем загрузку всех ПЗУ в списке.
	//если чего-то нет, выдаёт сообщение, но продолжаем
	while (m_aBoardRomList[n].nResource)
	{
		bRes = bRes && LoadRomModule(m_aBoardRomList[n].nResource, BANK_TO_OFFSET_ALTPRO(m_aBoardRomList[n].nBank), m_aBoardRomList[n].nBank);
		n++;
	}

	//в любом случае проинициализируем карту памяти
	MemoryManager();
	return bRes;
}

void CFDD_A16M::OnLoadRomModule(int nSize, int nBank)
{
	if (nBank == ALTPRO_A16M_FDDROM_BNK)
	{
		if (nSize <= 010000)
		{
			m_nBKPortsIOArea = 0170000;
		}
	}
}

CFDD_A16M::A16Mode_t CFDD_A16M::m_A16M_MemStatus[8] =
{
	{
		//режим 160
		//откл  MON10   MON11,  RAM10
		false,  false,  true,
		{
			{ 0, BNK_OFF }, { 0, BNK_OFF }, { 2, BNK_RAM }, { 3, BNK_RAM }, { 0, BNK_RAM }, { 1, BNK_RAM }, { 0, BNK_ROM }, { 0, BNK_ROM },
		}
	},
	{
		//режим 60
		false,  true,   true,
		{
			{ 0, BNK_OFF }, { 0, BNK_OFF }, { 2, BNK_RAM }, { 3, BNK_RAM }, { 0, BNK_RAM }, { 1, BNK_RAM }, { 0, BNK_ROM }, { 0, BNK_OFF },
		}
	},
	{
		//режим 120
		true,   false,  true,
		{
			{ 0, BNK_RAM }, { 1, BNK_RAM }, { 2, BNK_RAM }, { 3, BNK_RAM }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_ROM }, { 0, BNK_OFF },
		}
	},
	{
		//режим 20
		true,   true,   true,
		{
			{ 0, BNK_RAM }, { 1, BNK_RAM }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF },
		}
	},
	{
		//режим 140
		false,  false,  false,
		{
			{ 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_ROM }, { 0, BNK_OFF },
		}
	},
	{
		//режим 40
		false,  true,   false,
		{
			{ 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_RAM }, { 1, BNK_RAM }, { 2, BNK_RAM }, { 0, BNK_OFF },
		}
	},
	{
		//режим 100
		true,   false,  false,
		{
			{ 0, BNK_RAM }, { 1, BNK_RAM }, { 2, BNK_RAM }, { 3, BNK_RAM }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_ROM }, { 0, BNK_OFF },
		}
	},
	{
		//режим 0
		true,   true,   false,
		{
			{ 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_OFF }, { 0, BNK_RAM }, { 1, BNK_RAM }, { 2, BNK_RAM }, { 3, BNK_RAM }
		}
	}
};


void CFDD_A16M::MemoryManager()
{
	//устанавливаем новый режим работы
	int nModeNumber = 0;

	//перекодируем код режима в порядковый номер
	switch (m_sCustom.nAltProMode & ALTPRO_MODE_MASK)
	{
		case ALTPRO_A16M_START_MODE:
		{
			//Start (БК11М: откл.окно1)
			nModeNumber = 0;
			break;
		}

		case ALTPRO_A16M_STD10_MODE:
		{
			//Std10 (БК11М: откл.окно1, откл.мон11)
			nModeNumber = 1;
			break;
		}

		case ALTPRO_A16M_OZU10_MODE:
		{
			//ОЗУ10 (БК11М: откл.окно1; БК10: откл.мон10)
			nModeNumber = 2;
			break;
		}

		case ALTPRO_A16M_BASIC_MODE:
		{
			//Basic (БК11М: откл.окно1, откл.мон11; БК10: откл.мон10)
			nModeNumber = 3;
			break;
		}

		case ALTPRO_A16M_STD11_MODE:
		{
			//Std11
			nModeNumber = 4;
			break;
		}

		case ALTPRO_A16M_OZU11_MODE:
		{
			//ОЗУ11 (БК11М: откл.мон11)
			nModeNumber = 5;
			break;
		}

		case ALTPRO_A16M_OZUZZ_MODE:
		{
			//ОЗУзз (БК10: откл.мон10)
			nModeNumber = 6;
			break;
		}

		case ALTPRO_A16M_HLT11_MODE:
		{
			//Hlt11 (БК11М: откл.мон11; БК10: откл.мон10)
			nModeNumber = 7;
			break;
		}

		default:
			ASSERT(FALSE);
	}
	//теперь из массивов выберем нужные значения
	register auto brd = g_Platform.GetBoardPtr();
	brd->Mon10RomOFF(m_A16M_MemStatus[nModeNumber].bMON10);
	brd->BOS11RomOFF(m_A16M_MemStatus[nModeNumber].bBOS11);
	brd->RAM11Wn1OFF(m_A16M_MemStatus[nModeNumber].bWND1);
	SetBank(nModeNumber);

	//и обработаем дополнительные условия
	switch (nModeNumber)
	{
		case 3:
		case 6:
			m_MemoryMap[0].bWritable = FALSE; //банк 0 с защитой от записи
			break;

		case 7:
			m_MemoryMap[7].bReadable = FALSE; //банк 7 с доступом только по записи
			break;
	}
}

//установка страницы ОЗУ в заданном банке
void CFDD_A16M::SetBank(int nModeNumber)
{
	for (int nBank = 0; nBank < 8; ++nBank)
	{
		SET_BANK_MODE nMode = m_A16M_MemStatus[nModeNumber].aBankStatus[nBank].nMode;

		switch (nMode)
		{
			case BNK_OFF:
				m_MemoryMap[nBank].bReadable = FALSE;
				m_MemoryMap[nBank].bWritable = FALSE;
				m_MemoryMap[nBank].nBank = 0;
				m_MemoryMap[nBank].nOffset = 0;
				break;

			case BNK_RAM:
				m_MemoryMap[nBank].bReadable = TRUE;
				m_MemoryMap[nBank].bWritable = TRUE;
				m_MemoryMap[nBank].nBank = m_A16M_MemStatus[nModeNumber].aBankStatus[nBank].nPage;
				m_MemoryMap[nBank].nOffset = BANK_TO_OFFSET_ALTPRO(m_MemoryMap[nBank].nBank);
				break;

			case BNK_ROM:
				m_MemoryMap[nBank].bReadable = TRUE;
				m_MemoryMap[nBank].bWritable = FALSE;
				m_MemoryMap[nBank].nBank = ALTPRO_A16M_FDDROM_BNK;
				m_MemoryMap[nBank].nOffset = BANK_TO_OFFSET_ALTPRO(ALTPRO_A16M_FDDROM_BNK);
				break;
		}
	}
}



bool CFDD_A16M::GetSystemRegister(register uint16_t addr, register void *pDst, register bool bByteOperation)
{
	register bool bRet = false;
	register uint16_t w = 0;

	switch (addr & 0177776)
	{
		case 0177130:
			if (!(m_sCustom.nExtCodes & 4)) //бит 2 отключает эти порты по чтению
			{
				w = GetState();
				//TRACE(_T("Rd 177130: %06o\n"), v);
				bRet = true;
			}

			break;

		case 0177132:
			if (!(m_sCustom.nExtCodes & 4)) //бит 2 отключает эти порты по чтению
			{
				w = GetData();
				//TRACE(_T("fdr - pos:%06d  %06o\n"), m_drivedata[m_drive].dataptr, v);
				bRet = true;
			}

			break;
	}

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

	return bRet;
}

bool CFDD_A16M::SetSystemRegister(register uint16_t addr, register uint16_t value, register bool bByteOperation)
{
	register uint16_t src = bByteOperation ? ((addr & 1) ? value << 8 : value & 0377) : value;

	switch (addr & 0177776)
	{
		case 0177130:
			SetCommand(src);

			//обрабатываем только при словной операции или байтовой, но с младшим байтом
			if ((addr & 1) == 0)
			{
				//тут надо добавить манипуляцию дозу
				Change_AltPro_Mode(src);
			}

			return true;

		case 0177132:
			WriteData(src);
			return true;
	}

	return false;
}

bool CFDD_A16M::GetByte(register uint16_t addr, register uint8_t *pValue)
{
	if (addr & 0100000)
	{
		register bool bReg = GetSystemRegister(addr, pValue, true);
		//теперь проверим на чтение из банка памяти
		register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		register bool bMem;

		if (bMem = !!m_MemoryMap[nBank].bReadable) //это не сравнение, это присваивание
		{
			//если читать можно, читаем
			*pValue |= m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07777)];
		}

		return bReg || bMem;
	}

	*pValue = 0;
	return false;
}

bool CFDD_A16M::GetWord(register uint16_t addr, register uint16_t *pValue)
{
	if (addr & 0100000)
	{
		register bool bReg = GetSystemRegister(addr, pValue, false);
		//теперь проверим на чтение из банка памяти
		register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		register bool bMem;

		if (bMem = !!m_MemoryMap[nBank].bReadable)
		{
			//если читать можно, читаем
			*pValue |= *(uint16_t *)&m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07776)];
		}

		return bReg || bMem;
	}

	*pValue = 0;
	return false;
}

bool CFDD_A16M::SetByte(register uint16_t addr, register uint8_t value)
{
	if (addr & 0100000)
	{
		register bool bReg = SetSystemRegister(addr, value, true);
		register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		//теперь проверим на запись
		register bool bMem;

		if (bMem = !!m_MemoryMap[nBank].bWritable)
		{
			//если писать можно, пишем
			m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07777)] = value;
		}

		return bReg || bMem;
	}

	return false;
}

bool CFDD_A16M::SetWord(register uint16_t addr, register uint16_t value)
{
	if (addr & 0100000)
	{
		register bool bReg = SetSystemRegister(addr, value, false);
		register int nBank = ADDRESS_TO_BANK_ALTPRO(addr);
		//теперь проверим на запись
		register bool bMem;

		if (bMem = !!m_MemoryMap[nBank].bWritable)
		{
			//если писать можно, пишем
			*(uint16_t *)&m_pMemory[m_MemoryMap[nBank].nOffset + (addr & 07776)] = value;
		}

		return bReg || bMem;
	}

	return false;
}


//проверка и отлов кода режима
void CFDD_A16M::Change_AltPro_Mode(uint16_t w)
{
	//отловим nExtCodes
	m_sCustom.nExtCodes = w & 014;

	//подключение/отключение ПЗУ Бейсика
	//только на СМК с памятью < 256кб и А*
	if (g_Platform.GetBoardPtr() && GetBoardModel() != DEV_MPI_SMK512)
	{
		//если бит 3 установлен, то бейсик включён
		g_Platform.GetBoardPtr()->BasicRomOFF((m_sCustom.nExtCodes & 010) == 0);
	}

	if (!m_bA16M_Trigger && ((w & ALTPRO_BEGIN_STROB_TRIGGER) == ALTPRO_BEGIN_STROB_TRIGGER)) //если строб-код
	{
		m_bA16M_Trigger = true; //взводим триггер, означающий, что любое следующее значение будет кодом режима
		return; //обязательно ретурн, т.к. следующей записью должны принимать код.
	}

	//если никакой не строб, то проверяем
	if (m_bA16M_Trigger) //если триггер взведён
	{
		m_nAltproMode = w & ALTPRO_MODE_MASK;       //вычленим режим
		m_nAltProMemBank = w & ALTPRO_CODE_MASK;    //вычленим код страницы памяти на СМК.

		if ((w & ALTPRO_BEGIN_STROB_TRIGGER) != ALTPRO_BEGIN_STROB_TRIGGER)
		{
			m_bA16M_Trigger = false;    //сбрасываем триггер
			m_sCustom.nAltProMode = m_nAltproMode;
			m_sCustom.nAltProMemBank = m_nAltProMemBank;
			MemoryManager();    //запускаем менеджер памяти для переключения банков
		}
	}
}

bool CFDD_A16M::RestoreCustomData(DWORD id, CMSFManager *pMsf)
{
	if (pMsf->IsLoad())
	{
		bool bRet = pMsf->GetBlockCustomData(id, reinterpret_cast<uint8_t *>(&m_sCustom), sizeof(CustomData_t));

		if (bRet)
		{
			//подключение/отключение ПЗУ Бейсика
			//только на СМК с памятью < 256кб и А*
			if (g_Platform.GetBoardPtr() && GetBoardModel() != DEV_MPI_SMK512)
			{
				//если бит 3 установлен, то бейсик включён
				g_Platform.GetBoardPtr()->BasicRomOFF((m_sCustom.nExtCodes & 010) == 0);
			}

			MemoryManager();
		}

		return bRet;
	}
	else
	{
		return pMsf->SetBlockCustomData(id, reinterpret_cast<uint8_t *>(&m_sCustom), sizeof(CustomData_t));
	}
}


