// FDDStd326.cpp: implementation of the CFDD_Std class.
//


#include "stdafx.h"
#include "resource.h"
#include "FDDStd326.h"

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

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

// Маска флагов, сохраняемых в m_flags
constexpr auto FLOPPY_CMD_MASKSTORED = (FLOPPY_CMD_SELECT_DRIVE_A | FLOPPY_CMD_SELECT_DRIVE_B | FLOPPY_CMD_SELECT_DRIVE_C | FLOPPY_CMD_SELECT_DRIVE_D | \
                                        FLOPPY_CMD_ENGINESTART | FLOPPY_CMD_SIDEUP | FLOPPY_CMD_DIR | FLOPPY_CMD_STEP | FLOPPY_CMD_READDATA | \
                                        FLOPPY_CMD_WRITEMARKER | FLOPPY_CMD_PRECORRECTION);

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

CFloppyDrive::CFloppyDrive()
	: strFileName(_T(""))
	, bPresent(false)
	, okReadOnly(false)
	, datatrack(0)
	, dataside(0)
	, dataptr(0)
{
	memset(data, 0, FLOPPY_RAWTRACKSIZE);
	memset(marker, 0, FLOPPY_RAWMARKERSIZE);
}

void CFloppyDrive::Reset()
{
	datatrack = dataside = 0;
	dataptr = 0;
}


CFDDStd326::CFDDStd326()
	: m_pDrive(nullptr)
	, m_drive(BK_FDD_DRIVE::NONE)
{
	InitVariables();
#ifdef _DEBUG
	m_msec = GetTickCount();
	m_nFrame = 0;
#endif
	m_drivedata[BK_FDD_DRIVE::A].bPresent = true;
	m_drivedata[BK_FDD_DRIVE::B].bPresent = true;
	m_drivedata[BK_FDD_DRIVE::C].bPresent = true;
	m_drivedata[BK_FDD_DRIVE::D].bPresent = true;
	// пересоздадим массив памяти.
	m_nMemorySize = 0100000;
	InitMemory(BK_PORTSIO_AREA);
}

CFDDStd326::~CFDDStd326()
{
	DetachDrives();
}

BK_DEV_MPI CFDDStd326::GetBoardModel()
{
	return BK_DEV_MPI::STD_326;
}

bool CFDDStd326::InitDevice(uint16_t addrStart)
{
	// выключаем ПЗУ бейсика БК10
	if (g_Platform.GetBoardPtr())
	{
		g_Platform.GetBoardPtr()->BasicRomOFF(true);
	}

	// инициализация карты памяти
	bool bRet = OnInitDevice();

	if (bRet)
	{
		// 326 прошивка
		g_Platform.SetInterseptAddress(0160372, 0161564);
	}

	Reset();
	return bRet;
}

bool CFDDStd326::OnInitDevice()
{
	static const CBKMPIDevice::BoardRomList_t m_arrFDDRomList[FDDRomListSize] =
	{
		// инициализируем список загружаемых ПЗУ
		{ IDS_INI_FDR, KNGMD_FDDROM_BNK }
	};
	// инициализация карты памяти
	return InitMemoryModules(m_arrFDDRomList, FDDRomListSize);
}

void CFDDStd326::InitVariables()
{
	m_side = m_track = 0;
	m_datareg = m_writereg = m_shiftreg = 0;
	m_bSearchSyncTrigger = false;
	m_bWriteMode = m_writemarker = m_shiftmarker = m_bCRCCalc = false;
	m_bSearchSync = true;
	m_writeflag = m_shiftflag = false;
	m_bTrackChanged = false;
	m_status = 0;
	m_flags = FLOPPY_CMD_SIDEUP | FLOPPY_CMD_DIR | FLOPPY_CMD_WRITEMARKER;
}

void CFDDStd326::DetachDrives()
{
	for (int drive = BK_FDD_DRIVE::A; drive < BK_FDD_DRIVE::NUMS; ++drive)
	{
		DetachImage(drive);
	}
}


