Главная | Обратная связь | Поможем написать вашу работу!
МегаЛекции

Лабораторная работа № 19-20.  «Изучение принципов управления программами в MS DOS». Краткие теоретические и учебно-методические материалы




Лабораторная работа № 19-20

 «Изучение принципов управления программами в MS DOS»

Цель работы: изучение принципов управления программами в MS DOS и приобретение практических навыков работы с префиксом программного сегмента и его полями.

Образовательные результаты, заявленные во ФГОС третьего поколения:

Студент должен

уметь:

- осуществлять разработку кода программного модуля на современных языках программирования;

- создавать программу по разработанному алгоритму как отдельный модуль;

- выполнять отладку и тестирование программы на уровне модуля

знать:

- основные этапы разработки программного обеспечения;

- основные принципы технологии структурного и объектно-ориентированного программирования;

- основные принципы отладки итестирования программных продуктов.

Краткие теоретические и учебно-методические материалы

по теме лабораторной работы

Для программы, вызванной на выполнение, DOS выделяет блок памяти, называемый программным сегментом. Программный сегмент всегда начинается на границе параграфа. В начале программного сегмента DOS строит PSP (Program Segment Prefix - Префикс Программного Сегмента), который занимает 256 байт.

Директива ORG 100h, которой часто начинаются Ассемблерные программы, как раз учитывает смещение начала программы относительно начала программного сегмента, равное длине PSP. При передаче программе управления сегментный адрес PSP находится в регистрах DS и ES.

При загрузке программы DOS, кроме программного сегмента, создает для нее еще и сегмент окружения (environment). Сегмент окружения содержит ASCIIZ-строки, задающие значения некоторых глобальных переменных, эти значения могут устанавливаться командой DOS SET, они доступны командным файлам и - через PSP - программам. Набор строк окружения заканчивается пустой ASCIIZ-строкой (нулем). В DOS 3. 0 и выше за ним следует еще 2-байтное число строк вызова (обычно 1) и далее - строка (или строки) вызова программы. Обычно в первую (до строк вызова) часть порождаемой программы копируется содержимое окружения программы-родителя. Программа имеет доступ к своему сегменту окружения через поле env_seg PSP, содержащее сегментный адрес окружения.
Поле JFT (Job File Table - Таблица Файлов Задачи) представляет собой массив из 20 однобайтных элементов. При открытии программой файла DOS формирует для него блок-описатель в системной таблице файлов и помещает ссылку на него (его номер) в свободный элемент JFT. Дескриптор файла, возвращаемый программе DOS при открытии файла, является номером элемента в JFT. При запуске программы первые пять элементов создаваемой для нее JFT содержат ссылки на системные файлы, остальные свободны - содержат код 0xFF.

COM-файл - это обязательно программа размером не более 64 Кбайт. Все логические сегменты этой программы (код, данные, стек) помещаются в одном физическом сегменте памяти. COM-файл содержит двоичный код-образ программы, абсолютно идентичный тому образу, который программа будет иметь в оперативной памяти. При загрузке COM-программы система размещает ее сразу вслед за ее PSP (поэтому COM-программа должна начинаться директивой ORG 100h), во все сегментные регистры записывает адрес PSP, и содержимое этих регистров не меняется в ходе выполнения программы.

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

 

Пример:

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

Программа состоит из основной программы и двух функций:

void get_DOS_version_h(void) - функция, возвращающая в глобальной переменной dos_ver старшее число номера версии DOS.

void addr_PSP (void) - функция, получающая сегментный адрес префикса программного сегмента программы и возвращающая его в глобальной переменной pid.

Переменные, глобальные для всей программы:

p_psp - указатель на структуру struct PSP,

pid - сегментный адрес PSP;

dos_ver - старшее число номера версии DOS;

i - вспомогательная переменная, используемая для просмотра таблицы файлов задачи (JFT), которая представляет собой массив из 20 элементов (хотя возможно, что их число отлично от 20, поэтому размер массива определим из поля JFT_size);

