Рубрика "IS-DOS - программистам" No 6. music by Ironman (C)Леонтьев А.Г. _______________________________ КРАТКИЙ КУРС ПРОГРАММИРОВАНИЯ В СРЕДЕ IS-DOS. (под общей редакцией Елисеева В. А.) Продолжение, начало в No 5. От редактора. Здравствуйте, уважаемые читатели! Сегодня в рубрике "IS-DOS - программис- там" мы продолжаем публикацию новой кни- ги А. Леонтьева, начатую в прошлом номе- ре журнала. Для тех, кто еще не знает, сообщаем, что Леонтьев Алексей Григорье- вич является ведущим системным програм- мистом Отдела IS-DOS, признанным автори- тетом в области нашей системы, одним из Отцов" IS-DOS. Надеемся, что эта книга поможет Вам в освоении программирования всреде IS-DOS и пополнит ряды программистов, способных писать под IS-DOS, новыми та- лантами, а наш ассортимент программ - но- выми разработками, среди которых, между прочим, могут быть и Ваши. - Итак: А. Леонтьев ЧТО ДЕЛАТЬ? или КАК ПЕРЕСТАТЬ БЕСПОКОИТЬСЯ И НАЧАТЬ ПРОГРАММИРОВАТЬ В ИСДОСЕ 6. WAS IST DOS? или САМЫЙ НИЖНИЙ УРОВЕНЬ (0-ой уровень, он же "DOS.SYS") Нижний уровень системы в iS-DOS (рестарты с #00 по #1F) работает с кэшем, блочными и символьными устройствами, ка- налами и прерываниями. Этот уровень не знает ни о файлах, ни о командных стро- ках, ни об оконной технологии. Всем этим занимаются более высокие уровни. Итак, более подробно о рестартах: Первые 7 (с #00 по #06) рестарты уровня "DOS" работают с так называемой "кэш-памятью", поэтому сперва несколько слов о том, что это, собственно, такое. Итак кэш блочных устройств - это системный буфер используемый исключи- тельно для блоков (по 256 байт) из кото- рых состоят все блочные устройства. Кэш создается рестартом $creat(#00). Система автоматически создает его при загрузке, а также при выходе в систему по рестарту $shel0(#80) или по RET'у со зна- чениями #F6 или #F7 в регистре А процес- сора. Его пересоздают также некоторые ко- мандные файлы, например копировщики. Де- лают они это, в основном, чтобы уменьшить размер кэша, и, таким образом, выиграть несколько килобайт памяти под буферы, а также чтоб вернуть после этого кэш в прежнее состояние. Как уже говорилось в самом начале книги, система iS-DOS занимает память ZX сверху вниз, и кэш, создаваемый в послед- нюю очередь лежит под системой. Сверху он ограничен областью каналов. Верхний ад- рес области кэш-памяти можно достать из вектора конфигурации системы g_cnf, ис- пользовав рестарт $g_cnf(#10) со смеще- нием +32: LD C,#10 ;$g_cnf, возвращает в RST #10 ;HL' адрес вектора EXX ;адрес - в HL LD DE,32 ;прибавляем смещение ADD HL,DE LD E,(HL) ;получаем в DE INC HL ;искомый верхний LD D,(HL) ;адрес кэша Нижний адрес кэша, естественно, за- висит от верхнего адреса. Если Вам катас- трофически не хватает памяти, можно уб- рать лишние резиденты (в этом Вам помо- жет set.com с ключом /e). Можно также подсократить область каналов, что умеет делать channel.com. Это вызовет перемеще- ние всей области кэша вверх и, следова- тельно, поднимет и нижнюю его границу, освободив память для Ваших целей. Однако, из Вашей программы проще всего сократить кэш. Размер кэша, устанавливаемый по умолчанию, лежит в векторе g_cnf со сме- щением +7. Адрес нижней границы кэша на- ходится в том же векторе со смещением +5 и +6. При необходимости его можно рас- считать по формуле (1): CACHE = DGCHN - CSIZE * 260 (1) где CACHE - искомый адрес нижней границы кэша, DGCHN - адрес начала области кана- лов, а CSIZE - размер кэша в блоках. По этому адресу располагается ката- лог кэша в виде массива описателей бло- ков (по 4 байта на каждый блок кэша), после которого располагаются сами блоки. Структура кэша: ┌───────┬───────┬───────────────────────┐ │Смеще- │Размер │ Описание │ │ние│ │ │ ├───────┼───────┼───────────────────────┤ │ 0│EDSIZ*4│ каталог кэша, состоит │ │ │ │ из 4-байтовых описа- │ │ │ │ телей блоков: │ │ │ │ │ │ 0│4 │ описатель 1-го блока │ │ 4│4 │ описатель 2-го блока │ │ 8│4 │ описатель 3-го блока │ │ . . . │ . . │ . . . │ │EDSIZ*4│256│ 1-й блок ┐ │ │ │256│ 2-й блок ├───┐ │ │ │256│ 3-й блок ┘ │ │ │ . . . │ . . │ это, собственно, тело │ │ │ │ кэша, его адрес хра- │ │ │ │ нится в векторе g_cnf │ │ │ │ со смещением -5. │ └───────┴───────┴───────────────────────┘ где EDSIZ - размер кэша. Описатель блока имеетследующую структуру: ┌────┬────┬─────────────────────────────┐ │Сме-│Раз-│Описание │ │ще- │мер │ │ │ние ││ │ ├────┼────┼─────────────────────────────┤ │0 │1 │регистр состояния блока, │ │││совмещенный с номером устрой-│ │││ства. Биты: │ │││ 7 - блок модифицирован (1) │ │││ 6 - блок защищен (1) │ │││ 5 - блок существует(1) │ │││ 4 - не используется │ │││ 3..0 - номер устройства, │ │││которому принадлежит блок. │ ├────┼────┼─────────────────────────────┤ │1 │1 │ счетчик обращений к блоку │ ├────┼────┼─────────────────────────────┤ │2 │2 │ номер блока на устройстве. │ └────┴────┴─────────────────────────────┘ ММММММММММММММММММММММММММММММММинимальный размер кэша равен 6 бло- кам. Оптимальный в iS-DOS Classic - от 20 до 30, в CHIC - около 40. Кэш необходим для работы файловой службы. Через него система обращается к устройствам, т.е. читает и пишет описате- ли файлов каталогов, карту занятых бло- ков устройств, заголовки устройств и т.п. При нехватке места в кэше, в жертву при- носится самый старый считаный блок. Запись также осуществляется через кэш, причем система и не имеет других бу- феров, поэтому блок просто видоизменяет- ся, то бишь модифицируется прямо в кэше, а в описателе кэша устанавливается в еди- ницу 7-ой бит. По мере накопления модифицированных блоков их записывают на устройство, а бит модификации сбрасывается. Это действие называется "автоматический флаш" или "ав- то-флаш" (flush по-английски "слив", не путайте с flash - флэш - "вспышка", а также flesh - флеш - "плоть". Просьба произносить с чисто Оксфордским пронон- сом!) И, наконец, рестарты: #00 - $creat Создает и пересоздает кэш. Требуе- мый размер в блоках необходимо подавать в регистре A. Содержимое остальных регис- тров на работу рестарта не влияют. На выходе проверьте флаг C. В слу- чае ошибки он будет установлен в "1" Единственная возможная ошибка - Error 130 - нехватка памяти, т.е. входное значение регистра A так велико, что кэш не вле- зает над адресом Utop (User Top). Достать адрес Utop Вы можете приме- нив рестарт $g_cnf(#10) (смещение +3 в векторе g_cnf). Utop служит, в основном, предохранителем от опускания кэша так низко, что программа set.com в процессе работы убила бы им саму себя. Поэтому обычно Utop=26676. Рестарт $creat(#00) обнуляет блоки размером по 256 байт под нижней границей области каналов ($g_cnf(#10), смещение +32) в количестве, указанном в регистре А, и создает каталог кэша (по 4 байта на каждый блок). Работать с кэшем размером менее 4 блоков небезопасно (программа на это просто не расчитана). Рекомендуемые раз- меры от 6..10 блоков минимум (если Вы хо- тите выиграть побольше памяти под буферы для программ или данных) до 30 или даже может 70 (в iS-DOS Chic, разумеется). Хотя наращивание кэша после 20 бло- ков почти не дает заметного выигрыша в скорости работы программ. Для восстанов- ления размера кэша по-умолчанию советуем такой вот примерчик: LD C,#10 ;$g_cnf RST 16 EXX ;в HL - адрес вектора LD BC,-6 ;смещение -6, здесь ADD HL,BC ;хранится размер кэша ;по умолчанию LD A,(HL) ;помещаем размер в А LD C,#10 ;$creat RST 16 ;пересоздаем кэш RET C При расчетах размера создаваемого кэша, если известны верхняя и нижняя гра- ницы его, Вы можете пользоваться форму- лой (2), логически вытекающей из формулы (1): DGCHN - CACHE CSIZE = -------------- (2) 260 где CSIZE - искомый размер кэша, DGCHN - нижняя граница области каналов, она же - верхняя граница кэша, а CACHE - нижняя граница кэша. Пример: LD C,#10 ;$g_cnf RST 16 EXX ;в HL - адрес вектора LD BC,32 ;смещение +32 ADD HL,BC LD E,(HL) ;в DE - адрес верхней INC HL ;границы кэша LD D,(HL) LD HL,CACHE;это - требуемая ниж- ;няя граница кэша, ;ниже которой вся па- ;мять может быть ис- ;пользована Вашей ;программой EX DE,HL ;вычтем CACHE SBC HL,DE ;из DGCHN XOR A LD DE,260 ;делим результат 1$ SBC HL,DE ;на 260 INC A JR NC,1$ DEC A ;проверяем, не стал CP 6 ;ли CSIZE меньше JR NC,2$ ;6 блоков LD A,130 ;Нет места! Выход с RET ;ошибкой 130 ;код ошибки в рег. А 2$ LD C,#00 ;$creat RST 16 ;пересоздаем кэш RET C ... #01 - $clear Очистка кэша от блоков текущего ус- тройства. Эта операция необходима при смене дискеты в дисководе или при отказе выгрузки на диск блоков, модифицирован- ных рестартами $modwr(#03), $modo(#2E), $wpart(#2A), когда авто-флаш ещё не слу- чился, а так же при записи на диск мимо кэша блоков, считанных через кэш, дабы диск и кэш соответствовали друг другу. При ракботе рестарта вся информация, имеющаяся в кэше теряется. Если в кэше есть модифицированные блоки, и Вы хотите их сохранить, то пе- редочисткойвызовите рестарт $flush(#02). Ошибок после вызова быть не должно. Входных параметров нет. Пример использования рестарта $clear взят из программы format.com: Это - начало процедуры format. Воз- можно, перед форматированием был сменен диск. Поэтому кэш должен быть очищен рестартом $clear(#01). Этот пример также будет полезен для лучшего понимания рабо- ты рестартов $g_blk(#13), $g_drv(#14), $binit(#0F), которые будут рассмотрены нами далее. Вначале с помощью $g_blk(#13) мы уз- наем текущее блочное устройство и дос- таем адрес тела соответствующего ему драйвера. Далее проверяем, является ли этот драйвердрайверомфлоппи диска sys_driv.blk, т.е. можно ли форматиро- вать это устройство через TRDOS. Затем мы пытаемся открыть это ус- тройство как блочное устройство IS-DOS рестартами $binit(#0F) и $open(#20) дабы предотвратить случайное переформатирова- ние существующего диска: LD C,#13 ;$g_blk RST 16 ;получаем в А номер ;текущего блочного ;устройства INC C ;$g_drv RST 16 ;узнаем адрес его RET C ;драйвера EXX ;в HL - адрес тела ;драйвера текущего ;блочного устройства ;здесь мы проверяем, является ли этот ;драйвер драйвером дисковода LD BC,9 ADD HL,BC LD (DRCSR+1),HL RES 7,(HL) ;не вызывать $erdrv(1F) при ошибках на ;диске LD C,#01 ;$clear RST 16 ;очищаем кэш LD C,#0F ;$binit RST 16 ;инициализируем ;устройство JR C,FOR_1 ;переход, на про- ;цедуру форматиро- ;вания, если устрой- ;ство не существует LD C,#20 ;$open RST 16 ;пытаемся открыть ;устройство JR C,FOR_1 ;переход на формати- ;рование, если не ;открывается ... FOR_1 ;здесь расположена ;программа формати- ;рования диска ... #02 - $flush Рестарт $flush(#02) необходим для фиксации на любом блочном устройстве (диске, винчестере или электронном диске) изменений, произведённых в кэше после за- писи с помощью рестартов $modwr(#03), $modo(#2E), $wpart(#2A), $crfil(#23), $erfil(#24) и др. Все вышеперечисленные рестарты моди- фицирут блоки в кэше, естественно, что эти изменения должны в конце концов ока- заться на диске. Дело в том, что кэш блочных ус- тройств был создан во-первых для ускоре- ния работы нижних уровней системы, дабы дважды и трижды не читать с устройства одни и те же блоки. К тому же системе был нужен некий свой буфер для работы с ката- логами и файлами. В связи с этим, запись на устрой- ства система в большинстве случаев осу- ществляет не напрямую и не лезет с каж- дым новым блоком на диск, а помечает блок в кэше как "модифицированный", то есть устанавливает в 1 один из разрядов описа- теля блока в кэше. При достижении определенного коли- чества модифицированных блоков (обычно оно равно размеру кэша, деленному на 2 или размеру кэша минус 3) происходит ав- томатический флаш. При этом в 1 устанав- ливается 1-ый бит 1-го байта вектора блочного устройства. Входных параметров у этого рестарта нет. Возможны различные ошибки на выходе, например: Ошибки кэша: 61 - указанный блок не найден 62 - реальное кол-во модифицированных блоков не соответствует значе- нию системной переменной ($QNMOD) 63 - сектор защищен 64 - кэш защищен 65 - нет места в кэше 66 - чтение запрещено 67 - запись запрещена Ошибки блочного драйвера sys_driv.blk: 6 - нет диска 7 - ошибка чтения/записи 20 - обращение к диску прервано Если модифицированных блоков в кэше на текущем (!) устройстве нет, то рес- тарт ничего не делает. Советуем вызывать $flush(#02) в кон- це вашей программы перед выходом в обо- лочку, а так же перед запуском из Вашей программы некоторых системныхутилит (copy25.com, например) и перед пересосда- нием кэша при высвобождении памяти. Если Ваша программа писала через кэш на несколько различных блочных устройств (например: текущее T:, системное S:, быс- трое Q:), то не забудьте сделать flush на каждом из этих устройств, переключаясь на них с помощью рестарта $swblk(#1C) или $g_sta(#37). от пример выхода из программы, мо- дифицирующей блоки на нескольких устрой- ствах сразу, скажем на Q, S и T: LD A,"Q" ;для устройства "Q" CALL FLUSH ;вызов подпрограммы RET C LD A,"S" ;для устройства "S" CALL FLUSH ;вызов подпрограммы RET C LD A,"T" ;для устройства "T" CALL FLUSH ;вызов подпрограммы RET C ;здесь происходит восстановление среды, ;которая была на момент запуска LD C,#41 ;$fmrst RST #10 ;вызов рестарта RET C ;выход в оболочку с перепечаткой экрана ;стандартным способом XOR A LD A,#F4 RET ;подпрограмма FLUSH ;сначала преобразуем логическое имя ;устройства ("S","Q","T") в физический F;номер (0..7) FLUSH LD C,#4A ;$dvtrn RST #10 LD B,A ;номер устройства ;помещаем в рег. B ;переключаемся на соответствующее F;устройство LD C,#1C ;$swblk RST #10 RET C ;вот теперь, собственно, flush LD C,#02 ;$flush RST #10 RET ;возврат из ;подпрограммы #03 - $modwr Запись одного блока (256 байт) с ад- реса, поданного в HL на текущее блочное устройство под номером блока, поданным в DE. Запись происходит через кэш, т.е. сперва в кэше освобождается место - выки- дывается самый старый считаный блок - и на его место переносятся заданные 256 байт. Новый блок получает свой номер и но- мер устройства (от 0 до 7) и тут же поме- чается как модифицированный. Если пере- полнения модифицированных блоков при этом не произошло, то на этом всё и кончается, иначе происходит автоматический флаш, т.е. физическая запись модифицированных блоков на текущее устройство (посред- ством обращения к драйверу). При работе рестарта возможны ошибки, так что после его вызова советуем прове- рять флаг C. Коды ошибок те же что и у $flush(#02). Рестарт $modwr(#03) удобен при рабо- те непосредственно с устройством (минуя файл). Не забудьте тольковызвать $flush(#02) в конце программы! Примеризжизни: программа arzt+.com, а точнее - ее фрагмент, в ко- тором осуществляется запись на устрой- ство измененной таблицы занятых блоков: $MODWR LD HL,$BUFF ;адрес рабочего ;буфера помещаем ;в рег. HL LD DE,1 ;номер 1-го ;блока - в DE LD A,($MLTRD+2) ;число блоков LD B,A ;помещаем в B, ;это - счетчик ;последующего ;цикла LD C,#03 ;$modwr 3$ RST #10 RET C INC DE ;вызов в цикле INC H ;для всех блоков DJNZ 3$ ;по очереди RET #04 - $unird Парный к предыдущему рестарт - чте- ние одного блока устройства с кэша. В HL помещаем адрес, в DE - номер блока на ус- тройстве. Если блока в кэше нет, процедура считывает его через драйвер. Флаг C на выходе сообщает о неудачно проведенной операции. Коды ошибок, как и в предыду- щих двух случаях, суть коды ошибок кэша и драйвера (их описание см. $flush(#02)). В реальной жизни этот рестарт прак- тически не применяется, поэтому в качес- тве примера прочитаем 0-й блок устрой- ства в буфер BUFF, очистив перед этим на всякий случай (если была смена диска) кэш LD C,#01 ;$clear RST #10 ;очистка кэша LD DE,0;номер блока = 0 LD HL,BUFF ;адрес буфера LD C,#04 ;$unird RST #10 RET C ... #05 - $mltrd Почти тоже самое, но читается сразу несколько блоков. Число блоков задается в регистре B. Сперва эти блоки ищутся в кэ- ше и копируются по означенному адресу, затем прямо туда, куда заказывали считы- ваются через драйвер недостающие блоки, которые уже затем копируются в кэш. Если число считываемых блоков пре- восходит размер кэша пополам, то считыва- ние происходит мимо кэша, дабы не выби- вать из него такие часто требующиеся бло- ки как блоки каталогов и заголовок ус- тройства, и таким образом не замедлять работу системы с каталогами. Напрямую считываться блоки будут также при наличии в кэше модифицированых блоков. Можно заставить всегда работать этот рестарт напрямую с драйвером, сбро- сив 7-ой бит 0-го байта вектора блочных устройств. Этим рестартом пользуется рестарт чтения части файла $rpart(#29), c по- мощью которого в свою очередь запускают- ся все com-файлы системы. На всякий случай анализируйте флаг C на выходе. Коды ошибок те же, что и у $flush(#02). Пример взят, опять же, из arzt+.com: считывание карты занятых блоков устрой- ства в буфер $BUFF. Байт $MLTRD+2 зара- нее заполняется размером карты: LD C,#35 ;$bkfcb RST #10 ;считываем описатель EXX ;диска и находим в LD BC,-6 ;нем размер карты ADD HL,BC ;Округлим его до целых блоков: ;и поместим по адресу $MLTRD+2 LD A,(HL) INC HL OR A LD A,(HL) JR Z,$+3 INC A LD ($MLTRD+2),A ... CALL $MLTRD ;вызов подпрограммы .... ;подпрограмма, использующая $mltrd(#05) $MLTRD LD BC,#05 ;$mltrd LD HL,$BUFF;адрес буфера - в HL LD DE,1 RST #10 RET $BUFF ;это - буфер #06 - $quard Почти тоже что и $unird(#04), но блок считывается только в кэш (или прос- то находится в нем). Поэтому регистр HL на входе не используется, зато на выходе в HL' возвращается адрес блока в кэше, DE' - адрес заголовка блока в каталоге кэша (необходим для дальнейшего использо- вания $modo(#2E)). На входе в DE подается номер блока устройства. Возможны различные ошибки (те же, что и у рестарта $flush(#02)), посему проверяйте флаг C. Удобен для работы ре- зидентов или программ, которым жаль буфе- ров под блок. Пример взят из программы date.com: ;Считаем 0-ой блок устройства. Именно там ;хранится дата загрузки системы: LD DE,0;номер блока = 0 LD C,#06 ;$quard RST #10 RET C EXX ;Сохраним адрес считанного блока (HL) и ,;адрес описателя этого блока в кэше (DE): LD (TMPHL+1),HL LD (TMPDE+1),DE ... ;...печать и изменение даты... ;(эти процедуры пропущены здесь ;за ненадобностью) ... ;выход из программы: ;в DE подаем новое значение даты. ;Сравним его с хранящимся в 0-ом блоке ;устройства и, если они разнятся, ;положим новую дату в 0-ой блок и сохра- ;ним его на устройстве рестартами ;$modo(#2E) и $flush(#02): TMPHL LD HL,0;в этот пустой байт ;помещается вышеупо- ;мянутый адрес счи- ;танного блока LD BC,30 ADD HL,BC LD C,(HL) INC HL LD B,(HL) ;в BC теперь - старая дата, считанная F;с устройства EX DE,HL XOR A PUSH HL ; новая дата SBC HL,BC; сравнение POP HL EX DE,HL JR Z,SVSTYL ;даты равны. Обход ;Если же даты не равны - кладем новую ;поверх старой прямо в кэше: LD (HL),D DEC HL LD (HL),E ;Промодифицируем блок в кэше: TMPDE LD DE,0 ;в пустой байт после DE, как Вы помните, ,;мы ранее поместили адрес описателя блока ;в кэше, полученный из рестарта $quard(6) ;несколькими строками выше. ;Этот адрес необходим для $modo. LD C,#2E ;$modo RST #10 RET C LD C,#02 ;$flush RST #10 RET C SVSTYL ... Следующие 3 рестарта$key(#07), $kwait(#08) и $ktest(#09) работают с кла- виатурой: #07 - $key Ввод символа с клавиатуры. Обращается прямо в 1-ую точку входа драйвера клавиа- туры. Адрес этой точки входа лежит в драйвере со смещением 2). Если буфер драйвера пуст, тогда драйвер обращается к портам клавиатуры и ждет, пока клавиша не будет нажата. Выходное значение возвращается в ре- гистре A. Оно ависит от нажатой клавиши и от режима в котором находится устройство ввода. Режим определяется нулевым байтом вектора символьного устройстваввода g_key, который можно получить, обратясь к рестарту $g_key(#11). 0-ой байт вектора. Биты (0/1): 0 - строчные/ПРОПИСНЫЕ 1 - latin/русские 2 - текст/псевдографика 3 - не ждать отпускания клавиш при $kwait(8), применяется для макросов в редакторе, сбрасывается сам при опустошении буфера драйвера. Флаг С роли не играет. В качестве примера приводится подпрограмма из тек- стового редактора edit.com, зажигающая и гасящая курсор в текущих координатах и опрашивающая клавиатуру с ожиданием или без (управляется 5-ым битом в edcsr): ; Переключим клавиатуру в режим маленьких ,;латинских чтобы получить код управляющей ;клавиши, не зависящий от текущего состо- ;яния. 3-ий бит (макро) маскируется вось- ;мёркой дабы сохранить его, биты 0,1,2 ;обнуляются: $KEY LD C,#11 ;$g_key RST #10 EXX LD A,(HL) AND 8 LD (HL),A ;lat ;Переключение на текущий драйвер вывода ;на экран (t42/t64): CALL $TYCUR RET C ;Позиционирование. Пересчитаем координаты ;в тексте xc в позицию на экране и поло- F;жим YX в HL: LD H,(IX+ys) LD A,(IX+ys-1) ADD A,(IX+xc) SUB (IX+X_skip) LD L,A L_FLD C,#0C ;$typos RST #10 ;зажжем мигающий курсор в тек. позиции: LD C,#76 ;$y___ RST #10 LD C,#08 ;$kwait ;Обход ожидания отпускания клавиши: BIT 5,(IX+edcsr) JR Z,$+3 RST #10 DEC C ;7=$key - ввод RST #10 ;клавиши PUSH AF ;сохраним код LD C,#77 ;$n___ RST #10 ;погасить курсор POP AF ;восстановим код RET #08 - $kwait Ожидание отпускания клавиш. Рестарт необходим в некоторых случаях, когда опасна случайно дважды нажатая (удержи- ваемая) клавиша, например в самом начале программы, вызываемой клавишей и также опрашивающей клавиатуру с помощью преды- дущего рестарта. Это - вторая точка входа в драйвер (адрес ее находится в драйвере со смеще- нием 4). Используется врестартах $smbgt(#6E), $edstr(#7F), $menu(#91). На выходе может быть всё что угодно. Пример - см. предыдущий рестарт. Эта процедура вызывается, скажем, блочным оверлеем ed0+.ovr с ожиданием от- пуска клавиши перед рестартом $analy(7E) после печати нижней строки функций овер- лея и при вызове функции <Cs/D>, требую- щей подтверждения. Такой вызов гарантирует надежность. Однако при отметке блока та же процедура вызывается без ожидания отпускания, что позволяет отмечать большие блоки, удержи- вая клавишу и используя автоповтор. Этот рестарт при вызове опустошает буфер драйвера клавиатуры, т.о. драйвер "забывает" все нажатые до вызова рестар- та клавиши. Если бы эта функция драйвера не отключалась бы 3-им битом регистра состояния клавиатуры, макросы работали бы лишь до первого $kwait. #09 - $ktest Этот рестарт - третья точка входа в драйвер клавиатуры. Он определяет, нажа- та хоть одна клавиша на клавиатуре или нет. Выход во флаге Z: - не нажата, NZ - нажата. Как и $key(#07), в первую очередь опрашивает буфер драйвера, где накапли- ваются быстро нажатые клавиши, и который используется для макросов или эмуляции нажатой клавиши. Этот рестарт удобен для опроса кла- виатуры без ожидания, например для преры- вания работы циклов. При нажатии клавиши флаг на выходе - NZ, а код клавиши будет в регистре A, а также в буфере драйвера, откуда его необходимо извлечь рестартом $key(#07), иначе на него наткнется еще кто-нибудь. Пример: ;Эта подпрограмма вернется с флагом Z, ;если клавиш нажато не было ;и с флагом NZ и кодом клавиши ;в регистре A в противном случае. KTEST LD C,#09 ;$ktest RST #10 RET Z LD C,#07 ;$key RST #10 ;извлекаем "лишний" ;код из буфера драй- ;вера клавиатуры OR A RET Три следующих рестарта предназначены для символьного вывода на экран или прин- тер, это $type(#0A), $tycpl(#0B) и $typos(#0C): #0A - $type Печать символа из регистра A на те- кущем устройстве символьного вывода (эк- ран или принтер). Выбор устройства (экран или принтер) можно осуществить при помощи рестарта $svtyp(#1B). Флаг на выходе при печати на экране роли не играет. Обращаясь к принтеру мож- но схлопотать следующие ошибки: 150 - ошибка или принтер не готов, 151 - печать прервана. Последняя версия программы print.com не пользуется этим рестартом. Печать на экране происходит в теку- щей позиции, которая хранится в байтах 1..4 вектора символьного устройства выво- да. Вектор можно достать с помощью рес- тарта $g_typ(#12). Байты: - Позиция печати в строке 2 - Номер строки на экране Эти 2 байта подаются в HL на входе рестарта $typos(#0C), который передает их в регистре BC в 3-ю точку входа в драй- вер (смещение 6). 3,4 - Адрес печати на экране Это - левый верхний байт буквы. Нап- ример #4000 для 0-ой позиции в 0-ой стро- ке). Сюда эти байты кладутрестарты $typos(#0C) и $wtpos(#6B), Отсюда берет их для печати следующего символа рестарт $type(#0A). Сюда же он их и возвращает после печати. Рестарт обращается к 1-ой точке вхо- да драйвера (смещение 2 от начала драйве- ра), при этом адрес печати на экране по- дается в регистре DE'. Этим рестартом пользуются рестарты более высоких уров- ней, такие как $str(#6C), $smbgt(#6E) и др. #0B - $tycpl Устанавливает инверсный/нормальный режимы печати на экране в соответствии с содержимым регистра А: <>0 - инверсная печать A=0 - нормальный режим У драйверов принтеров не использует- ся. Флаг C на выходе значения не имеет. Рестарт используется новой версией текстового редактора для блочной отметки а также программой demon.com. Обращается во 2-ую точку входа в драйвер (адрес по смещению 4). #0C - $typos Задание позиции печати на экране для последующей печати рестартами $type(#0A), $str(#6C), $lnstr(#6D), $smbgt(#6E), $edstr(#7F). Координаты печати задаются в регис- тре HL: - номер строки, L - позиция в строке. Для позиционирования относительно окна также можно пользоваться рестартом $wtpos(#6B). Флаг C на выходе значения не имеет. Этот рестарт использует 3-ю точку входа в драйвер (смещение 6). У старых драйверов принтера эта точ- ка не использовалась, у новых она отве- чает за печать символа из регистра A без перекодировки. Вот как использует рестарты $typos и $type программа exebat.com для печати ко- мандной строки из системного буфера: ;Проверка наличия 8-го канала (канал ;драйвера символьного вывода), т.к. прог- ;рамма должна работать даже тогда, когда ;печатать нечем: PRINT LD A,8 ;ty42 LD C,#16 ;$stchn RST #10 RET C ;Позиционирование: SBC HL,HL ;HL=0: верхняя строка LD C,#0C ;$typos RST #10 LD C,#45 ;$g_com RST 16 EXX ;HL=адрес системного буфера LD BC,#2A0A ;$type ;B=#2A=42-столько букв помещается в одну ;строку на экране, #0A - $type ;Печатаем строку до символа #D: 1$ LD A,(HL) CP #D JR Z,2$ RST #10 INC HL DJNZ 1$ RET ;Допечатка пробелов: F2$ LD A," " RST 16 ;C=$type (см. выше) DJNZ 2$ RET Три рестарта, обращающиеся к драйверу блочного устройства: #0D - $read Чтение блоков напрямую с драйвера текущего блочного устройства. HL - адрес, DE - номер блока устройства, B - число блоков. Флаг C на выходе - признак ошибки. Ошибки только те, что выдает драйвер. Так драйвер электронного диска обычно не вы- дает ошибок, хотя никто не мешает встроить в него проверку, скажем, номера блока на слишком большой номер или число считываемых блоков на 0 или вставить про- верку контрольной суммы блока. У драйвера sys_driv.blk возможные ошибки следующие: 6 - нет диска 7 - сектор не читается 20 - чтение прервано. Обычно драйвер флоппи-диска отраба- тывает эти ситуации обращаясь к рестарту $erdrv(#1F) (см. ниже). Рестарт обращает- ся к 1-ой точке входа в драйвер. Пример взят из жизни копировщиков: начало рабочего цикла программы abc.com. В байты INDSK+2 и OUTDSK+2 кладутся номе- ра входного и выходного устройств. ;B=число блоков для чтения/записи COPY PUSH BC INDSK LD BC,#1C ;$swblk RST #10 POP BC RET C LD HL,BUFF ;буфер копирования LD C,#0D ;$read RST #10 RET C PUSH BC OUTDSK LD BC,#021C;$swblk - в C RST #10 POP BC RET C LD C,#0E ;$write RST #10 RET C ... #0E - $write Запись B блоков с адреса HL на теку- щее блочное устройство (флоппи диск, электронный диск или винчестер) начиная с лока номер DE. В IS-DOS любое блочное устройство состоит из логических блоков размером 256 (#100 hex) байт, что очень удобно для расчетов в ассемблере. Пересчётом номера блока в трек, сто- рону и сектор занимается драйвер. Драй- вер флоппи диска настраивается на формат IS-DOS дискеты сам рестартом $binit(#0F). Это - 2-ая точка входа в драйвер. Поскольку это прямое обращение к ус- тройству минуя кэш, рекомендуется сразу после вызова этого рестартавызвать $clear(#01) или $creat(#00) во избежании путаницы блоков в кэше и блоков на ус- тройстве! Ситуация с возможными ошибками точ- но такая же как и у $read(#0D). Пример также см. выше. #0F - $binit Автонастройка драйвера флоппи диска на формат IS-DOS диска. Считывает 0-й блок диска, проверяет наличие признака "DSK" со смещением 10. Если признака нет, то выходит с ошибкой 9. Иначе - настраи- вается на параметры диска, лежащие со смещением: ┌──┬────────────────────────────────────┐ │22│число цилиндров │ │23│тип диска (число сторон) │ ,│24│размер сектора (1/2/4: 256/512/1024)│ │ │байт │ │25│число секторов на дорожке │ │64│номера секторов (до 16 штук) │ └──┴────────────────────────────────────┘ Возможна также ошибка 10 - попытка работы с двусторонним диском на односто- роннем дисководе или с 80-дорожечным на 40-трековом. Это 3-ий вход в драйвер. Обычно не используется у драйверов элек- тронного диска и винчестера (там ставят- ся "заглушки" для совместимости). Этот рестарт полезно вызывать при первом обращении к новому блочному ус- тройству для настройки драйвера на диск и чтоб убедиться в том, что это именно IS-DOS устройство. Кроме ошибок 9 и 10 может выдать ошибки чтения: 6, 7 и 20. Драйвер можно настроить и на работу с форматами TR-DOS и MS-DOS, но рестарт здесь уже мало чем поможет. В таких случаях лучше найти драйвер с помощью рестарта $g_drv(#14), убе- диться, что это именно драйвер флоп- пи-диска, и по смещению 34(dec) сменить размер сектора, количество секторов и таблицу секторов. В качестве примера рекомендуем отры- вок из format.com приведенный после опи- сания рестарта $clear(#01). Cтруктура описателя устройства (диска) 0-ой БЛОК: (0-ой сектор 0-ой дорожки) ┌────┬────┬─────────────────────────────┐ │сме-│дли-│ комментарии │ G│ще- │на │ │ │ние ││ │ ├────┼────┼─────────────────────────────┤ │ 0 │ 2 │Не используется │ │ 2 │ 8 │Имя устройства │ │10 │ 3 │Признак iS-DOS: "DSK" │ │13 │ 5 │Не используется │ │18 │ 2 │Размер устройства │ │││(в блоках по 256 байт) │ │20 │ 2 │Номер 0-го блока главного│ │││каталога │ │22 │ 1 │Количество треков на устр. │ │23 │ 1 │Тип диска. Биты(0/1): │ │││ 0 - 40/80 дорожек │ │││ 1 - 1/2 стороны │ │24 │ 1 │Размер сектора: │ │││1/2/4: 256/512/1024 байт │ │25 │ 1 │Количество секторов на дор. │ │26 │ 1 │Не используется │ │27 │ 1 │Контр. сумма 32 байтового│ │││описателя ????_dos.sys │ │28 │ 2 │Не используется │ │30 │ 2 │Дата │ │32 │ 32 │Описатель ????_dos.sys │ │64 │ 16 │Таблица номеров секторов │ │││на дорожке │ └────┴────┴─────────────────────────────┘ 1-ый БЛОК: Бит-карта устройства. 1 бит/блок: 0-свободен/1-занят Описатель ????_dos.sys со смещением 32 помещается в 0-ой блокпрограммой con.com исчитываетсязагрузчиком boot.sys. Ну вот и все на сегодня. В следую- щем номере журнала мы обязательно продол- жим публикацию книги А. Леонтьева. Далее будут рассмотрены оставшиеся рестарты уровня DOS.SYS с номерами от #10 до #1F. Мы рады будем выслушать Ваши отзывы и пожелания, а так же замеченные вами не- точности, неясности и опечатки, неизбеж- ные при написании и подготовке подобных текстов. Пишите, звоните нам или в редакцию журнала, помните, что именно благодаря вашему интересу к нашей системе, она до сих пор существует и развивается. _______________________________