	IDEAL
	MODEL SMALL
	RADIX	16
	P286

	DATASEG

	include "tos.inc"

	PUBLIC	_beep

; Область памяти для инициализации IDTR

	idtr			idtr_struc <,,,0>

; Область памяти для инициализации GDTR

	gdt_ptr		dw  (8*15)-1  ; размер GDT, 15 элементов
	gdt_ptr2		dw  ?
	gdt_ptr4		dw  ?

; Область памяти для записи селектора задачи,
; на которую будет происходить переключение

	new_task		dw  00h
	new_select  dw  00h

; Область памяти для хранения регистров,
; используется для возврата в реальный режим

	real_ss		dw	?
	real_sp		dw	?
	real_es		dw	?

	protect_sel	dw	?

	init_tss		dw	?

CODESEG

	PUBLIC  _real_mode,_protected_mode,_jump_to_task
	PUBLIC  _load_task_register, _load_idtr, _enable_interrupt

; -------------------------------------------------------------------
; Процедура для переключения в защищённый режим.
; Прототип для вызова:
;  void protected_mode(unsigned long gdt_ptr, unsigned int gdt_size,
;		 unsigned int cseg, unsigned int dseg)
; -------------------------------------------------------------------

PROC _protected_mode NEAR
		push    bp
		mov     bp,sp

; Параметр gdt_ptr

		mov     ax,[bp+4] 		; мл. слово адреса GDT
		mov     dx,[bp+6]			; ст. слово адреса GDT

		mov     [gdt_ptr4], dx	; запоминаем адрес GDT
		mov     [gdt_ptr2], ax

; Параметр gdt_size

		mov     ax,[bp+8]			; получаем размер GDT
		mov     [gdt_ptr], ax	; и запоминаем его

; Параметры cseg и dseg

		mov     ax,[bp+10d]		; получаем селектор сегмента кода
		mov     dx,[bp+12d]		; получаем селектор сегмента данных
		mov     [cs:p_mode_select], ax	; запоминаем для команды
		mov	  [protect_sel], dx        ; перехода far jmp

; Подготовка к возврату в реальный режим

		push		ds					; готовим адрес возврата
		mov		ax,40h			; из защищённого режима
		mov		ds,ax
		mov		[WORD 67h],OFFSET shutdown_return
		mov		[WORD 69h],cs
		pop		ds

; Запрещаем и маскируем все прерывания

		cli
		in			al, INT_MASK_PORT
		and		al, 0ffh
		out		INT_MASK_PORT, al

; Записываем код возврата в CMOS-память

		mov		al,8f
		out		CMOS_PORT,al
		jmp		delay1
delay1:
		mov		al,5
		out		CMOS_PORT+1,al

		call	enable_a20		; открываем линию A20

		mov		[real_ss],ss	; запоминаем регистры SS и ES
		mov		[real_es],es

; Перепрограммируем контроллер прерываний
; для работы в защищённом режиме

		mov		dx,MASTER8259A
		mov		ah,20
		call		set_int_ctrlr
		mov		dx,SLAVE8259A
		mov		ah,28
		call		set_int_ctrlr

; Загружаем регистры IDTR и GDTR

		lidt		[FWORD idtr]
		lgdt    	[QWORD gdt_ptr]

		mov		ax, 0001h	; переключаем процессор
		lmsw		ax				; в защищённый режим

;		jmp	far flush
					db	0eah
					dw	OFFSET flush
p_mode_select 	dw	?

LABEL	flush	FAR

		mov  		dx, [protect_sel]
		mov     	ss, dx
		mov     	ds, dx
		mov     	es, dx

; Обнуляем содержимое регистра LDTR

		mov		ax, 0
		lldt		ax

		pop     bp
		ret
ENDP _protected_mode

; ----------------------------------------------------
; Возврат в реальный режим.
; Прототип для вызова
;   void real_mode();
; ----------------------------------------------------

PROC _real_mode   NEAR

; Сброс процессора

		cli
		mov		[real_sp], sp
		mov		al, SHUT_DOWN
		out		STATUS_PORT, al

rmode_wait:
		hlt
		jmp		rmode_wait

LABEL	shutdown_return FAR

; Вернулись в реальный режим

		mov		ax, DGROUP
		mov		ds, ax

