Лабораторная работа №4
Защищённый режим
Цель: В защищённом режиме создать 2 задачи. Каждая задача выводит на экран мигающий символ. Управляя клавишами ‘1' , ‘ 2 ' останавливать или возобновлять мигание символа соответствующей задачи. Задачи выполняются в защищенном режиме.
Теоретические сведения:
В подавляющем большинстве программы, составленные для реального режима процессора, выполняются в однозадачном режиме, полностью монополизируя все ресурсы компьютера. Однако в реальной жизни человеку, работающему с компьютером требуется одновременный доступ к двум или большему числу программ. Мультизадачность позволяет не только задействовать все ресурсы современных персональных компьютеров, но и существенно повышает производительность труда. В защищенном режиме процессора i80x86 можно как раз и реализовать эту мультизадачность.
Ход выполнения работы
Определяем селекторы как константы. У них все биты TI = 0 (выборка дескрипторов производится из GDT), RPL = 00 - уровень привилегий - нулевой.
Создаем GDT и описываем дескрипторы в ней. Нам понадобится создать 15 дескрипторов для кода, данных, стека , видеопамяти. Так же создаем дескрипторы для возврата в режим реальных адресов. После этого устанавливаем GDT, запретив перед этим прерывания.
Устанавливаем IDT для шлюзов исключений и аппаратных прерываний. Будем обрабатывать аппаратные прерывания только от таймера и клавиатуры. Для остальных устанавливает заглушки. Для исключения деления на ноль создаем обработчик, который вывод на экран число обращений к нему.
Для перевода процессора из реального режима в защищённый можно использовать специальную команду LMSW, загружающую регистр состояния процессора (Mashine Status Word). Младший бит этого регистра указывает режим работы процессора. Значение, равное 0, соответствует реальному режиму работы, а значение 1 - защищённому.
Если установить младший бит регистра состояния процессора в 1, процессор переключится в защищённый режим:
mov ax, 1
lmsw ax
Для того, чтобы вернуть процессор из защищённого режима в реальный, необходимо выполнить аппаратный сброс (отключение) процессора. Это можно сделать следующим образом:
mov ax, 0FEh ; команда отключения
out 64h, ax
В защищенном режиме запускаем две задачи, которые выводят на экран строчки Task in Progress or Task Stop, показывая в каком состоянии они находятся. Для остановки, какой либо задачи использует семафоры. Таким образом при нажатии цифровой клавиши 1 или 2 происходит остановка или запуск соответствующего потока. При нажатии на клавишу 3 вызывается процедура деления на ноль. При нажатии на Esc происходит выход в реальный режим. Диспетчеризация осуществляется на основе выделения кванта времени при помощи генерируемого прерывания от timer.
Пример работы приложения:
Вывод: в лабораторной работе написана программа, переходящая процессор из реального режима в защищенный, запускает в этом режиме два потока, кроме того, программа обрабатывает прерывания от таймера и клавиатуры.
Литература
1. В.И. Юров “Assembler” – СПб: Питер, 2002 – 624с.
2. С.В. Зубков “Assembler” – ДМК “Пресс”, 1999 – 624с.
3. А. Фролов, Г. Фролов Защищенный режим процессоров Intel 80286/80386/80486 Том 6, М.: Диалог-МИФИ, 1993, 234 стр.
4. А.В. Гордеев А.Ю. Молчанов. Системное программное обеспечение. Санкт-Петербург. Питер. 2002 г. 740 с.
Приложение
Файл EXCEPT.C
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"
void prg_abort(int err);
void div_abort();
// Номер текущей строки для вывода на экран
extern unsigned int y;
// Обработчики исключений
void exception_0(void) { div_abort(); }
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++,"!!! ---> Произошло исключение", 0xc);
real_mode(); // Возвращаемся в реальный режим
// В реальном режиме выводим сообщение об исключении
gotoxy(1, ++y);
cprintf(" Исключение %X, нажмите любую клавишу", err);
getch();
textcolor(WHITE);
textbackground(BLACK);
clrscr();
exit(0);
}
int num = 0;
void div_abort() {
num++;
vi_print(1,8,"Упсс. Деление на ноль", 0xc);
vi_print(1,9," Сколько раз можно нажимать эту клавишу", 0x7f);
vi_put_word(1,10,num,0x7f);
}
Файл INTPROC.C
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"
// Заглушки для необрабатываемых
// аппаратных прерываний.
void iret0(void) { // первый контроллер прерываний
asm {
push ax
mov al,EOI
out MASTER8259A,al
pop ax
pop bp
iret
}
}
void iret1(void) { // второй контроллер прерываний
asm {
push ax
mov al,EOI
out MASTER8259A,al
out SLAVE8259A,al
pop ax
pop bp
iret
}
}
Файл KEYB.C
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"
extern word key_code;
// Функция, ожидающая нажатия любой
// клавиши и возвращающая её скан-код
unsigned int kb_getch(void) {
asm int 30h
return(key_code);
}
Файл KEYBOARD.ASM
IDEAL
MODEL SMALL
RADIX 16
P386
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
Файл SCREEN.C
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.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,
" Protected Mode Monitor"
" v1.0 for CPU 80x86 Вершинин Алексей 2004 г.", 0x30);
}
Файл SEMAPHOR.C
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"
// Массив из пяти семафоров
word semaphore[7];
// Процедура сброса семафора.
// Параметр 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
}
int sem_test(int sem) {
if (semaphore[sem]) return 1;
return 0;
}
Файл TASKS.C
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"
word dispatcher(void);
// Номер текущей строки для вывода на экран
extern unsigned int y;
// Задача TASK_1
long delay_cnt1 = 0l;
void task2(void) {
while(1){
asm sti
if(delay_cnt1 > 150000l ) {
asm cli
if (!sem_test(2)) {
vi_print(60,3, " Task 1 In Progress", 0x4f);
}
else
{
sem_clear(2);
vi_print(60,3, " Task 1 Stop ", 0x1f);
sem_wait(2);
sem_clear(2);
}
delay_cnt1 = 0l;
asm sti
}
delay_cnt1++;
}
}
word flipflop = 0;
long delay_cnt = 0l;
//Задача TASK_2
void flipflop_task(void) {
while(1){
asm sti
if(delay_cnt > 150000l ) {
asm cli
if (!sem_test(3)) {
vi_print(60,4, " Task 2 In Progress", 0x4f);
}
else
{
sem_clear(3);
vi_print(60,4, " Task 2 Stop ", 0x1f);
sem_wait(3);
sem_clear(3);
}
delay_cnt=0l;
asm sti
}
delay_cnt++;
}
}
word keyb_code;
extern word keyb_status;
int error;
void keyb_task(void) {
// Эта задача вводит символы с клавиатуры.
// Работающая параллельно главная задача
// ожидает установку семафора. Как только
// семафор 0 окажется установлен, главная задача
// завершает свою работу и программа возвращает
// процессор в реальный режим, затем передаёт
// управление MS-DOS.
while(1){
keyb_code = kb_getch();
if (keyb_code == 0x0004) {
error =5;
error = error/0;
}
if (keyb_code == 0x0002) sem_set(2);
if (keyb_code == 0x0003) sem_set(3);
if ((keyb_code & 0x00ff) == 1) sem_set(0);
}
}
Файл TIMER.C
#include <stdio.h>
#include <dos.h>
#include <conio.h>
#include <stdlib.h>
#include "tos.h"
// ------------------------------------------// Модуль обслуживания таймера
// ------------------------------------------#define EOI 0x20
#define MASTER8259A 0x20
extern void beep(void);
extern void flipflop_task(void);
void Timer_int(void);
word dispatcher(void);
word timer_cnt;
// -----------------------------------------// Обработчик аппаратного прерывания таймера
// -----------------------------------------void Timer_int(void) {
asm pop bp
// Выдаём в контроллер команду конца
// прерывания
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]);
}
Файл TOS.C
#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 sseg);
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 task2(void);
void flipflop_task(void);
void keyb_task(void);
void init_tss(tss *t, word cs, word ds, word ss,
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
Уважаемый посетитель!
Чтобы распечатать файл, скачайте его (в формате Word).
Ссылка на скачивание - внизу страницы.