void CFDDStd326::OnReset()
{
	FlushChanges();
	m_pDrive = nullptr;
	m_drive = BK_FDD_DRIVE::NONE;
	InitVariables();
	ReadDrivesPath();
}

bool CFDDStd326::GetByteT(register uint16_t addr, register uint8_t *pValue, int &nTC)
{
	register uint16_t w;
	register bool b = CFDDStd326::GetWordT(addr, &w, nTC);
	*pValue = ((addr & 1) ? (w >> 8) : w) & 0377;
	return b;
}

bool CFDDStd326::GetWordT(register uint16_t addr, register uint16_t *pValue, int &nTC)
{
	if (0160000 <= addr && addr < m_nBKPortsIOArea)
	{
		nTC = m_MemoryMap[KNGMD_FDDROM_BNK].nTimingCorrection;
		*pValue = *(reinterpret_cast<uint16_t *>(&m_pMemory[addr & 077776]));
		return true;
	}

	switch (addr & 0177776)
	{
		case 0177130:
			*pValue = GetState();
			// TRACE(_T("Rd 177130: %06o\n"), *pValue);
			break;

		case 0177132:
			*pValue = GetData();
			// TRACE(_T("fdr - pos:%06d  %06o\n"), m_drivedata[m_drive].dataptr, *pValue);
			break;

		default:
			nTC = RAM_TIMING_CORR_VALUE_D;
			*pValue = 0;
			return false;
	}

	nTC = REG_TIMING_CORR_VALUE;
	return true;
}

bool CFDDStd326::SetByteT(register uint16_t addr, register uint8_t value, int &nTC)
{
	// запись в старший байт просто теряется, но RPLY выдаётся
	// запись в младший байт работает
	switch (addr & 0177776)
	{
		case 0177130:
			if (!(addr & 1))
			{
				SetCommand(value);
			}

			break;

		case 0177132:
			if (!(addr & 1))
			{
				WriteData(value);
			}

			break;

		default:
			nTC = RAM_TIMING_CORR_VALUE_D;
			return false;
	}

	nTC = REG_TIMING_CORR_VALUE;
	return true;
}

bool CFDDStd326::SetWordT(register uint16_t addr, register uint16_t value, int &nTC)
{
	switch (addr & 0177776)
	{
		case 0177130:
			SetCommand(value);
			break;

		case 0177132:
			WriteData(value);
			break;

		default:
			nTC = RAM_TIMING_CORR_VALUE_D;
			return false;
	}

	nTC = REG_TIMING_CORR_VALUE;
	return true;
}



void CFDDStd326::ReadDrivesPath()
{
	CString strImgName;

	for (int i = BK_FDD_DRIVE::A; i < BK_FDD_DRIVE::NUMS; ++i)
	{
		strImgName = g_Config.GetDriveImgName(i);

		if (strImgName.CompareNoCase(g_strEmptyUnit))
		{
			// если ничего не было подключено, то подключаем
			AttachImage(i, strImgName);
		}
		else
		{
			// если что-то было подключено - отключаем
			DetachImage(i);
		}
	}
}

bool CFDDStd326::IsAttached(int drive)
{
	return (m_drivedata[drive].fFile.m_hFile != CFile::hFileNull);
}

CFile *CFDDStd326::GetAttachedDriveFile(int drive)
{
	return &(m_drivedata[drive].fFile);
}

bool CFDDStd326::IsReadOnly(int drive)
{
	return m_drivedata[drive].okReadOnly;
}

bool CFDDStd326::GetDriveState(int drive)
{
	return m_drivedata[drive].bPresent; // true - привод есть, false - привода нет. для разных типов контроллеров
}