l - переменная, используемая для вывода содержимого сегмента окружения DOS и определения числа строк вызова (для версии DOS 3. 0 и выше);

s - переменная, которая вначале используется как указатель на таблицу файлов задачи, затем на строки сегмента окружения и строки вызова;

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

Данная программа производит распечатку основных полей своего PSP. Для этого префикс программного сегмента представим в виде следующей структуры:

  struct psp

  {   /* ФОРМАТ PSP */

    byte ret_op[2];   /* команда INT 20h           */

    word end_of_mem; /* вершина доступной памяти  */

    byte reserved1;

    byte old_call_dos[5]; /* старый вызов DOS          */

    void *term_ptr;   /* адрес завершения          */

    void *ctrlbrk_ptr; /* адрес обработчика Ctrl+Break */

    void *criterr_ptr; /* адрес обработчика крит. ошибок */

    word father_psp; /* PID родителя              */

   byte JFT[20];     /* таблица файлов программы  */

    word env_seg;     /* адрес окружения           */

    void *stack_ptr; /* адрес стека               */

    word JFT_size;    /* размер таблицы файлов        */

    byte *JFT_ptr;    /* адрес таблицы файлов      */

    byte reserved2[24];

    byte new_call_dos[3]; /* новый вызов DOS           */

    } *p_psp;

Поле ret_op используется для возможного завершения программы по команде RET 0, поле old_call_dos, содержит команду вызова диспетчера функций DOS. Обращение к этому полю в программе может использоваться вместо команды INT 21h, но в современных версиях DOS для этих целей лучше обращаться к полю new_call_dos.

Поле end_of_mem содержит сегментный адрес конца доступной памяти в системе. В три поля: term_ptr, ctrlbrk_ptr, criterr_ptr DOS при загрузке программы копирует содержимое векторов прерываний: 22h, 23h, 24, представляющее собой адреса обработчиков: завершения программы, комбинации клавиш Ctrl+Break, критической ошибки - соответственно. Предполагается, что программа может свободно перенаправить эти векторы на собственные обработчики соответствующих ситуаций, но от забот по восстановлению векторов программа избавляется, так как при ее завершении DOS сама восстанавливает векторы из соответствующих полей PSP завершаемой программы. Для аналогичных целей предназначено и поле stack_ptr - в нем сохраняется (а при завершении - из него восстанавливается) адрес стека, использовавшегося до вызова программы. Поле, именуемое father_psp, содержит сегментный адрес PSP родителя - программы, запустившей данную программу, обычно родителем является COMMAND. COM.

При загрузке программы DOS, кроме программного сегмента, создает для нее еще и сегмент окружения. Сегмент окружения содержит ASCIIZ-строки, задающие значения некоторых глобальных переменных, эти значения могут устанавливаться командой DOS SET, они доступны командным файлам и - через PSP - программам. Набор строк окружения заканчивается пустой ASCIIZ-строкой (нулем). В DOS 3. 0 и выше за ним следует еще 2-байтное число строк вызова (обычно 1) и далее - строка (или строки) вызова программы. Обычно в первую (до строк вызова) часть порождаемой программы копируется содержимое окружения программы-родителя. Программа имеет доступ к своему сегменту окружения через поле env_seg PSP, содержащее сегментный адрес окружения.

       Поле JFT (Job File Table - Таблица Файлов Задачи) представляет собой массив из 20 однобайтных элементов. При открытии программой файла DOS формирует для него блок-описатль в системной таблице файлов и помещает ссылку на него (его номер) в свободный элемент JFT. Дескриптор файла, возвращаемый программе DOS при открытии файла, является номером элемента в JFT. При запуске программы первые пять элементов создаваемой для нее JFT содержат ссылки на системные файлы, остальные свободны. При обработке JFT DOS использует не прямое обращение к полю JFT PSP, а косвенное - через поле JFT_ptr, а в качастве ограничителя размера JFT - не константу 20, а значение поля JFT_size PSP.

