;      Текст программы MULTASK иллюстрирующей возможности
;          многозадачности в процессорах 80286 и 80386
;    Данная программа  демонстрирует  одновременное   выполнение
;трех простых задач в защищенном режиме. Для ее работы необходи-
;мо наличие процессора i80386 или выше.
;    Рассмотрим работу программы по шагам.
;    - Инициализация GDT и трех LDT (каждая  задача  имеет  свою
;локальную  таблицу).  На  этом этапе в таблицы заносятся адреса
;используемых сегментов (вся остальная информация  занесена  при
;трансляции).
;    - Сохраняются состояния  контроллеров  прерываний  и  адрес
;возврата  после  останова  процессора (в программе используется
;способ возврата в реальный режим с  использованием  аппаратного
;сброса процессора;  BIOS передает управление по адресу содержа-
;щемуся в ячейках 0040:0067h и восстанавливает  состояние  конт-
;роллеров прерываний ).
;    - Перепрограммирование контроллеров прерываний.  Аппаратным
;прерываниям  IRQ0-IRQ7  соответствуют вектора INT20h-INT27h,  а
;IRQ8-IRQ15 - вектора INT30h-INT37h.
;    - Размаскируются прерывания от таймера и клавиатуры (IRQ0 и
;IRQ1).  Обработчик прерывания таймера - планировщик задач  (см.
;ниже).  Обработчик прерывания клавиатуры передает управление на
;точку выхода из защищенного режима, т.е. при нажатии любой кла-
;виши программа завершает работу и корректно выходит в DOS.
;    - Переход в защищенный режим и настройка сегментных регист-
;ров.
;    - В LDTR загружается селектор LDT первой задачи, а в TR -
;селектор TSS первой задачи.
;    - Передача управления процедуре PUBLIC_CODE.  Она  является
;общей для первой и второй задач (т.е. эти задачи выполняют один
;и тот же код,  но имеют разные сегменты данных). Таким образом,
;мы  запустили  Задачу,  и теперь при передаче управления другой
;задаче ее состояние полностью сохранится в сегменте TSS.
;    - При  возникновении прерывания таймера управление получает
;планировщик задач (он описан в IDT как шлюз задачи).  Пданиров-
;щик  находит  в  списке задач адрес селектора TSS для следующей
;задачи,  очищает ее бит Занятости и,  адресуя селектор TSS, пе-
;реключается на новую задачу.  Этот процесс повторяется примерно
;18 раз в секунду.  О частоте переключения задач можно судить по
;частоте щелчков (для наглядности планировщик перед переключени-
;ем задачи выдает короткий щелчок).

.386P
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       ;порт обращения к cmos памяти

Code_Seg_G         Equ 10000000b
Data_Seg_G         Equ 10000000b
Task_Seg_G         Equ 10000000b

Code_Seg_Access    Equ 10011010b  ;байт права доступа для
                                  ;сегмента кода
Data_Seg_Access    Equ 10010011b  ;байт права доступа для
                                  ;сегмента данных

Task_Seg_State     Equ 10001001b  ;байт прав доступа для
                                  ;свободного TSS 386
Ldt_Seg_Access     Equ 10000010b  ;байт прав доступа для LDT
Task_Gate          Equ 10000101b  ;шлюз задачи
Trap_Gate          Equ 10000111b  ;шлюз спец. прерывания
int_gate           Equ 10000110b  ;шлюз прерывания
shut_cmd           Equ 0FEh       ;команда 8042 отключения
                                  ;компьютера
;*********************************************************
;*     макрокоманда межсегментного перехода              *
;*     Jump_Offset  - смещение                           *
;*     Jump_Segment - сегмент                            *
;*********************************************************
JUMPFAR            MACRO    Jump_Offset,Jump_Segment
                    Db       0eah
                    Dw       (offset Jump_Offset)
                    Dw       Jump_Segment
                   ENDM
