; для компиляции: tasm 4.1, tlink, rtm.exe. dpmi16bi.ovl
include  debug.mac	;подключение файла с макросом
	.386p		;разрешение привелленированных команд 386
;**********************************************************************
; структура для описания дескрипторов сегментов
    descr struc
	limit    dw     0	;Граница, биты 0..15
        base_1   dw     0	;база, биты 0..15
        base_m   db     0	;база, биты 16..23
        attr_1   db     0	;байт аттрибутов 1
        attr_2   db     0	;байт аттрибутов 2
        base_h   db     0	;база, биты 24..31 
        descr    ends
;структура для описания дескрипторов исключений
    trap  struc
        offs_1          dw      0	;смещение обработчика
        sel             dw      16	;селектор сегмента команд
        rsrv            db      0	;зарезервировано
        attr            db      8fh	;байт атрибутов (шлюз ловушки)
        offs_h          dw      0	;смещение обработчика
        trap            ends
;структура для описания формата стека уровня 0
  oldstk      struc
    oldip          dw        0,0	;eip возврата
    oldcs          dw        0,0	;cs возврата
    oldfl          dw        0,0	;eflags
    oldsp          dw        0,0	;esp возврата
    oldss          dw        0,0	;ss возврата
    oldes          dw        0,0	;сегментный регистр es
    oldds          dw        0,0	;сегментный регистр ds
    oldfs          dw        0,0	;сегментный регистр fs
    oldgs          dw        0,0	;cегментный регистр gs
    oldstk   ends
;********************************************************************
;               Сегмент данных управляющей задачи
;********************************************************************
    data segment use16	;тип 16-  работа только с 16-ми операндами, 
			;однако на регистры такого ограничения нет

    	 	;Таблица GDT может быть только одна родимая

     gdt_null descr    <>                     ;0 обязательный нулевой д-р
     gdt_data descr    <data_size-1,,,92h>    ;8 д-р сегмента данных 
     gdt_code descr    <code_size-1,,,9ah>    ;16 д-р сегмента кода
     gdt_stack descr   <stk386-1,0,0,92h>     ;24 д-р сегмента стека
     gdt_screen descr  <4095,8000h,0bh,0f2h>  ;32 на 3-уровне для задач 3 
     gdt_tss_0 descr   <103,0,0,89h>          ;40 д-р TSS 0 
     gdt_tss_1 descr   <103,0,0,89h>          ;48 д-р TSS 1
     gdt_tss_2 descr   <103,0,0,89h>	      ;56    д-р TSS 2
     gdt_ldt_1 descr   <ldt_1_size-1,,,82h>   ;64   д-р LDT 1
     gdt_ldt_2 descr   <ldt_2_size-1,,,82h>   ;72   д-р LDT 2
     gdt_tss_v86 descr <tss_86_size-1,0,0,89h>;80 д-р TSS V86
     gdt_00    descr   <03ffh,0,0,92h>        ; 88-селектор сегмента векторов
                                              ;     реального  режима
     gdt_code_86 descr     <0ffffh,,,92h>     ;96-д-р сегмента данных V86
     gdt_stk_86 descr     <0ffffh,,,92h>      ;104 д-р сегмента кода V86
     gdt_size=$-gdt_null		      ;размер GDT

