Реферат: DOS-extender для компилятора Borland C

--PAGE_BREAK--

Рис. 3. Формат элементов дескрипторной таблицы прерываний IDT.

Расположение определяется содержимым 5-байтового внутреннего регистра процессора IDTR. Формат регистра IDTR полностью аналогичен формату регистра GDTR, для его загрузки используется команда LIDT. Так же, как регистр GDTR содержит 24-битовый физический адрес таблицы GDT и её предел, так и регистр IDTR содержит 24-битовый физический адрес дескрипторной таблицы прерываний IDT и её предел.
Регистр IDTR программа загружает перед переходом в защищённый режим, в функции protected_mode() модуля TOSSYST.ASM при помощи вызова функции set_int_ctrlr(), описанной в файле TOSSYST.ASM.
Для обработки особых ситуаций — исключений — разработчики процессора i80286 зарезервировали 31 номер прерывания. Каждому исключению соответствует одна из функций exception_XX() из модуля EXCEPT.C. Собственно, описав реакцию программы на каждое исключение можно обрабатывать любые ошибки защищенного режима. В моем случае достаточно завершать программу при возникновении любого исключения с выдачей на экран номера возникшего исключения. Поэтому функции exception_XX() просто вызывают prg_abort(), описанной там же, и передают ей номер возникшего исключения. Функция prg_abort() переключает процессор в реальный режим, выводит сообщение с данными возникшего исключения и завершает работу программы.
Теперь разберемся с аппаратными прерываниями, которые нас не интересуют в данной программе, однако это не мешает им происходить. Для этого в модуле INTPROC.C описаны две функции заглушки iret0() и iret1(), которые собственно ничего не делают кроме того, что выдают на контроллеры команды конца прерывания. Функция iret0() относится к первому контроллеру (Master), а вторая – ко второму (Slave).
 Неплохо было бы включить в программу поддержку программного прерывания 30h, чтобы можно было получать данные с клавиатуры. Это реализовано в модуле KEYBOARD.ASM, в функции Int_30h_Entry(). В IDT помещается вентиль программного прерывания, который вызывает данную функцию в момент прерывания 30h.
После запуска программа переходит в защищённый режим и размаскирует прерывания от таймера и клавиатуры. Далее она вызывает в цикле прерывание int 30h (ввод символа с клавиатуры), и выводит на экран скан-код нажатой клавиши и состояние переключающих клавиш (таких, как CapsLock, Ins, и т.д.). Если окажется нажатой клавиша ESC, программа выходит из цикла.

 Обработчик аппаратного прерывания клавиатуры — процедура с именем Keyb_int из модуля KEYBOARD.ASM. После прихода прерывания она выдаёт короткий звуковой сигнал (функция beep() из модуля TOSSYST.ASM), считывает и анализирует скан-код клавиши, вызвавшей прерывание. Скан-коды классифицируются на обычные и расширенные (для 101-клавишной клавиатуры). В отличие от прерывания BIOS INT 16h, мы для простоты не стали реализовывать очередь, а ограничились записью полученного скан-кода в глобальную ячейку памяти key_code. Причём прерывания, возникающие при отпускании клавиш, игнорируются.

Запись скан-кода в ячейку key_code выполняет процедура Keyb_PutQ() из модуля KEYBOARD.ASM. После записи эта процедура устанавливает признак того, что была нажата клавиша — записывает значение 0FFh в глобальную переменную key_flag.

Программное прерывание int 30h опрашивает состояние key_flag. Если этот флаг оказывается установленным, он сбрасывается, вслед за чем обработчик int 30h записывает в регистр AX скан-код нажатой клавиши, в регистр BX — состояние переключающих клавиш на момент нажатия клавиши, код которой передан в регистре AX.

 Ну и последнее, требующееся прерывание – это аппаратное прерывание таймера. Обработка этого прерывания реализована в функции Timer_int() модуля TIMER.C. Эта функция служит для переключения процессора между задачами. Более подробно я рассмотрю ее работу в следующей главе курсового проекта.
 Структура элемента дескрипторной таблицы прерываний IDT описана в файле tos.inc:
STRUC idtr_struc

 idt_len dw 0

 idt_low dw 0

 idt_hi db 0

 rsrv db 0

