unsigned int segm, unsigned int
Пример 7
int peek( unsigned int segm, unsigned int offs); char peekb(unsigned int segm, unsigned int offs);
Для записи данных в память испольэуются функции:
Пример 7
/*= ПРИМЕР 3.1 =*/ /*=============== Перехват прерывания ===================*/ #include <dos.h>
#define VECT_ADDR(x) x*4 /* Вычисление адреса вектора */ int intr_num = 9; /* Номер прерывания */ int intr_count = 0; /* Счетчик прерываний */ void interrupt new_handler(); /* Описание нового обработчика прерывания */ void interrupt (* old_handler)(); /* Переменная для сохранения старого вектора */ unsigned int segm, offs; /* Сегмент и смещение из старого вектора */ main() { /* Получение старого вектора */ offs=peek(0,VECT_ADDR(intr_num)); segm=peek(0,VECT_ADDR(intr_num)+2); old_handler=MK_FP(segm,offs); /* Запись нового вектора */ disable(); poke(0,VECT_ADDR(intr_num),FP_OFF(new_handler)); poke(0,VECT_ADDR(intr_num)+2,FP_SEG(new_handler)); enable(); /* Ожидание 10-кратного срабатывания */ while (intr_count
Прежде всего - что делает эта программа. Она перехватывает прерывание 9 (аппаратное прерывание, поступающее при нажатии и при отпускании любой клавиши клавиатуры), а затем ожидает, пока счетчик прерываний на достигнет числа 10. После этого программа восстанавливает вектор и выводит на экран значение счетчика. Ее обработчик прерывания вызывает старый обработчик, а в дополнение к этому подсчитивает количество прерываний.
Номер прерывания задан в программе переменной intr_num, макрос VECT_ADDR определяет физический адрес вектора прерывания с заданным номером. Счетчик прерываний - переменная intr_count. Новый обработчик прерываний new_handler описан в программе как было рассказано выше. Интересно определение переменной old_ handler, служащей для размещения в ней старого вектора - она определена как указатель на функцию, имеющую тип void interrupt. Переменные segm и offs служат для сохранения адресных частей старого вектора.
Получение старого вектора состоит в чтении из памяти двух слов. По адресу вектора считывается смещение, а из следующих двух байт - сегмент. Запись нового вектора состоит в записи по тем же адресам двух слов: первое слово получается как смещение обработчика new_handler, а второе - как его сегмент.
Пример 7
/*== ПРИМЕР 6.6 ==*/ /*==================== Модель АЦП ===============*/ #include <dos.h>
#include <math.h>
#define TIMEINT 8 #define NN 100 /* Максимальное число отсчетов */ void interrupt (*oldtime)(); void interrupt newtime(); static int y[NN]; /* Накопитель отсчетов */ static int ny; /* Индекс в массиве y */ static int yc; /* Текущее значение sin */ static int kf; /* Счетчик вызовов oldtime */ union REGS rr; struct SREGS sr; void *readvect(int in); void writevect(int in, void *h);
main() { unsigned oldtic=65535; /* Старый коэфф.деления */ unsigned newtic=4095; /* Новый коэфф.деления */ unsigned char d; int k; double x; /* Аргумент ф-ции sin */ char line[81]; /* Строка для вывода */ for (ny=0; ny>8); /* Старший байт счетчика */ ny=-1; /* Признак того, что АЦП еще не началось */ kf=15; /* Подключение к вектору */ oldtime=readvect(TIMEINT); writevect(TIMEINT,newtime); /* Запуск "непрерывного процесса" */ for (x=ny=0; ny>8); /* Старший байт счетчика */ /* Вывод запомненных результатов */ for(ny=0; ny=0) /* Если АЦП началось, */ &&(ny<NN)) /* и NN отсчетов еще не набрано, */ y[ny++]=yc; /* запоминание очередного отсчета */ } /*==== Получение старого вектора ====*/ 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); }
Последний пример этой главы демонстрирует принципиальную возможность обеспечения работы с разделением времени. MS-DOS - однопрограммная система, она не поддерживает разделения ресурсов вычислительной системы между процессами, но у пользователя есть возможность самостоятельно обеспечить разделение, хотя это и непросто. В нашей программе имеется два процесса, каждый из которых программно реализован своей функцией. Каждому процессу отводится свой квант времени, заданный в числе тиков таймера.
Пример 7
/*== ПРИМЕР 7.7 ==*/ /*============ Действие комбинации Ctrl+Break ============*/ /* ВНИМАНИЕ! Для проверки реакции на Ctrl+ Break программу следует запускать вне Турбо-среды */ #include <dos.h>
void main() { union REGS rr; int i,k,o,m; clrscr(); printf ("При выполнении этого цикла Ctrl+Break не сработает"); for (o=160,i=0,k=0; i
В системе имеется так называемый статус обработки Ctrl+ Break. Если этот статус 1 (включен), то комбинация обрабатывается при любом системном вызове, если 0 (отключен) - только при вызовах, связанных со стандартным вводом и выводом. Прочитать или установить этот статус позволяет функция DOS 0 x33. Если при вызове функции в регистре AL - 1, то регистр DL должен содержать устанавливаемое значение статуса, если в AL - 0, статус читается. В обоих случаях в DL возвращается текущее значение статуса. В примере 7.8 предлагается испытать действие Ctrl+Break при разных значениях статуса обработки. В теле циклов применяется ни на что не влияющий системный вызов "получение версии DOS" (функция 0x30). Но если заменить этот вызов на вызов, например, функции 0x0B, то и первый цикл (выполняемый при статусе 0) будет прерываемым, так как для функций стандартного ввода обработка Ctrl+ Break не отключается.
Пример 7
/*== ПРИМЕР 8.8 ==*/ /*========== Работа принтера в графическом режиме ========*/ #include <dos.h> #include <math.h> #include <stdio.h> #include <alloc.h> #define prt(x) putc(x,stdprn); union REGS rr; main() { int col=4; /* Цвет - красный */ paint(col); /* Построение картинки */ getch(); prtgraph(1,1,80,80,col); /* Вывод на принтер */ } /* Построение картинки (из окружностей) */ paint(int col) { int x,y,fy,fx,k; /* Установка графического режима */ rr.x.ax=0x0010; int86(0x10,&rr,&rr); /* Построение контуров */ for (x=-24; x<=24; x++) { y=(int)sqrt((double)(576-x*x)); point(x+32,32-y,col); point(x+32,32+y,col); } for (x=-12; x<=12; x++) { y=(int)sqrt((double)(144-x*x)); point(x+20,32+y,col); point(x+44,32-y,col); } for (x=-4; x<=4; x++) { y=(int)sqrt((double)(16-x*x)); point(x+20,32-y,col); point(x+20,32+y,col); point(x+44,32+y,col); point(x+44,32-y,col); } /* Закрашивание */ for (fx=-24; fx<=24; fx++) { fy=(int)sqrt((double)(576-fx*fx)); x=fx+32; for (k=0, y=32-fy; y<=32+fy; y++) { rr.x.dx=y; rr.x.cx=x; rr.h.bh=0; rr.h.ah=0x0d; int86(0x10,&rr,&rr); if ((rr.h.al==col)&&((y!=32)(x==32))) k=1-k; if (k) point(x,y,col); } } } /* Вывод одной графической точки */ point( int x, int y, int c) { rr.x.dx=y; rr.x.cx=x; rr.h.bh=0; rr.h.al=c; rr.h.ah=0x0c; int86(0x10,&rr,&rr); } /*----------------------------------------------------*/ /* Графическая копия экрана. (x1,y1),(x2,y2) - координаты окна, c - цвет */ prtgraph(int x1, int y1, int x2, int y2, int c) { char *str; /* Графический образ строки для печати */ char *s; int strsize; /* Размер образа */ char esc[] = /* Начало Esc-послед.графической печати */ { 27,42,0,0,0 }; int x,y; /* Экранные координаты */ int bit; /* Счетчик разрядов в образе */ int i; /* Инициализация принтера */ prt(27); prt(64); /* Установка расстояния между строк */ prt(27); prt(51); prt(25); /* Выделение памяти для образа */ strsize=x2-x1+1; str=malloc(strsize); for (s=str, i=0; i<strsize; i++, s++) *s=0; /* Запись размера образа в Esc-послед. */ esc[3]=strsize%256; esc[4]=strsize/256; /* Перебор строк */ for (bit=7, y=y1; y<=y2; y++) { /* Перебор точек в строке */ for (s=str, x=x1; x<=x2; x++, s++) { /* Чтение точки */ rr.x.dx=y; rr.x.cx=x; rr.h.bh=0; rr.h.ah=0x0d; int86(0x10,&rr,&rr); /* Если цвет точки совпадает - заносится 1 в соответствующий разряд образа */ if (rr.h.al==c) *s|=(1<<bit); } if (--bit<0) { /* Если сформированы 8 разрядов образа, то: выводится начало Esc-последовательности, */ for(i=0;i<5;i++) prt(esc[i]); /* выводится образ и обнуляется, */ for (i=0, s=str; i<strsize; i++,s++) { prt(*s); *s=0; } prt(10); /* перевод строки */ bit=7; } } /* Если не весь образ выведен - вывод остатка */ if (bit<7) { for (i=0; i<5; i++) prt(esc[i]); for (i=0, s=str; i<strsize; i++,s++) prt(*s); prt(10); } /* Инициализация принтера */ prt(27); prt(64); }
Пример 7
/*== ПРИМЕР 9.5 ==*/ /*=================== Управление курсором ================*/ #include <dos.h>
#define byte unsigned char #define word unsigned int #define Esc 27 #define Enter 13 #define Up 0x48 #define Down 0x50 #define Left 0x4b #define Right 0x4d # define Home 0x47 union REGS rr; main() { word posc; /* Позиция курсора в линейных координатах */ word post; /* Позиция вывода текста в линейных коорд. */ byte y,x; /* Позиция в координатах x,y */ byte s1, s2; /* Нач.и кон.строки образа курсора */ byte mode=0; /* Атрибут мигания */ char *modes[]= { "нормальный", "невидимый" }; byte flag; /* признак окончания */ /*== 1. Перемещение курсора ==*/ for(clrscr(),posc=post=0; ;posc++) { y=post/80; x=post%80; setcurpos(x,y); printf("текст->%d,курсор->%d ",post,posc); getcurpos(&x,&y); post=y*80+x; y=posc/80; x=posc%80; setcurpos(x,y); if (getch()==27) break; } /*== 2. Удаление курсора ==*/ clrscr(); printf("Курсор удален\n"); setcurpos(1,26); getch(); /*== 3. Управление формой курсора ==*/ clrscr(); printf("Изменение формы курсора\n"); printf(" \\ s1 "); for(s1=0; s10) s1--; else s1=8; break; case Up: if (s2>0) s2--; else s2=13; break; case Right: if (++s1>9) s1=0; break; case Down: if (++s2>13) s2=0; break; case Home: s1=7; s2=13; break; } } } } /*==== Установка курсора ====*/ setcurpos(byte x,byte y) { rr.h.ah=2; /* функция 2 */ rr.h.bh=0; /* страница 0 */ rr.h.dh=y; /* координата y */ rr.h.dl=x; /* координата x */ int86(0x10,&rr,&rr); } /*==== Чтение позиции курсора ====*/ getcurpos(byte *x,byte *y) { rr.h.ah=3; /* функция 3 */ rr.h.bh=0; /* страница 0 */ int86(0x10,&rr,&rr); *y=rr.h.dh; /* координата y */ *x=rr.h.dl; /* координата x */ } /*==== Изменение формы курсора ==*/ curform(byte s1, byte s2, byte mode) { rr.h.ah=1; /* функция 1 */ rr.h.ch=(mode
Набор средств BIOS и DOS для вывода на терминал весьма богат.
В прерывании BIOS 0x10 функции 8 и 9 - соответственно чтение и вывод символа и атрибута в текущей позиции курсора (курсор при этом не сдвигается); в BH задается номер страницы, в AL получается/задается код символа, в AH - цветовой атрибут.
Пример 7
0 a d l s h r,
где
r | - 1 в этом бите означает, что файл только для чтения; |
h | - скрытый файл; |
s | - системный файл; |
l | - метка тома (может быть только в Корневом Каталоге); |
d | - подкаталог; |
a | - 1 в этом бите означает, что копия файла утилитой BACKUP не создавалась. |
Поля time и date содержат время и дату последней модификации файла. Формат времени: ЧЧЧЧЧММММММССССС (Часы, Минуты, Секунды); формат даты: ГГГГГГГММММДДДДД (Год, Месяц, День).
Поле cl - номер первого кластера, распределенного файлу, то есть, начало той цепочки, которая продолжается в FAT.
Поле size - размер файла в байтах. В DOS не предусмотрены какие-либо специальные признаки конца файлов. Применяемый в некоторых случаях символ ^Z (код 26) интерпретируется как признак конца конкретными программами, но не DOS. DOS же определяет конец файла по его размеру, получаемому из этого поля Элемента Каталога. Для подкаталога это поле содержит 0.
Программа примера 10.5 считывает и выводит на печать содержимое Корневого Каталога. Как и в предыдущих примерах, программа вынуждена сначала прочитать Boot-сектор. Из Boot-сектора программа узнает:
- количество элементов в каталоге - поле RootSize;
- размер каталога в секторах - RootSize/16 (в один сектор помещаются 16 Элементов Каталога);
- номер сектора, с которого начинается Корневой Каталог - ResSect+FatSize*FatCnt (следом за двумя копиями FAT).
Пример 7
/*== ПРИМЕР 13.2 ==*/ /*================= Распечатка всех DPB =================*/ #include <dos.h> #define byte unsigned char # define word unsigned int struct DPB_1 { /* 1-я часть DPB */ byte dev_num, dev_num_mod; word SectSize; byte MaxSect, Log2Sect; word ResSect; byte FatCnt; word RootSize, Data1st, MaxClust, FatSize; } *dpb_1; struct DPB_2 { /* 2-я часть DPB */ word Root1st; void *drv_ptr; byte Media, access; struct DPB_1 *next; word Free1st, FreeCnt; } *dpb_2; word dd_seg, dd_off; /* адрес 1-го DPB */ byte ldrive; /* номер диска */ byte dos; /* номер версии DOS */ union REGS rr; struct SREGS sr; main() { /* номер версии DOS */ rr.h.ah=0x30; intdos(&rr,&rr); dos=rr.h.al; /* адрес CVT */ rr.h.ah=0x52; intdosx(&rr,&rr,&sr); printf("\nТаблица DPB по спискам\n"); /* адрес 1-го DPB */ dd_off=peek(sr.es,rr.x.bx); dd_seg=peek(sr.es,rr.x.bx+2); dpb_1=(struct DPB_1 *)MK_FP(dd_seg,dd_off); while(FP_OFF(dpb_1)!=0xffff) { /* движение по списку */ print_dpb(); dpb_1=dpb_2->next; } printf("\nТаблица DPB по INT 32H\n"); for(ldrive=1; ;ldrive++) { /* перебор всех дисков */ rr.h.ah=0x32; /* функция 32 */ rr.h.dl=ldrive; /* номер диска */ intdosx(&rr,&rr,&sr); if (rr.h.al==0xff) break; /* адрес DPB для диска ldrive */ dpb_1=(struct DPB_1 *)MK_FP(sr.ds,rr.x.bx); print_dpb(); } } /*==== распечатка содержимого DPB ====*/ print_dpb() { /* смещение 2-й части DPB зависит от версии DOS */ if (dos<4) dpb_2=(struct DPB_2 *)((char *)dpb_1+0x10); else dpb_2=(struct DPB_2 *)((char *)dpb_1+0x11); printf("\nАдрес DPB - %Fp\n",dpb_1); printf(" устройство %u(%u) - диск %c\n", dpb_1->dev_num,dpb_1->dev_num_mod,'A'+dpb_1->dev_num); printf(" тип носителя - %02X\n",dpb_2->Media); printf(" адрес драйвера - %Fp\n",dpb_2->drv_ptr); printf(" разм.сектора - %u(байт)\n", dpb_1->SectSize); printf(" разм.кластера - %u(сект) %u\n", dpb_1->MaxSect+1, 1<<dpb_1->Log2Sect); printf(" рез.секторов - %u\n",dpb_1->ResSect); printf (" всего кластеров - %u, начиная с сектора %u\n", dpb_1->MaxClust,dpb_1->Data1st); printf(" FAT - "); if (dos<4) printf("%u",(byte)dpb_1->FatSize); else printf("%u",dpb_1->FatSize); printf("(сект) * %u\n",dpb_1->FatCnt); printf (" корневой каталог - %u(элементов) с сектора %u\n", dpb_1->RootSize,dpb_2->Root1st); printf(" доступ - %X\n",dpb_2->access); printf(" свободный кластер - %04X",dpb_2->Free1st); printf(" (всего - %u)\n",dpb_2->FreeCnt); if (getch()==27) exit(); }
Одно из полей CVT содержит указатель на начало Массива Текущих Каталогов. В версиях DOS до 3.0 такого массива не было, подобная информация содержалась в DPB. В современных версиях DOS возникла необходимость в такой структуре данных в связи с тем, что новые команды DOS SUBST и JOINT позволяют присвоить некоторому узлу дерева каталогов идентификатор диска или наоборот - описать диск как подкаталог другого диска. Общее количество возможных идентификаторов дисков задается параметром команды LASTDRIVE в CONFIG.SYS и хранится в CVT. Каждый элемент массива - Структура Текущего Каталога (CDS - Current Directory Structure) сохраняет информацию о текущем каталоге на логическом диске.
Структура CDS для версий DOS 3.x следующая: