.

DOS-extender для компилятора Borland C++ 3.1

Язык: русский
Формат: реферат
Тип документа: Word Doc
78 1865
Скачать документ

Министерство образования Республики Беларусь Белорусский Государственный
Университет Информатики и Радиоэлектроники

Кафедра ЭВМ

Пояснительная записка

к курсовому проекту по курсу

“СПO ЭВМ”,

на тему:

“DOS-extender для компилятора Borland C++ 3.1, защищенный режим
процессора 80286, организация многозадачной работы процессора”

Выполнил:

студент группы 500501

Балахонов Е.В.

Минск 2000

Оглавление.

TOC \o “1-3” \h \z HYPERLINK \l “_Toc481841104” 1. Введение.
PAGEREF _Toc481841104 \h 2

HYPERLINK \l “_Toc481841105” 1.1 Уровни программной поддержки
защищенного режима. PAGEREF _Toc481841105 \h 2

HYPERLINK \l “_Toc481841106” 1.1.1 Интерфейс BIOS. PAGEREF
_Toc481841106 \h 2

HYPERLINK \l “_Toc481841107” 1.1.2 интерфейс драйвера HIMEM.SYS.
PAGEREF _Toc481841107 \h 2

HYPERLINK \l “_Toc481841108” 1.1.3 интерфейс EMS/VCPI. PAGEREF
_Toc481841108 \h 3

HYPERLINK \l “_Toc481841109” 1.1.4 интерфейс DPMI. PAGEREF
_Toc481841109 \h 3

HYPERLINK \l “_Toc481841110” 1.1.5 расширители DOS (DOS-экстендеры).
PAGEREF _Toc481841110 \h 3

HYPERLINK \l “_Toc481841111” 1.2 Текущее положение дел в мире
DOS-extender-ов. PAGEREF _Toc481841111 \h 4

HYPERLINK \l “_Toc481841112” 2. Обоснование выбора средств. PAGEREF
_Toc481841112 \h 4

HYPERLINK \l “_Toc481841113” 3. Реализация работы программы в
защищенном режиме процессора 80286. PAGEREF _Toc481841113 \h 5

HYPERLINK \l “_Toc481841114” 3.1 Адресация защищенного режима
процессора 80286. PAGEREF _Toc481841114 \h 5

HYPERLINK \l “_Toc481841115” 3.2 Переход в защищенный режим
процессора 80286 PAGEREF _Toc481841115 \h 8

HYPERLINK \l “_Toc481841116” 3.3 Возврат в реальный режим процессора.
PAGEREF _Toc481841116 \h 10

HYPERLINK \l “_Toc481841117” 3.4 Обработка прерываний в защищенном
режиме. PAGEREF _Toc481841117 \h 11

HYPERLINK \l “_Toc481841118” 3.5 Реализация мультизадачности.
PAGEREF _Toc481841118 \h 13

HYPERLINK \l “_Toc481841119” 3.5.1 Контекст задачи. PAGEREF
_Toc481841119 \h 14

HYPERLINK \l “_Toc481841120” 3.5.2 Переключение задач. PAGEREF
_Toc481841120 \h 15

HYPERLINK \l “_Toc481841121” 3.5.3 Разделение ресурсов. PAGEREF
_Toc481841121 \h 16

HYPERLINK \l “_Toc481841122” 3.5.4 Задачи. PAGEREF _Toc481841122 \h
16

HYPERLINK \l “_Toc481841123” 4. Полные исходные тексты программы.
PAGEREF _Toc481841123 \h 17

HYPERLINK \l “_Toc481841124” 4.1 Файл TOS.INC. Определение констант и
структур для модулей, составленных на языке ассемблера. PAGEREF
_Toc481841124 \h 17

HYPERLINK \l “_Toc481841125” 4.2 Файл TOS.H. Определение констант и
структур для модулей, составленных на языке Си. PAGEREF _Toc481841125
\h 18

HYPERLINK \l “_Toc481841126” 4.3 Файл TOS.H. Основной файл программы.
PAGEREF _Toc481841126 \h 19

HYPERLINK \l “_Toc481841127” 4.4 Файл TASKS.C. Содержит функции
задач. PAGEREF _Toc481841127 \h 24

