Программирование Базы данных
| |
Для того, чтобы
вы могли почувствовать мультизадачность,
мы подготовили пример программы,
реализующей параллельную работу
нескольких задач в режиме разделения
времени. Эта программа состоит из
нескольких модулей, составленных на языках
ассемблера и Си. Первые два файла
предназначены для определения
используемых констант и структур данных.
Листинг 4. Определение констант и структур для модулей,
составленных на языке ассемблера.Файл 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
Листинг 5. Определение констант и структур для модулей,
составленных на языке Си.Файл 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;
}
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);
Файл tos.c (листинг
6) содержит основную программу, которая
инициализирует процессор для работы в
защищённом режиме и запускает все задачи. С
помощью функции с названием
Init_And_Protected_Mode_Entry() мы попадаем в защищённый
режим и выводим сообщение на экран о том,
что в главной задаче установлен защищённый
режим. Регистр TR загружается селектором
главной задачи при помощи функции
load_task_register(). Сразу после этого
программа переключается на выполнение
задачи TASK_1. Эта задача просто выводит
сообщение о своём запуске на экран и
возвращает управление главной задаче. Цель
этой процедуры - продемонстрировать
процесс переключения задач с помощью
команды JMP. После возврата в главную
задачу программа размаскирует прерывания
от клавиатуры и таймера. Как только
начинают поступать и обрабатываться
прерывания таймера, оживают остальные
задачи, определённые при инициализации
системы. Начиная с этого момента
главная задача разделяет процессорное
время наравне с остальными задачами. Что же
она делает? Главная задача сбрасывает
семафор с номером 0, вслед за чем ожидает его
установку. Этот семафор устанавливается
задачей, которая занимается вводом
символов с клавиатуры и выводит на экран
скан-коды клавиш, а также состояние
переключающих клавиш. Как только окажется
нажатой клавиша ESC, задача ввода символов с
клавиатуры устанавливает семафор 0, что и
приводит к завершению работы главной
задачи. Перед тем, как завершить
работу, главная задача устанавливает
реальный режим работы процессора, стирает
экран и возвращает управление операционной
системе. Листинг
6. Программа мультизадачного монитора.Файл
tos.c
-----------------------------------------------------------
#include
#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
// Вентиль задачи, запускающейся по
прерыванию от клавиатуры
{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; // номер текущей строки для вывода на
экран
// -------------------------------------------
// Начало программы
// -------------------------------------------
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);
y++;
// Запускаем планировщик задач
vi_print(0, y++ ," Запущен планировщик задач",
0x70);
enable_interrupt();
// разрешаем прерывание таймера
// Ожидаем установки семафора с номером
0. После того,
// как этот семафор окажется установлен,
возвращаемся
// в реальный режим.
// Семафор 0 устанавливается задачей,
обрабатывающей ввод с
// клавиатуры, которая работает
независимо от
// главной задаче.
vi_print(0, y++ ,
" Для возврата в реальный
режим нажмите 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
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);}
Файл tasks.c
содержит тексты программ, которые будут
работать в режиме разделения времени (кроме
задачи TASK_1, эта задача запускается только
один раз). Задача TASK_1 (процедура task1)
выдаёт сообщение о своём запуске и передаёт
управление главной задаче. Задача TASK_2 (процедура task2)
попеременно выводит на экран строки
"FLIP" и "FLOP", переключая попутно
семафор с номером 1. Задача FLIP_TASK (процедура
flipflop_task) также попеременно выводит на экран
строки "FLIP" и "FLOP", но только тогда,
когда семафор с номером 1 установлен. Таким
образом, задача TASK_2 управляет работой
задачи FLIP_TASK. Задача KEYB_TASK (процедура
keyb_task) вводит символы с клавиатуры и выводит
скан-коды нажатых клавиш, а также состояние
переключающих клавиш. Как только
оказывается нажатой клавиша ESC, задача
устанавливает семафор с номером 0, что
приводит к завершению работы главной
задачи (ожидающей установки этого семафора).
Лиasm %1.asm
/l /zi
tlink %1.obj /vclude
#include
#include
#include
#include "tos.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
word flipflop1 = 0;
long delay_cnt1 = 0l;
void task2(void)
{
while(1)
{
// Периодически выводим на экран строки
// FLIP/FLOP, каждый раз переключая
// семафор номер 1. Этот семафор однозначно
// соответствует выведенной на экран строке.
asm sti
if(delay_cnt1 > 150000l )
{
asm cli
if(flipflop1)
{
vi_print(73,3," FLIP ",
0x4f);
sem_clear(1);
}
else
{
vi_print(73,3," FLOP ",
0x1f);
sem_set(1);
}
flipflop1 ^= 1;
delay_cnt1 = 0l;
asm sti
}
delay_cnt1++;
}
}
word flipflop = 0;
long delay_cnt = 0l;
void flipflop_task(void)
{
// Эта задача также периодически выводит на
экран
// строки FLIP/FLOP, но выводит строкой выше и
// с меньшим периодом. Кроме того, эта задача
// работает только тогда, когда установлен
// семафор номер 1.
while(1)
{
asm sti
if(delay_cnt > 20000l )
{
sem_wait(1);//ожидаем установки
семафора
asm cli
if(flipflop)
vi_print(73,2," FLIP ",
0x20);
else
vi_print(73,2," FLOP ",
0x20);
flipflop ^= 1;
delay_cnt = 0l;
asm sti
}
delay_cnt++;
}
}
word keyb_code;
extern word keyb_status;
void keyb_task(void)
{
// Эта задача вводит символы с клавиатуры
// и отображает скан-коды нажатых клавиш
// и состояние переключающих клавиш на
экране.
// Если нажимается клавиша ESC, задача
// устанавливает семафор номер 0.
// Работающая параллельно главная задача
// ожидает установку этого семафора. Как
только
// семафор 0 окажется установлен, главная
задача
// завершает свою работу и программа
возвращает
// процессор в реальный режим, затем
передаёт
// управление MS-DOS.
vi_print(60, 5, " Key code: .... ", 0x20);
vi_print(60, 6, " Key status: .... ", 0x20);
while(1)
{
keyb_code = kb_getch();
vi_put_word(73, 5, keyb_code, 0x4f);
vi_put_word(73, 6, keyb_status, 0x4f);
if((keyb_code & 0x00ff) == 1) sem_set(0);
}
}
Файл semaphor.c содержит
исходные тексты процедур сброса семафора,
установки семафора и ожидания семафора.
В массиве semaphore[5]
определено пять семафоров. Разумеется, что
когда вы будете экспериментировать с
программой, вы можете изменить количество
доступных семафоров.
Листинг 8. Процедуры для работы с семафорами.
Файл 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
}
Файл timer.c
содержит обработчик аппаратного
прерывания таймера, который периодически
выдаёт звуковой сигнал и инициирует работу
диспетчера задач. Диспетчер задач
циклически перебирает селекторы TSS задач,
участвующих в процессе разделения времени,
возвращая селектор той задачи, которая
должна стать активной. В самом конце
обработки аппаратного прерывания таймера
происходит переключение именно на эту
задачу.
Листинг 9. Процедуры для работы с таймером и
диспетчер задач.
Файл timer.c
---------------------------------------------
#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]);
}
Для сокращения
объёма и без того сложной программы мы не
стали делать функционально полную
обработку исключений, ограничившись
простым аварийным завершением работы
программы с выдачей номера исключения. Исходные тексты
обработчиков исключений находятся в файле
except.c.
Листинг 10. Обработка исключений.
Файл 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++,"!!! ---> Произошло исключение",0xc);
real_mode();
// Возвращаемся в реальный режим
// В реальном режиме выводим сообщение об исключении
gotoxy(1, ++y);
cprintf(" Исключение %X, нажмите любую клавишу", err);
getch();
textcolor(WHITE);
textbackground(BLACK);
clrscr();
exit(0);
}
В файле intproc.c
расположены заглушки для тех аппаратных
прерываний, обработка которых сводится к
простой посылке кода конца прерывания в
соответствующий контроллер прерывания.
Листинг 11. Заглушки для аппаратных прерываний.
Файл 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
}
}
Файл keyb.c
содержит простой интерфейс для вызова
программного прерывания int 30h,
обеспечивающего ввод с клавиатуры.
Листинг 12. Ввод символа с клавиатуры.
Файл keyb.c
--------------------------------------
#include
#include
#include
#include
#include "tos.h"
extern word key_code;
// Функция, ожидающая нажатия любой
// клавиши и возвращающая её скан-код
unsigned int kb_getch(void)
{
asm int 30h
return(key_code);
}
Обработчик
аппаратного прерывания клавиатуры мы взяли
практически без изменений из программы,
представленной в предыдущей главе.
Исходные тексты находятся в файле keyboard.asm.
Листинг 13. Процедуры для работы с клавиатурой.
Файл keyboard.asm
-----------------------------------------------
IDEALMODEL SMALL
RADIX 16P286include "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_sca
next_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_EntryEND
Обработчик
аппаратного прерывания клавиатуры мы взяли
практически без изменений из программы,
представленной в предыдущей главе.
Исходные тексты находятся в файле keyboard.asm.
Листинг 14. Процедуры для работы с видеоадаптером.
Файл screen.c
--------------------------------------------------
#include
#include
#include
#include
#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 *TINY/OS*, "
"v.1.2 for CPU 80286 ¦ © Frolov A.V., 1992 ",
0x30);
}
Последний файл -
tossyst.asm - содержит уже знакомые вам процедуры
для входа в защищённый режим и возврата
обратно в реальный режим. Обратите внимание
на процедуры _load_task_register и _jump_to_task,
выполняющие загрузку регистра задачи TR и
переключение на другую задачу
соответственно.
Листинг 15. Процедуры для инициализации, перехода в
защищённый режим и возврата в реальный режим,
для загрузки регистра TR и переключения задач.
Файл tossyst.asm
------------------------------------------------------
IDEAL
MODEL SMALL
RADIX 16
P286
DATASEG
include "tos.inc"
PUBLIC _beep
; Область памяти для инициализации IDTR
idtr idtr_struc <,,,0>
; Область памяти для инициализации 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
[ << Назад ] [ Содержание ] [ Далее >> ]
|