;********************************************************
;*         макрокоманда заполнения дескриптора          *
;*         Seg_Addr    - сегмент                        *
;*         Offset_Addr - смещение                       *
;*         Descr       - дескриптор                     *
;********************************************************
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

EXCEPT_PROC       MACRO     Ex_Number
                   pusha
                   push      Ex_Number
                   jmp       report_exc
                  ENDM
DESCRIPTOR        STRUC
  seg_limit        Dw   0              ; Длина сегмента
  base_lo_word     Dw   0              ; Физ. адрес
  base_hi_byte     Db   0
  acces_rights     Db   0              ; Байт прав доступа
                   Db   0
  base_top_byte    Db   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_Stencil386    STRUC          ; Шаблон сегмента TSS i386
   LINK            Dw   0        ; селектор возврата
                   Dw   0
   ESP0            Dd   0        ; значение ESP для стека
                                 ; уровня 0
    SS0            Dw   0        ; значение SS для стека
                   Dw   0        ; уровня 0
   ESP1            Dd   0        ; значение ESP для стека
                                 ; уровня 1
    SS1            Dw   0        ; значение SS для стека
                   Dw   0        ; уровня 1
   ESP2            Dd   0        ; значение ESP для стека
                                 ; уровня 2
    SS2            Dw   0        ; значение SS для стека
                   Dw   0        ; уровня 2
   CR3_            Dd   0        ; значение регистра CR3
   EIP_            Dw   0        ; значение регистра EIP
                   Dw   0
 EFLAGS            Dd   0        ; значение регистра EFLAGS
   EAX_            Dd   0        ; значение регистра EAX
   ECX_            Dd   0        ; значение регистра ECX
   EDX_            Dd   0        ; значение регистра EDX
   EBX_            Dd   0        ; значение регистра EBX
   ESP_            Dd   0        ; значение регистра ESP
   EBP_            Dd   0        ; значение регистра EBP
   ESI_            Dd   0        ; значение регистра ESI
   EDI_            Dd   0        ; значение регистра EDI
    ES_            Dw   0        ; значение регистра ES
                   Dw   0
    CS_            Dw   0        ; значение регистра CS
                   Dw   0
    SS_            Dw   0        ; значение регистра SS
                   Dw   0
    DS_            Dw   0        ; значение регистра DS
                   Dw   0
    FS_            Dw   0        ; значение регистра FS
                   Dw   0
    GS_            Dw   0        ; значение регистра GS
                   Dw   0
    LDTR_          Dw   0        ; значение регистра LDTR
                   Dw   0
                   Dw   0        ; бит 0 - бит ловушки Т
BaseMapIO          Dw   104      ; относительный адрес БКВВ
Tss_Stencil386    ENDS

