DOS-extender для компилятора Borland C++ 3.1
DOS-extender для компилятора Borland C++ 3.1
Министерство образования Республики Беларусь Белорусский Государственный
Университет Информатики и Радиоэлектроники
Кафедра ЭВМ
Пояснительная записка
к курсовому проекту по курсу
"СПO ЭВМ",
на тему:
"DOS-extender для компилятора Borland C++ 3.1, защищенный режим
процессора 80286, организация многозадачной работы процессора”
Выполнил:
студент группы 500501
Балахонов Е.В.
Минск 2000
Оглавление.
1. Введение. 2
1.1 Уровни программной поддержки защищенного режима. 2
1.1.1 Интерфейс BIOS. 2
1.1.2 интерфейс драйвера HIMEM.SYS. 2
1.1.3 интерфейс EMS/VCPI. 3
1.1.4 интерфейс DPMI. 3
1.1.5 расширители DOS (DOS-экстендеры). 3
1.2 Текущее положение дел в мире DOS-extender-ов. 4
2. Обоснование выбора средств. 4
3. Реализация работы программы в защищенном режиме процессора 80286. 5
3.1 Адресация защищенного режима процессора 80286. 5
3.2 Переход в защищенный режим процессора 80286 8
3.3 Возврат в реальный режим процессора. 10
3.4 Обработка прерываний в защищенном режиме. 11
3.5 Реализация мультизадачности. 13
3.5.1 Контекст задачи. 14
3.5.2 Переключение задач. 15
3.5.3 Разделение ресурсов. 16
3.5.4 Задачи. 16
4. Полные исходные тексты программы. 17
4.1 Файл TOS.INC. Определение констант и структур для модулей,
составленных на языке ассемблера. 17
4.2 Файл TOS.H. Определение констант и структур для модулей, составленных
на языке Си. 18
4.3 Файл TOS.H. Основной файл программы. 19
4.4 Файл TASKS.C. Содержит функции задач. 24
4.5 Файл SEMAPHOR.C. Содержит процедуры для работы с семафорами. 26
4.6 Файл TIMER.C. Процедуры для работы с таймером и диспетчер задач. 27
4.7 Файл EXCEPT.C. Обработка исключений. 28
4.8 Файл INTPROC.C. Заглушки для аппаратных прерываний. 29
4.9 Файл KEYB.C. Ввод символа с клавиатуры. 30
4.10 Файл KEYBOARD.ASM. Процедуры для работы с клавиатурой. 30
4.11 Файлы SCREEN.H и SCREEN.C – модуль для работы с видеоадаптером. 34
4.11.1 SCREEN.H 34
4.11.2 SCREEN.C 34
4.12 Файл TOSSYST.ASM. Процедуры для инициализации, перехода в защищённый
режим и возврата в реальный режим, для загрузки регистра TR и переключения
задач. 36
5. Выводы. 41
6. Литература. 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 (http://www.zrdx.da.ru)
Маленький и функциональный DOS-extender для Watcom C++ и 32-х
битных исполняемых файлов формата OS/2 LE. Используется в
коммерческих программах, таких как антивирус AVP для DOS32.
- WDOSX by Michael Tippach (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.
[pic]
Рис. 1. Схема преобразования логического адреса в физический в
защищенном режиме процессора 80286.
Перед переходом в защищённый режим программа должна создать в
оперативной памяти таблицу GDT и загрузить регистр GDTR при помощи
специальной команды LGDT.
Каждый элемент таблицы дескрипторов имеет следующий формат:
[pic]
Общая его длина составляет 8 байт, в которых расположены следующие
поля:
. поле базового адреса длиной 24 бита содержит физический адрес
сегмента, описываемого данным дескриптором;
. поле предела содержит размер сегмента в байтах, уменьшенный на
единицу;
. поле доступа описывает тип сегмента (сегмент кода, сегмент данных и
др.);
. зарезервированное поле длиной 16 бит для процессора i80286 должно
содержать нули, это поле используется процессорами i80386 и i80486
(там, в частности, хранится старший байт 32-разрядного базового адреса
сегмента).
Поле доступа, занимающее в дескрипторе один байт (байт доступа) служит
для классификации дескрипторов. На рис. 2 приведены форматы поля доступа
для трёх типов дескрипторов - дескрипторов сегментов кода, сегментов данных
и системных.
[pic]
Рис. 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)&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
#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 < max_task)
current_task++;
else
current_task = 0;
return(task_list[current_task]);
}
4.7 Файл EXCEPT.C. Обработка исключений.
#include
#include
#include
#include
#include "tos.h"
void prg_abort(int err);
// Номер текущей строки для вывода на экран
extern unsigned int y;
// Обработчики исключений
void exception_0(void) { prg_abort(0); }
void exception_1(void) { prg_abort(1); }
void exception_2(void) { prg_abort(2); }
void exception_3(void) { prg_abort(3); }
void exception_4(void) { prg_abort(4); }
void exception_5(void) { prg_abort(5); }
void exception_6(void) { prg_abort(6); }
void exception_7(void) { prg_abort(7); }
void exception_8(void) { prg_abort(8); }
void exception_9(void) { prg_abort(9); }
void exception_A(void) { prg_abort(0xA); }
void exception_B(void) { prg_abort(0xB); }
void exception_C(void) { prg_abort(0xC); }
void exception_D(void) { prg_abort(0xD); }
void exception_E(void) { prg_abort(0xE); }
void exception_F(void) { prg_abort(0xF); }
void exception_10(void) { prg_abort(0x10); }
void exception_11(void) { prg_abort(0x11); }
void exception_12(void) { prg_abort(0x12); }
void exception_13(void) { prg_abort(0x13); }
void exception_14(void) { prg_abort(0x14); }
void exception_15(void) { prg_abort(0x15); }
void exception_16(void) { prg_abort(0x16); }
void exception_17(void) { prg_abort(0x17); }
void exception_18(void) { prg_abort(0x18); }
void exception_19(void) { prg_abort(0x19); }
void exception_1A(void) { prg_abort(0x1A); }
void exception_1B(void) { prg_abort(0x1B); }
void exception_1C(void) { prg_abort(0x1C); }
void exception_1D(void) { prg_abort(0x1D); }
void exception_1E(void) { prg_abort(0x1E); }
void exception_1F(void) { prg_abort(0x1F); }
// ------------------------------
// Аварийный выход из программы
// ------------------------------
void prg_abort(int err)
{
vi_print(1, y++,"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 < B_SIZE)
{
Label1->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 < B_SIZE)
{
Label2->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. Литература.
1. «Защищенный режим процессоров Intel 80286/80386/80486» © Александр
Фролов, Григорий Фролов Том 6, М.: Диалог-МИФИ, 1993, 234 стр.
2. «MS-DOS для программиста» © Александр Фролов, Григорий Фролов
Том 18, часть 1, М.: Диалог-МИФИ, 1995, 254 стр.
3. «MS-DOS для программиста» © Александр Фролов, Григорий Фролов
Том 19, часть 2, М.: Диалог-МИФИ, 1995, 253 стр.
4. «Язык Ассемблера для IBM PC и программирования» © Питер Абель, М.:
«Высшая школа», Москва, 1992, 444 стр.
5. «Язык программирования для персонального компьютера Си», © С.О.
Бочков, Д.М. Субботин, М.: Диалог-МИФИ, 1990, 383 стр.
6. Материалы WEB-сервера для разработчиков фирмы Intel
http://developer.intel.com