Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

MS-DOS для программиста

© Александр Фролов, Григорий Фролов
Том 18, М.: Диалог-МИФИ, 1995, 254 стр.

[Назад] [Содеожание] [Дальше]

5.6. Листинги программы TSRDEMO


Листинг 5.1. Файл tsrdemo\tsrdemo.c


#include <ctype.h>
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
#include <dos.h>
#include <bios.h>
#include <process.h>
#include "tsrdemo.h"

// Структуры для работы с функциями прерываний
union  REGS   regs;
struct SREGS  sregs;

// Идентификатор для мультиплексного прерывания
unsigned char tsrid = 0xff;

// Указатель на стек, который будет использован
// после активизации TSR
char far *tsr_stack;

// Флаг активности TSR. Равен 1, если TSR активна
int tsr_already_active = 0;

// Флаг активизации TSR. Равен 1,
// если нажали клавишу активизации
int popup_while_dos_busy = 0;

// Флаг обработки прерывания INT 28h .
// Равен 1, когда работает INT 28
int int_28_in_progress = 0;

// Флаг обработки прерывания INT 13h.
// Равен 1, когда работает INT 13
int unsafe_flag = 0;

// Код клавиши, при помощи которой была
// активизирована TSR
unsigned keycode;

// Сегмент PSP  прерванного процесса
unsigned foreground_psp;

// Сегмент и смещение блока DTA 
// прерванного процесса
unsigned foreground_dta_seg;
unsigned foreground_dta_off;

// Адрес, по которому передается управление
// при деинсталляции TSR
unsigned long ExitAddress;

// Область сохранения расширенной
// информации об ошибках
struct ExtErr ErrInfo;

// Указатели на флаги MS-DOS
char far *indos_ptr = 0;
char far *crit_err_ptr=0;

// Область сохранения для старых прерываний
INTADDR old_int8, old_int9, old_int10,
	old_int13, old_int1b, old_int23;
INTADDR old_int24, old_int28,
	old_int2f, old_int1c;

typedef unsigned int (far *s_arrayptr);
typedef void interrupt (*fptr)(void);

// Определение размера кучи и стека
// с учетом области статических данных
extern unsigned _heaplen = STACK_SIZE +
   HEAP_RESERVED + STATIC_SIZE;
extern unsigned _stklen  = STACK_SYSTEM;

// ========================================
// Вход в программу - функция main()
// ========================================
void main(int argc, char *argv[])
{
  int tsrsize, i;

  printf("\n\nРезидентная программа TSRDEMO,"
	 " v1.1, (C) Фролов А.В., 1995\n");

  // Проверяем версию MS-DOS
  if(_osmajor < 4)
  {
    printf("\nИзвините, вы пользуетесь слишком"
           "старой версией MS-DOS");
    return;
  }

  // Если есть параметр, проверяем его
  if(argc > 1)
  {
    // Выгрузка из памяти
    if(argv[1][0] == 'u') unload();

    // Вывод инструкции на экран
    else
      printf("\nЗапуск TSRDEMO:\n"
	 "tsrdemo   : загрузка в память\n"
	 "tsrdemo u : выгрузка из памяти\n"
	 "<Ctrl+R>  : запись содержимого экрана"
	 " в файл !grabXXX.scr");
    return;
  }

  // Выход, если уже загружена
  if(tsrloaded())
  {
    printf("Программа TSRDEMO уже загружена\n"
	   "Введите 'tsrdemo u' для выгрузки");
    return;
  }

  // Инициализация TSR
  if(!tsrinit())
  {
    printf("Мало памяти для загрузки TSR");
    return;
  }

  // Оставляем резидентно в памяти
  tsrsize =  (_DS - _CS) + (_SP / 16);
  _dos_keep (0, tsrsize + 1);
}

