- адресовать до 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.
Каждый элемент таблицы дескрипторов имеет следующий формат:
Общая его длина составляет 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))ss = ds; t->ip = (word)ip; // указатель команд t->sp = (word)sp; // смещение стека t->bp = (word)sp; }
// ------------------------------------------------- // Функция инициализации дескриптора в таблице GDT // -------------------------------------------------
// ----------------------------------------------- // Инициализация всех таблиц и вход // в защищённый режим // -----------------------------------------------
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),
init_gdt_descriptor(&gdt[5],
MK_LIN_ADDR(_DS, &main_tss),
// Инициализируем 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,
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),
// Инициализация 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),
// Инициализация 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);
Страницы: 1, 2, 3, 4