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

unsigned int r_ax, r_bx, r_cx,



Пример 6

struct REGPACK { unsigned int r_ax, r_bx, r_cx, r_dx; unsigned int r_bp, r_si, r_di; unsigned int r_ds, r_es, r_flags; };
Данные этих типов служат агрументами функций обращения к прерываниям.
3). Интересный способ доступа к регистрам возможен в программах обработки прерываний, он будет рассмотрен ниже вместе с такими программами.



Пример 6

/*== ПРИМЕР 5.6 ==*/ /*================ Определение версии DOS ================*/ #include <dos.h>
union REGS rr; main() { rr.h.ah=0x30; intdos(&rr,&rr); printf("Версия MS-DOS %d.%d\n",rr.h.al,rr.h.ah); }



Пример 6

/*== ПРИМЕР 6.5 ==*/ /*============== Фоновая музыка ===================*/ #include <dos.h>
#define TIMEINT 8 /* Возможно также #define TIMEINT 0x1c */ void interrupt (*oldtime)(); /* Область сохранения системного вектоpа пpеpывания таймера */ void interrupt newtime(); /* Новый обpаботчик пpеpываний */ static int NM; /* Счетчик длительности ноты */ static int N; /* Индекс в массиве MUS */ static int MUS[]= { /* Кодировка мелодии. 1-е число в каждой паре - коэфф.деления, второе - длительность в тиках */ 1218,10,767,10,813,10,912,10,966,10,912,30,1218,10,609,10, 678,10,767,10,813,10,724,30,912,10,574,15,609,5,678,15, 767,5,813,5,767,5,678,20,574,10,609,10,678,10,767,10,678, 10,813,30,1218,10,767,10,813,10,912,10,966,10,912,30,1218, 10,609,10,678,10,767,10,813,10,724,30,912,10,574,15,609,5, 678,15,767,5,813,5,767,5,678,20,574,10,609,10,678,10,767, 10,678,10,813,30,0,2,609,10,609,10,912,10,724,10,609,10, 609,5,678,5,724,5,678,5,574,15,678,5,678,10,1024,10,813, 10,678,10,678,5,767,5,813,5,724,5,609,20,678,5,609,5,574, 5,456,5,342,5,384,5,406,5,456,5,456,20,483,20,678,5,609,5, 574,5,456,5,342,5,384,5,406,5,456,5,456,10,483,10,456,20, 0,2,456,5,456,10,483,10,456,20,0,30,-1 }; union REGS rr; struct SREGS sr; void *readvect(int in); void writevect(int in, void *h); /*==== Пусковая пpоцедуpа, программа переднего плана ====*/ main() { int c,i; union REGS rr; initmus(); /* Инициpование */ for ( ; ; ) { c=getch(); rr.h.ah=0x2c; intdos(&rr,&rr); printf("%02d:%02d:%02d\n",rr.h.ch,rr.h.cl,rr.h.dh); /* Завершение по клавише Esc */ if (c==27) break; } stopmus(); /* Завеpшение */ } /*==== Инициpование ====*/ initmus() { /* Начальные значения пеpеменных */ N=0; NM=1; /* Подмена вектоpа таймера */ oldtime = readvect(TIMEINT); writevect(TIMEINT,newtime); } /*==== Завеpшение. Выкл.звука и восстановление вектоpа ===*/ stopmus() { silence(); writevect(TIMEINT,oldtime); } /*==== Новый обpаботчик пpеpываний таймеpа ====*/ void interrupt newtime() { int i; /* Если мы подключились к вектору 8, то пpи отсутствии следующего опеpатоpа системная служба вpемени не pаботает, в main-функции значение t не меняется. */ (*oldtime)(); if (--NM==0) { /* Подсчет тактов */ /* Вpемя звучания ноты истекло.





Пример 6

