.286P
;----------------------------------------------
; только для отладки
;----------------------------------------------
sound   macro
        in al,61h
        or al,3
        out 61h,al
        endm
nosound macro
local z1
        mov cx,0000h
z1:     loop z1
        in al,61h
        and al,0FCh
        out 61h,al
        endm
toggle macro
local z1
        mov cx,0000h
z1:     loop z1
        in al,61h
        xor al,3
        out 61h,al
        endm
;--------------------------------------------

BIOS_DATA_SEG     SEGMENT   USE16  AT  0040h
                  ORG 0067h
 io_rom_init       Dw       ?
 io_rom_seg        Dw       ?
BIOS_DATA_SEG     ENDS

CSEG          SEGMENT   USE16   PARA PUBLIC 'CODE'
              ASSUME    CS:CSEG
              ORG       100h
Start:        jmp       Main

inta00             Equ 020h
inta01             Equ 021h            ;контроллер 8259 #1
intb00             Equ 0A0h
intb01             Equ 0A1h            ;контроллер 8259 #2
status_port        Equ 064h            ;порт состояния 8042
key_port_a         Equ 060h
key_port_b         Equ 061h
cmos_port          Equ 070h
Code_Seg_Access    Equ 10011010b      ;байт права доступа для сегмента кода
Data_Seg_Access    Equ 10010011b      ;байт права доступа для сегмента данных
Stack_Seg_Access   Equ 10010110b      ;байт права доступа для сегмента стека
Task_Seg_State     Equ 10000001b      ;байт прав доступа для свободного
                                      ;сегмента состояния задачи 286
Task_Gate          Equ 10000101b      ;шлюз задачи
Trap_Gate          Equ 10000111b      ;шлюз спец. прерывания
Interrupt_Gate     Equ 10000110b      ;шлюз прерывания
shut_cmd           Equ 0FEh           ;команда 8042 отключения компьютера

FILLDESCR         MACRO     Seg_Addr,Offset_Addr,Descr
                   mov       dx,Seg_Addr
                   mov       cx,offset Offset_Addr
                   call      _Form_24bit_Address
                   mov       &descr.base_lo_word,dx
                   mov       &descr.base_hi_byte,cl
                  ENDM

JUMPFAR            MACRO    Jump_Offset,Jump_Segment
                    Db       0eah
                    Dw       offset Jump_Offset
                    Dw       Jump_Segment
                   ENDM

EXCEPT_PROCI       MACRO     Ex_Number
local              l10
                   pusha
                   push      ds
                   mov       ax,system_data
                   mov       ds,ax
                   mov       cx,exc_mess_len
                   mov       si,offset exc_mess
                   mov       bh,14h
                   mov       bl,17h
                   mov       al,80
                   mul       bh
                   xor       bh,bh
                   add       ax,bx
                   shl       ax,1
                   mov       di,ax
                   mov       ah,0fh
                   push     es
                   push     video_desc
                   pop      es
l10:               lodsb
                   stosw
                   loop      l10
                   mov       al,Ex_Number
                   stosw
                   pop       es ds
                   popa
                   pop ax
                   jmp main_2
                   ENDM

EXCEPT_PROCII       MACRO     Ex_Number
                    pop ax
                    EXCEPT_PROCI  Ex_Number
                    ENDM

DESCRIPTOR        STRUC
  seg_limit        Dw   0                     ; Длина сегмента
  base_lo_word     Dw   0                     ; Физ. адрес
  base_hi_byte     Db   0
  acces_rights     Db   0                     ; Байт прав доступа
                   Dw   0
DESCRIPTOR        ENDS

IDT_DESCRIPTOR    STRUC
  int_offset       Dw   0                     ; Точка входа
  int_Selector     Dw   0                     ; Селектор дескриптора в GDT
                   Db   0
  int_ss           Db   0                     ; Вид обработчика прерывания
                   Dw   0
IDT_DESCRIPTOR    ENDS

Tss_Stencil286    STRUC                       ; Шаблон сегмента TSS
   LINK            Dw   0
    SP0            Dw   0
    SS0            Dw   0
    SP1            Dw   0
    SS1            Dw   0
    SP2            Dw   0
    SS2            Dw   0
    IP_            Dw   0
  FLAGS            Dw   0
    AX_            Dw   0
    CX_            Dw   0
    DX_            Dw   0
    BX_            Dw   0
    SP_            Dw   0
    BP_            Dw   0
    SI_            Dw   0
    DI_            Dw   0
    ES_            Dw   0
    CS_            Dw   0
    SS_            Dw   0
    DS_            Dw   0
    LDTR_          Dw   0
