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

/*
File: Board_FDD.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.
*/


// Board_FDD.cpp: implementation of the CMotherBoard_FDD class.
//


#include "stdafx.h"
#include "Board_FDD.h"

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

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


CMotherBoard_FDD::CMotherBoard_FDD()
{
	m_nBKPortsIOArea = BK_PORTSIO_AREA;
	m_nFDDCatchAddr = m_nFDDExitCatchAddr = -1;
	m_fdd.InitHDD();
	m_bBasicOn = FALSE;
	// Инициализация модуля памяти, 64 кб + 16 кб доп. +8кб доп пзу
	SAFE_DELETE_ARRAY(m_pMemory);
	m_pMemory = new BYTE [0260000];
	ZeroMemory(m_pMemory, 0260000);
}

CMotherBoard_FDD::~CMotherBoard_FDD()
{
}

void CMotherBoard_FDD::SetFDDType(BK_FDD_MODEL model, BOOL bInit)
{
	m_fdd.SetFDDType(model);

	if (bInit)
	{
		switch (model)
		{
			case DEV_FDD_STD:
				m_fdd.init_A16M_10(&m_ConfBKModel, ALTPRO_A16M_STD10_MODE);
				break;

			case DEV_FDD_A16M:
			case DEV_FDD_SMK512:
				m_fdd.init_A16M_10(&m_ConfBKModel, ALTPRO_A16M_START_MODE);
				break;
		}
	}

	if (model == DEV_FDD_SMK512)
	{
		SAFE_DELETE_ARRAY(m_pMemory);
		//пересоздадим массив памяти. добавим ещё 512кб
		m_pMemory = new BYTE[02220000];
		ZeroMemory(m_pMemory, 02220000);
		InitMemoryValues();
	}
}

void CMotherBoard_FDD::OnReset()
{
	CMotherBoard::OnReset();
	//в эмуляторе флоповода примонтируем образы, прописанные в ини
	m_fdd.ReadDrivesPath();
}
void CMotherBoard_FDD::ResetDevices()
{
	CMotherBoard::ResetDevices();
	m_fdd.Reset();
}

BOOL CMotherBoard_FDD::InitMemoryModules()
{
	CMotherBoard::InitMemoryModules();
	int RomNameIndex;

	switch (m_fdd.GetFDDType())
	{
		case DEV_FDD_SMK512:
			RomNameIndex = IDS_INI_FDR_SMK512;
			break;

		case DEV_FDD_A16M:
			RomNameIndex = IDS_INI_FDR_A16M;
			break;

		case DEV_FDD_STD:
		default:
			RomNameIndex = IDS_INI_FDR;
			break;
	}

	// Load FDD driver
	LoadRomModule(RomNameIndex, A16M_ROM_10);
	//по адресу 160000 - ПЗУ дисковода
	m_MemoryMap[016].bReadable = TRUE;
	m_MemoryMap[016].bWritable = FALSE;
	m_MemoryMap[016].nBank = A16M_ROM_10;
	m_MemoryMap[016].nPage = A16M_ROM_10 >> 2;
	m_MemoryMap[016].nOffset = A16M_ROM_10 << 12;

	if (0167 == GetWordIndirect(0160016))
	{
		m_nFDDCatchAddr = 0160372; //не 253 прошивка
		m_nFDDExitCatchAddr = 0161564;
	}
	else
	{
		//253 прошивка, там нету перехода к эмулятору EIS/FIS
		m_nFDDCatchAddr = 0160422;
		m_nFDDExitCatchAddr = 0161540;
	}

	return TRUE;
}

void CMotherBoard_FDD::MemoryManager(void)
{
	CMotherBoard::MemoryManager();

	switch (m_fdd.GetFDDType())
	{
		case DEV_FDD_SMK512:
			// а потом переустановим в соответствии с настройками SMK
			m_fdd.SMK512_MemManager_10(m_MemoryMap, &m_ConfBKModel);
			break;

		case DEV_FDD_A16M:
		case DEV_FDD_STD:
			// а потом переустановим в соответствии с настройками А16М
			m_fdd.A16M_MemManager_10(m_MemoryMap, &m_ConfBKModel);
			break;
	}
}


