4.4. Пример мультизадачного монитора

к содержанию

Для того, чтобы вы могли почувствовать мультизадачность, мы подготовили пример программы, реализующей параллельную работу нескольких задач в режиме разделения времени.

Эта программа состоит из нескольких модулей, составленных на языках ассемблера и Си.

Первые два файла предназначены для определения используемых констант и структур данных.

Листинг 4. Определение констант и структур для модулей,
                составленных на языке ассемблера.

Файл tos.inc
-----------------------------------------------------------

CMOS_PORT               equ     70h
PORT_6845               equ     63h
COLOR_PORT      equ     3d4h
MONO_PORT               equ     3b4h
STATUS_PORT     equ     64h
SHUT_DOWN               equ     0feh
INT_MASK_PORT   equ     21h
VIRTUAL_MODE    equ     0001
A20_PORT                equ     0d1h
A20_ON          equ     0dfh
A20_OFF         equ     0ddh
EOI                     equ     20h
MASTER8259A     equ     20h
SLAVE8259A      equ     0a0h
KBD_PORT_A      equ     60h
KBD_PORT_B      equ     61h

L_SHIFT         equ     0000000000000001b
NL_SHIFT                equ     1111111111111110b
R_SHIFT         equ     0000000000000010b
NR_SHIFT                equ     1111111111111101b

L_CTRL          equ     0000000000000100b
NL_CTRL         equ     1111111111111011b
R_CTRL          equ     0000000000001000b
NR_CTRL         equ     1111111111110111b

L_ALT           equ     0000000000010000b
NL_ALT          equ     1111111111101111b
R_ALT           equ     0000000000100000b
NR_ALT          equ     1111111111011111b

CAPS_LOCK               equ     0000000001000000b
SCR_LOCK                equ     0000000010000000b
NUM_LOCK                equ     0000000100000000b
INSERT          equ     0000001000000000b


STRUC   idtr_struc
        idt_len dw      0
        idt_low dw      0
        idt_hi  db      0
        rsrv    db      0
ENDS            idtr_struc

Листинг 5. Определение констант и структур для модулей,
                составленных на языке Си.

Файл tos.h
-----------------------------------------------------------

#define word unsigned int

// Селекторы, определённые в GDT

#define CODE_SELECTOR        0x08 // сегмент кода
#define DATA_SELECTOR        0x10 // сегмент данных

#define TASK_1_SELECTOR            0x18 // задача TASK_1
#define TASK_2_SELECTOR            0x20 // задача TASK_2
#define MAIN_TASK_SELECTOR      0x28 // главная задача

#define VID_MEM_SELECTOR          0x30 // сегмент видеопамяти
#define IDT_SELECTOR            0x38 // талица IDT

#define KEYBIN_TASK_SELECTOR    0x40 // задача ввода с клавиатуры
#define KEYB_TASK_SELECTOR       0x48 // задача обработки
                                                  // клавиатурного прерывания
#define FLIP_TASK_SELECTOR       0x50 // задача FLIP_TASK

// Байт доступа

typedef struct {
         unsigned accessed   : 1;
         unsigned read_write : 1;
         unsigned conf_exp   : 1;
         unsigned code       : 1;
         unsigned xsystem    : 1;
         unsigned dpl         : 2;
         unsigned present    : 1;
         } ACCESS;

// Структура дескриптора

typedef struct descriptor {
        word limit;
        word base_lo;
        unsigned char base_hi;
        unsigned char type_dpl;
        unsigned reserved;
} descriptor;

// Структура вентиля вызова, задачи, прерывания,
// исключения

typedef struct gate {
        word offset;
        word selector;
        unsigned char count;
        unsigned char type_dpl;
        word reserved;
} gate;

// Структура сегмента состояния задачи TSS

typedef struct tss {
        word link;      // поле обратной связи

        word sp0;               // указатель стека кольца 0
        word ss0;
        word sp1;               // указатель стека кольца 1
        word ss1;
        word sp2;               // указатель стека кольца 1
        word ss2;

        word ip;                // регистры процессора
        word flags;
        word ax;
        word cx;
        word dx;
        word bx;
        word sp;
        word bp;
        word si;
        word di;
        word es;
        word cs;
        word ss;
        word ds;
        word ldtr;
} tss;

// Размеры сегментов и структур

#define TSS_SIZE            (sizeof(tss))
#define DESCRIPTOR_SIZE     (sizeof(descriptor))
#define GATE_SIZE           (sizeof(gate))
#define IDT_SIZE            (sizeof(idt))

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

#define COLOR_VID_MEM       0xb8000L
#define MONO_VID_MEM        0xb0000L

// Видеоржеимы

#define MONO_MODE           0x07 // монохромный
#define BW_80_MODE          0x02 // монохромный, 80 символов
#define COLOR_80_MODE       0x03 // цветной, 80 символов

// Значения для поля доступа

#define TYPE_CODE_DESCR     0x18
#define TYPE_DATA_DESCR     0x10
#define TYPE_TSS_DESCR      0x01
#define TYPE_CALL_GATE      0x04
#define TYPE_TASK_GATE      0x85
#define TYPE_INTERRUPT_GATE 0x86
#define TYPE_TRAP_GATE      0x87