Tss_Stencil286    ENDS

                  EVEN
; *************** Глобальная дескрипторная таблица ***************
Gdt      Label    Word
 Gdt0              Descriptor<>
 Gdt_Desc               Equ (($-gdt)/8)*8
 Gdt_Pointer       Descriptor <gdt_leng-1,,,data_seg_access,>
 System_Code            Equ (($-gdt)/8)*8
 Gdt2              Descriptor<0ffffh,,,code_seg_access,>
 System_Data            Equ (($-gdt)/8)*8
 Gdt3              Descriptor<cseg_leng,,,data_seg_access,>
 System_Stack           Equ (($-gdt)/8)*8
 Gdt4              Descriptor<0h,,,stack_seg_access,>
 Idt_Pointer       Descriptor<idt_leng-1,,,data_seg_access>
 Video_Desc             Equ (($-gdt)/8)*8
 GDT5              Descriptor<1000h,8000h,0bh,data_seg_access>

 Sheduler_Tss_Selector      Equ (($-gdt)/8)*8
 GDT6              Descriptor<43,,,Task_seg_state>
 Task1_Tss_Selector         Equ (($-gdt)/8)*8
 GDT7              Descriptor<43,,,Task_seg_state>
 Task2_Tss_Selector         Equ (($-gdt)/8)*8
 GDT8              Descriptor<43,,,Task_seg_state>


 Task_List_desc         Equ (($-gdt)/8)*8
 GDT12             Descriptor<Task_List_len,,,data_seg_access>

 PrintTask_desc        Equ (($-gdt)/8)*8+0
 GDT14             Descriptor<PrintTask_len,,,code_seg_access>

  Task1_data            Equ (($-gdt)/8)*8
  gdt18        Descriptor<cseg_leng,,,data_seg_access>
  Task1_stack           Equ (($-gdt)/8)*8
  gdt16        Descriptor<0h,,,stack_seg_access>

  Task2_data            Equ (($-gdt)/8)*8
  gdt19        Descriptor<DataS4Task2_len,,,data_seg_access>
  Task2_stack           Equ (($-gdt)/8)*8
  gdt17        Descriptor<0h,,,stack_seg_access>

 Gdt_Leng          Equ    $ - GDT

                 EVEN
;****************** Таблица дескрипторов прерываний *******************
Idt     Label   Word
 ex0          idt_Descriptor<offset ex0_proc,System_Code,0,trap_gate,0>
 ex1          idt_Descriptor<offset ex1_proc,System_Code,0,trap_gate,0>
 ex2          idt_Descriptor<offset ex2_proc,System_Code,0,interrupt_gate,0>
 ex3          idt_Descriptor<offset ex3_proc,System_Code,0,trap_gate,0>
 ex4          idt_Descriptor<offset ex4_proc,System_Code,0,trap_gate,0>
 ex5          idt_Descriptor<offset ex5_proc,System_Code,0,trap_gate,0>
 ex6          idt_Descriptor<offset ex6_proc,System_Code,0,trap_gate,0>
 ex7          idt_Descriptor<offset ex7_proc,System_Code,0,trap_gate,0>
 ex8          idt_Descriptor<offset ex8_proc,System_Code,0,interrupt_gate,0>
 ex9          idt_Descriptor<offset ex9_proc,System_Code,0,trap_gate,0>
 ex10         idt_Descriptor<offset ex10_proc,System_Code,0,trap_gate,0>
 ex11         idt_Descriptor<offset ex11_proc,System_Code,0,trap_gate,0>
 ex12         idt_Descriptor<offset ex12_proc,System_Code,0,trap_gate,0>
 ex13         idt_Descriptor<offset ex13_proc,System_Code,0,trap_gate,0>
 ex14         idt_Descriptor<offset ex14_proc,System_Code,0,trap_gate,0>
 ex15         idt_Descriptor<offset ex15_proc,System_Code,0,trap_gate,0>
              idt_Descriptor 16 dup(<>)

int32         Idt_Descriptor<0,Sheduler_Tss_Selector,0,Task_gate,0>
int33         Idt_Descriptor<offset keyboard,System_Code,0,interrupt_gate,0>
              Idt_Descriptor 6 dup(<offset dummy1,System_Code,0,interrupt_gate,0>)
              Idt_Descriptor 8 dup(<offset dummy2,System_Code,0,interrupt_gate,0>)
Idt_Leng     Equ    $ - IDT

i8259_1       Db   ?
i8259_2       Db   ?