assume	ds:DGROUP

		mov	ss,[real_ss]
		mov	sp,[real_sp]

		in	al, INT_MASK_PORT
		and	al, 0
		out	INT_MASK_PORT, al

		call	disable_a20

		mov	ax, DGROUP
		mov	ds, ax
		mov	ss, ax
		mov 	es, ax

		mov	ax,000dh
		out	CMOS_PORT,al
		sti

		ret
ENDP _real_mode

; -------------------------------------------------------
; Загрузка регистра TR.
; Прототип для вызова:
;   void load_task_register(unsigned int tss_selector);
; -------------------------------------------------------

PROC _load_task_register  NEAR
		push    bp
		mov     bp,sp
		ltr	  [bp+4] ; селектор для текущей задачи
		pop     bp
		ret
ENDP _load_task_register

; -------------------------------------------------------
; Переключение на задачу.
; Прототип для вызова:
;   void jump_to_task(unsigned int tss_selector);
; -------------------------------------------------------

PROC _jump_to_task   NEAR
		push    bp
		mov     bp,sp
		mov     ax,[bp+4]          ; получаем селектор
											; новой задачи
		mov     [new_select],ax    ; запоминаем его

		jmp     [DWORD new_task]   ; переключаемся на
											; новую задачу
		pop     bp
		ret
ENDP _jump_to_task

; ------------------------------
; Открываем линию A20
; ------------------------------

PROC	enable_a20	NEAR
	push	ax
	mov	al, A20_PORT
	out	STATUS_PORT, al
	mov	al, A20_ON
	out	KBD_PORT_A, al
	pop	ax
	ret
ENDP	enable_a20

; ------------------------------
; Закрываем линию A20
; ------------------------------

PROC	disable_a20	NEAR
	push	ax
	mov	al, A20_PORT
	out	STATUS_PORT, al
	mov	al ,A20_OFF
	out	KBD_PORT_A, al
	pop	ax
	ret
ENDP	disable_a20

; -----------------------------------------------------------
; Готовим структуру для загрузки регистра IDTR
; Прототип для вызова функции:
; void load_idtr(unsigned long idt_ptr, word idt_size);
; -----------------------------------------------------------

PROC _load_idtr NEAR
		push    bp

		mov     bp,sp
		mov     ax,[bp+4] ; мл. слово адреса IDT
		mov     dx,[bp+6] ; ст. слово адреса IDT
		mov	  bx, OFFSET idtr

; Запоминаем адрес IDTR в структуре

		mov     [(idtr_struc bx).idt_low], ax
		mov     [(idtr_struc bx).idt_hi], dl

; Получаем предел IDT и запоминаем его в структуре

		mov		ax, [bp+8]
		mov     [(idtr_struc bx).idt_len], ax

		pop     bp
		ret
ENDP _load_idtr

; ----------------------------------
; Установка контроллера прерываний
; ----------------------------------

PROC	set_int_ctrlr	NEAR

	mov	al, 11
	out	dx, al
	jmp	SHORT $+2
	mov	al, ah
	inc	dx
	out	dx, al
	jmp	SHORT $+2
	mov	al, 4
	out	dx, al
	jmp	SHORT $+2
	mov	al, 1
	out	dx, al
	jmp	SHORT $+2
	mov	al, 0ffh
	out	dx, al
	dec	dx
	ret
ENDP	set_int_ctrlr

; --------------------------
; Выдача звукового сигнала
; --------------------------

PROC	_beep	NEAR

	push	ax bx cx

	in	al,KBD_PORT_B
	push	ax
	mov	cx,80

beep0:

	push	cx
	and	al,11111100b
	out	KBD_PORT_B,al
	mov	cx,60

idle1:

	loop	idle1
	or	al,00000010b
	out	KBD_PORT_B,al
	mov	cx,60

idle2:

	loop	idle2
	pop	cx
	loop	beep0

	pop	ax
	out	KBD_PORT_B,al

	pop	cx bx ax
	ret

ENDP	_beep

; -------------------------------
; Задержка выполнения программы
; -------------------------------

PROC	_pause		NEAR

	push	cx
	mov	cx,10

ploop0:

	push	cx
	xor	cx,cx

ploop1:

	loop	ploop1
	pop	cx
	loop	ploop0

	pop	cx
	ret

ENDP	_pause

; -----------------------
; Размаскирование прерываний
; -----------------------

PROC	_enable_interrupt NEAR

		in			al, INT_MASK_PORT
		and		al, 0fch
		out		INT_MASK_PORT, al

		sti
		ret
ENDP	_enable_interrupt

	end