; Таблица IDT - а вот их может быть сколько угодно, например в сложных задачах
; 		или в ОС с разделением доступа

     idt label word			  ;начало IDT
     trap      10       dup (<dummy_exc>) ;исключения с 0 по 9
     trap      <exc_0ah>		  ;10-исключение недопустимого TSS
     trap      <exc_0bh>		  ;11-отсутствие сегмента 
     trap      <exc_0ch>		  ;12-ошибка обращения к стеку 
     trap      <handler>		  ;13-Общая защита
     trap      <exc_0eh>		  ;14-страничное нарушение
     trap     18       dup (<dummy_exc>)  ;17 остальных+1-обработчик
     trap          <new_09h,,,8eh>	  ;прерываний от клавиатуры
     idt_size=$-idt			  ;размер IDT


     pdescr     dq      0 		  ;псевдодескриптор
     mes        db  5,5,5,' Вернулись в реальный режим' ,5,5,5,'$'
     tblhex     db  '0123456789abcdef'	;таблица преобразования 2->16
     string     db  '**** ****_**** ****_**** ****_**** ****'
     len=$-string	;строка для вывода диагностических сообщений
     mes0	db  5,5,5,' Работает управляющая задача уровня 0 ',5,5,5
     mes0_len=$-mes0	;понятно и так
     tss_0       dw     104  dup (0); TSS для задач, по идее минимальный
     tss_1       dw     104  dup (0); размер-104 байт, но в TSS хранятся
     tss_2       dw     104  dup (0); двойные слова, поэтому используется
     tss_v86     dw     104  dup (0); директива DW
     iomap	db	13000 dup (0); битовая карта ввода-вывода разрешения
     ioend	dw	0ffffh      ;или запрещения доступа к портам, т.е. к 
     tss_86_size=$-tss_v86  ; аппаратной части и для нашей программы не нужна
		       ;т.к. мы обрабатываем программные прерывания (INT 10h)
     home_sel    dw     home,16 ;селектор для выхода из обработчика 
; 13 исключения, в программе не используется, вместо него в случае аварийной
; ситуации происходит переключение во 2-ю задачу

	;Таблицы LDT-по одной для каждой прикладной задачи

; В один момент времени может быть загружена только одна LDT, так как 
; регистр LDTR только один, так обеспечивается разделение задач друг 
; от друга. Для каждой задачи свои сегменты данных, кода и стека.
; LDT-1
      ldt_1 label word
      ldt_data_1 descr  <data_1_size-1,,,0f2h>;4 (LDT) +3 (кольцо)
      ldt_code_1 descr  <code_1_size-1,,,0fah>;12 (LDT)+3 (кольцо)
      ldt_stk_1  descr  <255,,,0f2h,,>;20+3- если не работает- 96h
      ldt_1_size=$-ldt_1
; LDT-2
      ldt_2 label word
      ldt_data_2  descr  <data_2_size-1,,,0f2h>;4+3
      ldt_code_2  descr  <code_2_size-1,,,0fah>;12+3
      ldt_stk_2   descr  <255,,,0f2h,,>;20+3
      ldt_2_size=$-ldt_2
      data_size=$-gdt_null
      data ends
;********************************************************************* 
;               сегмента кода главной (управляющей) задачи 
;*********************************************************************
   text segment 'code' use16
             assume cs:text,ds:data ;инициализация сегментных регистров 
           begin  label word
; Обработчики исключений
;***************************************
    exc_0ah  proc; 
	     pop eax ; вытаскиваем код ошибки
	     pop eax ; вытаскиваем IP команды, вызвавшей исключение
             mov si,offset string+5; в si-смещение строки+5
             debug;         содержимое AX в строку string
             mov ax,0ah ;   в AX номер исключения
	     jmp home; выход из обработчика 
    exc_0ah endp; остальные  обработчики такие же 
;***************************************
        exc_0bh  proc
	     pop eax
	     pop eax
             mov si,offset string+5
             debug
             mov ax,0bh
             jmp home
    exc_0bh endp
;***************************************
        exc_0ch  proc
             pop eax
             pop eax ;????
             mov si,offset string+5
             debug
             mov ax,0ch
             jmp home
    exc_0ch endp		 
;***************************************
;место для ошибки
;**** Обработчик 13 исключения + отражатель прерываний V86 **********
handler proc
	push ax		; сохраняем AX
	mov ax,8	; настроим DS и FS на сегмент данных
	mov ds,ax	; управляющей задачи 
	mov fs,ax	; 
	mov ax,32	; ES-на видеобуфер
	mov es,ax	;
	pop ax		; восстановим AX
	cmp sp,stk0-40	; проверка, нет ли в стеке кода ошибки
	jne no_code	; нет, переход дальше
	add sp,4	; есть, снимаем его и переход дальше
no_code:
	pushad		; сохраним регистры
	mov bp,sp	; настроим BP на стек уровня 0 
	add bp,32	; скомпенсируем на pushad
	mov eax,dword ptr [bp].oldfl	; получим EFLAGS 
	bt eax,17	; установлен ли бит 17-флаг виртуальной машины
	jc vm_ok	; да, это было прерывание из виртуального режима
