Чтение RSS
Рефераты:
 
Рефераты бесплатно
 

 

 

 

 

 

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

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

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

WDOSX by Michael Tippach

Самый впечатливший меня 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))<<4)+(word)(off)

Специальный регистр процессора 286 LDTR имеет длину 16 разрядов и содержит селектор дескриптора, описывающего текущую таблицу LDT.

В данном курсовом проекте я не использую регистр LDTR и не создаю таблицы LDT, в моем варианте достаточно обойтись только одним кольцом защиты (0) процессора и только таблицей GDT.

3.2 Переход в защищенный режим процессора 80286

При переходе в защищенный режим программа совершает следующие операции:

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

Для обеспечения возможности возврата из защищённого режима в реальный записывает адрес возврата в реальный режим в область данных BIOS по адресу 0040h:0067h, а также пишет в CMOS-память в ячейку 0Fh код 5. Этот код обеспечит после выполнения сброса процессора передачу управления по адресу, подготовленному нами в области данных BIOS по адресу 0040h:0067h.

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

Открывает адресную линию A20 (попробуем оперировать блоками памяти выше 1 Мб).

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

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

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

Необходимые функции для этого реализованы в файлах tos.c и TOSSYST.ASM:

Подготовка GDT осуществляется при помощи описанных выше функции init_gdt_descriptor() и макроса MK_LIN_ADDR().

Остальные действия, необходимые для перехода в защищенный режим, описаны в функции protected_mode() модуля TOSSYST.ASM:

Обеспечение возможности возврата в реальный режим:

push ds ; готовим адрес возврата

mov ax,40h ; из защищённого режима

mov ds,ax

mov [WORD 67h],OFFSET shutdown_return

mov [WORD 69h],cs

pop ds

Запрет прерываний:

сli

in al, INT_MASK_PORT

and al, 0ffh

out INT_MASK_PORT, al

mov al,8f

out CMOS_PORT,al

Открытие линии A20 производится вызовом функции enable_a20(), описанной в файле TOSSYST.ASM:

PROC enable_a20 NEAR

mov al,A20_PORT

out STATUS_PORT,al

mov al,A20_ON

out KBD_PORT_A,al

ret

ENDP enable_a20

Запоминаем содержимое сегментных регистров SS и ES:

mov [real_ss],ss

mov [real_es],es

Программируем при помощи функции set_int_ctrlr(), описанной в файле TOSSYST.ASM каскад контроллеров прерываний (Master и Slave) для работы в защищенном режиме (описание работы прерываний в защищенном режиме приведено ниже):

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

 

3.3 Возврат в реальный режим процессора.

Для того, чтобы вернуть процессор 80286 из защищённого режима в реальный, необходимо выполнить аппаратный сброс (отключение) процессора. Это реализуется в функции real_mode(), описанной в файле TOSSYST.ASM:

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

Функция disable_a20(), описанная в файле TOSSYST.ASM закрывает адресную линию 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

 

3.4 Обработка прерываний в защищенном режиме.

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

Таблица прерываний защищённого режима называется дескрипторной таблицей прерываний IDT (Interrupt Descriptor Table). Также как и таблицы GDT и LDT, таблица IDT содержит 8-байтовые дескрипторы. Причём это системные дескрипторы - вентили прерываний, исключений и задач. Поле TYPE вентиля прерывания содержит значение 6, а вентиля исключения - значение 7.

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

Расположение определяется содержимым 5-байтового внутреннего регистра процессора IDTR. Формат регистра IDTR полностью аналогичен формату регистра GDTR, для его загрузки используется команда LIDT. Так же, как регистр GDTR содержит 24-битовый физический адрес таблицы GDT и её предел, так и регистр IDTR содержит 24-битовый физический адрес дескрипторной таблицы прерываний IDT и её предел.

Регистр IDTR программа загружает перед переходом в защищённый режим, в функции protected_mode() модуля TOSSYST.ASM при помощи вызова функции set_int_ctrlr(), описанной в файле TOSSYST.ASM.

