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

/*  This file is part of BKBTL.
    BKBTL is free software: you can redistribute it and/or modify it under the terms
of the GNU Lesser General Public License as published by the Free Software Foundation,
either version 3 of the License, or (at your option) any later version.
    BKBTL is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
See the GNU Lesser General Public License for more details.
    You should have received a copy of the GNU Lesser General Public License along with
BKBTL. If not, see <http://www.gnu.org/licenses/>. */


// FDDController.h: interface for the CFDDController class.
//


#pragma once

#include "Globals.h"
#include "Device.h"
#include "HDD.h"
#include "LockVarType.h"


#define A16M_ROM_10 16
#define A16M_A0_10 18
#define A16M_A2_10 20

#define A16M_ROM_11M 50
#define A16M_A0_11M 52
#define A16M_A2_11M 54

//////////////////////////////////////////////////////////////////////
// CFloppy

#define FLOPPY_FSM_IDLE         0

#define FLOPPY_CMD_SELECT_DRIVE_A       01      //выбор накопителя 0
#define FLOPPY_CMD_SELECT_DRIVE_B       02      //выбор накопителя 1
#define FLOPPY_CMD_SELECT_DRIVE_C       04      //выбор накопителя 2
#define FLOPPY_CMD_SELECT_DRIVE_D       010     //выбор накопителя 3
#define FLOPPY_CMD_ENGINESTART          020     //включение электродвигателя
#define FLOPPY_CMD_SIDEUP               040     //выбор стороны, 0 - верхняя
#define FLOPPY_CMD_DIR                  0100    //направление шага
#define FLOPPY_CMD_STEP                 0200    //шаг
#define FLOPPY_CMD_READDATA             0400    //признак "начало чтения"
#define FLOPPY_CMD_WRITEMARKER          01000   //признак "запись маркера"
#define FLOPPY_CMD_PRECORRECTION        02000   //включение схемы прекоррекции

//dir == 0 to center (towards trk0)
//dir == 1 from center (towards trk80)

#define FLOPPY_STATUS_TRACK0            01      //признак "дорожка 0"
#define FLOPPY_STATUS_RDY               02      //признак готовности накопителя
#define FLOPPY_STATUS_WRITEPROTECT      04      //запрет записи
#define FLOPPY_STATUS_MOREDATA          0200    //признак, что данные готовы или требование новых данных
#define FLOPPY_STATUS_CHECKSUMOK        040000  //для чтения - признак записи ЦКС
#define FLOPPY_STATUS_INDEXMARK         0100000 //датчик индексного отверстия

#define FLOPPY_RAWTRACKSIZE             6250
#define FLOPPY_RAWMARKERSIZE            (FLOPPY_RAWTRACKSIZE / 2)
#define FLOPPY_INDEXLENGTH              32

#define ALTPRO_A16M_START_MODE 0160
#define ALTPRO_A16M_STD10_MODE 060
#define ALTPRO_A16M_OZU10_MODE 0120
#define ALTPRO_A16M_BASIC_MODE 020
#define ALTPRO_A16M_STD11_MODE 0140
#define ALTPRO_A16M_OZU11_MODE 040
#define ALTPRO_A16M_OZUZZ_MODE 0100
#define ALTPRO_A16M_HLT11_MODE 0

#define ALTPRO_SMK_SYS_MODE   0160
#define ALTPRO_SMK_STD10_MODE 060
#define ALTPRO_SMK_OZU10_MODE 0120
#define ALTPRO_SMK_ALL_MODE   020
#define ALTPRO_SMK_STD11_MODE 0140
#define ALTPRO_SMK_OZU11_MODE 040
#define ALTPRO_SMK_HLT10_MODE 0100
#define ALTPRO_SMK_HLT11_MODE 0

#define ALTPRO_BEGIN_STROB_TRIGGER 6
#define ALTPRO_MODE_MASK 0160
#define ALTPRO_CODE_MASK 02015

struct CFloppyDrive
{
		CFile fFile;
		CString strFileName;                // имя файла образа, которй примонтирован в привод, чтобы повторно не перепримонтировывать
		bool bPresent;                      // Флаг физического присутствия привода, для разных контроллеров
		bool okReadOnly;                    // Флаг защиты от записи
		WORD datatrack;                     // Номер трека данных в массиве data
		WORD dataside;                      // Сторона диска данных в массиве data
		WORD dataptr;                       // Смещение данных внутри data - позиция заголовка
		BYTE data[FLOPPY_RAWTRACKSIZE];     // Raw track image for the current track
		BYTE marker[FLOPPY_RAWMARKERSIZE];  // Позиции маркеров

	public:
		CFloppyDrive();
		void Reset();
};

class CMotherBoard;