EVEN
; *************** Глобальная дескрипторная таблица ************
 Gdt      Label    Word
 Gdt0              Descriptor<>
 ;*************************************************************
 ;*                описание GDT как сегмента данных
 ;*************************************************************
 Gdt_Desc               Equ (($-gdt)/8)*8+0000000000000000b
 Gdt_Pointer       Descriptor <gdt_leng-1,,,data_seg_access,data_seg_g,>
 ;*************************************************************
 ;*   дескриптор описывающий CSEG как сегмент кода
 ;*************************************************************
 System_Code            Equ (($-gdt)/8)*8+0000000000000000b
 Gdt2              Descriptor<cseg_leng,,,code_seg_access,code_seg_g,>
 ;*************************************************************
 ;*   дескриптор описывающий CSEG как сегмент данных
 ;*************************************************************
 System_Data            Equ (($-gdt)/8)*8+0000000000000000b
 Gdt3              Descriptor<cseg_leng,,,data_seg_access,data_seg_g,>
 ;*************************************************************
 ;*  дескриптор описывающий CSEG как сегмент данных
 ;*************************************************************
 System_Stack           Equ (($-gdt)/8)*8+0000000000000000b
 Gdt4              Descriptor<0fffeh,,,data_seg_access,data_seg_g,>
 ;*************************************************************
 ;*  дескриптор описывающий IDT  как сегмент данных
 ;*************************************************************
 Idt_Pointer       Descriptor<idt_leng-1,,,data_seg_access>
 ;*************************************************************
 ;*  дескриптор описывающий видеопамять  как сегмент данных
 ;*************************************************************
 Video_Desc             Equ (($-gdt)/8)*8+0000000000000000b
 GDT5              Descriptor<1000h,8000h,0bh,data_seg_access,data_seg_g,>
 ;*************************************************************
 ;*     cелектор дескриптора TSS для планировщика
 ;*************************************************************
 Sheduler_Tss_Selector  Equ (($-gdt)/8)*8+0000000000000000b
 GDT6              Descriptor<103,,,Task_seg_state,task_seg_g,>
 ;*************************************************************
 ;*    cелектор дескриптора TSS для первой задачи
 ;*************************************************************
 Task1_Tss_Selector     Equ (($-gdt)/8)*8+0000000000000000b
 GDT7              Descriptor<103,,,Task_seg_state,task_seg_g,>
 ;*************************************************************
 ;*   cелектор дескриптора TSS для второй задачи
 ;*************************************************************
 Task2_Tss_Selector     Equ (($-gdt)/8)*8+0000000000000000b
 GDT8              Descriptor<103,,,Task_seg_state,task_seg_g,>

 ;*************************************************************
 ;*   cелектор дескриптора TSS для третьей задачи
 ;*************************************************************
 Task3_Tss_Selector     Equ (($-gdt)/8)*8+0000000000000000b
 GDT100              Descriptor<103,,,Task_seg_state,task_seg_g,>
 ;*************************************************************
 ;*  cелектор дескриптора LDT для первой задачи
 ;*************************************************************
 Task1_Ldt_Selector     Equ (($-gdt)/8)*8+0000000000000000b
 GDT10             Descriptor<Task1_LDT_len,,,ldt_seg_access>
 ;*************************************************************
 ;*   cелектор дескриптора LDT для второй задачи
 ;*************************************************************
 Task2_Ldt_Selector     Equ (($-gdt)/8)*8+0000000000000000b
 GDT11             Descriptor<Task2_LDT_len,,,ldt_seg_access>
 ;*************************************************************
 ;*   cелектор дескриптора LDT для третьей задачи
 ;*************************************************************
 Task3_Ldt_Selector     Equ (($-gdt)/8)*8+0000000000000000b
 GDT110            Descriptor<Task3_LDT_len,,,ldt_seg_access>
 ;*************************************************************
 ;*   дескриптор данных для списка задач
 ;*************************************************************
 Task_List_desc         Equ (($-gdt)/8)*8+0000000000000000b
 GDT12             Descriptor<Task_List_len,,,data_seg_access>
 ;*************************************************************
 ;*  дескриптор сегмента кода  для задач 1 и 2
 ;*************************************************************
 PublicCode_desc        Equ (($-gdt)/8)*8+0000000000000000b
 GDT14             Descriptor<PublicCode_len,,,code_seg_access>
 ;*************************************************************
 ;*  дескриптор сегмента кода  для задачи 3
 ;*************************************************************
 Task3Code_desc        Equ (($-gdt)/8)*8+0000000000000000b
 GDT15             Descriptor<Task3code_len,,,code_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,int_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,int_gate,0>
 ex9  idt_Descriptor<offset ex9_proc,System_Code,0,int_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,int_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,int_gate,0>
Idt_Descriptor 6 dup(<offset dummy1,System_Code,0,int_gate,0>)

 ;**** ********************************************************
 ;*          прерывание аналог INT 10h
 ;*************************************************************
int40 Idt_Descriptor<offset int40_proc,System_Code,0,int_gate,0>
      Idt_Descriptor 7 dup (<>)

int54 Idt_Descriptor<offset fdc,System_Code,0,int_gate,0>
Idt_Descriptor 7 dup(<offset dummy2,System_Code,0,int_gate,0>)