bool CFDDStd326::AttachImage(int drive, CString &sFileName)
{
	// если пытаемся приаттачить уже ранее приаттаченный этот же образ
	if (m_drivedata[drive].strFileName.CompareNoCase(sFileName) == 0)
	{
		return true; // то просто выйдем, как будто всё как надо сделали.
	}

	// Если какой-то другой образ подсоединён, сперва отсоединим
	DetachImage(drive);
	// Открываем файл
	m_drivedata[drive].okReadOnly = false;
	bool bRes = !!(m_drivedata[drive].fFile.Open(sFileName, CFile::modeReadWrite | CFile::shareDenyWrite)); // сперва для чтения-записи

	if (!bRes) // если не получилось
	{
		m_drivedata[drive].okReadOnly = true; // ставим защиту от записи
		bRes = !!(m_drivedata[drive].fFile.Open(sFileName, CFile::modeRead | CFile::shareDenyWrite)); // то просто для чтения

		if (!bRes)
		{
			return false; // вообще никак не получилось открыть
		}
	}

	m_drivedata[drive].strFileName = sFileName;
	InitVariables();
	m_status = FLOPPY_STATUS_TRACK0;

	if (m_drivedata[drive].okReadOnly)
	{
		m_status |= FLOPPY_STATUS_WRITEPROTECT;
	}

	PrepareTrack();
	return true;
}

void CFDDStd326::DetachImage(int drive)
{
	if (IsAttached(drive))
	{
		FlushChanges();
		m_drivedata[drive].strFileName.Empty();
		m_drivedata[drive].fFile.Close();
		m_drivedata[drive].okReadOnly = false;
		m_drivedata[drive].Reset();
	}
}

inline bool CFDDStd326::IsEngineOn()
{
	return !!(m_flags & FLOPPY_CMD_ENGINESTART);
}

uint16_t CFDDStd326::GetState()
{
	if (m_pDrive == nullptr)    // если привод не выбран
	{
		return 0;               // то возвращаем 0
	}

	if (m_pDrive->fFile.m_hFile == CFile::hFileNull)    // если файл не открыт - аналог того, что в дисководе нет диска
	{
		return FLOPPY_STATUS_INDEXMARK | (m_status & FLOPPY_STATUS_TRACK0);
	}

	std::lock_guard<std::mutex> lk(m_mutPeriodicBusy);

	// далее, формируем возвращаемый статус, для выбранного привода.
	// если двигатель включён, то выставим флаг готовности.
	if (IsEngineOn())
	{
		m_status |= FLOPPY_STATUS_RDY;
	}
	else
	{
		m_status &= ~(FLOPPY_STATUS_RDY | FLOPPY_STATUS_MOREDATA | FLOPPY_STATUS_CHECKSUMOK);
	}

	return m_status;
}

