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

то результаты выполнения операторов:



Пример 2

union REGS rr;
то результаты выполнения операторов:



Пример 2

int intdos(union REGS *inregs, union REGS *outregs);
Вызов этой функции эквивалентен вызову функции int86 со значением параметра int_num = 0x21.
В приведенных выше функциях для передачи параметров используются только регистры общего назначения. Если для передачи параметров требуется использовать также и сегментные регистры, то можно воспользоваться функциями:



Пример 2

/*= ПРИМЕР 4.3. =*/ /*========== Горячая перезагрузка системы ===========*/ #include <dos.h> main() { /* 2-й и 3-й параметры вызова нулевые, т.к. для прерывания 0x19 никаких параметров не требуется */ int86(0x19,00,00); }
Вы обратили внимание на слово "возможно" в предыдущей фразе? Скорее всего, программа приведет к "зависанию" системы. Дело в том, что при "горячей" перезагрузке не переустанавливаются векторы прерываний BIOS, таким образом, если прерывания BIOS были к моменту выдачи INT 19H перехвачены какими-то резидентными программами, после перезагрузки системы эти векторы будут указывать неизвестно куда. Поэтому не следует возлагать больших надежд на "горячую" перезагрузку.

В "холодном" и "теплом" вариантах перезагрузка выполняется с самого начала - с выполнения программы POST. Различие между "холодным" и "теплым вариантами заключается в том, что в первом случае POST выполняется в полном объеме - как при нажатии кнопки RESET, а во втором из нее исключается тестирование оборудования и оперативной памяти - как при нажатии комбинации клавиш Ctrl+ Alt+Del. Для запуска перезагрузки надо просто передать управление по адресу FFFF:0000. Тип перезагрузки BIOS определяет из содержимого слова по адресу 0040:0072 - если там находится код 0x1234, выполняется "теплая" перезагрузка, иначе - "холодная". Программное выполнение такой перезагрузки представлено в примере 4.4.





Пример 2

/*= ПРИМЕР 5.2 =*/ /*============= Получение списка оборудования ============*/ #include <dos.h>
main() { union REGS rr; unsigned int d; /* список оборудования */ int i;
/*== Чтение байта обоpудования через прерывание 0x11 ==*/ /* Прерывание 0x11 возвращает его в регистре AX */ int86(0x11,&rr,&rr); /* Побитная распечатка списка оборудования */ printf ("Список активного оборудования из прерывания 11 - "); for (i=15; i>=0; printf("%d",(rr.x.ax>>i--)&0x01)); printf(" (%04x)\n",rr.x.ax); /* == Чтение байта обоpудования из памяти BIOS == */ /* Будет получено то же самое */ d=peek(0x40,0x10); printf ("Список активного оборудования из памяти BIOS - "); for (i=15; i>=0; printf("%d",(d>>i--)&0x01)); printf(" (%04x)\n",d); /* == Раскодирование списка обоpудования == */ printf(" Дисководов ГМД - "); if (d&0x0001) printf("%d\n",((d&0x00c0)>>6)+1); else printf("нет\n"); printf(" Сопроцессор 8087 - "); if (d&0x0002) printf("есть\n"); else printf("нет\n"); printf(" Тип дисплейного адаптера - "); switch (d&0x0030) { case 0: printf("EGA/VGA"); break; case 0x10: printf("CGA,40-кол"); break; case 0x20: printf("CGA,80-кол"); break; case 0x30: printf("MDA"); break; } printf(" (неточно)\n"); printf(" Первичный блок памяти - "); switch (d&0x000c) { case 0: printf("16 Кбайт\n"); break; case 4: printf("32 Кбайт\n"); break; case 8: printf("48 Кбайт\n"); break; case 12: printf("64 Кбайт или больше\n"); break; } printf(" Портов RS232 - %d\n",(d&0x0e00)>>9); printf(" Джойстик - "); if (d&0x1000) printf("есть\n"); else printf("нет\n"); printf(" Принтеров - %d\n",(d&0xe000)>>14); }
Бросается в глаза, что список оборудования содержит явно недостаточно информации по дисплейному адаптеру - видимо, при его проектировании не была учтена возможность появления новых типов адаптеров после CGA.