Idt_Leng     Equ    $ - IDT            ; Длина IDT

i8259_1       Db   ?
i8259_2       Db   ?
Task_Counter  Dw   ?                   ; Номер текущей задачи
exc_mess      Db   'EXCEPTION : $'
exc_mess_len  Equ  12

 ;*************************************************************
 ;*                заполнение TSS для всех задач
 ;*************************************************************
 ;-------------------------------------------------------------
 ; IP=offset start_shed EFLAGS=00000000h SP=80h
 ; ES=gdt_desc          CS=system_code   SS=system_stack
 ; DS=Task_List_desc    FS=system_data
 ;-------------------------------------------------------------
 Sheduler_TSS  TSS_stencil386 <,,,,,,,,,,,,offset start_shed,,00000000h,,,,,80h,,,,gdt_desc,,system_code,,system_stack,,Task_List_desc,,System_Data,,,,>
;--------------------------------------------------------------
; EFLAGS=00000200h     SP=80h
; ES=TASK1_data        CS=PublicCode_desc  SS=Task1_stack
; DS=Task1_data        FS=system_data   LDTR=Task1_Ldt_selector
;--------------------------------------------------------------
 Task1_TSS     TSS_stencil386<,,,,,,,,,,,,,,00000200h,,,,,80h,,,,Task1_data,,PublicCode_desc,,Task1_stack,,Task1_data,,,,System_Data,,Task1_Ldt_Selector>
;--------------------------------------------------------------
; EFLAGS=00000200h  SP=80h
; ES=TASK2_data     CS=PublicCode_desc  SS=Task2_stack
; DS=Task2_data     FS=system_data      LDTR=Task2_Ldt_selektor
;--------------------------------------------------------------

 Task2_TSS     TSS_stencil386<,,,,,,,,,,,,,,00000200h,,,,,80h,,,,Task2_data,,PublicCode_desc,,Task2_stack,,Task2_data,,,,System_Data,,Task2_Ldt_Selector>
;--------------------------------------------------------------
; EFLAGS=00000200h  SP=80h
; ES=TASK3_data     CS=PublicCode_desc  SS=Task3_stack
; DS=Task3_data     FS=system_data      LDTR=Task3_Ldt_selektor
;--------------------------------------------------------------
 Task3_TSS     TSS_stencil386<,,,,,,,,,,,,,,00000200h,,,,,80h,,,,Task3_data,,Task3code_desc,,Task3_stack,,Task3_data,,,,System_Data,,Task3_Ldt_Selector>
 ;*************************************************************
 ;*  локальная дескрипторная таблица для Task1
 ;*************************************************************
Task1_LDT      Label    Word
  Task1_data          Equ (($-Task1_ldt)/8)*8+0000000000000100b
  t1LDT1        Descriptor<DataS4Task1_len,,,data_seg_access>
  Task1_stack         Equ (($-Task1_ldt)/8)*8+0000000000000100b
  t1LDT2        Descriptor<80h,,,data_seg_access>
Task1_LDT_len         Equ $-Task1_LDT
 ;*************************************************************
 ;*  локальная дескрипторная таблица для Task2
 ;*************************************************************
Task2_LDT      Label    Word
  Task2_data          Equ (($-Task2_ldt)/8)*8+0000000000000100b
  t2LDT1        Descriptor<DataS4Task2,,,data_seg_access>
  Task2_stack         Equ (($-Task2_ldt)/8)*8+0000000000000100b
  t2LDT2        Descriptor<80h,,,data_seg_access>
Task2_LDT_len         Equ $-Task2_LDT
 ;*************************************************************
 ;*  локальная дескрипторная таблица для Task3
 ;*************************************************************

Task3_LDT      Label    Word
  Task3_data          Equ (($-Task3_ldt)/8)*8+0000000000000100b
  t3LDT1        Descriptor<DataS4Task3,,,data_seg_access>
  Task3_stack         Equ (($-Task3_ldt)/8)*8+0000000000000100b
  t3LDT2        Descriptor<80h,,,data_seg_access>
