С для профессиональных программистов


Прикладная часть TSR-программы


Точкой входа в прикладную часть TSR-программы должна быть функция типа interrupt. В представленном ниже примере запуск прикладной части выполняется путем вызова функции window_main().

/* Точка входа в прикладную часть TSR-программы */

void interrupt tsr_ap()

if(!busy)

busy = !busy;

window_main();

busy = !busy;

Глобальная переменная  busy  первоначально устанавливается в

0. Прикладная часть TSR-программы не является повторно входимой, следовательно, она не должна запускаться дважды за время одного использования. Переменная busy используется как раз для того, чтобы предотвратить это. (Некоторые компиляторы Си могут создавать реентерабельные программы, но безопаснее для вас не обсуждать здесь этого вопроса).

В программы управления окнами необходимо внести некоторые изменения для того, чтобы их можно было использовать в TSR-программах. Во-первых, необходимо статически распределять память, необходимую для хранения текущего содержимого экрана, путем использования глобального массива. Вы могли привыкнуть к тому, что эта память распределялась динамически, но данный способ здесь непригоден, вследствие того, что функции динамического распределения используют системный вызов, который недопустим в TSR-программах. По этой же причине функция go_to_xy() не может быть использована для позиционирования курсора. Наконец, стандартные Си-функции sscanf() и sprintf() также не могут быть использованы (по крайней мере, в Турбо Си), потому что также осуществляют обращения к DOS. Вместо них используются функции атоi() и itoa(). Полный текст программы резидентного калькулятора представлен ниже.

/* TSR-программа, использующая прерывание печати экрана */

#include "dos.h"

#include "stdlib.h"



#define BORDER 1

#define ESC 27

#define MAX_FRAME 1

#define REV_VID 0x70

#define NORM_VID 7

#define BKSP 8

void interrupt tsr_ap();

void save_video(), restore_video();

void write_string(), write_char();

void     display_header(), draw_border();


void     window_gets();

void     window_cleol(), window();

void     calc();

char      far *vid_mem;

struct window_frame

int startx, endx, starty, endy;

int curx, cury; /* текущее положение курсора в окне */

unsigned char *p; /* указатель буфера */

char *header;                     /* сообщение в верхней части окна */

int border; /* включение/отключение бордюра */

int active; /* активация/деактивация окна */

 frame[MAX_FRAME];

char wp[4000]; /* буфер для хранения текущего содержимого экрана

/* busy установлена в 1, если программа активна, иначе - в 0 */

char busy = 0;

main()

struct address

char far *p;

 ;

/* адрес прерывания печати экрана */

struct address far *addr = (struct address far *) 20;

addr->p = (char far *) tsr_ap;

set_vid_mem();

tsr(2000);

set_vid_mem()

int vmode;

vmode = video_mode();

if((vmode!=2) && (vmode!=3) && (vmode!=7))

printf("video must be in &0 column text mode");

exit(1);

/* установить соответсвующий адрес видеопамяти */

if(vmode==7) vid_mem = (char far *) 0xB0000000;

else vid_mem = (char far *) 0xB8000000;

/* точка входа в прикладную часть TSR-программы */

void interrupt tsr_ap()

 

if(!busy)

busy = !busy;

window_main();

busy = !busy;

/* завершить, но оставить резидентной */

tsr(size)

unsigned size;

union REGS r;

r.h.ah = 49;   /* завершить, но оставить резидентной */

r.h.al = 0;                        /* код возврата */

r.x.ax = size;

int86(0x21, &r, &r);

window_main()

/* первым делом, создать структуру окна */

make_window(0, " Calculator ", 8, 20, 12, 60, BORDER);

/* для активации описанного окна используйте window() */

calc();

/*************************************************************/

/* Функции управления окнами                                                                                                       */

/*************************************************************/



/* Вывести на экран спускающееся меню */

void window(num)

int num; /* номер окна */

int vmode, choice;

int x, y;

/* сделать окно активным */

if(!frame[num].active)   /* используется не постоянно */

save_video(num);                                /* сохранить текущий экран */

frame[num].active = 1;  /* установить флаг активности */

if(frame[num].border) draw_border(num);

display_header(num); /* вывести окно */

/* Создать спускающееся окно

если окно может быть создано, возвращается 1;

иначе возвращается 0.

*/

make_window(num, header, startx, starty, endx, endy, border)

int num; /* номер окна */

char *header;  /* текст заголовка */

int startx, starty; /* координаты X,Y левого верхнего угла */