// ========================================
// activate_tsr
// Функция вызывается при активизации TSR
// ========================================
void activate_tsr()
{
  // Для работы в активном режиме
  // устанавливаем свой стек
  set_stack();

  // Определяем, когда TSR активизировалась -
  // во время работы MS-DOS или нет
  if(DosBusy() && !int_28_in_progress)

    // Если активизировалась, когда
    // работает функция MS-DOS, просто
    // устанавливаем флаг. Активизация будет
    // отложена до вызова INT 8h или INT 28h 
    popup_while_dos_busy = 1;

  else
  {
    // Если никакая функция прерывания MS-DOS
    // не вызвана, сбрасываем флаг и начинаем
    // активизацию TSR
    popup_while_dos_busy = 0;

    // Сохраняем адреса прерываний
    // <Ctrl+Break>, <Ctrl+C> и прерывания по
    // критической ошибке ввода/вывода
    old_int1b = _dos_getvect (0x1b);
    old_int23 = _dos_getvect (0x23);
    old_int24 = _dos_getvect (0x24);

    // Устанавливаем свои обработчики прерываний
    _dos_setvect (0x1b, new_int1b);
    _dos_setvect (0x23, new_int23);
    _dos_setvect (0x24, new_int24);

    // Сохраняем текущий PSP  и устанавливаем свой PSP
    foreground_psp = GetPSP();
    SetPSP(_psp);

    // Сохраняем текущую область DTA 
    regs.h.ah = 0x2f;
    intdosx (&regs, &regs, &sregs);
    foreground_dta_seg = sregs.es;
    foreground_dta_off = regs.x.bx;

    // Устанавливаем свою область DTA .
    // Используем DTA  в своем PSP 
    regs.h.ah = 0x1a;
    regs.x.dx = 0x80;
    sregs.ds = _psp;
    intdosx (&regs, &regs, &sregs);

    // Сохраняем расширенную информацию
    // об ошибках
    GetExtErr(&ErrInfo);

    // Очищаем буфер клавиатуры 
    while(_bios_keybrd(_KEYBRD_READY))
    _bios_keybrd(_KEYBRD_READ);

    // Вызываем функцию, выполняющую все то,
    // что должно быть сделано
    // при активизации TSR
    application();

    // Очищаем буфер клавиатуры 
    while(_bios_keybrd(_KEYBRD_READY))
      _bios_keybrd(_KEYBRD_READ);

    // Восстанавливаем информацию об ошибках
    SetExtErr(&ErrInfo);

    // Восстанавливаем DTA 
    regs.h.ah = 0x1a;
    regs.x.dx = foreground_dta_off;
    sregs.ds  = foreground_dta_seg;
    intdosx (&regs, &regs, &sregs);

    // Восстанавливаем PSP 
    SetPSP(foreground_psp);

    // Восстанавливаем адреса прерываний
    // <Ctrl+Break>, <Ctrl+C> и прерывания по
    // критической ошибке ввода/вывода
    _dos_setvect (0x1b, old_int1b);
    _dos_setvect (0x23, old_int23);
    _dos_setvect (0x24, old_int24);
  }

  // Восстанавливаем стек и переводим TSR
  // в неактивное систояние
  restore_stack();
}

// ========================================
// new_int1c
// Обработчик прерывания 1Ch.
// Выводит в правом верхнем углу мигающие
// символы "*" и "+", сигнализирующие
// о нормальной работе TSR
// ========================================
void interrupt new_int1c()
{
  s_arrayptr screen[80]; // видеопамять
  static int count;      // счетчик

  // Для цветного дисплея адрес сегмента
  // видеобуфера равен B800h, для ч/б - B000.
  // Устанавливаем указатель на видеобуфер
  screen[0] = (s_arrayptr) MK_FP (0xB800,0);

  // Увеличиваем содержимое счетчика
  // с ограничением
  count++;
  count %= 6;

  // Выводим символ, соответствующий значению
  // счетчика, в правый верхний угол экрана
  screen[0][79] =
    ((count > 2) ? '*' : '+') + ATTR;

  // Передаем управление по цепочке прерываний
  _chain_intr (old_int1c);
}