Пример 2

/*== ПРИМЕР 6.1 ==*/ /*============== Генерация звука ====================*/ #include <dos.h>
main() { unsigned int gamma[] = /* Коэффициенты деления для нот */ { 912,813,724,678,609,542,483 }; char *gnames[] = /* Названия нот */ { "до","ре","ми","фа","соль","ля","си" }; int i; for(i=0; i>8); /* Старший байт счетчика */ /* Включение динамика. Читается содержимое порта, в него записываются 1 в разряды 0, 1, затем пишется в порт */ outportb(0x61,inportb(0x61)|0x03); } /*==== Выключение звука ====*/ silence() { /* записываются 0 в разряды 0, 1 порта 0x61 */ outportb(0x61,inportb(0x61)&0xfc); }
И еще одно замечание. Большинство языков программирования высокого уровня имеют стандартные функции, аналогичные нашим tone и silence (в Турбо-Си это sound и nosound). Но высота звука для этих функций задается не коэффициентом деления, а частотой, которая связана с коэффициентом деления соотношением: Частота = 1193180 / Коэфф.деления



Пример 2

/*== ПРИМЕР 7.2 ==*/ /*============ Прерывание от клавиатуры ================*/ #include <dos.h>
void interrupt (*old9)(); /* Для сохр. старого вектора */ void interrupt new9(); /* Описание нового обработчика */ unsigned char a_code=0x1e; /* Скан-код клавиши "a" */ union REGS rr; struct SREGS sr; void *readvect(int in); void writevect(int in, void *h); /*==== main ====*/ main() { char string[80]; /* Перехват вектора 9 */ old9=readvect(9); writevect(9,new9); printf("\nВводите строку символов>"); scanf("%s",string); /* Ввод строки */ /* Восстановление вектора 9 */ writevect(9,old9); printf("%s\n",string); } /*==== Обработчик прерывания 9 ====*/ void interrupt new9() { unsigned char c; /* Чтение scan-кода и сравнение его с "горячим" */ if (inportb(0x60)==a_code) { /* Если горячий код - подтверждение приема... */ c=inportb(0x61); outportb(0x61,c|0x80); outportb(0x61,c); /* и сброс контроллера прерываний. */ outportb(0x20,0x20); } else (*old9)(); /* Если нет - вызов сист.обработчика */ } /*==== Получение старого вектора ====*/ void *readvect(int in) { rr.h.ah=0x35; rr.h.al=in; intdosx(&rr,&rr,&sr); return(MK_FP(sr.es,rr.x.bx)); } /*==== Запись нового вектора ====*/ void writevect(int in, void *h) { rr.h.ah=0x25; rr.h.al=in; sr.ds=FP_SEG(h); rr.x.dx=FP_OFF(h); intdosx(&rr,&rr,&sr); }



Пример 2

/*== ПРИМЕР 8.2 ==*/ /*=============== Печать символа на принтере =============*/ #include <dos.h> main() { int p1,p2,p3,i; /* Номера портов */ unsigned char stat; /* Байт состояния */ /* Символы для печати (10 - перевод строки) */ char a[]={24,'a','b','c','d','e','f','g','h','i','j',10}; /* Опеределение портов */ p1=peek(0x40,8); p2=p1+1; p3=p2+1; /* Инициализация принтера */ outportb(p3,0x08); delay(50); outportb(p3,0x0c); /* Проверка состояния */ stat=inport(p2); if ((stat&0x20)!=0) { printf("Ошибка принтера. Байт состояния = %02x\n",stat); exit(); } for(i=0;i<12;i++) { /* Ожидание готовности */ while ((stat&0x80)==0) stat=inport(p2); /* Символ в базовый порт */ outportb(p1,a[i]); /* Строб */ outportb(p3,0x0d); outportb(p3,0x0c); /* Проверка состояния */ stat=inport(p2); if ((stat&0x20)!=0) { printf("Ошибка принтера. Байт состояния = %02x\n",stat); exit(); } } }



