#pragma once
#include "FDDStd326.h"
#include "FloppyDrive.h"

/*
Эмуляция 1818ВГ93 взята из XSpeccy: https://github.com/samstyle/Xpeccy
*/

constexpr auto FDC_DATA = 0177620; // регистр данных
constexpr auto FDC_SEC = 0177622; // регистр секторов
constexpr auto FDC_TRK = 0177624; // регистр дорожки
constexpr auto FDC_COM = 0177626; // регистр запись - регистр команд / чтение - регистр состояния
constexpr auto FDC_CSR = 0177630; // регистр управления, доступен только по записи


/* Status Bits for Type I Commands */
constexpr uint8_t VG93_STAT_NOT_READY   = (1 << 7); // единица в этом разряде указывает, что ГМД не готов к
// работе.Команды считывания данных с диска или записи их на диск и команды
// поиска и чтения индексного поля на ГМД в этом случае не выполняются.
constexpr uint8_t VG93_STAT_WPROT       = (1 << 6); // сигнал защиты записи; единица в этом разряде запрещает
// режимы вывода информации на ГМД, в остальных случаях не используется.
constexpr uint8_t VG93_STAT_HLD         = (1 << 5); // единица в этом разряде указывает на то, что МГ
// загружена.Это состоянте определяется логическим состоянием сигналов HLD
// и HRDY.Применяются для выполнения вспомогательных команд (1 тип).
constexpr uint8_t VG93_STAT_SEEK_ERROR  = (1 << 4); // единица в этом разряде означает, что в процессе
// поиска требуемый массив не найден.
constexpr uint8_t VG93_STAT_CRC_ERROR   = (1 << 3); // единица в этом разряде означает для всех команд,
// что вычисленный КК не совпадает с записанным в конце массива.
constexpr uint8_t VG93_STAT_TRACK0      = (1 << 2); // единица в этом разряде указывает, что МГ установлена на
// нулевую дорожку(для вспомогательных команд).
constexpr uint8_t VG93_STAT_INDEX       = (1 << 1); // логическая единица данного разряда при выполнении вспомогательных команд
// указывает о приходе индексного импульса
constexpr uint8_t VG93_STAT_BUSY        = (1 << 0); // единица в этом разряде означает, что микросхема занята
// выполнением одной из команд - считывания данных с диска или записи их на
// диск и поика и чтения индексного поля на ГМД.Относительно вспомогательных
// команд S0 указывает о вводе их в микросхему.
/* Status Bits for Type II, III Commands */
constexpr uint8_t VG93_STAT_REC_TYPE    = (1 << 5); // определяет характер записи в процессе выполнения
// команды "Чтение сектора" в соответствии с кодом адресной метки:
// 1 - со стиранием адресной метки;
// 0 - без стирания адресной метки.
constexpr uint8_t VG93_STAT_WRT_FAULT   = (1 << 5); // применяется в режиме "Запись дорожки". Единица
// в этом разряде указывает, что требуемая дорожка не найдена(не совпадает
// адрес, ошибка КК, и т.д.).
constexpr uint8_t VG93_STAT_NOT_FOUND   = (1 << 4); // единица в этом разряде определяет, что при
// выполнении команды считывания данных с диска или записи их на диск и
// команд поиска и чтения индексного поля на ГМД не найдены требуемые массивы
// на диске.
// При выполнении команд "Чтение дорожки" и "Запись дорожки" этот разряд не
// используется, так как номер дорожки не проверяется.
constexpr uint8_t VG93_STAT_LOST_DATA   = (1 << 2); // единица в этом разряде указывает, что в процессе
// обмена информацией не обслужен сигнал "Запрос данных" при выполнении команд
// считывания данных с диска или записи их на диск.
constexpr uint8_t VG93_STAT_DRQ         = (1 << 1); // при командах считывания данных с диска или записи их на диски командах
// поиска и чтения индексного поля на ГМД означает запрос или вывод очередного
// байта данных

