Загрузка операционной системы — КиберПедия 

Двойное оплодотворение у цветковых растений: Оплодотворение - это процесс слияния мужской и женской половых клеток с образованием зиготы...

Эмиссия газов от очистных сооружений канализации: В последние годы внимание мирового сообщества сосредоточено на экологических проблемах...

Загрузка операционной системы

2020-12-06 102
Загрузка операционной системы 0.00 из 5.00 0 оценок
Заказать работу

Задача 1.

Условие:

Найти серийный номер диска.

Способ 1: программа читает загрузочную запись диска (BOOT-сектор) в специальную структуру и затем получает из нее значение поля,

 


Листинг программы:

 

#include <stdio.h>

#include <dos.h>

#include <conio.h>

#include <ctype.h>

 

typedef struct _EBPB_

{

unsigned sectsize;

char clustsize;

unsigned ressecs;

char fatcnt;

unsigned rootsize;

unsigned totsecs;

char media;

unsigned fatsize;

unsigned seccnt;

unsigned headcnt;

unsigned hiddensec_low;

unsigned hiddensec_hi;

unsigned long drvsecs;

} EBPB;

 

typedef struct _BOOT_

{

char jmp[3];

char oem[8];

EBPB bpb;

char drive;

char reserved;

char signature;

unsigned volser_lo;

unsigned volser_hi;

char label[11];

char fat_format[8];

char boot_code[450];

} BOOT;

 

int getboot(BOOT far *boot, int drive);

 

int main(void)

{

char boot[512];

BOOT far* boot_rec = (BOOT far*)boot;

int i, status;

char drive;

 

printf("\nЧтение загрузочной записи логического диска");

 

// Запрашиваем диск, для которого необходимо

// выполнить чтение загрузочной записи

printf(

"\nВведите обозначение диска, для просмотра"

"\nзагрузочной записи (A, B,...):");

drive = getche();

 

// Вычисляем номер дисковода

drive = toupper(drive) - 'A';

 

// Читаем загрузочную запись в буфер

status = getboot((BOOT far*)boot_rec, drive);

 

// Если произошла ошибка (например, неправильно

// указано обозначение диска),

// завершаем работу программы

if(status)

{

printf("\nОшибка при чтении загрузочного сектора");

return(-1);

}

 

printf("\nСодержимое загрузочного "

"сектора для диска %c", drive + 'A');

 

printf("\n"

"\nOEM - название фирмы и версия DOS - ");

 

for(i = 0; i < 8; i++)

printf("%c", boot_rec->oem[i]);

 

printf("\nСерийный номер диска         - %04X-%04X",

boot_rec->volser_hi);

return 0;

}

 

/**

* getboot

*

 

* Прочитать загрузочную запись

*

* int getmboot(BOOT far *boot, int drive);

*

* boot - указатель на буфер, в который

*      будет прочитана загрузочная запись

*

* drive - номер физического НМД

*      (0 - первый НМД, 1 - второй,...)

**/

 

int getboot(BOOT far *boot, int drive)

{

union REGS reg;

struct SREGS segreg;

 

reg.x.ax = drive;

reg.x.bx = FP_OFF(boot);

segreg.ds = FP_SEG(boot);

reg.x.cx = 1;

reg.x.dx = 0;

int86x(0x25, &reg, &reg, &segreg);

 

// Извлекаем из стека оставшееся там после

// вызова прерывания слово

asm pop ax

return(reg.x.cflag);

}


Теоретическое обоснование.

Главная загрузочная запись

Самый первый сектор жесткого диска (сектор 1, дорожка 0, головка 0) содержит так называемую главную загрузочную запись (Master Boot Record). Эта запись занимает не весь сектор, а только его начальную часть.

Сама по себе главная загрузочная запись является программой. Эта программа во время начальной загрузки операционной системы с НМД помещается по адресу 7C00h:0000h, после чего ей передается управление. Загрузочная запись продолжает процесс загрузки операционной системы.

Таблица разделов диска

В конце самого первого сектора жесткого диска располагается таблица разделов диска (Partition Table). Эта таблица содержит четыре элемента, описывающих до четырех разделов диска. В последних двух байтах сектора находится значение 55AAh. Это признак таблицы разделов (сигнатура таблицы разделов).

Для просмотра и изменения содержимого таблицы разделов НМД используется программа fdisk.exe.

Что представляет из себя элемент таблицы разделов диска?

Это структура размером 16 байт, соответствующая части диска, называемой разделом. В структуре располагается информация о расположении и размере раздела в секторах, а также о назначении раздела.

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

Приведем формат первого сектора жесткого диска:

Смещение, байт Размер, байт Описание
0 1BEh Загрузочная запись
1BEh 10H Элемент таблицы разделов диска
1CEh 10H Элемент таблицы разделов диска
1DEh 10H Элемент таблицы разделов диска
1EEh 10H Элемент таблицы разделов диска
1FEh 2 Признак таблицы разделов - значение 55AAh

Все элементы таблицы разделов диска имеют одинаковый формат:

Смещение, байт Размер, байт Описание
0 1 Признак активного раздела: 0 - раздел неактивный; 80h - раздел активный
1 1 Номер головки для начального сектора раздела
2 2 Номер сектора и дорожки для начального сектора раздела в формате функции чтения сектора INT 13h
4 1 Код системы: 0 - неизвестная система; 1, 4 - MS-DOS; 5 - расширенный раздел MS-DOS
5 1 Номер головки для последнего сектора раздела
6 2 Номер сектора и дорожки для последнего сектора раздела в формате функции чтения сектора INT 13h
8 4 Относительный номер сектора начала раздела
12 4 Размер раздела в секторах

Загрузочная запись

В самом первом секторе активного раздела расположена загрузочная запись (Boot Record), которую не следует путать с главной загрузочной записью (Master Boot Record). Загрузочная запись считывается в оперативную память главной загрузочной записью, после чего ей передается управление. Загрузочная запись и выполняет загрузку операционной системы.

Загрузочная запись

Самый первый сектор логического диска (и самый первый сектор на системной дискете) занимает загрузочная запись (Boot Record). Эта запись считывается из активного раздела диска программой главной загрузочной записи (Master Boot Record) и запускается на выполнение. Задача загрузочной записи - выполнить загрузку операционной системы. Каждый тип операционной системы имеет свою загрузочную запись. Даже для разных версий одной и той же операционной системы программа загрузки может выполнять различные действия.

Кроме программы начальной загрузки операционной системы в загрузочной записи находятся параметры, описывающие характеристики данного логического диска. Все эти параметры располагаются в самом начале сектора, в его так называемой форматированной области. Формат этой области изменился в версии 4.0 операционной системы MS-DOS.

Формат загрузочной записи

Сначала приведем формат загрузочной записи для версий MS-DOS, более ранних, чем 4.0.

Смещение Размер Содержимое
0 3 Команда JMP xxxx - ближний переход на программу начальной загрузки
3 8 Название фирмы-изготовителя операционной системы и версия, например: "IBM 4.0"
11 13 Блок параметров BIOS (BPB)
24 2 Количество секторов на дорожке
26 2 Количество головок (поверхностей диска)
28 2 Количество скрытых секторов, эти секторы могут использоваться для схемы разделения физического диска на разделы и логические диски

В самом начале загрузочного сектора располагается команда внутрисегментного перехода JMP. Она нужна для обхода форматированной зоны сектора и передачи управления загрузочной программе, располагающейся со смещением 30.

Название фирмы-изготовителя не используется операционной системой.

Со смещением 11 располагается BPB - блок параметров BIOS, о котором мы уже говорили в разделах книги, посвященных драйверам. Этот блок содержит некоторые характеристики логического диска, о которых мы будем говорить немного позже. Он активно используется дисковыми драйверами. Для MS-DOS версий до 4.0 блок BPB имеет следующий формат:

Смещение, байт Размер, байт Имя поля Описание
0 2 sect_siz Количество байт в одном секторе диска
2 1 clustsiz Количество секторов в одном кластере
3 2 res_sect Количество зарезервированных секторов
5 1 fat_cnt Количество таблиц FAT
6 2 root_siz Максимальное количество дескрипторов файлов в корневом каталоге диска
8 2 tot_sect Общее количество секторов на носителе данных (в разделе MS-DOS)
10 1 media Байт-описатель среды носителя данных
11 2 fat_size Количество секторов, занимаемых одной копией FAT

Поля загрузочного сектора со смещениями 24 и 26 содержат, соответственно, количество секторов на дорожке и количество головок в НМД. Поле со смещением 28 содержит количество "скрытых" секторов, которые не принадлежат ни одному логическому диску. Эти секторы могут содержать основную или вторичные таблицы разделов диска.

Для современных версий MS-DOS загрузочный сектор имеет другой формат:

Смещение Размер Содержимое
0 3 Команда JMP xxxx - ближний переход на программу начальной загрузки
3 8 Название фирмы-изготовителя операционной системы и версия
11 25 Extended BPB - расширенный блок параметров BIOS
36 1 Физический номер устройства (0 -НГМД, 80h -НМД)
37 1 Зарезервировано
38 1 Символ ')' - признак расширенной загрузочной записи
39 4 Серийный номер диска (Volume Serial Number), создается во время форматирования диска
43 11 Метка диска (Volume Label)
54 8 Зарезервировано, обычно содержит запись типа 'FAT12 ', которая идентифицирует формат таблицы размещения файлов FAT