;	mov  ax,0faceh	; нет, это была авария , на выход
;	jmp dword ptr home_sel ; с выведением строки string 
jmp dword ptr cs:trs	; а можно и не на выход, а на задачу 2
vm_ok:
	mov ax,96 	; настроим сегмент кода задачи V86 как сегмента
	mov ds,ax 	; данных для того, чтобы узнать вектор INT
	mov si,[bp].oldip	;si=ip V86 для команды lodsb 
	cld		; установка флага направления 
	lodsb		; чтение цепочки по адресу ds:si
	cmp al,0cdh	; код команды INT ?
	je beginxxx	; да, пошли дальше
	jmp dword ptr cs:trs ; нет, идем в задачу 2

beginxxx:
	lodsb 		; читаем  аргумент INT
	mov [bp].oldip,si;в стек смещение следующей после INT команды 
	sub ah,ah 	; обнуление AH
	shl ax,2	; получаем адрес вектора умножением аргумента на 4
	mov bx,ax	; в BX его
	mov si,[bp].oldsp ; в si sp возврата задачи V86
	sub si,6	; резервируем 6 байт под адрес возврата
	mov [bp].oldsp,si ; сдвинем на эту величину sp возврата в стеке
			; уровня 0
	mov ax,104	; настроим сегмент стека задачи V86 как сегмента
	mov ds,ax	; данных 
	mov cx,[bp].oldip ; IP возврата в задачу V86
	mov dx,[bp].oldcs ; CS возврата в задачу V86
	mov di,[bp].oldfl ; EFLAGS возврата туда же
	mov [si],cx	; IP занесем в стек задачи V86
	mov [si+2],dx	; вектор возврата  в нее же
	mov [si+4],di	; флаги (IP, CS, EFLAGS)
	and di,0fdffh	; сбросим IF, имитируя INT
	mov ax,88	; селектор сегмента данных векторов реального 
	mov ds,ax	; режима
	mov ax,[bx]	; получим смещение из вектора
	mov cx,[bx+2]	; получим сегмент из вектора
	mov [bp].oldfl,di ; занесем полученный вектор 
	mov [bp].oldip,ax ; обработчика DOS
	mov [bp].oldcs,cx ; в стек уровня 0
	popad		; восстановим регистры
	db 66h		; префикс 32-х разрядного операнда
	iret		; "возврат" (переключение) в DOS
trs dw 0,56		; селектор для задачи 2
handler endp
;********************************************************************
;***************************************
       exc_0eh  proc
             pop eax
             pop eax ;????
             mov si,offset string+5
             debug
             mov ax,0eh
             jmp home
    exc_0eh endp
;***************************************
    dummy_exc  proc
             pop eax
             pop eax ;????
             mov si,offset string+5
             debug
             mov ax,1111h
             jmp home
    dummy_exc endp
;***************************************

;****  Обработчик прерываний от клавиатуры *******

    new_09h   proc
              push      ax	; сохраним используемые 
              push      dx	; регистры
              in        al,60h	; введем скан-код
              mov       dl,al	; сохраним его 
              in        al,61h	; получим содержимое порта В
              or        al,80h	; установкой старшего бита 
              out       61h,al	; и последующим сбросом его 
              and       al,7fh	; сообщим контроллеру клавиатуры
              out       61h,al	; о приеме скан-кода символа
              mov       al,20h	; сигал EOI для разблокирования уровня
              out       20h,al	; IRQ клавиатуры и нижележащих 
              cmp       dl,0bh	; клавиша "0" ? 
              je        tsk0	; да, на запуск задачи 0
              cmp       dl,2	; клавиша "1" ?	
              je        tsk1	; да, на запуск задачи 1
              cmp       dl,3	; клавиша "2" ?
              je        tsk2	; да, на запуск задачи 2
              cmp       dl,2fh	; клавиша "v" ?
              je        tsk86	; да, на запуск задачи V86
		cmp   dl,44h	; клавиша F10 ?
		je  exit	; да, на выход из программы
              jmp       out_1	; другая клавиша, на выход обратно в задачу