После всего, сказанного выше, не составляет труда написать программу, осуществляющую форматный вывод своего префикса программного сегмента. Для в начале необходимо определить версию DOS (с помощью функции get_DOS_version_h() и получить адрес PSP (с помощью функции addr_PSP()).

Функция get_DOS_version_h() определяет старшее число номера версии DOS, используя для этого функцию DOS 30h (прерывание 21h), которая возвращает в регистре AL старшее число номера версии, а в регистре AH - младшее число. Нас интересует только значение регистра AL.

Функция addr_PSP() возвращает сегментный адрес PSP путем использования функции DOS 62h:

    Вход: AH = 62h

    Выход:       BX = сегментный адрес PSP текущего процесса.

/* Подключение стандартных заголовков */

#include < dos. h>

#include < conio. h>

 

/* Типы данных */

#define byte unsigned char

#define word unsigned int

 

/* Описание функций */

void get_DOS_version_h(void); /* Определение версии DOS */

void addr_PSP (void);      /* Получение адреса PSP */

struct psp

{   /* ФОРМАТ PSP */

byte ret_op[2];   /* команда INT 20h           */

word end_of_mem; /* вершина доступной памяти  */

byte reserved1;

byte old_call_dos[5]; /* старый вызов DOS               */

void *term_ptr;   /* адрес завершения          */

void *ctrlbrk_ptr; /* адрес обработчика Ctrl+Break */

void *criterr_ptr; /* адрес обработчика крит. ошибок */

word father_psp; /* PID родителя              */

byte JFT[20];     /* таблица файлов программы  */

word env_seg;     /* адрес окружения           */

void *stack_ptr; /* адрес стека               */

word JFT_size;    /* размер таблицы файлов     */

byte *JFT_ptr;    /* адрес таблицы файлов      */

byte reserved2[24];

byte new_call_dos[3]; /* новый вызов DOS           */

} *p_psp;

 

word pid;           /* сегм. адрес PSP            */

int dos_ver,        /* версия DOS                */

i, l, j;

char *s;

union REGS rr;

 

main()

{

textbackground(0);

clrscr();

textattr(0x0a);

cprintf(" ---------------" );

cprintf("         Лабораторная работа N10        " );

cprintf(" ---------------" );

cprintf(" ---------------" );

cprintf("          Управление программами         " );

cprintf(" ---------------" );

textcolor(11);

get_DOS_version_h();

addr_PSP();

/* распечатка PSP */

cprintf(" \n\n       Адрес PID = %04X\n\n\r", pid);

p_psp=(struct psp *)MK_FP(pid, 0);

textcolor(10);

cprintf(" Команды: \n\r" );

cprintf(" --------\n\r" );

textcolor(14);

cprintf(" Завершение - int 20h: " );

textcolor(12);

cprintf(" %02X %02X\n\r", p_psp-> ret_op[0], p_psp-> ret_op[1]);

textcolor(14);

cprintf(" Старый вызов DOS: " );

textcolor(12);

for (i=0; i< 5; cprintf(" %02X ", p_psp-> old_call_dos[i++]));

textcolor(14);

cprintf(" \n\r Новый вызов DOS:   " );

textcolor(12);

for(i=0; i< 3; cprintf(" %02X ", p_psp-> new_call_dos[i++]));

textcolor(10);

cprintf(" \n\n\rАдреса: \n\r" );

cprintf(" -------\n\r" );

textcolor(14);

cprintf(" Конец памяти:         " );

textcolor(12);

cprintf(" %04X: 0000\n\r", p_psp-> end_of_mem);

textcolor(14);

cprintf(" Обработчик завершения: " );

textcolor(12);

cprintf(" %Fp\n\r", p_psp-> term_ptr);

textcolor(14);

cprintf(" Обработчик Ctrl+Break: " );

textcolor(12);

cprintf(" %Fp\n\r", p_psp-> ctrlbrk_ptr);

textcolor(14);

cprintf(" Обработчик критич. ошибки: " );

textcolor(12);

  cprintf(" %Fp\n\r", p_psp-> criterr_ptr);

textcolor(14);

cprintf(" Стек:                 " );

textcolor(12);

cprintf(" %Fp\n\n\r", p_psp-> stack_ptr);

textcolor(14);

cprintf(" \n\rРодитель: " );

textcolor(12);

cprintf(" %04X ", p_psp-> father_psp);

textcolor(0x8b);

cprintf(" \n\n\rНажмите любую клавишу... \n\r\7" );

getch();

clrscr();

textattr(0x0a);

cprintf(" ---------------" );

cprintf("         Лабораторная работа N19-20        " );

cprintf(" ---------------" );

cprintf(" ---------------" );

cprintf("          Управление программами         " );

cprintf(" ---------------" );

/* Распечатка таблицы файлов */

s=p_psp-> JFT_ptr;

textcolor(10);

cprintf(" \n\n\rТаблица файлов: " );

textcolor(12);

cprintf(" %Fp (%d) ", s, p_psp-> JFT_size);

textcolor(11);

if (s==(byte *)p_psp+0x18)

cprintf(" - в этом же PSP" );

cprintf(" \n\r" );

for (i=0; ++i< =p_psp-> JFT_size; cprintf(" %d ", *(s++)));

textcolor(10);

cprintf(" \n\n\rОкружение DOS: " );

textcolor(12);

cprintf(" %04X\n\r", p_psp-> env_seg);

s=(char *)MK_FP(p_psp-> env_seg, 0);

textcolor(11);

while(l=strlen(s))

{

cprintf(" %s\n\r", s);

s+=l+1;

}

if (dos_ver> 2)

{

/* для DOS 3. 0 и дальше можно получить строку вызова */

s++;

l=*((int *)s);

textcolor(10);

cprintf(" \n\rЧисло строк вызова: " );

textcolor(12);

cprintf(" %d\n\r", l);

s+=2;

textcolor(11);

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

{

cprintf(" %s\n\r", s);

s+=strlen(s)+1;

}

}

textattr(0x8b);

cprintf(" \n\n\n\n\rНажмите любую клавишу... \7" );

textattr(0x07);

cprintf(" \n\r" );

getch();

clrscr();

}

 

/* Определение версии DOS */

void get_DOS_version_h(void)

{

rr. h. ah=0x30;

intdos(& rr, & rr);

dos_ver=rr. h. al;

}

 

/* Получение адреса PSP */

void addr_PSP (void)

{

rr. h. ah=0x62;

intdos(& rr, & rr);

pid=rr. x. bx;

}

В процессе работы программы на экран быдет выведена следующая информация:

--------------------------------------------------------------------------------

---------------        Лабораторная работа N19-20        ---------------

---------------         Управление программами         ---------------

       Адрес PID = 0BA0

Команды:

--------

Завершение - int 20h: CD 20

Старый вызов DOS: 9A F0 FE 1D F0

Новый вызов DOS:   CD 21 CB

 

Адреса:

Конец памяти:         9FC0: 0000

Обработчик завершения: 0AFA: 02B1

Обработчик Ctrl+Break: 0AFA: 014A

Обработчик критич. ошибки: 0AFA: 0155

Стек:                 0E04: 0F94

Родитель: 0AFA

Таблица файлов: 0BA0: 0018 (20) - в этом же PSP

1 1 1 0 2 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1 -1

Окружение DOS: 0A1E

CONFIG=STD

COMSPEC=C: \DOS\COMMAND. COM

PROMPT=$p$g

PATH=D: \WIN; C: \; C: \DOS; C: \ARH; C: \NC; C: \BAT; D: \TP; D: \TP7; D: \BC\BIN

TEMP=d: \~TMP

Число строк вызова: 1

D: \TC\TC_LAB10. EXE

 

Задания для лабораторной работы:

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

Контрольные вопросы:

1. Что такое программный сегмент?

2. За что отвечает формат PSP?

3. Что представляет собой поле JFT?

4. Что такое СОМ-файл?

5. Что такое поле? Приведите примеры.

 

Поделиться:





Воспользуйтесь поиском по сайту:



©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...