Первые два поля в загрузочном секторе аналогичны описанным раньше.

Поле со смещением 38 всегда содержит символ ')'. Этот символ означает, что используется формат расширенной загрузочной записи.

Серийный номер диска формируется во время форматирования диска на основе даты и времени форматирования. Это поле может быть использовано для определения факта замены дискеты.

Метка диска формируется при форматировании и может быть изменена командой LABEL операционной системы MS-DOS. Одновременно метка диска помещается в корневой каталог.

Логический номер сектора

MS-DOS предоставляет программе возможность работы с так называемыми логическими номерами секторов. Это номера секторов внутри логического диска.

Вы знаете, что для адресации сектора при помощи функций BIOS необходимо указывать номер дорожки, номер головки и номер сектора на дорожке. MS-DOS организует "сквозную" нумерацию секторов, при которой каждому сектору логического диска присваивается свой номер. Порядок нумерации выбран таким, что при последовательном увеличении номера сектора вначале увеличивается номер головки, затем номер дорожки. Это сделано для сокращения перемещений блока головок при обращении к последовательным логическим номерам секторов.

Пусть, например, у нас есть дискета с девятью секторами на дорожке. Сектор с логическим номером, равным 1, расположен на нулевой дорожке и для обращения к нему используется нулевая головка. Это самый первый сектор на дорожке, он имеет номер 1. Следующий сектор на нулевой дорожке имеет логический номер 2, последний сектор на нулевой дорожке имеет логический номер 9. Сектор с логическим номером 10 расположен также на нулевой дорожке. Это тоже самый первый сектор на дорожке, но теперь для доступа к нему используется головка с номером 1. И так далее, по мере увеличения логического номера сектора изменяются номера головок и дорожек.

Найти серийный номер диска.

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


Листинг программы:

 

/* Текст программы и выходные результаты  */

#include <stdio.h>

#include <alloc.h>

#include <dos.h>

/* Функция getbootc() считывает загрузочную запись

указанного НМД непосредственно в область из 512 байтов,

возвращает 0 в случае успешного считывания, а иначе - 1 */

int getbootc(char *boot,int drive)

{ union REGS reg;

struct SREGS segreg;

/* Заполняем регистровые структуры для вызова

прерывания DOS int 25h */

reg.x.ax=drive; reg.x.bx=FP_OFF(boot);

segreg.ds=FP_SEG(boot); reg.x.cx=1; reg.x.dx=0;

int86x(0x25,&reg,&reg,&segreg); return(reg.x.cflag);

}

void main(void)

{

char far * boot_rc;

int statusc; char drive;

/* Заказываем буфер для чтения BOOT-записи */

/* Адрес буфера присваиваем far-указателю */

boot_rc=malloc(512); /* Такова длина сектора загрузочного */

/* Запрашиваем диск, для которого необходимо выполнить

чтение загрузочной записи                        */

printf("\nВведите обозначение диска (A, B,...): ");

drive=getche();

/* Вычисляем номер дисковода */

drive=toupper(drive)-'A';

/* Читаем загрузочную запись в буфер */

statusc=getbootc((char far *)boot_rc,drive);

/* Если произошла ошибка, то завершим работу */

if(statusc)

 

{ printf("\nОшибка при чтении BOOT-сектора\n"); exit(-1); }

printf("\nКоличество секторов на дорожке равно %04x-%04x\n",

*(char *)(boot_rc+39)); /* 11 байт до начала

        подстр-ры EBPB, а там ещё сдвиг 2 байт */

/* Освобождаем буфер */ free(boot_rc);

}


Пояснительная записка к программе 1 (способ 2).

 

Здесь приводится листинг решения задачи, в котором загрузочная запись считывается в область памяти длины 512 байт. При этом используется прерывание DOS INT 25h, которое читает сектор по его логическому номеру.

На входе этого прерывания в регистр AL заносится адрес НГМД или НМД (0 - A:, 1 - B:,...); в CX - количество секторов, которые нужно прочитать; в DX - логический номер начального сектора; в DS:BX - адрес буфера для чтения. На выходе прерывания INT 25h в регистр AH помещён код ошибки при неуспешном завершении операции, а флаг CF содержит 1, если произошла ошибка и 0, если ошибки нет. Поскольку в рассматриваемом прерывании значения задаются и в сегментном регистре, то для реализации прерывания используется функция int86x, прототип которой описан в dos.h и имеет вид 

int int86x(int intno, union REGS *inregs, union REGS *outregs,

struct SREGS *segregs)

 