HYPERLINK \l “_Toc481841128” 4.5 Файл SEMAPHOR.C. Содержит процедуры
для работы с семафорами. PAGEREF _Toc481841128 \h 26

HYPERLINK \l “_Toc481841129” 4.6 Файл TIMER.C. Процедуры для работы с
таймером и диспетчер задач. PAGEREF _Toc481841129 \h 27

HYPERLINK \l “_Toc481841130” 4.7 Файл EXCEPT.C. Обработка исключений.
PAGEREF _Toc481841130 \h 28

HYPERLINK \l “_Toc481841131” 4.8 Файл INTPROC.C. Заглушки для
аппаратных прерываний. PAGEREF _Toc481841131 \h 29

HYPERLINK \l “_Toc481841132” 4.9 Файл KEYB.C. Ввод символа с
клавиатуры. PAGEREF _Toc481841132 \h 30

HYPERLINK \l “_Toc481841133” 4.10 Файл KEYBOARD.ASM. Процедуры для
работы с клавиатурой. PAGEREF _Toc481841133 \h 30

HYPERLINK \l “_Toc481841134” 4.11 Файлы SCREEN.H и SCREEN.C – модуль
для работы с видеоадаптером. PAGEREF _Toc481841134 \h 34

HYPERLINK \l “_Toc481841135” 4.11.1 SCREEN.H PAGEREF _Toc481841135
\h 34

HYPERLINK \l “_Toc481841136” 4.11.2 SCREEN.C PAGEREF _Toc481841136
\h 34

HYPERLINK \l “_Toc481841137” 4.12 Файл TOSSYST.ASM. Процедуры для
инициализации, перехода в защищённый режим и возврата в реальный режим,
для загрузки регистра TR и переключения задач. PAGEREF _Toc481841137
\h 36

HYPERLINK \l “_Toc481841138” 5. Выводы. PAGEREF _Toc481841138 \h
41

HYPERLINK \l “_Toc481841139” 6. Литература. PAGEREF _Toc481841139
\h 41

1. Введение.

Операционная система MS DOS, не смотря на свое моральное устаревание,
все еще довольно часто находит применение на парке старых ПК, а значит,
все еще существует необходимость создания программ для нее.

К сожалению, написание программ в реальном режиме процессоров
архитектуры Intel x86 осложнено отсутствием возможности использовать в
программе оперативную память объемом свыше пресловутых 640 килобайт, а
реально свыше 500-620 килобайт. Это ограничение к сожалению преследует
MS DOS и аналогичные ей ОС других производителей, начиная с того
момента, как горячо любимый в околокомпьютерных кругах Билл Гейтс
заявил, что 640 килобайт достаточно для всех возможных задач ПК.
Преодоление барьера 640 килобайт в новых версиях MS DOS усложнялось
необходимостью совместимости с старыми программами, которые жизненно
необходимо было поддерживать. Программирование защищенного режима
процессора и расширенной памяти требовало от программистов недюжинных
знаний архитектуры процессоров Intel и достаточно трудоемкого
программирования.

1.1 Уровни программной поддержки защищенного режима.

Инженерная мысль не стоит на месте, особенно в такой области, как
программирование. Задача программной поддержки защищённого режима и
поддержки работы с расширенной памятью получила не одно, а сразу
несколько решений. Этими решениями стали так называемые уровни
программной поддержки защищённого режима и поддержки работы с
расширенной памятью:

интерфейс BIOS;

интерфейс драйвера HIMEM.SYS;

интерфейс EMS/VCPI;

интерфейс DPMI;

расширители DOS (DOS-экстендеры).

1.1.1 Интерфейс BIOS.

Интерфейсом самого низкого уровня является интерфейс BIOS,
предоставляемый программам в виде нескольких функций прерывания BIOS
INT 15h. Интерфейс BIOS позволяет программе перевести процессор из
реального режима в защищённый, переслать блок памяти из стандартной
памяти в расширенную или из расширенной в стандартную. Этим все его
возможности и ограничиваются. Интерфейс BIOS используется для старта
мультизадачных операционных систем защищённого режима (таких, как OS/2)
или в старых программах, работающих с расширенной памятью в защищённом
режиме (например, СУБД ORACLE версии 5.1).

