#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(descriptor far *gdt_ptr);
void 	real_mode(void);
void 	init_gdt_descriptor(descriptor *descr, unsigned long base,
				word limit, unsigned char type);
void vi_print(unsigned int x, unsigned int y, char *s, char attr);
void vi_hello_msg(void);

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);

descriptor 	gdt[8];

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)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 20
	{ (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_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 y=0;

void main(void) {

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

	vi_hello_msg();

	y=3;
	vi_print(0, y++, " Вошли в защищённый режим", 0x7f);
	pause();
	vi_print(0, y++,
		" Для возврата в реальный режим нажмите любую клавишу", 0x7f);

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


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;
	descr->reserved = 0;
}

void Init_And_Protected_Mode_Entry(void) {

	union REGS r;
	word  crt_mode;
	extern word gv1_;

// Дескриптор, описывающий таблицу GDT

	init_gdt_descriptor(&gdt[1], MK_LIN_ADDR(_DS, &gdt),
		sizeof(gdt)-1,
		TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);

// Дескриптор, описывающий таблицу IDT

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

// Дескриптор сегмента данных

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

// Определяем текущий видеорежим

	r.h.ah=15;
	int86(0x10,&r,&r);
	crt_mode = r.h.al;

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

	if(crt_mode == MONO_MODE)
		init_gdt_descriptor(&gdt[4], MONO_VID_MEM,
			3999, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);

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

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

// Дескриптор для сегмента стека

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

// Дескриптор для сегмента кода

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

// Входим в защищённый режим
// В качестве параметра передаём адрес подготовленной
// таблицы GDT

	protected_mode(gdt);
}


void prg_abort(int err);

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 prg_abort(int err) {

	vi_print(1,y++,"---> Произошло исключение", 0xc);
	real_mode();
	gotoxy(1,24);
	cprintf("Исключение %X, нажмите любую клавишу", err);
	getch();

	textcolor(WHITE);	textbackground(BLACK); clrscr();
	exit(0);

}

void iret0(void) {
	asm {
		push	ax
		mov	al,EOI
		out	MASTER8259A,al
		pop	ax
		pop bp
		iret
	}
}

void iret1(void) {
	asm {
		push	ax
		mov	al,EOI
		out	MASTER8259A,al
		out	SLAVE8259A,al
		pop	ax
		pop bp
		iret
	}
}

void vi_putch(unsigned int x, unsigned int y ,char c, char attr) {

	register unsigned int offset;
	char far *vid_ptr;

	offset=(y*160) + (x*2);
	vid_ptr=MK_FP(VID_MEM_SELECTOR, offset);
	*vid_ptr++=c; *vid_ptr=attr;
}

void vi_print(unsigned int x, unsigned int y, char *s, char attr) {
	while(*s) vi_putch(x++, y, *s++, attr);
}

void vi_hello_msg(void) {

	vi_print(0, 0,
		" Protected mode monitor *TINY/OS*, "
		"v.1.11 for CPU 80286 ║ (C) Frolov A.V., 1992 ", 0x30);

}