Шаблон структуры SREGS содержится в файле dos.h и имеет вид

struct SREGS {

           unsigned int es;

           unsigned int cs;

           unsigned int ss;

           unsigned int ds;

};

По выходу из прерывания в переменную segreg копируются значения всех сегментных регистров. В функции getbootc() указано, как заполняются регистровые структуры для вызова прерывания 25h. В результате вызова функции getbootc() в буфер, выделенный функцией malloc(), считывается загрузочная запись указанного НМД.

Адрес буфера присваивается far-указателю boot_rc.

Далее, чтобы ответить на вопрос задачи, т.е. определить количество секторов на одну копию FAT, следует воспользоваться форматом загрузочной. Искомая величина находится в блоке параметров BIOS (BPB) со смещением 11 байт от его начала, а сам блок параметров BIOS располагается со смещением 11 байт от начала загрузочного сектора.

Поэтому количество секторов на одну копию FAT определяется величиной *(unsigned *)(boot_rc+22). Здесь операция приведения типа (unsigned *) сообщает компилятору размер объекта.

Использованное прерывание int 25h, а также BOOT-сектор имеют различный формат для разных версий MS-DOS. Выше использован формат для тех версий, которые не поддерживают размер логических дисков более 32 Мбайт.


Задача 2.

Условие:

Записать в буфер клавиатуры последовательность символов согласно выбранному варианту тремя способами (три программы на языке Си): с использованием функции 5 прерывания 16h, осуществляя непосредственный доступ к ячейкам памяти, выделенным под буфер клавиатуры, по текущему значению указателя "головы" буфера; то же, но по текущему указателю "хвоста" буфера.

 

Сменить активность окон программной оболочки Нортон, поместив в буфер клавиатуры код клавиши <Tab>.

 

 

Способ 1:

С использованием функции 5 прерывания 16h


Листинг программы:

 

#include <io.h>

#include <dos.h>

#include <process.h>

#include <conio.h>

#include <stdio.h>

#include <fcntl.h>

#include <sys\types.h>

#include <sys\stat.h>

#include <errno.h>

 

main()

{

union REGS rg;

int i;

 

rg.h.ah = 5;

rg.h.cl = 0x09;

rg.h.ch = 0x0F;

int86(0x16, &rg, &rg);

 

system("c:\\nc\\nc.exe");

return 0;

 

}


Пояснительная записка к задаче 2 (способ 1):

 

Набор функций для работы с клавиатурой, предоставляемый в распоряжение программиста прерыванием BIOS INT 16h, включает в себя функции для выборки кода нажатого символа из буфера с ожиданием нажатия, функции для проверки содержимого буфера и для управления содержимым буфера, функции для изменения скоростных характеристик клавиатуры.

 

С помощью функции 05h (прерывание 16h) можно вставить символы в буфер клавиатуры, как-будто они были введены оператором.

 

Регистры на входе: AH = 05h; CL = код ASCII записываемого символа; CH = скан-код записываемого символа, или 0
Регистры на выходе: AL = 0 - запись выполнена успешно; AL = 1 - буфер клавиатуры переполнен

 

Приведенная ниже фрагмент программы используется выше, для описания вводимых в память клавиатуры символов, на примере записи в буфер клавиатуры пять символов '*'. Если запустить программу, содержащую этот фрагмент кода, а затем посмотреть на системное приглашение, то вы увидите что-нибудь похожее на C:\>*****.

union REGS rg;

int i;

for(i=0; i<5; i++)

{

rg.h.ah = 5;

rg.h.cl = '*';

rg.h.ch = 9;

int86(0x16, &rg, &rg);

}

Для определения скан-кодов и кодов ASCII нажимаемых клавиш использовалась следующая программа:

 

#include <stdio.h>

#include <dos.h>

 

int main(void)

{

union REGS rg;

 

printf("Press <ESC> to exit\n");

 

for(;;)

{

// Вызываем прерывание INT 16h

rg.h.ah = 0;

int86(0x16, &rg, &rg);

 

// Выводим на экран содержимое регистров AH и AL,

// содержащих, соответственно, скан-код и код ASCII

// нажатой клавиши

printf("\nScan = %02.2X Ascii = %02.2X",

rg.h.ah, rg.h.al);

 

// Если была нажата клавиша ESC, завершаем работу

// программы

if(rg.h.ah == 1)

break;

}

return 0;

}

 

Буфер клавиатуры

Буфер клавиатуры имеет длину 32 байта и расположен в компьютере IBM PC/XT по адресу 0000h:041Eh.

