Жанр: Учеба

Техника программирования на турбо-С

сторінка №19

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

Сохраняется вектор прерывания по критической ошибке, затем
соответствующий обработчик присоединяется к этому вектору.
Текущий статус Ctrl-Break сохраняется, и прерывания по Ctrl-Break
запрещаются.

Адрес дискового буфера прерванной программы сохраняется, и
устанавливается на соответствующий текущему контексту. То же
производится и с идентификатором процесса. Затем вызывается
утилита popup из popup.c. Функция popup сохраняет текущее
положение курсора, вызывает вашу программу, после ее выполнения
восстанавливает курсор и заканчивается. В листинге popup.c
вызывается функция exec, вы можете подставить туда имя вашей
программы.

При завершении popup адреса идентификатора процесса,
дискового буфера, вектор прерывания по критической ошибке, статус
Ctrl-Break и указатель стека восстанавливаются в те значения,
которые они имели до вызова TSR-программы, и выполняется возврат
в прерванную программу.

Удаление TSR-программы.
-----------------------------------------------------------------

При удалении пользователем TSR-программы путем запуска ее
копии с соответствующим параметром в командной строке,
нерезидентная копия вызывает резидентную через коммуникационное
прерывание. Функция terminate в resident.c проверяет, может ли
быть снята программа путем просмотра, не изменились ли значения
векторов прерываний. Если изменились, выполнение TSR-программы
приостанавливается. Если нет, она может быть снята.

Для удаления TSR-программы необходимо проделать три
процедуры. Сначала все файлы должны быть закрыты. Когда ДОС
завершает программу, она проверяет все 20 элементов в массиве
указателей файлов в PSP. Эта процедура закрывает все файлы на
уровне указателей и не затрагивает потоковых файлов. Так как эти
файлы должны быть закрыты, то terminate вызывает функцию
closefiles в popup.c, закрывающую все открытые файлы.

Вторая процедура - восстановление всех векторов прерываний
в значение, которое они имели до присоединения их к себе
TSR-программой.

Завершающим шагом является освобождение всех блоков памяти,
распределенных под TSR-программу. Память распределяется двумя
способами - из ДОС и из программы через вызов функций ДОС. Эти
два типа блоков памяти должны освобождаться тем же путем, которым
и выделялись.

Блоки памяти и управляющие блоки памяти.
-----------------------------------------------------------------

Выделяемый ДОС блок памяти содержит 16-байтный блок
управления памятью (БУП), следующий сразу за соответствующим
распределяемым блоком памяти. Он содержит следующие поля:

- однобайтный маркер, идентифицирующий БУП. Все, кроме
последнего БУП в списке, имеют значение маркера 0x4d. Последний
БУП имеет маркер 0x5a.

- двубайтный идентификатор процесса, которому принадлежит
блок памяти. Если блок свободен, это поле содержит 0.

- двубайтный размер блока памяти в параграфах. Размер БУП не
учитывается в этом значении. БУП следует в памяти непосредственно
за блоком памяти, который он представляет. Связки БУП-блок памяти
располагаются в памяти смежно. Сегментный адрес следующего БУП
равен адресу предыдущего БУП + размер блока памяти + 1. Если у
вас есть адрес первого БУП в памяти, то можете проследить всю
цепочку.


В ДОС имеется функция (естественно, недокументированная),
которая может быть использована для определения адреса первого
БУП в цепочке. Эта функция 0х52 возвращает сегментный адрес
первого БУП в регистре es и смещение в регистре bx. Эффективный
адрес этой пары сегмент: смещение, уменьшенный на 2, дает адрес
слова, содержащего сегментный адрес первого БУП в цепочке
распределенных ДОС блоков памяти.

Для освобождения памяти, занимаемой TSR-программой, должен
быть просмотрен весь список БУП. Каждый блок, содержащий
идентификатор процесса(PID) TSR-программы, освобождается путем
обращения к функции ДОС 0х49. При завершении этого процесса
TSR-программа завершается и удаляется из памяти.