Для обработки особых ситуаций - исключений - разработчики процессора i80286 зарезервировали 31 номер прерывания. Каждому исключению соответствует одна из функций exception_XX() из модуля EXCEPT.C. Собственно, описав реакцию программы на каждое исключение можно обрабатывать любые ошибки защищенного режима. В моем случае достаточно завершать программу при возникновении любого исключения с выдачей на экран номера возникшего исключения. Поэтому функции exception_XX() просто вызывают prg_abort(), описанной там же, и передают ей номер возникшего исключения. Функция prg_abort() переключает процессор в реальный режим, выводит сообщение с данными возникшего исключения и завершает работу программы.

Теперь разберемся с аппаратными прерываниями, которые нас не интересуют в данной программе, однако это не мешает им происходить. Для этого в модуле INTPROC.C описаны две функции заглушки iret0() и iret1(), которые собственно ничего не делают кроме того, что выдают на контроллеры команды конца прерывания. Функция iret0() относится к первому контроллеру (Master), а вторая – ко второму (Slave).

Неплохо было бы включить в программу поддержку программного прерывания 30h, чтобы можно было получать данные с клавиатуры. Это реализовано в модуле KEYBOARD.ASM, в функции Int_30h_Entry(). В IDT помещается вентиль программного прерывания, который вызывает данную функцию в момент прерывания 30h.

После запуска программа переходит в защищённый режим и размаскирует прерывания от таймера и клавиатуры. Далее она вызывает в цикле прерывание int 30h (ввод символа с клавиатуры), и выводит на экран скан-код нажатой клавиши и состояние переключающих клавиш (таких, как CapsLock, Ins, и т.д.). Если окажется нажатой клавиша ESC, программа выходит из цикла.

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

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

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

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

Структура элемента дескрипторной таблицы прерываний IDT описана в файле tos.inc:

STRUC idtr_struc

idt_len dw 0

idt_low dw 0

idt_hi db 0

rsrv db 0

ENDS idtr_struc

3.5 Реализация мультизадачности.

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

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

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

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

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

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

3.5.1 Контекст задачи.

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

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

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

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

typedef struct tss

{

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

word sp0; // указатель стека кольца 0

word ss0;

word sp1; // указатель стека кольца 1

word ss1;

word sp2; // указатель стека кольца 1

word ss2;

word ip; // регистры процессора

word flags;

word ax;

word cx;

word dx;

word bx;

word sp;

word bp;

word si;

word di;

word es;

word cs;

word ss;

word ds;

word ldtr;

} tss;

3.5.2 Переключение задач.

В качестве способа переключения между задачами выберем команду JMP. Неудобство в этом случае представляет то, что если, к примеру, задача 1 вызвала задачу 2, то вернуться к задаче 2 можно только вызвав снова команду JMP и передав ей TSS задачи 1.

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

Функция переключения задач называется jump_to_task() и реализована в модуле TOSSYST.ASM:

PROC _jump_to_task NEAR

push bp

mov bp,sp

mov ax,[bp+4] ; получаем селектор

; новой задачи

mov [new_select],ax ; запоминаем его

jmp [DWORD new_task] ; переключаемся на

; новую задачу

pop bp

ret

ENDP _jump_to_task

Переключение задач происходит в функции Timer_int() из модуля TIMER.C. Эта функция вызывается по прерыванию таймера. Выбор какая задача получит процессор в данный момент решает диспетчер задач, организованный как функция dispatcher(), описанная в модуле TIMER.C. Диспетчер работает по самому простому алгоритму – по кругу переключает процессор между задачами.

3.5.3 Разделение ресурсов.

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

Так как операционная система у меня получается ну очень крошечная, я думаю будет достаточно предположить, что максимальное количество семафоров в системе будет равно 5. Поэтому в файле SEMAPHOR.C задан статический массив из 5 семафоров:

word semaphore[5];

Работа задач с семафорами организуется при помощи 3-х функций:

sem_clear() – процедура сброса семафора,

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

sem_wait() – процедура ожидания семафора.

3.5.4 Задачи.

Исполняющиеся задачи организованы как просто функции, в модуле TASKS.C.

Задача task1() выполняется единократно, после чего передает управление операционной системе.

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

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

4. Полные исходные тексты программы. >

4.1 Файл TOS.INC. Определение констант и структур для модулей, составленных на языке ассемблера.

