#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include "sysp.h"

#define CYL 0

void main(void);
void fdc_out(unsigned char byte);
int  fdc_inp(void);
void int_wait(void);
void dma_init(char *);

void main(void) {

	unsigned i;
	long l;
	char buffer[512];
	char status[7], main_status;
	DPT _far *fdpt;
	FILE *sect;

	printf("\n"
			 "\nРабота с контроллером НГМД"
			 "\n  (C)Фролов А., 1991"
			 "\n");


// Эта программа предназначена только для IBM AT

	if(pc_model() != 0xfc) {
		printf("Эта программа предназначена только для IBM AT\n");
		exit(-1);
	}

// Открываем файл, в который будем записывать
// содержимое самого первого сктора на дискете

	sect = fopen("!sector.dat","wb+");

// Устанавливаем указатель на таблицу
// параметров дискеты

	fdpt = get_dpt();

// Включаем мотор дисковода А:
// Перед этим разрешаем прерывания

//   _enable();
	outp(0x3F2, 0x1C);

// Выполняем задержку для разгона двигателя

	for(l=0;l<200000;l++);

// Показываем содержимое регистра основного
// состояния контроллера

	printf("Мотор включен.\t\t");
	printf("Основное состояние: %02.2X\n",inp(0x3F4));

// Перед чтением сектора необходимо установить
// головку на нужную дорожку, в нашем случае это
// дорожка с номером CYL.

// Выдаем контроллеру команду "Поиск"

	fdc_out(0xf);

// Для команды "Поиск" требуется два байта параметров:
// номер головки/номер накопителя и номер дорожки.
// Мы работаем с нулевой головкой накопителя А:,
// поэтому первый параметр равен 0, второй - CYL

	fdc_out(0);
	fdc_out(CYL);

// Показываем содержимое регистра основного
// состояния контроллера

	printf("\n<<<Поиск>>> \t\t");
	printf("Основное состояние: %02.2X\n",inp(0x3F4));

// Ожидаем прерывание по завершению операции

	int_wait();

// Задержка для позиционирования головки

	for(l=0;l<20000;l++);

// Для проверки результата выполнения команды
// "Поиск" выдаем контроллеру команду
// "Чтение состояния прерывания"

// Выводим содержимое регистра состояния
// ST0  и номер дорожки после выполнения команды
// "Поиск" PCN

	fdc_out(0x8);
	printf("Состояние прерывания:\t");
	printf(" ST0: %02.2X, \t", fdc_inp());
	printf("PCN: %02.2X\n", fdc_inp());

// Для более глубокой диагностики состояния
// контроллера выдаем контроллеру команду
// "Чтение состояния накопителя", выводим
// содержимое регистра состояния ST3

	fdc_out(4);
	fdc_out(0);
	printf("Состояние накопителя:\t ST3: %02.2X\n",fdc_inp());

// Устанавливаем скорость передачи данных 500 Кбайтов/с,
// это значение может различаться для разных типов дискет

	outp(0x3F7, 0);

// Инициализация канала прямого
// доступа к памяти

	dma_init(buffer);

// Выдаем команду "Чтение данных"

	fdc_out(0x66);
	fdc_out(0x0);     // накопитель 0, головка 0

	fdc_out(CYL);     // цилиндр CYL
	fdc_out(0);       // головка 0
	fdc_out(1);       // номер сектора - 1

// Передаем контроллеру технические параметры
// дисковода, берем их из таблицы параметров дискеты.
// Это такие параметры:
//    - размер сектора;
//    - номер последнего сектора на дорожке;
//    - размер промежутка;
//    - число считываемых/записываемых байтов

	fdc_out(fdpt->sec_size);
	fdc_out(fdpt->eot);
	fdc_out(fdpt->gap_rw);
	fdc_out(fdpt->dtl);

// Ожидаем прерывание по завершению операции

	int_wait();

// Считываем и выводим на экран байты результата
// операции "Чтение данных"

	printf("\n<<<Чтение сектора>>> \n");
	printf("   Байты состояния (ST0,ST1,ST2,C,H,R,N):\n");

	for(i=0; i<7; i++) printf("%02.2X\t", (char) fdc_inp());
	printf("\n");

// Выводим содержимое считанного сектора в файл

	for(i=0; i<512; i++) fputc(buffer[i],sect);
	fclose(sect);

// Выключаем мотор

	outp(0x3F2, 0xC);
}


// Вывод байта в контроллер дисковода

void fdc_out(unsigned char parm) {

	_asm {
		mov   dx,3F4h     // Порт основного состояния
loop_fdc_out:

		in    al,dx
		test  al,80h      // Проверяем готовность
		jz loop_fdc_out   //   контроллера

		inc   dx          // Выводим байт в порт данных
		mov   al, parm    //   контроллера
		out   dx, al
	}
}

// Ввод байта из порта данных контроллера дисковода

int fdc_inp(void) {

	_asm {
		mov   dx,3F4h     // Порт основного состояния
loop_fdc_inp:
		in    al,dx
		test  al,80h      // Проверяем готовность
		jz loop_fdc_inp   //   контроллера

		inc   dx          // Введенный байт записываем
		in    al, dx      // в регистр AX
	}
}

// Ожидание прерывания от контроллера

void int_wait(void) {

// Разрешаем прерывания

	_enable();
	_asm {
		mov   ax,40h         // После прихода прерывания
		mov   es,ax          // программа обработки прерывания
		mov   bx,3Eh         // устанавливает в 1 старший бит
wait_loop:                 // байта в области данных BIOS
		mov   dl,es:[bx]     // по адресу 0040:003E.
		test  dl,80h         // Мы ждем, когда этот бит будет
		jz    wait_loop      // установлен в 1, а затем
									// сбрасываем его.
		and   dl,01111111b
		mov   es:[bx],dl
	}
}

// Инициализация канала прямого доступа к памяти

void dma_init(char *buf) {

	unsigned long f_adr;
	unsigned sg, of;

// Вычисляем 24-разрядный адрес буфера для данных

	f_adr = ((unsigned long)_psp << 4)
				+ (((unsigned long)buf) & 0xffff);

// Расщепляем адрес на номер страницы
// и смещение

	sg = (f_adr >> 16) & 0xff;
	of = f_adr & 0xffff;

// На время программирования контроллера прямого
// доступа запрещаем прерывания

	_disable();

	_asm {
		mov   al,46h   // Команда чтения данных от
							// контроллера НГМД.

		out   12,al    // Сброс триггера-указателя байта
							// для работы с 16-разрядными портами.
							// Следующий байт, выводимый в 16-разрядный
							// порт будет интерпретироваться
							// как младший.

		out   11,al    // Установка режима контроллера ПДП

		mov   ax,of    // Смещение буфера, младший байт
		out   4,al
		mov   al,ah    // Смещение буфера, старший байт
		out   4,al

		mov   ax,sg    // Номер страницы
		out   81h,al

		mov   ax,511   // Длина передаваемых данных
		out   5,al
		mov   al,ah
		out   5,al

		mov   al,2     // Разблокировка канала 2 контроллера ПДП
		out   10,al
	}

// Инициализация контроллера закончена,
// разрешаем прерывания.

	_enable();
}