BOOL CMotherBoard_FDD::OnSetSystemRegister(WORD num, WORD src, BOOL bByteOperation)
{
	if (CMotherBoard::OnSetSystemRegister(num, src, bByteOperation))
	{
		return TRUE;
	}

	BOOL bRet = TRUE;
	WORD addr = num & 0177776;

	switch (addr)
	{
		case 0177130:

			//обрабатываем только при словной операции или байтовой, но с младшим байтом
			//хотя я подозреваю, что здесь как обычно, невозможно обратиться к регистру по нечётному адресу
			if ((num & 1) == 0)
			{
				if (bByteOperation) //если операция байтовая, то старший байт теряем
				{
					src &= 0377;
				}

				//тут надо добавить манипуляцию дозу
				if (m_fdd.Change_A16M_Mode(&m_ConfBKModel, src))
				{
					MemoryManager();

					//сохраняем копию
					for (int i = 012, j = 0; i <= 017; i++, j++)
					{
						m_MemMapBasic[j].bReadable = m_MemoryMap[i].bReadable;
						m_MemMapBasic[j].bWritable = m_MemoryMap[i].bWritable;
						m_MemMapBasic[j].nBank = m_MemoryMap[i].nBank;
						m_MemMapBasic[j].nPage = m_MemoryMap[i].nPage;
						m_MemMapBasic[j].nOffset = m_MemoryMap[i].nOffset;
					}
				}
			}

			//тут надо добавить манипуляцию ПЗУ бейска но только для А16М
			if ((m_fdd.GetFDDType() == DEV_FDD_A16M) && (GetAltProExtCode() & 010))
			{
				if (!m_bBasicOn)
				{
					m_bBasicOn = TRUE;

					//фокус покус. подключаем бейсик. и похер, че там было
					for (int i = 012; i <= 017; i++)
					{
						m_MemoryMap[i].bReadable = TRUE;
						m_MemoryMap[i].bWritable = FALSE;
						m_MemoryMap[i].nBank = i;
						m_MemoryMap[i].nPage = i >> 2;
						m_MemoryMap[i].nOffset = i << 12;
					}
				}
			}
			else
			{
				if (m_bBasicOn) //чтобы зазря циклы не гонять, сделаем флаг.
				{
					for (int i = 012, j = 0; i <= 017; i++, j++)
					{
						m_MemoryMap[i].bReadable = m_MemMapBasic[j].bReadable;
						m_MemoryMap[i].bWritable = m_MemMapBasic[j].bWritable;
						m_MemoryMap[i].nBank = m_MemMapBasic[j].nBank;
						m_MemoryMap[i].nPage = m_MemMapBasic[j].nPage;
						m_MemoryMap[i].nOffset = m_MemMapBasic[j].nOffset;
					}

					m_bBasicOn = FALSE;
				}
			}

		//и при этом это же передаётся в контроллер, т.е. по сути оно сперва передаётся в контроллер, а потом происходят вышеописанные действия
		case 0177132:
			if (bByteOperation)
			{
				m_fdd.SetByte(num, LOBYTE(src));
			}
			else
			{
				m_fdd.SetWord(addr, src);
			}

			break;

		default:
		{
			//если у нас SMK512, то у него есть регистры HDD
			bRet = m_fdd.WriteHDDRegisters(num, ~src);
		}
	}

	return bRet;
}

BOOL CMotherBoard_FDD::OnGetSystemRegister(WORD num, void *pDst, BOOL bByteOperation)
{
	if (CMotherBoard::OnGetSystemRegister(num, pDst, bByteOperation))
	{
		return TRUE;
	}

	WORD *pDst_W = reinterpret_cast<WORD *>(pDst);
	BYTE *pDst_B = reinterpret_cast<BYTE *>(pDst);
	BOOL bRet = TRUE;
	WORD addr = num & 0177776;

	switch (addr)
	{
		case 0177130:
		case 0177132:
			if (m_fdd.GetFDDType() != DEV_FDD_STD && (GetAltProExtCode() & 4)) //бит 2 отключает эти порты по чтению
			{
				return FALSE;
			}

			if (bByteOperation)
			{
				m_fdd.GetByte(num, pDst_B);
			}
			else
			{
				m_fdd.GetWord(addr, pDst_W);
			}

			break;

		default:
		{
			//если у нас SMK512, то у него есть регистры HDD
			WORD data;
			bRet = m_fdd.ReadHDDRegisters(num, data);

			if (bRet)
			{
				if (bByteOperation)
				{
					*pDst_B = LOBYTE(data);
				}
				else
				{
					*pDst_W = data;
				}
			}
		}
	}

	return bRet;
}