/* WD179X Commands */
constexpr uint8_t VG93_RESTORE          = 0x00; /* Type I */
constexpr uint8_t VG93_SEEK             = 0x10; /* Type I */
constexpr uint8_t VG93_STEP             = 0x20; /* Type I */
constexpr uint8_t VG93_STEP_IN          = 0x40; /* Type I */
constexpr uint8_t VG93_STEP_OUT         = 0x60; /* Type I */
constexpr uint8_t VG93_READ_REC         = 0x80; /* Type II */
constexpr uint8_t VG93_WRITE_REC        = 0xA0; /* Type II */
constexpr uint8_t VG93_READ_ADDR        = 0xC0; /* Type III */
constexpr uint8_t VG93_FORCE_INTR       = 0xD0; /* Type IV */
constexpr uint8_t VG93_READ_TRACK       = 0xE0; /* Type III */
constexpr uint8_t VG93_WRITE_TRACK      = 0xF0; /* Type III */


// биты параметров команд
// тип I
constexpr uint8_t CMD_U = (1 << 4); // код, определяюший состояние Рг.Дор при перемещении МГ
// (при u = 0 состояние Рг.Дор не изменяется;
// при u = 1 пр каждом шаговом импульсе Рг.Дор
// изменяется на один бит).
constexpr uint8_t CMD_H = (1 << 3); // код загрузки головки ( при h=0 головка не загружается;
// при h = 1 головка устанавливается на диск );
constexpr uint8_t CMD_V = (1 << 2); // код, определяющий необходимость проверки положения МГ
// (при V = 0 положение МГ не проверяется;
// при V = 1 проверяется номер дорожки, на которой находится МГ);
constexpr uint8_t CMD_SPD = 0x3;    // коды, определяющие скорость перемещения МГ.
// тип II,III
constexpr uint8_t CMD_M = (1 << 4); //код, указывающий на обращение к одному сектору (m=0)
// или больше одного(m = 1).При m = 0 после считывания / записи одного
// сектора работа прекращается.При m = 1 после окончания работы с
// первым сектором в Рг.Сект.прибавляется единица и продолжается
// обработка следующего сектора.Эта операция продолжается до тех
// пор, пока не будет обработан самый последний сектор на данной
// дорожке;
constexpr uint8_t CMD_S = (1 << 3); // код, определяющий номер стороны диска (0 или 1);
constexpr uint8_t CMD_E = (1 << 2); // код, указывающий на необходимость задержки продолжительностью 15мс
// перед выдачей сигнала HLD после появления сигнала HRDY
// (при E = 0 задержка не осуществляется);
constexpr uint8_t CMD_C = (1 << 1); // код, указывающий на необходимость проверки номера стороны диска
// в процессе идентификации индексной области
// (при C = 0 номер стороны диска не проверяется);
constexpr uint8_t CMD_A = (1 << 0); // код, используемый для выбора одного из двух возможных байтов
// адресной метки данных(ДАМ) для записи в области ДАМ.При a0 = 1
// записывается байт F8, указывающий, что данные могут стиратья;
// при a0 = 0 записывается байт FB, указывающий, что область данных
// сохраняется.


class CFDDOptok : public CFDDStd326
{
		DECLARE_DYNAMIC(CFDDOptok)

		using fdcCall = void(CFDDOptok::*)();

		using vgComItem = struct
		{
			int     mask;
			int     val;
			fdcCall plan;
		};
		std::vector<vgComItem> vgComTab;

		static const int    m_pauses[4];

		uint16_t            m_regCSR;
		uint8_t             m_data;
		uint8_t             m_com;
		uint8_t             m_debugCMD;
		uint8_t             m_sector;
		int                 m_nByteDelay;
		uint16_t            m_crc;              // calculated crc
		uint16_t            m_fcrc;             // crc get from floppy
		uint8_t             m_buf[6];
		int                 m_fmode;
		int                 m_cnt;
		int                 m_wait;             // pause (ns)
		std::vector<fdcCall> m_plan;            // current task
		int                 m_pos;              // pos in plan
		bool                m_drq;              // true: data request
		FLP_STEP            m_step;             // step dir
		bool                m_idle;
		bool                m_hd;               // HD mode (trklen 12500, bytedelay 16000)
		bool                m_motor;            // motor on/off

