Главная ]
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

#include

#include

#include

#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, что приводит к завершению работы главной задачи (ожидающей установки этого семафора).

Лиasm %1.asm /l /zi
tlink %1.obj /vclude
#include
#include
#include
#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 

#include 

#include 

#include 

#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 

#include 

#include 

#include 

#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 

#include 

#include 

#include 

#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 

#include 

#include 

#include 

#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 

#include 

#include 

#include 

#include "tos.h"

extern word key_code;
// Функция, ожидающая нажатия любой
// клавиши и возвращающая её скан-код
unsigned int kb_getch(void)
 {
  asm int 30h
  return(key_code);
 }

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

Листинг 13. Процедуры для работы с клавиатурой.
Файл keyboard.asm
-----------------------------------------------
IDEALMODEL SMALL
RADIX   16P286include "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_sca
next_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_EntryEND

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

Листинг 14. Процедуры для работы с видеоадаптером.
Файл screen.c
--------------------------------------------------
#include 

#include 

#include 

#include 

#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
 

<< Назад ] Содержание ] Далее >> ]

Дизайн: Piton Alien
Rambler's Top100 Рейтинг@Mail.ru
Сайт создан в системе uCoz