В компьютерах моделей IBM PC/AT и IBM PS/2 расположение клавиатурного буфера задается содержимым двух слов памяти с адресами 0000h:0480h (смещение адреса начала буфера) и 0000h:0482h (смещение конца буфера). Обычно эти ячейки памяти содержат значения, соответственно, 001Eh и 003Eh. Так как смещения заданы относительно сегментного адреса 0040h, то стандартное расположение клавиатурного буфера в IBM PC/AT и IBM PS/2 соответствует его расположению в IBM PC/XT.

Буфер клавиатуры организован циклически. Это означает, что при его переполнении самые старые значения будут потеряны. Две ячейки памяти, находящиеся в области данных BIOS с адресами 0000h:041Ah и 0000h:041Ch содержат, соответственно, указатели на начало и конец буфера. Если значения этих указателей равны друг другу, буфер пуст.

Заметим, что вы можете удалить все символы из буфера клавиатуры, установив оба указателя на начало буфера. Однако есть более предпочтительный способ с использованием прерывания BIOS INT 16h, функции которого мы опишем позже в этой главе.

Указателями на начало и конец клавиатурного буфера обычно управляют обработчики прерываний INT 09h и INT 16h. Программа извлекает из буфера коды нажатых клавиш, используя различные функции прерывания INT 16h.

При переполнении внутреннего буфера клавиатуры или буфера, расположенного в области данных BIOS программа-обработчик прерывания INT 09h генерирует звуковой сигнал.

В программах MS-DOS у вас едва ли появится необходимость непосредственного манипулирования содержимым буфера клавиатуры - вы можете использовать прерывание BIOS INT 16h для выполнения практически всех клавиатурных операций.


Способ 2: по текущему значению указателя "головы" буфера

Способ 3: по текущему значению указателя "хвоста"  буфера

 

Листинги программ:

#include <io.h>

#include <dos.h>

#include <process.h>

#include <conio.h>

#include <stdio.h>

#include <fcntl.h>

#include <sys\types.h>

#include <sys\stat.h>

#include <errno.h>

#define KB_BUFFER_START 0x1e /* Смещение начала буфеpа клавиатуpы */

#define KB_BUFFER_END 0x3e /* Смещение конца буфеpа клавиатуpы */

#define HEAD_PTR   0x1a /* Смещение указателя "головы" */

#define TAIL_PTR   0x1c /* Смещение указателя "хвоста" */

#define OK  0 /* Код нажатия записан в буфеp */

#define FULL 1 /* Буфеp клавиатуpы заполнен до пpедела */

/* Записать один символ в буфер клав-ры с "хвоста" */

int enter_kbt(unsigned key_code)

{unsigned register _es *head=(unsigned _es *) HEAD_PTR,

       _es *tail=(unsigned _es *) TAIL_PTR,

               _es *tmp,ret;

_ES=0x40;

disable(); /* Начало кpитической секции кода */

tmp=(unsigned _es *) *head;

if(tmp==(unsigned _es *)KB_BUFFER_START)

/* "Пеpескок" указателя на конец буфеpа */

tmp=(unsigned _es *)KB_BUFFER_END;

if(tmp-1==(unsigned _es *)*tail) /* Буфеp полон */

ret=FULL;

else

{ *(unsigned _es *)tmp=key_code;

tmp--;

*head=(unsigned)tmp;

    ret=OK; }

enable(); return ret;

}

 

int main()

{

unsigned key_codes[]={0x0f09};

/* В массиве key_codes[]все перечисленой в первом варианте*/

FILE *iii;

int source, taget, i;

char *buffer;

int count;

/*system("c:\\nc\\nc.exe");*/

  while(kbhit()) getch(); /* Очистка буфеpа клавиатуpы */

/* Запись в буфеp клавиатуpы массива key_codes с "головы". */

for(i=0;i>-1;i--)

{

enter_kbt(key_codes[i]);

}

system("c:\\nc\\nc.exe");

 }

 

 


#include <io.h>

#include <dos.h>

#include <process.h>

#include <conio.h>

#include <stdio.h>

#include <fcntl.h>

#include <sys\types.h>

#include <sys\stat.h>

#include <errno.h>

#define KB_BUFFER_START 0x1e /* Смещение начала буфеpа клавиатуpы */

#define KB_BUFFER_END 0x3e /* Смещение конца буфеpа клавиатуpы */

#define HEAD_PTR   0x1a /* Смещение указателя "головы" */

#define TAIL_PTR   0x1c /* Смещение указателя "хвоста" */

#define OK  0 /* Код нажатия записан в буфеp */

#define FULL 1 /* Буфеp клавиатуpы заполнен до пpедела */

/* Записать один символ в буфер клав-ры с "хвоста" */

int enter_kbt(unsigned key_code)

