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


Структура SREGS, служащая для задания



Пример 4

rr.h.ah=0x11; rr.h.al=0x22;
будут одинаковы.

Структура SREGS, служащая для задания содержимого сегментных регистров:



Пример 4

void geninterrupt(int int_num);
geninterrupt представляет собой функцию, основное содержание которой составляет единственная команда Ассемблера INT int_num. Значения регистров можно передать прерыванию через псевдорегистры - _AX, _BX и т.д., из них же получить и результаты. Но применение этой функции требует большой осторожности: во-первых, при формировании входных регистров имеется риск в процессе формирования второго и последующих испортить содержимое регистров, сформированных ранее, во-вторых, при выполнении прерывания может быть изменено содержимое регистров - в первую очередь DS и ES, а также и BP, DI, SI (поэтому мы всегда, применяя geninterrupt, сохраняем их содержимое в статической памяти).
Еще несколько подобных функций мы опускаем, так как они не используются в программах нашего пособия.



Пример 4

/*== ПРИМЕР 6.3 ==*/ /*============= Системная служба даты ================*/ #include <dos.h>
main() { union REGS rr; int y,m,d; /* Исходный год, месяц, день */ /* Получение текущей даты */ rr.h.ah=0x2a; /* Чтение даты */ intdos(&rr,&rr); d=rr.h.dl; m=rr.h.dh; y=rr.x.cx; printf("%02d:%02d:%02d\n",d,m,y); /* Убедимся, что во флаге смены даты BIOS - 0 */ printf("Флаг смены даты = %d\n",peekb(0x40,0x70)); /* Установка флага смены даты */ pokeb(0x40,0x70,1); /* Пока нет запроса даты, флаг остается взведенным (запрос будет выдан по клавише Esc) */ while(getch()!=27) printf("Флаг смены даты = %d\n",peekb(0x40,0x70)); /* Запрос даты. Мы получим дату на 1 большую исходной */ rr.h.ah=0x2a; /* Чтение даты */ intdos(&rr,&rr); printf("%02d:%02d:%02d\n",rr.h.dl,rr.h.dh,rr.x.cx); /* а флаг смены даты сбрасывается */ printf("Флаг смены даты = %d\n",peekb(0x40,0x70)); /* Восстановление исходной даты */ rr.h.ah=0x2b; /* Запись даты */ rr.h.dl=d; rr.h.dh=m; rr.x.cx=y; intdos(&rr,&rr); /* и вывод ее */ rr.h.ah=0x2a; /* Чтение даты */ intdos(&rr,&rr); printf("%02d:%02d:%02d\n",rr.h.dl,rr.h.dh,rr.x.cx); }


В AT имеются независимые часы реального времени, показания которых содержатся в CMOS-памяти. Регистры CMOS-памяти, связанные с временем и датой следующие: 0 - секунды, 2 - минуты, 4 - часы, 6 - день недели (0 - воскресенье), 7 - день месяца, 8 - месяц, 9 - год. Доступ к этим данным - либо через порты 0x70, 0x71, либо через прерывание 0x1A. Функция 2 этого прерывания (AH=2) - чтение часов реального времени, функция 3 - установка часов, функции 4, 5 - чтение и установка даты соответственно. Используются те же регистры, что и в функциях DOS 0x2C, 0x2A, но все данные представляются в двоично-десятичном коде. При загрузке системы на AT время дня и дата выбираются из этих часов, далее эти часы и системная служба времени работают независимо друг от друга.
Кроме того, в AT имеется также возможность запрограммировать прерывание на заданное время, в описаниях это часто называют сигналом тревоги (alarm). Время поступления этого сигнала заносится в регистры CMOS-памяти: 1 - секунды, 3 - минуты, 5 - часы, а прерывание по достижению заданного времени разрешается единицей в разряде 5 регистра 0x0B. При достижении заданного времени происходит прерывание 0x4A.

Сигнал тревоги может быть задан при помощи функции 6 прерывания 0x1A, а отменен - функцией 7, как это показано в следующем примере.



Пример 4

/*== ПРИМЕР 8.5 ==*/ /*=============== Печать символа на принтере =============*/ #include <dos.h> main() { union REGS rr; /* Символы для печати */ char a[]={24,'a','b','c','d','e','f','g','h','i ','j',10}; int i; for(i=0;i<12;i++) { rr.h.ah=5; /* Функция 5 DOS */ rr.h.dl=a[i]; intdos(&rr,&rr); } }
Кроме того, с принтером в DOS связан стандартный файл печати stdprn. Для вывода на принтер можно использовать функцию файлового вывода 0x40, указывая для нее номер файла - 4.