void CFDDStd326::SetCommand(uint16_t cmd)
{
	bool okPrepareTrack = false;  // Нужно ли считывать дорожку в буфер
	// Проверим, не сменился ли текущий привод
	int newdrive = BK_FDD_DRIVE::NONE;

	// тут выбирается текущий привод, чем более младший бит, тем больше приоритет
	// если установлено несколько битов - выбирается тот, что младше.
	if (cmd & 1)
	{
		newdrive = BK_FDD_DRIVE::A;
	}
	else if (cmd & 2)
	{
		newdrive = BK_FDD_DRIVE::B;
	}
	else if (cmd & 4)
	{
		newdrive = BK_FDD_DRIVE::C;
	}
	else if (cmd & 8)
	{
		newdrive = BK_FDD_DRIVE::D;
	}

	if (m_drive != newdrive)
	{
		FlushChanges();
		m_drive = newdrive;
		m_pDrive = (newdrive == BK_FDD_DRIVE::NONE) ? nullptr : m_drivedata + m_drive;
		okPrepareTrack = true;
	}

	// Копируем новые флаги в m_flags
	m_flags &= ~FLOPPY_CMD_MASKSTORED;
	m_flags |= cmd & FLOPPY_CMD_MASKSTORED;

	if (m_drive == BK_FDD_DRIVE::NONE) // если привод не выбран
	{
		return; // нечего и делать
	}

	// Проверяем, не сменилась ли сторона
	if (m_flags & FLOPPY_CMD_SIDEUP)  // Выбор стороны: 0 - нижняя, 1 - верхняя
	{
		if (m_side == 0)
		{
			m_side = 1;
			okPrepareTrack = true;
		}
	}
	else
	{
		if (m_side == 1)
		{
			m_side = 0;
			okPrepareTrack = true;
		}
	}

	if (m_flags & FLOPPY_CMD_STEP)  // Двигаем головки в заданном направлении
	{
		if (m_flags & FLOPPY_CMD_DIR)
		{
			if (m_track < 82)
			{
				m_track++;
				okPrepareTrack = true;
			}
		}
		else
		{
			if (m_track >= 1)
			{
				m_track--;
				okPrepareTrack = true;
			}
		}

		if (m_track == 0)
		{
			m_status |= FLOPPY_STATUS_TRACK0;
		}
		else
		{
			m_status &= ~FLOPPY_STATUS_TRACK0;
		}
	}

	if (okPrepareTrack)
	{
		PrepareTrack();
	}

	if (m_flags & FLOPPY_CMD_READDATA)  // Поиск маркера
	{
		m_bSearchSyncTrigger = true;    // если бит FLOPPY_CMD_READDATA == 1, взводим триггер
	}
	else if (m_bSearchSyncTrigger)      // если бит FLOPPY_CMD_READDATA == 0 и триггер взведён
	{
		m_bSearchSyncTrigger = false;   // переходим в режим поиска маркера
		m_bSearchSync = true;
		m_bCRCCalc = true;
		m_status &= ~(FLOPPY_STATUS_CHECKSUMOK | FLOPPY_STATUS_MOREDATA);
	}

	if (m_bWriteMode && (m_flags & FLOPPY_CMD_WRITEMARKER))  // Запись маркера
	{
		m_writemarker = true;
		m_status &= ~(FLOPPY_STATUS_CHECKSUMOK | FLOPPY_STATUS_MOREDATA);
	}
}

uint16_t CFDDStd326::GetDataDebug() // Получить значение порта 177132 - данные для отладки
{
	return m_datareg;
}

uint16_t CFDDStd326::GetStateDebug() // Получить значение порта 177130 - состояние устройства для отладки
{
	return m_status;
}

uint16_t CFDDStd326::GetWriteDataDebug() // Получить значение порта 177132 - переданные данные для отладки
{
	return m_writereg;
}

uint16_t CFDDStd326::GetCmdDebug() // Получить значение порта 177130 - переданные команды для отладки
{
	return m_flags;
}


void CFDDStd326::OnLoadRomModule(int nSize, int nBank)
{
	// у ПЗУ RE326 RE253 работает только 4кБ, выше - RPLY не выдаётся
	// если > 4кб, то m_nBKPortsIOArea = BK_PORTSIO_AREA
	// если <= 4кб, то m_nBKPortsIOArea = 0170000
	if (nBank == KNGMD_FDDROM_BNK)
	{
		if (nSize <= 010000)
		{
			m_nBKPortsIOArea = 0170000;
		}
	}
}

void CFDDStd326::MemoryManager()
{
	CBKMPIDevice::MemoryManager();
	m_MemoryMap[KNGMD_FDDROM_BNK].bReadable = TRUE;
	m_MemoryMap[KNGMD_FDDROM_BNK].nTimingCorrection = ROM_TIMING_CORR_VALUE;
}

bool CFDDStd326::RestoreRegisters(DWORD id, CMSFManager *pMsf)
{
	// тут можно реализовать сохранение/восстановление регистров
	return true;
}