exit:
            mov   al,0feh 	;сбросим  
            out   64h,al	;процессор
            hlt			;задержка
          tsk0:  str    ax	; читаем TR
                 cmp    ax,40	; нет ли уже в TR селектора задачи 0
                 je     out_1	; да, это задача 0, обратно
                 pop    dx	; нет, какая то другая задача, восстановим 
                 pop    ax	;    регистры
                 jmp   dword   ptr cs:  t0_addr ; переключение
                 jmp   out_09h	; это запишется в переключаемую задачу 
          tsk1:  str   ax
                 cmp   ax,48
                 je    out_1      		 
                 pop   dx
                 pop   ax
		
                 jmp   dword  ptr  cs:  t1_addr
                 jmp   out_09h;
          tsk2:  str   ax
                 cmp   ax,56
                 je    out_1
                 pop   dx
                 pop   ax
                 jmp   dword  ptr cs:      t2_addr
                 jmp   out_09h
          tsk86:
		 str   ax
                 cmp   ax,80
                 je    out_1
                 pop   dx
                 pop   ax
                 jmp   dword  ptr cs:  t86_addr

          out_09h:
		   db  66h ; префикс 32-х разрядных операндов
                   iret	   ; выход из обработчика
           out_1: pop    dx ; какая-то другая клавиша, восстановим 
                  pop    ax ; регистры и на выход из обработчика
                  jmp    out_09h
          t0_addr   dw    0,40	;ячейки для переключения на задачи 
          t1_addr   dw    0,48
          t2_addr   dw    0,56
          t86_addr  dw    0,80
               new_09h  endp

; Главная процедура задачи 0, по сути точка входа в программу

    main proc

; ********************************************************************
; 	Подготовка к переключению в защищенный режим-вычисление 
; 		линейных адресов, загрузка таблиц, регистров
; ********************************************************************
           xor  eax,eax	; очистим EAX
           mov  ax,data	; в ax-реальный адрес сегмента данных
           mov  ds,ax	; пока мы в реальном режиме 
                        ;seg data
           shl  eax,4	; получим линейный адрес сдвиг влево на 4 
			; равносильно умножению на 16
           mov  ebp,eax	; сохраним в EBP для будущего
           mov  bx,offset gdt_data ; в вх адрес дескриптора
           mov  [bx].base_1,ax	; загрузим младшую часть базы
           rol  eax,16		; обмен старшей и младшей частей
           mov  [bx].base_m,al	; загрузим среднюю часть базы
                        ;seg command
; остальные аналогично
           xor   eax,eax
           mov   ax,cs
           shl   eax,4
           mov   bx,offset  gdt_code
           mov   [bx].base_1,ax
           rol   eax,16
           mov   [bx].base_m,al
                        ;seg stack
           xor    eax,eax
           mov    ax,ss
           shl    eax,4
           mov    bx,offset  gdt_stack
           mov    [bx].base_1,ax
           rol    eax,16
           mov    [bx].base_m,al
                          ;seg tss_0
           mov    eax,ebp; в EAX линейный адрес data
           add    ax,offset tss_0; сложим со смещением от начала data
           mov    bx,offset gdt_tss_0
           mov    [bx].base_1,ax
           rol    eax,16
           mov    [bx].base_m,al
                         ;seg tss_1
           mov  eax,ebp
           add  ax,offset  tss_1
           mov  bx,offset  gdt_tss_1
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
                     ; gdt tss2
           mov  eax,ebp
           add  ax,offset tss_2
           mov bx,offset  gdt_tss_2
           mov [bx].base_1,ax
           rol eax,16
           mov [bx].base_m,al
           ;  gdt_ldt_1
           mov  eax,ebp
           add  ax,offset  ldt_1
           mov  bx,offset  gdt_ldt_1
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
           ;  gdt_ldt_2
           mov  eax,ebp
           add  ax,offset  ldt_2
           mov  bx,offset  gdt_ldt_2
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
; также вычислим и занесем в LDT линейные адреса сегментов задач
           ; data_1 in ldt_1
           xor  eax,eax
           mov  ax,data_1
           shl  eax,4
           mov  bx,offset ldt_data_1
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
           ; text_1 in ldt_1
           xor  eax,eax
           mov  ax,text_1
           shl  eax,4
           mov  bx,offset ldt_code_1
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
             ;  stk in ldt_1
           xor  eax,eax
           mov  ax,stk_1
           shl  eax,4
           mov  bx,offset ldt_stk_1
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
           ; data text  stk in ldt_2
           ; data_2 in ldt_2
           xor  eax,eax
           mov  ax,data_2
           shl  eax,4
           mov  bx,offset ldt_data_2
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
           ; text_2 in ldt_2
           xor  eax,eax
           mov  ax,text_2
           shl  eax,4
           mov  bx,offset ldt_code_2
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
             ;  stk in ldt_2
           xor  eax,eax
           mov  ax,stk_2
           shl  eax,4
           mov  bx,offset ldt_stk_2
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al
           ;V86
           mov  eax,ebp
           add  ax,offset tss_v86
           mov bx,offset  gdt_tss_v86
           mov [bx].base_1,ax
           rol eax,16
           mov [bx].base_m,al