1.1.2 интерфейс драйвера HIMEM.SYS.

С помощью функций, предоставляемых этим драйвером, программа может
выполнять различные действия с блоками расширенной памяти, а также
управлять адресной линией A20. Основное различие между способом работы с
расширенной памятью драйвера HIMEM.SYS и интерфейсом прерывания BIOS
INT 15h заключается в том, что первый выполняет выделение программе и
внутренний учёт блоков расширенной памяти, а второй рассматривает всю
расширенную память как один непрерывный участок. Однако драйвер
HIMEM.SYS не открывает для программ доступ к защищённому режиму. Он
полностью работает в реальном режиме, а для обращения к расширенной
памяти использует либо недокументированную машинную команду LOADALL
(если используется процессор 80286), либо возможности процессора 80386,
который позволяет адресовать расширенную память в реальном режиме (при
соответствующей инициализации системных регистров и таблиц).

1.1.3 интерфейс EMS/VCPI.

Используя трансляцию страниц, некоторые драйверы памяти (например,
EMM386 или QEMM) могут эмулировать присутствие дополнительной памяти,
используя расширенную память. При этом стандартный набор функций
управления дополнительной памятью, реализованный в рамках прерывания
INT 67h, дополнен еще несколькими функциями для работы в защищённом
режиме процессора. Эти новые функции реализуют интерфейс виртуальной
управляющей программы VCPI (Virtual Control Programm Interface). Они
позволяют устанавливать защищённый и виртуальный режимы работы
процессора, работать с расширенной памятью на уровне страниц и
устанавливать специальные отладочные регистры процессора i80386.
Интерфейс VCPI облегчает использование механизма трансляции страниц,
освобождая программиста от необходимости работать с системными
регистрами процессора.

1.1.4 интерфейс DPMI.

Интерфейс DPMI (DOS Protected Mode Interface – интерфейс защищённого
режима для DOS) реализуется модулем, называющимся сервером DPMI. Этот
интерфейс доступен для тех программ, которые работают на виртуальной
машине WINDOWS или OS/2 версии 2.0 (позже мы обсудим некоторые детали,
связанные с использованием интерфейса DPMI в WINDOWS). Интерфейс DPMI
предоставляет полный набор функций для создания однозадачных программ,
работающих в защищённом режиме. В этом интерфейсе имеются функции для
переключения из реального режима в защищённый и обратно, для работы с
локальной таблицей дескрипторов LDT, для работы с расширенной и
стандартной памятью на уровне страниц, для работы с прерываниями (в том
числе для вызова прерываний реального режима из защищённого режима), для
работы с отладочными регистрами процессора i80386. Это наиболее развитый
интерфейс из всех рассмотренных ранее.

1.1.5 расширители DOS (DOS-экстендеры).

Последний, самый высокий уровень программной поддержки защищённого
режима – расширители DOS или DOS-экстендеры (DOS-extender). Они
поставляются, как правило, вместе со средствами разработки программ
(трансляторами) в виде библиотек и компонуются вместе с создаваемой
программой в единый загрузочный модуль. DOS-экстендеры значительно
облегчают использование защищённого режима и расширенной памяти в
программах, предназначенных для запуска из среды MS-DOS. Программы,
составленные с использованием DOS-экстендеров, внешне очень похожи на
обычные программы MS-DOS, однако они получают управление, когда
процессор уже находится в защищённом режиме. К формируемому с помощью
DOS-экстендера загрузочному модулю добавляются процедуры, необходимые
для инициализации защищённого режима. Эти процедуры первыми получают
управление и выполняют начальную инициализацию таблиц GDT, LDT, IDT,
содержат обработчики прерываний и исключений, систему управления
виртуальной памятью и т.д.

1.2 Текущее положение дел в мире DOS-extender-ов.

Еще несколько лет назад целые фирмы зарабатывали себе на существование
созданием различных модификаций DOS extender-ов. Например довольно
известный externder фирмы Phar Lap. После перехода большинства
пользователей в среду Win32 необходимость в DOS extender-ах резко
сократилась и большинство таких фирм, не сумев сориентироваться в
изменившихся условиях, прекратили свое существование.