bool CFDDStd326::RestoreMemory(DWORD id, CMSFManager *pMsf)
{
	if (pMsf->IsLoad())
	{
		return pMsf->GetBlockBaseMemory(id, m_pMemory, m_nMemorySize);
	}
	else
	{
		return pMsf->SetBlockBaseMemory(id, m_pMemory, m_nMemorySize);
	}
}

bool CFDDStd326::RestoreMemoryMap(DWORD id, CMSFManager *pMsf)
{
	if (pMsf->IsLoad())
	{
		return pMsf->GetBlockMemMap(id, m_MemoryMap);
	}
	else
	{
		return pMsf->SetBlockMemMap(id, m_MemoryMap);
	}
}


uint16_t CFDDStd326::GetData()
{
	std::lock_guard<std::mutex> lk(m_mutPeriodicBusy);
	m_status &= ~(FLOPPY_STATUS_MOREDATA);
	m_bWriteMode = m_bSearchSync = false;
	m_writeflag = m_shiftflag = false;

	if (m_pDrive == nullptr || m_pDrive->fFile.m_hFile == CFile::hFileNull)
	{
		return 0;
	}

	return m_datareg;
}

void CFDDStd326::WriteData(uint16_t data)
{
	std::lock_guard<std::mutex> lk(m_mutPeriodicBusy);
	m_bWriteMode = true;  // Переключаемся в режим записи, если ещё не переключились
	m_bSearchSync = false;

	if (!m_writeflag && !m_shiftflag)  // оба регистра пустые
	{
		m_shiftreg = data;
		m_shiftflag = true;
		m_status |= FLOPPY_STATUS_MOREDATA;
	}
	else if (!m_writeflag && m_shiftflag)  // Регистр записи пуст
	{
		m_writereg = data;
		m_writeflag = true;
		m_status &= ~FLOPPY_STATUS_MOREDATA;
	}
	else if (m_writeflag && !m_shiftflag)  // Регистр сдвига пуст
	{
		m_shiftreg = m_writereg;
		m_shiftflag = m_writeflag;
		m_writereg = data;
		m_writeflag = true;
		m_status &= ~FLOPPY_STATUS_MOREDATA;
	}
	else // Оба регистра не пусты
	{
		m_writereg = data;
	}
}