CMOS_PORT equ 70h

PORT_6845 equ 63h

COLOR_PORT equ 3d4h

MONO_PORT equ 3b4h

STATUS_PORT equ 64h

SHUT_DOWN equ 0feh

INT_MASK_PORT equ 21h

VIRTUAL_MODE equ 0001

A20_PORT equ 0d1h

A20_ON equ 0dfh

A20_OFF equ 0ddh

EOI equ 20h

MASTER8259A equ 20h

SLAVE8259A equ 0a0h

KBD_PORT_A equ 60h

KBD_PORT_B equ 61h

L_SHIFT equ 0000000000000001b

NL_SHIFT equ 1111111111111110b

R_SHIFT equ 0000000000000010b

NR_SHIFT equ 1111111111111101b

L_CTRL equ 0000000000000100b

NL_CTRL equ 1111111111111011b

R_CTRL equ 0000000000001000b

NR_CTRL equ 1111111111110111b

L_ALT equ 0000000000010000b

NL_ALT equ 1111111111101111b

R_ALT equ 0000000000100000b

NR_ALT equ 1111111111011111b

CAPS_LOCK equ 0000000001000000b

SCR_LOCK equ 0000000010000000b

NUM_LOCK equ 0000000100000000b

INSERT equ 0000001000000000b

 

STRUC idtr_struc

idt_len dw 0

idt_low dw 0

idt_hi db 0

rsrv db 0

ENDS idtr_struc

 

 

4.2 Файл TOS.H. Определение констант и структур для модулей, составленных на языке Си.

#define word unsigned int

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

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

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

#define TASK_1_SELECTOR 0x18 // задача TASK_1

#define TASK_2_SELECTOR 0x20 // задача TASK_2

#define MAIN_TASK_SELECTOR 0x28 // главная задача

#define VID_MEM_SELECTOR 0x30 // сегмент видеопамяти

#define IDT_SELECTOR 0x38 // талица IDT

#define KEYBIN_TASK_SELECTOR 0x40 // задача ввода с клавиатуры

#define KEYB_TASK_SELECTOR 0x48 // задача обработки

// клавиатурного прерывания

#define FLIP_TASK_SELECTOR 0x50 // задача FLIP_TASK

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

typedef struct

{

unsigned accessed : 1;

unsigned read_write : 1;

unsigned conf_exp : 1;

unsigned code : 1;

unsigned xsystem : 1;

unsigned dpl : 2;

unsigned present : 1;

} ACCESS;

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

typedef struct descriptor

{

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

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

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

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

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

} descriptor;

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

// исключения

typedef struct gate

{

word offset;

word selector;

unsigned char count;

unsigned char type_dpl;

word reserved;

} gate;

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

typedef struct tss

{

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

word sp0; // указатель стека кольца 0

word ss0;

word sp1; // указатель стека кольца 1

word ss1;

word sp2; // указатель стека кольца 1

word ss2;

word ip; // регистры процессора

word flags;

word ax;

word cx;

word dx;

word bx;

word sp;

word bp;

word si;

word di;

word es;

word cs;

word ss;

word ds;

word ldtr;

} tss;

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

#define TSS_SIZE (sizeof(tss))

#define DESCRIPTOR_SIZE (sizeof(descriptor))

#define GATE_SIZE (sizeof(gate))

#define IDT_SIZE (sizeof(idt))

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

// и монохромного видеоадаптеров

#define COLOR_VID_MEM 0xb8000L

#define MONO_VID_MEM 0xb0000L

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

#define MONO_MODE 0x07 // монохромный

#define BW_80_MODE 0x02 // монохромный, 80 символов

#define COLOR_80_MODE 0x03 // цветной, 80 символов

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

#define TYPE_CODE_DESCR 0x18

#define TYPE_DATA_DESCR 0x10

#define TYPE_TSS_DESCR 0x01

#define TYPE_CALL_GATE 0x04

#define TYPE_TASK_GATE 0x85

#define TYPE_INTERRUPT_GATE 0x86

#define TYPE_TRAP_GATE 0x87

#define SEG_WRITABLE 0x02

#define SEG_READABLE 0x02

#define SEG_PRESENT_BIT 0x80

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