{unsigned register _es *tail=(unsigned _es *) TAIL_PTR,

               _es *head=(unsigned _es *) HEAD_PTR,

               _es *tmp,ret;

_ES=0x40;

disable(); /* Начало кpитической секции кода */

tmp=(unsigned _es *) *tail;

if(tmp==(unsigned _es *)KB_BUFFER_END)

/* "Пеpескок" указателя на начало буфеpа */

tmp=(unsigned _es *)KB_BUFFER_START;

if(tmp+1==(unsigned _es *)*head) /* Буфеp полон */

ret=FULL;

else

{ *(unsigned _es *)tmp=key_code;

   tmp++;

   *tail=(unsigned)tmp;

    ret=OK; }

enable(); return ret;

}

 

int main()

{

unsigned key_codes[]={0x0f09};

/* В массиве key_codes[]все перечисленой в первом варианте*/

FILE *iii;

int source, taget, i;

char *buffer;

int count;

/*system("c:\\nc\\nc.exe");*/

  while(kbhit()) getch(); /* Очистка буфеpа клавиатуpы */

/* Запись в буфеp клавиатуpы массива key_codes с "хвоста". */

for(i=0;i<1;i++)

{

enter_kbt(key_codes[i]);

}

system("c:\\nc\\nc.exe");

 }

 


Пояснительная записка к задаче 2 (способы 2 и3):

 

Буфер клавиатуры организуется, как кольцевая очередь, доступ к которой осуществляется с помощью указателя "головы": (head pointer), адрес которого 40h:1Ah, и указателя "хвоста" с адресом 40h:1Ch.  Указатель "хвоста" задаёт смещение, где будет записан обработчиком прерывания 9 двухбайтовый код буферизуемой клавиши. Указатель "головы" задаёт смещение возвращаемого слова по запросу операционной системы или BIOSа. Буфер клавиатуры занимает 32 байта памяти с адреса 40h:1Eh до 40h:3Eh. Если значения указателей "головы" и "хвоста" равны между собой, то буфер пуст.

В приведенном листинге функция int enter_kbt(unsigned key_code) вводит в первом случае с «головы», во втором с «хвоста» в буфер клавиатуры символ key_code.

Используются переменные tail, head, tmp типа register unsigned _es *. tail - это адрес, по которому хранится адрес "хвоста"; head - адрес, содержащий адрес "головы"; tmp - значение * tail указателя "хвоста" в способе 3 и значение * head указателя «головы» в способе 2, преобразованное к виду (unsigned _es *). В случае, когда tmp указывает на конец буфера, то ему присваивается значение на начало буфера и наоборот. Поэтому значение указателя "хвоста" может быть и меньше значения указателя "головы" и наоборот.

Если (для способа 3) tmp+1=(unsigned _es *)*head, то буфер полон и происходит выход из функции.

Если (для способа 2) tmp-1=(unsigned _es *)* tail, то буфер полон и происходит выход из функции.


Задача 3.

Условие:

Составить и отладить резидентную программу, которая, используя обработчики прерываний 9 и 1Ch, позволяет по нажатию определённой переключательной клавиши вызвать на экране появление заданного слова.

Создать резидентную программу, которая выводит на терминал слово "scanf" по нажатию ScrollLock.


Листинг программы:

 

#include <stdio.h>

#include <dos.h>

#define TAIL_PTR 0x1c

#define HEAD_PTR 0x1a

#define SOST_KLAV 0x18

void interrupt (*old_int09)(); /* Для стандартного обработчика

          прерывания 9 (нажатие/отпускание клавиши) */

void interrupt (*old_int1c)(); /* Для стандартного обработчика

          прерывания 0x1c (от таймера) */

 

union REGS rg;

int active,i; char c;

int n1,n2,n3,j,zflag;

unsigned key_codes[]={0x2267,0x1265,0x1474,0x2e63,0x2368,0x1e61,0x1372};

/* В массиве key_codes[] слово "skanf" в виде скэн-ASCII-кодов.

Но, если вместо скэн-составляющих здесь указать просто 6 нулей

или 6 любых скэн-кодов, то ничего от этого не изменится. */

void interrupt new_int1c()

{ /* Новый обработчик прерывания 0x1c - (от таймера) */

if(active)

{ i++; if(i>7) active=0;

else

{ disable();

  rg.x.ax=0x0500; /* Номеp вызываемой

  функции для пpеpывания 16h pавен ah=05h */

  rg.x.cx=key_codes[i]; /* ch - скэн-код, а cl - ASCII-код

  символа,записываемого в буфеp клавиатуpы */

  int86(0x16,&rg,&rg); /* тепеpь в al будет 0 если и только

  если запись в буфеp удалась, т.е. он не пеpеполнен */

  enable(); }

}

(*old_int1c)(); /* Передадим управление старому

обработчику прерывания 0x1c */

}