// ========================================
// new_int2f
// Новый обработчик мультиплексного
// прерывания INT 2Fh
// ========================================
#pragma argsused
void interrupt far new_int2f(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax)
{
  // Если ax == 0xff00, выполняется проверка,
  // была ли данная TSR загружена ранее
  // Возвращаем 0x00ff. Это означает, что
  // программа уже загружена
  if(ax == 0xff00) ax = 0x00ff;

  // Если ax == 0xff01, была запрошена
  // выгрузка программы из памяти.
  // Пытаемся ее выполнить
  else if(ax == 0xff01)
  {
    // Записываем переданный адрес завершения
    ExitAddress = ((long)bx << 16) + dx;

    // Выполняем выгрузку TSR из памяти,
    // если она неактивна
    if(!tsr_already_active)
    {
      _enable(); // разрешаем прерывания
      tsr_exit(); // пытаемся выгрузить TSR

      // Если попали управление передано в это
      // место программы, значит деинсталляция
      // не удалась,так как какая-то программа
      // уже перехватила наши прерывания.
      // В этом случае устанавливаем признак
      // неудачной выгрузки и блокируем
      // работу TSR
      ax = 0xFFFF;
      tsr_already_active = -ax;
    }
  }

  // Если TSR активна, передаем
  // управление по цепочке
  else _chain_intr (old_int2f);
}

// ========================================
// new_int28
// Новый обработчик прерывания INT 28h 
// Вызывается MS-DOS, когда она неактивна.
// Используется для активизации TSR
// ========================================
#pragma argsused
void interrupt far new_int28(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax)
{
  // Увеличиваем счетчик рекурсивных
  // вызовов прерывания INT 28h 
  int_28_in_progress++;

  // Если это безопасно, активизируем TSR.
  // Учитываем возможность рекурсивного вызова
  // прерывания INT 28h .
  // Не запускаемся, если TSR уже активизирована,
  // если вызвано прерывание INT 13h,
  // предназначенное для работы с диском, или
  // если есть рекурсия при вызове INT 28h 
  if(popup_while_dos_busy && (!Int28DosBusy())
     && !tsr_already_active && !unsafe_flag)
  {
    // Устанавливаем флаг активизации
    tsr_already_active = 1;

    activate_tsr(); // активизируем TSR

    // Сбрасываем флаг активизации
    tsr_already_active = 0;
  }

  // Уменьшаем счетчик рекурсивных вызовов INT 28
  int_28_in_progress--;

  // Передаем управление по цепочке прерываний
  _chain_intr (old_int28);
}

// ========================================
// new_int9
// Новый обработчик аппаратного прерывания
// от клавиатуры INT 9h
// ========================================
#pragma argsused
void interrupt far new_int9(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax)
{
  // Если TSR активна, ничего не делаем,
  // передавая управление по цепочке
  if(tsr_already_active) _chain_intr (old_int9);

  // Проверяем, была ли нажата клавиша <Ctrl>.
  // Если нет, передаем управление по цепочке.
  keycode = inp(KEYBOARD_PORT);
  if((_bios_keybrd(_KEYBRD_SHIFTSTATUS) & ShiftKey)
    != ShiftKey)
	 _chain_intr (old_int9);

  // Проверяем коды активизации. Если не наши коды,
  // передаем управление по цепочке.
  if(!(keycode == HotKeyRecording))
	_chain_intr (old_int9);

  // Если немедленная активизация невозможна,
  // устанавливаем флаг запроса на активизацию
  // при занятой MS-DOS. Активизация будет
  // выполнена при первой же возможности
  // обработчиками INT 8 или INT 28h 
  popup_while_dos_busy = 1;

  // Завершаем обработку аппаратного прерывания
  asm cli
  asm in al, 61h
  asm mov ah, al
  asm or al, 80h
  asm out 61h, al
  asm xchg ah, al
  asm out 61h, al
  asm mov al, 20h
  asm out 20h, al
  asm sti
}

// ========================================
// new_int8
// Новый обработчик прерывания INT 8h
// Вызывается по прерываниям  таймера
// Используется для активизации TSR
// ========================================
#pragma argsused
void interrupt far new_int8(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax)
{
  // Если это безопасно, активизируем TSR
  // Не запускаемся, если TSR уже активизирована,
  // если вызвано прерывание INT 13h для
  // работы с диском, если MS-DOS занята
  if(!tsr_already_active   &&
      popup_while_dos_busy &&
     !DosBusy() && !unsafe_flag)
  {
    // Активизация TSR
    popup_while_dos_busy = 0;
    tsr_already_active = 1;

    // Вызываем старый обработчик INT 8h
#pragma warn -pro
    (*old_int8)();
#pragma warn +pro

    _enable(); // разрешаем прерывания
    activate_tsr(); // активизируем TSR
    tsr_already_active = 0;
  }

  // Если активизация невозможна, передаем
  // управление по цепочке
#pragma warn -pro
  else (*old_int8)();
#pragma warn +pro
}