/*== ПРИМЕР 7.6 ==*/ /*====== Функции обслуживания стандартного ввода DOS =====*/ /* ВНИМАНИЕ! Для проверки реакции на Ctrl+Break программу следует запускать вне Турбо-среды */ #include <dos.h>
main() { union REGS in, rr; /* Регистры для вызовов DOS */ struct SREGS sr; /* Регистры для вызовов DOS */ int x,y; /* Коорд.курсора (1,6,7,8) или счетчик (A) */ unsigned char dosnum; /* Номер функции */ unsigned long d; /* Счетчик опросов (6) */ char string[13]; /* Буфер (A) */ char *s; /* Указатель в буфере (A) */ L0: clrscr(); printf("Укажите # функции DOS (1,6,7,8,B,A) >"); scanf("%x",&dosnum); printf("DOS-функция %02X.\n",dosnum); switch (dosnum) { case 1: printf("Ввод символа с эхо; "); printf("реагирует на Ctrl-Break.\n"); goto F; case 7: printf("Ввод символа без эхо; "); printf("не реагирует на Ctrl-Break.\n"); goto F; case 8: printf("Ввод символа без эхо; "); printf("реагирует на Ctrl-Break.\n"); goto F; case 6: printf("Ввод символа без эхо и без ожидания; "); printf("не реагирует на Ctrl-Break.\n"); printf("Окончание по Esc\n"); printf("Вводите >"); for(; ; ) { /* Цикл по клавишам */ for (d=0 ; ;d++ ) { /* Цикл ожидания нажатия клавиши */ rr.h.ah=6; rr.h.dl=0xff; /* FF в DL - запрос на ввод. */ intdos(&rr,&rr); /* Если символ готов, флаг ZF=0, а в AL - код символа, в противном случае ZF=1. */ if ((rr.x.flags&0x0040)==0) break; } x=wherex(); y=wherey(); gotoxy(1,7); clreol(); printf("Код==>%02x%c%d\n",rr.h.al); if (rr.h.al==0xff) break; delay(500); } break; case 0xa: printf("Буферизованный ввод строки; "); printf("реагирует на Ctrl-Break.\n"); string[0]=11; /* Макс.длина - 10 (без Enter) */ for(x=1; x"); for(x=0; x"); rr.h.ah=0x0a; sr.ds=FP_SEG(string); /* DS:DX - адрес буфера */ rr.x.dx=FP_OFF(string); intdosx(&rr,&rr,&sr); printf("\nБуфер после ввода>"); for(x=0; x"); for(s=string+2; *s!=13; printf("%c",*(s++))); printf("\n"); break; default: goto L0; } exit(); F: /* Общий алгоритм применения для функций 1,7,8 */ printf("Окончание по Esc\nВводите >"); for (; ; ) { rr.h.ah=dosnum; intdos(&rr,&rr); /* В AL - код символа. */ x=wherex(); y=wherey(); gotoxy(1,7); clreol(); printf(" Код==>%02x%c
И еще две возможности предоставляет DOS.
Функция 0x0C. При ее вызове в регистре AL должен быть номер одной из функций ввода (1, 6, 7, 8, 0x0A). Функция очищает буфер клавиатуры в памяти BIOS, а затем вызывает указанную в AL функцию.

Кроме того, поскольку стандартный ввод представляет собой файл DOS, с ним можно работать при помощи функции файлового ввода 0x3F, задавая дескриптор файла 1. Файловые функции будут рассмотрены позднее.



Пример 6

/*== ПРИМЕР 8.7 ==*/ /*=================== Загружаемый шрифт ==================*/ #include <dos.h> #include <stdio.h> void interrupt (* old17)(); void interrupt h17(); int p1,p2,p3; /* Номера портов принтера */ #define prt(x) putc(x,stdprn) #define byte unsigned char /*==== main ====*/ main() { char *st[]={ /* Текст для печати */ "He's a real Nowhere Man,", "Sitting in his Nowhere Land,", " Making all his nowhere plan for nobody." ; byte esct[]= { /* Esc-последовательность */ 27, 38, 0, /* Начало загрузки шрифта */ 40, 40, /* Номера начального и конечного кодов */ 0x8a, /* Ширина символа - 0a и сдвиг 80 */ /* Образ символа */ 32,28,34,42,42,42,42,42,16,0,0 }; int i; prt(27); prt(64); /* Инициализация принтера */ /* Выдача Esc-последовательности */ for (i=0; i<17; prt(esct[i++])); p1=peek(0x40,8); p2=p1+1; p3=p2+1; /* Опр.портов */ /* Подключение к вектору 17 */ *old17=getvect(0x17); setvect(0x17,h17); /* Печать текста */ for(i=0;i<3; fprintf(stdprn,"%s\n",st[i++])); setvect(0x17,old17); /* Восстановление вектора */ prt(27); prt(64); /* Инициализация принтера */ } /*==== Обработчик 17-го прерывания ====*/ void interrupt h17() { if (_AH==0) { /* Вывод символа */ if (_AL=='a') { /* Символ "a" - особая обработка */ /* Esc-послед. переключения на шрифт ОЗУ. */ ownchar(27); ownchar(0x25); ownchar(1); /* Печать символа ОЗУ с кодом 40 */ ownchar(40); /* Esc-послед. переключения на шрифт ПЗУ. */ ownchar(27); ownchar(0x25); ownchar(0); } else ownchar(_AL); /* Печать символа не "a". */ } else (*old17)(); /* Старый обработчик */ } /*==== Печать одного символа ====*/ ownchar(byte f) { byte stb; /* Байт состояния */ /* Ожидание готовности */ stb=0; while ((stb&0x80)==0) stb=inport(p2); outportb(p1,f); /* Вывод символа */ outportb(p3,0x0d); outportb(p3,0x0c); /* Строб */ }
Напрашивается мысль о том, что матричный принтер можно заставить выводить на печать не только символы, но и любые изображения, и такая возможность действительно имеется - это использование принтера в графическом режиме.



Пример 6

/*== ПРИМЕР 9.4 ==*/ /*==== Демонстpация страничной организации видеопамяти ===*/ #include <dos.h>
#include <stdio.h> #define PgUp 0x49 #define PgDown 0x51 #define Esc 27 #define byte unsigned char #define word unsigned int #define VSEG 0xb800 /* Адpес начала видеопамяти */ #define NPAGE 8
main() { char st[20]; /* Идентификатоp стpаницы */ char *s; word voff; /* Смещение относительно начала стp. */ word vpage; /* Сегментный адpес начала стpаницы */ byte tpage; /* Текущий номеp стpаницы */ int flag; /* Пpизнак окончания */ byte attr; /* Цвет */ /* Разметка стpаниц */ for (tpage=0; tpage<NPAGE; tpage++) { sprintf(st,"==page %d==",tpage); /* Каждая следующая стpаница в памяти сдвинута на 4096 байт или на 256 паpагpафов. */ vpage=VSEG+tpage*256; /* Код фонового цвета стpаницы совпадает с ее номеpом, код цвета символов - на 3 больше. */ attr=(tpage0xf0) tpage=NPAGE-1; textpage(tpage); break; case PgDown: /* Сдвиг на стpаницу впеpед */ if (++tpage>=NPAGE) tpage=0; textpage(tpage); break; default: putchar(7); break; } } } } /*==== Установка текущей стpаницы видеопамяти ====*/ textpage(byte pg) { union REGS rr; rr.h.ah=5; /* Функция 5 */ rr.h.al=pg; /* Номеp стpаницы */ int86(0x10,&rr,&rr); }
Роль альтернативных страниц в текстовом режиме уменьшается с повышением быстродействия компьютера. По-видимому, изначально дополнительные страницы были задуманы как средство быстрой смены изображения на экране: сначала новое изображение строится на невидимой в данный момент странице, а затем следует мгновенное переключение на эту страницу. Однако, даже на ПЭВМ с тактовой частотой 8 мгц разница во времени между построением на экране (методом прямой записи в видеопамять) даже весьма сложного изображения и переключением страниц практически не улавливается глазом. Поэтому этот метод сейчас не пользуется большой популярностью, многие программисты используют дополнительные страницы не по прямому назначению, а как обычную память для хранения данных, размещения буферов и т.п.



Пример 6

/*== ПРИМЕР 10.4 ==*/ /*========== Чтение Таблицы Размещения Файлов ==========*/ #include <dos.h> #include <alloc.h> #define byte unsigned char #define word unsigned int #define dword unsigned long /* Структура корневой записи */ 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, 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; main() { byte *buff; /* адрес буфера в ОП */ byte sys; /* признак диска > 32 Мбайт */ char drive; /* идентификатор диска */ byte fat16; /* признак 16-битной FAT */ word ss; int i,k,m,j;
ASK1:printf("\nУкажите идентификатор диска (A,B...) >"); drive=getche(); if (drive>'b') { ASK2:printf("\nОбьем лог. диска больше 32 Мбайт? (y/n) >"); sys=getche(); switch(sys) { 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 { sr.ds=FP_SEG(&parm); rr.x.bx=FP_OFF(&parm); parm.first_sect=0; parm.count=1; parm.ptr=buff; rr.x.cx=0xffff; } int86x(0x25,&rr,&rr,&sr); readerror(); rt=(struct RootRec *)buff; /* определение формата FAT */ if (rt->DOS4_flag==41) { /* для диска, отформатированного в DOS 4 можно определить формат FAT из Boot-сектора */ if (!memcmp(rt->FatForm,"FAT16",5)) fat16=1; else fat16=0; } else { ASK3:printf("\nФормат FAT 12-битный? (y/n)"); fat16=getche(); switch(fat16) { case 'y': fat16=0; break; case 'n': fat16=1; break; default: goto ASK3; } } /* Выделение памяти под FAT */ buff=(byte *)realloc(buff,rt->FatSize*512); if (buff==NULL) { printf("Нехватка памяти\n"); exit(); } /* Чтение FAT */ rr.h.al=drive-'a'; if (!sys) { /* маленький диск */ rr.x.cx=rt->FatSize; rr.x.dx=rt->ResSect; sr.ds=FP_SEG(buff); rr.x.bx=FP_OFF(buff); } else { /* большой диск */ parm.first_sect=rt->ResSect; parm.count=rt->FatSize; parm.ptr=buff; sr.ds=FP_SEG(&parm); rr.x.bx=FP_OFF(&parm); rr.x.cx=0xffff; } int86x(0x25,&rr,&rr,&sr); readerror(); clrscr(); /* Форматная распечатка FAT */ printf(" Диск %c: FAT - %d бит\n", drive-32,fat16*4+12); printf(" |"); for(i=0;i<10;printf("%5d ",i++)); printf("\n____|____________________________________"); printf("________________________"); for(j=0,i=0,k=0;i<220;i++) { if (!k) printf("\n%3d |",j); if (!fat16) { m=(i*3)/2; ss=*(word *)(buff+m); if(i%2) /* нечетный элемент */ ss>>=4; else /* четный элемент */ ss&=0x0fff; if (ss>0x0fef) printf(" %03xH ",ss); else printf("%5d ",ss); } else { m=i*2; ss=*(word *)(buff+m); if (ss>0xffef) printf("%04xH ",ss); else printf("%5d ",ss); } if (++k>=10) { j+=10; k=0; } } getch(); free(buff); } /*==== Проверка ошибок чтения ====*/ readerror() { if (rr.x.cflag) { printf("\nОшибка чтения: %x. ",rr.h.ah); printf("Нажмите любую клавишу...\n\7"); getch(); exit(); } }



Пример 6

struct DPB { byte dev_num; /* # драйвера */ byte dev_num_mod; /* # устр-ва в драйвере */ word SectSize; /* размер сектора (байт) */ byte MaxSect; /* макс.номер сектора в кластере */ byte Log2Sect; /* LOG2(размера кластера) */ word ResSect; /* число резервных секторов */ byte FatCnt; /* число копий FAT */ word RootSize; /* размер корневого каталога */ word Data1st; /* # 1-го сектора области данных */ word MaxClust; /* максимальный номер кластера */ byte FatSize; /* размер FAT */ word Root1st; /* 1-й сектор корневого каталога */ void *drv_ptr; /* адрес драйвера */ byte Media; /* тип носителя */ byte access; /* доступ к носителю */ struct DPB *next; /* ссылка */ word Free1st; /* первый свободный кластер */ word FreeCnt; /* число свободных кластеров */ };
Поля SectSize, ResSect, RootSize, FatCnt, FatSize, Media копируются из одноименных полей загрузочного сектора носителя (см.10.3.2). Поля MaxClust, Data1st и Root1st получются из данных загрузочного сектора путем несложных вычислений. Поле MaxSect содержит значение на 1 меньшее поля ClustSize загрузочного сектора. Поле Log2Sect дает возможность получить число секторов в кластере путем сдвига 1 на Log2Sect разрядов влево. Поле access имеет значение 0, если к носителю было обращение, или 0xFF - если не было, в последнем случае значения полей, источником которых является загрузочный сектор, могут не соответствовать действительности. Наконец, последние два поля содержат некоторую информацию о свободных кластерах, если такая информация отсутствует, то значения этих полей - 0 и 0xFFFF соответственно. При помощи поля next все DPB связываются в список (смещение 0xffff - признак конца спсика). Формат DPB для DOS 4.0 и последующих отличается от вышеприведенного тем, что размер поля FatSize составляет здесь не 1, а 2 байта и, следовательно, смещения всех последующих полей увеличиваются на 1.
В DOS имеются функции 0x1F и 0x32, возвращающие в DS:BX адрес DPB. Функция 0x1F возвращает DPB текущего диска, а 0x32 - диска, логический номер которого задается в DL (0 - текущий, 1 - A и т.д.; если задан неправильный номер диска, функция возвращает 0xFF в регистре AL). Эти функции, однако, "не удовлетворяются" значением доступа (access) 0xFF, а, если обращений к носителю не было, пытаются прочитать его загрузочную запись. В программных примерах главы 10 мы могли обращаться к функции 0x32 вместо того, чтобы читать загрузочный сектор.
В программе следующего примера, выводящей на экран содержимое всех блоков DPB, структура блока разбита на 2 части: struct DPB_1 и struct DPB_2. Поле FatSize описано как 2-байтное, но в зависимости от версии DOS адрес второй части блока (начиная с поля Root1st) назначается со смещением 16 (DOS 3.x) или 17 (DOS 4.x) относительно первой части.

Программа выводит все DPB дважды - получая их адреса как из системного списка, так и по функции DOS 0x32.

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