Пример 2

/*== ПРИМЕР 9.2 ==*/ /*============= Текстовые видеоpежимы (EGA) =============*/ #include <dos.h>
#include <conio.h> # define byte unsigned char byte getmode(); int x_modes(); byte nmode,mode; /* Номеp, код pежима */ union REGS rr; byte k; int i; main() { /* заполнение экрана */ clrscr(); textcolor(2); for(i=1; i
В этом примере код установленного режима и число позиций в строке экрана выбираются из области памяти BIOS. Приведем адреса ячеек этой области, связанных с видеоадаптерами:
40:49(1 байт)- текущий видеорежим;
40:4A(2 байта)- число знакомест в строке;
40:4C(2 байта)- размер видеообласти;
40:4E(2 байта)- смещение в видеопамяти текущей (активной) страницы;
40:50(8x4=32 байта)- координаты курсора на видеостраницах;
40:60(2 байта)- форма курсора;
40:62(1 байт)- номер текущей видеостраницы;
40:63(4 байта)- специальная информация вода-вывода;
40:84(5 байт)- специальная информация ввода-вывода (EGA);
40:A4(4 байта)- специальная информация ввода-вывода (EGA).


Пример 2

/*== ПРИМЕР 10.1 ==*/ /*========== Чтение сектора средствами BIOS и DOS ========*/ #include <dos.h> /* типы и структуры данных */ #define byte unsigned char #define word unsigned int /* описание функций */ void Sect_to_Daddr(word sect,struct daddr *d); word Daddr_to_Sect(struct daddr *d); word get( word min, word max, char *name); struct daddr { byte h; /* головка */ word s, /* сектор */ t, /* дорожка */ ts; /* сектор, дорожка упакованные */ } a; /* физический дисковый адрес */ word ls; /* номер логического сектора */ /* параметры дискеты */ word nh=2, /* число головок */ ns=9, /* число секторов на дорожке */ nt=40, /* число дорожек */ s0=0, /* номер 1-го сектора */ nls=720; /* общее число секторов */ byte drive; /* номер логич.диска */ byte buff13[512]; /* буфер сектора */ byte buff25[512]; /* буфер сектора */ /*==== main ====*/ main() { union REGS rr; struct SREGS sr; byte fx, flag; byte mode; int i, i0, y;
for (fx=0; fx==0;) { printf("диск (A|B) >"); drive=getche(); if ((drive!='a')&&(drive!='b')) printf("\7"); else fx=1; } drive-='a'; for (flag=0; flag==0;) { printf("\nA - задание физического адреса;\n"); printf("S - задание номера сектора;\n"); printf("Q - конец работы.\n"); printf("--->"); mode=getche(); printf("\n"); switch(mode) { case 'q': flag=1; continue; case 'a': a.h=get(0,nh-1,"головка"); a.t=get(0,nt-1,"дорожка"); a.s=get(1,ns,"сектор"); a.ts=(a.t<<8)|a.s|((a.t&0x300)>>2); ls=Daddr_to_Sect(&a); break; case 's': ls=get(0,nls-1,"логич.сектор"); Sect_to_Daddr(ls,&a); break; default: printf("\7"); continue; } /* чтение сектора при помощи прерывания 13. */ rr.h.ah=2; /* функция ЧТЕНИЕ */ rr.h.al=1; /* секторов 1 */ rr.h.dl=drive; /* адрес диска */ rr.h.dh=a.h; rr.x.cx=a.ts; /* адрес на диске */ sr.es=FP_SEG(buff13); /* адрес в ОП */ rr.x.bx=FP_OFF(buff13); int86x(0x13,&rr,&rr,&sr); /* Проверка ошибок чтения */ if (rr.x.cflag) { printf("Ошибка при INT 13 : %u\n",rr.h.ah); exit(0); } /* Чтение при помощи прерывания DOS 25 */ rr.h.al=drive; /* логич.диск */ rr.x.cx=1; /* секторов 1 */ rr.x.dx=ls; /* номер сектора */ sr.ds=FP_SEG(buff25); /* адрес в ОП */ rr.x.bx=FP_OFF(buff25); int86x(0x25,&rr,&rr,&sr); /* Проверка ошибок чтения */ if (rr.x.cflag) { printf("\nОшибка при INT 25 : %u. ",rr.h.ah); exit(0); } /* дамп буферов */ clrscr(); printf(" INT 13 : гол=%d дор=%-2d сект=%d |", a.h,a.t,a.s); printf("| INT 25 : лог.сект=%d \n",ls); printf(" -------------------------------------|"); printf("|-------------------------------------\n"); for(i0=0,y=3;y<25; y++,i0+=12) { for (i=0; i<12; printf(" %02X",buff13[i0+i++])); printf(" "); for (i=0; i<12; printf(" %02X",buff25[i0+i++])); printf("\n"); } printf(" Нажмите любую клавишу..."); getch(); } } /*=== формирование физич.дискового адреса из # сектора ===*/ void Sect_to_Daddr(word sect,struct daddr *d) { /* sect - номер сектора, a - адрес на диске */ int s; d->s=sect%ns+1; s=sect/ns; d->h=s%nh; d->t=s/nh; d->ts=(d->t<<8)|d->s|((d->t&0x300)>>2); } /*=== формирование # сектора из физич.дискового адреса ===*/ word Daddr_to_Sect(struct daddr *d) { word s; s=((d->t*nh)+d->h)*ns+d->s-1+s0; return(s); } /*==== ввод какого-либо параметра ====*/ word get(word min, word max, char *name) { byte fx; word val; for (fx=0; fx==0;) { printf("%s >",name); scanf("%d",&val); if ((val<min)(val>max)) printf("\7"); else fx=1; } return (val); }