void CFDDStd326::Periodic() // сдвиг на 1 RAW слово на дорожке
{
	if (!IsEngineOn())
	{
		return;    // Вращаем дискеты только если включён мотор
	}

	m_mutPeriodicBusy.lock();

	// Вращаем дискеты во всех приводах сразу
	for (auto &drive : m_drivedata)
	{
		drive.dataptr += 2;

		if (drive.dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			drive.dataptr = 0;
		}
	}

	// Далее обрабатываем чтение/запись на текущем приводе
	if (m_pDrive && IsAttached(m_drive))
	{
		if (m_pDrive->dataptr < FLOPPY_INDEXLENGTH)
			// if (m_pDrive->dataptr >= FLOPPY_RAWTRACKSIZE - FLOPPY_INDEXLENGTH)
		{
			m_status |= FLOPPY_STATUS_INDEXMARK;
		}
		else
		{
			m_status &= ~FLOPPY_STATUS_INDEXMARK;
		}

		if (m_bWriteMode)   // Режим записи
		{
			if (m_shiftflag)
			{
				*(reinterpret_cast<uint16_t *>(&m_pDrive->data[m_pDrive->dataptr])) = m_shiftreg;
				m_shiftflag = false;
				m_bTrackChanged = true;

				if (m_shiftmarker)
				{
					m_pDrive->marker[m_pDrive->dataptr / 2] = true;
					m_shiftmarker = false;
					m_bCRCCalc = true;  // Начинаем считать CRC
				}
				else
				{
					m_pDrive->marker[m_pDrive->dataptr / 2] = false;
				}

				if (m_writeflag)
				{
					m_shiftreg = m_writereg;
					m_shiftflag = m_writeflag;
					m_writeflag = false;
					m_shiftmarker = m_writemarker;
					m_writemarker = false;
					m_status |= FLOPPY_STATUS_MOREDATA;
				}
				else
				{
					if (m_bCRCCalc)  // Прекращаем считать CRC
					{
						m_shiftreg = 0x4444;  // STUB вот она, наша рассчитанная CRC, пока так.
						m_shiftflag = true;
						m_shiftmarker = false;
						m_bCRCCalc = false;
						m_status |= FLOPPY_STATUS_CHECKSUMOK;
					}
				}
			}
		}
		else    // Режим чтения
		{
			register uint16_t t = *(reinterpret_cast<uint16_t *>(&m_pDrive->data[m_pDrive->dataptr]));
			m_datareg = ((t & 0377) << 8) | ((t >> 8) & 0377);

			if (m_status & FLOPPY_STATUS_MOREDATA)
			{
				if (m_bCRCCalc)  // Прекращаем считать CRC
				{
					m_bCRCCalc = false;
					// TODO: Сравниваем рассчитанную CRC с m_datareg
					m_status |= FLOPPY_STATUS_CHECKSUMOK;
				}
			}
			else
			{
				if (m_bSearchSync)  // Поиск маркера
				{
					if (m_pDrive->marker[m_pDrive->dataptr / 2])  // Нашли маркер
					{
						m_status |= FLOPPY_STATUS_MOREDATA;
						m_bSearchSync = false;
					}
				}
				else  // Просто чтение
				{
					m_status |= FLOPPY_STATUS_MOREDATA;
				}
			}
		}
	}

	m_mutPeriodicBusy.unlock();
#ifdef _DEBUG
	// Посчитаем FPS, в норме должно быть 15625 в сек. == 300 об/мин == 5 об/сек
	m_nFrame++; // сколько раз в секунду вызывается эта функция
	DWORD mtmpsec;

	if ((mtmpsec = GetTickCount()) - m_msec >= 1000)
	{
		m_msec = mtmpsec;
		TRACE(_T("FDD Frames %d\n"), m_nFrame);
		m_nFrame = 0;
	}

#endif
}


// Читаем дорожку из файла и заполняем m_data
void CFDDStd326::PrepareTrack()
{
	FlushChanges();

	if (m_pDrive == nullptr)
	{
		return;
	}

	size_t count;
	m_bTrackChanged = false;
	m_pDrive->dataptr = 0;
	m_pDrive->datatrack = m_track;
	m_pDrive->dataside = m_side;
// Предполагаем, что дорожка состоит из 10 секторов, 512 байтов каждый; смещение в файле === ((Track<<1)+SIDE)*5120
	int foffset = (m_track * FLOPPY_SIDES + m_side) * FLOPPY_TRACK_SIZE_BYTES;
	uint8_t data[FLOPPY_TRACK_SIZE_BYTES];
	memset(data, 0, FLOPPY_TRACK_SIZE_BYTES);

	if (m_pDrive->fFile.m_hFile != CFile::hFileNull)
	{
		m_pDrive->fFile.Seek(foffset, CFile::begin);
		count = m_pDrive->fFile.Read(&data, FLOPPY_TRACK_SIZE_BYTES);
		// TODO: Контроль ошибок чтения файла.
	}

	// Заполняем массив m_data и массив m_marker промаркированными данными
	EncodeTrackData(data, m_pDrive->data, m_pDrive->marker, m_track, m_side);
}