Task3_LDT_len         Equ $-Task3_LDT

StackTask1    Dw   80h dup(0)     ; стек для Task1
StackTask2    Dw   80h dup(0)     ; стек для Task2
StackTask3    Dw   80h dup(0)     ; стек для Task3

 ;*************************************************************
 ;*                  сегмент данных для Task1
 ;*************************************************************
DataS4Task1       Label Byte
        X     Db   20          ;координаты для
        Y     Db   5           ;вывода на экран
        titl  Db   'Task1'     ;заголовок
ascii_count   Dw   3030h       ;счетчик
Datas4Task1_len   Equ $-DataS4Task1

 ;*************************************************************
 ;*          сегмент данных для Task2
 ;*************************************************************
DataS4Task2       Label Byte
              Db   50          ; координата X
              Db   5           ; координата Y
              Db   'Task2'     ; заголовок
              Dw   3030h       ; счетчик
DataS4Task2_len   Equ $-DataS4Task2

 ;*************************************************************
 ;*      сегмент данных для Task3
 ;*************************************************************
DataS4Task3       Label Byte
              Db   1         ; X
              Db   10        ; Y
              Db   'Демонстрация мультизадачности на примере '
              Db   'одновременного выполнения трех задач$'
DataS4Task3_len   Equ $-DataS4Task3

 ;*************************************************************
 ;*                  список переключаемых задач
 ;*************************************************************
Task_List    Label  Word
              Dw   0
              Dw   Task1_Tss_Selector
              Dw   0
              Dw   Task2_Tss_Selector
              Dw   0
              Dw   Task3_Tss_Selector
Task_List_len     Equ $-Task_List

MAIN PROC
      Assume  ds:Cseg
      call    Init_Tab               ; Инициализация GDT.
      call    _Set_Protected_Mode    ; Переход в защищенный
                                     ;  режим.
      mov     ax,Task1_Tss_Selector  ; Загрузка селектора TSS
      ltr     ax                     ;   первой задачи в TR
      mov     ax,Task1_Ldt_Selector  ; Загрузка селектора LDT
      lldt    ax                     ;   первой задачи в LDTR
      mov     ax,Task1_Data          ; Настройка регистров
      mov     ds,ax                  ;  DS и ES на область
      mov     es,ax                  ;  данных первой задачи
      mov     ax,Task1_Stack         ; Настройка на область
      mov     ss,ax                  ;  стека первой задачи
      mov     sp,60h
      jmp     PublicCode             ; Ближний переход на
                                     ; CS:PublicCode (переклю-
                                     ; чение на первую задачу)
;**************************** Планировщик ********************
; Сюда произойдет передача управления при первом прерывании от
; системного таймера.
SHEDULER     label byte
start_shed:
      xor    si,si
      mov    bx,fs:[task_counter]  ; Считаем номер активной на
                                   ;  момент прерывания задачи
      inc    bx
      cmp    bx,3
      jne    save_counter          ; Вычислим номер новой
      xor    bx,bx                 ;  активной задачи и
save_counter:
      mov    fs:[task_counter],bx  ;  сохраним его
      shl    bx,2
      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
Sheduler_len Equ   $ - Sheduler

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            ;закрытие шины А20
      mov       al,[i8259_1]          ;востановление
      out       inta01,al             ;состояния контроллеров
      mov       al,[i8259_2]          ;прерываний
      out       intb01,al
      sti
      int       20h                   ; Выход в DOS
MAIN    ENDP
 ;*************************************************************
 ;* обработчики особой ситуации просто выдают на экран ее номер
 ;*************************************************************