ИСХОДНЫЕ ТЕКСТЫ: popup.c, resident.c
-----------------------------------------------------------------

Листинги 12.3 и 12.4 содержат текст TSR-драйвера. Эти файлы
после компиляции и связывания с вашей программой на Турбо Си
сделают ее резидентной.

Листинг 12.3.
-------------

/*---- popup.c ----*/

#include "dos.h"
#include "stdio.h"
#include "string.h"
#include "dir.h"

static union REGS rg;

unsigned sizeprogram = 48000/16;
unsigned scancode = 52;
unsigned keymask = 8;
char *signature = "POPUP";

char notefile[64];

/*--------------------------------*/
int resident(char *,void interrupt(*)());
void resinit(void);
void terminate(void);
void restart(void);
void wait(void);
void resident_psp(void);
void interrupted_psp(void);
void exec(void);
void cursor(int,int);
void curr_cursor(int *,int *);

main(argc,argv)
char *argv[];
{
void interrupt ifunc();
int ivec;

if((ivec = resident(signature, ifunc)) != 0) {
if(argc " 1) {
rg.x.ax = 0;
if(strcmp(argv[1],"quit") == 0)
rg.x.ax = 1;
else if(strcmp(argv[1],"restart") == 0)
rg.x.ax = 2;
else if(strcmp(argv[1],"wait") == 0)
rg.x.ax = 3;
if(rg.x.ax) {
int86(ivec, &rg, &rg);
return;
}
}
printf("\n Popup is already resident");
}
else {

/*load_help("tcprogs.hlp");
getcwd(notefile, 64);
if(*(notefile+strlen(notefile)-1) != '\\')
strcat(notefile,"\\");
strcat(notefile,"note.pad"); */

printf("\nResident popup is loaded");
resinit();
}
}

/*--------------------------------*/
void interrupt ifunc(bp,di,si,ds,es,dx,cx,bx,ax)
{
if(ax == 1) /* quit */
terminate();
else if(ax == 2) /* restart */
restart();
else if(ax == 3) /* wait */
wait();
}

/*--------------------------------*/
/*void closefiles()
{
extern FILE *helpfp;

resident_psp();
if(helpfp)
fclose(helpfp);
interrupted_psp();
} */

/*--------------------------------*/
void popup()
{
int x,y;

curr_cursor(&x, &y);
exec();
cursor(x,y);
}

/*--------------------------------*/
void cursor(int x, int y)
{
rg.x.ax=0x0200;
rg.x.bx=0;
rg.x.dx=((y""8) &0xff00) + x;
int86(16,&rg,&rg);
}
/*--------------------------------*/
void curr_cursor(int *x, int *y)
{
rg.x.ax=0x0300;
rg.x.bx=0;
int86(16,&rg,&rg);
*x=rg.h.dl;
*y=rg.h.dh;
}

Листинг 12.4.
-------------

/*---- resident.c ----*/

#include "dos.h"
#include "stdio.h"

static union REGS rg;
static struct SREGS seg;
static unsigned mcbseg;
static unsigned dosseg;
static unsigned dosbusy;
static unsigned enddos;
char far *intdta;
static unsigned intsp;
static unsigned intss;
static char far *mydta;
static unsigned myss;
static unsigned stack;
static unsigned ctrl_break;
static unsigned mypsp;
static unsigned intpsp;
static unsigned pids[2];
static int pidctr = 0;
static int pp;
static void interrupt (*oldtimer)();
static void interrupt (*old28)();
static void interrupt (*oldkb)();
static void interrupt (*olddisk)();
static void interrupt (*oldcrit)();
static void interrupt (*ZeroDivVector)();
void interrupt newtimer();
void interrupt new28();
void interrupt newkb();
void interrupt newdisk();
void interrupt newcrit();
extern unsigned sizeprogram;
extern unsigned scancode;
extern unsigned keymask;
static int resoff = 0;
static int running = 0;
static int popflg = 0;
static int diskflag = 0;
static int kbval;
static int cflag;