// ========================================
// new_int1b
// Обработчик прерывания <Ctrl+Break>
// ========================================
#pragma argsused
void interrupt far new_int1b(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax)
{
  // Игнорируем попытку прервать TSR
}

// ========================================
// new_int23
// Обработчик прерывания <Ctrl+C>
// ========================================
#pragma argsused
void interrupt far new_int23(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax)
{
  // Игнорируем попытку прервать TSR
}

// ========================================
// new_int24
// Обработка критической ошибки
// ========================================
#pragma argsused
void interrupt far new_int24(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax)
{
  ax = 3; // возвращаем код ошибки 3
}

// ========================================
// RestoreIntVect
// Восстановление векторов прерываний.
// Возможно только в том случае, если
// они не были еще раз переустановлены
// уже после запуска TSR
// ========================================
int RestoreIntVect(int Vect,
  INTADDR NewInt, INTADDR OldInt)
{
  // Сравниваем текущий вектор прерывания с тем
  // значением, которое установил для себя TSR
  if(NewInt == _dos_getvect (Vect))
  {
    _dos_setvect (Vect, OldInt);
    return 0; // восстановили
  }
  return 1;   // не смогли восстановить
}

// ========================================
// tsr_exit
// Выгрузка резидентной программы
// ========================================
void tsr_exit(void)
{
  set_stack(); // используем стек TSR

  // Восстанавливаем обработчики прерываний,
  // если они не были переназначены
  // после запуска TSR
  _disable();
  if(!(
    RestoreIntVect(0x1c, new_int1c, old_int1c) |
    RestoreIntVect(8, new_int8, old_int8)      |
    RestoreIntVect(9, new_int9, old_int9)      |
    RestoreIntVect(0x13, new_int13, old_int13) |
    RestoreIntVect(0x28, new_int28, old_int28) |
    RestoreIntVect(0x2f, new_int2f, old_int2f)))
  {

    // Устанавливаем родительский PSP ,
    // записанный в нашем PSP 
    *(int far *)(((long)_psp << 16) + PSP _PARENT_PSP)
      = GetPSP();

    // Устанавливаем в нашем PSP  адрес завершения
    *(long far *)(((long)_psp << 16) + PSP _TERMINATE)
      = ExitAddress;

    // Устанавливаем наш PSP 
    SetPSP(_psp);
    _enable();

    bdos(DOS_EXIT, 0, 0); // завершаем работу TSR
  }

  // Если прерывания отсоединить не удалось,
  // восстанавливаем стек и возвращаем
  // управление
  restore_stack();
  _enable();
}

// ========================================
// tsrinit
// Инициализация резидентной программы
// ========================================
int tsrinit(void)
{
  char * wk_ptr;
  unsigned far *fp;

// Резервируем место для области,
// которая будет использована при
// активизации TSR для динамического
// выделения памяти
  wk_ptr = malloc(HEAP_RESERVED);
    if(wk_ptr == NULL) return 0;

  // Заказываем стек для резидентной программы
  tsr_stack = malloc(STACK_SIZE);
    if(tsr_stack == NULL) return 0;

  // Устанавливаем указатель стека,
  // который будет использоваться при
  // активизации TSR
  tsr_stack += STACK_SIZE;

  // Возвращаем зарезервированную память
  free(wk_ptr);

  // Инициализируем указатель на флаг InDos 
  getInDosFlag();

  // Запрещаем все прерывания
  _disable();

  // Получаем старое значение векторов прерываний
  old_int1c = _dos_getvect (0x1C);
  old_int2f = _dos_getvect (0x2F);
  old_int8  = _dos_getvect (8);
  old_int9  = _dos_getvect (9);
  old_int13 = _dos_getvect (0x13);
  old_int28 = _dos_getvect (0x28);

  // Сохраняем адрес обработчика INT 13h
  get_int_13();

  // Устанавливаем новые обработчики прерываний
  _dos_setvect (0x1C, (fptr)new_int1c);
  _dos_setvect (0x2F, (fptr)new_int2f);
  _dos_setvect (8,    (fptr)new_int8);
  _dos_setvect (9,    (fptr)new_int9);
  _dos_setvect (0x13, (fptr)new_int13);
  _dos_setvect (0x28, (fptr)new_int28);

// Разрешаем прерывания
  _enable();

  // Освобождаем блок среды MS-DOS
   FP_SEG (fp) = _psp;
   FP_OFF (fp) = 0x2c;
   _dos_freemem(*fp);

   return 1;
}

