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 (®s, ®s, &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 (®s, ®s, &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 (®s, ®s, &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, ®s, ®s); 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 (®s, ®s, &sregs); // Указатель на флаг InDos возвращается в ES:BX FP_SEG (indos_ptr) = sregs.es; FP_OFF (indos_ptr) = regs.x.bx; // Находим флаг критических ошибок regs.x.ax = 0x5D06; intdosx (®s,®s,&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 (®s,®s); 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 (®s,®s,&segregs); } // ======================================== // GetPSP // Получить адрес текущего блока PSP // ======================================== unsigned GetPSP(void) { regs.h.ah = 0x62; intdos (®s,®s); 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 (®s,®s); *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, ®s, ®s); } // ======================================== // 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); } |