int endx, endy; /* координаты X,Y правого нижнего угла */

int border;  /* без бордюра если 0 */

register int i;

int choice, vmode;

unsigned char *p;

if(num>MAX_FRAME)

  

window_puts(0, "Too many windows\n");

return 0;

if((startx>24) || (starty>78) || (starty<0))

 

window_puts(0, "range error");

return  0;

if((endx>24) || (endy>79))

 

window_puts(0, "window won't fit");

return 0;

/* создать структуру окна */

frame[num].startx = startx; frame[num].endx = endx;

frame[num].starty = starty; frame[num].endy = endy;

frame[num].p = wp;

frame[num].header = header;

frame[num].border = border;

frame[num].active = 0;

frame[num].curx = 0; frame[num].cury = 0;

return 1;

/* Деактивировать окно и удалить его с экрана */

deactivate(num)

int num;

/* установить курсор в левый верхний угол */

frame[num].curx = 0;

frame[num].cury = 0;

restore_video(num);

/* Вывести заголовок окна в соответсвующее поле */

void display_header(num)

int num;

register int i, y, len;

y = frame[num].starty;

/* Вычислить точное значение центральной позиции заголовка

если отрицательное - заголовок не может быть выведен

*/

len = strlen(frame[num].header);



len = (frame[num].endy - y - len) / 2;

if(len<0) return; /* don't display it */

y = y +len;

write_string(frame[num].startx, y,

frame[num].header, NORM_VID);

void draw_border(num)

int num;

register int i;

char far *v, far *t;

v = vid_mem;

t = v;

for(i=frame[num].startx+1; i<frame[num].endx; i++)

v += (i*160) + frame[num].starty*2;

*v++ = 179;

*v = NORM_VID;

v = t;

v += (i*160) + frame[num].endy*2;

*v++ = 179;

*v = NORM_VID;

v = t;

for(i=frame[num].starty+1; i<frame[num].endy; i++)

v += (frame[num].startx*160) + i*2;

*v++ = 196;

*v = NORM_VID;

v = t;

v += (frame[num].endx*160) + i*2;

*v++ = 190;

*v = NORM_VID;

v = t;

write_char(frame[num].startx, frame[num].starty, 218, NORM_VID);

write_char(frame[num].startx, frame[num].endy, 191, NORM_VID);

write_char(frame[num].endx, frame[num].starty, 192, NORM_VID);

write_char(frame[num].endx, frame[num].endy, 217, NORM_VID);

/**************************************************************/

/*  Оконные функции ввода/вывода                                                                                                   */

/**************************************************************/

/* Вывести строку начиная с текущей позиции курсора

описанного окна.

Возвратить 0 если окно не активное; и 1 в противном случае.

*/

window_puts(num, str)

int num;

char *str;

/* убедиться, что окно активное */

if(!frame[num].active) return 0;

for( ; *str; str++)

window_putchar(num, *str);

return 1;

/* Вывести символ в текущую позицию курсора

описанного окна.

Возвратить 0 если окно не активное, и 1 в противном случае.

*/

window_putchar(num, ch)

int num;

char ch;

register int x, y;

char far *v;

/* убедиться, что окно активное */ if(!frame[num].active) return 0;

x = frame[num].curx + frame[num].startx + 1; y = frame[num].cury + frame[num].starty + 1;

v = vid_mem;

v += (x*160) + y*2; /* вычислить адрес */ if(y>=frame[num].endy)



return 1;

if(x>=frame[num].endx)

return 1;

if(ch=='\n')  /* символ перехода на новую строку */ x++;

y = frame[num].startx+1;

v = vid_mem;

v += (x+160) + y*2; /* вычислить адрес */ frame[num].curx++;  /* инкрементировать X */ frame[num].cury = 0; /* сбросить Y */

else

frame[num].cury++;

*v++ = ch;  /* вывести символ */

*v++ = NORM_VID; /* нормальные атрибуты символа */

window_xy(num, frame[num].curx, frame[num].cury);

return 1;

/* Установка курсора в заданную позицию окна.

Возвращает 0 при выходе за границу; не ноль в противном случае.

*/

window_xy(num, x, y)

int num, x, y;

if(x<0 || x+frame[num].startx>=frame[num].endx-1)

return 0;

if(y<0 || y+frame[num].starty>=frame[num].endy-1)

return 0;

frame[num].curx = x;

frame[num].cury = y;

return 1;

/* Считать строку из окна. */

void window_gets(num, s)

int num;

char *s;

char ch, *temp;

temp = s;