Task_Counter  Dw   0 ;      включится задача 2

Gate_Failure  Db   "Address line A20 failed to Gate open$"

exc_mess      Db   'EXCEPTION :   '
exc_mess_len  Equ  12

 Sheduler_TSS  TSS_stencil286<,,,,,,,offset start_shed,0h,,,,,80h,,,,gdt_desc,system_code,system_stack,Task_List_desc,>
 Task1_TSS     TSS_stencil286<,,,,,,,,200h,,,,,80h,,,,Task1_data,PrintTask_desc,Task1_stack,Task1_data,>
 Task2_TSS     TSS_stencil286<,,,,,,,,200h,,,,,80h,,,,Task2_data,PrintTask_desc,Task2_stack,Task2_data,>

StackTask1    Dw   80h dup(0)
StackTask2    Dw   80h dup(0)

DataS4Task1       Label word
        X     Db   20
        Y     Db   5
        titl  Db   'Task1'
ascii_count   Dw   3030h
Datas4Task1_len   Equ $-DataS4Task1

DataS4Task2       Label word
              Db   50
              Db   5
              Db   'Task2'
              Dw   3030h
DataS4Task2_len   Equ $-DataS4Task2

Task_List    Label  Word
              Dw   0
              Dw   Task1_Tss_Selector
              Dw   0
              Dw   Task2_Tss_Selector
Task_List_len     Equ $-Task_List-1

MAIN         PROC
              Assume    ds:Cseg
              call      Init_Tab                 ; Инициализация GDT.
              call      _Set_Protected_Mode      ; Переход в защищенный
                                                 ;  режим.
              mov       dh,1fh                   ;
              call      Paint_Screen             ; Очистка экрана
              mov       ax,Task1_Tss_Selector    ; загpузка селектоpа
              ltr       ax                       ;   первой задачи в TR
              mov       ax,Task1_Data            ; Настройка регистров
              mov       ds,ax                    ;  DS и ES на область
              mov       es,ax                    ;  данных первой задачи
              mov       ax,Task1_Stack           ; Настройка на область
              mov       ss,ax                    ;  стека первой задачи
              mov       sp,80h
              sti
              jmp       PrintTask                ; Ближний переход на
                                                 ; CS:PrintTask (переклю-
                                                 ; чение на первую задачу)

;**************************** Планировщик *******************************
; Сюда произойдет передача управления при первом прерывании от системного
;  таймера.
SHEDULER     label      word
start_shed:
              mov       ax,task_list_desc
              mov       ds,ax
              push      es
              push      System_Data
              pop       es
              mov       bx,es:[task_counter]  ; Считаем номер активной на
                                              ;  момент прерывания задачи
              inc       bx                    ;
              cmp       bx,2                  ;
              jne        save_counter          ; Вычислим номер новой
              xor       bx,bx                 ;  активной задачи и
save_counter: mov       es:[task_counter],bx  ;  сохраним его
              shl       bx,2
              pop       es
              mov       di,[bx+2]             ; Сброс бита BUSY в байте
              add       di,5                  ; прав доступа дескриптора
              and       es:byte ptr[di],0fdh  ;   TSS новой задачи
              mov       al,20h                ; Выдача в PIC команды
              out       20h,al                ; завершения аппаратного
                                              ; прерывания
              call      Beep                  ; Короткий щелчок
              jmp       ds:Dword ptr [bx]     ; Переключение на следующую
                                              ;  задачу
;*** Сюда будет передаваться управление при прерывании от таймера ***
              jmp       short  start_shed

Main_2:       mov       al,Shut_Cmd           ; Команда отключения
              out       Status_Port,al        ; возврат в реальный режим
Main_1:       hlt                             ; Останов процессора
              jmp       short Main_1

;*** Сюда BIOS передаст управление после сброса процессора ***
Real:         mov       dx,cs
              mov       ds,dx
              mov       ss,dx
              call      _A20_Close
              mov       al,[i8259_1]
              out       inta01,al
              mov       al,[i8259_2]
              out       intb01,al
              sti
              int       20h                      ; Выход в DOS
MAIN         ENDP

ex0_proc :     Except_ProcI '0'
ex1_proc :     Except_ProcI '1'
ex2_proc :     Except_ProcI '2'
ex3_proc :     Except_ProcI '3'
ex4_proc :     Except_ProcI '4'
ex5_proc :     Except_ProcI '5'
ex6_proc :     Except_ProcI '6'
ex7_proc :     Except_ProcI '7'
ex8_proc :     Except_ProcII '8'
ex9_proc :     Except_ProcI '9'
ex10_proc:     Except_ProcII 'A'
ex11_proc:     Except_ProcII 'B'
ex12_proc:     Except_ProcII 'C'
ex13_proc:     Except_ProcII 'D'
ex14_proc:     Except_ProcI 'E'
ex15_proc:     Except_ProcI 'F'