// ========================================
// tsrloaded
// Проверяем, была ли уже загружена программа.
// Если была, завершаемся без установки
// ========================================
int tsrloaded(void)
{
  // Проверяем, была ли уже запущена программа
  // Для этого вызываем мультиплексное прерывание
  // со специальным кодом 0xff00
  regs.h.ah = tsrid;
  regs.h.al = 0x00;
  int86 (0x2f, &regs, &regs);
  if(regs.x.ax == 0x00ff)
    return 1;
  else
    return 0;
}


// ========================================
// unload
// Выгрузка из памяти
// ========================================
void unload(void)
{
   // Вызываем программу выгрузки TSR из
   // оперативной памяти
   switch (tsrunload())
   {
     case 1:
       printf("TSRDEMO не была запущена");
       break;

     case 2:
       printf("TSRDEMO успешно выгружена из памяти");
       break;

     default:
       printf("TSRDEMO отключена, но не выгружена");
       break;
   }
}

// ========================================
// getInDosFlag
// Инициализация указателей на флаг InDos 
// ========================================
void getInDosFlag(void)
{
  regs.h.ah = 0x34;
  intdosx (&regs, &regs, &sregs);

  // Указатель на флаг InDos  возвращается в ES:BX
  FP_SEG (indos_ptr) = sregs.es;
  FP_OFF (indos_ptr) = regs.x.bx;

  // Находим флаг критических ошибок
  regs.x.ax = 0x5D06;
  intdosx (&regs,&regs,&sregs);

  // Указатель на флаг находится в DS:SI
  FP_SEG (crit_err_ptr) = sregs.ds;
  FP_OFF (crit_err_ptr) = regs.x.si;
}

// ========================================
// DosBusy
// Возвращает 0xFFFF, если MS-DOS занята
// (если флаги не установлены)
// ========================================
int DosBusy(void)
{
  if(indos_ptr && crit_err_ptr)
    return(*crit_err_ptr || *indos_ptr);
  else
    return 0xFFFF;
}

// ========================================
// Int28DosBusy
// Функция возвращает ненулевое значение,
// если значение флага InDOS > 1 или
// установлен флаг критической ошибки.
// В этом случае MS-DOS занята
// ========================================
int Int28DosBusy(void)
{
  if(indos_ptr && crit_err_ptr)
    return (*crit_err_ptr || (*indos_ptr > 1));
  else
    return 0xFFFF;
}

// ========================================
// GetExtErr
// Получение расширенной информации об ошибках
// ========================================
void GetExtErr(struct ExtErr * ErrInfo)
{
  union  REGS  regs;

  regs.h.ah = 0x59;
  regs.x.bx = 0;
  intdos (&regs,&regs);
  ErrInfo->errax = regs.x.ax;
  ErrInfo->errbx = regs.x.bx;
  ErrInfo->errcx = regs.x.cx;
}

// ========================================
// SetExtErr
// Установка расширенной информации об ошибках
// ========================================
void SetExtErr(struct ExtErr near * ErrInfo)
{
  union  REGS  regs;
  struct SREGS  segregs;

  regs.x.ax = 0x5d0a;
  regs.x.bx = 0;

  // Запись адреса информации об ошибке в DS:DX
  segread(&segregs);
  regs.x.dx = (int) ErrInfo;
  intdosx (&regs,&regs,&segregs);
}


