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

// --------------------------------
// Определения вызываемых функций
// --------------------------------

void 		Init_And_Protected_Mode_Entry(void);

void 		protected_mode(unsigned long gdt_ptr, unsigned int gdt_size,
				word cseg, word dseg);

word 		load_task_register(word tss_selector);
void 		real_mode(void);
void 		jump_to_task(word tss_selector);
void 		load_idtr(unsigned long idt_ptr, word idt_size);
void 		Keyb_int(void);
void		Timer_int(void);
void 		Int_30h_Entry(void);

extern 	word kb_getch(void);
void		enable_interrupt(void);

void 		task1(void);
void 		task2(void);
void		flipflop_task(void);
void  	keyb_task(void);

void 		init_tss(tss *t, word cs, word ds,
				unsigned char *sp, func_ptr ip);

void 		init_gdt_descriptor(descriptor *descr, unsigned long base,
				word limit, unsigned char type);

void  exception_0(void); //{ prg_abort(0); }
void  exception_1(void); //{ prg_abort(1); }
void  exception_2(void); //{ prg_abort(2); }
void  exception_3(void); //{ prg_abort(3); }
void  exception_4(void); //{ prg_abort(4); }
void  exception_5(void); //{ prg_abort(5); }
void  exception_6(void); //{ prg_abort(6); }
void  exception_7(void); //{ prg_abort(7); }
void  exception_8(void); //{ prg_abort(8); }
void  exception_9(void); //{ prg_abort(9); }
void  exception_A(void); //{ prg_abort(0xA); }
void  exception_B(void); //{ prg_abort(0xB); }
void  exception_C(void); //{ prg_abort(0xC); }
void  exception_D(void); //{ prg_abort(0xD); }
void  exception_E(void); //{ prg_abort(0xE); }
void  exception_F(void); //{ prg_abort(0xF); }
void  exception_10(void); //{ prg_abort(0x10); }
void  exception_11(void); //{ prg_abort(0x11); }
void  exception_12(void); //{ prg_abort(0x12); }
void  exception_13(void); //{ prg_abort(0x13); }
void  exception_14(void); //{ prg_abort(0x14); }
void  exception_15(void); //{ prg_abort(0x15); }
void  exception_16(void); //{ prg_abort(0x16); }
void  exception_17(void); //{ prg_abort(0x17); }
void  exception_18(void); //{ prg_abort(0x18); }
void  exception_19(void); //{ prg_abort(0x19); }
void  exception_1A(void); //{ prg_abort(0x1A); }
void  exception_1B(void); //{ prg_abort(0x1B); }
void  exception_1C(void); //{ prg_abort(0x1C); }
void  exception_1D(void); //{ prg_abort(0x1D); }
void  exception_1E(void); //{ prg_abort(0x1E); }
void  exception_1F(void); //{ prg_abort(0x1F); }

void iret0(void);
void iret1(void);

// --------------------------------------
// Глобальная таблица дескрипторов GDT
// --------------------------------------

descriptor 	gdt[11];

// --------------------------------------
// Дескрипторная таблица прерываний IDT
// --------------------------------------