ex0_proc :     Except_Proc '0'
ex1_proc :     Except_Proc '1'
ex2_proc :     Except_Proc '2'
ex3_proc :     Except_Proc '3'
ex4_proc :     Except_Proc '4'
ex5_proc :     Except_Proc '5'
ex6_proc :     Except_Proc '6'
ex7_proc :     Except_Proc '7'
ex8_proc :     Except_Proc '8'
ex9_proc :     Except_Proc '9'
ex10_proc:     Except_Proc 'A'
ex11_proc:     Except_Proc 'B'
ex12_proc:     Except_Proc 'C'
ex13_proc:     Except_Proc 'D'
ex14_proc:     Except_Proc 'E'
ex15_proc:     Except_Proc 'F'
Report_Exc:
          pop   ax
          push  ds ax
          mov   ax,system_data
          mov   ds,ax
          mov   dh,0fh
          mov   cx,exc_mess_len
          mov   si,offset exc_mess
          mov   bh,14h
          mov   bl,17h
          call  writexy          ; выдача сообщения 'EXEPTION'
          pop   ax               ; извлекаем номер
          mov   bx,1423h         ; особой ситуации
          call  Print_Char       ; и вывод его на экран
          pop   ds
          popa
          iret
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
        mov al,110000b
        mov dx,3f2h
        out dx,al
KEYBOARD ENDP

fdc  proc
	  xor cx,cx
loop1:    call beep
          inc cx
          cmp cx,0ffh
          jnz loop1
          mov al,00b
          mov dx,3f2h
          out dx,al
          jmp main_2
fdc  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,00000010b
              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
Columns            Db   80d
Rows               Db   25d
Text_buf           Db   '           '
Cur_pos            Db   0
Cur_line           Db   0

 ;*************************************************************
 ;*                 вывод на экран
 ;*              AX=01 символа в AH
 ;*              AX=02 слова   в DX
 ;*************************************************************
INT40_PROC   PROC
              push      ds
              push      system_data
              pop       ds
              cmp       ah,01
              je        int_40_01
              cmp       ah,02
              je        int_40_02
              iret
int_40_01:    call      print_char
              jmp       Int_40_Exit
int_40_02:    mov       ax,dx
              call      print_word
              jmp       Int_40_Exit
int_40_Exit:  pop       ds
              iret
INT40_PROC   ENDP

;-------------------------------------------------------------
;  Вывод на экран символа из AL
;   (x,y) = (bl,bh)
;-------------------------------------------------------------
PRINT_CHAR   PROC
              push      di dx es
              push      video_desc
              pop       es
              mov       dx,bx
              call      set_addr
              stosb
              pop       es dx di
              ret
PRINT_CHAR   ENDP

;-------------------------------------------------------------
; Преобразование байта в символьный формат
;  ВХОД  : AL - входной байт
;  ВЫХОД : DX - выходное слово
;-------------------------------------------------------------
TABL          Db         '0123456789ABCDEF'
BYTE_TO_HEX  PROC
              push       cx bx
              mov        bx, offset TABL
              push       ax
              and        al,0fh
              xlat
              mov        dl,al
              pop        ax
              mov        cl,4
              shr        al,cl
              xlat
              mov        dh,al
              pop        bx cx
              ret
BYTE_TO_HEX  ENDP
;--------------------------------------------------------------
;  Вывод на экран содержимого AX
;   (x,y) = (bl,bh)
;--------------------------------------------------------------
PRINT_WORD   PROC
              assume gs:cseg
              push        ax bx dx ax
              mov         cl,8
              rol         ax,cl
              call        byte_to_hex
              mov         [text_buf],dh
              mov         [text_buf+1],dl
              pop         ax
              call        byte_to_hex
              mov         [text_buf+2],dh
              mov         [text_buf+3],dl
              mov         si, offset text_buf
              mov         dh,70h
              mov         cx,4
              call        writexy
              pop         dx bx ax
              ret
PRINT_WORD   ENDP
;--------------------------------------------------------------
; Процедура подсчитывает смещение от начала видеопамяти
; Вход : DH - строка
;        DL - колонка
; Выход: DI - смещение
; Used : Columns - количество столбцов на экране
;--------------------------------------------------------------
SET_ADDR     PROC
              push      dx ax
              xor       di,di
              mov       al,dh
              xor       ah,ah
              mul       columns
              xor       dh,dh
              add       ax,dx
              shl       ax,1
              mov       di,ax
              pop       ax dx
              ret