// ========================================
// GetPSP
// Получить адрес текущего блока PSP 
// ========================================
unsigned GetPSP(void)
{
  regs.h.ah = 0x62;
  intdos (&regs,&regs);
  return regs.x.bx;
}

// ========================================
// SetPSP
// Установить текущий PSP 
// ========================================
void SetPSP(unsigned segPSP)
{
  if(!crit_err_ptr)
    // Нельзя вызывать InitInDos
    return;

  *crit_err_ptr = 0xFF;
  regs.h.ah = 0x50;
  regs.x.bx = segPSP;

  intdos (&regs,&regs);
  *crit_err_ptr = 0;
}


Листинг 5.2. Файл tsrdemo\tsrdemo.h


// Коды клавиш
#define HotKeyRecording 0x13 // "R"
#define ShiftKey        0x04 // <Ctrl>

// Экранный атрибут  - голубой на сером
#define ATTR 0x7900

// Стек TSR при запуске
#define STACK_SYSTEM	512

// Размер кучи, доступный для использования
// в момент активизации
#define HEAP_RESERVED   2048

// Размер стека, доступного для использования
// в момент активизации
#define STACK_SIZE      1024

// Размер области статических данных
#define STATIC_SIZE     2000

// Адрес завершения в нашем PSP 
#define PSP _TERMINATE   0x0A

// Родительский PSP  из нашего PSP
#define PSP _PARENT_PSP  0x16

// Функция MS-DOS для завершения программы
#define DOS_EXIT        0x4C

// Порт данных клавиатуры
#define KEYBOARD_PORT   0x60

struct ExtErr
{
  unsigned int errax;
  unsigned int errbx;
  unsigned int errcx;
};

void interrupt far new_int2f(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax);
void interrupt far new_int28(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax);
void interrupt far new_int9(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax);
void interrupt far new_int8(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax);
void interrupt far new_int1b(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax);
void interrupt far new_int23(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax);
void interrupt far new_int24(
  unsigned bp, unsigned di, unsigned si,
  unsigned ds, unsigned es, unsigned dx,
  unsigned cx, unsigned bx, unsigned ax);

typedef void (interrupt far *INTADDR)();

void far idle_int_chain(void);

void interrupt far new_int10(void);
void interrupt far new_int13(void);
void interrupt far new_int25(void);
void interrupt far new_int26(void);

void far timer_int_chain(void);

extern char far * indos_ptr;
extern char far * crit_err_ptr;

void main(int argc,char *argv[]);
void Beep(void);
void application(void);
void unload(void);
int  tsrloaded(void);
int  tsrinit(void);
void getInDosFlag(void);
void get_int_13(void);
int  DosBusy(void);
int  Int28DosBusy(void);
void GetExtErr(struct ExtErr * ErrInfo);
void SetExtErr(struct ExtErr near * ErrInfo);
unsigned GetPSP(void);
void SetPSP(unsigned segPSP);
void activate_tsr(void);
void tsr_exit(void);
void usage(char *);
int RestoreIntVect(int Vect, INTADDR NewInt, INTADDR OldInt);
void set_stack(void);
void restore_stack(void);
int  tsrunload(void);


Листинг 5.3. Файл tsrdemo\tsrlib.asm


;// TSRLIB.ASM - дополнительные функции

  .MODEL small
  .DATA

  public  _new_int13, _get_int_13
  public  _tsrunload
  public  _set_stack, _restore_stack

;// Стек для TSR
  extrn   _tsr_stack:near

;// Флаг обработки прерывания INT 13h.
;// Равен 1, когда работает INT 13
  extrn   _unsafe_flag:near

;// Старый вектор прерывания INT 13h
  extrn   _old_int13:near

;// Идентификатор программы для
;// мультиплексного прерывания INT 2Fh
  extrn   _tsrid:near

;// Область сохранения
old_int13 dd  0
_ds_old   dw  0
_ss_old   dw  0
_sp_old   dw  0

  .CODE