Пример 2

/*== ПРИМЕР 11.2 ==*/ /*============ Определение места PID в системе ===========*/ #include <dos.h> # define word unsigned int word mypid; /* PID программы */ word dosend; /* конец памяти DOS*/ word seg,off; /* текущий адрес */ word vpid; /* прочитанный PID */ word /* массивы для запоминания адресов */ mseg[10], moff[10]; int nmem; /* число адресов */ int i; union REGS rr; struct SREGS sr; main() { /* получение адреса конца памяти DOS */ rr.h.ah=0x52; intdosx(&rr,&rr,&sr); dosend=peek(sr.es,rr.x.bx-2); /* Получение своего PSP (PID )*/ rr.h.ah=0x62; intdos(&rr,&rr); mypid=rr.x.bx; printf("PID программы = %04X\n",mypid); /* Поиск PID в системе */ nmem=0; /* поиск происходит начиная от сегмента 60 до конца памяти, занимаемой DOS */ for(seg=0x60;seg<dosend;seg++) for(off=0;off<16;off++) if (peek(seg,off)==mypid) { /* PID найден */ /* изменяется текущий PID */ rr.h.ah=0x50; rr.x.bx=mypid+1; intdos(&rr,&rr); /* повторная выборка из памяти */ vpid=peek(seg,off); /* PID восстанавливается */ rr.h.ah=0x50; rr.x.bx=mypid; intdos(&rr,&rr); if (vpid==mypid+1) { /* адрес слова, хранящего PID, запоминается */ mseg[nmem]=seg; moff[nmem++]=off; } } /* Контрольные операции */ /* По найденным адресам записывается другой PID */ for(i=0; i<nmem; i++) poke(mseg[i],moff[i],mypid+1); /* проверяется, изменился ли PID, хранящийся в системе */ rr.h.ah=0x62; intdos(&rr,&rr); vpid=rr.x.bx; /* Восстанавливается содержимое */ for(i=0; i<nmem; i++) poke(mseg[i],moff[i],mypid); /* Печатаемое значение должно совпадать с mypid+1 */ printf("Измененный PID = %04X\n",vpid); /* Распечатка адресов хранения PID в DOS */ for (i=0;i<nmem; i++) printf("Адрес хранения PID = %04X:%04X\n", mseg[i],moff[i]); printf("Нажмите любую клавишу...\n"); getch(); }
Примененная в примере 11.2 техника - поиск места PID в системе и изменение его прямой записью в память может быть использована в резидентных программах, если почему-либо невозможно применить функцию 0x50.