Многие фирмы, разрабатывавшие компиляторы для DOS, включали в поставку
своих сред программирования DOS-extender-ы собственной разработки. Таким
примером может служить фирма Borland (ныне подразделение фирмы Corel) с
ее Borland Pascal, Borland C++ и расширителем DOS RTM.

В данный момент доступно несколько DOS-extender-ов по свободной
лицензии, которые могут использоваться кем угодно для любых целей. И это
понятно, денег на них сейчас не заработаешь.

Примеры таких программ:

ZRDX by Sergey Belyakov ( HYPERLINK “http://www.zrdx.da.ru”
http://www.zrdx.da.ru )

Маленький и функциональный DOS-extender для Watcom C++ и 32-х битных
исполняемых файлов формата OS/2 LE. Используется в коммерческих
программах, таких как антивирус AVP для DOS32.

WDOSX by Michael Tippach ( HYPERLINK “http://www.wuschel.demon.co.uk”
http://www.wuschel.demon.co.uk )

Самый впечатливший меня DOS-extender. Список поддерживаемых функций
просто поражает. Поддерживает все распространенные среды
программирования: Visual C++ 4 и позже, Borland C++ 4 и позже, Delphi 2
и позже. При желании никто не запрещает использовать Assembler.

2. Обоснование выбора средств.

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

В настоящий момент науке известны всего один DOS-extender для Borland
C++ 3.1. Это программа фирмы Phar Lap, не имеющая собственного названия.
Фирмы, к сожалению, давно уже нет, как и исходных текстов этого
DOS-extender-а. В него входил собственная программа – линкер и набор
специальных библиотек функций специально для Borland C++ 3.1, которой и
проводилась окончательная сборка EXE-файла.

Написание собственной среды разработки, вроде программ-линкеров и
собственных трансляторов языка Ассемблера явно выходит за переделы
данного курсового проекта. Поэтому остановимся на разработке набора
функций, позволяющих:

реализовать защищенный режим процессора 80286,

адресовать до 16 Мб памяти,

обрабатывать прерывания реального режима DOS

реализуем набор средств для создания параллельно выполняющихся потоков в
среде DOS.

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

Итак, согласно заданию буду пользоваться следующими средствами
разработки:

Borland C++ 3.1

Borland Turbo Assembler из поставки Borland C++ 3.1

3. Реализация работы программы в защищенном режиме процессора 80286.

3.1 Адресация защищенного режима процессора 80286.

Логический адрес в защищённом режиме (иногда используется термин
“виртуальный адрес”) состоит из двух 16-разрядных компонент – селектора
и смещения. Селектор записывается в те же сегментные регистры, что и
сегментный адрес в реальном режиме. Однако преобразование логического
адреса в физический выполняется не простым сложением со сдвигом, а при
помощи специальных таблиц преобразования адресов.

В первом приближении можно считать, что для процессора i80286 селектор
является индексом в таблице, содержащей базовые 24-разрядные физические
адреса сегментов. В процессе преобразования логического адреса в
физический процессор прибавляет к базовому 24-разрядному адресу
16-разрядное смещение, т.е. вторую компоненту логического адреса (Рис.
1).

Такая схема формирования физического адреса позволяет непосредственно
адресовать 16 мегабайт памяти с помощью 16-разрядных компонент
логического адреса.

Таблиц дескрипторов в системе обычно присутствует от одной до нескольких
десятков. Но всегда существует так называемая таблица GDT (Global
Descriptor Table), в которой обычно хранится описание сегментов самой
операционной системы защищенного режима 80286. Таблицы LDT (Local
Descriptor Table) создаются на каждый новый запускаемый процесс в
операционной системе, и в них хранится описание сегментов только одной
отдельной задачи.

Таблица дескрипторов – это просто таблица преобразования адресов,
содержащая базовые 24-разрядные физические адреса сегментов и некоторую
другую информацию. То есть каждый элемент таблицы дескрипторов
(дескриптор) содержит 24-разрядный базовый адрес сегмента и другую
информацию, описывающую сегмент.

Процессор 80286 имеет специальный 5-байтный регистр защищенного режима
GDTR, в котором старшие 3 байта содержат 24-разрядный физический адрес
таблицы GDT, младшие два байта – длину таблицы GDT, уменьшенную на 1.

Рис. 1. Схема преобразования логического адреса в физический в
защищенном режиме процессора 80286.

Перед переходом в защищённый режим программа должна создать в
оперативной памяти таблицу GDT и загрузить регистр GDTR при помощи
специальной команды LGDT.

Каждый элемент таблицы дескрипторов имеет следующий формат:

Общая его длина составляет 8 байт, в которых расположены следующие поля:

поле базового адреса длиной 24 бита содержит физический адрес сегмента,
описываемого данным дескриптором;

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

поле доступа описывает тип сегмента (сегмент кода, сегмент данных и
др.);

зарезервированное поле длиной 16 бит для процессора i80286 должно
содержать нули, это поле используется процессорами i80386 и i80486 (там,
в частности, хранится старший байт 32-разрядного базового адреса
сегмента).

Поле доступа, занимающее в дескрипторе один байт (байт доступа) служит
для классификации дескрипторов. На рис. 2 приведены форматы поля доступа
для трёх типов дескрипторов – дескрипторов сегментов кода, сегментов
данных и системных.

Рис. 2. Форматы поля доступа дескриптора.

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

Биты P и A предназначены для организации виртуальной памяти. Их
назначение будет описано в разделе, посвящённом виртуальной памяти.
Сейчас отметим, что бит P называется битом присутствия сегмента в
памяти. Для тех сегментов, которые находятся в физической памяти (мы
будем иметь дело в основном с такими сегментами) этот бит должен быть
установлен в 1.

Любая попытка программы обратиться к сегменту памяти, в дескрипторе
которого бит P установлен в 0, приведёт к прерыванию.

Бит A называется битом обращения к сегменту и для всех наших программ
должен быть установлен в 0.

Поле доступа дескриптора сегмента данных имеет битовые поля W и D. Поле
W называется битом разрешения записи в сегмент. Если этот бит установлен
в 1, наряду с чтением возможна и запись в данный сегмент. В противном
случае при попытке чтения выполнение программы будет прервано.

Поле D задаёт направление расширения сегмента. Обычный сегмент данных
расширяется в область старших адресов (расширение вверх). Если же в
сегменте расположен стек, расширение происходит в обратном направлении –
в область младших адресов (расширение вниз). Для сегментов, в которых
организуются стеки, необходимо устанавливать поле D равным 1.

Рассмотрим, как таблица дескрипторов будет выглядеть на языке
программирования C. (В дальнейшем где это только возможно будем
применять язык С, а Ассемблер – только там, где это необходимо.):

typedef struct descriptor

{

word limit; // Предел (размер сегмента в байтах)

word base_lo; // Базовый адрес сегмента (младшее слово)

unsigned char base_hi; // Базовый адрес сегмента (старший байт)

unsigned char type_dpl; // Поле доступа дескриптора

unsigned reserved; // Зарезервированные 16 бит

} descriptor;

Данная структура описана в файле tos.h.

Инициализацию экземпляра такой структуры можно произвести при помощи
функции, подобной функции init_gdt_descriptor, описанной в файле tos.c:

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;

}

Например, запись в третий по счёту элемент GDT информации о сегменте
данных с сегментным адресом _DS и пределом 0xffff будет выглядеть так:

init_gdt_descriptor(&gdt[2], MK_LIN_ADDR(_DS, 0), 0xffffL,

TYPE_DATA_DESCR | SEG_PRESENT_BIT |
SEG_WRITABLE);

Макрос MK_LIN_ADDR определен в файле tos.h и служит для преобразования
адреса реального режима формата сегмент:смещение в физический адрес:

#define MK_LIN_ADDR(seg,off) (((unsigned long)(seg))

#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

// { (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)&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

#include

#include

#include

#include

#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] = ‘/’;

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

#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

}