;//-------------------------------------------
;// void tsrunload(void)
;// Функция выгрузки TSR из памяти
;//-------------------------------------------
_tsrunload proc
   push di
   push si
   push bp

;// Сохраняем указатель на стек
   mov word ptr _ss_old, ss
   mov word ptr _sp_old, sp

;// Сохраняем регистр DS
   mov cs:_ds_old, ds

;// Устанавливаем BX:DX на адрес завершения
   mov bx, cs
   mov dx, offset ExitTSR

;// Вызываем мультиплексное прерывание,
;// передавая ему команду завершения
   mov ah, byte ptr _tsrid
   mov al, 01h
   int 2fh

;// Если TSR завершилась, мы не попадаем на
;// команду jmp, а переходим
;// по адресу ExitTSR
   jmp short NotExit

ExitTSR:

;// Восстанавливаем DS и стек
   mov     ax,cs:_ds_old
   mov     ds,ax

;// Признак успешной деинсталляции
   mov     al,2

;// Восстанавливаем стек
   mov     ss, word ptr _ss_old
   mov     sp, word ptr _sp_old

NotExit:
;// Преобразуем возвращенное значение в
;// двойное слово
   cbw

   pop bp
   pop si
   pop di
   ret
_tsrunload endp

;//-------------------------------------------
;// void get_int_13(void)
;// Пересылка указателя на INT 13
;// в наш сегмент данных
;//-------------------------------------------
_get_int_13 proc
  push    es
  push    bx

;// Указатель на INT 13h
  les     bx,  dword ptr _old_int13
  mov     word ptr cs:old_int13, bx
  mov     word ptr cs:old_int13 + 2, es

  pop     bx
  pop     es
  ret
_get_int_13 endp

;//-------------------------------------------
;// void set_stack(void)
;// Сохранить старый стек и установить
;// стек, который будет использован для TSR
;//-------------------------------------------
_set_stack proc

;// Получаем смещение и сегмент
;// для возврата
   pop ax
   pop bx

;// Сохраняем стек
   mov word ptr _ss_old, ss
   mov word ptr _sp_old, sp

;// Устанавливаем стек для TSR
   mov ss, word ptr _tsr_stack + 2
   mov sp, word ptr _tsr_stack

;// Загружаем стек для возврата
   push    bx
   push    ax

   ret
_set_stack  endp

;//-------------------------------------------
;// void restore_stack(void) -
;// Восстанавливаем стек
;//-------------------------------------------
_restore_stack  proc
;// Получаем смещение и сегмент
;// для возврата
  pop cx
  pop bx

;// Сохраняем старый стек
  mov word ptr _tsr_stack + 2, ss
  mov word ptr _tsr_stack, sp

;// Восстанавливаем стек
  mov ss,word ptr _ss_old
  mov sp,word ptr _sp_old

;// Загружаем стек для возврата
  push bx
  push cx

  ret
_restore_stack  endp

;//-------------------------------------------
;// void far new_int13(void)
;// Новый обработчик INT 13h
;//-------------------------------------------
_new_int13 proc far
  push    ax
  push    ds

;// Устанавливаем DS на сегмент данных TSR
  mov ax, DGROUP
  mov ds, ax

;// Увеличиваем флаг _unsafe_flag
  inc word ptr _unsafe_flag

;// Восстанавливаем DS и AX
  pop ds
  pop ax

;// Вызываем прерывание
  pushf
  call    cs:old_int13

  push    ax
  push    ds
  mov ax, DGROUP
  mov ds, ax

;// Уменьшаем флаг _unsafe_flag
  dec word ptr _unsafe_flag

  pop ds
  pop ax

  ret 2
_new_int13 endp

  end


Листинг 5.4. Файл tsrdemo\applicat.c


#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <dos.h>
#include "tsrdemo.h"

int write_buf(void);

extern unsigned keycode;
int name_counter = 0;

// ========================================
// application(void)
// Вызывается при активизации TSR. Из этой
// функции можно вызывать прерывания MS-DOS
// ========================================
void application(void)
{
  if(keycode == HotKeyRecording)
  {
      write_buf();
      Beep();
  }
}