class CFDDController : public CDevice
{
#ifdef _DEBUG
//дебужные переменные, нужные для подсчёта и вывода в отладочную консоль число оборотов в секунду.
//правда результат получается какой-то абстрактный
		ULONGLONG m_msec; //счётчики миллисекунд, прошедшие с момента старта системы
		int m_nFrame;
#endif
		enum
		{
			FDD_NOERROR = 0,
			FDD_DISK_PROTECTED,         //1
			FDD_SECT_HEADER_ERROR,      //2
			FDD_0_TRACK_ERROR,          //3
			FDD_POSITIONING_ERROR,      //4
			FDD_SECTOR_NOT_FOUND,       //5
			FDD_NO_DISK,                //6
			FDD_STOP,                   //7
			FDD_ADDR_MARKER_NOT_FOUND,  //010
			FDD_DATA_MARKER_NOT_FOUND,  //011
			FDD_BAD_FORMAT              //012
		};


#pragma pack(push)
#pragma pack(1)
		struct TABLE_EMFDD
		{
			WORD    CSRW;                   //00 копия по записи регистра состояния КНГМД
			WORD    CURTRK;                 //02 адрес текущей дорожки (адрес одного из последующих байтов)
			BYTE    TRKTAB[4];              //04 таблица текущих дорожек
			WORD    TDOWN;                  //10 задержка опускания головки
			WORD    TSTEP;                  //12 задержка перехода с дорожки на дорожку
			BYTE    TRKCOR;                 //14 дорожка начала предкомпенсации
			BYTE    BRETRY;                 //15 число попыток повтора при ошибке
			BYTE    FLAGS;                  //16 рабочая ячейка драйвера
			BYTE    FILLB;                  //17 код заполнения при форматировании
			WORD    FLGPTR;                 //20 указатель на байт признаков (один из следующих байтов)
			BYTE    FLGTAB[4];              //22 байты признаков
			WORD    ADDR;                   //26 адрес начала массива данных в ОЗУ (обязательно чётный)
			short   WCNT;                   //30 количество слов для пересылки
			BYTE    SIDE;                   //32 номер стороны диска
			BYTE    TRK;                    //33 номер дорожки
			BYTE    UNIT;                   //34 номер привода
			BYTE    SECTOR;                 //35 номер сектора
			WORD    WRTVAR;                 //36 значение, записываемое при форматировании
			WORD    MARKER;                 //40 буфер маркера при записи
			WORD    FREE;                   //42 длина пустого остатка сектора
			WORD    INTIME;                 //44 счётчик длительности индекса
			WORD    BUF4;                   //46 буфер для сохранения вектора 4
			WORD    BUFSP;                  //50 буфер для сохранения SP
			WORD    BUFPSW;                 //52 буфер для сохранения PSW
			BYTE    CRETRY;                 //54 счётчик повторов при ошибке
			BYTE    TURNS;                  //55 число оборотов диска при поиске сектора
			BYTE    SECRET;                 //56 число повторных попыток поиска сектора
			BYTE    ERRNUM;                 //57 буфер для номера ошибки
			WORD    MAXSEC;                 //60 число секторов на дорожке
			WORD    HOLTIN;                 //62 время задержки после индекса до первого сектора
			WORD    SECLEN;                 //64 длина сектора в словах (следить за значением при записи, иначе портится разметка диска)
		};
#pragma pack(pop)
	public:
		enum { BK_FDD_DRIVE_A, BK_FDD_DRIVE_B, BK_FDD_DRIVE_C, BK_FDD_DRIVE_D, BK_FDD_DRIVES_NUMBER };

	protected:
		CFloppyDrive    m_drivedata[BK_FDD_DRIVES_NUMBER];

		int             m_drive;       // Номер привода: от 0 до 3; -1 если не выбран
		CFloppyDrive   *m_pDrive;      // Текущий привод; nullptr если не выбран
		WORD            m_track;       // Номер дорожки: от 0 до 79 (в реальных дисководах возможно до 81, в особых случаях даже до 83)
		WORD            m_side;        // Сторона диска: 0 или 1
		WORD            m_status;      // См. определения FLOPPY_STATUS_XXX (флаги, принимаемые от контроллера дисковода)
		WORD            m_flags;       // См. определения FLOPPY_CMD_XXX (флаги, передаваемые контроллеру дисковода)
		WORD            m_datareg;     // Регистр данных режима чтения
		WORD            m_writereg;    // Регистр данных режима записи
		bool            m_writeflag;   // В регистре данных режима записи есть данные
		bool            m_writemarker; // Запись маркера в m_marker
		WORD            m_shiftreg;    // Регистр сдвига режима записи
		bool            m_shiftflag;   // В регистре сдвига режима записи есть данные
		bool            m_shiftmarker; // Запись маркера в m_marker
		bool            m_bWriteMode;  // true = режим записи, false = режим чтения
		bool            m_bSearchSync; // Подрежим чтения: true = поиск синхро последовательности, false = просто чтение
		bool            m_bSearchSyncTrigger; //триггер для отлова перехода из 1 в 0 бита FLOPPY_CMD_READDATA
		bool            m_bCRCCalc;     // true = CRC в процессе подсчёта
		bool            m_bTrackChanged;    // true = m_data было изменено - надо сохранить его в файл