void dores(),pidaddr();
/*------------------------------------------------*/
void resinit()
{
segread(&seg);
myss=seg.ss;

rg.h.ah=0x34;
intdos(&rg, &rg);
dosseg = _ES;
dosbusy=rg.x.bx;

mydta=getdta();

pidaddr();

oldtimer=getvect(0x1c);
old28=getvect(0x28);
oldkb=getvect(9);
olddisk=getvect(0x13);

setvect(0x1c,newtimer);
setvect(0x9,newkb);
setvect(0x28,new28);
setvect(0x13,newdisk);

stack=(sizeprogram - (seg.ds - seg.cs)) * 16 - 300;

setvect(0,ZeroDivVector);

rg.x.ax=0x3100;
rg.x.dx=sizeprogram;
intdos(&rg, &rg);
}

/*------------------------------------------------*/
void interrupt newdisk(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)
{
diskflag++;
(*olddisk)();
ax=_AX;
cx=_CX;
dx=_DX;
newcrit();
flgs=cflag;
--diskflag;
}

/*------------------------------------------------*/
void interrupt newcrit(bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs)
{
ax=0;
cflag=flgs;
}

/*------------------------------------------------*/
void interrupt newkb()
{
if(inportb(0x60) == scancode) {
kbval=peekb(0,0x417);
if(!resoff && ((kbval & keymask) & keymask) == 0) { /* !!!!!!! & *
/
kbval=inportb(0x61);
outportb(0x61,kbval|0x80);
outportb(0x61,kbval);
outportb(0x20,0x20);
if(!running)
popflg=1;
return;
}
}
(*oldkb)();
}

/*------------------------------------------------*/
void interrupt newtimer()
{
(*oldtimer)();
if(popflg && peekb(dosseg, dosbusy) == 0)
if(diskflag == 0) {
outportb(0x20,0x20);
popflg=0;
dores();
}
}

/*------------------------------------------------*/
void interrupt new28()
{
(*old28)();
if(popflg && peekb(dosseg, dosbusy) != 0) {
popflg=0;
dores();
}
}

/*------------------------------------------------*/
resident_psp()
{
intpsp=peek(dosseg,*pids);
for(pp=0; pp " pidctr; pp++)
poke(dosseg,pids[pp],mypsp);
}

/*------------------------------------------------*/
interrupted_psp()
{
for(pp=0; pp " pidctr; pp++)
poke(dosseg,pids[pp],intpsp);
}
/*------------------------------------------------*/
void dores()
{
running=1;
disable();
intsp=_SP;
intss=_SS;
_SP=stack;
_SS=myss;
enable();
oldcrit = getvect(0x24);
setvect(0x24,newcrit);
rg.x.ax=0x3300;
intdos(&rg, &rg);
ctrl_break=rg.h.dl;
rg.x.ax=0x3301;
rg.h.dl=0;
intdos(&rg, &rg);
intdta=getdta();
setdta(mydta);
resident_psp();
popup();
interrupted_psp();
setdta(intdta);
setvect(0x24,oldcrit);
rg.x.ax=0x3301;
rg.h.dl=ctrl_break;
intdos(&rg, &rg);

disable();
_SP=intsp;
_SS=intss;
enable();
running=0;
}

/*------------------------------------------------*/
static int avec=0;
unsigned resident(signature, ifunc)
char *signature;
void interrupt (*ifunc)();
{
char *sg;
unsigned df;
int vec;

segread(&seg);
df=seg.ds - seg.cs;
for(vec=0x60; vec " 0x68; vec++) {
if(getvect(vec) == NULL) {
if(!avec)
avec=vec;
continue;
}
for(sg=signature; *sg; sg++)
if(*sg!=peekb(peek(0,2+vec*4)+df,(unsigned)sg))
break;
if(!*sg)
return vec;
}
if(avec)
setvect(avec, ifunc);
return 0;
}

/*------------------------------------------------*/
static void pidaddr()
{
unsigned adr=0;

rg.h.ah=0x51;
intdos(&rg, &rg);
mypsp=rg.x.bx;

rg.h.ah=0x52;
intdos(&rg, &rg);
enddos=_ES;
enddos = peek(enddos, rg.x.bx-2);
while(pidctr " 2 &&
(unsigned)((dosseg""4) + adr) " (enddos""4)) {
if(peek(dosseg, adr) == mypsp) {
rg.h.ah=0x50;
rg.x.bx=mypsp+1;
intdos(&rg, &rg);
if(peek(dosseg, adr) == mypsp +1)
pids[pidctr++]=adr;
rg.h.ah=0x50;
rg.x.bx=mypsp;
intdos(&rg, &rg);
}
adr++;
}
}

/*------------------------------------------------*/
static resterm()
{
/* closefiles();*/

setvect(0x1c,oldtimer);
setvect(9,oldkb);
setvect(0x28,old28);
setvect(0x13,olddisk);
setvect(avec, (void interrupt (*)()) 0);

rg.h.ah=0x52;
intdos(&rg, &rg);
mcbseg=_ES;
mcbseg=peek(mcbseg, rg.x.bx-2);

segread(&seg);
while(peek(mcbseg, 0) == 0x4d) {
if(peek(mcbseg, 1) == mypsp) {
rg.h.ah=0x49;
seg.es=mcbseg+1;
intdosx(&rg, &rg, &seg);
}
mcbseg+=peek(mcbseg,3)+1;
}
}

/*------------------------------------------------*/
terminate()
{
if(getvect(0x13) == (void interrupt (*)()) newdisk)
if(getvect(9) == newkb)
if(getvect(0x28) == new28)
if(getvect(0x1c) == newtimer) {
resterm();
return;
}
resoff=1;
}

/*------------------------------------------------*/
restart()
{
resoff=0;
}
/*------------------------------------------------*/
wait()
{
resoff=1;
}

TSR-ПРОГРАММА - ПРИЛОЖЕНИЕ.
-----------------------------------------------------------------

В popup.c функция popup вызывается, когда программа
TSR-драйвера обнаруживает, что нажата клавиша "горячего ключа" и
выполнение утилиты безопасно для ДОС._ .Функция popup сохраняет
текущее положение курсора, вызывает exec, затем восстанавливает
курсор и завершает выполнение._ .Функция exec - это вход в утилиту,
в данном случае программа-пример из главы 10._ .Дополнительно к
тому, что вы узнали о exec ранее, обратите внимание еще вот на
что. Она работает точно так же как и нерезидентная menu.exe, но
сейчас ее имя popup.exe, и она является TSR-программой. На
листинге 12.5 дан файл проекта для построения popup.exe в Турбо
_Си..

Листинг 12.5:popup.prj.

popup (twindow.h)
exec (twindow.h,keys.h)
tetstmove (twindow.h,keys.h)
promote (twindow.h,keys.h)
ccolor (twindow.h,keys.h)
fasttest (twindow.h)
notepad (twindow.h)
ordent (twindow.h)
maxims (twindow.h,keys.h)
poems (twindow.h,keys.h)
editor (twindow.h,keys.h)
entry (twindow.h,keys.h)
thelp (twindow.h,keys.h)
tmenu (twindow.h,keys.h)
twindow (twindow.h,keys.h)
resident
ibmpc.obj

Чтобы запустить резидентную утилиту, построенную Турбо Си по
этому проекту, введите следующую команду:

C"popup

Эта команда загрузит TSR-программу и оставит ее в памяти.
При этом она выдаст сообщение:

Resident popup is loaded.

При попытке повторного запуска будет выдано сообщение:

Popup is already resident.

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

C"popup wait
C"popup restart
C"popup quit

ПРОВЕРКА TSR-ПРОГРАММ.
-----------------------------------------------------------------

Если вы написали TSR-программу и хотите проверить ее как
резидентную программу, ваши опыты могут разочаровать вас. Прежде
всего у вас не будет возможности использовать Турбо Си для
интерактивного тестирования. TSR-программа устанавливается из
командной строки и вызывается по нажатию клавиши. Далее, до тех
пор, пока не заработает функция terminate, вам придется удалять
ее путем перезагрузки. То же придется делать при загрузке других
резидентных программ после нее. Так как ваша программа
присоединяется к прерываниям, то ее выполнение может подвешивать
вашу систему. Так что TSR-программу нелегко отлаживать как
резидентную.

Лучшее решение - это отлаживать TSR-программу как
нерезидентную. Все интерфейсы с ДОС, необходимые для установки и
действия TSR-программы, находятся в popup.c и resident.c. Почему
бы не пропустить эти операции до тех пор, пока вы надежно не
протестируете вашу программу? Как вы видите из примера, часть
программы, выполняющая основные функции, была создана и
протестирована отдельно без TSR-операций. По сути дела, эта часть
используется как пример некоторых возможностей, не связанных с
обсуждением TSR-программ. Вы можете тестировать свою программу
таким же способом.


Простейший путь тестировать вашу утилиту - это связать ее с
корневой программой, обеспечивающей функцию main и любой
начальный код, который вы потом включите в popup.c. Программа
menu.c из главы 10 является хорошим примером.

Другой способ - связать утилиту с popup.c и resident.c, как
будто бы вы строите резидентную программу, но с одним изменением.
Вместо вызова функции resinit из функции main в popup.c вставьте
вызовы popup и closefile из popup.c.Программа будет иметь те же
вид, структуру и размер, что и ее резидентная версия, но будет
функционировать как нерезидентная программа.

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

После тестирования программы вы можете перекомпоновать свою
программу как TSR-программу и протестировать ее как
резидентную. Начните с наивысшего значения переменной sizeprogram
и уменьшайте ее, как описано в главе 11.

Завершающий тест - это проверка, как ваша TSR-программа
будет вести себя в окружении себе подобных. Этот тест может
привести к непредвиденным результатам. Многие из популярных
TSR-программ не могут выполняться вместе, поэтому вы должны
выбрать те из них, которые совместимы друг с другом, и
попробовать тесты, которые загружают различные TSR-программы в
различной последовательности до тех пор, пока вы не добьетесь
нормальной работы. Укажите этот порядок в установочную процедуру
в руководстве пользователя по вашей программе.

ВЫВОДЫ.
-----------------------------------------------------------------

Используя программы из этой главы, вы будете иметь набор
средств, который позволит вам писать резидентные утилиты на Турбо
Си для IBM PC. Эти программы могут создаваться и тестироваться в
интегрированной среде Турбо Си как нормальные нерезидентные
программы для ДОС. Они могут использовать функции окон, ввода
данных, текста и меню из предыдущих глав в этой книге. После
доведения программ до рабочего состояния они могут быть
интегрированы со средствами из этой главы для превращения их в
полностью функционирующие TSR-программы.

ЭПИЛОГ.


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

Хэккеры в сердце могут почуствовать проникновенность за
работу, которая превратилась в программы из этой книги. Хэккер -
это человек, который "разбирает" компьютерные системы для того,
чтобы узнать, как они работают. Этот термин в дальнейшем потерял
свое значение ввиду его частого употребления в печати и общении,
но мы предпочитаем его начальное значение. Программист, который
интересуется техникой и принципами, лежащими в основе этих
функций, обнаружит, что многие хэккеры обнаружили то же самое. Вы
можете использовать эти средства без всяких вопросов. Но если вы
пытливая натура, то это подтолкнет вас к дальнейшему
проникновению во внутренности PC и ДОС. Если вы не работаете в
Microsoft и не имеете исходных текстов каждой из версий ДОС (или
времени на их изучение), то должны будете или сами разгадывать
секреты ДОС или узнавать их от других.

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


Список сторінок

Поділитися у соцмережах

☏ Замовлення реклами: +3805044-68-872

© Асоціація електронних бібліотек України

☝ Усі матеріали сайту (зокрема статті, зображення, рекламні оголошення тощо) призначені лише для попереднього ознайомлення. Усі права на публікації, представлені на сайті, належать їхнім законним власникам. Просимо Вас не зберігати копії інформації.