SET_ADDR     ENDP
;-------------------------------------------------------------
;Перепрограммирование контроллера прерываний
;  Вход : начальные номера прерываний :
;         BH - IRQ0
;         BL - IRQ8
;-------------------------------------------------------------
_SET_IRQ      PROC
              mov       ah,bh
              mov       dx,Inta00
              mov       al,11h
              out       dx,al
              jmp       short     $+2
              mov       al,ah
              inc       dx
              out       dx,al
              jmp       short     $+2
              mov       al,10000000b
              out       dx,al
              jmp       short     $+2
              mov       al,00011101b
              out       dx,al
              jmp       short     $+2
              mov       al,0ffh
              out       dx,al
              dec       dx

              mov       ah,bl
              mov       dx,Intb00
              mov       al,11h
              out       dx,al
              jmp       short     $+2
              mov       al,ah
              inc       dx
              out       dx,al
              jmp       short     $+2
              mov       al,00000111b
              out       dx,al
              jmp       short     $+2
              mov       al,00011001b
              out       dx,al
              jmp       short     $+2
              mov       al,0ffh
              out       dx,al
              dec       dx
              ret
ENDP
;-------------------------------------------------------------
;Вход:  DX       содержит номер сегмента
;       CX       содержит внутрисегментное смещение
;Выход: DX       содержит значение BASE_LO_WORD
;       CX       содержит значение BASE_HI_BYTE
;-------------------------------------------------------------
_FORM_24BIT_ADDRESS  PROC
              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
 ;*************************************************************
 ;*               открытие линии A20
 ;*************************************************************
_A20_OPEN     PROC
              mov       ah,0dfh
              call      _Gate_A20
              ret
ENDP
 ;*************************************************************
 ;*                закрытие линии A20
 ;*************************************************************
_A20_CLOSE     PROC
              mov       ah,0ddh
              call      _Gate_A20
              ret
ENDP

;-------------------------------------------------------------
;Управление прохождением сигнала A20
; ВХОД: (AH)=0DDH   A20 всегда равен 0
;       (AH)=0DFh   адресный разряд A20 открыт
; ВЫХОД: (AL)=0     8042 принял команду
;        (AH)=2     сбой
;-------------------------------------------------------------
_GATE_A20       PROC
              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   не пуст
;-------------------------------------------------------------
_EMPTY_8042     PROC
              push    cx
              sub     cx,cx
emp_1:        in      al,status_port
              and     al,00000010b
              loopnz  emp_1
              pop     cx
              ret
ENDP

 ;*************************************************************
 ;*  инициализация   защищенного   режима
 ;*************************************************************
_SET_PROTECTED_MODE   PROC
       push   ax bx
       call   _A20_Open
       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                             ;перпрограммирование
       mov    bh,32d                   ;конролеров
       mov    bl,48d                   ;прерываний
       call   _Set_Irq

       in     al,inta01
       and    al,11111100b            ; Разрешить прерывания
       out    inta01,al               ; таймера и клавиатуры

       in     al,intb01
       and    al,10111111b           
       out    intb01,al              

       lgdt   [Qword Ptr Gdt_Pointer]    ; загрузка GDTR
       lidt   [Fword Ptr Idt_Pointer]    ; загрузка IDTR
       mov    eax,cr0
       or     ax,1
       mov    cr0,eax               ;переход в защищенный режим
       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
       mov    gs,ax
       pop    bx ax
       sti
       ret
_SET_PROTECTED_MODE    ENDP
 ;*************************************************************
 ;* заполнение дескрипторов в GDT базовыми адресами
 ;*************************************************************