		BKFDD_MODEL     m_FDDModel;
		bool            m_bA16M_Trigger;
		WORD            m_nAltproMode; //код режима работы контроллера Альтпро
		WORD            m_nAltproMemBank; //номер банка памяти контроллера Альтпро

		CATA_IDE        m_ATA_IDE;
		LockVarType     m_lockPeriodicBusy;

	public:
		CFDDController();
		virtual ~CFDDController();

		// Virtual method called after reset command
		virtual void        OnReset();
		void                InitVariables();
		// Methods for Set/Get byte/word
		virtual void        GetByte(WORD addr, BYTE *pValue);
		virtual void        GetWord(WORD addr, WORD *pValue);
		virtual void        SetByte(WORD addr, BYTE value);
		virtual void        SetWord(WORD addr, WORD value);
		bool                AttachImage(int drive, CString &sFileName);
		void                DetachImage(int drive);
		void                ReadDrivesPath();
		void                DetachDrives();
		void                EmulateFDD(CMotherBoard *pChip);
		bool                IsAttached(int drive)
		{
			return (m_drivedata[drive].fFile.m_hFile != CFile::hFileNull);
		}
		inline bool         IsReadOnly(int drive)
		{
			return m_drivedata[drive].okReadOnly;
		}
		inline bool         GetDriveState(int drive)
		{
			return m_drivedata[drive].bPresent; //true - привод есть, false - привода нет. для разных типов контроллеров
		}
		void                init_A16M_10(ConfBKModel_Type *mmodl, int v);
		void                init_A16M_11M(ConfBKModel_Type *mmodl, int v);
		bool                Change_A16M_Mode(ConfBKModel_Type *mmodl, WORD w);
		void                A16M_MemManager_10(BKMEMBank_Type *mmap, ConfBKModel_Type *mmodl);
		void                A16M_MemManager_11M(BKMEMBank_Type *mmap, ConfBKModel_Type *mmodl);
		void                SMK512_MemManager_10(BKMEMBank_Type *mmap, ConfBKModel_Type *mmodl);
		void                SMK512_MemManager_11M(BKMEMBank_Type *mmap, ConfBKModel_Type *mmodl);

		void                SetFDDType(BKFDD_MODEL model);

		inline BKFDD_MODEL GetFDDType()
		{
			return m_FDDModel;
		}
		void                InitHDD();
		bool                WriteHDDRegisters(WORD num, WORD data);    //запись в регистры HDD
		bool                ReadHDDRegisters(WORD num, WORD &data);    //чтение из регистров HDD

		bool                IsEngineOn()
		{
			return (m_flags & FLOPPY_CMD_ENGINESTART) != 0;
		}
		WORD                GetData(void);          // Чтение порта 177132 - данные
		WORD                GetState(void);         // Чтение порта 177130 - состояние устройства
		void                WriteData(WORD Data);   // Запись в порт 177132 - данные
		void                SetCommand(WORD cmd);   // Запись в порт 177130 - команды

		WORD                GetDataDebug()       // Получить значение порта 177132 - данные для отладки
		{
			return m_datareg;
		}
		WORD                GetStateDebug()      // Получить значение порта 177130 - состояние устройства для отладки
		{
			return m_status;
		}
		WORD                GetWriteDataDebug()  // Получить значение порта 177132 - переданные данные для отладки
		{
			return m_writereg;
		}
		WORD                GetCmdDebug()    // Получить значение порта 177130 - переданные команды для отладки
		{
			return m_flags;
		}

		void                Periodic(); // Вращение диска; вызывается каждые 64 мкс - 15625 раз в секунду. Вызывается из основного цикла

		bool                IsPeriodic()
		{
			return m_lockPeriodicBusy.IsLocked() != 0;
		}

	private:
		inline void         A16M_SetMemBank(BKMEMBank_Type *mmap, int nBnk, int BnkNum);
		inline void         A16M_SetMemSegment(BKMEMBank_Type *mmap, int nBnk, int BnkNum);
		inline void         A16M_SetRomBank(BKMEMBank_Type *mmap, int BnkNum);
		void                PrepareTrack();
		void                FlushChanges();  // Если текущая дорожка была изменена, сохраним её
		static void         EncodeTrackData(const register BYTE *pSrc, register BYTE *data, register BYTE *marker, register WORD track, register WORD side);
		static bool         DecodeTrackData(const register BYTE *pRaw, register BYTE *pDest);

};