;*******************************************************
место для ошибки
; линейные адреса сегментов кода и стека задачи V86 
; вычисляются безотносительно к какому-либо сегменту
           xor  eax,eax 
           mov  ax,code_86
           shl  eax,4
           mov  bx,offset gdt_code_86
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al

           xor  eax,eax
           mov  ax,stk_86
           shl  eax,4
           mov  bx,offset gdt_stk_86
           mov  [bx].base_1,ax
           rol  eax,16
           mov  [bx].base_m,al

;*******************************************************


           ; подготовим псевдодескриптор 
           mov   dword ptr pdescr+2,ebp ; линейный адрес сегмента данных
				; равен началу GDT
           mov   word  ptr pdescr,gdt_size-1 ; граница GDT
           lgdt  pdescr ; и загрузим GDTR

           ;***init  task's fields

           mov       tss_1+4ch,15	;cs задачи 1: 12-LDT+3-уровень=15
           mov       tss_1+20h,offset task1	;точка входа в задачу 
           mov       tss_1+50h,23	;селектор сегмента стека
           mov       tss_1+38h,256	;sp-указатель вершины стека
           mov       tss_1+54h,07	;селектор сегмента данных
           mov       tss_1+48h,35	;видеобуфер

           sti				; установка флага IF-разрешение
           pushfd			;прерываний, чтобы задачи выполнялись
           pop eax			;при разрешенных прерываниях
           mov dword ptr tss_1+24h,eax  ; слово  флагов 
			; а можно было просто записать 3200h
           mov       tss_1+60h,64	; LDT задачи 
	   mov	     tss_1+4,256	;** ESP0 **!!!!самое уязвимое место!!!!!
	   mov	     tss_1+8,24		; SS0
           ; tss_2
           mov       tss_2+4ch,15;      cs
           mov       tss_2+20h,offset task2
           mov       tss_2+50h,23
           mov       tss_2+38h,256
           mov       tss_2+54h,07
           mov       tss_2+48h,35
           mov dword ptr tss_2+24h,eax
           mov       tss_2+60h,72
	   mov       tss_2+4,768; !!!!!!самое уязвимое место!!!!!
	   mov       tss_2+8,24
           ;tss_v86
; TSS V86 в части касаемой сегментых регистров, инициализируется
; по правилам реального режима
	   mov			ax,code_86 ; реальный адрес
	   mov	     tss_v86+4ch,ax  ; сегмента кода
           mov       tss_v86+20h,offset  task_v86 ; точка входа в задачу
           mov			ax,stk_86 ; аналогично
	   mov	     tss_v86+50h,ax
	   mov  		ax,stk86
           mov       tss_v86+38h,ax
           mov			ax,data_86
	   mov	     tss_v86+54h,ax
           mov       tss_v86+48h,0b800h
           mov dword ptr  tss_v86+24h,23200h; в слово флагов-
		; флаг VM, 3-е кольцо, флаг IF прерывания разрешены
           mov       tss_v86+8,24
	   mov  		ax,stk0
           mov       tss_v86+4,512;!!!!!!самое уязвимое место!!!!!!
       	   mov	     tss_v86+66h,68h;tss_86_size;104
	   mov     tss_0+60h,0

;******************* подготовка возврата из ЗР ************************
           cli

           mov  al,0fh ; выбираем байт состояния отключения
           out  70h,al ; порт  КМОП (CMOS) -микросхемы
           mov  al,05h ;5-сброс с восстановлением по адресу 		       
           out  71h,al ; 40:67 с перепрограммированием контроллера
           mov  ax,40h ; настроим es на область данных BIOS
           mov  es,ax  ; 
           mov  word ptr es:[67h],offset return ;смещение точки возврата
           mov  word ptr es:[69h],cs ; сегмент точки возврата