Пример 2

/*== ПРИМЕР 12.1 ==*/ /*================== Выдача карты памяти =================*/ #define byte unsigned char #define word unsigned int #include <dos.h> #include <string.h> struct MCB { /* блок упpавления памятью */ char type; word owner, size; byte reserved[3]; char pgmname[8]; }; struct MCB *cmcb; /* адpес текущего MCB */ struct MCB *emcb; /* адpес MCB сpеды */ word memtop; /* сегм.адрес начала памяти */ word csegm; /* сегментный адpес текущего MCB */ word othersegm; /* сегм.адрес другого MCB */ word fathersegm; /* сегм.адрес родителя */ byte *envstr; /* адpес стpоки окружения */ int envlen; /* длина очередной строки окружения */ int envsize; /* размер блока окружения */ byte dos; /* номер версии DOS */ union REGS rr; struct SREGS sr; int i; char *s; main() { clrscr(); /* получить номер версии DOS */ rr.h.ah=0x30; intdos(&rr,&rr); dos=rr.h.al; /* получить адрес системных блоков */ rr.h.ah=0x52; intdosx(&rr,&rr,&sr); /* получить адрес начала цепочки */ memtop=csegm=peek(sr.es,rr.x.bx-2); do { cmcb=(struct MCB *)MK_FP(csegm,0); printf("Addr=%04X:0000 PID=%04X Size=%-6u ", csegm,cmcb->owner,cmcb->size*16); if (cmcb->owner==0) printf(" Free"); /* блок свободен */ else { /* блок занят */ /* блок принадлежит DOS ? */ if (cmcb->owner<memtop) printf(" Dos "); else { /* блок не принадлежит DOS */ /* если PID хозяина указывает на текущий блок, то это программный сегмент */ if (csegm==cmcb->owner-1) printf(" Pgm "); else { /* адpес блока сpеды для хозяина этого блока памяти находится в PSP хозяина со смещением 0x2C */ othersegm=peek(cmcb->owner,0x2c); /* адpес родителя для программы-хозяина этого блока находится в PSP хозяина со смещением 0x16 */ fathersegm=peek(cmcb->owner,0x16); /* если на текущий блок указывает адрес окружения хозяина, то это блок окружения */ if (csegm==othersegm-1) printf(" Env "); /* иначе - это блок данных */ else printf(" Data "); } /* если хозяин сам себе родитель, то это COMMAND */ if (cmcb->owner==fathersegm) printf("COMMAND.COM"); else { /* для другой программы узнаем ее имя */ if (dos>3) { emcb=(struct MCB *)MK_FP(cmcb->owner-1,0); for (i=0,s=emcb->pgmname; i<8; i++) { if (*s>0) printf("%c",*(s++)); else printf(" "); } printf(" "); } if (dos>2) { /* для DOS 3.0 и выше имя - из строки вызова */ emcb=(struct MCB *)MK_FP(othersegm-1,0); envsize=emcb->size*16; /*размер окружения */ envstr=(char *)MK_FP(othersegm,0); do { /* пропуск строк окружения до пустой строки */ envlen=strlen(envstr)+1; envstr+=envlen; envsize-=envlen; } while ((envlen>1)&&(envsize>0)); envstr+=2; envsize-=2; /* 2 байта - кол.строк */ /* envstr - указатель на строку вызова */ if (envsize>0) printf("%s",envstr); } } } } printf("\n"); csegm+=(cmcb->size+1); /* переход к следующему блоку */ } while (cmcb->type!='Z'); /* выход по последн.блоку */ getch(); }



Пример 2

struct DR_HEAD { struct dr_head *next; /* адрес следующего */ word attr; /* атрибуты */ word strat_off, intr_off; /* смещения секций */ char name[8]; /* имя */ };