#define SEG_WRITABLE        0x02
#define SEG_READABLE        0x02
#define SEG_PRESENT_BIT     0x80

// Константы для обработки аппаратных
// прерываний

#define EOI                             0x20
#define MASTER8259A             0x20
#define SLAVE8259A                      0xa0

// Макро для формирования физического
// адреса из компонент сегменоного адреса
// и смещения

#define MK_LIN_ADDR(seg,off) (((unsigned long)(seg))<<4)+(word)(off)

// Тип указателя на функцию типа void без параметров

typedef void (func_ptr)(void);




Файл tos.c (листинг 6) содержит основную программу, которая инициализирует процессор для работы в защищённом режиме и запускает все задачи. С помощью функции с названием Init_And_Protected_Mode_Entry() мы попадаем в защищённый режим и выводим сообщение на экран о том, что в главной задаче установлен защищённый режим. Регистр TR загружается селектором главной задачи при помощи функции load_task_register().

Сразу после этого программа переключается на выполнение задачи TASK_1. Эта задача просто выводит сообщение о своём запуске на экран и возвращает управление главной задаче. Цель этой процедуры - продемонстрировать процесс переключения задач с помощью команды JMP.

После возврата в главную задачу программа размаскирует прерывания от клавиатуры и таймера. Как только начинают поступать и обрабатываться прерывания таймера, оживают остальные задачи, определённые при инициализации системы.

Начиная с этого момента главная задача разделяет процессорное время наравне с остальными задачами. Что же она делает?

Главная задача сбрасывает семафор с номером 0, вслед за чем ожидает его установку. Этот семафор устанавливается задачей, которая занимается вводом символов с клавиатуры и выводит на экран скан-коды клавиш, а также состояние переключающих клавиш. Как только окажется нажатой клавиша ESC, задача ввода символов с клавиатуры устанавливает семафор 0, что и приводит к завершению работы главной задачи.

Перед тем, как завершить работу, главная задача устанавливает реальный режим работы процессора, стирает экран и возвращает управление операционной системе.

Листинг 6. Программа мультизадачного монитора.

Файл tos.c
-----------------------------------------------------------


#include <stdio.h>
#include <stdlib.h>
#include <dos.h>
#include <conio.h>
#include "tos.h"

// --------------------------------
// Определения вызываемых функций
// --------------------------------

void Init_And_Protected_Mode_Entry(void);

void protected_mode(unsigned long gdt_ptr, unsigned int gdt_size,
                word cseg, word dseg);

word load_task_register(word tss_selector);
void real_mode(void);
void jump_to_task(word tss_selector);
void load_idtr(unsigned long idt_ptr, word idt_size);
void Keyb_int(void);
void    Timer_int(void);
void Int_30h_Entry(void);

extern  word kb_getch(void);
void            enable_interrupt(void);

void    task1(void);
void    task2(void);
void            flipflop_task(void);
void     keyb_task(void);

void init_tss(tss *t, word cs, word ds,
                        unsigned char *sp, func_ptr ip);

void init_gdt_descriptor(descriptor *descr, unsigned long base,
                        word limit, unsigned char type);

void  exception_0(void); //{ prg_abort(0); }
void  exception_1(void); //{ prg_abort(1); }
void  exception_2(void); //{ prg_abort(2); }
void  exception_3(void); //{ prg_abort(3); }
void  exception_4(void); //{ prg_abort(4); }
void  exception_5(void); //{ prg_abort(5); }
void  exception_6(void); //{ prg_abort(6); }
void  exception_7(void); //{ prg_abort(7); }
void  exception_8(void); //{ prg_abort(8); }
void  exception_9(void); //{ prg_abort(9); }
void  exception_A(void); //{ prg_abort(0xA); }
void  exception_B(void); //{ prg_abort(0xB); }
void  exception_C(void); //{ prg_abort(0xC); }
void  exception_D(void); //{ prg_abort(0xD); }
void  exception_E(void); //{ prg_abort(0xE); }
void  exception_F(void); //{ prg_abort(0xF); }
void  exception_10(void); //{ prg_abort(0x10); }
void  exception_11(void); //{ prg_abort(0x11); }
void  exception_12(void); //{ prg_abort(0x12); }
void  exception_13(void); //{ prg_abort(0x13); }
void  exception_14(void); //{ prg_abort(0x14); }
void  exception_15(void); //{ prg_abort(0x15); }
void  exception_16(void); //{ prg_abort(0x16); }
void  exception_17(void); //{ prg_abort(0x17); }
void  exception_18(void); //{ prg_abort(0x18); }
void  exception_19(void); //{ prg_abort(0x19); }
void  exception_1A(void); //{ prg_abort(0x1A); }
void  exception_1B(void); //{ prg_abort(0x1B); }
void  exception_1C(void); //{ prg_abort(0x1C); }
void  exception_1D(void); //{ prg_abort(0x1D); }
void  exception_1E(void); //{ prg_abort(0x1E); }
void  exception_1F(void); //{ prg_abort(0x1F); }

void iret0(void);
void iret1(void);

// --------------------------------------
// Глобальная таблица дескрипторов GDT
// --------------------------------------

descriptor      gdt[11];

// --------------------------------------
// Дескрипторная таблица прерываний IDT
// --------------------------------------