void CMotherBoard_FDD::Interception()
{
	WORD pc = (GetRON(R_PC) & 0177776);

	if (pc == 0116076)
	{
		if (m_pParent)
		{
			m_pParent->SendMessage(WM_TAPE_FILE_LOAD);
		}
	}
	else if (pc == m_nFDDCatchAddr)
	{
		//для контроллеров альтпро тут нужно сделать исключение. если вместо ПЗУ - ОЗУ.
		if (m_fdd.GetFDDType() == DEV_FDD_A16M)
		{
			WORD m = GetAltProMode();

			if (m == 0 || m == 040)
			{
				return;
			}
		}

		if (m_fdd.GetFDDType() == DEV_FDD_SMK512)
		{
			WORD m = GetAltProMode();

			if (m == 0 || m == 040 || m == 20 || m == 120 || m == 0100)
			{
				return;
			}
		}

		/*
		эмуляция работы с дисководом. Если мы работает стандартными методами,
		а если нестандартными - то у нас есть полная эмуляция работы с дисководом через порты.
		*/
		if (g_Config.m_bEmulateFDDIO)
		{
			m_fdd.EmulateFDD(this);
			SetRON(R_PC, m_nFDDExitCatchAddr);
		}
	}
}


/*
 Тут вот какая ситуация.
 для а16м карта памяти выглядит так
 0000000 - 64кб - основной массив памяти 32кб ОЗУ + 32кб ПЗУ или чего-либо в зависимости от конфигурации
 0200000 - 8кб ПЗУ прошивки контроллера
 0220000 - 16кб ОЗУ А16М
 0260000 - конец.
 для смк512 карта памяти выглядит так
 0000000 - 64кб - основной массив памяти 32кб ОЗУ + 32кб ПЗУ или чего-либо в зависимости от конфигурации
 0200000 - 8кб ПЗУ прошивки контроллера
 0220000 - 512кб ОЗУ СМК
 участок 0200000-0260000 сохраняется методом SetBlockExt16Memory
 участок 0260000-02220000 - сохраняется методом SetBlockMemorySMK512, тут сохраняются не 512 кб, как можно
 было ожидать, а 512кб - 16 кб
*/


BOOL CMotherBoard_FDD::RestoreMemory(CMSFManager &msf)
{
	//вот поэтому надо карту памяти загружать перед памятью. мы же не знаем, какой тип контроллера загружаем.
	if (msf.IsLoad())
	{
		g_Config.m_BKFDDModel = GetConfFDDModel(); //восстановим тип FDD, полученный на предыдущем этапе
		SetFDDType(g_Config.m_BKFDDModel, FALSE); //перераспределим память, для SMK512
	}

	if (CMotherBoard::RestoreMemory(msf))
	{
		if (msf.IsLoad())
		{
			if (!msf.GetBlockExt16Memory(m_pMemory + 0200000))
			{
				return FALSE;
			}

			if (m_fdd.GetFDDType() == DEV_FDD_SMK512)
				if (!msf.GetBlockMemorySMK512(m_pMemory + 0260000))
				{
					return FALSE;
				}
		}
		else
		{
			if (!msf.SetBlockExt16Memory(m_pMemory + 0200000))
			{
				return FALSE;
			}

			if (m_fdd.GetFDDType() == DEV_FDD_SMK512)
				if (!msf.SetBlockMemorySMK512(m_pMemory + 0260000))
				{
					return FALSE;
				}
		}

		return TRUE;
	}

	return FALSE;
}