;******  перепрограммирование  контроллера прерываний *****************
           mov  dx,20h ; 
           mov  al,11h ; будет ски3 и ски4
           out  dx,al  ; в порт 20h ски1 (ICW1)
           inc  dx     ; работаем с портом 21h
           mov  al,20h ; ски2-базовый вектор 20h=32
           out  dx,al  ; вывод
           mov  al,4   ; ски3-ведомый контроллер подключен к уровню 2
           out  dx,al  ; вывод
           mov  al,1   ; ски4-тип микропроцессора 8086 (Pentium)
           out  dx,al  ; и требование EOI
           mov  al,0fdh ; маска-разрешаем только клавиатуре
           out  dx,al  ; вывод
           mov  dx,0a1h ; порт ведомого контроллера
           mov  al,0ffh ; запретим все прерывания
           out  dx,al  ; вывод

; Подготовим псевдодескриптор и загрузим IDTR
           mov word ptr pdescr,idt_size-1
           xor eax,eax
           mov ax,offset idt
           add eax,ebp
           mov dword ptr pdescr+2,eax
           lidt pdescr

; *************** Переход в защищенный режим ****************

           mov  eax,cr0	; прочитаем содержимое CR0
           or   eax,1	; установим бит PE 
           mov  cr0,eax	; обратно

; загрузим CS:IP для работы в ЗР
           db   0eah		; код команды far jmp
           dw   offset continue ; смещение IP точки продолжения уже в ЗР
           dw   16		; селектор сегмента ЗР
     continue:

           mov  ax,8  ; инициализируем сегментные регистры по правилам 
           mov  ds,ax ; ЗР:заносим селекторы соответствующих  
           mov  ax,24 ; дескрипторов сегментов
           mov  ss,ax
           mov  ax,32
           mov  es,ax
	   mov  sp,1024

           ;***
           mov  ax,40; загрузим TR
           ltr  ax   ; селектором задачи 0-теперь главная задача 
		     ;действительно ею стала
;*********************************************************************
; Основная процедура управляющей задачи- 
; выводит в бесконечном цикле строку с разными 
; атрибутами. разбирайтесь здесь сами, господа, вывод 
; напрямую через видеобуфер
sti
          d1:    mov    ah,57h
                 mov    cx,2
          d2:    push   cx
                 mov    di,1280
                 mov    cx,mes0_len
                 mov    si,offset mes0
                 cld
          d3:    lodsb
                 stosw
                 loop   d3
                 delay  30
                 pop    cx
                 mov    ah,67h
                 loop   d2
                 jmp    d1
;*********************************************************************         
;Вывод диагностической строки
home:    mov  si,offset  string
	 debug
	 mov	si,offset string
	 mov	cx,len
	 mov	ah,74h
	 mov	di,1740
  scr:   lodsb
	 stosw
	 loop  scr

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

       return:   
  		 mov  ax,data
                 mov  ds,ax
		 mov ax,0 ; а вот на эти 2 строчки ушел почти месяц
		 mov  gs,ax; процессор после сброса меняет gs!!!!!!
			;и если его не обнулить, то при втором запуске
			; нельзя переключиться на управляющую задачу
			; происходит исключение 10-недопустимый TSS
                 mov  ax,stk
                 mov  ss,ax
                 mov  sp,stk386;256
                 sti
 ;Размаскируем  прерывания в ведущем и ведомом контроллерах
                mov  al,0b8h
                out  21h,al
                mov  al,9dh
                out  0a1h,al
 ; С помощью прерывания DOS выведем диагностическую строку
                mov  ah,09h
                mov  dx,offset  mes
                int  21h
 ; Завершим задачу по правилам DOS
                mov  ax,4c00h
                int  21h
       main  endp
;*********************************************************************
;Конец главной процедуры главной задачи
;*********************************************************************
     code_size=$-begin
     text  ends

stk386=400h ; мнемонические обозначения для стеков
stk0=200h
;*********************************************************************
		; Сегмент стека главной задачи