void CFDDStd326::FlushChanges()
{
	if (m_drive == BK_FDD_DRIVE::NONE)
	{
		return;
	}

	if (!IsAttached(m_drive))
	{
		return;
	}

	if (!m_bTrackChanged)
	{
		return;
	}

	// Декодируем данные дорожки из m_data
	uint8_t data[FLOPPY_TRACK_SIZE_BYTES];
	memset(data, 0, FLOPPY_TRACK_SIZE_BYTES);
	bool decoded = DecodeTrackData(m_pDrive->data, data);

	if (decoded)  // Записываем файл только если дорожка корректно декодировалась из raw data
	{
		// Предполагаем, что дорожка состоит из 10 секторов, 512 байтов каждый; смещение в файле === ((Track<<1)+SIDE)*5120
		register int foffset = (m_pDrive->datatrack * FLOPPY_SIDES + m_pDrive->dataside) * FLOPPY_TRACK_SIZE_BYTES;
		register int foffset_end = foffset + FLOPPY_TRACK_SIZE_BYTES;
		// Проверяем длину файла
		register auto currentFileSize = int(m_pDrive->fFile.GetLength());

		while (currentFileSize < foffset_end)
		{
			// увеличиваем файл посекторно на нужное кол-во секторов
			uint8_t datafill[FLOPPY_SECTOR];
			memset(datafill, 0, FLOPPY_SECTOR);
			register int bytesToWrite = (foffset_end - currentFileSize) % FLOPPY_SECTOR;

			if (bytesToWrite == 0)
			{
				bytesToWrite = FLOPPY_SECTOR;
			}

			m_pDrive->fFile.Write(datafill, bytesToWrite);
			// TODO: Проверка на ошибки записи
			currentFileSize += bytesToWrite;
		}

		// Сохраняем данные.
		m_pDrive->fFile.Seek(foffset, CFile::begin);
		// size_t dwBytesWritten =
		m_pDrive->fFile.Write(data, FLOPPY_TRACK_SIZE_BYTES);
		// TODO: Проверка на ошибки записи
	}
	else
	{
		// а иначе, просто не записываем, чтоб не попортить образ
	}

	m_bTrackChanged = false;
}


// Заполняем массив m_data и массив m_marker промаркированными данными
void CFDDStd326::EncodeTrackData(const register uint8_t *pSrc, register uint8_t *data, register uint8_t *marker, register int track, register int side)
{
	memset(data, 0, FLOPPY_RAWTRACKSIZE);
	memset(marker, 0, FLOPPY_RAWMARKERSIZE);
	register int ptr = 0;
	register int gap = 42;  // длина GAP4a + GAP1

	for (register int sect = 0; sect < FLOPPY_TRACK_LEN; ++sect)
	{
		// GAP
		for (register int count = 0; count < gap; ++count)
		{
			data[ptr++] = 0x4e;
		}

		// заголовок сектора
		for (register int count = 0; count < 12; ++count)
		{
			data[ptr++] = 0x00;
		}

		// маркер
		marker[ptr / 2] = true;  // Индексный маркер; начало подсчёта CRC
		data[ptr++] = 0xa1;
		data[ptr++] = 0xa1;
		data[ptr++] = 0xa1;
		data[ptr++] = 0xfe;
		data[ptr++] = LOBYTE(track);
		data[ptr++] = (side != 0);
		data[ptr++] = sect + 1;
		data[ptr++] = 2; // Предполагается 512 байтов на сектор;
		// crc
		// TODO: Подсчёт CRC
		data[ptr++] = 0x12;
		data[ptr++] = 0x34;  // CRC stub вот такое у нас пока ЦРЦ

		// синхропоследовательность
		for (register int count = 0; count < 22; ++count)
		{
			data[ptr++] = 0x4e;
		}

		// заголовок данных
		for (register int count = 0; count < 12; ++count)
		{
			data[ptr++] = 0x00;
		}

		// маркер
		marker[ptr / 2] = true;  // Маркер данных; начало подсчёта CRC
		data[ptr++] = 0xa1;
		data[ptr++] = 0xa1;
		data[ptr++] = 0xa1;
		data[ptr++] = 0xfb;
		// данные
		//      for (int count = 0; count < 512; ++count){
		//          data[ptr++] = pSrc[(sect * 512) + count];
		//      }
		// вот так быстрее будет
		memcpy(data + ptr, pSrc + (sect * FLOPPY_SECTOR), FLOPPY_SECTOR);
		ptr += FLOPPY_SECTOR;
		// crc
		// TODO: Подсчёт CRC
		data[ptr++] = 0x43;
		data[ptr++] = 0x21;  // CRC stub вот такое у нас пока ЦРЦ
		gap = 36;  // длина GAP3
	}

	// заполняем GAP4B до конца дорожки
	while (ptr < FLOPPY_RAWTRACKSIZE)
	{
		data[ptr++] = 0x4e;
	}
}