DUMMY1       PROC
              push      ax
              mov       al,20h
              out       20H,al
              pop       ax
              iret
DUMMY1       ENDP

DUMMY2       PROC
              push      ax
              mov       al,20h
              out       20H,al
              out       0A0H,al
              pop       ax
              iret
DUMMY2       ENDP

KEYBOARD     PROC
              jmp       main_2
KEYBOARD     ENDP

BEEP         PROC
              push      ax bx cx
              in        al,key_port_b
              push      ax
              mov       cx,80
beep0:        push      cx
              and       al,11111100b
              out       key_port_b,al
              mov       cx,60
id_1:         loop      id_1
              or        al,00000011b
              out       key_port_b,al
              mov       cx,60
id_2:         loop      id_2
              pop       cx
              loop      beep0
              pop       ax
              out       key_port_b,al
              pop       cx bx ax
              ret
BEEP         ENDP

;----------------------------------------------------------------
; Процедура  оцищает  экран  в защищенном режиме
;  Вход : DH - атрибут цвета
;  Использует дескриптор с именем VIDEO_DESC , описывающий
;  текстовый видеобуфер
;----------------------------------------------------------------
PAINT_SCREEN PROC near
              pusha
              push      es
              mov       ax,video_desc
              mov       es,ax
              mov       cx,80*25
              xor       di,di
              mov       ah,dh
              mov       al,20h
              rep       stosw
              pop       es
              popa
              ret
PAINT_SCREEN ENDP

Ideal
;----------------------------------------------------------------------------
;Перепрограммирование контроллера прерываний
;----------------------------------------------------------------------------
PROC         _SET_IRQ
              mov       al,11h          ;icw1
              out       inta00,al
              jmp       short     $+2
              mov       al,32           ;icw2
              out       inta01,al
              jmp       short     $+2
              mov       al,4            ;icw3
              out       inta01,al
              jmp       short     $+2
              mov       al,1            ;icw4
              out       inta01,al
              jmp       short     $+2
              mov       al,0fch         ;maska
              out       inta01,al
              mov       al,11h          ;icw1
              out       intb00,al
              jmp       short     $+2
              mov       al,40           ;icw2
              out       intb01,al
              jmp       short     $+2
              mov       al,2            ;icw3
              out       intb01,al
              jmp       short     $+2
              mov       al,1            ;icw4
              out       intb01,al
              jmp       short     $+2
              mov       al,0ffh         ;maska
              out       intb01,al
              ret
ENDP

;----------------------------------------------------------------------------
;Вход:  DX       содержит номер сегмента
;       CX       содержит внутрисегментное смещение
;Выход: DX       содержит значение BASE_LO_WORD
;       CX       содержит значение BASE_HI_BYTE
;---------------------------------------------------------------------------
PROC         _FORM_24BIT_ADDRESS
              push      ax
              rol       dx,4
              mov       ax,dx
              and       dl,0f0h
              and       ax,0fh
              add       dx,cx
              mov       cx,ax
              adc       cl,ch
              pop       ax
              ret
ENDP

PROC         _A20_OPEN       Near
              mov       ah,0dfh
              call      _Gate_A20
              ret
ENDP

PROC         _A20_CLOSE      Near
              mov       ah,0ddh
              call      _Gate_A20
              ret
ENDP

;----------------------------------------------------------------------------
;Управление прохождением сигнала A20
; ВХОД: (AH)=0DDH   A20 всегда равен 0
;       (AH)=0DFh   адресный разряд A20 открыт
; ВЫХОД: (AL)=0     8042 принял команду
;        (AH)=2     сбой
;----------------------------------------------------------------------------
PROC         _GATE_A20       Near
              cli
              call    _Empty_8042
              jnz     ga_1
              mov     al,0d1h
              out     Status_Port,al
              call    _Empty_8042
              jnz     ga_1
              mov     al,ah
              out     Key_Port_a,al
              call    _Empty_8042
ga_1:         ret
ENDP

;----------------------------------------------------------------------------
;Ждать пока буфер 8042 не опустеет
;Вход: нет
;Выход:(AL)=0   буфер пуст
;      (AL)=2   не пуст
;----------------------------------------------------------------------------
PROC         _EMPTY_8042     Near
              push    cx
              sub     cx,cx