4.6 Файл TIMER.C. Процедуры для работы с таймером и диспетчер задач.

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

#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

#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++,”ERROR!!! —> Произошло исключение”, 0xc);

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

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

gotoxy(1, ++y);

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

getch();

textcolor(WHITE);

textbackground(BLACK);

clrscr();

exit(0);

}

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

}

}

4.9 Файл KEYB.C. Ввод символа с клавиатуры.

#include

#include

#include

#include

#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

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

4.11 Файлы SCREEN.H и SCREEN.C – модуль для работы с видеоадаптером.

4.11.1 SCREEN.H

#ifndef SCREEN_H

#define SCREEN_H

// Границы перемещения бегунков

#define B_SIZE 70

// Структура, описывающая бегунок

typedef struct _TLabel

{

char Pos; // Позиция бегунка

char Dir; // Направление движения

} TLabel;

extern void StepLabel(TLabel* Label1, TLabel* Label2, char* Buf);

#endif

4.11.2 SCREEN.C

#include

#include

#include

#include

#include “tos.h”

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

” Threads for DOS, ”

” Version 0.1/i286, Copyright (c) 2000 Eugeny Balahonov “,
0x30);

}

// Вывод бегущей строки

void StepLabel(TLabel* Label1, TLabel* Label2, char* Buf)