;*********************************************************************

     stk segment stack 'stack'
     db   stk386   dup ('^');
     stk ends

;*********************************************************************
		;Cегмент данных задачи 1
;*********************************************************************

     data_1    segment  use16
     data_1_beg=$
     mes1        db     22,22,22,'Работает задача Task_1 уровня 3',22,22,22
     mes1_len=$-mes1
     data_1_size=$-data_1_beg
     data_1  ends

;*********************************************************************
		;Cегмент кода задачи 1
;*********************************************************************

     text_1   segment  'code'  use16
                assume cs:text_1;ds:data_1
;такая же процедура вывода мигающей строки, как и задачи 0, только 
; другие атрибуты
       task1    proc
           a1:  mov     ah,71h
                mov     cx,5
           a2:  push    cx
                mov     di,800
                mov     cx,mes1_len
                mov     si,offset  mes1
                cld
           a3:  lodsb
                stosw
                loop    a3
                delay   100
                pop     cx
                mov     ah,17h
                loop    a2
                jmp     a1
           task1 endp
       code_1_size=$-task1
       text_1  ends

;*********************************************************************
		;Cегмент стека задачи 1
;*********************************************************************

       stk_1  segment 'stack'
       db  256  dup ('^')
       stk_1 ends

;*********************************************************************
		;Cегмент данных задачи 2
;*********************************************************************

      data_2    segment  use16
     data_2_beg=$
     mes2        db     22,22,22,'Работает задача Task_2 уровня 3',22,22,22
     mes2_len=$-mes2
     data_2_size=$-data_2_beg
     data_2  ends

;*********************************************************************
		;Cегмент кода задачи 2
;*********************************************************************

     text_2   segment  'code'  use16
                assume cs:text_2;ds:data_2
;такая же процедура вывода мигающей строки, как и задачи 0, только 
; другие атрибуты
task2 proc
          b1:    mov    ah,34h
                 mov    cx,2
          b2:    push   cx
                 mov    di,1120
                 mov    cx,mes2_len
                 mov    si,offset mes2
                 cld
          b3:    lodsb
                 stosw
                 loop   b3
                 delay  30
                 pop    cx
                 mov    ah,43h
                 loop   b2
                 jmp    b1
          task2    endp
        code_2_size=$-task2
       text_2  ends

;*********************************************************************
		;Cегмент стека задачи 2
;*********************************************************************

       stk_2  segment  'stack'
       db    256  dup ('^')
       stk_2   ends

;*********************************************************************
		;Cегмент данных  задачи V86
;*********************************************************************

       data_86  segment   'abc'  use16
       mes_86   db      4,4,4,' Работает задача виртуального 8086',4,4,4
       mes_86_len=$-mes_86
	mes_00  db   3,3,3,'Такая вот фигня',3,3,3
	mes_00_len=$-mes_00
       data_86_size=$-mes_86
       data_86 ends

;*********************************************************************
		;Cегмент кода  задачи V86
;*********************************************************************

       code_86   segment  'abc' use16
                 assume  cs:code_86,ds:data_86;?????????
;такая же процедура вывода мигающей строки, как и задачи 0, только 
; другие атрибуты

         task_v86    proc
          c1:    mov    ah,31h
                 mov    cx,2
          c2:    push   cx
		 mov 	di,1020
                 mov    cx,mes_86_len
                 mov    si,offset mes_86
                 cld
          c3:    lodsb
                 stosw
                 loop   c3
		 delay 30
                 pop    cx		
                 mov    ah,41h     
                 loop   c2
; А вот это процедура вывода строки, используя  прерывание BIOS 
; INT 10h
	mov cx,mes_00_len
	mov bx,offset mes_00
	mov ah,0eh
wr:	mov al,[bx]
	inc bx
	int 10h
	loop wr
                 jmp    c1; а цикл такой же бесконечный

           task_v86  endp

         code_86_size=$-task_v86
         code_86 ends
stk86=200h
;*********************************************************************
		;Cегмент стека  задачи V86
;*********************************************************************

         stk_86   segment 'abc'
                  db    stk86  dup ('0')
	stk_86_size=$-stk_86
           stk_86  ends
              end   main
;*********************************************************************
		;Конец программы
;*********************************************************************