		void                fdcSync(int ns);
		void                add_crc_16(uint8_t val);
		void                rdFCRC();
		void                wrCRC();
		int                 waitADR();
		int                 seekADR();
		bool                vgSendByte();
		bool                vgGetByte();
		void                vgwait();
		void                vgstp00();
		void                vgstp01();
		void                vgStop();
		void                vgchk00();
		void                vgchk01();
		void                vgchk02();
		void                vgchk();
		void                vgres00();
		void                vgres01();
		void                vgRest();
		void                vgseek00();
		void                vgseek01();
		void                vgSeek();
		void                vgstpf();
		void                vgstpb();
		void                vgstep();
		void                vgStepF();
		void                vgStepB();
		void                vgStep();
		void                vgrds00();
		void                vgrds01();
		void                vgrds02();
		void                vgrds03();
		void                vgrds04();
		void                vgrds05();
		void                vgRdSec();
		void                vgwrs00();
		void                vgwrs01();
		void                vgwrs02();
		void                vgwrs03();
		void                vgWrSec();
		void                vgrda00();
		void                vgrda01();
		void                vgRdAdr();
		void                vgrdt00();
		void                vgrdt01();
		void                vgRdTrk();
		void                vgwrt00();
		void                vgwrt01();
		void                vgwrt02();
		void                vgWrTrk();
		void                vgExec(uint8_t com);
		void                vgReset();

#ifdef _DEBUG
		void                DecodeCommand(uint8_t com);
#endif
	protected:
		// Инициализация памяти
		virtual bool        OnInitDevice() override;

		virtual inline bool IsEngineOn() const override;

	public:
		CFDDOptok();
		virtual ~CFDDOptok() override;
		virtual bool        InitialSetDevice() override;

		virtual BK_DEV_MPI GetBoardModel() const override
		{
			return BK_DEV_MPI::OPTOK;
		}

		virtual bool        Interception() const override; // Вызывается после каждой команды, для перехвата функций
		virtual bool        InitDevice(const uint16_t addrStart) override;

		// Virtual method called after reset command
		virtual void        OnReset() override;

		// Methods for Set/Get byte/word
		virtual bool        GetByteT(const uint16_t addr, uint8_t *pValue, int &nTC) override;
		virtual bool        GetWordT(const uint16_t addr, uint16_t *pValue, int &nTC) override;
		virtual bool        SetByteT(const uint16_t addr, uint8_t value, int &nTC) override;
		virtual bool        SetWordT(const uint16_t addr, uint16_t value, int &nTC) override;
		virtual bool        GetByteTInternal(const uint16_t addr, uint8_t *pValue, int &nTC) override;
		virtual bool        GetWordTInternal(const uint16_t addr, uint16_t *pValue, int &nTC) override;

		virtual void        Periodic() override;

		void                SetHD(bool bhd);

		uint16_t            GetDebugCSR() const
		{
			return m_regCSR;
		}
		uint16_t            GetDebugCMD() const
		{
			return m_debugCMD;
		}
		uint16_t            GetDebugSector() const
		{
			return m_sector;
		}
		uint16_t            GetDebugTrack() const
		{
			return m_track;
		}
		uint16_t            GetDebugData() const
		{
			return m_data;
		}

		uint16_t            GetDebugStatus() const;

};

/*
Блок КНГМД Опток

000000--банк 0 ---страница 4 --- не используется
020000--банк 1 ---страница 5---- DISK_OPTOK.rom во время работы
040000--банк 2 ---страница 6 --- не используется
060000--банк 3 ---страница 7---- DISK_OPTOK.rom

Доп ОЗУ нет.
*/