// ========================================
// Beep
// Выдача звукового сигнала
// ========================================
void Beep(void)
{
  union REGS  regs;
  regs.x.ax = 0x0e07;
  regs.x.bx = 0x0000;
  int86 (0x10, &regs, &regs);
}

// ========================================
// get_vmode
// Функция возвращает номер
// текущего видеорежима
// ========================================
int get_vmode(void)
{
  char far *ptr;

  // Получаем указатель на байт, содержащий
  // номер текущего видеорежима
  ptr = (char far*)MK_FP (0x40, 0x49);
  return(*ptr);
}

// ========================================
// get_vbuf
// Функция возвращает сегментный адрес
// видеопамяти. Учитывается содержимое
// регистров смещения адреса видеобуфера
// ========================================
int get_vbuf(int vmode)
{
  unsigned vbase;
  unsigned adr_6845;
  unsigned high;
  unsigned low;
  unsigned offs;

  // В зависимости от видеорежима базовый адрес
  // видеопамяти может быть 0xb000 или 0xb800
  vbase = (vmode == 7) ? 0xb000 : 0xb800;

  // Получаем адрес порта видеоконтроллера 
  adr_6845 = *(unsigned far*)(MK_FP (0x40, 0x63));

  // Считываем содержимое регистров 12 и 13
  // видеоконтроллера 
  outp(adr_6845, 0xc);
  high = inp(adr_6845 + 1);

  outp(adr_6845, 0xd);
  low = inp(adr_6845 + 1);

  offs = ((high << 8) + low) >> 4;

  // Добавляем к базовому адресу видеопамяти
  // смещение, взятое из регистров видеоконтроллера 
  vbase += offs;

  return(vbase);
}

// ========================================
// get_column
// Функция возвращает количество символов в строке
// для текущего видеорежима
// ========================================
int get_column(void)
{
  return(*(int _far *)(MK_FP (0x40, 0x4a)));
}

// ========================================
// get_row
// Функция возвращает количество строк
// для текущего видеорежима
// ========================================
int get_row(void)
{
  unsigned char ega_info;
  ega_info =
    *(unsigned char far*)(MK_FP (0x40, 0x87));

  // Если тип контроллера не EGA , то размер
  // экрана по вертикали составляет 25 строк.
  // Если установлен контроллер EGA , число
  // строк находится в области данных
  // BIOS  по адресу 0040:0084.

  if(ega_info == 0 || ( (ega_info & 8) != 0) )
  {
    return(25);
  }
  else
  {
    return((*(unsigned char far *)
      (MK_FP (0x40, 0x84))) + 1);
  }
}

// ========================================
// write_buf
// Функция записи содержимого видеобуфера в
// файл
// ========================================
int write_buf(void)
{
  // Видеопамять состоит из байтов символов и байтов
  // атрибутов. Нам нужны байты символов chr.
  typedef struct _VIDEOBUF_
  {
    unsigned char chr;
    unsigned char attr;
  } VIDEOBUF;

  VIDEOBUF far *vbuf;
  int i, j, k, max_col, max_row;
  FILE *out_file;
  char fname[20], ext[8];

  // Определяем видеорежим 
  i = get_vmode();

  // Для графического режима ничего не записываем
  if(i > 3 && i != 7) return(-1);

  // Устанавливаем указатель vbuf на видеобуфер
  vbuf = (VIDEOBUF far *)MK_FP (get_vbuf(i), 0);

  // Определяем размеры экрана
  max_col = get_column();
  max_row = get_row();

  // Формируем имя файла для записи образа экрана

  itoa(name_counter++, ext, 10);
  strcpy(fname,"!grab");
  strcat(fname, ext);
  strcat(fname,".scr");

  out_file = fopen(fname,"wb+");

  // Записываем содержимое видеобуфера в файл
  for(i = 0; i < max_row; i++)
  {
    for(j = 0; j < max_col; j++)
    {
      fputc(vbuf->chr, out_file);
      vbuf++;
    }

    // В конце каждой строки добавляем
    // символы перевода строки и
    // возврата каретки
    fputc(0xd, out_file);
    fputc(0xa, out_file);
  }
  fclose(out_file);
  return(0);
}

[Назад] [Содеожание] [Дальше]