emp_1:        in      al,status_port
              and     al,00000010b
              loopnz  emp_1
              pop     cx
              ret
ENDP

Masm
_SET_PROTECTED_MODE   PROC near
              push      ax bx
              call      _A20_Open
              or        al,al
              jz        Spm_1
              mov       dx,offset gate_failure
              mov       ah,9
              int       21h
              int       20h
Spm_1:        in        al,inta01                  ; сохранение состояния
              mov       [i8259_1],al               ; контроллеров прерывания
              in        al,intb01
              mov       [i8259_2],al
              mov       bx,bios_data_seg
              mov       es,bx
              mov       [es:io_rom_seg],cs                ;адрес возврата в
              mov       [es:io_rom_init],offset real      ;реальный режим
              mov       al,08fh                           ;подготовка к
              out       cmos_port,al                      ;сбросу процессора
              jmp       short $+2
              mov       al,5
              out       cmos_port+1,al
              cli
              call      _Set_Irq
              lgdt      [Qword Ptr Gdt_Pointer]    ; загрузка GDTR
              lidt      [Qword Ptr Idt_Pointer]    ; загрузка IDTR
              mov       ax,01h
              lmsw      ax                      ; переход в защищенный режим
              jumpfar   Spm_2,System_Code      ; настройка CS на сегмент кода
Spm_2:        mov       ax,System_Stack        ; Инициализация сегментных
              mov       ss,ax                  ; регистров
              mov       ax,System_Data
              mov       ds,ax
              mov       es,ax
              pop       bx ax
              ret
_SET_PROTECTED_MODE    ENDP

INIT_TAB     PROC
              FillDescr CS, 0        , Gdt2
              FillDescr CS, 0        , Gdt3
              mov       dx,SS
              mov       cx,0h
              call      _Form_24bit_Address
              mov       gdt4.base_lo_word,dx
              mov       gdt4.base_hi_byte,cl
              FillDescr CS, Gdt         , Gdt_Pointer
              FillDescr CS, IDT         , IDT_POINTER
              FillDescr CS, SHEDULER_TSS, GDT6
              FillDescr CS, Task1_TSS   , GDT7
              FillDescr CS, Task2_TSS   , GDT8
              FillDescr CS, Task_List   , GDT12
              FillDescr CS, PrintTask   , GDT14
              FillDescr CS, StackTask1  , gdt16
              FillDescr CS, StackTask2  , gdt17
              FillDescr CS, DataS4Task1 , gdt18
              FillDescr CS, DataS4Task2 , gdt19
              ret
INIT_TAB     ENDP

PrintTask   Proc
Pc_begin:     xor       di,di
              mov       bh,byte ptr[di+1]     ;Y
              mov       bl,byte ptr[di]       ;X
              mov       si,2                 ;смещение для стpоки
              mov       cx,5
              mov       dh,00fh
              call      writeXY
Pc_1:         xor       di,di
              mov       bh,byte ptr[di+1]     ;Y
              mov       bl,byte ptr[di]       ;X
              add       bh,2
              inc       bl
              mov       cx,2
              mov       si,7
              call      writexy
              mov       cx,0fffh
              call      delay
              mov       cx,ds:[07]               ;ascii_count
              xor       cx,3030h
              inc       ch
              cmp       ch,0ah
              jb        m2
              xor       ch,ch
              inc       cl
              cmp       cl,0ah
              je        Pc_3
m2:           or        cx,3030h
              mov       ds:[7],cx
              jmp       pc_1
Pc_3:         mov       ds:[7],3030h
              jmp       Pc_Begin
PrintTask   Endp

;-------------------------------------------------------------------------
;  Запись строки в видеопамять
;  Вход : DS:SI - адрес строки
;            CX - длина строки
;            BH - строка
;            BL - колонка
;            DH - атрибут
;-------------------------------------------------------------------------
WRITEXY      PROC
              push      es
              push      video_desc
              pop       es
              xor       di,di
              mov       al,80
              mul       bh
              xor       bh,bh
              add       ax,bx
              shl       ax,1
              mov       di,ax
              mov       ah,dh
w_l:          lodsb
              stosw
              loop      w_l
              pop       es
              ret
WRITEXY      ENDP

DELAY        PROC
Del_1:        pusha
              popa
              loop      Del_1
              ret
DELAY        ENDP

PrintTask_len  Equ $-PrintTask

Cseg_Leng    Equ       $
Cseg         Ends
End          Start