// прерываний

#define EOI 0x20

#define MASTER8259A 0x20

#define SLAVE8259A 0xa0

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

// адреса из компонент сегменоного адреса

// и смещения

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

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

typedef void (func_ptr)(void);

 

4.3 Файл TOS.H. Основной файл программы.

#include <stdio.h>

#include <stdlib.h>

#include <dos.h>

#include <conio.h>

#include "tos.h"

// --------------------------------

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

// --------------------------------

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

void Init_And_Protected_Mode_Entry(void);

void protected_mode(unsigned long gdt_ptr, unsigned int gdt_size,

word cseg, word dseg);

word load_task_register(word tss_selector);

void real_mode(void);

void jump_to_task(word tss_selector);

void load_idtr(unsigned long idt_ptr, word idt_size);

void Keyb_int(void);

void Timer_int(void);

void Int_30h_Entry(void);

extern word kb_getch(void);

void enable_interrupt(void);

void task1(void);

void task2(void);

void flipflop_task(void);

void keyb_task(void);

void init_tss(tss *t, word cs, word ds,

unsigned char *sp, func_ptr ip);

void init_gdt_descriptor(descriptor *descr, unsigned long base,

word limit, unsigned char type);

void exception_0(void); //{ prg_abort(0); }

void exception_1(void); //{ prg_abort(1); }

void exception_2(void); //{ prg_abort(2); }

void exception_3(void); //{ prg_abort(3); }

void exception_4(void); //{ prg_abort(4); }

void exception_5(void); //{ prg_abort(5); }

void exception_6(void); //{ prg_abort(6); }

void exception_7(void); //{ prg_abort(7); }

void exception_8(void); //{ prg_abort(8); }

void exception_9(void); //{ prg_abort(9); }

void exception_A(void); //{ prg_abort(0xA); }

void exception_B(void); //{ prg_abort(0xB); }

void exception_C(void); //{ prg_abort(0xC); }

void exception_D(void); //{ prg_abort(0xD); }

void exception_E(void); //{ prg_abort(0xE); }

void exception_F(void); //{ prg_abort(0xF); }

void exception_10(void); //{ prg_abort(0x10); }

void exception_11(void); //{ prg_abort(0x11); }

void exception_12(void); //{ prg_abort(0x12); }

void exception_13(void); //{ prg_abort(0x13); }

void exception_14(void); //{ prg_abort(0x14); }

void exception_15(void); //{ prg_abort(0x15); }

void exception_16(void); //{ prg_abort(0x16); }

void exception_17(void); //{ prg_abort(0x17); }

void exception_18(void); //{ prg_abort(0x18); }

void exception_19(void); //{ prg_abort(0x19); }

void exception_1A(void); //{ prg_abort(0x1A); }

void exception_1B(void); //{ prg_abort(0x1B); }

void exception_1C(void); //{ prg_abort(0x1C); }

void exception_1D(void); //{ prg_abort(0x1D); }

void exception_1E(void); //{ prg_abort(0x1E); }

void exception_1F(void); //{ prg_abort(0x1F); }

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

 
     
Бесплатные рефераты
 
Банк рефератов
 
Бесплатные рефераты скачать
| Интенсификация изучения иностранного языка с использованием компьютерных технологий | Лыжный спорт | САИД Ахмад | экономическая дипломатия | Влияние экономической войны на глобальную экономику | экономическая война | экономическая война и дипломатия | Экономический шпионаж | АК Моор рефераты | АК Моор реферат | ноосфера ба забони точики | чесменское сражение | Закон всемирного тяготения | рефераты темы | иохан себастиян бах маълумот | Тарых | шерхо дар борат биология | скачать еротик китоб | Семетей | Караш | Influence of English in mass culture дипломная | Количественные отношения в английском языках | 6466 | чистонхои химия | Гунны | Чистон | Кус | кмс купить диплом о language:RU | купить диплом ргсу цена language:RU | куплю копии дипломов для сро language:RU
 
Рефераты Онлайн
 
Скачать реферат
 
 
 
 
  Все права защищены. Бесплатные рефераты и сочинения. Коллекция бесплатных рефератов! Коллекция рефератов!