Слово атрибутов драйвера в старшем разряде содержит признак символьного (1) или блочного (0) устройства. Интерпретация остальных разрядов слова атрибутов существенно разная для символьных и блочных устройств. Для символьных устройств слово атрибутов имеет формат:



Пример 2

/*==== ПРИМЕР 14.1 ====*/ /*==== Обработка критической ошибки ====*/ #include <dos.h> #include <stdio.h> #define byte unsigned char # define word unsigned int void interrupt new_24(); void interrupt (* old_24)(); /* адрес старого обработчика критической ошибки */ /* информация об ошибке, запомненная в обработчике */ byte save_ah, save_al, class, actions; word save_di, save_bp, save_si, ext_code, locate; /* сообщения */ char *msg1[]= {"ИГНОРИРОВАТЬ","ПОВТОРИТЬ","СНЯТЬ"}; char *msg2[]= {"системная обл.","FAT","каталог","обл.данных"}; char *msg3[]={"защита от записи","неизвестное устройство", "диск не готов","неизвестная команда","ошибка CRC", "неправильная структура запроса","ошибка поиска", "неизвестный тип диска","сектор не найден", "конец бумаги","ошибка записи","ошибка чтения", "общий сбой","наруш.режима разделения", "наруш.блокировки","ошибка смены диска", "FCB недоступен","переполн.буфера разделения" }; union REGS rr; struct SREGS sr; int dos; FILE *f; main() { /* получить версию DOS */ rr.h.ah=0x30; intdos(&rr,&rr); if ((dos=rr.h.al)==3) dos=0; /* перехват вектора */ old_24=getvect(0x24); setvect(0x24,new_24); /* эксперимент 1 */ save_ah=save_al=save_di=save_bp=save_si=0; /* получить адрес DPB (при неготовом диске) */ rr.h.ah=0x32; rr.h.dl=2; intdosx(&rr,&rr,&sr); error_scan(); /* эксперимент 2 */ save_ah=save_al=save_di=save_bp=save_si=0; /* печатать (при неготовом принтере) */ fprintf(stdprn,"aaa"); error_scan(); /* восстановление вектора (необязательно) */ setvect(0x24,old_24); } /*-------------------------------------*/ /*== Распечатка информации об ошибке ==*/ error_scan() { int i; byte k; char *s; if (!(save_ah|save_al|save_di|save_bp|save_si)) printf("Ошибки нет\n"); else { printf("\nОшибка при "); if (save_ah&1) printf("записи, "); else printf("чтении, "); if (save_ah&0x80){ printf("устройство "); s=(char far *)MK_FP(save_bp,save_si)+10; for (i=0; i<8; i++,s++) printf("%c",*s); printf("\n"); } else { printf("диск %c\n",save_al+'A'); printf("Место ошибки - %s\n",msg2[(save_ah>>1)&3]); } for (i=0,k=0x20; k>0x04; k>>=1,i++) if (save_ah&k) printf("Можно %s\n",msg1[i]); else printf("Нельзя %s\n",msg1[i]); printf("Тип ошибки - %s\n",msg3[save_di]); printf("РАСШИРЕННАЯ ИНФОРМАЦИЯ:\n"); printf(" Код ошибки - %04Xh\n",ext_code); printf(" Класс ошибки - %02Xh\n",class); printf(" Действия - %02Xh\n",actions); printf(" Место - %02Xh\n",locate); } printf("Нажмите любую клавишу...\n"); getch(); } /*-------------------------------------*/ /*== Обработчик критических ситуаций ==*/ void interrupt new_24 (bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs) word bp,di,si,ds,es,dx,cx,bx,ax,ip,cs,flgs; { /* сохранение основной информации об ошибке */ save_ah=ax>>8; save_al=ax&0xff; save_di=di; save_bp=bp; save_si=si; /* получение расширенной информации об ошибке */ rr.h.ah=0x59; /* функция 59 */ rr.x.bx=dos; intdos(&rr,&rr); ext_code=rr.x.ax; class=rr.h.bh; actions=rr.h.bl; locate=rr.h.ch; /* действия - отменить запрос */ ax=3; }

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