INIT_TAB     PROC
              FillDescr CS, 0           , Gdt2
              FillDescr CS, 0           , Gdt3
              FillDescr SS, 0           , Gdt4
              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, Task3_TSS   , GDT100
              FillDescr CS, Task1_LDT   , GDT10
              FillDescr CS, Task2_LDT   , GDT11
              FillDescr CS, Task3_LDT   , GDT110
              FillDescr CS, Task_List   , GDT12
              FillDescr CS, PublicCode  , GDT14
              FillDescr CS, Task3Code   , GDT15
              FillDescr CS, StackTask1  , t1LDT2
              FillDescr CS, StackTask2  , t2LDT2
              FillDescr CS, StackTask3  , t3LDT2
              FillDescr CS, DataS4Task1 , t1LDT1
              FillDescr CS, DataS4Task2 , t2LDT1
              FillDescr CS, DataS4Task3 , t3LDT1
              ret
INIT_TAB     ENDP
 ;*************************************************************
 ;*  процедура для задач Task1 и Task2
 ;*************************************************************
PublicCode   Proc
Pc_begin: xor   di,di
          mov   bh,byte ptr[di+1]     ;координата Y
          mov   bl,byte ptr[di]       ;координата X
          mov   si,02                 ;заголовок задачи titl
          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,07               ; смещение счетчика
                                    ;  ascii_count
          call  writexy             ;вывод содержимого счетчика
          mov   cx,6fffh
          call  delay               ; задержка
          mov   cx,ds:[07]          ; Ascii_count
          xor   cx,3030h
          inc   ch                  ; увеличение счетчика на 1
          cmp   ch,0ah
          jne   m2                  ;счетчик = 100?
          xor   ch,ch               ;да - начать работу сначала
          inc   cl                  ;нет- вывести новое
          cmp   cl,0ah              ;содержимое счетчика
          je    Pc_3
m2:       or    cx,3030h
m1:       mov   ds:[07],cx
          jmp   Pc_1
Pc_3:     jmp   Pc_Begin
PublicCode   Endp

;-------------------------------------------------------------
;  Запись строки в видеопамять
;  Вход : DS:SI - адрес строки
;            CX - длина строки
;            BH - строка
;            BL - колонка
;            DH - атрибут
;  Used : переменная COLUMNS : количество столбцов на экране
;-------------------------------------------------------------
WRITEXY      PROC
              push      di si
              push      es
              push      video_desc
              pop       es
              xor       di,di
              mov       dl,gs:[columns]
              mov       al,bh
              mul       dl
              xor       bh,bh
              add       ax,bx
              shl       ax,1
              mov       di,ax
              mov       ah,dh
w_l:          lodsb
              stosw
              loop      w_l
              pop       es
              pop       si di
              ret
WRITEXY      ENDP

 ;*************************************************************
 ;*             задержка               время в CX
 ;*************************************************************
DELAY        PROC
Del_1:        pusha
              popa
              loop      Del_1
              ret
DELAY        ENDP

PublicCode_len  Equ $-PublicCode

 ;*************************************************************
 ;*                     код задачи Task3
 ;*************************************************************
Task3code    Proc
              xor       di,di
              mov       cx,word ptr [di]     ; позиция
              mov       si,2                 ; смещение стороки
T3_1:         mov       bx,cx
              mov       al,byte ptr [si]
              inc       si
              cmp       al,'$'
              je        T3_Exit            ; вывод сообщения
              mov       ah,1               ; на экран с помощью
              int       40d                ; прерывания INT 40h
              call      T3_Delay           ; задержка
              inc       cl
              cmp       cl,80
              jne       t3_2
              xor       cl,cl
              inc       ch
T3_2:         jmp       Short     T3_1
              xor       dx,dx
T3_exit:      mov       ah,2
              mov       bx,0e25h
              int       40d
              inc       dx
              call      T3_Delay
              jmp       Short      T3_Exit
T3_delay:     push      cx
              mov       cx,09ffh
T3d_1:        pusha
              popa
              loop      t3d_1
              pop       cx
              ret
Task3code    Endp
Task3Code_len      Equ       $-Task3Code

Cseg_Leng    Equ       $
Cseg         Ends
End          Start