Пример 4

0 R G B r g b,

где r, g, b - соответственно красная, зеленая, синяя составляющие цвета 1/3 интенсивности; R, G, B - красная, зеленая, синяя составляющие 2/3 интенсивности. Таким обазом, возможны, например, три градации красного цвета:
00000100- тусклый красный;
00100000- красный;
00100100- яркий красный.

При выборе палитры в BL задается код изменяемого цвета, а в BH - код устанавливаемой для него палитры. В примере 9.3 код изменяемого цвета выбирается случайным (по счетчику тиков таймера), и для него перебираются все возможные палитры. Обратите внимание на то, что при окончании работы программы для нашего цвета восстанавливается палитра по умолчанию.



Пример 4

/*== ПРИМЕР 10.2 ==*/ /*=== Чтение и анализ гл.загруз.записи твердого диска ===*/ #include <dos.h> #define byte unsigned char #define word unsigned int #define dword unsigned long #define SECT(x) x&0x3f #define TRK(x) (x>>8)|((x<<2)&0x300) main() { /* структура элемента раздела */ struct Part { byte ActFlag; /* описатель*/ /* физический адрес начала раздела */ byte Begin_Hd; /* # головки */ word Begin_SecTrk; /* # сектора и дорожки */ byte SysCode; /* код системы */ /* физический адрес конца раздела */ byte End_Hd; /* # головки */ word End_SecTrk; /* # сектора и дорожки */ dword RelSec; /* # сектора начала */ dword Size; /* число секторов */ }; /* стpуктуpа главной загpузочной записи */ struct MBR { char LoadCode[0x1be]; /* пpогpамма загpузки */ struct Part rt[4]; /* 4 эл-та pазделов */ word EndFlag; /* подпись MBR */ } mbr; int x=10,y; /* экpанные кооpдинаты */ byte head=0; /* номеp головки (0) */ word Sect_Trk=1; /* номеp доpожки и сектоpа (0,1) */ int ndrive=0; /* номеp лог.диска */ word *EndList; /* указатель на подпись */ union REGS rr; struct SREGS sr; word i;
clrscr(); printf("Разделы жесткого диска # 1\n"); printf("==========================\n\n"); printf("Лог.диск \nПризнак = \n printf("Код системы = \nНачало: гол.= \n"); printf(" дор. = \n сект. = \n"); printf("Конец : гол.= \n дор. = \n"); printf(" сект.= \nНач.сектор = \n"); printf("Размер = \n"); NEXT: /* Чтение при помощи прерывания 13. Только таким путем можно прочитать MBR, т.к. она не принадлежит никакому логическому диску. */ rr.h.ah=2; /* Чтение */ rr.h.al=1; /* Секторов 1 */ rr.h.dl=0x80; /* Тв.диск */ rr.h.dh=head; /* Головка */ rr.x.cx=Sect_Trk; /* Дорожка, сектор */ sr.es=FP_SEG(&mbr); /* Адрес буфера в ОП */ rr.x.bx=FP_OFF(&mbr); int86x(0x13,&rr,&rr,&sr); /* Проверка ошибок чтения */ if (rr.x.cflag) { printf("Ошибка чтения: %x. ",rr.h.ah); printf("Нажмите любую клавишу...\n\7"); getch(); exit(); } /* В нач.установках EndList указывает на 1-й байт 1-го элемента pаздела */ for (EndList=(word *)&mbr.rt[(i=0)]; /* пока не встpетилась подпись MBR или pаздел нулевого pазмеpа */ (*EndList!=0xaa55)&&(mbr.rt[i].Size>0L); /* пеpеход к след.



Пример 4

/*== ПРИМЕР 11.4 ==*/ /*======== Обработка таблицы перемещений EXE-файла =====*/ #include <dos.h> #include <string.h> #include <stdlib.h> #define byte unsigned char #define word unsigned int # define dword unsigned long struct EXEH { /* заголовок EXE-файла */ word ExeFlag, LastPag, PageCnt, ReloCnt, HdrSize, MinMem, MaxMem,ReloSS, ExeSP, ChkSum, ExeIP, ReloCS, TabOff, Overlay; } exeh; /* Элемент таблицы перемещений */ struct ReloItem { word offs; /* смешение */ word segm; /* сегмент */ } ri; word psp, /* сегм.адрес PSP */ startseg, /* стартовый сегмент */ reloseg, /* сегмент модифицированного слова */ d; /* разность между памятью и файлом */ char fname[80],*fn; /* имя EXE-файла */ int exef; /* дескриптор EXE-файла */ dword fileoff, /* адрес в файле начала модуля */ itoff, /* адрес в файле модиф.слова */ seekoff; /* адрес в файле эл-та таблицы */ byte buff[10], /* буфер в ОП */ *s; int i, ni; union REGS rr; struct SREGS sr; main() { /* получение сегментного адреса PSP и открытие файла */ rr.h.ah=0x51; intdos(&rr,&rr); psp=rr.x.bx; for(fn=(char *)MK_FP(peek(psp,0x2C),0);*fn|*(fn+1);fn++); strcpy(fname,fn+4); if (strchr(fname,'.')==NULL) strcat(fname,".EXE"); rr.h.ah=0x3d; rr.h.al=0; sr.ds=FP_SEG(fname); rr.x.dx=FP_OFF(fname); intdosx(&rr,&rr,&sr); if (rr.x.cflag) { printf("Невозможно открыть файл %s\n",fname); exit(0); } else exef=rr.x.ax; /* чтение заголовка */ dread(&exeh,sizeof(struct EXEH)); startseg=psp+0x10; printf("\nСтартовый сегмент = %04Xh\n",startseg); printf("Число элементов = %d\n\n",exeh.ReloCnt); fileoff=exeh.HdrSize*16; /* перебор таблицы перемещений */ for (seekoff=exeh.TabOff,ni=0; ni<exeh.ReloCnt; ni++) { /* чтениe эл-та табл.перемещений */ dseek(seekoff); dread(&ri,sizeof(struct ReloItem)); printf("Элемент перемещения #%d : %04X:%04X\n", ni+1,ri.segm,ri.offs); /* выборка модифицированного адреса из программы в ОП */ reloseg=startseg+ri.segm; s=(byte *)MK_FP(reloseg,ri.offs)-4; printf(" Память : %Fp -> ",s); d=*((word *)(s+4)); prtmem(s); /* выборка немодифицированного адреса из EXE-файла */ itoff=fileoff+ri.segm*16+ri.offs-4; dseek(itoff);__ _.dread(buff,10); printf(" Файл : %9ld -> ",itoff); d-=*((word *)(buff+4)); prtmem(buff); printf(" Разность = %04X\n",d); seekoff+=sizeof(struct ReloItem); if (getch()==27) exit(0); } } /*==== Чтение из файла ====*/ dread(void *addr,int len) { rr.h.ah=0x3f; rr.x.bx=exef; rr.x.cx=len; sr.ds=FP_SEG(addr); rr.x.dx=FP_OFF(addr); intdosx(&rr,&rr,&sr); if (rr.x.cflag) { printf("Ошибка чтения %s\n"); exit(0); } } /*==== Позиционирование в файле ====*/ dseek(dword off) { rr.h.ah=0x42; rr.x.bx=exef; rr.h.al=0; rr.x.cx=off>>16; rr.x.dx=off&0xffff; intdos(&rr,&rr); if (rr.x.cflag) { printf("Ошибка позиционирования\n"); exit(0); } } /*==== Дамп участка памяти ====*/ prtmem(byte *a) { int i; for (i=0; i<10; i++) { if ((i==4)(i==6)) printf(" "); printf("%02X",*(a++)); } printf("\n"); }
Заголовок EXE-файла нужен только при загрузке программы в оперативную память - он не сохраняется после загрузки. Приведем итоговую схему загрузки EXE-программ:

  • форматированную часть заголовка DOS считывает в свой буфер;
  • определяется адрес в файле начала программного модуля: HdrSize * 16 и размер программного модуля: (PageCn - 1) * 512 + LastPage - HdrSize * 16;
  • выделяется память для программного сегмента и строится PSP;
  • вычисляется startseg - сегментный стартовый адрес; определяется начало в файле таблицы перемещений и элементы таблицы читаются в буфер DOS;
  • для каждого элемента выбирается слово по адресу (startseg + segm) : offs, к нему прибавляется startseg, результат записывается на то же место;
  • в регистры ES и DS записывается адрес PSP, в регистр SS - startseg + ReloSS, в SP - ExeSP, в CS - startseg + ReloCS, в IP - ExeIP, тем самым управление передается загруженной программе.




Пример 4

/*== ПРИМЕР 12.3 ==*/ /*=========== Дисциплины распределения памяти ===========*/ #include <dos.h> #define byte unsigned char # define word unsigned int void memfree(byte *a); byte *memget(int blksize); void setorder(byte d); byte getorder(); void init(void); void memmap(int x); struct MCB { byte type; word owner, size; byte reserved[11]; } *c; word freetop, cs; union REGS rr; struct SREGS sr; main() { byte *a[10] ,*b, order; word sz, i; init(); /* составление списка исходных блоков */ /* Формирование фрагментированной памяти */ for (sz=84,i=0; i<10; i++) { a[i]=memget(sz); b=memget(8); if (i>=5) sz+=4; else sz-=4; } for (i=0; i<10; i++) memfree(a[i]); order=getorder(); clrscr(); printf(" Исходное |Дисциплина_0"); printf("|Дисциплина_1|Дисциплина_2|\n"); printf("------------|------------"); printf("|------------|------------|"); memmap(1); /* отображение памяти */ setorder(0); /* установка дисциплины */ b=memget(70); /* выделение памяти */ memmap(14); /* отображение памяти */ memfree(b); /* возврат к исходному распределению */ setorder(2); b=memget(70); memmap(27); memfree(b); setorder(1); b=memget(70); memmap(40); memfree(b); setorder(order); /* восстановление исходной дисциплины */ } /*==== Установка дисциплины распределения ====*/ void setorder(byte d) { rr.h.ah=0x58; /* Ф-ция 58 */ rr.h.al=1; /* Установить */ rr.x.bx=d; /* Дисциплина */ intdos(&rr,&rr); } /*==== Чтение дисциплины распределения ====*/ byte getorder() { rr.h.ah=0x58; /* Ф-ция 58 */ rr.h.al=0; /* Прочитать */ intdos(&rr,&rr); return (rr.x.ax); /* Дисциплина - в AX */ } /*==== Выделение памяти ====*/ byte *memget(int blksize) { rr.h.ah=0x48; rr.x.bx=blksize; intdos(&rr,&rr); if (rr.x.cflag) printf("\7Неудовл.запрос mem=%d\n",rr.x.bx); else return(MK_FP(rr.x.ax,0)); } /*==== Освобождение памяти ====*/ void memfree(byte *a) { rr.h.ah=0x49; sr.es=FP_SEG(a); intdosx(&rr,&rr,&sr); if (rr.x.cflag) printf("\7Некорректный free\n"); } /*==== Определение адреса последнего свободного блока ====*/ void init(void) { rr.h.ah=0x52; intdosx(&rr,&rr,&sr); cs=peek(sr.es,rr.x.bx-2); do { freetop=cs; c=(struct MCB *)MK_FP(cs,0); cs+=(c->size+1); } while(c->type!='Z'); } /*==== Выдача карты памяти ====*/ void memmap(int x) { int y; cs=freetop; y=3; do { c=(struct MCB *)MK_FP(cs,0); cs+=(c->size+1); gotoxy(x,y++); printf("%04X %-4u",cs,c->size); if (c->owner==0) printf("...|\n"); else printf("***|\n"); } while(c->type!='Z'); }



Пример 4

0 e m 0 p 0 0 0 0 l 0 0 0 0 s 0,
где
  • i - драйвер консоли ввода;
  • o - драйвер консоли вывода;
  • n - драйвер нулевого (пустого) устройства;
  • c - драйвер часов;
  • l - драйвер поддерживает функции, введенные в DOS 3.2;
  • p - драйвер выполняет операции Open/Close;
  • b - драйвер неIBM-овского блочного устройства;
  • e - драйвер поддерживает функции IOCTL;
  • s - драйвер поддерживает 32-битную адресацию сектора;
  • m - драйвер определяет тип диска проверкой 1-го байта FAT.
    По правилам DOS кодовая часть драйвера состоит из двух секций, называемых секцией стратегии и секцией обработки прерываний. Поля strat_off и intr_off заголовка содержат смещения этих секций от начала драйвера. Поле name для символьного устройства содержит имя устройства (возможно, дополненное пробелами), а для блочного - первый байт этого поля содержит количество устройств, поддерживаемых драйвером.

    При помощи поля next, заполняемого системой при загрузке драйвера, все драйверы (системные и устанавливаемые) связываются в список. Начало этого списка - заголовок драйвера NUL в CVT. Значение 0xFFFF в части смещения поля next является признаком конца списка (такой же признак конца используется и в списках, образуемых другими управляющими блоками). Программа примера 13.1 отслеживает этот список.

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