void interrupt new_int09()

{ /* Новый обработчик прерывания 0x09 -

(нажатие/отпускание любой клавиши)      */

register unsigned _es *t1, _ec *t2, _ec *t3;

disable();

t1=SOST_KLAV&0x40;

t2=TAIL_PTR&0x40;

t3=HEAD_PTR&0x40;

n2=*t2; n3=*t3; n1=*t1;

printf("n1 = %x",n1&0x10);

if(((n1&0x10)==0x36)&&(!active)&&(n2==n3))

/* Если прижата RightShift и active==0 и буфер клавиатуры пуст*/

{ i=-1; active=1;j=0;

for(j=0;j<6;j++)

printf("%c",key_codes[j]); (*old_int09)();}

else

(*old_int09)();

/* Передадим управление старому обработчику прерывания 0x09 */

enable();

}

 

void main(void)

{ /* Прототипы вызываемых отсюда ф-ий - в dos.h */

active=0; disable(); /* Запретить прерывания */

old_int09=getvect(0x09); /* Запомнит старый вектор 9-го

прер-ия (нажатие или отпускание клавиши) */

setvect(0x09,new_int09); /* Занести на место этого вектора

          новый - адрес нашего обработчика */

old_int1c=getvect(0x1c); /* Запомнит старый вектор

прер-ия 0x1c (от таймера) */

setvect(0x1c,new_int1c); /* Занести на место вектора 0x1c

прер-ния от таймера - адрес нашего обработчика этого прер-ния */

enable(); /* Разрешить прерывания */

keep(0,750); /* Примерно разделили на 8 размер EXE-файла этой

программы и получили 750 */

}


Пояснительная записка к программе 3.

Составление собственных программ обработки прерываний и замена стандартных обработчиков MS-DOS и BIOS является достаточно сложной задачей. Необходимо учитывать все тонкости работы аппаратуры, а также взаимодействия программного и аппаратного обеспечения. При отладке возможно разрушение операционной системы с непредсказуемыми последствиями, поэтому надо очень внимательно следить за тем, что делает ваша программа.

Таблица векторов прерываний

Для того чтобы связать адрес обработчика прерывания с номером прерывания, используется таблица векторов прерываний, занимающая первый килобайт оперативной памяти. Эта таблица находится в диапазоне адресов от 0000:0000 до 0000:03FFh и состоит из 256 элементов - дальних адресов обработчиков прерываний.

Элементы таблицы векторов прерываний называются векторами прерываний. В первом слове элемента таблицы записана компонента смещения, а во втором - сегментная компонента адреса обработчика прерывания.

Вектор прерывания с номером 0 находится по адресу 0000:0000, с номером 1 - по адресу 0000:0004 и т. д.

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

void (far* interrupt_table[256])();

Инициализация таблицы выполняется частично системой базового ввода/вывода BIOS после тестирования аппаратуры и перед началом загрузки операционной системой, частично при загрузке MS-DOS. Операционная система MS-DOS может изменить некоторые вектора прерываний, установленные BIOS.

Расскажем о назначении наиболее важных векторов прерываний.