ENDS idtr_struc
3.5 Реализация мультизадачности.
 Я пошел в данном курсовом проекте самым простым способом – реализации мультизадачности через аппаратный таймер компьютера. Реализация более сложных алгоритмов явно тянет на дипломный проект.

 Как известно, таймер вырабатывает прерывание IRQ0 примерно 18,2 раза в секунду. Можно использовать данный факт для переключения между задачами, выделяя каждой квант времени. Я не буду здесь реализовывать механизм приоритетов задач. Все выполняемые задачи имеют равный приоритет.

 Для реализации разделения ресурсов компьютера между задачами и их взаимодействию друг с другом и средой исполнения (можно даже ее назвать операционной системой), я реализовал механизм семафоров.

 В моем случае семафор представляет собой ячейку памяти, отражающая текущее состояние ресурса — свободен или занят.

 Я иду еще на одно упрощение — не создаю здесь таблицы LDT для каждой задачи. Все-таки это не настоящая ОС, а ее так скажем, модель.

 Настоящие многозадачные ОС квантуют время не на уровне программы, а на уровне задачи, так как каждая программа может иметь несколько параллельно выполняющихся потоков. Я не буду здесь организовывать механизм потоков. Это, я думаю, простительно, так как он не реализован полностью даже в Linux. Буду исходить из предпосылки, что одна программа равна одной задаче.
3.5.1 Контекст задачи.

Для хранения контекста неактивной в настоящей момент задачи процессор i80286 использует специальную область памяти, называемую сегментом состояния задачи TSS (Task State Segment). Формат TSS представлен на рис. 4.

<img width=«180» height=«445» src=«ref-1_365050628-4511.coolpic» v:shapes="_x0000_i1029">

Рис. 4. Формат сегмента состояния задачи TSS.

Сегмент TSS адресуется процессором при помощи 16-битного регистра TR (Task Register), содержащего селектор дескриптора TSS, находящегося в глобальной таблице дескрипторов GDT (рис. 5).

<img width=«484» height=«143» src=«ref-1_365055139-3530.coolpic» v:shapes="_x0000_i1030">

Рис. 5. Дескриптор сегмента состояния задачи TSS.

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

Сегмент состояния задачи описан в файле tos.h:

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;
3.5.2 Переключение задач.
 В качестве способа переключения между задачами выберем команду JMP. Неудобство в этом случае представляет то, что если, к примеру, задача 1 вызвала задачу 2, то вернуться к задаче 2 можно только вызвав снова команду JMP и передав ей TSS задачи 1.

Реализация альтернативного метода через команду CALL позволяет создавать механизм вложенных вызовов задач, но выглядит гораздо более трудоемким и требует организации вентилей вызова задач.
Функция переключения задач называется jump_to_task() и реализована в модуле TOSSYST.ASM:
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
Переключение задач происходит в функции Timer_int() из модуля TIMER.C. Эта функция вызывается по прерыванию таймера. Выбор какая задача получит процессор в данный момент решает диспетчер задач, организованный как функция dispatcher(), описанная в модуле TIMER.C. Диспетчер работает по самому простому алгоритму – по кругу переключает процессор между задачами.

3.5.3 Разделение ресурсов.
 Разделение ресурсов для задач организовано в файле SEMAPHOR.C. Сам семафор представляет собой целое 2-х байтное число (int). В принципе можно было обойтись и одним битом, но это требует несколько более сложного кода.

 Так как операционная система у меня получается ну очень крошечная, я думаю будет достаточно предположить, что максимальное количество семафоров в системе будет равно 5. Поэтому в файле SEMAPHOR.C задан статический массив из 5 семафоров:
word semaphore[5];
 Работа задач с семафорами организуется при помощи 3-х функций:
sem_clear() – процедура сброса семафора,

sem_set() – процедура установки семафора,

sem_wait() – процедура ожидания семафора.
 
3.5.4 Задачи.
 Исполняющиеся задачи организованы как просто функции, в модуле TASKS.C.
Задача task1() выполняется единократно, после чего передает управление операционной системе.

Задачи task2() и flipflop_task() работают в бесконечных циклах, рисуя на экране двигающиеся линии, тем самым обозначая свою работу. Задача flipflop_task() работает с меньшим периодом и только тогда, когда установлен семафор 1.

Задача keyb_task() вводит символы с клавиатуры и отображает скан-коды нажатых клавиш, а также состояние переключающих клавиш на экране. Если нажимается клавиша ESC, задача устанавливает семафор номер 0. Работающая параллельно главная задача ожидает установку этого семафора. Как только семафор 0 окажется установлен, главная задача завершает свою работу и программа возвращает процессор в реальный режим, затем передаёт управление MS-DOS.
4. Полные исходные тексты программы.
4.1 Файл 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
4.2 Файл 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; // Зарезервированные16 бит

}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);
4.3 Файл TOS.H. Основной файл программы.
#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); }
    продолжение
--PAGE_BREAK--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
 // { (word)&Keyb_int, CODE_SELECTOR, 0, TYPE_INTERRUPT_GATE, 0 }, // 21
 // Вентиль задачи, запускающейся по прерыванию от клавиатуры
 {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; // номер текущей строки для вывода на экран
// -------------------------------------------

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

// -------------------------------------------
extern int getcpu(void);
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);
 // Запускаем планировщик задач
 vi_print(0, y++ ," Запущен планировщик задач", 0x70);

 enable_interrupt(); // разрешаем прерывание таймера
 // Ожидаем установки семафора с номером 0. После того,

 // как этот семафор окажется установлен, возвращаемся

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

 // клавиатуры, которая работает независимо от

 // главной задаче.
 vi_print(18, 24," Для возврата в реальный режим нажмите 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 всегда (для процессоров 286)

 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);

}
4.4 Файл TASKS.C. Содержит функции задач.
#include <stdio.h>