gate            idt[] = {

// Обработчики исключений

 { (word)&exception_0, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 0
 { (word)&exception_1, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1
 { (word)&exception_2, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 2
 { (word)&exception_3, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 3
 { (word)&exception_4, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 4
 { (word)&exception_5, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 5
 { (word)&exception_6, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 6
 { (word)&exception_7, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 7
 { (word)&exception_8, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 8
 { (word)&exception_9, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 9
 { (word)&exception_A, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // A
 { (word)&exception_B, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // B
 { (word)&exception_C, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // C
 { (word)&exception_D, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // D
 { (word)&exception_E, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // E
 { (word)&exception_F, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // F
 { (word)&exception_10, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 10
 { (word)&exception_11, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 11
 { (word)&exception_12, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 12
 { (word)&exception_13, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 13
 { (word)&exception_14, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 14
 { (word)&exception_15, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 15
 { (word)&exception_16, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 16
 { (word)&exception_17, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 17
 { (word)&exception_18, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 18
 { (word)&exception_19, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 19
 { (word)&exception_1A, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1A
 { (word)&exception_1B, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1B
 { (word)&exception_1C, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1C
 { (word)&exception_1D, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1D
 { (word)&exception_1E, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1E
 { (word)&exception_1F, CODE_SELECTOR, 0, TYPE_TRAP_GATE, 0 }, // 1F

// Обработчик прерываний таймера

 { (word)&Timer_int, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 20

// Вентиль задачи, запускающейся по прерыванию от клавиатуры

 { 0, KEYB_TASK_SELECTOR, 0, TYPE_TASK_GATE, 0 }, // 21

// Заглушки для остальных аппаратных прерываний

 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 22
 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 23
 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 24
 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 25
 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 26
 { (word)&iret0, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 27

 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 28
 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 29
 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2A
 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2B
 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2C
 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2D
 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2E
 { (word)&iret1, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 2F

// Обработчик для программного прерывания, которое
// используется для ввода с клавиатуры

 { (word)&Int_30h_Entry, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 },  // 30

// Вентиль задачи FLIP_TASK

 { 0, FLIP_TASK_SELECTOR, 0, TYPE_TASK_GATE, 0 } // 31

};

// -------------------------------------------
// Сегменты TSS для различных задач
// -------------------------------------------

tss main_tss;           // TSS главной задачи
tss task_1_tss; // TSS задачи TASK_1
tss task_2_tss;     // TSS задачи TASK_2
tss keyb_task_tss;  // TSS задач обслуживания
tss keyb_tss;           //              клавиатуры
tss flipflop_tss;       // TSS задачи FLIP_TASK

// -------------------------------------------
// Стеки для задач
// -------------------------------------------

unsigned char task_1_stack[1024];
unsigned char task_2_stack[1024];
unsigned char keyb_task_stack[1024];
unsigned char keyb_stack[1024];
unsigned char flipflop_stack[1024];

word y=0;       // номер текущей строки для вывода на экран



// -------------------------------------------
// Начало программы
// -------------------------------------------

void main(void) {

// Стираем экран

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

// Входим в защищённый режим

        Init_And_Protected_Mode_Entry();

// Выводим сообщение

        vi_hello_msg();
        y=3;
        vi_print(0, y++,
         " Установлен защищённый режим в главной задаче", 0x7f);

// Загружаем регистр TR селектором главной задачи
// т.е. задачи main()

        load_task_register(MAIN_TASK_SELECTOR);

// Переключаемся на задачу TASK_1

        jump_to_task(TASK_1_SELECTOR);

// После возврата в главную задачу выдаём сообщение

        vi_print(0, y++ ," Вернулись в главную задачу", 0x7f);
        y++;

// Запускаем планировщик задач

        vi_print(0, y++ ," Запущен планировщик задач", 0x70);
        enable_interrupt(); // разрешаем прерывание таймера

// Ожидаем установки семафора с номером 0. После того,
// как этот семафор окажется установлен, возвращаемся
// в реальный режим.

// Семафор 0 устанавливается задачей, обрабатывающей ввод с
// клавиатуры, которая работает независимо от
// главной задаче.

        vi_print(0, y++ ," Для возврата в реальный режим нажмите ESC", 0x70);

        sem_clear(0); // сброс семафора 0
        sem_wait(0);  // ожидание установки семафора 0

// Возврат в реальный режим, стирание экрана и
// передача управления MS-DOS

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

// -----------------------------------
// Функция инициализации сегмента TSS
// -----------------------------------

void init_tss(tss *t, word cs, word ds,
        unsigned char *sp, func_ptr ip) {

        t->cs = cs;          // селектор сегмента кода
        t->ds = ds;          // поля ds, es, ss устанавливаем
        t->es = ds;          // на сегмент данных
        t->ss = ds;
        t->ip = (word)ip;    // указатель команд
        t->sp = (word)sp;    // смещение стека
        t->bp = (word)sp;
}

// -------------------------------------------------
// Функция инициализации дескриптора в таблице GDT
// -------------------------------------------------

void init_gdt_descriptor(descriptor *descr,
                unsigned long base, word limit,
                unsigned char type) {

// Младшее слово базового адреса
        descr->base_lo  = (word)base;

// Старший байт базового адреса
        descr->base_hi  = (unsigned char)(base >> 16);

// Поле доступа дескриптора
        descr->type_dpl = type;

// Предел
        descr->limit    = limit;

// Зарезервированное поле, должно быть
// сброшено в 0
        descr->reserved = 0;
}

// -----------------------------------------------
// Инициализация всех таблиц и вход
// в защищённый режим
// -----------------------------------------------

void Init_And_Protected_Mode_Entry(void) {

        union REGS r;

// Инициализируем таблицу GDT, элементы с 1 по 5

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

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

        init_gdt_descriptor(&gdt[3],
         MK_LIN_ADDR(_DS, &task_1_tss),
         (unsigned long)TSS_SIZE-1, TYPE_TSS_DESCR | SEG_PRESENT_BIT);

        init_gdt_descriptor(&gdt[4],
         MK_LIN_ADDR(_DS, &task_2_tss),
         (unsigned long)TSS_SIZE-1, TYPE_TSS_DESCR | SEG_PRESENT_BIT);

        init_gdt_descriptor(&gdt[5],
         MK_LIN_ADDR(_DS, &main_tss),
         (unsigned long)TSS_SIZE-1, TYPE_TSS_DESCR | SEG_PRESENT_BIT);


// Инициализируем TSS для задач TASK_1, TASK_2

        init_tss(&task_1_tss, CODE_SELECTOR, DATA_SELECTOR, task_1_stack+
                sizeof(task_1_stack), task1);

        init_tss(&task_2_tss, CODE_SELECTOR, DATA_SELECTOR, task_2_stack+
                sizeof(task_2_stack), task2);

// Инициализируем элемент 6 таблицы GDT -
// дескриптор для сегмента видеопамяти

// Определяем видеорежим
        r.h.ah=15;
        int86(0x10,&r,&r);

// Инициализация для монохромного режима

        if(r.h.al==MONO_MODE)
          init_gdt_descriptor(&gdt[6], MONO_VID_MEM,
          3999, TYPE_DATA_DESCR | SEG_PRESENT_BIT | SEG_WRITABLE);

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

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

// Инициализация элементов 7 и 8 таблицы GDT

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

        init_gdt_descriptor(&gdt[8],
                MK_LIN_ADDR(_DS, &keyb_task_tss),
                (unsigned long)TSS_SIZE-1,
                TYPE_TSS_DESCR | SEG_PRESENT_BIT);

// Инициализация TSS для задачи KEYB_TASK

        init_tss(&keyb_task_tss, CODE_SELECTOR, DATA_SELECTOR,
                keyb_task_stack + sizeof(keyb_task_stack), keyb_task);

// Инициализация элемента 9 таблицы GDT

        init_gdt_descriptor(&gdt[9],
                MK_LIN_ADDR(_DS, &keyb_tss),
                (unsigned long)TSS_SIZE-1,
                TYPE_TSS_DESCR | SEG_PRESENT_BIT);

// Инициализация TSS для задачи KEYB обработки ввода с клавиатуры

        init_tss(&keyb_tss, CODE_SELECTOR, DATA_SELECTOR,
                keyb_stack + sizeof(keyb_stack), Keyb_int);


// Инициализация элемента 10 таблицы GDT

        init_gdt_descriptor(&gdt[10],
                MK_LIN_ADDR(_DS, &flipflop_tss),
                (unsigned long)TSS_SIZE-1,
                TYPE_TSS_DESCR | SEG_PRESENT_BIT);

// Инициализация TSS для задачи FLIP_TASK

        init_tss(&flipflop_tss, CODE_SELECTOR, DATA_SELECTOR,
                flipflop_stack + sizeof(flipflop_stack), flipflop_task);

// Загрузка регистра IDTR

        load_idtr(MK_LIN_ADDR(_DS, &idt), IDT_SIZE);

// Вход в защищённый режим

        protected_mode(MK_LIN_ADDR(_DS, &gdt), sizeof(gdt),
                        CODE_SELECTOR, DATA_SELECTOR);
}




Файл tasks.c содержит тексты программ, которые будут работать в режиме разделения времени (кроме задачи TASK_1, эта задача запускается только один раз).

Задача TASK_1 (процедура task1) выдаёт сообщение о своём запуске и передаёт управление главной задаче.

Задача TASK_2 (процедура task2) попеременно выводит на экран строки "FLIP" и "FLOP", переключая попутно семафор с номером 1.

Задача FLIP_TASK (процедура flipflop_task) также попеременно выводит на экран строки "FLIP" и "FLOP", но только тогда, когда семафор с номером 1 установлен. Таким образом, задача TASK_2 управляет работой задачи FLIP_TASK.

Задача KEYB_TASK (процедура keyb_task) вводит символы с клавиатуры и выводит скан-коды нажатых клавиш, а также состояние переключающих клавиш. Как только оказывается нажатой клавиша ESC, задача устанавливает семафор с номером 0, что приводит к завершению работы главной задачи (ожидающей установки этого семафора).

Листинг 7. Задачи, которые будут работать параллельно.

Файл tasks.c
-----------------------------------------------------------

#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"

word dispatcher(void);

// Номер текущей строки для вывода на экран

extern unsigned int y;

// Задача TASK_1

void task1(void) {
        while(1){
                vi_print(0,y++," Запущена задача TASK_1, "
                                        "переходим к главной задаче", 0x70);
                jump_to_task(MAIN_TASK_SELECTOR);

// После повторного запуска этой задачи
// снова входим в цикл.

        }
}

// Задача TASK_2

word flipflop1 = 0;
long delay_cnt1 = 0l;

void task2(void) {
        while(1){

// Периодически выводим на экран строки
// FLIP/FLOP, каждый раз переключая
// семафор номер 1. Этот семафор однозначно
// соответствует выведенной на экран строке.

                asm sti
                if(delay_cnt1 > 150000l ) {
                        asm cli
                        if(flipflop1)   {
                                vi_print(73,3," FLIP ", 0x4f);
                                sem_clear(1);
                        }
                        else  {
                                vi_print(73,3," FLOP ", 0x1f);
                                sem_set(1);
                        }
                        flipflop1 ^= 1;
                        delay_cnt1 = 0l;
                        asm sti
                }
                delay_cnt1++;
        }
}

word flipflop = 0;
long delay_cnt = 0l;

void flipflop_task(void) {

// Эта задача также периодически выводит на экран
// строки FLIP/FLOP, но выводит строкой выше и
// с меньшим периодом. Кроме того, эта задача
// работает только тогда, когда установлен
// семафор номер 1.

        while(1){
                asm sti
                if(delay_cnt > 20000l ) {
                        sem_wait(1); // ожидаем установки семафора
                        asm cli
                        if(flipflop)    vi_print(73,2," FLIP ", 0x20);
                        else            vi_print(73,2," FLOP ", 0x20);
                        flipflop ^= 1;
                        delay_cnt = 0l;
                        asm sti
                }
                delay_cnt++;
        }
}

word keyb_code;

extern word keyb_status;

void keyb_task(void) {

// Эта задача вводит символы с клавиатуры
// и отображает скан-коды нажатых клавиш
// и состояние переключающих клавиш на экране.
// Если нажимается клавиша ESC, задача
// устанавливает семафор номер 0.
// Работающая параллельно главная задача
// ожидает установку этого семафора. Как только
// семафор 0 окажется установлен, главная задача
// завершает свою работу и программа возвращает
// процессор в реальный режим, затем передаёт
// управление MS-DOS.


        vi_print(60, 5, " Key code:   .... ", 0x20);
        vi_print(60, 6, " Key status: .... ", 0x20);
        while(1){
                keyb_code = kb_getch();
                vi_put_word(73, 5, keyb_code, 0x4f);
                vi_put_word(73, 6, keyb_status, 0x4f);
                if((keyb_code & 0x00ff) == 1) sem_set(0);
        }
}




Файл semaphor.c содержит исходные тексты процедур сброса семафора, установки семафора и ожидания семафора.

В массиве semaphore[5] определено пять семафоров. Разумеется, что когда вы будете экспериментировать с программой, вы можете изменить количество доступных семафоров.

Листинг 8. Процедуры для работы с семафорами.

Файл semaphor.c
-----------------------------------------------------------

#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"

// Массив из пяти семафоров

word semaphore[5];

// Процедура сброса семафора.
// Параметр sem - номер сбрасываемого семафора

void sem_clear(int sem) {
        asm cli
        semaphore[sem] = 0;
        asm sti
}

// Процедура установки семафора
// Параметр sem - номер устанавливаемого семафора

void sem_set(int sem) {
        asm cli
        semaphore[sem] = 1;
        asm sti
}

// Ожидание установки семафора
// Параметр sem - номер ожидаемого семафора

void sem_wait(int sem) {
        while(1) {
                asm cli
                if(semaphore[sem]) break; // проверяем семафор

                asm sti // ожидаем установки семафора
                asm nop
                asm nop
        }
        asm sti
}




Файл timer.c содержит обработчик аппаратного прерывания таймера, который периодически выдаёт звуковой сигнал и инициирует работу диспетчера задач. Диспетчер задач циклически перебирает селекторы TSS задач, участвующих в процессе разделения времени, возвращая селектор той задачи, которая должна стать активной. В самом конце обработки аппаратного прерывания таймера происходит переключение именно на эту задачу.

Листинг 9. Процедуры для работы с таймером и
                диспетчер задач.

Файл timer.c
-----------------------------------------------------------


#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"

// -------------------------------------------
//      Модуль обслуживания таймера
// -------------------------------------------

#define EOI 0x20
#define MASTER8259A 0x20

extern void beep(void);
extern void flipflop_task(void);
void Timer_int(void);
word dispatcher(void);

word    timer_cnt;

// ------------------------------------------
// Обработчик аппаратного прерывания таймера
// ------------------------------------------

void Timer_int(void) {

        asm pop bp

// Периодически выдаём звуковой сигнал

        timer_cnt += 1;
        if((timer_cnt & 0xf) == 0xf) {
                beep();
        }

// Выдаём в контроллер команду конца
// прерывания

        asm mov al,EOI
        asm out MASTER8259A,al

// Переключаемся на следующую задачу,
// селектор TSS которой получаем от
// диспетчера задач dispatcher()

        jump_to_task(dispatcher());
        asm iret
}

// --------------------------------------
// Диспетчер задач
// --------------------------------------

// Массив селекторов, указывающих на TSS
// задач, участвующих в параллельной работе,
// т.е. диспетчеризуемых задач

word task_list[] = {
        MAIN_TASK_SELECTOR,
        FLIP_TASK_SELECTOR,
        KEYBIN_TASK_SELECTOR,
        TASK_2_SELECTOR
};

word current_task = 0; // текущая задача
word max_task = 3;     // количество задач - 1

// Используем простейший алгоритм диспетчеризации -
// выполняем последовательное переключение на все
// задачи, селекторы TSS которых находятся
// в массиве task_list[].

word dispatcher(void) {

        if(current_task < max_task) current_task++;
        else current_task = 0;
        return(task_list[current_task]);
}




Для сокращения объёма и без того сложной программы мы не стали делать функционально полную обработку исключений, ограничившись простым аварийным завершением работы программы с выдачей номера исключения.

Исходные тексты обработчиков исключений находятся в файле except.c.

Листинг 10. Обработка исключений.

Файл except.c
-----------------------------------------------------------

#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"

void prg_abort(int err);

// Номер текущей строки для вывода на экран

extern unsigned int y;

// Обработчики исключений

void  exception_0(void) { prg_abort(0); }
void  exception_1(void) { prg_abort(1); }
void  exception_2(void) { prg_abort(2); }
void  exception_3(void) { prg_abort(3); }
void  exception_4(void) { prg_abort(4); }
void  exception_5(void) { prg_abort(5); }
void  exception_6(void) { prg_abort(6); }
void  exception_7(void) { prg_abort(7); }
void  exception_8(void) { prg_abort(8); }
void  exception_9(void) { prg_abort(9); }
void  exception_A(void) { prg_abort(0xA); }
void  exception_B(void) { prg_abort(0xB); }
void  exception_C(void) { prg_abort(0xC); }
void  exception_D(void) { prg_abort(0xD); }
void  exception_E(void) { prg_abort(0xE); }
void  exception_F(void) { prg_abort(0xF); }
void  exception_10(void) { prg_abort(0x10); }
void  exception_11(void) { prg_abort(0x11); }
void  exception_12(void) { prg_abort(0x12); }
void  exception_13(void) { prg_abort(0x13); }
void  exception_14(void) { prg_abort(0x14); }
void  exception_15(void) { prg_abort(0x15); }
void  exception_16(void) { prg_abort(0x16); }
void  exception_17(void) { prg_abort(0x17); }
void  exception_18(void) { prg_abort(0x18); }
void  exception_19(void) { prg_abort(0x19); }
void  exception_1A(void) { prg_abort(0x1A); }
void  exception_1B(void) { prg_abort(0x1B); }
void  exception_1C(void) { prg_abort(0x1C); }
void  exception_1D(void) { prg_abort(0x1D); }
void  exception_1E(void) { prg_abort(0x1E); }
void  exception_1F(void) { prg_abort(0x1F); }

// ------------------------------
// Аварийный выход из программы
// ------------------------------

void prg_abort(int err) {

        vi_print(1,y++,"!!! ---> Произошло исключение", 0xc);

        real_mode(); // Возвращаемся в реальный режим

// В реальном режиме выводим сообщение об исключении

        gotoxy(1, ++y);
        cprintf(" Исключение %X, нажмите любую клавишу", err);
        getch();

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

}




В файле intproc.c расположены заглушки для тех аппаратных прерываний, обработка которых сводится к простой посылке кода конца прерывания в соответствующий контроллер прерывания.

Листинг 11. Заглушки для аппаратных прерываний.

Файл intproc.c
-----------------------------------------------------------


#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"

// Заглушки для необрабатываемых
// аппаратных прерываний.

void iret0(void) {     // первый контроллер прерываний
        asm {
                push    ax
                mov     al,EOI
                out     MASTER8259A,al
                pop     ax
                pop bp
                iret
        }
}

void iret1(void) {     // второй контроллер прерываний
        asm {
                push    ax
                mov     al,EOI
                out     MASTER8259A,al
                out     SLAVE8259A,al
                pop     ax
                pop bp
                iret
        }
}




Файл keyb.c содержит простой интерфейс для вызова программного прерывания int 30h, обеспечивающего ввод с клавиатуры.

Листинг 12. Ввод символа с клавиатуры.

Файл keyb.c
-----------------------------------------------------------


#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"

extern word key_code;

// Функция, ожидающая нажатия любой
// клавиши и возвращающая её скан-код

unsigned int kb_getch(void) {
        asm int 30h
        return(key_code);
}




Обработчик аппаратного прерывания клавиатуры мы взяли практически без изменений из программы, представленной в предыдущей главе. Исходные тексты находятся в файле keyboard.asm.

Листинг 13. Процедуры для работы с клавиатурой.

Файл keyboard.asm
-----------------------------------------------------------

IDEAL

MODEL SMALL
RADIX   16

P286
include "tos.inc"

; ------------------------------------------
; Модуль обслуживания клавиатуры
; ------------------------------------------

PUBLIC  _Keyb_int, _Int_30h_Entry, _key_code, _keyb_status
EXTRN   _beep:PROC
DATASEG

        _key_flag       db      0
        _key_code       dw      0
        ext_scan        db      0
        _keyb_status    dw      0

CODESEG

PROC    _Keyb_int        NEAR
        cli

        call    _beep

        push    ax
        mov     al, [ext_scan]
        cmp     al, 0
        jz      normal_scan1
        cmp     al, 0e1h
        jz      pause_key

        in      al, 60h
        cmp     al, 2ah
        jz      intkeyb_exit_1
        cmp     al, 0aah
        jz      intkeyb_exit_1

        mov     ah, [ext_scan]
        call    Keyb_PutQ

        mov     al, 0
        mov     [ext_scan], al
        jmp     intkeyb_exit

pause_key:

        in      al, 60h
        cmp     al, 0c5h
        jz      pause_key1
        cmp     al, 45h
        jz      pause_key1

        jmp     intkeyb_exit

pause_key1:
        mov     ah, [ext_scan]
        call    Keyb_PutQ

        mov     al, 0
        mov     [ext_scan], al
        jmp     intkeyb_exit


normal_scan1:
        in      al, 60h
        cmp     al, 0feh
        jz      intkeyb_exit
        cmp     al, 0e1h
        jz      ext_key
        cmp     al, 0e0h
        jnz     normal_scan

ext_key:
        mov     [ext_scan], al
        jmp     intkeyb_exit


intkeyb_exit_1:
        mov     al, 0
        mov     [ext_scan], al
        jmp     intkeyb_exit

normal_scan:
        mov     ah, 0
        call    Keyb_PutQ

intkeyb_exit:
        in      al, 61h
        mov     ah, al
        or      al, 80h
        out     61h, al
        xchg    ah, al
        out     61h, al
        mov     al,EOI
        out     MASTER8259A,al

        pop     ax
        sti
        iret
        jmp     _Keyb_int
ENDP    _Keyb_int


PROC    Keyb_PutQ       NEAR

        push    ax

        cmp     ax, 002ah       ; L_SHIFT down
        jnz     @@kb1
        mov     ax, [_keyb_status]
        or      ax, L_SHIFT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb1:
        cmp     ax, 00aah       ; L_SHIFT up
        jnz     @@kb2
        mov     ax, [_keyb_status]
        and     ax, NL_SHIFT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb2:
        cmp     ax, 0036h       ; R_SHIFT down
        jnz     @@kb3
        mov     ax, [_keyb_status]
        or      ax, R_SHIFT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb3:
        cmp     ax, 00b6h       ; R_SHIFT up
        jnz     @@kb4
        mov     ax, [_keyb_status]
        and     ax, NR_SHIFT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb4:
        cmp     ax, 001dh       ; L_CTRL down
        jnz     @@kb5
        mov     ax, [_keyb_status]
        or      ax, L_CTRL
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb5:
        cmp     ax, 009dh       ; L_CTRL up
        jnz     @@kb6
        mov     ax, [_keyb_status]
        and     ax, NL_CTRL
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb6:
        cmp     ax, 0e01dh      ; R_CTRL down
        jnz     @@kb7
        mov     ax, [_keyb_status]
        or      ax, R_CTRL
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb7:
        cmp     ax, 0e09dh      ; R_CTRL up
        jnz     @@kb8
        mov     ax, [_keyb_status]
        and     ax, NR_CTRL
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb8:
        cmp     ax, 0038h       ; L_ALT down
        jnz     @@kb9
        mov     ax, [_keyb_status]
        or      ax, L_ALT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb9:
        cmp     ax, 00b8h       ; L_ALT up
        jnz     @@kb10
        mov     ax, [_keyb_status]
        and     ax, NL_ALT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb10:
        cmp     ax, 0e038h      ; R_ALT down
        jnz     @@kb11
        mov     ax, [_keyb_status]
        or      ax, R_ALT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb11:
        cmp     ax, 0e0b8h      ; R_ALT up
        jnz     @@kb12
        mov     ax, [_keyb_status]
        and     ax, NR_ALT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb12:
        cmp     ax, 003ah       ; CAPS_LOCK up
        jnz     @@kb13
        mov     ax, [_keyb_status]
        xor     ax, CAPS_LOCK
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb13:
        cmp     ax, 00bah       ; CAPS_LOCK down
        jnz     @@kb14
        jmp     keyb_putq_exit
@@kb14:
        cmp     ax, 0046h       ; SCR_LOCK up
        jnz     @@kb15
        mov     ax, [_keyb_status]
        xor     ax, SCR_LOCK
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb15:
        cmp     ax, 00c6h       ; SCR_LOCK down
        jnz     @@kb16
        jmp     keyb_putq_exit
@@kb16:
        cmp     ax, 0045h       ; NUM_LOCK up
        jnz     @@kb17
        mov     ax, [_keyb_status]
        xor     ax, NUM_LOCK
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb17:
        cmp     ax, 00c5h       ; NUM_LOCK down
        jnz     @@kb18
        jmp     keyb_putq_exit
@@kb18:
        cmp     ax, 0e052h      ; INSERT up
        jnz     @@kb19
        mov     ax, [_keyb_status]
        xor     ax, INSERT
        mov     [_keyb_status], ax
        jmp     keyb_putq_exit
@@kb19:
        cmp     ax, 0e0d2h      ; INSERT down
        jnz     @@kb20
        jmp     keyb_putq_exit
@@kb20:

        test    ax, 0080h
        jnz     keyb_putq_exit

        mov     [_key_code], ax

        mov     al, 0ffh
        mov     [_key_flag], al
keyb_putq_exit:
        pop     ax
        ret
ENDP    Keyb_PutQ

; Обработчик программного прерывания
; для ввода с клавиатуры. По своим функциям
; напоминает прерывание INT 16 реального
; режима.


PROC    _Int_30h_Entry   NEAR
        push   ax dx

; Ожидаем прерывание от клавиатуры

keyb_int_wait:
        sti
        nop
        nop
        cli

; Проверяем флаг, который устанавливается
; обработчиком аппаратного прерывания клавиатуры

        mov     al, [_key_flag]
        cmp     al, 0
        jz      keyb_int_wait

; Сбрасываем флаг после прихода прерывания

        mov     al, 0
        mov     [_key_flag], al
        sti
        pop     dx ax
        iret
ENDP   _Int_30h_Entry

END




Файл screen.c содержит процедуры, необходимые для вывода информации на экран дисплея. Работа этих процедур основана на непосредственной записи данных в видеопамять.

Листинг 14. Процедуры для работы с видеоадаптером.

Файл screen.c
-----------------------------------------------------------

#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"

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

char hex_tabl[] = "0123456789ABCDEF";

// Вывод байта на экран, координаты (x,y),
// выводится шестнадцатеричное представление
// байта chr с экранными атрибутами attr.

void vi_put_byte(unsigned int x,
                unsigned int y, unsigned char chr, char attr) {
        unsigned char temp;

        temp = hex_tabl[(chr & 0xf0) >> 4];
        vi_putch(x, y, temp, attr);

        temp = hex_tabl[chr & 0xf];
        vi_putch(x+1, y, temp, attr);
}
// Вывод слова на экран, координаты (x,y),
// выводится шестнадцатеричное представление
// слова chr с экранными атрибутами attr.

void vi_put_word(unsigned int x,
        unsigned int y, word chr, char attr) {

        vi_put_byte(x, y, (chr & 0xff00) >> 8, attr);
        vi_put_byte(x+2, y, chr & 0xff, attr);

}

// Вывод символа c на экран, координаты - (x,y),
// атрибут выводимого символа - attr

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

        register unsigned int offset;
        char far *vid_ptr;

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

// Вывод строки s на экран, координаты - (x,y),
// атрибут выводимой строки - attr

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

// Вывод стоки сообщения о запуске программы

void vi_hello_msg(void) {

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

}




Последний файл - tossyst.asm - содержит уже знакомые вам процедуры для входа в защищённый режим и возврата обратно в реальный режим. Обратите внимание на процедуры _load_task_register и _jump_to_task, выполняющие загрузку регистра задачи TR и переключение на другую задачу соответственно.

Листинг 15. Процедуры для инициализации, перехода в
                защищённый режим и возврата в реальный режим,
                для загрузки регистра TR и переключения задач.

Файл tossyst.asm
-----------------------------------------------------------

IDEAL
MODEL SMALL
RADIX   16
P286

        DATASEG

        include "tos.inc"

        PUBLIC  _beep

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

        idtr                    idtr_struc <,,,0>

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

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

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

        new_task                dw  00h
        new_select       dw  00h

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

        real_ss         dw      ?
        real_sp         dw      ?
        real_es         dw      ?

        protect_sel     dw      ?

        init_tss                dw      ?

CODESEG

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

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

PROC _protected_mode NEAR
                push    bp
                mov     bp,sp

; Параметр gdt_ptr

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

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

; Параметр gdt_size

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

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

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

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

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

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

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

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

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

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

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

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

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

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

                lidt            [FWORD idtr]
                lgdt       [QWORD gdt_ptr]

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

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

LABEL   flush   FAR

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

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

                mov             ax, 0
                lldt            ax

                pop         bp
                ret
ENDP _protected_mode

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

PROC _real_mode   NEAR

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

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

rmode_wait:
                hlt
                jmp             rmode_wait

LABEL   shutdown_return FAR

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

                mov             ax, DGROUP
                mov             ds, ax

assume  ds:DGROUP

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

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

                call    disable_a20

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

                mov     ax,000dh
                out     CMOS_PORT,al
                sti

                ret
ENDP _real_mode

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

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

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

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

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

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

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

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

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

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

PROC _load_idtr NEAR
                push    bp

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

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

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

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

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

                pop         bp
                ret
ENDP _load_idtr

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

PROC    set_int_ctrlr   NEAR

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

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

PROC    _beep   NEAR

        push    ax bx cx

        in      al,KBD_PORT_B
        push    ax
        mov     cx,80

beep0:

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

idle1:

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

idle2:

        loop    idle2
        pop     cx
        loop    beep0

        pop     ax
        out     KBD_PORT_B,al

        pop     cx bx ax
        ret

ENDP    _beep

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

PROC    _pause          NEAR

        push    cx
        mov     cx,10

ploop0:

        push    cx
        xor     cx,cx

ploop1:

        loop    ploop1
        pop     cx
        loop    ploop0

        pop     cx
        ret

ENDP    _pause

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

PROC    _enable_interrupt NEAR

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

                sti
                ret
ENDP    _enable_interrupt

        end
к содержанию
Hosted by uCoz