// Декодирование данных дорожки из raw data
// pRaw - массив FLOPPY_RAWTRACKSIZE байтов
// pDest - массив 5120 байтов
// Возвращается: true - декодировано, false - ошибка декодирования
bool CFDDStd326::DecodeTrackData(const register uint8_t *pRaw, register uint8_t *pDest)
{
	register int dataptr = 0;  // Смещение в массиве m_data
	register int destptr = 0;  // Смещение в массиве data
	register uint8_t sectcyl, secthd, sectsec, sectno;
	register int sectorsize;

	for (;;)
	{
		while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x4e)
		{
			dataptr++;    // Пропускаем GAP1 или GAP3
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			break;    // Конец дорожки или ошибка
		}

		while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x00)
		{
			dataptr++;    // Пропускаем синхропоследовательность
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			return false;    // Что-то не так
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1)
		{
			dataptr++;
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1)
		{
			dataptr++;
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1)
		{
			dataptr++;
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			return false;    // Что-то не так
		}

		if (pRaw[dataptr++] != 0xfe)
		{
			return false;    // Индексный маркер не найден
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			sectcyl = pRaw[dataptr++];
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			secthd = pRaw[dataptr++];
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			sectsec = pRaw[dataptr++];
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			sectno = pRaw[dataptr++];
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			return false;    // Что-то не так
		}

		switch (sectno)
		{
			case 1:
				sectorsize = 256;
				break;

			case 2:
				sectorsize = 512;
				break;

			case 3:
				sectorsize = 1024;
				break;

			default:
				return false;    // Что-то не так: неправильный размер сектора
		}

		// crc
		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			dataptr++;
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			dataptr++;
		}

		while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x4e)
		{
			dataptr++;    // Пропускаем GAP2
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			return false;    // Что-то не так
		}

		while (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0x00)
		{
			dataptr++;    // Пропускаем синхропоследовательность
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			return false;    // Что-то не так
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1)
		{
			dataptr++;
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1)
		{
			dataptr++;
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE && pRaw[dataptr] == 0xa1)
		{
			dataptr++;
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			return false;    // Что-то не так
		}

		if (pRaw[dataptr++] != 0xfb)
		{
			return false;    // Маркер данных не найден
		}

		for (int count = 0; count < sectorsize; ++count)  // Копируем данные сектора
		{
			if (destptr >= FLOPPY_TRACK_SIZE_BYTES)
			{
				break;    // Конец дорожки или ошибка
			}

			pDest[destptr++] = pRaw[dataptr++];

			if (dataptr >= FLOPPY_RAWTRACKSIZE)
			{
				return false;    // Что-то не так
			}
		}

		if (dataptr >= FLOPPY_RAWTRACKSIZE)
		{
			return false;    // Что-то не так
		}

		// crc
		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			dataptr++;
		}

		if (dataptr < FLOPPY_RAWTRACKSIZE)
		{
			dataptr++;
		}
	}

	return true;
}


bool CFDDStd326::Interception()
{
	/*
	эмуляция работы с дисководом. Если мы работаем стандартными методами,
	а если нестандартными - то у нас есть полная эмуляция работы с дисководом через порты.
	*/
	if (g_Config.m_bEmulateFDDIO)
	{
		g_Platform.EmulateFDD();
		return true;
	}

	return false;
}