gate		idt[] = {

// Обработчики исключений

	{ (word)&exception_0, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 0
	{ (word)&exception_1, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1
	{ (word)&exception_2, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 2
	{ (word)&exception_3, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 3
	{ (word)&exception_4, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 4
	{ (word)&exception_5, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 5
	{ (word)&exception_6, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 6
	{ (word)&exception_7, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 7
	{ (word)&exception_8, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 8
	{ (word)&exception_9, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 9
	{ (word)&exception_A, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // A
	{ (word)&exception_B, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // B
	{ (word)&exception_C, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // C
	{ (word)&exception_D, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // D
	{ (word)&exception_E, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // E
	{ (word)&exception_F, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // F
	{ (word)&exception_10, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 10
	{ (word)&exception_11, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 11
	{ (word)&exception_12, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 12
	{ (word)&exception_13, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 13
	{ (word)&exception_14, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 14
	{ (word)&exception_15, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 15
	{ (word)&exception_16, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 16
	{ (word)&exception_17, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 17
	{ (word)&exception_18, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 18
	{ (word)&exception_19, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 19
	{ (word)&exception_1A, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1A
	{ (word)&exception_1B, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1B
	{ (word)&exception_1C, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1C
	{ (word)&exception_1D, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1D
	{ (word)&exception_1E, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1E
	{ (word)&exception_1F, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1F

// Обработчик прерываний таймера

	{ (word)&Timer_int, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 20

//	{ (word)&Keyb_int, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 21

// Вентиль задачи, запускающейся по прерыванию от клавиатуры

	{ 0, KEYB_TASK_SELECTOR, 0, TYPE_TASK_GATE, 0 }, // 21

// Заглушки для остальных аппаратных прерываний

	{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 22
	{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 23
	{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 24
	{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 25
	{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 26
	{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 27

	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 28
	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 29
	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2A
	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2B
	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2C
	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2D
	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2E
	{ (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2F

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

	{ (word)&Int_30h_Entry, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 },  // 30

// Вентиль задачи FLIP_TASK

	{ 0, FLIP_TASK_SELECTOR, 0, TYPE_TASK_GATE, 0 } // 31

};

// -------------------------------------------
// Сегменты TSS для различных задач
// -------------------------------------------

tss main_tss;			// TSS главной задачи
tss task_1_tss;		// TSS задачи TASK_1
tss task_2_tss;      // TSS задачи TASK_2
tss keyb_task_tss;   // TSS задач обслуживания
tss keyb_tss;			//		клавиатуры
tss flipflop_tss;		// TSS задачи FLIP_TASK

// -------------------------------------------
// Стеки для задач
// -------------------------------------------

unsigned char task_1_stack[1024];
unsigned char task_2_stack[1024];
unsigned char keyb_task_stack[1024];
unsigned char keyb_stack[1024];
unsigned char flipflop_stack[1024];

word y=0;	// номер текущей строки для вывода на экран



// -------------------------------------------
// Начало программы
// -------------------------------------------

void main(void) {

// Стираем экран

	textcolor(BLACK); textbackground(LIGHTGRAY); clrscr();

// Входим в защищённый режим

	Init_And_Protected_Mode_Entry();

// Выводим сообщение

	vi_hello_msg();
	y=3;
	vi_print(0, y++,
		 " Установлен защищённый режим в главной задаче", 0x7f);

// Загружаем регистр TR селектором главной задачи
// т.е. задачи main()

	load_task_register(MAIN_TASK_SELECTOR);

// Переключаемся на задачу TASK_1

	jump_to_task(TASK_1_SELECTOR);

// После возврата в главную задачу выдаём сообщение

	vi_print(0, y++ ," Вернулись в главную задачу", 0x7f);
	y++;

// Запускаем планировщик задач

	vi_print(0, y++ ," Запущен планировщик задач", 0x70);
	enable_interrupt(); // разрешаем прерывание таймера

// Ожидаем установки семафора с номером 0. После того,
// как этот семафор окажется установлен, возвращаемся
// в реальный режим.

// Семафор 0 устанавливается задачей, обрабатывающей ввод с
// клавиатуры, которая работает независимо от
// главной задаче.

	vi_print(0, y++ ," Для возврата в реальный режим нажмите ESC", 0x70);

	sem_clear(0); // сброс семафора 0
	sem_wait(0);  // ожидание установки семафора 0

// Возврат в реальный режим, стирание экрана и
// передача управления MS-DOS

	real_mode();
	textcolor(WHITE); textbackground(BLACK); clrscr();
}

// -----------------------------------
// Функция инициализации сегмента TSS
// -----------------------------------

void init_tss(tss *t, word cs, word ds,
	unsigned char *sp, func_ptr ip) {

	t->cs = cs;          // селектор сегмента кода
	t->ds = ds;          // поля ds, es, ss устанавливаем
	t->es = ds;          // на сегмент данных
	t->ss = ds;
	t->ip = (word)ip;    // указатель команд
	t->sp = (word)sp;    // смещение стека
	t->bp = (word)sp;
}

// -------------------------------------------------
// Функция инициализации дескриптора в таблице GDT
// -------------------------------------------------

void init_gdt_descriptor(descriptor *descr,
		unsigned long base, word limit,
		unsigned char type) {

// Младшее слово базового адреса
	descr->base_lo  = (word)base;

// Старший байт базового адреса
	descr->base_hi  = (unsigned char)(base >> 16);

// Поле доступа дескриптора
	descr->type_dpl = type;

// Предел
	descr->limit    = limit;

// Зарезервированное поле, должно быть
// сброшено в 0
	descr->reserved = 0;
}

// -----------------------------------------------
// Инициализация всех таблиц и вход
// в защищённый режим
// -----------------------------------------------

void Init_And_Protected_Mode_Entry(void) {

	union REGS r;

// Инициализируем таблицу GDT, элементы с 1 по 5

	init_gdt_descriptor(&gdt[1], MK_LIN_ADDR(_CS, 0),
		0xffffL, TYPE_CODE_DESCR | SEG_PRESENT_BIT | SEG_READABLE);

	init_gdt_descriptor(&gdt[2], MK_LIN_ADDR(_DS, 0),
		0xffffL, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);

	init_gdt_descriptor(&gdt[3],
		MK_LIN_ADDR(_DS, &task_1_tss),
		(unsigned long)TSS_SIZE-1, TYPE_TSS_DESCR | SEG_PRESENT_BIT);

	init_gdt_descriptor(&gdt[4],
		MK_LIN_ADDR(_DS, &task_2_tss),
		(unsigned long)TSS_SIZE-1, TYPE_TSS_DESCR | SEG_PRESENT_BIT);

	init_gdt_descriptor(&gdt[5],
		MK_LIN_ADDR(_DS, &main_tss),
		(unsigned long)TSS_SIZE-1, TYPE_TSS_DESCR | SEG_PRESENT_BIT);


// Инициализируем TSS для задач TASK_1, TASK_2

	init_tss(&task_1_tss, CODE_SELECTOR, DATA_SELECTOR, task_1_stack+
				sizeof(task_1_stack), task1);

	init_tss(&task_2_tss, CODE_SELECTOR, DATA_SELECTOR, task_2_stack+
				sizeof(task_2_stack), task2);

// Инициализируем элемент 6 таблицы GDT -
// дескриптор для сегмента видеопамяти

// Определяем видеорежим
	r.h.ah=15;
	int86(0x10,&r,&r);

// Инициализация для монохромного режима

	if(r.h.al==MONO_MODE)
		init_gdt_descriptor(&gdt[6], MONO_VID_MEM,
			3999, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);

// Инициализация для цветного режима

	else if(r.h.al == BW_80_MODE || r.h.al == COLOR_80_MODE)
		init_gdt_descriptor(&gdt[6], COLOR_VID_MEM,
			3999, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);
	else {
		printf("\nИзвините, этот видеорежим недопустим.");
		exit(-1);
	}

// Инициализация элементов 7 и 8 таблицы GDT

	init_gdt_descriptor(&gdt[7],
		MK_LIN_ADDR(_DS, &idt),
		(unsigned long)IDT_SIZE-1,
		TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);

	init_gdt_descriptor(&gdt[8],
		MK_LIN_ADDR(_DS, &keyb_task_tss),
		(unsigned long)TSS_SIZE-1,
		TYPE_TSS_DESCR | SEG_PRESENT_BIT);

// Инициализация TSS для задачи KEYB_TASK

	init_tss(&keyb_task_tss, CODE_SELECTOR, DATA_SELECTOR,
		keyb_task_stack + sizeof(keyb_task_stack), keyb_task);

// Инициализация элемента 9 таблицы GDT

	init_gdt_descriptor(&gdt[9],
		MK_LIN_ADDR(_DS, &keyb_tss),
		(unsigned long)TSS_SIZE-1,
		TYPE_TSS_DESCR | SEG_PRESENT_BIT);

// Инициализация TSS для задачи KEYB обработки ввода с клавиатуры

	init_tss(&keyb_tss, CODE_SELECTOR, DATA_SELECTOR,
		keyb_stack + sizeof(keyb_stack), Keyb_int);


// Инициализация элемента 10 таблицы GDT

	init_gdt_descriptor(&gdt[10],
		MK_LIN_ADDR(_DS, &flipflop_tss),
		(unsigned long)TSS_SIZE-1,
		TYPE_TSS_DESCR | SEG_PRESENT_BIT);

// Инициализация TSS для задачи FLIP_TASK

	init_tss(&flipflop_tss, CODE_SELECTOR, DATA_SELECTOR,
		flipflop_stack + sizeof(flipflop_stack), flipflop_task);

// Загрузка регистра IDTR

	load_idtr(MK_LIN_ADDR(_DS, &idt), IDT_SIZE);

// Вход в защищённый режим

	protected_mode(MK_LIN_ADDR(_DS, &gdt), sizeof(gdt),
			CODE_SELECTOR, DATA_SELECTOR);
}