{

// Стираем символы меток

Buf[Label1->Pos] = ‘ ‘;

Buf[Label2->Pos] = ‘ ‘;

// Если двигаемся налево

if (Label1->Dir == 0)

{

// Если не дошли до крайней левой позиции

if (Label1->Pos > 0)

{

Label1->Pos–;

Buf[Label1->Pos] = ‘\\’;

}

else

{

Label1->Dir = 1;

Buf[Label1->Pos] = ‘/’;

}

}

// Если двигаемся направо

else

{

// Если не дошли до крайней правой позиции

if (Label1->Pos Pos++;

Buf[Label1->Pos] = ‘/’;

}

else

{

Label1->Dir = 0;

Buf[Label1->Pos] = ‘\\’;

}

}

// Если двигаемся налево

if (Label2->Dir == 0)

{

// Если не дошли до крайней левой позиции

if (Label2->Pos > 0)

{

Label2->Pos–;

Buf[Label2->Pos] = ‘\\’;

}

else

{

Label2->Dir = 1;

Buf[Label2->Pos] = ‘/’;

}

}

// Если двигаемся направо

else

{

// Если не дошли до крайней правой позиции

if (Label2->Pos Pos++;

Buf[Label2->Pos] = ‘/’;

}

else

{

Label2->Dir = 0;

Buf[Label2->Pos] = ‘\\’;

}

}

}

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

IDEAL

MODEL SMALL

RADIX 16

P286

DATASEG

include “tos.inc”

PUBLIC _beep

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

idtr idtr_struc

; Область памяти для инициализации 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

5. Выводы.

Процессоры семейства Intel x86 реализуют необходимые средства для
организации мультизадачных ОС с разделением адресного пространства и
виртуальной памяти.

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

6. Литература.

«Защищенный режим процессоров Intel 80286/80386/80486» © Александр
Фролов, Григорий Фролов Том 6, М.: Диалог-МИФИ, 1993, 234 стр.

«MS-DOS для программиста» © Александр Фролов, Григорий Фролов

Том 18, часть 1, М.: Диалог-МИФИ, 1995, 254 стр.

«MS-DOS для программиста» © Александр Фролов, Григорий Фролов

Том 19, часть 2, М.: Диалог-МИФИ, 1995, 253 стр.

«Язык Ассемблера для IBM PC и программирования» © Питер Абель, М.:
«Высшая школа», Москва, 1992, 444 стр.

«Язык программирования для персонального компьютера Си», © С.О. Бочков,
Д.М. Субботин, М.: Диалог-МИФИ, 1990, 383 стр.

Материалы WEB-сервера для разработчиков фирмы Intel HYPERLINK
“http://developer.intel.com” http://developer.intel.com

PAGE

PAGE 1

Нашли опечатку? Выделите и нажмите CTRL+Enter

Похожие документы
Обсуждение

Ответить

Курсовые, Дипломы, Рефераты на заказ в кратчайшие сроки
Заказать реферат!
UkrReferat.com. Всі права захищені. 2000-2020