#include <dos.h>

#include <conio.h>

#include <stdlib.h>

#include <mem.h>

#include «tos.h»

#include «screen.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

long delay_cnt1 = 0l;

word flipflop1 = 0;

void task2(void)

{

 char Buf[B_SIZE + 1]; // Буфер вывода задачи 2

 static TLabel Label1;

 static TLabel Label2;
 memset(Buf, ' ', B_SIZE);

 Buf[B_SIZE] = 0;
 Label1.Pos = 0;

 Label1.Dir = 1;

 Buf[Label1.Pos] = '/';
 Label2.Pos = B_SIZE;

 Label2.Dir = 0;

 Buf[Label2.Pos] = '\\';
 vi_print(30, 15, «Работает задача 2:», 0x7f);
 while (1)

 {

 // Периодически выводим на экран движки,

 // каждый раз переключая

 // семафор номер 1. Этот семафор однозначно

 // соответствует выведенной на экран строке.

 asm sti

 if (delay_cnt1 > 150000l)

 {

 asm cli
 StepLabel(&Label1, &Label2, Buf);
 if (flipflop1)

 {

 vi_print(5, 16, Buf, 0x1f);

 sem_clear(1);

 }

 else

 {

 vi_print(5, 16, Buf, 0x1f);

 sem_set(1);

 }

 flipflop1 ^= 1;

 delay_cnt1 = 0l;

 asm sti

 }

 delay_cnt1++;

 }

}
word flipflop = 0;

long delay_cnt = 0l;
// Эта задача также периодически выводит на экран

// с меньшим периодом. Кроме того, эта задача

// работает только тогда, когда установлен

// семафорномер1.

void flipflop_task(void)

{

 char Buf[B_SIZE + 1]; // Буфер вывода задачи 2

 static TLabel Label1;

 static TLabel Label2;
 memset(Buf, ' ', B_SIZE);

 Buf[B_SIZE] = 0;
 Label1.Pos = 0;

 Label1.Dir = 1;

 Buf[Label1.Pos] = '/';
    продолжение
--PAGE_BREAK-- Label2.Pos = B_SIZE;

 Label2.Dir = 0;

 Buf[Label2.Pos] = '\\';
 vi_print(30, 12, «Работает задача 0:», 0x7f);
 while(1)

 {

 asm sti

 if (delay_cnt > 20000l )

 {

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

 asm cli

 StepLabel(&Label1, &Label2, Buf);

 vi_print(5, 13, Buf, 0x1f);

 flipflop ^= 1;

 delay_cnt = 0l;

 asm sti

 }

 delay_cnt++;

 }

}
word keyb_code;
extern word keyb_status;
// Эта задача вводит символы с клавиатуры

// и отображает скан-коды нажатых клавиш

// и состояние переключающих клавиш на экране.

// Если нажимается клавиша ESC, задача

// устанавливает семафор номер 0.

// Работающая параллельно главная задача

// ожидает установку этого семафора. Как только

// семафор 0 окажется установлен, главная задача

// завершает свою работу и программа возвращает

// процессор в реальный режим, затем передаёт

// управление MS-DOS.

void keyb_task(void)

{

 vi_print(32, 20, " Key code:… ", 0x20);

 vi_print(32, 21, " Key status:… ", 0x20);

 while(1)

 {

 keyb_code = kb_getch();

 vi_put_word(45, 20, keyb_code, 0x4f);

 vi_put_word(45, 21, keyb_status, 0x4f);

 if ((keyb_code & 0x00ff) == 1)

 sem_set(0);

 }

}
4.5 Файл 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

}
4.6 Файл TIMER.C. Процедуры для работы с таймером и диспетчер задач.
Cодержит обработчик аппаратного прерывания таймера, который периодически выдаёт звуковой сигнал и инициирует работу диспетчера задач. Диспетчер задач циклически перебирает селекторы TSS задач, участвующих в процессе разделения времени, возвращая селектор той задачи, которая должна стать активной. В самом конце обработки аппаратного прерывания таймера происходит переключение именно на эту задачу.
#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]);

}
4.7 Файл 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++,«ERROR!!! ---> Произошло исключение», 0xc);
 real_mode(); // Возвращаемся в реальный режим
 // В реальном режиме выводим сообщение об исключении
 gotoxy(1, ++y);

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

 getch();
 textcolor(WHITE);

 textbackground(BLACK);

 clrscr();

 exit(0);

}
4.8 Файл 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

 }

}
4.9 Файл 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);

}
4.10 Файл 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

    продолжение
--PAGE_BREAK--
еще рефераты
Еще работы по информатике