for(;;)

ch = window_getche(num);

switch(ch)

case '\r':  /* нажата клавиша ENTER */

*s='\0';

return;

case BKSP: /* возврат */

if(s>temp)

s--;

frame[num].cury--;

if(frame[num].cury<0) frame[num].cury = 0;

window_xy(num, frame[num].curx, frame[num].cury); write_char(frame[num].startx+ frame[num].curx+1,

frame[num].starty+frame[num].cury+1, ' ', NORM_VID);

break;

default:  *s = ch;

s++;

/* Ввод символа с клавиатуры в окно.

Возвращает полный 16-разрядный скан-код.

/*

window_getche(num)

int num;

union inkey

char ch[2];

int i;

 c;

if(!frame[num].active) return 0; /* window not active */

window_xy(num, frame[num].curx, frame[num].cury);

c.i = bioskey(0);                            /* обработать нажатие клавиши */

if(c.ch[0])

switch(c.ch[0])

case '\r': /* нажата клавиша ENTER */

break;

case BKSP: /* возврат */

break;

default:

if(frame[num].cury+frame[num].starty < frame[num].endy-1)

write char(frame[num].startx+ frame[num].curx+1,

frame[num].curx--;



window_xy(num, frame[num].curx, frame[num].cury);

return c.i;

/* Очистить до конца строки */

void window_cleol(num)

int num;

register int i, x, y;

x = frame[num].curx;

y = frame[num].cury;

window_xy(num, frame[num].curx, frame[num].cury);

for(i=frame[num].cury; i<frame[num].endy-1; i++)

window_putchar(num,' ');

window_xy(num, x, y);

/* Переместить курсор на одну строку вверх.

При успешном завершении вернуть ненулевое значение; в противном случае - 0.

*/

window_upline(num)

int num;

if(frame[num].curx>0)

frame[num].curx--;

window_xy(num, frame[num].curx, frame[num].cury); return 1;

return 0;

/* Переместить курсор на одну строку вниз.

При успешном завершении вернуть ненулевое значение; в противном случае - 0.

*/

window_downline(num)

int num;

if(frame[num].curx<frame[num].endx-frame[num].startx-1  frame[num].curx++;

window_xy(num, frame[num].curx, frame[num].cury);

return 1;

return 1;

/* вернуться на одну позицию назад */

window_bksp(num)

int num;

if(frame[num].cury>0)

frame[num].cury--;

window_xy(num, frame[num].curx, frame[num].cury);

window_putchar(num, ' ');

frame[num].cury--;

window_xy(num, framenum.curx, frame[num].cury);

/********************************************************/

/* Дополнительные функции                                                                                               */

/********************************************************/

/* Вывести строку с установленными атрибутами */

void write_string(x, y, p, attrib)

int x, y;

char *p;

int attrib;

register int i;

char far *v;

v = vid_mem;

v += (x*160) + y*2; /* вычислить адрес */

for(i+y; *p; i++)

 

*v++ = *p++;  /* вывести символ */

*v++ = attrib;                        /* вывести атрибуты */

/* Вывести символ с утановленными атрибутами */

void write_char(x, y, ch, attrib)

int x, y;

char ch;

int attrib;



register int i;

char far *v;

v = vid_mem;

v += (x*160) +y*2;

*v++ = ch;  /* вывести символ */

*v = attrib;                       /* вывести атрибуты */

/* Сохранить содержимое области экрана */

void save_video(num)

int num;

register int i,j;

char far *v, far *t;

char *but_ptr;

but_ptr = frame[num].p;

v = vid_mem;

t=v;

for(i=frame[num].starty; i<frame[num].endy+1; i++)

for(j=frame[num].startx; j<frame[num].endx+1; j++)

t = (v + (j*160) + i*2);

*buf_ptr++ = *t++;

*buf_ptr++ = *t;

*(t-1) = ' ';  /* очистить окно */

/* Восстановить содержимое области экрана */

void save_video(num)

int num;

register int i,j;

char far *v, far *t;

char *but_ptr;

but_ptr = frame[num].p;

v = vid_mem;

t=v;

for(i=frame[num].starty; i<frame[num].endy+1; i++)

for(j=frame[num].startx; j<frame[num].endx+1; j++)

v = t;

v += (j*160) + i*2;

*v++ = *but_ptr++;   /* вывести символ */

*v = *but_ptr++;   /* вывести атрибуты */

frame[num].active = 0; /* восстановить изображение */

/* Возвращает код текущего видеорежима */

video_mode()

union REGS r;

r.h.ah =15;  /* получить код видеорежима */

return int86(0x10, &r, &r) & 255;

/**********************************************************

калькулятор **********************************************************/

#define MAX 100

int *p;   /* указатель стека */

int *tos; /* указатель вершины стека  */

int *bos; /* указатель дна стека */

char in[80], out[80];

int stack[MAX];

/* Стековый, с постфиксной записью калькулятор

на четыре функции */

void calc()

int      answer;

int      a, b;

p =     stack;

tos     = p;

bos    = p + MAX - 1;

window(0);

do

window_xy(0, 0, 0);

window_cleol(0);

window_puts(0, " : "); /* промптер калькулятора */

window_gets(0, in);

window_puts(0, " \n ");

window_cleol(0);

switch(*in)

case '+ ':

a = pop();

b = pop();

answer = a + b;

push(a+b);

break;

case '-':



a = pop();

b = pop();

answer = b-a;

push(b-a);

break;

case '- ':

a = pop();

b = pop();

answer = b*a;

push(b*a);

break;

case '/ ':

a = pop();

b = pop();

if(a==0)

window_puts(0, "divide by 0\n");

break;

answer = b/a;

push(b/a);

break;

default:

push(atoi(in));

continue;

itoa(answer, out, 10);

window_puts(0, out);

   while(*in);

deactivate(0);

/* Поместить число в стек.

Возвратить 1 при успешном завершении; и 0, если стек переполнен

*/

push(i)

int i;

if(p>bos) return 0;

*p = i;

p++;

return 1;

/* Извлечь верхний элемент стека Возвратить 0, если стек пуст.

*/

pop()

p--;

if(p<tos)

p++;

return 0;

return *p;

Вы можете сразу вводить эту программу в ЭВМ. Для того, чтобы установить прикладную часть, запустите ее на выполнение. Для вызова калькулятора нажмите клавишу PT SCR.



window_puts(1, "\n ");

window_cleol(1);

switch(*in)

 

case '+':

a = pop();

b = pop();

answer = a+b;

push(a+b);

break;

case '-':

a = pop();

b = pop();

answer = b-a;

push(b-a);

break;

case '* ':

a = pop();

b = pop();

answer = b*a;

push(b*a);

break;

case '/ ':

a = pop();

b = pop();

if(a==0)

window_puts(0, "divide by 0\n");

break;

answer = b/a;

push(b/a);

break;

default:

push(atoi(in));

continue;

itoa(answer, out, 10);

window_puts(1, out);

   while(*in);

deactivate(1);

/* Поместить число в стек.

Возвратить 1 в случае успеха и 0 если стек переполнен

*/

push(i)

int i;

if(p>bos) return 0;

*p = i;

p++;

return 1;

/* Извлечь верхний элемент из стека. Возвратить 0 если стек пуст.

*/

pop()

p--;

if(p<tos)

p++;

return 0;

return *p;

/**********************************************************/

/*                                            Всплывающая записная книжка                                                */

#define MAX_NOTE 10

#define BKSP 8

char notes[MAX_NOTE] [80];

void notepad()

static first = 1;

register int i, j;

union inkey

char ch[2];

int i;

 c;

char ch;

/* инициализировать массив записей если это необходимо */

if(first)

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

*notes[i] = '\0 ';

first = !first;

window(0);

/* вывести существующие записи */

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

if(*notes[i]) window_puts(0, notes[i]);

window_putchar(0, '\n ');

i = 0;

window_xy(0, 0, 0);

for(;;)

c.i = bioskey(0);  /* обработать нажатие клавиши */

if(tolower(c.ch[1])==59)  /* F1 для выхода */

deactivate(0);

break;

/* если обычная клавиша */

if(isprint(c.ch[0]) || c.ch[0]==BKSP)

window_cleol(0);

notes[i][0] = c.ch[0];

j = 1;

window_putchar(0, notes[i][0]);

do

 

ch = window_getche(0);

if(ch == BKSP)

if( j>0 )

j--;

window_bksp(0);

else

notes[i][j] = ch;

j++;

 while(notes[i][j-1]! = '\r ');

notes[i][j-1] = '\0 ';

i++;

window_putchar(0, '\n ');

else  /* если специальная клавиша */

switch(c.ch[1])

case 72: /* стрелка вверх */

if(i>0)

i--;

window_upline(0);

break;

case 80: /* стрелка вниз */

if(i<MAX_NOTE-1)

i++;

window_downline(0);

break;


Содержание раздела