Номер Описание
0 Ошибка деления.Вызывается автоматически после выполнения команд DIV или IDIV, если в результате деления происходит переполнение (например, при делении на 0). Обычно при обработке этого прерывания MS-DOS выводит сообщение об ошибке и останавливает выполнение программы. При этом для процессора i8086 адрес возврата указывает на команду, следующую после команды деления, а для процессора i80286 и более старших моделей - на первый байт команды, вызвавшей прерывание
1 Прерывание пошагового режима.Вырабатывается после выполнения каждой машинной команды, если в слове флагов установлен бит пошаговой трассировки TF. Используется для отладки программ. Это прерывание не вырабатывается после пересылки данных в сегментные регистры командами MOV и POP
2 Аппаратное немаскируемое прерывание.Это прерывание может использоваться по-разному в разных машинах. Обычно оно вырабатывается при ошибке четности в оперативной памяти и при запросе прерывания от сопроцессора
3 Прерывание для трассировки. Генерируется при выполнении однобайтовой машинной команды с кодом CCh и обычно используется отладчиками для установки точки прерывания
4 Переполнение. Генерируется машинной командой INTO, если установлен флаг переполнения OF. Если флаг не установлен, команда INTO выполняется как NOP. Это прерывание используется для обработки ошибок при выполнении арифметических операций
5 Печать копии экрана. Генерируется, если пользователь нажал клавишу <PrtSc>. В программах MS-DOS обычно используется для печати образа экрана. Для процессора i80286 и более старших моделей генерируется при выполнении машинной команды BOUND, если проверяемое значение вышло за пределы заданного диапазона
6 Неопределенный код операции или длина команды больше 10 байт
7 Особый случай отсутствия арифметического сопроцессора
8 IRQ0 - прерывание интервального таймера, возникает 18,2 раза в секунду
9 IRQ1 - прерывание от клавиатуры. Генерируется, когда пользователь нажимает и отжимает клавиши. Используется для чтения данных из клавиатуры
A IRQ2 - используется для каскадирования аппаратных прерываний
B IRQ3 - прерывание асинхронного порта COM2
C IRQ4 - прерывание асинхронного порта COM1
D IRQ5 - прерывание от контроллера жесткого диска (только для компьютеров IBM PC/XT)
E IRQ6 - прерывание генерируется контроллером НГМД после завершения операции ввода/вывода
F IRQ7 - прерывание от параллельного адаптера. Генерируется, когда подключенный к адаптеру принтер готов к выполнению очередной операции. Обычно не используется
10 Обслуживание видеоадаптера
11 Определение конфигурации устройств в системе
12 Определение размера оперативной памяти
13 Обслуживание дисковой системы
14 Работа с асинхронным последовательным адаптером
15 Расширенный сервис
16 Обслуживание клавиатуры
17 Обслуживание принтера
18 Запуск BASIC в ПЗУ, если он есть
19 Перезагрузка операционной системы
1A Обслуживание часов
1B Обработчик прерывания, возникающего, если пользователь нажал комбинацию клавиш <Ctrl+Break>
1C Программное прерывание, вызывается 18,2 раза в секунду обработчиком аппаратного прерывания таймера
1D Адрес видеотаблицы для контроллера видеоадаптера 6845
1E Указатель на таблицу параметров дискеты
1F Указатель на графическую таблицу для символов с кодами ASCII 128-255
20-5F Используется MS-DOS или зарезервировано для MS-DOS
60-67 Прерывания, зарезервированные для программ пользователя
68-6F Не используются
70 IRQ8 - прерывание от часов реального времени
71 IRQ9 - прерывание от контроллера EGA
72 IRQ10 - зарезервировано
73 IRQ11 - зарезервировано
74 IRQ12 - зарезервировано
75 IRQ13 - прерывание от арифметического сопроцессора
76 IRQ14 - прерывание от контроллера жесткого диска
77 IRQ15 - зарезервировано
78 - 7F Не используются
80-85 Зарезервировано для BASIC
86-F0 Используются интерпретатором BASIC
F1-FF Не используются

Прерывания, обозначенные как IRQ0 - IRQ15 являются аппаратными прерываниями.

 

Что это такое резидентная программа?

Обычно после завершения очередной программы MS-DOS освобождает место в памяти, которое занимала программа, чтобы загрузить на это место новую. Однако есть способ оставить программу в памяти и после ее завершения. Такая программа и будет резидентной, т. е. постоянно присутствующей в памяти.

Для чего используются резидентные программы?

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

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

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

Такие программы тоже обычно запускаются через файл autoexec.bat или при необходимости. Они перехватывают прерывания, предназначенные для работы с клавиатурой. Как только пользователь нажимает заранее определенную комбинацию клавиш, резидентная программа активизируется. Поверх имеющегося на экране изображения выводится диалоговое окно резидентной программы.

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

Задача 1.

Условие:

Найти серийный номер диска.

Способ 1: программа читает загрузочную запись диска (BOOT-сектор) в специальную структуру и затем получает из нее значение поля,

 


Листинг программы:

 

#include <stdio.h>

#include <dos.h>

#include <conio.h>

#include <ctype.h>

 

typedef struct _EBPB_

{

unsigned sectsize;

char clustsize;

unsigned ressecs;

char fatcnt;

unsigned rootsize;

unsigned totsecs;

char media;

unsigned fatsize;

unsigned seccnt;

un


Поделиться с друзьями:

Эмиссия газов от очистных сооружений канализации: В последние годы внимание мирового сообщества сосредоточено на экологических проблемах...

Индивидуальные и групповые автопоилки: для животных. Схемы и конструкции...

Поперечные профили набережных и береговой полосы: На городских территориях берегоукрепление проектируют с учетом технических и экономических требований, но особое значение придают эстетическим...

Автоматическое растормаживание колес: Тормозные устройства колес предназначены для уменьше­ния длины пробега и улучшения маневрирования ВС при...



© cyberpedia.su 2017-2024 - Не является автором материалов. Исключительное право сохранено за автором текста.
Если вы не хотите, чтобы данный материал был у нас на сайте, перейдите по ссылке: Нарушение авторских прав. Мы поможем в написании вашей работы!

0.359 с.