Системное программное обеспечение персональных ЭВМ

unsigned int segm, unsigned int



Пример 8

void poke( unsigned int segm, unsigned int offs, int val); void pokeb(unsigned int segm, unsigned int offs, char val);
Аргументы этих функций: segm - сегментный адрес памяти, offs - смещение в сегменте. Функции peek и peekb возвращают прочитанное значение, а в функциях poke и pokeb записываемое в память значение задается аргументом val. Функции peekb и pokeb работают с одним байтом, peek и poke - с двухбайтным словом.

Следует помнить, что слово в памяти ПЭВМ хранится в двух последовательных байтах памяти, причем в байте с меньшим адресом - младшая часть слова. Поэтому при побайтном (peekb) чтении из двух последовательных адресов мы получим сначала младшую, а затем старшую часть слова, а при чтении слова (peek) - целое слово, в котором обе части находятся "на своих местах".
Определение физических адресов переменных возможно благодаря наличию в языке Си данных типа "указатели", представляющих собой адреса переменных. Указатель может быть преобразован в физический адрес при помощи макросов:



Пример 8

/*= ПРИМЕР 3.2 =*/ /*=============== Перехват прерывания ===================*/ #include <dos.h>
int intr_num = 9; /* Номер прерывания */ int intr_count = 0; /* Счетчик прерываний */ void interrupt new_handler(); /* Описание нового обработчика прерывания */ void interrupt (* old_handler)(); /* Переменная для сохранения старого вектора */ union REGS rr; /* Регистры общего назначения */ struct SREGS sr; /* Сегментные регистры */ void *readvect(int in); void writevect(int in, void *h); main() { /* Получение старого вектора */ old_handler=readvect(intr_num); /* Запись нового вектора */ writevect(intr_num,new_handler); /* Ожидание 10-кратного срабатывания */ while (intr_count
Введенные здесь дополнительные переменные rr и sr служат для передачи параметров функциям DOS. Чтение старого вектора производится при помощи функции 0x35 (ее номер перед обращением к DOS заносится в регистр AH). По спецификациям функции 0x35 в регистр AL должен быть занесен номер прерывания, вектор которого читается. Функция возвращает в регистре ES сегментную часть вектора, а в регистре BX - смещение, эти значения наша программа запоминает в переменных segm и offs.

Установка нового вектора производится при помощи функции 0x25. По спецификациям этой функции в регистр AL должен быть занесен номер прерывания, вектор которого мы устанавливаем, в регистре DS - сегментная часть вектора, а в регистр DX - смещение. Обратите внимание на то, что здесь при записи вектора мы не запрещаем прерывания - эти действия функция 0x25 выполняет сама. Восстановление вектора производится также при помощи функции 0x35.
В этой программе у нас нет необходимости вычислять адрес, по которому расположен вектор прерывания, поэтому макрос VECT_ ADDR здесь отсутствует. В прикладных задачах использованию функций DOS для чтения/установки векторов следует отдавать предпочтение еще и потому, что в новых версиях DOS адреса векторов, возможно, не будут так легко доступны пользователю.

В дальнейшем мы неоднократно будем использовать функции readvect и writevect, приведенные в примере 3.2.





Пример 8

/*== ПРИМЕР 6.7 ==*/ /*================== Разделение времени ==================*/ /* Два процесса - PROCESS1 и PROCESS2 - поочередно активизируются, каждый на заданный интервал времени */ /* ВНИМАНИЕ !!! При компиляции этого модуля в Турбо-Си необходимо установить Options -> Compiler ->Code generation ->Test stack overflow -> Off */ #include <dos.h>
#define word unsigned int #define byte unsigned char /* Номер используемого прерывания таймера */ #define TIMEINT 8 /* Размер стека (подобран экспериментально) */ #define StackSize 600 /* Максимальное число переключений */ #define NSWITCH 10 /* Кванты времени (в тиках таймера) */ static int TimeC[3]={1,40,120}; /* TimeC[0] - не используется; TimeC[i] - квант, отведенный i-му процессу */ /* Флаги инициализации */ static byte initF[3]={0,0,0}; /* initF[0] - общий флаг; initF[i] - флаг i-го процесса */ /* Сегменты и указатели стека прерванных процессов */ static word newSS[3], newSP[3]; /* newSS[0] и newSP[0] - для пусковой программы newSS[i] и newSP[i] - для i-го процесса */ static int TimeCount; /* Счетчик квантов времени */ static byte nproc; /* Номер текущего процесса */ static byte newstack[StackSize]; /* Стек для процессов. 1-я половина этого масива - для процесса 2, 2-я для процесса 1 */ static byte Nswitch=0; /* Счетчик переключений */ /* Вектор системного обработчика прерываний таймера */ void interrupt (*oldtime)(); /* Описание нового обработчика прерываний от таймера */ void interrupt RTS(); void *readvect(int in); void writevect(int in, void *h); union REGS rr; struct SREGS sr; /*==== main ====*/ main() { word st_seg,st_off; clrscr(); /* Запоминание старого стека */ newSP[0]=_SP; newSS[0]=_SS; /* Определение адреса нового стека */ st_seg=FP_SEG(newstack); st_off=FP_OFF(newstack); /* Подключение к вектору таймера */ TimeCount=TimeC[0]; *oldtime=readvect(TIMEINT); writevect(TIMEINT,RTS); /* Инициализация процесса 1 */ /* При перекл.на другой стек запрещаются прерывания */ nproc=1; disable(); _SS=st_seg; _SP=st_off+StackSize; enable(); process1(); /* Инициализация процесса 2 */ nproc=2; disable(); _SS=st_seg; _SP=st_off+StackSize/2; enable(); process2(); /* Восстановление стека */ disable(); _SS=newSS[0]; _SP=newSP[0]; enable(); initF[0]++; /* Инициализация закончена */ /* Запуск в рабочее состояние */ nproc=0; TimeCount=1; /* Этот цикл прервется таймером.



Пример 8

/*== ПРИМЕР 7.8 ==*/ /*============ Статус обработки Ctrl+Break ===============*/ /* ВНИМАНИЕ! Для проверки реакции на Ctrl+ Break программу следует запускать вне Турбо-среды */ #include <dos.h>
void main() { union REGS rr; int i,k,o,m; clrscr(); /* Отключение Ctrl+Break */ rr.h.ah=0x33; rr.h.al=1; /* Подфункция установка Ctrl+Break */ rr.h.dl=0; /* OFF */ intdos(&rr,&rr); printf("Статус Ctrl+Break = %d\n",rr.h.dl); /* Этот цикл будет непрерываемым */ for (o=160,i=0,k=0; i
Мы можем перехватить вектор 0x23 и производить собственную обработку Ctrl+Break. Это демонстрирует пример 7.9, в котором обработка комбинации заключается в смене цвета выводимого на экран символа.



Пример 8

/*== ПРИМЕР 9.6 ==*/ /*================ Функции прерывания 0x10 ===============*/ /* функции 6, 7 - сдвиг экрана; 8, 9 - читать/писать сим- вол/атрибут, 0x0A, 0x0E - вывод символа */ #include <dos.h>
#include <stdlib.h>
#define byte unsigned char #define word unsigned int #define Esc 27 #define Up 0x48 #define Down 0x50 #define Left 0x4b # define Right 0x4d union REGS rr; struct SREGS sr; byte x1=34, y1=7; /* координаты окна (левый,верхний) */ byte x2, y2; /* координаты окна (правый,нижний) */ byte xc, yc; /* координаты курсора */ char *msg[]={"=========Вывод по функции 0x0A==========", "=========Вывод по функции 0x0E==========", "=========Вывод по функции 0x09==========", "=========Вывод по функции 0x13==========", "========Вывод по функции DOS 2==========", "========Вывод по функции DOS 6==========", "========Вывод по функции DOS 9==========$" }; word save[5]; /* для сохранения регистров */ word i; char *s; main() { xc=0; yc=0; /*== Демонстрация функций вывода ==*/ /* очистка экрана и заполн. его случайными атрибутами */ randomize(); for (i=0; i1) { for(y=y1; y=x1; xx1--,xx2--) { getca(xx1,y1,&c,&a); putca(xx2,y2,c,a); } } } /*==== Посимвольный вывод строки функцией 0A ====*/ putstring0A() { for (s=msg[0]; *s; s++) { rr.h.ah=0x0a; /* функция 0A */ rr.h.bh=0; /* страница 0 */ rr.h.al=*s; /* символ */ rr.x.cx=1; /* число повторений */ int86(0x10,&rr,&rr); /* перевычисление координат и сдвиг курсора */ if (++xc>79) { xc=0; yc++; } setcurpos(xc,yc); } } /*==== Посимвольный вывод строки функцией 0E ====*/ putstring0E() { for (s=msg[1]; *s; s++) { rr.h.ah=0x0e; /* функция 0E */ rr.h.al=*s; /* символ */ int86(0x10,&rr,&rr); } /* перевычисление координат, курсор сдвигается сам */ xc+=40; yc+=xc/80; xc=xc%80; } /*==== Посимвольный вывод строки функцией 9 ====*/ putstring09() { char a, c; getca(xc,yc,&c,&a); /* определение атрибута 1-й позиции */ for (s=msg[2]; *s; s++) { putca(xc,yc,*s,a); /* перевычисление коорд., курсор сдвигается в putca */ if (++xc>79) { xc=0; yc++; } } setcurpos(xc,yc); } /*==== Посимвольный вывод строки функцией 13 ====*/ putstring13() { char a, c; getca(xc,yc,&c,&a); /* определение атрибута 1-й позиции */ save[0]=_BP; save[1]=_DI; save[2]=_SI; save[3]=_DS; save[4]=_ES; _BP=FP_SEG(msg[3]); /* адрес строки */ _ES=FP_OFF(msg[3]); _BH=0; /* страница 0 */ _BL=a; /* атрибут */ _DH=xc; _DL=yc; /* координаты начала */ _AX=0x1300; /* функция 13, подфункция 0 */ geninterrupt(0x10); _BP=save[0]; _DI=save[1]; _SI=save[2]; _DS=save[3]; _ES=save[4]; /* перевычисление координат, курсор сдвигается сам */ xc+=40; yc+=xc/80; xc=xc%80; } /*==== Посимвольный вывод строки функцией DOS 2 ====*/ putstringdos2() { for (s=msg[4]; *s; s++) { rr.h.ah=2; /* функция 2 */ rr.h.dl=*s; /* символ */ intdos(&rr,&rr); } /* перевычисление координат, курсор сдвигается сам */ xc+=40; yc+=xc/80; xc=xc%80; } /*==== Посимвольный вывод строки функцией DOS 6 ====*/ putstringdos6() { for (s=msg[5]; *s; s++) { rr.h.ah=6; /* функция 6 */ rr.h.dl=*s; /* символ */ intdos(&rr,&rr); } /* перевычисление координат, курсор сдвигается сам */ xc+=40; yc+=xc/80; xc=xc%80; } /*==== Вывод строки функцией DOS 9 ====*/ putstringdos9() { rr.h.ah=9; /* функция 9 */ rr.x.dx=FP_OFF(msg[6]); /* адрес строки */ sr.ds=FP_SEG(msg[6]); /* символ */ intdosx(&rr,&rr,&sr); /* перевычисление координат, курсор сдвигается сам */ xc+=40; yc+=xc/80; xc=xc%80; } /*==== Установка курсора ====*/ setcurpos(byte x,byte y) { rr.h.ah=2; rr.h.bh=0; rr.h.dh=y; rr.h.dl=x; int86(0x10,&rr,&rr); }
В BIOS имеются средства, позволяющие осуществлять быстрый сдвиг изображения по вертикали: функции 6 (сдвиг окна вверх) и 7 (сдвиг вниз). В регистрах CH и CL задаются соответственно y- и x-координаты левого верхнего угла сдвигаемого окна, в DH и DL - правого нижнего угла; в AL - число строк сдвига (при AL=0 функция 6 очищает окно); а в BH - атрибут, которым заполняются освобождающиеся строки. Во второй части примера 9.6 при сдвиге вверх самая верхняя строка посимвольно копируется под самую нижнюю, а затем все окно, начиная со 2-й строки сдвигается; при сдвиге вверх - наоборот. При сдвигах вправо и влево каждая строка изображения посимвольно (функции 8 и 9) переносится на новое место. При выполнении этой программы хорошо заметно, что эти сдвиги выполняются гораздо медленнее, чем сдвиги по вертикали, лучше для этих целей использовать прямое копирование областей видеопамяти.



Пример 8

/*== ПРИМЕР 10.5 ==*/ /*============= Чтение корневого оглавления ==============*/ #include <alloc.h> #include <dos.h> #define byte unsigned char #define word unsigned int #define dword unsigned long /* Структура, описывающая элемент оглавления */ struct Dir_Item { char fname[11]; /* имя файла */ byte attr; /* атрибут */ byte reserved[10]; word time; /* время */ word date; /* дата */ word cl; /* номер 1-го кластера */ dword size; /* размер файла */ } *dir; /* Структура корневой записи */ struct RootRec { byte jmp[3], ident[8]; word SectSize; byte ClustSize; word ResSect; byte FatCnt; word RootSize, TotSecs; byte Media; word FatSize, TrkSecs, HeadCnt, HidnSecL, HidnSecH; dword LongTotSecs; byte Drive; byte reserved1; byte DOS4_flag; dword VolNum; char VolLabel[11], FatForm[8]; } *rt; /* Структура параметров для INT 25 */ struct{ dword first_sect; word count; byte *ptr; } parm; union REGS rr; struct SREGS sr; char *attrs[]={"ТОЛЬКО_ЧТЕНИЕ","СКРЫТЫЙ_ФАЙЛ", "СИСТЕМНЫЙ_ФАЙЛ","МЕТКА_ТОМА","ПОДКАТАЛОГ"}; main() { byte *buff; /* адрес буфера в ОП */ byte sys, /* признак диска > 32 Мбайт */ drive; /* идентификатор диска */ int i, j, k, m; byte ms; /* маска атрибутов */ word RootSect, /* номер сектора */ RootCnt, /* число элементов */ RootSects; /* число секторов */
ASK1: printf("\nУкажите идентификатор диска (A,B...) >"); drive=getche(); if (drive>'b') { ASK2:printf("\nОбьем лог. диска больше 32 Мбайт? (y/n) >"); switch(sys=getche()) { case 'y' : sys=1; break; case 'n' : sys=0; break; default: goto ASK2; } } else sys=0; buff=(byte *)malloc(512); /* Чтение boot-сектора */ rr.h.al=drive-'a'; /* Диск */ if (!sys) { rr.x.cx=1; rr.x.dx=0; sr.ds=FP_SEG(buff); rr.x.bx=FP_OFF(buff); } else { parm.first_sect=0; parm.count=1; parm.ptr=buff; sr.ds=FP_SEG(&parm); rr.x.bx=FP_OFF(&parm); rr.x.cx=0xffff; } int86x(0x25,&rr,&rr,&sr); readerror(); rt=(struct RootRec *)buff; /* Параметры корневого каталога */ RootCnt=rt->RootSize; RootSect=rt->ResSect+rt->FatSize*rt->FatCnt; RootSects=rt->RootSize/16; /* Выделение памяти под корневой каталог */ buff=(byte *)realloc(buff,RootSects*512); if (buff==NULL) { printf("Нехватка памяти\n"); exit(); } dir=(struct Dir_Item *)buff; clrscr(); /* Чтение каталога */ rr.h.al=drive-'a'; if (!sys) { /* маленький диск */ rr.x.cx=RootSects; rr.x.dx=RootSect; sr.ds=FP_SEG(buff); rr.x.bx=FP_OFF(buff); } else { /* большой диск */ parm.first_sect=RootSect; parm.count=RootSects; parm.ptr=buff; sr.ds=FP_SEG(&parm); rr.x.bx=FP_OFF(&parm); rr.x.cx=0xffff; } int86x(0x25,&rr,&rr,&sr); readerror(); /* Распечатка оглавления */ for (j=0, k=0; j<RootCnt; j++) { printf("#%3d ",j); if (dir[j].fname[0]!=0) { /* Если элемент оглавления непустой */ for (i=0; i<11; i++) { if (i==8) printf("."); if ((i==0)&&(dir[j].fname[i]==0xe5)) printf("?"); else printf("%c",dir[j].fname[i]); } if (dir[j].fname[0]==0xE5) printf(" ******УДАЛЕН****** "); printf(" атрибут=%02x (",dir[j].attr); if (!(dir[j].attr&0x01f)) printf("ЧТЕНИЕ/ЗАПИСЬ"); else for(ms=1, m=0; m<5; m++,ms<<=1) if (dir[j].attr&ms) printf("%s ",attrs[m]); printf(")\n "); printf(" время=%02d/%02d/%02d", (dir[j].time>>11)&0x001f,(dir[j].time>>5)&0x003f, (dir[j].time&0x001f)*2); printf(" дата=%02d/%02d/%04d", ((dir[j].date>>9)&0x007f)+1980, (dir[j].date>>5)&0x000f, dir[j].date&0x001f); printf(" нач.кластер=%03x",dir[j].cl); printf(" размер=%lu\n",dir[j].size); } else printf(" свободен\n"); if (++k>7) { k=0; if (getch()==27) goto EOJ; } } EOJ:free(buff); } /* Проверка ошибок чтения */ readerror() { if (rr.x.cflag) { printf("\nОшибка чтения: %x. ",rr.h.ah); printf("Нажмите любую клавишу...\n\7"); getch(); exit(); } }



Пример 8

struct CDS { char path[67]; /* ASCIIZ-строка, содержащая полный путь к текущему каталогу */ word flags; /* слово признаков, позволяющее определить, связан ли данный идентификатор с физическим диском или введен командой SUBST */ void *dpb; /* адрес DPB для этого диска */ word dir_clust; /* номер 1-го кластер текущ.каталога */ byte reserved[6]; };
Для DOS 4.x размер последнего поля reserved - 13 байт.

Для DOS 5.0 и выше структура CDS иная:

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